wrangler 0.0.0-e29a241 → 0.0.0-e2f9bb2

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.
Files changed (88) hide show
  1. package/README.md +25 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4828 -21
  4. package/package.json +29 -24
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +11 -0
  8. package/pages/functions/filepath-routing.ts +8 -0
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +692 -321
  14. package/src/__tests__/dev.test.tsx +284 -18
  15. package/src/__tests__/guess-worker-format.test.ts +28 -1
  16. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  17. package/src/__tests__/helpers/mock-console.ts +20 -3
  18. package/src/__tests__/helpers/mock-dialogs.ts +24 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  20. package/src/__tests__/helpers/mock-istty.ts +27 -0
  21. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  22. package/src/__tests__/helpers/run-in-tmp.ts +7 -0
  23. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  24. package/src/__tests__/https-options.test.ts +5 -2
  25. package/src/__tests__/index.test.ts +27 -769
  26. package/src/__tests__/init.test.ts +1042 -0
  27. package/src/__tests__/jest.setup.ts +4 -4
  28. package/src/__tests__/kv.test.ts +133 -61
  29. package/src/__tests__/logger.test.ts +115 -0
  30. package/src/__tests__/pages.test.ts +311 -26
  31. package/src/__tests__/publish.test.ts +2789 -618
  32. package/src/__tests__/r2.test.ts +26 -10
  33. package/src/__tests__/route.test.ts +4 -4
  34. package/src/__tests__/secret.test.ts +55 -34
  35. package/src/__tests__/tail.test.ts +146 -20
  36. package/src/__tests__/user.test.ts +131 -0
  37. package/src/__tests__/whoami.test.tsx +14 -9
  38. package/src/bundle.ts +30 -3
  39. package/src/cfetch/internal.ts +0 -2
  40. package/src/cli.ts +5 -11
  41. package/src/config/README.md +1 -1
  42. package/src/config/config.ts +13 -53
  43. package/src/config/environment.ts +88 -4
  44. package/src/config/index.ts +11 -4
  45. package/src/config/validation-helpers.ts +96 -7
  46. package/src/config/validation.ts +407 -79
  47. package/src/config-cache.ts +45 -0
  48. package/src/create-worker-preview.ts +22 -13
  49. package/src/create-worker-upload-form.ts +32 -9
  50. package/src/dev/dev-vars.ts +36 -0
  51. package/src/dev/dev.tsx +132 -78
  52. package/src/dev/local.tsx +115 -29
  53. package/src/dev/remote.tsx +23 -13
  54. package/src/dev/use-esbuild.ts +23 -5
  55. package/src/dialogs.tsx +16 -6
  56. package/src/entry.ts +83 -24
  57. package/src/environment-variables.ts +3 -1
  58. package/src/errors.ts +11 -0
  59. package/src/https-options.ts +3 -2
  60. package/src/index.tsx +619 -377
  61. package/src/inspect.ts +17 -11
  62. package/src/intl-polyfill.d.ts +139 -0
  63. package/src/logger.ts +66 -0
  64. package/src/miniflare-cli/index.ts +4 -0
  65. package/src/miniflare-cli/request-context.ts +40 -0
  66. package/src/module-collection.ts +7 -7
  67. package/src/open-in-browser.ts +5 -4
  68. package/src/package-manager.ts +9 -8
  69. package/src/pages.tsx +883 -62
  70. package/src/parse.ts +1 -1
  71. package/src/proxy.ts +25 -11
  72. package/src/publish.ts +263 -141
  73. package/src/sites.tsx +27 -14
  74. package/src/tail/index.ts +2 -2
  75. package/src/tail/printing.ts +13 -8
  76. package/src/update-check.ts +19 -0
  77. package/src/user.tsx +116 -41
  78. package/src/whoami.tsx +2 -1
  79. package/src/worker.ts +15 -6
  80. package/templates/gitignore +171 -0
  81. package/templates/static-asset-facade.js +1 -1
  82. package/wrangler-dist/cli.js +21167 -50229
  83. package/miniflare-dist/index.mjs.map +0 -7
  84. package/src/__tests__/dev2.test.tsx +0 -154
  85. package/src/__tests__/logout.test.ts +0 -52
  86. package/src/__tests__/sentry.test.ts +0 -146
  87. package/src/reporting.ts +0 -131
  88. package/wrangler-dist/cli.js.map +0 -7
