@archie/devtools 0.0.41 → 0.0.43

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.d.ts CHANGED
@@ -12,6 +12,10 @@ import { types, PluginObj } from '@babel/core';
12
12
  * nodeId, file, line, col, componentName, staticProps, movable, parentNodeId
13
13
  * + (ARCH-883 F1) slot, propBindings, propOwner
14
14
  * + (ARCH-883-KOI) origin
15
+ * + (ARCH-1444) childrenText — non-host elements only
16
+ * + (ARCH-1450) childrenSource, propSources — same-file identifier tracing
17
+ * for values `propBindings` cannot reach (no destructured-prop component
18
+ * in scope, or a value that never touches a prop at all)
15
19
  *
16
20
  * At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
17
21
  * element identity without any DOM attributes.
@@ -169,6 +173,17 @@ declare function babelPluginEditorMeta({ types: t }: {
169
173
  * `id`/`htmlFor`/`name`/`type`/`role`/`slot`) are downgraded to `formatted` by the
170
174
  * PRODUCER, because no usage value could make them a text edit. The consumer does
171
175
  * not need to re-derive that list; it only needs the allowlist above.
176
+ *
177
+ * ARCH-1443: `{ target: 'children', prop: 'children', kind: 'direct' }` is emitted
178
+ * for an element that forwards its component's props REST and renders no children
179
+ * of its own — the shape EVERY ejected shadcn-style primitive has
180
+ * (`({ className, ...p }) => <h3 className={…} {...p} />`). Nothing about the
181
+ * allowlist changes: the usage value of `children` is the JSX children written at
182
+ * the usage site (`<CardTitle>Cómo funciona</CardTitle>`), and the same
183
+ * usage-value law applies to it — plain text is writable, a nested element or a
184
+ * computed expression is a gap. The producer refuses whenever more than one
185
+ * element in the component forwards the rest, since one `children` prop cannot
186
+ * name which of them an edit is about.
172
187
  */
173
188
  interface PropBinding {
174
189
  /**
@@ -204,6 +219,134 @@ interface PropBinding {
204
219
  */
205
220
  kind: 'direct' | 'derived' | 'formatted';
206
221
  }
222
+ /**
223
+ * ARCH-1450 — where a rendered EXPRESSION traces to in the SAME file, for the
224
+ * one case `propBindings` structurally cannot answer: a value that never
225
+ * touches a component prop at all.
226
+ *
227
+ * `propBindings`/`propOwner` trace an expression to a PROP of the enclosing
228
+ * (module-level, destructured) component — that machinery is silent whenever
229
+ * there is no such component (a screen's `export default function X()` with
230
+ * no destructured params) or whenever the expression bottoms out in a plain
231
+ * local rather than a prop (`const title = "Contenido"; … <Section
232
+ * title={title}>`). In both cases `collectPropBindings` returns `{bindings:
233
+ * [], owner: null}` and the identifier name never crosses the postMessage
234
+ * boundary in any form — the FE's `usage-prop-resolver` has nothing to walk.
235
+ * `childrenSource`/`propSources` close exactly that gap, independently of
236
+ * whether a `PropBinding` was also emitted for the same target: the two
237
+ * fields answer different questions ("is this a prop, and is it a safe
238
+ * rewrite" vs "what source-level thing does this identifier name") and a
239
+ * consumer may need either, or both, depending on what it already knows.
240
+ *
241
+ * THE FOUR ANSWERS:
242
+ *
243
+ * `identifier` — the expression is a bare identifier whose binding
244
+ * resolves (constant, same file, not an import) but is
245
+ * not a literal-init local — a destructured prop used
246
+ * directly, a plain function parameter, or a local
247
+ * `const`/`let` whose initializer is not itself a plain
248
+ * string. `name` is the LOCAL identifier as written at
249
+ * this usage — NOT necessarily the prop name `propBindings`
250
+ * would report for a renamed destructure
251
+ * (`{ headline: h }` reports `h` here, `headline` there).
252
+ * Consumers that need the true prop name still read
253
+ * `propBindings`; this field only says "this traces to a
254
+ * resolvable local named X", which is enough to tell a
255
+ * prop/parameter apart from a dead end.
256
+ * `local-const` — the identifier resolves to a `const`/`let` declared in
257
+ * THIS SAME FILE (by construction — scope resolution never
258
+ * crosses a file boundary) whose initializer is a
259
+ * `StringLiteral` or a `TemplateLiteral` with NO
260
+ * interpolations. `literal` is that string — the exact
261
+ * text a consumer would show/replace. `file`/`line`/`col`
262
+ * point at the INITIALIZER, not the identifier's usage —
263
+ * that is where a rewrite must land: replacing the
264
+ * identifier's occurrence would rename a variable, not
265
+ * change what it renders wherever else it is used.
266
+ * `refs` (FE MINOR-5) is `binding.referencePaths.length` —
267
+ * how many places in the file READ this const, counting
268
+ * every usage, not only the one this `PropSource` sits on.
269
+ * `refs > 1` means the SAME initializer feeds more than
270
+ * one rendered element (`const title = "X"; <A
271
+ * title={title}/><B title={title}/>` — both usages report
272
+ * `refs: 2`): rewriting the initializer changes what BOTH
273
+ * elements show, so a consumer must warn or refuse rather
274
+ * than treat the write as scoped to the one element the
275
+ * edit was made on. `refs === 1` is the only shape safe to
276
+ * treat as a single-target rewrite.
277
+ * `member` — a non-computed member-expression chain
278
+ * (`props.title`, `item.address.city`) whose ROOT resolves
279
+ * the same way `identifier`'s binding does (constant, same
280
+ * file, not an import). `root` is the base identifier;
281
+ * `path` is the property chain beyond it, in order
282
+ * (`['address', 'city']` for `item.address.city`).
283
+ * `other` — the honest gap marker, same role `formatted` plays for
284
+ * `PropBinding.kind` and `'__dynamic__'` plays for
285
+ * `childrenText`: an expression the plugin looked at and
286
+ * declined to trace — a computed member (`obj[key]`), a
287
+ * call, a template WITH interpolations, a conditional, an
288
+ * identifier with no binding (a free/global read), a
289
+ * binding that IS reassigned somewhere in the file, or one
290
+ * that comes from an `import`. NEVER inferred past any of
291
+ * those — a wrong trace is strictly worse than an honest
292
+ * gap.
293
+ *
294
+ * SCOPE — deliberately NARROW, unlike `childrenText`/`origin`: both fields
295
+ * are OMITTED (not stamped `other`) whenever there is nothing to say, the
296
+ * same convention `propBindings`/`propOwner`/`slot` already use ("emitted
297
+ * only when it carries information"). `childrenSource` is only computed when
298
+ * the element's children are EXACTLY one `JSXExpressionContainer` (the case
299
+ * that makes `childrenText === '__dynamic__'` for the single most common
300
+ * reason — `<h1>{title}</h1>` — and therefore the case where a source trace
301
+ * is actually useful); self-closing elements, plain-text children and
302
+ * mixed/multi children never get it. `propSources` only carries entries for
303
+ * attributes whose value is a `JSXExpressionContainer` wrapping something
304
+ * OTHER than a literal `extractStaticProps` already captures exactly
305
+ * (`staticProps` already has the true value for `title={"Static"}`; adding a
306
+ * `propSources.title: {kind:'other'}` next to it would look like a gap where
307
+ * there is none). Both fields are ABSENT — not `null`/`{}` — on every bundle
308
+ * built before ARCH-1450, the same absence-means-"cannot answer" law every
309
+ * other additive field in this file states.
310
+ *
311
+ * Emitted for BOTH host and non-host elements for `propSources` (attribute
312
+ * resolution does not depend on the element being a component usage — a host
313
+ * `<div title={title}>` traces exactly the same way `staticProps` treats host
314
+ * and component attributes identically). `childrenSource` follows
315
+ * `childrenText`'s own host exemption: a host element's children are the
316
+ * screen's own literal JSX, not a component usage, so there is no target for
317
+ * an editor-driven rewrite to route through in the first place.
318
+ *
319
+ * CROSS-REPO CONTRACT (ARCH-1450 CR MINOR-1): `usage-prop-resolver.ts`
320
+ * (archie-app) is the only consumer, and the two repos share no fixture of
321
+ * their own — a rename here (e.g. `literal` -> `value`) would leave both
322
+ * suites green while every real generated project broke. The published
323
+ * package ships `fixtures/prop-source.fixture.json` (see `files` in
324
+ * `package.json`) specifically so archie-app's tests can assert against the
325
+ * REAL emitted shape instead of a hand-typed copy of it. That file is
326
+ * GENERATED, never hand-edited — see `src/prop-source-fixture.generate.test.ts`,
327
+ * which regenerates it from the live plugin and fails loud the moment the
328
+ * committed copy drifts. Regenerate with `UPDATE_FIXTURES=1 npx vitest run
329
+ * src/prop-source-fixture.generate.test.ts`, review the diff, and bump
330
+ * archie-app's copy in the same change.
331
+ */
332
+ type PropSource = {
333
+ kind: 'identifier';
334
+ name: string;
335
+ } | {
336
+ kind: 'local-const';
337
+ name: string;
338
+ file: string;
339
+ line: number;
340
+ col: number;
341
+ literal: string | null;
342
+ refs: number;
343
+ } | {
344
+ kind: 'member';
345
+ root: string;
346
+ path: string[];
347
+ } | {
348
+ kind: 'other';
349
+ };
207
350
  /** Metadata injected into every JSX element by babel-plugin-editor-meta. */
208
351
  interface EditorMeta {
209
352
  /** Stable identifier: "file:line:col:componentName" */
@@ -216,6 +359,38 @@ interface EditorMeta {
216
359
  componentName: string;
217
360
  /** Safely serializable literal prop values. Dynamic expressions are "__dynamic__". */
218
361
  staticProps: Record<string, unknown> | null;
362
+ /**
363
+ * ARCH-1444 — the SHAPE of this element's JSX children, and their text when
364
+ * that text is plain. `staticProps` is built from ATTRIBUTES only, so without
365
+ * this field the `children` prop is the one binding target whose value at the
366
+ * usage a consumer cannot see — and the usage-value law above ("plain text is
367
+ * writable, a nested element or a computed expression is a gap") is stated in
368
+ * terms of exactly that value.
369
+ *
370
+ * `null` — the element is SELF-CLOSING: no children container at
371
+ * all. NOT the same as `''` (`<X></X>`), which can take a
372
+ * text write; `<X />` cannot without a structural rewrite.
373
+ * `''` — an empty children container.
374
+ * `'__dynamic__'` — anything that is not exactly one JSXText child: an
375
+ * expression container, a nested element, a comment, or
376
+ * several children. Same sentinel as `staticProps`.
377
+ * any other string— the sole JSXText child, TRIMMED (JSX itself trims
378
+ * leading/trailing whitespace-with-newlines, so the
379
+ * trimmed form is what renders). A newline that SURVIVES
380
+ * the trim is real multi-line content: a consumer that
381
+ * writes by splicing must refuse it, because replacing it
382
+ * with one line deletes source lines and invalidates every
383
+ * anchor below.
384
+ *
385
+ * Emitted for NON-HOST elements only — a usage is always a component tag, and
386
+ * stamping every `<div>` would be weight for an answer nothing asks.
387
+ *
388
+ * ABSENT (`undefined`) on a host element and on every bundle built before this
389
+ * change. Absence is NOT "no children": it means the page cannot answer the
390
+ * question, and a consumer must gap the edit rather than assume (the same law
391
+ * `origin`'s docstring states at length).
392
+ */
393
+ childrenText?: string | null;
219
394
  /**
220
395
  * Whether the JSX node is in a reorderable position (2+ siblings).
221
396
  *
@@ -354,6 +529,30 @@ interface EditorMeta {
354
529
  * not the usage's, and routing those would write the wrong prop. Mismatch = gap.
355
530
  */
356
531
  propOwner?: string | null;
532
+ /**
533
+ * ARCH-1450 — source trace for this element's SOLE children expression
534
+ * (`<h1>{title}</h1>`), independently of whether `propBindings` also has a
535
+ * `target: 'children'` entry for the same node. See `PropSource`'s own
536
+ * docstring for the full contract (the four kinds, the narrow emission
537
+ * scope, why it exists alongside `propBindings`).
538
+ *
539
+ * Absent when the element is self-closing, has plain-text children, has
540
+ * more than one child, or is a HOST element — see `PropSource`'s docstring.
541
+ * Absent on every bundle built before ARCH-1450.
542
+ */
543
+ childrenSource?: PropSource;
544
+ /**
545
+ * ARCH-1450 — source trace for each ATTRIBUTE whose value is a
546
+ * `JSXExpressionContainer`, keyed by the attribute name (never `attr:`
547
+ * prefixed — this is not `PropBinding.target`). See `PropSource`'s own
548
+ * docstring for the full contract.
549
+ *
550
+ * Emitted only when non-empty — read it as `meta.propSources ?? {}`. An
551
+ * attribute whose expression is a literal already exact in `staticProps`
552
+ * never gets an entry here (nothing to trace beyond what `staticProps`
553
+ * already says). Absent on every bundle built before ARCH-1450.
554
+ */
555
+ propSources?: Record<string, PropSource>;
357
556
  }
358
557
 
359
558
  interface ArchieDevToolsOptions {
package/dist/index.js CHANGED
@@ -277,6 +277,9 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
277
277
  }
278
278
  return "";
279
279
  }
280
+ function isIntrinsicTagName(name) {
281
+ return t.isJSXIdentifier(name) && /^[a-z]/.test(name.name);
282
+ }
280
283
  function isHostLikeMemberExpressionName(name) {
281
284
  if (!t.isJSXMemberExpression(name)) return false;
282
285
  const propertyName = name.property.name;
@@ -367,6 +370,158 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
367
370
  }
368
371
  return hasAny ? t.objectExpression(props) : null;
369
372
  }
