swagger-typescript-api 10.0.3 → 11.0.0--alpha

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.
Files changed (53) hide show
  1. package/README.md +218 -0
  2. package/index.d.ts +93 -0
  3. package/index.js +230 -103
  4. package/package.json +121 -119
  5. package/src/code-formatter.js +101 -0
  6. package/src/code-gen-process.js +456 -0
  7. package/src/configuration.js +425 -0
  8. package/src/constants.js +14 -31
  9. package/src/index.js +6 -257
  10. package/src/schema-components-map.js +60 -0
  11. package/src/schema-parser/schema-formatters.js +145 -0
  12. package/src/schema-parser/schema-parser.js +497 -0
  13. package/src/schema-parser/schema-routes.js +902 -0
  14. package/src/swagger-schema-resolver.js +187 -0
  15. package/src/templates.js +141 -122
  16. package/src/translators/JavaScript.js +49 -49
  17. package/src/type-name.js +79 -0
  18. package/src/util/file-system.js +76 -0
  19. package/src/{utils → util}/id.js +0 -0
  20. package/src/util/internal-case.js +5 -0
  21. package/src/util/logger.js +100 -0
  22. package/src/{utils/resolveName.js → util/name-resolver.js} +1 -1
  23. package/src/util/object-assign.js +11 -0
  24. package/src/util/pascal-case.js +5 -0
  25. package/src/{utils → util}/random.js +1 -1
  26. package/templates/base/data-contract-jsdoc.ejs +29 -24
  27. package/templates/base/data-contracts.ejs +3 -3
  28. package/templates/base/interface-data-contract.ejs +1 -0
  29. package/templates/base/route-docs.ejs +2 -2
  30. package/templates/base/route-type.ejs +2 -2
  31. package/templates/default/route-types.ejs +2 -2
  32. package/templates/modular/api.ejs +2 -2
  33. package/templates/modular/route-types.ejs +2 -2
  34. package/src/apiConfig.js +0 -30
  35. package/src/common.js +0 -28
  36. package/src/components.js +0 -91
  37. package/src/config.js +0 -108
  38. package/src/filePrefix.js +0 -14
  39. package/src/files.js +0 -56
  40. package/src/formatFileContent.js +0 -81
  41. package/src/logger.js +0 -68
  42. package/src/modelNames.js +0 -74
  43. package/src/modelTypes.js +0 -31
  44. package/src/output.js +0 -165
  45. package/src/prettierOptions.js +0 -23
  46. package/src/render/utils/fmtToJSDocLine.js +0 -10
  47. package/src/render/utils/index.js +0 -31
  48. package/src/render/utils/templateRequire.js +0 -17
  49. package/src/routeNames.js +0 -46
  50. package/src/routes.js +0 -809
  51. package/src/schema.js +0 -474
  52. package/src/swagger.js +0 -152
  53. package/src/typeFormatters.js +0 -121
