react-code-locator 0.1.5 → 0.1.7

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/vite.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { Options } from '@vitejs/plugin-react';
2
1
  import { PluginOption } from 'vite';
3
2
  import { LocatorOptions } from './client.js';
3
+ export { BabelInjectComponentSourceOptions, babelInjectComponentSource } from './babelInjectComponentSource.js';
4
+ import '@babel/core';
4
5
 
5
6
  type ElementLocatorReactOptions = {
6
7
  command?: "serve" | "build";
7
- react?: Options;
8
8
  locator?: LocatorOptions;
9
9
  injectClient?: boolean;
10
10
  };
package/dist/vite.js CHANGED
@@ -1,5 +1,46 @@
1
1
  // src/elementLocatorReact.ts
2
- import react from "@vitejs/plugin-react";
2
+ var VIRTUAL_CLIENT_MODULE_ID = "virtual:react-code-locator/client";
3
+ var RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\0${VIRTUAL_CLIENT_MODULE_ID}`;
4
+ function createClientInjector(locatorOptions = {}) {
5
+ const serialized = JSON.stringify(locatorOptions);
6
+ return {
7
+ name: "element-locator-client-injector",
8
+ apply: "serve",
9
+ resolveId(id) {
10
+ if (id === VIRTUAL_CLIENT_MODULE_ID) {
11
+ return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;
12
+ }
13
+ return null;
14
+ },
15
+ load(id) {
16
+ if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {
17
+ return null;
18
+ }
19
+ return `
20
+ import { enableReactComponentJump } from "react-code-locator/client";
21
+
22
+ enableReactComponentJump(${serialized});
23
+ `;
24
+ },
25
+ transformIndexHtml() {
26
+ return [
27
+ {
28
+ tag: "script",
29
+ attrs: {
30
+ type: "module"
31
+ },
32
+ children: `import "${VIRTUAL_CLIENT_MODULE_ID}";`,
33
+ injectTo: "head"
34
+ }
35
+ ];
36
+ }
37
+ };
38
+ }
39
+ function elementLocatorReact(options = {}) {
40
+ const { command = "serve", locator = {}, injectClient = true } = options;
41
+ const isServe = command === "serve";
42
+ return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);
43
+ }
3
44
 
4
45
  // src/babelInjectComponentSource.ts
5
46
  import path from "path";
@@ -12,6 +53,21 @@ var SOURCE_PROP = "__componentSourceLoc";
12
53
  function isComponentName(name) {
13
54
  return /^[A-Z]/.test(name);
14
55
  }
56
+ function isSupportedComponentInit(node) {
57
+ if (!node) {
58
+ return false;
59
+ }
60
+ if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {
61
+ return true;
62
+ }
63
+ if (!t.isCallExpression(node)) {
64
+ return false;
65
+ }
66
+ if (t.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
67
+ return true;
68
+ }
69
+ return t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: "React" }) && t.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
70
+ }
15
71
  function getSourceValue(state, loc) {
16
72
  const filename = state.file?.opts?.filename;
17
73
  if (!filename || !loc) {
@@ -53,7 +109,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
53
109
  if (!declarator.init) {
54
110
  return [];
55
111
  }
56
- if (!t.isArrowFunctionExpression(declarator.init) && !t.isFunctionExpression(declarator.init)) {
112
+ if (!isSupportedComponentInit(declarator.init)) {
57
113
  return [];
58
114
  }
59
115
  const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
@@ -67,13 +123,17 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
67
123
  insertAfterPath.insertAfter(assignments);
68
124
  }
69
125
  }
70
- function babelInjectComponentSource() {
126
+ function babelInjectComponentSource(options = {}) {
127
+ const { injectJsxSource = false, injectComponentSource = true } = options;
71
128
  return {
72
129
  name: "babel-inject-component-source",
73
130
  visitor: {
74
131
  JSXOpeningElement(pathNode, state) {
132
+ if (!injectJsxSource) {
133
+ return;
134
+ }
75
135
  const hasSourceProp = pathNode.node.attributes.some(
76
- (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "__source"
136
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP
77
137
  );
78
138
  if (hasSourceProp) {
79
139
  return;
@@ -85,18 +145,15 @@ function babelInjectComponentSource() {
85
145
  }
86
146
  pathNode.node.attributes.push(
87
147
  t.jsxAttribute(
88
- t.jsxIdentifier("__source"),
89
- t.jsxExpressionContainer(
90
- t.objectExpression([
91
- t.objectProperty(t.identifier("fileName"), t.stringLiteral(filename)),
92
- t.objectProperty(t.identifier("lineNumber"), t.numericLiteral(loc.line)),
93
- t.objectProperty(t.identifier("columnNumber"), t.numericLiteral(loc.column + 1))
94
- ])
95
- )
148
+ t.jsxIdentifier(SOURCE_PROP),
149
+ t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\/g, "/")}:${loc.line}:${loc.column + 1}`)
96
150
  )
