wrangler 2.0.23 → 2.0.26

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 (80) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +235 -47
  4. package/package.json +11 -6
  5. package/src/__tests__/configuration.test.ts +89 -17
  6. package/src/__tests__/dev.test.tsx +29 -4
  7. package/src/__tests__/generate.test.ts +93 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +87 -2
  9. package/src/__tests__/index.test.ts +10 -27
  10. package/src/__tests__/init.test.ts +537 -359
  11. package/src/__tests__/jest.setup.ts +34 -1
  12. package/src/__tests__/kv.test.ts +2 -2
  13. package/src/__tests__/metrics.test.ts +5 -0
  14. package/src/__tests__/pages.test.ts +14 -0
  15. package/src/__tests__/publish.test.ts +497 -254
  16. package/src/__tests__/r2.test.ts +173 -71
  17. package/src/__tests__/tail.test.ts +112 -42
  18. package/src/__tests__/user.test.ts +1 -0
  19. package/src/__tests__/validate-dev-props.test.ts +56 -0
  20. package/src/__tests__/whoami.test.tsx +60 -1
  21. package/src/api/dev.ts +7 -0
  22. package/src/bundle.ts +279 -44
  23. package/src/cfetch/internal.ts +73 -2
  24. package/src/config/config.ts +8 -3
  25. package/src/config/environment.ts +40 -8
  26. package/src/config/index.ts +13 -0
  27. package/src/config/validation.ts +102 -8
  28. package/src/create-worker-upload-form.ts +25 -0
  29. package/src/dev/dev.tsx +121 -28
  30. package/src/dev/local.tsx +88 -14
  31. package/src/dev/remote.tsx +39 -8
  32. package/src/dev/use-esbuild.ts +28 -0
  33. package/src/dev/validate-dev-props.ts +31 -0
  34. package/src/dev-registry.tsx +160 -0
  35. package/src/dev.tsx +107 -80
  36. package/src/generate.ts +112 -14
  37. package/src/index.tsx +212 -4
  38. package/src/init.ts +111 -38
  39. package/src/inspect.ts +90 -5
  40. package/src/metrics/index.ts +1 -0
  41. package/src/metrics/metrics-dispatcher.ts +1 -0
  42. package/src/metrics/metrics-usage-headers.ts +24 -0
  43. package/src/metrics/send-event.ts +2 -2
  44. package/src/miniflare-cli/assets.ts +27 -16
  45. package/src/miniflare-cli/index.ts +124 -2
  46. package/src/module-collection.ts +3 -3
  47. package/src/pages/build.tsx +75 -41
  48. package/src/pages/constants.ts +5 -0
  49. package/src/pages/deployments.tsx +10 -10
  50. package/src/pages/dev.tsx +177 -52
  51. package/src/pages/errors.ts +22 -0
  52. package/src/pages/functions/buildPlugin.ts +4 -0
  53. package/src/pages/functions/buildWorker.ts +4 -0
  54. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  55. package/src/pages/functions/routes-consolidation.ts +73 -0
  56. package/src/pages/functions/routes-transformation.test.ts +271 -0
  57. package/src/pages/functions/routes-transformation.ts +122 -0
  58. package/src/pages/functions.tsx +96 -0
  59. package/src/pages/index.tsx +65 -55
  60. package/src/pages/projects.tsx +9 -3
  61. package/src/pages/publish.tsx +76 -23
  62. package/src/pages/types.ts +9 -0
  63. package/src/pages/upload.tsx +38 -21
  64. package/src/publish.ts +126 -112
  65. package/src/r2.ts +81 -0
  66. package/src/tail/filters.ts +3 -1
  67. package/src/tail/index.ts +15 -2
  68. package/src/tail/printing.ts +43 -3
  69. package/src/user/user.tsx +20 -2
  70. package/src/whoami.tsx +79 -1
  71. package/src/worker.ts +12 -0
  72. package/templates/first-party-worker-module-facade.ts +18 -0
  73. package/templates/format-dev-errors.ts +32 -0
  74. package/templates/pages-template-plugin.ts +16 -4
  75. package/templates/pages-template-worker.ts +16 -5
  76. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  77. package/templates/service-bindings-module-facade.js +54 -0
  78. package/templates/service-bindings-sw-facade.js +42 -0
  79. package/wrangler-dist/cli.d.ts +7 -0
  80. package/wrangler-dist/cli.js +40851 -15332
