wrangler 2.1.13 → 2.1.15

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.
@@ -0,0 +1,217 @@
1
+ import * as fs from "fs";
2
+ import * as TOML from "@iarna/toml";
3
+ import { mockConsoleMethods } from "./helpers/mock-console";
4
+ import { runInTempDir } from "./helpers/run-in-tmp";
5
+ import { runWrangler } from "./helpers/run-wrangler";
6
+ import type { Config } from "../config";
7
+ import type { CfWorkerInit } from "../worker";
8
+
9
+ const bindingsConfigMock: CfWorkerInit["bindings"] & {
10
+ rules: Config["rules"];
11
+ } = {
12
+ kv_namespaces: [{ binding: "TEST_KV_NAMESPACE", id: "1234" }],
13
+ vars: {
14
+ SOMETHING: "asdasdfasdf",
15
+ ANOTHER: "thing",
16
+ OBJECT_VAR: {
17
+ enterprise: "1701-D",
18
+ activeDuty: true,
19
+ captian: "Picard",
20
+ }, // We can assume the objects will be stringified
21
+ },
22
+ durable_objects: {
23
+ bindings: [
24
+ { name: "DURABLE_TEST1", class_name: "Durability1" },
25
+ { name: "DURABLE_TEST2", class_name: "Durability2" },
26
+ ],
27
+ },
28
+ r2_buckets: [
29
+ {
30
+ binding: "R2_BUCKET_BINDING",
31
+ bucket_name: "R2BUCKET_NAME_TEST",
32
+ },
33
+ ],
34
+ d1_databases: [
35
+ {
36
+ // @ts-expect-error This type is resolved in the function that handles creating BETA string
37
+ binding: "D1_TESTING_SOMETHING",
38
+ database_name: "D1_BINDING",
39
+ database_id: "1234",
40
+ },
41
+ ],
42
+ services: [{ binding: "SERVICE_BINDING", service: "SERVICE_NAME" }],
43
+ dispatch_namespaces: [
44
+ { binding: "NAMESPACE_BINDING", namespace: "NAMESPACE_ID" },
45
+ ],
46
+ logfwdr: {
47
+ schema: "LOGFWDER_SCHEMA",
48
+ bindings: [{ name: "LOGFWDR_BINDING", destination: "LOGFWDR_DESTINATION" }],
49
+ },
50
+ data_blobs: {
51
+ SOME_DATA_BLOB1: "SOME_DATA_BLOB1.bin",
52
+ SOME_DATA_BLOB2: "SOME_DATA_BLOB2.bin",
53
+ },
54
+ text_blobs: {
55
+ SOME_TEXT_BLOB1: "SOME_TEXT_BLOB1.txt",
56
+ SOME_TEXT_BLOB2: "SOME_TEXT_BLOB2.txt",
57
+ },
58
+ wasm_modules: { MODULE1: "module1.wasm", MODULE2: "module2.wasm" },
59
+ unsafe: [
60
+ {
61
+ // @ts-expect-error Unsafe bindings type is somewhat different in different places
62
+ bindings: [{ name: "testing_unsafe", type: "plain_text" }],
63
+ },
64
+ ],
65
+ rules: [
66
+ {
67
+ type: "Text",
68
+ globs: ["**/*.txt"],
69
+ fallthrough: true,
70
+ },
71
+ {
72
+ type: "Data",
73
+ globs: ["**/*.webp"],
74
+ fallthrough: true,
75
+ },
76
+ { type: "CompiledWasm", globs: ["**/*.wasm"], fallthrough: true },
77
+ ],
78
+ };
79
+
80
+ describe("generateTypes()", () => {
81
+ const std = mockConsoleMethods();
82
+ runInTempDir();
83
+
84
+ it("should log the interface type generated and declare modules", async () => {
85
+ fs.writeFileSync("./index.ts", "export default { async fetch () {} };");
86
+ fs.writeFileSync(
87
+ "./wrangler.toml",
88
+ TOML.stringify({
89
+ compatibility_date: "2022-01-12",
90
+ name: "test-name",
91
+ main: "./index.ts",
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ ...(bindingsConfigMock as any),
94
+ unsafe: bindingsConfigMock.unsafe?.at(0) ?? {},
95
+ }),
96
+ "utf-8"
97
+ );
98
+
99
+ await runWrangler("types");
100
+ expect(std.out).toMatchInlineSnapshot(`
101
+ "interface Env {
102
+ TEST_KV_NAMESPACE: KVNamespace;
103
+ SOMETHING: asdasdfasdf;
104
+ ANOTHER: thing;
105
+ OBJECT_VAR: {\\"enterprise\\":\\"1701-D\\",\\"activeDuty\\":true,\\"captian\\":\\"Picard\\"};
106
+ DURABLE_TEST1: DurableObjectNamespace;
107
+ DURABLE_TEST2: DurableObjectNamespace;
108
+ R2_BUCKET_BINDING: R2Bucket;
109
+ D1_TESTING_SOMETHING: D1Database;
110
+ SERVICE_BINDING: Fetcher;
111
+ NAMESPACE_BINDING: any;
112
+ LOGFWDR_SCHEMA: any;
113
+ SOME_DATA_BLOB1: ArrayBuffer;
114
+ SOME_DATA_BLOB2: ArrayBuffer;
115
+ SOME_TEXT_BLOB1: string;
116
+ SOME_TEXT_BLOB2: string;
117
+ testing_unsafe: any;
118
+ }
119
+ declare module \\"*.txt\\" {
120
+ const value: string;
121
+ export default value;
122
+ }
123
+ declare module \\"*.webp\\" {
124
+ const value: ArrayBuffer;
125
+ export default value;
126
+ }
127
+ declare module \\"*.wasm\\" {
128
+ const value: WebAssembly.Module;
129
+ export default value;
130
+ }"
131
+ `);
132
+ });
133
+
134
+ it("should create a DTS file at the location that the command is executed from", async () => {
135
+ fs.writeFileSync("./index.ts", "export default { async fetch () {} };");
136
+ fs.writeFileSync(
137
+ "./wrangler.toml",
138
+ TOML.stringify({
139
+ compatibility_date: "2022-01-12",
140
+ name: "test-name",
141
+ main: "./index.ts",
142
+ // @ts-expect-error This type is out of sync with the actual bindingsConfig type
143
+ vars: bindingsConfigMock.vars,
144
+ }),
145
+ "utf-8"
146
+ );
147
+ await runWrangler("types");
148
+ expect(fs.existsSync("./worker-configuration.d.ts")).toBe(true);
149
+ });
150
+
151
+ it("should not create DTS file if there is nothing in the config to generate types from", async () => {
152
+ fs.writeFileSync("./index.ts", "export default { async fetch () {} };");
153
+ fs.writeFileSync(
154
+ "./wrangler.toml",
155
+ TOML.stringify({
156
+ compatibility_date: "2022-01-12",
157
+ name: "test-name",
158
+ main: "./index.ts",
159
+ }),
160
+ "utf-8"
161
+ );
162
+ await runWrangler("types");
163
+ expect(fs.existsSync("./worker-configuration.d.ts")).toBe(false);
164
+ expect(std.out).toMatchInlineSnapshot(`""`);
165
+ });
166
+
167
+ it("should create a DTS file at the location that the command is executed from", async () => {
168
+ fs.writeFileSync(
169
+ "./worker-configuration.d.ts",
170
+ "NOT THE CONTENTS OF THE GENERATED FILE"
171
+ );
172
+ fs.writeFileSync("./index.ts", "export default { async fetch () {} };");
173
+ fs.writeFileSync(
174
+ "./wrangler.toml",
175
+ TOML.stringify({
176
+ compatibility_date: "2022-01-12",
177
+ name: "test-name",
178
+ main: "./index.ts",
179
+ // @ts-expect-error This type is out of sync with the actual bindingsConfig type
180
+ vars: bindingsConfigMock.vars,
181
+ }),
182
+ "utf-8"
183
+ );
184
+
185
+ await expect(runWrangler("types")).rejects.toMatchInlineSnapshot(
186
+ `[Error: A non-wrangler worker-configuration.d.ts already exists, please rename and try again.]`
187
+ );
188
+ expect(fs.existsSync("./worker-configuration.d.ts")).toBe(true);
189
+ });
190
+
191
+ it("should log the declare global type generated and declare modules", async () => {
192
+ fs.writeFileSync(
193
+ "./index.ts",
194
+ `addEventListener('fetch', event => { event.respondWith(handleRequest(event.request));
195
+ }); async function handleRequest(request) { return new Response('Hello worker!', {headers: { 'content-type': 'text/plain' },});}`
196
+ );
197
+ fs.writeFileSync(
198
+ "./wrangler.toml",
199
+ TOML.stringify({
200
+ compatibility_date: "2022-01-12",
201
+ name: "test-name",
202
+ main: "./index.ts",
203
+ // @ts-expect-error This type is out of sync with the actual bindingsConfig type
204
+ unsafe: bindingsConfigMock.unsafe?.at(0) ?? {},
205
+ }),
206
+ "utf-8"
207
+ );
208
+
209
+ await runWrangler("types");
210
+ expect(std.out).toMatchInlineSnapshot(`
211
+ "declare global {
212
+ testing_unsafe: any;
213
+ }
214
+ "
215
+ `);
216
+ });
217
+ });
package/src/delete.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import assert from "assert";
1
2
  import path from "path";
