swagger-typescript-api 10.0.0 → 10.0.2

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 (56) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +284 -260
  3. package/index.d.ts +7 -1
  4. package/index.js +115 -115
  5. package/package.json +116 -113
  6. package/src/apiConfig.js +30 -30
  7. package/src/common.js +28 -28
  8. package/src/components.js +3 -5
  9. package/src/config.js +4 -0
  10. package/src/constants.js +7 -0
  11. package/src/filePrefix.js +14 -14
  12. package/src/files.js +6 -6
  13. package/src/formatFileContent.js +13 -6
  14. package/src/index.js +271 -270
  15. package/src/logger.js +59 -59
  16. package/src/modelNames.js +78 -78
  17. package/src/modelTypes.js +31 -30
  18. package/src/output.js +165 -166
  19. package/src/prettierOptions.js +23 -23
  20. package/src/render/utils/fmtToJSDocLine.js +10 -10
  21. package/src/render/utils/index.js +31 -23
  22. package/src/render/utils/templateRequire.js +17 -17
  23. package/src/routeNames.js +46 -46
  24. package/src/routes.js +4 -1
  25. package/src/schema.js +87 -64
  26. package/src/swagger.js +4 -1
  27. package/src/templates.js +155 -132
  28. package/src/translators/JavaScript.js +60 -60
  29. package/src/typeFormatters.js +121 -75
  30. package/src/utils/id.js +9 -9
  31. package/src/utils/random.js +14 -14
  32. package/src/utils/resolveName.js +97 -97
  33. package/templates/README.md +17 -13
  34. package/templates/base/README.md +7 -7
  35. package/templates/base/data-contract-jsdoc.ejs +32 -0
  36. package/templates/base/data-contracts.ejs +28 -0
  37. package/templates/base/enum-data-contract.ejs +15 -0
  38. package/templates/base/{http-client.eta → http-client.ejs} +2 -2
  39. package/templates/base/http-clients/{axios-http-client.eta → axios-http-client.ejs} +133 -145
  40. package/templates/base/http-clients/{fetch-http-client.eta → fetch-http-client.ejs} +222 -222
  41. package/templates/base/interface-data-contract.ejs +10 -0
  42. package/templates/base/object-field-jsdoc.ejs +28 -0
  43. package/templates/base/{route-docs.eta → route-docs.ejs} +31 -31
  44. package/templates/base/{route-name.eta → route-name.ejs} +42 -42
  45. package/templates/base/{route-type.eta → route-type.ejs} +21 -21
  46. package/templates/base/type-data-contract.ejs +15 -0
  47. package/templates/default/README.md +6 -6
  48. package/templates/default/{api.eta → api.ejs} +65 -65
  49. package/templates/default/{procedure-call.eta → procedure-call.ejs} +98 -98
  50. package/templates/default/{route-types.eta → route-types.ejs} +28 -28
  51. package/templates/modular/README.md +6 -6
  52. package/templates/modular/{api.eta → api.ejs} +28 -28
  53. package/templates/modular/{procedure-call.eta → procedure-call.ejs} +98 -98
  54. package/templates/modular/{route-types.eta → route-types.ejs} +18 -18
  55. package/CHANGELOG.md +0 -866
  56. package/templates/base/data-contracts.eta +0 -45