@@ -22,17 +22,21 @@ import {
22
22
  all,
23
23
  isMutuallyExclusiveWith,
24
24
  inheritableInLegacyEnvironments,
25
+ appendEnvName,
26
+ getBindingNames,
27
+ isValidName,
25
28
  } from "./validation-helpers";
29
+ import type { Config, DevConfig, RawConfig, RawDevConfig } from "./config";
26
30
  import type {
27
- Config,
31
+ RawEnvironment,
28
32
  DeprecatedUpload,
29
- DevConfig,
30
- RawConfig,
31
- RawDevConfig,
32
- } from "./config";
33
- import type { RawEnvironment, Environment, Rule } from "./environment";
33
+ Environment,
34
+ Rule,
35
+ } from "./environment";
34
36
  import type { ValidatorFn } from "./validation-helpers";
35
37
 
38
+ const ENGLISH = new Intl.ListFormat("en");
39
+
36
40
  /**
37
41
  * Validate the given `rawConfig` object that was loaded from `configPath`.
38
42
  *
@@ -44,7 +48,7 @@ import type { ValidatorFn } from "./validation-helpers";
44
48
  export function normalizeAndValidateConfig(
45
49
  rawConfig: RawConfig,
46
50
  configPath: string | undefined,
47
- envName: string | undefined
51
+ args: unknown
48
52
  ): {
49
53
  config: Config;
50
54
  diagnostics: Diagnostics;
@@ -58,65 +62,87 @@ export function normalizeAndValidateConfig(
58
62
  deprecated(
59
63
  diagnostics,
60
64
  rawConfig,
61
- "type",
62
- "DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
63
- true
65
+ "miniflare",
66
+ "Wrangler does not use configuration in the `miniflare` section. Unless you are using Miniflare directly you can remove this section.",
67
+ true,
68
+ "😶 Ignored"
64
69
  );
65
70
 
66
71
  deprecated(
67
72
  diagnostics,
68
73
  rawConfig,
69
- "webpack_config",
70
- "DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
71
- true
74
+ "type",
75
+ "Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
76
+ true,
77
+ "😶 Ignored"
72
78
  );
73
79
 
74
80
  deprecated(
75
81
  diagnostics,
76
82
  rawConfig,
77
- `site.entry-point`,
78
- `The \`site.entry-point\` config field is no longer used.\nThe entry-point should be specified via the command line or the \`main\` config field.`,
79
- false,
80
- true
83
+ "webpack_config",
84
+ "Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
85
+ true,
86
+ "😶 Ignored"
81
87
  );
82
88
 
83
- const { deprecatedUpload, ...build } = normalizeAndValidateBuild(
89
+ validateOptionalProperty(
84
90
  diagnostics,
85
- rawConfig,
86
- rawConfig.build ?? {},
87
- configPath
91
+ "",
92
+ "legacy_env",
93
+ rawConfig.legacy_env,
94
+ "boolean"
88
95
  );
89
96
 
90
97
  validateOptionalProperty(
91
98
  diagnostics,
92
99
  "",
93
- "legacy_env",
94
- rawConfig.legacy_env,
100
+ "minify",
101
+ rawConfig.minify,
102
+ "boolean"
103
+ );
104
+
105
+ validateOptionalProperty(
106
+ diagnostics,
107
+ "",
108
+ "node_compat",
109
+ rawConfig.node_compat,
95
110
  "boolean"
96
111
  );
97
112
 
98
113
  // TODO: set the default to false to turn on service environments as the default
99
- const isLegacyEnv = rawConfig.legacy_env ?? true;
114
+ const isLegacyEnv =
115
+ (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
116
+ rawConfig.legacy_env ??
117
+ true;
118
+
119
+ // TODO: remove this once service environments goes GA.
120
+ if (!isLegacyEnv) {
121
+ diagnostics.warnings.push(
122
+ "Experimental: Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION."
123
+ );
124
+ }
100
125
 
101
126
  const topLevelEnv = normalizeAndValidateEnvironment(
102
127
  diagnostics,
103
128
  configPath,
104
- rawConfig,
105
- deprecatedUpload
129
+ rawConfig
106
130
  );
107
131
 
132
+ //TODO: find a better way to define the type of Args that can be passed to the normalizeAndValidateConfig()
133
+ const envName = (args as { env: string | undefined }).env;
134
+
108
135
  let activeEnv = topLevelEnv;
109
136
  if (envName !== undefined) {
137
+ const envDiagnostics = new Diagnostics(
138
+ `"env.${envName}" environment configuration`
139
+ );
110
140
  const rawEnv = rawConfig.env?.[envName];
111
141
  if (rawEnv !== undefined) {
112
- const envDiagnostics = new Diagnostics(
113
- `"env.${envName}" environment configuration`
114
- );
115
142
  activeEnv = normalizeAndValidateEnvironment(
116
143
  envDiagnostics,
117
144
  configPath,
118
145
  rawEnv,
119
- deprecatedUpload,
120
146
  envName,
121
147
  topLevelEnv,
122
148
  isLegacyEnv,
@@ -124,6 +150,18 @@ export function normalizeAndValidateConfig(
124
150
  );
125
151
  diagnostics.addChild(envDiagnostics);
126
152
  } else {
153
+ // An environment was specified, but no configuration for it was found.
154
+ // To cover any legacy environment cases, where the `envName` is used,
155
+ // Let's create a fake active environment with the specified `envName`.
156
+ activeEnv = normalizeAndValidateEnvironment(
157
+ envDiagnostics,
158
+ configPath,
159
+ {},
160
+ envName,
161
+ topLevelEnv,
162
+ isLegacyEnv,
163
+ rawConfig
164
+ );
127
165
  const envNames = rawConfig.env
128
166
  ? `The available configured environment names are: ${JSON.stringify(
129
167
  Object.keys(rawConfig.env)
@@ -157,7 +195,12 @@ export function normalizeAndValidateConfig(
157
195
  diagnostics,
158
196
  rawConfig.migrations ?? []
159
197
  ),
160
- site: normalizeAndValidateSite(diagnostics, rawConfig.site),
198
+ site: normalizeAndValidateSite(
199
+ diagnostics,
200
+ configPath,
201
+ rawConfig,
202
+ activeEnv.main
203
+ ),
161
204
  wasm_modules: normalizeAndValidateModulePaths(
162
205
  diagnostics,
163
206
  configPath,
@@ -170,14 +213,21 @@ export function normalizeAndValidateConfig(
170
213
  "text_blobs",
171
214
  rawConfig.text_blobs
172
215
  ),
173
- build,
216
+ data_blobs: normalizeAndValidateModulePaths(
217
+ diagnostics,
218
+ configPath,
219
+ "data_blobs",
220
+ rawConfig.data_blobs
221
+ ),
174
222
  };
175
223
 
224
+ validateBindingsHaveUniqueNames(diagnostics, config);
225
+
176
226
  validateAdditionalProperties(
177
227
  diagnostics,
178
228
  "top-level",
179
229
  Object.keys(rawConfig),
180
- [...Object.keys(config), "env", "miniflare"]
230
+ [...Object.keys(config), "env"]
181
231
  );
182
232
 
183
233
  return { config, diagnostics };
@@ -188,7 +238,7 @@ export function normalizeAndValidateConfig(
188
238
  */
