@nocobase/app 3.0.0-alpha.3 → 3.0.0-alpha.4
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/__tests__/browserChecker.test.ts +4 -4
- package/__tests__/clientDevProxy.test.ts +34 -0
- package/client/rsbuild.config.ts +4 -5
- package/client-v2/public/browser-checker.js +1 -45
- package/client-v2/rsbuild.config.ts +8 -6
- package/clientDevProxy.ts +32 -0
- package/dist/client/assets/{index-38b2037d.js → index-54849ef3.js} +256 -256
- package/dist/client/assets/{vendor-antd-b77deb8f.js → vendor-antd-f421fb70.js} +4 -4
- package/dist/client/index.html +1 -1
- package/dist/client/index.html.tpl +1 -1
- package/dist/client/settings/assets/{index-a1af4e61.js → index-e8b81081.js} +158 -98
- package/dist/client/settings/browser-checker.js +1 -45
- package/dist/client/settings/index.html +1 -1
- package/dist/client/v/assets/{index-42d9798f.js → index-8ecef45a.js} +10 -10
- package/dist/client/v/browser-checker.js +1 -45
- package/dist/client/v/index.html +1 -1
- package/package.json +7 -7
- /package/dist/client/assets/{index-38b2037d.js.LICENSE.txt → index-54849ef3.js.LICENSE.txt} +0 -0
- /package/dist/client/settings/assets/{index-a1af4e61.js.LICENSE.txt → index-e8b81081.js.LICENSE.txt} +0 -0
- /package/dist/client/v/assets/{index-42d9798f.js.LICENSE.txt → index-8ecef45a.js.LICENSE.txt} +0 -0
|
@@ -76,19 +76,19 @@ function runBrowserChecker(options: RunBrowserCheckerOptions) {
|
|
|
76
76
|
|
|
77
77
|
describe('v2 browser checker', () => {
|
|
78
78
|
it.each([undefined, 'modern-default', 'modern-only'] as const)(
|
|
79
|
-
'
|
|
79
|
+
'lets the modern client handle its root for entry mode %s',
|
|
80
80
|
(appClientEntryMode) => {
|
|
81
81
|
expect(
|
|
82
82
|
runBrowserChecker({
|
|
83
83
|
appClientEntryMode,
|
|
84
84
|
pathname: '/v/',
|
|
85
85
|
}).replacements,
|
|
86
|
-
).toEqual([
|
|
86
|
+
).toEqual([]);
|
|
87
87
|
},
|
|
88
88
|
);
|
|
89
89
|
|
|
90
90
|
it.each([undefined, 'modern-default', 'modern-only'] as const)(
|
|
91
|
-
'
|
|
91
|
+
'lets scoped modern clients handle their roots for entry mode %s',
|
|
92
92
|
(appClientEntryMode) => {
|
|
93
93
|
expect(
|
|
94
94
|
runBrowserChecker({
|
|
@@ -97,7 +97,7 @@ describe('v2 browser checker', () => {
|
|
|
97
97
|
search: '?from=admin',
|
|
98
98
|
hash: '#portal',
|
|
99
99
|
}).replacements,
|
|
100
|
-
).toEqual([
|
|
100
|
+
).toEqual([]);
|
|
101
101
|
},
|
|
102
102
|
);
|
|
103
103
|
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { isClientDevProxyPath, rewriteClientDevProxyRootPath } from '../clientDevProxy';
|
|
12
|
+
|
|
13
|
+
describe('client dev proxy', () => {
|
|
14
|
+
it.each([
|
|
15
|
+
['/x', '/x/', true],
|
|
16
|
+
['/x/', '/x/', true],
|
|
17
|
+
['/x/test2/assets/index.js', '/x/', true],
|
|
18
|
+
['/x-other', '/x/', false],
|
|
19
|
+
['/console/v?from=portal', '/console/v/', true],
|
|
20
|
+
['/v', '/console/v/', false],
|
|
21
|
+
])('matches complete client base paths: %s', (url, basePath, expected) => {
|
|
22
|
+
expect(isClientDevProxyPath(url, basePath)).toBe(expected);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it.each([
|
|
26
|
+
['/v', '/v/', '/v/'],
|
|
27
|
+
['/v?from=portal', '/v/', '/v/?from=portal'],
|
|
28
|
+
['/console/v#section', '/console/v/', '/console/v/#section'],
|
|
29
|
+
['/v/admin', '/v/', '/v/admin'],
|
|
30
|
+
['/v/', '/v/', '/v/'],
|
|
31
|
+
])('normalizes only a slashless client root: %s', (url, basePath, expected) => {
|
|
32
|
+
expect(rewriteClientDevProxyRootPath(url, basePath)).toBe(expected);
|
|
33
|
+
});
|
|
34
|
+
});
|
package/client/rsbuild.config.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill';
|
|
|
16
16
|
import { pluginReact } from '@rsbuild/plugin-react';
|
|
17
17
|
import { pluginSvgr } from '@rsbuild/plugin-svgr';
|
|
18
18
|
import { generatePlugins, getRsbuildBrowserAlias } from '@nocobase/devtools/rsbuildConfig';
|
|
19
|
+
import { isClientDevProxyPath, rewriteClientDevProxyRootPath } from '../clientDevProxy';
|
|
19
20
|
import { createSettingsDevProxyOptions } from '../settingsDevProxy';
|
|
20
21
|
|
|
21
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -259,17 +260,15 @@ export default defineConfig(({ command }) => {
|
|
|
259
260
|
xfwd: true,
|
|
260
261
|
},
|
|
261
262
|
{
|
|
262
|
-
context: v2BasePath,
|
|
263
|
+
context: (pathname) => isClientDevProxyPath(pathname, v2BasePath),
|
|
263
264
|
target: `http://127.0.0.1:${v2Port}`,
|
|
264
265
|
changeOrigin: true,
|
|
265
266
|
ws: true,
|
|
266
|
-
pathRewrite:
|
|
267
|
-
[`^${v2BasePath}`]: v2BasePath,
|
|
268
|
-
},
|
|
267
|
+
pathRewrite: (pathname) => rewriteClientDevProxyRootPath(pathname, v2BasePath),
|
|
269
268
|
xfwd: true,
|
|
270
269
|
},
|
|
271
270
|
{
|
|
272
|
-
context: portalBasePath,
|
|
271
|
+
context: (pathname) => isClientDevProxyPath(pathname, portalBasePath),
|
|
273
272
|
target: proxyTargetUrl,
|
|
274
273
|
changeOrigin: true,
|
|
275
274
|
ws: true,
|
|
@@ -17,46 +17,6 @@ function normalizePublicPath(value) {
|
|
|
17
17
|
return ensureTrailingSlash(normalized);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function normalizePathname(value) {
|
|
21
|
-
const normalized = ensureLeadingSlash(String(value || '/').trim() || '/').replace(/\/{2,}/g, '/');
|
|
22
|
-
return normalized === '/' ? normalized : normalized.replace(/\/+$/g, '');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveSettingsRootPath(publicPath, modernPrefix, pathname) {
|
|
26
|
-
const normalizedPublicPath = normalizePublicPath(publicPath);
|
|
27
|
-
const normalizedModernPrefix =
|
|
28
|
-
String(modernPrefix || 'v')
|
|
29
|
-
.trim()
|
|
30
|
-
.replace(/^\/+|\/+$/g, '') || 'v';
|
|
31
|
-
const modernPublicPathSuffix = `/${normalizedModernPrefix}/`;
|
|
32
|
-
if (!normalizedPublicPath.endsWith(modernPublicPathSuffix)) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const modernRootPath = normalizePathname(normalizedPublicPath);
|
|
37
|
-
const normalizedPathname = normalizePathname(pathname);
|
|
38
|
-
let relativePath = '';
|
|
39
|
-
if (normalizedPathname !== modernRootPath) {
|
|
40
|
-
if (!normalizedPathname.startsWith(`${modernRootPath}/`)) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
relativePath = normalizedPathname.slice(modernRootPath.length + 1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let appScope = '';
|
|
47
|
-
if (relativePath) {
|
|
48
|
-
const match = /^(apps|_app)\/([^/]+)$/.exec(relativePath);
|
|
49
|
-
if (!match) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
appScope = `/${match[1]}/${match[2]}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const rootPublicPath = normalizedPublicPath.slice(0, -(normalizedModernPrefix.length + 1));
|
|
56
|
-
const rootPrefix = rootPublicPath === '/' ? '' : rootPublicPath.replace(/\/+$/g, '');
|
|
57
|
-
return `${rootPrefix}/settings${appScope}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
20
|
function isSettingsBrowserCheckerScript(script) {
|
|
61
21
|
const source = String((script && script.src) || '').split(/[?#]/)[0];
|
|
62
22
|
return source.endsWith('/settings/browser-checker.js');
|
|
@@ -81,15 +41,11 @@ function resolveScopedSettingsRootPath(publicPath, pathname) {
|
|
|
81
41
|
const basename = normalizePublicPath(window['__nocobase_public_path__'] || '/');
|
|
82
42
|
const currentPath = ensureLeadingSlash(String(window.location.pathname || '/').trim() || '/').replace(/\/{2,}/g, '/');
|
|
83
43
|
const basenameWithoutTrailingSlash = basename === '/' ? '/' : basename.replace(/\/+$/, '');
|
|
84
|
-
const settingsRootPath = resolveSettingsRootPath(basename, window['__nocobase_modern_client_prefix__'], currentPath);
|
|
85
44
|
const scopedSettingsRootPath = isSettingsBrowserCheckerScript(document.currentScript)
|
|
86
45
|
? resolveScopedSettingsRootPath(basename, currentPath)
|
|
87
46
|
: null;
|
|
88
47
|
|
|
89
|
-
if (
|
|
90
|
-
const newUrl = `${window.location.origin}${settingsRootPath}${window.location.search}${window.location.hash}`;
|
|
91
|
-
window.location.replace(newUrl);
|
|
92
|
-
} else if (scopedSettingsRootPath) {
|
|
48
|
+
if (scopedSettingsRootPath) {
|
|
93
49
|
const newUrl = `${window.location.origin}${scopedSettingsRootPath}${window.location.search}${window.location.hash}`;
|
|
94
50
|
window.location.replace(newUrl);
|
|
95
51
|
} else if (basename !== '/' && currentPath === basenameWithoutTrailingSlash) {
|
|
@@ -15,6 +15,7 @@ import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill';
|
|
|
15
15
|
import { pluginReact } from '@rsbuild/plugin-react';
|
|
16
16
|
import { pluginSvgr } from '@rsbuild/plugin-svgr';
|
|
17
17
|
import { generateV2Plugins, getRsbuildBrowserAlias } from '@nocobase/devtools/rsbuildConfig';
|
|
18
|
+
import { isClientDevProxyPath, rewriteClientDevProxyRootPath } from '../clientDevProxy';
|
|
18
19
|
import { createSettingsDevProxyOptions, isSettingsDevPath } from '../settingsDevProxy';
|
|
19
20
|
|
|
20
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -300,7 +301,7 @@ export default defineConfig(({ command }) => {
|
|
|
300
301
|
changeOrigin: true,
|
|
301
302
|
},
|
|
302
303
|
{
|
|
303
|
-
context: portalBasePath,
|
|
304
|
+
context: (pathname) => isClientDevProxyPath(pathname, portalBasePath),
|
|
304
305
|
target: proxyTargetUrl,
|
|
305
306
|
changeOrigin: true,
|
|
306
307
|
ws: true,
|
|
@@ -324,11 +325,12 @@ export default defineConfig(({ command }) => {
|
|
|
324
325
|
lazyCompilation: false,
|
|
325
326
|
setupMiddlewares: [
|
|
326
327
|
(middlewares) => {
|
|
327
|
-
if (appClientEntryMode !== 'modern-only') {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
328
|
middlewares.unshift((req, res, next) => {
|
|
329
|
+
req.url = rewriteClientDevProxyRootPath(String(req.url || '/'), v2PublicPath);
|
|
330
|
+
if (appClientEntryMode !== 'modern-only') {
|
|
331
|
+
next();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
332
334
|
const [rawPathname = '/', query = ''] = String(req.url || '/').split('?');
|
|
333
335
|
const pathname = normalizePathname(rawPathname);
|
|
334
336
|
if (
|
|
@@ -337,7 +339,7 @@ export default defineConfig(({ command }) => {
|
|
|
337
339
|
pathname.startsWith(apiBasePath) ||
|
|
338
340
|
pathname.startsWith(wsBasePath) ||
|
|
339
341
|
pathname.startsWith(localStorageBasePath) ||
|
|
340
|
-
pathname
|
|
342
|
+
isClientDevProxyPath(pathname, portalBasePath) ||
|
|
341
343
|
pathname.startsWith(staticBasePath) ||
|
|
342
344
|
isSettingsDevPath(pathname, appPublicPath)
|
|
343
345
|
) {
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
function normalizeBasePath(value: string) {
|
|
11
|
+
let normalized = value || '/';
|
|
12
|
+
if (!normalized.startsWith('/')) {
|
|
13
|
+
normalized = `/${normalized}`;
|
|
14
|
+
}
|
|
15
|
+
normalized = normalized.replace(/\/{2,}/g, '/');
|
|
16
|
+
return normalized === '/' ? normalized : normalized.replace(/\/+$/g, '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isClientDevProxyPath(url: string, basePath: string) {
|
|
20
|
+
const [pathname] = String(url || '/').split(/[?#]/, 1);
|
|
21
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
22
|
+
return pathname === normalizedBasePath || pathname.startsWith(`${normalizedBasePath}/`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function rewriteClientDevProxyRootPath(url: string, basePath: string) {
|
|
26
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
27
|
+
const [pathname] = String(url || '/').split(/[?#]/, 1);
|
|
28
|
+
if (pathname !== normalizedBasePath || normalizedBasePath === '/') {
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
return `${normalizedBasePath}/${url.slice(pathname.length)}`;
|
|
32
|
+
}
|