@serwist/next 9.2.3 → 9.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import type { BuildOptions } from "@serwist/cli";
2
+ import type { SerwistOptions } from "./lib/config/types.js";
3
+ import { generateGlobPatterns } from "./lib/config/utils.js";
4
+ export declare const serwist: (options: SerwistOptions) => Promise<BuildOptions>;
5
+ export { generateGlobPatterns };
6
+ export type { SerwistOptions };
7
+ //# sourceMappingURL=index.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.config.d.ts","sourceRoot":"","sources":["../src/index.config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAI7D,eAAO,MAAM,OAAO,GAAU,SAAS,cAAc,KAAG,OAAO,CAAC,YAAY,CA4E3E,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,YAAY,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,100 @@
1
+ import { createRequire } from 'module';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { rebasePath } from '@serwist/build';
5
+ import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from 'next/constants.js';
6
+
7
+ const generateGlobPatterns = (distDir)=>[
8
+ `${distDir}static/**/*.{js,css,html,ico,apng,png,avif,jpg,jpeg,jfif,pjpeg,pjp,gif,svg,webp,json,webmanifest}`,
9
+ "public/**/*"
10
+ ];
11
+
12
+ const __require = createRequire(import.meta.url);
13
+ const loadNextConfig = __require("next/dist/server/config.js");
14
+ const serwist = async (options)=>{
15
+ const isWatch = process.env.SERWIST_ENV === "watch";
16
+ const nextPhase = isWatch ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_BUILD;
17
+ const config = await loadNextConfig.default(nextPhase, process.cwd(), {
18
+ silent: false
19
+ });
20
+ const basePath = config.basePath || "/";
21
+ let distDir = config.distDir;
22
+ if (distDir[0] === "/") distDir = distDir.slice(1);
23
+ if (distDir[distDir.length - 1] !== "/") distDir += "/";
24
+ const distServerDir = `${distDir}server/`;
25
+ const distAppDir = `${distServerDir}app/`;
26
+ const distPagesDir = `${distServerDir}pages/`;
27
+ const { precachePrerendered = true, globDirectory = process.cwd(), ...cliOptions } = options;
28
+ for (const file of [
29
+ cliOptions.swDest,
30
+ `${cliOptions.swDest}.map`
31
+ ]){
32
+ fs.rmSync(file, {
33
+ force: true
34
+ });
35
+ }
36
+ return {
37
+ dontCacheBustURLsMatching: new RegExp(`^${distDir}static/`),
38
+ disablePrecacheManifest: isWatch,
39
+ ...cliOptions,
40
+ globDirectory,
41
+ globPatterns: [
42
+ ...cliOptions.globPatterns ?? generateGlobPatterns(distDir),
43
+ ...precachePrerendered ? [
44
+ `${distServerDir}{app,pages}/**/*.html`
45
+ ] : []
46
+ ],
47
+ globIgnores: [
48
+ `${distAppDir}**/_not-found.html`,
49
+ `${distPagesDir}404.html`,
50
+ `${distPagesDir}500.html`,
51
+ ...cliOptions.globIgnores ?? [],
52
+ rebasePath({
53
+ baseDirectory: globDirectory,
54
+ file: cliOptions.swSrc
55
+ }),
56
+ rebasePath({
57
+ baseDirectory: globDirectory,
58
+ file: cliOptions.swDest
59
+ }),
60
+ rebasePath({
61
+ baseDirectory: globDirectory,
62
+ file: `${cliOptions.swDest}.map`
63
+ })
64
+ ],
65
+ manifestTransforms: [
66
+ ...cliOptions.manifestTransforms ?? [],
67
+ (manifestEntries)=>{
68
+ const manifest = manifestEntries.map((m)=>{
69
+ if (m.url.startsWith(distAppDir)) {
70
+ m.url = m.url.slice(distAppDir.length - 1);
71
+ }
72
+ if (m.url.startsWith(distPagesDir)) {
73
+ m.url = m.url.slice(distPagesDir.length - 1);
74
+ }
75
+ if (m.url.endsWith(".html")) {
76
+ if (m.url.endsWith("/index.html")) {
77
+ m.url = m.url.slice(0, m.url.lastIndexOf("/") + 1);
78
+ } else {
79
+ m.url = m.url.substring(0, m.url.lastIndexOf("."));
80
+ }
81
+ m.url = path.posix.join(basePath, m.url);
82
+ }
83
+ if (m.url.startsWith(distDir)) {
84
+ m.url = `${config.assetPrefix ?? ""}/_next/${m.url.slice(distDir.length)}`;
85
+ }
86
+ if (m.url.startsWith("public/")) {
87
+ m.url = path.posix.join(basePath, m.url.slice(7));
88
+ }
89
+ return m;
90
+ });
91
+ return {
92
+ manifest,
93
+ warnings: []
94
+ };
95
+ }
96
+ ]
97
+ };
98
+ };
99
+
100
+ export { generateGlobPatterns, serwist };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAMnE;;;;GAIG;AACH,QAAA,MAAM,eAAe,GAAI,aAAa,qBAAqB,KAAG,CAAC,CAAC,UAAU,CAAC,EAAE,UAAU,KAAK,UAAU,CAgOrG,CAAC;AAEF,eAAe,eAAe,CAAC;AAC/B,OAAO,EAAE,6BAA6B,EAAE,CAAC;AACzC,YAAY,EAAE,qBAAqB,IAAI,aAAa,EAAE,6BAA6B,IAAI,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAMnE;;;;GAIG;AACH,QAAA,MAAM,eAAe,GAAI,aAAa,qBAAqB,KAAG,CAAC,CAAC,UAAU,CAAC,EAAE,UAAU,KAAK,UAAU,CA6NrG,CAAC;AAEF,eAAe,eAAe,CAAC;AAC/B,OAAO,EAAE,6BAA6B,EAAE,CAAC;AACzC,YAAY,EAAE,qBAAqB,IAAI,aAAa,EAAE,6BAA6B,IAAI,qBAAqB,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -6,7 +6,8 @@ import { ChildCompilationPlugin, relativeToOutputPath } from '@serwist/webpack-p
6
6
  import { globSync } from 'glob';
