@eik/webpack-plugin 1.0.38 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eik/webpack-plugin",
3
- "version": "1.0.38",
3
+ "version": "2.0.0",
4
4
  "description": "WebPack plugin for loading import maps from an Eik server and applying the mapping to ECMAScript modules in preparation for upload to the same server.",
5
5
  "type": "module",
6
6
  "main": "./src/loader.cjs",
@@ -8,10 +8,9 @@
8
8
  "src"
9
9
  ],
10
10
  "scripts": {
11
- "test": "tap --no-coverage",
12
- "test:snapshot": "TAP_SNAPSHOT=1 tap --no-coverage",
13
- "lint": "eslint . --ext=js,cjs",
14
- "lint:fix": "eslint . --fix --ext=js,cjs"
11
+ "test": "tap --disable-coverage --allow-empty-coverage",
12
+ "test:snapshot": "TAP_SNAPSHOT=1 tap --allow-empty-coverage",
13
+ "lint": "eslint ."
15
14
  },
16
15
  "repository": {
17
16
  "type": "git",
@@ -34,18 +33,19 @@
34
33
  "devDependencies": {
35
34
  "@semantic-release/changelog": "6.0.3",
36
35
  "@semantic-release/git": "10.0.1",
37
- "webpack": "5.90.0",
36
+ "webpack": "5.91.0",
38
37
  "webpack-cli": "5.1.4",
39
- "eslint": "8.56.0",
40
- "eslint-config-airbnb-base": "15.0.0",
41
- "eslint-plugin-import": "2.29.1",
42
- "fastify": "4.26.0",
43
- "semantic-release": "21.1.2",
44
- "tap": "16.3.10",
45
- "memfs": "4.6.0"
38
+ "eslint": "9.6.0",
39
+ "eslint-config-prettier": "9.1.0",
40
+ "eslint-plugin-prettier": "5.1.3",
41
+ "fastify": "4.28.1",
42
+ "globals": "15.8.0",
43
+ "semantic-release": "24.0.0",
44
+ "tap": "21.0.0",
45
+ "memfs": "4.9.3"
46
46
  },
47
47
  "dependencies": {
48
48
  "@eik/common": "3.0.1",
49
- "undici": "5.28.3"
49
+ "undici": "6.19.4"
50
50
  }
51
51
  }
