@fluixi/devtools 0.2.0-alpha.1 → 0.2.0-alpha.3

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.
Files changed (96) hide show
  1. package/README.md +80 -2
  2. package/dist/callsite.d.ts +22 -0
  3. package/dist/callsite.d.ts.map +1 -0
  4. package/dist/callsite.js +82 -0
  5. package/dist/change-view.d.ts +10 -0
  6. package/dist/change-view.d.ts.map +1 -0
  7. package/dist/change-view.js +102 -0
  8. package/dist/copy.d.ts +9 -0
  9. package/dist/copy.d.ts.map +1 -0
  10. package/dist/copy.js +52 -0
  11. package/dist/describe.d.ts +3 -0
  12. package/dist/describe.d.ts.map +1 -0
  13. package/dist/describe.js +47 -0
  14. package/dist/detail-panel.d.ts +17 -0
  15. package/dist/detail-panel.d.ts.map +1 -0
  16. package/dist/detail-panel.js +217 -0
  17. package/dist/diff.d.ts +31 -0
  18. package/dist/diff.d.ts.map +1 -0
  19. package/dist/diff.js +132 -0
  20. package/dist/dom.d.ts +81 -0
  21. package/dist/dom.d.ts.map +1 -0
  22. package/dist/dom.js +269 -0
  23. package/dist/export.d.ts +55 -0
  24. package/dist/export.d.ts.map +1 -0
  25. package/dist/export.js +90 -0
  26. package/dist/float.d.ts +54 -0
  27. package/dist/float.d.ts.map +1 -0
  28. package/dist/float.js +256 -0
  29. package/dist/format.d.ts +16 -0
  30. package/dist/format.d.ts.map +1 -0
  31. package/dist/format.js +188 -0
  32. package/dist/graph-view.d.ts +79 -2
  33. package/dist/graph-view.d.ts.map +1 -1
  34. package/dist/graph-view.js +866 -50
  35. package/dist/highlight.d.ts +56 -0
  36. package/dist/highlight.d.ts.map +1 -0
  37. package/dist/highlight.js +154 -0
  38. package/dist/history.d.ts +59 -0
  39. package/dist/history.d.ts.map +1 -0
  40. package/dist/history.js +85 -0
  41. package/dist/hook.d.ts +60 -0
  42. package/dist/hook.d.ts.map +1 -0
  43. package/dist/hook.js +142 -0
  44. package/dist/index.d.ts +44 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +25 -1
  47. package/dist/inspector.d.ts +60 -0
  48. package/dist/inspector.d.ts.map +1 -0
  49. package/dist/inspector.js +415 -0
  50. package/dist/instrument.d.ts +147 -5
  51. package/dist/instrument.d.ts.map +1 -1
  52. package/dist/instrument.js +164 -44
  53. package/dist/kind-filter.d.ts +39 -0
  54. package/dist/kind-filter.d.ts.map +1 -0
  55. package/dist/kind-filter.js +151 -0
  56. package/dist/menu.d.ts +37 -0
  57. package/dist/menu.d.ts.map +1 -0
  58. package/dist/menu.js +125 -0
  59. package/dist/observe.d.ts +80 -0
  60. package/dist/observe.d.ts.map +1 -0
  61. package/dist/observe.js +893 -0
  62. package/dist/palette.d.ts +11 -0
  63. package/dist/palette.d.ts.map +1 -0
  64. package/dist/palette.js +27 -0
  65. package/dist/path.d.ts +19 -0
  66. package/dist/path.d.ts.map +1 -0
  67. package/dist/path.js +49 -0
  68. package/dist/plugin.d.ts +121 -0
  69. package/dist/plugin.d.ts.map +1 -0
  70. package/dist/plugin.js +74 -0
  71. package/dist/plugins/context.d.ts +10 -0
  72. package/dist/plugins/context.d.ts.map +1 -0
  73. package/dist/plugins/context.js +80 -0
  74. package/dist/plugins/directives.d.ts +25 -0
  75. package/dist/plugins/directives.d.ts.map +1 -0
  76. package/dist/plugins/directives.js +78 -0
  77. package/dist/plugins/index.d.ts +7 -0
  78. package/dist/plugins/index.d.ts.map +1 -0
  79. package/dist/plugins/index.js +9 -0
  80. package/dist/source.d.ts +72 -0
  81. package/dist/source.d.ts.map +1 -0
  82. package/dist/source.js +153 -0
  83. package/dist/timeline.d.ts +30 -0
  84. package/dist/timeline.d.ts.map +1 -0
  85. package/dist/timeline.js +271 -0
  86. package/dist/tree.d.ts +128 -0
  87. package/dist/tree.d.ts.map +1 -0
  88. package/dist/tree.js +645 -0
  89. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  90. package/dist/value-pane.d.ts +10 -0
  91. package/dist/value-pane.d.ts.map +1 -0
  92. package/dist/value-pane.js +61 -0
  93. package/dist/wire.d.ts +100 -0
  94. package/dist/wire.d.ts.map +1 -0
  95. package/dist/wire.js +93 -0
  96. package/package.json +10 -3
