@esmx/core 3.0.0-rc.60 → 3.0.0-rc.63
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 -4
- package/README.zh-CN.md +4 -4
- package/dist/app.d.ts +27 -27
- package/dist/core.d.ts +274 -272
- package/dist/core.mjs +235 -232
- package/dist/pack-config.d.ts +92 -92
- package/dist/render-context.d.ts +465 -465
- package/dist/render-context.mjs +338 -338
- package/dist/utils/cache.d.ts +15 -15
- package/dist/utils/import-map.d.ts +31 -1
- package/dist/utils/import-map.mjs +18 -0
- package/dist/utils/import-map.test.mjs +577 -1
- package/dist/utils/middleware.d.ts +19 -19
- package/dist/utils/static-import-lexer.d.ts +12 -12
- package/dist/utils/static-import-lexer.mjs +1 -1
- package/package.json +3 -3
- package/src/app.ts +34 -34
- package/src/core.ts +320 -317
- package/src/pack-config.ts +92 -92
- package/src/render-context.ts +465 -465
- package/src/utils/cache.ts +15 -15
- package/src/utils/import-map.test.ts +713 -1
- package/src/utils/import-map.ts +53 -1
- package/src/utils/middleware.ts +19 -19
- package/src/utils/static-import-lexer.ts +18 -18
package/src/utils/import-map.ts
CHANGED
|
@@ -80,12 +80,64 @@ export function buildScopesMap(
|
|
|
80
80
|
|
|
81
81
|
return scopes;
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Fixes Chrome's nested scope resolution bug in import maps.
|
|
85
|
+
*
|
|
86
|
+
* Chrome has a bug where nested scopes in import maps are not resolved correctly.
|
|
87
|
+
* For example, when you have both "/shared-modules/" and "/shared-modules/vue2/" scopes,
|
|
88
|
+
* Chrome fails to properly apply the more specific nested scope.
|
|
89
|
+
*
|
|
90
|
+
* This function works around the bug by:
|
|
91
|
+
* 1. Sorting scopes by path depth (shallow paths first, deeper paths last)
|
|
92
|
+
* 2. Manually applying scopes to matching imports in the correct order
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* Problematic import map that fails in Chrome:
|
|
96
|
+
* ```json
|
|
97
|
+
* {
|
|
98
|
+
* "scopes": {
|
|
99
|
+
* "/shared-modules/": {
|
|
100
|
+
* "vue": "/shared-modules/vue.d8c7a640.final.mjs"
|
|
101
|
+
* },
|
|
102
|
+
* "/shared-modules/vue2/": {
|
|
103
|
+
* "vue": "/shared-modules/vue2.9b4efaf3.final.mjs"
|
|
104
|
+
* }
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @see https://github.com/guybedford/es-module-shims/issues/529
|
|
110
|
+
* @see https://issues.chromium.org/issues/453147451
|
|
111
|
+
*/
|
|
112
|
+
export function fixNestedScopesResolution(
|
|
113
|
+
importMap: Required<ImportMap>
|
|
114
|
+
): Required<ImportMap> {
|
|
115
|
+
Object.entries(importMap.scopes)
|
|
116
|
+
.sort(([pathA], [pathB]) => {
|
|
117
|
+
const depthA = pathA.split('/').length;
|
|
118
|
+
const depthB = pathB.split('/').length;
|
|
119
|
+
return depthA - depthB;
|
|
120
|
+
})
|
|
121
|
+
.forEach(([scopePath, scopeMappings]) => {
|
|
122
|
+
Object.values(importMap.imports).forEach((importPath) => {
|
|
123
|
+
if (importPath.startsWith(scopePath)) {
|
|
124
|
+
importMap.scopes[importPath] = {
|
|
125
|
+
...importMap.scopes[importPath],
|
|
126
|
+
...scopeMappings
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
Reflect.deleteProperty(importMap.scopes, scopePath);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return importMap;
|
|
134
|
+
}
|
|
83
135
|
|
|
84
136
|
export function getImportMap({
|
|
85
137
|
manifests,
|
|
86
138
|
getFile,
|
|
87
139
|
getScope
|
|
88
|
-
}: GetImportMapOptions): ImportMap {
|
|
140
|
+
}: GetImportMapOptions): Required<ImportMap> {
|
|
89
141
|
const imports = buildImportsMap(manifests, getFile);
|
|
90
142
|
|
|
91
143
|
const scopes = buildScopesMap(imports, manifests, getScope);
|
package/src/utils/middleware.ts
CHANGED
|
@@ -4,18 +4,18 @@ import send from 'send';
|
|
|
4
4
|
import type { Esmx } from '../core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Middleware function type definition
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
10
|
+
* Middleware is a function used to handle HTTP requests. It receives the request object, response object, and the next middleware function as parameters.
|
|
11
|
+
* Middleware can perform the following operations:
|
|
12
|
+
* - Modify request and response objects
|
|
13
|
+
* - End the request-response cycle
|
|
14
|
+
* - Call the next middleware
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* ```ts
|
|
18
|
-
* //
|
|
18
|
+
* // Create a simple logging middleware
|
|
19
19
|
* const loggerMiddleware: Middleware = (req, res, next) => {
|
|
20
20
|
* console.log(`${req.method} ${req.url}`);
|
|
21
21
|
* next();
|
|
@@ -30,24 +30,24 @@ export type Middleware = (
|
|
|
30
30
|
|
|
31
31
|
const reFinal = /\.final\.[a-zA-Z0-9]+$/;
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @param path
|
|
33
|
+
* Determine if a file path is an immutable file that complies with esmx specifications
|
|
34
|
+
* @param path File path
|
|
35
35
|
*/
|
|
36
36
|
export function isImmutableFile(filename: string) {
|
|
37
37
|
return reFinal.test(filename);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* Create middleware for Esmx application
|
|
42
42
|
*
|
|
43
|
-
* @param esmx - Esmx
|
|
44
|
-
* @returns
|
|
43
|
+
* @param esmx - Esmx instance
|
|
44
|
+
* @returns Returns a middleware that handles static resources
|
|
45
45
|
*
|
|
46
46
|
* @description
|
|
47
|
-
*
|
|
48
|
-
* -
|
|
49
|
-
* -
|
|
50
|
-
* -
|
|
47
|
+
* This function creates a middleware to handle static resource requests for modules. It will:
|
|
48
|
+
* - Create corresponding static resource middleware based on module configuration
|
|
49
|
+
* - Handle cache control for resources
|
|
50
|
+
* - Support long-term caching for immutable files
|
|
51
51
|
*
|
|
52
52
|
* @example
|
|
53
53
|
* ```ts
|
|
@@ -56,7 +56,7 @@ export function isImmutableFile(filename: string) {
|
|
|
56
56
|
* const esmx = new Esmx();
|
|
57
57
|
* const middleware = createMiddleware(esmx);
|
|
58
58
|
*
|
|
59
|
-
* //
|
|
59
|
+
* // Use in HTTP server
|
|
60
60
|
* server.use(middleware);
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
@@ -96,8 +96,8 @@ export function createMiddleware(esmx: Esmx): Middleware {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
100
|
-
* @param middlewares
|
|
99
|
+
* Merge multiple middlewares into one middleware execution
|
|
100
|
+
* @param middlewares List of middlewares
|
|
101
101
|
* @returns
|
|
102
102
|
*/
|
|
103
103
|
export function mergeMiddlewares(middlewares: Middleware[]): Middleware {
|
|
@@ -7,23 +7,23 @@ import type { ParsedModuleConfig } from '../module-config';
|
|
|
7
7
|
import * as esmLexer from 'es-module-lexer';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param code
|
|
12
|
-
* @returns `Promise<string[]>`
|
|
10
|
+
* Get the list of statically imported module names from JS code. Maybe cannot handle multiple concurrent calls, not tested.
|
|
11
|
+
* @param code JS code
|
|
12
|
+
* @returns `Promise<string[]>` List of statically imported module names
|
|
13
13
|
*/
|
|
14
14
|
export async function getImportsFromJsCode(code: string): Promise<string[]> {
|
|
15
15
|
await esmLexer.init;
|
|
16
16
|
const [imports] = esmLexer.parse(code);
|
|
17
|
-
//
|
|
17
|
+
// Static import && has module name
|
|
18
18
|
return imports
|
|
19
19
|
.filter((item) => item.t === 1 && item.n)
|
|
20
20
|
.map((item) => item.n as string);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param filepath
|
|
26
|
-
* @returns `Promise<string[]>`
|
|
24
|
+
* Get the list of statically imported module names from a JS file.
|
|
25
|
+
* @param filepath JS file path
|
|
26
|
+
* @returns `Promise<string[]>` List of statically imported module names
|
|
27
27
|
*/
|
|
28
28
|
export async function getImportsFromJsFile(
|
|
29
29
|
filepath: fs.PathLike | fs.promises.FileHandle
|
|
@@ -34,13 +34,13 @@ export async function getImportsFromJsFile(
|
|
|
34
34
|
|
|
35
35
|
export type ImportPreloadInfo = SpecifierMap;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
* @param specifier
|
|
39
|
-
* @param importMap
|
|
40
|
-
* @param moduleConfig
|
|
37
|
+
* Get import preload information.
|
|
38
|
+
* @param specifier Module name
|
|
39
|
+
* @param importMap Import map object
|
|
40
|
+
* @param moduleConfig Module configuration
|
|
41
41
|
* @returns
|
|
42
|
-
* - `Promise<{ [specifier: string]: ImportPreloadPathString }>`
|
|
43
|
-
* - `null` specifier
|
|
42
|
+
* - `Promise<{ [specifier: string]: ImportPreloadPathString }>` Mapping object of module names to file paths
|
|
43
|
+
* - `null` specifier does not exist
|
|
44
44
|
*/
|
|
45
45
|
export async function getImportPreloadInfo(
|
|
46
46
|
specifier: string,
|
|
@@ -53,18 +53,18 @@ export async function getImportPreloadInfo(
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const ans: ImportPreloadInfo = {
|
|
56
|
-
//
|
|
56
|
+
// Entry file is also added to the preload list
|
|
57
57
|
[specifier]: importInfo[specifier]
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// Lexical analysis is a time-consuming operation, so the fewer files processed, the faster, in other words, the shallower the depth, the faster, so breadth-first search is used here
|
|
61
61
|
const needHandles: string[] = [specifier];
|
|
62
62
|
while (needHandles.length) {
|
|
63
63
|
const specifier = needHandles.shift()!;
|
|
64
64
|
let filepath = importInfo[specifier];
|
|
65
65
|
const splitRes = filepath.split('/');
|
|
66
66
|
if (splitRes[0] === '') splitRes.shift();
|
|
67
|
-
//
|
|
67
|
+
// Here it is assumed that the first directory in the path is the soft package name
|
|
68
68
|
const name = splitRes.shift() + '';
|
|
69
69
|
const link = moduleConfig.links[name];
|
|
70
70
|
if (!link) {
|
|
@@ -73,13 +73,13 @@ export async function getImportPreloadInfo(
|
|
|
73
73
|
filepath = path.join(link.client, ...splitRes);
|
|
74
74
|
const imports = await getImportsFromJsFile(filepath);
|
|
75
75
|
for (const specifier of imports) {
|
|
76
|
-
//
|
|
76
|
+
// If the module name does not exist in importMap or has been processed
|
|
77
77
|
if (!(specifier in importInfo) || specifier in ans) continue;
|
|
78
78
|
ans[specifier] = importInfo[specifier];
|
|
79
79
|
needHandles.push(specifier);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// Reverse order, theoretically browser parsing may be faster after reversing
|
|
84
84
|
return Object.fromEntries(Object.entries(ans).reverse());
|
|
85
85
|
}
|