@nocobase/cli-v1 2.1.0-beta.44 → 2.1.0-beta.46
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/nocobase.conf.tpl
CHANGED
|
@@ -58,6 +58,14 @@ server {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
location ^~ {{distPath}} {
|
|
62
|
+
alias {{cwd}}/storage/dist-client/;
|
|
63
|
+
expires 365d;
|
|
64
|
+
add_header Cache-Control "public";
|
|
65
|
+
access_log off;
|
|
66
|
+
autoindex off;
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
location {{publicPath}}static/plugins/ {
|
|
62
70
|
alias {{cwd}}/node_modules/;
|
|
63
71
|
expires 365d;
|
|
@@ -91,7 +99,7 @@ server {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
location {{v2PublicPath}}assets/ {
|
|
94
|
-
alias {{cwd}}/node_modules/@nocobase/app/dist/client/
|
|
102
|
+
alias {{cwd}}/node_modules/@nocobase/app/dist/client/v/assets/;
|
|
95
103
|
expires 365d;
|
|
96
104
|
add_header Cache-Control "public";
|
|
97
105
|
access_log off;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli-v1",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.46",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"nocobase-v1": "./bin/index.js"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@nocobase/cli": "2.1.0-beta.
|
|
11
|
+
"@nocobase/cli": "2.1.0-beta.46",
|
|
12
12
|
"@nocobase/license-kit": "^0.3.8",
|
|
13
|
-
"@nocobase/utils": "2.1.0-beta.
|
|
13
|
+
"@nocobase/utils": "2.1.0-beta.46",
|
|
14
14
|
"chalk": "^4.1.1",
|
|
15
15
|
"commander": "^9.2.0",
|
|
16
16
|
"deepmerge": "^4.3.1",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"tree-kill": "^1.2.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@nocobase/devtools": "2.1.0-beta.
|
|
28
|
+
"@nocobase/devtools": "2.1.0-beta.46",
|
|
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": "91fd3ab238a5ff58735bbfca04a8cb05d233b0af"
|
|
37
37
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 fs = require('fs-extra');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { resolveDefaultCdnBaseUrlFromActiveVersion } = require('../util');
|
|
16
|
+
|
|
17
|
+
describe('cli-v1 default CDN_BASE_URL', () => {
|
|
18
|
+
const originalEnv = { ...process.env };
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
process.env = { ...originalEnv };
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('prefers the extracted dist-client active version when available', async () => {
|
|
25
|
+
const storagePath = await fs.mkdtemp(path.join(os.tmpdir(), 'nocobase-dist-client-'));
|
|
26
|
+
const distClientRoot = path.join(storagePath, 'dist-client');
|
|
27
|
+
await fs.ensureDir(distClientRoot);
|
|
28
|
+
await fs.writeFile(path.join(distClientRoot, 'active-version'), '2.1.0-beta.45\n', 'utf8');
|
|
29
|
+
process.env.STORAGE_PATH = storagePath;
|
|
30
|
+
|
|
31
|
+
expect(resolveDefaultCdnBaseUrlFromActiveVersion('/console/')).toBe('/console/dist/2.1.0-beta.45/');
|
|
32
|
+
expect(resolveDefaultCdnBaseUrlFromActiveVersion('/')).toBe('/dist/2.1.0-beta.45/');
|
|
33
|
+
|
|
34
|
+
await fs.remove(storagePath);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('returns undefined when the active version file is missing', async () => {
|
|
38
|
+
const storagePath = await fs.mkdtemp(path.join(os.tmpdir(), 'nocobase-dist-client-empty-'));
|
|
39
|
+
process.env.STORAGE_PATH = storagePath;
|
|
40
|
+
|
|
41
|
+
expect(resolveDefaultCdnBaseUrlFromActiveVersion('/console/')).toBeUndefined();
|
|
42
|
+
|
|
43
|
+
await fs.remove(storagePath);
|
|
44
|
+
});
|
|
45
|
+
});
|
package/src/commands/client.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
|
-
const { Command } = require('commander');
|
|
12
11
|
const fs = require('fs-extra');
|
|
13
12
|
const { resolve } = require('path');
|
|
13
|
+
const { discoverPluginPackages } = require('@nocobase/utils/plugin-package');
|
|
14
14
|
const { storagePathJoin } = require('../util');
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -20,39 +20,47 @@ const { storagePathJoin } = require('../util');
|
|
|
20
20
|
*/
|
|
21
21
|
async function copyMainClient(source, target) {
|
|
22
22
|
if (!(await fs.exists(source))) {
|
|
23
|
-
console.
|
|
24
|
-
return;
|
|
23
|
+
console.warn(chalk.yellow(`Source directory does not exist: ${source}`));
|
|
24
|
+
return false;
|
|
25
25
|
}
|
|
26
26
|
// 确保目标目录存在且为空
|
|
27
27
|
await fs.ensureDir(target);
|
|
28
28
|
await fs.emptyDir(target);
|
|
29
29
|
await fs.copy(source, target, { recursive: true });
|
|
30
|
-
|
|
30
|
+
return true;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* 复制插件客户端文件
|
|
35
|
-
* @param {string}
|
|
36
|
-
* @param {string} namespace - 命名空间(如 '@nocobase' 或 '@nocobase-example')
|
|
35
|
+
* @param {Array<{ packageName: string, resolvedPath: string }>} plugins - 插件清单
|
|
37
36
|
* @param {string} target - 目标目录
|
|
38
37
|
*/
|
|
39
|
-
async function copyPluginClients(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await fs.mkdir(resolve(pluginTarget, '..'), { recursive: true });
|
|
50
|
-
await fs.copy(pluginDistClient, pluginTarget, { recursive: true });
|
|
51
|
-
console.log(chalk.green(`Copied ${namespace}/${pluginName} ${lane} files`));
|
|
52
|
-
}
|
|
38
|
+
async function copyPluginClients(plugins, target) {
|
|
39
|
+
let copiedCount = 0;
|
|
40
|
+
for (const plugin of plugins) {
|
|
41
|
+
for (const lane of ['client', 'client-v2']) {
|
|
42
|
+
const pluginDistClient = resolve(plugin.resolvedPath, `dist/${lane}`);
|
|
43
|
+
if (await fs.exists(pluginDistClient)) {
|
|
44
|
+
const pluginTarget = resolve(target, 'static/plugins', plugin.packageName, 'dist', lane);
|
|
45
|
+
await fs.mkdir(resolve(pluginTarget, '..'), { recursive: true });
|
|
46
|
+
await fs.copy(pluginDistClient, pluginTarget, { recursive: true });
|
|
47
|
+
copiedCount++;
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
50
|
}
|
|
51
|
+
return copiedCount;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function writeActiveVersion(version) {
|
|
55
|
+
const distClientRoot = storagePathJoin('dist-client');
|
|
56
|
+
const activeVersionFile = resolve(distClientRoot, 'active-version');
|
|
57
|
+
const tempFile = resolve(distClientRoot, `.active-version.${process.pid}.${Date.now()}.tmp`);
|
|
58
|
+
|
|
59
|
+
await fs.ensureDir(distClientRoot);
|
|
60
|
+
await fs.writeFile(tempFile, `${version}\n`, 'utf8');
|
|
61
|
+
await fs.move(tempFile, activeVersionFile, { overwrite: true });
|
|
62
|
+
|
|
63
|
+
return activeVersionFile;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
/**
|
|
@@ -108,15 +116,38 @@ async function uploadDirectoryToOSS(client, localDir, ossPrefix = '') {
|
|
|
108
116
|
module.exports = (cli) => {
|
|
109
117
|
cli
|
|
110
118
|
.command('client:extract')
|
|
119
|
+
.option('--json', 'Output machine-readable JSON')
|
|
111
120
|
.allowUnknownOption()
|
|
112
|
-
.action(async ()
|
|
121
|
+
.action(async function () {
|
|
122
|
+
const json = this.opts().json === true;
|
|
113
123
|
const version = require('../../package.json').version;
|
|
114
124
|
const target = storagePathJoin('dist-client', version);
|
|
115
125
|
const mainClientSource = resolve(process.cwd(), 'node_modules/@nocobase/app/dist/client');
|
|
116
|
-
await
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
await
|
|
126
|
+
const plugins = await discoverPluginPackages({
|
|
127
|
+
nodeModulesPath: resolve(process.cwd(), 'node_modules'),
|
|
128
|
+
});
|
|
129
|
+
const copiedMainClient = await copyMainClient(mainClientSource, target);
|
|
130
|
+
const copiedPluginBundles = await copyPluginClients(plugins, target);
|
|
131
|
+
const activeVersionFile = await writeActiveVersion(version);
|
|
132
|
+
|
|
133
|
+
if (json) {
|
|
134
|
+
process.stdout.write(
|
|
135
|
+
JSON.stringify({
|
|
136
|
+
version,
|
|
137
|
+
target,
|
|
138
|
+
activeVersionFile,
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log(
|
|
145
|
+
chalk.green(
|
|
146
|
+
`Extracted client assets ${version} to ${target} (main client: ${
|
|
147
|
+
copiedMainClient ? 'yes' : 'no'
|
|
148
|
+
}, plugin bundles: ${copiedPluginBundles}).`,
|
|
149
|
+
),
|
|
150
|
+
);
|
|
120
151
|
});
|
|
121
152
|
|
|
122
153
|
cli
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const { resolve, posix } = require('path');
|
|
11
|
-
const { storagePathJoin, resolvePublicPath, resolveV2PublicPath } = require('../util');
|
|
12
|
-
const { Command } = require('commander');
|
|
11
|
+
const { storagePathJoin, resolvePublicPath, resolveV2PublicPath, normalizeModernClientPrefix } = require('../util');
|
|
13
12
|
const { readFileSync, writeFileSync } = require('fs');
|
|
14
13
|
|
|
15
14
|
/**
|
|
@@ -20,18 +19,22 @@ module.exports = (cli) => {
|
|
|
20
19
|
cli.command('create-nginx-conf').action(async (name, options) => {
|
|
21
20
|
const rawAppPublicPath = process.env.APP_PUBLIC_PATH || '/';
|
|
22
21
|
const appPublicPath = resolvePublicPath(rawAppPublicPath);
|
|
22
|
+
const distPath = `${appPublicPath.replace(/\/$/, '')}/dist/`;
|
|
23
23
|
const v2PublicPath = resolveV2PublicPath(rawAppPublicPath);
|
|
24
|
+
const modernClientPrefix = normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX);
|
|
24
25
|
const appPublicPathWithoutTrailingSlash = appPublicPath.replace(/\/$/, '');
|
|
25
26
|
const v2PublicPathWithoutTrailingSlash = v2PublicPath.replace(/\/$/, '');
|
|
26
27
|
const file = resolve(__dirname, '../../nocobase.conf.tpl');
|
|
27
28
|
const data = readFileSync(file, 'utf-8');
|
|
28
29
|
let otherLocation = '';
|
|
29
30
|
if (appPublicPath !== '/') {
|
|
30
|
-
|
|
31
|
+
// When the app is mounted under a sub-path, redirect the root-level
|
|
32
|
+
// `/<prefix>` and `/<prefix>/` to the real (sub-path-prefixed) location.
|
|
33
|
+
otherLocation = `location = /${modernClientPrefix} {
|
|
31
34
|
return 302 ${v2PublicPath}$is_args$args;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
location /
|
|
37
|
+
location /${modernClientPrefix}/ {
|
|
35
38
|
return 302 ${appPublicPathWithoutTrailingSlash}$uri$is_args$args;
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -43,6 +46,7 @@ module.exports = (cli) => {
|
|
|
43
46
|
const replaced = data
|
|
44
47
|
.replace(/\{\{cwd\}\}/g, posix.resolve(process.cwd()))
|
|
45
48
|
.replace(/\{\{publicPath\}\}/g, appPublicPath)
|
|
49
|
+
.replace(/\{\{distPath\}\}/g, distPath)
|
|
46
50
|
.replace(/\{\{v2PublicPath\}\}/g, v2PublicPath)
|
|
47
51
|
.replace(/\{\{v2PublicPathNoTrailingSlash\}\}/g, v2PublicPathWithoutTrailingSlash)
|
|
48
52
|
.replace(/\{\{apiPort\}\}/g, process.env.APP_PORT)
|
package/src/util.js
CHANGED
|
@@ -367,9 +367,64 @@ function resolvePublicPath(appPublicPath = '/') {
|
|
|
367
367
|
|
|
368
368
|
exports.resolvePublicPath = resolvePublicPath;
|
|
369
369
|
|
|
370
|
+
function resolveDistPublicPath(appPublicPath = '/') {
|
|
371
|
+
const publicPath = resolvePublicPath(appPublicPath).replace(/\/+$/, '');
|
|
372
|
+
return `${publicPath}/dist/`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function buildDefaultCdnBaseUrl(appPublicPath, version) {
|
|
376
|
+
const normalizedVersion = String(version || '')
|
|
377
|
+
.trim()
|
|
378
|
+
.replace(/^\/+|\/+$/g, '');
|
|
379
|
+
if (!normalizedVersion) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
return `${resolveDistPublicPath(appPublicPath)}${normalizedVersion}/`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
exports.buildDefaultCdnBaseUrl = buildDefaultCdnBaseUrl;
|
|
386
|
+
|
|
387
|
+
function resolveDefaultCdnBaseUrlFromActiveVersion(appPublicPath = '/') {
|
|
388
|
+
try {
|
|
389
|
+
const activeVersionFile = storagePathJoin('dist-client', 'active-version');
|
|
390
|
+
if (!fs.existsSync(activeVersionFile)) {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
const activeVersion = String(fs.readFileSync(activeVersionFile, 'utf8') || '').trim();
|
|
394
|
+
return buildDefaultCdnBaseUrl(appPublicPath, activeVersion);
|
|
395
|
+
} catch (_error) {
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
exports.resolveDefaultCdnBaseUrlFromActiveVersion = resolveDefaultCdnBaseUrlFromActiveVersion;
|
|
401
|
+
|
|
402
|
+
// Default URL segment under which the modern (v2) client is served.
|
|
403
|
+
// Kept local here so the CLI bootstrap (bin/index.js -> initEnv) stays lightweight
|
|
404
|
+
// and does not have to require heavier packages. A second copy of the fixed
|
|
405
|
+
// build-output directory name lives in:
|
|
406
|
+
// - packages/core/app/client-v2/rsbuild.config.ts (output.distPath)
|
|
407
|
+
// - packages/core/server/src/gateway/index.ts (MODERN_CLIENT_DIST_DIR)
|
|
408
|
+
// Keep them in sync. See docs/adr/0001-modern-client-prefix.md.
|
|
409
|
+
const DEFAULT_MODERN_CLIENT_PREFIX = 'v';
|
|
410
|
+
|
|
411
|
+
exports.DEFAULT_MODERN_CLIENT_PREFIX = DEFAULT_MODERN_CLIENT_PREFIX;
|
|
412
|
+
|
|
413
|
+
// Normalize APP_MODERN_CLIENT_PREFIX (accepts `v`, `/v`, `/v/`)
|
|
414
|
+
// down to a bare segment like `v`.
|
|
415
|
+
function normalizeModernClientPrefix(value) {
|
|
416
|
+
const segment = String(value || '')
|
|
417
|
+
.trim()
|
|
418
|
+
.replace(/^\/+|\/+$/g, '');
|
|
419
|
+
return segment || DEFAULT_MODERN_CLIENT_PREFIX;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
exports.normalizeModernClientPrefix = normalizeModernClientPrefix;
|
|
423
|
+
|
|
370
424
|
function resolveV2PublicPath(appPublicPath = '/') {
|
|
371
425
|
const publicPath = resolvePublicPath(appPublicPath);
|
|
372
|
-
|
|
426
|
+
const prefix = normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX);
|
|
427
|
+
return `${publicPath.replace(/\/$/, '')}/${prefix}/`;
|
|
373
428
|
}
|
|
374
429
|
|
|
375
430
|
exports.resolveV2PublicPath = resolveV2PublicPath;
|
|
@@ -533,6 +588,7 @@ exports.initEnv = function initEnv() {
|
|
|
533
588
|
APP_BASE_URL: '',
|
|
534
589
|
CDN_BASE_URL: '',
|
|
535
590
|
APP_PUBLIC_PATH: '/',
|
|
591
|
+
APP_MODERN_CLIENT_PREFIX: DEFAULT_MODERN_CLIENT_PREFIX,
|
|
536
592
|
ESM_CDN_BASE_URL: 'https://esm.sh',
|
|
537
593
|
ESM_CDN_SUFFIX: '',
|
|
538
594
|
};
|
|
@@ -598,8 +654,10 @@ exports.initEnv = function initEnv() {
|
|
|
598
654
|
process.env.__env_modified__ = true;
|
|
599
655
|
}
|
|
600
656
|
|
|
601
|
-
if (!process.env.CDN_BASE_URL
|
|
602
|
-
process.env.CDN_BASE_URL =
|
|
657
|
+
if (!process.env.CDN_BASE_URL) {
|
|
658
|
+
process.env.CDN_BASE_URL =
|
|
659
|
+
resolveDefaultCdnBaseUrlFromActiveVersion(process.env.APP_PUBLIC_PATH) ||
|
|
660
|
+
(process.env.APP_PUBLIC_PATH !== '/' ? process.env.APP_PUBLIC_PATH : '');
|
|
603
661
|
}
|
|
604
662
|
|
|
605
663
|
if (process.env.CDN_BASE_URL.includes('http') && process.env.CDN_VERSION === 'auto') {
|