mushroom-cli 1.2.7 → 2.0.0-alpha.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/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Mushroom CLI
2
2
 
3
+ > In the examples below, `[...]` marks an optional argument and `|` separates alternatives. They are notation, not
4
+ > characters to type. Quote any argument containing spaces, for example
5
+ > `npx mushroom-cli config add "./my api/project.json"`.
6
+
3
7
  ## Init
4
8
 
5
9
  Init a mushroom json file
@@ -8,10 +12,10 @@ Init a mushroom json file
8
12
  npx mushroom-cli init json ./api/SampleProject.json [silent]
9
13
  ```
10
14
 
11
- Init a DotNet Core project which pre-install Mushroom Server
15
+ Init a DotNet project
12
16
 
13
17
  ```bash
14
- npx mushroom-cli init netcore [projectName]
18
+ npx mushroom-cli init netcore [projectName] [--version latest|alpha|0.2|0.3]
15
19
  ```
16
20
 
17
21
  ## Configure
@@ -31,19 +35,19 @@ npx mushroom-cli config gen projectName docs ./wwwroot/docs.html [alias aliasNam
31
35
  Config gen database docs
32
36
 
33
37
  ```bash
34
- npx mushroom-cli config gen projectName "db-docs" ./wwwroot/db-docs.html [alias aliasName] [features file,auth,mail]
38
+ npx mushroom-cli config gen projectName db-docs ./wwwroot/db-docs.html [alias aliasName] [features file,auth,mail]
35
39
  ```
36
40
 
37
41
  Config gen javascript driver
38
42
 
39
43
  ```bash
40
- npx mushroom-cli config gen projectName "js-driver" ./src/api/mushroom.api.js [alias aliasName] [roles Admin,User-Contributor] [tarball tarballName]
44
+ npx mushroom-cli config gen projectName js-driver ./src/api/mushroom.api.js [alias aliasName] [roles Admin,User-Contributor]
41
45
  ```
42
46
 
43
47
  Config gen typescript driver
44
48
 
45
49
  ```bash
46
- npx mushroom-cli config gen projectName "ts-driver" ./src/api/mushroom.api.ts [alias aliasName] [roles Admin,User-Contributor]
50
+ npx mushroom-cli config gen projectName ts-driver ./src/api/mushroom.api.ts [alias aliasName] [roles Admin,User-Contributor]
47
51
  ```
48
52
 
49
53
  Config gen entities for Mushroom project in CSharp and using MongoDb
@@ -58,36 +62,30 @@ Config gen entities for CSharp project which calls Mushroom API
58
62
  npx mushroom-cli config gen projectName "c#-driver" ./API/Generated.cs [roles Admin,User-Contributor] [alias aliasName] [access-modifier public] [features file,auth,mail]
59
63
  ```
60
64
 
61
- ## Genarate
65
+ ## Generate
62
66
 
63
67
  Generate docs api (html format)
64
68
 
65
69
  ```bash
66
- npx mushroom-cli gen projectName[@aliasName] docs [v1] [tmp]
70
+ npx mushroom-cli gen projectName[@aliasName] docs [tmp]
67
71
  ```
68
72
 
69
73
  Generate database docs (html format)
70
74
 
71
75
  ```bash
72
- npx mushroom-cli gen projectName[@aliasName] "db-docs" [tmp]
73
- ```
74
-
75
- Generate npm package (tarball file)
76
-
77
- ```bash
78
- npx mushroom-cli gen projectName[@aliasName] "js-driver-tarball"
76
+ npx mushroom-cli gen projectName[@aliasName] db-docs [tmp]
79
77
  ```
80
78
 
81
79
  Generate javascript driver
82
80
 
83
81
  ```bash
84
- npx mushroom-cli gen projectName[@aliasName] "js-driver"
82
+ npx mushroom-cli gen projectName[@aliasName] js-driver
85
83
  ```
86
84
 
87
85
  Generate typescript driver
88
86
 
89
87
  ```bash
90
- npx mushroom-cli gen projectName[@aliasName] "ts-driver"
88
+ npx mushroom-cli gen projectName[@aliasName] ts-driver
91
89
  ```
92
90
 
93
91
  Generate entities for Mushroom project in CSharp and using MongoDb
@@ -113,13 +111,13 @@ npx mushroom-cli merge output-path.json
113
111
  Update ts code (configuration was stored in mushroom-config.json file)
