@metamask/messenger
Advanced tools
| "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 |
+15
-1
| { | ||
| "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" |
358340
60.13%68
240%2356
106.12%4
Infinity%13
30%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added