wrangler 2.12.3 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +11 -7
  2. package/src/__tests__/api-devregistry.test.ts +121 -0
  3. package/src/__tests__/api.test.ts +27 -5
  4. package/src/__tests__/configuration.test.ts +59 -12
  5. package/src/__tests__/deployments.test.ts +335 -95
  6. package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
  7. package/src/__tests__/helpers/msw/index.ts +4 -2
  8. package/src/__tests__/index.test.ts +10 -4
  9. package/src/__tests__/jest.setup.ts +4 -0
  10. package/src/__tests__/mtls-certificates.test.ts +5 -2
  11. package/src/__tests__/pages/publish.test.ts +67 -23
  12. package/src/__tests__/publish.test.ts +138 -59
  13. package/src/__tests__/queues.test.ts +5 -2
  14. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  15. package/src/api/dev.ts +7 -18
  16. package/src/api/pages/create-worker-bundle-contents.ts +1 -0
  17. package/src/bundle.ts +19 -5
  18. package/src/config/environment.ts +27 -0
  19. package/src/config/index.ts +18 -0
  20. package/src/config/validation.ts +91 -1
  21. package/src/create-worker-upload-form.ts +18 -1
  22. package/src/d1/execute.tsx +1 -1
  23. package/src/d1/migrations/apply.tsx +2 -1
  24. package/src/deployments.ts +260 -8
  25. package/src/dev/start-server.ts +2 -8
  26. package/src/dev/use-esbuild.ts +2 -8
  27. package/src/dev-registry.ts +2 -1
  28. package/src/dev.tsx +1 -0
  29. package/src/entry.ts +18 -8
  30. package/src/index.ts +75 -22
  31. package/src/init.ts +144 -135
  32. package/src/metrics/send-event.ts +2 -1
  33. package/src/module-collection.ts +91 -25
  34. package/src/pages/functions/buildPlugin.ts +1 -0
  35. package/src/pages/functions/buildWorker.ts +2 -8
  36. package/src/publish/publish.ts +10 -26
  37. package/src/queues/cli/commands/consumer/add.ts +6 -0
  38. package/src/queues/client.ts +1 -0
  39. package/src/secret/index.ts +1 -0
  40. package/src/traverse-module-graph.ts +53 -0
  41. package/src/worker.ts +10 -0
  42. package/wrangler-dist/cli.d.ts +24 -0
  43. package/wrangler-dist/cli.js +19083 -18680
  44. package/src/__tests__/api-devregistry.test.js +0 -64
  45. package/src/__tests__/tsconfig.tsbuildinfo +0 -1
  46. package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
  47. package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
  48. package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
  49. package/templates/tsconfig.tsbuildinfo +0 -1
@@ -2245,20 +2245,22 @@ and that at least one include rule is provided.
2245
2245
  mkdirSync("public");
2246
2246
  writeFileSync("public/README.md", "This is a readme");
2247
2247
 
2248
- // set up hello.wasm
2249
- mkdirSync("wasm");
2250
- writeFileSync("wasm/hello.wasm", "HELLO WORLD");
2248
+ // set up some "external" modules
2249
+ mkdirSync("external");
2250
+ writeFileSync("external/hello.wasm", "Hello Wasm modules world!");
2251
+ writeFileSync("external/hello.txt", "Hello Text modules world!");
2251
2252
 
2252
2253
  // set up Functions
2253
2254
  mkdirSync("functions");
2254
2255
  writeFileSync(
2255
2256
  "functions/hello.js",
2256
2257
  `
2257
- import hello from "./../wasm/hello.wasm";
2258
+ import wasm from "./../external/hello.wasm";
2259
+ import text from "./../external/hello.txt"
2258
2260
  export async function onRequest() {
2259
- const helloModule = await WebAssembly.instantiate(hello);
2260
- const greeting = helloModule.exports.hello;
2261
- return new Response(greeting);
2261
+ const helloModule = await WebAssembly.instantiate(wasm);
2262
+ const wasmGreeting = helloModule.exports.hello;
2263
+ return new Response(wasmGreeting + text);
2262
2264
  }
2263
2265
  `
2264
2266
  );