@@ -0,0 +1,11 @@
1
+ import type { NodeKind } from './instrument.js';
2
+ /** Element rows exist only in the tree, which is why this is wider than NodeKind. */
3
+ export type ColouredKind = NodeKind | 'element';
4
+ export declare const KIND_COLOR: Record<ColouredKind, string>;
5
+ /** The variable a host can set to override one, per kind. */
6
+ export declare const kindVar: (kind: ColouredKind) => string;
7
+ /** `var(--fx-kind-memo, #a78bfa)`, so a host may theme it and nothing breaks if it does not. */
8
+ export declare const kindColor: (kind: ColouredKind) => string;
9
+ /** One rule per kind, built from the table so a new kind cannot be missed in one host. */
10
+ export declare function kindRules(selector: (kind: ColouredKind) => string, property: string): string;
11
+ //# sourceMappingURL=palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../src/palette.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,qFAAqF;AACrF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhD,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAiBnD,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,OAAO,GAAI,MAAM,YAAY,KAAG,MAA6B,CAAC;AAE3E,gGAAgG;AAChG,eAAO,MAAM,SAAS,GAAI,MAAM,YAAY,KAAG,MACD,CAAC;AAI/C,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5F"}
@@ -0,0 +1,27 @@
1
+ export const KIND_COLOR = {
2
+ state: '#34d399',
3
+ memo: '#a78bfa',
4
+ effect: '#fbbf24',
5
+ store: '#38bdf8',
6
+ resource: '#fb7185',
7
+ component: '#f0abfc',
8
+ provider: '#fb923c',
9
+ control: '#22d3ee',
10
+ router: '#818cf8',
11
+ handler: '#f472b6',
12
+ context: '#a3e635',
13
+ directive: '#5eead4',
14
+ binding: '#5eead4',
15
+ setter: '#fb7185',
16
+ group: '#8a90a2',
17
+ element: '#8a90a2',
18
+ };
19
+ /** The variable a host can set to override one, per kind. */
20
+ export const kindVar = (kind) => `--fx-kind-${kind}`;
21
+ /** `var(--fx-kind-memo, #a78bfa)`, so a host may theme it and nothing breaks if it does not. */
22
+ export const kindColor = (kind) => `var(${kindVar(kind)}, ${KIND_COLOR[kind]})`;
23
+ const KINDS = Object.keys(KIND_COLOR);
24
+ /** One rule per kind, built from the table so a new kind cannot be missed in one host. */
25
+ export function kindRules(selector, property) {
26
+ return KINDS.map((kind) => `${selector(kind)} { ${property}: ${kindColor(kind)}; }`).join('\n');
27
+ }
package/dist/path.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Showing a file path at the length a reader wants.
3
+ *
4
+ * A node the framework made carries the path of the file that made it, on the machine that
5
+ * ran it: `/Users/someone/work/app/packages/dom/src/lib/dom/runtime.ts`. All of it before
6
+ * the package name says nothing about the project, and it is most of the line.
7
+ *
8
+ * The full path is kept for opening the file — only what is displayed is shortened.
9
+ */
10
+ /**
11
+ * The part of `file` worth showing.
12
+ *
13
+ * Tried in order: below the project root when the host knows it, then the package a
14
+ * node_modules path names, then the workspace package a monorepo path names. A path that
15
+ * matches none of those is left as it is — guessing at a shorter form would hide which file
16
+ * it is, and the point of the line is to say that.
17
+ */
18
+ export declare function shortenPath(file: string, root?: string): string;
19
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAyB/D"}
package/dist/path.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Showing a file path at the length a reader wants.
3
+ *
4
+ * A node the framework made carries the path of the file that made it, on the machine that
5
+ * ran it: `/Users/someone/work/app/packages/dom/src/lib/dom/runtime.ts`. All of it before
6
+ * the package name says nothing about the project, and it is most of the line.
7
+ *
8
+ * The full path is kept for opening the file — only what is displayed is shortened.
9
+ */
10
+ /** Windows separators, and the leading slash an origin-stripped path can lose. */
11
+ const normalize = (file) => file.replace(/\\/g, '/');
12
+ /** `Users/…`, `home/…`: an absolute path whose leading slash was trimmed off. */
13
+ const ROOTED = /^\/|^(Users|home|var|opt|private|tmp|mnt|srv)\//;
14
+ /** Where a monorepo keeps the things it builds. */
15
+ const WORKSPACE_DIRS = new Set(['packages', 'apps', 'libs', 'examples']);
16
+ /**
17
+ * The part of `file` worth showing.
18
+ *
19
+ * Tried in order: below the project root when the host knows it, then the package a
20
+ * node_modules path names, then the workspace package a monorepo path names. A path that
21
+ * matches none of those is left as it is — guessing at a shorter form would hide which file
22
+ * it is, and the point of the line is to say that.
23
+ */
24
+ export function shortenPath(file, root) {
25
+ const path = normalize(file);
26
+ if (root) {
27
+ const base = normalize(root).replace(/\/+$/, '');
28
+ if (base && path.startsWith(`${base}/`))
29
+ return path.slice(base.length + 1);
30
+ // The path lost its leading slash on the way here; the root still has one.
31
+ if (base.startsWith('/') && path.startsWith(`${base.slice(1)}/`))
32
+ return path.slice(base.length);
33
+ }
34
+ // Everything up to the last node_modules is the machine's, not the project's.
35
+ const installed = path.lastIndexOf('node_modules/');
36
+ if (installed >= 0)
37
+ return path.slice(installed + 'node_modules/'.length);
38
+ // A monorepo package, which is where a framework's own files sit while it is worked on.
39
+ // The last of these wins: a checkout under `~/Developer/libs` has one in the machine's
40
+ // part of the path as well as the project's, and the project's is the one meant.
41
+ if (ROOTED.test(path)) {
42
+ const segments = path.split('/');
43
+ for (let i = segments.length - 2; i > 0; i--) {
44
+ if (WORKSPACE_DIRS.has(segments[i]))
45
+ return segments.slice(i).join('/');
46
+ }
47
+ }
48
+ return path;
49
+ }
@@ -0,0 +1,121 @@
1
+ import type { NodeKind, RNode } from './instrument.js';
2
+ /** A directive the runtime applied to an element. */
3
+ export interface DirectiveReport {
4
+ /** The node it was applied inside, usually a component. */
5
+ owner: number | undefined;
6
+ /** `use`, `class`, `style`, `bind`. */
7
+ kind: string;
8
+ /** The key after the colon, or the function name for `use:`. */
9
+ name: string;
10
+ /** The element as a selector. */
11
+ element: string;
12
+ /** The element itself. Only in the page, and the key to annotate it by. */
13
+ target?: unknown;
14
+ at?: {
15
+ file?: string;
16
+ line?: number;
17
+ column?: number;
18
+ };
19
+ }
20
+ /** A node a plugin contributes. Ids come from the graph so they cannot collide. */
21
+ export interface PluginNode {
22
+ label: string;
23
+ kind: NodeKind;
24
+ createdIn?: number;
25
+ deps?: number[];
26
+ /** Short enough for a card. */
27
+ value?: string;
28
+ /** The whole of it, for the inspector. */
29
+ detail?: string;
30
+ /**
31
+ * Whether the application wrote this or the framework did. False folds it into the
32
+ * machinery card, the way an unnamed memo is folded.
33
+ */
34
+ authored?: boolean;
35
+ source?: {
36
+ file?: string;
37
+ line?: number;
38
+ column?: number;
39
+ };
40
+ }
41
+ /** A context provided or read. */
42
+ export interface ContextReport {
43
+ /** The node it happened in. */
44
+ owner: number | undefined;
45
+ /** Identity of the context, stable for its lifetime. */
46
+ id: symbol;
47
+ /** What the symbol was created with, when it says anything. */
48
+ name: string;
49
+ /** Label of the node it happened in, for naming a context nobody named. */
50
+ ownerLabel?: string;
51
+ /** Where the context was written, and whether that was inside a package. */
52
+ at?: {
53
+ file?: string;
54
+ line?: number;
55
+ column?: number;
56
+ external?: boolean;
57
+ };
58
+ kind: 'provide' | 'read';
59
+ /** Formatted, since a panel elsewhere cannot hold the value itself. Not yet clipped. */
60
+ value?: string;
61
+ }
62
+ /** What a plugin gets in the page. */
63
+ export interface CollectContext {
64
+ onDirective(fn: (report: DirectiveReport) => void): void;
65
+ onContext(fn: (report: ContextReport) => void): void;
66
+ /** Attach data under this plugin's name. Must survive JSON.stringify. */
67
+ annotate(id: number, data: unknown): void;
68
+ /**
69
+ * The same, for an element rather than a node. A directive belongs to the element it was
70
+ * applied to, and an element is not a node, so it is keyed by the element itself and
71
+ * lands on the description a panel draws.
72
+ */
73
+ annotateElement(target: unknown, data: unknown): void;
74
+ /** What this plugin already attached to an element. */
75
+ elementAnnotation(target: unknown): unknown;
76
+ add(node: PluginNode): number;
77
+ /**
78
+ * Say what a plain component really is. A component that exists to provide a context is a
79
+ * provider, and nothing but the plugin watching the context can know that.
80
+ *
81
+ * Ignored for a node the framework already named, and ignored for any kind that is not
82
+ * itself a tag: a Router that provides a context is still a Router, and a component
83
+ * relabelled a signal would leave the tree along with everything it rendered.
84
+ */
85
+ retype(id: number, kind: NodeKind): void;
86
+ /** What this plugin already attached, for adding to it rather than replacing it. */
87
+ annotation(id: number): unknown;
88
+ }
89
+ /** A line a plugin adds to the inspector. `at` makes it a link the host can open. */
90
+ export interface PluginRow {
91
+ label: string;
92
+ value?: string;
93
+ at?: {
94
+ file?: string;
95
+ line?: number;
96
+ column?: number;
97
+ };
98
+ }
99
+ export interface DevtoolsPlugin {
100
+ /** Namespaces everything it writes. */
101
+ name: string;
102
+ /** In the page. */
103
+ collect?(ctx: CollectContext): void;
104
+ /** In a panel, for the selected node. Sees only what crossed the wire. */
105
+ rows?(node: RNode, graph: Map<number, RNode>): PluginRow[];
106
+ /** A short word next to the label, in the tree and on the card. */
107
+ badge?(node: RNode): string | undefined;
108
+ /** The same, for an element row. Given what plugins attached to that element. */
109
+ elementBadge?(extra: Record<string, unknown>): string | undefined;
110
+ /** Rows for a selected element row. */
111
+ elementRows?(extra: Record<string, unknown>): PluginRow[];
112
+ }
113
+ /** Replaced rather than added twice, so a module evaluated in two bundles does not double. */
114
+ export declare function registerDevtoolsPlugin(plugin: DevtoolsPlugin): void;
115
+ export declare function devtoolsPlugins(): readonly DevtoolsPlugin[];
116
+ export declare function annotationOf(node: RNode, name: string): unknown;
117
+ export declare function pluginRows(node: RNode, graph: Map<number, RNode>): PluginRow[];
118
+ export declare function pluginElementRows(extra?: Record<string, unknown>): PluginRow[];
119
+ export declare function pluginElementBadge(extra?: Record<string, unknown>): string | undefined;
120
+ export declare function pluginBadge(node: RNode): string | undefined;
121
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/dist/plugin.js ADDED
@@ -0,0 +1,74 @@
1
+ const plugins = [];
2
+ /** Replaced rather than added twice, so a module evaluated in two bundles does not double. */
3
+ export function registerDevtoolsPlugin(plugin) {
4
+ const at = plugins.findIndex((p) => p.name === plugin.name);
5
+ if (at === -1)
6
+ plugins.push(plugin);
7
+ else
8
+ plugins[at] = plugin;
9
+ }
10
+ export function devtoolsPlugins() {
11
+ return plugins;
12
+ }
13
+ export function annotationOf(node, name) {
14
+ return node.extra?.[name];
15
+ }
16
+ export function pluginRows(node, graph) {
17
+ const out = [];
18
+ for (const plugin of plugins) {
19
+ if (!plugin.rows)
20
+ continue;
21
+ // A plugin with a bug is not a reason for the panel to stop.
22
+ try {
23
+ out.push(...plugin.rows(node, graph));
24
+ }
25
+ catch {
26
+ /* ignored */
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+ export function pluginElementRows(extra) {
32
+ if (!extra)
33
+ return [];
34
+ const out = [];
35
+ for (const plugin of plugins) {
36
+ if (!plugin.elementRows)
37
+ continue;
38
+ try {
39
+ out.push(...plugin.elementRows(extra));
40
+ }
41
+ catch {
42
+ /* ignored */
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+ export function pluginElementBadge(extra) {
48
+ if (!extra)
49
+ return undefined;
50
+ for (const plugin of plugins) {
51
+ try {
52
+ const badge = plugin.elementBadge?.(extra);
53
+ if (badge)
54
+ return badge;
55
+ }
56
+ catch {
57
+ /* ignored */
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ export function pluginBadge(node) {
63
+ for (const plugin of plugins) {
64
+ try {
65
+ const badge = plugin.badge?.(node);
66
+ if (badge)
67
+ return badge;
68
+ }
69
+ catch {
70
+ /* ignored */
71
+ }
72
+ }
73
+ return undefined;
74
+ }
@@ -0,0 +1,10 @@
1
+ import { type DevtoolsPlugin } from '../plugin.js';
2
+ export declare const NAME = "context";
3
+ /** What lands on a component under this plugin's name. */
4
+ export interface ContextNote {
5
+ name: string;
6
+ kind: 'provide' | 'read';
7
+ value?: string;
8
+ }
9
+ export declare const contextPlugin: DevtoolsPlugin;
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/plugins/context.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAI3E,eAAO,MAAM,IAAI,YAAY,CAAC;AAE9B,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,aAAa,EAAE,cAuE3B,CAAC"}
@@ -0,0 +1,80 @@
1
+ // Which components provide a context, and which read it.
2
+ //
3
+ // The graph cannot show this on its own. A provider stamps a value on the owner chain and a
4
+ // reader walks up looking for it, so neither is a dependency and no edge is ever made. The
5
+ // two components end up as unrelated cards even though one feeds the other.
6
+ import { registerDevtoolsPlugin } from '../plugin.js';
7
+ import { shorten } from '../format.js';
8
+ export const NAME = 'context';
9
+ export const contextPlugin = {
10
+ name: NAME,
11
+ collect(ctx) {
12
+ /** One node per context, however many components touch it. */
13
+ const nodes = new Map();
14
+ ctx.onContext((report) => {
15
+ if (report.owner === undefined)
16
+ return;
17
+ let node = nodes.get(report.id);
18
+ if (node === undefined) {
19
+ // `createContext` names its symbol `context` unless told otherwise, so an unnamed
20
+ // one is said by where it came from rather than drawn seven times identically.
21
+ const label = report.name === 'context' && report.ownerLabel
22
+ ? `${report.ownerLabel} context`
23
+ : report.name;
24
+ node = ctx.add({
25
+ label,
26
+ kind: 'context',
27
+ // A context is created at module scope with nothing running, so the only line it
28
+ // can carry is the one that provided or read it.
29
+ ...(report.at ? { source: report.at } : {}),
30
+ // The router's own contexts are the framework's, and belong in its card rather
31
+ // than beside the ones the application wrote.
32
+ authored: report.at?.external !== true,
33
+ // Under whoever provided it. A context read with no provider above it belongs to
34
+ // the reader, which is the honest place for a default value.
35
+ createdIn: report.owner,
36
+ // Short on the card, whole in the inspector, the way every other node works.
37
+ ...(report.value !== undefined
38
+ ? { value: shorten(report.value), detail: report.value }
39
+ : {}),
40
+ });
41
+ nodes.set(report.id, node);
42
+ }
43
+ // A component that provides is a provider. Only this plugin can tell, and a card
44
+ // reading `component` says nothing about why it is there.
45
+ if (report.kind === 'provide')
46
+ ctx.retype(report.owner, 'provider');
47
+ const held = ctx.annotation(report.owner) ?? [];
48
+ const already = held.some((n) => n.name === report.name && n.kind === report.kind);
49
+ if (already)
50
+ return;
51
+ held.push({
52
+ name: report.name,
53
+ kind: report.kind,
54
+ ...(report.value !== undefined ? { value: report.value } : {}),
55
+ });
56
+ ctx.annotate(report.owner, held);
57
+ });
58
+ },
59
+ rows(node) {
60
+ const notes = node.extra?.[NAME];
61
+ if (!notes?.length)
62
+ return [];
63
+ return notes.map((note) => ({
64
+ label: note.kind === 'provide' ? `provides ${note.name}` : `reads ${note.name}`,
65
+ ...(note.value !== undefined ? { value: note.value } : {}),
66
+ }));
67
+ },
68
+ badge(node) {
69
+ const notes = node.extra?.[NAME];
70
+ if (!notes?.length)
71
+ return undefined;
72
+ const given = notes.filter((n) => n.kind === 'provide');
73
+ if (given.length === 1)
74
+ return `provides ${given[0].name}`;
75
+ if (given.length > 1)
76
+ return `provides ${given.length}`;
77
+ return notes.length === 1 ? `reads ${notes[0].name}` : `reads ${notes.length}`;
78
+ },
79
+ };
80
+ registerDevtoolsPlugin(contextPlugin);
@@ -0,0 +1,25 @@
1
+ import { type DevtoolsPlugin } from '../plugin.js';
2
+ export declare const NAME = "directives";
3
+ /** The notes on an element of a description, for a panel drawing element rows. */
4
+ export declare function directivesOn(extra?: Record<string, unknown>): DirectiveNote[];
5
+ /** A short word for a badge. The whole expression an inline ref was written as is not one. */
6
+ export declare function directiveBadge(notes: DirectiveNote[]): string | undefined;
7
+ /** A row per directive, for the inspector. */
8
+ export declare function directiveRows(notes: DirectiveNote[]): {
9
+ label: string;
10
+ value?: string;
11
+ at?: DirectiveNote['at'];
12
+ }[];
13
+ /** What lands on a node under this plugin's name. */
14
+ export interface DirectiveNote {
15
+ kind: string;
16
+ name: string;
17
+ element: string;
18
+ at?: {
19
+ file?: string;
20
+ line?: number;
21
+ column?: number;
22
+ };
23
+ }
24
+ export declare const directivesPlugin: DevtoolsPlugin;
25
+ //# sourceMappingURL=directives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directives.d.ts","sourceRoot":"","sources":["../../src/plugins/directives.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0B,KAAK,cAAc,EAAwB,MAAM,cAAc,CAAC;AAGjG,eAAO,MAAM,IAAI,eAAe,CAAC;AAEjC,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,EAAE,CAE7E;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAA;CAAE,EAAE,CAMnH;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD;AAKD,eAAO,MAAM,gBAAgB,EAAE,cAiD9B,CAAC"}
@@ -0,0 +1,78 @@
1
+ // What directives an element was given.
2
+ //
3
+ // The graph already shows that something writes a class or a style, because the dom reports
4
+ // the binding. It does not show which directive asked for it, and `use:` is invisible
5
+ // entirely since a custom directive is a function call with no reactive node behind it.
6
+ import { registerDevtoolsPlugin } from '../plugin.js';
7
+ export const NAME = 'directives';
8
+ /** The notes on an element of a description, for a panel drawing element rows. */
9
+ export function directivesOn(extra) {
10
+ return extra?.[NAME] ?? [];
11
+ }
12
+ /** A short word for a badge. The whole expression an inline ref was written as is not one. */
13
+ export function directiveBadge(notes) {
14
+ if (!notes.length)
15
+ return undefined;
16
+ const first = notes[0];
17
+ const short = first.kind === 'ref' ? 'ref' : label(first);
18
+ return notes.length > 1 ? `${short} +${notes.length - 1}` : short;
19
+ }
20
+ /** A row per directive, for the inspector. */
21
+ export function directiveRows(notes) {
22
+ return notes.map((note) => ({
23
+ label: label(note),
24
+ value: note.element,
25
+ ...(note.at ? { at: note.at } : {}),
26
+ }));
27
+ }
28
+ const label = (note) => note.kind === 'ref' ? `ref ${note.name}` : `${note.kind}:${note.name}`;
29
+ export const directivesPlugin = {
30
+ name: NAME,
31
+ collect(ctx) {
32
+ ctx.onDirective((report) => {
33
+ // On the element, which is what the directive was applied to. The component that ran
34
+ // is only where the call happened.
35
+ if (!report.target)
36
+ return;
37
+ const held = ctx.elementAnnotation(report.target) ?? [];
38
+ // A render effect re-runs, so the same directive arrives more than once.
39
+ const already = held.some((n) => n.kind === report.kind && n.name === report.name);
40
+ if (already)
41
+ return;
42
+ held.push({
43
+ kind: report.kind,
44
+ name: report.name,
45
+ element: report.element,
46
+ ...(report.at ? { at: report.at } : {}),
47
+ });
48
+ ctx.annotateElement(report.target, held);
49
+ });
50
+ },
51
+ rows(node) {
52
+ const notes = node.extra?.[NAME];
53
+ if (!notes?.length)
54
+ return [];
55
+ return notes.map((note) => ({
56
+ label: label(note),
57
+ value: note.element,
58
+ ...(note.at ? { at: note.at } : {}),
59
+ }));
60
+ },
61
+ elementRows(extra) {
62
+ return directiveRows(directivesOn(extra));
63
+ },
64
+ elementBadge(extra) {
65
+ return directiveBadge(directivesOn(extra));
66
+ },
67
+ badge(node) {
68
+ const notes = node.extra?.[NAME];
69
+ if (!notes?.length)
70
+ return undefined;
71
+ // A badge sits beside a label, so it says which directive and not the whole expression
72
+ // an inline ref was written as.
73
+ const first = notes[0];
74
+ const short = first.kind === 'ref' ? 'ref' : label(first);
75
+ return notes.length > 1 ? `${short} +${notes.length - 1}` : short;
76
+ },
77
+ };
78
+ registerDevtoolsPlugin(directivesPlugin);
@@ -0,0 +1,7 @@
1
+ import './directives.js';
2
+ import './context.js';
3
+ export { directivesPlugin, NAME as DIRECTIVES } from './directives.js';
4
+ export type { DirectiveNote } from './directives.js';
5
+ export { contextPlugin, NAME as CONTEXT } from './context.js';
6
+ export type { ContextNote } from './context.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAKA,OAAO,iBAAiB,CAAC;AACzB,OAAO,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,9 @@
1
+ // The plugins that ship with devtools.
2
+ //
3
+ // Importing this registers them. Both halves of a plugin live in one module, so an agent in
4
+ // the page and a panel elsewhere import the same thing and each uses the half it can run.
5
+ // A host that wants none of them imports `../plugin.js` directly and registers its own.
6
+ import './directives.js';
7
+ import './context.js';
8
+ export { directivesPlugin, NAME as DIRECTIVES } from './directives.js';
9
+ export { contextPlugin, NAME as CONTEXT } from './context.js';
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Where a reactive node was created.
3
+ *
4
+ * The compiler marks each creation site in development (`sourceLocations`) by calling the
5
+ * global installed here, just before the statement that makes the node. The instrumented
6
+ * primitive takes the mark and keeps it on the graph node.
7
+ *
8
+ * A single slot, not a stack: in `signal(compute(signal(1)))` the inner call takes the mark
9
+ * and the outer gets none, rather than claiming a location that is not its own.
10
+ */
11
+ /** Where in the author's source a node was created. Every field is optional. */
12
+ export interface ReactiveSourceLocation {
13
+ file?: string;
14
+ line?: number;
15
+ column?: number;
16
+ /** The source text this came from, when the compiler recorded it. Refs carry it. */
17
+ name?: string;
18
+ /**
19
+ * The node came from a dependency rather than from the project. Only ever set when the
20
+ * compiler was told to mark dependencies too; otherwise they carry no location at all.
21
+ */
22
+ external?: boolean;
23
+ /**
24
+ * Where this is in the compiled output, when the location above came from a source map.
25
+ *
26
+ * Both are worth having. The original says which component and which file; the compiled one
27
+ * says which of that component's bindings, because a template's holes all map back to the
28
+ * position the template opens at — one entry per emitted root is all the map has. Until
29
+ * that changes, the compiled line is the only thing that tells two bindings apart.
30
+ */
31
+ compiled?: {
32
+ line?: number;
33
+ column?: number;
34
+ };
35
+ }
36
+ /** A location, plus the variable name the source gave it when there was one. */
37
+ export interface ReactiveSourceMetadata {
38
+ name?: string;
39
+ location?: ReactiveSourceLocation;
40
+ }
41
+ /**
42
+ * Where each component was declared, by name.
43
+ *
44
+ * The graph sees the memo the compiler wraps a component in, and all that memo carries back
45
+ * is the name stamped on what the component returned — so name is the only key available.
46
+ * Two components sharing a name are recorded as ambiguous and reported as unknown, since a
47
+ * location pointing at the wrong file is worse than none.
48
+ */
49
+ /**
50
+ * Record where the next node is about to be created. The compiler calls this; call it
51
+ * yourself only for a node the compiler cannot see, immediately before the primitive.
52
+ */
53
+ export declare function markReactiveSource(file?: string, line?: number, column?: number, name?: string): void;
54
+ /** Record where a component was declared. The compiler calls this. */
55
+ export declare function markComponentSource(name: string, file?: string, line?: number, column?: number): void;
56
+ /** Where a component of this name was declared, when exactly one was. */
57
+ export declare function componentSource(name: string): ReactiveSourceLocation | undefined;
58
+ /**
59
+ * Reading keeps it for the rest of the turn.
60
+ *
61
+ * One statement can make several nodes: `createStore({ a, b })` builds a signal per key
62
+ * inside the framework, and all of them belong to the line that asked for the store. So the
63
+ * mark survives the read and is dropped on the next microtask, which is the end of the
64
+ * statement that left it as far as anything synchronous is concerned.
65
+ */
66
+ export declare function takeReactiveSource(): ReactiveSourceMetadata | null;
67
+ /** Where the props of this object were written, when the compiler recorded them. */
68
+ export declare function propsLocations(props: unknown): Record<string, [number, number]> | undefined;
69
+ /** The hole an accessor came from, when the compiler marked it. */
70
+ export declare function holeLocation(accessor: unknown): ReactiveSourceLocation | undefined;
71
+ export declare function installSourceMarker(): void;
72
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AAgCD;;;;;;;GAOG;AAGH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAQrG;AAYD,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAQrG;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAEhF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,sBAAsB,GAAG,IAAI,CAelE;AA4CD,oFAAoF;AACpF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAE9C;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,sBAAsB,GAAG,SAAS,CAGlF;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C"}