@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.
Files changed (66) hide show
  1. package/dist/api.d.ts +1 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/assets.d.ts +1 -0
  4. package/dist/assets.d.ts.map +1 -0
  5. package/dist/constants.d.ts +1 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/context.d.ts +1 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/index.browser.d.ts +1 -0
  10. package/dist/index.browser.d.ts.map +1 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +2 -12
  14. package/dist/index.worker.d.ts +2 -1
  15. package/dist/index.worker.d.ts.map +1 -0
  16. package/dist/integration/svelte/build.d.ts +1 -0
  17. package/dist/integration/svelte/build.d.ts.map +1 -0
  18. package/dist/integration/svelte/config.d.ts +1 -0
  19. package/dist/integration/svelte/config.d.ts.map +1 -0
  20. package/dist/integration/svelte/index.d.ts +1 -0
  21. package/dist/integration/svelte/index.d.ts.map +1 -0
  22. package/dist/integration/svelte/index.js +1 -19
  23. package/dist/integration/svelte/types.d.ts +1 -0
  24. package/dist/integration/svelte/types.d.ts.map +1 -0
  25. package/dist/log.d.ts +1 -0
  26. package/dist/log.d.ts.map +1 -0
  27. package/dist/main.js +15 -87
  28. package/dist/modules.d.ts +1 -0
  29. package/dist/modules.d.ts.map +1 -0
  30. package/dist/options.d.ts +1 -0
  31. package/dist/options.d.ts.map +1 -0
  32. package/dist/plugins/build.d.ts +1 -0
  33. package/dist/plugins/build.d.ts.map +1 -0
  34. package/dist/plugins/dev.d.ts +1 -0
  35. package/dist/plugins/dev.d.ts.map +1 -0
  36. package/dist/plugins/main.d.ts +1 -0
  37. package/dist/plugins/main.d.ts.map +1 -0
  38. package/dist/types.d.ts +1 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/utils-types.d.ts +1 -0
  41. package/dist/utils-types.d.ts.map +1 -0
  42. package/dist/utils.d.ts +2 -1
  43. package/dist/utils.d.ts.map +1 -0
  44. package/package.json +38 -34
  45. package/src/api.ts +38 -0
  46. package/src/assets.ts +76 -0
  47. package/src/constants.ts +2 -0
  48. package/src/context.ts +45 -0
  49. package/src/index.browser.ts +8 -0
  50. package/src/index.ts +25 -0
  51. package/src/index.worker.ts +95 -0
  52. package/src/integration/svelte/build.ts +21 -0
  53. package/src/integration/svelte/config.ts +141 -0
  54. package/src/integration/svelte/index.ts +27 -0
  55. package/src/integration/svelte/types.ts +26 -0
  56. package/src/log.ts +25 -0
  57. package/src/modules.ts +174 -0
  58. package/src/options.ts +90 -0
  59. package/src/plugins/build.ts +31 -0
  60. package/src/plugins/dev.ts +61 -0
  61. package/src/plugins/main.ts +49 -0
  62. package/src/rollup.js +46 -0
  63. package/src/types.ts +207 -0
  64. package/src/utils-types.ts +1 -0
  65. package/src/utils.ts +69 -0
  66. package/src/virtual.d.ts +5 -0
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@serwist/vite",
3
- "version": "8.4.4",
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
- "dist",
8
- "!dist/dts"
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
- "import": {
52
- "types": "./dist/index.d.ts",
53
- "default": "./dist/index.js"
54
- }
54
+ "types": "./dist/index.d.ts",
55
+ "default": "./dist/index.js"
55
56
  },
56
57
  "./browser": {
57
- "import": {
58
- "types": "./dist/index.browser.d.ts",
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
- "import": {
64
- "types": "./dist/index.worker.d.ts",
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
- "import": {
70
- "types": "./dist/integration/*/index.d.ts",
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": "8.4.4",
81
- "@serwist/window": "8.4.4"
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.40.1",
85
- "@sveltejs/kit": "2.0.6",
79
+ "@playwright/test": "1.41.2",
80
+ "@sveltejs/kit": "2.5.0",
86
81
  "@types/debug": "4.1.12",
87
- "@types/node": "20.10.5",
82
+ "@types/node": "20.11.16",
88
83
  "@types/prompts": "2.4.9",
89
- "@types/react": "18.2.45",
90
- "bumpp": "9.2.1",
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.1",
97
- "solid-js": "1.8.7",
98
- "svelte": "5.0.0-next.26",
99
- "typescript": "5.4.0-dev.20231226",
100
- "vite": "5.0.10",
101
- "vue": "3.3.13",
102
- "@serwist/constants": "8.4.4"
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
+ };
@@ -0,0 +1,2 @@
1
+ export const INTERNAL_SERWIST_VIRTUAL = "virtual:internal-serwist";
2
+ export const RESOLVED_INTERNAL_SERWIST_VIRTUAL = `\0${INTERNAL_SERWIST_VIRTUAL}`;
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
+ };