diff --git a/.changeset/pretty-boats-refuse.md b/.changeset/pretty-boats-refuse.md new file mode 100644 index 000000000..d329ac7aa --- /dev/null +++ b/.changeset/pretty-boats-refuse.md @@ -0,0 +1,5 @@ +--- +"nextjs-darkmode": patch +--- + +Optimize diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ebb988d3b..8c05f89c0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,13 +29,11 @@ jobs: git pull - run: npm i -g pnpm && pnpm i name: Install dependencies - - name: Test - run: npm test - run: git status && git clean -f -d && git status name: clean up working directory - run: npx @turbo/codemod update . && pnpm update --latest -r name: Update dependencies - - run: pnpm build + - run: pnpm build --filter @example/nextjs name: Build all apps to make sure it is not broken due to dependency upgrades - name: Run unit tests run: pnpm test diff --git a/examples/nextjs-pages/.eslintrc.js b/examples/nextjs-pages/.eslintrc.js deleted file mode 100644 index 6582db49b..000000000 --- a/examples/nextjs-pages/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: ["@repo/eslint-config/next.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/examples/nextjs-pages/eslint.config.mjs b/examples/nextjs-pages/eslint.config.mjs new file mode 100644 index 000000000..70f2dfa49 --- /dev/null +++ b/examples/nextjs-pages/eslint.config.mjs @@ -0,0 +1,4 @@ +import { nextJsConfig } from "@repo/eslint-config/next.js"; + +/** @type {import("eslint").Linter.Config} */ +export default nextJsConfig; diff --git a/examples/nextjs/.eslintrc.js b/examples/nextjs/.eslintrc.js deleted file mode 100644 index 6582db49b..000000000 --- a/examples/nextjs/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: ["@repo/eslint-config/next.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/examples/nextjs/eslint.config.mjs b/examples/nextjs/eslint.config.mjs new file mode 100644 index 000000000..70f2dfa49 --- /dev/null +++ b/examples/nextjs/eslint.config.mjs @@ -0,0 +1,4 @@ +import { nextJsConfig } from "@repo/eslint-config/next.js"; + +/** @type {import("eslint").Linter.Config} */ +export default nextJsConfig; diff --git a/examples/nextjs/next-env.d.ts b/examples/nextjs/next-env.d.ts index 1b3be0840..3cd7048ed 100644 --- a/examples/nextjs/next-env.d.ts +++ b/examples/nextjs/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/tailwind/.eslintrc.js b/examples/tailwind/.eslintrc.js deleted file mode 100644 index 6582db49b..000000000 --- a/examples/tailwind/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: ["@repo/eslint-config/next.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/examples/tailwind/eslint.config.mjs b/examples/tailwind/eslint.config.mjs new file mode 100644 index 000000000..70f2dfa49 --- /dev/null +++ b/examples/tailwind/eslint.config.mjs @@ -0,0 +1,4 @@ +import { nextJsConfig } from "@repo/eslint-config/next.js"; + +/** @type {import("eslint").Linter.Config} */ +export default nextJsConfig; diff --git a/examples/vite/.eslintrc.js b/examples/vite/.eslintrc.js deleted file mode 100644 index d83112c07..000000000 --- a/examples/vite/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: ["@repo/eslint-config/react.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/examples/vite/eslint.config.mjs b/examples/vite/eslint.config.mjs new file mode 100644 index 000000000..00bf0d907 --- /dev/null +++ b/examples/vite/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@repo/eslint-config/react.js"; + +/** @type {import("eslint").Linter.Config} */ +export default config; diff --git a/lib/.eslintrc.js b/lib/.eslintrc.js deleted file mode 100644 index d83112c07..000000000 --- a/lib/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: ["@repo/eslint-config/react.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/lib/eslint.config.mjs b/lib/eslint.config.mjs new file mode 100644 index 000000000..00bf0d907 --- /dev/null +++ b/lib/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@repo/eslint-config/react.js"; + +/** @type {import("eslint").Linter.Config} */ +export default config; diff --git a/lib/package.json b/lib/package.json index 04160d506..47e7180e3 100644 --- a/lib/package.json +++ b/lib/package.json @@ -74,7 +74,7 @@ "vitest": "^2.1.8" }, "dependencies": { - "r18gs": "^3.0.1" + "r18gs": "2.0.2" }, "peerDependencies": { "@types/react": "16.8 - 19", diff --git a/lib/src/client/core/core.test.tsx b/lib/src/client/core/core.test.tsx index 2fee4bee0..8cdda1dbd 100644 --- a/lib/src/client/core/core.test.tsx +++ b/lib/src/client/core/core.test.tsx @@ -42,7 +42,7 @@ describe("theme-switcher", () => { await act(() => { // globalThis.window.media = LIGHT as ResolvedScheme; // @ts-expect-error -- ok - m.onchange?.(); + q.onchange?.(); }); expect(hook.result.current.mode).toBe(DARK); }); diff --git a/lib/src/client/core/core.tsx b/lib/src/client/core/core.tsx index 6645bfd41..65d451a8c 100644 --- a/lib/src/client/core/core.tsx +++ b/lib/src/client/core/core.tsx @@ -1,6 +1,5 @@ import { DARK, LIGHT } from "../../constants"; import { ColorSchemePreference, ResolvedScheme, useStore } from "../../utils"; -import { useEffect } from "react"; import { noFOUCScript } from "./no-fouc"; let media: MediaQueryList, @@ -36,17 +35,18 @@ export interface CoreProps { /** Modify transition globally to avoid patched transitions */ const modifyTransition = (themeTransition = "none", nonce = "") => { - const css = document.createElement("style"); + const doc = document; + const css = doc.createElement("style"); /** split by ';' to prevent CSS injection */ css.textContent = `*,*:after,*:before{transition:${themeTransition.split(";")[0]} !important;}`; - nonce && css.setAttribute("nonce", nonce); - document.head.appendChild(css); + css.setAttribute("nonce", nonce); + doc.head.appendChild(css); return () => { // Force restyle - getComputedStyle(document.body); + getComputedStyle(doc.body); // Wait for next tick before removing - setTimeout(() => document.head.removeChild(css), 1); + setTimeout(() => doc.head.removeChild(css), 1); }; }; @@ -62,14 +62,15 @@ const modifyTransition = (themeTransition = "none", nonce = "") => { * @source - Source code */ export const Core = ({ t, nonce, k = "o" }: CoreProps) => { + const isWindowDefined = typeof window != "undefined"; // handle client side exceptions when script is not run. <- for client side apps like vite or CRA - if (typeof window !== "undefined" && !window.m) noFOUCScript(k); + if (isWindowDefined && !window.q) noFOUCScript(k); - const [{ m: mode, s: systemMode }, setThemeState] = useStore(); + const [{ m, s }, setThemeState] = useStore(); - useEffect(() => { + if (!updateDOM && isWindowDefined) { // store global functions to local variables to avoid any interference - [media, updateDOM] = [m, u]; + [media, updateDOM] = [q, u]; /** Updating media: prefers-color-scheme*/ media.addEventListener("change", () => setThemeState(state => ({ ...state, s: media.matches ? DARK : LIGHT })), @@ -78,13 +79,12 @@ export const Core = ({ t, nonce, k = "o" }: CoreProps) => { addEventListener("storage", (e: StorageEvent): void => { e.key === k && setThemeState(state => ({ ...state, m: e.newValue as ColorSchemePreference })); }); - }, []); - - useEffect(() => { + } + if (updateDOM) { const restoreTransitions = modifyTransition(t, nonce); - updateDOM(mode, systemMode); + updateDOM(m, s); restoreTransitions(); - }, [systemMode, mode, t, nonce]); + } return