@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/src/modules.ts ADDED
@@ -0,0 +1,174 @@
1
+ import assert from "node:assert";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ import type * as SerwistBuild from "@serwist/build";
6
+
7
+ import type { SerwistViteContext } from "./context.js";
8
+
9
+ export const loadSerwistBuild = async (): Promise<typeof SerwistBuild> => {
10
+ // "@serwist/build" is large and makes config loading slow.
11
+ // Since it is not always used, we only load this when it is needed.
12
+ try {
13
+ return await import("@serwist/build");
14
+ } catch (_) {
15
+ // We don't have a default export, don't worry.
16
+ return require("@serwist/build");
17
+ }
18
+ };
19
+
20
+ interface BuildResult extends SerwistBuild.GetManifestResult {
21
+ manifestString: string;
22
+ }
23
+
24
+ export const injectManifest = async (config: SerwistBuild.ViteInjectManifestOptions): Promise<BuildResult> => {
25
+ const { validateViteInjectManifestOptions, getFileManifestEntries, stringify } = await loadSerwistBuild();
26
+ const options = await validateViteInjectManifestOptions(config);
27
+ const { count, size, manifestEntries, warnings } = await getFileManifestEntries(options);
28
+ const manifestString = manifestEntries === undefined ? "undefined" : stringify(manifestEntries);
29
+ return {
30
+ warnings,
31
+ size,
32
+ count,
33
+ manifestEntries,
34
+ manifestString,
35
+ };
36
+ };
37
+
38
+ export const generateServiceWorker = async (ctx: SerwistViteContext) => {
39
+ const { format, plugins, rollupOptions } = ctx.options.injectManifestRollupOptions;
40
+
41
+ const parsedSwDest = path.parse(ctx.options.injectManifest.swDest);
42
+
43
+ let injectManifestResult: BuildResult | undefined = undefined;
44
+
45
+ if (ctx.options.injectManifest.injectionPoint) {
46
+ await ctx.options.integration?.beforeBuildServiceWorker?.(ctx.options);
47
+ injectManifestResult = await injectManifest(ctx.options.injectManifest);
48
+ }
49
+
50
+ const isProduction = ctx.options.mode === "production";
51
+ const isDev = ctx.options.mode === "development";
52
+
53
+ if ((isProduction && ctx.framework === "sveltekit") || (isDev && !ctx.options.devOptions.bundle)) {
54
+ if (!injectManifestResult) {
55
+ throw new Error("injectManifest failed to generate results. This is likely a bug.");
56
+ }
57
+
58
+ const { errors, escapeRegExp, getSourceMapURL, rebasePath, replaceAndUpdateSourceMap, translateURLToSourcemapPaths } = await loadSerwistBuild();
59
+
60
+ // Make sure we leave swSrc and swDest out of the precache manifest.
61
+ for (const file of [ctx.options.injectManifest.swSrc, ctx.options.injectManifest.swDest]) {
62
+ ctx.options.injectManifest.globIgnores!.push(
63
+ rebasePath({
64
+ file,
65
+ baseDirectory: ctx.options.injectManifest.globDirectory,
66
+ }),
67
+ );
68
+ }
69
+
70
+ const injectionPoint = ctx.options.injectManifest.injectionPoint!;
71
+
72
+ const globalRegexp = new RegExp(escapeRegExp(injectionPoint), "g");
73
+
74
+ let swFileContents: string;
75
+ try {
76
+ swFileContents = await fs.readFile(ctx.options.injectManifest.swSrc, "utf8");
77
+ } catch (error) {
78
+ throw new Error(`${errors["invalid-sw-src"]} ${error instanceof Error && error.message ? error.message : ""}`);
79
+ }
80
+
81
+ const injectionResults = swFileContents.match(globalRegexp);
82
+ // See https://github.com/GoogleChrome/workbox/issues/2230
83
+ if (!injectionResults) {
84
+ throw new Error(`${errors["injection-point-not-found"]} ${injectionPoint}`);
85
+ }
86
+
87
+ assert(injectionResults.length === 1, `${errors["multiple-injection-points"]} ${injectionPoint}`);
88
+
89
+ const filesToWrite: Record<string, string> = {};
90
+
91
+ const url = getSourceMapURL(swFileContents);
92
+ // See https://github.com/GoogleChrome/workbox/issues/2957
93
+ const { destPath, srcPath, warning } = translateURLToSourcemapPaths(url, ctx.options.injectManifest.swSrc, ctx.options.injectManifest.swDest);
94
+ if (warning) {
95
+ injectManifestResult.warnings.push(warning);
96
+ }
97
+
98
+ // If our swSrc file contains a sourcemap, we would invalidate that
99
+ // mapping if we just replaced injectionPoint with the stringified manifest.
100
+ // Instead, we need to update the swDest contents as well as the sourcemap
101
+ // (assuming it's a real file, not a data: URL) at the same time.
102
+ // See https://github.com/GoogleChrome/workbox/issues/2235
103
+ // and https://github.com/GoogleChrome/workbox/issues/2648
104
+ if (srcPath && destPath) {
105
+ const { map, source } = await replaceAndUpdateSourceMap({
106
+ originalMap: JSON.parse(await fs.readFile(srcPath, "utf8")),
107
+ jsFilename: path.basename(ctx.options.injectManifest.swDest),
108
+ originalSource: swFileContents,
109
+ replaceString: injectManifestResult.manifestString,
110
+ searchString: injectionPoint,
111
+ });
112
+
113
+ filesToWrite[ctx.options.injectManifest.swDest] = source;
114
+ filesToWrite[destPath] = map;
115
+ } else {
116
+ // If there's no sourcemap associated with swSrc, a simple string
117
+ // replacement will suffice.
118
+ filesToWrite[ctx.options.injectManifest.swDest] = swFileContents.replace(globalRegexp, injectManifestResult.manifestString);
119
+ }
120
+
121
+ for (const [file, contents] of Object.entries(filesToWrite)) {
122
+ try {
123
+ await fs.mkdir(path.dirname(file), { recursive: true });
124
+ } catch (error: unknown) {
125
+ throw new Error(`${errors["unable-to-make-sw-directory"]} '${error instanceof Error && error.message ? error.message : ""}'`);
126
+ }
127
+
128
+ await fs.writeFile(file, contents);
129
+ }
130
+ } else {
131
+ const define: Record<string, any> = {
132
+ // Nuxt does some really weird stuff. During the build, they MANUALLY
133
+ // set browser APIs, such as window, document, location,..., to `undefined`??
134
+ // Probably some Vue or server stuff. Their `define` doesn't seem to have anything
135
+ // particularly useful for the service worker anyway, so we don't extend it.
136
+ ...(ctx.framework === "nuxt" ? undefined : ctx.viteConfig.define),
137
+ "process.env.NODE_ENV": `"${ctx.options.mode}"`,
138
+ };
139
+ if (ctx.options.injectManifest.injectionPoint && injectManifestResult) {
140
+ define[ctx.options.injectManifest.injectionPoint] = injectManifestResult.manifestString;
141
+ }
142
+ const { build } = await import("vite");
143
+ await build({
144
+ logLevel: ctx.viteConfig.isProduction ? "info" : "warn",
145
+ root: ctx.viteConfig.root,
146
+ base: ctx.viteConfig.base,
147
+ resolve: ctx.viteConfig.resolve,
148
+ // Don't copy anything from public folder
149
+ publicDir: false,
150
+ build: {
151
+ sourcemap: ctx.viteConfig.build.sourcemap,
152
+ lib: {
153
+ entry: ctx.options.injectManifest.swSrc,
154
+ name: "app",
155
+ formats: [format],
156
+ },
157
+ rollupOptions: {
158
+ ...rollupOptions,
159
+ plugins,
160
+ output: {
161
+ entryFileNames: parsedSwDest.base,
162
+ },
163
+ },
164
+ outDir: parsedSwDest.dir,
165
+ emptyOutDir: false,
166
+ minify: isProduction || ctx.options.devOptions.minify,
167
+ },
168
+ configFile: false,
169
+ define,
170
+ });
171
+ }
172
+
173
+ return injectManifestResult;
174
+ };
package/src/options.ts ADDED
@@ -0,0 +1,90 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+
4
+ import type { ResolvedConfig } from "vite";
5
+
6
+ import { configureStaticAssets } from "./assets.js";
7
+ import { loadSerwistBuild } from "./modules.js";
8
+ import type { PluginOptions, ResolvedPluginOptions } from "./types.js";
9
+ import { resolveBasePath, slash } from "./utils.js";
10
+
11
+ export const resolveOptions = async (options: PluginOptions, viteConfig: ResolvedConfig): Promise<ResolvedPluginOptions> => {
12
+ const {
13
+ type = "classic",
14
+ mode = (process.env.NODE_ENV || "production") as "production" | "development",
15
+ injectRegister = "auto",
16
+ registerType = "prompt",
17
+ minify = true,
18
+ base = viteConfig.base,
19
+ scope: _scope,
20
+ swUrl = "/sw.js",
21
+ includeAssets = undefined,
22
+ useCredentials = false,
23
+ disable = false,
24
+ integration = {},
25
+ buildBase,
26
+ devOptions,
27
+ plugins = [],
28
+ rollupOptions = {},
29
+ rollupFormat = "es",
30
+ ...injectManifest
31
+ } = options;
32
+
33
+ const basePath = resolveBasePath(base);
34
+ // check typescript service worker for injectManifest strategy
35
+ const scope = _scope || basePath;
36
+
37
+ let assetsDir = slash(viteConfig.build.assetsDir ?? "assets");
38
+ if (assetsDir[assetsDir.length - 1] !== "/") assetsDir += "/";
39
+
40
+ const resolvedDevOptions: ResolvedPluginOptions["devOptions"] = {
41
+ bundle: true,
42
+ minify: false,
43
+ ...devOptions,
44
+ };
45
+
46
+ // remove './' prefix from assetsDir
47
+ const dontCacheBustURLsMatching = new RegExp(`^${assetsDir.replace(/^\.*?\//, "")}`);
48
+
49
+ const { validateViteInjectManifestOptions } = await loadSerwistBuild();
50
+
51
+ const validatedInjectManifest = await validateViteInjectManifestOptions(injectManifest);
52
+
53
+ const { swSrc, swDest, ...userInjectManifest } = validatedInjectManifest || {};
54
+
55
+ const resolvedPluginOptions = {
56
+ base: basePath,
57
+ type,
58
+ mode,
59
+ injectRegister,
60
+ registerType,
61
+ useCredentials,
62
+ swUrl,
63
+ injectManifest: {
64
+ dontCacheBustURLsMatching,
65
+ ...userInjectManifest,
66
+ swSrc: path.resolve(viteConfig.root, swSrc),
67
+ swDest: path.resolve(viteConfig.root, viteConfig.build.outDir, swDest),
68
+ disablePrecacheManifest: !viteConfig.isProduction,
69
+ },
70
+ scope,
71
+ minify,
72
+ includeAssets,
73
+ disable,
74
+ integration,
75
+ buildBase: buildBase ?? basePath,
76
+ injectManifestRollupOptions: {
77
+ plugins,
78
+ rollupOptions,
79
+ format: rollupFormat,
80
+ },
81
+ devOptions: resolvedDevOptions,
82
+ } satisfies ResolvedPluginOptions;
83
+
84
+ // calculate hash only when required
85
+ const calculateHash = !resolvedPluginOptions.disable && resolvedPluginOptions.includeAssets && viteConfig.command === "build";
86
+
87
+ if (calculateHash) await configureStaticAssets(resolvedPluginOptions, viteConfig);
88
+
89
+ return resolvedPluginOptions;
90
+ };
@@ -0,0 +1,31 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ import type { SerwistViteContext } from "../context.js";
4
+ import type { SerwistViteApi } from "../types.js";
5
+
6
+ /**
7
+ * Internal build plugin used by `@serwist/vite`.
8
+ * @internal
9
+ * @param ctx
10
+ * @param api
11
+ * @returns
12
+ */
13
+ export const buildPlugin = (ctx: SerwistViteContext, api: SerwistViteApi) => {
14
+ return <Plugin>{
15
+ name: "@serwist/vite:build",
16
+ enforce: "post",
17
+ apply: "build",
18
+ closeBundle: {
19
+ sequential: true,
20
+ order: ctx.userOptions?.integration?.closeBundleOrder,
21
+ async handler() {
22
+ if (!ctx.viteConfig.build.ssr && !ctx.options.disable) {
23
+ await api.generateSW();
24
+ }
25
+ },
26
+ },
27
+ async buildEnd(error) {
28
+ if (error) throw error;
29
+ },
30
+ };
31
+ };
@@ -0,0 +1,61 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { type Plugin, normalizePath } from "vite";
5
+
6
+ import type { SerwistViteContext } from "../context.js";
7
+ import type { SerwistViteApi } from "../types.js";
8
+ import { toFs } from "../utils.js";
9
+
10
+ // This plugin handles the service worker in two ways:
11
+ // - If `devOptions.bundle` is enabled, hook a middleware that bundles the service worker
12
+ // through `api.generateSW()` and returns the result into Vite's dev server.
13
+ // - Otherwise, run `injectManifest` and return the service worker through `async load(id)`. Although
14
+ // `precacheEntries` is always `undefined`, we still do this to check the user's `injectManifest` options
15
+ // in dev mode.
16
+ /**
17
+ * Internal dev plugin used by `@serwist/vite`.
18
+ * @internal
19
+ * @param ctx
20
+ * @param api
21
+ * @returns
22
+ */
23
+ export const devPlugin = (ctx: SerwistViteContext, api: SerwistViteApi): Plugin => {
24
+ return {
25
+ name: "@serwist/vite:dev",
26
+ apply: "serve",
27
+ configureServer(server) {
28
+ ctx.devEnvironment = true;
29
+ server.middlewares.use(async (req, res, next) => {
30
+ if (!ctx.options.disable && req.url === ctx.options.swUrl) {
31
+ if (ctx.options.devOptions.bundle) {
32
+ await api.generateSW();
33
+ const content = await fs.readFile(ctx.options.injectManifest.swDest, "utf-8");
34
+ await fs.rm(ctx.options.injectManifest.swDest);
35
+ res.setHeader("Content-Type", "application/javascript");
36
+ res.write(content);
37
+ res.end();
38
+ } else {
39
+ res.setHeader("Content-Type", "application/javascript");
40
+ res.write(`import "${toFs(path.resolve(ctx.options.injectManifest.swSrc))}";`);
41
+ res.end();
42
+ }
43
+ } else {
44
+ next();
45
+ }
46
+ });
47
+ },
48
+ async load(id) {
49
+ if (!ctx.options.disable && !ctx.options.devOptions.bundle) {
50
+ const swSrcId = normalizePath(ctx.options.injectManifest.swSrc);
51
+ if (id === swSrcId) {
52
+ await api.generateSW();
53
+ const content = await fs.readFile(ctx.options.injectManifest.swDest, "utf-8");
54
+ await fs.rm(ctx.options.injectManifest.swDest);
55
+ return content;
56
+ }
57
+ }
58
+ return undefined;
59
+ },
60
+ };
61
+ };
@@ -0,0 +1,49 @@
1
+ import path from "node:path";
2
+
3
+ import type { Plugin, UserConfig } from "vite";
4
+
5
+ import { INTERNAL_SERWIST_VIRTUAL, RESOLVED_INTERNAL_SERWIST_VIRTUAL } from "../constants.js";
6
+ import type { SerwistViteContext } from "../context.js";
7
+ import { resolveOptions } from "../options.js";
8
+ import type { SerwistViteApi } from "../types.js";
9
+
10
+ /**
11
+ * Internal plugin used by `@serwist/vite`.
12
+ * @internal
13
+ * @param ctx
14
+ * @param api
15
+ * @returns
16
+ */
17
+ export const mainPlugin = (ctx: SerwistViteContext, api: SerwistViteApi) => {
18
+ return <Plugin>{
19
+ name: "@serwist/vite",
20
+ enforce: "pre",
21
+ config() {
22
+ return <UserConfig>{
23
+ ssr: {
24
+ noExternal: [],
25
+ },
26
+ };
27
+ },
28
+ async configResolved(config) {
29
+ ctx.viteConfig = config;
30
+ ctx.userOptions?.integration?.configureOptions?.(config, ctx.userOptions);
31
+ ctx.options = await resolveOptions(ctx.userOptions, config);
32
+ },
33
+ resolveId(id) {
34
+ if (id === INTERNAL_SERWIST_VIRTUAL) {
35
+ return RESOLVED_INTERNAL_SERWIST_VIRTUAL;
36
+ }
37
+ return undefined;
38
+ },
39
+ load(id) {
40
+ if (id === RESOLVED_INTERNAL_SERWIST_VIRTUAL) {
41
+ return `export const swUrl = "${path.posix.join(ctx.options.buildBase, ctx.options.swUrl)}";
42
+ export const swScope = "${ctx.options.scope}";
43
+ export const swType = "${ctx.devEnvironment ? "module" : ctx.options.type}";`;
44
+ }
45
+ return undefined;
46
+ },
47
+ api,
48
+ };
49
+ };
package/src/rollup.js ADDED
@@ -0,0 +1,46 @@
1
+ // @ts-check
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { getRollupOptions } from "@serwist/constants/rollup";
6
+ import fg from "fast-glob";
7
+
8
+ import packageJson from "../package.json" assert { type: "json" };
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+
12
+ const clientEntries = /** @type {Record<string, string>} */ ({});
13
+
14
+ for (const clientPath of await fg("client/*.ts", { cwd: __dirname })) {
15
+ const parsedClientPath = path.parse(clientPath);
16
+ clientEntries[`client/${parsedClientPath.name}`] = path.join("src", clientPath);
17
+ }
18
+
19
+ const integrationEntries = /** @type {Record<string, string>} */ ({});
20
+
21
+ for (const integrationPath of await fg("integration/*/index.ts", { cwd: __dirname })) {
22
+ const parsedClientPath = path.parse(integrationPath);
23
+ integrationEntries[`${parsedClientPath.dir}/${parsedClientPath.name}`] = path.join("src", integrationPath);
24
+ }
25
+
26
+ export default getRollupOptions({
27
+ packageJson,
28
+ jsFiles: [
29
+ {
30
+ input: {
31
+ index: "src/index.ts",
32
+ "index.browser": "src/index.browser.ts",
33
+ "index.worker": "src/index.worker.ts",
34
+ ...clientEntries,
35
+ ...integrationEntries,
36
+ },
37
+ output: {
38
+ dir: "dist",
39
+ format: "esm",
40
+ entryFileNames: "[name].js",
41
+ chunkFileNames: "[name].js",
42
+ },
43
+ external: ["virtual:internal-serwist"],
44
+ },
45
+ ],
46
+ });
package/src/types.ts ADDED
@@ -0,0 +1,207 @@
1
+ import type { ManifestEntry, ViteInjectManifestOptions } from "@serwist/build";
2
+ import type { RollupOptions } from "rollup";
3
+ import type { BuildOptions, Plugin, ResolvedConfig } from "vite";
4
+
5
+ export type InjectManifestVitePlugins = string[] | ((vitePluginIds: string[]) => string[]);
6
+ export interface CustomInjectManifestOptions extends Omit<ViteInjectManifestOptions, "disablePrecacheManifest"> {
7
+ /**
8
+ * The URL to the service worker.
9
+ * @default "/sw.js"
10
+ */
11
+ swUrl?: string;
12
+ /**
13
+ * Configure the format to use in the Rollup build.
14
+ *
15
+ * @default 'es'
16
+ */
17
+ rollupFormat?: "es" | "iife";
18
+ /**
19
+ * Since `v0.15.0` you can add plugins to build your service worker.
20
+ *
21
+ * When using `injectManifest` there are 2 builds, your application and the service worker.
22
+ * If you're using custom configuration for your service worker (for example custom plugins) you can use this option to configure the service worker build.
23
+ * Both configurations cannot be shared, and so you'll need to duplicate the configuration, with the exception of `define`.
24
+ *
25
+ * **WARN**: this option is for advanced usage, be aware that you may break your application build.
26
+ */
27
+ plugins?: Plugin[];
28
+ /**
29
+ * Since `v0.15.0` you can add custom Rollup options to build your service worker: we expose the same configuration to build a worker using Vite.
30
+ */
31
+ rollupOptions?: Omit<RollupOptions, "plugins" | "output">;
32
+ }
33
+
34
+ export interface SerwistViteHooks {
35
+ beforeBuildServiceWorker?: (options: ResolvedPluginOptions) => void | Promise<void>;
36
+ closeBundleOrder?: "pre" | "post" | null;
37
+ configureOptions?: (viteOptions: ResolvedConfig, options: PluginOptions) => void | Promise<void>;
38
+ }
39
+
40
+ export interface DevOptions {
41
+ /**
42
+ * Whether the service worker should be bundled in development mode.
43
+ *
44
+ * [Many browsers still do not support ES Modules in service workers.](https://caniuse.com/mdn-api_serviceworker_ecmascript_modules) However, in development
45
+ * mode, certain frameworks, such as SvelteKit, do not bundle the service worker. As a result, trying to register that service worker on browsers lacking
46
+ * support, such as Firefox or Safari, will fail, but doing so on browsers not lacking support will not fail. This option is provided to prevent that from
47
+ * happening. What the plugin does is intercepting any request to the service worker (requests for `swUrl`) and returning a bundled one.
48
+ */
49
+ bundle?: boolean;
50
+ minify?: BuildOptions["minify"];
51
+ }
52
+
53
+ /**
54
+ * Plugin options.
55
+ */
56
+ export interface BasePluginOptions {
57
+ /**
58
+ * Build mode
59
+ *
60
+ * @default
61
+ * process.env.NODE_ENV // or "production" if undefined
62
+ */
63
+ mode?: "development" | "production";
64
+ /**
65
+ * The service worker type.
66
+ *
67
+ * @default "classic"
68
+ */
69
+ type?: WorkerType;
70
+ /**
71
+ * The scope to register the Service Worker
72
+ *
73
+ * @default `viteOptions.base`
74
+ */
75
+ scope?: string;
76
+ /**
77
+ * Inject the service worker register inlined in the index.html
78
+ *
79
+ * If set to "auto", depends on whether you used the `import { registerSW } from 'virtual:pwa-register'`
80
+ * it will do nothing or use the `script` mode
81
+ *
82
+ * `"inline"` - inject a simple register, inlined with the generated html
83
+ *
84
+ * `"script"` - inject `<script/>` in `<head>` with `src` attribute to a generated script to register the service worker
85
+ *
86
+ * `"script-defer"` - inject `<script defer />` in `<head>`, with `src` attribute to a generated script to register the service worker
87
+ *
88
+ * `null` - do nothing. You will need to register the service worker yourself or import `registerSW` from `virtual:pwa-register`.
89
+ *
90
+ * @default "auto"
91
+ */
92
+ injectRegister: "inline" | "script" | "script-defer" | "auto" | null | false;
93
+ /**
94
+ * Mode for the virtual register.
95
+ * This is NOT available if `injectRegister` is set to `"inline"` or `"script"`
96
+ *
97
+ * `"prompt"` - you will need to show a popup/dialog to the user to confirm the reload.
98
+ *
99
+ * `"autoUpdate"` - when new content is available, the new service worker will update caches and reload all browser
100
+ * windows/tabs with the application open automatically, it must take the control for the application to work
101
+ * properly.
102
+ *
103
+ * @default "prompt"
104
+ */
105
+ registerType?: "prompt" | "autoUpdate";
106
+ /**
107
+ * Minify the generated manifest
108
+ *
109
+ * @default true
110
+ */
111
+ minify: boolean;
112
+ /**
113
+ * Whether to add the `crossorigin="use-credentials"` attribute to `<link rel="manifest">`
114
+ * @default false
115
+ */
116
+ useCredentials?: boolean;
117
+ /**
118
+ * Override Vite's base options for `@serwist/vite`.
119
+ *
120
+ * @default viteOptions.base
121
+ */
122
+ base?: string;
123
+ /**
124
+ * `public` resources to be added to the PWA manifest.
125
+ *
126
+ * You don't need to add `manifest` icons here, it will be auto included.
127
+ *
128
+ * The `public` directory will be resolved from Vite's `publicDir` option directory.
129
+ */
130
+ includeAssets: string | string[] | undefined;
131
+ /**
132
+ * Whether Serwist should be disabled.
133
+ *
134
+ * @default false
135
+ */
136
+ disable: boolean;
137
+ /**
138
+ * `@serwist/vite` integration.
139
+ */
140
+ integration?: SerwistViteHooks;
141
+ /**
142
+ * When Vite's build folder is not the same as your base root folder, configure it here.
143
+ *
144
+ * This option will be useful for integrations like `vite-plugin-laravel` where Vite's build folder is `public/build` but Laravel's base path is `public`.
145
+ *
146
+ * This option will be used to configure the path for the service worker, "registerSW.js" and the web manifest assets.
147
+ *
148
+ * For example, if your base path is `/`, then, in your Laravel PWA configuration use `buildPath: '/build/'`.
149
+ *
150
+ * By default: `vite.base`.
151
+ */
152
+ buildBase?: string;
153
+ }
154
+
155
+ export interface PluginOptions extends Partial<BasePluginOptions>, CustomInjectManifestOptions {
156
+ devOptions?: DevOptions;
157
+ }
158
+
159
+ export interface InjectManifestRollupOptions {
160
+ format: "es" | "iife";
161
+ plugins: Plugin[];
162
+ rollupOptions: RollupOptions;
163
+ }
164
+
165
+ export interface ResolvedPluginOptions extends Required<BasePluginOptions>, Required<Pick<CustomInjectManifestOptions, "swUrl">> {
166
+ injectManifest: ViteInjectManifestOptions;
167
+ injectManifestRollupOptions: InjectManifestRollupOptions;
168
+ devOptions: Required<DevOptions>;
169
+ }
170
+
171
+ export interface ShareTargetFiles {
172
+ name: string;
173
+ accept: string | string[];
174
+ }
175
+
176
+ /**
177
+ * @see https://developer.mozilla.org/en-US/docs/Web/Manifest/launch_handler#launch_handler_item_values
178
+ */
179
+ export type LaunchHandlerClientMode = "auto" | "focus-existing" | "navigate-existing" | "navigate-new";
180
+
181
+ export type Display = "fullscreen" | "standalone" | "minimal-ui" | "browser";
182
+ export type DisplayOverride = Display | "window-controls-overlay";
183
+ export type IconPurpose = "monochrome" | "maskable" | "any";
184
+
185
+ // biome-ignore lint/complexity/noBannedTypes: We intentionally have this.
186
+ type Nothing = {};
187
+
188
+ /**
189
+ * type StringLiteralUnion<'maskable'> = 'maskable' | string
190
+ * This has auto completion whereas `'maskable' | string` doesn't
191
+ * Adapted from https://github.com/microsoft/TypeScript/issues/29729
192
+ */
193
+ export type StringLiteralUnion<T extends U, U = string> = T | (U & Nothing);
194
+
195
+ export interface SerwistViteApi {
196
+ /**
197
+ * Is the plugin disabled?
198
+ */
199
+ disabled: boolean;
200
+ extendManifestEntries(fn: ExtendManifestEntriesHook): void;
201
+ /*
202
+ * Generate the service worker.
203
+ */
204
+ generateSW(): Promise<void>;
205
+ }
206
+
207
+ export type ExtendManifestEntriesHook = (manifestEntries: (string | ManifestEntry)[]) => (string | ManifestEntry)[] | undefined;
@@ -0,0 +1 @@
1
+ export type Optional<T, U extends keyof T> = Omit<T, U> & Partial<Pick<T, U>>;