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": {
|
||||
"eslint": "node ./node_modules/eslint/bin/eslint.js 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": {
|
||||
"@types/css-font-loading-module": "^0.0.7",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"classnames": "^2.3.2",
|
||||
"es-cookie": "^1.4.0",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"prettier": "^2.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"ts-loader": "^9.3.1",
|
||||
"ts-standard": "^12.0.1",
|
||||
"typescript": "^4.8.2",
|
||||
"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() {
|
||||
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
||||
|
@ -6,7 +7,7 @@ import WebPackage from 'lib/WebPackage';
|
|||
const progressNumber: HTMLSpanElement = parent.querySelector('.percentage')!;
|
||||
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
||||
|
||||
WebPackage({
|
||||
Packet({
|
||||
scripts: ['/js/pages/surah/index.js'],
|
||||
stylesheets: ['/css/pages/surah/index.css'],
|
||||
images: ['/images/moon.svg', '/images/leaf.svg'],
|
||||
|
@ -21,7 +22,7 @@ import WebPackage from 'lib/WebPackage';
|
|||
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
||||
}
|
||||
}).fetch()
|
||||
.then((pkg) => {
|
||||
.then((pkg: PacketTarget) => {
|
||||
inlineStyle.remove();
|
||||
parent.remove();
|
||||
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() {
|
||||
const parent: HTMLElement = document.querySelector('.webpackage.loader')!;
|
||||
|
@ -7,7 +8,7 @@ import WebPackage from 'lib/WebPackage';
|
|||
const inlineStyle: HTMLStyleElement = document.querySelector('.css.webpackage')!;
|
||||
const { locale, surahId } = document.querySelector<HTMLElement>('.root')!.dataset;
|
||||
|
||||
WebPackage({
|
||||
Packet({
|
||||
scripts: ['/js/pages/surah/stream.js'],
|
||||
stylesheets: ['/css/pages/surah/stream.css'],
|
||||
images: ['/images/moon.svg', '/images/leaf.svg'],
|
||||
|
@ -22,7 +23,7 @@ import WebPackage from 'lib/WebPackage';
|
|||
progressNumber.innerText = `${percent.toFixed(0)}%`;
|
||||
}
|
||||
}).fetch()
|
||||
.then((pkg) => {
|
||||
.then((pkg: PacketTarget) => {
|
||||
inlineStyle.remove();
|
||||
parent.remove();
|
||||
pkg.fonts.forEach((f) => document.fonts.add(f));
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["src/js/*"]
|
||||
},
|
||||
"lib": [
|
||||
"dom"
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"strict": true,
|
||||
"module": "es6",
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"strictNullChecks": false,
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"lib": [ "ES2020", "DOM" ],
|
||||
|
||||
"baseUrl": "src/",
|
||||
"paths": { "*": ["js/*"] },
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,6 @@ const process = require('process');
|
|||
|
||||
module.exports = {
|
||||
mode: process.env.NODE_ENV || "development",
|
||||
experiments: {
|
||||
asyncWebAssembly: true
|
||||
},
|
||||
resolve: {
|
||||
roots: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||
modules: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||
|
|
Loading…
Reference in a new issue