@eik/webpack-plugin 2.0.1 → 2.0.2

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
@@ -7,38 +7,38 @@ WebPack [Eik](https://eik.dev/) plugin to support the use of import maps to map
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- $ npm install @eik/webpack-plugin
10
+ npm install @eik/webpack-plugin
11
11
  ```
12
12
 
13
13
  ## Usage
14
14
 
15
15
  ```js
16
16
  export default {
17
- experiments: {
18
- outputModule: true,
19
- },
20
- entry: '/src/input.js',
21
- mode: 'production',
22
- output: {
23
- environment: {
24
- module: true,
25
- },
26
- filename: 'bundle.js',
27
- path: '/out/',
28
- },
29
- module: {
30
- rules: [
31
- {
32
- test: /\.js$/,
33
- use: {
34
- loader: '@eik/webpack-plugin',
35
- options: {
36
- path: '/path/to/eik-json-folder'
37
- },
38
- },
39
- },
40
- ],
41
- },
17
+ experiments: {
18
+ outputModule: true,
19
+ },
20
+ entry: "/src/input.js",
21
+ mode: "production",
22
+ output: {
23
+ environment: {
24
+ module: true,
25
+ },
26
+ filename: "bundle.js",
27
+ path: "/out/",
28
+ },
29
+ module: {
30
+ rules: [
31
+ {
32
+ test: /\.js$/,
33
+ use: {
34
+ loader: "@eik/webpack-plugin",
35
+ options: {
36
+ path: "/path/to/eik-json-folder",
37
+ },
38
+ },
39
+ },
40
+ ],
41
+ },
42
42
  };
43
43
  ```
44
44
 
@@ -55,20 +55,20 @@ The path to the location of an `eik.json` file can be specified with the `path`
55
55
 
56
56
  ```js
57
57
  export default {
58
- //...
59
- module: {
60
- rules: [
61
- {
62
- test: /\.js$/,
63
- use: {
64
- loader: '@eik/webpack-plugin',
65
- options: {
66
- path: '/path/to/eik-json-folder'
67
- },
68
- },
69
- },
70
- ],
71
- },
58
+ //...
59
+ module: {
60
+ rules: [
61
+ {
62
+ test: /\.js$/,
63
+ use: {
64
+ loader: "@eik/webpack-plugin",
65
+ options: {
66
+ path: "/path/to/eik-json-folder",
67
+ },
68
+ },
69
+ },
70
+ ],
71
+ },
72
72
  };
73
73
  ```
74
74
 
@@ -76,20 +76,20 @@ The plugin can also be told which URLs to load import maps from directly using t
76
76
 
77
77
  ```js
78
78
  export default {
79
- //...
80
- module: {
81
- rules: [
82
- {
83
- test: /\.js$/,
84
- use: {
85
- loader: '@eik/webpack-plugin',
86
- options: {
87
- urls: ['http://myserver/import-map']
88
- },
89
- },
90
- },
91
- ],
92
- },
79
+ //...
80
+ module: {
81
+ rules: [
82
+ {
83
+ test: /\.js$/,
84
+ use: {
85
+ loader: "@eik/webpack-plugin",
86
+ options: {
87
+ urls: ["http://myserver/import-map"],
88
+ },
89
+ },
90
+ },
91
+ ],
92
+ },
93
93
  };
94
94
  ```
95
95
 
@@ -97,24 +97,26 @@ Additionally, individual mappings can be specified using the `maps` option.
97
97
 
98
98
  ```js
99
99
  export default {
100
- //...
101
- module: {
102
- rules: [
103
- {
104
- test: /\.js$/,
105
- use: {
106
- loader: '@eik/webpack-plugin',
107
- options: {
108
- maps: [{
109
- imports: {
110
- "lit-element": "https://cdn.eik.dev/lit-element/v2",
111
- }
112
- }],
113
- },
114
- },
115
- },
116
- ],
117
- },
100
+ //...
101
+ module: {
102
+ rules: [
103
+ {
104
+ test: /\.js$/,
105
+ use: {
106
+ loader: "@eik/webpack-plugin",
107
+ options: {
108
+ maps: [
109
+ {
110
+ imports: {
111
+ "lit-element": "https://cdn.eik.dev/lit-element/v2",
112
+ },
113
+ },
114
+ ],
115
+ },
116
+ },
117
+ },
118
+ ],
119
+ },
118
120
  };
119
121
  ```
120
122
 
@@ -124,26 +126,28 @@ ie. in the following example
124
126
 
125
127
  ```js
126
128
  export default {
127
- //...
128
- module: {
129
- rules: [
130
- {
131
- test: /\.js$/,
132
- use: {
133
- loader: '@eik/webpack-plugin',
134
- options: {
135
- path: '/path/to/eik-json-folder',
136
- urls: ['http://myserver/import-map'],
137
- maps: [{
138
- imports: {
139
- "lit-element": "https://cdn.eik.dev/lit-element/v2",
140
- }
141
- }],
142
- },
143
- },
144
- },
145
- ],
146
- },
129
+ //...
130
+ module: {
131
+ rules: [
132
+ {
133
+ test: /\.js$/,
134
+ use: {
135
+ loader: "@eik/webpack-plugin",
136
+ options: {
137
+ path: "/path/to/eik-json-folder",
138
+ urls: ["http://myserver/import-map"],
139
+ maps: [
140
+ {
141
+ imports: {
142
+ "lit-element": "https://cdn.eik.dev/lit-element/v2",
143
+ },
144
+ },
145
+ ],
146
+ },
147
+ },
148
+ },
149
+ ],
150
+ },
147
151
  };
148
152
  ```
149
153
 
@@ -151,7 +155,7 @@ Any import map URLs in `eik.json` will be loaded first, then merged with (and ov
151
155
 
152
156
  ### Plugin result
153
157
 
154
- Bundles will have bare imports mapped to absolute URLs.
158
+ Bundles will have bare imports mapped to absolute URLs.
155
159
 
156
160
  Ie. Something like this...
157
161
 
@@ -169,29 +173,29 @@ import { LitElement, html, css } from "https://cdn.eik.dev/lit-element/v2";
169
173
 
170
174
  This plugin takes the following options:
171
175
 
172
- | option | default | type | required | details |
173
- | ------- | -------------- | -------- | -------- | ----------------------------------------------------------- |
174
- | path | `cwd/eik.json` | `string` | `false` | Path to eik.json file. |
175
- | urls | `[]` | `array` | `false` | Array of import map URLs to fetch from. |
176
- | maps | `[]` | `array` | `false` | Array of import map as objects. |
176
+ | option | default | type | required | details |
177
+ | ------ | -------------- | -------- | -------- | --------------------------------------- |
178
+ | path | `cwd/eik.json` | `string` | `false` | Path to eik.json file. |
179
+ | urls | `[]` | `array` | `false` | Array of import map URLs to fetch from. |
180
+ | maps | `[]` | `array` | `false` | Array of import map as objects. |
177
181
 
178
182
  ## Note on ESM with WebPack
179
183
 
180
- This plugin will only apply import maps to ESM modules. Due to this its more or less given that the source of your build must be ESM and that your build output is ESM. WebPack does __not__ by default output ESM so this needs to be configured.
184
+ This plugin will only apply import maps to ESM modules. Due to this its more or less given that the source of your build must be ESM and that your build output is ESM. WebPack does **not** by default output ESM so this needs to be configured.
181
185
 
182
186
  You enable ESM output in WebPack as follows ([reference](https://webpack.js.org/configuration/output/#outputmodule)):
183
187
 
184
188
  ```js
185
189
  export default {
186
- //...
187
- experiments: {
188
- outputModule: true,
189
- },
190
- output: {
191
- environment: {
192
- module: true,
193
- },
194
- },
190
+ //...
191
+ experiments: {
192
+ outputModule: true,
193
+ },
194
+ output: {
195
+ environment: {
196
+ module: true,
197
+ },
198
+ },
195
199
  };
196
200
  ```
197
201
 
package/package.json CHANGED
@@ -1,16 +1,22 @@
1
1
  {
2
2
  "name": "@eik/webpack-plugin",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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
- "main": "./src/loader.cjs",
6
+ "main": "./src/loader.js",
7
+ "types": "./types/loader.d.ts",
7
8
  "files": [
8
- "src"
9
+ "src",
10
+ "types"
9
11
  ],
10
12
  "scripts": {
11
13
  "test": "tap --disable-coverage --allow-empty-coverage",
12
14
  "test:snapshot": "TAP_SNAPSHOT=1 tap --allow-empty-coverage",
13
- "lint": "eslint ."
15
+ "lint": "eslint .",
16
+ "lint:fix": "npm run lint -- --fix",
17
+ "types": "run-s types:module types:test",
18
+ "types:module": "tsc",
19
+ "types:test": "tsc --project tsconfig.test.json"
14
20
  },
15
21
  "repository": {
16
22
  "type": "git",
@@ -31,18 +37,20 @@
31
37
  },
32
38
  "homepage": "https://github.com/eik-lib/webpack-plugin#readme",
33
39
  "devDependencies": {
34
- "@semantic-release/changelog": "6.0.3",
35
- "@semantic-release/git": "10.0.1",
36
- "webpack": "5.91.0",
37
- "webpack-cli": "5.1.4",
40
+ "@eik/eslint-config": "1.0.2",
41
+ "@eik/prettier-config": "1.0.1",
42
+ "@eik/semantic-release-config": "1.0.0",
43
+ "@eik/typescript-config": "1.0.0",
38
44
  "eslint": "9.6.0",
39
- "eslint-config-prettier": "9.1.0",
40
- "eslint-plugin-prettier": "5.1.3",
41
45
  "fastify": "4.28.1",
42
- "globals": "15.8.0",
46
+ "memfs": "4.11.1",
47
+ "npm-run-all": "4.1.5",
48
+ "prettier": "3.3.3",
43
49
  "semantic-release": "24.0.0",
44
50
  "tap": "21.0.0",
45
- "memfs": "4.9.3"
51
+ "typescript": "5.5.4",
52
+ "webpack": "5.93.0",
53
+ "webpack-cli": "5.1.4"
46
54
  },
47
55
  "dependencies": {
48
56
  "@eik/common": "3.0.1",
package/src/builders.js CHANGED
@@ -4,41 +4,59 @@ const RX_SIDE_EFFECTS_IMPORT = parsers.sideEffectsImport();
4
4
  const RX_STANDARD_IMPORT = parsers.standardImport();
5
5
  const RX_DYNAMIC_IMPORT = parsers.dynamicImport();
6
6
 
7
+ /**
8
+ * @typedef {object} BuilderParams
9
+ * @property {Map<string, string>} [dictionary]
10
+ * @property {string} [source]
11
+ */
12
+
13
+ /**
14
+ * @param {BuilderParams} params
15
+ * @returns {BuilderParams}
16
+ */
7
17
  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
- };
18
+ const result = source.replace(
19
+ RX_STANDARD_IMPORT,
20
+ (replacer, g1, g2, g3, g4) => {
21
+ const dep = dictionary.get(g4) || g4;
22
+ return `${g1} ${g2} ${g3} '${dep}'`;
23
+ },
24
+ );
25
+
26
+ return {
27
+ source: result,
28
+ dictionary,
29
+ };
20
30
  };
21
31
 
32
+ /**
33
+ * @param {BuilderParams} params
34
+ * @returns {BuilderParams}
35
+ */
22
36
  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
- };
37
+ const result = source.replace(RX_DYNAMIC_IMPORT, (replacer, g1, g2) => {
38
+ const dep = dictionary.get(g2) || g2;
39
+ return `${g1}('${dep}')`;
40
+ });
41
+
42
+ return {
43
+ source: result,
44
+ dictionary,
45
+ };
32
46
  };
33
47
 
48
+ /**
49
+ * @param {BuilderParams} params
50
+ * @returns {BuilderParams}
51
+ */
34
52
  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
- };
53
+ const result = source.replace(RX_SIDE_EFFECTS_IMPORT, (replacer, g1, g2) => {
54
+ const dep = dictionary.get(g2) || g2;
55
+ return `${g1} '${dep}'`;
56
+ });
57
+
58
+ return {
59
+ source: result,
60
+ dictionary,
61
+ };
44
62
  };
package/src/loader.js CHANGED
@@ -1,67 +1,73 @@
1
1
  import { helpers } from "@eik/common";
2
2
  import * as utils from "./utils.js";
3
3
  import {
4
- standardImport,
5
- dynamicImport,
6
- sideEffectsImport,
4
+ standardImport,
5
+ dynamicImport,
6
+ sideEffectsImport,
7
7
  } from "./builders.js";
8
8
 
9
9
  const dictionary = new Map();
10
10
  let cold = true;
11
11
 
12
+ /**
13
+ * @param {string} source
14
+ */
12
15
  export default async function loader(source) {
13
- const options = this.getOptions();
14
- const callback = this.async();
16
+ const options = this.getOptions();
17
+ const callback = this.async();
15
18
 
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];
19
+ if (cold) {
20
+ /** @type {string} */
21
+ const pPath = options.path ? options.path : process.cwd();
22
+ /** @type {import("./utils.js").ImportMap[]} */
23
+ const pMaps = Array.isArray(options.maps) ? options.maps : [options.maps];
24
+ /** @type {string[]} */
25
+ const pUrls = Array.isArray(options.urls) ? options.urls : [options.urls];
20
26
 
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
- });
27
+ // Filter out any empty (undefined, null) values in the option arrays
28
+ const urls = pUrls.filter((item) => {
29
+ if (item) return true;
30
+ return false;
31
+ });
26
32
 
27
- const maps = pMaps.filter((item) => {
28
- if (item) return true;
29
- return false;
30
- });
33
+ const maps = pMaps.filter((item) => {
34
+ if (item) return true;
35
+ return false;
36
+ });
31
37
 
32
- // Load eik config from eik.json or package.json
33
- const eikConfig = await helpers.getDefaults(pPath);
38
+ // Load eik config from eik.json or package.json
39
+ const eikConfig = await helpers.getDefaults(pPath);
34
40
 
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
- ]);
41
+ // Merge map from eik config and the plugin options and Fetch all import maps over http
42
+ const fetchedMaps = await utils.fetchImportMaps([
43
+ ...eikConfig.map,
44
+ ...urls,
45
+ ]);
40
46
 
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
- });
47
+ // Validate each import map and push each import statement into a dictionary
48
+ maps
49
+ .concat(fetchedMaps)
50
+ .map((item) => utils.validate(item))
51
+ .forEach((item) => {
52
+ item.forEach((obj) => {
53
+ dictionary.set(obj.key, obj.value);
54
+ });
55
+ });
50
56
 
51
- // Loading of config and import maps should only happen once
52
- cold = false;
53
- }
57
+ // Loading of config and import maps should only happen once
58
+ cold = false;
59
+ }
54
60
 
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
- });
61
+ new Promise((resolve) => {
62
+ resolve({
63
+ dictionary,
64
+ source,
65
+ });
66
+ })
67
+ .then(standardImport)
68
+ .then(dynamicImport)
69
+ .then(sideEffectsImport)
70
+ .then((obj) => {
71
+ callback(null, obj.source);
72
+ });
67
73
  }
package/src/parsers.js CHANGED
@@ -1,11 +1,11 @@
1
1
  export const standardImport = (flags = "sgm") =>
2
- new RegExp(
3
- "(import)\\s*([\\w{},\\n\\s\\[\\]\\*\\.]+?)\\s*(from)\\s*['\"]([\\w@\\-\\./]+?)['\"]",
4
- flags,
5
- );
2
+ new RegExp(
3
+ "(import)\\s*([\\w{},\\n\\s\\[\\]\\*\\.]+?)\\s*(from)\\s*['\"]([\\w@\\-\\./]+?)['\"]",
4
+ flags,
5
+ );
6
6
 
7
7
  export const dynamicImport = (flags = "gm") =>
8
- new RegExp("(import)[(]['\"](.+?)['\"][)]", flags);
8
+ new RegExp("(import)[(]['\"](.+?)['\"][)]", flags);
9
9
 
10
10
  export const sideEffectsImport = (flags = "gm") =>
11
- new RegExp("(import)\\s*['\"](.+?)['\"]", flags);
11
+ new RegExp("(import)\\s*['\"](.+?)['\"]", flags);
package/src/utils.js CHANGED
@@ -6,16 +6,16 @@ import { request } from "undici";
6
6
  * @returns {boolean}
7
7
  */
8
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;
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
19
  };
20
20
 
21
21
  /**
@@ -26,46 +26,50 @@ export const isBare = (str) => {
26
26
  export const isString = (str) => typeof str === "string";
27
27
 
28
28
  /**
29
- * @param {any} map
30
- * @returns {Array<{ key: string; value: string }>}
29
+ * @param {ImportMap} map
30
+ * @returns {Array<{ key: string; value: string; }>}
31
31
  */
32
32
  export const validate = (map) =>
33
- Object.keys(map.imports).map((key) => {
34
- const value = map.imports[key];
33
+ Object.keys(map.imports).map((key) => {
34
+ const value = map.imports[key];
35
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
- }
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
41
 
42
- return { key, value };
43
- });
42
+ return { key, value };
43
+ });
44
44
 
45
45
  /**
46
- *
47
- * @param {string[]} [urls=[]]
48
- * @returns
46
+ * @typedef {object} ImportMap
47
+ * @property {Record<string, string>} imports
48
+ */
49
+
50
+ /**
51
+ * @param {string[]} urls
52
+ * @returns {Promise<ImportMap[]>}
49
53
  */
50
54
  export const fetchImportMaps = async (urls = []) => {
51
- try {
52
- const maps = urls.map(async (map) => {
53
- const { statusCode, body } = await request(map, { maxRedirections: 2 });
55
+ try {
56
+ const maps = urls.map(async (map) => {
57
+ const { statusCode, body } = await request(map, { maxRedirections: 2 });
54
58
 
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
- }
59
+ if (statusCode === 404) {
60
+ throw new Error("Import map could not be found on server");
61
+ } else if (statusCode >= 400 && statusCode < 500) {
62
+ throw new Error("Server rejected client request");
63
+ } else if (statusCode >= 500) {
64
+ throw new Error("Server error");
65
+ }
62
66
 
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
- }
67
+ return /** @type {Promise<ImportMap>} */ (body.json());
68
+ });
69
+ return await Promise.all(maps);
70
+ } catch (err) {
71
+ throw new Error(
72
+ `Unable to load import map file from server: ${err.message}`,
73
+ );
74
+ }
71
75
  };
@@ -0,0 +1,7 @@
1
+ export function standardImport({ dictionary, source }: BuilderParams): BuilderParams;
2
+ export function dynamicImport({ dictionary, source }: BuilderParams): BuilderParams;
3
+ export function sideEffectsImport({ dictionary, source }: BuilderParams): BuilderParams;
4
+ export type BuilderParams = {
5
+ dictionary?: Map<string, string>;
6
+ source?: string;
7
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @param {string} source
3
+ */
4
+ export default function loader(source: string): Promise<void>;
@@ -0,0 +1,3 @@
1
+ export function standardImport(flags?: string): RegExp;
2
+ export function dynamicImport(flags?: string): RegExp;
3
+ export function sideEffectsImport(flags?: string): RegExp;
@@ -0,0 +1,10 @@
1
+ export function isBare(str: string): boolean;
2
+ export function isString(str: any): boolean;
3
+ export function validate(map: ImportMap): Array<{
4
+ key: string;
5
+ value: string;
6
+ }>;
7
+ export function fetchImportMaps(urls?: string[]): Promise<ImportMap[]>;
8
+ export type ImportMap = {
9
+ imports: Record<string, string>;
10
+ };