@@ -2363,6 +2365,10 @@ and that at least one include rule is provided.
2363
2365
  /[0-9a-z]*-hello.wasm/g,
2364
2366
  "test-hello.wasm"
2365
2367
  );
2368
+ workerBundleWithConstantData = workerBundleWithConstantData.replace(
2369
+ /[0-9a-z]*-hello.txt/g,
2370
+ "test-hello.txt"
2371
+ );
2366
2372
 
2367
2373
  // check we appended the metadata
2368
2374
  expect(workerBundleWithConstantData).toContain(
@@ -2377,18 +2383,29 @@ and that at least one include rule is provided.
2377
2383
  `Content-Disposition: form-data; name="functionsWorker-0.test.js"; filename="functionsWorker-0.test.js"`
2378
2384
  );
2379
2385
  expect(workerBundleWithConstantData).toContain(`
2380
- import hello from "./test-hello.wasm";
2386
+ import wasm from "./test-hello.wasm";
2387
+ import text from "./test-hello.txt";
2381
2388
  async function onRequest() {
2382
- const helloModule = await WebAssembly.instantiate(hello);
2383
- const greeting = helloModule.exports.hello;
2384
- return new Response(greeting);
2389
+ const helloModule = await WebAssembly.instantiate(wasm);
2390
+ const wasmGreeting = helloModule.exports.hello;
2391
+ return new Response(wasmGreeting + text);
2385
2392
  }`);
2386
2393
 
2387
2394
  // check we appended the wasm module
2388
2395
  expect(workerBundleWithConstantData).toContain(
2389
2396
  `Content-Disposition: form-data; name="./test-hello.wasm"; filename="./test-hello.wasm"`
2390
2397
  );
2391
- expect(workerBundleWithConstantData).toContain(`HELLO WORLD`);
2398
+ expect(workerBundleWithConstantData).toContain(
2399
+ `Hello Wasm modules world!`
2400
+ );
2401
+
2402
+ // check we appended the text module
2403
+ expect(workerBundleWithConstantData).toContain(
2404
+ `Content-Disposition: form-data; name="./test-hello.txt"; filename="./test-hello.txt"`
2405
+ );
2406
+ expect(workerBundleWithConstantData).toContain(
2407
+ `Hello Text modules world!`
2408
+ );
2392
2409
 
