@nocobase/cli 0.18.0-alpha.9 → 0.19.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "0.18.0-alpha.9",
3
+ "version": "0.19.0-alpha.10",
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": "0.18.0-alpha.9",
11
+ "@nocobase/app": "0.19.0-alpha.10",
12
12
  "@types/fs-extra": "^11.0.1",
13
13
  "@umijs/utils": "3.5.20",
14
14
  "chalk": "^4.1.1",
@@ -21,15 +21,16 @@
21
21
  "pm2": "^5.2.0",
22
22
  "portfinder": "^1.0.28",
23
23
  "serve": "^13.0.2",
24
+ "tree-kill": "^1.2.2",
24
25
  "tsx": "^4.6.2"
25
26
  },
26
27
  "devDependencies": {
27
- "@nocobase/devtools": "0.18.0-alpha.9"
28
+ "@nocobase/devtools": "0.19.0-alpha.10"
28
29
  },
29
30
  "repository": {
30
31
  "type": "git",
31
32
  "url": "git+https://github.com/nocobase/nocobase.git",
32
33
  "directory": "packages/core/cli"
33
34
  },
34
- "gitHead": "34ca0df4eede2e83fc86297b0fe19eba970e2b1b"
35
+ "gitHead": "d09d81eba67339da36bcec27939a85b35d180770"
35
36
  }
