@mandujs/core 0.54.32 → 0.55.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- 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 +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- 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 +14 -2
- 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/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- 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 +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- 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/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- 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 +16 -15
- 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/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- 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__/props-serialization.test.ts +37 -37
- 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 +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- 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-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- 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/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- 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/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- 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 +82 -82
- 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__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- 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/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- 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/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 +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- 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/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/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- 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/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 +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- 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/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- 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/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- 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/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- 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/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- 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/perf/user-marks.ts +1 -1
- 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 +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- 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/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- 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__/devtools-adapter.test.ts +68 -68
- 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__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- 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/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- 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/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- 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/spec/schema.ts +35 -35
- 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/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- 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/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,219 +1,219 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Guard `DESIGN_INLINE_CLASS` rule tests (Issue #245 M2).
|
|
3
|
-
*
|
|
4
|
-
* Cover the three forbid-list sources (explicit / DESIGN.md auto /
|
|
5
|
-
* combined), the exclude paths (canonical component dirs), and the
|
|
6
|
-
* regex matcher's variant-prefix handling.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect } from "bun:test";
|
|
9
|
-
import fs from "node:fs/promises";
|
|
10
|
-
import os from "node:os";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
|
|
13
|
-
import { checkDesignInlineClasses } from "../design-inline-class";
|
|
14
|
-
|
|
15
|
-
async function makeRoot(prefix: string): Promise<string> {
|
|
16
|
-
return fs.mkdtemp(path.join(os.tmpdir(), `mandu-design-guard-${prefix}-`));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function writeFile(root: string, rel: string, content: string): Promise<void> {
|
|
20
|
-
const full = path.join(root, rel);
|
|
21
|
-
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
22
|
-
await fs.writeFile(full, content);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("checkDesignInlineClasses", () => {
|
|
26
|
-
it("returns no violations when guard.design is undefined", async () => {
|
|
27
|
-
const root = await makeRoot("undef");
|
|
28
|
-
await writeFile(
|
|
29
|
-
root,
|
|
30
|
-
"src/client/page.tsx",
|
|
31
|
-
`export default () => <div className="btn-hard">x</div>;\n`,
|
|
32
|
-
);
|
|
33
|
-
const out = await checkDesignInlineClasses(root, undefined);
|
|
34
|
-
expect(out).toHaveLength(0);
|
|
35
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("flags an inline forbidden class with line number", async () => {
|
|
39
|
-
const root = await makeRoot("flag");
|
|
40
|
-
await writeFile(
|
|
41
|
-
root,
|
|
42
|
-
"src/client/page.tsx",
|
|
43
|
-
`import * as React from 'react';
|
|
44
|
-
|
|
45
|
-
export default () => (
|
|
46
|
-
<div className="px-4 py-2 btn-hard rounded-md">x</div>
|
|
47
|
-
);
|
|
48
|
-
`,
|
|
49
|
-
);
|
|
50
|
-
const out = await checkDesignInlineClasses(root, {
|
|
51
|
-
forbidInlineClasses: ["btn-hard"],
|
|
52
|
-
});
|
|
53
|
-
expect(out).toHaveLength(1);
|
|
54
|
-
expect(out[0]?.ruleId).toBe("DESIGN_INLINE_CLASS");
|
|
55
|
-
expect(out[0]?.file.replace(/\\/g, "/")).toBe("src/client/page.tsx");
|
|
56
|
-
expect(out[0]?.line).toBe(4);
|
|
57
|
-
expect(out[0]?.message).toContain("btn-hard");
|
|
58
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("surfaces the replacement component in the message + suggestion", async () => {
|
|
62
|
-
const root = await makeRoot("hint");
|
|
63
|
-
await writeFile(
|
|
64
|
-
root,
|
|
65
|
-
"src/client/page.tsx",
|
|
66
|
-
`<div className="btn-hard">x</div>`,
|
|
67
|
-
);
|
|
68
|
-
const out = await checkDesignInlineClasses(root, {
|
|
69
|
-
forbidInlineClasses: ["btn-hard"],
|
|
70
|
-
requireComponent: { "btn-hard": "@/client/shared/ui#MButton" },
|
|
71
|
-
});
|
|
72
|
-
expect(out[0]?.message).toContain("@/client/shared/ui#MButton");
|
|
73
|
-
expect(out[0]?.suggestion).toContain("@/client/shared/ui#MButton");
|
|
74
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("excludes the canonical component dirs by default", async () => {
|
|
78
|
-
const root = await makeRoot("exclude-default");
|
|
79
|
-
// The component definition itself uses btn-hard — that's where it
|
|
80
|
-
// legitimately lives. Default exclude must skip this file.
|
|
81
|
-
await writeFile(
|
|
82
|
-
root,
|
|
83
|
-
"src/client/shared/ui/m-button.tsx",
|
|
84
|
-
`<button className="btn-hard">x</button>`,
|
|
85
|
-
);
|
|
86
|
-
// A page that imports it must still be flagged.
|
|
87
|
-
await writeFile(
|
|
88
|
-
root,
|
|
89
|
-
"src/client/pages/home.tsx",
|
|
90
|
-
`<div className="btn-hard">x</div>`,
|
|
91
|
-
);
|
|
92
|
-
const out = await checkDesignInlineClasses(root, {
|
|
93
|
-
forbidInlineClasses: ["btn-hard"],
|
|
94
|
-
});
|
|
95
|
-
const files = out.map((v) => v.file.replace(/\\/g, "/"));
|
|
96
|
-
expect(files).toContain("src/client/pages/home.tsx");
|
|
97
|
-
expect(files).not.toContain("src/client/shared/ui/m-button.tsx");
|
|
98
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("strips Tailwind variant prefixes (hover:btn-hard matches btn-hard)", async () => {
|
|
102
|
-
const root = await makeRoot("variant");
|
|
103
|
-
await writeFile(
|
|
104
|
-
root,
|
|
105
|
-
"src/client/page.tsx",
|
|
106
|
-
`<div className="hover:btn-hard focus:btn-hard">x</div>`,
|
|
107
|
-
);
|
|
108
|
-
const out = await checkDesignInlineClasses(root, {
|
|
109
|
-
forbidInlineClasses: ["btn-hard"],
|
|
110
|
-
});
|
|
111
|
-
expect(out.length).toBeGreaterThanOrEqual(1);
|
|
112
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("auto-extracts forbid tokens from DESIGN.md §7 Don't section", async () => {
|
|
116
|
-
const root = await makeRoot("auto-design");
|
|
117
|
-
await writeFile(
|
|
118
|
-
root,
|
|
119
|
-
"DESIGN.md",
|
|
120
|
-
`# X
|
|
121
|
-
|
|
122
|
-
## Do's & Don'ts
|
|
123
|
-
|
|
124
|
-
### Do
|
|
125
|
-
- Use the \`MButton\` component.
|
|
126
|
-
|
|
127
|
-
### Don't
|
|
128
|
-
- Inline \`btn-hard\` directly in pages.
|
|
129
|
-
- Don't use \`shadow-hard\` outside ui/.
|
|
130
|
-
`,
|
|
131
|
-
);
|
|
132
|
-
await writeFile(
|
|
133
|
-
root,
|
|
134
|
-
"src/client/page.tsx",
|
|
135
|
-
`<div className="btn-hard shadow-hard">x</div>`,
|
|
136
|
-
);
|
|
137
|
-
const out = await checkDesignInlineClasses(root, {
|
|
138
|
-
autoFromDesignMd: true,
|
|
139
|
-
});
|
|
140
|
-
const tokens = out.map((v) => v.message.match(/"([\w-]+)"/)?.[1]);
|
|
141
|
-
expect(tokens).toContain("btn-hard");
|
|
142
|
-
expect(tokens).toContain("shadow-hard");
|
|
143
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("merges explicit forbid + DESIGN.md auto", async () => {
|
|
147
|
-
const root = await makeRoot("merge");
|
|
148
|
-
await writeFile(
|
|
149
|
-
root,
|
|
150
|
-
"DESIGN.md",
|
|
151
|
-
`## Do's & Don'ts
|
|
152
|
-
|
|
153
|
-
### Don't
|
|
154
|
-
- Avoid \`shadow-hard\`.
|
|
155
|
-
`,
|
|
156
|
-
);
|
|
157
|
-
await writeFile(
|
|
158
|
-
root,
|
|
159
|
-
"src/client/page.tsx",
|
|
160
|
-
`<div className="btn-hard shadow-hard">x</div>`,
|
|
161
|
-
);
|
|
162
|
-
const out = await checkDesignInlineClasses(root, {
|
|
163
|
-
forbidInlineClasses: ["btn-hard"],
|
|
164
|
-
autoFromDesignMd: true,
|
|
165
|
-
});
|
|
166
|
-
const tokens = out.map((v) => v.message.match(/"([\w-]+)"/)?.[1]);
|
|
167
|
-
expect(tokens).toContain("btn-hard");
|
|
168
|
-
expect(tokens).toContain("shadow-hard");
|
|
169
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("scans both src/ and app/ when present", async () => {
|
|
173
|
-
const root = await makeRoot("dual-roots");
|
|
174
|
-
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
175
|
-
await writeFile(root, "app/y.tsx", `<div className="btn-hard">y</div>`);
|
|
176
|
-
const out = await checkDesignInlineClasses(root, {
|
|
177
|
-
forbidInlineClasses: ["btn-hard"],
|
|
178
|
-
});
|
|
179
|
-
const files = out.map((v) => v.file.replace(/\\/g, "/")).sort();
|
|
180
|
-
expect(files).toEqual(["app/y.tsx", "src/x.tsx"]);
|
|
181
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("respects custom exclude patterns", async () => {
|
|
185
|
-
const root = await makeRoot("custom-exclude");
|
|
186
|
-
await writeFile(root, "src/legacy/old-page.tsx", `<div className="btn-hard">x</div>`);
|
|
187
|
-
const out = await checkDesignInlineClasses(root, {
|
|
188
|
-
forbidInlineClasses: ["btn-hard"],
|
|
189
|
-
exclude: ["src/legacy/**"],
|
|
190
|
-
});
|
|
191
|
-
expect(out).toHaveLength(0);
|
|
192
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("honours severity setting on emitted violations", async () => {
|
|
196
|
-
const root = await makeRoot("severity");
|
|
197
|
-
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
198
|
-
const errors = await checkDesignInlineClasses(root, {
|
|
199
|
-
forbidInlineClasses: ["btn-hard"],
|
|
200
|
-
severity: "error",
|
|
201
|
-
});
|
|
202
|
-
expect(errors[0]?.severity).toBe("error");
|
|
203
|
-
const warnings = await checkDesignInlineClasses(root, {
|
|
204
|
-
forbidInlineClasses: ["btn-hard"],
|
|
205
|
-
severity: "warning",
|
|
206
|
-
});
|
|
207
|
-
expect(warnings[0]?.severity).toBe("warning");
|
|
208
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("returns nothing when forbid list resolves to empty", async () => {
|
|
212
|
-
const root = await makeRoot("empty-forbid");
|
|
213
|
-
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
214
|
-
// No explicit list, autoFromDesignMd false → nothing to enforce.
|
|
215
|
-
const out = await checkDesignInlineClasses(root, {});
|
|
216
|
-
expect(out).toHaveLength(0);
|
|
217
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
218
|
-
});
|
|
219
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Guard `DESIGN_INLINE_CLASS` rule tests (Issue #245 M2).
|
|
3
|
+
*
|
|
4
|
+
* Cover the three forbid-list sources (explicit / DESIGN.md auto /
|
|
5
|
+
* combined), the exclude paths (canonical component dirs), and the
|
|
6
|
+
* regex matcher's variant-prefix handling.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from "bun:test";
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
import { checkDesignInlineClasses } from "../design-inline-class";
|
|
14
|
+
|
|
15
|
+
async function makeRoot(prefix: string): Promise<string> {
|
|
16
|
+
return fs.mkdtemp(path.join(os.tmpdir(), `mandu-design-guard-${prefix}-`));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function writeFile(root: string, rel: string, content: string): Promise<void> {
|
|
20
|
+
const full = path.join(root, rel);
|
|
21
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
22
|
+
await fs.writeFile(full, content);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("checkDesignInlineClasses", () => {
|
|
26
|
+
it("returns no violations when guard.design is undefined", async () => {
|
|
27
|
+
const root = await makeRoot("undef");
|
|
28
|
+
await writeFile(
|
|
29
|
+
root,
|
|
30
|
+
"src/client/page.tsx",
|
|
31
|
+
`export default () => <div className="btn-hard">x</div>;\n`,
|
|
32
|
+
);
|
|
33
|
+
const out = await checkDesignInlineClasses(root, undefined);
|
|
34
|
+
expect(out).toHaveLength(0);
|
|
35
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("flags an inline forbidden class with line number", async () => {
|
|
39
|
+
const root = await makeRoot("flag");
|
|
40
|
+
await writeFile(
|
|
41
|
+
root,
|
|
42
|
+
"src/client/page.tsx",
|
|
43
|
+
`import * as React from 'react';
|
|
44
|
+
|
|
45
|
+
export default () => (
|
|
46
|
+
<div className="px-4 py-2 btn-hard rounded-md">x</div>
|
|
47
|
+
);
|
|
48
|
+
`,
|
|
49
|
+
);
|
|
50
|
+
const out = await checkDesignInlineClasses(root, {
|
|
51
|
+
forbidInlineClasses: ["btn-hard"],
|
|
52
|
+
});
|
|
53
|
+
expect(out).toHaveLength(1);
|
|
54
|
+
expect(out[0]?.ruleId).toBe("DESIGN_INLINE_CLASS");
|
|
55
|
+
expect(out[0]?.file.replace(/\\/g, "/")).toBe("src/client/page.tsx");
|
|
56
|
+
expect(out[0]?.line).toBe(4);
|
|
57
|
+
expect(out[0]?.message).toContain("btn-hard");
|
|
58
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("surfaces the replacement component in the message + suggestion", async () => {
|
|
62
|
+
const root = await makeRoot("hint");
|
|
63
|
+
await writeFile(
|
|
64
|
+
root,
|
|
65
|
+
"src/client/page.tsx",
|
|
66
|
+
`<div className="btn-hard">x</div>`,
|
|
67
|
+
);
|
|
68
|
+
const out = await checkDesignInlineClasses(root, {
|
|
69
|
+
forbidInlineClasses: ["btn-hard"],
|
|
70
|
+
requireComponent: { "btn-hard": "@/client/shared/ui#MButton" },
|
|
71
|
+
});
|
|
72
|
+
expect(out[0]?.message).toContain("@/client/shared/ui#MButton");
|
|
73
|
+
expect(out[0]?.suggestion).toContain("@/client/shared/ui#MButton");
|
|
74
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("excludes the canonical component dirs by default", async () => {
|
|
78
|
+
const root = await makeRoot("exclude-default");
|
|
79
|
+
// The component definition itself uses btn-hard — that's where it
|
|
80
|
+
// legitimately lives. Default exclude must skip this file.
|
|
81
|
+
await writeFile(
|
|
82
|
+
root,
|
|
83
|
+
"src/client/shared/ui/m-button.tsx",
|
|
84
|
+
`<button className="btn-hard">x</button>`,
|
|
85
|
+
);
|
|
86
|
+
// A page that imports it must still be flagged.
|
|
87
|
+
await writeFile(
|
|
88
|
+
root,
|
|
89
|
+
"src/client/pages/home.tsx",
|
|
90
|
+
`<div className="btn-hard">x</div>`,
|
|
91
|
+
);
|
|
92
|
+
const out = await checkDesignInlineClasses(root, {
|
|
93
|
+
forbidInlineClasses: ["btn-hard"],
|
|
94
|
+
});
|
|
95
|
+
const files = out.map((v) => v.file.replace(/\\/g, "/"));
|
|
96
|
+
expect(files).toContain("src/client/pages/home.tsx");
|
|
97
|
+
expect(files).not.toContain("src/client/shared/ui/m-button.tsx");
|
|
98
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("strips Tailwind variant prefixes (hover:btn-hard matches btn-hard)", async () => {
|
|
102
|
+
const root = await makeRoot("variant");
|
|
103
|
+
await writeFile(
|
|
104
|
+
root,
|
|
105
|
+
"src/client/page.tsx",
|
|
106
|
+
`<div className="hover:btn-hard focus:btn-hard">x</div>`,
|
|
107
|
+
);
|
|
108
|
+
const out = await checkDesignInlineClasses(root, {
|
|
109
|
+
forbidInlineClasses: ["btn-hard"],
|
|
110
|
+
});
|
|
111
|
+
expect(out.length).toBeGreaterThanOrEqual(1);
|
|
112
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("auto-extracts forbid tokens from DESIGN.md §7 Don't section", async () => {
|
|
116
|
+
const root = await makeRoot("auto-design");
|
|
117
|
+
await writeFile(
|
|
118
|
+
root,
|
|
119
|
+
"DESIGN.md",
|
|
120
|
+
`# X
|
|
121
|
+
|
|
122
|
+
## Do's & Don'ts
|
|
123
|
+
|
|
124
|
+
### Do
|
|
125
|
+
- Use the \`MButton\` component.
|
|
126
|
+
|
|
127
|
+
### Don't
|
|
128
|
+
- Inline \`btn-hard\` directly in pages.
|
|
129
|
+
- Don't use \`shadow-hard\` outside ui/.
|
|
130
|
+
`,
|
|
131
|
+
);
|
|
132
|
+
await writeFile(
|
|
133
|
+
root,
|
|
134
|
+
"src/client/page.tsx",
|
|
135
|
+
`<div className="btn-hard shadow-hard">x</div>`,
|
|
136
|
+
);
|
|
137
|
+
const out = await checkDesignInlineClasses(root, {
|
|
138
|
+
autoFromDesignMd: true,
|
|
139
|
+
});
|
|
140
|
+
const tokens = out.map((v) => v.message.match(/"([\w-]+)"/)?.[1]);
|
|
141
|
+
expect(tokens).toContain("btn-hard");
|
|
142
|
+
expect(tokens).toContain("shadow-hard");
|
|
143
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("merges explicit forbid + DESIGN.md auto", async () => {
|
|
147
|
+
const root = await makeRoot("merge");
|
|
148
|
+
await writeFile(
|
|
149
|
+
root,
|
|
150
|
+
"DESIGN.md",
|
|
151
|
+
`## Do's & Don'ts
|
|
152
|
+
|
|
153
|
+
### Don't
|
|
154
|
+
- Avoid \`shadow-hard\`.
|
|
155
|
+
`,
|
|
156
|
+
);
|
|
157
|
+
await writeFile(
|
|
158
|
+
root,
|
|
159
|
+
"src/client/page.tsx",
|
|
160
|
+
`<div className="btn-hard shadow-hard">x</div>`,
|
|
161
|
+
);
|
|
162
|
+
const out = await checkDesignInlineClasses(root, {
|
|
163
|
+
forbidInlineClasses: ["btn-hard"],
|
|
164
|
+
autoFromDesignMd: true,
|
|
165
|
+
});
|
|
166
|
+
const tokens = out.map((v) => v.message.match(/"([\w-]+)"/)?.[1]);
|
|
167
|
+
expect(tokens).toContain("btn-hard");
|
|
168
|
+
expect(tokens).toContain("shadow-hard");
|
|
169
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("scans both src/ and app/ when present", async () => {
|
|
173
|
+
const root = await makeRoot("dual-roots");
|
|
174
|
+
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
175
|
+
await writeFile(root, "app/y.tsx", `<div className="btn-hard">y</div>`);
|
|
176
|
+
const out = await checkDesignInlineClasses(root, {
|
|
177
|
+
forbidInlineClasses: ["btn-hard"],
|
|
178
|
+
});
|
|
179
|
+
const files = out.map((v) => v.file.replace(/\\/g, "/")).sort();
|
|
180
|
+
expect(files).toEqual(["app/y.tsx", "src/x.tsx"]);
|
|
181
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("respects custom exclude patterns", async () => {
|
|
185
|
+
const root = await makeRoot("custom-exclude");
|
|
186
|
+
await writeFile(root, "src/legacy/old-page.tsx", `<div className="btn-hard">x</div>`);
|
|
187
|
+
const out = await checkDesignInlineClasses(root, {
|
|
188
|
+
forbidInlineClasses: ["btn-hard"],
|
|
189
|
+
exclude: ["src/legacy/**"],
|
|
190
|
+
});
|
|
191
|
+
expect(out).toHaveLength(0);
|
|
192
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("honours severity setting on emitted violations", async () => {
|
|
196
|
+
const root = await makeRoot("severity");
|
|
197
|
+
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
198
|
+
const errors = await checkDesignInlineClasses(root, {
|
|
199
|
+
forbidInlineClasses: ["btn-hard"],
|
|
200
|
+
severity: "error",
|
|
201
|
+
});
|
|
202
|
+
expect(errors[0]?.severity).toBe("error");
|
|
203
|
+
const warnings = await checkDesignInlineClasses(root, {
|
|
204
|
+
forbidInlineClasses: ["btn-hard"],
|
|
205
|
+
severity: "warning",
|
|
206
|
+
});
|
|
207
|
+
expect(warnings[0]?.severity).toBe("warning");
|
|
208
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("returns nothing when forbid list resolves to empty", async () => {
|
|
212
|
+
const root = await makeRoot("empty-forbid");
|
|
213
|
+
await writeFile(root, "src/x.tsx", `<div className="btn-hard">x</div>`);
|
|
214
|
+
// No explicit list, autoFromDesignMd false → nothing to enforce.
|
|
215
|
+
const out = await checkDesignInlineClasses(root, {});
|
|
216
|
+
expect(out).toHaveLength(0);
|
|
217
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
218
|
+
});
|
|
219
|
+
});
|