openxiangda 1.0.34 → 1.0.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/README.md +18 -0
- package/lib/cli.js +410 -1
- package/lib/workspace-init.js +1 -0
- package/openxiangda-skills/SKILL.md +5 -3
- package/openxiangda-skills/references/best-practices.md +11 -6
- package/openxiangda-skills/references/component-guide.md +10 -11
- package/openxiangda-skills/references/connector-resources.md +3 -0
- package/openxiangda-skills/references/data-views.md +217 -0
- package/openxiangda-skills/references/forms/component-registry.md +4 -3
- package/openxiangda-skills/references/forms/form-schema.md +31 -2
- package/openxiangda-skills/references/pages/page-sdk.md +43 -0
- package/openxiangda-skills/references/style-system.md +14 -18
- package/openxiangda-skills/references/troubleshooting.md +13 -13
- package/openxiangda-skills/references/workspace-state.md +9 -0
- package/openxiangda-skills/skills/openxiangda-form/SKILL.md +3 -3
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +2 -2
- package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +1 -1
- package/package.json +1 -1
- package/packages/sdk/dist/components/index.cjs +944 -765
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +18 -2
- package/packages/sdk/dist/components/index.d.ts +18 -2
- package/packages/sdk/dist/components/index.mjs +938 -761
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/index.cjs +114 -30
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +18 -1
- package/packages/sdk/dist/runtime/index.d.ts +18 -1
- package/packages/sdk/dist/runtime/index.mjs +114 -30
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/styles/antd-theme.cjs +11 -3
- package/packages/sdk/dist/styles/antd-theme.cjs.map +1 -1
- package/packages/sdk/dist/styles/antd-theme.d.mts +2 -1
- package/packages/sdk/dist/styles/antd-theme.d.ts +2 -1
- package/packages/sdk/dist/styles/antd-theme.mjs +11 -3
- package/packages/sdk/dist/styles/antd-theme.mjs.map +1 -1
- package/packages/sdk/dist/styles/tailwind-preset.cjs +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.cjs.map +1 -1
- package/packages/sdk/dist/styles/tailwind-preset.d.mts +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.d.ts +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.mjs +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.mjs.map +1 -1
- package/packages/sdk/dist/styles/tokens.css +1 -0
- package/packages/sdk/src/build-source/scripts/build-forms.mjs +135 -50
- package/packages/sdk/src/build-source/scripts/build-pages.mjs +37 -10
- package/packages/sdk/src/build-source/scripts/register.mjs +2 -0
- package/packages/sdk/src/build-source/scripts/utils/form-api.mjs +1 -0
- package/packages/sdk/src/build-source/scripts/utils/load-config.mjs +3 -2
- package/packages/sdk/src/build-source/scripts/utils/register-payload.test.ts +2 -1
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.mjs +9 -7
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.test.ts +6 -4
- package/packages/sdk/src/build-source/src/cli.mjs +17 -0
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +3 -3
- package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +4 -3
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/domain/role-governance/permissions.test.ts +1 -1
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/forms/app-role/schema.ts +36 -18
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/forms/service-ticket/schema.ts +36 -18
- package/templates/sy-lowcode-app-workspace/postcss.config.cjs +0 -15
- package/templates/sy-lowcode-app-workspace/src/main.tsx +1 -12
- package/templates/sy-lowcode-app-workspace/src/shared/form-schema.ts +0 -1
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { gzipSync } from "node:zlib";
|
|
11
11
|
import { build } from "vite";
|
|
12
|
-
import { rootDir } from "./utils/load-config.mjs";
|
|
12
|
+
import { loadConfig, rootDir } from "./utils/load-config.mjs";
|
|
13
13
|
import {
|
|
14
14
|
cleanBuildCache,
|
|
15
15
|
commitIncrementalBuild,
|
|
@@ -36,6 +36,7 @@ const tmpDir = path.join(rootDir, ".tmp");
|
|
|
36
36
|
const runtimeVersionPlaceholder = "__SY_FORM_RUNTIME_VERSION__";
|
|
37
37
|
const runtimeCacheFileName = "build-cache.json";
|
|
38
38
|
const portalContainerResolverGlobal = "__OPENXIANGDA_GET_PORTAL_CONTAINER__";
|
|
39
|
+
let workspaceConfigPromise = null;
|
|
39
40
|
|
|
40
41
|
const runtimePackages = [
|
|
41
42
|
"react",
|
|
@@ -73,6 +74,20 @@ function readTextIfExists(filePath) {
|
|
|
73
74
|
return fs.readFileSync(filePath, "utf-8");
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function normalizeCssIsolation(value) {
|
|
78
|
+
if (value === "namespace" || value === "shadow") return value;
|
|
79
|
+
return "none";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function usesNamespaceCss(cssIsolation) {
|
|
83
|
+
return cssIsolation === "namespace" || cssIsolation === "shadow";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getWorkspaceConfig() {
|
|
87
|
+
if (!workspaceConfigPromise) workspaceConfigPromise = loadConfig();
|
|
88
|
+
return workspaceConfigPromise;
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
function readPackageVersion(packageName) {
|
|
77
92
|
try {
|
|
78
93
|
let current = path.dirname(require.resolve(packageName));
|
|
@@ -92,10 +107,11 @@ function readPackageVersion(packageName) {
|
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
function createRuntimeInputHash(runtimeEntryContent) {
|
|
110
|
+
function createRuntimeInputHash(runtimeEntryContent, cssIsolation) {
|
|
96
111
|
const hash = crypto.createHash("sha256");
|
|
97
112
|
const configFiles = [
|
|
98
113
|
path.join(rootDir, "src/index.css"),
|
|
114
|
+
path.join(rootDir, "app-workspace.config.ts"),
|
|
99
115
|
path.join(rootDir, "tailwind.config.cjs"),
|
|
100
116
|
path.join(rootDir, "postcss.config.cjs"),
|
|
101
117
|
fileURLToPath(import.meta.url),
|
|
@@ -107,6 +123,7 @@ function createRuntimeInputHash(runtimeEntryContent) {
|
|
|
107
123
|
|
|
108
124
|
hash.update("sy-form-runtime-v2-input");
|
|
109
125
|
hash.update(runtimeEntryContent);
|
|
126
|
+
hash.update(normalizeCssIsolation(cssIsolation));
|
|
110
127
|
hash.update(JSON.stringify(packageVersions));
|
|
111
128
|
configFiles.forEach((filePath) => {
|
|
112
129
|
hash.update(filePath);
|
|
@@ -115,18 +132,21 @@ function createRuntimeInputHash(runtimeEntryContent) {
|
|
|
115
132
|
return hash.digest("hex");
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
async function createCssOptions() {
|
|
135
|
+
async function createCssOptions(cssIsolation = "none") {
|
|
119
136
|
const tailwindcssModule = await import("tailwindcss");
|
|
120
137
|
const autoprefixerModule = await import("autoprefixer");
|
|
121
138
|
const tailwindcss = tailwindcssModule.default ?? tailwindcssModule;
|
|
122
139
|
const autoprefixer = autoprefixerModule.default ?? autoprefixerModule;
|
|
140
|
+
const plugins = [
|
|
141
|
+
tailwindcss({ config: path.join(rootDir, "tailwind.config.cjs") }),
|
|
142
|
+
];
|
|
143
|
+
if (usesNamespaceCss(normalizeCssIsolation(cssIsolation))) {
|
|
144
|
+
plugins.push(createNamespaceCssPlugin());
|
|
145
|
+
}
|
|
146
|
+
plugins.push(autoprefixer());
|
|
123
147
|
return {
|
|
124
148
|
postcss: {
|
|
125
|
-
plugins
|
|
126
|
-
tailwindcss({ config: path.join(rootDir, "tailwind.config.cjs") }),
|
|
127
|
-
createNamespaceCssPlugin(),
|
|
128
|
-
autoprefixer(),
|
|
129
|
-
],
|
|
149
|
+
plugins,
|
|
130
150
|
},
|
|
131
151
|
};
|
|
132
152
|
}
|
|
@@ -241,7 +261,7 @@ import { StyleProvider } from '@ant-design/cssinjs';
|
|
|
241
261
|
import { App as AntdApp, ConfigProvider, message } from 'antd';
|
|
242
262
|
import zhCN from 'antd/locale/zh_CN';
|
|
243
263
|
import * as SyFormComponentsModule from 'openxiangda';
|
|
244
|
-
import { antdTheme } from 'openxiangda/antd-theme';
|
|
264
|
+
import { antdTheme, legacyAntdTheme } from 'openxiangda/antd-theme';
|
|
245
265
|
import 'antd-mobile/es/global';
|
|
246
266
|
import '../src/index.css';
|
|
247
267
|
|
|
@@ -256,6 +276,33 @@ const PORTAL_CONTAINER_RESOLVER_GLOBAL = '${portalContainerResolverGlobal}';
|
|
|
256
276
|
const MESSAGE_PROXY_GLOBAL = '__OPENXIANGDA_ANTD_MESSAGE_PROXY__';
|
|
257
277
|
const MESSAGE_METHODS = ['open', 'success', 'info', 'warning', 'error', 'loading', 'destroy'];
|
|
258
278
|
const portalContainers = new WeakMap();
|
|
279
|
+
const portalCssIsolations = new WeakMap();
|
|
280
|
+
|
|
281
|
+
function normalizeRuntimeCssIsolation(value) {
|
|
282
|
+
return value === 'namespace' || value === 'shadow' ? value : 'none';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getRuntimeCssIsolation(context = {}) {
|
|
286
|
+
return normalizeRuntimeCssIsolation(
|
|
287
|
+
context.cssIsolation ||
|
|
288
|
+
context.runtime?.cssIsolation ||
|
|
289
|
+
context.page?.capabilities?.cssIsolation,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function usesLegacyCssIsolation(cssIsolation) {
|
|
294
|
+
return cssIsolation === 'namespace' || cssIsolation === 'shadow';
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getAntdRuntimeOptions(cssIsolation) {
|
|
298
|
+
const legacy = usesLegacyCssIsolation(cssIsolation);
|
|
299
|
+
return {
|
|
300
|
+
prefixCls: legacy ? 'sy-ant' : 'ant',
|
|
301
|
+
iconPrefixCls: legacy ? 'sy-anticon' : 'anticon',
|
|
302
|
+
messagePrefixCls: legacy ? 'sy-ant-message' : 'ant-message',
|
|
303
|
+
theme: legacy ? legacyAntdTheme : antdTheme,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
259
306
|
|
|
260
307
|
function getStyleContainer(el) {
|
|
261
308
|
const rootNode = el.getRootNode?.();
|
|
@@ -291,14 +338,19 @@ function getTargetContainer(el) {
|
|
|
291
338
|
return () => getRuntimeRoot(el);
|
|
292
339
|
}
|
|
293
340
|
|
|
294
|
-
function createAntdConfig(el, portalContainer) {
|
|
341
|
+
function createAntdConfig(el, portalContainer, cssIsolation) {
|
|
342
|
+
const antdOptions = getAntdRuntimeOptions(cssIsolation);
|
|
295
343
|
return {
|
|
296
344
|
locale: zhCN,
|
|
297
|
-
prefixCls:
|
|
298
|
-
iconPrefixCls:
|
|
299
|
-
theme:
|
|
300
|
-
|
|
301
|
-
|
|
345
|
+
prefixCls: antdOptions.prefixCls,
|
|
346
|
+
iconPrefixCls: antdOptions.iconPrefixCls,
|
|
347
|
+
theme: antdOptions.theme,
|
|
348
|
+
...(usesLegacyCssIsolation(cssIsolation)
|
|
349
|
+
? {
|
|
350
|
+
getPopupContainer: getPopupContainer(el, portalContainer),
|
|
351
|
+
getTargetContainer: getTargetContainer(el),
|
|
352
|
+
}
|
|
353
|
+
: {}),
|
|
302
354
|
};
|
|
303
355
|
}
|
|
304
356
|
|
|
@@ -352,12 +404,15 @@ function registerAntdMessageApi(api) {
|
|
|
352
404
|
};
|
|
353
405
|
}
|
|
354
406
|
|
|
355
|
-
function installRuntimePortalContainer(el) {
|
|
356
|
-
const portalContainer =
|
|
407
|
+
function installRuntimePortalContainer(el, cssIsolation) {
|
|
408
|
+
const portalContainer = usesLegacyCssIsolation(cssIsolation)
|
|
409
|
+
? createPortalContainer(el)
|
|
410
|
+
: null;
|
|
357
411
|
const stack = Array.isArray(globalThis[PORTAL_CONTAINER_STACK_GLOBAL])
|
|
358
412
|
? globalThis[PORTAL_CONTAINER_STACK_GLOBAL]
|
|
359
413
|
: [];
|
|
360
|
-
|
|
414
|
+
const stackTarget = portalContainer || el.ownerDocument.body;
|
|
415
|
+
stack.push(stackTarget);
|
|
361
416
|
globalThis[PORTAL_CONTAINER_STACK_GLOBAL] = stack;
|
|
362
417
|
globalThis[PORTAL_CONTAINER_RESOLVER_GLOBAL] = () => {
|
|
363
418
|
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
@@ -375,39 +430,44 @@ function installRuntimePortalContainer(el) {
|
|
|
375
430
|
return {
|
|
376
431
|
container: portalContainer,
|
|
377
432
|
release: () => {
|
|
378
|
-
const position = stack.lastIndexOf(
|
|
433
|
+
const position = stack.lastIndexOf(stackTarget);
|
|
379
434
|
if (position >= 0) stack.splice(position, 1);
|
|
380
|
-
portalContainer
|
|
435
|
+
portalContainer?.remove();
|
|
381
436
|
},
|
|
382
437
|
};
|
|
383
438
|
}
|
|
384
439
|
|
|
385
|
-
function installAntdStaticHolder(el, portalContainer) {
|
|
440
|
+
function installAntdStaticHolder(el, portalContainer, cssIsolation) {
|
|
386
441
|
installAntdMessageProxy();
|
|
442
|
+
const antdOptions = getAntdRuntimeOptions(cssIsolation);
|
|
387
443
|
const getMessageContainer = () =>
|
|
388
|
-
portalContainer?.isConnected
|
|
444
|
+
portalContainer?.isConnected
|
|
445
|
+
? portalContainer
|
|
446
|
+
: usesLegacyCssIsolation(cssIsolation)
|
|
447
|
+
? getRuntimeRoot(el)
|
|
448
|
+
: document.body;
|
|
389
449
|
|
|
390
450
|
ConfigProvider.config({
|
|
391
|
-
prefixCls:
|
|
392
|
-
iconPrefixCls:
|
|
393
|
-
theme:
|
|
451
|
+
prefixCls: antdOptions.prefixCls,
|
|
452
|
+
iconPrefixCls: antdOptions.iconPrefixCls,
|
|
453
|
+
theme: antdOptions.theme,
|
|
394
454
|
holderRender: (children) => {
|
|
395
455
|
if (!el.isConnected) {
|
|
396
456
|
return (
|
|
397
|
-
<ConfigProvider prefixCls=
|
|
457
|
+
<ConfigProvider prefixCls={antdOptions.prefixCls} iconPrefixCls={antdOptions.iconPrefixCls} theme={antdOptions.theme}>
|
|
398
458
|
{children}
|
|
399
459
|
</ConfigProvider>
|
|
400
460
|
);
|
|
401
461
|
}
|
|
402
462
|
return (
|
|
403
463
|
<StyleProvider hashPriority="high" container={getStyleContainer(el)}>
|
|
404
|
-
<ConfigProvider {...createAntdConfig(el, portalContainer)}>{children}</ConfigProvider>
|
|
464
|
+
<ConfigProvider {...createAntdConfig(el, portalContainer, cssIsolation)}>{children}</ConfigProvider>
|
|
405
465
|
</StyleProvider>
|
|
406
466
|
);
|
|
407
467
|
},
|
|
408
468
|
});
|
|
409
469
|
message.config({
|
|
410
|
-
prefixCls:
|
|
470
|
+
prefixCls: antdOptions.messagePrefixCls,
|
|
411
471
|
getContainer: getMessageContainer,
|
|
412
472
|
});
|
|
413
473
|
}
|
|
@@ -422,42 +482,62 @@ function RuntimeMessageBridge({ children }) {
|
|
|
422
482
|
|
|
423
483
|
function renderStandardForm(el, schemaInput, context = {}) {
|
|
424
484
|
let root = roots.get(el);
|
|
425
|
-
|
|
485
|
+
const cssIsolation = getRuntimeCssIsolation(context);
|
|
486
|
+
if (usesLegacyCssIsolation(cssIsolation)) {
|
|
487
|
+
el.classList.add(NAMESPACE_ROOT_CLASS);
|
|
488
|
+
} else {
|
|
489
|
+
el.classList.remove(NAMESPACE_ROOT_CLASS);
|
|
490
|
+
}
|
|
426
491
|
let portalContainer = portalContainers.get(el);
|
|
427
|
-
|
|
492
|
+
const previousCssIsolation = portalCssIsolations.get(el);
|
|
493
|
+
if (
|
|
494
|
+
previousCssIsolation !== cssIsolation ||
|
|
495
|
+
(usesLegacyCssIsolation(cssIsolation) && !portalContainer?.isConnected) ||
|
|
496
|
+
(!usesLegacyCssIsolation(cssIsolation) && portalContainer)
|
|
497
|
+
) {
|
|
428
498
|
const cleanup = portalContainerCleanups.get(el);
|
|
429
499
|
if (cleanup) cleanup();
|
|
430
|
-
const portalHandle = installRuntimePortalContainer(el);
|
|
500
|
+
const portalHandle = installRuntimePortalContainer(el, cssIsolation);
|
|
431
501
|
portalContainer = portalHandle.container;
|
|
432
|
-
|
|
502
|
+
if (portalContainer) {
|
|
503
|
+
portalContainers.set(el, portalContainer);
|
|
504
|
+
} else {
|
|
505
|
+
portalContainers.delete(el);
|
|
506
|
+
}
|
|
507
|
+
portalCssIsolations.set(el, cssIsolation);
|
|
433
508
|
portalContainerCleanups.set(el, portalHandle.release);
|
|
434
509
|
}
|
|
435
|
-
installAntdStaticHolder(el, portalContainer);
|
|
510
|
+
installAntdStaticHolder(el, portalContainer, cssIsolation);
|
|
436
511
|
if (!root) {
|
|
437
512
|
root = createRoot(el);
|
|
438
513
|
roots.set(el, root);
|
|
439
514
|
}
|
|
440
515
|
|
|
441
516
|
const schema = defineFormSchema(schemaInput);
|
|
442
|
-
const antdConfig = createAntdConfig(el, portalContainer);
|
|
517
|
+
const antdConfig = createAntdConfig(el, portalContainer, cssIsolation);
|
|
518
|
+
const content = (
|
|
519
|
+
<StandardFormPage
|
|
520
|
+
schema={schema}
|
|
521
|
+
mode={context.mode || 'submit'}
|
|
522
|
+
initialValues={context.initialValues}
|
|
523
|
+
permissions={context.permissions}
|
|
524
|
+
formUuid={context.formUuid}
|
|
525
|
+
appType={context.appType}
|
|
526
|
+
formInstanceId={context.formInstanceId}
|
|
527
|
+
onSubmit={context.onSubmit}
|
|
528
|
+
inDrawer={context.inDrawer}
|
|
529
|
+
/>
|
|
530
|
+
);
|
|
443
531
|
root.render(
|
|
444
532
|
<StyleProvider hashPriority="high" container={getStyleContainer(el)}>
|
|
445
533
|
<ConfigProvider {...antdConfig}>
|
|
446
534
|
<AntdApp>
|
|
447
535
|
<RuntimeMessageBridge>
|
|
448
|
-
|
|
449
|
-
<
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
permissions={context.permissions}
|
|
454
|
-
formUuid={context.formUuid}
|
|
455
|
-
appType={context.appType}
|
|
456
|
-
formInstanceId={context.formInstanceId}
|
|
457
|
-
onSubmit={context.onSubmit}
|
|
458
|
-
inDrawer={context.inDrawer}
|
|
459
|
-
/>
|
|
460
|
-
</div>
|
|
536
|
+
{usesLegacyCssIsolation(cssIsolation) ? (
|
|
537
|
+
<div className="sy-app-workspace">{content}</div>
|
|
538
|
+
) : (
|
|
539
|
+
content
|
|
540
|
+
)}
|
|
461
541
|
</RuntimeMessageBridge>
|
|
462
542
|
</AntdApp>
|
|
463
543
|
</ConfigProvider>
|
|
@@ -476,6 +556,7 @@ function unmountStandardForm(el) {
|
|
|
476
556
|
releasePortalContainer();
|
|
477
557
|
portalContainerCleanups.delete(el);
|
|
478
558
|
portalContainers.delete(el);
|
|
559
|
+
portalCssIsolations.delete(el);
|
|
479
560
|
}
|
|
480
561
|
}
|
|
481
562
|
|
|
@@ -670,7 +751,9 @@ function logRuntimeAssetSummary(prefix = "共享 runtime") {
|
|
|
670
751
|
async function buildSharedRuntime(options = {}) {
|
|
671
752
|
const startedAt = Date.now();
|
|
672
753
|
const entryContent = createRuntimeEntryContent();
|
|
673
|
-
const
|
|
754
|
+
const config = await getWorkspaceConfig();
|
|
755
|
+
const cssIsolation = normalizeCssIsolation(config.defaults?.cssIsolation);
|
|
756
|
+
const inputHash = createRuntimeInputHash(entryContent, cssIsolation);
|
|
674
757
|
if (options.runtimeCache !== false && isRuntimeCacheValid(inputHash)) {
|
|
675
758
|
console.log("[build] 表单共享 runtime 缓存命中,跳过构建");
|
|
676
759
|
logRuntimeAssetSummary("缓存 runtime");
|
|
@@ -681,7 +764,7 @@ async function buildSharedRuntime(options = {}) {
|
|
|
681
764
|
|
|
682
765
|
try {
|
|
683
766
|
console.log("[build] 构建表单共享 runtime");
|
|
684
|
-
const cssOptions = await createCssOptions();
|
|
767
|
+
const cssOptions = await createCssOptions(cssIsolation);
|
|
685
768
|
const reactPlugin = await createReactPlugin();
|
|
686
769
|
await build({
|
|
687
770
|
configFile: false,
|
|
@@ -828,7 +911,9 @@ async function buildForm(form) {
|
|
|
828
911
|
|
|
829
912
|
try {
|
|
830
913
|
console.log(`[build] 构建表单: ${form.name} (shared runtime)`);
|
|
831
|
-
const
|
|
914
|
+
const config = await getWorkspaceConfig();
|
|
915
|
+
const cssIsolation = normalizeCssIsolation(config.defaults?.cssIsolation);
|
|
916
|
+
const cssOptions = await createCssOptions(cssIsolation);
|
|
832
917
|
const reactPlugin = await createReactPlugin();
|
|
833
918
|
await build({
|
|
834
919
|
configFile: false,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
formatExtractedAssetSummary,
|
|
24
24
|
} from "./utils/static-assets.mjs";
|
|
25
25
|
import { warnShadcnTailwindTokens } from "./utils/tailwind-token-warnings.mjs";
|
|
26
|
+
import { loadConfig } from "./utils/load-config.mjs";
|
|
26
27
|
|
|
27
28
|
process.env.NODE_ENV = "production";
|
|
28
29
|
process.env.BABEL_ENV = "production";
|
|
@@ -40,6 +41,7 @@ const runtimeDistDir = path.join(distRoot, "page-runtime");
|
|
|
40
41
|
const runtimeVersionPlaceholder = "__SY_PAGE_RUNTIME_VERSION__";
|
|
41
42
|
const runtimeCacheFileName = "build-cache.json";
|
|
42
43
|
const portalContainerResolverGlobal = "__OPENXIANGDA_GET_PORTAL_CONTAINER__";
|
|
44
|
+
let workspaceConfigPromise = null;
|
|
43
45
|
|
|
44
46
|
const runtimePackages = [
|
|
45
47
|
"react",
|
|
@@ -115,6 +117,20 @@ function readTextIfExists(filePath) {
|
|
|
115
117
|
return fsSync.readFileSync(filePath, "utf-8");
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
function normalizeCssIsolation(value) {
|
|
121
|
+
if (value === "namespace" || value === "shadow") return value;
|
|
122
|
+
return "none";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function usesNamespaceCss(cssIsolation) {
|
|
126
|
+
return cssIsolation === "namespace" || cssIsolation === "shadow";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getWorkspaceConfig() {
|
|
130
|
+
if (!workspaceConfigPromise) workspaceConfigPromise = loadConfig();
|
|
131
|
+
return workspaceConfigPromise;
|
|
132
|
+
}
|
|
133
|
+
|
|
118
134
|
function readPackageVersion(packageName) {
|
|
119
135
|
try {
|
|
120
136
|
let current = path.dirname(require.resolve(packageName));
|
|
@@ -208,7 +224,7 @@ build-pages - 构建复杂代码页
|
|
|
208
224
|
`);
|
|
209
225
|
}
|
|
210
226
|
|
|
211
|
-
function createRuntimeInputHash(runtimeEntryContent) {
|
|
227
|
+
function createRuntimeInputHash(runtimeEntryContent, cssIsolation) {
|
|
212
228
|
const hash = crypto.createHash("sha256");
|
|
213
229
|
const packageVersions = runtimePackages.reduce((result, packageName) => {
|
|
214
230
|
result[packageName] = readPackageVersion(packageName);
|
|
@@ -216,6 +232,7 @@ function createRuntimeInputHash(runtimeEntryContent) {
|
|
|
216
232
|
}, {});
|
|
217
233
|
const configFiles = [
|
|
218
234
|
path.join(rootDir, "src/index.css"),
|
|
235
|
+
path.join(rootDir, "app-workspace.config.ts"),
|
|
219
236
|
path.join(rootDir, "tailwind.config.cjs"),
|
|
220
237
|
path.join(rootDir, "postcss.config.cjs"),
|
|
221
238
|
fileURLToPath(import.meta.url),
|
|
@@ -223,6 +240,7 @@ function createRuntimeInputHash(runtimeEntryContent) {
|
|
|
223
240
|
|
|
224
241
|
hash.update("sy-page-runtime-v1-input");
|
|
225
242
|
hash.update(runtimeEntryContent);
|
|
243
|
+
hash.update(normalizeCssIsolation(cssIsolation));
|
|
226
244
|
hash.update(JSON.stringify(packageVersions));
|
|
227
245
|
configFiles.forEach((filePath) => {
|
|
228
246
|
hash.update(filePath);
|
|
@@ -329,18 +347,21 @@ function isRuntimeCacheValid(inputHash) {
|
|
|
329
347
|
return true;
|
|
330
348
|
}
|
|
331
349
|
|
|
332
|
-
async function createCssOptions() {
|
|
350
|
+
async function createCssOptions(cssIsolation = "none") {
|
|
333
351
|
const tailwindcssModule = await import("tailwindcss");
|
|
334
352
|
const autoprefixerModule = await import("autoprefixer");
|
|
335
353
|
const tailwindcss = tailwindcssModule.default ?? tailwindcssModule;
|
|
336
354
|
const autoprefixer = autoprefixerModule.default ?? autoprefixerModule;
|
|
355
|
+
const plugins = [
|
|
356
|
+
tailwindcss({ config: path.join(rootDir, "tailwind.config.cjs") }),
|
|
357
|
+
];
|
|
358
|
+
if (usesNamespaceCss(normalizeCssIsolation(cssIsolation))) {
|
|
359
|
+
plugins.push(createNamespaceCssPlugin());
|
|
360
|
+
}
|
|
361
|
+
plugins.push(autoprefixer());
|
|
337
362
|
return {
|
|
338
363
|
postcss: {
|
|
339
|
-
plugins
|
|
340
|
-
tailwindcss({ config: path.join(rootDir, "tailwind.config.cjs") }),
|
|
341
|
-
createNamespaceCssPlugin(),
|
|
342
|
-
autoprefixer(),
|
|
343
|
-
],
|
|
364
|
+
plugins,
|
|
344
365
|
},
|
|
345
366
|
};
|
|
346
367
|
}
|
|
@@ -375,7 +396,9 @@ function createResolveAlias() {
|
|
|
375
396
|
async function buildSharedRuntime(options = {}) {
|
|
376
397
|
const startedAt = Date.now();
|
|
377
398
|
const entryContent = createRuntimeEntryContent();
|
|
378
|
-
const
|
|
399
|
+
const config = await getWorkspaceConfig();
|
|
400
|
+
const cssIsolation = normalizeCssIsolation(config.defaults?.cssIsolation);
|
|
401
|
+
const inputHash = createRuntimeInputHash(entryContent, cssIsolation);
|
|
379
402
|
if (options.runtimeCache !== false && isRuntimeCacheValid(inputHash)) {
|
|
380
403
|
const manifest = readRuntimeManifest();
|
|
381
404
|
console.log(`[build] 代码页共享 runtime 缓存命中: ${manifest.version}`);
|
|
@@ -386,7 +409,7 @@ async function buildSharedRuntime(options = {}) {
|
|
|
386
409
|
await fs.writeFile(entryPath, entryContent, "utf8");
|
|
387
410
|
|
|
388
411
|
try {
|
|
389
|
-
const cssOptions = await createCssOptions();
|
|
412
|
+
const cssOptions = await createCssOptions(cssIsolation);
|
|
390
413
|
console.log("[build] 构建代码页共享 runtime");
|
|
391
414
|
await build({
|
|
392
415
|
configFile: false,
|
|
@@ -692,7 +715,11 @@ async function buildPage(page) {
|
|
|
692
715
|
const outDir = path.join(distRoot, "pages", page.config.code);
|
|
693
716
|
await fs.mkdir(outDir, { recursive: true });
|
|
694
717
|
|
|
695
|
-
const
|
|
718
|
+
const config = await getWorkspaceConfig();
|
|
719
|
+
const cssIsolation = normalizeCssIsolation(
|
|
720
|
+
page.config.cssIsolation || config.defaults?.cssIsolation,
|
|
721
|
+
);
|
|
722
|
+
const cssOptions = await createCssOptions(cssIsolation);
|
|
696
723
|
await build({
|
|
697
724
|
configFile: false,
|
|
698
725
|
mode: "production",
|
|
@@ -130,6 +130,7 @@ async function registerForms() {
|
|
|
130
130
|
runtime,
|
|
131
131
|
formCssPath: path.resolve(rootDir, `dist/forms/${formName}/style.css`),
|
|
132
132
|
}),
|
|
133
|
+
cssIsolation: config.defaults.cssIsolation,
|
|
133
134
|
version: config.version,
|
|
134
135
|
};
|
|
135
136
|
if (runtime) {
|
|
@@ -164,6 +165,7 @@ async function registerForms() {
|
|
|
164
165
|
? {
|
|
165
166
|
bundleUrl: payload.bundleUrl,
|
|
166
167
|
cssUrl: payload.cssUrl,
|
|
168
|
+
cssIsolation: payload.cssIsolation,
|
|
167
169
|
version: payload.version,
|
|
168
170
|
runtimeMode: payload.runtimeMode,
|
|
169
171
|
runtime: payload.runtime,
|
|
@@ -52,6 +52,7 @@ function ensureResourceBuckets(bound) {
|
|
|
52
52
|
bound.resources.automations = bound.resources.automations || {};
|
|
53
53
|
bound.resources.menus = bound.resources.menus || {};
|
|
54
54
|
bound.resources.roles = bound.resources.roles || {};
|
|
55
|
+
bound.resources.dataViews = bound.resources.dataViews || {};
|
|
55
56
|
bound.resources.pagePermissionGroups = bound.resources.pagePermissionGroups || {};
|
|
56
57
|
bound.resources.formPermissionGroups = bound.resources.formPermissionGroups || {};
|
|
57
58
|
}
|
|
@@ -111,13 +111,14 @@ function normalizeBaseUrl(value) {
|
|
|
111
111
|
|
|
112
112
|
function normalizeCssIsolation(value) {
|
|
113
113
|
if (value === "none") return "none";
|
|
114
|
+
if (value === "namespace") return "namespace";
|
|
114
115
|
if (value === "shadow") {
|
|
115
116
|
console.warn(
|
|
116
|
-
"[lowcode-workspace] cssIsolation='shadow' is deprecated; use '
|
|
117
|
+
"[lowcode-workspace] cssIsolation='shadow' is deprecated; use 'none' unless legacy isolation is required.",
|
|
117
118
|
);
|
|
118
119
|
return "shadow";
|
|
119
120
|
}
|
|
120
|
-
return "
|
|
121
|
+
return "none";
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
export function resolveOpenXiangdaEndpointConfig(baseUrl) {
|
|
@@ -19,7 +19,7 @@ const config = {
|
|
|
19
19
|
defaults: {
|
|
20
20
|
frameworkVersion: "19.0.0",
|
|
21
21
|
protocolVersion: "1.0",
|
|
22
|
-
cssIsolation: "
|
|
22
|
+
cssIsolation: "none",
|
|
23
23
|
pageMenuParentId: "MENU_PARENT",
|
|
24
24
|
pageMenuIcon: "dashboard",
|
|
25
25
|
},
|
|
@@ -105,6 +105,7 @@ describe("register payload helpers", () => {
|
|
|
105
105
|
hidePlatformNav: true,
|
|
106
106
|
defaultRoute: "home",
|
|
107
107
|
});
|
|
108
|
+
expect(payload.pages[0].runtime.cssIsolation).toBe("none");
|
|
108
109
|
expect(JSON.stringify(payload)).not.toMatch(/manifest/i);
|
|
109
110
|
});
|
|
110
111
|
|
|
@@ -29,12 +29,10 @@ const layeredTailwindCss = `@layer tailwind-base {
|
|
|
29
29
|
`;
|
|
30
30
|
const canonicalPostcssConfig = `const tailwindcss = require("tailwindcss");
|
|
31
31
|
const autoprefixer = require("autoprefixer");
|
|
32
|
-
const { createOpenXiangdaNamespaceCssPlugin } = require("openxiangda/build");
|
|
33
32
|
|
|
34
33
|
module.exports = {
|
|
35
34
|
plugins: [
|
|
36
35
|
tailwindcss(),
|
|
37
|
-
createOpenXiangdaNamespaceCssPlugin(),
|
|
38
36
|
autoprefixer(),
|
|
39
37
|
],
|
|
40
38
|
};
|
|
@@ -83,10 +81,10 @@ export function isWorkspaceTailwindCssCurrent(content) {
|
|
|
83
81
|
|
|
84
82
|
export function isWorkspacePostcssConfigCurrent(content) {
|
|
85
83
|
return Boolean(
|
|
86
|
-
content.includes("createOpenXiangdaNamespaceCssPlugin") &&
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
!content.includes("createOpenXiangdaNamespaceCssPlugin") &&
|
|
85
|
+
/plugins\s*:\s*\[[\s\S]*tailwindcss\(\)[\s\S]*autoprefixer\(/s.test(
|
|
86
|
+
content,
|
|
87
|
+
),
|
|
90
88
|
);
|
|
91
89
|
}
|
|
92
90
|
|
|
@@ -211,7 +209,11 @@ function isStandardPostcssConfig(content) {
|
|
|
211
209
|
|
|
212
210
|
export function patchWorkspacePostcssConfig(content) {
|
|
213
211
|
if (isWorkspacePostcssConfigCurrent(content)) return content;
|
|
214
|
-
if (
|
|
212
|
+
if (
|
|
213
|
+
!content.trim() ||
|
|
214
|
+
isStandardPostcssConfig(content) ||
|
|
215
|
+
content.includes("createOpenXiangdaNamespaceCssPlugin")
|
|
216
|
+
) {
|
|
215
217
|
return canonicalPostcssConfig;
|
|
216
218
|
}
|
|
217
219
|
return content;
|
|
@@ -58,8 +58,10 @@ describe("workspace Tailwind config helpers", () => {
|
|
|
58
58
|
expect(cssContent).toContain("@tailwind base;");
|
|
59
59
|
expect(cssContent).toContain("@tailwind components;");
|
|
60
60
|
expect(cssContent).toContain("@tailwind utilities;");
|
|
61
|
-
expect(postcssContent).toContain("
|
|
62
|
-
expect(postcssContent).toContain(
|
|
61
|
+
expect(postcssContent).toContain("tailwindcss()");
|
|
62
|
+
expect(postcssContent).toContain("autoprefixer()");
|
|
63
|
+
expect(postcssContent).not.toContain("createOpenXiangdaNamespaceCssPlugin");
|
|
64
|
+
expect(postcssContent).not.toContain('require("openxiangda/build")');
|
|
63
65
|
expect(cssContent).not.toContain('@import "openxiangda/styles/tokens.css";');
|
|
64
66
|
expect(validateWorkspaceTailwindConfig(workspaceRoot)).toEqual([]);
|
|
65
67
|
});
|
|
@@ -176,7 +178,7 @@ module.exports = {
|
|
|
176
178
|
expect(nextContent).toContain("#root");
|
|
177
179
|
});
|
|
178
180
|
|
|
179
|
-
it("patches standard PostCSS config
|
|
181
|
+
it("patches standard PostCSS config without the namespace css plugin", () => {
|
|
180
182
|
const nextContent = patchWorkspacePostcssConfig(`module.exports = {
|
|
181
183
|
plugins: {
|
|
182
184
|
tailwindcss: {},
|
|
@@ -185,9 +187,9 @@ module.exports = {
|
|
|
185
187
|
};
|
|
186
188
|
`);
|
|
187
189
|
|
|
188
|
-
expect(nextContent).toContain("createOpenXiangdaNamespaceCssPlugin");
|
|
189
190
|
expect(nextContent).toContain("tailwindcss()");
|
|
190
191
|
expect(nextContent).toContain("autoprefixer()");
|
|
192
|
+
expect(nextContent).not.toContain("createOpenXiangdaNamespaceCssPlugin");
|
|
191
193
|
});
|
|
192
194
|
|
|
193
195
|
it("preserves custom PostCSS config when it cannot be patched safely", () => {
|
|
@@ -460,6 +460,22 @@ function rewriteLocalSdkConfig(workspaceRoot) {
|
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
+
function rewriteCssIsolationDefault(workspaceRoot) {
|
|
464
|
+
const configPath = join(workspaceRoot, "app-workspace.config.ts");
|
|
465
|
+
if (!existsSync(configPath)) return;
|
|
466
|
+
replaceInFile(configPath, (content) =>
|
|
467
|
+
content.replace(
|
|
468
|
+
/cssIsolation:\s*\n\s*process\.env\.APP_PAGE_CSS_ISOLATION === ["']none["']\s*\n\s*\?\s*["']none["']\s*\n\s*:\s*process\.env\.APP_PAGE_CSS_ISOLATION === ["']shadow["']\s*\n\s*\?\s*["']shadow["']\s*\n\s*:\s*["']namespace["']/g,
|
|
469
|
+
`cssIsolation:
|
|
470
|
+
process.env.APP_PAGE_CSS_ISOLATION === "namespace"
|
|
471
|
+
? "namespace"
|
|
472
|
+
: process.env.APP_PAGE_CSS_ISOLATION === "shadow"
|
|
473
|
+
? "shadow"
|
|
474
|
+
: "none"`,
|
|
475
|
+
),
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
463
479
|
function walkFiles(root, onFile) {
|
|
464
480
|
for (const entry of readdirSync(root)) {
|
|
465
481
|
if (ignoredDirs.has(entry)) continue;
|
|
@@ -675,6 +691,7 @@ async function updateWorkspace(argv, { migrate = false } = {}) {
|
|
|
675
691
|
|
|
676
692
|
updatePackageJson(workspaceRoot, channel);
|
|
677
693
|
rewriteImports(workspaceRoot);
|
|
694
|
+
rewriteCssIsolationDefault(workspaceRoot);
|
|
678
695
|
ensureWorkspaceTailwindConfig(workspaceRoot);
|
|
679
696
|
ensureWrapperScripts(workspaceRoot);
|
|
680
697
|
rewriteLocalSdkConfig(workspaceRoot);
|
|
@@ -21,11 +21,11 @@ export default defineAppWorkspaceConfig({
|
|
|
21
21
|
protocolVersion: process.env.APP_PAGE_PROTOCOL_VERSION || "1.0",
|
|
22
22
|
frameworkVersion: process.env.APP_FRAMEWORK_VERSION || "18.3.1",
|
|
23
23
|
cssIsolation:
|
|
24
|
-
process.env.APP_PAGE_CSS_ISOLATION === "
|
|
25
|
-
? "
|
|
24
|
+
process.env.APP_PAGE_CSS_ISOLATION === "namespace"
|
|
25
|
+
? "namespace"
|
|
26
26
|
: process.env.APP_PAGE_CSS_ISOLATION === "shadow"
|
|
27
27
|
? "shadow"
|
|
28
|
-
: "
|
|
28
|
+
: "none",
|
|
29
29
|
formMenuParentId: process.env.APP_FORM_MENU_PARENT_ID || "",
|
|
30
30
|
formMenuIcon: process.env.APP_FORM_MENU_ICON || "",
|
|
31
31
|
pageMenuParentId: process.env.APP_PAGE_MENU_PARENT_ID || "",
|
|
@@ -42,8 +42,9 @@ Frontend code must not be responsible for data isolation or background jobs.
|
|
|
42
42
|
For dynamic multi-role apps, create a business role table and synchronize it to
|
|
43
43
|
platform app roles. User-facing scope fields should be maintainable controls:
|
|
44
44
|
personnel/department fields for people and orgs, enum fields for fixed option
|
|
45
|
-
sets, and
|
|
46
|
-
other records maintained by forms.
|
|
47
|
-
|
|
45
|
+
sets, and SDK-backed `SelectField` dropdowns for classes, colleges, projects,
|
|
46
|
+
customers, and other records maintained by forms. Use `remoteSearch: true` when
|
|
47
|
+
the source form can have many records. If the platform permission condition
|
|
48
|
+
needs a scalar value, derive a hidden scope key such as `collegeScopeKey` or
|
|
48
49
|
`ownerDeptScopeKey`. Use form permission groups with condition-style data
|
|
49
50
|
permissions to enforce data isolation.
|