@serwist/turbopack 10.0.0-preview.10
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.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +182 -0
- package/dist/index.react.d.ts +16 -0
- package/dist/index.react.d.ts.map +1 -0
- package/dist/index.react.js +48 -0
- package/dist/index.schema.d.ts +144 -0
- package/dist/index.schema.d.ts.map +1 -0
- package/dist/index.schema.js +26 -0
- package/dist/index.worker.d.ts +14 -0
- package/dist/index.worker.d.ts.map +1 -0
- package/dist/index.worker.js +258 -0
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/context.d.ts +7 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/logger.d.ts +6 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +103 -0
- package/src/index.react.tsx +44 -0
- package/src/index.schema.ts +22 -0
- package/src/index.ts +126 -0
- package/src/index.worker.ts +278 -0
- package/src/lib/constants.ts +4 -0
- package/src/lib/context.ts +16 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/logger.ts +57 -0
- package/src/types.ts +37 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { NetworkOnly, CacheFirst, StaleWhileRevalidate, NetworkFirst, ExpirationPlugin, RangeRequestsPlugin } from 'serwist';
|
|
2
|
+
|
|
3
|
+
const PAGES_CACHE_NAME = {
|
|
4
|
+
rscPrefetch: "pages-rsc-prefetch",
|
|
5
|
+
rsc: "pages-rsc",
|
|
6
|
+
html: "pages"
|
|
7
|
+
};
|
|
8
|
+
const defaultCache = process.env.NODE_ENV !== "production" ? [
|
|
9
|
+
{
|
|
10
|
+
matcher: /.*/i,
|
|
11
|
+
handler: new NetworkOnly()
|
|
12
|
+
}
|
|
13
|
+
] : [
|
|
14
|
+
{
|
|
15
|
+
matcher: /^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,
|
|
16
|
+
handler: new CacheFirst({
|
|
17
|
+
cacheName: "google-fonts-webfonts",
|
|
18
|
+
plugins: [
|
|
19
|
+
new ExpirationPlugin({
|
|
20
|
+
maxEntries: 4,
|
|
21
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
22
|
+
maxAgeFrom: "last-used"
|
|
23
|
+
})
|
|
24
|
+
]
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
matcher: /^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,
|
|
29
|
+
handler: new StaleWhileRevalidate({
|
|
30
|
+
cacheName: "google-fonts-stylesheets",
|
|
31
|
+
plugins: [
|
|
32
|
+
new ExpirationPlugin({
|
|
33
|
+
maxEntries: 4,
|
|
34
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
35
|
+
maxAgeFrom: "last-used"
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
matcher: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
|
|
42
|
+
handler: new StaleWhileRevalidate({
|
|
43
|
+
cacheName: "static-font-assets",
|
|
44
|
+
plugins: [
|
|
45
|
+
new ExpirationPlugin({
|
|
46
|
+
maxEntries: 4,
|
|
47
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
48
|
+
maxAgeFrom: "last-used"
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
matcher: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
|
|
55
|
+
handler: new StaleWhileRevalidate({
|
|
56
|
+
cacheName: "static-image-assets",
|
|
57
|
+
plugins: [
|
|
58
|
+
new ExpirationPlugin({
|
|
59
|
+
maxEntries: 64,
|
|
60
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
61
|
+
maxAgeFrom: "last-used"
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
matcher: /\/_next\/static.+\.js$/i,
|
|
68
|
+
handler: new CacheFirst({
|
|
69
|
+
cacheName: "next-static-js-assets",
|
|
70
|
+
plugins: [
|
|
71
|
+
new ExpirationPlugin({
|
|
72
|
+
maxEntries: 64,
|
|
73
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
74
|
+
maxAgeFrom: "last-used"
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
matcher: /\/_next\/image\?url=.+$/i,
|
|
81
|
+
handler: new StaleWhileRevalidate({
|
|
82
|
+
cacheName: "next-image",
|
|
83
|
+
plugins: [
|
|
84
|
+
new ExpirationPlugin({
|
|
85
|
+
maxEntries: 64,
|
|
86
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
87
|
+
maxAgeFrom: "last-used"
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
matcher: /\.(?:mp3|wav|ogg)$/i,
|
|
94
|
+
handler: new CacheFirst({
|
|
95
|
+
cacheName: "static-audio-assets",
|
|
96
|
+
plugins: [
|
|
97
|
+
new ExpirationPlugin({
|
|
98
|
+
maxEntries: 32,
|
|
99
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
100
|
+
maxAgeFrom: "last-used"
|
|
101
|
+
}),
|
|
102
|
+
new RangeRequestsPlugin()
|
|
103
|
+
]
|
|
104
|
+
})
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
matcher: /\.(?:mp4|webm)$/i,
|
|
108
|
+
handler: new CacheFirst({
|
|
109
|
+
cacheName: "static-video-assets",
|
|
110
|
+
plugins: [
|
|
111
|
+
new ExpirationPlugin({
|
|
112
|
+
maxEntries: 32,
|
|
113
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
114
|
+
maxAgeFrom: "last-used"
|
|
115
|
+
}),
|
|
116
|
+
new RangeRequestsPlugin()
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
matcher: /\.(?:js)$/i,
|
|
122
|
+
handler: new StaleWhileRevalidate({
|
|
123
|
+
cacheName: "static-js-assets",
|
|
124
|
+
plugins: [
|
|
125
|
+
new ExpirationPlugin({
|
|
126
|
+
maxEntries: 48,
|
|
127
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
128
|
+
maxAgeFrom: "last-used"
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
matcher: /\.(?:css|less)$/i,
|
|
135
|
+
handler: new StaleWhileRevalidate({
|
|
136
|
+
cacheName: "static-style-assets",
|
|
137
|
+
plugins: [
|
|
138
|
+
new ExpirationPlugin({
|
|
139
|
+
maxEntries: 32,
|
|
140
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
141
|
+
maxAgeFrom: "last-used"
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
})
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
matcher: /\/_next\/data\/.+\/.+\.json$/i,
|
|
148
|
+
handler: new NetworkFirst({
|
|
149
|
+
cacheName: "next-data",
|
|
150
|
+
plugins: [
|
|
151
|
+
new ExpirationPlugin({
|
|
152
|
+
maxEntries: 32,
|
|
153
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
154
|
+
maxAgeFrom: "last-used"
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
})
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
matcher: /\.(?:json|xml|csv)$/i,
|
|
161
|
+
handler: new NetworkFirst({
|
|
162
|
+
cacheName: "static-data-assets",
|
|
163
|
+
plugins: [
|
|
164
|
+
new ExpirationPlugin({
|
|
165
|
+
maxEntries: 32,
|
|
166
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
167
|
+
maxAgeFrom: "last-used"
|
|
168
|
+
})
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
matcher: ({ sameOrigin, url: { pathname } })=>{
|
|
174
|
+
if (!sameOrigin || pathname.startsWith("/api/auth/callback")) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (pathname.startsWith("/api/")) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
},
|
|
182
|
+
method: "GET",
|
|
183
|
+
handler: new NetworkFirst({
|
|
184
|
+
cacheName: "apis",
|
|
185
|
+
plugins: [
|
|
186
|
+
new ExpirationPlugin({
|
|
187
|
+
maxEntries: 16,
|
|
188
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
189
|
+
maxAgeFrom: "last-used"
|
|
190
|
+
})
|
|
191
|
+
],
|
|
192
|
+
networkTimeoutSeconds: 10
|
|
193
|
+
})
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
matcher: ({ request, url: { pathname }, sameOrigin })=>request.headers.get("RSC") === "1" && request.headers.get("Next-Router-Prefetch") === "1" && sameOrigin && !pathname.startsWith("/api/"),
|
|
197
|
+
handler: new NetworkFirst({
|
|
198
|
+
cacheName: PAGES_CACHE_NAME.rscPrefetch,
|
|
199
|
+
plugins: [
|
|
200
|
+
new ExpirationPlugin({
|
|
201
|
+
maxEntries: 32,
|
|
202
|
+
maxAgeSeconds: 24 * 60 * 60
|
|
203
|
+
})
|
|
204
|
+
]
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
matcher: ({ request, url: { pathname }, sameOrigin })=>request.headers.get("RSC") === "1" && sameOrigin && !pathname.startsWith("/api/"),
|
|
209
|
+
handler: new NetworkFirst({
|
|
210
|
+
cacheName: PAGES_CACHE_NAME.rsc,
|
|
211
|
+
plugins: [
|
|
212
|
+
new ExpirationPlugin({
|
|
213
|
+
maxEntries: 32,
|
|
214
|
+
maxAgeSeconds: 24 * 60 * 60
|
|
215
|
+
})
|
|
216
|
+
]
|
|
217
|
+
})
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
matcher: ({ request, url: { pathname }, sameOrigin })=>request.headers.get("Content-Type")?.includes("text/html") && sameOrigin && !pathname.startsWith("/api/"),
|
|
221
|
+
handler: new NetworkFirst({
|
|
222
|
+
cacheName: PAGES_CACHE_NAME.html,
|
|
223
|
+
plugins: [
|
|
224
|
+
new ExpirationPlugin({
|
|
225
|
+
maxEntries: 32,
|
|
226
|
+
maxAgeSeconds: 24 * 60 * 60
|
|
227
|
+
})
|
|
228
|
+
]
|
|
229
|
+
})
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
matcher: ({ url: { pathname }, sameOrigin })=>sameOrigin && !pathname.startsWith("/api/"),
|
|
233
|
+
handler: new NetworkFirst({
|
|
234
|
+
cacheName: "others",
|
|
235
|
+
plugins: [
|
|
236
|
+
new ExpirationPlugin({
|
|
237
|
+
maxEntries: 32,
|
|
238
|
+
maxAgeSeconds: 24 * 60 * 60
|
|
239
|
+
})
|
|
240
|
+
]
|
|
241
|
+
})
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
matcher: ({ sameOrigin })=>!sameOrigin,
|
|
245
|
+
handler: new NetworkFirst({
|
|
246
|
+
cacheName: "cross-origin",
|
|
247
|
+
plugins: [
|
|
248
|
+
new ExpirationPlugin({
|
|
249
|
+
maxEntries: 32,
|
|
250
|
+
maxAgeSeconds: 60 * 60
|
|
251
|
+
})
|
|
252
|
+
],
|
|
253
|
+
networkTimeoutSeconds: 10
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
export { PAGES_CACHE_NAME, defaultCache };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,UAGjC,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const wait: (...message: any[]) => void;
|
|
2
|
+
export declare const error: (...message: any[]) => void;
|
|
3
|
+
export declare const warn: (...message: any[]) => void;
|
|
4
|
+
export declare const info: (...message: any[]) => void;
|
|
5
|
+
export declare const event: (...message: any[]) => void;
|
|
6
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BasePartial, BaseResolved, GlobPartial, GlobResolved, InjectPartial, InjectResolved, OptionalGlobDirectoryPartial, RequiredGlobDirectoryResolved } from "@serwist/build";
|
|
2
|
+
import type { Prettify } from "@serwist/utils";
|
|
3
|
+
export interface TurboPartial {
|
|
4
|
+
/**
|
|
5
|
+
* The path to your working directory.
|
|
6
|
+
*
|
|
7
|
+
* @default process.cwd()
|
|
8
|
+
*/
|
|
9
|
+
cwd?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The Next.js `basePath` config option. If this option
|
|
12
|
+
* is not configured, set to `/`.
|
|
13
|
+
*/
|
|
14
|
+
basePath: string;
|
|
15
|
+
}
|
|
16
|
+
export type TurboResolved = Required<TurboPartial>;
|
|
17
|
+
export type InjectManifestOptions = Prettify<BasePartial & GlobPartial & InjectPartial & OptionalGlobDirectoryPartial & TurboPartial>;
|
|
18
|
+
export type InjectManifestOptionsComplete = Prettify<BaseResolved & GlobResolved & InjectResolved & RequiredGlobDirectoryResolved & TurboResolved>;
|
|
19
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,4BAA4B,GAAG,YAAY,CAAC,CAAC;AAEtI,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC,YAAY,GAAG,YAAY,GAAG,cAAc,GAAG,6BAA6B,GAAG,aAAa,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@serwist/turbopack",
|
|
3
|
+
"version": "10.0.0-preview.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "A module that integrates Serwist into your Next.js / Turbopack application.",
|
|
7
|
+
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"serwist",
|
|
13
|
+
"serwistjs",
|
|
14
|
+
"sw",
|
|
15
|
+
"service worker",
|
|
16
|
+
"progressive web apps",
|
|
17
|
+
"web",
|
|
18
|
+
"service-worker",
|
|
19
|
+
"progressive-web-apps",
|
|
20
|
+
"next",
|
|
21
|
+
"next.js",
|
|
22
|
+
"turbo",
|
|
23
|
+
"turbopack",
|
|
24
|
+
"pwa"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.0.0"
|
|
28
|
+
},
|
|
29
|
+
"author": "Serwist <ducanh2912.rusty@gmail.com> (https://serwist.pages.dev/)",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": "https://github.com/serwist/serwist",
|
|
32
|
+
"bugs": "https://github.com/serwist/serwist/issues",
|
|
33
|
+
"homepage": "https://serwist.pages.dev",
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"typesVersions": {
|
|
37
|
+
"*": {
|
|
38
|
+
"worker": [
|
|
39
|
+
"./dist/index.worker.d.ts"
|
|
40
|
+
],
|
|
41
|
+
"schema": [
|
|
42
|
+
"./dist/index.schema.d.ts"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"types": "./dist/index.d.ts",
|
|
49
|
+
"default": "./dist/index.js"
|
|
50
|
+
},
|
|
51
|
+
"./react": {
|
|
52
|
+
"types": "./dist/index.react.d.ts",
|
|
53
|
+
"default": "./dist/index.react.js"
|
|
54
|
+
},
|
|
55
|
+
"./worker": {
|
|
56
|
+
"types": "./dist/index.worker.d.ts",
|
|
57
|
+
"default": "./dist/index.worker.js"
|
|
58
|
+
},
|
|
59
|
+
"./schema": {
|
|
60
|
+
"types": "./dist/index.schema.d.ts",
|
|
61
|
+
"default": "./dist/index.schema.js"
|
|
62
|
+
},
|
|
63
|
+
"./package.json": "./package.json"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@swc/core": "1.12.14",
|
|
67
|
+
"esbuild-wasm": "0.25.8",
|
|
68
|
+
"kolorist": "1.8.0",
|
|
69
|
+
"zod": "4.0.5",
|
|
70
|
+
"@serwist/build": "10.0.0-preview.10",
|
|
71
|
+
"@serwist/window": "10.0.0-preview.10",
|
|
72
|
+
"serwist": "10.0.0-preview.10"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/node": "24.0.14",
|
|
76
|
+
"@types/react": "19.1.8",
|
|
77
|
+
"next": "15.4.1",
|
|
78
|
+
"react": "19.1.0",
|
|
79
|
+
"react-dom": "19.1.0",
|
|
80
|
+
"rollup": "4.45.1",
|
|
81
|
+
"type-fest": "4.41.0",
|
|
82
|
+
"typescript": "5.8.3",
|
|
83
|
+
"@serwist/configs": "10.0.0-preview.10",
|
|
84
|
+
"@serwist/utils": "10.0.0-preview.10"
|
|
85
|
+
},
|
|
86
|
+
"peerDependencies": {
|
|
87
|
+
"next": ">=14.0.0",
|
|
88
|
+
"react": ">=18.0.0",
|
|
89
|
+
"typescript": ">=5.0.0"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"typescript": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "rimraf dist && NODE_ENV=production rollup --config rollup.config.js",
|
|
98
|
+
"dev": "rollup --config rollup.config.js --watch",
|
|
99
|
+
"lint": "biome lint ./src",
|
|
100
|
+
"qcheck": "biome check ./src",
|
|
101
|
+
"typecheck": "tsc"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Serwist } from "@serwist/window";
|
|
2
|
+
import { isCurrentPageOutOfScope } from "@serwist/window/internal";
|
|
3
|
+
import { type ReactNode, useEffect, useState } from "react";
|
|
4
|
+
import { SerwistContext } from "./lib/context.js";
|
|
5
|
+
|
|
6
|
+
export interface SerwistProviderProps {
|
|
7
|
+
swUrl: string;
|
|
8
|
+
register?: boolean;
|
|
9
|
+
reloadOnOnline?: boolean;
|
|
10
|
+
options?: RegistrationOptions;
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
serwist: Serwist;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function SerwistProvider({ swUrl, register = true, reloadOnOnline = true, options, children }: SerwistProviderProps) {
|
|
21
|
+
const [serwist] = useState(() => {
|
|
22
|
+
if (typeof window === "undefined") return null;
|
|
23
|
+
if (!(window.serwist && window.serwist instanceof Serwist) && "serviceWorker" in navigator) {
|
|
24
|
+
window.serwist = new Serwist(swUrl, { ...options, scope: options?.scope || "/" });
|
|
25
|
+
}
|
|
26
|
+
return window.serwist ?? null;
|
|
27
|
+
});
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const scope = options?.scope || "/";
|
|
30
|
+
if (register && !isCurrentPageOutOfScope(scope)) {
|
|
31
|
+
void serwist?.register();
|
|
32
|
+
}
|
|
33
|
+
}, [register, options?.scope, serwist?.register]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const reload = () => location.reload();
|
|
36
|
+
if (reloadOnOnline) {
|
|
37
|
+
window.addEventListener("online", reload);
|
|
38
|
+
}
|
|
39
|
+
return () => {
|
|
40
|
+
window.removeEventListener("online", reload);
|
|
41
|
+
};
|
|
42
|
+
}, [reloadOnOnline]);
|
|
43
|
+
return <SerwistContext.Provider value={{ serwist }}>{children}</SerwistContext.Provider>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { basePartial, globPartial, injectPartial } from "@serwist/build/schema";
|
|
3
|
+
import z from "zod";
|
|
4
|
+
import { DEFAULT_GLOB_PATTERNS } from "./lib/constants.js";
|
|
5
|
+
|
|
6
|
+
export const injectManifestOptions = z
|
|
7
|
+
.strictObject({
|
|
8
|
+
...basePartial.shape,
|
|
9
|
+
...globPartial.shape,
|
|
10
|
+
...injectPartial.shape,
|
|
11
|
+
cwd: z.string().prefault(process.cwd()),
|
|
12
|
+
basePath: z.string(),
|
|
13
|
+
globPatterns: z.array(z.string()).prefault(DEFAULT_GLOB_PATTERNS),
|
|
14
|
+
globDirectory: z.string().optional(),
|
|
15
|
+
})
|
|
16
|
+
.transform((input) => {
|
|
17
|
+
return {
|
|
18
|
+
...input,
|
|
19
|
+
swSrc: path.isAbsolute(input.swSrc) ? input.swSrc : path.join(input.cwd, input.swSrc),
|
|
20
|
+
globDirectory: input.globDirectory ?? input.cwd,
|
|
21
|
+
};
|
|
22
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { type BuildResult, getFileManifestEntries, rebasePath } from "@serwist/build";
|
|
3
|
+
import { SerwistConfigError, validationErrorMap } from "@serwist/build/schema";
|
|
4
|
+
import { cyan, dim, yellow } from "kolorist";
|
|
5
|
+
import { NextResponse } from "next/server.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { injectManifestOptions } from "./index.schema.js";
|
|
8
|
+
import { logger } from "./lib/index.js";
|
|
9
|
+
import type { InjectManifestOptions, InjectManifestOptionsComplete } from "./types.js";
|
|
10
|
+
|
|
11
|
+
// TODO(workarond): `esbuild` doesn't load when in Turbopack production
|
|
12
|
+
const esbuild = import("esbuild-wasm");
|
|
13
|
+
|
|
14
|
+
const logSerwistResult = (filePath: string, buildResult: Pick<BuildResult, "count" | "size" | "warnings">) => {
|
|
15
|
+
const { count, size, warnings } = buildResult;
|
|
16
|
+
const hasWarnings = warnings && warnings.length > 0;
|
|
17
|
+
// The route is reinitiated for each `path` param, so we only log results
|
|
18
|
+
// if we're prerendering for sw.js.
|
|
19
|
+
if (filePath === "sw.js") {
|
|
20
|
+
logger[hasWarnings ? "warn" : "event"](
|
|
21
|
+
`${cyan(count)} precache entries ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}${
|
|
22
|
+
hasWarnings ? `\n${yellow(["⚠ warnings", ...warnings.map((w) => ` ${w}`), ""].join("\n"))}` : ""
|
|
23
|
+
}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const validateGetManifestOptions = async (input: unknown): Promise<InjectManifestOptionsComplete> => {
|
|
29
|
+
const result = await injectManifestOptions.spa(input, {
|
|
30
|
+
error: validationErrorMap,
|
|
31
|
+
});
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
throw new SerwistConfigError({
|
|
34
|
+
moduleName: "@serwist/turbopack",
|
|
35
|
+
message: z.prettifyError(result.error),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return result.data;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
42
|
+
|
|
43
|
+
const contentTypeMap: Record<string, string> = {
|
|
44
|
+
".js": "application/javascript",
|
|
45
|
+
".map": "application/json; charset=UTF-8",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a Route Handler that returns the precache manifest.
|
|
50
|
+
* @param options Options for {@linkcode getFileManifestEntries}.
|
|
51
|
+
*/
|
|
52
|
+
export const createSerwistRoute = (options: InjectManifestOptions) => {
|
|
53
|
+
const dynamic = "force-static",
|
|
54
|
+
dynamicParams = false,
|
|
55
|
+
revalidate = false;
|
|
56
|
+
const validation = validateGetManifestOptions(options).then((config) => {
|
|
57
|
+
// Make sure we leave swSrc out of the precache manifest.
|
|
58
|
+
config.globIgnores.push(
|
|
59
|
+
rebasePath({
|
|
60
|
+
file: config.swSrc,
|
|
61
|
+
baseDirectory: config.globDirectory,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
if (!config.manifestTransforms) {
|
|
65
|
+
config.manifestTransforms = [];
|
|
66
|
+
}
|
|
67
|
+
config.manifestTransforms.push((manifestEntries) => {
|
|
68
|
+
const manifest = manifestEntries.map((m) => {
|
|
69
|
+
// Replace all references to .next/ with "/_next/".
|
|
70
|
+
if (m.url.startsWith(".next/")) m.url = `/_next/${m.url.slice(6)}`;
|
|
71
|
+
// Replace all references to public/ with "$(basePath)/".
|
|
72
|
+
if (m.url.startsWith("public/")) m.url = path.posix.join(config.basePath, m.url.slice(7));
|
|
73
|
+
return m;
|
|
74
|
+
});
|
|
75
|
+
return { manifest, warnings: [] };
|
|
76
|
+
});
|
|
77
|
+
return config;
|
|
78
|
+
});
|
|
79
|
+
const generateStaticParams = () => {
|
|
80
|
+
return [{ path: "sw.js" }, { path: "sw.js.map" }];
|
|
81
|
+
};
|
|
82
|
+
let map: Map<string, string> | null = null;
|
|
83
|
+
const loadMap = async (filePath: string) => {
|
|
84
|
+
const config = await validation;
|
|
85
|
+
const { count, size, manifestEntries, warnings } = await getFileManifestEntries(config);
|
|
86
|
+
// See https://github.com/GoogleChrome/workbox/issues/2230
|
|
87
|
+
const injectionPoint = config.injectionPoint ? config.injectionPoint : "";
|
|
88
|
+
const manifestString = manifestEntries === undefined ? "undefined" : JSON.stringify(manifestEntries, null, 2);
|
|
89
|
+
logSerwistResult(filePath, { count, size, warnings });
|
|
90
|
+
const result = await (await esbuild).build({
|
|
91
|
+
entryPoints: [{ in: config.swSrc, out: "sw" }],
|
|
92
|
+
format: "esm",
|
|
93
|
+
sourcemap: true,
|
|
94
|
+
bundle: true,
|
|
95
|
+
write: false,
|
|
96
|
+
minify: !isDev,
|
|
97
|
+
define: injectionPoint
|
|
98
|
+
? {
|
|
99
|
+
[injectionPoint]: manifestString,
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
outdir: process.cwd(),
|
|
103
|
+
entryNames: "[name]",
|
|
104
|
+
});
|
|
105
|
+
if (result.errors.length) {
|
|
106
|
+
console.error("Failed to build the service worker.", result.errors);
|
|
107
|
+
throw new Error();
|
|
108
|
+
}
|
|
109
|
+
if (result.warnings.length) {
|
|
110
|
+
console.warn(result.warnings);
|
|
111
|
+
}
|
|
112
|
+
return new Map(result.outputFiles.map((e) => [e.path, e.text]));
|
|
113
|
+
};
|
|
114
|
+
const GET = async (_: Request, { params }: { params: Promise<{ path: string }> }) => {
|
|
115
|
+
// TODO: obviously, files get stale in development when we pull this off.
|
|
116
|
+
const { path: filePath } = await params;
|
|
117
|
+
if (!map) map = await loadMap(filePath);
|
|
118
|
+
return new NextResponse(map.get(path.join(process.cwd(), filePath)), {
|
|
119
|
+
headers: {
|
|
120
|
+
"Content-Type": contentTypeMap[path.extname(filePath)] || "text/plain",
|
|
121
|
+
"Service-Worker-Allowed": "/",
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
return { dynamic, dynamicParams, revalidate, generateStaticParams, GET };
|
|
126
|
+
};
|