wrangler 0.0.0-e1c615f → 0.0.0-e5d3690
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 +9 -5
- package/pages/functions/buildWorker.ts +5 -0
- package/pages/functions/filepath-routing.ts +15 -8
- package/pages/functions/routes.ts +1 -1
- package/pages/functions/template-worker.ts +18 -19
- package/src/__tests__/dev.test.tsx +5 -4
- package/src/__tests__/index.test.ts +2 -0
- package/src/__tests__/kv.test.ts +173 -72
- package/src/__tests__/mock-cfetch.ts +8 -6
- package/src/__tests__/publish.test.ts +672 -0
- package/src/__tests__/whoami.test.tsx +130 -0
- package/src/api/form_data.ts +1 -1
- package/src/api/worker.ts +6 -6
- package/src/cfetch/index.ts +10 -2
- package/src/cfetch/internal.ts +10 -8
- package/src/config.ts +633 -118
- package/src/dev.tsx +119 -214
- package/src/index.tsx +268 -222
- package/src/inspect.ts +543 -0
- package/src/kv.tsx +18 -9
- package/src/pages.tsx +117 -45
- package/src/proxy.ts +275 -76
- package/src/publish.ts +78 -46
- package/src/sites.tsx +167 -38
- package/src/user.tsx +56 -59
- package/src/whoami.tsx +69 -0
- package/wrangler-dist/cli.js +31405 -30705
- package/wrangler-dist/cli.js.map +3 -3
- package/src/api/inspect.ts +0 -430
package/src/publish.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
-
import esbuild from "esbuild";
|
|
4
|
+
import * as esbuild from "esbuild";
|
|
5
|
+
import type { Metafile } from "esbuild";
|
|
5
6
|
import { execa } from "execa";
|
|
6
7
|
import tmp from "tmp-promise";
|
|
7
8
|
import type { CfWorkerInit } from "./api/worker";
|
|
@@ -9,23 +10,26 @@ import { toFormData } from "./api/form_data";
|
|
|
9
10
|
import { fetchResult } from "./cfetch";
|
|
10
11
|
import type { Config } from "./config";
|
|
11
12
|
import makeModuleCollector from "./module-collection";
|
|
13
|
+
import type { AssetPaths } from "./sites";
|
|
12
14
|
import { syncAssets } from "./sites";
|
|
13
15
|
|
|
14
|
-
type CfScriptFormat =
|
|
16
|
+
type CfScriptFormat = undefined | "modules" | "service-worker";
|
|
15
17
|
|
|
16
18
|
type Props = {
|
|
17
19
|
config: Config;
|
|
18
|
-
format
|
|
19
|
-
script
|
|
20
|
-
name
|
|
21
|
-
env
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
format: CfScriptFormat | undefined;
|
|
21
|
+
script: string | undefined;
|
|
22
|
+
name: string | undefined;
|
|
23
|
+
env: string | undefined;
|
|
24
|
+
compatibilityDate: string | undefined;
|
|
25
|
+
compatibilityFlags: string[] | undefined;
|
|
26
|
+
assetPaths: AssetPaths | undefined;
|
|
27
|
+
triggers: (string | number)[] | undefined;
|
|
28
|
+
routes: (string | number)[] | undefined;
|
|
29
|
+
legacyEnv: boolean | undefined;
|
|
30
|
+
jsxFactory: undefined | string;
|
|
31
|
+
jsxFragment: undefined | string;
|
|
32
|
+
experimentalPublic: boolean;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
function sleep(ms: number) {
|
|
@@ -33,10 +37,10 @@ function sleep(ms: number) {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export default async function publish(props: Props): Promise<void> {
|
|
36
|
-
if (props.
|
|
40
|
+
if (props.experimentalPublic && props.format === "service-worker") {
|
|
37
41
|
// TODO: check config too
|
|
38
42
|
throw new Error(
|
|
39
|
-
"You cannot
|
|
43
|
+
"You cannot publish in the service worker format with a public directory."
|
|
40
44
|
);
|
|
41
45
|
}
|
|
42
46
|
// TODO: warn if git/hg has uncommitted changes
|
|
@@ -48,6 +52,18 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
48
52
|
__path__,
|
|
49
53
|
} = config;
|
|
50
54
|
|
|
55
|
+
const envRootObj =
|
|
56
|
+
props.env && config.env ? config.env[props.env] || {} : config;
|
|
57
|
+
|
|
58
|
+
assert(
|
|
59
|
+
envRootObj.compatibility_date || props.compatibilityDate,
|
|
60
|
+
"A compatibility_date is required when publishing. Add one to your wrangler.toml file, or pass it in your terminal as --compatibility_date. See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (accountId === undefined) {
|
|
64
|
+
throw new Error("No account_id provided.");
|
|
65
|
+
}
|
|
66
|
+
|
|
51
67
|
const triggers = props.triggers || config.triggers?.crons;
|
|
52
68
|
const routes = props.routes || config.routes;
|
|
53
69
|
|
|
@@ -64,10 +80,12 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
64
80
|
|
|
65
81
|
let file: string;
|
|
66
82
|
if (props.script) {
|
|
67
|
-
|
|
83
|
+
// If the script name comes from the command line it is relative to the current working directory.
|
|
84
|
+
file = path.resolve(props.script);
|
|
68
85
|
} else {
|
|
86
|
+
// If the script name comes from the config, then it is relative to the wrangler.toml file.
|
|
69
87
|
assert(build?.upload?.main, "missing main file");
|
|
70
|
-
file = path.
|
|
88
|
+
file = path.resolve(path.dirname(__path__), build.upload.main);
|
|
71
89
|
}
|
|
72
90
|
|
|
73
91
|
if (props.legacyEnv) {
|
|
@@ -76,7 +94,6 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
76
94
|
const envName = props.env ?? "production";
|
|
77
95
|
|
|
78
96
|
const destination = await tmp.dir({ unsafeCleanup: true });
|
|
79
|
-
|
|
80
97
|
if (props.config.build?.command) {
|
|
81
98
|
// TODO: add a deprecation message here?
|
|
82
99
|
console.log("running:", props.config.build.command);
|
|
@@ -90,7 +107,7 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
90
107
|
|
|
91
108
|
const moduleCollector = makeModuleCollector();
|
|
92
109
|
const result = await esbuild.build({
|
|
93
|
-
...(props.
|
|
110
|
+
...(props.experimentalPublic
|
|
94
111
|
? {
|
|
95
112
|
stdin: {
|
|
96
113
|
contents: (
|
|
@@ -98,14 +115,14 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
98
115
|
path.join(__dirname, "../static-asset-facade.js"),
|
|
99
116
|
"utf8"
|
|
100
117
|
)
|
|
101
|
-
).replace("__ENTRY_POINT__",
|
|
118
|
+
).replace("__ENTRY_POINT__", file),
|
|
102
119
|
sourcefile: "static-asset-facade.js",
|
|
103
120
|
resolveDir: path.dirname(file),
|
|
104
121
|
},
|
|
122
|
+
nodePaths: [path.join(__dirname, "../vendor")],
|
|
105
123
|
}
|
|
106
124
|
: { entryPoints: [file] }),
|
|
107
125
|
bundle: true,
|
|
108
|
-
nodePaths: props.public ? [path.join(__dirname, "../vendor")] : undefined,
|
|
109
126
|
outdir: destination.path,
|
|
110
127
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
111
128
|
format: "esm",
|
|
@@ -120,18 +137,28 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
120
137
|
...(jsxFragment && { jsxFragment }),
|
|
121
138
|
});
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
: file)
|
|
140
|
+
// result.metafile is defined because of the `metafile: true` option above.
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
142
|
+
const metafile = result.metafile!;
|
|
143
|
+
const entryPoints = Object.entries(metafile.outputs).filter(
|
|
144
|
+
([_path, output]) => output.entryPoint !== undefined
|
|
129
145
|
);
|
|
130
|
-
|
|
146
|
+
assert(
|
|
147
|
+
entryPoints.length > 0,
|
|
148
|
+
`Cannot find entry-point "${file}" in generated bundle.` +
|
|
149
|
+
listEntryPoints(entryPoints)
|
|
150
|
+
);
|
|
151
|
+
assert(
|
|
152
|
+
entryPoints.length < 2,
|
|
153
|
+
"More than one entry-point found for generated bundle." +
|
|
154
|
+
listEntryPoints(entryPoints)
|
|
155
|
+
);
|
|
156
|
+
const entryPointExports = entryPoints[0][1].exports;
|
|
157
|
+
const resolvedEntryPointPath = entryPoints[0][0];
|
|
131
158
|
const { format } = props;
|
|
132
159
|
const bundle = {
|
|
133
|
-
type:
|
|
134
|
-
exports:
|
|
160
|
+
type: entryPointExports.length > 0 ? "esm" : "commonjs",
|
|
161
|
+
exports: entryPointExports,
|
|
135
162
|
};
|
|
136
163
|
|
|
137
164
|
// TODO: instead of bundling the facade with the worker, we should just bundle the worker and expose it as a module.
|
|
@@ -148,13 +175,13 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
148
175
|
return;
|
|
149
176
|
}
|
|
150
177
|
|
|
151
|
-
const content = await readFile(
|
|
178
|
+
const content = await readFile(resolvedEntryPointPath, { encoding: "utf-8" });
|
|
152
179
|
await destination.cleanup();
|
|
153
180
|
|
|
154
181
|
// if config.migrations
|
|
155
182
|
// get current migration tag
|
|
156
183
|
let migrations;
|
|
157
|
-
if (
|
|
184
|
+
if (config.migrations !== undefined) {
|
|
158
185
|
const scripts = await fetchResult<{ id: string; migration_tag: string }[]>(
|
|
159
186
|
`/accounts/${accountId}/workers/scripts`
|
|
160
187
|
);
|
|
@@ -190,17 +217,13 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
190
217
|
}
|
|
191
218
|
}
|
|
192
219
|
|
|
193
|
-
const assets =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
201
|
-
: { manifest: undefined, namespace: undefined };
|
|
202
|
-
|
|
203
|
-
const envRootObj = props.env ? config.env[props.env] || {} : config;
|
|
220
|
+
const assets = await syncAssets(
|
|
221
|
+
accountId,
|
|
222
|
+
scriptName,
|
|
223
|
+
props.assetPaths,
|
|
224
|
+
false
|
|
225
|
+
);
|
|
226
|
+
|
|
204
227
|
const bindings: CfWorkerInit["bindings"] = {
|
|
205
228
|
kv_namespaces: envRootObj.kv_namespaces?.concat(
|
|
206
229
|
assets.namespace
|
|
@@ -215,7 +238,7 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
215
238
|
const worker: CfWorkerInit = {
|
|
216
239
|
name: scriptName,
|
|
217
240
|
main: {
|
|
218
|
-
name: path.basename(
|
|
241
|
+
name: path.basename(resolvedEntryPointPath),
|
|
219
242
|
content: content,
|
|
220
243
|
type: bundle.type === "esm" ? "esm" : "commonjs",
|
|
221
244
|
},
|
|
@@ -248,12 +271,13 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
248
271
|
|
|
249
272
|
// Upload the script so it has time to propagate.
|
|
250
273
|
const { available_on_subdomain } = await fetchResult(
|
|
251
|
-
|
|
274
|
+
workerUrl,
|
|
252
275
|
{
|
|
253
276
|
method: "PUT",
|
|
254
277
|
// @ts-expect-error: TODO: fix this type error!
|
|
255
278
|
body: toFormData(worker),
|
|
256
|
-
}
|
|
279
|
+
},
|
|
280
|
+
new URLSearchParams({ available_on_subdomains: "true" })
|
|
257
281
|
);
|
|
258
282
|
|
|
259
283
|
const uploadMs = Date.now() - start;
|
|
@@ -348,3 +372,11 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
348
372
|
console.log(" ", target);
|
|
349
373
|
}
|
|
350
374
|
}
|
|
375
|
+
|
|
376
|
+
function listEntryPoints(
|
|
377
|
+
outputs: [string, ValueOf<Metafile["outputs"]>][]
|
|
378
|
+
): string {
|
|
379
|
+
return outputs.map(([_input, output]) => output.entryPoint).join("\n");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
type ValueOf<T> = T[keyof T];
|
package/src/sites.tsx
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import { createReadStream } from "node:fs";
|
|
3
1
|
import * as path from "node:path";
|
|
4
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
+
import ignore from "ignore";
|
|
4
|
+
import { XXHash64 } from "xxhash-addon";
|
|
5
5
|
import { fetchResult } from "./cfetch";
|
|
6
|
+
import type { Config } from "./config";
|
|
6
7
|
import { listNamespaceKeys, listNamespaces, putBulkKeyValue } from "./kv";
|
|
7
8
|
|
|
9
|
+
/** Paths to always ignore. */
|
|
10
|
+
const ALWAYS_IGNORE = ["node_modules"];
|
|
11
|
+
const HIDDEN_FILES_TO_INCLUDE = [
|
|
12
|
+
".well-known", // See https://datatracker.ietf.org/doc/html/rfc8615
|
|
13
|
+
];
|
|
14
|
+
|
|
8
15
|
async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
|
|
9
16
|
const files = await readdir(dirPath, { withFileTypes: true });
|
|
10
17
|
for (const file of files) {
|
|
18
|
+
// Skip files that we never want to process.
|
|
19
|
+
if (ALWAYS_IGNORE.some((p) => file.name === p)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// Skip hidden files (starting with .) except for some special ones
|
|
23
|
+
if (
|
|
24
|
+
file.name.startsWith(".") &&
|
|
25
|
+
!HIDDEN_FILES_TO_INCLUDE.some((p) => file.name === p)
|
|
26
|
+
) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
// TODO: follow symlinks??
|
|
11
30
|
if (file.isDirectory()) {
|
|
12
31
|
yield* await getFilesInFolder(path.join(dirPath, file.name));
|
|
13
32
|
} else {
|
|
@@ -16,27 +35,32 @@ async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
|
|
|
16
35
|
}
|
|
17
36
|
}
|
|
18
37
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Create a hash key for the given content using the xxhash algorithm.
|
|
40
|
+
*
|
|
41
|
+
* Note we only return the first 10 characters, since we will also include the file name in the asset manifest key
|
|
42
|
+
* the most important thing here is to detect changes of a single file to invalidate the cache and
|
|
43
|
+
* it's impossible to serve two different files with the same name
|
|
44
|
+
*/
|
|
45
|
+
function hashFileContent(content: string): string {
|
|
46
|
+
const hasher = new XXHash64();
|
|
47
|
+
hasher.update(Buffer.from(content));
|
|
48
|
+
const hash = hasher.digest();
|
|
49
|
+
return hash.toString("hex").substring(0, 10);
|
|
27
50
|
}
|
|
28
51
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Create a hashed asset key for the given asset.
|
|
54
|
+
*
|
|
55
|
+
* The key will change if the file path or content of the asset changes.
|
|
56
|
+
* The algorithm used here matches that of Wrangler 1.
|
|
57
|
+
*/
|
|
58
|
+
function hashAsset(filePath: string, content: string): string {
|
|
59
|
+
const extName = path.extname(filePath) || "";
|
|
34
60
|
const baseName = path.basename(filePath, extName);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
hash,
|
|
39
|
-
};
|
|
61
|
+
const directory = path.dirname(filePath);
|
|
62
|
+
const hash = hashFileContent(content);
|
|
63
|
+
return urlSafe(path.join(directory, `${baseName}.${hash}${extName}`));
|
|
40
64
|
}
|
|
41
65
|
|
|
42
66
|
async function createKVNamespaceIfNotAlreadyExisting(
|
|
@@ -70,13 +94,32 @@ async function createKVNamespaceIfNotAlreadyExisting(
|
|
|
70
94
|
};
|
|
71
95
|
}
|
|
72
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Upload the assets found within the `dirPath` directory to the sites assets KV namespace for
|
|
99
|
+
* the worker given by `scriptName`.
|
|
100
|
+
*
|
|
101
|
+
* @param accountId the account to upload to.
|
|
102
|
+
* @param scriptName the name of the worker whose assets we are uploading.
|
|
103
|
+
* @param siteAssets an objects describing what assets to upload, or undefined if there are no assets to upload.
|
|
104
|
+
* @param preview if true then upload to a "preview" KV namespace.
|
|
105
|
+
* @param _env (not implemented).
|
|
106
|
+
* @returns a promise for an object mapping the relative paths of the assets to the key of that
|
|
107
|
+
* asset in the KV namespace.
|
|
108
|
+
*/
|
|
73
109
|
export async function syncAssets(
|
|
74
110
|
accountId: string,
|
|
75
111
|
scriptName: string,
|
|
76
|
-
|
|
112
|
+
siteAssets: AssetPaths | undefined,
|
|
77
113
|
preview: boolean,
|
|
78
114
|
_env?: string
|
|
79
|
-
) {
|
|
115
|
+
): Promise<{
|
|
116
|
+
manifest: { [filePath: string]: string } | undefined;
|
|
117
|
+
namespace: string | undefined;
|
|
118
|
+
}> {
|
|
119
|
+
if (siteAssets === undefined) {
|
|
120
|
+
return { manifest: undefined, namespace: undefined };
|
|
121
|
+
}
|
|
122
|
+
|
|
80
123
|
const title = `__${scriptName}_sites_assets${preview ? "_preview" : ""}`;
|
|
81
124
|
const { id: namespace } = await createKVNamespaceIfNotAlreadyExisting(
|
|
82
125
|
title,
|
|
@@ -84,31 +127,117 @@ export async function syncAssets(
|
|
|
84
127
|
);
|
|
85
128
|
|
|
86
129
|
// let's get all the keys in this namespace
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
);
|
|
130
|
+
const result = await listNamespaceKeys(accountId, namespace);
|
|
131
|
+
const keys = new Set(result.map((x) => x.name));
|
|
90
132
|
|
|
91
133
|
const manifest = {};
|
|
92
|
-
const upload
|
|
134
|
+
const upload: {
|
|
135
|
+
key: string;
|
|
136
|
+
value: string;
|
|
137
|
+
base64: boolean;
|
|
138
|
+
}[] = [];
|
|
139
|
+
|
|
140
|
+
const include = createPatternMatcher(siteAssets.includePatterns, false);
|
|
141
|
+
const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
|
|
93
142
|
// TODO: this can be more efficient by parallelising
|
|
94
|
-
for await (const file of getFilesInFolder(
|
|
95
|
-
|
|
96
|
-
|
|
143
|
+
for await (const file of getFilesInFolder(siteAssets.baseDirectory)) {
|
|
144
|
+
if (!include(file)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (exclude(file)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await validateAssetSize(file);
|
|
152
|
+
console.log(`reading ${file}...`);
|
|
153
|
+
const content = await readFile(file, "base64");
|
|
154
|
+
|
|
155
|
+
const assetKey = hashAsset(file, content);
|
|
156
|
+
validateAssetKey(assetKey);
|
|
157
|
+
|
|
97
158
|
// now put each of the files into kv
|
|
98
|
-
if (!keys.has(
|
|
99
|
-
console.log(`uploading ${
|
|
100
|
-
const content = await readFile(file, "base64");
|
|
101
|
-
if (content.length > 25 * 1024 * 1024) {
|
|
102
|
-
throw new Error(`File ${file} is too big, it should be under 25 mb.`);
|
|
103
|
-
}
|
|
159
|
+
if (!keys.has(assetKey)) {
|
|
160
|
+
console.log(`uploading as ${assetKey}...`);
|
|
104
161
|
upload.push({
|
|
105
|
-
key:
|
|
162
|
+
key: assetKey,
|
|
106
163
|
value: content,
|
|
107
164
|
base64: true,
|
|
108
165
|
});
|
|
166
|
+
} else {
|
|
167
|
+
console.log(`skipping - already uploaded`);
|
|
109
168
|
}
|
|
110
|
-
manifest[path.relative(
|
|
169
|
+
manifest[path.relative(siteAssets.baseDirectory, file)] = assetKey;
|
|
111
170
|
}
|
|
112
171
|
await putBulkKeyValue(accountId, namespace, JSON.stringify(upload));
|
|
113
172
|
return { manifest, namespace };
|
|
114
173
|
}
|
|
174
|
+
|
|
175
|
+
function createPatternMatcher(
|
|
176
|
+
patterns: string[],
|
|
177
|
+
exclude: boolean
|
|
178
|
+
): (filePath: string) => boolean {
|
|
179
|
+
if (patterns.length === 0) {
|
|
180
|
+
return (_filePath) => !exclude;
|
|
181
|
+
} else {
|
|
182
|
+
const ignorer = ignore().add(patterns);
|
|
183
|
+
return (filePath) => ignorer.test(filePath).ignored;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function validateAssetSize(filePath: string) {
|
|
188
|
+
const { size } = await stat(filePath);
|
|
189
|
+
if (size > 25 * 1024 * 1024) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`File ${filePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validateAssetKey(assetKey: string) {
|
|
197
|
+
if (assetKey.length > 512) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`The asset path key "${assetKey}" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits",`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Convert a filePath to be safe to use as a relative URL.
|
|
206
|
+
*
|
|
207
|
+
* Primarily this involves converting Windows backslashes to forward slashes.
|
|
208
|
+
*/
|
|
209
|
+
function urlSafe(filePath: string): string {
|
|
210
|
+
return filePath.replace(/\\/g, "/");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Information about the assets that should be uploaded
|
|
215
|
+
*/
|
|
216
|
+
export interface AssetPaths {
|
|
217
|
+
baseDirectory: string;
|
|
218
|
+
includePatterns: string[];
|
|
219
|
+
excludePatterns: string[];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get an object that describes what assets to upload, if any.
|
|
224
|
+
*
|
|
225
|
+
* Uses the args (passed from the command line) if available,
|
|
226
|
+
* falling back to those defined in the config.
|
|
227
|
+
*
|
|
228
|
+
* // TODO: Support for environments
|
|
229
|
+
*/
|
|
230
|
+
export function getAssetPaths(
|
|
231
|
+
config: Config,
|
|
232
|
+
baseDirectory = config.site?.bucket,
|
|
233
|
+
includePatterns = config.site?.include ?? [],
|
|
234
|
+
excludePatterns = config.site?.exclude ?? []
|
|
235
|
+
): undefined | AssetPaths {
|
|
236
|
+
return baseDirectory
|
|
237
|
+
? {
|
|
238
|
+
baseDirectory,
|
|
239
|
+
includePatterns,
|
|
240
|
+
excludePatterns,
|
|
241
|
+
}
|
|
242
|
+
: undefined;
|
|
243
|
+
}
|