diff --git a/.changeset/tiny-crabs-join.md b/.changeset/tiny-crabs-join.md
new file mode 100644
index 00000000..082483d9
--- /dev/null
+++ b/.changeset/tiny-crabs-join.md
@@ -0,0 +1,7 @@
+---
+"@ducanh2912/next-pwa": patch
+---
+
+fix(mjs): fixed the ESM build crashing
+
+- This was due to us referencing `__dirname`, which was `undefined` in the ESM build...
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index bca6e18c..541f379d 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -25,20 +25,20 @@ jobs:
version: latest
- name: Run Biome
run: biome ci .
- # test-integration-dev:
- # name: ⚫️ Run Jest (development mode) integration tests
- # uses: ./.github/workflows/build-reusable.yml
- # with:
- # afterBuild: |
- # NEXT_TEST_MODE=dev pnpm test -- -- __tests__/integration --ci --runInBand
- # secrets: inherit
- # test-integration-prod:
- # name: ⚫️ Run Jest (production mode) integration tests
- # uses: ./.github/workflows/build-reusable.yml
- # with:
- # afterBuild: |
- # NEXT_TEST_MODE=start pnpm test -- -- __tests__/integration --ci --runInBand
- # secrets: inherit
+ test-integration-dev:
+ name: ⚫️ Run Jest (development mode) integration tests
+ uses: ./.github/workflows/build-reusable.yml
+ with:
+ afterBuild: |
+ NEXT_TEST_MODE=dev pnpm test -- -- __tests__/integration --ci --runInBand
+ secrets: inherit
+ test-integration-prod:
+ name: ⚫️ Run Jest (production mode) integration tests
+ uses: ./.github/workflows/build-reusable.yml
+ with:
+ afterBuild: |
+ NEXT_TEST_MODE=start pnpm test -- -- __tests__/integration --ci --runInBand
+ secrets: inherit
test-e2e-dev:
name: ⚫️ Run Jest (development mode) E2E tests
uses: ./.github/workflows/build-reusable.yml
diff --git a/packages/next-pwa/__tests__/e2e/app-dir/index.test.ts b/packages/next-pwa/__tests__/e2e/app-dir/index.test.ts
index 873735ba..729c9a10 100644
--- a/packages/next-pwa/__tests__/e2e/app-dir/index.test.ts
+++ b/packages/next-pwa/__tests__/e2e/app-dir/index.test.ts
@@ -1,6 +1,6 @@
import { createDescribe } from "../../test-utils/index.ts";
-createDescribe("e2e app-dir", { sourceDir: __dirname, skipInstall: false }, ({ next }) => {
+createDescribe("integration mjs", { sourceDir: __dirname, skipInstall: false }, ({ next }) => {
it("should render", async () => {
const $ = await next.render("/");
expect($("#welcome-text").text()).toBe("This is a Next.js PWA!");
diff --git a/packages/next-pwa/__tests__/integration/mjs/app/favicon.ico b/packages/next-pwa/__tests__/integration/mjs/app/favicon.ico
new file mode 100644
index 00000000..718d6fea
Binary files /dev/null and b/packages/next-pwa/__tests__/integration/mjs/app/favicon.ico differ
diff --git a/packages/next-pwa/__tests__/integration/mjs/app/layout.tsx b/packages/next-pwa/__tests__/integration/mjs/app/layout.tsx
new file mode 100644
index 00000000..7d0f37de
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/app/layout.tsx
@@ -0,0 +1,8 @@
+const RootLayout = ({ children }: { children: React.ReactNode }) => (
+
+
{children}
+
+);
+
+export default RootLayout;
diff --git a/packages/next-pwa/__tests__/integration/mjs/app/page.tsx b/packages/next-pwa/__tests__/integration/mjs/app/page.tsx
new file mode 100644
index 00000000..23bede38
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/app/page.tsx
@@ -0,0 +1,10 @@
+import Image from "next/image";
+
+const Page = () => (
+
+ This is a Next.js PWA!
+
+
+);
+
+export default Page;
diff --git a/packages/next-pwa/__tests__/integration/mjs/index.test.ts b/packages/next-pwa/__tests__/integration/mjs/index.test.ts
new file mode 100644
index 00000000..873735ba
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/index.test.ts
@@ -0,0 +1,21 @@
+import { createDescribe } from "../../test-utils/index.ts";
+
+createDescribe("e2e app-dir", { sourceDir: __dirname, skipInstall: false }, ({ next }) => {
+ it("should render", async () => {
+ const $ = await next.render("/");
+ expect($("#welcome-text").text()).toBe("This is a Next.js PWA!");
+ });
+
+ it("should fetch image", async () => {
+ const image = await next.fetch("/next.svg");
+ expect(image.status).toBe(200);
+ const favicon = await next.fetch("/favicon.ico");
+ expect(favicon.status).toBe(200);
+ });
+
+ it("should be able to fetch service worker", async () => {
+ const sw = await next.fetch("/sw.js");
+ expect(sw.status).toBe(200);
+ expect(sw.headers.get("Content-Type")?.includes("application/javascript")).toBe(true);
+ });
+});
diff --git a/packages/next-pwa/__tests__/integration/mjs/next.config.mjs b/packages/next-pwa/__tests__/integration/mjs/next.config.mjs
new file mode 100644
index 00000000..7d0c6c38
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/next.config.mjs
@@ -0,0 +1,11 @@
+// @ts-check
+import withPWAInit from "@ducanh2912/next-pwa";
+
+const withPWA = withPWAInit({
+ dest: "public",
+});
+
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+export default withPWA(nextConfig);
diff --git a/packages/next-pwa/__tests__/integration/mjs/public/next.svg b/packages/next-pwa/__tests__/integration/mjs/public/next.svg
new file mode 100644
index 00000000..5174b28c
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/next-pwa/__tests__/integration/mjs/tsconfig.json b/packages/next-pwa/__tests__/integration/mjs/tsconfig.json
new file mode 100644
index 00000000..a8922ff1
--- /dev/null
+++ b/packages/next-pwa/__tests__/integration/mjs/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "allowImportingTsExtensions": true,
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/next-pwa/src/context.ts b/packages/next-pwa/src/context.ts
index 411f2356..3badbc19 100644
--- a/packages/next-pwa/src/context.ts
+++ b/packages/next-pwa/src/context.ts
@@ -1,4 +1,5 @@
import path from "node:path";
+import { fileURLToPath } from "node:url";
import fg from "fast-glob";
import type { NextConfig, NextConfigComplete, WebpackConfigContext } from "next/dist/server/config-shared.js";
@@ -11,6 +12,8 @@ import type { PluginOptions } from "./types.js";
import { getFileHash } from "./utils.js";
import { getDefaultDocumentPage } from "./webpack/builders/get-default-document-page.js";
+const __dirname = fileURLToPath(new URL(".", import.meta.url));
+
export type PluginOptionsComplete = Required;
type PublicPath = NonNullable["publicPath"]>;
@@ -59,6 +62,7 @@ export const parseOptions = (
}: PluginOptions,
): PluginOptionsComplete => {
if (!additionalManifestEntries) {
+ const publicDir = path.resolve(webpackContext.dir, "public");
additionalManifestEntries = fg
.sync(
[
@@ -74,12 +78,12 @@ export const parseOptions = (
...publicExcludes,
],
{
- cwd: path.resolve(webpackContext.dir, "public"),
+ cwd: publicDir,
},
)
.map((f) => ({
url: path.posix.join(nextConfig.basePath, f),
- revision: getFileHash(`public/${f}`),
+ revision: getFileHash(path.resolve(publicDir, f)),
}));
}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 0089cc30..f2fab808 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -2,5 +2,5 @@ packages:
- "docs"
- "packages/**"
- "examples/**"
- - "!examples/**/.next"
+ - "!**/.next"
- "next-pwa-e2e-test"