7
7
  import crypto from 'node:crypto';
8
8
  import { createRequire } from 'node:module';
9
- import chalk from 'chalk';
9
+ import { green, bold, white, yellow, red } from 'kolorist';
10
+ import semver from 'semver';
10
11
  import { validationErrorMap, SerwistConfigError } from '@serwist/build/schema';
11
12
  import { z } from 'zod';
12
13
  import { a as injectManifestOptions } from './chunks/schema.js';
@@ -51,38 +52,44 @@ const loadTSConfig = (baseDir, relativeTSConfigPath)=>{
51
52
  }
52
53
  };
53
54
 
54
- const mapLoggingMethodToConsole = {
55
- wait: "log",
56
- error: "error",
57
- warn: "warn",
58
- info: "log",
59
- event: "log"
60
- };
61
- const prefixes = {
62
- wait: `${chalk.white(chalk.bold("○"))} (serwist)`,
63
- error: `${chalk.red(chalk.bold("X"))} (serwist)`,
64
- warn: `${chalk.yellow(chalk.bold("⚠"))} (serwist)`,
65
- info: `${chalk.white(chalk.bold("○"))} (serwist)`,
66
- event: `${chalk.green(chalk.bold("✓"))} (serwist)`
67
- };
55
+ const require$1 = createRequire(import.meta.url);
56
+ const LOGGING_SPACE_PREFIX = semver.gte(require$1("next/package.json").version, "16.0.0") ? "" : " ";
68
57
  const prefixedLog = (prefixType, ...message)=>{
69
- const consoleMethod = mapLoggingMethodToConsole[prefixType];
70
- const prefix = prefixes[prefixType];
58
+ let prefix;
59
+ let consoleMethod;
60
+ switch(prefixType){
61
+ case "wait":
62
+ prefix = `${white(bold("○"))} (serwist)`;
63
+ consoleMethod = "log";
64
+ break;
65
+ case "error":
66
+ prefix = `${red(bold("X"))} (serwist)`;
67
+ consoleMethod = "error";
68
+ break;
69
+ case "warn":
70
+ prefix = `${yellow(bold("⚠"))} (serwist)`;
71
+ consoleMethod = "warn";
72
+ break;
73
+ case "info":
74
+ prefix = `${white(bold("○"))} (serwist)`;
75
+ consoleMethod = "log";
76
+ break;
77
+ case "event":
78
+ prefix = `${green(bold("✓"))} (serwist)`;
79
+ consoleMethod = "log";
80
+ break;
81
+ }
71
82
  if ((message[0] === "" || message[0] === undefined) && message.length === 1) {
72
83
  message.shift();
73
84
  }
74
85
  if (message.length === 0) {
75
86
  console[consoleMethod]("");
76
87
  } else {
77
- console[consoleMethod](` ${prefix}`, ...message);
88
+ console[consoleMethod](`${LOGGING_SPACE_PREFIX}${prefix}`, ...message);
78
89
  }
79
90
  };
80
- const info = (...message)=>{
81
- prefixedLog("info", ...message);
82
- };
83
- const event = (...message)=>{
84
- prefixedLog("event", ...message);
85
- };
91
+ const info = (...message)=>prefixedLog("info", ...message);
92
+ const event = (...message)=>prefixedLog("event", ...message);
86
93
 