97
151
  );
98
152
  },
99
153
  Program(programPath, state) {
154
+ if (!injectComponentSource) {
155
+ return;
156
+ }
100
157
  const seen = /* @__PURE__ */ new Set();
101
158
  for (const childPath of programPath.get("body")) {
102
159
  if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {
@@ -112,61 +169,8 @@ function babelInjectComponentSource() {
112
169
  }
113
170
  };
114
171
  }
115
-
116
- // src/elementLocatorReact.ts
117
- function withLocatorBabel(reactOptions = {}) {
118
- const baseBabel = reactOptions.babel;
119
- if (typeof baseBabel === "function") {
120
- return {
121
- ...reactOptions,
122
- babel(id, options) {
123
- const result = baseBabel(id, options);
124
- const resolved2 = result ?? {};
125
- const plugins = [...resolved2.plugins ?? [], babelInjectComponentSource];
126
- return {
127
- ...resolved2,
128
- plugins
129
- };
130
- }
131
- };
132
- }
133
- const resolved = baseBabel ?? {};
134
- return {
135
- ...reactOptions,
136
- babel: {
137
- ...resolved,
138
- plugins: [...resolved.plugins ?? [], babelInjectComponentSource]
139
- }
140
- };
141
- }
142
- function createClientInjector(locatorOptions = {}) {
143
- const serialized = JSON.stringify(locatorOptions);
144
- return {
145
- name: "element-locator-client-injector",
146
- apply: "serve",
147
- transformIndexHtml() {
148
- return [
149
- {
150
- tag: "script",
151
- attrs: {
152
- type: "module"
153
- },
154
- children: `import { enableReactComponentJump } from "react-code-locator/client"; enableReactComponentJump(${serialized});`,
155
- injectTo: "head"
156
- }
157
- ];
158
- }
159
- };
160
- }
161
- function elementLocatorReact(options = {}) {
162
- const { command = "serve", react: reactOptions, locator = {}, injectClient = true } = options;
163
- const isServe = command === "serve";
164
- return [
165
- react(isServe ? withLocatorBabel(reactOptions) : reactOptions),
166
- isServe && injectClient ? createClientInjector(locator) : null
167
- ].filter(Boolean);
168
- }
169
172
  export {
173
+ babelInjectComponentSource,
170
174
  elementLocatorReact,
171
175
  elementLocatorReact as reactComponentJump
172
176
  };
package/dist/vite.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import type { TransformOptions, PluginItem } from \"@babel/core\";\nimport react, { type Options as ReactOptions } from \"@vitejs/plugin-react\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nimport { type LocatorOptions } from \"./runtime\";\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n react?: ReactOptions;\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction withLocatorBabel(reactOptions: ReactOptions = {}): ReactOptions {\n const baseBabel = reactOptions.babel;\n\n if (typeof baseBabel === \"function\") {\n return {\n ...reactOptions,\n babel(id, options) {\n const result = baseBabel(id, options);\n const resolved = (result ?? {}) as TransformOptions;\n const plugins = [...(resolved.plugins ?? []), babelInjectComponentSource as PluginItem];\n\n return {\n ...resolved,\n plugins,\n };\n },\n };\n }\n\n const resolved = (baseBabel ?? {}) as TransformOptions;\n return {\n ...reactOptions,\n babel: {\n ...resolved,\n plugins: [...(resolved.plugins ?? []), babelInjectComponentSource as PluginItem],\n },\n };\n}\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import { enableReactComponentJump } from \"react-code-locator/client\"; enableReactComponentJump(${serialized});`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", react: reactOptions, locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [\n react(isServe ? withLocatorBabel(reactOptions) : reactOptions),\n isServe && injectClient ? createClientInjector(locator) : null,\n ].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!t.isArrowFunctionExpression(declarator.init) && !t.isFunctionExpression(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(): PluginObj<BabelState> {\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === \"__source\",\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(\"__source\"),\n t.jsxExpressionContainer(\n t.objectExpression([\n t.objectProperty(t.identifier(\"fileName\"), t.stringLiteral(filename)),\n t.objectProperty(t.identifier(\"lineNumber\"), t.numericLiteral(loc.line)),\n t.objectProperty(t.identifier(\"columnNumber\"), t.numericLiteral(loc.column + 1)),\n ]),\n ),\n ),\n );\n },\n Program(programPath, state) {\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":";AACA,OAAO,WAA6C;;;ACDpD,OAAO,UAAU;AACjB,SAAS,SAAS,SAAwC;;;ACDnD,IAAM,cAAc;;;ADY3B,SAAS,gBAAgB,MAAc;AACrC,SAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,eAAe,OAAmB,KAA0D;AACnG,QAAM,WAAW,MAAM,MAAM,MAAM;AACnC,MAAI,CAAC,YAAY,CAAC,KAAK;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,KAAK,SAAS,QAAQ,IAAI,GAAG,QAAQ,EAAE,QAAQ,OAAO,GAAG;AACzE,SAAO,GAAG,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,CAAC;AACjD;AAEA,SAAS,gBAAgB,MAAc,aAAqB;AAC1D,SAAO,EAAE;AAAA,IACP,EAAE;AAAA,MACA;AAAA,MACA,EAAE,iBAAiB,EAAE,WAAW,IAAI,GAAG,EAAE,WAAW,WAAW,CAAC;AAAA,MAChE,EAAE,cAAc,WAAW;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,SAAS,iBACP,iBACA,iBACA,OACA,MACA;AACA,MAAI,gBAAgB,sBAAsB,KAAK,gBAAgB,mBAAmB,GAAG;AACnF,UAAM,OAAO,gBAAgB,KAAK,IAAI;AACtC,QAAI,CAAC,QAAQ,CAAC,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG;AACrD;AAAA,IACF;AAEA,UAAM,cAAc,eAAe,OAAO,gBAAgB,KAAK,KAAK,KAAK;AACzE,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,SAAK,IAAI,IAAI;AACb,oBAAgB,YAAY,gBAAgB,MAAM,WAAW,CAAC;AAC9D;AAAA,EACF;AAEA,MAAI,CAAC,gBAAgB,sBAAsB,GAAG;AAC5C;AAAA,EACF;AAEA,QAAM,cAAc,gBAAgB,KAAK,aAAa,QAAQ,CAAC,eAAe;AAC5E,QAAI,CAAC,EAAE,aAAa,WAAW,EAAE,KAAK,CAAC,gBAAgB,WAAW,GAAG,IAAI,KAAK,KAAK,IAAI,WAAW,GAAG,IAAI,GAAG;AAC1G,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,CAAC,WAAW,MAAM;AACpB,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,CAAC,EAAE,0BAA0B,WAAW,IAAI,KAAK,CAAC,EAAE,qBAAqB,WAAW,IAAI,GAAG;AAC7F,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,cAAc,eAAe,OAAO,WAAW,KAAK,SAAS,WAAW,KAAK,KAAK,KAAK;AAC7F,QAAI,CAAC,aAAa;AAChB,aAAO,CAAC;AAAA,IACV;AAEA,SAAK,IAAI,WAAW,GAAG,IAAI;AAC3B,WAAO,CAAC,gBAAgB,WAAW,GAAG,MAAM,WAAW,CAAC;AAAA,EAC1D,CAAC;AAED,MAAI,YAAY,SAAS,GAAG;AAC1B,oBAAgB,YAAY,WAAW;AAAA,EACzC;AACF;AAEO,SAAS,6BAAoD;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,MACP,kBAAkB,UAAU,OAAO;AACjC,cAAM,gBAAgB,SAAS,KAAK,WAAW;AAAA,UAC7C,CAAC,SAAS,EAAE,eAAe,IAAI,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS;AAAA,QACzF;AACA,YAAI,eAAe;AACjB;AAAA,QACF;AAEA,cAAM,WAAW,MAAM,MAAM,MAAM;AACnC,cAAM,MAAM,SAAS,KAAK,KAAK;AAC/B,YAAI,CAAC,YAAY,CAAC,KAAK;AACrB;AAAA,QACF;AAEA,iBAAS,KAAK,WAAW;AAAA,UACvB,EAAE;AAAA,YACA,EAAE,cAAc,UAAU;AAAA,YAC1B,EAAE;AAAA,cACA,EAAE,iBAAiB;AAAA,gBACjB,EAAE,eAAe,EAAE,WAAW,UAAU,GAAG,EAAE,cAAc,QAAQ,CAAC;AAAA,gBACpE,EAAE,eAAe,EAAE,WAAW,YAAY,GAAG,EAAE,eAAe,IAAI,IAAI,CAAC;AAAA,gBACvE,EAAE,eAAe,EAAE,WAAW,cAAc,GAAG,EAAE,eAAe,IAAI,SAAS,CAAC,CAAC;AAAA,cACjF,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,QAAQ,aAAa,OAAO;AAC1B,cAAM,OAAO,oBAAI,IAAY;AAE7B,mBAAW,aAAa,YAAY,IAAI,MAAM,GAAG;AAC/C,cAAI,UAAU,yBAAyB,KAAK,UAAU,2BAA2B,GAAG;AAClF,kBAAM,kBAAkB,UAAU,IAAI,aAAa;AACnD,gBAAI,CAAC,MAAM,QAAQ,eAAe,KAAK,gBAAgB,MAAM;AAC3D,+BAAiB,iBAAiB,WAAW,OAAO,IAAI;AAAA,YAC1D;AACA;AAAA,UACF;AAEA,2BAAiB,WAAW,WAAW,OAAO,IAAI;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AD5HA,SAAS,iBAAiB,eAA6B,CAAC,GAAiB;AACvE,QAAM,YAAY,aAAa;AAE/B,MAAI,OAAO,cAAc,YAAY;AACnC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,MAAM,IAAI,SAAS;AACjB,cAAM,SAAS,UAAU,IAAI,OAAO;AACpC,cAAMA,YAAY,UAAU,CAAC;AAC7B,cAAM,UAAU,CAAC,GAAIA,UAAS,WAAW,CAAC,GAAI,0BAAwC;AAEtF,eAAO;AAAA,UACL,GAAGA;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAY,aAAa,CAAC;AAChC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,CAAC,GAAI,SAAS,WAAW,CAAC,GAAI,0BAAwC;AAAA,IACjF;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,iBAAiC,CAAC,GAAW;AACzE,QAAM,aAAa,KAAK,UAAU,cAAc;AAEhD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,qBAAqB;AACnB,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,YACL,MAAM;AAAA,UACR;AAAA,UACA,UAAU,kGAAkG,UAAU;AAAA,UACtH,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,oBAAoB,UAAsC,CAAC,GAAmB;AAC5F,QAAM,EAAE,UAAU,SAAS,OAAO,cAAc,UAAU,CAAC,GAAG,eAAe,KAAK,IAAI;AACtF,QAAM,UAAU,YAAY;AAE5B,SAAO;AAAA,IACL,MAAM,UAAU,iBAAiB,YAAY,IAAI,YAAY;AAAA,IAC7D,WAAW,eAAe,qBAAqB,OAAO,IAAI;AAAA,EAC5D,EAAE,OAAO,OAAO;AAClB;","names":["resolved"]}
1
+ {"version":3,"sources":["../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import type { Plugin, PluginOption } from \"vite\";\nimport { type LocatorOptions } from \"./runtime\";\n\nconst VIRTUAL_CLIENT_MODULE_ID = \"virtual:react-code-locator/client\";\nconst RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\\0${VIRTUAL_CLIENT_MODULE_ID}`;\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n resolveId(id) {\n if (id === VIRTUAL_CLIENT_MODULE_ID) {\n return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;\n }\n\n return null;\n },\n load(id) {\n if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {\n return null;\n }\n\n return `\n import { enableReactComponentJump } from \"react-code-locator/client\";\n\n enableReactComponentJump(${serialized});\n `;\n },\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import \"${VIRTUAL_CLIENT_MODULE_ID}\";`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":[]}
@@ -48,7 +48,22 @@ function getSourceFromType(type) {
48
48
  const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];
49
49
  return typeof source === "string" ? source : null;
50
50
  }
51
- function resolveSourceFromFiber(fiber) {
51
+ function getSourceFromProps(props) {
52
+ const source = props?.[SOURCE_PROP];
53
+ return typeof source === "string" ? source : null;
54
+ }
55
+ function resolveJsxSourceFromFiber(fiber) {
56
+ let current = fiber;
57
+ while (current) {
58
+ const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps);
59
+ if (source) {
60
+ return source;
61
+ }
62
+ current = current.return ?? null;
63
+ }
64
+ return null;
65
+ }
66
+ function resolveComponentSourceFromFiber(fiber) {
52
67
  let current = fiber;
53
68
  while (current) {
54
69
  const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
@@ -76,14 +91,14 @@ function locateComponentSource(target) {
76
91
  if (!fiber) {
77
92
  return null;
78
93
  }
79
- const debugSource = getDebugSource(fiber);
80
- if (debugSource) {
94
+ const jsxSource = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(fiber);
95
+ if (jsxSource) {
81
96
  return {
82
- source: debugSource,
97
+ source: jsxSource,
83
98
  mode: "jsx"
84
99
  };
85
100
  }
86
- const componentSource = resolveSourceFromFiber(fiber);
101
+ const componentSource = resolveComponentSourceFromFiber(fiber);
87
102
  if (!componentSource) {
88
103
  return null;
89
104
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/runtime.ts","../src/webpackRuntimeEntry.ts"],"sourcesContent":["export const SOURCE_PROP = \"__componentSourceLoc\";\n\n","import { SOURCE_PROP } from \"./constants\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: \"jsx\" | \"component\";\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nexport function locateComponentSource(target: EventTarget | null): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const debugSource = getDebugSource(fiber);\n if (debugSource) {\n return {\n source: debugSource,\n mode: \"jsx\",\n };\n }\n\n const componentSource = resolveSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode: \"component\",\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n };\n}\n","import { enableReactComponentJump } from \"./runtime\";\n\nif (typeof document !== \"undefined\") {\n enableReactComponentJump();\n}\n\n"],"mappings":"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","names":["source"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/runtime.ts","../src/webpackRuntimeEntry.ts"],"sourcesContent":["export const SOURCE_PROP = \"__componentSourceLoc\";\n\n","import { SOURCE_PROP } from \"./constants\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n pendingProps?: Record<string, unknown> | null;\n memoizedProps?: Record<string, unknown> | null;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: \"jsx\" | \"component\";\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction getSourceFromProps(props: Record<string, unknown> | null | undefined) {\n const source = props?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveJsxSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction resolveComponentSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nexport function locateComponentSource(target: EventTarget | null): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const jsxSource = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(fiber);\n if (jsxSource) {\n return {\n source: jsxSource,\n mode: \"jsx\",\n };\n }\n\n const componentSource = resolveComponentSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode: \"component\",\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n };\n}\n","import { enableReactComponentJump } from \"./runtime\";\n\nif (typeof document !== \"undefined\") {\n enableReactComponentJump();\n}\n\n"],"mappings":"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","names":["source"]}
@@ -46,7 +46,22 @@ function getSourceFromType(type) {
46
46
  const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];
47
47
  return typeof source === "string" ? source : null;
48
48
  }
49
- function resolveSourceFromFiber(fiber) {
49
+ function getSourceFromProps(props) {
50
+ const source = props?.[SOURCE_PROP];
51
+ return typeof source === "string" ? source : null;
52
+ }
53
+ function resolveJsxSourceFromFiber(fiber) {
54
+ let current = fiber;
55
+ while (current) {
56
+ const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps);
57
+ if (source) {
58
+ return source;
59
+ }
60
+ current = current.return ?? null;
61
+ }
62
+ return null;
63
+ }
64
+ function resolveComponentSourceFromFiber(fiber) {
50
65
  let current = fiber;
51
66
  while (current) {
52
67
  const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
@@ -74,14 +89,14 @@ function locateComponentSource(target) {
74
89
  if (!fiber) {
75
90
  return null;
76
91
  }
77
- const debugSource = getDebugSource(fiber);
78
- if (debugSource) {
92
+ const jsxSource = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(fiber);
93
+ if (jsxSource) {
79
94
  return {
80
- source: debugSource,
95
+ source: jsxSource,
81
96
  mode: "jsx"
82
97
  };
83
98
  }
84
- const componentSource = resolveSourceFromFiber(fiber);
99
+ const componentSource = resolveComponentSourceFromFiber(fiber);
85
100
  if (!componentSource) {
86
101
  return null;
87
102
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/runtime.ts","../src/webpackRuntimeEntry.ts"],"sourcesContent":["export const SOURCE_PROP = \"__componentSourceLoc\";\n\n","import { SOURCE_PROP } from \"./constants\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: \"jsx\" | \"component\";\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nexport function locateComponentSource(target: EventTarget | null): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const debugSource = getDebugSource(fiber);\n if (debugSource) {\n return {\n source: debugSource,\n mode: \"jsx\",\n };\n }\n\n const componentSource = resolveSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode: \"component\",\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n };\n}\n","import { enableReactComponentJump } from \"./runtime\";\n\nif (typeof document !== \"undefined\") {\n enableReactComponentJump();\n}\n\n"],"mappings":"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","names":["source"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/runtime.ts","../src/webpackRuntimeEntry.ts"],"sourcesContent":["export const SOURCE_PROP = \"__componentSourceLoc\";\n\n","import { SOURCE_PROP } from \"./constants\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n pendingProps?: Record<string, unknown> | null;\n memoizedProps?: Record<string, unknown> | null;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: \"jsx\" | \"component\";\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction getSourceFromProps(props: Record<string, unknown> | null | undefined) {\n const source = props?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveJsxSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction resolveComponentSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nexport function locateComponentSource(target: EventTarget | null): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const jsxSource = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(fiber);\n if (jsxSource) {\n return {\n source: jsxSource,\n mode: \"jsx\",\n };\n }\n\n const componentSource = resolveComponentSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode: \"component\",\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n };\n}\n","import { enableReactComponentJump } from \"./runtime\";\n\nif (typeof document !== \"undefined\") {\n enableReactComponentJump();\n}\n\n"],"mappings":"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","names":["source"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-code-locator",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -13,12 +13,12 @@
13
13
  },
14
14
  "./client": {
15
15
  "types": "./dist/client.d.ts",
16
- "import": "./dist/client.cjs",
16
+ "import": "./dist/client.js",
17
17
  "require": "./dist/client.cjs"
18
18
  },
19
19
  "./babel": {
20
20
  "types": "./dist/babel.d.ts",
21
- "import": "./dist/babel.cjs",
21
+ "import": "./dist/babel.js",
22
22
  "require": "./dist/babel.cjs"
23
23
  },
24
24
  "./vite": {
@@ -42,21 +42,16 @@
42
42
  "typecheck": "tsc --noEmit"
43
43
  },
44
44
  "peerDependencies": {
45
- "@babel/core": ">=7",
46
- "@vitejs/plugin-react": ">=4"
45
+ "@babel/core": ">=7"
47
46
  },
48
47
  "peerDependenciesMeta": {
49
48
  "@babel/core": {
50
49
  "optional": true
51
- },
52
- "@vitejs/plugin-react": {
53
- "optional": true
54
50
  }
55
51
  },
56
52
  "devDependencies": {
57
53
  "@babel/core": "^7.26.0",
58
54
  "@types/node": "^22.13.10",
59
- "@vitejs/plugin-react": "^4.3.4",
60
55
  "tsup": "^8.5.1",
61
56
  "typescript": "^5.8.2",
62
57
  "vite": "^5.4.14"