114
112
 
115
113
  ```bash
116
- npx mushroom-cli update "ts-code"
114
+ npx mushroom-cli update ts-code
117
115
  ```
118
116
 
119
117
  Update ts code (simple call)
120
118
 
121
119
  ```bash
122
- npx mushroom-cli update "ts-code" file-path.ts remote-ts-code-url
120
+ npx mushroom-cli update ts-code file-path.ts remote-ts-code-url
123
121
  ```
124
122
 
125
123
  ## Other
@@ -132,6 +130,49 @@ npx mushroom-cli parameters --no-check-update
132
130
 
133
131
  ## Version
134
132
 
133
+ ### 2.0.0-alpha.0
134
+
135
+ Breaking changes:
136
+
137
+ - gen docs: remove the docs engine v1. The `v1` parameter is now ignored with a warning and the v1.2 engine is always
138
+ used
139
+ - gen js-driver: remove the `js-driver-tarball` command, the `tarball tarballName` option of `config gen js-driver`
140
+ and the `jsDriver.alias.*.tarballName` field in mushroom-config.json
141
+ - gen c#-mongo: generated code now requires Mushroom Server 0.3 or higher
142
+ - require Node.js `>=14.13.1`
143
+
144
+ New features:
145
+
146
+ - init netcore: `npx mushroom-cli init netcore [--version 0.2|0.3|latest|alpha]` to select the template branch
147
+ - init netcore: the `replace` step accepts wildcards in the file name, for example `./api/*.json` or
148
+ `./api/dulich-*.json`, and resolves them recursively including sub-folders
149
+ - gen c#-mongo: support `routes` in the master file (`path`, `pathMode`, `caseInsensitive`, `httpMethod`, `grant`,
150
+ `deny`, `logRetentionMode`, `delegator`, `codeHint`)
151
+ - gen c#-mongo: support `extensions` in the master file (`startup`, `config`, `ensureIndex`, `routes`) and generate
152
+ the matching extension event handlers
153
+ - gen c#-mongo: new registration signatures `RegisterJob`, `RegisterBeforeEvent`, `RegisterAfterEvent`,
154
+ `RegisterCustomLoginAsyncHandler`, `RegisterGetAuthLogInfoAsyncHandler`, `RegisterLoginSuccessHandler`,
155
+ `RegisterSendCodeItemHandler`, `RegisterCustomVnTextBuilderMethod`
156
+
157
+ Improvement/Changes:
158
+
159
+ - gen docs: inline `bootstrap.min.css`, `bootstrap-theme.min.css`, `bootstrap.min.js` and `jquery.min.js` into the
160
+ html. The generated docs is now a single self-contained file and no static file is copied next to it
161
+ - gen docs: build the whole document in memory and write it once, about 4 times faster
162
+ - gen c#-mongo, gen c#-driver: escape descriptions for XML docs comments
163
+ - init netcore: clone the template with `--depth 1`
164
+ - replace `minify` with `html-minifier-terser` and use `open` 8: installing the CLI drops from 84 packages (75 MB) to
165
+ 36 packages (20 MB) and works on Node.js 14 again
166
+ - the `only-allow npm` check moved from `preinstall` to `prepare` so it no longer runs when the CLI is installed as a
167
+ dependency
168
+
169
+ ### 1.2.8
170
+
171
+ Improvement
172
+
173
+ - gen docs: fix bug: gen docs fail when entity action has codehint.delegator.params
174
+ - gen docs: fix bug: gen wrong docs for entity view params
175
+
135
176
  ### 1.2.6, 1.2.7
136
177
 
137
178
  Hot fixed
package/dist/common.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import readlineSync from 'readline-sync';
5
4
  const __filename = fileURLToPath(import.meta.url);
6
5
  const __dirname = path.dirname(__filename);
7
6
  export function getPackageVersion() {
@@ -131,148 +130,6 @@ export function filterRoles(roleList, allowRoleList) {
131
130
  });
132
131
  return roles;
133
132
  }
