niuma-ui 1.2.1 → 1.2.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/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.2.2] - 2026-08-30
|
|
10
|
+
|
|
11
|
+
### 修复
|
|
12
|
+
|
|
13
|
+
- `niumaUiHost` 能解析 npm `dist/index.js` 桶(`import X_default` + `export { X_default as RsX }`)。此前只认源码 `export { default as RsX } from './...'`,宿主 `vite build` 会把具名 re-export 收成 `export type`,官网等产品打包报 `MISSING_EXPORT`。解析不到运行时导出时直接失败,不再静默改成 type-only。
|
|
14
|
+
|
|
9
15
|
## [1.2.1] - 2026-08-30
|
|
10
16
|
|
|
11
17
|
### 变更
|
|
@@ -20,3 +20,9 @@ export declare function niumaUiHost(options?: NiumaUiHostOptions): Plugin[];
|
|
|
20
20
|
* 类型-only、namespace、动态 import 原样返回 null。
|
|
21
21
|
*/
|
|
22
22
|
export declare function rewriteHostStatement(stmt: string, spec: string, map: Map<string, NiumaUiBinding>, targetOf: (from: string) => string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* ParseRuntimeBindings 从主入口抽出运行时导出名到文件的映射。
|
|
25
|
+
* 同时认源码 `export { default as RsX } from './...'` 和发布桶
|
|
26
|
+
* `import RsX_default from './...'` + `export { RsX_default as RsX }`。
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseRuntimeBindings(source: string): Map<string, NiumaUiBinding>;
|
|
@@ -109,12 +109,9 @@ export function rewriteHostStatement(stmt, spec, map, targetOf) {
|
|
|
109
109
|
if (parsed.defaultName) {
|
|
110
110
|
const binding = map.get(parsed.defaultName);
|
|
111
111
|
if (!binding) {
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
const target = targetOf(binding.from);
|
|
116
|
-
lines.push(emitBinding(keyword, binding.kind, parsed.defaultName, parsed.defaultName, target));
|
|
112
|
+
throw new Error(`niumaUiHost: 未找到运行时导出 ${parsed.defaultName},无法改写到发布子路径`);
|
|
117
113
|
}
|
|
114
|
+
lines.push(emitBinding(keyword, binding.kind, parsed.defaultName, parsed.defaultName, targetOf(binding.from)));
|
|
118
115
|
}
|
|
119
116
|
for (const part of parsed.named) {
|
|
120
117
|
if (part.isType) {
|
|
@@ -123,11 +120,9 @@ export function rewriteHostStatement(stmt, spec, map, targetOf) {
|
|
|
123
120
|
}
|
|
124
121
|
const binding = map.get(part.imported);
|
|
125
122
|
if (!binding) {
|
|
126
|
-
|
|
127
|
-
continue;
|
|
123
|
+
throw new Error(`niumaUiHost: 未找到运行时导出 ${part.imported},无法改写到发布子路径`);
|
|
128
124
|
}
|
|
129
|
-
|
|
130
|
-
lines.push(emitBinding(keyword, binding.kind, part.imported, part.local, target));
|
|
125
|
+
lines.push(emitBinding(keyword, binding.kind, part.imported, part.local, targetOf(binding.from)));
|
|
131
126
|
}
|
|
132
127
|
if (types.length > 0) {
|
|
133
128
|
const kw = parsed.isExport ? 'export type' : 'import type';
|
|
@@ -191,39 +186,101 @@ function resolvePkgRoot() {
|
|
|
191
186
|
return dirname(require.resolve('niuma-ui/package.json'));
|
|
192
187
|
}
|
|
193
188
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
189
|
+
/**
|
|
190
|
+
* ParseRuntimeBindings 从主入口抽出运行时导出名到文件的映射。
|
|
191
|
+
* 同时认源码 `export { default as RsX } from './...'` 和发布桶
|
|
192
|
+
* `import RsX_default from './...'` + `export { RsX_default as RsX }`。
|
|
193
|
+
*/
|
|
194
|
+
export function parseRuntimeBindings(source) {
|
|
199
195
|
const map = new Map();
|
|
200
|
-
const
|
|
196
|
+
const reexport = /^[ \t]*export\s+(?!type\b)\{([^}]+)\}\s+from\s*['"](\.[^'"]+)['"]/gm;
|
|
201
197
|
let match;
|
|
202
|
-
while ((match =
|
|
198
|
+
while ((match = reexport.exec(source)) !== null) {
|
|
203
199
|
const from = match[2] ?? '';
|
|
204
200
|
if (!from)
|
|
205
201
|
continue;
|
|
206
|
-
for (const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
202
|
+
for (const spec of parseSpecifierList(match[1] ?? '')) {
|
|
203
|
+
map.set(spec.right, { from, kind: spec.isDefault ? 'default' : 'named' });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const locals = new Map();
|
|
207
|
+
const imports = /^[ \t]*import\s+(?!type\b)([\s\S]+?)\s+from\s*['"](\.[^'"]+)['"]/gm;
|
|
208
|
+
while ((match = imports.exec(source)) !== null) {
|
|
209
|
+
const from = match[2] ?? '';
|
|
210
|
+
if (!from)
|
|
211
|
+
continue;
|
|
212
|
+
recordImportLocals(match[1] ?? '', from, locals);
|
|
213
|
+
}
|
|
214
|
+
const barrel = /^[ \t]*export\s+(?!type\b)\{([^}]+)\}(?!\s*from)/gm;
|
|
215
|
+
while ((match = barrel.exec(source)) !== null) {
|
|
216
|
+
for (const spec of parseSpecifierList(match[1] ?? '')) {
|
|
217
|
+
if (map.has(spec.right))
|
|
218
218
|
continue;
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
map.set(
|
|
222
|
-
}
|
|
219
|
+
const binding = locals.get(spec.left);
|
|
220
|
+
if (binding)
|
|
221
|
+
map.set(spec.right, binding);
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
224
|
return map;
|
|
226
225
|
}
|
|
226
|
+
function parseSpecifierList(inner) {
|
|
227
|
+
const out = [];
|
|
228
|
+
for (const raw of inner.split(',')) {
|
|
229
|
+
const part = raw.trim();
|
|
230
|
+
if (!part || part.startsWith('type '))
|
|
231
|
+
continue;
|
|
232
|
+
const def = part.match(/^default\s+as\s+(\w+)$/);
|
|
233
|
+
if (def?.[1]) {
|
|
234
|
+
out.push({ left: 'default', right: def[1], isDefault: true });
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const aliased = part.match(/^(\w+)\s+as\s+(\w+)$/);
|
|
238
|
+
if (aliased?.[1] && aliased[2]) {
|
|
239
|
+
out.push({ left: aliased[1], right: aliased[2], isDefault: false });
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (/^\w+$/.test(part)) {
|
|
243
|
+
out.push({ left: part, right: part, isDefault: false });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
function recordImportLocals(clause, from, locals) {
|
|
249
|
+
const trimmed = clause.trim();
|
|
250
|
+
if (!trimmed || trimmed.startsWith('*'))
|
|
251
|
+
return;
|
|
252
|
+
const namedStart = trimmed.indexOf('{');
|
|
253
|
+
if (namedStart === -1) {
|
|
254
|
+
if (/^\w+$/.test(trimmed))
|
|
255
|
+
locals.set(trimmed, { from, kind: 'default' });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const before = trimmed.slice(0, namedStart).replace(/,\s*$/, '').trim();
|
|
259
|
+
if (before && /^\w+$/.test(before)) {
|
|
260
|
+
locals.set(before, { from, kind: 'default' });
|
|
261
|
+
}
|
|
262
|
+
const namedEnd = trimmed.lastIndexOf('}');
|
|
263
|
+
if (namedEnd <= namedStart)
|
|
264
|
+
return;
|
|
265
|
+
for (const spec of parseSpecifierList(trimmed.slice(namedStart + 1, namedEnd))) {
|
|
266
|
+
if (spec.isDefault)
|
|
267
|
+
continue;
|
|
268
|
+
locals.set(spec.right, { from, kind: 'named' });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function loadRuntimeBindings(root) {
|
|
272
|
+
const srcIndex = join(root, 'src/index.ts');
|
|
273
|
+
const distIndex = join(root, 'dist/index.js');
|
|
274
|
+
const indexPath = existsSync(srcIndex) ? srcIndex : distIndex;
|
|
275
|
+
if (!existsSync(indexPath)) {
|
|
276
|
+
throw new Error(`niumaUiHost: 未找到 ${srcIndex} 或 ${distIndex}`);
|
|
277
|
+
}
|
|
278
|
+
const map = parseRuntimeBindings(readFileSync(indexPath, 'utf8'));
|
|
279
|
+
if (map.size === 0) {
|
|
280
|
+
throw new Error(`niumaUiHost: ${indexPath} 没有解析到运行时导出(需要 src re-export 或 dist 桶 import/export)`);
|
|
281
|
+
}
|
|
282
|
+
return map;
|
|
283
|
+
}
|
|
227
284
|
function resolveTarget(from, spec, root, useSource) {
|
|
228
285
|
if (!useSource)
|
|
229
286
|
return `${spec}/${toPublishedRel(from)}`;
|