Skip to content

Commit

Permalink
add loose-vars option to wrangler types
Browse files Browse the repository at this point in the history
  • Loading branch information
dario-piotrowicz committed Jan 1, 2025
1 parent 376a46b commit b4ac0d9
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 38 deletions.
46 changes: 46 additions & 0 deletions .changeset/unlucky-timers-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
"wrangler": minor
---

add `loose-vars` option to `wrangler types`

add a new `--loose-vars` option to `wrangler types` that developers can use to get more
loose types for their variables instead of literal and union types

these more loose types can be useful when developers change often their `vars` values,
even more so when multiple environments are involved

## Example

With a toml containing:

```toml
[vars]
MY_VARIABLE = "production_value"
MY_NUMBERS = [1, 2, 3]

[env.staging.vars]
MY_VARIABLE = "staging_value"
MY_NUMBERS = [7, 8, 9]
```

the `wrangler types` command would generate the following interface:

```
interface Env {
MY_VARIABLE: "production_value" | "staging_value";
MY_NUMBERS: [1,2,3] | [7,8,9];
}
```

while `wrangler types --loose-vars` would instead generate:

```
interface Env {
MY_VARIABLE: string;
MY_NUMBERS: number[];
}
```

(allowing the developer to easily change their toml variables without the
risk of braking typescript types)
121 changes: 85 additions & 36 deletions packages/wrangler/src/__tests__/type-generation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,35 @@ describe("generateTypes()", () => {
`);
});

it("should respect the loose-vars option", async () => {
fs.writeFileSync(
"./wrangler.toml",
TOML.stringify({
vars: {
varStr: "A from wrangler toml",
varArrNum: [1, 2, 3],
varArrMix: [1, "two", 3, true],
varObj: { test: true },
},
} as TOML.JsonMap),
"utf-8"
);

await runWrangler("types --loose-vars");

expect(std.out).toMatchInlineSnapshot(`
"Generating project types...
interface Env {
varStr: string;
varArrNum: number[];
varArrMix: (boolean|number|string)[];
varObj: object;
}
"
`);
});

it("should override vars with secrets", async () => {
fs.writeFileSync(
"./wrangler.toml",
Expand Down Expand Up @@ -635,48 +664,68 @@ describe("generateTypes()", () => {
`);
});

it("should produce unions where appropriate for vars present in multiple environments", async () => {
fs.writeFileSync(
"./wrangler.toml",
TOML.stringify({
vars: {
MY_VAR: "a var",
MY_VAR_A: "A (dev)",
MY_VAR_B: { value: "B (dev)" },
MY_VAR_C: ["a", "b", "c"],
},
env: {
production: {
vars: {
MY_VAR: "a var",
MY_VAR_A: "A (prod)",
MY_VAR_B: { value: "B (prod)" },
MY_VAR_C: [1, 2, 3],
},
describe("vars present in multiple environments", () => {
beforeEach(() => {
fs.writeFileSync(
"./wrangler.toml",
TOML.stringify({
vars: {
MY_VAR: "a var",
MY_VAR_A: "A (dev)",
MY_VAR_B: { value: "B (dev)" },
MY_VAR_C: ["a", "b", "c"],
},
staging: {
vars: {
MY_VAR_A: "A (stag)",
env: {
production: {
vars: {
MY_VAR: "a var",
MY_VAR_A: "A (prod)",
MY_VAR_B: { value: "B (prod)" },
MY_VAR_C: [1, 2, 3],
},
},
staging: {
vars: {
MY_VAR_A: "A (stag)",
},
},
},
},
} as TOML.JsonMap),
"utf-8"
);
} as TOML.JsonMap),
"utf-8"
);
});

await runWrangler("types");
it("should produce string and union types for variables (default)", async () => {
await runWrangler("types");

expect(std.out).toMatchInlineSnapshot(`
"Generating project types...
expect(std.out).toMatchInlineSnapshot(`
"Generating project types...
interface Env {
MY_VAR: \\"a var\\";
MY_VAR_A: \\"A (dev)\\" | \\"A (prod)\\" | \\"A (stag)\\";
MY_VAR_C: [\\"a\\",\\"b\\",\\"c\\"] | [1,2,3];
MY_VAR_B: {\\"value\\":\\"B (dev)\\"} | {\\"value\\":\\"B (prod)\\"};
}
"
`);
interface Env {
MY_VAR: \\"a var\\";
MY_VAR_A: \\"A (dev)\\" | \\"A (prod)\\" | \\"A (stag)\\";
MY_VAR_C: [\\"a\\",\\"b\\",\\"c\\"] | [1,2,3];
MY_VAR_B: {\\"value\\":\\"B (dev)\\"} | {\\"value\\":\\"B (prod)\\"};
}
"
`);
});

it("should produce loose types for variables (with --loose-vars)", async () => {
await runWrangler("types --loose-vars=true");

expect(std.out).toMatchInlineSnapshot(`
"Generating project types...
interface Env {
MY_VAR: string;
MY_VAR_A: string;
MY_VAR_C: string[] | number[];
MY_VAR_B: object;
}
"
`);
});
});

describe("customization", () => {
Expand Down
24 changes: 22 additions & 2 deletions packages/wrangler/src/type-generation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export function typesOptions(yargs: CommonYargsArgv) {
type: "string",
describe: "The path of the generated runtime types file",
demandOption: false,
})
.option("loose-vars", {
type: "boolean",
default: false,
describe:
'Generate "loose"/generic types instead of literal and union types for variables',
});
}

Expand Down Expand Up @@ -147,7 +153,8 @@ export async function typesHandler(
configBindingsWithSecrets,
config,
envInterface,
outputPath
outputPath,
args.looseVars
);
}

Expand Down Expand Up @@ -245,7 +252,8 @@ async function generateTypes(
configToDTS: ConfigToDTS,
config: Config,
envInterface: string,
outputPath: string
outputPath: string,
looseVars: boolean
) {
const configContainsEntrypoint =
config.main !== undefined || !!config.site?.["entry-point"];
Expand Down Expand Up @@ -296,6 +304,18 @@ async function generateTypes(
varInfo
.map(({ value }) => value)
.map((varValue) => {
if (looseVars) {
if (Array.isArray(varValue)) {
const typesInArray = [
...new Set(varValue.map((item) => typeof item)),
].sort();
if (typesInArray.length === 1) {
return `${typesInArray[0]}[]`;
}
return `(${typesInArray.join("|")})[]`;
}
return typeof varValue;
}
if (
typeof varValue === "string" ||
typeof varValue === "number" ||
Expand Down

0 comments on commit b4ac0d9

Please sign in to comment.