134
- export function processTemplate(basePath, templatePath) {
135
- const templateJsonContent = fs.readFileSync(path.join(basePath, templatePath)).toString();
136
- const template = JSON.parse(templateJsonContent);
137
- if (template && typeof template !== 'object') {
138
- throw new TypeError(`Invalid template file ${templatePath}`);
139
- }
140
- const commands = template.steps;
141
- if (!Array.isArray(commands)) {
142
- throw new TypeError(`Invalid template file ${templatePath}`);
143
- }
144
- const variables = {};
145
- commands.forEach((command) => processCommand(command, basePath, variables));
146
- return variables;
147
- }
148
- function processCommand(command, basePath, variables) {
149
- const commandName = command.command;
150
- switch (commandName) {
151
- case 'input':
152
- processInputCommand(command, variables);
153
- return;
154
- case 'set':
155
- processSetCommand(command, variables);
156
- return;
157
- case 'rename':
158
- processRenameCommand(command, basePath, variables);
159
- return;
160
- case 'delete':
161
- processDeleteCommand(command, basePath, variables);
162
- return;
163
- case 'replace':
164
- processReplaceCommand(command, basePath, variables);
165
- return;
166
- default:
167
- throw new Error(`Unknown command: ${commandName}`);
168
- }
169
- }
170
- function processInputCommand(command, variables) {
171
- const value = readlineSync.question(command.message ?? '');
172
- if (command.validation) {
173
- if (command.validation.required) {
174
- if (value === '')
175
- throw new TypeError(`${command.variable} is required`);
176
- }
177
- if (command.validation.pattern) {
178
- if (!new RegExp(command.validation.pattern).test(value))
179
- throw new Error(`${value} is not valid`);
180
- }
181
- }
182
- variables[command.variable] = value;
183
- }
184
- function processSetCommand(command, variables) {
185
- switch (command['function']) {
186
- case 'New-GUID':
187
- processSetNewGuidCommand(command.variable, variables);
188
- break;
189
- case 'New-RandomNumber':
190
- processSetNewRandomNumberCommand(command.variable, variables, command.min, command.max);
191
- break;
192
- case 'New-RandomString':
193
- processSetNewRandomStringCommand(command.variable, variables, command.length, command.characterPool);
194
- break;
195
- case 'ToLowerCase':
196
- toLowerCommand(command.variable, variables, command.input);
197
- break;
198
- case 'ToUpperCase':
199
- toUpperCaseCommand(command.variable, variables, command.input);
200
- break;
201
- case 'Trim':
202
- trimCommand(command.variable, variables, command.input);
203
- break;
204
- case 'Replace':
205
- replaceCommand(command.variable, variables, command.input, command.replacement, command.pattern, command.mode);
206
- break;
207
- default:
208
- throw new Error(`Unknown set-command: ${command['function']}`);
209
- }
210
- }
211
- function processSetNewGuidCommand(variable, variables) {
212
- variables[variable] = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
213
- .replace(/[xy]/g, (c) => {
214
- const r = (Math.random() * 16) | 0;
215
- const v = c == 'x' ? r : (r & 0x3) | 0x8;
216
- return v.toString(16);
217
- })
218
- .toUpperCase();
219
- }
220
- function processSetNewRandomNumberCommand(variable, variables, min = 0, max = (1 << 30) * 4) {
221
- variables[variable] = Math.floor(min + Math.random() * (max - min)).toString();
222
- }
223
- function processSetNewRandomStringCommand(variable, variables, length, characterPool = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM') {
224
- let result = '';
225
- for (let i = 0; i < length; i++) {
226
- result += characterPool.charAt(Math.floor(Math.random() * characterPool.length));
227
- }
228
- variables[variable] = result;
229
- }
230
- function toLowerCommand(variable, variables, input) {
231
- variables[variable] = variables[input].toLowerCase();
232
- }
233
- function toUpperCaseCommand(variable, variables, input) {
234
- variables[variable] = variables[input].toUpperCase();
235
- }
236
- function trimCommand(variable, variables, input) {
237
- variables[variable] = variables[input].trim();
238
- }
239
- function replaceCommand(variable, variables, input, replacement, pattern, mode) {
240
- const regex = new RegExp(pattern, mode);
241
- variables[variable] = variables[input].replace(regex, replacement);
242
- }
243
- function processRenameCommand(command, basePath, variables) {
244
- const input = replaceVariables(command.input, variables);
245
- const output = replaceVariables(command.output, variables);
246
- fs.renameSync(path.join(basePath, input), path.join(basePath, output));
247
- }
248
- function processDeleteCommand(command, basePath, variables) {
249
- const input = replaceVariables(command.input, variables);
250
- const deletedPath = path.join(basePath, input);
251
- if (fs.lstatSync(deletedPath).isDirectory()) {
252
- fs.rmSync(deletedPath, { recursive: true, force: true });
253
- }
254
- else {
255
- fs.rmSync(deletedPath, { force: true });
256
- }
257
- }
258
- function processReplaceCommand(command, basePath, variables) {
259
- const files = command.input;
260
- const pattern = new RegExp(command.pattern ?? '', 'g');
261
- const replacement = replaceVariables(command.replacement ?? '', variables);
262
- files.forEach((file) => {
263
- const filePath = path.join(basePath, replaceVariables(file, variables));
264
- if (!fs.existsSync(filePath)) {
265
- console.warn(`File ${file} does not exist, skipping...`);
266
- return;
267
- }
268
- let content = fs.readFileSync(filePath).toString();
269
- content = content.replace(pattern, () => replacement);
270
- fs.writeFileSync(filePath, content);
271
- });
272
- }
273
- function replaceVariables(str, variables) {
274
- return str.replace(/\$\{(\w+)\}/g, (_, varName) => variables[varName] ?? '');
275
- }
276
133
  export function logInfo(message, ...parameters) {
277
134
  console.log(message, ...parameters);
278
135
  }
