diff --git a/packages/fresh/src/commands.ts b/packages/fresh/src/commands.ts index f207d5bc8e5..c841c9bc402 100644 --- a/packages/fresh/src/commands.ts +++ b/packages/fresh/src/commands.ts @@ -1,4 +1,3 @@ -import { setAdditionalStyles } from "./context.ts"; import { HttpError } from "./error.ts"; import { isHandlerByMethod, type PageResponse } from "./handlers.ts"; import { @@ -74,11 +73,13 @@ export function newErrorCmd( export interface AppCommand { type: CommandType.App; component: RouteComponent; + css?: string[]; } export function newAppCmd( component: RouteComponent, + css?: string[], ): AppCommand { - return { type: CommandType.App, component }; + return { type: CommandType.App, component, css }; } export interface LayoutCommand { @@ -86,6 +87,7 @@ export interface LayoutCommand { pattern: string; component: RouteComponent; config?: LayoutConfig; + css?: string[]; includeLastSegment: boolean; } export function newLayoutCmd( @@ -93,12 +95,14 @@ export function newLayoutCmd( component: RouteComponent, config: LayoutConfig | undefined, includeLastSegment: boolean, + css?: string[], ): LayoutCommand { return { type: CommandType.Layout, pattern, component, config, + css, includeLastSegment, }; } @@ -253,7 +257,7 @@ function applyCommandsInner( break; } case CommandType.App: { - root.app = cmd.component; + root.app = { component: cmd.component, css: cmd.css ?? null }; break; } case CommandType.Layout: { @@ -265,6 +269,7 @@ function applyCommandsInner( segment.layout = { component: cmd.component, config: cmd.config ?? null, + css: cmd.css ?? null, }; break; } @@ -290,10 +295,6 @@ function applyCommandsInner( def = await route(); } - if (def.css !== undefined) { - setAdditionalStyles(ctx, def.css); - } - return renderRoute(ctx, def); }); diff --git a/packages/fresh/src/context.ts b/packages/fresh/src/context.ts index 3c2d9c07815..f487627d2d7 100644 --- a/packages/fresh/src/context.ts +++ b/packages/fresh/src/context.ts @@ -98,8 +98,11 @@ export type ServerIslandRegistry = Map; export const internals: unique symbol = Symbol("fresh_internal"); export interface UiTree { - app: AnyComponent> | null; - layouts: ComponentDef[]; + app: { + component: AnyComponent>; + css: string[] | null; + } | null; + layouts: (ComponentDef & { css: string[] | null })[]; } /** @@ -109,7 +112,10 @@ export type FreshContext = Context; export let getBuildCache: (ctx: Context) => BuildCache; export let getInternals: (ctx: Context) => UiTree; -export let setAdditionalStyles: (ctx: Context, css: string[]) => void; +export let setAdditionalStyles: ( + ctx: Context, + css: string[] | null | undefined, +) => void; /** * The context passed to every middleware. It is unique for every request. @@ -182,8 +188,23 @@ export class Context { // deno-lint-ignore no-explicit-any getInternals = (ctx: Context) => ctx.#internal as any; getBuildCache = (ctx: Context) => ctx.#buildCache; - setAdditionalStyles = (ctx: Context, css: string[]) => - ctx.#additionalStyles = css; + setAdditionalStyles = ( + ctx: Context, + css: string[] | null | undefined, + ) => { + if (css == null) return; + + if (ctx.#additionalStyles === null) { + ctx.#additionalStyles = css.slice(); + return; + } + + for (const href of css) { + if (!ctx.#additionalStyles.includes(href)) { + ctx.#additionalStyles.push(href); + } + } + }; } constructor( @@ -283,6 +304,7 @@ export class Context { props.Component = () => child; const def = defs[i]; + setAdditionalStyles(this, def.css); const result = await renderRouteComponent(this, def, () => child); if (result instanceof Response) { @@ -298,16 +320,20 @@ export class Context { let hasApp = true; - if (isAsyncAnyComponent(appDef)) { + if (appDef !== null) { + setAdditionalStyles(this, appDef.css); + } + + if (appDef !== null && isAsyncAnyComponent(appDef.component)) { props.Component = () => appChild; - const result = await renderAsyncAnyComponent(appDef, props); + const result = await renderAsyncAnyComponent(appDef.component, props); if (result instanceof Response) { return result; } appVNode = result; } else if (appDef !== null) { - appVNode = h(appDef, { + appVNode = h(appDef.component, { Component: () => appChild, config: this.config, data: null, diff --git a/packages/fresh/src/dev/dev_build_cache.ts b/packages/fresh/src/dev/dev_build_cache.ts index dd985551113..90a01462744 100644 --- a/packages/fresh/src/dev/dev_build_cache.ts +++ b/packages/fresh/src/dev/dev_build_cache.ts @@ -20,7 +20,7 @@ const WINDOWS_SEPARATOR = pathWin32.SEPARATOR; /** Normalize a path to use forward slashes so that generated files * are portable across operating systems (e.g. build on Windows, * deploy on Linux). */ -function toPosix(p: string): string { +export function toPosix(p: string): string { return p.replaceAll(WINDOWS_SEPARATOR, "/"); } diff --git a/packages/fresh/src/dev/fs_crawl.ts b/packages/fresh/src/dev/fs_crawl.ts index cc61f6669f2..5612a84ad2b 100644 --- a/packages/fresh/src/dev/fs_crawl.ts +++ b/packages/fresh/src/dev/fs_crawl.ts @@ -1,6 +1,6 @@ import { type FsAdapter, fsAdapter } from "../fs.ts"; import type { WalkEntry } from "@std/fs/walk"; -import type { FsRouteFileNoMod } from "./dev_build_cache.ts"; +import { type FsRouteFileNoMod, toPosix } from "./dev_build_cache.ts"; import * as path from "@std/path"; import { pathToPattern } from "../router.ts"; import { CommandType } from "../commands.ts"; @@ -86,7 +86,7 @@ export async function crawlRouteDir( files.push({ id, - filePath: entry.path, + filePath: toPosix(entry.path), type, pattern, routePattern, diff --git a/packages/fresh/src/dev/fs_crawl_test.ts b/packages/fresh/src/dev/fs_crawl_test.ts index fe55f04981d..af75f8a2348 100644 --- a/packages/fresh/src/dev/fs_crawl_test.ts +++ b/packages/fresh/src/dev/fs_crawl_test.ts @@ -1,6 +1,6 @@ import { expect } from "@std/expect/expect"; import { createFakeFs } from "../test_utils.ts"; -import { walkDir } from "./fs_crawl.ts"; +import { crawlRouteDir, walkDir } from "./fs_crawl.ts"; Deno.test("walkDir - ", async () => { const fs = createFakeFs({ @@ -43,3 +43,25 @@ Deno.test("walkDir - respects skip patterns", async () => { "routes/api/users.ts", ]); }); + +Deno.test({ + name: "crawlRouteDir.filePath - normalized Windows paths", + ignore: Deno.build.os !== "windows", + fn: async () => { + const fs = createFakeFs({ + "foo\\bar\\baz.txt": "foo", + "D:\\foo\\bar.tsx": "foo", + }); + + const rawFiles = await crawlRouteDir(fs, "foo", [], () => {}); + + expect(rawFiles).toEqual(expect.arrayContaining([ + expect.objectContaining({ + filePath: "foo/bar/baz.txt", + }), + expect.objectContaining({ + filePath: "D:/foo/bar.tsx", + }), + ])); + }, +}); diff --git a/packages/fresh/src/fs_routes.ts b/packages/fresh/src/fs_routes.ts index a50826d99e6..52afe50e58f 100644 --- a/packages/fresh/src/fs_routes.ts +++ b/packages/fresh/src/fs_routes.ts @@ -106,7 +106,9 @@ export function fsItemsToCommands( } if (!mod.default) continue; - commands.push(newLayoutCmd(pattern, mod.default, mod.config, true)); + commands.push( + newLayoutCmd(pattern, mod.default, mod.config, true, mod.css), + ); continue; } case CommandType.Error: { @@ -116,6 +118,7 @@ export function fsItemsToCommands( { component: mod.default ?? undefined, config: mod.config ?? undefined, + css: mod.css, // deno-lint-ignore no-explicit-any handler: (handlers as any) ?? undefined, }, @@ -128,6 +131,7 @@ export function fsItemsToCommands( commands.push(newNotFoundCmd({ config: mod.config, component: mod.default, + css: mod.css, // deno-lint-ignore no-explicit-any handler: handlers as any ?? undefined, })); @@ -137,7 +141,7 @@ export function fsItemsToCommands( const { mod } = validateFsMod(filePath, rawMod, type); if (mod.default === undefined) continue; - commands.push(newAppCmd(mod.default)); + commands.push(newAppCmd(mod.default, mod.css)); continue; } case CommandType.Route: { diff --git a/packages/fresh/src/internals_dev.ts b/packages/fresh/src/internals_dev.ts index e2d78b3d68b..bda505f237c 100644 --- a/packages/fresh/src/internals_dev.ts +++ b/packages/fresh/src/internals_dev.ts @@ -7,6 +7,7 @@ export { type IslandModChunk, type PendingStaticFile, prepareStaticFile, + toPosix, writeCompiledEntry, } from "./dev/dev_build_cache.ts"; export { specToName } from "./dev/builder.ts"; diff --git a/packages/fresh/src/segments.ts b/packages/fresh/src/segments.ts index 07ae745aa15..b2f65d7da66 100644 --- a/packages/fresh/src/segments.ts +++ b/packages/fresh/src/segments.ts @@ -2,7 +2,7 @@ import type { AnyComponent } from "preact"; import type { MaybeLazyMiddleware, Middleware } from "./middlewares/mod.ts"; import { type Method, patternToSegments } from "./router.ts"; import type { LayoutConfig, Route } from "./types.ts"; -import { type Context, getInternals } from "./context.ts"; +import { type Context, getInternals, setAdditionalStyles } from "./context.ts"; import { recordSpanError, tracer } from "./otel.ts"; import { type HandlerFn, isHandlerByMethod } from "./handlers.ts"; import { @@ -22,10 +22,14 @@ export interface Segment { layout: { component: RouteComponent; config: LayoutConfig | null; + css: string[] | null; } | null; errorRoute: Route | null; notFound: Middleware | null; - app: RouteComponent | null; + app: { + component: RouteComponent; + css: string[] | null; + } | null; children: Map>; parent: Segment | null; } @@ -105,7 +109,11 @@ export function segmentToMiddlewares( internals.app = null; } - const def = { props: null, component: layout.component }; + const def = { + props: null, + component: layout.component, + css: layout.css, + }; if (layout.config?.skipInheritedLayouts) { internals.layouts = [def]; } else { @@ -145,6 +153,8 @@ export async function renderRoute( route: Route, status = 200, ): Promise { + setAdditionalStyles(ctx, route.css); + const internals = getInternals(ctx); if (route.config?.skipAppWrapper) { internals.app = null; diff --git a/packages/fresh/src/test_utils.ts b/packages/fresh/src/test_utils.ts index ed6b6776238..f76dff31015 100644 --- a/packages/fresh/src/test_utils.ts +++ b/packages/fresh/src/test_utils.ts @@ -7,6 +7,7 @@ import { DEFAULT_CONN_INFO } from "./app.ts"; import type { Command } from "./commands.ts"; import { fsItemsToCommands, type FsRouteFile } from "./fs_routes.ts"; import * as path from "@std/path"; +import { toPosix } from "./dev/dev_build_cache.ts"; const STUB = {} as unknown as Deno.ServeHandlerInfo; @@ -123,7 +124,10 @@ export function createFakeFs(files: Record): FsAdapter { }, // deno-lint-ignore require-await async isDirectory(dir) { - return Object.keys(files).some((file) => file.startsWith(dir + "/")); + return Object.keys(files).some((file) => + // normalize path to posix before comparing + toPosix(file).startsWith(dir + "/") + ); }, async mkdirp(_dir: string) { }, diff --git a/packages/plugin-vite/src/plugins/server_snapshot.ts b/packages/plugin-vite/src/plugins/server_snapshot.ts index cf37c0452dd..192d2936c68 100644 --- a/packages/plugin-vite/src/plugins/server_snapshot.ts +++ b/packages/plugin-vite/src/plugins/server_snapshot.ts @@ -13,6 +13,7 @@ import { pathToSpec, type PendingStaticFile, specToName, + toPosix, UniqueNamer, } from "fresh/internal-dev"; import { @@ -23,6 +24,19 @@ import { import * as path from "@std/path"; import { getBuildId } from "./build_id.ts"; +const CSS_LANG_REG = /\.(css|less|sass|scss)(\?.*)?$/; +export const FRESH_CSS_PLACEHOLDER = `["__FRESH_CSS_PLACEHOLDER__"]`; + +export function replaceFreshCssPlaceholders( + content: string, + css: string[] | undefined, +): string { + return content.replaceAll( + FRESH_CSS_PLACEHOLDER, + css ? JSON.stringify(css.map((href) => `/${href}`)) : "null", + ); +} + export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] { const modName = "fresh:server-snapshot"; @@ -158,7 +172,7 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] { const route = result.routes[i]; const name = routeNamer.getUniqueName(route.id); - routeFileToName.set(route.filePath, name); + routeFileToName.set(toPosix(route.filePath), name); routes.set(name, route); } @@ -401,7 +415,7 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] { }, transform: { filter: { - id: /\.(css|less|sass|scss)(\?.*)?$/, + id: CSS_LANG_REG, }, handler(_code, id) { if (server) { @@ -416,7 +430,7 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] { let item: EnvironmentModuleNode | undefined; while ((item = queue.pop()) !== undefined) { if (item.file !== null) { - const normalized = path.normalize(item.file); + const normalized = toPosix(path.normalize(item.file)); const name = routeFileToName.get(normalized); if (name !== undefined) { @@ -520,19 +534,19 @@ export default ${JSON.stringify(route.css)} const manifest = JSON.parse(asset.source as string) as Manifest; for (const info of Object.values(manifest)) { - if (info.name?.startsWith("_fresh-route___")) { - const filePath = path.join(serverOutDir, info.file); - const content = await Deno.readTextFile(filePath); - - const replaced = content.replace( - `["__FRESH_CSS_PLACEHOLDER__"]`, - info.css - ? JSON.stringify(info.css.map((css) => `/${css}`)) - : "null", - ); + // Utility-file(_app/_layout/_error)'s CSS can be hoisted into + // shared chunks like "server-entry", not just route chunks. + // Replace placeholders in any emitted JS chunk that contains one. + if (!/\.(?:c|m)?js$/.test(info.file)) continue; - await Deno.writeTextFile(filePath, replaced); - } + const filePath = path.join(serverOutDir, info.file); + const content = await Deno.readTextFile(filePath); + if (!content.includes(FRESH_CSS_PLACEHOLDER)) continue; + + // Replace all placeholders in the file with the CSS + const replaced = replaceFreshCssPlaceholders(content, info.css); + + await Deno.writeTextFile(filePath, replaced); } } }, diff --git a/packages/plugin-vite/src/plugins/server_snapshot_test.ts b/packages/plugin-vite/src/plugins/server_snapshot_test.ts new file mode 100644 index 00000000000..9202cf8fc3e --- /dev/null +++ b/packages/plugin-vite/src/plugins/server_snapshot_test.ts @@ -0,0 +1,40 @@ +import { expect } from "@std/expect/expect"; +import { + FRESH_CSS_PLACEHOLDER, + replaceFreshCssPlaceholders, +} from "./server_snapshot.ts"; + +Deno.test("server snapshot - replaceFreshCssPlaceholders with no css", () => { + const output = replaceFreshCssPlaceholders( + `export default ${FRESH_CSS_PLACEHOLDER};`, + undefined, + ); + + expect(output).toEqual("export default null;"); +}); + +Deno.test("server snapshot - replaceFreshCssPlaceholders once", () => { + const output = replaceFreshCssPlaceholders( + `const css = ${FRESH_CSS_PLACEHOLDER};`, + ["assets/server-entry.css"], + ); + + expect(output).toEqual(`const css = ["/assets/server-entry.css"];`); +}); + +Deno.test("server snapshot - replaceFreshCssPlaceholders multiple times", () => { + const output = replaceFreshCssPlaceholders( + [ + `const appCss = ${FRESH_CSS_PLACEHOLDER};`, + `const layoutCss = ${FRESH_CSS_PLACEHOLDER};`, + `const errorCss = ${FRESH_CSS_PLACEHOLDER};`, + ].join("\n"), + ["assets/server-entry.css"], + ); + + expect(output).toEqual([ + `const appCss = ["/assets/server-entry.css"];`, + `const layoutCss = ["/assets/server-entry.css"];`, + `const errorCss = ["/assets/server-entry.css"];`, + ].join("\n")); +}); diff --git a/packages/plugin-vite/tests/build_test.ts b/packages/plugin-vite/tests/build_test.ts index 8edafe63d2f..e4d8354e059 100644 --- a/packages/plugin-vite/tests/build_test.ts +++ b/packages/plugin-vite/tests/build_test.ts @@ -1,4 +1,5 @@ import { expect } from "@std/expect"; +import { walk } from "@std/fs/walk"; import { waitFor, waitForText, @@ -13,8 +14,13 @@ import { usingEnv, } from "./test_utils.ts"; import * as path from "@std/path"; +import { FRESH_CSS_PLACEHOLDER } from "../src/plugins/server_snapshot.ts"; const viteResult = await buildVite(DEMO_DIR); +const NON_ISLAND_CSS_MODULES_FIXTURE = path.join( + FIXTURE_DIR, + "non_island_css_modules", +); integrationTest("vite build - launches", async () => { await launchProd( @@ -388,6 +394,81 @@ integrationTest( }, ); +integrationTest( + "vite build - css modules in _app/_layout/_error non-island component are injected", + async () => { + await using res = await buildVite(NON_ISLAND_CSS_MODULES_FIXTURE); + + await launchProd( + { cwd: res.tmp }, + async (address) => { + await withBrowser(async (page) => { + { + // check _app/_layout + await page.goto(`${address}`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + } + + { + // check _app/_layout/_error + await page.goto(`${address}/boom`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + + const _error = await page + .locator(".blue > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_error).toEqual("rgb(0, 0, 255)"); + } + + { + // check _app/_layout/_404 + await page.goto(`${address}/non_existent`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + + const _404 = await page + .locator(".orange > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_404).toEqual("rgb(255, 165, 0)"); + } + }); + }, + ); + }, +); + integrationTest("vite build - route css import", async () => { await launchProd( { cwd: viteResult.tmp }, @@ -410,6 +491,45 @@ integrationTest("vite build - route css import", async () => { ); }); +integrationTest( + "vite build - __FRESH_CSS_PLACEHOLDER__ has been replaced in all server chunks", + async () => { + await using res = await buildVite(NON_ISLAND_CSS_MODULES_FIXTURE, { + base: "/my-app/", + }); + + const serverDir = path.join(res.tmp, "_fresh", "server"); + const inspected: string[] = []; + for await ( + const entry of walk(serverDir, { + exts: [".mjs"], + includeDirs: false, + }) + ) { + const content = await Deno.readTextFile(entry.path); + inspected.push(path.relative(serverDir, entry.path)); + expect(content).not.toContain(FRESH_CSS_PLACEHOLDER); + } + + expect(inspected.length).toBeGreaterThan(0); + }, +); + +integrationTest( + "vite build - shared server chunk keeps utility css references after replacement", + async () => { + await using res = await buildVite(NON_ISLAND_CSS_MODULES_FIXTURE); + + const serverEntryJs = await Deno.readTextFile( + path.join(res.tmp, "_fresh", "server", "server-entry.mjs"), + ); + + const cssRefs = serverEntryJs.match(/"\/assets\/server-entry-.*?\.css"/g) ?? + []; + expect(cssRefs.length).toBeGreaterThanOrEqual(4); + }, +); + integrationTest("vite build - remote island", async () => { const fixture = path.join(FIXTURE_DIR, "remote_island"); await using res = await buildVite(fixture); diff --git a/packages/plugin-vite/tests/dev_server_test.ts b/packages/plugin-vite/tests/dev_server_test.ts index ffc30f92ee2..2349f22dd50 100644 --- a/packages/plugin-vite/tests/dev_server_test.ts +++ b/packages/plugin-vite/tests/dev_server_test.ts @@ -282,6 +282,77 @@ integrationTest( }, ); +integrationTest( + "vite dev - css modules in _app/_layout/_error non-island component are injected", + async () => { + const fixture = path.join(FIXTURE_DIR, "non_island_css_modules"); + await launchDevServer(fixture, async (address) => { + await withBrowser(async (page) => { + { + // check _app/_layout + await page.goto(`${address}`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + } + + { + // check _app/_layout/_error + await page.goto(`${address}/boom`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + + const _error = await page + .locator(".blue > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_error).toEqual("rgb(0, 0, 255)"); + } + + { + // check _app/_layout/_404 + await page.goto(`${address}/non_existent`, { + waitUntil: "networkidle2", + }); + + const _app = await page + .locator(".green > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_app).toEqual("rgb(0, 128, 0)"); + + const _layout = await page + .locator(".red > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_layout).toEqual("rgb(255, 0, 0)"); + + const _404 = await page + .locator(".orange > h1") + .evaluate((el) => window.getComputedStyle(el).color); + expect(_404).toEqual("rgb(255, 165, 0)"); + } + }); + }); + }, +); + integrationTest("vite dev - route css import", async () => { await withBrowser(async (page) => { await page.goto(`${demoServer.address()}/tests/css`, { diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland.tsx new file mode 100644 index 00000000000..c36712eb206 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland.tsx @@ -0,0 +1,10 @@ +// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560 +import styles from "./CssModulesNonIsland.module.css"; + +export function CssModulesNonIsland() { + return ( +
+

green text

+
+ ); +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland2.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland2.tsx new file mode 100644 index 00000000000..ff43d15c98c --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland2.tsx @@ -0,0 +1,9 @@ +import styles from "./CssModulesNonIsland2.module.css"; + +export function CssModulesNonIsland2() { + return ( +
+

blue text

+
+ ); +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland3.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland3.tsx new file mode 100644 index 00000000000..1d4bc9af0e9 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland3.tsx @@ -0,0 +1,9 @@ +import styles from "./CssModulesNonIsland3.module.css"; + +export function CssModulesNonIsland3() { + return ( +
+

red text

+
+ ); +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland4.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland4.tsx new file mode 100644 index 00000000000..7ae45e828f0 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModuleNonIsland4.tsx @@ -0,0 +1,9 @@ +import styles from "./CssModulesNonIsland4.module.css"; + +export function CssModulesNonIsland4() { + return ( +
+

orange text

+
+ ); +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland.module.css b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland.module.css new file mode 100644 index 00000000000..211cf429c98 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland.module.css @@ -0,0 +1,3 @@ +.root { + color: green; +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland2.module.css b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland2.module.css new file mode 100644 index 00000000000..98cbbee4c6d --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland2.module.css @@ -0,0 +1,3 @@ +.root { + color: blue; +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland3.module.css b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland3.module.css new file mode 100644 index 00000000000..d810e45683b --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland3.module.css @@ -0,0 +1,3 @@ +.root { + color: red; +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland4.module.css b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland4.module.css new file mode 100644 index 00000000000..d70e5bc2823 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/components/CssModulesNonIsland4.module.css @@ -0,0 +1,3 @@ +.root { + color: orange; +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/main.ts b/packages/plugin-vite/tests/fixtures/non_island_css_modules/main.ts new file mode 100644 index 00000000000..e5cf428a2cd --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/main.ts @@ -0,0 +1,5 @@ +import { App, staticFiles } from "@fresh/core"; + +export const app = new App() + .use(staticFiles()) + .fsRoutes(); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_404.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_404.tsx new file mode 100644 index 00000000000..2e8e201adc2 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_404.tsx @@ -0,0 +1,6 @@ +import { CssModulesNonIsland4 } from "../components/CssModuleNonIsland4.tsx"; +import { define } from "../utils.ts"; + +export default define.page(() => { + return ; +}); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_app.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_app.tsx new file mode 100644 index 00000000000..332ae5830b7 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_app.tsx @@ -0,0 +1,17 @@ +import type { PageProps } from "fresh"; +import { CssModulesNonIsland } from "../components/CssModuleNonIsland.tsx"; + +export default function App({ Component }: PageProps) { + return ( + + + + + + + + + + + ); +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_error.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_error.tsx new file mode 100644 index 00000000000..9a263934760 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_error.tsx @@ -0,0 +1,6 @@ +import { define } from "../utils.ts"; +import { CssModulesNonIsland2 } from "../components/CssModuleNonIsland2.tsx"; + +export default define.page(() => { + return ; +}); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_layout.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_layout.tsx new file mode 100644 index 00000000000..b94e8413fb9 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/_layout.tsx @@ -0,0 +1,11 @@ +import { CssModulesNonIsland3 } from "../components/CssModuleNonIsland3.tsx"; +import { define } from "../utils.ts"; + +export default define.layout(({ Component }) => { + return ( + <> + + + + ); +}); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/boom.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/boom.tsx new file mode 100644 index 00000000000..cd79ff05703 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/boom.tsx @@ -0,0 +1,5 @@ +import { define } from "../utils.ts"; + +export default define.page(() => { + throw new Error("boom"); +}); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/index.tsx b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/index.tsx new file mode 100644 index 00000000000..00fdc5813f9 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/routes/index.tsx @@ -0,0 +1,3 @@ +export default function Hello() { + return

ok

; +} diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/utils.ts b/packages/plugin-vite/tests/fixtures/non_island_css_modules/utils.ts new file mode 100644 index 00000000000..8c8da636c20 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/utils.ts @@ -0,0 +1,4 @@ +import { createDefine } from "@fresh/core"; + +// deno-lint-ignore no-explicit-any +export const define = createDefine(); diff --git a/packages/plugin-vite/tests/fixtures/non_island_css_modules/vite.config.ts b/packages/plugin-vite/tests/fixtures/non_island_css_modules/vite.config.ts new file mode 100644 index 00000000000..727d49a2772 --- /dev/null +++ b/packages/plugin-vite/tests/fixtures/non_island_css_modules/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import { fresh } from "@fresh/plugin-vite"; + +export default defineConfig({ + plugins: [fresh()], +});