373
+ function describeChildrenText(opening) {
374
+ const elementPath = opening.parentPath;
375
+ if (!elementPath || !elementPath.isJSXElement()) return null;
376
+ const element = elementPath.node;
377
+ if (!element.closingElement) return null;
378
+ const children = element.children;
379
+ if (children.length === 0) return "";
380
+ if (children.length !== 1) return "__dynamic__";
381
+ const [only] = children;
382
+ if (!t.isJSXText(only)) return "__dynamic__";
383
+ return only.value.trim();
384
+ }
385
+ function literalInitText(init) {
386
+ if (t.isStringLiteral(init)) return init.value;
387
+ if (t.isTemplateLiteral(init) && init.expressions.length === 0) {
388
+ const [quasi] = init.quasis;
389
+ if (!quasi) return null;
390
+ return quasi.value.cooked ?? quasi.value.raw;
391
+ }
392
+ return null;
393
+ }
394
+ function describeLocalConstSource(name, binding) {
395
+ const declarator = binding.path;
396
+ if (!declarator.isVariableDeclarator()) return null;
397
+ const declaration = declarator.parentPath;
398
+ if (!declaration?.isVariableDeclaration()) return null;
399
+ if (declaration.node.kind !== "const" && declaration.node.kind !== "let") return null;
400
+ const init = declarator.node.init;
401
+ if (!init) return null;
402
+ const literal = literalInitText(init);
403
+ if (literal === null) return null;
404
+ const loc = init.loc?.start;
405
+ if (!loc) return null;
406
+ return {
407
+ kind: "local-const",
408
+ name,
409
+ // The init lives in the SAME file being transformed by construction —
410
+ // `scope.getBinding` never crosses a file boundary — so the caller's
411
+ // own `file` (computed once per visited node) is correct here too.
412
+ file: currentFile,
413
+ line: loc.line,
414
+ col: loc.column + 1,
415
+ literal,
416
+ // FE MINOR-5: how many places in the FILE read this const — every
417
+ // reference, not only the one this call is describing. `refs > 1`
418
+ // means one initializer feeds more than one rendered element, so a
419
+ // consumer must warn/refuse rather than treat a rewrite as scoped to
420
+ // just this usage (see `PropSource`'s docstring for the full contract).
421
+ refs: binding.referencePaths.length
422
+ };
423
+ }
424
+ function resolvableSameFileBinding(name, scope) {
425
+ const binding = scope.getBinding(name);
426
+ if (!binding) return null;
427
+ if (!binding.constant) return null;
428
+ const bp = binding.path;
429
+ if (bp.isImportSpecifier() || bp.isImportDefaultSpecifier() || bp.isImportNamespaceSpecifier()) {
430
+ return null;
431
+ }
432
+ return binding;
433
+ }
434
+ function memberChain(expr) {
435
+ const path = [];
436
+ let current = expr;
437
+ while (t.isMemberExpression(current)) {
438
+ if (current.computed) return null;
439
+ if (!t.isIdentifier(current.property)) return null;
440
+ path.unshift(current.property.name);
441
+ current = current.object;
442
+ }
443
+ if (!t.isIdentifier(current)) return null;
444
+ return { root: current.name, path };
445
+ }
446
+ let currentFile = "";
447
+ function describeSource(expr, scope) {
448
+ if (t.isIdentifier(expr)) {
449
+ const binding = resolvableSameFileBinding(expr.name, scope);
450
+ if (!binding) return { kind: "other" };
451
+ const local = describeLocalConstSource(expr.name, binding);
452
+ if (local) return local;
453
+ return { kind: "identifier", name: expr.name };
454
+ }
455
+ if (t.isMemberExpression(expr) && !expr.computed) {
456
+ const chain = memberChain(expr);
457
+ if (chain && resolvableSameFileBinding(chain.root, scope)) {
458
+ return { kind: "member", root: chain.root, path: chain.path };
459
+ }
460
+ }
461
+ return { kind: "other" };
462
+ }
463
+ function isStaticPropsLiteral(expr) {
464
+ return t.isStringLiteral(expr) || t.isNumericLiteral(expr) || t.isBooleanLiteral(expr) || t.isNullLiteral(expr);
465
+ }
466
+ function describeChildrenSource(opening) {
467
+ const elementPath = opening.parentPath;
468
+ if (!elementPath || !elementPath.isJSXElement()) return null;
469
+ const children = elementPath.node.children;
470
+ if (children.length !== 1) return null;
471
+ const [only] = children;
472
+ if (!t.isJSXExpressionContainer(only)) return null;
473
+ if (t.isJSXEmptyExpression(only.expression)) return null;
474
+ return describeSource(only.expression, opening.scope);
475
+ }
476
+ function collectPropSources(attrs, scope) {
477
+ const entries = [];
478
+ for (const attr of attrs) {
479
+ if (!t.isJSXAttribute(attr)) continue;
480
+ const name = t.isJSXIdentifier(attr.name) ? attr.name.name : `${attr.name.namespace.name}:${attr.name.name.name}`;
481
+ if (NON_BINDING_ATTRS.has(name)) continue;
482
+ const value = attr.value;
483
+ if (!t.isJSXExpressionContainer(value)) continue;
484
+ const expression = value.expression;
485
+ if (t.isJSXEmptyExpression(expression)) continue;
486
+ if (isStaticPropsLiteral(expression)) continue;
487
+ entries.push([name, describeSource(expression, scope)]);
488
+ }
489
+ return entries;
490
+ }
491
+ function propSourceToAst(source) {
492
+ switch (source.kind) {
493
+ case "identifier":
494
+ return t.objectExpression([
495
+ t.objectProperty(t.identifier("kind"), t.stringLiteral("identifier")),
496
+ t.objectProperty(t.identifier("name"), t.stringLiteral(source.name))
497
+ ]);
498
+ case "local-const":
499
+ return t.objectExpression([
500
+ t.objectProperty(t.identifier("kind"), t.stringLiteral("local-const")),
501
+ t.objectProperty(t.identifier("name"), t.stringLiteral(source.name)),
502
+ t.objectProperty(t.identifier("file"), t.stringLiteral(source.file)),
503
+ t.objectProperty(t.identifier("line"), t.numericLiteral(source.line)),
504
+ t.objectProperty(t.identifier("col"), t.numericLiteral(source.col)),
505
+ t.objectProperty(
506
+ t.identifier("literal"),
507
+ source.literal === null ? t.nullLiteral() : t.stringLiteral(source.literal)
508
+ ),
509
+ t.objectProperty(t.identifier("refs"), t.numericLiteral(source.refs))
510
+ ]);
511
+ case "member":
512
+ return t.objectExpression([
513
+ t.objectProperty(t.identifier("kind"), t.stringLiteral("member")),
514
+ t.objectProperty(t.identifier("root"), t.stringLiteral(source.root)),
515
+ t.objectProperty(
516
+ t.identifier("path"),
517
+ t.arrayExpression(source.path.map((segment) => t.stringLiteral(segment)))
518
+ )
519
+ ]);
520
+ case "other":
521
+ default:
522
+ return t.objectExpression([t.objectProperty(t.identifier("kind"), t.stringLiteral("other"))]);
523
+ }
524
+ }
370
525
  function isMetaStripSpreadExpression(arg) {
371
526
  if (!t.isCallExpression(arg)) return false;
372
527
  if (!t.isArrowFunctionExpression(arg.callee)) return false;
@@ -474,6 +629,14 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
474
629
  }
