openxiangda 1.0.55 → 1.0.56
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/openxiangda-skills/skills/openxiangda-app/SKILL.md +3 -0
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +16 -0
- package/openxiangda-skills/skills/openxiangda-form/SKILL.md +9 -1
- package/package.json +1 -1
- package/packages/sdk/dist/build/index.cjs.map +1 -1
- package/packages/sdk/dist/build/index.d.mts +4 -1
- package/packages/sdk/dist/build/index.d.ts +4 -1
- package/packages/sdk/dist/build/index.mjs.map +1 -1
- package/packages/sdk/src/build-source/scripts/publish-all.mjs +19 -4
- package/packages/sdk/src/build-source/scripts/utils/load-config.mjs +10 -0
- package/templates/openxiangda-react-spa/app-workspace.config.ts +1 -0
- package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +5 -0
|
@@ -65,6 +65,8 @@ openxiangda runtime deploy --profile <name>
|
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
`runtime deploy` builds and activates the app-level SPA release for `/view/:appType/*`.
|
|
68
|
+
Publish React SPA forms separately with `openxiangda workspace publish --form <formCode>`;
|
|
69
|
+
with `runtimeMode: "react-spa"` this is schema-only and does not require OSS.
|
|
68
70
|
|
|
69
71
|
Bind to an existing app only when the user explicitly provides `appType` or asks to reuse an existing platform app:
|
|
70
72
|
|
|
@@ -99,6 +101,7 @@ openxiangda workspace bind --profile <name> --app-type APP_XXX
|
|
|
99
101
|
- Use logical local codes for forms, pages, workflows, automations, and menus.
|
|
100
102
|
- Before scaffolding a new business app, read `../../references/best-practices.md` and pick an architecture from the initialized `examples/best-practices/` catalog. Do not generate a large single-file app page when a template pattern already covers the scenario.
|
|
101
103
|
- For Phase 6 React SPA apps, use `--runtime react-spa`. The generated app owns routing/layouts/menus with React Router and Tailwind CSS; platform permissions still come from backend runtime bootstrap and route checks.
|
|
104
|
+
- React SPA form publish is schema-only by default. Do not add OSS credentials or old embedded form bundles unless explicitly maintaining legacy compatibility with `--legacy-form-bundle`.
|
|
102
105
|
- When publishing after app edits, prefer targeted commands (`workspace publish --changed --dry-run`, then `--changed`, `--page`, `--form`, or `--only`). Do not publish all forms/pages just because one page changed.
|
|
103
106
|
- Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` before changing an existing app.
|
|
104
107
|
|
|
@@ -45,6 +45,20 @@ openxiangda runtime deploy --profile <name>
|
|
|
45
45
|
|
|
46
46
|
`runtime deploy` builds the Vite app with a release-specific asset base, uploads `dist/` to the platform runtime release store, and activates the release unless `--no-activate` is passed.
|
|
47
47
|
|
|
48
|
+
For React SPA forms, still use targeted form publish to create/bind the form and
|
|
49
|
+
sync schema:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
openxiangda workspace publish --profile <name> --form <formCode> --dry-run
|
|
53
|
+
openxiangda workspace publish --profile <name> --form <formCode>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
When `app-workspace.config.ts` has `runtimeMode: "react-spa"`, form publish is
|
|
57
|
+
schema-only by default: it does not build or upload the legacy form page bundle
|
|
58
|
+
to OSS. The React SPA renders form submit/detail/list routes through
|
|
59
|
+
`openxiangda/runtime` default pages. Use `--legacy-form-bundle` only when a
|
|
60
|
+
React SPA app intentionally needs the old embedded form page bundle.
|
|
61
|
+
|
|
48
62
|
## DO NOT
|
|
49
63
|
|
|
50
64
|
- ❌ `pnpm publish:all` / `pnpm publish:oss` / `pnpm register` / `lowcode-workspace publish-*` directly. They are workspace internals and miss `OPENXIANGDA_PROFILE / BASE_URL / ACCESS_TOKEN / APP_TYPE` injection.
|
|
@@ -147,6 +161,8 @@ menus, and route permission checks. Do not publish React SPA pages through the
|
|
|
147
161
|
old custom-page app-shell path unless explicitly maintaining a legacy app.
|
|
148
162
|
Use `openxiangda resource publish` for roles/menus/page permission groups and
|
|
149
163
|
`openxiangda runtime deploy` for the SPA frontend release.
|
|
164
|
+
Use `openxiangda workspace publish --form <formCode>` only for schema/form shell
|
|
165
|
+
sync; it is schema-only by default and does not require OSS credentials.
|
|
150
166
|
|
|
151
167
|
Local workspace state is authoritative. If `.openxiangda/state.json` already contains an app binding for the target profile, use it. If there is no local binding, create a new app with `workspace init --app-name`; do not search platform apps for similar names.
|
|
152
168
|
|
|
@@ -85,6 +85,14 @@ openxiangda workspace publish --profile <name> --form <formCode>
|
|
|
85
85
|
|
|
86
86
|
The workspace publish script receives `OPENXIANGDA_BASE_URL`, `OPENXIANGDA_ACCESS_TOKEN`, and `OPENXIANGDA_APP_TYPE` from the CLI. It creates/binds the platform form shell only when needed, syncs form metadata, builds the React form page bundle, uploads assets, and registers the bundle.
|
|
87
87
|
|
|
88
|
+
For Phase 6 React SPA workspaces, `app-workspace.config.ts` should declare
|
|
89
|
+
`runtimeMode: "react-spa"`. In that mode, `workspace publish --form <code>` is
|
|
90
|
+
schema-only by default: it creates/binds the form and syncs schema, but skips
|
|
91
|
+
the legacy form page bundle, OSS upload, and bundle register. The app-level
|
|
92
|
+
React runtime renders submit/detail/list pages. Pass `--legacy-form-bundle`
|
|
93
|
+
only when intentionally maintaining compatibility with an old embedded form
|
|
94
|
+
page bundle.
|
|
95
|
+
|
|
88
96
|
## Low-level Commands
|
|
89
97
|
|
|
90
98
|
Use these only for diagnosis, binding existing resources, or repairing a broken publish state:
|
|
@@ -131,7 +139,7 @@ Read these references only when writing or reviewing schema:
|
|
|
131
139
|
- Put validation on field-level `rules`; never generate old top-level validation arrays under `schema.rules`. Top-level `rules` is only for `FormEffect[]` with `when` and `then`.
|
|
132
140
|
- Always provide `options` for option components. `SelectField`, `MultiSelectField`, `RadioField`, `CheckboxField`, and `CascadeSelectField` must not be emitted with `options` undefined. See `../../references/component-guide.md` for the full list and `../../references/platform-data-model.md` for the `{label, value}` storage contract.
|
|
133
141
|
- Do not emit raw native form controls in app/workspace source. For basic entry use `TextField`, `TextAreaField`, `NumberField`, `DateField`, `SelectField`, `RadioField`, etc.; for platform-specific entry use `UserSelectField`, `DepartmentSelectField`, `AttachmentField`, `ImageField`, `EditorField`, `DigitalSignatureField`, and `LocationField`.
|
|
134
|
-
- Workflow form pages use the same workspace form structure plus workflow v3 configuration
|
|
142
|
+
- Workflow form pages use the same workspace form structure plus workflow v3 configuration. In legacy workspaces, publish the form page bundle through workspace publish before treating it as complete. In React SPA workspaces, `workspace publish --form` syncs schema only, and the SPA default process pages render through `runtime deploy`.
|
|
135
143
|
- After editing one form, use `workspace publish --form <formCode>` or preview `--changed --dry-run`; do not run full workspace publish by default.
|
|
136
144
|
- Use `openxiangda form pull` or app snapshot before overwriting an existing form.
|
|
137
145
|
- Before assuming a value shape (e.g. dates, attachments, member fields, option fields), verify against `../../references/platform-data-model.md` instead of guessing.
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string };\n pages?: { dir?: string };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCO,SAAS,yBAAuD,QAAc;AACnF,SAAO;AACT;AAEA,IAAM,2BAA2B;AAEjC,SAAS,kBAAkB,UAAkB;AAC3C,QAAM,YAAsB,CAAC;AAC7B,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO;AACT,iBAAW;AACX,UAAI,SAAS,MAAO,SAAQ;AAC5B;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,KAAK;AAChC,cAAQ;AACR,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,UAAU,GAAG;AAC/B,gBAAU,KAAK,QAAQ,KAAK,CAAC;AAC7B,gBAAU;AACV;AAAA,IACF;AACA,eAAW;AAAA,EACb;AACA,MAAI,QAAQ,KAAK,EAAG,WAAU,KAAK,QAAQ,KAAK,CAAC;AACjD,SAAO;AACT;AAEA,SAAS,oBAAoB,UAAkB,QAAgB;AAC7D,MAAI,CAAC,YAAY,SAAS,SAAS,MAAM,EAAG,QAAO;AACnD,MAAI,gCAAgC,KAAK,QAAQ,EAAG,QAAO;AAC3D,MAAI,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACnD,MAAI,+BAA+B,KAAK,QAAQ,EAAG,QAAO;AAC1D,SAAO;AACT;AAEA,SAAS,wBAAwB,UAAkB;AACjD,QAAM,UAAU,SACb,QAAQ,6BAA6B,YAAY,EACjD,QAAQ,iCAAiC,gBAAgB,EACzD,QAAQ,oDAAoD,eAAe;AAC9E,SAAO,YAAY,WAAW,OAAO;AACvC;AAEO,SAAS,oCACd,SAAS,0BACT;AACA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAA2C;AAC9C,UAAI,CAAC,KAAK,SAAU;AACpB,UAAI,SAAS,KAAK;AAClB,aAAO,QAAQ;AACb,YAAI,OAAO,SAAS,YAAY,cAAc,KAAK,OAAO,IAAI,GAAG;AAC/D;AAAA,QACF;AACA,iBAAS,OAAO;AAAA,MAClB;AACA,WAAK,WAAW,MAAM;AAAA,QACpB,IAAI;AAAA,UACF,kBAAkB,KAAK,QAAQ,EAAE,QAAQ,CAAC,aAAa;AACrD,kBAAM,iBAAiB,oBAAoB,UAAU,MAAM,IACvD,WACA,GAAG,MAAM,IAAI,QAAQ;AACzB,kBAAM,YAAY,wBAAwB,cAAc;AACxD,mBAAO,YAAY,CAAC,gBAAgB,SAAS,IAAI,CAAC,cAAc;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACF;AAEC,oCAA4C,UAAU;AAEhD,IAAM,2BAA2B;","names":[]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
type CssIsolation = 'namespace' | 'shadow' | 'none';
|
|
2
|
+
type WorkspaceRuntimeMode = 'legacy' | 'react-spa';
|
|
2
3
|
interface AppWorkspaceConfig {
|
|
3
4
|
appType?: string;
|
|
4
5
|
appName?: string;
|
|
6
|
+
runtimeMode?: WorkspaceRuntimeMode;
|
|
5
7
|
platformUrl?: string;
|
|
6
8
|
servicePrefix?: string;
|
|
7
9
|
appKey?: string;
|
|
@@ -30,6 +32,7 @@ interface AppWorkspaceConfig {
|
|
|
30
32
|
};
|
|
31
33
|
forms?: {
|
|
32
34
|
dir?: string;
|
|
35
|
+
publishLegacyBundle?: boolean;
|
|
33
36
|
};
|
|
34
37
|
pages?: {
|
|
35
38
|
dir?: string;
|
|
@@ -45,4 +48,4 @@ declare function createOpenXiangdaNamespaceCssPlugin(prefix?: string): {
|
|
|
45
48
|
};
|
|
46
49
|
declare const createNamespaceCssPlugin: typeof createOpenXiangdaNamespaceCssPlugin;
|
|
47
50
|
|
|
48
|
-
export { type AppWorkspaceConfig, type CssIsolation, createNamespaceCssPlugin, createOpenXiangdaNamespaceCssPlugin, defineAppWorkspaceConfig };
|
|
51
|
+
export { type AppWorkspaceConfig, type CssIsolation, type WorkspaceRuntimeMode, createNamespaceCssPlugin, createOpenXiangdaNamespaceCssPlugin, defineAppWorkspaceConfig };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
type CssIsolation = 'namespace' | 'shadow' | 'none';
|
|
2
|
+
type WorkspaceRuntimeMode = 'legacy' | 'react-spa';
|
|
2
3
|
interface AppWorkspaceConfig {
|
|
3
4
|
appType?: string;
|
|
4
5
|
appName?: string;
|
|
6
|
+
runtimeMode?: WorkspaceRuntimeMode;
|
|
5
7
|
platformUrl?: string;
|
|
6
8
|
servicePrefix?: string;
|
|
7
9
|
appKey?: string;
|
|
@@ -30,6 +32,7 @@ interface AppWorkspaceConfig {
|
|
|
30
32
|
};
|
|
31
33
|
forms?: {
|
|
32
34
|
dir?: string;
|
|
35
|
+
publishLegacyBundle?: boolean;
|
|
33
36
|
};
|
|
34
37
|
pages?: {
|
|
35
38
|
dir?: string;
|
|
@@ -45,4 +48,4 @@ declare function createOpenXiangdaNamespaceCssPlugin(prefix?: string): {
|
|
|
45
48
|
};
|
|
46
49
|
declare const createNamespaceCssPlugin: typeof createOpenXiangdaNamespaceCssPlugin;
|
|
47
50
|
|
|
48
|
-
export { type AppWorkspaceConfig, type CssIsolation, createNamespaceCssPlugin, createOpenXiangdaNamespaceCssPlugin, defineAppWorkspaceConfig };
|
|
51
|
+
export { type AppWorkspaceConfig, type CssIsolation, type WorkspaceRuntimeMode, createNamespaceCssPlugin, createOpenXiangdaNamespaceCssPlugin, defineAppWorkspaceConfig };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string };\n pages?: { dir?: string };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";AAqCO,SAAS,yBAAuD,QAAc;AACnF,SAAO;AACT;AAEA,IAAM,2BAA2B;AAEjC,SAAS,kBAAkB,UAAkB;AAC3C,QAAM,YAAsB,CAAC;AAC7B,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO;AACT,iBAAW;AACX,UAAI,SAAS,MAAO,SAAQ;AAC5B;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,KAAK;AAChC,cAAQ;AACR,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,UAAU,GAAG;AAC/B,gBAAU,KAAK,QAAQ,KAAK,CAAC;AAC7B,gBAAU;AACV;AAAA,IACF;AACA,eAAW;AAAA,EACb;AACA,MAAI,QAAQ,KAAK,EAAG,WAAU,KAAK,QAAQ,KAAK,CAAC;AACjD,SAAO;AACT;AAEA,SAAS,oBAAoB,UAAkB,QAAgB;AAC7D,MAAI,CAAC,YAAY,SAAS,SAAS,MAAM,EAAG,QAAO;AACnD,MAAI,gCAAgC,KAAK,QAAQ,EAAG,QAAO;AAC3D,MAAI,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACnD,MAAI,+BAA+B,KAAK,QAAQ,EAAG,QAAO;AAC1D,SAAO;AACT;AAEA,SAAS,wBAAwB,UAAkB;AACjD,QAAM,UAAU,SACb,QAAQ,6BAA6B,YAAY,EACjD,QAAQ,iCAAiC,gBAAgB,EACzD,QAAQ,oDAAoD,eAAe;AAC9E,SAAO,YAAY,WAAW,OAAO;AACvC;AAEO,SAAS,oCACd,SAAS,0BACT;AACA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAA2C;AAC9C,UAAI,CAAC,KAAK,SAAU;AACpB,UAAI,SAAS,KAAK;AAClB,aAAO,QAAQ;AACb,YAAI,OAAO,SAAS,YAAY,cAAc,KAAK,OAAO,IAAI,GAAG;AAC/D;AAAA,QACF;AACA,iBAAS,OAAO;AAAA,MAClB;AACA,WAAK,WAAW,MAAM;AAAA,QACpB,IAAI;AAAA,UACF,kBAAkB,KAAK,QAAQ,EAAE,QAAQ,CAAC,aAAa;AACrD,kBAAM,iBAAiB,oBAAoB,UAAU,MAAM,IACvD,WACA,GAAG,MAAM,IAAI,QAAQ;AACzB,kBAAM,YAAY,wBAAwB,cAAc;AACxD,mBAAO,YAAY,CAAC,gBAAgB,SAAS,IAAI,CAAC,cAAc;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACF;AAEC,oCAA4C,UAAU;AAEhD,IAAM,2BAA2B;","names":[]}
|
|
@@ -10,7 +10,7 @@ import { createRequire } from "node:module";
|
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import minimist from "minimist";
|
|
13
|
-
import { rootDir } from "./utils/load-config.mjs";
|
|
13
|
+
import { loadConfig, rootDir } from "./utils/load-config.mjs";
|
|
14
14
|
import {
|
|
15
15
|
discoverWorkspaceModules,
|
|
16
16
|
commitIncrementalPublish,
|
|
@@ -55,6 +55,7 @@ publish-all - 同步、构建、上传并注册应用工作区产物
|
|
|
55
55
|
--changed 只发布 git 变更触达的 src/forms/* 和 src/pages/*
|
|
56
56
|
--since <ref> 配合 --changed 使用,默认 HEAD
|
|
57
57
|
--force 忽略增量缓存,强制发布
|
|
58
|
+
--legacy-form-bundle React SPA 下仍发布旧表单 bundle 到 OSS
|
|
58
59
|
--help, -h 显示帮助信息
|
|
59
60
|
`);
|
|
60
61
|
process.exit(0);
|
|
@@ -137,6 +138,11 @@ function run(script, scriptArgs = []) {
|
|
|
137
138
|
|
|
138
139
|
const maybeDryRun = dryRun ? ["--dry-run"] : [];
|
|
139
140
|
const maybeForce = force ? ["--force"] : [];
|
|
141
|
+
const config = await loadConfig();
|
|
142
|
+
const publishLegacyFormBundle =
|
|
143
|
+
config.runtimeMode !== "react-spa" ||
|
|
144
|
+
Boolean(args["legacy-form-bundle"]) ||
|
|
145
|
+
Boolean(config.forms?.publishLegacyBundle);
|
|
140
146
|
|
|
141
147
|
const allModules = discoverWorkspaceModules();
|
|
142
148
|
const plan = planIncrementalPublish(allModules, { force, only });
|
|
@@ -155,9 +161,18 @@ const publishedModules = [];
|
|
|
155
161
|
for (const moduleItem of plan.changed) {
|
|
156
162
|
if (moduleItem.kind === "forms") {
|
|
157
163
|
await run("sync-schema.mjs", ["--form", moduleItem.name, ...maybeDryRun]);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
if (publishLegacyFormBundle) {
|
|
165
|
+
await run("build-forms.mjs", ["--form", moduleItem.name, ...maybeForce]);
|
|
166
|
+
await run("publish-oss.mjs", ["--form", moduleItem.name, ...maybeDryRun]);
|
|
167
|
+
await run("register.mjs", ["--form", moduleItem.name, ...maybeDryRun]);
|
|
168
|
+
} else {
|
|
169
|
+
console.log(
|
|
170
|
+
`[publish] React SPA 表单 ${moduleItem.name}: 已同步 schema,跳过旧表单 bundle/OSS/register。`,
|
|
171
|
+
);
|
|
172
|
+
console.log(
|
|
173
|
+
"[publish] 如需兼容旧表单页面 bundle,请传 --legacy-form-bundle 或配置 forms.publishLegacyBundle=true。",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
161
176
|
publishedModules.push(moduleItem);
|
|
162
177
|
} else if (moduleItem.kind === "pages") {
|
|
163
178
|
await run("build-pages.mjs", ["--page", moduleItem.name, ...maybeForce]);
|
|
@@ -121,6 +121,11 @@ function normalizeCssIsolation(value) {
|
|
|
121
121
|
return "none";
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
function normalizeRuntimeMode(value) {
|
|
125
|
+
if (value === "react-spa" || value === "spa") return "react-spa";
|
|
126
|
+
return value === "legacy" ? "legacy" : "";
|
|
127
|
+
}
|
|
128
|
+
|
|
124
129
|
export function resolveOpenXiangdaEndpointConfig(baseUrl) {
|
|
125
130
|
const raw = normalizeBaseUrl(baseUrl);
|
|
126
131
|
if (!raw) {
|
|
@@ -174,6 +179,11 @@ export async function loadConfig() {
|
|
|
174
179
|
...source,
|
|
175
180
|
appType: process.env.OPENXIANGDA_APP_TYPE || source.appType || process.env.APP_TYPE,
|
|
176
181
|
appName: source.appName || process.env.APP_NAME || "低代码应用",
|
|
182
|
+
runtimeMode: normalizeRuntimeMode(
|
|
183
|
+
process.env.OPENXIANGDA_RUNTIME_MODE ||
|
|
184
|
+
process.env.APP_RUNTIME_MODE ||
|
|
185
|
+
source.runtimeMode,
|
|
186
|
+
),
|
|
177
187
|
platformUrl,
|
|
178
188
|
servicePrefix:
|
|
179
189
|
openXiangdaEndpoint?.servicePrefix ??
|
|
@@ -3,6 +3,7 @@ import { defineAppWorkspaceConfig } from "openxiangda/build";
|
|
|
3
3
|
export default defineAppWorkspaceConfig({
|
|
4
4
|
appType: process.env.APP_TYPE || process.env.OPENXIANGDA_APP_TYPE || "APP_XXXXXXXXXXXXXXXX",
|
|
5
5
|
appName: process.env.APP_NAME || "OpenXiangda React SPA",
|
|
6
|
+
runtimeMode: "react-spa",
|
|
6
7
|
platformUrl:
|
|
7
8
|
process.env.APP_PLATFORM_URL ||
|
|
8
9
|
process.env.OPENXIANGDA_BASE_URL ||
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface AppWorkspaceConfig {
|
|
2
2
|
appType: string;
|
|
3
3
|
appName: string;
|
|
4
|
+
runtimeMode?: "legacy" | "react-spa";
|
|
4
5
|
platformUrl: string;
|
|
5
6
|
servicePrefix: string;
|
|
6
7
|
appKey: string;
|
|
@@ -24,6 +25,10 @@ export interface AppWorkspaceConfig {
|
|
|
24
25
|
pageMenuParentId: string;
|
|
25
26
|
pageMenuIcon: string;
|
|
26
27
|
};
|
|
28
|
+
forms?: {
|
|
29
|
+
dir?: string;
|
|
30
|
+
publishLegacyBundle?: boolean;
|
|
31
|
+
};
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export type CustomPageEntryMode = "app-shell" | "plain-page";
|