@@ -106,7 +106,9 @@ module.exports = (cli) => {
106
106
  env: {
107
107
  PORT: clientPort,
108
108
  APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
109
- WEBSOCKET_URL: process.env.WEBSOCKET_URL || (serverPort ? `ws://localhost:${serverPort}/ws` : undefined),
109
+ WEBSOCKET_URL:
110
+ process.env.WEBSOCKET_URL ||
111
+ (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
110
112
  PROXY_TARGET_URL:
111
113
  process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
112
114
  },
@@ -3,6 +3,9 @@ const { run, isPortReachable } = require('../util');
3
3
  const { execSync } = require('node:child_process');
4
4
  const axios = require('axios');
5
5
  const { pTest } = require('./p-test');
6
+ const os = require('os');
7
+ const treeKill = require('tree-kill');
8
+ const chalk = require('chalk');
6
9
 
7
10
  /**
8
11
  * 检查服务是否启动成功
@@ -32,7 +35,7 @@ const checkServer = async (duration = 1000, max = 60 * 10) => {
32
35
  }
33
36
  })
34
37
  .catch((error) => {
35
- console.error('Request error:', error.message);
38
+ console.error('Request error:', error?.response?.data?.error);
36
39
  });
37
40
  }, duration);
38
41
  });
@@ -90,6 +93,17 @@ async function runApp(options = {}) {
90
93
  run('nocobase', [process.env.APP_ENV === 'production' ? 'start' : 'dev'], options);
91
94
  }
92
95
 
96
+ process.on('SIGINT', async () => {
97
+ treeKill(process.pid, (error) => {
98
+ if (error) {
99
+ console.error(error);
100
+ } else {
101
+ console.log(chalk.yellow('Force killing...'));
102
+ }
103
+ process.exit();
104
+ });
105
+ });
106
+
93
107
  const commonConfig = {
94
108
  stdio: 'inherit',
95
109
  };
@@ -127,6 +141,12 @@ const filterArgv = () => {
127
141
  if (element === '--skip-reporter') {
128
142
  continue;
129
143
  }
144
+ if (element === '--build') {
145
+ continue;
146
+ }
147
+ if (element === '--production') {
148
+ continue;
149
+ }
130
150
  argv.push(element);
131
151
  }
132
152
  return argv;
@@ -143,12 +163,23 @@ module.exports = (cli) => {
143
163
  console.log('APP_BASE_URL:', process.env.APP_BASE_URL);
144
164
  }
145
165
  });
166
+
146
167
  e2e
147
168
  .command('test')
148
169
  .allowUnknownOption()
149
170
  .option('--url [url]')
150
171
  .option('--skip-reporter')
172
+ .option('--build')
173
+ .option('--production')
151
174
  .action(async (options) => {
175
+ process.env.__E2E__ = true;
176
+ if (options.production) {
177
+ process.env.APP_ENV = 'production';
178
+ }
179
+ if (options.build) {
180
+ process.env.APP_ENV = 'production';
181
+ await run('yarn', ['build']);
182
+ }
152
183
  if (options.skipReporter) {
153
184
  process.env.PLAYWRIGHT_SKIP_REPORTER = true;
154
185
  }
@@ -183,8 +214,13 @@ module.exports = (cli) => {
183
214
  e2e
184
215
  .command('start-app')
185
216
  .option('--production')
217
+ .option('--build')
186
218
  .option('--port [port]')
187
219
  .action(async (options) => {
220
+ process.env.__E2E__ = true;
221
+ if (options.build) {
222
+ await run('yarn', ['build']);
223
+ }
188
224
  if (options.production) {
189
225
  process.env.APP_ENV = 'production';
190
226
  }
@@ -205,8 +241,14 @@ module.exports = (cli) => {
205
241
  e2e
206
242
  .command('p-test')
207
243
  .option('--stop-on-error')
208
- .option('--concurrency [concurrency]', '', 3)
209
- .action(async (opts) => {
210
- await pTest(opts);
244
+ .option('--build')
245
+ .option('--concurrency [concurrency]', '', os.cpus().length)
246
+ .action(async (options) => {
247
+ process.env.__E2E__ = true;
248
+ if (options.build) {
249
+ process.env.APP_ENV = 'production';
250
+ await run('yarn', ['build']);
251
+ }
252
+ await pTest({ ...options, concurrency: 1 * options.concurrency });
211
253
  });
212
254
  };
@@ -3,7 +3,6 @@ const { resolve } = require('path');
3
3
  const pAll = require('p-all');
4
4
  const dotenv = require('dotenv');
5
5
  const fs = require('fs');
6
- const { Client } = require('pg');
7
6
  const glob = require('glob');
8
7
  const _ = require('lodash');
9
8
 
@@ -22,7 +21,7 @@ const config = {
22
21
  async function runApp(dir, index = 0) {
23
22
  // 一个进程需要占用两个端口? (一个是应用端口,一个是 socket 端口)
24
23
  index = index * 2;
25
-
24
+ const { Client } = require('pg');
26
25
  const database = `nocobase${index}`;
27
26
  const client = new Client({
28
27
  host: config['DB_HOST'],
@@ -35,7 +34,7 @@ async function runApp(dir, index = 0) {
35
34
  await client.query(`DROP DATABASE IF EXISTS "${database}"`);
36
35
  await client.query(`CREATE DATABASE "${database}";`);
37
36
  await client.end();
38
- return execa('yarn', ['nocobase', 'e2e', 'test', dir, '-x', '--skip-reporter'], {
37
+ return execa('yarn', ['nocobase', 'e2e', 'test', dir, '--skip-reporter'], {
39
38
  shell: true,
40
39
  stdio: 'inherit',
41
40
  env: {
@@ -47,23 +46,29 @@ async function runApp(dir, index = 0) {
47
46
  APP_ENV: 'production',
48
47
  APP_PORT: 20000 + index,
49
48
  DB_DATABASE: `nocobase${index}`,
50
- SOCKET_PATH: `storage/gateway-e2e-${index}.sock`,
51
- PM2_HOME: resolve(process.cwd(), `storage/.pm2-${index}`),
49
+ SOCKET_PATH: `storage/e2e/gateway-e2e-${index}.sock`,
50
+ PM2_HOME: resolve(process.cwd(), `storage/e2e/.pm2-${index}`),
52
51
  PLAYWRIGHT_AUTH_FILE: resolve(process.cwd(), `storage/playwright/.auth/admin-${index}.json`),
53
52
  },
54
53
  });
55
54
  }
56
55
 
57
56
  exports.pTest = async (options) => {
57
+ const dir = resolve(process.cwd(), 'storage/e2e');
58
+
59
+ if (!fs.existsSync(dir)) {
60
+ fs.mkdirSync(dir, { recursive: true });
61
+ }
62
+
58
63
  const files = glob.sync('packages/**/__e2e__/**/*.test.ts', {
59
64
  root: process.cwd(),
60
65
  });
61
66
 
