@metamask-previews/messenger 0.3.0-preview-3f2e0ea ā 0.3.0-preview-a462582
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generate-action-types/cli.cjs +2 -2
- package/dist/generate-action-types/cli.cjs.map +1 -1
- package/dist/generate-action-types/cli.mjs +2 -2
- package/dist/generate-action-types/cli.mjs.map +1 -1
- package/dist/generate-action-types/parse-source.cjs +18 -4
- package/dist/generate-action-types/parse-source.cjs.map +1 -1
- package/dist/generate-action-types/parse-source.d.cts +1 -0
- package/dist/generate-action-types/parse-source.d.cts.map +1 -1
- package/dist/generate-action-types/parse-source.d.mts +1 -0
- package/dist/generate-action-types/parse-source.d.mts.map +1 -1
- package/dist/generate-action-types/parse-source.mjs +18 -4
- package/dist/generate-action-types/parse-source.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -59,8 +59,8 @@ async function loadESLint() {
|
|
|
59
59
|
});
|
|
60
60
|
return {
|
|
61
61
|
instance,
|
|
62
|
-
outputFixes: ESLintClass.outputFixes,
|
|
63
|
-
getErrorResults: ESLintClass.getErrorResults,
|
|
62
|
+
outputFixes: ESLintClass.outputFixes.bind(ESLintClass),
|
|
63
|
+
getErrorResults: ESLintClass.getErrorResults.bind(ESLintClass),
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":";;;;;;AAEA,kDAA0B;AAE1B,uCAAgD;AAChD,mCAAoD;AACpD,qDAA+D;AAS/D;;;;GAIG;AACH,KAAK,UAAU,yBAAyB;IACtC,MAAM,EACJ,KAAK,EACL,GAAG,EACH,IAAI,EAAE,UAAU,GACjB,GAAG,MAAM,IAAA,eAAK,EAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC9C,OAAO,CACN,WAAW,EACX,oEAAoE,EACpE,CAAC,aAAa,EAAE,EAAE;QAChB,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE;YAC/B,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,2DAA2D;YAC7D,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;IACL,CAAC,CACF;SACA,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,qDAAqD;QAClE,OAAO,EAAE,KAAK;KACf,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,mCAAmC;QAChD,OAAO,EAAE,KAAK;KACf,CAAC;SACD,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACd,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,IAAI,CAAC;IAEV,OAAO;QACL,KAAK;QACL,GAAG;QACH,UAAU,EAAE,UAAoB;KACjC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,UAAU;IACvB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,UAAU,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC;YAC/B,GAAG,EAAE,IAAI;YACT,uBAAuB,EAAE,KAAK;SAC/B,CAAC,CAAC;QACH,OAAO;YACL,QAAQ;YACR,WAAW,EAAE,WAAW,CAAC,WAAW;
|
|
1
|
+
{"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 * Uses `yargs` to parse the arguments given to the script.\n *\n * @returns The command line arguments.\n */\nasync function parseCommandLineArguments(): Promise<CommandLineArguments> {\n const {\n check,\n fix,\n path: sourcePath,\n } = await yargs(globalThis.process.argv.slice(2))\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 outputFixes: ESLintClass.outputFixes.bind(ESLintClass),\n getErrorResults: ESLintClass.getErrorResults.bind(ESLintClass),\n };\n } catch {\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\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 await generateAllActionTypesFiles(sources, eslint);\n console.log('\\nš All action types generated successfully!');\n } else {\n await checkActionTypesFiles(sources, eslint);\n }\n}\n\nmain().catch((error) => {\n console.error('ā Script failed:', error);\n globalThis.process.exitCode = 1;\n});\n"]}
|
|
@@ -54,8 +54,8 @@ async function loadESLint() {
|
|
|
54
54
|
});
|
|
55
55
|
return {
|
|
56
56
|
instance,
|
|
57
|
-
outputFixes: ESLintClass.outputFixes,
|
|
58
|
-
getErrorResults: ESLintClass.getErrorResults,
|
|
57
|
+
outputFixes: ESLintClass.outputFixes.bind(ESLintClass),
|
|
58
|
+
getErrorResults: ESLintClass.getErrorResults.bind(ESLintClass),
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","sourceRoot":"","sources":["../../src/generate-action-types/cli.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,cAAc;AAE1B,OAAO,EAAE,qBAAqB,EAAE,oBAAgB;AAChD,OAAO,EAAE,2BAA2B,EAAE,kBAAc;AACpD,OAAO,EAAE,6BAA6B,EAAE,2BAAuB;AAS/D;;;;GAIG;AACH,KAAK,UAAU,yBAAyB;IACtC,MAAM,EACJ,KAAK,EACL,GAAG,EACH,IAAI,EAAE,UAAU,GACjB,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC9C,OAAO,CACN,WAAW,EACX,oEAAoE,EACpE,CAAC,aAAa,EAAE,EAAE;QAChB,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE;YAC/B,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,2DAA2D;YAC7D,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;IACL,CAAC,CACF;SACA,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,qDAAqD;QAClE,OAAO,EAAE,KAAK;KACf,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,mCAAmC;QAChD,OAAO,EAAE,KAAK;KACf,CAAC;SACD,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACd,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,IAAI,CAAC;IAEV,OAAO;QACL,KAAK;QACL,GAAG;QACH,UAAU,EAAE,UAAoB;KACjC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,UAAU;IACvB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,UAAU,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC;YAC/B,GAAG,EAAE,IAAI;YACT,uBAAuB,EAAE,KAAK;SAC/B,CAAC,CAAC;QACH,OAAO;YACL,QAAQ;YACR,WAAW,EAAE,WAAW,CAAC,WAAW;
|
|
1
|
+
{"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 * Uses `yargs` to parse the arguments given to the script.\n *\n * @returns The command line arguments.\n */\nasync function parseCommandLineArguments(): Promise<CommandLineArguments> {\n const {\n check,\n fix,\n path: sourcePath,\n } = await yargs(globalThis.process.argv.slice(2))\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 outputFixes: ESLintClass.outputFixes.bind(ESLintClass),\n getErrorResults: ESLintClass.getErrorResults.bind(ESLintClass),\n };\n } catch {\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\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 await generateAllActionTypesFiles(sources, eslint);\n console.log('\\nš All action types generated successfully!');\n } else {\n await checkActionTypesFiles(sources, eslint);\n }\n}\n\nmain().catch((error) => {\n console.error('ā Script failed:', error);\n globalThis.process.exitCode = 1;\n});\n"]}
|
|
@@ -267,8 +267,23 @@ async function parseSourceFile(filePath) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
exports.parseSourceFile = parseSourceFile;
|
|
270
|
+
/**
|
|
271
|
+
* Recursively get all files in a directory and its subdirectories.
|
|
272
|
+
*
|
|
273
|
+
* @param directory - The directory to search.
|
|
274
|
+
* @returns An array of file paths.
|
|
275
|
+
*/
|
|
276
|
+
async function getFiles(directory) {
|
|
277
|
+
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
278
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
279
|
+
const fullPath = path.join(directory, entry.name);
|
|
280
|
+
return entry.isDirectory() ? await getFiles(fullPath) : fullPath;
|
|
281
|
+
}));
|
|
282
|
+
return files.flat();
|
|
283
|
+
}
|
|
270
284
|
/**
|
|
271
285
|
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
|
|
286
|
+
* Searches recursively through subdirectories.
|
|
272
287
|
*
|
|
273
288
|
* @param sourcePath - Path to the folder where controllers/services are located.
|
|
274
289
|
* @returns A list of source information objects.
|
|
@@ -279,15 +294,14 @@ async function findSourcesWithExposedMethods(sourcePath) {
|
|
|
279
294
|
if (!(await isDirectory(srcPath))) {
|
|
280
295
|
throw new Error(`The specified path is not a directory: ${srcPath}`);
|
|
281
296
|
}
|
|
282
|
-
const srcFiles = await
|
|
297
|
+
const srcFiles = await getFiles(srcPath);
|
|
283
298
|
for (const file of srcFiles) {
|
|
284
299
|
if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {
|
|
285
300
|
continue;
|
|
286
301
|
}
|
|
287
|
-
const
|
|
288
|
-
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
302
|
+
const content = await fs.promises.readFile(file, 'utf8');
|
|
289
303
|
if (content.includes('MESSENGER_EXPOSED_METHODS')) {
|
|
290
|
-
const sourceInfo = await parseSourceFile(
|
|
304
|
+
const sourceInfo = await parseSourceFile(file);
|
|
291
305
|
if (sourceInfo) {
|
|
292
306
|
sources.push(sourceInfo);
|
|
293
307
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 * Finds all source files that have MESSENGER_EXPOSED_METHODS constants.\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 fs.promises.readdir(srcPath);\n\n for (const file of srcFiles) {\n if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {\n continue;\n }\n\n const filePath = path.join(srcPath, file);\n const content = await fs.promises.readFile(filePath, 'utf8');\n\n if (content.includes('MESSENGER_EXPOSED_METHODS')) {\n const sourceInfo = await parseSourceFile(filePath);\n if (sourceInfo) {\n sources.push(sourceInfo);\n }\n }\n }\n\n return sources;\n}\n"]}
|
|
1
|
+
{"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 */\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 return entry.isDirectory() ? await getFiles(fullPath) : 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"]}
|
|
@@ -16,6 +16,7 @@ export type SourceInfo = {
|
|
|
16
16
|
export declare function parseSourceFile(filePath: string): Promise<SourceInfo | null>;
|
|
17
17
|
/**
|
|
18
18
|
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
|
|
19
|
+
* Searches recursively through subdirectories.
|
|
19
20
|
*
|
|
20
21
|
* @param sourcePath - Path to the folder where controllers/services are located.
|
|
21
22
|
* @returns A list of source information objects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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;
|
|
1
|
+
{"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;AAoBD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB"}
|
|
@@ -16,6 +16,7 @@ export type SourceInfo = {
|
|
|
16
16
|
export declare function parseSourceFile(filePath: string): Promise<SourceInfo | null>;
|
|
17
17
|
/**
|
|
18
18
|
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
|
|
19
|
+
* Searches recursively through subdirectories.
|
|
19
20
|
*
|
|
20
21
|
* @param sourcePath - Path to the folder where controllers/services are located.
|
|
21
22
|
* @returns A list of source information objects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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;
|
|
1
|
+
{"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;AAoBD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB"}
|
|
@@ -241,8 +241,23 @@ export async function parseSourceFile(filePath) {
|
|
|
241
241
|
return null;
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Recursively get all files in a directory and its subdirectories.
|
|
246
|
+
*
|
|
247
|
+
* @param directory - The directory to search.
|
|
248
|
+
* @returns An array of file paths.
|
|
249
|
+
*/
|
|
250
|
+
async function getFiles(directory) {
|
|
251
|
+
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
252
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
253
|
+
const fullPath = path.join(directory, entry.name);
|
|
254
|
+
return entry.isDirectory() ? await getFiles(fullPath) : fullPath;
|
|
255
|
+
}));
|
|
256
|
+
return files.flat();
|
|
257
|
+
}
|
|
244
258
|
/**
|
|
245
259
|
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
|
|
260
|
+
* Searches recursively through subdirectories.
|
|
246
261
|
*
|
|
247
262
|
* @param sourcePath - Path to the folder where controllers/services are located.
|
|
248
263
|
* @returns A list of source information objects.
|
|
@@ -253,15 +268,14 @@ export async function findSourcesWithExposedMethods(sourcePath) {
|
|
|
253
268
|
if (!(await isDirectory(srcPath))) {
|
|
254
269
|
throw new Error(`The specified path is not a directory: ${srcPath}`);
|
|
255
270
|
}
|
|
256
|
-
const srcFiles = await
|
|
271
|
+
const srcFiles = await getFiles(srcPath);
|
|
257
272
|
for (const file of srcFiles) {
|
|
258
273
|
if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {
|
|
259
274
|
continue;
|
|
260
275
|
}
|
|
261
|
-
const
|
|
262
|
-
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
276
|
+
const content = await fs.promises.readFile(file, 'utf8');
|
|
263
277
|
if (content.includes('MESSENGER_EXPOSED_METHODS')) {
|
|
264
|
-
const sourceInfo = await parseSourceFile(
|
|
278
|
+
const sourceInfo = await parseSourceFile(file);
|
|
265
279
|
if (sourceInfo) {
|
|
266
280
|
sources.push(sourceInfo);
|
|
267
281
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 * Finds all source files that have MESSENGER_EXPOSED_METHODS constants.\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 fs.promises.readdir(srcPath);\n\n for (const file of srcFiles) {\n if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {\n continue;\n }\n\n const filePath = path.join(srcPath, file);\n const content = await fs.promises.readFile(filePath, 'utf8');\n\n if (content.includes('MESSENGER_EXPOSED_METHODS')) {\n const sourceInfo = await parseSourceFile(filePath);\n if (sourceInfo) {\n sources.push(sourceInfo);\n }\n }\n }\n\n return sources;\n}\n"]}
|
|
1
|
+
{"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 */\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 return entry.isDirectory() ? await getFiles(fullPath) : 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"]}
|