@@ -0,0 +1,187 @@
1
+ const { Configuration } = require("./configuration.js");
2
+ const _ = require("lodash");
3
+ const converter = require("swagger2openapi");
4
+ const https = require("https");
5
+ const axios = require("axios");
6
+ const yaml = require("js-yaml");
7
+
8
+ class SwaggerSchemaResolver {
9
+ /**
10
+ * @type {Configuration}
11
+ */
12
+ config;
13
+ /**
14
+ * @type {Logger}
15
+ */
16
+ logger;
17
+ /**
18
+ * @type {FileSystem}
19
+ */
20
+ fileSystem;
21
+
22
+ constructor(configuration, logger, fileSystem) {
23
+ this.config = configuration;
24
+ this.logger = logger;
25
+ this.fileSystem = fileSystem;
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @returns {Promise<{usageSchema: Record<string, *>, originalSchema: Record<string, *>}>}
31
+ */
32
+ async create() {
33
+ const { spec, patch, input, url, disableStrictSSL, disableProxy, authorizationToken } = this.config;
34
+
35
+ if (this.config.spec) {
36
+ return await this.convertSwaggerObject(spec, { patch });
37
+ }
38
+
39
+ const swaggerSchemaFile = await this.fetchSwaggerSchemaFile(
40
+ input,
41
+ url,
42
+ disableStrictSSL,
43
+ disableProxy,
44
+ authorizationToken,
45
+ );
46
+ const swaggerSchemaObject = this.processSwaggerSchemaFile(swaggerSchemaFile);
47
+ return await this.convertSwaggerObject(swaggerSchemaObject, { patch });
48
+ }
49
+
50
+ /**
51
+ *
52
+ * @param swaggerSchema {Record<string, any>}
53
+ * @param converterOptions {{ patch?: boolean }}
54
+ * @returns {Promise<{ usageSchema: Record<string, any>, originalSchema: Record<string, any>}>}
55
+ */
56
+ convertSwaggerObject(swaggerSchema, converterOptions) {
57
+ return new Promise((resolve) => {
58
+ const result = _.cloneDeep(swaggerSchema);
59
+ result.info = _.merge(
60
+ {
61
+ title: "No title",
62
+ version: "",
63
+ },
64
+ result.info,
65
+ );
66
+
67
+ if (!result.openapi) {
68
+ result.paths = _.merge({}, result.paths);
69
+
70
+ converter.convertObj(
71
+ result,
72
+ {
73
+ ...converterOptions,
74
+ warnOnly: true,
75
+ refSiblings: "preserve",
76
+ rbname: "requestBodyName",
77
+ },
78
+ (err, options) => {
79
+ const parsedSwaggerSchema = _.get(err, "options.openapi", _.get(options, "openapi"));
80
+ if (!parsedSwaggerSchema && err) {
81
+ throw new Error(err);
82
+ }
83
+ this.config.update({ convertedFromSwagger2: true });
84
+ resolve({
85
+ usageSchema: parsedSwaggerSchema,
86
+ originalSchema: result,
87
+ });
88
+ },
89
+ );
90
+ } else {
91
+ resolve({
92
+ usageSchema: result,
93
+ originalSchema: _.cloneDeep(result),
94
+ });
95
+ }
96
+ });
97
+ }
98
+
99
+ fetchSwaggerSchemaFile(pathToSwagger, urlToSwagger, disableStrictSSL, disableProxy, authorizationToken) {
100
+ return new Promise((resolve, reject) => {
101
+ if (this.fileSystem.pathIsExist(pathToSwagger)) {
102
+ this.logger.log(`try to get swagger by path "${pathToSwagger}"`);
103
+ resolve(this.fileSystem.getFileContent(pathToSwagger));
104
+ } else {
105
+ this.logger.log(`try to get swagger by URL "${urlToSwagger}"`);
106
+ // setup options for Axios
107
+ const axiosOptions = {
108
+ maxContentLength: Infinity,
109
+ maxBodyLength: Infinity,
110
+ };
111
+ //
112
+ if (disableStrictSSL) {
113
+ axiosOptions.httpsAgent = new https.Agent({
114
+ rejectUnauthorized: false,
115
+ });
116
+ }
117
+ //
118
+ if (authorizationToken) {
119
+ axiosOptions.headers = {
120
+ Authorization: authorizationToken,
121
+ };
122
+ }
123
+ //
124
+ if (disableProxy) axiosOptions.proxy = false;
125
+ //
126
+ axios
127
+ .get(urlToSwagger, axiosOptions)
128
+ .then((res) => resolve(res.data))
129
+ .catch((error) => {
130
+ const message = `error while getting swagger by URL ${urlToSwagger}`;
131
+
132
+ this.logger.error(message, "response" in error ? error.response : error);
133
+
134
+ reject(message);
135
+ });
136
+ }
137
+ });
138
+ }
139
+
140
+ processSwaggerSchemaFile(file) {
141
+ if (typeof file !== "string") return file;
142
+
143
+ try {
144
+ return JSON.parse(file);
145
+ } catch (e) {
146
+ return yaml.load(file);
147
+ }
148
+ }
149
+
150
+ fixSwaggerSchema({ usageSchema, originalSchema }) {
151
+ const usagePaths = _.get(usageSchema, "paths");
152
+ const originalPaths = _.get(originalSchema, "paths");
153
+
154
+ // walk by routes
155
+ _.each(usagePaths, (usagePathObject, route) => {
156
+ const originalPathObject = _.get(originalPaths, route);
157
+
158
+ // walk by methods
159
+ _.each(usagePathObject, (usageRouteInfo, methodName) => {
160
+ const originalRouteInfo = _.get(originalPathObject, methodName);
161
+ const usageRouteParams = _.get(usageRouteInfo, "parameters", []);
162
+ const originalRouteParams = _.get(originalRouteInfo, "parameters", []);
163
+
164
+ usageRouteInfo.consumes = _.uniq(
165
+ _.compact([...(usageRouteInfo.consumes || []), ...(originalRouteInfo.consumes || [])]),
166
+ );
167
+ usageRouteInfo.produces = _.uniq(
168
+ _.compact([...(usageRouteInfo.produces || []), ...(originalRouteInfo.produces || [])]),
169
+ );
170
+
171
+ _.each(originalRouteParams, (originalRouteParam) => {
172
+ const existUsageParam = _.find(
173
+ usageRouteParams,
174
+ (param) => originalRouteParam.in === param.in && originalRouteParam.name === param.name,
175
+ );
176
+ if (!existUsageParam) {
177
+ usageRouteParams.push(originalRouteParam);
178
+ }
179
+ });
180
+ });
181
+ });
182
+ }
183
+ }
184
+
185
+ module.exports = {
186
+ SwaggerSchemaResolver,
187
+ };
package/src/templates.js CHANGED
@@ -1,155 +1,174 @@
1
+ const { resolve } = require("path");
1
2
  const _ = require("lodash");
2
3
  const Eta = require("eta");
3
- const { getFileContent, pathIsExist } = require("./files");
4
- const { config } = require("./config");
5
- const { resolve } = require("path");
6
- const { logger } = require("./logger");
7
-
8
- const TEMPLATE_EXTENSIONS = [".eta", ".ejs"];
9
-
10
- /**
11
- * name - project template name,
12
- * fileName - template file name,
13
- */
14
- const TEMPLATE_INFOS = [
15
- { name: "api", fileName: "api" },
16
- { name: "dataContracts", fileName: "data-contracts" },
17
- { name: "dataContractJsDoc", fileName: "data-contract-jsdoc" },
18
- { name: "interfaceDataContract", fileName: "interface-data-contract" },
19
- { name: "typeDataContract", fileName: "type-data-contract" },
20
- { name: "enumDataContract", fileName: "enum-data-contract" },
21
- { name: "objectFieldJsDoc", fileName: "object-field-jsdoc" },
22
- { name: "httpClient", fileName: "http-client" },
23
- { name: "routeTypes", fileName: "route-types" },
24
- { name: "routeName", fileName: "route-name" },
25
- ];
26
-
27
- const getTemplatePaths = ({ templates, modular }) => {
28
- const baseTemplatesPath = resolve(__dirname, "../templates/base");
29
- const defaultTemplatesPath = resolve(__dirname, "../templates/default");
30
- const modularTemplatesPath = resolve(__dirname, "../templates/modular");
31
- const originalTemplatesPath = modular ? modularTemplatesPath : defaultTemplatesPath;
32
- const customTemplatesPath = templates ? resolve(process.cwd(), templates) : originalTemplatesPath;
33
-
34
- return {
35
- /** `templates/base` */
36
- base: baseTemplatesPath,
37
- /** `templates/default` */
38
- default: defaultTemplatesPath,
39
- /** `templates/modular` */
40
- modular: modularTemplatesPath,
41
- /** usage path if `--templates` option is not set */
42
- original: originalTemplatesPath,
43
- /** custom path to templates (`--templates`) */
44
- custom: customTemplatesPath,
45
- };
46
- };
4
+ const path = require("path");
5
+
6
+ class Templates {
7
+ /**
8
+ * @type {Configuration}
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
+ }
47
30
 
48
- const cropExtension = (path) =>
49
- TEMPLATE_EXTENSIONS.reduce((path, ext) => (_.endsWith(path, ext) ? path.replace(ext, "") : path), path);
31
+ getTemplatePaths = ({ templates, modular }) => {
32
+ const baseTemplatesPath = resolve(__dirname, "../templates/base");
33
+ const defaultTemplatesPath = resolve(__dirname, "../templates/default");
34
+ const modularTemplatesPath = resolve(__dirname, "../templates/modular");
35
+ const originalTemplatesPath = modular ? modularTemplatesPath : defaultTemplatesPath;
36
+ const customTemplatesPath = templates ? resolve(process.cwd(), templates) : originalTemplatesPath;
37
+
38
+ return {
39
+ /** `templates/base` */
40
+ base: baseTemplatesPath,
41
+ /** `templates/default` */
42
+ default: defaultTemplatesPath,
43
+ /** `templates/modular` */
44
+ modular: modularTemplatesPath,
45
+ /** usage path if `--templates` option is not set */
46
+ original: originalTemplatesPath,
47
+ /** custom path to templates (`--templates`) */
48
+ custom: customTemplatesPath,
49
+ };
50
+ };
50
51
 
