🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@metamask/messenger

Package Overview
Dependencies
Maintainers
4
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@metamask/messenger - npm Package Compare versions

Comparing version
1.0.0
to
1.1.0
+105
dist/generate-action-types/check.cjs
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkActionTypesFiles = void 0;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const generate_content_1 = require("./generate-content.cjs");
/**
* Checks if generated action types files are up to date.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files are up to date.
*/
async function checkActionTypesFiles(sources, eslint) {
let hasErrors = false;
const fileComparisonJobs = [];
try {
for (const source of sources) {
console.log(`\n🔧 Checking ${source.name}...`);
const outputDir = path.dirname(source.filePath);
const baseFileName = path.basename(source.filePath, '.ts');
const actualFile = path.join(outputDir, `${baseFileName}-method-action-types.ts`);
const expectedContent = (0, generate_content_1.generateActionTypesContent)(source);
const expectedTempFile = actualFile.replace('.ts', '.tmp.ts');
try {
await fs.promises.access(actualFile);
await fs.promises.writeFile(expectedTempFile, expectedContent, 'utf8');
fileComparisonJobs.push({
expectedTempFile,
actualFile,
baseFileName,
});
}
catch (error) {
if (error.code === 'ENOENT') {
console.error(`❌ ${baseFileName}-method-action-types.ts does not exist`);
}
else {
console.error(`❌ Error reading ${baseFileName}-method-action-types.ts:`, error);
}
hasErrors = true;
}
}
if (fileComparisonJobs.length > 0) {
if (eslint) {
console.log('\n📝 Running ESLint to compare files...');
const results = await eslint.instance.lintFiles(fileComparisonJobs.map((job) => job.expectedTempFile));
await eslint.eslintClass.outputFixes(results);
}
for (const job of fileComparisonJobs) {
const expectedContent = await fs.promises.readFile(job.expectedTempFile, 'utf8');
const actualContent = await fs.promises.readFile(job.actualFile, 'utf8');
if (expectedContent === actualContent) {
console.log(`✅ ${job.baseFileName}-method-action-types.ts is up to date`);
}
else {
console.error(`❌ ${job.baseFileName}-method-action-types.ts is out of date`);
hasErrors = true;
}
}
}
}
finally {
for (const job of fileComparisonJobs) {
try {
await fs.promises.unlink(job.expectedTempFile);
}
catch {
// Ignore cleanup errors
}
}
}
if (hasErrors) {
console.error('\n💥 Some action type files are out of date or missing.');
console.error('Run `yarn generate-method-action-types --fix` to update them.');
return false;
}
console.log('\n🎉 All action type files are up to date!');
return true;
}
exports.checkActionTypesFiles = checkActionTypesFiles;
//# sourceMappingURL=check.cjs.map
{"version":3,"file":"check.cjs","sourceRoot":"","sources":["../../src/generate-action-types/check.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { generateActionTypesContent } from './generate-content';\nimport type { SourceInfo } from './parse-source';\nimport type { ESLint } from './types';\n\n/**\n * Checks if generated action types files are up to date.\n *\n * @param sources - Array of source information objects.\n * @param eslint - Optional ESLint instance and static methods for formatting.\n * @returns Whether all files are up to date.\n */\nexport async function checkActionTypesFiles(\n sources: SourceInfo[],\n eslint: ESLint | null,\n): Promise<boolean> {\n let hasErrors = false;\n\n const fileComparisonJobs: {\n expectedTempFile: string;\n actualFile: string;\n baseFileName: string;\n }[] = [];\n\n try {\n for (const source of sources) {\n console.log(`\\n🔧 Checking ${source.name}...`);\n const outputDir = path.dirname(source.filePath);\n const baseFileName = path.basename(source.filePath, '.ts');\n const actualFile = path.join(\n outputDir,\n `${baseFileName}-method-action-types.ts`,\n );\n\n const expectedContent = generateActionTypesContent(source);\n const expectedTempFile = actualFile.replace('.ts', '.tmp.ts');\n\n try {\n await fs.promises.access(actualFile);\n\n await fs.promises.writeFile(expectedTempFile, expectedContent, 'utf8');\n\n fileComparisonJobs.push({\n expectedTempFile,\n actualFile,\n baseFileName,\n });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n console.error(\n `❌ ${baseFileName}-method-action-types.ts does not exist`,\n );\n } else {\n console.error(\n `❌ Error reading ${baseFileName}-method-action-types.ts:`,\n error,\n );\n }\n hasErrors = true;\n }\n }\n\n if (fileComparisonJobs.length > 0) {\n if (eslint) {\n console.log('\\n📝 Running ESLint to compare files...');\n\n const results = await eslint.instance.lintFiles(\n fileComparisonJobs.map((job) => job.expectedTempFile),\n );\n await eslint.eslintClass.outputFixes(results);\n }\n\n for (const job of fileComparisonJobs) {\n const expectedContent = await fs.promises.readFile(\n job.expectedTempFile,\n 'utf8',\n );\n const actualContent = await fs.promises.readFile(\n job.actualFile,\n 'utf8',\n );\n\n if (expectedContent === actualContent) {\n console.log(\n `✅ ${job.baseFileName}-method-action-types.ts is up to date`,\n );\n } else {\n console.error(\n `❌ ${job.baseFileName}-method-action-types.ts is out of date`,\n );\n hasErrors = true;\n }\n }\n }\n } finally {\n for (const job of fileComparisonJobs) {\n try {\n await fs.promises.unlink(job.expectedTempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n if (hasErrors) {\n console.error('\\n💥 Some action type files are out of date or missing.');\n console.error(\n 'Run `yarn generate-method-action-types --fix` to update them.',\n );\n return false;\n }\n\n console.log('\\n🎉 All action type files are up to date!');\n return true;\n}\n"]}
import type { SourceInfo } from "./parse-source.cjs";
import type { ESLint } from "./types.cjs";
/**
* Checks if generated action types files are up to date.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files are up to date.
*/
export declare function checkActionTypesFiles(sources: SourceInfo[], eslint: ESLint | null): Promise<boolean>;
//# sourceMappingURL=check.d.cts.map
{"version":3,"file":"check.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/check.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAgB;AAEtC;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CAmGlB"}
import type { SourceInfo } from "./parse-source.mjs";
import type { ESLint } from "./types.mjs";
/**
* Checks if generated action types files are up to date.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files are up to date.
*/
export declare function checkActionTypesFiles(sources: SourceInfo[], eslint: ESLint | null): Promise<boolean>;
//# sourceMappingURL=check.d.mts.map
{"version":3,"file":"check.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/check.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAgB;AAEtC;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CAmGlB"}
import * as fs from "node:fs";
import * as path from "node:path";
import { generateActionTypesContent } from "./generate-content.mjs";
/**
* Checks if generated action types files are up to date.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files are up to date.
*/
export async function checkActionTypesFiles(sources, eslint) {
let hasErrors = false;
const fileComparisonJobs = [];
try {
for (const source of sources) {
console.log(`\n🔧 Checking ${source.name}...`);
const outputDir = path.dirname(source.filePath);
const baseFileName = path.basename(source.filePath, '.ts');
const actualFile = path.join(outputDir, `${baseFileName}-method-action-types.ts`);
const expectedContent = generateActionTypesContent(source);
const expectedTempFile = actualFile.replace('.ts', '.tmp.ts');
try {
await fs.promises.access(actualFile);
await fs.promises.writeFile(expectedTempFile, expectedContent, 'utf8');
fileComparisonJobs.push({
expectedTempFile,
actualFile,
baseFileName,
});
}
catch (error) {
if (error.code === 'ENOENT') {
console.error(`❌ ${baseFileName}-method-action-types.ts does not exist`);
}
else {
console.error(`❌ Error reading ${baseFileName}-method-action-types.ts:`, error);
}
hasErrors = true;
}
}
if (fileComparisonJobs.length > 0) {
if (eslint) {
console.log('\n📝 Running ESLint to compare files...');
const results = await eslint.instance.lintFiles(fileComparisonJobs.map((job) => job.expectedTempFile));
await eslint.eslintClass.outputFixes(results);
}
for (const job of fileComparisonJobs) {
const expectedContent = await fs.promises.readFile(job.expectedTempFile, 'utf8');
const actualContent = await fs.promises.readFile(job.actualFile, 'utf8');
if (expectedContent === actualContent) {
console.log(`✅ ${job.baseFileName}-method-action-types.ts is up to date`);
}
else {
console.error(`❌ ${job.baseFileName}-method-action-types.ts is out of date`);
hasErrors = true;
}
}
}
}
finally {
for (const job of fileComparisonJobs) {
try {
await fs.promises.unlink(job.expectedTempFile);
}
catch {
// Ignore cleanup errors
}
}
}
if (hasErrors) {
console.error('\n💥 Some action type files are out of date or missing.');
console.error('Run `yarn generate-method-action-types --fix` to update them.');
return false;
}
console.log('\n🎉 All action type files are up to date!');
return true;
}
//# sourceMappingURL=check.mjs.map
{"version":3,"file":"check.mjs","sourceRoot":"","sources":["../../src/generate-action-types/check.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { generateActionTypesContent } from './generate-content';\nimport type { SourceInfo } from './parse-source';\nimport type { ESLint } from './types';\n\n/**\n * Checks if generated action types files are up to date.\n *\n * @param sources - Array of source information objects.\n * @param eslint - Optional ESLint instance and static methods for formatting.\n * @returns Whether all files are up to date.\n */\nexport async function checkActionTypesFiles(\n sources: SourceInfo[],\n eslint: ESLint | null,\n): Promise<boolean> {\n let hasErrors = false;\n\n const fileComparisonJobs: {\n expectedTempFile: string;\n actualFile: string;\n baseFileName: string;\n }[] = [];\n\n try {\n for (const source of sources) {\n console.log(`\\n🔧 Checking ${source.name}...`);\n const outputDir = path.dirname(source.filePath);\n const baseFileName = path.basename(source.filePath, '.ts');\n const actualFile = path.join(\n outputDir,\n `${baseFileName}-method-action-types.ts`,\n );\n\n const expectedContent = generateActionTypesContent(source);\n const expectedTempFile = actualFile.replace('.ts', '.tmp.ts');\n\n try {\n await fs.promises.access(actualFile);\n\n await fs.promises.writeFile(expectedTempFile, expectedContent, 'utf8');\n\n fileComparisonJobs.push({\n expectedTempFile,\n actualFile,\n baseFileName,\n });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n console.error(\n `❌ ${baseFileName}-method-action-types.ts does not exist`,\n );\n } else {\n console.error(\n `❌ Error reading ${baseFileName}-method-action-types.ts:`,\n error,\n );\n }\n hasErrors = true;\n }\n }\n\n if (fileComparisonJobs.length > 0) {\n if (eslint) {\n console.log('\\n📝 Running ESLint to compare files...');\n\n const results = await eslint.instance.lintFiles(\n fileComparisonJobs.map((job) => job.expectedTempFile),\n );\n await eslint.eslintClass.outputFixes(results);\n }\n\n for (const job of fileComparisonJobs) {\n const expectedContent = await fs.promises.readFile(\n job.expectedTempFile,\n 'utf8',\n );\n const actualContent = await fs.promises.readFile(\n job.actualFile,\n 'utf8',\n );\n\n if (expectedContent === actualContent) {\n console.log(\n `✅ ${job.baseFileName}-method-action-types.ts is up to date`,\n );\n } else {\n console.error(\n `❌ ${job.baseFileName}-method-action-types.ts is out of date`,\n );\n hasErrors = true;\n }\n }\n }\n } finally {\n for (const job of fileComparisonJobs) {\n try {\n await fs.promises.unlink(job.expectedTempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n if (hasErrors) {\n console.error('\\n💥 Some action type files are out of date or missing.');\n console.error(\n 'Run `yarn generate-method-action-types --fix` to update them.',\n );\n return false;\n }\n\n console.log('\\n🎉 All action type files are up to date!');\n return true;\n}\n"]}
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const yargs_1 = __importDefault(require("yargs"));
const check_1 = require("./check.cjs");
const fix_1 = require("./fix.cjs");
const parse_source_1 = require("./parse-source.cjs");
/**
* Parses the given CLI arguments.
*
* @param args - The arguments to parse.
* @returns The parsed command line arguments.
*/
async function parseCommandLineArguments(args) {
const { check, fix, path: sourcePath, } = await (0, yargs_1.default)(args)
.command('$0 [path]', 'Generate method action types for controller and service messengers', (yargsInstance) => {
yargsInstance.positional('path', {
type: 'string',
description: 'Path to the folder where controllers/services are located',
default: 'src',
});
})
.option('check', {
type: 'boolean',
description: 'Check if generated action type files are up to date',
default: false,
})
.option('fix', {
type: 'boolean',
description: 'Generate/update action type files',
default: false,
})
.help()
.check((argv) => {
if (!argv.check && !argv.fix) {
throw new Error('Either --check or --fix must be provided.\n');
}
return true;
}).argv;
return {
check,
fix,
sourcePath: sourcePath,
};
}
/**
* Attempt to load ESLint from the current project. Returns null if unavailable.
*
* @returns An ESLint object with instance and static methods, or null if unavailable.
*/
async function loadESLint() {
try {
const { ESLint: ESLintClass } = await import("eslint");
const instance = new ESLintClass({
fix: true,
errorOnUnmatchedPattern: false,
});
return {
instance,
eslintClass: ESLintClass,
};
}
catch {
console.warn('⚠️ ESLint could not be loaded. Generated files will not be formatted.');
return null;
}
}
/**
* Main entry point for the CLI.
*/
async function main() {
const { fix, sourcePath } = await parseCommandLineArguments(globalThis.process.argv.slice(2));
console.log('🔍 Searching for controllers/services with MESSENGER_EXPOSED_METHODS...');
const sources = await (0, parse_source_1.findSourcesWithExposedMethods)(sourcePath);
if (sources.length === 0) {
console.log('⚠️ No controllers/services found with MESSENGER_EXPOSED_METHODS');
return;
}
console.log(`📦 Found ${sources.length} controller(s)/service(s) with exposed methods`);
const eslint = await loadESLint();
if (fix) {
const success = await (0, fix_1.generateAllActionTypesFiles)(sources, eslint);
if (success) {
console.log('\n🎉 All action types generated successfully!');
}
else {
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
}
}
else {
const success = await (0, check_1.checkActionTypesFiles)(sources, eslint);
if (!success) {
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
}
}
}
main().catch((error) => {
console.error('❌ Script failed:', error);
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
});
//# sourceMappingURL=cli.cjs.map
{"version":3,"file":"cli.cjs","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport yargs from 'yargs';\n\nimport { checkActionTypesFiles } from './check';\nimport { generateAllActionTypesFiles } from './fix';\nimport { findSourcesWithExposedMethods } from './parse-source';\nimport type { ESLint } from './types';\n\ntype CommandLineArguments = {\n check: boolean;\n fix: boolean;\n sourcePath: string;\n};\n\n/**\n * Parses the given CLI arguments.\n *\n * @param args - The arguments to parse.\n * @returns The parsed command line arguments.\n */\nasync function parseCommandLineArguments(\n args: string[],\n): Promise<CommandLineArguments> {\n const {\n check,\n fix,\n path: sourcePath,\n } = await yargs(args)\n .command(\n '$0 [path]',\n 'Generate method action types for controller and service messengers',\n (yargsInstance) => {\n yargsInstance.positional('path', {\n type: 'string',\n description:\n 'Path to the folder where controllers/services are located',\n default: 'src',\n });\n },\n )\n .option('check', {\n type: 'boolean',\n description: 'Check if generated action type files are up to date',\n default: false,\n })\n .option('fix', {\n type: 'boolean',\n description: 'Generate/update action type files',\n default: false,\n })\n .help()\n .check((argv) => {\n if (!argv.check && !argv.fix) {\n throw new Error('Either --check or --fix must be provided.\\n');\n }\n return true;\n }).argv;\n\n return {\n check,\n fix,\n sourcePath: sourcePath as string,\n };\n}\n\n/**\n * Attempt to load ESLint from the current project. Returns null if unavailable.\n *\n * @returns An ESLint object with instance and static methods, or null if unavailable.\n */\nasync function loadESLint(): Promise<ESLint | null> {\n try {\n const { ESLint: ESLintClass } = await import('eslint');\n const instance = new ESLintClass({\n fix: true,\n errorOnUnmatchedPattern: false,\n });\n return {\n instance,\n eslintClass: ESLintClass,\n };\n } catch {\n console.warn(\n '⚠️ ESLint could not be loaded. Generated files will not be formatted.',\n );\n return null;\n }\n}\n\n/**\n * Main entry point for the CLI.\n */\nasync function main(): Promise<void> {\n const { fix, sourcePath } = await parseCommandLineArguments(\n globalThis.process.argv.slice(2),\n );\n\n console.log(\n '🔍 Searching for controllers/services with MESSENGER_EXPOSED_METHODS...',\n );\n\n const sources = await findSourcesWithExposedMethods(sourcePath);\n\n if (sources.length === 0) {\n console.log(\n '⚠️ No controllers/services found with MESSENGER_EXPOSED_METHODS',\n );\n return;\n }\n\n console.log(\n `📦 Found ${sources.length} controller(s)/service(s) with exposed methods`,\n );\n\n const eslint = await loadESLint();\n\n if (fix) {\n const success = await generateAllActionTypesFiles(sources, eslint);\n if (success) {\n console.log('\\n🎉 All action types generated successfully!');\n } else {\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n }\n } else {\n const success = await checkActionTypesFiles(sources, eslint);\n if (!success) {\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n }\n }\n}\n\nmain().catch((error) => {\n console.error('❌ Script failed:', error);\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n});\n"]}
#!/usr/bin/env node
export {};
//# sourceMappingURL=cli.d.cts.map
{"version":3,"file":"cli.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":""}
#!/usr/bin/env node
export {};
//# sourceMappingURL=cli.d.mts.map
{"version":3,"file":"cli.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":""}
#!/usr/bin/env node
import yargs from "yargs";
import { checkActionTypesFiles } from "./check.mjs";
import { generateAllActionTypesFiles } from "./fix.mjs";
import { findSourcesWithExposedMethods } from "./parse-source.mjs";
/**
* Parses the given CLI arguments.
*
* @param args - The arguments to parse.
* @returns The parsed command line arguments.
*/
async function parseCommandLineArguments(args) {
const { check, fix, path: sourcePath, } = await yargs(args)
.command('$0 [path]', 'Generate method action types for controller and service messengers', (yargsInstance) => {
yargsInstance.positional('path', {
type: 'string',
description: 'Path to the folder where controllers/services are located',
default: 'src',
});
})
.option('check', {
type: 'boolean',
description: 'Check if generated action type files are up to date',
default: false,
})
.option('fix', {
type: 'boolean',
description: 'Generate/update action type files',
default: false,
})
.help()
.check((argv) => {
if (!argv.check && !argv.fix) {
throw new Error('Either --check or --fix must be provided.\n');
}
return true;
}).argv;
return {
check,
fix,
sourcePath: sourcePath,
};
}
/**
* Attempt to load ESLint from the current project. Returns null if unavailable.
*
* @returns An ESLint object with instance and static methods, or null if unavailable.
*/
async function loadESLint() {
try {
const { ESLint: ESLintClass } = await import("eslint");
const instance = new ESLintClass({
fix: true,
errorOnUnmatchedPattern: false,
});
return {
instance,
eslintClass: ESLintClass,
};
}
catch {
console.warn('⚠️ ESLint could not be loaded. Generated files will not be formatted.');
return null;
}
}
/**
* Main entry point for the CLI.
*/
async function main() {
const { fix, sourcePath } = await parseCommandLineArguments(globalThis.process.argv.slice(2));
console.log('🔍 Searching for controllers/services with MESSENGER_EXPOSED_METHODS...');
const sources = await findSourcesWithExposedMethods(sourcePath);
if (sources.length === 0) {
console.log('⚠️ No controllers/services found with MESSENGER_EXPOSED_METHODS');
return;
}
console.log(`📦 Found ${sources.length} controller(s)/service(s) with exposed methods`);
const eslint = await loadESLint();
if (fix) {
const success = await generateAllActionTypesFiles(sources, eslint);
if (success) {
console.log('\n🎉 All action types generated successfully!');
}
else {
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
}
}
else {
const success = await checkActionTypesFiles(sources, eslint);
if (!success) {
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
}
}
}
main().catch((error) => {
console.error('❌ Script failed:', error);
// eslint-disable-next-line no-restricted-globals
process.exitCode = 1;
});
//# sourceMappingURL=cli.mjs.map
{"version":3,"file":"cli.mjs","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport yargs from 'yargs';\n\nimport { checkActionTypesFiles } from './check';\nimport { generateAllActionTypesFiles } from './fix';\nimport { findSourcesWithExposedMethods } from './parse-source';\nimport type { ESLint } from './types';\n\ntype CommandLineArguments = {\n check: boolean;\n fix: boolean;\n sourcePath: string;\n};\n\n/**\n * Parses the given CLI arguments.\n *\n * @param args - The arguments to parse.\n * @returns The parsed command line arguments.\n */\nasync function parseCommandLineArguments(\n args: string[],\n): Promise<CommandLineArguments> {\n const {\n check,\n fix,\n path: sourcePath,\n } = await yargs(args)\n .command(\n '$0 [path]',\n 'Generate method action types for controller and service messengers',\n (yargsInstance) => {\n yargsInstance.positional('path', {\n type: 'string',\n description:\n 'Path to the folder where controllers/services are located',\n default: 'src',\n });\n },\n )\n .option('check', {\n type: 'boolean',\n description: 'Check if generated action type files are up to date',\n default: false,\n })\n .option('fix', {\n type: 'boolean',\n description: 'Generate/update action type files',\n default: false,\n })\n .help()\n .check((argv) => {\n if (!argv.check && !argv.fix) {\n throw new Error('Either --check or --fix must be provided.\\n');\n }\n return true;\n }).argv;\n\n return {\n check,\n fix,\n sourcePath: sourcePath as string,\n };\n}\n\n/**\n * Attempt to load ESLint from the current project. Returns null if unavailable.\n *\n * @returns An ESLint object with instance and static methods, or null if unavailable.\n */\nasync function loadESLint(): Promise<ESLint | null> {\n try {\n const { ESLint: ESLintClass } = await import('eslint');\n const instance = new ESLintClass({\n fix: true,\n errorOnUnmatchedPattern: false,\n });\n return {\n instance,\n eslintClass: ESLintClass,\n };\n } catch {\n console.warn(\n '⚠️ ESLint could not be loaded. Generated files will not be formatted.',\n );\n return null;\n }\n}\n\n/**\n * Main entry point for the CLI.\n */\nasync function main(): Promise<void> {\n const { fix, sourcePath } = await parseCommandLineArguments(\n globalThis.process.argv.slice(2),\n );\n\n console.log(\n '🔍 Searching for controllers/services with MESSENGER_EXPOSED_METHODS...',\n );\n\n const sources = await findSourcesWithExposedMethods(sourcePath);\n\n if (sources.length === 0) {\n console.log(\n '⚠️ No controllers/services found with MESSENGER_EXPOSED_METHODS',\n );\n return;\n }\n\n console.log(\n `📦 Found ${sources.length} controller(s)/service(s) with exposed methods`,\n );\n\n const eslint = await loadESLint();\n\n if (fix) {\n const success = await generateAllActionTypesFiles(sources, eslint);\n if (success) {\n console.log('\\n🎉 All action types generated successfully!');\n } else {\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n }\n } else {\n const success = await checkActionTypesFiles(sources, eslint);\n if (!success) {\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n }\n }\n}\n\nmain().catch((error) => {\n console.error('❌ Script failed:', error);\n // eslint-disable-next-line no-restricted-globals\n process.exitCode = 1;\n});\n"]}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateAllActionTypesFiles = void 0;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const generate_content_1 = require("./generate-content.cjs");
/**
* Generates action types files for all controllers/services.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files were generated successfully.
*/
async function generateAllActionTypesFiles(sources, eslint) {
const outputFiles = [];
for (const source of sources) {
console.log(`\n🔧 Processing ${source.name}...`);
const outputDir = path.dirname(source.filePath);
const baseFileName = path.basename(source.filePath, '.ts');
const outputFile = path.join(outputDir, `${baseFileName}-method-action-types.ts`);
const generatedContent = (0, generate_content_1.generateActionTypesContent)(source);
await fs.promises.writeFile(outputFile, generatedContent, 'utf8');
outputFiles.push(outputFile);
console.log(`✅ Generated action types for ${source.name}`);
}
if (outputFiles.length > 0 && eslint) {
console.log('\n📝 Running ESLint on generated files...');
const results = await eslint.instance.lintFiles(outputFiles);
await eslint.eslintClass.outputFixes(results);
const errors = eslint.eslintClass.getErrorResults(results);
if (errors.length > 0) {
console.error('❌ ESLint errors:', errors);
return false;
}
console.log('✅ ESLint formatting applied');
}
return true;
}
exports.generateAllActionTypesFiles = generateAllActionTypesFiles;
//# sourceMappingURL=fix.cjs.map
{"version":3,"file":"fix.cjs","sourceRoot":"","sources":["../../src/generate-action-types/fix.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAA8B;AAC9B,gDAAkC;AAElC,6DAAgE;AAIhE;;;;;;GAMG;AACI,KAAK,UAAU,2BAA2B,CAC/C,OAAqB,EACrB,MAAqB;IAErB,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAC1B,SAAS,EACT,GAAG,YAAY,yBAAyB,CACzC,CAAC;QAEF,MAAM,gBAAgB,GAAG,IAAA,6CAA0B,EAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC;QAClE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC7D,MAAM,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YAC1C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAnCD,kEAmCC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { generateActionTypesContent } from './generate-content';\nimport type { SourceInfo } from './parse-source';\nimport type { ESLint } from './types';\n\n/**\n * Generates action types files for all controllers/services.\n *\n * @param sources - Array of source information objects.\n * @param eslint - Optional ESLint instance and static methods for formatting.\n * @returns Whether all files were generated successfully.\n */\nexport async function generateAllActionTypesFiles(\n sources: SourceInfo[],\n eslint: ESLint | null,\n): Promise<boolean> {\n const outputFiles: string[] = [];\n\n for (const source of sources) {\n console.log(`\\n🔧 Processing ${source.name}...`);\n const outputDir = path.dirname(source.filePath);\n const baseFileName = path.basename(source.filePath, '.ts');\n const outputFile = path.join(\n outputDir,\n `${baseFileName}-method-action-types.ts`,\n );\n\n const generatedContent = generateActionTypesContent(source);\n await fs.promises.writeFile(outputFile, generatedContent, 'utf8');\n outputFiles.push(outputFile);\n console.log(`✅ Generated action types for ${source.name}`);\n }\n\n if (outputFiles.length > 0 && eslint) {\n console.log('\\n📝 Running ESLint on generated files...');\n\n const results = await eslint.instance.lintFiles(outputFiles);\n await eslint.eslintClass.outputFixes(results);\n const errors = eslint.eslintClass.getErrorResults(results);\n if (errors.length > 0) {\n console.error('❌ ESLint errors:', errors);\n return false;\n }\n console.log('✅ ESLint formatting applied');\n }\n\n return true;\n}\n"]}
import type { SourceInfo } from "./parse-source.cjs";
import type { ESLint } from "./types.cjs";
/**
* Generates action types files for all controllers/services.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files were generated successfully.
*/
export declare function generateAllActionTypesFiles(sources: SourceInfo[], eslint: ESLint | null): Promise<boolean>;
//# sourceMappingURL=fix.d.cts.map
{"version":3,"file":"fix.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/fix.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAgB;AAEtC;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CAgClB"}
import type { SourceInfo } from "./parse-source.mjs";
import type { ESLint } from "./types.mjs";
/**
* Generates action types files for all controllers/services.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files were generated successfully.
*/
export declare function generateAllActionTypesFiles(sources: SourceInfo[], eslint: ESLint | null): Promise<boolean>;
//# sourceMappingURL=fix.d.mts.map
{"version":3,"file":"fix.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/fix.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAgB;AAEtC;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CAgClB"}
import * as fs from "node:fs";
import * as path from "node:path";
import { generateActionTypesContent } from "./generate-content.mjs";
/**
* Generates action types files for all controllers/services.
*
* @param sources - Array of source information objects.
* @param eslint - Optional ESLint instance and static methods for formatting.
* @returns Whether all files were generated successfully.
*/
export async function generateAllActionTypesFiles(sources, eslint) {
const outputFiles = [];
for (const source of sources) {
console.log(`\n🔧 Processing ${source.name}...`);
const outputDir = path.dirname(source.filePath);
const baseFileName = path.basename(source.filePath, '.ts');
const outputFile = path.join(outputDir, `${baseFileName}-method-action-types.ts`);
const generatedContent = generateActionTypesContent(source);
await fs.promises.writeFile(outputFile, generatedContent, 'utf8');
outputFiles.push(outputFile);
console.log(`✅ Generated action types for ${source.name}`);
}
if (outputFiles.length > 0 && eslint) {
console.log('\n📝 Running ESLint on generated files...');
const results = await eslint.instance.lintFiles(outputFiles);
await eslint.eslintClass.outputFixes(results);
const errors = eslint.eslintClass.getErrorResults(results);
if (errors.length > 0) {
console.error('❌ ESLint errors:', errors);
return false;
}
console.log('✅ ESLint formatting applied');
}
return true;
}
//# sourceMappingURL=fix.mjs.map
{"version":3,"file":"fix.mjs","sourceRoot":"","sources":["../../src/generate-action-types/fix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB;AAC9B,OAAO,KAAK,IAAI,kBAAkB;AAElC,OAAO,EAAE,0BAA0B,EAAE,+BAA2B;AAIhE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,OAAqB,EACrB,MAAqB;IAErB,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAC1B,SAAS,EACT,GAAG,YAAY,yBAAyB,CACzC,CAAC;QAEF,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC;QAClE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC7D,MAAM,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YAC1C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { generateActionTypesContent } from './generate-content';\nimport type { SourceInfo } from './parse-source';\nimport type { ESLint } from './types';\n\n/**\n * Generates action types files for all controllers/services.\n *\n * @param sources - Array of source information objects.\n * @param eslint - Optional ESLint instance and static methods for formatting.\n * @returns Whether all files were generated successfully.\n */\nexport async function generateAllActionTypesFiles(\n sources: SourceInfo[],\n eslint: ESLint | null,\n): Promise<boolean> {\n const outputFiles: string[] = [];\n\n for (const source of sources) {\n console.log(`\\n🔧 Processing ${source.name}...`);\n const outputDir = path.dirname(source.filePath);\n const baseFileName = path.basename(source.filePath, '.ts');\n const outputFile = path.join(\n outputDir,\n `${baseFileName}-method-action-types.ts`,\n );\n\n const generatedContent = generateActionTypesContent(source);\n await fs.promises.writeFile(outputFile, generatedContent, 'utf8');\n outputFiles.push(outputFile);\n console.log(`✅ Generated action types for ${source.name}`);\n }\n\n if (outputFiles.length > 0 && eslint) {\n console.log('\\n📝 Running ESLint on generated files...');\n\n const results = await eslint.instance.lintFiles(outputFiles);\n await eslint.eslintClass.outputFixes(results);\n const errors = eslint.eslintClass.getErrorResults(results);\n if (errors.length > 0) {\n console.error('❌ ESLint errors:', errors);\n return false;\n }\n console.log('✅ ESLint formatting applied');\n }\n\n return true;\n}\n"]}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateActionTypesContent = void 0;
const path = __importStar(require("node:path"));
/**
* Generates the content for the action types file.
*
* @param source - The source information object (controller or service).
* @returns The content for the action types file.
*/
function generateActionTypesContent(source) {
const baseFileName = path.basename(source.filePath, '.ts');
const sourceImportPath = `./${baseFileName}`;
let content = `/**
* This file is auto generated.
* Do not edit manually.
*/
import type { ${source.name} } from '${sourceImportPath}';
`;
const actionTypeNames = [];
for (const method of source.methods) {
const capitalizedName = method.name.charAt(0).toUpperCase() + method.name.slice(1);
const actionTypeName = `${source.name}${capitalizedName}Action`;
const actionString = `${source.name}:${method.name}`;
actionTypeNames.push(actionTypeName);
if (method.jsDoc) {
content += `${method.jsDoc}\n`;
}
content += `export type ${actionTypeName} = {
type: \`${actionString}\`;
handler: ${source.name}['${method.name}'];
};\n\n`;
}
if (actionTypeNames.length > 0) {
const unionTypeName = `${source.name}MethodActions`;
content += `/**
* Union of all ${source.name} action types.
*/
export type ${unionTypeName} = ${actionTypeNames.join(' | ')};\n`;
}
return `${content.trimEnd()}\n`;
}
exports.generateActionTypesContent = generateActionTypesContent;
//# sourceMappingURL=generate-content.cjs.map
{"version":3,"file":"generate-content.cjs","sourceRoot":"","sources":["../../src/generate-action-types/generate-content.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAIlC;;;;;GAKG;AACH,SAAgB,0BAA0B,CAAC,MAAkB;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3D,MAAM,gBAAgB,GAAG,KAAK,YAAY,EAAE,CAAC;IAE7C,IAAI,OAAO,GAAG;;;;;gBAKA,MAAM,CAAC,IAAI,YAAY,gBAAgB;;CAEtD,CAAC;IAEA,MAAM,eAAe,GAAa,EAAE,CAAC;IAErC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,eAAe,GACnB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,eAAe,QAAQ,CAAC;QAChE,MAAM,YAAY,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAErD,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAErC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;QACjC,CAAC;QAED,OAAO,IAAI,eAAe,cAAc;YAChC,YAAY;aACX,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;OACjC,CAAC;IACN,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,aAAa,GAAG,GAAG,MAAM,CAAC,IAAI,eAAe,CAAC;QACpD,OAAO,IAAI;kBACG,MAAM,CAAC,IAAI;;cAEf,aAAa,MAAM,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAChE,CAAC;IAED,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;AAClC,CAAC;AA1CD,gEA0CC","sourcesContent":["import * as path from 'node:path';\n\nimport type { SourceInfo } from './parse-source';\n\n/**\n * Generates the content for the action types file.\n *\n * @param source - The source information object (controller or service).\n * @returns The content for the action types file.\n */\nexport function generateActionTypesContent(source: SourceInfo): string {\n const baseFileName = path.basename(source.filePath, '.ts');\n const sourceImportPath = `./${baseFileName}`;\n\n let content = `/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { ${source.name} } from '${sourceImportPath}';\n\n`;\n\n const actionTypeNames: string[] = [];\n\n for (const method of source.methods) {\n const capitalizedName =\n method.name.charAt(0).toUpperCase() + method.name.slice(1);\n const actionTypeName = `${source.name}${capitalizedName}Action`;\n const actionString = `${source.name}:${method.name}`;\n\n actionTypeNames.push(actionTypeName);\n\n if (method.jsDoc) {\n content += `${method.jsDoc}\\n`;\n }\n\n content += `export type ${actionTypeName} = {\n type: \\`${actionString}\\`;\n handler: ${source.name}['${method.name}'];\n};\\n\\n`;\n }\n\n if (actionTypeNames.length > 0) {\n const unionTypeName = `${source.name}MethodActions`;\n content += `/**\n * Union of all ${source.name} action types.\n */\nexport type ${unionTypeName} = ${actionTypeNames.join(' | ')};\\n`;\n }\n\n return `${content.trimEnd()}\\n`;\n}\n"]}
import type { SourceInfo } from "./parse-source.cjs";
/**
* Generates the content for the action types file.
*
* @param source - The source information object (controller or service).
* @returns The content for the action types file.
*/
export declare function generateActionTypesContent(source: SourceInfo): string;
//# sourceMappingURL=generate-content.d.cts.map
{"version":3,"file":"generate-content.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/generate-content.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0CrE"}
import type { SourceInfo } from "./parse-source.mjs";
/**
* Generates the content for the action types file.
*
* @param source - The source information object (controller or service).
* @returns The content for the action types file.
*/
export declare function generateActionTypesContent(source: SourceInfo): string;
//# sourceMappingURL=generate-content.d.mts.map
{"version":3,"file":"generate-content.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/generate-content.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0CrE"}
import * as path from "node:path";
/**
* Generates the content for the action types file.
*
* @param source - The source information object (controller or service).
* @returns The content for the action types file.
*/
export function generateActionTypesContent(source) {
const baseFileName = path.basename(source.filePath, '.ts');
const sourceImportPath = `./${baseFileName}`;
let content = `/**
* This file is auto generated.
* Do not edit manually.
*/
import type { ${source.name} } from '${sourceImportPath}';
`;
const actionTypeNames = [];
for (const method of source.methods) {
const capitalizedName = method.name.charAt(0).toUpperCase() + method.name.slice(1);
const actionTypeName = `${source.name}${capitalizedName}Action`;
const actionString = `${source.name}:${method.name}`;
actionTypeNames.push(actionTypeName);
if (method.jsDoc) {
content += `${method.jsDoc}\n`;
}
content += `export type ${actionTypeName} = {
type: \`${actionString}\`;
handler: ${source.name}['${method.name}'];
};\n\n`;
}
if (actionTypeNames.length > 0) {
const unionTypeName = `${source.name}MethodActions`;
content += `/**
* Union of all ${source.name} action types.
*/
export type ${unionTypeName} = ${actionTypeNames.join(' | ')};\n`;
}
return `${content.trimEnd()}\n`;
}
//# sourceMappingURL=generate-content.mjs.map
{"version":3,"file":"generate-content.mjs","sourceRoot":"","sources":["../../src/generate-action-types/generate-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,kBAAkB;AAIlC;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAkB;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3D,MAAM,gBAAgB,GAAG,KAAK,YAAY,EAAE,CAAC;IAE7C,IAAI,OAAO,GAAG;;;;;gBAKA,MAAM,CAAC,IAAI,YAAY,gBAAgB;;CAEtD,CAAC;IAEA,MAAM,eAAe,GAAa,EAAE,CAAC;IAErC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,eAAe,GACnB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,eAAe,QAAQ,CAAC;QAChE,MAAM,YAAY,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAErD,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAErC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;QACjC,CAAC;QAED,OAAO,IAAI,eAAe,cAAc;YAChC,YAAY;aACX,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;OACjC,CAAC;IACN,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,aAAa,GAAG,GAAG,MAAM,CAAC,IAAI,eAAe,CAAC;QACpD,OAAO,IAAI;kBACG,MAAM,CAAC,IAAI;;cAEf,aAAa,MAAM,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAChE,CAAC;IAED,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;AAClC,CAAC","sourcesContent":["import * as path from 'node:path';\n\nimport type { SourceInfo } from './parse-source';\n\n/**\n * Generates the content for the action types file.\n *\n * @param source - The source information object (controller or service).\n * @returns The content for the action types file.\n */\nexport function generateActionTypesContent(source: SourceInfo): string {\n const baseFileName = path.basename(source.filePath, '.ts');\n const sourceImportPath = `./${baseFileName}`;\n\n let content = `/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { ${source.name} } from '${sourceImportPath}';\n\n`;\n\n const actionTypeNames: string[] = [];\n\n for (const method of source.methods) {\n const capitalizedName =\n method.name.charAt(0).toUpperCase() + method.name.slice(1);\n const actionTypeName = `${source.name}${capitalizedName}Action`;\n const actionString = `${source.name}:${method.name}`;\n\n actionTypeNames.push(actionTypeName);\n\n if (method.jsDoc) {\n content += `${method.jsDoc}\\n`;\n }\n\n content += `export type ${actionTypeName} = {\n type: \\`${actionString}\\`;\n handler: ${source.name}['${method.name}'];\n};\\n\\n`;\n }\n\n if (actionTypeNames.length > 0) {\n const unionTypeName = `${source.name}MethodActions`;\n content += `/**\n * Union of all ${source.name} action types.\n */\nexport type ${unionTypeName} = ${actionTypeNames.join(' | ')};\\n`;\n }\n\n return `${content.trimEnd()}\\n`;\n}\n"]}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findSourcesWithExposedMethods = exports.parseSourceFile = void 0;
const utils_1 = require("@metamask/utils");
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const typescript_1 = require("typescript");
/**
* Extracts JSDoc comment from a method declaration.
*
* @param node - The method declaration node.
* @param source - The source file.
* @returns The JSDoc comment.
*/
function extractJSDoc(node, source) {
const jsDocTags = (0, typescript_1.getJSDocCommentsAndTags)(node);
if (jsDocTags.length === 0) {
return '';
}
const jsDoc = jsDocTags[0];
if ((0, typescript_1.isJSDoc)(jsDoc)) {
const fullText = source.getFullText();
const start = jsDoc.getFullStart();
const end = jsDoc.getEnd();
const rawJsDoc = fullText.substring(start, end).trim();
return formatJSDoc(rawJsDoc);
}
// istanbul ignore next: defensive check — getJSDocCommentsAndTags always returns JSDoc nodes
return '';
}
/**
* Formats JSDoc comments to have consistent indentation for the generated file.
*
* @param rawJsDoc - The raw JSDoc comment from the source.
* @returns The formatted JSDoc comment.
*/
function formatJSDoc(rawJsDoc) {
const lines = rawJsDoc.split('\n');
const formattedLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (i === 0) {
formattedLines.push('/**');
}
else if (i === lines.length - 1) {
formattedLines.push(' */');
}
else {
const trimmed = line.trim();
if (trimmed.startsWith('*')) {
const content = trimmed.substring(1).trim();
formattedLines.push(content ? ` * ${content}` : ' *');
}
else {
formattedLines.push(trimmed ? ` * ${trimmed}` : ' *');
}
}
}
return formattedLines.join('\n');
}
/**
* Visits AST nodes to find exposed methods and controller/service class.
*
* @param context - The visitor context.
* @returns A function to visit nodes.
*/
function createASTVisitor(context) {
function visitNode(node) {
if ((0, typescript_1.isVariableStatement)(node)) {
const declaration = node.declarationList.declarations[0];
if ((0, typescript_1.isIdentifier)(declaration.name) &&
declaration.name.text === 'MESSENGER_EXPOSED_METHODS') {
if (declaration.initializer) {
let arrayExpression;
if ((0, typescript_1.isArrayLiteralExpression)(declaration.initializer)) {
arrayExpression = declaration.initializer;
}
else if ((0, typescript_1.isAsExpression)(declaration.initializer) &&
(0, typescript_1.isArrayLiteralExpression)(declaration.initializer.expression)) {
arrayExpression = declaration.initializer.expression;
}
if (arrayExpression) {
context.exposedMethods = arrayExpression.elements
.filter(typescript_1.isStringLiteral)
.map((element) => element.text);
}
}
}
}
if ((0, typescript_1.isClassDeclaration)(node) && node.name) {
const classText = node.name.text;
if (classText.includes('Controller') || classText.includes('Service')) {
context.className = classText;
const seenMethods = new Set();
for (const member of node.members) {
if ((0, typescript_1.isMethodDeclaration)(member) &&
member.name &&
(0, typescript_1.isIdentifier)(member.name)) {
const methodName = member.name.text;
if (context.exposedMethods.includes(methodName) &&
!seenMethods.has(methodName)) {
seenMethods.add(methodName);
const jsDoc = extractJSDoc(member, context.sourceFile);
context.methods.push({
name: methodName,
jsDoc,
});
}
}
}
}
}
(0, typescript_1.forEachChild)(node, visitNode);
}
return visitNode;
}
/**
* Create a TypeScript program for the given file by locating the nearest
* tsconfig.json.
*
* @param filePath - Absolute path to the source file.
* @returns A TypeScript program, or null if no tsconfig was found.
*/
function createProgramForFile(filePath) {
const configPath = (0, typescript_1.findConfigFile)(path.dirname(filePath), typescript_1.sys.fileExists.bind(typescript_1.sys), 'tsconfig.json');
if (!configPath) {
return null;
}
const { config, error } = (0, typescript_1.readConfigFile)(configPath, typescript_1.sys.readFile.bind(typescript_1.sys));
if (error) {
return null;
}
const parsedConfig = (0, typescript_1.parseJsonConfigFileContent)(config, typescript_1.sys, path.dirname(configPath));
return (0, typescript_1.createProgram)({
rootNames: parsedConfig.fileNames,
options: parsedConfig.options,
});
}
/**
* Find a class declaration with the given name in a source file.
*
* @param source - The source file to search.
* @param className - The class name to look for.
* @returns The class declaration node, or null if not found.
*/
function findClassInSourceFile(source, className) {
return (source.statements.find((node) => (0, typescript_1.isClassDeclaration)(node) && node.name?.text === className) ?? // istanbul ignore next: class is always found when called from parseSourceFile
null);
}
/**
* Search through the class hierarchy of a TypeScript type to find the
* declaration of a method with the given name.
*
* @param classType - The class type to search.
* @param methodName - The method name to look for.
* @returns The method declaration node, or null if not found.
*/
function findMethodInHierarchy(classType, methodName) {
const symbol = classType.getProperty(methodName);
if (!symbol) {
return null;
}
const declarations = symbol.getDeclarations();
// istanbul ignore next: defensive check — symbols from getProperty always have declarations
if (!declarations) {
return null;
}
for (const declaration of declarations) {
if ((0, typescript_1.isMethodDeclaration)(declaration)) {
return declaration;
}
}
// istanbul ignore next: defensive fallback — property found but not a method declaration
return null;
}
/**
* Check if a path is a directory.
*
* @param pathValue - The path to check.
* @returns True if the path is a directory, false otherwise.
*/
async function isDirectory(pathValue) {
try {
const stats = await fs.promises.stat(pathValue);
return stats.isDirectory();
}
catch (error) {
if ((0, utils_1.isObject)(error) &&
(0, utils_1.hasProperty)(error, 'code') &&
error.code === 'ENOENT') {
return false;
}
throw error;
}
}
/**
* Parses a source file to extract exposed methods and their metadata.
*
* @param filePath - Path to the controller/service file to parse.
* @returns Source information or null if parsing fails.
*/
async function parseSourceFile(filePath) {
try {
const content = await fs.promises.readFile(filePath, 'utf8');
const source = (0, typescript_1.createSourceFile)(filePath, content, typescript_1.ScriptTarget.Latest, true);
const context = {
exposedMethods: [],
className: '',
methods: [],
sourceFile: source,
};
createASTVisitor(context)(source);
if (context.exposedMethods.length === 0 || !context.className) {
return null;
}
const foundMethodNames = new Set(context.methods.map((method) => method.name));
const inheritedMethodNames = context.exposedMethods.filter((name) => !foundMethodNames.has(name));
if (inheritedMethodNames.length > 0) {
const program = createProgramForFile(filePath);
const checker = program?.getTypeChecker();
const programSourceFile = program?.getSourceFile(filePath);
(0, utils_1.assert)(checker, `Type checker could not be created for "${filePath}". Ensure a valid tsconfig.json is present.`);
(0, utils_1.assert)(programSourceFile, `Source file "${filePath}" not found in program.`);
const classNode = findClassInSourceFile(programSourceFile, context.className);
(0, utils_1.assert)(classNode, `Class "${context.className}" not found in "${filePath}".`);
const classType = checker.getTypeAtLocation(classNode);
for (const methodName of inheritedMethodNames) {
const methodDeclaration = findMethodInHierarchy(classType, methodName);
const jsDoc = methodDeclaration
? extractJSDoc(methodDeclaration, methodDeclaration.getSourceFile())
: '';
context.methods.push({ name: methodName, jsDoc });
}
}
return {
name: context.className,
filePath,
methods: context.methods,
};
}
catch (error) {
console.error(`Error parsing ${filePath}:`, error);
return null;
}
}
exports.parseSourceFile = parseSourceFile;
/**
* Recursively get all files in a directory and its subdirectories.
*
* @param directory - The directory to search.
* @returns An array of file paths.
*/
const EXCLUDED_DIRECTORIES = new Set([
'node_modules',
'dist',
'.git',
'coverage',
]);
async function getFiles(directory) {
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
const files = await Promise.all(entries.map(async (entry) => {
const fullPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
return EXCLUDED_DIRECTORIES.has(entry.name)
? []
: await getFiles(fullPath);
}
return fullPath;
}));
return files.flat();
}
/**
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
* Searches recursively through subdirectories.
*
* @param sourcePath - Path to the folder where controllers/services are located.
* @returns A list of source information objects.
*/
async function findSourcesWithExposedMethods(sourcePath) {
const srcPath = path.resolve(globalThis.process.cwd(), sourcePath);
const sources = [];
if (!(await isDirectory(srcPath))) {
throw new Error(`The specified path is not a directory: ${srcPath}`);
}
const srcFiles = await getFiles(srcPath);
for (const file of srcFiles) {
if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {
continue;
}
const content = await fs.promises.readFile(file, 'utf8');
if (content.includes('MESSENGER_EXPOSED_METHODS')) {
const sourceInfo = await parseSourceFile(file);
if (sourceInfo) {
sources.push(sourceInfo);
}
}
}
return sources;
}
exports.findSourcesWithExposedMethods = findSourcesWithExposedMethods;
//# sourceMappingURL=parse-source.cjs.map
{"version":3,"file":"parse-source.cjs","sourceRoot":"","sources":["../../src/generate-action-types/parse-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, hasProperty, isObject } from '@metamask/utils';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type {\n ArrayLiteralExpression,\n ClassDeclaration,\n MethodDeclaration,\n Node as TSNode,\n Program,\n SourceFile,\n Type,\n} from 'typescript';\nimport {\n ScriptTarget,\n createProgram,\n createSourceFile,\n findConfigFile,\n forEachChild,\n getJSDocCommentsAndTags,\n isArrayLiteralExpression,\n isAsExpression,\n isClassDeclaration,\n isIdentifier,\n isJSDoc,\n isMethodDeclaration,\n isStringLiteral,\n isVariableStatement,\n parseJsonConfigFileContent,\n readConfigFile,\n sys,\n} from 'typescript';\n\nexport type MethodInfo = {\n name: string;\n jsDoc: string;\n};\n\nexport type SourceInfo = {\n name: string;\n filePath: string;\n methods: MethodInfo[];\n};\n\ntype VisitorContext = {\n exposedMethods: string[];\n className: string;\n methods: MethodInfo[];\n sourceFile: SourceFile;\n};\n\n/**\n * Extracts JSDoc comment from a method declaration.\n *\n * @param node - The method declaration node.\n * @param source - The source file.\n * @returns The JSDoc comment.\n */\nfunction extractJSDoc(node: MethodDeclaration, source: SourceFile): string {\n const jsDocTags = getJSDocCommentsAndTags(node);\n if (jsDocTags.length === 0) {\n return '';\n }\n\n const jsDoc = jsDocTags[0];\n if (isJSDoc(jsDoc)) {\n const fullText = source.getFullText();\n const start = jsDoc.getFullStart();\n const end = jsDoc.getEnd();\n const rawJsDoc = fullText.substring(start, end).trim();\n return formatJSDoc(rawJsDoc);\n }\n\n // istanbul ignore next: defensive check — getJSDocCommentsAndTags always returns JSDoc nodes\n return '';\n}\n\n/**\n * Formats JSDoc comments to have consistent indentation for the generated file.\n *\n * @param rawJsDoc - The raw JSDoc comment from the source.\n * @returns The formatted JSDoc comment.\n */\nfunction formatJSDoc(rawJsDoc: string): string {\n const lines = rawJsDoc.split('\\n');\n const formattedLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (i === 0) {\n formattedLines.push('/**');\n } else if (i === lines.length - 1) {\n formattedLines.push(' */');\n } else {\n const trimmed = line.trim();\n if (trimmed.startsWith('*')) {\n const content = trimmed.substring(1).trim();\n formattedLines.push(content ? ` * ${content}` : ' *');\n } else {\n formattedLines.push(trimmed ? ` * ${trimmed}` : ' *');\n }\n }\n }\n\n return formattedLines.join('\\n');\n}\n\n/**\n * Visits AST nodes to find exposed methods and controller/service class.\n *\n * @param context - The visitor context.\n * @returns A function to visit nodes.\n */\nfunction createASTVisitor(context: VisitorContext): (node: TSNode) => void {\n function visitNode(node: TSNode): void {\n if (isVariableStatement(node)) {\n const declaration = node.declarationList.declarations[0];\n if (\n isIdentifier(declaration.name) &&\n declaration.name.text === 'MESSENGER_EXPOSED_METHODS'\n ) {\n if (declaration.initializer) {\n let arrayExpression: ArrayLiteralExpression | undefined;\n\n if (isArrayLiteralExpression(declaration.initializer)) {\n arrayExpression = declaration.initializer;\n } else if (\n isAsExpression(declaration.initializer) &&\n isArrayLiteralExpression(declaration.initializer.expression)\n ) {\n arrayExpression = declaration.initializer.expression;\n }\n\n if (arrayExpression) {\n context.exposedMethods = arrayExpression.elements\n .filter(isStringLiteral)\n .map((element) => element.text);\n }\n }\n }\n }\n\n if (isClassDeclaration(node) && node.name) {\n const classText = node.name.text;\n if (classText.includes('Controller') || classText.includes('Service')) {\n context.className = classText;\n\n const seenMethods = new Set<string>();\n for (const member of node.members) {\n if (\n isMethodDeclaration(member) &&\n member.name &&\n isIdentifier(member.name)\n ) {\n const methodName = member.name.text;\n if (\n context.exposedMethods.includes(methodName) &&\n !seenMethods.has(methodName)\n ) {\n seenMethods.add(methodName);\n const jsDoc = extractJSDoc(member, context.sourceFile);\n context.methods.push({\n name: methodName,\n jsDoc,\n });\n }\n }\n }\n }\n }\n\n forEachChild(node, visitNode);\n }\n\n return visitNode;\n}\n\n/**\n * Create a TypeScript program for the given file by locating the nearest\n * tsconfig.json.\n *\n * @param filePath - Absolute path to the source file.\n * @returns A TypeScript program, or null if no tsconfig was found.\n */\nfunction createProgramForFile(filePath: string): Program | null {\n const configPath = findConfigFile(\n path.dirname(filePath),\n sys.fileExists.bind(sys),\n 'tsconfig.json',\n );\n if (!configPath) {\n return null;\n }\n\n const { config, error } = readConfigFile(configPath, sys.readFile.bind(sys));\n\n if (error) {\n return null;\n }\n\n const parsedConfig = parseJsonConfigFileContent(\n config,\n sys,\n path.dirname(configPath),\n );\n\n return createProgram({\n rootNames: parsedConfig.fileNames,\n options: parsedConfig.options,\n });\n}\n\n/**\n * Find a class declaration with the given name in a source file.\n *\n * @param source - The source file to search.\n * @param className - The class name to look for.\n * @returns The class declaration node, or null if not found.\n */\nfunction findClassInSourceFile(\n source: SourceFile,\n className: string,\n): ClassDeclaration | null {\n return (\n source.statements.find(\n (node): node is ClassDeclaration =>\n isClassDeclaration(node) && node.name?.text === className,\n ) ?? // istanbul ignore next: class is always found when called from parseSourceFile\n null\n );\n}\n\n/**\n * Search through the class hierarchy of a TypeScript type to find the\n * declaration of a method with the given name.\n *\n * @param classType - The class type to search.\n * @param methodName - The method name to look for.\n * @returns The method declaration node, or null if not found.\n */\nfunction findMethodInHierarchy(\n classType: Type,\n methodName: string,\n): MethodDeclaration | null {\n const symbol = classType.getProperty(methodName);\n if (!symbol) {\n return null;\n }\n\n const declarations = symbol.getDeclarations();\n // istanbul ignore next: defensive check — symbols from getProperty always have declarations\n if (!declarations) {\n return null;\n }\n\n for (const declaration of declarations) {\n if (isMethodDeclaration(declaration)) {\n return declaration;\n }\n }\n\n // istanbul ignore next: defensive fallback — property found but not a method declaration\n return null;\n}\n\n/**\n * Check if a path is a directory.\n *\n * @param pathValue - The path to check.\n * @returns True if the path is a directory, false otherwise.\n */\nasync function isDirectory(pathValue: string): Promise<boolean> {\n try {\n const stats = await fs.promises.stat(pathValue);\n return stats.isDirectory();\n } catch (error) {\n if (\n isObject(error) &&\n hasProperty(error, 'code') &&\n error.code === 'ENOENT'\n ) {\n return false;\n }\n\n throw error;\n }\n}\n\n/**\n * Parses a source file to extract exposed methods and their metadata.\n *\n * @param filePath - Path to the controller/service file to parse.\n * @returns Source information or null if parsing fails.\n */\nexport async function parseSourceFile(\n filePath: string,\n): Promise<SourceInfo | null> {\n try {\n const content = await fs.promises.readFile(filePath, 'utf8');\n const source = createSourceFile(\n filePath,\n content,\n ScriptTarget.Latest,\n true,\n );\n\n const context: VisitorContext = {\n exposedMethods: [],\n className: '',\n methods: [],\n sourceFile: source,\n };\n\n createASTVisitor(context)(source);\n\n if (context.exposedMethods.length === 0 || !context.className) {\n return null;\n }\n\n const foundMethodNames = new Set(\n context.methods.map((method) => method.name),\n );\n\n const inheritedMethodNames = context.exposedMethods.filter(\n (name) => !foundMethodNames.has(name),\n );\n\n if (inheritedMethodNames.length > 0) {\n const program = createProgramForFile(filePath);\n const checker = program?.getTypeChecker();\n const programSourceFile = program?.getSourceFile(filePath);\n\n assert(\n checker,\n `Type checker could not be created for \"${filePath}\". Ensure a valid tsconfig.json is present.`,\n );\n\n assert(\n programSourceFile,\n `Source file \"${filePath}\" not found in program.`,\n );\n\n const classNode = findClassInSourceFile(\n programSourceFile,\n context.className,\n );\n\n assert(\n classNode,\n `Class \"${context.className}\" not found in \"${filePath}\".`,\n );\n\n const classType = checker.getTypeAtLocation(classNode);\n for (const methodName of inheritedMethodNames) {\n const methodDeclaration = findMethodInHierarchy(classType, methodName);\n\n const jsDoc = methodDeclaration\n ? extractJSDoc(methodDeclaration, methodDeclaration.getSourceFile())\n : '';\n context.methods.push({ name: methodName, jsDoc });\n }\n }\n\n return {\n name: context.className,\n filePath,\n methods: context.methods,\n };\n } catch (error) {\n console.error(`Error parsing ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Recursively get all files in a directory and its subdirectories.\n *\n * @param directory - The directory to search.\n * @returns An array of file paths.\n */\nconst EXCLUDED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n '.git',\n 'coverage',\n]);\n\nasync function getFiles(directory: string): Promise<string[]> {\n const entries = await fs.promises.readdir(directory, { withFileTypes: true });\n const files = await Promise.all(\n entries.map(async (entry) => {\n const fullPath = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n return EXCLUDED_DIRECTORIES.has(entry.name)\n ? []\n : await getFiles(fullPath);\n }\n return fullPath;\n }),\n );\n\n return files.flat();\n}\n\n/**\n * Finds all source files that have MESSENGER_EXPOSED_METHODS constants.\n * Searches recursively through subdirectories.\n *\n * @param sourcePath - Path to the folder where controllers/services are located.\n * @returns A list of source information objects.\n */\nexport async function findSourcesWithExposedMethods(\n sourcePath: string,\n): Promise<SourceInfo[]> {\n const srcPath = path.resolve(globalThis.process.cwd(), sourcePath);\n const sources: SourceInfo[] = [];\n\n if (!(await isDirectory(srcPath))) {\n throw new Error(`The specified path is not a directory: ${srcPath}`);\n }\n\n const srcFiles = await getFiles(srcPath);\n\n for (const file of srcFiles) {\n if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {\n continue;\n }\n\n const content = await fs.promises.readFile(file, 'utf8');\n\n if (content.includes('MESSENGER_EXPOSED_METHODS')) {\n const sourceInfo = await parseSourceFile(file);\n if (sourceInfo) {\n sources.push(sourceInfo);\n }\n }\n }\n\n return sources;\n}\n"]}
export type MethodInfo = {
name: string;
jsDoc: string;
};
export type SourceInfo = {
name: string;
filePath: string;
methods: MethodInfo[];
};
/**
* Parses a source file to extract exposed methods and their metadata.
*
* @param filePath - Path to the controller/service file to parse.
* @returns Source information or null if parsing fails.
*/
export declare function parseSourceFile(filePath: string): Promise<SourceInfo | null>;
/**
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
* Searches recursively through subdirectories.
*
* @param sourcePath - Path to the folder where controllers/services are located.
* @returns A list of source information objects.
*/
export declare function findSourcesWithExposedMethods(sourcePath: string): Promise<SourceInfo[]>;
//# sourceMappingURL=parse-source.d.cts.map
{"version":3,"file":"parse-source.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/parse-source.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB,CAAC;AAsPF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA4E5B;AAgCD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB"}
export type MethodInfo = {
name: string;
jsDoc: string;
};
export type SourceInfo = {
name: string;
filePath: string;
methods: MethodInfo[];
};
/**
* Parses a source file to extract exposed methods and their metadata.
*
* @param filePath - Path to the controller/service file to parse.
* @returns Source information or null if parsing fails.
*/
export declare function parseSourceFile(filePath: string): Promise<SourceInfo | null>;
/**
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
* Searches recursively through subdirectories.
*
* @param sourcePath - Path to the folder where controllers/services are located.
* @returns A list of source information objects.
*/
export declare function findSourcesWithExposedMethods(sourcePath: string): Promise<SourceInfo[]>;
//# sourceMappingURL=parse-source.d.mts.map
{"version":3,"file":"parse-source.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/parse-source.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB,CAAC;AAsPF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA4E5B;AAgCD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB"}
import { assert, hasProperty, isObject } from "@metamask/utils";
import * as fs from "node:fs";
import * as path from "node:path";
import $typescript from "typescript";
const { ScriptTarget, createProgram, createSourceFile, findConfigFile, forEachChild, getJSDocCommentsAndTags, isArrayLiteralExpression, isAsExpression, isClassDeclaration, isIdentifier, isJSDoc, isMethodDeclaration, isStringLiteral, isVariableStatement, parseJsonConfigFileContent, readConfigFile, sys } = $typescript;
/**
* Extracts JSDoc comment from a method declaration.
*
* @param node - The method declaration node.
* @param source - The source file.
* @returns The JSDoc comment.
*/
function extractJSDoc(node, source) {
const jsDocTags = getJSDocCommentsAndTags(node);
if (jsDocTags.length === 0) {
return '';
}
const jsDoc = jsDocTags[0];
if (isJSDoc(jsDoc)) {
const fullText = source.getFullText();
const start = jsDoc.getFullStart();
const end = jsDoc.getEnd();
const rawJsDoc = fullText.substring(start, end).trim();
return formatJSDoc(rawJsDoc);
}
// istanbul ignore next: defensive check — getJSDocCommentsAndTags always returns JSDoc nodes
return '';
}
/**
* Formats JSDoc comments to have consistent indentation for the generated file.
*
* @param rawJsDoc - The raw JSDoc comment from the source.
* @returns The formatted JSDoc comment.
*/
function formatJSDoc(rawJsDoc) {
const lines = rawJsDoc.split('\n');
const formattedLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (i === 0) {
formattedLines.push('/**');
}
else if (i === lines.length - 1) {
formattedLines.push(' */');
}
else {
const trimmed = line.trim();
if (trimmed.startsWith('*')) {
const content = trimmed.substring(1).trim();
formattedLines.push(content ? ` * ${content}` : ' *');
}
else {
formattedLines.push(trimmed ? ` * ${trimmed}` : ' *');
}
}
}
return formattedLines.join('\n');
}
/**
* Visits AST nodes to find exposed methods and controller/service class.
*
* @param context - The visitor context.
* @returns A function to visit nodes.
*/
function createASTVisitor(context) {
function visitNode(node) {
if (isVariableStatement(node)) {
const declaration = node.declarationList.declarations[0];
if (isIdentifier(declaration.name) &&
declaration.name.text === 'MESSENGER_EXPOSED_METHODS') {
if (declaration.initializer) {
let arrayExpression;
if (isArrayLiteralExpression(declaration.initializer)) {
arrayExpression = declaration.initializer;
}
else if (isAsExpression(declaration.initializer) &&
isArrayLiteralExpression(declaration.initializer.expression)) {
arrayExpression = declaration.initializer.expression;
}
if (arrayExpression) {
context.exposedMethods = arrayExpression.elements
.filter(isStringLiteral)
.map((element) => element.text);
}
}
}
}
if (isClassDeclaration(node) && node.name) {
const classText = node.name.text;
if (classText.includes('Controller') || classText.includes('Service')) {
context.className = classText;
const seenMethods = new Set();
for (const member of node.members) {
if (isMethodDeclaration(member) &&
member.name &&
isIdentifier(member.name)) {
const methodName = member.name.text;
if (context.exposedMethods.includes(methodName) &&
!seenMethods.has(methodName)) {
seenMethods.add(methodName);
const jsDoc = extractJSDoc(member, context.sourceFile);
context.methods.push({
name: methodName,
jsDoc,
});
}
}
}
}
}
forEachChild(node, visitNode);
}
return visitNode;
}
/**
* Create a TypeScript program for the given file by locating the nearest
* tsconfig.json.
*
* @param filePath - Absolute path to the source file.
* @returns A TypeScript program, or null if no tsconfig was found.
*/
function createProgramForFile(filePath) {
const configPath = findConfigFile(path.dirname(filePath), sys.fileExists.bind(sys), 'tsconfig.json');
if (!configPath) {
return null;
}
const { config, error } = readConfigFile(configPath, sys.readFile.bind(sys));
if (error) {
return null;
}
const parsedConfig = parseJsonConfigFileContent(config, sys, path.dirname(configPath));
return createProgram({
rootNames: parsedConfig.fileNames,
options: parsedConfig.options,
});
}
/**
* Find a class declaration with the given name in a source file.
*
* @param source - The source file to search.
* @param className - The class name to look for.
* @returns The class declaration node, or null if not found.
*/
function findClassInSourceFile(source, className) {
return (source.statements.find((node) => isClassDeclaration(node) && node.name?.text === className) ?? // istanbul ignore next: class is always found when called from parseSourceFile
null);
}
/**
* Search through the class hierarchy of a TypeScript type to find the
* declaration of a method with the given name.
*
* @param classType - The class type to search.
* @param methodName - The method name to look for.
* @returns The method declaration node, or null if not found.
*/
function findMethodInHierarchy(classType, methodName) {
const symbol = classType.getProperty(methodName);
if (!symbol) {
return null;
}
const declarations = symbol.getDeclarations();
// istanbul ignore next: defensive check — symbols from getProperty always have declarations
if (!declarations) {
return null;
}
for (const declaration of declarations) {
if (isMethodDeclaration(declaration)) {
return declaration;
}
}
// istanbul ignore next: defensive fallback — property found but not a method declaration
return null;
}
/**
* Check if a path is a directory.
*
* @param pathValue - The path to check.
* @returns True if the path is a directory, false otherwise.
*/
async function isDirectory(pathValue) {
try {
const stats = await fs.promises.stat(pathValue);
return stats.isDirectory();
}
catch (error) {
if (isObject(error) &&
hasProperty(error, 'code') &&
error.code === 'ENOENT') {
return false;
}
throw error;
}
}
/**
* Parses a source file to extract exposed methods and their metadata.
*
* @param filePath - Path to the controller/service file to parse.
* @returns Source information or null if parsing fails.
*/
export async function parseSourceFile(filePath) {
try {
const content = await fs.promises.readFile(filePath, 'utf8');
const source = createSourceFile(filePath, content, ScriptTarget.Latest, true);
const context = {
exposedMethods: [],
className: '',
methods: [],
sourceFile: source,
};
createASTVisitor(context)(source);
if (context.exposedMethods.length === 0 || !context.className) {
return null;
}
const foundMethodNames = new Set(context.methods.map((method) => method.name));
const inheritedMethodNames = context.exposedMethods.filter((name) => !foundMethodNames.has(name));
if (inheritedMethodNames.length > 0) {
const program = createProgramForFile(filePath);
const checker = program?.getTypeChecker();
const programSourceFile = program?.getSourceFile(filePath);
assert(checker, `Type checker could not be created for "${filePath}". Ensure a valid tsconfig.json is present.`);
assert(programSourceFile, `Source file "${filePath}" not found in program.`);
const classNode = findClassInSourceFile(programSourceFile, context.className);
assert(classNode, `Class "${context.className}" not found in "${filePath}".`);
const classType = checker.getTypeAtLocation(classNode);
for (const methodName of inheritedMethodNames) {
const methodDeclaration = findMethodInHierarchy(classType, methodName);
const jsDoc = methodDeclaration
? extractJSDoc(methodDeclaration, methodDeclaration.getSourceFile())
: '';
context.methods.push({ name: methodName, jsDoc });
}
}
return {
name: context.className,
filePath,
methods: context.methods,
};
}
catch (error) {
console.error(`Error parsing ${filePath}:`, error);
return null;
}
}
/**
* Recursively get all files in a directory and its subdirectories.
*
* @param directory - The directory to search.
* @returns An array of file paths.
*/
const EXCLUDED_DIRECTORIES = new Set([
'node_modules',
'dist',
'.git',
'coverage',
]);
async function getFiles(directory) {
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
const files = await Promise.all(entries.map(async (entry) => {
const fullPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
return EXCLUDED_DIRECTORIES.has(entry.name)
? []
: await getFiles(fullPath);
}
return fullPath;
}));
return files.flat();
}
/**
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
* Searches recursively through subdirectories.
*
* @param sourcePath - Path to the folder where controllers/services are located.
* @returns A list of source information objects.
*/
export async function findSourcesWithExposedMethods(sourcePath) {
const srcPath = path.resolve(globalThis.process.cwd(), sourcePath);
const sources = [];
if (!(await isDirectory(srcPath))) {
throw new Error(`The specified path is not a directory: ${srcPath}`);
}
const srcFiles = await getFiles(srcPath);
for (const file of srcFiles) {
if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {
continue;
}
const content = await fs.promises.readFile(file, 'utf8');
if (content.includes('MESSENGER_EXPOSED_METHODS')) {
const sourceInfo = await parseSourceFile(file);
if (sourceInfo) {
sources.push(sourceInfo);
}
}
}
return sources;
}
//# sourceMappingURL=parse-source.mjs.map
{"version":3,"file":"parse-source.mjs","sourceRoot":"","sources":["../../src/generate-action-types/parse-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, hasProperty, isObject } from '@metamask/utils';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type {\n ArrayLiteralExpression,\n ClassDeclaration,\n MethodDeclaration,\n Node as TSNode,\n Program,\n SourceFile,\n Type,\n} from 'typescript';\nimport {\n ScriptTarget,\n createProgram,\n createSourceFile,\n findConfigFile,\n forEachChild,\n getJSDocCommentsAndTags,\n isArrayLiteralExpression,\n isAsExpression,\n isClassDeclaration,\n isIdentifier,\n isJSDoc,\n isMethodDeclaration,\n isStringLiteral,\n isVariableStatement,\n parseJsonConfigFileContent,\n readConfigFile,\n sys,\n} from 'typescript';\n\nexport type MethodInfo = {\n name: string;\n jsDoc: string;\n};\n\nexport type SourceInfo = {\n name: string;\n filePath: string;\n methods: MethodInfo[];\n};\n\ntype VisitorContext = {\n exposedMethods: string[];\n className: string;\n methods: MethodInfo[];\n sourceFile: SourceFile;\n};\n\n/**\n * Extracts JSDoc comment from a method declaration.\n *\n * @param node - The method declaration node.\n * @param source - The source file.\n * @returns The JSDoc comment.\n */\nfunction extractJSDoc(node: MethodDeclaration, source: SourceFile): string {\n const jsDocTags = getJSDocCommentsAndTags(node);\n if (jsDocTags.length === 0) {\n return '';\n }\n\n const jsDoc = jsDocTags[0];\n if (isJSDoc(jsDoc)) {\n const fullText = source.getFullText();\n const start = jsDoc.getFullStart();\n const end = jsDoc.getEnd();\n const rawJsDoc = fullText.substring(start, end).trim();\n return formatJSDoc(rawJsDoc);\n }\n\n // istanbul ignore next: defensive check — getJSDocCommentsAndTags always returns JSDoc nodes\n return '';\n}\n\n/**\n * Formats JSDoc comments to have consistent indentation for the generated file.\n *\n * @param rawJsDoc - The raw JSDoc comment from the source.\n * @returns The formatted JSDoc comment.\n */\nfunction formatJSDoc(rawJsDoc: string): string {\n const lines = rawJsDoc.split('\\n');\n const formattedLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (i === 0) {\n formattedLines.push('/**');\n } else if (i === lines.length - 1) {\n formattedLines.push(' */');\n } else {\n const trimmed = line.trim();\n if (trimmed.startsWith('*')) {\n const content = trimmed.substring(1).trim();\n formattedLines.push(content ? ` * ${content}` : ' *');\n } else {\n formattedLines.push(trimmed ? ` * ${trimmed}` : ' *');\n }\n }\n }\n\n return formattedLines.join('\\n');\n}\n\n/**\n * Visits AST nodes to find exposed methods and controller/service class.\n *\n * @param context - The visitor context.\n * @returns A function to visit nodes.\n */\nfunction createASTVisitor(context: VisitorContext): (node: TSNode) => void {\n function visitNode(node: TSNode): void {\n if (isVariableStatement(node)) {\n const declaration = node.declarationList.declarations[0];\n if (\n isIdentifier(declaration.name) &&\n declaration.name.text === 'MESSENGER_EXPOSED_METHODS'\n ) {\n if (declaration.initializer) {\n let arrayExpression: ArrayLiteralExpression | undefined;\n\n if (isArrayLiteralExpression(declaration.initializer)) {\n arrayExpression = declaration.initializer;\n } else if (\n isAsExpression(declaration.initializer) &&\n isArrayLiteralExpression(declaration.initializer.expression)\n ) {\n arrayExpression = declaration.initializer.expression;\n }\n\n if (arrayExpression) {\n context.exposedMethods = arrayExpression.elements\n .filter(isStringLiteral)\n .map((element) => element.text);\n }\n }\n }\n }\n\n if (isClassDeclaration(node) && node.name) {\n const classText = node.name.text;\n if (classText.includes('Controller') || classText.includes('Service')) {\n context.className = classText;\n\n const seenMethods = new Set<string>();\n for (const member of node.members) {\n if (\n isMethodDeclaration(member) &&\n member.name &&\n isIdentifier(member.name)\n ) {\n const methodName = member.name.text;\n if (\n context.exposedMethods.includes(methodName) &&\n !seenMethods.has(methodName)\n ) {\n seenMethods.add(methodName);\n const jsDoc = extractJSDoc(member, context.sourceFile);\n context.methods.push({\n name: methodName,\n jsDoc,\n });\n }\n }\n }\n }\n }\n\n forEachChild(node, visitNode);\n }\n\n return visitNode;\n}\n\n/**\n * Create a TypeScript program for the given file by locating the nearest\n * tsconfig.json.\n *\n * @param filePath - Absolute path to the source file.\n * @returns A TypeScript program, or null if no tsconfig was found.\n */\nfunction createProgramForFile(filePath: string): Program | null {\n const configPath = findConfigFile(\n path.dirname(filePath),\n sys.fileExists.bind(sys),\n 'tsconfig.json',\n );\n if (!configPath) {\n return null;\n }\n\n const { config, error } = readConfigFile(configPath, sys.readFile.bind(sys));\n\n if (error) {\n return null;\n }\n\n const parsedConfig = parseJsonConfigFileContent(\n config,\n sys,\n path.dirname(configPath),\n );\n\n return createProgram({\n rootNames: parsedConfig.fileNames,\n options: parsedConfig.options,\n });\n}\n\n/**\n * Find a class declaration with the given name in a source file.\n *\n * @param source - The source file to search.\n * @param className - The class name to look for.\n * @returns The class declaration node, or null if not found.\n */\nfunction findClassInSourceFile(\n source: SourceFile,\n className: string,\n): ClassDeclaration | null {\n return (\n source.statements.find(\n (node): node is ClassDeclaration =>\n isClassDeclaration(node) && node.name?.text === className,\n ) ?? // istanbul ignore next: class is always found when called from parseSourceFile\n null\n );\n}\n\n/**\n * Search through the class hierarchy of a TypeScript type to find the\n * declaration of a method with the given name.\n *\n * @param classType - The class type to search.\n * @param methodName - The method name to look for.\n * @returns The method declaration node, or null if not found.\n */\nfunction findMethodInHierarchy(\n classType: Type,\n methodName: string,\n): MethodDeclaration | null {\n const symbol = classType.getProperty(methodName);\n if (!symbol) {\n return null;\n }\n\n const declarations = symbol.getDeclarations();\n // istanbul ignore next: defensive check — symbols from getProperty always have declarations\n if (!declarations) {\n return null;\n }\n\n for (const declaration of declarations) {\n if (isMethodDeclaration(declaration)) {\n return declaration;\n }\n }\n\n // istanbul ignore next: defensive fallback — property found but not a method declaration\n return null;\n}\n\n/**\n * Check if a path is a directory.\n *\n * @param pathValue - The path to check.\n * @returns True if the path is a directory, false otherwise.\n */\nasync function isDirectory(pathValue: string): Promise<boolean> {\n try {\n const stats = await fs.promises.stat(pathValue);\n return stats.isDirectory();\n } catch (error) {\n if (\n isObject(error) &&\n hasProperty(error, 'code') &&\n error.code === 'ENOENT'\n ) {\n return false;\n }\n\n throw error;\n }\n}\n\n/**\n * Parses a source file to extract exposed methods and their metadata.\n *\n * @param filePath - Path to the controller/service file to parse.\n * @returns Source information or null if parsing fails.\n */\nexport async function parseSourceFile(\n filePath: string,\n): Promise<SourceInfo | null> {\n try {\n const content = await fs.promises.readFile(filePath, 'utf8');\n const source = createSourceFile(\n filePath,\n content,\n ScriptTarget.Latest,\n true,\n );\n\n const context: VisitorContext = {\n exposedMethods: [],\n className: '',\n methods: [],\n sourceFile: source,\n };\n\n createASTVisitor(context)(source);\n\n if (context.exposedMethods.length === 0 || !context.className) {\n return null;\n }\n\n const foundMethodNames = new Set(\n context.methods.map((method) => method.name),\n );\n\n const inheritedMethodNames = context.exposedMethods.filter(\n (name) => !foundMethodNames.has(name),\n );\n\n if (inheritedMethodNames.length > 0) {\n const program = createProgramForFile(filePath);\n const checker = program?.getTypeChecker();\n const programSourceFile = program?.getSourceFile(filePath);\n\n assert(\n checker,\n `Type checker could not be created for \"${filePath}\". Ensure a valid tsconfig.json is present.`,\n );\n\n assert(\n programSourceFile,\n `Source file \"${filePath}\" not found in program.`,\n );\n\n const classNode = findClassInSourceFile(\n programSourceFile,\n context.className,\n );\n\n assert(\n classNode,\n `Class \"${context.className}\" not found in \"${filePath}\".`,\n );\n\n const classType = checker.getTypeAtLocation(classNode);\n for (const methodName of inheritedMethodNames) {\n const methodDeclaration = findMethodInHierarchy(classType, methodName);\n\n const jsDoc = methodDeclaration\n ? extractJSDoc(methodDeclaration, methodDeclaration.getSourceFile())\n : '';\n context.methods.push({ name: methodName, jsDoc });\n }\n }\n\n return {\n name: context.className,\n filePath,\n methods: context.methods,\n };\n } catch (error) {\n console.error(`Error parsing ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Recursively get all files in a directory and its subdirectories.\n *\n * @param directory - The directory to search.\n * @returns An array of file paths.\n */\nconst EXCLUDED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n '.git',\n 'coverage',\n]);\n\nasync function getFiles(directory: string): Promise<string[]> {\n const entries = await fs.promises.readdir(directory, { withFileTypes: true });\n const files = await Promise.all(\n entries.map(async (entry) => {\n const fullPath = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n return EXCLUDED_DIRECTORIES.has(entry.name)\n ? []\n : await getFiles(fullPath);\n }\n return fullPath;\n }),\n );\n\n return files.flat();\n}\n\n/**\n * Finds all source files that have MESSENGER_EXPOSED_METHODS constants.\n * Searches recursively through subdirectories.\n *\n * @param sourcePath - Path to the folder where controllers/services are located.\n * @returns A list of source information objects.\n */\nexport async function findSourcesWithExposedMethods(\n sourcePath: string,\n): Promise<SourceInfo[]> {\n const srcPath = path.resolve(globalThis.process.cwd(), sourcePath);\n const sources: SourceInfo[] = [];\n\n if (!(await isDirectory(srcPath))) {\n throw new Error(`The specified path is not a directory: ${srcPath}`);\n }\n\n const srcFiles = await getFiles(srcPath);\n\n for (const file of srcFiles) {\n if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {\n continue;\n }\n\n const content = await fs.promises.readFile(file, 'utf8');\n\n if (content.includes('MESSENGER_EXPOSED_METHODS')) {\n const sourceInfo = await parseSourceFile(file);\n if (sourceInfo) {\n sources.push(sourceInfo);\n }\n }\n }\n\n return sources;\n}\n"]}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.cjs.map
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../src/generate-action-types/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ESLint as eslintClass } from 'eslint';\n\nexport type ESLint = {\n instance: eslintClass;\n eslintClass: typeof eslintClass;\n};\n"]}
import type { ESLint as eslintClass } from "eslint";
export type ESLint = {
instance: eslintClass;
eslintClass: typeof eslintClass;
};
//# sourceMappingURL=types.d.cts.map
{"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../src/generate-action-types/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,eAAe;AAEpD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,OAAO,WAAW,CAAC;CACjC,CAAC"}
import type { ESLint as eslintClass } from "eslint";
export type ESLint = {
instance: eslintClass;
eslintClass: typeof eslintClass;
};
//# sourceMappingURL=types.d.mts.map
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../src/generate-action-types/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,eAAe;AAEpD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,OAAO,WAAW,CAAC;CACjC,CAAC"}
export {};
//# sourceMappingURL=types.mjs.map
{"version":3,"file":"types.mjs","sourceRoot":"","sources":["../../src/generate-action-types/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ESLint as eslintClass } from 'eslint';\n\nexport type ESLint = {\n instance: eslintClass;\n eslintClass: typeof eslintClass;\n};\n"]}
+11
-1