62
- const commands = splitArrayIntoParts(files, options.concurrency || 3).map((v, i) => {
67
+ const commands = splitArrayIntoParts(_.shuffle(files), options.concurrency || 4).map((v, i) => {
63
68
  return () => runApp(v.join(' '), i);
64
69
  });
65
70
 
66
- await pAll(commands, { concurrency: 3, stopOnError: false, ...options });
71
+ await pAll(commands, { concurrency: 4, stopOnError: false, ...options });
67
72
  };
68
73
 
69
74
  function splitArrayIntoParts(array, parts) {
@@ -3,7 +3,7 @@ const { run, isDev, isPackageValid, generatePlaywrightPath } = require('../util'
3
3
  const { resolve } = require('path');
4
4
  const { existsSync } = require('fs');
5
5
  const { readFile, writeFile } = require('fs').promises;
6
- const { createStoragePluginsSymlink } = require('@nocobase/utils/plugin-symlink');
6
+ const { createStoragePluginsSymlink, createDevPluginsSymlink } = require('@nocobase/utils/plugin-symlink');
7
7
 
8
8
  /**
9
9
  * @param {Command} cli
@@ -13,12 +13,14 @@ module.exports = (cli) => {
13
13
  cli
14
14
  .command('postinstall')
15
15
  .allowUnknownOption()
16
- .action(async () => {
16
+ .option('--skip-umi')
17
+ .action(async (options) => {
17
18
  generatePlaywrightPath(true);
18
19
  await createStoragePluginsSymlink();
19
20
  if (!isDev()) {
20
21
  return;
21
22
  }
23
+ await createDevPluginsSymlink();
22
24
  const cwd = process.cwd();
23
25
  if (!existsSync(resolve(cwd, '.env')) && existsSync(resolve(cwd, '.env.example'))) {
24
26
  const content = await readFile(resolve(cwd, '.env.example'), 'utf-8');
@@ -31,11 +33,13 @@ module.exports = (cli) => {
31
33
  if (!isPackageValid('umi')) {
32
34
  return;
33
35
  }
34
- run('umi', ['generate', 'tmp'], {
35
- stdio: 'pipe',
36
- env: {
37
- APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
38
- },
39
- });
36
+ if (!options.skipUmi) {
37
+ run('umi', ['generate', 'tmp'], {
38
+ stdio: 'pipe',
39
+ env: {
40
+ APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
41
+ },
42
+ });
43
+ }
40
44
  });
41
45
  };
@@ -36,26 +36,26 @@ function addTestCommand(name, cli) {
36
36
  if (!opts.watch && !opts.run) {
37
37
  process.argv.push('--run');
38
38
  }
39
+ const first = paths?.[0];
40
+ if (!process.env.TEST_ENV && first) {
41
+ const key = first.split(path.sep).join('/');
42
+ if (key.includes('/client/')) {
43
+ process.env.TEST_ENV = 'client-side';
44
+ } else {
45
+ process.env.TEST_ENV = 'server-side';
46
+ }
47
+ }
39
48
  if (process.env.TEST_ENV === 'server-side' && opts.singleThread !== 'false') {
40
49
  process.argv.push('--poolOptions.threads.singleThread=true');
41
50
  }
42
51
  if (opts.singleThread === 'false') {
43
52
  process.argv.splice(process.argv.indexOf('--single-thread=false'), 1);
44
53
  }
45
- const cliArgs = ['--max_old_space_size=4096', './node_modules/.bin/vitest', ...process.argv.slice(3)];
54
+ const cliArgs = ['--max_old_space_size=14096', './node_modules/.bin/vitest', ...process.argv.slice(3)];
46
55
  if (process.argv.includes('-h') || process.argv.includes('--help')) {
47
56
  await run('node', cliArgs);
48
57
  return;
49
58
  }
50
- const first = paths?.[0];
51
- if (!process.env.TEST_ENV && first) {
52
- const key = first.split(path.sep).join('/');
53
- if (key.includes('/client/')) {
54
- process.env.TEST_ENV = 'client-side';
55
- } else {
56
- process.env.TEST_ENV = 'server-side';
57
- }
58
- }
59
59
  if (process.env.TEST_ENV) {
60
60
  console.log('process.env.TEST_ENV', process.env.TEST_ENV, cliArgs);
61
61
  await run('node', cliArgs);
@@ -29,9 +29,10 @@ async function getProjectVersion() {
29
29
 
30
30
  class PluginGenerator extends Generator {
31
31
  constructor(options) {
32
- const { context = {}, ...opts } = options;
32
+ const { log, context = {}, ...opts } = options;
33
33
  super(opts);
34
34
  this.context = context;
35
+ this.log = log || console.log;
35
36
  }
36
37
 
37
38
  async getContext() {
@@ -51,20 +52,19 @@ class PluginGenerator extends Generator {
51
52
  const { name } = this.context;
52
53
  const target = resolve(process.cwd(), 'packages/plugins/', name);
53
54
  if (existsSync(target)) {
54
- console.log(chalk.red(`[${name}] plugin already exists.`));
55
+ this.log(chalk.red(`[${name}] plugin already exists.`));
55
56
  return;
56
57
  }
57
- console.log('Creating plugin');
58
+ this.log('Creating plugin');
58
59
  this.copyDirectory({
59
60
  target,
60
61
  context: await this.getContext(),
61
62
  path: join(__dirname, '../templates/plugin'),
62
63
  });
63
- console.log('');
64
+ this.log('');
64
65
  genTsConfigPaths();
65
- execa.sync('yarn', ['install'], { shell: true, stdio: 'inherit' });
66
- // execa.sync('yarn', ['build', `plugins/${name}`], { shell: true, stdio: 'inherit' });
67
- console.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`);
66
+ execa.sync('yarn', ['postinstall', '--skip-umi'], { shell: true, stdio: 'inherit' });
67
+ this.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`);
68
68
  }
69
69
  }
70
70
 
package/src/util.js CHANGED
@@ -245,6 +245,18 @@ function generatePlaywrightPath(clean = false) {
245
245
 
246
246
  exports.generatePlaywrightPath = generatePlaywrightPath;
247
247
 
248
+ function parseEnv(name) {
249
+ if (name === 'DB_UNDERSCORED') {
250
+ if (process.env.DB_UNDERSCORED === 'true') {
251
+ return 'true';
252
+ }
253
+ if (process.env.DB_UNDERSCORED) {
254
+ return 'true';
255
+ }
256
+ return 'false';
257
+ }
258
+ }
259
+
248
260
  exports.initEnv = function initEnv() {
249
261
  const env = {
250
262
  APP_ENV: 'development',
@@ -254,15 +266,21 @@ exports.initEnv = function initEnv() {
254
266
  DB_DIALECT: 'sqlite',
255
267
  DB_STORAGE: 'storage/db/nocobase.sqlite',
256
268
  DB_TIMEZONE: '+00:00',
269
+ DB_UNDERSCORED: parseEnv('DB_UNDERSCORED'),
257
270
  DEFAULT_STORAGE_TYPE: 'local',
258
271
  LOCAL_STORAGE_DEST: 'storage/uploads',
259
272
  PLUGIN_STORAGE_PATH: resolve(process.cwd(), 'storage/plugins'),
260
273
  MFSU_AD: 'none',
274
+ WS_PATH: '/ws',
275
+ SOCKET_PATH: 'storage/gateway.sock',
261
276
  NODE_MODULES_PATH: resolve(process.cwd(), 'node_modules'),
262
277
  PM2_HOME: resolve(process.cwd(), './storage/.pm2'),
263
278
  PLUGIN_PACKAGE_PREFIX: '@nocobase/plugin-,@nocobase/plugin-sample-,@nocobase/preset-',
264
279
  SERVER_TSCONFIG_PATH: './tsconfig.server.json',
265
280
  PLAYWRIGHT_AUTH_FILE: resolve(process.cwd(), 'storage/playwright/.auth/admin.json'),
281
+ CACHE_DEFAULT_STORE: 'memory',
282
+ CACHE_MEMORY_MAX: 2000,
283
+ LOGGER_BASE_PATH: 'storage/logs',
266
284
  };
267
285
 
268
286
  if (
@@ -1,13 +1,13 @@
1
- import { InstallOptions, Plugin } from '@nocobase/server';
1
+ import { Plugin } from '@nocobase/server';
2
2
 
3
3
  export class {{{pascalCaseName}}}Server extends Plugin {
4
- afterAdd() {}
4
+ async afterAdd() {}
5
5
 
6
- beforeLoad() {}
6
+ async beforeLoad() {}
7
7
 
8
8
  async load() {}
9
9
 
10
- async install(options?: InstallOptions) {}
10
+ async install() {}
11
11
 
12
12
  async afterEnable() {}
13
13