@nocobase/app 2.2.0-alpha.1 → 2.2.0-alpha.3
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/client/public/browser-checker.js +58 -0
- package/client/rsbuild.config.ts +9 -0
- package/client/src/__tests__/runtimePublicPath.test.ts +24 -0
- package/client/src/main.tsx +1 -0
- package/client/src/runtimePublicPath.ts +40 -0
- package/client-v2/rsbuild.config.ts +53 -0
- package/dist/client/assets/index-774ce234.js +3609 -0
- package/dist/client/assets/{vendor-misc-b5370062.js → vendor-misc-0f74f11a.js} +2 -2
- package/dist/client/browser-checker.js +58 -0
- package/dist/client/index.html +3 -1
- package/dist/client/index.html.tpl +3 -1
- package/dist/client/v/assets/index-f228f175.js +1745 -0
- package/dist/client/v/index.html +1 -1
- package/package.json +7 -7
- package/dist/client/assets/index-055f2436.js +0 -3480
- package/dist/client/v/assets/index-5d0f70ac.js +0 -1616
- /package/dist/client/assets/{index-055f2436.js.LICENSE.txt → index-774ce234.js.LICENSE.txt} +0 -0
- /package/dist/client/assets/{vendor-misc-b5370062.js.LICENSE.txt → vendor-misc-0f74f11a.js.LICENSE.txt} +0 -0
- /package/dist/client/v/assets/{index-5d0f70ac.js.LICENSE.txt → index-f228f175.js.LICENSE.txt} +0 -0
|
@@ -17,9 +17,31 @@ function normalizePublicPath(value) {
|
|
|
17
17
|
return ensureTrailingSlash(normalized);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function trimTrailingSlash(value) {
|
|
21
|
+
return value === '/' ? value : value.replace(/\/+$/g, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizePathname(value) {
|
|
25
|
+
const normalized = ensureLeadingSlash(String(value || '/').trim() || '/').replace(/\/{2,}/g, '/');
|
|
26
|
+
if (normalized !== '/' && normalized.endsWith('/')) {
|
|
27
|
+
return normalized.replace(/\/+$/g, '');
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isClientDocumentEntryPath(pathname) {
|
|
33
|
+
const normalized = normalizePathname(pathname);
|
|
34
|
+
return normalized === '/' || normalized === '/index.html' || !/\.[^/]+$/.test(normalized);
|
|
35
|
+
}
|
|
36
|
+
|
|
20
37
|
const basename = normalizePublicPath(window['__nocobase_public_path__'] || '/');
|
|
21
38
|
const currentPath = ensureLeadingSlash(String(window.location.pathname || '/').trim() || '/').replace(/\/{2,}/g, '/');
|
|
22
39
|
const basenameWithoutTrailingSlash = basename === '/' ? '/' : basename.replace(/\/+$/, '');
|
|
40
|
+
const modernClientPrefix =
|
|
41
|
+
String(window['__nocobase_modern_client_prefix__'] || 'v')
|
|
42
|
+
.trim()
|
|
43
|
+
.replace(/^\/+|\/+$/g, '') || 'v';
|
|
44
|
+
const appClientEntryMode = window['__nocobase_app_client_entry_mode__'];
|
|
23
45
|
|
|
24
46
|
if (basename !== '/' && currentPath === basenameWithoutTrailingSlash) {
|
|
25
47
|
const newUrl = `${window.location.origin}${basename}${window.location.search}${window.location.hash}`;
|
|
@@ -28,6 +50,42 @@ if (basename !== '/' && currentPath === basenameWithoutTrailingSlash) {
|
|
|
28
50
|
const newPath = currentPath === '/' ? basename : `${basenameWithoutTrailingSlash}${currentPath}`;
|
|
29
51
|
let newUrl = window.location.origin + newPath + window.location.search + window.location.hash;
|
|
30
52
|
window.location.replace(newUrl);
|
|
53
|
+
} else {
|
|
54
|
+
// This client-side redirect is still needed because legacy `index.html` is
|
|
55
|
+
// not always served through the node gateway. In nginx/static delivery paths
|
|
56
|
+
// the browser may already be running the legacy shell by the time entry-mode
|
|
57
|
+
// logic is evaluated, so the last hop into the modern entry has to be
|
|
58
|
+
// recoverable in the browser as well.
|
|
59
|
+
const normalizedPath = normalizePathname(currentPath);
|
|
60
|
+
const relativePath =
|
|
61
|
+
basename === '/'
|
|
62
|
+
? normalizedPath
|
|
63
|
+
: normalizedPath === basenameWithoutTrailingSlash
|
|
64
|
+
? '/'
|
|
65
|
+
: normalizedPath.startsWith(basename)
|
|
66
|
+
? normalizePathname(normalizedPath.slice(basename.length - 1))
|
|
67
|
+
: null;
|
|
68
|
+
const modernBase = `${trimTrailingSlash(basename)}/${modernClientPrefix}/`.replace(/\/{2,}/g, '/');
|
|
69
|
+
const isModernDefault = appClientEntryMode === 'modern-default';
|
|
70
|
+
const isModernOnly = appClientEntryMode === 'modern-only';
|
|
71
|
+
if (
|
|
72
|
+
relativePath &&
|
|
73
|
+
isClientDocumentEntryPath(relativePath) &&
|
|
74
|
+
(isModernDefault || isModernOnly) &&
|
|
75
|
+
!normalizedPath.startsWith(modernBase)
|
|
76
|
+
) {
|
|
77
|
+
const targetPath = isModernDefault
|
|
78
|
+
? relativePath === '/' || relativePath === '/index.html'
|
|
79
|
+
? relativePath === '/index.html'
|
|
80
|
+
? `${modernBase}index.html`
|
|
81
|
+
: modernBase
|
|
82
|
+
: null
|
|
83
|
+
: `${trimTrailingSlash(modernBase)}${relativePath}`;
|
|
84
|
+
if (targetPath && targetPath !== currentPath) {
|
|
85
|
+
const newUrl = window.location.origin + targetPath + window.location.search + window.location.hash;
|
|
86
|
+
window.location.replace(newUrl);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
31
89
|
}
|
|
32
90
|
let showLog = true;
|
|
33
91
|
function log(m) {
|
package/client/rsbuild.config.ts
CHANGED
|
@@ -47,9 +47,16 @@ function toDefineLiteral(value: string | undefined) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function createRuntimeHeadScript(appPublicPath: string, isBuild: boolean) {
|
|
50
|
+
const modernClientPrefix =
|
|
51
|
+
String(process.env.APP_MODERN_CLIENT_PREFIX || 'v')
|
|
52
|
+
.trim()
|
|
53
|
+
.replace(/^\/+|\/+$/g, '') || 'v';
|
|
54
|
+
const appClientEntryMode = process.env.APP_CLIENT_ENTRY_MODE;
|
|
50
55
|
if (!isBuild) {
|
|
51
56
|
return [
|
|
52
57
|
`window['__nocobase_public_path__'] = ${JSON.stringify(appPublicPath)};`,
|
|
58
|
+
`window['__nocobase_modern_client_prefix__'] = ${JSON.stringify(modernClientPrefix)};`,
|
|
59
|
+
`window['__nocobase_app_client_entry_mode__'] = ${JSON.stringify(appClientEntryMode)};`,
|
|
53
60
|
`window['__nocobase_dev_public_path__'] = "/";`,
|
|
54
61
|
`window['__nocobase_app_dev__'] = ${JSON.stringify(process.env.NOCOBASE_APP_DEV === 'true')};`,
|
|
55
62
|
`window['__esm_cdn_base_url__'] = ${JSON.stringify(process.env.ESM_CDN_BASE_URL || '')};`,
|
|
@@ -60,6 +67,8 @@ function createRuntimeHeadScript(appPublicPath: string, isBuild: boolean) {
|
|
|
60
67
|
return [
|
|
61
68
|
`window['__webpack_public_path__'] = '{{env.CDN_BASE_URL}}';`,
|
|
62
69
|
`window['__nocobase_public_path__'] = '${appPublicPath}';`,
|
|
70
|
+
`window['__nocobase_modern_client_prefix__'] = '{{env.APP_MODERN_CLIENT_PREFIX}}';`,
|
|
71
|
+
`window['__nocobase_app_client_entry_mode__'] = '{{env.APP_CLIENT_ENTRY_MODE}}';`,
|
|
63
72
|
`window['__nocobase_api_base_url__'] = '{{env.API_BASE_URL}}';`,
|
|
64
73
|
`window['__nocobase_api_client_storage_prefix__'] = '{{env.API_CLIENT_STORAGE_PREFIX}}';`,
|
|
65
74
|
`window['__nocobase_api_client_storage_type__'] = '{{env.API_CLIENT_STORAGE_TYPE}}';`,
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { resolveRuntimeAssetPublicPath } from '../runtimePublicPath';
|
|
11
|
+
|
|
12
|
+
describe('resolveRuntimeAssetPublicPath', () => {
|
|
13
|
+
it('should prefer the injected window public path over an auto-detected runtime path', () => {
|
|
14
|
+
expect(resolveRuntimeAssetPublicPath('/dist/2.1.8/', 'https://static.cloudflareinsights.com/assets/../')).toBe(
|
|
15
|
+
'/dist/2.1.8/',
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should fall back to the runtime public path when the injected value is missing', () => {
|
|
20
|
+
expect(resolveRuntimeAssetPublicPath(undefined, 'https://cdn.nocobase.com/dist/2.1.8/')).toBe(
|
|
21
|
+
'https://cdn.nocobase.com/dist/2.1.8/',
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/client/src/main.tsx
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
__webpack_public_path__?: string;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizePublicPath(value: string | undefined, fallback = '/') {
|
|
17
|
+
let normalized = value?.trim() || fallback;
|
|
18
|
+
if (!normalized.endsWith('/')) {
|
|
19
|
+
normalized = `${normalized}/`;
|
|
20
|
+
}
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveRuntimeAssetPublicPath(
|
|
25
|
+
windowPublicPath: string | undefined,
|
|
26
|
+
runtimePublicPath: string | undefined,
|
|
27
|
+
) {
|
|
28
|
+
return normalizePublicPath(windowPublicPath, normalizePublicPath(runtimePublicPath, '/'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// `__webpack_public_path__` is a webpack magic global that must be assigned
|
|
32
|
+
// before any lazy-loaded chunk request happens.
|
|
33
|
+
declare let __webpack_public_path__: string;
|
|
34
|
+
|
|
35
|
+
const runtimePublicPath = typeof __webpack_public_path__ === 'string' ? __webpack_public_path__ : undefined;
|
|
36
|
+
|
|
37
|
+
if (typeof window !== 'undefined' && typeof __webpack_public_path__ !== 'undefined') {
|
|
38
|
+
// eslint-disable-next-line prefer-const
|
|
39
|
+
__webpack_public_path__ = resolveRuntimeAssetPublicPath(window.__webpack_public_path__, runtimePublicPath);
|
|
40
|
+
}
|
|
@@ -52,6 +52,27 @@ function toNumber(value: string | undefined, fallback: number) {
|
|
|
52
52
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function normalizePathname(value: string | undefined) {
|
|
56
|
+
let normalized = value || '/';
|
|
57
|
+
if (!normalized.startsWith('/')) {
|
|
58
|
+
normalized = `/${normalized}`;
|
|
59
|
+
}
|
|
60
|
+
normalized = normalized.replace(/\/{2,}/g, '/');
|
|
61
|
+
if (normalized !== '/' && normalized.endsWith('/')) {
|
|
62
|
+
return normalized.replace(/\/+$/g, '');
|
|
63
|
+
}
|
|
64
|
+
return normalized || '/';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isClientDocumentEntryPath(pathname: string) {
|
|
68
|
+
return pathname === '/' || pathname === '/index.html' || !/\.[^/]+$/.test(pathname);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isWithinBasePath(pathname: string, basePath: string) {
|
|
72
|
+
const normalizedBasePath = normalizePathname(basePath);
|
|
73
|
+
return pathname === normalizedBasePath || pathname.startsWith(`${normalizedBasePath}/`);
|
|
74
|
+
}
|
|
75
|
+
|
|
55
76
|
function createRuntimeHeadScript(v2PublicPath: string, isBuild: boolean, modernClientPrefix: string) {
|
|
56
77
|
if (!isBuild) {
|
|
57
78
|
return [
|
|
@@ -132,6 +153,7 @@ export default defineConfig(({ command }) => {
|
|
|
132
153
|
const modernClientDistPath = ensurePublicPath(`${appPublicPath.replace(/\/$/, '')}/${MODERN_CLIENT_DIST_DIR}/`);
|
|
133
154
|
const modernClientPublicPath = ensurePublicPath(`${appPublicPath.replace(/\/$/, '')}/${modernClientPrefix}/`);
|
|
134
155
|
const v2PublicPath = isBuild ? modernClientDistPath : modernClientPublicPath;
|
|
156
|
+
const appClientEntryMode = process.env.APP_CLIENT_ENTRY_MODE;
|
|
135
157
|
const wsBasePath = ensurePublicPath(process.env.WS_PATH || '/ws/');
|
|
136
158
|
const hmrPath = `${v2PublicPath.replace(/\/$/, '')}/__rspack_hmr`;
|
|
137
159
|
const v2Port = toNumber(process.env.APP_V2_PORT, 13002);
|
|
@@ -275,6 +297,37 @@ export default defineConfig(({ command }) => {
|
|
|
275
297
|
dev: {
|
|
276
298
|
assetPrefix: v2PublicPath,
|
|
277
299
|
lazyCompilation: false,
|
|
300
|
+
setupMiddlewares: [
|
|
301
|
+
(middlewares) => {
|
|
302
|
+
if (appClientEntryMode !== 'modern-only') {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
middlewares.unshift((req, res, next) => {
|
|
307
|
+
const [rawPathname = '/', query = ''] = String(req.url || '/').split('?');
|
|
308
|
+
const pathname = normalizePathname(rawPathname);
|
|
309
|
+
if (
|
|
310
|
+
!isClientDocumentEntryPath(pathname) ||
|
|
311
|
+
pathname.startsWith('/.well-known/') ||
|
|
312
|
+
pathname.startsWith(apiBasePath) ||
|
|
313
|
+
pathname.startsWith(wsBasePath) ||
|
|
314
|
+
pathname.startsWith(localStorageBasePath) ||
|
|
315
|
+
pathname.startsWith(staticBasePath)
|
|
316
|
+
) {
|
|
317
|
+
next();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!isWithinBasePath(pathname, v2PublicPath)) {
|
|
321
|
+
const target = pathname === '/' ? v2PublicPath : `${v2PublicPath.replace(/\/$/, '')}${pathname}`;
|
|
322
|
+
res.statusCode = 302;
|
|
323
|
+
res.setHeader('Location', `${target}${query ? `?${query}` : ''}`);
|
|
324
|
+
res.end();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
next();
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
],
|
|
278
331
|
client: {
|
|
279
332
|
overlay: false,
|
|
280
333
|
protocol: 'ws',
|