@mandujs/core 0.53.3 → 0.54.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 +1 -1
- 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
|
@@ -1,263 +1,263 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regression tests for `mandu:block-generated-imports` (issue #207).
|
|
3
|
-
*
|
|
4
|
-
* Split into two sections:
|
|
5
|
-
* A — pure unit tests on `ForbiddenGeneratedImportError` + helpers.
|
|
6
|
-
* B — integration tests driving a real `Bun.build` with a synthetic
|
|
7
|
-
* `__generated__/` import in a tmpdir, asserting the build fails
|
|
8
|
-
* with the expected error text.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
12
|
-
import { mkdtemp, mkdir, rm, writeFile } from "fs/promises";
|
|
13
|
-
import { tmpdir } from "os";
|
|
14
|
-
import path from "path";
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
ForbiddenGeneratedImportError,
|
|
18
|
-
blockGeneratedImports,
|
|
19
|
-
defaultAllowImporter,
|
|
20
|
-
DEFAULT_BLOCK_FILTER,
|
|
21
|
-
defaultBundlerPlugins,
|
|
22
|
-
} from "../index";
|
|
23
|
-
import { GENERATED_IMPORT_DOCS_URL } from "../../../guard/check";
|
|
24
|
-
|
|
25
|
-
// ────────────────────────────────────────────────────────────────────
|
|
26
|
-
// Section A — unit
|
|
27
|
-
// ────────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
describe("ForbiddenGeneratedImportError", () => {
|
|
30
|
-
test("captures specifier and importer fields", () => {
|
|
31
|
-
const err = new ForbiddenGeneratedImportError(
|
|
32
|
-
"./__generated__/routes",
|
|
33
|
-
"/repo/src/app.ts",
|
|
34
|
-
);
|
|
35
|
-
expect(err).toBeInstanceOf(Error);
|
|
36
|
-
expect(err).toBeInstanceOf(ForbiddenGeneratedImportError);
|
|
37
|
-
expect(err.specifier).toBe("./__generated__/routes");
|
|
38
|
-
expect(err.importer).toBe("/repo/src/app.ts");
|
|
39
|
-
expect(err.docsUrl).toBe(GENERATED_IMPORT_DOCS_URL);
|
|
40
|
-
expect(err.name).toBe("ForbiddenGeneratedImportError");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("message includes specifier, importer, docs URL, and getGenerated() hint", () => {
|
|
44
|
-
const err = new ForbiddenGeneratedImportError(
|
|
45
|
-
"./__generated__/data",
|
|
46
|
-
"/repo/src/page.tsx",
|
|
47
|
-
);
|
|
48
|
-
expect(err.message).toContain("./__generated__/data");
|
|
49
|
-
expect(err.message).toContain("/repo/src/page.tsx");
|
|
50
|
-
expect(err.message).toContain(GENERATED_IMPORT_DOCS_URL);
|
|
51
|
-
expect(err.message).toContain("getGenerated");
|
|
52
|
-
expect(err.message).toContain('@mandujs/core/runtime');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("message falls back to <unknown> importer when empty", () => {
|
|
56
|
-
const err = new ForbiddenGeneratedImportError("./__generated__/x", "");
|
|
57
|
-
expect(err.message).toContain("<unknown>");
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("defaultAllowImporter", () => {
|
|
62
|
-
test("exempts packages/core/src/runtime/** paths", () => {
|
|
63
|
-
expect(
|
|
64
|
-
defaultAllowImporter("/repo/packages/core/src/runtime/registry.ts"),
|
|
65
|
-
).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("normalises Windows backslashes", () => {
|
|
69
|
-
expect(
|
|
70
|
-
defaultAllowImporter(
|
|
71
|
-
"C:\\repo\\packages\\core\\src\\runtime\\registry.ts",
|
|
72
|
-
),
|
|
73
|
-
).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("does NOT exempt regular user code", () => {
|
|
77
|
-
expect(defaultAllowImporter("/repo/src/app.ts")).toBe(false);
|
|
78
|
-
expect(defaultAllowImporter("")).toBe(false);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe("DEFAULT_BLOCK_FILTER", () => {
|
|
83
|
-
test("matches __generated__ specifiers", () => {
|
|
84
|
-
expect("./__generated__/foo").toMatch(DEFAULT_BLOCK_FILTER);
|
|
85
|
-
expect("../../src/__generated__/routes").toMatch(DEFAULT_BLOCK_FILTER);
|
|
86
|
-
});
|
|
87
|
-
test("does NOT match look-alikes without double underscores", () => {
|
|
88
|
-
expect("./generated/foo".match(DEFAULT_BLOCK_FILTER)).toBeNull();
|
|
89
|
-
expect("./src/generate/foo".match(DEFAULT_BLOCK_FILTER)).toBeNull();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("defaultBundlerPlugins", () => {
|
|
94
|
-
test("installs block-generated-imports by default", () => {
|
|
95
|
-
const plugins = defaultBundlerPlugins();
|
|
96
|
-
expect(plugins).toHaveLength(1);
|
|
97
|
-
expect(plugins[0]?.name).toBe("mandu:block-generated-imports");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("respects opt-out via guard.blockGeneratedImport === false", () => {
|
|
101
|
-
const plugins = defaultBundlerPlugins({
|
|
102
|
-
config: { guard: { blockGeneratedImport: false } },
|
|
103
|
-
});
|
|
104
|
-
expect(plugins).toHaveLength(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("treats undefined as default-on", () => {
|
|
108
|
-
const plugins = defaultBundlerPlugins({ config: { guard: {} } });
|
|
109
|
-
expect(plugins).toHaveLength(1);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// ────────────────────────────────────────────────────────────────────
|
|
114
|
-
// Section B — integration against real Bun.build
|
|
115
|
-
// ────────────────────────────────────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Integration tests invoke real `Bun.build`. On shards where the
|
|
119
|
-
* Windows `onResolve` panic is flaky, set `MANDU_SKIP_BUNDLER_TESTS=1`
|
|
120
|
-
* to skip — matches the convention used by `fast-refresh.test.ts`.
|
|
121
|
-
*/
|
|
122
|
-
const SKIP_BUNDLER = process.env.MANDU_SKIP_BUNDLER_TESTS === "1";
|
|
123
|
-
|
|
124
|
-
describe.skipIf(SKIP_BUNDLER)("blockGeneratedImports — Bun.build integration", () => {
|
|
125
|
-
let tmpRoot: string;
|
|
126
|
-
|
|
127
|
-
beforeAll(async () => {
|
|
128
|
-
tmpRoot = await mkdtemp(path.join(tmpdir(), "mandu-block-gen-"));
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
afterAll(async () => {
|
|
132
|
-
await rm(tmpRoot, { recursive: true, force: true }).catch(() => {});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("Bun.build fails when source imports a __generated__ file", async () => {
|
|
136
|
-
const dir = path.join(tmpRoot, "scenario-block");
|
|
137
|
-
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
138
|
-
await writeFile(
|
|
139
|
-
path.join(dir, "__generated__/data.ts"),
|
|
140
|
-
"export const routes = [];\n",
|
|
141
|
-
);
|
|
142
|
-
await writeFile(
|
|
143
|
-
path.join(dir, "entry.ts"),
|
|
144
|
-
`import { routes } from "./__generated__/data";\nconsole.log(routes);\n`,
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
// Bun's plugin host surfaces a thrown onResolve error either as an
|
|
148
|
-
// exception from `Bun.build(...)` OR as an unsuccessful result whose
|
|
149
|
-
// logs contain the message. Accept either shape so the assertion is
|
|
150
|
-
// robust across Bun patch releases.
|
|
151
|
-
// Bun's plugin host surfaces a thrown onResolve error via multiple
|
|
152
|
-
// channels across patch releases: an exception from `Bun.build()`
|
|
153
|
-
// (with `AggregateError.errors[]` on recent versions), or an
|
|
154
|
-
// unsuccessful `result.logs[]`. Collect from every channel and assert
|
|
155
|
-
// the aggregated text.
|
|
156
|
-
const collected: string[] = [];
|
|
157
|
-
try {
|
|
158
|
-
const result = await Bun.build({
|
|
159
|
-
entrypoints: [path.join(dir, "entry.ts")],
|
|
160
|
-
outdir: path.join(dir, "out"),
|
|
161
|
-
target: "browser",
|
|
162
|
-
plugins: [blockGeneratedImports()],
|
|
163
|
-
});
|
|
164
|
-
expect(result.success).toBe(false);
|
|
165
|
-
for (const log of result.logs) {
|
|
166
|
-
collected.push(String(log?.message ?? log));
|
|
167
|
-
}
|
|
168
|
-
} catch (err) {
|
|
169
|
-
if (err instanceof Error) collected.push(err.message);
|
|
170
|
-
const maybeAgg = err as { errors?: unknown[] };
|
|
171
|
-
if (Array.isArray(maybeAgg.errors)) {
|
|
172
|
-
for (const inner of maybeAgg.errors) {
|
|
173
|
-
if (inner instanceof Error) collected.push(inner.message);
|
|
174
|
-
else collected.push(String(inner));
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const message = collected.join("\n");
|
|
180
|
-
expect(message).toContain("__generated__");
|
|
181
|
-
expect(message).toContain(GENERATED_IMPORT_DOCS_URL);
|
|
182
|
-
expect(message).toContain("getGenerated");
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test("Bun.build succeeds when opt-out is set (no plugin installed)", async () => {
|
|
186
|
-
const dir = path.join(tmpRoot, "scenario-optout");
|
|
187
|
-
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
188
|
-
await writeFile(
|
|
189
|
-
path.join(dir, "__generated__/data.ts"),
|
|
190
|
-
"export const routes = [];\n",
|
|
191
|
-
);
|
|
192
|
-
await writeFile(
|
|
193
|
-
path.join(dir, "entry.ts"),
|
|
194
|
-
`import { routes } from "./__generated__/data";\nconsole.log(routes);\n`,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
// Simulate the opt-out path: defaultBundlerPlugins with the flag off
|
|
198
|
-
// returns an empty plugin list, so the build has nothing to reject it.
|
|
199
|
-
const plugins = defaultBundlerPlugins({
|
|
200
|
-
config: { guard: { blockGeneratedImport: false } },
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const result = await Bun.build({
|
|
204
|
-
entrypoints: [path.join(dir, "entry.ts")],
|
|
205
|
-
outdir: path.join(dir, "out"),
|
|
206
|
-
target: "browser",
|
|
207
|
-
plugins,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
expect(result.success).toBe(true);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("Bun.build succeeds when source has no __generated__ imports", async () => {
|
|
214
|
-
const dir = path.join(tmpRoot, "scenario-clean");
|
|
215
|
-
await mkdir(dir, { recursive: true });
|
|
216
|
-
await writeFile(
|
|
217
|
-
path.join(dir, "clean.ts"),
|
|
218
|
-
`export const value = 42;\n`,
|
|
219
|
-
);
|
|
220
|
-
await writeFile(
|
|
221
|
-
path.join(dir, "entry.ts"),
|
|
222
|
-
`import { value } from "./clean";\nconsole.log(value);\n`,
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const result = await Bun.build({
|
|
226
|
-
entrypoints: [path.join(dir, "entry.ts")],
|
|
227
|
-
outdir: path.join(dir, "out"),
|
|
228
|
-
target: "browser",
|
|
229
|
-
plugins: [blockGeneratedImports()],
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
expect(result.success).toBe(true);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("allowImporter lets whitelisted files import __generated__", async () => {
|
|
236
|
-
const dir = path.join(tmpRoot, "scenario-allow");
|
|
237
|
-
const runtimeDir = path.join(dir, "packages/core/src/runtime");
|
|
238
|
-
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
239
|
-
await mkdir(runtimeDir, { recursive: true });
|
|
240
|
-
await writeFile(
|
|
241
|
-
path.join(dir, "__generated__/data.ts"),
|
|
242
|
-
"export const routes = [];\n",
|
|
243
|
-
);
|
|
244
|
-
// Use a relative import so the filter can see `__generated__` in the
|
|
245
|
-
// specifier and invoke our hook — at which point the importer path
|
|
246
|
-
// matches the default allow-list.
|
|
247
|
-
await writeFile(
|
|
248
|
-
path.join(runtimeDir, "registry.ts"),
|
|
249
|
-
`import { routes } from "../../../../__generated__/data";\nexport { routes };\n`,
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
const result = await Bun.build({
|
|
253
|
-
entrypoints: [path.join(runtimeDir, "registry.ts")],
|
|
254
|
-
outdir: path.join(dir, "out"),
|
|
255
|
-
target: "browser",
|
|
256
|
-
plugins: [blockGeneratedImports()],
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// The allow predicate lets the import fall through to Bun's default
|
|
260
|
-
// resolution, which succeeds because the file exists.
|
|
261
|
-
expect(result.success).toBe(true);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for `mandu:block-generated-imports` (issue #207).
|
|
3
|
+
*
|
|
4
|
+
* Split into two sections:
|
|
5
|
+
* A — pure unit tests on `ForbiddenGeneratedImportError` + helpers.
|
|
6
|
+
* B — integration tests driving a real `Bun.build` with a synthetic
|
|
7
|
+
* `__generated__/` import in a tmpdir, asserting the build fails
|
|
8
|
+
* with the expected error text.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
12
|
+
import { mkdtemp, mkdir, rm, writeFile } from "fs/promises";
|
|
13
|
+
import { tmpdir } from "os";
|
|
14
|
+
import path from "path";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
ForbiddenGeneratedImportError,
|
|
18
|
+
blockGeneratedImports,
|
|
19
|
+
defaultAllowImporter,
|
|
20
|
+
DEFAULT_BLOCK_FILTER,
|
|
21
|
+
defaultBundlerPlugins,
|
|
22
|
+
} from "../index";
|
|
23
|
+
import { GENERATED_IMPORT_DOCS_URL } from "../../../guard/check";
|
|
24
|
+
|
|
25
|
+
// ────────────────────────────────────────────────────────────────────
|
|
26
|
+
// Section A — unit
|
|
27
|
+
// ────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
describe("ForbiddenGeneratedImportError", () => {
|
|
30
|
+
test("captures specifier and importer fields", () => {
|
|
31
|
+
const err = new ForbiddenGeneratedImportError(
|
|
32
|
+
"./__generated__/routes",
|
|
33
|
+
"/repo/src/app.ts",
|
|
34
|
+
);
|
|
35
|
+
expect(err).toBeInstanceOf(Error);
|
|
36
|
+
expect(err).toBeInstanceOf(ForbiddenGeneratedImportError);
|
|
37
|
+
expect(err.specifier).toBe("./__generated__/routes");
|
|
38
|
+
expect(err.importer).toBe("/repo/src/app.ts");
|
|
39
|
+
expect(err.docsUrl).toBe(GENERATED_IMPORT_DOCS_URL);
|
|
40
|
+
expect(err.name).toBe("ForbiddenGeneratedImportError");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("message includes specifier, importer, docs URL, and getGenerated() hint", () => {
|
|
44
|
+
const err = new ForbiddenGeneratedImportError(
|
|
45
|
+
"./__generated__/data",
|
|
46
|
+
"/repo/src/page.tsx",
|
|
47
|
+
);
|
|
48
|
+
expect(err.message).toContain("./__generated__/data");
|
|
49
|
+
expect(err.message).toContain("/repo/src/page.tsx");
|
|
50
|
+
expect(err.message).toContain(GENERATED_IMPORT_DOCS_URL);
|
|
51
|
+
expect(err.message).toContain("getGenerated");
|
|
52
|
+
expect(err.message).toContain('@mandujs/core/runtime');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("message falls back to <unknown> importer when empty", () => {
|
|
56
|
+
const err = new ForbiddenGeneratedImportError("./__generated__/x", "");
|
|
57
|
+
expect(err.message).toContain("<unknown>");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("defaultAllowImporter", () => {
|
|
62
|
+
test("exempts packages/core/src/runtime/** paths", () => {
|
|
63
|
+
expect(
|
|
64
|
+
defaultAllowImporter("/repo/packages/core/src/runtime/registry.ts"),
|
|
65
|
+
).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("normalises Windows backslashes", () => {
|
|
69
|
+
expect(
|
|
70
|
+
defaultAllowImporter(
|
|
71
|
+
"C:\\repo\\packages\\core\\src\\runtime\\registry.ts",
|
|
72
|
+
),
|
|
73
|
+
).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("does NOT exempt regular user code", () => {
|
|
77
|
+
expect(defaultAllowImporter("/repo/src/app.ts")).toBe(false);
|
|
78
|
+
expect(defaultAllowImporter("")).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("DEFAULT_BLOCK_FILTER", () => {
|
|
83
|
+
test("matches __generated__ specifiers", () => {
|
|
84
|
+
expect("./__generated__/foo").toMatch(DEFAULT_BLOCK_FILTER);
|
|
85
|
+
expect("../../src/__generated__/routes").toMatch(DEFAULT_BLOCK_FILTER);
|
|
86
|
+
});
|
|
87
|
+
test("does NOT match look-alikes without double underscores", () => {
|
|
88
|
+
expect("./generated/foo".match(DEFAULT_BLOCK_FILTER)).toBeNull();
|
|
89
|
+
expect("./src/generate/foo".match(DEFAULT_BLOCK_FILTER)).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("defaultBundlerPlugins", () => {
|
|
94
|
+
test("installs block-generated-imports by default", () => {
|
|
95
|
+
const plugins = defaultBundlerPlugins();
|
|
96
|
+
expect(plugins).toHaveLength(1);
|
|
97
|
+
expect(plugins[0]?.name).toBe("mandu:block-generated-imports");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("respects opt-out via guard.blockGeneratedImport === false", () => {
|
|
101
|
+
const plugins = defaultBundlerPlugins({
|
|
102
|
+
config: { guard: { blockGeneratedImport: false } },
|
|
103
|
+
});
|
|
104
|
+
expect(plugins).toHaveLength(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("treats undefined as default-on", () => {
|
|
108
|
+
const plugins = defaultBundlerPlugins({ config: { guard: {} } });
|
|
109
|
+
expect(plugins).toHaveLength(1);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ────────────────────────────────────────────────────────────────────
|
|
114
|
+
// Section B — integration against real Bun.build
|
|
115
|
+
// ────────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Integration tests invoke real `Bun.build`. On shards where the
|
|
119
|
+
* Windows `onResolve` panic is flaky, set `MANDU_SKIP_BUNDLER_TESTS=1`
|
|
120
|
+
* to skip — matches the convention used by `fast-refresh.test.ts`.
|
|
121
|
+
*/
|
|
122
|
+
const SKIP_BUNDLER = process.env.MANDU_SKIP_BUNDLER_TESTS === "1";
|
|
123
|
+
|
|
124
|
+
describe.skipIf(SKIP_BUNDLER)("blockGeneratedImports — Bun.build integration", () => {
|
|
125
|
+
let tmpRoot: string;
|
|
126
|
+
|
|
127
|
+
beforeAll(async () => {
|
|
128
|
+
tmpRoot = await mkdtemp(path.join(tmpdir(), "mandu-block-gen-"));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
afterAll(async () => {
|
|
132
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => {});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("Bun.build fails when source imports a __generated__ file", async () => {
|
|
136
|
+
const dir = path.join(tmpRoot, "scenario-block");
|
|
137
|
+
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
138
|
+
await writeFile(
|
|
139
|
+
path.join(dir, "__generated__/data.ts"),
|
|
140
|
+
"export const routes = [];\n",
|
|
141
|
+
);
|
|
142
|
+
await writeFile(
|
|
143
|
+
path.join(dir, "entry.ts"),
|
|
144
|
+
`import { routes } from "./__generated__/data";\nconsole.log(routes);\n`,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Bun's plugin host surfaces a thrown onResolve error either as an
|
|
148
|
+
// exception from `Bun.build(...)` OR as an unsuccessful result whose
|
|
149
|
+
// logs contain the message. Accept either shape so the assertion is
|
|
150
|
+
// robust across Bun patch releases.
|
|
151
|
+
// Bun's plugin host surfaces a thrown onResolve error via multiple
|
|
152
|
+
// channels across patch releases: an exception from `Bun.build()`
|
|
153
|
+
// (with `AggregateError.errors[]` on recent versions), or an
|
|
154
|
+
// unsuccessful `result.logs[]`. Collect from every channel and assert
|
|
155
|
+
// the aggregated text.
|
|
156
|
+
const collected: string[] = [];
|
|
157
|
+
try {
|
|
158
|
+
const result = await Bun.build({
|
|
159
|
+
entrypoints: [path.join(dir, "entry.ts")],
|
|
160
|
+
outdir: path.join(dir, "out"),
|
|
161
|
+
target: "browser",
|
|
162
|
+
plugins: [blockGeneratedImports()],
|
|
163
|
+
});
|
|
164
|
+
expect(result.success).toBe(false);
|
|
165
|
+
for (const log of result.logs) {
|
|
166
|
+
collected.push(String(log?.message ?? log));
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err instanceof Error) collected.push(err.message);
|
|
170
|
+
const maybeAgg = err as { errors?: unknown[] };
|
|
171
|
+
if (Array.isArray(maybeAgg.errors)) {
|
|
172
|
+
for (const inner of maybeAgg.errors) {
|
|
173
|
+
if (inner instanceof Error) collected.push(inner.message);
|
|
174
|
+
else collected.push(String(inner));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const message = collected.join("\n");
|
|
180
|
+
expect(message).toContain("__generated__");
|
|
181
|
+
expect(message).toContain(GENERATED_IMPORT_DOCS_URL);
|
|
182
|
+
expect(message).toContain("getGenerated");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("Bun.build succeeds when opt-out is set (no plugin installed)", async () => {
|
|
186
|
+
const dir = path.join(tmpRoot, "scenario-optout");
|
|
187
|
+
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
188
|
+
await writeFile(
|
|
189
|
+
path.join(dir, "__generated__/data.ts"),
|
|
190
|
+
"export const routes = [];\n",
|
|
191
|
+
);
|
|
192
|
+
await writeFile(
|
|
193
|
+
path.join(dir, "entry.ts"),
|
|
194
|
+
`import { routes } from "./__generated__/data";\nconsole.log(routes);\n`,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Simulate the opt-out path: defaultBundlerPlugins with the flag off
|
|
198
|
+
// returns an empty plugin list, so the build has nothing to reject it.
|
|
199
|
+
const plugins = defaultBundlerPlugins({
|
|
200
|
+
config: { guard: { blockGeneratedImport: false } },
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const result = await Bun.build({
|
|
204
|
+
entrypoints: [path.join(dir, "entry.ts")],
|
|
205
|
+
outdir: path.join(dir, "out"),
|
|
206
|
+
target: "browser",
|
|
207
|
+
plugins,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(result.success).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("Bun.build succeeds when source has no __generated__ imports", async () => {
|
|
214
|
+
const dir = path.join(tmpRoot, "scenario-clean");
|
|
215
|
+
await mkdir(dir, { recursive: true });
|
|
216
|
+
await writeFile(
|
|
217
|
+
path.join(dir, "clean.ts"),
|
|
218
|
+
`export const value = 42;\n`,
|
|
219
|
+
);
|
|
220
|
+
await writeFile(
|
|
221
|
+
path.join(dir, "entry.ts"),
|
|
222
|
+
`import { value } from "./clean";\nconsole.log(value);\n`,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const result = await Bun.build({
|
|
226
|
+
entrypoints: [path.join(dir, "entry.ts")],
|
|
227
|
+
outdir: path.join(dir, "out"),
|
|
228
|
+
target: "browser",
|
|
229
|
+
plugins: [blockGeneratedImports()],
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result.success).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("allowImporter lets whitelisted files import __generated__", async () => {
|
|
236
|
+
const dir = path.join(tmpRoot, "scenario-allow");
|
|
237
|
+
const runtimeDir = path.join(dir, "packages/core/src/runtime");
|
|
238
|
+
await mkdir(path.join(dir, "__generated__"), { recursive: true });
|
|
239
|
+
await mkdir(runtimeDir, { recursive: true });
|
|
240
|
+
await writeFile(
|
|
241
|
+
path.join(dir, "__generated__/data.ts"),
|
|
242
|
+
"export const routes = [];\n",
|
|
243
|
+
);
|
|
244
|
+
// Use a relative import so the filter can see `__generated__` in the
|
|
245
|
+
// specifier and invoke our hook — at which point the importer path
|
|
246
|
+
// matches the default allow-list.
|
|
247
|
+
await writeFile(
|
|
248
|
+
path.join(runtimeDir, "registry.ts"),
|
|
249
|
+
`import { routes } from "../../../../__generated__/data";\nexport { routes };\n`,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const result = await Bun.build({
|
|
253
|
+
entrypoints: [path.join(runtimeDir, "registry.ts")],
|
|
254
|
+
outdir: path.join(dir, "out"),
|
|
255
|
+
target: "browser",
|
|
256
|
+
plugins: [blockGeneratedImports()],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// The allow predicate lets the import fall through to Bun's default
|
|
260
|
+
// resolution, which succeeds because the file exists.
|
|
261
|
+
expect(result.success).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
});
|