2
3
  import { fetchResult } from "./cfetch";
3
4
  import { findWranglerToml, readConfig } from "./config";
4
5
  import { confirm } from "./dialogs";
5
6
  import { CI } from "./is-ci";
6
7
  import isInteractive from "./is-interactive";
8
+ import { deleteKVNamespace, listKVNamespaces } from "./kv/helpers";
7
9
  import { logger } from "./logger";
8
10
  import * as metrics from "./metrics";
9
11
  import { requireAuth } from "./user";
@@ -60,11 +62,18 @@ export async function deleteHandler(args: ArgumentsCamelCase<DeleteArgs>) {
60
62
 
61
63
  const scriptName = getScriptName(args, config);
62
64
 
65
+ assert(
66
+ scriptName,
67
+ "A worker name must be defined, either via --name, or in wrangler.toml"
68
+ );
69
+
63
70
  if (args.dryRun) {
64
71
  logger.log(`--dry-run: exiting now.`);
65
72
  return;
66
73
  }
67
74
 
75
+ assert(accountId, "Missing accountId");
76
+
68
77
  let confirmed = true;
69
78
  if (isInteractive() || !CI.isCI()) {
70
79
  confirmed = await confirm(
@@ -79,8 +88,24 @@ export async function deleteHandler(args: ArgumentsCamelCase<DeleteArgs>) {
79
88
  new URLSearchParams({ force: "true" })
80
89
  );
81
90
 
91
+ await deleteSiteNamespaceIfExisting(scriptName, accountId);
92
+
82
93
  logger.log("Successfully deleted", scriptName);
83
94
  }
95
+ }
84
96
 
85
- // TODO: maybe delete sites/assets kv namespace as well?
97
+ async function deleteSiteNamespaceIfExisting(
98
+ scriptName: string,
99
+ accountId: string
100
+ ): Promise<void> {
101
+ const title = `__${scriptName}-workers_sites_assets`;
102
+ const previewTitle = `__${scriptName}-workers_sites_assets_preview`;
103
+ const allNamespaces = await listKVNamespaces(accountId);
104
+ const namespacesToDelete = allNamespaces.filter(
105
+ (ns) => ns.title === title || ns.title === previewTitle
106
+ );
107
+ for (const ns of namespacesToDelete) {
108
+ await deleteKVNamespace(accountId, ns.id);
109
+ logger.log(`🌀 Deleted asset namespace for Workers Site "${ns.title}"`);
110
+ }
86
111
  }
package/src/dev/local.tsx CHANGED
@@ -3,6 +3,7 @@ import { fork } from "node:child_process";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { readFile, writeFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
+ import chalk from "chalk";
6
7
  import { npxImport } from "npx-import";
7
8
  import { useState, useEffect, useRef } from "react";
8
9
  import onExit from "signal-exit";
@@ -221,6 +222,10 @@ function useLocalWorker({
221
222
  cwd: path.dirname(scriptPath),
222
223
  execArgv: nodeOptions,
223
224
  stdio: "pipe",
225
+ env: {
226
+ ...process.env,
227
+ FORCE_COLOR: chalk.supportsColor.hasBasic ? "1" : undefined,
228
+ },
224
229
  }));
225
230
 
226
231
  child.on("message", async (messageString) => {
package/src/entry.ts CHANGED
@@ -26,7 +26,7 @@ export async function getEntry(
26
26
  assets?: string | undefined;
27
27
  },
28
28
  config: Config,
29
- command: "dev" | "publish"
29
+ command: "dev" | "publish" | "types"
30
30
  ): Promise<Entry> {
31
31
  let file: string;
32
32
  let directory = process.cwd();
package/src/index.tsx CHANGED
@@ -5,6 +5,7 @@ import supportsColor from "supports-color";
5
5
  import { ProxyAgent, setGlobalDispatcher } from "undici";
6
6
  import makeCLI from "yargs";
7
7
  import { version as wranglerVersion } from "../package.json";
8
+ import { fetchResult } from "./cfetch";
8
9
  import { readConfig } from "./config";
9
10
  import { d1 } from "./d1";
10
11
  import { deleteHandler, deleteOptions } from "./delete";
@@ -35,11 +36,22 @@ import { pubSubCommands } from "./pubsub/pubsub-commands";
35
36
  import { r2 } from "./r2";
36
37
  import { secret, secretBulkHandler, secretBulkOptions } from "./secret";
37
38
  import { tailOptions, tailHandler } from "./tail";
39
+ import { generateTypes } from "./type-generation";
38
40
  import { updateCheck } from "./update-check";
39
- import { listScopes, login, logout, validateScopeKeys } from "./user";
41
+ import {
42
+ listScopes,
43
+ login,
44
+ logout,
45
+ requireAuth,
46
+ validateScopeKeys,
47
+ } from "./user";
40
48
  import { whoami } from "./whoami";
41
49
 
50
+ import type { DeploymentListRes } from "./__tests__/helpers/msw/handlers/deployments";
42
51
  import type { Config } from "./config";
52
+ import type { ServiceMetadataRes } from "./init";
53
+ import type { PartialConfigToDTS } from "./type-generation";
54
+ import type { ArgumentsCamelCase } from "yargs";
43
55
  import type Yargs from "yargs";
44
56
 
45
57
  export type ConfigPath = string | undefined;
@@ -262,7 +274,7 @@ export function createCLIParser(argv: string[]) {
262
274
  // delete
263
275
  wrangler.command(
264
276
  "delete [script]",
265
- "🗑 Delete your Worker from Cloudflare.",
277
+ "🗑 Delete your Worker from Cloudflare.",
266
278
  deleteOptions,
267
279
  deleteHandler
268
280
  );
@@ -467,6 +479,80 @@ export function createCLIParser(argv: string[]) {
467
479
  }
468
480
  );
469
481
 
482
+ // type generation
483
+ wrangler.command(
484
+ "types",
485
+ "📝 Generate types from bindings & module rules in config",
486
+ () => {},
487
+ async () => {
488
+ await printWranglerBanner();
489
+ const config = readConfig(undefined, {});
490
+
491
+ const configBindings: PartialConfigToDTS = {
492
+ kv_namespaces: config.kv_namespaces ?? [],
493
+ vars: { ...config.vars },
494
+ wasm_modules: config.wasm_modules,
495
+ text_blobs: {
496
+ ...config.text_blobs,
497
+ },
498
+ data_blobs: config.data_blobs,
499
+ durable_objects: config.durable_objects,
500
+ r2_buckets: config.r2_buckets,
501
+ // @ts-expect-error - We don't want the type generated to inlcude the Beta prefix
502
+ d1_databases: config.d1_databases,
503
+ services: config.services,
504
+ dispatch_namespaces: config.dispatch_namespaces,
505
+ logfwdr: config.logfwdr,
506
+ unsafe: config.unsafe?.bindings,
507
+ rules: config.rules,
508
+ };
509
+
510
+ await generateTypes(configBindings, config);
511
+ }
512
+ );
513
+
514
+ wrangler.command(
515
+ "deployments",
516
+ false,
517
+ // "🚢 Logs the 10 most recent deployments with 'Version ID', 'Version number','Author email', 'Created on' and 'Latest deploy'",
518
+ (yargs) => {
519
+ yargs.option("name", {
520
+ describe: "The name of your worker",
521
+ type: "string",
522
+ });
523
+ },
524
+ async (deploymentsYargs: ArgumentsCamelCase<{ name: string }>) => {
525
+ await printWranglerBanner();
526
+ const config = readConfig(
527
+ deploymentsYargs.config as ConfigPath,
528
+ deploymentsYargs
529
+ );
530
+ const accountId = await requireAuth(config);
531
+ const scriptName = getScriptName(
532
+ { name: deploymentsYargs.name, env: undefined },
533
+ config
534
+ );
535
+ const scriptMetadata = await fetchResult<ServiceMetadataRes>(
536
+ `/accounts/${accountId}/workers/services/${scriptName}`
537
+ );
538
+
539
+ const scriptTag = scriptMetadata.default_environment.script.tag;
540
+ const deployments = await fetchResult<DeploymentListRes>(
541
+ `/accounts/${accountId}/workers/versions/by-script/${scriptTag}`
542
+ );
543
+
544
+ const versionMessages = deployments.versions.map(
545
+ (versions, index) =>
546
+ `\nVersion ID: ${versions.version_id}\nVersion number: ${
547
+ versions.version_number
548
+ }\nCreated on: ${versions.metadata.created_on}\nAuthor email: ${
549
+ versions.metadata.author_email
550
+ }\nLatest deploy: ${index === 0}\n`
551
+ );
552
+ logger.log(...versionMessages);
553
+ }
554
+ );
555
+
470
556
  // This set to false to allow overwrite of default behaviour
471
557
  wrangler.version(false);
472
558
 
package/src/init.ts CHANGED
@@ -16,8 +16,8 @@ import { parsePackageJSON, parseTOML, readFileSync } from "./parse";
16
16
  import { getBasePath } from "./paths";
17
17
  import { requireAuth } from "./user";
18
18
  import { CommandLineArgsError, printWranglerBanner } from "./index";
19
- import type { RawConfig } from "./config";
20
19
 
20
+ import type { RawConfig } from "./config";
21
21
  import type { Route, SimpleRoute } from "./config/environment";
22
22
  import type { WorkerMetadata } from "./create-worker-upload-form";
23
23
  import type { ConfigPath } from "./index";
@@ -4,6 +4,10 @@
4
4
  * or you're piping values from / to another process, etc
5
5
  */
6
6
  export default function isInteractive(): boolean {
7
+ if (process.env.CF_PAGES === "1") {
8
+ return false;
9
+ }
10
+
7
11
  try {
8
12
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
9
13
  } catch {
package/src/logger.ts CHANGED
@@ -2,7 +2,7 @@ import { format } from "node:util";
2
2
  import { formatMessagesSync } from "esbuild";
3
3
  import { getEnvironmentVariableFactory } from "./environment-variables";
4
4
 
5
- const LOGGER_LEVELS = {
5
+ export const LOGGER_LEVELS = {
6
6
  none: -1,
7
7
  error: 0,
8
8
  warn: 1,
@@ -11,7 +11,7 @@ const LOGGER_LEVELS = {
11
11
  debug: 4,
12
12
  } as const;
13
13
 
14
- type LoggerLevel = keyof typeof LOGGER_LEVELS;
14
+ export type LoggerLevel = keyof typeof LOGGER_LEVELS;
15
15
 
16
16
  /** A map from LOGGER_LEVEL to the error `kind` needed by `formatMessagesSync()`. */
17
17
  const LOGGER_LEVEL_FORMAT_TYPE_MAP = {
@@ -4,16 +4,18 @@ import {
4
4
  DurableObjectStub,
5
5
  } from "@miniflare/durable-objects";
6
6
  import {
7
- Log,
7
+ Log as MiniflareLog,
8
+ LogLevel as MiniflareLogLevel,
8
9
  Miniflare,
9
- Response as MiniflareResponse,
10
10
  Request as MiniflareRequest,
11
+ Response as MiniflareResponse,
11
12
  } from "miniflare";
12
13
  import yargs from "yargs";
13
14
  import { hideBin } from "yargs/helpers";
14
15
  import { FatalError } from "../errors";
15
16
  import generateASSETSBinding from "./assets";
16
17
  import { getRequestContextCheckOptions } from "./request-context";
18
+ import type { LoggerLevel } from "../logger";
17
19
  import type { Options } from "./assets";
18
20
  import type { AddressInfo } from "net";
19
21
 
@@ -24,7 +26,7 @@ export interface EnablePagesAssetsServiceBindingOptions {
24
26
 
25
27
  // miniflare defines this but importing it throws:
26
28
  // Dynamic require of "path" is not supported
27
- class NoOpLog extends Log {
29
+ class MiniflareNoOpLog extends MiniflareLog {
28
30
  log(): void {}
29
31
 
30
32
  error(message: Error): void {
@@ -41,14 +43,17 @@ async function main() {
41
43
  ...JSON.parse((args._[0] as string) ?? "{}"),
42
44
  ...requestContextCheckOptions,
43
45
  };
44
- const logLevel = config.logLevel.toUpperCase();
46
+
47
+ let logLevelString: Uppercase<LoggerLevel> = config.logLevel.toUpperCase();
48
+ if (logLevelString === "LOG") logLevelString = "INFO";
49
+ const logLevel = MiniflareLogLevel[logLevelString];
45
50
 
46
51
  config.log =
47
- config.logLevel === "none"
48
- ? new NoOpLog()
49
- : new Log(logLevel, config.logOptions);
52
+ logLevel === MiniflareLogLevel.NONE
53
+ ? new MiniflareNoOpLog()
54
+ : new MiniflareLog(logLevel, config.logOptions);
50
55
 
51
- if (logLevel === "DEBUG" || logLevel === "VERBOSE") {
56
+ if (logLevel === MiniflareLogLevel.DEBUG) {
52
57
  console.log("MINIFLARE OPTIONS:\n", JSON.stringify(config, null, 2));
53
58
  }
54
59
 
@@ -1,5 +1,7 @@
1
1
  import { version as wranglerVersion } from "../../package.json";
2
2
 
3
+ export const MAX_ASSET_COUNT = 20_000;
4
+ export const MAX_ASSET_SIZE = 25 * 1024 * 1024;
3
5
  export const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
4
6
  export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
5
7
  export const MAX_BUCKET_FILE_COUNT = 5000;
package/src/pages/dev.tsx CHANGED
@@ -379,6 +379,10 @@ export const Handler = async ({
379
379
  plugins: [
380
380
  esbuildAliasExternalPlugin({
381
381
  __ENTRY_POINT__: entrypointFile,
382
+ "./pages-dev-util": resolve(
383
+ getBasePath(),
384
+ "templates/pages-dev-util.ts"
385
+ ),
382
386
  }),
383
387
  ],
384
388
  outfile,
@@ -3,13 +3,17 @@ import { dirname, join, relative, resolve, sep } from "node:path";
3
3
  import { render, Text } from "ink";
4
4
  import Spinner from "ink-spinner";
5
5
  import { getType } from "mime";
6
+ import { Minimatch } from "minimatch";
6
7
  import PQueue from "p-queue";
7
8
  import prettyBytes from "pretty-bytes";
8
9
  import React from "react";
9
10
  import { fetchResult } from "../cfetch";
10
11
  import { FatalError } from "../errors";
12
+ import isInteractive from "../is-interactive";
11
13
  import { logger } from "../logger";
12
14
  import {
15
+ MAX_ASSET_COUNT,
16
+ MAX_ASSET_SIZE,
13
17
  BULK_UPLOAD_CONCURRENCY,
14
18
  MAX_BUCKET_FILE_COUNT,
15
19
  MAX_BUCKET_SIZE,
@@ -96,10 +100,11 @@ export const upload = async (
96
100
  "_redirects",
97
101
  "_headers",
98
102
  "_routes.json",
99
- ".DS_Store",
100
- "node_modules",
101
- ".git",
102
- ];
103
+ "functions",
104
+ "**/.DS_Store",
105
+ "**/node_modules",
106
+ "**/.git",
107
+ ].map((pattern) => new Minimatch(pattern));
103
108
 
104
109
  const directory = resolve(args.directory);
105
110
 
@@ -121,10 +126,13 @@ export const upload = async (
121
126
  await Promise.all(
122
127
  files.map(async (file) => {
123
128
  const filepath = join(dir, file);
129
+ const relativeFilepath = relative(startingDir, filepath);
124
130
  const filestat = await stat(filepath);
125
131
 
126
- if (IGNORE_LIST.includes(file)) {
127
- return;
132
+ for (const minimatch of IGNORE_LIST) {
133
+ if (minimatch.match(relativeFilepath)) {
134
+ return;
135
+ }
128
136
  }
129
137
 
130
138
  if (filestat.isSymbolicLink()) {
@@ -134,12 +142,12 @@ export const upload = async (
134
142
  if (filestat.isDirectory()) {
135
143
  fileMap = await walk(filepath, fileMap, startingDir);
136
144
  } else {
137
- const name = relative(startingDir, filepath).split(sep).join("/");
145
+ const name = relativeFilepath.split(sep).join("/");
138
146
 
139
- if (filestat.size > 25 * 1024 * 1024) {
147
+ if (filestat.size > MAX_ASSET_SIZE) {
140
148
  throw new FatalError(
141
149
  `Error: Pages only supports files up to ${prettyBytes(
142
- 25 * 1024 * 1024
150
+ MAX_ASSET_SIZE
143
151
  )} in size\n${name} is ${prettyBytes(filestat.size)} in size`,
144
152
  1
145
153
  );
@@ -161,9 +169,9 @@ export const upload = async (
161
169
 
162
170
  const fileMap = await walk(directory);
163
171
 
164
- if (fileMap.size > 20000) {
172
+ if (fileMap.size > MAX_ASSET_COUNT) {
165
173
  throw new FatalError(
166
- `Error: Pages only supports up to 20,000 files in a deployment. Ensure you have specified your build output directory correctly.`,
174
+ `Error: Pages only supports up to ${MAX_ASSET_COUNT.toLocaleString()} files in a deployment. Ensure you have specified your build output directory correctly.`,
167
175
  1
168
176
  );
169
177
  }
@@ -309,7 +317,9 @@ export const upload = async (
309
317
  (error) => {
310
318
  return Promise.reject(
311
319
  new FatalError(
312
- "Failed to upload files. Please try again.",
320
+ `Failed to upload files. Please try again. Error: ${JSON.stringify(
321
+ error
322
+ )})`,
313
323
  error.code || 1
314
324
  )
315
325
  );
@@ -390,7 +400,7 @@ function Progress({ done, total }: { done: number; total: number }) {
390
400
  return (
391
401
  <>
392
402
  <Text>
393
- <Spinner type="earth" />
403
+ {isInteractive() ? <Spinner type="earth" /> : null}
394
404
  {` Uploading... (${done}/${total})\n`}
395
405
  </Text>
396
406
  </>
package/src/sites.tsx CHANGED
@@ -77,7 +77,7 @@ async function createKVNamespaceIfNotAlreadyExisting(
77
77
  // check if it already exists
78
78
  // TODO: this is super inefficient, should be made better
79
79
  const namespaces = await listKVNamespaces(accountId);
80
- const found = namespaces.find((x) => x.title === title);
80
+ const found = namespaces.find((ns) => ns.title === title);
81
81
  if (found) {
82
82
  return { created: false, id: found.id };
83
83
  }