2393
2410
  return res.once(
2394
2411
  ctx.status(200),
@@ -2445,20 +2462,31 @@ async function onRequest() {
2445
2462
  writeFileSync("public/README.md", "This is a readme");
2446
2463
 
2447
2464
  // set up hello.wasm
2448
- mkdirSync("wasm");
2449
- writeFileSync("wasm/hello.wasm", "HELLO");
2465
+ mkdirSync("external");
2466
+ writeFileSync("external/hello.wasm", "Hello wasm modules");
2467
+ writeFileSync(
2468
+ "external/hello.html",
2469
+ "<html><body>Hello text modules</body></html>"
2470
+ );
2450
2471
 
2451
2472
  // set up _worker.js
2452
2473
  writeFileSync(
2453
2474
  "public/_worker.js",
2454
2475
  `
2455
- import hello from "./../wasm/hello.wasm";
2476
+ import wasm from "./../external/hello.wasm";
2477
+ import html from "./../external/hello.html";
2456
2478
  export default {
2457
2479
  async fetch(request, env) {
2458
2480
  const url = new URL(request.url);
2459
- const helloModule = await WebAssembly.instantiate(hello);
2460
- const greeting = helloModule.exports.hello;
2461
- return url.pathname.startsWith('/hello') ? new Response(greeting) : env.ASSETS.fetch(request);
2481
+ const helloModule = await WebAssembly.instantiate(wasm);
2482
+ const wasmGreeting = helloModule.exports.hello;
2483
+ if(url.pathname.startsWith('/hello-wasm')) {
2484
+ return new Response(wasmGreeting);
2485
+ }
2486
+ if(url.pathname.startsWith('/hello-text')) {
2487
+ return new Response(html);
2488
+ }
2489
+ return env.ASSETS.fetch(request);
2462
2490
  }
2463
2491
  };
2464
2492
  `
@@ -2558,6 +2586,10 @@ async function onRequest() {
2558
2586
  /[0-9a-z]*-hello.wasm/g,
2559
2587
  "test-hello.wasm"
2560
2588
  );
2589
+ workerBundleWithConstantData = workerBundleWithConstantData.replace(
2590
+ /[0-9a-z]*-hello.html/g,
2591
+ "test-hello.html"
2592
+ );
2561
2593
 
2562
2594
  // we care about a couple of things here, like the presence of `metadata`,
2563
2595
  // `bundledWorker`, the wasm import, etc., and since `workerBundle` is
@@ -2572,13 +2604,20 @@ async function onRequest() {
2572
2604
  Content-Type: application/javascript+module
2573
2605
 
2574
2606
  // _worker.js
2575
- import hello from \\"./test-hello.wasm\\";
2607
+ import wasm from \\"./test-hello.wasm\\";
2608
+ import html from \\"./test-hello.html\\";
2576
2609
  var worker_default = {
2577
2610
  async fetch(request, env) {
2578
2611
  const url = new URL(request.url);
2579
- const helloModule = await WebAssembly.instantiate(hello);
2580
- const greeting = helloModule.exports.hello;
2581
- return url.pathname.startsWith(\\"/hello\\") ? new Response(greeting) : env.ASSETS.fetch(request);
2612
+ const helloModule = await WebAssembly.instantiate(wasm);
2613
+ const wasmGreeting = helloModule.exports.hello;
2614
+ if (url.pathname.startsWith(\\"/hello-wasm\\")) {
2615
+ return new Response(wasmGreeting);
2616
+ }
2617
+ if (url.pathname.startsWith(\\"/hello-text\\")) {
2618
+ return new Response(html);
2619
+ }
2620
+ return env.ASSETS.fetch(request);
2582
2621
  }
2583
2622
  };
2584
2623
  export {
@@ -2590,7 +2629,12 @@ async function onRequest() {
2590
2629
  Content-Disposition: form-data; name=\\"./test-hello.wasm\\"; filename=\\"./test-hello.wasm\\"
2591
2630
  Content-Type: application/wasm
2592
2631
 
2593
- HELLO
2632
+ Hello wasm modules
2633
+ ------formdata-undici-0.test
2634
+ Content-Disposition: form-data; name=\\"./test-hello.html\\"; filename=\\"./test-hello.html\\"
2635
+ Content-Type: text/plain
2636
+
2637
+ <html><body>Hello text modules</body></html>
2594
2638
  ------formdata-undici-0.test--"
2595
2639
  `);
2596
2640
 
@@ -32,7 +32,7 @@ import {
32
32
  createFetchResult,
33
33
  msw,
34
34
  mswSuccessDeployments,
35
- mswSuccessLastDeployment,
35
+ mswSuccessDeploymentScriptMetadata,
36
36
  } from "./helpers/msw";
37
37
  import { FileReaderSync } from "./helpers/msw/read-file-sync";
38
38
  import { runInTempDir } from "./helpers/run-in-tmp";
@@ -89,7 +89,7 @@ describe("publish", () => {
89
89
  );
90
90
  writeWorkerSource();
91
91
  mockSubDomainRequest();
92
- mockUploadWorkerRequest({ expectedType: "esm", sendScriptIds: true });
92
+ mockUploadWorkerRequest({ expectedType: "esm" });
93
93
  mockOAuthServerCallback();
94
94
 
95
95
  await runWrangler("publish ./index");
@@ -1341,6 +1341,32 @@ export default{
1341
1341
  expect(std.err).toMatchInlineSnapshot(`""`);
1342
1342
  });
1343
1343
 
1344
+ it("should allow cloudflare module import", async () => {
1345
+ writeWranglerToml();
1346
+ fs.writeFileSync(
1347
+ "./index.js",
1348
+ `
1349
+ import { EmailMessage } from "cloudflare:email";
1350
+ export default{
1351
+ fetch(){
1352
+ return new Response("all done");
1353
+ }
1354
+ }
1355
+ `
1356
+ );
1357
+ mockUploadWorkerRequest();
1358
+ mockSubDomainRequest();
1359
+ await runWrangler("publish index.js");
1360
+ expect(std.out).toMatchInlineSnapshot(`
1361
+ "Total Upload: xx KiB / gzip: xx KiB
1362
+ Uploaded test-name (TIMINGS)
1363
+ Published test-name (TIMINGS)
1364
+ https://test-name.test-sub-domain.workers.dev
1365
+ Current Deployment ID: Galaxy-Class"
1366
+ `);
1367
+ expect(std.err).toMatchInlineSnapshot(`""`);
1368
+ });
1369
+
1344
1370
  it("should be able to transpile entry-points in sub-directories (esm)", async () => {
1345
1371
  writeWranglerToml();
1346
1372
  writeWorkerSource({ basePath: "./src" });
@@ -7324,54 +7350,6 @@ export default{
7324
7350
  });
7325
7351
  });
7326
7352
 
7327
- it("should publish if the last deployed source check fails", async () => {
7328
- writeWorkerSource();
7329
- writeWranglerToml();
7330
- mockSubDomainRequest();
7331
- mockUploadWorkerRequest();
7332
- msw.use(
7333
- rest.get(
7334
- "*/accounts/:accountId/workers/deployments/by-script/:scriptTag",
7335
- (_, res, ctx) => {
7336
- return res(
7337
- ctx.json(
7338
- createFetchResult({
7339
- latest: { number: "2" },
7340
- })
7341
- )
7342
- );
7343
- }
7344
- ),
7345
- rest.get(
7346
- "*/accounts/:accountId/workers/services/:scriptName",
7347
- (_, res, ctx) => {
7348
- return res(
7349
- ctx.json(
7350
- createFetchResult(null, false, [
7351
- { code: 10090, message: "workers.api.error.service_not_found" },
7352
- ])
7353
- )
7354
- );
7355
- }
7356
- )
7357
- );
7358
-
7359
- await runWrangler("publish index.js");
7360
- expect(std).toMatchInlineSnapshot(`
7361
- Object {
7362
- "debug": "",
7363
- "err": "",
7364
- "info": "",
7365
- "out": "Total Upload: xx KiB / gzip: xx KiB
7366
- Uploaded test-name (TIMINGS)
7367
- Published test-name (TIMINGS)
7368
- https://test-name.test-sub-domain.workers.dev
7369
- Current Deployment ID: undefined",
7370
- "warn": "",
7371
- }
7372
- `);
7373
- });
7374
-
7375
7353
  it("should not publish if there's any other kind of error when checking deployment source", async () => {
7376
7354
  writeWorkerSource();
7377
7355
  writeWranglerToml();
@@ -7481,6 +7459,83 @@ export default{
7481
7459
  `);
7482
7460
  });
7483
7461
 
7462
+ it("should support queue consumer concurrency with a max concurrency specified", async () => {
7463
+ writeWranglerToml({
7464
+ queues: {
7465
+ consumers: [
7466
+ {
7467
+ queue: "queue1",
7468
+ dead_letter_queue: "myDLQ",
7469
+ max_batch_size: 5,
7470
+ max_batch_timeout: 3,
7471
+ max_retries: 10,
7472
+ max_concurrency: 5,
7473
+ },
7474
+ ],
7475
+ },
7476
+ });
7477
+ await fs.promises.writeFile("index.js", `export default {};`);
7478
+ mockSubDomainRequest();
7479
+ mockUploadWorkerRequest();
7480
+ mockGetQueue("queue1");
7481
+ mockPutQueueConsumer("queue1", "test-name", {
7482
+ dead_letter_queue: "myDLQ",
7483
+ settings: {
7484
+ batch_size: 5,
7485
+ max_retries: 10,
7486
+ max_wait_time_ms: 3000,
7487
+ max_concurrency: 5,
7488
+ },
7489
+ });
7490
+ await runWrangler("publish index.js");
7491
+ expect(std.out).toMatchInlineSnapshot(`
7492
+ "Total Upload: xx KiB / gzip: xx KiB
7493
+ Uploaded test-name (TIMINGS)
7494
+ Published test-name (TIMINGS)
7495
+ https://test-name.test-sub-domain.workers.dev
7496
+ Consumer for queue1
7497
+ Current Deployment ID: Galaxy-Class"
7498
+ `);
7499
+ });
7500
+
7501
+ it("should support queue consumer concurrency with a null max concurrency", async () => {
7502
+ writeWranglerToml({
7503
+ queues: {
7504
+ consumers: [
7505
+ {
7506
+ queue: "queue1",
7507
+ dead_letter_queue: "myDLQ",
7508
+ max_batch_size: 5,
7509
+ max_batch_timeout: 3,
7510
+ max_retries: 10,
7511
+ max_concurrency: null,
7512
+ },
7513
+ ],
7514
+ },
7515
+ });
7516
+ await fs.promises.writeFile("index.js", `export default {};`);
7517
+ mockSubDomainRequest();
7518
+ mockUploadWorkerRequest();
7519
+ mockGetQueue("queue1");
7520
+ mockPutQueueConsumer("queue1", "test-name", {
7521
+ dead_letter_queue: "myDLQ",
7522
+ settings: {
7523
+ batch_size: 5,
7524
+ max_retries: 10,
7525
+ max_wait_time_ms: 3000,
7526
+ },
7527
+ });
7528
+ await runWrangler("publish index.js");
7529
+ expect(std.out).toMatchInlineSnapshot(`
7530
+ "Total Upload: xx KiB / gzip: xx KiB
7531
+ Uploaded test-name (TIMINGS)
7532
+ Published test-name (TIMINGS)
7533
+ https://test-name.test-sub-domain.workers.dev
7534
+ Consumer for queue1
7535
+ Current Deployment ID: Galaxy-Class"
7536
+ `);
7537
+ });
7538
+
7484
7539
  it("consumer should error when a queue doesn't exist", async () => {
7485
7540
  writeWranglerToml({
7486
7541
  queues: {
@@ -7609,6 +7664,33 @@ export default{
7609
7664
  `);
7610
7665
  expect(std.err).toMatchInlineSnapshot(`""`);
7611
7666
  });
7667
+
7668
+ it("should send keepVars when `keep_vars = true`", async () => {
7669
+ process.env = {
7670
+ CLOUDFLARE_API_TOKEN: "hunter2",
7671
+ CLOUDFLARE_ACCOUNT_ID: "some-account-id",
7672
+ };
7673
+ setIsTTY(false);
7674
+ writeWranglerToml({
7675
+ keep_vars: true,
7676
+ });
7677
+ writeWorkerSource();
7678
+ mockSubDomainRequest();
7679
+ mockUploadWorkerRequest({ keepVars: true });
7680
+ mockOAuthServerCallback();
7681
+ mockGetMemberships([]);
7682
+
7683
+ await runWrangler("publish index.js");
7684
+
7685
+ expect(std.out).toMatchInlineSnapshot(`
7686
+ "Total Upload: xx KiB / gzip: xx KiB
7687
+ Uploaded test-name (TIMINGS)
7688
+ Published test-name (TIMINGS)
7689
+ https://test-name.test-sub-domain.workers.dev
7690
+ Current Deployment ID: Galaxy-Class"
7691
+ `);
7692
+ expect(std.err).toMatchInlineSnapshot(`""`);
7693
+ });
7612
7694
  });
7613
7695
  });
7614
7696
 
@@ -7630,7 +7712,7 @@ function mockDeploymentsListRequest() {
7630
7712
  }
7631
7713
 
7632
7714
  function mockLastDeploymentRequest() {
7633
- msw.use(...mswSuccessLastDeployment);
7715
+ msw.use(...mswSuccessDeploymentScriptMetadata);
7634
7716
  }
7635
7717
 
7636
7718
  /** Create a mock handler for the request to upload a worker script. */
@@ -7648,7 +7730,6 @@ function mockUploadWorkerRequest(
7648
7730
  expectedUnsafeMetaData?: Record<string, string>;
7649
7731
  env?: string;
7650
7732
  legacyEnv?: boolean;
7651
- sendScriptIds?: boolean;
7652
7733
  keepVars?: boolean;
7653
7734
  tag?: string;
7654
7735
  } = {}
@@ -7666,7 +7747,6 @@ function mockUploadWorkerRequest(
7666
7747
  legacyEnv = false,
7667
7748
  expectedMigrations,
7668
7749
  expectedUnsafeMetaData,
7669
- sendScriptIds,
7670
7750
  keepVars,
7671
7751
  } = options;
7672
7752
  if (env && !legacyEnv) {
@@ -7748,12 +7828,11 @@ function mockUploadWorkerRequest(
7748
7828
  ctx.json(
7749
7829
  createFetchResult({
7750
7830
  available_on_subdomain,
7751
- ...(sendScriptIds && {
7752
- id: "abc12345",
7753
- etag: "etag98765",
7754
- pipeline_hash: "hash9999",
7755
- tag: "sample-tag",
7756
- }),
7831
+ id: "abc12345",
7832
+ etag: "etag98765",
7833
+ pipeline_hash: "hash9999",
7834
+ tag: "sample-tag",
7835
+ deployment_id: "Galaxy-Class",
7757
7836
  })
7758
7837
  )
7759
7838
  );
@@ -351,7 +351,8 @@ describe("wrangler", () => {
351
351
  --batch-size Maximum number of messages per batch [number]
352
352
  --batch-timeout Maximum number of seconds to wait to fill a batch with messages [number]
353
353
  --message-retries Maximum number of retries for each message [number]
354
- --dead-letter-queue Queue to send messages that failed to be consumed [string]"
354
+ --dead-letter-queue Queue to send messages that failed to be consumed [string]
355
+ --max-concurrency The maximum number of concurrent consumer Worker invocations. Must be a positive integer [number]"
355
356
  `);
356
357
  });
357
358
 
@@ -363,6 +364,7 @@ describe("wrangler", () => {
363
364
  batch_size: undefined,
364
365
  max_retries: undefined,
365
366
  max_wait_time_ms: undefined,
367
+ max_concurrency: undefined,
366
368
  },
367
369
  dead_letter_queue: undefined,
368
370
  };
@@ -382,13 +384,14 @@ describe("wrangler", () => {
382
384
  batch_size: 20,
383
385
  max_retries: 3,
384
386
  max_wait_time_ms: 10 * 1000,
387
+ max_concurrency: 3,
385
388
  },
386
389
  dead_letter_queue: "myDLQ",
387
390
  };
388
391
  mockPostRequest("testQueue", expectedBody);
389
392
 
390
393
  await runWrangler(
391
- "queues consumer add testQueue testScript --env myEnv --batch-size 20 --batch-timeout 10 --message-retries 3 --dead-letter-queue myDLQ"
394
+ "queues consumer add testQueue testScript --env myEnv --batch-size 20 --batch-timeout 10 --message-retries 3 --max-concurrency 3 --dead-letter-queue myDLQ"
392
395
  );
393
396
  expect(std.out).toMatchInlineSnapshot(`
394
397
  "Adding consumer to queue testQueue.
@@ -0,0 +1,220 @@
1
+ import { mkdir, writeFile } from "fs/promises";
2
+ import path from "path";
3
+ import dedent from "ts-dedent";
4
+ import traverseModuleGraph from "../traverse-module-graph";
5
+ import { runInTempDir } from "./helpers/run-in-tmp";
6
+ import type { ConfigModuleRuleType } from "../config";
7
+
8
+ /*
9
+ * This file contains inline comments with the word "javascript"
10
+ * This signals to a compatible editor extension that the template string
11
+ * contents should be syntax-highlighted as JavaScript. One such extension
12
+ * is zjcompt.es6-string-javascript, but there are others.
13
+ */
14
+
15
+ describe("traverse module graph", () => {
16
+ runInTempDir();
17
+
18
+ it("should not detect JS without module rules", async () => {
19
+ await writeFile(
20
+ "./index.js",
21
+ dedent/* javascript */ `
22
+ import { HELLO } from "./other.js"
23
+ export default {
24
+ async fetch(request) {
25
+ return new Response(HELLO)
26
+ }
27
+ }
28
+ `
29
+ );
30
+ await writeFile(
31
+ "./other.js",
32
+ dedent/* javascript */ `
33
+ export const HELLO = "WORLD"
34
+ `
35
+ );
36
+
37
+ const bundle = await traverseModuleGraph(
38
+ {
39
+ file: path.join(process.cwd(), "./index.js"),
40
+ directory: process.cwd(),
41
+ format: "modules",
42
+ moduleRoot: process.cwd(),
43
+ },
44
+ []
45
+ );
46
+
47
+ expect(bundle.modules).toStrictEqual([]);
48
+ });
49
+
50
+ it.each([
51
+ ["ESModule", "esm"],
52
+ ["CommonJS", "commonjs"],
53
+ ])("should detect JS as %s", async (type, format) => {
54
+ await writeFile(
55
+ "./index.js",
56
+ dedent/* javascript */ `
57
+ import { HELLO } from "./other.js"
58
+ export default {
59
+ async fetch(request) {
60
+ return new Response(HELLO)
61
+ }
62
+ }
63
+ `
64
+ );
65
+ await writeFile(
66
+ "./other.js",
67
+ dedent/* javascript */ `
68
+ export const HELLO = "WORLD"
69
+ `
70
+ );
71
+
72
+ const bundle = await traverseModuleGraph(
73
+ {
74
+ file: path.join(process.cwd(), "./index.js"),
75
+ directory: process.cwd(),
76
+ format: "modules",
77
+ moduleRoot: process.cwd(),
78
+ },
79
+ [{ type: type as ConfigModuleRuleType, globs: ["**/*.js"] }]
80
+ );
81
+
82
+ expect(bundle.modules[0].type).toStrictEqual(format);
83
+ });
84
+
85
+ it("should not resolve JS outside the module root", async () => {
86
+ await mkdir("./src/nested", { recursive: true });
87
+ await writeFile(
88
+ "./src/nested/index.js",
89
+ dedent/* javascript */ `
90
+ import { HELLO } from "../other.js"
91
+ export default {
92
+ async fetch(request) {
93
+ return new Response(HELLO)
94
+ }
95
+ }
96
+ `
97
+ );
98
+ await writeFile(
99
+ "./src/other.js",
100
+ dedent/* javascript */ `
101
+ export const HELLO = "WORLD"
102
+ `
103
+ );
104
+
105
+ const bundle = await traverseModuleGraph(
106
+ {
107
+ file: path.join(process.cwd(), "./src/nested/index.js"),
108
+ directory: path.join(process.cwd(), "./src/nested"),
109
+ format: "modules",
110
+ // The default module root is dirname(file)
111
+ moduleRoot: path.join(process.cwd(), "./src/nested"),
112
+ },
113
+ [{ type: "ESModule", globs: ["**/*.js"] }]
114
+ );
115
+
116
+ expect(bundle.modules).toStrictEqual([]);
117
+ });
118
+
119
+ it("should resolve JS with module root", async () => {
120
+ await mkdir("./src/nested", { recursive: true });
121
+ await writeFile(
122
+ "./src/nested/index.js",
123
+ dedent/* javascript */ `
124
+ import { HELLO } from "../other.js"
125
+ export default {
126
+ async fetch(request) {
127
+ return new Response(HELLO)
128
+ }
129
+ }
130
+ `
131
+ );
132
+ await writeFile(
133
+ "./src/other.js",
134
+ dedent/* javascript */ `
135
+ export const HELLO = "WORLD"
136
+ `
137
+ );
138
+
139
+ const bundle = await traverseModuleGraph(
140
+ {
141
+ file: path.join(process.cwd(), "./src/nested/index.js"),
142
+ directory: path.join(process.cwd(), "./src/nested"),
143
+ format: "modules",
144
+ // The default module root is dirname(file)
145
+ moduleRoot: path.join(process.cwd(), "./src"),
146
+ },
147
+ [{ type: "ESModule", globs: ["**/*.js"] }]
148
+ );
149
+
150
+ expect(bundle.modules[0].name).toStrictEqual("other.js");
151
+ });
152
+
153
+ it("should ignore files not matched by glob", async () => {
154
+ await mkdir("./src/nested", { recursive: true });
155
+ await writeFile(
156
+ "./src/nested/index.js",
157
+ dedent/* javascript */ `
158
+ import { HELLO } from "../other.js"
159
+ export default {
160
+ async fetch(request) {
161
+ return new Response(HELLO)
162
+ }
163
+ }
164
+ `
165
+ );
166
+ await writeFile(
167
+ "./src/other.js",
168
+ dedent/* javascript */ `
169
+ export const HELLO = "WORLD"
170
+ `
171
+ );
172
+
173
+ const bundle = await traverseModuleGraph(
174
+ {
175
+ file: path.join(process.cwd(), "./src/nested/index.js"),
176
+ directory: path.join(process.cwd(), "./src/nested"),
177
+ format: "modules",
178
+ // The default module root is dirname(file)
179
+ moduleRoot: path.join(process.cwd(), "./src"),
180
+ },
181
+ [{ type: "ESModule", globs: ["**/*.mjs"] }]
182
+ );
183
+
184
+ expect(bundle.modules.length).toStrictEqual(0);
185
+ });
186
+
187
+ it("should resolve files that match the default rules", async () => {
188
+ await mkdir("./src", { recursive: true });
189
+ await writeFile(
190
+ "./src/index.js",
191
+ dedent/* javascript */ `
192
+ import HELLO from "../other.txt"
193
+ export default {
194
+ async fetch(request) {
195
+ return new Response(HELLO)
196
+ }
197
+ }
198
+ `
199
+ );
200
+ await writeFile(
201
+ "./src/other.txt",
202
+ dedent/* javascript */ `
203
+ export const HELLO = "WORLD"
204
+ `
205
+ );
206
+
207
+ const bundle = await traverseModuleGraph(
208
+ {
209
+ file: path.join(process.cwd(), "./src/index.js"),
210
+ directory: path.join(process.cwd(), "./src"),
211
+ format: "modules",
212
+ // The default module root is dirname(file)
213
+ moduleRoot: path.join(process.cwd(), "./src"),
214
+ },
215
+ []
216
+ );
217
+
218
+ expect(bundle.modules[0].name).toStrictEqual("other.txt");
219
+ });
220
+ });