189
239
  function normalizeAndValidateBuild(
190
240
  diagnostics: Diagnostics,
191
- rawConfig: RawConfig,
241
+ rawEnv: RawEnvironment,
192
242
  rawBuild: Config["build"],
193
243
  configPath: string | undefined
194
244
  ): Config["build"] & { deprecatedUpload: DeprecatedUpload } {
@@ -208,13 +258,13 @@ function normalizeAndValidateBuild(
208
258
 
209
259
  deprecated(
210
260
  diagnostics,
211
- rawConfig,
261
+ rawEnv,
212
262
  "build.upload.format",
213
263
  "The format is inferred automatically from the code.",
214
264
  true
215
265
  );
216
266
 
217
- if (rawConfig.main !== undefined && rawBuild.upload?.main) {
267
+ if (rawEnv.main !== undefined && rawBuild.upload?.main) {
218
268
  diagnostics.errors.push(
219
269
  `Don't define both the \`main\` and \`build.upload.main\` fields in your configuration.\n` +
220
270
  `They serve the same purpose: to point to the entry-point of your worker.\n` +
@@ -223,7 +273,7 @@ function normalizeAndValidateBuild(
223
273
  } else {
224
274
  deprecated(
225
275
  diagnostics,
226
- rawConfig,
276
+ rawEnv,
227
277
  "build.upload.main",
228
278
  `Delete the \`build.upload.main\` and \`build.upload.dir\` fields.\n` +
229
279
  `Then add the top level \`main\` field to your configuration file:\n` +
@@ -238,7 +288,7 @@ function normalizeAndValidateBuild(
238
288
 
239
289
  deprecated(
240
290
  diagnostics,
241
- rawConfig,
291
+ rawEnv,
242
292
  "build.upload.dir",
243
293
  `Use the top level "main" field or a command-line argument to specify the entry-point for the Worker.`,
244
294
  true
@@ -280,7 +330,7 @@ function normalizeAndValidateMainField(
280
330
  const directory = path.resolve(configDir);
281
331
  return path.resolve(directory, rawMain);
282
332
  } else {
283
- return;
333
+ return rawMain;
284
334
  }
285
335
  } else if (deprecatedUpload?.main !== undefined) {
286
336
  const directory = path.resolve(
@@ -400,26 +450,91 @@ function normalizeAndValidateMigrations(
400
450
  */
401
451
  function normalizeAndValidateSite(
402
452
  diagnostics: Diagnostics,
403
- rawSite: Config["site"]
453
+ configPath: string | undefined,
454
+ rawConfig: RawConfig,
455
+ mainEntryPoint: string | undefined
404
456
  ): Config["site"] {
405
- if (rawSite !== undefined) {
406
- const { bucket, include = [], exclude = [], ...rest } = rawSite;
407
- validateAdditionalProperties(diagnostics, "site", Object.keys(rest), []);
457
+ if (rawConfig?.site !== undefined) {
458
+ const { bucket, include = [], exclude = [], ...rest } = rawConfig.site;
459
+
460
+ validateAdditionalProperties(diagnostics, "site", Object.keys(rest), [
461
+ "entry-point",
462
+ ]);
408
463
  validateRequiredProperty(diagnostics, "site", "bucket", bucket, "string");
409
464
  validateTypedArray(diagnostics, "sites.include", include, "string");
410
465
  validateTypedArray(diagnostics, "sites.exclude", exclude, "string");
411
- return { bucket, include, exclude };
466
+ validateOptionalProperty(
467
+ diagnostics,
468
+ "site",
469
+ "entry-point",
470
+ rawConfig.site["entry-point"],
471
+ "string"
472
+ );
473
+
474
+ deprecated(
475
+ diagnostics,
476
+ rawConfig,
477
+ `site.entry-point`,
478
+ `Delete the \`site.entry-point\` field, then add the top level \`main\` field to your configuration file:\n` +
479
+ `\`\`\`\n` +
480
+ `main = "${path.join(
481
+ String(rawConfig.site["entry-point"]) || "workers-site",
482
+ path.extname(String(rawConfig.site["entry-point"]) || "workers-site")
483
+ ? ""
484
+ : "index.js"
485
+ )}"\n` +
486
+ `\`\`\``,
487
+ false,
488
+ undefined,
489
+ "warning"
490
+ );
491
+
492
+ let siteEntryPoint = rawConfig.site["entry-point"];
493
+
494
+ if (!mainEntryPoint && !siteEntryPoint) {
495
+ // this means that we're defaulting to "workers-site"
496
+ // so let's add the deprecation warning
497
+ diagnostics.warnings.push(
498
+ `Because you've defined a [site] configuration, we're defaulting to "workers-site" for the deprecated \`site.entry-point\`field.\n` +
499
+ `Add the top level \`main\` field to your configuration file:\n` +
500
+ `\`\`\`\n` +
501
+ `main = "workers-site/index.js"\n` +
502
+ `\`\`\``
503
+ );
504
+ siteEntryPoint = "workers-site";
505
+ } else if (mainEntryPoint && siteEntryPoint) {
506
+ diagnostics.errors.push(
507
+ `Don't define both the \`main\` and \`site.entry-point\` fields in your configuration.\n` +
508
+ `They serve the same purpose: to point to the entry-point of your worker.\n` +
509
+ `Delete the deprecated \`site.entry-point\` field from your config.`
510
+ );
511
+ }
512
+
513
+ if (configPath && siteEntryPoint) {
514
+ // rewrite the path to be relative to the working directory
515
+ siteEntryPoint = path.relative(
516
+ process.cwd(),
517
+ path.join(path.dirname(configPath), siteEntryPoint)
518
+ );
519
+ }
520
+
521
+ return {
522
+ bucket,
523
+ "entry-point": siteEntryPoint,
524
+ include,
525
+ exclude,
526
+ };
412
527
  }
413
528
  return undefined;
414
529
  }
415
530
 
416
531
  /**
417
- * Map the paths of the `wasm_modules` or `text_blobs` configuration to be relative to the current working directory.
532
+ * Map the paths of the `wasm_modules`, `text_blobs` or `data_blobs` configuration to be relative to the current working directory.
418
533
  */
419
534
  function normalizeAndValidateModulePaths(
420
535
  diagnostics: Diagnostics,
421
536
  configPath: string | undefined,
422
- field: "wasm_modules" | "text_blobs",
537
+ field: "wasm_modules" | "text_blobs" | "data_blobs",
423
538
  rawMapping: Record<string, string> | undefined
424
539
  ): Record<string, string> | undefined {
425
540
  if (rawMapping === undefined) {
@@ -442,14 +557,109 @@ function normalizeAndValidateModulePaths(
442
557
  return mapping;
443
558
  }
444
559
 
560
+ /**
561
+ * Check whether a value has the shape of a route, which can be a string
562
+ * or an object that looks like {pattern: string, zone_id: string }
563
+ */
564
+ function isValidRouteValue(item: unknown): boolean {
565
+ return (
566
+ !!item &&
567
+ (typeof item === "string" ||
568
+ (typeof item === "object" &&
569
+ hasProperty(item, "pattern") &&
570
+ typeof item.pattern === "string" &&
571
+ // it could have a zone_name
572
+ ((hasProperty(item, "zone_name") &&
573
+ typeof item.zone_name === "string") ||
574
+ // or a zone_id
575
+ (hasProperty(item, "zone_id") && typeof item.zone_id === "string")) &&
576
+ Object.keys(item).length === 2))
577
+ );
578
+ }
579
+
580
+ /**
581
+ * Validate that the field is a route.
582
+ */
583
+ const isRoute: ValidatorFn = (diagnostics, field, value) => {
584
+ if (value !== undefined && !isValidRouteValue(value)) {
585
+ diagnostics.errors.push(
586
+ `Expected "${field}" to be either a string, or an object with shape { pattern, zone_id | zone_name }, but got ${JSON.stringify(
587
+ value
588
+ )}.`
589
+ );
590
+ return false;
591
+ }
592
+ return true;
593
+ };
594
+
595
+ /**
596
+ * Validate that the field is an array of routes.
597
+ */
598
+ const isRouteArray: ValidatorFn = (diagnostics, field, value) => {
599
+ if (value === undefined) {
600
+ return true;
601
+ }
602
+ if (!Array.isArray(value)) {
603
+ diagnostics.errors.push(
604
+ `Expected "${field}" to be an array but got ${JSON.stringify(value)}.`
605
+ );
606
+ return false;
607
+ }
608
+ const invalidRoutes = [];
609
+ for (const item of value) {
610
+ if (!isValidRouteValue(item)) {
611
+ invalidRoutes.push(item);
612
+ }
613
+ }
614
+ if (invalidRoutes.length > 0) {
615
+ diagnostics.errors.push(
616
+ `Expected "${field}" to be an array of either strings or objects with the shape { pattern, zone_id | zone_name }, but these weren't valid: ${JSON.stringify(
617
+ invalidRoutes,
618
+ null,
619
+ 2
620
+ )}.`
621
+ );
622
+ }
623
+ return invalidRoutes.length === 0;
624
+ };
625
+
626
+ function validateRoute(
627
+ diagnostics: Diagnostics,
628
+ topLevelEnv: Environment | undefined,
629
+ rawEnv: RawEnvironment
630
+ ): Config["route"] {
631
+ return inheritable(
632
+ diagnostics,
633
+ topLevelEnv,
634
+ rawEnv,
635
+ "route",
636
+ isRoute,
637
+ undefined
638
+ );
639
+ }
640
+
641
+ function validateRoutes(
642
+ diagnostics: Diagnostics,
643
+ topLevelEnv: Environment | undefined,
644
+ rawEnv: RawEnvironment
645
+ ): Config["routes"] {
646
+ return inheritable(
647
+ diagnostics,
648
+ topLevelEnv,
649
+ rawEnv,
650
+ "routes",
651
+ all(isRouteArray, isMutuallyExclusiveWith(rawEnv, "route")),
652
+ undefined
653
+ );
654
+ }
655
+
445
656
  /**
446
657
  * Validate top-level environment configuration and return the normalized values.
447
658
  */
448
659
  function normalizeAndValidateEnvironment(
449
660
  diagnostics: Diagnostics,
450
661
  configPath: string | undefined,
451
- topLevelEnv: RawEnvironment,
452
- deprecatedUpload: DeprecatedUpload | undefined
662
+ topLevelEnv: RawEnvironment
453
663
  ): Environment;
454
664
  /**
455
665
  * Validate the named environment configuration and return the normalized values.
@@ -458,7 +668,6 @@ function normalizeAndValidateEnvironment(
458
668
  diagnostics: Diagnostics,
459
669
  configPath: string | undefined,
460
670
  rawEnv: RawEnvironment,
461
- deprecatedUpload: DeprecatedUpload | undefined,
462
671
  envName: string,
463
672
  topLevelEnv: Environment,
464
673
  isLegacyEnv: boolean,
@@ -468,7 +677,6 @@ function normalizeAndValidateEnvironment(
468
677
  diagnostics: Diagnostics,
469
678
  configPath: string | undefined,
470
679
  rawEnv: RawEnvironment,
471
- deprecatedUpload: DeprecatedUpload | undefined,
472
680
  envName = "top level",
473
681
  topLevelEnv?: Environment | undefined,
474
682
  isLegacyEnv?: boolean,
@@ -510,39 +718,36 @@ function normalizeAndValidateEnvironment(
510
718
 
511
719
  experimental(diagnostics, rawEnv, "unsafe");
512
720
 
513
- const route = inheritable(
514
- diagnostics,
515
- topLevelEnv,
516
- rawEnv,
517
- "route",
518
- isString,
519
- undefined
520
- );
521
- const routes = inheritable(
721
+ const route = validateRoute(diagnostics, topLevelEnv, rawEnv);
722
+
723
+ const routes = validateRoutes(diagnostics, topLevelEnv, rawEnv);
724
+
725
+ const workers_dev = inheritable(
522
726
  diagnostics,
523
727
  topLevelEnv,
524
728
  rawEnv,
525
- "routes",
526
- all(isStringArray, isMutuallyExclusiveWith(rawEnv, "route")),
729
+ "workers_dev",
730
+ isBoolean,
527
731
  undefined
528
732
  );
529
- const workers_dev = inheritable(
733
+
734
+ const { deprecatedUpload, ...build } = normalizeAndValidateBuild(
530
735
  diagnostics,
531
- topLevelEnv,
532
736
  rawEnv,
533
- "workers_dev",
534
- isBoolean,
535
- !(routes || route)
737
+ rawEnv.build ?? topLevelEnv?.build ?? {},
738
+ configPath
536
739
  );
537
740
 
538
741
  const environment: Environment = {
539
742
  // Inherited fields
540
- account_id: inheritable(
743
+ account_id: inheritableInLegacyEnvironments(
541
744
  diagnostics,
745
+ isLegacyEnv,
542
746
  topLevelEnv,
543
747
  rawEnv,
544
748
  "account_id",
545
749
  isString,
750
+ undefined,
546
751
  undefined
547
752
  ),
548
753
  compatibility_date: inheritable(
@@ -577,6 +782,12 @@ function normalizeAndValidateEnvironment(
577
782
  isString,
578
783
  "React.Fragment"
579
784
  ),
785
+ tsconfig: validateAndNormalizeTsconfig(
786
+ diagnostics,
787
+ topLevelEnv,
788
+ rawEnv,
789
+ configPath
790
+ ),
580
791
  rules: validateAndNormalizeRules(
581
792
  diagnostics,
582
793
  topLevelEnv,
@@ -590,7 +801,8 @@ function normalizeAndValidateEnvironment(
590
801
  topLevelEnv,
591
802
  rawEnv,
592
803
  "name",
593
- isString,
804
+ isValidName,
805
+ appendEnvName(envName),
594
806
  undefined
595
807
  ),
596
808
  main: normalizeAndValidateMainField(
@@ -623,6 +835,7 @@ function normalizeAndValidateEnvironment(
623
835
  isOneOf("bundled", "unbound"),
624
836
  undefined
625
837
  ),
838
+ build,
626
839
  workers_dev,
627
840
  // Not inherited fields
628
841
  vars: notInheritable(
@@ -680,11 +893,36 @@ function normalizeAndValidateEnvironment(
680
893
  }
681
894
  ),
682
895
  zone_id: rawEnv.zone_id,
896
+ minify: rawEnv.minify,
897
+ node_compat: rawEnv.node_compat,
683
898
  };
684
899
 
685
900
  return environment;
686
901
  }
687
902
 
903
+ function validateAndNormalizeTsconfig(
904
+ diagnostics: Diagnostics,
905
+ topLevelEnv: Environment | undefined,
906
+ rawEnv: RawEnvironment,
907
+ configPath: string | undefined
908
+ ) {
909
+ const tsconfig = inheritable(
910
+ diagnostics,
911
+ topLevelEnv,
912
+ rawEnv,
913
+ "tsconfig",
914
+ isString,
915
+ undefined
916
+ );
917
+
918
+ return configPath && tsconfig
919
+ ? path.relative(
920
+ process.cwd(),
921
+ path.join(path.dirname(configPath), tsconfig)
922
+ )
923
+ : tsconfig;
924
+ }
925
+
688
926
  const validateAndNormalizeRules = (
689
927
  diagnostics: Diagnostics,
690
928
  topLevelEnv: Environment | undefined,
@@ -700,7 +938,7 @@ const validateAndNormalizeRules = (
700
938
  );
701
939
  } else if (deprecatedRules) {
702
940
  diagnostics.warnings.push(
703
- `DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:\n` +
941
+ `Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:\n` +
704
942
  "```\n" +
705
943
  TOML.stringify({ rules: deprecatedRules }) +
706
944
  "```"
@@ -873,7 +1111,7 @@ const validateBindingsProperty =
873
1111
  diagnostics.warnings.push(
874
1112
  `The following bindings are at the top level, but not on "env.${envName}".\n` +
875
1113
  `This is not what you probably want, since "${field}" configuration is not inherited by environments.\n` +
876
- `Please add a binding for each to "${fieldPath}.bindings".` +
1114
+ `Please add a binding for each to "${fieldPath}.bindings":\n` +
877
1115
  missingBindings.map((name) => `- ${name}`).join("\n")
878
1116
  );
879
1117
  }
@@ -882,14 +1120,6 @@ const validateBindingsProperty =
882
1120
  return isValid;
883
1121
  };
884
1122
 
885
- /**
886
- * Get the names of the bindings collection in `value`.
887
- */
888
- const getBindingNames = (value: unknown): string[] =>
889
- ((value as { bindings: { name: string }[] })?.bindings ?? []).map(
890
- (binding) => binding.name
891
- );
892
-
893
1123
  /**
894
1124
  * Check that the given field is a valid "durable_object" binding object.
895
1125
  */
@@ -1060,7 +1290,7 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
1060
1290
  const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
1061
1291
  if (typeof value !== "object" || value === null) {
1062
1292
  diagnostics.errors.push(
1063
- `"kv_namespaces" bindings should be objects, but got ${JSON.stringify(
1293
+ `"r2_buckets" bindings should be objects, but got ${JSON.stringify(
1064
1294
  value
1065
1295
  )}`
1066
1296
  );
@@ -1094,3 +1324,101 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
1094
1324
  }
1095
1325
  return isValid;
1096
1326
  };
1327
+
1328
+ /**
1329
+ * Check that bindings whose names might conflict, don't.
1330
+ *
1331
+ * We don't want to have, for example, a KV namespace named "DATA"
1332
+ * and a Durable Object also named "DATA". Then it would be ambiguous
1333
+ * what exactly would live at `env.DATA` (or in the case of service-workers,
1334
+ * the `DATA` global).
1335
+ */
1336
+ const validateBindingsHaveUniqueNames = (
1337
+ diagnostics: Diagnostics,
1338
+ {
1339
+ durable_objects,
1340
+ kv_namespaces,
1341
+ r2_buckets,
1342
+ text_blobs,
1343
+ unsafe,
1344
+ vars,
1345
+ wasm_modules,
1346
+ data_blobs,
1347
+ }: Partial<Config>
1348
+ ): boolean => {
1349
+ let hasDuplicates = false;
1350
+
1351
+ const bindingsGroupedByType = {
1352
+ "Durable Object": getBindingNames(durable_objects),
1353
+ "KV Namespace": getBindingNames(kv_namespaces),
1354
+ "R2 Bucket": getBindingNames(r2_buckets),
1355
+ "Text Blob": getBindingNames(text_blobs),
1356
+ Unsafe: getBindingNames(unsafe),
1357
+ "Environment Variable": getBindingNames(vars),
1358
+ "WASM Module": getBindingNames(wasm_modules),
1359
+ "Data Blob": getBindingNames(data_blobs),
1360
+ } as Record<string, string[]>;
1361
+
1362
+ const bindingsGroupedByName: Record<string, string[]> = {};
1363
+
1364
+ for (const bindingType in bindingsGroupedByType) {
1365
+ const bindingNames = bindingsGroupedByType[bindingType];
1366
+
1367
+ for (const bindingName of bindingNames) {
1368
+ if (!(bindingName in bindingsGroupedByName)) {
1369
+ bindingsGroupedByName[bindingName] = [];
1370
+ }
1371
+
1372
+ bindingsGroupedByName[bindingName].push(bindingType);
1373
+ }
1374
+ }
1375
+
1376
+ for (const bindingName in bindingsGroupedByName) {
1377
+ const bindingTypes = bindingsGroupedByName[bindingName];
1378
+ if (bindingTypes.length < 2) {
1379
+ // there's only one (or zero) binding(s) with this name, which is fine, actually
1380
+ continue;
1381
+ }
1382
+
1383
+ hasDuplicates = true;
1384
+
1385
+ // there's two types of duplicates we want to look for:
1386
+ // - bindings with the same name of the same type (e.g. two Durable Objects both named "OBJ")
1387
+ // - bindings with the same name of different types (a KV namespace and DO both named "DATA")
1388
+
1389
+ const sameType = bindingTypes
1390
+ // filter once to find duplicate binding types
1391
+ .filter((type, i) => bindingTypes.indexOf(type) !== i)
1392
+ // filter twice to only get _unique_ duplicate binding types
1393
+ .filter(
1394
+ (type, i, duplicateBindingTypes) =>
1395
+ duplicateBindingTypes.indexOf(type) === i
1396
+ );
1397
+
1398
+ const differentTypes = bindingTypes.filter(
1399
+ (type, i) => bindingTypes.indexOf(type) === i
1400
+ );
1401
+
1402
+ if (differentTypes.length > 1) {
1403
+ // we have multiple different types using the same name
1404
+ diagnostics.errors.push(
1405
+ `${bindingName} assigned to ${ENGLISH.format(differentTypes)} bindings.`
1406
+ );
1407
+ }
1408
+
1409
+ sameType.forEach((bindingType) => {
1410
+ diagnostics.errors.push(
1411
+ `${bindingName} assigned to multiple ${bindingType} bindings.`
1412
+ );
1413
+ });
1414
+ }
1415
+
1416
+ if (hasDuplicates) {
1417
+ const problem =
1418
+ "Bindings must have unique names, so that they can all be referenced in the worker.";
1419
+ const resolution = "Please change your bindings to have unique names.";
1420
+ diagnostics.errors.push(`${problem}\n${resolution}`);
1421
+ }
1422
+
1423
+ return !hasDuplicates;
1424
+ };