@nocobase/cli 0.17.0-alpha.7 → 0.18.0-alpha.8

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
@@ -1,85 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const dotenv = require('dotenv');
4
- const { resolve } = require('path');
5
- const fs = require('fs');
6
3
  const chalk = require('chalk');
7
- const { genTsConfigPaths } = require('../src/util');
8
-
9
- const env = {
10
- APP_ENV: 'development',
11
- APP_KEY: 'test-jwt-secret',
12
- APP_PORT: 13000,
13
- API_BASE_PATH: '/api/',
14
- DB_DIALECT: 'sqlite',
15
- DB_STORAGE: 'storage/db/nocobase.sqlite',
16
- DB_TIMEZONE: '+00:00',
17
- DEFAULT_STORAGE_TYPE: 'local',
18
- LOCAL_STORAGE_DEST: 'storage/uploads',
19
- PLUGIN_STORAGE_PATH: resolve(process.cwd(), 'storage/plugins'),
20
- MFSU_AD: 'none',
21
- NODE_MODULES_PATH: resolve(process.cwd(), 'node_modules'),
22
- PM2_HOME: resolve(process.cwd(), './storage/.pm2'),
23
- PLUGIN_PACKAGE_PREFIX: '@nocobase/plugin-,@nocobase/plugin-sample-,@nocobase/preset-',
24
- SERVER_TSCONFIG_PATH: './tsconfig.server.json',
25
- };
26
-
27
- if (!process.env.APP_ENV_PATH && process.argv[2] && process.argv[2] === 'test') {
28
- if (fs.existsSync(resolve(process.cwd(), '.env.test'))) {
29
- process.env.APP_ENV_PATH = '.env.test';
30
- }
31
- }
32
-
33
- if (process.argv[2] === 'e2e') {
34
- // 用于存放 playwright 自动生成的相关的文件
35
- if (!fs.existsSync('playwright')) {
36
- fs.mkdirSync('playwright');
37
- }
38
- if (!fs.existsSync('.env.e2e') && fs.existsSync('.env.e2e.example')) {
39
- const env = fs.readFileSync('.env.e2e.example');
40
- fs.writeFileSync('.env.e2e', env);
41
- }
42
- if (!fs.existsSync('.env.e2e')) {
43
- throw new Error('Please create .env.e2e file first!');
44
- }
45
- process.env.APP_ENV_PATH = '.env.e2e';
46
- }
4
+ const { initEnv, genTsConfigPaths } = require('../src/util');
47
5
 
6
+ initEnv();
48
7
  genTsConfigPaths();
49
8
 
50
- dotenv.config({
51
- path: resolve(process.cwd(), process.env.APP_ENV_PATH || '.env'),
52
- });
53
-
54
- for (const key in env) {
55
- if (!process.env[key]) {
56
- process.env[key] = env[key];
57
- }
58
- }
59
-
60
- if (process.argv[2] === 'e2e' && !process.env.APP_BASE_URL) {
61
- process.env.APP_BASE_URL = `http://127.0.0.1:${process.env.APP_PORT}`;
62
- }
63
-
64
9
  if (require('semver').satisfies(process.version, '<16')) {
65
10
  console.error(chalk.red('[nocobase cli]: Node.js version must be >= 16'));
66
11
  process.exit(1);
67
12
  }
68
13
 
69
- if (require('semver').satisfies(process.version, '>16') && !process.env.UNSET_NODE_OPTIONS) {
70
- if (process.env.NODE_OPTIONS) {
71
- let opts = process.env.NODE_OPTIONS;
72
- if (!opts.includes('--openssl-legacy-provider')) {
73
- opts = opts + ' --openssl-legacy-provider';
74
- }
75
- if (!opts.includes('--no-experimental-fetch')) {
76
- opts = opts + ' --no-experimental-fetch';
77
- }
78
- process.env.NODE_OPTIONS = opts;
79
- } else {
80
- process.env.NODE_OPTIONS = '--openssl-legacy-provider --no-experimental-fetch';
81
- }
82
- }
14
+ // if (require('semver').satisfies(process.version, '>16') && !process.env.UNSET_NODE_OPTIONS) {
15
+ // if (process.env.NODE_OPTIONS) {
16
+ // let opts = process.env.NODE_OPTIONS;
17
+ // if (!opts.includes('--openssl-legacy-provider')) {
18
+ // opts = opts + ' --openssl-legacy-provider';
19
+ // }
20
+ // if (!opts.includes('--no-experimental-fetch')) {
21
+ // opts = opts + ' --no-experimental-fetch';
22
+ // }
23
+ // process.env.NODE_OPTIONS = opts;
24
+ // } else {
25
+ // process.env.NODE_OPTIONS = '--openssl-legacy-provider --no-experimental-fetch';
26
+ // }
27
+ // }
83
28
 
