Skip to content

Commit b4d2af4

Browse files
committed
BREAKING_CHANGE: fully rewrite render Eta templates process, overriding only one template(#166), template path prefixes
1 parent f162595 commit b4d2af4

31 files changed

+875
-400
lines changed

.vscode/launch.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
"cwd": "${workspaceFolder}",
3636
"runtimeExecutable": "npm",
3737
"runtimeArgs": ["run-script", "node:debug"]
38+
},
39+
{
40+
"name": "Debug partialTemplates test",
41+
"type": "node",
42+
"request": "launch",
43+
"cwd": "${workspaceFolder}",
44+
"runtimeExecutable": "npm",
45+
"runtimeArgs": ["run-script", "test:partialBaseTemplate"]
3846
}
3947
]
4048
}

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# next release
22

3+
BREAKING_CHANGES:
4+
- Ability to override only one template (issue #166, thanks @Nihisil)
5+
6+
Features:
7+
- template path prefixes `@base`, `@default`, `@modular` (using in Eta templates, `includeFile()`, see README.md)
8+
39
# 5.1.7
410

511
Fixes:

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,24 @@ How to use it:
144144
1. copy swagger-typescript-api templates into your place in project
145145
- from [/templates/default](https://github.com/acacode/swagger-typescript-api/tree/next/templates/default) for single api file
146146
- from [/templates/modular](https://github.com/acacode/swagger-typescript-api/tree/next/templates/modular) for multiple api files (with `--modular` option)
147+
- from [/templates/base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base) for base templates (templates using both in default and modular)
147148
1. add `--templates PATH_TO_YOUR_TEMPLATES` option
148149
2. modify [ETA](https://eta.js.org/docs/syntax) templates as you like
149150

151+
NOTE:
152+
Eta has special directive to render template in your Eta templates - `includeFile(pathToTemplate, payload)`
153+
If you want to use some default templates from this tool you can use path prefixes: `@base`, `@default`, `@modular`.
154+
Examples:
155+
- `includeFile("@base/data-contracts.eta", configuration)`
156+
- `includeFile("@default/api.eta", configuration)`
157+
- `includeFile("@default/procedure-call.eta", configuration)`
158+
- `includeFile("@modular/api.eta", configuration)`
159+
- `includeFile("@modular/procedure-call.eta", configuration)`
160+
- `includeFile("@base/route-docs.eta", configuration)`
161+
- `includeFile("@base/route-name.eta", configuration)`
162+
- `includeFile("@base/route-type.eta", configuration)`
163+
- `includeFile("@base/route-types.eta", configuration)`
164+
150165
### **`--module-name-index`**
151166
This option should be used in cases when you have api with one global prefix like `/api`
152167
Example:

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"test:--extract-request-params": "node tests/spec/extractRequestParams/test.js",
3030
"test:--enum-names-as-values": "node tests/spec/enumNamesAsValues/test.js",
3131
"test:--default-response": "node tests/spec/defaultResponse/test.js",
32-
"test:--js": "node tests/spec/js/test.js"
32+
"test:--js": "node tests/spec/js/test.js",
33+
"test:partialBaseTemplate": "node tests/spec/partialBaseTemplate/test.js",
34+
"test:partialDefaultTemplate": "node tests/spec/partialDefaultTemplate/test.js"
3335
},
3436
"author": "acacode",
3537
"license": "MIT",

src/config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ const config = {
4949
},
5050
defaultResponseType: constants.TS_KEYWORDS.VOID,
5151
singleHttpClient: false,
52+
templatePaths: {
53+
/** `templates/base` */
54+
base: "",
55+
/** `templates/default` */
56+
default: "",
57+
/** `templates/modular` */
58+
modular: "",
59+
/** usage path if `--templates` option is not set */
60+
original: "",
61+
/** custom path to templates (`--templates`) */
62+
custom: "",
63+
},
64+
/** Record<templateName, templateContent> */
65+
templatesToRender: {},
5266
};
5367

5468
/** needs to use data everywhere in project */

src/files.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ const { resolve } = require("path");
44
const { filePrefix } = require("./filePrefix");
55
const makeDir = require("make-dir");
66

7-
const getFileContent = (path) => fs.readFileSync(path, { encoding: "UTF-8" });
7+
const getFileContent = (path) => {
8+
return fs.readFileSync(path, { encoding: "UTF-8" });
9+
};
810

911
const pathIsDir = (path) => {
1012
if (!path) return false;

src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const { getSwaggerObject, fixSwaggerScheme, convertSwaggerObject } = require("./
1515
const { createComponentsMap, filterComponentsMap } = require("./components");
1616
const { createFile, pathIsExist, pathIsDir, createDir, cleanDir } = require("./files");
1717
const { addToConfig, config } = require("./config");
18-
const { getTemplates } = require("./templates");
18+
const { getTemplates, getTemplatePaths } = require("./templates");
1919
const constants = require("./constants");
2020
const { generateOutputFiles } = require("./output");
2121

@@ -66,6 +66,10 @@ module.exports = {
6666
});
6767
(spec ? convertSwaggerObject(spec) : getSwaggerObject(input, url, disableStrictSSL))
6868
.then(({ usageSchema, originalSchema }) => {
69+
const templatePaths = getTemplatePaths(config);
70+
71+
addToConfig({ templatePaths });
72+
6973
const templatesToRender = getTemplates(config);
7074

7175
console.log("☄️ start generating your typescript api");

src/templates.js

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,74 @@
11
const _ = require("lodash");
22
const Eta = require("eta");
33
const { getFileContent, pathIsExist } = require("./files");
4+
const { config, addToConfig } = require("./config");
45
const { resolve } = require("path");
56

6-
const getTemplates = ({ templates, modular }) => {
7-
const originalTemplatesPath = resolve(
8-
__dirname,
9-
modular ? "../templates/modular" : "../templates/default",
10-
);
7+
/**
8+
* name - project template name,
9+
* fileName - template file name,
10+
*/
11+
const TEMPLATE_INFOS = [
12+
{ name: "api", fileName: "api.eta" },
13+
{ name: "dataContracts", fileName: "data-contracts.eta" },
14+
{ name: "httpClient", fileName: "http-client.eta" },
15+
{ name: "routeTypes", fileName: "route-types.eta" },
16+
{ name: "routeName", fileName: "route-name.eta" },
17+
];
18+
19+
const getTemplatePaths = ({ templates, modular }) => {
20+
const baseTemplatesPath = resolve(__dirname, "../templates/base");
21+
const defaultTemplatesPath = resolve(__dirname, "../templates/default");
22+
const modularTemplatesPath = resolve(__dirname, "../templates/modular");
23+
const originalTemplatesPath = modular ? modularTemplatesPath : defaultTemplatesPath;
1124
const customTemplatesPath = templates ? resolve(process.cwd(), templates) : originalTemplatesPath;
1225

13-
console.log(`✨ try to read templates from directory "${customTemplatesPath}"`);
26+
return {
27+
/** `templates/base` */
28+
base: baseTemplatesPath,
29+
/** `templates/default` */
30+
default: defaultTemplatesPath,
31+
/** `templates/modular` */
32+
modular: modularTemplatesPath,
33+
/** usage path if `--templates` option is not set */
34+
original: originalTemplatesPath,
35+
/** custom path to templates (`--templates`) */
36+
custom: customTemplatesPath,
37+
};
38+
};
39+
40+
const getTemplates = ({ templatePaths }) => {
41+
console.log(`✨ try to read templates from directory "${templatePaths.custom}"`);
1442

1543
Eta.configure({
16-
views: customTemplatesPath,
44+
views: templatePaths.custom,
1745
});
1846

19-
const templatePaths = {
20-
api: "./api.eta",
21-
dataContracts: "./data-contracts.eta",
22-
httpClient: "./http-client.eta",
23-
routeTypes: "./route-types.eta",
24-
routeName: "./route-name.eta",
25-
};
26-
2747
const templatesMap = _.reduce(
28-
templatePaths,
29-
(acc, pathToTemplate, key) => {
30-
const customFullPath = resolve(customTemplatesPath, pathToTemplate)
48+
TEMPLATE_INFOS,
49+
(acc, { fileName, name }) => {
50+
const customFullPath = resolve(templatePaths.custom, "./", fileName);
3151
let fileContent = pathIsExist(customFullPath) && getFileContent(customFullPath);
3252

3353
if (!fileContent) {
34-
console.log(
35-
`❗❗❗ ${_.lowerCase(key)} template not found in ${pathToTemplate}\n` +
36-
`Code generator will use the default template`,
37-
);
38-
fileContent = getFileContent(resolve(originalTemplatesPath, pathToTemplate));
54+
const baseFullPath = resolve(templatePaths.base, "./", fileName);
55+
const originalFullPath = resolve(templatePaths.original, "./", fileName);
56+
57+
if (pathIsExist(baseFullPath)) {
58+
fileContent = getFileContent(baseFullPath);
59+
} else {
60+
console.log(
61+
`❗❗❗ ${_.lowerCase(name)} template not found in ${customFullPath}\n` +
62+
`Code generator will use the default template`,
63+
);
64+
}
65+
66+
if (pathIsExist(originalFullPath)) {
67+
fileContent = getFileContent(originalFullPath);
68+
}
3969
}
4070

41-
acc[key] = fileContent;
71+
acc[name] = fileContent;
4272

4373
return acc;
4474
},
@@ -48,13 +78,46 @@ const getTemplates = ({ templates, modular }) => {
4878
return templatesMap;
4979
};
5080

81+
const getTemplateContent = (path) => {
82+
let fixedPath = _.endsWith(path, ".eta") ? path : `${path}.eta`;
83+
84+
_.keys(config.templatePaths).forEach((key) => {
85+
if (_.startsWith(fixedPath, `@${key}`)) {
86+
fixedPath = resolve(_.replace(fixedPath, `@${key}`, config.templatePaths[key]));
87+
}
88+
});
89+
90+
if (pathIsExist(fixedPath)) {
91+
return getFileContent(fixedPath);
92+
}
93+
94+
const customPath = resolve(config.templatePaths.custom, fixedPath);
95+
96+
if (pathIsExist(customPath)) {
97+
return getFileContent(customPath);
98+
}
99+
100+
const originalPath = resolve(config.templatePaths.original, fixedPath);
101+
102+
if (pathIsExist(originalPath)) {
103+
return getFileContent(originalPath);
104+
}
105+
106+
return "";
107+
};
108+
51109
const renderTemplate = (template, configuration, options) => {
52110
if (!template) return "";
53111

54-
return Eta.render(template, configuration, { async: false, ...(options || {}) });
112+
return Eta.render(template, configuration, {
113+
async: false,
114+
...(options || {}),
115+
includeFile: (path, payload) => renderTemplate(getTemplateContent(path), payload),
116+
});
55117
};
56118

57119
module.exports = {
58120
getTemplates,
121+
getTemplatePaths,
59122
renderTemplate,
60123
};
File renamed without changes.
File renamed without changes.
File renamed without changes.

templates/default/route-type.eta renamed to templates/base/route-type.eta

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { route, utils, config } = it;
33
const { _, classNameCase, require } = utils;
44
const { query, payload, pathParams, headers } = route.request;
55

6-
const routeDocs = includeFile("./route-docs", { config, route, utils });
6+
const routeDocs = includeFile("@base/route-docs", { config, route, utils });
77
const routeNamespace = classNameCase(route.routeName.usage);
88

99
%>

templates/default/route-types.eta renamed to templates/base/route-types.eta

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ const { utils, config, routes } = it;
77

88
<% routes.outOfModule && routes.outOfModule.forEach(({ routes = [] }) => { %>
99
<% routes.forEach((route) => { %>
10-
<%~ includeFile('./route-type.eta', { route, utils, config }) %>
10+
<%~ includeFile('@base/route-type.eta', { route, utils, config }) %>
1111
<% }) %>
1212
<% }) %>
1313

1414
<% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %>
1515
export namespace <%~ moduleName %> {
1616
<% routes.forEach((route) => { %>
17-
<%~ includeFile('./route-type.eta', { route, utils, config }) %>
17+
<%~ includeFile('@base/route-type.eta', { route, utils, config }) %>
1818
<% }) %>
1919
}
2020

templates/default/procedure-call.eta

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { requestBodyInfo, responseBodyInfo } = route;
44
const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils;
55
const { parameters, path, method, payload, params, query, formData, security, requestParams } = route.request;
66
const { type, errorType, contentTypes } = route.response;
7-
const routeDocs = includeFile("./route-docs", { config, route, utils });
7+
const routeDocs = includeFile("@base/route-docs", { config, route, utils });
88
const queryName = (query && query.name) || "query";
99
const pathParams = _.values(parameters);
1010

templates/modular/data-contracts.eta

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)