wrangler 0.0.0-e6733a3 → 0.0.0-e88a54e
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/bin/wrangler.js +2 -2
- package/miniflare-dist/index.mjs +79 -0
- package/miniflare-dist/index.mjs.map +7 -0
- package/package.json +35 -16
- package/pages/functions/buildWorker.ts +1 -1
- package/pages/functions/filepath-routing.test.ts +212 -28
- package/pages/functions/filepath-routing.ts +91 -131
- package/pages/functions/routes.ts +11 -18
- package/pages/functions/template-worker.ts +19 -33
- package/src/__tests__/configuration.test.ts +2796 -0
- package/src/__tests__/dev.test.tsx +488 -58
- package/src/__tests__/dev2.test.tsx +154 -0
- package/src/__tests__/guess-worker-format.test.ts +58 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +48 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +55 -19
- package/src/__tests__/helpers/mock-console.ts +70 -0
- package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
- package/src/__tests__/helpers/mock-kv.ts +33 -0
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +32 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/https-options.test.ts +127 -0
- package/src/__tests__/index.test.ts +779 -75
- package/src/__tests__/jest.setup.ts +53 -6
- package/src/__tests__/kv.test.ts +738 -454
- package/src/__tests__/logout.test.ts +52 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/pages.test.ts +44 -0
- package/src/__tests__/parse.test.ts +322 -0
- package/src/__tests__/publish.test.ts +2704 -0
- package/src/__tests__/r2.test.ts +198 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +425 -0
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +564 -0
- package/src/__tests__/whoami.test.tsx +100 -0
- package/src/bundle.ts +142 -0
- package/src/cfetch/index.ts +21 -19
- package/src/cfetch/internal.ts +94 -24
- package/src/cli.ts +10 -0
- package/src/config/README.md +107 -0
- package/src/config/config.ts +218 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +366 -0
- package/src/config/index.ts +62 -0
- package/src/config/validation-helpers.ts +486 -0
- package/src/config/validation.ts +1090 -0
- package/src/{api/preview.ts → create-worker-preview.ts} +61 -23
- package/src/create-worker-upload-form.ts +217 -0
- package/src/dev/dev.tsx +364 -0
- package/src/dev/local.tsx +232 -0
- package/src/dev/remote.tsx +203 -0
- package/src/dev/use-esbuild.ts +100 -0
- package/src/dialogs.tsx +2 -2
- package/src/entry.ts +208 -0
- package/src/environment-variables.ts +33 -0
- package/src/https-options.ts +122 -0
- package/src/index.tsx +1198 -799
- package/src/inspect.ts +159 -66
- package/src/{kv.tsx → kv.ts} +117 -65
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +37 -0
- package/src/module-collection.ts +229 -35
- package/src/open-in-browser.ts +25 -0
- package/src/package-manager.ts +126 -0
- package/src/pages.tsx +58 -42
- package/src/parse.ts +183 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +224 -78
- package/src/publish.ts +281 -292
- package/src/r2.ts +50 -0
- package/src/reporting.ts +131 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +224 -58
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +62 -0
- package/src/user.tsx +273 -180
- package/src/whoami.tsx +69 -0
- package/src/{api/worker.ts → worker.ts} +45 -37
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +130612 -112486
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/run-in-tmp.ts +0 -19
- package/src/__tests__/run-wrangler.ts +0 -32
- package/src/api/form_data.ts +0 -131
- package/src/config.ts +0 -487
- package/src/dev.tsx +0 -771
- package/src/tail.tsx +0 -73
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Log, LogLevel, Miniflare } from "miniflare";
|
|
2
|
+
import yargs from "yargs";
|
|
3
|
+
import { hideBin } from "yargs/helpers";
|
|
4
|
+
import { enumKeys } from "./enum-keys";
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
const args = await yargs(hideBin(process.argv))
|
|
8
|
+
.help(false)
|
|
9
|
+
.version(false)
|
|
10
|
+
.option("log", {
|
|
11
|
+
choices: enumKeys(LogLevel),
|
|
12
|
+
}).argv;
|
|
13
|
+
|
|
14
|
+
const logLevel = LogLevel[args.log ?? "INFO"];
|
|
15
|
+
const config = {
|
|
16
|
+
...JSON.parse((args._[0] as string) ?? "{}"),
|
|
17
|
+
log: new Log(logLevel),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if (logLevel > LogLevel.INFO) {
|
|
21
|
+
console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const mf = new Miniflare(config);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Start Miniflare development server
|
|
28
|
+
await mf.startServer();
|
|
29
|
+
} catch (e) {
|
|
30
|
+
mf.log.error(e as Error);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
// Unmount any mounted workers
|
|
33
|
+
await mf.dispose();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await main();
|
package/src/module-collection.ts
CHANGED
|
@@ -1,21 +1,96 @@
|
|
|
1
|
-
import type { CfModule } from "./api/worker";
|
|
2
|
-
import type esbuild from "esbuild";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import globToRegExp from "glob-to-regexp";
|
|
5
|
+
import type { Config, ConfigModuleRuleType } from "./config";
|
|
6
|
+
import type { CfModule, CfModuleType, CfScriptFormat } from "./worker";
|
|
7
|
+
import type esbuild from "esbuild";
|
|
8
|
+
|
|
9
|
+
const RuleTypeToModuleType: Record<ConfigModuleRuleType, CfModuleType> = {
|
|
10
|
+
ESModule: "esm",
|
|
11
|
+
CommonJS: "commonjs",
|
|
12
|
+
CompiledWasm: "compiled-wasm",
|
|
13
|
+
Data: "buffer",
|
|
14
|
+
Text: "text",
|
|
15
|
+
};
|
|
6
16
|
|
|
7
17
|
// This is a combination of an esbuild plugin and a mutable array
|
|
8
18
|
// that we use to collect module references from source code.
|
|
9
19
|
// There will be modules that _shouldn't_ be inlined directly into
|
|
10
20
|
// the bundle. (eg. wasm modules, some text files, etc). We can include
|
|
11
|
-
// those files as modules in the multi part
|
|
21
|
+
// those files as modules in the multi part worker form upload. This
|
|
12
22
|
// plugin+array is used to collect references to these modules, reference
|
|
13
23
|
// them correctly in the bundle, and add them to the form upload.
|
|
14
24
|
|
|
15
|
-
export
|
|
25
|
+
export const DEFAULT_MODULE_RULES: Config["rules"] = [
|
|
26
|
+
{ type: "Text", globs: ["**/*.txt", "**/*.html"] },
|
|
27
|
+
{ type: "Data", globs: ["**/*.bin"] },
|
|
28
|
+
{ type: "CompiledWasm", globs: ["**/*.wasm"] },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default function createModuleCollector(props: {
|
|
32
|
+
format: CfScriptFormat;
|
|
33
|
+
rules?: Config["rules"];
|
|
34
|
+
// a collection of "legacy" style module references, which are just file names
|
|
35
|
+
// we will eventually deprecate this functionality, hence the verbose greppable name
|
|
36
|
+
wrangler1xlegacyModuleReferences: {
|
|
37
|
+
rootDirectory: string;
|
|
38
|
+
fileNames: Set<string>;
|
|
39
|
+
};
|
|
40
|
+
}): {
|
|
16
41
|
modules: CfModule[];
|
|
17
42
|
plugin: esbuild.Plugin;
|
|
18
43
|
} {
|
|
44
|
+
const rules: Config["rules"] = [
|
|
45
|
+
...(props.rules || []),
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
47
|
+
...DEFAULT_MODULE_RULES!,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// First, we want to add some validations to the module rules
|
|
51
|
+
// We want to warn if rules are accidentally configured in such a way that
|
|
52
|
+
// subsequent rules will never match because `fallthrough` hasn't been set
|
|
53
|
+
|
|
54
|
+
const completedRuleLocations: Record<string, number> = {};
|
|
55
|
+
let index = 0;
|
|
56
|
+
const rulesToRemove: Config["rules"] = [];
|
|
57
|
+
for (const rule of rules) {
|
|
58
|
+
if (rule.type in completedRuleLocations) {
|
|
59
|
+
if (rules[completedRuleLocations[rule.type]].fallthrough !== false) {
|
|
60
|
+
if (index < (props.rules || []).length) {
|
|
61
|
+
console.warn(
|
|
62
|
+
`The module rule at position ${index} (${JSON.stringify(
|
|
63
|
+
rule
|
|
64
|
+
)}) has the same type as a previous rule (at position ${
|
|
65
|
+
completedRuleLocations[rule.type]
|
|
66
|
+
}, ${JSON.stringify(
|
|
67
|
+
rules[completedRuleLocations[rule.type]]
|
|
68
|
+
)}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning.`
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
console.warn(
|
|
72
|
+
`The default module rule ${JSON.stringify(
|
|
73
|
+
rule
|
|
74
|
+
)} has the same type as a previous rule (at position ${
|
|
75
|
+
completedRuleLocations[rule.type]
|
|
76
|
+
}, ${JSON.stringify(
|
|
77
|
+
rules[completedRuleLocations[rule.type]]
|
|
78
|
+
)}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
rulesToRemove.push(rule);
|
|
84
|
+
}
|
|
85
|
+
if (!(rule.type in completedRuleLocations) && rule.fallthrough !== true) {
|
|
86
|
+
completedRuleLocations[rule.type] = index;
|
|
87
|
+
}
|
|
88
|
+
index++;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
92
|
+
rulesToRemove.forEach((rule) => rules!.splice(rules!.indexOf(rule), 1));
|
|
93
|
+
|
|
19
94
|
const modules: CfModule[] = [];
|
|
20
95
|
return {
|
|
21
96
|
modules,
|
|
@@ -23,41 +98,160 @@ export default function makeModuleCollector(): {
|
|
|
23
98
|
name: "wrangler-module-collector",
|
|
24
99
|
setup(build) {
|
|
25
100
|
build.onStart(() => {
|
|
26
|
-
// reset the
|
|
101
|
+
// reset the module collection array
|
|
27
102
|
modules.splice(0);
|
|
28
103
|
});
|
|
29
104
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
async (args: esbuild.OnResolveArgs) => {
|
|
35
|
-
// take the file and massage it to a
|
|
36
|
-
// transportable/manageable format
|
|
37
|
-
const fileExt = path.extname(args.path);
|
|
38
|
-
const filePath = path.join(args.resolveDir, args.path);
|
|
39
|
-
const fileContent = await readFile(filePath);
|
|
40
|
-
const fileHash = crypto
|
|
41
|
-
.createHash("sha1")
|
|
42
|
-
.update(fileContent)
|
|
43
|
-
.digest("hex");
|
|
44
|
-
const fileName = `${fileHash}-${path.basename(args.path)}`;
|
|
45
|
-
|
|
46
|
-
// add the module to the array
|
|
47
|
-
modules.push({
|
|
48
|
-
name: fileName,
|
|
49
|
-
content: fileContent,
|
|
50
|
-
type: fileExt === ".wasm" ? "compiled-wasm" : "text",
|
|
51
|
-
});
|
|
105
|
+
// ~ start legacy module specifier support ~
|
|
106
|
+
|
|
107
|
+
// This section detects usage of "legacy" 1.x style module specifiers
|
|
108
|
+
// and modifies them so they "work" in wrangler v2, but with a warning
|
|
52
109
|
|
|
110
|
+
const rulesMatchers = rules.flatMap((rule) => {
|
|
111
|
+
return rule.globs.map((glob) => {
|
|
112
|
+
const regex = globToRegExp(glob);
|
|
53
113
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
namespace: "wrangler-module-collector-ns", // just a tag, this isn't strictly necessary
|
|
57
|
-
watchFiles: [filePath], // we also add the file to esbuild's watch list
|
|
114
|
+
regex,
|
|
115
|
+
rule,
|
|
58
116
|
};
|
|
59
|
-
}
|
|
60
|
-
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (props.wrangler1xlegacyModuleReferences.fileNames.size > 0) {
|
|
121
|
+
build.onResolve(
|
|
122
|
+
{
|
|
123
|
+
filter: new RegExp(
|
|
124
|
+
"^(" +
|
|
125
|
+
[...props.wrangler1xlegacyModuleReferences.fileNames].join(
|
|
126
|
+
"|"
|
|
127
|
+
) +
|
|
128
|
+
")$"
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
async (args: esbuild.OnResolveArgs) => {
|
|
132
|
+
if (
|
|
133
|
+
args.kind !== "import-statement" &&
|
|
134
|
+
args.kind !== "require-call"
|
|
135
|
+
) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// In the future, this will simply throw an error
|
|
139
|
+
console.warn(
|
|
140
|
+
`Deprecation warning: detected a legacy module import in "./${path.relative(
|
|
141
|
+
process.cwd(),
|
|
142
|
+
args.importer
|
|
143
|
+
)}". This will stop working in the future. Replace references to "${
|
|
144
|
+
args.path
|
|
145
|
+
}" with "./${args.path}";`
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// take the file and massage it to a
|
|
149
|
+
// transportable/manageable format
|
|
150
|
+
const filePath = path.join(
|
|
151
|
+
props.wrangler1xlegacyModuleReferences.rootDirectory,
|
|
152
|
+
args.path
|
|
153
|
+
);
|
|
154
|
+
const fileContent = await readFile(filePath);
|
|
155
|
+
const fileHash = crypto
|
|
156
|
+
.createHash("sha1")
|
|
157
|
+
.update(fileContent)
|
|
158
|
+
.digest("hex");
|
|
159
|
+
const fileName = `./${fileHash}-${path.basename(args.path)}`;
|
|
160
|
+
|
|
161
|
+
const { rule } =
|
|
162
|
+
rulesMatchers.find(({ regex }) => regex.test(fileName)) || {};
|
|
163
|
+
if (rule) {
|
|
164
|
+
// add the module to the array
|
|
165
|
+
modules.push({
|
|
166
|
+
name: fileName,
|
|
167
|
+
content: fileContent,
|
|
168
|
+
type: RuleTypeToModuleType[rule.type],
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
path: fileName, // change the reference to the changed module
|
|
172
|
+
external: props.format === "modules", // mark it as external in the bundle
|
|
173
|
+
namespace: `wrangler-module-${rule.type}`, // just a tag, this isn't strictly necessary
|
|
174
|
+
watchFiles: [filePath], // we also add the file to esbuild's watch list
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ~ end legacy module specifier support ~
|
|
182
|
+
|
|
183
|
+
rules?.forEach((rule) => {
|
|
184
|
+
if (rule.type === "ESModule" || rule.type === "CommonJS") return; // TODO: we should treat these as js files, and use the jsx loader
|
|
185
|
+
|
|
186
|
+
rule.globs.forEach((glob) => {
|
|
187
|
+
build.onResolve(
|
|
188
|
+
{ filter: globToRegExp(glob) },
|
|
189
|
+
async (args: esbuild.OnResolveArgs) => {
|
|
190
|
+
// take the file and massage it to a
|
|
191
|
+
// transportable/manageable format
|
|
192
|
+
|
|
193
|
+
const filePath = path.join(args.resolveDir, args.path);
|
|
194
|
+
const fileContent = await readFile(filePath);
|
|
195
|
+
const fileHash = crypto
|
|
196
|
+
.createHash("sha1")
|
|
197
|
+
.update(fileContent)
|
|
198
|
+
.digest("hex");
|
|
199
|
+
const fileName = `./${fileHash}-${path.basename(args.path)}`;
|
|
200
|
+
|
|
201
|
+
// add the module to the array
|
|
202
|
+
modules.push({
|
|
203
|
+
name: fileName,
|
|
204
|
+
content: fileContent,
|
|
205
|
+
type: RuleTypeToModuleType[rule.type],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
path: fileName, // change the reference to the changed module
|
|
210
|
+
external: props.format === "modules", // mark it as external in the bundle
|
|
211
|
+
namespace: `wrangler-module-${rule.type}`, // just a tag, this isn't strictly necessary
|
|
212
|
+
watchFiles: [filePath], // we also add the file to esbuild's watch list
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (props.format === "service-worker") {
|
|
218
|
+
build.onLoad(
|
|
219
|
+
{ filter: globToRegExp(glob) },
|
|
220
|
+
async (args: esbuild.OnLoadArgs) => {
|
|
221
|
+
return {
|
|
222
|
+
// We replace the the module with an identifier
|
|
223
|
+
// that we'll separately add to the form upload
|
|
224
|
+
// as part of [wasm_modules]/[text_blobs]. This identifier has to be a valid
|
|
225
|
+
// JS identifier, so we replace all non alphanumeric characters
|
|
226
|
+
// with an underscore.
|
|
227
|
+
// TODO: what of "Data"?
|
|
228
|
+
contents: `export default ${args.path.replace(
|
|
229
|
+
/[^a-zA-Z0-9_$]/g,
|
|
230
|
+
"_"
|
|
231
|
+
)};`,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
rulesToRemove.forEach((rule) => {
|
|
240
|
+
rule.globs.forEach((glob) => {
|
|
241
|
+
build.onResolve(
|
|
242
|
+
{ filter: globToRegExp(glob) },
|
|
243
|
+
async (args: esbuild.OnResolveArgs) => {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`The file ${
|
|
246
|
+
args.path
|
|
247
|
+
} matched a module rule in your configuration (${JSON.stringify(
|
|
248
|
+
rule
|
|
249
|
+
)}), but was ignored because a previous rule with the same type was not marked as \`fallthrough = true\`.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
61
255
|
},
|
|
62
256
|
},
|
|
63
257
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import open from "open";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An extremely simple wrapper around the open command.
|
|
5
|
+
* Specifically, it adds an 'error' event handler so that when this function
|
|
6
|
+
* is called in environments where we can't open the browser (e.g. github codespaces,
|
|
7
|
+
* stackblitz, remote servers), it doesn't just crash the process.
|
|
8
|
+
*
|
|
9
|
+
* @param url the URL to point the browser at
|
|
10
|
+
* @param options open a chromium-based browser instead of the default
|
|
11
|
+
*/
|
|
12
|
+
export default async function openInBrowser(
|
|
13
|
+
url: string,
|
|
14
|
+
{ forceChromium }: { forceChromium: boolean } = { forceChromium: false }
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
const options: open.Options | undefined = forceChromium
|
|
17
|
+
? {
|
|
18
|
+
app: [{ name: open.apps.chrome }, { name: open.apps.edge }],
|
|
19
|
+
}
|
|
20
|
+
: undefined;
|
|
21
|
+
const childProcess = await open(url, options);
|
|
22
|
+
childProcess.on("error", () => {
|
|
23
|
+
console.warn(`Failed to open ${url} in a browser`);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { execa, execaCommandSync } from "execa";
|
|
4
|
+
|
|
5
|
+
export interface PackageManager {
|
|
6
|
+
cwd: string;
|
|
7
|
+
type: "npm" | "yarn";
|
|
8
|
+
addDevDeps(...packages: string[]): Promise<void>;
|
|
9
|
+
install(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function getPackageManager(cwd: string): Promise<PackageManager> {
|
|
13
|
+
const [hasYarn, hasNpm] = await Promise.all([supportsYarn(), supportsNpm()]);
|
|
14
|
+
const hasYarnLock = existsSync(join(cwd, "yarn.lock"));
|
|
15
|
+
const hasNpmLock = existsSync(join(cwd, "package-lock.json"));
|
|
16
|
+
|
|
17
|
+
if (hasNpmLock) {
|
|
18
|
+
if (hasNpm) {
|
|
19
|
+
console.log(
|
|
20
|
+
"Using npm as package manager, as there is already a package-lock.json file."
|
|
21
|
+
);
|
|
22
|
+
return { ...NpmPackageManager, cwd };
|
|
23
|
+
} else if (hasYarn) {
|
|
24
|
+
console.log("Using yarn as package manager.");
|
|
25
|
+
console.warn(
|
|
26
|
+
"There is already a package-lock.json file but could not find npm on the PATH."
|
|
27
|
+
);
|
|
28
|
+
return { ...YarnPackageManager, cwd };
|
|
29
|
+
}
|
|
30
|
+
} else if (hasYarnLock) {
|
|
31
|
+
if (hasYarn) {
|
|
32
|
+
console.log(
|
|
33
|
+
"Using yarn as package manager, as there is already a yarn.lock file."
|
|
34
|
+
);
|
|
35
|
+
return { ...YarnPackageManager, cwd };
|
|
36
|
+
} else if (hasNpm) {
|
|
37
|
+
console.log("Using npm as package manager.");
|
|
38
|
+
console.warn(
|
|
39
|
+
"There is already a yarn.lock file but could not find yarn on the PATH."
|
|
40
|
+
);
|
|
41
|
+
return { ...NpmPackageManager, cwd };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (hasNpm) {
|
|
46
|
+
console.log("Using npm as package manager.");
|
|
47
|
+
return { ...NpmPackageManager, cwd };
|
|
48
|
+
} else if (hasYarn) {
|
|
49
|
+
console.log("Using yarn as package manager.");
|
|
50
|
+
return { ...YarnPackageManager, cwd };
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"Unable to find a package manager. Supported managers are: npm and yarn."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the name of the given `packageManager`.
|
|
60
|
+
*/
|
|
61
|
+
export function getPackageManagerName(packageManager: PackageManager): string {
|
|
62
|
+
return packageManager.type ?? "unknown";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Manage packages using npm
|
|
67
|
+
*/
|
|
68
|
+
const NpmPackageManager: PackageManager = {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
type: "npm",
|
|
71
|
+
/** Add and install a new devDependency into the local package.json. */
|
|
72
|
+
async addDevDeps(...packages: string[]): Promise<void> {
|
|
73
|
+
await execa("npm", ["install", ...packages, "--save-dev"], {
|
|
74
|
+
stdio: "inherit",
|
|
75
|
+
cwd: this.cwd,
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/** Install all the dependencies in the local package.json. */
|
|
80
|
+
async install(): Promise<void> {
|
|
81
|
+
await execa("npm", ["install"], {
|
|
82
|
+
stdio: "inherit",
|
|
83
|
+
cwd: this.cwd,
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Manage packages using yarn
|
|
90
|
+
*/
|
|
91
|
+
const YarnPackageManager: PackageManager = {
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
type: "yarn",
|
|
94
|
+
/** Add and install a new devDependency into the local package.json. */
|
|
95
|
+
async addDevDeps(...packages: string[]): Promise<void> {
|
|
96
|
+
await execa("yarn", ["add", ...packages, "--dev"], {
|
|
97
|
+
stdio: "inherit",
|
|
98
|
+
cwd: this.cwd,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/** Install all the dependencies in the local package.json. */
|
|
103
|
+
async install(): Promise<void> {
|
|
104
|
+
await execa("yarn", ["install"], {
|
|
105
|
+
stdio: "inherit",
|
|
106
|
+
cwd: this.cwd,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
async function supports(name: string): Promise<boolean> {
|
|
112
|
+
try {
|
|
113
|
+
execaCommandSync(`${name} --version`, { stdio: "ignore" });
|
|
114
|
+
return true;
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function supportsYarn(): Promise<boolean> {
|
|
121
|
+
return supports("yarn");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function supportsNpm(): Promise<boolean> {
|
|
125
|
+
return supports("npm");
|
|
126
|
+
}
|
package/src/pages.tsx
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/* eslint-disable no-shadow */
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { execSync, spawn } from "child_process";
|
|
9
|
-
import { URL } from "url";
|
|
10
|
-
import { getType } from "mime";
|
|
11
|
-
import open from "open";
|
|
3
|
+
import { execSync, spawn } from "node:child_process";
|
|
4
|
+
import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { URL } from "node:url";
|
|
12
8
|
import { watch } from "chokidar";
|
|
13
|
-
import
|
|
9
|
+
import { getType } from "mime";
|
|
14
10
|
import { buildWorker } from "../pages/functions/buildWorker";
|
|
15
|
-
import type { Config } from "../pages/functions/routes";
|
|
16
|
-
import { writeRoutesModule } from "../pages/functions/routes";
|
|
17
11
|
import { generateConfigFromFileTree } from "../pages/functions/filepath-routing";
|
|
12
|
+
import { writeRoutesModule } from "../pages/functions/routes";
|
|
13
|
+
import openInBrowser from "./open-in-browser";
|
|
14
|
+
import { toUrlPath } from "./paths";
|
|
15
|
+
import type { Config } from "../pages/functions/routes";
|
|
16
|
+
import type { Headers, Request, fetch } from "@miniflare/core";
|
|
17
|
+
import type { BuildResult } from "esbuild";
|
|
18
|
+
import type { MiniflareOptions } from "miniflare";
|
|
19
|
+
import type { BuilderCallback } from "yargs";
|
|
18
20
|
|
|
19
21
|
// Defer importing miniflare until we really need it. This takes ~0.5s
|
|
20
22
|
// and also modifies some `stream/web` and `undici` prototypes, so we
|
|
21
23
|
// don't want to do this if pages commands aren't being called.
|
|
22
|
-
import type { Headers, Request, fetch } from "@miniflare/core";
|
|
23
|
-
import type { MiniflareOptions } from "miniflare";
|
|
24
24
|
|
|
25
|
-
const EXIT_CALLBACKS = [];
|
|
25
|
+
const EXIT_CALLBACKS: (() => void)[] = [];
|
|
26
26
|
const EXIT = (message?: string, code?: number) => {
|
|
27
27
|
if (message) console.log(message);
|
|
28
28
|
if (code) process.exitCode = code;
|
|
@@ -122,7 +122,9 @@ async function spawnProxyProcess({
|
|
|
122
122
|
},
|
|
123
123
|
}
|
|
124
124
|
);
|
|
125
|
-
EXIT_CALLBACKS.push(() =>
|
|
125
|
+
EXIT_CALLBACKS.push(() => {
|
|
126
|
+
proxy.kill();
|
|
127
|
+
});
|
|
126
128
|
|
|
127
129
|
proxy.stdout.on("data", (data) => {
|
|
128
130
|
console.log(`[proxy]: ${data}`);
|
|
@@ -664,16 +666,17 @@ async function buildFunctions({
|
|
|
664
666
|
);
|
|
665
667
|
|
|
666
668
|
const routesModule = join(tmpdir(), "./functionsRoutes.mjs");
|
|
669
|
+
const baseURL = toUrlPath("/");
|
|
667
670
|
|
|
668
671
|
const config: Config = await generateConfigFromFileTree({
|
|
669
672
|
baseDir: functionsDirectory,
|
|
670
|
-
baseURL
|
|
673
|
+
baseURL,
|
|
671
674
|
});
|
|
672
675
|
|
|
673
676
|
if (outputConfigPath) {
|
|
674
677
|
writeFileSync(
|
|
675
678
|
outputConfigPath,
|
|
676
|
-
JSON.stringify({ ...config, baseURL
|
|
679
|
+
JSON.stringify({ ...config, baseURL }, null, 2)
|
|
677
680
|
);
|
|
678
681
|
}
|
|
679
682
|
|
|
@@ -768,7 +771,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
768
771
|
kv: kvs = [],
|
|
769
772
|
do: durableObjects = [],
|
|
770
773
|
"live-reload": liveReload,
|
|
771
|
-
|
|
774
|
+
_: [_pages, _dev, ...remaining],
|
|
772
775
|
}) => {
|
|
773
776
|
if (!local) {
|
|
774
777
|
console.error("Only local mode is supported at the moment.");
|
|
@@ -792,8 +795,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
792
795
|
|
|
793
796
|
let miniflareArgs: MiniflareOptions = {};
|
|
794
797
|
|
|
795
|
-
let scriptReadyResolve;
|
|
796
|
-
const scriptReadyPromise = new Promise(
|
|
798
|
+
let scriptReadyResolve: () => void;
|
|
799
|
+
const scriptReadyPromise = new Promise<void>(
|
|
797
800
|
(resolve) => (scriptReadyResolve = resolve)
|
|
798
801
|
);
|
|
799
802
|
|
|
@@ -802,13 +805,15 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
802
805
|
|
|
803
806
|
console.log(`Compiling worker to "${scriptPath}"...`);
|
|
804
807
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
808
|
+
try {
|
|
809
|
+
await buildFunctions({
|
|
810
|
+
scriptPath,
|
|
811
|
+
functionsDirectory,
|
|
812
|
+
sourcemap: true,
|
|
813
|
+
watch: true,
|
|
814
|
+
onEnd: () => scriptReadyResolve(),
|
|
815
|
+
});
|
|
816
|
+
} catch {}
|
|
812
817
|
|
|
813
818
|
watch([functionsDirectory], {
|
|
814
819
|
persistent: true,
|
|
@@ -827,6 +832,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
827
832
|
scriptPath,
|
|
828
833
|
};
|
|
829
834
|
} else {
|
|
835
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
836
|
+
scriptReadyResolve!();
|
|
837
|
+
|
|
830
838
|
const scriptPath =
|
|
831
839
|
directory !== undefined
|
|
832
840
|
? join(directory, singleWorkerScriptPath)
|
|
@@ -862,11 +870,13 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
862
870
|
// internally just waits for that promise to resolve.
|
|
863
871
|
await scriptReadyPromise;
|
|
864
872
|
|
|
865
|
-
//
|
|
866
|
-
//
|
|
867
|
-
const assetsFetch =
|
|
868
|
-
|
|
869
|
-
|
|
873
|
+
// `assetsFetch()` will only be called if there is `proxyPort` defined.
|
|
874
|
+
// We only define `proxyPort`, above, when there is no `directory` defined.
|
|
875
|
+
const assetsFetch =
|
|
876
|
+
directory !== undefined
|
|
877
|
+
? await generateAssetsFetch(directory)
|
|
878
|
+
: invalidAssetsFetch;
|
|
879
|
+
|
|
870
880
|
const miniflare = new Miniflare({
|
|
871
881
|
port,
|
|
872
882
|
watch: true,
|
|
@@ -887,17 +897,17 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
887
897
|
// User bindings
|
|
888
898
|
bindings: {
|
|
889
899
|
...Object.fromEntries(
|
|
890
|
-
bindings.map((binding) => binding.toString().split("="))
|
|
900
|
+
bindings.map((binding) => binding.toString().split("=", 1))
|
|
891
901
|
),
|
|
892
902
|
},
|
|
893
903
|
|
|
894
904
|
// env.ASSETS.fetch
|
|
895
905
|
serviceBindings: {
|
|
896
|
-
async ASSETS(request) {
|
|
906
|
+
async ASSETS(request: Request) {
|
|
897
907
|
if (proxyPort) {
|
|
898
908
|
try {
|
|
899
909
|
const url = new URL(request.url);
|
|
900
|
-
url.host = `
|
|
910
|
+
url.host = `localhost:${proxyPort}`;
|
|
901
911
|
return await fetch(url, request);
|
|
902
912
|
} catch (thrown) {
|
|
903
913
|
console.error(`Could not proxy request: ${thrown}`);
|
|
@@ -935,12 +945,10 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
935
945
|
try {
|
|
936
946
|
// `startServer` might throw if user code contains errors
|
|
937
947
|
const server = await miniflare.startServer();
|
|
938
|
-
console.log(`Serving at http://
|
|
948
|
+
console.log(`Serving at http://localhost:${port}/`);
|
|
939
949
|
|
|
940
950
|
if (process.env.BROWSER !== "none") {
|
|
941
|
-
|
|
942
|
-
await open(`http://127.0.0.1:${port}/`);
|
|
943
|
-
} catch {}
|
|
951
|
+
await openInBrowser(`http://localhost:${port}/`);
|
|
944
952
|
}
|
|
945
953
|
|
|
946
954
|
if (directory !== undefined && liveReload) {
|
|
@@ -957,12 +965,14 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
957
965
|
miniflare.dispose().catch((err) => miniflare.log.error(err));
|
|
958
966
|
});
|
|
959
967
|
} catch (e) {
|
|
960
|
-
miniflare.log.error(e);
|
|
968
|
+
miniflare.log.error(e as Error);
|
|
961
969
|
EXIT("Could not start Miniflare.", 1);
|
|
962
970
|
}
|
|
963
971
|
}
|
|
964
972
|
)
|
|
965
|
-
.command("functions",
|
|
973
|
+
.command("functions", false, (yargs) =>
|
|
974
|
+
// we hide this command from help output because
|
|
975
|
+
// it's not meant to be used directly right now
|
|
966
976
|
yargs.command(
|
|
967
977
|
"build [directory]",
|
|
968
978
|
"Compile a folder of Cloudflare Pages Functions into a single Worker",
|
|
@@ -1029,3 +1039,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1029
1039
|
)
|
|
1030
1040
|
);
|
|
1031
1041
|
};
|
|
1042
|
+
|
|
1043
|
+
const invalidAssetsFetch: typeof fetch = () => {
|
|
1044
|
+
throw new Error(
|
|
1045
|
+
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
|
|
1046
|
+
);
|
|
1047
|
+
};
|