84
29
  const cli = require('../src/cli');
85
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "0.17.0-alpha.7",
3
+ "version": "0.18.0-alpha.8",
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.17.0-alpha.7",
11
+ "@nocobase/app": "0.18.0-alpha.8",
12
12
  "@types/fs-extra": "^11.0.1",
13
13
  "@umijs/utils": "3.5.20",
14
14
  "chalk": "^4.1.1",
@@ -17,18 +17,19 @@
17
17
  "execa": "^5.1.1",
18
18
  "fast-glob": "^3.3.1",
19
19
  "fs-extra": "^11.1.1",
20
+ "p-all": "3.0.0",
20
21
  "pm2": "^5.2.0",
21
22
  "portfinder": "^1.0.28",
22
23
  "serve": "^13.0.2",
23
24
  "tsx": "^4.6.2"
24
25
  },
25
26
  "devDependencies": {
26
- "@nocobase/devtools": "0.17.0-alpha.7"
27
+ "@nocobase/devtools": "0.18.0-alpha.8"
27
28
  },
28
29
  "repository": {
29
30
  "type": "git",
30
31
  "url": "git+https://github.com/nocobase/nocobase.git",
31
32
  "directory": "packages/core/cli"
32
33
  },
33
- "gitHead": "91377a3abd1124f79fb3819f2d0e80905e091a32"
34
+ "gitHead": "727d42f6f14e5f863831da3dbf3255ba1165b567"
34
35
  }
@@ -2,6 +2,7 @@ const { Command } = require('commander');
2
2
  const { run, isPortReachable } = require('../util');
3
3
  const { execSync } = require('node:child_process');
4
4
  const axios = require('axios');
5
+ const { pTest } = require('./p-test');
5
6
 
