@esmx/import 3.0.0-rc.10
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/dist/import-loader.d.ts +12 -0
- package/dist/import-loader.mjs +37 -0
- package/dist/import-vm.d.ts +6 -0
- package/dist/import-vm.mjs +121 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +2 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.mjs +0 -0
- package/package.json +46 -0
- package/src/import-loader.ts +56 -0
- package/src/import-vm.ts +146 -0
- package/src/index.ts +3 -0
- package/src/types.ts +8 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ImportMap } from './types';
|
|
2
|
+
interface Data {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
importMap: ImportMap;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 创建一个使用 loader 实现的 importmap 的 import 函数,只能创建一次,无热更新,适合生产使用。
|
|
8
|
+
*/
|
|
9
|
+
export declare function createLoaderImport(baseURL: URL, importMap?: ImportMap): (specifier: string) => Promise<Record<string, any>>;
|
|
10
|
+
export declare function initialize(data: Data): void;
|
|
11
|
+
export declare function resolve(specifier: string, context: Record<string, any>, nextResolve: Function): any;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import module from "node:module";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import IM from "@import-maps/resolve";
|
|
4
|
+
let registered = "";
|
|
5
|
+
export function createLoaderImport(baseURL, importMap = {}) {
|
|
6
|
+
if (!registered) {
|
|
7
|
+
module.register(fileURLToPath(import.meta.url), {
|
|
8
|
+
parentURL: baseURL,
|
|
9
|
+
data: {
|
|
10
|
+
baseURL: baseURL.href,
|
|
11
|
+
importMap
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
registered = JSON.stringify(importMap);
|
|
15
|
+
} else if (registered !== JSON.stringify(importMap)) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`'createLoaderImport()' can only be created once and cannot be created repeatedly`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return (specifier) => {
|
|
21
|
+
return import(specifier);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
let loaderBaseURL = new URL("file:");
|
|
25
|
+
let loaderParsedImportMap = {};
|
|
26
|
+
export function initialize(data) {
|
|
27
|
+
loaderBaseURL = new URL(data.baseURL);
|
|
28
|
+
loaderParsedImportMap = IM.parse(data.importMap, loaderBaseURL);
|
|
29
|
+
}
|
|
30
|
+
export function resolve(specifier, context, nextResolve) {
|
|
31
|
+
const scriptURL = new URL(context.parentURL);
|
|
32
|
+
const result = IM.resolve(specifier, loaderParsedImportMap, scriptURL);
|
|
33
|
+
if (result.matched && result.resolvedImport) {
|
|
34
|
+
return nextResolve(result.resolvedImport.href);
|
|
35
|
+
}
|
|
36
|
+
return nextResolve(specifier, context);
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import vm from 'node:vm';
|
|
2
|
+
import type { ImportMap } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* 创建一个使用 vm 实现的 importmap 的 import 函数,可以创建多次来实现热更新效果,适合开发使用。
|
|
5
|
+
*/
|
|
6
|
+
export declare function createVmImport(baseURL: URL, importMap?: ImportMap): (specifier: string, parent: string, sandbox?: vm.Context, options?: vm.CreateContextOptions) => Promise<Record<string, any>>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { isBuiltin } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import vm from "node:vm";
|
|
5
|
+
import IM from "@import-maps/resolve";
|
|
6
|
+
async function importBuiltinModule(specifier, context) {
|
|
7
|
+
const nodeModule = await import(specifier);
|
|
8
|
+
const keys = Object.keys(nodeModule);
|
|
9
|
+
const module = new vm.SyntheticModule(
|
|
10
|
+
keys,
|
|
11
|
+
function evaluateCallback() {
|
|
12
|
+
keys.forEach((key) => {
|
|
13
|
+
this.setExport(key, nodeModule[key]);
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
identifier: specifier,
|
|
18
|
+
context
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
await module.link(() => {
|
|
22
|
+
throw new TypeError(`Native modules should not be linked`);
|
|
23
|
+
});
|
|
24
|
+
await module.evaluate();
|
|
25
|
+
return module;
|
|
26
|
+
}
|
|
27
|
+
export function createVmImport(baseURL, importMap = {}) {
|
|
28
|
+
const parsedImportMap = IM.parse(importMap, baseURL);
|
|
29
|
+
const parse = (specifier, parent) => {
|
|
30
|
+
const result = IM.resolve(specifier, parsedImportMap, new URL(parent));
|
|
31
|
+
let filename;
|
|
32
|
+
if (result.matched && result.resolvedImport) {
|
|
33
|
+
filename = result.resolvedImport.href;
|
|
34
|
+
} else {
|
|
35
|
+
filename = import.meta.resolve(specifier, parent);
|
|
36
|
+
}
|
|
37
|
+
const url = new URL(filename);
|
|
38
|
+
return {
|
|
39
|
+
filename,
|
|
40
|
+
url,
|
|
41
|
+
pathname: url.pathname
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
async function moduleLinker(specifier, parent, context, cache, moduleIds) {
|
|
45
|
+
if (isBuiltin(specifier)) {
|
|
46
|
+
return importBuiltinModule(specifier, context);
|
|
47
|
+
}
|
|
48
|
+
const parsed = parse(specifier, parent);
|
|
49
|
+
if (moduleIds.includes(parsed.pathname)) {
|
|
50
|
+
throw new RangeError(
|
|
51
|
+
`Module circular reference:
|
|
52
|
+
${JSON.stringify([...moduleIds, parsed.pathname], null, 4)}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const module = cache.get(parsed.pathname);
|
|
56
|
+
if (module) {
|
|
57
|
+
return module;
|
|
58
|
+
}
|
|
59
|
+
const pe = new Promise((resolve) => {
|
|
60
|
+
process.nextTick(() => {
|
|
61
|
+
moduleBuild().then(resolve);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const dirname = path.dirname(parsed.filename);
|
|
65
|
+
cache.set(parsed.pathname, pe);
|
|
66
|
+
return pe;
|
|
67
|
+
async function moduleBuild() {
|
|
68
|
+
let text;
|
|
69
|
+
try {
|
|
70
|
+
text = fs.readFileSync(parsed.pathname, "utf-8");
|
|
71
|
+
} catch {
|
|
72
|
+
throw new Error(`Failed to read module: ${parsed.pathname}`);
|
|
73
|
+
}
|
|
74
|
+
const module2 = new vm.SourceTextModule(text, {
|
|
75
|
+
initializeImportMeta: (meta) => {
|
|
76
|
+
meta.filename = parsed.filename;
|
|
77
|
+
meta.dirname = dirname;
|
|
78
|
+
meta.resolve = (specifier2, parent2 = parsed.url) => {
|
|
79
|
+
return import.meta.resolve(specifier2, parent2);
|
|
80
|
+
};
|
|
81
|
+
meta.url = parsed.url.toString();
|
|
82
|
+
},
|
|
83
|
+
identifier: specifier,
|
|
84
|
+
context,
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
importModuleDynamically: (specifier2, referrer) => {
|
|
87
|
+
return moduleLinker(
|
|
88
|
+
specifier2,
|
|
89
|
+
parsed.filename,
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
referrer.context,
|
|
92
|
+
cache,
|
|
93
|
+
[...moduleIds, parsed.pathname]
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
await module2.link((specifier2, referrer) => {
|
|
98
|
+
return moduleLinker(
|
|
99
|
+
specifier2,
|
|
100
|
+
parsed.filename,
|
|
101
|
+
referrer.context,
|
|
102
|
+
cache,
|
|
103
|
+
[...moduleIds, parsed.pathname]
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
await module2.evaluate();
|
|
107
|
+
return module2;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return async (specifier, parent, sandbox, options) => {
|
|
111
|
+
const context = vm.createContext(sandbox, options);
|
|
112
|
+
const module = await moduleLinker(
|
|
113
|
+
specifier,
|
|
114
|
+
parent,
|
|
115
|
+
context,
|
|
116
|
+
/* @__PURE__ */ new Map(),
|
|
117
|
+
[]
|
|
118
|
+
);
|
|
119
|
+
return module.namespace;
|
|
120
|
+
};
|
|
121
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
package/dist/types.d.ts
ADDED
package/dist/types.mjs
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@esmx/import",
|
|
3
|
+
"template": "library-node",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"lint:js": "biome check --write --no-errors-on-unmatched",
|
|
7
|
+
"lint:css": "stylelint '**/*.{css,vue}' --fix --aei",
|
|
8
|
+
"lint:type": "tsc --noEmit",
|
|
9
|
+
"test": "vitest run --pass-with-no-tests",
|
|
10
|
+
"coverage": "vitest run --coverage --pass-with-no-tests",
|
|
11
|
+
"build": "unbuild"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@import-maps/resolve": "^2.0.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@biomejs/biome": "1.9.4",
|
|
18
|
+
"@esmx/lint": "3.0.0-rc.10",
|
|
19
|
+
"@types/node": "22.13.10",
|
|
20
|
+
"@vitest/coverage-v8": "3.0.8",
|
|
21
|
+
"stylelint": "16.15.0",
|
|
22
|
+
"typescript": "5.8.2",
|
|
23
|
+
"unbuild": "2.0.0",
|
|
24
|
+
"vitest": "3.0.8"
|
|
25
|
+
},
|
|
26
|
+
"version": "3.0.0-rc.10",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"private": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"types": "./dist/index.d.ts"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"module": "dist/index.mjs",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"files": [
|
|
38
|
+
"lib",
|
|
39
|
+
"src",
|
|
40
|
+
"dist",
|
|
41
|
+
"*.mjs",
|
|
42
|
+
"template",
|
|
43
|
+
"public"
|
|
44
|
+
],
|
|
45
|
+
"gitHead": "4a528ffecfdc6f2c6e7d97bc952427745f467691"
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import module from 'node:module';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import IM from '@import-maps/resolve';
|
|
4
|
+
import type { ImportMap } from './types';
|
|
5
|
+
|
|
6
|
+
interface Data {
|
|
7
|
+
baseURL: string;
|
|
8
|
+
importMap: ImportMap;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let registered = '';
|
|
12
|
+
/**
|
|
13
|
+
* 创建一个使用 loader 实现的 importmap 的 import 函数,只能创建一次,无热更新,适合生产使用。
|
|
14
|
+
*/
|
|
15
|
+
export function createLoaderImport(baseURL: URL, importMap: ImportMap = {}) {
|
|
16
|
+
if (!registered) {
|
|
17
|
+
module.register<Data>(fileURLToPath(import.meta.url), {
|
|
18
|
+
parentURL: baseURL,
|
|
19
|
+
data: {
|
|
20
|
+
baseURL: baseURL.href,
|
|
21
|
+
importMap
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
registered = JSON.stringify(importMap);
|
|
25
|
+
} else if (registered !== JSON.stringify(importMap)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`'createLoaderImport()' can only be created once and cannot be created repeatedly`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return (specifier: string): Promise<Record<string, any>> => {
|
|
31
|
+
return import(specifier);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// loader 线程时的处理逻辑
|
|
36
|
+
let loaderBaseURL: URL = new URL('file:');
|
|
37
|
+
let loaderParsedImportMap: IM.ParsedImportMap = {};
|
|
38
|
+
|
|
39
|
+
export function initialize(data: Data) {
|
|
40
|
+
loaderBaseURL = new URL(data.baseURL);
|
|
41
|
+
loaderParsedImportMap = IM.parse(data.importMap, loaderBaseURL);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolve(
|
|
45
|
+
specifier: string,
|
|
46
|
+
context: Record<string, any>,
|
|
47
|
+
nextResolve: Function
|
|
48
|
+
) {
|
|
49
|
+
const scriptURL = new URL(context.parentURL);
|
|
50
|
+
const result = IM.resolve(specifier, loaderParsedImportMap, scriptURL);
|
|
51
|
+
|
|
52
|
+
if (result.matched && result.resolvedImport) {
|
|
53
|
+
return nextResolve(result.resolvedImport.href);
|
|
54
|
+
}
|
|
55
|
+
return nextResolve(specifier, context);
|
|
56
|
+
}
|
package/src/import-vm.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { isBuiltin } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import vm from 'node:vm';
|
|
5
|
+
import IM from '@import-maps/resolve';
|
|
6
|
+
import type { ImportMap } from './types';
|
|
7
|
+
|
|
8
|
+
async function importBuiltinModule(specifier: string, context: vm.Context) {
|
|
9
|
+
const nodeModule = await import(specifier);
|
|
10
|
+
const keys = Object.keys(nodeModule);
|
|
11
|
+
const module = new vm.SyntheticModule(
|
|
12
|
+
keys,
|
|
13
|
+
function evaluateCallback() {
|
|
14
|
+
keys.forEach((key) => {
|
|
15
|
+
this.setExport(key, nodeModule[key]);
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
identifier: specifier,
|
|
20
|
+
context: context
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
await module.link(() => {
|
|
24
|
+
throw new TypeError(`Native modules should not be linked`);
|
|
25
|
+
});
|
|
26
|
+
await module.evaluate();
|
|
27
|
+
return module;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 创建一个使用 vm 实现的 importmap 的 import 函数,可以创建多次来实现热更新效果,适合开发使用。
|
|
32
|
+
*/
|
|
33
|
+
export function createVmImport(baseURL: URL, importMap: ImportMap = {}) {
|
|
34
|
+
const parsedImportMap = IM.parse(importMap, baseURL);
|
|
35
|
+
const parse = (specifier: string, parent: string) => {
|
|
36
|
+
const result = IM.resolve(specifier, parsedImportMap, new URL(parent));
|
|
37
|
+
|
|
38
|
+
let filename: string;
|
|
39
|
+
if (result.matched && result.resolvedImport) {
|
|
40
|
+
filename = result.resolvedImport.href;
|
|
41
|
+
} else {
|
|
42
|
+
filename = import.meta.resolve(specifier, parent);
|
|
43
|
+
}
|
|
44
|
+
const url = new URL(filename);
|
|
45
|
+
return {
|
|
46
|
+
filename,
|
|
47
|
+
url,
|
|
48
|
+
pathname: url.pathname
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
async function moduleLinker(
|
|
52
|
+
specifier: string,
|
|
53
|
+
parent: string,
|
|
54
|
+
context: vm.Context,
|
|
55
|
+
cache: Map<string, Promise<vm.SourceTextModule>>,
|
|
56
|
+
moduleIds: string[]
|
|
57
|
+
) {
|
|
58
|
+
if (isBuiltin(specifier)) {
|
|
59
|
+
return importBuiltinModule(specifier, context);
|
|
60
|
+
}
|
|
61
|
+
const parsed = parse(specifier, parent);
|
|
62
|
+
|
|
63
|
+
if (moduleIds.includes(parsed.pathname)) {
|
|
64
|
+
throw new RangeError(
|
|
65
|
+
`Module circular reference: \n ${JSON.stringify([...moduleIds, parsed.pathname], null, 4)}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const module = cache.get(parsed.pathname);
|
|
70
|
+
if (module) {
|
|
71
|
+
return module;
|
|
72
|
+
}
|
|
73
|
+
const pe = new Promise<vm.SourceTextModule>((resolve) => {
|
|
74
|
+
process.nextTick(() => {
|
|
75
|
+
moduleBuild().then(resolve);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const dirname = path.dirname(parsed.filename);
|
|
80
|
+
cache.set(parsed.pathname, pe);
|
|
81
|
+
return pe;
|
|
82
|
+
|
|
83
|
+
async function moduleBuild(): Promise<vm.SourceTextModule> {
|
|
84
|
+
let text: string;
|
|
85
|
+
try {
|
|
86
|
+
text = fs.readFileSync(parsed.pathname, 'utf-8');
|
|
87
|
+
} catch {
|
|
88
|
+
throw new Error(`Failed to read module: ${parsed.pathname}`);
|
|
89
|
+
}
|
|
90
|
+
const module = new vm.SourceTextModule(text, {
|
|
91
|
+
initializeImportMeta: (meta) => {
|
|
92
|
+
meta.filename = parsed.filename;
|
|
93
|
+
meta.dirname = dirname;
|
|
94
|
+
meta.resolve = (
|
|
95
|
+
specifier: string,
|
|
96
|
+
parent: string | URL = parsed.url
|
|
97
|
+
) => {
|
|
98
|
+
return import.meta.resolve(specifier, parent);
|
|
99
|
+
};
|
|
100
|
+
meta.url = parsed.url.toString();
|
|
101
|
+
},
|
|
102
|
+
identifier: specifier,
|
|
103
|
+
context: context,
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
importModuleDynamically: (specifier, referrer) => {
|
|
106
|
+
return moduleLinker(
|
|
107
|
+
specifier,
|
|
108
|
+
parsed.filename,
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
referrer.context,
|
|
111
|
+
cache,
|
|
112
|
+
[...moduleIds, parsed.pathname]
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
await module.link((specifier: string, referrer) => {
|
|
117
|
+
return moduleLinker(
|
|
118
|
+
specifier,
|
|
119
|
+
parsed.filename,
|
|
120
|
+
referrer.context,
|
|
121
|
+
cache,
|
|
122
|
+
[...moduleIds, parsed.pathname]
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
await module.evaluate();
|
|
126
|
+
return module;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return async (
|
|
130
|
+
specifier: string,
|
|
131
|
+
parent: string,
|
|
132
|
+
sandbox?: vm.Context,
|
|
133
|
+
options?: vm.CreateContextOptions
|
|
134
|
+
) => {
|
|
135
|
+
const context = vm.createContext(sandbox, options);
|
|
136
|
+
const module = await moduleLinker(
|
|
137
|
+
specifier,
|
|
138
|
+
parent,
|
|
139
|
+
context,
|
|
140
|
+
new Map(),
|
|
141
|
+
[]
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return module.namespace as Record<string, any>;
|
|
145
|
+
};
|
|
146
|
+
}
|
package/src/index.ts
ADDED