@package-verse/esmpack 1.0.11 → 1.0.13
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 +4 -7
- package/package.json +1 -1
- package/src/pack/FilePacker.ts +9 -4
- package/src/pack/JSModule.ts +27 -0
- package/src/pack/packageMap.ts +64 -0
- package/src/serve/send/packageInfo.ts +5 -1
package/README.md
CHANGED
|
@@ -2,22 +2,19 @@
|
|
|
2
2
|
ESM Pack Packer and Web Server with PostCSS and ESM Loader
|
|
3
3
|
|
|
4
4
|
# Why?
|
|
5
|
-
Because, there is no simple packer that just
|
|
5
|
+
Because, there is no simple packer that just generates module paths. After ES6 and HTTP2, there is no need to bundle all JavaScripts into a single file. Parsing and loading entire bundle is a single threaded operation, which blocks UI rendering and also consumes very high memory.
|
|
6
6
|
|
|
7
7
|
# So what does this do?
|
|
8
8
|
1. Creates a single pack JS file which only has import definition of all the imports.
|
|
9
|
-
2.
|
|
10
|
-
3. Retains ESM source code as it is except import path, import path is rewritten to fully qualified CDN url. So caching is preserved over different main module versions but same dependencies.
|
|
9
|
+
2. Import map contains inline JavaScript module via data url to inject CSS link into document.
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
## Dev Packer
|
|
14
13
|
|
|
15
|
-
1. Development time packer will generate HTML file along with the
|
|
14
|
+
1. Development time packer will generate HTML file along with the import map and inline script to host the module.
|
|
16
15
|
2. Dev Packer will generate `let cs = document.currentScript;import("imported-path").then((r) => ESMPack.render(r, cs))` script inside html for every JS's corresponding html.
|
|
17
16
|
3. Library author must implement `ESMPack.render` method which will accept exports from imported method and `currentScript`.
|
|
18
|
-
4. Every `js` file's imports will be changed to fully qualified references for external imports.
|
|
19
17
|
|
|
20
18
|
## Release Packer
|
|
21
19
|
|
|
22
|
-
1.
|
|
23
|
-
2. Every `js` file's imports will be changed to fully qualified references for external imports.
|
|
20
|
+
1. `pack.js` will generate a single JS file that will inject import map into document and it will call `ESMPack.render` method with import.
|
package/package.json
CHANGED
package/src/pack/FilePacker.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { Babel } from "../parser/babel.js";
|
|
3
|
+
import packageMap, { IPackageMap } from "./packageMap.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* File Packer must do following tasks...
|
|
@@ -20,15 +21,13 @@ import { Babel } from "../parser/babel.js";
|
|
|
20
21
|
* Imports all nested dependencies of App.js that should not contain fully qualified path
|
|
21
22
|
* Import dynamically loaded modules as well
|
|
22
23
|
* Imports App.js dynamically so CSS can be ready before hosting the User interface
|
|
23
|
-
* 2. App.pack.global.css
|
|
24
|
-
* 3. App.pack.local.css
|
|
25
|
-
* 4. App.pack.{hash-of-absolute-module-path}.js <-- this will be a dependency for non js module such as image or json etc.
|
|
26
|
-
* This will load an absolute path via resolve
|
|
27
24
|
*/
|
|
28
25
|
export default class FilePacker {
|
|
29
26
|
|
|
30
27
|
readonly absoluteSrc: string;
|
|
31
28
|
|
|
29
|
+
modules: IPackageMap;
|
|
30
|
+
|
|
32
31
|
readonly cssImports = [];
|
|
33
32
|
|
|
34
33
|
readonly jsonImports = [];
|
|
@@ -50,6 +49,9 @@ export default class FilePacker {
|
|
|
50
49
|
|
|
51
50
|
async pack() {
|
|
52
51
|
|
|
52
|
+
// resolve package.json
|
|
53
|
+
this.modules = await packageMap(this.root);
|
|
54
|
+
|
|
53
55
|
const resolve = (url, sourceFile) => this.resolve(url, sourceFile);
|
|
54
56
|
|
|
55
57
|
// we don't need the code
|
|
@@ -82,6 +84,9 @@ export default class FilePacker {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
moduleUrl(url: string, sourceFile: string) {
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
85
90
|
return url;
|
|
86
91
|
}
|
|
87
92
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { IPackageInfo, IPackageMap } from "./packageMap.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This class holds a path of single module along
|
|
5
|
+
* with it's own package info
|
|
6
|
+
*/
|
|
7
|
+
export default class JSModule {
|
|
8
|
+
|
|
9
|
+
public imports: IPackageMap;
|
|
10
|
+
|
|
11
|
+
public owner: IPackageInfo;
|
|
12
|
+
public main: IPackageInfo;
|
|
13
|
+
|
|
14
|
+
public mainFolder: string;
|
|
15
|
+
public ownerFolder: string;
|
|
16
|
+
|
|
17
|
+
public src: string;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
p: Partial<JSModule>
|
|
21
|
+
) {
|
|
22
|
+
// do nothing
|
|
23
|
+
Object.setPrototypeOf(p, JSModule.prototype);
|
|
24
|
+
return p as any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
const resolvedImports = new Map<string, Promise<IPackageMap>>();
|
|
5
|
+
|
|
6
|
+
export interface IPackageInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
main: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IPackageMap {
|
|
13
|
+
[key: string]: IPackageInfo
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function populatePackageInfo(root, imports: IPackageMap, packageJsonPath) {
|
|
17
|
+
const {
|
|
18
|
+
name,
|
|
19
|
+
version,
|
|
20
|
+
dependencies,
|
|
21
|
+
type,
|
|
22
|
+
module: moduleMain,
|
|
23
|
+
main
|
|
24
|
+
} = JSON.parse(await readFile(packageJsonPath, "utf-8"));
|
|
25
|
+
|
|
26
|
+
if (imports[name]) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Dependency must specify module or main, we will not assume it.
|
|
32
|
+
* We want to keep import map small.
|
|
33
|
+
*
|
|
34
|
+
* Otherwise, caller must explicitly specify the fully qualified module path to load
|
|
35
|
+
*/
|
|
36
|
+
imports[name] = {
|
|
37
|
+
name,
|
|
38
|
+
version,
|
|
39
|
+
main: moduleMain
|
|
40
|
+
|| (type === "module" ? main : void 0)
|
|
41
|
+
|| void 0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for(const key of Object.keys(dependencies)) {
|
|
45
|
+
if (imports[key]) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
await populatePackageInfo(root, imports, join(root, "node_modules", key, "package.json"));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default function packageMap(root: string) {
|
|
53
|
+
let value = resolvedImports.get(root);
|
|
54
|
+
if(value) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
value = (async ()=> {
|
|
58
|
+
const moduleImports = {} as IPackageMap;
|
|
59
|
+
await populatePackageInfo(root, moduleImports, join(root, "package.json"));
|
|
60
|
+
return moduleImports;
|
|
61
|
+
})();
|
|
62
|
+
resolvedImports.set(root, value);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
@@ -17,14 +17,18 @@ for (const [key] of Object.entries(packageInfo.dependencies)) {
|
|
|
17
17
|
continue;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
let modulePath = "/node_modules/" + key + "/";
|
|
21
21
|
|
|
22
22
|
// read package.json
|
|
23
23
|
const modulePackageJson = JSON.parse(readFileSync(modulePackageJsonFilePath, "utf-8"));
|
|
24
|
+
|
|
24
25
|
imports[key] = path.join(modulePath , modulePackageJson.module
|
|
25
26
|
|| (modulePackageJson.type === "module"
|
|
26
27
|
? modulePackageJson.main : "index.js"));
|
|
27
28
|
|
|
29
|
+
if (modulePackageJson.esm) {
|
|
30
|
+
modulePath += modulePackageJson.esm + "/";
|
|
31
|
+
}
|
|
28
32
|
imports[key + "/"] = modulePath;
|
|
29
33
|
|
|
30
34
|
}
|