我想创建一个库,其中包含由节点js代码自动生成的组件示例页面。 使用 Copilot 的帮助,我得到了这个函数来读取组件的元数据,但它返回的是空的。我对node的了解还不是很发达,我想了解这个过程,而不是仅仅复制AI。 这是带有接口的函数 - 代码位于节点的打字稿中。 该函数接收角度分量文件路径作为参数
interface InputMetadata {
name: string;
type: string | null;
defaultValue: string | null;
}
interface ComponentMetadata {
selector: string;
standalone: boolean;
inputs: InputMetadata[];
}
// Function to read component metadata
function readComponentMetadata(componentPath: string): ComponentMetadata {
const componentFile = fs.readFileSync(componentPath, 'utf-8');
const sourceFile = ts.createSourceFile(componentPath, componentFile, ts.ScriptTarget.Latest, true);
const inputs: InputMetadata[] = [];
let selector: string = '';
let standalone: boolean = false;
function visit(node: ts.Node) {
if (ts.isClassDeclaration(node) && node.decorators) {
node.decorators.forEach(decorator => {
if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() === 'Component') {
const args = decorator.expression.arguments;
if (args.length) {
const componentMetadata = args[0] as ts.ObjectLiteralExpression;
componentMetadata.properties.forEach(property => {
if (ts.isPropertyAssignment(property)) {
if (property.name.getText() === 'selector') {
selector = (property.initializer as ts.StringLiteral).text;
} else if (property.name.getText() === 'standalone') {
standalone = (property.initializer.kind === ts.SyntaxKind.TrueKeyword);
}
}
});
}
}
});
}
if (ts.isPropertyDeclaration(node) && node.decorators) {
node.decorators.forEach(decorator => {
if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() === 'Input') {
const name = node.name.getText();
const type = node.type ? node.type.getText() : null;
const initializer = node.initializer ? node.initializer.getText() : null;
inputs.push({name, type, defaultValue: initializer});
}
});
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
return {selector, standalone, inputs};
}
文件必须使用
npx tsc
“编译”,然后才能调用js文件。
我使用的节点和打字稿是:
"devDependencies": {
"@types/node": "^22.10.1",
"typescript": "^5.7.2"
}
这是 tsconfig.json 文件:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
对我来说,代码抛出类型错误,就像评论中提到的那样。似乎
ClassDeclaration
没有 decorators
属性。要获取装饰器,您需要调用 ts.getDecorators(node)
。更新代码:
function readComponentMetadata(componentPath: string): ComponentMetadata {
const componentFile = fs.readFileSync(componentPath, 'utf-8');
const sourceFile = ts.createSourceFile(componentPath, componentFile, ts.ScriptTarget.Latest, true);
const inputs: InputMetadata[] = [];
let selector: string = '';
let standalone: boolean = false;
function visit(node: ts.Node) {
if (ts.isClassDeclaration(node)) {
const decorators = ts.getDecorators(node);
decorators?.forEach(decorator => {
if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() === 'Component') {
const args = decorator.expression.arguments;
if (args.length) {
const componentMetadata = args[0] as ts.ObjectLiteralExpression;
componentMetadata.properties.forEach(property => {
if (ts.isPropertyAssignment(property)) {
if (property.name.getText() === 'selector') {
selector = (property.initializer as ts.StringLiteral).text;
} else if (property.name.getText() === 'standalone') {
standalone = (property.initializer.kind === ts.SyntaxKind.TrueKeyword);
}
}
});
}
}
});
}
if (ts.isPropertyDeclaration(node)) {
const decorators = ts.getDecorators(node);
decorators?.forEach(decorator => {
if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() === 'Input') {
const name = node.name.getText();
const type = node.type ? node.type.getText() : null;
const initializer = node.initializer ? node.initializer.getText() : null;
inputs.push({name, type, defaultValue: initializer});
}
});
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
return {selector, standalone, inputs};
}
我还在一个真正的 Angular 项目中测试了这段代码,似乎工作得很好。