package/dist/config.js CHANGED
@@ -170,13 +170,6 @@ function configGenJavascriptDriver(args, mushroomConfig, projectName) {
170
170
  alias = strAlias;
171
171
  break;
172
172
  }
173
- case 'tarball': {
174
- index++;
175
- const strTarball = args[index];
176
- if (strTarball)
177
- parameters.tarballName = strTarball;
178
- break;
179
- }
180
173
  }
181
174
  index++;
182
175
  }
@@ -1310,8 +1310,8 @@
1310
1310
  // fieldName: value
1311
1311
  };
1312
1312
  try {
1313
- const newId = await mushroom.<span data-mt-attr='{"text": "$entityName"}'></span>.createAsync(<span data-mt-attr='{"text": "$entityName"}'></span>_object);
1314
- console.log("Thêm mới thành công, new id: %o", newId);
1313
+ const response = await mushroom.<span data-mt-attr='{"text": "$entityName"}'></span>.createAsync(<span data-mt-attr='{"text": "$entityName"}'></span>_object);
1314
+ console.log("Thêm mới thành công, new id: %o", response.result);
1315
1315
  } catch (error) {
1316
1316
  console.error("Có lỗi: %o", error);
1317
1317
  }</pre>
@@ -1025,7 +1025,7 @@ function renderGeneralAction(args) {
1025
1025
  type: (field.type === 'array' ? 'array of ' : '') + (field.items?.type ?? 'string'),
1026
1026
  description: field.description ?? '',
1027
1027
  required: field.required ? 'required' : 'optional',
1028
- validation: getValidationStringDocs(p, true),
1028
+ validation: getValidationStringDocs(field, true),
1029
1029
  })
1030
1030
  }).join('');
1031
1031
  } else {
@@ -1150,7 +1150,7 @@ function renderEntityViewRolePage(entityName, viewName, grant) {
1150
1150
  type: (p.isArray ? 'array of ' : '') + (p.type ?? 'string'),
1151
1151
  description: p.description ?? '',
1152
1152
  defaultValue: p.defaultValue ? `, default (${p.defaultValue})` : '',
1153
- required: p.required ? 'required' : 'optional',
1153
+ required: p.validation?.required ? 'required' : 'optional',
1154
1154
  validation: getValidationStringDocs(p.validation, true),
1155
1155
  }),
1156
1156
  )
