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

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
@@ -50,6 +50,14 @@ server {
50
50
  allow all;
51
51
  }
52
52
 
53
+ location ~ ^/static/plugins/@([^/]+)/([^/]+)/dist/client-v2/(.*)$ {
54
+ allow all;
55
+ }
56
+
57
+ location ~ ^/static/plugins/([^/]+)/dist/client-v2/(.*)$ {
58
+ allow all;
59
+ }
60
+
53
61
  location ~ ^/static/plugins/(.*)$ {
54
62
  deny all;
55
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.1.0-alpha.10",
3
+ "version": "2.1.0-alpha.12",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./src/index.js",
@@ -8,7 +8,7 @@
8
8
  "nocobase": "./bin/index.js"
9
9
  },
10
10
  "dependencies": {
11
- "@nocobase/app": "2.1.0-alpha.10",
11
+ "@nocobase/app": "2.1.0-alpha.12",
12
12
  "@nocobase/license-kit": "^0.3.8",
13
13
  "@types/fs-extra": "^11.0.1",
14
14
  "@umijs/utils": "3.5.20",
@@ -27,12 +27,12 @@
27
27
  "tsx": "^4.19.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@nocobase/devtools": "2.1.0-alpha.10"
30
+ "@nocobase/devtools": "2.1.0-alpha.12"
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
34
34
  "url": "git+https://github.com/nocobase/nocobase.git",
35
35
  "directory": "packages/core/cli"
36
36
  },
37
- "gitHead": "ce790d46c0a5768ca9618c7d0d77ab8300de75c8"
37
+ "gitHead": "f12c4a75470590b1670ce54510b96ef94c2cd7a2"
38
38
  }
@@ -41,12 +41,14 @@ async function copyPluginClients(pluginsBaseDir, namespace, target) {
41
41
  const pluginNames = await fs.readdir(pluginsDir);
42
42
  for (const pluginName of pluginNames) {
43
43
  const pluginPath = resolve(pluginsDir, pluginName);
44
- const pluginDistClient = resolve(pluginPath, 'dist/client');
45
- if (await fs.exists(pluginDistClient)) {
46
- const pluginTarget = resolve(target, 'static/plugins', namespace, pluginName, 'dist/client');
47
- await fs.mkdir(resolve(pluginTarget, '..'), { recursive: true });
48
- await fs.copy(pluginDistClient, pluginTarget, { recursive: true });
49
- console.log(chalk.green(`Copied ${namespace}/${pluginName} client files`));
44
+ for (const lane of ['client', 'client-v2']) {
45
+ const pluginDistClient = resolve(pluginPath, `dist/${lane}`);
46
+ if (await fs.exists(pluginDistClient)) {
47
+ const pluginTarget = resolve(target, 'static/plugins', namespace, pluginName, 'dist', lane);
48
+ await fs.mkdir(resolve(pluginTarget, '..'), { recursive: true });
49
+ await fs.copy(pluginDistClient, pluginTarget, { recursive: true });
50
+ console.log(chalk.green(`Copied ${namespace}/${pluginName} ${lane} files`));
51
+ }
50
52
  }
51
53
  }
52
54
  }
@@ -8,7 +8,17 @@
8
8
  */
9
9
  const _ = require('lodash');
10
10
  const { Command } = require('commander');
11
- const { generatePlugins, run, postCheck, nodeCheck, promptForTs, isPortReachable, checkDBDialect } = require('../util');
11
+ const {
12
+ generatePlugins,
13
+ run,
14
+ runWithPrefix,
15
+ postCheck,
16
+ nodeCheck,
17
+ promptForTs,
18
+ isPortReachable,
19
+ buildWSURL,
20
+ checkDBDialect,
21
+ } = require('../util');
12
22
  const { getPortPromise } = require('portfinder');
13
23
  const chokidar = require('chokidar');
14
24
  const { uid } = require('@formily/shared');
@@ -42,6 +52,7 @@ module.exports = (cli) => {
42
52
  .option('-c, --client')
43
53
  .option('-s, --server')
44
54
  .option('--db-sync')
55
+ .option('--rsbuild')
45
56
  .option('--client-v2-only')
46
57
  .option('-i, --inspect [port]')
47
58
  .allowUnknownOption()
@@ -65,7 +76,7 @@ module.exports = (cli) => {
65
76
  return;
66
77
  }
67
78
 
68
- const { port, client, server, inspect, clientV2Only } = opts;
79
+ const { port, client, server, inspect, clientV2Only, rsbuild } = opts;
69
80
 
70
81
  if (port) {
71
82
  process.env.APP_PORT = opts.port;
@@ -83,6 +94,7 @@ module.exports = (cli) => {
83
94
  const shouldRunClientV2 = clientV2Only || client || !server;
84
95
  const shouldRunClient = !clientV2Only && (client || !server);
85
96
  const shouldRunServer = !clientV2Only && (server || !client);
97
+ const shouldRunClientWithRsbuild = shouldRunClient && !!rsbuild;
86
98
 
87
99
  if (shouldRunServer && server) {
88
100
  serverPort = APP_PORT;
@@ -98,27 +110,35 @@ module.exports = (cli) => {
98
110
  });
99
111
  }
100
112
 
113
+ let subprocessClient;
114
+ let subprocessClientV2;
115
+
101
116
  const runDevClientV2 = () => {
102
117
  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),
118
+ subprocessClientV2 = runWithPrefix(
119
+ 'rsbuild',
120
+ ['dev', '--config', `${APP_PACKAGE_ROOT}/client-v2/rsbuild.config.ts`],
121
+ {
122
+ prefix: 'client-v2',
123
+ color: 'magenta',
124
+ env: {
125
+ ...process.env,
126
+ APP_V2_PORT: `${clientV2Port}`,
127
+ NODE_ENV: 'development',
128
+ RSPACK_HMR_CLIENT_PORT: `${clientV2Only ? clientV2Port : clientPort}`,
129
+ API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
130
+ API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
131
+ API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
132
+ API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
133
+ WEBSOCKET_URL: process.env.WEBSOCKET_URL || buildWSURL(process.env.API_BASE_URL, serverPort),
134
+ WS_PATH: process.env.WS_PATH,
135
+ ESM_CDN_BASE_URL: process.env.ESM_CDN_BASE_URL || 'https://esm.sh',
136
+ ESM_CDN_SUFFIX: process.env.ESM_CDN_SUFFIX || '',
137
+ PROXY_TARGET_URL:
138
+ process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
139
+ },
120
140
  },
121
- });
141
+ );
122
142
  };
