@serwist/vite 8.2.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2023 Anthony Fu <https://github.com/antfu>, 2023 Serwist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ This module's documentation is a work-in-progress.
package/dist/api.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { SerwistViteContext } from "./context.js";
2
+ import type { SerwistViteApi } from "./types.js";
3
+ export declare const createApi: (ctx: SerwistViteContext) => SerwistViteApi;
@@ -0,0 +1,3 @@
1
+ import type { ResolvedConfig } from "vite";
2
+ import type { ResolvedPluginOptions } from "./types.js";
3
+ export declare const configureStaticAssets: (resolvedPluginOptions: ResolvedPluginOptions, viteConfig: ResolvedConfig) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export declare const INTERNAL_SERWIST_VIRTUAL = "virtual:internal-serwist";
2
+ export declare const RESOLVED_INTERNAL_SERWIST_VIRTUAL = "\0virtual:internal-serwist";
@@ -0,0 +1,10 @@
1
+ import type { ResolvedConfig } from "vite";
2
+ import type { PluginOptions, ResolvedPluginOptions } from "./types.js";
3
+ export interface SerwistViteContext {
4
+ viteConfig: ResolvedConfig;
5
+ userOptions: PluginOptions;
6
+ options: ResolvedPluginOptions;
7
+ useImportRegister: boolean;
8
+ devEnvironment: boolean;
9
+ }
10
+ export declare const createContext: (userOptions: PluginOptions) => SerwistViteContext;
@@ -0,0 +1 @@
1
+ export declare const getSerwist: () => Promise<import("@serwist/window").Serwist | undefined>;
@@ -0,0 +1,13 @@
1
+ import { swUrl, swScope, swType } from 'virtual:internal-serwist';
2
+
3
+ const getSerwist = async ()=>{
4
+ if ("serviceWorker" in navigator) {
5
+ return new (await import('@serwist/window')).Serwist(swUrl, {
6
+ scope: swScope,
7
+ type: swType
8
+ });
9
+ }
10
+ return undefined;
11
+ };
12
+
13
+ export { getSerwist };
@@ -0,0 +1,9 @@
1
+ import type { Plugin } from "vite";
2
+ import type { PluginOptions } from "./types.js";
3
+ /**
4
+ * Integrates Serwist into your Vite app.
5
+ * @param userOptions
6
+ * @returns
7
+ */
8
+ export declare const serwist: (userOptions: PluginOptions) => Plugin[];
9
+ export * from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import { m as mainPlugin, d as devPlugin, c as createContext, a as createApi } from './main-dzWmj8BB.js';
2
+ import 'node:path';
3
+ import 'node:process';
4
+ import 'node:crypto';
5
+ import 'node:fs';
6
+ import 'fast-glob';
7
+
8
+ const buildPlugin = (ctx, api)=>{
9
+ return {
10
+ name: "@serwist/vite:build",
11
+ enforce: "post",
12
+ apply: "build",
13
+ closeBundle: {
14
+ sequential: true,
15
+ order: ctx.userOptions?.integration?.closeBundleOrder,
16
+ async handler () {
17
+ if (!ctx.viteConfig.build.ssr && !ctx.options.disable) {
18
+ await api.generateSW();
19
+ }
20
+ }
21
+ },
22
+ async buildEnd (error) {
23
+ if (error) throw error;
24
+ }
25
+ };
26
+ };
27
+
28
+ /**
29
+ * Integrates Serwist into your Vite app.
30
+ * @param userOptions
31
+ * @returns
32
+ */ const serwist = (userOptions)=>{
33
+ const ctx = createContext(userOptions);
34
+ const api = createApi(ctx);
35
+ return [
36
+ mainPlugin(ctx, api),
37
+ buildPlugin(ctx, api),
38
+ devPlugin(ctx)
39
+ ];
40
+ };
41
+
42
+ export { serwist };
@@ -0,0 +1,2 @@
1
+ import type { RuntimeCaching } from "@serwist/build";
2
+ export declare const defaultCache: RuntimeCaching[];
@@ -0,0 +1,95 @@
1
+ const defaultCache = [
2
+ {
3
+ urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
4
+ handler: "CacheFirst",
5
+ options: {
6
+ cacheName: "google-fonts",
7
+ expiration: {
8
+ maxEntries: 4,
9
+ maxAgeSeconds: 365 * 24 * 60 * 60
10
+ }
11
+ }
12
+ },
13
+ {
14
+ urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
15
+ handler: "StaleWhileRevalidate",
16
+ options: {
17
+ cacheName: "static-font-assets",
18
+ expiration: {
19
+ maxEntries: 4,
20
+ maxAgeSeconds: 7 * 24 * 60 * 60
21
+ }
22
+ }
23
+ },
24
+ {
25
+ urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
26
+ handler: "StaleWhileRevalidate",
27
+ options: {
28
+ cacheName: "static-image-assets",
29
+ expiration: {
30
+ maxEntries: 64,
31
+ maxAgeSeconds: 24 * 60 * 60
32
+ }
33
+ }
34
+ },
35
+ {
36
+ urlPattern: /\.(?:js)$/i,
37
+ handler: "StaleWhileRevalidate",
38
+ options: {
39
+ cacheName: "static-js-assets",
40
+ expiration: {
41
+ maxEntries: 32,
42
+ maxAgeSeconds: 24 * 60 * 60
43
+ }
44
+ }
45
+ },
46
+ {
47
+ urlPattern: /\.(?:css|less)$/i,
48
+ handler: "StaleWhileRevalidate",
49
+ options: {
50
+ cacheName: "static-style-assets",
51
+ expiration: {
52
+ maxEntries: 32,
53
+ maxAgeSeconds: 24 * 60 * 60
54
+ }
55
+ }
56
+ },
57
+ {
58
+ urlPattern: /\.(?:json|xml|csv)$/i,
59
+ handler: "NetworkFirst",
60
+ options: {
61
+ cacheName: "static-data-assets",
62
+ expiration: {
63
+ maxEntries: 32,
64
+ maxAgeSeconds: 24 * 60 * 60
65
+ }
66
+ }
67
+ },
68
+ {
69
+ urlPattern: /\/api\/.*$/i,
70
+ handler: "NetworkFirst",
71
+ method: "GET",
72
+ options: {
73
+ cacheName: "apis",
74
+ expiration: {
75
+ maxEntries: 16,
76
+ maxAgeSeconds: 24 * 60 * 60
77
+ },
78
+ networkTimeoutSeconds: 10
79
+ }
80
+ },
81
+ {
82
+ urlPattern: /.*/i,
83
+ handler: "NetworkFirst",
84
+ options: {
85
+ cacheName: "others",
86
+ expiration: {
87
+ maxEntries: 32,
88
+ maxAgeSeconds: 24 * 60 * 60
89
+ },
90
+ networkTimeoutSeconds: 10
91
+ }
92
+ }
93
+ ];
94
+
95
+ export { defaultCache };
@@ -0,0 +1,8 @@
1
+ import type { ManifestTransform } from "@serwist/build";
2
+ import type { ResolvedConfig } from "vite";
3
+ import type { PluginOptions as BasePluginOptions } from "../../types.js";
4
+ import type { KitOptions } from "./types.js";
5
+ export declare const configurateSvelteKitOptions: (viteConfig: ResolvedConfig, kit: KitOptions, options: BasePluginOptions) => void;
6
+ export declare function createManifestTransform(base: string, webManifestName?: string, options?: KitOptions): ManifestTransform;
7
+ export declare function buildGlobPatterns(globPatterns?: string[]): string[];
8
+ export declare function buildGlobIgnores(globIgnores?: string[]): string[];
@@ -0,0 +1,9 @@
1
+ import type { Plugin } from "vite";
2
+ import type { PluginOptions } from "./types.js";
3
+ /**
4
+ * Integrates Serwist into your SvelteKit app.
5
+ * @param userOptions
6
+ * @returns
7
+ */
8
+ export declare const serwist: (userOptions?: PluginOptions) => Plugin[];
9
+ export * from "./types.js";
@@ -0,0 +1,151 @@
1
+ import { l as loadSerwistBuild, m as mainPlugin, d as devPlugin, c as createContext, a as createApi } from '../../main-dzWmj8BB.js';
2
+ import path from 'node:path';
3
+ import 'node:process';
4
+ import 'node:crypto';
5
+ import 'node:fs';
6
+ import 'fast-glob';
7
+
8
+ const configurateSvelteKitOptions = (viteConfig, kit, options)=>{
9
+ const clientOutDir = path.resolve(viteConfig.root, viteConfig.build.outDir, "../client");
10
+ // Kit fixes the service worker's name to 'service-worker.js'
11
+ options.swSrc = path.resolve(clientOutDir, "service-worker.js");
12
+ options.swDest = path.resolve(clientOutDir, "service-worker.js");
13
+ options.swUrl = "/service-worker.js";
14
+ // SvelteKit's outDir is `.svelte-kit/output/client`.
15
+ // We need to include the parent folder in globDirectory since SvelteKit will generate SSG in `.svelte-kit/output/prerendered` folder.
16
+ if (!options.globDirectory) {
17
+ options.globDirectory = path.resolve(clientOutDir, "..");
18
+ }
19
+ if (!options.manifestTransforms) {
20
+ options.manifestTransforms = [
21
+ createManifestTransform(viteConfig.base, undefined, kit)
22
+ ];
23
+ }
24
+ let buildAssetsDir = kit.appDir ?? "_app/";
25
+ if (buildAssetsDir[0] === "/") buildAssetsDir = buildAssetsDir.slice(1);
26
+ if (buildAssetsDir[buildAssetsDir.length - 1] !== "/") buildAssetsDir += "/";
27
+ if (!options.modifyURLPrefix) {
28
+ options.globPatterns = buildGlobPatterns(options.globPatterns);
29
+ if (kit.includeVersionFile) {
30
+ options.globPatterns.push(`client/${buildAssetsDir}version.json`);
31
+ }
32
+ }
33
+ // Exclude server assets: sw is built on SSR build
34
+ options.globIgnores = buildGlobIgnores(options.globIgnores);
35
+ // Vite 5 support: allow override dontCacheBustURLsMatching
36
+ if (!("dontCacheBustURLsMatching" in options)) {
37
+ options.dontCacheBustURLsMatching = new RegExp(`${buildAssetsDir}immutable/`);
38
+ }
39
+ };
40
+ function createManifestTransform(base, webManifestName, options) {
41
+ return async (entries)=>{
42
+ const defaultAdapterFallback = "prerendered/fallback.html";
43
+ const suffix = options?.trailingSlash === "always" ? "/" : "";
44
+ let adapterFallback = options?.adapterFallback;
45
+ let excludeFallback = false;
46
+ // the fallback will be always generated by SvelteKit.
47
+ // The adapter will copy the fallback only if it is provided in its options: we need to exclude it
48
+ if (!adapterFallback) {
49
+ adapterFallback = defaultAdapterFallback;
50
+ excludeFallback = true;
51
+ }
52
+ // the fallback will be always in .svelte-kit/output/prerendered/fallback.html
53
+ const manifest = entries.filter(({ url })=>!(excludeFallback && url === defaultAdapterFallback)).map((e)=>{
54
+ let url = e.url;
55
+ // client assets in `.svelte-kit/output/client` folder.
56
+ // SSG pages in `.svelte-kit/output/prerendered/pages` folder.
57
+ // fallback page in `.svelte-kit/output/prerendered` folder (fallback.html is the default).
58
+ if (url.startsWith("client/")) url = url.slice(7);
59
+ else if (url.startsWith("prerendered/pages/")) url = url.slice(18);
60
+ else if (url === defaultAdapterFallback) url = adapterFallback;
61
+ if (url.endsWith(".html")) {
62
+ if (url.startsWith("/")) url = url.slice(1);
63
+ if (url === "index.html") {
64
+ url = base;
65
+ } else {
66
+ const idx = url.lastIndexOf("/");
67
+ if (idx > -1) {
68
+ // abc/index.html -> abc/?
69
+ if (url.endsWith("/index.html")) url = `${url.slice(0, idx)}${suffix}`;
70
+ else url = `${url.substring(0, url.lastIndexOf("."))}${suffix}`;
71
+ } else {
72
+ // xxx.html -> xxx/?
73
+ url = `${url.substring(0, url.lastIndexOf("."))}${suffix}`;
74
+ }
75
+ }
76
+ }
77
+ e.url = url;
78
+ return e;
79
+ });
80
+ if (!webManifestName) return {
81
+ manifest
82
+ };
83
+ return {
84
+ manifest: manifest.filter((e)=>e.url !== webManifestName)
85
+ };
86
+ };
87
+ }
88
+ function buildGlobPatterns(globPatterns) {
89
+ if (globPatterns) {
90
+ if (!globPatterns.some((g)=>g.startsWith("prerendered/"))) globPatterns.push("prerendered/**/*.html");
91
+ if (!globPatterns.some((g)=>g.startsWith("client/"))) globPatterns.push("client/**/*.{js,css,ico,png,svg,webp,webmanifest}");
92
+ if (!globPatterns.some((g)=>g.includes("webmanifest"))) globPatterns.push("client/*.webmanifest");
93
+ return globPatterns;
94
+ }
95
+ return [
96
+ "client/**/*.{js,css,ico,png,svg,webp,webmanifest}",
97
+ "prerendered/**/*.html"
98
+ ];
99
+ }
100
+ function buildGlobIgnores(globIgnores) {
101
+ if (globIgnores) {
102
+ if (!globIgnores.some((g)=>g.startsWith("server/"))) globIgnores.push("server/*.*");
103
+ return globIgnores;
104
+ }
105
+ return [
106
+ "server/*.*"
107
+ ];
108
+ }
109
+
110
+ const serwistSveltePlugin = (ctx, api)=>{
111
+ return {
112
+ name: "@serwist/vite/integration-svelte:build",
113
+ apply: "build",
114
+ enforce: "pre",
115
+ closeBundle: {
116
+ sequential: true,
117
+ enforce: "pre",
118
+ async handler () {
119
+ if (api && !api.disabled && ctx.viteConfig.build.ssr) {
120
+ const [injectManifest, logSerwistResult] = await Promise.all([
121
+ loadSerwistBuild().then((m)=>m.injectManifest),
122
+ import('../../log-Dyh-Moyt.js').then((m)=>m.logSerwistResult)
123
+ ]);
124
+ // Inject the manifest
125
+ const buildResult = await injectManifest(ctx.options.injectManifest);
126
+ // Log Serwist result
127
+ logSerwistResult(buildResult, ctx.viteConfig);
128
+ }
129
+ }
130
+ }
131
+ };
132
+ };
133
+
134
+ /**
135
+ * Integrates Serwist into your SvelteKit app.
136
+ * @param userOptions
137
+ * @returns
138
+ */ const serwist = (userOptions = {})=>{
139
+ if (!userOptions.integration) userOptions.integration = {};
140
+ userOptions.integration.closeBundleOrder = "pre";
141
+ userOptions.integration.configureOptions = (viteConfig, options)=>configurateSvelteKitOptions(viteConfig, userOptions.kit ?? {}, options);
142
+ const ctx = createContext(userOptions);
143
+ const api = createApi(ctx);
144
+ return [
145
+ mainPlugin(ctx, api),
146
+ devPlugin(ctx),
147
+ serwistSveltePlugin(ctx, api)
148
+ ];
149
+ };
150
+
151
+ export { serwist };
@@ -0,0 +1,3 @@
1
+ import type { BuildResult } from "@serwist/build";
2
+ import type { ResolvedConfig } from "vite";
3
+ export declare function logSerwistResult(buildResult: Pick<BuildResult, "count" | "size" | "warnings">, viteOptions: ResolvedConfig): void;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "vite";
2
+ import type { SerwistViteContext } from "../../context.js";
3
+ import type { SerwistViteApi } from "../../types.js";
4
+ export declare const serwistSveltePlugin: (ctx: SerwistViteContext, api: SerwistViteApi) => Plugin<any>;
@@ -0,0 +1,23 @@
1
+ import type { KitConfig } from "@sveltejs/kit";
2
+ import type { PluginOptions as BasePluginOptions } from "../../types.js";
3
+ import type { Optional } from "../../utils-types.js";
4
+ export interface KitOptions extends Pick<KitConfig, "appDir"> {
5
+ /**
6
+ * @see https://kit.svelte.dev/docs/adapter-static#options-fallback
7
+ */
8
+ adapterFallback?: string;
9
+ /**
10
+ * `trailingSlash` in `+page.{ts,js}` or `+layout.{ts,js}` files.
11
+ * @default "never"
12
+ */
13
+ trailingSlash?: "never" | "always" | "ignore";
14
+ /**
15
+ * Should `"${appDir}/version.json"` be included in the service worker's precache manifest?
16
+ *
17
+ * @default false
18
+ */
19
+ includeVersionFile?: boolean;
20
+ }
21
+ export interface PluginOptions extends Optional<Omit<BasePluginOptions, "swSrc" | "swDest" | "swUrl">, "globDirectory"> {
22
+ kit?: KitOptions;
23
+ }
@@ -0,0 +1,26 @@
1
+ import { b as cyan, v as version, g as green, e as dim, y as yellow } from './main-dzWmj8BB.js';
2
+ import 'node:path';
3
+ import 'node:process';
4
+ import 'node:crypto';
5
+ import 'node:fs';
6
+ import 'fast-glob';
7
+
8
+ function logSerwistResult(buildResult, viteOptions) {
9
+ const { logLevel = "info" } = viteOptions;
10
+ if (logLevel === "silent") return;
11
+ const { count, size, warnings } = buildResult;
12
+ if (logLevel === "info") {
13
+ console.info([
14
+ "",
15
+ `${cyan(`@serwist/vite/integration-svelte v${version}`)} ${green("files generated.")}`,
16
+ `${green("✓")} ${count} precache entries ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
17
+ warnings && warnings.length > 0 ? yellow([
18
+ "⚠ warnings",
19
+ ...warnings.map((w)=>` ${w}`),
20
+ ""
21
+ ].join("\n")) : ""
22
+ ].join("\n"));
23
+ }
24
+ }
25
+
26
+ export { logSerwistResult };
package/dist/log.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { BuildResult } from "@serwist/build";
2
+ import type { ResolvedConfig } from "vite";
3
+ export declare const logSerwistResult: (buildResult: Pick<BuildResult, "count" | "size" | "warnings">, viteOptions: ResolvedConfig) => void;
@@ -0,0 +1,348 @@
1
+ import path, { resolve } from 'node:path';
2
+ import process$1 from 'node:process';
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import fg from 'fast-glob';
6
+
7
+ let enabled = true;
8
+ // Support both browser and node environments
9
+ const globalVar = typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : {};
10
+ /**
11
+ * Detect how much colors the current terminal supports
12
+ */ let supportLevel = 0 /* none */ ;
13
+ if (globalVar.process && globalVar.process.env && globalVar.process.stdout) {
14
+ const { FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM, COLORTERM } = globalVar.process.env;
15
+ if (NODE_DISABLE_COLORS || NO_COLOR || FORCE_COLOR === '0') {
16
+ enabled = false;
17
+ } else if (FORCE_COLOR === '1' || FORCE_COLOR === '2' || FORCE_COLOR === '3') {
18
+ enabled = true;
19
+ } else if (TERM === 'dumb') {
20
+ enabled = false;
21
+ } else if ('CI' in globalVar.process.env && [
22
+ 'TRAVIS',
23
+ 'CIRCLECI',
24
+ 'APPVEYOR',
25
+ 'GITLAB_CI',
26
+ 'GITHUB_ACTIONS',
27
+ 'BUILDKITE',
28
+ 'DRONE'
29
+ ].some((vendor)=>vendor in globalVar.process.env)) {
30
+ enabled = true;
31
+ } else {
32
+ enabled = process.stdout.isTTY;
33
+ }
34
+ if (enabled) {
35
+ // Windows supports 24bit True Colors since Windows 10 revision #14931,
36
+ // see https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/
37
+ if (process.platform === 'win32') {
38
+ supportLevel = 3 /* trueColor */ ;
39
+ } else {
40
+ if (COLORTERM && (COLORTERM === 'truecolor' || COLORTERM === '24bit')) {
41
+ supportLevel = 3 /* trueColor */ ;
42
+ } else if (TERM && (TERM.endsWith('-256color') || TERM.endsWith('256'))) {
43
+ supportLevel = 2 /* ansi256 */ ;
44
+ } else {
45
+ supportLevel = 1 /* ansi */ ;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ let options = {
51
+ enabled,
52
+ supportLevel
53
+ };
54
+ function kolorist(start, end, level = 1 /* ansi */ ) {
55
+ const open = `\x1b[${start}m`;
56
+ const close = `\x1b[${end}m`;
57
+ const regex = new RegExp(`\\x1b\\[${end}m`, 'g');
58
+ return (str)=>{
59
+ return options.enabled && options.supportLevel >= level ? open + ('' + str).replace(regex, open) + close : '' + str;
60
+ };
61
+ }
62
+ const dim = kolorist(2, 22);
63
+ const green = kolorist(32, 39);
64
+ const yellow = kolorist(33, 39);
65
+ const cyan = kolorist(36, 39);
66
+
67
+ var version = "8.2.0";
68
+
69
+ const logSerwistResult = (buildResult, viteOptions)=>{
70
+ const { logLevel = "info" } = viteOptions;
71
+ if (logLevel === "silent") return;
72
+ const { count, size, warnings } = buildResult;
73
+ if (logLevel === "info") {
74
+ console.info([
75
+ "",
76
+ `${cyan(`@serwist/vite v${version}`)} ${green("files generated.")}`,
77
+ `${green("✓")} ${count} precache entries ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
78
+ // log build warning
79
+ warnings && warnings.length > 0 ? yellow([
80
+ "⚠ warnings",
81
+ ...warnings.map((w)=>` ${w}`),
82
+ ""
83
+ ].join("\n")) : ""
84
+ ].join("\n"));
85
+ }
86
+ };
87
+
88
+ const loadSerwistBuild = async ()=>{
89
+ // "@serwist/build" is large and makes config loading slow.
90
+ // Since it is not always used, we only load this when it is needed.
91
+ try {
92
+ return await import('@serwist/build');
93
+ } catch (_) {
94
+ return require("@serwist/build");
95
+ }
96
+ };
97
+ const generateInjectManifest = async (options, viteOptions)=>{
98
+ // We will have something like this from swSrc:
99
+ /*
100
+ // sw.js
101
+ import { precacheAndRoute } from 'workbox-precaching'
102
+ // self.__WB_MANIFEST is default injection point
103
+ precacheAndRoute(self.__WB_MANIFEST)
104
+ */ const { build } = await import('vite');
105
+ const define = {
106
+ ...viteOptions.define ?? {}
107
+ };
108
+ define["process.env.NODE_ENV"] = JSON.stringify(options.mode);
109
+ const { format, plugins, rollupOptions } = options.injectManifestRollupOptions;
110
+ const parsedSwDest = path.parse(options.injectManifest.swDest);
111
+ await build({
112
+ root: viteOptions.root,
113
+ base: viteOptions.base,
114
+ resolve: viteOptions.resolve,
115
+ // Don't copy anything from public folder
116
+ publicDir: false,
117
+ build: {
118
+ sourcemap: viteOptions.build.sourcemap,
119
+ lib: {
120
+ entry: options.injectManifest.swSrc,
121
+ name: "app",
122
+ formats: [
123
+ format
124
+ ]
125
+ },
126
+ rollupOptions: {
127
+ ...rollupOptions,
128
+ plugins,
129
+ output: {
130
+ entryFileNames: parsedSwDest.base
131
+ }
132
+ },
133
+ outDir: parsedSwDest.dir,
134
+ emptyOutDir: false
135
+ },
136
+ configFile: false,
137
+ define
138
+ });
139
+ // If the user doesn't have an injectionPoint, skip injectManifest.
140
+ if (!options.injectManifest.injectionPoint) return;
141
+ await options.integration?.beforeBuildServiceWorker?.(options);
142
+ const resolvedInjectManifestOptions = {
143
+ ...options.injectManifest,
144
+ // This will not fail since there is an injectionPoint
145
+ swSrc: options.injectManifest.swDest
146
+ };
147
+ const { injectManifest } = await loadSerwistBuild();
148
+ // Inject the manifest
149
+ const buildResult = await injectManifest(resolvedInjectManifestOptions);
150
+ // Log workbox result
151
+ logSerwistResult(buildResult, viteOptions);
152
+ };
153
+
154
+ const createApi = (ctx)=>{
155
+ return {
156
+ get disabled () {
157
+ return ctx?.options?.disable;
158
+ },
159
+ async generateSW () {
160
+ if (ctx.options.disable) {
161
+ return undefined;
162
+ }
163
+ return await generateInjectManifest(ctx.options, ctx.viteConfig);
164
+ },
165
+ extendManifestEntries (fn) {
166
+ const { options } = ctx;
167
+ if (options.disable) return;
168
+ const result = fn(options.injectManifest.additionalPrecacheEntries || []);
169
+ if (result != null) {
170
+ options.injectManifest.additionalPrecacheEntries = result;
171
+ }
172
+ }
173
+ };
174
+ };
175
+
176
+ const createContext = (userOptions)=>{
177
+ return {
178
+ userOptions,
179
+ options: undefined,
180
+ viteConfig: undefined,
181
+ useImportRegister: false,
182
+ devEnvironment: false
183
+ };
184
+ };
185
+
186
+ const devPlugin = (ctx)=>{
187
+ return {
188
+ name: "@serwist/vite:dev",
189
+ apply: "serve",
190
+ configureServer () {
191
+ ctx.devEnvironment = true;
192
+ }
193
+ };
194
+ };
195
+
196
+ const INTERNAL_SERWIST_VIRTUAL = "virtual:internal-serwist";
197
+ const RESOLVED_INTERNAL_SERWIST_VIRTUAL = `\0${INTERNAL_SERWIST_VIRTUAL}`;
198
+
199
+ const buildManifestEntry = (publicDir, url)=>{
200
+ return new Promise((resolve$1, reject)=>{
201
+ const cHash = crypto.createHash("MD5");
202
+ const stream = fs.createReadStream(resolve(publicDir, url));
203
+ stream.on("error", (err)=>{
204
+ reject(err);
205
+ });
206
+ stream.on("data", (chunk)=>{
207
+ cHash.update(chunk);
208
+ });
209
+ stream.on("end", ()=>{
210
+ return resolve$1({
211
+ url,
212
+ revision: `${cHash.digest("hex")}`
213
+ });
214
+ });
215
+ });
216
+ };
217
+ const lookupAdditionalPrecacheEntries = (serwistOptions)=>{
218
+ return serwistOptions.additionalPrecacheEntries || [];
219
+ };
220
+ // we need to make icons relative, we can have for example icon entries with: /pwa.png
221
+ // fast-glob will not resolve absolute paths
222
+ const normalizeIconPath = (path)=>{
223
+ return path.startsWith("/") ? path.substring(1) : path;
224
+ };
225
+ const configureStaticAssets = async (resolvedPluginOptions, viteConfig)=>{
226
+ const { injectManifest, includeAssets } = resolvedPluginOptions;
227
+ const { publicDir } = viteConfig;
228
+ const globs = [];
229
+ const manifestEntries = lookupAdditionalPrecacheEntries(injectManifest);
230
+ if (includeAssets) {
231
+ // we need to make icons relative, we can have for example icon entries with: /pwa.png
232
+ // fast-glob will not resolve absolute paths
233
+ if (Array.isArray(includeAssets)) globs.push(...includeAssets.map(normalizeIconPath));
234
+ else globs.push(normalizeIconPath(includeAssets));
235
+ }
236
+ if (globs.length > 0) {
237
+ let assets = await fg(globs, {
238
+ cwd: publicDir,
239
+ onlyFiles: true,
240
+ unique: true
241
+ });
242
+ // we also need to remove from the list existing included by the user
243
+ if (manifestEntries.length > 0) {
244
+ const included = manifestEntries.map((me)=>{
245
+ if (typeof me === "string") return me;
246
+ else return me.url;
247
+ });
248
+ assets = assets.filter((a)=>!included.includes(a));
249
+ }
250
+ const assetsEntries = await Promise.all(assets.map((a)=>{
251
+ return buildManifestEntry(publicDir, a);
252
+ }));
253
+ manifestEntries.push(...assetsEntries);
254
+ }
255
+ if (manifestEntries.length > 0) {
256
+ injectManifest.additionalPrecacheEntries = manifestEntries;
257
+ }
258
+ };
259
+
260
+ const slash = (str)=>{
261
+ return str.replace(/\\/g, "/");
262
+ };
263
+ const resolveBasePath = (base)=>{
264
+ if (isAbsolute(base)) return base;
265
+ return !base.startsWith("/") && !base.startsWith("./") ? `/${base}` : base;
266
+ };
267
+ const isAbsolute = (url)=>{
268
+ return url.match(/^(?:[a-z]+:)?\/\//i);
269
+ };
270
+
271
+ const resolveOptions = async (options, viteConfig)=>{
272
+ const { type = "classic", mode = process$1.env.NODE_ENV || "production", injectRegister = "auto", registerType = "prompt", minify = true, base = viteConfig.base, includeAssets = undefined, useCredentials = false, disable = false, integration = {}, buildBase, ...injectManifest } = options;
273
+ const basePath = resolveBasePath(base);
274
+ // check typescript service worker for injectManifest strategy
275
+ const scope = options.scope || basePath;
276
+ let assetsDir = slash(viteConfig.build.assetsDir ?? "assets");
277
+ if (assetsDir[assetsDir.length - 1] !== "/") assetsDir += "/";
278
+ // remove './' prefix from assetsDir
279
+ const dontCacheBustURLsMatching = new RegExp(`^${assetsDir.replace(/^\.*?\//, "")}`);
280
+ const { plugins = [], rollupOptions = {}, rollupFormat = "es", swUrl = "/sw.js", swSrc, swDest, ...userInjectManifest } = injectManifest || {};
281
+ const resolvedPluginOptions = {
282
+ base: basePath,
283
+ type,
284
+ mode,
285
+ injectRegister,
286
+ registerType,
287
+ useCredentials,
288
+ swUrl,
289
+ injectManifest: {
290
+ dontCacheBustURLsMatching,
291
+ ...userInjectManifest,
292
+ swSrc: path.resolve(viteConfig.root, swSrc),
293
+ swDest: path.resolve(viteConfig.root, viteConfig.build.outDir, swDest),
294
+ disablePrecacheManifest: !viteConfig.isProduction
295
+ },
296
+ scope,
297
+ minify,
298
+ includeAssets,
299
+ disable,
300
+ integration,
301
+ buildBase: buildBase ?? basePath,
302
+ injectManifestRollupOptions: {
303
+ plugins,
304
+ rollupOptions,
305
+ format: rollupFormat
306
+ }
307
+ };
308
+ // calculate hash only when required
309
+ const calculateHash = !resolvedPluginOptions.disable && resolvedPluginOptions.includeAssets && viteConfig.command === "build";
310
+ if (calculateHash) await configureStaticAssets(resolvedPluginOptions, viteConfig);
311
+ return resolvedPluginOptions;
312
+ };
313
+
314
+ const mainPlugin = (ctx, api)=>{
315
+ return {
316
+ name: "@serwist/vite",
317
+ enforce: "pre",
318
+ config () {
319
+ return {
320
+ ssr: {
321
+ noExternal: []
322
+ }
323
+ };
324
+ },
325
+ async configResolved (config) {
326
+ ctx.viteConfig = config;
327
+ ctx.userOptions?.integration?.configureOptions?.(config, ctx.userOptions);
328
+ ctx.options = await resolveOptions(ctx.userOptions, config);
329
+ },
330
+ resolveId (id) {
331
+ if (id === INTERNAL_SERWIST_VIRTUAL) {
332
+ return RESOLVED_INTERNAL_SERWIST_VIRTUAL;
333
+ }
334
+ return undefined;
335
+ },
336
+ load (id) {
337
+ if (id === RESOLVED_INTERNAL_SERWIST_VIRTUAL) {
338
+ return `export const swUrl = "${path.posix.join(ctx.options.buildBase, ctx.options.swUrl)}";
339
+ export const swScope = "${ctx.options.scope}";
340
+ export const swType = "${ctx.devEnvironment ? "module" : ctx.options.type}";`;
341
+ }
342
+ return undefined;
343
+ },
344
+ api
345
+ };
346
+ };
347
+
348
+ export { createApi as a, cyan as b, createContext as c, devPlugin as d, dim as e, green as g, loadSerwistBuild as l, mainPlugin as m, version as v, yellow as y };
@@ -0,0 +1,5 @@
1
+ import type * as SerwistBuild from "@serwist/build";
2
+ import type { ResolvedConfig } from "vite";
3
+ import type { ResolvedPluginOptions } from "./types.js";
4
+ export declare const loadSerwistBuild: () => Promise<typeof SerwistBuild>;
5
+ export declare const generateInjectManifest: (options: ResolvedPluginOptions, viteOptions: ResolvedConfig) => Promise<void>;
@@ -0,0 +1,3 @@
1
+ import type { ResolvedConfig } from "vite";
2
+ import type { PluginOptions, ResolvedPluginOptions } from "./types.js";
3
+ export declare const resolveOptions: (options: PluginOptions, viteConfig: ResolvedConfig) => Promise<ResolvedPluginOptions>;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "vite";
2
+ import type { SerwistViteContext } from "../context.js";
3
+ import type { SerwistViteApi } from "../types.js";
4
+ export declare const buildPlugin: (ctx: SerwistViteContext, api: SerwistViteApi) => Plugin<any>;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ import type { SerwistViteContext } from "../context.js";
3
+ export declare const devPlugin: (ctx: SerwistViteContext) => Plugin;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "vite";
2
+ import type { SerwistViteContext } from "../context.js";
3
+ import type { SerwistViteApi } from "../types.js";
4
+ export declare const mainPlugin: (ctx: SerwistViteContext, api: SerwistViteApi) => Plugin<any>;
@@ -0,0 +1,176 @@
1
+ import type { InjectManifestOptions, ManifestEntry } from "@serwist/build";
2
+ import type { RollupOptions } from "rollup";
3
+ import type { Plugin, ResolvedConfig } from "vite";
4
+ export type InjectManifestVitePlugins = string[] | ((vitePluginIds: string[]) => string[]);
5
+ export interface CustomInjectManifestOptions extends Omit<InjectManifestOptions, "disablePrecacheManifest"> {
6
+ /**
7
+ * The URL to the service worker.
8
+ * @default "/sw.js"
9
+ */
10
+ swUrl?: string;
11
+ /**
12
+ * Configure the format to use in the Rollup build.
13
+ *
14
+ * @default 'es'
15
+ */
16
+ rollupFormat?: "es" | "iife";
17
+ /**
18
+ * Since `v0.15.0` you can add plugins to build your service worker.
19
+ *
20
+ * When using `injectManifest` there are 2 builds, your application and the service worker.
21
+ * 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.
22
+ * Both configurations cannot be shared, and so you'll need to duplicate the configuration, with the exception of `define`.
23
+ *
24
+ * **WARN**: this option is for advanced usage, be aware that you may break your application build.
25
+ */
26
+ plugins?: Plugin[];
27
+ /**
28
+ * 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.
29
+ */
30
+ rollupOptions?: Omit<RollupOptions, "plugins" | "output">;
31
+ }
32
+ export interface SerwistViteHooks {
33
+ beforeBuildServiceWorker?: (options: ResolvedPluginOptions) => void | Promise<void>;
34
+ closeBundleOrder?: "pre" | "post" | null;
35
+ configureOptions?: (viteOptions: ResolvedConfig, options: PluginOptions) => void | Promise<void>;
36
+ }
37
+ /**
38
+ * Plugin options.
39
+ */
40
+ export interface BasePluginOptions {
41
+ /**
42
+ * Build mode
43
+ *
44
+ * @default
45
+ * process.env.NODE_ENV // or "production" if undefined
46
+ */
47
+ mode?: "development" | "production";
48
+ /**
49
+ * The service worker type.
50
+ *
51
+ * @default "classic"
52
+ */
53
+ type?: WorkerType;
54
+ /**
55
+ * The scope to register the Service Worker
56
+ *
57
+ * @default `viteOptions.base`
58
+ */
59
+ scope?: string;
60
+ /**
61
+ * Inject the service worker register inlined in the index.html
62
+ *
63
+ * If set to "auto", depends on whether you used the `import { registerSW } from 'virtual:pwa-register'`
64
+ * it will do nothing or use the `script` mode
65
+ *
66
+ * `"inline"` - inject a simple register, inlined with the generated html
67
+ *
68
+ * `"script"` - inject `<script/>` in `<head>` with `src` attribute to a generated script to register the service worker
69
+ *
70
+ * `"script-defer"` - inject `<script defer />` in `<head>`, with `src` attribute to a generated script to register the service worker
71
+ *
72
+ * `null` - do nothing. You will need to register the service worker yourself or import `registerSW` from `virtual:pwa-register`.
73
+ *
74
+ * @default "auto"
75
+ */
76
+ injectRegister: "inline" | "script" | "script-defer" | "auto" | null | false;
77
+ /**
78
+ * Mode for the virtual register.
79
+ * This is NOT available if `injectRegister` is set to `"inline"` or `"script"`
80
+ *
81
+ * `"prompt"` - you will need to show a popup/dialog to the user to confirm the reload.
82
+ *
83
+ * `"autoUpdate"` - when new content is available, the new service worker will update caches and reload all browser
84
+ * windows/tabs with the application open automatically, it must take the control for the application to work
85
+ * properly.
86
+ *
87
+ * @default "prompt"
88
+ */
89
+ registerType?: "prompt" | "autoUpdate";
90
+ /**
91
+ * Minify the generated manifest
92
+ *
93
+ * @default true
94
+ */
95
+ minify: boolean;
96
+ /**
97
+ * Whether to add the `crossorigin="use-credentials"` attribute to `<link rel="manifest">`
98
+ * @default false
99
+ */
100
+ useCredentials?: boolean;
101
+ /**
102
+ * Override Vite's base options for `@serwist/vite`.
103
+ *
104
+ * @default viteOptions.base
105
+ */
106
+ base?: string;
107
+ /**
108
+ * `public` resources to be added to the PWA manifest.
109
+ *
110
+ * You don't need to add `manifest` icons here, it will be auto included.
111
+ *
112
+ * The `public` directory will be resolved from Vite's `publicDir` option directory.
113
+ */
114
+ includeAssets: string | string[] | undefined;
115
+ /**
116
+ * Whether Serwist should be disabled.
117
+ *
118
+ * @default false
119
+ */
120
+ disable: boolean;
121
+ /**
122
+ * `@serwist/vite` integration.
123
+ */
124
+ integration?: SerwistViteHooks;
125
+ /**
126
+ * When Vite's build folder is not the same as your base root folder, configure it here.
127
+ *
128
+ * 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`.
129
+ *
130
+ * This option will be used to configure the path for the service worker, "registerSW.js" and the web manifest assets.
131
+ *
132
+ * For example, if your base path is `/`, then, in your Laravel PWA configuration use `buildPath: '/build/'`.
133
+ *
134
+ * By default: `vite.base`.
135
+ */
136
+ buildBase?: string;
137
+ }
138
+ export type PluginOptions = Partial<BasePluginOptions> & CustomInjectManifestOptions;
139
+ export interface InjectManifestRollupOptions {
140
+ format: "es" | "iife";
141
+ plugins: Plugin[];
142
+ rollupOptions: RollupOptions;
143
+ }
144
+ export interface ResolvedPluginOptions extends Required<BasePluginOptions>, Required<Pick<CustomInjectManifestOptions, "swUrl">> {
145
+ injectManifest: InjectManifestOptions;
146
+ injectManifestRollupOptions: InjectManifestRollupOptions;
147
+ }
148
+ export interface ShareTargetFiles {
149
+ name: string;
150
+ accept: string | string[];
151
+ }
152
+ /**
153
+ * @see https://developer.mozilla.org/en-US/docs/Web/Manifest/launch_handler#launch_handler_item_values
154
+ */
155
+ export type LaunchHandlerClientMode = "auto" | "focus-existing" | "navigate-existing" | "navigate-new";
156
+ export type Display = "fullscreen" | "standalone" | "minimal-ui" | "browser";
157
+ export type DisplayOverride = Display | "window-controls-overlay";
158
+ export type IconPurpose = "monochrome" | "maskable" | "any";
159
+ interface Nothing {
160
+ }
161
+ /**
162
+ * type StringLiteralUnion<'maskable'> = 'maskable' | string
163
+ * This has auto completion whereas `'maskable' | string` doesn't
164
+ * Adapted from https://github.com/microsoft/TypeScript/issues/29729
165
+ */
166
+ export type StringLiteralUnion<T extends U, U = string> = T | (U & Nothing);
167
+ export interface SerwistViteApi {
168
+ /**
169
+ * Is the plugin disabled?
170
+ */
171
+ disabled: boolean;
172
+ extendManifestEntries(fn: ExtendManifestEntriesHook): void;
173
+ generateSW(): Promise<void>;
174
+ }
175
+ export type ExtendManifestEntriesHook = (manifestEntries: (string | ManifestEntry)[]) => (string | ManifestEntry)[] | undefined;
176
+ export {};
@@ -0,0 +1 @@
1
+ export type Optional<T, U extends keyof T> = Omit<T, U> & Partial<Pick<T, U>>;
@@ -0,0 +1,4 @@
1
+ export declare const slash: (str: string) => string;
2
+ export declare const resolveBasePath: (base: string) => string;
3
+ export declare const isAbsolute: (url: string) => RegExpMatchArray | null;
4
+ export declare const normalizePath: (path: string) => string;
package/package.json ADDED
@@ -0,0 +1,139 @@
1
+ {
2
+ "name": "@serwist/vite",
3
+ "version": "8.2.0",
4
+ "type": "module",
5
+ "description": "A module that integrates Serwist into your Vite application.",
6
+ "files": [
7
+ "dist",
8
+ "!dist/dts"
9
+ ],
10
+ "keywords": [
11
+ "react",
12
+ "remix",
13
+ "vue",
14
+ "preact",
15
+ "svelte",
16
+ "sveltekit",
17
+ "solidjs",
18
+ "vite",
19
+ "vite-plugin",
20
+ "serwist",
21
+ "serwistjs",
22
+ "pwa",
23
+ "sw",
24
+ "service worker",
25
+ "web",
26
+ "service-worker"
27
+ ],
28
+ "author": "antfu <anthonyfu117@hotmail.com>, Serwist's Team",
29
+ "license": "MIT",
30
+ "repository": "serwist/serwist",
31
+ "bugs": "https://github.com/serwist/serwist/issues",
32
+ "homepage": "https://serwist.vercel.app",
33
+ "sideEffects": false,
34
+ "main": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "typesVersions": {
37
+ "*": {
38
+ "browser": [
39
+ "./dist/index.browser.d.ts"
40
+ ],
41
+ "worker": [
42
+ "./dist/index.worker.d.ts"
43
+ ],
44
+ "integration-*": [
45
+ "./dist/integration/*/index.d.ts"
46
+ ]
47
+ }
48
+ },
49
+ "exports": {
50
+ ".": {
51
+ "import": {
52
+ "types": "./dist/index.d.ts",
53
+ "default": "./dist/index.js"
54
+ }
55
+ },
56
+ "./browser": {
57
+ "import": {
58
+ "types": "./dist/index.browser.d.ts",
59
+ "default": "./dist/index.browser.js"
60
+ }
61
+ },
62
+ "./worker": {
63
+ "import": {
64
+ "types": "./dist/index.worker.d.ts",
65
+ "default": "./dist/index.worker.js"
66
+ }
67
+ },
68
+ "./integration-*": {
69
+ "import": {
70
+ "types": "./dist/integration/*/index.d.ts",
71
+ "default": "./dist/integration/*/index.js"
72
+ }
73
+ },
74
+ "./package.json": "./package.json"
75
+ },
76
+ "dependencies": {
77
+ "debug": "4.3.4",
78
+ "fast-glob": "3.3.2",
79
+ "pretty-bytes": "6.1.1",
80
+ "@serwist/build": "8.2.0",
81
+ "@serwist/window": "8.2.0"
82
+ },
83
+ "devDependencies": {
84
+ "@playwright/test": "1.40.1",
85
+ "@sveltejs/kit": "2.0.6",
86
+ "@types/debug": "4.1.12",
87
+ "@types/node": "20.10.5",
88
+ "@types/prompts": "2.4.9",
89
+ "@types/react": "18.2.45",
90
+ "bumpp": "9.2.1",
91
+ "eslint": "8.56.0",
92
+ "kolorist": "1.8.0",
93
+ "preact": "10.19.3",
94
+ "prompts": "2.4.2",
95
+ "publint": "0.2.7",
96
+ "react": "18.2.0",
97
+ "rollup": "4.9.1",
98
+ "solid-js": "1.8.7",
99
+ "svelte": "5.0.0-next.26",
100
+ "typescript": "5.4.0-dev.20231226",
101
+ "vite": "5.0.10",
102
+ "vue": "3.3.13",
103
+ "@serwist/constants": "8.2.0"
104
+ },
105
+ "peerDependencies": {
106
+ "@sveltejs/kit": "^1.0.0 || ^2.0.0",
107
+ "preact": "^10.0.0",
108
+ "react": "^18.0.0",
109
+ "solid-js": "^1.8.7",
110
+ "svelte": "^4.0.0 || ^5.0.0",
111
+ "vite": "^5.0.0",
112
+ "vue": "^3.0.0"
113
+ },
114
+ "peerDependenciesMeta": {
115
+ "@sveltejs/kit": {
116
+ "optional": true
117
+ },
118
+ "preact": {
119
+ "optional": true
120
+ },
121
+ "react": {
122
+ "optional": true
123
+ },
124
+ "solid-js": {
125
+ "optional": true
126
+ },
127
+ "svelte": {
128
+ "optional": true
129
+ },
130
+ "vue": {
131
+ "optional": true
132
+ }
133
+ },
134
+ "scripts": {
135
+ "build": "rimraf dist && cross-env NODE_ENV=production rollup --config rollup.config.js",
136
+ "lint": "eslint src --ext ts,tsx,js,jsx,cjs,mjs",
137
+ "typecheck": "tsc"
138
+ }
139
+ }