@@ -1,32 +1,29 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { dirname, join } from "node:path";
4
+ import { FatalError } from "../errors";
4
5
  import { logger } from "../logger";
5
6
  import * as metrics from "../metrics";
6
7
  import { toUrlPath } from "../paths";
7
8
  import { isInPagesCI } from "./constants";
9
+ import {
10
+ EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR,
11
+ FunctionsNoRoutesError,
12
+ getFunctionsNoRoutesWarning,
13
+ } from "./errors";
8
14
  import { buildPlugin } from "./functions/buildPlugin";
9
15
  import { buildWorker } from "./functions/buildWorker";
10
16
  import { generateConfigFromFileTree } from "./functions/filepath-routing";
11
17
  import { writeRoutesModule } from "./functions/routes";
18
+ import { convertRoutesToRoutesJSONSpec } from "./functions/routes-transformation";
12
19
  import { pagesBetaWarning, RUNNING_BUILDERS } from "./utils";
13
20
  import type { Config } from "./functions/routes";
14
- import type { ArgumentsCamelCase, Argv } from "yargs";
21
+ import type { YargsOptionsToInterface } from "./types";
22
+ import type { Argv } from "yargs";
15
23
 
16
- type PagesBuildArgs = {
17
- directory: string;
18
- outfile: string;
19
- "output-config-path"?: string;
20
- minify: boolean;
21
- sourcemap: boolean;
22
- "fallback-service": string;
23
- watch: boolean;
24
- plugin: boolean;
25
- "build-output-directory"?: string;
26
- "node-compat": boolean;
27
- };
24
+ type PagesBuildArgs = YargsOptionsToInterface<typeof Options>;
28
25
 