123
143
 
124
144
  if (clientV2Only) {
@@ -126,28 +146,60 @@ module.exports = (cli) => {
126
146
  return;
127
147
  }
128
148
 
129
- let subprocess;
130
149
  const runDevClient = () => {
131
150
  console.log('starting client', 1 * clientPort);
132
- subprocess = run('umi', ['dev'], {
151
+ const command = shouldRunClientWithRsbuild ? 'rsbuild' : 'umi';
152
+ const args = shouldRunClientWithRsbuild
153
+ ? ['dev', '--config', `${APP_PACKAGE_ROOT}/client/rsbuild.config.ts`]
154
+ : ['dev'];
155
+ subprocessClient = runWithPrefix(command, args, {
156
+ prefix: 'client',
157
+ color: 'cyan',
133
158
  env: {
134
159
  ...process.env,
135
160
  stdio: 'inherit',
136
161
  shell: true,
137
162
  PORT: clientPort,
163
+ APP_PORT: `${clientPort}`,
138
164
  APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
139
165
  APP_V2_PORT: `${clientV2Port}`,
140
- WEBSOCKET_URL:
141
- process.env.WEBSOCKET_URL ||
142
- (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
166
+ NODE_ENV: 'development',
167
+ RSPACK_HMR_CLIENT_PORT: `${clientPort}`,
168
+ API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
169
+ API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
170
+ API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
171
+ API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
172
+ WEBSOCKET_URL: process.env.WEBSOCKET_URL || buildWSURL(process.env.API_BASE_URL, serverPort),
143
173
  PROXY_TARGET_URL:
144
174
  process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
145
175
  },
146
176
  });
147
177
  };
148
178
 
179
+ const restartSubprocess = async (subprocessRef, port, start) => {
180
+ if (!subprocessRef) {
181
+ start();
182
+ return;
183
+ }
184
+ subprocessRef.cancel();
185
+ let i = 0;
186
+ while (true) {
187
+ ++i;
188
+ const result = await isPortReachable(port);
189
+ if (!result) {
190
+ break;
191
+ }
192
+ await sleep(500);
193
+ if (i > 10) {
194
+ break;
195
+ }
196
+ }
197
+ start();
198
+ };
199
+
149
200
  if (shouldRunClient) {
150
- const watcher = chokidar.watch('./storage/plugins/**/*', {
201
+ const storagePluginPath = path.resolve(process.cwd(), 'storage/plugins');
202
+ const watcher = chokidar.watch(`${storagePluginPath}/**/*`, {
151
203
  cwd: process.cwd(),
152
204
  ignored: /(^|[\/\\])\../, // 忽略隐藏文件
153
205
  persistent: true,
@@ -160,28 +212,18 @@ module.exports = (cli) => {
160
212
  const restartClient = _.debounce(async () => {
161
213
  if (!isReady) return;
162
214
  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');
215
+ if (shouldRunClient) {
216
+ await restartSubprocess(subprocessClient, clientPort, runDevClient);
217
+ }
218
+ if (shouldRunClientV2) {
219
+ await restartSubprocess(subprocessClientV2, clientV2Port, runDevClientV2);
180
220
  }
221
+ await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
181
222
  }, 500);
182
223
 
183
224
  watcher
184
225
  .on('ready', () => {
226
+ console.log('watching plugin folder changes...');
185
227
  isReady = true;
186
228
  })
187
229
  .on('addDir', async () => {
package/src/util.js CHANGED
@@ -83,6 +83,44 @@ exports.run = (command, args, options = {}) => {
83
83
  });
84
84
  };
85
85
 
86
+ exports.runWithPrefix = (command, args, options = {}) => {
87
+ if (command === 'tsx') {
88
+ command = 'node';
89
+ args = ['./node_modules/tsx/dist/cli.mjs'].concat(args || []);
90
+ }
91
+
92
+ const prefix = options.prefix || 'process';
93
+ const color = options.color || 'cyan';
94
+ const label = chalk[color](`[${prefix}]`);
95
+ const subprocess = execa(command, args, {
96
+ shell: true,
97
+ stdio: 'pipe',
98
+ ...options,
99
+ env: {
100
+ ...process.env,
101
+ ...options.env,
102
+ },
103
+ });
104
+
105
+ const writePrefixed = (chunk, writer) => {
106
+ const text = chunk.toString();
107
+ const lines = text.split(/\r?\n/);
108
+ const trailingNewline = text.endsWith('\n') || text.endsWith('\r');
109
+
110
+ lines.forEach((line, index) => {
111
+ if (!line && index === lines.length - 1 && trailingNewline) {
112
+ return;
113
+ }
114
+ writer.write(`${label} ${line}\n`);
115
+ });
116
+ };
117
+
118
+ subprocess.stdout?.on('data', (chunk) => writePrefixed(chunk, process.stdout));
119
+ subprocess.stderr?.on('data', (chunk) => writePrefixed(chunk, process.stderr));
120
+
121
+ return subprocess;
122
+ };
123
+
86
124
  exports.isPortReachable = async (port, { timeout = 1000, host } = {}) => {
87
125
  const promise = new Promise((resolve, reject) => {
88
126
  const socket = new net.Socket();
@@ -232,6 +270,7 @@ exports.genTsConfigPaths = function genTsConfigPaths() {
232
270
  .split(sep)
233
271
  .join('/');
234
272
  paths[`${packageJsonName}/client`] = [`${relativePath}/src/client`];
273
+ paths[`${packageJsonName}/client-v2`] = [`${relativePath}/src/client-v2`];
235
274
  paths[`${packageJsonName}/package.json`] = [`${relativePath}/package.json`];
236
275
  paths[packageJsonName] = [`${relativePath}/src`];
237
276
  if (packageJsonName === '@nocobase/test') {
@@ -523,10 +562,40 @@ exports.checkDBDialect = function () {
523
562
 
524
563
  exports.generatePlugins = function () {
525
564
  try {
526
- require.resolve('@nocobase/devtools/umiConfig');
527
- const { generatePlugins } = require('@nocobase/devtools/umiConfig');
528
- generatePlugins();
565
+ require.resolve('@nocobase/devtools/common');
566
+ const { generateAllPlugins, generatePlugins, generateV2Plugins } = require('@nocobase/devtools/common');
567
+ if (typeof generateAllPlugins === 'function') {
568
+ generateAllPlugins();
569
+ return;
570
+ }
571
+ generatePlugins?.();
572
+ generateV2Plugins?.();
529
573
  } catch (error) {
530
574
  return;
531
575
  }
532
576
  };
577
+
578
+ exports.isURL = function isURL(str) {
579
+ let url;
580
+
581
+ try {
582
+ url = new URL(str);
583
+ } catch (e) {
584
+ return false;
585
+ }
586
+
587
+ return url.protocol === 'http:' || url.protocol === 'https:';
588
+ };
589
+
590
+ exports.buildWSURL = function buildWSURL(urlString, serverPort) {
591
+ if (exports.isURL(urlString)) {
592
+ const parsedUrl = new URL(urlString);
593
+ parsedUrl.protocol = parsedUrl.protocol === 'https:' ? 'wss:' : 'ws:';
594
+ parsedUrl.pathname =
595
+ process.env.WS_PATH || (process.env.APP_PUBLIC_PATH ? process.env.APP_PUBLIC_PATH + 'ws' : '/ws');
596
+ const url = parsedUrl.toString();
597
+ return url;
598
+ }
599
+
600
+ return serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined;
601
+ };
@@ -0,0 +1,2 @@
1
+ export * from './dist/client-v2';
2
+ export { default } from './dist/client-v2';
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/client-v2/index.js');
@@ -5,6 +5,7 @@
5
5
  "dependencies": {},
6
6
  "peerDependencies": {
7
7
  "@nocobase/client": "2.x",
8
+ "@nocobase/client-v2": "2.x",
8
9
  "@nocobase/server": "2.x",
9
10
  "@nocobase/test": "2.x"
10
11
  }
@@ -0,0 +1,103 @@
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
+ // CSS modules
11
+ type CSSModuleClasses = { readonly [key: string]: string };
12
+
13
+ declare module '*.module.css' {
14
+ const classes: CSSModuleClasses;
15
+ export default classes;
16
+ }
17
+ declare module '*.module.scss' {
18
+ const classes: CSSModuleClasses;
19
+ export default classes;
20
+ }
21
+ declare module '*.module.sass' {
22
+ const classes: CSSModuleClasses;
23
+ export default classes;
24
+ }
25
+ declare module '*.module.less' {
26
+ const classes: CSSModuleClasses;
27
+ export default classes;
28
+ }
29
+ declare module '*.module.styl' {
30
+ const classes: CSSModuleClasses;
31
+ export default classes;
32
+ }
33
+ declare module '*.module.stylus' {
34
+ const classes: CSSModuleClasses;
35
+ export default classes;
36
+ }
37
+ declare module '*.module.pcss' {
38
+ const classes: CSSModuleClasses;
39
+ export default classes;
40
+ }
41
+ declare module '*.module.sss' {
42
+ const classes: CSSModuleClasses;
43
+ export default classes;
44
+ }
45
+
46
+ // CSS
47
+ declare module '*.css' {}
48
+ declare module '*.scss' {}
49
+ declare module '*.sass' {}
50
+ declare module '*.less' {}
51
+ declare module '*.styl' {}
52
+ declare module '*.stylus' {}
53
+ declare module '*.pcss' {}
54
+ declare module '*.sss' {}
55
+
56
+ declare module '*.apng' {
57
+ const src: string;
58
+ export default src;
59
+ }
60
+ declare module '*.png' {
61
+ const src: string;
62
+ export default src;
63
+ }
64
+ declare module '*.jpg' {
65
+ const src: string;
66
+ export default src;
67
+ }
68
+ declare module '*.jpeg' {
69
+ const src: string;
70
+ export default src;
71
+ }
72
+ declare module '*.jfif' {
73
+ const src: string;
74
+ export default src;
75
+ }
76
+ declare module '*.pjpeg' {
77
+ const src: string;
78
+ export default src;
79
+ }
80
+ declare module '*.pjp' {
81
+ const src: string;
82
+ export default src;
83
+ }
84
+ declare module '*.gif' {
85
+ const src: string;
86
+ export default src;
87
+ }
88
+ declare module '*.svg' {
89
+ const src: string;
90
+ export default src;
91
+ }
92
+ declare module '*.ico' {
93
+ const src: string;
94
+ export default src;
95
+ }
96
+ declare module '*.webp' {
97
+ const src: string;
98
+ export default src;
99
+ }
100
+ declare module '*.avif' {
101
+ const src: string;
102
+ export default src;
103
+ }
@@ -0,0 +1 @@
1
+ export { default } from './plugin';
@@ -0,0 +1,7 @@
1
+ import { Plugin } from '@nocobase/client-v2';
2
+
3
+ export class {{{pascalCaseName}}}ClientV2 extends Plugin {
4
+ async load() {}
5
+ }
6
+
7
+ export default {{{pascalCaseName}}}ClientV2;