wrangler 0.0.0-e3cab74 → 0.0.0-e3e3243

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 (66) hide show
  1. package/README.md +1 -1
  2. package/bin/wrangler.js +10 -1
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +10 -7
  6. package/pages/functions/filepath-routing.test.ts +125 -25
  7. package/pages/functions/filepath-routing.ts +6 -3
  8. package/src/__tests__/configuration.test.ts +959 -324
  9. package/src/__tests__/dev.test.tsx +512 -84
  10. package/src/__tests__/guess-worker-format.test.ts +19 -17
  11. package/src/__tests__/helpers/mock-account-id.ts +22 -4
  12. package/src/__tests__/helpers/mock-cfetch.ts +3 -3
  13. package/src/__tests__/helpers/mock-console.ts +17 -9
  14. package/src/__tests__/helpers/mock-kv.ts +9 -16
  15. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  16. package/src/__tests__/helpers/run-in-tmp.ts +3 -0
  17. package/src/__tests__/https-options.test.ts +127 -0
  18. package/src/__tests__/index.test.ts +128 -11
  19. package/src/__tests__/jest.setup.ts +16 -4
  20. package/src/__tests__/kv.test.ts +364 -125
  21. package/src/__tests__/logout.test.ts +15 -13
  22. package/src/__tests__/pages.test.ts +82 -0
  23. package/src/__tests__/publish.test.ts +1838 -735
  24. package/src/__tests__/r2.test.ts +4 -4
  25. package/src/__tests__/route.test.ts +45 -0
  26. package/src/__tests__/secret.test.ts +167 -68
  27. package/src/__tests__/tail.test.ts +2 -4
  28. package/src/__tests__/whoami.test.tsx +21 -7
  29. package/src/bundle.ts +27 -5
  30. package/src/cfetch/internal.ts +11 -5
  31. package/src/config/README.md +6 -6
  32. package/src/config/config.ts +54 -101
  33. package/src/config/environment.ts +67 -1
  34. package/src/config/index.ts +13 -3
  35. package/src/config/validation-helpers.ts +207 -23
  36. package/src/config/validation.ts +344 -129
  37. package/src/{api/preview.ts → create-worker-preview.ts} +39 -20
  38. package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
  39. package/src/dev/dev.tsx +392 -0
  40. package/src/dev/local.tsx +232 -0
  41. package/src/dev/remote.tsx +203 -0
  42. package/src/dev/use-esbuild.ts +104 -0
  43. package/src/entry.ts +192 -9
  44. package/src/https-options.ts +122 -0
  45. package/src/index.tsx +577 -515
  46. package/src/inspect.ts +9 -1
  47. package/src/intl-polyfill.d.ts +139 -0
  48. package/src/{kv.tsx → kv.ts} +97 -53
  49. package/src/miniflare-cli/README.md +30 -0
  50. package/src/miniflare-cli/enum-keys.ts +17 -0
  51. package/src/miniflare-cli/index.ts +37 -0
  52. package/src/module-collection.ts +90 -2
  53. package/src/open-in-browser.ts +14 -2
  54. package/src/pages.tsx +26 -10
  55. package/src/proxy.ts +104 -40
  56. package/src/publish.ts +53 -71
  57. package/src/reporting.ts +25 -9
  58. package/src/selfsigned.d.ts +29 -0
  59. package/src/sites.tsx +68 -29
  60. package/src/user.tsx +163 -85
  61. package/src/{api/worker.ts → worker.ts} +1 -24
  62. package/wrangler-dist/cli.js +5933 -3842
  63. package/wrangler-dist/cli.js.map +3 -3
  64. package/src/__tests__/helpers/mock-user.ts +0 -27
  65. package/src/dev.tsx +0 -961
  66. package/src/guess-worker-format.ts +0 -68
@@ -19,11 +19,23 @@ import {
19
19
  validateOptionalTypedArray,
20
20
  validateRequiredProperty,
21
21
  validateTypedArray,
22
+ all,
23
+ isMutuallyExclusiveWith,
24
+ inheritableInLegacyEnvironments,
25
+ appendEnvName,
26
+ getBindingNames,
22
27
  } from "./validation-helpers";
23
- import type { Config, DeprecatedUpload, RawConfig } from "./config";
24
- import type { RawEnvironment, Environment, Rule } from "./environment";
28
+ import type { Config, DevConfig, RawConfig, RawDevConfig } from "./config";
29
+ import type {
30
+ RawEnvironment,
31
+ DeprecatedUpload,
32
+ Environment,
33
+ Rule,
34
+ } from "./environment";
25
35
  import type { ValidatorFn } from "./validation-helpers";
26
36
 