package/src/modelNames.js CHANGED
@@ -1,78 +1,78 @@
1
- const _ = require("lodash");
2
- const { config } = require("./config");
3
- const { logger } = require("./logger");
4
-
5
- const isValidName = (name) => /^([A-Za-z$_]{1,})$/g.test(name);
6
-
7
- const formattedModelNamesMap = new Map();
8
-
9
- const fixModelName = (name) => {
10
- if (!isValidName(name)) {
11
- if (!/^[a-zA-Z_$]/g.test(name)) {
12
- name = `Type ${name}`;
13
- }
14
-
15
- // specific replaces for TSOA 3.x
16
- if (name.includes("."))
17
- name = name
18
- .replace(/Exclude_keyof[A-Za-z]{1,}/g, (match) => "ExcludeKeys")
19
- .replace(/%22\~AND\~%22/g, "And")
20
- .replace(/%22\~OR\~%22/g, "Or")
21
- .replace(/(\.?%22)|\./g, "_")
22
- .replace(/__+$/, "");
23
-
24
- if (name.includes("-")) name = _.startCase(name).replace(/ /g, "");
25
- }
26
-
27
- return name;
28
- };
29
-
30
- /**
31
- *
32
- * @param {string} name
33
- * @param {{ ignorePrefix?: boolean; ignoreSuffix?: boolean }} options
34
- * @returns
35
- */
36
- const formatModelName = (name, options) => {
37
- const typePrefix = options && options.ignorePrefix ? "" : config.typePrefix;
38
- const typeSuffix = options && options.ignoreSuffix ? "" : config.typeSuffix;
39
- const hashKey = `${typePrefix}_${name}_${typeSuffix}`;
40
-
41
- if (typeof name !== "string") {
42
- logger.warn("wrong name of the model name", name);
43
- return name;
44
- }
45
-
46
- if (/^([A-Z_]{1,})$/g.test(name)) {
47
- return _.compact([typePrefix, name, typeSuffix]).join("_");
48
- }
49
-
50
- if (formattedModelNamesMap.has(hashKey)) {
51
- return formattedModelNamesMap.get(hashKey);
52
- }
53
-
54
- const fixedModelName = fixModelName(name);
55
-
56
- const formattedModelName = _.replace(
57
- _.startCase(`${typePrefix}_${fixedModelName}_${typeSuffix}`),
58
- /\s/g,
59
- "",
60
- );
61
- const modelName = config.hooks.onFormatTypeName(formattedModelName, name) || formattedModelName;
62
-
63
- formattedModelNamesMap.set(hashKey, modelName);
64
-
65
- return modelName;
66
- };
67
-
68
- const formatEnumKey = (key) =>
69
- formatModelName(key, {
70
- ignorePrefix: true,
71
- ignoreSuffix: true,
72
- });
73
-
74
- module.exports = {
75
- formatModelName: formatModelName,
76
- formatEnumKey: formatEnumKey,
77
- isValidName,
78
- };
1
+ const _ = require("lodash");
2
+ const { config } = require("./config");
3
+ const { logger } = require("./logger");
4
+
5
+ const isValidName = (name) => /^([A-Za-z$_]{1,})$/g.test(name);
6
+
7
+ const formattedModelNamesMap = new Map();
8
+
9
+ const fixModelName = (name) => {
10
+ if (!isValidName(name)) {
11
+ if (!/^[a-zA-Z_$]/g.test(name)) {
12
+ name = `Type ${name}`;
13
+ }
14
+
15
+ // specific replaces for TSOA 3.x
16
+ if (name.includes("."))
17
+ name = name
18
+ .replace(/Exclude_keyof[A-Za-z]{1,}/g, (match) => "ExcludeKeys")
19
+ .replace(/%22\~AND\~%22/g, "And")
20
+ .replace(/%22\~OR\~%22/g, "Or")
21
+ .replace(/(\.?%22)|\./g, "_")
22
+ .replace(/__+$/, "");
23
+
24
+ if (name.includes("-")) name = _.startCase(name).replace(/ /g, "");
25
+ }
26
+
27
+ return name;
28
+ };
29
+
30
+ /**
31
+ *
32
+ * @param {string} name
33
+ * @param {{ ignorePrefix?: boolean; ignoreSuffix?: boolean }} options
34
+ * @returns
35
+ */
36
+ const formatModelName = (name, options) => {
37
+ const typePrefix = options && options.ignorePrefix ? "" : config.typePrefix;
38
+ const typeSuffix = options && options.ignoreSuffix ? "" : config.typeSuffix;
39
+ const hashKey = `${typePrefix}_${name}_${typeSuffix}`;
40
+
41
+ if (typeof name !== "string") {
42
+ logger.warn("wrong name of the model name", name);
43
+ return name;
44
+ }
45
+
46
+ if (/^([A-Z_]{1,})$/g.test(name)) {
47
+ return _.compact([typePrefix, name, typeSuffix]).join("_");
48
+ }
49
+
50
+ if (formattedModelNamesMap.has(hashKey)) {
51
+ return formattedModelNamesMap.get(hashKey);
52
+ }
53
+
54
+ const fixedModelName = fixModelName(name);
55
+
56
+ const formattedModelName = _.replace(
57
+ _.startCase(`${typePrefix}_${fixedModelName}_${typeSuffix}`),
58
+ /\s/g,
59
+ "",
60
+ );
61
+ const modelName = config.hooks.onFormatTypeName(formattedModelName, name) || formattedModelName;
62
+
63
+ formattedModelNamesMap.set(hashKey, modelName);
64
+
65
+ return modelName;
66
+ };
67
+
68
+ const formatEnumKey = (key) =>
69
+ formatModelName(key, {
70
+ ignorePrefix: true,
71
+ ignoreSuffix: true,
72
+ });
73
+
74
+ module.exports = {
75
+ formatModelName: formatModelName,
76
+ formatEnumKey: formatEnumKey,
77
+ isValidName,
78
+ };
package/src/modelTypes.js CHANGED
@@ -1,30 +1,31 @@
1
- const { formatters } = require("./typeFormatters");
2
- const { formatModelName } = require("./modelNames");
3
- const { config } = require("./config");
4
- const { getTypeData } = require("./components");
5
-
6
- /**
7
- *
8
- * @param {import("./components").TypeInfo} typeInfo
9
- * @returns
10
- */
11
- const prepareModelType = (typeInfo) => {
12
- const typeData = getTypeData(typeInfo);
13
- let { typeIdentifier, name: originalName, content, type, description } = typeData;
14
-
15
- const resultContent = formatters[type] ? formatters[type](content) : content;
16
- const name = formatModelName(originalName);
17
-
18
- return {
19
- typeIdentifier,
20
- name,
21
- description,
22
- rawContent: content,
23
- content: resultContent,
24
- typeData,
25
- };
26
- };
27
-
28
- module.exports = {
29
- prepareModelType,
30
- };
1
+ const { formatters } = require("./typeFormatters");
2
+ const { formatModelName } = require("./modelNames");
3
+ const { config } = require("./config");
4
+ const { getTypeData } = require("./components");
5
+
6
+ /**
7
+ *
8
+ * @param {import("./components").TypeInfo} typeInfo
9
+ * @returns
10
+ */
11
+ const prepareModelType = (typeInfo) => {
12
+ const rawTypeData = getTypeData(typeInfo);
13
+ const typeData = formatters[rawTypeData.type] ? formatters[rawTypeData.type](rawTypeData) : rawTypeData;
14
+ let { typeIdentifier, name: originalName, content, description } = typeData;
15
+ const name = formatModelName(originalName);
16
+
17
+ return {
18
+ ...typeData,
19
+ typeIdentifier,
20
+ name,
21
+ description,
22
+ $content: rawTypeData.content,
23
+ rawContent: rawTypeData.content,
24
+ content: content,
25
+ typeData,
26
+ };
27
+ };
28
+
29
+ module.exports = {
30
+ prepareModelType,
31
+ };
package/src/output.js CHANGED
@@ -1,166 +1,165 @@
1
- const _ = require("lodash");
2
- const { getFileContent } = require("./files");
3
- const { classNameCase } = require("./render/utils");
4
- const { renderTemplate } = require("./templates");
5
- const { translate: translateToJS } = require("./translators/JavaScript");
6
- const ts = require("typescript");
7
- const formatFileContent = require("./formatFileContent");
8
-
9
- const getFileNameWithoutExt = (fileName) => {
10
- const fileNameParts = _.split(fileName, ".");
11
-
12
- if (fileNameParts.length > 1) {
13
- fileNameParts.pop();
14
- }
15
-
16
- return fileNameParts.join(".");
17
- };
18
-
19
- const createFileInfo = (configuration, fileName, content) => {
20
- const fixedFileName = getFileNameWithoutExt(fileName);
21
-
22
- if (configuration.translateToJavaScript) {
23
- const { sourceContent, declarationContent } = translateToJS(
24
- `${fixedFileName}${ts.Extension.Ts}`,
25
- content,
26
- );
27
-
28
- return {
29
- name: `${fixedFileName}${ts.Extension.Js}`,
30
- content: formatFileContent(sourceContent),
31
- declaration: {
32
- name: `${fixedFileName}${ts.Extension.Dts}`,
33
- content: formatFileContent(declarationContent),
34
- },
35
- };
36
- }
37
-
38
- return {
39
- name: `${fixedFileName}${ts.Extension.Ts}`,
40
- content: formatFileContent(content),
41
- declaration: null,
42
- };
43
- };
44
-
45
- const createMultipleFileInfos = (templatesToRender, configuration) => {
46
- const { routes } = configuration;
47
- const { fileNames, generateRouteTypes, generateClient } = configuration.config;
48
- const modularApiFileInfos = [];
49
-
50
- if (routes.$outOfModule) {
51
- if (generateRouteTypes) {
52
- const outOfModuleRouteContent = renderTemplate(templatesToRender.routeTypes, {
53
- ...configuration,
54
- route: configuration.routes.$outOfModule,
55
- });
56
-
57
- modularApiFileInfos.push(
58
- createFileInfo(configuration, fileNames.outOfModuleApi, outOfModuleRouteContent),
59
- );
60
- }
61
- if (generateClient) {
62
- const outOfModuleApiContent = renderTemplate(templatesToRender.api, {
63
- ...configuration,
64
- route: configuration.routes.$outOfModule,
65
- });
66
-
67
- modularApiFileInfos.push(
68
- createFileInfo(configuration, fileNames.outOfModuleApi, outOfModuleApiContent),
69
- );
70
- }
71
- }
72
-
73
- if (routes.combined) {
74
- modularApiFileInfos.push(
75
- ..._.reduce(
76
- routes.combined,
77
- (apiFileInfos, route) => {
78
- if (generateRouteTypes) {
79
- const routeModuleContent = renderTemplate(templatesToRender.routeTypes, {
80
- ...configuration,
81
- route,
82
- });
83
-
84
- apiFileInfos.push(
85
- createFileInfo(
86
- configuration,
87
- classNameCase(`${route.moduleName}_Route`),
88
- routeModuleContent,
89
- ),
90
- );
91
- }
92
-
93
- if (generateClient) {
94
- const apiModuleContent = renderTemplate(templatesToRender.api, {
95
- ...configuration,
96
- route,
97
- });
98
-
99
- apiFileInfos.push(
100
- createFileInfo(configuration, classNameCase(route.moduleName), apiModuleContent),
101
- );
102
- }
103
-
104
- return apiFileInfos;
105
- },
106
- [],
107
- ),
108
- );
109
- }
110
-
111
- return [
112
- createFileInfo(
113
- configuration,
114
- fileNames.dataContracts,
115
- renderTemplate(templatesToRender.dataContracts, configuration),
116
- ),
117
- generateClient &&
118
- createFileInfo(
119
- configuration,
120
- fileNames.httpClient,
121
- renderTemplate(templatesToRender.httpClient, configuration),
122
- ),
123
- ...modularApiFileInfos,
124
- ];
125
- };
126
-
127
- const createSingleFileInfo = (templatesToRender, configuration) => {
128
- const { generateRouteTypes, generateClient } = configuration.config;
129
-
130
- return [
131
- createFileInfo(
132
- configuration,
133
- configuration.fileName,
134
- _.compact([
135
- renderTemplate(templatesToRender.dataContracts, configuration),
136
- generateRouteTypes && renderTemplate(templatesToRender.routeTypes, configuration),
137
- generateClient && renderTemplate(templatesToRender.httpClient, configuration),
138
- generateClient && renderTemplate(templatesToRender.api, configuration),
139
- ]).join("\n"),
140
- ),
141
- ];
142
- };
143
-
144
- const createExtraFileInfos = (configuration) => {
145
- return _.map(configuration.extraTemplates, (extraTemplate) => {
146
- return createFileInfo(
147
- configuration,
148
- extraTemplate.name,
149
- renderTemplate(getFileContent(extraTemplate.path), configuration),
150
- );
151
- });
152
- };
153
-
154
- module.exports = {
155
- generateOutputFiles: ({ modular, templatesToRender, configuration }) => {
156
- const output = modular
157
- ? createMultipleFileInfos(templatesToRender, configuration)
158
- : createSingleFileInfo(templatesToRender, configuration);
159
-
160
- if (!_.isEmpty(configuration.extraTemplates)) {
161
- output.push(...createExtraFileInfos(configuration));
162
- }
163
-
164
- return output.filter((fileInfo) => !!fileInfo && !!fileInfo.content);
165
- },
166
- };
1
+ const _ = require("lodash");
2
+ const { getFileContent } = require("./files");
3
+ const { classNameCase } = require("./render/utils");
4
+ const { renderTemplate } = require("./templates");
5
+ const { translate: translateToJS } = require("./translators/JavaScript");
6
+ const ts = require("typescript");
7
+ const formatFileContent = require("./formatFileContent");
8
+ const { config } = require("./config");
9
+
10
+ const getFileNameWithoutExt = (fileName) => {
11
+ const fileNameParts = _.split(fileName, ".");
12
+
13
+ if (fileNameParts.length > 1) {
14
+ fileNameParts.pop();
15
+ }
16
+
17
+ return fileNameParts.join(".");
18
+ };
19
+
20
+ const createFileInfo = (configuration, fileName, content) => {
21
+ const fixedFileName = getFileNameWithoutExt(fileName);
22
+
23
+ if (configuration.translateToJavaScript) {
24
+ const { sourceContent, declarationContent } = translateToJS(`${fixedFileName}${ts.Extension.Ts}`, content);
25
+
26
+ if (config.debug) {
27
+ console.info("generating output for", `${fixedFileName}${ts.Extension.Js}`);
28
+ console.info(sourceContent);
29
+ }
30
+
31
+ if (config.debug) {
32
+ console.info("generating output for", `${fixedFileName}${ts.Extension.Dts}`);
33
+ console.info(declarationContent);
34
+ }
35
+
36
+ return {
37
+ name: `${fixedFileName}${ts.Extension.Js}`,
38
+ content: formatFileContent(sourceContent),
39
+ declaration: {
40
+ name: `${fixedFileName}${ts.Extension.Dts}`,
41
+ content: formatFileContent(declarationContent),
42
+ },
43
+ };
44
+ }
45
+
46
+ if (config.debug) {
47
+ console.info("generating output for", `${fixedFileName}${ts.Extension.Ts}`);
48
+ console.info(content);
49
+ }
50
+
51
+ return {
52
+ name: `${fixedFileName}${ts.Extension.Ts}`,
53
+ content: formatFileContent(content),
54
+ declaration: null,
55
+ };
56
+ };
57
+
58
+ const createMultipleFileInfos = (templatesToRender, configuration) => {
59
+ const { routes } = configuration;
60
+ const { fileNames, generateRouteTypes, generateClient } = configuration.config;
61
+ const modularApiFileInfos = [];
62
+
63
+ if (routes.$outOfModule) {
64
+ if (generateRouteTypes) {
65
+ const outOfModuleRouteContent = renderTemplate(templatesToRender.routeTypes, {
66
+ ...configuration,
67
+ route: configuration.routes.$outOfModule,
68
+ });
69
+
70
+ modularApiFileInfos.push(createFileInfo(configuration, fileNames.outOfModuleApi, outOfModuleRouteContent));
71
+ }
72
+ if (generateClient) {
73
+ const outOfModuleApiContent = renderTemplate(templatesToRender.api, {
74
+ ...configuration,
75
+ route: configuration.routes.$outOfModule,
76
+ });
77
+
78
+ modularApiFileInfos.push(createFileInfo(configuration, fileNames.outOfModuleApi, outOfModuleApiContent));
79
+ }
80
+ }
81
+
82
+ if (routes.combined) {
83
+ modularApiFileInfos.push(
84
+ ..._.reduce(
85
+ routes.combined,
86
+ (apiFileInfos, route) => {
87
+ if (generateRouteTypes) {
88
+ const routeModuleContent = renderTemplate(templatesToRender.routeTypes, {
89
+ ...configuration,
90
+ route,
91
+ });
92
+
93
+ apiFileInfos.push(
94
+ createFileInfo(configuration, classNameCase(`${route.moduleName}_Route`), routeModuleContent),
95
+ );
96
+ }
97
+
98
+ if (generateClient) {
99
+ const apiModuleContent = renderTemplate(templatesToRender.api, {
100
+ ...configuration,
101
+ route,
102
+ });
103
+
104
+ apiFileInfos.push(createFileInfo(configuration, classNameCase(route.moduleName), apiModuleContent));
105
+ }
106
+
107
+ return apiFileInfos;
108
+ },
109
+ [],
110
+ ),
111
+ );
112
+ }
113
+
114
+ return [
115
+ createFileInfo(
116
+ configuration,
117
+ fileNames.dataContracts,
118
+ renderTemplate(templatesToRender.dataContracts, configuration),
119
+ ),
120
+ generateClient &&
121
+ createFileInfo(configuration, fileNames.httpClient, renderTemplate(templatesToRender.httpClient, configuration)),
122
+ ...modularApiFileInfos,
123
+ ];
124
+ };
125
+
126
+ const createSingleFileInfo = (templatesToRender, configuration) => {
127
+ const { generateRouteTypes, generateClient } = configuration.config;
128
+
129
+ return [
130
+ createFileInfo(
131
+ configuration,
132
+ configuration.fileName,
133
+ _.compact([
134
+ renderTemplate(templatesToRender.dataContracts, configuration),
135
+ generateRouteTypes && renderTemplate(templatesToRender.routeTypes, configuration),
136
+ generateClient && renderTemplate(templatesToRender.httpClient, configuration),
137
+ generateClient && renderTemplate(templatesToRender.api, configuration),
138
+ ]).join("\n"),
139
+ ),
140
+ ];
141
+ };
142
+
143
+ const createExtraFileInfos = (configuration) => {
144
+ return _.map(configuration.extraTemplates, (extraTemplate) => {
145
+ return createFileInfo(
146
+ configuration,
147
+ extraTemplate.name,
148
+ renderTemplate(getFileContent(extraTemplate.path), configuration),
149
+ );
150
+ });
151
+ };
152
+
153
+ module.exports = {
154
+ generateOutputFiles: ({ modular, templatesToRender, configuration }) => {
155
+ const output = modular
156
+ ? createMultipleFileInfos(templatesToRender, configuration)
157
+ : createSingleFileInfo(templatesToRender, configuration);
158
+
159
+ if (!_.isEmpty(configuration.extraTemplates)) {
160
+ output.push(...createExtraFileInfos(configuration));
161
+ }
162
+
163
+ return output.filter((fileInfo) => !!fileInfo && !!fileInfo.content);
164
+ },
165
+ };
@@ -1,23 +1,23 @@
1
- const { cosmiconfigSync } = require("cosmiconfig");
2
- const constants = require("./constants");
3
-
4
- /**
5
- * Get prettier options from user's project or return the default one
6
- * @return {import('prettier').Options} Prettier options
7
- */
8
- function getPrettierOptions() {
9
- const prettier = cosmiconfigSync("prettier").search();
10
-
11
- if (prettier) {
12
- return {
13
- ...prettier.config,
14
- parser: "typescript",
15
- };
16
- }
17
-
18
- return constants.PRETTIER_OPTIONS;
19
- }
20
-
21
- module.exports = {
22
- getPrettierOptions,
23
- };
1
+ const { cosmiconfigSync } = require("cosmiconfig");
2
+ const constants = require("./constants");
3
+
4
+ /**
5
+ * Get prettier options from user's project or return the default one
6
+ * @return {import('prettier').Options} Prettier options
7
+ */
8
+ function getPrettierOptions() {
9
+ const prettier = cosmiconfigSync("prettier").search();
10
+
11
+ if (prettier) {
12
+ return {
13
+ ...prettier.config,
14
+ parser: "typescript",
15
+ };
16
+ }
17
+
18
+ return constants.PRETTIER_OPTIONS;
19
+ }
20
+
21
+ module.exports = {
22
+ getPrettierOptions,
23
+ };
@@ -1,10 +1,10 @@
1
- /**
2
- * Format a line in JSDOC line by adding ' *' at the beginning and \n character at the end
3
- *
4
- * @example 'abc' -> ' * abc\n'
5
- * @param line
6
- * @return {string}
7
- */
8
- module.exports = function fmtToJSDocLine(line, { eol = true }) {
9
- return ` * ${line}${eol ? "\n" : ""}`;
10
- };
1
+ /**
2
+ * Format a line in JSDOC line by adding ' *' at the beginning and \n character at the end
3
+ *
4
+ * @example 'abc' -> ' * abc\n'
5
+ * @param line
6
+ * @return {string}
7
+ */
8
+ module.exports = function fmtToJSDocLine(line, { eol = true }) {
9
+ return ` * ${line}${eol ? "\n" : ""}`;
10
+ };