@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.
- package/LICENSE +201 -661
- package/README.md +79 -10
- package/nocobase.conf.tpl +77 -1
- package/package.json +6 -6
- package/src/commands/build.js +34 -2
- package/src/commands/client.js +166 -0
- package/src/commands/create-nginx-conf.js +19 -3
- package/src/commands/dev.js +128 -70
- package/src/commands/index.js +1 -0
- package/src/commands/pkg.js +6 -1
- package/src/commands/pm2.js +1 -1
- package/src/commands/update-deps.js +1 -0
- package/src/license.js +1 -1
- package/src/util.js +43 -1
- package/templates/bundle-status.html +338 -0
- package/templates/create-app-package.json +1 -1
package/src/commands/dev.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
89
|
+
} else if (shouldRunServer) {
|
|
138
90
|
serverPort = await getPortPromise({
|
|
139
91
|
port: 1 * clientPort + 1,
|
|
140
92
|
});
|
|
141
93
|
}
|
|
142
94
|
|
|
143
|
-
if (
|
|
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 (
|
|
240
|
+
if (shouldRunClient) {
|
|
187
241
|
runDevClient();
|
|
188
242
|
}
|
|
243
|
+
|
|
244
|
+
if (shouldRunClientV2) {
|
|
245
|
+
runDevClientV2();
|
|
246
|
+
}
|
|
189
247
|
});
|
|
190
248
|
};
|
package/src/commands/index.js
CHANGED
package/src/commands/pkg.js
CHANGED
|
@@ -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
|
|
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 {
|
package/src/commands/pm2.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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);
|