@serwist/vite 8.4.4 → 9.0.0-preview.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.
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/assets.d.ts +1 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/context.d.ts +1 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -12
- package/dist/index.worker.d.ts +2 -1
- package/dist/index.worker.d.ts.map +1 -0
- package/dist/integration/svelte/build.d.ts +1 -0
- package/dist/integration/svelte/build.d.ts.map +1 -0
- package/dist/integration/svelte/config.d.ts +1 -0
- package/dist/integration/svelte/config.d.ts.map +1 -0
- package/dist/integration/svelte/index.d.ts +1 -0
- package/dist/integration/svelte/index.d.ts.map +1 -0
- package/dist/integration/svelte/index.js +1 -19
- package/dist/integration/svelte/types.d.ts +1 -0
- package/dist/integration/svelte/types.d.ts.map +1 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/main.js +15 -87
- package/dist/modules.d.ts +1 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/options.d.ts +1 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/plugins/build.d.ts +1 -0
- package/dist/plugins/build.d.ts.map +1 -0
- package/dist/plugins/dev.d.ts +1 -0
- package/dist/plugins/dev.d.ts.map +1 -0
- package/dist/plugins/main.d.ts +1 -0
- package/dist/plugins/main.d.ts.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils-types.d.ts +1 -0
- package/dist/utils-types.d.ts.map +1 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.d.ts.map +1 -0
- package/package.json +38 -34
- package/src/api.ts +38 -0
- package/src/assets.ts +76 -0
- package/src/constants.ts +2 -0
- package/src/context.ts +45 -0
- package/src/index.browser.ts +8 -0
- package/src/index.ts +25 -0
- package/src/index.worker.ts +95 -0
- package/src/integration/svelte/build.ts +21 -0
- package/src/integration/svelte/config.ts +141 -0
- package/src/integration/svelte/index.ts +27 -0
- package/src/integration/svelte/types.ts +26 -0
- package/src/log.ts +25 -0
- package/src/modules.ts +174 -0
- package/src/options.ts +90 -0
- package/src/plugins/build.ts +31 -0
- package/src/plugins/dev.ts +61 -0
- package/src/plugins/main.ts +49 -0
- package/src/rollup.js +46 -0
- package/src/types.ts +207 -0
- package/src/utils-types.ts +1 -0
- package/src/utils.ts +69 -0
- package/src/virtual.d.ts +5 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serwist/vite",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0-preview.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A module that integrates Serwist into your Vite application.",
|
|
6
6
|
"files": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"src",
|
|
8
|
+
"dist"
|
|
9
9
|
],
|
|
10
10
|
"keywords": [
|
|
11
11
|
"react",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"web",
|
|
26
26
|
"service-worker"
|
|
27
27
|
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
28
31
|
"author": "antfu <anthonyfu117@hotmail.com>, Serwist's Team",
|
|
29
32
|
"license": "MIT",
|
|
30
33
|
"repository": "serwist/serwist",
|
|
@@ -48,28 +51,20 @@
|
|
|
48
51
|
},
|
|
49
52
|
"exports": {
|
|
50
53
|
".": {
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"default": "./dist/index.js"
|
|
54
|
-
}
|
|
54
|
+
"types": "./dist/index.d.ts",
|
|
55
|
+
"default": "./dist/index.js"
|
|
55
56
|
},
|
|
56
57
|
"./browser": {
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
"default": "./dist/index.browser.js"
|
|
60
|
-
}
|
|
58
|
+
"types": "./dist/index.browser.d.ts",
|
|
59
|
+
"default": "./dist/index.browser.js"
|
|
61
60
|
},
|
|
62
61
|
"./worker": {
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
"default": "./dist/index.worker.js"
|
|
66
|
-
}
|
|
62
|
+
"types": "./dist/index.worker.d.ts",
|
|
63
|
+
"default": "./dist/index.worker.js"
|
|
67
64
|
},
|
|
68
65
|
"./integration-*": {
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
"default": "./dist/integration/*/index.js"
|
|
72
|
-
}
|
|
66
|
+
"types": "./dist/integration/*/index.d.ts",
|
|
67
|
+
"default": "./dist/integration/*/index.js"
|
|
73
68
|
},
|
|
74
69
|
"./package.json": "./package.json"
|
|
75
70
|
},
|
|
@@ -77,29 +72,30 @@
|
|
|
77
72
|
"debug": "4.3.4",
|
|
78
73
|
"fast-glob": "3.3.2",
|
|
79
74
|
"pretty-bytes": "6.1.1",
|
|
80
|
-
"@serwist/build": "
|
|
81
|
-
"@serwist/window": "
|
|
75
|
+
"@serwist/build": "9.0.0-preview.1",
|
|
76
|
+
"@serwist/window": "9.0.0-preview.1"
|
|
82
77
|
},
|
|
83
78
|
"devDependencies": {
|
|
84
|
-
"@playwright/test": "1.
|
|
85
|
-
"@sveltejs/kit": "2.0
|
|
79
|
+
"@playwright/test": "1.41.2",
|
|
80
|
+
"@sveltejs/kit": "2.5.0",
|
|
86
81
|
"@types/debug": "4.1.12",
|
|
87
|
-
"@types/node": "20.
|
|
82
|
+
"@types/node": "20.11.16",
|
|
88
83
|
"@types/prompts": "2.4.9",
|
|
89
|
-
"@types/react": "18.2.
|
|
90
|
-
"bumpp": "9.
|
|
84
|
+
"@types/react": "18.2.52",
|
|
85
|
+
"bumpp": "9.3.0",
|
|
91
86
|
"kolorist": "1.8.0",
|
|
92
87
|
"preact": "10.19.3",
|
|
93
88
|
"prompts": "2.4.2",
|
|
94
89
|
"publint": "0.2.7",
|
|
95
90
|
"react": "18.2.0",
|
|
96
|
-
"rollup": "4.9.
|
|
97
|
-
"solid-js": "1.8.
|
|
98
|
-
"svelte": "5.0.0-next.
|
|
99
|
-
"typescript": "5.4.0-dev.
|
|
100
|
-
"vite": "5.0.
|
|
101
|
-
"vue": "3.
|
|
102
|
-
"@serwist/constants": "
|
|
91
|
+
"rollup": "4.9.6",
|
|
92
|
+
"solid-js": "1.8.12",
|
|
93
|
+
"svelte": "5.0.0-next.45",
|
|
94
|
+
"typescript": "5.4.0-dev.20240203",
|
|
95
|
+
"vite": "5.0.12",
|
|
96
|
+
"vue": "3.4.15",
|
|
97
|
+
"@serwist/constants": "9.0.0-preview.1",
|
|
98
|
+
"@serwist/sw": "9.0.0-preview.1"
|
|
103
99
|
},
|
|
104
100
|
"peerDependencies": {
|
|
105
101
|
"@sveltejs/kit": "^1.0.0 || ^2.0.0",
|
|
@@ -107,10 +103,15 @@
|
|
|
107
103
|
"react": "^18.0.0",
|
|
108
104
|
"solid-js": "^1.8.7",
|
|
109
105
|
"svelte": "^4.0.0 || ^5.0.0",
|
|
106
|
+
"typescript": ">=5.0.0",
|
|
110
107
|
"vite": "^5.0.0",
|
|
111
|
-
"vue": "^3.0.0"
|
|
108
|
+
"vue": "^3.0.0",
|
|
109
|
+
"@serwist/sw": "9.0.0-preview.1"
|
|
112
110
|
},
|
|
113
111
|
"peerDependenciesMeta": {
|
|
112
|
+
"@serwist/sw": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
114
115
|
"@sveltejs/kit": {
|
|
115
116
|
"optional": true
|
|
116
117
|
},
|
|
@@ -126,6 +127,9 @@
|
|
|
126
127
|
"svelte": {
|
|
127
128
|
"optional": true
|
|
128
129
|
},
|
|
130
|
+
"typescript": {
|
|
131
|
+
"optional": true
|
|
132
|
+
},
|
|
129
133
|
"vue": {
|
|
130
134
|
"optional": true
|
|
131
135
|
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { yellow } from "kolorist";
|
|
2
|
+
|
|
3
|
+
import type { SerwistViteContext } from "./context.js";
|
|
4
|
+
import { logSerwistResult } from "./log.js";
|
|
5
|
+
import { generateServiceWorker } from "./modules.js";
|
|
6
|
+
import type { ExtendManifestEntriesHook, SerwistViteApi } from "./types.js";
|
|
7
|
+
|
|
8
|
+
export const createApi = (ctx: SerwistViteContext): SerwistViteApi => {
|
|
9
|
+
return {
|
|
10
|
+
get disabled() {
|
|
11
|
+
return ctx?.options?.disable;
|
|
12
|
+
},
|
|
13
|
+
async generateSW() {
|
|
14
|
+
if (ctx.options.disable) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const buildResult = await generateServiceWorker(ctx);
|
|
18
|
+
if (buildResult) {
|
|
19
|
+
if (ctx.viteConfig.isProduction) {
|
|
20
|
+
// Log Serwist result
|
|
21
|
+
logSerwistResult(buildResult, ctx.viteConfig);
|
|
22
|
+
} else if (buildResult.warnings && buildResult.warnings.length > 0) {
|
|
23
|
+
console.warn(yellow(["[@serwist/vite] Warnings", ...buildResult.warnings.map((w) => ` - ${w}`), ""].join("\n")));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
extendManifestEntries(fn: ExtendManifestEntriesHook) {
|
|
28
|
+
const { options } = ctx;
|
|
29
|
+
if (options.disable) return;
|
|
30
|
+
|
|
31
|
+
const result = fn(options.injectManifest.additionalPrecacheEntries || []);
|
|
32
|
+
|
|
33
|
+
if (result != null) {
|
|
34
|
+
options.injectManifest.additionalPrecacheEntries = result;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
package/src/assets.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { resolve as resolveFs } from "node:path";
|
|
4
|
+
|
|
5
|
+
import type { InjectManifestOptions, ManifestEntry } from "@serwist/build";
|
|
6
|
+
import fg from "fast-glob";
|
|
7
|
+
import type { ResolvedConfig } from "vite";
|
|
8
|
+
|
|
9
|
+
import type { ResolvedPluginOptions } from "./types.js";
|
|
10
|
+
|
|
11
|
+
const buildManifestEntry = (publicDir: string, url: string): Promise<ManifestEntry> => {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const cHash = crypto.createHash("MD5");
|
|
14
|
+
const stream = fs.createReadStream(resolveFs(publicDir, url));
|
|
15
|
+
stream.on("error", (err) => {
|
|
16
|
+
reject(err);
|
|
17
|
+
});
|
|
18
|
+
stream.on("data", (chunk) => {
|
|
19
|
+
cHash.update(chunk);
|
|
20
|
+
});
|
|
21
|
+
stream.on("end", () => {
|
|
22
|
+
return resolve({
|
|
23
|
+
url,
|
|
24
|
+
revision: `${cHash.digest("hex")}`,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const lookupAdditionalPrecacheEntries = (serwistOptions: Partial<InjectManifestOptions>): (string | ManifestEntry)[] => {
|
|
31
|
+
return serwistOptions.additionalPrecacheEntries || [];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// we need to make icons relative, we can have for example icon entries with: /pwa.png
|
|
35
|
+
// fast-glob will not resolve absolute paths
|
|
36
|
+
const normalizeIconPath = (path: string) => {
|
|
37
|
+
return path.startsWith("/") ? path.substring(1) : path;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const configureStaticAssets = async (resolvedPluginOptions: ResolvedPluginOptions, viteConfig: ResolvedConfig) => {
|
|
41
|
+
const { injectManifest, includeAssets } = resolvedPluginOptions;
|
|
42
|
+
|
|
43
|
+
const { publicDir } = viteConfig;
|
|
44
|
+
const globs: string[] = [];
|
|
45
|
+
const manifestEntries: (string | ManifestEntry)[] = lookupAdditionalPrecacheEntries(injectManifest);
|
|
46
|
+
if (includeAssets) {
|
|
47
|
+
// we need to make icons relative, we can have for example icon entries with: /pwa.png
|
|
48
|
+
// fast-glob will not resolve absolute paths
|
|
49
|
+
if (Array.isArray(includeAssets)) globs.push(...includeAssets.map(normalizeIconPath));
|
|
50
|
+
else globs.push(normalizeIconPath(includeAssets));
|
|
51
|
+
}
|
|
52
|
+
if (globs.length > 0) {
|
|
53
|
+
let assets = await fg(globs, {
|
|
54
|
+
cwd: publicDir,
|
|
55
|
+
onlyFiles: true,
|
|
56
|
+
unique: true,
|
|
57
|
+
});
|
|
58
|
+
// we also need to remove from the list existing included by the user
|
|
59
|
+
if (manifestEntries.length > 0) {
|
|
60
|
+
const included = manifestEntries.map((me) => {
|
|
61
|
+
if (typeof me === "string") return me;
|
|
62
|
+
return me.url;
|
|
63
|
+
});
|
|
64
|
+
assets = assets.filter((a) => !included.includes(a));
|
|
65
|
+
}
|
|
66
|
+
const assetsEntries = await Promise.all(
|
|
67
|
+
assets.map((a) => {
|
|
68
|
+
return buildManifestEntry(publicDir, a);
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
manifestEntries.push(...assetsEntries);
|
|
72
|
+
}
|
|
73
|
+
if (manifestEntries.length > 0) {
|
|
74
|
+
injectManifest.additionalPrecacheEntries = manifestEntries;
|
|
75
|
+
}
|
|
76
|
+
};
|
package/src/constants.ts
ADDED
package/src/context.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "vite";
|
|
2
|
+
|
|
3
|
+
import type { PluginOptions, ResolvedPluginOptions } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export type SerwistViteFrameworks = "nuxt" | "sveltekit";
|
|
6
|
+
|
|
7
|
+
export interface SerwistViteContext {
|
|
8
|
+
/**
|
|
9
|
+
* Resolved Vite config.
|
|
10
|
+
*
|
|
11
|
+
* Note: This value is set by our main plugin, located at plugins/main.ts.
|
|
12
|
+
*/
|
|
13
|
+
viteConfig: ResolvedConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Provided options.
|
|
16
|
+
*/
|
|
17
|
+
userOptions: PluginOptions;
|
|
18
|
+
/**
|
|
19
|
+
* Resolved options.
|
|
20
|
+
*
|
|
21
|
+
* Note: this is different from `userOptions` in that it has been parsed, whereas
|
|
22
|
+
* `userOptions` is the raw configuration that the user provides us.
|
|
23
|
+
*/
|
|
24
|
+
options: ResolvedPluginOptions;
|
|
25
|
+
useImportRegister: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Is the plugin running on dev?
|
|
28
|
+
*
|
|
29
|
+
* Note: This value is set by our dev plugin, located at plugins/dev.ts.
|
|
30
|
+
*/
|
|
31
|
+
devEnvironment: boolean;
|
|
32
|
+
/** To tailor our APIs to these frameworks. */
|
|
33
|
+
framework: SerwistViteFrameworks | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const createContext = (userOptions: PluginOptions, framework: SerwistViteFrameworks | undefined): SerwistViteContext => {
|
|
37
|
+
return {
|
|
38
|
+
userOptions,
|
|
39
|
+
options: undefined!,
|
|
40
|
+
viteConfig: undefined!,
|
|
41
|
+
useImportRegister: false,
|
|
42
|
+
devEnvironment: false,
|
|
43
|
+
framework,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { swScope, swType, swUrl } from "virtual:internal-serwist";
|
|
2
|
+
|
|
3
|
+
export const getSerwist = async () => {
|
|
4
|
+
if ("serviceWorker" in navigator) {
|
|
5
|
+
return new (await import("@serwist/window")).Serwist(swUrl, { scope: swScope, type: swType });
|
|
6
|
+
}
|
|
7
|
+
return undefined;
|
|
8
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
import { createApi } from "./api.js";
|
|
4
|
+
import { createContext } from "./context.js";
|
|
5
|
+
import { buildPlugin } from "./plugins/build.js";
|
|
6
|
+
import { devPlugin } from "./plugins/dev.js";
|
|
7
|
+
import { mainPlugin } from "./plugins/main.js";
|
|
8
|
+
import type { PluginOptions } from "./types.js";
|
|
9
|
+
import { resolveEntry, toFs } from "./utils.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Integrates Serwist into your Vite app.
|
|
13
|
+
* @param userOptions
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export const serwist = (userOptions: PluginOptions): Plugin[] => {
|
|
17
|
+
const ctx = createContext(userOptions, undefined);
|
|
18
|
+
const api = createApi(ctx);
|
|
19
|
+
return [mainPlugin(ctx, api), buildPlugin(ctx, api), devPlugin(ctx, api)];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// This allows for customization.
|
|
23
|
+
export { buildPlugin as build, createApi, createContext, devPlugin as dev, mainPlugin as main, resolveEntry, toFs };
|
|
24
|
+
export type { SerwistViteContext } from "./context.js";
|
|
25
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { RuntimeCaching } from "@serwist/sw";
|
|
2
|
+
|
|
3
|
+
export const defaultCache: RuntimeCaching[] = [
|
|
4
|
+
{
|
|
5
|
+
urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
|
|
6
|
+
handler: "CacheFirst",
|
|
7
|
+
options: {
|
|
8
|
+
cacheName: "google-fonts",
|
|
9
|
+
expiration: {
|
|
10
|
+
maxEntries: 4,
|
|
11
|
+
maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
|
|
17
|
+
handler: "StaleWhileRevalidate",
|
|
18
|
+
options: {
|
|
19
|
+
cacheName: "static-font-assets",
|
|
20
|
+
expiration: {
|
|
21
|
+
maxEntries: 4,
|
|
22
|
+
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
|
|
28
|
+
handler: "StaleWhileRevalidate",
|
|
29
|
+
options: {
|
|
30
|
+
cacheName: "static-image-assets",
|
|
31
|
+
expiration: {
|
|
32
|
+
maxEntries: 64,
|
|
33
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
urlPattern: /\.(?:js)$/i,
|
|
39
|
+
handler: "StaleWhileRevalidate",
|
|
40
|
+
options: {
|
|
41
|
+
cacheName: "static-js-assets",
|
|
42
|
+
expiration: {
|
|
43
|
+
maxEntries: 32,
|
|
44
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
urlPattern: /\.(?:css|less)$/i,
|
|
50
|
+
handler: "StaleWhileRevalidate",
|
|
51
|
+
options: {
|
|
52
|
+
cacheName: "static-style-assets",
|
|
53
|
+
expiration: {
|
|
54
|
+
maxEntries: 32,
|
|
55
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
urlPattern: /\.(?:json|xml|csv)$/i,
|
|
61
|
+
handler: "NetworkFirst",
|
|
62
|
+
options: {
|
|
63
|
+
cacheName: "static-data-assets",
|
|
64
|
+
expiration: {
|
|
65
|
+
maxEntries: 32,
|
|
66
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
urlPattern: /\/api\/.*$/i,
|
|
72
|
+
handler: "NetworkFirst",
|
|
73
|
+
method: "GET",
|
|
74
|
+
options: {
|
|
75
|
+
cacheName: "apis",
|
|
76
|
+
expiration: {
|
|
77
|
+
maxEntries: 16,
|
|
78
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
79
|
+
},
|
|
80
|
+
networkTimeoutSeconds: 10, // fallback to cache if API does not response within 10 seconds
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
urlPattern: /.*/i,
|
|
85
|
+
handler: "NetworkFirst",
|
|
86
|
+
options: {
|
|
87
|
+
cacheName: "others",
|
|
88
|
+
expiration: {
|
|
89
|
+
maxEntries: 32,
|
|
90
|
+
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
91
|
+
},
|
|
92
|
+
networkTimeoutSeconds: 10,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
import type { SerwistViteContext } from "../../context.js";
|
|
4
|
+
import type { SerwistViteApi } from "../../types.js";
|
|
5
|
+
|
|
6
|
+
export const buildPlugin = (ctx: SerwistViteContext, api: SerwistViteApi) => {
|
|
7
|
+
return <Plugin>{
|
|
8
|
+
name: "@serwist/vite/integration-svelte:build",
|
|
9
|
+
apply: "build",
|
|
10
|
+
enforce: "pre",
|
|
11
|
+
closeBundle: {
|
|
12
|
+
sequential: true,
|
|
13
|
+
enforce: "pre",
|
|
14
|
+
async handler() {
|
|
15
|
+
if (api && !api.disabled && ctx.viteConfig.build.ssr) {
|
|
16
|
+
await api.generateSW();
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import type { ManifestTransform } from "@serwist/build";
|
|
6
|
+
import { errors } from "@serwist/build";
|
|
7
|
+
import type { ResolvedConfig } from "vite";
|
|
8
|
+
|
|
9
|
+
import type { PluginOptions as BasePluginOptions } from "../../types.js";
|
|
10
|
+
import { resolveEntry } from "../../utils.js";
|
|
11
|
+
import type { KitOptions } from "./types.js";
|
|
12
|
+
|
|
13
|
+
export const configurateSvelteKitOptions = (viteConfig: ResolvedConfig, kit: KitOptions, options: BasePluginOptions) => {
|
|
14
|
+
const clientOutDir = path.resolve(viteConfig.root, viteConfig.build.outDir, "../client");
|
|
15
|
+
|
|
16
|
+
// Kit fixes the service worker's name to 'service-worker.js'
|
|
17
|
+
if (viteConfig.isProduction) {
|
|
18
|
+
options.swSrc = path.resolve(clientOutDir, "service-worker.js");
|
|
19
|
+
options.swDest = path.resolve(clientOutDir, "service-worker.js");
|
|
20
|
+
} else {
|
|
21
|
+
const swSrc = resolveEntry(path.join(viteConfig.root, kit.files?.serviceWorker ?? "src/service-worker"));
|
|
22
|
+
if (swSrc) {
|
|
23
|
+
options.swSrc = swSrc;
|
|
24
|
+
options.swDest = path.join(os.tmpdir(), `serwist-vite-integration-svelte-${crypto.randomUUID()}.js`);
|
|
25
|
+
} else {
|
|
26
|
+
throw new Error(errors["invalid-sw-src"]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
options.swUrl = "/service-worker.js";
|
|
30
|
+
|
|
31
|
+
// SvelteKit's outDir is `.svelte-kit/output/client`.
|
|
32
|
+
// We need to include the parent folder in globDirectory since SvelteKit will generate SSG in `.svelte-kit/output/prerendered` folder.
|
|
33
|
+
if (!options.globDirectory) {
|
|
34
|
+
options.globDirectory = path.resolve(clientOutDir, "..");
|
|
35
|
+
}
|
|
36
|
+
if (!options.manifestTransforms) {
|
|
37
|
+
options.manifestTransforms = [createManifestTransform(viteConfig.base, undefined, kit)];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let buildAssetsDir = kit.appDir ?? "_app/";
|
|
41
|
+
if (buildAssetsDir[0] === "/") buildAssetsDir = buildAssetsDir.slice(1);
|
|
42
|
+
if (buildAssetsDir[buildAssetsDir.length - 1] !== "/") buildAssetsDir += "/";
|
|
43
|
+
|
|
44
|
+
if (!options.modifyURLPrefix) {
|
|
45
|
+
options.globPatterns = buildGlobPatterns(options.globPatterns);
|
|
46
|
+
if (kit.includeVersionFile) {
|
|
47
|
+
options.globPatterns.push(`client/${buildAssetsDir}version.json`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Exclude server assets: sw is built on SSR build
|
|
52
|
+
options.globIgnores = buildGlobIgnores(options.globIgnores);
|
|
53
|
+
|
|
54
|
+
// Vite 5 support: allow override dontCacheBustURLsMatching
|
|
55
|
+
if (!("dontCacheBustURLsMatching" in options)) {
|
|
56
|
+
options.dontCacheBustURLsMatching = new RegExp(`${buildAssetsDir}immutable/`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!options.injectionPoint) {
|
|
60
|
+
options.injectionPoint = "self.__SW_MANIFEST";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export function createManifestTransform(base: string, webManifestName?: string, options?: KitOptions): ManifestTransform {
|
|
65
|
+
return async (entries) => {
|
|
66
|
+
const defaultAdapterFallback = "prerendered/fallback.html";
|
|
67
|
+
const suffix = options?.trailingSlash === "always" ? "/" : "";
|
|
68
|
+
let adapterFallback = options?.adapterFallback;
|
|
69
|
+
let excludeFallback = false;
|
|
70
|
+
// the fallback will be always generated by SvelteKit.
|
|
71
|
+
// The adapter will copy the fallback only if it is provided in its options: we need to exclude it
|
|
72
|
+
if (!adapterFallback) {
|
|
73
|
+
adapterFallback = defaultAdapterFallback;
|
|
74
|
+
excludeFallback = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// the fallback will be always in .svelte-kit/output/prerendered/fallback.html
|
|
78
|
+
const manifest = entries
|
|
79
|
+
.filter(({ url }) => !(excludeFallback && url === defaultAdapterFallback))
|
|
80
|
+
.map((e) => {
|
|
81
|
+
let url = e.url;
|
|
82
|
+
// client assets in `.svelte-kit/output/client` folder.
|
|
83
|
+
// SSG pages in `.svelte-kit/output/prerendered/pages` folder.
|
|
84
|
+
// fallback page in `.svelte-kit/output/prerendered` folder (fallback.html is the default).
|
|
85
|
+
if (url.startsWith("client/")) url = url.slice(7);
|
|
86
|
+
else if (url.startsWith("prerendered/pages/")) url = url.slice(18);
|
|
87
|
+
else if (url === defaultAdapterFallback) url = adapterFallback!;
|
|
88
|
+
|
|
89
|
+
if (url.endsWith(".html")) {
|
|
90
|
+
if (url.startsWith("/")) url = url.slice(1);
|
|
91
|
+
|
|
92
|
+
if (url === "index.html") {
|
|
93
|
+
url = base;
|
|
94
|
+
} else {
|
|
95
|
+
const idx = url.lastIndexOf("/");
|
|
96
|
+
if (idx > -1) {
|
|
97
|
+
// abc/index.html -> abc/?
|
|
98
|
+
if (url.endsWith("/index.html")) url = `${url.slice(0, idx)}${suffix}`;
|
|
99
|
+
// abc/def.html -> abc/def/?
|
|
100
|
+
else url = `${url.substring(0, url.lastIndexOf("."))}${suffix}`;
|
|
101
|
+
} else {
|
|
102
|
+
// xxx.html -> xxx/?
|
|
103
|
+
url = `${url.substring(0, url.lastIndexOf("."))}${suffix}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
e.url = url;
|
|
109
|
+
|
|
110
|
+
return e;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!webManifestName) return { manifest };
|
|
114
|
+
|
|
115
|
+
return { manifest: manifest.filter((e) => e.url !== webManifestName) };
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildGlobPatterns(globPatterns?: string[]) {
|
|
120
|
+
if (globPatterns) {
|
|
121
|
+
if (!globPatterns.some((g) => g.startsWith("prerendered/"))) globPatterns.push("prerendered/**/*.html");
|
|
122
|
+
|
|
123
|
+
if (!globPatterns.some((g) => g.startsWith("client/"))) globPatterns.push("client/**/*.{js,css,ico,png,svg,webp,webmanifest}");
|
|
124
|
+
|
|
125
|
+
if (!globPatterns.some((g) => g.includes("webmanifest"))) globPatterns.push("client/*.webmanifest");
|
|
126
|
+
|
|
127
|
+
return globPatterns;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return ["client/**/*.{js,css,ico,png,svg,webp,webmanifest}", "prerendered/**/*.html"];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function buildGlobIgnores(globIgnores?: string[]) {
|
|
134
|
+
if (globIgnores) {
|
|
135
|
+
if (!globIgnores.some((g) => g.startsWith("server/"))) globIgnores.push("server/*.*");
|
|
136
|
+
|
|
137
|
+
return globIgnores;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return ["server/*.*"];
|
|
141
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
import { createApi } from "../../api.js";
|
|
4
|
+
import { createContext } from "../../context.js";
|
|
5
|
+
import { devPlugin } from "../../plugins/dev.js";
|
|
6
|
+
import { mainPlugin } from "../../plugins/main.js";
|
|
7
|
+
import type { PluginOptions as BasePluginOptions } from "../../types.js";
|
|
8
|
+
import { buildPlugin } from "./build.js";
|
|
9
|
+
import { configurateSvelteKitOptions } from "./config.js";
|
|
10
|
+
import type { PluginOptions } from "./types.js";
|
|
11
|
+
|
|
12
|
+
// TODO: handle SvelteKit build errors.
|
|
13
|
+
/**
|
|
14
|
+
* Integrates Serwist into your SvelteKit app.
|
|
15
|
+
* @param userOptions
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export const serwist = (userOptions: PluginOptions = {}): Plugin[] => {
|
|
19
|
+
if (!userOptions.integration) userOptions.integration = {};
|
|
20
|
+
userOptions.integration.closeBundleOrder = "pre";
|
|
21
|
+
userOptions.integration.configureOptions = (viteConfig, options) => configurateSvelteKitOptions(viteConfig, userOptions.kit ?? {}, options);
|
|
22
|
+
const ctx = createContext(userOptions as BasePluginOptions, "sveltekit");
|
|
23
|
+
const api = createApi(ctx);
|
|
24
|
+
return [mainPlugin(ctx, api), devPlugin(ctx, api), buildPlugin(ctx, api)];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { KitConfig } from "@sveltejs/kit";
|
|
2
|
+
|
|
3
|
+
import type { PluginOptions as BasePluginOptions } from "../../types.js";
|
|
4
|
+
import type { Optional } from "../../utils-types.js";
|
|
5
|
+
|
|
6
|
+
export interface KitOptions extends Pick<KitConfig, "appDir" | "files"> {
|
|
7
|
+
/**
|
|
8
|
+
* @see https://kit.svelte.dev/docs/adapter-static#options-fallback
|
|
9
|
+
*/
|
|
10
|
+
adapterFallback?: string;
|
|
11
|
+
/**
|
|
12
|
+
* `trailingSlash` in `+page.{ts,js}` or `+layout.{ts,js}` files.
|
|
13
|
+
* @default "never"
|
|
14
|
+
*/
|
|
15
|
+
trailingSlash?: "never" | "always" | "ignore";
|
|
16
|
+
/**
|
|
17
|
+
* Should `"${appDir}/version.json"` be included in the service worker's precache manifest?
|
|
18
|
+
*
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
includeVersionFile?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PluginOptions extends Optional<Omit<BasePluginOptions, "swSrc" | "swDest" | "swUrl">, "globDirectory"> {
|
|
25
|
+
kit?: KitOptions;
|
|
26
|
+
}
|
package/src/log.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { BuildResult } from "@serwist/build";
|
|
2
|
+
import { cyan, dim, green, yellow } from "kolorist";
|
|
3
|
+
import type { ResolvedConfig } from "vite";
|
|
4
|
+
|
|
5
|
+
import { version } from "../package.json";
|
|
6
|
+
|
|
7
|
+
export const logSerwistResult = (buildResult: Pick<BuildResult, "count" | "size" | "warnings">, viteOptions: ResolvedConfig) => {
|
|
8
|
+
const { logLevel = "info" } = viteOptions;
|
|
9
|
+
|
|
10
|
+
if (logLevel === "silent") return;
|
|
11
|
+
|
|
12
|
+
const { count, size, warnings } = buildResult;
|
|
13
|
+
|
|
14
|
+
if (logLevel === "info") {
|
|
15
|
+
console.info(
|
|
16
|
+
[
|
|
17
|
+
"",
|
|
18
|
+
`${cyan(`@serwist/vite v${version}`)} ${green("files generated.")}`,
|
|
19
|
+
`${green("✓")} ${count} precache entries ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
|
|
20
|
+
// log build warning
|
|
21
|
+
warnings && warnings.length > 0 ? yellow(["⚠ warnings", ...warnings.map((w) => ` ${w}`), ""].join("\n")) : "",
|
|
22
|
+
].join("\n"),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
};
|