@genesislcap/vite-builder 15.80.0 → 15.81.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/README.md +17 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/pwa.d.ts +39 -0
- package/dist/pwa.d.ts.map +1 -0
- package/dist/pwa.js +453 -0
- package/dist/vite.d.ts +2 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +5 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -2,7 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Builder wrapping [Vite](https://vitejs.dev/) bundler.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## PWA and Service Worker
|
|
6
|
+
|
|
7
|
+
The `pwa` and `serviceWorker` options in `genx.config.ts` are supported by the Vite builder, matching the Webpack builder:
|
|
8
|
+
|
|
9
|
+
- `pwa`: emits `manifest.json` and injects `<link rel="manifest">`, `theme-color` and, when `ios` is set, the Apple meta and touch-icon tags into `index.html`. Icon and manifest URLs use `pwa.publicPath`, falling back to `PUBLIC_PATH`.
|
|
10
|
+
- Icons under `public/` are served from there. Other icon paths are resolved from the app root and emitted to `destination` (default `icons/`). When two different files share a name there, the later one gets a content hash appended.
|
|
11
|
+
- Icons are not resized, so a raster icon needs one entry per size. A warning is logged when one file is declared at several sizes.
|
|
12
|
+
- Tags `index.html` already has (same `meta` name, or same `link` rel and sizes) are not added again.
|
|
13
|
+
- `serviceWorker`: compiles `serviceWorker.worker` (relative to `src/`) into a classic script at `assetName` (default: the worker's file name with a `.js` extension). `assetName` may include a directory.
|
|
14
|
+
- `registration` is added to `index.html`. Without it, genx adds a script that registers the worker once the page has loaded. Set `registration: false` when the app registers the worker itself.
|
|
15
|
+
- Workbox `injectManifest` fills `self.__WB_MANIFEST` with every emitted file except those matched by `exclude`. As in the Webpack builder, strings match as prefixes, functions receive `{ asset }`, and the default is `[/\.map$/, /^manifest.*\.js$/]`.
|
|
16
|
+
- Files Vite names with a content hash are precached without a revision. Every other file, including those copied from `public/` and icons emitted under a fixed name, gets one, so a changed file is fetched again.
|
|
17
|
+
- Files over `maximumFileSizeToCache` that `exclude` doesn't already drop are left out of the precache with a warning.
|
|
18
|
+
- `precache: false` builds a worker that doesn't precache, so it needn't reference `self.__WB_MANIFEST`.
|
|
19
|
+
- Under `genx dev` the worker is bundled on request and served at `assetName` under the public path, the URL it has in the build, with an empty precache manifest.
|
|
20
|
+
|
|
21
|
+
When neither option is configured, output remains unchanged.
|
|
6
22
|
|
|
7
23
|
## Installation
|
|
8
24
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;yBA6CrC,KAAK,YAAY;AAAvC,wBAgSE"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ const node_fs_1 = require("node:fs");
|
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
6
|
const build_kit_1 = require("@genesislcap/build-kit");
|
|
7
7
|
const consola_1 = tslib_1.__importDefault(require("consola"));
|
|
8
|
+
const pwa_js_1 = require("./pwa.js");
|
|
8
9
|
const vite_js_1 = require("./vite.js");
|
|
9
10
|
/**
|
|
10
11
|
* There seems to be various documented issues using Vite in a monorepo setup that are impacted by various factors.
|
|
@@ -154,6 +155,7 @@ exports.default = (ctx) => tslib_1.__awaiter(void 0, void 0, void 0, function* (
|
|
|
154
155
|
typescript: { tsconfigPath, buildMode },
|
|
155
156
|
})
|
|
156
157
|
: null;
|
|
158
|
+
const pwaPlugins = yield (0, pwa_js_1.resolvePwaPlugins)(ctx);
|
|
157
159
|
const baseHrefPlugin = {
|
|
158
160
|
name: 'genesis-base-href',
|
|
159
161
|
transformIndexHtml(html) {
|
|
@@ -179,6 +181,7 @@ exports.default = (ctx) => tslib_1.__awaiter(void 0, void 0, void 0, function* (
|
|
|
179
181
|
insertEntryPoint: true,
|
|
180
182
|
},
|
|
181
183
|
}),
|
|
184
|
+
...pwaPlugins,
|
|
182
185
|
],
|
|
183
186
|
resolve: {
|
|
184
187
|
alias: Object.assign(Object.assign({}, alias), { 'foundationRapid/RapidDesignSystem': '@genesislcap/rapid-design-system' }),
|
package/dist/pwa.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BuildContext, PWAIcon, PWAManifest } from '@genesislcap/build-kit';
|
|
2
|
+
import type { HtmlTagDescriptor, Plugin, ResolvedConfig } from 'vite';
|
|
3
|
+
import type { VitePWAOptions } from 'vite-plugin-pwa';
|
|
4
|
+
import type { ManifestTransform } from 'workbox-build';
|
|
5
|
+
type ServiceWorkerConfig = BuildContext['config']['serviceWorker'];
|
|
6
|
+
export interface ManifestIcon {
|
|
7
|
+
src: string;
|
|
8
|
+
sizes: string;
|
|
9
|
+
type: string;
|
|
10
|
+
purpose?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedManifestIcons {
|
|
13
|
+
icons: ManifestIcon[];
|
|
14
|
+
appleTags: HtmlTagDescriptor[];
|
|
15
|
+
/** Output file name to source path, for icons that are not served from `public/` */
|
|
16
|
+
files: Map<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare const iconMimeType: (src: string) => string;
|
|
19
|
+
export declare const resolveManifestIcons: (icons: PWAIcon | PWAIcon[] | undefined, base: string, cwd: string) => ResolvedManifestIcons;
|
|
20
|
+
export declare const appleHeadTags: (pwa: PWAManifest) => HtmlTagDescriptor[];
|
|
21
|
+
export declare const resolveManifest: (pwa: PWAManifest, base: string, cwd: string) => {
|
|
22
|
+
manifest: Record<string, unknown>;
|
|
23
|
+
appleTags: HtmlTagDescriptor[];
|
|
24
|
+
iconFiles: Map<string, string>;
|
|
25
|
+
};
|
|
26
|
+
export declare const createManifestPlugin: (pwa: PWAManifest, cwd: string) => Plugin;
|
|
27
|
+
/**
|
|
28
|
+
* Applies `exclude` before the size limit, as workbox-webpack-plugin does, and precaches only the
|
|
29
|
+
* files in `hashedFiles` without a revision.
|
|
30
|
+
*/
|
|
31
|
+
export declare const createPrecacheTransform: ({ exclude, maximumFileSizeToCache }: ServiceWorkerConfig, hashedFiles?: ReadonlySet<string>) => ManifestTransform;
|
|
32
|
+
export declare const createServiceWorkerOptions: (serviceWorker: ServiceWorkerConfig, hashedFiles?: ReadonlySet<string>) => Partial<VitePWAOptions>;
|
|
33
|
+
export declare const bundleServiceWorker: (swSrc: string, config: ResolvedConfig) => Promise<{
|
|
34
|
+
code: string;
|
|
35
|
+
moduleIds: Set<string>;
|
|
36
|
+
}>;
|
|
37
|
+
export declare const resolvePwaPlugins: (ctx: BuildContext) => Promise<Plugin[]>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=pwa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,EAGP,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAShC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,cAAc,EAAU,MAAM,MAAM,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,KAAK,mBAAmB,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,eAAe,CAAC,CAAC;AAGnE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,oFAAoF;IACpF,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B;AAsED,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAK1C,CAAC;AAuCF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,OAAO,GAAG,OAAO,EAAE,GAAG,SAAS,EACtC,MAAM,MAAM,EACZ,KAAK,MAAM,KACV,qBA4BF,CAAC;AAWF,eAAO,MAAM,aAAa,GAAI,KAAK,WAAW,KAAG,iBAAiB,EAqBjE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,WAAW,EAAE,MAAM,MAAM,EAAE,KAAK,MAAM;;;;CAc1E,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,KAAK,WAAW,EAAE,KAAK,MAAM,KAAG,MAqDpE,CAAC;AA0DF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAEhC,qCAAqC,mBAAmB,EACxD,cAAa,WAAW,CAAC,MAAM,CAAa,KAC3C,iBAeF,CAAC;AAWJ,eAAO,MAAM,0BAA0B,GACrC,eAAe,mBAAmB,EAClC,cAAa,WAAW,CAAC,MAAM,CAAa,KAC3C,OAAO,CAAC,cAAc,CA4BxB,CAAC;AAmCF,eAAO,MAAM,mBAAmB,GAAU,OAAO,MAAM,EAAE,QAAQ,cAAc;;;EAyB9E,CAAC;AA2DF,eAAO,MAAM,iBAAiB,GAAU,KAAK,YAAY,KAAG,OAAO,CAAC,MAAM,EAAE,CAyB3E,CAAC"}
|
package/dist/pwa.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePwaPlugins = exports.bundleServiceWorker = exports.createServiceWorkerOptions = exports.createPrecacheTransform = exports.createManifestPlugin = exports.resolveManifest = exports.appleHeadTags = exports.resolveManifestIcons = exports.iconMimeType = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const build_kit_1 = require("@genesislcap/build-kit");
|
|
9
|
+
const consola_1 = tslib_1.__importDefault(require("consola"));
|
|
10
|
+
const vite_js_1 = require("./vite.js");
|
|
11
|
+
const MANIFEST_FILE_NAME = 'manifest.json';
|
|
12
|
+
// workbox-webpack-plugin's default `exclude`
|
|
13
|
+
const DEFAULT_SW_EXCLUDE = [/\.map$/, /^manifest.*\.js$/];
|
|
14
|
+
// Workbox's default `maximumFileSizeToCacheInBytes`
|
|
15
|
+
const DEFAULT_MAXIMUM_FILE_SIZE_TO_CACHE = 2097152;
|
|
16
|
+
// webpack-pwa-manifest options (snake_cased) that are not Web App Manifest members
|
|
17
|
+
const NON_MANIFEST_KEYS = [
|
|
18
|
+
'crossorigin',
|
|
19
|
+
'filename',
|
|
20
|
+
'fingerprints',
|
|
21
|
+
'icon',
|
|
22
|
+
'include_directory',
|
|
23
|
+
'inject',
|
|
24
|
+
'ios',
|
|
25
|
+
'public_path',
|
|
26
|
+
'use_webpack_public_path',
|
|
27
|
+
];
|
|
28
|
+
const APPLE_META_TAGS = [
|
|
29
|
+
'apple-mobile-web-app-title',
|
|
30
|
+
'apple-mobile-web-app-capable',
|
|
31
|
+
'apple-mobile-web-app-status-bar-style',
|
|
32
|
+
];
|
|
33
|
+
const ICON_MIME_TYPES = {
|
|
34
|
+
svg: 'image/svg+xml',
|
|
35
|
+
png: 'image/png',
|
|
36
|
+
ico: 'image/x-icon',
|
|
37
|
+
webp: 'image/webp',
|
|
38
|
+
jpg: 'image/jpeg',
|
|
39
|
+
jpeg: 'image/jpeg',
|
|
40
|
+
};
|
|
41
|
+
const ICON_HASH_LENGTH = 8;
|
|
42
|
+
const PUBLIC_DIR_PREFIX = /^(\.\/)?public\//;
|
|
43
|
+
const REMOTE_SRC = /^(https?:|data:)/;
|
|
44
|
+
const withTrailingSlash = (path) => (path.endsWith('/') ? path : `${path}/`);
|
|
45
|
+
const formatSize = (size) => /^\d+$/.test(String(size)) ? `${size}x${size}` : String(size);
|
|
46
|
+
const headTag = (tag, attrs) => ({
|
|
47
|
+
tag,
|
|
48
|
+
attrs,
|
|
49
|
+
injectTo: 'head',
|
|
50
|
+
});
|
|
51
|
+
const parseAttrs = (source) => {
|
|
52
|
+
var _a, _b, _c;
|
|
53
|
+
const attrs = {};
|
|
54
|
+
const pattern = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
|
|
55
|
+
for (let match = pattern.exec(source); match; match = pattern.exec(source)) {
|
|
56
|
+
attrs[match[1].toLowerCase()] = (_c = (_b = (_a = match[2]) !== null && _a !== void 0 ? _a : match[3]) !== null && _b !== void 0 ? _b : match[4]) !== null && _c !== void 0 ? _c : '';
|
|
57
|
+
}
|
|
58
|
+
return attrs;
|
|
59
|
+
};
|
|
60
|
+
const htmlTags = (html) => {
|
|
61
|
+
const tags = [];
|
|
62
|
+
const pattern = /<(link|meta|script)\b([^>]*)>/gi;
|
|
63
|
+
for (let match = pattern.exec(html); match; match = pattern.exec(html)) {
|
|
64
|
+
tags.push({ tag: match[1].toLowerCase(), attrs: parseAttrs(match[2]) });
|
|
65
|
+
}
|
|
66
|
+
return tags;
|
|
67
|
+
};
|
|
68
|
+
const headTagKey = (tag, attrs) => {
|
|
69
|
+
const value = (name) => { var _a; return String((_a = attrs[name]) !== null && _a !== void 0 ? _a : '').toLowerCase(); };
|
|
70
|
+
return tag === 'meta' ? `meta:${value('name')}` : `${tag}:${value('rel')}:${value('sizes')}`;
|
|
71
|
+
};
|
|
72
|
+
const iconMimeType = (src) => {
|
|
73
|
+
var _a, _b;
|
|
74
|
+
const dataUriType = (_a = /^data:([^;,]+)/.exec(src)) === null || _a === void 0 ? void 0 : _a[1];
|
|
75
|
+
if (dataUriType)
|
|
76
|
+
return dataUriType;
|
|
77
|
+
const ext = (0, node_path_1.extname)(src.split(/[?#]/)[0]).slice(1).toLowerCase();
|
|
78
|
+
return (_b = ICON_MIME_TYPES[ext]) !== null && _b !== void 0 ? _b : 'image/png';
|
|
79
|
+
};
|
|
80
|
+
exports.iconMimeType = iconMimeType;
|
|
81
|
+
const iconOutputDir = (destination = 'icons') => destination.replace(/^\.?\/+/, '').replace(/\/+$/, '');
|
|
82
|
+
// Distinct files that share a base name would otherwise be emitted over each other
|
|
83
|
+
const uniqueIconFileName = (fileName, file, files) => {
|
|
84
|
+
const claimedBy = files.get(fileName);
|
|
85
|
+
if (claimedBy === undefined || claimedBy === file)
|
|
86
|
+
return fileName;
|
|
87
|
+
const { dir, name, ext } = node_path_1.posix.parse(fileName);
|
|
88
|
+
const hash = (0, node_crypto_1.createHash)('sha256')
|
|
89
|
+
.update((0, node_fs_1.readFileSync)(file))
|
|
90
|
+
.digest('hex')
|
|
91
|
+
.slice(0, ICON_HASH_LENGTH);
|
|
92
|
+
return node_path_1.posix.join(dir, `${name}-${hash}${ext}`);
|
|
93
|
+
};
|
|
94
|
+
const locateIcon = (icon, base, cwd, files) => {
|
|
95
|
+
const { src } = icon;
|
|
96
|
+
const publicUrl = `${base}${src.replace(PUBLIC_DIR_PREFIX, '').replace(/^\//, '')}`;
|
|
97
|
+
if (REMOTE_SRC.test(src))
|
|
98
|
+
return src;
|
|
99
|
+
if (PUBLIC_DIR_PREFIX.test(src) || src.startsWith('/'))
|
|
100
|
+
return publicUrl;
|
|
101
|
+
const file = (0, node_path_1.resolve)(cwd, src);
|
|
102
|
+
if ((0, node_fs_1.existsSync)(file)) {
|
|
103
|
+
const fileName = uniqueIconFileName(node_path_1.posix.join(iconOutputDir(icon.destination), (0, node_path_1.basename)(src)), file, files);
|
|
104
|
+
files.set(fileName, file);
|
|
105
|
+
return `${base}${fileName}`;
|
|
106
|
+
}
|
|
107
|
+
if (!(0, node_fs_1.existsSync)((0, node_path_1.resolve)(cwd, 'public', src))) {
|
|
108
|
+
consola_1.default.warn(`PWA icon ${src} not found in ${cwd} or its public directory.`);
|
|
109
|
+
}
|
|
110
|
+
return publicUrl;
|
|
111
|
+
};
|
|
112
|
+
const resolveManifestIcons = (icons, base, cwd) => {
|
|
113
|
+
var _a;
|
|
114
|
+
const resolved = { icons: [], appleTags: [], files: new Map() };
|
|
115
|
+
for (const icon of icons ? [icons].flat() : []) {
|
|
116
|
+
const url = locateIcon(icon, base, cwd, resolved.files);
|
|
117
|
+
const type = icon.type || (0, exports.iconMimeType)(icon.src);
|
|
118
|
+
const declaredSizes = [(_a = icon.sizes) !== null && _a !== void 0 ? _a : icon.size].flat().filter(Boolean);
|
|
119
|
+
const sizes = declaredSizes.length ? declaredSizes.map(formatSize) : ['any'];
|
|
120
|
+
if (sizes.length > 1 && type !== 'image/svg+xml') {
|
|
121
|
+
consola_1.default.warn(`PWA icon ${icon.src} is declared at ${sizes.join(', ')}, but the Vite builder does not resize icons. Add one icon entry per size.`);
|
|
122
|
+
}
|
|
123
|
+
for (const size of sizes) {
|
|
124
|
+
resolved.icons.push(Object.assign({ src: url, sizes: size, type }, (icon.purpose ? { purpose: icon.purpose } : {})));
|
|
125
|
+
if (icon.ios) {
|
|
126
|
+
const rel = icon.ios === 'startup' ? 'apple-touch-startup-image' : 'apple-touch-icon';
|
|
127
|
+
resolved.appleTags.push(headTag('link', { rel, sizes: size, href: url }));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return resolved;
|
|
132
|
+
};
|
|
133
|
+
exports.resolveManifestIcons = resolveManifestIcons;
|
|
134
|
+
const appleTouchIconTag = (value) => {
|
|
135
|
+
const { href, sizes } = typeof value === 'string' ? { href: value, sizes: undefined } : value;
|
|
136
|
+
return headTag('link', Object.assign({ rel: 'apple-touch-icon', href }, (sizes ? { sizes: String(sizes) } : {})));
|
|
137
|
+
};
|
|
138
|
+
const appleHeadTags = (pwa) => {
|
|
139
|
+
if (!pwa.ios)
|
|
140
|
+
return [];
|
|
141
|
+
const apple = Object.assign({ 'apple-mobile-web-app-title': pwa.name, 'apple-mobile-web-app-capable': 'yes', 'apple-mobile-web-app-status-bar-style': 'default' }, (typeof pwa.ios === 'object' ? pwa.ios : {}));
|
|
142
|
+
return Object.entries(apple).flatMap(([name, value]) => {
|
|
143
|
+
if (APPLE_META_TAGS.includes(name)) {
|
|
144
|
+
const content = typeof value === 'boolean' ? (value ? 'yes' : 'no') : String(value);
|
|
145
|
+
return [headTag('meta', { name, content })];
|
|
146
|
+
}
|
|
147
|
+
if (name === 'apple-touch-icon') {
|
|
148
|
+
return [appleTouchIconTag(value)];
|
|
149
|
+
}
|
|
150
|
+
if (name === 'apple-touch-startup-image') {
|
|
151
|
+
return [headTag('link', { rel: name, href: String(value) })];
|
|
152
|
+
}
|
|
153
|
+
return [];
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
exports.appleHeadTags = appleHeadTags;
|
|
157
|
+
const resolveManifest = (pwa, base, cwd) => {
|
|
158
|
+
var _a, _b, _c, _d;
|
|
159
|
+
const fields = Object.fromEntries(Object.entries((0, build_kit_1.objToSnakeCase)(pwa)).filter(([key]) => !NON_MANIFEST_KEYS.includes(key)));
|
|
160
|
+
const { icons, appleTags, files } = (0, exports.resolveManifestIcons)(pwa.icons, base, cwd);
|
|
161
|
+
const manifest = Object.assign(Object.assign(Object.assign({}, fields), { short_name: (_a = fields.short_name) !== null && _a !== void 0 ? _a : pwa.name, start_url: (_b = fields.start_url) !== null && _b !== void 0 ? _b : '.', scope: (_c = fields.scope) !== null && _c !== void 0 ? _c : base, display: (_d = fields.display) !== null && _d !== void 0 ? _d : 'standalone' }), (pwa.icons ? { icons } : {}));
|
|
162
|
+
return { manifest, appleTags, iconFiles: files };
|
|
163
|
+
};
|
|
164
|
+
exports.resolveManifest = resolveManifest;
|
|
165
|
+
const createManifestPlugin = (pwa, cwd) => {
|
|
166
|
+
let base;
|
|
167
|
+
let resolved;
|
|
168
|
+
const manifestJson = () => JSON.stringify(resolved.manifest, null, 2);
|
|
169
|
+
return {
|
|
170
|
+
name: 'genesis-pwa-manifest',
|
|
171
|
+
configResolved(config) {
|
|
172
|
+
// Resolved per config so a dev restart picks up a changed PUBLIC_PATH
|
|
173
|
+
base = withTrailingSlash(typeof pwa.publicPath === 'string' ? pwa.publicPath : config.base);
|
|
174
|
+
resolved = (0, exports.resolveManifest)(pwa, base, cwd);
|
|
175
|
+
},
|
|
176
|
+
transformIndexHtml(html) {
|
|
177
|
+
const themeColor = resolved.manifest.theme_color;
|
|
178
|
+
const existing = new Set(htmlTags(html).map(({ tag, attrs }) => headTagKey(tag, attrs)));
|
|
179
|
+
const tags = [
|
|
180
|
+
headTag('link', Object.assign({ rel: 'manifest', href: `${base}${MANIFEST_FILE_NAME}` }, (pwa.crossorigin ? { crossorigin: pwa.crossorigin } : {}))),
|
|
181
|
+
...(typeof themeColor === 'string'
|
|
182
|
+
? [headTag('meta', { name: 'theme-color', content: themeColor })]
|
|
183
|
+
: []),
|
|
184
|
+
...(0, exports.appleHeadTags)(pwa),
|
|
185
|
+
...resolved.appleTags,
|
|
186
|
+
].filter(({ tag, attrs }) => !existing.has(headTagKey(tag, attrs)));
|
|
187
|
+
return { html, tags };
|
|
188
|
+
},
|
|
189
|
+
configureServer(server) {
|
|
190
|
+
server.middlewares.use((req, res, next) => {
|
|
191
|
+
var _a, _b;
|
|
192
|
+
const path = (_b = (_a = req.url) === null || _a === void 0 ? void 0 : _a.split('?')[0]) !== null && _b !== void 0 ? _b : '';
|
|
193
|
+
if (path === `${base}${MANIFEST_FILE_NAME}`) {
|
|
194
|
+
res.setHeader('Content-Type', 'application/manifest+json');
|
|
195
|
+
res.end(manifestJson());
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const iconFile = path.startsWith(base) && resolved.iconFiles.get(path.slice(base.length));
|
|
199
|
+
if (iconFile) {
|
|
200
|
+
res.setHeader('Content-Type', (0, exports.iconMimeType)(iconFile));
|
|
201
|
+
res.end((0, node_fs_1.readFileSync)(iconFile));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
next();
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
generateBundle() {
|
|
208
|
+
this.emitFile({ type: 'asset', fileName: MANIFEST_FILE_NAME, source: manifestJson() });
|
|
209
|
+
for (const [fileName, file] of resolved.iconFiles) {
|
|
210
|
+
this.emitFile({ type: 'asset', fileName, source: (0, node_fs_1.readFileSync)(file) });
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
exports.createManifestPlugin = createManifestPlugin;
|
|
216
|
+
const createRegistrationPlugin = (registration) => {
|
|
217
|
+
const regPath = (0, build_kit_1.getSrcRelativePath)(registration);
|
|
218
|
+
const isRegistrationScript = ({ tag, attrs }) => { var _a; return tag === 'script' && (0, build_kit_1.getSrcRelativePath)((_a = attrs.src) !== null && _a !== void 0 ? _a : '') === regPath; };
|
|
219
|
+
return {
|
|
220
|
+
name: 'genesis-sw-registration',
|
|
221
|
+
transformIndexHtml: {
|
|
222
|
+
order: 'pre',
|
|
223
|
+
handler: (html) => ({
|
|
224
|
+
html,
|
|
225
|
+
tags: htmlTags(html).some(isRegistrationScript)
|
|
226
|
+
? []
|
|
227
|
+
: [
|
|
228
|
+
{
|
|
229
|
+
tag: 'script',
|
|
230
|
+
attrs: { type: 'module', src: `/src/${regPath}` },
|
|
231
|
+
injectTo: 'head',
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
}),
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
const createDefaultRegistrationPlugin = (serviceWorker) => {
|
|
239
|
+
let url;
|
|
240
|
+
return {
|
|
241
|
+
name: 'genesis-sw-registration',
|
|
242
|
+
configResolved(config) {
|
|
243
|
+
url = (0, build_kit_1.getServiceWorkerUrl)(config.base, serviceWorker);
|
|
244
|
+
},
|
|
245
|
+
transformIndexHtml: {
|
|
246
|
+
order: 'pre',
|
|
247
|
+
handler: (html) => ({
|
|
248
|
+
html,
|
|
249
|
+
tags: [
|
|
250
|
+
{
|
|
251
|
+
tag: 'script',
|
|
252
|
+
attrs: { type: 'module' },
|
|
253
|
+
children: (0, build_kit_1.createServiceWorkerRegistration)(url),
|
|
254
|
+
injectTo: 'head',
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
}),
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
// Matches workbox-webpack-plugin: strings are prefixes and functions receive `{ asset }`
|
|
262
|
+
const excludeMatches = (url, pattern) => {
|
|
263
|
+
if (typeof pattern === 'string')
|
|
264
|
+
return url.startsWith(pattern);
|
|
265
|
+
if (pattern instanceof RegExp)
|
|
266
|
+
return pattern.test(url);
|
|
267
|
+
return pattern({ asset: { name: url } });
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Applies `exclude` before the size limit, as workbox-webpack-plugin does, and precaches only the
|
|
271
|
+
* files in `hashedFiles` without a revision.
|
|
272
|
+
*/
|
|
273
|
+
const createPrecacheTransform = ({ exclude, maximumFileSizeToCache }, hashedFiles = new Set()) => (entries) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
274
|
+
const patterns = exclude !== null && exclude !== void 0 ? exclude : DEFAULT_SW_EXCLUDE;
|
|
275
|
+
const limit = maximumFileSizeToCache || DEFAULT_MAXIMUM_FILE_SIZE_TO_CACHE;
|
|
276
|
+
const included = entries.filter(({ url }) => !patterns.some((p) => excludeMatches(url, p)));
|
|
277
|
+
const oversized = included.filter(({ size }) => size > limit);
|
|
278
|
+
return {
|
|
279
|
+
manifest: included
|
|
280
|
+
.filter((entry) => !oversized.includes(entry))
|
|
281
|
+
.map((entry) => (hashedFiles.has(entry.url) ? Object.assign(Object.assign({}, entry), { revision: null }) : entry)),
|
|
282
|
+
warnings: oversized.map(({ url, size }) => `${url} is ${size} bytes, over the serviceWorker.maximumFileSizeToCache limit of ${limit} bytes, so it won't be precached.`),
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
exports.createPrecacheTransform = createPrecacheTransform;
|
|
286
|
+
const precacheOptions = (serviceWorker, hashedFiles) => ({
|
|
287
|
+
globPatterns: ['**/*'],
|
|
288
|
+
// Replaced by createPrecacheTransform: vite-plugin-pwa drops the revision of everything under
|
|
289
|
+
// `assets/`, including unhashed files copied from `public/assets/`
|
|
290
|
+
dontCacheBustURLsMatching: undefined,
|
|
291
|
+
maximumFileSizeToCacheInBytes: Number.MAX_SAFE_INTEGER,
|
|
292
|
+
manifestTransforms: [(0, exports.createPrecacheTransform)(serviceWorker, hashedFiles)],
|
|
293
|
+
});
|
|
294
|
+
const createServiceWorkerOptions = (serviceWorker, hashedFiles = new Set()) => {
|
|
295
|
+
const assetName = (0, build_kit_1.getServiceWorkerAssetName)(serviceWorker);
|
|
296
|
+
let swDest;
|
|
297
|
+
return {
|
|
298
|
+
strategies: 'injectManifest',
|
|
299
|
+
srcDir: 'src',
|
|
300
|
+
filename: (0, build_kit_1.getSrcRelativePath)(serviceWorker.worker),
|
|
301
|
+
manifest: false,
|
|
302
|
+
injectRegister: false,
|
|
303
|
+
injectManifest: Object.assign({ rollupFormat: 'iife' }, (serviceWorker.precache === false
|
|
304
|
+
? { injectionPoint: undefined }
|
|
305
|
+
: precacheOptions(serviceWorker, hashedFiles))),
|
|
306
|
+
integration: {
|
|
307
|
+
beforeBuildServiceWorker(options) {
|
|
308
|
+
swDest = (0, node_path_1.resolve)(options.injectManifest.globDirectory, assetName);
|
|
309
|
+
options.swDest = swDest;
|
|
310
|
+
options.injectManifest.swDest = swDest;
|
|
311
|
+
},
|
|
312
|
+
// vite-plugin-pwa writes the worker under its base name to `outDir`, losing any directory
|
|
313
|
+
configureCustomSWViteBuild(inlineConfig) {
|
|
314
|
+
inlineConfig.build.outDir = (0, node_path_1.dirname)(swDest);
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
exports.createServiceWorkerOptions = createServiceWorkerOptions;
|
|
320
|
+
// Files Rollup names land in `assetsDir` with a content hash. Assets emitted with a fixed `fileName`,
|
|
321
|
+
// such as the manifest plugin's icons, keep their name, and `public/` files aren't emitted at all.
|
|
322
|
+
const createHashedFilesPlugin = (hashedFiles) => {
|
|
323
|
+
let assetsDir;
|
|
324
|
+
return {
|
|
325
|
+
name: 'genesis-sw-hashed-files',
|
|
326
|
+
apply: 'build',
|
|
327
|
+
configResolved(config) {
|
|
328
|
+
assetsDir = withTrailingSlash(config.build.assetsDir);
|
|
329
|
+
},
|
|
330
|
+
buildStart() {
|
|
331
|
+
hashedFiles.clear();
|
|
332
|
+
},
|
|
333
|
+
writeBundle(_, bundle) {
|
|
334
|
+
for (const output of Object.values(bundle)) {
|
|
335
|
+
const namedByRollup = output.type === 'chunk' || output.names.length > 0;
|
|
336
|
+
if (namedByRollup && output.fileName.startsWith(assetsDir)) {
|
|
337
|
+
hashedFiles.add(output.fileName);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
const createServiceWorkerBuildPlugins = (serviceWorker) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
344
|
+
const hashedFiles = new Set();
|
|
345
|
+
const VitePWA = yield (0, vite_js_1.getPwaModule)();
|
|
346
|
+
return [
|
|
347
|
+
createHashedFilesPlugin(hashedFiles),
|
|
348
|
+
...VitePWA((0, exports.createServiceWorkerOptions)(serviceWorker, hashedFiles)),
|
|
349
|
+
];
|
|
350
|
+
});
|
|
351
|
+
const bundleServiceWorker = (swSrc, config) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
352
|
+
const { build } = yield (0, vite_js_1.getViteModule)();
|
|
353
|
+
const result = yield build({
|
|
354
|
+
configFile: false,
|
|
355
|
+
root: config.root,
|
|
356
|
+
mode: config.mode,
|
|
357
|
+
logLevel: 'warn',
|
|
358
|
+
publicDir: false,
|
|
359
|
+
resolve: { alias: config.resolve.alias, dedupe: config.resolve.dedupe },
|
|
360
|
+
define: Object.assign(Object.assign({}, config.define), { 'process.env.NODE_ENV': JSON.stringify('development'), 'self.__WB_MANIFEST': '[]' }),
|
|
361
|
+
build: {
|
|
362
|
+
write: false,
|
|
363
|
+
minify: false,
|
|
364
|
+
emptyOutDir: false,
|
|
365
|
+
// vite-plugin-pwa's production name; `serviceWorker` is a worker-scope global
|
|
366
|
+
lib: { entry: swSrc, name: 'app', formats: ['iife'], fileName: 'service-worker' },
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
const [{ output }] = [result].flat();
|
|
370
|
+
const [chunk] = output;
|
|
371
|
+
return { code: chunk.code, moduleIds: new Set(chunk.moduleIds) };
|
|
372
|
+
});
|
|
373
|
+
exports.bundleServiceWorker = bundleServiceWorker;
|
|
374
|
+
// Rollup reports module ids as real paths, while the dev watcher reports them as configured
|
|
375
|
+
const toRealPath = (file) => {
|
|
376
|
+
try {
|
|
377
|
+
return (0, node_fs_1.realpathSync)(file);
|
|
378
|
+
}
|
|
379
|
+
catch (_a) {
|
|
380
|
+
return file;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
// Serves the worker at its built URL so the app's own registration script works under `genx dev`
|
|
384
|
+
const createDevServiceWorkerPlugin = (serviceWorker, cwd) => {
|
|
385
|
+
const swSrc = (0, node_path_1.resolve)(cwd, 'src', (0, build_kit_1.getSrcRelativePath)(serviceWorker.worker));
|
|
386
|
+
return {
|
|
387
|
+
name: 'genesis-sw-dev',
|
|
388
|
+
apply: 'serve',
|
|
389
|
+
configureServer(server) {
|
|
390
|
+
const url = (0, build_kit_1.getServiceWorkerUrl)(server.config.base, serviceWorker);
|
|
391
|
+
let worker;
|
|
392
|
+
let moduleIds = new Set();
|
|
393
|
+
const changesDuringBundles = new Set();
|
|
394
|
+
server.watcher.on('change', (file) => {
|
|
395
|
+
const id = toRealPath(file);
|
|
396
|
+
for (const changes of changesDuringBundles)
|
|
397
|
+
changes.add(id);
|
|
398
|
+
if (moduleIds.has(id))
|
|
399
|
+
worker = undefined;
|
|
400
|
+
});
|
|
401
|
+
const bundle = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
402
|
+
const changes = new Set();
|
|
403
|
+
changesDuringBundles.add(changes);
|
|
404
|
+
try {
|
|
405
|
+
const bundled = yield (0, exports.bundleServiceWorker)(swSrc, server.config);
|
|
406
|
+
moduleIds = bundled.moduleIds;
|
|
407
|
+
if ([...changes].some((id) => moduleIds.has(id)))
|
|
408
|
+
worker = undefined;
|
|
409
|
+
return bundled;
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
changesDuringBundles.delete(changes);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
server.middlewares.use((req, res, next) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
416
|
+
var _a;
|
|
417
|
+
if (((_a = req.url) === null || _a === void 0 ? void 0 : _a.split('?')[0]) !== url)
|
|
418
|
+
return next();
|
|
419
|
+
try {
|
|
420
|
+
worker !== null && worker !== void 0 ? worker : (worker = bundle());
|
|
421
|
+
const { code } = yield worker;
|
|
422
|
+
res.setHeader('Content-Type', 'text/javascript');
|
|
423
|
+
res.end(code);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
worker = undefined;
|
|
427
|
+
next(error);
|
|
428
|
+
}
|
|
429
|
+
}));
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
const resolvePwaPlugins = (ctx) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
434
|
+
const { config: { pwa, serviceWorker }, dirs: { cwd }, cli: { isDev }, } = ctx;
|
|
435
|
+
const plugins = [];
|
|
436
|
+
if (pwa) {
|
|
437
|
+
plugins.push((0, exports.createManifestPlugin)(pwa, cwd));
|
|
438
|
+
}
|
|
439
|
+
if (serviceWorker) {
|
|
440
|
+
const { registration } = serviceWorker;
|
|
441
|
+
if (registration) {
|
|
442
|
+
plugins.push(createRegistrationPlugin(registration));
|
|
443
|
+
}
|
|
444
|
+
else if (registration !== false) {
|
|
445
|
+
plugins.push(createDefaultRegistrationPlugin(serviceWorker));
|
|
446
|
+
}
|
|
447
|
+
plugins.push(...(isDev
|
|
448
|
+
? [createDevServiceWorkerPlugin(serviceWorker, cwd)]
|
|
449
|
+
: yield createServiceWorkerBuildPlugins(serviceWorker)));
|
|
450
|
+
}
|
|
451
|
+
return plugins;
|
|
452
|
+
});
|
|
453
|
+
exports.resolvePwaPlugins = resolvePwaPlugins;
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
+
import type { VitePWAOptions } from 'vite-plugin-pwa';
|
|
2
3
|
export declare const getHandlebarsModule: () => Promise<(...args: any) => Promise<Plugin>>;
|
|
3
4
|
export declare const getCheckerModule: () => Promise<(...args: any) => Plugin>;
|
|
4
5
|
export declare const getViteModule: () => Promise<{
|
|
5
6
|
build: any;
|
|
6
7
|
createServer: any;
|
|
7
8
|
}>;
|
|
9
|
+
export declare const getPwaModule: () => Promise<(options: Partial<VitePWAOptions>) => Plugin[]>;
|
|
8
10
|
//# sourceMappingURL=vite.d.ts.map
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAWtD,eAAO,MAAM,mBAAmB,0BACuC,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA,CAAC;AAE/F,eAAO,MAAM,gBAAgB,0BACuC,GAAG,KAAK,MAAM,CAAA,CAAC;AAEnF,eAAO,MAAM,aAAa;;;EAMzB,CAAC;AAEF,eAAO,MAAM,YAAY,0BAEZ,OAAO,CAAC,cAAc,CAAC,KAC7B,MAAM,EAAE,CAAA,CAAC"}
|
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getViteModule = exports.getCheckerModule = exports.getHandlebarsModule = void 0;
|
|
3
|
+
exports.getPwaModule = exports.getViteModule = exports.getCheckerModule = exports.getHandlebarsModule = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
/**
|
|
6
6
|
* This is a glue layer to import the ESM modules into the commonjs plugin.
|
|
@@ -22,3 +22,7 @@ const getViteModule = () => tslib_1.__awaiter(void 0, void 0, void 0, function*
|
|
|
22
22
|
};
|
|
23
23
|
});
|
|
24
24
|
exports.getViteModule = getViteModule;
|
|
25
|
+
const getPwaModule = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
return (yield dynamicImport('vite-plugin-pwa')).VitePWA;
|
|
27
|
+
});
|
|
28
|
+
exports.getPwaModule = getPwaModule;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/vite-builder",
|
|
3
3
|
"description": "Vite builder",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.81.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -12,16 +12,24 @@
|
|
|
12
12
|
"build": "npm run clean && tsc -b ./tsconfig.json",
|
|
13
13
|
"clean": "rimraf dist temp tsconfig.tsbuildinfo",
|
|
14
14
|
"dev": "tsc -b ./tsconfig.json -w",
|
|
15
|
+
"test": "uvu -r tsx/cjs test 'test.ts'",
|
|
15
16
|
"lint": "genx lint -l ox",
|
|
16
17
|
"lint:fix": "genx lint -l ox --fix"
|
|
17
18
|
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"tsx": "^4.7.0",
|
|
21
|
+
"uvu": "0.5.6"
|
|
22
|
+
},
|
|
18
23
|
"dependencies": {
|
|
19
|
-
"@genesislcap/build-kit": "15.
|
|
24
|
+
"@genesislcap/build-kit": "15.81.0",
|
|
20
25
|
"consola": "^3.0.2",
|
|
21
26
|
"copyfiles": "^2.4.1",
|
|
22
27
|
"vite": "^7.2.2",
|
|
23
28
|
"vite-plugin-checker": "^0.11.0",
|
|
24
|
-
"vite-plugin-handlebars": "^2.0.3"
|
|
29
|
+
"vite-plugin-handlebars": "^2.0.3",
|
|
30
|
+
"vite-plugin-pwa": "^2.0.0",
|
|
31
|
+
"workbox-build": "^7.4.1",
|
|
32
|
+
"workbox-window": "^7.4.1"
|
|
25
33
|
},
|
|
26
34
|
"repository": {
|
|
27
35
|
"type": "git",
|