wrangler 2.13.0 → 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.
- package/package.json +11 -7
- package/src/__tests__/api-devregistry.test.ts +121 -0
- package/src/__tests__/api.test.ts +27 -5
- package/src/__tests__/configuration.test.ts +45 -0
- package/src/__tests__/pages/publish.test.ts +67 -23
- package/src/__tests__/traverse-module-graph.test.ts +220 -0
- package/src/api/dev.ts +7 -18
- package/src/api/pages/create-worker-bundle-contents.ts +1 -0
- package/src/bundle.ts +9 -5
- package/src/config/environment.ts +24 -0
- package/src/config/index.ts +18 -0
- package/src/config/validation.ts +88 -0
- package/src/create-worker-upload-form.ts +17 -0
- package/src/d1/execute.tsx +1 -1
- package/src/dev/start-server.ts +2 -8
- package/src/dev/use-esbuild.ts +2 -8
- package/src/dev-registry.ts +2 -1
- package/src/dev.tsx +1 -0
- package/src/entry.ts +18 -8
- package/src/module-collection.ts +91 -25
- package/src/pages/functions/buildPlugin.ts +1 -0
- package/src/pages/functions/buildWorker.ts +2 -8
- package/src/publish/publish.ts +3 -8
- package/src/secret/index.ts +1 -0
- package/src/traverse-module-graph.ts +53 -0
- package/src/worker.ts +10 -0
- package/wrangler-dist/cli.d.ts +22 -0
- package/wrangler-dist/cli.js +1906 -1725
- package/src/__tests__/api-devregistry.test.js +0 -64
- package/src/__tests__/tsconfig.tsbuildinfo +0 -1
- package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
- package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
- package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
- package/templates/tsconfig.tsbuildinfo +0 -1
package/src/module-collection.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
4
5
|
import globToRegExp from "glob-to-regexp";
|
|
5
6
|
import { logger } from "./logger";
|
|
6
7
|
import type { Config, ConfigModuleRuleType } from "./config";
|
|
@@ -38,28 +39,8 @@ export const DEFAULT_MODULE_RULES: Config["rules"] = [
|
|
|
38
39
|
{ type: "CompiledWasm", globs: ["**/*.wasm"] },
|
|
39
40
|
];
|
|
40
41
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
rules?: Config["rules"];
|
|
44
|
-
// a collection of "legacy" style module references, which are just file names
|
|
45
|
-
// we will eventually deprecate this functionality, hence the verbose greppable name
|
|
46
|
-
wrangler1xlegacyModuleReferences: {
|
|
47
|
-
rootDirectory: string;
|
|
48
|
-
fileNames: Set<string>;
|
|
49
|
-
};
|
|
50
|
-
}): {
|
|
51
|
-
modules: CfModule[];
|
|
52
|
-
plugin: esbuild.Plugin;
|
|
53
|
-
} {
|
|
54
|
-
const rules: Config["rules"] = [
|
|
55
|
-
...(props.rules || []),
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
57
|
-
...DEFAULT_MODULE_RULES!,
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
// First, we want to add some validations to the module rules
|
|
61
|
-
// We want to warn if rules are accidentally configured in such a way that
|
|
62
|
-
// subsequent rules will never match because `fallthrough` hasn't been set
|
|
42
|
+
export function parseRules(userRules: Config["rules"] = []) {
|
|
43
|
+
const rules: Config["rules"] = [...userRules, ...DEFAULT_MODULE_RULES];
|
|
63
44
|
|
|
64
45
|
const completedRuleLocations: Record<string, number> = {};
|
|
65
46
|
let index = 0;
|
|
@@ -67,7 +48,7 @@ export default function createModuleCollector(props: {
|
|
|
67
48
|
for (const rule of rules) {
|
|
68
49
|
if (rule.type in completedRuleLocations) {
|
|
69
50
|
if (rules[completedRuleLocations[rule.type]].fallthrough !== false) {
|
|
70
|
-
if (index <
|
|
51
|
+
if (index < userRules.length) {
|
|
71
52
|
logger.warn(
|
|
72
53
|
`The module rule at position ${index} (${JSON.stringify(
|
|
73
54
|
rule
|
|
@@ -101,6 +82,89 @@ export default function createModuleCollector(props: {
|
|
|
101
82
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
102
83
|
rulesToRemove.forEach((rule) => rules!.splice(rules!.indexOf(rule), 1));
|
|
103
84
|
|
|
85
|
+
return { rules, removedRules: rulesToRemove };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function matchFiles(
|
|
89
|
+
files: string[],
|
|
90
|
+
relativeTo: string,
|
|
91
|
+
{
|
|
92
|
+
rules,
|
|
93
|
+
removedRules,
|
|
94
|
+
}: { rules: Config["rules"]; removedRules: Config["rules"] }
|
|
95
|
+
) {
|
|
96
|
+
const modules: CfModule[] = [];
|
|
97
|
+
|
|
98
|
+
// Deduplicate modules. This is usually a poorly specified `wrangler.toml` configuration, but duplicate modules will cause a crash at runtime
|
|
99
|
+
const moduleNames = new Set<string>();
|
|
100
|
+
for (const rule of rules) {
|
|
101
|
+
for (const glob of rule.globs) {
|
|
102
|
+
const regexp = globToRegExp(glob, {
|
|
103
|
+
globstar: true,
|
|
104
|
+
});
|
|
105
|
+
const newModules = await Promise.all(
|
|
106
|
+
files
|
|
107
|
+
.filter((f) => regexp.test(f))
|
|
108
|
+
.map(async (name) => {
|
|
109
|
+
const filePath = name;
|
|
110
|
+
const fileContent = await readFile(path.join(relativeTo, filePath));
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
name: filePath,
|
|
114
|
+
content: fileContent,
|
|
115
|
+
type: RuleTypeToModuleType[rule.type],
|
|
116
|
+
};
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
for (const module of newModules) {
|
|
120
|
+
if (!moduleNames.has(module.name)) {
|
|
121
|
+
moduleNames.add(module.name);
|
|
122
|
+
modules.push(module);
|
|
123
|
+
} else {
|
|
124
|
+
logger.warn(
|
|
125
|
+
`Ignoring duplicate module: ${chalk.blue(
|
|
126
|
+
module.name
|
|
127
|
+
)} (${chalk.green(module.type ?? "")})`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// This is just a sanity check verifying that no files match rules that were removed
|
|
135
|
+
for (const rule of removedRules) {
|
|
136
|
+
for (const glob of rule.globs) {
|
|
137
|
+
const regexp = globToRegExp(glob);
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
if (regexp.test(file)) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`The file ${file} matched a module rule in your configuration (${JSON.stringify(
|
|
142
|
+
rule
|
|
143
|
+
)}), but was ignored because a previous rule with the same type was not marked as \`fallthrough = true\`.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return modules;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default function createModuleCollector(props: {
|
|
153
|
+
format: CfScriptFormat;
|
|
154
|
+
rules?: Config["rules"];
|
|
155
|
+
// a collection of "legacy" style module references, which are just file names
|
|
156
|
+
// we will eventually deprecate this functionality, hence the verbose greppable name
|
|
157
|
+
wrangler1xlegacyModuleReferences: {
|
|
158
|
+
rootDirectory: string;
|
|
159
|
+
fileNames: Set<string>;
|
|
160
|
+
};
|
|
161
|
+
preserveFileNames?: boolean;
|
|
162
|
+
}): {
|
|
163
|
+
modules: CfModule[];
|
|
164
|
+
plugin: esbuild.Plugin;
|
|
165
|
+
} {
|
|
166
|
+
const { rules, removedRules } = parseRules(props.rules);
|
|
167
|
+
|
|
104
168
|
const modules: CfModule[] = [];
|
|
105
169
|
return {
|
|
106
170
|
modules,
|
|
@@ -206,7 +270,9 @@ export default function createModuleCollector(props: {
|
|
|
206
270
|
.createHash("sha1")
|
|
207
271
|
.update(fileContent)
|
|
208
272
|
.digest("hex");
|
|
209
|
-
const fileName =
|
|
273
|
+
const fileName = props.preserveFileNames
|
|
274
|
+
? filePath
|
|
275
|
+
: `./${fileHash}-${path.basename(args.path)}`;
|
|
210
276
|
|
|
211
277
|
// add the module to the array
|
|
212
278
|
modules.push({
|
|
@@ -245,7 +311,7 @@ export default function createModuleCollector(props: {
|
|
|
245
311
|
});
|
|
246
312
|
});
|
|
247
313
|
|
|
248
|
-
|
|
314
|
+
removedRules.forEach((rule) => {
|
|
249
315
|
rule.globs.forEach((glob) => {
|
|
250
316
|
build.onResolve(
|
|
251
317
|
{ filter: globToRegExp(glob) },
|
|
@@ -48,6 +48,7 @@ export function buildWorker({
|
|
|
48
48
|
file: resolve(getBasePath(), "templates/pages-template-worker.ts"),
|
|
49
49
|
directory: functionsDirectory,
|
|
50
50
|
format: "modules",
|
|
51
|
+
moduleRoot: functionsDirectory,
|
|
51
52
|
},
|
|
52
53
|
outdir ? resolve(outdir) : resolve(outfile),
|
|
53
54
|
{
|
|
@@ -58,10 +59,6 @@ export function buildWorker({
|
|
|
58
59
|
watch,
|
|
59
60
|
legacyNodeCompat,
|
|
60
61
|
nodejsCompat,
|
|
61
|
-
loader: {
|
|
62
|
-
".txt": "text",
|
|
63
|
-
".html": "text",
|
|
64
|
-
},
|
|
65
62
|
define: {
|
|
66
63
|
__FALLBACK_SERVICE__: JSON.stringify(fallbackService),
|
|
67
64
|
},
|
|
@@ -202,6 +199,7 @@ export function buildRawWorker({
|
|
|
202
199
|
file: workerScriptPath,
|
|
203
200
|
directory: resolve(directory),
|
|
204
201
|
format: "modules",
|
|
202
|
+
moduleRoot: resolve(directory),
|
|
205
203
|
},
|
|
206
204
|
outdir ? resolve(outdir) : resolve(outfile),
|
|
207
205
|
{
|
|
@@ -210,10 +208,6 @@ export function buildRawWorker({
|
|
|
210
208
|
watch,
|
|
211
209
|
legacyNodeCompat,
|
|
212
210
|
nodejsCompat,
|
|
213
|
-
loader: {
|
|
214
|
-
".txt": "text",
|
|
215
|
-
".html": "text",
|
|
216
|
-
},
|
|
217
211
|
define: {},
|
|
218
212
|
betaD1Shims: (betaD1Shims || []).map(
|
|
219
213
|
(binding) => `${D1_BETA_PREFIX}${binding}`
|
package/src/publish/publish.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { ParseError } from "../parse";
|
|
|
21
21
|
import { getQueue, putConsumer } from "../queues/client";
|
|
22
22
|
import { getWorkersDevSubdomain } from "../routes";
|
|
23
23
|
import { syncAssets } from "../sites";
|
|
24
|
+
import traverseModuleGraph from "../traverse-module-graph";
|
|
24
25
|
import { identifyD1BindingsAsBeta } from "../worker";
|
|
25
26
|
import { getZoneForRoute } from "../zones";
|
|
26
27
|
import type { FetchError } from "../cfetch";
|
|
@@ -463,14 +464,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
463
464
|
resolvedEntryPointPath,
|
|
464
465
|
bundleType,
|
|
465
466
|
}: Awaited<ReturnType<typeof bundleWorker>> = props.noBundle
|
|
466
|
-
?
|
|
467
|
-
{
|
|
468
|
-
modules: [],
|
|
469
|
-
dependencies: {},
|
|
470
|
-
resolvedEntryPointPath: props.entry.file,
|
|
471
|
-
bundleType: props.entry.format === "modules" ? "esm" : "commonjs",
|
|
472
|
-
stop: undefined,
|
|
473
|
-
}
|
|
467
|
+
? await traverseModuleGraph(props.entry, props.rules)
|
|
474
468
|
: await bundleWorker(
|
|
475
469
|
props.entry,
|
|
476
470
|
typeof destination === "string" ? destination : destination.path,
|
|
@@ -541,6 +535,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
541
535
|
? { binding: "__STATIC_CONTENT", id: assets.namespace }
|
|
542
536
|
: []
|
|
543
537
|
),
|
|
538
|
+
send_email: config.send_email,
|
|
544
539
|
vars: { ...config.vars, ...props.vars },
|
|
545
540
|
wasm_modules: config.wasm_modules,
|
|
546
541
|
text_blobs: {
|
package/src/secret/index.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { logger } from "./logger";
|
|
5
|
+
import { matchFiles, parseRules } from "./module-collection";
|
|
6
|
+
import type { BundleResult } from "./bundle";
|
|
7
|
+
import type { Config } from "./config";
|
|
8
|
+
import type { Entry } from "./entry";
|
|
9
|
+
|
|
10
|
+
async function getFiles(root: string, relativeTo: string): Promise<string[]> {
|
|
11
|
+
const files = [];
|
|
12
|
+
for (const file of await readdir(root, { withFileTypes: true })) {
|
|
13
|
+
if (file.isDirectory()) {
|
|
14
|
+
files.push(...(await getFiles(path.join(root, file.name), relativeTo)));
|
|
15
|
+
} else {
|
|
16
|
+
files.push(path.relative(relativeTo, path.join(root, file.name)));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default async function traverseModuleGraph(
|
|
23
|
+
entry: Entry,
|
|
24
|
+
rules: Config["rules"]
|
|
25
|
+
): Promise<BundleResult> {
|
|
26
|
+
const files = await getFiles(entry.moduleRoot, entry.moduleRoot);
|
|
27
|
+
const relativeEntryPoint = path.relative(entry.moduleRoot, entry.file);
|
|
28
|
+
|
|
29
|
+
const modules = (await matchFiles(files, entry.moduleRoot, parseRules(rules)))
|
|
30
|
+
.filter((m) => m.name !== relativeEntryPoint)
|
|
31
|
+
.map((m) => ({
|
|
32
|
+
...m,
|
|
33
|
+
name: m.name,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const bundleType = entry.format === "modules" ? "esm" : "commonjs";
|
|
37
|
+
|
|
38
|
+
if (modules.length > 0) {
|
|
39
|
+
logger.info(`Uploading additional modules:`);
|
|
40
|
+
modules.forEach(({ name, type }) => {
|
|
41
|
+
logger.info(`- ${chalk.blue(name)} (${chalk.green(type ?? "")})`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
modules,
|
|
47
|
+
dependencies: {},
|
|
48
|
+
resolvedEntryPointPath: entry.file,
|
|
49
|
+
bundleType,
|
|
50
|
+
stop: undefined,
|
|
51
|
+
sourceMapPath: undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
package/src/worker.ts
CHANGED
|
@@ -79,6 +79,15 @@ export interface CfKvNamespace {
|
|
|
79
79
|
id: string;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* A binding to send email.
|
|
84
|
+
*/
|
|
85
|
+
export interface CfSendEmailBindings {
|
|
86
|
+
name: string;
|
|
87
|
+
destination_address?: string;
|
|
88
|
+
allowed_destination_addresses?: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
/**
|
|
83
92
|
* A binding to a wasm module (in service-worker format)
|
|
84
93
|
*/
|
|
@@ -216,6 +225,7 @@ export interface CfWorkerInit {
|
|
|
216
225
|
bindings: {
|
|
217
226
|
vars: CfVars | undefined;
|
|
218
227
|
kv_namespaces: CfKvNamespace[] | undefined;
|
|
228
|
+
send_email: CfSendEmailBindings[] | undefined;
|
|
219
229
|
wasm_modules: CfWasmModuleBindings | undefined;
|
|
220
230
|
text_blobs: CfTextBlobBindings | undefined;
|
|
221
231
|
data_blobs: CfDataBlobBindings | undefined;
|
package/wrangler-dist/cli.d.ts
CHANGED
|
@@ -532,6 +532,11 @@ declare interface EnvironmentInheritable {
|
|
|
532
532
|
* The entrypoint/path to the JavaScript file that will be executed.
|
|
533
533
|
*/
|
|
534
534
|
main: string | undefined;
|
|
535
|
+
/**
|
|
536
|
+
* The directory in which module rules should be evaluated in a `--no-bundle` worker
|
|
537
|
+
* This defaults to dirname(main) when left undefined
|
|
538
|
+
*/
|
|
539
|
+
base_dir: string | undefined;
|
|
535
540
|
/**
|
|
536
541
|
* Whether we use <name>.<subdomain>.workers.dev to
|
|
537
542
|
* test and deploy your worker.
|
|
@@ -770,6 +775,23 @@ declare interface EnvironmentNonInheritable {
|
|
|
770
775
|
/** The ID of the KV namespace used during `wrangler dev` */
|
|
771
776
|
preview_id?: string;
|
|
772
777
|
}[];
|
|
778
|
+
/**
|
|
779
|
+
* These specify bindings to send email from inside your Worker.
|
|
780
|
+
*
|
|
781
|
+
* NOTE: This field is not automatically inherited from the top level environment,
|
|
782
|
+
* and so must be specified in every named environment.
|
|
783
|
+
*
|
|
784
|
+
* @default `[]`
|
|
785
|
+
* @nonInheritable
|
|
786
|
+
*/
|
|
787
|
+
send_email: {
|
|
788
|
+
/** The binding name used to refer to the this binding */
|
|
789
|
+
name: string;
|
|
790
|
+
/** If this binding should be restricted to a specific verified address */
|
|
791
|
+
destination_address?: string;
|
|
792
|
+
/** If this binding should be restricted to a set of verified addresses */
|
|
793
|
+
allowed_destination_addresses?: string[];
|
|
794
|
+
}[];
|
|
773
795
|
/**
|
|
774
796
|
* Specifies Queues that are bound to this Worker environment.
|
|
775
797
|
*
|