6
7
  /**
7
8
  * 检查服务是否启动成功
@@ -96,13 +97,13 @@ const commonConfig = {
96
97
  const runCodegenSync = () => {
97
98
  try {
98
99
  execSync(
99
- `npx playwright codegen --load-storage=playwright/.auth/codegen.auth.json ${process.env.APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`,
100
+ `npx playwright codegen --load-storage=storage/playwright/.auth/codegen.auth.json ${process.env.APP_BASE_URL} --save-storage=storage/playwright/.auth/codegen.auth.json`,
100
101
  commonConfig,
101
102
  );
102
103
  } catch (err) {
103
104
  if (err.message.includes('auth.json')) {
104
105
  execSync(
105
- `npx playwright codegen ${process.env.APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`,
106
+ `npx playwright codegen ${process.env.APP_BASE_URL} --save-storage=storage/playwright/.auth/codegen.auth.json`,
106
107
  commonConfig,
107
108
  );
108
109
  } else {
@@ -196,4 +197,16 @@ module.exports = (cli) => {
196
197
  e2e.command('reinstall-app').action(async (options) => {
197
198
  await run('nocobase', ['install', '-f'], options);
198
199
  });
200
+
201
+ e2e.command('install-deps').action(async () => {
202
+ await run('npx', ['playwright', 'install', '--with-deps']);
203
+ });
204
+
205
+ e2e
206
+ .command('p-test')
207
+ .option('--stop-on-error')
208
+ .option('--concurrency [concurrency]', '', 3)
209
+ .action(async (opts) => {
210
+ await pTest(opts);
211
+ });
199
212
  };
@@ -12,10 +12,10 @@ module.exports = (cli) => {
12
12
  require('./tar')(cli);
13
13
  require('./dev')(cli);
14
14
  require('./start')(cli);
15
- require('./test')(cli);
16
15
  require('./e2e')(cli);
17
16
  require('./clean')(cli);
18
17
  require('./doc')(cli);
18
+ require('./test')(cli);
19
19
  require('./umi')(cli);
20
20
  require('./upgrade')(cli);
21
21
  require('./postinstall')(cli);
@@ -0,0 +1,68 @@
1
+ const execa = require('execa');
2
+ const { resolve, dirname } = require('path');
3
+ const pAll = require('p-all');
4
+ const dotenv = require('dotenv');
5
+ const fs = require('fs');
6
+ const { Client } = require('pg');
7
+ const glob = require('glob');
8
+
9
+ let ENV_FILE = resolve(process.cwd(), '.env.e2e');
10
+
11
+ if (!fs.existsSync(ENV_FILE)) {
12
+ ENV_FILE = resolve(process.cwd(), '.env.e2e.example');
13
+ }
14
+
15
+ const data = fs.readFileSync(ENV_FILE, 'utf-8');
16
+ const config = {
17
+ ...dotenv.parse(data),
18
+ ...process.env,
19
+ };
20
+
21
+ async function runApp(index = 1, dir) {
22
+ const database = `nocobase${index}`;
23
+ const client = new Client({
24
+ host: config['DB_HOST'],
25
+ port: Number(config['DB_PORT']),
26
+ user: config['DB_USER'],
27
+ password: config['DB_PASSWORD'],
28
+ database: 'postgres',
29
+ });
30
+ await client.connect();
31
+ await client.query(`DROP DATABASE IF EXISTS "${database}"`);
32
+ await client.query(`CREATE DATABASE "${database}";`);
33
+ await client.end();
34
+ return execa('yarn', ['nocobase', 'e2e', 'test', dir, '-x', '--skip-reporter'], {
35
+ shell: true,
36
+ stdio: 'inherit',
37
+ env: {
38
+ ...config,
39
+ CI: true,
40
+ __E2E__: true,
41
+ APP_BASE_URL: undefined,
42
+ LOGGER_LEVEL: 'error',
43
+ APP_ENV: 'production',
44
+ APP_PORT: 20000 + index,
45
+ DB_DATABASE: `nocobase${index}`,
46
+ SOCKET_PATH: `storage/gateway-e2e-${index}.sock`,
47
+ PM2_HOME: resolve(process.cwd(), `storage/.pm2-${index}`),
48
+ PLAYWRIGHT_AUTH_FILE: resolve(process.cwd(), `storage/playwright/.auth/admin-${index}.json`),
49
+ },
50
+ });
51
+ }
52
+
53
+ exports.pTest = async (options) => {
54
+ const files = glob.sync('packages/**/__e2e__/**/*.test.ts', {
55
+ root: process.cwd(),
56
+ });
57
+ const fileSet = new Set();
58
+
59
+ for (const file of files) {
60
+ fileSet.add(dirname(file));
61
+ }
62
+
63
+ const commands = [...fileSet.values()].map((v, i) => {
64
+ return () => runApp(i + 1, v);
65
+ });
66
+
67
+ await pAll(commands, { concurrency: 3, stopOnError: false, ...options });
68
+ };
@@ -1,5 +1,5 @@
1
1
  const { Command } = require('commander');
