@archie/devtools 0.0.42 → 0.0.44
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/{chunk-O3UMTM4N.mjs → chunk-P5XMHBFS.mjs} +160 -0
- package/dist/client/client.js +19 -11
- package/dist/client/client.mjs +19 -11
- package/dist/collection-binding.mjs +1 -1
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +160 -0
- package/dist/index.mjs +1 -1
- package/fixtures/prop-source.fixture.json +310 -0
- package/package.json +3 -2
|
@@ -183,6 +183,146 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
183
183
|
if (!t.isJSXText(only)) return "__dynamic__";
|
|
184
184
|
return only.value.trim();
|
|
185
185
|
}
|
|
186
|
+
function literalInitText(init) {
|
|
187
|
+
if (t.isStringLiteral(init)) return init.value;
|
|
188
|
+
if (t.isTemplateLiteral(init) && init.expressions.length === 0) {
|
|
189
|
+
const [quasi] = init.quasis;
|
|
190
|
+
if (!quasi) return null;
|
|
191
|
+
return quasi.value.cooked ?? quasi.value.raw;
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
function describeLocalConstSource(name, binding) {
|
|
196
|
+
const declarator = binding.path;
|
|
197
|
+
if (!declarator.isVariableDeclarator()) return null;
|
|
198
|
+
const declaration = declarator.parentPath;
|
|
199
|
+
if (!declaration?.isVariableDeclaration()) return null;
|
|
200
|
+
if (declaration.node.kind !== "const" && declaration.node.kind !== "let") return null;
|
|
201
|
+
const init = declarator.node.init;
|
|
202
|
+
if (!init) return null;
|
|
203
|
+
const literal = literalInitText(init);
|
|
204
|
+
if (literal === null) return null;
|
|
205
|
+
const loc = init.loc?.start;
|
|
206
|
+
if (!loc) return null;
|
|
207
|
+
return {
|
|
208
|
+
kind: "local-const",
|
|
209
|
+
name,
|
|
210
|
+
// The init lives in the SAME file being transformed by construction —
|
|
211
|
+
// `scope.getBinding` never crosses a file boundary — so the caller's
|
|
212
|
+
// own `file` (computed once per visited node) is correct here too.
|
|
213
|
+
file: currentFile,
|
|
214
|
+
line: loc.line,
|
|
215
|
+
col: loc.column + 1,
|
|
216
|
+
literal,
|
|
217
|
+
// FE MINOR-5: how many places in the FILE read this const — every
|
|
218
|
+
// reference, not only the one this call is describing. `refs > 1`
|
|
219
|
+
// means one initializer feeds more than one rendered element, so a
|
|
220
|
+
// consumer must warn/refuse rather than treat a rewrite as scoped to
|
|
221
|
+
// just this usage (see `PropSource`'s docstring for the full contract).
|
|
222
|
+
refs: binding.referencePaths.length
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function resolvableSameFileBinding(name, scope) {
|
|
226
|
+
const binding = scope.getBinding(name);
|
|
227
|
+
if (!binding) return null;
|
|
228
|
+
if (!binding.constant) return null;
|
|
229
|
+
const bp = binding.path;
|
|
230
|
+
if (bp.isImportSpecifier() || bp.isImportDefaultSpecifier() || bp.isImportNamespaceSpecifier()) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return binding;
|
|
234
|
+
}
|
|
235
|
+
function memberChain(expr) {
|
|
236
|
+
const path2 = [];
|
|
237
|
+
let current = expr;
|
|
238
|
+
while (t.isMemberExpression(current)) {
|
|
239
|
+
if (current.computed) return null;
|
|
240
|
+
if (!t.isIdentifier(current.property)) return null;
|
|
241
|
+
path2.unshift(current.property.name);
|
|
242
|
+
current = current.object;
|
|
243
|
+
}
|
|
244
|
+
if (!t.isIdentifier(current)) return null;
|
|
245
|
+
return { root: current.name, path: path2 };
|
|
246
|
+
}
|
|
247
|
+
let currentFile = "";
|
|
248
|
+
function describeSource(expr, scope) {
|
|
249
|
+
if (t.isIdentifier(expr)) {
|
|
250
|
+
const binding = resolvableSameFileBinding(expr.name, scope);
|
|
251
|
+
if (!binding) return { kind: "other" };
|
|
252
|
+
const local = describeLocalConstSource(expr.name, binding);
|
|
253
|
+
if (local) return local;
|
|
254
|
+
return { kind: "identifier", name: expr.name };
|
|
255
|
+
}
|
|
256
|
+
if (t.isMemberExpression(expr) && !expr.computed) {
|
|
257
|
+
const chain = memberChain(expr);
|
|
258
|
+
if (chain && resolvableSameFileBinding(chain.root, scope)) {
|
|
259
|
+
return { kind: "member", root: chain.root, path: chain.path };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return { kind: "other" };
|
|
263
|
+
}
|
|
264
|
+
function isStaticPropsLiteral(expr) {
|
|
265
|
+
return t.isStringLiteral(expr) || t.isNumericLiteral(expr) || t.isBooleanLiteral(expr) || t.isNullLiteral(expr);
|
|
266
|
+
}
|
|
267
|
+
function describeChildrenSource(opening) {
|
|
268
|
+
const elementPath = opening.parentPath;
|
|
269
|
+
if (!elementPath || !elementPath.isJSXElement()) return null;
|
|
270
|
+
const children = elementPath.node.children;
|
|
271
|
+
if (children.length !== 1) return null;
|
|
272
|
+
const [only] = children;
|
|
273
|
+
if (!t.isJSXExpressionContainer(only)) return null;
|
|
274
|
+
if (t.isJSXEmptyExpression(only.expression)) return null;
|
|
275
|
+
return describeSource(only.expression, opening.scope);
|
|
276
|
+
}
|
|
277
|
+
function collectPropSources(attrs, scope) {
|
|
278
|
+
const entries = [];
|
|
279
|
+
for (const attr of attrs) {
|
|
280
|
+
if (!t.isJSXAttribute(attr)) continue;
|
|
281
|
+
const name = t.isJSXIdentifier(attr.name) ? attr.name.name : `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
282
|
+
if (NON_BINDING_ATTRS.has(name)) continue;
|
|
283
|
+
const value = attr.value;
|
|
284
|
+
if (!t.isJSXExpressionContainer(value)) continue;
|
|
285
|
+
const expression = value.expression;
|
|
286
|
+
if (t.isJSXEmptyExpression(expression)) continue;
|
|
287
|
+
if (isStaticPropsLiteral(expression)) continue;
|
|
288
|
+
entries.push([name, describeSource(expression, scope)]);
|
|
289
|
+
}
|
|
290
|
+
return entries;
|
|
291
|
+
}
|
|
292
|
+
function propSourceToAst(source) {
|
|
293
|
+
switch (source.kind) {
|
|
294
|
+
case "identifier":
|
|
295
|
+
return t.objectExpression([
|
|
296
|
+
t.objectProperty(t.identifier("kind"), t.stringLiteral("identifier")),
|
|
297
|
+
t.objectProperty(t.identifier("name"), t.stringLiteral(source.name))
|
|
298
|
+
]);
|
|
299
|
+
case "local-const":
|
|
300
|
+
return t.objectExpression([
|
|
301
|
+
t.objectProperty(t.identifier("kind"), t.stringLiteral("local-const")),
|
|
302
|
+
t.objectProperty(t.identifier("name"), t.stringLiteral(source.name)),
|
|
303
|
+
t.objectProperty(t.identifier("file"), t.stringLiteral(source.file)),
|
|
304
|
+
t.objectProperty(t.identifier("line"), t.numericLiteral(source.line)),
|
|
305
|
+
t.objectProperty(t.identifier("col"), t.numericLiteral(source.col)),
|
|
306
|
+
t.objectProperty(
|
|
307
|
+
t.identifier("literal"),
|
|
308
|
+
source.literal === null ? t.nullLiteral() : t.stringLiteral(source.literal)
|
|
309
|
+
),
|
|
310
|
+
t.objectProperty(t.identifier("refs"), t.numericLiteral(source.refs))
|
|
311
|
+
]);
|
|
312
|
+
case "member":
|
|
313
|
+
return t.objectExpression([
|
|
314
|
+
t.objectProperty(t.identifier("kind"), t.stringLiteral("member")),
|
|
315
|
+
t.objectProperty(t.identifier("root"), t.stringLiteral(source.root)),
|
|
316
|
+
t.objectProperty(
|
|
317
|
+
t.identifier("path"),
|
|
318
|
+
t.arrayExpression(source.path.map((segment) => t.stringLiteral(segment)))
|
|
319
|
+
)
|
|
320
|
+
]);
|
|
321
|
+
case "other":
|
|
322
|
+
default:
|
|
323
|
+
return t.objectExpression([t.objectProperty(t.identifier("kind"), t.stringLiteral("other"))]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
186
326
|
function isMetaStripSpreadExpression(arg) {
|
|
187
327
|
if (!t.isCallExpression(arg)) return false;
|
|
188
328
|
if (!t.isArrowFunctionExpression(arg.callee)) return false;
|
|
@@ -574,6 +714,7 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
574
714
|
if (!loc) return;
|
|
575
715
|
const root = state.file.opts.root ?? "";
|
|
576
716
|
const file = root ? nodePath.relative(root, filename) : filename;
|
|
717
|
+
currentFile = file;
|
|
577
718
|
const line = loc.line;
|
|
578
719
|
const col = loc.column + 1;
|
|
579
720
|
const componentName = getJSXTagName(path2.node);
|
|
@@ -657,6 +798,12 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
657
798
|
childrenText === null ? t.nullLiteral() : t.stringLiteral(childrenText)
|
|
658
799
|
)
|
|
659
800
|
);
|
|
801
|
+
const childrenSource = describeChildrenSource(path2);
|
|
802
|
+
if (childrenSource) {
|
|
803
|
+
metaObj.properties.push(
|
|
804
|
+
t.objectProperty(t.identifier("childrenSource"), propSourceToAst(childrenSource))
|
|
805
|
+
);
|
|
806
|
+
}
|
|
660
807
|
}
|
|
661
808
|
if (bindings.length > 0) {
|
|
662
809
|
metaObj.properties.push(
|
|
@@ -669,6 +816,19 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
669
816
|
)
|
|
670
817
|
);
|
|
671
818
|
}
|
|
819
|
+
const propSourceEntries = collectPropSources(path2.node.attributes, path2.scope);
|
|
820
|
+
if (propSourceEntries.length > 0) {
|
|
821
|
+
metaObj.properties.push(
|
|
822
|
+
t.objectProperty(
|
|
823
|
+
t.identifier("propSources"),
|
|
824
|
+
t.objectExpression(
|
|
825
|
+
propSourceEntries.map(
|
|
826
|
+
([name, source]) => t.objectProperty(t.stringLiteral(name), propSourceToAst(source))
|
|
827
|
+
)
|
|
828
|
+
)
|
|
829
|
+
)
|
|
830
|
+
);
|
|
831
|
+
}
|
|
672
832
|
if (isHostElement && includeHostSourceRef) {
|
|
673
833
|
path2.node.attributes.push(
|
|
674
834
|
t.jsxAttribute(
|
package/dist/client/client.js
CHANGED
|
@@ -5259,8 +5259,10 @@ function ensureUniqueNodeId(base, used) {
|
|
|
5259
5259
|
while (used.has(`${base}::${idx}`)) idx += 1;
|
|
5260
5260
|
return `${base}::${idx}`;
|
|
5261
5261
|
}
|
|
5262
|
+
var INSERT_NODE_ID_PREFIX = "insert:";
|
|
5262
5263
|
function resolveStableNodeId(el, used) {
|
|
5263
5264
|
const persisted = el.getAttribute(DND_NODE_ID_ATTR);
|
|
5265
|
+
if (persisted && persisted.startsWith(INSERT_NODE_ID_PREFIX)) return persisted;
|
|
5264
5266
|
if (persisted && !used.has(persisted)) return persisted;
|
|
5265
5267
|
const projected = projectUsageMeta(el);
|
|
5266
5268
|
if (projected) return ensureUniqueNodeId(projected.nodeId, used);
|
|
@@ -5331,7 +5333,7 @@ function isFlexOrGridParent(el) {
|
|
|
5331
5333
|
const d = getComputedStyle(el).display;
|
|
5332
5334
|
return d === "flex" || d === "grid" || d === "inline-flex" || d === "inline-grid";
|
|
5333
5335
|
}
|
|
5334
|
-
function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(), seenResolvedHosts = /* @__PURE__ */ new WeakSet()) {
|
|
5336
|
+
function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(), seenResolvedHosts = /* @__PURE__ */ new WeakSet(), resolvedUnreported = /* @__PURE__ */ new Set()) {
|
|
5335
5337
|
if (depth > MAX_DEPTH) return [];
|
|
5336
5338
|
const results = [];
|
|
5337
5339
|
for (const child of Array.from(root.children)) {
|
|
@@ -5356,7 +5358,9 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5356
5358
|
const nodeId2 = resolveStableNodeId(el, usedNodeIds);
|
|
5357
5359
|
usedNodeIds.add(nodeId2);
|
|
5358
5360
|
el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
|
|
5359
|
-
if (
|
|
5361
|
+
if (isZeroRect) {
|
|
5362
|
+
resolvedUnreported.add(el);
|
|
5363
|
+
} else {
|
|
5360
5364
|
seenResolvedHosts.add(el);
|
|
5361
5365
|
const surfaceMeta2 = el instanceof HTMLElement ? getDirectEditorMeta(el) ?? void 0 : void 0;
|
|
5362
5366
|
const identityMeta3 = projectUsageMeta(el) ?? surfaceMeta2;
|
|
@@ -5380,7 +5384,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5380
5384
|
});
|
|
5381
5385
|
}
|
|
5382
5386
|
}
|
|
5383
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5387
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5384
5388
|
continue;
|
|
5385
5389
|
}
|
|
5386
5390
|
if (isSvgRoot && isInspectorEligible(el) === null) {
|
|
@@ -5389,7 +5393,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5389
5393
|
const eligibilitySource = isInspectorEligible(el);
|
|
5390
5394
|
const inferredEligibility = eligibilitySource ?? inferEligibilityFromSortableParent(el) ?? (projectUsageMeta(el) ? "projected-usage" : null);
|
|
5391
5395
|
if (inferredEligibility === null) {
|
|
5392
|
-
const childResults = scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts);
|
|
5396
|
+
const childResults = scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported);
|
|
5393
5397
|
results.push(...childResults);
|
|
5394
5398
|
if (isFlexOrGridParent(el) && !seenResolvedHosts.has(el) && childResults.some((r) => r.draggable && r.element.parentElement === el)) {
|
|
5395
5399
|
const nodeId2 = resolveStableNodeId(el, usedNodeIds);
|
|
@@ -5417,13 +5421,13 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5417
5421
|
const dragSurface = resolveDragSurface(el);
|
|
5418
5422
|
const hostEl = dragSurface.host;
|
|
5419
5423
|
if (!hostEl) {
|
|
5420
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5424
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5421
5425
|
continue;
|
|
5422
5426
|
}
|
|
5423
5427
|
const hostRect = hostEl.getBoundingClientRect();
|
|
5424
5428
|
const nodeId = resolveStableNodeId(hostEl, usedNodeIds);
|
|
5425
5429
|
if (seenResolvedHosts.has(hostEl)) {
|
|
5426
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5430
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5427
5431
|
continue;
|
|
5428
5432
|
}
|
|
5429
5433
|
seenResolvedHosts.add(hostEl);
|
|
@@ -5453,9 +5457,9 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5453
5457
|
identityOrigin: templateFrameLabel(identityMeta2),
|
|
5454
5458
|
editorMeta: surfaceMeta
|
|
5455
5459
|
});
|
|
5456
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5460
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5457
5461
|
} else {
|
|
5458
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5462
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5459
5463
|
}
|
|
5460
5464
|
}
|
|
5461
5465
|
return results;
|
|
@@ -5511,8 +5515,10 @@ function useElementScanner(enabled) {
|
|
|
5511
5515
|
if (pausedRef.current) return;
|
|
5512
5516
|
const root = document.getElementById("root") ?? document.body;
|
|
5513
5517
|
const usedNodeIds = /* @__PURE__ */ new Set();
|
|
5514
|
-
const
|
|
5518
|
+
const resolvedUnreported = /* @__PURE__ */ new Set();
|
|
5519
|
+
const scanned = scanElements(root, 0, usedNodeIds, void 0, resolvedUnreported);
|
|
5515
5520
|
const seenEls = new Set(scanned.map((s) => s.element));
|
|
5521
|
+
for (const el of resolvedUnreported) seenEls.add(el);
|
|
5516
5522
|
for (const el of Array.from(document.querySelectorAll("[data-dnd-moved]"))) {
|
|
5517
5523
|
if (seenEls.has(el)) continue;
|
|
5518
5524
|
const entry = buildMovedElementEntry(el, usedNodeIds);
|
|
@@ -5752,7 +5758,7 @@ function ElementHighlight({
|
|
|
5752
5758
|
"data-outline-for": el.nodeId,
|
|
5753
5759
|
"data-design-mode-ui": true,
|
|
5754
5760
|
style: getOutlineStyle(rect, isSelected, inspectorTheme),
|
|
5755
|
-
children: isSelected && HANDLES.map((dir) => {
|
|
5761
|
+
children: isSelected && el.draggable && HANDLES.map((dir) => {
|
|
5756
5762
|
const pos = handlePosition(dir);
|
|
5757
5763
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
5758
5764
|
"div",
|
|
@@ -8519,7 +8525,9 @@ function useResizeSession({
|
|
|
8519
8525
|
}) {
|
|
8520
8526
|
const handleResizeHandlePointerDown = (0, import_react14.useCallback)((e, nodeId, dir) => {
|
|
8521
8527
|
if (refs.resizeSessionRef.current) return;
|
|
8522
|
-
const
|
|
8528
|
+
const scanned = refs.nodeMapRef.current.get(nodeId);
|
|
8529
|
+
if (scanned && scanned.draggable === false) return;
|
|
8530
|
+
const selectedEl = scanned?.element ?? document.querySelector(`[data-dnd-node-id="${nodeId}"]`);
|
|
8523
8531
|
if (!selectedEl) return;
|
|
8524
8532
|
e.preventDefault();
|
|
8525
8533
|
e.stopPropagation();
|
package/dist/client/client.mjs
CHANGED
|
@@ -5224,8 +5224,10 @@ function ensureUniqueNodeId(base, used) {
|
|
|
5224
5224
|
while (used.has(`${base}::${idx}`)) idx += 1;
|
|
5225
5225
|
return `${base}::${idx}`;
|
|
5226
5226
|
}
|
|
5227
|
+
var INSERT_NODE_ID_PREFIX = "insert:";
|
|
5227
5228
|
function resolveStableNodeId(el, used) {
|
|
5228
5229
|
const persisted = el.getAttribute(DND_NODE_ID_ATTR);
|
|
5230
|
+
if (persisted && persisted.startsWith(INSERT_NODE_ID_PREFIX)) return persisted;
|
|
5229
5231
|
if (persisted && !used.has(persisted)) return persisted;
|
|
5230
5232
|
const projected = projectUsageMeta(el);
|
|
5231
5233
|
if (projected) return ensureUniqueNodeId(projected.nodeId, used);
|
|
@@ -5296,7 +5298,7 @@ function isFlexOrGridParent(el) {
|
|
|
5296
5298
|
const d = getComputedStyle(el).display;
|
|
5297
5299
|
return d === "flex" || d === "grid" || d === "inline-flex" || d === "inline-grid";
|
|
5298
5300
|
}
|
|
5299
|
-
function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(), seenResolvedHosts = /* @__PURE__ */ new WeakSet()) {
|
|
5301
|
+
function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(), seenResolvedHosts = /* @__PURE__ */ new WeakSet(), resolvedUnreported = /* @__PURE__ */ new Set()) {
|
|
5300
5302
|
if (depth > MAX_DEPTH) return [];
|
|
5301
5303
|
const results = [];
|
|
5302
5304
|
for (const child of Array.from(root.children)) {
|
|
@@ -5321,7 +5323,9 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5321
5323
|
const nodeId2 = resolveStableNodeId(el, usedNodeIds);
|
|
5322
5324
|
usedNodeIds.add(nodeId2);
|
|
5323
5325
|
el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
|
|
5324
|
-
if (
|
|
5326
|
+
if (isZeroRect) {
|
|
5327
|
+
resolvedUnreported.add(el);
|
|
5328
|
+
} else {
|
|
5325
5329
|
seenResolvedHosts.add(el);
|
|
5326
5330
|
const surfaceMeta2 = el instanceof HTMLElement ? getDirectEditorMeta(el) ?? void 0 : void 0;
|
|
5327
5331
|
const identityMeta3 = projectUsageMeta(el) ?? surfaceMeta2;
|
|
@@ -5345,7 +5349,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5345
5349
|
});
|
|
5346
5350
|
}
|
|
5347
5351
|
}
|
|
5348
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5352
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5349
5353
|
continue;
|
|
5350
5354
|
}
|
|
5351
5355
|
if (isSvgRoot && isInspectorEligible(el) === null) {
|
|
@@ -5354,7 +5358,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5354
5358
|
const eligibilitySource = isInspectorEligible(el);
|
|
5355
5359
|
const inferredEligibility = eligibilitySource ?? inferEligibilityFromSortableParent(el) ?? (projectUsageMeta(el) ? "projected-usage" : null);
|
|
5356
5360
|
if (inferredEligibility === null) {
|
|
5357
|
-
const childResults = scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts);
|
|
5361
|
+
const childResults = scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported);
|
|
5358
5362
|
results.push(...childResults);
|
|
5359
5363
|
if (isFlexOrGridParent(el) && !seenResolvedHosts.has(el) && childResults.some((r) => r.draggable && r.element.parentElement === el)) {
|
|
5360
5364
|
const nodeId2 = resolveStableNodeId(el, usedNodeIds);
|
|
@@ -5382,13 +5386,13 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5382
5386
|
const dragSurface = resolveDragSurface(el);
|
|
5383
5387
|
const hostEl = dragSurface.host;
|
|
5384
5388
|
if (!hostEl) {
|
|
5385
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5389
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5386
5390
|
continue;
|
|
5387
5391
|
}
|
|
5388
5392
|
const hostRect = hostEl.getBoundingClientRect();
|
|
5389
5393
|
const nodeId = resolveStableNodeId(hostEl, usedNodeIds);
|
|
5390
5394
|
if (seenResolvedHosts.has(hostEl)) {
|
|
5391
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5395
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5392
5396
|
continue;
|
|
5393
5397
|
}
|
|
5394
5398
|
seenResolvedHosts.add(hostEl);
|
|
@@ -5418,9 +5422,9 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
|
|
|
5418
5422
|
identityOrigin: templateFrameLabel(identityMeta2),
|
|
5419
5423
|
editorMeta: surfaceMeta
|
|
5420
5424
|
});
|
|
5421
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5425
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5422
5426
|
} else {
|
|
5423
|
-
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
|
|
5427
|
+
if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
|
|
5424
5428
|
}
|
|
5425
5429
|
}
|
|
5426
5430
|
return results;
|
|
@@ -5476,8 +5480,10 @@ function useElementScanner(enabled) {
|
|
|
5476
5480
|
if (pausedRef.current) return;
|
|
5477
5481
|
const root = document.getElementById("root") ?? document.body;
|
|
5478
5482
|
const usedNodeIds = /* @__PURE__ */ new Set();
|
|
5479
|
-
const
|
|
5483
|
+
const resolvedUnreported = /* @__PURE__ */ new Set();
|
|
5484
|
+
const scanned = scanElements(root, 0, usedNodeIds, void 0, resolvedUnreported);
|
|
5480
5485
|
const seenEls = new Set(scanned.map((s) => s.element));
|
|
5486
|
+
for (const el of resolvedUnreported) seenEls.add(el);
|
|
5481
5487
|
for (const el of Array.from(document.querySelectorAll("[data-dnd-moved]"))) {
|
|
5482
5488
|
if (seenEls.has(el)) continue;
|
|
5483
5489
|
const entry = buildMovedElementEntry(el, usedNodeIds);
|
|
@@ -5717,7 +5723,7 @@ function ElementHighlight({
|
|
|
5717
5723
|
"data-outline-for": el.nodeId,
|
|
5718
5724
|
"data-design-mode-ui": true,
|
|
5719
5725
|
style: getOutlineStyle(rect, isSelected, inspectorTheme),
|
|
5720
|
-
children: isSelected && HANDLES.map((dir) => {
|
|
5726
|
+
children: isSelected && el.draggable && HANDLES.map((dir) => {
|
|
5721
5727
|
const pos = handlePosition(dir);
|
|
5722
5728
|
return /* @__PURE__ */ jsx2(
|
|
5723
5729
|
"div",
|
|
@@ -8484,7 +8490,9 @@ function useResizeSession({
|
|
|
8484
8490
|
}) {
|
|
8485
8491
|
const handleResizeHandlePointerDown = useCallback7((e, nodeId, dir) => {
|
|
8486
8492
|
if (refs.resizeSessionRef.current) return;
|
|
8487
|
-
const
|
|
8493
|
+
const scanned = refs.nodeMapRef.current.get(nodeId);
|
|
8494
|
+
if (scanned && scanned.draggable === false) return;
|
|
8495
|
+
const selectedEl = scanned?.element ?? document.querySelector(`[data-dnd-node-id="${nodeId}"]`);
|
|
8488
8496
|
if (!selectedEl) return;
|
|
8489
8497
|
e.preventDefault();
|
|
8490
8498
|
e.stopPropagation();
|
package/dist/index.d.mts
CHANGED
|
@@ -13,6 +13,9 @@ import { types, PluginObj } from '@babel/core';
|
|
|
13
13
|
* + (ARCH-883 F1) slot, propBindings, propOwner
|
|
14
14
|
* + (ARCH-883-KOI) origin
|
|
15
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)
|
|
16
19
|
*
|
|
17
20
|
* At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
|
|
18
21
|
* element identity without any DOM attributes.
|
|
@@ -216,6 +219,134 @@ interface PropBinding {
|
|
|
216
219
|
*/
|
|
217
220
|
kind: 'direct' | 'derived' | 'formatted';
|
|
218
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
|
+
};
|
|
219
350
|
/** Metadata injected into every JSX element by babel-plugin-editor-meta. */
|
|
220
351
|
interface EditorMeta {
|
|
221
352
|
/** Stable identifier: "file:line:col:componentName" */
|
|
@@ -398,6 +529,30 @@ interface EditorMeta {
|
|
|
398
529
|
* not the usage's, and routing those would write the wrong prop. Mismatch = gap.
|
|
399
530
|
*/
|
|
400
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>;
|
|
401
556
|
}
|
|
402
557
|
|
|
403
558
|
interface ArchieDevToolsOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ import { types, PluginObj } from '@babel/core';
|
|
|
13
13
|
* + (ARCH-883 F1) slot, propBindings, propOwner
|
|
14
14
|
* + (ARCH-883-KOI) origin
|
|
15
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)
|
|
16
19
|
*
|
|
17
20
|
* At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
|
|
18
21
|
* element identity without any DOM attributes.
|
|
@@ -216,6 +219,134 @@ interface PropBinding {
|
|
|
216
219
|
*/
|
|
217
220
|
kind: 'direct' | 'derived' | 'formatted';
|
|
218
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
|
+
};
|
|
219
350
|
/** Metadata injected into every JSX element by babel-plugin-editor-meta. */
|
|
220
351
|
interface EditorMeta {
|
|
221
352
|
/** Stable identifier: "file:line:col:componentName" */
|
|
@@ -398,6 +529,30 @@ interface EditorMeta {
|
|
|
398
529
|
* not the usage's, and routing those would write the wrong prop. Mismatch = gap.
|
|
399
530
|
*/
|
|
400
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>;
|
|
401
556
|
}
|
|
402
557
|
|
|
403
558
|
interface ArchieDevToolsOptions {
|
package/dist/index.js
CHANGED
|
@@ -382,6 +382,146 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
382
382
|
if (!t.isJSXText(only)) return "__dynamic__";
|
|
383
383
|
return only.value.trim();
|
|
384
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
|
+
}
|
|
385
525
|
function isMetaStripSpreadExpression(arg) {
|
|
386
526
|
if (!t.isCallExpression(arg)) return false;
|
|
387
527
|
if (!t.isArrowFunctionExpression(arg.callee)) return false;
|
|
@@ -773,6 +913,7 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
773
913
|
if (!loc) return;
|
|
774
914
|
const root = state.file.opts.root ?? "";
|
|
775
915
|
const file = root ? nodePath.relative(root, filename) : filename;
|
|
916
|
+
currentFile = file;
|
|
776
917
|
const line = loc.line;
|
|
777
918
|
const col = loc.column + 1;
|
|
778
919
|
const componentName = getJSXTagName(path.node);
|
|
@@ -856,6 +997,12 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
856
997
|
childrenText === null ? t.nullLiteral() : t.stringLiteral(childrenText)
|
|
857
998
|
)
|
|
858
999
|
);
|
|
1000
|
+
const childrenSource = describeChildrenSource(path);
|
|
1001
|
+
if (childrenSource) {
|
|
1002
|
+
metaObj.properties.push(
|
|
1003
|
+
t.objectProperty(t.identifier("childrenSource"), propSourceToAst(childrenSource))
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
859
1006
|
}
|
|
860
1007
|
if (bindings.length > 0) {
|
|
861
1008
|
metaObj.properties.push(
|
|
@@ -868,6 +1015,19 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
|
|
|
868
1015
|
)
|
|
869
1016
|
);
|
|
870
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
|
+
}
|
|
871
1031
|
if (isHostElement && includeHostSourceRef) {
|
|
872
1032
|
path.node.attributes.push(
|
|
873
1033
|
t.jsxAttribute(
|
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "attr-local-const-string",
|
|
4
|
+
"description": "const local con init string, sin props desestructuradas — atributo de la USAGE",
|
|
5
|
+
"code": "export default function Screen() {\n const title = \"Contenido\";\n return <Section title={title} />;\n}",
|
|
6
|
+
"usageComponentName": "Section",
|
|
7
|
+
"editorMeta": {
|
|
8
|
+
"nodeId": "src/components.jsx:3:10:Section",
|
|
9
|
+
"file": "src/components.jsx",
|
|
10
|
+
"line": 3,
|
|
11
|
+
"col": 10,
|
|
12
|
+
"componentName": "Section",
|
|
13
|
+
"staticProps": {
|
|
14
|
+
"title": "__dynamic__"
|
|
15
|
+
},
|
|
16
|
+
"movable": false,
|
|
17
|
+
"parentNodeId": null,
|
|
18
|
+
"childrenText": null,
|
|
19
|
+
"propSources": {
|
|
20
|
+
"title": {
|
|
21
|
+
"kind": "local-const",
|
|
22
|
+
"name": "title",
|
|
23
|
+
"file": "src/components.jsx",
|
|
24
|
+
"line": 2,
|
|
25
|
+
"col": 17,
|
|
26
|
+
"literal": "Contenido",
|
|
27
|
+
"refs": 1
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "attr-local-const-template-no-interp",
|
|
34
|
+
"description": "const local con init template literal SIN interpolaciones",
|
|
35
|
+
"code": "export default function Screen() {\n const title = `Contenido fijo`;\n return <Section title={title} />;\n}",
|
|
36
|
+
"usageComponentName": "Section",
|
|
37
|
+
"editorMeta": {
|
|
38
|
+
"nodeId": "src/components.jsx:3:10:Section",
|
|
39
|
+
"file": "src/components.jsx",
|
|
40
|
+
"line": 3,
|
|
41
|
+
"col": 10,
|
|
42
|
+
"componentName": "Section",
|
|
43
|
+
"staticProps": {
|
|
44
|
+
"title": "__dynamic__"
|
|
45
|
+
},
|
|
46
|
+
"movable": false,
|
|
47
|
+
"parentNodeId": null,
|
|
48
|
+
"childrenText": null,
|
|
49
|
+
"propSources": {
|
|
50
|
+
"title": {
|
|
51
|
+
"kind": "local-const",
|
|
52
|
+
"name": "title",
|
|
53
|
+
"file": "src/components.jsx",
|
|
54
|
+
"line": 2,
|
|
55
|
+
"col": 17,
|
|
56
|
+
"literal": "Contenido fijo",
|
|
57
|
+
"refs": 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "attr-local-const-refs-2",
|
|
64
|
+
"description": "FE MINOR-5: el MISMO const alimenta dos elementos — refs:2 en ambas usages",
|
|
65
|
+
"code": "export default function Screen() {\n const title = \"Contenido\";\n return (\n <div>\n <Section title={title} />\n <Aside title={title} />\n </div>\n );\n}",
|
|
66
|
+
"usageComponentName": "Section",
|
|
67
|
+
"editorMeta": {
|
|
68
|
+
"nodeId": "src/components.jsx:5:7:Section",
|
|
69
|
+
"file": "src/components.jsx",
|
|
70
|
+
"line": 5,
|
|
71
|
+
"col": 7,
|
|
72
|
+
"componentName": "Section",
|
|
73
|
+
"staticProps": {
|
|
74
|
+
"title": "__dynamic__"
|
|
75
|
+
},
|
|
76
|
+
"movable": true,
|
|
77
|
+
"parentNodeId": "src/components.jsx:4:5:div",
|
|
78
|
+
"childrenText": null,
|
|
79
|
+
"propSources": {
|
|
80
|
+
"title": {
|
|
81
|
+
"kind": "local-const",
|
|
82
|
+
"name": "title",
|
|
83
|
+
"file": "src/components.jsx",
|
|
84
|
+
"line": 2,
|
|
85
|
+
"col": 17,
|
|
86
|
+
"literal": "Contenido",
|
|
87
|
+
"refs": 2
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "attr-identifier-destructured-prop",
|
|
94
|
+
"description": "identificador que es una prop desestructurada del componente",
|
|
95
|
+
"code": "function Screen({ title }) { return <Section title={title} />; }",
|
|
96
|
+
"usageComponentName": "Section",
|
|
97
|
+
"editorMeta": {
|
|
98
|
+
"nodeId": "src/components.jsx:1:37:Section",
|
|
99
|
+
"file": "src/components.jsx",
|
|
100
|
+
"line": 1,
|
|
101
|
+
"col": 37,
|
|
102
|
+
"componentName": "Section",
|
|
103
|
+
"staticProps": {
|
|
104
|
+
"title": "__dynamic__"
|
|
105
|
+
},
|
|
106
|
+
"movable": false,
|
|
107
|
+
"parentNodeId": null,
|
|
108
|
+
"childrenText": null,
|
|
109
|
+
"propBindings": [
|
|
110
|
+
{
|
|
111
|
+
"target": "attr:title",
|
|
112
|
+
"prop": "title",
|
|
113
|
+
"kind": "direct"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"propOwner": "Screen",
|
|
117
|
+
"propSources": {
|
|
118
|
+
"title": {
|
|
119
|
+
"kind": "identifier",
|
|
120
|
+
"name": "title"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"id": "attr-identifier-plain-param",
|
|
127
|
+
"description": "identificador que es un parametro NO desestructurado",
|
|
128
|
+
"code": "function Screen(props) { return <Section title={props} />; }",
|
|
129
|
+
"usageComponentName": "Section",
|
|
130
|
+
"editorMeta": {
|
|
131
|
+
"nodeId": "src/components.jsx:1:33:Section",
|
|
132
|
+
"file": "src/components.jsx",
|
|
133
|
+
"line": 1,
|
|
134
|
+
"col": 33,
|
|
135
|
+
"componentName": "Section",
|
|
136
|
+
"staticProps": {
|
|
137
|
+
"title": "__dynamic__"
|
|
138
|
+
},
|
|
139
|
+
"movable": false,
|
|
140
|
+
"parentNodeId": null,
|
|
141
|
+
"childrenText": null,
|
|
142
|
+
"propSources": {
|
|
143
|
+
"title": {
|
|
144
|
+
"kind": "identifier",
|
|
145
|
+
"name": "props"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "attr-member-props-title",
|
|
152
|
+
"description": "props.title — parametro plano accedido por propiedad",
|
|
153
|
+
"code": "function Screen(props) { return <Section title={props.title} />; }",
|
|
154
|
+
"usageComponentName": "Section",
|
|
155
|
+
"editorMeta": {
|
|
156
|
+
"nodeId": "src/components.jsx:1:33:Section",
|
|
157
|
+
"file": "src/components.jsx",
|
|
158
|
+
"line": 1,
|
|
159
|
+
"col": 33,
|
|
160
|
+
"componentName": "Section",
|
|
161
|
+
"staticProps": {
|
|
162
|
+
"title": "__dynamic__"
|
|
163
|
+
},
|
|
164
|
+
"movable": false,
|
|
165
|
+
"parentNodeId": null,
|
|
166
|
+
"childrenText": null,
|
|
167
|
+
"propSources": {
|
|
168
|
+
"title": {
|
|
169
|
+
"kind": "member",
|
|
170
|
+
"root": "props",
|
|
171
|
+
"path": [
|
|
172
|
+
"title"
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"id": "attr-member-deep-chain",
|
|
180
|
+
"description": "item.address.city — cadena de propiedades, en orden",
|
|
181
|
+
"code": "function Screen({ item }) { return <Section title={item.address.city} />; }",
|
|
182
|
+
"usageComponentName": "Section",
|
|
183
|
+
"editorMeta": {
|
|
184
|
+
"nodeId": "src/components.jsx:1:36:Section",
|
|
185
|
+
"file": "src/components.jsx",
|
|
186
|
+
"line": 1,
|
|
187
|
+
"col": 36,
|
|
188
|
+
"componentName": "Section",
|
|
189
|
+
"staticProps": {
|
|
190
|
+
"title": "__dynamic__"
|
|
191
|
+
},
|
|
192
|
+
"movable": false,
|
|
193
|
+
"parentNodeId": null,
|
|
194
|
+
"childrenText": null,
|
|
195
|
+
"propBindings": [
|
|
196
|
+
{
|
|
197
|
+
"target": "attr:title",
|
|
198
|
+
"prop": "item",
|
|
199
|
+
"kind": "formatted"
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"propOwner": "Screen",
|
|
203
|
+
"propSources": {
|
|
204
|
+
"title": {
|
|
205
|
+
"kind": "member",
|
|
206
|
+
"root": "item",
|
|
207
|
+
"path": [
|
|
208
|
+
"address",
|
|
209
|
+
"city"
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"id": "attr-other-reassigned",
|
|
217
|
+
"description": "binding reasignado en el fichero — nunca se traza",
|
|
218
|
+
"code": "export default function Screen() {\n let title = \"Contenido\";\n title = \"Otro valor\";\n return <Section title={title} />;\n}",
|
|
219
|
+
"usageComponentName": "Section",
|
|
220
|
+
"editorMeta": {
|
|
221
|
+
"nodeId": "src/components.jsx:4:10:Section",
|
|
222
|
+
"file": "src/components.jsx",
|
|
223
|
+
"line": 4,
|
|
224
|
+
"col": 10,
|
|
225
|
+
"componentName": "Section",
|
|
226
|
+
"staticProps": {
|
|
227
|
+
"title": "__dynamic__"
|
|
228
|
+
},
|
|
229
|
+
"movable": false,
|
|
230
|
+
"parentNodeId": null,
|
|
231
|
+
"childrenText": null,
|
|
232
|
+
"propSources": {
|
|
233
|
+
"title": {
|
|
234
|
+
"kind": "other"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": "attr-other-cross-module-import",
|
|
241
|
+
"description": "identificador importado de otro modulo — nunca se traza",
|
|
242
|
+
"code": "import { title } from './constants';\nexport default function Screen() {\n return <Section title={title} />;\n}",
|
|
243
|
+
"usageComponentName": "Section",
|
|
244
|
+
"editorMeta": {
|
|
245
|
+
"nodeId": "src/components.jsx:3:10:Section",
|
|
246
|
+
"file": "src/components.jsx",
|
|
247
|
+
"line": 3,
|
|
248
|
+
"col": 10,
|
|
249
|
+
"componentName": "Section",
|
|
250
|
+
"staticProps": {
|
|
251
|
+
"title": "__dynamic__"
|
|
252
|
+
},
|
|
253
|
+
"movable": false,
|
|
254
|
+
"parentNodeId": null,
|
|
255
|
+
"childrenText": null,
|
|
256
|
+
"propSources": {
|
|
257
|
+
"title": {
|
|
258
|
+
"kind": "other"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"id": "children-local-const",
|
|
265
|
+
"description": "const local trazado a traves de la SOLA expresion hija",
|
|
266
|
+
"code": "export default function Screen() {\n const title = \"Contenido\";\n return <Section>{title}</Section>;\n}",
|
|
267
|
+
"usageComponentName": "Section",
|
|
268
|
+
"editorMeta": {
|
|
269
|
+
"nodeId": "src/components.jsx:3:10:Section",
|
|
270
|
+
"file": "src/components.jsx",
|
|
271
|
+
"line": 3,
|
|
272
|
+
"col": 10,
|
|
273
|
+
"componentName": "Section",
|
|
274
|
+
"staticProps": null,
|
|
275
|
+
"movable": false,
|
|
276
|
+
"parentNodeId": null,
|
|
277
|
+
"childrenText": "__dynamic__",
|
|
278
|
+
"childrenSource": {
|
|
279
|
+
"kind": "local-const",
|
|
280
|
+
"name": "title",
|
|
281
|
+
"file": "src/components.jsx",
|
|
282
|
+
"line": 2,
|
|
283
|
+
"col": 17,
|
|
284
|
+
"literal": "Contenido",
|
|
285
|
+
"refs": 1
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "children-shadowing-map-callback-param",
|
|
291
|
+
"description": "MINOR-1: un const de modulo con el mismo nombre NUNCA gana — el parametro del callback de .map() shadowea",
|
|
292
|
+
"code": "const title = \"modulo\";\nexport default function Screen({ items }) {\n return items.map(({ title }) => <CardTitle>{title}</CardTitle>);\n}",
|
|
293
|
+
"usageComponentName": "CardTitle",
|
|
294
|
+
"editorMeta": {
|
|
295
|
+
"nodeId": "src/components.jsx:3:35:CardTitle",
|
|
296
|
+
"file": "src/components.jsx",
|
|
297
|
+
"line": 3,
|
|
298
|
+
"col": 35,
|
|
299
|
+
"componentName": "CardTitle",
|
|
300
|
+
"staticProps": null,
|
|
301
|
+
"movable": false,
|
|
302
|
+
"parentNodeId": null,
|
|
303
|
+
"childrenText": "__dynamic__",
|
|
304
|
+
"childrenSource": {
|
|
305
|
+
"kind": "identifier",
|
|
306
|
+
"name": "title"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archie/devtools",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
4
4
|
"description": "DevTools for Archie generated applications - Route synchronization and editor communication",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"release": "node publish.js"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
|
-
"dist/**/*"
|
|
43
|
+
"dist/**/*",
|
|
44
|
+
"fixtures/**/*"
|
|
44
45
|
],
|
|
45
46
|
"keywords": [
|
|
46
47
|
"vite",
|