dopant 5.0.2 → 6.0.0

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/README.md CHANGED
@@ -1,31 +1,60 @@
1
- The aerogel-weight & dead-simple resource loader 🚚
2
- ---
3
- This package provides **lightweight** dynamic resource loader for the web browsers.
4
-
5
- ## Prerequisites
6
-
7
- * Node.js `>= 20.x`
8
-
9
- ## Installation
10
-
11
- ```bash
12
- npm install dopant --save
13
- ```
14
-
15
- ### Usage
16
-
17
- ```javascript
18
- import dopant from 'dopant';
19
-
20
- dopant([
21
- '//somewhe.re/assets/css/layout.css',
22
- '//somewhe.re/assets/js/main.cjs',
23
- ]).then(() => console.log('pow!'));
24
-
25
- dopant('//somewhe.re/assets/js/lib.js')
26
- .then(() => dopant('//somewhe.re/assets/js/main.js'))
27
- .finally(() => console.log('pow!'));
28
-
29
- dopant('//somewhe.re/assets/js/index.mjs')
30
- .then(() => console.log('pow!'));
31
- ```
1
+ The aerogel-weight & dead-simple resource loader 🚚
2
+ ---
3
+ This package provides a **lightweight** dynamic resource loader for web browsers.
4
+
5
+ ## Abstract
6
+
7
+ * Promise-based API
8
+ * Parallel by default, ordered when required ⚡
9
+ * Supports all `<link>` relations (`preconnect`, `preload`, `stylesheet`, etc.) 🔗
10
+ * Supports all `<script>` types (`importmap`, `module`, `nomodule`, etc.) 📃
11
+ * Zero dependencies 🗽
12
+
13
+ ## Prerequisites
14
+
15
+ * Node.js `>= 20.0.0`
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install dopant --save
21
+ ```
22
+
23
+ ### Usage
24
+
25
+ ```javascript
26
+ import dopant from 'dopant';
27
+
28
+ await dopant(
29
+ '/assets/css/layout.css',
30
+ '/assets/js/main.js',
31
+ ['/assets/js/importmap.js', { type: 'importmap' }],
32
+ ['/assets/js/module.js', { defer: true, type: 'module' }],
33
+ [
34
+ '/assets/webfonts/font.woff2',
35
+ {
36
+ as: 'font',
37
+ rel: 'preload',
38
+ type: 'font/woff2',
39
+ }
40
+ ],
41
+ );
42
+ ```
43
+
44
+ ### API
45
+
46
+ #### `dopant(...resources)`
47
+
48
+ * `...resources` **{string | [string, attrs]}** Resources w/wo extra attributes to load into the web page
49
+ * **Returns:** Promise that resolves to a list of resolutions
50
+
51
+ ### Behavior
52
+
53
+ * CSS files default to `rel="stylesheet"`
54
+ * If `rel` is provided, a `<link>` element is created
55
+ * Otherwise, a `<script>` element is created
56
+ * Scripts default to `async: true` (unless overridden or `defer: true` is set)
57
+
58
+ ---
59
+
60
+ For more details, please check tests in the repository.
package/dist/index.cjs ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _default = (...args) => {
8
+ return Promise.allSettled(args.map(async it => {
9
+ const [url, attrs = {}] = Array.isArray(it) ? it : [it];
10
+ if (url?.constructor !== String || attrs?.constructor !== Object) {
11
+ throw new TypeError('Invalid input');
12
+ }
13
+ const isLink = attrs.rel || /\.\bcss\b/i.test(url);
14
+ const el = document.createElement(isLink ? 'link' : 'script');
15
+ if (/\bimportmap\b|\bspeculationrules\b/i.test(attrs.type)) {
16
+ const res = await fetch(url);
17
+ if (!res.ok) {
18
+ throw new Error(`Failed to fetch importmap: ${url}`);
19
+ }
20
+ el.textContent = await res.text();
21
+ el.type = attrs.type;
22
+ document.head.append(el);
23
+ return el;
24
+ }
25
+ Object.assign(el, isLink ? {
26
+ ...attrs,
27
+ href: url,
28
+ rel: attrs.rel || 'stylesheet'
29
+ } : {
30
+ ...attrs,
31
+ async: attrs.async ?? !attrs.defer,
32
+ src: url
33
+ });
34
+ return new Promise((res, rej) => {
35
+ el.onerror = ev => (ev.target.remove(), rej(ev.target));
36
+ el.onload = ev => res(ev.target);
37
+ document.head.append(el);
38
+ });
39
+ }));
40
+ };
41
+ exports.default = _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": {
3
- "name": "Yehor Sergeenko",
4
3
  "email": "yehor.sergeenko@gmail.com",
4
+ "name": "Yehor Sergeenko",
5
5
  "url": "https://github.com/bricss"
6
6
  },
7
7
  "bugs": {
@@ -9,22 +9,21 @@
9
9
  },
10
10
  "description": "The aerogel-weight & dead-simple resource loader 🚚",
11
11
  "devDependencies": {
12
- "@babel/cli": "^7.28.3",
13
- "@babel/core": "^7.28.5",
14
- "@babel/eslint-parser": "^7.28.5",
15
- "@babel/preset-env": "^7.28.5",
12
+ "@babel/cli": "^7.28.6",
13
+ "@babel/core": "^7.29.0",
14
+ "@babel/preset-env": "^7.29.0",
16
15
  "c8": "^10.1.3",
17
- "eslint": "^9.39.2",
18
- "eslint-config-ultra-refined": "^3.8.4",
16
+ "eslint": "^10.0.0",
17
+ "eslint-config-ultra-refined": "^4.0.1",
19
18
  "mocha": "^11.7.5",
20
- "playwright-chromium": "^1.57.0"
19
+ "playwright-chromium": "^1.58.2"
21
20
  },
22
21
  "engines": {
23
- "node": ">=20.x"
22
+ "node": ">=20.0.0"
24
23
  },
25
24
  "exports": {
26
- "import": "./src/index.mjs",
27
- "require": "./dist/index.js"
25
+ "import": "./src/index.js",
26
+ "require": "./dist/index.cjs"
28
27
  },
29
28
  "files": [
30
29
  "dist",
@@ -43,12 +42,14 @@
43
42
  "url": "git+https://github.com/bricss/dopant.git"
44
43
  },
45
44
  "scripts": {
46
- "build": "rm -rf dist && npx babel src -d dist",
47
- "lint": "eslint",
48
- "prepack": "npm run build",
45
+ "build": "rm -rf dist && npx babel src --out-dir dist --out-file-extension .cjs",
46
+ "lint": "eslint --concurrency=auto",
47
+ "prepack": "npm run build && sh misc.sh && npm run lint",
49
48
  "pretest": "rm -rf coverage",
50
- "test": "mocha --exit --recursive",
49
+ "test": "mocha",
50
+ "test:bail": "mocha --bail",
51
51
  "test:cover": "npm test && c8 report --reporter=lcov --reporter=text"
52
52
  },
53
- "version": "5.0.2"
53
+ "type": "module",
54
+ "version": "6.0.0"
54
55
  }
package/src/index.js ADDED
@@ -0,0 +1,49 @@
1
+ export default (...args) => {
2
+ return Promise.allSettled(
3
+ args.map(async (it) => {
4
+ const [url, attrs = {}] = Array.isArray(it) ? it : [it];
5
+
6
+ if (url?.constructor !== String || attrs?.constructor !== Object) {
7
+ throw new TypeError('Invalid input');
8
+ }
9
+
10
+ const isLink = attrs.rel || /\.\bcss\b/i.test(url);
11
+ const el = document.createElement(isLink ? 'link' : 'script');
12
+
13
+ if (/\bimportmap\b|\bspeculationrules\b/i.test(attrs.type)) {
14
+ const res = await fetch(url);
15
+
16
+ if (!res.ok) {
17
+ throw new Error(`Failed to fetch importmap: ${ url }`);
18
+ }
19
+
20
+ el.textContent = await res.text();
21
+ el.type = attrs.type;
22
+ document.head.append(el);
23
+
24
+ return el;
25
+ }
26
+
27
+ Object.assign(
28
+ el,
29
+ isLink
30
+ ? {
31
+ ...attrs,
32
+ href: url,
33
+ rel: attrs.rel || 'stylesheet',
34
+ }
35
+ : {
36
+ ...attrs,
37
+ async: attrs.async ?? !attrs.defer,
38
+ src: url,
39
+ },
40
+ );
41
+
42
+ return new Promise((res, rej) => {
43
+ el.onerror = (ev) => (ev.target.remove(), rej(ev.target));
44
+ el.onload = (ev) => res(ev.target);
45
+ document.head.append(el);
46
+ });
47
+ }),
48
+ );
49
+ };
package/dist/index.js DELETED
@@ -1,50 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _default = (...args) => {
8
- if (!args.length) {
9
- throw new TypeError('Missing required arguments');
10
- }
11
- args = args.flat();
12
- const {
13
- head
14
- } = document;
15
- for (let i = 0, j = args.length - 1; i <= j; i++) {
16
- const ext = args[i].split('?')[0].substring((~-args[i].lastIndexOf('.') >>> 0) + 2).toLowerCase();
17
- let el;
18
- if (ext === 'css') {
19
- el = document.createElement('link');
20
- el.href = args[i];
21
- el.rel = 'stylesheet';
22
- } else if (ext.match(/^c?js/)) {
23
- el = document.createElement('script');
24
- el.async = true;
25
- el.src = args[i];
26
- } else if (ext === 'mjs') {
27
- el = document.createElement('script');
28
- el.async = true;
29
- el.src = args[i];
30
- el.type = 'module';
31
- } else {
32
- args[i] = `Unsupported file type or extension: ${args[i]}`;
33
- console.warn(args[i]);
34
- }
35
- args[i] = el && new Promise((resolve, reject) => {
36
- el.onerror = ev => {
37
- reject((head.removeChild(ev.target), ev));
38
- };
39
- el.onload = ev => {
40
- resolve((ev.target.onload = ev.target.onerror = void 0, ev));
41
- };
42
- head.appendChild(el);
43
- });
44
- }
45
- return Promise.allSettled(args).then(results => results.reduce((acc, val) => {
46
- val.status === 'rejected' ? console.error(val.reason) : acc.push(val.value);
47
- return acc;
48
- }, []));
49
- };
50
- exports.default = _default;
package/src/index.mjs DELETED
@@ -1,56 +0,0 @@
1
- export default (...args) => {
2
- if (!args.length) {
3
- throw new TypeError('Missing required arguments');
4
- }
5
-
6
- args = args.flat();
7
-
8
- const { head } = document;
9
-
10
- for (let i = 0, j = args.length - 1; i <= j; i++) {
11
- const ext = args[i]
12
- .split('?')[0]
13
- .substring((~-args[i].lastIndexOf('.') >>> 0) + 2)
14
- .toLowerCase();
15
- let el;
16
-
17
- if (ext === 'css') {
18
- el = document.createElement('link');
19
- el.href = args[i];
20
- el.rel = 'stylesheet';
21
- } else if (ext.match(/^c?js/)) {
22
- el = document.createElement('script');
23
- el.async = true;
24
- el.src = args[i];
25
- } else if (ext === 'mjs') {
26
- el = document.createElement('script');
27
- el.async = true;
28
- el.src = args[i];
29
- el.type = 'module';
30
- } else {
31
- args[i] = `Unsupported file type or extension: ${ args[i] }`;
32
- console.warn(args[i]);
33
- }
34
-
35
- args[i] = el && new Promise((resolve, reject) => {
36
- el.onerror = (ev) => {
37
- reject((head.removeChild(ev.target), ev));
38
- };
39
-
40
- el.onload = (ev) => {
41
- resolve((ev.target.onload = ev.target.onerror = void 0, ev));
42
- };
43
-
44
- head.appendChild(el);
45
- });
46
- }
47
-
48
- return Promise.allSettled(args)
49
- .then((results) => results.reduce((acc, val) => {
50
- val.status === 'rejected'
51
- ? console.error(val.reason)
52
- : acc.push(val.value);
53
-
54
- return acc;
55
- }, []));
56
- };