openyida 2026.9.17 → 2026.9.18-1
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 +7 -6
- package/bin/yida.js +7 -3
- package/lib/app/canvas-compile.js +12 -12
- package/lib/app/create-form.js +8 -16
- package/lib/app/create-page.js +84 -17
- package/lib/app/custom-theme.js +11 -0
- package/lib/app/form-field-validator.js +26 -5
- package/lib/app/form-open-guard.js +97 -10
- package/lib/app/get-form-config.js +1 -0
- package/lib/app/inline-css-guard.js +1 -31
- package/lib/app/theme-from-design.js +110 -39
- package/lib/asset/ai-image.js +2 -0
- package/lib/asset/asset-cmd.js +4 -1
- package/lib/asset/asset-plan.js +36 -5
- package/lib/asset/asset-resolve.js +4 -0
- package/lib/core/agent-capabilities.js +2 -0
- package/lib/core/command-manifest.js +121 -15
- package/lib/core/css-structure.js +37 -0
- package/lib/core/locales/en.js +31 -10
- package/lib/core/locales/zh.js +31 -10
- package/lib/core/sample.js +10 -2
- package/lib/design/check-design.js +51 -0
- package/lib/design/document.js +457 -0
- package/lib/design/icon-contrast.js +85 -0
- package/lib/design/page-content.js +11 -0
- package/lib/design-plan/artifact-updates.js +117 -0
- package/lib/design-plan/entry-navigation.js +2 -2
- package/lib/design-plan/init.js +27 -17
- package/lib/design-plan/materialize.js +291 -274
- package/lib/design-plan/normalize.js +10 -16
- package/lib/design-plan/patch.js +8 -3
- package/lib/design-plan/preview.js +3 -6
- package/lib/design-plan/themes.js +86 -0
- package/lib/design-plan/validate.js +26 -6
- package/lib/design-plan/visual-policy.js +1 -1
- package/lib/process/create-process.js +84 -50
- package/package.json +2 -1
- package/yida-skills/SKILL.md +4 -3
- package/yida-skills/skills/yida-app/SKILL.md +4 -4
- package/yida-skills/skills/yida-app/references/entry-navigation.md +25 -5
- package/yida-skills/skills/yida-app/workflow/parallel-work.md +4 -4
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +8 -2
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +5 -3
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +6 -4
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-6-main-page.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +5 -5
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +12 -9
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +6 -4
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +5 -5
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +43 -35
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +1 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/component-library-guide.md +2 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +2 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +21 -21
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +7 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +4 -4
- package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +3 -3
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +50 -9
- package/yida-skills/skills/yida-create-page/SKILL.md +14 -6
- package/yida-skills/skills/yida-create-process/SKILL.md +13 -2
- package/yida-skills/skills/yida-dashboard/SKILL.md +5 -4
- package/yida-skills/skills/yida-data-management/SKILL.md +4 -2
- package/yida-skills/skills/yida-data-management/references/data-format-guide.md +4 -4
- package/yida-skills/skills/yida-design/SKILL.md +17 -10
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +109 -0
- package/yida-skills/skills/yida-design/references/design-mode.md +1 -1
- package/yida-skills/skills/yida-design/references/navigation-decision.md +2 -1
- package/yida-skills/skills/yida-design/references/page-continuity.md +1 -3
- package/yida-skills/skills/yida-design/references/page-quality-gates.md +26 -38
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +243 -434
- package/yida-skills/skills/yida-design/references/theme-selection.md +66 -0
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +19 -120
- package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +15 -27
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +259 -0
- package/yida-skills/skills/yida-design/sub_skill/page-design/SKILL.md +4 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +11 -7
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/README.md +14 -10
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/build-plan-template.html +9 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +13 -14
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +6 -5
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +32 -119
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +12 -29
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +15 -48
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +68 -0
- package/yida-skills/skills/yida-design/{sub_skill/yida-design-plan/templates → templates}/design-themes/basic-tokens.json +21 -5
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline.md +333 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines.md +324 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels.md +324 -0
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid.md +321 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands.md +308 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks.md +357 -0
- package/yida-skills/skills/yida-design/templates/design-themes/index.json +96 -0
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels.md +325 -0
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm.md +350 -0
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone.md +335 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces.md +320 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm.md +417 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band.md +340 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels.md +337 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels.md +354 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels.md +331 -0
- package/yida-skills/skills/yida-design/workflow/output-design.md +115 -378
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +21 -26
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +2 -0
- package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +22 -106
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +15 -22
- package/yida-skills/skills/yida-image-assets/SKILL.md +1 -1
- package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +31 -0
- package/yida-skills/skills/yida-image-assets/references/source-policy.md +1 -1
- package/yida-skills/skills/yida-nav-shell/SKILL.md +2 -2
- package/yida-skills/skills/yida-page-config/SKILL.md +1 -1
- package/yida-skills/skills/yida-prd/SKILL.md +2 -2
- package/yida-skills/skills/yida-prd/references/app/blueprint.md +3 -3
- package/yida-skills/skills/yida-prd/references/app/role-journey.md +5 -3
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -1
- package/yida-skills/skills/yida-prd/workflow/plan-business.md +2 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +3 -2
- package/yida-skills/skills/yida-publish-page/SKILL.md +5 -1
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +20 -0
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +1 -1
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +2 -2
- package/yida-skills/skills-index.json +30 -10
- package/yida-skills/skills/yida-design/references/style-design-selection.md +0 -149
- package/yida-skills/skills/yida-design/references/style-designs/_design-md-template.md +0 -185
- package/yida-skills/skills/yida-design/references/style-designs/aqua-service-progress-dashboard.md +0 -242
- package/yida-skills/skills/yida-design/references/style-designs/blue-insight-operations-dashboard.md +0 -243
- package/yida-skills/skills/yida-design/references/style-designs/blue-productivity-insight-workbench.md +0 -242
- package/yida-skills/skills/yida-design/references/style-designs/command-filter-card-console.md +0 -304
- package/yida-skills/skills/yida-design/references/style-designs/contrast-command-analytics-workbench.md +0 -261
- package/yida-skills/skills/yida-design/references/style-designs/dark-stage-analytic-dashboard.md +0 -369
- package/yida-skills/skills/yida-design/references/style-designs/filterable-card-catalog.md +0 -354
- package/yida-skills/skills/yida-design/references/style-designs/green-timeline-progress-workbench.md +0 -250
- package/yida-skills/skills/yida-design/references/style-designs/registry.md +0 -117
- package/yida-skills/skills/yida-design/references/style-designs/soft-analytic-workbench.md +0 -348
- package/yida-skills/skills/yida-design/references/style-designs/soft-blue-grid-analytic-dashboard.md +0 -658
- package/yida-skills/skills/yida-design/references/style-designs/soft-bordered-analytic-workbench.md +0 -385
- package/yida-skills/skills/yida-design/references/style-designs/soft-curated-filter-gallery.md +0 -300
- package/yida-skills/skills/yida-design/references/style-designs/soft-modular-analytic-workbench.md +0 -404
- package/yida-skills/skills/yida-design/references/style-designs/soft-progress-analytics-workbench.md +0 -382
- package/yida-skills/skills/yida-design/references/style-designs/soft-timeline-analytics-workbench.md +0 -367
- package/yida-skills/skills/yida-design/references/style-designs/teal-rail-analytics-workbench.md +0 -341
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/validate_design_themes.py +0 -219
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/README.md +0 -110
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +0 -338
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +0 -330
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +0 -320
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +0 -341
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +0 -327
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +0 -369
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +0 -345
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +0 -378
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +0 -339
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/index.json +0 -329
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +0 -343
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +0 -337
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +0 -381
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +0 -340
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +0 -341
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +0 -374
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +0 -378
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +0 -380
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +0 -339
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +0 -358
package/README.md
CHANGED
|
@@ -399,8 +399,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
399
399
|
| `openyida design-plan catalog [--json]` | List available planning themes and page patterns |
|
|
400
400
|
| `openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]` | Initialize a plan draft from confirmed requirements |
|
|
401
401
|
| `openyida design-plan preview <build-plan.json> --part-file <module.json> [--json]` | Update plan drafts by module |
|
|
402
|
-
| `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--check] [--json]` | Generate
|
|
403
|
-
| `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch
|
|
402
|
+
| `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--check] [--json]` | Generate or update plan documents and theme with consistency checks |
|
|
403
|
+
| `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch plan fields and optionally sync documents and theme |
|
|
404
404
|
| `openyida update-app <appType> [--name "..."] [--desc "..."] [--icon <name>] [--icon-color <color>] [--colour <key>] [--theme-color <color>] [--theme-file <css>] [--nav-theme light\|dark\|white\|gray] [--logo-source appIcon\|customImage] [--layout side\|top\|l_shape] [--hide-app-nav\|--show-app-nav]` | Update app info |
|
|
405
405
|
| `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
|
|
406
406
|
| `openyida app-offline <appType> [--to-ding-app-center] [--show-app-center]` | Disable a Yida app |
|
|
@@ -430,12 +430,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
430
430
|
| `openyida aggregate-table <list\|create-empty\|inspect\|preview\|save\|publish\|status> <appType> ...` | Manage aggregate tables (virtualView) |
|
|
431
431
|
| `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
|
|
432
432
|
| `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check PRD page/resource count risk |
|
|
433
|
+
| `openyida check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]` | Validate design documents, theme tokens and PRD handoff |
|
|
433
434
|
| `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
|
|
434
435
|
| `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
|
|
435
436
|
| `openyida build-page <sourceFile> [--output file\|--write]` | Build Yida-compatible page source |
|
|
436
|
-
| `openyida check-page <src> [--compat]` | Check custom page standards |
|
|
437
|
-
| `openyida compile <src> [--canvas] [--json]` | Compile custom page locally |
|
|
438
|
-
| `openyida publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--auto-nav-order] [--open\|--no-open]` | Compile and publish custom page |
|
|
437
|
+
| `openyida check-page <src> [--compat] [--json]` | Check custom page standards |
|
|
438
|
+
| `openyida compile <src> [--canvas] [--compat] [--skip-lint] [--json]` | Compile custom page locally |
|
|
439
|
+
| `openyida publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--compat] [--skip-lint] [--auto-nav-order] [--open\|--no-open] [--json]` | Compile and publish custom page |
|
|
439
440
|
| `openyida update-form-config <appType> <formUuid> <true\|false\|keep> "<title>" [--locale zh_CN\|en_US\|ja_JP]` | Update form configuration |
|
|
440
441
|
| `openyida get-form-config <appType> <formUuid> [--json]` | Query form configuration |
|
|
441
442
|
|
|
@@ -458,7 +459,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
458
459
|
| Command | Description |
|
|
459
460
|
|---------|-------------|
|
|
460
461
|
| `openyida configure-process <appType> <formUuid> <definition> [processCode] [--replace]` | Configure and publish process rules; Supports append/forward via JSON nodes[].actions.normalActions/appendActions |
|
|
461
|
-
| `openyida create-process <appType>
|
|
462
|
+
| `openyida create-process <appType> <formTitle> <fieldsJsonFile> <processDefinitionFile> [--replace] \| create-process <appType> --formUuid <formUuid> <processDefinitionFile> [--replace]` | Create process form (all-in-one); Supports append/forward via JSON nodes[].actions.normalActions/appendActions |
|
|
462
463
|
| `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
|
|
463
464
|
| `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
|
|
464
465
|
|
package/bin/yida.js
CHANGED
|
@@ -526,6 +526,7 @@ function printManifestCommandHelp(commandName) {
|
|
|
526
526
|
printCommandUsage(...entries.flatMap((entry) => [
|
|
527
527
|
entry.usage,
|
|
528
528
|
entry.description,
|
|
529
|
+
...(entry.notes || []),
|
|
529
530
|
...(entry.examples || []),
|
|
530
531
|
]));
|
|
531
532
|
return true;
|
|
@@ -798,6 +799,12 @@ async function main() {
|
|
|
798
799
|
break;
|
|
799
800
|
}
|
|
800
801
|
|
|
802
|
+
case 'check-design': {
|
|
803
|
+
const { run } = require('../lib/design/check-design');
|
|
804
|
+
await run(args);
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
|
|
801
808
|
case 'er': {
|
|
802
809
|
const { run } = require('../lib/app/er');
|
|
803
810
|
await run(args);
|
|
@@ -1009,9 +1016,6 @@ async function main() {
|
|
|
1009
1016
|
}
|
|
1010
1017
|
|
|
1011
1018
|
case 'create-process': {
|
|
1012
|
-
if (args.length < 2) {
|
|
1013
|
-
throwCliUsage(t('cli.create_process_usage'), t('cli.create_process_example'));
|
|
1014
|
-
}
|
|
1015
1019
|
const { run: runCreateProcess } = require('../lib/process/create-process');
|
|
1016
1020
|
await runCreateProcess(args);
|
|
1017
1021
|
break;
|
|
@@ -940,29 +940,29 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
940
940
|
},
|
|
941
941
|
});
|
|
942
942
|
}
|
|
943
|
-
const
|
|
944
|
-
if (
|
|
945
|
-
const firstIssue =
|
|
946
|
-
throw new CliError(
|
|
947
|
-
code: '
|
|
943
|
+
const formDetailLinkIssues = findFormDetailLinkIssues(source);
|
|
944
|
+
if (formDetailLinkIssues.length > 0) {
|
|
945
|
+
const firstIssue = formDetailLinkIssues[0];
|
|
946
|
+
throw new CliError(formatFormDetailLinkMessage(firstIssue), {
|
|
947
|
+
code: 'OPENYIDA_CANVAS_FORM_DETAIL_LINK_INVALID',
|
|
948
948
|
details: {
|
|
949
949
|
stage: 'canvas_compile',
|
|
950
950
|
sourcePath: options.sourcePath || '',
|
|
951
951
|
line: firstIssue.line,
|
|
952
|
-
|
|
952
|
+
issueType: firstIssue.type,
|
|
953
953
|
},
|
|
954
954
|
});
|
|
955
955
|
}
|
|
956
|
-
const
|
|
957
|
-
if (
|
|
958
|
-
const firstIssue =
|
|
959
|
-
throw new CliError(
|
|
960
|
-
code: '
|
|
956
|
+
const directFormOpenIssues = findDirectFormOpenIssues(source);
|
|
957
|
+
if (directFormOpenIssues.length > 0) {
|
|
958
|
+
const firstIssue = directFormOpenIssues[0];
|
|
959
|
+
throw new CliError(formatDirectFormOpenMessage(), {
|
|
960
|
+
code: 'OPENYIDA_CANVAS_FORM_OPEN_CONTAINER_REQUIRED',
|
|
961
961
|
details: {
|
|
962
962
|
stage: 'canvas_compile',
|
|
963
963
|
sourcePath: options.sourcePath || '',
|
|
964
964
|
line: firstIssue.line,
|
|
965
|
-
|
|
965
|
+
callee: firstIssue.callee,
|
|
966
966
|
},
|
|
967
967
|
});
|
|
968
968
|
}
|
package/lib/app/create-form.js
CHANGED
|
@@ -101,6 +101,8 @@ const { SMART_VALIDATION_BLOCK_END, SMART_VALIDATION_BLOCK_START } = require('./
|
|
|
101
101
|
const { createFieldNormalizers } = require('./create-form/field-normalizers');
|
|
102
102
|
const {
|
|
103
103
|
validateFormFieldDefinitions: collectFormFieldValidationDiagnostics,
|
|
104
|
+
DIVIDER_TYPES,
|
|
105
|
+
DEFAULT_DIVIDER_TYPE,
|
|
104
106
|
} = require('./form-field-validator');
|
|
105
107
|
|
|
106
108
|
function parseOpenOption(inputArgs) {
|
|
@@ -293,21 +295,6 @@ const FORM_PRESENTATION_TYPE_ALIAS = {
|
|
|
293
295
|
};
|
|
294
296
|
|
|
295
297
|
const FORM_PRESENTATION_COMPONENTS = ['Divider', 'ColumnsLayout', 'Column', 'PageSection'];
|
|
296
|
-
const DIVIDER_TYPE_PRIORITY = [
|
|
297
|
-
'bold-with-thin',
|
|
298
|
-
'double-color-trapezoid',
|
|
299
|
-
'left-dot-title',
|
|
300
|
-
'solid',
|
|
301
|
-
'dashed',
|
|
302
|
-
'thick',
|
|
303
|
-
'dotted',
|
|
304
|
-
];
|
|
305
|
-
const DEFAULT_DIVIDER_TYPE = DIVIDER_TYPE_PRIORITY[0];
|
|
306
|
-
const LEGACY_STRONG_DIVIDER_TYPE = 'multi-parallelograms-end';
|
|
307
|
-
const ALLOWED_DIVIDER_TYPES = new Set([
|
|
308
|
-
...DIVIDER_TYPE_PRIORITY,
|
|
309
|
-
LEGACY_STRONG_DIVIDER_TYPE,
|
|
310
|
-
]);
|
|
311
298
|
|
|
312
299
|
const COMPONENT_ALIAS_META = Symbol('openyida.componentAlias');
|
|
313
300
|
|
|
@@ -574,7 +561,12 @@ function normalizeDividerType(type) {
|
|
|
574
561
|
return DEFAULT_DIVIDER_TYPE;
|
|
575
562
|
}
|
|
576
563
|
const normalized = String(type).trim();
|
|
577
|
-
|
|
564
|
+
if (!DIVIDER_TYPES.includes(normalized)) {
|
|
565
|
+
throwCreateFormError(t('create_form.divider_type_invalid', normalized, DIVIDER_TYPES.join(', ')), 'CREATE_FORM_INVALID_DIVIDER_TYPE', {
|
|
566
|
+
actual: type, expected: DIVIDER_TYPES,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return normalized;
|
|
578
570
|
}
|
|
579
571
|
|
|
580
572
|
function buildDividerComponent(field) {
|
package/lib/app/create-page.js
CHANGED
|
@@ -19,6 +19,7 @@ const { buildYidaI18n, buildYidaTitleI18n, normalizeYidaLocale, resolveContentLo
|
|
|
19
19
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
20
20
|
const { throwCommandError } = require('../core/command-errors');
|
|
21
21
|
const { buildApiPath } = require('./create-form/api-path');
|
|
22
|
+
const { fetchFormSchemaInfo } = require('./get-form-config');
|
|
22
23
|
|
|
23
24
|
function parseArgs(args) {
|
|
24
25
|
const openOption = parseOpenOption(args);
|
|
@@ -91,13 +92,13 @@ function parseBooleanOption(value, optionName) {
|
|
|
91
92
|
throw new Error(`Invalid boolean value for ${optionName}: ${value}`);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function buildPageInfoPostData(formUuid, pageName, isRenderNav, csrfToken) {
|
|
95
|
+
function buildPageInfoPostData(formUuid, pageName, isRenderNav, csrfToken, currentConfig) {
|
|
95
96
|
const titleJson = JSON.stringify(buildYidaTitleI18n(pageName, {
|
|
96
97
|
en_US: pageName,
|
|
97
98
|
ja_JP: pageName,
|
|
98
99
|
}));
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
const payload = {
|
|
101
102
|
_api: 'Form.updateFormSchemaInfo',
|
|
102
103
|
_csrf_token: csrfToken,
|
|
103
104
|
_locale_time_zone_offset: '28800000',
|
|
@@ -125,17 +126,64 @@ function buildPageInfoPostData(formUuid, pageName, isRenderNav, csrfToken) {
|
|
|
125
126
|
displayType: 'RE',
|
|
126
127
|
isRenderNav: isRenderNav ? 'true' : 'false',
|
|
127
128
|
manageCustomActionInfo: '[]',
|
|
128
|
-
}
|
|
129
|
+
};
|
|
130
|
+
// 与 update-form-config 一样,保留当前配置,只覆盖标题和导航。
|
|
131
|
+
for (const key of Object.keys(payload)) {
|
|
132
|
+
if (key.startsWith('_') || ['formUuid', 'title', 'isRenderNav'].includes(key)) {continue;}
|
|
133
|
+
const value = currentConfig?.[key];
|
|
134
|
+
if (value !== undefined && value !== null) {
|
|
135
|
+
payload[key] = typeof value === 'object' ? JSON.stringify(value) : value;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return querystring.stringify(payload);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readRenderNav(config) {
|
|
142
|
+
const value = config && Object.prototype.hasOwnProperty.call(config, 'renderNav')
|
|
143
|
+
? config.renderNav : config?.isRenderNav;
|
|
144
|
+
if (value === false || value === 'false') {return false;}
|
|
145
|
+
if (value === true || value === 'true') {return true;}
|
|
146
|
+
return null;
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
async function configureNavigationVisibility(authRef, appType, pageId, pageName, isRenderNav) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
const verification = { requestedRenderNav: isRenderNav, renderNav: null, verified: false, attempts: 0 };
|
|
151
|
+
let currentConfig = null;
|
|
152
|
+
const readConfig = async () => {
|
|
153
|
+
const response = await fetchFormSchemaInfo(authRef, appType, pageId);
|
|
154
|
+
if (!response || response.success === false || response.__needLogin || response.__csrfExpired) {
|
|
155
|
+
throw new Error(response?.errorMsg || t('common.request_failed'));
|
|
156
|
+
}
|
|
157
|
+
currentConfig = response.content || response.result || response.data || {};
|
|
158
|
+
verification.renderNav = readRenderNav(currentConfig);
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
await readConfig();
|
|
162
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
163
|
+
verification.attempts++;
|
|
164
|
+
// 写入后旧读值不再证明当前状态;回读失败必须报告未知。
|
|
165
|
+
verification.renderNav = null;
|
|
166
|
+
const response = await requestWithAutoLogin((auth) => httpPost(
|
|
167
|
+
auth.baseUrl,
|
|
168
|
+
buildApiPath(appType, 'updateFormSchemaInfo'),
|
|
169
|
+
buildPageInfoPostData(pageId, pageName, isRenderNav, auth.csrfToken, currentConfig)
|
|
170
|
+
), authRef);
|
|
171
|
+
await readConfig();
|
|
172
|
+
if (verification.renderNav === isRenderNav) {
|
|
173
|
+
verification.verified = true;
|
|
174
|
+
return verification;
|
|
175
|
+
}
|
|
176
|
+
if (!response?.success || response.__needLogin || response.__csrfExpired) {
|
|
177
|
+
verification.error = response?.errorMsg || t('common.request_failed');
|
|
178
|
+
return verification;
|
|
179
|
+
}
|
|
180
|
+
// 只补写已创建页面的配置,不重试创建;未知状态也不能当作隐藏成功。
|
|
181
|
+
}
|
|
182
|
+
verification.error = t('create_page.navigation_unverified');
|
|
183
|
+
} catch (err) {
|
|
184
|
+
verification.error = err.message;
|
|
185
|
+
}
|
|
186
|
+
return verification;
|
|
139
187
|
}
|
|
140
188
|
|
|
141
189
|
async function run(args) {
|
|
@@ -213,30 +261,49 @@ async function run(args) {
|
|
|
213
261
|
const workbenchUrl = `${authRef.baseUrl}/${appType}/workbench/${pageId}`;
|
|
214
262
|
let pageUrl = workbenchUrl;
|
|
215
263
|
let dashboardConfigWarning = null;
|
|
264
|
+
let navigationVerification = null;
|
|
216
265
|
|
|
217
266
|
if (options.hideNav) {
|
|
218
267
|
step(3, t('create_page.step_dashboard_config'));
|
|
219
|
-
|
|
220
|
-
if (
|
|
268
|
+
navigationVerification = await configureNavigationVisibility(authRef, appType, pageId, pageName, false);
|
|
269
|
+
if (navigationVerification.verified) {
|
|
221
270
|
chalkSuccess(t('create_page.dashboard_config_ok'));
|
|
222
271
|
pageUrl = `${authRef.baseUrl}/${appType}/custom/${pageId}?isRenderNav=false`;
|
|
223
272
|
} else {
|
|
224
|
-
dashboardConfigWarning =
|
|
273
|
+
dashboardConfigWarning = navigationVerification.error;
|
|
225
274
|
warn(t('create_page.dashboard_config_failed', dashboardConfigWarning));
|
|
226
275
|
}
|
|
227
276
|
}
|
|
228
277
|
|
|
229
|
-
|
|
278
|
+
const complete = !options.hideNav || navigationVerification.verified;
|
|
279
|
+
const result = {
|
|
280
|
+
success: complete, pageCreated: true, pageId, pageName, appType, mode,
|
|
281
|
+
hideNavRequested: options.hideNav,
|
|
282
|
+
hideNav: options.hideNav ? (navigationVerification.renderNav === null ? null : !navigationVerification.renderNav) : false,
|
|
283
|
+
chromeless: options.hideNav && navigationVerification.verified,
|
|
284
|
+
navigationVerification, url: pageUrl, workbenchUrl, dashboardConfigWarning,
|
|
285
|
+
};
|
|
286
|
+
if (!complete) {
|
|
287
|
+
result.errorCode = 'CREATE_PAGE_NAVIGATION_NOT_VERIFIED';
|
|
288
|
+
result.recovery = {
|
|
289
|
+
command: 'update-form-config', args: [appType, pageId, 'false', pageName],
|
|
290
|
+
verifyCommand: 'get-form-config', verifyArgs: [appType, pageId, '--json'],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
chalkResult(complete, complete ? t('create_page.success') : t('create_page.navigation_unverified'), [
|
|
230
294
|
['Page ID', pageId],
|
|
231
295
|
['URL', `${c.cyan}${pageUrl}${c.reset}`],
|
|
232
296
|
]);
|
|
233
297
|
|
|
234
|
-
console.log(JSON.stringify(withBrowserHandoff(
|
|
235
|
-
|
|
298
|
+
console.log(JSON.stringify(complete ? withBrowserHandoff(
|
|
299
|
+
result,
|
|
236
300
|
pageUrl,
|
|
237
301
|
{ stage: 'create_page_success', title: pageName },
|
|
238
302
|
options.openMode
|
|
239
|
-
)));
|
|
303
|
+
) : result));
|
|
304
|
+
if (!complete) {
|
|
305
|
+
throwCommandError(t('create_page.navigation_unverified'), { code: result.errorCode, details: result });
|
|
306
|
+
}
|
|
240
307
|
} else {
|
|
241
308
|
const errorMsg = response ? response.errorMsg || response.error || t('common.unknown_error') : t('common.request_failed');
|
|
242
309
|
const errorCode = response && response.errorCode ? response.errorCode : 'CREATE_PAGE_FAILED';
|
package/lib/app/custom-theme.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { getAccessToken } = require('../auth/token-auth');
|
|
6
|
+
const { CliError } = require('../core/cli-error');
|
|
7
|
+
const { t } = require('../core/i18n');
|
|
8
|
+
const { findCssStructureIssue } = require('../core/css-structure');
|
|
6
9
|
|
|
7
10
|
const MAX_THEME_FILE_BYTES = 20 * 1024 * 1024;
|
|
8
11
|
const REQUIRED_BRAND_SCALE_TOKENS = Object.freeze([
|
|
@@ -147,6 +150,14 @@ function extractThemeColor(cssText) {
|
|
|
147
150
|
|
|
148
151
|
function validateThemeCssContent(cssText) {
|
|
149
152
|
const source = String(cssText || '');
|
|
153
|
+
// Check the original text before stripping comments or rewriting token scopes.
|
|
154
|
+
const issue = findCssStructureIssue(source);
|
|
155
|
+
if (issue) {
|
|
156
|
+
const line = source.slice(0, issue.index).split('\n').length;
|
|
157
|
+
throw new CliError(t('design_document.theme_css_invalid', line), {
|
|
158
|
+
code: 'THEME_CSS_STRUCTURE_INVALID', details: { line, ...issue },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
150
161
|
const sourceWithoutComments = source.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
151
162
|
for (const rule of UNSAFE_THEME_PATTERNS) {
|
|
152
163
|
if (rule.pattern.test(sourceWithoutComments)) {
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { t } = require('../core/i18n');
|
|
4
|
+
|
|
5
|
+
// vc-deep-yida: components/vs-divider-style + components/yida-divider.
|
|
6
|
+
// 这里只定义平台能力,不按业务场景排序;样式由页面设计选择。
|
|
7
|
+
const DIVIDER_TYPES = Object.freeze([
|
|
8
|
+
'solid', 'dashed', 'thick', 'dotted', 'bold-with-thin',
|
|
9
|
+
'solid-center', 'solid-left', 'left-dot-title', 'light-left-bar',
|
|
10
|
+
'light-bar', 'dark-bar', 'light-left-title', 'light-center-title',
|
|
11
|
+
'light-house-title', 'light-hexagon-title', 'hexagon-title', 'badge-line',
|
|
12
|
+
'multi-parallelograms-end', 'center-title-with-bar', 'double-color-trapezoid',
|
|
13
|
+
'arrow-right-title', 'square-blocks-title', 'inner-ellipse-title',
|
|
14
|
+
]);
|
|
15
|
+
const DEFAULT_DIVIDER_TYPE = 'bold-with-thin';
|
|
16
|
+
|
|
3
17
|
const FIELD_TYPE_ALIAS = Object.freeze({
|
|
4
18
|
TextAreaField: 'TextareaField',
|
|
5
19
|
Textareafield: 'TextareaField',
|
|
@@ -232,14 +246,19 @@ function validateDivider(field, path, diagnostics) {
|
|
|
232
246
|
'Use a plain string title or omit title.'
|
|
233
247
|
);
|
|
234
248
|
}
|
|
235
|
-
|
|
249
|
+
const styleInputs = ['dividerType', 'dividerStyle', 'styleType', 'typeStyle']
|
|
250
|
+
.map(key => [key, field[key]]);
|
|
251
|
+
styleInputs.push(['props.type', field.props?.type]);
|
|
252
|
+
for (const [key, value] of styleInputs) {
|
|
253
|
+
if (value === undefined || value === null || value === '') {continue;}
|
|
254
|
+
if (typeof value === 'string' && DIVIDER_TYPES.includes(value.trim())) {continue;}
|
|
236
255
|
pushDiagnostic(
|
|
237
256
|
diagnostics,
|
|
238
257
|
'INVALID_DIVIDER_TYPE',
|
|
239
|
-
path + '.
|
|
240
|
-
'
|
|
241
|
-
describeActual(
|
|
242
|
-
'
|
|
258
|
+
path + '.' + key,
|
|
259
|
+
DIVIDER_TYPES.join(' | '),
|
|
260
|
+
typeof value === 'string' ? value : describeActual(value),
|
|
261
|
+
t('create_form.divider_type_invalid', String(value), DIVIDER_TYPES.join(', ')),
|
|
243
262
|
'Use a supported divider style string or omit dividerType.'
|
|
244
263
|
);
|
|
245
264
|
}
|
|
@@ -557,4 +576,6 @@ module.exports = {
|
|
|
557
576
|
normalizeDefinitionType,
|
|
558
577
|
BUSINESS_FIELD_TYPES,
|
|
559
578
|
PRESENTATION_FIELD_TYPES,
|
|
579
|
+
DIVIDER_TYPES,
|
|
580
|
+
DEFAULT_DIVIDER_TYPE,
|
|
560
581
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Babel = require('@babel/standalone');
|
|
4
|
+
const { t } = require('../core/i18n');
|
|
4
5
|
|
|
5
6
|
const parser = Babel.packages.parser;
|
|
6
7
|
const traverse = Babel.packages.traverse.default || Babel.packages.traverse;
|
|
@@ -77,6 +78,13 @@ function isWindowOpenCall(callee) {
|
|
|
77
78
|
);
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
function isLocationCall(callee) {
|
|
82
|
+
return callee?.type === 'MemberExpression'
|
|
83
|
+
&& ['assign', 'replace'].includes(callee.property?.name)
|
|
84
|
+
&& (callee.object?.name === 'location'
|
|
85
|
+
|| (callee.object?.object?.name === 'window' && callee.object?.property?.name === 'location'));
|
|
86
|
+
}
|
|
87
|
+
|
|
80
88
|
function isOpenPageCall(callee) {
|
|
81
89
|
return !!(
|
|
82
90
|
callee &&
|
|
@@ -114,18 +122,26 @@ function isLocationHrefAssignment(node) {
|
|
|
114
122
|
}
|
|
115
123
|
|
|
116
124
|
function isMobileGuarded(pathRef, sourceCode) {
|
|
125
|
+
const mobileCondition = node => {
|
|
126
|
+
if (node?.type === 'UnaryExpression' && node.operator === '!') {return -mobileCondition(node.argument);}
|
|
127
|
+
const text = getNodeText(sourceCode, node);
|
|
128
|
+
if (node?.type === 'Identifier' || node?.type === 'MemberExpression' || node?.type === 'CallExpression') {
|
|
129
|
+
return MOBILE_GUARD_PATTERN.test(text) && !/min-width/.test(text) ? 1 : 0;
|
|
130
|
+
}
|
|
131
|
+
return 0;
|
|
132
|
+
};
|
|
117
133
|
let cursor = pathRef;
|
|
118
134
|
while (cursor && cursor.parentPath) {
|
|
119
135
|
const parent = cursor.parentPath;
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
if (['IfStatement', 'ConditionalExpression'].includes(parent.node?.type)) {
|
|
137
|
+
const direction = mobileCondition(parent.node.test);
|
|
138
|
+
if ((direction === 1 && nodeContains(parent.node.consequent, pathRef.node))
|
|
139
|
+
|| (direction === -1 && nodeContains(parent.node.alternate, pathRef.node))) {return true;}
|
|
124
140
|
}
|
|
125
|
-
if (parent.node && parent.node.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
if (parent.node?.type === 'LogicalExpression' && nodeContains(parent.node.right, pathRef.node)) {
|
|
142
|
+
const direction = mobileCondition(parent.node.left);
|
|
143
|
+
if ((parent.node.operator === '&&' && direction === 1)
|
|
144
|
+
|| (parent.node.operator === '||' && direction === -1)) {return true;}
|
|
129
145
|
}
|
|
130
146
|
cursor = parent;
|
|
131
147
|
}
|
|
@@ -161,9 +177,17 @@ function findDirectFormOpenIssues(sourceCode, options = {}) {
|
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
const knownFormUrlNames = new Set();
|
|
180
|
+
const containerNames = new Set(['formOpenContainer']);
|
|
164
181
|
traverse(ast, {
|
|
165
182
|
VariableDeclarator(pathRef) {
|
|
166
183
|
const id = pathRef.node.id;
|
|
184
|
+
if (id?.type === 'ObjectPattern' && pathRef.node.init?.callee?.name === 'useYidaFormOpen') {
|
|
185
|
+
for (const property of id.properties) {
|
|
186
|
+
if (property.key?.name === 'formOpenContainer' && property.value?.type === 'Identifier') {
|
|
187
|
+
containerNames.add(property.value.name);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
167
191
|
if (!id || id.type !== 'Identifier') {
|
|
168
192
|
return;
|
|
169
193
|
}
|
|
@@ -174,10 +198,30 @@ function findDirectFormOpenIssues(sourceCode, options = {}) {
|
|
|
174
198
|
});
|
|
175
199
|
|
|
176
200
|
const issues = [];
|
|
201
|
+
const templateParts = new Map();
|
|
202
|
+
const templateUses = [];
|
|
203
|
+
let containerRendered = false;
|
|
204
|
+
const functionName = pathRef => {
|
|
205
|
+
const owner = pathRef.getFunctionParent();
|
|
206
|
+
return owner?.node.id?.name || owner?.parentPath?.node.id?.name;
|
|
207
|
+
};
|
|
208
|
+
const addTemplatePart = (owner, part) => {
|
|
209
|
+
if (!templateParts.has(owner)) {templateParts.set(owner, new Set());}
|
|
210
|
+
templateParts.get(owner).add(part);
|
|
211
|
+
};
|
|
177
212
|
traverse(ast, {
|
|
178
213
|
CallExpression(pathRef) {
|
|
179
214
|
const callee = pathRef.node.callee;
|
|
180
|
-
if (
|
|
215
|
+
if (callee.type === 'Identifier' && callee.name === 'useYidaFormOpen') {
|
|
216
|
+
templateUses.push(pathRef.node);
|
|
217
|
+
}
|
|
218
|
+
if (callee.type === 'Identifier' && ['openForm', 'setFormRequest'].includes(callee.name)
|
|
219
|
+
&& pathRef.node.arguments[0]?.type === 'ObjectExpression'
|
|
220
|
+
&& pathRef.node.arguments[0].properties.some(property => (property.key?.name || property.key?.value) === 'type'
|
|
221
|
+
&& ['submission', 'detail'].includes(property.value?.value))) {
|
|
222
|
+
templateUses.push(pathRef.node);
|
|
223
|
+
}
|
|
224
|
+
if (!isWindowOpenCall(callee) && !isOpenPageCall(callee) && !isLocationCall(callee)) {
|
|
181
225
|
return;
|
|
182
226
|
}
|
|
183
227
|
const firstArg = pathRef.node.arguments && pathRef.node.arguments[0];
|
|
@@ -207,13 +251,56 @@ function findDirectFormOpenIssues(sourceCode, options = {}) {
|
|
|
207
251
|
callee: getNodeText(sourceCode, pathRef.node.left),
|
|
208
252
|
});
|
|
209
253
|
},
|
|
254
|
+
JSXExpressionContainer(pathRef) {
|
|
255
|
+
if ((pathRef.parentPath.isJSXElement() || pathRef.parentPath.isJSXFragment())
|
|
256
|
+
&& containerNames.has(pathRef.node.expression?.name)) {
|
|
257
|
+
containerRendered = true;
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
JSXOpeningElement(pathRef) {
|
|
261
|
+
const name = pathRef.node.name?.name;
|
|
262
|
+
const owner = functionName(pathRef);
|
|
263
|
+
addTemplatePart(owner, name);
|
|
264
|
+
if (name === 'FormOpenContainer' && owner !== 'useYidaFormOpen') {
|
|
265
|
+
templateUses.push(pathRef.node);
|
|
266
|
+
containerRendered = true;
|
|
267
|
+
}
|
|
268
|
+
const attribute = key => pathRef.node.attributes.find(item => item.type === 'JSXAttribute' && item.name?.name === key)?.value;
|
|
269
|
+
const value = node => node?.type === 'JSXExpressionContainer' ? node.expression : node;
|
|
270
|
+
if (owner === 'CanvasDrawer' && attribute('className')?.value === 'oy-drawer-resize'
|
|
271
|
+
&& attribute('onPointerDown') && attribute('onPointerMove') && attribute('onPointerUp') && attribute('onKeyDown')) {
|
|
272
|
+
addTemplatePart(owner, 'resize-handle');
|
|
273
|
+
}
|
|
274
|
+
if (name === 'a' && expressionLooksLikeFormUrl(value(attribute('href')), sourceCode, knownFormUrlNames)) {
|
|
275
|
+
issues.push({ line: getNodeLine(pathRef.node), callee: 'a[href]' });
|
|
276
|
+
}
|
|
277
|
+
if (!['iframe', 'YidaCanvasIframe'].includes(name) || owner === 'FormOpenContainer') {return;}
|
|
278
|
+
if (!expressionLooksLikeFormUrl(value(attribute('src')), sourceCode, knownFormUrlNames)) {return;}
|
|
279
|
+
// 导航主内容可以嵌原生页;弹层内的表单必须使用提供的容器。
|
|
280
|
+
const overlay = pathRef.findParent(parent => parent.isJSXElement() && (
|
|
281
|
+
['Drawer', 'Modal', 'CanvasDrawer'].includes(parent.node.openingElement.name?.name)
|
|
282
|
+
|| parent.node.openingElement.attributes.some(item => item.type === 'JSXAttribute'
|
|
283
|
+
&& ((item.name?.name === 'role' && item.value?.value === 'dialog')
|
|
284
|
+
|| (item.name?.name === 'style' && /position\s*:\s*['"]fixed['"]/.test(getNodeText(sourceCode, item.value)))))));
|
|
285
|
+
if (overlay) {issues.push({ line: getNodeLine(pathRef.node), callee: `${name} outside FormOpenContainer` });}
|
|
286
|
+
},
|
|
210
287
|
});
|
|
211
288
|
|
|
289
|
+
if (templateUses.length) {
|
|
290
|
+
const hasParts = (owner, parts) => parts.every(part => templateParts.get(owner)?.has(part));
|
|
291
|
+
const complete = hasParts('FormOpenContainer', ['CanvasDrawer', 'iframe'])
|
|
292
|
+
&& hasParts('CanvasDrawer', ['Drawer', 'ExternalLink', 'Maximize2', 'Minimize2', 'X', 'resize-handle'])
|
|
293
|
+
&& hasParts('useYidaFormOpen', ['FormOpenContainer']) && containerRendered;
|
|
294
|
+
if (!complete) {
|
|
295
|
+
issues.push({ line: getNodeLine(templateUses[0]), callee: 'incomplete FormOpenContainer template' });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
212
299
|
return issues;
|
|
213
300
|
}
|
|
214
301
|
|
|
215
302
|
function formatDirectFormOpenMessage() {
|
|
216
|
-
return '
|
|
303
|
+
return t('publish.lint_form_open_container');
|
|
217
304
|
}
|
|
218
305
|
|
|
219
306
|
module.exports = {
|
|
@@ -4,37 +4,7 @@ const Babel = require('@babel/standalone');
|
|
|
4
4
|
const { CliError } = require('../core/cli-error');
|
|
5
5
|
const { t } = require('../core/i18n');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
// 不执行页面代码,也不把动态插值当成完整 CSS 进行猜测性校验。
|
|
9
|
-
function findCssStructureIssue(css) {
|
|
10
|
-
const stack = [];
|
|
11
|
-
const pairs = { '(': ')', '[': ']', '{': '}' };
|
|
12
|
-
for (let index = 0; index < css.length; index++) {
|
|
13
|
-
const char = css[index];
|
|
14
|
-
if (char === '\\') { index++; continue; }
|
|
15
|
-
if (char === '/' && css[index + 1] === '*') {
|
|
16
|
-
const end = css.indexOf('*/', index + 2);
|
|
17
|
-
if (end < 0) { return { index, type: 'unclosed_comment' }; }
|
|
18
|
-
index = end + 1;
|
|
19
|
-
} else if (char === '"' || char === "'") {
|
|
20
|
-
const start = index;
|
|
21
|
-
for (index++; index < css.length; index++) {
|
|
22
|
-
if (css[index] === '\\') { index++; continue; }
|
|
23
|
-
if (css[index] === char) { break; }
|
|
24
|
-
}
|
|
25
|
-
if (index >= css.length) { return { index: start, type: 'unclosed_string' }; }
|
|
26
|
-
} else if (pairs[char]) {
|
|
27
|
-
stack.push({ char, index });
|
|
28
|
-
} else if (')]}'.includes(char)) {
|
|
29
|
-
const open = stack.pop();
|
|
30
|
-
if (!open || pairs[open.char] !== char) {
|
|
31
|
-
return { index: open ? open.index : index, type: 'mismatched_delimiter', expected: open && pairs[open.char], actual: char };
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const open = stack.pop();
|
|
36
|
-
return open ? { index: open.index, type: 'unclosed_delimiter', expected: pairs[open.char] } : null;
|
|
37
|
-
}
|
|
7
|
+
const { findCssStructureIssue } = require('../core/css-structure');
|
|
38
8
|
|
|
39
9
|
function resolveStaticCss(pathRef, seen = new Set()) {
|
|
40
10
|
if (pathRef.isStringLiteral()) { return { text: pathRef.node.value, node: pathRef.node }; }
|