475
630
  return map;
476
631
  }
632
+ function restParamName(pattern) {
633
+ for (const prop of pattern.properties) {
634
+ if (!t.isRestElement(prop)) continue;
635
+ if (t.isIdentifier(prop.argument)) return prop.argument.name;
636
+ return null;
637
+ }
638
+ return null;
639
+ }
477
640
  function resolvePropContext(path) {
478
641
  let fn = path.getFunctionParent();
479
642
  while (fn) {
@@ -482,7 +645,9 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
482
645
  return {
483
646
  componentScope: fn.scope,
484
647
  propMap: buildPropMap(first),
485
- componentName: getFunctionName(fn)
648
+ componentName: getFunctionName(fn),
649
+ restParam: restParamName(first),
650
+ componentPath: fn
486
651
  };
487
652
  }
488
653
  fn = fn.getFunctionParent();
@@ -602,12 +767,62 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
602
767
  const props = collectPropIdentifiers(expr, scope, ctx);
603
768
  return props.length > 0 ? { kind: "formatted", prop: props[0] } : null;
604
769
  }
770
+ function unwrapSanitizedSpread(arg) {
771
+ if (!t.isCallExpression(arg)) return arg;
772
+ if (!isMetaStripSpreadExpression(arg)) return arg;
773
+ if (arg.arguments.length !== 1) return arg;
774
+ const [payload] = arg.arguments;
775
+ if (!t.isObjectExpression(payload) || payload.properties.length !== 1) return arg;
776
+ const [only] = payload.properties;
777
+ if (!t.isSpreadElement(only)) return arg;
778
+ return only.argument;
779
+ }
780
+ function forwardsOwnerRestChildren(opening, ctx) {
781
+ if (ctx.restParam === null) return false;
782
+ const elementPath = opening.parentPath;
783
+ if (!elementPath || !elementPath.isJSXElement()) return false;
784
+ if (meaningfulChildren(t, elementPath.node.children).length > 0) return false;
785
+ let spreadsRest = 0;
786
+ for (const attr of opening.node.attributes) {
787
+ if (t.isJSXAttribute(attr)) {
788
+ if (t.isJSXIdentifier(attr.name, { name: "children" })) return false;
789
+ continue;
790
+ }
791
+ if (!t.isJSXSpreadAttribute(attr)) continue;
792
+ const payload = unwrapSanitizedSpread(attr.argument);
793
+ if (!t.isIdentifier(payload, { name: ctx.restParam })) return false;
794
+ spreadsRest += 1;
795
+ }
796
+ if (spreadsRest !== 1) return false;
797
+ const binding = opening.scope.getBinding(ctx.restParam);
798
+ if (!binding) return false;
799
+ if (binding.scope !== ctx.componentScope) return false;
800
+ return binding.constant;
801
+ }
802
+ const restForwarderCounts = /* @__PURE__ */ new WeakMap();
803
+ function countRestForwarders(ctx) {
804
+ const cached = restForwarderCounts.get(ctx.componentPath.node);
805
+ if (cached !== void 0) return cached;
806
+ let count = 0;
807
+ ctx.componentPath.traverse({
808
+ JSXOpeningElement(inner) {
809
+ if (forwardsOwnerRestChildren(inner, ctx)) count += 1;
810
+ }
811
+ });
812
+ restForwarderCounts.set(ctx.componentPath.node, count);
813
+ return count;
814
+ }
605
815
  function collectPropBindings(path, attrs) {
606
816
  const ctx = resolvePropContext(path);
607
- if (!ctx || ctx.propMap.size === 0) return { bindings: [], owner: null };
817
+ if (!ctx || ctx.propMap.size === 0 && ctx.restParam === null) {
818
+ return { bindings: [], owner: null };
819
+ }
608
820
  if (ctx.componentName === null) return { bindings: [], owner: null };
609
821
  const scope = path.scope;
610
822
  const bindings = [];
823
+ if (forwardsOwnerRestChildren(path, ctx) && countRestForwarders(ctx) === 1) {
824
+ bindings.push({ target: "children", prop: "children", kind: "direct" });
825
+ }
611
826
  const elementPath = path.parentPath;
612
827
  if (elementPath && elementPath.isJSXElement()) {
613
828
  const children = meaningfulChildren(t, elementPath.node.children);
@@ -698,6 +913,7 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
698
913
  if (!loc) return;
699
914
  const root = state.file.opts.root ?? "";
700
915
  const file = root ? nodePath.relative(root, filename) : filename;
916
+ currentFile = file;
701
917
  const line = loc.line;
702
918
  const col = loc.column + 1;
703
919
  const componentName = getJSXTagName(path.node);
@@ -709,7 +925,9 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
709
925
  return;
710
926
  }
711
927
  let rootName = null;
712
- if (t.isJSXIdentifier(path.node.name)) {
928
+ if (isIntrinsicTagName(path.node.name)) {
929
+ rootName = null;
930
+ } else if (t.isJSXIdentifier(path.node.name)) {
713
931
  rootName = path.node.name.name;
714
932
  } else if (t.isJSXMemberExpression(path.node.name)) {
715
933
  let cur = path.node.name;
@@ -771,6 +989,21 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
771
989
  t.objectProperty(t.identifier("slot"), t.stringLiteral(slot))
772
990
  );
773
991
  }
992
+ if (!isHostElement) {
993
+ const childrenText = describeChildrenText(path);
994
+ metaObj.properties.push(
995
+ t.objectProperty(
996
+ t.identifier("childrenText"),
997
+ childrenText === null ? t.nullLiteral() : t.stringLiteral(childrenText)
998
+ )
999
+ );
1000
+ const childrenSource = describeChildrenSource(path);
1001
+ if (childrenSource) {
1002
+ metaObj.properties.push(
1003
+ t.objectProperty(t.identifier("childrenSource"), propSourceToAst(childrenSource))
1004
+ );
1005
+ }
1006
+ }
774
1007
  if (bindings.length > 0) {
775
1008
  metaObj.properties.push(
776
1009
  t.objectProperty(t.identifier("propBindings"), propBindingsToAst(bindings))
@@ -782,6 +1015,19 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
782
1015
  )
783
1016
  );
784
1017
  }
1018
+ const propSourceEntries = collectPropSources(path.node.attributes, path.scope);
1019
+ if (propSourceEntries.length > 0) {
1020
+ metaObj.properties.push(
1021
+ t.objectProperty(
1022
+ t.identifier("propSources"),
1023
+ t.objectExpression(
1024
+ propSourceEntries.map(
1025
+ ([name, source]) => t.objectProperty(t.stringLiteral(name), propSourceToAst(source))
1026
+ )
1027
+ )
1028
+ )
1029
+ );
1030
+ }
785
1031
  if (isHostElement && includeHostSourceRef) {
786
1032
  path.node.attributes.push(
787
1033
  t.jsxAttribute(
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __dirname,
3
3
  babelPluginEditorMeta
4
- } from "./chunk-6QB35SLQ.mjs";
4
+ } from "./chunk-P5XMHBFS.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { readFileSync } from "fs";