@@ -0,0 +1,195 @@
1
+ const extEvents = {
2
+ auth: {
3
+ 'Events.Login.Before': {
4
+ type: 'Before',
5
+ },
6
+ 'Events.Login.Validate.Before': {
7
+ type: 'Before',
8
+ },
9
+ 'Events.Login.Validate.After': {
10
+ type: 'After',
11
+ },
12
+ 'Events.Login.CheckLocking.Before': {
13
+ type: 'Before',
14
+ },
15
+ 'Events.Login.CheckLocking.After': {
16
+ type: 'After',
17
+ data: 'Mushroom.Extensions.DefaultAuth.UserLock',
18
+ },
19
+ CustomLoginAsyncFunc: {
20
+ type: 'Func',
21
+ registerFunc: 'RegisterCustomLoginAsyncHandler',
22
+ },
23
+ 'Events.Login.LogAuth.Before': {
24
+ type: 'Before',
25
+ },
26
+ GetAuthLogInfoAsyncFunc: {
27
+ type: 'Func',
28
+ registerFunc: 'RegisterGetAuthLogInfoAsyncHandler',
29
+ },
30
+ 'Events.Login.LogAuth.After': {
31
+ type: 'After',
32
+ data: 'Mushroom.Extensions.DefaultAuth.UserAuthLogResult',
33
+ },
34
+ 'Events.Login.LoginFail': {
35
+ type: 'After',
36
+ },
37
+ 'Events.Login.LoginSuccess': {
38
+ type: 'Event',
39
+ registerFunc: 'RegisterLoginSuccessHandler',
40
+ },
41
+ 'Events.Login.After': {
42
+ type: 'After',
43
+ data: 'string',
44
+ },
45
+ 'Events.Logout.Before': {
46
+ type: 'Before',
47
+ },
48
+ 'Events.Logout.After': {
49
+ type: 'After',
50
+ data: 'bool',
51
+ },
52
+ 'Events.Status.Before': {
53
+ type: 'Before',
54
+ },
55
+ 'Events.Status.After': {
56
+ type: 'After',
57
+ data: 'bool',
58
+ },
59
+ 'Events.Me.Before': {
60
+ type: 'Before',
61
+ },
62
+ 'Events.Me.After': {
63
+ type: 'After',
64
+ data: 'Mushroom.Extensions.DefaultAuth.User',
65
+ },
66
+ 'Events.Register.Before': {
67
+ type: 'Before',
68
+ },
69
+ SendCodeItemFunc: {
70
+ type: 'Func',
71
+ registerFunc: 'RegisterSendCodeItemHandler',
72
+ },
73
+ 'Events.Register.After': {
74
+ type: 'After',
75
+ data: 'Mushroom.Extensions.DefaultAuth.User',
76
+ },
77
+ 'Events.Activate.Before': {
78
+ type: 'Before',
79
+ },
80
+ 'Events.Activate.After': {
81
+ type: 'After',
82
+ data: 'Mushroom.Extensions.DefaultAuth.User',
83
+ },
84
+ 'Events.RecoverPassword.Before': {
85
+ type: 'Before',
86
+ },
87
+ 'Events.RecoverPassword.After': {
88
+ type: 'After',
89
+ data: 'Mushroom.Extensions.DefaultAuth.User',
90
+ },
91
+ 'Events.ResetPassword.Before': {
92
+ type: 'Before',
93
+ },
94
+ 'Events.ResetPassword.After': {
95
+ type: 'After',
96
+ data: 'Mushroom.Extensions.DefaultAuth.User',
97
+ },
98
+ },
99
+ file: {
100
+ 'Events.Upload.Before': {
101
+ type: 'Before',
102
+ },
103
+ 'Events.Upload.After': {
104
+ type: 'After',
105
+ },
106
+ 'Events.Thumb.Before': {
107
+ type: 'Before',
108
+ },
109
+ 'Events.Thumb.After': {
110
+ type: 'After',
111
+ },
112
+ 'Events.Download.Before': {
113
+ type: 'Before',
114
+ },
115
+ 'Events.Download.After': {
116
+ type: 'After',
117
+ },
118
+ 'Events.Zip.Before': {
119
+ type: 'Before',
120
+ },
121
+ 'Events.Zip.After': {
122
+ type: 'After',
123
+ },
124
+ LimitFileUploadSizeFunc: {
125
+ type: 'Func',
126
+ data: 'System.Threading.Tasks.Task<long?>',
127
+ },
128
+ },
129
+ mail: {
130
+ AuthorizationFunc: {
131
+ type: 'Func',
132
+ data: 'System.Threading.Tasks.Task<bool>, Mushroom.Extensions.Mailing.MailingAuthorizationArgument',
133
+ },
134
+ },
135
+ };
136
+ export const getExtensionEventsCode = (source, newLine) => {
137
+ const eventCodes = [];
138
+ const addEventCodes = (key, code, source) => {
139
+ const eventCode = eventCodes.find((ec) => ec.key === key);
140
+ if (eventCode) {
141
+ eventCode.source.push(source);
142
+ return;
143
+ }
144
+ eventCodes.push({
145
+ key,
146
+ code,
147
+ source: [source],
148
+ });
149
+ };
150
+ Object.keys(extEvents).forEach((ext) => {
151
+ const extension = source.extensions?.[ext];
152
+ if (!extension?.config)
153
+ return;
154
+ const config = extension.config;
155
+ Object.keys(extEvents[ext]).forEach((configName) => {
156
+ const def = extEvents[ext][configName];
157
+ const configValue = config[configName];
158
+ if (!configValue || typeof configValue !== 'string' || !configValue.startsWith('lib:'))
159
+ return;
160
+ if (def.type == 'Func') {
161
+ const defaultFunc = `RegisterCustomMethod<${def.data || 'System.Threading.Tasks.Task'}>`;
162
+ addEventCodes(ext + '.' + configName, `${def.registerFunc || defaultFunc}(${configValue.substring(4)});`, `extension: ${ext} - config: ${configName}`);
163
+ return;
164
+ }
165
+ if (def.type == 'Event') {
166
+ configValue.split(',').forEach((lib) => {
167
+ if (!lib || !lib.startsWith('lib:'))
168
+ return;
169
+ addEventCodes(ext + '.' + configName, `${def.registerFunc || 'RegisterEvent'}(${lib.substring(4)});`, `extension: ${ext} - config: ${configName}`);
170
+ });
171
+ return;
172
+ }
173
+ if (def.type == 'Before') {
174
+ configValue.split(',').forEach((lib) => {
175
+ if (!lib || !lib.startsWith('lib:'))
176
+ return;
177
+ addEventCodes(ext + '.' + configName, `${def.registerFunc || 'RegisterBeforeEvent'}(${lib.substring(4)});`, `extension: ${ext} - config: ${configName}`);
178
+ });
179
+ return;
180
+ }
181
+ if (def.type == 'After') {
182
+ configValue.split(',').forEach((lib) => {
183
+ if (!lib || !lib.startsWith('lib:'))
184
+ return;
185
+ addEventCodes(ext + '.' + configName, `${def.registerFunc || (def.data ? `RegisterAfterEvent<${def.data}>` : 'RegisterAfterEvent')}(${lib.substring(4)});`, `extension: ${ext} - config: ${configName}`);
186
+ });
187
+ return;
188
+ }
189
+ return '';
190
+ });
191
+ });
192
+ return eventCodes
193
+ .map((ec) => `${ec.source.map((s) => ' // ' + s).join(newLine)}${newLine} ${ec.code}`)
194
+ .join(newLine);
195
+ };
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import open from 'open';
3
- import { minify } from 'minify';
3
+ import { minify } from 'html-minifier-terser';
4
4
  import { fileURLToPath } from 'url';
