@sovereignbase/pwa 0.0.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.
@@ -0,0 +1,220 @@
1
+ /*
2
+ * Copyright 2026 Sovereignbase
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { Bytes } from "@sovereignbase/bytecodec";
18
+ //#region src/cspHash/index.ts
19
+ const cspHash = async (source) => {
20
+ const digest = await crypto.subtle.digest("SHA-256", Bytes.fromString(source));
21
+ return `sha256-${Bytes.toBase64String(digest)}`;
22
+ };
23
+ //#endregion
24
+ //#region src/seoComponents/jsonldMarkup.ts
25
+ /**
26
+ * Generates Schema.org JSON-LD markup describing a website,
27
+ * its web application, the current localized page, and publisher.
28
+ */
29
+ const jsonLDMarkup = ({ site, application, page, organization }) => {
30
+ const siteId = `${site.url}#website`;
31
+ const applicationId = `${application.url}#application`;
32
+ const organizationId = `${organization.url}#organization`;
33
+ const pageId = `${page.url}#webpage`;
34
+ const data = {
35
+ "@context": "https://schema.org",
36
+ "@graph": [
37
+ {
38
+ "@type": "WebSite",
39
+ "@id": siteId,
40
+ name: site.name,
41
+ url: site.url,
42
+ publisher: { "@id": organizationId }
43
+ },
44
+ {
45
+ "@type": "WebApplication",
46
+ "@id": applicationId,
47
+ name: application.name,
48
+ url: application.url,
49
+ inLanguage: application.inLanguage,
50
+ ...application.applicationCategory ? { applicationCategory: application.applicationCategory } : {},
51
+ ...application.operatingSystem ? { operatingSystem: application.operatingSystem } : {},
52
+ ...application.browserRequirements ? { browserRequirements: application.browserRequirements } : {},
53
+ ...application.featureList ? { featureList: application.featureList } : {},
54
+ ...application.screenshot ? { screenshot: application.screenshot } : {},
55
+ publisher: { "@id": organizationId }
56
+ },
57
+ {
58
+ "@type": "WebPage",
59
+ "@id": pageId,
60
+ name: page.name,
61
+ description: page.description,
62
+ url: page.url,
63
+ inLanguage: page.inLanguage,
64
+ isPartOf: { "@id": siteId },
65
+ mainEntity: { "@id": applicationId }
66
+ },
67
+ {
68
+ "@type": "Organization",
69
+ "@id": organizationId,
70
+ name: organization.name,
71
+ url: organization.url,
72
+ logo: {
73
+ "@type": "ImageObject",
74
+ url: organization.logo
75
+ }
76
+ }
77
+ ]
78
+ };
79
+ return `
80
+ <script type="application/ld+json">
81
+ ${JSON.stringify(data).replaceAll("<", "\\u003c")}
82
+ <\/script>
83
+ `;
84
+ };
85
+ //#endregion
86
+ //#region src/seoComponents/languageLinksMarkup.ts
87
+ /**
88
+ * Generates canonical and language-alternate link markup for a localized page.
89
+ *
90
+ * Produces:
91
+ * - a canonical link for the current language,
92
+ * - `hreflang` alternate links for each supported alternate language,
93
+ * - an `x-default` alternate link for the default language.
94
+ *
95
+ * @param host Registrable domain, such as `example.com` or `example.co.uk`.
96
+ * @param defaultLanguage Language used for the `x-default` URL.
97
+ * @param canonicalLanguage Language used for the canonical URL.
98
+ * @param alternateLanguages Languages exposed through `hreflang` alternate links.
99
+ * @param pathSuffix Optional path suffix starting with `/`.
100
+ */
101
+ const languageLinksMarkup = (host, defaultLanguage, canonicalLanguage, alternateLanguages, pathSuffix = "") => `
102
+ <link rel="canonical" href="https://${host}/${canonicalLanguage}${pathSuffix}" />
103
+ ${(() => {
104
+ let markup = ``;
105
+ for (const language of alternateLanguages) markup += ` <link rel="alternate" hreflang="${language}" href="https://${host}/${language}${pathSuffix}" />`;
106
+ return markup;
107
+ })()}
108
+ <link rel="alternate" hreflang="x-default" href="https://${host}/${defaultLanguage}${pathSuffix}" />
109
+ `;
110
+ //#endregion
111
+ //#region src/seoComponents/ogMarkup.ts
112
+ /**
113
+ * Generates Open Graph metadata markup.
114
+ *
115
+ * @param locale Open Graph locale, such as `fi_FI` or `en_US`.
116
+ * @param siteName Site or application name.
117
+ * @param title Page title.
118
+ * @param description Page description.
119
+ * @param url Canonical URL of the page.
120
+ * @param imageUrl URL of the social sharing image.
121
+ * @param imageAlt Alternative text for the social sharing image.
122
+ * @param imageWidth Width of the social sharing image in pixels.
123
+ * @param imageHeight Height of the social sharing image in pixels.
124
+ */
125
+ const ogMarkup = (locale, siteName, title, description, url, imageUrl, imageAlt, imageWidth = 1200, imageHeight = 630) => `
126
+ <meta property="og:locale" content="${locale}" />
127
+ <meta property="og:type" content="website" />
128
+ <meta property="og:site_name" content="${siteName}" />
129
+ <meta property="og:title" content="${title}" />
130
+ <meta property="og:description" content="${description}" />
131
+ <meta property="og:url" content="${url}" />
132
+ <meta property="og:image" content="${imageUrl}" />
133
+ <meta property="og:image:width" content="${imageWidth}" />
134
+ <meta property="og:image:height" content="${imageHeight}" />
135
+ <meta property="og:image:alt" content="${imageAlt}" />
136
+ `;
137
+ //#endregion
138
+ //#region src/seoComponents/twitterMarkup.ts
139
+ /**
140
+ * Generates Twitter Card metadata markup.
141
+ *
142
+ * @param title Page title.
143
+ * @param description Page description.
144
+ * @param url Canonical URL of the page.
145
+ * @param imageUrl URL of the social sharing image.
146
+ * @param imageAlt Alternative text for the social sharing image.
147
+ * @param site Twitter/X handle of the site.
148
+ * @param creator Twitter/X handle of the content creator.
149
+ */
150
+ const twitterMarkup = (title, description, url, imageUrl, imageAlt, site, creator) => `
151
+ <meta name="twitter:card" content="summary_large_image" />
152
+ <meta name="twitter:title" content="${title}" />
153
+ <meta name="twitter:description" content="${description}" />
154
+ <meta name="twitter:url" content="${url}" />
155
+ <meta name="twitter:image" content="${imageUrl}" />
156
+ <meta name="twitter:image:alt" content="${imageAlt}" />
157
+ <meta name="twitter:site" content="${site}" />
158
+ <meta name="twitter:creator" content="${creator}" />
159
+ `;
160
+ //#endregion
161
+ //#region src/htmlDocument/index.ts
162
+ /**
163
+ * Generates a complete HTML document for a web application.
164
+ *
165
+ * Supports:
166
+ * - localized document language,
167
+ * - application and theme metadata,
168
+ * - Web App Manifest integration,
169
+ * - favicon and Apple/Safari application icons,
170
+ * - critical inline styles,
171
+ * - inline ES module initialization,
172
+ * - arbitrary additional head markup.
173
+ */
174
+ const documentMarkup = async ({ language, title, applicationName, themeColor, bodyMarkup = "", headMarkup = "", stylesheet = "", entrypoint = "", iconUrl, appleTouchIconUrl, maskIconUrl, manifestUrl, maskIconColor = themeColor, colorScheme = "light dark", appleStatusBarStyle = "black-translucent", seo: documentSEO }) => `<!DOCTYPE html>
175
+ <html lang="${language}">
176
+ <head>
177
+ <meta charset="UTF-8" />
178
+ <title>${title}</title>
179
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
180
+
181
+ <meta name="application-name" content="${applicationName}" />
182
+ <meta name="color-scheme" content="${colorScheme}" />
183
+ <meta name="theme-color" content="${themeColor}" />
184
+
185
+ <meta name="mobile-web-app-capable" content="yes" />
186
+ <meta name="apple-mobile-web-app-capable" content="yes" />
187
+ <meta name="apple-mobile-web-app-title" content="${applicationName}" />
188
+ <meta
189
+ name="apple-mobile-web-app-status-bar-style"
190
+ content="${appleStatusBarStyle}"
191
+ />
192
+
193
+ ${iconUrl ? `<link rel="icon" href="${iconUrl}" />` : ""}
194
+ ${appleTouchIconUrl ? `<link rel="apple-touch-icon" sizes="180x180" href="${appleTouchIconUrl}" />` : ""}
195
+ ${maskIconUrl ? `<link rel="mask-icon" href="${maskIconUrl}" color="${maskIconColor}" />` : ""}
196
+ ${manifestUrl ? `<link rel="manifest" href="${manifestUrl}" />` : ""}
197
+
198
+ ${jsonLDMarkup(documentSEO.jsonLD)}
199
+ ${languageLinksMarkup(documentSEO.languageLinks.host, documentSEO.languageLinks.defaultLanguage, documentSEO.languageLinks.canonicalLanguage, documentSEO.languageLinks.alternateLanguages, documentSEO.languageLinks.pathSuffix)}
200
+ ${ogMarkup(documentSEO.openGraph.locale, documentSEO.openGraph.siteName, documentSEO.openGraph.title, documentSEO.openGraph.description, documentSEO.openGraph.url, documentSEO.openGraph.imageUrl, documentSEO.openGraph.imageAlt, documentSEO.openGraph.imageWidth, documentSEO.openGraph.imageHeight)}
201
+ ${twitterMarkup(documentSEO.twitter.title, documentSEO.twitter.description, documentSEO.twitter.url, documentSEO.twitter.imageUrl, documentSEO.twitter.imageAlt, documentSEO.twitter.site, documentSEO.twitter.creator)}
202
+
203
+ ${headMarkup}
204
+
205
+ ${stylesheet ? `<style integrity="${await cspHash(stylesheet)}">
206
+ ${stylesheet}
207
+ </style>` : ""}
208
+
209
+ </head>
210
+ <body>
211
+ ${bodyMarkup}
212
+ ${entrypoint ? `<script type="module" integrity="${await cspHash(entrypoint)}">
213
+ ${entrypoint}
214
+ <\/script>` : ""}
215
+ </body>
216
+ </html>`.replace(/>\s+</g, "><").trim();
217
+ //#endregion
218
+ export { documentMarkup as t };
219
+
220
+ //# sourceMappingURL=htmlDocument-DABSNDvf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlDocument-DABSNDvf.js","names":["seo.jsonLDMarkup","seo.languageLinksMarkup","seo.ogMarkup","seo.twitterMarkup"],"sources":["../src/cspHash/index.ts","../src/seoComponents/jsonldMarkup.ts","../src/seoComponents/languageLinksMarkup.ts","../src/seoComponents/ogMarkup.ts","../src/seoComponents/twitterMarkup.ts","../src/htmlDocument/index.ts"],"sourcesContent":["import { Bytes } from '@sovereignbase/bytecodec'\n\nexport const cspHash = async (source: string): Promise<`sha256-${string}`> => {\n const digest = await crypto.subtle.digest(\n 'SHA-256',\n Bytes.fromString(source) as BufferSource\n )\n\n return `sha256-${Bytes.toBase64String(digest)}`\n}\n","import type { JSONLDMarkup } from '../.types/index.js'\n\n/**\n * Generates Schema.org JSON-LD markup describing a website,\n * its web application, the current localized page, and publisher.\n */\nexport const jsonLDMarkup = ({\n site,\n application,\n page,\n organization,\n}: JSONLDMarkup) => {\n const siteId = `${site.url}#website`\n const applicationId = `${application.url}#application`\n const organizationId = `${organization.url}#organization`\n const pageId = `${page.url}#webpage`\n\n const data = {\n '@context': 'https://schema.org',\n '@graph': [\n {\n '@type': 'WebSite',\n '@id': siteId,\n name: site.name,\n url: site.url,\n publisher: {\n '@id': organizationId,\n },\n },\n\n {\n '@type': 'WebApplication',\n '@id': applicationId,\n name: application.name,\n url: application.url,\n inLanguage: application.inLanguage,\n\n ...(application.applicationCategory\n ? { applicationCategory: application.applicationCategory }\n : {}),\n\n ...(application.operatingSystem\n ? { operatingSystem: application.operatingSystem }\n : {}),\n\n ...(application.browserRequirements\n ? { browserRequirements: application.browserRequirements }\n : {}),\n\n ...(application.featureList\n ? { featureList: application.featureList }\n : {}),\n\n ...(application.screenshot\n ? { screenshot: application.screenshot }\n : {}),\n\n publisher: {\n '@id': organizationId,\n },\n },\n\n {\n '@type': 'WebPage',\n '@id': pageId,\n name: page.name,\n description: page.description,\n url: page.url,\n inLanguage: page.inLanguage,\n\n isPartOf: {\n '@id': siteId,\n },\n\n mainEntity: {\n '@id': applicationId,\n },\n },\n\n {\n '@type': 'Organization',\n '@id': organizationId,\n name: organization.name,\n url: organization.url,\n logo: {\n '@type': 'ImageObject',\n url: organization.logo,\n },\n },\n ],\n }\n\n return `\n <script type=\"application/ld+json\">\n${JSON.stringify(data).replaceAll('<', '\\\\u003c')}\n </script>\n`\n}\n","import type { BCP47LanguageTag } from '@sovereignbase/utils'\n\n/**\n * Generates canonical and language-alternate link markup for a localized page.\n *\n * Produces:\n * - a canonical link for the current language,\n * - `hreflang` alternate links for each supported alternate language,\n * - an `x-default` alternate link for the default language.\n *\n * @param host Registrable domain, such as `example.com` or `example.co.uk`.\n * @param defaultLanguage Language used for the `x-default` URL.\n * @param canonicalLanguage Language used for the canonical URL.\n * @param alternateLanguages Languages exposed through `hreflang` alternate links.\n * @param pathSuffix Optional path suffix starting with `/`.\n */\nexport const languageLinksMarkup = (\n host: `${string /* domain */}.${string /* public suffix */}`,\n defaultLanguage: BCP47LanguageTag,\n canonicalLanguage: BCP47LanguageTag,\n alternateLanguages: BCP47LanguageTag[],\n pathSuffix: '' | `/${string}` = ''\n) => `\n <link rel=\"canonical\" href=\"https://${host}/${canonicalLanguage}${pathSuffix}\" />\n ${(() => {\n let markup = ``\n for (const language of alternateLanguages)\n markup += ` <link rel=\"alternate\" hreflang=\"${language}\" href=\"https://${host}/${language}${pathSuffix}\" />`\n return markup\n })()}\n <link rel=\"alternate\" hreflang=\"x-default\" href=\"https://${host}/${defaultLanguage}${pathSuffix}\" />\n`\n","import type { OpenGraphLocale } from '@sovereignbase/utils'\n\n/**\n * Generates Open Graph metadata markup.\n *\n * @param locale Open Graph locale, such as `fi_FI` or `en_US`.\n * @param siteName Site or application name.\n * @param title Page title.\n * @param description Page description.\n * @param url Canonical URL of the page.\n * @param imageUrl URL of the social sharing image.\n * @param imageAlt Alternative text for the social sharing image.\n * @param imageWidth Width of the social sharing image in pixels.\n * @param imageHeight Height of the social sharing image in pixels.\n */\nexport const ogMarkup = (\n locale: OpenGraphLocale,\n siteName: string,\n title: string,\n description: string,\n url: `https://${string}`,\n imageUrl: string,\n imageAlt: string,\n imageWidth: number = 1200,\n imageHeight: number = 630\n) => `\n <meta property=\"og:locale\" content=\"${locale}\" />\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:site_name\" content=\"${siteName}\" />\n <meta property=\"og:title\" content=\"${title}\" />\n <meta property=\"og:description\" content=\"${description}\" />\n <meta property=\"og:url\" content=\"${url}\" />\n <meta property=\"og:image\" content=\"${imageUrl}\" />\n <meta property=\"og:image:width\" content=\"${imageWidth}\" />\n <meta property=\"og:image:height\" content=\"${imageHeight}\" />\n <meta property=\"og:image:alt\" content=\"${imageAlt}\" />\n`\n","/**\n * Generates Twitter Card metadata markup.\n *\n * @param title Page title.\n * @param description Page description.\n * @param url Canonical URL of the page.\n * @param imageUrl URL of the social sharing image.\n * @param imageAlt Alternative text for the social sharing image.\n * @param site Twitter/X handle of the site.\n * @param creator Twitter/X handle of the content creator.\n */\nexport const twitterMarkup = (\n title: string,\n description: string,\n url: `https://${string}`,\n imageUrl: string,\n imageAlt: string,\n site: `@${string}`,\n creator: `@${string}`\n) => `\n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:title\" content=\"${title}\" />\n <meta name=\"twitter:description\" content=\"${description}\" />\n <meta name=\"twitter:url\" content=\"${url}\" />\n <meta name=\"twitter:image\" content=\"${imageUrl}\" />\n <meta name=\"twitter:image:alt\" content=\"${imageAlt}\" />\n <meta name=\"twitter:site\" content=\"${site}\" />\n <meta name=\"twitter:creator\" content=\"${creator}\" />\n`\n","import { DocumentMarkupOptions } from '../.types/index.js'\nimport { cspHash } from '../cspHash/index.js'\nimport * as seo from '../seoComponents/index.js'\n/**\n * Generates a complete HTML document for a web application.\n *\n * Supports:\n * - localized document language,\n * - application and theme metadata,\n * - Web App Manifest integration,\n * - favicon and Apple/Safari application icons,\n * - critical inline styles,\n * - inline ES module initialization,\n * - arbitrary additional head markup.\n */\nexport const documentMarkup = async ({\n language,\n title,\n applicationName,\n themeColor,\n bodyMarkup = '',\n headMarkup = '',\n stylesheet = '',\n entrypoint = '',\n iconUrl,\n appleTouchIconUrl,\n maskIconUrl,\n manifestUrl,\n maskIconColor = themeColor,\n colorScheme = 'light dark',\n appleStatusBarStyle = 'black-translucent',\n seo: documentSEO,\n}: DocumentMarkupOptions): Promise<string> =>\n `<!DOCTYPE html>\n<html lang=\"${language}\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>${title}</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\n <meta name=\"application-name\" content=\"${applicationName}\" />\n <meta name=\"color-scheme\" content=\"${colorScheme}\" />\n <meta name=\"theme-color\" content=\"${themeColor}\" />\n\n <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-title\" content=\"${applicationName}\" />\n <meta\n name=\"apple-mobile-web-app-status-bar-style\"\n content=\"${appleStatusBarStyle}\"\n />\n\n ${iconUrl ? `<link rel=\"icon\" href=\"${iconUrl}\" />` : ''}\n ${\n appleTouchIconUrl\n ? `<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"${appleTouchIconUrl}\" />`\n : ''\n }\n ${\n maskIconUrl\n ? `<link rel=\"mask-icon\" href=\"${maskIconUrl}\" color=\"${maskIconColor}\" />`\n : ''\n }\n ${manifestUrl ? `<link rel=\"manifest\" href=\"${manifestUrl}\" />` : ''}\n\n ${seo.jsonLDMarkup(documentSEO.jsonLD)}\n ${seo.languageLinksMarkup(\n documentSEO.languageLinks.host,\n documentSEO.languageLinks.defaultLanguage,\n documentSEO.languageLinks.canonicalLanguage,\n documentSEO.languageLinks.alternateLanguages,\n documentSEO.languageLinks.pathSuffix\n )}\n ${seo.ogMarkup(\n documentSEO.openGraph.locale,\n documentSEO.openGraph.siteName,\n documentSEO.openGraph.title,\n documentSEO.openGraph.description,\n documentSEO.openGraph.url,\n documentSEO.openGraph.imageUrl,\n documentSEO.openGraph.imageAlt,\n documentSEO.openGraph.imageWidth,\n documentSEO.openGraph.imageHeight\n )}\n ${seo.twitterMarkup(\n documentSEO.twitter.title,\n documentSEO.twitter.description,\n documentSEO.twitter.url,\n documentSEO.twitter.imageUrl,\n documentSEO.twitter.imageAlt,\n documentSEO.twitter.site,\n documentSEO.twitter.creator\n )}\n\n ${headMarkup}\n\n ${\n stylesheet\n ? `<style integrity=\"${await cspHash(stylesheet)}\">\n${stylesheet}\n </style>`\n : ''\n }\n\n </head>\n <body>\n${bodyMarkup}\n${\n entrypoint\n ? `<script type=\"module\" integrity=\"${await cspHash(entrypoint)}\">\n${entrypoint}\n </script>`\n : ''\n}\n </body>\n</html>`\n .replace(/>\\s+</g, '><')\n .trim()\n"],"mappings":"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"}
package/dist/index.cjs ADDED
@@ -0,0 +1,258 @@
1
+ /*
2
+ * Copyright 2026 Sovereignbase
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
18
+ const require_htmlDocument = require("./htmlDocument-Cx-dprM5.cjs");
19
+ let node_fs = require("node:fs");
20
+ let node_fs_promises = require("node:fs/promises");
21
+ let node_path = require("node:path");
22
+ let esbuild = require("esbuild");
23
+ let lightningcss = require("lightningcss");
24
+ let html_minifier_terser = require("html-minifier-terser");
25
+ let node_url = require("node:url");
26
+ let terser = require("terser");
27
+ //#region src/minifyCss/index.ts
28
+ /** Bundles and minifies a CSS entrypoint into a dense string. */
29
+ async function minifyCss(entrypoint) {
30
+ const bundled = await (0, esbuild.build)({
31
+ entryPoints: [entrypoint.toString()],
32
+ bundle: true,
33
+ legalComments: "none",
34
+ minify: true,
35
+ treeShaking: true,
36
+ write: false
37
+ });
38
+ const { code } = (0, lightningcss.transform)({
39
+ code: bundled.outputFiles[0].contents,
40
+ filename: entrypoint.toString(),
41
+ minify: true
42
+ });
43
+ return new TextDecoder().decode(code).trim();
44
+ }
45
+ //#endregion
46
+ //#region src/minifyHtml/index.ts
47
+ /** Minifies a complete HTML document, including inline CSS and JavaScript. */
48
+ async function minifyHtml(source) {
49
+ return (0, html_minifier_terser.minify)(source, {
50
+ collapseBooleanAttributes: true,
51
+ collapseInlineTagWhitespace: true,
52
+ collapseWhitespace: true,
53
+ decodeEntities: true,
54
+ html5: true,
55
+ minifyCSS: true,
56
+ minifyJS: {
57
+ compress: {
58
+ dead_code: true,
59
+ passes: 3,
60
+ toplevel: true,
61
+ unused: true
62
+ },
63
+ mangle: { toplevel: true },
64
+ module: true,
65
+ toplevel: true
66
+ },
67
+ removeAttributeQuotes: true,
68
+ removeComments: true,
69
+ removeEmptyAttributes: true,
70
+ removeRedundantAttributes: true,
71
+ sortAttributes: true,
72
+ sortClassName: true,
73
+ useShortDoctype: true
74
+ });
75
+ }
76
+ //#endregion
77
+ //#region src/minifyJs/index.ts
78
+ /** Bundles, tree-shakes, mangles, and repeatedly minifies JavaScript. */
79
+ async function minifyJs(input, { define, passes = 3 } = {}) {
80
+ let output = (await (0, esbuild.build)({
81
+ ...typeof input === "object" && "source" in input ? { stdin: {
82
+ contents: input.source,
83
+ resolveDir: process.cwd()
84
+ } } : { entryPoints: [input instanceof URL ? (0, node_url.fileURLToPath)(input) : input.toString()] },
85
+ bundle: true,
86
+ define,
87
+ format: "esm",
88
+ legalComments: "none",
89
+ minify: true,
90
+ platform: "browser",
91
+ treeShaking: true,
92
+ write: false
93
+ })).outputFiles[0].text;
94
+ for (let round = 0; round < passes; round += 1) {
95
+ const result = await (0, terser.minify)(output, {
96
+ compress: {
97
+ dead_code: true,
98
+ passes,
99
+ toplevel: true,
100
+ unused: true
101
+ },
102
+ ecma: 2024,
103
+ format: {
104
+ beautify: false,
105
+ comments: false
106
+ },
107
+ mangle: { toplevel: true },
108
+ module: true,
109
+ toplevel: true
110
+ });
111
+ if (result.code === void 0) throw new Error("Terser did not produce JavaScript output");
112
+ output = result.code;
113
+ }
114
+ return output;
115
+ }
116
+ //#endregion
117
+ //#region src/webManifest/index.ts
118
+ /**
119
+ * Generates a standards-based Web App Manifest JSON string.
120
+ */
121
+ const webManifest = ({ name, shortName, description, startUrl, themeColor, icon192, icon512, maskableIcon512, id = "/", scope = "/", backgroundColor = themeColor, lang, display = "standalone", orientation, categories, screenshots, shortcuts }) => JSON.stringify({
122
+ id,
123
+ name,
124
+ short_name: shortName,
125
+ description,
126
+ start_url: startUrl,
127
+ scope,
128
+ display,
129
+ theme_color: themeColor,
130
+ background_color: backgroundColor,
131
+ ...lang ? { lang } : {},
132
+ ...orientation ? { orientation } : {},
133
+ ...categories?.length ? { categories } : {},
134
+ ...screenshots?.length ? { screenshots } : {},
135
+ ...shortcuts?.length ? { shortcuts } : {},
136
+ icons: [
137
+ {
138
+ src: icon192,
139
+ sizes: "192x192",
140
+ type: "image/png",
141
+ purpose: "any"
142
+ },
143
+ {
144
+ src: icon512,
145
+ sizes: "512x512",
146
+ type: "image/png",
147
+ purpose: "any"
148
+ },
149
+ {
150
+ src: maskableIcon512,
151
+ sizes: "512x512",
152
+ type: "image/png",
153
+ purpose: "maskable"
154
+ }
155
+ ]
156
+ });
157
+ //#endregion
158
+ //#region src/index.ts
159
+ async function pwaize(config) {
160
+ const outputDirectory = (0, node_path.join)(config.outDir.toString(), "web");
161
+ const buildId = crypto.randomUUID();
162
+ const minifyPasses = config.minifyPasses ?? 3;
163
+ const serviceWorkerPath = "/ServiceWorker";
164
+ await (0, node_fs_promises.mkdir)(outputDirectory, { recursive: true });
165
+ for (const directory of [config.assetsDir, config.i18nDir]) {
166
+ if (directory === void 0) continue;
167
+ const sourceDirectory = directory.toString();
168
+ await (0, node_fs_promises.cp)(sourceDirectory, (0, node_path.join)(outputDirectory, (0, node_path.basename)(sourceDirectory)), { recursive: true });
169
+ }
170
+ const stylesheet = await minifyCss(config.stylesheet);
171
+ const entrypoint = await minifyJs(config.entrypoint, { passes: minifyPasses });
172
+ const installer = await minifyJs({ source: `const registration=await navigator.serviceWorker.register(${JSON.stringify(serviceWorkerPath)},{scope:"/",type:"module"});await navigator.serviceWorker.ready;if(!navigator.serviceWorker.controller)location.reload();` }, { passes: minifyPasses });
173
+ const languages = [config.defaultLanguage, ...config.alternateLanguages];
174
+ for (const language of languages) {
175
+ const localized = config.languages[language];
176
+ if (localized === void 0) throw new Error(`Missing PWA configuration for language "${language}"`);
177
+ const languageDirectory = (0, node_path.join)(outputDirectory, language);
178
+ const manifestPath = `/${language}/manifest.webmanifest`;
179
+ const manifest = webManifest({
180
+ ...localized.manifest,
181
+ lang: language
182
+ });
183
+ await (0, node_fs_promises.mkdir)(languageDirectory, { recursive: true });
184
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(languageDirectory, "manifest.webmanifest"), manifest);
185
+ const installerDocument = await minifyHtml(await require_htmlDocument.documentMarkup({
186
+ ...localized.document,
187
+ language,
188
+ stylesheet: "",
189
+ entrypoint: installer,
190
+ manifestUrl: manifestPath
191
+ }));
192
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(languageDirectory, "index.html"), installerDocument);
193
+ if (language === config.defaultLanguage) {
194
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDirectory, "index.html"), installerDocument);
195
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDirectory, "manifest.webmanifest"), manifest);
196
+ }
197
+ }
198
+ const buildIdPath = (0, node_path.join)(outputDirectory, "@sovereignbase", "pwa", "pwaize-build-id.txt");
199
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(outputDirectory, "@sovereignbase", "pwa"), { recursive: true });
200
+ await (0, node_fs_promises.writeFile)(buildIdPath, buildId);
201
+ if (config._headersFile === true) await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDirectory, "_headers"), `/${serviceWorkerPath.slice(1)}\n Cache-Control: no-cache\n Content-Type: text/javascript;charset=UTF-8\n\n/*\n X-Content-Type-Options: nosniff\n`);
202
+ const precache = [...await publicFiles(outputDirectory), ...config.serviceWorker?.precache ?? []];
203
+ const bypassRules = (config.serviceWorker?.bypass ?? []).map(globRule);
204
+ const compiledServiceWorker = new URL("./serviceWorker/entrypoint.js", require("url").pathToFileURL(__filename).href);
205
+ const serviceWorker = await minifyJs((0, node_fs.existsSync)(compiledServiceWorker) ? compiledServiceWorker : new URL("./serviceWorker/entrypoint.ts", require("url").pathToFileURL(__filename).href), {
206
+ define: {
207
+ buildId: JSON.stringify(buildId),
208
+ buildIdUrl: JSON.stringify("/@sovereignbase/pwa/pwaize-build-id.txt"),
209
+ bypassRules: JSON.stringify(bypassRules),
210
+ customInitialize: config.serviceWorker?.initialize === void 0 ? "undefined" : `(${config.serviceWorker.initialize.toString()})`,
211
+ customWaitUntil: config.serviceWorker?.waitUntil === void 0 ? "undefined" : `(${config.serviceWorker.waitUntil.toString()})`,
212
+ defaultLanguage: JSON.stringify(config.defaultLanguage),
213
+ documentOptions: JSON.stringify(Object.fromEntries(languages.map((language) => [language, config.languages[language].document]))),
214
+ entrypoint: JSON.stringify(entrypoint),
215
+ precache: JSON.stringify(precache),
216
+ stylesheet: JSON.stringify(stylesheet)
217
+ },
218
+ passes: minifyPasses
219
+ });
220
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDirectory, serviceWorkerPath.slice(1)), serviceWorker);
221
+ }
222
+ async function publicFiles(directory, root = directory) {
223
+ const entries = await (0, node_fs_promises.readdir)(directory, { withFileTypes: true });
224
+ const files = [];
225
+ for (const entry of entries) {
226
+ const path = (0, node_path.join)(directory, entry.name);
227
+ if (entry.isDirectory()) files.push(...await publicFiles(path, root));
228
+ else if (entry.isFile() && entry.name !== "_headers") files.push(`/${(0, node_path.relative)(root, path).split(node_path.sep).join("/")}`);
229
+ }
230
+ return files.sort();
231
+ }
232
+ function globRule(pattern) {
233
+ if (pattern instanceof RegExp) return {
234
+ absolute: true,
235
+ flags: pattern.flags,
236
+ source: pattern.source
237
+ };
238
+ let source = "^";
239
+ for (let index = 0; index < pattern.length; index += 1) {
240
+ const character = pattern[index];
241
+ if (character === "*") {
242
+ if (pattern[index + 1] === "*") {
243
+ source += ".*";
244
+ index += 1;
245
+ } else source += "[^/]*";
246
+ } else if (character === "?") source += ".";
247
+ else source += character.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
248
+ }
249
+ return {
250
+ absolute: pattern.includes("://"),
251
+ flags: "",
252
+ source: `${source}$`
253
+ };
254
+ }
255
+ //#endregion
256
+ exports.pwaize = pwaize;
257
+
258
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["build","transform","minify","build","fileURLToPath","minify","join","mkdir","cp","basename","writeFile","documentMarkup","existsSync","readdir","relative","sep"],"sources":["../src/minifyCss/index.ts","../src/minifyHtml/index.ts","../src/minifyJs/index.ts","../src/webManifest/index.ts","../src/index.ts"],"sourcesContent":["import type { PathLike } from 'node:fs'\nimport { build } from 'esbuild'\nimport { transform } from 'lightningcss'\n\n/** Bundles and minifies a CSS entrypoint into a dense string. */\nexport default async function minifyCss(entrypoint: PathLike): Promise<string> {\n const bundled = await build({\n entryPoints: [entrypoint.toString()],\n bundle: true,\n legalComments: 'none',\n minify: true,\n treeShaking: true,\n write: false,\n })\n const { code } = transform({\n code: bundled.outputFiles[0].contents,\n filename: entrypoint.toString(),\n minify: true,\n })\n\n return new TextDecoder().decode(code).trim()\n}\n","import { minify } from 'html-minifier-terser'\n\n/** Minifies a complete HTML document, including inline CSS and JavaScript. */\nexport default async function minifyHtml(source: string): Promise<string> {\n return minify(source, {\n collapseBooleanAttributes: true,\n collapseInlineTagWhitespace: true,\n collapseWhitespace: true,\n decodeEntities: true,\n html5: true,\n minifyCSS: true,\n minifyJS: {\n compress: {\n dead_code: true,\n passes: 3,\n toplevel: true,\n unused: true,\n },\n mangle: {\n toplevel: true,\n },\n module: true,\n toplevel: true,\n },\n removeAttributeQuotes: true,\n removeComments: true,\n removeEmptyAttributes: true,\n removeRedundantAttributes: true,\n sortAttributes: true,\n sortClassName: true,\n useShortDoctype: true,\n })\n}\n","import type { PathLike } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { build } from 'esbuild'\nimport { minify } from 'terser'\n\ntype JavaScriptInput = PathLike | { source: string }\n\ntype MinifyJsOptions = {\n define?: Record<string, string>\n passes?: number\n}\n\n/** Bundles, tree-shakes, mangles, and repeatedly minifies JavaScript. */\nexport default async function minifyJs(\n input: JavaScriptInput,\n { define, passes = 3 }: MinifyJsOptions = {}\n): Promise<string> {\n const bundled = await build({\n ...(typeof input === 'object' && 'source' in input\n ? { stdin: { contents: input.source, resolveDir: process.cwd() } }\n : {\n entryPoints: [\n input instanceof URL ? fileURLToPath(input) : input.toString(),\n ],\n }),\n bundle: true,\n define,\n format: 'esm',\n legalComments: 'none',\n minify: true,\n platform: 'browser',\n treeShaking: true,\n write: false,\n })\n\n let output = bundled.outputFiles[0].text\n\n for (let round = 0; round < passes; round += 1) {\n const result = await minify(output, {\n compress: {\n dead_code: true,\n passes,\n toplevel: true,\n unused: true,\n },\n ecma: 2024,\n format: {\n beautify: false,\n comments: false,\n },\n mangle: {\n toplevel: true,\n },\n module: true,\n toplevel: true,\n })\n\n if (result.code === undefined) {\n throw new Error('Terser did not produce JavaScript output')\n }\n\n output = result.code\n }\n\n return output\n}\n","import type { BCP47LanguageTag } from '@sovereignbase/utils'\n\ntype Path = `/${string}`\ntype ImageURL = Path | `https://${string}`\n\nexport interface WebManifestScreenshot {\n src: ImageURL\n sizes: `${number}x${number}`\n type?: `image/${string}`\n form_factor?: 'narrow' | 'wide'\n label?: string\n}\n\nexport interface WebManifestShortcut {\n name: string\n url: Path\n description?: string\n icons?: {\n src: ImageURL\n sizes: `${number}x${number}` | 'any'\n type?: `image/${string}`\n }[]\n}\n\nexport interface WebManifestOptions {\n name: string\n shortName: string\n description: string\n startUrl: Path\n themeColor: string\n icon192: ImageURL\n icon512: ImageURL\n maskableIcon512: ImageURL\n id?: Path\n scope?: Path\n backgroundColor?: string\n lang?: BCP47LanguageTag\n display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser'\n orientation?:\n | 'any'\n | 'natural'\n | 'portrait'\n | 'portrait-primary'\n | 'portrait-secondary'\n | 'landscape'\n | 'landscape-primary'\n | 'landscape-secondary'\n categories?: string[]\n screenshots?: WebManifestScreenshot[]\n shortcuts?: WebManifestShortcut[]\n}\n\n/**\n * Generates a standards-based Web App Manifest JSON string.\n */\nexport const webManifest = ({\n name,\n shortName,\n description,\n startUrl,\n themeColor,\n icon192,\n icon512,\n maskableIcon512,\n id = '/',\n scope = '/',\n backgroundColor = themeColor,\n lang,\n display = 'standalone',\n orientation,\n categories,\n screenshots,\n shortcuts,\n}: WebManifestOptions): string =>\n JSON.stringify({\n id,\n name,\n short_name: shortName,\n description,\n start_url: startUrl,\n scope,\n display,\n theme_color: themeColor,\n background_color: backgroundColor,\n ...(lang ? { lang } : {}),\n ...(orientation ? { orientation } : {}),\n ...(categories?.length ? { categories } : {}),\n ...(screenshots?.length ? { screenshots } : {}),\n ...(shortcuts?.length ? { shortcuts } : {}),\n icons: [\n {\n src: icon192,\n sizes: '192x192',\n type: 'image/png',\n purpose: 'any',\n },\n {\n src: icon512,\n sizes: '512x512',\n type: 'image/png',\n purpose: 'any',\n },\n {\n src: maskableIcon512,\n sizes: '512x512',\n type: 'image/png',\n purpose: 'maskable',\n },\n ],\n })\n","import { existsSync, type PathLike } from 'node:fs'\nimport { cp, mkdir, readdir, writeFile } from 'node:fs/promises'\nimport { basename, join, relative, sep } from 'node:path'\nimport type { BCP47LanguageTag } from '@sovereignbase/utils'\nimport type { DocumentMarkupOptions } from './.types/index.js'\nimport { documentMarkup } from './htmlDocument/index.js'\nimport minifyCss from './minifyCss/index.js'\nimport minifyHtml from './minifyHtml/index.js'\nimport minifyJs from './minifyJs/index.js'\nimport { webManifest, type WebManifestOptions } from './webManifest/index.js'\n\nexport async function pwaize(config: PWAizeConfig): Promise<void> {\n const outputDirectory = join(config.outDir.toString(), 'web')\n const buildId = crypto.randomUUID()\n const minifyPasses = config.minifyPasses ?? 3\n const serviceWorkerPath = '/ServiceWorker'\n\n await mkdir(outputDirectory, { recursive: true })\n\n for (const directory of [config.assetsDir, config.i18nDir]) {\n if (directory === undefined) continue\n\n const sourceDirectory = directory.toString()\n await cp(\n sourceDirectory,\n join(outputDirectory, basename(sourceDirectory)),\n {\n recursive: true,\n }\n )\n }\n\n const stylesheet = await minifyCss(config.stylesheet)\n const entrypoint = await minifyJs(config.entrypoint, {\n passes: minifyPasses,\n })\n const installer = await minifyJs(\n {\n source: `const registration=await navigator.serviceWorker.register(${JSON.stringify(serviceWorkerPath)},{scope:\"/\",type:\"module\"});await navigator.serviceWorker.ready;if(!navigator.serviceWorker.controller)location.reload();`,\n },\n { passes: minifyPasses }\n )\n const languages = [config.defaultLanguage, ...config.alternateLanguages]\n\n for (const language of languages) {\n const localized = config.languages[language]\n if (localized === undefined) {\n throw new Error(`Missing PWA configuration for language \"${language}\"`)\n }\n\n const languageDirectory = join(outputDirectory, language)\n const manifestPath = `/${language}/manifest.webmanifest` as const\n const manifest = webManifest({\n ...localized.manifest,\n lang: language,\n })\n\n await mkdir(languageDirectory, { recursive: true })\n await writeFile(join(languageDirectory, 'manifest.webmanifest'), manifest)\n\n const installerDocument = await minifyHtml(\n await documentMarkup({\n ...localized.document,\n language,\n stylesheet: '',\n entrypoint: installer,\n manifestUrl: manifestPath,\n })\n )\n\n await writeFile(join(languageDirectory, 'index.html'), installerDocument)\n\n if (language === config.defaultLanguage) {\n await writeFile(join(outputDirectory, 'index.html'), installerDocument)\n await writeFile(join(outputDirectory, 'manifest.webmanifest'), manifest)\n }\n }\n\n const buildIdPath = join(\n outputDirectory,\n '@sovereignbase',\n 'pwa',\n 'pwaize-build-id.txt'\n )\n await mkdir(join(outputDirectory, '@sovereignbase', 'pwa'), {\n recursive: true,\n })\n await writeFile(buildIdPath, buildId)\n\n if (config._headersFile === true) {\n await writeFile(\n join(outputDirectory, '_headers'),\n `/${serviceWorkerPath.slice(1)}\\n Cache-Control: no-cache\\n Content-Type: text/javascript;charset=UTF-8\\n\\n/*\\n X-Content-Type-Options: nosniff\\n`\n )\n }\n\n const precache = [\n ...(await publicFiles(outputDirectory)),\n ...(config.serviceWorker?.precache ?? []),\n ]\n const bypassRules = (config.serviceWorker?.bypass ?? []).map(globRule)\n const compiledServiceWorker = new URL(\n './serviceWorker/entrypoint.js',\n import.meta.url\n )\n const serviceWorker = await minifyJs(\n existsSync(compiledServiceWorker)\n ? compiledServiceWorker\n : new URL('./serviceWorker/entrypoint.ts', import.meta.url),\n {\n define: {\n buildId: JSON.stringify(buildId),\n buildIdUrl: JSON.stringify('/@sovereignbase/pwa/pwaize-build-id.txt'),\n bypassRules: JSON.stringify(bypassRules),\n customInitialize:\n config.serviceWorker?.initialize === undefined\n ? 'undefined'\n : `(${config.serviceWorker.initialize.toString()})`,\n customWaitUntil:\n config.serviceWorker?.waitUntil === undefined\n ? 'undefined'\n : `(${config.serviceWorker.waitUntil.toString()})`,\n defaultLanguage: JSON.stringify(config.defaultLanguage),\n documentOptions: JSON.stringify(\n Object.fromEntries(\n languages.map((language) => [\n language,\n config.languages[language].document,\n ])\n )\n ),\n entrypoint: JSON.stringify(entrypoint),\n precache: JSON.stringify(precache),\n stylesheet: JSON.stringify(stylesheet),\n },\n passes: minifyPasses,\n }\n )\n\n await writeFile(\n join(outputDirectory, serviceWorkerPath.slice(1)),\n serviceWorker\n )\n}\n\nasync function publicFiles(\n directory: string,\n root = directory\n): Promise<string[]> {\n const entries = await readdir(directory, { withFileTypes: true })\n const files: string[] = []\n\n for (const entry of entries) {\n const path = join(directory, entry.name)\n if (entry.isDirectory()) {\n files.push(...(await publicFiles(path, root)))\n } else if (entry.isFile() && entry.name !== '_headers') {\n files.push(`/${relative(root, path).split(sep).join('/')}`)\n }\n }\n\n return files.sort()\n}\n\nfunction globRule(pattern: string | RegExp): {\n absolute: boolean\n flags: string\n source: string\n} {\n if (pattern instanceof RegExp) {\n return {\n absolute: true,\n flags: pattern.flags,\n source: pattern.source,\n }\n }\n\n let source = '^'\n for (let index = 0; index < pattern.length; index += 1) {\n const character = pattern[index]\n\n if (character === '*') {\n if (pattern[index + 1] === '*') {\n source += '.*'\n index += 1\n } else {\n source += '[^/]*'\n }\n } else if (character === '?') {\n source += '.'\n } else {\n source += character.replace(/[|\\\\{}()[\\]^$+?.]/g, '\\\\$&')\n }\n }\n\n return {\n absolute: pattern.includes('://'),\n flags: '',\n source: `${source}$`,\n }\n}\n\nexport type PWAizeConfig = {\n defaultLanguage: BCP47LanguageTag\n canonicalLanguage: BCP47LanguageTag\n alternateLanguages: BCP47LanguageTag[]\n\n languages: Record<\n string,\n {\n document: Omit<\n DocumentMarkupOptions,\n 'entrypoint' | 'language' | 'manifestUrl' | 'stylesheet'\n >\n manifest: Omit<WebManifestOptions, 'lang'>\n }\n >\n\n stylesheet: PathLike\n entrypoint: PathLike\n outDir: PathLike\n\n assetsDir?: PathLike\n i18nDir?: PathLike\n minifyPasses?: number\n\n serviceWorker?: {\n bypass?: Array<string | RegExp>\n precache?: `/${string}`[]\n initialize?: () => void\n waitUntil?: () => Promise<void>\n }\n\n _headersFile?: boolean\n}\n"],"mappings":"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"}