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
3 changes: 2 additions & 1 deletion packages/plugin-vite/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"rollup-plugin-visualizer": "npm:rollup-plugin-visualizer@^6.0.3",
"stripe": "npm:stripe@^19.1.0",
"vite": "npm:vite@^7.1.4",
"vite-plugin-inspect": "npm:vite-plugin-inspect@^11.3.2"
"vite-plugin-inspect": "npm:vite-plugin-inspect@^11.3.2",
"vite-plugin-pwa": "npm:vite-plugin-pwa@^1.0.3"
}
}
41 changes: 41 additions & 0 deletions packages/plugin-vite/src/plugins/server_snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,47 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {
}
}
}

// Scan client output directory for any additional files generated
// by Vite plugins (e.g., vite-plugin-pwa generates sw.js,
// manifest.webmanifest) that aren't in the Vite manifest or
// public directory.
if (await fsAdapter.isDirectory(clientOutDir)) {
// Normalize registered pathnames (strip leading /) for comparison
const registeredPaths = new Set(
staticFiles.map((f) =>
f.pathname.startsWith("/") ? f.pathname.slice(1) : f.pathname
),
);

const clientFiles = await fsAdapter.walk(
clientOutDir,
{
followSymlinks: false,
includeDirs: false,
includeFiles: true,
},
);

for await (const entry of clientFiles) {
const relative = path.relative(clientOutDir, entry.path);

// Skip .vite directory and already-registered files
if (
relative.startsWith(".vite/") ||
relative === ".vite" ||
registeredPaths.has(relative)
) {
continue;
}

staticFiles.push({
filePath: entry.path,
hash: null,
pathname: relative,
});
}
}
}

const code = await generateSnapshotServer({
Expand Down
62 changes: 62 additions & 0 deletions packages/plugin-vite/tests/build_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,3 +664,65 @@ Deno.test({
sanitizeOps: false,
sanitizeResources: false,
});

Deno.test({
name: "vite build - vite-plugin-pwa generates service worker",
fn: async () => {
const fixture = path.join(FIXTURE_DIR, "vite_plugin_pwa");
await using res = await buildVite(fixture);

// Verify that vite-plugin-pwa generated the expected files in _fresh/client
const swPath = path.join(res.tmp, "_fresh", "client", "sw.js");
const manifestPath = path.join(
res.tmp,
"_fresh",
"client",
"manifest.webmanifest",
);

// Check that files were generated
const swStat = await Deno.stat(swPath);
expect(swStat.isFile).toEqual(true);

const manifestStat = await Deno.stat(manifestPath);
expect(manifestStat.isFile).toEqual(true);
},
sanitizeOps: false,
sanitizeResources: false,
});

Deno.test({
name: "vite build - vite-plugin-pwa files are accessible via HTTP",
fn: async () => {
const fixture = path.join(FIXTURE_DIR, "vite_plugin_pwa");
await using res = await buildVite(fixture);

await launchProd(
{ cwd: res.tmp },
async (address) => {
// Test that service worker is accessible
const swRes = await fetch(`${address}/sw.js`);
expect(swRes.status).toEqual(200);
expect(swRes.headers.get("content-type")).toMatch(/javascript/);

const swContent = await swRes.text();
expect(swContent.length).toBeGreaterThan(0);

// Test that manifest is accessible
const manifestRes = await fetch(`${address}/manifest.webmanifest`);
expect(manifestRes.status).toEqual(200);
expect(manifestRes.headers.get("content-type")).toMatch(/json/);

const manifestContent = await manifestRes.json();
expect(manifestContent.name).toEqual("Fresh PWA Test");

// Test that registerSW.js is accessible
const registerRes = await fetch(`${address}/registerSW.js`);
expect(registerRes.status).toEqual(200);
expect(registerRes.headers.get("content-type")).toMatch(/javascript/);
},
);
},
sanitizeOps: false,
sanitizeResources: false,
});
5 changes: 5 additions & 0 deletions packages/plugin-vite/tests/fixtures/vite_plugin_pwa/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { App, staticFiles } from "@fresh/core";

export const app = new App()
.use(staticFiles())
.fsRoutes();
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function Home() {
return (
<div>
<h1>PWA Test</h1>
<p>Testing vite-plugin-pwa integration</p>
</div>
);
}
27 changes: 27 additions & 0 deletions packages/plugin-vite/tests/fixtures/vite_plugin_pwa/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineConfig } from "vite";
import { fresh } from "@fresh/plugin-vite";
import { VitePWA } from "vite-plugin-pwa";

export default defineConfig({
plugins: [
fresh(),
VitePWA({
// Minimal configuration to generate PWA files
registerType: "autoUpdate",
includeAssets: [],
manifest: {
name: "Fresh PWA Test",
short_name: "PWA Test",
description: "Testing vite-plugin-pwa with Fresh",
theme_color: "#ffffff",
},
workbox: {
globPatterns: ["**/*.{js,css,html}"],
},
devOptions: {
enabled: true,
type: "module",
},
}),
],
});
Loading