5
5
  import path from 'path';
6
6
  import * as common from '../common.js';
@@ -227,42 +227,58 @@ function filterEntityFields(entity) {
227
227
  async function writeDocs(filePath, strTemplate, strData) {
228
228
  const placeholder = '<script id="INJECT-JAVASCRIPT-HERE"></script>';
229
229
  const idx = strTemplate.indexOf(placeholder);
230
- fs.writeFileSync(filePath, strTemplate.substring(0, idx));
231
- fs.appendFileSync(filePath, '<script>window.docs = ');
232
- fs.appendFileSync(filePath, strData);
233
- fs.appendFileSync(filePath, ';\n');
234
- fs.appendFileSync(filePath, fs.readFileSync(path.join(__dirname, '../docs/docs.v2.js')));
235
- fs.appendFileSync(filePath, '</script>');
236
- fs.appendFileSync(filePath, strTemplate.substring(idx + placeholder.length));
237
- fs.writeFileSync(filePath, await minify(filePath, {
238
- html: {
239
- removeComments: true,
240
- collapseWhitespace: true,
241
- collapseBooleanAttributes: true,
242
- removeAttributeQuotes: true,
243
- removeRedundantAttributes: true,
244
- useShortDoctype: true,
245
- removeEmptyAttributes: true,
246
- removeEmptyElements: false,
247
- removeOptionalTags: true,
248
- removeScriptTypeAttributes: false,
249
- removeStyleLinkTypeAttributes: true,
250
- minifyJS: true,
251
- minifyCSS: true,
252
- html5: true,
253
- },
254
- }));
255
- copyStaticFiles(path.dirname(filePath), [
256
- 'bootstrap-theme.min.css',
257
- 'bootstrap.min.css',
258
- 'bootstrap.min.js',
259
- 'jquery.min.js',
260
- ]);
230
+ if (idx === -1) {
231
+ throw new Error(`Docs template does not contain the placeholder ${placeholder}`);
232
+ }
233
+ const docsScript = fs.readFileSync(path.join(__dirname, '../docs/docs.v2.js')).toString();
234
+ let html = strTemplate.substring(0, idx) +
235
+ '<script>window.docs = ' +
236
+ strData +
237
+ ';\n' +
238
+ docsScript +
239
+ '</script>' +
240
+ strTemplate.substring(idx + placeholder.length);
241
+ html = await minify(html, {
242
+ removeComments: true,
243
+ collapseWhitespace: true,
244
+ collapseBooleanAttributes: true,
245
+ removeAttributeQuotes: true,
246
+ removeRedundantAttributes: true,
247
+ useShortDoctype: true,
248
+ removeEmptyAttributes: true,
249
+ removeEmptyElements: false,
250
+ removeOptionalTags: true,
251
+ removeScriptTypeAttributes: false,
252
+ removeStyleLinkTypeAttributes: true,
253
+ minifyJS: true,
254
+ minifyCSS: true,
255
+ html5: true,
256
+ });
257
+ html = injectStaticFiles(html, ['bootstrap-theme.min.css', 'bootstrap.min.css', 'bootstrap.min.js', 'jquery.min.js']);
258
+ fs.writeFileSync(filePath, html);
261
259
  }
262
- const copyStaticFiles = (folderPath, files) => {
260
+ const injectStaticFiles = (content, files) => {
261
+ let html = content;
263
262
  files.forEach((file) => {
264
- fs.copyFileSync(path.join(__dirname, '../docs/', file), path.join(folderPath, file));
263
+ const sourcePath = path.join(__dirname, '../docs/', file);
264
+ if (!fs.existsSync(sourcePath)) {
265
+ common.logWarning(`Static file ${file} does not exist, skipping...`);
266
+ return;
267
+ }
268
+ const escapedFile = file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
269
+ const isCss = path.extname(file).toLowerCase() === '.css';
270
+ const tagPattern = isCss
271
+ ? new RegExp(`<link[^>]*href=["']?${escapedFile}["']?[^>]*>`, 'gi')
272
+ : new RegExp(`<script[^>]*src=["']?${escapedFile}["']?[^>]*>\\s*</script>`, 'gi');
273
+ if (!tagPattern.test(html)) {
274
+ return;
275
+ }
276
+ tagPattern.lastIndex = 0;
277
+ const assetContent = fs.readFileSync(sourcePath).toString();
278
+ const inlined = isCss ? `<style>${assetContent}</style>` : `<script>${assetContent}</script>`;
279
+ html = html.replace(tagPattern, () => inlined);
265
280
  });
281
+ return html;
266
282
  };
267
283
  export default async function genDocs(args, projectName, project, alias, source) {
268
284
  const aliasProject = ((project.docs || {}).alias || {})[alias];
@@ -300,6 +316,7 @@ export default async function genDocs(args, projectName, project, alias, source)
300
316
  environmentVariables: source.environmentVariables,
301
317
  entities: entities,
302
318
  });
319
+ common.ensureFolder(aliasProject.output);
303
320
  const html = fs.readFileSync(__dirname + '/../docs/docs.v2.html').toString();
304
321
  if (args[3] === 'tmp') {
305
322
  const htmlFilePath = aliasProject.output + '_tmp.html';
@@ -320,7 +337,6 @@ export default async function genDocs(args, projectName, project, alias, source)
320
337
  }
321
338
  return;
322
339
  }
323
- common.ensureFolder(aliasProject.output);
324
340
  common.logInfo('Writing to', aliasProject.output);
325
341
  await writeDocs(aliasProject.output, html, data);
326
342
  common.logInfo(`Generated API docs for ${projectName}@${alias} to ${aliasProject.output}`);