@rozie/babel-plugin 0.1.4 → 0.2.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/dist/index.cjs +1220 -55
- package/dist/index.mjs +1209 -44
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1587,7 +1587,12 @@ const RozieErrorCode = {
|
|
|
1587
1587
|
KEYNAV_ORPHAN_ITEM: "ROZ984",
|
|
1588
1588
|
KEYNAV_BAD_FOCUS_MODEL: "ROZ985",
|
|
1589
1589
|
KEYNAV_MULTIPLE_ROOTS: "ROZ986",
|
|
1590
|
-
KEYNAV_SOURCE_UNRESOLVED: "ROZ987"
|
|
1590
|
+
KEYNAV_SOURCE_UNRESOLVED: "ROZ987",
|
|
1591
|
+
MANIFEST_SCHEMA_VERSION_MISMATCH: "ROZ988",
|
|
1592
|
+
MALFORMED_MANIFEST: "ROZ989",
|
|
1593
|
+
PORTAL_DIRECTIVE_ON_SLOT: "ROZ990",
|
|
1594
|
+
PORTAL_DIRECTIVE_ON_COMPONENT: "ROZ991",
|
|
1595
|
+
PORTAL_DIRECTIVE_EMPTY_VALUE: "ROZ992"
|
|
1591
1596
|
};
|
|
1592
1597
|
//#endregion
|
|
1593
1598
|
//#region ../core/src/splitter/splitBlocks.ts
|
|
@@ -41192,6 +41197,13 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41192
41197
|
const args = [];
|
|
41193
41198
|
for (const attr of el.attributes) if (attr.kind === "static" && attr.name === "name" && attr.value !== null) slotName = attr.value;
|
|
41194
41199
|
else if (attr.kind === "static" && attr.name === "portal" && attr.value === null) isPortal = true;
|
|
41200
|
+
else if (attr.kind === "directive" && attr.name === "portal") diagnostics.push({
|
|
41201
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_ON_SLOT,
|
|
41202
|
+
severity: "error",
|
|
41203
|
+
message: "r-portal is not valid on <slot> — use the boolean `portal` attribute for slot-content portalling (r-portal is a distinct directive for portalling an ordinary element's own rendered subtree).",
|
|
41204
|
+
loc: attr.loc,
|
|
41205
|
+
hint: "Use <slot portal /> (no r-, boolean form) to portal slot content into an engine-owned container; r-portal=\"<expr>\" portals an ordinary element out to a container."
|
|
41206
|
+
});
|
|
41195
41207
|
else if (attr.kind === "binding" && attr.value !== null) {
|
|
41196
41208
|
if (attr.name === "params") continue;
|
|
41197
41209
|
const expr = tryParseExpression(attr.value);
|
|
@@ -41222,6 +41234,8 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41222
41234
|
let keynavItem;
|
|
41223
41235
|
let keynavActiveClassExpression;
|
|
41224
41236
|
let keynavActiveClassDeps;
|
|
41237
|
+
let portalToExpression;
|
|
41238
|
+
let portalToDeps;
|
|
41225
41239
|
for (const attr of el.attributes) {
|
|
41226
41240
|
if (attr.kind === "event") {
|
|
41227
41241
|
const handlerExpr = attr.value !== null ? tryParseExpression(attr.value) : null;
|
|
@@ -41431,6 +41445,34 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41431
41445
|
isExternal = true;
|
|
41432
41446
|
continue;
|
|
41433
41447
|
}
|
|
41448
|
+
if (attr.name === "portal") {
|
|
41449
|
+
if (attr.value === null || attr.value.trim() === "") {
|
|
41450
|
+
diagnostics.push({
|
|
41451
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_EMPTY_VALUE,
|
|
41452
|
+
severity: "error",
|
|
41453
|
+
message: "r-portal requires a container expression (e.g. r-portal=\"$props.appendTo\") — an empty value is not a valid portal target.",
|
|
41454
|
+
loc: attr.loc,
|
|
41455
|
+
hint: "Provide an expression that evaluates to an Element (or a falsy value / null to render in place)."
|
|
41456
|
+
});
|
|
41457
|
+
continue;
|
|
41458
|
+
}
|
|
41459
|
+
if (elementIsComponent) {
|
|
41460
|
+
diagnostics.push({
|
|
41461
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_ON_COMPONENT,
|
|
41462
|
+
severity: "error",
|
|
41463
|
+
message: "r-portal is not supported on a <components>-registered child component element (v1 limitation) — only plain/host elements can be portalled.",
|
|
41464
|
+
loc: attr.loc,
|
|
41465
|
+
hint: "Wrap the component in a plain host element and put r-portal on that wrapper instead."
|
|
41466
|
+
});
|
|
41467
|
+
continue;
|
|
41468
|
+
}
|
|
41469
|
+
const expr = tryParseExpression(attr.value);
|
|
41470
|
+
if (expr) {
|
|
41471
|
+
portalToExpression = expr;
|
|
41472
|
+
portalToDeps = computeExpressionDeps(expr, bindings);
|
|
41473
|
+
}
|
|
41474
|
+
continue;
|
|
41475
|
+
}
|
|
41434
41476
|
if ((attr.name === "model" || attr.name === "show" || attr.name === "html" || attr.name === "text") && attr.value !== null) {
|
|
41435
41477
|
const expr = tryParseExpression(attr.value);
|
|
41436
41478
|
const modifiers = attr.name === "model" ? resolveModelModifiers(attr.chain, registry, diagnostics) : [];
|
|
@@ -41482,7 +41524,11 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41482
41524
|
...isExternal ? { isExternal: true } : {},
|
|
41483
41525
|
...remountKeyExpression !== void 0 ? { remountKeyExpression } : {},
|
|
41484
41526
|
...keynavRoot !== void 0 ? { keynavRoot } : {},
|
|
41485
|
-
...keynavItem !== void 0 ? { keynavItem } : {}
|
|
41527
|
+
...keynavItem !== void 0 ? { keynavItem } : {},
|
|
41528
|
+
...portalToExpression !== void 0 ? { portalTo: {
|
|
41529
|
+
expression: portalToExpression,
|
|
41530
|
+
deps: portalToDeps ?? []
|
|
41531
|
+
} } : {}
|
|
41486
41532
|
};
|
|
41487
41533
|
}
|
|
41488
41534
|
/**
|
|
@@ -55808,6 +55854,340 @@ var IRCache = class {
|
|
|
55808
55854
|
}
|
|
55809
55855
|
};
|
|
55810
55856
|
//#endregion
|
|
55857
|
+
//#region ../core/src/manifest/buildManifest.ts
|
|
55858
|
+
var import_lib = /* @__PURE__ */ __toESM(require_lib$6(), 1);
|
|
55859
|
+
typeof import_lib.default === "function" ? import_lib.default : import_lib.default.default;
|
|
55860
|
+
//#endregion
|
|
55861
|
+
//#region ../core/src/manifest/readManifest.ts
|
|
55862
|
+
/**
|
|
55863
|
+
* parseManifest(raw) — validate + deserialize a published-primitive manifest
|
|
55864
|
+
* into a producer slot/prop/expose surface (Phase 75, D-04).
|
|
55865
|
+
*
|
|
55866
|
+
* Fails CLOSED on any trust-boundary violation (T-75-01): an unknown or
|
|
55867
|
+
* mismatched `schemaVersion` — or any structurally malformed field — never
|
|
55868
|
+
* silently degrades into a partial/best-effort surface. Both failure modes
|
|
55869
|
+
* return `{ surface: null, error }`; parseManifest NEVER throws (T-75-02: a
|
|
55870
|
+
* crafted/oversized paramType source string is dropped per-entry, not fatal).
|
|
55871
|
+
*
|
|
55872
|
+
* Per T-75-03, every field is read via explicit key access into a FRESH
|
|
55873
|
+
* object — parseManifest never `Object.assign`/spreads the parsed JSON into
|
|
55874
|
+
* a shared target, so a `__proto__`/`constructor`-keyed manifest cannot
|
|
55875
|
+
* pollute anything beyond the literal-keyed surface it produces.
|
|
55876
|
+
*
|
|
55877
|
+
* @experimental — shape may change before v1.0
|
|
55878
|
+
*/
|
|
55879
|
+
function malformed(message) {
|
|
55880
|
+
return {
|
|
55881
|
+
surface: null,
|
|
55882
|
+
error: {
|
|
55883
|
+
code: RozieErrorCode.MALFORMED_MANIFEST,
|
|
55884
|
+
message
|
|
55885
|
+
}
|
|
55886
|
+
};
|
|
55887
|
+
}
|
|
55888
|
+
function schemaMismatch(message) {
|
|
55889
|
+
return {
|
|
55890
|
+
surface: null,
|
|
55891
|
+
error: {
|
|
55892
|
+
code: RozieErrorCode.MANIFEST_SCHEMA_VERSION_MISMATCH,
|
|
55893
|
+
message
|
|
55894
|
+
}
|
|
55895
|
+
};
|
|
55896
|
+
}
|
|
55897
|
+
function isPlainObject(v) {
|
|
55898
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
55899
|
+
}
|
|
55900
|
+
/**
|
|
55901
|
+
* Deserialize one paramType SOURCE STRING back into a `TSType` by parsing
|
|
55902
|
+
* `type __RozieParam = <src>;` and lifting the type-alias's typeAnnotation.
|
|
55903
|
+
* Wrapped in try/catch — a garbage/oversized string is dropped (returns
|
|
55904
|
+
* `null`) rather than throwing out of parseManifest (T-75-02).
|
|
55905
|
+
*/
|
|
55906
|
+
function deserializeParamType(src) {
|
|
55907
|
+
try {
|
|
55908
|
+
const stmt = (0, import_lib$3.parse)(`type __RozieParam = ${src};`, {
|
|
55909
|
+
sourceType: "module",
|
|
55910
|
+
plugins: ["typescript"]
|
|
55911
|
+
}).program.body[0];
|
|
55912
|
+
if (!stmt || !t$3.isTSTypeAliasDeclaration(stmt)) return null;
|
|
55913
|
+
return stmt.typeAnnotation;
|
|
55914
|
+
} catch {
|
|
55915
|
+
return null;
|
|
55916
|
+
}
|
|
55917
|
+
}
|
|
55918
|
+
/** Validate + rebuild one manifest prop entry. Returns null on structural violation. */
|
|
55919
|
+
function readProp(raw) {
|
|
55920
|
+
if (!isPlainObject(raw)) return null;
|
|
55921
|
+
const name = raw.name;
|
|
55922
|
+
const isModel = raw.isModel;
|
|
55923
|
+
if (typeof name !== "string") return null;
|
|
55924
|
+
if (typeof isModel !== "boolean") return null;
|
|
55925
|
+
return {
|
|
55926
|
+
name,
|
|
55927
|
+
isModel
|
|
55928
|
+
};
|
|
55929
|
+
}
|
|
55930
|
+
/** Validate + rebuild one manifest slot-param entry. Returns null on structural violation. */
|
|
55931
|
+
function readSlotParam(raw) {
|
|
55932
|
+
if (!isPlainObject(raw)) return null;
|
|
55933
|
+
const name = raw.name;
|
|
55934
|
+
if (typeof name !== "string") return null;
|
|
55935
|
+
return {
|
|
55936
|
+
type: "ParamDecl",
|
|
55937
|
+
name,
|
|
55938
|
+
valueExpression: t$3.identifier(name),
|
|
55939
|
+
sourceLoc: {
|
|
55940
|
+
start: 0,
|
|
55941
|
+
end: 0
|
|
55942
|
+
}
|
|
55943
|
+
};
|
|
55944
|
+
}
|
|
55945
|
+
/** Validate + rebuild one manifest slot entry into a minimal SlotDecl. */
|
|
55946
|
+
function readSlot(raw) {
|
|
55947
|
+
if (!isPlainObject(raw)) return null;
|
|
55948
|
+
const name = raw.name;
|
|
55949
|
+
const paramsRaw = raw.params;
|
|
55950
|
+
const paramTypesRaw = raw.paramTypes;
|
|
55951
|
+
const isPortal = raw.isPortal;
|
|
55952
|
+
const isReactive = raw.isReactive;
|
|
55953
|
+
if (typeof name !== "string") return null;
|
|
55954
|
+
if (!Array.isArray(paramsRaw)) return null;
|
|
55955
|
+
if (paramTypesRaw !== null && !Array.isArray(paramTypesRaw)) return null;
|
|
55956
|
+
if (typeof isPortal !== "boolean") return null;
|
|
55957
|
+
if (typeof isReactive !== "boolean") return null;
|
|
55958
|
+
const params = [];
|
|
55959
|
+
for (const p of paramsRaw) {
|
|
55960
|
+
const param = readSlotParam(p);
|
|
55961
|
+
if (param === null) return null;
|
|
55962
|
+
params.push(param);
|
|
55963
|
+
}
|
|
55964
|
+
let paramTypes;
|
|
55965
|
+
if (paramTypesRaw !== null) {
|
|
55966
|
+
const deserialized = [];
|
|
55967
|
+
for (const src of paramTypesRaw) {
|
|
55968
|
+
if (typeof src !== "string") continue;
|
|
55969
|
+
const ty = deserializeParamType(src);
|
|
55970
|
+
if (ty !== null) deserialized.push(ty);
|
|
55971
|
+
}
|
|
55972
|
+
paramTypes = deserialized;
|
|
55973
|
+
}
|
|
55974
|
+
return {
|
|
55975
|
+
type: "SlotDecl",
|
|
55976
|
+
name,
|
|
55977
|
+
defaultContent: null,
|
|
55978
|
+
params,
|
|
55979
|
+
...paramTypes !== void 0 ? { paramTypes } : {},
|
|
55980
|
+
presence: "always",
|
|
55981
|
+
nestedSlots: [],
|
|
55982
|
+
sourceLoc: {
|
|
55983
|
+
start: 0,
|
|
55984
|
+
end: 0
|
|
55985
|
+
},
|
|
55986
|
+
isPortal,
|
|
55987
|
+
isReactive
|
|
55988
|
+
};
|
|
55989
|
+
}
|
|
55990
|
+
/** Validate + rebuild one manifest expose entry. Returns null on structural violation. */
|
|
55991
|
+
function readExposeMember(raw) {
|
|
55992
|
+
if (!isPlainObject(raw)) return null;
|
|
55993
|
+
const name = raw.name;
|
|
55994
|
+
if (typeof name !== "string") return null;
|
|
55995
|
+
return { name };
|
|
55996
|
+
}
|
|
55997
|
+
/**
|
|
55998
|
+
* Validate a manifest's `schemaVersion` FIRST (fail closed, D-04), then
|
|
55999
|
+
* shape-validate props/slots/emits/expose and deserialize into a
|
|
56000
|
+
* `ProducerSurface` consumable by `threadParamTypes` / `validateTwoWayBindings`.
|
|
56001
|
+
* Never throws (T-75-01/T-75-02).
|
|
56002
|
+
*/
|
|
56003
|
+
function parseManifest(raw, _opts) {
|
|
56004
|
+
if (!isPlainObject(raw)) return malformed("Manifest is not a JSON object.");
|
|
56005
|
+
const schemaVersion = raw.schemaVersion;
|
|
56006
|
+
if (typeof schemaVersion !== "number") return malformed("Manifest is missing a numeric \"schemaVersion\" field.");
|
|
56007
|
+
if (schemaVersion !== 1) return schemaMismatch(`Manifest schemaVersion ${schemaVersion} is incompatible with the compiler's MANIFEST_SCHEMA_VERSION 1. Reinstall a compatible version of the published primitive.`);
|
|
56008
|
+
const propsRaw = raw.props;
|
|
56009
|
+
const slotsRaw = raw.slots;
|
|
56010
|
+
const emitsRaw = raw.emits;
|
|
56011
|
+
const exposeRaw = raw.expose;
|
|
56012
|
+
if (!Array.isArray(propsRaw)) return malformed("Manifest \"props\" is not an array.");
|
|
56013
|
+
if (!Array.isArray(slotsRaw)) return malformed("Manifest \"slots\" is not an array.");
|
|
56014
|
+
if (!Array.isArray(emitsRaw)) return malformed("Manifest \"emits\" is not an array.");
|
|
56015
|
+
if (!Array.isArray(exposeRaw)) return malformed("Manifest \"expose\" is not an array.");
|
|
56016
|
+
const props = [];
|
|
56017
|
+
for (const p of propsRaw) {
|
|
56018
|
+
const prop = readProp(p);
|
|
56019
|
+
if (prop === null) return malformed("Manifest \"props\" entry is malformed.");
|
|
56020
|
+
props.push(prop);
|
|
56021
|
+
}
|
|
56022
|
+
const slots = [];
|
|
56023
|
+
for (const s of slotsRaw) {
|
|
56024
|
+
const slot = readSlot(s);
|
|
56025
|
+
if (slot === null) return malformed("Manifest \"slots\" entry is malformed.");
|
|
56026
|
+
slots.push(slot);
|
|
56027
|
+
}
|
|
56028
|
+
for (const e of emitsRaw) if (typeof e !== "string") return malformed("Manifest \"emits\" entry is not a string.");
|
|
56029
|
+
const expose = [];
|
|
56030
|
+
for (const m of exposeRaw) {
|
|
56031
|
+
const member = readExposeMember(m);
|
|
56032
|
+
if (member === null) return malformed("Manifest \"expose\" entry is malformed.");
|
|
56033
|
+
expose.push(member);
|
|
56034
|
+
}
|
|
56035
|
+
return {
|
|
56036
|
+
surface: {
|
|
56037
|
+
slots,
|
|
56038
|
+
props,
|
|
56039
|
+
expose
|
|
56040
|
+
},
|
|
56041
|
+
error: null
|
|
56042
|
+
};
|
|
56043
|
+
}
|
|
56044
|
+
//#endregion
|
|
56045
|
+
//#region ../core/src/manifest/resolveManifestProducer.ts
|
|
56046
|
+
/**
|
|
56047
|
+
* resolveManifestProducer — Phase 75 Plan 02 (D-08/D-09/D-10).
|
|
56048
|
+
*
|
|
56049
|
+
* Wires the manifest contract (Plan 01's `buildManifest`/`parseManifest`)
|
|
56050
|
+
* into `@rozie/core`'s producer-resolution seam so a PUBLISHED cross-package
|
|
56051
|
+
* `<components>` specifier resolves a producer contract from a compiled
|
|
56052
|
+
* `rozie-manifest.json` instead of a `.rozie` source file.
|
|
56053
|
+
*
|
|
56054
|
+
* **COMP-04 bounded exception**: local `.rozie` composition (relative /
|
|
56055
|
+
* tsconfig-alias specifiers) still needs NO cross-`.rozie` IR type info and
|
|
56056
|
+
* keeps reading on-disk `.rozie` source unchanged via the existing
|
|
56057
|
+
* `resolver.resolveProducerPath` + `IRCache.getIRComponent` path. ONLY
|
|
56058
|
+
* published cross-PACKAGE specifiers (D-08's `@scope/pkg/Component.rozie`
|
|
56059
|
+
* shape) read this compiled manifest artifact. This module and its two
|
|
56060
|
+
* call-sites (threadParamTypes.ts, validateTwoWayBindings.ts) are the entire
|
|
56061
|
+
* surface of that exception.
|
|
56062
|
+
*
|
|
56063
|
+
* Convention-based per-target package derivation (D-08): for a published
|
|
56064
|
+
* specifier `@scope/pkg/Component.rozie`, the trailing `/Component.rozie`
|
|
56065
|
+
* segment is stripped to yield the base package `@scope/pkg`, then
|
|
56066
|
+
* `-${target}` is appended to yield the derived per-target package name
|
|
56067
|
+
* (e.g. `@rozie-ui/combobox` + `-react` → `@rozie-ui/combobox-react`).
|
|
56068
|
+
*
|
|
56069
|
+
* Location (D-10): the derived package is located via the SAME
|
|
56070
|
+
* `enhanced-resolve` layer `ProducerResolver` already uses for `.rozie`
|
|
56071
|
+
* producer resolution — walking `node_modules` from the consumer file's
|
|
56072
|
+
* directory (NOT from `ResolverOptions.root`, which feeds only the
|
|
56073
|
+
* tsconfig-paths matcher). Because a real per-target leaf's `package.json`
|
|
56074
|
+
* `exports` map does not (and need not) list `"./package.json"` or
|
|
56075
|
+
* `"./rozie-manifest.json"` as exported subpaths, this resolver deliberately
|
|
56076
|
+
* resolves the package's bare `"."` export entry (its normal `import`
|
|
56077
|
+
* condition target, e.g. `dist/index.mjs`) and then walks UP from that
|
|
56078
|
+
* resolved file to the nearest ancestor directory whose `package.json`
|
|
56079
|
+
* `name` matches the derived package name (T-75-04/T-75-05 — bounded walk,
|
|
56080
|
+
* name-verified, never an unauthenticated `../` traversal). The manifest is
|
|
56081
|
+
* then read at a FIXED literal relative path inside that directory
|
|
56082
|
+
* (`rozie-manifest.json`) — no specifier-derived path segment ever reaches
|
|
56083
|
+
* `readFileSync` (T-75-04).
|
|
56084
|
+
*
|
|
56085
|
+
* @experimental — shape may change before v1.0
|
|
56086
|
+
*/
|
|
56087
|
+
/** Tsconfig-alias-shaped prefix (empty npm scope, e.g. `@/components/Modal.rozie`). */
|
|
56088
|
+
const TSCONFIG_ALIAS_PREFIX$1 = "@/";
|
|
56089
|
+
/**
|
|
56090
|
+
* True iff `specifier` is a PUBLISHED cross-package `<components>` specifier
|
|
56091
|
+
* (D-08 shape): ends in `.rozie`, is neither a relative/absolute path nor a
|
|
56092
|
+
* tsconfig `paths`-alias-shaped specifier.
|
|
56093
|
+
*
|
|
56094
|
+
* `isPublishedSpecifier` is a pure, syntactic classifier — it does not itself
|
|
56095
|
+
* consult a real tsconfig (that happens inside `ProducerResolver`'s local
|
|
56096
|
+
* COMP-04 path when this returns `false`). A leading `@/` is NOT a valid npm
|
|
56097
|
+
* scoped-package prefix (npm scopes require at least one character between
|
|
56098
|
+
* `@` and the following `/`), so it is the reliable syntactic signal that a
|
|
56099
|
+
* `@/...` specifier is tsconfig-alias-shaped rather than a real scoped
|
|
56100
|
+
* package — matching `ProducerResolver`'s own documented `@/components/Modal.rozie`
|
|
56101
|
+
* example of a tsconfig-paths alias.
|
|
56102
|
+
*/
|
|
56103
|
+
function isPublishedSpecifier$1(specifier) {
|
|
56104
|
+
if (!specifier.endsWith(".rozie")) return false;
|
|
56105
|
+
if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
|
|
56106
|
+
if (specifier.startsWith(TSCONFIG_ALIAS_PREFIX$1)) return false;
|
|
56107
|
+
return true;
|
|
56108
|
+
}
|
|
56109
|
+
/**
|
|
56110
|
+
* Strip the trailing `/<Component>.rozie` leaf off a published specifier to
|
|
56111
|
+
* derive the base package name (e.g. `@rozie-ui/combobox/Combobox.rozie` →
|
|
56112
|
+
* `@rozie-ui/combobox`). Returns `null` when the specifier has no `/`
|
|
56113
|
+
* separating a package from a leaf (malformed — never a valid published
|
|
56114
|
+
* specifier in practice, since `isPublishedSpecifier` requires `.rozie`).
|
|
56115
|
+
*/
|
|
56116
|
+
function deriveBasePackageName(specifier) {
|
|
56117
|
+
const lastSlash = specifier.lastIndexOf("/");
|
|
56118
|
+
if (lastSlash <= 0) return null;
|
|
56119
|
+
return specifier.slice(0, lastSlash);
|
|
56120
|
+
}
|
|
56121
|
+
/**
|
|
56122
|
+
* Walk UP from a resolved entry-file path to the nearest ancestor directory
|
|
56123
|
+
* whose `package.json` `name` field equals `expectedName` AND which carries a
|
|
56124
|
+
* sibling `rozie-manifest.json`. Bounded to 10 levels (T-75-05 — a real
|
|
56125
|
+
* per-target leaf's package root sits 1-3 levels above its resolved `dist/*`
|
|
56126
|
+
* entry; 10 is a generous, still-finite bound that can never escape into an
|
|
56127
|
+
* unrelated ancestor tree undetected, since EVERY level is name-verified
|
|
56128
|
+
* before being accepted).
|
|
56129
|
+
*
|
|
56130
|
+
* Requiring the manifest sibling (not just a name match) is deliberate: some
|
|
56131
|
+
* per-target build tooling generates a SECONDARY `package.json` inside the
|
|
56132
|
+
* `dist/` output with the SAME `name` field as the real package root (e.g.
|
|
56133
|
+
* `ng-packagr`'s Angular `Package Format v9+` legacy-entry-point convention
|
|
56134
|
+
* emits `dist/package.json`). A name-only match would stop at that inner
|
|
56135
|
+
* duplicate — one level too shallow — and never reach the real package root
|
|
56136
|
+
* where `rozie-manifest.json` actually lives (only shipped at the root,
|
|
56137
|
+
* sibling to the AUTHORED `package.json`, never duplicated into `dist/`).
|
|
56138
|
+
* Continuing the walk past a name-matched-but-manifest-less directory finds
|
|
56139
|
+
* the correct root for every current `@rozie-ui/*-<target>` leaf shape.
|
|
56140
|
+
*/
|
|
56141
|
+
function findPackageRoot(entryFilePath, expectedName) {
|
|
56142
|
+
let dir = dirname(entryFilePath);
|
|
56143
|
+
for (let i = 0; i < 10; i++) {
|
|
56144
|
+
try {
|
|
56145
|
+
const raw = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
56146
|
+
if (typeof raw === "object" && raw !== null && raw.name === expectedName) try {
|
|
56147
|
+
readFileSync(join(dir, "rozie-manifest.json"), "utf-8");
|
|
56148
|
+
return dir;
|
|
56149
|
+
} catch {}
|
|
56150
|
+
} catch {}
|
|
56151
|
+
const parent = dirname(dir);
|
|
56152
|
+
if (parent === dir) return null;
|
|
56153
|
+
dir = parent;
|
|
56154
|
+
}
|
|
56155
|
+
return null;
|
|
56156
|
+
}
|
|
56157
|
+
function lookupFailed(pkgLabel) {
|
|
56158
|
+
return {
|
|
56159
|
+
surface: null,
|
|
56160
|
+
error: {
|
|
56161
|
+
code: RozieErrorCode.CROSS_PACKAGE_LOOKUP_FAILED,
|
|
56162
|
+
message: `Cannot locate a published manifest for '${pkgLabel}'. Verify the per-target package is installed as a dependency reachable from the consumer file and ships a rozie-manifest.json.`
|
|
56163
|
+
}
|
|
56164
|
+
};
|
|
56165
|
+
}
|
|
56166
|
+
/**
|
|
56167
|
+
* Derive the per-target package for a published `<components>` specifier,
|
|
56168
|
+
* locate its installed `rozie-manifest.json` via node module resolution, and
|
|
56169
|
+
* validate + deserialize it into a `ProducerSurface` (D-08/D-09/D-10/D-04).
|
|
56170
|
+
*
|
|
56171
|
+
* Never throws — every failure mode returns `{ surface: null, error }`.
|
|
56172
|
+
*/
|
|
56173
|
+
function resolveManifestProducer(args) {
|
|
56174
|
+
const { specifier, target, fromFile, resolver } = args;
|
|
56175
|
+
const basePkg = deriveBasePackageName(specifier);
|
|
56176
|
+
if (basePkg === null) return lookupFailed(specifier);
|
|
56177
|
+
const derivedPkg = `${basePkg}-${target}`;
|
|
56178
|
+
const entryFilePath = resolver.resolveProducerPath(derivedPkg, fromFile);
|
|
56179
|
+
if (entryFilePath === null) return lookupFailed(derivedPkg);
|
|
56180
|
+
const packageDir = findPackageRoot(entryFilePath, derivedPkg);
|
|
56181
|
+
if (packageDir === null) return lookupFailed(derivedPkg);
|
|
56182
|
+
let raw;
|
|
56183
|
+
try {
|
|
56184
|
+
raw = JSON.parse(readFileSync(join(packageDir, "rozie-manifest.json"), "utf-8"));
|
|
56185
|
+
} catch {
|
|
56186
|
+
return lookupFailed(derivedPkg);
|
|
56187
|
+
}
|
|
56188
|
+
return parseManifest(raw, { packageName: derivedPkg });
|
|
56189
|
+
}
|
|
56190
|
+
//#endregion
|
|
55811
56191
|
//#region ../core/src/ir/threadParamTypes.ts
|
|
55812
56192
|
/**
|
|
55813
56193
|
* Recursive template walker: visit every TemplateNode in source order.
|
|
@@ -55892,9 +56272,11 @@ function bodyContainsReprojection(body) {
|
|
|
55892
56272
|
* the cache reverse-dep edge + resolver `fromFile` argument)
|
|
55893
56273
|
* @param cache - per-compiler-instance IRCache (D-01)
|
|
55894
56274
|
* @param resolver - per-compiler-instance ProducerResolver (D-02 / D-12)
|
|
56275
|
+
* @param target - the per-call compile target (Phase 75 Plan 02 —
|
|
56276
|
+
* selects the derived per-target package for a PUBLISHED specifier; D-09)
|
|
55895
56277
|
* @param diagnostics - accumulator
|
|
55896
56278
|
*/
|
|
55897
|
-
function threadParamTypes(ir, consumerPath, cache, resolver, diagnostics) {
|
|
56279
|
+
function threadParamTypes(ir, consumerPath, cache, resolver, target, diagnostics) {
|
|
55898
56280
|
const declaredSlotNames = new Set(ir.slots.map((s) => s.name));
|
|
55899
56281
|
const { reprojections } = collectSlotInvocationsByContext(ir.template);
|
|
55900
56282
|
for (const reproj of reprojections) if (!declaredSlotNames.has(reproj.slotName)) diagnostics.push({
|
|
@@ -55914,6 +56296,26 @@ function threadParamTypes(ir, consumerPath, cache, resolver, diagnostics) {
|
|
|
55914
56296
|
if (node.tagKind === "self") {
|
|
55915
56297
|
producerSlots = ir.slots;
|
|
55916
56298
|
producerProps = ir.props;
|
|
56299
|
+
} else if (isPublishedSpecifier$1(node.componentRef.importPath)) {
|
|
56300
|
+
const { surface, error } = resolveManifestProducer({
|
|
56301
|
+
specifier: node.componentRef.importPath,
|
|
56302
|
+
target,
|
|
56303
|
+
fromFile: consumerPath,
|
|
56304
|
+
resolver
|
|
56305
|
+
});
|
|
56306
|
+
if (error !== null) {
|
|
56307
|
+
diagnostics.push({
|
|
56308
|
+
code: error.code,
|
|
56309
|
+
severity: "error",
|
|
56310
|
+
message: error.message,
|
|
56311
|
+
loc: node.componentRef.sourceLoc,
|
|
56312
|
+
hint: "Verify the per-target package is installed as a dependency and ships a compatible rozie-manifest.json."
|
|
56313
|
+
});
|
|
56314
|
+
return;
|
|
56315
|
+
}
|
|
56316
|
+
if (surface === null) return;
|
|
56317
|
+
producerSlots = surface.slots;
|
|
56318
|
+
producerProps = surface.props;
|
|
55917
56319
|
} else {
|
|
55918
56320
|
const resolvedPath = resolver.resolveProducerPath(node.componentRef.importPath, consumerPath);
|
|
55919
56321
|
if (resolvedPath === null) {
|
|
@@ -56118,9 +56520,11 @@ function walkTemplate(node, visit) {
|
|
|
56118
56520
|
* @param cache - per-compiler-instance IRCache (D-01) — shared with
|
|
56119
56521
|
* threadParamTypes so producers parsed once during threading are reused here
|
|
56120
56522
|
* @param resolver - per-compiler-instance ProducerResolver (D-02 / D-12)
|
|
56523
|
+
* @param target - the per-call compile target (Phase 75 Plan 02 —
|
|
56524
|
+
* selects the derived per-target package for a PUBLISHED specifier; D-09)
|
|
56121
56525
|
* @param diagnostics - accumulator (mutated in place; ROZ949/950/951/945 pushed)
|
|
56122
56526
|
*/
|
|
56123
|
-
function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics) {
|
|
56527
|
+
function validateTwoWayBindings(ir, consumerPath, cache, resolver, target, diagnostics) {
|
|
56124
56528
|
walkTemplate(ir.template, (node) => {
|
|
56125
56529
|
if (node.type !== "TemplateElement") return;
|
|
56126
56530
|
const twoWayAttrs = node.attributes.filter((a) => a.kind === "twoWayBinding");
|
|
@@ -56135,6 +56539,27 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56135
56539
|
return producerProps;
|
|
56136
56540
|
}
|
|
56137
56541
|
if (node.tagKind !== "component" || !node.componentRef) return null;
|
|
56542
|
+
if (isPublishedSpecifier$1(node.componentRef.importPath)) {
|
|
56543
|
+
const { surface, error } = resolveManifestProducer({
|
|
56544
|
+
specifier: node.componentRef.importPath,
|
|
56545
|
+
target,
|
|
56546
|
+
fromFile: consumerPath,
|
|
56547
|
+
resolver
|
|
56548
|
+
});
|
|
56549
|
+
if (error !== null) {
|
|
56550
|
+
diagnostics.push({
|
|
56551
|
+
code: error.code,
|
|
56552
|
+
severity: "error",
|
|
56553
|
+
message: error.message,
|
|
56554
|
+
loc: node.componentRef.sourceLoc,
|
|
56555
|
+
hint: "Verify the per-target package is installed as a dependency and ships a compatible rozie-manifest.json."
|
|
56556
|
+
});
|
|
56557
|
+
return null;
|
|
56558
|
+
}
|
|
56559
|
+
if (surface === null) return null;
|
|
56560
|
+
producerProps = surface.props;
|
|
56561
|
+
return producerProps;
|
|
56562
|
+
}
|
|
56138
56563
|
const resolvedPath = resolver.resolveProducerPath(node.componentRef.importPath, consumerPath);
|
|
56139
56564
|
if (resolvedPath === null) {
|
|
56140
56565
|
diagnostics.push({
|
|
@@ -56189,6 +56614,7 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56189
56614
|
if (producerProp === void 0) continue;
|
|
56190
56615
|
if (!producerProp.isModel) {
|
|
56191
56616
|
const producerImportPath = node.tagKind === "self" ? "(self-reference)" : node.componentRef?.importPath ?? "(unknown)";
|
|
56617
|
+
const relatedLoc = producerProp.sourceLoc ?? node.componentRef?.sourceLoc ?? attr.sourceLoc;
|
|
56192
56618
|
diagnostics.push({
|
|
56193
56619
|
code: RozieErrorCode.TWO_WAY_PROP_NOT_MODEL,
|
|
56194
56620
|
severity: "error",
|
|
@@ -56197,7 +56623,7 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56197
56623
|
hint: `Add 'model: true' to the '${attr.name}' prop declaration on ${producerImportPath}, or pass a one-way value via :${attr.name}="…" instead.`,
|
|
56198
56624
|
related: [{
|
|
56199
56625
|
message: `'${attr.name}' declared here without model: true`,
|
|
56200
|
-
loc:
|
|
56626
|
+
loc: relatedLoc
|
|
56201
56627
|
}]
|
|
56202
56628
|
});
|
|
56203
56629
|
continue;
|
|
@@ -56508,10 +56934,37 @@ const TARGET_EXT_MAP = {
|
|
|
56508
56934
|
lit: ""
|
|
56509
56935
|
};
|
|
56510
56936
|
const ROZIE_EXT$1 = ".rozie";
|
|
56937
|
+
/** Tsconfig-alias-shaped prefix (empty npm scope, e.g. `@/components/Modal.rozie`). */
|
|
56938
|
+
const TSCONFIG_ALIAS_PREFIX = "@/";
|
|
56939
|
+
/**
|
|
56940
|
+
* True iff `importPath` is a PUBLISHED cross-package `<components>`
|
|
56941
|
+
* specifier (D-08 shape): ends in `.rozie`, is neither a relative/absolute
|
|
56942
|
+
* path nor a tsconfig `paths`-alias-shaped specifier. Mirrors
|
|
56943
|
+
* `resolveManifestProducer.ts`'s `isPublishedSpecifier` (kept duplicated,
|
|
56944
|
+
* not imported, so this module stays dependency-free — both are tiny, pure,
|
|
56945
|
+
* syntactic classifiers over the same D-08 specifier shape and must be kept
|
|
56946
|
+
* in lockstep if that shape ever changes).
|
|
56947
|
+
*
|
|
56948
|
+
* Exported so callers that do NOT already route an import path through
|
|
56949
|
+
* `rewriteRozieImport` (e.g. the Lit emitter's side-effect/type-only
|
|
56950
|
+
* composed-component imports, which deliberately leave a LOCAL `.rozie`
|
|
56951
|
+
* specifier verbatim for the unplugin-consumer-coexist flow) can still
|
|
56952
|
+
* gate a published-specifier rewrite without duplicating this check.
|
|
56953
|
+
*/
|
|
56954
|
+
function isPublishedSpecifier(importPath) {
|
|
56955
|
+
if (!importPath.endsWith(ROZIE_EXT$1)) return false;
|
|
56956
|
+
if (importPath.startsWith(".") || importPath.startsWith("/")) return false;
|
|
56957
|
+
if (importPath.startsWith(TSCONFIG_ALIAS_PREFIX)) return false;
|
|
56958
|
+
return true;
|
|
56959
|
+
}
|
|
56511
56960
|
function rewriteRozieImport(importPath, target) {
|
|
56512
56961
|
if (!importPath.endsWith(ROZIE_EXT$1)) return importPath;
|
|
56513
56962
|
const targetExt = TARGET_EXT_MAP[target];
|
|
56514
56963
|
if (targetExt === void 0) throw new Error(`rewriteRozieImport: unknown target '${target}' — expected one of vue|react|svelte|angular|solid|lit`);
|
|
56964
|
+
if (isPublishedSpecifier(importPath)) {
|
|
56965
|
+
const lastSlash = importPath.lastIndexOf("/");
|
|
56966
|
+
if (lastSlash > 0) return `${importPath.slice(0, lastSlash)}-${target}`;
|
|
56967
|
+
}
|
|
56515
56968
|
return importPath.slice(0, -6) + targetExt;
|
|
56516
56969
|
}
|
|
56517
56970
|
//#endregion
|
|
@@ -56903,7 +57356,6 @@ function deconflictVueRefSuffix(ir, topLevelBindingNames) {
|
|
|
56903
57356
|
}
|
|
56904
57357
|
//#endregion
|
|
56905
57358
|
//#region ../core/src/codegen/buildPropJsdoc.ts
|
|
56906
|
-
var import_lib = /* @__PURE__ */ __toESM(require_lib$6(), 1);
|
|
56907
57359
|
/**
|
|
56908
57360
|
* Neutralize the JSDoc comment terminator inside an author-controlled string so
|
|
56909
57361
|
* it cannot prematurely close the emitted comment block (T-58-04). Replaces each
|
|
@@ -57795,6 +58247,22 @@ function renderComputedArrow$3(body, cast) {
|
|
|
57795
58247
|
* `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
|
|
57796
58248
|
* mirrored trailing copy spaces correctly.
|
|
57797
58249
|
*/
|
|
58250
|
+
/**
|
|
58251
|
+
* Quick task 260714-orv — compare two Babel comment nodes by SOURCE POSITION
|
|
58252
|
+
* (type + starting line/column) rather than object reference. A comment
|
|
58253
|
+
* shared between two adjacent statements' trailing/leading arrays sometimes
|
|
58254
|
+
* IS the exact same object, but an earlier pipeline pass (hoistModuleLet /
|
|
58255
|
+
* rewriteRozieIdentifiers / etc.) may clone an individual statement, minting
|
|
58256
|
+
* a content-identical comment object at a different reference. Two distinct,
|
|
58257
|
+
* independently-authored comments can never share a starting source
|
|
58258
|
+
* position, so this is a safe substitute for `===` this late in the
|
|
58259
|
+
* pipeline.
|
|
58260
|
+
*/
|
|
58261
|
+
function isSameSourceComment$1(a, b) {
|
|
58262
|
+
if (a === b) return true;
|
|
58263
|
+
if (!a.loc || !b.loc) return false;
|
|
58264
|
+
return a.type === b.type && a.loc.start.line === b.loc.start.line && a.loc.start.column === b.loc.start.column;
|
|
58265
|
+
}
|
|
57798
58266
|
function mirrorSpliceBoundaryComments$2(stmts) {
|
|
57799
58267
|
for (let i = 1; i < stmts.length; i++) {
|
|
57800
58268
|
const cur = stmts[i];
|
|
@@ -57803,7 +58271,15 @@ function mirrorSpliceBoundaryComments$2(stmts) {
|
|
|
57803
58271
|
const prevExtra = prev.extra;
|
|
57804
58272
|
const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
|
|
57805
58273
|
const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
|
|
57806
|
-
if (!curSpliced && !prevSpliced)
|
|
58274
|
+
if (!curSpliced && !prevSpliced) {
|
|
58275
|
+
const prevTrail = prev.trailingComments;
|
|
58276
|
+
const curLead = cur.leadingComments;
|
|
58277
|
+
if (prevTrail && prevTrail.length > 0 && curLead && curLead.length > 0) {
|
|
58278
|
+
const deduped = prevTrail.filter((c) => !curLead.some((lc) => isSameSourceComment$1(c, lc)));
|
|
58279
|
+
prev.trailingComments = deduped.length > 0 ? deduped : null;
|
|
58280
|
+
}
|
|
58281
|
+
continue;
|
|
58282
|
+
}
|
|
57807
58283
|
const lead = cur.leadingComments;
|
|
57808
58284
|
const prevTrail = prev.trailingComments;
|
|
57809
58285
|
if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
|
|
@@ -59914,7 +60390,61 @@ function emitElement$5(node, ctx) {
|
|
|
59914
60390
|
* (`<template #header="{ close }">…</template>`); default-shorthand
|
|
59915
60391
|
* (`{ name: '' }`) emits as bare children inside the component tag.
|
|
59916
60392
|
*/
|
|
60393
|
+
/**
|
|
60394
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
60395
|
+
* teleport. Wraps the element in Vue's NATIVE `<Teleport :to :disabled>`
|
|
60396
|
+
* component — authors cannot write `<Teleport>` directly (ROZ926 rejects
|
|
60397
|
+
* it), but the EMITTER may synthesize it as the lowering target for
|
|
60398
|
+
* `r-portal` (ROZ926 gates author input, not emit output). `<Teleport>`
|
|
60399
|
+
* tolerates a `v-if`/`v-for` child, so `extraDirective` (the r-if/r-for
|
|
60400
|
+
* directive the parent conditional/loop walker wants to inject) is applied
|
|
60401
|
+
* to the INNER element, not the `<Teleport>` wrapper — the wrapper is
|
|
60402
|
+
* unconditional; when `disabled` the child's own `v-if` still governs
|
|
60403
|
+
* whether anything renders at all.
|
|
60404
|
+
*
|
|
60405
|
+
* `:disabled="!(<expr>)"` — a falsy container renders in place (Vue's
|
|
60406
|
+
* Teleport skips target resolution/warnings entirely when disabled; see
|
|
60407
|
+
* `resolveTarget`/`mountToTarget` in @vue/runtime-core — no console warning,
|
|
60408
|
+
* no throw, for a falsy `to` when `disabled` is true).
|
|
60409
|
+
*
|
|
60410
|
+
* Finding 4 (R2) — the container expression is HOISTED into a single Vue
|
|
60411
|
+
* `computed` (the same D-04 hoist pattern `r-match` uses) instead of being
|
|
60412
|
+
* spliced verbatim into both `:to` and `:disabled`. This fixes three
|
|
60413
|
+
* defects at once:
|
|
60414
|
+
* 1. SSR guard — the computed body wraps the expression in the SAME
|
|
60415
|
+
* `typeof document === 'undefined' ? null : (...)` guard the React and
|
|
60416
|
+
* Solid targets emit, so a container expression that touches `document`
|
|
60417
|
+
* (e.g. `document.querySelector(...)`) is null on the server rather
|
|
60418
|
+
* than throwing; a null container makes `:disabled` truthy → renders
|
|
60419
|
+
* in place (parity with React's ternary / Solid's <Show> fallback).
|
|
60420
|
+
* 2. single evaluation — a Vue computed caches, so a side-effecting
|
|
60421
|
+
* selector runs once per render, not twice (`:to` + `:disabled`).
|
|
60422
|
+
* 3. escaping — the raw expression now lives in the script (where
|
|
60423
|
+
* @babel/generator escapes JS strings), and `:to`/`:disabled` bind a
|
|
60424
|
+
* bare computed identifier, so an expression containing a `"` can no
|
|
60425
|
+
* longer break the double-quoted attribute (obviating a per-attribute
|
|
60426
|
+
* escapeAttrValue call at the interpolation site).
|
|
60427
|
+
*/
|
|
60428
|
+
function emitPortalElement$1(node, extraDirective, ctx) {
|
|
60429
|
+
const { expression } = node.portalTo;
|
|
60430
|
+
if (ctx.elementPortalFlag) ctx.elementPortalFlag.seen = true;
|
|
60431
|
+
const containerCode = rewriteTemplateExpression$5(expression, ctx.ir);
|
|
60432
|
+
const hoistName = `__roziePortalTo${ctx.injectionCounter.next++}`;
|
|
60433
|
+
ctx.scriptInjections.push({
|
|
60434
|
+
wrapName: hoistName,
|
|
60435
|
+
import: {
|
|
60436
|
+
from: "vue",
|
|
60437
|
+
name: "computed"
|
|
60438
|
+
},
|
|
60439
|
+
decl: `const ${hoistName} = computed(() => (typeof document === 'undefined' ? null : (${containerCode})));`
|
|
60440
|
+
});
|
|
60441
|
+
return `<Teleport :to="${hoistName}" :disabled="!${hoistName}">${emitElementWithExtraDirectiveInner(node, extraDirective, ctx)}</Teleport>`;
|
|
60442
|
+
}
|
|
59917
60443
|
function emitElementWithExtraDirective(origNode, extraDirective, ctx) {
|
|
60444
|
+
if (origNode.portalTo) return emitPortalElement$1(origNode, extraDirective, ctx);
|
|
60445
|
+
return emitElementWithExtraDirectiveInner(origNode, extraDirective, ctx);
|
|
60446
|
+
}
|
|
60447
|
+
function emitElementWithExtraDirectiveInner(origNode, extraDirective, ctx) {
|
|
59918
60448
|
const node = stripKeynavCommitEvent$5(origNode);
|
|
59919
60449
|
const rHtml = findRHtml$2(node.attributes);
|
|
59920
60450
|
const attrText = emitMergedAttributes(rHtml ? node.attributes.filter((a) => !(a.kind === "binding" && a.name === "r-html")) : node.attributes, {
|
|
@@ -65011,6 +65541,20 @@ function genBlockInner(block, indent) {
|
|
|
65011
65541
|
}).join("\n");
|
|
65012
65542
|
}
|
|
65013
65543
|
/**
|
|
65544
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
65545
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
65546
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
65547
|
+
* later import's `leadingComments`) prints exactly once. Mirrors
|
|
65548
|
+
* `genBlockInner`'s single-pass precedent — generating imports one at a time
|
|
65549
|
+
* (`nodes.map(genCode).join`) gives each import its OWN comment-dedup set,
|
|
65550
|
+
* doubling any shared comment. `t.program(nodes)` prints only its body
|
|
65551
|
+
* statements (no wrapping braces/`File` boilerplate), so non-comment cases
|
|
65552
|
+
* stay byte-identical.
|
|
65553
|
+
*/
|
|
65554
|
+
function genImportsBlock$2(nodes) {
|
|
65555
|
+
return generate$21(t$3.program(nodes), GEN_OPTS$19).code;
|
|
65556
|
+
}
|
|
65557
|
+
/**
|
|
65014
65558
|
* Phase 14.1 follow-up — does any element in the IR template carry a
|
|
65015
65559
|
* `spreadBinding` attribute? The Plan 14-05 gate at `emitScript` only
|
|
65016
65560
|
* synthesises `const attrs = ...` (the source for the `$attrs` rewrite at
|
|
@@ -65862,7 +66406,7 @@ function emitScript$4(ir, collectors, opts = {}) {
|
|
|
65862
66406
|
const cloned = cloneScriptProgram$4(ir.setupBody.scriptProgram);
|
|
65863
66407
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts } = partitionUserImports$3(cloned);
|
|
65864
66408
|
cloned.program.body = bodyStmts;
|
|
65865
|
-
const userImports = userImportNodes.length > 0 ?
|
|
66409
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock$2(userImportNodes) + "\n" : "";
|
|
65866
66410
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => genCode$7(decl));
|
|
65867
66411
|
deconflictDeclareThenAssignRef(cloned, ir);
|
|
65868
66412
|
const hoistResult = hoistModuleLet(cloned, ir);
|
|
@@ -69246,7 +69790,42 @@ function scopeAttrForElement$1(node, ctx) {
|
|
|
69246
69790
|
* declarations are hoisted within their containing scope). Both emit the tag
|
|
69247
69791
|
* verbatim PascalCase below; no template AST rewrite needed.
|
|
69248
69792
|
*/
|
|
69793
|
+
/**
|
|
69794
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
69795
|
+
* teleport. Wraps the normal element emit (`emitElementInner`) in a
|
|
69796
|
+
* `container ? createPortal(tree, container) : tree` check, using React's
|
|
69797
|
+
* native `createPortal` (react-dom) — NEVER the P33 `emitPortals.ts`
|
|
69798
|
+
* createRoot-into-container machinery (that is the INVERSE direction: slot-
|
|
69799
|
+
* content mounted INTO an engine-owned container, not a component's own
|
|
69800
|
+
* subtree relocated OUT).
|
|
69801
|
+
*
|
|
69802
|
+
* SSR guard: the container expression is evaluated ONLY when `document`
|
|
69803
|
+
* exists — some author expressions call `document.querySelector` internally
|
|
69804
|
+
* (see `resolveAppendTo`/`resolveTo` in the authoring convention), so simply
|
|
69805
|
+
* checking the RESULT for null is not enough; the whole resolution must be
|
|
69806
|
+
* skipped on the server. Falsy/null container falls back to rendering the
|
|
69807
|
+
* subtree in place — this is what makes `appendTo:false` byte-behavior-
|
|
69808
|
+
* identical to no directive at emit time (the lower-time field is always
|
|
69809
|
+
* present per Task 1; this is the runtime decision point).
|
|
69810
|
+
*
|
|
69811
|
+
* Returns a `{...}` JSX-expression-container mustache — valid directly in
|
|
69812
|
+
* JSX children position, and `renderBranchBody` (emitConditional.ts) strips
|
|
69813
|
+
* the outer braces via `stripBalancedMustache` when this is a single-child
|
|
69814
|
+
* r-if branch body, so `r-portal` + `r-if` on the same element compose for
|
|
69815
|
+
* free with zero special-casing in the conditional emitter.
|
|
69816
|
+
*/
|
|
69817
|
+
function emitPortalElement(node, ctx) {
|
|
69818
|
+
const { expression } = node.portalTo;
|
|
69819
|
+
if (ctx.elementPortalFlag) ctx.elementPortalFlag.seen = true;
|
|
69820
|
+
const containerCode = rewriteTemplateExpression$4(expression, ctx.ir);
|
|
69821
|
+
const treeJsx = emitElementInner$4(node, ctx);
|
|
69822
|
+
return `{(() => { const __rozieContainer = typeof document === 'undefined' ? null : (${containerCode}); return __rozieContainer ? createPortal(${treeJsx}, __rozieContainer) : (${treeJsx}); })()}`;
|
|
69823
|
+
}
|
|
69249
69824
|
function emitElement$4(origNode, ctx) {
|
|
69825
|
+
if (origNode.portalTo) return emitPortalElement(origNode, ctx);
|
|
69826
|
+
return emitElementInner$4(origNode, ctx);
|
|
69827
|
+
}
|
|
69828
|
+
function emitElementInner$4(origNode, ctx) {
|
|
69250
69829
|
let node = stripKeynavCommitEvent$4(origNode);
|
|
69251
69830
|
const pendingKey = ctx.pendingKey ?? null;
|
|
69252
69831
|
const childCtx = {
|
|
@@ -69796,9 +70375,11 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69796
70375
|
scriptInjections: [],
|
|
69797
70376
|
slotPropFields: slotResult.slotPropFields,
|
|
69798
70377
|
slotCtxInterfaces: slotResult.slotCtxInterfaces,
|
|
69799
|
-
diagnostics: []
|
|
70378
|
+
diagnostics: [],
|
|
70379
|
+
hasElementPortal: false
|
|
69800
70380
|
};
|
|
69801
70381
|
const keynav = resolveKeynavPlan$4(ir);
|
|
70382
|
+
const elementPortalFlag = { seen: false };
|
|
69802
70383
|
const ctx = {
|
|
69803
70384
|
ir,
|
|
69804
70385
|
collectors,
|
|
@@ -69807,7 +70388,8 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69807
70388
|
scriptInjections,
|
|
69808
70389
|
injectionCounter: { next: 0 },
|
|
69809
70390
|
...opts.scopeAttr !== void 0 ? { scopeAttr: opts.scopeAttr } : {},
|
|
69810
|
-
keynav
|
|
70391
|
+
keynav,
|
|
70392
|
+
elementPortalFlag
|
|
69811
70393
|
};
|
|
69812
70394
|
const jsx = emitNode$4(ir.template, ctx);
|
|
69813
70395
|
if (keynav !== null) scriptInjections.push(...buildKeynavScriptInjections$2(keynav, ir, collectors));
|
|
@@ -69816,7 +70398,8 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69816
70398
|
scriptInjections,
|
|
69817
70399
|
slotPropFields: slotResult.slotPropFields,
|
|
69818
70400
|
slotCtxInterfaces: slotResult.slotCtxInterfaces,
|
|
69819
|
-
diagnostics
|
|
70401
|
+
diagnostics,
|
|
70402
|
+
hasElementPortal: elementPortalFlag.seen
|
|
69820
70403
|
};
|
|
69821
70404
|
}
|
|
69822
70405
|
//#endregion
|
|
@@ -73736,11 +74319,11 @@ function emitReact(ir, opts = {}) {
|
|
|
73736
74319
|
runtime: runtimeImports,
|
|
73737
74320
|
filename: opts.filename
|
|
73738
74321
|
}, scriptOpts);
|
|
73739
|
-
const portalImport = hasPortals ? "import { createRoot, type Root } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\n" : "";
|
|
73740
74322
|
const tmpl = emitTemplate$4(ir, {
|
|
73741
74323
|
react: reactImports,
|
|
73742
74324
|
runtime: runtimeImports
|
|
73743
74325
|
}, registry, { scopeAttr });
|
|
74326
|
+
const portalImport = (hasPortals ? "import { createRoot, type Root } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\n" : "") + (tmpl.hasElementPortal ? "import { createPortal } from 'react-dom';\n" : "");
|
|
73744
74327
|
const listeners = emitListeners$4(ir, {
|
|
73745
74328
|
react: reactImports,
|
|
73746
74329
|
runtime: runtimeImports
|
|
@@ -73941,11 +74524,11 @@ function renderPropType(ann) {
|
|
|
73941
74524
|
case "Boolean": return "boolean";
|
|
73942
74525
|
case "Array": return "unknown[]";
|
|
73943
74526
|
case "Object": return "Record<string, unknown>";
|
|
73944
|
-
case "Function": return "(...args:
|
|
74527
|
+
case "Function": return "(...args: any[]) => any";
|
|
73945
74528
|
default: return ann.name;
|
|
73946
74529
|
}
|
|
73947
74530
|
if (ann.kind === "literal") switch (ann.value) {
|
|
73948
|
-
case "function": return "(...args:
|
|
74531
|
+
case "function": return "(...args: any[]) => any";
|
|
73949
74532
|
case "object": return "Record<string, unknown>";
|
|
73950
74533
|
case "array": return "unknown[]";
|
|
73951
74534
|
case "string": return "string";
|
|
@@ -75231,6 +75814,22 @@ function arrowBody$2(body) {
|
|
|
75231
75814
|
* `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
|
|
75232
75815
|
* mirrored trailing copy spaces correctly.
|
|
75233
75816
|
*/
|
|
75817
|
+
/**
|
|
75818
|
+
* Quick task 260714-orv — compare two Babel comment nodes by SOURCE POSITION
|
|
75819
|
+
* (type + starting line/column) rather than object reference. A comment
|
|
75820
|
+
* shared between two adjacent statements' trailing/leading arrays sometimes
|
|
75821
|
+
* IS the exact same object, but an earlier pipeline pass (hoistModuleLet /
|
|
75822
|
+
* rewriteRozieIdentifiers / etc.) may clone an individual statement, minting
|
|
75823
|
+
* a content-identical comment object at a different reference. Two distinct,
|
|
75824
|
+
* independently-authored comments can never share a starting source
|
|
75825
|
+
* position, so this is a safe substitute for `===` this late in the
|
|
75826
|
+
* pipeline.
|
|
75827
|
+
*/
|
|
75828
|
+
function isSameSourceComment(a, b) {
|
|
75829
|
+
if (a === b) return true;
|
|
75830
|
+
if (!a.loc || !b.loc) return false;
|
|
75831
|
+
return a.type === b.type && a.loc.start.line === b.loc.start.line && a.loc.start.column === b.loc.start.column;
|
|
75832
|
+
}
|
|
75234
75833
|
function mirrorSpliceBoundaryComments$1(stmts) {
|
|
75235
75834
|
for (let i = 1; i < stmts.length; i++) {
|
|
75236
75835
|
const cur = stmts[i];
|
|
@@ -75239,7 +75838,15 @@ function mirrorSpliceBoundaryComments$1(stmts) {
|
|
|
75239
75838
|
const prevExtra = prev.extra;
|
|
75240
75839
|
const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
|
|
75241
75840
|
const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
|
|
75242
|
-
if (!curSpliced && !prevSpliced)
|
|
75841
|
+
if (!curSpliced && !prevSpliced) {
|
|
75842
|
+
const prevTrail = prev.trailingComments;
|
|
75843
|
+
const curLead = cur.leadingComments;
|
|
75844
|
+
if (prevTrail && prevTrail.length > 0 && curLead && curLead.length > 0) {
|
|
75845
|
+
const deduped = prevTrail.filter((c) => !curLead.some((lc) => isSameSourceComment(c, lc)));
|
|
75846
|
+
prev.trailingComments = deduped.length > 0 ? deduped : null;
|
|
75847
|
+
}
|
|
75848
|
+
continue;
|
|
75849
|
+
}
|
|
75243
75850
|
const lead = cur.leadingComments;
|
|
75244
75851
|
const prevTrail = prev.trailingComments;
|
|
75245
75852
|
if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
|
|
@@ -77775,6 +78382,11 @@ function emitElementInner$3(origNode, ctx) {
|
|
|
77775
78382
|
hasFallthroughSpread = true;
|
|
77776
78383
|
break;
|
|
77777
78384
|
}
|
|
78385
|
+
let portalAttr = null;
|
|
78386
|
+
if (node.portalTo) {
|
|
78387
|
+
ctx.runtimeImports.add("roziePortal");
|
|
78388
|
+
portalAttr = `use:roziePortal={${rewriteTemplateExpression$3(node.portalTo.expression, ctx.ir, ctxRenames(ctx))}}`;
|
|
78389
|
+
}
|
|
77778
78390
|
const attrText = emitAttributes$2(node.attributes, {
|
|
77779
78391
|
ir: ctx.ir,
|
|
77780
78392
|
elementTagKind: node.tagKind,
|
|
@@ -77803,6 +78415,7 @@ function emitElementInner$3(origNode, ctx) {
|
|
|
77803
78415
|
if (eventText) partsHead.push(eventText);
|
|
77804
78416
|
for (const sp of spreadTexts) partsHead.push(sp);
|
|
77805
78417
|
for (const ka of keynavAttrs) partsHead.push(ka);
|
|
78418
|
+
if (portalAttr) partsHead.push(portalAttr);
|
|
77806
78419
|
if (ctx.scopeAttr.length > 0) partsHead.push(ctx.scopeAttr);
|
|
77807
78420
|
const head = partsHead.length > 0 ? " " + partsHead.join(" ") : "";
|
|
77808
78421
|
if (rHtml !== null) {
|
|
@@ -79534,6 +80147,88 @@ function hoistDoubleReadAccessors(program) {
|
|
|
79534
80147
|
}
|
|
79535
80148
|
visit(program.program);
|
|
79536
80149
|
}
|
|
80150
|
+
/** True for `$props.X` where X is in `polymorphicModelProps`. */
|
|
80151
|
+
function isPolymorphicModelRead$1(node, polymorphicModelProps) {
|
|
80152
|
+
return t$3.isMemberExpression(node) && !node.computed && t$3.isIdentifier(node.object) && node.object.name === "$props" && t$3.isIdentifier(node.property) && polymorphicModelProps.has(node.property.name);
|
|
80153
|
+
}
|
|
80154
|
+
/**
|
|
80155
|
+
* When `test` is `typeof $props.X === '<lit>'` (or `!==`) or `'<lit>' in
|
|
80156
|
+
* $props.X`, for X in `polymorphicModelProps`, returns X. Otherwise null.
|
|
80157
|
+
*/
|
|
80158
|
+
function matchesPolymorphicModelGuard$1(test, polymorphicModelProps) {
|
|
80159
|
+
if (t$3.isLogicalExpression(test)) return matchesPolymorphicModelGuard$1(test.left, polymorphicModelProps) ?? matchesPolymorphicModelGuard$1(test.right, polymorphicModelProps);
|
|
80160
|
+
if (!t$3.isBinaryExpression(test)) return null;
|
|
80161
|
+
const { operator, left, right } = test;
|
|
80162
|
+
if (operator === "===" || operator === "!==") {
|
|
80163
|
+
if (t$3.isUnaryExpression(left) && left.operator === "typeof" && isPolymorphicModelRead$1(left.argument, polymorphicModelProps) && t$3.isStringLiteral(right)) return left.argument.property.name;
|
|
80164
|
+
if (t$3.isUnaryExpression(right) && right.operator === "typeof" && isPolymorphicModelRead$1(right.argument, polymorphicModelProps) && t$3.isStringLiteral(left)) return right.argument.property.name;
|
|
80165
|
+
return null;
|
|
80166
|
+
}
|
|
80167
|
+
if (operator === "in" && isPolymorphicModelRead$1(right, polymorphicModelProps)) return right.property.name;
|
|
80168
|
+
return null;
|
|
80169
|
+
}
|
|
80170
|
+
/**
|
|
80171
|
+
* Quick task 260711-v2l (project_angular_typeof_narrow_gap) — port of Solid's
|
|
80172
|
+
* `hoistPolymorphicModelGuards` (emitter-hardening backlog item #11,
|
|
80173
|
+
* `project_solid_polymorphic_model_typeof_narrow_gap`, 73-02 Task 2) to the
|
|
80174
|
+
* Angular target. `$props.X` (a union or `unknown`-typed `model: true` prop)
|
|
80175
|
+
* lowers to an Angular signal accessor CALL (`this.X()`); TS does not narrow a
|
|
80176
|
+
* `typeof`/`in` guard across two SEPARATE calls to that accessor the way it
|
|
80177
|
+
* narrows a plain variable/property read. When a `ConditionalExpression`
|
|
80178
|
+
* guards on `typeof $props.X === '<lit>'` / `'<lit>' in $props.X` (X a
|
|
80179
|
+
* polymorphic/unknown model prop) AND re-reads `$props.X` again inside the
|
|
80180
|
+
* conditional, hoist ONE local binding before the guard and route every
|
|
80181
|
+
* `$props.X` read inside the conditional through it — mirroring the hand-
|
|
80182
|
+
* authored `const v = $props.value; return typeof v === 'string' ? v : ''`
|
|
80183
|
+
* workaround this replaces (DatePicker.rozie, commit bf3766b5).
|
|
80184
|
+
*
|
|
80185
|
+
* Gated to two known-safe shapes so this never mis-hoists into the wrong scope:
|
|
80186
|
+
* 1. the conditional IS an arrow function's own concise body — converted to
|
|
80187
|
+
* a block body wrapping `const v = ...; return <conditional>;`
|
|
80188
|
+
* 2. the conditional sits inside a statement that lives in a statement LIST
|
|
80189
|
+
* (block body, switch-case consequent, program body) — including
|
|
80190
|
+
* statements nested inside control-flow blocks (`if`/`for`/`while`/
|
|
80191
|
+
* `switch`/`try`). The local is inserted right before that statement, in
|
|
80192
|
+
* the same block as the guarded reads (Spike-012 BUG-4).
|
|
80193
|
+
* A conditional not in either shape (e.g. a bare single-statement `if (x)
|
|
80194
|
+
* return …;` with no block) is left alone (falsify-to-no-op, never a wrong fix).
|
|
80195
|
+
*
|
|
80196
|
+
* Runs on the raw (pre-$props-rewrite) Program, before the main
|
|
80197
|
+
* `rewriteRozieIdentifiers` traversal — the injected `$props.X` reference is
|
|
80198
|
+
* left for that later pass to lower to the normal `this.X()` accessor call
|
|
80199
|
+
* (identical downstream lowering to the DatePicker hand-authored workaround
|
|
80200
|
+
* this replaces).
|
|
80201
|
+
*/
|
|
80202
|
+
function hoistPolymorphicModelGuards$1(cloned, polymorphicModelProps) {
|
|
80203
|
+
if (polymorphicModelProps.size === 0) return;
|
|
80204
|
+
traverse$8(cloned, { ConditionalExpression(path) {
|
|
80205
|
+
const propName = matchesPolymorphicModelGuard$1(path.node.test, polymorphicModelProps);
|
|
80206
|
+
if (!propName) return;
|
|
80207
|
+
let occurrences = 0;
|
|
80208
|
+
path.traverse({ MemberExpression(inner) {
|
|
80209
|
+
if (isPolymorphicModelRead$1(inner.node, polymorphicModelProps) && inner.node.property.name === propName) occurrences++;
|
|
80210
|
+
} });
|
|
80211
|
+
if (occurrences < 2) return;
|
|
80212
|
+
const parentPath = path.parentPath;
|
|
80213
|
+
const isConciseArrowBody = parentPath.isArrowFunctionExpression() && parentPath.node.body === path.node;
|
|
80214
|
+
const stmtPath = path.getStatementParent();
|
|
80215
|
+
const canInsertBeforeStmt = !!stmtPath && stmtPath.inList;
|
|
80216
|
+
if (!isConciseArrowBody && !canInsertBeforeStmt) return;
|
|
80217
|
+
const localName = path.scope.hasBinding("v") ? path.scope.generateUidIdentifier("v").name : "v";
|
|
80218
|
+
path.traverse({ MemberExpression(inner) {
|
|
80219
|
+
if (isPolymorphicModelRead$1(inner.node, polymorphicModelProps) && inner.node.property.name === propName) {
|
|
80220
|
+
inner.replaceWith(t$3.identifier(localName));
|
|
80221
|
+
inner.skip();
|
|
80222
|
+
}
|
|
80223
|
+
} });
|
|
80224
|
+
const varDecl = t$3.variableDeclaration("const", [t$3.variableDeclarator(t$3.identifier(localName), t$3.memberExpression(t$3.identifier("$props"), t$3.identifier(propName)))]);
|
|
80225
|
+
if (isConciseArrowBody) {
|
|
80226
|
+
parentPath.node.body = t$3.blockStatement([varDecl, t$3.returnStatement(path.node)]);
|
|
80227
|
+
return;
|
|
80228
|
+
}
|
|
80229
|
+
stmtPath.insertBefore(varDecl);
|
|
80230
|
+
} });
|
|
80231
|
+
}
|
|
79537
80232
|
/**
|
|
79538
80233
|
* Rewrite Rozie magic-accessor identifiers in-place on a cloned Program.
|
|
79539
80234
|
*
|
|
@@ -81751,6 +82446,19 @@ function genCode$3(node) {
|
|
|
81751
82446
|
return generate$9(node, GEN_OPTS$8).code;
|
|
81752
82447
|
}
|
|
81753
82448
|
/**
|
|
82449
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
82450
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
82451
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
82452
|
+
* later import's `leadingComments`) prints exactly once. Generating imports
|
|
82453
|
+
* one at a time (`nodes.map(genCode).join`) gives each import its OWN
|
|
82454
|
+
* comment-dedup set, doubling any shared comment. `t.program(nodes)` prints
|
|
82455
|
+
* only its body statements (no wrapping braces/`File` boilerplate), so
|
|
82456
|
+
* non-comment cases stay byte-identical.
|
|
82457
|
+
*/
|
|
82458
|
+
function genImportsBlock$1(nodes) {
|
|
82459
|
+
return generate$9(t$3.program(nodes), GEN_OPTS$8).code;
|
|
82460
|
+
}
|
|
82461
|
+
/**
|
|
81754
82462
|
* Emit `() => body` for an Expression or BlockStatement body.
|
|
81755
82463
|
*
|
|
81756
82464
|
* Building the arrow as a Babel node (rather than string-templating
|
|
@@ -81833,7 +82541,7 @@ function renderType$2(ann) {
|
|
|
81833
82541
|
case "Boolean": return "boolean";
|
|
81834
82542
|
case "Array": return "any[]";
|
|
81835
82543
|
case "Object": return "Record<string, any>";
|
|
81836
|
-
case "Function": return "(...args:
|
|
82544
|
+
case "Function": return "(...args: any[]) => any";
|
|
81837
82545
|
default: return ann.name;
|
|
81838
82546
|
}
|
|
81839
82547
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -81843,7 +82551,7 @@ function renderType$2(ann) {
|
|
|
81843
82551
|
if (ann.kind === "literal") {
|
|
81844
82552
|
if (ann.value === "array") return "any[]";
|
|
81845
82553
|
if (ann.value === "object") return "Record<string, any>";
|
|
81846
|
-
if (ann.value === "function") return "(...args:
|
|
82554
|
+
if (ann.value === "function") return "(...args: any[]) => any";
|
|
81847
82555
|
return ann.value;
|
|
81848
82556
|
}
|
|
81849
82557
|
return "unknown";
|
|
@@ -82158,8 +82866,9 @@ function emitScript$2(ir, opts = {}) {
|
|
|
82158
82866
|
deconflictReservedImportBindings(cloned, new Set([...ir.props.map((p) => p.name), ...reservedClassMembers("angular", { singleModel: importSingleModel })]));
|
|
82159
82867
|
const { userImports: userImportNodes, hoistedTypeDecls, bodyStmts, valueImportNames } = partitionUserImports$2(cloned);
|
|
82160
82868
|
cloned.program.body = bodyStmts;
|
|
82161
|
-
const userImports = userImportNodes.length > 0 ?
|
|
82869
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock$1(userImportNodes) + "\n" : "";
|
|
82162
82870
|
normalizeModelAccessor$2(cloned);
|
|
82871
|
+
hoistPolymorphicModelGuards$1(cloned, new Set(ir.props.filter((p) => p.isModel && (p.typeAnnotation.kind === "union" || renderType$2(p.typeAnnotation) === "unknown")).map((p) => p.name)));
|
|
82163
82872
|
hoistDoubleReadAccessors(cloned);
|
|
82164
82873
|
const lifecyclePairing = pairClonedLifecycle(cloned, ir);
|
|
82165
82874
|
const emitsWithPayload = collectEmitsWithPayload(cloned);
|
|
@@ -82797,6 +83506,42 @@ function shouldWrapAttrBinding$1(name, expr, ctx, elementTagName) {
|
|
|
82797
83506
|
return true;
|
|
82798
83507
|
}
|
|
82799
83508
|
/**
|
|
83509
|
+
* Quick task 260711-tgk — Angular's `[style]=` (`ɵɵstyleMap`) parses the bound
|
|
83510
|
+
* value into a semicolon-delimited key:value map. Its parser corrupts on an
|
|
83511
|
+
* EMPTY declaration (`;;` — e.g. `pinStyle(colId) + ';' + pad` when
|
|
83512
|
+
* `pinStyle` already ends in `;`), silently DROPPING the declaration that
|
|
83513
|
+
* follows the empty segment. The other 5 targets apply a `:style` STRING
|
|
83514
|
+
* value via the browser's own (tolerant) CSS parser — Angular is the lone
|
|
83515
|
+
* outlier because `[style]=` binds through `ɵɵstyleMap`, not `setAttribute`.
|
|
83516
|
+
*
|
|
83517
|
+
* Fix (see the call site in emitSingleAttr's binding branch): when a
|
|
83518
|
+
* `:style` binding's expression is PROVABLY a string at compile time, emit
|
|
83519
|
+
* `[attr.style]="<expr>"` instead of `[style]="<expr>"`. `[attr.style]`
|
|
83520
|
+
* compiles to `ɵɵattribute` → `setAttribute('style', <string>)`, which the
|
|
83521
|
+
* browser parses tolerantly — matching the other 5 targets' behavior. This
|
|
83522
|
+
* is only reachable for the element's SOLE style contributor: emitAttributes'
|
|
83523
|
+
* `counts.get('style') > 1` merge path (`[ngStyle]`) always intercepts
|
|
83524
|
+
* multi-source style bindings (a co-existing static `style=` or a second
|
|
83525
|
+
* `:style`) via its own `continue`, before emitSingleAttr is ever called for
|
|
83526
|
+
* a style attribute in that case.
|
|
83527
|
+
*
|
|
83528
|
+
* Conservative by design: a false NEGATIVE (returning false for a value that
|
|
83529
|
+
* IS always a string) just keeps the current, already-correct-for-non-`;;`
|
|
83530
|
+
* `[style]=` behavior. A false POSITIVE (returning true for a value that can
|
|
83531
|
+
* be an object) would break object-valued styles — so every branch below is
|
|
83532
|
+
* additive-only and defaults to `false`.
|
|
83533
|
+
*/
|
|
83534
|
+
function isProvablyStringStyleExpression(expr) {
|
|
83535
|
+
if (t$3.isStringLiteral(expr)) return true;
|
|
83536
|
+
if (t$3.isTemplateLiteral(expr)) return true;
|
|
83537
|
+
if (t$3.isBinaryExpression(expr) && expr.operator === "+") {
|
|
83538
|
+
if (!t$3.isExpression(expr.left)) return false;
|
|
83539
|
+
return isProvablyStringStyleExpression(expr.left) || isProvablyStringStyleExpression(expr.right);
|
|
83540
|
+
}
|
|
83541
|
+
if (t$3.isConditionalExpression(expr)) return isProvablyStringStyleExpression(expr.consequent) && isProvablyStringStyleExpression(expr.alternate);
|
|
83542
|
+
return false;
|
|
83543
|
+
}
|
|
83544
|
+
/**
|
|
82800
83545
|
* Plan 14-05 R6 — keys that must never reach the emitted object from an
|
|
82801
83546
|
* author-controlled `r-bind` LITERAL. Mirrors the React/Solid/Vue/Svelte
|
|
82802
83547
|
* `FORBIDDEN_SPREAD_KEYS` set + the Phase 02 `collectPropDecls` write-time
|
|
@@ -83206,6 +83951,151 @@ function emitListenerSpread$1(spread, ctx) {
|
|
|
83206
83951
|
return `#${refName}`;
|
|
83207
83952
|
}
|
|
83208
83953
|
/**
|
|
83954
|
+
* command-palette-portal-overlay phase — the SHARED (once-per-component)
|
|
83955
|
+
* `__roziePortalPlace` helper method. Mirrors `applyAttrsHelperDecl` /
|
|
83956
|
+
* `listenersRendererFieldDecl`'s dedup pattern: pushed onto
|
|
83957
|
+
* `ctx.scriptInjections` at most once regardless of how many `r-portal`
|
|
83958
|
+
* elements the component has.
|
|
83959
|
+
*
|
|
83960
|
+
* A `WeakMap<Element, anchor>` lazily captures each portalled element's
|
|
83961
|
+
* ORIGINAL parent + next-sibling on first placement (so a later falsy
|
|
83962
|
+
* `target` restores it to its exact natural template position — the AOT-
|
|
83963
|
+
* safe, signals-era analog of Vue's `<Teleport :disabled>` / React's
|
|
83964
|
+
* ternary in-place fallback). A truthy `target` moves the element via
|
|
83965
|
+
* native `appendChild` and records it in `__roziePortalMoved` so the
|
|
83966
|
+
* shared `ngOnDestroy` teardown (registered by the per-portal effect, see
|
|
83967
|
+
* `emitPortalDirective`) can remove any STILL-relocated node — Angular's
|
|
83968
|
+
* own view-destroy machinery is not guaranteed to find a node that has
|
|
83969
|
+
* been moved outside its logical view position.
|
|
83970
|
+
*/
|
|
83971
|
+
const ROZIE_PORTAL_PLACE_FIELD_NAME = "__roziePortalPlace";
|
|
83972
|
+
const ROZIE_PORTAL_ANCHORS_FIELD_NAME = "__roziePortalAnchors";
|
|
83973
|
+
const ROZIE_PORTAL_MOVED_FIELD_NAME = "__roziePortalMoved";
|
|
83974
|
+
function portalAnchorsFieldDecl() {
|
|
83975
|
+
return `private ${ROZIE_PORTAL_ANCHORS_FIELD_NAME} = new WeakMap<Element, { parent: Node | null; next: Node | null }>();`;
|
|
83976
|
+
}
|
|
83977
|
+
function portalMovedFieldDecl() {
|
|
83978
|
+
return `private ${ROZIE_PORTAL_MOVED_FIELD_NAME} = new Set<Element>();`;
|
|
83979
|
+
}
|
|
83980
|
+
function portalPlaceMethodDecl() {
|
|
83981
|
+
return [
|
|
83982
|
+
`private ${ROZIE_PORTAL_PLACE_FIELD_NAME}(el: Element, target: Element | null | undefined): void {`,
|
|
83983
|
+
` let anchor = this.${ROZIE_PORTAL_ANCHORS_FIELD_NAME}.get(el);`,
|
|
83984
|
+
` if (!anchor) {`,
|
|
83985
|
+
` anchor = { parent: el.parentNode, next: el.nextSibling };`,
|
|
83986
|
+
` this.${ROZIE_PORTAL_ANCHORS_FIELD_NAME}.set(el, anchor);`,
|
|
83987
|
+
` }`,
|
|
83988
|
+
` if (target) {`,
|
|
83989
|
+
` if (!this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.has(el) && !el.isConnected) return;`,
|
|
83990
|
+
` if (el.parentNode !== target) {`,
|
|
83991
|
+
` target.appendChild(el);`,
|
|
83992
|
+
` }`,
|
|
83993
|
+
` this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.add(el);`,
|
|
83994
|
+
` return;`,
|
|
83995
|
+
` }`,
|
|
83996
|
+
` if (!this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.has(el)) return;`,
|
|
83997
|
+
` this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.delete(el);`,
|
|
83998
|
+
` if (anchor.parent) {`,
|
|
83999
|
+
` if (anchor.next && anchor.next.parentNode === anchor.parent) {`,
|
|
84000
|
+
` anchor.parent.insertBefore(el, anchor.next);`,
|
|
84001
|
+
` } else {`,
|
|
84002
|
+
` anchor.parent.appendChild(el);`,
|
|
84003
|
+
` }`,
|
|
84004
|
+
` }`,
|
|
84005
|
+
`}`
|
|
84006
|
+
].join("\n");
|
|
84007
|
+
}
|
|
84008
|
+
/**
|
|
84009
|
+
* command-palette-portal-overlay phase — emit the per-element machinery for
|
|
84010
|
+
* ONE `r-portal="<expr>"` element:
|
|
84011
|
+
* - a `#roziePortal_<N>` template-ref attribute (returned for splicing
|
|
84012
|
+
* onto the open tag),
|
|
84013
|
+
* - a `viewChild<ElementRef>('roziePortal_<N>')` private field,
|
|
84014
|
+
* - the SHARED `__roziePortalPlace`/anchors/moved-set fields (once per
|
|
84015
|
+
* component — mirrors `emitSpreadBinding`'s `applyAttrsHelperDecl`
|
|
84016
|
+
* dedup),
|
|
84017
|
+
* - a `private __roziePortal_<N>_effect = effect(() => { ... });` field
|
|
84018
|
+
* initializer that guards `nativeElement` (Pitfall 7 — `viewChild()`
|
|
84019
|
+
* signals return `undefined` until the view is initialized, so a
|
|
84020
|
+
* pre-render effect tick is a safe no-op) and re-runs the placement
|
|
84021
|
+
* whenever the container expression's SIGNAL dependencies change
|
|
84022
|
+
* (the AOT-safe, signals-era analog of a reactive `$watch`).
|
|
84023
|
+
*
|
|
84024
|
+
* AOT-SAFE by construction: no `import.meta.url` (not referenced at all),
|
|
84025
|
+
* no inline template arrow (the container expression is a plain property/
|
|
84026
|
+
* method-call expression spliced into a `effect()` callback BODY — a
|
|
84027
|
+
* function statement, not a template-attribute arrow literal; analogjs AOT
|
|
84028
|
+
* rejects arrows INSIDE `{{ }}`/binding attribute VALUES, not inside a
|
|
84029
|
+
* class-field-initializer's own script-side callback).
|
|
84030
|
+
*
|
|
84031
|
+
* `effect()` in a field initializer is valid Angular injection context
|
|
84032
|
+
* (mirrors `emitSpreadBinding`/`emitListenerSpread`'s identical Pitfall-8
|
|
84033
|
+
* rationale) — no separate `ngAfterViewInit`/constructor wiring is needed,
|
|
84034
|
+
* unlike the r-keynav controller (which needs `viewChild()` to resolve
|
|
84035
|
+
* itself for the FIRST commit-timing-sensitive read; a portal's placement
|
|
84036
|
+
* has no such ordering hazard — `effect()`'s own no-op-until-mounted guard
|
|
84037
|
+
* covers the initial-render case identically to every render after it).
|
|
84038
|
+
*/
|
|
84039
|
+
function emitPortalDirective(element, ctx) {
|
|
84040
|
+
const counter = ctx.injectionCounter ?? { next: 0 };
|
|
84041
|
+
const idx = counter.next++;
|
|
84042
|
+
const refName = `roziePortal_${idx}`;
|
|
84043
|
+
const effectFieldName = `__roziePortal_${idx}_effect`;
|
|
84044
|
+
const destroyRegisteredFieldName = `__roziePortal_${idx}_destroyRegistered`;
|
|
84045
|
+
const containerCode = rewriteListenerExpression(element.portalTo.expression, ctx.ir, {
|
|
84046
|
+
collisionRenames: ctx.collisionRenames,
|
|
84047
|
+
classMembers: ctx.classMembers,
|
|
84048
|
+
cvaModelProp: ctx.cvaModelProp,
|
|
84049
|
+
cvaMergeDisabled: ctx.cvaMergeDisabled
|
|
84050
|
+
});
|
|
84051
|
+
if (ctx.scriptInjections !== void 0) {
|
|
84052
|
+
ctx.scriptInjections.push({
|
|
84053
|
+
name: refName,
|
|
84054
|
+
decl: `private ${refName} = viewChild<ElementRef>('${refName}');`
|
|
84055
|
+
});
|
|
84056
|
+
if (!ctx.scriptInjections.some((si) => si.name === ROZIE_PORTAL_ANCHORS_FIELD_NAME)) {
|
|
84057
|
+
ctx.scriptInjections.push({
|
|
84058
|
+
name: ROZIE_PORTAL_ANCHORS_FIELD_NAME,
|
|
84059
|
+
decl: portalAnchorsFieldDecl()
|
|
84060
|
+
});
|
|
84061
|
+
ctx.scriptInjections.push({
|
|
84062
|
+
name: ROZIE_PORTAL_MOVED_FIELD_NAME,
|
|
84063
|
+
decl: portalMovedFieldDecl()
|
|
84064
|
+
});
|
|
84065
|
+
ctx.scriptInjections.push({
|
|
84066
|
+
name: ROZIE_PORTAL_PLACE_FIELD_NAME,
|
|
84067
|
+
decl: portalPlaceMethodDecl()
|
|
84068
|
+
});
|
|
84069
|
+
}
|
|
84070
|
+
ctx.scriptInjections.push({
|
|
84071
|
+
name: destroyRegisteredFieldName,
|
|
84072
|
+
decl: `private ${destroyRegisteredFieldName} = false;`
|
|
84073
|
+
});
|
|
84074
|
+
const effectDecl = [
|
|
84075
|
+
`private ${effectFieldName} = effect(() => {`,
|
|
84076
|
+
` const el = this.${refName}()?.nativeElement;`,
|
|
84077
|
+
` if (!el) return;`,
|
|
84078
|
+
` this.${ROZIE_PORTAL_PLACE_FIELD_NAME}(el, ${containerCode});`,
|
|
84079
|
+
` if (!this.${destroyRegisteredFieldName}) {`,
|
|
84080
|
+
` this.${destroyRegisteredFieldName} = true;`,
|
|
84081
|
+
` this.__rozieDestroyRef.onDestroy(() => {`,
|
|
84082
|
+
` for (const moved of this.${ROZIE_PORTAL_MOVED_FIELD_NAME}) {`,
|
|
84083
|
+
` moved.parentNode?.removeChild(moved);`,
|
|
84084
|
+
` }`,
|
|
84085
|
+
` });`,
|
|
84086
|
+
` }`,
|
|
84087
|
+
`});`
|
|
84088
|
+
].join("\n");
|
|
84089
|
+
ctx.scriptInjections.push({
|
|
84090
|
+
name: effectFieldName,
|
|
84091
|
+
decl: effectDecl
|
|
84092
|
+
});
|
|
84093
|
+
}
|
|
84094
|
+
if (ctx.hasListenerSpread !== void 0) ctx.hasListenerSpread.value = true;
|
|
84095
|
+
if (ctx.needsDestroyRefField !== void 0) ctx.needsDestroyRefField.value = true;
|
|
84096
|
+
return `#${refName}`;
|
|
84097
|
+
}
|
|
84098
|
+
/**
|
|
83209
84099
|
* Emit a single attribute. Returns null when the attribute should be dropped
|
|
83210
84100
|
* (e.g., r-html, which gets emitted later as `[innerHTML]="..."` by the
|
|
83211
84101
|
* element emitter).
|
|
@@ -83252,6 +84142,7 @@ function emitSingleAttr(attr, ctx, elementTagName) {
|
|
|
83252
84142
|
if (ctx.hasDisplayWrap) ctx.hasDisplayWrap.value = true;
|
|
83253
84143
|
return `[${bindingName.startsWith("attr.") ? bindingName : `attr.${attr.name}`}]="rozieAttr(${expr})"`;
|
|
83254
84144
|
}
|
|
84145
|
+
if (attr.name === "style" && isProvablyStringStyleExpression(attr.expression)) return `[attr.style]="${expr}"`;
|
|
83255
84146
|
return `[${bindingName}]="${expr}"`;
|
|
83256
84147
|
}
|
|
83257
84148
|
if (attr.segments.length === 1 && attr.segments[0].kind === "binding") {
|
|
@@ -84429,6 +85320,21 @@ function emitElementInner$2(origNode, ctx) {
|
|
|
84429
85320
|
if (attrText) partsHead.push(attrText);
|
|
84430
85321
|
if (eventText) partsHead.push(eventText);
|
|
84431
85322
|
for (const text of dynamicListenerTexts) partsHead.push(text);
|
|
85323
|
+
if (node.portalTo) {
|
|
85324
|
+
const portalRefText = emitPortalDirective(node, {
|
|
85325
|
+
ir: ctx.ir,
|
|
85326
|
+
collisionRenames: ctx.collisionRenames,
|
|
85327
|
+
loopBindings: ctx.loopBindings,
|
|
85328
|
+
scriptInjections: ctx.scriptInjections,
|
|
85329
|
+
injectionCounter: ctx.injectionCounter,
|
|
85330
|
+
hasListenerSpread: ctx.hasListenerSpread,
|
|
85331
|
+
needsDestroyRefField: ctx.needsDestroyRefField,
|
|
85332
|
+
cvaModelProp: ctx.cvaModelProp,
|
|
85333
|
+
cvaMergeDisabled: ctx.cvaMergeDisabled,
|
|
85334
|
+
classMembers: ctx.classMembers
|
|
85335
|
+
});
|
|
85336
|
+
partsHead.push(portalRefText);
|
|
85337
|
+
}
|
|
84432
85338
|
const keynav = ctx.keynav ?? null;
|
|
84433
85339
|
const keynavAttrs = [...keynavRootAttrs$2(keynav, node, ctx.ir), ...keynavItemAttrs$2(keynav, node, ctx.keynavItemIndexAlias ?? null, ctx.ir)];
|
|
84434
85340
|
for (const a of keynavAttrs) partsHead.push(a);
|
|
@@ -86076,7 +86982,7 @@ function renderType$1(ann) {
|
|
|
86076
86982
|
case "Boolean": return "boolean";
|
|
86077
86983
|
case "Array": return "any[]";
|
|
86078
86984
|
case "Object": return "Record<string, any>";
|
|
86079
|
-
case "Function": return "(...args:
|
|
86985
|
+
case "Function": return "(...args: any[]) => any";
|
|
86080
86986
|
default: return ann.name;
|
|
86081
86987
|
}
|
|
86082
86988
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -86086,7 +86992,7 @@ function renderType$1(ann) {
|
|
|
86086
86992
|
if (ann.kind === "literal") {
|
|
86087
86993
|
if (ann.value === "array") return "any[]";
|
|
86088
86994
|
if (ann.value === "object") return "Record<string, any>";
|
|
86089
|
-
if (ann.value === "function") return "(...args:
|
|
86995
|
+
if (ann.value === "function") return "(...args: any[]) => any";
|
|
86090
86996
|
return ann.value;
|
|
86091
86997
|
}
|
|
86092
86998
|
return "unknown";
|
|
@@ -86215,6 +87121,63 @@ function buildSetterCall$1(varName, operator, rhs) {
|
|
|
86215
87121
|
return t$3.callExpression(t$3.identifier(setterName), [t$3.binaryExpression(binOp, t$3.callExpression(t$3.identifier(varName), []), rhs)]);
|
|
86216
87122
|
}
|
|
86217
87123
|
/**
|
|
87124
|
+
* 260712-ig6 Task B — detects the "null-widened prop spliced into a 3rd-party
|
|
87125
|
+
* object-literal call argument" shape: a `$props.<name>` read (where `<name>`
|
|
87126
|
+
* has `default: null`) that ultimately feeds an `ObjectProperty` VALUE inside
|
|
87127
|
+
* an `ObjectExpression` that is (possibly via nested object-literal property
|
|
87128
|
+
* values, e.g. `useEngine(el, { options: { group: $props.group } })`) itself
|
|
87129
|
+
* a DIRECT argument of a `CallExpression` — the mount-time engine-init shape
|
|
87130
|
+
* (`useSortableJS($refs.listEl, { options: { handle: $props.handle, ... } })`).
|
|
87131
|
+
*
|
|
87132
|
+
* Walks UP from the read, tolerating exactly two pass-through shapes on the
|
|
87133
|
+
* way to an `ObjectProperty` value:
|
|
87134
|
+
* - `ConditionalExpression` consequent/alternate (the `group` prop's
|
|
87135
|
+
* `cloneable ? { ... } : $props.group` ternary)
|
|
87136
|
+
* - nested `ObjectExpression` → `ObjectProperty` value chains (an object
|
|
87137
|
+
* literal nested inside another object literal's property value)
|
|
87138
|
+
*
|
|
87139
|
+
* Any OTHER parent shape (binary/logical expression, array literal, bare
|
|
87140
|
+
* statement, non-direct call argument, …) fails the match — this is
|
|
87141
|
+
* intentionally narrow, mirroring Pattern D's `isDirectCallArg` precedent.
|
|
87142
|
+
* Distinct from Pattern D: Pattern D fires on `$refs` DOM-ref reads that are
|
|
87143
|
+
* THEMSELVES a direct call argument; this fires on `$props` reads nested
|
|
87144
|
+
* inside an object-literal call argument.
|
|
87145
|
+
*
|
|
87146
|
+
* `excludeCallees` DISQUALIFIES the match when the terminal call's callee is
|
|
87147
|
+
* one of the emitter's OWN synthesized reactive setters (`set<Data>` /
|
|
87148
|
+
* `set<ModelProp>`, minted by `buildSetterCall` for `$data.x = {...}` /
|
|
87149
|
+
* `$model.x = {...}` writes). Those setter calls are Rozie-internal — the
|
|
87150
|
+
* `{...}` argument feeds a Solid `createSignal` setter, not a 3rd-party lib's
|
|
87151
|
+
* typed options object, so a null-widened prop spliced into one must stay
|
|
87152
|
+
* `null` verbatim (the cross-target `PropDefaultCoercion` conformance probe
|
|
87153
|
+
* asserts `JSON.stringify({ a: null, ... })` renders the literal `"a":null`
|
|
87154
|
+
* substring on every target — `?? undefined` would DROP the key entirely,
|
|
87155
|
+
* a real cross-target behavior regression, not a type-only change).
|
|
87156
|
+
*/
|
|
87157
|
+
function isNullWidenedPropObjectLiteralCallArgTarget(path, excludeCallees) {
|
|
87158
|
+
let cur = path;
|
|
87159
|
+
for (;;) {
|
|
87160
|
+
const parent = cur.parentPath;
|
|
87161
|
+
if (!parent) return false;
|
|
87162
|
+
if (parent.isConditionalExpression() && (parent.node.consequent === cur.node || parent.node.alternate === cur.node)) {
|
|
87163
|
+
cur = parent;
|
|
87164
|
+
continue;
|
|
87165
|
+
}
|
|
87166
|
+
if (parent.isObjectProperty() && parent.node.value === cur.node) {
|
|
87167
|
+
const objExpr = parent.parentPath;
|
|
87168
|
+
if (!objExpr || !objExpr.isObjectExpression()) return false;
|
|
87169
|
+
cur = objExpr;
|
|
87170
|
+
continue;
|
|
87171
|
+
}
|
|
87172
|
+
if (parent.isCallExpression() && parent.node.arguments.some((arg) => arg === cur.node)) {
|
|
87173
|
+
const callee = parent.node.callee;
|
|
87174
|
+
if (t$3.isIdentifier(callee) && excludeCallees.has(callee.name)) return false;
|
|
87175
|
+
return true;
|
|
87176
|
+
}
|
|
87177
|
+
return false;
|
|
87178
|
+
}
|
|
87179
|
+
}
|
|
87180
|
+
/**
|
|
86218
87181
|
* Phase 18 (Req 2) — normalize the producer-side two-way-write sigil `$model`
|
|
86219
87182
|
* to `$props` across a cloned File, in place. See the call-site comment in
|
|
86220
87183
|
* `rewriteRozieIdentifiers` for the full contract; `$model.X` is model-only and
|
|
@@ -86321,7 +87284,7 @@ function hoistPolymorphicModelGuards(cloned, polymorphicModelProps) {
|
|
|
86321
87284
|
*
|
|
86322
87285
|
* @experimental — shape may change before v1.0
|
|
86323
87286
|
*/
|
|
86324
|
-
function rewriteRozieExpressionNode(expr, ir) {
|
|
87287
|
+
function rewriteRozieExpressionNode(expr, ir, options) {
|
|
86325
87288
|
let programBody;
|
|
86326
87289
|
const isBlock = t$3.isBlockStatement(expr);
|
|
86327
87290
|
if (isBlock) programBody = t$3.cloneNode(expr, true, false).body;
|
|
@@ -86335,7 +87298,7 @@ function rewriteRozieExpressionNode(expr, ir) {
|
|
|
86335
87298
|
sourceType: "module"
|
|
86336
87299
|
},
|
|
86337
87300
|
comments: []
|
|
86338
|
-
}, ir).rewrittenProgram.program.body;
|
|
87301
|
+
}, ir, options).rewrittenProgram.program.body;
|
|
86339
87302
|
if (isBlock) return t$3.blockStatement(body);
|
|
86340
87303
|
if (body.length === 1 && t$3.isExpressionStatement(body[0])) return body[0].expression;
|
|
86341
87304
|
const last = body[body.length - 1];
|
|
@@ -86346,10 +87309,12 @@ function rewriteRozieExpressionNode(expr, ir) {
|
|
|
86346
87309
|
* Full Solid identifier rewrite pass. Replaces all $props/$data/$refs/$emit
|
|
86347
87310
|
* references with their Solid-idiomatic equivalents.
|
|
86348
87311
|
*/
|
|
86349
|
-
function rewriteRozieIdentifiers(cloned, ir) {
|
|
87312
|
+
function rewriteRozieIdentifiers(cloned, ir, options) {
|
|
86350
87313
|
const diagnostics = [];
|
|
87314
|
+
const nonNullRefCallArgs = options?.nonNullRefCallArgs === true;
|
|
86351
87315
|
const modelProps = new Set(ir.props.filter((p) => p.isModel).map((p) => p.name));
|
|
86352
87316
|
const nonModelProps = new Set(ir.props.filter((p) => !p.isModel).map((p) => p.name));
|
|
87317
|
+
const nullWidenedNonModelProps = new Set(ir.props.filter((p) => !p.isModel && t$3.isNullLiteral(p.defaultValue)).map((p) => p.name));
|
|
86353
87318
|
const unknownModelProps = new Set(ir.props.filter((p) => p.isModel && renderType$1(p.typeAnnotation) === "unknown").map((p) => p.name));
|
|
86354
87319
|
const polymorphicModelProps = new Set(ir.props.filter((p) => p.isModel && (p.typeAnnotation.kind === "union" || renderType$1(p.typeAnnotation) === "unknown")).map((p) => p.name));
|
|
86355
87320
|
const dataNames = new Set(ir.state.map((s) => s.name));
|
|
@@ -86466,6 +87431,12 @@ function rewriteRozieIdentifiers(cloned, ir) {
|
|
|
86466
87431
|
return;
|
|
86467
87432
|
}
|
|
86468
87433
|
if (nonModelProps.has(property.name)) {
|
|
87434
|
+
if (nonNullRefCallArgs && nullWidenedNonModelProps.has(property.name) && isNullWidenedPropObjectLiteralCallArgTarget(path, solidSetters)) {
|
|
87435
|
+
const localRead = t$3.memberExpression(t$3.identifier("local"), t$3.identifier(property.name));
|
|
87436
|
+
path.replaceWith(t$3.logicalExpression("??", localRead, t$3.identifier("undefined")));
|
|
87437
|
+
path.skip();
|
|
87438
|
+
return;
|
|
87439
|
+
}
|
|
86469
87440
|
path.node.object = t$3.identifier("local");
|
|
86470
87441
|
return;
|
|
86471
87442
|
}
|
|
@@ -86479,7 +87450,8 @@ function rewriteRozieIdentifiers(cloned, ir) {
|
|
|
86479
87450
|
}
|
|
86480
87451
|
if (object.name === "$refs" && refNames.has(property.name)) {
|
|
86481
87452
|
const refIdent = t$3.identifier(property.name + "Ref");
|
|
86482
|
-
|
|
87453
|
+
const isDirectCallArg = nonNullRefCallArgs && path.parentPath?.isCallExpression() && path.parentPath.node.arguments.includes(path.node);
|
|
87454
|
+
if (property.name === "__rozieRoot" || isDirectCallArg) path.replaceWith(t$3.tsNonNullExpression(refIdent));
|
|
86483
87455
|
else path.replaceWith(refIdent);
|
|
86484
87456
|
path.skip();
|
|
86485
87457
|
return;
|
|
@@ -86860,6 +87832,19 @@ function genCode$1(node) {
|
|
|
86860
87832
|
return generate$7(node, GEN_OPTS$6).code;
|
|
86861
87833
|
}
|
|
86862
87834
|
/**
|
|
87835
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
87836
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
87837
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
87838
|
+
* later import's `leadingComments`) prints exactly once. Generating imports
|
|
87839
|
+
* one at a time (`nodes.map(genCode).join`) gives each import its OWN
|
|
87840
|
+
* comment-dedup set, doubling any shared comment. `t.program(nodes)` prints
|
|
87841
|
+
* only its body statements (no wrapping braces/`File` boilerplate), so
|
|
87842
|
+
* non-comment cases stay byte-identical.
|
|
87843
|
+
*/
|
|
87844
|
+
function genImportsBlock(nodes) {
|
|
87845
|
+
return generate$7(t$3.program(nodes), GEN_OPTS$6).code;
|
|
87846
|
+
}
|
|
87847
|
+
/**
|
|
86863
87848
|
* ROZ-cast-blindness fix — Solid emits each ComputedDecl's body via
|
|
86864
87849
|
* `rewriteNode(c.body, ir)` directly from IR (never re-scanning the cloned
|
|
86865
87850
|
* Program for the body itself), so there is no existing "find the cloned
|
|
@@ -86918,6 +87903,17 @@ function capitalize$6(name) {
|
|
|
86918
87903
|
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
86919
87904
|
}
|
|
86920
87905
|
/**
|
|
87906
|
+
* 260712-kl1 — exact-AST-shape gate mirroring the `isMutableLiteralFactoryDefault`
|
|
87907
|
+
* / `isNullWidenedPropObjectLiteralCallArgTarget` predicate convention. Matches
|
|
87908
|
+
* ONLY a Babel `NullLiteral` prop default (`default: null`) — never `undefined`,
|
|
87909
|
+
* `void 0`, or any other falsy/nullish shape. Used to widen a model prop's
|
|
87910
|
+
* `createControllableSignal<T>` generic to `T | null` (see 260712-kb9's
|
|
87911
|
+
* NEW-controllable-null-default catalog entry).
|
|
87912
|
+
*/
|
|
87913
|
+
function isNullLiteralDefault(dv) {
|
|
87914
|
+
return t$3.isNullLiteral(dv);
|
|
87915
|
+
}
|
|
87916
|
+
/**
|
|
86921
87917
|
* WR-01 fix (73-REVIEW.md, 73-10 gap-closure): collect every Identifier
|
|
86922
87918
|
* binding name introduced by a declarator's `id` pattern — Identifier
|
|
86923
87919
|
* directly, or recursively through ObjectPattern / ArrayPattern /
|
|
@@ -87130,7 +88126,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87130
88126
|
diagnostics.push(...rewriteResult.diagnostics);
|
|
87131
88127
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts } = partitionUserImports$1(rewriteResult.rewrittenProgram);
|
|
87132
88128
|
rewriteResult.rewrittenProgram.program.body = bodyStmts;
|
|
87133
|
-
const userImports = userImportNodes.length > 0 ? userImportNodes
|
|
88129
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock(userImportNodes) + "\n" : "";
|
|
87134
88130
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => genCode$1(decl));
|
|
87135
88131
|
const contextEmit = emitContext$1(ir, {
|
|
87136
88132
|
solid: collectors.solidImports,
|
|
@@ -87146,7 +88142,9 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87146
88142
|
const dv = p.defaultValue;
|
|
87147
88143
|
const raw = genCode$1(dv);
|
|
87148
88144
|
const val = isMutableLiteralFactoryDefault(dv) ? `(${raw})()` : raw;
|
|
87149
|
-
|
|
88145
|
+
const ann = p.typeAnnotation;
|
|
88146
|
+
const entryVal = isMutableLiteralFactoryDefault(dv) && ann.kind === "identifier" && (ann.name === "Object" || ann.name === "Array") ? `${val} as ${renderType$1(ann)}` : val;
|
|
88147
|
+
return `${p.name}: ${entryVal}`;
|
|
87150
88148
|
}).join(", ")} }, _props);\n`;
|
|
87151
88149
|
}
|
|
87152
88150
|
for (const p of ir.props) {
|
|
@@ -87160,7 +88158,8 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87160
88158
|
else dflt = raw;
|
|
87161
88159
|
}
|
|
87162
88160
|
const tsType = renderType$1(p.typeAnnotation);
|
|
87163
|
-
|
|
88161
|
+
const genericType = isNullLiteralDefault(p.defaultValue) && tsType !== "unknown" ? `${tsType} | null` : tsType;
|
|
88162
|
+
hookLines.push(`const [${p.name}, ${setterName}] = createControllableSignal<${genericType}>(_props as unknown as Record<string, unknown>, '${p.name}', ${dflt});`);
|
|
87164
88163
|
}
|
|
87165
88164
|
for (const s of ir.state) {
|
|
87166
88165
|
collectors.solidImports.add("createSignal");
|
|
@@ -87192,7 +88191,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87192
88191
|
for (const lh of ir.lifecycle) if (lh.phase === "mount") if (lh.cleanup) {
|
|
87193
88192
|
collectors.solidImports.add("onMount");
|
|
87194
88193
|
collectors.solidImports.add("onCleanup");
|
|
87195
|
-
const rewrittenSetup = rewriteRozieExpressionNode(lh.setup, ir);
|
|
88194
|
+
const rewrittenSetup = rewriteRozieExpressionNode(lh.setup, ir, { nonNullRefCallArgs: true });
|
|
87196
88195
|
const rewrittenCleanup = rewriteRozieExpressionNode(lh.cleanup, ir);
|
|
87197
88196
|
if (t$3.isBlockStatement(lh.setup) && cleanupReferencesSetupLocal(lh.setup, lh.cleanup)) {
|
|
87198
88197
|
const setupStatements = rewrittenSetup.body;
|
|
@@ -87206,7 +88205,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87206
88205
|
}
|
|
87207
88206
|
} else {
|
|
87208
88207
|
collectors.solidImports.add("onMount");
|
|
87209
|
-
const arg = lifecycleArg(rewriteRozieExpressionNode(lh.setup, ir));
|
|
88208
|
+
const arg = lifecycleArg(rewriteRozieExpressionNode(lh.setup, ir, { nonNullRefCallArgs: true }));
|
|
87210
88209
|
hookLines.push(`onMount(${arg});`);
|
|
87211
88210
|
}
|
|
87212
88211
|
else if (lh.phase === "unmount") {
|
|
@@ -89881,7 +90880,22 @@ function emitLoop$1(node, ctx) {
|
|
|
89881
90880
|
invokeAccessors: ctx.invokeAccessors,
|
|
89882
90881
|
loopValueBindings: new Set([...ctx.loopValueBindings ?? [], node.itemAlias])
|
|
89883
90882
|
});
|
|
89884
|
-
return `<Key each={${
|
|
90883
|
+
return `<Key each={${new Set([
|
|
90884
|
+
"Identifier",
|
|
90885
|
+
"MemberExpression",
|
|
90886
|
+
"OptionalMemberExpression",
|
|
90887
|
+
"CallExpression",
|
|
90888
|
+
"OptionalCallExpression",
|
|
90889
|
+
"NewExpression",
|
|
90890
|
+
"ArrayExpression",
|
|
90891
|
+
"ObjectExpression",
|
|
90892
|
+
"TSNonNullExpression",
|
|
90893
|
+
"TSAsExpression",
|
|
90894
|
+
"ParenthesizedExpression",
|
|
90895
|
+
"StringLiteral",
|
|
90896
|
+
"NumericLiteral",
|
|
90897
|
+
"TemplateLiteral"
|
|
90898
|
+
]).has(node.iterableExpression.type) ? iterableCode : `(${iterableCode})`} as readonly any[]} by={(${node.itemAlias}) => ${keyCode}}>{${aliasStr} => ${bodyJsx}}</Key>`;
|
|
89885
90899
|
}
|
|
89886
90900
|
/**
|
|
89887
90901
|
* Find an attribute by name.
|
|
@@ -90227,8 +91241,34 @@ function mergeEventAttributes(attrsJsx, eventsJsx, elementTag) {
|
|
|
90227
91241
|
* (so the child is never hidden) while still changing value — and thus
|
|
90228
91242
|
* still triggering `keyed` recreation — whenever the underlying key changes.
|
|
90229
91243
|
*/
|
|
91244
|
+
/**
|
|
91245
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
91246
|
+
* teleport. Wraps the element markup in Solid's native `<Show when={c}
|
|
91247
|
+
* fallback={tree}><Portal mount={c}>{tree}</Portal></Show>` — falsy
|
|
91248
|
+
* container renders `tree` in place via the `fallback` prop (never mounts
|
|
91249
|
+
* `<Portal>` at all), matching React's `container ? createPortal(tree,
|
|
91250
|
+
* container) : tree` and Vue's `<Teleport :disabled>` in-place semantics.
|
|
91251
|
+
*
|
|
91252
|
+
* SSR guard: `typeof document === 'undefined'` short-circuits the container
|
|
91253
|
+
* expression to `null` BEFORE it is evaluated at all — some author
|
|
91254
|
+
* expressions call `document.querySelector` internally (see
|
|
91255
|
+
* `resolveAppendTo`/`resolveTo`), so the guard must gate evaluation, not
|
|
91256
|
+
* just the result.
|
|
91257
|
+
*/
|
|
91258
|
+
function emitPortalWrap(node, markup, ctx) {
|
|
91259
|
+
const { expression } = node.portalTo;
|
|
91260
|
+
if (ctx.elementPortalImport) ctx.elementPortalImport.needed = true;
|
|
91261
|
+
const containerCode = `(typeof document === 'undefined' ? null : (${rewriteTemplateExpression$1(expression, ctx.ir, {
|
|
91262
|
+
invokeAccessors: ctx.invokeAccessors,
|
|
91263
|
+
loopValueBindings: ctx.loopValueBindings,
|
|
91264
|
+
scopeAccessorParams: ctx.scopeAccessorParams
|
|
91265
|
+
})}))`;
|
|
91266
|
+
ctx.collectors.solid.add("Show");
|
|
91267
|
+
return `<Show when={${containerCode}} fallback={${markup}}><Portal mount={${containerCode}}>${markup}</Portal></Show>`;
|
|
91268
|
+
}
|
|
90230
91269
|
function emitElement$1(origNode, ctx) {
|
|
90231
91270
|
const markup = emitElementInner$1(origNode, ctx);
|
|
91271
|
+
if (origNode.portalTo) return emitPortalWrap(origNode, markup, ctx);
|
|
90232
91272
|
if ((origNode.tagKind === "component" || origNode.tagKind === "self") && origNode.remountKeyExpression) {
|
|
90233
91273
|
const keyExprCode = rewriteTemplateExpression$1(origNode.remountKeyExpression, ctx.ir, {
|
|
90234
91274
|
invokeAccessors: ctx.invokeAccessors,
|
|
@@ -90466,11 +91506,13 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90466
91506
|
jsx: "null",
|
|
90467
91507
|
scriptInjections: [],
|
|
90468
91508
|
needsKeyedImport: false,
|
|
90469
|
-
diagnostics: []
|
|
91509
|
+
diagnostics: [],
|
|
91510
|
+
hasElementPortal: false
|
|
90470
91511
|
};
|
|
90471
91512
|
const scriptInjections = [];
|
|
90472
91513
|
const injectionCounter = { next: 0 };
|
|
90473
91514
|
const keyedImport = { needed: false };
|
|
91515
|
+
const elementPortalImport = { needed: false };
|
|
90474
91516
|
const keynav = resolveKeynavPlan$1(ir);
|
|
90475
91517
|
const ctx = {
|
|
90476
91518
|
ir,
|
|
@@ -90480,6 +91522,7 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90480
91522
|
scriptInjections,
|
|
90481
91523
|
injectionCounter,
|
|
90482
91524
|
keyedImport,
|
|
91525
|
+
elementPortalImport,
|
|
90483
91526
|
keynav,
|
|
90484
91527
|
...opts.scopeAttr !== void 0 ? { scopeAttr: opts.scopeAttr } : {}
|
|
90485
91528
|
};
|
|
@@ -90489,7 +91532,8 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90489
91532
|
jsx,
|
|
90490
91533
|
scriptInjections,
|
|
90491
91534
|
needsKeyedImport: keyedImport.needed,
|
|
90492
|
-
diagnostics
|
|
91535
|
+
diagnostics,
|
|
91536
|
+
hasElementPortal: elementPortalImport.needed
|
|
90493
91537
|
};
|
|
90494
91538
|
}
|
|
90495
91539
|
//#endregion
|
|
@@ -91458,7 +92502,7 @@ function emitSolid(ir, opts = {}) {
|
|
|
91458
92502
|
...listenersResult.scriptInjections
|
|
91459
92503
|
].filter((s) => s.trim().length > 0).join("\n\n");
|
|
91460
92504
|
const solidImportsStr = `import type { JSX } from 'solid-js';\n` + solidImports.render();
|
|
91461
|
-
const portalImport = scriptResult.hasPortals ? "import { render } from 'solid-js/web';\n" : "";
|
|
92505
|
+
const portalImport = (scriptResult.hasPortals ? "import { render } from 'solid-js/web';\n" : "") + (templateResult.hasElementPortal ? "import { Portal } from 'solid-js/web';\n" : "");
|
|
91462
92506
|
const keyedImport = templateResult.needsKeyedImport ? "import { Key } from '@solid-primitives/keyed';\n" : "";
|
|
91463
92507
|
const shell = buildShell$1({
|
|
91464
92508
|
componentName: ir.name,
|
|
@@ -92950,7 +93994,7 @@ function renderTsType(ann) {
|
|
|
92950
93994
|
case "Boolean": return "boolean";
|
|
92951
93995
|
case "Array": return "any[]";
|
|
92952
93996
|
case "Object": return "any";
|
|
92953
|
-
case "Function": return "((...args:
|
|
93997
|
+
case "Function": return "((...args: any[]) => any) | null";
|
|
92954
93998
|
default: return ann.name;
|
|
92955
93999
|
}
|
|
92956
94000
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -92979,14 +94023,81 @@ function renderTsType(ann) {
|
|
|
92979
94023
|
* The `!`-shape matches what Rozie already emits for Angular `@Input` and Lit
|
|
92980
94024
|
* `@query` ref fields.
|
|
92981
94025
|
*/
|
|
94026
|
+
/**
|
|
94027
|
+
* 260712-a09 (Pattern B) — an explicit `default: undefined` (or `default: void 0`)
|
|
94028
|
+
* is authorial shorthand for "no meaningful default", not a real default value.
|
|
94029
|
+
* `prop.defaultValue` is a live (non-null) AST node for either form, so the
|
|
94030
|
+
* `prop.defaultValue != null` guard below would otherwise route it into the
|
|
94031
|
+
* `= <default>` initializer branch and emit `foo: string = undefined` — a
|
|
94032
|
+
* TS2322 under strictNullChecks (a typed field can't be initialized with
|
|
94033
|
+
* `undefined` unless the type itself admits it). Detect both spellings here
|
|
94034
|
+
* and treat them identically to "no default" so they fall through to the
|
|
94035
|
+
* existing `?:`/`!:` branch.
|
|
94036
|
+
*/
|
|
94037
|
+
function isUndefinedDefault(expr) {
|
|
94038
|
+
return t$3.isIdentifier(expr, { name: "undefined" }) || t$3.isUnaryExpression(expr) && expr.operator === "void";
|
|
94039
|
+
}
|
|
94040
|
+
/**
|
|
94041
|
+
* 260712-ig6 Task A — the full ARIAMixin field-name set (lib.dom.d.ts). A
|
|
94042
|
+
* prop authored with one of these names collides with LitElement's own
|
|
94043
|
+
* `implements ARIAMixin` field declaration (always `<T> | null`, NEVER
|
|
94044
|
+
* `undefined`-admitting) if it lands in the ordinary no-default `?:`/`!:`
|
|
94045
|
+
* branch below — Pattern B's `default: undefined` routes there and would
|
|
94046
|
+
* otherwise emit `ariaLabel?: string;` (`string | undefined`), which is not
|
|
94047
|
+
* assignable to the base class's `string | null` field (TS2416/TS1240).
|
|
94048
|
+
* Gated STRICTLY on name-set membership so every other prop name keeps its
|
|
94049
|
+
* pre-existing byte-identical `?:`/`!:` output.
|
|
94050
|
+
*/
|
|
94051
|
+
const ARIA_MIXIN_FIELDS = new Set([
|
|
94052
|
+
"ariaAtomic",
|
|
94053
|
+
"ariaAutoComplete",
|
|
94054
|
+
"ariaBusy",
|
|
94055
|
+
"ariaChecked",
|
|
94056
|
+
"ariaColCount",
|
|
94057
|
+
"ariaColIndex",
|
|
94058
|
+
"ariaColSpan",
|
|
94059
|
+
"ariaCurrent",
|
|
94060
|
+
"ariaDescription",
|
|
94061
|
+
"ariaDisabled",
|
|
94062
|
+
"ariaExpanded",
|
|
94063
|
+
"ariaHasPopup",
|
|
94064
|
+
"ariaHidden",
|
|
94065
|
+
"ariaInvalid",
|
|
94066
|
+
"ariaKeyShortcuts",
|
|
94067
|
+
"ariaLabel",
|
|
94068
|
+
"ariaLevel",
|
|
94069
|
+
"ariaLive",
|
|
94070
|
+
"ariaModal",
|
|
94071
|
+
"ariaMultiLine",
|
|
94072
|
+
"ariaMultiSelectable",
|
|
94073
|
+
"ariaOrientation",
|
|
94074
|
+
"ariaPlaceholder",
|
|
94075
|
+
"ariaPosInSet",
|
|
94076
|
+
"ariaPressed",
|
|
94077
|
+
"ariaReadOnly",
|
|
94078
|
+
"ariaRequired",
|
|
94079
|
+
"ariaRoleDescription",
|
|
94080
|
+
"ariaRowCount",
|
|
94081
|
+
"ariaRowIndex",
|
|
94082
|
+
"ariaRowSpan",
|
|
94083
|
+
"ariaSelected",
|
|
94084
|
+
"ariaSetSize",
|
|
94085
|
+
"ariaSort",
|
|
94086
|
+
"ariaValueMax",
|
|
94087
|
+
"ariaValueMin",
|
|
94088
|
+
"ariaValueNow",
|
|
94089
|
+
"ariaValueText",
|
|
94090
|
+
"role"
|
|
94091
|
+
]);
|
|
92982
94092
|
function renderFieldSuffix(prop) {
|
|
92983
94093
|
const tsType = renderTsType(prop.typeAnnotation);
|
|
92984
|
-
if (prop.defaultValue != null) {
|
|
94094
|
+
if (prop.defaultValue != null && !isUndefinedDefault(prop.defaultValue)) {
|
|
92985
94095
|
let fieldType = tsType;
|
|
92986
94096
|
const admitsNull = /\bnull\b/.test(fieldType) || /(^|\|)\s*(any|unknown)\s*(\||$)/.test(fieldType);
|
|
92987
94097
|
if (t$3.isNullLiteral(prop.defaultValue) && !admitsNull) fieldType = `${fieldType} | null`;
|
|
92988
94098
|
return `: ${fieldType} = ${renderDefault(prop.defaultValue, prop.typeAnnotation)}`;
|
|
92989
94099
|
}
|
|
94100
|
+
if (ARIA_MIXIN_FIELDS.has(prop.name)) return `: ${/\bnull\b/.test(tsType) || /(^|\|)\s*(any|unknown)\s*(\||$)/.test(tsType) ? tsType : `${tsType} | null`} = null`;
|
|
92990
94101
|
return prop.required ? `!: ${tsType}` : `?: ${tsType}`;
|
|
92991
94102
|
}
|
|
92992
94103
|
function isPrimitiveType(ann) {
|
|
@@ -93359,12 +94470,15 @@ function emitScript(ir, opts) {
|
|
|
93359
94470
|
const composedTypeImportLines = [];
|
|
93360
94471
|
for (const typeName of composedTypeImports) {
|
|
93361
94472
|
const decl = (ir.components ?? []).find((c) => c.localName === typeName);
|
|
93362
|
-
if (decl)
|
|
94473
|
+
if (decl) {
|
|
94474
|
+
const importPath = isPublishedSpecifier(decl.importPath) ? rewriteRozieImport(decl.importPath, "lit") : decl.importPath;
|
|
94475
|
+
composedTypeImportLines.push(`import type { ${typeName} } from '${importPath}';`);
|
|
94476
|
+
}
|
|
93363
94477
|
}
|
|
93364
94478
|
const rewritten = rewriteScript(ir.setupBody.scriptProgram, ir, { runtime: opts.runtime });
|
|
93365
94479
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts: nonImportStmts } = partitionUserImports(rewritten.file);
|
|
93366
94480
|
rewritten.file.program.body = nonImportStmts;
|
|
93367
|
-
const userImports = (composedTypeImportLines.length > 0 ? composedTypeImportLines.join("\n") + "\n" : "") + (userImportNodes.length > 0 ?
|
|
94481
|
+
const userImports = (composedTypeImportLines.length > 0 ? composedTypeImportLines.join("\n") + "\n" : "") + (userImportNodes.length > 0 ? generate$1(t$3.program(userImportNodes), GEN_OPTS$1).code + "\n" : "");
|
|
93368
94482
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => generate$1(decl, GEN_OPTS$1).code);
|
|
93369
94483
|
const partition = partitionScript(rewritten.file);
|
|
93370
94484
|
const computedNames = new Set(ir.computed.map((c) => c.name));
|
|
@@ -95092,6 +96206,7 @@ function emitElementOpenTag(node, ir, irName, opts) {
|
|
|
95092
96206
|
const tagName = resolveTagName(node, irName);
|
|
95093
96207
|
const parts = [];
|
|
95094
96208
|
let refAttr = null;
|
|
96209
|
+
let portalSentinel = "";
|
|
95095
96210
|
const rModelAttr = node.attributes.find((a) => attributeIsRModel(a));
|
|
95096
96211
|
const staticClassValues = [];
|
|
95097
96212
|
let bindingClass = null;
|
|
@@ -95194,16 +96309,27 @@ function emitElementOpenTag(node, ir, irName, opts) {
|
|
|
95194
96309
|
parts.push(`\${rozieListeners(${expr})}`);
|
|
95195
96310
|
}
|
|
95196
96311
|
if (refAttr) parts.push(refAttr);
|
|
96312
|
+
if (node.portalTo && opts._state) {
|
|
96313
|
+
const fieldName = `__roziePortal${opts._state.portalCount++}`;
|
|
96314
|
+
parts.push(`data-rozie-portal-ref="${fieldName}"`);
|
|
96315
|
+
portalSentinel = `<span data-rozie-portal-anchor="${fieldName}" hidden></span>`;
|
|
96316
|
+
opts.decorators.add("query");
|
|
96317
|
+
opts.runtime.add("RoziePortalController");
|
|
96318
|
+
const containerCode = rewriteTemplateExpression(node.portalTo.expression, ir);
|
|
96319
|
+
opts._state.portalFieldDecls.push(` @query('[data-rozie-portal-ref="${fieldName}"]') private ${fieldName}!: HTMLElement;`);
|
|
96320
|
+
opts._state.portalFieldDecls.push(` @query('[data-rozie-portal-anchor="${fieldName}"]') private ${fieldName}Anchor!: HTMLElement;`);
|
|
96321
|
+
opts._state.portalFieldDecls.push(` private ${fieldName}Controller = new RoziePortalController(this, () => this.${fieldName}, () => this.${fieldName}Anchor, () => (${containerCode}));`);
|
|
96322
|
+
}
|
|
95197
96323
|
const keynav = opts.keynav ?? null;
|
|
95198
96324
|
parts.push(...keynavRootAttrs(keynav, node, opts.runtime), ...keynavItemAttrs(keynav, node, opts.keynavItemIndexAlias ?? null));
|
|
95199
96325
|
if ((node.tagKind === "html" || node.tagKind === "component") && opts.scopeHash) parts.push(`data-rozie-s-${opts.scopeHash}`);
|
|
95200
96326
|
const attrsText = parts.length > 0 ? " " + parts.join(" ") : "";
|
|
95201
96327
|
if (isVoidElement(node.tagName) && node.children.length === 0) return {
|
|
95202
|
-
open:
|
|
96328
|
+
open: `${portalSentinel}<${tagName}${attrsText} />`,
|
|
95203
96329
|
selfClose: true
|
|
95204
96330
|
};
|
|
95205
96331
|
return {
|
|
95206
|
-
open:
|
|
96332
|
+
open: `${portalSentinel}<${tagName}${attrsText}>`,
|
|
95207
96333
|
selfClose: false
|
|
95208
96334
|
};
|
|
95209
96335
|
}
|
|
@@ -95472,6 +96598,8 @@ function emitTemplate(ir, opts) {
|
|
|
95472
96598
|
slotFillerClassFields: [],
|
|
95473
96599
|
slotFillerUpdatedBody: [],
|
|
95474
96600
|
slotFillerDisconnectReset: [],
|
|
96601
|
+
portalFieldDecls: [],
|
|
96602
|
+
portalCount: 0,
|
|
95475
96603
|
diagnostics
|
|
95476
96604
|
};
|
|
95477
96605
|
const keynav = resolveKeynavPlan(ir);
|
|
@@ -95500,6 +96628,7 @@ function emitTemplate(ir, opts) {
|
|
|
95500
96628
|
slotFillerUpdatedBody: state.slotFillerUpdatedBody,
|
|
95501
96629
|
slotFillerDisconnectReset: state.slotFillerDisconnectReset,
|
|
95502
96630
|
keynavFieldDecls,
|
|
96631
|
+
portalFieldDecls: state.portalFieldDecls,
|
|
95503
96632
|
diagnostics
|
|
95504
96633
|
};
|
|
95505
96634
|
}
|
|
@@ -95522,6 +96651,7 @@ function emitTemplate(ir, opts) {
|
|
|
95522
96651
|
slotFillerUpdatedBody: state.slotFillerUpdatedBody,
|
|
95523
96652
|
slotFillerDisconnectReset: state.slotFillerDisconnectReset,
|
|
95524
96653
|
keynavFieldDecls,
|
|
96654
|
+
portalFieldDecls: state.portalFieldDecls,
|
|
95525
96655
|
diagnostics
|
|
95526
96656
|
};
|
|
95527
96657
|
}
|
|
@@ -95982,7 +97112,11 @@ function emitStyle(styles, source, opts) {
|
|
|
95982
97112
|
const engineCss = stringifyRules((styles.engineRules ?? []).flatMap((r) => r.children ?? []), source);
|
|
95983
97113
|
const rawScopedCss = stringifyRules(scopedRules, source);
|
|
95984
97114
|
const scopedCss = opts.scopeHash ? scopeCss(rawScopedCss, opts.scopeHash) : rawScopedCss;
|
|
95985
|
-
const globalCss = [
|
|
97115
|
+
const globalCss = [
|
|
97116
|
+
rootRules.length > 0 ? stringifyRules(rootRules, source) : "",
|
|
97117
|
+
engineCss,
|
|
97118
|
+
opts.hasElementPortal === true ? scopedCss : ""
|
|
97119
|
+
].filter((s) => s.length > 0).join("\n");
|
|
95986
97120
|
const combinedScoped = [
|
|
95987
97121
|
HOST_DISPLAY_DEFAULT,
|
|
95988
97122
|
scopedCss,
|
|
@@ -96059,6 +97193,33 @@ function buildShell(parts) {
|
|
|
96059
97193
|
}
|
|
96060
97194
|
//#endregion
|
|
96061
97195
|
//#region ../targets/lit/src/emitLit.ts
|
|
97196
|
+
/**
|
|
97197
|
+
* command-palette-portal-overlay phase — a cheap, INDEPENDENT presence-only
|
|
97198
|
+
* walk over `ir.template` for at least one `r-portal` element. Needed
|
|
97199
|
+
* because `emitStyle` runs BEFORE `emitTemplate` in this file's call
|
|
97200
|
+
* sequence (styles must be ready before the template walk references
|
|
97201
|
+
* `opts.scopeHash`), so the template-walk-derived `portalCount` isn't
|
|
97202
|
+
* available yet at the point `emitStyle` needs to know whether to ALSO
|
|
97203
|
+
* push `scopedCss` through `injectGlobalStyles` (see `emitStyle.ts`'s
|
|
97204
|
+
* `opts.hasElementPortal` doc comment / `RoziePortalController.ts`'s module
|
|
97205
|
+
* doc comment for WHY). A second, tiny boolean-only walk is simpler and
|
|
97206
|
+
* lower-risk than reordering the two emit passes or synchronizing two
|
|
97207
|
+
* independent per-element counters.
|
|
97208
|
+
*/
|
|
97209
|
+
function hasElementPortal(node) {
|
|
97210
|
+
if (node === null) return false;
|
|
97211
|
+
switch (node.type) {
|
|
97212
|
+
case "TemplateElement":
|
|
97213
|
+
if (node.portalTo !== void 0) return true;
|
|
97214
|
+
return node.children.some(hasElementPortal);
|
|
97215
|
+
case "TemplateFragment": return node.children.some(hasElementPortal);
|
|
97216
|
+
case "TemplateConditional": return node.branches.some((b) => b.body.some(hasElementPortal));
|
|
97217
|
+
case "TemplateMatch": return node.branches.some((b) => b.body.some(hasElementPortal)) || node.hostElement !== void 0 && hasElementPortal(node.hostElement);
|
|
97218
|
+
case "TemplateLoop": return node.body.some(hasElementPortal);
|
|
97219
|
+
case "TemplateSlotInvocation": return node.fallback.some(hasElementPortal);
|
|
97220
|
+
default: return false;
|
|
97221
|
+
}
|
|
97222
|
+
}
|
|
96062
97223
|
function emitLit(ir, opts = {}) {
|
|
96063
97224
|
const litImports = new LitImportCollector();
|
|
96064
97225
|
const decoratorImports = new LitDecoratorImportCollector();
|
|
@@ -96079,7 +97240,8 @@ function emitLit(ir, opts = {}) {
|
|
|
96079
97240
|
componentName: ir.name,
|
|
96080
97241
|
lit: litImports,
|
|
96081
97242
|
runtime: runtimeImports,
|
|
96082
|
-
scopeHash
|
|
97243
|
+
scopeHash,
|
|
97244
|
+
hasElementPortal: hasElementPortal(ir.template)
|
|
96083
97245
|
});
|
|
96084
97246
|
diagnostics.push(...styleResult.diagnostics);
|
|
96085
97247
|
const scriptResult = emitScript(ir, {
|
|
@@ -96167,6 +97329,7 @@ function emitLit(ir, opts = {}) {
|
|
|
96167
97329
|
debouncedFieldDecls: templateResult.debouncedFieldDecls.join("\n"),
|
|
96168
97330
|
hoistedLiteralFieldDecls: templateResult.hoistedLiteralFieldDecls.join("\n"),
|
|
96169
97331
|
keynavFieldDecls: templateResult.keynavFieldDecls.join("\n"),
|
|
97332
|
+
portalFieldDecls: templateResult.portalFieldDecls.join("\n"),
|
|
96170
97333
|
slotFillerClassFields: templateResult.slotFillerClassFields.map((f) => " " + f).join("\n"),
|
|
96171
97334
|
slotFields: slotResult.fields,
|
|
96172
97335
|
cleanupField: " private _disconnectCleanups: Array<() => void> = [];\n // Re-parenting guard: set true once the deferred teardown has actually\n // run (a genuine un-mount), so a subsequent reconnect knows to re-arm.\n private _rozieTornDown = false;",
|
|
@@ -96243,6 +97406,7 @@ function composeClassBody(parts) {
|
|
|
96243
97406
|
if (parts.debouncedFieldDecls.trim().length > 0) sections.push(parts.debouncedFieldDecls);
|
|
96244
97407
|
if (parts.hoistedLiteralFieldDecls.trim().length > 0) sections.push(parts.hoistedLiteralFieldDecls);
|
|
96245
97408
|
if (parts.keynavFieldDecls.trim().length > 0) sections.push(parts.keynavFieldDecls);
|
|
97409
|
+
if (parts.portalFieldDecls.trim().length > 0) sections.push(parts.portalFieldDecls);
|
|
96246
97410
|
if (parts.slotFillerClassFields.trim().length > 0) sections.push(parts.slotFillerClassFields);
|
|
96247
97411
|
if (parts.slotFields.trim().length > 0) sections.push(parts.slotFields);
|
|
96248
97412
|
sections.push(parts.cleanupField);
|
|
@@ -96326,7 +97490,8 @@ function buildComponentImportsBlock(components, selfName) {
|
|
|
96326
97490
|
const lines = [];
|
|
96327
97491
|
for (const decl of components) {
|
|
96328
97492
|
if (decl.localName === selfName) continue;
|
|
96329
|
-
|
|
97493
|
+
const importPath = isPublishedSpecifier(decl.importPath) ? rewriteRozieImport(decl.importPath, "lit") : decl.importPath;
|
|
97494
|
+
lines.push(`import '${importPath}';`);
|
|
96330
97495
|
}
|
|
96331
97496
|
if (lines.length === 0) return void 0;
|
|
96332
97497
|
return lines.join("\n") + "\n";
|
|
@@ -96441,9 +97606,9 @@ function compile(source, opts) {
|
|
|
96441
97606
|
});
|
|
96442
97607
|
const acc = [...parseDiags, ...irDiags];
|
|
96443
97608
|
if (!ir) return guardEmpty(fail(acc));
|
|
96444
|
-
threadParamTypes(ir, filename ?? "<anonymous>", cache, resolver, acc);
|
|
97609
|
+
threadParamTypes(ir, filename ?? "<anonymous>", cache, resolver, opts.target, acc);
|
|
96445
97610
|
validatePortalScopedStyle(ir, acc);
|
|
96446
|
-
validateTwoWayBindings(ir, filename ?? "<anonymous>", cache, resolver, acc);
|
|
97611
|
+
validateTwoWayBindings(ir, filename ?? "<anonymous>", cache, resolver, opts.target, acc);
|
|
96447
97612
|
stampMissingFilename(acc, filename);
|
|
96448
97613
|
if (acc.some((d) => d.severity === "error")) return fail(acc);
|
|
96449
97614
|
const emitOpts = {
|