@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.
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -0
- package/dist/src/main.d.ts +4 -0
- package/dist/src/main.js +129 -0
- package/dist/src/obfuscate.d.ts +2 -0
- package/dist/src/obfuscate.js +35 -0
- package/dist/src/sw.d.ts +7 -0
- package/dist/src/sw.js +173 -0
- package/dist/src/sw_register.d.ts +0 -0
- package/dist/src/sw_register.js +10 -0
- package/dist/src/types.d.ts +262 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils.d.ts +18 -0
- package/dist/src/utils.js +276 -0
- package/dist/src/web.d.ts +2 -0
- package/dist/src/web.js +317 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 DegreeSign
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# DegreeSign webpack setup for WebApps
|
|
2
|
+
The progressive web application template built with Webpack and TypeScript. Leverage `@degreesign/webapp` package for streamlined configuration and deployment. In addition to server bundling.
|
|
3
|
+
|
|
4
|
+
## Features
|
|
5
|
+
- **Webpack-powered**: Optimised bundling for production-ready applications.
|
|
6
|
+
- **TypeScript Support**: Ensures type safety and modern JavaScript features.
|
|
7
|
+
- **Modular Structure**: Organised folder layout for scalability.
|
|
8
|
+
- **SEO & PWA Ready**: Configurable metadata and manifest for progressive web apps.
|
|
9
|
+
- **Customisable**: Flexible Webpack configuration via `@degreesign/webapp`.
|
|
10
|
+
|
|
11
|
+
## Recommended Setup
|
|
12
|
+
1. **Node.js**: Version 18.x or higher.
|
|
13
|
+
2. **Package Manager**: npm or Yarn.
|
|
14
|
+
3. **Dependencies**:
|
|
15
|
+
- Install the `@degreesign/webapp` package: `npm install @degreesign/webapp`.
|
|
16
|
+
- Install Webpack and TypeScript: `npm install webpack webpack-cli typescript ts-loader`.
|
|
17
|
+
4. **Environment**:
|
|
18
|
+
- Create a `.env` file in the root directory with for any keys used in ts code.
|
|
19
|
+
- Ensure a TypeScript configuration (`tsconfig.json`) is set up.
|
|
20
|
+
5. **IDE**: Use VS Code or any TypeScript-compatible editor for best experience.
|
|
21
|
+
|
|
22
|
+
## Package File
|
|
23
|
+
Add file `package.json` as following
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "webpack --config webpack.web.ts",
|
|
29
|
+
"start": "webpack serve --config webpack.web.ts",
|
|
30
|
+
"build_server": "webpack --config webpack.server.ts",
|
|
31
|
+
"start_server": "webpack serve --config webpack.server.ts"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@degreesign/webapp": "latest"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## WebApp FrontEnd
|
|
40
|
+
WebApp front-end website configuration
|
|
41
|
+
|
|
42
|
+
### Folder Structure
|
|
43
|
+
```
|
|
44
|
+
app_folder/
|
|
45
|
+
├── public_html/ # Output directory for production build
|
|
46
|
+
├── src/ # Source files
|
|
47
|
+
│ ├── assets/ # Static assets
|
|
48
|
+
│ │ ├── images/ # Image assets
|
|
49
|
+
│ │ │ ├── favicon.ico # Favicon
|
|
50
|
+
│ │ │ ├── app_icon.png # App icon for PWA
|
|
51
|
+
│ │ │ └── app_cover_image.webp # Cover image for SEO
|
|
52
|
+
│ ├── code/ # Utility scripts
|
|
53
|
+
│ │ └── utils.ts # Shared TypeScript utilities (optional)
|
|
54
|
+
│ ├── pages/ # Page-specific files
|
|
55
|
+
│ │ ├── home/ # Home page
|
|
56
|
+
│ │ │ ├── home.ts # Home page logic
|
|
57
|
+
│ │ │ └── home.html # Home page template
|
|
58
|
+
│ └── styles.css # Global styles
|
|
59
|
+
├── webpack.web.ts # Webpack configuration
|
|
60
|
+
├── .env # Environment variables
|
|
61
|
+
├── tsconfig.json # TypeScript configuration
|
|
62
|
+
└── package.json # Project metadata and scripts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Configuration
|
|
66
|
+
Add `webpack.web.ts` file as the core of the webapp build process as following
|
|
67
|
+
```typescript
|
|
68
|
+
import { build } from "@degreesign/webapp";
|
|
69
|
+
|
|
70
|
+
module.exports = build({
|
|
71
|
+
type: "webapp",
|
|
72
|
+
websiteDomain: "example.com",
|
|
73
|
+
websiteName: "Your App Name",
|
|
74
|
+
appShortName: "AppName",
|
|
75
|
+
twitterUserName: "YourApp",
|
|
76
|
+
publishedTime: "2025-01-01T00:00:00+00:00",
|
|
77
|
+
author: "Your Name",
|
|
78
|
+
websiteTitle: "Your App Slogan",
|
|
79
|
+
websiteDescription: "A brief description of your app.",
|
|
80
|
+
coverImage: "app_cover_image.webp",
|
|
81
|
+
coverImageDescription: "A descriptive alt text for the cover image.",
|
|
82
|
+
background_color: "#ffffff",
|
|
83
|
+
theme_color: "#000000",
|
|
84
|
+
appIcon: "app_icon.png",
|
|
85
|
+
appIconMaskable: "app_icon_maskable.png",
|
|
86
|
+
fav_icon: "favicon.ico",
|
|
87
|
+
orientation: "portrait",
|
|
88
|
+
pagesList: [{
|
|
89
|
+
uri: `home`,
|
|
90
|
+
name: `HomePage`,
|
|
91
|
+
description: `Progressive Web App (PWA) HomePage`,
|
|
92
|
+
}],
|
|
93
|
+
htmlCommonElements: [],
|
|
94
|
+
obfuscateON: false,
|
|
95
|
+
srcDir: "src",
|
|
96
|
+
assetsDir: "assets",
|
|
97
|
+
commonDir: "code",
|
|
98
|
+
imagesDir: "images",
|
|
99
|
+
pagesDir: "pages",
|
|
100
|
+
pageHome: "home",
|
|
101
|
+
productionDir: "public_html",
|
|
102
|
+
htaccessCustom: "",
|
|
103
|
+
startURI: "/",
|
|
104
|
+
language: "en_GB",
|
|
105
|
+
port: 3210,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## WebApp BackEnd
|
|
110
|
+
WebApp back-end server configuration
|
|
111
|
+
|
|
112
|
+
### Folder Structure
|
|
113
|
+
```
|
|
114
|
+
app_folder/
|
|
115
|
+
├── server_build/ # Output directory for production build
|
|
116
|
+
├── server/ # Source files
|
|
117
|
+
│ ├── code/ # Utility scripts
|
|
118
|
+
│ │ └── utils.ts # Shared TypeScript utilities (optional)
|
|
119
|
+
│ └── main.ts # Main server file
|
|
120
|
+
├── webpack.server.ts # Webpack configuration
|
|
121
|
+
├── .env # Environment variables
|
|
122
|
+
├── tsconfig.json # TypeScript configuration
|
|
123
|
+
└── package.json # Project metadata and scripts
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Configuration
|
|
127
|
+
Add `webpack.server.ts` file as the core of the server build process as following
|
|
128
|
+
```typescript
|
|
129
|
+
import { build } from "@degreesign/webapp";
|
|
130
|
+
|
|
131
|
+
module.exports = build({
|
|
132
|
+
type: `server`,
|
|
133
|
+
obfuscateON: true,
|
|
134
|
+
srcDir: `server`,
|
|
135
|
+
productionDir: `server_build`,
|
|
136
|
+
filesList: [`main`],
|
|
137
|
+
port: 3211,
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Key Configuration Notes
|
|
142
|
+
- **Environment Variables**: Use `.env` to securely store sensitive data.
|
|
143
|
+
- **Development Server**: Runs on `port: 3210` by default.
|
|
144
|
+
- **PWA Support**: Customise `app_icon`, `fav_icon`, and `orientation` for a native-like experience.
|
|
145
|
+
- **SEO Optimisation**: Set `websiteTitle`, `websiteDescription`, and `coverImage` for better search visibility.
|
|
146
|
+
- **Pages**: Add all pages to `pagesList`.
|
|
147
|
+
|
|
148
|
+
## Getting Started
|
|
149
|
+
1. **Clone the Repository**:
|
|
150
|
+
```bash
|
|
151
|
+
git clone https://github.com/DegreeSign/ds_webapp.git
|
|
152
|
+
cd ds_webapp
|
|
153
|
+
```
|
|
154
|
+
2. **Install Dependencies**:
|
|
155
|
+
```bash
|
|
156
|
+
yarn install
|
|
157
|
+
```
|
|
158
|
+
3. **Create Folders Structure**:
|
|
159
|
+
Create folder structure for webapp, server, or both and add necessary files as explained above.
|
|
160
|
+
4. **Run Development Server**:
|
|
161
|
+
*For WebApp*
|
|
162
|
+
```bash
|
|
163
|
+
yarn start
|
|
164
|
+
```
|
|
165
|
+
*For Server*
|
|
166
|
+
```bash
|
|
167
|
+
yarn start_server
|
|
168
|
+
```
|
|
169
|
+
5. **Build for Production**:
|
|
170
|
+
*For WebApp*
|
|
171
|
+
```bash
|
|
172
|
+
yarn build
|
|
173
|
+
```
|
|
174
|
+
Output will be in the `public_html/` directory.
|
|
175
|
+
*For Server*
|
|
176
|
+
```bash
|
|
177
|
+
yarn build_server
|
|
178
|
+
```
|
|
179
|
+
Output will be in the `server_build/` directory.
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
1. Fork the repository.
|
|
183
|
+
2. Create a feature branch: `git checkout -b feature-name`.
|
|
184
|
+
3. Commit changes: `git commit -m "Add feature"`.
|
|
185
|
+
4. Push to the branch: `git push origin feature-name`.
|
|
186
|
+
5. Submit a pull request.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readJSON = exports.readData = exports.writeJSON = exports.writeData = exports.build = void 0;
|
|
4
|
+
const main_1 = require("./src/main");
|
|
5
|
+
Object.defineProperty(exports, "build", { enumerable: true, get: function () { return main_1.build; } });
|
|
6
|
+
const utils_1 = require("./src/utils");
|
|
7
|
+
Object.defineProperty(exports, "readData", { enumerable: true, get: function () { return utils_1.readData; } });
|
|
8
|
+
Object.defineProperty(exports, "readJSON", { enumerable: true, get: function () { return utils_1.readJSON; } });
|
|
9
|
+
Object.defineProperty(exports, "writeData", { enumerable: true, get: function () { return utils_1.writeData; } });
|
|
10
|
+
Object.defineProperty(exports, "writeJSON", { enumerable: true, get: function () { return utils_1.writeJSON; } });
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.build = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const webpack_1 = __importDefault(require("webpack"));
|
|
9
|
+
const clean_webpack_plugin_1 = require("clean-webpack-plugin");
|
|
10
|
+
const terser_webpack_plugin_1 = __importDefault(require("terser-webpack-plugin"));
|
|
11
|
+
const webpack_node_externals_1 = __importDefault(require("webpack-node-externals"));
|
|
12
|
+
const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
|
|
13
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
14
|
+
const web_1 = require("./web");
|
|
15
|
+
const obfuscate_1 = require("./obfuscate");
|
|
16
|
+
dotenv_1.default.config();
|
|
17
|
+
const build = (params) => {
|
|
18
|
+
const isWebApp = params.type != `server`, { mode = `production`, obfuscateON = false, minimiseON = true, srcDir = `src`, productionDir = `public_html`, port = 3210, maxFileSizeMB = 2, resolveOptions = {}, licenseText = ``, } = params, { filesList = [], } = isWebApp ? {} : params, { entryPoints = {}, customWebRules = [], configWebPlugins = [], cssMinimise = [] } = isWebApp ? (0, web_1.webConfig)(params) : {}, fileSize = maxFileSizeMB * 1024 ** 2, envKeys = {};
|
|
19
|
+
// Environment keys
|
|
20
|
+
for (const key in process.env)
|
|
21
|
+
envKeys[`process.env.${key}`] = JSON.stringify(process.env[key]);
|
|
22
|
+
// entry points
|
|
23
|
+
if (!isWebApp)
|
|
24
|
+
for (let i = 0; i < filesList.length; i++) {
|
|
25
|
+
const fileName = filesList[i];
|
|
26
|
+
entryPoints[fileName] =
|
|
27
|
+
`./${srcDir}/${fileName}.ts`;
|
|
28
|
+
}
|
|
29
|
+
;
|
|
30
|
+
return {
|
|
31
|
+
entry: entryPoints,
|
|
32
|
+
performance: {
|
|
33
|
+
maxAssetSize: fileSize,
|
|
34
|
+
maxEntrypointSize: fileSize,
|
|
35
|
+
},
|
|
36
|
+
output: {
|
|
37
|
+
path: path_1.default.resolve(process.cwd(), productionDir),
|
|
38
|
+
...isWebApp ? {
|
|
39
|
+
filename: `code/[name].[contenthash].js`,
|
|
40
|
+
publicPath: '/', // Ensures assets are referenced with absolute paths starting from the root
|
|
41
|
+
} : {
|
|
42
|
+
filename: `[name].js`,
|
|
43
|
+
libraryTarget: `commonjs`, // CommonJS for Node.js
|
|
44
|
+
globalObject: `typeof globalThis !== 'undefined' ? globalThis : (typeof self !== 'undefined' ? self : this)`,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
...isWebApp ? {} : {
|
|
48
|
+
target: 'node18', // Target Node.js environment
|
|
49
|
+
node: {
|
|
50
|
+
__dirname: false, // Prevent Webpack from mocking __dirname
|
|
51
|
+
__filename: false, // Prevent Webpack from mocking __filename
|
|
52
|
+
},
|
|
53
|
+
externals: [(0, webpack_node_externals_1.default)()], // Externalize all node_modules
|
|
54
|
+
},
|
|
55
|
+
resolve: {
|
|
56
|
+
extensions: [`.tsx`, `.ts`, `.js`, `.json`], // Resolve files
|
|
57
|
+
...isWebApp ? {} : {
|
|
58
|
+
mainFields: [`module`, `main`],
|
|
59
|
+
conditionNames: [`import`, `default`],
|
|
60
|
+
aliasFields: [],
|
|
61
|
+
preferRelative: true,
|
|
62
|
+
},
|
|
63
|
+
...resolveOptions,
|
|
64
|
+
},
|
|
65
|
+
module: {
|
|
66
|
+
rules: [
|
|
67
|
+
{
|
|
68
|
+
test: /\.ts$/,
|
|
69
|
+
exclude: /node_modules/,
|
|
70
|
+
use: `ts-loader`,
|
|
71
|
+
},
|
|
72
|
+
...isWebApp ? [] : [{
|
|
73
|
+
test: /\.node$/,
|
|
74
|
+
loader: 'node-loader',
|
|
75
|
+
options: {
|
|
76
|
+
name: '[name].[ext]',
|
|
77
|
+
},
|
|
78
|
+
}],
|
|
79
|
+
...customWebRules
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
plugins: [
|
|
83
|
+
new webpack_bundle_analyzer_1.BundleAnalyzerPlugin({
|
|
84
|
+
analyzerMode: params.openAnalyzer == undefined ? `disabled` : `server`,
|
|
85
|
+
openAnalyzer: params.openAnalyzer || false, // Prevents auto-opening the browser
|
|
86
|
+
}),
|
|
87
|
+
new webpack_1.default.DefinePlugin(envKeys),
|
|
88
|
+
...licenseText ? [new webpack_1.default.BannerPlugin({
|
|
89
|
+
banner: licenseText,
|
|
90
|
+
raw: true,
|
|
91
|
+
})] : [],
|
|
92
|
+
new clean_webpack_plugin_1.CleanWebpackPlugin({
|
|
93
|
+
cleanOnceBeforeBuildPatterns: ['code/*.js', 'code/*.js.LICENSE.txt'],
|
|
94
|
+
}),
|
|
95
|
+
...obfuscateON ? [obfuscate_1.obfuscationSettings] : [],
|
|
96
|
+
...configWebPlugins,
|
|
97
|
+
],
|
|
98
|
+
optimization: {
|
|
99
|
+
minimize: minimiseON, // Enable minimization
|
|
100
|
+
minimizer: [
|
|
101
|
+
new terser_webpack_plugin_1.default({
|
|
102
|
+
terserOptions: {
|
|
103
|
+
compress: {
|
|
104
|
+
drop_console: false, // Do not remove console logs
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
...isWebApp ? {} : {
|
|
108
|
+
extractComments: false, // Prevent duplicate comments for external modules
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
...cssMinimise,
|
|
112
|
+
],
|
|
113
|
+
...isWebApp ? {} : {
|
|
114
|
+
usedExports: true, // Enable tree-shaking
|
|
115
|
+
sideEffects: false, // Mark the project as free of side effects
|
|
116
|
+
},
|
|
117
|
+
}, // @ts-ignore
|
|
118
|
+
devServer: {
|
|
119
|
+
static: {
|
|
120
|
+
directory: path_1.default.join(process.cwd(), productionDir),
|
|
121
|
+
},
|
|
122
|
+
port, // Specify your desired port
|
|
123
|
+
open: true, // Automatically open the browser
|
|
124
|
+
compress: true, // Enable gzip compression for files served
|
|
125
|
+
},
|
|
126
|
+
mode
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
exports.build = build;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.obfuscationSettings = void 0;
|
|
7
|
+
const webpack_obfuscator_1 = __importDefault(require("webpack-obfuscator"));
|
|
8
|
+
exports.obfuscationSettings = new webpack_obfuscator_1.default({
|
|
9
|
+
compact: true, // Minify the output
|
|
10
|
+
controlFlowFlattening: false, // Disable control flow flattening to avoid errors
|
|
11
|
+
deadCodeInjection: false, // Disable dead code injection
|
|
12
|
+
debugProtection: false, // Disable debug protection
|
|
13
|
+
debugProtectionInterval: 0,
|
|
14
|
+
disableConsoleOutput: false, // Allow console output
|
|
15
|
+
identifierNamesGenerator: 'hexadecimal', // Use hexadecimal names for identifiers
|
|
16
|
+
log: false,
|
|
17
|
+
numbersToExpressions: false, // Disable numbers to expressions to reduce bloat
|
|
18
|
+
renameGlobals: false, // Avoid renaming global variables
|
|
19
|
+
selfDefending: false, // Disable self-defending code
|
|
20
|
+
simplify: true, // Simplify code structure
|
|
21
|
+
splitStrings: true, // Split strings into chunks
|
|
22
|
+
splitStringsChunkLength: 8, // chunk length
|
|
23
|
+
stringArray: true, // Enable string array transformation
|
|
24
|
+
stringArrayCallsTransform: false,
|
|
25
|
+
stringArrayEncoding: [], // No encoding for string array
|
|
26
|
+
stringArrayIndexShift: false, // Disable index shifting
|
|
27
|
+
stringArrayRotate: false, // Disable rotation to avoid potential issues
|
|
28
|
+
stringArrayShuffle: false, // Disable shuffling for stability
|
|
29
|
+
stringArrayWrappersCount: 1, // Match wrapper count
|
|
30
|
+
stringArrayWrappersChainedCalls: false, // Disable chained calls
|
|
31
|
+
stringArrayWrappersParametersMaxCount: 2, // Match max parameters
|
|
32
|
+
stringArrayWrappersType: `variable`, // Use variable wrappers
|
|
33
|
+
stringArrayThreshold: 1, // Obfuscate all strings
|
|
34
|
+
unicodeEscapeSequence: false // Disable unicode escape sequences
|
|
35
|
+
});
|
package/dist/src/sw.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const DomainName: string, CACHE_NAME: string, ROOT_URI = "APP_URL", NOTIFICATION_ICON = "APP_ICON", NOTIFICATION_BADGE = "APP_BADGE", DEFAULT_NOTIFICATION_DATA: {
|
|
2
|
+
title: string;
|
|
3
|
+
body: string;
|
|
4
|
+
url: string;
|
|
5
|
+
},
|
|
6
|
+
/** Check internet connectivity */
|
|
7
|
+
isOnline: () => Promise<boolean>;
|
package/dist/src/sw.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const DomainName = self.location.origin?.split(`.`)?.[0], CACHE_NAME = `cache_${DomainName}`, ROOT_URI = `APP_URL`, NOTIFICATION_ICON = `APP_ICON`, NOTIFICATION_BADGE = `APP_BADGE`, DEFAULT_NOTIFICATION_DATA = {
|
|
3
|
+
title: `New Notification`,
|
|
4
|
+
body: `You have a new notification!`,
|
|
5
|
+
url: `NOTIFICATION_URI`
|
|
6
|
+
},
|
|
7
|
+
/** Check internet connectivity */
|
|
8
|
+
isOnline = async () => {
|
|
9
|
+
try {
|
|
10
|
+
// Attempt to fetch a small resource to check connectivity
|
|
11
|
+
await fetch(`REFERENCE_FILE`, {
|
|
12
|
+
method: `HEAD`,
|
|
13
|
+
mode: `no-cors`,
|
|
14
|
+
cache: `no-store`
|
|
15
|
+
});
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
;
|
|
22
|
+
};
|
|
23
|
+
// // Install event: Activate immediately, no pre-caching
|
|
24
|
+
// (self as unknown as ServiceWorkerGlobalScope).addEventListener(`install`, (event: ExtendableEvent) => {
|
|
25
|
+
// event.waitUntil(
|
|
26
|
+
// (self as unknown as ServiceWorkerGlobalScope).skipWaiting()
|
|
27
|
+
// );
|
|
28
|
+
// });
|
|
29
|
+
// // Activate event: Clean up old caches
|
|
30
|
+
// (self as unknown as ServiceWorkerGlobalScope).addEventListener(`activate`, (event: ExtendableEvent) => {
|
|
31
|
+
// event.waitUntil(
|
|
32
|
+
// caches.keys().then(cacheNames =>
|
|
33
|
+
// Promise.all(
|
|
34
|
+
// cacheNames.map(name => {
|
|
35
|
+
// if (name !== CACHE_NAME) {
|
|
36
|
+
// return caches.delete(name);
|
|
37
|
+
// }
|
|
38
|
+
// return null;
|
|
39
|
+
// })
|
|
40
|
+
// )
|
|
41
|
+
// ).then(() =>
|
|
42
|
+
// (self as unknown as ServiceWorkerGlobalScope)
|
|
43
|
+
// ?.clients
|
|
44
|
+
// ?.claim()
|
|
45
|
+
// )
|
|
46
|
+
// );
|
|
47
|
+
// });
|
|
48
|
+
// Fetch event: Check isOnline only for .html files, others load from cache if available
|
|
49
|
+
self.addEventListener(`fetch`, (event) => {
|
|
50
|
+
// event.respondWith(
|
|
51
|
+
// (async () => {
|
|
52
|
+
// const isHtmlRequest = event.request.url.endsWith('.html') || event.request.url.endsWith('/');
|
|
53
|
+
// if (event.request.method !== `GET`) {
|
|
54
|
+
// // Non-GET requests are not cached
|
|
55
|
+
// if (isHtmlRequest) {
|
|
56
|
+
// const online = await isOnline();
|
|
57
|
+
// if (online) {
|
|
58
|
+
// return fetch(event.request).catch(() => {
|
|
59
|
+
// return new Response(`Network unavailable`, {
|
|
60
|
+
// status: 503,
|
|
61
|
+
// statusText: `Service Unavailable`
|
|
62
|
+
// });
|
|
63
|
+
// });
|
|
64
|
+
// }
|
|
65
|
+
// return new Response(`Offline and non-GET request`, {
|
|
66
|
+
// status: 503,
|
|
67
|
+
// statusText: `Service Unavailable`
|
|
68
|
+
// });
|
|
69
|
+
// }
|
|
70
|
+
// return new Response(`Non-GET request not supported`, {
|
|
71
|
+
// status: 503,
|
|
72
|
+
// statusText: `Service Unavailable`
|
|
73
|
+
// });
|
|
74
|
+
// }
|
|
75
|
+
// // For non-.html files, try cache first
|
|
76
|
+
// if (!isHtmlRequest) {
|
|
77
|
+
// const cachedResponse = await caches.match(event.request);
|
|
78
|
+
// if (cachedResponse) {
|
|
79
|
+
// return cachedResponse;
|
|
80
|
+
// }
|
|
81
|
+
// // If not in cache, fetch from network
|
|
82
|
+
// try {
|
|
83
|
+
// const networkResponse = await fetch(event.request);
|
|
84
|
+
// if (networkResponse.ok) {
|
|
85
|
+
// const cache = await caches.open(CACHE_NAME);
|
|
86
|
+
// cache.put(event.request, networkResponse.clone());
|
|
87
|
+
// }
|
|
88
|
+
// return networkResponse;
|
|
89
|
+
// } catch {
|
|
90
|
+
// return new Response(`Network unavailable and no cached response found`, {
|
|
91
|
+
// status: 503,
|
|
92
|
+
// statusText: `Service Unavailable`
|
|
93
|
+
// });
|
|
94
|
+
// }
|
|
95
|
+
// }
|
|
96
|
+
// // For .html files, check online status
|
|
97
|
+
// const online = await isOnline();
|
|
98
|
+
// if (online) {
|
|
99
|
+
// // When online, always fetch from network
|
|
100
|
+
// try {
|
|
101
|
+
// const networkResponse = await fetch(event.request);
|
|
102
|
+
// if (networkResponse.ok) {
|
|
103
|
+
// const cache = await caches.open(CACHE_NAME);
|
|
104
|
+
// cache.put(event.request, networkResponse.clone());
|
|
105
|
+
// }
|
|
106
|
+
// return networkResponse;
|
|
107
|
+
// } catch {
|
|
108
|
+
// // Network failed, try cache
|
|
109
|
+
// const cachedResponse = await caches.match(event.request);
|
|
110
|
+
// if (cachedResponse) {
|
|
111
|
+
// return cachedResponse;
|
|
112
|
+
// }
|
|
113
|
+
// return new Response(`Network unavailable and no cached response found`, {
|
|
114
|
+
// status: 503,
|
|
115
|
+
// statusText: `Service Unavailable`
|
|
116
|
+
// });
|
|
117
|
+
// }
|
|
118
|
+
// } else {
|
|
119
|
+
// // When offline, serve from cache
|
|
120
|
+
// const cachedResponse = await caches.match(event.request);
|
|
121
|
+
// if (cachedResponse) {
|
|
122
|
+
// return cachedResponse;
|
|
123
|
+
// }
|
|
124
|
+
// return new Response(`Offline and no cached response found`, {
|
|
125
|
+
// status: 503,
|
|
126
|
+
// statusText: `Service Unavailable`
|
|
127
|
+
// });
|
|
128
|
+
// }
|
|
129
|
+
// })()
|
|
130
|
+
// );
|
|
131
|
+
});
|
|
132
|
+
// Notification click: Open or focus window
|
|
133
|
+
self.addEventListener(`notificationclick`, (event) => {
|
|
134
|
+
event.notification.close();
|
|
135
|
+
const url = new URL(ROOT_URI, self.location.origin).href;
|
|
136
|
+
event.waitUntil(self
|
|
137
|
+
?.clients
|
|
138
|
+
?.matchAll({ type: `window`, includeUncontrolled: true })
|
|
139
|
+
?.then(clients => {
|
|
140
|
+
for (const client of clients) {
|
|
141
|
+
if (client.url === url && `focus` in client)
|
|
142
|
+
return client.focus();
|
|
143
|
+
}
|
|
144
|
+
; // @ts-ignore
|
|
145
|
+
if (self?.clients.openWindow)
|
|
146
|
+
return self.clients.openWindow(url);
|
|
147
|
+
}));
|
|
148
|
+
});
|
|
149
|
+
// Notification close: Log event
|
|
150
|
+
self.addEventListener(`notificationclose`, (event) => {
|
|
151
|
+
// console.log(`Notification closed:`, event.notification);
|
|
152
|
+
});
|
|
153
|
+
// Push event: Show notification
|
|
154
|
+
self.addEventListener(`push`, (event) => {
|
|
155
|
+
let data = DEFAULT_NOTIFICATION_DATA;
|
|
156
|
+
if (event.data) {
|
|
157
|
+
try {
|
|
158
|
+
data = event.data.json();
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
console.warn(`Invalid push data, using default: ${e}`);
|
|
162
|
+
}
|
|
163
|
+
;
|
|
164
|
+
}
|
|
165
|
+
;
|
|
166
|
+
const options = {
|
|
167
|
+
body: data.body,
|
|
168
|
+
icon: NOTIFICATION_ICON,
|
|
169
|
+
badge: NOTIFICATION_BADGE,
|
|
170
|
+
data: { url: new URL(data.url || ROOT_URI, self.location.origin).href }
|
|
171
|
+
};
|
|
172
|
+
event.waitUntil(self.registration.showNotification(data.title, options));
|
|
173
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
if ('serviceWorker' in navigator) {
|
|
3
|
+
const serviceWorker = navigator.serviceWorker;
|
|
4
|
+
serviceWorker?.getRegistrations?.()?.then((registrations) => {
|
|
5
|
+
for (let registration of registrations)
|
|
6
|
+
registration.unregister();
|
|
7
|
+
serviceWorker.register(`/sw.js?updated=TIME_UPDATED`);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
;
|