@nocobase/cli 2.1.0-alpha.1 → 2.1.0-alpha.10

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.
@@ -21,6 +21,15 @@ function sleep(ms = 1000) {
21
21
  });
22
22
  }
23
23
 
24
+ async function buildBundleStatusHtml() {
25
+ const data = await fs.promises.readFile(path.resolve(__dirname, '../../templates/bundle-status.html'), 'utf-8');
26
+ await fs.promises.writeFile(
27
+ path.resolve(process.cwd(), 'node_modules/@umijs/preset-umi/assets/bundle-status.html'),
28
+ data,
29
+ 'utf-8',
30
+ );
31
+ }
32
+
24
33
  /**
25
34
  *
26
35
  * @param {Command} cli
@@ -33,73 +42,12 @@ module.exports = (cli) => {
33
42
  .option('-c, --client')
34
43
  .option('-s, --server')
35
44
  .option('--db-sync')
45
+ .option('--client-v2-only')
36
46
  .option('-i, --inspect [port]')
37
47
  .allowUnknownOption()
38
48
  .action(async (opts) => {
39
49
  checkDBDialect();
40
- let subprocess;
41
- const runDevClient = () => {
42
- console.log('starting client', 1 * clientPort);
43
- subprocess = run('umi', ['dev'], {
44
- env: {
45
- ...process.env,
46
- stdio: 'inherit',
47
- shell: true,
48
- PORT: clientPort,
49
- APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
50
- WEBSOCKET_URL:
51
- process.env.WEBSOCKET_URL ||
52
- (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
53
- PROXY_TARGET_URL:
54
- process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
55
- },
56
- });
57
- };
58
- const watcher = chokidar.watch('./storage/plugins/**/*', {
59
- cwd: process.cwd(),
60
- ignored: /(^|[\/\\])\../, // 忽略隐藏文件
61
- persistent: true,
62
- depth: 1, // 只监听第一层目录
63
- });
64
-
65
- await fs.promises.mkdir(path.dirname(process.env.WATCH_FILE), { recursive: true });
66
- let isReady = false;
67
-
68
- const restartClient = _.debounce(async () => {
69
- if (!isReady) return;
70
- generatePlugins();
71
- if (subprocess) {
72
- console.log('client restarting...');
73
- subprocess.cancel();
74
- let i = 0;
75
- while (true) {
76
- ++i;
77
- const result = await isPortReachable(clientPort);
78
- if (!result) {
79
- break;
80
- }
81
- await sleep(500);
82
- if (i > 10) {
83
- break;
84
- }
85
- }
86
- runDevClient();
87
- await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
88
- }
89
- }, 500);
90
-
91
- watcher
92
- .on('ready', () => {
93
- isReady = true;
94
- })
95
- .on('addDir', async (pathname) => {
96
- if (!isReady) return;
97
- restartClient();
98
- })
99
- .on('unlinkDir', async (pathname) => {
100
- if (!isReady) return;
101
- restartClient();
102
- });
50
+ await buildBundleStatusHtml();
103
51
 
104
52
  promptForTs();
105
53
  const { SERVER_TSCONFIG_PATH } = process.env;
@@ -117,30 +65,136 @@ module.exports = (cli) => {
117
65
  return;
118
66
  }
119
67
 
120
- const { port, client, server, inspect } = opts;
68
+ const { port, client, server, inspect, clientV2Only } = opts;
121
69
 
122
70
  if (port) {
123
71
  process.env.APP_PORT = opts.port;
124
72
  }
125
73
 
126
- const { APP_PORT } = process.env;
74
+ const APP_PORT = Number(process.env.APP_PORT);
127
75
 
128
76
  let clientPort = APP_PORT;
129
77
  let serverPort;
78
+ let clientV2Port = APP_PORT;
130
79
 
131
80
  nodeCheck();
132
-
133
81
  await postCheck(opts);
134
82
 
