Skip to content

Commit e277a4b

Browse files
Refactor index.tsx (and config.ts) to pass strict-null checks
This actually slightly changes the processing of the environment configuration. Previously there were corner cases where the configuration might just crash. These are now handled more cleanly with more appropriate warnings. The yargs commands have been refactored slightly to be more consistent. Now each command that can be either in local or remote mode is structured the same way. The `config` is extracted from the args, then there is an `if-then-else` statement that handles the local and remote cases. This ensures that the `account_id` has the correct typings in remote mode as needed.
1 parent 402c77d commit e277a4b

File tree

3 files changed

+318
-209
lines changed

3 files changed

+318
-209
lines changed

.changeset/orange-cycles-rule.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Do not crash when processing environment configuration.
6+
7+
Previously there were corner cases where the configuration might just crash.
8+
These are now handled more cleanly with more appropriate warnings.

packages/wrangler/src/config.ts

+159
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import assert from "node:assert";
2+
13
/**
24
* This is the static type definition for the configuration object.
35
* It reflects the configuration that you can write in wrangler.toml,
@@ -485,3 +487,160 @@ export async function validateConfig(
485487

486488
return results;
487489
}
490+
491+
/**
492+
* Process the environments (`env`) specified in the `config`.
493+
*
494+
* The environments configuration is complicated since each environment is a customized version of the main config.
495+
* Some of the configuration can be inherited from the main config, while other configuration must replace what is in the main config.
496+
*
497+
* This function ensures that each environment is set up correctly with inherited configuration, as necessary.
498+
* It will log a warning if an environment is missing required configuration.
499+
*/
500+
export function normaliseAndValidateEnvironmentsConfig(config: Config) {
501+
if (config.env == undefined) {
502+
// There are no environments specified so there is nothing to do here.
503+
return;
504+
}
505+
506+
const environments = config.env;
507+
508+
for (const envKey of Object.keys(environments)) {
509+
const environment = environments[envKey];
510+
511+
// Given how TOML works, there should never be an environment containing nothing.
512+
// I.e. if there is a section in a TOML file, then the parser will create an object for it.
513+
// But it may be possible in the future if we change how the configuration is stored.
514+
assert(
515+
environment,
516+
`Environment ${envKey} is specified in the config but not defined.`
517+
);
518+
519+
// Fall back on "inherited fields" from the config, if not specified in the environment.
520+
const inheritedFields = [
521+
"name",
522+
"account_id",
523+
"workers_dev",
524+
"compatibility_date",
525+
"compatibility_flags",
526+
"zone_id",
527+
"routes",
528+
"route",
529+
"jsx_factory",
530+
"jsx_fragment",
531+
"site",
532+
"triggers",
533+
"usage_model",
534+
];
535+
for (const inheritedField of inheritedFields) {
536+
if (config[inheritedField] !== undefined) {
537+
if (environment[inheritedField] === undefined) {
538+
environment[inheritedField] = config[inheritedField]; // TODO: - shallow or deep copy?
539+
}
540+
}
541+
}
542+
543+
// Warn if there is a "required" field in the top level config that has not been specified specified in the environment.
544+
// These required fields are `vars`, `durable_objects`, `kv_namespaces` and `experimental_services`.
545+
// Each of them has different characteristics that need to be checked.
546+
547+
// `vars` is just an object
548+
if (config.vars !== undefined) {
549+
if (environment.vars === undefined) {
550+
console.warn(
551+
`In your configuration, "vars" exists at the top level, but not on "env.${envKey}".\n` +
552+
`This is not what you probably want, since "vars" is not inherited by environments.\n` +
553+
`Please add "vars" to "env.${envKey}".`
554+
);
555+
} else {
556+
for (const varField of Object.keys(config.vars)) {
557+
if (!(varField in environment.vars)) {
558+
console.warn(
559+
`In your configuration, "vars.${varField}" exists at the top level, but not on "env.${envKey}".\n` +
560+
`This is not what you probably want, since "vars" is not inherited by environments.\n` +
561+
`Please add "vars.${varField}" to "env.${envKey}".`
562+
);
563+
}
564+
}
565+
}
566+
}
567+
568+
// `durable_objects` is an object containing a `bindings` array
569+
if (config.durable_objects !== undefined) {
570+
if (environment.durable_objects === undefined) {
571+
console.warn(
572+
`In your configuration, "durable_objects.bindings" exists at the top level, but not on "env.${envKey}".\n` +
573+
`This is not what you probably want, since "durable_objects" is not inherited by environments.\n` +
574+
`Please add "durable_objects.bindings" to "env.${envKey}".`
575+
);
576+
} else {
577+
const envBindingNames = new Set(
578+
environment.durable_objects.bindings.map((b) => b.name)
579+
);
580+
for (const bindingName of config.durable_objects.bindings.map(
581+
(b) => b.name
582+
)) {
583+
if (!envBindingNames.has(bindingName)) {
584+
console.warn(
585+
`In your configuration, there is a durable_objects binding with name "${bindingName}" at the top level, but not on "env.${envKey}".\n` +
586+
`This is not what you probably want, since "durable_objects" is not inherited by environments.\n` +
587+
`Please add a binding for "${bindingName}" to "env.${envKey}.durable_objects.bindings".`
588+
);
589+
}
590+
}
591+
}
592+
}
593+
594+
// `kv_namespaces` contains an array of namespace bindings
595+
if (config.kv_namespaces !== undefined) {
596+
if (environment.kv_namespaces === undefined) {
597+
console.warn(
598+
`In your configuration, "kv_namespaces" exists at the top level, but not on "env.${envKey}".\n` +
599+
`This is not what you probably want, since "kv_namespaces" is not inherited by environments.\n` +
600+
`Please add "kv_namespaces" to "env.${envKey}".`
601+
);
602+
} else {
603+
const envBindings = new Set(
604+
environment.kv_namespaces.map((kvNamespace) => kvNamespace.binding)
605+
);
606+
for (const bindingName of config.kv_namespaces.map(
607+
(kvNamespace) => kvNamespace.binding
608+
)) {
609+
if (!envBindings.has(bindingName)) {
610+
console.warn(
611+
`In your configuration, there is a kv_namespaces with binding "${bindingName}" at the top level, but not on "env.${envKey}".\n` +
612+
`This is not what you probably want, since "kv_namespaces" is not inherited by environments.\n` +
613+
`Please add a binding for "${bindingName}" to "env.${envKey}.kv_namespaces".`
614+
);
615+
}
616+
}
617+
}
618+
}
619+
620+
// `experimental_services` contains an array of namespace bindings
621+
if (config.experimental_services !== undefined) {
622+
if (environment.experimental_services === undefined) {
623+
console.warn(
624+
`In your configuration, "experimental_services" exists at the top level, but not on "env.${envKey}".\n` +
625+
`This is not what you probably want, since "experimental_services" is not inherited by environments.\n` +
626+
`Please add "experimental_services" to "env.${envKey}".`
627+
);
628+
} else {
629+
const envBindingNames = new Set(
630+
environment.experimental_services.map((service) => service.name)
631+
);
632+
for (const bindingName of config.experimental_services.map(
633+
(service) => service.name
634+
)) {
635+
if (!envBindingNames.has(bindingName)) {
636+
console.warn(
637+
`In your configuration, there is a experimental_services with binding name "${bindingName}" at the top level, but not on "env.${envKey}".\n` +
638+
`This is not what you probably want, since "experimental_services" is not inherited by environments.\n` +
639+
`Please add a service for "${bindingName}" to "env.${envKey}.experimental_services".`
640+
);
641+
}
642+
}
643+
}
644+
}
645+
}
646+
}

0 commit comments

Comments
 (0)