wrangler 2.1.4 → 2.1.6
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/miniflare-dist/index.mjs +4 -1
- package/package.json +1 -1
- package/src/__tests__/api-dev.test.ts +3 -3
- package/src/__tests__/api-devregistry.test.js +56 -0
- package/src/__tests__/configuration.test.ts +3 -0
- package/src/__tests__/dev.test.tsx +39 -1
- package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
- package/src/__tests__/helpers/{hello-world-worker.js → worker-scripts/hello-world-worker.js} +0 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
- package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
- package/src/__tests__/init.test.ts +72 -0
- package/src/__tests__/middleware.scheduled.test.ts +135 -0
- package/src/__tests__/middleware.test.ts +703 -745
- package/src/__tests__/pages.test.ts +35 -40
- package/src/__tests__/publish.test.ts +57 -1
- package/src/api/dev.ts +2 -0
- package/src/bundle.ts +14 -16
- package/src/config/config.ts +12 -0
- package/src/config/validation.ts +9 -0
- package/src/create-worker-upload-form.ts +3 -2
- package/src/dev/dev.tsx +12 -21
- package/src/dev/local.tsx +1 -0
- package/src/dev/remote.tsx +38 -50
- package/src/dev/start-server.ts +43 -8
- package/src/dev/use-esbuild.ts +4 -0
- package/src/dev-registry.tsx +30 -0
- package/src/dev.tsx +21 -3
- package/src/index.tsx +8 -1
- package/src/init.ts +3 -1
- package/src/inspect.ts +26 -26
- package/src/miniflare-cli/assets.ts +8 -1
- package/src/pages/constants.ts +2 -1
- package/src/pages/dev.tsx +133 -10
- package/src/pages/errors.ts +48 -4
- package/src/pages/functions/routes-transformation.ts +1 -14
- package/src/pages/functions/routes-validation.test.ts +403 -0
- package/src/pages/functions/routes-validation.ts +202 -0
- package/src/pages/functions.tsx +4 -18
- package/src/pages/publish.tsx +6 -22
- package/src/publish.ts +3 -1
- package/src/worker.ts +1 -1
- package/templates/middleware/middleware-scheduled.ts +2 -1
- package/templates/pages-dev-pipeline.ts +35 -0
- package/wrangler-dist/cli.d.ts +2 -0
- package/wrangler-dist/cli.js +611 -353
package/src/dev/start-server.ts
CHANGED
|
@@ -2,10 +2,16 @@ import { fork } from "node:child_process";
|
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { writeFile } from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import * as util from "node:util";
|
|
5
6
|
import onExit from "signal-exit";
|
|
6
7
|
import tmp from "tmp-promise";
|
|
7
8
|
import { bundleWorker } from "../bundle";
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getBoundRegisteredWorkers,
|
|
11
|
+
registerWorker,
|
|
12
|
+
startWorkerRegistry,
|
|
13
|
+
stopWorkerRegistry,
|
|
14
|
+
} from "../dev-registry";
|
|
9
15
|
import { runCustomBuild } from "../entry";
|
|
10
16
|
import { logger } from "../logger";
|
|
11
17
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
@@ -17,6 +23,7 @@ import {
|
|
|
17
23
|
import { validateDevProps } from "./validate-dev-props";
|
|
18
24
|
|
|
19
25
|
import type { Config } from "../config";
|
|
26
|
+
import type { WorkerRegistry } from "../dev-registry";
|
|
20
27
|
import type { Entry } from "../entry";
|
|
21
28
|
import type { DevProps, DirectorySyncResult } from "./dev";
|
|
22
29
|
import type { LocalProps } from "./local";
|
|
@@ -30,6 +37,7 @@ export async function startDevServer(
|
|
|
30
37
|
}
|
|
31
38
|
) {
|
|
32
39
|
try {
|
|
40
|
+
let workerDefinitions: WorkerRegistry = {};
|
|
33
41
|
validateDevProps(props);
|
|
34
42
|
|
|
35
43
|
if (props.build.command) {
|
|
@@ -48,6 +56,21 @@ export async function startDevServer(
|
|
|
48
56
|
throw new Error("Failed to create temporary directory.");
|
|
49
57
|
}
|
|
50
58
|
|
|
59
|
+
//start the worker registry
|
|
60
|
+
startWorkerRegistry().catch((err) => {
|
|
61
|
+
logger.error("failed to start worker registry", err);
|
|
62
|
+
});
|
|
63
|
+
if (props.local) {
|
|
64
|
+
const boundRegisteredWorkers = await getBoundRegisteredWorkers({
|
|
65
|
+
services: props.bindings.services,
|
|
66
|
+
durableObjects: props.bindings.durable_objects,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!util.isDeepStrictEqual(boundRegisteredWorkers, workerDefinitions)) {
|
|
70
|
+
workerDefinitions = boundRegisteredWorkers || {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
//implement a react-free version of useEsbuild
|
|
52
75
|
const bundle = await runEsbuild({
|
|
53
76
|
entry: props.entry,
|
|
@@ -64,7 +87,10 @@ export async function startDevServer(
|
|
|
64
87
|
define: props.define,
|
|
65
88
|
noBundle: props.noBundle,
|
|
66
89
|
assets: props.assetsConfig,
|
|
90
|
+
workerDefinitions,
|
|
67
91
|
services: props.bindings.services,
|
|
92
|
+
firstPartyWorkerDevFacade: props.firstPartyWorker,
|
|
93
|
+
testScheduled: props.testScheduled,
|
|
68
94
|
});
|
|
69
95
|
|
|
70
96
|
//run local now
|
|
@@ -90,13 +116,14 @@ export async function startDevServer(
|
|
|
90
116
|
inspect: props.inspect,
|
|
91
117
|
onReady: props.onReady,
|
|
92
118
|
enablePagesAssetsServiceBinding: props.enablePagesAssetsServiceBinding,
|
|
93
|
-
usageModel:
|
|
94
|
-
workerDefinitions
|
|
119
|
+
usageModel: props.usageModel,
|
|
120
|
+
workerDefinitions,
|
|
95
121
|
});
|
|
96
122
|
|
|
97
123
|
return {
|
|
98
124
|
stop: async () => {
|
|
99
125
|
stop();
|
|
126
|
+
await stopWorkerRegistry();
|
|
100
127
|
},
|
|
101
128
|
inspectorUrl,
|
|
102
129
|
};
|
|
@@ -129,6 +156,10 @@ async function runEsbuild({
|
|
|
129
156
|
nodeCompat,
|
|
130
157
|
define,
|
|
131
158
|
noBundle,
|
|
159
|
+
workerDefinitions,
|
|
160
|
+
services,
|
|
161
|
+
firstPartyWorkerDevFacade,
|
|
162
|
+
testScheduled,
|
|
132
163
|
}: {
|
|
133
164
|
entry: Entry;
|
|
134
165
|
destination: string | undefined;
|
|
@@ -143,6 +174,9 @@ async function runEsbuild({
|
|
|
143
174
|
minify: boolean | undefined;
|
|
144
175
|
nodeCompat: boolean | undefined;
|
|
145
176
|
noBundle: boolean;
|
|
177
|
+
workerDefinitions: WorkerRegistry;
|
|
178
|
+
firstPartyWorkerDevFacade: boolean | undefined;
|
|
179
|
+
testScheduled?: boolean;
|
|
146
180
|
}): Promise<EsbuildBundle | undefined> {
|
|
147
181
|
if (!destination) return;
|
|
148
182
|
|
|
@@ -174,10 +208,11 @@ async function runEsbuild({
|
|
|
174
208
|
// disable the cache in dev
|
|
175
209
|
bypassCache: true,
|
|
176
210
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
firstPartyWorkerDevFacade
|
|
211
|
+
workerDefinitions,
|
|
212
|
+
services,
|
|
213
|
+
firstPartyWorkerDevFacade,
|
|
180
214
|
targetConsumer: "dev", // We are starting a dev server
|
|
215
|
+
testScheduled,
|
|
181
216
|
});
|
|
182
217
|
|
|
183
218
|
return {
|
|
@@ -237,8 +272,8 @@ export async function startLocalServer({
|
|
|
237
272
|
}
|
|
238
273
|
|
|
239
274
|
if (bindings.services && bindings.services.length > 0) {
|
|
240
|
-
|
|
241
|
-
"⎔
|
|
275
|
+
logger.warn(
|
|
276
|
+
"⎔ Support for service bindings in local mode is experimental and may change."
|
|
242
277
|
);
|
|
243
278
|
}
|
|
244
279
|
|
package/src/dev/use-esbuild.ts
CHANGED
|
@@ -37,6 +37,7 @@ export function useEsbuild({
|
|
|
37
37
|
durableObjects,
|
|
38
38
|
firstPartyWorkerDevFacade,
|
|
39
39
|
targetConsumer,
|
|
40
|
+
testScheduled,
|
|
40
41
|
}: {
|
|
41
42
|
entry: Entry;
|
|
42
43
|
destination: string | undefined;
|
|
@@ -55,6 +56,7 @@ export function useEsbuild({
|
|
|
55
56
|
durableObjects: Config["durable_objects"];
|
|
56
57
|
firstPartyWorkerDevFacade: boolean | undefined;
|
|
57
58
|
targetConsumer: "dev" | "publish";
|
|
59
|
+
testScheduled: boolean;
|
|
58
60
|
}): EsbuildBundle | undefined {
|
|
59
61
|
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
60
62
|
const { exit } = useApp();
|
|
@@ -119,6 +121,7 @@ export function useEsbuild({
|
|
|
119
121
|
services,
|
|
120
122
|
firstPartyWorkerDevFacade,
|
|
121
123
|
targetConsumer,
|
|
124
|
+
testScheduled,
|
|
122
125
|
});
|
|
123
126
|
|
|
124
127
|
// Capture the `stop()` method to use as the `useEffect()` destructor.
|
|
@@ -177,6 +180,7 @@ export function useEsbuild({
|
|
|
177
180
|
workerDefinitions,
|
|
178
181
|
firstPartyWorkerDevFacade,
|
|
179
182
|
targetConsumer,
|
|
183
|
+
testScheduled,
|
|
180
184
|
]);
|
|
181
185
|
return bundle;
|
|
182
186
|
}
|
package/src/dev-registry.tsx
CHANGED
|
@@ -5,6 +5,8 @@ import express from "express";
|
|
|
5
5
|
import { createHttpTerminator } from "http-terminator";
|
|
6
6
|
import { fetch } from "undici";
|
|
7
7
|
import { logger } from "./logger";
|
|
8
|
+
|
|
9
|
+
import type { Config } from "./config";
|
|
8
10
|
import type { Server } from "http";
|
|
9
11
|
import type { HttpTerminator } from "http-terminator";
|
|
10
12
|
|
|
@@ -158,3 +160,31 @@ export async function getRegisteredWorkers(): Promise<
|
|
|
158
160
|
}
|
|
159
161
|
}
|
|
160
162
|
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* a function that takes your serviceNames and durableObjectNames and returns a
|
|
166
|
+
* list of the running workers that we're bound to
|
|
167
|
+
*/
|
|
168
|
+
export async function getBoundRegisteredWorkers({
|
|
169
|
+
services,
|
|
170
|
+
durableObjects,
|
|
171
|
+
}: {
|
|
172
|
+
services: Config["services"] | undefined;
|
|
173
|
+
durableObjects: Config["durable_objects"] | undefined;
|
|
174
|
+
}) {
|
|
175
|
+
const serviceNames = (services || []).map(
|
|
176
|
+
(serviceBinding) => serviceBinding.service
|
|
177
|
+
);
|
|
178
|
+
const durableObjectServices = (
|
|
179
|
+
durableObjects || { bindings: [] }
|
|
180
|
+
).bindings.map((durableObjectBinding) => durableObjectBinding.script_name);
|
|
181
|
+
|
|
182
|
+
const workerDefinitions = await getRegisteredWorkers();
|
|
183
|
+
const filteredWorkers = Object.fromEntries(
|
|
184
|
+
Object.entries(workerDefinitions || {}).filter(
|
|
185
|
+
([key, _value]) =>
|
|
186
|
+
serviceNames.includes(key) || durableObjectServices.includes(key)
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
return filteredWorkers;
|
|
190
|
+
}
|
package/src/dev.tsx
CHANGED
|
@@ -72,6 +72,7 @@ interface DevArgs {
|
|
|
72
72
|
logLevel?: "none" | "error" | "log" | "warn" | "debug";
|
|
73
73
|
logPrefix?: string;
|
|
74
74
|
showInteractiveDevSession?: boolean;
|
|
75
|
+
"test-scheduled"?: boolean;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export function devOptions(yargs: Argv): Argv<DevArgs> {
|
|
@@ -273,6 +274,17 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
|
|
|
273
274
|
describe: "Use legacy environments",
|
|
274
275
|
hidden: true,
|
|
275
276
|
})
|
|
277
|
+
.option("test-scheduled", {
|
|
278
|
+
describe: "Test scheduled events by visiting /__scheduled in browser",
|
|
279
|
+
type: "boolean",
|
|
280
|
+
default: false,
|
|
281
|
+
})
|
|
282
|
+
.option("log-level", {
|
|
283
|
+
// "none" will currently default to "error" for Wrangler Logger
|
|
284
|
+
choices: ["debug", "info", "log", "warn", "error", "none"] as const,
|
|
285
|
+
describe: "Specify logging level",
|
|
286
|
+
default: "log",
|
|
287
|
+
})
|
|
276
288
|
);
|
|
277
289
|
}
|
|
278
290
|
|
|
@@ -309,6 +321,7 @@ export type AdditionalDevProps = {
|
|
|
309
321
|
preview_bucket_name?: string;
|
|
310
322
|
}[];
|
|
311
323
|
};
|
|
324
|
+
|
|
312
325
|
type StartDevOptions = ArgumentsCamelCase<DevArgs> &
|
|
313
326
|
// These options can be passed in directly when called with the `wrangler.dev()` API.
|
|
314
327
|
// They aren't exposed as CLI arguments.
|
|
@@ -430,6 +443,7 @@ export async function startDev(args: StartDevOptions) {
|
|
|
430
443
|
enablePagesAssetsServiceBinding={args.enablePagesAssetsServiceBinding}
|
|
431
444
|
firstPartyWorker={configParam.first_party_worker}
|
|
432
445
|
sendMetrics={configParam.send_metrics}
|
|
446
|
+
testScheduled={args["test-scheduled"]}
|
|
433
447
|
/>
|
|
434
448
|
);
|
|
435
449
|
}
|
|
@@ -541,8 +555,9 @@ export async function startApiDev(args: StartDevOptions) {
|
|
|
541
555
|
forceLocal: args.forceLocal,
|
|
542
556
|
enablePagesAssetsServiceBinding: args.enablePagesAssetsServiceBinding,
|
|
543
557
|
local: true,
|
|
544
|
-
firstPartyWorker:
|
|
545
|
-
sendMetrics:
|
|
558
|
+
firstPartyWorker: configParam.first_party_worker,
|
|
559
|
+
sendMetrics: configParam.send_metrics,
|
|
560
|
+
testScheduled: args.testScheduled,
|
|
546
561
|
});
|
|
547
562
|
}
|
|
548
563
|
|
|
@@ -699,7 +714,10 @@ async function validateDevServerSettings(
|
|
|
699
714
|
: args.persist
|
|
700
715
|
? // If just flagged on, treat it as relative to wrangler.toml,
|
|
701
716
|
// if one can be found, otherwise cwd()
|
|
702
|
-
path.resolve(
|
|
717
|
+
path.resolve(
|
|
718
|
+
config.configPath ? path.dirname(config.configPath) : process.cwd(),
|
|
719
|
+
".wrangler/state"
|
|
720
|
+
)
|
|
703
721
|
: null;
|
|
704
722
|
|
|
705
723
|
const cliDefines =
|
package/src/index.tsx
CHANGED
|
@@ -527,6 +527,12 @@ function createCLIParser(argv: string[]) {
|
|
|
527
527
|
describe: "Don't actually publish",
|
|
528
528
|
type: "boolean",
|
|
529
529
|
})
|
|
530
|
+
.option("keep-vars", {
|
|
531
|
+
describe:
|
|
532
|
+
"Stop wrangler from deleting vars that are not present in the wrangler.toml\nBy default Wrangler will remove all vars and replace them with those found in the wrangler.toml configuration.\nIf your development approach is to modify vars after deployment via the dashboard you may wish to set this flag.",
|
|
533
|
+
default: false,
|
|
534
|
+
type: "boolean",
|
|
535
|
+
})
|
|
530
536
|
.option("legacy-env", {
|
|
531
537
|
type: "boolean",
|
|
532
538
|
describe: "Use legacy environments",
|
|
@@ -631,6 +637,7 @@ function createCLIParser(argv: string[]) {
|
|
|
631
637
|
outDir: args.outdir,
|
|
632
638
|
dryRun: args.dryRun,
|
|
633
639
|
noBundle: !(args.bundle ?? !config.no_bundle),
|
|
640
|
+
keepVars: args.keepVars,
|
|
634
641
|
});
|
|
635
642
|
}
|
|
636
643
|
);
|
|
@@ -1021,7 +1028,7 @@ function createCLIParser(argv: string[]) {
|
|
|
1021
1028
|
compatibility_date: undefined,
|
|
1022
1029
|
compatibility_flags: undefined,
|
|
1023
1030
|
usage_model: undefined,
|
|
1024
|
-
|
|
1031
|
+
keepVars: false, // this doesn't matter since it's a new script anyway
|
|
1025
1032
|
}),
|
|
1026
1033
|
}
|
|
1027
1034
|
);
|
package/src/init.ts
CHANGED
|
@@ -941,7 +941,9 @@ async function getWorkerConfig(
|
|
|
941
941
|
: { route: routeOrRoutes[0] };
|
|
942
942
|
|
|
943
943
|
return {
|
|
944
|
-
compatibility_date:
|
|
944
|
+
compatibility_date:
|
|
945
|
+
serviceEnvMetadata.script.compatibility_date ??
|
|
946
|
+
new Date().toISOString().substring(0, 10),
|
|
945
947
|
...routeOrRoutesToConfig,
|
|
946
948
|
usage_model: serviceEnvMetadata.script.usage_model,
|
|
947
949
|
migrations: [
|
package/src/inspect.ts
CHANGED
|
@@ -610,36 +610,36 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
|
|
|
610
610
|
break;
|
|
611
611
|
case "weakmap":
|
|
612
612
|
case "map":
|
|
613
|
-
|
|
614
|
-
"{
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
613
|
+
ro.preview.entries === undefined
|
|
614
|
+
? args.push("{}")
|
|
615
|
+
: args.push(
|
|
616
|
+
"{\n" +
|
|
617
|
+
ro.preview.entries
|
|
618
|
+
.map(({ key, value }) => {
|
|
619
|
+
return ` ${key?.description ?? "<unknown>"} => ${
|
|
620
|
+
value.description
|
|
621
|
+
}`;
|
|
622
|
+
})
|
|
623
|
+
.join(",\n") +
|
|
624
|
+
(ro.preview.overflow ? "\n ..." : "") +
|
|
625
|
+
"\n}"
|
|
626
|
+
);
|
|
627
627
|
|
|
628
628
|
break;
|
|
629
629
|
case "weakset":
|
|
630
630
|
case "set":
|
|
631
|
-
|
|
632
|
-
"{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
631
|
+
ro.preview.entries === undefined
|
|
632
|
+
? args.push("{}")
|
|
633
|
+
: args.push(
|
|
634
|
+
"{ " +
|
|
635
|
+
ro.preview.entries
|
|
636
|
+
.map(({ value }) => {
|
|
637
|
+
return `${value.description}`;
|
|
638
|
+
})
|
|
639
|
+
.join(", ") +
|
|
640
|
+
(ro.preview.overflow ? ", ..." : "") +
|
|
641
|
+
" }"
|
|
642
|
+
);
|
|
643
643
|
break;
|
|
644
644
|
case "regexp":
|
|
645
645
|
break;
|
|
@@ -189,7 +189,14 @@ async function generateAssetsFetch(
|
|
|
189
189
|
}
|
|
190
190
|
const body = readFileSync(filepath) as unknown as ReadableStream;
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
let contentType = getType(filepath) || "application/octet-stream";
|
|
193
|
+
if (
|
|
194
|
+
contentType.startsWith("text/") &&
|
|
195
|
+
!contentType.includes("charset")
|
|
196
|
+
) {
|
|
197
|
+
contentType = `${contentType}; charset=utf-8`;
|
|
198
|
+
}
|
|
199
|
+
|
|
193
200
|
return { body, contentType };
|
|
194
201
|
},
|
|
195
202
|
});
|
package/src/pages/constants.ts
CHANGED
|
@@ -8,8 +8,9 @@ export const MAX_UPLOAD_ATTEMPTS = 5;
|
|
|
8
8
|
export const MAX_CHECK_MISSING_ATTEMPTS = 5;
|
|
9
9
|
export const SECONDS_TO_WAIT_FOR_PROXY = 5;
|
|
10
10
|
export const isInPagesCI = !!process.env.CF_PAGES;
|
|
11
|
-
/**
|
|
11
|
+
/** Max number of rules in _routes.json */
|
|
12
12
|
export const MAX_FUNCTIONS_ROUTES_RULES = 100;
|
|
13
|
+
/** Max char length of each rule in _routes.json */
|
|
13
14
|
export const MAX_FUNCTIONS_ROUTES_RULE_LENGTH = 100;
|
|
14
15
|
export const ROUTES_SPEC_VERSION = 1;
|
|
15
16
|
export const ROUTES_SPEC_DESCRIPTION = `Generated by wrangler@${wranglerVersion}`;
|
package/src/pages/dev.tsx
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { execSync, spawn } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { watch } from "chokidar";
|
|
6
|
-
import
|
|
6
|
+
import * as esbuild from "esbuild";
|
|
7
7
|
import { unstable_dev } from "../api";
|
|
8
|
+
import { esbuildAliasExternalPlugin } from "../bundle";
|
|
8
9
|
import { FatalError } from "../errors";
|
|
9
10
|
import { logger } from "../logger";
|
|
10
11
|
import * as metrics from "../metrics";
|
|
11
12
|
import { getBasePath } from "../paths";
|
|
12
13
|
import { buildFunctions } from "./build";
|
|
13
|
-
import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
|
|
14
|
+
import { ROUTES_SPEC_VERSION, SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
|
|
14
15
|
import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
|
|
16
|
+
import { validateRoutes } from "./functions/routes-validation";
|
|
15
17
|
import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils";
|
|
16
18
|
import type { AdditionalDevProps } from "../dev";
|
|
19
|
+
import type { RoutesJSONSpec } from "./functions/routes-transformation";
|
|
17
20
|
import type { YargsOptionsToInterface } from "./types";
|
|
18
|
-
import type { Plugin } from "esbuild";
|
|
19
21
|
import type { Argv } from "yargs";
|
|
20
22
|
|
|
21
23
|
const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
|
|
@@ -144,6 +146,13 @@ export function Options(yargs: Argv) {
|
|
|
144
146
|
type: "string",
|
|
145
147
|
hidden: true,
|
|
146
148
|
},
|
|
149
|
+
|
|
150
|
+
"log-level": {
|
|
151
|
+
// "none" will currently default to "error" for Wrangler Logger
|
|
152
|
+
choices: ["debug", "info", "log", "warn", "error", "none"] as const,
|
|
153
|
+
describe: "Specify logging level",
|
|
154
|
+
default: "log",
|
|
155
|
+
},
|
|
147
156
|
})
|
|
148
157
|
.epilogue(pagesBetaWarning);
|
|
149
158
|
}
|
|
@@ -170,10 +179,21 @@ export const Handler = async ({
|
|
|
170
179
|
"node-compat": nodeCompat,
|
|
171
180
|
config: config,
|
|
172
181
|
_: [_pages, _dev, ...remaining],
|
|
182
|
+
logLevel,
|
|
173
183
|
}: PagesDevArgs) => {
|
|
174
184
|
// Beta message for `wrangler pages <commands>` usage
|
|
175
185
|
logger.log(pagesBetaWarning);
|
|
176
186
|
|
|
187
|
+
type LogLevelArg = "debug" | "info" | "log" | "warn" | "error" | "none";
|
|
188
|
+
if (logLevel) {
|
|
189
|
+
// we don't define a "none" logLevel, so "error" will do for now.
|
|
190
|
+
// The YargsOptionsToInterface doesn't handle the passing in of Unions from choices in Yargs
|
|
191
|
+
logger.loggerLevel =
|
|
192
|
+
(logLevel as LogLevelArg) === "none"
|
|
193
|
+
? "error"
|
|
194
|
+
: (logLevel as Exclude<"none", LogLevelArg>);
|
|
195
|
+
}
|
|
196
|
+
|
|
177
197
|
if (!local) {
|
|
178
198
|
throw new FatalError("Only local mode is supported at the moment.", 1);
|
|
179
199
|
}
|
|
@@ -184,6 +204,7 @@ export const Handler = async ({
|
|
|
184
204
|
|
|
185
205
|
const functionsDirectory = "./functions";
|
|
186
206
|
let usingFunctions = existsSync(functionsDirectory);
|
|
207
|
+
let usingWorkerScript = false;
|
|
187
208
|
|
|
188
209
|
const command = remaining;
|
|
189
210
|
|
|
@@ -285,6 +306,7 @@ export const Handler = async ({
|
|
|
285
306
|
}
|
|
286
307
|
}
|
|
287
308
|
}
|
|
309
|
+
|
|
288
310
|
// Depending on the result of building Functions, we may not actually be using
|
|
289
311
|
// Functions even if the directory exists.
|
|
290
312
|
if (!usingFunctions) {
|
|
@@ -295,14 +317,15 @@ export const Handler = async ({
|
|
|
295
317
|
directory !== undefined
|
|
296
318
|
? join(directory, singleWorkerScriptPath)
|
|
297
319
|
: singleWorkerScriptPath;
|
|
320
|
+
usingWorkerScript = existsSync(scriptPath);
|
|
298
321
|
|
|
299
|
-
if (!
|
|
322
|
+
if (!usingWorkerScript) {
|
|
300
323
|
logger.log("No functions. Shimming...");
|
|
301
324
|
scriptPath = resolve(getBasePath(), "templates/pages-shim.ts");
|
|
302
325
|
} else {
|
|
303
326
|
const runBuild = async () => {
|
|
304
327
|
try {
|
|
305
|
-
await
|
|
328
|
+
await esbuild.build({
|
|
306
329
|
entryPoints: [scriptPath],
|
|
307
330
|
write: false,
|
|
308
331
|
plugins: [blockWorkerJsImports],
|
|
@@ -330,8 +353,108 @@ export const Handler = async ({
|
|
|
330
353
|
);
|
|
331
354
|
}
|
|
332
355
|
|
|
356
|
+
let entrypoint = scriptPath;
|
|
357
|
+
|
|
358
|
+
// custom _routes.json apply only to Functions or Advanced Mode Pages projects
|
|
359
|
+
if (directory && (usingFunctions || usingWorkerScript)) {
|
|
360
|
+
const routesJSONPath = join(directory, "_routes.json");
|
|
361
|
+
|
|
362
|
+
if (existsSync(routesJSONPath)) {
|
|
363
|
+
let routesJSONContents: string;
|
|
364
|
+
const runBuild = async (
|
|
365
|
+
entrypointFile: string,
|
|
366
|
+
outfile: string,
|
|
367
|
+
routes: string
|
|
368
|
+
) => {
|
|
369
|
+
await esbuild.build({
|
|
370
|
+
entryPoints: [
|
|
371
|
+
resolve(getBasePath(), "templates/pages-dev-pipeline.ts"),
|
|
372
|
+
],
|
|
373
|
+
bundle: true,
|
|
374
|
+
sourcemap: true,
|
|
375
|
+
format: "esm",
|
|
376
|
+
plugins: [
|
|
377
|
+
esbuildAliasExternalPlugin({
|
|
378
|
+
__ENTRY_POINT__: entrypointFile,
|
|
379
|
+
}),
|
|
380
|
+
],
|
|
381
|
+
outfile,
|
|
382
|
+
define: {
|
|
383
|
+
__ROUTES__: routes,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
// always run the routes validation first. If _routes.json is invalid we
|
|
390
|
+
// want to throw accordingly and exit.
|
|
391
|
+
routesJSONContents = readFileSync(routesJSONPath, "utf-8");
|
|
392
|
+
validateRoutes(JSON.parse(routesJSONContents), directory);
|
|
393
|
+
|
|
394
|
+
entrypoint = join(
|
|
395
|
+
tmpdir(),
|
|
396
|
+
`${Math.random().toString(36).slice(2)}.js`
|
|
397
|
+
);
|
|
398
|
+
await runBuild(scriptPath, entrypoint, routesJSONContents);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
if (err instanceof FatalError) {
|
|
401
|
+
throw err;
|
|
402
|
+
} else {
|
|
403
|
+
throw new FatalError(
|
|
404
|
+
`Could not validate _routes.json at ${directory}: ${err}`,
|
|
405
|
+
1
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
watch([routesJSONPath], {
|
|
411
|
+
persistent: true,
|
|
412
|
+
ignoreInitial: true,
|
|
413
|
+
}).on("all", async () => {
|
|
414
|
+
try {
|
|
415
|
+
/**
|
|
416
|
+
* Watch for _routes.json file changes and validate file each time.
|
|
417
|
+
* If file is valid proceed to running the build.
|
|
418
|
+
*/
|
|
419
|
+
routesJSONContents = readFileSync(routesJSONPath, "utf-8");
|
|
420
|
+
validateRoutes(JSON.parse(routesJSONContents), directory as string);
|
|
421
|
+
await runBuild(scriptPath, entrypoint, routesJSONContents);
|
|
422
|
+
} catch (err) {
|
|
423
|
+
/**
|
|
424
|
+
* If _routes.json is invalid, don't exit but instead fallback to a sensible default
|
|
425
|
+
* and continue to serve the assets. At the same time make sure we warn users that we
|
|
426
|
+
* we detected an invalid file and that we'll be using a default.
|
|
427
|
+
* This basically equivalates to serving a Functions or _worker.js project as is,
|
|
428
|
+
* without applying any additional routing rules on top.
|
|
429
|
+
*/
|
|
430
|
+
const error =
|
|
431
|
+
err instanceof FatalError
|
|
432
|
+
? err
|
|
433
|
+
: `Could not validate _routes.json at ${directory}: ${err}`;
|
|
434
|
+
const defaultRoutesJSONSpec: RoutesJSONSpec = {
|
|
435
|
+
version: ROUTES_SPEC_VERSION,
|
|
436
|
+
include: ["/*"],
|
|
437
|
+
exclude: [],
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
logger.error(error);
|
|
441
|
+
logger.warn(
|
|
442
|
+
`Falling back to the following _routes.json default: ${JSON.stringify(
|
|
443
|
+
defaultRoutesJSONSpec,
|
|
444
|
+
null,
|
|
445
|
+
2
|
|
446
|
+
)}`
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
routesJSONContents = JSON.stringify(defaultRoutesJSONSpec);
|
|
450
|
+
await runBuild(scriptPath, entrypoint, routesJSONContents);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
333
456
|
const { stop, waitUntilExit } = await unstable_dev(
|
|
334
|
-
|
|
457
|
+
entrypoint,
|
|
335
458
|
{
|
|
336
459
|
ip,
|
|
337
460
|
port,
|
|
@@ -385,7 +508,7 @@ export const Handler = async ({
|
|
|
385
508
|
persistTo,
|
|
386
509
|
showInteractiveDevSession: undefined,
|
|
387
510
|
inspect: true,
|
|
388
|
-
logLevel: "
|
|
511
|
+
logLevel: "warn",
|
|
389
512
|
logPrefix: "pages",
|
|
390
513
|
},
|
|
391
514
|
{ testMode: false, disableExperimentalWarning: true }
|
|
@@ -544,9 +667,9 @@ async function spawnProxyProcess({
|
|
|
544
667
|
return port;
|
|
545
668
|
}
|
|
546
669
|
|
|
547
|
-
const blockWorkerJsImports: Plugin = {
|
|
670
|
+
const blockWorkerJsImports: esbuild.Plugin = {
|
|
548
671
|
name: "block-worker-js-imports",
|
|
549
|
-
setup(build) {
|
|
672
|
+
setup(build: esbuild.PluginBuild) {
|
|
550
673
|
build.onResolve({ filter: /.*/g }, (_args) => {
|
|
551
674
|
logger.error(
|
|
552
675
|
`_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.`
|