Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ describe("Build Output Specification", () => {
expect(response).toBe("hello from text binding");
});

test("serves the additional module", async ({ expect }) => {
const response = await getTextResponse("/additional-module");
expect(response).toBe("hello from additional module\n");
});

test("serves static assets", async ({ expect }) => {
const response = await getTextResponse("/static.txt");
expect(response.trim()).toBe("static asset");
Expand All @@ -44,6 +49,7 @@ describe.runIf(isBuild)("Build Output Specification files", () => {
"bundle",
config.manifest.mainModule
);
expect(config.manifest.mainModule).toMatch(/^chunks\/index-[\w-]+\.mjs$/);
expect(fs.existsSync(entryPath)).toBe(true);
});

Expand All @@ -63,7 +69,7 @@ describe.runIf(isBuild)("Build Output Specification files", () => {
expect(config).not.toHaveProperty("entrypoint");
expect(typeof config.manifest).toBe("object");
const manifest = config.manifest as Record<string, unknown>;
expect(manifest.type).toBe("complete");
expect(manifest.type).toBe("partial");
expect(typeof manifest.mainModule).toBe("string");
expect(typeof manifest.modules).toBe("object");
});
Expand All @@ -83,7 +89,7 @@ describe.runIf(isBuild)("Build Output Specification files", () => {
}
});

