@degreesign/webapp 1.6.8

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,317 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.webConfig = void 0;
40
+ const path_1 = __importDefault(require("path"));
41
+ const css_minimizer_webpack_plugin_1 = __importDefault(require("css-minimizer-webpack-plugin"));
42
+ const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin"));
43
+ const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin"));
44
+ const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin"));
45
+ const SitemapPlugin = __importStar(require("sitemap-webpack-plugin"));
46
+ const HtmlInlineCssWebpackPlugin = __importStar(require("html-inline-css-webpack-plugin"));
47
+ const utils_1 = require("./utils");
48
+ const webConfig = (params) => {
49
+ const { srcDir = `src`, productionDir = `public_html`, appShortName = `WebApp`, twitterUserName = `degreesign`, websiteName = `DegreeSign WebApp`, websiteDomain = `degreesign.com`, publishedTime = `2025-01-01T00:00:00+00:00`, author = `DegreeSign Team`, websiteTitle = `progressive webapp`, websiteDescription = `Webpack progressive web app`, coverImage = `degreesign_screenshot.webp`, coverImageDescription = `Screenshot of website`, background_color = `#fff`, theme_color = `#000`, appIcon = `app_icon.png`, appIconMaskable = `app_icon_maskable.png`, fav_icon = `favicon.ico`, orientation = `portrait`, pagesList = [], htmlCommonElements = [], assetsDir = `assets`, commonDir = `common`, imagesDir = `images`, pagesDir = `pages`, pageHome = `home`, htaccessCustom = ``, startURI = ``, language = `en_GB`, cssDiscardUnused = false, updateServiceWorker = false, onlineIndicatorFile = `https://degreesign.com/assets/images/Degree_Sign_Logo_2022.svg`, } = params || {};
50
+ const latestUpdates = (0, utils_1.readJSON)(`./updateTimes.json`) || {}, updateTimes = () => (0, utils_1.writeJSON)(`./updateTimes.json`, latestUpdates), dataString = new Date().toISOString(), timeNow = Date.now(), websiteLink = `https://${websiteDomain}`,
51
+ /** Update Image URL */
52
+ getImageURI = (image, full) =>
53
+ // raw link string
54
+ image?.includes(`/`) || image?.includes(`<`) ? image
55
+ // assets link
56
+ : `${full ? websiteLink : ``}/${assetsDir}/${imagesDir}/${image}`, coverImageLink = getImageURI(coverImage, true), appIconFile = getImageURI(appIcon), appIconMaskableFile = getImageURI(appIconMaskable ?? appIcon), favIconFile = getImageURI(fav_icon), htmlElements = (() => {
57
+ const updateSW = !latestUpdates.serviceWorker || !updateServiceWorker, swTime = updateSW ? timeNow : latestUpdates.serviceWorker, elements = {
58
+ headerHTML: `<script>${(0, utils_1.readData)(`sw_register.js`, true)
59
+ ?.replaceAll(`TIME_UPDATED`, `${swTime}`)}</script>`
60
+ };
61
+ if (updateSW) {
62
+ latestUpdates.serviceWorker = timeNow;
63
+ updateTimes();
64
+ }
65
+ ;
66
+ if (htmlCommonElements?.length)
67
+ for (let i = 0; i < htmlCommonElements.length; i++) {
68
+ const elm = htmlCommonElements[i];
69
+ elements[`${elm}HTML`] =
70
+ (elm == `header` ? elements.headerHTML : ``)
71
+ + (0, utils_1.readData)(`./${srcDir}/${commonDir}/${elm}.html`);
72
+ }
73
+ ;
74
+ return elements;
75
+ })(), appManifest = {
76
+ background_color,
77
+ theme_color,
78
+ icons: [{
79
+ src: appIconMaskableFile,
80
+ type: `image/png`,
81
+ sizes: `512x512`,
82
+ purpose: `maskable`
83
+ }, {
84
+ src: appIconFile,
85
+ type: `image/png`,
86
+ sizes: `512x512`,
87
+ purpose: `any`
88
+ }],
89
+ shortcuts: [],
90
+ display: `standalone`,
91
+ orientation,
92
+ short_name: appShortName,
93
+ start_url: `/${startURI}`,
94
+ description: websiteDescription,
95
+ name: websiteName
96
+ }, siteMap = [{ path: `/`, priority: 1.0, lastmod: dataString }], templateContent = ({ htmlWebpackPlugin }) => {
97
+ const { links, headerHTML, title, menuHTML, bodyHTML, pageBody, footerHTML, } = htmlWebpackPlugin.options || {};
98
+ return `<!-- Copyright © ${new Date(publishedTime).getFullYear()}-present ${websiteName}, All rights reserved. -->
99
+ <!DOCTYPE html>
100
+ <!-- Last Published: ${new Date().toUTCString()}+0000 (Coordinated Universal Time) -->
101
+ <html lang="en" prefix="og: https://ogp.me/">
102
+ <head>
103
+ <style type="text/css">nav,main,footer{width:100%;display:flex !important;flex-direction:column;align-items:center;}</style>
104
+ ${links || ``}
105
+ ${headerHTML || ``}
106
+ <title>${title || ``}</title>
107
+ </head>
108
+ <body>
109
+ ${menuHTML ? `<nav role="navigation">${menuHTML}</nav>` : ``}
110
+ <main role="main">
111
+ ${bodyHTML || ``}
112
+ ${pageBody || ``}
113
+ </main>
114
+ ${footerHTML ? `<footer role="contentinfo">${footerHTML}</footer>` : ``}
115
+ </body>
116
+ </html>`;
117
+ }, robots = `User-agent: *
118
+ Allow: /
119
+ ${pagesList
120
+ ?.map(page => page.noindex ? `Disallow: /${page.uri}` : ``)
121
+ ?.filter(tag => tag)
122
+ ?.join(`\n`)}
123
+
124
+ Sitemap: https://${websiteDomain}/sitemap.xml`, getServiceWorkerContent = () => {
125
+ const urlsToCache = [`/`, `/index.html`, `/app.json`].concat(pagesList.map(pageData => {
126
+ return `/${pageData?.uri}`;
127
+ })), file = (0, utils_1.readData)(`sw.js`, true);
128
+ return file
129
+ ?.replaceAll(`APP_URL`, `/${startURI}`)
130
+ ?.replaceAll(`APP_ICON`, appIconFile)
131
+ ?.replaceAll(`APP_BADGE`, appIconFile)
132
+ ?.replaceAll(`NOTIFICATION_URI`, `/${startURI}`)
133
+ ?.replaceAll(`REFERENCE_FILE`, onlineIndicatorFile);
134
+ };
135
+ let htaccessFile = `# Policies
136
+ <IfModule mod_headers.c>
137
+ SetEnvIf Origin ^*\\.${websiteDomain?.replaceAll(`.`, `\\.`)}$ ORIGIN=$0
138
+ Header always set Access-Control-Allow-Origin %{ORIGIN}e env=ORIGIN
139
+ Header set Referrer-Policy "strict-origin-when-cross-origin"
140
+ Header set X-Frame-Options "SAMEORIGIN"
141
+ Header set Content-Security-Policy ""
142
+ Header set Permissions-Policy ""
143
+ Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
144
+ </IfModule>
145
+
146
+ # disable indexing
147
+ Options -Indexes
148
+
149
+ <IfModule mod_rewrite.c>
150
+ RewriteEngine On
151
+ RewriteCond %{HTTPS} off
152
+ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
153
+ </IfModule>
154
+
155
+ # Errors
156
+ ErrorDocument 404 /404
157
+ ErrorDocument 403 /404
158
+
159
+ # Add content type
160
+ `;
161
+ for (let i = 0; i < pagesList.length; i++) {
162
+ const pageData = pagesList[i], pageURI = pageData.uri, isHome = pageHome == pageURI;
163
+ // app shortcut
164
+ if (pageData.shortcut) {
165
+ const icon = pageData.icon ?
166
+ pageData.icon?.includes(`/`) ? pageData.icon
167
+ : `/${assetsDir}/${imagesDir}/${pageData.icon}`
168
+ : appIconFile;
169
+ appManifest.shortcuts.push({
170
+ name: ((0, utils_1.isPHPTag)(pageData.name) ? pageData.short_name : undefined)
171
+ || pageData.name,
172
+ short_name: pageData.short_name || pageData.name,
173
+ description: ((0, utils_1.isPHPTag)(pageData.description) ? pageData.short_name : undefined)
174
+ || pageData.description,
175
+ url: isHome ? `/` : `/${pageURI}`,
176
+ icons: [{
177
+ src: pageData.iconMaskable ?? icon,
178
+ type: `image/png`,
179
+ sizes: `512x512`,
180
+ purpose: `maskable`
181
+ }, {
182
+ src: icon,
183
+ type: `image/png`,
184
+ sizes: `512x512`,
185
+ purpose: `any`
186
+ }]
187
+ });
188
+ }
189
+ ;
190
+ // file type (includes all children)
191
+ htaccessFile += `<Files ${isHome ? "index.html" : pageURI}>
192
+ ${pageData?.isPHP ? `SetHandler application/x-httpd-php
193
+ ` : ``}Header set Content-Type "text/html; charset=UTF-8"
194
+ </Files>\n`;
195
+ // site map
196
+ if (!pageData.noindex && !isHome)
197
+ siteMap.push({
198
+ path: `/${pageURI}`,
199
+ priority: 0.8,
200
+ lastmod: dataString
201
+ });
202
+ }
203
+ ;
204
+ htaccessFile += htaccessCustom;
205
+ (0, utils_1.writeData)(`./${productionDir}/robots.txt`, robots);
206
+ (0, utils_1.writeData)(`./${productionDir}/.htaccess`, htaccessFile);
207
+ (0, utils_1.writeData)(`./${productionDir}/app.json`, JSON.stringify(appManifest));
208
+ (0, utils_1.writeData)(`./${productionDir}/sw.js`, getServiceWorkerContent());
209
+ const pagesHTML = pagesList.map(pageData => {
210
+ const { noindex, uri: fileName, coverImage: coverImagePage, coverImageDescription: coverImageDescriptionPage, description, name: pageTitle, publishDate, } = pageData, isHome = pageHome == fileName, coverImageLinkNew = coverImagePage ? getImageURI(coverImagePage) : coverImageLink;
211
+ return new html_webpack_plugin_1.default({
212
+ chunks: [fileName],
213
+ title: isHome ? `${websiteName || ``} | ${pageData?.name || websiteTitle || ``}`
214
+ : `${pageData?.name} | ${websiteName || ``}`,
215
+ meta: (0, utils_1.metaTags)({
216
+ author,
217
+ websiteName,
218
+ websiteTitle: pageTitle || websiteTitle,
219
+ websiteDescription: description || websiteDescription,
220
+ coverImageLink: coverImageLinkNew,
221
+ coverImageDescription: (coverImagePage ? coverImageDescriptionPage : coverImageDescription) || ``,
222
+ publishedTime: publishDate || publishedTime,
223
+ websiteLink: isHome ? websiteLink : `${websiteLink}/${fileName}`,
224
+ dataString,
225
+ theme_color,
226
+ twitterUserName,
227
+ appIconFile,
228
+ noindex,
229
+ language,
230
+ isHome,
231
+ keywords: pageData.keywords,
232
+ }),
233
+ links: (0, utils_1.linkTags)({
234
+ favIconFile,
235
+ timeNow,
236
+ coverImageLink: coverImageLinkNew,
237
+ canonicalURL: pageData.canonicalURL
238
+ || `https://${websiteDomain}${isHome ? `` : `/${fileName}`}`,
239
+ }),
240
+ pageBody: (0, utils_1.readData)(`./${srcDir}/${pagesDir}/${fileName}/${fileName}.html`),
241
+ filename: isHome ? `index.html` : fileName,
242
+ ...htmlElements,
243
+ ...pageData.headerHTML ? {
244
+ headerHTML: pageData.headerHTML
245
+ + (htmlElements.headerHTML || ``)
246
+ } : {},
247
+ ...pageData.menuHTML ? {
248
+ menuHTML: pageData.menuHTML
249
+ + (htmlElements.menuHTML || ``)
250
+ } : {},
251
+ ...pageData.footerHTML ? {
252
+ footerHTML: pageData.footerHTML
253
+ + (htmlElements.footerHTML || ``)
254
+ } : {},
255
+ ...pageData.customHTML?.length ? {
256
+ bodyHTML: pageData.customHTML.map(elm => (0, utils_1.readData)(`./${srcDir}/${commonDir}/${elm}.html`))?.join(``),
257
+ } : {},
258
+ templateContent,
259
+ minify: {
260
+ collapseWhitespace: true,
261
+ removeComments: false, // Preserve comments during minification
262
+ },
263
+ });
264
+ }), configWebPlugins = [
265
+ ...pagesHTML,
266
+ new SitemapPlugin.default({
267
+ base: websiteLink, // Replace with your site base URL
268
+ paths: siteMap,
269
+ options: {
270
+ filename: `sitemap.xml`, // The name of the generated sitemap
271
+ },
272
+ }),
273
+ new copy_webpack_plugin_1.default({
274
+ patterns: [
275
+ { from: `${srcDir}/${assetsDir}`, to: `${assetsDir}` },
276
+ ],
277
+ }),
278
+ new mini_css_extract_plugin_1.default({
279
+ filename: `styles/styles.css`, // Output CSS file with original name
280
+ }),
281
+ new HtmlInlineCssWebpackPlugin.default() // Inline CSS into the HTML
282
+ ], customWebRules = [{
283
+ test: /\.(png|jpe?g|gif|svg|ico)$/, // For images
284
+ exclude: /node_modules/,
285
+ type: `${assetsDir}/${imagesDir}`,
286
+ include: path_1.default.resolve(process.cwd(), `${srcDir}/${assetsDir}/${imagesDir}`), // Only include files from the assets folder
287
+ generator: {
288
+ filename: `${assetsDir}/${imagesDir}/[name][ext][query]`, // Output to dist/assets folder
289
+ },
290
+ }, {
291
+ test: /\.css$/, // For CSS files
292
+ exclude: /node_modules/,
293
+ use: [
294
+ mini_css_extract_plugin_1.default.loader, // Extract CSS into files
295
+ `css-loader`, // Process CSS files
296
+ ],
297
+ }], cssMinimise = [new css_minimizer_webpack_plugin_1.default({
298
+ minimizerOptions: {
299
+ preset: [`default`, { discardUnused: cssDiscardUnused }], // Prevent removing unused styles
300
+ },
301
+ })], entryPoints = {};
302
+ // entry points
303
+ entryPoints[`${pageHome}`] = `./${srcDir}/${pagesDir}/${pageHome}/${pageHome}.ts`;
304
+ for (let i = 0; i < pagesList.length; i++) {
305
+ const { uri: fileName } = pagesList[i];
306
+ entryPoints[fileName] =
307
+ `./${srcDir}/${pagesDir}/${fileName}/${fileName}.ts`;
308
+ }
309
+ ;
310
+ return {
311
+ entryPoints,
312
+ customWebRules,
313
+ configWebPlugins,
314
+ cssMinimise,
315
+ };
316
+ };
317
+ exports.webConfig = webConfig;
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "license": "MIT",
3
+ "name": "@degreesign/webapp",
4
+ "version": "1.6.8",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc"
10
+ },
11
+ "keywords": [
12
+ "webpack",
13
+ "webdev",
14
+ "development"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/DegreeSign/ds_webapp.git"
19
+ },
20
+ "author": "DegreeSign",
21
+ "homepage": "https://degreesign.com",
22
+ "description": "DegreeSign webpack setup for WebApps",
23
+ "bugs": {
24
+ "url": "https://github.com/DegreeSign/ds_webapp/issues"
25
+ },
26
+ "dependencies": {
27
+ "@types/node": "^22.10.5",
28
+ "@types/webpack-bundle-analyzer": "^4.7.0",
29
+ "@types/webpack-node-externals": "^3.0.4",
30
+ "clean-webpack-plugin": "^4.0.0",
31
+ "copy-webpack-plugin": "^12.0.2",
32
+ "css-loader": "^7.1.2",
33
+ "css-minimizer-webpack-plugin": "^7.0.0",
34
+ "dotenv": "^16.4.7",
35
+ "html-inline-css-webpack-plugin": "^1.11.2",
36
+ "html-webpack-plugin": "^5.6.3",
37
+ "javascript-obfuscator": "^4.1.1",
38
+ "mini-css-extract-plugin": "^2.9.2",
39
+ "node-loader": "^2.1.0",
40
+ "sitemap-webpack-plugin": "^1.1.1",
41
+ "terser-webpack-plugin": "^5.3.11",
42
+ "ts-loader": "^9.5.1",
43
+ "ts-node": "^10.9.2",
44
+ "typescript": "^5.7.3",
45
+ "webpack": "^5.101.3",
46
+ "webpack-bundle-analyzer": "^4.10.2",
47
+ "webpack-cli": "^6.0.1",
48
+ "webpack-dev-server": "^5.2.0",
49
+ "webpack-node-externals": "^3.0.0",
50
+ "webpack-obfuscator": "^3.5.1"
51
+ },
52
+ "files": [
53
+ "dist/"
54
+ ]
55
+ }