@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ee13359
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 +114 -76
- package/api/blog/blog.doc.mjs +1 -0
- package/api/build/build.doc.mjs +4 -3
- package/api/build/build.test.mjs +14 -3
- package/api/build/build.type.d.mts +36 -2
- package/api/build/build.type.mjs +25 -4
- package/api/build/help/help.d.mts +8 -5
- package/api/build/help/help.mjs +56 -6
- package/api/component/_adapter.d.mts +6 -12
- package/api/component/_adapter.mjs +20 -10
- package/api/component/component.doc.mjs +13 -3
- package/api/component/component.mjs +91 -14
- package/api/component/component.test.mjs +38 -0
- package/api/component/component.type.d.mts +22 -11
- package/api/component/component.type.mjs +32 -24
- package/api/component/detail/blocks/blocks.d.mts +2 -1
- package/api/component/detail/blocks/blocks.mjs +4 -3
- package/api/component/list/list.d.mts +0 -5
- package/api/component/list/list.mjs +40 -11
- package/api/discover/discover.doc.mjs +2 -1
- package/api/discover/discover.type.d.mts +4 -4
- package/api/discover/discover.type.mjs +10 -10
- package/api/docs/_adapter.d.mts +230 -41
- package/api/docs/_adapter.mjs +734 -108
- package/api/docs/compiled-topics.test.mjs +78 -0
- package/api/docs/detail/detail.mjs +22 -63
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +54 -19
- package/api/docs/detail/section/section.test.mjs +50 -0
- package/api/docs/docs.d.mts +10 -3
- package/api/docs/docs.doc.mjs +54 -16
- package/api/docs/docs.mjs +53 -10
- package/api/docs/docs.test.mjs +164 -4
- package/api/docs/docs.type.d.mts +205 -5
- package/api/docs/docs.type.mjs +137 -11
- package/api/docs/index/index.d.mts +18 -0
- package/api/docs/index/index.mjs +40 -0
- package/api/docs/index/index.test.mjs +62 -0
- package/api/docs/integration-tree.test.mjs +547 -0
- package/api/docs/integrationDocs.test.mjs +114 -8
- package/api/docs/list/list.mjs +28 -12
- package/api/docs/node/node.d.mts +43 -0
- package/api/docs/node/node.mjs +192 -0
- package/api/doctor/doctor.d.mts +99 -1
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +544 -1
- package/api/doctor/doctor.test.mjs +610 -1
- package/api/gap-report/gap-report.doc.mjs +8 -4
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/hook.type.d.mts +3 -3
- package/api/hook/hook.type.mjs +11 -11
- package/api/hook/list/list.d.mts +2 -2
- package/api/hook/list/list.mjs +69 -17
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +6 -1
- package/api/init/init.test.mjs +41 -1
- package/api/init/remove/remove.mjs +1 -1
- package/api/init/run/run.mjs +20 -10
- package/api/integration/add-contribution.component-names.test.mjs +120 -0
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +130 -15
- package/api/integration/add-contribution.test.mjs +258 -7
- package/api/integration/add-theme.mjs +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.mjs +108 -31
- package/api/integration/authoring-checks.test.mjs +171 -6
- package/api/integration/authoring-checks.type.d.mts +3 -1
- package/api/integration/authoring-checks.type.mjs +3 -1
- package/api/integration/integration-authoring.type.d.mts +3 -1
- package/api/integration/integration-authoring.type.mjs +2 -0
- package/api/integration/integration-block-exports.test.mjs +10 -6
- package/api/integration/integrationAdd.doc.mjs +14 -4
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +2 -1
- package/api/integration/integrationAddDoc.doc.mjs +8 -1
- package/api/integration/integrationAddTemplate.doc.mjs +1 -0
- package/api/integration/integrationAddTheme.doc.mjs +6 -5
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
- package/api/integration/integrationDocConflicts.doc.mjs +2 -1
- package/api/integration/integrationPackCheck.doc.mjs +2 -1
- package/api/integration/integrationTemplateConflicts.doc.mjs +7 -5
- package/api/integration/pack-check.mjs +83 -7
- package/api/integration/pack-check.test.mjs +387 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +52 -102
- package/api/integration/validate-integration.test.mjs +179 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +3 -2
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/envelope-types.test.mjs +76 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +3 -2
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/expand/expand.mjs +7 -5
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.mjs +2 -1
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +2 -1
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search-return-type.test.mjs +54 -0
- package/api/search/search.d.mts +29 -11
- package/api/search/search.doc.mjs +8 -2
- package/api/search/search.mjs +337 -41
- package/api/search/search.test.mjs +124 -1
- package/api/search/search.type.d.mts +15 -3
- package/api/search/search.type.mjs +5 -2
- package/api/swizzle/copy/copy.mjs +28 -11
- package/api/swizzle/swizzle.doc.mjs +2 -1
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/copy/copy.mjs +17 -23
- package/api/template/copy/copy.test.mjs +17 -0
- package/api/template/list/list.mjs +1 -0
- package/api/template/template-integration.test.mjs +1022 -3
- package/api/template/template-suffix.test.mjs +41 -21
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +30 -8
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +12 -14
- package/api/template/template.type.mjs +15 -14
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +84 -0
- package/api/theme/add/add.mjs +20 -3
- package/api/theme/add/add.staging.test.mjs +66 -0
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/build/build.mjs +133 -42
- package/api/theme/build/build.public-component-vars.test.mjs +1 -1
- package/api/theme/build/build.receipt-doc.test.mjs +111 -0
- package/api/theme/build/build.test.mjs +127 -0
- package/api/theme/build/font-warning.mjs +3 -3
- package/api/theme/build/font-warning.test.mjs +5 -2
- package/api/theme/generateTonalPalette.doc.mjs +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/palette/generate/generate.mjs +8 -3
- package/api/theme/palette/generate/generate.test.mjs +96 -0
- package/api/theme/palette/generate/generator.d.mts +10 -13
- package/api/theme/palette/generate/generator.mjs +15 -4
- package/api/theme/palette/generate/generator.test.mjs +10 -0
- package/api/theme/template/template.mjs +11 -2
- package/api/theme/template/template.test.mjs +20 -0
- package/api/theme/theme.type.d.mts +170 -11
- package/api/theme/theme.type.mjs +94 -27
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +8 -4
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +6 -2
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +139 -22
- package/api/upgrade/list/list.mjs +2 -1
- package/api/upgrade/list/list.test.mjs +73 -0
- package/api/upgrade/project-context.test.mjs +272 -0
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +356 -59
- package/api/upgrade/status/status.mjs +2 -2
- package/api/upgrade/upgrade.doc.mjs +12 -5
- package/api/upgrade/upgrade.type.d.mts +43 -5
- package/api/upgrade/upgrade.type.mjs +27 -11
- package/assets/codemods/__tests__/runner.test.mjs +330 -8
- package/assets/codemods/integration-discovery.mjs +48 -4
- package/assets/codemods/integration-discovery.test.mjs +73 -0
- package/assets/codemods/integration-runner.mjs +56 -4
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +350 -102
- package/assets/codemods/term-log.mjs +32 -8
- package/assets/codemods/term-log.test.mjs +19 -1
- package/assets/codemods/transform-prop.mjs +109 -0
- package/assets/codemods/transform-prop.test.mjs +95 -0
- package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/next/index.mjs +19 -1
- package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
- package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
- package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
- package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
- package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
- package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
- package/assets/docs/README.md +9 -0
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +3 -3
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +4 -4
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +124 -25
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +1 -1
- package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
- package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
- package/assets/templates/pages/table-tree/page.tsx +1704 -0
- package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
- package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
- package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
- package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
- package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
- package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
- package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
- package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
- package/authoring/_shared/contract.ts +22 -0
- package/authoring/codemod/codemod.doc.mjs +7 -2
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +8 -6
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +2 -2
- package/authoring/config/debug-composition.test.mjs +92 -0
- package/authoring/config/parse.d.mts +13 -13
- package/authoring/config/parse.mjs +8 -8
- package/authoring/config/type.ts +18 -6
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/debug/parse.d.mts +8 -8
- package/authoring/debug/parse.mjs +3 -3
- package/authoring/doctypes/_schema.d.mts +718 -23
- package/authoring/doctypes/_schema.mjs +519 -39
- package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
- package/authoring/doctypes/base/type.ts +41 -0
- package/authoring/doctypes/command/command.doc.mjs +4 -3
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +5 -4
- package/authoring/doctypes/component/component.doc.mjs +6 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +6 -5
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +4 -2
- package/authoring/doctypes/function/function.doc.mjs +7 -2
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +9 -4
- package/authoring/doctypes/hook/hook.doc.mjs +4 -0
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +5 -4
- package/authoring/doctypes/legacy.d.mts +8 -6
- package/authoring/doctypes/legacy.mjs +5 -4
- package/authoring/doctypes/load-contract.test.mjs +208 -0
- package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
- package/authoring/doctypes/namespace/parse.d.mts +12 -0
- package/authoring/doctypes/namespace/parse.mjs +25 -0
- package/authoring/doctypes/namespace/parse.test.mjs +163 -0
- package/authoring/doctypes/namespace/type.ts +74 -0
- package/authoring/doctypes/parse.d.mts +22 -18
- package/authoring/doctypes/parse.mjs +22 -11
- package/authoring/doctypes/parse.test.mjs +77 -3
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +8 -5
- package/authoring/doctypes/reference/reference.doc.mjs +20 -5
- package/authoring/doctypes/reference/type.ts +53 -5
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +4 -4
- package/authoring/doctypes/template/parse.d.mts +94 -1
- package/authoring/doctypes/template/parse.mjs +40 -2
- package/authoring/doctypes/template/parse.test.mjs +26 -2
- package/authoring/doctypes/template/template.doc.mjs +13 -3
- package/authoring/doctypes/template/type.ts +13 -2
- package/authoring/doctypes/theme/parse.d.mts +35 -0
- package/authoring/doctypes/theme/parse.mjs +76 -0
- package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
- package/authoring/doctypes/theme/theme.doc.mjs +79 -0
- package/authoring/doctypes/theme/type.ts +42 -0
- package/authoring/doctypes/types.ts +12 -10
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/gap-report/parse.d.mts +10 -10
- package/authoring/gap-report/parse.mjs +6 -6
- package/authoring/gap-report/type.ts +1 -1
- package/authoring/identity/identity.doc.d.mts +9 -0
- package/authoring/identity/identity.doc.mjs +61 -0
- package/authoring/identity/type.ts +132 -0
- package/authoring/index.d.mts +2 -0
- package/authoring/index.d.ts +52 -17
- package/authoring/index.mjs +3 -1
- package/authoring/integration/integration.doc.mjs +15 -8
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +10 -1
- package/authoring/integration/schema.d.mts +6 -4
- package/authoring/integration/schema.mjs +9 -3
- package/authoring/integration/type.ts +19 -8
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/command-load-failure.test.mjs +83 -0
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/blog.mjs +23 -8
- package/clients/cli/commands/blog.test.mjs +42 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
- package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
- package/clients/cli/commands/build-theme.mjs +16 -50
- package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
- package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
- package/clients/cli/commands/build.doc.mjs +5 -2
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
- package/clients/cli/commands/build.mjs +37 -49
- package/clients/cli/commands/build.playbook.test.mjs +75 -0
- package/clients/cli/commands/build.text-fields.test.mjs +40 -0
- package/clients/cli/commands/component/index.mjs +3 -8
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/component-package.test.mjs +46 -0
- package/clients/cli/commands/component-resolution.test.mjs +21 -0
- package/clients/cli/commands/component.doc.mjs +1 -1
- package/clients/cli/commands/component.test.mjs +19 -0
- package/clients/cli/commands/detail-levels.test.mjs +2 -2
- package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
- package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
- package/clients/cli/commands/discover.doc.mjs +5 -3
- package/clients/cli/commands/discover.mjs +4 -4
- package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
- package/clients/cli/commands/docs.doc.mjs +28 -6
- package/clients/cli/commands/docs.mjs +238 -24
- package/clients/cli/commands/docs.test.mjs +193 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
- package/clients/cli/commands/doctor-integration.test.mjs +83 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +30 -25
- package/clients/cli/commands/doctor.test.mjs +42 -0
- package/clients/cli/commands/gap-report.doc.mjs +17 -6
- package/clients/cli/commands/gap-report.test.mjs +72 -0
- package/clients/cli/commands/hook/index.mjs +7 -17
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
- package/clients/cli/commands/init.doc.mjs +20 -9
- package/clients/cli/commands/init.flag-help.test.mjs +153 -0
- package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
- package/clients/cli/commands/integration-add.doc.mjs +32 -6
- package/clients/cli/commands/integration-authoring.test.mjs +13 -9
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/interactive-guard.test.mjs +101 -24
- package/clients/cli/commands/json-contract.test.mjs +33 -0
- package/clients/cli/commands/layout-check.doc.mjs +15 -4
- package/clients/cli/commands/layout-expand.doc.mjs +22 -5
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +3 -3
- package/clients/cli/commands/layout.mjs +21 -9
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
- package/clients/cli/commands/search.doc.mjs +7 -4
- package/clients/cli/commands/search.mjs +28 -9
- package/clients/cli/commands/search.test.mjs +75 -0
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +3 -2
- package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
- package/clients/cli/commands/template.doc.mjs +52 -13
- package/clients/cli/commands/template.flag-help.test.mjs +117 -0
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/template.path-help.test.mjs +40 -0
- package/clients/cli/commands/text-json-parity.test.mjs +719 -0
- package/clients/cli/commands/theme-add.doc.mjs +4 -3
- package/clients/cli/commands/theme-build.doc.mjs +8 -7
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
- package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
- package/clients/cli/commands/theme-template.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
- package/clients/cli/commands/upgrade.doc.mjs +22 -10
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
- package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +164 -1
- package/clients/cli/formatters/index.test.mjs +97 -0
- package/clients/cli/index.mjs +21 -30
- package/clients/cli/latest-version-env.test.mjs +50 -0
- package/clients/cli/lib/cli-error.test.mjs +7 -0
- package/clients/cli/lib/component-format.mjs +9 -9
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +32 -6
- package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
- package/clients/cli/lib/exit-codes.test.mjs +97 -0
- package/clients/cli/lib/hook-format.mjs +19 -10
- package/clients/cli/lib/json-shim.mjs +38 -2
- package/clients/cli/lib/json-shim.test.mjs +83 -0
- package/clients/cli/lib/manifest.d.ts +2 -0
- package/clients/cli/lib/manifest.mjs +36 -2
- package/clients/cli/lib/manifest.test.mjs +17 -0
- package/foundation/agent-docs/agent-docs.d.mts +4 -0
- package/foundation/agent-docs/agent-docs.mjs +70 -3
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
- package/foundation/agent-docs/agent-docs.test.mjs +10 -1
- package/foundation/config/integration-debug.test.mjs +28 -3
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +263 -91
- package/foundation/config/project.test.mjs +270 -21
- package/foundation/discovery/authoring-self-docs.d.mts +75 -0
- package/foundation/discovery/authoring-self-docs.mjs +224 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +165 -0
- package/foundation/discovery/authoring-surface.d.mts +74 -0
- package/foundation/discovery/authoring-surface.mjs +525 -0
- package/foundation/discovery/authoring-surface.test.mjs +392 -0
- package/foundation/discovery/cli-self-docs.d.mts +119 -0
- package/foundation/discovery/cli-self-docs.mjs +490 -0
- package/foundation/discovery/cli-self-docs.test.mjs +375 -0
- package/foundation/discovery/component-discovery.d.mts +39 -1
- package/foundation/discovery/component-discovery.mjs +50 -1
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +116 -11
- package/foundation/discovery/docs-discovery.mjs +389 -108
- package/foundation/discovery/docs-discovery.test.mjs +325 -20
- package/foundation/discovery/docs-output-budget.d.mts +28 -0
- package/foundation/discovery/docs-output-budget.mjs +50 -0
- package/foundation/discovery/docs-section-key.d.mts +116 -0
- package/foundation/discovery/docs-section-key.mjs +322 -0
- package/foundation/discovery/docs-section-key.test.mjs +246 -0
- package/foundation/discovery/template-adapter.d.mts +113 -11
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
- package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
- package/foundation/discovery/template-adapter.mjs +774 -83
- package/foundation/discovery/template-adapter.test.mjs +57 -0
- package/foundation/discovery/theme-discovery.d.mts +67 -7
- package/foundation/discovery/theme-discovery.mjs +916 -186
- package/foundation/discovery/theme-discovery.test.mjs +613 -219
- package/foundation/discovery/theming-targets.test.mjs +4 -0
- package/foundation/doc-compiler/bundle.d.mts +47 -0
- package/foundation/doc-compiler/bundle.mjs +278 -0
- package/foundation/doc-compiler/bundle.test.mjs +266 -0
- package/foundation/doc-compiler/compile.d.mts +343 -0
- package/foundation/doc-compiler/compile.mjs +558 -0
- package/foundation/doc-compiler/diagnostics.d.mts +126 -0
- package/foundation/doc-compiler/diagnostics.mjs +305 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +291 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +22 -0
- package/foundation/doc-compiler/ir.mjs +471 -0
- package/foundation/doc-compiler/lenses.d.mts +34 -0
- package/foundation/doc-compiler/lenses.mjs +128 -0
- package/foundation/doc-compiler/links.d.mts +135 -0
- package/foundation/doc-compiler/links.mjs +253 -0
- package/foundation/doc-compiler/links.test.mjs +139 -0
- package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
- package/foundation/doc-compiler/overlays.d.mts +37 -0
- package/foundation/doc-compiler/overlays.mjs +206 -0
- package/foundation/doc-compiler/parse-readable.d.mts +9 -0
- package/foundation/doc-compiler/parse-readable.mjs +29 -0
- package/foundation/doc-compiler/read.d.mts +127 -0
- package/foundation/doc-compiler/read.mjs +325 -0
- package/foundation/doc-compiler/read.test.mjs +313 -0
- package/foundation/doc-compiler/source.d.mts +33 -0
- package/foundation/doc-compiler/source.mjs +128 -0
- package/foundation/doc-compiler/tree.d.mts +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/fs/file-protection.d.mts +33 -0
- package/foundation/fs/file-protection.mjs +825 -0
- package/foundation/fs/file-protection.test.mjs +250 -0
- package/foundation/fs/module-loader.d.mts +1 -0
- package/foundation/fs/module-loader.mjs +50 -1
- package/foundation/fs/module-loader.stdout.test.mjs +332 -0
- package/foundation/fs/path-safety.d.mts +3 -2
- package/foundation/fs/path-safety.mjs +49 -19
- package/foundation/fs/path-safety.test.mjs +50 -0
- package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
- package/foundation/identity/provider-identity.d.mts +90 -0
- package/foundation/identity/provider-identity.mjs +320 -0
- package/foundation/identity/provider-identity.test.mjs +254 -0
- package/foundation/identity/providers.d.mts +7 -0
- package/foundation/identity/providers.mjs +16 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -45
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +109 -0
- package/foundation/integrations/contribution-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +3 -2
- package/foundation/integrations/contribution-inventory.mjs +27 -24
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +52 -21
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/integrations.d.mts +49 -2
- package/foundation/integrations/integrations.mjs +81 -8
- package/foundation/integrations/integrations.test.mjs +384 -1
- package/foundation/integrations/provider-conflicts.test.mjs +125 -0
- package/foundation/integrations/provider-ledger.test.mjs +275 -0
- package/foundation/integrations/provider-resolution.d.mts +152 -0
- package/foundation/integrations/provider-resolution.mjs +576 -0
- package/foundation/integrations/provider-resolution.test.mjs +369 -0
- package/foundation/integrations/theme-descriptor.d.mts +8 -0
- package/foundation/integrations/theme-descriptor.mjs +44 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +131 -29
- package/foundation/response/base.d.ts +8 -4
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.d.ts +2 -0
- package/foundation/response/error-codes.doc.mjs +13 -4
- package/foundation/response/error-codes.mjs +8 -2
- package/foundation/response/error-codes.test.mjs +137 -10
- package/foundation/response/json-contract.test.mjs +57 -17
- package/foundation/response/json.d.mts +4 -2
- package/foundation/response/json.mjs +8 -10
- package/foundation/response/response-types.doc.mjs +26 -16
- package/foundation/response/response-types.doc.test.mjs +158 -0
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.mjs +22 -10
- package/foundation/xle/expand.d.mts +2 -0
- package/foundation/xle/expand.mjs +4 -3
- package/foundation/xle/expand.test.mjs +54 -0
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -11
- package/assets/templates/themes/manifest.json +0 -95
- package/clients/cli/lib/update-check.mjs +0 -83
- package/clients/cli/lib/update-check.test.mjs +0 -137
- package/clients/cli/update-hint-commands.test.mjs +0 -54
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import {themeAdd} from './add.mjs';
|
|
8
|
+
import {listThemes} from '../_adapter.mjs';
|
|
9
|
+
import {isErrorCode} from '../../../foundation/response/error-codes.mjs';
|
|
10
|
+
|
|
11
|
+
let tmpDir;
|
|
12
|
+
let outsideDir;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-staging-'));
|
|
16
|
+
outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-outside-'));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
21
|
+
fs.rmSync(outsideDir, {recursive: true, force: true});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Put a symlink where `theme add` stages the first file of `slug`.
|
|
26
|
+
* @param {string} slug
|
|
27
|
+
* @param {string} target
|
|
28
|
+
*/
|
|
29
|
+
function plantStagingLink(slug, target) {
|
|
30
|
+
const theme = listThemes().find(entry => entry.slug === slug);
|
|
31
|
+
if (!theme) throw new Error(`missing bundled theme ${slug}`);
|
|
32
|
+
const first = theme.files[0];
|
|
33
|
+
const dest = path.join(tmpDir, 'src', 'themes', slug, first);
|
|
34
|
+
fs.mkdirSync(path.dirname(dest), {recursive: true});
|
|
35
|
+
fs.symlinkSync(target, `${dest}.${process.pid}.tmp`);
|
|
36
|
+
return dest;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('themeAdd staging writes stay inside the project', () => {
|
|
40
|
+
it('refuses a staging path that links outside the project', async () => {
|
|
41
|
+
const victim = path.join(outsideDir, 'victim.txt');
|
|
42
|
+
fs.writeFileSync(victim, 'outside\n');
|
|
43
|
+
const dest = plantStagingLink('stone', victim);
|
|
44
|
+
|
|
45
|
+
await expect(themeAdd('stone', {cwd: tmpDir})).rejects.toMatchObject({
|
|
46
|
+
code: 'ERR_PATH_TRAVERSAL',
|
|
47
|
+
});
|
|
48
|
+
expect(fs.readFileSync(victim, 'utf-8')).toBe('outside\n');
|
|
49
|
+
expect(fs.existsSync(dest)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('never creates a file through a dangling staging link', async () => {
|
|
53
|
+
const victim = path.join(outsideDir, 'created.txt');
|
|
54
|
+
plantStagingLink('stone', victim);
|
|
55
|
+
|
|
56
|
+
let error;
|
|
57
|
+
try {
|
|
58
|
+
await themeAdd('stone', {cwd: tmpDir});
|
|
59
|
+
} catch (caught) {
|
|
60
|
+
error = caught;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
expect(isErrorCode(error?.code)).toBe(true);
|
|
64
|
+
expect(fs.existsSync(victim)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -30,7 +30,20 @@ describe('themeAdd (api/theme/add)', () => {
|
|
|
30
30
|
expect(result.data.slug).toBe('neutral');
|
|
31
31
|
expect(result.data.package).toBe('@astryxdesign/cli');
|
|
32
32
|
expect(result.data.outputDir).toBe(path.join('src', 'themes', 'neutral'));
|
|
33
|
-
|
|
33
|
+
// Exactly what the bundle has always copied, in that order: the CLI's own
|
|
34
|
+
// descriptor stays behind.
|
|
35
|
+
expect(result.data.files).toEqual([
|
|
36
|
+
'neutralTheme.ts',
|
|
37
|
+
'icons.tsx',
|
|
38
|
+
'neutralPalettes.ts',
|
|
39
|
+
'neutralPalettes.generated.ts',
|
|
40
|
+
'neutralPaletteRefs.generated.ts',
|
|
41
|
+
'neutralPalettes.generated.receipt.json',
|
|
42
|
+
'palette.config.json',
|
|
43
|
+
]);
|
|
44
|
+
expect(
|
|
45
|
+
fs.readdirSync(path.join(tmpDir, 'src', 'themes', 'neutral')).sort(),
|
|
46
|
+
).toEqual([...result.data.files].sort());
|
|
34
47
|
for (const f of result.data.files) {
|
|
35
48
|
expect(
|
|
36
49
|
fs.existsSync(path.join(tmpDir, 'src', 'themes', 'neutral', f)),
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* - A .d.ts (plus an optional .variants.d.ts for custom prop values)
|
|
13
13
|
*
|
|
14
14
|
* It performs the writes and returns a `theme.build` receipt — its `warnings`
|
|
15
|
-
* carry override problems
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* carry override problems, every declaration core's generator dropped because
|
|
16
|
+
* its value could not stay one CSS declaration, and any fonts the theme names
|
|
17
|
+
* but does not load (font-warning.mjs) — or `null` when the theme produced no
|
|
18
|
+
* CSS (nothing to build). Errors throw AstryxError (with
|
|
18
19
|
* a stable code). Human progress is emitted through the shared `logger`
|
|
19
20
|
* (silent by default), so the CLI keeps its exact output while a programmatic
|
|
20
21
|
* caller stays quiet.
|
|
@@ -223,10 +224,10 @@ function staleBuildOutputs(writes, cwd) {
|
|
|
223
224
|
/** @param {Array<{dest: string, content: string}>} writes */
|
|
224
225
|
function writeBuildOutputs(writes) {
|
|
225
226
|
if (writes.length === 0) return;
|
|
226
|
-
fs.mkdirSync(path.dirname(writes[0].dest), {recursive: true});
|
|
227
227
|
/** @type {Array<{tmp: string, dest: string}>} */
|
|
228
228
|
const staged = [];
|
|
229
229
|
try {
|
|
230
|
+
fs.mkdirSync(path.dirname(writes[0].dest), {recursive: true});
|
|
230
231
|
for (const write of writes) {
|
|
231
232
|
const tmp = `${write.dest}.${process.pid}.tmp`;
|
|
232
233
|
fs.writeFileSync(tmp, write.content);
|
|
@@ -1870,7 +1871,7 @@ function validatePrivateVars(themeDef) {
|
|
|
1870
1871
|
if (prop.startsWith('--_')) {
|
|
1871
1872
|
errors.push(
|
|
1872
1873
|
`Component "${component}" (${[key, ...path].join(' ')}) sets private var "${prop}". ` +
|
|
1873
|
-
`Private vars (--_*) are internal
|
|
1874
|
+
`Private vars (--_*) are internal; use standard CSS properties ` +
|
|
1874
1875
|
`(e.g. borderRadius, padding) instead. The pipeline expands them automatically.`,
|
|
1875
1876
|
);
|
|
1876
1877
|
}
|
|
@@ -1930,20 +1931,7 @@ function getThemeComponentLayers(themeDef) {
|
|
|
1930
1931
|
*/
|
|
1931
1932
|
function validateCustomHeadingTypes(themeDef) {
|
|
1932
1933
|
const layers = getThemeComponentLayers(themeDef);
|
|
1933
|
-
const customTypes =
|
|
1934
|
-
for (const {components} of layers) {
|
|
1935
|
-
const headingRules = components.heading;
|
|
1936
|
-
if (!headingRules || typeof headingRules !== 'object') continue;
|
|
1937
|
-
|
|
1938
|
-
for (const key of Object.keys(headingRules)) {
|
|
1939
|
-
for (const pair of key.split('+')) {
|
|
1940
|
-
const colon = pair.indexOf(':');
|
|
1941
|
-
if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
|
|
1942
|
-
const value = pair.slice(colon + 1);
|
|
1943
|
-
if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1934
|
+
const customTypes = customHeadingTypes(themeDef);
|
|
1947
1935
|
|
|
1948
1936
|
const errors = [];
|
|
1949
1937
|
for (const type of customTypes) {
|
|
@@ -1970,10 +1958,65 @@ function validateCustomHeadingTypes(themeDef) {
|
|
|
1970
1958
|
return errors;
|
|
1971
1959
|
}
|
|
1972
1960
|
|
|
1961
|
+
/**
|
|
1962
|
+
* Custom Heading type names declared on the root or media-surface component
|
|
1963
|
+
* maps: the names the generated `HeadingTypeMap` augmentation enrolls.
|
|
1964
|
+
*
|
|
1965
|
+
* @param {{components?: Record<string, Record<string, unknown>>}} themeDef
|
|
1966
|
+
* @returns {Set<string>}
|
|
1967
|
+
*/
|
|
1968
|
+
function customHeadingTypes(themeDef) {
|
|
1969
|
+
/** @type {Set<string>} */
|
|
1970
|
+
const customTypes = new Set();
|
|
1971
|
+
for (const {components} of getThemeComponentLayers(themeDef)) {
|
|
1972
|
+
const headingRules = components.heading;
|
|
1973
|
+
if (!headingRules || typeof headingRules !== 'object') continue;
|
|
1974
|
+
|
|
1975
|
+
for (const key of Object.keys(headingRules)) {
|
|
1976
|
+
for (const pair of key.split('+')) {
|
|
1977
|
+
const colon = pair.indexOf(':');
|
|
1978
|
+
if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
|
|
1979
|
+
const value = pair.slice(colon + 1);
|
|
1980
|
+
if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
return customTypes;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* Custom Heading types with no standalone rule in the generated CSS. A type
|
|
1989
|
+
* whose every declaration the compiler dropped still passes the source check,
|
|
1990
|
+
* and its augmentation would then name a type that has no styles.
|
|
1991
|
+
*
|
|
1992
|
+
* @param {Set<string>} types
|
|
1993
|
+
* @param {string} css - Generated root component and media-surface rules.
|
|
1994
|
+
* @returns {string[]}
|
|
1995
|
+
*/
|
|
1996
|
+
function headingTypesWithoutEmittedRule(types, css) {
|
|
1997
|
+
/** @type {string[]} */
|
|
1998
|
+
const missing = [];
|
|
1999
|
+
for (const type of types) {
|
|
2000
|
+
const selector = `.astryx-heading[data-type="${type}"]`;
|
|
2001
|
+
let emitted = false;
|
|
2002
|
+
for (
|
|
2003
|
+
let at = css.indexOf(selector);
|
|
2004
|
+
at !== -1 && !emitted;
|
|
2005
|
+
at = css.indexOf(selector, at + 1)
|
|
2006
|
+
) {
|
|
2007
|
+
// A following attribute is a combined selector, not the standalone rule.
|
|
2008
|
+
emitted = css[at + selector.length] !== '[';
|
|
2009
|
+
}
|
|
2010
|
+
if (!emitted) missing.push(type);
|
|
2011
|
+
}
|
|
2012
|
+
return missing;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
1973
2015
|
/**
|
|
1974
2016
|
* Return true when a style object contains a declaration that the theme
|
|
1975
|
-
* generator can emit. Empty pseudo blocks do not count: they
|
|
1976
|
-
* and would leave the generated type augmentation
|
|
2017
|
+
* generator can emit. Empty pseudo blocks and blank values do not count: they
|
|
2018
|
+
* produce no usable CSS and would leave the generated type augmentation
|
|
2019
|
+
* without a usable rule.
|
|
1977
2020
|
*
|
|
1978
2021
|
* @param {unknown} styles
|
|
1979
2022
|
* @returns {boolean}
|
|
@@ -1987,7 +2030,12 @@ function hasUsableStyleDeclaration(styles) {
|
|
|
1987
2030
|
if (hasUsableStyleDeclaration(value)) return true;
|
|
1988
2031
|
continue;
|
|
1989
2032
|
}
|
|
1990
|
-
if (
|
|
2033
|
+
if (
|
|
2034
|
+
value !== undefined &&
|
|
2035
|
+
value !== null &&
|
|
2036
|
+
typeof value !== 'object' &&
|
|
2037
|
+
String(value).trim() !== ''
|
|
2038
|
+
) {
|
|
1991
2039
|
return true;
|
|
1992
2040
|
}
|
|
1993
2041
|
}
|
|
@@ -2162,14 +2210,14 @@ async function themeBuildInternal(
|
|
|
2162
2210
|
const noticeMessages = [];
|
|
2163
2211
|
for (const w of warnings) {
|
|
2164
2212
|
warningMessages.push(w);
|
|
2165
|
-
logger.warn(`
|
|
2213
|
+
logger.warn(` [warn] ${w}`);
|
|
2166
2214
|
}
|
|
2167
2215
|
|
|
2168
2216
|
// Validate no private vars are set directly
|
|
2169
2217
|
const privateVarErrors = validatePrivateVars(themeDef);
|
|
2170
2218
|
for (const e of privateVarErrors) {
|
|
2171
2219
|
warningMessages.push(e);
|
|
2172
|
-
logger.error(`
|
|
2220
|
+
logger.error(` [error] ${e}`);
|
|
2173
2221
|
}
|
|
2174
2222
|
if (privateVarErrors.length > 0) {
|
|
2175
2223
|
logger.error(
|
|
@@ -2195,7 +2243,7 @@ async function themeBuildInternal(
|
|
|
2195
2243
|
// capability exports are checked against what the theme actually asks for.
|
|
2196
2244
|
if (!_defineTheme || !_generateThemeRulesSplit) {
|
|
2197
2245
|
throw new AstryxError(
|
|
2198
|
-
'Could not load @astryxdesign/core/theme
|
|
2246
|
+
'Could not load @astryxdesign/core/theme: `astryx theme build` requires a ' +
|
|
2199
2247
|
'built, resolvable @astryxdesign/core so it emits the same CSS as the ' +
|
|
2200
2248
|
'runtime <Theme>. Build @astryxdesign/core first (e.g. `pnpm -F @astryxdesign/core ' +
|
|
2201
2249
|
'build`)' +
|
|
@@ -2303,7 +2351,15 @@ async function themeBuildInternal(
|
|
|
2303
2351
|
const scopeSelector = themeScopeStart(themeDef.name);
|
|
2304
2352
|
const scopeTo = THEME_SCOPE_TO;
|
|
2305
2353
|
|
|
2306
|
-
|
|
2354
|
+
// Older cores ignore this optional collector. Current core writes the same
|
|
2355
|
+
// warning text here that runtime callers receive on the console.
|
|
2356
|
+
/** @type {string[]} */
|
|
2357
|
+
const droppedDeclarations = [];
|
|
2358
|
+
|
|
2359
|
+
const {component, prose} = _generateThemeRulesSplit(
|
|
2360
|
+
resolvedTheme,
|
|
2361
|
+
droppedDeclarations,
|
|
2362
|
+
);
|
|
2307
2363
|
const cssParts = [];
|
|
2308
2364
|
// Prose element defaults always ship — the `<Theme>` runtime
|
|
2309
2365
|
// (generateThemeCSS) always emits them, so the build must too, or the
|
|
@@ -2322,7 +2378,7 @@ async function themeBuildInternal(
|
|
|
2322
2378
|
let adaptationCss;
|
|
2323
2379
|
try {
|
|
2324
2380
|
adaptationCss = _generateAdaptationCSS
|
|
2325
|
-
? _generateAdaptationCSS(resolvedTheme)
|
|
2381
|
+
? _generateAdaptationCSS(resolvedTheme, droppedDeclarations)
|
|
2326
2382
|
: {component: '', prose: ''};
|
|
2327
2383
|
} catch (error) {
|
|
2328
2384
|
const message =
|
|
@@ -2368,13 +2424,37 @@ async function themeBuildInternal(
|
|
|
2368
2424
|
// adaptations on the same resolved leaf.
|
|
2369
2425
|
let onMediaCss = '';
|
|
2370
2426
|
if (_generateOnMediaCSS) {
|
|
2371
|
-
onMediaCss = _generateOnMediaCSS(resolvedTheme);
|
|
2427
|
+
onMediaCss = _generateOnMediaCSS(resolvedTheme, droppedDeclarations);
|
|
2372
2428
|
if (onMediaCss) {
|
|
2373
2429
|
cssParts.push(`@layer astryx-theme {\n${onMediaCss}\n}`);
|
|
2374
2430
|
}
|
|
2375
2431
|
}
|
|
2432
|
+
for (const message of droppedDeclarations) {
|
|
2433
|
+
const w = `Declaration ${message}. The generated CSS omits it; fix the value in the theme source.`;
|
|
2434
|
+
warningMessages.push(w);
|
|
2435
|
+
logger.warn(` [warn] ${w}`);
|
|
2436
|
+
}
|
|
2437
|
+
const unemittedHeadingTypes = headingTypesWithoutEmittedRule(
|
|
2438
|
+
customHeadingTypes(themeDef),
|
|
2439
|
+
`${component.join('\n')}\n${onMediaCss}`,
|
|
2440
|
+
);
|
|
2441
|
+
if (unemittedHeadingTypes.length > 0) {
|
|
2442
|
+
throw new AstryxError(
|
|
2443
|
+
unemittedHeadingTypes
|
|
2444
|
+
.map(
|
|
2445
|
+
type =>
|
|
2446
|
+
`Custom Heading type "${type}" has no declaration the theme ` +
|
|
2447
|
+
`compiler could emit: every declaration in its standalone ` +
|
|
2448
|
+
`"type:${type}" rule was dropped. Give that rule at least one ` +
|
|
2449
|
+
'valid declaration.',
|
|
2450
|
+
)
|
|
2451
|
+
.join('\n'),
|
|
2452
|
+
undefined,
|
|
2453
|
+
ERROR_CODES.ERR_THEME_INVALID,
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2376
2456
|
if (cssParts.length === 0) {
|
|
2377
|
-
logger.log('No overrides found
|
|
2457
|
+
logger.log('No overrides found; nothing to build.');
|
|
2378
2458
|
return null;
|
|
2379
2459
|
}
|
|
2380
2460
|
// The data-token defaults are theme-independent and go in @layer
|
|
@@ -2425,7 +2505,18 @@ async function themeBuildInternal(
|
|
|
2425
2505
|
// Guard: relative paths must not escape cwd via `../`. Absolute paths are
|
|
2426
2506
|
// trusted (the user explicitly controls where output goes, like gcc -o).
|
|
2427
2507
|
if (!path.isAbsolute(options.out)) {
|
|
2428
|
-
|
|
2508
|
+
try {
|
|
2509
|
+
assertWithin(options.out, cwd, {label: 'output path'});
|
|
2510
|
+
} catch (err) {
|
|
2511
|
+
if (err instanceof PathSafetyError) {
|
|
2512
|
+
throw new AstryxError(
|
|
2513
|
+
err.message,
|
|
2514
|
+
undefined,
|
|
2515
|
+
ERROR_CODES.ERR_PATH_TRAVERSAL,
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
2518
|
+
throw err;
|
|
2519
|
+
}
|
|
2429
2520
|
}
|
|
2430
2521
|
} else {
|
|
2431
2522
|
outPath = path.join(path.dirname(filePath), `${baseName}.css`);
|
|
@@ -2534,10 +2625,10 @@ async function themeBuildInternal(
|
|
|
2534
2625
|
const stale = staleBuildOutputs(writes, cwd);
|
|
2535
2626
|
const upToDate = stale.length === 0;
|
|
2536
2627
|
if (upToDate) {
|
|
2537
|
-
logger.log(`\n
|
|
2628
|
+
logger.log(`\n[ok] Theme outputs are up to date with ${sourceRelative}.`);
|
|
2538
2629
|
} else {
|
|
2539
2630
|
logger.error(
|
|
2540
|
-
`\n
|
|
2631
|
+
`\n[fail] ${stale.length} theme output(s) are out of date with ${sourceRelative}:`,
|
|
2541
2632
|
);
|
|
2542
2633
|
for (const s of stale) {
|
|
2543
2634
|
logger.error(
|
|
@@ -2559,17 +2650,17 @@ async function themeBuildInternal(
|
|
|
2559
2650
|
|
|
2560
2651
|
writeBuildOutputs(writes);
|
|
2561
2652
|
|
|
2562
|
-
logger.log(`\n
|
|
2653
|
+
logger.log(`\n[ok] ${path.relative(cwd, outPath)}`);
|
|
2563
2654
|
logger.log(
|
|
2564
2655
|
` ${tokenCount} token overrides, ${componentCount} component overrides`,
|
|
2565
2656
|
);
|
|
2566
2657
|
logger.log(` ${size} KB`);
|
|
2567
|
-
logger.log(
|
|
2568
|
-
logger.log(
|
|
2658
|
+
logger.log(`[ok] ${path.relative(cwd, jsPath)}`);
|
|
2659
|
+
logger.log(`[ok] ${path.relative(cwd, dtsPath)}`);
|
|
2569
2660
|
if (variantDtsPath && variantDecl) {
|
|
2570
2661
|
const augCount = (variantDecl.match(/': true;/g) || []).length;
|
|
2571
2662
|
logger.log(
|
|
2572
|
-
|
|
2663
|
+
`[ok] ${path.relative(cwd, variantDtsPath)} (${augCount} type augmentations)`,
|
|
2573
2664
|
);
|
|
2574
2665
|
}
|
|
2575
2666
|
|
|
@@ -2579,7 +2670,7 @@ async function themeBuildInternal(
|
|
|
2579
2670
|
const cssImport = importSpecifier(relOutDir, cssBase) + '.css';
|
|
2580
2671
|
const exportName = `${toIdentifier(baseName)}Theme`;
|
|
2581
2672
|
logger.log(`
|
|
2582
|
-
Install in your app (paths are relative to a file in src
|
|
2673
|
+
Install in your app (paths are relative to a file in src/; adjust if yours lives elsewhere):
|
|
2583
2674
|
|
|
2584
2675
|
import { ${exportName} } from '${jsImport}';
|
|
2585
2676
|
import '${cssImport}';
|
|
@@ -2611,7 +2702,7 @@ Or with a <link> tag:
|
|
|
2611
2702
|
// Adaptation rules are resolved theme writes in their own right, so a family
|
|
2612
2703
|
// named only inside one needs the same notice as one named at the root.
|
|
2613
2704
|
for (const family of unloadedFonts) {
|
|
2614
|
-
const msg = `Font "${family}" is named by this theme but not loaded
|
|
2705
|
+
const msg = `Font "${family}" is named by this theme but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`;
|
|
2615
2706
|
noticeMessages.push(msg);
|
|
2616
2707
|
logger.log(` note: ${msg}`);
|
|
2617
2708
|
}
|
|
@@ -2905,11 +2996,11 @@ export async function themeBuildFamily(
|
|
|
2905
2996
|
const upToDate = stale.length === 0;
|
|
2906
2997
|
if (upToDate) {
|
|
2907
2998
|
logger.log(
|
|
2908
|
-
`\n
|
|
2999
|
+
`\n[ok] Theme family outputs are up to date with ${sourceRelative}.`,
|
|
2909
3000
|
);
|
|
2910
3001
|
} else {
|
|
2911
3002
|
logger.error(
|
|
2912
|
-
`\n
|
|
3003
|
+
`\n[fail] ${stale.length} theme family output(s) are out of date:`,
|
|
2913
3004
|
);
|
|
2914
3005
|
for (const entry of stale) {
|
|
2915
3006
|
logger.error(
|
|
@@ -2929,7 +3020,7 @@ export async function themeBuildFamily(
|
|
|
2929
3020
|
};
|
|
2930
3021
|
}
|
|
2931
3022
|
writeBuildOutputs(writes);
|
|
2932
|
-
for (const output of Object.values(outputs)) logger.log(
|
|
3023
|
+
for (const output of Object.values(outputs)) logger.log(`[ok] ${output}`);
|
|
2933
3024
|
const themeBindings = members.map(member => bindings.get(member.theme.name));
|
|
2934
3025
|
const relOutDir = path.relative(cwd, outDir) || '.';
|
|
2935
3026
|
const jsImport = importSpecifier(relOutDir, baseName);
|
|
@@ -2947,7 +3038,7 @@ Or load the same stylesheet natively:
|
|
|
2947
3038
|
];
|
|
2948
3039
|
const notices = unloadedFonts.map(
|
|
2949
3040
|
family =>
|
|
2950
|
-
`Font "${family}" is named by this theme family but not loaded
|
|
3041
|
+
`Font "${family}" is named by this theme family but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`,
|
|
2951
3042
|
);
|
|
2952
3043
|
for (const notice of notices) logger.log(` note: ${notice}`);
|
|
2953
3044
|
return {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* next one is covered without touching this file.
|
|
20
20
|
*
|
|
21
21
|
* Asserted on the receipt's `warnings` rather than on a rejection: a private
|
|
22
|
-
* var is reported (logged
|
|
22
|
+
* var is reported (logged `[error]`, collected into the receipt) and the build then
|
|
23
23
|
* emits its CSS and resolves anyway. Asserting a throw would pass for the
|
|
24
24
|
* wrong reason — it never throws, which is why a throwaway build read as a
|
|
25
25
|
* pass on the first version of #5214.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every field `themeBuild()` returns is named in its FunctionDoc and in the
|
|
5
|
+
* response-types doc.
|
|
6
|
+
*
|
|
7
|
+
* The receipt is a stable machine-readable response, so each field needs
|
|
8
|
+
* consumer documentation, not only a typedef. The fields are read off a real
|
|
9
|
+
* build, so a field added to the receipt without documentation fails here.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {describe, it, expect, beforeAll, afterEach} from 'vitest';
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import {themeBuild} from './build.mjs';
|
|
17
|
+
import {doc} from '../themeBuild.doc.mjs';
|
|
18
|
+
import {doc as responseTypes} from '../../../foundation/response/response-types.doc.mjs';
|
|
19
|
+
import {ensureCoreBuilt} from '../../../clients/cli/commands/ensure-core-built.mjs';
|
|
20
|
+
|
|
21
|
+
/** @type {string[]} */
|
|
22
|
+
const dirs = [];
|
|
23
|
+
|
|
24
|
+
beforeAll(() => {
|
|
25
|
+
ensureCoreBuilt();
|
|
26
|
+
}, 200_000);
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
for (const dir of dirs.splice(0)) {
|
|
30
|
+
fs.rmSync(dir, {recursive: true, force: true});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** @returns {string} a temp dir holding a theme that exercises every field */
|
|
35
|
+
function themeDir() {
|
|
36
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-receipt-doc-'));
|
|
37
|
+
dirs.push(dir);
|
|
38
|
+
fs.writeFileSync(
|
|
39
|
+
path.join(dir, 'ocean.mjs'),
|
|
40
|
+
`export default {
|
|
41
|
+
name: 'ocean',
|
|
42
|
+
tokens: {'--color-background-body': '#ffffff', '--font-family-body': '"Inter", sans-serif'},
|
|
43
|
+
components: {progressbar: {base: {color: 'red'}}},
|
|
44
|
+
};
|
|
45
|
+
`,
|
|
46
|
+
);
|
|
47
|
+
return dir;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @param {string} type */
|
|
51
|
+
function returnsDescription(type) {
|
|
52
|
+
const entry = doc.returns?.find(r => r.type === type);
|
|
53
|
+
expect(entry, `themeBuild doc has no ${type} return`).toBeDefined();
|
|
54
|
+
return /** @type {{description: string}} */ (entry).description;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @param {string} description @param {string[]} fields */
|
|
58
|
+
function undocumented(description, fields) {
|
|
59
|
+
return fields.filter(
|
|
60
|
+
field => !new RegExp(String.raw`\b${field}\b`).test(description),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe('themeBuild() consumer docs name every receipt field', () => {
|
|
65
|
+
it('theme.build', async () => {
|
|
66
|
+
const dir = themeDir();
|
|
67
|
+
const result = await themeBuild('ocean.mjs', {}, {cwd: dir});
|
|
68
|
+
|
|
69
|
+
expect(result?.type).toBe('theme.build');
|
|
70
|
+
const data = /** @type {Record<string, any>} */ (result?.data);
|
|
71
|
+
// The theme is chosen so the optional arrays are non-empty.
|
|
72
|
+
expect(data.warnings.length).toBeGreaterThan(0);
|
|
73
|
+
expect(data.notices.length).toBeGreaterThan(0);
|
|
74
|
+
|
|
75
|
+
const fields = [
|
|
76
|
+
...Object.keys(data),
|
|
77
|
+
...Object.keys(data.outputs),
|
|
78
|
+
'variantsDts',
|
|
79
|
+
];
|
|
80
|
+
expect(undocumented(returnsDescription('theme.build'), fields)).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('theme.build in the response-types doc', async () => {
|
|
84
|
+
const dir = themeDir();
|
|
85
|
+
const result = await themeBuild('ocean.mjs', {}, {cwd: dir});
|
|
86
|
+
const data = /** @type {Record<string, any>} */ (result?.data);
|
|
87
|
+
const member = responseTypes.members.find(m => m.value === 'theme.build');
|
|
88
|
+
expect(member, 'response-types has no theme.build member').toBeDefined();
|
|
89
|
+
|
|
90
|
+
const fields = [
|
|
91
|
+
...Object.keys(data),
|
|
92
|
+
...Object.keys(data.outputs),
|
|
93
|
+
'variantsDts',
|
|
94
|
+
];
|
|
95
|
+
expect(
|
|
96
|
+
undocumented(/** @type {{description: string}} */ (member).description, fields),
|
|
97
|
+
).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('theme.build.check', async () => {
|
|
101
|
+
const dir = themeDir();
|
|
102
|
+
const result = await themeBuild('ocean.mjs', {check: true}, {cwd: dir});
|
|
103
|
+
|
|
104
|
+
expect(result?.type).toBe('theme.build.check');
|
|
105
|
+
const data = /** @type {Record<string, any>} */ (result?.data);
|
|
106
|
+
const fields = [...Object.keys(data), ...Object.keys(data.stale[0])];
|
|
107
|
+
expect(
|
|
108
|
+
undocumented(returnsDescription('theme.build.check'), fields),
|
|
109
|
+
).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -206,6 +206,133 @@ describe('themeBuild() — receipt', () => {
|
|
|
206
206
|
});
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
+
describe('themeBuild() — dropped declarations reach the receipt', () => {
|
|
210
|
+
// Core's generator refuses a declaration whose value would end it early
|
|
211
|
+
// (an unquoted `;` or brace, an unclosed string/comment/url). The runtime
|
|
212
|
+
// says so on the console; a build has a receipt, so every drop must land in
|
|
213
|
+
// `warnings` — a programmatic caller otherwise sees `warnings: []` while the
|
|
214
|
+
// CSS silently omits a value the generated JS still carries.
|
|
215
|
+
it('reports every dropped declaration, from every generator path, in warnings', async () => {
|
|
216
|
+
const themeFile = path.join(tmpDir, 'dropped.mjs');
|
|
217
|
+
fs.writeFileSync(
|
|
218
|
+
themeFile,
|
|
219
|
+
`export default {
|
|
220
|
+
name: 'dropped',
|
|
221
|
+
tokens: {
|
|
222
|
+
'--color-bg': '#0a0a0a } body { background: url(https://example.com/token) ',
|
|
223
|
+
},
|
|
224
|
+
components: {
|
|
225
|
+
button: {
|
|
226
|
+
'variant:secondary': {
|
|
227
|
+
backgroundColor: 'red; background-image: url(https://example.com/component)',
|
|
228
|
+
':hover': {color: '"https://example.com/pseudo'},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
onDark: {
|
|
233
|
+
tokens: {'--color-bg': 'url(https://example.com/dark'},
|
|
234
|
+
},
|
|
235
|
+
adaptations: {
|
|
236
|
+
rules: [
|
|
237
|
+
{
|
|
238
|
+
when: {pointer: 'coarse'},
|
|
239
|
+
value: {tokens: {'--color-bg': 'red /* https://example.com/adaptation'}},
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
};\n`,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
247
|
+
let result;
|
|
248
|
+
try {
|
|
249
|
+
result = await themeBuild('dropped.mjs', {}, {cwd: tmpDir});
|
|
250
|
+
// Drops are collected, not printed: the programmatic API stays silent.
|
|
251
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
252
|
+
} finally {
|
|
253
|
+
warnSpy.mockRestore();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const css = fs.readFileSync(path.join(tmpDir, 'dropped.css'), 'utf8');
|
|
257
|
+
expect(css).not.toContain('example.com');
|
|
258
|
+
|
|
259
|
+
const warnings = result?.data.warnings ?? [];
|
|
260
|
+
expect(warnings).toHaveLength(5);
|
|
261
|
+
expect(warnings).toEqual(
|
|
262
|
+
expect.arrayContaining([
|
|
263
|
+
expect.stringMatching(
|
|
264
|
+
/^Declaration dropped "--color-bg" in tokens: an unquoted "}" would close the rule/,
|
|
265
|
+
),
|
|
266
|
+
expect.stringMatching(
|
|
267
|
+
/^Declaration dropped "background-color" in components\.button\["variant:secondary"\]: an unquoted ";" would end the declaration/,
|
|
268
|
+
),
|
|
269
|
+
expect.stringMatching(
|
|
270
|
+
/^Declaration dropped "color" in components\.button\["variant:secondary"\]\[":hover"\]: an unclosed " string/,
|
|
271
|
+
),
|
|
272
|
+
expect.stringMatching(
|
|
273
|
+
/^Declaration dropped "--color-bg" in onDark\.tokens: an unclosed url\(/,
|
|
274
|
+
),
|
|
275
|
+
expect.stringMatching(
|
|
276
|
+
/^Declaration dropped "--color-bg" in adaptations\[0\]\.tokens: an unclosed \/\* comment/,
|
|
277
|
+
),
|
|
278
|
+
]),
|
|
279
|
+
);
|
|
280
|
+
for (const w of warnings) {
|
|
281
|
+
expect(w).toContain('The generated CSS omits it');
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('generates CSS for legacy null tokens without losing neighboring declarations', async () => {
|
|
286
|
+
fs.writeFileSync(
|
|
287
|
+
path.join(tmpDir, 'legacy.mjs'),
|
|
288
|
+
`export default {
|
|
289
|
+
name: 'legacy',
|
|
290
|
+
tokens: {'--color-background-body': null, '--spacing-4': 12},
|
|
291
|
+
onDark: {tokens: {'--color-background-body': null}},
|
|
292
|
+
components: {button: {base: {borderRadius: '4px'}}},
|
|
293
|
+
};\n`,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const result = await themeBuild('legacy.mjs', {}, {cwd: tmpDir});
|
|
297
|
+
const css = fs.readFileSync(path.join(tmpDir, 'legacy.css'), 'utf8');
|
|
298
|
+
expect(css).toContain('--color-background-body: null;');
|
|
299
|
+
expect(css).toContain('--spacing-4: 12;');
|
|
300
|
+
expect(css).toContain('border-radius: 4px;');
|
|
301
|
+
expect(result?.data.warnings).toEqual([]);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('keeps valid CSS that carries semicolons, and reports nothing', async () => {
|
|
305
|
+
const themeFile = path.join(tmpDir, 'kept.mjs');
|
|
306
|
+
fs.writeFileSync(
|
|
307
|
+
themeFile,
|
|
308
|
+
`export default {
|
|
309
|
+
name: 'kept',
|
|
310
|
+
tokens: {'--font-family-body': 'Gill\\\\ Sans, "Segoe;UI", serif /* ; */'},
|
|
311
|
+
components: {
|
|
312
|
+
button: {
|
|
313
|
+
'variant:secondary': {
|
|
314
|
+
backgroundImage: 'URL(data:image/svg+xml;base64,PHN2Zz4=)',
|
|
315
|
+
WebkitLineClamp: '2',
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
};\n`,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const result = await themeBuild('kept.mjs', {}, {cwd: tmpDir});
|
|
323
|
+
const css = fs.readFileSync(path.join(tmpDir, 'kept.css'), 'utf8');
|
|
324
|
+
|
|
325
|
+
expect(css).toContain(
|
|
326
|
+
'--font-family-body: Gill\\ Sans, "Segoe;UI", serif /* ; */;',
|
|
327
|
+
);
|
|
328
|
+
expect(css).toContain(
|
|
329
|
+
'background-image: URL(data:image/svg+xml;base64,PHN2Zz4=);',
|
|
330
|
+
);
|
|
331
|
+
expect(css).toContain('-webkit-line-clamp: 2;');
|
|
332
|
+
expect(result?.data.warnings).toEqual([]);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
209
336
|
describe('themeBuild() — nothing to build', () => {
|
|
210
337
|
it('returns null and writes nothing when the generator yields no CSS', async () => {
|
|
211
338
|
const themeFile = path.join(tmpDir, 'empty.mjs');
|