Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/nasty-kiwis-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"wrangler": minor
Copy link
Contributor Author

@emily-shen emily-shen Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

major? pretty sure there's nothing breaking. inclusion in v4 is mostly for politeness as there's a fair amount of CI usage

---

Include runtime types in the output of `wrangler types` by default

`wrangler types` will now produce one file that contains both `Env` types and runtime types based on your compatibility date and flags. This is located at `worker-configuration.d.ts` by default.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth taking this opportunity to rename this file, maybe to cloudflare.d.ts? cc @irvinebroque if you have any thoughts here


This behaviour was previously gated behind `--experimental-include-runtime`. That flag is no longer necessary and has been removed. It has been replaced by `--include-runtime` and `--include-env`, both of which are set to `true` by default. If you were previously using `--x-include-runtime`, you can drop that flag and remove the separate `runtime.d.ts` file.

If you were previously using `@cloudflare/workers-types` we recommend you run uninstall (e.g. `npm uninstall @cloudflare/workers-types`) and run `wrangler types` instead. Note that `@cloudflare/workers-types` will continue to be published.
7 changes: 7 additions & 0 deletions .changeset/shiny-donkeys-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

feat: prompt users to rerun `wrangler types` during `wrangler dev`

If a generated types file is found at the default output location of `wrangler types` (`worker-configuration.d.ts`), remind users to rerun `wrangler types` if it looks like they're out of date.
16 changes: 0 additions & 16 deletions fixtures/type-generation/package.json

This file was deleted.

This file was deleted.

9 changes: 0 additions & 9 deletions fixtures/type-generation/vitest.config.mts

This file was deleted.

4 changes: 0 additions & 4 deletions fixtures/type-generation/wrangler.toml

This file was deleted.