87
94
  const validateInjectManifestOptions = (input)=>{
88
95
  const result = injectManifestOptions.safeParse(input, {
@@ -102,7 +109,7 @@ let warnedTurbopack = false;
102
109
  const withSerwistInit = (userOptions)=>{
103
110
  if (!warnedTurbopack && process.env.TURBOPACK && !userOptions.disable && !process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING) {
104
111
  warnedTurbopack = true;
105
- console.warn(`[@serwist/next] WARNING: You are using '@serwist/next' with \`next dev --turbopack\`, but Serwist doesn't support Turbopack at the moment. It is recommended that you set \`disable\` to \`process.env.NODE_ENV !== \"production\"\`. Follow https://github.com/serwist/serwist/issues/54 for progress on Serwist + Turbopack. You can also suppress this warning by setting SERWIST_SUPPRESS_TURBOPACK_WARNING=1.`);
112
+ console.warn(`[@serwist/next] WARNING: You are using '@serwist/next' with \`next dev --turbopack\`, but Serwist doesn't support Turbopack at the moment. It is recommended that you set \`disable\` to \`process.env.NODE_ENV !== "production"\`. Follow https://github.com/serwist/serwist/issues/54 for progress on Serwist + Turbopack. You can also suppress this warning by setting SERWIST_SUPPRESS_TURBOPACK_WARNING=1.`);
106
113
  }
107
114
  return (nextConfig = {})=>({
108
115
  ...nextConfig,
@@ -187,10 +194,8 @@ const withSerwistInit = (userOptions)=>{
187
194
  cwd: destDir
188
195
  });
189
196
  for (const file of cleanUpList){
190
- fs.rm(file, {
197
+ fs.rmSync(file, {
191
198
  force: true
192
- }, (err)=>{
193
- if (err) throw err;
194
199
  });
195
200
  }
196
201
  const shouldBuildSWEntryWorker = cacheOnNavigation;
@@ -0,0 +1,19 @@
1
+ import { Serwist } from "@serwist/window";
2
+ import { type ReactNode } from "react";
3
+ import { useSerwist } from "./lib/context.js";
4
+ export interface SerwistProviderProps {
5
+ swUrl: string;
6
+ register?: boolean;
7
+ cacheOnNavigation?: boolean;
8
+ reloadOnOnline?: boolean;
9
+ options?: RegistrationOptions;
10
+ children?: ReactNode;
11
+ }
12
+ declare global {
13
+ interface Window {
14
+ serwist: Serwist;
15
+ }
16
+ }
17
+ export declare function SerwistProvider({ swUrl, register, cacheOnNavigation, reloadOnOnline, options, children, }: SerwistProviderProps): import("react").JSX.Element;
18
+ export { useSerwist };
19
+ //# sourceMappingURL=index.react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.react.d.ts","sourceRoot":"","sources":["../src/index.react.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,OAAO,EAAE,OAAO,CAAC;KAClB;CACF;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAe,EACf,iBAAwB,EACxB,cAAqB,EACrB,OAAO,EACP,QAAQ,GACT,EAAE,oBAAoB,+BAwDtB;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Serwist } from '@serwist/window';
3
+ import { isCurrentPageOutOfScope } from '@serwist/window/internal';
4
+ import { createContext, useContext, useState, useEffect } from 'react';
5
+
6
+ const SerwistContext = createContext(null);
7
+ const useSerwist = ()=>{
8
+ const context = useContext(SerwistContext);
9
+ if (!context) {
10
+ throw new Error("[useSerwist]: 'SerwistContext' is not available.");
11
+ }
12
+ return context;
13
+ };
14
+
15
+ function SerwistProvider({ swUrl, register = true, cacheOnNavigation = true, reloadOnOnline = true, options, children }) {
16
+ const [serwist] = useState(()=>{
17
+ if (typeof window === "undefined") return null;
18
+ if (!(window.serwist && window.serwist instanceof Serwist) && "serviceWorker" in navigator) {
19
+ window.serwist = new Serwist(swUrl, {
20
+ ...options,
21
+ type: options?.type || "module",
22
+ scope: options?.scope || "/"
23
+ });
24
+ }
25
+ return window.serwist ?? null;
26
+ });
27
+ useEffect(()=>{
28
+ const scope = options?.scope || "/";
29
+ if (register && !isCurrentPageOutOfScope(scope)) {
30
+ void serwist?.register();
31
+ }
32
+ }, [
33
+ register,
34
+ options?.scope,
35
+ serwist?.register
36
+ ]);
37
+ useEffect(()=>{
38
+ const cacheUrls = async (url)=>{
39
+ if (!window.navigator.onLine || !url) {
40
+ return;
41
+ }
42
+ serwist?.messageSW({
43
+ type: "CACHE_URLS",
44
+ payload: {
45
+ urlsToCache: [
46
+ url
47
+ ]
48
+ }
49
+ });
50
+ };
51
+ const cacheCurrentPathname = ()=>cacheUrls(window.location.pathname);
52
+ const pushState = history.pushState;
53
+ const replaceState = history.replaceState;
54
+ if (cacheOnNavigation) {
55
+ history.pushState = (...args)=>{
56
+ pushState.apply(history, args);
57
+ cacheUrls(args[2]);
58
+ };
59
+ history.replaceState = (...args)=>{
60
+ replaceState.apply(history, args);
61
+ cacheUrls(args[2]);
62
+ };
63
+ window.addEventListener("online", cacheCurrentPathname);
64
+ }
65
+ return ()=>{
66
+ history.pushState = pushState;
67
+ history.replaceState = replaceState;
68
+ window.removeEventListener("online", cacheCurrentPathname);
69
+ };
70
+ }, [
71
+ serwist?.messageSW,
72
+ cacheOnNavigation
73
+ ]);
74
+ useEffect(()=>{
75
+ const reload = ()=>location.reload();
76
+ if (reloadOnOnline) {
77
+ window.addEventListener("online", reload);
78
+ }
79
+ return ()=>{
80
+ window.removeEventListener("online", reload);
81
+ };
82
+ }, [
83
+ reloadOnOnline
84
+ ]);
85
+ return jsx(SerwistContext.Provider, {
86
+ value: {
87
+ serwist
88
+ },
89
+ children: children
90
+ });
91
+ }
92
+
93
+ export { SerwistProvider, useSerwist };
@@ -0,0 +1,11 @@
1
+ import type { BuildOptions } from "@serwist/cli";
2
+ import type { Optional } from "@serwist/utils";
3
+ export interface SerwistOptions extends Optional<BuildOptions, "globDirectory"> {
4
+ /**
5
+ * Whether Serwist should precache prerendered routes.
6
+ *
7
+ * @default true
8
+ */
9
+ precachePrerendered?: boolean;
10
+ }
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IAC7E;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B"}
@@ -0,0 +1,2 @@
1
+ export declare const generateGlobPatterns: (distDir: string) => string[];
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/config/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,aAGnD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Serwist } from "@serwist/window";
2
+ export interface SerwistContextValues {
3
+ serwist: Serwist | null;
4
+ }
5
+ export declare const SerwistContext: import("react").Context<SerwistContextValues>;
6
+ export declare const useSerwist: () => SerwistContextValues;
7
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED,eAAO,MAAM,cAAc,+CAA6C,CAAC;AAEzE,eAAO,MAAM,UAAU,4BAMtB,CAAC"}
@@ -1,3 +1,4 @@
1
+ export type LoggingMethods = "wait" | "error" | "warn" | "info" | "event";
1
2
  export declare const wait: (...message: any[]) => void;
2
3
  export declare const error: (...message: any[]) => void;
3
4
  export declare const warn: (...message: any[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAErC,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,GAAG,SAAS,GAAG,EAAE,SAEtC,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAErC,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAErC,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,GAAG,SAAS,GAAG,EAAE,SAEtC,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAyC1E,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAAoC,CAAC;AAE3E,eAAO,MAAM,KAAK,GAAI,GAAG,SAAS,GAAG,EAAE,SAAqC,CAAC;AAE7E,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAAoC,CAAC;AAE3E,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,GAAG,EAAE,SAAoC,CAAC;AAE3E,eAAO,MAAM,KAAK,GAAI,GAAG,SAAS,GAAG,EAAE,SAAqC,CAAC"}
@@ -9,22 +9,6 @@ export declare const injectPartial: z.ZodObject<{
9
9
  globPublicPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
10
10
  }, z.core.$strict>;
11
11
  export declare const injectManifestOptions: z.ZodObject<{
12
- cacheOnNavigation: z.ZodDefault<z.ZodBoolean>;
13
- disable: z.ZodDefault<z.ZodBoolean>;
14
- register: z.ZodDefault<z.ZodBoolean>;
15
- reloadOnOnline: z.ZodDefault<z.ZodBoolean>;
16
- scope: z.ZodOptional<z.ZodString>;
17
- swUrl: z.ZodDefault<z.ZodString>;
18
- globPublicPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
19
- swDest: z.ZodString;
20
- compileSrc: z.ZodDefault<z.ZodBoolean>;
21
- webpackCompilationPlugins: z.ZodOptional<z.ZodArray<z.ZodAny>>;
22
- injectionPoint: z.ZodDefault<z.ZodString>;
23
- swSrc: z.ZodString;
24
- chunks: z.ZodOptional<z.ZodArray<z.ZodString>>;
25
- exclude: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodCustom<RegExp, RegExp>, z.ZodPipe<z.ZodCustom<z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>, z.ZodTransform<(args_0: any) => boolean, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>>]>>>;
26
- excludeChunks: z.ZodOptional<z.ZodArray<z.ZodString>>;
27
- include: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodCustom<RegExp, RegExp>, z.ZodPipe<z.ZodCustom<z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>, z.ZodTransform<(args_0: any) => boolean, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>>]>>>;
28
12
  additionalPrecacheEntries: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
29
13
  integrity: z.ZodOptional<z.ZodString>;
30
14
  revision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -86,5 +70,21 @@ export declare const injectManifestOptions: z.ZodObject<{
86
70
  }, z.core.$strict>>>>>>;
87
71
  maximumFileSizeToCacheInBytes: z.ZodDefault<z.ZodNumber>;
88
72
  modifyURLPrefix: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
73
+ injectionPoint: z.ZodDefault<z.ZodString>;
74
+ swSrc: z.ZodString;
75
+ swDest: z.ZodString;
76
+ cacheOnNavigation: z.ZodDefault<z.ZodBoolean>;
77
+ disable: z.ZodDefault<z.ZodBoolean>;
78
+ register: z.ZodDefault<z.ZodBoolean>;
79
+ reloadOnOnline: z.ZodDefault<z.ZodBoolean>;
80
+ scope: z.ZodOptional<z.ZodString>;
81
+ swUrl: z.ZodDefault<z.ZodString>;
82
+ globPublicPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
83
+ compileSrc: z.ZodDefault<z.ZodBoolean>;
84
+ webpackCompilationPlugins: z.ZodOptional<z.ZodArray<z.ZodAny>>;
85
+ chunks: z.ZodOptional<z.ZodArray<z.ZodString>>;
86
+ exclude: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodCustom<RegExp, RegExp>, z.ZodPipe<z.ZodCustom<z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>, z.ZodTransform<(args_0: any) => boolean, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>>]>>>;
87
+ excludeChunks: z.ZodOptional<z.ZodArray<z.ZodString>>;
88
+ include: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodCustom<RegExp, RegExp>, z.ZodPipe<z.ZodCustom<z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>, z.ZodTransform<(args_0: any) => boolean, z.core.$InferInnerFunctionType<z.ZodTuple<[z.ZodAny], null>, z.ZodBoolean>>>]>>>;
89
89
  }, z.core.$strict>;
90
90
  //# sourceMappingURL=schema.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serwist/next",
3
- "version": "9.2.3",
3
+ "version": "9.3.1",
4
4
  "type": "module",
5
5
  "description": "A module that integrates Serwist into your Next.js application.",
6
6
  "files": [
@@ -32,14 +32,20 @@
32
32
  "types": "./dist/index.d.ts",
33
33
  "typesVersions": {
34
34
  "*": {
35
- "worker": [
36
- "./dist/index.worker.d.ts"
35
+ "config": [
36
+ "./dist/index.config.d.ts"
37
37
  ],
38
- "typings": [
39
- "./dist/sw-entry.d.ts"
38
+ "react": [
39
+ "./dist/index.react.d.ts"
40
40
  ],
41
41
  "schema": [
42
42
  "./dist/index.schema.d.ts"
43
+ ],
44
+ "typings": [
45
+ "./dist/sw-entry.d.ts"
46
+ ],
47
+ "worker": [
48
+ "./dist/index.worker.d.ts"
43
49
  ]
44
50
  }
45
51
  },
@@ -48,45 +54,62 @@
48
54
  "types": "./dist/index.d.ts",
49
55
  "default": "./dist/index.js"
50
56
  },
51
- "./worker": {
52
- "types": "./dist/index.worker.d.ts",
53
- "default": "./dist/index.worker.js"
57
+ "./config": {
58
+ "types": "./dist/index.config.d.ts",
59
+ "default": "./dist/index.config.js"
54
60
  },
55
- "./typings": {
56
- "types": "./dist/sw-entry.d.ts"
61
+ "./react": {
62
+ "types": "./dist/index.react.d.ts",
63
+ "default": "./dist/index.react.js"
57
64
  },
58
65
  "./schema": {
59
66
  "types": "./dist/index.schema.d.ts",
60
67
  "default": "./dist/index.schema.js"
61
68
  },
69
+ "./typings": {
70
+ "types": "./dist/sw-entry.d.ts"
71
+ },
72
+ "./worker": {
73
+ "types": "./dist/index.worker.d.ts",
74
+ "default": "./dist/index.worker.js"
75
+ },
62
76
  "./package.json": "./package.json"
63
77
  },
64
78
  "dependencies": {
65
- "chalk": "5.6.2",
66
79
  "glob": "10.5.0",
67
- "zod": "4.1.12",
68
- "@serwist/build": "9.2.3",
69
- "@serwist/webpack-plugin": "9.2.3",
70
- "@serwist/window": "9.2.3",
71
- "serwist": "9.2.3"
80
+ "kolorist": "1.8.0",
81
+ "semver": "7.7.3",
82
+ "zod": "4.2.1",
83
+ "@serwist/build": "9.3.1",
84
+ "@serwist/webpack-plugin": "9.3.1",
85
+ "@serwist/window": "9.3.1",
86
+ "serwist": "9.3.1"
72
87
  },
73
88
  "devDependencies": {
74
- "@types/node": "24.10.1",
75
- "next": "16.0.3",
76
- "react": "19.2.0",
77
- "react-dom": "19.2.0",
78
- "rollup": "4.53.3",
79
- "type-fest": "5.2.0",
89
+ "@types/node": "25.0.3",
90
+ "@types/react": "19.2.7",
91
+ "@types/semver": "7.7.1",
92
+ "next": "16.1.0",
93
+ "react": "19.2.3",
94
+ "react-dom": "19.2.3",
95
+ "rollup": "4.54.0",
96
+ "type-fest": "5.3.1",
80
97
  "typescript": "5.9.3",
81
- "webpack": "5.103.0",
82
- "@serwist/configs": "9.2.3",
83
- "@serwist/utils": "9.2.3"
98
+ "webpack": "5.104.1",
99
+ "@serwist/cli": "9.3.1",
100
+ "@serwist/configs": "9.3.1",
101
+ "@serwist/utils": "9.3.1"
84
102
  },
85
103
  "peerDependencies": {
86
104
  "next": ">=14.0.0",
87
- "typescript": ">=5.0.0"
105
+ "react": ">=18.0.0",
106
+ "typescript": ">=5.0.0",
107
+ "@serwist/cli": "9.3.1"
88
108
  },
89
109
  "peerDependenciesMeta": {
110
+ "@serwist/cli": {
111
+ "optional": true
112
+ },
90
113
  "typescript": {
91
114
  "optional": true
92
115
  }
@@ -0,0 +1,91 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { rebasePath } from "@serwist/build";
4
+ import type { BuildOptions } from "@serwist/cli";
5
+ import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "next/constants.js";
6
+ import type { SerwistOptions } from "./lib/config/types.js";
7
+ import { generateGlobPatterns } from "./lib/config/utils.js";
8
+
9
+ import loadNextConfig = require("next/dist/server/config.js");
10
+
11
+ export const serwist = async (options: SerwistOptions): Promise<BuildOptions> => {
12
+ const isWatch = process.env.SERWIST_ENV === "watch";
13
+ const nextPhase = isWatch ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_BUILD;
14
+ const config = await loadNextConfig.default(nextPhase, process.cwd(), {
15
+ silent: false,
16
+ });
17
+ const basePath = config.basePath || "/";
18
+ let distDir = config.distDir;
19
+ if (distDir[0] === "/") distDir = distDir.slice(1);
20
+ if (distDir[distDir.length - 1] !== "/") distDir += "/";
21
+ const distServerDir = `${distDir}server/`;
22
+ const distAppDir = `${distServerDir}app/`;
23
+ const distPagesDir = `${distServerDir}pages/`;
24
+ const { precachePrerendered = true, globDirectory = process.cwd(), ...cliOptions } = options;
25
+ for (const file of [cliOptions.swDest, `${cliOptions.swDest}.map`]) {
26
+ fs.rmSync(file, { force: true });
27
+ }
28
+ return {
29
+ dontCacheBustURLsMatching: new RegExp(`^${distDir}static/`),
30
+ disablePrecacheManifest: isWatch,
31
+ ...cliOptions,
32
+ globDirectory,
33
+ globPatterns: [
34
+ ...(cliOptions.globPatterns ?? generateGlobPatterns(distDir)),
35
+ ...(precachePrerendered ? [`${distServerDir}{app,pages}/**/*.html`] : []),
36
+ ],
37
+ globIgnores: [
38
+ `${distAppDir}**/_not-found.html`,
39
+ `${distPagesDir}404.html`,
40
+ `${distPagesDir}500.html`,
41
+ ...(cliOptions.globIgnores ?? []),
42
+ rebasePath({ baseDirectory: globDirectory, file: cliOptions.swSrc }),
43
+ rebasePath({ baseDirectory: globDirectory, file: cliOptions.swDest }),
44
+ rebasePath({ baseDirectory: globDirectory, file: `${cliOptions.swDest}.map` }),
45
+ ],
46
+ manifestTransforms: [
47
+ ...(cliOptions.manifestTransforms ?? []),
48
+ (manifestEntries) => {
49
+ const manifest = manifestEntries.map((m) => {
50
+ if (m.url.startsWith(distAppDir)) {
51
+ // Keep the prefixing slash.
52
+ m.url = m.url.slice(distAppDir.length - 1);
53
+ }
54
+ if (m.url.startsWith(distPagesDir)) {
55
+ // Keep the prefixing slash.
56
+ m.url = m.url.slice(distPagesDir.length - 1);
57
+ }
58
+ if (m.url.endsWith(".html")) {
59
+ // trailingSlash: true && output: 'export'
60
+ // or root index.html.
61
+ // https://nextjs.org/docs/app/api-reference/config/next-config-js/trailingSlash
62
+ // "/abc/index.html" -> "/abc/"
63
+ // "/index.html" -> "/"
64
+ if (m.url.endsWith("/index.html")) {
65
+ m.url = m.url.slice(0, m.url.lastIndexOf("/") + 1);
66
+ }
67
+ // "/xxx.html" -> "/xxx"
68
+ else {
69
+ m.url = m.url.substring(0, m.url.lastIndexOf("."));
70
+ }
71
+ m.url = path.posix.join(basePath, m.url);
72
+ }
73
+ // Replace all references to "$(distDir)" with "$(assetPrefix)/_next/".
74
+ if (m.url.startsWith(distDir)) {
75
+ m.url = `${config.assetPrefix ?? ""}/_next/${m.url.slice(distDir.length)}`;
76
+ }
77
+ // Replace all references to public/ with "$(basePath)/".
78
+ if (m.url.startsWith("public/")) {
79
+ m.url = path.posix.join(basePath, m.url.slice(7));
80
+ }
81
+ return m;
82
+ });
83
+ return { manifest, warnings: [] };
84
+ },
85
+ ],
86
+ };
87
+ };
88
+
89
+ export { generateGlobPatterns };
90
+
91
+ export type { SerwistOptions };
@@ -0,0 +1,86 @@
1
+ import { Serwist } from "@serwist/window";
2
+ import { isCurrentPageOutOfScope } from "@serwist/window/internal";
3
+ import { type ReactNode, useEffect, useState } from "react";
4
+ import { SerwistContext, useSerwist } from "./lib/context.js";
5
+
6
+ export interface SerwistProviderProps {
7
+ swUrl: string;
8
+ register?: boolean;
9
+ cacheOnNavigation?: boolean;
10
+ reloadOnOnline?: boolean;
11
+ options?: RegistrationOptions;
12
+ children?: ReactNode;
13
+ }
14
+
15
+ declare global {
16
+ interface Window {
17
+ serwist: Serwist;
18
+ }
19
+ }
20
+
21
+ export function SerwistProvider({
22
+ swUrl,
23
+ register = true,
24
+ cacheOnNavigation = true,
25
+ reloadOnOnline = true,
26
+ options,
27
+ children,
28
+ }: SerwistProviderProps) {
29
+ const [serwist] = useState(() => {
30
+ if (typeof window === "undefined") return null;
31
+ if (!(window.serwist && window.serwist instanceof Serwist) && "serviceWorker" in navigator) {
32
+ window.serwist = new Serwist(swUrl, { ...options, type: options?.type || "module", scope: options?.scope || "/" });
33
+ }
34
+ return window.serwist ?? null;
35
+ });
36
+ useEffect(() => {
37
+ const scope = options?.scope || "/";
38
+ if (register && !isCurrentPageOutOfScope(scope)) {
39
+ void serwist?.register();
40
+ }
41
+ }, [register, options?.scope, serwist?.register]);
42
+ useEffect(() => {
43
+ const cacheUrls = async (url?: string | URL | null | undefined) => {
44
+ if (!window.navigator.onLine || !url) {
45
+ return;
46
+ }
47
+ serwist?.messageSW({
48
+ type: "CACHE_URLS",
49
+ payload: { urlsToCache: [url] },
50
+ });
51
+ };
52
+ const cacheCurrentPathname = () => cacheUrls(window.location.pathname);
53
+ const pushState = history.pushState;
54
+ const replaceState = history.replaceState;
55
+
56
+ if (cacheOnNavigation) {
57
+ history.pushState = (...args) => {
58
+ pushState.apply(history, args);
59
+ cacheUrls(args[2]);
60
+ };
61
+ history.replaceState = (...args) => {
62
+ replaceState.apply(history, args);
63
+ cacheUrls(args[2]);
64
+ };
65
+ window.addEventListener("online", cacheCurrentPathname);
66
+ }
67
+
68
+ return () => {
69
+ history.pushState = pushState;
70
+ history.replaceState = replaceState;
71
+ window.removeEventListener("online", cacheCurrentPathname);
72
+ };
73
+ }, [serwist?.messageSW, cacheOnNavigation]);
74
+ useEffect(() => {
75
+ const reload = () => location.reload();
76
+ if (reloadOnOnline) {
77
+ window.addEventListener("online", reload);
78
+ }
79
+ return () => {
80
+ window.removeEventListener("online", reload);
81
+ };
82
+ }, [reloadOnOnline]);
83
+ return <SerwistContext.Provider value={{ serwist }}>{children}</SerwistContext.Provider>;
84
+ }
85
+
86
+ export { useSerwist };
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@ const withSerwistInit = (userOptions: InjectManifestOptions): ((nextConfig?: Nex
24
24
  if (!warnedTurbopack && process.env.TURBOPACK && !userOptions.disable && !process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING) {
25
25
  warnedTurbopack = true;
26
26
  console.warn(
27
- `[@serwist/next] WARNING: You are using '@serwist/next' with \`next dev --turbopack\`, but Serwist doesn't support Turbopack at the moment. It is recommended that you set \`disable\` to \`process.env.NODE_ENV !== \"production\"\`. Follow https://github.com/serwist/serwist/issues/54 for progress on Serwist + Turbopack. You can also suppress this warning by setting SERWIST_SUPPRESS_TURBOPACK_WARNING=1.`,
27
+ `[@serwist/next] WARNING: You are using '@serwist/next' with \`next dev --turbopack\`, but Serwist doesn't support Turbopack at the moment. It is recommended that you set \`disable\` to \`process.env.NODE_ENV !== "production"\`. Follow https://github.com/serwist/serwist/issues/54 for progress on Serwist + Turbopack. You can also suppress this warning by setting SERWIST_SUPPRESS_TURBOPACK_WARNING=1.`,
28
28
  );
29
29
  }
30
30
  return (nextConfig = {}) => ({
@@ -141,9 +141,7 @@ const withSerwistInit = (userOptions: InjectManifestOptions): ((nextConfig?: Nex
141
141
  });
142
142
 
143
143
  for (const file of cleanUpList) {
144
- fs.rm(file, { force: true }, (err) => {
145
- if (err) throw err;
146
- });
144
+ fs.rmSync(file, { force: true });
147
145
  }
148
146
 
149
147
  const shouldBuildSWEntryWorker = cacheOnNavigation;
@@ -214,7 +212,6 @@ const withSerwistInit = (userOptions: InjectManifestOptions): ((nextConfig?: Nex
214
212
  },
215
213
  ],
216
214
  manifestTransforms: [
217
- // TODO(ducanhgh): move this spread to below our transform function?
218
215
  ...manifestTransforms,
219
216
  async (manifestEntries, compilation) => {
220
217
  // This path always uses forward slashes, so it is safe to use it in the following string replace.
@@ -0,0 +1,11 @@
1
+ import type { BuildOptions } from "@serwist/cli";
2
+ import type { Optional } from "@serwist/utils";
3
+
4
+ export interface SerwistOptions extends Optional<BuildOptions, "globDirectory"> {
5
+ /**
6
+ * Whether Serwist should precache prerendered routes.
7
+ *
8
+ * @default true
9
+ */
10
+ precachePrerendered?: boolean;
11
+ }
@@ -0,0 +1,4 @@
1
+ export const generateGlobPatterns = (distDir: string) => [
2
+ `${distDir}static/**/*.{js,css,html,ico,apng,png,avif,jpg,jpeg,jfif,pjpeg,pjp,gif,svg,webp,json,webmanifest}`,
3
+ "public/**/*",
4
+ ];
@@ -0,0 +1,16 @@
1
+ import type { Serwist } from "@serwist/window";
2
+ import { createContext, useContext } from "react";
3
+
4
+ export interface SerwistContextValues {
5
+ serwist: Serwist | null;
6
+ }
7
+
8
+ export const SerwistContext = createContext<SerwistContextValues>(null!);
9
+
10
+ export const useSerwist = () => {
11
+ const context = useContext(SerwistContext);
12
+ if (!context) {
13
+ throw new Error("[useSerwist]: 'SerwistContext' is not available.");
14
+ }
15
+ return context;
16
+ };
package/src/lib/logger.ts CHANGED
@@ -1,28 +1,39 @@
1
- import chalk from "chalk";
1
+ import { createRequire } from "node:module";
2
+ import { bold, green, red, white, yellow } from "kolorist";
3
+ import semver from "semver";
2
4
 
3
- const LOGGING_METHOD = ["wait", "error", "warn", "info", "event"] as const;
5
+ const require = createRequire(import.meta.url);
4
6
 
5
- type LoggingMethods = (typeof LOGGING_METHOD)[number];
7
+ const LOGGING_SPACE_PREFIX = semver.gte(require("next/package.json").version, "16.0.0") ? "" : " ";
6
8
 
7
- const mapLoggingMethodToConsole: Record<LoggingMethods, "log" | "error" | "warn" | "log"> = {
8
- wait: "log",
9
- error: "error",
10
- warn: "warn",
11
- info: "log",
12
- event: "log",
13
- };
14
-
15
- const prefixes = {
16
- wait: `${chalk.white(chalk.bold("○"))} (serwist)`,
17
- error: `${chalk.red(chalk.bold("X"))} (serwist)`,
18
- warn: `${chalk.yellow(chalk.bold("⚠"))} (serwist)`,
19
- info: `${chalk.white(chalk.bold("○"))} (serwist)`,
20
- event: `${chalk.green(chalk.bold("✓"))} (serwist)`,
21
- };
9
+ export type LoggingMethods = "wait" | "error" | "warn" | "info" | "event";
22
10
 
23
11
  const prefixedLog = (prefixType: LoggingMethods, ...message: any[]) => {
24
- const consoleMethod = mapLoggingMethodToConsole[prefixType];
25
- const prefix = prefixes[prefixType];
12
+ let prefix: string;
13
+ let consoleMethod: keyof Console;
14
+
15
+ switch (prefixType) {
16
+ case "wait":
17
+ prefix = `${white(bold("○"))} (serwist)`;
18
+ consoleMethod = "log";
19
+ break;
20
+ case "error":
21
+ prefix = `${red(bold("X"))} (serwist)`;
22
+ consoleMethod = "error";
23
+ break;
24
+ case "warn":
25
+ prefix = `${yellow(bold("⚠"))} (serwist)`;
26
+ consoleMethod = "warn";
27
+ break;
28
+ case "info":
29
+ prefix = `${white(bold("○"))} (serwist)`;
30
+ consoleMethod = "log";
31
+ break;
32
+ case "event":
33
+ prefix = `${green(bold("✓"))} (serwist)`;
34
+ consoleMethod = "log";
35
+ break;
36
+ }
26
37
 
27
38
  if ((message[0] === "" || message[0] === undefined) && message.length === 1) {
28
39
  message.shift();
@@ -32,26 +43,16 @@ const prefixedLog = (prefixType: LoggingMethods, ...message: any[]) => {
32
43
  if (message.length === 0) {
33
44
  console[consoleMethod]("");
34
45
  } else {
35
- console[consoleMethod](` ${prefix}`, ...message);
46
+ console[consoleMethod](`${LOGGING_SPACE_PREFIX}${prefix}`, ...message);
36
47
  }
37
48
  };
38
49
 
39
- export const wait = (...message: any[]) => {
40
- prefixedLog("wait", ...message);
41
- };
50
+ export const wait = (...message: any[]) => prefixedLog("wait", ...message);
42
51
 
43
- export const error = (...message: any[]) => {
44
- prefixedLog("error", ...message);
45
- };
52
+ export const error = (...message: any[]) => prefixedLog("error", ...message);
46
53
 
47
- export const warn = (...message: any[]) => {
48
- prefixedLog("warn", ...message);
49
- };
54
+ export const warn = (...message: any[]) => prefixedLog("warn", ...message);
50
55
 
51
- export const info = (...message: any[]) => {
52
- prefixedLog("info", ...message);
53
- };
56
+ export const info = (...message: any[]) => prefixedLog("info", ...message);
54
57
 
55
- export const event = (...message: any[]) => {
56
- prefixedLog("event", ...message);
57
- };
58
+ export const event = (...message: any[]) => prefixedLog("event", ...message);