swagger-typescript-api 12.0.4 → 13.0.0
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/LICENSE +21 -21
- package/README.md +1 -1
- package/cli/constants.js +3 -3
- package/cli/execute.js +52 -31
- package/cli/index.d.ts +1 -2
- package/cli/index.js +18 -17
- package/cli/operations/display-help.js +51 -29
- package/cli/parse-args.js +3 -3
- package/cli/process-option.js +28 -20
- package/index.d.ts +99 -7
- package/index.js +158 -136
- package/package.json +35 -30
- package/src/code-formatter.js +28 -13
- package/src/code-gen-process.js +367 -264
- package/src/commands/generate-templates/configuration.js +2 -2
- package/src/commands/generate-templates/index.js +1 -2
- package/src/commands/generate-templates/templates-gen-process.js +62 -35
- package/src/component-type-name-resolver.js +44 -0
- package/src/configuration.js +166 -98
- package/src/constants.js +28 -22
- package/src/index.js +3 -4
- package/src/schema-components-map.js +39 -23
- package/src/schema-parser/base-schema-parsers/array.js +43 -0
- package/src/schema-parser/base-schema-parsers/complex.js +51 -0
- package/src/schema-parser/base-schema-parsers/discriminator.js +301 -0
- package/src/schema-parser/base-schema-parsers/enum.js +158 -0
- package/src/schema-parser/base-schema-parsers/object.js +105 -0
- package/src/schema-parser/base-schema-parsers/primitive.js +63 -0
- package/src/schema-parser/complex-schema-parsers/all-of.js +26 -0
- package/src/schema-parser/complex-schema-parsers/any-of.js +34 -0
- package/src/schema-parser/complex-schema-parsers/not.js +9 -0
- package/src/schema-parser/complex-schema-parsers/one-of.js +27 -0
- package/src/schema-parser/mono-schema-parser.js +48 -0
- package/src/schema-parser/schema-formatters.js +58 -44
- package/src/schema-parser/schema-parser-fabric.js +131 -0
- package/src/schema-parser/schema-parser.js +212 -361
- package/src/schema-parser/schema-utils.js +158 -33
- package/src/schema-parser/util/enum-key-resolver.js +26 -0
- package/src/{schema-parser → schema-routes}/schema-routes.js +472 -203
- package/src/schema-routes/util/specific-arg-name-resolver.js +26 -0
- package/src/schema-walker.js +93 -0
- package/src/swagger-schema-resolver.js +61 -28
- package/src/templates-worker.js +240 -0
- package/src/translators/javascript.js +83 -0
- package/src/translators/translator.js +35 -0
- package/src/{type-name.js → type-name-formatter.js} +35 -20
- package/src/util/file-system.js +30 -14
- package/src/util/id.js +2 -2
- package/src/util/internal-case.js +1 -1
- package/src/util/logger.js +46 -20
- package/src/util/name-resolver.js +50 -58
- package/src/util/object-assign.js +7 -3
- package/src/util/pascal-case.js +1 -1
- package/src/util/request.js +5 -5
- package/src/util/sort-by-property.js +17 -0
- package/templates/base/data-contract-jsdoc.ejs +37 -37
- package/templates/base/data-contracts.ejs +40 -28
- package/templates/base/enum-data-contract.ejs +12 -12
- package/templates/base/http-client.ejs +2 -2
- package/templates/base/http-clients/axios-http-client.ejs +139 -138
- package/templates/base/http-clients/fetch-http-client.ejs +224 -224
- package/templates/base/interface-data-contract.ejs +10 -10
- package/templates/base/object-field-jsdoc.ejs +28 -28
- package/templates/base/route-docs.ejs +30 -30
- package/templates/base/route-name.ejs +42 -42
- package/templates/base/route-type.ejs +22 -21
- package/templates/base/type-data-contract.ejs +15 -15
- package/templates/default/api.ejs +69 -65
- package/templates/default/procedure-call.ejs +100 -100
- package/templates/default/route-types.ejs +32 -28
- package/templates/modular/api.ejs +28 -28
- package/templates/modular/procedure-call.ejs +100 -100
- package/templates/modular/route-types.ejs +18 -18
- package/src/templates.js +0 -177
- package/src/translators/JavaScript.js +0 -60
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { NameResolver } = require('../../util/name-resolver');
|
|
2
|
+
|
|
3
|
+
class SpecificArgNameResolver extends NameResolver {
|
|
4
|
+
counter = 1;
|
|
5
|
+
/**
|
|
6
|
+
* @param {CodeGenConfig} config;
|
|
7
|
+
* @param {Logger} logger;
|
|
8
|
+
* @param {string[]} reservedNames
|
|
9
|
+
*/
|
|
10
|
+
constructor(config, logger, reservedNames) {
|
|
11
|
+
super(config, logger, reservedNames, (variants) => {
|
|
12
|
+
const generatedVariant =
|
|
13
|
+
(variants[0] && `${variants[0]}${this.counter++}`) ||
|
|
14
|
+
`${this.config.specificArgNameResolverName}${this.counter++}`;
|
|
15
|
+
this.logger.debug(
|
|
16
|
+
'generated fallback type name for specific arg - ',
|
|
17
|
+
generatedVariant,
|
|
18
|
+
);
|
|
19
|
+
return generatedVariant;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
SpecificArgNameResolver,
|
|
26
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
|
|
3
|
+
// TODO: WIP
|
|
4
|
+
// this class will be needed to walk by schema everywhere
|
|
5
|
+
class SchemaWalker {
|
|
6
|
+
/** @type {Logger} */
|
|
7
|
+
logger;
|
|
8
|
+
/** @type {CodeGenConfig} */
|
|
9
|
+
config;
|
|
10
|
+
/** @type {SwaggerSchemaResolver} */
|
|
11
|
+
swaggerSchemaResolver;
|
|
12
|
+
/** @type {Map<string, Record<string, any>>} */
|
|
13
|
+
schemas = new Map();
|
|
14
|
+
/** @type {Map<string, Record<string, any>>} */
|
|
15
|
+
caches = new Map();
|
|
16
|
+
|
|
17
|
+
constructor({ config, logger }) {
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param name {string}
|
|
24
|
+
* @param schema {Record<string, any>}
|
|
25
|
+
*/
|
|
26
|
+
addSchema = (name, schema) => {
|
|
27
|
+
this.schemas.set(name, _.cloneDeep(schema));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param ref {string}
|
|
32
|
+
* @returns {any}
|
|
33
|
+
*/
|
|
34
|
+
findByRef = (ref) => {
|
|
35
|
+
this.logger.debug('try to resolve ref by path', ref);
|
|
36
|
+
|
|
37
|
+
if (this.caches.has(ref)) {
|
|
38
|
+
return this.caches.get(ref);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const schemas = Array.from(this.schemas.values());
|
|
42
|
+
if (this._isLocalRef(ref)) {
|
|
43
|
+
for (const schema of schemas) {
|
|
44
|
+
const refData = this._getRefDataFromSchema(schema, ref);
|
|
45
|
+
if (refData) {
|
|
46
|
+
return refData;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} else if (this._isRemoteRef(ref)) {
|
|
50
|
+
this.logger.debug('remote refs not supported', ref);
|
|
51
|
+
return null;
|
|
52
|
+
} else {
|
|
53
|
+
const [address, path] = path.split('#');
|
|
54
|
+
let swaggerSchemaObject;
|
|
55
|
+
|
|
56
|
+
if (this.schemas.has(address)) {
|
|
57
|
+
swaggerSchemaObject = this.schemas.get(address);
|
|
58
|
+
} else {
|
|
59
|
+
const pathToSchema = path.resolve(process.cwd(), address);
|
|
60
|
+
const swaggerSchemaFile =
|
|
61
|
+
this.swaggerSchemaResolver.getSwaggerSchemaByPath(pathToSchema);
|
|
62
|
+
swaggerSchemaObject =
|
|
63
|
+
this.swaggerSchemaResolver.processSwaggerSchemaFile(
|
|
64
|
+
swaggerSchemaFile,
|
|
65
|
+
);
|
|
66
|
+
this.schemas.set(address, swaggerSchemaObject);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this._getRefDataFromSchema(swaggerSchemaObject, path);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
_isLocalRef = (ref) => {
|
|
74
|
+
return ref.startsWith('#');
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
_isRemoteRef = (ref) => {
|
|
78
|
+
return ref.startsWith('http://') || ref.startsWith('https://');
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
_getRefDataFromSchema = (schema, ref) => {
|
|
82
|
+
const path = ref.replace('#', '').split('/');
|
|
83
|
+
const refData = _.get(schema, path);
|
|
84
|
+
if (refData) {
|
|
85
|
+
this.caches.set(ref, refData);
|
|
86
|
+
}
|
|
87
|
+
return refData;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
SchemaWalker,
|
|
93
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const _ = require(
|
|
2
|
-
const converter = require(
|
|
3
|
-
const yaml = require(
|
|
4
|
-
const { Request } = require(
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const converter = require('swagger2openapi');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const { Request } = require('./util/request');
|
|
5
5
|
|
|
6
6
|
class SwaggerSchemaResolver {
|
|
7
7
|
/**
|
|
@@ -21,7 +21,7 @@ class SwaggerSchemaResolver {
|
|
|
21
21
|
*/
|
|
22
22
|
request;
|
|
23
23
|
|
|
24
|
-
constructor(config, logger, fileSystem) {
|
|
24
|
+
constructor({ config, logger, fileSystem }) {
|
|
25
25
|
this.config = config;
|
|
26
26
|
this.logger = logger;
|
|
27
27
|
this.fileSystem = fileSystem;
|
|
@@ -33,7 +33,15 @@ class SwaggerSchemaResolver {
|
|
|
33
33
|
* @returns {Promise<{usageSchema: Record<string, *>, originalSchema: Record<string, *>}>}
|
|
34
34
|
*/
|
|
35
35
|
async create() {
|
|
36
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
spec,
|
|
38
|
+
patch,
|
|
39
|
+
input,
|
|
40
|
+
url,
|
|
41
|
+
disableStrictSSL,
|
|
42
|
+
disableProxy,
|
|
43
|
+
authorizationToken,
|
|
44
|
+
} = this.config;
|
|
37
45
|
|
|
38
46
|
if (this.config.spec) {
|
|
39
47
|
return await this.convertSwaggerObject(spec, { patch });
|
|
@@ -46,7 +54,8 @@ class SwaggerSchemaResolver {
|
|
|
46
54
|
disableProxy,
|
|
47
55
|
authorizationToken,
|
|
48
56
|
);
|
|
49
|
-
const swaggerSchemaObject =
|
|
57
|
+
const swaggerSchemaObject =
|
|
58
|
+
this.processSwaggerSchemaFile(swaggerSchemaFile);
|
|
50
59
|
return await this.convertSwaggerObject(swaggerSchemaObject, { patch });
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -61,8 +70,8 @@ class SwaggerSchemaResolver {
|
|
|
61
70
|
const result = _.cloneDeep(swaggerSchema);
|
|
62
71
|
result.info = _.merge(
|
|
63
72
|
{
|
|
64
|
-
title:
|
|
65
|
-
version:
|
|
73
|
+
title: 'No title',
|
|
74
|
+
version: '',
|
|
66
75
|
},
|
|
67
76
|
result.info,
|
|
68
77
|
);
|
|
@@ -75,11 +84,15 @@ class SwaggerSchemaResolver {
|
|
|
75
84
|
{
|
|
76
85
|
...converterOptions,
|
|
77
86
|
warnOnly: true,
|
|
78
|
-
refSiblings:
|
|
79
|
-
rbname:
|
|
87
|
+
refSiblings: 'preserve',
|
|
88
|
+
rbname: 'requestBodyName',
|
|
80
89
|
},
|
|
81
90
|
(err, options) => {
|
|
82
|
-
const parsedSwaggerSchema = _.get(
|
|
91
|
+
const parsedSwaggerSchema = _.get(
|
|
92
|
+
err,
|
|
93
|
+
'options.openapi',
|
|
94
|
+
_.get(options, 'openapi'),
|
|
95
|
+
);
|
|
83
96
|
if (!parsedSwaggerSchema && err) {
|
|
84
97
|
throw new Error(err);
|
|
85
98
|
}
|
|
@@ -99,10 +112,20 @@ class SwaggerSchemaResolver {
|
|
|
99
112
|
});
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
|
|
115
|
+
getSwaggerSchemaByPath = (pathToSwagger) => {
|
|
116
|
+
this.logger.log(`try to get swagger by path "${pathToSwagger}"`);
|
|
117
|
+
return this.fileSystem.getFileContent(pathToSwagger);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
async fetchSwaggerSchemaFile(
|
|
121
|
+
pathToSwagger,
|
|
122
|
+
urlToSwagger,
|
|
123
|
+
disableStrictSSL,
|
|
124
|
+
disableProxy,
|
|
125
|
+
authToken,
|
|
126
|
+
) {
|
|
103
127
|
if (this.fileSystem.pathIsExist(pathToSwagger)) {
|
|
104
|
-
this.
|
|
105
|
-
return this.fileSystem.getFileContent(pathToSwagger);
|
|
128
|
+
return this.getSwaggerSchemaByPath(pathToSwagger);
|
|
106
129
|
} else {
|
|
107
130
|
this.logger.log(`try to get swagger by URL "${urlToSwagger}"`);
|
|
108
131
|
return await this.request.download({
|
|
@@ -115,7 +138,7 @@ class SwaggerSchemaResolver {
|
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
processSwaggerSchemaFile(file) {
|
|
118
|
-
if (typeof file !==
|
|
141
|
+
if (typeof file !== 'string') return file;
|
|
119
142
|
|
|
120
143
|
try {
|
|
121
144
|
return JSON.parse(file);
|
|
@@ -125,8 +148,8 @@ class SwaggerSchemaResolver {
|
|
|
125
148
|
}
|
|
126
149
|
|
|
127
150
|
fixSwaggerSchema({ usageSchema, originalSchema }) {
|
|
128
|
-
const usagePaths = _.get(usageSchema,
|
|
129
|
-
const originalPaths = _.get(originalSchema,
|
|
151
|
+
const usagePaths = _.get(usageSchema, 'paths');
|
|
152
|
+
const originalPaths = _.get(originalSchema, 'paths');
|
|
130
153
|
|
|
131
154
|
// walk by routes
|
|
132
155
|
_.each(usagePaths, (usagePathObject, route) => {
|
|
@@ -135,20 +158,30 @@ class SwaggerSchemaResolver {
|
|
|
135
158
|
// walk by methods
|
|
136
159
|
_.each(usagePathObject, (usageRouteInfo, methodName) => {
|
|
137
160
|
const originalRouteInfo = _.get(originalPathObject, methodName);
|
|
138
|
-
const usageRouteParams = _.get(usageRouteInfo,
|
|
139
|
-
const originalRouteParams = _.get(originalRouteInfo,
|
|
140
|
-
|
|
141
|
-
usageRouteInfo
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
const usageRouteParams = _.get(usageRouteInfo, 'parameters', []);
|
|
162
|
+
const originalRouteParams = _.get(originalRouteInfo, 'parameters', []);
|
|
163
|
+
|
|
164
|
+
if (typeof usageRouteInfo === 'object') {
|
|
165
|
+
usageRouteInfo.consumes = _.uniq(
|
|
166
|
+
_.compact([
|
|
167
|
+
...(usageRouteInfo.consumes || []),
|
|
168
|
+
...(originalRouteInfo.consumes || []),
|
|
169
|
+
]),
|
|
170
|
+
);
|
|
171
|
+
usageRouteInfo.produces = _.uniq(
|
|
172
|
+
_.compact([
|
|
173
|
+
...(usageRouteInfo.produces || []),
|
|
174
|
+
...(originalRouteInfo.produces || []),
|
|
175
|
+
]),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
147
178
|
|
|
148
179
|
_.each(originalRouteParams, (originalRouteParam) => {
|
|
149
180
|
const existUsageParam = _.find(
|
|
150
181
|
usageRouteParams,
|
|
151
|
-
(param) =>
|
|
182
|
+
(param) =>
|
|
183
|
+
originalRouteParam.in === param.in &&
|
|
184
|
+
originalRouteParam.name === param.name,
|
|
152
185
|
);
|
|
153
186
|
if (!existUsageParam) {
|
|
154
187
|
usageRouteParams.push(originalRouteParam);
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
const { resolve } = require('path');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const Eta = require('eta');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
class TemplatesWorker {
|
|
7
|
+
/**
|
|
8
|
+
* @type {CodeGenConfig}
|
|
9
|
+
*/
|
|
10
|
+
config;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {Logger}
|
|
14
|
+
*/
|
|
15
|
+
logger;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @type {FileSystem}
|
|
19
|
+
*/
|
|
20
|
+
fileSystem;
|
|
21
|
+
|
|
22
|
+
getRenderTemplateData;
|
|
23
|
+
|
|
24
|
+
constructor({ config, logger, fileSystem, getRenderTemplateData }) {
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.logger = logger;
|
|
27
|
+
this.fileSystem = fileSystem;
|
|
28
|
+
this.getRenderTemplateData = getRenderTemplateData;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param config {CodeGenConfig}
|
|
34
|
+
* @returns {CodeGenConfig.templatePaths}
|
|
35
|
+
*/
|
|
36
|
+
getTemplatePaths = (config) => {
|
|
37
|
+
const baseTemplatesPath = resolve(__dirname, '../templates/base');
|
|
38
|
+
const defaultTemplatesPath = resolve(__dirname, '../templates/default');
|
|
39
|
+
const modularTemplatesPath = resolve(__dirname, '../templates/modular');
|
|
40
|
+
const originalTemplatesPath = config.modular
|
|
41
|
+
? modularTemplatesPath
|
|
42
|
+
: defaultTemplatesPath;
|
|
43
|
+
const customTemplatesPath =
|
|
44
|
+
(config.templates && resolve(process.cwd(), config.templates)) || null;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
/** `templates/base` */
|
|
48
|
+
base: baseTemplatesPath,
|
|
49
|
+
/** `templates/default` */
|
|
50
|
+
default: defaultTemplatesPath,
|
|
51
|
+
/** `templates/modular` */
|
|
52
|
+
modular: modularTemplatesPath,
|
|
53
|
+
/** usage path if `--templates` option is not set */
|
|
54
|
+
original: originalTemplatesPath,
|
|
55
|
+
/** custom path to templates (`--templates`) */
|
|
56
|
+
custom: customTemplatesPath,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
cropExtension = (path) =>
|
|
61
|
+
this.config.templateExtensions.reduce(
|
|
62
|
+
(path, ext) => (_.endsWith(path, ext) ? path.replace(ext, '') : path),
|
|
63
|
+
path,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
getTemplateFullPath = (path, fileName) => {
|
|
67
|
+
const raw = resolve(path, './', this.cropExtension(fileName));
|
|
68
|
+
const pathVariants = this.config.templateExtensions.map(
|
|
69
|
+
(extension) => `${raw}${extension}`,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return pathVariants.find(
|
|
73
|
+
(variant) => !!this.fileSystem.pathIsExist(variant),
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
requireFnFromTemplate = (packageOrPath) => {
|
|
78
|
+
const isPath =
|
|
79
|
+
_.startsWith(packageOrPath, './') || _.startsWith(packageOrPath, '../');
|
|
80
|
+
|
|
81
|
+
if (isPath) {
|
|
82
|
+
return require(path.resolve(
|
|
83
|
+
this.config.templatePaths.custom || this.config.templatePaths.original,
|
|
84
|
+
packageOrPath,
|
|
85
|
+
));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return require(packageOrPath);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
getTemplate = ({ fileName, name, path }) => {
|
|
92
|
+
const { templatePaths } = this.config;
|
|
93
|
+
|
|
94
|
+
if (path) {
|
|
95
|
+
return this.fileSystem.getFileContent(path);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!fileName) return '';
|
|
99
|
+
|
|
100
|
+
const customFullPath =
|
|
101
|
+
templatePaths.custom &&
|
|
102
|
+
this.getTemplateFullPath(templatePaths.custom, fileName);
|
|
103
|
+
let fileContent =
|
|
104
|
+
customFullPath && this.fileSystem.getFileContent(customFullPath);
|
|
105
|
+
|
|
106
|
+
if (fileContent) {
|
|
107
|
+
this.logger.log(
|
|
108
|
+
`"${_.lowerCase(name)}" template found in "${templatePaths.custom}"`,
|
|
109
|
+
);
|
|
110
|
+
return fileContent;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const baseFullPath = this.getTemplateFullPath(templatePaths.base, fileName);
|
|
114
|
+
|
|
115
|
+
if (baseFullPath) {
|
|
116
|
+
fileContent = this.fileSystem.getFileContent(baseFullPath);
|
|
117
|
+
} else {
|
|
118
|
+
if (templatePaths.custom) {
|
|
119
|
+
this.logger.warn(
|
|
120
|
+
`"${_.lowerCase(name)}" template not found in "${
|
|
121
|
+
templatePaths.custom
|
|
122
|
+
}"`,
|
|
123
|
+
`\nCode generator will use the default template`,
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
this.logger.log(
|
|
127
|
+
`Code generator will use the default template for "${_.lowerCase(
|
|
128
|
+
name,
|
|
129
|
+
)}"`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const originalFullPath = this.getTemplateFullPath(
|
|
135
|
+
templatePaths.original,
|
|
136
|
+
fileName,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (originalFullPath) {
|
|
140
|
+
fileContent = this.fileSystem.getFileContent(originalFullPath);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return fileContent;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
getTemplates = ({ templatePaths }) => {
|
|
147
|
+
if (templatePaths.custom) {
|
|
148
|
+
this.logger.log(
|
|
149
|
+
`try to read templates from directory "${templatePaths.custom}"`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return _.reduce(
|
|
154
|
+
this.config.templateInfos,
|
|
155
|
+
(acc, { fileName, name }) => ({
|
|
156
|
+
...acc,
|
|
157
|
+
[name]: this.getTemplate({ fileName, name }),
|
|
158
|
+
}),
|
|
159
|
+
{},
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
findTemplateWithExt = (path) => {
|
|
164
|
+
const raw = this.cropExtension(path);
|
|
165
|
+
const pathVariants = this.config.templateExtensions.map(
|
|
166
|
+
(extension) => `${raw}${extension}`,
|
|
167
|
+
);
|
|
168
|
+
return pathVariants.find((variant) => this.fileSystem.pathIsExist(variant));
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
getTemplateContent = (path) => {
|
|
172
|
+
const foundTemplatePathKey = _.keys(this.config.templatePaths).find((key) =>
|
|
173
|
+
_.startsWith(path, `@${key}`),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const rawPath = resolve(
|
|
177
|
+
_.replace(
|
|
178
|
+
path,
|
|
179
|
+
`@${foundTemplatePathKey}`,
|
|
180
|
+
this.config.templatePaths[foundTemplatePathKey],
|
|
181
|
+
),
|
|
182
|
+
);
|
|
183
|
+
const fixedPath = this.findTemplateWithExt(rawPath);
|
|
184
|
+
|
|
185
|
+
if (fixedPath) {
|
|
186
|
+
return this.fileSystem.getFileContent(fixedPath);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const customPath =
|
|
190
|
+
this.config.templatePaths.custom &&
|
|
191
|
+
this.findTemplateWithExt(resolve(this.config.templatePaths.custom, path));
|
|
192
|
+
|
|
193
|
+
if (customPath) {
|
|
194
|
+
return this.fileSystem.getFileContent(customPath);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const originalPath = this.findTemplateWithExt(
|
|
198
|
+
resolve(this.config.templatePaths.original, path),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (originalPath) {
|
|
202
|
+
return this.fileSystem.getFileContent(originalPath);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return '';
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @param template
|
|
210
|
+
* @param configuration
|
|
211
|
+
* @param options
|
|
212
|
+
* @returns {Promise<string|string|void>}
|
|
213
|
+
*/
|
|
214
|
+
renderTemplate = (template, configuration, options) => {
|
|
215
|
+
if (!template) return '';
|
|
216
|
+
|
|
217
|
+
return Eta.render(
|
|
218
|
+
template,
|
|
219
|
+
{
|
|
220
|
+
...this.getRenderTemplateData(),
|
|
221
|
+
...configuration,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
async: false,
|
|
225
|
+
...(options || {}),
|
|
226
|
+
includeFile: (path, configuration, options) => {
|
|
227
|
+
return this.renderTemplate(
|
|
228
|
+
this.getTemplateContent(path),
|
|
229
|
+
configuration,
|
|
230
|
+
options,
|
|
231
|
+
);
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
TemplatesWorker,
|
|
240
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const ts = require('typescript');
|
|
2
|
+
const { Translator } = require('./translator');
|
|
3
|
+
|
|
4
|
+
class JavascriptTranslator extends Translator {
|
|
5
|
+
/**
|
|
6
|
+
* @param {TranslatorIO} input
|
|
7
|
+
* @returns {Record<string, string>}
|
|
8
|
+
*/
|
|
9
|
+
compileTSCode = (input) => {
|
|
10
|
+
const fileNameFull = `${input.fileName}${input.fileExtension}`;
|
|
11
|
+
const output = {};
|
|
12
|
+
const host = ts.createCompilerHost(this.config.compilerTsConfig, true);
|
|
13
|
+
const fileNames = [fileNameFull];
|
|
14
|
+
const originalSourceFileGet = host.getSourceFile.bind(host);
|
|
15
|
+
host.getSourceFile = (
|
|
16
|
+
sourceFileName,
|
|
17
|
+
languageVersion,
|
|
18
|
+
onError,
|
|
19
|
+
shouldCreateNewSourceFile,
|
|
20
|
+
) => {
|
|
21
|
+
if (sourceFileName !== fileNameFull)
|
|
22
|
+
return originalSourceFileGet(
|
|
23
|
+
sourceFileName,
|
|
24
|
+
languageVersion,
|
|
25
|
+
onError,
|
|
26
|
+
shouldCreateNewSourceFile,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return ts.createSourceFile(
|
|
30
|
+
sourceFileName,
|
|
31
|
+
input.fileContent,
|
|
32
|
+
languageVersion,
|
|
33
|
+
true,
|
|
34
|
+
ts.ScriptKind.TS,
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
host.writeFile = (fileName, contents) => {
|
|
39
|
+
output[fileName] = contents;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
ts.createProgram(fileNames, this.config.compilerTsConfig, host).emit();
|
|
43
|
+
|
|
44
|
+
return output;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
translate = async (input) => {
|
|
48
|
+
const compiled = this.compileTSCode(input);
|
|
49
|
+
|
|
50
|
+
const jsFileName = `${input.fileName}${ts.Extension.Js}`;
|
|
51
|
+
const dtsFileName = `${input.fileName}${ts.Extension.Dts}`;
|
|
52
|
+
const sourceContent = compiled[jsFileName];
|
|
53
|
+
const tsImportRows = input.fileContent
|
|
54
|
+
.split('\n')
|
|
55
|
+
.filter((line) => line.startsWith('import '));
|
|
56
|
+
const declarationContent = compiled[dtsFileName]
|
|
57
|
+
.split('\n')
|
|
58
|
+
.map((line) => {
|
|
59
|
+
if (line.startsWith('import ')) {
|
|
60
|
+
return tsImportRows.shift();
|
|
61
|
+
}
|
|
62
|
+
return line;
|
|
63
|
+
})
|
|
64
|
+
.join('\n');
|
|
65
|
+
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
fileName: input.fileName,
|
|
69
|
+
fileExtension: ts.Extension.Js,
|
|
70
|
+
fileContent: await this.codeFormatter.formatCode(sourceContent),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
fileName: input.fileName,
|
|
74
|
+
fileExtension: ts.Extension.Dts,
|
|
75
|
+
fileContent: await this.codeFormatter.formatCode(declarationContent),
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
JavascriptTranslator,
|
|
83
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {{ fileName: string, fileExtension: string, fileContent: string }} TranslatorIO
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class Translator {
|
|
6
|
+
/** @type {Logger} */
|
|
7
|
+
logger;
|
|
8
|
+
/** @type {CodeGenConfig} */
|
|
9
|
+
config;
|
|
10
|
+
/** @type {CodeFormatter} */
|
|
11
|
+
codeFormatter;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param codeGenProcess
|
|
15
|
+
*/
|
|
16
|
+
constructor(codeGenProcess) {
|
|
17
|
+
this.logger = codeGenProcess.logger;
|
|
18
|
+
this.config = codeGenProcess.config;
|
|
19
|
+
this.codeFormatter = codeGenProcess.codeFormatter;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param input {TranslatorIO}
|
|
25
|
+
* @return {Promise<TranslatorIO[]>}
|
|
26
|
+
*/
|
|
27
|
+
// eslint-disable-next-line no-unused-vars
|
|
28
|
+
translate(input) {
|
|
29
|
+
throw new Error('not implemented');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
Translator,
|
|
35
|
+
};
|