@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.cjs
CHANGED
|
@@ -1592,7 +1592,12 @@ const RozieErrorCode = {
|
|
|
1592
1592
|
KEYNAV_ORPHAN_ITEM: "ROZ984",
|
|
1593
1593
|
KEYNAV_BAD_FOCUS_MODEL: "ROZ985",
|
|
1594
1594
|
KEYNAV_MULTIPLE_ROOTS: "ROZ986",
|
|
1595
|
-
KEYNAV_SOURCE_UNRESOLVED: "ROZ987"
|
|
1595
|
+
KEYNAV_SOURCE_UNRESOLVED: "ROZ987",
|
|
1596
|
+
MANIFEST_SCHEMA_VERSION_MISMATCH: "ROZ988",
|
|
1597
|
+
MALFORMED_MANIFEST: "ROZ989",
|
|
1598
|
+
PORTAL_DIRECTIVE_ON_SLOT: "ROZ990",
|
|
1599
|
+
PORTAL_DIRECTIVE_ON_COMPONENT: "ROZ991",
|
|
1600
|
+
PORTAL_DIRECTIVE_EMPTY_VALUE: "ROZ992"
|
|
1596
1601
|
};
|
|
1597
1602
|
//#endregion
|
|
1598
1603
|
//#region ../core/src/splitter/splitBlocks.ts
|
|
@@ -19047,8 +19052,8 @@ var require_source_map$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
19047
19052
|
//#endregion
|
|
19048
19053
|
//#region ../../node_modules/.pnpm/postcss@8.5.13/node_modules/postcss/lib/previous-map.js
|
|
19049
19054
|
var require_previous_map = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
19050
|
-
let { existsSync: existsSync$1, readFileSync: readFileSync$
|
|
19051
|
-
let { dirname: dirname$
|
|
19055
|
+
let { existsSync: existsSync$1, readFileSync: readFileSync$4 } = require("fs");
|
|
19056
|
+
let { dirname: dirname$5, join: join$2 } = require("path");
|
|
19052
19057
|
let { SourceMapConsumer, SourceMapGenerator } = require_source_map$1();
|
|
19053
19058
|
function fromBase64(str) {
|
|
19054
19059
|
if (Buffer) return Buffer.from(str, "base64").toString();
|
|
@@ -19065,7 +19070,7 @@ var require_previous_map = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19065
19070
|
let prev = opts.map ? opts.map.prev : void 0;
|
|
19066
19071
|
let text = this.loadMap(opts.from, prev);
|
|
19067
19072
|
if (!this.mapFile && opts.from) this.mapFile = opts.from;
|
|
19068
|
-
if (this.mapFile) this.root = dirname$
|
|
19073
|
+
if (this.mapFile) this.root = dirname$5(this.mapFile);
|
|
19069
19074
|
if (text) this.text = text;
|
|
19070
19075
|
}
|
|
19071
19076
|
consumer() {
|
|
@@ -19102,10 +19107,10 @@ var require_previous_map = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19102
19107
|
if (!trusted && !this.unsafeMap) {
|
|
19103
19108
|
if (!/\.map$/i.test(path$5)) return;
|
|
19104
19109
|
}
|
|
19105
|
-
this.root = dirname$
|
|
19110
|
+
this.root = dirname$5(path$5);
|
|
19106
19111
|
if (existsSync$1(path$5)) {
|
|
19107
19112
|
this.mapFile = path$5;
|
|
19108
|
-
return readFileSync$
|
|
19113
|
+
return readFileSync$4(path$5, "utf-8").toString().trim();
|
|
19109
19114
|
}
|
|
19110
19115
|
}
|
|
19111
19116
|
loadMap(file, prev) {
|
|
@@ -19125,7 +19130,7 @@ var require_previous_map = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19125
19130
|
else if (this.inline) return this.decodeInline(this.annotation);
|
|
19126
19131
|
else if (this.annotation) {
|
|
19127
19132
|
let map = this.annotation;
|
|
19128
|
-
if (file) map = join$
|
|
19133
|
+
if (file) map = join$2(dirname$5(file), map);
|
|
19129
19134
|
let unknown = this.loadFile(map, file, false);
|
|
19130
19135
|
if (unknown) try {
|
|
19131
19136
|
/* c8 ignore next 4 */
|
|
@@ -19497,12 +19502,12 @@ var require_fromJSON = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19497
19502
|
//#endregion
|
|
19498
19503
|
//#region ../../node_modules/.pnpm/postcss@8.5.13/node_modules/postcss/lib/map-generator.js
|
|
19499
19504
|
var require_map_generator = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
19500
|
-
let { dirname: dirname$
|
|
19505
|
+
let { dirname: dirname$4, relative, resolve: resolve$1, sep } = require("path");
|
|
19501
19506
|
let { SourceMapConsumer, SourceMapGenerator } = require_source_map$1();
|
|
19502
19507
|
let { pathToFileURL } = require("url");
|
|
19503
19508
|
let Input = require_input();
|
|
19504
19509
|
let sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator);
|
|
19505
|
-
let pathAvailable = Boolean(dirname$
|
|
19510
|
+
let pathAvailable = Boolean(dirname$4 && resolve$1 && relative && sep);
|
|
19506
19511
|
var MapGenerator = class {
|
|
19507
19512
|
constructor(stringify, root, opts, cssString) {
|
|
19508
19513
|
this.stringify = stringify;
|
|
@@ -19529,7 +19534,7 @@ var require_map_generator = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
19529
19534
|
applyPrevMaps() {
|
|
19530
19535
|
for (let prev of this.previous()) {
|
|
19531
19536
|
let from = this.toUrl(this.path(prev.file));
|
|
19532
|
-
let root = prev.root || dirname$
|
|
19537
|
+
let root = prev.root || dirname$4(prev.file);
|
|
19533
19538
|
let map;
|
|
19534
19539
|
if (this.mapOpts.sourcesContent === false) {
|
|
19535
19540
|
map = new SourceMapConsumer(prev.text);
|
|
@@ -19694,8 +19699,8 @@ var require_map_generator = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
19694
19699
|
if (/^\w+:\/\//.test(file)) return file;
|
|
19695
19700
|
let cached = this.memoizedPaths.get(file);
|
|
19696
19701
|
if (cached) return cached;
|
|
19697
|
-
let from = this.opts.to ? dirname$
|
|
19698
|
-
if (typeof this.mapOpts.annotation === "string") from = dirname$
|
|
19702
|
+
let from = this.opts.to ? dirname$4(this.opts.to) : ".";
|
|
19703
|
+
if (typeof this.mapOpts.annotation === "string") from = dirname$4(resolve$1(from, this.mapOpts.annotation));
|
|
19699
19704
|
let path$2 = relative(from, file);
|
|
19700
19705
|
this.memoizedPaths.set(file, path$2);
|
|
19701
19706
|
return path$2;
|
|
@@ -41266,6 +41271,13 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41266
41271
|
const args = [];
|
|
41267
41272
|
for (const attr of el.attributes) if (attr.kind === "static" && attr.name === "name" && attr.value !== null) slotName = attr.value;
|
|
41268
41273
|
else if (attr.kind === "static" && attr.name === "portal" && attr.value === null) isPortal = true;
|
|
41274
|
+
else if (attr.kind === "directive" && attr.name === "portal") diagnostics.push({
|
|
41275
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_ON_SLOT,
|
|
41276
|
+
severity: "error",
|
|
41277
|
+
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).",
|
|
41278
|
+
loc: attr.loc,
|
|
41279
|
+
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."
|
|
41280
|
+
});
|
|
41269
41281
|
else if (attr.kind === "binding" && attr.value !== null) {
|
|
41270
41282
|
if (attr.name === "params") continue;
|
|
41271
41283
|
const expr = tryParseExpression(attr.value);
|
|
@@ -41296,6 +41308,8 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41296
41308
|
let keynavItem;
|
|
41297
41309
|
let keynavActiveClassExpression;
|
|
41298
41310
|
let keynavActiveClassDeps;
|
|
41311
|
+
let portalToExpression;
|
|
41312
|
+
let portalToDeps;
|
|
41299
41313
|
for (const attr of el.attributes) {
|
|
41300
41314
|
if (attr.kind === "event") {
|
|
41301
41315
|
const handlerExpr = attr.value !== null ? tryParseExpression(attr.value) : null;
|
|
@@ -41505,6 +41519,34 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41505
41519
|
isExternal = true;
|
|
41506
41520
|
continue;
|
|
41507
41521
|
}
|
|
41522
|
+
if (attr.name === "portal") {
|
|
41523
|
+
if (attr.value === null || attr.value.trim() === "") {
|
|
41524
|
+
diagnostics.push({
|
|
41525
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_EMPTY_VALUE,
|
|
41526
|
+
severity: "error",
|
|
41527
|
+
message: "r-portal requires a container expression (e.g. r-portal=\"$props.appendTo\") — an empty value is not a valid portal target.",
|
|
41528
|
+
loc: attr.loc,
|
|
41529
|
+
hint: "Provide an expression that evaluates to an Element (or a falsy value / null to render in place)."
|
|
41530
|
+
});
|
|
41531
|
+
continue;
|
|
41532
|
+
}
|
|
41533
|
+
if (elementIsComponent) {
|
|
41534
|
+
diagnostics.push({
|
|
41535
|
+
code: RozieErrorCode.PORTAL_DIRECTIVE_ON_COMPONENT,
|
|
41536
|
+
severity: "error",
|
|
41537
|
+
message: "r-portal is not supported on a <components>-registered child component element (v1 limitation) — only plain/host elements can be portalled.",
|
|
41538
|
+
loc: attr.loc,
|
|
41539
|
+
hint: "Wrap the component in a plain host element and put r-portal on that wrapper instead."
|
|
41540
|
+
});
|
|
41541
|
+
continue;
|
|
41542
|
+
}
|
|
41543
|
+
const expr = tryParseExpression(attr.value);
|
|
41544
|
+
if (expr) {
|
|
41545
|
+
portalToExpression = expr;
|
|
41546
|
+
portalToDeps = computeExpressionDeps(expr, bindings);
|
|
41547
|
+
}
|
|
41548
|
+
continue;
|
|
41549
|
+
}
|
|
41508
41550
|
if ((attr.name === "model" || attr.name === "show" || attr.name === "html" || attr.name === "text") && attr.value !== null) {
|
|
41509
41551
|
const expr = tryParseExpression(attr.value);
|
|
41510
41552
|
const modifiers = attr.name === "model" ? resolveModelModifiers(attr.chain, registry, diagnostics) : [];
|
|
@@ -41556,7 +41598,11 @@ function lowerBareElement(el, bindings, depGraph, registry, diagnostics, templat
|
|
|
41556
41598
|
...isExternal ? { isExternal: true } : {},
|
|
41557
41599
|
...remountKeyExpression !== void 0 ? { remountKeyExpression } : {},
|
|
41558
41600
|
...keynavRoot !== void 0 ? { keynavRoot } : {},
|
|
41559
|
-
...keynavItem !== void 0 ? { keynavItem } : {}
|
|
41601
|
+
...keynavItem !== void 0 ? { keynavItem } : {},
|
|
41602
|
+
...portalToExpression !== void 0 ? { portalTo: {
|
|
41603
|
+
expression: portalToExpression,
|
|
41604
|
+
deps: portalToDeps ?? []
|
|
41605
|
+
} } : {}
|
|
41560
41606
|
};
|
|
41561
41607
|
}
|
|
41562
41608
|
/**
|
|
@@ -55882,6 +55928,340 @@ var IRCache = class {
|
|
|
55882
55928
|
}
|
|
55883
55929
|
};
|
|
55884
55930
|
//#endregion
|
|
55931
|
+
//#region ../core/src/manifest/buildManifest.ts
|
|
55932
|
+
var import_lib = /* @__PURE__ */ __toESM(require_lib$6(), 1);
|
|
55933
|
+
typeof import_lib.default === "function" ? import_lib.default : import_lib.default.default;
|
|
55934
|
+
//#endregion
|
|
55935
|
+
//#region ../core/src/manifest/readManifest.ts
|
|
55936
|
+
/**
|
|
55937
|
+
* parseManifest(raw) — validate + deserialize a published-primitive manifest
|
|
55938
|
+
* into a producer slot/prop/expose surface (Phase 75, D-04).
|
|
55939
|
+
*
|
|
55940
|
+
* Fails CLOSED on any trust-boundary violation (T-75-01): an unknown or
|
|
55941
|
+
* mismatched `schemaVersion` — or any structurally malformed field — never
|
|
55942
|
+
* silently degrades into a partial/best-effort surface. Both failure modes
|
|
55943
|
+
* return `{ surface: null, error }`; parseManifest NEVER throws (T-75-02: a
|
|
55944
|
+
* crafted/oversized paramType source string is dropped per-entry, not fatal).
|
|
55945
|
+
*
|
|
55946
|
+
* Per T-75-03, every field is read via explicit key access into a FRESH
|
|
55947
|
+
* object — parseManifest never `Object.assign`/spreads the parsed JSON into
|
|
55948
|
+
* a shared target, so a `__proto__`/`constructor`-keyed manifest cannot
|
|
55949
|
+
* pollute anything beyond the literal-keyed surface it produces.
|
|
55950
|
+
*
|
|
55951
|
+
* @experimental — shape may change before v1.0
|
|
55952
|
+
*/
|
|
55953
|
+
function malformed(message) {
|
|
55954
|
+
return {
|
|
55955
|
+
surface: null,
|
|
55956
|
+
error: {
|
|
55957
|
+
code: RozieErrorCode.MALFORMED_MANIFEST,
|
|
55958
|
+
message
|
|
55959
|
+
}
|
|
55960
|
+
};
|
|
55961
|
+
}
|
|
55962
|
+
function schemaMismatch(message) {
|
|
55963
|
+
return {
|
|
55964
|
+
surface: null,
|
|
55965
|
+
error: {
|
|
55966
|
+
code: RozieErrorCode.MANIFEST_SCHEMA_VERSION_MISMATCH,
|
|
55967
|
+
message
|
|
55968
|
+
}
|
|
55969
|
+
};
|
|
55970
|
+
}
|
|
55971
|
+
function isPlainObject(v) {
|
|
55972
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
55973
|
+
}
|
|
55974
|
+
/**
|
|
55975
|
+
* Deserialize one paramType SOURCE STRING back into a `TSType` by parsing
|
|
55976
|
+
* `type __RozieParam = <src>;` and lifting the type-alias's typeAnnotation.
|
|
55977
|
+
* Wrapped in try/catch — a garbage/oversized string is dropped (returns
|
|
55978
|
+
* `null`) rather than throwing out of parseManifest (T-75-02).
|
|
55979
|
+
*/
|
|
55980
|
+
function deserializeParamType(src) {
|
|
55981
|
+
try {
|
|
55982
|
+
const stmt = (0, import_lib$3.parse)(`type __RozieParam = ${src};`, {
|
|
55983
|
+
sourceType: "module",
|
|
55984
|
+
plugins: ["typescript"]
|
|
55985
|
+
}).program.body[0];
|
|
55986
|
+
if (!stmt || !_babel_types.isTSTypeAliasDeclaration(stmt)) return null;
|
|
55987
|
+
return stmt.typeAnnotation;
|
|
55988
|
+
} catch {
|
|
55989
|
+
return null;
|
|
55990
|
+
}
|
|
55991
|
+
}
|
|
55992
|
+
/** Validate + rebuild one manifest prop entry. Returns null on structural violation. */
|
|
55993
|
+
function readProp(raw) {
|
|
55994
|
+
if (!isPlainObject(raw)) return null;
|
|
55995
|
+
const name = raw.name;
|
|
55996
|
+
const isModel = raw.isModel;
|
|
55997
|
+
if (typeof name !== "string") return null;
|
|
55998
|
+
if (typeof isModel !== "boolean") return null;
|
|
55999
|
+
return {
|
|
56000
|
+
name,
|
|
56001
|
+
isModel
|
|
56002
|
+
};
|
|
56003
|
+
}
|
|
56004
|
+
/** Validate + rebuild one manifest slot-param entry. Returns null on structural violation. */
|
|
56005
|
+
function readSlotParam(raw) {
|
|
56006
|
+
if (!isPlainObject(raw)) return null;
|
|
56007
|
+
const name = raw.name;
|
|
56008
|
+
if (typeof name !== "string") return null;
|
|
56009
|
+
return {
|
|
56010
|
+
type: "ParamDecl",
|
|
56011
|
+
name,
|
|
56012
|
+
valueExpression: _babel_types.identifier(name),
|
|
56013
|
+
sourceLoc: {
|
|
56014
|
+
start: 0,
|
|
56015
|
+
end: 0
|
|
56016
|
+
}
|
|
56017
|
+
};
|
|
56018
|
+
}
|
|
56019
|
+
/** Validate + rebuild one manifest slot entry into a minimal SlotDecl. */
|
|
56020
|
+
function readSlot(raw) {
|
|
56021
|
+
if (!isPlainObject(raw)) return null;
|
|
56022
|
+
const name = raw.name;
|
|
56023
|
+
const paramsRaw = raw.params;
|
|
56024
|
+
const paramTypesRaw = raw.paramTypes;
|
|
56025
|
+
const isPortal = raw.isPortal;
|
|
56026
|
+
const isReactive = raw.isReactive;
|
|
56027
|
+
if (typeof name !== "string") return null;
|
|
56028
|
+
if (!Array.isArray(paramsRaw)) return null;
|
|
56029
|
+
if (paramTypesRaw !== null && !Array.isArray(paramTypesRaw)) return null;
|
|
56030
|
+
if (typeof isPortal !== "boolean") return null;
|
|
56031
|
+
if (typeof isReactive !== "boolean") return null;
|
|
56032
|
+
const params = [];
|
|
56033
|
+
for (const p of paramsRaw) {
|
|
56034
|
+
const param = readSlotParam(p);
|
|
56035
|
+
if (param === null) return null;
|
|
56036
|
+
params.push(param);
|
|
56037
|
+
}
|
|
56038
|
+
let paramTypes;
|
|
56039
|
+
if (paramTypesRaw !== null) {
|
|
56040
|
+
const deserialized = [];
|
|
56041
|
+
for (const src of paramTypesRaw) {
|
|
56042
|
+
if (typeof src !== "string") continue;
|
|
56043
|
+
const ty = deserializeParamType(src);
|
|
56044
|
+
if (ty !== null) deserialized.push(ty);
|
|
56045
|
+
}
|
|
56046
|
+
paramTypes = deserialized;
|
|
56047
|
+
}
|
|
56048
|
+
return {
|
|
56049
|
+
type: "SlotDecl",
|
|
56050
|
+
name,
|
|
56051
|
+
defaultContent: null,
|
|
56052
|
+
params,
|
|
56053
|
+
...paramTypes !== void 0 ? { paramTypes } : {},
|
|
56054
|
+
presence: "always",
|
|
56055
|
+
nestedSlots: [],
|
|
56056
|
+
sourceLoc: {
|
|
56057
|
+
start: 0,
|
|
56058
|
+
end: 0
|
|
56059
|
+
},
|
|
56060
|
+
isPortal,
|
|
56061
|
+
isReactive
|
|
56062
|
+
};
|
|
56063
|
+
}
|
|
56064
|
+
/** Validate + rebuild one manifest expose entry. Returns null on structural violation. */
|
|
56065
|
+
function readExposeMember(raw) {
|
|
56066
|
+
if (!isPlainObject(raw)) return null;
|
|
56067
|
+
const name = raw.name;
|
|
56068
|
+
if (typeof name !== "string") return null;
|
|
56069
|
+
return { name };
|
|
56070
|
+
}
|
|
56071
|
+
/**
|
|
56072
|
+
* Validate a manifest's `schemaVersion` FIRST (fail closed, D-04), then
|
|
56073
|
+
* shape-validate props/slots/emits/expose and deserialize into a
|
|
56074
|
+
* `ProducerSurface` consumable by `threadParamTypes` / `validateTwoWayBindings`.
|
|
56075
|
+
* Never throws (T-75-01/T-75-02).
|
|
56076
|
+
*/
|
|
56077
|
+
function parseManifest(raw, _opts) {
|
|
56078
|
+
if (!isPlainObject(raw)) return malformed("Manifest is not a JSON object.");
|
|
56079
|
+
const schemaVersion = raw.schemaVersion;
|
|
56080
|
+
if (typeof schemaVersion !== "number") return malformed("Manifest is missing a numeric \"schemaVersion\" field.");
|
|
56081
|
+
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.`);
|
|
56082
|
+
const propsRaw = raw.props;
|
|
56083
|
+
const slotsRaw = raw.slots;
|
|
56084
|
+
const emitsRaw = raw.emits;
|
|
56085
|
+
const exposeRaw = raw.expose;
|
|
56086
|
+
if (!Array.isArray(propsRaw)) return malformed("Manifest \"props\" is not an array.");
|
|
56087
|
+
if (!Array.isArray(slotsRaw)) return malformed("Manifest \"slots\" is not an array.");
|
|
56088
|
+
if (!Array.isArray(emitsRaw)) return malformed("Manifest \"emits\" is not an array.");
|
|
56089
|
+
if (!Array.isArray(exposeRaw)) return malformed("Manifest \"expose\" is not an array.");
|
|
56090
|
+
const props = [];
|
|
56091
|
+
for (const p of propsRaw) {
|
|
56092
|
+
const prop = readProp(p);
|
|
56093
|
+
if (prop === null) return malformed("Manifest \"props\" entry is malformed.");
|
|
56094
|
+
props.push(prop);
|
|
56095
|
+
}
|
|
56096
|
+
const slots = [];
|
|
56097
|
+
for (const s of slotsRaw) {
|
|
56098
|
+
const slot = readSlot(s);
|
|
56099
|
+
if (slot === null) return malformed("Manifest \"slots\" entry is malformed.");
|
|
56100
|
+
slots.push(slot);
|
|
56101
|
+
}
|
|
56102
|
+
for (const e of emitsRaw) if (typeof e !== "string") return malformed("Manifest \"emits\" entry is not a string.");
|
|
56103
|
+
const expose = [];
|
|
56104
|
+
for (const m of exposeRaw) {
|
|
56105
|
+
const member = readExposeMember(m);
|
|
56106
|
+
if (member === null) return malformed("Manifest \"expose\" entry is malformed.");
|
|
56107
|
+
expose.push(member);
|
|
56108
|
+
}
|
|
56109
|
+
return {
|
|
56110
|
+
surface: {
|
|
56111
|
+
slots,
|
|
56112
|
+
props,
|
|
56113
|
+
expose
|
|
56114
|
+
},
|
|
56115
|
+
error: null
|
|
56116
|
+
};
|
|
56117
|
+
}
|
|
56118
|
+
//#endregion
|
|
56119
|
+
//#region ../core/src/manifest/resolveManifestProducer.ts
|
|
56120
|
+
/**
|
|
56121
|
+
* resolveManifestProducer — Phase 75 Plan 02 (D-08/D-09/D-10).
|
|
56122
|
+
*
|
|
56123
|
+
* Wires the manifest contract (Plan 01's `buildManifest`/`parseManifest`)
|
|
56124
|
+
* into `@rozie/core`'s producer-resolution seam so a PUBLISHED cross-package
|
|
56125
|
+
* `<components>` specifier resolves a producer contract from a compiled
|
|
56126
|
+
* `rozie-manifest.json` instead of a `.rozie` source file.
|
|
56127
|
+
*
|
|
56128
|
+
* **COMP-04 bounded exception**: local `.rozie` composition (relative /
|
|
56129
|
+
* tsconfig-alias specifiers) still needs NO cross-`.rozie` IR type info and
|
|
56130
|
+
* keeps reading on-disk `.rozie` source unchanged via the existing
|
|
56131
|
+
* `resolver.resolveProducerPath` + `IRCache.getIRComponent` path. ONLY
|
|
56132
|
+
* published cross-PACKAGE specifiers (D-08's `@scope/pkg/Component.rozie`
|
|
56133
|
+
* shape) read this compiled manifest artifact. This module and its two
|
|
56134
|
+
* call-sites (threadParamTypes.ts, validateTwoWayBindings.ts) are the entire
|
|
56135
|
+
* surface of that exception.
|
|
56136
|
+
*
|
|
56137
|
+
* Convention-based per-target package derivation (D-08): for a published
|
|
56138
|
+
* specifier `@scope/pkg/Component.rozie`, the trailing `/Component.rozie`
|
|
56139
|
+
* segment is stripped to yield the base package `@scope/pkg`, then
|
|
56140
|
+
* `-${target}` is appended to yield the derived per-target package name
|
|
56141
|
+
* (e.g. `@rozie-ui/combobox` + `-react` → `@rozie-ui/combobox-react`).
|
|
56142
|
+
*
|
|
56143
|
+
* Location (D-10): the derived package is located via the SAME
|
|
56144
|
+
* `enhanced-resolve` layer `ProducerResolver` already uses for `.rozie`
|
|
56145
|
+
* producer resolution — walking `node_modules` from the consumer file's
|
|
56146
|
+
* directory (NOT from `ResolverOptions.root`, which feeds only the
|
|
56147
|
+
* tsconfig-paths matcher). Because a real per-target leaf's `package.json`
|
|
56148
|
+
* `exports` map does not (and need not) list `"./package.json"` or
|
|
56149
|
+
* `"./rozie-manifest.json"` as exported subpaths, this resolver deliberately
|
|
56150
|
+
* resolves the package's bare `"."` export entry (its normal `import`
|
|
56151
|
+
* condition target, e.g. `dist/index.mjs`) and then walks UP from that
|
|
56152
|
+
* resolved file to the nearest ancestor directory whose `package.json`
|
|
56153
|
+
* `name` matches the derived package name (T-75-04/T-75-05 — bounded walk,
|
|
56154
|
+
* name-verified, never an unauthenticated `../` traversal). The manifest is
|
|
56155
|
+
* then read at a FIXED literal relative path inside that directory
|
|
56156
|
+
* (`rozie-manifest.json`) — no specifier-derived path segment ever reaches
|
|
56157
|
+
* `readFileSync` (T-75-04).
|
|
56158
|
+
*
|
|
56159
|
+
* @experimental — shape may change before v1.0
|
|
56160
|
+
*/
|
|
56161
|
+
/** Tsconfig-alias-shaped prefix (empty npm scope, e.g. `@/components/Modal.rozie`). */
|
|
56162
|
+
const TSCONFIG_ALIAS_PREFIX$1 = "@/";
|
|
56163
|
+
/**
|
|
56164
|
+
* True iff `specifier` is a PUBLISHED cross-package `<components>` specifier
|
|
56165
|
+
* (D-08 shape): ends in `.rozie`, is neither a relative/absolute path nor a
|
|
56166
|
+
* tsconfig `paths`-alias-shaped specifier.
|
|
56167
|
+
*
|
|
56168
|
+
* `isPublishedSpecifier` is a pure, syntactic classifier — it does not itself
|
|
56169
|
+
* consult a real tsconfig (that happens inside `ProducerResolver`'s local
|
|
56170
|
+
* COMP-04 path when this returns `false`). A leading `@/` is NOT a valid npm
|
|
56171
|
+
* scoped-package prefix (npm scopes require at least one character between
|
|
56172
|
+
* `@` and the following `/`), so it is the reliable syntactic signal that a
|
|
56173
|
+
* `@/...` specifier is tsconfig-alias-shaped rather than a real scoped
|
|
56174
|
+
* package — matching `ProducerResolver`'s own documented `@/components/Modal.rozie`
|
|
56175
|
+
* example of a tsconfig-paths alias.
|
|
56176
|
+
*/
|
|
56177
|
+
function isPublishedSpecifier$1(specifier) {
|
|
56178
|
+
if (!specifier.endsWith(".rozie")) return false;
|
|
56179
|
+
if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
|
|
56180
|
+
if (specifier.startsWith(TSCONFIG_ALIAS_PREFIX$1)) return false;
|
|
56181
|
+
return true;
|
|
56182
|
+
}
|
|
56183
|
+
/**
|
|
56184
|
+
* Strip the trailing `/<Component>.rozie` leaf off a published specifier to
|
|
56185
|
+
* derive the base package name (e.g. `@rozie-ui/combobox/Combobox.rozie` →
|
|
56186
|
+
* `@rozie-ui/combobox`). Returns `null` when the specifier has no `/`
|
|
56187
|
+
* separating a package from a leaf (malformed — never a valid published
|
|
56188
|
+
* specifier in practice, since `isPublishedSpecifier` requires `.rozie`).
|
|
56189
|
+
*/
|
|
56190
|
+
function deriveBasePackageName(specifier) {
|
|
56191
|
+
const lastSlash = specifier.lastIndexOf("/");
|
|
56192
|
+
if (lastSlash <= 0) return null;
|
|
56193
|
+
return specifier.slice(0, lastSlash);
|
|
56194
|
+
}
|
|
56195
|
+
/**
|
|
56196
|
+
* Walk UP from a resolved entry-file path to the nearest ancestor directory
|
|
56197
|
+
* whose `package.json` `name` field equals `expectedName` AND which carries a
|
|
56198
|
+
* sibling `rozie-manifest.json`. Bounded to 10 levels (T-75-05 — a real
|
|
56199
|
+
* per-target leaf's package root sits 1-3 levels above its resolved `dist/*`
|
|
56200
|
+
* entry; 10 is a generous, still-finite bound that can never escape into an
|
|
56201
|
+
* unrelated ancestor tree undetected, since EVERY level is name-verified
|
|
56202
|
+
* before being accepted).
|
|
56203
|
+
*
|
|
56204
|
+
* Requiring the manifest sibling (not just a name match) is deliberate: some
|
|
56205
|
+
* per-target build tooling generates a SECONDARY `package.json` inside the
|
|
56206
|
+
* `dist/` output with the SAME `name` field as the real package root (e.g.
|
|
56207
|
+
* `ng-packagr`'s Angular `Package Format v9+` legacy-entry-point convention
|
|
56208
|
+
* emits `dist/package.json`). A name-only match would stop at that inner
|
|
56209
|
+
* duplicate — one level too shallow — and never reach the real package root
|
|
56210
|
+
* where `rozie-manifest.json` actually lives (only shipped at the root,
|
|
56211
|
+
* sibling to the AUTHORED `package.json`, never duplicated into `dist/`).
|
|
56212
|
+
* Continuing the walk past a name-matched-but-manifest-less directory finds
|
|
56213
|
+
* the correct root for every current `@rozie-ui/*-<target>` leaf shape.
|
|
56214
|
+
*/
|
|
56215
|
+
function findPackageRoot(entryFilePath, expectedName) {
|
|
56216
|
+
let dir = (0, node_path.dirname)(entryFilePath);
|
|
56217
|
+
for (let i = 0; i < 10; i++) {
|
|
56218
|
+
try {
|
|
56219
|
+
const raw = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(dir, "package.json"), "utf-8"));
|
|
56220
|
+
if (typeof raw === "object" && raw !== null && raw.name === expectedName) try {
|
|
56221
|
+
(0, node_fs.readFileSync)((0, node_path.join)(dir, "rozie-manifest.json"), "utf-8");
|
|
56222
|
+
return dir;
|
|
56223
|
+
} catch {}
|
|
56224
|
+
} catch {}
|
|
56225
|
+
const parent = (0, node_path.dirname)(dir);
|
|
56226
|
+
if (parent === dir) return null;
|
|
56227
|
+
dir = parent;
|
|
56228
|
+
}
|
|
56229
|
+
return null;
|
|
56230
|
+
}
|
|
56231
|
+
function lookupFailed(pkgLabel) {
|
|
56232
|
+
return {
|
|
56233
|
+
surface: null,
|
|
56234
|
+
error: {
|
|
56235
|
+
code: RozieErrorCode.CROSS_PACKAGE_LOOKUP_FAILED,
|
|
56236
|
+
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.`
|
|
56237
|
+
}
|
|
56238
|
+
};
|
|
56239
|
+
}
|
|
56240
|
+
/**
|
|
56241
|
+
* Derive the per-target package for a published `<components>` specifier,
|
|
56242
|
+
* locate its installed `rozie-manifest.json` via node module resolution, and
|
|
56243
|
+
* validate + deserialize it into a `ProducerSurface` (D-08/D-09/D-10/D-04).
|
|
56244
|
+
*
|
|
56245
|
+
* Never throws — every failure mode returns `{ surface: null, error }`.
|
|
56246
|
+
*/
|
|
56247
|
+
function resolveManifestProducer(args) {
|
|
56248
|
+
const { specifier, target, fromFile, resolver } = args;
|
|
56249
|
+
const basePkg = deriveBasePackageName(specifier);
|
|
56250
|
+
if (basePkg === null) return lookupFailed(specifier);
|
|
56251
|
+
const derivedPkg = `${basePkg}-${target}`;
|
|
56252
|
+
const entryFilePath = resolver.resolveProducerPath(derivedPkg, fromFile);
|
|
56253
|
+
if (entryFilePath === null) return lookupFailed(derivedPkg);
|
|
56254
|
+
const packageDir = findPackageRoot(entryFilePath, derivedPkg);
|
|
56255
|
+
if (packageDir === null) return lookupFailed(derivedPkg);
|
|
56256
|
+
let raw;
|
|
56257
|
+
try {
|
|
56258
|
+
raw = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(packageDir, "rozie-manifest.json"), "utf-8"));
|
|
56259
|
+
} catch {
|
|
56260
|
+
return lookupFailed(derivedPkg);
|
|
56261
|
+
}
|
|
56262
|
+
return parseManifest(raw, { packageName: derivedPkg });
|
|
56263
|
+
}
|
|
56264
|
+
//#endregion
|
|
55885
56265
|
//#region ../core/src/ir/threadParamTypes.ts
|
|
55886
56266
|
/**
|
|
55887
56267
|
* Recursive template walker: visit every TemplateNode in source order.
|
|
@@ -55966,9 +56346,11 @@ function bodyContainsReprojection(body) {
|
|
|
55966
56346
|
* the cache reverse-dep edge + resolver `fromFile` argument)
|
|
55967
56347
|
* @param cache - per-compiler-instance IRCache (D-01)
|
|
55968
56348
|
* @param resolver - per-compiler-instance ProducerResolver (D-02 / D-12)
|
|
56349
|
+
* @param target - the per-call compile target (Phase 75 Plan 02 —
|
|
56350
|
+
* selects the derived per-target package for a PUBLISHED specifier; D-09)
|
|
55969
56351
|
* @param diagnostics - accumulator
|
|
55970
56352
|
*/
|
|
55971
|
-
function threadParamTypes(ir, consumerPath, cache, resolver, diagnostics) {
|
|
56353
|
+
function threadParamTypes(ir, consumerPath, cache, resolver, target, diagnostics) {
|
|
55972
56354
|
const declaredSlotNames = new Set(ir.slots.map((s) => s.name));
|
|
55973
56355
|
const { reprojections } = collectSlotInvocationsByContext(ir.template);
|
|
55974
56356
|
for (const reproj of reprojections) if (!declaredSlotNames.has(reproj.slotName)) diagnostics.push({
|
|
@@ -55988,6 +56370,26 @@ function threadParamTypes(ir, consumerPath, cache, resolver, diagnostics) {
|
|
|
55988
56370
|
if (node.tagKind === "self") {
|
|
55989
56371
|
producerSlots = ir.slots;
|
|
55990
56372
|
producerProps = ir.props;
|
|
56373
|
+
} else if (isPublishedSpecifier$1(node.componentRef.importPath)) {
|
|
56374
|
+
const { surface, error } = resolveManifestProducer({
|
|
56375
|
+
specifier: node.componentRef.importPath,
|
|
56376
|
+
target,
|
|
56377
|
+
fromFile: consumerPath,
|
|
56378
|
+
resolver
|
|
56379
|
+
});
|
|
56380
|
+
if (error !== null) {
|
|
56381
|
+
diagnostics.push({
|
|
56382
|
+
code: error.code,
|
|
56383
|
+
severity: "error",
|
|
56384
|
+
message: error.message,
|
|
56385
|
+
loc: node.componentRef.sourceLoc,
|
|
56386
|
+
hint: "Verify the per-target package is installed as a dependency and ships a compatible rozie-manifest.json."
|
|
56387
|
+
});
|
|
56388
|
+
return;
|
|
56389
|
+
}
|
|
56390
|
+
if (surface === null) return;
|
|
56391
|
+
producerSlots = surface.slots;
|
|
56392
|
+
producerProps = surface.props;
|
|
55991
56393
|
} else {
|
|
55992
56394
|
const resolvedPath = resolver.resolveProducerPath(node.componentRef.importPath, consumerPath);
|
|
55993
56395
|
if (resolvedPath === null) {
|
|
@@ -56192,9 +56594,11 @@ function walkTemplate(node, visit) {
|
|
|
56192
56594
|
* @param cache - per-compiler-instance IRCache (D-01) — shared with
|
|
56193
56595
|
* threadParamTypes so producers parsed once during threading are reused here
|
|
56194
56596
|
* @param resolver - per-compiler-instance ProducerResolver (D-02 / D-12)
|
|
56597
|
+
* @param target - the per-call compile target (Phase 75 Plan 02 —
|
|
56598
|
+
* selects the derived per-target package for a PUBLISHED specifier; D-09)
|
|
56195
56599
|
* @param diagnostics - accumulator (mutated in place; ROZ949/950/951/945 pushed)
|
|
56196
56600
|
*/
|
|
56197
|
-
function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics) {
|
|
56601
|
+
function validateTwoWayBindings(ir, consumerPath, cache, resolver, target, diagnostics) {
|
|
56198
56602
|
walkTemplate(ir.template, (node) => {
|
|
56199
56603
|
if (node.type !== "TemplateElement") return;
|
|
56200
56604
|
const twoWayAttrs = node.attributes.filter((a) => a.kind === "twoWayBinding");
|
|
@@ -56209,6 +56613,27 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56209
56613
|
return producerProps;
|
|
56210
56614
|
}
|
|
56211
56615
|
if (node.tagKind !== "component" || !node.componentRef) return null;
|
|
56616
|
+
if (isPublishedSpecifier$1(node.componentRef.importPath)) {
|
|
56617
|
+
const { surface, error } = resolveManifestProducer({
|
|
56618
|
+
specifier: node.componentRef.importPath,
|
|
56619
|
+
target,
|
|
56620
|
+
fromFile: consumerPath,
|
|
56621
|
+
resolver
|
|
56622
|
+
});
|
|
56623
|
+
if (error !== null) {
|
|
56624
|
+
diagnostics.push({
|
|
56625
|
+
code: error.code,
|
|
56626
|
+
severity: "error",
|
|
56627
|
+
message: error.message,
|
|
56628
|
+
loc: node.componentRef.sourceLoc,
|
|
56629
|
+
hint: "Verify the per-target package is installed as a dependency and ships a compatible rozie-manifest.json."
|
|
56630
|
+
});
|
|
56631
|
+
return null;
|
|
56632
|
+
}
|
|
56633
|
+
if (surface === null) return null;
|
|
56634
|
+
producerProps = surface.props;
|
|
56635
|
+
return producerProps;
|
|
56636
|
+
}
|
|
56212
56637
|
const resolvedPath = resolver.resolveProducerPath(node.componentRef.importPath, consumerPath);
|
|
56213
56638
|
if (resolvedPath === null) {
|
|
56214
56639
|
diagnostics.push({
|
|
@@ -56263,6 +56688,7 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56263
56688
|
if (producerProp === void 0) continue;
|
|
56264
56689
|
if (!producerProp.isModel) {
|
|
56265
56690
|
const producerImportPath = node.tagKind === "self" ? "(self-reference)" : node.componentRef?.importPath ?? "(unknown)";
|
|
56691
|
+
const relatedLoc = producerProp.sourceLoc ?? node.componentRef?.sourceLoc ?? attr.sourceLoc;
|
|
56266
56692
|
diagnostics.push({
|
|
56267
56693
|
code: RozieErrorCode.TWO_WAY_PROP_NOT_MODEL,
|
|
56268
56694
|
severity: "error",
|
|
@@ -56271,7 +56697,7 @@ function validateTwoWayBindings(ir, consumerPath, cache, resolver, diagnostics)
|
|
|
56271
56697
|
hint: `Add 'model: true' to the '${attr.name}' prop declaration on ${producerImportPath}, or pass a one-way value via :${attr.name}="…" instead.`,
|
|
56272
56698
|
related: [{
|
|
56273
56699
|
message: `'${attr.name}' declared here without model: true`,
|
|
56274
|
-
loc:
|
|
56700
|
+
loc: relatedLoc
|
|
56275
56701
|
}]
|
|
56276
56702
|
});
|
|
56277
56703
|
continue;
|
|
@@ -56582,10 +57008,37 @@ const TARGET_EXT_MAP = {
|
|
|
56582
57008
|
lit: ""
|
|
56583
57009
|
};
|
|
56584
57010
|
const ROZIE_EXT$1 = ".rozie";
|
|
57011
|
+
/** Tsconfig-alias-shaped prefix (empty npm scope, e.g. `@/components/Modal.rozie`). */
|
|
57012
|
+
const TSCONFIG_ALIAS_PREFIX = "@/";
|
|
57013
|
+
/**
|
|
57014
|
+
* True iff `importPath` is a PUBLISHED cross-package `<components>`
|
|
57015
|
+
* specifier (D-08 shape): ends in `.rozie`, is neither a relative/absolute
|
|
57016
|
+
* path nor a tsconfig `paths`-alias-shaped specifier. Mirrors
|
|
57017
|
+
* `resolveManifestProducer.ts`'s `isPublishedSpecifier` (kept duplicated,
|
|
57018
|
+
* not imported, so this module stays dependency-free — both are tiny, pure,
|
|
57019
|
+
* syntactic classifiers over the same D-08 specifier shape and must be kept
|
|
57020
|
+
* in lockstep if that shape ever changes).
|
|
57021
|
+
*
|
|
57022
|
+
* Exported so callers that do NOT already route an import path through
|
|
57023
|
+
* `rewriteRozieImport` (e.g. the Lit emitter's side-effect/type-only
|
|
57024
|
+
* composed-component imports, which deliberately leave a LOCAL `.rozie`
|
|
57025
|
+
* specifier verbatim for the unplugin-consumer-coexist flow) can still
|
|
57026
|
+
* gate a published-specifier rewrite without duplicating this check.
|
|
57027
|
+
*/
|
|
57028
|
+
function isPublishedSpecifier(importPath) {
|
|
57029
|
+
if (!importPath.endsWith(ROZIE_EXT$1)) return false;
|
|
57030
|
+
if (importPath.startsWith(".") || importPath.startsWith("/")) return false;
|
|
57031
|
+
if (importPath.startsWith(TSCONFIG_ALIAS_PREFIX)) return false;
|
|
57032
|
+
return true;
|
|
57033
|
+
}
|
|
56585
57034
|
function rewriteRozieImport(importPath, target) {
|
|
56586
57035
|
if (!importPath.endsWith(ROZIE_EXT$1)) return importPath;
|
|
56587
57036
|
const targetExt = TARGET_EXT_MAP[target];
|
|
56588
57037
|
if (targetExt === void 0) throw new Error(`rewriteRozieImport: unknown target '${target}' — expected one of vue|react|svelte|angular|solid|lit`);
|
|
57038
|
+
if (isPublishedSpecifier(importPath)) {
|
|
57039
|
+
const lastSlash = importPath.lastIndexOf("/");
|
|
57040
|
+
if (lastSlash > 0) return `${importPath.slice(0, lastSlash)}-${target}`;
|
|
57041
|
+
}
|
|
56589
57042
|
return importPath.slice(0, -6) + targetExt;
|
|
56590
57043
|
}
|
|
56591
57044
|
//#endregion
|
|
@@ -56977,7 +57430,6 @@ function deconflictVueRefSuffix(ir, topLevelBindingNames) {
|
|
|
56977
57430
|
}
|
|
56978
57431
|
//#endregion
|
|
56979
57432
|
//#region ../core/src/codegen/buildPropJsdoc.ts
|
|
56980
|
-
var import_lib = /* @__PURE__ */ __toESM(require_lib$6(), 1);
|
|
56981
57433
|
/**
|
|
56982
57434
|
* Neutralize the JSDoc comment terminator inside an author-controlled string so
|
|
56983
57435
|
* it cannot prematurely close the emitted comment block (T-58-04). Replaces each
|
|
@@ -57869,6 +58321,22 @@ function renderComputedArrow$3(body, cast) {
|
|
|
57869
58321
|
* `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
|
|
57870
58322
|
* mirrored trailing copy spaces correctly.
|
|
57871
58323
|
*/
|
|
58324
|
+
/**
|
|
58325
|
+
* Quick task 260714-orv — compare two Babel comment nodes by SOURCE POSITION
|
|
58326
|
+
* (type + starting line/column) rather than object reference. A comment
|
|
58327
|
+
* shared between two adjacent statements' trailing/leading arrays sometimes
|
|
58328
|
+
* IS the exact same object, but an earlier pipeline pass (hoistModuleLet /
|
|
58329
|
+
* rewriteRozieIdentifiers / etc.) may clone an individual statement, minting
|
|
58330
|
+
* a content-identical comment object at a different reference. Two distinct,
|
|
58331
|
+
* independently-authored comments can never share a starting source
|
|
58332
|
+
* position, so this is a safe substitute for `===` this late in the
|
|
58333
|
+
* pipeline.
|
|
58334
|
+
*/
|
|
58335
|
+
function isSameSourceComment$1(a, b) {
|
|
58336
|
+
if (a === b) return true;
|
|
58337
|
+
if (!a.loc || !b.loc) return false;
|
|
58338
|
+
return a.type === b.type && a.loc.start.line === b.loc.start.line && a.loc.start.column === b.loc.start.column;
|
|
58339
|
+
}
|
|
57872
58340
|
function mirrorSpliceBoundaryComments$2(stmts) {
|
|
57873
58341
|
for (let i = 1; i < stmts.length; i++) {
|
|
57874
58342
|
const cur = stmts[i];
|
|
@@ -57877,7 +58345,15 @@ function mirrorSpliceBoundaryComments$2(stmts) {
|
|
|
57877
58345
|
const prevExtra = prev.extra;
|
|
57878
58346
|
const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
|
|
57879
58347
|
const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
|
|
57880
|
-
if (!curSpliced && !prevSpliced)
|
|
58348
|
+
if (!curSpliced && !prevSpliced) {
|
|
58349
|
+
const prevTrail = prev.trailingComments;
|
|
58350
|
+
const curLead = cur.leadingComments;
|
|
58351
|
+
if (prevTrail && prevTrail.length > 0 && curLead && curLead.length > 0) {
|
|
58352
|
+
const deduped = prevTrail.filter((c) => !curLead.some((lc) => isSameSourceComment$1(c, lc)));
|
|
58353
|
+
prev.trailingComments = deduped.length > 0 ? deduped : null;
|
|
58354
|
+
}
|
|
58355
|
+
continue;
|
|
58356
|
+
}
|
|
57881
58357
|
const lead = cur.leadingComments;
|
|
57882
58358
|
const prevTrail = prev.trailingComments;
|
|
57883
58359
|
if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
|
|
@@ -59988,7 +60464,61 @@ function emitElement$5(node, ctx) {
|
|
|
59988
60464
|
* (`<template #header="{ close }">…</template>`); default-shorthand
|
|
59989
60465
|
* (`{ name: '' }`) emits as bare children inside the component tag.
|
|
59990
60466
|
*/
|
|
60467
|
+
/**
|
|
60468
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
60469
|
+
* teleport. Wraps the element in Vue's NATIVE `<Teleport :to :disabled>`
|
|
60470
|
+
* component — authors cannot write `<Teleport>` directly (ROZ926 rejects
|
|
60471
|
+
* it), but the EMITTER may synthesize it as the lowering target for
|
|
60472
|
+
* `r-portal` (ROZ926 gates author input, not emit output). `<Teleport>`
|
|
60473
|
+
* tolerates a `v-if`/`v-for` child, so `extraDirective` (the r-if/r-for
|
|
60474
|
+
* directive the parent conditional/loop walker wants to inject) is applied
|
|
60475
|
+
* to the INNER element, not the `<Teleport>` wrapper — the wrapper is
|
|
60476
|
+
* unconditional; when `disabled` the child's own `v-if` still governs
|
|
60477
|
+
* whether anything renders at all.
|
|
60478
|
+
*
|
|
60479
|
+
* `:disabled="!(<expr>)"` — a falsy container renders in place (Vue's
|
|
60480
|
+
* Teleport skips target resolution/warnings entirely when disabled; see
|
|
60481
|
+
* `resolveTarget`/`mountToTarget` in @vue/runtime-core — no console warning,
|
|
60482
|
+
* no throw, for a falsy `to` when `disabled` is true).
|
|
60483
|
+
*
|
|
60484
|
+
* Finding 4 (R2) — the container expression is HOISTED into a single Vue
|
|
60485
|
+
* `computed` (the same D-04 hoist pattern `r-match` uses) instead of being
|
|
60486
|
+
* spliced verbatim into both `:to` and `:disabled`. This fixes three
|
|
60487
|
+
* defects at once:
|
|
60488
|
+
* 1. SSR guard — the computed body wraps the expression in the SAME
|
|
60489
|
+
* `typeof document === 'undefined' ? null : (...)` guard the React and
|
|
60490
|
+
* Solid targets emit, so a container expression that touches `document`
|
|
60491
|
+
* (e.g. `document.querySelector(...)`) is null on the server rather
|
|
60492
|
+
* than throwing; a null container makes `:disabled` truthy → renders
|
|
60493
|
+
* in place (parity with React's ternary / Solid's <Show> fallback).
|
|
60494
|
+
* 2. single evaluation — a Vue computed caches, so a side-effecting
|
|
60495
|
+
* selector runs once per render, not twice (`:to` + `:disabled`).
|
|
60496
|
+
* 3. escaping — the raw expression now lives in the script (where
|
|
60497
|
+
* @babel/generator escapes JS strings), and `:to`/`:disabled` bind a
|
|
60498
|
+
* bare computed identifier, so an expression containing a `"` can no
|
|
60499
|
+
* longer break the double-quoted attribute (obviating a per-attribute
|
|
60500
|
+
* escapeAttrValue call at the interpolation site).
|
|
60501
|
+
*/
|
|
60502
|
+
function emitPortalElement$1(node, extraDirective, ctx) {
|
|
60503
|
+
const { expression } = node.portalTo;
|
|
60504
|
+
if (ctx.elementPortalFlag) ctx.elementPortalFlag.seen = true;
|
|
60505
|
+
const containerCode = rewriteTemplateExpression$5(expression, ctx.ir);
|
|
60506
|
+
const hoistName = `__roziePortalTo${ctx.injectionCounter.next++}`;
|
|
60507
|
+
ctx.scriptInjections.push({
|
|
60508
|
+
wrapName: hoistName,
|
|
60509
|
+
import: {
|
|
60510
|
+
from: "vue",
|
|
60511
|
+
name: "computed"
|
|
60512
|
+
},
|
|
60513
|
+
decl: `const ${hoistName} = computed(() => (typeof document === 'undefined' ? null : (${containerCode})));`
|
|
60514
|
+
});
|
|
60515
|
+
return `<Teleport :to="${hoistName}" :disabled="!${hoistName}">${emitElementWithExtraDirectiveInner(node, extraDirective, ctx)}</Teleport>`;
|
|
60516
|
+
}
|
|
59991
60517
|
function emitElementWithExtraDirective(origNode, extraDirective, ctx) {
|
|
60518
|
+
if (origNode.portalTo) return emitPortalElement$1(origNode, extraDirective, ctx);
|
|
60519
|
+
return emitElementWithExtraDirectiveInner(origNode, extraDirective, ctx);
|
|
60520
|
+
}
|
|
60521
|
+
function emitElementWithExtraDirectiveInner(origNode, extraDirective, ctx) {
|
|
59992
60522
|
const node = stripKeynavCommitEvent$5(origNode);
|
|
59993
60523
|
const rHtml = findRHtml$2(node.attributes);
|
|
59994
60524
|
const attrText = emitMergedAttributes(rHtml ? node.attributes.filter((a) => !(a.kind === "binding" && a.name === "r-html")) : node.attributes, {
|
|
@@ -65085,6 +65615,20 @@ function genBlockInner(block, indent) {
|
|
|
65085
65615
|
}).join("\n");
|
|
65086
65616
|
}
|
|
65087
65617
|
/**
|
|
65618
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
65619
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
65620
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
65621
|
+
* later import's `leadingComments`) prints exactly once. Mirrors
|
|
65622
|
+
* `genBlockInner`'s single-pass precedent — generating imports one at a time
|
|
65623
|
+
* (`nodes.map(genCode).join`) gives each import its OWN comment-dedup set,
|
|
65624
|
+
* doubling any shared comment. `t.program(nodes)` prints only its body
|
|
65625
|
+
* statements (no wrapping braces/`File` boilerplate), so non-comment cases
|
|
65626
|
+
* stay byte-identical.
|
|
65627
|
+
*/
|
|
65628
|
+
function genImportsBlock$2(nodes) {
|
|
65629
|
+
return generate$21(_babel_types.program(nodes), GEN_OPTS$19).code;
|
|
65630
|
+
}
|
|
65631
|
+
/**
|
|
65088
65632
|
* Phase 14.1 follow-up — does any element in the IR template carry a
|
|
65089
65633
|
* `spreadBinding` attribute? The Plan 14-05 gate at `emitScript` only
|
|
65090
65634
|
* synthesises `const attrs = ...` (the source for the `$attrs` rewrite at
|
|
@@ -65936,7 +66480,7 @@ function emitScript$4(ir, collectors, opts = {}) {
|
|
|
65936
66480
|
const cloned = cloneScriptProgram$4(ir.setupBody.scriptProgram);
|
|
65937
66481
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts } = partitionUserImports$3(cloned);
|
|
65938
66482
|
cloned.program.body = bodyStmts;
|
|
65939
|
-
const userImports = userImportNodes.length > 0 ?
|
|
66483
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock$2(userImportNodes) + "\n" : "";
|
|
65940
66484
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => genCode$7(decl));
|
|
65941
66485
|
deconflictDeclareThenAssignRef(cloned, ir);
|
|
65942
66486
|
const hoistResult = hoistModuleLet(cloned, ir);
|
|
@@ -69320,7 +69864,42 @@ function scopeAttrForElement$1(node, ctx) {
|
|
|
69320
69864
|
* declarations are hoisted within their containing scope). Both emit the tag
|
|
69321
69865
|
* verbatim PascalCase below; no template AST rewrite needed.
|
|
69322
69866
|
*/
|
|
69867
|
+
/**
|
|
69868
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
69869
|
+
* teleport. Wraps the normal element emit (`emitElementInner`) in a
|
|
69870
|
+
* `container ? createPortal(tree, container) : tree` check, using React's
|
|
69871
|
+
* native `createPortal` (react-dom) — NEVER the P33 `emitPortals.ts`
|
|
69872
|
+
* createRoot-into-container machinery (that is the INVERSE direction: slot-
|
|
69873
|
+
* content mounted INTO an engine-owned container, not a component's own
|
|
69874
|
+
* subtree relocated OUT).
|
|
69875
|
+
*
|
|
69876
|
+
* SSR guard: the container expression is evaluated ONLY when `document`
|
|
69877
|
+
* exists — some author expressions call `document.querySelector` internally
|
|
69878
|
+
* (see `resolveAppendTo`/`resolveTo` in the authoring convention), so simply
|
|
69879
|
+
* checking the RESULT for null is not enough; the whole resolution must be
|
|
69880
|
+
* skipped on the server. Falsy/null container falls back to rendering the
|
|
69881
|
+
* subtree in place — this is what makes `appendTo:false` byte-behavior-
|
|
69882
|
+
* identical to no directive at emit time (the lower-time field is always
|
|
69883
|
+
* present per Task 1; this is the runtime decision point).
|
|
69884
|
+
*
|
|
69885
|
+
* Returns a `{...}` JSX-expression-container mustache — valid directly in
|
|
69886
|
+
* JSX children position, and `renderBranchBody` (emitConditional.ts) strips
|
|
69887
|
+
* the outer braces via `stripBalancedMustache` when this is a single-child
|
|
69888
|
+
* r-if branch body, so `r-portal` + `r-if` on the same element compose for
|
|
69889
|
+
* free with zero special-casing in the conditional emitter.
|
|
69890
|
+
*/
|
|
69891
|
+
function emitPortalElement(node, ctx) {
|
|
69892
|
+
const { expression } = node.portalTo;
|
|
69893
|
+
if (ctx.elementPortalFlag) ctx.elementPortalFlag.seen = true;
|
|
69894
|
+
const containerCode = rewriteTemplateExpression$4(expression, ctx.ir);
|
|
69895
|
+
const treeJsx = emitElementInner$4(node, ctx);
|
|
69896
|
+
return `{(() => { const __rozieContainer = typeof document === 'undefined' ? null : (${containerCode}); return __rozieContainer ? createPortal(${treeJsx}, __rozieContainer) : (${treeJsx}); })()}`;
|
|
69897
|
+
}
|
|
69323
69898
|
function emitElement$4(origNode, ctx) {
|
|
69899
|
+
if (origNode.portalTo) return emitPortalElement(origNode, ctx);
|
|
69900
|
+
return emitElementInner$4(origNode, ctx);
|
|
69901
|
+
}
|
|
69902
|
+
function emitElementInner$4(origNode, ctx) {
|
|
69324
69903
|
let node = stripKeynavCommitEvent$4(origNode);
|
|
69325
69904
|
const pendingKey = ctx.pendingKey ?? null;
|
|
69326
69905
|
const childCtx = {
|
|
@@ -69870,9 +70449,11 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69870
70449
|
scriptInjections: [],
|
|
69871
70450
|
slotPropFields: slotResult.slotPropFields,
|
|
69872
70451
|
slotCtxInterfaces: slotResult.slotCtxInterfaces,
|
|
69873
|
-
diagnostics: []
|
|
70452
|
+
diagnostics: [],
|
|
70453
|
+
hasElementPortal: false
|
|
69874
70454
|
};
|
|
69875
70455
|
const keynav = resolveKeynavPlan$4(ir);
|
|
70456
|
+
const elementPortalFlag = { seen: false };
|
|
69876
70457
|
const ctx = {
|
|
69877
70458
|
ir,
|
|
69878
70459
|
collectors,
|
|
@@ -69881,7 +70462,8 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69881
70462
|
scriptInjections,
|
|
69882
70463
|
injectionCounter: { next: 0 },
|
|
69883
70464
|
...opts.scopeAttr !== void 0 ? { scopeAttr: opts.scopeAttr } : {},
|
|
69884
|
-
keynav
|
|
70465
|
+
keynav,
|
|
70466
|
+
elementPortalFlag
|
|
69885
70467
|
};
|
|
69886
70468
|
const jsx = emitNode$4(ir.template, ctx);
|
|
69887
70469
|
if (keynav !== null) scriptInjections.push(...buildKeynavScriptInjections$2(keynav, ir, collectors));
|
|
@@ -69890,7 +70472,8 @@ function emitTemplate$4(ir, collectors, registry, opts = {}) {
|
|
|
69890
70472
|
scriptInjections,
|
|
69891
70473
|
slotPropFields: slotResult.slotPropFields,
|
|
69892
70474
|
slotCtxInterfaces: slotResult.slotCtxInterfaces,
|
|
69893
|
-
diagnostics
|
|
70475
|
+
diagnostics,
|
|
70476
|
+
hasElementPortal: elementPortalFlag.seen
|
|
69894
70477
|
};
|
|
69895
70478
|
}
|
|
69896
70479
|
//#endregion
|
|
@@ -73810,11 +74393,11 @@ function emitReact(ir, opts = {}) {
|
|
|
73810
74393
|
runtime: runtimeImports,
|
|
73811
74394
|
filename: opts.filename
|
|
73812
74395
|
}, scriptOpts);
|
|
73813
|
-
const portalImport = hasPortals ? "import { createRoot, type Root } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\n" : "";
|
|
73814
74396
|
const tmpl = emitTemplate$4(ir, {
|
|
73815
74397
|
react: reactImports,
|
|
73816
74398
|
runtime: runtimeImports
|
|
73817
74399
|
}, registry, { scopeAttr });
|
|
74400
|
+
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" : "");
|
|
73818
74401
|
const listeners = emitListeners$4(ir, {
|
|
73819
74402
|
react: reactImports,
|
|
73820
74403
|
runtime: runtimeImports
|
|
@@ -74015,11 +74598,11 @@ function renderPropType(ann) {
|
|
|
74015
74598
|
case "Boolean": return "boolean";
|
|
74016
74599
|
case "Array": return "unknown[]";
|
|
74017
74600
|
case "Object": return "Record<string, unknown>";
|
|
74018
|
-
case "Function": return "(...args:
|
|
74601
|
+
case "Function": return "(...args: any[]) => any";
|
|
74019
74602
|
default: return ann.name;
|
|
74020
74603
|
}
|
|
74021
74604
|
if (ann.kind === "literal") switch (ann.value) {
|
|
74022
|
-
case "function": return "(...args:
|
|
74605
|
+
case "function": return "(...args: any[]) => any";
|
|
74023
74606
|
case "object": return "Record<string, unknown>";
|
|
74024
74607
|
case "array": return "unknown[]";
|
|
74025
74608
|
case "string": return "string";
|
|
@@ -75305,6 +75888,22 @@ function arrowBody$2(body) {
|
|
|
75305
75888
|
* `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
|
|
75306
75889
|
* mirrored trailing copy spaces correctly.
|
|
75307
75890
|
*/
|
|
75891
|
+
/**
|
|
75892
|
+
* Quick task 260714-orv — compare two Babel comment nodes by SOURCE POSITION
|
|
75893
|
+
* (type + starting line/column) rather than object reference. A comment
|
|
75894
|
+
* shared between two adjacent statements' trailing/leading arrays sometimes
|
|
75895
|
+
* IS the exact same object, but an earlier pipeline pass (hoistModuleLet /
|
|
75896
|
+
* rewriteRozieIdentifiers / etc.) may clone an individual statement, minting
|
|
75897
|
+
* a content-identical comment object at a different reference. Two distinct,
|
|
75898
|
+
* independently-authored comments can never share a starting source
|
|
75899
|
+
* position, so this is a safe substitute for `===` this late in the
|
|
75900
|
+
* pipeline.
|
|
75901
|
+
*/
|
|
75902
|
+
function isSameSourceComment(a, b) {
|
|
75903
|
+
if (a === b) return true;
|
|
75904
|
+
if (!a.loc || !b.loc) return false;
|
|
75905
|
+
return a.type === b.type && a.loc.start.line === b.loc.start.line && a.loc.start.column === b.loc.start.column;
|
|
75906
|
+
}
|
|
75308
75907
|
function mirrorSpliceBoundaryComments$1(stmts) {
|
|
75309
75908
|
for (let i = 1; i < stmts.length; i++) {
|
|
75310
75909
|
const cur = stmts[i];
|
|
@@ -75313,7 +75912,15 @@ function mirrorSpliceBoundaryComments$1(stmts) {
|
|
|
75313
75912
|
const prevExtra = prev.extra;
|
|
75314
75913
|
const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
|
|
75315
75914
|
const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
|
|
75316
|
-
if (!curSpliced && !prevSpliced)
|
|
75915
|
+
if (!curSpliced && !prevSpliced) {
|
|
75916
|
+
const prevTrail = prev.trailingComments;
|
|
75917
|
+
const curLead = cur.leadingComments;
|
|
75918
|
+
if (prevTrail && prevTrail.length > 0 && curLead && curLead.length > 0) {
|
|
75919
|
+
const deduped = prevTrail.filter((c) => !curLead.some((lc) => isSameSourceComment(c, lc)));
|
|
75920
|
+
prev.trailingComments = deduped.length > 0 ? deduped : null;
|
|
75921
|
+
}
|
|
75922
|
+
continue;
|
|
75923
|
+
}
|
|
75317
75924
|
const lead = cur.leadingComments;
|
|
75318
75925
|
const prevTrail = prev.trailingComments;
|
|
75319
75926
|
if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
|
|
@@ -77849,6 +78456,11 @@ function emitElementInner$3(origNode, ctx) {
|
|
|
77849
78456
|
hasFallthroughSpread = true;
|
|
77850
78457
|
break;
|
|
77851
78458
|
}
|
|
78459
|
+
let portalAttr = null;
|
|
78460
|
+
if (node.portalTo) {
|
|
78461
|
+
ctx.runtimeImports.add("roziePortal");
|
|
78462
|
+
portalAttr = `use:roziePortal={${rewriteTemplateExpression$3(node.portalTo.expression, ctx.ir, ctxRenames(ctx))}}`;
|
|
78463
|
+
}
|
|
77852
78464
|
const attrText = emitAttributes$2(node.attributes, {
|
|
77853
78465
|
ir: ctx.ir,
|
|
77854
78466
|
elementTagKind: node.tagKind,
|
|
@@ -77877,6 +78489,7 @@ function emitElementInner$3(origNode, ctx) {
|
|
|
77877
78489
|
if (eventText) partsHead.push(eventText);
|
|
77878
78490
|
for (const sp of spreadTexts) partsHead.push(sp);
|
|
77879
78491
|
for (const ka of keynavAttrs) partsHead.push(ka);
|
|
78492
|
+
if (portalAttr) partsHead.push(portalAttr);
|
|
77880
78493
|
if (ctx.scopeAttr.length > 0) partsHead.push(ctx.scopeAttr);
|
|
77881
78494
|
const head = partsHead.length > 0 ? " " + partsHead.join(" ") : "";
|
|
77882
78495
|
if (rHtml !== null) {
|
|
@@ -79608,6 +80221,88 @@ function hoistDoubleReadAccessors(program) {
|
|
|
79608
80221
|
}
|
|
79609
80222
|
visit(program.program);
|
|
79610
80223
|
}
|
|
80224
|
+
/** True for `$props.X` where X is in `polymorphicModelProps`. */
|
|
80225
|
+
function isPolymorphicModelRead$1(node, polymorphicModelProps) {
|
|
80226
|
+
return _babel_types.isMemberExpression(node) && !node.computed && _babel_types.isIdentifier(node.object) && node.object.name === "$props" && _babel_types.isIdentifier(node.property) && polymorphicModelProps.has(node.property.name);
|
|
80227
|
+
}
|
|
80228
|
+
/**
|
|
80229
|
+
* When `test` is `typeof $props.X === '<lit>'` (or `!==`) or `'<lit>' in
|
|
80230
|
+
* $props.X`, for X in `polymorphicModelProps`, returns X. Otherwise null.
|
|
80231
|
+
*/
|
|
80232
|
+
function matchesPolymorphicModelGuard$1(test, polymorphicModelProps) {
|
|
80233
|
+
if (_babel_types.isLogicalExpression(test)) return matchesPolymorphicModelGuard$1(test.left, polymorphicModelProps) ?? matchesPolymorphicModelGuard$1(test.right, polymorphicModelProps);
|
|
80234
|
+
if (!_babel_types.isBinaryExpression(test)) return null;
|
|
80235
|
+
const { operator, left, right } = test;
|
|
80236
|
+
if (operator === "===" || operator === "!==") {
|
|
80237
|
+
if (_babel_types.isUnaryExpression(left) && left.operator === "typeof" && isPolymorphicModelRead$1(left.argument, polymorphicModelProps) && _babel_types.isStringLiteral(right)) return left.argument.property.name;
|
|
80238
|
+
if (_babel_types.isUnaryExpression(right) && right.operator === "typeof" && isPolymorphicModelRead$1(right.argument, polymorphicModelProps) && _babel_types.isStringLiteral(left)) return right.argument.property.name;
|
|
80239
|
+
return null;
|
|
80240
|
+
}
|
|
80241
|
+
if (operator === "in" && isPolymorphicModelRead$1(right, polymorphicModelProps)) return right.property.name;
|
|
80242
|
+
return null;
|
|
80243
|
+
}
|
|
80244
|
+
/**
|
|
80245
|
+
* Quick task 260711-v2l (project_angular_typeof_narrow_gap) — port of Solid's
|
|
80246
|
+
* `hoistPolymorphicModelGuards` (emitter-hardening backlog item #11,
|
|
80247
|
+
* `project_solid_polymorphic_model_typeof_narrow_gap`, 73-02 Task 2) to the
|
|
80248
|
+
* Angular target. `$props.X` (a union or `unknown`-typed `model: true` prop)
|
|
80249
|
+
* lowers to an Angular signal accessor CALL (`this.X()`); TS does not narrow a
|
|
80250
|
+
* `typeof`/`in` guard across two SEPARATE calls to that accessor the way it
|
|
80251
|
+
* narrows a plain variable/property read. When a `ConditionalExpression`
|
|
80252
|
+
* guards on `typeof $props.X === '<lit>'` / `'<lit>' in $props.X` (X a
|
|
80253
|
+
* polymorphic/unknown model prop) AND re-reads `$props.X` again inside the
|
|
80254
|
+
* conditional, hoist ONE local binding before the guard and route every
|
|
80255
|
+
* `$props.X` read inside the conditional through it — mirroring the hand-
|
|
80256
|
+
* authored `const v = $props.value; return typeof v === 'string' ? v : ''`
|
|
80257
|
+
* workaround this replaces (DatePicker.rozie, commit bf3766b5).
|
|
80258
|
+
*
|
|
80259
|
+
* Gated to two known-safe shapes so this never mis-hoists into the wrong scope:
|
|
80260
|
+
* 1. the conditional IS an arrow function's own concise body — converted to
|
|
80261
|
+
* a block body wrapping `const v = ...; return <conditional>;`
|
|
80262
|
+
* 2. the conditional sits inside a statement that lives in a statement LIST
|
|
80263
|
+
* (block body, switch-case consequent, program body) — including
|
|
80264
|
+
* statements nested inside control-flow blocks (`if`/`for`/`while`/
|
|
80265
|
+
* `switch`/`try`). The local is inserted right before that statement, in
|
|
80266
|
+
* the same block as the guarded reads (Spike-012 BUG-4).
|
|
80267
|
+
* A conditional not in either shape (e.g. a bare single-statement `if (x)
|
|
80268
|
+
* return …;` with no block) is left alone (falsify-to-no-op, never a wrong fix).
|
|
80269
|
+
*
|
|
80270
|
+
* Runs on the raw (pre-$props-rewrite) Program, before the main
|
|
80271
|
+
* `rewriteRozieIdentifiers` traversal — the injected `$props.X` reference is
|
|
80272
|
+
* left for that later pass to lower to the normal `this.X()` accessor call
|
|
80273
|
+
* (identical downstream lowering to the DatePicker hand-authored workaround
|
|
80274
|
+
* this replaces).
|
|
80275
|
+
*/
|
|
80276
|
+
function hoistPolymorphicModelGuards$1(cloned, polymorphicModelProps) {
|
|
80277
|
+
if (polymorphicModelProps.size === 0) return;
|
|
80278
|
+
traverse$8(cloned, { ConditionalExpression(path) {
|
|
80279
|
+
const propName = matchesPolymorphicModelGuard$1(path.node.test, polymorphicModelProps);
|
|
80280
|
+
if (!propName) return;
|
|
80281
|
+
let occurrences = 0;
|
|
80282
|
+
path.traverse({ MemberExpression(inner) {
|
|
80283
|
+
if (isPolymorphicModelRead$1(inner.node, polymorphicModelProps) && inner.node.property.name === propName) occurrences++;
|
|
80284
|
+
} });
|
|
80285
|
+
if (occurrences < 2) return;
|
|
80286
|
+
const parentPath = path.parentPath;
|
|
80287
|
+
const isConciseArrowBody = parentPath.isArrowFunctionExpression() && parentPath.node.body === path.node;
|
|
80288
|
+
const stmtPath = path.getStatementParent();
|
|
80289
|
+
const canInsertBeforeStmt = !!stmtPath && stmtPath.inList;
|
|
80290
|
+
if (!isConciseArrowBody && !canInsertBeforeStmt) return;
|
|
80291
|
+
const localName = path.scope.hasBinding("v") ? path.scope.generateUidIdentifier("v").name : "v";
|
|
80292
|
+
path.traverse({ MemberExpression(inner) {
|
|
80293
|
+
if (isPolymorphicModelRead$1(inner.node, polymorphicModelProps) && inner.node.property.name === propName) {
|
|
80294
|
+
inner.replaceWith(_babel_types.identifier(localName));
|
|
80295
|
+
inner.skip();
|
|
80296
|
+
}
|
|
80297
|
+
} });
|
|
80298
|
+
const varDecl = _babel_types.variableDeclaration("const", [_babel_types.variableDeclarator(_babel_types.identifier(localName), _babel_types.memberExpression(_babel_types.identifier("$props"), _babel_types.identifier(propName)))]);
|
|
80299
|
+
if (isConciseArrowBody) {
|
|
80300
|
+
parentPath.node.body = _babel_types.blockStatement([varDecl, _babel_types.returnStatement(path.node)]);
|
|
80301
|
+
return;
|
|
80302
|
+
}
|
|
80303
|
+
stmtPath.insertBefore(varDecl);
|
|
80304
|
+
} });
|
|
80305
|
+
}
|
|
79611
80306
|
/**
|
|
79612
80307
|
* Rewrite Rozie magic-accessor identifiers in-place on a cloned Program.
|
|
79613
80308
|
*
|
|
@@ -81825,6 +82520,19 @@ function genCode$3(node) {
|
|
|
81825
82520
|
return generate$9(node, GEN_OPTS$8).code;
|
|
81826
82521
|
}
|
|
81827
82522
|
/**
|
|
82523
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
82524
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
82525
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
82526
|
+
* later import's `leadingComments`) prints exactly once. Generating imports
|
|
82527
|
+
* one at a time (`nodes.map(genCode).join`) gives each import its OWN
|
|
82528
|
+
* comment-dedup set, doubling any shared comment. `t.program(nodes)` prints
|
|
82529
|
+
* only its body statements (no wrapping braces/`File` boilerplate), so
|
|
82530
|
+
* non-comment cases stay byte-identical.
|
|
82531
|
+
*/
|
|
82532
|
+
function genImportsBlock$1(nodes) {
|
|
82533
|
+
return generate$9(_babel_types.program(nodes), GEN_OPTS$8).code;
|
|
82534
|
+
}
|
|
82535
|
+
/**
|
|
81828
82536
|
* Emit `() => body` for an Expression or BlockStatement body.
|
|
81829
82537
|
*
|
|
81830
82538
|
* Building the arrow as a Babel node (rather than string-templating
|
|
@@ -81907,7 +82615,7 @@ function renderType$2(ann) {
|
|
|
81907
82615
|
case "Boolean": return "boolean";
|
|
81908
82616
|
case "Array": return "any[]";
|
|
81909
82617
|
case "Object": return "Record<string, any>";
|
|
81910
|
-
case "Function": return "(...args:
|
|
82618
|
+
case "Function": return "(...args: any[]) => any";
|
|
81911
82619
|
default: return ann.name;
|
|
81912
82620
|
}
|
|
81913
82621
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -81917,7 +82625,7 @@ function renderType$2(ann) {
|
|
|
81917
82625
|
if (ann.kind === "literal") {
|
|
81918
82626
|
if (ann.value === "array") return "any[]";
|
|
81919
82627
|
if (ann.value === "object") return "Record<string, any>";
|
|
81920
|
-
if (ann.value === "function") return "(...args:
|
|
82628
|
+
if (ann.value === "function") return "(...args: any[]) => any";
|
|
81921
82629
|
return ann.value;
|
|
81922
82630
|
}
|
|
81923
82631
|
return "unknown";
|
|
@@ -82232,8 +82940,9 @@ function emitScript$2(ir, opts = {}) {
|
|
|
82232
82940
|
deconflictReservedImportBindings(cloned, new Set([...ir.props.map((p) => p.name), ...reservedClassMembers("angular", { singleModel: importSingleModel })]));
|
|
82233
82941
|
const { userImports: userImportNodes, hoistedTypeDecls, bodyStmts, valueImportNames } = partitionUserImports$2(cloned);
|
|
82234
82942
|
cloned.program.body = bodyStmts;
|
|
82235
|
-
const userImports = userImportNodes.length > 0 ?
|
|
82943
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock$1(userImportNodes) + "\n" : "";
|
|
82236
82944
|
normalizeModelAccessor$2(cloned);
|
|
82945
|
+
hoistPolymorphicModelGuards$1(cloned, new Set(ir.props.filter((p) => p.isModel && (p.typeAnnotation.kind === "union" || renderType$2(p.typeAnnotation) === "unknown")).map((p) => p.name)));
|
|
82237
82946
|
hoistDoubleReadAccessors(cloned);
|
|
82238
82947
|
const lifecyclePairing = pairClonedLifecycle(cloned, ir);
|
|
82239
82948
|
const emitsWithPayload = collectEmitsWithPayload(cloned);
|
|
@@ -82871,6 +83580,42 @@ function shouldWrapAttrBinding$1(name, expr, ctx, elementTagName) {
|
|
|
82871
83580
|
return true;
|
|
82872
83581
|
}
|
|
82873
83582
|
/**
|
|
83583
|
+
* Quick task 260711-tgk — Angular's `[style]=` (`ɵɵstyleMap`) parses the bound
|
|
83584
|
+
* value into a semicolon-delimited key:value map. Its parser corrupts on an
|
|
83585
|
+
* EMPTY declaration (`;;` — e.g. `pinStyle(colId) + ';' + pad` when
|
|
83586
|
+
* `pinStyle` already ends in `;`), silently DROPPING the declaration that
|
|
83587
|
+
* follows the empty segment. The other 5 targets apply a `:style` STRING
|
|
83588
|
+
* value via the browser's own (tolerant) CSS parser — Angular is the lone
|
|
83589
|
+
* outlier because `[style]=` binds through `ɵɵstyleMap`, not `setAttribute`.
|
|
83590
|
+
*
|
|
83591
|
+
* Fix (see the call site in emitSingleAttr's binding branch): when a
|
|
83592
|
+
* `:style` binding's expression is PROVABLY a string at compile time, emit
|
|
83593
|
+
* `[attr.style]="<expr>"` instead of `[style]="<expr>"`. `[attr.style]`
|
|
83594
|
+
* compiles to `ɵɵattribute` → `setAttribute('style', <string>)`, which the
|
|
83595
|
+
* browser parses tolerantly — matching the other 5 targets' behavior. This
|
|
83596
|
+
* is only reachable for the element's SOLE style contributor: emitAttributes'
|
|
83597
|
+
* `counts.get('style') > 1` merge path (`[ngStyle]`) always intercepts
|
|
83598
|
+
* multi-source style bindings (a co-existing static `style=` or a second
|
|
83599
|
+
* `:style`) via its own `continue`, before emitSingleAttr is ever called for
|
|
83600
|
+
* a style attribute in that case.
|
|
83601
|
+
*
|
|
83602
|
+
* Conservative by design: a false NEGATIVE (returning false for a value that
|
|
83603
|
+
* IS always a string) just keeps the current, already-correct-for-non-`;;`
|
|
83604
|
+
* `[style]=` behavior. A false POSITIVE (returning true for a value that can
|
|
83605
|
+
* be an object) would break object-valued styles — so every branch below is
|
|
83606
|
+
* additive-only and defaults to `false`.
|
|
83607
|
+
*/
|
|
83608
|
+
function isProvablyStringStyleExpression(expr) {
|
|
83609
|
+
if (_babel_types.isStringLiteral(expr)) return true;
|
|
83610
|
+
if (_babel_types.isTemplateLiteral(expr)) return true;
|
|
83611
|
+
if (_babel_types.isBinaryExpression(expr) && expr.operator === "+") {
|
|
83612
|
+
if (!_babel_types.isExpression(expr.left)) return false;
|
|
83613
|
+
return isProvablyStringStyleExpression(expr.left) || isProvablyStringStyleExpression(expr.right);
|
|
83614
|
+
}
|
|
83615
|
+
if (_babel_types.isConditionalExpression(expr)) return isProvablyStringStyleExpression(expr.consequent) && isProvablyStringStyleExpression(expr.alternate);
|
|
83616
|
+
return false;
|
|
83617
|
+
}
|
|
83618
|
+
/**
|
|
82874
83619
|
* Plan 14-05 R6 — keys that must never reach the emitted object from an
|
|
82875
83620
|
* author-controlled `r-bind` LITERAL. Mirrors the React/Solid/Vue/Svelte
|
|
82876
83621
|
* `FORBIDDEN_SPREAD_KEYS` set + the Phase 02 `collectPropDecls` write-time
|
|
@@ -83280,6 +84025,151 @@ function emitListenerSpread$1(spread, ctx) {
|
|
|
83280
84025
|
return `#${refName}`;
|
|
83281
84026
|
}
|
|
83282
84027
|
/**
|
|
84028
|
+
* command-palette-portal-overlay phase — the SHARED (once-per-component)
|
|
84029
|
+
* `__roziePortalPlace` helper method. Mirrors `applyAttrsHelperDecl` /
|
|
84030
|
+
* `listenersRendererFieldDecl`'s dedup pattern: pushed onto
|
|
84031
|
+
* `ctx.scriptInjections` at most once regardless of how many `r-portal`
|
|
84032
|
+
* elements the component has.
|
|
84033
|
+
*
|
|
84034
|
+
* A `WeakMap<Element, anchor>` lazily captures each portalled element's
|
|
84035
|
+
* ORIGINAL parent + next-sibling on first placement (so a later falsy
|
|
84036
|
+
* `target` restores it to its exact natural template position — the AOT-
|
|
84037
|
+
* safe, signals-era analog of Vue's `<Teleport :disabled>` / React's
|
|
84038
|
+
* ternary in-place fallback). A truthy `target` moves the element via
|
|
84039
|
+
* native `appendChild` and records it in `__roziePortalMoved` so the
|
|
84040
|
+
* shared `ngOnDestroy` teardown (registered by the per-portal effect, see
|
|
84041
|
+
* `emitPortalDirective`) can remove any STILL-relocated node — Angular's
|
|
84042
|
+
* own view-destroy machinery is not guaranteed to find a node that has
|
|
84043
|
+
* been moved outside its logical view position.
|
|
84044
|
+
*/
|
|
84045
|
+
const ROZIE_PORTAL_PLACE_FIELD_NAME = "__roziePortalPlace";
|
|
84046
|
+
const ROZIE_PORTAL_ANCHORS_FIELD_NAME = "__roziePortalAnchors";
|
|
84047
|
+
const ROZIE_PORTAL_MOVED_FIELD_NAME = "__roziePortalMoved";
|
|
84048
|
+
function portalAnchorsFieldDecl() {
|
|
84049
|
+
return `private ${ROZIE_PORTAL_ANCHORS_FIELD_NAME} = new WeakMap<Element, { parent: Node | null; next: Node | null }>();`;
|
|
84050
|
+
}
|
|
84051
|
+
function portalMovedFieldDecl() {
|
|
84052
|
+
return `private ${ROZIE_PORTAL_MOVED_FIELD_NAME} = new Set<Element>();`;
|
|
84053
|
+
}
|
|
84054
|
+
function portalPlaceMethodDecl() {
|
|
84055
|
+
return [
|
|
84056
|
+
`private ${ROZIE_PORTAL_PLACE_FIELD_NAME}(el: Element, target: Element | null | undefined): void {`,
|
|
84057
|
+
` let anchor = this.${ROZIE_PORTAL_ANCHORS_FIELD_NAME}.get(el);`,
|
|
84058
|
+
` if (!anchor) {`,
|
|
84059
|
+
` anchor = { parent: el.parentNode, next: el.nextSibling };`,
|
|
84060
|
+
` this.${ROZIE_PORTAL_ANCHORS_FIELD_NAME}.set(el, anchor);`,
|
|
84061
|
+
` }`,
|
|
84062
|
+
` if (target) {`,
|
|
84063
|
+
` if (!this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.has(el) && !el.isConnected) return;`,
|
|
84064
|
+
` if (el.parentNode !== target) {`,
|
|
84065
|
+
` target.appendChild(el);`,
|
|
84066
|
+
` }`,
|
|
84067
|
+
` this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.add(el);`,
|
|
84068
|
+
` return;`,
|
|
84069
|
+
` }`,
|
|
84070
|
+
` if (!this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.has(el)) return;`,
|
|
84071
|
+
` this.${ROZIE_PORTAL_MOVED_FIELD_NAME}.delete(el);`,
|
|
84072
|
+
` if (anchor.parent) {`,
|
|
84073
|
+
` if (anchor.next && anchor.next.parentNode === anchor.parent) {`,
|
|
84074
|
+
` anchor.parent.insertBefore(el, anchor.next);`,
|
|
84075
|
+
` } else {`,
|
|
84076
|
+
` anchor.parent.appendChild(el);`,
|
|
84077
|
+
` }`,
|
|
84078
|
+
` }`,
|
|
84079
|
+
`}`
|
|
84080
|
+
].join("\n");
|
|
84081
|
+
}
|
|
84082
|
+
/**
|
|
84083
|
+
* command-palette-portal-overlay phase — emit the per-element machinery for
|
|
84084
|
+
* ONE `r-portal="<expr>"` element:
|
|
84085
|
+
* - a `#roziePortal_<N>` template-ref attribute (returned for splicing
|
|
84086
|
+
* onto the open tag),
|
|
84087
|
+
* - a `viewChild<ElementRef>('roziePortal_<N>')` private field,
|
|
84088
|
+
* - the SHARED `__roziePortalPlace`/anchors/moved-set fields (once per
|
|
84089
|
+
* component — mirrors `emitSpreadBinding`'s `applyAttrsHelperDecl`
|
|
84090
|
+
* dedup),
|
|
84091
|
+
* - a `private __roziePortal_<N>_effect = effect(() => { ... });` field
|
|
84092
|
+
* initializer that guards `nativeElement` (Pitfall 7 — `viewChild()`
|
|
84093
|
+
* signals return `undefined` until the view is initialized, so a
|
|
84094
|
+
* pre-render effect tick is a safe no-op) and re-runs the placement
|
|
84095
|
+
* whenever the container expression's SIGNAL dependencies change
|
|
84096
|
+
* (the AOT-safe, signals-era analog of a reactive `$watch`).
|
|
84097
|
+
*
|
|
84098
|
+
* AOT-SAFE by construction: no `import.meta.url` (not referenced at all),
|
|
84099
|
+
* no inline template arrow (the container expression is a plain property/
|
|
84100
|
+
* method-call expression spliced into a `effect()` callback BODY — a
|
|
84101
|
+
* function statement, not a template-attribute arrow literal; analogjs AOT
|
|
84102
|
+
* rejects arrows INSIDE `{{ }}`/binding attribute VALUES, not inside a
|
|
84103
|
+
* class-field-initializer's own script-side callback).
|
|
84104
|
+
*
|
|
84105
|
+
* `effect()` in a field initializer is valid Angular injection context
|
|
84106
|
+
* (mirrors `emitSpreadBinding`/`emitListenerSpread`'s identical Pitfall-8
|
|
84107
|
+
* rationale) — no separate `ngAfterViewInit`/constructor wiring is needed,
|
|
84108
|
+
* unlike the r-keynav controller (which needs `viewChild()` to resolve
|
|
84109
|
+
* itself for the FIRST commit-timing-sensitive read; a portal's placement
|
|
84110
|
+
* has no such ordering hazard — `effect()`'s own no-op-until-mounted guard
|
|
84111
|
+
* covers the initial-render case identically to every render after it).
|
|
84112
|
+
*/
|
|
84113
|
+
function emitPortalDirective(element, ctx) {
|
|
84114
|
+
const counter = ctx.injectionCounter ?? { next: 0 };
|
|
84115
|
+
const idx = counter.next++;
|
|
84116
|
+
const refName = `roziePortal_${idx}`;
|
|
84117
|
+
const effectFieldName = `__roziePortal_${idx}_effect`;
|
|
84118
|
+
const destroyRegisteredFieldName = `__roziePortal_${idx}_destroyRegistered`;
|
|
84119
|
+
const containerCode = rewriteListenerExpression(element.portalTo.expression, ctx.ir, {
|
|
84120
|
+
collisionRenames: ctx.collisionRenames,
|
|
84121
|
+
classMembers: ctx.classMembers,
|
|
84122
|
+
cvaModelProp: ctx.cvaModelProp,
|
|
84123
|
+
cvaMergeDisabled: ctx.cvaMergeDisabled
|
|
84124
|
+
});
|
|
84125
|
+
if (ctx.scriptInjections !== void 0) {
|
|
84126
|
+
ctx.scriptInjections.push({
|
|
84127
|
+
name: refName,
|
|
84128
|
+
decl: `private ${refName} = viewChild<ElementRef>('${refName}');`
|
|
84129
|
+
});
|
|
84130
|
+
if (!ctx.scriptInjections.some((si) => si.name === ROZIE_PORTAL_ANCHORS_FIELD_NAME)) {
|
|
84131
|
+
ctx.scriptInjections.push({
|
|
84132
|
+
name: ROZIE_PORTAL_ANCHORS_FIELD_NAME,
|
|
84133
|
+
decl: portalAnchorsFieldDecl()
|
|
84134
|
+
});
|
|
84135
|
+
ctx.scriptInjections.push({
|
|
84136
|
+
name: ROZIE_PORTAL_MOVED_FIELD_NAME,
|
|
84137
|
+
decl: portalMovedFieldDecl()
|
|
84138
|
+
});
|
|
84139
|
+
ctx.scriptInjections.push({
|
|
84140
|
+
name: ROZIE_PORTAL_PLACE_FIELD_NAME,
|
|
84141
|
+
decl: portalPlaceMethodDecl()
|
|
84142
|
+
});
|
|
84143
|
+
}
|
|
84144
|
+
ctx.scriptInjections.push({
|
|
84145
|
+
name: destroyRegisteredFieldName,
|
|
84146
|
+
decl: `private ${destroyRegisteredFieldName} = false;`
|
|
84147
|
+
});
|
|
84148
|
+
const effectDecl = [
|
|
84149
|
+
`private ${effectFieldName} = effect(() => {`,
|
|
84150
|
+
` const el = this.${refName}()?.nativeElement;`,
|
|
84151
|
+
` if (!el) return;`,
|
|
84152
|
+
` this.${ROZIE_PORTAL_PLACE_FIELD_NAME}(el, ${containerCode});`,
|
|
84153
|
+
` if (!this.${destroyRegisteredFieldName}) {`,
|
|
84154
|
+
` this.${destroyRegisteredFieldName} = true;`,
|
|
84155
|
+
` this.__rozieDestroyRef.onDestroy(() => {`,
|
|
84156
|
+
` for (const moved of this.${ROZIE_PORTAL_MOVED_FIELD_NAME}) {`,
|
|
84157
|
+
` moved.parentNode?.removeChild(moved);`,
|
|
84158
|
+
` }`,
|
|
84159
|
+
` });`,
|
|
84160
|
+
` }`,
|
|
84161
|
+
`});`
|
|
84162
|
+
].join("\n");
|
|
84163
|
+
ctx.scriptInjections.push({
|
|
84164
|
+
name: effectFieldName,
|
|
84165
|
+
decl: effectDecl
|
|
84166
|
+
});
|
|
84167
|
+
}
|
|
84168
|
+
if (ctx.hasListenerSpread !== void 0) ctx.hasListenerSpread.value = true;
|
|
84169
|
+
if (ctx.needsDestroyRefField !== void 0) ctx.needsDestroyRefField.value = true;
|
|
84170
|
+
return `#${refName}`;
|
|
84171
|
+
}
|
|
84172
|
+
/**
|
|
83283
84173
|
* Emit a single attribute. Returns null when the attribute should be dropped
|
|
83284
84174
|
* (e.g., r-html, which gets emitted later as `[innerHTML]="..."` by the
|
|
83285
84175
|
* element emitter).
|
|
@@ -83326,6 +84216,7 @@ function emitSingleAttr(attr, ctx, elementTagName) {
|
|
|
83326
84216
|
if (ctx.hasDisplayWrap) ctx.hasDisplayWrap.value = true;
|
|
83327
84217
|
return `[${bindingName.startsWith("attr.") ? bindingName : `attr.${attr.name}`}]="rozieAttr(${expr})"`;
|
|
83328
84218
|
}
|
|
84219
|
+
if (attr.name === "style" && isProvablyStringStyleExpression(attr.expression)) return `[attr.style]="${expr}"`;
|
|
83329
84220
|
return `[${bindingName}]="${expr}"`;
|
|
83330
84221
|
}
|
|
83331
84222
|
if (attr.segments.length === 1 && attr.segments[0].kind === "binding") {
|
|
@@ -84503,6 +85394,21 @@ function emitElementInner$2(origNode, ctx) {
|
|
|
84503
85394
|
if (attrText) partsHead.push(attrText);
|
|
84504
85395
|
if (eventText) partsHead.push(eventText);
|
|
84505
85396
|
for (const text of dynamicListenerTexts) partsHead.push(text);
|
|
85397
|
+
if (node.portalTo) {
|
|
85398
|
+
const portalRefText = emitPortalDirective(node, {
|
|
85399
|
+
ir: ctx.ir,
|
|
85400
|
+
collisionRenames: ctx.collisionRenames,
|
|
85401
|
+
loopBindings: ctx.loopBindings,
|
|
85402
|
+
scriptInjections: ctx.scriptInjections,
|
|
85403
|
+
injectionCounter: ctx.injectionCounter,
|
|
85404
|
+
hasListenerSpread: ctx.hasListenerSpread,
|
|
85405
|
+
needsDestroyRefField: ctx.needsDestroyRefField,
|
|
85406
|
+
cvaModelProp: ctx.cvaModelProp,
|
|
85407
|
+
cvaMergeDisabled: ctx.cvaMergeDisabled,
|
|
85408
|
+
classMembers: ctx.classMembers
|
|
85409
|
+
});
|
|
85410
|
+
partsHead.push(portalRefText);
|
|
85411
|
+
}
|
|
84506
85412
|
const keynav = ctx.keynav ?? null;
|
|
84507
85413
|
const keynavAttrs = [...keynavRootAttrs$2(keynav, node, ctx.ir), ...keynavItemAttrs$2(keynav, node, ctx.keynavItemIndexAlias ?? null, ctx.ir)];
|
|
84508
85414
|
for (const a of keynavAttrs) partsHead.push(a);
|
|
@@ -86150,7 +87056,7 @@ function renderType$1(ann) {
|
|
|
86150
87056
|
case "Boolean": return "boolean";
|
|
86151
87057
|
case "Array": return "any[]";
|
|
86152
87058
|
case "Object": return "Record<string, any>";
|
|
86153
|
-
case "Function": return "(...args:
|
|
87059
|
+
case "Function": return "(...args: any[]) => any";
|
|
86154
87060
|
default: return ann.name;
|
|
86155
87061
|
}
|
|
86156
87062
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -86160,7 +87066,7 @@ function renderType$1(ann) {
|
|
|
86160
87066
|
if (ann.kind === "literal") {
|
|
86161
87067
|
if (ann.value === "array") return "any[]";
|
|
86162
87068
|
if (ann.value === "object") return "Record<string, any>";
|
|
86163
|
-
if (ann.value === "function") return "(...args:
|
|
87069
|
+
if (ann.value === "function") return "(...args: any[]) => any";
|
|
86164
87070
|
return ann.value;
|
|
86165
87071
|
}
|
|
86166
87072
|
return "unknown";
|
|
@@ -86289,6 +87195,63 @@ function buildSetterCall$1(varName, operator, rhs) {
|
|
|
86289
87195
|
return _babel_types.callExpression(_babel_types.identifier(setterName), [_babel_types.binaryExpression(binOp, _babel_types.callExpression(_babel_types.identifier(varName), []), rhs)]);
|
|
86290
87196
|
}
|
|
86291
87197
|
/**
|
|
87198
|
+
* 260712-ig6 Task B — detects the "null-widened prop spliced into a 3rd-party
|
|
87199
|
+
* object-literal call argument" shape: a `$props.<name>` read (where `<name>`
|
|
87200
|
+
* has `default: null`) that ultimately feeds an `ObjectProperty` VALUE inside
|
|
87201
|
+
* an `ObjectExpression` that is (possibly via nested object-literal property
|
|
87202
|
+
* values, e.g. `useEngine(el, { options: { group: $props.group } })`) itself
|
|
87203
|
+
* a DIRECT argument of a `CallExpression` — the mount-time engine-init shape
|
|
87204
|
+
* (`useSortableJS($refs.listEl, { options: { handle: $props.handle, ... } })`).
|
|
87205
|
+
*
|
|
87206
|
+
* Walks UP from the read, tolerating exactly two pass-through shapes on the
|
|
87207
|
+
* way to an `ObjectProperty` value:
|
|
87208
|
+
* - `ConditionalExpression` consequent/alternate (the `group` prop's
|
|
87209
|
+
* `cloneable ? { ... } : $props.group` ternary)
|
|
87210
|
+
* - nested `ObjectExpression` → `ObjectProperty` value chains (an object
|
|
87211
|
+
* literal nested inside another object literal's property value)
|
|
87212
|
+
*
|
|
87213
|
+
* Any OTHER parent shape (binary/logical expression, array literal, bare
|
|
87214
|
+
* statement, non-direct call argument, …) fails the match — this is
|
|
87215
|
+
* intentionally narrow, mirroring Pattern D's `isDirectCallArg` precedent.
|
|
87216
|
+
* Distinct from Pattern D: Pattern D fires on `$refs` DOM-ref reads that are
|
|
87217
|
+
* THEMSELVES a direct call argument; this fires on `$props` reads nested
|
|
87218
|
+
* inside an object-literal call argument.
|
|
87219
|
+
*
|
|
87220
|
+
* `excludeCallees` DISQUALIFIES the match when the terminal call's callee is
|
|
87221
|
+
* one of the emitter's OWN synthesized reactive setters (`set<Data>` /
|
|
87222
|
+
* `set<ModelProp>`, minted by `buildSetterCall` for `$data.x = {...}` /
|
|
87223
|
+
* `$model.x = {...}` writes). Those setter calls are Rozie-internal — the
|
|
87224
|
+
* `{...}` argument feeds a Solid `createSignal` setter, not a 3rd-party lib's
|
|
87225
|
+
* typed options object, so a null-widened prop spliced into one must stay
|
|
87226
|
+
* `null` verbatim (the cross-target `PropDefaultCoercion` conformance probe
|
|
87227
|
+
* asserts `JSON.stringify({ a: null, ... })` renders the literal `"a":null`
|
|
87228
|
+
* substring on every target — `?? undefined` would DROP the key entirely,
|
|
87229
|
+
* a real cross-target behavior regression, not a type-only change).
|
|
87230
|
+
*/
|
|
87231
|
+
function isNullWidenedPropObjectLiteralCallArgTarget(path, excludeCallees) {
|
|
87232
|
+
let cur = path;
|
|
87233
|
+
for (;;) {
|
|
87234
|
+
const parent = cur.parentPath;
|
|
87235
|
+
if (!parent) return false;
|
|
87236
|
+
if (parent.isConditionalExpression() && (parent.node.consequent === cur.node || parent.node.alternate === cur.node)) {
|
|
87237
|
+
cur = parent;
|
|
87238
|
+
continue;
|
|
87239
|
+
}
|
|
87240
|
+
if (parent.isObjectProperty() && parent.node.value === cur.node) {
|
|
87241
|
+
const objExpr = parent.parentPath;
|
|
87242
|
+
if (!objExpr || !objExpr.isObjectExpression()) return false;
|
|
87243
|
+
cur = objExpr;
|
|
87244
|
+
continue;
|
|
87245
|
+
}
|
|
87246
|
+
if (parent.isCallExpression() && parent.node.arguments.some((arg) => arg === cur.node)) {
|
|
87247
|
+
const callee = parent.node.callee;
|
|
87248
|
+
if (_babel_types.isIdentifier(callee) && excludeCallees.has(callee.name)) return false;
|
|
87249
|
+
return true;
|
|
87250
|
+
}
|
|
87251
|
+
return false;
|
|
87252
|
+
}
|
|
87253
|
+
}
|
|
87254
|
+
/**
|
|
86292
87255
|
* Phase 18 (Req 2) — normalize the producer-side two-way-write sigil `$model`
|
|
86293
87256
|
* to `$props` across a cloned File, in place. See the call-site comment in
|
|
86294
87257
|
* `rewriteRozieIdentifiers` for the full contract; `$model.X` is model-only and
|
|
@@ -86395,7 +87358,7 @@ function hoistPolymorphicModelGuards(cloned, polymorphicModelProps) {
|
|
|
86395
87358
|
*
|
|
86396
87359
|
* @experimental — shape may change before v1.0
|
|
86397
87360
|
*/
|
|
86398
|
-
function rewriteRozieExpressionNode(expr, ir) {
|
|
87361
|
+
function rewriteRozieExpressionNode(expr, ir, options) {
|
|
86399
87362
|
let programBody;
|
|
86400
87363
|
const isBlock = _babel_types.isBlockStatement(expr);
|
|
86401
87364
|
if (isBlock) programBody = _babel_types.cloneNode(expr, true, false).body;
|
|
@@ -86409,7 +87372,7 @@ function rewriteRozieExpressionNode(expr, ir) {
|
|
|
86409
87372
|
sourceType: "module"
|
|
86410
87373
|
},
|
|
86411
87374
|
comments: []
|
|
86412
|
-
}, ir).rewrittenProgram.program.body;
|
|
87375
|
+
}, ir, options).rewrittenProgram.program.body;
|
|
86413
87376
|
if (isBlock) return _babel_types.blockStatement(body);
|
|
86414
87377
|
if (body.length === 1 && _babel_types.isExpressionStatement(body[0])) return body[0].expression;
|
|
86415
87378
|
const last = body[body.length - 1];
|
|
@@ -86420,10 +87383,12 @@ function rewriteRozieExpressionNode(expr, ir) {
|
|
|
86420
87383
|
* Full Solid identifier rewrite pass. Replaces all $props/$data/$refs/$emit
|
|
86421
87384
|
* references with their Solid-idiomatic equivalents.
|
|
86422
87385
|
*/
|
|
86423
|
-
function rewriteRozieIdentifiers(cloned, ir) {
|
|
87386
|
+
function rewriteRozieIdentifiers(cloned, ir, options) {
|
|
86424
87387
|
const diagnostics = [];
|
|
87388
|
+
const nonNullRefCallArgs = options?.nonNullRefCallArgs === true;
|
|
86425
87389
|
const modelProps = new Set(ir.props.filter((p) => p.isModel).map((p) => p.name));
|
|
86426
87390
|
const nonModelProps = new Set(ir.props.filter((p) => !p.isModel).map((p) => p.name));
|
|
87391
|
+
const nullWidenedNonModelProps = new Set(ir.props.filter((p) => !p.isModel && _babel_types.isNullLiteral(p.defaultValue)).map((p) => p.name));
|
|
86427
87392
|
const unknownModelProps = new Set(ir.props.filter((p) => p.isModel && renderType$1(p.typeAnnotation) === "unknown").map((p) => p.name));
|
|
86428
87393
|
const polymorphicModelProps = new Set(ir.props.filter((p) => p.isModel && (p.typeAnnotation.kind === "union" || renderType$1(p.typeAnnotation) === "unknown")).map((p) => p.name));
|
|
86429
87394
|
const dataNames = new Set(ir.state.map((s) => s.name));
|
|
@@ -86540,6 +87505,12 @@ function rewriteRozieIdentifiers(cloned, ir) {
|
|
|
86540
87505
|
return;
|
|
86541
87506
|
}
|
|
86542
87507
|
if (nonModelProps.has(property.name)) {
|
|
87508
|
+
if (nonNullRefCallArgs && nullWidenedNonModelProps.has(property.name) && isNullWidenedPropObjectLiteralCallArgTarget(path, solidSetters)) {
|
|
87509
|
+
const localRead = _babel_types.memberExpression(_babel_types.identifier("local"), _babel_types.identifier(property.name));
|
|
87510
|
+
path.replaceWith(_babel_types.logicalExpression("??", localRead, _babel_types.identifier("undefined")));
|
|
87511
|
+
path.skip();
|
|
87512
|
+
return;
|
|
87513
|
+
}
|
|
86543
87514
|
path.node.object = _babel_types.identifier("local");
|
|
86544
87515
|
return;
|
|
86545
87516
|
}
|
|
@@ -86553,7 +87524,8 @@ function rewriteRozieIdentifiers(cloned, ir) {
|
|
|
86553
87524
|
}
|
|
86554
87525
|
if (object.name === "$refs" && refNames.has(property.name)) {
|
|
86555
87526
|
const refIdent = _babel_types.identifier(property.name + "Ref");
|
|
86556
|
-
|
|
87527
|
+
const isDirectCallArg = nonNullRefCallArgs && path.parentPath?.isCallExpression() && path.parentPath.node.arguments.includes(path.node);
|
|
87528
|
+
if (property.name === "__rozieRoot" || isDirectCallArg) path.replaceWith(_babel_types.tsNonNullExpression(refIdent));
|
|
86557
87529
|
else path.replaceWith(refIdent);
|
|
86558
87530
|
path.skip();
|
|
86559
87531
|
return;
|
|
@@ -86934,6 +87906,19 @@ function genCode$1(node) {
|
|
|
86934
87906
|
return generate$7(node, GEN_OPTS$6).code;
|
|
86935
87907
|
}
|
|
86936
87908
|
/**
|
|
87909
|
+
* Quick task 260714-orv — render hoisted user imports in ONE
|
|
87910
|
+
* @babel/generator pass so a comment shared between two adjacent imports
|
|
87911
|
+
* (Babel attaches it as BOTH the earlier import's `trailingComments` AND the
|
|
87912
|
+
* later import's `leadingComments`) prints exactly once. Generating imports
|
|
87913
|
+
* one at a time (`nodes.map(genCode).join`) gives each import its OWN
|
|
87914
|
+
* comment-dedup set, doubling any shared comment. `t.program(nodes)` prints
|
|
87915
|
+
* only its body statements (no wrapping braces/`File` boilerplate), so
|
|
87916
|
+
* non-comment cases stay byte-identical.
|
|
87917
|
+
*/
|
|
87918
|
+
function genImportsBlock(nodes) {
|
|
87919
|
+
return generate$7(_babel_types.program(nodes), GEN_OPTS$6).code;
|
|
87920
|
+
}
|
|
87921
|
+
/**
|
|
86937
87922
|
* ROZ-cast-blindness fix — Solid emits each ComputedDecl's body via
|
|
86938
87923
|
* `rewriteNode(c.body, ir)` directly from IR (never re-scanning the cloned
|
|
86939
87924
|
* Program for the body itself), so there is no existing "find the cloned
|
|
@@ -86992,6 +87977,17 @@ function capitalize$6(name) {
|
|
|
86992
87977
|
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
86993
87978
|
}
|
|
86994
87979
|
/**
|
|
87980
|
+
* 260712-kl1 — exact-AST-shape gate mirroring the `isMutableLiteralFactoryDefault`
|
|
87981
|
+
* / `isNullWidenedPropObjectLiteralCallArgTarget` predicate convention. Matches
|
|
87982
|
+
* ONLY a Babel `NullLiteral` prop default (`default: null`) — never `undefined`,
|
|
87983
|
+
* `void 0`, or any other falsy/nullish shape. Used to widen a model prop's
|
|
87984
|
+
* `createControllableSignal<T>` generic to `T | null` (see 260712-kb9's
|
|
87985
|
+
* NEW-controllable-null-default catalog entry).
|
|
87986
|
+
*/
|
|
87987
|
+
function isNullLiteralDefault(dv) {
|
|
87988
|
+
return _babel_types.isNullLiteral(dv);
|
|
87989
|
+
}
|
|
87990
|
+
/**
|
|
86995
87991
|
* WR-01 fix (73-REVIEW.md, 73-10 gap-closure): collect every Identifier
|
|
86996
87992
|
* binding name introduced by a declarator's `id` pattern — Identifier
|
|
86997
87993
|
* directly, or recursively through ObjectPattern / ArrayPattern /
|
|
@@ -87204,7 +88200,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87204
88200
|
diagnostics.push(...rewriteResult.diagnostics);
|
|
87205
88201
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts } = partitionUserImports$1(rewriteResult.rewrittenProgram);
|
|
87206
88202
|
rewriteResult.rewrittenProgram.program.body = bodyStmts;
|
|
87207
|
-
const userImports = userImportNodes.length > 0 ? userImportNodes
|
|
88203
|
+
const userImports = userImportNodes.length > 0 ? genImportsBlock(userImportNodes) + "\n" : "";
|
|
87208
88204
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => genCode$1(decl));
|
|
87209
88205
|
const contextEmit = emitContext$1(ir, {
|
|
87210
88206
|
solid: collectors.solidImports,
|
|
@@ -87220,7 +88216,9 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87220
88216
|
const dv = p.defaultValue;
|
|
87221
88217
|
const raw = genCode$1(dv);
|
|
87222
88218
|
const val = isMutableLiteralFactoryDefault(dv) ? `(${raw})()` : raw;
|
|
87223
|
-
|
|
88219
|
+
const ann = p.typeAnnotation;
|
|
88220
|
+
const entryVal = isMutableLiteralFactoryDefault(dv) && ann.kind === "identifier" && (ann.name === "Object" || ann.name === "Array") ? `${val} as ${renderType$1(ann)}` : val;
|
|
88221
|
+
return `${p.name}: ${entryVal}`;
|
|
87224
88222
|
}).join(", ")} }, _props);\n`;
|
|
87225
88223
|
}
|
|
87226
88224
|
for (const p of ir.props) {
|
|
@@ -87234,7 +88232,8 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87234
88232
|
else dflt = raw;
|
|
87235
88233
|
}
|
|
87236
88234
|
const tsType = renderType$1(p.typeAnnotation);
|
|
87237
|
-
|
|
88235
|
+
const genericType = isNullLiteralDefault(p.defaultValue) && tsType !== "unknown" ? `${tsType} | null` : tsType;
|
|
88236
|
+
hookLines.push(`const [${p.name}, ${setterName}] = createControllableSignal<${genericType}>(_props as unknown as Record<string, unknown>, '${p.name}', ${dflt});`);
|
|
87238
88237
|
}
|
|
87239
88238
|
for (const s of ir.state) {
|
|
87240
88239
|
collectors.solidImports.add("createSignal");
|
|
@@ -87266,7 +88265,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87266
88265
|
for (const lh of ir.lifecycle) if (lh.phase === "mount") if (lh.cleanup) {
|
|
87267
88266
|
collectors.solidImports.add("onMount");
|
|
87268
88267
|
collectors.solidImports.add("onCleanup");
|
|
87269
|
-
const rewrittenSetup = rewriteRozieExpressionNode(lh.setup, ir);
|
|
88268
|
+
const rewrittenSetup = rewriteRozieExpressionNode(lh.setup, ir, { nonNullRefCallArgs: true });
|
|
87270
88269
|
const rewrittenCleanup = rewriteRozieExpressionNode(lh.cleanup, ir);
|
|
87271
88270
|
if (_babel_types.isBlockStatement(lh.setup) && cleanupReferencesSetupLocal(lh.setup, lh.cleanup)) {
|
|
87272
88271
|
const setupStatements = rewrittenSetup.body;
|
|
@@ -87280,7 +88279,7 @@ function emitScript$1(ir, collectors, _registry) {
|
|
|
87280
88279
|
}
|
|
87281
88280
|
} else {
|
|
87282
88281
|
collectors.solidImports.add("onMount");
|
|
87283
|
-
const arg = lifecycleArg(rewriteRozieExpressionNode(lh.setup, ir));
|
|
88282
|
+
const arg = lifecycleArg(rewriteRozieExpressionNode(lh.setup, ir, { nonNullRefCallArgs: true }));
|
|
87284
88283
|
hookLines.push(`onMount(${arg});`);
|
|
87285
88284
|
}
|
|
87286
88285
|
else if (lh.phase === "unmount") {
|
|
@@ -89955,7 +90954,22 @@ function emitLoop$1(node, ctx) {
|
|
|
89955
90954
|
invokeAccessors: ctx.invokeAccessors,
|
|
89956
90955
|
loopValueBindings: new Set([...ctx.loopValueBindings ?? [], node.itemAlias])
|
|
89957
90956
|
});
|
|
89958
|
-
return `<Key each={${
|
|
90957
|
+
return `<Key each={${new Set([
|
|
90958
|
+
"Identifier",
|
|
90959
|
+
"MemberExpression",
|
|
90960
|
+
"OptionalMemberExpression",
|
|
90961
|
+
"CallExpression",
|
|
90962
|
+
"OptionalCallExpression",
|
|
90963
|
+
"NewExpression",
|
|
90964
|
+
"ArrayExpression",
|
|
90965
|
+
"ObjectExpression",
|
|
90966
|
+
"TSNonNullExpression",
|
|
90967
|
+
"TSAsExpression",
|
|
90968
|
+
"ParenthesizedExpression",
|
|
90969
|
+
"StringLiteral",
|
|
90970
|
+
"NumericLiteral",
|
|
90971
|
+
"TemplateLiteral"
|
|
90972
|
+
]).has(node.iterableExpression.type) ? iterableCode : `(${iterableCode})`} as readonly any[]} by={(${node.itemAlias}) => ${keyCode}}>{${aliasStr} => ${bodyJsx}}</Key>`;
|
|
89959
90973
|
}
|
|
89960
90974
|
/**
|
|
89961
90975
|
* Find an attribute by name.
|
|
@@ -90301,8 +91315,34 @@ function mergeEventAttributes(attrsJsx, eventsJsx, elementTag) {
|
|
|
90301
91315
|
* (so the child is never hidden) while still changing value — and thus
|
|
90302
91316
|
* still triggering `keyed` recreation — whenever the underlying key changes.
|
|
90303
91317
|
*/
|
|
91318
|
+
/**
|
|
91319
|
+
* command-palette-portal-overlay phase — `r-portal="<expr>"` element
|
|
91320
|
+
* teleport. Wraps the element markup in Solid's native `<Show when={c}
|
|
91321
|
+
* fallback={tree}><Portal mount={c}>{tree}</Portal></Show>` — falsy
|
|
91322
|
+
* container renders `tree` in place via the `fallback` prop (never mounts
|
|
91323
|
+
* `<Portal>` at all), matching React's `container ? createPortal(tree,
|
|
91324
|
+
* container) : tree` and Vue's `<Teleport :disabled>` in-place semantics.
|
|
91325
|
+
*
|
|
91326
|
+
* SSR guard: `typeof document === 'undefined'` short-circuits the container
|
|
91327
|
+
* expression to `null` BEFORE it is evaluated at all — some author
|
|
91328
|
+
* expressions call `document.querySelector` internally (see
|
|
91329
|
+
* `resolveAppendTo`/`resolveTo`), so the guard must gate evaluation, not
|
|
91330
|
+
* just the result.
|
|
91331
|
+
*/
|
|
91332
|
+
function emitPortalWrap(node, markup, ctx) {
|
|
91333
|
+
const { expression } = node.portalTo;
|
|
91334
|
+
if (ctx.elementPortalImport) ctx.elementPortalImport.needed = true;
|
|
91335
|
+
const containerCode = `(typeof document === 'undefined' ? null : (${rewriteTemplateExpression$1(expression, ctx.ir, {
|
|
91336
|
+
invokeAccessors: ctx.invokeAccessors,
|
|
91337
|
+
loopValueBindings: ctx.loopValueBindings,
|
|
91338
|
+
scopeAccessorParams: ctx.scopeAccessorParams
|
|
91339
|
+
})}))`;
|
|
91340
|
+
ctx.collectors.solid.add("Show");
|
|
91341
|
+
return `<Show when={${containerCode}} fallback={${markup}}><Portal mount={${containerCode}}>${markup}</Portal></Show>`;
|
|
91342
|
+
}
|
|
90304
91343
|
function emitElement$1(origNode, ctx) {
|
|
90305
91344
|
const markup = emitElementInner$1(origNode, ctx);
|
|
91345
|
+
if (origNode.portalTo) return emitPortalWrap(origNode, markup, ctx);
|
|
90306
91346
|
if ((origNode.tagKind === "component" || origNode.tagKind === "self") && origNode.remountKeyExpression) {
|
|
90307
91347
|
const keyExprCode = rewriteTemplateExpression$1(origNode.remountKeyExpression, ctx.ir, {
|
|
90308
91348
|
invokeAccessors: ctx.invokeAccessors,
|
|
@@ -90540,11 +91580,13 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90540
91580
|
jsx: "null",
|
|
90541
91581
|
scriptInjections: [],
|
|
90542
91582
|
needsKeyedImport: false,
|
|
90543
|
-
diagnostics: []
|
|
91583
|
+
diagnostics: [],
|
|
91584
|
+
hasElementPortal: false
|
|
90544
91585
|
};
|
|
90545
91586
|
const scriptInjections = [];
|
|
90546
91587
|
const injectionCounter = { next: 0 };
|
|
90547
91588
|
const keyedImport = { needed: false };
|
|
91589
|
+
const elementPortalImport = { needed: false };
|
|
90548
91590
|
const keynav = resolveKeynavPlan$1(ir);
|
|
90549
91591
|
const ctx = {
|
|
90550
91592
|
ir,
|
|
@@ -90554,6 +91596,7 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90554
91596
|
scriptInjections,
|
|
90555
91597
|
injectionCounter,
|
|
90556
91598
|
keyedImport,
|
|
91599
|
+
elementPortalImport,
|
|
90557
91600
|
keynav,
|
|
90558
91601
|
...opts.scopeAttr !== void 0 ? { scopeAttr: opts.scopeAttr } : {}
|
|
90559
91602
|
};
|
|
@@ -90563,7 +91606,8 @@ function emitTemplate$1(ir, collectors, registry, opts = {}) {
|
|
|
90563
91606
|
jsx,
|
|
90564
91607
|
scriptInjections,
|
|
90565
91608
|
needsKeyedImport: keyedImport.needed,
|
|
90566
|
-
diagnostics
|
|
91609
|
+
diagnostics,
|
|
91610
|
+
hasElementPortal: elementPortalImport.needed
|
|
90567
91611
|
};
|
|
90568
91612
|
}
|
|
90569
91613
|
//#endregion
|
|
@@ -91532,7 +92576,7 @@ function emitSolid(ir, opts = {}) {
|
|
|
91532
92576
|
...listenersResult.scriptInjections
|
|
91533
92577
|
].filter((s) => s.trim().length > 0).join("\n\n");
|
|
91534
92578
|
const solidImportsStr = `import type { JSX } from 'solid-js';\n` + solidImports.render();
|
|
91535
|
-
const portalImport = scriptResult.hasPortals ? "import { render } from 'solid-js/web';\n" : "";
|
|
92579
|
+
const portalImport = (scriptResult.hasPortals ? "import { render } from 'solid-js/web';\n" : "") + (templateResult.hasElementPortal ? "import { Portal } from 'solid-js/web';\n" : "");
|
|
91536
92580
|
const keyedImport = templateResult.needsKeyedImport ? "import { Key } from '@solid-primitives/keyed';\n" : "";
|
|
91537
92581
|
const shell = buildShell$1({
|
|
91538
92582
|
componentName: ir.name,
|
|
@@ -93024,7 +94068,7 @@ function renderTsType(ann) {
|
|
|
93024
94068
|
case "Boolean": return "boolean";
|
|
93025
94069
|
case "Array": return "any[]";
|
|
93026
94070
|
case "Object": return "any";
|
|
93027
|
-
case "Function": return "((...args:
|
|
94071
|
+
case "Function": return "((...args: any[]) => any) | null";
|
|
93028
94072
|
default: return ann.name;
|
|
93029
94073
|
}
|
|
93030
94074
|
if (ann.kind === "union") return ann.members.map((m) => {
|
|
@@ -93053,14 +94097,81 @@ function renderTsType(ann) {
|
|
|
93053
94097
|
* The `!`-shape matches what Rozie already emits for Angular `@Input` and Lit
|
|
93054
94098
|
* `@query` ref fields.
|
|
93055
94099
|
*/
|
|
94100
|
+
/**
|
|
94101
|
+
* 260712-a09 (Pattern B) — an explicit `default: undefined` (or `default: void 0`)
|
|
94102
|
+
* is authorial shorthand for "no meaningful default", not a real default value.
|
|
94103
|
+
* `prop.defaultValue` is a live (non-null) AST node for either form, so the
|
|
94104
|
+
* `prop.defaultValue != null` guard below would otherwise route it into the
|
|
94105
|
+
* `= <default>` initializer branch and emit `foo: string = undefined` — a
|
|
94106
|
+
* TS2322 under strictNullChecks (a typed field can't be initialized with
|
|
94107
|
+
* `undefined` unless the type itself admits it). Detect both spellings here
|
|
94108
|
+
* and treat them identically to "no default" so they fall through to the
|
|
94109
|
+
* existing `?:`/`!:` branch.
|
|
94110
|
+
*/
|
|
94111
|
+
function isUndefinedDefault(expr) {
|
|
94112
|
+
return _babel_types.isIdentifier(expr, { name: "undefined" }) || _babel_types.isUnaryExpression(expr) && expr.operator === "void";
|
|
94113
|
+
}
|
|
94114
|
+
/**
|
|
94115
|
+
* 260712-ig6 Task A — the full ARIAMixin field-name set (lib.dom.d.ts). A
|
|
94116
|
+
* prop authored with one of these names collides with LitElement's own
|
|
94117
|
+
* `implements ARIAMixin` field declaration (always `<T> | null`, NEVER
|
|
94118
|
+
* `undefined`-admitting) if it lands in the ordinary no-default `?:`/`!:`
|
|
94119
|
+
* branch below — Pattern B's `default: undefined` routes there and would
|
|
94120
|
+
* otherwise emit `ariaLabel?: string;` (`string | undefined`), which is not
|
|
94121
|
+
* assignable to the base class's `string | null` field (TS2416/TS1240).
|
|
94122
|
+
* Gated STRICTLY on name-set membership so every other prop name keeps its
|
|
94123
|
+
* pre-existing byte-identical `?:`/`!:` output.
|
|
94124
|
+
*/
|
|
94125
|
+
const ARIA_MIXIN_FIELDS = new Set([
|
|
94126
|
+
"ariaAtomic",
|
|
94127
|
+
"ariaAutoComplete",
|
|
94128
|
+
"ariaBusy",
|
|
94129
|
+
"ariaChecked",
|
|
94130
|
+
"ariaColCount",
|
|
94131
|
+
"ariaColIndex",
|
|
94132
|
+
"ariaColSpan",
|
|
94133
|
+
"ariaCurrent",
|
|
94134
|
+
"ariaDescription",
|
|
94135
|
+
"ariaDisabled",
|
|
94136
|
+
"ariaExpanded",
|
|
94137
|
+
"ariaHasPopup",
|
|
94138
|
+
"ariaHidden",
|
|
94139
|
+
"ariaInvalid",
|
|
94140
|
+
"ariaKeyShortcuts",
|
|
94141
|
+
"ariaLabel",
|
|
94142
|
+
"ariaLevel",
|
|
94143
|
+
"ariaLive",
|
|
94144
|
+
"ariaModal",
|
|
94145
|
+
"ariaMultiLine",
|
|
94146
|
+
"ariaMultiSelectable",
|
|
94147
|
+
"ariaOrientation",
|
|
94148
|
+
"ariaPlaceholder",
|
|
94149
|
+
"ariaPosInSet",
|
|
94150
|
+
"ariaPressed",
|
|
94151
|
+
"ariaReadOnly",
|
|
94152
|
+
"ariaRequired",
|
|
94153
|
+
"ariaRoleDescription",
|
|
94154
|
+
"ariaRowCount",
|
|
94155
|
+
"ariaRowIndex",
|
|
94156
|
+
"ariaRowSpan",
|
|
94157
|
+
"ariaSelected",
|
|
94158
|
+
"ariaSetSize",
|
|
94159
|
+
"ariaSort",
|
|
94160
|
+
"ariaValueMax",
|
|
94161
|
+
"ariaValueMin",
|
|
94162
|
+
"ariaValueNow",
|
|
94163
|
+
"ariaValueText",
|
|
94164
|
+
"role"
|
|
94165
|
+
]);
|
|
93056
94166
|
function renderFieldSuffix(prop) {
|
|
93057
94167
|
const tsType = renderTsType(prop.typeAnnotation);
|
|
93058
|
-
if (prop.defaultValue != null) {
|
|
94168
|
+
if (prop.defaultValue != null && !isUndefinedDefault(prop.defaultValue)) {
|
|
93059
94169
|
let fieldType = tsType;
|
|
93060
94170
|
const admitsNull = /\bnull\b/.test(fieldType) || /(^|\|)\s*(any|unknown)\s*(\||$)/.test(fieldType);
|
|
93061
94171
|
if (_babel_types.isNullLiteral(prop.defaultValue) && !admitsNull) fieldType = `${fieldType} | null`;
|
|
93062
94172
|
return `: ${fieldType} = ${renderDefault(prop.defaultValue, prop.typeAnnotation)}`;
|
|
93063
94173
|
}
|
|
94174
|
+
if (ARIA_MIXIN_FIELDS.has(prop.name)) return `: ${/\bnull\b/.test(tsType) || /(^|\|)\s*(any|unknown)\s*(\||$)/.test(tsType) ? tsType : `${tsType} | null`} = null`;
|
|
93064
94175
|
return prop.required ? `!: ${tsType}` : `?: ${tsType}`;
|
|
93065
94176
|
}
|
|
93066
94177
|
function isPrimitiveType(ann) {
|
|
@@ -93433,12 +94544,15 @@ function emitScript(ir, opts) {
|
|
|
93433
94544
|
const composedTypeImportLines = [];
|
|
93434
94545
|
for (const typeName of composedTypeImports) {
|
|
93435
94546
|
const decl = (ir.components ?? []).find((c) => c.localName === typeName);
|
|
93436
|
-
if (decl)
|
|
94547
|
+
if (decl) {
|
|
94548
|
+
const importPath = isPublishedSpecifier(decl.importPath) ? rewriteRozieImport(decl.importPath, "lit") : decl.importPath;
|
|
94549
|
+
composedTypeImportLines.push(`import type { ${typeName} } from '${importPath}';`);
|
|
94550
|
+
}
|
|
93437
94551
|
}
|
|
93438
94552
|
const rewritten = rewriteScript(ir.setupBody.scriptProgram, ir, { runtime: opts.runtime });
|
|
93439
94553
|
const { userImports: userImportNodes, hoistedTypeDecls: hoistedTypeNodes, bodyStmts: nonImportStmts } = partitionUserImports(rewritten.file);
|
|
93440
94554
|
rewritten.file.program.body = nonImportStmts;
|
|
93441
|
-
const userImports = (composedTypeImportLines.length > 0 ? composedTypeImportLines.join("\n") + "\n" : "") + (userImportNodes.length > 0 ?
|
|
94555
|
+
const userImports = (composedTypeImportLines.length > 0 ? composedTypeImportLines.join("\n") + "\n" : "") + (userImportNodes.length > 0 ? generate$1(_babel_types.program(userImportNodes), GEN_OPTS$1).code + "\n" : "");
|
|
93442
94556
|
const hoistedTypeDecls = hoistedTypeNodes.map((decl) => generate$1(decl, GEN_OPTS$1).code);
|
|
93443
94557
|
const partition = partitionScript(rewritten.file);
|
|
93444
94558
|
const computedNames = new Set(ir.computed.map((c) => c.name));
|
|
@@ -95166,6 +96280,7 @@ function emitElementOpenTag(node, ir, irName, opts) {
|
|
|
95166
96280
|
const tagName = resolveTagName(node, irName);
|
|
95167
96281
|
const parts = [];
|
|
95168
96282
|
let refAttr = null;
|
|
96283
|
+
let portalSentinel = "";
|
|
95169
96284
|
const rModelAttr = node.attributes.find((a) => attributeIsRModel(a));
|
|
95170
96285
|
const staticClassValues = [];
|
|
95171
96286
|
let bindingClass = null;
|
|
@@ -95268,16 +96383,27 @@ function emitElementOpenTag(node, ir, irName, opts) {
|
|
|
95268
96383
|
parts.push(`\${rozieListeners(${expr})}`);
|
|
95269
96384
|
}
|
|
95270
96385
|
if (refAttr) parts.push(refAttr);
|
|
96386
|
+
if (node.portalTo && opts._state) {
|
|
96387
|
+
const fieldName = `__roziePortal${opts._state.portalCount++}`;
|
|
96388
|
+
parts.push(`data-rozie-portal-ref="${fieldName}"`);
|
|
96389
|
+
portalSentinel = `<span data-rozie-portal-anchor="${fieldName}" hidden></span>`;
|
|
96390
|
+
opts.decorators.add("query");
|
|
96391
|
+
opts.runtime.add("RoziePortalController");
|
|
96392
|
+
const containerCode = rewriteTemplateExpression(node.portalTo.expression, ir);
|
|
96393
|
+
opts._state.portalFieldDecls.push(` @query('[data-rozie-portal-ref="${fieldName}"]') private ${fieldName}!: HTMLElement;`);
|
|
96394
|
+
opts._state.portalFieldDecls.push(` @query('[data-rozie-portal-anchor="${fieldName}"]') private ${fieldName}Anchor!: HTMLElement;`);
|
|
96395
|
+
opts._state.portalFieldDecls.push(` private ${fieldName}Controller = new RoziePortalController(this, () => this.${fieldName}, () => this.${fieldName}Anchor, () => (${containerCode}));`);
|
|
96396
|
+
}
|
|
95271
96397
|
const keynav = opts.keynav ?? null;
|
|
95272
96398
|
parts.push(...keynavRootAttrs(keynav, node, opts.runtime), ...keynavItemAttrs(keynav, node, opts.keynavItemIndexAlias ?? null));
|
|
95273
96399
|
if ((node.tagKind === "html" || node.tagKind === "component") && opts.scopeHash) parts.push(`data-rozie-s-${opts.scopeHash}`);
|
|
95274
96400
|
const attrsText = parts.length > 0 ? " " + parts.join(" ") : "";
|
|
95275
96401
|
if (isVoidElement(node.tagName) && node.children.length === 0) return {
|
|
95276
|
-
open:
|
|
96402
|
+
open: `${portalSentinel}<${tagName}${attrsText} />`,
|
|
95277
96403
|
selfClose: true
|
|
95278
96404
|
};
|
|
95279
96405
|
return {
|
|
95280
|
-
open:
|
|
96406
|
+
open: `${portalSentinel}<${tagName}${attrsText}>`,
|
|
95281
96407
|
selfClose: false
|
|
95282
96408
|
};
|
|
95283
96409
|
}
|
|
@@ -95546,6 +96672,8 @@ function emitTemplate(ir, opts) {
|
|
|
95546
96672
|
slotFillerClassFields: [],
|
|
95547
96673
|
slotFillerUpdatedBody: [],
|
|
95548
96674
|
slotFillerDisconnectReset: [],
|
|
96675
|
+
portalFieldDecls: [],
|
|
96676
|
+
portalCount: 0,
|
|
95549
96677
|
diagnostics
|
|
95550
96678
|
};
|
|
95551
96679
|
const keynav = resolveKeynavPlan(ir);
|
|
@@ -95574,6 +96702,7 @@ function emitTemplate(ir, opts) {
|
|
|
95574
96702
|
slotFillerUpdatedBody: state.slotFillerUpdatedBody,
|
|
95575
96703
|
slotFillerDisconnectReset: state.slotFillerDisconnectReset,
|
|
95576
96704
|
keynavFieldDecls,
|
|
96705
|
+
portalFieldDecls: state.portalFieldDecls,
|
|
95577
96706
|
diagnostics
|
|
95578
96707
|
};
|
|
95579
96708
|
}
|
|
@@ -95596,6 +96725,7 @@ function emitTemplate(ir, opts) {
|
|
|
95596
96725
|
slotFillerUpdatedBody: state.slotFillerUpdatedBody,
|
|
95597
96726
|
slotFillerDisconnectReset: state.slotFillerDisconnectReset,
|
|
95598
96727
|
keynavFieldDecls,
|
|
96728
|
+
portalFieldDecls: state.portalFieldDecls,
|
|
95599
96729
|
diagnostics
|
|
95600
96730
|
};
|
|
95601
96731
|
}
|
|
@@ -96056,7 +97186,11 @@ function emitStyle(styles, source, opts) {
|
|
|
96056
97186
|
const engineCss = stringifyRules((styles.engineRules ?? []).flatMap((r) => r.children ?? []), source);
|
|
96057
97187
|
const rawScopedCss = stringifyRules(scopedRules, source);
|
|
96058
97188
|
const scopedCss = opts.scopeHash ? scopeCss(rawScopedCss, opts.scopeHash) : rawScopedCss;
|
|
96059
|
-
const globalCss = [
|
|
97189
|
+
const globalCss = [
|
|
97190
|
+
rootRules.length > 0 ? stringifyRules(rootRules, source) : "",
|
|
97191
|
+
engineCss,
|
|
97192
|
+
opts.hasElementPortal === true ? scopedCss : ""
|
|
97193
|
+
].filter((s) => s.length > 0).join("\n");
|
|
96060
97194
|
const combinedScoped = [
|
|
96061
97195
|
HOST_DISPLAY_DEFAULT,
|
|
96062
97196
|
scopedCss,
|
|
@@ -96133,6 +97267,33 @@ function buildShell(parts) {
|
|
|
96133
97267
|
}
|
|
96134
97268
|
//#endregion
|
|
96135
97269
|
//#region ../targets/lit/src/emitLit.ts
|
|
97270
|
+
/**
|
|
97271
|
+
* command-palette-portal-overlay phase — a cheap, INDEPENDENT presence-only
|
|
97272
|
+
* walk over `ir.template` for at least one `r-portal` element. Needed
|
|
97273
|
+
* because `emitStyle` runs BEFORE `emitTemplate` in this file's call
|
|
97274
|
+
* sequence (styles must be ready before the template walk references
|
|
97275
|
+
* `opts.scopeHash`), so the template-walk-derived `portalCount` isn't
|
|
97276
|
+
* available yet at the point `emitStyle` needs to know whether to ALSO
|
|
97277
|
+
* push `scopedCss` through `injectGlobalStyles` (see `emitStyle.ts`'s
|
|
97278
|
+
* `opts.hasElementPortal` doc comment / `RoziePortalController.ts`'s module
|
|
97279
|
+
* doc comment for WHY). A second, tiny boolean-only walk is simpler and
|
|
97280
|
+
* lower-risk than reordering the two emit passes or synchronizing two
|
|
97281
|
+
* independent per-element counters.
|
|
97282
|
+
*/
|
|
97283
|
+
function hasElementPortal(node) {
|
|
97284
|
+
if (node === null) return false;
|
|
97285
|
+
switch (node.type) {
|
|
97286
|
+
case "TemplateElement":
|
|
97287
|
+
if (node.portalTo !== void 0) return true;
|
|
97288
|
+
return node.children.some(hasElementPortal);
|
|
97289
|
+
case "TemplateFragment": return node.children.some(hasElementPortal);
|
|
97290
|
+
case "TemplateConditional": return node.branches.some((b) => b.body.some(hasElementPortal));
|
|
97291
|
+
case "TemplateMatch": return node.branches.some((b) => b.body.some(hasElementPortal)) || node.hostElement !== void 0 && hasElementPortal(node.hostElement);
|
|
97292
|
+
case "TemplateLoop": return node.body.some(hasElementPortal);
|
|
97293
|
+
case "TemplateSlotInvocation": return node.fallback.some(hasElementPortal);
|
|
97294
|
+
default: return false;
|
|
97295
|
+
}
|
|
97296
|
+
}
|
|
96136
97297
|
function emitLit(ir, opts = {}) {
|
|
96137
97298
|
const litImports = new LitImportCollector();
|
|
96138
97299
|
const decoratorImports = new LitDecoratorImportCollector();
|
|
@@ -96153,7 +97314,8 @@ function emitLit(ir, opts = {}) {
|
|
|
96153
97314
|
componentName: ir.name,
|
|
96154
97315
|
lit: litImports,
|
|
96155
97316
|
runtime: runtimeImports,
|
|
96156
|
-
scopeHash
|
|
97317
|
+
scopeHash,
|
|
97318
|
+
hasElementPortal: hasElementPortal(ir.template)
|
|
96157
97319
|
});
|
|
96158
97320
|
diagnostics.push(...styleResult.diagnostics);
|
|
96159
97321
|
const scriptResult = emitScript(ir, {
|
|
@@ -96241,6 +97403,7 @@ function emitLit(ir, opts = {}) {
|
|
|
96241
97403
|
debouncedFieldDecls: templateResult.debouncedFieldDecls.join("\n"),
|
|
96242
97404
|
hoistedLiteralFieldDecls: templateResult.hoistedLiteralFieldDecls.join("\n"),
|
|
96243
97405
|
keynavFieldDecls: templateResult.keynavFieldDecls.join("\n"),
|
|
97406
|
+
portalFieldDecls: templateResult.portalFieldDecls.join("\n"),
|
|
96244
97407
|
slotFillerClassFields: templateResult.slotFillerClassFields.map((f) => " " + f).join("\n"),
|
|
96245
97408
|
slotFields: slotResult.fields,
|
|
96246
97409
|
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;",
|
|
@@ -96317,6 +97480,7 @@ function composeClassBody(parts) {
|
|
|
96317
97480
|
if (parts.debouncedFieldDecls.trim().length > 0) sections.push(parts.debouncedFieldDecls);
|
|
96318
97481
|
if (parts.hoistedLiteralFieldDecls.trim().length > 0) sections.push(parts.hoistedLiteralFieldDecls);
|
|
96319
97482
|
if (parts.keynavFieldDecls.trim().length > 0) sections.push(parts.keynavFieldDecls);
|
|
97483
|
+
if (parts.portalFieldDecls.trim().length > 0) sections.push(parts.portalFieldDecls);
|
|
96320
97484
|
if (parts.slotFillerClassFields.trim().length > 0) sections.push(parts.slotFillerClassFields);
|
|
96321
97485
|
if (parts.slotFields.trim().length > 0) sections.push(parts.slotFields);
|
|
96322
97486
|
sections.push(parts.cleanupField);
|
|
@@ -96400,7 +97564,8 @@ function buildComponentImportsBlock(components, selfName) {
|
|
|
96400
97564
|
const lines = [];
|
|
96401
97565
|
for (const decl of components) {
|
|
96402
97566
|
if (decl.localName === selfName) continue;
|
|
96403
|
-
|
|
97567
|
+
const importPath = isPublishedSpecifier(decl.importPath) ? rewriteRozieImport(decl.importPath, "lit") : decl.importPath;
|
|
97568
|
+
lines.push(`import '${importPath}';`);
|
|
96404
97569
|
}
|
|
96405
97570
|
if (lines.length === 0) return void 0;
|
|
96406
97571
|
return lines.join("\n") + "\n";
|
|
@@ -96515,9 +97680,9 @@ function compile(source, opts) {
|
|
|
96515
97680
|
});
|
|
96516
97681
|
const acc = [...parseDiags, ...irDiags];
|
|
96517
97682
|
if (!ir) return guardEmpty(fail(acc));
|
|
96518
|
-
threadParamTypes(ir, filename ?? "<anonymous>", cache, resolver, acc);
|
|
97683
|
+
threadParamTypes(ir, filename ?? "<anonymous>", cache, resolver, opts.target, acc);
|
|
96519
97684
|
validatePortalScopedStyle(ir, acc);
|
|
96520
|
-
validateTwoWayBindings(ir, filename ?? "<anonymous>", cache, resolver, acc);
|
|
97685
|
+
validateTwoWayBindings(ir, filename ?? "<anonymous>", cache, resolver, opts.target, acc);
|
|
96521
97686
|
stampMissingFilename(acc, filename);
|
|
96522
97687
|
if (acc.some((d) => d.severity === "error")) return fail(acc);
|
|
96523
97688
|
const emitOpts = {
|