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!

+ Next.js Logo +
+); + +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"