@nocobase/cli 1.3.44-beta → 1.4.0-alpha.0

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/bin/index.js CHANGED
@@ -11,6 +11,14 @@ if (require('semver').satisfies(process.version, '<16')) {
11
11
  process.exit(1);
12
12
  }
13
13
 
14
+ if (__dirname.includes(' ')) {
15
+ console.error(chalk.red(`[nocobase cli]: PathError: Invalid path "${process.cwd()}"`));
16
+ console.error(
17
+ chalk.red('[nocobase cli]: PathError: The path cannot contain spaces. Please modify the path and try again.'),
18
+ );
19
+ process.exit(1);
20
+ }
21
+
14
22
  // if (require('semver').satisfies(process.version, '>16') && !process.env.UNSET_NODE_OPTIONS) {
15
23
  // if (process.env.NODE_OPTIONS) {
16
24
  // let opts = process.env.NODE_OPTIONS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "1.3.44-beta",
3
+ "version": "1.4.0-alpha.0",
4
4
  "description": "",
5
5
  "license": "AGPL-3.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": "1.3.44-beta",
11
+ "@nocobase/app": "1.4.0-alpha.0",
12
12
  "@types/fs-extra": "^11.0.1",
13
13
  "@umijs/utils": "3.5.20",
14
14
  "chalk": "^4.1.1",
@@ -25,12 +25,12 @@
25
25
  "tsx": "^4.19.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@nocobase/devtools": "1.3.44-beta"
28
+ "@nocobase/devtools": "1.4.0-alpha.0"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
32
32
  "url": "git+https://github.com/nocobase/nocobase.git",
33
33
  "directory": "packages/core/cli"
34
34
  },
35
- "gitHead": "1d5666123ac1e2997e434e38defef963ba0d9f90"
35
+ "gitHead": "8ffa7b54bbaf720c0c9857da4b19a99110dffc4b"
36
36
  }
@@ -9,8 +9,12 @@
9
9
 
10
10
  const chalk = require('chalk');
11
11
  const { Command } = require('commander');
12
- const { runAppCommand, runInstall, run, postCheck, nodeCheck, promptForTs } = require('../util');
12
+ const { generatePlugins, run, postCheck, nodeCheck, promptForTs } = require('../util');
13
13
  const { getPortPromise } = require('portfinder');
14
+ const chokidar = require('chokidar');
15
+ const { uid } = require('@formily/shared');
16
+ const path = require('path');
17
+ const fs = require('fs');
14
18
 
15
19
  /**
16
20
  *
@@ -27,6 +31,25 @@ module.exports = (cli) => {
27
31
  .option('--inspect [port]')
28
32
  .allowUnknownOption()
29
33
  .action(async (opts) => {
34
+ const watcher = chokidar.watch('./storage/plugins/**/*', {
35
+ cwd: process.cwd(),
36
+ ignored: /(^|[\/\\])\../, // 忽略隐藏文件
37
+ persistent: true,
38
+ depth: 1, // 只监听第一层目录
39
+ });
40
+
41
+ await fs.promises.mkdir(path.dirname(process.env.WATCH_FILE), { recursive: true });
42
+
43
+ watcher
44
+ .on('addDir', async (pathname) => {
45
+ generatePlugins();
46
+ await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
47
+ })
48
+ .on('unlinkDir', async (pathname) => {
49
+ generatePlugins();
50
+ await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
51
+ });
52
+
30
53
  promptForTs();
31
54
  const { SERVER_TSCONFIG_PATH } = process.env;
32
55
  process.env.IS_DEV_CMD = true;
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  const { Command } = require('commander');
11
- const { run, isDev, isProd, promptForTs } = require('../util');
11
+ const { run, isDev, isProd, promptForTs, downloadPro } = require('../util');
12
12
 
