@nocobase/cli-v1 2.2.0-alpha.1 → 2.2.0-alpha.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.
- package/package.json +5 -5
- package/src/__tests__/app-dev-utils.test.js +1 -0
- package/src/__tests__/build-index-html.test.js +34 -0
- package/src/__tests__/dev-command.test.js +61 -0
- package/src/__tests__/util.test.js +33 -0
- package/src/commands/app-dev-utils.js +1 -1
- package/src/commands/dev.js +54 -11
- package/src/commands/update-deps.js +8 -0
- package/src/util.js +71 -5
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli-v1",
|
|
3
|
-
"version": "2.2.0-alpha.
|
|
3
|
+
"version": "2.2.0-alpha.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
+
"nocobase": "./bin/index.js",
|
|
8
9
|
"nocobase-v1": "./bin/index.js"
|
|
9
10
|
},
|
|
10
11
|
"dependencies": {
|
|
11
|
-
"@nocobase/cli": "2.2.0-alpha.
|
|
12
|
+
"@nocobase/cli": "2.2.0-alpha.2",
|
|
12
13
|
"@nocobase/license-kit": "^0.3.8",
|
|
13
|
-
"@nocobase/utils": "2.2.0-alpha.
|
|
14
|
+
"@nocobase/utils": "2.2.0-alpha.2",
|
|
14
15
|
"chalk": "^4.1.1",
|
|
15
16
|
"commander": "^9.2.0",
|
|
16
17
|
"deepmerge": "^4.3.1",
|
|
@@ -25,7 +26,6 @@
|
|
|
25
26
|
"tree-kill": "^1.2.2"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
|
-
"@nocobase/devtools": "2.2.0-alpha.1",
|
|
29
29
|
"@types/fs-extra": "^11.0.1"
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
34
34
|
"directory": "packages/core/cli"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "9dd5224bb8f6c1ac12d766b9f175bdc29bddd845"
|
|
37
37
|
}
|
|
@@ -24,6 +24,8 @@ function createAppPackageRoot() {
|
|
|
24
24
|
[
|
|
25
25
|
"window['__nocobase_app_dev__'] = {{env.NOCOBASE_APP_DEV}};",
|
|
26
26
|
"window['__nocobase_public_path__'] = '{{env.APP_PUBLIC_PATH}}';",
|
|
27
|
+
"window['__nocobase_modern_client_prefix__'] = '{{env.APP_MODERN_CLIENT_PREFIX}}';",
|
|
28
|
+
"window['__nocobase_app_client_entry_mode__'] = '{{env.APP_CLIENT_ENTRY_MODE}}';",
|
|
27
29
|
].join('\n'),
|
|
28
30
|
'utf-8',
|
|
29
31
|
);
|
|
@@ -73,11 +75,43 @@ describe('cli-v1 buildIndexHtml', () => {
|
|
|
73
75
|
process.env.APP_PACKAGE_ROOT = appRoot;
|
|
74
76
|
process.env.APP_PUBLIC_PATH = '/';
|
|
75
77
|
process.env.NOCOBASE_APP_DEV = '';
|
|
78
|
+
process.env.APP_MODERN_CLIENT_PREFIX = 'console';
|
|
79
|
+
process.env.APP_CLIENT_ENTRY_MODE = 'modern-default';
|
|
76
80
|
|
|
77
81
|
buildIndexHtml();
|
|
78
82
|
|
|
79
83
|
const html = fs.readFileSync(path.join(appRoot, 'dist/client/index.html'), 'utf-8');
|
|
80
84
|
expect(html).toContain("window['__nocobase_app_dev__'] = false;");
|
|
85
|
+
expect(html).toContain("window['__nocobase_modern_client_prefix__'] = 'console';");
|
|
86
|
+
expect(html).toContain("window['__nocobase_app_client_entry_mode__'] = 'modern-default';");
|
|
87
|
+
fs.removeSync(appRoot);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('refreshes cached tpl when new runtime placeholders are missing', () => {
|
|
91
|
+
const appRoot = createAppPackageRoot();
|
|
92
|
+
const tplPath = path.join(appRoot, 'dist/client/index.html.tpl');
|
|
93
|
+
const indexPath = path.join(appRoot, 'dist/client/index.html');
|
|
94
|
+
fs.writeFileSync(tplPath, "window['__nocobase_public_path__'] = '{{env.APP_PUBLIC_PATH}}';", 'utf-8');
|
|
95
|
+
fs.writeFileSync(
|
|
96
|
+
indexPath,
|
|
97
|
+
[
|
|
98
|
+
"window['__nocobase_public_path__'] = '{{env.APP_PUBLIC_PATH}}';",
|
|
99
|
+
"window['__nocobase_modern_client_prefix__'] = '{{env.APP_MODERN_CLIENT_PREFIX}}';",
|
|
100
|
+
"window['__nocobase_app_client_entry_mode__'] = '{{env.APP_CLIENT_ENTRY_MODE}}';",
|
|
101
|
+
].join('\n'),
|
|
102
|
+
'utf-8',
|
|
103
|
+
);
|
|
104
|
+
process.argv = ['node', 'nocobase-v1', 'start'];
|
|
105
|
+
process.env.APP_PACKAGE_ROOT = appRoot;
|
|
106
|
+
process.env.APP_PUBLIC_PATH = '/';
|
|
107
|
+
process.env.APP_MODERN_CLIENT_PREFIX = 'v';
|
|
108
|
+
process.env.APP_CLIENT_ENTRY_MODE = 'modern-only';
|
|
109
|
+
|
|
110
|
+
buildIndexHtml();
|
|
111
|
+
|
|
112
|
+
const tpl = fs.readFileSync(tplPath, 'utf-8');
|
|
113
|
+
expect(tpl).toContain('__nocobase_modern_client_prefix__');
|
|
114
|
+
expect(tpl).toContain('__nocobase_app_client_entry_mode__');
|
|
81
115
|
fs.removeSync(appRoot);
|
|
82
116
|
});
|
|
83
117
|
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* eslint-env jest */
|
|
11
|
+
|
|
12
|
+
const { buildAppDevForwardArgs, forwardDevToAppDev, resolveDevRuntimeMode } = require('../commands/dev')._test;
|
|
13
|
+
|
|
14
|
+
describe('cli-v1 dev command', () => {
|
|
15
|
+
test('buildAppDevForwardArgs rewrites dev argv to app-dev while preserving extra args', () => {
|
|
16
|
+
expect(
|
|
17
|
+
buildAppDevForwardArgs(['node', 'nocobase-v1', 'dev', '--rsbuild', '--port', '13000', '--inspect=9229']),
|
|
18
|
+
).toEqual(['app-dev', '--rsbuild', '--port', '13000', '--inspect=9229']);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('forwardDevToAppDev delegates to nocobase-v1 app-dev for create-app projects', async () => {
|
|
22
|
+
const calls = [];
|
|
23
|
+
const runCommand = async (...args) => {
|
|
24
|
+
calls.push(args);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
await forwardDevToAppDev({
|
|
28
|
+
argv: ['node', 'nocobase-v1', 'dev', '--port', '13000', '--db-sync'],
|
|
29
|
+
runCommand,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(calls).toEqual([['nocobase-v1', ['app-dev', '--port', '13000', '--db-sync']]]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('resolveDevRuntimeMode keeps both clients in legacy-default', () => {
|
|
36
|
+
expect(resolveDevRuntimeMode({ appClientEntryMode: 'legacy-default' })).toMatchObject({
|
|
37
|
+
useModernOnlyEntryMode: false,
|
|
38
|
+
shouldRunClient: true,
|
|
39
|
+
shouldRunClientV2: true,
|
|
40
|
+
shouldRunServer: true,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('resolveDevRuntimeMode only runs v2 client and server in modern-only', () => {
|
|
45
|
+
expect(resolveDevRuntimeMode({ appClientEntryMode: 'modern-only' })).toMatchObject({
|
|
46
|
+
useModernOnlyEntryMode: true,
|
|
47
|
+
shouldRunClient: false,
|
|
48
|
+
shouldRunClientV2: true,
|
|
49
|
+
shouldRunServer: true,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('resolveDevRuntimeMode preserves explicit client-v2-only flag behavior', () => {
|
|
54
|
+
expect(resolveDevRuntimeMode({ clientV2Only: true, appClientEntryMode: 'legacy-default' })).toMatchObject({
|
|
55
|
+
useModernOnlyEntryMode: false,
|
|
56
|
+
shouldRunClient: false,
|
|
57
|
+
shouldRunClientV2: true,
|
|
58
|
+
shouldRunServer: false,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* eslint-env jest */
|
|
11
|
+
|
|
12
|
+
const { colorizedDevLogEnv, createRunWithPrefixLabel } = require('../util')._test;
|
|
13
|
+
|
|
14
|
+
describe('cli-v1 util helpers', () => {
|
|
15
|
+
test('colorizedDevLogEnv enables color for dev child output by default', () => {
|
|
16
|
+
expect(colorizedDevLogEnv({})).toEqual({ FORCE_COLOR: '1' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('colorizedDevLogEnv keeps explicit FORCE_COLOR', () => {
|
|
20
|
+
expect(colorizedDevLogEnv({ FORCE_COLOR: '3' })).toEqual({ FORCE_COLOR: '3' });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('colorizedDevLogEnv removes inherited NO_COLOR so dev logs stay colored', () => {
|
|
24
|
+
expect(colorizedDevLogEnv({ NO_COLOR: '1', TERM: 'dumb' })).toEqual({
|
|
25
|
+
FORCE_COLOR: '1',
|
|
26
|
+
TERM: 'dumb',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('createRunWithPrefixLabel colorizes prefixed output even when the parent disables colors', () => {
|
|
31
|
+
expect(createRunWithPrefixLabel('client', 'cyan')).toBe('\u001b[36m[client]\u001b[39m');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -150,7 +150,7 @@ function buildAppDevServerArgs({
|
|
|
150
150
|
argv = process.argv,
|
|
151
151
|
serverTsconfigPath = process.env.SERVER_TSCONFIG_PATH,
|
|
152
152
|
} = {}) {
|
|
153
|
-
const args = ['watch', `--ignore=${resolvePluginStoragePath()}/**`];
|
|
153
|
+
const args = ['watch', '--clear-screen=false', `--ignore=${resolvePluginStoragePath()}/**`];
|
|
154
154
|
|
|
155
155
|
if (serverTsconfigPath) {
|
|
156
156
|
args.push('--tsconfig', serverTsconfigPath);
|
package/src/commands/dev.js
CHANGED
|
@@ -10,14 +10,17 @@ const _ = require('lodash');
|
|
|
10
10
|
const { Command } = require('commander');
|
|
11
11
|
const {
|
|
12
12
|
generatePlugins,
|
|
13
|
+
hasCorePackages,
|
|
13
14
|
run,
|
|
14
15
|
runWithPrefix,
|
|
16
|
+
colorizedDevLogEnv,
|
|
15
17
|
postCheck,
|
|
16
18
|
nodeCheck,
|
|
17
19
|
promptForTs,
|
|
18
20
|
isPortReachable,
|
|
19
21
|
buildWSURL,
|
|
20
22
|
checkDBDialect,
|
|
23
|
+
resolveAppClientEntryMode,
|
|
21
24
|
} = require('../util');
|
|
22
25
|
const { getPortPromise } = require('portfinder');
|
|
23
26
|
const chokidar = require('chokidar');
|
|
@@ -41,6 +44,33 @@ async function buildBundleStatusHtml() {
|
|
|
41
44
|
);
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
function buildAppDevForwardArgs(argv = process.argv) {
|
|
48
|
+
return ['app-dev', ...argv.slice(3)];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveDevRuntimeMode(opts = {}) {
|
|
52
|
+
const appClientEntryMode = opts.appClientEntryMode || resolveAppClientEntryMode();
|
|
53
|
+
const useModernOnlyEntryMode = appClientEntryMode === 'modern-only';
|
|
54
|
+
const clientV2Only = !!opts.clientV2Only;
|
|
55
|
+
const forceClient = !!opts.client;
|
|
56
|
+
const forceServer = !!opts.server;
|
|
57
|
+
const shouldRunClientV2 = clientV2Only || useModernOnlyEntryMode || forceClient || !forceServer;
|
|
58
|
+
const shouldRunClient = !clientV2Only && !useModernOnlyEntryMode && (forceClient || !forceServer);
|
|
59
|
+
const shouldRunServer = !clientV2Only && (forceServer || !forceClient || useModernOnlyEntryMode);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
appClientEntryMode,
|
|
63
|
+
useModernOnlyEntryMode,
|
|
64
|
+
shouldRunClientV2,
|
|
65
|
+
shouldRunClient,
|
|
66
|
+
shouldRunServer,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function forwardDevToAppDev({ argv = process.argv, runCommand = run } = {}) {
|
|
71
|
+
await runCommand('nocobase-v1', buildAppDevForwardArgs(argv));
|
|
72
|
+
}
|
|
73
|
+
|
|
44
74
|
/**
|
|
45
75
|
*
|
|
46
76
|
* @param {Command} cli
|
|
@@ -58,6 +88,11 @@ module.exports = (cli) => {
|
|
|
58
88
|
.option('-i, --inspect [port]')
|
|
59
89
|
.allowUnknownOption()
|
|
60
90
|
.action(async (opts) => {
|
|
91
|
+
if (!hasCorePackages()) {
|
|
92
|
+
await forwardDevToAppDev();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
61
96
|
checkDBDialect();
|
|
62
97
|
await buildBundleStatusHtml();
|
|
63
98
|
|
|
@@ -92,9 +127,9 @@ module.exports = (cli) => {
|
|
|
92
127
|
nodeCheck();
|
|
93
128
|
await postCheck(opts);
|
|
94
129
|
|
|
95
|
-
const shouldRunClientV2
|
|
96
|
-
|
|
97
|
-
|
|
130
|
+
const { useModernOnlyEntryMode, shouldRunClientV2, shouldRunClient, shouldRunServer } = resolveDevRuntimeMode(
|
|
131
|
+
opts,
|
|
132
|
+
);
|
|
98
133
|
const shouldRunClientWithRsbuild = shouldRunClient && !!rsbuild;
|
|
99
134
|
|
|
100
135
|
if (shouldRunServer && server) {
|
|
@@ -105,12 +140,16 @@ module.exports = (cli) => {
|
|
|
105
140
|
});
|
|
106
141
|
}
|
|
107
142
|
|
|
108
|
-
if (shouldRunClientV2 && !clientV2Only) {
|
|
143
|
+
if (shouldRunClientV2 && !clientV2Only && !useModernOnlyEntryMode) {
|
|
109
144
|
clientV2Port = await getPortPromise({
|
|
110
145
|
port: 1 * clientPort + 2,
|
|
111
146
|
});
|
|
112
147
|
}
|
|
113
148
|
|
|
149
|
+
if (useModernOnlyEntryMode) {
|
|
150
|
+
clientV2Port = APP_PORT;
|
|
151
|
+
}
|
|
152
|
+
|
|
114
153
|
let subprocessClient;
|
|
115
154
|
let subprocessClientV2;
|
|
116
155
|
|
|
@@ -184,16 +223,13 @@ module.exports = (cli) => {
|
|
|
184
223
|
}
|
|
185
224
|
subprocessRef.cancel();
|
|
186
225
|
let i = 0;
|
|
187
|
-
while (
|
|
226
|
+
while (i <= 10) {
|
|
188
227
|
++i;
|
|
189
228
|
const result = await isPortReachable(port);
|
|
190
229
|
if (!result) {
|
|
191
230
|
break;
|
|
192
231
|
}
|
|
193
232
|
await sleep(500);
|
|
194
|
-
if (i > 10) {
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
233
|
}
|
|
198
234
|
start();
|
|
199
235
|
};
|
|
@@ -202,7 +238,7 @@ module.exports = (cli) => {
|
|
|
202
238
|
const storagePluginPath = resolvePluginStoragePath();
|
|
203
239
|
const watcher = chokidar.watch(`${storagePluginPath}/**/*`, {
|
|
204
240
|
cwd: process.cwd(),
|
|
205
|
-
ignored: /(^|[
|
|
241
|
+
ignored: /(^|[/\\])\../, // 忽略隐藏文件
|
|
206
242
|
persistent: true,
|
|
207
243
|
depth: 1, // 只监听第一层目录
|
|
208
244
|
});
|
|
@@ -246,6 +282,7 @@ module.exports = (cli) => {
|
|
|
246
282
|
|
|
247
283
|
const argv = [
|
|
248
284
|
'watch',
|
|
285
|
+
'--clear-screen=false',
|
|
249
286
|
...(inspect ? [`--inspect=${inspect === true ? 9229 : inspect}`] : []),
|
|
250
287
|
`--ignore=${resolvePluginStoragePath()}/**`,
|
|
251
288
|
'--tsconfig',
|
|
@@ -264,9 +301,9 @@ module.exports = (cli) => {
|
|
|
264
301
|
|
|
265
302
|
const runDevServer = () => {
|
|
266
303
|
run('tsx', argv, {
|
|
267
|
-
env: {
|
|
304
|
+
env: colorizedDevLogEnv(process.env, {
|
|
268
305
|
APP_PORT: serverPort,
|
|
269
|
-
},
|
|
306
|
+
}),
|
|
270
307
|
}).catch((err) => {
|
|
271
308
|
if (err.exitCode == 100) {
|
|
272
309
|
console.log('Restarting server...');
|
|
@@ -289,3 +326,9 @@ module.exports = (cli) => {
|
|
|
289
326
|
}
|
|
290
327
|
});
|
|
291
328
|
};
|
|
329
|
+
|
|
330
|
+
module.exports._test = {
|
|
331
|
+
buildAppDevForwardArgs,
|
|
332
|
+
forwardDevToAppDev,
|
|
333
|
+
resolveDevRuntimeMode,
|
|
334
|
+
};
|
|
@@ -68,6 +68,14 @@ module.exports = (cli) => {
|
|
|
68
68
|
if (descJson['dependencies']?.['@nocobase/app']) {
|
|
69
69
|
descJson['dependencies']['@nocobase/app'] = stdout;
|
|
70
70
|
}
|
|
71
|
+
descJson['scripts']['dev'] = 'nocobase app-dev';
|
|
72
|
+
if (descJson['dependencies']?.['@nocobase/cli']) {
|
|
73
|
+
descJson['dependencies']['@nocobase/app'] = stdout;
|
|
74
|
+
console.log(
|
|
75
|
+
chalk.yellow('The @nocobase/cli package is no longer needed, it will be removed from dependencies.'),
|
|
76
|
+
);
|
|
77
|
+
delete descJson['dependencies']['@nocobase/cli'];
|
|
78
|
+
}
|
|
71
79
|
if (descJson['devDependencies']?.['@nocobase/devtools']) {
|
|
72
80
|
descJson['devDependencies']['@nocobase/devtools'] = stdout;
|
|
73
81
|
}
|
package/src/util.js
CHANGED
|
@@ -84,6 +84,24 @@ exports.run = (command, args, options = {}) => {
|
|
|
84
84
|
});
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
+
function colorizedDevLogEnv(env = process.env, overrides = {}) {
|
|
88
|
+
const nextEnv = {
|
|
89
|
+
...env,
|
|
90
|
+
...overrides,
|
|
91
|
+
};
|
|
92
|
+
delete nextEnv.NO_COLOR;
|
|
93
|
+
if (!nextEnv.FORCE_COLOR) {
|
|
94
|
+
nextEnv.FORCE_COLOR = '1';
|
|
95
|
+
}
|
|
96
|
+
return nextEnv;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createRunWithPrefixLabel(prefix, color) {
|
|
100
|
+
const forcedChalk = new chalk.Instance({ level: 1 });
|
|
101
|
+
const colorize = forcedChalk[color] || forcedChalk.cyan;
|
|
102
|
+
return colorize(`[${prefix}]`);
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
exports.runWithPrefix = (command, args, options = {}) => {
|
|
88
106
|
if (command === 'tsx') {
|
|
89
107
|
command = 'node';
|
|
@@ -92,15 +110,12 @@ exports.runWithPrefix = (command, args, options = {}) => {
|
|
|
92
110
|
|
|
93
111
|
const prefix = options.prefix || 'process';
|
|
94
112
|
const color = options.color || 'cyan';
|
|
95
|
-
const label =
|
|
113
|
+
const label = createRunWithPrefixLabel(prefix, color);
|
|
96
114
|
const subprocess = execa(command, args, {
|
|
97
115
|
shell: true,
|
|
98
116
|
stdio: 'pipe',
|
|
99
117
|
...options,
|
|
100
|
-
env:
|
|
101
|
-
...process.env,
|
|
102
|
-
...options.env,
|
|
103
|
-
},
|
|
118
|
+
env: colorizedDevLogEnv(process.env, options.env),
|
|
104
119
|
});
|
|
105
120
|
|
|
106
121
|
const writePrefixed = (chunk, writer) => {
|
|
@@ -122,6 +137,13 @@ exports.runWithPrefix = (command, args, options = {}) => {
|
|
|
122
137
|
return subprocess;
|
|
123
138
|
};
|
|
124
139
|
|
|
140
|
+
exports._test = {
|
|
141
|
+
createRunWithPrefixLabel,
|
|
142
|
+
colorizedDevLogEnv,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
exports.colorizedDevLogEnv = colorizedDevLogEnv;
|
|
146
|
+
|
|
125
147
|
exports.isPortReachable = async (port, { timeout = 1000, host } = {}) => {
|
|
126
148
|
const promise = new Promise((resolve, reject) => {
|
|
127
149
|
const socket = new net.Socket();
|
|
@@ -410,6 +432,30 @@ const DEFAULT_MODERN_CLIENT_PREFIX = 'v';
|
|
|
410
432
|
|
|
411
433
|
exports.DEFAULT_MODERN_CLIENT_PREFIX = DEFAULT_MODERN_CLIENT_PREFIX;
|
|
412
434
|
|
|
435
|
+
const DEFAULT_APP_CLIENT_ENTRY_MODE = 'legacy-default';
|
|
436
|
+
const APP_CLIENT_ENTRY_MODES = new Set(['legacy-default', 'modern-default', 'modern-only']);
|
|
437
|
+
|
|
438
|
+
exports.DEFAULT_APP_CLIENT_ENTRY_MODE = DEFAULT_APP_CLIENT_ENTRY_MODE;
|
|
439
|
+
|
|
440
|
+
function isAppClientEntryMode(value) {
|
|
441
|
+
return APP_CLIENT_ENTRY_MODES.has(String(value || '').trim());
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
exports.isAppClientEntryMode = isAppClientEntryMode;
|
|
445
|
+
|
|
446
|
+
function normalizeAppClientEntryMode(value) {
|
|
447
|
+
const normalized = String(value || '').trim();
|
|
448
|
+
return isAppClientEntryMode(normalized) ? normalized : DEFAULT_APP_CLIENT_ENTRY_MODE;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
exports.normalizeAppClientEntryMode = normalizeAppClientEntryMode;
|
|
452
|
+
|
|
453
|
+
function resolveAppClientEntryMode() {
|
|
454
|
+
return normalizeAppClientEntryMode(process.env.APP_CLIENT_ENTRY_MODE);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
exports.resolveAppClientEntryMode = resolveAppClientEntryMode;
|
|
458
|
+
|
|
413
459
|
// Normalize APP_MODERN_CLIENT_PREFIX (accepts `v`, `/v`, `/v/`)
|
|
414
460
|
// down to a bare segment like `v`.
|
|
415
461
|
function normalizeModernClientPrefix(value) {
|
|
@@ -433,6 +479,13 @@ function isAppDevHtml() {
|
|
|
433
479
|
return process.argv[2] === 'app-dev' || process.env.NOCOBASE_APP_DEV === 'true';
|
|
434
480
|
}
|
|
435
481
|
|
|
482
|
+
function shouldRefreshLegacyIndexTemplate(data = '') {
|
|
483
|
+
return (
|
|
484
|
+
!data.includes("window['__nocobase_modern_client_prefix__']") ||
|
|
485
|
+
!data.includes("window['__nocobase_app_client_entry_mode__']")
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
436
489
|
function buildIndexHtml(force = false) {
|
|
437
490
|
const file = `${process.env.APP_PACKAGE_ROOT}/dist/client/index.html`;
|
|
438
491
|
if (!fs.existsSync(file)) {
|
|
@@ -444,11 +497,15 @@ function buildIndexHtml(force = false) {
|
|
|
444
497
|
}
|
|
445
498
|
if (!fs.existsSync(tpl)) {
|
|
446
499
|
fs.copyFileSync(file, tpl);
|
|
500
|
+
} else if (shouldRefreshLegacyIndexTemplate(fs.readFileSync(tpl, 'utf-8'))) {
|
|
501
|
+
fs.copyFileSync(file, tpl);
|
|
447
502
|
}
|
|
448
503
|
const data = fs.readFileSync(tpl, 'utf-8');
|
|
449
504
|
let replacedData = data
|
|
450
505
|
.replace(/\{\{env.CDN_BASE_URL\}\}/g, process.env.CDN_BASE_URL)
|
|
451
506
|
.replace(/\{\{env.APP_PUBLIC_PATH\}\}/g, process.env.APP_PUBLIC_PATH)
|
|
507
|
+
.replace(/\{\{env.APP_MODERN_CLIENT_PREFIX\}\}/g, normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX))
|
|
508
|
+
.replace(/\{\{env.APP_CLIENT_ENTRY_MODE\}\}/g, resolveAppClientEntryMode())
|
|
452
509
|
.replace(/\{\{env.API_CLIENT_SHARE_TOKEN\}\}/g, process.env.API_CLIENT_SHARE_TOKEN || 'false')
|
|
453
510
|
.replace(/\{\{env.API_CLIENT_STORAGE_TYPE\}\}/g, process.env.API_CLIENT_STORAGE_TYPE)
|
|
454
511
|
.replace(/\{\{env.API_CLIENT_STORAGE_PREFIX\}\}/g, process.env.API_CLIENT_STORAGE_PREFIX)
|
|
@@ -589,6 +646,7 @@ exports.initEnv = function initEnv() {
|
|
|
589
646
|
CDN_BASE_URL: '',
|
|
590
647
|
APP_PUBLIC_PATH: '/',
|
|
591
648
|
APP_MODERN_CLIENT_PREFIX: DEFAULT_MODERN_CLIENT_PREFIX,
|
|
649
|
+
APP_CLIENT_ENTRY_MODE: DEFAULT_APP_CLIENT_ENTRY_MODE,
|
|
592
650
|
ESM_CDN_BASE_URL: 'https://esm.sh',
|
|
593
651
|
ESM_CDN_SUFFIX: '',
|
|
594
652
|
};
|
|
@@ -640,6 +698,14 @@ exports.initEnv = function initEnv() {
|
|
|
640
698
|
}
|
|
641
699
|
}
|
|
642
700
|
|
|
701
|
+
const rawAppClientEntryMode = String(process.env.APP_CLIENT_ENTRY_MODE || '').trim();
|
|
702
|
+
if (rawAppClientEntryMode && !isAppClientEntryMode(rawAppClientEntryMode)) {
|
|
703
|
+
console.warn(
|
|
704
|
+
`Unknown APP_CLIENT_ENTRY_MODE "${rawAppClientEntryMode}", falling back to ${DEFAULT_APP_CLIENT_ENTRY_MODE}.`,
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
process.env.APP_CLIENT_ENTRY_MODE = normalizeAppClientEntryMode(rawAppClientEntryMode);
|
|
708
|
+
|
|
643
709
|
if (!process.env.__env_modified__ && process.env.APP_PUBLIC_PATH) {
|
|
644
710
|
const publicPath = process.env.APP_PUBLIC_PATH.replace(/\/$/g, '');
|
|
645
711
|
const keys = ['API_BASE_PATH', 'WS_PATH', 'PLUGIN_STATICS_PATH'];
|