@@ -10,2 +10,11 @@ # Changelog

## [1.1.0]
### Added
- Add `generate-action-types` CLI tool ([#8264](https://github.com/MetaMask/core/pull/8264))
- Generates TypeScript action type files for controllers and services that define `MESSENGER_EXPOSED_METHODS`.
- Available as a CLI binary (`messenger-generate-action-types`).
- `typescript` and `eslint` are peer dependencies.
## [1.0.0]

@@ -68,3 +77,4 @@

[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/messenger@1.0.0...HEAD
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/messenger@1.1.0...HEAD
[1.1.0]: https://github.com/MetaMask/core/compare/@metamask/messenger@1.0.0...@metamask/messenger@1.1.0
[1.0.0]: https://github.com/MetaMask/core/compare/@metamask/messenger@0.3.0...@metamask/messenger@1.0.0

@@ -71,0 +81,0 @@ [0.3.0]: https://github.com/MetaMask/core/compare/@metamask/messenger@0.2.0...@metamask/messenger@0.3.0

{
"name": "@metamask/messenger",
"version": "1.0.0",
"version": "1.1.0",
"description": "A type-safe message bus library",

@@ -34,2 +34,5 @@ "keywords": [

"types": "./dist/index.d.cts",
"bin": {
"messenger-generate-action-types": "./dist/generate-action-types/cli.mjs"
},
"files": [

@@ -50,2 +53,6 @@ "dist/"

},
"dependencies": {
"@metamask/utils": "^11.9.0",
"yargs": "^17.7.2"
},
"devDependencies": {

@@ -55,3 +62,6 @@ "@metamask/auto-changelog": "^3.4.4",

"@types/jest": "^29.5.14",
"@types/yargs": "^17.0.32",
"deepmerge": "^4.2.2",
"eslint": "^9.39.1",
"execa": "^5.0.0",
"immer": "^9.0.6",

@@ -64,2 +74,6 @@ "jest": "^29.7.0",

},
"peerDependencies": {
"eslint": ">=8",
"typescript": ">=5.0.0"
},
"engines": {

@@ -66,0 +80,0 @@ "node": "^18.18 || >=20"