@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,347 +1,347 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Guard — oxlint type-aware bridge tests (Follow-up E).
|
|
3
|
-
*
|
|
4
|
-
* Coverage:
|
|
5
|
-
* 1. extractRuleId — normalizes oxlint's `plugin(rule)` shape.
|
|
6
|
-
* 2. mapOxlintSeverity — oxlint severity vocabulary → Mandu `Severity`.
|
|
7
|
-
* 3. translateDiagnostic — shape + filename resolution.
|
|
8
|
-
* 4. resolveOxlintBinary — returns undefined for missing binaries.
|
|
9
|
-
* 5. runTsgolint — skips gracefully when binary missing.
|
|
10
|
-
* 6. runTsgolint — severity=off short-circuit.
|
|
11
|
-
* 7. runTsgolint — real oxlint invocation (when available) on a
|
|
12
|
-
* fixture with `const x: any = 1` + JSON round-trip.
|
|
13
|
-
*
|
|
14
|
-
* Fixtures are isolated under `os.tmpdir()` and scrubbed in afterEach.
|
|
15
|
-
* Tests that need a real oxlint binary probe `findRepoOxlint()` and
|
|
16
|
-
* skip themselves with an in-place assertion when the binary isn't
|
|
17
|
-
* available — keeping the suite green on contributors' machines where
|
|
18
|
-
* oxlint may not be installed yet.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
22
|
-
import fs from "fs/promises";
|
|
23
|
-
import path from "path";
|
|
24
|
-
import os from "os";
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
extractRuleId,
|
|
28
|
-
mapOxlintSeverity,
|
|
29
|
-
translateDiagnostic,
|
|
30
|
-
resolveOxlintBinary,
|
|
31
|
-
runTsgolint,
|
|
32
|
-
} from "../tsgolint-bridge";
|
|
33
|
-
|
|
34
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
35
|
-
// Helpers
|
|
36
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
async function mkFixtureRoot(): Promise<string> {
|
|
39
|
-
return fs.mkdtemp(path.join(os.tmpdir(), "mandu-tsgolint-"));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function writeFile(root: string, rel: string, content: string): Promise<void> {
|
|
43
|
-
const abs = path.join(root, rel);
|
|
44
|
-
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
45
|
-
await fs.writeFile(abs, content, "utf-8");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Locate the repo root so real-binary tests can point the bridge at
|
|
50
|
-
* the already-installed `node_modules/.bin/oxlint` without copying
|
|
51
|
-
* shim files (Bun's bin shims embed lockfile metadata and can't
|
|
52
|
-
* survive a plain `copyFile` onto a test fixture).
|
|
53
|
-
*
|
|
54
|
-
* When the walk-up fails (detached tree / fresh clone without install)
|
|
55
|
-
* the caller gracefully asserts the `oxlint-not-installed` skip path
|
|
56
|
-
* instead, keeping the suite green.
|
|
57
|
-
*/
|
|
58
|
-
async function findRepoRoot(): Promise<string | undefined> {
|
|
59
|
-
let dir = __dirname;
|
|
60
|
-
for (let i = 0; i < 10; i++) {
|
|
61
|
-
const bin = path.join(
|
|
62
|
-
dir,
|
|
63
|
-
"node_modules",
|
|
64
|
-
".bin",
|
|
65
|
-
process.platform === "win32" ? "oxlint.exe" : "oxlint",
|
|
66
|
-
);
|
|
67
|
-
try {
|
|
68
|
-
await fs.access(bin);
|
|
69
|
-
return dir;
|
|
70
|
-
} catch {
|
|
71
|
-
/* keep walking */
|
|
72
|
-
}
|
|
73
|
-
const parent = path.dirname(dir);
|
|
74
|
-
if (parent === dir) break;
|
|
75
|
-
dir = parent;
|
|
76
|
-
}
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
81
|
-
// Pure helpers
|
|
82
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
83
|
-
|
|
84
|
-
describe("extractRuleId", () => {
|
|
85
|
-
it("normalizes typescript-eslint plugin shape to `typescript/<rule>`", () => {
|
|
86
|
-
expect(extractRuleId("typescript-eslint(no-floating-promises)")).toBe(
|
|
87
|
-
"typescript/no-floating-promises",
|
|
88
|
-
);
|
|
89
|
-
expect(extractRuleId("typescript-eslint(no-explicit-any)")).toBe(
|
|
90
|
-
"typescript/no-explicit-any",
|
|
91
|
-
);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("returns bare rule name for eslint / unprefixed codes", () => {
|
|
95
|
-
expect(extractRuleId("no-debugger")).toBe("no-debugger");
|
|
96
|
-
expect(extractRuleId("eslint(no-debugger)")).toBe("no-debugger");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("preserves other plugin prefixes verbatim", () => {
|
|
100
|
-
expect(extractRuleId("unicorn(no-array-sort)")).toBe("unicorn/no-array-sort");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("returns 'unknown' for empty or undefined codes", () => {
|
|
104
|
-
expect(extractRuleId(undefined)).toBe("unknown");
|
|
105
|
-
expect(extractRuleId("")).toBe("unknown");
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("mapOxlintSeverity", () => {
|
|
110
|
-
it("maps oxlint severity strings to Mandu severities", () => {
|
|
111
|
-
expect(mapOxlintSeverity("error")).toBe("error");
|
|
112
|
-
expect(mapOxlintSeverity("warning")).toBe("warn");
|
|
113
|
-
expect(mapOxlintSeverity("advice")).toBe("info");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("defaults unknown / undefined severity to 'warn'", () => {
|
|
117
|
-
expect(mapOxlintSeverity(undefined)).toBe("warn");
|
|
118
|
-
expect(mapOxlintSeverity("unexpected")).toBe("warn");
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("translateDiagnostic", () => {
|
|
123
|
-
it("maps filename / line / column / rule / severity correctly", () => {
|
|
124
|
-
const v = translateDiagnostic(
|
|
125
|
-
{
|
|
126
|
-
message: "Unexpected any.",
|
|
127
|
-
code: "typescript-eslint(no-explicit-any)",
|
|
128
|
-
severity: "error",
|
|
129
|
-
filename: "src/foo.ts",
|
|
130
|
-
labels: [{ span: { offset: 9, length: 3, line: 2, column: 10 } }],
|
|
131
|
-
help: "Use unknown instead.",
|
|
132
|
-
url: "https://oxc.rs/...",
|
|
133
|
-
},
|
|
134
|
-
"/root",
|
|
135
|
-
);
|
|
136
|
-
expect(v.ruleName).toBe("typescript/no-explicit-any");
|
|
137
|
-
expect(v.ruleDescription).toBe("Unexpected any.");
|
|
138
|
-
expect(v.severity).toBe("error");
|
|
139
|
-
expect(v.line).toBe(2);
|
|
140
|
-
expect(v.column).toBe(10);
|
|
141
|
-
expect(v.filePath).toBe(path.join("/root", "src/foo.ts"));
|
|
142
|
-
expect(v.suggestions).toContain("Use unknown instead.");
|
|
143
|
-
expect(v.suggestions.some((s) => s.includes("https://oxc.rs"))).toBe(true);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("falls back to line 1 column 1 when labels are missing", () => {
|
|
147
|
-
const v = translateDiagnostic(
|
|
148
|
-
{
|
|
149
|
-
message: "X",
|
|
150
|
-
code: "typescript-eslint(y)",
|
|
151
|
-
severity: "warning",
|
|
152
|
-
filename: "src/y.ts",
|
|
153
|
-
},
|
|
154
|
-
"/root",
|
|
155
|
-
);
|
|
156
|
-
expect(v.line).toBe(1);
|
|
157
|
-
expect(v.column).toBe(1);
|
|
158
|
-
expect(v.severity).toBe("warn");
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("applies severity override when provided", () => {
|
|
162
|
-
const v = translateDiagnostic(
|
|
163
|
-
{
|
|
164
|
-
message: "X",
|
|
165
|
-
code: "typescript-eslint(y)",
|
|
166
|
-
severity: "warning",
|
|
167
|
-
filename: "src/y.ts",
|
|
168
|
-
},
|
|
169
|
-
"/root",
|
|
170
|
-
"error",
|
|
171
|
-
);
|
|
172
|
-
expect(v.severity).toBe("error");
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
177
|
-
// resolveOxlintBinary
|
|
178
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
179
|
-
|
|
180
|
-
describe("resolveOxlintBinary", () => {
|
|
181
|
-
let root: string;
|
|
182
|
-
|
|
183
|
-
beforeEach(async () => {
|
|
184
|
-
root = await mkFixtureRoot();
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
afterEach(async () => {
|
|
188
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("returns undefined when node_modules/.bin/oxlint is absent", async () => {
|
|
192
|
-
const found = await resolveOxlintBinary(root);
|
|
193
|
-
expect(found).toBeUndefined();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it("returns the resolved path when the binary exists", async () => {
|
|
197
|
-
const binDir = path.join(root, "node_modules", ".bin");
|
|
198
|
-
await fs.mkdir(binDir, { recursive: true });
|
|
199
|
-
const binName = process.platform === "win32" ? "oxlint.exe" : "oxlint";
|
|
200
|
-
await fs.writeFile(path.join(binDir, binName), "");
|
|
201
|
-
const found = await resolveOxlintBinary(root);
|
|
202
|
-
expect(found).toBe(path.join(binDir, binName));
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
207
|
-
// runTsgolint — integration
|
|
208
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
209
|
-
|
|
210
|
-
describe("runTsgolint", () => {
|
|
211
|
-
let root: string;
|
|
212
|
-
|
|
213
|
-
beforeEach(async () => {
|
|
214
|
-
root = await mkFixtureRoot();
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
afterEach(async () => {
|
|
218
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("skips gracefully when oxlint binary is missing (no throw)", async () => {
|
|
222
|
-
const result = await runTsgolint({ projectRoot: root });
|
|
223
|
-
expect(result.skipped).toBe("oxlint-not-installed");
|
|
224
|
-
expect(result.violations).toEqual([]);
|
|
225
|
-
expect(result.summary.elapsedMs).toBe(0);
|
|
226
|
-
expect(result.summary.filesAnalyzed).toBe(0);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("short-circuits when severity is 'off' without touching the filesystem", async () => {
|
|
230
|
-
const result = await runTsgolint({ projectRoot: root, severity: "off" });
|
|
231
|
-
expect(result.skipped).toBe("severity-off");
|
|
232
|
-
expect(result.violations).toEqual([]);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("runs oxlint on a real fixture and round-trips an explicit-any violation", async () => {
|
|
236
|
-
const repoRoot = await findRepoRoot();
|
|
237
|
-
if (!repoRoot) {
|
|
238
|
-
// Environment without an installed oxlint — assert the skip
|
|
239
|
-
// reason as a sanity check and bail early. Keeps the suite
|
|
240
|
-
// passing on fresh clones.
|
|
241
|
-
const result = await runTsgolint({ projectRoot: root });
|
|
242
|
-
expect(result.skipped).toBe("oxlint-not-installed");
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// We anchor `projectRoot` at the repo root so `resolveOxlintBinary()`
|
|
247
|
-
// finds the lockfile-aware bin shim. The fixture lives in an
|
|
248
|
-
// isolated subdir at `<repoRoot>/.tmp-tsgolint-*` and we pass only
|
|
249
|
-
// that file path as `paths`, so oxlint lints just our fixture.
|
|
250
|
-
const fixtureSubdir = `.tmp-tsgolint-fixture-${process.pid}-${Date.now()}`;
|
|
251
|
-
const fixtureDir = path.join(repoRoot, fixtureSubdir);
|
|
252
|
-
await fs.mkdir(fixtureDir, { recursive: true });
|
|
253
|
-
|
|
254
|
-
try {
|
|
255
|
-
await writeFile(
|
|
256
|
-
fixtureDir,
|
|
257
|
-
".oxlintrc.json",
|
|
258
|
-
JSON.stringify({
|
|
259
|
-
rules: { "typescript/no-explicit-any": "error" },
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
await writeFile(
|
|
263
|
-
fixtureDir,
|
|
264
|
-
"bad.ts",
|
|
265
|
-
"const x: any = 1;\nexport { x };\n",
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
// Anchor projectRoot at repoRoot so the bin-shim resolves;
|
|
269
|
-
// point paths at the fixture file (absolute path so oxlint
|
|
270
|
-
// honors it regardless of cwd).
|
|
271
|
-
const result = await runTsgolint({
|
|
272
|
-
projectRoot: repoRoot,
|
|
273
|
-
configPath: path.join(fixtureDir, ".oxlintrc.json"),
|
|
274
|
-
paths: [path.join(fixtureDir, "bad.ts")],
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(result.skipped).toBeUndefined();
|
|
278
|
-
expect(result.violations.length).toBeGreaterThanOrEqual(1);
|
|
279
|
-
|
|
280
|
-
const hit = result.violations.find(
|
|
281
|
-
(v) => v.ruleName === "typescript/no-explicit-any",
|
|
282
|
-
);
|
|
283
|
-
expect(hit).toBeDefined();
|
|
284
|
-
expect(hit?.severity).toBe("error");
|
|
285
|
-
expect(hit?.filePath.endsWith("bad.ts")).toBe(true);
|
|
286
|
-
expect(hit?.line).toBeGreaterThan(0);
|
|
287
|
-
|
|
288
|
-
expect(result.summary.filesAnalyzed).toBeGreaterThanOrEqual(1);
|
|
289
|
-
expect(result.summary.rulesEnabled).toContain(
|
|
290
|
-
"typescript/no-explicit-any",
|
|
291
|
-
);
|
|
292
|
-
expect(result.summary.elapsedMs).toBeGreaterThan(0);
|
|
293
|
-
} finally {
|
|
294
|
-
await fs.rm(fixtureDir, { recursive: true, force: true });
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it("filters diagnostics by the `rules` allowlist when provided", async () => {
|
|
299
|
-
const repoRoot = await findRepoRoot();
|
|
300
|
-
if (!repoRoot) {
|
|
301
|
-
// Skip — same graceful-degradation pattern as above.
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const fixtureSubdir = `.tmp-tsgolint-filter-${process.pid}-${Date.now()}`;
|
|
306
|
-
const fixtureDir = path.join(repoRoot, fixtureSubdir);
|
|
307
|
-
await fs.mkdir(fixtureDir, { recursive: true });
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
await writeFile(
|
|
311
|
-
fixtureDir,
|
|
312
|
-
".oxlintrc.json",
|
|
313
|
-
JSON.stringify({
|
|
314
|
-
rules: {
|
|
315
|
-
"typescript/no-explicit-any": "error",
|
|
316
|
-
"no-debugger": "error",
|
|
317
|
-
},
|
|
318
|
-
}),
|
|
319
|
-
);
|
|
320
|
-
await writeFile(
|
|
321
|
-
fixtureDir,
|
|
322
|
-
"mixed.ts",
|
|
323
|
-
"const x: any = 1;\ndebugger;\nexport { x };\n",
|
|
324
|
-
);
|
|
325
|
-
|
|
326
|
-
const result = await runTsgolint({
|
|
327
|
-
projectRoot: repoRoot,
|
|
328
|
-
configPath: path.join(fixtureDir, ".oxlintrc.json"),
|
|
329
|
-
paths: [path.join(fixtureDir, "mixed.ts")],
|
|
330
|
-
rules: ["typescript/no-explicit-any"],
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// Only typescript/no-explicit-any should survive the post-filter;
|
|
334
|
-
// `no-debugger` hits oxlint but the bridge drops them.
|
|
335
|
-
for (const v of result.violations) {
|
|
336
|
-
expect(v.ruleName).toBe("typescript/no-explicit-any");
|
|
337
|
-
}
|
|
338
|
-
// diagnosticsReceived counts pre-filter and should exceed
|
|
339
|
-
// post-filter violation count when both rules fired.
|
|
340
|
-
expect(result.summary.diagnosticsReceived).toBeGreaterThanOrEqual(
|
|
341
|
-
result.violations.length,
|
|
342
|
-
);
|
|
343
|
-
} finally {
|
|
344
|
-
await fs.rm(fixtureDir, { recursive: true, force: true });
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Guard — oxlint type-aware bridge tests (Follow-up E).
|
|
3
|
+
*
|
|
4
|
+
* Coverage:
|
|
5
|
+
* 1. extractRuleId — normalizes oxlint's `plugin(rule)` shape.
|
|
6
|
+
* 2. mapOxlintSeverity — oxlint severity vocabulary → Mandu `Severity`.
|
|
7
|
+
* 3. translateDiagnostic — shape + filename resolution.
|
|
8
|
+
* 4. resolveOxlintBinary — returns undefined for missing binaries.
|
|
9
|
+
* 5. runTsgolint — skips gracefully when binary missing.
|
|
10
|
+
* 6. runTsgolint — severity=off short-circuit.
|
|
11
|
+
* 7. runTsgolint — real oxlint invocation (when available) on a
|
|
12
|
+
* fixture with `const x: any = 1` + JSON round-trip.
|
|
13
|
+
*
|
|
14
|
+
* Fixtures are isolated under `os.tmpdir()` and scrubbed in afterEach.
|
|
15
|
+
* Tests that need a real oxlint binary probe `findRepoOxlint()` and
|
|
16
|
+
* skip themselves with an in-place assertion when the binary isn't
|
|
17
|
+
* available — keeping the suite green on contributors' machines where
|
|
18
|
+
* oxlint may not be installed yet.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
22
|
+
import fs from "fs/promises";
|
|
23
|
+
import path from "path";
|
|
24
|
+
import os from "os";
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
extractRuleId,
|
|
28
|
+
mapOxlintSeverity,
|
|
29
|
+
translateDiagnostic,
|
|
30
|
+
resolveOxlintBinary,
|
|
31
|
+
runTsgolint,
|
|
32
|
+
} from "../tsgolint-bridge";
|
|
33
|
+
|
|
34
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Helpers
|
|
36
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
async function mkFixtureRoot(): Promise<string> {
|
|
39
|
+
return fs.mkdtemp(path.join(os.tmpdir(), "mandu-tsgolint-"));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function writeFile(root: string, rel: string, content: string): Promise<void> {
|
|
43
|
+
const abs = path.join(root, rel);
|
|
44
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
45
|
+
await fs.writeFile(abs, content, "utf-8");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Locate the repo root so real-binary tests can point the bridge at
|
|
50
|
+
* the already-installed `node_modules/.bin/oxlint` without copying
|
|
51
|
+
* shim files (Bun's bin shims embed lockfile metadata and can't
|
|
52
|
+
* survive a plain `copyFile` onto a test fixture).
|
|
53
|
+
*
|
|
54
|
+
* When the walk-up fails (detached tree / fresh clone without install)
|
|
55
|
+
* the caller gracefully asserts the `oxlint-not-installed` skip path
|
|
56
|
+
* instead, keeping the suite green.
|
|
57
|
+
*/
|
|
58
|
+
async function findRepoRoot(): Promise<string | undefined> {
|
|
59
|
+
let dir = __dirname;
|
|
60
|
+
for (let i = 0; i < 10; i++) {
|
|
61
|
+
const bin = path.join(
|
|
62
|
+
dir,
|
|
63
|
+
"node_modules",
|
|
64
|
+
".bin",
|
|
65
|
+
process.platform === "win32" ? "oxlint.exe" : "oxlint",
|
|
66
|
+
);
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(bin);
|
|
69
|
+
return dir;
|
|
70
|
+
} catch {
|
|
71
|
+
/* keep walking */
|
|
72
|
+
}
|
|
73
|
+
const parent = path.dirname(dir);
|
|
74
|
+
if (parent === dir) break;
|
|
75
|
+
dir = parent;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
81
|
+
// Pure helpers
|
|
82
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
describe("extractRuleId", () => {
|
|
85
|
+
it("normalizes typescript-eslint plugin shape to `typescript/<rule>`", () => {
|
|
86
|
+
expect(extractRuleId("typescript-eslint(no-floating-promises)")).toBe(
|
|
87
|
+
"typescript/no-floating-promises",
|
|
88
|
+
);
|
|
89
|
+
expect(extractRuleId("typescript-eslint(no-explicit-any)")).toBe(
|
|
90
|
+
"typescript/no-explicit-any",
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("returns bare rule name for eslint / unprefixed codes", () => {
|
|
95
|
+
expect(extractRuleId("no-debugger")).toBe("no-debugger");
|
|
96
|
+
expect(extractRuleId("eslint(no-debugger)")).toBe("no-debugger");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("preserves other plugin prefixes verbatim", () => {
|
|
100
|
+
expect(extractRuleId("unicorn(no-array-sort)")).toBe("unicorn/no-array-sort");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("returns 'unknown' for empty or undefined codes", () => {
|
|
104
|
+
expect(extractRuleId(undefined)).toBe("unknown");
|
|
105
|
+
expect(extractRuleId("")).toBe("unknown");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("mapOxlintSeverity", () => {
|
|
110
|
+
it("maps oxlint severity strings to Mandu severities", () => {
|
|
111
|
+
expect(mapOxlintSeverity("error")).toBe("error");
|
|
112
|
+
expect(mapOxlintSeverity("warning")).toBe("warn");
|
|
113
|
+
expect(mapOxlintSeverity("advice")).toBe("info");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("defaults unknown / undefined severity to 'warn'", () => {
|
|
117
|
+
expect(mapOxlintSeverity(undefined)).toBe("warn");
|
|
118
|
+
expect(mapOxlintSeverity("unexpected")).toBe("warn");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("translateDiagnostic", () => {
|
|
123
|
+
it("maps filename / line / column / rule / severity correctly", () => {
|
|
124
|
+
const v = translateDiagnostic(
|
|
125
|
+
{
|
|
126
|
+
message: "Unexpected any.",
|
|
127
|
+
code: "typescript-eslint(no-explicit-any)",
|
|
128
|
+
severity: "error",
|
|
129
|
+
filename: "src/foo.ts",
|
|
130
|
+
labels: [{ span: { offset: 9, length: 3, line: 2, column: 10 } }],
|
|
131
|
+
help: "Use unknown instead.",
|
|
132
|
+
url: "https://oxc.rs/...",
|
|
133
|
+
},
|
|
134
|
+
"/root",
|
|
135
|
+
);
|
|
136
|
+
expect(v.ruleName).toBe("typescript/no-explicit-any");
|
|
137
|
+
expect(v.ruleDescription).toBe("Unexpected any.");
|
|
138
|
+
expect(v.severity).toBe("error");
|
|
139
|
+
expect(v.line).toBe(2);
|
|
140
|
+
expect(v.column).toBe(10);
|
|
141
|
+
expect(v.filePath).toBe(path.join("/root", "src/foo.ts"));
|
|
142
|
+
expect(v.suggestions).toContain("Use unknown instead.");
|
|
143
|
+
expect(v.suggestions.some((s) => s.includes("https://oxc.rs"))).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("falls back to line 1 column 1 when labels are missing", () => {
|
|
147
|
+
const v = translateDiagnostic(
|
|
148
|
+
{
|
|
149
|
+
message: "X",
|
|
150
|
+
code: "typescript-eslint(y)",
|
|
151
|
+
severity: "warning",
|
|
152
|
+
filename: "src/y.ts",
|
|
153
|
+
},
|
|
154
|
+
"/root",
|
|
155
|
+
);
|
|
156
|
+
expect(v.line).toBe(1);
|
|
157
|
+
expect(v.column).toBe(1);
|
|
158
|
+
expect(v.severity).toBe("warn");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("applies severity override when provided", () => {
|
|
162
|
+
const v = translateDiagnostic(
|
|
163
|
+
{
|
|
164
|
+
message: "X",
|
|
165
|
+
code: "typescript-eslint(y)",
|
|
166
|
+
severity: "warning",
|
|
167
|
+
filename: "src/y.ts",
|
|
168
|
+
},
|
|
169
|
+
"/root",
|
|
170
|
+
"error",
|
|
171
|
+
);
|
|
172
|
+
expect(v.severity).toBe("error");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
177
|
+
// resolveOxlintBinary
|
|
178
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
describe("resolveOxlintBinary", () => {
|
|
181
|
+
let root: string;
|
|
182
|
+
|
|
183
|
+
beforeEach(async () => {
|
|
184
|
+
root = await mkFixtureRoot();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
afterEach(async () => {
|
|
188
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("returns undefined when node_modules/.bin/oxlint is absent", async () => {
|
|
192
|
+
const found = await resolveOxlintBinary(root);
|
|
193
|
+
expect(found).toBeUndefined();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("returns the resolved path when the binary exists", async () => {
|
|
197
|
+
const binDir = path.join(root, "node_modules", ".bin");
|
|
198
|
+
await fs.mkdir(binDir, { recursive: true });
|
|
199
|
+
const binName = process.platform === "win32" ? "oxlint.exe" : "oxlint";
|
|
200
|
+
await fs.writeFile(path.join(binDir, binName), "");
|
|
201
|
+
const found = await resolveOxlintBinary(root);
|
|
202
|
+
expect(found).toBe(path.join(binDir, binName));
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
207
|
+
// runTsgolint — integration
|
|
208
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
describe("runTsgolint", () => {
|
|
211
|
+
let root: string;
|
|
212
|
+
|
|
213
|
+
beforeEach(async () => {
|
|
214
|
+
root = await mkFixtureRoot();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
afterEach(async () => {
|
|
218
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("skips gracefully when oxlint binary is missing (no throw)", async () => {
|
|
222
|
+
const result = await runTsgolint({ projectRoot: root });
|
|
223
|
+
expect(result.skipped).toBe("oxlint-not-installed");
|
|
224
|
+
expect(result.violations).toEqual([]);
|
|
225
|
+
expect(result.summary.elapsedMs).toBe(0);
|
|
226
|
+
expect(result.summary.filesAnalyzed).toBe(0);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("short-circuits when severity is 'off' without touching the filesystem", async () => {
|
|
230
|
+
const result = await runTsgolint({ projectRoot: root, severity: "off" });
|
|
231
|
+
expect(result.skipped).toBe("severity-off");
|
|
232
|
+
expect(result.violations).toEqual([]);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("runs oxlint on a real fixture and round-trips an explicit-any violation", async () => {
|
|
236
|
+
const repoRoot = await findRepoRoot();
|
|
237
|
+
if (!repoRoot) {
|
|
238
|
+
// Environment without an installed oxlint — assert the skip
|
|
239
|
+
// reason as a sanity check and bail early. Keeps the suite
|
|
240
|
+
// passing on fresh clones.
|
|
241
|
+
const result = await runTsgolint({ projectRoot: root });
|
|
242
|
+
expect(result.skipped).toBe("oxlint-not-installed");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// We anchor `projectRoot` at the repo root so `resolveOxlintBinary()`
|
|
247
|
+
// finds the lockfile-aware bin shim. The fixture lives in an
|
|
248
|
+
// isolated subdir at `<repoRoot>/.tmp-tsgolint-*` and we pass only
|
|
249
|
+
// that file path as `paths`, so oxlint lints just our fixture.
|
|
250
|
+
const fixtureSubdir = `.tmp-tsgolint-fixture-${process.pid}-${Date.now()}`;
|
|
251
|
+
const fixtureDir = path.join(repoRoot, fixtureSubdir);
|
|
252
|
+
await fs.mkdir(fixtureDir, { recursive: true });
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
await writeFile(
|
|
256
|
+
fixtureDir,
|
|
257
|
+
".oxlintrc.json",
|
|
258
|
+
JSON.stringify({
|
|
259
|
+
rules: { "typescript/no-explicit-any": "error" },
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
await writeFile(
|
|
263
|
+
fixtureDir,
|
|
264
|
+
"bad.ts",
|
|
265
|
+
"const x: any = 1;\nexport { x };\n",
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// Anchor projectRoot at repoRoot so the bin-shim resolves;
|
|
269
|
+
// point paths at the fixture file (absolute path so oxlint
|
|
270
|
+
// honors it regardless of cwd).
|
|
271
|
+
const result = await runTsgolint({
|
|
272
|
+
projectRoot: repoRoot,
|
|
273
|
+
configPath: path.join(fixtureDir, ".oxlintrc.json"),
|
|
274
|
+
paths: [path.join(fixtureDir, "bad.ts")],
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
expect(result.skipped).toBeUndefined();
|
|
278
|
+
expect(result.violations.length).toBeGreaterThanOrEqual(1);
|
|
279
|
+
|
|
280
|
+
const hit = result.violations.find(
|
|
281
|
+
(v) => v.ruleName === "typescript/no-explicit-any",
|
|
282
|
+
);
|
|
283
|
+
expect(hit).toBeDefined();
|
|
284
|
+
expect(hit?.severity).toBe("error");
|
|
285
|
+
expect(hit?.filePath.endsWith("bad.ts")).toBe(true);
|
|
286
|
+
expect(hit?.line).toBeGreaterThan(0);
|
|
287
|
+
|
|
288
|
+
expect(result.summary.filesAnalyzed).toBeGreaterThanOrEqual(1);
|
|
289
|
+
expect(result.summary.rulesEnabled).toContain(
|
|
290
|
+
"typescript/no-explicit-any",
|
|
291
|
+
);
|
|
292
|
+
expect(result.summary.elapsedMs).toBeGreaterThan(0);
|
|
293
|
+
} finally {
|
|
294
|
+
await fs.rm(fixtureDir, { recursive: true, force: true });
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("filters diagnostics by the `rules` allowlist when provided", async () => {
|
|
299
|
+
const repoRoot = await findRepoRoot();
|
|
300
|
+
if (!repoRoot) {
|
|
301
|
+
// Skip — same graceful-degradation pattern as above.
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const fixtureSubdir = `.tmp-tsgolint-filter-${process.pid}-${Date.now()}`;
|
|
306
|
+
const fixtureDir = path.join(repoRoot, fixtureSubdir);
|
|
307
|
+
await fs.mkdir(fixtureDir, { recursive: true });
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
await writeFile(
|
|
311
|
+
fixtureDir,
|
|
312
|
+
".oxlintrc.json",
|
|
313
|
+
JSON.stringify({
|
|
314
|
+
rules: {
|
|
315
|
+
"typescript/no-explicit-any": "error",
|
|
316
|
+
"no-debugger": "error",
|
|
317
|
+
},
|
|
318
|
+
}),
|
|
319
|
+
);
|
|
320
|
+
await writeFile(
|
|
321
|
+
fixtureDir,
|
|
322
|
+
"mixed.ts",
|
|
323
|
+
"const x: any = 1;\ndebugger;\nexport { x };\n",
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const result = await runTsgolint({
|
|
327
|
+
projectRoot: repoRoot,
|
|
328
|
+
configPath: path.join(fixtureDir, ".oxlintrc.json"),
|
|
329
|
+
paths: [path.join(fixtureDir, "mixed.ts")],
|
|
330
|
+
rules: ["typescript/no-explicit-any"],
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Only typescript/no-explicit-any should survive the post-filter;
|
|
334
|
+
// `no-debugger` hits oxlint but the bridge drops them.
|
|
335
|
+
for (const v of result.violations) {
|
|
336
|
+
expect(v.ruleName).toBe("typescript/no-explicit-any");
|
|
337
|
+
}
|
|
338
|
+
// diagnosticsReceived counts pre-filter and should exceed
|
|
339
|
+
// post-filter violation count when both rules fired.
|
|
340
|
+
expect(result.summary.diagnosticsReceived).toBeGreaterThanOrEqual(
|
|
341
|
+
result.violations.length,
|
|
342
|
+
);
|
|
343
|
+
} finally {
|
|
344
|
+
await fs.rm(fixtureDir, { recursive: true, force: true });
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|