@metamask-previews/messenger-cli 0.0.0-preview-23f4dfd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +20 -0
- package/README.md +15 -0
- package/dist/check.cjs +105 -0
- package/dist/check.cjs.map +1 -0
- package/dist/check.d.cts +11 -0
- package/dist/check.d.cts.map +1 -0
- package/dist/check.d.mts +11 -0
- package/dist/check.d.mts.map +1 -0
- package/dist/check.mjs +78 -0
- package/dist/check.mjs.map +1 -0
- package/dist/cli.cjs +107 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +3 -0
- package/dist/cli.d.cts.map +1 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +102 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/fix.cjs +63 -0
- package/dist/fix.cjs.map +1 -0
- package/dist/fix.d.cts +11 -0
- package/dist/fix.d.cts.map +1 -0
- package/dist/fix.d.mts +11 -0
- package/dist/fix.d.mts.map +1 -0
- package/dist/fix.mjs +36 -0
- package/dist/fix.mjs.map +1 -0
- package/dist/generate-content.cjs +69 -0
- package/dist/generate-content.cjs.map +1 -0
- package/dist/generate-content.d.cts +9 -0
- package/dist/generate-content.d.cts.map +1 -0
- package/dist/generate-content.d.mts +9 -0
- package/dist/generate-content.d.mts.map +1 -0
- package/dist/generate-content.mjs +42 -0
- package/dist/generate-content.mjs.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -0
- package/dist/parse-source.cjs +324 -0
- package/dist/parse-source.cjs.map +1 -0
- package/dist/parse-source.d.cts +25 -0
- package/dist/parse-source.d.cts.map +1 -0
- package/dist/parse-source.d.mts +25 -0
- package/dist/parse-source.d.mts.map +1 -0
- package/dist/parse-source.mjs +297 -0
- package/dist/parse-source.mjs.map +1 -0
- package/dist/types.cjs +3 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +6 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +6 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +2 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +77 -0
package/dist/fix.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SourceInfo } from "./parse-source.mjs";
|
|
2
|
+
import type { ESLint } from "./types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Generates action types files for all controllers/services.
|
|
5
|
+
*
|
|
6
|
+
* @param sources - Array of source information objects.
|
|
7
|
+
* @param eslint - Optional ESLint instance and static methods for formatting.
|
|
8
|
+
* @returns Whether all files were generated successfully.
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateAllActionTypesFiles(sources: SourceInfo[], eslint: ESLint | null): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=fix.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix.d.mts","sourceRoot":"","sources":["../src/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"}
|
package/dist/fix.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { generateActionTypesContent } from "./generate-content.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Generates action types files for all controllers/services.
|
|
6
|
+
*
|
|
7
|
+
* @param sources - Array of source information objects.
|
|
8
|
+
* @param eslint - Optional ESLint instance and static methods for formatting.
|
|
9
|
+
* @returns Whether all files were generated successfully.
|
|
10
|
+
*/
|
|
11
|
+
export async function generateAllActionTypesFiles(sources, eslint) {
|
|
12
|
+
const outputFiles = [];
|
|
13
|
+
for (const source of sources) {
|
|
14
|
+
console.log(`\nš§ Processing ${source.name}...`);
|
|
15
|
+
const outputDir = path.dirname(source.filePath);
|
|
16
|
+
const baseFileName = path.basename(source.filePath, '.ts');
|
|
17
|
+
const outputFile = path.join(outputDir, `${baseFileName}-method-action-types.ts`);
|
|
18
|
+
const generatedContent = generateActionTypesContent(source);
|
|
19
|
+
await fs.promises.writeFile(outputFile, generatedContent, 'utf8');
|
|
20
|
+
outputFiles.push(outputFile);
|
|
21
|
+
console.log(`ā
Generated action types for ${source.name}`);
|
|
22
|
+
}
|
|
23
|
+
if (outputFiles.length > 0 && eslint) {
|
|
24
|
+
console.log('\nš Running ESLint on generated files...');
|
|
25
|
+
const results = await eslint.instance.lintFiles(outputFiles);
|
|
26
|
+
await eslint.eslintClass.outputFixes(results);
|
|
27
|
+
const errors = eslint.eslintClass.getErrorResults(results);
|
|
28
|
+
if (errors.length > 0) {
|
|
29
|
+
console.error('ā ESLint errors:', errors);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
console.log('ā
ESLint formatting applied');
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=fix.mjs.map
|
package/dist/fix.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix.mjs","sourceRoot":"","sources":["../src/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"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.generateActionTypesContent = void 0;
|
|
27
|
+
const path = __importStar(require("node:path"));
|
|
28
|
+
/**
|
|
29
|
+
* Generates the content for the action types file.
|
|
30
|
+
*
|
|
31
|
+
* @param source - The source information object (controller or service).
|
|
32
|
+
* @returns The content for the action types file.
|
|
33
|
+
*/
|
|
34
|
+
function generateActionTypesContent(source) {
|
|
35
|
+
const baseFileName = path.basename(source.filePath, '.ts');
|
|
36
|
+
const sourceImportPath = `./${baseFileName}`;
|
|
37
|
+
let content = `/**
|
|
38
|
+
* This file is auto generated.
|
|
39
|
+
* Do not edit manually.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import type { ${source.name} } from '${sourceImportPath}';
|
|
43
|
+
|
|
44
|
+
`;
|
|
45
|
+
const actionTypeNames = [];
|
|
46
|
+
for (const method of source.methods) {
|
|
47
|
+
const capitalizedName = method.name.charAt(0).toUpperCase() + method.name.slice(1);
|
|
48
|
+
const actionTypeName = `${source.name}${capitalizedName}Action`;
|
|
49
|
+
const actionString = `${source.name}:${method.name}`;
|
|
50
|
+
actionTypeNames.push(actionTypeName);
|
|
51
|
+
if (method.jsDoc) {
|
|
52
|
+
content += `${method.jsDoc}\n`;
|
|
53
|
+
}
|
|
54
|
+
content += `export type ${actionTypeName} = {
|
|
55
|
+
type: \`${actionString}\`;
|
|
56
|
+
handler: ${source.name}['${method.name}'];
|
|
57
|
+
};\n\n`;
|
|
58
|
+
}
|
|
59
|
+
if (actionTypeNames.length > 0) {
|
|
60
|
+
const unionTypeName = `${source.name}MethodActions`;
|
|
61
|
+
content += `/**
|
|
62
|
+
* Union of all ${source.name} action types.
|
|
63
|
+
*/
|
|
64
|
+
export type ${unionTypeName} = ${actionTypeNames.join(' | ')};\n`;
|
|
65
|
+
}
|
|
66
|
+
return `${content.trimEnd()}\n`;
|
|
67
|
+
}
|
|
68
|
+
exports.generateActionTypesContent = generateActionTypesContent;
|
|
69
|
+
//# sourceMappingURL=generate-content.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-content.cjs","sourceRoot":"","sources":["../src/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"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SourceInfo } from "./parse-source.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Generates the content for the action types file.
|
|
4
|
+
*
|
|
5
|
+
* @param source - The source information object (controller or service).
|
|
6
|
+
* @returns The content for the action types file.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateActionTypesContent(source: SourceInfo): string;
|
|
9
|
+
//# sourceMappingURL=generate-content.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-content.d.cts","sourceRoot":"","sources":["../src/generate-content.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0CrE"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SourceInfo } from "./parse-source.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Generates the content for the action types file.
|
|
4
|
+
*
|
|
5
|
+
* @param source - The source information object (controller or service).
|
|
6
|
+
* @returns The content for the action types file.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateActionTypesContent(source: SourceInfo): string;
|
|
9
|
+
//# sourceMappingURL=generate-content.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-content.d.mts","sourceRoot":"","sources":["../src/generate-content.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0CrE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Generates the content for the action types file.
|
|
4
|
+
*
|
|
5
|
+
* @param source - The source information object (controller or service).
|
|
6
|
+
* @returns The content for the action types file.
|
|
7
|
+
*/
|
|
8
|
+
export function generateActionTypesContent(source) {
|
|
9
|
+
const baseFileName = path.basename(source.filePath, '.ts');
|
|
10
|
+
const sourceImportPath = `./${baseFileName}`;
|
|
11
|
+
let content = `/**
|
|
12
|
+
* This file is auto generated.
|
|
13
|
+
* Do not edit manually.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { ${source.name} } from '${sourceImportPath}';
|
|
17
|
+
|
|
18
|
+
`;
|
|
19
|
+
const actionTypeNames = [];
|
|
20
|
+
for (const method of source.methods) {
|
|
21
|
+
const capitalizedName = method.name.charAt(0).toUpperCase() + method.name.slice(1);
|
|
22
|
+
const actionTypeName = `${source.name}${capitalizedName}Action`;
|
|
23
|
+
const actionString = `${source.name}:${method.name}`;
|
|
24
|
+
actionTypeNames.push(actionTypeName);
|
|
25
|
+
if (method.jsDoc) {
|
|
26
|
+
content += `${method.jsDoc}\n`;
|
|
27
|
+
}
|
|
28
|
+
content += `export type ${actionTypeName} = {
|
|
29
|
+
type: \`${actionString}\`;
|
|
30
|
+
handler: ${source.name}['${method.name}'];
|
|
31
|
+
};\n\n`;
|
|
32
|
+
}
|
|
33
|
+
if (actionTypeNames.length > 0) {
|
|
34
|
+
const unionTypeName = `${source.name}MethodActions`;
|
|
35
|
+
content += `/**
|
|
36
|
+
* Union of all ${source.name} action types.
|
|
37
|
+
*/
|
|
38
|
+
export type ${unionTypeName} = ${actionTypeNames.join(' | ')};\n`;
|
|
39
|
+
}
|
|
40
|
+
return `${content.trimEnd()}\n`;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=generate-content.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-content.mjs","sourceRoot":"","sources":["../src/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"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseSourceFile = exports.findSourcesWithExposedMethods = exports.generateActionTypesContent = exports.generateAllActionTypesFiles = exports.checkActionTypesFiles = void 0;
|
|
4
|
+
var check_1 = require("./check.cjs");
|
|
5
|
+
Object.defineProperty(exports, "checkActionTypesFiles", { enumerable: true, get: function () { return check_1.checkActionTypesFiles; } });
|
|
6
|
+
var fix_1 = require("./fix.cjs");
|
|
7
|
+
Object.defineProperty(exports, "generateAllActionTypesFiles", { enumerable: true, get: function () { return fix_1.generateAllActionTypesFiles; } });
|
|
8
|
+
var generate_content_1 = require("./generate-content.cjs");
|
|
9
|
+
Object.defineProperty(exports, "generateActionTypesContent", { enumerable: true, get: function () { return generate_content_1.generateActionTypesContent; } });
|
|
10
|
+
var parse_source_1 = require("./parse-source.cjs");
|
|
11
|
+
Object.defineProperty(exports, "findSourcesWithExposedMethods", { enumerable: true, get: function () { return parse_source_1.findSourcesWithExposedMethods; } });
|
|
12
|
+
Object.defineProperty(exports, "parseSourceFile", { enumerable: true, get: function () { return parse_source_1.parseSourceFile; } });
|
|
13
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAgD;AAAvC,8GAAA,qBAAqB,OAAA;AAC9B,iCAAoD;AAA3C,kHAAA,2BAA2B,OAAA;AACpC,2DAAgE;AAAvD,8HAAA,0BAA0B,OAAA;AACnC,mDAAgF;AAAvE,6HAAA,6BAA6B,OAAA;AAAE,+GAAA,eAAe,OAAA","sourcesContent":["export { checkActionTypesFiles } from './check';\nexport { generateAllActionTypesFiles } from './fix';\nexport { generateActionTypesContent } from './generate-content';\nexport { findSourcesWithExposedMethods, parseSourceFile } from './parse-source';\nexport type { SourceInfo, MethodInfo } from './parse-source';\nexport type { ESLint } from './types';\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { checkActionTypesFiles } from "./check.cjs";
|
|
2
|
+
export { generateAllActionTypesFiles } from "./fix.cjs";
|
|
3
|
+
export { generateActionTypesContent } from "./generate-content.cjs";
|
|
4
|
+
export { findSourcesWithExposedMethods, parseSourceFile } from "./parse-source.cjs";
|
|
5
|
+
export type { SourceInfo, MethodInfo } from "./parse-source.cjs";
|
|
6
|
+
export type { ESLint } from "./types.cjs";
|
|
7
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,oBAAgB;AAChD,OAAO,EAAE,2BAA2B,EAAE,kBAAc;AACpD,OAAO,EAAE,0BAA0B,EAAE,+BAA2B;AAChE,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,2BAAuB;AAChF,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,2BAAuB;AAC7D,YAAY,EAAE,MAAM,EAAE,oBAAgB"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { checkActionTypesFiles } from "./check.mjs";
|
|
2
|
+
export { generateAllActionTypesFiles } from "./fix.mjs";
|
|
3
|
+
export { generateActionTypesContent } from "./generate-content.mjs";
|
|
4
|
+
export { findSourcesWithExposedMethods, parseSourceFile } from "./parse-source.mjs";
|
|
5
|
+
export type { SourceInfo, MethodInfo } from "./parse-source.mjs";
|
|
6
|
+
export type { ESLint } from "./types.mjs";
|
|
7
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,oBAAgB;AAChD,OAAO,EAAE,2BAA2B,EAAE,kBAAc;AACpD,OAAO,EAAE,0BAA0B,EAAE,+BAA2B;AAChE,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,2BAAuB;AAChF,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,2BAAuB;AAC7D,YAAY,EAAE,MAAM,EAAE,oBAAgB"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { checkActionTypesFiles } from "./check.mjs";
|
|
2
|
+
export { generateAllActionTypesFiles } from "./fix.mjs";
|
|
3
|
+
export { generateActionTypesContent } from "./generate-content.mjs";
|
|
4
|
+
export { findSourcesWithExposedMethods, parseSourceFile } from "./parse-source.mjs";
|
|
5
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,oBAAgB;AAChD,OAAO,EAAE,2BAA2B,EAAE,kBAAc;AACpD,OAAO,EAAE,0BAA0B,EAAE,+BAA2B;AAChE,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,2BAAuB","sourcesContent":["export { checkActionTypesFiles } from './check';\nexport { generateAllActionTypesFiles } from './fix';\nexport { generateActionTypesContent } from './generate-content';\nexport { findSourcesWithExposedMethods, parseSourceFile } from './parse-source';\nexport type { SourceInfo, MethodInfo } from './parse-source';\nexport type { ESLint } from './types';\n"]}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.findSourcesWithExposedMethods = exports.parseSourceFile = void 0;
|
|
27
|
+
const utils_1 = require("@metamask/utils");
|
|
28
|
+
const fs = __importStar(require("node:fs"));
|
|
29
|
+
const path = __importStar(require("node:path"));
|
|
30
|
+
const typescript_1 = require("typescript");
|
|
31
|
+
/**
|
|
32
|
+
* Extracts JSDoc comment from a method declaration.
|
|
33
|
+
*
|
|
34
|
+
* @param node - The method declaration node.
|
|
35
|
+
* @param source - The source file.
|
|
36
|
+
* @returns The JSDoc comment.
|
|
37
|
+
*/
|
|
38
|
+
function extractJSDoc(node, source) {
|
|
39
|
+
const jsDocTags = (0, typescript_1.getJSDocCommentsAndTags)(node);
|
|
40
|
+
if (jsDocTags.length === 0) {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
const jsDoc = jsDocTags[0];
|
|
44
|
+
if ((0, typescript_1.isJSDoc)(jsDoc)) {
|
|
45
|
+
const fullText = source.getFullText();
|
|
46
|
+
const start = jsDoc.getFullStart();
|
|
47
|
+
const end = jsDoc.getEnd();
|
|
48
|
+
const rawJsDoc = fullText.substring(start, end).trim();
|
|
49
|
+
return formatJSDoc(rawJsDoc);
|
|
50
|
+
}
|
|
51
|
+
// istanbul ignore next: defensive check ā getJSDocCommentsAndTags always returns JSDoc nodes
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Formats JSDoc comments to have consistent indentation for the generated file.
|
|
56
|
+
*
|
|
57
|
+
* @param rawJsDoc - The raw JSDoc comment from the source.
|
|
58
|
+
* @returns The formatted JSDoc comment.
|
|
59
|
+
*/
|
|
60
|
+
function formatJSDoc(rawJsDoc) {
|
|
61
|
+
const lines = rawJsDoc.split('\n');
|
|
62
|
+
const formattedLines = [];
|
|
63
|
+
for (let i = 0; i < lines.length; i++) {
|
|
64
|
+
const line = lines[i];
|
|
65
|
+
if (i === 0) {
|
|
66
|
+
formattedLines.push('/**');
|
|
67
|
+
}
|
|
68
|
+
else if (i === lines.length - 1) {
|
|
69
|
+
formattedLines.push(' */');
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const trimmed = line.trim();
|
|
73
|
+
if (trimmed.startsWith('*')) {
|
|
74
|
+
const content = trimmed.substring(1).trim();
|
|
75
|
+
formattedLines.push(content ? ` * ${content}` : ' *');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
formattedLines.push(trimmed ? ` * ${trimmed}` : ' *');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return formattedLines.join('\n');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Visits AST nodes to find exposed methods and controller/service class.
|
|
86
|
+
*
|
|
87
|
+
* @param context - The visitor context.
|
|
88
|
+
* @returns A function to visit nodes.
|
|
89
|
+
*/
|
|
90
|
+
function createASTVisitor(context) {
|
|
91
|
+
function visitNode(node) {
|
|
92
|
+
if ((0, typescript_1.isVariableStatement)(node)) {
|
|
93
|
+
const declaration = node.declarationList.declarations[0];
|
|
94
|
+
if ((0, typescript_1.isIdentifier)(declaration.name) &&
|
|
95
|
+
declaration.name.text === 'MESSENGER_EXPOSED_METHODS') {
|
|
96
|
+
if (declaration.initializer) {
|
|
97
|
+
let arrayExpression;
|
|
98
|
+
if ((0, typescript_1.isArrayLiteralExpression)(declaration.initializer)) {
|
|
99
|
+
arrayExpression = declaration.initializer;
|
|
100
|
+
}
|
|
101
|
+
else if ((0, typescript_1.isAsExpression)(declaration.initializer) &&
|
|
102
|
+
(0, typescript_1.isArrayLiteralExpression)(declaration.initializer.expression)) {
|
|
103
|
+
arrayExpression = declaration.initializer.expression;
|
|
104
|
+
}
|
|
105
|
+
if (arrayExpression) {
|
|
106
|
+
context.exposedMethods = arrayExpression.elements
|
|
107
|
+
.filter(typescript_1.isStringLiteral)
|
|
108
|
+
.map((element) => element.text);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if ((0, typescript_1.isClassDeclaration)(node) && node.name) {
|
|
114
|
+
const classText = node.name.text;
|
|
115
|
+
if (classText.includes('Controller') || classText.includes('Service')) {
|
|
116
|
+
context.className = classText;
|
|
117
|
+
const seenMethods = new Set();
|
|
118
|
+
for (const member of node.members) {
|
|
119
|
+
if ((0, typescript_1.isMethodDeclaration)(member) &&
|
|
120
|
+
member.name &&
|
|
121
|
+
(0, typescript_1.isIdentifier)(member.name)) {
|
|
122
|
+
const methodName = member.name.text;
|
|
123
|
+
if (context.exposedMethods.includes(methodName) &&
|
|
124
|
+
!seenMethods.has(methodName)) {
|
|
125
|
+
seenMethods.add(methodName);
|
|
126
|
+
const jsDoc = extractJSDoc(member, context.sourceFile);
|
|
127
|
+
context.methods.push({
|
|
128
|
+
name: methodName,
|
|
129
|
+
jsDoc,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
(0, typescript_1.forEachChild)(node, visitNode);
|
|
137
|
+
}
|
|
138
|
+
return visitNode;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create a TypeScript program for the given file by locating the nearest
|
|
142
|
+
* tsconfig.json.
|
|
143
|
+
*
|
|
144
|
+
* @param filePath - Absolute path to the source file.
|
|
145
|
+
* @returns A TypeScript program, or null if no tsconfig was found.
|
|
146
|
+
*/
|
|
147
|
+
function createProgramForFile(filePath) {
|
|
148
|
+
const configPath = (0, typescript_1.findConfigFile)(path.dirname(filePath), typescript_1.sys.fileExists.bind(typescript_1.sys), 'tsconfig.json');
|
|
149
|
+
if (!configPath) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const { config, error } = (0, typescript_1.readConfigFile)(configPath, typescript_1.sys.readFile.bind(typescript_1.sys));
|
|
153
|
+
if (error) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const parsedConfig = (0, typescript_1.parseJsonConfigFileContent)(config, typescript_1.sys, path.dirname(configPath));
|
|
157
|
+
return (0, typescript_1.createProgram)({
|
|
158
|
+
rootNames: parsedConfig.fileNames,
|
|
159
|
+
options: parsedConfig.options,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Find a class declaration with the given name in a source file.
|
|
164
|
+
*
|
|
165
|
+
* @param source - The source file to search.
|
|
166
|
+
* @param className - The class name to look for.
|
|
167
|
+
* @returns The class declaration node, or null if not found.
|
|
168
|
+
*/
|
|
169
|
+
function findClassInSourceFile(source, className) {
|
|
170
|
+
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
|
|
171
|
+
null);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Search through the class hierarchy of a TypeScript type to find the
|
|
175
|
+
* declaration of a method with the given name.
|
|
176
|
+
*
|
|
177
|
+
* @param classType - The class type to search.
|
|
178
|
+
* @param methodName - The method name to look for.
|
|
179
|
+
* @returns The method declaration node, or null if not found.
|
|
180
|
+
*/
|
|
181
|
+
function findMethodInHierarchy(classType, methodName) {
|
|
182
|
+
const symbol = classType.getProperty(methodName);
|
|
183
|
+
if (!symbol) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const declarations = symbol.getDeclarations();
|
|
187
|
+
// istanbul ignore next: defensive check ā symbols from getProperty always have declarations
|
|
188
|
+
if (!declarations) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
for (const declaration of declarations) {
|
|
192
|
+
if ((0, typescript_1.isMethodDeclaration)(declaration)) {
|
|
193
|
+
return declaration;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// istanbul ignore next: defensive fallback ā property found but not a method declaration
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if a path is a directory.
|
|
201
|
+
*
|
|
202
|
+
* @param pathValue - The path to check.
|
|
203
|
+
* @returns True if the path is a directory, false otherwise.
|
|
204
|
+
*/
|
|
205
|
+
async function isDirectory(pathValue) {
|
|
206
|
+
try {
|
|
207
|
+
const stats = await fs.promises.stat(pathValue);
|
|
208
|
+
return stats.isDirectory();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
if ((0, utils_1.isObject)(error) &&
|
|
212
|
+
(0, utils_1.hasProperty)(error, 'code') &&
|
|
213
|
+
error.code === 'ENOENT') {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Parses a source file to extract exposed methods and their metadata.
|
|
221
|
+
*
|
|
222
|
+
* @param filePath - Path to the controller/service file to parse.
|
|
223
|
+
* @returns Source information or null if parsing fails.
|
|
224
|
+
*/
|
|
225
|
+
async function parseSourceFile(filePath) {
|
|
226
|
+
try {
|
|
227
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
228
|
+
const source = (0, typescript_1.createSourceFile)(filePath, content, typescript_1.ScriptTarget.Latest, true);
|
|
229
|
+
const context = {
|
|
230
|
+
exposedMethods: [],
|
|
231
|
+
className: '',
|
|
232
|
+
methods: [],
|
|
233
|
+
sourceFile: source,
|
|
234
|
+
};
|
|
235
|
+
createASTVisitor(context)(source);
|
|
236
|
+
if (context.exposedMethods.length === 0 || !context.className) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
const foundMethodNames = new Set(context.methods.map((method) => method.name));
|
|
240
|
+
const inheritedMethodNames = context.exposedMethods.filter((name) => !foundMethodNames.has(name));
|
|
241
|
+
if (inheritedMethodNames.length > 0) {
|
|
242
|
+
const program = createProgramForFile(filePath);
|
|
243
|
+
const checker = program?.getTypeChecker();
|
|
244
|
+
const programSourceFile = program?.getSourceFile(filePath);
|
|
245
|
+
(0, utils_1.assert)(checker, `Type checker could not be created for "${filePath}". Ensure a valid tsconfig.json is present.`);
|
|
246
|
+
(0, utils_1.assert)(programSourceFile, `Source file "${filePath}" not found in program.`);
|
|
247
|
+
const classNode = findClassInSourceFile(programSourceFile, context.className);
|
|
248
|
+
(0, utils_1.assert)(classNode, `Class "${context.className}" not found in "${filePath}".`);
|
|
249
|
+
const classType = checker.getTypeAtLocation(classNode);
|
|
250
|
+
for (const methodName of inheritedMethodNames) {
|
|
251
|
+
const methodDeclaration = findMethodInHierarchy(classType, methodName);
|
|
252
|
+
const jsDoc = methodDeclaration
|
|
253
|
+
? extractJSDoc(methodDeclaration, methodDeclaration.getSourceFile())
|
|
254
|
+
: '';
|
|
255
|
+
context.methods.push({ name: methodName, jsDoc });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
name: context.className,
|
|
260
|
+
filePath,
|
|
261
|
+
methods: context.methods,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
console.error(`Error parsing ${filePath}:`, error);
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
exports.parseSourceFile = parseSourceFile;
|
|
270
|
+
/**
|
|
271
|
+
* Recursively get all files in a directory and its subdirectories.
|
|
272
|
+
*
|
|
273
|
+
* @param directory - The directory to search.
|
|
274
|
+
* @returns An array of file paths.
|
|
275
|
+
*/
|
|
276
|
+
const EXCLUDED_DIRECTORIES = new Set([
|
|
277
|
+
'node_modules',
|
|
278
|
+
'dist',
|
|
279
|
+
'.git',
|
|
280
|
+
'coverage',
|
|
281
|
+
]);
|
|
282
|
+
async function getFiles(directory) {
|
|
283
|
+
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
284
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
285
|
+
const fullPath = path.join(directory, entry.name);
|
|
286
|
+
if (entry.isDirectory()) {
|
|
287
|
+
return EXCLUDED_DIRECTORIES.has(entry.name)
|
|
288
|
+
? []
|
|
289
|
+
: await getFiles(fullPath);
|
|
290
|
+
}
|
|
291
|
+
return fullPath;
|
|
292
|
+
}));
|
|
293
|
+
return files.flat();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Finds all source files that have MESSENGER_EXPOSED_METHODS constants.
|
|
297
|
+
* Searches recursively through subdirectories.
|
|
298
|
+
*
|
|
299
|
+
* @param sourcePath - Path to the folder where controllers/services are located.
|
|
300
|
+
* @returns A list of source information objects.
|
|
301
|
+
*/
|
|
302
|
+
async function findSourcesWithExposedMethods(sourcePath) {
|
|
303
|
+
const srcPath = path.resolve(globalThis.process.cwd(), sourcePath);
|
|
304
|
+
const sources = [];
|
|
305
|
+
if (!(await isDirectory(srcPath))) {
|
|
306
|
+
throw new Error(`The specified path is not a directory: ${srcPath}`);
|
|
307
|
+
}
|
|
308
|
+
const srcFiles = await getFiles(srcPath);
|
|
309
|
+
for (const file of srcFiles) {
|
|
310
|
+
if (!file.endsWith('.ts') || file.endsWith('.test.ts')) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const content = await fs.promises.readFile(file, 'utf8');
|
|
314
|
+
if (content.includes('MESSENGER_EXPOSED_METHODS')) {
|
|
315
|
+
const sourceInfo = await parseSourceFile(file);
|
|
316
|
+
if (sourceInfo) {
|
|
317
|
+
sources.push(sourceInfo);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return sources;
|
|
322
|
+
}
|
|
323
|
+
exports.findSourcesWithExposedMethods = findSourcesWithExposedMethods;
|
|
324
|
+
//# sourceMappingURL=parse-source.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-source.cjs","sourceRoot":"","sources":["../src/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"]}
|