Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions .changeset/fix-type-generation-multi-worker-env.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": patch
---

fix: resolve secondary worker types when environment overrides the worker name in multi-worker type generation

When running `wrangler types` with multiple `-c` config flags and the secondary worker has named environments that override the worker name (e.g. a worker named `do-worker` with env `staging` whose effective name becomes `do-worker-staging`), service bindings and Durable Object bindings in the primary worker that reference `do-worker-staging` now correctly resolve to the typed entry point instead of falling back to an unresolved comment type such as `DurableObjectNamespace /* MyClass from do-worker-staging */`.

The fix extends the secondary entries map to also register environment-specific worker names, so that lookups by the env-qualified name (e.g. `do-worker-staging`) resolve to the same source file as the base worker name.
115 changes: 115 additions & 0 deletions packages/wrangler/src/__tests__/type-generation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,121 @@ describe("generate types", () => {
`);
});

it("should resolve secondary worker types when env overrides the worker name", async ({
expect,
}) => {
// Reproduces https://github.com/cloudflare/workers-sdk/issues/12971
// When a secondary worker has named environments that override the worker name
// (e.g. name "do-worker" with env "staging" whose effective name is "do-worker-staging"),
// service/DO bindings in the primary worker that reference "do-worker-staging" should
// resolve to the typed entry instead of falling back to an unresolved comment type.
fs.mkdirSync("primary");
fs.mkdirSync("secondary");

fs.writeFileSync(
"./secondary/index.ts",
`import { DurableObject } from 'cloudflare:workers';
export default { async fetch() {} };
export class MyDurableObject extends DurableObject {}
`
);
fs.writeFileSync(
"./secondary/wrangler.jsonc",
JSON.stringify({
compatibility_date: "2022-01-12",
name: "do-worker",
main: "./index.ts",
durable_objects: {
bindings: [{ name: "MY_DO", class_name: "MyDurableObject" }],
},
migrations: [{ tag: "v1", new_classes: ["MyDurableObject"] }],
env: {
staging: {
name: "do-worker-staging",
durable_objects: {
bindings: [{ name: "MY_DO", class_name: "MyDurableObject" }],
},
},
},
}),
"utf-8"
);

fs.writeFileSync(
"./primary/index.ts",
"export default { async fetch() {} };"
);
fs.writeFileSync(
"./primary/wrangler.jsonc",
JSON.stringify({
compatibility_date: "2022-01-12",
name: "primary-worker",
main: "./index.ts",
services: [{ binding: "DO_WORKER", service: "do-worker" }],
durable_objects: {
bindings: [
{
name: "DO_OBJECT",
class_name: "MyDurableObject",
script_name: "do-worker",
},
],
},
env: {
staging: {
name: "primary-worker-staging",
services: [
{ binding: "DO_WORKER", service: "do-worker-staging" },
],
durable_objects: {
bindings: [
{
name: "DO_OBJECT",
class_name: "MyDurableObject",
script_name: "do-worker-staging",
},
],
},
},
},
}),
"utf-8"
);

await runWrangler(
"types --include-runtime=false -c primary/wrangler.jsonc -c secondary/wrangler.jsonc --path primary/worker-configuration.d.ts"
);

expect(std.out).toMatchInlineSnapshot(`
"
⛅️ wrangler x.x.x
──────────────────
- Found Worker 'do-worker' at 'secondary/index.ts' (secondary/wrangler.jsonc)
Generating project types...

declare namespace Cloudflare {
interface GlobalProps {
mainModule: typeof import("./index");
}
interface StagingEnv {
DO_OBJECT: DurableObjectNamespace<import("../secondary/index").MyDurableObject>;
DO_WORKER: Service<typeof import("../secondary/index").default>;
}
interface Env {
DO_OBJECT: DurableObjectNamespace<import("../secondary/index").MyDurableObject>;
DO_WORKER: Service<typeof import("../secondary/index").default>;
}
}
interface Env extends Cloudflare.Env {}

────────────────────────────────────────────────────────────
✨ Types written to primary/worker-configuration.d.ts

📣 Remember to rerun 'wrangler types' after you change your wrangler.jsonc file.
"
`);
});

it("should create a DTS file at the location that the command is executed from", async ({
expect,
}) => {
Expand Down
16 changes: 16 additions & 0 deletions packages/wrangler/src/type-generation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@ export const typesCommand = createCommand({
`- Found Worker '${key}' at '${relative(process.cwd(), serviceEntry.file)}' (${secondaryConfig.configPath})`
)
);

// Also register environment-specific names (e.g. "worker-foo" for env "foo")
// so that service/DO bindings referencing those names can be resolved.
const { rawConfig } = experimental_readRawConfig({
config: secondaryConfig.configPath,
});
for (const envName of Object.keys(rawConfig.env ?? {})) {
const envConfig = readConfig({
config: secondaryConfig.configPath,
env: envName,
});
const envKey = envConfig.name;
if (envKey && envKey !== key && !secondaryEntries.has(envKey)) {
secondaryEntries.set(envKey, serviceEntry);
}
}
} else {
throw new UserError(
`Could not resolve entry point for service config '${secondaryConfig}'.`
Expand Down
Loading