@inspecto-dev/plugin 0.3.4 → 0.3.6

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 (39) hide show
  1. package/dist/index.cjs +179 -44
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +179 -44
  4. package/dist/index.js.map +1 -1
  5. package/dist/legacy/rspack/index.cjs +108 -18
  6. package/dist/legacy/rspack/index.cjs.map +1 -1
  7. package/dist/legacy/rspack/index.js +108 -18
  8. package/dist/legacy/rspack/index.js.map +1 -1
  9. package/dist/legacy/rspack/loader.cjs +49 -4
  10. package/dist/legacy/rspack/loader.cjs.map +1 -1
  11. package/dist/legacy/rspack/loader.js +49 -4
  12. package/dist/legacy/rspack/loader.js.map +1 -1
  13. package/dist/legacy/webpack4/index.cjs +120 -20
  14. package/dist/legacy/webpack4/index.cjs.map +1 -1
  15. package/dist/legacy/webpack4/index.d.cts +2 -0
  16. package/dist/legacy/webpack4/index.d.ts +2 -0
  17. package/dist/legacy/webpack4/index.js +120 -20
  18. package/dist/legacy/webpack4/index.js.map +1 -1
  19. package/dist/legacy/webpack4/loader.cjs +49 -4
  20. package/dist/legacy/webpack4/loader.cjs.map +1 -1
  21. package/dist/legacy/webpack4/loader.js +49 -4
  22. package/dist/legacy/webpack4/loader.js.map +1 -1
  23. package/dist/rollup.cjs +179 -44
  24. package/dist/rollup.cjs.map +1 -1
  25. package/dist/rollup.js +179 -44
  26. package/dist/rollup.js.map +1 -1
  27. package/dist/rspack.cjs +179 -44
  28. package/dist/rspack.cjs.map +1 -1
  29. package/dist/rspack.js +179 -44
  30. package/dist/rspack.js.map +1 -1
  31. package/dist/vite.cjs +179 -44
  32. package/dist/vite.cjs.map +1 -1
  33. package/dist/vite.js +179 -44
  34. package/dist/vite.js.map +1 -1
  35. package/dist/webpack.cjs +179 -44
  36. package/dist/webpack.cjs.map +1 -1
  37. package/dist/webpack.js +179 -44
  38. package/dist/webpack.js.map +1 -1
  39. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/legacy/webpack4/loader.ts","../../../src/transform/index.ts","../../../src/transform/transform-jsx.ts","../../../src/transform/utils.ts","../../../src/transform/transform-vue.ts"],"sourcesContent":["import { transformRouter } from '../../transform/index'\nimport { shouldTransform } from '../../transform/utils'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\n\nconst injectedClient = false\n\nexport default function legacyWebpack4Loader(this: any, source: string) {\n const id = this.resourcePath\n\n // In Webpack 4, loader-utils is typically used, or we read from this.query\n // since this.getOptions() is a Webpack 5+ API.\n let options: any = {}\n if (typeof this.getOptions === 'function') {\n options = this.getOptions()\n } else if (this.query) {\n if (typeof this.query === 'string') {\n // Very fallback query parsing\n options = {}\n } else {\n options = this.query\n }\n }\n\n // Give default include/exclude options\n const defaultOptions: Required<UnpluginOptions> = {\n include: options.include || [/\\.[jt]sx?$/, /\\.vue$/],\n exclude: options.exclude || [/node_modules/, /\\.html$/],\n hotKey: options.hotKey || 'altKey',\n ...options,\n }\n\n if (!shouldTransform(id, defaultOptions)) {\n return source\n }\n\n const result = transformRouter({\n filePath: id,\n source,\n projectRoot: process.cwd(),\n pluginOptions: defaultOptions,\n })\n\n let finalSource = result?.changed ? result.code : source\n\n // Inject the client dynamically into the first source module we process\n // WARNING: We must NOT use process-level state like `let injectedClient = false`\n // because in webpack-dev-server, files are recompiled and the loader might hit\n // multiple entries or the same entry again on HMR.\n // Instead, we inject it conditionally based on a known module that acts as an entry\n // Webpack uses standard regex matching, so let's match any file that has `react-dom` rendering\n\n const isMainEntry =\n finalSource.includes('react-dom/client') ||\n finalSource.includes('react-dom') ||\n /[/\\\\](index|main)\\.[jt]sx?$/.test(id)\n\n if (isMainEntry && finalSource.indexOf('window.InspectoClient') === -1) {\n const clientPath = options.clientPath || '@inspecto-dev/core'\n // For Webpack 4 and Babel environments, using require() is safer than prepending `import`\n // to a file that might be CommonJS, and it doesn't get hoisted irregularly.\n finalSource =\n `\nif (typeof window !== 'undefined' && !window.InspectoClient) {\n try {\n var __inspecto_core__ = require('${clientPath.replace(/\\\\/g, '\\\\\\\\')}');\n window.InspectoClient = __inspecto_core__.default || __inspecto_core__;\n } catch (e) {\n console.error('[inspecto] core load error', e);\n }\n}\n` + finalSource\n }\n\n if (result && result.changed) {\n this.callback(null, finalSource, result.map)\n return\n }\n\n return finalSource\n}\n","import path from 'node:path'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\nimport { transformJsx } from './transform-jsx.js'\nimport { transformVue } from './transform-vue.js'\nimport { JSX_EXTENSIONS, type TransformResult } from './utils.js'\n\nexport interface RouterOptions {\n filePath: string\n source: string\n projectRoot: string\n pluginOptions: Required<UnpluginOptions>\n}\n\n/**\n * Route a file to the appropriate transform based on extension.\n * Returns null if no transform applies.\n */\nexport function transformRouter(options: RouterOptions): TransformResult | null {\n const { filePath, source, projectRoot, pluginOptions } = options\n const ext = path.extname(filePath).toLowerCase()\n\n if (JSX_EXTENSIONS.has(ext)) {\n return transformJsx({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n // ── Vue SFC ──────────────────────────────────────────────────────────────\n if (ext === '.vue') {\n return transformVue({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n return null\n}\n// Export transforms for testing\nexport { transformJsx, transformVue }\n","import * as parser from '@babel/parser'\nimport traverse_ from '@babel/traverse'\n// Support both ESM default and CommonJS module.exports\nconst traverse =\n typeof traverse_ === 'function' ? traverse_ : (traverse_ as any).default || traverse_\nimport type { NodePath } from '@babel/traverse'\nimport type { JSXOpeningElement } from '@babel/types'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { UnpluginOptions, PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformJsxOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform JSX/TSX source code by injecting data-inspecto attributes.\n */\nexport function transformJsx(options: TransformJsxOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve the file path based on pathType config\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n // Normalize path separators on Windows\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n let ast: ReturnType<typeof parser.parse>\n try {\n ast = parser.parse(source, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'optionalChaining',\n 'nullishCoalescingOperator',\n 'importMeta',\n ],\n errorRecovery: true,\n })\n } catch {\n // If parsing fails, return source unchanged\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n traverse(ast, {\n JSXOpeningElement(nodePath: NodePath<JSXOpeningElement>) {\n const node = nodePath.node\n\n // Skip elements that already have the attribute\n const alreadyHasAttr = node.attributes.some(\n attr =>\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // Get element tag name\n const nameNode = node.name\n let tagName: string\n if (nameNode.type === 'JSXIdentifier') {\n tagName = nameNode.name\n } else if (nameNode.type === 'JSXMemberExpression') {\n const objName = nameNode.object.type === 'JSXIdentifier' ? nameNode.object.name : ''\n const propName = nameNode.property.type === 'JSXIdentifier' ? nameNode.property.name : ''\n tagName = objName && propName ? `${objName}.${propName}` : objName\n } else {\n tagName = ''\n }\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Get position from AST location\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n // Babel uses 0-based columns, convert to 1-based\n const attrValue = formatAttrValue(normalizedPath, line, column + 1)\n\n // Determine the best insertion position for the attribute\n // When a JSX element has type arguments (e.g. <Component<string> />),\n // inserting after `node.name.end` might inject inside the generic bracket `<`.\n // The safest place to insert is right before the first attribute,\n // or right before the closing slash/bracket if there are no attributes.\n let insertPos: number | null | undefined = null\n if (node.attributes && node.attributes.length > 0) {\n const firstAttr = node.attributes[0]\n if (firstAttr && firstAttr.start != null) {\n insertPos = firstAttr.start\n }\n }\n\n if (insertPos == null) {\n // Find the start of the closing bracket or self-closing slash\n // We know node.end is the index right after the '>'\n // So we look backwards. But Babel AST doesn't give us exact token positions\n // for the closing tag easily.\n // For a safe fallback, we use node.typeParameters?.end || node.name.end\n if (node.typeParameters && node.typeParameters.end != null) {\n insertPos = node.typeParameters.end\n } else if (node.name.end != null) {\n insertPos = node.name.end\n }\n }\n\n if (insertPos == null) return\n\n ms.appendLeft(\n insertPos,\n ` ${attributeName}=\"${attrValue}\"${node.attributes && node.attributes.length > 0 ? '' : ' '}`,\n )\n changed = true\n },\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n","import type { UnpluginOptions } from '@inspecto-dev/types'\nimport type MagicString from 'magic-string'\n\nexport interface TransformResult {\n code: string\n map: ReturnType<MagicString['generateMap']> | null\n changed: boolean\n}\n\n/** Default tags whose JSX elements should NOT receive data-inspecto attributes */\nexport const DEFAULT_ESCAPE_TAGS = new Set([\n 'template',\n 'script',\n 'style',\n // React special elements\n 'Fragment',\n 'React.Fragment',\n 'StrictMode',\n 'React.StrictMode',\n 'Suspense',\n 'React.Suspense',\n 'Profiler',\n 'React.Profiler',\n // React transitions\n 'Transition',\n 'TransitionGroup',\n // Vue built-in components\n 'KeepAlive',\n 'Teleport',\n 'Suspense',\n // Vue router built-ins\n 'RouterView',\n 'RouterLink',\n 'NuxtPage',\n 'NuxtLink',\n])\n\n/** File extensions that contain JSX/TSX syntax */\nexport const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.js', '.ts', '.mjs', '.mts'])\n\n/**\n * Determine if a file should be transformed.\n * Always skips node_modules and dist directories.\n */\nexport function shouldTransform(filePath: string, options: Required<UnpluginOptions>): boolean {\n // Never transform in production\n if (process.env['NODE_ENV'] === 'production') return false\n\n // Skip node_modules always\n if (filePath.includes('node_modules')) return false\n\n // Skip virtual modules\n if (filePath.startsWith('\\x00')) return false\n\n // Skip dist/build directories\n if (/[/\\\\](dist|build|\\.next|\\.nuxt)[/\\\\]/.test(filePath)) return false\n\n // Skip non-code files (like .html, .css)\n const ext = filePath.split('.').pop()?.toLowerCase()\n if (ext && !['js', 'jsx', 'ts', 'tsx', 'mjs', 'mts', 'vue'].includes(ext)) {\n return false\n }\n\n // Check user-defined exclude patterns\n // (picomatch integration — see index.ts for how options.exclude is applied)\n\n return true\n}\n\n/**\n * Build the escape tags set from user options merged with defaults.\n */\nexport function buildEscapeTagsSet(escapeTags?: string[]): Set<string> {\n const merged = new Set(DEFAULT_ESCAPE_TAGS)\n if (escapeTags) {\n for (const tag of escapeTags) {\n merged.add(tag)\n }\n }\n return merged\n}\n\n/**\n * Format a source location value for the data-inspecto attribute.\n * Format: \"filepath:line:column\"\n */\nexport function formatAttrValue(file: string, line: number, column: number): string {\n return `${file}:${line}:${column}`\n}\n","import * as vueCompiler from '@vue/compiler-dom'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport type { ElementNode, AttributeNode } from '@vue/compiler-core'\nimport { NodeTypes } from '@vue/compiler-core'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformVueOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform Vue SFC source by injecting data-inspecto attributes\n * into template elements.\n *\n * Strategy:\n * 1. Locate the <template> block in the SFC source\n * 2. Parse only the template block with @vue/compiler-dom\n * 3. Walk ElementNode nodes in the AST\n * 4. For each eligible element, inject the attribute using MagicString\n * at the exact offset within the original source\n */\nexport function transformVue(options: TransformVueOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve path\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n // ── Find <template> block boundaries ──────────────────────────────────────\n // Use @vue/compiler-sfc to parse the file and extract the template block.\n // This is much safer than regex for handling nested templates.\n const { descriptor, errors } = parseSFC(source, {\n filename: filePath,\n sourceMap: false,\n ignoreEmpty: true,\n })\n\n if (errors.length > 0 || !descriptor.template) {\n return { code: source, map: null, changed: false }\n }\n\n const templateContent = descriptor.template.content\n const templateBlockStart = descriptor.template.loc.start.offset\n\n // ── Parse template block ───────────────────────────────────────────────────\n let ast: vueCompiler.RootNode\n try {\n ast = vueCompiler.parse(templateContent, {\n parseMode: 'html',\n // Preserve source locations relative to templateContent\n onError: () => {\n /* ignore non-fatal parse errors */\n },\n })\n } catch {\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n // ── Walk AST ───────────────────────────────────────────────────────────────\n walkElement(ast, node => {\n // Skip non-element nodes\n if (node.type !== NodeTypes.ELEMENT) return\n\n const tagName = node.tag\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Skip <template> wrapper itself (it's the root, not a real element)\n if (tagName === 'template' && node === ast.children[0]) return\n\n // Skip elements that already have the attribute (idempotency)\n const alreadyHasAttr = node.props.some(\n (p): p is AttributeNode => p.type === NodeTypes.ATTRIBUTE && p.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // node.loc is relative to templateContent — add templateBlockStart offset\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n\n // Calculate absolute line and column in the original source\n // @vue/compiler-dom uses 1-based line and 1-based column\n const templateStartLoc = descriptor.template!.loc.start\n const absoluteLine = templateStartLoc.line + line - 1\n const absoluteColumn = line === 1 ? templateStartLoc.column + column - 1 : column\n\n const attrValue = formatAttrValue(normalizedPath, absoluteLine, absoluteColumn)\n\n // Find insert position: right after the tag name in the original source\n // node.loc.start.offset is 0-based offset within templateContent\n const tagNameEnd = loc.start.offset + tagName.length + 1 // +1 for '<'\n const absoluteOffset = templateBlockStart + tagNameEnd\n\n ms.appendLeft(absoluteOffset, ` ${attributeName}=\"${attrValue}\"`)\n changed = true\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n\n// ── AST walker ────────────────────────────────────────────────────────────────\n\ntype AnyNode = vueCompiler.RootNode | vueCompiler.TemplateChildNode\n\nfunction walkElement(node: AnyNode, visitor: (node: ElementNode) => void): void {\n if (node.type === NodeTypes.ELEMENT) {\n visitor(node)\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n } else if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n }\n}\n"],"mappings":"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","names":["import_node_path","traverse_","path","MagicString","import_magic_string","import_node_path","path","parseSFC","MagicString","path"]}
1
+ {"version":3,"sources":["../../../src/legacy/webpack4/loader.ts","../../../src/transform/index.ts","../../../src/transform/transform-jsx.ts","../../../src/transform/utils.ts","../../../src/transform/transform-vue.ts"],"sourcesContent":["import { transformRouter } from '../../transform/index'\nimport { shouldTransform } from '../../transform/utils'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\n\nconst injectedClient = false\n\nexport default function legacyWebpack4Loader(this: any, source: string) {\n const id = this.resourcePath\n\n // In Webpack 4, loader-utils is typically used, or we read from this.query\n // since this.getOptions() is a Webpack 5+ API.\n let options: any = {}\n if (typeof this.getOptions === 'function') {\n options = this.getOptions()\n } else if (this.query) {\n if (typeof this.query === 'string') {\n // Very fallback query parsing\n options = {}\n } else {\n options = this.query\n }\n }\n\n // Give default include/exclude options\n const defaultOptions: Required<UnpluginOptions> = {\n include: options.include || [/\\.[jt]sx?$/, /\\.vue$/],\n exclude: options.exclude || [/node_modules/, /\\.html$/],\n hotKey: options.hotKey || 'altKey',\n ...options,\n }\n\n if (!shouldTransform(id, defaultOptions)) {\n return source\n }\n\n const result = transformRouter({\n filePath: id,\n source,\n projectRoot: process.cwd(),\n pluginOptions: defaultOptions,\n })\n\n let finalSource = result?.changed ? result.code : source\n\n // Inject the client dynamically into the first source module we process\n // WARNING: We must NOT use process-level state like `let injectedClient = false`\n // because in webpack-dev-server, files are recompiled and the loader might hit\n // multiple entries or the same entry again on HMR.\n // Instead, we inject it conditionally based on a known module that acts as an entry\n // Webpack uses standard regex matching, so let's match any file that has `react-dom` rendering\n\n const isMainEntry =\n finalSource.includes('react-dom/client') ||\n finalSource.includes('react-dom') ||\n /[/\\\\](index|main)\\.[jt]sx?$/.test(id)\n\n if (isMainEntry && finalSource.indexOf('window.InspectoClient') === -1) {\n const clientPath = options.clientPath || '@inspecto-dev/core'\n // For Webpack 4 and Babel environments, using require() is safer than prepending `import`\n // to a file that might be CommonJS, and it doesn't get hoisted irregularly.\n finalSource =\n `\nif (typeof window !== 'undefined' && !window.InspectoClient) {\n try {\n var __inspecto_core__ = require('${clientPath.replace(/\\\\/g, '\\\\\\\\')}');\n window.InspectoClient = __inspecto_core__.default || __inspecto_core__;\n } catch (e) {\n console.error('[inspecto] core load error', e);\n }\n}\n` + finalSource\n }\n\n if (result && result.changed) {\n this.callback(null, finalSource, result.map)\n return\n }\n\n return finalSource\n}\n","import path from 'node:path'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\nimport { transformJsx } from './transform-jsx.js'\nimport { transformVue } from './transform-vue.js'\nimport { JSX_EXTENSIONS, type TransformResult } from './utils.js'\n\nexport interface RouterOptions {\n filePath: string\n source: string\n projectRoot: string\n pluginOptions: Required<UnpluginOptions>\n}\n\n/**\n * Route a file to the appropriate transform based on extension.\n * Returns null if no transform applies.\n */\nexport function transformRouter(options: RouterOptions): TransformResult | null {\n const { filePath, source, projectRoot, pluginOptions } = options\n const ext = path.extname(filePath).toLowerCase()\n\n if (JSX_EXTENSIONS.has(ext)) {\n return transformJsx({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n // ── Vue SFC ──────────────────────────────────────────────────────────────\n if (ext === '.vue') {\n return transformVue({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n return null\n}\n// Export transforms for testing\nexport { transformJsx, transformVue }\n","import * as parser from '@babel/parser'\nimport traverse_ from '@babel/traverse'\n// Support both ESM default and CommonJS module.exports\nconst traverse =\n typeof traverse_ === 'function' ? traverse_ : (traverse_ as any).default || traverse_\nimport type { NodePath } from '@babel/traverse'\nimport type { JSXOpeningElement } from '@babel/types'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { UnpluginOptions, PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformJsxOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform JSX/TSX source code by injecting data-inspecto attributes.\n */\nexport function transformJsx(options: TransformJsxOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve the file path based on pathType config\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n // Normalize path separators on Windows\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n let ast: ReturnType<typeof parser.parse>\n try {\n ast = parser.parse(source, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'optionalChaining',\n 'nullishCoalescingOperator',\n 'importMeta',\n ],\n errorRecovery: true,\n })\n } catch {\n // If parsing fails, return source unchanged\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n traverse(ast, {\n JSXOpeningElement(nodePath: NodePath<JSXOpeningElement>) {\n const node = nodePath.node\n\n // Skip elements that already have the attribute\n const alreadyHasAttr = node.attributes.some(\n attr =>\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // Get element tag name\n const nameNode = node.name\n let tagName: string\n if (nameNode.type === 'JSXIdentifier') {\n tagName = nameNode.name\n } else if (nameNode.type === 'JSXMemberExpression') {\n const objName = nameNode.object.type === 'JSXIdentifier' ? nameNode.object.name : ''\n const propName = nameNode.property.type === 'JSXIdentifier' ? nameNode.property.name : ''\n tagName = objName && propName ? `${objName}.${propName}` : objName\n } else {\n tagName = ''\n }\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Get position from AST location\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n // Babel uses 0-based columns, convert to 1-based\n const attrValue = formatAttrValue(normalizedPath, line, column + 1)\n\n // Determine the best insertion position for the attribute\n // When a JSX element has type arguments (e.g. <Component<string> />),\n // inserting after `node.name.end` might inject inside the generic bracket `<`.\n // The safest place to insert is right before the first attribute,\n // or right before the closing slash/bracket if there are no attributes.\n let insertPos: number | null | undefined = null\n if (node.attributes && node.attributes.length > 0) {\n const firstAttr = node.attributes[0]\n if (firstAttr && firstAttr.start != null) {\n insertPos = firstAttr.start\n }\n }\n\n if (insertPos == null) {\n // Find the start of the closing bracket or self-closing slash\n // We know node.end is the index right after the '>'\n // So we look backwards. But Babel AST doesn't give us exact token positions\n // for the closing tag easily.\n // For a safe fallback, we use node.typeParameters?.end || node.name.end\n if (node.typeParameters && node.typeParameters.end != null) {\n insertPos = node.typeParameters.end\n } else if (node.name.end != null) {\n insertPos = node.name.end\n }\n }\n\n if (insertPos == null) return\n\n ms.appendLeft(\n insertPos,\n ` ${attributeName}=\"${attrValue}\"${node.attributes && node.attributes.length > 0 ? '' : ' '}`,\n )\n changed = true\n },\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n","import type { UnpluginOptions } from '@inspecto-dev/types'\nimport type MagicString from 'magic-string'\n\nexport interface TransformResult {\n code: string\n map: ReturnType<MagicString['generateMap']> | null\n changed: boolean\n}\n\nexport interface NormalizedTransformTarget {\n requestId: string\n filePath: string\n wrapped: boolean\n}\n\n/** Default tags whose JSX elements should NOT receive data-inspecto attributes */\nexport const DEFAULT_ESCAPE_TAGS = new Set([\n 'template',\n 'script',\n 'style',\n // React special elements\n 'Fragment',\n 'React.Fragment',\n 'StrictMode',\n 'React.StrictMode',\n 'Suspense',\n 'React.Suspense',\n 'Profiler',\n 'React.Profiler',\n // React transitions\n 'Transition',\n 'TransitionGroup',\n // Vue built-in components\n 'KeepAlive',\n 'Teleport',\n 'Suspense',\n // Vue router built-ins\n 'RouterView',\n 'RouterLink',\n 'NuxtPage',\n 'NuxtLink',\n])\n\n/** File extensions that contain JSX/TSX syntax */\nexport const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.js', '.ts', '.mjs', '.mts'])\n\nfunction normalizeWebpackModuleRequest(id: string): string {\n return id.replace(/!+$/, '').replace(/^\\((?:app-pages-browser|rsc|ssr)\\)\\/\\.\\//, '')\n}\n\nfunction extractNextModuleRequest(id: string): string | undefined {\n if (!id.includes('next-flight-client-entry-loader.js?')) {\n return undefined\n }\n\n const queryIndex = id.indexOf('?')\n if (queryIndex === -1) {\n return undefined\n }\n\n const params = new URLSearchParams(id.slice(queryIndex + 1).replace(/!+$/, ''))\n for (const entry of params.getAll('modules')) {\n try {\n const parsed = JSON.parse(entry) as { request?: unknown }\n if (typeof parsed.request === 'string' && parsed.request.length > 0) {\n return parsed.request\n }\n } catch {\n continue\n }\n }\n\n return undefined\n}\n\nexport function extractTransformFilePath(requestId: string): NormalizedTransformTarget {\n const normalizedRequestId = normalizeWebpackModuleRequest(requestId)\n const nextModuleRequest = extractNextModuleRequest(normalizedRequestId)\n if (nextModuleRequest) {\n return {\n requestId,\n filePath: nextModuleRequest,\n wrapped: true,\n }\n }\n\n const lastLoaderSeparator = normalizedRequestId.lastIndexOf('!')\n const resourceRequest =\n lastLoaderSeparator >= 0\n ? normalizedRequestId.slice(lastLoaderSeparator + 1)\n : normalizedRequestId\n const queryIndex = resourceRequest.indexOf('?')\n const filePath = queryIndex >= 0 ? resourceRequest.slice(0, queryIndex) : resourceRequest\n\n return {\n requestId,\n filePath,\n wrapped: filePath !== requestId,\n }\n}\n\n/**\n * Determine if a file should be transformed.\n * Always skips node_modules and dist directories.\n */\nexport function shouldTransform(filePath: string, options: Required<UnpluginOptions>): boolean {\n const resolvedFilePath = extractTransformFilePath(filePath).filePath\n\n // Never transform in production\n if (process.env['NODE_ENV'] === 'production') return false\n\n // Skip node_modules always\n if (resolvedFilePath.includes('node_modules')) return false\n\n // Skip virtual modules\n if (resolvedFilePath.startsWith('\\x00')) return false\n\n // Skip dist/build directories\n if (/[/\\\\](dist|build|\\.next|\\.nuxt)[/\\\\]/.test(resolvedFilePath)) return false\n\n // Skip non-code files (like .html, .css)\n const ext = resolvedFilePath.split('.').pop()?.toLowerCase()\n if (ext && !['js', 'jsx', 'ts', 'tsx', 'mjs', 'mts', 'vue'].includes(ext)) {\n return false\n }\n\n // Check user-defined exclude patterns\n // (picomatch integration — see index.ts for how options.exclude is applied)\n\n return true\n}\n\n/**\n * Build the escape tags set from user options merged with defaults.\n */\nexport function buildEscapeTagsSet(escapeTags?: string[]): Set<string> {\n const merged = new Set(DEFAULT_ESCAPE_TAGS)\n if (escapeTags) {\n for (const tag of escapeTags) {\n merged.add(tag)\n }\n }\n return merged\n}\n\n/**\n * Format a source location value for the data-inspecto attribute.\n * Format: \"filepath:line:column\"\n */\nexport function formatAttrValue(file: string, line: number, column: number): string {\n return `${file}:${line}:${column}`\n}\n","import * as vueCompiler from '@vue/compiler-dom'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport type { ElementNode, AttributeNode } from '@vue/compiler-core'\nimport { NodeTypes } from '@vue/compiler-core'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformVueOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform Vue SFC source by injecting data-inspecto attributes\n * into template elements.\n *\n * Strategy:\n * 1. Locate the <template> block in the SFC source\n * 2. Parse only the template block with @vue/compiler-dom\n * 3. Walk ElementNode nodes in the AST\n * 4. For each eligible element, inject the attribute using MagicString\n * at the exact offset within the original source\n */\nexport function transformVue(options: TransformVueOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve path\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n // ── Find <template> block boundaries ──────────────────────────────────────\n // Use @vue/compiler-sfc to parse the file and extract the template block.\n // This is much safer than regex for handling nested templates.\n const { descriptor, errors } = parseSFC(source, {\n filename: filePath,\n sourceMap: false,\n ignoreEmpty: true,\n })\n\n if (errors.length > 0 || !descriptor.template) {\n return { code: source, map: null, changed: false }\n }\n\n const templateContent = descriptor.template.content\n const templateBlockStart = descriptor.template.loc.start.offset\n\n // ── Parse template block ───────────────────────────────────────────────────\n let ast: vueCompiler.RootNode\n try {\n ast = vueCompiler.parse(templateContent, {\n parseMode: 'html',\n // Preserve source locations relative to templateContent\n onError: () => {\n /* ignore non-fatal parse errors */\n },\n })\n } catch {\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n // ── Walk AST ───────────────────────────────────────────────────────────────\n walkElement(ast, node => {\n // Skip non-element nodes\n if (node.type !== NodeTypes.ELEMENT) return\n\n const tagName = node.tag\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Skip <template> wrapper itself (it's the root, not a real element)\n if (tagName === 'template' && node === ast.children[0]) return\n\n // Skip elements that already have the attribute (idempotency)\n const alreadyHasAttr = node.props.some(\n (p): p is AttributeNode => p.type === NodeTypes.ATTRIBUTE && p.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // node.loc is relative to templateContent — add templateBlockStart offset\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n\n // Calculate absolute line and column in the original source\n // @vue/compiler-dom uses 1-based line and 1-based column\n const templateStartLoc = descriptor.template!.loc.start\n const absoluteLine = templateStartLoc.line + line - 1\n const absoluteColumn = line === 1 ? templateStartLoc.column + column - 1 : column\n\n const attrValue = formatAttrValue(normalizedPath, absoluteLine, absoluteColumn)\n\n // Find insert position: right after the tag name in the original source\n // node.loc.start.offset is 0-based offset within templateContent\n const tagNameEnd = loc.start.offset + tagName.length + 1 // +1 for '<'\n const absoluteOffset = templateBlockStart + tagNameEnd\n\n ms.appendLeft(absoluteOffset, ` ${attributeName}=\"${attrValue}\"`)\n changed = true\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n\n// ── AST walker ────────────────────────────────────────────────────────────────\n\ntype AnyNode = vueCompiler.RootNode | vueCompiler.TemplateChildNode\n\nfunction walkElement(node: AnyNode, visitor: (node: ElementNode) => void): void {\n if (node.type === NodeTypes.ELEMENT) {\n visitor(node)\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n } else if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAAiB;;;ACAjB,aAAwB;AACxB,sBAAsB;AAMtB,0BAAwB;AACxB,uBAAiB;;;ACQV,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,iBAAiB,oBAAI,IAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,QAAQ,MAAM,CAAC;AAEpF,SAAS,8BAA8B,IAAoB;AACzD,SAAO,GAAG,QAAQ,OAAO,EAAE,EAAE,QAAQ,4CAA4C,EAAE;AACrF;AAEA,SAAS,yBAAyB,IAAgC;AAChE,MAAI,CAAC,GAAG,SAAS,qCAAqC,GAAG;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,GAAG,QAAQ,GAAG;AACjC,MAAI,eAAe,IAAI;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,gBAAgB,GAAG,MAAM,aAAa,CAAC,EAAE,QAAQ,OAAO,EAAE,CAAC;AAC9E,aAAW,SAAS,OAAO,OAAO,SAAS,GAAG;AAC5C,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,UAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,SAAS,GAAG;AACnE,eAAO,OAAO;AAAA,MAChB;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,yBAAyB,WAA8C;AACrF,QAAM,sBAAsB,8BAA8B,SAAS;AACnE,QAAM,oBAAoB,yBAAyB,mBAAmB;AACtE,MAAI,mBAAmB;AACrB,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,sBAAsB,oBAAoB,YAAY,GAAG;AAC/D,QAAM,kBACJ,uBAAuB,IACnB,oBAAoB,MAAM,sBAAsB,CAAC,IACjD;AACN,QAAM,aAAa,gBAAgB,QAAQ,GAAG;AAC9C,QAAM,WAAW,cAAc,IAAI,gBAAgB,MAAM,GAAG,UAAU,IAAI;AAE1E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,EACxB;AACF;AAMO,SAAS,gBAAgB,UAAkB,SAA6C;AAC7F,QAAM,mBAAmB,yBAAyB,QAAQ,EAAE;AAG5D,MAAI,QAAQ,IAAI,UAAU,MAAM,aAAc,QAAO;AAGrD,MAAI,iBAAiB,SAAS,cAAc,EAAG,QAAO;AAGtD,MAAI,iBAAiB,WAAW,IAAM,EAAG,QAAO;AAGhD,MAAI,uCAAuC,KAAK,gBAAgB,EAAG,QAAO;AAG1E,QAAM,MAAM,iBAAiB,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC3D,MAAI,OAAO,CAAC,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AACzE,WAAO;AAAA,EACT;AAKA,SAAO;AACT;AAKO,SAAS,mBAAmB,YAAoC;AACrE,QAAM,SAAS,IAAI,IAAI,mBAAmB;AAC1C,MAAI,YAAY;AACd,eAAW,OAAO,YAAY;AAC5B,aAAO,IAAI,GAAG;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,gBAAgB,MAAc,MAAc,QAAwB;AAClF,SAAO,GAAG,IAAI,IAAI,IAAI,IAAI,MAAM;AAClC;;;ADpJA,IAAM,WACJ,OAAO,gBAAAC,YAAc,aAAa,gBAAAA,UAAa,gBAAAA,QAAkB,WAAW,gBAAAA;AAoBvE,SAAS,aAAa,SAA+C;AAC1E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,gBAAgB,mBAAmB,UAAU;AAGnD,QAAM,eACJ,aAAa,aACT,iBAAAC,QAAK,QAAQ,QAAQ,IACrB,iBAAAA,QAAK,SAAS,aAAa,iBAAAA,QAAK,QAAQ,QAAQ,CAAC;AAGvD,QAAM,iBAAiB,aAAa,QAAQ,OAAO,GAAG;AAEtD,MAAI;AACJ,MAAI;AACF,UAAa,aAAM,QAAQ;AAAA,MACzB,YAAY;AAAA,MACZ,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAe;AAAA,IACjB,CAAC;AAAA,EACH,QAAQ;AAEN,WAAO,EAAE,MAAM,QAAQ,KAAK,MAAM,SAAS,MAAM;AAAA,EACnD;AAEA,QAAM,KAAK,IAAI,oBAAAC,QAAY,MAAM;AACjC,MAAI,UAAU;AAEd,WAAS,KAAK;AAAA,IACZ,kBAAkB,UAAuC;AACvD,YAAM,OAAO,SAAS;AAGtB,YAAM,iBAAiB,KAAK,WAAW;AAAA,QACrC,UACE,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,KAAK,KAAK,SAAS;AAAA,MACvB;AACA,UAAI,eAAgB;AAGpB,YAAM,WAAW,KAAK;AACtB,UAAI;AACJ,UAAI,SAAS,SAAS,iBAAiB;AACrC,kBAAU,SAAS;AAAA,MACrB,WAAW,SAAS,SAAS,uBAAuB;AAClD,cAAM,UAAU,SAAS,OAAO,SAAS,kBAAkB,SAAS,OAAO,OAAO;AAClF,cAAM,WAAW,SAAS,SAAS,SAAS,kBAAkB,SAAS,SAAS,OAAO;AACvF,kBAAU,WAAW,WAAW,GAAG,OAAO,IAAI,QAAQ,KAAK;AAAA,MAC7D,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,UAAI,cAAc,IAAI,OAAO,EAAG;AAGhC,YAAM,MAAM,KAAK;AACjB,UAAI,CAAC,IAAK;AAEV,YAAM,EAAE,MAAM,OAAO,IAAI,IAAI;AAE7B,YAAM,YAAY,gBAAgB,gBAAgB,MAAM,SAAS,CAAC;AAOlE,UAAI,YAAuC;AAC3C,UAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;AACjD,cAAM,YAAY,KAAK,WAAW,CAAC;AACnC,YAAI,aAAa,UAAU,SAAS,MAAM;AACxC,sBAAY,UAAU;AAAA,QACxB;AAAA,MACF;AAEA,UAAI,aAAa,MAAM;AAMrB,YAAI,KAAK,kBAAkB,KAAK,eAAe,OAAO,MAAM;AAC1D,sBAAY,KAAK,eAAe;AAAA,QAClC,WAAW,KAAK,KAAK,OAAO,MAAM;AAChC,sBAAY,KAAK,KAAK;AAAA,QACxB;AAAA,MACF;AAEA,UAAI,aAAa,KAAM;AAEvB,SAAG;AAAA,QACD;AAAA,QACA,IAAI,aAAa,KAAK,SAAS,IAAI,KAAK,cAAc,KAAK,WAAW,SAAS,IAAI,KAAK,GAAG;AAAA,MAC7F;AACA,gBAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,MAAM,QAAQ,KAAK,MAAM,SAAS,MAAM;AAAA,EACnD;AAEA,SAAO;AAAA,IACL,MAAM,GAAG,SAAS;AAAA,IAClB,KAAK,GAAG,YAAY,EAAE,OAAO,MAAM,QAAQ,SAAS,CAAC;AAAA,IACrD,SAAS;AAAA,EACX;AACF;;;AEtJA,kBAA6B;AAC7B,0BAAkC;AAElC,2BAA0B;AAC1B,IAAAC,uBAAwB;AACxB,IAAAC,oBAAiB;AAwBV,SAAS,aAAa,SAA+C;AAC1E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,gBAAgB,mBAAmB,UAAU;AAGnD,QAAM,eACJ,aAAa,aACT,kBAAAC,QAAK,QAAQ,QAAQ,IACrB,kBAAAA,QAAK,SAAS,aAAa,kBAAAA,QAAK,QAAQ,QAAQ,CAAC;AAEvD,QAAM,iBAAiB,aAAa,QAAQ,OAAO,GAAG;AAKtD,QAAM,EAAE,YAAY,OAAO,QAAI,oBAAAC,OAAS,QAAQ;AAAA,IAC9C,UAAU;AAAA,IACV,WAAW;AAAA,IACX,aAAa;AAAA,EACf,CAAC;AAED,MAAI,OAAO,SAAS,KAAK,CAAC,WAAW,UAAU;AAC7C,WAAO,EAAE,MAAM,QAAQ,KAAK,MAAM,SAAS,MAAM;AAAA,EACnD;AAEA,QAAM,kBAAkB,WAAW,SAAS;AAC5C,QAAM,qBAAqB,WAAW,SAAS,IAAI,MAAM;AAGzD,MAAI;AACJ,MAAI;AACF,UAAkB,kBAAM,iBAAiB;AAAA,MACvC,WAAW;AAAA;AAAA,MAEX,SAAS,MAAM;AAAA,MAEf;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,EAAE,MAAM,QAAQ,KAAK,MAAM,SAAS,MAAM;AAAA,EACnD;AAEA,QAAM,KAAK,IAAI,qBAAAC,QAAY,MAAM;AACjC,MAAI,UAAU;AAGd,cAAY,KAAK,UAAQ;AAEvB,QAAI,KAAK,SAAS,+BAAU,QAAS;AAErC,UAAM,UAAU,KAAK;AAGrB,QAAI,cAAc,IAAI,OAAO,EAAG;AAGhC,QAAI,YAAY,cAAc,SAAS,IAAI,SAAS,CAAC,EAAG;AAGxD,UAAM,iBAAiB,KAAK,MAAM;AAAA,MAChC,CAAC,MAA0B,EAAE,SAAS,+BAAU,aAAa,EAAE,SAAS;AAAA,IAC1E;AACA,QAAI,eAAgB;AAGpB,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK;AAEV,UAAM,EAAE,MAAM,OAAO,IAAI,IAAI;AAI7B,UAAM,mBAAmB,WAAW,SAAU,IAAI;AAClD,UAAM,eAAe,iBAAiB,OAAO,OAAO;AACpD,UAAM,iBAAiB,SAAS,IAAI,iBAAiB,SAAS,SAAS,IAAI;AAE3E,UAAM,YAAY,gBAAgB,gBAAgB,cAAc,cAAc;AAI9E,UAAM,aAAa,IAAI,MAAM,SAAS,QAAQ,SAAS;AACvD,UAAM,iBAAiB,qBAAqB;AAE5C,OAAG,WAAW,gBAAgB,IAAI,aAAa,KAAK,SAAS,GAAG;AAChE,cAAU;AAAA,EACZ,CAAC;AAED,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,MAAM,QAAQ,KAAK,MAAM,SAAS,MAAM;AAAA,EACnD;AAEA,SAAO;AAAA,IACL,MAAM,GAAG,SAAS;AAAA,IAClB,KAAK,GAAG,YAAY,EAAE,OAAO,MAAM,QAAQ,SAAS,CAAC;AAAA,IACrD,SAAS;AAAA,EACX;AACF;AAMA,SAAS,YAAY,MAAe,SAA4C;AAC9E,MAAI,KAAK,SAAS,+BAAU,SAAS;AACnC,YAAQ,IAAI;AACZ,eAAW,SAAS,KAAK,UAAU;AACjC,kBAAY,OAAkB,OAAO;AAAA,IACvC;AAAA,EACF,WAAW,cAAc,QAAQ,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC7D,eAAW,SAAS,KAAK,UAAU;AACjC,kBAAY,OAAkB,OAAO;AAAA,IACvC;AAAA,EACF;AACF;;;AHrIO,SAAS,gBAAgB,SAAgD;AAC9E,QAAM,EAAE,UAAU,QAAQ,aAAa,cAAc,IAAI;AACzD,QAAM,MAAM,kBAAAC,QAAK,QAAQ,QAAQ,EAAE,YAAY;AAE/C,MAAI,eAAe,IAAI,GAAG,GAAG;AAC3B,WAAO,aAAa;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,cAAc;AAAA,MAC1B,UAAU,cAAc;AAAA,MACxB,eAAe,cAAc;AAAA,IAC/B,CAAC;AAAA,EACH;AAGA,MAAI,QAAQ,QAAQ;AAClB,WAAO,aAAa;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,cAAc;AAAA,MAC1B,UAAU,cAAc;AAAA,MACxB,eAAe,cAAc;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ADvCe,SAAR,qBAAiD,QAAgB;AACtE,QAAM,KAAK,KAAK;AAIhB,MAAI,UAAe,CAAC;AACpB,MAAI,OAAO,KAAK,eAAe,YAAY;AACzC,cAAU,KAAK,WAAW;AAAA,EAC5B,WAAW,KAAK,OAAO;AACrB,QAAI,OAAO,KAAK,UAAU,UAAU;AAElC,gBAAU,CAAC;AAAA,IACb,OAAO;AACL,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAGA,QAAM,iBAA4C;AAAA,IAChD,SAAS,QAAQ,WAAW,CAAC,cAAc,QAAQ;AAAA,IACnD,SAAS,QAAQ,WAAW,CAAC,gBAAgB,SAAS;AAAA,IACtD,QAAQ,QAAQ,UAAU;AAAA,IAC1B,GAAG;AAAA,EACL;AAEA,MAAI,CAAC,gBAAgB,IAAI,cAAc,GAAG;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,gBAAgB;AAAA,IAC7B,UAAU;AAAA,IACV;AAAA,IACA,aAAa,QAAQ,IAAI;AAAA,IACzB,eAAe;AAAA,EACjB,CAAC;AAED,MAAI,cAAc,QAAQ,UAAU,OAAO,OAAO;AASlD,QAAM,cACJ,YAAY,SAAS,kBAAkB,KACvC,YAAY,SAAS,WAAW,KAChC,8BAA8B,KAAK,EAAE;AAEvC,MAAI,eAAe,YAAY,QAAQ,uBAAuB,MAAM,IAAI;AACtE,UAAM,aAAa,QAAQ,cAAc;AAGzC,kBACE;AAAA;AAAA;AAAA,uCAGiC,WAAW,QAAQ,OAAO,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMpE;AAAA,EACF;AAEA,MAAI,UAAU,OAAO,SAAS;AAC5B,SAAK,SAAS,MAAM,aAAa,OAAO,GAAG;AAC3C;AAAA,EACF;AAEA,SAAO;AACT;","names":["import_node_path","traverse_","path","MagicString","import_magic_string","import_node_path","path","parseSFC","MagicString","path"]}
@@ -35,12 +35,57 @@ var DEFAULT_ESCAPE_TAGS = /* @__PURE__ */ new Set([
35
35
  "NuxtLink"
36
36
  ]);
37
37
  var JSX_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx", ".js", ".ts", ".mjs", ".mts"]);
38
+ function normalizeWebpackModuleRequest(id) {
39
+ return id.replace(/!+$/, "").replace(/^\((?:app-pages-browser|rsc|ssr)\)\/\.\//, "");
40
+ }
41
+ function extractNextModuleRequest(id) {
42
+ if (!id.includes("next-flight-client-entry-loader.js?")) {
43
+ return void 0;
44
+ }
45
+ const queryIndex = id.indexOf("?");
46
+ if (queryIndex === -1) {
47
+ return void 0;
48
+ }
49
+ const params = new URLSearchParams(id.slice(queryIndex + 1).replace(/!+$/, ""));
50
+ for (const entry of params.getAll("modules")) {
51
+ try {
52
+ const parsed = JSON.parse(entry);
53
+ if (typeof parsed.request === "string" && parsed.request.length > 0) {
54
+ return parsed.request;
55
+ }
56
+ } catch {
57
+ continue;
58
+ }
59
+ }
60
+ return void 0;
61
+ }
62
+ function extractTransformFilePath(requestId) {
63
+ const normalizedRequestId = normalizeWebpackModuleRequest(requestId);
64
+ const nextModuleRequest = extractNextModuleRequest(normalizedRequestId);
65
+ if (nextModuleRequest) {
66
+ return {
67
+ requestId,
68
+ filePath: nextModuleRequest,
69
+ wrapped: true
70
+ };
71
+ }
72
+ const lastLoaderSeparator = normalizedRequestId.lastIndexOf("!");
73
+ const resourceRequest = lastLoaderSeparator >= 0 ? normalizedRequestId.slice(lastLoaderSeparator + 1) : normalizedRequestId;
74
+ const queryIndex = resourceRequest.indexOf("?");
75
+ const filePath = queryIndex >= 0 ? resourceRequest.slice(0, queryIndex) : resourceRequest;
76
+ return {
77
+ requestId,
78
+ filePath,
79
+ wrapped: filePath !== requestId
80
+ };
81
+ }
38
82
  function shouldTransform(filePath, options) {
83
+ const resolvedFilePath = extractTransformFilePath(filePath).filePath;
39
84
  if (process.env["NODE_ENV"] === "production") return false;
40
- if (filePath.includes("node_modules")) return false;
41
- if (filePath.startsWith("\0")) return false;
42
- if (/[/\\](dist|build|\.next|\.nuxt)[/\\]/.test(filePath)) return false;
43
- const ext = filePath.split(".").pop()?.toLowerCase();
85
+ if (resolvedFilePath.includes("node_modules")) return false;
86
+ if (resolvedFilePath.startsWith("\0")) return false;
87
+ if (/[/\\](dist|build|\.next|\.nuxt)[/\\]/.test(resolvedFilePath)) return false;
88
+ const ext = resolvedFilePath.split(".").pop()?.toLowerCase();
44
89
  if (ext && !["js", "jsx", "ts", "tsx", "mjs", "mts", "vue"].includes(ext)) {
45
90
  return false;
46
91
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/transform/index.ts","../../../src/transform/transform-jsx.ts","../../../src/transform/utils.ts","../../../src/transform/transform-vue.ts","../../../src/legacy/webpack4/loader.ts"],"sourcesContent":["import path from 'node:path'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\nimport { transformJsx } from './transform-jsx.js'\nimport { transformVue } from './transform-vue.js'\nimport { JSX_EXTENSIONS, type TransformResult } from './utils.js'\n\nexport interface RouterOptions {\n filePath: string\n source: string\n projectRoot: string\n pluginOptions: Required<UnpluginOptions>\n}\n\n/**\n * Route a file to the appropriate transform based on extension.\n * Returns null if no transform applies.\n */\nexport function transformRouter(options: RouterOptions): TransformResult | null {\n const { filePath, source, projectRoot, pluginOptions } = options\n const ext = path.extname(filePath).toLowerCase()\n\n if (JSX_EXTENSIONS.has(ext)) {\n return transformJsx({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n // ── Vue SFC ──────────────────────────────────────────────────────────────\n if (ext === '.vue') {\n return transformVue({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n return null\n}\n// Export transforms for testing\nexport { transformJsx, transformVue }\n","import * as parser from '@babel/parser'\nimport traverse_ from '@babel/traverse'\n// Support both ESM default and CommonJS module.exports\nconst traverse =\n typeof traverse_ === 'function' ? traverse_ : (traverse_ as any).default || traverse_\nimport type { NodePath } from '@babel/traverse'\nimport type { JSXOpeningElement } from '@babel/types'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { UnpluginOptions, PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformJsxOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform JSX/TSX source code by injecting data-inspecto attributes.\n */\nexport function transformJsx(options: TransformJsxOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve the file path based on pathType config\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n // Normalize path separators on Windows\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n let ast: ReturnType<typeof parser.parse>\n try {\n ast = parser.parse(source, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'optionalChaining',\n 'nullishCoalescingOperator',\n 'importMeta',\n ],\n errorRecovery: true,\n })\n } catch {\n // If parsing fails, return source unchanged\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n traverse(ast, {\n JSXOpeningElement(nodePath: NodePath<JSXOpeningElement>) {\n const node = nodePath.node\n\n // Skip elements that already have the attribute\n const alreadyHasAttr = node.attributes.some(\n attr =>\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // Get element tag name\n const nameNode = node.name\n let tagName: string\n if (nameNode.type === 'JSXIdentifier') {\n tagName = nameNode.name\n } else if (nameNode.type === 'JSXMemberExpression') {\n const objName = nameNode.object.type === 'JSXIdentifier' ? nameNode.object.name : ''\n const propName = nameNode.property.type === 'JSXIdentifier' ? nameNode.property.name : ''\n tagName = objName && propName ? `${objName}.${propName}` : objName\n } else {\n tagName = ''\n }\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Get position from AST location\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n // Babel uses 0-based columns, convert to 1-based\n const attrValue = formatAttrValue(normalizedPath, line, column + 1)\n\n // Determine the best insertion position for the attribute\n // When a JSX element has type arguments (e.g. <Component<string> />),\n // inserting after `node.name.end` might inject inside the generic bracket `<`.\n // The safest place to insert is right before the first attribute,\n // or right before the closing slash/bracket if there are no attributes.\n let insertPos: number | null | undefined = null\n if (node.attributes && node.attributes.length > 0) {\n const firstAttr = node.attributes[0]\n if (firstAttr && firstAttr.start != null) {\n insertPos = firstAttr.start\n }\n }\n\n if (insertPos == null) {\n // Find the start of the closing bracket or self-closing slash\n // We know node.end is the index right after the '>'\n // So we look backwards. But Babel AST doesn't give us exact token positions\n // for the closing tag easily.\n // For a safe fallback, we use node.typeParameters?.end || node.name.end\n if (node.typeParameters && node.typeParameters.end != null) {\n insertPos = node.typeParameters.end\n } else if (node.name.end != null) {\n insertPos = node.name.end\n }\n }\n\n if (insertPos == null) return\n\n ms.appendLeft(\n insertPos,\n ` ${attributeName}=\"${attrValue}\"${node.attributes && node.attributes.length > 0 ? '' : ' '}`,\n )\n changed = true\n },\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n","import type { UnpluginOptions } from '@inspecto-dev/types'\nimport type MagicString from 'magic-string'\n\nexport interface TransformResult {\n code: string\n map: ReturnType<MagicString['generateMap']> | null\n changed: boolean\n}\n\n/** Default tags whose JSX elements should NOT receive data-inspecto attributes */\nexport const DEFAULT_ESCAPE_TAGS = new Set([\n 'template',\n 'script',\n 'style',\n // React special elements\n 'Fragment',\n 'React.Fragment',\n 'StrictMode',\n 'React.StrictMode',\n 'Suspense',\n 'React.Suspense',\n 'Profiler',\n 'React.Profiler',\n // React transitions\n 'Transition',\n 'TransitionGroup',\n // Vue built-in components\n 'KeepAlive',\n 'Teleport',\n 'Suspense',\n // Vue router built-ins\n 'RouterView',\n 'RouterLink',\n 'NuxtPage',\n 'NuxtLink',\n])\n\n/** File extensions that contain JSX/TSX syntax */\nexport const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.js', '.ts', '.mjs', '.mts'])\n\n/**\n * Determine if a file should be transformed.\n * Always skips node_modules and dist directories.\n */\nexport function shouldTransform(filePath: string, options: Required<UnpluginOptions>): boolean {\n // Never transform in production\n if (process.env['NODE_ENV'] === 'production') return false\n\n // Skip node_modules always\n if (filePath.includes('node_modules')) return false\n\n // Skip virtual modules\n if (filePath.startsWith('\\x00')) return false\n\n // Skip dist/build directories\n if (/[/\\\\](dist|build|\\.next|\\.nuxt)[/\\\\]/.test(filePath)) return false\n\n // Skip non-code files (like .html, .css)\n const ext = filePath.split('.').pop()?.toLowerCase()\n if (ext && !['js', 'jsx', 'ts', 'tsx', 'mjs', 'mts', 'vue'].includes(ext)) {\n return false\n }\n\n // Check user-defined exclude patterns\n // (picomatch integration — see index.ts for how options.exclude is applied)\n\n return true\n}\n\n/**\n * Build the escape tags set from user options merged with defaults.\n */\nexport function buildEscapeTagsSet(escapeTags?: string[]): Set<string> {\n const merged = new Set(DEFAULT_ESCAPE_TAGS)\n if (escapeTags) {\n for (const tag of escapeTags) {\n merged.add(tag)\n }\n }\n return merged\n}\n\n/**\n * Format a source location value for the data-inspecto attribute.\n * Format: \"filepath:line:column\"\n */\nexport function formatAttrValue(file: string, line: number, column: number): string {\n return `${file}:${line}:${column}`\n}\n","import * as vueCompiler from '@vue/compiler-dom'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport type { ElementNode, AttributeNode } from '@vue/compiler-core'\nimport { NodeTypes } from '@vue/compiler-core'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformVueOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform Vue SFC source by injecting data-inspecto attributes\n * into template elements.\n *\n * Strategy:\n * 1. Locate the <template> block in the SFC source\n * 2. Parse only the template block with @vue/compiler-dom\n * 3. Walk ElementNode nodes in the AST\n * 4. For each eligible element, inject the attribute using MagicString\n * at the exact offset within the original source\n */\nexport function transformVue(options: TransformVueOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve path\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n // ── Find <template> block boundaries ──────────────────────────────────────\n // Use @vue/compiler-sfc to parse the file and extract the template block.\n // This is much safer than regex for handling nested templates.\n const { descriptor, errors } = parseSFC(source, {\n filename: filePath,\n sourceMap: false,\n ignoreEmpty: true,\n })\n\n if (errors.length > 0 || !descriptor.template) {\n return { code: source, map: null, changed: false }\n }\n\n const templateContent = descriptor.template.content\n const templateBlockStart = descriptor.template.loc.start.offset\n\n // ── Parse template block ───────────────────────────────────────────────────\n let ast: vueCompiler.RootNode\n try {\n ast = vueCompiler.parse(templateContent, {\n parseMode: 'html',\n // Preserve source locations relative to templateContent\n onError: () => {\n /* ignore non-fatal parse errors */\n },\n })\n } catch {\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n // ── Walk AST ───────────────────────────────────────────────────────────────\n walkElement(ast, node => {\n // Skip non-element nodes\n if (node.type !== NodeTypes.ELEMENT) return\n\n const tagName = node.tag\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Skip <template> wrapper itself (it's the root, not a real element)\n if (tagName === 'template' && node === ast.children[0]) return\n\n // Skip elements that already have the attribute (idempotency)\n const alreadyHasAttr = node.props.some(\n (p): p is AttributeNode => p.type === NodeTypes.ATTRIBUTE && p.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // node.loc is relative to templateContent — add templateBlockStart offset\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n\n // Calculate absolute line and column in the original source\n // @vue/compiler-dom uses 1-based line and 1-based column\n const templateStartLoc = descriptor.template!.loc.start\n const absoluteLine = templateStartLoc.line + line - 1\n const absoluteColumn = line === 1 ? templateStartLoc.column + column - 1 : column\n\n const attrValue = formatAttrValue(normalizedPath, absoluteLine, absoluteColumn)\n\n // Find insert position: right after the tag name in the original source\n // node.loc.start.offset is 0-based offset within templateContent\n const tagNameEnd = loc.start.offset + tagName.length + 1 // +1 for '<'\n const absoluteOffset = templateBlockStart + tagNameEnd\n\n ms.appendLeft(absoluteOffset, ` ${attributeName}=\"${attrValue}\"`)\n changed = true\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n\n// ── AST walker ────────────────────────────────────────────────────────────────\n\ntype AnyNode = vueCompiler.RootNode | vueCompiler.TemplateChildNode\n\nfunction walkElement(node: AnyNode, visitor: (node: ElementNode) => void): void {\n if (node.type === NodeTypes.ELEMENT) {\n visitor(node)\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n } else if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n }\n}\n","import { transformRouter } from '../../transform/index'\nimport { shouldTransform } from '../../transform/utils'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\n\nconst injectedClient = false\n\nexport default function legacyWebpack4Loader(this: any, source: string) {\n const id = this.resourcePath\n\n // In Webpack 4, loader-utils is typically used, or we read from this.query\n // since this.getOptions() is a Webpack 5+ API.\n let options: any = {}\n if (typeof this.getOptions === 'function') {\n options = this.getOptions()\n } else if (this.query) {\n if (typeof this.query === 'string') {\n // Very fallback query parsing\n options = {}\n } else {\n options = this.query\n }\n }\n\n // Give default include/exclude options\n const defaultOptions: Required<UnpluginOptions> = {\n include: options.include || [/\\.[jt]sx?$/, /\\.vue$/],\n exclude: options.exclude || [/node_modules/, /\\.html$/],\n hotKey: options.hotKey || 'altKey',\n ...options,\n }\n\n if (!shouldTransform(id, defaultOptions)) {\n return source\n }\n\n const result = transformRouter({\n filePath: id,\n source,\n projectRoot: process.cwd(),\n pluginOptions: defaultOptions,\n })\n\n let finalSource = result?.changed ? result.code : source\n\n // Inject the client dynamically into the first source module we process\n // WARNING: We must NOT use process-level state like `let injectedClient = false`\n // because in webpack-dev-server, files are recompiled and the loader might hit\n // multiple entries or the same entry again on HMR.\n // Instead, we inject it conditionally based on a known module that acts as an entry\n // Webpack uses standard regex matching, so let's match any file that has `react-dom` rendering\n\n const isMainEntry =\n finalSource.includes('react-dom/client') ||\n finalSource.includes('react-dom') ||\n /[/\\\\](index|main)\\.[jt]sx?$/.test(id)\n\n if (isMainEntry && finalSource.indexOf('window.InspectoClient') === -1) {\n const clientPath = options.clientPath || '@inspecto-dev/core'\n // For Webpack 4 and Babel environments, using require() is safer than prepending `import`\n // to a file that might be CommonJS, and it doesn't get hoisted irregularly.\n finalSource =\n `\nif (typeof window !== 'undefined' && !window.InspectoClient) {\n try {\n var __inspecto_core__ = require('${clientPath.replace(/\\\\/g, '\\\\\\\\')}');\n window.InspectoClient = __inspecto_core__.default || __inspecto_core__;\n } catch (e) {\n console.error('[inspecto] core load error', e);\n }\n}\n` + finalSource\n }\n\n if (result && result.changed) {\n this.callback(null, finalSource, result.map)\n return\n }\n\n return finalSource\n}\n"],"mappings":"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","names":["path","MagicString","path","path","MagicString","path"]}
1
+ {"version":3,"sources":["../../../src/transform/index.ts","../../../src/transform/transform-jsx.ts","../../../src/transform/utils.ts","../../../src/transform/transform-vue.ts","../../../src/legacy/webpack4/loader.ts"],"sourcesContent":["import path from 'node:path'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\nimport { transformJsx } from './transform-jsx.js'\nimport { transformVue } from './transform-vue.js'\nimport { JSX_EXTENSIONS, type TransformResult } from './utils.js'\n\nexport interface RouterOptions {\n filePath: string\n source: string\n projectRoot: string\n pluginOptions: Required<UnpluginOptions>\n}\n\n/**\n * Route a file to the appropriate transform based on extension.\n * Returns null if no transform applies.\n */\nexport function transformRouter(options: RouterOptions): TransformResult | null {\n const { filePath, source, projectRoot, pluginOptions } = options\n const ext = path.extname(filePath).toLowerCase()\n\n if (JSX_EXTENSIONS.has(ext)) {\n return transformJsx({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n // ── Vue SFC ──────────────────────────────────────────────────────────────\n if (ext === '.vue') {\n return transformVue({\n filePath,\n source,\n projectRoot,\n escapeTags: pluginOptions.escapeTags,\n pathType: pluginOptions.pathType,\n attributeName: pluginOptions.attributeName,\n })\n }\n\n return null\n}\n// Export transforms for testing\nexport { transformJsx, transformVue }\n","import * as parser from '@babel/parser'\nimport traverse_ from '@babel/traverse'\n// Support both ESM default and CommonJS module.exports\nconst traverse =\n typeof traverse_ === 'function' ? traverse_ : (traverse_ as any).default || traverse_\nimport type { NodePath } from '@babel/traverse'\nimport type { JSXOpeningElement } from '@babel/types'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { UnpluginOptions, PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformJsxOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform JSX/TSX source code by injecting data-inspecto attributes.\n */\nexport function transformJsx(options: TransformJsxOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve the file path based on pathType config\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n // Normalize path separators on Windows\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n let ast: ReturnType<typeof parser.parse>\n try {\n ast = parser.parse(source, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'optionalChaining',\n 'nullishCoalescingOperator',\n 'importMeta',\n ],\n errorRecovery: true,\n })\n } catch {\n // If parsing fails, return source unchanged\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n traverse(ast, {\n JSXOpeningElement(nodePath: NodePath<JSXOpeningElement>) {\n const node = nodePath.node\n\n // Skip elements that already have the attribute\n const alreadyHasAttr = node.attributes.some(\n attr =>\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // Get element tag name\n const nameNode = node.name\n let tagName: string\n if (nameNode.type === 'JSXIdentifier') {\n tagName = nameNode.name\n } else if (nameNode.type === 'JSXMemberExpression') {\n const objName = nameNode.object.type === 'JSXIdentifier' ? nameNode.object.name : ''\n const propName = nameNode.property.type === 'JSXIdentifier' ? nameNode.property.name : ''\n tagName = objName && propName ? `${objName}.${propName}` : objName\n } else {\n tagName = ''\n }\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Get position from AST location\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n // Babel uses 0-based columns, convert to 1-based\n const attrValue = formatAttrValue(normalizedPath, line, column + 1)\n\n // Determine the best insertion position for the attribute\n // When a JSX element has type arguments (e.g. <Component<string> />),\n // inserting after `node.name.end` might inject inside the generic bracket `<`.\n // The safest place to insert is right before the first attribute,\n // or right before the closing slash/bracket if there are no attributes.\n let insertPos: number | null | undefined = null\n if (node.attributes && node.attributes.length > 0) {\n const firstAttr = node.attributes[0]\n if (firstAttr && firstAttr.start != null) {\n insertPos = firstAttr.start\n }\n }\n\n if (insertPos == null) {\n // Find the start of the closing bracket or self-closing slash\n // We know node.end is the index right after the '>'\n // So we look backwards. But Babel AST doesn't give us exact token positions\n // for the closing tag easily.\n // For a safe fallback, we use node.typeParameters?.end || node.name.end\n if (node.typeParameters && node.typeParameters.end != null) {\n insertPos = node.typeParameters.end\n } else if (node.name.end != null) {\n insertPos = node.name.end\n }\n }\n\n if (insertPos == null) return\n\n ms.appendLeft(\n insertPos,\n ` ${attributeName}=\"${attrValue}\"${node.attributes && node.attributes.length > 0 ? '' : ' '}`,\n )\n changed = true\n },\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n","import type { UnpluginOptions } from '@inspecto-dev/types'\nimport type MagicString from 'magic-string'\n\nexport interface TransformResult {\n code: string\n map: ReturnType<MagicString['generateMap']> | null\n changed: boolean\n}\n\nexport interface NormalizedTransformTarget {\n requestId: string\n filePath: string\n wrapped: boolean\n}\n\n/** Default tags whose JSX elements should NOT receive data-inspecto attributes */\nexport const DEFAULT_ESCAPE_TAGS = new Set([\n 'template',\n 'script',\n 'style',\n // React special elements\n 'Fragment',\n 'React.Fragment',\n 'StrictMode',\n 'React.StrictMode',\n 'Suspense',\n 'React.Suspense',\n 'Profiler',\n 'React.Profiler',\n // React transitions\n 'Transition',\n 'TransitionGroup',\n // Vue built-in components\n 'KeepAlive',\n 'Teleport',\n 'Suspense',\n // Vue router built-ins\n 'RouterView',\n 'RouterLink',\n 'NuxtPage',\n 'NuxtLink',\n])\n\n/** File extensions that contain JSX/TSX syntax */\nexport const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.js', '.ts', '.mjs', '.mts'])\n\nfunction normalizeWebpackModuleRequest(id: string): string {\n return id.replace(/!+$/, '').replace(/^\\((?:app-pages-browser|rsc|ssr)\\)\\/\\.\\//, '')\n}\n\nfunction extractNextModuleRequest(id: string): string | undefined {\n if (!id.includes('next-flight-client-entry-loader.js?')) {\n return undefined\n }\n\n const queryIndex = id.indexOf('?')\n if (queryIndex === -1) {\n return undefined\n }\n\n const params = new URLSearchParams(id.slice(queryIndex + 1).replace(/!+$/, ''))\n for (const entry of params.getAll('modules')) {\n try {\n const parsed = JSON.parse(entry) as { request?: unknown }\n if (typeof parsed.request === 'string' && parsed.request.length > 0) {\n return parsed.request\n }\n } catch {\n continue\n }\n }\n\n return undefined\n}\n\nexport function extractTransformFilePath(requestId: string): NormalizedTransformTarget {\n const normalizedRequestId = normalizeWebpackModuleRequest(requestId)\n const nextModuleRequest = extractNextModuleRequest(normalizedRequestId)\n if (nextModuleRequest) {\n return {\n requestId,\n filePath: nextModuleRequest,\n wrapped: true,\n }\n }\n\n const lastLoaderSeparator = normalizedRequestId.lastIndexOf('!')\n const resourceRequest =\n lastLoaderSeparator >= 0\n ? normalizedRequestId.slice(lastLoaderSeparator + 1)\n : normalizedRequestId\n const queryIndex = resourceRequest.indexOf('?')\n const filePath = queryIndex >= 0 ? resourceRequest.slice(0, queryIndex) : resourceRequest\n\n return {\n requestId,\n filePath,\n wrapped: filePath !== requestId,\n }\n}\n\n/**\n * Determine if a file should be transformed.\n * Always skips node_modules and dist directories.\n */\nexport function shouldTransform(filePath: string, options: Required<UnpluginOptions>): boolean {\n const resolvedFilePath = extractTransformFilePath(filePath).filePath\n\n // Never transform in production\n if (process.env['NODE_ENV'] === 'production') return false\n\n // Skip node_modules always\n if (resolvedFilePath.includes('node_modules')) return false\n\n // Skip virtual modules\n if (resolvedFilePath.startsWith('\\x00')) return false\n\n // Skip dist/build directories\n if (/[/\\\\](dist|build|\\.next|\\.nuxt)[/\\\\]/.test(resolvedFilePath)) return false\n\n // Skip non-code files (like .html, .css)\n const ext = resolvedFilePath.split('.').pop()?.toLowerCase()\n if (ext && !['js', 'jsx', 'ts', 'tsx', 'mjs', 'mts', 'vue'].includes(ext)) {\n return false\n }\n\n // Check user-defined exclude patterns\n // (picomatch integration — see index.ts for how options.exclude is applied)\n\n return true\n}\n\n/**\n * Build the escape tags set from user options merged with defaults.\n */\nexport function buildEscapeTagsSet(escapeTags?: string[]): Set<string> {\n const merged = new Set(DEFAULT_ESCAPE_TAGS)\n if (escapeTags) {\n for (const tag of escapeTags) {\n merged.add(tag)\n }\n }\n return merged\n}\n\n/**\n * Format a source location value for the data-inspecto attribute.\n * Format: \"filepath:line:column\"\n */\nexport function formatAttrValue(file: string, line: number, column: number): string {\n return `${file}:${line}:${column}`\n}\n","import * as vueCompiler from '@vue/compiler-dom'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport type { ElementNode, AttributeNode } from '@vue/compiler-core'\nimport { NodeTypes } from '@vue/compiler-core'\nimport MagicString from 'magic-string'\nimport path from 'node:path'\nimport type { PathType } from '@inspecto-dev/types'\nimport { buildEscapeTagsSet, formatAttrValue, type TransformResult } from './utils.js'\n\nexport interface TransformVueOptions {\n filePath: string\n source: string\n projectRoot: string\n escapeTags?: string[]\n pathType?: PathType\n attributeName?: string\n}\n\n/**\n * Transform Vue SFC source by injecting data-inspecto attributes\n * into template elements.\n *\n * Strategy:\n * 1. Locate the <template> block in the SFC source\n * 2. Parse only the template block with @vue/compiler-dom\n * 3. Walk ElementNode nodes in the AST\n * 4. For each eligible element, inject the attribute using MagicString\n * at the exact offset within the original source\n */\nexport function transformVue(options: TransformVueOptions): TransformResult {\n const {\n filePath,\n source,\n projectRoot,\n escapeTags,\n pathType = 'absolute',\n attributeName = 'data-inspecto',\n } = options\n\n const escapeTagsSet = buildEscapeTagsSet(escapeTags)\n\n // Resolve path\n const resolvedPath =\n pathType === 'absolute'\n ? path.resolve(filePath)\n : path.relative(projectRoot, path.resolve(filePath))\n\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\n\n // ── Find <template> block boundaries ──────────────────────────────────────\n // Use @vue/compiler-sfc to parse the file and extract the template block.\n // This is much safer than regex for handling nested templates.\n const { descriptor, errors } = parseSFC(source, {\n filename: filePath,\n sourceMap: false,\n ignoreEmpty: true,\n })\n\n if (errors.length > 0 || !descriptor.template) {\n return { code: source, map: null, changed: false }\n }\n\n const templateContent = descriptor.template.content\n const templateBlockStart = descriptor.template.loc.start.offset\n\n // ── Parse template block ───────────────────────────────────────────────────\n let ast: vueCompiler.RootNode\n try {\n ast = vueCompiler.parse(templateContent, {\n parseMode: 'html',\n // Preserve source locations relative to templateContent\n onError: () => {\n /* ignore non-fatal parse errors */\n },\n })\n } catch {\n return { code: source, map: null, changed: false }\n }\n\n const ms = new MagicString(source)\n let changed = false\n\n // ── Walk AST ───────────────────────────────────────────────────────────────\n walkElement(ast, node => {\n // Skip non-element nodes\n if (node.type !== NodeTypes.ELEMENT) return\n\n const tagName = node.tag\n\n // Skip escaped tags\n if (escapeTagsSet.has(tagName)) return\n\n // Skip <template> wrapper itself (it's the root, not a real element)\n if (tagName === 'template' && node === ast.children[0]) return\n\n // Skip elements that already have the attribute (idempotency)\n const alreadyHasAttr = node.props.some(\n (p): p is AttributeNode => p.type === NodeTypes.ATTRIBUTE && p.name === attributeName,\n )\n if (alreadyHasAttr) return\n\n // node.loc is relative to templateContent — add templateBlockStart offset\n const loc = node.loc\n if (!loc) return\n\n const { line, column } = loc.start\n\n // Calculate absolute line and column in the original source\n // @vue/compiler-dom uses 1-based line and 1-based column\n const templateStartLoc = descriptor.template!.loc.start\n const absoluteLine = templateStartLoc.line + line - 1\n const absoluteColumn = line === 1 ? templateStartLoc.column + column - 1 : column\n\n const attrValue = formatAttrValue(normalizedPath, absoluteLine, absoluteColumn)\n\n // Find insert position: right after the tag name in the original source\n // node.loc.start.offset is 0-based offset within templateContent\n const tagNameEnd = loc.start.offset + tagName.length + 1 // +1 for '<'\n const absoluteOffset = templateBlockStart + tagNameEnd\n\n ms.appendLeft(absoluteOffset, ` ${attributeName}=\"${attrValue}\"`)\n changed = true\n })\n\n if (!changed) {\n return { code: source, map: null, changed: false }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({ hires: true, source: filePath }),\n changed: true,\n }\n}\n\n// ── AST walker ────────────────────────────────────────────────────────────────\n\ntype AnyNode = vueCompiler.RootNode | vueCompiler.TemplateChildNode\n\nfunction walkElement(node: AnyNode, visitor: (node: ElementNode) => void): void {\n if (node.type === NodeTypes.ELEMENT) {\n visitor(node)\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n } else if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n walkElement(child as AnyNode, visitor)\n }\n }\n}\n","import { transformRouter } from '../../transform/index'\nimport { shouldTransform } from '../../transform/utils'\nimport type { UnpluginOptions } from '@inspecto-dev/types'\n\nconst injectedClient = false\n\nexport default function legacyWebpack4Loader(this: any, source: string) {\n const id = this.resourcePath\n\n // In Webpack 4, loader-utils is typically used, or we read from this.query\n // since this.getOptions() is a Webpack 5+ API.\n let options: any = {}\n if (typeof this.getOptions === 'function') {\n options = this.getOptions()\n } else if (this.query) {\n if (typeof this.query === 'string') {\n // Very fallback query parsing\n options = {}\n } else {\n options = this.query\n }\n }\n\n // Give default include/exclude options\n const defaultOptions: Required<UnpluginOptions> = {\n include: options.include || [/\\.[jt]sx?$/, /\\.vue$/],\n exclude: options.exclude || [/node_modules/, /\\.html$/],\n hotKey: options.hotKey || 'altKey',\n ...options,\n }\n\n if (!shouldTransform(id, defaultOptions)) {\n return source\n }\n\n const result = transformRouter({\n filePath: id,\n source,\n projectRoot: process.cwd(),\n pluginOptions: defaultOptions,\n })\n\n let finalSource = result?.changed ? result.code : source\n\n // Inject the client dynamically into the first source module we process\n // WARNING: We must NOT use process-level state like `let injectedClient = false`\n // because in webpack-dev-server, files are recompiled and the loader might hit\n // multiple entries or the same entry again on HMR.\n // Instead, we inject it conditionally based on a known module that acts as an entry\n // Webpack uses standard regex matching, so let's match any file that has `react-dom` rendering\n\n const isMainEntry =\n finalSource.includes('react-dom/client') ||\n finalSource.includes('react-dom') ||\n /[/\\\\](index|main)\\.[jt]sx?$/.test(id)\n\n if (isMainEntry && finalSource.indexOf('window.InspectoClient') === -1) {\n const clientPath = options.clientPath || '@inspecto-dev/core'\n // For Webpack 4 and Babel environments, using require() is safer than prepending `import`\n // to a file that might be CommonJS, and it doesn't get hoisted irregularly.\n finalSource =\n `\nif (typeof window !== 'undefined' && !window.InspectoClient) {\n try {\n var __inspecto_core__ = require('${clientPath.replace(/\\\\/g, '\\\\\\\\')}');\n window.InspectoClient = __inspecto_core__.default || __inspecto_core__;\n } catch (e) {\n console.error('[inspecto] core load error', e);\n }\n}\n` + finalSource\n }\n\n if (result && result.changed) {\n this.callback(null, finalSource, result.map)\n return\n }\n\n return finalSource\n}\n"],"mappings":"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","names":["path","MagicString","path","path","MagicString","path"]}