wrangler 2.0.11 → 2.0.12

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 (55) hide show
  1. package/kv-asset-handler.js +1 -0
  2. package/package.json +3 -1
  3. package/src/__tests__/configuration.test.ts +221 -142
  4. package/src/__tests__/dev.test.tsx +27 -0
  5. package/src/__tests__/index.test.ts +2 -1
  6. package/src/__tests__/kv.test.ts +23 -2
  7. package/src/__tests__/publish.test.ts +173 -54
  8. package/src/bundle.ts +9 -5
  9. package/src/config/environment.ts +1 -1
  10. package/src/config/validation.ts +22 -13
  11. package/src/dev/dev.tsx +29 -45
  12. package/src/dev/local.tsx +10 -7
  13. package/src/dev/remote.tsx +4 -1
  14. package/src/dev/use-esbuild.ts +1 -4
  15. package/src/index.tsx +236 -180
  16. package/src/kv.ts +1 -1
  17. package/src/parse.ts +21 -1
  18. package/src/proxy.ts +19 -6
  19. package/src/publish.ts +6 -1
  20. package/src/sites.tsx +1 -3
  21. package/templates/static-asset-facade.js +1 -5
  22. package/wrangler-dist/cli.js +73310 -73243
  23. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  24. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  25. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  26. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  27. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  28. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  29. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  30. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  31. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  32. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  33. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  34. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  35. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  36. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  37. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  38. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  39. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  40. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  41. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  42. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  43. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  44. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  45. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  46. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  47. package/vendor/wrangler-mime/LICENSE +0 -21
  48. package/vendor/wrangler-mime/Mime.js +0 -97
  49. package/vendor/wrangler-mime/README.md +0 -187
  50. package/vendor/wrangler-mime/cli.js +0 -46
  51. package/vendor/wrangler-mime/index.js +0 -4
  52. package/vendor/wrangler-mime/lite.js +0 -4
  53. package/vendor/wrangler-mime/package.json +0 -52
  54. package/vendor/wrangler-mime/types/other.js +0 -1
  55. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -1404,6 +1404,69 @@ addEventListener('fetch', event => {});`
1404
1404
  expect(std.err).toMatchInlineSnapshot(`""`);
1405
1405
  });
1406
1406
 
1407
+ it("should upload all the files in the directory specified by `--experimental-public`", async () => {
1408
+ const assets = [
1409
+ { filePath: "file-1.txt", content: "Content of file-1" },
1410
+ { filePath: "file-2.txt", content: "Content of file-2" },
1411
+ ];
1412
+ const kvNamespace = {
1413
+ title: "__test-name-workers_sites_assets",
1414
+ id: "__test-name-workers_sites_assets-id",
1415
+ };
1416
+ writeWranglerToml({
1417
+ main: "./index.js",
1418
+ });
1419
+ writeWorkerSource();
1420
+ writeAssets(assets);
1421
+ mockUploadWorkerRequest({
1422
+ expectedMainModule: "stdin.js",
1423
+ });
1424
+ mockSubDomainRequest();
1425
+ mockListKVNamespacesRequest(kvNamespace);
1426
+ mockKeyListRequest(kvNamespace.id, []);
1427
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets);
1428
+ await runWrangler("publish --experimental-public assets");
1429
+
1430
+ expect(std.out).toMatchInlineSnapshot(`
1431
+ "Reading file-1.txt...
1432
+ Uploading as file-1.2ca234f380.txt...
1433
+ Reading file-2.txt...
1434
+ Uploading as file-2.5938485188.txt...
1435
+ ↗️ Done syncing assets
1436
+ Uploaded test-name (TIMINGS)
1437
+ Published test-name (TIMINGS)
1438
+ test-name.test-sub-domain.workers.dev"
1439
+ `);
1440
+ expect(std.err).toMatchInlineSnapshot(`""`);
1441
+ });
1442
+
1443
+ it("should error if --experimental-public and --site are used together", async () => {
1444
+ writeWranglerToml({
1445
+ main: "./index.js",
1446
+ });
1447
+ writeWorkerSource();
1448
+ await expect(
1449
+ runWrangler("publish --experimental-public abc --site xyz")
1450
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1451
+ `"Cannot use --experimental-public and a Site configuration together."`
1452
+ );
1453
+ });
1454
+
1455
+ it("should error if --experimental-public and config.site are used together", async () => {
1456
+ writeWranglerToml({
1457
+ main: "./index.js",
1458
+ site: {
1459
+ bucket: "xyz",
1460
+ },
1461
+ });
1462
+ writeWorkerSource();
1463
+ await expect(
1464
+ runWrangler("publish --experimental-public abc")
1465
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1466
+ `"Cannot use --experimental-public and a Site configuration together."`
1467
+ );
1468
+ });
1469
+
1407
1470
  it("should not contain backslash for assets with nested directories", async () => {
1408
1471
  const assets = [
1409
1472
  { filePath: "subdir/file-1.txt", content: "Content of file-1" },
@@ -2098,34 +2161,25 @@ addEventListener('fetch', event => {});`
2098
2161
  mockSubDomainRequest();
