@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 CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@nocobase/cli-v1",
3
- "version": "2.2.0-alpha.1",
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.1",
12
+ "@nocobase/cli": "2.2.0-alpha.2",
12
13
  "@nocobase/license-kit": "^0.3.8",
13
- "@nocobase/utils": "2.2.0-alpha.1",
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": "303663aba6c6eefa27e6a6435b4c0352074ec40f"
36
+ "gitHead": "9dd5224bb8f6c1ac12d766b9f175bdc29bddd845"
37
37
  }
@@ -82,6 +82,7 @@ describe('cli-v1 app-dev utils', () => {
82
82
  }),
83
83
  ).toEqual([
84
84
  'watch',
85
+ '--clear-screen=false',
85
86
  `--ignore=${path.resolve(process.cwd(), 'storage/plugins')}/**`,
86
87
  '--tsconfig',
87
88
  './tsconfig.server.json',
@@ -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);
@@ -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 = clientV2Only || client || !server;
96
- const shouldRunClient = !clientV2Only && (client || !server);
97
- const shouldRunServer = !clientV2Only && (server || !client);
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 (true) {
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 = chalk[color](`[${prefix}]`);
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'];