UNPKG

dom-to-code

Version:
201 lines (192 loc) 6.52 kB
'use strict'; const core = require('@babel/core'); const MagicString = require('magic-string'); const constant = require('./dom-to-code.401158b7.cjs'); const compilerDom = require('@vue/compiler-dom'); const vueJsxPlugin = require('@vue/babel-plugin-jsx'); const typescriptPlugin = require('@babel/plugin-transform-typescript'); const importMeta = require('@babel/plugin-syntax-import-meta'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; } const MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString); const vueJsxPlugin__default = /*#__PURE__*/_interopDefaultLegacy(vueJsxPlugin); const typescriptPlugin__default = /*#__PURE__*/_interopDefaultLegacy(typescriptPlugin); const importMeta__default = /*#__PURE__*/_interopDefaultLegacy(importMeta); function createDomAttrLineInfo(filePath, line, column = 0) { return `${constant.DOM_ATTR}="${filePath}:${line}:${column}"`; } function resolveOption(options) { const mode = options.mode || "vue"; if (!constant.SUPPORT_MODE.includes(mode)) throw new Error(`dom-to-code: mode ${mode} is not supported`); const defaultIncludeMap = { vue: [constant.REGEX_VUE_SFC, constant.REGEX_SETUP_SFC, constant.REGEX_JSX_FILE], react: [constant.REGEX_JSX_FILE] }; return { mode, include: options.include || defaultIncludeMap[mode], exclude: options.exclude || [/node_modules/, /\.git/, /\.nuxt/] }; } function parseJSXIdentifier(name) { if (name.type === "JSXIdentifier") return name.name || ""; else if (name.type === "JSXNamespacedName") return parseJSXIdentifier(name.name) || ""; else return `${parseJSXIdentifier(name.object)}.${parseJSXIdentifier(name.property)}`; } function getJsxElementName(openingElement) { return parseJSXIdentifier(openingElement.name); } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } const getTransformResult = (s, id) => { return { code: s?.toString() || "", get map() { return s?.generateMap({ source: id, includeContent: true, hires: true }); } }; }; const transformReact = (code, id) => { if (id.endsWith(".tsx") || id.endsWith(".jsx")) { const transformedCode = code; const s = new MagicString__default(transformedCode); const ast = core.parseSync(code, { configFile: false, filename: id, ast: true, presets: [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ] }); core.traverse(ast, { enter({ node }) { if (node.type === "JSXElement" && !getJsxElementName(node.openingElement).endsWith("Fragment")) { if (node?.openingElement?.name?.object?.name === "React") return; const { start } = node; const { column, line } = node?.loc?.start; const toInsertPosition = (start || 0) + parseJSXIdentifier(node.openingElement.name).length + 1; const content = ` ${createDomAttrLineInfo(id, line, column)}`; s.appendLeft(toInsertPosition, content); } } }); const sourceMap = s.generateMap({ source: id, includeContent: true }); return { code: s.toString(), map: sourceMap }; } }; const EXCLUDE_TAG = ["template", "script", "style"]; async function compileSFCTemplate({ code, id, type }) { const s = new MagicString__default(code); switch (type) { case "template": { const ast = compilerDom.parse(code, { comments: true }); compilerDom.transform(ast, { nodeTransforms: [ (node) => { if (node.type === 1) { if (node.tagType === 0 && !EXCLUDE_TAG.includes(node.tag)) { if (node.loc.source.includes(constant.DOM_ATTR)) return; const insertPosition = node.loc.start.offset + node.tag.length + 1; const { line, column } = node.loc.start; const content = ` ${createDomAttrLineInfo(id, line, column)}`; s.prependLeft( insertPosition, content ); } } } ] }); break; } case "jsx": { const ast = core.parse(code, { babelrc: false, comments: true, plugins: [ importMeta__default, [vueJsxPlugin__default, {}], [ typescriptPlugin__default, { isTSX: true, allowExtensions: true } ] ] }); core.traverse(ast, { enter({ node }) { if (node.type === "JSXElement") { if (node.openingElement.attributes.some( (attr) => attr.type !== "JSXSpreadAttribute" && attr.name.name === constant.DOM_ATTR )) return; const insertPosition = (node.start || 0) + parseJSXIdentifier(node.openingElement.name).length + 1; const { line = 1, column = 0 } = node.loc?.start || {}; const content = ` ${createDomAttrLineInfo(id, line, column)}`; s.prependLeft( insertPosition, content ); } } }); break; } } return getTransformResult(s, id); } function parseVueRequest(id) { const [filename] = id.split("?", 2); const url = new URL(id, "http://domain.inspector"); const query = Object.fromEntries(url.searchParams.entries()); if (query.vue != null) query.vue = true; if (query.src != null) query.src = true; if (query.index != null) query.index = Number(query.index); if (query.raw != null) query.raw = true; if (hasOwn(query, "lang.tsx") || hasOwn(query, "lang.jsx")) query.isJsx = true; return { filename, query }; } function transformVue(code, id) { const { filename, query } = parseVueRequest(id); const isJsx = filename.endsWith(".jsx") || filename.endsWith(".tsx") || filename.endsWith(".vue") && query.isJsx; const isTpl = filename.endsWith(".vue") && query.type !== "style"; if (isJsx || isTpl) return compileSFCTemplate({ code, id: filename, type: isJsx ? "jsx" : "template" }); return { code }; } function transform(code, id, options) { if (options.mode === "vue") return transformVue(code, id); else if (options.mode === "react") return transformReact(code, id); else return code; } exports.resolveOption = resolveOption; exports.transform = transform;