Compare commits
10 commits
39d5f51ee0
...
4f222ffda6
Author | SHA1 | Date | |
---|---|---|---|
4f222ffda6 | |||
d552685ff2 | |||
9edf257a1d | |||
9d831d6d04 | |||
3b9f683b5b | |||
c7112a7640 | |||
466c10ba08 | |||
2efc9eace2 | |||
23a2b2f9e8 | |||
bb1d672339 |
8 changed files with 195 additions and 112 deletions
18
.eslintrc.js
Normal file
18
.eslintrc.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: ["standard-with-typescript", "prettier"],
|
||||||
|
plugins: ["prettier"],
|
||||||
|
parserOptions: { project: "./tsconfig.json", },
|
||||||
|
rules: {
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 85,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
};
|
37
README.md
37
README.md
|
@ -19,7 +19,7 @@ progress bar is removed once the delivery is complete:
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Postman</title>
|
<title>Postman</title>
|
||||||
<script type="module" src="/postman.js"></script>
|
<script type="module" src="delivery.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="postman loader">
|
<div class="postman loader">
|
||||||
|
@ -31,39 +31,46 @@ progress bar is removed once the delivery is complete:
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
**postman.js**
|
**delivery.js**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import postman, { item } from "postman";
|
import postman, { item } from "postman";
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const progressBar = document.querySelector("progress");
|
const bar = document.querySelector("progress");
|
||||||
const span = document.querySelector(".percentage");
|
const span = document.querySelector(".percentage");
|
||||||
postman(
|
const delivery = postman(
|
||||||
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||||
item.script("/js/app.js"),
|
item.script("/js/app.js"),
|
||||||
item.image("/images/app.png"),
|
item.image("/images/app.png"),
|
||||||
item.css("/css/app.css"),
|
item.css("/css/app.css"),
|
||||||
item.progress((percent) => {
|
item.progress((percent) => {
|
||||||
progressBar.value = percent;
|
bar.value = percent;
|
||||||
span.innerText = `${percent}%`;
|
span.innerText = `${percent}%`;
|
||||||
})
|
})
|
||||||
).deliver()
|
).deliver();
|
||||||
.then((package) => {
|
|
||||||
/* Add page assets */
|
delivery.then((package) => {
|
||||||
package.fonts.forEach((font) => documents.fonts.add(font));
|
/* Add page assets */
|
||||||
package.scripts.forEach((script) => document.body.appendChild(script));
|
package.fonts.forEach((font) => documents.fonts.add(font));
|
||||||
package.css.forEach((css) => document.head.appendChild(css));
|
package.scripts.forEach((script) => document.body.appendChild(script));
|
||||||
/* Replace progress bar */
|
package.css.forEach((css) => document.head.appendChild(css));
|
||||||
progressBar.remove();
|
/* Replace progress bar */
|
||||||
span.remove();
|
bar.remove();
|
||||||
});
|
span.remove();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
* [https://al-quran.reflectslight.io/](https://al-quran.reflectslight.io) <br>
|
||||||
|
Delivers all of its assets with Postman
|
||||||
|
|
||||||
## Sources
|
## Sources
|
||||||
|
|
||||||
* [GitHub](https://github.com/0x1eef/postman)
|
* [GitHub](https://github.com/0x1eef/postman)
|
||||||
* [GitLab](https://gitlab.com/0x1eef/postman)
|
* [GitLab](https://gitlab.com/0x1eef/postman)
|
||||||
|
* [brew.bsd.cafe/@0x1eef](https://brew.bsd.cafe/@0x1eef)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
12
package.json
12
package.json
|
@ -3,10 +3,14 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Delivers the assets of a web page",
|
"description": "Delivers the assets of a web page",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": ["dist/index.d.ts"],
|
"types": [
|
||||||
|
"dist/index.d.ts"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm exec tsc",
|
"build": "npm exec tsc",
|
||||||
"prepare": "npm run build"
|
"prepare": "npm run build",
|
||||||
|
"tsc": "npm exec tsc -- --noEmit",
|
||||||
|
"eslint": "npm exec eslint -- src/*.ts src/**/*.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -16,6 +20,10 @@
|
||||||
"license": "0BSDL",
|
"license": "0BSDL",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.18",
|
"@types/node": "^16.18",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
|
"ts-standard": "^12.0.2",
|
||||||
"typescript": "^4.5"
|
"typescript": "^4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
share/postman/examples/progressbar/delivery.js
Normal file
25
share/postman/examples/progressbar/delivery.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import postman, { item } from "postman";
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const bar = document.querySelector("progress");
|
||||||
|
const span = document.querySelector(".percentage");
|
||||||
|
const delivery = postman(
|
||||||
|
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||||
|
item.script("/js/app.js"),
|
||||||
|
item.image("/images/app.png"),
|
||||||
|
item.css("/css/app.css"),
|
||||||
|
item.progress((percent) => {
|
||||||
|
bar.value = percent;
|
||||||
|
span.innerText = `${percent}%`;
|
||||||
|
})
|
||||||
|
).deliver();
|
||||||
|
|
||||||
|
delivery.then((package) => {
|
||||||
|
/* Add page assets */
|
||||||
|
package.fonts.forEach((font) => documents.fonts.add(font));
|
||||||
|
package.scripts.forEach((script) => document.body.appendChild(script));
|
||||||
|
package.css.forEach((css) => document.head.appendChild(css));
|
||||||
|
/* Replace progress bar */
|
||||||
|
bar.remove();
|
||||||
|
span.remove();
|
||||||
|
});
|
||||||
|
});
|
14
share/postman/examples/progressbar/index.html
Normal file
14
share/postman/examples/progressbar/index.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Postman</title>
|
||||||
|
<script type="module" src="postman.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="postman loader">
|
||||||
|
<progress value="0" max="100"></progress>
|
||||||
|
<span class="percentage"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
96
src/index.ts
96
src/index.ts
|
@ -1,73 +1,79 @@
|
||||||
import type { Item, FontItem } from './postman/item';
|
import type { Item, FontItem } from "./postman/item";
|
||||||
import item from './postman/item';
|
import item from "./postman/item";
|
||||||
import request from './postman/request';
|
import request from "./postman/request";
|
||||||
|
|
||||||
type Postman = { deliver: () => Promise<Package> };
|
interface Postman {
|
||||||
type Args = Array<Item | FontItem | Function>
|
deliver: () => Promise<Package>;
|
||||||
|
}
|
||||||
|
type Args = Array<Item | FontItem | ((n: number) => number)>;
|
||||||
type Items = Array<Item | FontItem>;
|
type Items = Array<Item | FontItem>;
|
||||||
type Package = {
|
interface Package {
|
||||||
fonts: FontFace[]
|
fonts: FontFace[];
|
||||||
images: HTMLElement[]
|
images: HTMLElement[];
|
||||||
css: HTMLElement[]
|
css: HTMLElement[];
|
||||||
scripts: HTMLElement[]
|
scripts: HTMLElement[];
|
||||||
json: HTMLElement[]
|
json: HTMLElement[];
|
||||||
};
|
}
|
||||||
|
|
||||||
function parseArgs(args: Args): [Items, Function] {
|
function parseArgs(args: Args): [Items, (n: number) => number] {
|
||||||
const items: Items = [];
|
const items: Items = [];
|
||||||
let callback: Function = (n: number) => n
|
let progresscb = (n: number): number => n;
|
||||||
args.forEach((item) => {
|
args.forEach(item => {
|
||||||
if (typeof item === 'function') {
|
if (typeof item === "function") {
|
||||||
callback = item;
|
progresscb = item;
|
||||||
} else {
|
} else {
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return [items, callback];
|
return [items.sort((i1, i2) => (i1.priority >= i2.priority ? 1 : -1)), progresscb];
|
||||||
}
|
}
|
||||||
|
|
||||||
export { item };
|
export { item };
|
||||||
|
|
||||||
export default function (...args: Args) {
|
export default function (...args: Args): Postman {
|
||||||
const self: Postman = Object.create(null);
|
const self = Object.create(null);
|
||||||
const result: Package = { fonts: [], images: [], css: [], scripts: [], json: [] };
|
const [items, progresscb] = parseArgs(args);
|
||||||
const [items, callback] = parseArgs(args);
|
const _package: Package = {
|
||||||
items.sort((i1, i2) => i1.priority >= i2.priority ? 1 : -1);
|
fonts: [],
|
||||||
|
images: [],
|
||||||
|
css: [],
|
||||||
|
scripts: [],
|
||||||
|
json: [],
|
||||||
|
};
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const onProgress = <T>(el: T) => {
|
function onProgress<T>(el: T): T {
|
||||||
index++;
|
index++;
|
||||||
if (index <= items.length) {
|
if (index <= items.length) {
|
||||||
callback(100 * (index / items.length));
|
progresscb(100 * (index / items.length));
|
||||||
}
|
}
|
||||||
return el;
|
return el;
|
||||||
};
|
}
|
||||||
|
|
||||||
const spawnRequests = () => {
|
function fetch(): Array<Promise<Package | null>> {
|
||||||
const reqs = items.map((item: Item | FontItem) => {
|
return items.map(async (item: Item | FontItem) => {
|
||||||
if ('fontFamily' in item) {
|
if ("fontFamily" in item) {
|
||||||
const req = request.font;
|
const req = request.font;
|
||||||
return req(item)
|
return await req(item)
|
||||||
.then((el) => onProgress<FontFace>(el))
|
.then(el => onProgress<FontFace>(el))
|
||||||
.then((font) => result.fonts.push(font))
|
.then(font => _package.fonts.push(font))
|
||||||
.then(() => result);
|
.then(() => _package);
|
||||||
} else if(item.requestId !== 'font' && item.group !== 'fonts') {
|
} else if (item.requestId !== "font" && item.group !== "fonts") {
|
||||||
const req = request[item.requestId];
|
const req = request[item.requestId];
|
||||||
const ary = result[item.group];
|
const ary = _package[item.group];
|
||||||
return req(item)
|
return await req(item)
|
||||||
.then((el) => onProgress<HTMLElement>(el))
|
.then(el => onProgress<HTMLElement>(el))
|
||||||
.then((el) => ary.push(el))
|
.then(el => ary.push(el))
|
||||||
.then(() => result);
|
.then(() => _package);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
/* unreachable */
|
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
return reqs as Array<Promise<Package>>;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
self.deliver = async () => {
|
self.deliver = async () => {
|
||||||
await Promise.all<Package>(spawnRequests());
|
await Promise.all(fetch());
|
||||||
return result;
|
return _package;
|
||||||
};
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
|
@ -1,65 +1,70 @@
|
||||||
type Group = 'fonts' | 'images' | 'css' | 'scripts' | 'json';
|
type Group = "fonts" | "images" | "css" | "scripts" | "json";
|
||||||
type RequestID = 'font' | 'image' | 'css' | 'script' | 'json';
|
type RequestID = "font" | "image" | "css" | "script" | "json";
|
||||||
|
|
||||||
export type Item = {
|
export interface Item {
|
||||||
priority: number
|
priority: number;
|
||||||
group: Group
|
group: Group;
|
||||||
requestId: RequestID
|
requestId: RequestID;
|
||||||
href: string
|
href: string;
|
||||||
props?: Partial<HTMLElement>
|
props?: Partial<HTMLElement>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type FontItem = {
|
export type FontItem = {
|
||||||
fontFamily: string
|
fontFamily: string;
|
||||||
} & Item;
|
} & Item;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
font(fontFamily: string, href: string): FontItem {
|
font(fontFamily: string, href: string): FontItem {
|
||||||
return {
|
return {
|
||||||
priority: 1,
|
priority: 1,
|
||||||
group: 'fonts',
|
group: "fonts",
|
||||||
requestId: 'font',
|
requestId: "font",
|
||||||
href, fontFamily,
|
href,
|
||||||
|
fontFamily,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
image(href: string, props?: Partial<HTMLElement>): Item {
|
image(href: string, props?: Partial<HTMLElement>): Item {
|
||||||
return {
|
return {
|
||||||
priority: 2,
|
priority: 2,
|
||||||
group: 'images',
|
group: "images",
|
||||||
requestId: 'image',
|
requestId: "image",
|
||||||
href, props
|
href,
|
||||||
|
props,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
css(href: string, props?: Partial<HTMLElement>): Item {
|
css(href: string, props?: Partial<HTMLElement>): Item {
|
||||||
return {
|
return {
|
||||||
priority: 3,
|
priority: 3,
|
||||||
group: 'css',
|
group: "css",
|
||||||
requestId: 'css',
|
requestId: "css",
|
||||||
href, props
|
href,
|
||||||
|
props,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
script(href: string, props?: Partial<HTMLElement>): Item {
|
script(href: string, props?: Partial<HTMLElement>): Item {
|
||||||
return {
|
return {
|
||||||
priority: 4,
|
priority: 4,
|
||||||
group: 'scripts',
|
group: "scripts",
|
||||||
requestId: 'script',
|
requestId: "script",
|
||||||
href, props
|
href,
|
||||||
|
props,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
json(href: string, props?: Partial<HTMLElement>): Item {
|
json(href: string, props?: Partial<HTMLElement>): Item {
|
||||||
return {
|
return {
|
||||||
priority: 5,
|
priority: 5,
|
||||||
group: 'json',
|
group: "json",
|
||||||
requestId: 'json',
|
requestId: "json",
|
||||||
href, props
|
href,
|
||||||
}
|
props,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
progress(fn: (percent: number) => void): (percent: number) => void {
|
progress(fn: (percent: number) => void): (percent: number) => void {
|
||||||
return fn;
|
return fn;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
import type { Item, FontItem } from './item';
|
import type { Item, FontItem } from "./item";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
font(item: FontItem): Promise<FontFace> {
|
async font(item: FontItem): Promise<FontFace> {
|
||||||
const { fontFamily, href } = item;
|
const { fontFamily, href } = item;
|
||||||
return new FontFace(fontFamily, href).load();
|
return await new FontFace(fontFamily, href).load();
|
||||||
},
|
},
|
||||||
|
|
||||||
script(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
async script(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
||||||
const { href } = item;
|
const { href } = item;
|
||||||
return fetch(href, options)
|
return await fetch(href, options)
|
||||||
.then((res) => res.text())
|
.then(async res => await res.text())
|
||||||
.then((text) => ({ type: 'application/javascript', text }))
|
.then(text => ({ type: "application/javascript", text }))
|
||||||
.then((props) => Object.assign(document.createElement('script'), props));
|
.then(props => Object.assign(document.createElement("script"), props));
|
||||||
},
|
},
|
||||||
|
|
||||||
css(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
async css(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
||||||
const { href } = item;
|
const { href } = item;
|
||||||
return fetch(href, options)
|
return await fetch(href, options)
|
||||||
.then((res) => res.text())
|
.then(async res => await res.text())
|
||||||
.then((text) => ({ innerText: text }))
|
.then(text => ({ innerText: text }))
|
||||||
.then((props) => Object.assign(document.createElement('style'), props));
|
.then(props => Object.assign(document.createElement("style"), props));
|
||||||
},
|
},
|
||||||
|
|
||||||
image(item: Item): Promise<HTMLElement> {
|
async image(item: Item): Promise<HTMLElement> {
|
||||||
const { href } = item;
|
const { href } = item;
|
||||||
return new Promise<HTMLElement>((resolve, reject) => {
|
return await new Promise<HTMLElement>((resolve, reject) => {
|
||||||
const el = document.createElement('img');
|
const el = document.createElement("img");
|
||||||
el.onload = () => resolve(el);
|
el.onload = () => resolve(el);
|
||||||
el.onerror = reject;
|
el.onerror = reject;
|
||||||
el.src = href;
|
el.src = href;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
json(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
async json(item: Item, options: RequestInit = {}): Promise<HTMLElement> {
|
||||||
const { href } = item;
|
const { href } = item;
|
||||||
return fetch(href, options)
|
return await fetch(href, options)
|
||||||
.then((res) => res.text())
|
.then(async res => await res.text())
|
||||||
.then((text) => ({type: 'application/json', text}))
|
.then(text => ({ type: "application/json", text }))
|
||||||
.then((props) => Object.assign(props, item.props || {}))
|
.then(props => Object.assign(props, item.props != null || {}))
|
||||||
.then((props) => Object.assign(document.createElement('script'), props));
|
.then(props => Object.assign(document.createElement("script"), props));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue