@lcap/asl
Version:
NetEase Application Specific Language
99 lines (87 loc) • 3.75 kB
text/typescript
import * as fs from 'fs';
import * as path from 'path';
import * as globby from 'globby';
import * as babel from '@babel/core';
import generator from '@babel/generator';
import traverse from '@babel/traverse';
const isFirstUpperCase = (str: string) => str[0].toUpperCase() === str[0];
const firstToLowerCase = (str: string) => str[0].toLowerCase() + str.slice(1);
interface ConceptProperty {
name: string,
value?: string,
type?: string,
immutable?: boolean,
circular?: boolean,
excludedInJSON?: boolean,
action?: string,
inherited?: boolean,
}
interface Concept {
name: string,
propertyList: Array<ConceptProperty>,
}
const conceptMap: { [name: string]: Concept } = {};
function parse() {
globby.sync(['./src/types/**/*.ts']).forEach((filePath) => {
const baseName = path.basename(filePath, '.ts');
if (!isFirstUpperCase(baseName))
return;
const code = fs.readFileSync(filePath, 'utf8');
const ast = babel.parse(code, {
filename: filePath,
plugins: [
require('@babel/plugin-syntax-typescript'),
[require('@babel/plugin-proposal-decorators'), {
decoratorsBeforeExport: false,
legacy: false,
}],
],
});
traverse(ast, {
enter(path) {
if (path.isClassDeclaration()) {
const concept: Concept = {
name: path.node.id.name,
propertyList: path.node.body.body
.filter((property) => property.type === 'ClassProperty')
.map((property) => {
property = property as babel.types.ClassProperty;
const result: ConceptProperty = {
name: (property.key as babel.types.Identifier).name,
type: property.typeAnnotation ? generator(property.typeAnnotation).code.replace(/^:\s*/, '') : undefined,
};
property.decorators && property.decorators.forEach((decorator) => {
if (decorator.expression.type === 'CallExpression' && decorator.expression.callee.type === 'Identifier') {
if (decorator.expression.callee.name === 'immutable')
result.immutable = true;
else if (decorator.expression.callee.name === 'circular')
result.circular = true;
else if (decorator.expression.callee.name === 'excludedInJSON')
result.excludedInJSON = true;
else if (decorator.expression.callee.name === 'action')
result.action = 'true';
}
});
return result;
}),
};
conceptMap[concept.name] = concept;
}
},
});
});
}
function generateConcept(concept: Concept) {
const outputs = [];
outputs.push(`${concept.name}`);
concept.propertyList.forEach((property) => {
outputs.push(` ${property.name}: ${property.type}`);
});
return outputs.join('\n');
}
function generate() {
const json = JSON.stringify(conceptMap, null, 4);
fs.writeFileSync('./spec/conceptMap.json', json, 'utf8');
}
parse();
generate();