WebPackage -> Packet
Rename WebPackage to Packet. Move Packet to `/packages/typescript/packet`.
This commit is contained in:
parent
42707049a3
commit
6935e67a49
13 changed files with 325 additions and 285 deletions
358
package-lock.json
generated
358
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
@ -1,20 +1,24 @@
|
||||||
{
|
{
|
||||||
|
"name": "al-quran.reflectslight.io",
|
||||||
|
"workspaces": ["./packages/typescript/*"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"eslint": "node ./node_modules/eslint/bin/eslint.js src/js/",
|
"eslint": "node ./node_modules/eslint/bin/eslint.js src/js/",
|
||||||
"eslint-autofix": "node ./node_modules/eslint/bin/eslint.js --fix src/js/"
|
"eslint-autofix": "node ./node_modules/eslint/bin/eslint.js --fix src/js/"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"ts-loader": "^9.3.1",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"es-cookie": "^1.4.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/css-font-loading-module": "^0.0.7",
|
"@types/css-font-loading-module": "^0.0.7",
|
||||||
"@types/react": "^18.0.18",
|
"@types/react": "^18.0.18",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"classnames": "^2.3.2",
|
|
||||||
"es-cookie": "^1.4.0",
|
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"ts-loader": "^9.3.1",
|
|
||||||
"ts-standard": "^12.0.1",
|
"ts-standard": "^12.0.1",
|
||||||
"typescript": "^4.8.2",
|
"typescript": "^4.8.2",
|
||||||
"webpack": "^5.74.0",
|
"webpack": "^5.74.0",
|
||||||
|
|
1
packages/typescript/packet/.gitignore
vendored
Normal file
1
packages/typescript/packet/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dist/
|
21
packages/typescript/packet/package.json
Normal file
21
packages/typescript/packet/package.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "packet",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Download a web page's dependencies before the page loads.",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": ["dist/index.d.ts"],
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm exec tsc",
|
||||||
|
"prepare": "npm run build"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ReflectsLight/al-quran.reflectslight.io.git"
|
||||||
|
},
|
||||||
|
"author": "0x1eef",
|
||||||
|
"license": "0BSDL",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^16.18",
|
||||||
|
"typescript": "^4.5"
|
||||||
|
}
|
||||||
|
}
|
52
packages/typescript/packet/src/index.ts
Normal file
52
packages/typescript/packet/src/index.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import {
|
||||||
|
Packet,
|
||||||
|
PacketSpec,
|
||||||
|
PacketTarget,
|
||||||
|
} from './packet/types';
|
||||||
|
import {
|
||||||
|
image,
|
||||||
|
stylesheet,
|
||||||
|
script,
|
||||||
|
other,
|
||||||
|
font
|
||||||
|
} from './packet/loaders';
|
||||||
|
|
||||||
|
export type { Packet, PacketSpec, PacketTarget };
|
||||||
|
|
||||||
|
export default function (pkgspec: PacketSpec) {
|
||||||
|
const self: Packet = Object.create(null);
|
||||||
|
const pkg: PacketTarget = { fonts: [], images: [], stylesheets: [], scripts: [], others: [] };
|
||||||
|
const { fonts, images, stylesheets, scripts, others, onprogress } = Object.assign({}, pkg, pkgspec);
|
||||||
|
const total = [...fonts, ...images, ...stylesheets, ...scripts, ...others].length;
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
const reporter = <T>(el: T) => {
|
||||||
|
index++;
|
||||||
|
if (onprogress && index <= total) {
|
||||||
|
onprogress(100 * (index / total));
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
let fetcher: Promise<PacketTarget> | null = null;
|
||||||
|
self.fetch = () => {
|
||||||
|
if (fetcher) {
|
||||||
|
return fetcher;
|
||||||
|
} else {
|
||||||
|
fetcher = font(fonts, reporter)
|
||||||
|
.then((fonts: FontFace[]) => pkg.fonts.push(...fonts))
|
||||||
|
.then(() => image(images, reporter))
|
||||||
|
.then((images: HTMLElement[]) => pkg.images.push(...images))
|
||||||
|
.then(() => stylesheet(stylesheets, reporter))
|
||||||
|
.then((stylesheets: HTMLElement[]) => pkg.stylesheets.push(...stylesheets))
|
||||||
|
.then(() => script(scripts, reporter))
|
||||||
|
.then((scripts: HTMLElement[]) => pkg.scripts.push(...scripts))
|
||||||
|
.then(() => other(others, reporter))
|
||||||
|
.then((others: HTMLElement[]) => pkg.others.push(...others))
|
||||||
|
.then(() => pkg);
|
||||||
|
return fetcher;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
8
packages/typescript/packet/src/packet/fetchOptions.ts
Normal file
8
packages/typescript/packet/src/packet/fetchOptions.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const getNavigationEntries = (): PerformanceNavigationTiming[] => {
|
||||||
|
return performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchOptions(): RequestInit {
|
||||||
|
const pageHasRefreshed = getNavigationEntries().some((e) => e.type === 'reload');
|
||||||
|
return pageHasRefreshed ? { cache: 'reload' } : {};
|
||||||
|
}
|
80
packages/typescript/packet/src/packet/loaders.ts
Normal file
80
packages/typescript/packet/src/packet/loaders.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
const fetchOptions = (): RequestInit => {
|
||||||
|
const getNavigationEntries = (): PerformanceNavigationTiming[] => {
|
||||||
|
return performance
|
||||||
|
.getEntriesByType('navigation') as PerformanceNavigationTiming[];
|
||||||
|
};
|
||||||
|
const pageHasRefreshed = getNavigationEntries()
|
||||||
|
.some((e) => e.type === 'reload');
|
||||||
|
return pageHasRefreshed ? { cache: 'reload' } : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function script(
|
||||||
|
scripts: string[] | undefined,
|
||||||
|
reporter: <T>(f: T) => T
|
||||||
|
) {
|
||||||
|
return Promise.all(
|
||||||
|
(scripts || []).map((src) => {
|
||||||
|
return fetch(src, fetchOptions())
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((text) => Object.assign(document.createElement('script'), { type: 'application/javascript', text }))
|
||||||
|
.then((el) => reporter<HTMLElement>(el));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stylesheet(
|
||||||
|
stylesheets: string[] | undefined,
|
||||||
|
reporter: <T>(f: T) => T
|
||||||
|
) {
|
||||||
|
return Promise.all(
|
||||||
|
(stylesheets || []).map((href) => {
|
||||||
|
return fetch(href, fetchOptions())
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((innerText) => Object.assign(document.createElement('style'), { innerText }))
|
||||||
|
.then((el) => reporter<HTMLElement>(el));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function image(
|
||||||
|
images: string[] | undefined,
|
||||||
|
reporter: <T>(f: T) => T
|
||||||
|
) {
|
||||||
|
return Promise.all(
|
||||||
|
(images || []).map((src) => {
|
||||||
|
return new Promise<HTMLElement>((resolve, reject) => {
|
||||||
|
const el = document.createElement('img');
|
||||||
|
el.onload = () => resolve(el);
|
||||||
|
el.onerror = reject;
|
||||||
|
el.src = src;
|
||||||
|
}).then((el) => reporter<HTMLElement>(el));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function font(
|
||||||
|
fonts: Array<[string, string]> | undefined,
|
||||||
|
reporter: <T>(f: T) => T
|
||||||
|
) {
|
||||||
|
return Promise.all(
|
||||||
|
(fonts || []).map(async (font) => {
|
||||||
|
return await new FontFace(...font)
|
||||||
|
.load()
|
||||||
|
.then((font) => reporter<FontFace>(font));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function other(
|
||||||
|
others: string[] | undefined,
|
||||||
|
reporter: <T>(f: T) => T
|
||||||
|
) {
|
||||||
|
return Promise.all(
|
||||||
|
(others || []).map((src) => {
|
||||||
|
return fetch(src, fetchOptions())
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((text) => Object.assign(document.createElement('script'), { type: 'text/plain', src, text }))
|
||||||
|
.then((el) => reporter<HTMLElement>(el));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
22
packages/typescript/packet/src/packet/types.ts
Normal file
22
packages/typescript/packet/src/packet/types.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
export interface Packet {
|
||||||
|
fetch: () => Promise<PacketTarget> | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PacketTarget = {
|
||||||
|
scripts: HTMLElement[]
|
||||||
|
stylesheets: HTMLElement[]
|
||||||
|
images: HTMLElement[]
|
||||||
|
fonts: FontFace[]
|
||||||
|
others: HTMLElement[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PacketSpec {
|
||||||
|
scripts: string[]
|
||||||
|
stylesheets: string[]
|
||||||
|
images: string[]
|
||||||
|
fonts: Array<[string, string]>
|
||||||
|
others: string[]
|
||||||
|
onprogress?: (percent: number) => any
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PackageItem = HTMLElement | FontFace;
|
15
packages/typescript/packet/tsconfig.json
Normal file
15
packages/typescript/packet/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"module": "ESNEXT",
|
||||||
|
"target": "ES2020",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
|
||||||
|
"baseUrl": "src/",
|
||||||
|
"paths": { "*": ["*"] },
|
||||||
|
|
||||||
|
"outDir": "dist",
|
||||||
|
"declaration": true,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import WebPackage from 'lib/WebPackage';
|
import Packet from 'packet';
|
||||||
|
import type { PacketTarget } from 'packet';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
||||||
|
@ -6,7 +7,7 @@ import WebPackage from 'lib/WebPackage';
|
||||||
const progressNumber: HTMLSpanElement = parent.querySelector('.percentage')!;
|
const progressNumber: HTMLSpanElement = parent.querySelector('.percentage')!;
|
||||||
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
||||||
|
|
||||||
WebPackage({
|
Packet({
|
||||||
scripts: ['/js/pages/surah/index.js'],
|
scripts: ['/js/pages/surah/index.js'],
|
||||||
stylesheets: ['/css/pages/surah/index.css'],
|
stylesheets: ['/css/pages/surah/index.css'],
|
||||||
images: ['/images/moon.svg', '/images/leaf.svg'],
|
images: ['/images/moon.svg', '/images/leaf.svg'],
|
||||||
|
@ -21,7 +22,7 @@ import WebPackage from 'lib/WebPackage';
|
||||||
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
||||||
}
|
}
|
||||||
}).fetch()
|
}).fetch()
|
||||||
.then((pkg) => {
|
.then((pkg: PacketTarget) => {
|
||||||
inlineStyle.remove();
|
inlineStyle.remove();
|
||||||
parent.remove();
|
parent.remove();
|
||||||
pkg.fonts.forEach((f) => document.fonts.add(f));
|
pkg.fonts.forEach((f) => document.fonts.add(f));
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import WebPackage from 'lib/WebPackage';
|
import Packet from 'packet';
|
||||||
|
import type { PacketTarget } from 'packet';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
||||||
|
@ -7,7 +8,7 @@ import WebPackage from 'lib/WebPackage';
|
||||||
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
||||||
const { locale, surahId } = document.querySelector<HTMLElement>('.root')!.dataset;
|
const { locale, surahId } = document.querySelector<HTMLElement>('.root')!.dataset;
|
||||||
|
|
||||||
WebPackage({
|
Packet({
|
||||||
scripts: ['/js/pages/surah/stream.js'],
|
scripts: ['/js/pages/surah/stream.js'],
|
||||||
stylesheets: ['/css/pages/surah/stream.css'],
|
stylesheets: ['/css/pages/surah/stream.css'],
|
||||||
images: ['/images/moon.svg', '/images/leaf.svg'],
|
images: ['/images/moon.svg', '/images/leaf.svg'],
|
||||||
|
@ -22,7 +23,7 @@ import WebPackage from 'lib/WebPackage';
|
||||||
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
||||||
}
|
}
|
||||||
}).fetch()
|
}).fetch()
|
||||||
.then((pkg) => {
|
.then((pkg: PacketTarget) => {
|
||||||
inlineStyle.remove();
|
inlineStyle.remove();
|
||||||
parent.remove();
|
parent.remove();
|
||||||
pkg.fonts.forEach((f) => document.fonts.add(f));
|
pkg.fonts.forEach((f) => document.fonts.add(f));
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"*": ["src/js/*"]
|
|
||||||
},
|
|
||||||
"lib": [
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"module": "es6",
|
"strictNullChecks": false,
|
||||||
"target": "es6",
|
"module": "commonjs",
|
||||||
"jsx": "react",
|
"target": "ES2020",
|
||||||
"allowJs": true,
|
"noImplicitAny": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"allowJs": true,
|
||||||
|
"lib": [ "ES2020", "DOM" ],
|
||||||
|
|
||||||
|
"baseUrl": "src/",
|
||||||
|
"paths": { "*": ["js/*"] },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,6 @@ const process = require('process');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: process.env.NODE_ENV || "development",
|
mode: process.env.NODE_ENV || "development",
|
||||||
experiments: {
|
|
||||||
asyncWebAssembly: true
|
|
||||||
},
|
|
||||||
resolve: {
|
resolve: {
|
||||||
roots: [path.resolve('src/js'), path.resolve('node_modules')],
|
roots: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||||
modules: [path.resolve('src/js'), path.resolve('node_modules')],
|
modules: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||||
|
|
Loading…
Reference in a new issue