dopant 5.0.4 → 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 a **lightweight** dynamic resource loader for the web browsers.
4
-
5
- ## Prerequisites
6
-
7
- * Node.js `>= 20.0.0`
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 CHANGED
@@ -5,46 +5,37 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
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]);
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');
34
12
  }
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);
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);
43
38
  });
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
- }, []));
39
+ }));
49
40
  };
50
41
  exports.default = _default;
package/package.json CHANGED
@@ -11,13 +11,12 @@
11
11
  "devDependencies": {
12
12
  "@babel/cli": "^7.28.6",
13
13
  "@babel/core": "^7.29.0",
14
- "@babel/eslint-parser": "^7.28.6",
15
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.5",
16
+ "eslint": "^10.0.0",
17
+ "eslint-config-ultra-refined": "^4.0.1",
19
18
  "mocha": "^11.7.5",
20
- "playwright-chromium": "^1.58.1"
19
+ "playwright-chromium": "^1.58.2"
21
20
  },
22
21
  "engines": {
23
22
  "node": ">=20.0.0"
@@ -52,5 +51,5 @@
52
51
  "test:cover": "npm test && c8 report --reporter=lcov --reporter=text"
53
52
  },
54
53
  "type": "module",
55
- "version": "5.0.4"
54
+ "version": "6.0.0"
56
55
  }
package/src/index.js CHANGED
@@ -1,55 +1,49 @@
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).then((results) => results.reduce((acc, val) => {
49
- val.status === 'rejected'
50
- ? console.error(val.reason)
51
- : acc.push(val.value);
52
-
53
- return acc;
54
- }, []));
55
- };
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
+ };