51
- const getTemplateFullPath = (path, fileName) => {
52
- const raw = resolve(path, "./", cropExtension(fileName));
53
- const pathVariants = TEMPLATE_EXTENSIONS.map((extension) => `${raw}${extension}`);
52
+ cropExtension = (path) =>
53
+ this.config.templateExtensions.reduce((path, ext) => (_.endsWith(path, ext) ? path.replace(ext, "") : path), path);
54
54
 
55
- return pathVariants.find((variant) => !!pathIsExist(variant));
56
- };
55
+ getTemplateFullPath = (path, fileName) => {
56
+ const raw = resolve(path, "./", this.cropExtension(fileName));
57
+ const pathVariants = this.config.templateExtensions.map((extension) => `${raw}${extension}`);
57
58
 
58
- const getTemplate = ({ fileName, name, path }) => {
59
- const { templatePaths } = config;
59
+ return pathVariants.find((variant) => !!this.fileSystem.pathIsExist(variant));
60
+ };
60
61
 
61
- if (path) {
62
- return getFileContent(path);
63
- }
62
+ requireFnFromTemplate = (packageOrPath) => {
63
+ const isPath = _.startsWith(packageOrPath, "./") || _.startsWith(packageOrPath, "../");
64
64
 
65
- if (!fileName) return "";
65
+ if (isPath) {
66
+ return require(path.resolve(this.config.templates, packageOrPath));
67
+ }
66
68
 
67
- const customFullPath = getTemplateFullPath(templatePaths.custom, fileName);
68
- let fileContent = customFullPath && getFileContent(customFullPath);
69
+ return require(packageOrPath);
70
+ };
69
71
 
70
- if (!fileContent) {
71
- const baseFullPath = getTemplateFullPath(templatePaths.base, fileName);
72
+ getTemplate = ({ fileName, name, path }) => {
73
+ const { templatePaths } = this.config;
72
74
 
73
- if (baseFullPath) {
74
- fileContent = getFileContent(baseFullPath);
75
- } else {
76
- logger.warn(
77
- `${_.lowerCase(name)} template not found in ${customFullPath}`,
78
- `\nCode generator will use the default template`,
79
- );
75
+ if (path) {
76
+ return this.fileSystem.getFileContent(path);
80
77
  }
81
78
 
82
- const originalFullPath = getTemplateFullPath(templatePaths.original, fileName);
79
+ if (!fileName) return "";
83
80
 
84
- if (originalFullPath) {
85
- fileContent = getFileContent(originalFullPath);
86
- }
87
- }
81
+ const customFullPath = this.getTemplateFullPath(templatePaths.custom, fileName);
82
+ let fileContent = customFullPath && this.fileSystem.getFileContent(customFullPath);
88
83
 
89
- return fileContent;
90
- };
84
+ if (!fileContent) {
85
+ const baseFullPath = this.getTemplateFullPath(templatePaths.base, fileName);
91
86
 
92
- const getTemplates = ({ templatePaths }) => {
93
- logger.log(`try to read templates from directory "${templatePaths.custom}"`);
87
+ if (baseFullPath) {
88
+ fileContent = this.fileSystem.getFileContent(baseFullPath);
89
+ } else {
90
+ this.logger.warn(
91
+ `${_.lowerCase(name)} template not found in ${customFullPath}`,
92
+ `\nCode generator will use the default template`,
93
+ );
94
+ }
94
95
 
95
- const templatesMap = _.reduce(
96
- TEMPLATE_INFOS,
97
- (acc, { fileName, name }) => ({
98
- ...acc,
99
- [name]: getTemplate({ fileName, name }),
100
- }),
101
- {},
102
- );
96
+ const originalFullPath = this.getTemplateFullPath(templatePaths.original, fileName);
103
97
 
104
- return templatesMap;
105
- };
98
+ if (originalFullPath) {
99
+ fileContent = this.fileSystem.getFileContent(originalFullPath);
100
+ }
101
+ }
106
102
 
107
- const getTemplateContent = (path) => {
108
- const foundTemplatePathKey = _.keys(config.templatePaths).find((key) => _.startsWith(path, `@${key}`));
103
+ return fileContent;
104
+ };
109
105
 
110
- const findPathWithExt = (path) => {
111
- const raw = cropExtension(path);
112
- const pathVariants = TEMPLATE_EXTENSIONS.map((extension) => `${raw}${extension}`);
113
- return pathVariants.find((variant) => pathIsExist(variant));
106
+ getTemplates = ({ templatePaths }) => {
107
+ this.logger.log(`try to read templates from directory "${templatePaths.custom}"`);
108
+
109
+ return _.reduce(
110
+ this.config.templateInfos,
111
+ (acc, { fileName, name }) => ({
112
+ ...acc,
113
+ [name]: this.getTemplate({ fileName, name }),
114
+ }),
115
+ {},
116
+ );
114
117
  };
115
118
 
116
- const rawPath = resolve(_.replace(path, `@${foundTemplatePathKey}`, config.templatePaths[foundTemplatePathKey]));
117
- const fixedPath = findPathWithExt(rawPath);
119
+ getTemplateContent = (path) => {
120
+ const foundTemplatePathKey = _.keys(this.config.templatePaths).find((key) => _.startsWith(path, `@${key}`));
118
121
 
119
- if (fixedPath) {
120
- return getFileContent(fixedPath);
121
- }
122
+ const findPathWithExt = (path) => {
123
+ const raw = this.cropExtension(path);
124
+ const pathVariants = this.config.templateExtensions.map((extension) => `${raw}${extension}`);
125
+ return pathVariants.find((variant) => this.fileSystem.pathIsExist(variant));
126
+ };
122
127
 
123
- const customPath = findPathWithExt(resolve(config.templatePaths.custom, path));
128
+ const rawPath = resolve(
129
+ _.replace(path, `@${foundTemplatePathKey}`, this.config.templatePaths[foundTemplatePathKey]),
130
+ );
131
+ const fixedPath = findPathWithExt(rawPath);
124
132
 
125
- if (customPath) {
126
- return getFileContent(customPath);
127
- }
133
+ if (fixedPath) {
134
+ return this.fileSystem.getFileContent(fixedPath);
135
+ }
128
136
 
129
- const originalPath = findPathWithExt(resolve(config.templatePaths.original, path));
137
+ const customPath = findPathWithExt(resolve(this.config.templatePaths.custom, path));
130
138
 
131
- if (originalPath) {
132
- return getFileContent(originalPath);
133
- }
139
+ if (customPath) {
140
+ return this.fileSystem.getFileContent(customPath);
141
+ }
134
142
 
135
- return "";
136
- };
143
+ const originalPath = findPathWithExt(resolve(this.config.templatePaths.original, path));
137
144
 
138
- const renderTemplate = (template, configuration, options) => {
139
- if (!template) return "";
145
+ if (originalPath) {
146
+ return this.fileSystem.getFileContent(originalPath);
147
+ }
140
148
 
141
- return Eta.render(template, configuration, {
142
- async: false,
143
- ...(options || {}),
144
- includeFile: (path, payload, options) => {
145
- return renderTemplate(getTemplateContent(path), payload, options);
146
- },
147
- });
148
- };
149
+ return "";
150
+ };
151
+
152
+ renderTemplate = (template, configuration, options) => {
153
+ if (!template) return "";
154
+
155
+ return Eta.render(
156
+ template,
157
+ {
158
+ ...this.getRenderTemplateData(),
159
+ ...configuration,
160
+ },
161
+ {
162
+ async: false,
163
+ ...(options || {}),
164
+ includeFile: (path, payload, options) => {
165
+ return this.renderTemplate(this.getTemplateContent(path), payload, options);
166
+ },
167
+ },
168
+ );
169
+ };
170
+ }
149
171
 
150
172
  module.exports = {
151
- getTemplate,
152
- getTemplates,
153
- getTemplatePaths,
154
- renderTemplate,
173
+ Templates,
155
174
  };
@@ -1,49 +1,49 @@
1
- const ts = require("typescript");
2
-
3
- function translate(fileName, content, options) {
4
- const output = {};
5
- const host = ts.createCompilerHost(options, true);
6
- const fileNames = [fileName];
7
- const originalSourceFileGet = host.getSourceFile.bind(host);
8
- host.getSourceFile = (sourceFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
9
- if (sourceFileName !== fileName)
10
- return originalSourceFileGet(sourceFileName, languageVersion, onError, shouldCreateNewSourceFile);
11
-
12
- return ts.createSourceFile(sourceFileName, content, languageVersion, true, ts.ScriptKind.External);
13
- };
14
-
15
- host.writeFile = (fileName, contents) => {
16
- output[fileName] = contents;
17
- };
18
-
19
- ts.createProgram(fileNames, options, host).emit();
20
-
21
- return output;
22
- }
23
-
24
- module.exports = {
25
- translate: (fileName, sourceTypeScript) => {
26
- const translated = translate(fileName, sourceTypeScript, {
27
- module: "ESNext",
28
- noImplicitReturns: true,
29
- alwaysStrict: true,
30
- target: ts.ScriptTarget.ESNext,
31
- declaration: true,
32
- noImplicitAny: false,
33
- sourceMap: false,
34
- removeComments: false,
35
- disableSizeLimit: true,
36
- esModuleInterop: true,
37
- emitDecoratorMetadata: true,
38
- skipLibCheck: true,
39
- });
40
-
41
- const sourceFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Js);
42
- const declarationFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Dts);
43
-
44
- return {
45
- sourceContent: translated[sourceFileName],
46
- declarationContent: translated[declarationFileName],
47
- };
48
- },
49
- };
1
+ const ts = require("typescript");
2
+
3
+ function translate(fileName, content, options) {
4
+ const output = {};
5
+ const host = ts.createCompilerHost(options, true);
6
+ const fileNames = [fileName];
7
+ const originalSourceFileGet = host.getSourceFile.bind(host);
8
+ host.getSourceFile = (sourceFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
9
+ if (sourceFileName !== fileName)
10
+ return originalSourceFileGet(sourceFileName, languageVersion, onError, shouldCreateNewSourceFile);
11
+
12
+ return ts.createSourceFile(sourceFileName, content, languageVersion, true, ts.ScriptKind.External);
13
+ };
14
+
15
+ host.writeFile = (fileName, contents) => {
16
+ output[fileName] = contents;
17
+ };
18
+
19
+ ts.createProgram(fileNames, options, host).emit();
20
+
21
+ return output;
22
+ }
23
+
24
+ module.exports = {
25
+ translate: (fileName, sourceTypeScript) => {
26
+ const translated = translate(fileName, sourceTypeScript, {
27
+ module: "ESNext",
28
+ noImplicitReturns: true,
29
+ alwaysStrict: true,
30
+ target: ts.ScriptTarget.ESNext,
31
+ declaration: true,
32
+ noImplicitAny: false,
33
+ sourceMap: false,
34
+ removeComments: false,
35
+ disableSizeLimit: true,
36
+ esModuleInterop: true,
37
+ emitDecoratorMetadata: true,
38
+ skipLibCheck: true,
39
+ });
40
+
41
+ const sourceFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Js);
42
+ const declarationFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Dts);
43
+
44
+ return {
45
+ sourceContent: translated[sourceFileName],
46
+ declarationContent: translated[declarationFileName],
47
+ };
48
+ },
49
+ };
@@ -0,0 +1,79 @@
1
+ const _ = require("lodash");
2
+
3
+ class TypeName {
4
+ /** @type {Map<string, string>} */
5
+ formattedModelNamesMap = new Map();
6
+
7
+ /** @type {Configuration} */
8
+ config;
9
+
10
+ /** @type {Logger} */
11
+ logger;
12
+
13
+ constructor(config, logger) {
14
+ this.config = config;
15
+ this.logger = logger;
16
+ }
17
+
18
+ /**
19
+ * @param name
20
+ * @param options {{ ignorePrefix?: boolean, ignoreSuffix?: boolean }}
21
+ * @return {string}
22
+ */
23
+ format = (name, options) => {
24
+ const typePrefix = options && options.ignorePrefix ? "" : this.config.typePrefix;
25
+ const typeSuffix = options && options.ignoreSuffix ? "" : this.config.typeSuffix;
26
+ const hashKey = `${typePrefix}_${name}_${typeSuffix}`;
27
+
28
+ if (typeof name !== "string") {
29
+ this.logger.warn("wrong name of the model name", name);
30
+ return name;
31
+ }
32
+
33
+ if (/^([A-Z_]{1,})$/g.test(name)) {
34
+ return _.compact([typePrefix, name, typeSuffix]).join("_");
35
+ }
36
+
37
+ if (this.formattedModelNamesMap.has(hashKey)) {
38
+ return this.formattedModelNamesMap.get(hashKey);
39
+ }
40
+
41
+ const fixedModelName = fixModelName(name);
42
+
43
+ const formattedModelName = _.replace(_.startCase(`${typePrefix}_${fixedModelName}_${typeSuffix}`), /\s/g, "");
44
+ const modelName = this.config.hooks.onFormatTypeName(formattedModelName, name) || formattedModelName;
45
+
46
+ this.formattedModelNamesMap.set(hashKey, modelName);
47
+
48
+ return modelName;
49
+ };
50
+
51
+ isValidName = isValidName;
52
+ }
53
+
54
+ const isValidName = (name) => /^([A-Za-z$_]{1,})$/g.test(name);
55
+
56
+ const fixModelName = (name) => {
57
+ if (!isValidName(name)) {
58
+ if (!/^[a-zA-Z_$]/g.test(name)) {
59
+ name = `Type ${name}`;
60
+ }
61
+
62
+ // specific replaces for TSOA 3.x
63
+ if (name.includes("."))
64
+ name = name
65
+ .replace(/Exclude_keyof[A-Za-z]{1,}/g, (match) => "ExcludeKeys")
66
+ .replace(/%22\~AND\~%22/g, "And")
67
+ .replace(/%22\~OR\~%22/g, "Or")
68
+ .replace(/(\.?%22)|\./g, "_")
69
+ .replace(/__+$/, "");
70
+
71
+ if (name.includes("-")) name = _.startCase(name).replace(/ /g, "");
72
+ }
73
+
74
+ return name;
75
+ };
76
+
77
+ module.exports = {
78
+ TypeName,
79
+ };