wrangler 2.0.0 → 2.0.3

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/src/index.tsx CHANGED
@@ -73,6 +73,7 @@ type ConfigPath = string | undefined;
73
73
 
74
74
  const resetColor = "\x1b[0m";
75
75
  const fgGreenColor = "\x1b[32m";
76
+ const DEFAULT_LOCAL_PORT = 8787;
76
77
 
77
78
  function getRules(config: Config): Config["rules"] {
78
79
  const rules = config.rules ?? config.build?.upload?.rules ?? [];
@@ -406,7 +407,17 @@ export async function main(argv: string[]): Promise<void> {
406
407
  const isInsideGitProject = Boolean(
407
408
  await findUp(".git", { cwd: creationDirectory, type: "directory" })
408
409
  );
409
- const isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
410
+ let isGitInstalled;
411
+ try {
412
+ isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
413
+ } catch (err) {
414
+ if ((err as { code: string | undefined }).code !== "ENOENT") {
415
+ // only throw if the error is not because git is not installed
416
+ throw err;
417
+ } else {
418
+ isGitInstalled = false;
419
+ }
420
+ }
410
421
  if (!isInsideGitProject && isGitInstalled) {
411
422
  const shouldInitGit =
412
423
  yesFlag ||
@@ -712,7 +723,7 @@ export async function main(argv: string[]): Promise<void> {
712
723
  () => {
713
724
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
714
725
  throw new DeprecationError(
715
- "`wrangler build` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives"
726
+ "`wrangler build` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#build for alternatives"
716
727
  );
717
728
  }
718
729
  );
@@ -725,7 +736,7 @@ export async function main(argv: string[]): Promise<void> {
725
736
  () => {
726
737
  // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
727
738
  throw new DeprecationError(
728
- "`wrangler config` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#config for alternatives"
739
+ "`wrangler config` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#config for alternatives"
729
740
  );
730
741
  }
731
742
  );
@@ -869,6 +880,11 @@ export async function main(argv: string[]): Promise<void> {
869
880
  describe: "Enable dev tools",
870
881
  type: "boolean",
871
882
  deprecated: true,
883
+ })
884
+ .option("legacy-env", {
885
+ type: "boolean",
886
+ describe: "Use legacy environments",
887
+ hidden: true,
872
888
  });
873
889
  },
874
890
  async (args) => {
@@ -995,7 +1011,7 @@ export async function main(argv: string[]): Promise<void> {
995
1011
  const nodeCompat = args.nodeCompat ?? config.node_compat;
996
1012
  if (nodeCompat) {
997
1013
  logger.warn(
998
- "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."
1014
+ "Enabling node.js compatibility mode for built-ins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
999
1015
  );
1000
1016
  }
1001
1017
 
@@ -1027,7 +1043,9 @@ export async function main(argv: string[]): Promise<void> {
1027
1043
  args.siteExclude
1028
1044
  )}
1029
1045
  port={
1030
- args.port || config.dev?.port || (await getPort({ port: 8787 }))
1046
+ args.port ||
1047
+ config.dev.port ||
1048
+ (await getPort({ port: DEFAULT_LOCAL_PORT }))
1031
1049
  }
1032
1050
  ip={args.ip || config.dev.ip}
1033
1051
  inspectorPort={
@@ -1206,6 +1224,11 @@ export async function main(argv: string[]): Promise<void> {
1206
1224
  .option("dry-run", {
1207
1225
  describe: "Don't actually publish",
1208
1226
  type: "boolean",
1227
+ })
1228
+ .option("legacy-env", {
1229
+ type: "boolean",
1230
+ describe: "Use legacy environments",
1231
+ hidden: true,
1209
1232
  });
1210
1233
  },
1211
1234
  async (args) => {
@@ -1327,6 +1350,11 @@ export async function main(argv: string[]): Promise<void> {
1327
1350
  default: false,
1328
1351
  describe:
1329
1352
  "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1353
+ })
1354
+ .option("legacy-env", {
1355
+ type: "boolean",
1356
+ describe: "Use legacy environments",
1357
+ hidden: true,
1330
1358
  });
1331
1359
  },
1332
1360
  async (args) => {
@@ -1476,7 +1504,9 @@ export async function main(argv: string[]): Promise<void> {
1476
1504
  enableLocalPersistence={false}
1477
1505
  accountId={accountId}
1478
1506
  assetPaths={undefined}
1479
- port={config.dev?.port}
1507
+ port={
1508
+ config.dev.port || (await getPort({ port: DEFAULT_LOCAL_PORT }))
1509
+ }
1480
1510
  ip={config.dev.ip}
1481
1511
  public={undefined}
1482
1512
  compatibilityDate={getDevCompatibilityDate(config)}
@@ -1623,7 +1653,7 @@ export async function main(argv: string[]): Promise<void> {
1623
1653
  },
1624
1654
  () => {
1625
1655
  throw new DeprecationError(
1626
- "`wrangler subdomain` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#subdomain for alternatives"
1656
+ "`wrangler subdomain` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#subdomain for alternatives"
1627
1657
  );
1628
1658
  }
1629
1659
  );
@@ -1635,6 +1665,11 @@ export async function main(argv: string[]): Promise<void> {
1635
1665
  (secretYargs) => {
1636
1666
  return secretYargs
1637
1667
  .command(subHelp)
1668
+ .option("legacy-env", {
1669
+ type: "boolean",
1670
+ describe: "Use legacy environments",
1671
+ hidden: true,
1672
+ })
1638
1673
  .command(
1639
1674
  "put <key>",
1640
1675
  "Create or update a secret variable for a script",
@@ -1981,7 +2016,9 @@ export async function main(argv: string[]): Promise<void> {
1981
2016
  const accountId = await requireAuth(config);
1982
2017
 
1983
2018
  await fetchResult<{ id: string }>(
1984
- `/accounts/${accountId}/storage/kv/namespaces/${id}`,
2019
+ `/accounts/${accountId}/storage/kv/namespaces/${encodeURIComponent(
2020
+ id
2021
+ )}`,
1985
2022
  { method: "DELETE" }
1986
2023
  );
1987
2024
 
@@ -2235,7 +2272,9 @@ export async function main(argv: string[]): Promise<void> {
2235
2272
  const accountId = await requireAuth(config);
2236
2273
 
2237
2274
  await fetchResult(
2238
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`,
2275
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
2276
+ key
2277
+ )}`,
2239
2278
  { method: "DELETE" }
2240
2279
  );
2241
2280
  }
@@ -2604,19 +2643,14 @@ export async function main(argv: string[]): Promise<void> {
2604
2643
  }
2605
2644
  );
2606
2645
 
2607
- wrangler
2608
- .option("legacy-env", {
2609
- type: "boolean",
2610
- describe: "Use legacy environments",
2611
- })
2612
- .option("config", {
2613
- alias: "c",
2614
- describe: "Path to .toml configuration file",
2615
- type: "string",
2616
- requiresArg: true,
2617
- });
2646
+ wrangler.option("config", {
2647
+ alias: "c",
2648
+ describe: "Path to .toml configuration file",
2649
+ type: "string",
2650
+ requiresArg: true,
2651
+ });
2618
2652
 
2619
- wrangler.group(["config", "help", "version", "legacy-env"], "Flags:");
2653
+ wrangler.group(["config", "help", "version"], "Flags:");
2620
2654
  wrangler.help().alias("h", "help");
2621
2655
  wrangler.version(wranglerVersion).alias("v", "version");
2622
2656
  wrangler.exitProcess(false);
package/src/kv.ts CHANGED
@@ -151,7 +151,9 @@ export async function putKeyValue(
151
151
  }
152
152
  }
153
153
  return await fetchResult(
154
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${keyValue.key}`,
154
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
155
+ keyValue.key
156
+ )}`,
155
157
  { method: "PUT", body: keyValue.value },
156
158
  searchParams
157
159
  );
package/src/pages.tsx CHANGED
@@ -82,6 +82,8 @@ const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
82
82
  export const pagesBetaWarning =
83
83
  "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
84
84
 
85
+ const isInPagesCI = !!process.env.CF_PAGES;
86
+
85
87
  const CLEANUP_CALLBACKS: (() => void)[] = [];
86
88
  const CLEANUP = () => {
87
89
  CLEANUP_CALLBACKS.forEach((callback) => callback());
@@ -802,34 +804,30 @@ const createDeployment: CommandModule<
802
804
  return yargs
803
805
  .positional("directory", {
804
806
  type: "string",
805
- default: "functions",
806
- description: "The directory of Pages Functions",
807
+ demandOption: true,
808
+ description: "The directory of static files to upload",
807
809
  })
808
810
  .options({
809
811
  "project-name": {
810
812
  type: "string",
811
- description:
812
- "The name of the project you want to list deployments for",
813
+ description: "The name of the project you want to deploy to",
813
814
  },
814
815
  branch: {
815
816
  type: "string",
816
- description:
817
- "The branch of the project you want to list deployments for",
817
+ description: "The name of the branch you want to deploy to",
818
818
  },
819
819
  "commit-hash": {
820
820
  type: "string",
821
- description:
822
- "The branch of the project you want to list deployments for",
821
+ description: "The SHA to attach to this deployment",
823
822
  },
824
823
  "commit-message": {
825
824
  type: "string",
826
- description:
827
- "The branch of the project you want to list deployments for",
825
+ description: "The commit message to attach to this deployment",
828
826
  },
829
827
  "commit-dirty": {
830
828
  type: "boolean",
831
829
  description:
832
- "The branch of the project you want to list deployments for",
830
+ "Whether or not the workspace should be considered dirty for this deployment",
833
831
  },
834
832
  })
835
833
  .epilogue(pagesBetaWarning);
@@ -842,6 +840,10 @@ const createDeployment: CommandModule<
842
840
  commitMessage,
843
841
  commitDirty,
844
842
  }) => {
843
+ if (!directory) {
844
+ throw new FatalError("Must specify a directory.", 1);
845
+ }
846
+
845
847
  const config = getConfigCache<PagesConfigCache>(
846
848
  PAGES_CONFIG_CACHE_FILENAME
847
849
  );
@@ -851,40 +853,45 @@ const createDeployment: CommandModule<
851
853
 
852
854
  const isInteractive = process.stdin.isTTY;
853
855
  if (!projectName && isInteractive) {
854
- const existingOrNew = await new Promise<"new" | "existing">((resolve) => {
855
- const { unmount } = render(
856
- <>
857
- <Text>
858
- No project selected. Would you like to create one or use an
859
- existing project?
860
- </Text>
861
- <SelectInput
862
- items={[
863
- {
864
- key: "new",
865
- label: "Create a new project",
866
- value: "new",
867
- },
868
- {
869
- key: "existing",
870
- label: "Use an existing project",
871
- value: "existing",
872
- },
873
- ]}
874
- onSelect={async (selected) => {
875
- resolve(selected.value as "new" | "existing");
876
- unmount();
877
- }}
878
- />
879
- </>
880
- );
881
- });
856
+ const projects = (await listProjects({ accountId })).filter(
857
+ (project) => !project.source
858
+ );
859
+
860
+ let existingOrNew: "existing" | "new" = "new";
861
+
862
+ if (projects.length > 0) {
863
+ existingOrNew = await new Promise<"new" | "existing">((resolve) => {
864
+ const { unmount } = render(
865
+ <>
866
+ <Text>
867
+ No project selected. Would you like to create one or use an
868
+ existing project?
869
+ </Text>
870
+ <SelectInput
871
+ items={[
872
+ {
873
+ key: "new",
874
+ label: "Create a new project",
875
+ value: "new",
876
+ },
877
+ {
878
+ key: "existing",
879
+ label: "Use an existing project",
880
+ value: "existing",
881
+ },
882
+ ]}
883
+ onSelect={async (selected) => {
884
+ resolve(selected.value as "new" | "existing");
885
+ unmount();
886
+ }}
887
+ />
888
+ </>
889
+ );
890
+ });
891
+ }
882
892
 
883
893
  switch (existingOrNew) {
884
894
  case "existing": {
885
- const projects = (await listProjects({ accountId })).filter(
886
- (project) => !project.source
887
- );
888
895
  projectName = await new Promise((resolve) => {
889
896
  const { unmount } = render(
890
897
  <>
@@ -1569,6 +1576,10 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1569
1576
  default: false,
1570
1577
  description: "Build a plugin rather than a Worker script",
1571
1578
  },
1579
+ "build-output-directory": {
1580
+ type: "string",
1581
+ description: "The directory to output static assets to",
1582
+ },
1572
1583
  })
1573
1584
  .epilogue(pagesBetaWarning),
1574
1585
  async ({
@@ -1580,9 +1591,14 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1580
1591
  fallbackService,
1581
1592
  watch,
1582
1593
  plugin,
1594
+ "build-output-directory": buildOutputDirectory,
1583
1595
  }) => {
1584
- // Beta message for `wrangler pages <commands>` usage
1585
- logger.log(pagesBetaWarning);
1596
+ if (!isInPagesCI) {
1597
+ // Beta message for `wrangler pages <commands>` usage
1598
+ logger.log(pagesBetaWarning);
1599
+ }
1600
+
1601
+ buildOutputDirectory ??= dirname(outfile);
1586
1602
 
1587
1603
  await buildFunctions({
1588
1604
  outfile,
@@ -1593,7 +1609,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1593
1609
  fallbackService,
1594
1610
  watch,
1595
1611
  plugin,
1596
- buildOutputDirectory: dirname(outfile),
1612
+ buildOutputDirectory,
1597
1613
  });
1598
1614
  }
1599
1615
  )
package/src/parse.ts CHANGED
@@ -77,7 +77,9 @@ type TomlError = Error & {
77
77
  */
78
78
  export function parseTOML(input: string, file?: string): TOML.JsonMap | never {
79
79
  try {
80
- return TOML.parse(input);
80
+ // Normalize CRLF to LF to avoid hitting https://github.com/iarna/iarna-toml/issues/33.
81
+ const normalizedInput = input.replace(/\r\n$/g, "\n");
82
+ return TOML.parse(normalizedInput);
81
83
  } catch (err) {
82
84
  const { name, message, line, col } = err as TomlError;
83
85
  if (name !== TOML_ERROR_NAME) {
package/src/proxy.ts CHANGED
@@ -210,6 +210,10 @@ export function usePreviewServer({
210
210
  const request = message.pipe(remote.request(headers));
211
211
  request.on("response", (responseHeaders) => {
212
212
  const status = responseHeaders[":status"] ?? 500;
213
+
214
+ // log all requests to terminal
215
+ logger.log(new Date().toLocaleTimeString(), method, url, status);
216
+
213
217
  rewriteRemoteHostToLocalHostInHeaders(
214
218
  responseHeaders,
215
219
  previewToken.host,
@@ -349,15 +353,6 @@ async function createProxyServer(
349
353
  : createHttpServer();
350
354
 
351
355
  return server
352
- .on("request", function (req, res) {
353
- // log all requests
354
- logger.log(
355
- new Date().toLocaleTimeString(),
356
- req.method,
357
- req.url,
358
- res.statusCode
359
- );
360
- })
361
356
  .on("upgrade", (req) => {
362
357
  // log all websocket connections
363
358
  logger.log(
package/src/user.tsx CHANGED
@@ -214,6 +214,7 @@ import path from "node:path";
214
214
  import url from "node:url";
215
215
  import { TextEncoder } from "node:util";
216
216
  import TOML from "@iarna/toml";
217
+ import { HostURL } from "@webcontainer/env";
217
218
  import { render, Text } from "ink";
218
219
  import SelectInput from "ink-select-input";
219
220
  import Table from "ink-table";
@@ -339,9 +340,18 @@ export function validateScopeKeys(
339
340
  const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
340
341
  const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
341
342
  const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
342
- const CALLBACK_URL = "http://localhost:8976/oauth/callback";
343
343
  const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
344
344
 
345
+ /**
346
+ * To allow OAuth callbacks in environments such as WebContainer we need to
347
+ * create a host URL which only resolves `localhost` to a WebContainer
348
+ * hostname if the process is running in a WebContainer. On local this will
349
+ * be a no-op and it leaves the URL unmodified.
350
+ *
351
+ * @see https://www.npmjs.com/package/@webcontainer/env
352
+ */
353
+ const CALLBACK_URL = HostURL.parse("http://localhost:8976/oauth/callback").href;
354
+
345
355
  let LocalState: State = getAuthTokens();
346
356
 
347
357
  /**