@nocobase/cli-v1 2.1.0-alpha.45 → 2.1.0-alpha.47

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
@@ -30,6 +30,9 @@ server {
30
30
  client_max_body_size 0;
31
31
  access_log /var/log/nginx/nocobase.log apm;
32
32
 
33
+ include /etc/nginx/mime.types;
34
+ types { application/javascript mjs; }
35
+
33
36
  gzip on;
34
37
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
35
38
 
@@ -55,6 +58,14 @@ server {
55
58
  }
56
59
  }
57
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
+
58
69
  location {{publicPath}}static/plugins/ {
59
70
  alias {{cwd}}/node_modules/;
60
71
  expires 365d;
@@ -88,7 +99,7 @@ server {
88
99
  }
89
100
 
90
101
  location {{v2PublicPath}}assets/ {
91
- alias {{cwd}}/node_modules/@nocobase/app/dist/client/v2/assets/;
102
+ alias {{cwd}}/node_modules/@nocobase/app/dist/client/v/assets/;
92
103
  expires 365d;
93
104
  add_header Cache-Control "public";
94
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-alpha.45",
3
+ "version": "2.1.0-alpha.47",
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-alpha.45",
11
+ "@nocobase/cli": "2.1.0-alpha.47",
12
12
  "@nocobase/license-kit": "^0.3.8",
13
- "@nocobase/utils": "2.1.0-alpha.45",
13
+ "@nocobase/utils": "2.1.0-alpha.47",
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-alpha.45",
28
+ "@nocobase/devtools": "2.1.0-alpha.47",
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": "e9e24987e12d0ad10a5db8815b1e1b7b447f1938"
36
+ "gitHead": "66196b57f9043ea0ac6ebdafbc732bfb98af1396"
37
37
  }
@@ -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.log(chalk.yellow(`Source directory does not exist: ${source}`));
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
- console.log(chalk.green(`Copied main client files from ${source} to ${target}`));
30
+ return true;
31
31
  }
32
32
 
33
33
  /**
34
34
  * 复制插件客户端文件
35
- * @param {string} pluginsBaseDir - 插件基础目录路径
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(pluginsBaseDir, namespace, target) {
40
- const pluginsDir = resolve(process.cwd(), pluginsBaseDir, namespace);
41
- if (await fs.exists(pluginsDir)) {
42
- const pluginNames = await fs.readdir(pluginsDir);
43
- for (const pluginName of pluginNames) {
44
- const pluginPath = resolve(pluginsDir, pluginName);
45
- for (const lane of ['client', 'client-v2']) {
46
- const pluginDistClient = resolve(pluginPath, `dist/${lane}`);
47
- if (await fs.exists(pluginDistClient)) {
48
- const pluginTarget = resolve(target, 'static/plugins', namespace, pluginName, 'dist', lane);
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 copyMainClient(mainClientSource, target);
117
- await copyPluginClients('packages/plugins', '@nocobase', target);
118
- await copyPluginClients('packages/plugins', '@nocobase-example', target);
119
- await copyPluginClients('packages/pro-plugins', '@nocobase', target);
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
- otherLocation = `location = /v2 {
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 /v2/ {
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,32 @@ function resolvePublicPath(appPublicPath = '/') {
367
367
 
368
368
  exports.resolvePublicPath = resolvePublicPath;
369
369
 
370
+ // Default URL segment under which the modern (v2) client is served.
371
+ // Kept local here so the CLI bootstrap (bin/index.js -> initEnv) stays lightweight
372
+ // and does not have to require heavier packages. A second copy of the fixed
373
+ // build-output directory name lives in:
374
+ // - packages/core/app/client-v2/rsbuild.config.ts (output.distPath)
375
+ // - packages/core/server/src/gateway/index.ts (MODERN_CLIENT_DIST_DIR)
376
+ // Keep them in sync. See docs/adr/0001-modern-client-prefix.md.
377
+ const DEFAULT_MODERN_CLIENT_PREFIX = 'v';
378
+
379
+ exports.DEFAULT_MODERN_CLIENT_PREFIX = DEFAULT_MODERN_CLIENT_PREFIX;
380
+
381
+ // Normalize APP_MODERN_CLIENT_PREFIX (accepts `v`, `/v`, `/v/`)
382
+ // down to a bare segment like `v`.
383
+ function normalizeModernClientPrefix(value) {
384
+ const segment = String(value || '')
385
+ .trim()
386
+ .replace(/^\/+|\/+$/g, '');
387
+ return segment || DEFAULT_MODERN_CLIENT_PREFIX;
388
+ }
389
+
390
+ exports.normalizeModernClientPrefix = normalizeModernClientPrefix;
391
+
370
392
  function resolveV2PublicPath(appPublicPath = '/') {
371
393
  const publicPath = resolvePublicPath(appPublicPath);
372
- return `${publicPath.replace(/\/$/, '')}/v2/`;
394
+ const prefix = normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX);
395
+ return `${publicPath.replace(/\/$/, '')}/${prefix}/`;
373
396
  }
374
397
 
375
398
  exports.resolveV2PublicPath = resolveV2PublicPath;
@@ -533,6 +556,7 @@ exports.initEnv = function initEnv() {
533
556
  APP_BASE_URL: '',
534
557
  CDN_BASE_URL: '',
535
558
  APP_PUBLIC_PATH: '/',
559
+ APP_MODERN_CLIENT_PREFIX: DEFAULT_MODERN_CLIENT_PREFIX,
536
560
  ESM_CDN_BASE_URL: 'https://esm.sh',
537
561
  ESM_CDN_SUFFIX: '',
538
562
  };