wrangler 0.0.0-e2f9bb2 → 0.0.0-e3278fa
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/Cloudflare_CA.pem +18 -0
- package/README.md +3 -1
- package/bin/wrangler.js +36 -4
- package/package.json +12 -7
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +14 -1
- package/pages/functions/template-plugin.ts +3 -2
- package/src/__tests__/configuration.test.ts +370 -87
- package/src/__tests__/dev.test.tsx +298 -77
- package/src/__tests__/helpers/mock-dialogs.ts +43 -2
- package/src/__tests__/index.test.ts +79 -72
- package/src/__tests__/init.test.ts +718 -123
- package/src/__tests__/jest.setup.ts +8 -0
- package/src/__tests__/kv.test.ts +173 -146
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +478 -53
- package/src/__tests__/parse.test.ts +8 -0
- package/src/__tests__/publish.test.ts +1094 -215
- package/src/__tests__/r2.test.ts +55 -36
- package/src/__tests__/secret.test.ts +39 -4
- package/src/__tests__/whoami.test.tsx +34 -0
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +34 -6
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +23 -18
- package/src/config/config.ts +2 -2
- package/src/config/environment.ts +40 -14
- package/src/config/index.ts +162 -0
- package/src/config/validation-helpers.ts +10 -1
- package/src/config/validation.ts +180 -65
- package/src/create-worker-preview.ts +32 -22
- package/src/create-worker-upload-form.ts +22 -8
- package/src/dev/dev.tsx +12 -18
- package/src/dev/local.tsx +10 -0
- package/src/dev/remote.tsx +40 -16
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/entry.ts +64 -9
- package/src/index.tsx +548 -317
- package/src/inspect.ts +39 -0
- package/src/kv.ts +114 -25
- package/src/open-in-browser.ts +5 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +434 -207
- package/src/parse.ts +23 -4
- package/src/proxy.ts +59 -35
- package/src/publish.ts +374 -127
- package/src/sites.tsx +61 -28
- package/src/user.tsx +23 -2
- package/src/whoami.tsx +3 -2
- package/src/worker.ts +10 -1
- package/src/zones.ts +73 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/templates/static-asset-facade.js +0 -4
- package/wrangler-dist/cli.js +96965 -83414
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
package/src/pages.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { execSync, spawn } from "node:child_process";
|
|
|
4
4
|
import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
|
-
import { dirname, join, sep } from "node:path";
|
|
7
|
+
import { dirname, join, sep, extname, basename } from "node:path";
|
|
8
8
|
import { cwd } from "node:process";
|
|
9
9
|
import { URL } from "node:url";
|
|
10
10
|
import { hash } from "blake3-wasm";
|
|
@@ -14,6 +14,7 @@ import SelectInput from "ink-select-input";
|
|
|
14
14
|
import Spinner from "ink-spinner";
|
|
15
15
|
import Table from "ink-table";
|
|
16
16
|
import { getType } from "mime";
|
|
17
|
+
import PQueue from "p-queue";
|
|
17
18
|
import prettyBytes from "pretty-bytes";
|
|
18
19
|
import React from "react";
|
|
19
20
|
import { format as timeagoFormat } from "timeago.js";
|
|
@@ -32,7 +33,11 @@ import openInBrowser from "./open-in-browser";
|
|
|
32
33
|
import { toUrlPath } from "./paths";
|
|
33
34
|
import { requireAuth } from "./user";
|
|
34
35
|
import type { Config } from "../pages/functions/routes";
|
|
35
|
-
import type {
|
|
36
|
+
import type {
|
|
37
|
+
Headers as MiniflareHeaders,
|
|
38
|
+
Request as MiniflareRequest,
|
|
39
|
+
fetch as miniflareFetch,
|
|
40
|
+
} from "@miniflare/core";
|
|
36
41
|
import type { BuildResult } from "esbuild";
|
|
37
42
|
import type { MiniflareOptions } from "miniflare";
|
|
38
43
|
import type { BuilderCallback, CommandModule } from "yargs";
|
|
@@ -68,12 +73,23 @@ export type Deployment = {
|
|
|
68
73
|
project_name: string;
|
|
69
74
|
};
|
|
70
75
|
|
|
76
|
+
export type UploadPayloadFile = {
|
|
77
|
+
key: string;
|
|
78
|
+
value: string;
|
|
79
|
+
metadata: { contentType: string };
|
|
80
|
+
base64: boolean;
|
|
81
|
+
};
|
|
82
|
+
|
|
71
83
|
interface PagesConfigCache {
|
|
72
84
|
account_id?: string;
|
|
73
85
|
project_name?: string;
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
|
|
89
|
+
const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
|
|
90
|
+
const MAX_BUCKET_FILE_COUNT = 5000;
|
|
91
|
+
const BULK_UPLOAD_CONCURRENCY = 3;
|
|
92
|
+
const MAX_UPLOAD_ATTEMPTS = 5;
|
|
77
93
|
|
|
78
94
|
// Defer importing miniflare until we really need it. This takes ~0.5s
|
|
79
95
|
// and also modifies some `stream/web` and `undici` prototypes, so we
|
|
@@ -82,6 +98,8 @@ const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
|
|
|
82
98
|
export const pagesBetaWarning =
|
|
83
99
|
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
|
|
84
100
|
|
|
101
|
+
const isInPagesCI = !!process.env.CF_PAGES;
|
|
102
|
+
|
|
85
103
|
const CLEANUP_CALLBACKS: (() => void)[] = [];
|
|
86
104
|
const CLEANUP = () => {
|
|
87
105
|
CLEANUP_CALLBACKS.forEach((callback) => callback());
|
|
@@ -282,7 +300,7 @@ function generateRulesMatcher<T>(
|
|
|
282
300
|
T
|
|
283
301
|
][];
|
|
284
302
|
|
|
285
|
-
return ({ request }: { request:
|
|
303
|
+
return ({ request }: { request: MiniflareRequest }) => {
|
|
286
304
|
const { pathname, host } = new URL(request.url);
|
|
287
305
|
|
|
288
306
|
return compiledRules
|
|
@@ -355,7 +373,7 @@ function generateHeadersMatcher(headersFile: string) {
|
|
|
355
373
|
)
|
|
356
374
|
);
|
|
357
375
|
|
|
358
|
-
return (request:
|
|
376
|
+
return (request: MiniflareRequest) => {
|
|
359
377
|
const matches = rulesMatcher({
|
|
360
378
|
request,
|
|
361
379
|
});
|
|
@@ -404,7 +422,7 @@ function generateRedirectsMatcher(redirectsFile: string) {
|
|
|
404
422
|
})
|
|
405
423
|
);
|
|
406
424
|
|
|
407
|
-
return (request:
|
|
425
|
+
return (request: MiniflareRequest) => {
|
|
408
426
|
const match = rulesMatcher({
|
|
409
427
|
request,
|
|
410
428
|
})[0];
|
|
@@ -459,7 +477,9 @@ function hasFileExtension(pathname: string) {
|
|
|
459
477
|
return /\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
460
478
|
}
|
|
461
479
|
|
|
462
|
-
async function generateAssetsFetch(
|
|
480
|
+
async function generateAssetsFetch(
|
|
481
|
+
directory: string
|
|
482
|
+
): Promise<typeof miniflareFetch> {
|
|
463
483
|
// Defer importing miniflare until we really need it
|
|
464
484
|
const { Headers, Request, Response } = await import("@miniflare/core");
|
|
465
485
|
|
|
@@ -508,12 +528,12 @@ async function generateAssetsFetch(directory: string): Promise<typeof fetch> {
|
|
|
508
528
|
return readFileSync(file);
|
|
509
529
|
};
|
|
510
530
|
|
|
511
|
-
const generateResponse = (request:
|
|
531
|
+
const generateResponse = (request: MiniflareRequest) => {
|
|
512
532
|
const url = new URL(request.url);
|
|
513
533
|
|
|
514
534
|
const deconstructedResponse: {
|
|
515
535
|
status: number;
|
|
516
|
-
headers:
|
|
536
|
+
headers: MiniflareHeaders;
|
|
517
537
|
body?: Buffer;
|
|
518
538
|
} = {
|
|
519
539
|
status: 200,
|
|
@@ -665,8 +685,12 @@ async function generateAssetsFetch(directory: string): Promise<typeof fetch> {
|
|
|
665
685
|
};
|
|
666
686
|
|
|
667
687
|
const attachHeaders = (
|
|
668
|
-
request:
|
|
669
|
-
deconstructedResponse: {
|
|
688
|
+
request: MiniflareRequest,
|
|
689
|
+
deconstructedResponse: {
|
|
690
|
+
status: number;
|
|
691
|
+
headers: MiniflareHeaders;
|
|
692
|
+
body?: Buffer;
|
|
693
|
+
}
|
|
670
694
|
) => {
|
|
671
695
|
const headers = deconstructedResponse.headers;
|
|
672
696
|
const newHeaders = new Headers({});
|
|
@@ -720,6 +744,7 @@ async function buildFunctions({
|
|
|
720
744
|
onEnd,
|
|
721
745
|
plugin = false,
|
|
722
746
|
buildOutputDirectory,
|
|
747
|
+
nodeCompat,
|
|
723
748
|
}: {
|
|
724
749
|
outfile: string;
|
|
725
750
|
outputConfigPath?: string;
|
|
@@ -731,12 +756,13 @@ async function buildFunctions({
|
|
|
731
756
|
onEnd?: () => void;
|
|
732
757
|
plugin?: boolean;
|
|
733
758
|
buildOutputDirectory?: string;
|
|
759
|
+
nodeCompat?: boolean;
|
|
734
760
|
}) {
|
|
735
761
|
RUNNING_BUILDERS.forEach(
|
|
736
762
|
(runningBuilder) => runningBuilder.stop && runningBuilder.stop()
|
|
737
763
|
);
|
|
738
764
|
|
|
739
|
-
const routesModule = join(tmpdir(),
|
|
765
|
+
const routesModule = join(tmpdir(), `./functionsRoutes-${Math.random()}.mjs`);
|
|
740
766
|
const baseURL = toUrlPath("/");
|
|
741
767
|
|
|
742
768
|
const config: Config = await generateConfigFromFileTree({
|
|
@@ -765,6 +791,7 @@ async function buildFunctions({
|
|
|
765
791
|
minify,
|
|
766
792
|
sourcemap,
|
|
767
793
|
watch,
|
|
794
|
+
nodeCompat,
|
|
768
795
|
onEnd,
|
|
769
796
|
})
|
|
770
797
|
);
|
|
@@ -779,6 +806,7 @@ async function buildFunctions({
|
|
|
779
806
|
watch,
|
|
780
807
|
onEnd,
|
|
781
808
|
buildOutputDirectory,
|
|
809
|
+
nodeCompat,
|
|
782
810
|
})
|
|
783
811
|
);
|
|
784
812
|
}
|
|
@@ -793,6 +821,299 @@ interface CreateDeploymentArgs {
|
|
|
793
821
|
commitDirty?: boolean;
|
|
794
822
|
}
|
|
795
823
|
|
|
824
|
+
const upload = async ({
|
|
825
|
+
directory,
|
|
826
|
+
accountId,
|
|
827
|
+
projectName,
|
|
828
|
+
}: {
|
|
829
|
+
directory: string;
|
|
830
|
+
accountId: string;
|
|
831
|
+
projectName: string;
|
|
832
|
+
}) => {
|
|
833
|
+
type FileContainer = {
|
|
834
|
+
content: string;
|
|
835
|
+
contentType: string;
|
|
836
|
+
sizeInBytes: number;
|
|
837
|
+
hash: string;
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const IGNORE_LIST = [
|
|
841
|
+
"_worker.js",
|
|
842
|
+
"_redirects",
|
|
843
|
+
"_headers",
|
|
844
|
+
".DS_Store",
|
|
845
|
+
"node_modules",
|
|
846
|
+
".git",
|
|
847
|
+
];
|
|
848
|
+
|
|
849
|
+
const walk = async (
|
|
850
|
+
dir: string,
|
|
851
|
+
fileMap: Map<string, FileContainer> = new Map(),
|
|
852
|
+
depth = 0
|
|
853
|
+
) => {
|
|
854
|
+
const files = await readdir(dir);
|
|
855
|
+
|
|
856
|
+
await Promise.all(
|
|
857
|
+
files.map(async (file) => {
|
|
858
|
+
const filepath = join(dir, file);
|
|
859
|
+
const filestat = await stat(filepath);
|
|
860
|
+
|
|
861
|
+
if (IGNORE_LIST.includes(file)) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (filestat.isSymbolicLink()) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (filestat.isDirectory()) {
|
|
870
|
+
fileMap = await walk(filepath, fileMap, depth + 1);
|
|
871
|
+
} else {
|
|
872
|
+
let name;
|
|
873
|
+
if (depth) {
|
|
874
|
+
name = filepath.split(sep).slice(1).join("/");
|
|
875
|
+
} else {
|
|
876
|
+
name = file;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// TODO: Move this to later so we don't hold as much in memory
|
|
880
|
+
const fileContent = await readFile(filepath);
|
|
881
|
+
|
|
882
|
+
const base64Content = fileContent.toString("base64");
|
|
883
|
+
const extension = extname(basename(name)).substring(1);
|
|
884
|
+
|
|
885
|
+
if (filestat.size > 25 * 1024 * 1024) {
|
|
886
|
+
throw new Error(
|
|
887
|
+
`Error: Pages only supports files up to ${prettyBytes(
|
|
888
|
+
25 * 1024 * 1024
|
|
889
|
+
)} in size\n${name} is ${prettyBytes(filestat.size)} in size`
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
fileMap.set(name, {
|
|
894
|
+
content: base64Content,
|
|
895
|
+
contentType: getType(name) || "application/octet-stream",
|
|
896
|
+
sizeInBytes: filestat.size,
|
|
897
|
+
hash: hash(base64Content + extension)
|
|
898
|
+
.toString("hex")
|
|
899
|
+
.slice(0, 32),
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
})
|
|
903
|
+
);
|
|
904
|
+
|
|
905
|
+
return fileMap;
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
const fileMap = await walk(directory);
|
|
909
|
+
|
|
910
|
+
if (fileMap.size > 20000) {
|
|
911
|
+
throw new FatalError(
|
|
912
|
+
`Error: Pages only supports up to 20,000 files in a deployment. Ensure you have specified your build output directory correctly.`,
|
|
913
|
+
1
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const files = [...fileMap.values()];
|
|
918
|
+
|
|
919
|
+
async function fetchJwt(): Promise<string> {
|
|
920
|
+
return (
|
|
921
|
+
await fetchResult<{ jwt: string }>(
|
|
922
|
+
`/accounts/${accountId}/pages/projects/${projectName}/upload-token`
|
|
923
|
+
)
|
|
924
|
+
).jwt;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
let jwt = await fetchJwt();
|
|
928
|
+
|
|
929
|
+
const start = Date.now();
|
|
930
|
+
|
|
931
|
+
const missingHashes = await fetchResult<string[]>(
|
|
932
|
+
`/pages/assets/check-missing`,
|
|
933
|
+
{
|
|
934
|
+
method: "POST",
|
|
935
|
+
headers: {
|
|
936
|
+
"Content-Type": "application/json",
|
|
937
|
+
Authorization: `Bearer ${jwt}`,
|
|
938
|
+
},
|
|
939
|
+
body: JSON.stringify({
|
|
940
|
+
hashes: files.map(({ hash }) => hash),
|
|
941
|
+
}),
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
const sortedFiles = files
|
|
946
|
+
.filter((file) => missingHashes.includes(file.hash))
|
|
947
|
+
.sort((a, b) => b.sizeInBytes - a.sizeInBytes);
|
|
948
|
+
|
|
949
|
+
// Start with a few buckets so small projects still get
|
|
950
|
+
// the benefit of multiple upload streams
|
|
951
|
+
const buckets: {
|
|
952
|
+
files: FileContainer[];
|
|
953
|
+
remainingSize: number;
|
|
954
|
+
}[] = new Array(BULK_UPLOAD_CONCURRENCY).fill(null).map(() => ({
|
|
955
|
+
files: [],
|
|
956
|
+
remainingSize: MAX_BUCKET_SIZE,
|
|
957
|
+
}));
|
|
958
|
+
|
|
959
|
+
let bucketOffset = 0;
|
|
960
|
+
for (const file of sortedFiles) {
|
|
961
|
+
let inserted = false;
|
|
962
|
+
|
|
963
|
+
for (let i = 0; i < buckets.length; i++) {
|
|
964
|
+
// Start at a different bucket for each new file
|
|
965
|
+
const bucket = buckets[(i + bucketOffset) % buckets.length];
|
|
966
|
+
if (
|
|
967
|
+
bucket.remainingSize >= file.sizeInBytes &&
|
|
968
|
+
bucket.files.length < MAX_BUCKET_FILE_COUNT
|
|
969
|
+
) {
|
|
970
|
+
bucket.files.push(file);
|
|
971
|
+
bucket.remainingSize -= file.sizeInBytes;
|
|
972
|
+
inserted = true;
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
if (!inserted) {
|
|
978
|
+
buckets.push({
|
|
979
|
+
files: [file],
|
|
980
|
+
remainingSize: MAX_BUCKET_SIZE - file.sizeInBytes,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
bucketOffset++;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
let counter = fileMap.size - sortedFiles.length;
|
|
987
|
+
const { rerender, unmount } = render(
|
|
988
|
+
<Progress done={counter} total={fileMap.size} />
|
|
989
|
+
);
|
|
990
|
+
|
|
991
|
+
const queue = new PQueue({ concurrency: BULK_UPLOAD_CONCURRENCY });
|
|
992
|
+
|
|
993
|
+
for (const bucket of buckets) {
|
|
994
|
+
// Don't upload empty buckets (can happen for tiny projects)
|
|
995
|
+
if (bucket.files.length === 0) continue;
|
|
996
|
+
|
|
997
|
+
const payload: UploadPayloadFile[] = bucket.files.map((file) => ({
|
|
998
|
+
key: file.hash,
|
|
999
|
+
value: file.content,
|
|
1000
|
+
metadata: {
|
|
1001
|
+
contentType: file.contentType,
|
|
1002
|
+
},
|
|
1003
|
+
base64: true,
|
|
1004
|
+
}));
|
|
1005
|
+
|
|
1006
|
+
let attempts = 0;
|
|
1007
|
+
const doUpload = async (): Promise<void> => {
|
|
1008
|
+
try {
|
|
1009
|
+
return await fetchResult(`/pages/assets/upload`, {
|
|
1010
|
+
method: "POST",
|
|
1011
|
+
headers: {
|
|
1012
|
+
"Content-Type": "application/json",
|
|
1013
|
+
Authorization: `Bearer ${jwt}`,
|
|
1014
|
+
},
|
|
1015
|
+
body: JSON.stringify(payload),
|
|
1016
|
+
});
|
|
1017
|
+
} catch (e) {
|
|
1018
|
+
if (attempts < MAX_UPLOAD_ATTEMPTS) {
|
|
1019
|
+
// Linear backoff, 0 second first time, then 1 second etc.
|
|
1020
|
+
await new Promise((resolve) =>
|
|
1021
|
+
setTimeout(resolve, attempts++ * 1000)
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
if ((e as { code: number }).code === 8000013) {
|
|
1025
|
+
// Looks like the JWT expired, fetch another one
|
|
1026
|
+
jwt = await fetchJwt();
|
|
1027
|
+
}
|
|
1028
|
+
return doUpload();
|
|
1029
|
+
} else {
|
|
1030
|
+
throw e;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
queue.add(() =>
|
|
1036
|
+
doUpload().then(
|
|
1037
|
+
() => {
|
|
1038
|
+
counter += bucket.files.length;
|
|
1039
|
+
rerender(<Progress done={counter} total={fileMap.size} />);
|
|
1040
|
+
},
|
|
1041
|
+
(error) => {
|
|
1042
|
+
return Promise.reject(
|
|
1043
|
+
new FatalError(
|
|
1044
|
+
"Failed to upload files. Please try again.",
|
|
1045
|
+
error.code || 1
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
)
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
await queue.onIdle();
|
|
1054
|
+
|
|
1055
|
+
unmount();
|
|
1056
|
+
|
|
1057
|
+
const uploadMs = Date.now() - start;
|
|
1058
|
+
|
|
1059
|
+
const skipped = fileMap.size - missingHashes.length;
|
|
1060
|
+
const skippedMessage = skipped > 0 ? `(${skipped} already uploaded) ` : "";
|
|
1061
|
+
|
|
1062
|
+
logger.log(
|
|
1063
|
+
`✨ Success! Uploaded ${
|
|
1064
|
+
sortedFiles.length
|
|
1065
|
+
} files ${skippedMessage}${formatTime(uploadMs)}\n`
|
|
1066
|
+
);
|
|
1067
|
+
|
|
1068
|
+
const doUpsertHashes = async (): Promise<void> => {
|
|
1069
|
+
try {
|
|
1070
|
+
return await fetchResult(`/pages/assets/upsert-hashes`, {
|
|
1071
|
+
method: "POST",
|
|
1072
|
+
headers: {
|
|
1073
|
+
"Content-Type": "application/json",
|
|
1074
|
+
Authorization: `Bearer ${jwt}`,
|
|
1075
|
+
},
|
|
1076
|
+
body: JSON.stringify({
|
|
1077
|
+
hashes: files.map(({ hash }) => hash),
|
|
1078
|
+
}),
|
|
1079
|
+
});
|
|
1080
|
+
} catch (e) {
|
|
1081
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1082
|
+
|
|
1083
|
+
if ((e as { code: number }).code === 8000013) {
|
|
1084
|
+
// Looks like the JWT expired, fetch another one
|
|
1085
|
+
jwt = await fetchJwt();
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
return await fetchResult(`/pages/assets/upsert-hashes`, {
|
|
1089
|
+
method: "POST",
|
|
1090
|
+
headers: {
|
|
1091
|
+
"Content-Type": "application/json",
|
|
1092
|
+
Authorization: `Bearer ${jwt}`,
|
|
1093
|
+
},
|
|
1094
|
+
body: JSON.stringify({
|
|
1095
|
+
hashes: files.map(({ hash }) => hash),
|
|
1096
|
+
}),
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
try {
|
|
1102
|
+
await doUpsertHashes();
|
|
1103
|
+
} catch {
|
|
1104
|
+
logger.warn(
|
|
1105
|
+
"Failed to update file hashes. Every upload appeared to succeed for this deployment, but you might need to re-upload for future deployments. This shouldn't have any impact other than slowing the upload speed of your next deployment."
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
return Object.fromEntries(
|
|
1110
|
+
[...fileMap.entries()].map(([fileName, file]) => [
|
|
1111
|
+
`/${fileName}`,
|
|
1112
|
+
file.hash,
|
|
1113
|
+
])
|
|
1114
|
+
);
|
|
1115
|
+
};
|
|
1116
|
+
|
|
796
1117
|
const createDeployment: CommandModule<
|
|
797
1118
|
CreateDeploymentArgs,
|
|
798
1119
|
CreateDeploymentArgs
|
|
@@ -802,34 +1123,30 @@ const createDeployment: CommandModule<
|
|
|
802
1123
|
return yargs
|
|
803
1124
|
.positional("directory", {
|
|
804
1125
|
type: "string",
|
|
805
|
-
|
|
806
|
-
description: "The directory of
|
|
1126
|
+
demandOption: true,
|
|
1127
|
+
description: "The directory of static files to upload",
|
|
807
1128
|
})
|
|
808
1129
|
.options({
|
|
809
1130
|
"project-name": {
|
|
810
1131
|
type: "string",
|
|
811
|
-
description:
|
|
812
|
-
"The name of the project you want to list deployments for",
|
|
1132
|
+
description: "The name of the project you want to deploy to",
|
|
813
1133
|
},
|
|
814
1134
|
branch: {
|
|
815
1135
|
type: "string",
|
|
816
|
-
description:
|
|
817
|
-
"The branch of the project you want to list deployments for",
|
|
1136
|
+
description: "The name of the branch you want to deploy to",
|
|
818
1137
|
},
|
|
819
1138
|
"commit-hash": {
|
|
820
1139
|
type: "string",
|
|
821
|
-
description:
|
|
822
|
-
"The branch of the project you want to list deployments for",
|
|
1140
|
+
description: "The SHA to attach to this deployment",
|
|
823
1141
|
},
|
|
824
1142
|
"commit-message": {
|
|
825
1143
|
type: "string",
|
|
826
|
-
description:
|
|
827
|
-
"The branch of the project you want to list deployments for",
|
|
1144
|
+
description: "The commit message to attach to this deployment",
|
|
828
1145
|
},
|
|
829
1146
|
"commit-dirty": {
|
|
830
1147
|
type: "boolean",
|
|
831
1148
|
description:
|
|
832
|
-
"
|
|
1149
|
+
"Whether or not the workspace should be considered dirty for this deployment",
|
|
833
1150
|
},
|
|
834
1151
|
})
|
|
835
1152
|
.epilogue(pagesBetaWarning);
|
|
@@ -842,6 +1159,10 @@ const createDeployment: CommandModule<
|
|
|
842
1159
|
commitMessage,
|
|
843
1160
|
commitDirty,
|
|
844
1161
|
}) => {
|
|
1162
|
+
if (!directory) {
|
|
1163
|
+
throw new FatalError("Must specify a directory.", 1);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
845
1166
|
const config = getConfigCache<PagesConfigCache>(
|
|
846
1167
|
PAGES_CONFIG_CACHE_FILENAME
|
|
847
1168
|
);
|
|
@@ -851,40 +1172,45 @@ const createDeployment: CommandModule<
|
|
|
851
1172
|
|
|
852
1173
|
const isInteractive = process.stdin.isTTY;
|
|
853
1174
|
if (!projectName && isInteractive) {
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1175
|
+
const projects = (await listProjects({ accountId })).filter(
|
|
1176
|
+
(project) => !project.source
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
let existingOrNew: "existing" | "new" = "new";
|
|
1180
|
+
|
|
1181
|
+
if (projects.length > 0) {
|
|
1182
|
+
existingOrNew = await new Promise<"new" | "existing">((resolve) => {
|
|
1183
|
+
const { unmount } = render(
|
|
1184
|
+
<>
|
|
1185
|
+
<Text>
|
|
1186
|
+
No project selected. Would you like to create one or use an
|
|
1187
|
+
existing project?
|
|
1188
|
+
</Text>
|
|
1189
|
+
<SelectInput
|
|
1190
|
+
items={[
|
|
1191
|
+
{
|
|
1192
|
+
key: "new",
|
|
1193
|
+
label: "Create a new project",
|
|
1194
|
+
value: "new",
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
key: "existing",
|
|
1198
|
+
label: "Use an existing project",
|
|
1199
|
+
value: "existing",
|
|
1200
|
+
},
|
|
1201
|
+
]}
|
|
1202
|
+
onSelect={async (selected) => {
|
|
1203
|
+
resolve(selected.value as "new" | "existing");
|
|
1204
|
+
unmount();
|
|
1205
|
+
}}
|
|
1206
|
+
/>
|
|
1207
|
+
</>
|
|
1208
|
+
);
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
882
1211
|
|
|
883
1212
|
switch (existingOrNew) {
|
|
884
1213
|
case "existing": {
|
|
885
|
-
const projects = (await listProjects({ accountId })).filter(
|
|
886
|
-
(project) => !project.source
|
|
887
|
-
);
|
|
888
1214
|
projectName = await new Promise((resolve) => {
|
|
889
1215
|
const { unmount } = render(
|
|
890
1216
|
<>
|
|
@@ -994,7 +1320,7 @@ const createDeployment: CommandModule<
|
|
|
994
1320
|
|
|
995
1321
|
if (isGitDirty && !commitDirty) {
|
|
996
1322
|
logger.warn(
|
|
997
|
-
`Warning: Your working directory is a git repo and has uncommitted changes\nTo
|
|
1323
|
+
`Warning: Your working directory is a git repo and has uncommitted changes\nTo silence this warning, pass in --commit-dirty=true`
|
|
998
1324
|
);
|
|
999
1325
|
}
|
|
1000
1326
|
|
|
@@ -1006,7 +1332,7 @@ const createDeployment: CommandModule<
|
|
|
1006
1332
|
let builtFunctions: string | undefined = undefined;
|
|
1007
1333
|
const functionsDirectory = join(cwd(), "functions");
|
|
1008
1334
|
if (existsSync(functionsDirectory)) {
|
|
1009
|
-
const outfile = join(tmpdir(),
|
|
1335
|
+
const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
|
|
1010
1336
|
|
|
1011
1337
|
await new Promise((resolve) =>
|
|
1012
1338
|
buildFunctions({
|
|
@@ -1020,154 +1346,11 @@ const createDeployment: CommandModule<
|
|
|
1020
1346
|
builtFunctions = readFileSync(outfile, "utf-8");
|
|
1021
1347
|
}
|
|
1022
1348
|
|
|
1023
|
-
|
|
1024
|
-
content: Buffer;
|
|
1025
|
-
metadata: Metadata;
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
|
-
type Metadata = {
|
|
1029
|
-
sizeInBytes: number;
|
|
1030
|
-
hash: string;
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
const IGNORE_LIST = [
|
|
1034
|
-
"_worker.js",
|
|
1035
|
-
"_redirects",
|
|
1036
|
-
"_headers",
|
|
1037
|
-
".DS_Store",
|
|
1038
|
-
"node_modules",
|
|
1039
|
-
];
|
|
1040
|
-
|
|
1041
|
-
const walk = async (
|
|
1042
|
-
dir: string,
|
|
1043
|
-
fileMap: Map<string, File> = new Map(),
|
|
1044
|
-
depth = 0
|
|
1045
|
-
) => {
|
|
1046
|
-
const files = await readdir(dir);
|
|
1047
|
-
|
|
1048
|
-
await Promise.all(
|
|
1049
|
-
files.map(async (file) => {
|
|
1050
|
-
const filepath = join(dir, file);
|
|
1051
|
-
const filestat = await stat(filepath);
|
|
1052
|
-
|
|
1053
|
-
if (IGNORE_LIST.includes(file)) {
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
if (filestat.isSymbolicLink()) {
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
if (filestat.isDirectory()) {
|
|
1062
|
-
fileMap = await walk(filepath, fileMap, depth + 1);
|
|
1063
|
-
} else {
|
|
1064
|
-
let name;
|
|
1065
|
-
if (depth) {
|
|
1066
|
-
name = filepath.split(sep).slice(1).join("/");
|
|
1067
|
-
} else {
|
|
1068
|
-
name = file;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// TODO: Move this to later so we don't hold as much in memory
|
|
1072
|
-
const fileContent = await readFile(filepath);
|
|
1073
|
-
|
|
1074
|
-
const base64Content = fileContent.toString("base64");
|
|
1075
|
-
const extension =
|
|
1076
|
-
name.split(".").length > 1 ? name.split(".").at(-1) || "" : "";
|
|
1077
|
-
|
|
1078
|
-
const content = base64Content + extension;
|
|
1079
|
-
|
|
1080
|
-
if (filestat.size > 25 * 1024 * 1024) {
|
|
1081
|
-
throw new Error(
|
|
1082
|
-
`Error: Pages only supports files up to ${prettyBytes(
|
|
1083
|
-
25 * 1024 * 1024
|
|
1084
|
-
)} in size\n${name} is ${prettyBytes(filestat.size)} in size`
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
fileMap.set(name, {
|
|
1089
|
-
content: fileContent,
|
|
1090
|
-
metadata: {
|
|
1091
|
-
sizeInBytes: filestat.size,
|
|
1092
|
-
hash: hash(content).toString("hex").slice(0, 32),
|
|
1093
|
-
},
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
})
|
|
1097
|
-
);
|
|
1098
|
-
|
|
1099
|
-
return fileMap;
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
const fileMap = await walk(directory);
|
|
1103
|
-
|
|
1104
|
-
const start = Date.now();
|
|
1105
|
-
|
|
1106
|
-
const files: Array<Promise<void>> = [];
|
|
1107
|
-
|
|
1108
|
-
if (fileMap.size > 1000) {
|
|
1109
|
-
throw new Error(
|
|
1110
|
-
`Error: Pages only supports up to 1,000 files in a deployment at the moment.\nTry a smaller project perhaps?`
|
|
1111
|
-
);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
let counter = 0;
|
|
1115
|
-
|
|
1116
|
-
const { rerender, unmount } = render(
|
|
1117
|
-
<Progress done={counter} total={fileMap.size} />
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
fileMap.forEach((file: File, name: string) => {
|
|
1121
|
-
const form = new FormData();
|
|
1122
|
-
form.append(
|
|
1123
|
-
"file",
|
|
1124
|
-
new File([new Uint8Array(file.content.buffer)], name)
|
|
1125
|
-
);
|
|
1126
|
-
|
|
1127
|
-
// TODO: Consider a retry
|
|
1128
|
-
|
|
1129
|
-
const promise = fetchResult<{ id: string }>(
|
|
1130
|
-
`/accounts/${accountId}/pages/projects/${projectName}/file`,
|
|
1131
|
-
{
|
|
1132
|
-
method: "POST",
|
|
1133
|
-
body: form,
|
|
1134
|
-
}
|
|
1135
|
-
).then((response) => {
|
|
1136
|
-
counter++;
|
|
1137
|
-
rerender(<Progress done={counter} total={fileMap.size} />);
|
|
1138
|
-
if (response.id != file.metadata.hash) {
|
|
1139
|
-
throw new Error(
|
|
1140
|
-
`Looks like there was an issue uploading '${name}'. Try again perhaps?`
|
|
1141
|
-
);
|
|
1142
|
-
}
|
|
1143
|
-
});
|
|
1144
|
-
|
|
1145
|
-
files.push(promise);
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
await Promise.all(files);
|
|
1149
|
-
|
|
1150
|
-
unmount();
|
|
1151
|
-
|
|
1152
|
-
const uploadMs = Date.now() - start;
|
|
1153
|
-
|
|
1154
|
-
logger.log(
|
|
1155
|
-
`✨ Success! Uploaded ${fileMap.size} files ${formatTime(uploadMs)}\n`
|
|
1156
|
-
);
|
|
1349
|
+
const manifest = await upload({ directory, accountId, projectName });
|
|
1157
1350
|
|
|
1158
1351
|
const formData = new FormData();
|
|
1159
1352
|
|
|
1160
|
-
formData.append(
|
|
1161
|
-
"manifest",
|
|
1162
|
-
JSON.stringify(
|
|
1163
|
-
Object.fromEntries(
|
|
1164
|
-
[...fileMap.entries()].map(([fileName, file]) => [
|
|
1165
|
-
`/${fileName}`,
|
|
1166
|
-
file.metadata.hash,
|
|
1167
|
-
])
|
|
1168
|
-
)
|
|
1169
|
-
)
|
|
1170
|
-
);
|
|
1353
|
+
formData.append("manifest", JSON.stringify(manifest));
|
|
1171
1354
|
|
|
1172
1355
|
if (branch) {
|
|
1173
1356
|
formData.append("branch", branch);
|
|
@@ -1237,7 +1420,7 @@ const createDeployment: CommandModule<
|
|
|
1237
1420
|
export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
1238
1421
|
return yargs
|
|
1239
1422
|
.command(
|
|
1240
|
-
"dev [directory] [-- command]",
|
|
1423
|
+
"dev [directory] [-- command..]",
|
|
1241
1424
|
"🧑💻 Develop your full-stack Pages application locally",
|
|
1242
1425
|
(yargs) => {
|
|
1243
1426
|
return yargs
|
|
@@ -1293,6 +1476,12 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1293
1476
|
default: false,
|
|
1294
1477
|
description: "Auto reload HTML pages when change is detected",
|
|
1295
1478
|
},
|
|
1479
|
+
"node-compat": {
|
|
1480
|
+
describe: "Enable node.js compatibility",
|
|
1481
|
+
default: false,
|
|
1482
|
+
type: "boolean",
|
|
1483
|
+
hidden: true,
|
|
1484
|
+
},
|
|
1296
1485
|
// TODO: Miniflare user options
|
|
1297
1486
|
})
|
|
1298
1487
|
.epilogue(pagesBetaWarning);
|
|
@@ -1307,6 +1496,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1307
1496
|
kv: kvs = [],
|
|
1308
1497
|
do: durableObjects = [],
|
|
1309
1498
|
"live-reload": liveReload,
|
|
1499
|
+
"node-compat": nodeCompat,
|
|
1310
1500
|
_: [_pages, _dev, ...remaining],
|
|
1311
1501
|
}) => {
|
|
1312
1502
|
// Beta message for `wrangler pages <commands>` usage
|
|
@@ -1340,7 +1530,16 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1340
1530
|
);
|
|
1341
1531
|
|
|
1342
1532
|
if (usingFunctions) {
|
|
1343
|
-
const outfile = join(
|
|
1533
|
+
const outfile = join(
|
|
1534
|
+
tmpdir(),
|
|
1535
|
+
`./functionsWorker-${Math.random()}.js`
|
|
1536
|
+
);
|
|
1537
|
+
|
|
1538
|
+
if (nodeCompat) {
|
|
1539
|
+
console.warn(
|
|
1540
|
+
"Enabling node.js compatibility mode for builtins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1344
1543
|
|
|
1345
1544
|
logger.log(`Compiling worker to "${outfile}"...`);
|
|
1346
1545
|
|
|
@@ -1352,6 +1551,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1352
1551
|
watch: true,
|
|
1353
1552
|
onEnd: () => scriptReadyResolve(),
|
|
1354
1553
|
buildOutputDirectory: directory,
|
|
1554
|
+
nodeCompat,
|
|
1355
1555
|
});
|
|
1356
1556
|
} catch {}
|
|
1357
1557
|
|
|
@@ -1366,6 +1566,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1366
1566
|
watch: true,
|
|
1367
1567
|
onEnd: () => scriptReadyResolve(),
|
|
1368
1568
|
buildOutputDirectory: directory,
|
|
1569
|
+
nodeCompat,
|
|
1369
1570
|
});
|
|
1370
1571
|
});
|
|
1371
1572
|
|
|
@@ -1388,6 +1589,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1388
1589
|
} else {
|
|
1389
1590
|
logger.log("No functions. Shimming...");
|
|
1390
1591
|
miniflareArgs = {
|
|
1592
|
+
// cfFetch sets the `cf` object that a function could expect
|
|
1593
|
+
// If there are no functions, there's no reason to set this up (and not make that network call)
|
|
1594
|
+
cfFetch: false,
|
|
1391
1595
|
// TODO: The fact that these request/response hacks are necessary is ridiculous.
|
|
1392
1596
|
// We need to eliminate them from env.ASSETS.fetch (not sure if just local or prod as well)
|
|
1393
1597
|
script: `
|
|
@@ -1449,7 +1653,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1449
1653
|
|
|
1450
1654
|
// env.ASSETS.fetch
|
|
1451
1655
|
serviceBindings: {
|
|
1452
|
-
async ASSETS(request:
|
|
1656
|
+
async ASSETS(request: MiniflareRequest) {
|
|
1453
1657
|
if (proxyPort) {
|
|
1454
1658
|
try {
|
|
1455
1659
|
const url = new URL(request.url);
|
|
@@ -1569,6 +1773,16 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1569
1773
|
default: false,
|
|
1570
1774
|
description: "Build a plugin rather than a Worker script",
|
|
1571
1775
|
},
|
|
1776
|
+
"build-output-directory": {
|
|
1777
|
+
type: "string",
|
|
1778
|
+
description: "The directory to output static assets to",
|
|
1779
|
+
},
|
|
1780
|
+
"node-compat": {
|
|
1781
|
+
describe: "Enable node.js compatibility",
|
|
1782
|
+
default: false,
|
|
1783
|
+
type: "boolean",
|
|
1784
|
+
hidden: true,
|
|
1785
|
+
},
|
|
1572
1786
|
})
|
|
1573
1787
|
.epilogue(pagesBetaWarning),
|
|
1574
1788
|
async ({
|
|
@@ -1580,9 +1794,21 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1580
1794
|
fallbackService,
|
|
1581
1795
|
watch,
|
|
1582
1796
|
plugin,
|
|
1797
|
+
"build-output-directory": buildOutputDirectory,
|
|
1798
|
+
"node-compat": nodeCompat,
|
|
1583
1799
|
}) => {
|
|
1584
|
-
|
|
1585
|
-
|
|
1800
|
+
if (!isInPagesCI) {
|
|
1801
|
+
// Beta message for `wrangler pages <commands>` usage
|
|
1802
|
+
logger.log(pagesBetaWarning);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
if (nodeCompat) {
|
|
1806
|
+
console.warn(
|
|
1807
|
+
"Enabling node.js compatibility mode for builtins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
buildOutputDirectory ??= dirname(outfile);
|
|
1586
1812
|
|
|
1587
1813
|
await buildFunctions({
|
|
1588
1814
|
outfile,
|
|
@@ -1593,7 +1819,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1593
1819
|
fallbackService,
|
|
1594
1820
|
watch,
|
|
1595
1821
|
plugin,
|
|
1596
|
-
buildOutputDirectory
|
|
1822
|
+
buildOutputDirectory,
|
|
1823
|
+
nodeCompat,
|
|
1597
1824
|
});
|
|
1598
1825
|
}
|
|
1599
1826
|
)
|
|
@@ -1818,7 +2045,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
1818
2045
|
} as CommandModule);
|
|
1819
2046
|
};
|
|
1820
2047
|
|
|
1821
|
-
const invalidAssetsFetch: typeof
|
|
2048
|
+
const invalidAssetsFetch: typeof miniflareFetch = () => {
|
|
1822
2049
|
throw new Error(
|
|
1823
2050
|
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
|
|
1824
2051
|
);
|