13
13
  /**
14
14
  *
@@ -20,10 +20,14 @@ module.exports = (cli) => {
20
20
  .allowUnknownOption()
21
21
  .option('-h, --help')
22
22
  .option('--ts-node-dev')
23
- .action((options) => {
23
+ .action(async (options) => {
24
+ const cmd = process.argv.slice(2)?.[0];
25
+ if (cmd === 'install') {
26
+ await downloadPro();
27
+ }
24
28
  if (isDev()) {
25
29
  promptForTs();
26
- run('tsx', [
30
+ await run('tsx', [
27
31
  '--tsconfig',
28
32
  SERVER_TSCONFIG_PATH,
29
33
  '-r',
@@ -32,7 +36,7 @@ module.exports = (cli) => {
32
36
  ...process.argv.slice(2),
33
37
  ]);
34
38
  } else if (isProd()) {
35
- run('node', [`${APP_PACKAGE_ROOT}/lib/index.js`, ...process.argv.slice(2)]);
39
+ await run('node', [`${APP_PACKAGE_ROOT}/lib/index.js`, ...process.argv.slice(2)]);
36
40
  }
37
41
  });
38
42
  };
@@ -31,6 +31,7 @@ module.exports = (cli) => {
31
31
  require('./umi')(cli);
32
32
  require('./upgrade')(cli);
33
33
  require('./postinstall')(cli);
34
+ require('./pkg')(cli);
34
35
  if (isPackageValid('@umijs/utils')) {
35
36
  require('./create-plugin')(cli);
36
37
  }
@@ -0,0 +1,218 @@
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
+ const { Command } = require('commander');
11
+ const axios = require('axios');
12
+ const fs = require('fs-extra');
13
+ const zlib = require('zlib');
14
+ const tar = require('tar');
15
+ const path = require('path');
16
+ const { createStoragePluginsSymlink } = require('@nocobase/utils/plugin-symlink');
17
+ const chalk = require('chalk');
18
+
19
+ class Package {
20
+ data;
21
+ constructor(packageName, packageManager) {
22
+ this.packageName = packageName;
23
+ this.packageManager = packageManager;
24
+ this.outputDir = path.resolve(process.cwd(), `storage/plugins/${this.packageName}`);
25
+ }
26
+
27
+ get token() {
28
+ return this.packageManager.getToken();
29
+ }
30
+
31
+ url(path) {
32
+ return this.packageManager.url(path);
33
+ }
34
+
35
+ async mkdir() {
36
+ if (await fs.exists(this.outputDir)) {
37
+ await fs.rm(this.outputDir, { recursive: true, force: true });
38
+ }
39
+ await fs.mkdirp(this.outputDir);
40
+ }
41
+
42
+ async getInfo() {
43
+ try {
44
+ const res = await axios.get(this.url(this.packageName), {
45
+ headers: {
46
+ Authorization: `Bearer ${this.token}`,
47
+ },
48
+ responseType: 'json',
49
+ });
50
+ this.data = res.data;
51
+ } catch (error) {
52
+ return;
53
+ }
54
+ }
55
+
56
+ getTarball(version = 'latest') {
57
+ if (this.data.versions[version]) {
58
+ return [version, this.data.versions[version].dist.tarball];
59
+ }
60
+
61
+ if (version.includes('beta')) {
62
+ version = version.split('beta')[0] + 'beta';
63
+ } else if (version.includes('alpha')) {
64
+ const prefix = (version = version.split('alpha')[0]);
65
+ version = Object.keys(this.data.versions)
66
+ .filter((ver) => ver.startsWith(`${prefix}alpha`))
67
+ .sort()
68
+ .pop();
69
+ }
70
+
71
+ if (version === 'latest') {
72
+ version = this.data['dist-tags']['latest'];
73
+ } else if (version === 'next') {
74
+ version = this.data['dist-tags']['next'];
75
+ }
76
+
77
+ if (!this.data.versions[version]) {
78
+ console.log(chalk.redBright(`Download failed: ${this.packageName}@${version} package does not exist`));
79
+ }
80
+
81
+ return [version, this.data.versions[version].dist.tarball];
82
+ }
83
+
84
+ async isDevPackage() {
85
+ let file = path.resolve(process.cwd(), 'packages/plugins', this.packageName, 'package.json');
86
+ if (await fs.exists(file)) {
87
+ return true;
88
+ }
89
+ file = path.resolve(process.cwd(), 'packages/pro-plugins', this.packageName, 'package.json');
90
+ if (await fs.exists(file)) {
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+
96
+ async download(options = {}) {
97
+ if (await this.isDevPackage()) {
98
+ console.log(chalk.yellowBright(`Skipped: ${this.packageName} is dev package`));
99
+ return;
100
+ }
101
+ await this.getInfo();
102
+ if (!this.data) {
103
+ console.log(chalk.redBright(`Download failed: ${this.packageName} package does not exist`));
104
+ return;
105
+ }
106
+ try {
107
+ const [version, url] = this.getTarball(options.version);
108
+ const response = await axios({
109
+ url,
110
+ responseType: 'stream',
111
+ method: 'GET',
112
+ headers: {
113
+ Authorization: `Bearer ${this.token}`,
114
+ },
115
+ });
116
+ await this.mkdir();
117
+ await new Promise((resolve, reject) => {
118
+ response.data
119
+ .pipe(zlib.createGunzip()) // 解压 gzip
120
+ .pipe(tar.extract({ cwd: this.outputDir, strip: 1 })) // 解压 tar
121
+ .on('finish', resolve)
122
+ .on('error', reject);
123
+ });
124
+ console.log(chalk.greenBright(`Download success: ${this.packageName}@${version}`));
125
+ } catch (error) {
126
+ console.log(chalk.redBright(`Download failed: ${this.packageName}`));
127
+ }
128
+ }
129
+ }
130
+
131
+ class PackageManager {
132
+ token;
133
+ baseURL;
134
+
135
+ constructor({ baseURL }) {
136
+ this.baseURL = baseURL;
137
+ }
138
+
139
+ getToken() {
140
+ return this.token;
141
+ }
142
+
143
+ getBaseURL() {
144
+ return this.baseURL;
145
+ }
146
+
147
+ url(path) {
148
+ return this.baseURL + path;
149
+ }
150
+
151
+ async login(credentials) {
152
+ try {
153
+ const res1 = await axios.post(`${this.baseURL}-/verdaccio/sec/login`, credentials, {
154
+ responseType: 'json',
155
+ });
156
+ this.token = res1.data.token;
157
+ } catch (error) {
158
+ console.error(chalk.redBright(`Login failed: ${this.baseURL}`));
159
+ }
160
+ }
161
+
162
+ getPackage(packageName) {
163
+ return new Package(packageName, this);
164
+ }
165
+
166
+ async getProPackages() {
167
+ const res = await axios.get(this.url('pro-packages'), {
168
+ headers: {
169
+ Authorization: `Bearer ${this.token}`,
170
+ },
171
+ responseType: 'json',
172
+ });
173
+ return res.data.data;
174
+ }
175
+
176
+ async getPackages() {
177
+ const pkgs = await this.getProPackages();
178
+ return pkgs;
179
+ }
180
+
181
+ async download(options = {}) {
182
+ const { version } = options;
183
+ if (!this.token) {
184
+ return;
185
+ }
186
+ const pkgs = await this.getPackages();
187
+ for (const pkg of pkgs) {
188
+ await this.getPackage(pkg).download({ version });
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ *
195
+ * @param {Command} cli
196
+ */
197
+ module.exports = (cli) => {
198
+ const pkg = cli.command('pkg');
199
+ pkg
200
+ .command('download-pro')
201
+ .option('-V, --version [version]')
202
+ .action(async () => {
203
+ const { NOCOBASE_PKG_URL, NOCOBASE_PKG_USERNAME, NOCOBASE_PKG_PASSWORD } = process.env;
204
+ if (!(NOCOBASE_PKG_URL && NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD)) {
205
+ return;
206
+ }
207
+ const credentials = { username: NOCOBASE_PKG_USERNAME, password: NOCOBASE_PKG_PASSWORD };
208
+ const pm = new PackageManager({ baseURL: NOCOBASE_PKG_URL });
209
+ await pm.login(credentials);
210
+ const file = path.resolve(__dirname, '../../package.json');
211
+ const json = await fs.readJson(file);
212
+ await pm.download({ version: json.version });
213
+ await createStoragePluginsSymlink();
214
+ });
215
+ pkg.command('export-all').action(async () => {
216
+ console.log('Todo...');
217
+ });
218
+ };
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  const { Command } = require('commander');
11
- const { run, isDev, isPackageValid, generatePlaywrightPath } = require('../util');
11
+ const { run, isDev, isPackageValid, generatePlaywrightPath, generatePlugins } = require('../util');
12
12
  const { dirname, resolve } = require('path');