@@ -0,0 +1,44 @@
1
+ import * as parsers from "./parsers.js";
2
+
3
+ const RX_SIDE_EFFECTS_IMPORT = parsers.sideEffectsImport();
4
+ const RX_STANDARD_IMPORT = parsers.standardImport();
5
+ const RX_DYNAMIC_IMPORT = parsers.dynamicImport();
6
+
7
+ export const standardImport = ({ dictionary = new Map(), source = "" }) => {
8
+ const result = source.replace(
9
+ RX_STANDARD_IMPORT,
10
+ (replacer, g1, g2, g3, g4) => {
11
+ const dep = dictionary.get(g4) || g4;
12
+ return `${g1} ${g2} ${g3} '${dep}'`;
13
+ },
14
+ );
15
+
16
+ return {
17
+ source: result,
18
+ dictionary,
19
+ };
20
+ };
21
+
22
+ export const dynamicImport = ({ dictionary = new Map(), source = "" }) => {
23
+ const result = source.replace(RX_DYNAMIC_IMPORT, (replacer, g1, g2) => {
24
+ const dep = dictionary.get(g2) || g2;
25
+ return `${g1}('${dep}')`;
26
+ });
27
+
28
+ return {
29
+ source: result,
30
+ dictionary,
31
+ };
32
+ };
33
+
34
+ export const sideEffectsImport = ({ dictionary = new Map(), source = "" }) => {
35
+ const result = source.replace(RX_SIDE_EFFECTS_IMPORT, (replacer, g1, g2) => {
36
+ const dep = dictionary.get(g2) || g2;
37
+ return `${g1} '${dep}'`;
38
+ });
39
+
40
+ return {
41
+ source: result,
42
+ dictionary,
43
+ };
44
+ };
package/src/loader.js ADDED
@@ -0,0 +1,67 @@
1
+ import { helpers } from "@eik/common";
2
+ import * as utils from "./utils.js";
3
+ import {
4
+ standardImport,
5
+ dynamicImport,
6
+ sideEffectsImport,
7
+ } from "./builders.js";
8
+
9
+ const dictionary = new Map();
10
+ let cold = true;
11
+
12
+ export default async function loader(source) {
13
+ const options = this.getOptions();
14
+ const callback = this.async();
15
+
16
+ if (cold) {
17
+ const pPath = options.path ? options.path : process.cwd();
18
+ const pMaps = Array.isArray(options.maps) ? options.maps : [options.maps];
19
+ const pUrls = Array.isArray(options.urls) ? options.urls : [options.urls];
20
+
21
+ // Filter out any empty (undefined, null) values in the option arrays
22
+ const urls = pUrls.filter((item) => {
23
+ if (item) return true;
24
+ return false;
25
+ });
26
+
27
+ const maps = pMaps.filter((item) => {
28
+ if (item) return true;
29
+ return false;
30
+ });
31
+
32
+ // Load eik config from eik.json or package.json
33
+ const eikConfig = await helpers.getDefaults(pPath);
34
+
35
+ // Merge map from eik config and the plugin options and Fetch all import maps over http
36
+ const fetchedMaps = await utils.fetchImportMaps([
37
+ ...eikConfig.map,
38
+ ...urls,
39
+ ]);
40
+
41
+ // Validate each import map and push each import statement into a dictionary
42
+ maps
43
+ .concat(fetchedMaps)
44
+ .map((item) => utils.validate(item))
45
+ .forEach((item) => {
46
+ item.forEach((obj) => {
47
+ dictionary.set(obj.key, obj.value);
48
+ });
49
+ });
50
+
51
+ // Loading of config and import maps should only happen once
52
+ cold = false;
53
+ }
54
+
55
+ new Promise((resolve) => {
56
+ resolve({
57
+ dictionary,
58
+ source,
59
+ });
60
+ })
61
+ .then(standardImport)
62
+ .then(dynamicImport)
63
+ .then(sideEffectsImport)
64
+ .then((obj) => {
65
+ callback(null, obj.source);
66
+ });
67
+ }
package/src/parsers.js ADDED
@@ -0,0 +1,11 @@
1
+ export const standardImport = (flags = "sgm") =>
2
+ new RegExp(
3
+ "(import)\\s*([\\w{},\\n\\s\\[\\]\\*\\.]+?)\\s*(from)\\s*['\"]([\\w@\\-\\./]+?)['\"]",
4
+ flags,
5
+ );
6
+
7
+ export const dynamicImport = (flags = "gm") =>
8
+ new RegExp("(import)[(]['\"](.+?)['\"][)]", flags);
9
+
10
+ export const sideEffectsImport = (flags = "gm") =>
11
+ new RegExp("(import)\\s*['\"](.+?)['\"]", flags);
package/src/utils.js ADDED
@@ -0,0 +1,71 @@
1
+ import { request } from "undici";
2
+
3
+ /**
4
+ * Whether or not a string looks like a bare import.
5
+ * @param {string} str
6
+ * @returns {boolean}
7
+ */
8
+ export const isBare = (str) => {
9
+ if (
10
+ str.startsWith("/") ||
11
+ str.startsWith("./") ||
12
+ str.startsWith("../") ||
13
+ str.substr(0, 7) === "http://" ||
14
+ str.substr(0, 8) === "https://"
15
+ ) {
16
+ return false;
17
+ }
18
+ return true;
19
+ };
20
+
21
+ /**
22
+ * Runs typeof
23
+ * @param {any} str
24
+ * @returns {boolean}
25
+ */
26
+ export const isString = (str) => typeof str === "string";
27
+
28
+ /**
29
+ * @param {any} map
30
+ * @returns {Array<{ key: string; value: string }>}
31
+ */
32
+ export const validate = (map) =>
33
+ Object.keys(map.imports).map((key) => {
34
+ const value = map.imports[key];
35
+
36
+ if (isBare(value)) {
37
+ throw Error(
38
+ `Import specifier can NOT be mapped to a bare import statement. Import specifier "${key}" is being wrongly mapped to "${value}"`,
39
+ );
40
+ }
41
+
42
+ return { key, value };
43
+ });
44
+
45
+ /**
46
+ *
47
+ * @param {string[]} [urls=[]]
48
+ * @returns
49
+ */
50
+ export const fetchImportMaps = async (urls = []) => {
51
+ try {
52
+ const maps = urls.map(async (map) => {
53
+ const { statusCode, body } = await request(map, { maxRedirections: 2 });
54
+
55
+ if (statusCode === 404) {
56
+ throw new Error("Import map could not be found on server");
57
+ } else if (statusCode >= 400 && statusCode < 500) {
58
+ throw new Error("Server rejected client request");
59
+ } else if (statusCode >= 500) {
60
+ throw new Error("Server error");
61
+ }
62
+
63
+ return body.json();
64
+ });
65
+ return await Promise.all(maps);
66
+ } catch (err) {
67
+ throw new Error(
68
+ `Unable to load import map file from server: ${err.message}`,
69
+ );
70
+ }
71
+ };
package/src/builders.cjs DELETED
@@ -1,44 +0,0 @@
1
- const parsers = require('./parsers.cjs');
2
-
3
- const RX_SIDE_EFFECTS_IMPORT = parsers.sideEffectsImport();
4
- const RX_STANDARD_IMPORT = parsers.standardImport();
5
- const RX_DYNAMIC_IMPORT = parsers.dynamicImport();
6
-
7
- const standardImport = ({ dictionary = new Map(), source = '' }) => {
8
- const result = source.replace(RX_STANDARD_IMPORT, (replacer, g1, g2, g3, g4) => {
9
- const dep = dictionary.get(g4) || g4;
10
- return `${g1} ${g2} ${g3} '${dep}'`;
11
- });
12
-
13
- return {
14
- source: result,
15
- dictionary,
16
- };
17
- };
18
- module.exports.standardImport = standardImport;
19
-
20
- const dynamicImport = ({ dictionary = new Map(), source = '' }) => {
21
- const result = source.replace(RX_DYNAMIC_IMPORT, (replacer, g1, g2) => {
22
- const dep = dictionary.get(g2) || g2;
23
- return `${g1}('${dep}')`;
24
- });
25
-
26
- return {
27
- source: result,
28
- dictionary,
29
- };
30
- };
31
- module.exports.dynamicImport = dynamicImport;
32
-
33
- const sideEffectsImport = ({ dictionary = new Map(), source = '' }) => {
34
- const result = source.replace(RX_SIDE_EFFECTS_IMPORT, (replacer, g1, g2) => {
35
- const dep = dictionary.get(g2) || g2;
36
- return `${g1} '${dep}'`;
37
- });
38
-
39
- return {
40
- source: result,
41
- dictionary,
42
- };
43
- };
44
- module.exports.sideEffectsImport = sideEffectsImport;
package/src/loader.cjs DELETED
@@ -1,58 +0,0 @@
1
- const { helpers } = require('@eik/common');
2
- const utils = require('./utils.cjs');
3
- const { standardImport, dynamicImport, sideEffectsImport } = require('./builders.cjs');
4
-
5
- const dictionary = new Map();
6
- let cold = true;
7
-
8
- async function loader(source) {
9
- const options = this.getOptions();
10
- const callback = this.async();
11
-
12
- if (cold) {
13
- const pPath = options.path ? options.path : process.cwd();
14
- const pMaps = Array.isArray(options.maps) ? options.maps : [options.maps];
15
- const pUrls = Array.isArray(options.urls) ? options.urls : [options.urls];
16
-
17
- // Filter out any empty (undefined, null) values in the option arrays
18
- const urls = pUrls.filter((item) => {
19
- if (item) return true;
20
- return false;
21
- });
22
-
23
- const maps = pMaps.filter((item) => {
24
- if (item) return true;
25
- return false;
26
- });
27
-
28
- // Load eik config from eik.json or package.json
29
- const eikConfig = await helpers.getDefaults(pPath);
30
-
31
- // Merge map from eik config and the plugin options and Fetch all import maps over http
32
- const fetchedMaps = await utils.fetchImportMaps([...eikConfig.map, ...urls]);
33
-
34
- // Validate each import map and push each import statement into a dictionary
35
- maps.concat(fetchedMaps).map((item) => utils.validate(item)).forEach((item) => {
36
- item.forEach((obj) => {
37
- dictionary.set(obj.key, obj.value);
38
- });
39
- });
40
-
41
- // Loading of config and import maps should only happen once
42
- cold = false;
43
- }
44
-
45
- new Promise((resolve) => {
46
- resolve({
47
- dictionary,
48
- source,
49
- });
50
- })
51
- .then(standardImport)
52
- .then(dynamicImport)
53
- .then(sideEffectsImport)
54
- .then((obj) => {
55
- callback(null, obj.source);
56
- });
57
- }
58
- module.exports = loader;
package/src/parsers.cjs DELETED
@@ -1,8 +0,0 @@
1
- const standardImport = (flags = 'sgm') => new RegExp('(import)\\s*([\\w{},\\n\\s\\[\\]\\*\\.]+?)\\s*(from)\\s*[\'"]([\\w@\\-\\./]+?)[\'"]', flags);
2
- module.exports.standardImport = standardImport;
3
-
4
- const dynamicImport = (flags = 'gm') => new RegExp('(import)[(][\'"](.+?)[\'"][)]', flags);
5
- module.exports.dynamicImport = dynamicImport;
6
-
7
- const sideEffectsImport = (flags = 'gm') => new RegExp('(import)\\s*[\'"](.+?)[\'"]', flags);
8
- module.exports.sideEffectsImport = sideEffectsImport;
package/src/utils.cjs DELETED
@@ -1,50 +0,0 @@
1
- const { request } = require('undici');
2
-
3
- const isBare = (str) => {
4
- if (str.startsWith('/') || str.startsWith('./') || str.startsWith('../') || str.substr(0, 7) === 'http://' || str.substr(0, 8) === 'https://') {
5
- return false;
6
- }
7
- return true;
8
- };
9
- module.exports.isBare = isBare;
10
-
11
- const isString = (str) => typeof str === 'string';
12
- module.exports.isString = isString;
13
-
14
- const validate = (map) => Object.keys(map.imports).map((key) => {
15
- const value = map.imports[key];
16
-
17
- if (isBare(value)) {
18
- throw Error(`Import specifier can NOT be mapped to a bare import statement. Import specifier "${key}" is being wrongly mapped to "${value}"`);
19
- }
20
-
21
- return { key, value };
22
- });
23
- module.exports.validate = validate;
24
-
25
- const fetchImportMaps = async (urls = []) => {
26
- try {
27
- const maps = urls.map(async (map) => {
28
- const {
29
- statusCode,
30
- body,
31
- } = await request(map, { maxRedirections: 2 });
32
-
33
- if (statusCode === 404) {
34
- throw new Error('Import map could not be found on server');
35
- } else if (statusCode >= 400 && statusCode < 500) {
36
- throw new Error('Server rejected client request');
37
- } else if (statusCode >= 500) {
38
- throw new Error('Server error');
39
- }
40
-
41
- return body.json();
42
- });
43
- return await Promise.all(maps);
44
- } catch (err) {
45
- throw new Error(
46
- `Unable to load import map file from server: ${err.message}`,
47
- );
48
- }
49
- };
50
- module.exports.fetchImportMaps = fetchImportMaps;