29
- export function Options(yargs: Argv): Argv<PagesBuildArgs> {
26
+ export function Options(yargs: Argv) {
30
27
  return yargs
31
28
  .positional("directory", {
32
29
  type: "string",
@@ -43,6 +40,10 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
43
40
  type: "string",
44
41
  description: "The location for the output config file",
45
42
  },
43
+ "output-routes-path": {
44
+ type: "string",
45
+ description: "The location for the output _routes.json file",
46
+ },
46
47
  minify: {
47
48
  type: "boolean",
48
49
  default: false,
@@ -87,15 +88,16 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
87
88
  export const Handler = async ({
88
89
  directory,
89
90
  outfile,
90
- "output-config-path": outputConfigPath,
91
+ outputConfigPath,
92
+ outputRoutesPath: routesOutputPath,
91
93
  minify,
92
94
  sourcemap,
93
95
  fallbackService,
94
96
  watch,
95
97
  plugin,
96
- "build-output-directory": buildOutputDirectory,
97
- "node-compat": nodeCompat,
98
- }: ArgumentsCamelCase<PagesBuildArgs>) => {
98
+ buildOutputDirectory,
99
+ nodeCompat,
100
+ }: PagesBuildArgs) => {
99
101
  if (!isInPagesCI) {
100
102
  // Beta message for `wrangler pages <commands>` usage
101
103
  logger.log(pagesBetaWarning);
@@ -108,22 +110,37 @@ export const Handler = async ({
108
110
  }
109
111
 
110
112
  buildOutputDirectory ??= dirname(outfile);
111
-
112
- await buildFunctions({
113
- outfile,
114
- outputConfigPath,
115
- functionsDirectory: directory,
116
- minify,
117
- sourcemap,
118
- fallbackService,
119
- watch,
120
- plugin,
121
- buildOutputDirectory,
122
- nodeCompat,
123
- });
113
+ try {
114
+ await buildFunctions({
115
+ outfile,
116
+ outputConfigPath,
117
+ functionsDirectory: directory,
118
+ minify,
119
+ sourcemap,
120
+ fallbackService,
121
+ watch,
122
+ plugin,
123
+ buildOutputDirectory,
124
+ nodeCompat,
125
+ routesOutputPath,
126
+ });
127
+ } catch (e) {
128
+ if (e instanceof FunctionsNoRoutesError) {
129
+ throw new FatalError(
130
+ getFunctionsNoRoutesWarning(directory),
131
+ EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR
132
+ );
133
+ } else {
134
+ throw e;
135
+ }
136
+ }
124
137
  await metrics.sendMetricsEvent("build pages functions");
125
138
  };
126
139
 
140
+ /**
141
+ * Builds a Functions worker based on the functions directory, with filepath and handler based routing.
142
+ * @throws FunctionsNoRoutesError when there are no routes found in the functions directory
143
+ */
127
144
  export async function buildFunctions({
128
145
  outfile,
129
146
  outputConfigPath,
@@ -135,19 +152,25 @@ export async function buildFunctions({
135
152
  onEnd,
136
153
  plugin = false,
137
154
  buildOutputDirectory,
155
+ routesOutputPath,
138
156
  nodeCompat,
139
- }: {
140
- outfile: string;
141
- outputConfigPath?: string;
157
+ }: Partial<
158
+ Pick<
159
+ PagesBuildArgs,
160
+ | "outputConfigPath"
161
+ | "minify"
162
+ | "sourcemap"
163
+ | "fallbackService"
164
+ | "watch"
165
+ | "plugin"
166
+ | "buildOutputDirectory"
167
+ | "nodeCompat"
168
+ >
169
+ > & {
142
170
  functionsDirectory: string;
143
- minify?: boolean;
144
- sourcemap?: boolean;
145
- fallbackService?: string;
146
- watch?: boolean;
147
171
  onEnd?: () => void;
148
- plugin?: boolean;
149
- buildOutputDirectory?: string;
150
- nodeCompat?: boolean;
172
+ outfile: Required<PagesBuildArgs>["outfile"];
173
+ routesOutputPath?: PagesBuildArgs["outputRoutesPath"];
151
174
  }) {
152
175
  RUNNING_BUILDERS.forEach(
153
176
  (runningBuilder) => runningBuilder.stop && runningBuilder.stop()
@@ -161,6 +184,17 @@ export async function buildFunctions({
161
184
  baseURL,
162
185
  });
163
186
 
187
+ if (!config.routes || config.routes.length === 0) {
188
+ throw new FunctionsNoRoutesError(
189
+ `Failed to find any routes while compiling Functions in: ${functionsDirectory}`
190
+ );
191
+ }
192
+
193
+ if (routesOutputPath) {
194
+ const routesJSON = convertRoutesToRoutesJSONSpec(config.routes);
195
+ writeFileSync(routesOutputPath, JSON.stringify(routesJSON, null, 2));
196
+ }
197
+
164
198
  if (outputConfigPath) {
165
199
  writeFileSync(
166
200
  outputConfigPath,
@@ -3,5 +3,10 @@ export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
3
3
  export const MAX_BUCKET_FILE_COUNT = 5000;
4
4
  export const BULK_UPLOAD_CONCURRENCY = 3;
5
5
  export const MAX_UPLOAD_ATTEMPTS = 5;
6
+ export const MAX_CHECK_MISSING_ATTEMPTS = 5;
6
7
  export const SECONDS_TO_WAIT_FOR_PROXY = 5;
7
8
  export const isInPagesCI = !!process.env.CF_PAGES;
9
+ /** The max number of rules in _routes.json */
10
+ export const MAX_FUNCTIONS_ROUTES_RULES = 100;
11
+ export const MAX_FUNCTIONS_ROUTES_RULE_LENGTH = 100;
12
+ export const ROUTES_SPEC_VERSION = 1;
@@ -11,14 +11,16 @@ import { requireAuth } from "../user";
11
11
  import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
12
12
  import { listProjects } from "./projects";
13
13
  import { pagesBetaWarning } from "./utils";
14
- import type { Deployment, PagesConfigCache } from "./types";
15
- import type { ArgumentsCamelCase, Argv } from "yargs";
14
+ import type {
15
+ Deployment,
16
+ PagesConfigCache,
17
+ YargsOptionsToInterface,
18
+ } from "./types";
19
+ import type { Argv } from "yargs";
16
20
 
17
- type ListArgs = {
18
- "project-name"?: string;
19
- };
21
+ type ListArgs = YargsOptionsToInterface<typeof ListOptions>;
20
22
 
21
- export function ListOptions(yargs: Argv): Argv<ListArgs> {
23
+ export function ListOptions(yargs: Argv) {
22
24
  return yargs
23
25
  .options({
24
26
  "project-name": {
@@ -30,9 +32,7 @@ export function ListOptions(yargs: Argv): Argv<ListArgs> {
30
32
  .epilogue(pagesBetaWarning);
31
33
  }
32
34
 
33
- export async function ListHandler({
34
- projectName,
35
- }: ArgumentsCamelCase<ListArgs>) {
35
+ export async function ListHandler({ projectName }: ListArgs) {
36
36
  const config = getConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME);
37
37
  const accountId = await requireAuth(config);
38
38
 
@@ -102,5 +102,5 @@ export async function ListHandler({
102
102
  patchConsole: false,
103
103
  });
104
104
  unmount();
105
- await metrics.sendMetricsEvent("list pages projects deployments");
105
+ await metrics.sendMetricsEvent("list pages deployments");
106
106
  }
package/src/pages/dev.tsx CHANGED
@@ -1,34 +1,29 @@
1
1
  import { execSync, spawn } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
- import { tmpdir } from "node:os";
3
+ import { homedir, tmpdir } from "node:os";
4
4
  import { join, resolve } from "node:path";
5
5
  import { watch } from "chokidar";
6
+ import { build as workerJsBuild } from "esbuild";
6
7
  import { unstable_dev } from "../api";
7
8
  import { FatalError } from "../errors";
8
9
  import { logger } from "../logger";
9
10
  import * as metrics from "../metrics";
10
11
  import { buildFunctions } from "./build";
11
12
  import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
13
+ import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
12
14
  import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils";
13
- import type { ArgumentsCamelCase, Argv } from "yargs";
14
-
15
- type PagesDevArgs = {
16
- directory?: string;
17
- command?: string;
18
- local: boolean;
19
- port: number;
20
- proxy?: number;
21
- "script-path": string;
22
- binding?: (string | number)[];
23
- kv?: (string | number)[];
24
- do?: (string | number)[];
25
- "live-reload": boolean;
26
- "local-protocol"?: "https" | "http";
27
- "experimental-enable-local-persistence": boolean;
28
- "node-compat": boolean;
29
- };
15
+ import type { AdditionalDevProps } from "../dev";
16
+ import type { YargsOptionsToInterface } from "./types";
17
+ import type { Plugin } from "esbuild";
18
+ import type { Argv } from "yargs";
19
+
20
+ const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
21
+ /^(?<binding>[^=]+)=(?<className>[^@\s]+)(@(?<scriptName>.*)$)?$/
22
+ );
23
+
24
+ type PagesDevArgs = YargsOptionsToInterface<typeof Options>;
30
25
 
31
- export function Options(yargs: Argv): Argv<PagesDevArgs> {
26
+ export function Options(yargs: Argv) {
32
27
  return yargs
33
28
  .positional("directory", {
34
29
  type: "string",
@@ -46,11 +41,41 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
46
41
  default: true,
47
42
  description: "Run on my machine",
48
43
  },
44
+ "compatibility-date": {
45
+ describe: "Date to use for compatibility checks",
46
+ type: "string",
47
+ },
48
+ "compatibility-flags": {
49
+ describe: "Flags to use for compatibility checks",
50
+ alias: "compatibility-flag",
51
+ type: "string",
52
+ array: true,
53
+ },
54
+
55
+ // TODO
56
+ // For now, all Pages projects are set to 2021-11-02. We're adding compat date soon, and we can then adopt `wrangler dev`'s `default: true`.
57
+ // However, it looks like it isn't actually connected up properly in `wrangler dev` at the moment, hence commenting this out for now.
58
+
59
+ // latest: {
60
+ // describe: "Use the latest version of the worker runtime",
61
+ // type: "boolean",
62
+ // default: false,
63
+ // },
64
+
65
+ ip: {
66
+ type: "string",
67
+ default: "0.0.0.0",
68
+ description: "The IP address to listen on",
69
+ },
49
70
  port: {
50
71
  type: "number",
51
72
  default: 8788,
52
73
  description: "The port to listen on (serve from)",
53
74
  },
75
+ "inspector-port": {
76
+ type: "number",
77
+ describe: "Port for devtools to connect to",
78
+ },
54
79
  proxy: {
55
80
  type: "number",
56
81
  description: "The port to proxy (where the static assets are served)",
@@ -68,14 +93,18 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
68
93
  },
69
94
  kv: {
70
95
  type: "array",
71
- description: "KV namespace to bind",
96
+ description: "KV namespace to bind (--kv KV_BINDING)",
72
97
  alias: "k",
73
98
  },
74
99
  do: {
75
100
  type: "array",
76
- description: "Durable Object to bind (NAME=CLASS)",
101
+ description: "Durable Object to bind (--do NAME=CLASS)",
77
102
  alias: "o",
78
103
  },
104
+ r2: {
105
+ type: "array",
106
+ description: "R2 bucket to bind (--r2 R2_BINDING)",
107
+ },
79
108
  "live-reload": {
80
109
  type: "boolean",
81
110
  default: false,
@@ -108,19 +137,24 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
108
137
  export const Handler = async ({
109
138
  local,
110
139
  directory,
140
+ "compatibility-date": compatibilityDate = "2021-11-02",
141
+ "compatibility-flags": compatibilityFlags,
142
+ ip,
111
143
  port,
144
+ "inspector-port": inspectorPort,
112
145
  proxy: requestedProxyPort,
113
146
  "script-path": singleWorkerScriptPath,
114
147
  binding: bindings = [],
115
148
  kv: kvs = [],
116
149
  do: durableObjects = [],
150
+ r2: r2s = [],
117
151
  "live-reload": liveReload,
118
152
  "local-protocol": localProtocol,
119
153
  "experimental-enable-local-persistence": experimentalEnableLocalPersistence,
120
154
  "node-compat": nodeCompat,
121
155
  config: config,
122
156
  _: [_pages, _dev, ...remaining],
123
- }: ArgumentsCamelCase<PagesDevArgs>) => {
157
+ }: PagesDevArgs) => {
124
158
  // Beta message for `wrangler pages <commands>` usage
125
159
  logger.log(pagesBetaWarning);
126
160
 
@@ -133,13 +167,18 @@ export const Handler = async ({
133
167
  }
134
168
 
135
169
  const functionsDirectory = "./functions";
136
- const usingFunctions = existsSync(functionsDirectory);
170
+ let usingFunctions = existsSync(functionsDirectory);
137
171
 
138
172
  const command = remaining;
139
173
 
140
174
  let proxyPort: number | undefined;
141
175
 
142
- if (directory === undefined) {
176
+ if (directory !== undefined && command.length > 0) {
177
+ throw new FatalError(
178
+ "Specify either a directory OR a proxy command, not both.",
179
+ 1
180
+ );
181
+ } else if (directory === undefined) {
143
182
  proxyPort = await spawnProxyProcess({
144
183
  port: requestedProxyPort,
145
184
  command,
@@ -154,8 +193,9 @@ export const Handler = async ({
154
193
  (promiseResolve) => (scriptReadyResolve = promiseResolve)
155
194
  );
156
195
 
157
- let scriptPath: string;
196
+ let scriptPath = "";
158
197
 
198
+ // Try to use Functions
159
199
  if (usingFunctions) {
160
200
  const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
161
201
  scriptPath = outfile;
@@ -167,36 +207,63 @@ export const Handler = async ({
167
207
  }
168
208
 
169
209
  logger.log(`Compiling worker to "${outfile}"...`);
170
-
210
+ const onEnd = () => scriptReadyResolve();
171
211
  try {
172
212
  await buildFunctions({
173
213
  outfile,
174
214
  functionsDirectory,
175
215
  sourcemap: true,
176
216
  watch: true,
177
- onEnd: () => scriptReadyResolve(),
217
+ onEnd,
178
218
  buildOutputDirectory: directory,
179
219
  nodeCompat,
180
220
  });
181
221
  await metrics.sendMetricsEvent("build pages functions");
182
- } catch {}
183
222
 
184
- watch([functionsDirectory], {
185
- persistent: true,
186
- ignoreInitial: true,
187
- }).on("all", async () => {
188
- await buildFunctions({
189
- outfile,
190
- functionsDirectory,
191
- sourcemap: true,
192
- watch: true,
193
- onEnd: () => scriptReadyResolve(),
194
- buildOutputDirectory: directory,
195
- nodeCompat,
223
+ // If Functions found routes, continue using Functions
224
+ watch([functionsDirectory], {
225
+ persistent: true,
226
+ ignoreInitial: true,
227
+ }).on("all", async () => {
228
+ try {
229
+ await buildFunctions({
230
+ outfile,
231
+ functionsDirectory,
232
+ sourcemap: true,
233
+ watch: true,
234
+ onEnd,
235
+ buildOutputDirectory: directory,
236
+ nodeCompat,
237
+ });
238
+ await metrics.sendMetricsEvent("build pages functions");
239
+ } catch (e) {
240
+ if (e instanceof FunctionsNoRoutesError) {
241
+ logger.warn(
242
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
243
+ );
244
+ } else {
245
+ throw e;
246
+ }
247
+ }
196
248
  });
197
- await metrics.sendMetricsEvent("build pages functions");
198
- });
199
- } else {
249
+ } catch (e) {
250
+ // If there are no Functions, then Pages will only serve assets.
251
+ if (e instanceof FunctionsNoRoutesError) {
252
+ logger.warn(
253
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
254
+ );
255
+ // Resolve anyway and run without Functions
256
+ onEnd();
257
+ // Turn off Functions
258
+ usingFunctions = false;
259
+ } else {
260
+ throw e;
261
+ }
262
+ }
263
+ }
264
+ // Depending on the result of building Functions, we may not actually be using
265
+ // Functions even if the directory exists.
266
+ if (!usingFunctions) {
200
267
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
201
268
  scriptReadyResolve!();
202
269
 
@@ -208,20 +275,48 @@ export const Handler = async ({
208
275
  if (!existsSync(scriptPath)) {
209
276
  logger.log("No functions. Shimming...");
210
277
  scriptPath = resolve(__dirname, "../templates/pages-shim.ts");
278
+ } else {
279
+ const runBuild = async () => {
280
+ try {
281
+ await workerJsBuild({
282
+ entryPoints: [scriptPath],
283
+ write: false,
284
+ plugins: [blockWorkerJsImports],
285
+ });
286
+ } catch {}
287
+ };
288
+ await runBuild();
289
+ watch([scriptPath], {
290
+ persistent: true,
291
+ ignoreInitial: true,
292
+ }).on("all", async () => {
293
+ await runBuild();
294
+ });
211
295
  }
212
296
  }
213
297
 
214
298
  await scriptReadyPromise;
215
299
 
300
+ if (scriptPath === "") {
301
+ // Failed to get a script with or without Functions,
302
+ // something really bad must have happend.
303
+ throw new FatalError(
304
+ "Failed to start wrangler pages dev due to an unknown error",
305
+ 1
306
+ );
307
+ }
308
+
216
309
  const { stop, waitUntilExit } = await unstable_dev(
217
310
  scriptPath,
218
311
  {
312
+ ip,
219
313
  port,
314
+ inspectorPort,
220
315
  watch: true,
221
316
  localProtocol,
222
317
  liveReload,
223
-
224
- compatibilityDate: "2021-11-02",
318
+ compatibilityDate,
319
+ compatibilityFlags,
225
320
  nodeCompat,
226
321
  vars: Object.fromEntries(
227
322
  bindings
@@ -232,12 +327,29 @@ export const Handler = async ({
232
327
  binding: val.toString(),
233
328
  id: "",
234
329
  })),
235
- durableObjects: durableObjects.map((durableObject) => {
236
- const [name, class_name] = durableObject.toString().split("=");
237
- return {
238
- name,
239
- class_name,
240
- };
330
+ durableObjects: durableObjects
331
+ .map((durableObject) => {
332
+ const { binding, className, scriptName } =
333
+ DURABLE_OBJECTS_BINDING_REGEXP.exec(durableObject.toString())
334
+ ?.groups || {};
335
+
336
+ if (!binding || !className) {
337
+ logger.warn(
338
+ "Could not parse Durable Object binding:",
339
+ durableObject.toString()
340
+ );
341
+ return;
342
+ }
343
+
344
+ return {
345
+ name: binding,
346
+ class_name: className,
347
+ script_name: scriptName,
348
+ };
349
+ })
350
+ .filter(Boolean) as AdditionalDevProps["durableObjects"],
351
+ r2: r2s.map((binding) => {
352
+ return { binding: binding.toString(), bucket_name: "" };
241
353
  }),
242
354
 
243
355
  enablePagesAssetsServiceBinding: {
@@ -314,7 +426,8 @@ function getPort(pid: number) {
314
426
  let command: string, regExp: RegExp;
315
427
 
316
428
  if (isWindows()) {
317
- command = "\\windows\\system32\\netstat.exe -nao";
429
+ const drive = homedir().split(":\\")[0];
430
+ command = drive + ":\\windows\\system32\\netstat.exe -nao";
318
431
  regExp = new RegExp(`TCP\\s+.*:(\\d+)\\s+.*:\\d+\\s+LISTENING\\s+${pid}`);
319
432
  } else {
320
433
  command = "lsof -nPi";
@@ -407,3 +520,15 @@ async function spawnProxyProcess({
407
520
 
408
521
  return port;
409
522
  }
523
+
524
+ const blockWorkerJsImports: Plugin = {
525
+ name: "block-worker-js-imports",
526
+ setup(build) {
527
+ build.onResolve({ filter: /.*/g }, (_args) => {
528
+ logger.error(
529
+ `_worker.js is importing from another file. This will throw an error if deployed.\nYou should bundle your Worker or remove the import if it is unused.`
530
+ );
531
+ return null;
532
+ });
533
+ },
534
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pages error when no routes are found in the functions directory
3
+ */
4
+ export class FunctionsNoRoutesError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ }
8
+ }
9
+ /**
10
+ * Exit code for `pages functions build` when no routes are found.
11
+ */
12
+ export const EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR = 156;
13
+
14
+ /** Warning message for when buildFunctions throws FunctionsNoRoutesError */
15
+ export function getFunctionsNoRoutesWarning(
16
+ functionsDirectory: string,
17
+ suffix?: string
18
+ ) {
19
+ return `No routes found when building Functions directory: ${functionsDirectory}${
20
+ suffix ? " - " + suffix : ""
21
+ }`;
22
+ }
@@ -29,6 +29,10 @@ export function buildPlugin({
29
29
  bundle: true,
30
30
  format: "esm",
31
31
  target: "esnext",
32
+ loader: {
33
+ ".html": "text",
34
+ ".txt": "text",
35
+ },
32
36
  outfile,
33
37
  minify,
34
38
  sourcemap,
@@ -34,6 +34,10 @@ export function buildWorker({
34
34
  bundle: true,
35
35
  format: "esm",
36
36
  target: "esnext",
37
+ loader: {
38
+ ".html": "text",
39
+ ".txt": "text",
40
+ },
37
41
  outfile,
38
42
  minify,
39
43
  sourcemap,