@sola-air-ui/compiler 1.2.0 → 1.3.0
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/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/index.js +158 -81
- package/types/index.d.ts +1 -1
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
|
18
18
|
--target <esm|iife> Output format (default: esm)
|
|
19
19
|
esm — ES module with import/export (default, for bundlers)
|
|
20
20
|
iife — Self-contained IIFE for no-bundler environments
|
|
21
|
-
(
|
|
21
|
+
(enterprise portal widgets, plain <script> tags)
|
|
22
22
|
--export-name <Name> Global name for IIFE target (default: SolaComponent)
|
|
23
23
|
The compiled component mounts as window['Name'](el, props)
|
|
24
24
|
-v, --version Print compiler version
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,14 @@ const { version: COMPILER_VERSION } = JSON.parse(
|
|
|
6
6
|
readFileSync(new URL('../package.json', import.meta.url), 'utf8')
|
|
7
7
|
);
|
|
8
8
|
|
|
9
|
+
// Names every compiled component imports from core under their own name. A
|
|
10
|
+
// script that imports one of these again is not given a second binding.
|
|
11
|
+
const CORE_PROVIDED = new Set([
|
|
12
|
+
'createSignal', 'createDerived', 'createEffect', 'createIntent', 'createData',
|
|
13
|
+
'createPersistentSignal', 'onMount', 'onDestroy', 'pushContext', 'popContext',
|
|
14
|
+
'__flush_mounts', '__flush_destroys', '__untrack'
|
|
15
|
+
]);
|
|
16
|
+
|
|
9
17
|
const VOID_ELEMENTS = new Set([
|
|
10
18
|
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
11
19
|
'link', 'meta', 'param', 'source', 'track', 'wbr'
|
|
@@ -325,7 +333,7 @@ function preprocessTemplate(template, dynAttrs, handlers) {
|
|
|
325
333
|
} else if (hasDyn) {
|
|
326
334
|
// Build a string-concatenation expression instead of a runtime backtick
|
|
327
335
|
// template literal. Confirmed by driving a real browser against a live
|
|
328
|
-
//
|
|
336
|
+
// enterprise portal instance: its widget-script delivery pipeline
|
|
329
337
|
// silently mangles `${expr}` interpolation inside a template literal down
|
|
330
338
|
// to bare, unevaluated expression text (e.g. `background: ${x}` becomes
|
|
331
339
|
// the literal string "background: x" — the function reference itself,
|
|
@@ -889,6 +897,12 @@ export function compile(source, options = {}) {
|
|
|
889
897
|
const stateVars = new Set();
|
|
890
898
|
const exportedProps = [];
|
|
891
899
|
const componentImports = [];
|
|
900
|
+
// Every other import in the script. They used to be deleted, so a package
|
|
901
|
+
// import (`import DataCard from '@sola-air-ui/ui/data-card'`, or anything from
|
|
902
|
+
// '@sola-air-ui/core' that the compiler does not already provide) was gone from
|
|
903
|
+
// the output and left an undefined name behind.
|
|
904
|
+
const otherImports = [];
|
|
905
|
+
const coreImportExtras = [];
|
|
892
906
|
|
|
893
907
|
let reactiveVars = NO_REACTIVE_VARS;
|
|
894
908
|
if (strippedScript.trim()) {
|
|
@@ -914,6 +928,21 @@ export function compile(source, options = {}) {
|
|
|
914
928
|
node.specifiers.forEach(spec => {
|
|
915
929
|
componentImports.push({ localName: spec.local.name, path: src });
|
|
916
930
|
});
|
|
931
|
+
} else if (src === '@sola-air-ui/core' || src === '@sola/core') {
|
|
932
|
+
// The compiler already imports these names under their own names.
|
|
933
|
+
// Importing one again would be a duplicate binding, so keep only the
|
|
934
|
+
// rest: names it does not provide, and aliased ones.
|
|
935
|
+
node.specifiers.forEach(spec => {
|
|
936
|
+
if (spec.type !== 'ImportSpecifier') return;
|
|
937
|
+
const imported = spec.imported.name ?? spec.imported.value;
|
|
938
|
+
if (spec.local.name === imported && CORE_PROVIDED.has(imported)) return;
|
|
939
|
+
coreImportExtras.push({ imported, local: spec.local.name });
|
|
940
|
+
});
|
|
941
|
+
} else {
|
|
942
|
+
otherImports.push({
|
|
943
|
+
code: strippedScript.slice(node.start, node.end).trim().replace(/;?$/, ';'),
|
|
944
|
+
locals: node.specifiers.map(spec => spec.local.name)
|
|
945
|
+
});
|
|
917
946
|
}
|
|
918
947
|
edits.push({
|
|
919
948
|
start: node.start,
|
|
@@ -1050,6 +1079,11 @@ export function compile(source, options = {}) {
|
|
|
1050
1079
|
let uid = 0;
|
|
1051
1080
|
let domCode = '';
|
|
1052
1081
|
const importedComponentSet = new Set(componentImports.map(i => i.localName));
|
|
1082
|
+
// A capitalised tag that matches a name imported from a package is a component
|
|
1083
|
+
// too (`import DataCard from '@sola-air-ui/ui/data-card'`), not only a `.sola` path.
|
|
1084
|
+
for (const imp of otherImports) {
|
|
1085
|
+
for (const name of imp.locals) if (/^[A-Z]/.test(name)) importedComponentSet.add(name);
|
|
1086
|
+
}
|
|
1053
1087
|
|
|
1054
1088
|
const sourceFile = options.filename || '<component>';
|
|
1055
1089
|
|
|
@@ -1130,25 +1164,43 @@ export function compile(source, options = {}) {
|
|
|
1130
1164
|
domCode += ` ${parentVar}.appendChild(${cid}_target);\n`;
|
|
1131
1165
|
|
|
1132
1166
|
// Dynamically-bound props (rows={items}) arrive here as __soladyn_N__ placeholders —
|
|
1133
|
-
// resolve them back to the real expression from dynAttrs
|
|
1134
|
-
// (unquoted) property so the child receives live data, not the placeholder text.
|
|
1167
|
+
// resolve them back to the real expression from dynAttrs.
|
|
1135
1168
|
//
|
|
1136
1169
|
// `onXxx`-named props (e.g. onChange={handler}) arrive as a
|
|
1137
1170
|
// __solahandler_N__ marker, because preprocessTemplate cannot tell a
|
|
1138
1171
|
// component apart from a native element and lifts both the same way.
|
|
1139
1172
|
// Either way the expression is passed through as a live function.
|
|
1173
|
+
//
|
|
1174
|
+
// Three kinds of dynamic prop:
|
|
1175
|
+
// - a bare name (rows={items}) is passed as-is. For a $state that is the
|
|
1176
|
+
// signal accessor, which is how a child receives live data (the shipped
|
|
1177
|
+
// components accept either an accessor or a plain value);
|
|
1178
|
+
// - an expression that reads a signal (title={ctx.title}) is evaluated
|
|
1179
|
+
// with the signal reads rewritten, and the child is re-mounted when
|
|
1180
|
+
// the value changes;
|
|
1181
|
+
// - an expression that reads no signal (text={msg.text} in an {#each})
|
|
1182
|
+
// is evaluated once, when the child is mounted.
|
|
1140
1183
|
const propEntries = [];
|
|
1184
|
+
let reactiveProps = false;
|
|
1141
1185
|
for (const [key, val] of Object.entries(node.attribs || {})) {
|
|
1142
1186
|
const dynMatch = /^__soladyn_(\d+)__$/.exec(val);
|
|
1143
1187
|
const handlerMatch = /^__solahandler_(\d+)__$/.exec(val);
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1188
|
+
let propValue;
|
|
1189
|
+
if (dynMatch) {
|
|
1190
|
+
const raw = dynAttrs[parseInt(dynMatch[1], 10)];
|
|
1191
|
+
if (/^\s*[A-Za-z_$][\w$]*\s*$/.test(raw)) {
|
|
1192
|
+
propValue = `(${raw})`;
|
|
1193
|
+
} else {
|
|
1194
|
+
checkExpression(raw, 'component prop');
|
|
1195
|
+
const rewritten = rewrite(raw);
|
|
1196
|
+
if (rewritten !== raw) reactiveProps = true;
|
|
1197
|
+
propValue = `(${rewritten})`;
|
|
1198
|
+
}
|
|
1199
|
+
} else if (handlerMatch) {
|
|
1200
|
+
propValue = `(${rewrite(handlers[parseInt(handlerMatch[1], 10)])})`;
|
|
1201
|
+
} else {
|
|
1202
|
+
propValue = JSON.stringify(val);
|
|
1203
|
+
}
|
|
1152
1204
|
propEntries.push(`${JSON.stringify(key)}: ${propValue}`);
|
|
1153
1205
|
}
|
|
1154
1206
|
|
|
@@ -1162,7 +1214,21 @@ export function compile(source, options = {}) {
|
|
|
1162
1214
|
propEntries.push(`children: ${slotVar}`);
|
|
1163
1215
|
}
|
|
1164
1216
|
|
|
1165
|
-
|
|
1217
|
+
// The child mounts untracked, so nothing it reads while building itself
|
|
1218
|
+
// can re-run the parent's effect. Its teardown is kept and registered with
|
|
1219
|
+
// whatever scope is being built, so it runs when that scope is destroyed.
|
|
1220
|
+
domCode += ` let ${cid}_unmount = null;\n`;
|
|
1221
|
+
domCode += ` onDestroy(() => { if (typeof ${cid}_unmount === 'function') ${cid}_unmount(); ${cid}_unmount = null; });\n`;
|
|
1222
|
+
if (reactiveProps) {
|
|
1223
|
+
domCode += ` createEffect(() => {\n`;
|
|
1224
|
+
domCode += ` const ${cid}_props = { ${propEntries.join(', ')} };\n`;
|
|
1225
|
+
domCode += ` if (typeof ${cid}_unmount === 'function') __untrack(${cid}_unmount);\n`;
|
|
1226
|
+
domCode += ` ${cid}_target.textContent = '';\n`;
|
|
1227
|
+
domCode += ` ${cid}_unmount = __untrack(() => ${node.name}(${cid}_target, ${cid}_props));\n`;
|
|
1228
|
+
domCode += ` });\n`;
|
|
1229
|
+
} else {
|
|
1230
|
+
domCode += ` ${cid}_unmount = __untrack(() => ${node.name}(${cid}_target, { ${propEntries.join(', ')} }));\n`;
|
|
1231
|
+
}
|
|
1166
1232
|
return;
|
|
1167
1233
|
}
|
|
1168
1234
|
|
|
@@ -1173,37 +1239,50 @@ export function compile(source, options = {}) {
|
|
|
1173
1239
|
}
|
|
1174
1240
|
|
|
1175
1241
|
// {#if ...} {:else} ... {/if}
|
|
1242
|
+
//
|
|
1243
|
+
// Each time a branch is built it gets its own scope (pushContext). Effects,
|
|
1244
|
+
// onDestroy callbacks and child components created inside register with that
|
|
1245
|
+
// scope, and switching or removing the branch destroys it, so nothing built
|
|
1246
|
+
// for a branch that is no longer on the page keeps running.
|
|
1176
1247
|
if (node.name === 'sola-if') {
|
|
1177
1248
|
const cid = `c${uid++}`;
|
|
1178
1249
|
domCode += ` const ${cid}_a = document.createComment('if');\n`;
|
|
1179
1250
|
domCode += ` ${parentVar}.appendChild(${cid}_a);\n`;
|
|
1180
1251
|
domCode += ` let ${cid}_els = [];\n`;
|
|
1181
|
-
|
|
1252
|
+
domCode += ` let ${cid}_ctx = null;\n`;
|
|
1253
|
+
domCode += ` onDestroy(() => { if (${cid}_ctx) { __flush_destroys(${cid}_ctx); ${cid}_ctx = null; } });\n`;
|
|
1254
|
+
|
|
1182
1255
|
const thenChild = node.children.find(c => c.name === 'sola-then');
|
|
1183
1256
|
const elseChild = node.children.find(c => c.name === 'sola-else');
|
|
1184
1257
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1258
|
+
const emitBranch = (branch) => {
|
|
1259
|
+
if (!branch) return;
|
|
1260
|
+
domCode += ` ${cid}_ctx = pushContext();\n`;
|
|
1261
|
+
domCode += ` try {\n`;
|
|
1189
1262
|
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
1190
|
-
|
|
1263
|
+
branch.children.forEach(child => emitNode(child, 'f', locals));
|
|
1191
1264
|
domCode += ` Array.from(f.childNodes).forEach(n => ${cid}_els.push(n));\n`;
|
|
1192
1265
|
domCode += ` ${cid}_a.parentNode.insertBefore(f, ${cid}_a.nextSibling);\n`;
|
|
1193
|
-
}
|
|
1266
|
+
domCode += ` } finally { popContext(${cid}_ctx); }\n`;
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
domCode += ` createEffect(() => {\n`;
|
|
1270
|
+
domCode += ` if (${cid}_ctx) { __flush_destroys(${cid}_ctx); ${cid}_ctx = null; }\n`;
|
|
1271
|
+
domCode += ` ${cid}_els.forEach(e => e.remove()); ${cid}_els = [];\n`;
|
|
1272
|
+
domCode += ` if (${rewrite(node.attribs.condition)}) {\n`;
|
|
1273
|
+
emitBranch(thenChild);
|
|
1194
1274
|
domCode += ` } else {\n`;
|
|
1195
|
-
|
|
1196
|
-
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
1197
|
-
elseChild.children.forEach(child => emitNode(child, 'f', locals));
|
|
1198
|
-
domCode += ` Array.from(f.childNodes).forEach(n => ${cid}_els.push(n));\n`;
|
|
1199
|
-
domCode += ` ${cid}_a.parentNode.insertBefore(f, ${cid}_a.nextSibling);\n`;
|
|
1200
|
-
}
|
|
1275
|
+
emitBranch(elseChild);
|
|
1201
1276
|
domCode += ` }\n`;
|
|
1202
1277
|
domCode += ` });\n`;
|
|
1203
1278
|
return;
|
|
1204
1279
|
}
|
|
1205
1280
|
|
|
1206
1281
|
// {#each} with optional keyed diffing: {#each items as item (item.id)}
|
|
1282
|
+
//
|
|
1283
|
+
// Like {#if}, what is built for the list gets its own scope so it can be
|
|
1284
|
+
// destroyed: the whole list for an unkeyed each, one scope per key for a
|
|
1285
|
+
// keyed one (so removing an item stops that item's effects and children).
|
|
1207
1286
|
if (node.name === 'sola-each') {
|
|
1208
1287
|
const eid = `e${uid++}`;
|
|
1209
1288
|
// Item/index names shadow signals inside the loop body and key expression
|
|
@@ -1217,70 +1296,53 @@ export function compile(source, options = {}) {
|
|
|
1217
1296
|
domCode += ` const ${eid}_a = document.createComment('each');\n`;
|
|
1218
1297
|
domCode += ` ${parentVar}.appendChild(${eid}_a);\n`;
|
|
1219
1298
|
if (keyExpr) {
|
|
1220
|
-
// Keyed: maintain a Map<key,
|
|
1299
|
+
// Keyed: maintain a Map<key, { nodes, ctx }> and reconcile on each run
|
|
1221
1300
|
// Reconcile by walking the new order and moving each key's nodes into
|
|
1222
1301
|
// place against a cursor. The previous version only ever inserted, and
|
|
1223
1302
|
// against an anchor it never advanced — so reordering a keyed list (a
|
|
1224
1303
|
// sort, a drag, a reverse) left the DOM in its original order, silently.
|
|
1225
|
-
domCode += ` let ${eid}_keyMap = new Map()
|
|
1226
|
-
`;
|
|
1227
|
-
domCode += ` createEffect(() => {
|
|
1228
|
-
`;
|
|
1229
|
-
domCode += ` const
|
|
1230
|
-
`;
|
|
1231
|
-
domCode += `
|
|
1232
|
-
`;
|
|
1233
|
-
domCode += `
|
|
1234
|
-
`;
|
|
1235
|
-
domCode += `
|
|
1236
|
-
`;
|
|
1237
|
-
domCode += `
|
|
1238
|
-
`;
|
|
1239
|
-
domCode += `
|
|
1240
|
-
`;
|
|
1241
|
-
|
|
1242
|
-
`;
|
|
1243
|
-
domCode += ` const _key = String(${keyExpr});
|
|
1244
|
-
`;
|
|
1245
|
-
domCode += ` _seen.add(_key);
|
|
1246
|
-
`;
|
|
1247
|
-
domCode += ` let _nodes = ${eid}_keyMap.get(_key);
|
|
1248
|
-
`;
|
|
1249
|
-
domCode += ` if (!_nodes) {
|
|
1250
|
-
`;
|
|
1251
|
-
domCode += ` const f = document.createDocumentFragment();
|
|
1252
|
-
`;
|
|
1304
|
+
domCode += ` let ${eid}_keyMap = new Map();\n`;
|
|
1305
|
+
domCode += ` onDestroy(() => { for (const _e of ${eid}_keyMap.values()) __flush_destroys(_e.ctx); ${eid}_keyMap.clear(); });\n`;
|
|
1306
|
+
domCode += ` createEffect(() => {\n`;
|
|
1307
|
+
domCode += ` const _items = ${arrayExpr} || [];\n`;
|
|
1308
|
+
domCode += ` const _parent = ${eid}_a.parentNode;\n`;
|
|
1309
|
+
domCode += ` const _seen = new Set();\n`;
|
|
1310
|
+
domCode += ` let _cursor = ${eid}_a.nextSibling;\n`;
|
|
1311
|
+
domCode += ` for (let _i = 0; _i < _items.length; _i++) {\n`;
|
|
1312
|
+
domCode += ` const ${node.attribs.item} = _items[_i];\n`;
|
|
1313
|
+
if (node.attribs.index) domCode += ` const ${node.attribs.index} = _i;\n`;
|
|
1314
|
+
domCode += ` const _key = String(${keyExpr});\n`;
|
|
1315
|
+
domCode += ` _seen.add(_key);\n`;
|
|
1316
|
+
domCode += ` let _entry = ${eid}_keyMap.get(_key);\n`;
|
|
1317
|
+
domCode += ` if (!_entry) {\n`;
|
|
1318
|
+
domCode += ` const _kctx = pushContext();\n`;
|
|
1319
|
+
domCode += ` try {\n`;
|
|
1320
|
+
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
1253
1321
|
node.children.forEach(child => emitNode(child, 'f', eachLocals));
|
|
1254
|
-
domCode += `
|
|
1255
|
-
`;
|
|
1256
|
-
domCode += ` ${eid}_keyMap.set(_key,
|
|
1257
|
-
`;
|
|
1258
|
-
domCode += `
|
|
1259
|
-
`;
|
|
1260
|
-
domCode += `
|
|
1261
|
-
`;
|
|
1262
|
-
domCode += `
|
|
1263
|
-
`;
|
|
1264
|
-
domCode += `
|
|
1265
|
-
`;
|
|
1266
|
-
domCode += `
|
|
1267
|
-
`;
|
|
1268
|
-
domCode += ` }
|
|
1269
|
-
`;
|
|
1270
|
-
domCode += ` for (const [_k, _stale] of ${eid}_keyMap) {
|
|
1271
|
-
`;
|
|
1272
|
-
domCode += ` if (!_seen.has(_k)) { _stale.forEach(n => n.remove()); ${eid}_keyMap.delete(_k); }
|
|
1273
|
-
`;
|
|
1274
|
-
domCode += ` }
|
|
1275
|
-
`;
|
|
1276
|
-
domCode += ` });
|
|
1277
|
-
`;
|
|
1322
|
+
domCode += ` _entry = { nodes: Array.from(f.childNodes), ctx: _kctx };\n`;
|
|
1323
|
+
domCode += ` } finally { popContext(_kctx); }\n`;
|
|
1324
|
+
domCode += ` ${eid}_keyMap.set(_key, _entry);\n`;
|
|
1325
|
+
domCode += ` }\n`;
|
|
1326
|
+
domCode += ` for (const _n of _entry.nodes) {\n`;
|
|
1327
|
+
domCode += ` if (_n === _cursor) { _cursor = _cursor.nextSibling; }\n`;
|
|
1328
|
+
domCode += ` else { _parent.insertBefore(_n, _cursor); }\n`;
|
|
1329
|
+
domCode += ` }\n`;
|
|
1330
|
+
domCode += ` }\n`;
|
|
1331
|
+
domCode += ` for (const [_k, _stale] of ${eid}_keyMap) {\n`;
|
|
1332
|
+
domCode += ` if (!_seen.has(_k)) { __flush_destroys(_stale.ctx); _stale.nodes.forEach(n => n.remove()); ${eid}_keyMap.delete(_k); }\n`;
|
|
1333
|
+
domCode += ` }\n`;
|
|
1334
|
+
domCode += ` });\n`;
|
|
1278
1335
|
} else {
|
|
1279
1336
|
// Unkeyed: full teardown + rebuild
|
|
1280
1337
|
domCode += ` let ${eid}_els = [];\n`;
|
|
1338
|
+
domCode += ` let ${eid}_ctx = null;\n`;
|
|
1339
|
+
domCode += ` onDestroy(() => { if (${eid}_ctx) { __flush_destroys(${eid}_ctx); ${eid}_ctx = null; } });\n`;
|
|
1281
1340
|
domCode += ` createEffect(() => {\n`;
|
|
1341
|
+
domCode += ` if (${eid}_ctx) { __flush_destroys(${eid}_ctx); ${eid}_ctx = null; }\n`;
|
|
1282
1342
|
domCode += ` ${eid}_els.forEach(e => e.remove()); ${eid}_els = [];\n`;
|
|
1283
1343
|
domCode += ` const _items = ${arrayExpr} || [];\n`;
|
|
1344
|
+
domCode += ` ${eid}_ctx = pushContext();\n`;
|
|
1345
|
+
domCode += ` try {\n`;
|
|
1284
1346
|
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
1285
1347
|
domCode += ` for (let _i = 0; _i < _items.length; _i++) {\n`;
|
|
1286
1348
|
domCode += ` const ${node.attribs.item} = _items[_i];\n`;
|
|
@@ -1289,6 +1351,7 @@ export function compile(source, options = {}) {
|
|
|
1289
1351
|
domCode += ` }\n`;
|
|
1290
1352
|
domCode += ` Array.from(f.childNodes).forEach(n => ${eid}_els.push(n));\n`;
|
|
1291
1353
|
domCode += ` ${eid}_a.parentNode.insertBefore(f, ${eid}_a.nextSibling);\n`;
|
|
1354
|
+
domCode += ` } finally { popContext(${eid}_ctx); }\n`;
|
|
1292
1355
|
domCode += ` });\n`;
|
|
1293
1356
|
}
|
|
1294
1357
|
return;
|
|
@@ -1356,7 +1419,7 @@ export function compile(source, options = {}) {
|
|
|
1356
1419
|
root.children.forEach(child => emitNode(child, '__target'));
|
|
1357
1420
|
|
|
1358
1421
|
// Assemble final output module
|
|
1359
|
-
const CORE_EXPORTS = '
|
|
1422
|
+
const CORE_EXPORTS = [...CORE_PROVIDED].join(', ');
|
|
1360
1423
|
// The default bus is imported under an alias so a user variable named `bus`
|
|
1361
1424
|
// cannot shadow what $on / $lastEvent compile to.
|
|
1362
1425
|
const ESM_CORE_IMPORTS = CORE_EXPORTS + ', bus as __solaBus';
|
|
@@ -1365,19 +1428,30 @@ export function compile(source, options = {}) {
|
|
|
1365
1428
|
output += `// Compiled by @sola-air-ui/compiler v${COMPILER_VERSION}\n`;
|
|
1366
1429
|
|
|
1367
1430
|
if (target === 'iife') {
|
|
1368
|
-
//
|
|
1431
|
+
// No-bundler mode: pull from window.SolaCore global
|
|
1369
1432
|
output += `(function() {\n`;
|
|
1370
|
-
|
|
1433
|
+
const iifeExtras = coreImportExtras.map(e => (e.local === e.imported ? e.imported : `${e.imported}: ${e.local}`));
|
|
1434
|
+
output += `const { ${[IIFE_CORE_PULL, ...iifeExtras].join(', ')} } = window.SolaCore;\n`;
|
|
1371
1435
|
for (const imp of componentImports) {
|
|
1372
1436
|
output += `// Note: sub-component ${imp.localName} must be pre-loaded before this script\n`;
|
|
1373
1437
|
}
|
|
1438
|
+
for (const imp of otherImports) {
|
|
1439
|
+
output += `// Note: ${imp.locals.join(', ')} must be loaded before this script (no-bundler output cannot import)\n`;
|
|
1440
|
+
}
|
|
1374
1441
|
output += `\n`;
|
|
1375
1442
|
output += `window['${options.exportName || 'SolaComponent'}'] = function mount(__target, props = {}) {\n`;
|
|
1376
1443
|
} else {
|
|
1377
1444
|
output += `import { ${ESM_CORE_IMPORTS} } from '@sola-air-ui/core';\n`;
|
|
1445
|
+
if (coreImportExtras.length) {
|
|
1446
|
+
const names = coreImportExtras.map(e => (e.local === e.imported ? e.imported : `${e.imported} as ${e.local}`));
|
|
1447
|
+
output += `import { ${names.join(', ')} } from '@sola-air-ui/core';\n`;
|
|
1448
|
+
}
|
|
1378
1449
|
for (const imp of componentImports) {
|
|
1379
1450
|
output += `import ${imp.localName} from '${imp.path}';\n`;
|
|
1380
1451
|
}
|
|
1452
|
+
for (const imp of otherImports) {
|
|
1453
|
+
output += `${imp.code}\n`;
|
|
1454
|
+
}
|
|
1381
1455
|
output += `\n`;
|
|
1382
1456
|
output += `export default function mount(__target, props = {}) {\n`;
|
|
1383
1457
|
}
|
|
@@ -1462,6 +1536,9 @@ function inferPropType(defaultSrc) {
|
|
|
1462
1536
|
if (/^(['"`])[\s\S]*\1$/.test(d)) return 'string';
|
|
1463
1537
|
if (/^-?\d[\d_]*(\.\d+)?([eE][+-]?\d+)?$/.test(d)) return 'number';
|
|
1464
1538
|
if (d === 'true' || d === 'false') return 'boolean';
|
|
1539
|
+
// A null or undefined default says nothing about the type. `unknown` would stop a
|
|
1540
|
+
// TypeScript host from reading or calling the prop; `any` lets it.
|
|
1541
|
+
if (d === 'null' || d === 'undefined') return 'any';
|
|
1465
1542
|
if (d.startsWith('[')) return 'unknown[]';
|
|
1466
1543
|
if (d.startsWith('{')) return 'Record<string, unknown>';
|
|
1467
1544
|
if (/^(\(.*\)|\w+)\s*=>|^function\b/.test(d)) return '(...args: any[]) => any';
|
package/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface CompileOptions {
|
|
|
4
4
|
/**
|
|
5
5
|
* 'esm' (default) emits a module importing from @sola-air-ui/core.
|
|
6
6
|
* 'iife' emits a self-contained bundle reading from window.SolaCore, for
|
|
7
|
-
*
|
|
7
|
+
* enterprise portals and other no-bundler hosts.
|
|
8
8
|
*/
|
|
9
9
|
target?: 'esm' | 'iife';
|
|
10
10
|
/** Global name the IIFE build registers itself under. Default 'SolaComponent'. */
|