135
- if (server) {
83
+ const shouldRunClientV2 = clientV2Only || client || !server;
84
+ const shouldRunClient = !clientV2Only && (client || !server);
85
+ const shouldRunServer = !clientV2Only && (server || !client);
86
+
87
+ if (shouldRunServer && server) {
136
88
  serverPort = APP_PORT;
137
- } else if (!server && !client) {
89
+ } else if (shouldRunServer) {
138
90
  serverPort = await getPortPromise({
139
91
  port: 1 * clientPort + 1,
140
92
  });
141
93
  }
142
94
 
143
- if (server || !client) {
95
+ if (shouldRunClientV2 && !clientV2Only) {
96
+ clientV2Port = await getPortPromise({
97
+ port: 1 * clientPort + 2,
98
+ });
99
+ }
100
+
101
+ const runDevClientV2 = () => {
102
+ console.log('starting client-v2', 1 * clientV2Port);
103
+ run('rsbuild', ['dev', '--config', `${APP_PACKAGE_ROOT}/client-v2/rsbuild.config.ts`], {
104
+ env: {
105
+ ...process.env,
106
+ APP_V2_PORT: `${clientV2Port}`,
107
+ NODE_ENV: 'development',
108
+ RSPACK_HMR_CLIENT_PORT: `${clientV2Only ? clientV2Port : clientPort}`,
109
+ API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
110
+ API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
111
+ API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
112
+ API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
113
+ WEBSOCKET_URL:
114
+ process.env.WEBSOCKET_URL || (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : ''),
115
+ WS_PATH: process.env.WS_PATH,
116
+ ESM_CDN_BASE_URL: process.env.ESM_CDN_BASE_URL || 'https://esm.sh',
117
+ ESM_CDN_SUFFIX: process.env.ESM_CDN_SUFFIX || '',
118
+ PROXY_TARGET_URL:
119
+ process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
120
+ },
121
+ });
122
+ };
123
+
124
+ if (clientV2Only) {
125
+ runDevClientV2();
126
+ return;
127
+ }
128
+
129
+ let subprocess;
130
+ const runDevClient = () => {
131
+ console.log('starting client', 1 * clientPort);
132
+ subprocess = run('umi', ['dev'], {
133
+ env: {
134
+ ...process.env,
135
+ stdio: 'inherit',
136
+ shell: true,
137
+ PORT: clientPort,
138
+ APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
139
+ APP_V2_PORT: `${clientV2Port}`,
140
+ WEBSOCKET_URL:
141
+ process.env.WEBSOCKET_URL ||
142
+ (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
143
+ PROXY_TARGET_URL:
144
+ process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
145
+ },
146
+ });
147
+ };
148
+
149
+ if (shouldRunClient) {
150
+ const watcher = chokidar.watch('./storage/plugins/**/*', {
151
+ cwd: process.cwd(),
152
+ ignored: /(^|[\/\\])\../, // 忽略隐藏文件
153
+ persistent: true,
154
+ depth: 1, // 只监听第一层目录
155
+ });
156
+
157
+ await fs.promises.mkdir(path.dirname(process.env.WATCH_FILE), { recursive: true });
158
+ let isReady = false;
159
+
160
+ const restartClient = _.debounce(async () => {
161
+ if (!isReady) return;
162
+ generatePlugins();
163
+ if (subprocess) {
164
+ console.log('client restarting...');
165
+ subprocess.cancel();
166
+ let i = 0;
167
+ while (true) {
168
+ ++i;
169
+ const result = await isPortReachable(clientPort);
170
+ if (!result) {
171
+ break;
172
+ }
173
+ await sleep(500);
174
+ if (i > 10) {
175
+ break;
176
+ }
177
+ }
178
+ runDevClient();
179
+ await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
180
+ }
181
+ }, 500);
182
+
183
+ watcher
184
+ .on('ready', () => {
185
+ isReady = true;
186
+ })
187
+ .on('addDir', async () => {
188
+ if (!isReady) return;
189
+ restartClient();
190
+ })
191
+ .on('unlinkDir', async () => {
192
+ if (!isReady) return;
193
+ restartClient();
194
+ });
195
+ }
196
+
197
+ if (shouldRunServer) {
144
198
  console.log('starting server', serverPort);
145
199
 
146
200
  const filteredArgs = process.argv.filter(
@@ -183,8 +237,12 @@ module.exports = (cli) => {
183
237
  runDevServer();
184
238
  }
185
239
 
186
- if (client || !server) {
240
+ if (shouldRunClient) {
187
241
  runDevClient();
188
242
  }
243
+
244
+ if (shouldRunClientV2) {
245
+ runDevClientV2();
246
+ }
189
247
  });
190
248
  };
@@ -38,6 +38,7 @@ module.exports = (cli) => {
38
38
  require('./pkg')(cli);
39
39
  require('./instance-id')(cli);
40
40
  require('./view-license-key')(cli);
41
+ require('./client')(cli);
41
42
  if (isPackageValid('@umijs/utils')) {
42
43
  require('./create-plugin')(cli);
43
44
  }
@@ -208,7 +208,7 @@ class PackageManager {
208
208
  showLicenseInfo(LicenseKeyError.notValid);
209
209
  }
210
210
  logger.error(`Login failed: ${this.baseURL}`);
211
- logger.error(error?.message || error);
211
+ logger.error(error?.message, { error });
212
212
  }
213
213
  }
214
214
 
@@ -283,7 +283,12 @@ module.exports = (cli) => {
283
283
  NOCOBASE_PKG_URL = 'https://pkg.nocobase.com/',
284
284
  NOCOBASE_PKG_USERNAME,
285
285
  NOCOBASE_PKG_PASSWORD,
286
+ DISABLE_PKG_DOWNLOAD,
286
287
  } = process.env;
288
+ if (DISABLE_PKG_DOWNLOAD === 'true') {
289
+ logger.info('Package download is disabled.');
290
+ return;
291
+ }
287
292
  let accessKeyId;
288
293
  let accessKeySecret;
289
294
  try {
@@ -32,6 +32,6 @@ module.exports = (cli) => {
32
32
  .command('pm2-stop')
33
33
  .allowUnknownOption()
34
34
  .action(() => {
35
- run('pm2', ['stop', 'all']);
35
+ run('pm2', ['kill']);
36
36
  });
37
37
  };
@@ -62,6 +62,7 @@ module.exports = (cli) => {
62
62
  if (descJson['devDependencies']?.['@nocobase/devtools']) {
63
63
  descJson['devDependencies']['@nocobase/devtools'] = stdout;
64
64
  }
65
+ descJson['resolutions'] = sourceJson['resolutions'];
65
66
  const json = deepmerge(descJson, sourceJson);
66
67
  await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' });
67
68
  await run('yarn', ['install']);
package/src/license.js CHANGED
@@ -18,7 +18,7 @@ const { pick } = require('lodash');
18
18
  exports.getAccessKeyPair = async function () {
19
19
  const keyFile = resolve(process.cwd(), 'storage/.license/license-key');
20
20
  if (!fs.existsSync(keyFile)) {
21
- logger.error('License key not found');
21
+ logger.info('License key not found');
22
22
  return {};
23
23
  }
24
24
  logger.info('License key found');
package/src/util.js CHANGED
@@ -282,6 +282,21 @@ function parseEnv(name) {
282
282
  }
283
283
  }
284
284
 
285
+ function resolvePublicPath(appPublicPath = '/') {
286
+ const normalized = String(appPublicPath || '/').trim() || '/';
287
+ const withLeadingSlash = normalized.startsWith('/') ? normalized : `/${normalized}`;
288
+ return withLeadingSlash.endsWith('/') ? withLeadingSlash : `${withLeadingSlash}/`;
289
+ }
290
+
291
+ exports.resolvePublicPath = resolvePublicPath;
292
+
293
+ function resolveV2PublicPath(appPublicPath = '/') {
294
+ const publicPath = resolvePublicPath(appPublicPath);
295
+ return `${publicPath.replace(/\/$/, '')}/v2/`;
296
+ }
297
+
298
+ exports.resolveV2PublicPath = resolveV2PublicPath;
299
+
285
300
  function buildIndexHtml(force = false) {
286
301
  const file = `${process.env.APP_PACKAGE_ROOT}/dist/client/index.html`;
287
302
  if (!fs.existsSync(file)) {
@@ -295,14 +310,26 @@ function buildIndexHtml(force = false) {
295
310
  fs.copyFileSync(file, tpl);
296
311
  }
297
312
  const data = fs.readFileSync(tpl, 'utf-8');
298
- const replacedData = data
313
+ let replacedData = data
314
+ .replace(/\{\{env.CDN_BASE_URL\}\}/g, process.env.CDN_BASE_URL)
299
315
  .replace(/\{\{env.APP_PUBLIC_PATH\}\}/g, process.env.APP_PUBLIC_PATH)
316
+ .replace(/\{\{env.API_CLIENT_SHARE_TOKEN\}\}/g, process.env.API_CLIENT_SHARE_TOKEN || 'false')
300
317
  .replace(/\{\{env.API_CLIENT_STORAGE_TYPE\}\}/g, process.env.API_CLIENT_STORAGE_TYPE)
301
318
  .replace(/\{\{env.API_CLIENT_STORAGE_PREFIX\}\}/g, process.env.API_CLIENT_STORAGE_PREFIX)
302
319
  .replace(/\{\{env.API_BASE_URL\}\}/g, process.env.API_BASE_URL || process.env.API_BASE_PATH)
303
320
  .replace(/\{\{env.WS_URL\}\}/g, process.env.WEBSOCKET_URL || '')
304
321
  .replace(/\{\{env.WS_PATH\}\}/g, process.env.WS_PATH)
322
+ .replace(/\{\{env.ESM_CDN_BASE_URL\}\}/g, process.env.ESM_CDN_BASE_URL || '')
323
+ .replace(/\{\{env.ESM_CDN_SUFFIX\}\}/g, process.env.ESM_CDN_SUFFIX || '')
305
324
  .replace('src="/umi.', `src="${process.env.APP_PUBLIC_PATH}umi.`);
325
+
326
+ if (process.env.CDN_BASE_URL) {
327
+ const appBaseUrl = process.env.CDN_BASE_URL.replace(/\/+$/, '');
328
+ const appPublicPath = process.env.APP_PUBLIC_PATH.replace(/\/+$/, '');
329
+ const re1 = new RegExp(`src="${appPublicPath}/`, 'g');
330
+ const re2 = new RegExp(`href="${appPublicPath}/`, 'g');
331
+ replacedData = replacedData.replace(re1, `src="${appBaseUrl}/`).replace(re2, `href="${appBaseUrl}/`);
332
+ }
306
333
  fs.writeFileSync(file, replacedData, 'utf-8');
307
334
  }
308
335
 
@@ -360,6 +387,7 @@ exports.initEnv = function initEnv() {
360
387
  APP_PORT: 13000,
361
388
  API_BASE_PATH: '/api/',
362
389
  API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_',
390
+ API_CLIENT_SHARE_TOKEN: 'false',
363
391
  API_CLIENT_STORAGE_TYPE: 'localStorage',
364
392
  // DB_DIALECT: 'sqlite',
365
393
  DB_STORAGE: 'storage/db/nocobase.sqlite',
@@ -383,8 +411,12 @@ exports.initEnv = function initEnv() {
383
411
  PLUGIN_STATICS_PATH: '/static/plugins/',
384
412
  LOGGER_BASE_PATH: 'storage/logs',
385
413
  APP_SERVER_BASE_URL: '',
414
+ APP_BASE_URL: '',
415
+ CDN_BASE_URL: '',
386
416
  APP_PUBLIC_PATH: '/',
387
417
  WATCH_FILE: resolve(process.cwd(), 'storage/app.watch.ts'),
418
+ ESM_CDN_BASE_URL: 'https://esm.sh',
419
+ ESM_CDN_SUFFIX: '',
388
420
  };
389
421
 
390
422
  if (
@@ -438,6 +470,16 @@ exports.initEnv = function initEnv() {
438
470
  process.env.__env_modified__ = true;
439
471
  }
440
472
 
473
+ if (!process.env.CDN_BASE_URL && process.env.APP_PUBLIC_PATH !== '/') {
474
+ process.env.CDN_BASE_URL = process.env.APP_PUBLIC_PATH;
475
+ }
476
+
477
+ if (process.env.CDN_BASE_URL.includes('http') && process.env.CDN_VERSION === 'auto') {
478
+ const version = require('../package.json').version;
479
+ process.env.CDN_BASE_URL = process.env.CDN_BASE_URL.replace(/\/+$/, '') + '/' + version + '/';
480
+ process.env.CDN_VERSION = '';
481
+ }
482
+
441
483
  if (!process.env.TZ) {
442
484
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
443
485
  process.env.TZ = getTimezonesByOffset(process.env.DB_TIMEZONE || timeZone);