@nocobase/cli-v1 2.1.0-beta.34 → 2.1.0-beta.36
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 +5 -5
- package/src/__tests__/app-dev-utils.test.js +121 -0
- package/src/__tests__/build-index-html.test.js +83 -0
- package/src/__tests__/plugin-generator.test.js +43 -0
- package/src/commands/app-dev-plugin-server.js +181 -0
- package/src/commands/app-dev-utils.js +191 -0
- package/src/commands/app-dev.js +93 -0
- package/src/commands/build.js +0 -33
- package/src/commands/clean.js +2 -2
- package/src/commands/client.js +3 -2
- package/src/commands/dev.js +3 -2
- package/src/commands/e2e.js +4 -6
- package/src/commands/index.js +1 -0
- package/src/commands/instance-id.js +3 -4
- package/src/commands/locale.js +4 -5
- package/src/commands/p-test.js +5 -4
- package/src/commands/pkg.js +1 -1
- package/src/commands/start.js +20 -8
- package/src/commands/update-deps.js +11 -2
- package/src/commands/view-license-key.js +2 -3
- package/src/license.js +2 -2
- package/src/plugin-generator.js +5 -2
- package/src/util.js +20 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli-v1",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.36",
|
|
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-beta.
|
|
11
|
+
"@nocobase/cli": "2.1.0-beta.36",
|
|
12
12
|
"@nocobase/license-kit": "^0.3.8",
|
|
13
|
-
"@nocobase/utils": "2.1.0-beta.
|
|
13
|
+
"@nocobase/utils": "2.1.0-beta.36",
|
|
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-beta.
|
|
28
|
+
"@nocobase/devtools": "2.1.0-beta.36",
|
|
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": "
|
|
36
|
+
"gitHead": "397d45c744f6eb48b3a0cd785c87cbf1257c3513"
|
|
37
37
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
/* eslint-env jest */
|
|
11
|
+
|
|
12
|
+
const fs = require('fs-extra');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const {
|
|
16
|
+
buildAppDevServerArgs,
|
|
17
|
+
createPluginClientExternals,
|
|
18
|
+
getPluginClientModuleIds,
|
|
19
|
+
shouldUseAppDevServerSource,
|
|
20
|
+
toPosixPath,
|
|
21
|
+
} = require('../commands/app-dev-utils');
|
|
22
|
+
|
|
23
|
+
describe('cli-v1 app-dev utils', () => {
|
|
24
|
+
test('toPosixPath normalizes Windows paths for generated browser imports', () => {
|
|
25
|
+
expect(toPosixPath('C:\\Users\\tester\\app\\packages\\plugins\\demo\\src\\client\\index.tsx')).toBe(
|
|
26
|
+
'C:/Users/tester/app/packages/plugins/demo/src/client/index.tsx',
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('toPosixPath keeps POSIX paths unchanged', () => {
|
|
31
|
+
expect(toPosixPath('/Users/tester/app/packages/plugins/demo/src/client/index.tsx')).toBe(
|
|
32
|
+
'/Users/tester/app/packages/plugins/demo/src/client/index.tsx',
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('shouldUseAppDevServerSource requires app-dev and the app source entry', () => {
|
|
37
|
+
const appRoot = path.resolve('/app');
|
|
38
|
+
const appSourceEntry = path.resolve(appRoot, 'storage/.app-dev/src/index.ts');
|
|
39
|
+
const existsSync = (file) => file === appSourceEntry;
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
shouldUseAppDevServerSource({
|
|
43
|
+
cwd: appRoot,
|
|
44
|
+
env: {
|
|
45
|
+
APP_ENV: 'development',
|
|
46
|
+
APP_PACKAGE_ROOT: 'storage/.app-dev',
|
|
47
|
+
NOCOBASE_APP_DEV: 'true',
|
|
48
|
+
},
|
|
49
|
+
existsSync,
|
|
50
|
+
}),
|
|
51
|
+
).toBe(true);
|
|
52
|
+
expect(
|
|
53
|
+
shouldUseAppDevServerSource({
|
|
54
|
+
cwd: appRoot,
|
|
55
|
+
env: {
|
|
56
|
+
APP_ENV: 'production',
|
|
57
|
+
APP_PACKAGE_ROOT: 'storage/.app-dev',
|
|
58
|
+
NOCOBASE_APP_DEV: 'true',
|
|
59
|
+
},
|
|
60
|
+
existsSync,
|
|
61
|
+
}),
|
|
62
|
+
).toBe(false);
|
|
63
|
+
expect(
|
|
64
|
+
shouldUseAppDevServerSource({
|
|
65
|
+
cwd: appRoot,
|
|
66
|
+
env: {
|
|
67
|
+
APP_ENV: 'development',
|
|
68
|
+
APP_PACKAGE_ROOT: 'storage/.app-dev',
|
|
69
|
+
NOCOBASE_APP_DEV: '',
|
|
70
|
+
},
|
|
71
|
+
existsSync,
|
|
72
|
+
}),
|
|
73
|
+
).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('buildAppDevServerArgs runs the app source through tsx watch', () => {
|
|
77
|
+
expect(
|
|
78
|
+
buildAppDevServerArgs({
|
|
79
|
+
appPackageRoot: 'storage/.app-dev',
|
|
80
|
+
argv: ['node', 'nocobase-v1', 'start', '--launch-mode', 'direct'],
|
|
81
|
+
serverTsconfigPath: './tsconfig.server.json',
|
|
82
|
+
}),
|
|
83
|
+
).toEqual([
|
|
84
|
+
'watch',
|
|
85
|
+
`--ignore=${path.resolve(process.cwd(), 'storage/plugins')}/**`,
|
|
86
|
+
'--tsconfig',
|
|
87
|
+
'./tsconfig.server.json',
|
|
88
|
+
'-r',
|
|
89
|
+
'tsconfig-paths/register',
|
|
90
|
+
path.join('storage/.app-dev', 'src/index.ts'),
|
|
91
|
+
'start',
|
|
92
|
+
'--launch-mode',
|
|
93
|
+
'direct',
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('getPluginClientModuleIds discovers plugin client marker files', () => {
|
|
98
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'nocobase-app-dev-modules-'));
|
|
99
|
+
const localPluginDir = path.join(cwd, 'packages/plugins/@nocobase/plugin-local');
|
|
100
|
+
const remotePluginDir = path.join(cwd, 'node_modules/@nocobase/plugin-remote');
|
|
101
|
+
fs.ensureDirSync(localPluginDir);
|
|
102
|
+
fs.ensureDirSync(remotePluginDir);
|
|
103
|
+
fs.writeJsonSync(path.join(localPluginDir, 'package.json'), { name: '@nocobase/plugin-local' });
|
|
104
|
+
fs.writeFileSync(path.join(localPluginDir, 'client.js'), '');
|
|
105
|
+
fs.writeJsonSync(path.join(remotePluginDir, 'package.json'), { name: '@nocobase/plugin-remote' });
|
|
106
|
+
fs.writeFileSync(path.join(remotePluginDir, 'client-v2.js'), '');
|
|
107
|
+
|
|
108
|
+
expect(getPluginClientModuleIds({ cwd }).sort()).toEqual([
|
|
109
|
+
'@nocobase/plugin-local/client',
|
|
110
|
+
'@nocobase/plugin-remote/client-v2',
|
|
111
|
+
]);
|
|
112
|
+
fs.removeSync(cwd);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('createPluginClientExternals maps plugin client imports to app-dev modules', () => {
|
|
116
|
+
expect(createPluginClientExternals(['@nocobase/plugin-demo/client'])).toEqual({
|
|
117
|
+
'@nocobase/plugin-demo/client':
|
|
118
|
+
'window.__nocobase_app_dev_plugins__ && window.__nocobase_app_dev_plugins__["@nocobase/plugin-demo/client"]',
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
/* eslint-env jest */
|
|
11
|
+
|
|
12
|
+
const fs = require('fs-extra');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { buildIndexHtml } = require('../util');
|
|
16
|
+
|
|
17
|
+
function createAppPackageRoot() {
|
|
18
|
+
const appRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nocobase-app-html-'));
|
|
19
|
+
const clientDir = path.join(appRoot, 'dist/client');
|
|
20
|
+
fs.ensureDirSync(clientDir);
|
|
21
|
+
fs.writeFileSync(path.join(clientDir, 'index.html'), '<html></html>', 'utf-8');
|
|
22
|
+
fs.writeFileSync(
|
|
23
|
+
path.join(clientDir, 'index.html.tpl'),
|
|
24
|
+
[
|
|
25
|
+
"window['__nocobase_app_dev__'] = {{env.NOCOBASE_APP_DEV}};",
|
|
26
|
+
"window['__nocobase_public_path__'] = '{{env.APP_PUBLIC_PATH}}';",
|
|
27
|
+
].join('\n'),
|
|
28
|
+
'utf-8',
|
|
29
|
+
);
|
|
30
|
+
return appRoot;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('cli-v1 buildIndexHtml', () => {
|
|
34
|
+
const originalArgv = process.argv.slice();
|
|
35
|
+
const originalEnv = { ...process.env };
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
process.argv = originalArgv.slice();
|
|
39
|
+
process.env = { ...originalEnv };
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('marks app-dev HTML when running the app-dev command before child env is injected', () => {
|
|
43
|
+
const appRoot = createAppPackageRoot();
|
|
44
|
+
process.argv = ['node', 'nocobase-v1', 'app-dev'];
|
|
45
|
+
process.env.APP_PACKAGE_ROOT = appRoot;
|
|
46
|
+
process.env.APP_PUBLIC_PATH = '/';
|
|
47
|
+
process.env.NOCOBASE_APP_DEV = '';
|
|
48
|
+
|
|
49
|
+
buildIndexHtml();
|
|
50
|
+
|
|
51
|
+
const html = fs.readFileSync(path.join(appRoot, 'dist/client/index.html'), 'utf-8');
|
|
52
|
+
expect(html).toContain("window['__nocobase_app_dev__'] = true;");
|
|
53
|
+
fs.removeSync(appRoot);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('marks app-dev HTML when running a child command with app-dev env', () => {
|
|
57
|
+
const appRoot = createAppPackageRoot();
|
|
58
|
+
process.argv = ['node', 'nocobase-v1', 'start'];
|
|
59
|
+
process.env.APP_PACKAGE_ROOT = appRoot;
|
|
60
|
+
process.env.APP_PUBLIC_PATH = '/';
|
|
61
|
+
process.env.NOCOBASE_APP_DEV = 'true';
|
|
62
|
+
|
|
63
|
+
buildIndexHtml();
|
|
64
|
+
|
|
65
|
+
const html = fs.readFileSync(path.join(appRoot, 'dist/client/index.html'), 'utf-8');
|
|
66
|
+
expect(html).toContain("window['__nocobase_app_dev__'] = true;");
|
|
67
|
+
fs.removeSync(appRoot);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('does not mark normal start HTML as app-dev', () => {
|
|
71
|
+
const appRoot = createAppPackageRoot();
|
|
72
|
+
process.argv = ['node', 'nocobase-v1', 'start'];
|
|
73
|
+
process.env.APP_PACKAGE_ROOT = appRoot;
|
|
74
|
+
process.env.APP_PUBLIC_PATH = '/';
|
|
75
|
+
process.env.NOCOBASE_APP_DEV = '';
|
|
76
|
+
|
|
77
|
+
buildIndexHtml();
|
|
78
|
+
|
|
79
|
+
const html = fs.readFileSync(path.join(appRoot, 'dist/client/index.html'), 'utf-8');
|
|
80
|
+
expect(html).toContain("window['__nocobase_app_dev__'] = false;");
|
|
81
|
+
fs.removeSync(appRoot);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
/* eslint-env jest */
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { PluginGenerator } = require('../plugin-generator');
|
|
14
|
+
|
|
15
|
+
describe('cli-v1 plugin generator', () => {
|
|
16
|
+
test('uses cwd as baseDir when baseDir is not provided', () => {
|
|
17
|
+
const cwd = path.resolve('/tmp/nocobase-plugin-generator');
|
|
18
|
+
const generator = new PluginGenerator({
|
|
19
|
+
cwd,
|
|
20
|
+
args: {},
|
|
21
|
+
context: {
|
|
22
|
+
name: '@nocobase/plugin-demo',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
expect(generator.baseDir).toBe(cwd);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('prefers explicit baseDir over cwd', () => {
|
|
30
|
+
const baseDir = path.resolve('/tmp/nocobase-plugin-generator-base');
|
|
31
|
+
const cwd = path.resolve('/tmp/nocobase-plugin-generator-cwd');
|
|
32
|
+
const generator = new PluginGenerator({
|
|
33
|
+
baseDir,
|
|
34
|
+
cwd,
|
|
35
|
+
args: {},
|
|
36
|
+
context: {
|
|
37
|
+
name: '@nocobase/plugin-demo',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(generator.baseDir).toBe(baseDir);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
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 path = require('path');
|
|
11
|
+
const { createRsbuild } = require('@rsbuild/core');
|
|
12
|
+
const { pluginLess } = require('@rsbuild/plugin-less');
|
|
13
|
+
const { pluginReact } = require('@rsbuild/plugin-react');
|
|
14
|
+
const { pluginSvgr } = require('@rsbuild/plugin-svgr');
|
|
15
|
+
const {
|
|
16
|
+
createPluginClientExternals,
|
|
17
|
+
discoverLocalPluginEntries,
|
|
18
|
+
getPluginClientModuleIds,
|
|
19
|
+
writePluginDevEntryFiles,
|
|
20
|
+
} = require('./app-dev-utils');
|
|
21
|
+
const { storagePathJoin } = require('../util');
|
|
22
|
+
|
|
23
|
+
const appDevExternalDeps = [
|
|
24
|
+
'react',
|
|
25
|
+
'react-dom',
|
|
26
|
+
'react/jsx-runtime',
|
|
27
|
+
'react-router',
|
|
28
|
+
'react-router-dom',
|
|
29
|
+
'antd',
|
|
30
|
+
'antd-style',
|
|
31
|
+
'@ant-design/icons',
|
|
32
|
+
'@ant-design/cssinjs',
|
|
33
|
+
'i18next',
|
|
34
|
+
'react-i18next',
|
|
35
|
+
'@formily/antd-v5',
|
|
36
|
+
'@formily/core',
|
|
37
|
+
'@formily/json-schema',
|
|
38
|
+
'@formily/path',
|
|
39
|
+
'@formily/react',
|
|
40
|
+
'@formily/reactive',
|
|
41
|
+
'@formily/reactive-react',
|
|
42
|
+
'@formily/shared',
|
|
43
|
+
'@formily/validator',
|
|
44
|
+
'@dnd-kit/core',
|
|
45
|
+
'@dnd-kit/sortable',
|
|
46
|
+
'@nocobase/client',
|
|
47
|
+
'@nocobase/client/client',
|
|
48
|
+
'@nocobase/client-v2',
|
|
49
|
+
'@nocobase/client-v2/client-v2',
|
|
50
|
+
'@nocobase/evaluators',
|
|
51
|
+
'@nocobase/evaluators/client',
|
|
52
|
+
'@nocobase/flow-engine',
|
|
53
|
+
'@nocobase/sdk',
|
|
54
|
+
'@nocobase/utils',
|
|
55
|
+
'@nocobase/utils/client',
|
|
56
|
+
'@emotion/css',
|
|
57
|
+
'ahooks',
|
|
58
|
+
'axios',
|
|
59
|
+
'dayjs',
|
|
60
|
+
'file-saver',
|
|
61
|
+
'lodash',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
function createExternals() {
|
|
65
|
+
return appDevExternalDeps.reduce((memo, dep) => {
|
|
66
|
+
memo[dep] = `window.__nocobase_app_dev_deps__ && window.__nocobase_app_dev_deps__[${JSON.stringify(dep)}]`;
|
|
67
|
+
return memo;
|
|
68
|
+
}, {});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
const cwd = process.cwd();
|
|
73
|
+
const port = Number(process.env.NOCOBASE_APP_DEV_PLUGIN_PORT || 14100);
|
|
74
|
+
const entryDir = storagePathJoin('.app-dev', 'plugin-dev', 'entries');
|
|
75
|
+
const outDir = storagePathJoin('.app-dev', 'plugin-dev', 'dist');
|
|
76
|
+
const entries = discoverLocalPluginEntries({ cwd, port });
|
|
77
|
+
const externals = {
|
|
78
|
+
...createExternals(),
|
|
79
|
+
...createPluginClientExternals(getPluginClientModuleIds({ cwd })),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (!entries.length) {
|
|
83
|
+
console.log('[app-dev] no local plugin client entries found');
|
|
84
|
+
await new Promise(() => {});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const rsbuildEntries = await writePluginDevEntryFiles(entries, entryDir);
|
|
89
|
+
const rsbuild = await createRsbuild({
|
|
90
|
+
cwd,
|
|
91
|
+
rsbuildConfig: {
|
|
92
|
+
plugins: [pluginReact(), pluginLess(), pluginSvgr()],
|
|
93
|
+
source: {
|
|
94
|
+
entry: Object.fromEntries(
|
|
95
|
+
Object.entries(rsbuildEntries).map(([name, entryFile]) => [
|
|
96
|
+
name,
|
|
97
|
+
{
|
|
98
|
+
import: entryFile,
|
|
99
|
+
html: false,
|
|
100
|
+
},
|
|
101
|
+
]),
|
|
102
|
+
),
|
|
103
|
+
tsconfigPath: path.resolve(cwd, 'tsconfig.json'),
|
|
104
|
+
decorators: {
|
|
105
|
+
version: 'legacy',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
output: {
|
|
109
|
+
target: 'web',
|
|
110
|
+
distPath: {
|
|
111
|
+
root: outDir,
|
|
112
|
+
js: '.',
|
|
113
|
+
jsAsync: '.',
|
|
114
|
+
css: '.',
|
|
115
|
+
cssAsync: '.',
|
|
116
|
+
svg: '.',
|
|
117
|
+
font: '.',
|
|
118
|
+
image: '.',
|
|
119
|
+
media: '.',
|
|
120
|
+
assets: '.',
|
|
121
|
+
},
|
|
122
|
+
filename: {
|
|
123
|
+
js: '[name].js',
|
|
124
|
+
css: '[name].css',
|
|
125
|
+
svg: '[name][ext][query]',
|
|
126
|
+
font: '[name][ext][query]',
|
|
127
|
+
image: '[name][ext][query]',
|
|
128
|
+
media: '[name][ext][query]',
|
|
129
|
+
assets: '[name][ext][query]',
|
|
130
|
+
},
|
|
131
|
+
assetPrefix: `http://localhost:${port}/`,
|
|
132
|
+
cleanDistPath: true,
|
|
133
|
+
sourceMap: {
|
|
134
|
+
js: 'eval-cheap-module-source-map',
|
|
135
|
+
css: false,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
server: {
|
|
139
|
+
host: '0.0.0.0',
|
|
140
|
+
port,
|
|
141
|
+
cors: true,
|
|
142
|
+
},
|
|
143
|
+
dev: {
|
|
144
|
+
hmr: false,
|
|
145
|
+
liveReload: true,
|
|
146
|
+
assetPrefix: `http://localhost:${port}/`,
|
|
147
|
+
lazyCompilation: false,
|
|
148
|
+
client: {
|
|
149
|
+
port,
|
|
150
|
+
protocol: 'ws',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
tools: {
|
|
154
|
+
rspack(config) {
|
|
155
|
+
config.output = config.output || {};
|
|
156
|
+
config.output.library = { type: 'module' };
|
|
157
|
+
config.output.module = true;
|
|
158
|
+
config.output.chunkFormat = 'module';
|
|
159
|
+
config.output.chunkLoading = 'import';
|
|
160
|
+
config.output.workerChunkLoading = 'import';
|
|
161
|
+
config.experiments = {
|
|
162
|
+
...config.experiments,
|
|
163
|
+
outputModule: true,
|
|
164
|
+
};
|
|
165
|
+
config.externalsType = 'var';
|
|
166
|
+
config.externals = {
|
|
167
|
+
...(config.externals || {}),
|
|
168
|
+
...externals,
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await rsbuild.startDevServer();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
main().catch((error) => {
|
|
179
|
+
console.error(error);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
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 fs = require('fs-extra');
|
|
11
|
+
const fg = require('fast-glob');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { resolvePluginStoragePath } = require('../util');
|
|
14
|
+
|
|
15
|
+
const pluginClientLanes = {
|
|
16
|
+
client: {
|
|
17
|
+
rootEntryFile: 'client.js',
|
|
18
|
+
sourceDir: 'client',
|
|
19
|
+
},
|
|
20
|
+
'client-v2': {
|
|
21
|
+
rootEntryFile: 'client-v2.js',
|
|
22
|
+
sourceDir: 'client-v2',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function toPosixPath(filePath) {
|
|
27
|
+
return filePath.replace(/\\/g, '/');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getEntryName(packageName, lane) {
|
|
31
|
+
return `${packageName.replace(/[^a-zA-Z0-9_]/g, '_')}__${lane.replace(/[^a-zA-Z0-9_]/g, '_')}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readPackageName(packageJsonPath) {
|
|
35
|
+
try {
|
|
36
|
+
return fs.readJsonSync(packageJsonPath).name;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function findLocalPluginPackageJsons(cwd = process.cwd()) {
|
|
43
|
+
return fg.sync(['packages/plugins/*/package.json', 'packages/plugins/@*/*/package.json'], {
|
|
44
|
+
cwd,
|
|
45
|
+
absolute: true,
|
|
46
|
+
onlyFiles: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function findPluginClientPackageJsons(cwd = process.cwd()) {
|
|
51
|
+
return fg.sync(
|
|
52
|
+
[
|
|
53
|
+
'packages/plugins/*/package.json',
|
|
54
|
+
'packages/plugins/@*/*/package.json',
|
|
55
|
+
'node_modules/@nocobase/plugin-*/package.json',
|
|
56
|
+
'node_modules/@nocobase/preset-*/package.json',
|
|
57
|
+
],
|
|
58
|
+
{
|
|
59
|
+
cwd,
|
|
60
|
+
absolute: true,
|
|
61
|
+
onlyFiles: true,
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getPluginClientModuleIds({ cwd = process.cwd(), packageJsonPaths = findPluginClientPackageJsons(cwd) } = {}) {
|
|
67
|
+
const moduleIds = new Set();
|
|
68
|
+
|
|
69
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
70
|
+
const pluginDir = path.dirname(packageJsonPath);
|
|
71
|
+
const packageName = readPackageName(packageJsonPath);
|
|
72
|
+
if (!packageName) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const [lane, config] of Object.entries(pluginClientLanes)) {
|
|
77
|
+
if (fs.existsSync(path.join(pluginDir, config.rootEntryFile))) {
|
|
78
|
+
moduleIds.add(`${packageName}/${lane}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [...moduleIds];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createPluginClientExternals(moduleIds) {
|
|
87
|
+
return moduleIds.reduce((memo, moduleId) => {
|
|
88
|
+
memo[moduleId] = `window.__nocobase_app_dev_plugins__ && window.__nocobase_app_dev_plugins__[${JSON.stringify(
|
|
89
|
+
moduleId,
|
|
90
|
+
)}]`;
|
|
91
|
+
return memo;
|
|
92
|
+
}, {});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function discoverLocalPluginEntries({ cwd = process.cwd(), port } = {}) {
|
|
96
|
+
const entries = [];
|
|
97
|
+
|
|
98
|
+
for (const packageJsonPath of findLocalPluginPackageJsons(cwd)) {
|
|
99
|
+
const pluginDir = path.dirname(packageJsonPath);
|
|
100
|
+
const packageJson = fs.readJsonSync(packageJsonPath);
|
|
101
|
+
const packageName = packageJson.name;
|
|
102
|
+
if (!packageName) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const [lane, config] of Object.entries(pluginClientLanes)) {
|
|
107
|
+
const rootEntry = path.join(pluginDir, config.rootEntryFile);
|
|
108
|
+
const sourceEntry = fg.sync('index.{ts,tsx,js,jsx}', {
|
|
109
|
+
cwd: path.join(pluginDir, 'src', config.sourceDir),
|
|
110
|
+
absolute: true,
|
|
111
|
+
onlyFiles: true,
|
|
112
|
+
})[0];
|
|
113
|
+
|
|
114
|
+
if (!fs.existsSync(rootEntry) || !sourceEntry) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const entryName = getEntryName(packageName, lane);
|
|
119
|
+
entries.push({
|
|
120
|
+
packageName,
|
|
121
|
+
lane,
|
|
122
|
+
entryName,
|
|
123
|
+
sourceEntry,
|
|
124
|
+
url: port ? `http://localhost:${port}/${entryName}.js` : '',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return entries;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildPluginDevUrlMap(entries) {
|
|
133
|
+
return entries.reduce((memo, item) => {
|
|
134
|
+
memo[item.packageName] = memo[item.packageName] || {};
|
|
135
|
+
memo[item.packageName][item.lane] = item.url;
|
|
136
|
+
return memo;
|
|
137
|
+
}, {});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function shouldUseAppDevServerSource({ env = process.env, cwd = process.cwd(), existsSync = fs.existsSync } = {}) {
|
|
141
|
+
if (env.NOCOBASE_APP_DEV !== 'true' || env.APP_ENV === 'production' || !env.APP_PACKAGE_ROOT) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return existsSync(path.resolve(cwd, env.APP_PACKAGE_ROOT, 'src/index.ts'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildAppDevServerArgs({
|
|
149
|
+
appPackageRoot = process.env.APP_PACKAGE_ROOT,
|
|
150
|
+
argv = process.argv,
|
|
151
|
+
serverTsconfigPath = process.env.SERVER_TSCONFIG_PATH,
|
|
152
|
+
} = {}) {
|
|
153
|
+
const args = ['watch', `--ignore=${resolvePluginStoragePath()}/**`];
|
|
154
|
+
|
|
155
|
+
if (serverTsconfigPath) {
|
|
156
|
+
args.push('--tsconfig', serverTsconfigPath);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
args.push('-r', 'tsconfig-paths/register', path.join(appPackageRoot, 'src/index.ts'), ...argv.slice(2));
|
|
160
|
+
return args;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function writePluginDevEntryFiles(entries, entryDir) {
|
|
164
|
+
await fs.ensureDir(entryDir);
|
|
165
|
+
await fs.emptyDir(entryDir);
|
|
166
|
+
|
|
167
|
+
const rsbuildEntries = {};
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const entryFile = path.join(entryDir, `${entry.entryName}.tsx`);
|
|
170
|
+
const sourceEntry = toPosixPath(entry.sourceEntry);
|
|
171
|
+
await fs.writeFile(
|
|
172
|
+
entryFile,
|
|
173
|
+
[`export { default } from '${sourceEntry}';`, `export * from '${sourceEntry}';`, ''].join('\n'),
|
|
174
|
+
'utf-8',
|
|
175
|
+
);
|
|
176
|
+
rsbuildEntries[entry.entryName] = entryFile;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return rsbuildEntries;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = {
|
|
183
|
+
buildAppDevServerArgs,
|
|
184
|
+
buildPluginDevUrlMap,
|
|
185
|
+
createPluginClientExternals,
|
|
186
|
+
discoverLocalPluginEntries,
|
|
187
|
+
getPluginClientModuleIds,
|
|
188
|
+
shouldUseAppDevServerSource,
|
|
189
|
+
toPosixPath,
|
|
190
|
+
writePluginDevEntryFiles,
|
|
191
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
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 path = require('path');
|
|
11
|
+
const { getPortPromise } = require('portfinder');
|
|
12
|
+
const { buildPluginDevUrlMap, discoverLocalPluginEntries } = require('./app-dev-utils');
|
|
13
|
+
const { run, runWithPrefix } = require('../util');
|
|
14
|
+
|
|
15
|
+
function hasArg(args, name) {
|
|
16
|
+
return args.some((item) => item === name || item.startsWith(`${name}=`));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function omitOptionWithValue(args, name) {
|
|
20
|
+
const nextArgs = [];
|
|
21
|
+
for (let index = 0; index < args.length; index++) {
|
|
22
|
+
const item = args[index];
|
|
23
|
+
if (item === name) {
|
|
24
|
+
index += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (item.startsWith(`${name}=`)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
nextArgs.push(item);
|
|
31
|
+
}
|
|
32
|
+
return nextArgs;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {import('commander').Command} cli
|
|
38
|
+
*/
|
|
39
|
+
module.exports = (cli) => {
|
|
40
|
+
cli
|
|
41
|
+
.command('app-dev')
|
|
42
|
+
.description('Run the published app shell with local plugin development support')
|
|
43
|
+
.option('--plugin-port [port]', 'local plugin dev server port')
|
|
44
|
+
.allowUnknownOption()
|
|
45
|
+
.action(async (opts) => {
|
|
46
|
+
const passthroughArgs = omitOptionWithValue(process.argv.slice(3), '--plugin-port');
|
|
47
|
+
const pluginPort = opts.pluginPort
|
|
48
|
+
? Number(opts.pluginPort)
|
|
49
|
+
: await getPortPromise({ port: Number(process.env.NOCOBASE_APP_DEV_PLUGIN_PORT || 14100) });
|
|
50
|
+
const entries = discoverLocalPluginEntries({ cwd: process.cwd(), port: pluginPort });
|
|
51
|
+
const pluginDevUrlMap = buildPluginDevUrlMap(entries);
|
|
52
|
+
let pluginDevServer;
|
|
53
|
+
|
|
54
|
+
const sharedEnv = {
|
|
55
|
+
NOCOBASE_APP_DEV: 'true',
|
|
56
|
+
NOCOBASE_APP_DEV_PLUGIN_PORT: `${pluginPort}`,
|
|
57
|
+
NOCOBASE_APP_DEV_PLUGIN_URLS: JSON.stringify(pluginDevUrlMap),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (entries.length) {
|
|
61
|
+
pluginDevServer = runWithPrefix('node', [path.resolve(__dirname, './app-dev-plugin-server.js')], {
|
|
62
|
+
prefix: 'plugin-dev',
|
|
63
|
+
color: 'green',
|
|
64
|
+
env: sharedEnv,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const cleanup = () => {
|
|
69
|
+
pluginDevServer?.kill('SIGTERM');
|
|
70
|
+
};
|
|
71
|
+
process.once('exit', cleanup);
|
|
72
|
+
process.once('SIGINT', () => {
|
|
73
|
+
cleanup();
|
|
74
|
+
process.exit(130);
|
|
75
|
+
});
|
|
76
|
+
process.once('SIGTERM', () => {
|
|
77
|
+
cleanup();
|
|
78
|
+
process.exit(143);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const startArgs = ['start'];
|
|
82
|
+
if (!hasArg(passthroughArgs, '--launch-mode')) {
|
|
83
|
+
startArgs.push('--launch-mode', 'direct');
|
|
84
|
+
}
|
|
85
|
+
startArgs.push(...passthroughArgs);
|
|
86
|
+
|
|
87
|
+
await run('nocobase-v1', startArgs, {
|
|
88
|
+
env: {
|
|
89
|
+
...sharedEnv,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
};
|
package/src/commands/build.js
CHANGED
|
@@ -7,34 +7,10 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const { existsSync } = require('fs');
|
|
11
10
|
const { resolve } = require('path');
|
|
12
11
|
const { Command } = require('commander');
|
|
13
12
|
const { run, nodeCheck, isPackageValid, buildIndexHtml } = require('../util');
|
|
14
13
|
|
|
15
|
-
async function buildClientV2() {
|
|
16
|
-
const configPath = resolve(process.env.APP_PACKAGE_ROOT, 'client-v2/rsbuild.config.ts');
|
|
17
|
-
if (!existsSync(configPath)) {
|
|
18
|
-
console.log(`client-v2 config not found: ${configPath}`);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
await run('rsbuild', ['build', '--config', configPath], {
|
|
22
|
-
env: {
|
|
23
|
-
...process.env,
|
|
24
|
-
APP_ENV: 'production',
|
|
25
|
-
NODE_ENV: 'production',
|
|
26
|
-
API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
|
|
27
|
-
API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
|
|
28
|
-
API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
|
|
29
|
-
API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
|
|
30
|
-
WEBSOCKET_URL: process.env.WEBSOCKET_URL || '',
|
|
31
|
-
WS_PATH: process.env.WS_PATH,
|
|
32
|
-
ESM_CDN_BASE_URL: process.env.ESM_CDN_BASE_URL || 'https://esm.sh',
|
|
33
|
-
ESM_CDN_SUFFIX: process.env.ESM_CDN_SUFFIX || '',
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
14
|
/**
|
|
39
15
|
*
|
|
40
16
|
* @param {Command} cli
|
|
@@ -50,17 +26,11 @@ module.exports = (cli) => {
|
|
|
50
26
|
.option('-w, --watch', 'watch compile the @nocobase/build package')
|
|
51
27
|
.option('-s, --sourcemap', 'generate sourcemap')
|
|
52
28
|
.option('--no-dts', 'not generate dts')
|
|
53
|
-
.option('--client-v2-only', 'build client-v2 shell only')
|
|
54
29
|
.action(async (pkgs, options) => {
|
|
55
30
|
nodeCheck();
|
|
56
31
|
process.env['VITE_CJS_IGNORE_WARNING'] = 'true';
|
|
57
32
|
process.env.APP_ENV = 'production';
|
|
58
33
|
|
|
59
|
-
if (options.clientV2Only) {
|
|
60
|
-
await buildClientV2();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
34
|
if (pkgs.length === 0) {
|
|
65
35
|
await run('nocobase-v1', ['clean', '--dist']);
|
|
66
36
|
}
|
|
@@ -80,8 +50,5 @@ module.exports = (cli) => {
|
|
|
80
50
|
options.retry ? '--retry' : '',
|
|
81
51
|
]);
|
|
82
52
|
buildIndexHtml(true);
|
|
83
|
-
if (!pkgs.includes('@nocobase/app')) {
|
|
84
|
-
await buildClientV2();
|
|
85
|
-
}
|
|
86
53
|
});
|
|
87
54
|
};
|
package/src/commands/clean.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const { Command } = require('commander');
|
|
12
|
-
const { run, isDev } = require('../util');
|
|
12
|
+
const { run, isDev, storagePathJoin } = require('../util');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
*
|
|
@@ -34,7 +34,7 @@ module.exports = (cli) => {
|
|
|
34
34
|
run('rimraf', ['-rf', ...distDirs]);
|
|
35
35
|
}
|
|
36
36
|
} else {
|
|
37
|
-
run('rimraf', ['-rf', '
|
|
37
|
+
run('rimraf', ['-rf', storagePathJoin('.app-dev')]);
|
|
38
38
|
run('rimraf', ['-rf', 'packages/*/*/{lib,esm,es,dist,node_modules}']);
|
|
39
39
|
run('rimraf', ['-rf', 'packages/*/@*/*/{lib,esm,es,dist,node_modules}']);
|
|
40
40
|
}
|
package/src/commands/client.js
CHANGED
|
@@ -11,6 +11,7 @@ const chalk = require('chalk');
|
|
|
11
11
|
const { Command } = require('commander');
|
|
12
12
|
const fs = require('fs-extra');
|
|
13
13
|
const { resolve } = require('path');
|
|
14
|
+
const { storagePathJoin } = require('../util');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* 复制主应用客户端文件
|
|
@@ -110,7 +111,7 @@ module.exports = (cli) => {
|
|
|
110
111
|
.allowUnknownOption()
|
|
111
112
|
.action(async () => {
|
|
112
113
|
const version = require('../../package.json').version;
|
|
113
|
-
const target =
|
|
114
|
+
const target = storagePathJoin('dist-client', version);
|
|
114
115
|
const mainClientSource = resolve(process.cwd(), 'node_modules/@nocobase/app/dist/client');
|
|
115
116
|
await copyMainClient(mainClientSource, target);
|
|
116
117
|
await copyPluginClients('packages/plugins', '@nocobase', target);
|
|
@@ -123,7 +124,7 @@ module.exports = (cli) => {
|
|
|
123
124
|
.allowUnknownOption()
|
|
124
125
|
.action(async () => {
|
|
125
126
|
const version = require('../../package.json').version;
|
|
126
|
-
const target =
|
|
127
|
+
const target = storagePathJoin('dist-client', version);
|
|
127
128
|
|
|
128
129
|
// 检查必要的环境变量
|
|
129
130
|
if (
|
package/src/commands/dev.js
CHANGED
|
@@ -24,6 +24,7 @@ const chokidar = require('chokidar');
|
|
|
24
24
|
const { uid } = require('@formily/shared');
|
|
25
25
|
const path = require('path');
|
|
26
26
|
const fs = require('fs');
|
|
27
|
+
const { resolvePluginStoragePath } = require('@nocobase/utils/plugin-symlink');
|
|
27
28
|
|
|
28
29
|
function sleep(ms = 1000) {
|
|
29
30
|
return new Promise((resolve) => {
|
|
@@ -198,7 +199,7 @@ module.exports = (cli) => {
|
|
|
198
199
|
};
|
|
199
200
|
|
|
200
201
|
if (shouldRunClient) {
|
|
201
|
-
const storagePluginPath =
|
|
202
|
+
const storagePluginPath = resolvePluginStoragePath();
|
|
202
203
|
const watcher = chokidar.watch(`${storagePluginPath}/**/*`, {
|
|
203
204
|
cwd: process.cwd(),
|
|
204
205
|
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
|
|
@@ -246,7 +247,7 @@ module.exports = (cli) => {
|
|
|
246
247
|
const argv = [
|
|
247
248
|
'watch',
|
|
248
249
|
...(inspect ? [`--inspect=${inspect === true ? 9229 : inspect}`] : []),
|
|
249
|
-
|
|
250
|
+
`--ignore=${resolvePluginStoragePath()}/**`,
|
|
250
251
|
'--tsconfig',
|
|
251
252
|
SERVER_TSCONFIG_PATH,
|
|
252
253
|
'-r',
|
package/src/commands/e2e.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const { Command } = require('commander');
|
|
11
|
-
const { run, isPortReachable, checkDBDialect } = require('../util');
|
|
11
|
+
const { run, isPortReachable, checkDBDialect, storagePathJoin } = require('../util');
|
|
12
12
|
const { execSync } = require('node:child_process');
|
|
13
13
|
const axios = require('axios');
|
|
14
14
|
const { pTest } = require('./p-test');
|
|
@@ -119,17 +119,15 @@ const commonConfig = {
|
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
const runCodegenSync = () => {
|
|
122
|
+
const authFile = storagePathJoin('playwright', '.auth', 'codegen.auth.json');
|
|
122
123
|
try {
|
|
123
124
|
execSync(
|
|
124
|
-
`npx playwright codegen --load-storage
|
|
125
|
+
`npx playwright codegen --load-storage=${authFile} ${process.env.APP_BASE_URL} --save-storage=${authFile}`,
|
|
125
126
|
commonConfig,
|
|
126
127
|
);
|
|
127
128
|
} catch (err) {
|
|
128
129
|
if (err.message.includes('auth.json')) {
|
|
129
|
-
execSync(
|
|
130
|
-
`npx playwright codegen ${process.env.APP_BASE_URL} --save-storage=storage/playwright/.auth/codegen.auth.json`,
|
|
131
|
-
commonConfig,
|
|
132
|
-
);
|
|
130
|
+
execSync(`npx playwright codegen ${process.env.APP_BASE_URL} --save-storage=${authFile}`, commonConfig);
|
|
133
131
|
} else {
|
|
134
132
|
console.error(err);
|
|
135
133
|
}
|
package/src/commands/index.js
CHANGED
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const { Command } = require('commander');
|
|
12
|
-
const { run, isDev } = require('../util');
|
|
12
|
+
const { run, isDev, storagePathJoin } = require('../util');
|
|
13
13
|
const { getInstanceIdAsync } = require('@nocobase/license-kit');
|
|
14
|
-
const path = require('path');
|
|
15
14
|
const fs = require('fs');
|
|
16
15
|
const { logger } = require('../logger');
|
|
17
16
|
|
|
@@ -26,8 +25,8 @@ module.exports = (cli) => {
|
|
|
26
25
|
.option('--force', 'Force generate InstanceID')
|
|
27
26
|
.action(async (options) => {
|
|
28
27
|
logger.info('Generating InstanceID...');
|
|
29
|
-
const dir =
|
|
30
|
-
const filePath =
|
|
28
|
+
const dir = storagePathJoin('.license');
|
|
29
|
+
const filePath = storagePathJoin('.license', 'instance-id');
|
|
31
30
|
if (fs.existsSync(filePath) && !options.force) {
|
|
32
31
|
logger.info('InstanceID already exists at ' + filePath);
|
|
33
32
|
return;
|
package/src/commands/locale.js
CHANGED
|
@@ -15,6 +15,7 @@ const _ = require('lodash');
|
|
|
15
15
|
const deepmerge = require('deepmerge');
|
|
16
16
|
const { getCronstrueLocale } = require('./locale/cronstrue');
|
|
17
17
|
const { getReactJsCron } = require('./locale/react-js-cron');
|
|
18
|
+
const { storagePathJoin } = require('../util');
|
|
18
19
|
|
|
19
20
|
function sortJSON(json) {
|
|
20
21
|
if (Array.isArray(json)) {
|
|
@@ -41,8 +42,9 @@ module.exports = (cli) => {
|
|
|
41
42
|
const files = await fg('./*/src/locale/*.json', {
|
|
42
43
|
cwd,
|
|
43
44
|
});
|
|
45
|
+
const localeDir = storagePathJoin('locales');
|
|
44
46
|
let locales = {};
|
|
45
|
-
await fs.mkdirp(
|
|
47
|
+
await fs.mkdirp(localeDir);
|
|
46
48
|
for (const file of files) {
|
|
47
49
|
const locale = path.basename(file, '.json');
|
|
48
50
|
const pkg = path.basename(path.dirname(path.dirname(path.dirname(file))));
|
|
@@ -70,10 +72,7 @@ module.exports = (cli) => {
|
|
|
70
72
|
}
|
|
71
73
|
locales = sortJSON(locales);
|
|
72
74
|
for (const locale of Object.keys(locales)) {
|
|
73
|
-
await fs.writeFile(
|
|
74
|
-
path.resolve(process.cwd(), 'storage/locales', `${locale}.json`),
|
|
75
|
-
JSON.stringify(sortJSON(locales[locale]), null, 2),
|
|
76
|
-
);
|
|
75
|
+
await fs.writeFile(path.resolve(localeDir, `${locale}.json`), JSON.stringify(sortJSON(locales[locale]), null, 2));
|
|
77
76
|
}
|
|
78
77
|
});
|
|
79
78
|
|
package/src/commands/p-test.js
CHANGED
|
@@ -14,6 +14,7 @@ const dotenv = require('dotenv');
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const glob = require('glob');
|
|
16
16
|
const _ = require('lodash');
|
|
17
|
+
const { storagePathJoin } = require('../util');
|
|
17
18
|
|
|
18
19
|
let ENV_FILE = resolve(process.cwd(), '.env.e2e');
|
|
19
20
|
|
|
@@ -55,16 +56,16 @@ async function runApp(dir, index = 0) {
|
|
|
55
56
|
APP_ENV: 'production',
|
|
56
57
|
APP_PORT: 20000 + index,
|
|
57
58
|
DB_DATABASE: `nocobase${index}`,
|
|
58
|
-
SOCKET_PATH: `
|
|
59
|
-
PM2_HOME:
|
|
60
|
-
PLAYWRIGHT_AUTH_FILE:
|
|
59
|
+
SOCKET_PATH: storagePathJoin('e2e', `gateway-e2e-${index}.sock`),
|
|
60
|
+
PM2_HOME: storagePathJoin('e2e', `.pm2-${index}`),
|
|
61
|
+
PLAYWRIGHT_AUTH_FILE: storagePathJoin('playwright', '.auth', `admin-${index}.json`),
|
|
61
62
|
E2E_JOB_ID: index,
|
|
62
63
|
},
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
exports.pTest = async (options) => {
|
|
67
|
-
const dir =
|
|
68
|
+
const dir = storagePathJoin('e2e');
|
|
68
69
|
|
|
69
70
|
if (!fs.existsSync(dir)) {
|
|
70
71
|
fs.mkdirSync(dir, { recursive: true });
|
package/src/commands/pkg.js
CHANGED
|
@@ -22,7 +22,7 @@ class Package {
|
|
|
22
22
|
constructor(packageName, packageManager) {
|
|
23
23
|
this.packageName = packageName;
|
|
24
24
|
this.packageManager = packageManager;
|
|
25
|
-
this.outputDir = path.resolve(
|
|
25
|
+
this.outputDir = path.resolve(resolvePluginStoragePath(), this.packageName);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
get token() {
|
package/src/commands/start.js
CHANGED
|
@@ -13,6 +13,8 @@ const { existsSync, rmSync } = require('fs');
|
|
|
13
13
|
const { resolve, isAbsolute } = require('path');
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const chokidar = require('chokidar');
|
|
16
|
+
const { buildAppDevServerArgs, shouldUseAppDevServerSource } = require('./app-dev-utils');
|
|
17
|
+
const { resolvePluginStoragePath } = require('@nocobase/utils/plugin-symlink');
|
|
16
18
|
|
|
17
19
|
function getSocketPath() {
|
|
18
20
|
const { SOCKET_PATH } = process.env;
|
|
@@ -52,6 +54,7 @@ module.exports = (cli) => {
|
|
|
52
54
|
.allowUnknownOption()
|
|
53
55
|
.action(async (opts) => {
|
|
54
56
|
checkDBDialect();
|
|
57
|
+
const useAppDevServerSource = shouldUseAppDevServerSource();
|
|
55
58
|
if (opts.quickstart) {
|
|
56
59
|
await downloadPro();
|
|
57
60
|
}
|
|
@@ -62,7 +65,7 @@ module.exports = (cli) => {
|
|
|
62
65
|
await run('yarn', ['nocobase', 'pm2-restart']);
|
|
63
66
|
}, 500);
|
|
64
67
|
|
|
65
|
-
const watcher = chokidar.watch(
|
|
68
|
+
const watcher = chokidar.watch(`${resolvePluginStoragePath()}/**/*`, {
|
|
66
69
|
cwd: process.cwd(),
|
|
67
70
|
ignoreInitial: true,
|
|
68
71
|
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
|
|
@@ -99,7 +102,7 @@ module.exports = (cli) => {
|
|
|
99
102
|
]);
|
|
100
103
|
return;
|
|
101
104
|
}
|
|
102
|
-
if (!existsSync(resolve(process.cwd(), `${APP_PACKAGE_ROOT}/lib/index.js`))) {
|
|
105
|
+
if (!useAppDevServerSource && !existsSync(resolve(process.cwd(), `${APP_PACKAGE_ROOT}/lib/index.js`))) {
|
|
103
106
|
console.log('The code is not compiled, please execute it first');
|
|
104
107
|
console.log(chalk.yellow('$ yarn build'));
|
|
105
108
|
console.log('If you want to run in development mode, please execute');
|
|
@@ -136,12 +139,21 @@ module.exports = (cli) => {
|
|
|
136
139
|
].filter(Boolean),
|
|
137
140
|
);
|
|
138
141
|
} else {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
if (useAppDevServerSource) {
|
|
143
|
+
promptForTs();
|
|
144
|
+
run('tsx', buildAppDevServerArgs(), {
|
|
145
|
+
env: {
|
|
146
|
+
IS_DEV_CMD: 'true',
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
run(
|
|
151
|
+
'node',
|
|
152
|
+
[`${APP_PACKAGE_ROOT}/lib/index.js`, ...(NODE_ARGS || '').split(' '), ...process.argv.slice(2)].filter(
|
|
153
|
+
Boolean,
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
145
157
|
}
|
|
146
158
|
}
|
|
147
159
|
});
|
|
@@ -10,14 +10,23 @@
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const { Command } = require('commander');
|
|
12
12
|
const { resolve } = require('path');
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
run,
|
|
15
|
+
promptForTs,
|
|
16
|
+
runAppCommand,
|
|
17
|
+
hasCorePackages,
|
|
18
|
+
downloadPro,
|
|
19
|
+
hasTsNode,
|
|
20
|
+
checkDBDialect,
|
|
21
|
+
storagePathJoin,
|
|
22
|
+
} = require('../util');
|
|
14
23
|
const { existsSync, rmSync } = require('fs');
|
|
15
24
|
const { readJSON, writeJSON } = require('fs-extra');
|
|
16
25
|
const deepmerge = require('deepmerge');
|
|
17
26
|
|
|
18
27
|
const rmAppDir = () => {
|
|
19
28
|
// If ts-node is not installed, do not do the following
|
|
20
|
-
const appDevDir =
|
|
29
|
+
const appDevDir = storagePathJoin('.app-dev');
|
|
21
30
|
if (existsSync(appDevDir)) {
|
|
22
31
|
rmSync(appDevDir, { recursive: true, force: true });
|
|
23
32
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const { Command } = require('commander');
|
|
12
12
|
const { keyDecrypt } = require('@nocobase/license-kit');
|
|
13
|
-
const path = require('path');
|
|
14
13
|
const fs = require('fs');
|
|
14
|
+
const { storagePathJoin } = require('../util');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
*
|
|
@@ -22,8 +22,7 @@ module.exports = (cli) => {
|
|
|
22
22
|
.command('view-license-key')
|
|
23
23
|
.description('View License Key')
|
|
24
24
|
.action(async (options) => {
|
|
25
|
-
const
|
|
26
|
-
const filePath = path.resolve(dir, 'license-key');
|
|
25
|
+
const filePath = storagePathJoin('.license', 'license-key');
|
|
27
26
|
if (!fs.existsSync(filePath)) {
|
|
28
27
|
console.log('License key not found at ' + filePath);
|
|
29
28
|
return;
|
package/src/license.js
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
|
-
const { resolve } = require('path');
|
|
12
11
|
const fs = require('fs-extra');
|
|
13
12
|
const { keyDecrypt, getEnvAsync } = require('@nocobase/license-kit');
|
|
14
13
|
const { isEnvMatch } = require('@nocobase/plugin-license/utils/env');
|
|
15
14
|
const { logger } = require('./logger');
|
|
15
|
+
const { storagePathJoin } = require('./util');
|
|
16
16
|
const { pick } = require('lodash');
|
|
17
17
|
|
|
18
18
|
exports.getAccessKeyPair = async function () {
|
|
19
|
-
const keyFile =
|
|
19
|
+
const keyFile = storagePathJoin('.license', 'license-key');
|
|
20
20
|
if (!fs.existsSync(keyFile)) {
|
|
21
21
|
logger.info('License key not found');
|
|
22
22
|
return {};
|
package/src/plugin-generator.js
CHANGED
|
@@ -38,8 +38,11 @@ async function getProjectVersion() {
|
|
|
38
38
|
|
|
39
39
|
class PluginGenerator extends Generator {
|
|
40
40
|
constructor(options) {
|
|
41
|
-
const { log, context = {}, ...opts } = options;
|
|
42
|
-
super(
|
|
41
|
+
const { log, context = {}, cwd, baseDir, ...opts } = options;
|
|
42
|
+
super({
|
|
43
|
+
...opts,
|
|
44
|
+
baseDir: baseDir || cwd || process.cwd(),
|
|
45
|
+
});
|
|
43
46
|
this.context = context;
|
|
44
47
|
this.log = log || console.log;
|
|
45
48
|
}
|
package/src/util.js
CHANGED
|
@@ -261,7 +261,7 @@ function normalizeAppDevFiles(appDevDir) {
|
|
|
261
261
|
|
|
262
262
|
exports.generateAppDir = function generateAppDir() {
|
|
263
263
|
const appPkgPath = dirname(dirname(require.resolve('@nocobase/app/src/index.ts')));
|
|
264
|
-
const appDevDir =
|
|
264
|
+
const appDevDir = storagePathJoin('.app-dev');
|
|
265
265
|
if (exports.isDev() && !exports.hasCorePackages() && appPkgPath.includes('node_modules')) {
|
|
266
266
|
if (!existsSync(appDevDir)) {
|
|
267
267
|
mkdirSync(appDevDir, { force: true, recursive: true });
|
|
@@ -374,6 +374,10 @@ function resolveV2PublicPath(appPublicPath = '/') {
|
|
|
374
374
|
|
|
375
375
|
exports.resolveV2PublicPath = resolveV2PublicPath;
|
|
376
376
|
|
|
377
|
+
function isAppDevHtml() {
|
|
378
|
+
return process.argv[2] === 'app-dev' || process.env.NOCOBASE_APP_DEV === 'true';
|
|
379
|
+
}
|
|
380
|
+
|
|
377
381
|
function buildIndexHtml(force = false) {
|
|
378
382
|
const file = `${process.env.APP_PACKAGE_ROOT}/dist/client/index.html`;
|
|
379
383
|
if (!fs.existsSync(file)) {
|
|
@@ -396,6 +400,7 @@ function buildIndexHtml(force = false) {
|
|
|
396
400
|
.replace(/\{\{env.API_BASE_URL\}\}/g, process.env.API_BASE_URL || process.env.API_BASE_PATH)
|
|
397
401
|
.replace(/\{\{env.WS_URL\}\}/g, process.env.WEBSOCKET_URL || '')
|
|
398
402
|
.replace(/\{\{env.WS_PATH\}\}/g, process.env.WS_PATH)
|
|
403
|
+
.replace(/\{\{env.NOCOBASE_APP_DEV\}\}/g, isAppDevHtml() ? 'true' : 'false')
|
|
399
404
|
.replace(/\{\{env.ESM_CDN_BASE_URL\}\}/g, process.env.ESM_CDN_BASE_URL || '')
|
|
400
405
|
.replace(/\{\{env.ESM_CDN_SUFFIX\}\}/g, process.env.ESM_CDN_SUFFIX || '')
|
|
401
406
|
.replace(/((?:src|href)=")(?:\.\/)?assets\//g, `$1${process.env.APP_PUBLIC_PATH}assets/`)
|
|
@@ -455,6 +460,7 @@ function storagePathJoin(...segments) {
|
|
|
455
460
|
|
|
456
461
|
exports.resolveStorageRoot = resolveStorageRoot;
|
|
457
462
|
exports.storagePathJoin = storagePathJoin;
|
|
463
|
+
exports.resolvePluginStoragePath = resolvePluginStoragePath;
|
|
458
464
|
/** @deprecated Use resolveStorageRoot — kept for backward compatibility */
|
|
459
465
|
exports.generateStoragePath = resolveStorageRoot;
|
|
460
466
|
|
|
@@ -485,6 +491,17 @@ function generatePm2Home() {
|
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
exports.initEnv = function initEnv() {
|
|
494
|
+
const preserveSymlinksFlag = '--preserve-symlinks';
|
|
495
|
+
const currentNodeOptions = String(process.env.NODE_OPTIONS || '').trim();
|
|
496
|
+
const hasPreserveSymlinksFlag = currentNodeOptions
|
|
497
|
+
? currentNodeOptions.split(/\s+/).includes(preserveSymlinksFlag)
|
|
498
|
+
: false;
|
|
499
|
+
if (!hasPreserveSymlinksFlag) {
|
|
500
|
+
process.env.NODE_OPTIONS = currentNodeOptions
|
|
501
|
+
? `${currentNodeOptions} ${preserveSymlinksFlag}`
|
|
502
|
+
: preserveSymlinksFlag;
|
|
503
|
+
}
|
|
504
|
+
|
|
488
505
|
const env = {
|
|
489
506
|
APP_ENV: 'development',
|
|
490
507
|
APP_KEY: 'test-jwt-secret',
|
|
@@ -504,11 +521,13 @@ exports.initEnv = function initEnv() {
|
|
|
504
521
|
// PM2_HOME: generatePm2Home(),
|
|
505
522
|
// SOCKET_PATH: generateGatewayPath(),
|
|
506
523
|
NODE_MODULES_PATH: resolve(process.cwd(), 'node_modules'),
|
|
524
|
+
NODE_PATH: resolve(process.cwd(), 'node_modules'),
|
|
507
525
|
PLUGIN_PACKAGE_PREFIX: '@nocobase/plugin-,@nocobase/plugin-sample-,@nocobase/preset-',
|
|
508
526
|
SERVER_TSCONFIG_PATH: './tsconfig.server.json',
|
|
509
527
|
CACHE_DEFAULT_STORE: 'memory',
|
|
510
528
|
CACHE_MEMORY_MAX: 2000,
|
|
511
529
|
BROWSERSLIST_IGNORE_OLD_DATA: true,
|
|
530
|
+
NOCOBASE_DEV_LOCAL_PLUGINS_ONLY: 'true',
|
|
512
531
|
PLUGIN_STATICS_PATH: '/static/plugins/',
|
|
513
532
|
APP_SERVER_BASE_URL: '',
|
|
514
533
|
APP_BASE_URL: '',
|