223 changes: 111 additions & 112 deletions packages/wrangler/e2e/types.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
import { readFileSync } from "node:fs";
import { writeFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { dedent } from "../src/utils/dedent";
Expand All @@ -11,6 +11,8 @@ const seed = {
main = "src/index.ts"
compatibility_date = "2023-01-01"
compatibility_flags = ["nodejs_compat", "no_global_navigator"]
[vars]
MY_VAR = "my-var-value"
`,
"src/index.ts": dedent`
export default {
Expand All @@ -29,159 +31,156 @@ const seed = {
};

describe("types", () => {
it("should not generate runtime types without flag", async () => {
it("should generate runtime types without a flag", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed(seed);
const output = await helper.run(`wrangler types`);

expect(output.stdout).not.toContain(`Generating runtime types...`);
expect(output.stdout).toContain("Generating runtime types...");
expect(output.stdout).toContain("Runtime types generated.");
expect(output.stdout).toContain(
"✨ Types written to worker-configuration.d.ts"
);
expect(output.stdout).toContain("📖 Read about runtime types");
});

it("should generate runtime types at the default path", async () => {
it("should generate runtime types and env types in one file at the default path", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed(seed);
const output = await helper.run(`wrangler types --x-include-runtime`);

const fileExists = existsSync(
path.join(helper.tmpPath, "./.wrangler/types/runtime.d.ts")
);

expect(fileExists).toEqual(true);
expect(output.stdout).toContain(`Generating runtime types...`);
expect(output.stdout).toContain(`Generating project types...`);
expect(output.stdout).toContain(
`✨ Runtime types written to ./.wrangler/types/runtime.d.ts`
);
expect(output.stdout).toContain(
`"types": ["./.wrangler/types/runtime.d.ts"]`
);
const output = await helper.run(`wrangler types`);
expect(output.stdout).toContain("Generating project types...");
expect(output.stdout).toContain("interface Env {");
expect(output.stdout).toContain("Generating runtime types...");
expect(output.stdout).toContain("Runtime types generated.");
expect(output.stdout).toContain(
`📣 Since you have Node.js compatibility mode enabled, you should consider adding Node.js for TypeScript by running "npm i --save-dev @types/[email protected]". Please see the docs for more details: https://developers.cloudflare.com/workers/languages/typescript/#transitive-loading-of-typesnode-overrides-cloudflareworkers-types`
"✨ Types written to worker-configuration.d.ts"
);
expect(output.stdout).toContain(
`Remember to run 'wrangler types --x-include-runtime' again if you change 'compatibility_date' or 'compatibility_flags' in your wrangler.toml file.`
const file = readFileSync(
path.join(helper.tmpPath, "./worker-configuration.d.ts"),
"utf8"
);
expect(file).contains('declare module "cloudflare:workers"');
expect(file).contains("interface Env");
});

it("should generate runtime types at the provided path", async () => {
it("should be able to generate an Env type only", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed(seed);
const output = await helper.run(
`wrangler types --x-include-runtime="./types.d.ts"`
const output = await helper.run(`wrangler types --include-runtime=false`);
expect(output.stdout).not.toContain("Generating runtime types...");
const file = readFileSync(
path.join(helper.tmpPath, "./worker-configuration.d.ts"),
"utf8"
);

const fileExists = existsSync(path.join(helper.tmpPath, "./types.d.ts"));

expect(fileExists).toEqual(true);
expect(output.stdout).toContain(`✨ Runtime types written to ./types.d.ts`);
expect(output.stdout).toContain(`"types": ["./types.d.ts"]`);
expect(file).toMatchInlineSnapshot(`
"// Generated by Wrangler by running \`wrangler types --include-runtime=false\` (hash: 7fbca0b39560512499078acfe5f450c0)
interface Env {
MY_VAR: "my-var-value";
}
"
`);
});

it("should generate types", async () => {
it("should include header with version information in the generated types", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed(seed);
await helper.run(`wrangler types --x-include-runtime="./types.d.ts"`);
await helper.run(`wrangler types "./types.d.ts" `);

const file = (
await readFile(path.join(helper.tmpPath, "./types.d.ts"))
).toString();
const lines = readFileSync(
path.join(helper.tmpPath, "./types.d.ts"),
"utf8"
).split("\n");

expect(file).contains('declare module "cloudflare:workers"');
expect(lines[0]).toMatchInlineSnapshot(
`"// Generated by Wrangler by running \`wrangler types ./types.d.ts\` (hash: 7fbca0b39560512499078acfe5f450c0)"`
);
expect(lines[1]).match(
/\/\/ Runtime types generated with workerd@1\.\d{8}\.\d \d{4}-\d{2}-\d{2} ([a-z_]+,?)*/
);
});

it("should recommend to uninstall @cloudflare/workers-types", async () => {
it("should include header with wrangler command that generated it", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed({
...seed,
"tsconfig.json": dedent`
{
"compilerOptions": {
"types": ["@cloudflare/workers-types"]
}
}
`,
"wranglerA.toml": dedent`
name = "test-worker"
main = "src/index.ts"
compatibility_date = "2023-01-01"
`,
});
const output = await helper.run(
`wrangler types --x-include-runtime="./types.d.ts"`
await helper.run(
"wrangler types -c wranglerA.toml --env-interface MyCloudflareEnv ./cflare-env.d.ts"
);

expect(output.stdout).toContain(
`📣 You can now uninstall "@cloudflare/workers-types".`
const lines = readFileSync(
path.join(helper.tmpPath, "./cflare-env.d.ts"),
"utf8"
).split("\n");

expect(lines[0]).toMatchInlineSnapshot(
`"// Generated by Wrangler by running \`wrangler types -c wranglerA.toml --env-interface MyCloudflareEnv ./cflare-env.d.ts\` (hash: 8fcf1ed67a52a2d34d6d34c3068e89b8)"`
);
expect(lines[1]).match(
/\/\/ Runtime types generated with workerd@1\.\d{8}\.\d \d{4}-\d{2}-\d{2} ([a-z_]+,?)*/
);
});

it("should not recommend to install @types/node if 'node' exists in types array", async () => {
it("should not regenerate runtime types if the header matches, but should regenerate env types", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed({
...seed,
"tsconfig.json": dedent`
{
"compilerOptions": {
"types": ["node"]
}
}
`,
});
const output = await helper.run(
`wrangler types --x-include-runtime="./types.d.ts"`
await helper.seed(seed);
await helper.run(`wrangler types`);

const typesPath = path.join(helper.tmpPath, "worker-configuration.d.ts");
const file = readFileSync(typesPath, "utf8").split("\n");

await writeFile(
typesPath,
[
file[0],
file[1],
"FAKE ENV",
"// Begin runtime types",
"FAKE RUNTIME",
].join("\n")
);

expect(output.stdout).not.toContain(
`📣 Since you have Node.js compatibility mode enabled, you should consider adding Node.js for TypeScript by running "npm i --save-dev @types/[email protected]". Please see the docs for more details: https://developers.cloudflare.com/workers/languages/typescript/#transitive-loading-of-typesnode-overrides-cloudflareworkers-types`
);
await helper.run(`wrangler types`);

const file2 = readFileSync(typesPath, "utf8");

// regenerates env types
expect(file2).toContain("interface Env {");
// uses cached runtime types
expect(file2).toContain("// Begin runtime types");
expect(file2).toContain("FAKE RUNTIME");
});

it("should not error with nodejs_compat flags", async () => {
it("should prompt you to update types if they've been changed", async () => {
const helper = new WranglerE2ETestHelper();
await helper.seed({
...seed,
"wrangler.toml": dedent`
await helper.seed(seed);
await helper.run(`wrangler types`);
seed["wrangler.toml"] = dedent`
name = "test-worker"
main = "src/index.ts"
compatibility_date = "2023-01-01"
compatibility_flags = ["nodejs_compat", "experimental:nodejs_compat_v2"]
`,
});

const output = await helper.run(
`wrangler types --x-include-runtime="./types.d.ts"`
);

expect(output.stderr).toBe("");
expect(output.status).toBe(0);
});
it("should include header with version information in the generated types", async () => {
const helper = new WranglerE2ETestHelper();
compatibility_flags = ["nodejs_compat", "no_global_navigator"]
[vars]
BEEP = "BOOP"
`;
await helper.seed(seed);
await helper.run(`wrangler types --x-include-runtime="./types.d.ts"`);

const file = (
await readFile(path.join(helper.tmpPath, "./types.d.ts"))
).toString();

expect(file.split("\n")[0]).match(
/\/\/ Runtime types generated with workerd@1\.\d+\.\d \d\d\d\d-\d\d-\d\d ([a-z_]+,?)*/
);
});
it("should not regenerate types if the header matches", async () => {
const helper = new WranglerE2ETestHelper();
const worker = helper.runLongLived("wrangler dev");
await worker.readUntil(/❓ Your types might be out of date./);
seed["wrangler.toml"] = dedent`
name = "test-worker"
main = "src/index.ts"
compatibility_date = "2023-01-01"
compatibility_flags = ["nodejs_compat"]
[vars]
BEEP = "BOOP"
ASDf = "ADSfadsf"
`;
await helper.seed(seed);
await helper.run(`wrangler types --x-include-runtime`);

const runtimeTypesFile = path.join(
helper.tmpPath,
"./.wrangler/types/runtime.d.ts"
);
const file = (await readFile(runtimeTypesFile)).toString();

const header = file.split("\n")[0];

await writeFile(runtimeTypesFile, header + "\n" + "SOME_RANDOM_DATA");

await helper.run(`wrangler types --x-include-runtime`);

const file2 = (await readFile(runtimeTypesFile)).toString();

expect(file2.split("\n")[1]).toBe("SOME_RANDOM_DATA");
await worker.readUntil(/❓ Your types might be out of date./);
});
});
Loading
Loading