@polymorphism-tech/morph-spec 8.47.0 → 9.1.0
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/CLAUDE.md +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,508 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MORPH-SPEC Design System Generator Library
|
|
3
|
-
*
|
|
4
|
-
* Reads design-system.md and generates:
|
|
5
|
-
* - CSS variables (design-system.css)
|
|
6
|
-
*
|
|
7
|
-
* Supports BOTH:
|
|
8
|
-
* - New CSS :root format (design-system.md template v2)
|
|
9
|
-
* - Old markdown list format (backward compat)
|
|
10
|
-
*
|
|
11
|
-
* Used both by CLI commands and internal automation.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { readFileSync, existsSync } from 'fs';
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// CSS :root Helpers
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Extract all CSS :root blocks from markdown (inside ```css fences)
|
|
22
|
-
* @param {string} markdown
|
|
23
|
-
* @returns {string} Combined CSS content from all :root blocks
|
|
24
|
-
*/
|
|
25
|
-
function extractCSSRootBlocks(markdown) {
|
|
26
|
-
const cssBlocks = [];
|
|
27
|
-
const fenceRegex = /```css\s*\n([\s\S]*?)```/g;
|
|
28
|
-
let match;
|
|
29
|
-
while ((match = fenceRegex.exec(markdown)) !== null) {
|
|
30
|
-
cssBlocks.push(match[1]);
|
|
31
|
-
}
|
|
32
|
-
return cssBlocks.join('\n');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract CSS custom properties from :root blocks
|
|
37
|
-
* Returns array of { name, value } for all --xxx: yyy; declarations
|
|
38
|
-
*/
|
|
39
|
-
function extractCSSProperties(cssContent) {
|
|
40
|
-
const props = [];
|
|
41
|
-
const propRegex = /(--[\w-]+)\s*:\s*([^;]+);/g;
|
|
42
|
-
let match;
|
|
43
|
-
while ((match = propRegex.exec(cssContent)) !== null) {
|
|
44
|
-
props.push({ name: match[1].trim(), value: match[2].trim() });
|
|
45
|
-
}
|
|
46
|
-
return props;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ============================================================================
|
|
50
|
-
// Parsing Functions
|
|
51
|
-
// ============================================================================
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Parse colors from markdown — supports CSS :root format and old markdown format
|
|
55
|
-
* @param {string} markdown - Markdown content
|
|
56
|
-
* @returns {Object} Colors object with primary, accent, secondary, neutral, semantic, surface, text, other
|
|
57
|
-
*/
|
|
58
|
-
export function parseColors(markdown) {
|
|
59
|
-
const colors = {
|
|
60
|
-
primary: {},
|
|
61
|
-
accent: {},
|
|
62
|
-
secondary: {},
|
|
63
|
-
neutral: {},
|
|
64
|
-
semantic: {},
|
|
65
|
-
surface: {},
|
|
66
|
-
text: {},
|
|
67
|
-
other: {}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Try CSS :root format first
|
|
71
|
-
const cssContent = extractCSSRootBlocks(markdown);
|
|
72
|
-
const props = extractCSSProperties(cssContent);
|
|
73
|
-
|
|
74
|
-
let cssColorCount = 0;
|
|
75
|
-
|
|
76
|
-
for (const { name, value } of props) {
|
|
77
|
-
if (!name.startsWith('--color-')) continue;
|
|
78
|
-
|
|
79
|
-
const suffix = name.slice('--color-'.length); // e.g. "primary", "primary-light", "neutral-500"
|
|
80
|
-
|
|
81
|
-
if (suffix.startsWith('primary-rgb')) {
|
|
82
|
-
// Store RGB metadata as-is
|
|
83
|
-
colors.primary['rgb'] = value;
|
|
84
|
-
cssColorCount++;
|
|
85
|
-
} else if (suffix.startsWith('primary')) {
|
|
86
|
-
const variant = suffix.slice('primary'.length).replace(/^-/, '') || 'default';
|
|
87
|
-
colors.primary[variant] = value;
|
|
88
|
-
cssColorCount++;
|
|
89
|
-
} else if (suffix.startsWith('accent')) {
|
|
90
|
-
const variant = suffix.slice('accent'.length).replace(/^-/, '') || 'default';
|
|
91
|
-
colors.accent[variant] = value;
|
|
92
|
-
cssColorCount++;
|
|
93
|
-
} else if (suffix.startsWith('neutral-')) {
|
|
94
|
-
const variant = suffix.slice('neutral-'.length);
|
|
95
|
-
colors.neutral[variant] = value;
|
|
96
|
-
cssColorCount++;
|
|
97
|
-
} else if (suffix.startsWith('surface-')) {
|
|
98
|
-
const variant = suffix.slice('surface-'.length);
|
|
99
|
-
colors.surface[variant] = value;
|
|
100
|
-
cssColorCount++;
|
|
101
|
-
} else if (suffix.startsWith('text-')) {
|
|
102
|
-
const variant = suffix.slice('text-'.length);
|
|
103
|
-
colors.text[variant] = value;
|
|
104
|
-
cssColorCount++;
|
|
105
|
-
} else if (suffix === 'success' || suffix === 'warning' || suffix === 'error' || suffix === 'info') {
|
|
106
|
-
colors.semantic[suffix] = value;
|
|
107
|
-
cssColorCount++;
|
|
108
|
-
} else if (suffix === 'border') {
|
|
109
|
-
colors.other['border'] = value;
|
|
110
|
-
cssColorCount++;
|
|
111
|
-
} else {
|
|
112
|
-
// Any other --color-xxx token
|
|
113
|
-
colors.other[suffix] = value;
|
|
114
|
-
cssColorCount++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// If CSS format found colors, return them
|
|
119
|
-
if (cssColorCount > 0) {
|
|
120
|
-
return colors;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// FALL BACK to old markdown regex format
|
|
124
|
-
const colorRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^\(]*\(`(#[0-9A-Fa-f]{6})`\)/gm;
|
|
125
|
-
|
|
126
|
-
let match;
|
|
127
|
-
while ((match = colorRegex.exec(markdown)) !== null) {
|
|
128
|
-
const [, name, hex] = match;
|
|
129
|
-
|
|
130
|
-
if (name.includes('Primary')) {
|
|
131
|
-
const variant = name.match(/\d+/)?.[0] || 'default';
|
|
132
|
-
colors.primary[variant] = hex;
|
|
133
|
-
} else if (name.includes('Secondary')) {
|
|
134
|
-
const variant = name.match(/\d+/)?.[0] || 'default';
|
|
135
|
-
colors.secondary[variant] = hex;
|
|
136
|
-
} else if (name.includes('Neutral') || name.includes('Gray')) {
|
|
137
|
-
const variant = name.match(/\d+/)?.[0] || 'default';
|
|
138
|
-
colors.neutral[variant] = hex;
|
|
139
|
-
} else if (name.includes('Success') || name.includes('Error') || name.includes('Warning') || name.includes('Info')) {
|
|
140
|
-
const type = name.toLowerCase().replace(/\s+/g, '-');
|
|
141
|
-
colors.semantic[type] = hex;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return colors;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Parse typography from markdown — supports CSS :root format and old markdown format
|
|
150
|
-
* @param {string} markdown - Markdown content
|
|
151
|
-
* @returns {Object} Typography with fontFamily, fontSize, fontWeight, lineHeight
|
|
152
|
-
*/
|
|
153
|
-
export function parseTypography(markdown) {
|
|
154
|
-
const typography = {
|
|
155
|
-
fontFamily: {},
|
|
156
|
-
fontSize: {},
|
|
157
|
-
fontWeight: {},
|
|
158
|
-
lineHeight: {}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Try CSS :root format first
|
|
162
|
-
const cssContent = extractCSSRootBlocks(markdown);
|
|
163
|
-
const props = extractCSSProperties(cssContent);
|
|
164
|
-
|
|
165
|
-
let cssTypoCount = 0;
|
|
166
|
-
|
|
167
|
-
for (const { name, value } of props) {
|
|
168
|
-
if (name === '--font-display') {
|
|
169
|
-
typography.fontFamily.display = value;
|
|
170
|
-
cssTypoCount++;
|
|
171
|
-
} else if (name === '--font-body') {
|
|
172
|
-
typography.fontFamily.body = value;
|
|
173
|
-
cssTypoCount++;
|
|
174
|
-
} else if (name === '--font-mono') {
|
|
175
|
-
typography.fontFamily.mono = value;
|
|
176
|
-
cssTypoCount++;
|
|
177
|
-
} else if (name.startsWith('--text-')) {
|
|
178
|
-
// Font sizes: --text-xs, --text-sm, --text-base, etc.
|
|
179
|
-
const sizeName = name.slice('--text-'.length);
|
|
180
|
-
typography.fontSize[sizeName] = value;
|
|
181
|
-
cssTypoCount++;
|
|
182
|
-
} else if (name.startsWith('--font-weight-')) {
|
|
183
|
-
const weightName = name.slice('--font-weight-'.length);
|
|
184
|
-
typography.fontWeight[weightName] = value;
|
|
185
|
-
cssTypoCount++;
|
|
186
|
-
} else if (name.startsWith('--leading-')) {
|
|
187
|
-
const leadingName = name.slice('--leading-'.length);
|
|
188
|
-
typography.lineHeight[leadingName] = value;
|
|
189
|
-
cssTypoCount++;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// If CSS format found typography, return it
|
|
194
|
-
if (cssTypoCount > 0) {
|
|
195
|
-
return typography;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// FALL BACK to old markdown format
|
|
199
|
-
// Match font family
|
|
200
|
-
const fontFamilyRegex = /(?:font[- ]?family|typeface)[:\s]+([^\n]+)/gi;
|
|
201
|
-
const fontMatch = fontFamilyRegex.exec(markdown);
|
|
202
|
-
if (fontMatch) {
|
|
203
|
-
typography.fontFamily.primary = fontMatch[1].trim();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Match font sizes (but exclude spacing-related)
|
|
207
|
-
const fontSizeRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
|
|
208
|
-
let match;
|
|
209
|
-
while ((match = fontSizeRegex.exec(markdown)) !== null) {
|
|
210
|
-
const [, name, size] = match;
|
|
211
|
-
const lowerName = name.toLowerCase();
|
|
212
|
-
// Skip if it's spacing-related
|
|
213
|
-
if (lowerName.includes('spacing') || lowerName.includes('gap') || lowerName.includes('margin') || lowerName.includes('padding')) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
|
|
217
|
-
typography.fontSize[cleanName] = size;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return typography;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Parse spacing from markdown — supports CSS :root format and old markdown format
|
|
225
|
-
* @param {string} markdown - Markdown content
|
|
226
|
-
* @returns {Object} Spacing values
|
|
227
|
-
*/
|
|
228
|
-
export function parseSpacing(markdown) {
|
|
229
|
-
const spacing = {};
|
|
230
|
-
|
|
231
|
-
// Try CSS :root format first
|
|
232
|
-
const cssContent = extractCSSRootBlocks(markdown);
|
|
233
|
-
const props = extractCSSProperties(cssContent);
|
|
234
|
-
|
|
235
|
-
let cssSpaceCount = 0;
|
|
236
|
-
|
|
237
|
-
for (const { name, value } of props) {
|
|
238
|
-
if (name.startsWith('--space-')) {
|
|
239
|
-
const spaceName = name.slice('--space-'.length);
|
|
240
|
-
spacing[spaceName] = value;
|
|
241
|
-
cssSpaceCount++;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// If CSS format found spacing, return it
|
|
246
|
-
if (cssSpaceCount > 0) {
|
|
247
|
-
return spacing;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// FALL BACK to old markdown format
|
|
251
|
-
const spacingRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
|
|
252
|
-
let match;
|
|
253
|
-
while ((match = spacingRegex.exec(markdown)) !== null) {
|
|
254
|
-
const [, name, value] = match;
|
|
255
|
-
if (name.toLowerCase().includes('spacing') || name.toLowerCase().includes('gap') || name.toLowerCase().includes('margin') || name.toLowerCase().includes('padding')) {
|
|
256
|
-
const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
|
|
257
|
-
spacing[cleanName] = value;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return spacing;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Parse motion tokens from markdown (CSS :root format only)
|
|
266
|
-
* @param {string} markdown - Markdown content
|
|
267
|
-
* @returns {Object} { durations, easings, scroll }
|
|
268
|
-
*/
|
|
269
|
-
export function parseMotion(markdown) {
|
|
270
|
-
const motion = {
|
|
271
|
-
durations: {},
|
|
272
|
-
easings: {},
|
|
273
|
-
scroll: {}
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const cssContent = extractCSSRootBlocks(markdown);
|
|
277
|
-
const props = extractCSSProperties(cssContent);
|
|
278
|
-
|
|
279
|
-
for (const { name, value } of props) {
|
|
280
|
-
if (name.startsWith('--duration-')) {
|
|
281
|
-
const key = name.slice('--duration-'.length);
|
|
282
|
-
motion.durations[key] = value;
|
|
283
|
-
} else if (name.startsWith('--easing-')) {
|
|
284
|
-
const key = name.slice('--easing-'.length);
|
|
285
|
-
motion.easings[key] = value;
|
|
286
|
-
} else if (name.startsWith('--scroll-')) {
|
|
287
|
-
const key = name.slice('--scroll-'.length);
|
|
288
|
-
motion.scroll[key] = value;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return motion;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Parse advanced effects tokens from markdown (CSS :root format only)
|
|
297
|
-
* @param {string} markdown - Markdown content
|
|
298
|
-
* @returns {Object} { glass, aurora, perspective, container, viewTransition, noise }
|
|
299
|
-
*/
|
|
300
|
-
export function parseAdvancedEffects(markdown) {
|
|
301
|
-
const effects = {
|
|
302
|
-
glass: {},
|
|
303
|
-
aurora: {},
|
|
304
|
-
perspective: {},
|
|
305
|
-
container: {},
|
|
306
|
-
viewTransition: {},
|
|
307
|
-
noise: {}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const cssContent = extractCSSRootBlocks(markdown);
|
|
311
|
-
const props = extractCSSProperties(cssContent);
|
|
312
|
-
|
|
313
|
-
for (const { name, value } of props) {
|
|
314
|
-
if (name.startsWith('--glass-')) {
|
|
315
|
-
const key = name.slice('--glass-'.length);
|
|
316
|
-
effects.glass[key] = value;
|
|
317
|
-
} else if (name.startsWith('--aurora-')) {
|
|
318
|
-
const key = name.slice('--aurora-'.length);
|
|
319
|
-
effects.aurora[key] = value;
|
|
320
|
-
} else if (name.startsWith('--noise-')) {
|
|
321
|
-
const key = name.slice('--noise-'.length);
|
|
322
|
-
effects.noise[key] = value;
|
|
323
|
-
} else if (name.startsWith('--perspective-')) {
|
|
324
|
-
const key = name.slice('--perspective-'.length);
|
|
325
|
-
effects.perspective[key] = value;
|
|
326
|
-
} else if (name.startsWith('--container-')) {
|
|
327
|
-
const key = name.slice('--container-'.length);
|
|
328
|
-
effects.container[key] = value;
|
|
329
|
-
} else if (name.startsWith('--view-transition-')) {
|
|
330
|
-
const key = name.slice('--view-transition-'.length);
|
|
331
|
-
effects.viewTransition[key] = value;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return effects;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Parse all design system properties from markdown
|
|
340
|
-
* @param {string} markdownPath - Path to design-system.md file
|
|
341
|
-
* @returns {Object} Parsed design system with colors, typography, spacing, motion, effects
|
|
342
|
-
*/
|
|
343
|
-
export function parseDesignSystem(markdownPath) {
|
|
344
|
-
if (!existsSync(markdownPath)) {
|
|
345
|
-
throw new Error(`File not found: ${markdownPath}`);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const markdown = readFileSync(markdownPath, 'utf-8');
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
colors: parseColors(markdown),
|
|
352
|
-
typography: parseTypography(markdown),
|
|
353
|
-
spacing: parseSpacing(markdown),
|
|
354
|
-
motion: parseMotion(markdown),
|
|
355
|
-
effects: parseAdvancedEffects(markdown)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// ============================================================================
|
|
360
|
-
// Generation Functions
|
|
361
|
-
// ============================================================================
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Helper to emit CSS variable entries for a color category
|
|
365
|
-
*/
|
|
366
|
-
function emitColorCategory(colors, prefix, label) {
|
|
367
|
-
if (Object.keys(colors).length === 0) return '';
|
|
368
|
-
let css = ` /* ${label} */\n`;
|
|
369
|
-
Object.entries(colors).forEach(([variant, value]) => {
|
|
370
|
-
if (variant === 'rgb') {
|
|
371
|
-
css += ` --color-${prefix}-rgb: ${value};\n`;
|
|
372
|
-
} else if (variant === 'default') {
|
|
373
|
-
css += ` --color-${prefix}: ${value};\n`;
|
|
374
|
-
} else {
|
|
375
|
-
css += ` --color-${prefix}-${variant}: ${value};\n`;
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
css += `\n`;
|
|
379
|
-
return css;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Generate CSS variables file
|
|
384
|
-
* @param {Object} colors - Colors object
|
|
385
|
-
* @param {Object} typography - Typography object
|
|
386
|
-
* @param {Object} spacing - Spacing object
|
|
387
|
-
* @returns {string} CSS content
|
|
388
|
-
*/
|
|
389
|
-
export function generateCSS(colors, typography, spacing) {
|
|
390
|
-
let css = `/* Auto-generated by MORPH-SPEC Design System Generator */\n`;
|
|
391
|
-
css += `/* Do not edit manually - regenerate from design-system.md */\n\n`;
|
|
392
|
-
css += `:root {\n`;
|
|
393
|
-
|
|
394
|
-
// Colors — handle expanded structure
|
|
395
|
-
css += emitColorCategory(colors.primary, 'primary', 'Primary Colors');
|
|
396
|
-
|
|
397
|
-
// Support both accent (new) and secondary (old)
|
|
398
|
-
if (Object.keys(colors.accent || {}).length > 0) {
|
|
399
|
-
css += emitColorCategory(colors.accent, 'accent', 'Accent Colors');
|
|
400
|
-
}
|
|
401
|
-
if (Object.keys(colors.secondary || {}).length > 0) {
|
|
402
|
-
css += emitColorCategory(colors.secondary, 'secondary', 'Secondary Colors');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
css += emitColorCategory(colors.neutral || {}, 'neutral', 'Neutral Colors');
|
|
406
|
-
|
|
407
|
-
if (Object.keys(colors.surface || {}).length > 0) {
|
|
408
|
-
css += emitColorCategory(colors.surface, 'surface', 'Surface Colors');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (Object.keys(colors.text || {}).length > 0) {
|
|
412
|
-
css += emitColorCategory(colors.text, 'text', 'Text Colors');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (Object.keys(colors.semantic || {}).length > 0) {
|
|
416
|
-
css += ` /* Semantic Colors */\n`;
|
|
417
|
-
Object.entries(colors.semantic).forEach(([type, hex]) => {
|
|
418
|
-
css += ` --color-${type}: ${hex};\n`;
|
|
419
|
-
});
|
|
420
|
-
css += `\n`;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (Object.keys(colors.other || {}).length > 0) {
|
|
424
|
-
css += ` /* Other Colors */\n`;
|
|
425
|
-
Object.entries(colors.other).forEach(([key, value]) => {
|
|
426
|
-
css += ` --color-${key}: ${value};\n`;
|
|
427
|
-
});
|
|
428
|
-
css += `\n`;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Typography
|
|
432
|
-
const fontFamilies = typography.fontFamily || {};
|
|
433
|
-
if (fontFamilies.display || fontFamilies.body || fontFamilies.mono || fontFamilies.primary) {
|
|
434
|
-
css += ` /* Typography */\n`;
|
|
435
|
-
if (fontFamilies.display) css += ` --font-display: ${fontFamilies.display};\n`;
|
|
436
|
-
if (fontFamilies.body) css += ` --font-body: ${fontFamilies.body};\n`;
|
|
437
|
-
if (fontFamilies.mono) css += ` --font-mono: ${fontFamilies.mono};\n`;
|
|
438
|
-
if (fontFamilies.primary) css += ` --font-family-primary: ${fontFamilies.primary};\n`;
|
|
439
|
-
css += `\n`;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (Object.keys(typography.fontSize).length > 0) {
|
|
443
|
-
css += ` /* Font Sizes */\n`;
|
|
444
|
-
Object.entries(typography.fontSize).forEach(([name, size]) => {
|
|
445
|
-
css += ` --font-size-${name}: ${size};\n`;
|
|
446
|
-
});
|
|
447
|
-
css += `\n`;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Spacing
|
|
451
|
-
if (Object.keys(spacing).length > 0) {
|
|
452
|
-
css += ` /* Spacing */\n`;
|
|
453
|
-
Object.entries(spacing).forEach(([name, value]) => {
|
|
454
|
-
// If key is numeric (from --space-N), use --space-N format
|
|
455
|
-
if (/^\d+$/.test(name)) {
|
|
456
|
-
css += ` --space-${name}: ${value};\n`;
|
|
457
|
-
} else {
|
|
458
|
-
css += ` --${name}: ${value};\n`;
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
css += `}\n`;
|
|
464
|
-
|
|
465
|
-
return css;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Count non-empty keys in an object
|
|
470
|
-
*/
|
|
471
|
-
function countTokens(obj) {
|
|
472
|
-
return Object.keys(obj || {}).length;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Generate all design system files
|
|
477
|
-
* @param {string} designSystemPath - Path to design-system.md
|
|
478
|
-
* @param {Object} options - Generation options
|
|
479
|
-
* @returns {Object} Generated files content
|
|
480
|
-
*/
|
|
481
|
-
export function generateDesignSystem(designSystemPath, options = {}) {
|
|
482
|
-
// Parse design system
|
|
483
|
-
const { colors, typography, spacing, motion, effects } = parseDesignSystem(designSystemPath);
|
|
484
|
-
|
|
485
|
-
const generated = {
|
|
486
|
-
css: generateCSS(colors, typography, spacing),
|
|
487
|
-
stats: {
|
|
488
|
-
primaryColors: countTokens(colors.primary),
|
|
489
|
-
accentColors: countTokens(colors.accent),
|
|
490
|
-
secondaryColors: countTokens(colors.secondary),
|
|
491
|
-
neutralColors: countTokens(colors.neutral),
|
|
492
|
-
semanticColors: countTokens(colors.semantic),
|
|
493
|
-
surfaceColors: countTokens(colors.surface),
|
|
494
|
-
textColors: countTokens(colors.text),
|
|
495
|
-
fontSizes: countTokens(typography.fontSize),
|
|
496
|
-
fontWeights: countTokens(typography.fontWeight),
|
|
497
|
-
lineHeights: countTokens(typography.lineHeight),
|
|
498
|
-
spacingValues: countTokens(spacing),
|
|
499
|
-
motionDurations: countTokens(motion.durations),
|
|
500
|
-
motionEasings: countTokens(motion.easings),
|
|
501
|
-
effectsGlass: countTokens(effects.glass),
|
|
502
|
-
effectsAurora: countTokens(effects.aurora),
|
|
503
|
-
effectsPerspective: countTokens(effects.perspective)
|
|
504
|
-
}
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
return generated;
|
|
508
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shapes every reader and writer of the acervo agrees on — one copy each.
|
|
3
|
-
* A second copy of any of these is how two readers start disagreeing about the
|
|
4
|
-
* same file: a use case one of them sees and the other does not, a table row
|
|
5
|
-
* one of them splits differently.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync, readdirSync } from 'fs';
|
|
9
|
-
import { OUTPUT_SCHEMA } from '../../core/paths/output-schema.js';
|
|
10
|
-
|
|
11
|
-
/** A use-case id: `UC-07`. */
|
|
12
|
-
export const UC_ID_RE = /^UC-\d+$/;
|
|
13
|
-
|
|
14
|
-
/** A use case no longer holds as the system's behaviour (or holds only in its `scope`). */
|
|
15
|
-
export const SUPERSEDED = 'superseded';
|
|
16
|
-
|
|
17
|
-
const UC_FILENAME_RE = new RegExp(OUTPUT_SCHEMA.usecases.filenamePattern);
|
|
18
|
-
|
|
19
|
-
/** `UC-07-listar-veterinarios.md` → `UC-07`. Null when the name isn't a use case. */
|
|
20
|
-
export function useCaseId(filename) {
|
|
21
|
-
const m = /^(UC-\d+)-/.exec(filename);
|
|
22
|
-
return m ? m[1] : null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** The name the promotion carries: `UC-{nn}-{slug}.md`, slug in lowercase kebab. */
|
|
26
|
-
export const isCanonicalUseCaseName = (name) => UC_FILENAME_RE.test(name);
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Every use case in `dir`, grouped by id. By default only the canonical names
|
|
30
|
-
* — the ones the promotion ever carries. `canonicalOnly: false` also lists any
|
|
31
|
-
* `UC-{nn}-….md`, for the plan-time check that has always accepted them and
|
|
32
|
-
* now warns instead. An id with more than one file is a broken acervo, and
|
|
33
|
-
* readers must be able to see it rather than silently pick one.
|
|
34
|
-
*
|
|
35
|
-
* @param {string} dir
|
|
36
|
-
* @param {{ canonicalOnly?: boolean }} [options]
|
|
37
|
-
* @returns {Map<string, string[]>} id → filenames, sorted; empty when the folder is missing or unreadable
|
|
38
|
-
*/
|
|
39
|
-
export function useCaseFilesById(dir, { canonicalOnly = true } = {}) {
|
|
40
|
-
const byId = new Map();
|
|
41
|
-
try {
|
|
42
|
-
if (!existsSync(dir)) return byId;
|
|
43
|
-
for (const name of readdirSync(dir).sort()) {
|
|
44
|
-
const id = useCaseId(name);
|
|
45
|
-
if (!id || !name.endsWith('.md')) continue;
|
|
46
|
-
if (canonicalOnly && !isCanonicalUseCaseName(name)) continue;
|
|
47
|
-
if (!byId.has(id)) byId.set(id, []);
|
|
48
|
-
byId.get(id).push(name);
|
|
49
|
-
}
|
|
50
|
-
} catch { /* unreadable — nothing to see */ }
|
|
51
|
-
return byId;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** A markdown table separator row: `|---|:--:|`, with or without the outer pipes. */
|
|
55
|
-
export const SEPARATOR_ROW_RE = /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/;
|
|
56
|
-
|
|
57
|
-
/** A line that belongs to a table: it carries an unescaped pipe (GFM does not require the leading one). */
|
|
58
|
-
export const isTableLine = (line) => /(?<!\\)\|/.test(line);
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Split a table row on unescaped pipes, outer pipes optional; `\|` inside a
|
|
62
|
-
* cell is a literal pipe.
|
|
63
|
-
*
|
|
64
|
-
* @param {string} row
|
|
65
|
-
* @returns {string[]} trimmed cells
|
|
66
|
-
*/
|
|
67
|
-
export function tableCells(row) {
|
|
68
|
-
const inner = row.trim().replace(/^\|/, '').replace(/(?<!\\)\|$/, '');
|
|
69
|
-
return inner.split(/(?<!\\)\|/).map((c) => c.replace(/\\\|/g, '|').trim());
|
|
70
|
-
}
|