packages/ros-cdk-codegen/lib/index.ts (128 lines of code) (raw):
import * as rosSpec from '@alicloud/ros-cdk-spec';
import * as fs from 'fs-extra';
import CodeGenerator, { CodeGeneratorOptions } from './codegen';
import ResourceCodeGenerator, {ExtensionCodeGenerator, ResourceCodeGeneratorOptions} from './resource-codegen';
import { packageName } from './genspec';
import IndexCodeGenerator from './index-codegen';
const rosResourceName = '.generated.ts';
export default async function (
scopes: string | string[],
outPath: string,
options: CodeGeneratorOptions = {},
): Promise<void> {
if (outPath !== '.' && !fs.existsSync(outPath)) {
await fs.mkdirp(outPath);
}
if (typeof scopes === 'string') {
scopes = [scopes];
}
let aliyunResourceTypesArray = new Array();
// 优先处理ALIYUN开头的资源类型(需要创建资源类型对应的根目录)
for (const scope of scopes) {
if (scope.startsWith('ALIYUN')) {
const spec = rosSpec.filteredSpecification((s) => s.startsWith(`${scope}::`));
const originalResourceName = scope.split('::')[1]
const dataSourceResourceName = 'DATASOURCE::' + originalResourceName
let hasDataSource = false
if (scopes.indexOf(dataSourceResourceName) !== -1) {
hasDataSource = true
}
if (Object.keys(spec.ResourceTypes).length === 0) {
throw new Error(`No resource was found for scope ${scope}`);
}
// name: package/module name (the folder name)
const moduleName = `${packageName(scope)}`;
// rosResourceFileName: the xxx.generated.ts
const rosResourceFileName = `${moduleName}${rosResourceName}`;
const affix = computeAffix(scope, scopes);
const scopePath = `${outPath}/ros-cdk-${moduleName}/lib`;
// fs.mkdirp(scopePath);
const generator = new CodeGenerator(rosResourceFileName, spec, affix, options);
await generator.emitCode(Object.keys(spec.ResourceTypes));
await generator.save(scopePath);
// for every specification create resource class file
// e.g. vpc.ts
let fileNames: string[] = new Array();
for (const resourceName of Object.keys(spec.ResourceTypes).sort()) {
const resourceSpec = rosSpec.filteredSpecification((s) => s.startsWith(`${resourceName}`));
let fileName = resourceName.split('::')[2].toLowerCase();
fileName = fileName === 'index' ? 'index-resource' : fileName;
fileNames.push(fileName);
const resourceGenerator = new ResourceCodeGenerator(
`${fileName}.ts`,
resourceSpec,
'',
options as ResourceCodeGeneratorOptions,
);
await resourceGenerator.emitCode(resourceName);
await resourceGenerator.save(scopePath);
}
// create index.ts
const indexGenerator = new IndexCodeGenerator(moduleName, fileNames, scope);
await indexGenerator.emitCode(hasDataSource);
await indexGenerator.save(scopePath);
aliyunResourceTypesArray.push(originalResourceName)
}
}
// 处理DATASOURCE开头的资源类型
for (const scope of scopes) {
if (scope.startsWith('DATASOURCE')) {
const dataSourceResourceName = scope.split('::')[1]
// 处理只有Datasource没有Aliyun这类的资源类型
if (aliyunResourceTypesArray.indexOf(dataSourceResourceName) !== -1) {
// 处理DATASOURCE::EIP::Addresses 这个原属于VPC的特殊资源,不再创建资源根目录
if (scope === 'DATASOURCE::EIP' || scope === 'DATASOURCE::Eipanycast') {
continue
}
const dataSourceSpec = rosSpec.filteredSpecification((s) => s.startsWith(`${scope}::`));
const cloudProductResourceName = scope.split('::')[1]
let dataSourceResourceTypes: string[] = new Array();
dataSourceResourceTypes = Object.keys(dataSourceSpec.ResourceTypes)
// 处理DATASOURCE::EIP::Addresses 这个原属于VPC的特殊资源,添加到VPC所属资源目录
if (cloudProductResourceName === 'VPC') {
if (scopes.indexOf('DATASOURCE::EIP') !== -1) {
const datasourceEipResourceSpec = rosSpec.filteredSpecification((s) => s.startsWith('DATASOURCE::EIP::'));
dataSourceResourceTypes = dataSourceResourceTypes.concat(Object.keys(datasourceEipResourceSpec.ResourceTypes))
}
if (scopes.indexOf('DATASOURCE::Eipanycast') !== -1) {
const datasourceEipanycastResourceSpec = rosSpec.filteredSpecification((s) => s.startsWith('DATASOURCE::Eipanycast::'));
dataSourceResourceTypes = dataSourceResourceTypes.concat(Object.keys(datasourceEipanycastResourceSpec.ResourceTypes))
}
}
if (dataSourceResourceTypes.length === 0) {
throw new Error(`No resource was found for scope ${scope}`);
}
// name: package/module name (the folder name)
const moduleName = `${packageName(scope)}`;
// rosDataSourceResourceFileName: the xxx.generated.ts
const rosDataSourceResourceFileName = `${moduleName}${rosResourceName}`;
const affix = computeAffix(scope, scopes);
const scopePath = `${outPath}/ros-cdk-${moduleName}/lib/datasource`;
// fs.mkdirp(scopePath);
const generator = new CodeGenerator(rosDataSourceResourceFileName, dataSourceSpec, affix, options);
await generator.emitCode(dataSourceResourceTypes);
await generator.save(scopePath);
// for every specification create datasource resource class file
// e.g. vpc.ts
let fileNames: string[] = new Array();
for (const resourceName of dataSourceResourceTypes.sort()) {
const resourceSpec = rosSpec.filteredSpecification((s) => s.startsWith(`${resourceName}`));
let fileName = resourceName.split('::')[2].toLowerCase();
fileName = fileName === 'index' ? 'index-resource' : fileName;
fileNames.push(fileName);
const resourceGenerator = new ResourceCodeGenerator(
`${fileName}.ts`,
resourceSpec,
'',
options as ResourceCodeGeneratorOptions,
);
await resourceGenerator.emitCode(resourceName);
await resourceGenerator.save(scopePath);
}
// create index.ts
const indexGenerator = new IndexCodeGenerator(moduleName, fileNames, scope);
await indexGenerator.emitCode(false);
await indexGenerator.save(scopePath);
}
}
}
const extensionGenerator = new ExtensionCodeGenerator();
await extensionGenerator.emitCode(outPath);
}
/**
* Finds an affix for class names generated for a scope, given all the scopes that share the same package.
* @param scope the scope for which an affix is needed (e.g: ALIYUN::ApiGatewayV2)
* @param allScopes all the scopes hosted in the package (e.g: ["ALIYUN::ApiGateway", "ALIYUN::ApiGatewayV2"])
* @returns the affix (e.g: "V2"), if any, or an empty string.
*/
function computeAffix(scope: string, allScopes: string[]): string {
if (allScopes.length === 1) {
return '';
}
const parts = scope.match(/^(.+)(V\d+)$/);
if (!parts) {
return '';
}
const [, root, version] = parts;
if (allScopes.indexOf(root) !== -1) {
return version;
}
return '';
}