2
- const { run, isDev, isPackageValid } = require('../util');
2
+ 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;
@@ -14,6 +14,7 @@ module.exports = (cli) => {
14
14
  .command('postinstall')
15
15
  .allowUnknownOption()
16
16
  .action(async () => {
17
+ generatePlaywrightPath(true);
17
18
  await createStoragePluginsSymlink();
18
19
  if (!isDev()) {
19
20
  return;
@@ -1,30 +1,87 @@
1
1
  const { Command } = require('commander');
2
- const { nodeCheck, runAppCommand, promptForTs, genTsConfigPaths } = require('../util');
2
+ const { run } = require('../util');
3
+ const path = require('path');
3
4
 
4
5
  /**
5
6
  *
7
+ * @param {String} name
6
8
  * @param {Command} cli
7
9
  */
8
- module.exports = (cli) => {
9
- cli
10
- .command('test')
11
- .option('-c, --db-clean')
10
+ function addTestCommand(name, cli) {
11
+ return cli
12
+ .command(name)
13
+ .option('-w, --watch')
14
+ .option('--run')
15
+ .option('--allowOnly')
16
+ .option('--bail')
17
+ .option('-h, --help')
18
+ .option('--single-thread [singleThread]')
19
+ .arguments('[paths...]')
12
20
  .allowUnknownOption()
13
- .action(async (options) => {
14
- nodeCheck();
15
- if (options.dbClean) {
16
- promptForTs();
17
- await runAppCommand('db:clean', ['-y']);
18
- }
19
- let index = process.argv.indexOf('-c');
20
- if (index > 0) {
21
- process.argv.splice(index, 1);
22
- }
23
- index = process.argv.indexOf('--db-clean');
24
- if (index > 0) {
25
- process.argv.splice(index, 1);
26
- }
27
- process.argv.splice(2, 1, '-i');
28
- require('jest-cli/bin/jest');
21
+ .action(async (paths, opts) => {
22
+ if (name === 'test:server') {
23
+ process.env.TEST_ENV = 'server-side';
24
+ } else if (name === 'test:client') {
25
+ process.env.TEST_ENV = 'client-side';
26
+ }
27
+ if (opts.server) {
28
+ process.env.TEST_ENV = 'server-side';
29
+ process.argv.splice(process.argv.indexOf('--server'), 1);
30
+ }
31
+ if (opts.client) {
32
+ process.env.TEST_ENV = 'client-side';
33
+ process.argv.splice(process.argv.indexOf('--client'), 1);
34
+ }
35
+ process.env.NODE_ENV = 'test';
36
+ if (!opts.watch && !opts.run) {
37
+ process.argv.push('--run');
38
+ }
39
+ if (process.env.TEST_ENV === 'server-side' && opts.singleThread !== 'false') {
40
+ process.argv.push('--poolOptions.threads.singleThread=true');
41
+ }
42
+ if (opts.singleThread === 'false') {
43
+ process.argv.splice(process.argv.indexOf('--single-thread=false'), 1);
44
+ }
45
+ const cliArgs = ['--max_old_space_size=4096', './node_modules/.bin/vitest', ...process.argv.slice(3)];
46
+ if (process.argv.includes('-h') || process.argv.includes('--help')) {
47
+ await run('node', cliArgs);
48
+ return;
49
+ }
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
+ if (process.env.TEST_ENV) {
60
+ console.log('process.env.TEST_ENV', process.env.TEST_ENV, cliArgs);
61
+ await run('node', cliArgs);
62
+ } else {
63
+ await Promise.all([
64
+ run('node', cliArgs, {
65
+ env: {
66
+ TEST_ENV: 'client-side',
67
+ },
68
+ }),
69
+ run('node', cliArgs, {
70
+ env: {
71
+ TEST_ENV: 'server-side',
72
+ },
73
+ }),
74
+ ]);
75
+ }
29
76
  });
77
+ }
78
+
79
+ /**
80
+ *
81
+ * @param {Command} cli
82
+ */
83
+ module.exports = (cli) => {
84
+ addTestCommand('test:server', cli);
85
+ addTestCommand('test:client', cli);
86
+ addTestCommand('test', cli).option('--client').option('--server');
30
87
  };
package/src/util.js CHANGED
@@ -5,10 +5,12 @@ const fg = require('fast-glob');
5
5
  const { dirname, join, resolve, sep } = require('path');
6
6
  const { readFile, writeFile } = require('fs').promises;
7
7
  const { existsSync, mkdirSync, cpSync, writeFileSync } = require('fs');
8
+ const dotenv = require('dotenv');
9
+ const fs = require('fs');
8
10
 
9
- exports.isPackageValid = (package) => {
11
+ exports.isPackageValid = (pkg) => {
10
12
  try {
11
- require.resolve(package);
13
+ require.resolve(pkg);
12
14
  return true;
13
15
  } catch (error) {
14
16
  return false;
@@ -55,6 +57,10 @@ exports.nodeCheck = () => {
55
57
  };
56
58
 
57
59
  exports.run = (command, args, options = {}) => {
60
+ if (command === 'tsx') {
61
+ command = 'node';
62
+ args = ['./node_modules/tsx/dist/cli.mjs'].concat(args || []);
63
+ }
58
64
  return execa(command, args, {
59
65
  shell: true,
60
66
  stdio: 'inherit',
@@ -177,6 +183,17 @@ exports.generateAppDir = function generateAppDir() {
177
183
  };
178
184
 
179
185
  exports.genTsConfigPaths = function genTsConfigPaths() {
186
+ try {
187
+ fs.unlinkSync(resolve(process.cwd(), 'node_modules/.bin/tsx'));
188
+ fs.symlinkSync(
189
+ resolve(process.cwd(), 'node_modules/tsx/dist/cli.mjs'),
190
+ resolve(process.cwd(), 'node_modules/.bin/tsx'),
191
+ 'file',
192
+ );
193
+ } catch (error) {
194
+ //
195
+ }
196
+
180
197
  const cwd = process.cwd();
181
198
  const cwdLength = cwd.length;
182
199
  const paths = {
@@ -193,8 +210,16 @@ exports.genTsConfigPaths = function genTsConfigPaths() {
193
210
  .slice(cwdLength + 1)
194
211
  .split(sep)
195
212
  .join('/');
196
- paths[packageJsonName] = [`${relativePath}/src`];
197
213
  paths[`${packageJsonName}/client`] = [`${relativePath}/src/client`];
214
+ paths[`${packageJsonName}/package.json`] = [`${relativePath}/package.json`];
215
+ paths[packageJsonName] = [`${relativePath}/src`];
216
+ if (packageJsonName === '@nocobase/test') {
217
+ paths[`${packageJsonName}/server`] = [`${relativePath}/src/server`];
218
+ paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`];
219
+ }
220
+ if (packageJsonName === '@nocobase/plugin-workflow-test') {
221
+ paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`];
222
+ }
198
223
  });
199
224
 
200
225
  const tsConfigJsonPath = join(cwd, './tsconfig.paths.json');
@@ -202,3 +227,78 @@ exports.genTsConfigPaths = function genTsConfigPaths() {
202
227
  writeFileSync(tsConfigJsonPath, JSON.stringify(content, null, 2), 'utf-8');
203
228
  return content;
204
229
  };
230
+
231
+ function generatePlaywrightPath(clean = false) {
232
+ try {
233
+ const playwright = resolve(process.cwd(), 'storage/playwright/tests');
234
+ if (clean && fs.existsSync(playwright)) {
235
+ fs.rmSync(dirname(playwright), { force: true, recursive: true });
236
+ }
237
+ if (!fs.existsSync(playwright)) {
238
+ const testPkg = require.resolve('@nocobase/test/package.json');
239
+ fs.cpSync(resolve(dirname(testPkg), 'playwright/tests'), playwright, { recursive: true });
240
+ }
241
+ } catch (error) {
242
+ // empty
243
+ }
244
+ }
245
+
246
+ exports.generatePlaywrightPath = generatePlaywrightPath;
247
+
248
+ exports.initEnv = function initEnv() {
249
+ const env = {
250
+ APP_ENV: 'development',
251
+ APP_KEY: 'test-jwt-secret',
252
+ APP_PORT: 13000,
253
+ API_BASE_PATH: '/api/',
254
+ DB_DIALECT: 'sqlite',
255
+ DB_STORAGE: 'storage/db/nocobase.sqlite',
256
+ DB_TIMEZONE: '+00:00',
257
+ DEFAULT_STORAGE_TYPE: 'local',
258
+ LOCAL_STORAGE_DEST: 'storage/uploads',
259
+ PLUGIN_STORAGE_PATH: resolve(process.cwd(), 'storage/plugins'),
260
+ MFSU_AD: 'none',
261
+ NODE_MODULES_PATH: resolve(process.cwd(), 'node_modules'),
262
+ PM2_HOME: resolve(process.cwd(), './storage/.pm2'),
263
+ PLUGIN_PACKAGE_PREFIX: '@nocobase/plugin-,@nocobase/plugin-sample-,@nocobase/preset-',
264
+ SERVER_TSCONFIG_PATH: './tsconfig.server.json',
265
+ PLAYWRIGHT_AUTH_FILE: resolve(process.cwd(), 'storage/playwright/.auth/admin.json'),
266
+ };
267
+
268
+ if (
269
+ !process.env.APP_ENV_PATH &&
270
+ process.argv[2] &&
271
+ ['test', 'test:client', 'test:server'].includes(process.argv[2])
272
+ ) {
273
+ if (fs.existsSync(resolve(process.cwd(), '.env.test'))) {
274
+ process.env.APP_ENV_PATH = '.env.test';
275
+ }
276
+ }
277
+
278
+ if (!process.env.APP_ENV_PATH && process.argv[2] === 'e2e') {
279
+ // 用于存放 playwright 自动生成的相关的文件
280
+ generatePlaywrightPath();
281
+ if (!fs.existsSync('.env.e2e') && fs.existsSync('.env.e2e.example')) {
282
+ const env = fs.readFileSync('.env.e2e.example');
283
+ fs.writeFileSync('.env.e2e', env);
284
+ }
285
+ if (!fs.existsSync('.env.e2e')) {
286
+ throw new Error('Please create .env.e2e file first!');
287
+ }
288
+ process.env.APP_ENV_PATH = '.env.e2e';
289
+ }
290
+
291
+ dotenv.config({
292
+ path: resolve(process.cwd(), process.env.APP_ENV_PATH || '.env'),
293
+ });
294
+
295
+ if (process.argv[2] === 'e2e' && !process.env.APP_BASE_URL) {
296
+ process.env.APP_BASE_URL = `http://127.0.0.1:${process.env.APP_PORT}`;
297
+ }
298
+
299
+ for (const key in env) {
300
+ if (!process.env[key]) {
301
+ process.env[key] = env[key];
302
+ }
303
+ }
304
+ };