@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.
@@ -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);
@@ -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
- * 中间件是一个函数,用于处理 HTTP 请求。它接收请求对象、响应对象和下一个中间件函数作为参数。
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
- * 判断文件路径是否是一个符合 esmx 规范的不可变文件
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
- * 创建 Esmx 应用的中间件
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
- * // HTTP 服务器中使用
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
- * JS 代码中获取静态 import 的模块名列表。也许不能并发多个调用,没实验过。
11
- * @param code js 代码
12
- * @returns `Promise<string[]>` 静态 import 的模块名列表
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
- * JS 文件中获取静态 import 的模块名列表。
25
- * @param filepath js 文件路径
26
- * @returns `Promise<string[]>` 静态 import 的模块名列表
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
- // 如果模块名在 importMap 中不存在,或已经处理过
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
  }