diff --git a/generator/dummyClasses/ali.js b/generator/dummyClasses/ali.js new file mode 100644 index 00000000..ea8fbd03 --- /dev/null +++ b/generator/dummyClasses/ali.js @@ -0,0 +1,23 @@ +class ClassName { + /** + * @param {module} alisdk ali SDK + * @param {string} accessKeyId SDK secrets accessKeyId + * @param {string} accessKeySecret SDK secrets accessKeySecret + */ + constructor(alisdk, accessKeyId, accessKeySecret) { + this._ali = alisdk; + this._instance = new this._ali(accessKeyId, accessKeySecret); + this._sdkClassName = this._instance.SDKClassName; + } + + function() { + return new Promise((resolve, reject) => { + this._sdkClassName + .SDKFunctionName() + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } +} + +module.exports = ClassName; diff --git a/generator/generatedClasses/Aliyun/storage/ali-storageBucket.js b/generator/generatedClasses/Aliyun/storage/ali-storageBucket.js new file mode 100644 index 00000000..fbd3b2fe --- /dev/null +++ b/generator/generatedClasses/Aliyun/storage/ali-storageBucket.js @@ -0,0 +1,107 @@ +/*This is an auto generated class, please do not edit this file!*/ +/** +* Class to create a Oss object +* @category AliCloud +*/ +class ALI_StorageBucket { + /** + * @param {module} alisdk ali SDK + * @param {string} accessKeyId SDK secrets accessKeyId + * @param {string} accessKeySecret SDK secrets accessKeySecret + */ + constructor(alisdk, accessKeyId, accessKeySecret) { + this._ali = alisdk; + this._instance = new this._ali(accessKeyId, accessKeySecret); + this._oss = this._instance.oss; + } + /** + * Trigers the setRegion function of Oss + * @param {StringKeyword} region - Data required for setRegion + * @returns {Promise} + */ + setRegion(region) { + return new Promise((resolve, reject) => { + this._oss.setRegion(region) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the listBuckets function of Oss + * @param {AnyKeyword} params - Data required for listBuckets + * @returns {Promise} + */ + listBuckets(params) { + return new Promise((resolve, reject) => { + this._oss.listBuckets(params) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the create function of Oss + * @param {StringKeyword} name - Data required for create + * @param {AnyKeyword} params - Data required for create + * @returns {Promise} + */ + create(name, params = undefined) { + return new Promise((resolve, reject) => { + this._oss.create(name, params) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the delete function of Oss + * @param {StringKeyword} name - Data required for delete + * @returns {Promise} + */ + delete(name) { + return new Promise((resolve, reject) => { + this._oss.delete(name) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the describeBucket function of Oss + * @param {StringKeyword} name - Data required for describeBucket + * @returns {Promise} + */ + describeBucket(name) { + return new Promise((resolve, reject) => { + this._oss.describeBucket(name) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the listBucketObjects function of Oss + * @param {StringKeyword} name - Data required for listBucketObjects + * @param {AnyKeyword} params - Data required for listBucketObjects + * @returns {Promise} + */ + listBucketObjects(name, params) { + return new Promise((resolve, reject) => { + this._oss.listBucketObjects(name, params) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } + /** + * Trigers the uploadLocalObject function of Oss + * @param {StringKeyword} bucketName - Data required for uploadLocalObject + * @param {StringKeyword} objectName - Data required for uploadLocalObject + * @param {AnyKeyword} file - Data required for uploadLocalObject + * @param {UnionType} params - Data required for uploadLocalObject + * @returns {Promise} + */ + uploadLocalObject(bucketName, objectName, file, params = undefined) { + return new Promise((resolve, reject) => { + this._oss.uploadLocalObject(bucketName, objectName, file, params) + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } +} +module.exports = ALI_StorageBucket; diff --git a/generator/generators/aliyun/generator.js b/generator/generators/aliyun/generator.js new file mode 100644 index 00000000..6ae861f0 --- /dev/null +++ b/generator/generators/aliyun/generator.js @@ -0,0 +1,219 @@ +'use strict'; +var __awaiter = + (this && this.__awaiter) || + function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator['throw'](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done + ? resolve(result.value) + : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; +var __generator = + (this && this.__generator) || + function(thisArg, body) { + var _ = { + label: 0, + sent: function() { + if (t[0] & 1) throw t[1]; + return t[1]; + }, + trys: [], + ops: [], + }, + f, + y, + t, + g; + return ( + (g = { next: verb(0), throw: verb(1), return: verb(2) }), + typeof Symbol === 'function' && + (g[Symbol.iterator] = function() { + return this; + }), + g + ); + function verb(n) { + return function(v) { + return step([n, v]); + }; + } + function step(op) { + if (f) throw new TypeError('Generator is already executing.'); + while (_) + try { + if ( + ((f = 1), + y && + (t = + op[0] & 2 + ? y['return'] + : op[0] + ? y['throw'] || ((t = y['return']) && t.call(y), 0) + : y.next) && + !(t = t.call(y, op[1])).done) + ) + return t; + if (((y = 0), t)) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: + case 1: + t = op; + break; + case 4: + _.label++; + return { value: op[1], done: false }; + case 5: + _.label++; + y = op[1]; + op = [0]; + continue; + case 7: + op = _.ops.pop(); + _.trys.pop(); + continue; + default: + if ( + !((t = _.trys), (t = t.length > 0 && t[t.length - 1])) && + (op[0] === 6 || op[0] === 2) + ) { + _ = 0; + continue; + } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { + _.label = op[1]; + break; + } + if (op[0] === 6 && _.label < t[1]) { + _.label = t[1]; + t = op; + break; + } + if (t && _.label < t[2]) { + _.label = t[2]; + _.ops.push(op); + break; + } + if (t[2]) _.ops.pop(); + _.trys.pop(); + continue; + } + op = body.call(thisArg, _); + } catch (e) { + op = [6, e]; + y = 0; + } finally { + f = t = 0; + } + if (op[0] & 5) throw op[1]; + return { value: op[0] ? op[1] : void 0, done: true }; + } + }; +exports.__esModule = true; +exports.generateAliyunClass = exports.dummyAst = void 0; +var fs = require('fs'); +var typescript_1 = require('typescript'); +var parser_1 = require('../../parsers/aliyun/parser'); +var transformer_1 = require('../../transformers/aliyun/transformer'); +var helper_1 = require('../lib/helper'); +var dummyClassPath = '/dummyClasses/ali.js'; +var dummyFile = process.cwd() + dummyClassPath; +exports.dummyAst = typescript_1.createSourceFile( + dummyFile, + fs.readFileSync(dummyFile).toString(), + typescript_1.ScriptTarget.Latest, + true +); +exports.generateAliyunClass = function(serviceClass, serviceName) { + var sdkfile = serviceClass[Object.keys(serviceClass)[0]]; + parser_1 + .getAST(sdkfile) + .then(function(result) { + return __awaiter(void 0, void 0, void 0, function() { + var sdkClassAst, classData, output, filePath, dir, err_1; + return __generator(this, function(_a) { + switch (_a.label) { + case 0: + sdkClassAst = result; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + classData = parser_1.extractSDKData(sdkClassAst, serviceClass); + classData.serviceName = serviceName; + return [ + 4 /*yield*/, + transformer_1.transform(exports.dummyAst, classData), + ]; + case 2: + output = _a.sent(); + filePath = void 0; + dir = helper_1.getDir(serviceName); + if ( + !fs.existsSync( + process.cwd() + '/generatedClasses/Aliyun/' + dir + ) + ) { + fs.mkdirSync( + process.cwd() + '/generatedClasses/Aliyun/' + dir, + { + recursive: true, + } + ); + } + if (/^[A-Z]*$/.test(serviceName)) { + filePath = + process.cwd() + + '/generatedClasses/Aliyun/' + + dir + + '/ali-' + + serviceName + + '.js'; + } else { + filePath = + process.cwd() + + '/generatedClasses/Aliyun/' + + dir + + '/ali-' + + serviceName.charAt(0).toLowerCase() + + serviceName.slice(1) + + '.js'; + } + helper_1.printFile(filePath, output); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + console.error('Error : ', err_1); + return [3 /*break*/, 4]; + case 4: + return [2 /*return*/]; + } + }); + }); + }) + ['catch'](function(error) { + console.error('Error : ', error); + }); +}; diff --git a/generator/generators/aliyun/generator.ts b/generator/generators/aliyun/generator.ts index d87ef857..32d161f5 100644 --- a/generator/generators/aliyun/generator.ts +++ b/generator/generators/aliyun/generator.ts @@ -1,4 +1,19 @@ +import * as fs from 'fs'; +import { createSourceFile, ScriptTarget } from 'typescript'; + import { ClassData, extractSDKData, getAST } from '../../parsers/aliyun/parser'; +import { transform } from '../../transformers/aliyun/transformer'; +import { getDir, printFile } from '../lib/helper'; + +const dummyClassPath = '/dummyClasses/ali.js'; +const dummyFile = process.cwd() + dummyClassPath; + +export const dummyAst = createSourceFile( + dummyFile, + fs.readFileSync(dummyFile).toString(), + ScriptTarget.Latest, + true +); export const generateAliyunClass = ( serviceClass: unknown, @@ -11,6 +26,39 @@ export const generateAliyunClass = ( try { const classData: ClassData = extractSDKData(sdkClassAst, serviceClass); classData.serviceName = serviceName; + + // perform transformation + const output = await transform(dummyAst, classData); + + // print to file + let filePath; + const dir = getDir(serviceName); + + if (!fs.existsSync(process.cwd() + '/generatedClasses/Aliyun/' + dir)) { + fs.mkdirSync(process.cwd() + '/generatedClasses/Aliyun/' + dir, { + recursive: true, + }); + } + + if (/^[A-Z]*$/.test(serviceName)) { + filePath = + process.cwd() + + '/generatedClasses/Aliyun/' + + dir + + '/ali-' + + serviceName + + '.js'; + } else { + filePath = + process.cwd() + + '/generatedClasses/Aliyun/' + + dir + + '/ali-' + + serviceName.charAt(0).toLowerCase() + + serviceName.slice(1) + + '.js'; + } + printFile(filePath, output); } catch (err) { console.error('Error : ', err); } diff --git a/generator/test/transformers/aliyun/dummyData/invalidDataset_1/data.json b/generator/test/transformers/aliyun/dummyData/invalidDataset_1/data.json new file mode 100644 index 00000000..09642325 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/invalidDataset_1/data.json @@ -0,0 +1,120 @@ +{ + "className": "Oss", + "functions": [ + { + "functionName": "setRegion", + "SDKFunctionName": "setRegion", + "params": [ + { + "name": "region", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "listBuckets", + "SDKFunctionName": "listBuckets", + "params": [ + { + "name": "params", + "optional": false, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "create", + "SDKFunctionName": "create", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "delete", + "SDKFunctionName": "delete", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "describeBucket", + "SDKFunctionName": "describeBucket", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "listBucketObjects", + "SDKFunctionName": "listBucketObjects", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": false, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "uploadLocalObject", + "SDKFunctionName": "uploadLocalObject", + "params": [ + { + "name": "bucketName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "objectName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "file", + "optional": false, + "type": "AnyKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "UnionType", + "typeName": null + } + ] + } + ], + "serviceName": "StorageBucket" +} diff --git a/generator/test/transformers/aliyun/dummyData/invalidDataset_1/sourceFile.js b/generator/test/transformers/aliyun/dummyData/invalidDataset_1/sourceFile.js new file mode 100644 index 00000000..d56fed49 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/invalidDataset_1/sourceFile.js @@ -0,0 +1,18 @@ +class ClassName { + constructor(alisdk, accessKeyId, accessKeySecret) { + this._ali = alisdk; + this._instance = new this._ali(accessKeyId, accessKeySecret); + this._sdkClassName = this._instance.SDKClassName; + } + + function() { + return new Promise((resolve, reject) => { + this._sdkClassName + .SDKFunctionName() + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } +} + +module.exports = ClassName; diff --git a/generator/test/transformers/aliyun/dummyData/invalidDataset_2/data.json b/generator/test/transformers/aliyun/dummyData/invalidDataset_2/data.json new file mode 100644 index 00000000..10ba6fa3 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/invalidDataset_2/data.json @@ -0,0 +1,96 @@ +{ + "className": "Oss", + "functions": [ + { + "functionName": "create", + "SDKFunctionName": "create", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "delete", + "SDKFunctionName": "delete", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "describeBucket", + "SDKFunctionName": "describeBucket", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "listBucketObjects", + "SDKFunctionName": "listBucketObjects", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": false, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "uploadLocalObject", + "SDKFunctionName": "uploadLocalObject", + "params": [ + { + "name": "bucketName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "objectName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "file", + "optional": false, + "type": "AnyKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "UnionType", + "typeName": null + } + ] + } + ], + "serviceName": "StorageBucket" +} diff --git a/generator/test/transformers/aliyun/dummyData/invalidDataset_2/sourceFile.js b/generator/test/transformers/aliyun/dummyData/invalidDataset_2/sourceFile.js new file mode 100644 index 00000000..d56fed49 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/invalidDataset_2/sourceFile.js @@ -0,0 +1,18 @@ +class ClassName { + constructor(alisdk, accessKeyId, accessKeySecret) { + this._ali = alisdk; + this._instance = new this._ali(accessKeyId, accessKeySecret); + this._sdkClassName = this._instance.SDKClassName; + } + + function() { + return new Promise((resolve, reject) => { + this._sdkClassName + .SDKFunctionName() + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } +} + +module.exports = ClassName; diff --git a/generator/test/transformers/aliyun/dummyData/validDataset/data.json b/generator/test/transformers/aliyun/dummyData/validDataset/data.json new file mode 100644 index 00000000..09642325 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/validDataset/data.json @@ -0,0 +1,120 @@ +{ + "className": "Oss", + "functions": [ + { + "functionName": "setRegion", + "SDKFunctionName": "setRegion", + "params": [ + { + "name": "region", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "listBuckets", + "SDKFunctionName": "listBuckets", + "params": [ + { + "name": "params", + "optional": false, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "create", + "SDKFunctionName": "create", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "delete", + "SDKFunctionName": "delete", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "describeBucket", + "SDKFunctionName": "describeBucket", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + } + ] + }, + { + "functionName": "listBucketObjects", + "SDKFunctionName": "listBucketObjects", + "params": [ + { + "name": "name", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "params", + "optional": false, + "type": "TypeReference", + "typeName": "undefined" + } + ] + }, + { + "functionName": "uploadLocalObject", + "SDKFunctionName": "uploadLocalObject", + "params": [ + { + "name": "bucketName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "objectName", + "optional": false, + "type": "StringKeyword", + "typeName": null + }, + { + "name": "file", + "optional": false, + "type": "AnyKeyword", + "typeName": null + }, + { + "name": "params", + "optional": true, + "type": "UnionType", + "typeName": null + } + ] + } + ], + "serviceName": "StorageBucket" +} diff --git a/generator/test/transformers/aliyun/dummyData/validDataset/sourceFile.js b/generator/test/transformers/aliyun/dummyData/validDataset/sourceFile.js new file mode 100644 index 00000000..d56fed49 --- /dev/null +++ b/generator/test/transformers/aliyun/dummyData/validDataset/sourceFile.js @@ -0,0 +1,18 @@ +class ClassName { + constructor(alisdk, accessKeyId, accessKeySecret) { + this._ali = alisdk; + this._instance = new this._ali(accessKeyId, accessKeySecret); + this._sdkClassName = this._instance.SDKClassName; + } + + function() { + return new Promise((resolve, reject) => { + this._sdkClassName + .SDKFunctionName() + .then(data => resolve(data)) + .catch(err => reject(err)); + }); + } +} + +module.exports = ClassName; diff --git a/generator/test/transformers/aliyun/transformer.test.ts b/generator/test/transformers/aliyun/transformer.test.ts new file mode 100644 index 00000000..2cdbce65 --- /dev/null +++ b/generator/test/transformers/aliyun/transformer.test.ts @@ -0,0 +1,73 @@ +import { expect } from 'chai'; +import { createSourceFile, isSourceFile, ScriptTarget } from 'typescript'; + +import { transform } from '../../../transformers/aliyun/transformer'; +import { readJsonData, readSourceFile } from '../lib/helper'; + +interface TestData { + AST: any; + data: any; +} + +describe('Aliyun transformer transformations', () => { + context('Valid source code and valid data', () => { + const testData: TestData = { AST: null, data: null }; + before(async () => { + testData.AST = await readSourceFile('validDataset', 'aliyun'); + testData.data = await readJsonData('validDataset', 'aliyun'); + }); + + it('Should return a String', async () => { + const result = await transform(testData.AST, testData.data); + expect(result).to.be.string; + }); + + it('Should return a Javascript code in String format', async () => { + const result = await transform(testData.AST, testData.data); + try { + const sourceCode = createSourceFile( + 'someClass.js', + result, + ScriptTarget.Latest + ); + expect(isSourceFile(sourceCode)).to.be.true; + } catch (error) { + console.log(error); + } + }); + }); + + context('Invalid source code and valid data', () => { + const testData: TestData = { AST: null, data: null }; + before(async () => { + testData.AST = await readSourceFile('invalidDataset_1', 'aliyun'); + testData.data = await readJsonData('invalidDataset_1', 'aliyun'); + }); + + it('Should return a validation Error', async () => { + try { + await transform(testData.AST, testData.data); + } catch (error) { + expect(error.message).to.eql('Code is invalid'); + } + }); + }); + + context('Valid source code and invalid data', () => { + const testData: TestData = { AST: null, data: null }; + before(async () => { + testData.AST = await readSourceFile('invalidDataset_2', 'aliyun'); + testData.data = await readJsonData('invalidDataset_2', 'aliyun'); + }); + + it('Should return a validation Error', async () => { + try { + await transform(testData.AST, testData.data); + } catch (error) { + expect(error.message).to.eql( + 'Invalid Input, missing class declerations' + ); + } + }); + }); +}); diff --git a/generator/transformers/aliyun/transformer.js b/generator/transformers/aliyun/transformer.js new file mode 100644 index 00000000..90e1faad --- /dev/null +++ b/generator/transformers/aliyun/transformer.js @@ -0,0 +1,445 @@ +'use strict'; +var __assign = + (this && this.__assign) || + function() { + __assign = + Object.assign || + function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) + if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; +var __awaiter = + (this && this.__awaiter) || + function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator['throw'](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done + ? resolve(result.value) + : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; +var __generator = + (this && this.__generator) || + function(thisArg, body) { + var _ = { + label: 0, + sent: function() { + if (t[0] & 1) throw t[1]; + return t[1]; + }, + trys: [], + ops: [], + }, + f, + y, + t, + g; + return ( + (g = { next: verb(0), throw: verb(1), return: verb(2) }), + typeof Symbol === 'function' && + (g[Symbol.iterator] = function() { + return this; + }), + g + ); + function verb(n) { + return function(v) { + return step([n, v]); + }; + } + function step(op) { + if (f) throw new TypeError('Generator is already executing.'); + while (_) + try { + if ( + ((f = 1), + y && + (t = + op[0] & 2 + ? y['return'] + : op[0] + ? y['throw'] || ((t = y['return']) && t.call(y), 0) + : y.next) && + !(t = t.call(y, op[1])).done) + ) + return t; + if (((y = 0), t)) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: + case 1: + t = op; + break; + case 4: + _.label++; + return { value: op[1], done: false }; + case 5: + _.label++; + y = op[1]; + op = [0]; + continue; + case 7: + op = _.ops.pop(); + _.trys.pop(); + continue; + default: + if ( + !((t = _.trys), (t = t.length > 0 && t[t.length - 1])) && + (op[0] === 6 || op[0] === 2) + ) { + _ = 0; + continue; + } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { + _.label = op[1]; + break; + } + if (op[0] === 6 && _.label < t[1]) { + _.label = t[1]; + t = op; + break; + } + if (t && _.label < t[2]) { + _.label = t[2]; + _.ops.push(op); + break; + } + if (t[2]) _.ops.pop(); + _.trys.pop(); + continue; + } + op = body.call(thisArg, _); + } catch (e) { + op = [6, e]; + y = 0; + } finally { + f = t = 0; + } + if (op[0] & 5) throw op[1]; + return { value: op[0] ? op[1] : void 0, done: true }; + } + }; +exports.__esModule = true; +exports.transform = void 0; +var lodash_1 = require('lodash'); +var ts = require('typescript'); +var dummyIdentifiers = [ + 'ClassName', + '_sdkClassName', + 'SDKClassName', + 'SDKFunctionName', +]; +var printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed, + removeComments: false, +}); +var addMultiLineComment = function(node, comment) { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + comment, + true + ); +}; +var runTransformation = function(sourceCode, transformMethod) { + return new Promise(function(resolve, reject) { + try { + var result = ts.transform(sourceCode, [transformMethod]); + var transformedNodes = result.transformed[0]; + var output = printer.printNode( + ts.EmitHint.SourceFile, + transformedNodes, + sourceCode + ); + resolve(output); + } catch (error) { + reject(error); + } + }); +}; +var toSourceFile = function(sourceCode) { + return ts.createSourceFile( + 'dummyClass.js', + sourceCode, + ts.ScriptTarget.Latest, + true + ); +}; +var extractParams = function(classData, identifierCount) { + var parameters = classData.functions[identifierCount].params.map(function( + param + ) { + var paramNode = ts.createParameter( + undefined, + undefined, + undefined, + param.name + ); + if (param.optional) { + paramNode.initializer = ts.createIdentifier('undefined'); + } + return paramNode; + }); + return parameters; +}; +var extractArgs = function(classData, identifierCount) { + var args = classData.functions[identifierCount].params.map(function(param) { + return ts.createIdentifier(param.name); + }); + return args; +}; +exports.transform = function(code, classData) { + return __awaiter(void 0, void 0, void 0, function() { + var addFunctions, + addIdentifiers, + addComments, + node, + result_1, + result_2, + result_3; + return __generator(this, function(_a) { + switch (_a.label) { + case 0: + addFunctions = function(context) { + return function(rootNode) { + var visit = function(node) { + if (ts.isClassDeclaration(node)) { + var functions = classData.functions.map(function(method) { + // const clonedNode = Object.assign({}, node.members[1]); + var clonedNode = __assign({}, node.members[1]); + clonedNode.name = ts.createIdentifier(method.functionName); + return clonedNode; + }); + // console.log(functions); + var updatedClass = ts.updateClassDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + ts.createNodeArray([node.members[0]].concat(functions)) + ); + return updatedClass; + } + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + }; + addIdentifiers = function(context) { + return function(rootNode) { + var count = 0; + var visit = function(node) { + if (ts.isMethodDeclaration(node)) { + var parameters = extractParams(classData, count); + node.parameters = parameters; + } + if ( + ts.isIdentifier(node) && + dummyIdentifiers.includes(node.text) + ) { + var updatedIdentifier = void 0; + switch (node.text) { + case 'ClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier('ALI_' + classData.serviceName) + ); + break; + case '_sdkClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier( + '_' + + classData.className.charAt(0).toLowerCase() + + classData.className.substr(1) + ) + ); + break; + case 'SDKClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier( + classData.className.charAt(0).toLowerCase() + + classData.className.substr(1) + ) + ); + break; + case 'SDKFunctionName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier( + classData.functions[count].SDKFunctionName + ) + ); + count++; + } + return updatedIdentifier; + } + if (ts.isCallExpression(node)) { + node.expression.forEachChild(function(childNode) { + if ( + ts.isIdentifier(childNode) && + childNode.text === 'SDKFunctionName' + ) { + // const args = classData.functions[count].params.map( + // param => ts.createIdentifier(param.name) + // ); + var args = extractArgs(classData, count); + node.arguments = args.concat(node.arguments); + } + }); + } + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + }; + addComments = function(context) { + return function(rootNode) { + var count = 0; + var visit = function(node) { + if (ts.isClassDeclaration(node)) { + addMultiLineComment( + node, + 'This is an auto generated class, please do not edit this file!' + ); + var comment = + '*\n* Class to create a ' + + classData.className + + ' object\n* @category AliCloud \n'; + addMultiLineComment(node, comment); + } + if (ts.isMethodDeclaration(node)) { + var parameters = classData.functions[count].params.map( + function(param) { + var statment; + if (param.optional) { + if (param.type == 'TypeReference') + statment = + '* @param {' + + param.typeName + + '} ' + + param.name + + ' - Data required for ' + + classData.functions[count].SDKFunctionName; + else + statment = + '* @param {' + + param.type + + '} ' + + param.name + + ' - Data required for ' + + classData.functions[count].SDKFunctionName; + } else { + if (param.type == 'TypeReference') + statment = + '* @param {' + + param.typeName + + '} ' + + param.name + + ' - Data required for ' + + classData.functions[count].SDKFunctionName; + else + statment = + '* @param {' + + param.type + + '} ' + + param.name + + ' - Data required for ' + + classData.functions[count].SDKFunctionName; + } + return statment; + } + ); + var comment = void 0; + if (parameters.length > 0) { + var paramStatments_1 = ''; + parameters.forEach(function(param) { + paramStatments_1 = paramStatments_1.concat( + paramStatments_1 === '' ? '' + param : '\n' + param + ); + }); + comment = + '*\n* Trigers the ' + + classData.functions[count].SDKFunctionName + + ' function of ' + + classData.className + + '\n' + + paramStatments_1 + + '\n* @returns {Promise<' + + classData.functions[count].SDKFunctionName + + 'Response>}\n'; + } else { + comment = + '*\n* Trigers the ' + + classData.functions[count].SDKFunctionName + + ' function of ' + + classData.className + + '\n* @returns {Promise<' + + classData.functions[count].SDKFunctionName + + 'Response>}\n'; + } + addMultiLineComment(node, comment); + count++; + } + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + }; + node = code.statements.find(function(stm) { + return ts.isClassDeclaration(stm); + }); + if (!classData.className) { + throw new Error('Invalid Input, missing class declerations'); + } + if (!classData.functions) { + throw new Error('Invalid Input, missing class member functions'); + } + code = lodash_1.cloneDeep(code); + return [4 /*yield*/, runTransformation(code, addFunctions)]; + case 1: + result_1 = _a.sent(); + return [ + 4 /*yield*/, + runTransformation(toSourceFile(result_1), addIdentifiers), + ]; + case 2: + result_2 = _a.sent(); + return [ + 4 /*yield*/, + runTransformation(toSourceFile(result_2), addComments), + ]; + case 3: + result_3 = _a.sent(); + return [2 /*return*/, result_3]; + } + }); + }); +}; diff --git a/generator/transformers/aliyun/transformer.ts b/generator/transformers/aliyun/transformer.ts new file mode 100644 index 00000000..fc731465 --- /dev/null +++ b/generator/transformers/aliyun/transformer.ts @@ -0,0 +1,265 @@ +import { cloneDeep } from 'lodash'; +import * as ts from 'typescript'; + +const dummyIdentifiers = [ + 'ClassName', + '_sdkClassName', + 'SDKClassName', + 'SDKFunctionName', +]; + +const extractParams = (classData: any, identifierCount: number) => { + const parameters = classData.functions[identifierCount].params.map(param => { + const paramNode = ts.createParameter( + undefined, + undefined, + undefined, + param.name + ); + + if (param.optional) { + paramNode.initializer = ts.createIdentifier('undefined'); + } + + return paramNode; + }); + return parameters; +}; + +const extractArgs = (classData: any, identifierCount: number) => { + const args = classData.functions[identifierCount].params.map(param => + ts.createIdentifier(param.name) + ); + return args; +}; + +const printer: ts.Printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed, + removeComments: false, +}); + +const addMultiLineComment = (node: any, comment: string) => { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + comment, + true + ); +}; + +const runTransformation = ( + sourceCode: any, + transformMethod: ts.TransformerFactory +): Promise => { + return new Promise((resolve, reject) => { + try { + const result = ts.transform(sourceCode, [transformMethod]); + const transformedNodes = result.transformed[0]; + const output = printer.printNode( + ts.EmitHint.SourceFile, + transformedNodes, + sourceCode + ); + resolve(output); + } catch (error) { + reject(error); + } + }); +}; + +const toSourceFile = (sourceCode: string): ts.SourceFile => { + return ts.createSourceFile( + 'dummyClass.js', + sourceCode, + ts.ScriptTarget.Latest, + true + ); +}; + +export const transform = async ( + code: ts.SourceFile, + classData: any +): Promise => { + /* + * Transformation function for adding Functions + */ + const addFunctions = ( + context: ts.TransformationContext + ) => (rootNode: T) => { + const visit = (node: ts.Node): ts.Node => { + if (ts.isClassDeclaration(node)) { + const functions: Array = classData.functions.map(method => { + const clonedNode = { ...node.members[1] }; + clonedNode.name = ts.createIdentifier(method.functionName); + return clonedNode; + }); + const updatedClass = ts.updateClassDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + ts.createNodeArray([node.members[0]].concat(functions)) + ); + + return updatedClass; + } + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + + /* + * Transformation function for adding Identifiers/Parameters + */ + const addIdentifiers = ( + context: ts.TransformationContext + ) => (rootNode: T) => { + let count = 0; + const visit = (node: ts.Node): ts.Node => { + if (ts.isMethodDeclaration(node)) { + const parameters = extractParams(classData, count); + node.parameters = parameters; + } + + if (ts.isIdentifier(node) && dummyIdentifiers.includes(node.text)) { + let updatedIdentifier; + switch (node.text) { + case 'ClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier('ALI_' + classData.serviceName) + ); + break; + case '_sdkClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier( + '_' + + classData.className.charAt(0).toLowerCase() + + classData.className.substr(1) + ) + ); + break; + case 'SDKClassName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier( + classData.className.charAt(0).toLowerCase() + + classData.className.substr(1) + ) + ); + break; + case 'SDKFunctionName': + updatedIdentifier = ts.updateIdentifier( + ts.createIdentifier(classData.functions[count].SDKFunctionName) + ); + count++; + } + return updatedIdentifier; + } + + if (ts.isCallExpression(node)) { + node.expression.forEachChild(childNode => { + if ( + ts.isIdentifier(childNode) && + childNode.text === 'SDKFunctionName' + ) { + const args = extractArgs(classData, count); + node.arguments = args.concat(node.arguments); + } + }); + } + + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + + /* + *Transformation function for adding comments + */ + const addComments = ( + context: ts.TransformationContext + ) => (rootNode: T) => { + let count = 0; + + const visit = (node: ts.Node): ts.Node => { + if (ts.isClassDeclaration(node)) { + addMultiLineComment( + node, + 'This is an auto generated class, please do not edit this file!' + ); + const comment = `* +* Class to create a ${classData.className} object +* @category AliCloud +`; + addMultiLineComment(node, comment); + } + + if (ts.isMethodDeclaration(node)) { + let parameters = classData.functions[count].params.map(param => { + let statment; + + if (param.optional) { + if (param.type == 'TypeReference') + statment = `* @param {${param.typeName}} ${param.name} - Data required for ${classData.functions[count].SDKFunctionName}`; + else + statment = `* @param {${param.type}} ${param.name} - Data required for ${classData.functions[count].SDKFunctionName}`; + } else { + if (param.type == 'TypeReference') + statment = `* @param {${param.typeName}} ${param.name} - Data required for ${classData.functions[count].SDKFunctionName}`; + else + statment = `* @param {${param.type}} ${param.name} - Data required for ${classData.functions[count].SDKFunctionName}`; + } + return statment; + }); + + let comment; + if (parameters.length > 0) { + let paramStatments: string = ''; + parameters.forEach(param => { + paramStatments = paramStatments.concat( + paramStatments === '' ? `${param}` : `\n${param}` + ); + }); + + comment = `* +* Trigers the ${classData.functions[count].SDKFunctionName} function of ${classData.className} +${paramStatments} +* @returns {Promise<${classData.functions[count].SDKFunctionName}Response>} +`; + } else { + comment = `* +* Trigers the ${classData.functions[count].SDKFunctionName} function of ${classData.className} +* @returns {Promise<${classData.functions[count].SDKFunctionName}Response>} +`; + } + + addMultiLineComment(node, comment); + count++; + } + + return ts.visitEachChild(node, visit, context); + }; + return ts.visitNode(rootNode, visit); + }; + + /* + * Code to get node and run tranformations + */ + const node: any = code.statements.find(stm => ts.isClassDeclaration(stm)); + if (!classData.className) { + throw new Error('Invalid Input, missing class declerations'); + } + if (!classData.functions) { + throw new Error('Invalid Input, missing class member functions'); + } + + code = cloneDeep(code); + const result_1 = await runTransformation(code, addFunctions); + const result_2 = await runTransformation( + toSourceFile(result_1), + addIdentifiers + ); + const result_3 = await runTransformation(toSourceFile(result_2), addComments); + return result_3; +};