2099
2162
  mockListKVNamespacesRequest(kvNamespace);
2100
2163
  mockKeyListRequest(kvNamespace.id, []);
2164
+ const requests = mockUploadAssetsToKVRequest(kvNamespace.id);
2165
+
2166
+ await runWrangler("publish");
2101
2167
 
2102
2168
  // We expect this to be uploaded in 3 batches
2103
2169
  // The first batch has 11 files (88mb)
2170
+ expect(requests[0].uploads.length).toEqual(11);
2104
2171
  // The next batch has 5 files (93mb)
2172
+ expect(requests[1].uploads.length).toEqual(5);
2105
2173
  // And the last one has 4 files (98mb)
2174
+ expect(requests[2].uploads.length).toEqual(4);
2106
2175
 
2107
- // Since the bulk upload api endpoint stays the same
2108
- // We're going to have to clear the mock as soon as it's resolved
2109
- // And immediately add a mock for another one
2110
- // Welcome to a callback pyramid in 2022
2111
-
2112
- const removeFirstBulkUploadMockFn = mockUploadAssetsToKVRequest(
2113
- kvNamespace.id,
2114
- assets.slice(0, 11),
2115
- () => {
2116
- removeFirstBulkUploadMockFn();
2117
- const removeSecondBulkUploadMockFn = mockUploadAssetsToKVRequest(
2118
- kvNamespace.id,
2119
- assets.slice(11, 16),
2120
- () => {
2121
- removeSecondBulkUploadMockFn();
2122
- mockUploadAssetsToKVRequest(kvNamespace.id, assets.slice(16, 20));
2123
- }
2124
- );
2176
+ let assetIndex = 0;
2177
+ for (const request of requests) {
2178
+ for (const upload of request.uploads) {
2179
+ checkAssetUpload(assets[assetIndex], upload);
2180
+ assetIndex++;
2125
2181
  }
2126
- );
2127
-
2128
- await runWrangler("publish");
2182
+ }
2129
2183
 
2130
2184
  expect(std).toMatchInlineSnapshot(`
2131
2185
  Object {
@@ -2276,6 +2330,59 @@ addEventListener('fetch', event => {});`
2276
2330
  `);
2277
2331
  expect(std.err).toMatchInlineSnapshot(`""`);
2278
2332
  });
2333
+
2334
+ it("should generate an asset manifest with keys relative to site.bucket", async () => {
2335
+ const assets = [
2336
+ { filePath: "file-1.txt", content: "Content of file-1" },
2337
+ { filePath: "file-2.txt", content: "Content of file-2" },
2338
+ ];
2339
+ const kvNamespace = {
2340
+ title: "__test-name-workers_sites_assets",
2341
+ id: "__test-name-workers_sites_assets-id",
2342
+ };
2343
+
2344
+ writeWranglerToml({
2345
+ main: "./src/index.js",
2346
+ site: {
2347
+ bucket: "assets",
2348
+ },
2349
+ });
2350
+ writeWorkerSource({ basePath: "src", type: "esm" });
2351
+ writeAssets(assets);
2352
+ mockUploadWorkerRequest({
2353
+ expectedBindings: [
2354
+ {
2355
+ name: "__STATIC_CONTENT",
2356
+ namespace_id: "__test-name-workers_sites_assets-id",
2357
+ type: "kv_namespace",
2358
+ },
2359
+ ],
2360
+ expectedModules: {
2361
+ __STATIC_CONTENT_MANIFEST:
2362
+ '{"file-1.txt":"file-1.2ca234f380.txt","file-2.txt":"file-2.5938485188.txt"}',
2363
+ },
2364
+ });
2365
+ mockSubDomainRequest();
2366
+ mockListKVNamespacesRequest(kvNamespace);
2367
+ mockKeyListRequest(kvNamespace.id, []);
2368
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets);
2369
+
2370
+ process.chdir("./src");
2371
+ await runWrangler("publish");
2372
+ process.chdir("../");
2373
+
2374
+ expect(std.out).toMatchInlineSnapshot(`
2375
+ "Reading file-1.txt...
2376
+ Uploading as file-1.2ca234f380.txt...
2377
+ Reading file-2.txt...
2378
+ Uploading as file-2.5938485188.txt...
2379
+ ↗️ Done syncing assets
2380
+ Uploaded test-name (TIMINGS)
2381
+ Published test-name (TIMINGS)
2382
+ test-name.test-sub-domain.workers.dev"
2383
+ `);
2384
+ expect(std.err).toMatchInlineSnapshot(`""`);
2385
+ });
2279
2386
  });
2280
2387
 
2281
2388
  describe("workers_dev setting", () => {
@@ -5258,6 +5365,7 @@ function mockUploadWorkerRequest(
5258
5365
  options: {
5259
5366
  available_on_subdomain?: boolean;
5260
5367
  expectedEntry?: string;
5368
+ expectedMainModule?: string;
5261
5369
  expectedType?: "esm" | "sw";
5262
5370
  expectedBindings?: unknown;
5263
5371
  expectedModules?: Record<string, string>;
@@ -5271,6 +5379,7 @@ function mockUploadWorkerRequest(
5271
5379
  const {
5272
5380
  available_on_subdomain = true,
5273
5381
  expectedEntry,
5382
+ expectedMainModule = "index.js",
5274
5383
  expectedType = "esm",
5275
5384
  expectedBindings,
5276
5385
  expectedModules = {},
@@ -5294,6 +5403,7 @@ function mockUploadWorkerRequest(
5294
5403
  expect(envName).toEqual(env);
5295
5404
  }
5296
5405
  expect(queryParams.get("include_subdomain_availability")).toEqual("true");
5406
+ expect(queryParams.get("excludeScript")).toEqual("true");
5297
5407
  const formBody = body as FormData;
5298
5408
  if (expectedEntry !== undefined) {
5299
5409
  expect(await (formBody.get("index.js") as File).text()).toMatch(
@@ -5304,7 +5414,7 @@ function mockUploadWorkerRequest(
5304
5414
  formBody.get("metadata") as string
5305
5415
  ) as WorkerMetadata;
5306
5416
  if (expectedType === "esm") {
5307
- expect(metadata.main_module).toEqual("index.js");
5417
+ expect(metadata.main_module).toEqual(expectedMainModule);
5308
5418
  } else {
5309
5419
  expect(metadata.body_part).toEqual("index.js");
5310
5420
  }
@@ -5565,51 +5675,60 @@ function mockListKVNamespacesRequest(...namespaces: KVNamespaceInfo[]) {
5565
5675
  );
5566
5676
  }
5567
5677
 
5678
+ interface ExpectedAsset {
5679
+ filePath: string;
5680
+ content: string;
5681
+ expiration?: number;
5682
+ expiration_ttl?: number;
5683
+ }
5684
+ interface StaticAssetUpload {
5685
+ key: string;
5686
+ base64: boolean;
5687
+ value: string;
5688
+ expiration: number | undefined;
5689
+ expiration_ttl: number | undefined;
5690
+ }
5691
+
5568
5692
  /** Create a mock handler for the request that tries to do a bulk upload of assets to a KV namespace. */
5569
5693
  function mockUploadAssetsToKVRequest(
5570
5694
  expectedNamespaceId: string,
5571
- assets: {
5572
- filePath: string;
5573
- content: string;
5574
- expiration?: number;
5575
- expiration_ttl?: number;
5576
- }[],
5577
- onUpload?: () => void
5695
+ assets?: ExpectedAsset[]
5578
5696
  ) {
5579
- return setMockResponse(
5697
+ const requests: {
5698
+ uploads: StaticAssetUpload[];
5699
+ }[] = [];
5700
+ setMockResponse(
5580
5701
  "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
5581
5702
  "PUT",
5582
5703
  ([_url, accountId, namespaceId], { body }) => {
5583
5704
  expect(accountId).toEqual("some-account-id");
5584
5705
  expect(namespaceId).toEqual(expectedNamespaceId);
5585
5706
  const uploads = JSON.parse(body as string);
5586
- expect(assets.length).toEqual(uploads.length);
5587
- for (let i = 0; i < uploads.length; i++) {
5588
- const asset = assets[i];
5589
- const upload = uploads[i];
5590
- // The asset key consists of:
5591
- // - the basename of the filepath
5592
- // - some hash value
5593
- // - the extension
5594
- const keyMatcher = new RegExp(
5595
- "^" +
5596
- asset.filePath
5597
- .replace(/(\.[^.]+)$/, ".[a-z0-9]+$1")
5598
- .replace(/\./g, "\\.")
5599
- );
5600
- expect(upload.key).toMatch(keyMatcher);
5601
- // The asset value is base64 encoded.
5602
- expect(upload.base64).toBe(true);
5603
- expect(Buffer.from(upload.value, "base64").toString()).toEqual(
5604
- asset.content
5605
- );
5606
- expect(upload.expiration).toEqual(asset.expiration);
5607
- expect(upload.expiration_ttl).toEqual(asset.expiration_ttl);
5707
+ if (assets) {
5708
+ expect(assets.length).toEqual(uploads.length);
5709
+ for (let i = 0; i < uploads.length; i++) {
5710
+ checkAssetUpload(assets[i], uploads[i]);
5711
+ }
5712
+ } else {
5713
+ requests.push({ uploads });
5608
5714
  }
5609
- onUpload?.();
5610
- return null;
5611
5715
  }
5612
5716
  );
5717
+ return requests;
5718
+ }
5719
+
5720
+ function checkAssetUpload(asset: ExpectedAsset, upload: StaticAssetUpload) {
5721
+ // The asset key consists of: `<basename>.<hash>.<extension>`
5722
+ const keyMatcher = new RegExp(
5723
+ "^" +
5724
+ asset.filePath.replace(/(\.[^.]+)$/, ".[a-z0-9]+$1").replace(/\./g, "\\.")
5725
+ );
5726
+ expect(upload.key).toMatch(keyMatcher);
5727
+ // The asset value is base64 encoded.
5728
+ expect(upload.base64).toBe(true);
5729
+ expect(Buffer.from(upload.value, "base64").toString()).toEqual(asset.content);
5730
+ expect(upload.expiration).toEqual(asset.expiration);
5731
+ expect(upload.expiration_ttl).toEqual(asset.expiration_ttl);
5613
5732
  }
5614
5733
 
5615
5734
  /** Create a mock handler for thr request that does a bulk delete of unused assets */
package/src/bundle.ts CHANGED
@@ -154,9 +154,7 @@ export async function bundleWorker(
154
154
  };
155
155
  }
156
156
 
157
- type EntryPoint =
158
- | { stdin: esbuild.StdinOptions; nodePaths: string[] }
159
- | { entryPoints: string[] };
157
+ type EntryPoint = { stdin: esbuild.StdinOptions } | { entryPoints: string[] };
160
158
 
161
159
  /**
162
160
  * Create an object that describes the entry point for esbuild.
@@ -177,11 +175,17 @@ function getEntryPoint(
177
175
  path.join(__dirname, "../templates/static-asset-facade.js"),
178
176
  "utf8"
179
177
  )
180
- .replace("__ENTRY_POINT__", entryFile),
178
+ // on windows, escape backslashes in the path (`\`)
179
+ .replace("__ENTRY_POINT__", entryFile.replaceAll("\\", "\\\\"))
180
+ .replace(
181
+ "__KV_ASSET_HANDLER__",
182
+ path
183
+ .join(__dirname, "../kv-asset-handler.js")
184
+ .replaceAll("\\", "\\\\")
185
+ ),
181
186
  sourcefile: "static-asset-facade.js",
182
187
  resolveDir: path.dirname(entryFile),
183
188
  },
184
- nodePaths: [path.join(__dirname, "../vendor")],
185
189
  };
186
190
  } else {
187
191
  return { entryPoints: [entryFile] };
@@ -175,7 +175,7 @@ interface EnvironmentInheritable {
175
175
  /** The directory in which the command is executed. */
176
176
  cwd?: string;
177
177
  /** The directory to watch for changes while using wrangler dev, defaults to the current working directory */
178
- watch_dir?: string;
178
+ watch_dir?: string | string[];
179
179
  /**
180
180
  * Deprecated field previously used to configure the build and upload of the script.
181
181
  * @deprecated
@@ -227,19 +227,23 @@ function normalizeAndValidateBuild(
227
227
  rawBuild: Config["build"],
228
228
  configPath: string | undefined
229
229
  ): Config["build"] & { deprecatedUpload: DeprecatedUpload } {
230
- const { command, cwd, watch_dir, upload, ...rest } = rawBuild;
230
+ const { command, cwd, watch_dir = "./src", upload, ...rest } = rawBuild;
231
231
  const deprecatedUpload: DeprecatedUpload = { ...upload };
232
232
  validateAdditionalProperties(diagnostics, "build", Object.keys(rest), []);
233
233
 
234
234
  validateOptionalProperty(diagnostics, "build", "command", command, "string");
235
235
  validateOptionalProperty(diagnostics, "build", "cwd", cwd, "string");
236
- validateOptionalProperty(
237
- diagnostics,
238
- "build",
239
- "watch_dir",
240
- watch_dir,
241
- "string"
242
- );
236
+ if (Array.isArray(watch_dir)) {
237
+ validateTypedArray(diagnostics, "build.watch_dir", watch_dir, "string");
238
+ } else {
239
+ validateOptionalProperty(
240
+ diagnostics,
241
+ "build",
242
+ "watch_dir",
243
+ watch_dir,
244
+ "string"
245
+ );
246
+ }
243
247
 
244
248
  deprecated(
245
249
  diagnostics,
@@ -288,13 +292,18 @@ function normalizeAndValidateBuild(
288
292
  // - `configPath` will always be defined since `build` can only
289
293
  // be configured in `wrangler.toml`, but who knows, that may
290
294
  // change in the future, so we do a check anyway
291
- command
292
- ? configPath
293
- ? path.relative(
295
+ command && configPath
296
+ ? Array.isArray(watch_dir)
297
+ ? watch_dir.map((dir) =>
298
+ path.relative(
299
+ process.cwd(),
300
+ path.join(path.dirname(configPath), `${dir}`)
301
+ )
302
+ )
303
+ : path.relative(
294
304
  process.cwd(),
295
- path.join(path.dirname(configPath), watch_dir || "./src")
305
+ path.join(path.dirname(configPath), `${watch_dir}`)
296
306
  )
297
- : watch_dir || "./src"
298
307
  : watch_dir,
299
308
  cwd,
300
309
  deprecatedUpload,
package/src/dev/dev.tsx CHANGED
@@ -20,7 +20,6 @@ import type { Config } from "../config";
20
20
  import type { Entry } from "../entry";
21
21
  import type { AssetPaths } from "../sites";
22
22
  import type { CfWorkerInit } from "../worker";
23
- import type { EsbuildBundle } from "./use-esbuild";
24
23
 
25
24
  export type DevProps = {
26
25
  name?: string;
@@ -46,11 +45,7 @@ export type DevProps = {
46
45
  usageModel: "bundled" | "unbound" | undefined;
47
46
  minify: boolean | undefined;
48
47
  nodeCompat: boolean | undefined;
49
- build: {
50
- command?: string | undefined;
51
- cwd?: string | undefined;
52
- watch_dir?: string | undefined;
53
- };
48
+ build: Config["build"];
54
49
  env: string | undefined;
55
50
  legacyEnv: boolean;
56
51
  zone: string | undefined;
@@ -58,10 +53,6 @@ export type DevProps = {
58
53
  };
59
54
 
60
55
  export function DevImplementation(props: DevProps): JSX.Element {
61
- const directory = useTmpDir();
62
-
63
- useCustomBuild(props.entry, props.build);
64
-
65
56
  if (props.public && props.entry.format === "service-worker") {
66
57
  throw new Error(
67
58
  "You cannot use the service-worker format with a `public` directory."
@@ -86,37 +77,16 @@ export function DevImplementation(props: DevProps): JSX.Element {
86
77
  );
87
78
  }
88
79
 
89
- const bundle = useEsbuild({
90
- entry: props.entry,
91
- destination: directory,
92
- staticRoot: props.public,
93
- jsxFactory: props.jsxFactory,
94
- rules: props.rules,
95
- jsxFragment: props.jsxFragment,
96
- serveAssetsFromWorker: !!props.public,
97
- tsconfig: props.tsconfig,
98
- minify: props.minify,
99
- nodeCompat: props.nodeCompat,
100
- });
101
-
102
80
  // only load the UI if we're running in a supported environment
103
81
  const { isRawModeSupported } = useStdin();
104
82
  return isRawModeSupported ? (
105
- <InteractiveDevSession {...props} bundle={bundle} />
83
+ <InteractiveDevSession {...props} />
106
84
  ) : (
107
- <DevSession
108
- {...props}
109
- bundle={bundle}
110
- local={props.initialMode === "local"}
111
- />
85
+ <DevSession {...props} local={props.initialMode === "local"} />
112
86
  );
113
87
  }
114
88
 
115
- type InteractiveDevSessionProps = DevProps & {
116
- bundle: EsbuildBundle | undefined;
117
- };
118
-
119
- function InteractiveDevSession(props: InteractiveDevSessionProps) {
89
+ function InteractiveDevSession(props: DevProps) {
120
90
  const toggles = useHotkeys(
121
91
  {
122
92
  local: props.initialMode === "local",
@@ -149,15 +119,36 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
149
119
  );
150
120
  }
151
121
 
152
- type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
122
+ type DevSessionProps = DevProps & {
123
+ local: boolean;
124
+ };
153
125
 
154
126
  function DevSession(props: DevSessionProps) {
127
+ useCustomBuild(props.entry, props.build);
128
+
129
+ const directory = useTmpDir();
130
+
131
+ const bundle = useEsbuild({
132
+ entry: props.entry,
133
+ destination: directory,
134
+ staticRoot: props.public,
135
+ jsxFactory: props.jsxFactory,
136
+ rules: props.rules,
137
+ jsxFragment: props.jsxFragment,
138
+ // In dev for remote mode, we serve --experimental-assets from the local proxy before we send the request to the worker.
139
+ serveAssetsFromWorker: !!props.public && !!props.local,
140
+ tsconfig: props.tsconfig,
141
+ minify: props.minify,
142
+ nodeCompat: props.nodeCompat,
143
+ });
144
+
155
145
  return props.local ? (
156
146
  <Local
157
147
  name={props.name}
158
- bundle={props.bundle}
148
+ bundle={bundle}
159
149
  format={props.entry.format}
160
150
  compatibilityDate={props.compatibilityDate}
151
+ localProtocol={props.localProtocol}
161
152
  compatibilityFlags={props.compatibilityFlags}
162
153
  bindings={props.bindings}
163
154
  assetPaths={props.assetPaths}
@@ -172,7 +163,7 @@ function DevSession(props: DevSessionProps) {
172
163
  ) : (
173
164
  <Remote
174
165
  name={props.name}
175
- bundle={props.bundle}
166
+ bundle={bundle}
176
167
  format={props.entry.format}
177
168
  accountId={props.accountId}
178
169
  bindings={props.bindings}
@@ -220,14 +211,7 @@ function useTmpDir(): string | undefined {
220
211
  return directory?.name;
221
212
  }
222
213
 
223
- function useCustomBuild(
224
- expectedEntry: Entry,
225
- build: {
226
- command?: string | undefined;
227
- cwd?: string | undefined;
228
- watch_dir?: string | undefined;
229
- }
230
- ): void {
214
+ function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
231
215
  useEffect(() => {
232
216
  if (!build.command) return;
233
217
  let watcher: ReturnType<typeof watch> | undefined;
package/src/dev/local.tsx CHANGED
@@ -21,6 +21,7 @@ interface LocalProps {
21
21
  compatibilityDate: string;
22
22
  compatibilityFlags: string[] | undefined;
23
23
  bindings: CfWorkerInit["bindings"];
24
+ localProtocol: "http" | "https";
24
25
  assetPaths: AssetPaths | undefined;
25
26
  public: string | undefined;
26
27
  port: number;
@@ -54,6 +55,7 @@ function useLocalWorker({
54
55
  rules,
55
56
  enableLocalPersistence,
56
57
  ip,
58
+ localProtocol,
57
59
  crons,
58
60
  }: LocalProps) {
59
61
  // TODO: pass vars via command line
@@ -82,11 +84,6 @@ function useLocalWorker({
82
84
  abortSignal: abortController.signal,
83
85
  });
84
86
 
85
- if (publicDirectory) {
86
- throw new Error(
87
- '⎔ A "public" folder is not yet supported in local mode.'
88
- );
89
- }
90
87
  if (bindings.services && bindings.services.length > 0) {
91
88
  throw new Error(
92
89
  "⎔ Service bindings are not yet supported in local mode."
@@ -164,6 +161,7 @@ function useLocalWorker({
164
161
  name: workerName,
165
162
  port,
166
163
  scriptPath,
164
+ https: localProtocol === "https",
167
165
  host: ip,
168
166
  modules: format === "modules",
169
167
  modulesRules: (rules || [])
@@ -251,12 +249,16 @@ function useLocalWorker({
251
249
  process.stdout.write(data);
252
250
  });
253
251
 
252
+ // parse the node inspector url (which may be received in chunks) from stderr
253
+ let stderrData = "";
254
254
  local.current.stderr?.on("data", (data: Buffer) => {
255
+ stderrData += data.toString();
255
256
  const matches =
256
- /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
257
- data.toString()
257
+ /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)[\r|\n]/.exec(
258
+ stderrData
258
259
  );
259
260
  if (matches) {
261
+ local.current?.stderr?.removeAllListeners("data");
260
262
  setInspectorUrl(matches[1]);
261
263
  }
262
264
  });
@@ -298,6 +300,7 @@ function useLocalWorker({
298
300
  workerName,
299
301
  format,
300
302
  port,
303
+ localProtocol,
301
304
  ip,
302
305
  bindings.durable_objects?.bindings,
303
306
  bindings.kv_namespaces,
@@ -40,6 +40,7 @@ export function Remote(props: {
40
40
  accountId: props.accountId,
41
41
  bindings: props.bindings,
42
42
  assetPaths: props.assetPaths,
43
+ public: props.public,
43
44
  port: props.port,
44
45
  compatibilityDate: props.compatibilityDate,
45
46
  compatibilityFlags: props.compatibilityFlags,
@@ -74,6 +75,7 @@ export function useWorker(props: {
74
75
  accountId: string | undefined;
75
76
  bindings: CfWorkerInit["bindings"];
76
77
  assetPaths: AssetPaths | undefined;
78
+ public: string | undefined;
77
79
  port: number;
78
80
  compatibilityDate: string | undefined;
79
81
  compatibilityFlags: string[] | undefined;
@@ -131,7 +133,7 @@ export function useWorker(props: {
131
133
  // include it in the kv namespace name regardless (since there's no
132
134
  // concept of service environments for kv namespaces yet).
133
135
  name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
134
- assetPaths,
136
+ props.public ? undefined : assetPaths,
135
137
  true,
136
138
  false
137
139
  ); // TODO: cancellable?
@@ -223,6 +225,7 @@ export function useWorker(props: {
223
225
  accountId,
224
226
  port,
225
227
  assetPaths,
228
+ props.public,
226
229
  compatibilityDate,
227
230
  compatibilityFlags,
228
231
  usageModel,
@@ -14,7 +14,6 @@ export type EsbuildBundle = {
14
14
  entry: Entry;
15
15
  type: "esm" | "commonjs";
16
16
  modules: CfModule[];
17
- serveAssetsFromWorker: boolean;
18
17
  };
19
18
 
20
19
  export function useEsbuild({
@@ -67,8 +66,7 @@ export function useEsbuild({
67
66
 
68
67
  const { resolvedEntryPointPath, bundleType, modules, stop } =
69
68
  await bundleWorker(entry, destination, {
70
- // In dev, we serve assets from the local proxy before we send the request to the worker.
71
- serveAssetsFromWorker: false,
69
+ serveAssetsFromWorker,
72
70
  jsxFactory,
73
71
  jsxFragment,
74
72
  rules,
@@ -87,7 +85,6 @@ export function useEsbuild({
87
85
  path: resolvedEntryPointPath,
88
86
  type: bundleType,
89
87
  modules,
90
- serveAssetsFromWorker,
91
88
  });
92
89
  }
93
90