Summary
bin/theme-graph declares a WebComponentMap, passes a getWebComponentDefinitionReference resolver built on it, but never calls getWebComponentMap — so the map is always empty and the resolver always returns undefined.
The result: custom-element references (<my-widget>) are never resolved to the asset that defines them, so an asset referenced only as a custom element is missing from the CLI's graph and looks like dead code.
Calling getWebComponentMap fixes it — verified below.
Versions
@shopify/theme-graph 0.3.2
@shopify/theme-check-common 3.29.0
- Node 24.11.1, Windows 11
The code
bin/theme-graph:
/** @type {import('@shopify/theme-graph').WebComponentMap} */
const webComponentDefs = new Map(); // <- created empty
const dependencies = {
// ...
getWebComponentDefinitionReference: (customElementName) =>
webComponentDefs.get(customElementName), // <- always undefined
};
$ grep -c getWebComponentMap bin/theme-graph
0
getWebComponentMap is exported from the package and works correctly; the CLI just never invokes it to populate the map.
Reproduction
sections/widget.liquid — custom element plus an explicit script tag
<script src="{{ 'my-widget.js' | asset_url }}" defer></script>
<my-widget></my-widget>
{% schema %}{"name":"widget"}{% endschema %}
sections/ce-only.liquid — custom element only, no explicit asset reference
<my-orphan-widget></my-orphan-widget>
{% schema %}{"name":"ce-only"}{% endschema %}
assets/my-widget.js
class W extends HTMLElement {}
customElements.define('my-widget', W);
assets/my-orphan-widget.js
class O extends HTMLElement {}
customElements.define('my-orphan-widget', O);
npx theme-graph minitheme > graph.json
Actual (CLI)
nodes: 8 edges: 6
IN GRAPH my-widget.js <- only because of the explicit asset_url script tag
ABSENT my-orphan-widget.js <- referenced as <my-orphan-widget>, not resolved
sections/ce-only.liquid is in the graph as a section entry point but has zero outbound edges.
With the map populated (library path)
Same theme, same buildThemeGraph, the only change being one added call:
const webComponentDefs = await getWebComponentMap(root, { fs: NodeFS, getSourceCode });
web components discovered: my-orphan-widget, my-widget
nodes: 9 edges: 8
IN GRAPH my-widget.js
IN GRAPH my-orphan-widget.js <- now resolved
So the resolution logic is correct and complete; only the CLI's wiring is missing.
Suggested fix
In bin/theme-graph, replace the empty map with a populated one:
const webComponentDefs = await getWebComponentMap(rootUri, {
fs: NodeFileSystem,
getSourceCode,
});
(after the preload, so the source-code cache is warm).
Why it matters
Custom-element-per-component is the dominant architecture in current commercial themes. On a production Clean Canvas Enterprise 2.3.0 theme, four assets (side-drawer.js, quantity-input.js, quantity-input.css, product-recommendations.js) were referenced only via their custom-element tags and were absent from the CLI graph — indistinguishable from genuinely dead assets.
Since the VS Code extension's dependencies / references / dead-code features are built on this graph, anything consuming the CLI output will report these as unused. Related failure mode to #1279 and to #1290 (the parse-error case).
Summary
bin/theme-graphdeclares aWebComponentMap, passes agetWebComponentDefinitionReferenceresolver built on it, but never callsgetWebComponentMap— so the map is always empty and the resolver always returnsundefined.The result: custom-element references (
<my-widget>) are never resolved to the asset that defines them, so an asset referenced only as a custom element is missing from the CLI's graph and looks like dead code.Calling
getWebComponentMapfixes it — verified below.Versions
@shopify/theme-graph0.3.2@shopify/theme-check-common3.29.0The code
bin/theme-graph:getWebComponentMapis exported from the package and works correctly; the CLI just never invokes it to populate the map.Reproduction
sections/widget.liquid— custom element plus an explicit script tagsections/ce-only.liquid— custom element only, no explicit asset referenceassets/my-widget.jsassets/my-orphan-widget.jsActual (CLI)
sections/ce-only.liquidis in the graph as a section entry point but has zero outbound edges.With the map populated (library path)
Same theme, same
buildThemeGraph, the only change being one added call:So the resolution logic is correct and complete; only the CLI's wiring is missing.
Suggested fix
In
bin/theme-graph, replace the empty map with a populated one:(after the preload, so the source-code cache is warm).
Why it matters
Custom-element-per-component is the dominant architecture in current commercial themes. On a production Clean Canvas Enterprise 2.3.0 theme, four assets (
side-drawer.js,quantity-input.js,quantity-input.css,product-recommendations.js) were referenced only via their custom-element tags and were absent from the CLI graph — indistinguishable from genuinely dead assets.Since the VS Code extension's dependencies / references / dead-code features are built on this graph, anything consuming the CLI output will report these as unused. Related failure mode to #1279 and to #1290 (the parse-error case).