@mandujs/core 0.53.3 → 0.54.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/README.md +654 -654
- package/package.json +2 -2
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +120 -120
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/bundler/__tests__/build-runner.ts +113 -113
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +155 -155
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +182 -182
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/overlay.tsx +368 -368
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/client/persistence.ts +335 -335
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/server/source-context.ts +450 -450
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -451
- package/src/diagnose/checks.ts +832 -832
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +93 -93
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/result.ts +60 -60
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/index.ts +3 -3
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/watcher.ts +18 -18
package/src/design/index.ts
CHANGED
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@mandujs/core/design` — DESIGN.md primitives.
|
|
3
|
-
*
|
|
4
|
-
* Public surface for Issue #245 M1 (parser + scaffold + import +
|
|
5
|
-
* validate). Other modules — Guard rule, MCP discovery, token bridge —
|
|
6
|
-
* consume `parseDesignMd` and the `DesignSpec` type.
|
|
7
|
-
*
|
|
8
|
-
* @module core/design
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
parseDesignMd,
|
|
13
|
-
validateDesignSpec,
|
|
14
|
-
humanizeSectionId,
|
|
15
|
-
} from "./parser";
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
EMPTY_DESIGN_MD,
|
|
19
|
-
fetchUpstreamDesignMd,
|
|
20
|
-
AWESOME_DESIGN_MD_RAW_BASE,
|
|
21
|
-
} from "./scaffold";
|
|
22
|
-
|
|
23
|
-
export {
|
|
24
|
-
compileTailwindTheme,
|
|
25
|
-
mergeThemeIntoCss,
|
|
26
|
-
stripMarkeredBlock,
|
|
27
|
-
slugifyTokenName,
|
|
28
|
-
THEME_MARKER_START,
|
|
29
|
-
THEME_MARKER_END,
|
|
30
|
-
type CompiledTheme,
|
|
31
|
-
type CompiledThemeEntry,
|
|
32
|
-
type CompiledThemeWarning,
|
|
33
|
-
type ThemeMergeConflict,
|
|
34
|
-
type ThemeMergeResult,
|
|
35
|
-
} from "./tailwind-theme";
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
extractDesignTokens,
|
|
39
|
-
type ExtractKind,
|
|
40
|
-
type ExtractOptions,
|
|
41
|
-
type ExtractProposal,
|
|
42
|
-
type ExtractResult,
|
|
43
|
-
} from "./extract";
|
|
44
|
-
|
|
45
|
-
export {
|
|
46
|
-
patchDesignMd,
|
|
47
|
-
patchDesignMdBatch,
|
|
48
|
-
type PatchableSection,
|
|
49
|
-
type PatchOperation,
|
|
50
|
-
type PatchResult,
|
|
51
|
-
type PatchBatchResult,
|
|
52
|
-
} from "./patch";
|
|
53
|
-
|
|
54
|
-
export {
|
|
55
|
-
diffDesignSpecs,
|
|
56
|
-
type DesignSpecDiff,
|
|
57
|
-
type DiffEntry,
|
|
58
|
-
} from "./diff";
|
|
59
|
-
|
|
60
|
-
export {
|
|
61
|
-
buildAgentsDesignBlock,
|
|
62
|
-
linkAgentsToDesignMd,
|
|
63
|
-
DESIGN_LINK_MARKER_START,
|
|
64
|
-
DESIGN_LINK_MARKER_END,
|
|
65
|
-
type LinkAgentsOptions,
|
|
66
|
-
type LinkAgentsResult,
|
|
67
|
-
} from "./agents-link";
|
|
68
|
-
|
|
69
|
-
export {
|
|
70
|
-
lintDesignSpec,
|
|
71
|
-
type LintIssue,
|
|
72
|
-
type LintResult,
|
|
73
|
-
type LintSeverity,
|
|
74
|
-
} from "./lint";
|
|
75
|
-
|
|
76
|
-
export type {
|
|
77
|
-
AgentPrompt,
|
|
78
|
-
AgentPromptsSection,
|
|
79
|
-
AnyDesignSection,
|
|
80
|
-
ColorPaletteSection,
|
|
81
|
-
ColorToken,
|
|
82
|
-
ComponentToken,
|
|
83
|
-
ComponentsSection,
|
|
84
|
-
DesignSection,
|
|
85
|
-
DesignSectionId,
|
|
86
|
-
DesignSpec,
|
|
87
|
-
DoDontRule,
|
|
88
|
-
DosDontsSection,
|
|
89
|
-
LayoutSection,
|
|
90
|
-
ResponsiveBreakpoint,
|
|
91
|
-
ResponsiveSection,
|
|
92
|
-
ShadowToken,
|
|
93
|
-
ShadowsSection,
|
|
94
|
-
SpacingToken,
|
|
95
|
-
ThemeSection,
|
|
96
|
-
TypographyToken,
|
|
97
|
-
TypographySection,
|
|
98
|
-
ValidationIssue,
|
|
99
|
-
ValidationResult,
|
|
100
|
-
} from "./types";
|
|
101
|
-
|
|
102
|
-
export { DESIGN_SECTION_IDS } from "./types";
|
|
1
|
+
/**
|
|
2
|
+
* `@mandujs/core/design` — DESIGN.md primitives.
|
|
3
|
+
*
|
|
4
|
+
* Public surface for Issue #245 M1 (parser + scaffold + import +
|
|
5
|
+
* validate). Other modules — Guard rule, MCP discovery, token bridge —
|
|
6
|
+
* consume `parseDesignMd` and the `DesignSpec` type.
|
|
7
|
+
*
|
|
8
|
+
* @module core/design
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
parseDesignMd,
|
|
13
|
+
validateDesignSpec,
|
|
14
|
+
humanizeSectionId,
|
|
15
|
+
} from "./parser";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
EMPTY_DESIGN_MD,
|
|
19
|
+
fetchUpstreamDesignMd,
|
|
20
|
+
AWESOME_DESIGN_MD_RAW_BASE,
|
|
21
|
+
} from "./scaffold";
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
compileTailwindTheme,
|
|
25
|
+
mergeThemeIntoCss,
|
|
26
|
+
stripMarkeredBlock,
|
|
27
|
+
slugifyTokenName,
|
|
28
|
+
THEME_MARKER_START,
|
|
29
|
+
THEME_MARKER_END,
|
|
30
|
+
type CompiledTheme,
|
|
31
|
+
type CompiledThemeEntry,
|
|
32
|
+
type CompiledThemeWarning,
|
|
33
|
+
type ThemeMergeConflict,
|
|
34
|
+
type ThemeMergeResult,
|
|
35
|
+
} from "./tailwind-theme";
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
extractDesignTokens,
|
|
39
|
+
type ExtractKind,
|
|
40
|
+
type ExtractOptions,
|
|
41
|
+
type ExtractProposal,
|
|
42
|
+
type ExtractResult,
|
|
43
|
+
} from "./extract";
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
patchDesignMd,
|
|
47
|
+
patchDesignMdBatch,
|
|
48
|
+
type PatchableSection,
|
|
49
|
+
type PatchOperation,
|
|
50
|
+
type PatchResult,
|
|
51
|
+
type PatchBatchResult,
|
|
52
|
+
} from "./patch";
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
diffDesignSpecs,
|
|
56
|
+
type DesignSpecDiff,
|
|
57
|
+
type DiffEntry,
|
|
58
|
+
} from "./diff";
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
buildAgentsDesignBlock,
|
|
62
|
+
linkAgentsToDesignMd,
|
|
63
|
+
DESIGN_LINK_MARKER_START,
|
|
64
|
+
DESIGN_LINK_MARKER_END,
|
|
65
|
+
type LinkAgentsOptions,
|
|
66
|
+
type LinkAgentsResult,
|
|
67
|
+
} from "./agents-link";
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
lintDesignSpec,
|
|
71
|
+
type LintIssue,
|
|
72
|
+
type LintResult,
|
|
73
|
+
type LintSeverity,
|
|
74
|
+
} from "./lint";
|
|
75
|
+
|
|
76
|
+
export type {
|
|
77
|
+
AgentPrompt,
|
|
78
|
+
AgentPromptsSection,
|
|
79
|
+
AnyDesignSection,
|
|
80
|
+
ColorPaletteSection,
|
|
81
|
+
ColorToken,
|
|
82
|
+
ComponentToken,
|
|
83
|
+
ComponentsSection,
|
|
84
|
+
DesignSection,
|
|
85
|
+
DesignSectionId,
|
|
86
|
+
DesignSpec,
|
|
87
|
+
DoDontRule,
|
|
88
|
+
DosDontsSection,
|
|
89
|
+
LayoutSection,
|
|
90
|
+
ResponsiveBreakpoint,
|
|
91
|
+
ResponsiveSection,
|
|
92
|
+
ShadowToken,
|
|
93
|
+
ShadowsSection,
|
|
94
|
+
SpacingToken,
|
|
95
|
+
ThemeSection,
|
|
96
|
+
TypographyToken,
|
|
97
|
+
TypographySection,
|
|
98
|
+
ValidationIssue,
|
|
99
|
+
ValidationResult,
|
|
100
|
+
} from "./types";
|
|
101
|
+
|
|
102
|
+
export { DESIGN_SECTION_IDS } from "./types";
|
package/src/design/lint.ts
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DESIGN.md self-consistency lint (Issue #245 M5).
|
|
3
|
-
*
|
|
4
|
-
* Different from `validateDesignSpec` (M1), which reports section
|
|
5
|
-
* presence / shape. The linter checks the *content* for issues that
|
|
6
|
-
* compile fine but waste reader time:
|
|
7
|
-
*
|
|
8
|
-
* - color-palette: hex values that aren't 3/6/8 digits, duplicate
|
|
9
|
-
* hex values across distinct names, slug collisions across names
|
|
10
|
-
* - typography: tokens missing both `fontFamily` and `size`,
|
|
11
|
-
* duplicate display-name slugs
|
|
12
|
-
* - layout / shadows: tokens with no value, duplicate slugs
|
|
13
|
-
* - components: duplicate H3 names
|
|
14
|
-
* - dos-donts: rules under §7 with no `do` / `don't` mode marker
|
|
15
|
-
*
|
|
16
|
-
* The linter is conservative — every rule is opt-out via severity
|
|
17
|
-
* filtering at the call site. The tool returns `{ ok, issues[] }`
|
|
18
|
-
* so CLIs can produce exit codes and MCP tools can stream the issue
|
|
19
|
-
* list.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { slugifyTokenName } from "./tailwind-theme";
|
|
23
|
-
import type { DesignSpec } from "./types";
|
|
24
|
-
|
|
25
|
-
export type LintSeverity = "error" | "warning" | "info";
|
|
26
|
-
|
|
27
|
-
export interface LintIssue {
|
|
28
|
-
rule: string;
|
|
29
|
-
section:
|
|
30
|
-
| "color-palette"
|
|
31
|
-
| "typography"
|
|
32
|
-
| "layout"
|
|
33
|
-
| "shadows"
|
|
34
|
-
| "components"
|
|
35
|
-
| "dos-donts"
|
|
36
|
-
| "agent-prompts"
|
|
37
|
-
| "responsive";
|
|
38
|
-
severity: LintSeverity;
|
|
39
|
-
message: string;
|
|
40
|
-
/** Token / row name when the issue is per-row. */
|
|
41
|
-
name?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface LintResult {
|
|
45
|
-
ok: boolean;
|
|
46
|
-
issues: LintIssue[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const HEX_RX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
50
|
-
const COLOR_FN_RX = /^(rgba?|hsla?|oklch|hwb|lab|lch)\(/i;
|
|
51
|
-
|
|
52
|
-
export function lintDesignSpec(spec: DesignSpec): LintResult {
|
|
53
|
-
const issues: LintIssue[] = [];
|
|
54
|
-
|
|
55
|
-
// ─── color-palette ────────────────────────────────────────────────
|
|
56
|
-
const seenColorSlug = new Map<string, string>();
|
|
57
|
-
const seenColorValue = new Map<string, string>();
|
|
58
|
-
for (const token of spec.sections["color-palette"].tokens) {
|
|
59
|
-
if (!token.value) {
|
|
60
|
-
issues.push({
|
|
61
|
-
rule: "color-missing-value",
|
|
62
|
-
section: "color-palette",
|
|
63
|
-
severity: "warning",
|
|
64
|
-
message: `Color "${token.name}" has no parseable value — Tailwind sync will skip it.`,
|
|
65
|
-
name: token.name,
|
|
66
|
-
});
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (!HEX_RX.test(token.value) && !COLOR_FN_RX.test(token.value)) {
|
|
70
|
-
issues.push({
|
|
71
|
-
rule: "color-malformed-value",
|
|
72
|
-
section: "color-palette",
|
|
73
|
-
severity: "error",
|
|
74
|
-
message: `Color "${token.name}" value "${token.value}" is not a recognised hex/rgb/hsl/oklch literal.`,
|
|
75
|
-
name: token.name,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
const sl = slugifyTokenName(token.name);
|
|
79
|
-
const prevByName = seenColorSlug.get(sl);
|
|
80
|
-
if (prevByName) {
|
|
81
|
-
issues.push({
|
|
82
|
-
rule: "color-slug-collision",
|
|
83
|
-
section: "color-palette",
|
|
84
|
-
severity: "warning",
|
|
85
|
-
message: `Color "${token.name}" slugifies to the same id as "${prevByName}" — Tailwind sync keeps only the first.`,
|
|
86
|
-
name: token.name,
|
|
87
|
-
});
|
|
88
|
-
} else {
|
|
89
|
-
seenColorSlug.set(sl, token.name);
|
|
90
|
-
}
|
|
91
|
-
const prevByValue = seenColorValue.get(token.value.toLowerCase());
|
|
92
|
-
if (prevByValue && prevByValue !== token.name) {
|
|
93
|
-
issues.push({
|
|
94
|
-
rule: "color-duplicate-value",
|
|
95
|
-
section: "color-palette",
|
|
96
|
-
severity: "info",
|
|
97
|
-
message: `Color "${token.name}" shares value ${token.value} with "${prevByValue}" — consider a single canonical name.`,
|
|
98
|
-
name: token.name,
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
seenColorValue.set(token.value.toLowerCase(), token.name);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ─── typography ──────────────────────────────────────────────────
|
|
106
|
-
const seenTypoSlug = new Map<string, string>();
|
|
107
|
-
for (const token of spec.sections.typography.tokens) {
|
|
108
|
-
if (!token.fontFamily && !token.size) {
|
|
109
|
-
issues.push({
|
|
110
|
-
rule: "typography-empty-token",
|
|
111
|
-
section: "typography",
|
|
112
|
-
severity: "warning",
|
|
113
|
-
message: `Typography token "${token.name}" has neither fontFamily nor size — nothing to sync.`,
|
|
114
|
-
name: token.name,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
const sl = slugifyTokenName(token.name);
|
|
118
|
-
const prev = seenTypoSlug.get(sl);
|
|
119
|
-
if (prev) {
|
|
120
|
-
issues.push({
|
|
121
|
-
rule: "typography-slug-collision",
|
|
122
|
-
section: "typography",
|
|
123
|
-
severity: "warning",
|
|
124
|
-
message: `Typography "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
125
|
-
name: token.name,
|
|
126
|
-
});
|
|
127
|
-
} else {
|
|
128
|
-
seenTypoSlug.set(sl, token.name);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ─── layout (spacing) ────────────────────────────────────────────
|
|
133
|
-
const seenLayoutSlug = new Map<string, string>();
|
|
134
|
-
for (const token of spec.sections.layout.tokens) {
|
|
135
|
-
if (!token.value) {
|
|
136
|
-
issues.push({
|
|
137
|
-
rule: "spacing-missing-value",
|
|
138
|
-
section: "layout",
|
|
139
|
-
severity: "warning",
|
|
140
|
-
message: `Spacing "${token.name}" has no value.`,
|
|
141
|
-
name: token.name,
|
|
142
|
-
});
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
const sl = slugifyTokenName(token.name);
|
|
146
|
-
const prev = seenLayoutSlug.get(sl);
|
|
147
|
-
if (prev) {
|
|
148
|
-
issues.push({
|
|
149
|
-
rule: "spacing-slug-collision",
|
|
150
|
-
section: "layout",
|
|
151
|
-
severity: "warning",
|
|
152
|
-
message: `Spacing "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
153
|
-
name: token.name,
|
|
154
|
-
});
|
|
155
|
-
} else {
|
|
156
|
-
seenLayoutSlug.set(sl, token.name);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ─── shadows ─────────────────────────────────────────────────────
|
|
161
|
-
const seenShadowSlug = new Map<string, string>();
|
|
162
|
-
for (const token of spec.sections.shadows.tokens) {
|
|
163
|
-
if (!token.value) {
|
|
164
|
-
issues.push({
|
|
165
|
-
rule: "shadow-missing-value",
|
|
166
|
-
section: "shadows",
|
|
167
|
-
severity: "warning",
|
|
168
|
-
message: `Shadow "${token.name}" has no value.`,
|
|
169
|
-
name: token.name,
|
|
170
|
-
});
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
const sl = slugifyTokenName(token.name);
|
|
174
|
-
const prev = seenShadowSlug.get(sl);
|
|
175
|
-
if (prev) {
|
|
176
|
-
issues.push({
|
|
177
|
-
rule: "shadow-slug-collision",
|
|
178
|
-
section: "shadows",
|
|
179
|
-
severity: "warning",
|
|
180
|
-
message: `Shadow "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
181
|
-
name: token.name,
|
|
182
|
-
});
|
|
183
|
-
} else {
|
|
184
|
-
seenShadowSlug.set(sl, token.name);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// ─── components ──────────────────────────────────────────────────
|
|
189
|
-
const seenComponentSlug = new Map<string, string>();
|
|
190
|
-
for (const token of spec.sections.components.tokens) {
|
|
191
|
-
const sl = slugifyTokenName(token.name);
|
|
192
|
-
const prev = seenComponentSlug.get(sl);
|
|
193
|
-
if (prev) {
|
|
194
|
-
issues.push({
|
|
195
|
-
rule: "component-duplicate",
|
|
196
|
-
section: "components",
|
|
197
|
-
severity: "warning",
|
|
198
|
-
message: `Component "${token.name}" appears twice (also as "${prev}").`,
|
|
199
|
-
name: token.name,
|
|
200
|
-
});
|
|
201
|
-
} else {
|
|
202
|
-
seenComponentSlug.set(sl, token.name);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// The most critical level of severity wins for the `ok` flag.
|
|
207
|
-
const ok = !issues.some((i) => i.severity === "error");
|
|
208
|
-
return { ok, issues };
|
|
209
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* DESIGN.md self-consistency lint (Issue #245 M5).
|
|
3
|
+
*
|
|
4
|
+
* Different from `validateDesignSpec` (M1), which reports section
|
|
5
|
+
* presence / shape. The linter checks the *content* for issues that
|
|
6
|
+
* compile fine but waste reader time:
|
|
7
|
+
*
|
|
8
|
+
* - color-palette: hex values that aren't 3/6/8 digits, duplicate
|
|
9
|
+
* hex values across distinct names, slug collisions across names
|
|
10
|
+
* - typography: tokens missing both `fontFamily` and `size`,
|
|
11
|
+
* duplicate display-name slugs
|
|
12
|
+
* - layout / shadows: tokens with no value, duplicate slugs
|
|
13
|
+
* - components: duplicate H3 names
|
|
14
|
+
* - dos-donts: rules under §7 with no `do` / `don't` mode marker
|
|
15
|
+
*
|
|
16
|
+
* The linter is conservative — every rule is opt-out via severity
|
|
17
|
+
* filtering at the call site. The tool returns `{ ok, issues[] }`
|
|
18
|
+
* so CLIs can produce exit codes and MCP tools can stream the issue
|
|
19
|
+
* list.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { slugifyTokenName } from "./tailwind-theme";
|
|
23
|
+
import type { DesignSpec } from "./types";
|
|
24
|
+
|
|
25
|
+
export type LintSeverity = "error" | "warning" | "info";
|
|
26
|
+
|
|
27
|
+
export interface LintIssue {
|
|
28
|
+
rule: string;
|
|
29
|
+
section:
|
|
30
|
+
| "color-palette"
|
|
31
|
+
| "typography"
|
|
32
|
+
| "layout"
|
|
33
|
+
| "shadows"
|
|
34
|
+
| "components"
|
|
35
|
+
| "dos-donts"
|
|
36
|
+
| "agent-prompts"
|
|
37
|
+
| "responsive";
|
|
38
|
+
severity: LintSeverity;
|
|
39
|
+
message: string;
|
|
40
|
+
/** Token / row name when the issue is per-row. */
|
|
41
|
+
name?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface LintResult {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
issues: LintIssue[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const HEX_RX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
50
|
+
const COLOR_FN_RX = /^(rgba?|hsla?|oklch|hwb|lab|lch)\(/i;
|
|
51
|
+
|
|
52
|
+
export function lintDesignSpec(spec: DesignSpec): LintResult {
|
|
53
|
+
const issues: LintIssue[] = [];
|
|
54
|
+
|
|
55
|
+
// ─── color-palette ────────────────────────────────────────────────
|
|
56
|
+
const seenColorSlug = new Map<string, string>();
|
|
57
|
+
const seenColorValue = new Map<string, string>();
|
|
58
|
+
for (const token of spec.sections["color-palette"].tokens) {
|
|
59
|
+
if (!token.value) {
|
|
60
|
+
issues.push({
|
|
61
|
+
rule: "color-missing-value",
|
|
62
|
+
section: "color-palette",
|
|
63
|
+
severity: "warning",
|
|
64
|
+
message: `Color "${token.name}" has no parseable value — Tailwind sync will skip it.`,
|
|
65
|
+
name: token.name,
|
|
66
|
+
});
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!HEX_RX.test(token.value) && !COLOR_FN_RX.test(token.value)) {
|
|
70
|
+
issues.push({
|
|
71
|
+
rule: "color-malformed-value",
|
|
72
|
+
section: "color-palette",
|
|
73
|
+
severity: "error",
|
|
74
|
+
message: `Color "${token.name}" value "${token.value}" is not a recognised hex/rgb/hsl/oklch literal.`,
|
|
75
|
+
name: token.name,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const sl = slugifyTokenName(token.name);
|
|
79
|
+
const prevByName = seenColorSlug.get(sl);
|
|
80
|
+
if (prevByName) {
|
|
81
|
+
issues.push({
|
|
82
|
+
rule: "color-slug-collision",
|
|
83
|
+
section: "color-palette",
|
|
84
|
+
severity: "warning",
|
|
85
|
+
message: `Color "${token.name}" slugifies to the same id as "${prevByName}" — Tailwind sync keeps only the first.`,
|
|
86
|
+
name: token.name,
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
seenColorSlug.set(sl, token.name);
|
|
90
|
+
}
|
|
91
|
+
const prevByValue = seenColorValue.get(token.value.toLowerCase());
|
|
92
|
+
if (prevByValue && prevByValue !== token.name) {
|
|
93
|
+
issues.push({
|
|
94
|
+
rule: "color-duplicate-value",
|
|
95
|
+
section: "color-palette",
|
|
96
|
+
severity: "info",
|
|
97
|
+
message: `Color "${token.name}" shares value ${token.value} with "${prevByValue}" — consider a single canonical name.`,
|
|
98
|
+
name: token.name,
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
seenColorValue.set(token.value.toLowerCase(), token.name);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── typography ──────────────────────────────────────────────────
|
|
106
|
+
const seenTypoSlug = new Map<string, string>();
|
|
107
|
+
for (const token of spec.sections.typography.tokens) {
|
|
108
|
+
if (!token.fontFamily && !token.size) {
|
|
109
|
+
issues.push({
|
|
110
|
+
rule: "typography-empty-token",
|
|
111
|
+
section: "typography",
|
|
112
|
+
severity: "warning",
|
|
113
|
+
message: `Typography token "${token.name}" has neither fontFamily nor size — nothing to sync.`,
|
|
114
|
+
name: token.name,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const sl = slugifyTokenName(token.name);
|
|
118
|
+
const prev = seenTypoSlug.get(sl);
|
|
119
|
+
if (prev) {
|
|
120
|
+
issues.push({
|
|
121
|
+
rule: "typography-slug-collision",
|
|
122
|
+
section: "typography",
|
|
123
|
+
severity: "warning",
|
|
124
|
+
message: `Typography "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
125
|
+
name: token.name,
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
seenTypoSlug.set(sl, token.name);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── layout (spacing) ────────────────────────────────────────────
|
|
133
|
+
const seenLayoutSlug = new Map<string, string>();
|
|
134
|
+
for (const token of spec.sections.layout.tokens) {
|
|
135
|
+
if (!token.value) {
|
|
136
|
+
issues.push({
|
|
137
|
+
rule: "spacing-missing-value",
|
|
138
|
+
section: "layout",
|
|
139
|
+
severity: "warning",
|
|
140
|
+
message: `Spacing "${token.name}" has no value.`,
|
|
141
|
+
name: token.name,
|
|
142
|
+
});
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const sl = slugifyTokenName(token.name);
|
|
146
|
+
const prev = seenLayoutSlug.get(sl);
|
|
147
|
+
if (prev) {
|
|
148
|
+
issues.push({
|
|
149
|
+
rule: "spacing-slug-collision",
|
|
150
|
+
section: "layout",
|
|
151
|
+
severity: "warning",
|
|
152
|
+
message: `Spacing "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
153
|
+
name: token.name,
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
seenLayoutSlug.set(sl, token.name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── shadows ─────────────────────────────────────────────────────
|
|
161
|
+
const seenShadowSlug = new Map<string, string>();
|
|
162
|
+
for (const token of spec.sections.shadows.tokens) {
|
|
163
|
+
if (!token.value) {
|
|
164
|
+
issues.push({
|
|
165
|
+
rule: "shadow-missing-value",
|
|
166
|
+
section: "shadows",
|
|
167
|
+
severity: "warning",
|
|
168
|
+
message: `Shadow "${token.name}" has no value.`,
|
|
169
|
+
name: token.name,
|
|
170
|
+
});
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const sl = slugifyTokenName(token.name);
|
|
174
|
+
const prev = seenShadowSlug.get(sl);
|
|
175
|
+
if (prev) {
|
|
176
|
+
issues.push({
|
|
177
|
+
rule: "shadow-slug-collision",
|
|
178
|
+
section: "shadows",
|
|
179
|
+
severity: "warning",
|
|
180
|
+
message: `Shadow "${token.name}" collides with "${prev}" on slug "${sl}".`,
|
|
181
|
+
name: token.name,
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
seenShadowSlug.set(sl, token.name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─── components ──────────────────────────────────────────────────
|
|
189
|
+
const seenComponentSlug = new Map<string, string>();
|
|
190
|
+
for (const token of spec.sections.components.tokens) {
|
|
191
|
+
const sl = slugifyTokenName(token.name);
|
|
192
|
+
const prev = seenComponentSlug.get(sl);
|
|
193
|
+
if (prev) {
|
|
194
|
+
issues.push({
|
|
195
|
+
rule: "component-duplicate",
|
|
196
|
+
section: "components",
|
|
197
|
+
severity: "warning",
|
|
198
|
+
message: `Component "${token.name}" appears twice (also as "${prev}").`,
|
|
199
|
+
name: token.name,
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
seenComponentSlug.set(sl, token.name);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// The most critical level of severity wins for the `ok` flag.
|
|
207
|
+
const ok = !issues.some((i) => i.severity === "error");
|
|
208
|
+
return { ok, issues };
|
|
209
|
+
}
|