test("includes source maps in `manifest.modules` with type `sourcemap`", ({
test("only includes explicitly typed additional modules in the manifest", ({
expect,
}) => {
const configPath = path.join(getBuildOutputDir(), "config.json");
Expand All @@ -93,10 +99,19 @@ describe.runIf(isBuild)("Build Output Specification files", () => {
modules: Record<string, { type: string }>;
};
};
const additionalModule = Object.entries(config.manifest.modules).find(
([moduleName]) =>
moduleName.includes("additional-module-") && moduleName.endsWith(".txt")
);
expect(additionalModule).toBeDefined();
expect(additionalModule?.[1]).toEqual({ type: "text" });
expect(config.manifest.modules).not.toHaveProperty(
config.manifest.mainModule
);
expect(Object.keys(config.manifest.modules)).toHaveLength(1);

const sourceMapName = `${config.manifest.mainModule}.map`;
expect(config.manifest.modules[sourceMapName]).toEqual({
type: "sourcemap",
});
expect(config.manifest.modules).not.toHaveProperty(sourceMapName);
const sourceMapPath = path.join(
getBuildOutputDir(),
"bundle",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello from additional module
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { env } from "cloudflare:workers";
import additionalModule from "./additional-module.txt";

export default {
fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/text-binding") {
return new Response(env.MY_TEXT);
}
if (url.pathname === "/additional-module") {
return new Response(additionalModule);
}
return new Response("hello from worker");
},
} satisfies ExportedHandler;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module "*.txt" {
const text: string;
export default text;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export default defineConfig({
environments: {
ssr: {
build: {
rollupOptions: {
output: {
entryFileNames: "chunks/[name]-[hash].mjs",
},
},
sourcemap: true,
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { test } from "vitest";
import { getTextResponse, isBuild } from "../../__test-utils__";
import { getTextResponse } from "../../__test-utils__";

test.runIf(!isBuild)(
"can import module from child environment",
async ({ expect }) => {
const response = await getTextResponse();
expect(response).toBe("Hello from the child environment");
}
);
test("can import module from child environment", async ({ expect }) => {
const response = await getTextResponse();
expect(response).toBe("Hello from the child environment");
});

test("can import additional module from child environment", async ({
expect,
}) => {
const response = await getTextResponse("/additional-module");
expect(response).toBe("Hello from a child environment additional module\n");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from a child environment additional module
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// @ts-expect-error - no types
import { getEnvironmentName } from "virtual:environment-name";
import additionalModule from "./additional-module.txt";

export { additionalModule };

export function getMessage() {
return `Hello from the ${getEnvironmentName()} environment`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@ declare global {
}

export default {
async fetch() {
const childEnvironmentModule = (await __VITE_ENVIRONMENT_RUNNER_IMPORT__(
"child",
"./src/child-environment-module"
)) as { getMessage: () => string };
async fetch(request) {
const childEnvironmentEntry = "./child/child-environment-module.js";
const childEnvironmentModule =
(await (typeof __VITE_ENVIRONMENT_RUNNER_IMPORT__ === "function"
? __VITE_ENVIRONMENT_RUNNER_IMPORT__(
"child",
"./src/child-environment-module"
)
: import(/* @vite-ignore */ childEnvironmentEntry))) as {
additionalModule: string;
getMessage: () => string;
};

return new Response(childEnvironmentModule.getMessage());
return new Response(
new URL(request.url).pathname === "/additional-module"
? childEnvironmentModule.additionalModule
: childEnvironmentModule.getMessage()
);
},
} satisfies ExportedHandler;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module "*.txt" {
const text: string;
export default text;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
import assert from "node:assert";
import * as path from "node:path";
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
environments: {
child: {
build: {
rollupOptions: {
input: path.resolve(__dirname, "src/child-environment-module.ts"),
},
},
},
},
builder: {
async buildApp(builder) {
const parentEnvironment = builder.environments.parent;
const childEnvironment = builder.environments.child;

assert(parentEnvironment, `No "parent" environment`);
assert(childEnvironment, `No "child" environment`);

await builder.build(parentEnvironment);
await builder.build(childEnvironment);
},
},
plugins: [
cloudflare({
types: { includeRuntime: false },
Expand Down

This file was deleted.

3 changes: 2 additions & 1 deletion packages/vite-plugin-cloudflare/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ async function fallbackBuild(
fs.unlinkSync(fallbackEntryPath);
}

function loadViteManifest(directory: string) {
/** Read the Vite manifest emitted for a completed environment build. */
export function loadViteManifest(directory: string): vite.Manifest {
const contents = fs.readFileSync(
path.resolve(directory, ".vite", "manifest.json"),
"utf-8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,13 @@ export function createCloudflareEnvironmentOptions({
userConfig,
mode,
environmentName,
isEntryWorker,
isParentEnvironment,
hasNodeJsCompat,
}: {
workerConfig: ResolvedWorkerConfig;
userConfig: vite.UserConfig;
mode: vite.ConfigEnv["mode"];
environmentName: string;
isEntryWorker: boolean;
isParentEnvironment: boolean;
hasNodeJsCompat: boolean;
}): vite.EnvironmentOptions {
Expand Down Expand Up @@ -278,7 +276,7 @@ export function createCloudflareEnvironmentOptions({
},
target,
emitAssets: true,
manifest: isEntryWorker,
manifest: isParentEnvironment,
outDir: getOutputDirectory(userConfig, environmentName),
copyPublicDir: false,
ssr: true,
Expand Down
59 changes: 59 additions & 0 deletions packages/vite-plugin-cloudflare/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
WorkersResolvedConfig,
} from "./plugin-config";
import type {
ModuleType,
ParsedInputSettingsConfig,
ParsedInputWorkerConfig,
ParsedOutputWorkerConfig,
Expand All @@ -33,6 +34,13 @@ export interface SharedContext {
tunnelHostnames: Set<string>;
}

/** An explicitly typed additional module emitted by a Vite environment. */
export interface AdditionalModuleMetadata {
environmentName: string;
fileName: string;
type: ModuleType;
}

/**
* Used to provide context to internal plugins.
* It should be reinstantiated each time the main plugin is created.
Expand All @@ -41,6 +49,10 @@ export class PluginContext {
#sharedContext: SharedContext;
#resolvedPluginConfig?: ResolvedPluginConfig;
#resolvedViteConfig?: vite.ResolvedConfig;
#environmentNameToAdditionalModules = new Map<
string,
Map<string, ModuleType>
>();

constructor(sharedContext: SharedContext) {
this.#sharedContext = sharedContext;
Expand Down Expand Up @@ -206,6 +218,53 @@ export class PluginContext {
return this.#getWorker(environmentName)?.config;
}

/** Clear the additional modules collected for an environment before rebuilding it. */
clearAdditionalModules(environmentName: string): void {
this.#environmentNameToAdditionalModules.delete(environmentName);
}

/** Record an explicitly typed additional module emitted by an environment. */
addAdditionalModule(
environmentName: string,
fileName: string,
type: ModuleType
): void {
const additionalModules =
this.#environmentNameToAdditionalModules.get(environmentName) ??
new Map();
const existingType = additionalModules.get(fileName);
assert(
existingType === undefined || existingType === type,
`Additional module "${fileName}" was emitted with conflicting types "${existingType}" and "${type}".`
);
additionalModules.set(fileName, type);
this.#environmentNameToAdditionalModules.set(
environmentName,
additionalModules
);
}

/** Get the additional modules emitted by a Worker environment and its children. */
getAdditionalModules(
workerEnvironmentName: string
): AdditionalModuleMetadata[] {
const environmentNames = [
workerEnvironmentName,
...(this.resolvedPluginConfig.type === "preview"
? []
: (this.resolvedPluginConfig.environmentNameToChildEnvironmentNamesMap.get(
workerEnvironmentName
) ?? [])),
];

return environmentNames.flatMap((environmentName) =>
Array.from(
this.#environmentNameToAdditionalModules.get(environmentName) ?? [],
([fileName, type]) => ({ environmentName, fileName, type })
)
);
}

get allWorkerConfigs(): Array<
ParsedInputWorkerConfig | ParsedOutputWorkerConfig
> {
Expand Down
Loading
Loading