37
+ const ENGLISH = new Intl.ListFormat("en");
38
+
27
39
  /**
28
40
  * Validate the given `rawConfig` object that was loaded from `configPath`.
29
41
  *
@@ -34,7 +46,8 @@ import type { ValidatorFn } from "./validation-helpers";
34
46
  */
35
47
  export function normalizeAndValidateConfig(
36
48
  rawConfig: RawConfig,
37
- configPath: string | undefined
49
+ configPath: string | undefined,
50
+ args: unknown
38
51
  ): {
39
52
  config: Config;
40
53
  diagnostics: Diagnostics;
@@ -61,10 +74,13 @@ export function normalizeAndValidateConfig(
61
74
  true
62
75
  );
63
76
 
64
- const { deprecatedUpload, ...build } = normalizeAndValidateBuild(
77
+ deprecated(
65
78
  diagnostics,
66
79
  rawConfig,
67
- rawConfig.build ?? {}
80
+ `site.entry-point`,
81
+ `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.`,
82
+ false,
83
+ true
68
84
  );
69
85
 
70
86
  validateOptionalProperty(
@@ -75,23 +91,79 @@ export function normalizeAndValidateConfig(
75
91
  "boolean"
76
92
  );
77
93
 
94
+ // TODO: set the default to false to turn on service environments as the default
95
+ const isLegacyEnv =
96
+ (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
97
+ rawConfig.legacy_env ??
98
+ true;
99
+
100
+ const topLevelEnv = normalizeAndValidateEnvironment(
101
+ diagnostics,
102
+ configPath,
103
+ rawConfig
104
+ );
105
+
106
+ //TODO: find a better way to define the type of Args that can be passed to the normalizeAndValidateConfig()
107
+ const envName = (args as { env: string | undefined }).env;
108
+
109
+ let activeEnv = topLevelEnv;
110
+ if (envName !== undefined) {
111
+ const envDiagnostics = new Diagnostics(
112
+ `"env.${envName}" environment configuration`
113
+ );
114
+ const rawEnv = rawConfig.env?.[envName];
115
+ if (rawEnv !== undefined) {
116
+ activeEnv = normalizeAndValidateEnvironment(
117
+ envDiagnostics,
118
+ configPath,
119
+ rawEnv,
120
+ envName,
121
+ topLevelEnv,
122
+ isLegacyEnv,
123
+ rawConfig
124
+ );
125
+ diagnostics.addChild(envDiagnostics);
126
+ } else {
127
+ // An environment was specified, but no configuration for it was found.
128
+ // To cover any legacy environment cases, where the `envName` is used,
129
+ // Let's create a fake active environment with the specified `envName`.
130
+ activeEnv = normalizeAndValidateEnvironment(
131
+ envDiagnostics,
132
+ configPath,
133
+ {},
134
+ envName,
135
+ topLevelEnv,
136
+ isLegacyEnv,
137
+ rawConfig
138
+ );
139
+ const envNames = rawConfig.env
140
+ ? `The available configured environment names are: ${JSON.stringify(
141
+ Object.keys(rawConfig.env)
142
+ )}\n`
143
+ : "";
144
+ const message =
145
+ `No environment found in configuration with name "${envName}".\n` +
146
+ `Before using \`--env=${envName}\` there should be an equivalent environment section in the configuration.\n` +
147
+ `${envNames}\n` +
148
+ `Consider adding an environment configuration section to the wrangler.toml file:\n` +
149
+ "```\n[env." +
150
+ envName +
151
+ "]\n```\n";
152
+
153
+ if (envNames.length > 0) {
154
+ diagnostics.errors.push(message);
155
+ } else {
156
+ // Only warn (rather than error) if there are not actually any environments configured in wrangler.toml.
157
+ diagnostics.warnings.push(message);
158
+ }
159
+ }
160
+ }
161
+
78
162
  // Process the top-level default environment configuration.
79
163
  const config: Config = {
80
164
  configPath,
81
- main: normalizeAndValidateMainField(
82
- diagnostics,
83
- configPath,
84
- rawConfig.main,
85
- deprecatedUpload
86
- ),
87
- // TODO: set the default to false to turn on service environments as the default
88
- legacy_env: rawConfig.legacy_env ?? true,
89
- ...normalizeAndValidateEnvironment(
90
- diagnostics,
91
- rawConfig,
92
- deprecatedUpload.rules
93
- ),
94
- env: {}, // Will be filled below by `normalizeAndValidateEnvironment()`.
165
+ legacy_env: isLegacyEnv,
166
+ ...activeEnv,
95
167
  dev: normalizeAndValidateDev(diagnostics, rawConfig.dev ?? {}),
96
168
  migrations: normalizeAndValidateMigrations(
97
169
  diagnostics,
@@ -110,33 +182,15 @@ export function normalizeAndValidateConfig(
110
182
  "text_blobs",
111
183
  rawConfig.text_blobs
112
184
  ),
113
- build,
114
185
  };
115
186
 
116
- // Process each of the specific environment configurations.
117
- if (rawConfig.env !== undefined) {
118
- for (const envName of Object.keys(rawConfig.env)) {
119
- const envDiagnostics = new Diagnostics(
120
- `"env.${envName}" environment configuration`
121
- );
122
- const environment = normalizeAndValidateEnvironment(
123
- envDiagnostics,
124
- rawConfig.env[envName] as RawEnvironment,
125
- deprecatedUpload.rules,
126
- envName,
127
- config,
128
- rawConfig
129
- );
130
- config.env[envName] = environment;
131
- diagnostics.addChild(envDiagnostics);
132
- }
133
- }
187
+ validateBindingsHaveUniqueNames(diagnostics, config);
134
188
 
135
189
  validateAdditionalProperties(
136
190
  diagnostics,
137
191
  "top-level",
138
192
  Object.keys(rawConfig),
139
- Object.keys(config)
193
+ [...Object.keys(config), "env", "miniflare"]
140
194
  );
141
195
 
142
196
  return { config, diagnostics };
@@ -147,8 +201,9 @@ export function normalizeAndValidateConfig(
147
201
  */
148
202
  function normalizeAndValidateBuild(
149
203
  diagnostics: Diagnostics,
150
- rawConfig: RawConfig,
151
- rawBuild: Config["build"]
204
+ rawEnv: RawEnvironment,
205
+ rawBuild: Config["build"],
206
+ configPath: string | undefined
152
207
  ): Config["build"] & { deprecatedUpload: DeprecatedUpload } {
153
208
  const { command, cwd, watch_dir, upload, ...rest } = rawBuild;
154
209
  const deprecatedUpload: DeprecatedUpload = { ...upload };
@@ -166,13 +221,13 @@ function normalizeAndValidateBuild(
166
221
 
167
222
  deprecated(
168
223
  diagnostics,
169
- rawConfig,
224
+ rawEnv,
170
225
  "build.upload.format",
171
226
  "The format is inferred automatically from the code.",
172
227
  true
173
228
  );
174
229
 
175
- if (rawConfig.main !== undefined && rawBuild.upload?.main) {
230
+ if (rawEnv.main !== undefined && rawBuild.upload?.main) {
176
231
  diagnostics.errors.push(
177
232
  `Don't define both the \`main\` and \`build.upload.main\` fields in your configuration.\n` +
178
233
  `They serve the same purpose: to point to the entry-point of your worker.\n` +
@@ -181,7 +236,7 @@ function normalizeAndValidateBuild(
181
236
  } else {
182
237
  deprecated(
183
238
  diagnostics,
184
- rawConfig,
239
+ rawEnv,
185
240
  "build.upload.main",
186
241
  `Delete the \`build.upload.main\` and \`build.upload.dir\` fields.\n` +
187
242
  `Then add the top level \`main\` field to your configuration file:\n` +
@@ -196,24 +251,41 @@ function normalizeAndValidateBuild(
196
251
 
197
252
  deprecated(
198
253
  diagnostics,
199
- rawConfig,
254
+ rawEnv,
200
255
  "build.upload.dir",
201
256
  `Use the top level "main" field or a command-line argument to specify the entry-point for the Worker.`,
202
257
  true
203
258
  );
204
259
  }
205
260
 
206
- return { command, cwd, watch_dir, deprecatedUpload };
261
+ return {
262
+ command,
263
+ watch_dir:
264
+ // - `watch_dir` only matters when `command` is defined, so we apply
265
+ // a default only when `command` is defined
266
+ // - `configPath` will always be defined since `build` can only
267
+ // be configured in `wrangler.toml`, but who knows, that may
268
+ // change in the future, so we do a check anyway
269
+ command
270
+ ? configPath
271
+ ? path.relative(
272
+ process.cwd(),
273
+ path.join(path.dirname(configPath), watch_dir || "./src")
274
+ )
275
+ : watch_dir || "./src"
276
+ : watch_dir,
277
+ cwd,
278
+ deprecatedUpload,
279
+ };
207
280
  }
208
281
 
209
282
  /**
210
283
  * Validate the `main` field and return the normalized values.
211
284
  */
212
285
  function normalizeAndValidateMainField(
213
- diagnostics: Diagnostics,
214
286
  configPath: string | undefined,
215
287
  rawMain: string | undefined,
216
- deprecatedUpload: { dir?: string; main?: string }
288
+ deprecatedUpload: DeprecatedUpload | undefined
217
289
  ): string | undefined {
218
290
  const configDir = path.dirname(configPath ?? "wrangler.toml");
219
291
  if (rawMain !== undefined) {
@@ -221,13 +293,13 @@ function normalizeAndValidateMainField(
221
293
  const directory = path.resolve(configDir);
222
294
  return path.resolve(directory, rawMain);
223
295
  } else {
224
- diagnostics.errors.push(
225
- `ERROR: "main" should be a string but got ${JSON.stringify(rawMain)}`
226
- );
227
- return;
296
+ return rawMain;
228
297
  }
229
- } else if (deprecatedUpload.main !== undefined) {
230
- const directory = path.resolve(configDir, deprecatedUpload.dir || "./dist");
298
+ } else if (deprecatedUpload?.main !== undefined) {
299
+ const directory = path.resolve(
300
+ configDir,
301
+ deprecatedUpload?.dir || "./dist"
302
+ );
231
303
  return path.resolve(directory, deprecatedUpload.main);
232
304
  } else {
233
305
  return;
@@ -239,13 +311,14 @@ function normalizeAndValidateMainField(
239
311
  */
240
312
  function normalizeAndValidateDev(
241
313
  diagnostics: Diagnostics,
242
- rawDev: Config["dev"]
243
- ): Config["dev"] {
314
+ rawDev: RawDevConfig
315
+ ): DevConfig {
244
316
  const {
245
- ip = "127.0.0.1",
317
+ ip = "localhost",
246
318
  port = 8787,
247
319
  local_protocol = "http",
248
320
  upstream_protocol = "https",
321
+ host,
249
322
  ...rest
250
323
  } = rawDev;
251
324
  validateAdditionalProperties(diagnostics, "dev", Object.keys(rest), []);
@@ -257,16 +330,19 @@ function normalizeAndValidateDev(
257
330
  "dev",
258
331
  "local_protocol",
259
332
  local_protocol,
260
- "string"
333
+ "string",
334
+ ["http", "https"]
261
335
  );
262
336
  validateOptionalProperty(
263
337
  diagnostics,
264
338
  "dev",
265
339
  "upstream_protocol",
266
340
  upstream_protocol,
267
- "string"
341
+ "string",
342
+ ["http", "https"]
268
343
  );
269
- return { ip, port, local_protocol, upstream_protocol };
344
+ validateOptionalProperty(diagnostics, "dev", "host", host, "string");
345
+ return { ip, port, local_protocol, upstream_protocol, host };
270
346
  }
271
347
 
272
348
  /**
@@ -302,7 +378,7 @@ function normalizeAndValidateMigrations(
302
378
  if (migration.renamed_classes !== undefined) {
303
379
  if (!Array.isArray(migration.renamed_classes)) {
304
380
  diagnostics.errors.push(
305
- `"migrations[${i}].renamed_classes" should be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
381
+ `Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
306
382
  migration.renamed_classes
307
383
  )}.`
308
384
  );
@@ -315,7 +391,7 @@ function normalizeAndValidateMigrations(
315
391
  )
316
392
  ) {
317
393
  diagnostics.errors.push(
318
- `"migrations[${i}].renamed_classes" should be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
394
+ `Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
319
395
  migration.renamed_classes
320
396
  )}.`
321
397
  );
@@ -340,25 +416,12 @@ function normalizeAndValidateSite(
340
416
  rawSite: Config["site"]
341
417
  ): Config["site"] {
342
418
  if (rawSite !== undefined) {
343
- const {
344
- bucket,
345
- "entry-point": entryPoint,
346
- include = [],
347
- exclude = [],
348
- ...rest
349
- } = rawSite;
419
+ const { bucket, include = [], exclude = [], ...rest } = rawSite;
350
420
  validateAdditionalProperties(diagnostics, "site", Object.keys(rest), []);
351
421
  validateRequiredProperty(diagnostics, "site", "bucket", bucket, "string");
352
- validateOptionalProperty(
353
- diagnostics,
354
- "site",
355
- "entry_point",
356
- entryPoint,
357
- "string"
358
- );
359
422
  validateTypedArray(diagnostics, "sites.include", include, "string");
360
423
  validateTypedArray(diagnostics, "sites.exclude", exclude, "string");
361
- return { bucket, "entry-point": entryPoint, include, exclude };
424
+ return { bucket, include, exclude };
362
425
  }
363
426
  return undefined;
364
427
  }
@@ -397,26 +460,28 @@ function normalizeAndValidateModulePaths(
397
460
  */
398
461
  function normalizeAndValidateEnvironment(
399
462
  diagnostics: Diagnostics,
400
- topLevelEnv: RawEnvironment,
401
- deprecatedRules: Environment["rules"] | undefined
463
+ configPath: string | undefined,
464
+ topLevelEnv: RawEnvironment
402
465
  ): Environment;
403
466
  /**
404
467
  * Validate the named environment configuration and return the normalized values.
405
468
  */
406
469
  function normalizeAndValidateEnvironment(
407
470
  diagnostics: Diagnostics,
471
+ configPath: string | undefined,
408
472
  rawEnv: RawEnvironment,
409
- deprecatedRules: Environment["rules"] | undefined,
410
473
  envName: string,
411
- config: Config,
474
+ topLevelEnv: Environment,
475
+ isLegacyEnv: boolean,
412
476
  rawConfig: RawConfig
413
477
  ): Environment;
414
478
  function normalizeAndValidateEnvironment(
415
479
  diagnostics: Diagnostics,
480
+ configPath: string | undefined,
416
481
  rawEnv: RawEnvironment,
417
- deprecatedRules: Environment["rules"] | undefined,
418
482
  envName = "top level",
419
- config?: Config | undefined,
483
+ topLevelEnv?: Environment | undefined,
484
+ isLegacyEnv?: boolean,
420
485
  rawConfig?: RawConfig | undefined
421
486
  ): Environment {
422
487
  deprecated(
@@ -457,7 +522,7 @@ function normalizeAndValidateEnvironment(
457
522
 
458
523
  const route = inheritable(
459
524
  diagnostics,
460
- config,
525
+ topLevelEnv,
461
526
  rawEnv,
462
527
  "route",
463
528
  isString,
@@ -465,26 +530,33 @@ function normalizeAndValidateEnvironment(
465
530
  );
466
531
  const routes = inheritable(
467
532
  diagnostics,
468
- config,
533
+ topLevelEnv,
469
534
  rawEnv,
470
535
  "routes",
471
- isStringArray,
536
+ all(isStringArray, isMutuallyExclusiveWith(rawEnv, "route")),
472
537
  undefined
473
538
  );
474
539
  const workers_dev = inheritable(
475
540
  diagnostics,
476
- config,
541
+ topLevelEnv,
477
542
  rawEnv,
478
543
  "workers_dev",
479
544
  isBoolean,
480
- !(routes || route)
545
+ undefined
546
+ );
547
+
548
+ const { deprecatedUpload, ...build } = normalizeAndValidateBuild(
549
+ diagnostics,
550
+ rawEnv,
551
+ rawEnv.build ?? topLevelEnv?.build ?? {},
552
+ configPath
481
553
  );
482
554
 
483
555
  const environment: Environment = {
484
556
  // Inherited fields
485
557
  account_id: inheritable(
486
558
  diagnostics,
487
- config,
559
+ topLevelEnv,
488
560
  rawEnv,
489
561
  "account_id",
490
562
  isString,
@@ -492,7 +564,7 @@ function normalizeAndValidateEnvironment(
492
564
  ),
493
565
  compatibility_date: inheritable(
494
566
  diagnostics,
495
- config,
567
+ topLevelEnv,
496
568
  rawEnv,
497
569
  "compatibility_date",
498
570
  isString,
@@ -500,7 +572,7 @@ function normalizeAndValidateEnvironment(
500
572
  ),
501
573
  compatibility_flags: inheritable(
502
574
  diagnostics,
503
- config,
575
+ topLevelEnv,
504
576
  rawEnv,
505
577
  "compatibility_flags",
506
578
  isStringArray,
@@ -508,7 +580,7 @@ function normalizeAndValidateEnvironment(
508
580
  ),
509
581
  jsx_factory: inheritable(
510
582
  diagnostics,
511
- config,
583
+ topLevelEnv,
512
584
  rawEnv,
513
585
  "jsx_factory",
514
586
  isString,
@@ -516,25 +588,52 @@ function normalizeAndValidateEnvironment(
516
588
  ),
517
589
  jsx_fragment: inheritable(
518
590
  diagnostics,
519
- config,
591
+ topLevelEnv,
520
592
  rawEnv,
521
593
  "jsx_fragment",
522
594
  isString,
523
595
  "React.Fragment"
524
596
  ),
597
+ tsconfig: validateAndNormalizeTsconfig(
598
+ diagnostics,
599
+ topLevelEnv,
600
+ rawEnv,
601
+ configPath
602
+ ),
525
603
  rules: validateAndNormalizeRules(
526
604
  diagnostics,
527
- config,
605
+ topLevelEnv,
528
606
  rawEnv,
529
- deprecatedRules,
607
+ deprecatedUpload?.rules,
530
608
  envName
531
609
  ),
532
- name: inheritable(diagnostics, config, rawEnv, "name", isString, undefined),
610
+ name: inheritableInLegacyEnvironments(
611
+ diagnostics,
612
+ isLegacyEnv,
613
+ topLevelEnv,
614
+ rawEnv,
615
+ "name",
616
+ isString,
617
+ appendEnvName(envName),
618
+ undefined
619
+ ),
620
+ main: normalizeAndValidateMainField(
621
+ configPath,
622
+ inheritable(
623
+ diagnostics,
624
+ topLevelEnv,
625
+ rawEnv,
626
+ "main",
627
+ isString,
628
+ undefined
629
+ ),
630
+ deprecatedUpload
631
+ ),
533
632
  route,
534
633
  routes,
535
634
  triggers: inheritable(
536
635
  diagnostics,
537
- config,
636
+ topLevelEnv,
538
637
  rawEnv,
539
638
  "triggers",
540
639
  isObjectWith("crons"),
@@ -542,17 +641,18 @@ function normalizeAndValidateEnvironment(
542
641
  ),
543
642
  usage_model: inheritable(
544
643
  diagnostics,
545
- config,
644
+ topLevelEnv,
546
645
  rawEnv,
547
646
  "usage_model",
548
647
  isOneOf("bundled", "unbound"),
549
648
  undefined
550
649
  ),
650
+ build,
551
651
  workers_dev,
552
652
  // Not inherited fields
553
653
  vars: notInheritable(
554
654
  diagnostics,
555
- config,
655
+ topLevelEnv,
556
656
  rawConfig,
557
657
  rawEnv,
558
658
  envName,
@@ -562,7 +662,7 @@ function normalizeAndValidateEnvironment(
562
662
  ),
563
663
  durable_objects: notInheritable(
564
664
  diagnostics,
565
- config,
665
+ topLevelEnv,
566
666
  rawConfig,
567
667
  rawEnv,
568
668
  envName,
@@ -574,7 +674,7 @@ function normalizeAndValidateEnvironment(
574
674
  ),
575
675
  kv_namespaces: notInheritable(
576
676
  diagnostics,
577
- config,
677
+ topLevelEnv,
578
678
  rawConfig,
579
679
  rawEnv,
580
680
  envName,
@@ -584,7 +684,7 @@ function normalizeAndValidateEnvironment(
584
684
  ),
585
685
  r2_buckets: notInheritable(
586
686
  diagnostics,
587
- config,
687
+ topLevelEnv,
588
688
  rawConfig,
589
689
  rawEnv,
590
690
  envName,
@@ -594,7 +694,7 @@ function normalizeAndValidateEnvironment(
594
694
  ),
595
695
  unsafe: notInheritable(
596
696
  diagnostics,
597
- config,
697
+ topLevelEnv,
598
698
  rawConfig,
599
699
  rawEnv,
600
700
  envName,
@@ -610,18 +710,41 @@ function normalizeAndValidateEnvironment(
610
710
  return environment;
611
711
  }
612
712
 
713
+ function validateAndNormalizeTsconfig(
714
+ diagnostics: Diagnostics,
715
+ topLevelEnv: Environment | undefined,
716
+ rawEnv: RawEnvironment,
717
+ configPath: string | undefined
718
+ ) {
719
+ const tsconfig = inheritable(
720
+ diagnostics,
721
+ topLevelEnv,
722
+ rawEnv,
723
+ "tsconfig",
724
+ isString,
725
+ undefined
726
+ );
727
+
728
+ return configPath && tsconfig
729
+ ? path.relative(
730
+ process.cwd(),
731
+ path.join(path.dirname(configPath), tsconfig)
732
+ )
733
+ : tsconfig;
734
+ }
735
+
613
736
  const validateAndNormalizeRules = (
614
737
  diagnostics: Diagnostics,
615
- config: Config | undefined,
738
+ topLevelEnv: Environment | undefined,
616
739
  rawEnv: RawEnvironment,
617
740
  deprecatedRules: Rule[] | undefined,
618
741
  envName: string
619
742
  ): Rule[] => {
620
- if (config === undefined) {
743
+ if (topLevelEnv === undefined) {
621
744
  // Only create errors/warnings for the top-level environment
622
745
  if (rawEnv.rules && deprecatedRules) {
623
746
  diagnostics.errors.push(
624
- `ERROR: You cannot configure both [rules] and [build.upload.rules] in your wrangler.toml. Delete the \`build.upload\` section.`
747
+ `You cannot configure both [rules] and [build.upload.rules] in your wrangler.toml. Delete the \`build.upload\` section.`
625
748
  );
626
749
  } else if (deprecatedRules) {
627
750
  diagnostics.warnings.push(
@@ -635,7 +758,7 @@ const validateAndNormalizeRules = (
635
758
 
636
759
  return inheritable(
637
760
  diagnostics,
638
- config,
761
+ topLevelEnv,
639
762
  rawEnv,
640
763
  "rules",
641
764
  validateRules(envName),
@@ -671,7 +794,9 @@ const validateRules =
671
794
 
672
795
  const validateRule: ValidatorFn = (diagnostics, field, value) => {
673
796
  if (typeof value !== "object" || value === null) {
674
- diagnostics.errors.push(`ERROR: should be an object.`);
797
+ diagnostics.errors.push(
798
+ `"${field}" should be an object but got ${JSON.stringify(value)}.`
799
+ );
675
800
  return false;
676
801
  }
677
802
  // Rules must have a type string and glob string array, and optionally a fallthrough boolean.
@@ -688,7 +813,7 @@ const validateRule: ValidatorFn = (diagnostics, field, value) => {
688
813
  ])
689
814
  ) {
690
815
  diagnostics.errors.push(
691
- `ERROR: bindings should have a string "type" field, which contains one of "ESModule", "CommonJS", "CompiledWasm", "Text", or "Data".`
816
+ `bindings should have a string "type" field, which contains one of "ESModule", "CommonJS", "CompiledWasm", "Text", or "Data".`
692
817
  );
693
818
  isValid = false;
694
819
  }
@@ -699,7 +824,7 @@ const validateRule: ValidatorFn = (diagnostics, field, value) => {
699
824
 
700
825
  if (!isOptionalProperty(rule, "fallthrough", "boolean")) {
701
826
  diagnostics.errors.push(
702
- `ERROR: should, optionally, have a boolean "fallthrough" field.`
827
+ `binding should, optionally, have a boolean "fallthrough" field.`
703
828
  );
704
829
  isValid = false;
705
830
  }
@@ -805,42 +930,34 @@ const validateBindingsProperty =
805
930
  return isValid;
806
931
  };
807
932
 
808
- /**
809
- * Get the names of the bindings collection in `value`.
810
- */
811
- const getBindingNames = (value: unknown): string[] =>
812
- ((value as { bindings: { name: string }[] })?.bindings ?? []).map(
813
- (binding) => binding.name
814
- );
815
-
816
933
  /**
817
934
  * Check that the given field is a valid "durable_object" binding object.
818
935
  */
819
936
  const validateDurableObjectBinding: ValidatorFn = (
820
937
  diagnostics,
821
- _field,
938
+ field,
822
939
  value
823
940
  ) => {
824
941
  if (typeof value !== "object" || value === null) {
825
- diagnostics.errors.push(`ERROR: should be an object.`);
942
+ diagnostics.errors.push(
943
+ `Expected "${field}" to be an object but got ${JSON.stringify(value)}`
944
+ );
826
945
  return false;
827
946
  }
828
947
 
829
948
  // Durable Object bindings must have a name and class_name, and optionally a script_name.
830
949
  let isValid = true;
831
950
  if (!isRequiredProperty(value, "name", "string")) {
832
- diagnostics.errors.push(
833
- `ERROR: bindings should have a string "name" field.`
834
- );
951
+ diagnostics.errors.push(`binding should have a string "name" field.`);
835
952
  isValid = false;
836
953
  }
837
954
  if (!isRequiredProperty(value, "class_name", "string")) {
838
- diagnostics.errors.push(`ERROR: should have a string "class_name" field.`);
955
+ diagnostics.errors.push(`binding should have a string "class_name" field.`);
839
956
  isValid = false;
840
957
  }
841
958
  if (!isOptionalProperty(value, "script_name", "string")) {
842
959
  diagnostics.errors.push(
843
- `ERROR: should, optionally, have a string "script_name" field.`
960
+ `binding should, optionally, have a string "script_name" field.`
844
961
  );
845
962
  isValid = false;
846
963
  }
@@ -853,16 +970,18 @@ const validateDurableObjectBinding: ValidatorFn = (
853
970
  *
854
971
  * TODO: further validation of known unsafe bindings.
855
972
  */
856
- const validateUnsafeBinding: ValidatorFn = (diagnostics, _field, value) => {
973
+ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
857
974
  if (typeof value !== "object" || value === null) {
858
- diagnostics.errors.push(`ERROR: should be an object.`);
975
+ diagnostics.errors.push(
976
+ `Expected ${field} to be an object but got ${JSON.stringify(value)}.`
977
+ );
859
978
  return false;
860
979
  }
861
980
 
862
981
  let isValid = true;
863
982
  // Unsafe bindings must have a name and type.
864
983
  if (!isRequiredProperty(value, "name", "string")) {
865
- diagnostics.errors.push(`ERROR: should have a string "name" field.`);
984
+ diagnostics.errors.push(`binding should have a string "name" field.`);
866
985
  isValid = false;
867
986
  }
868
987
  if (isRequiredProperty(value, "type", "string")) {
@@ -881,7 +1000,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, _field, value) => {
881
1000
  );
882
1001
  }
883
1002
  } else {
884
- diagnostics.errors.push(`ERROR: should have a string "type" field.`);
1003
+ diagnostics.errors.push(`binding should have a string "type" field.`);
885
1004
  isValid = false;
886
1005
  }
887
1006
  return isValid;
@@ -1015,3 +1134,99 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
1015
1134
  }
1016
1135
  return isValid;
1017
1136
  };
1137
+
1138
+ /**
1139
+ * Check that bindings whose names might conflict, don't.
1140
+ *
1141
+ * We don't want to have, for example, a KV namespace named "DATA"
1142
+ * and a Durable Object also named "DATA". Then it would be ambiguous
1143
+ * what exactly would live at `env.DATA` (or in the case of service workers,
1144
+ * the `DATA` global).
1145
+ */
1146
+ const validateBindingsHaveUniqueNames = (
1147
+ diagnostics: Diagnostics,
1148
+ {
1149
+ durable_objects,
1150
+ kv_namespaces,
1151
+ r2_buckets,
1152
+ text_blobs,
1153
+ unsafe,
1154
+ vars,
1155
+ wasm_modules,
1156
+ }: Partial<Config>
1157
+ ): boolean => {
1158
+ let hasDuplicates = false;
1159
+
1160
+ const bindingsGroupedByType = {
1161
+ "Durable Object": getBindingNames(durable_objects),
1162
+ "KV Namespace": getBindingNames(kv_namespaces),
1163
+ "R2 Bucket": getBindingNames(r2_buckets),
1164
+ "Text Blob": getBindingNames(text_blobs),
1165
+ Unsafe: getBindingNames(unsafe),
1166
+ "Environment Variable": getBindingNames(vars),
1167
+ "WASM Module": getBindingNames(wasm_modules),
1168
+ } as Record<string, string[]>;
1169
+
1170
+ const bindingsGroupedByName: Record<string, string[]> = {};
1171
+
1172
+ for (const bindingType in bindingsGroupedByType) {
1173
+ const bindingNames = bindingsGroupedByType[bindingType];
1174
+
1175
+ for (const bindingName of bindingNames) {
1176
+ if (!(bindingName in bindingsGroupedByName)) {
1177
+ bindingsGroupedByName[bindingName] = [];
1178
+ }
1179
+
1180
+ bindingsGroupedByName[bindingName].push(bindingType);
1181
+ }
1182
+ }
1183
+
1184
+ for (const bindingName in bindingsGroupedByName) {
1185
+ const bindingTypes = bindingsGroupedByName[bindingName];
1186
+ if (bindingTypes.length < 2) {
1187
+ // there's only one (or zero) binding(s) with this name, which is fine, actually
1188
+ continue;
1189
+ }
1190
+
1191
+ hasDuplicates = true;
1192
+
1193
+ // there's two types of duplicates we want to look for:
1194
+ // - bindings with the same name of the same type (e.g. two Durable Objects both named "OBJ")
1195
+ // - bindings with the same name of different types (a KV namespace and DO both named "DATA")
1196
+
1197
+ const sameType = bindingTypes
1198
+ // filter once to find duplicate binding types
1199
+ .filter((type, i) => bindingTypes.indexOf(type) !== i)
1200
+ // filter twice to only get _unique_ duplicate binding types
1201
+ .filter(
1202
+ (type, i, duplicateBindingTypes) =>
1203
+ duplicateBindingTypes.indexOf(type) === i
1204
+ );
1205
+
1206
+ const differentTypes = bindingTypes.filter(
1207
+ (type, i) => bindingTypes.indexOf(type) === i
1208
+ );
1209
+
1210
+ if (differentTypes.length > 1) {
1211
+ // we have multiple different types using the same name
1212
+ diagnostics.errors.push(
1213
+ `${bindingName} assigned to ${ENGLISH.format(differentTypes)} bindings.`
1214
+ );
1215
+ }
1216
+
1217
+ sameType.forEach((bindingType) => {
1218
+ diagnostics.errors.push(
1219
+ `${bindingName} assigned to multiple ${bindingType} bindings.`
1220
+ );
1221
+ });
1222
+ }
1223
+
1224
+ if (hasDuplicates) {
1225
+ const problem =
1226
+ "Bindings must have unique names, so that they can all be referenced in the worker.";
1227
+ const resolution = "Please change your bindings to have unique names.";
1228
+ diagnostics.errors.push(`${problem}\n${resolution}`);
1229
+ }
1230
+
1231
+ return !hasDuplicates;
1232
+ };