13
13
  const { existsSync, mkdirSync, readFileSync, appendFileSync } = require('fs');
14
14
  const { readFile, writeFile } = require('fs').promises;
@@ -48,7 +48,7 @@ module.exports = (cli) => {
48
48
  .option('--skip-umi')
49
49
  .action(async (options) => {
50
50
  writeToExclude();
51
-
51
+ generatePlugins();
52
52
  generatePlaywrightPath(true);
53
53
  await createStoragePluginsSymlink();
54
54
  if (!isDev()) {
@@ -8,10 +8,11 @@
8
8
  */
9
9
 
10
10
  const { Command } = require('commander');
11
- const { isDev, run, postCheck, runInstall, promptForTs } = require('../util');
11
+ const { isDev, run, postCheck, downloadPro, promptForTs } = require('../util');
12
12
  const { existsSync, rmSync } = require('fs');
13
13
  const { resolve } = require('path');
14
14
  const chalk = require('chalk');
15
+ const chokidar = require('chokidar');
15
16
 
16
17
  function deleteSockFiles() {
17
18
  const { SOCKET_PATH, PM2_HOME } = process.env;
@@ -38,6 +39,23 @@ module.exports = (cli) => {
38
39
  .option('--quickstart')
39
40
  .allowUnknownOption()
40
41
  .action(async (opts) => {
42
+ if (opts.quickstart) {
43
+ await downloadPro();
44
+ }
45
+
46
+ const watcher = chokidar.watch('./storage/plugins/**/*', {
47
+ cwd: process.cwd(),
48
+ ignoreInitial: true,
49
+ ignored: /(^|[\/\\])\../, // 忽略隐藏文件
50
+ persistent: true,
51
+ depth: 1, // 只监听第一层目录
52
+ });
53
+
54
+ watcher.on('addDir', async (pathname) => {
55
+ console.log('pathname', pathname);
56
+ await run('yarn', ['nocobase', 'pm2-restart']);
57
+ });
58
+
41
59
  if (opts.port) {
42
60
  process.env.APP_PORT = opts.port;
43
61
  }
@@ -10,7 +10,7 @@
10
10
  const chalk = require('chalk');
11
11
  const { Command } = require('commander');
12
12
  const { resolve } = require('path');
13
- const { run, promptForTs, runAppCommand, hasCorePackages, updateJsonFile, hasTsNode } = require('../util');
13
+ const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode } = require('../util');
14
14
  const { existsSync, rmSync } = require('fs');
15
15
 
16
16
  /**
@@ -29,15 +29,18 @@ module.exports = (cli) => {
29
29
  if (hasTsNode()) promptForTs();
30
30
  if (hasCorePackages()) {
31
31
  // await run('yarn', ['install']);
32
+ await downloadPro();
32
33
  await runAppCommand('upgrade');
33
34
  return;
34
35
  }
35
36
  if (options.skipCodeUpdate) {
37
+ await downloadPro();
36
38
  await runAppCommand('upgrade');
37
39
  return;
38
40
  }
39
41
  // await runAppCommand('upgrade');
40
42
  if (!hasTsNode()) {
43
+ await downloadPro();
41
44
  await runAppCommand('upgrade');
42
45
  return;
43
46
  }
@@ -54,8 +57,9 @@ module.exports = (cli) => {
54
57
  stdio: 'pipe',
55
58
  });
56
59
  if (pkg.version === stdout) {
60
+ await downloadPro();
57
61
  await runAppCommand('upgrade');
58
- rmAppDir();
62
+ await rmAppDir();
59
63
  return;
60
64
  }
61
65
  const currentY = 1 * pkg.version.split('.')[1];
@@ -66,7 +70,8 @@ module.exports = (cli) => {
66
70
  await run('yarn', ['add', '@nocobase/cli', '@nocobase/devtools', '-W']);
67
71
  }
68
72
  await run('yarn', ['install']);
73
+ await downloadPro();
69
74
  await runAppCommand('upgrade');
70
- rmAppDir();
75
+ await rmAppDir();
71
76
  });
72
77
  };
@@ -72,7 +72,7 @@ class PluginGenerator extends Generator {
72
72
  });
73
73
  this.log('');
74
74
  genTsConfigPaths();
75
- execa.sync('yarn', ['postinstall', '--skip-umi'], { shell: true, stdio: 'inherit' });
75
+ execa.sync('yarn', ['postinstall'], { shell: true, stdio: 'inherit' });
76
76
  this.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`);
77
77
  }
78
78
  }
package/src/util.js CHANGED
@@ -163,6 +163,10 @@ exports.promptForTs = () => {
163
163
  console.log(chalk.green('WAIT: ') + 'TypeScript compiling...');
164
164
  };
165
165
 
166
+ exports.downloadPro = async () => {
167
+ await exports.run('yarn', ['nocobase', 'pkg', 'download-pro']);
168
+ };
169
+
166
170
  exports.updateJsonFile = async (target, fn) => {
167
171
  const content = await readFile(target, 'utf-8');
168
172
  const json = JSON.parse(content);
@@ -287,6 +291,7 @@ function buildIndexHtml(force = false) {
287
291
  const data = fs.readFileSync(tpl, 'utf-8');
288
292
  const replacedData = data
289
293
  .replace(/\{\{env.APP_PUBLIC_PATH\}\}/g, process.env.APP_PUBLIC_PATH)
294
+ .replace(/\{\{env.API_CLIENT_STORAGE_TYPE\}\}/g, process.env.API_CLIENT_STORAGE_TYPE)
290
295
  .replace(/\{\{env.API_CLIENT_STORAGE_PREFIX\}\}/g, process.env.API_CLIENT_STORAGE_PREFIX)
291
296
  .replace(/\{\{env.API_BASE_URL\}\}/g, process.env.API_BASE_URL || process.env.API_BASE_PATH)
292
297
  .replace(/\{\{env.WS_URL\}\}/g, process.env.WEBSOCKET_URL || '')
@@ -323,6 +328,7 @@ exports.initEnv = function initEnv() {
323
328
  APP_PORT: 13000,
324
329
  API_BASE_PATH: '/api/',
325
330
  API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_',
331
+ API_CLIENT_STORAGE_TYPE: 'localStorage',
326
332
  DB_DIALECT: 'sqlite',
327
333
  DB_STORAGE: 'storage/db/nocobase.sqlite',
328
334
  // DB_TIMEZONE: '+00:00',
@@ -344,6 +350,7 @@ exports.initEnv = function initEnv() {
344
350
  LOGGER_BASE_PATH: 'storage/logs',
345
351
  APP_SERVER_BASE_URL: '',
346
352
  APP_PUBLIC_PATH: '/',
353
+ WATCH_FILE: resolve(process.cwd(), 'storage/app.watch.ts'),
347
354
  };
348
355
 
349
356
  if (
@@ -416,3 +423,13 @@ exports.initEnv = function initEnv() {
416
423
  );
417
424
  }
418
425
  };
426
+
427
+ exports.generatePlugins = function () {
428
+ try {
429
+ require.resolve('@nocobase/devtools/umiConfig');
430
+ const { generatePlugins } = require('@nocobase/devtools/umiConfig');
431
+ generatePlugins();
432
+ } catch (error) {
433
+ return;
434
+ }
435
+ };