From b93728773240a203bab52ec1a20904943b72f072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 10 Aug 2025 09:05:18 +0800 Subject: [PATCH] chore: emit dts by oxc --- packages/babel-plugin-jsx/src/index.ts | 275 +++++++-------- .../babel-plugin-resolve-type/src/index.ts | 317 +++++++++--------- tsdown.config.ts | 7 +- 3 files changed, 309 insertions(+), 290 deletions(-) diff --git a/packages/babel-plugin-jsx/src/index.ts b/packages/babel-plugin-jsx/src/index.ts index 50654a4..09f96cc 100644 --- a/packages/babel-plugin-jsx/src/index.ts +++ b/packages/babel-plugin-jsx/src/index.ts @@ -40,112 +40,118 @@ function interopDefault(m: any) { const syntaxJsx = /*#__PURE__*/ interopDefault(_syntaxJsx); const template = /*#__PURE__*/ interopDefault(_template); -export default declare>( - (api, opt, dirname) => { - const { types } = api; - let resolveType: BabelCore.PluginObj | undefined; - if (opt.resolveType) { - if (typeof opt.resolveType === 'boolean') opt.resolveType = {}; - resolveType = ResolveType(api, opt.resolveType, dirname); - } - return { - ...(resolveType || {}), - name: 'babel-plugin-jsx', - inherits: /*#__PURE__*/ interopDefault(syntaxJsx), - visitor: { - ...(resolveType?.visitor as Visitor), - ...transformVueJSX, - ...sugarFragment, - Program: { - enter(path, state) { - if (hasJSX(path)) { - const importNames = [ - 'createVNode', - 'Fragment', - 'resolveComponent', - 'withDirectives', - 'vShow', - 'vModelSelect', - 'vModelText', - 'vModelCheckbox', - 'vModelRadio', - 'vModelText', - 'vModelDynamic', - 'resolveDirective', - 'mergeProps', - 'createTextVNode', - 'isVNode', - ]; - if (isModule(path)) { - // import { createVNode } from "vue"; - const importMap: Record< - string, - t.MemberExpression | t.Identifier - > = {}; - importNames.forEach((name) => { - state.set(name, () => { - if (importMap[name]) { - return types.cloneNode(importMap[name]); - } - const identifier = addNamed(path, name, 'vue', { - ensureLiveReference: true, - }); - importMap[name] = identifier; - return identifier; +const plugin: ( + api: object, + options: VueJSXPluginOptions | null | undefined, + dirname: string +) => BabelCore.PluginObj = declare< + VueJSXPluginOptions, + BabelCore.PluginObj +>((api, opt, dirname) => { + const { types } = api; + let resolveType: BabelCore.PluginObj | undefined; + if (opt.resolveType) { + if (typeof opt.resolveType === 'boolean') opt.resolveType = {}; + resolveType = ResolveType(api, opt.resolveType, dirname); + } + return { + ...(resolveType || {}), + name: 'babel-plugin-jsx', + inherits: /*#__PURE__*/ interopDefault(syntaxJsx), + visitor: { + ...(resolveType?.visitor as Visitor), + ...transformVueJSX, + ...sugarFragment, + Program: { + enter(path, state) { + if (hasJSX(path)) { + const importNames = [ + 'createVNode', + 'Fragment', + 'resolveComponent', + 'withDirectives', + 'vShow', + 'vModelSelect', + 'vModelText', + 'vModelCheckbox', + 'vModelRadio', + 'vModelText', + 'vModelDynamic', + 'resolveDirective', + 'mergeProps', + 'createTextVNode', + 'isVNode', + ]; + if (isModule(path)) { + // import { createVNode } from "vue"; + const importMap: Record< + string, + t.MemberExpression | t.Identifier + > = {}; + importNames.forEach((name) => { + state.set(name, () => { + if (importMap[name]) { + return types.cloneNode(importMap[name]); + } + const identifier = addNamed(path, name, 'vue', { + ensureLiveReference: true, }); + importMap[name] = identifier; + return identifier; }); - const { enableObjectSlots = true } = state.opts; - if (enableObjectSlots) { - state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => { - if (importMap.runtimeIsSlot) { - return importMap.runtimeIsSlot; - } - const { name: isVNodeName } = state.get( - 'isVNode' - )() as t.Identifier; - const isSlot = path.scope.generateUidIdentifier('isSlot'); - const ast = template.ast` + }); + const { enableObjectSlots = true } = state.opts; + if (enableObjectSlots) { + state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => { + if (importMap.runtimeIsSlot) { + return importMap.runtimeIsSlot; + } + const { name: isVNodeName } = state.get( + 'isVNode' + )() as t.Identifier; + const isSlot = path.scope.generateUidIdentifier('isSlot'); + const ast = template.ast` function ${isSlot.name}(s) { return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s)); } `; - const lastImport = (path.get('body') as NodePath[]) - .filter((p) => p.isImportDeclaration()) - .pop(); - if (lastImport) { - lastImport.insertAfter(ast); - } - importMap.runtimeIsSlot = isSlot; - return isSlot; - }); - } - } else { - // var _vue = require('vue'); - let sourceName: t.Identifier; - importNames.forEach((name) => { - state.set(name, () => { - if (!sourceName) { - sourceName = addNamespace(path, 'vue', { - ensureLiveReference: true, - }); - } - return t.memberExpression(sourceName, t.identifier(name)); - }); + const lastImport = (path.get('body') as NodePath[]) + .filter((p) => p.isImportDeclaration()) + .pop(); + if (lastImport) { + lastImport.insertAfter(ast); + } + importMap.runtimeIsSlot = isSlot; + return isSlot; }); + } + } else { + // var _vue = require('vue'); + let sourceName: t.Identifier; + importNames.forEach((name) => { + state.set(name, () => { + if (!sourceName) { + sourceName = addNamespace(path, 'vue', { + ensureLiveReference: true, + }); + } + return t.memberExpression(sourceName, t.identifier(name)); + }); + }); - const helpers: Record = {}; + const helpers: Record = {}; - const { enableObjectSlots = true } = state.opts; - if (enableObjectSlots) { - state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => { - if (helpers.runtimeIsSlot) { - return helpers.runtimeIsSlot; - } - const isSlot = path.scope.generateUidIdentifier('isSlot'); - const { object: objectName } = state.get( - 'isVNode' - )() as t.MemberExpression; - const ast = template.ast` + const { enableObjectSlots = true } = state.opts; + if (enableObjectSlots) { + state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => { + if (helpers.runtimeIsSlot) { + return helpers.runtimeIsSlot; + } + const isSlot = path.scope.generateUidIdentifier('isSlot'); + const { object: objectName } = state.get( + 'isVNode' + )() as t.MemberExpression; + const ast = template.ast` function ${isSlot.name}(s) { return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${ (objectName as t.Identifier).name @@ -153,46 +159,47 @@ export default declare>( } `; - const nodePaths = path.get('body') as NodePath[]; - const lastImport = nodePaths - .filter( - (p) => - p.isVariableDeclaration() && - p.node.declarations.some( - (d) => - (d.id as t.Identifier)?.name === sourceName.name - ) - ) - .pop(); - if (lastImport) { - lastImport.insertAfter(ast); - } - return isSlot; - }); - } - } - - const { - opts: { pragma = '' }, - file, - } = state; - - if (pragma) { - state.set('createVNode', () => t.identifier(pragma)); - } - - if (file.ast.comments) { - for (const comment of file.ast.comments) { - const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value); - if (jsxMatches) { - state.set('createVNode', () => t.identifier(jsxMatches[1])); + const nodePaths = path.get('body') as NodePath[]; + const lastImport = nodePaths + .filter( + (p) => + p.isVariableDeclaration() && + p.node.declarations.some( + (d) => + (d.id as t.Identifier)?.name === sourceName.name + ) + ) + .pop(); + if (lastImport) { + lastImport.insertAfter(ast); } + return isSlot; + }); + } + } + + const { + opts: { pragma = '' }, + file, + } = state; + + if (pragma) { + state.set('createVNode', () => t.identifier(pragma)); + } + + if (file.ast.comments) { + for (const comment of file.ast.comments) { + const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value); + if (jsxMatches) { + state.set('createVNode', () => t.identifier(jsxMatches[1])); } } } - }, + } }, }, - }; - } -); + }, + }; +}); + +export default plugin; diff --git a/packages/babel-plugin-resolve-type/src/index.ts b/packages/babel-plugin-resolve-type/src/index.ts index 854597a..030a9db 100644 --- a/packages/babel-plugin-resolve-type/src/index.ts +++ b/packages/babel-plugin-resolve-type/src/index.ts @@ -12,173 +12,182 @@ import { declare } from '@babel/helper-plugin-utils'; export { SimpleTypeResolveOptions as Options }; -export default declare(({ types: t }, options) => { - let ctx: SimpleTypeResolveContext | undefined; - let helpers: Set | undefined; +const plugin: ( + api: object, + options: SimpleTypeResolveOptions | null | undefined, + dirname: string +) => BabelCore.PluginObj = + declare(({ types: t }, options) => { + let ctx: SimpleTypeResolveContext | undefined; + let helpers: Set | undefined; - return { - name: 'babel-plugin-resolve-type', - pre(file) { - const filename = file.opts.filename || 'unknown.js'; - helpers = new Set(); - ctx = { - filename: filename, - source: file.code, - options, - ast: file.ast.program.body, - isCE: false, - error(msg, node) { - throw new Error( - `[@vue/babel-plugin-resolve-type] ${msg}\n\n${filename}\n${codeFrameColumns( - file.code, - { - start: { - line: node.loc!.start.line, - column: node.loc!.start.column + 1, - }, - end: { - line: node.loc!.end.line, - column: node.loc!.end.column + 1, - }, - } - )}` - ); - }, - helper(key) { - helpers!.add(key); - return `_${key}`; - }, - getString(node) { - return file.code.slice(node.start!, node.end!); - }, - propsTypeDecl: undefined, - propsRuntimeDefaults: undefined, - propsDestructuredBindings: {}, - emitsTypeDecl: undefined, - }; - }, - visitor: { - CallExpression(path) { - if (!ctx) { - throw new Error( - '[@vue/babel-plugin-resolve-type] context is not loaded.' - ); - } - - const { node } = path; - - if (!t.isIdentifier(node.callee, { name: 'defineComponent' })) return; - if (!checkDefineComponent(path)) return; - - const comp = node.arguments[0]; - if (!comp || !t.isFunction(comp)) return; - - let options = node.arguments[1]; - if (!options) { - options = t.objectExpression([]); - node.arguments.push(options); - } - - node.arguments[1] = processProps(comp, options) || options; - node.arguments[1] = processEmits(comp, node.arguments[1]) || options; + return { + name: 'babel-plugin-resolve-type', + pre(file) { + const filename = file.opts.filename || 'unknown.js'; + helpers = new Set(); + ctx = { + filename: filename, + source: file.code, + options, + ast: file.ast.program.body, + isCE: false, + error(msg, node) { + throw new Error( + `[@vue/babel-plugin-resolve-type] ${msg}\n\n${filename}\n${codeFrameColumns( + file.code, + { + start: { + line: node.loc!.start.line, + column: node.loc!.start.column + 1, + }, + end: { + line: node.loc!.end.line, + column: node.loc!.end.column + 1, + }, + } + )}` + ); + }, + helper(key) { + helpers!.add(key); + return `_${key}`; + }, + getString(node) { + return file.code.slice(node.start!, node.end!); + }, + propsTypeDecl: undefined, + propsRuntimeDefaults: undefined, + propsDestructuredBindings: {}, + emitsTypeDecl: undefined, + }; }, - VariableDeclarator(path) { - inferComponentName(path); + visitor: { + CallExpression(path) { + if (!ctx) { + throw new Error( + '[@vue/babel-plugin-resolve-type] context is not loaded.' + ); + } + + const { node } = path; + + if (!t.isIdentifier(node.callee, { name: 'defineComponent' })) return; + if (!checkDefineComponent(path)) return; + + const comp = node.arguments[0]; + if (!comp || !t.isFunction(comp)) return; + + let options = node.arguments[1]; + if (!options) { + options = t.objectExpression([]); + node.arguments.push(options); + } + + node.arguments[1] = processProps(comp, options) || options; + node.arguments[1] = processEmits(comp, node.arguments[1]) || options; + }, + VariableDeclarator(path) { + inferComponentName(path); + }, }, - }, - post(file) { - for (const helper of helpers!) { - addNamed(file.path, `_${helper}`, 'vue'); + post(file) { + for (const helper of helpers!) { + addNamed(file.path, `_${helper}`, 'vue'); + } + }, + }; + + function inferComponentName( + path: BabelCore.NodePath + ) { + const id = path.get('id'); + const init = path.get('init'); + if (!id || !id.isIdentifier() || !init || !init.isCallExpression()) + return; + + if (!init.get('callee')?.isIdentifier({ name: 'defineComponent' })) + return; + if (!checkDefineComponent(init)) return; + + const nameProperty = t.objectProperty( + t.identifier('name'), + t.stringLiteral(id.node.name) + ); + const { arguments: args } = init.node; + if (args.length === 0) return; + + if (args.length === 1) { + init.node.arguments.push(t.objectExpression([])); } - }, - }; - - function inferComponentName( - path: BabelCore.NodePath - ) { - const id = path.get('id'); - const init = path.get('init'); - if (!id || !id.isIdentifier() || !init || !init.isCallExpression()) return; - - if (!init.get('callee')?.isIdentifier({ name: 'defineComponent' })) return; - if (!checkDefineComponent(init)) return; - - const nameProperty = t.objectProperty( - t.identifier('name'), - t.stringLiteral(id.node.name) - ); - const { arguments: args } = init.node; - if (args.length === 0) return; - - if (args.length === 1) { - init.node.arguments.push(t.objectExpression([])); - } - args[1] = addProperty(t, args[1], nameProperty); - } - - function processProps( - comp: BabelCore.types.Function, - options: - | BabelCore.types.ArgumentPlaceholder - | BabelCore.types.SpreadElement - | BabelCore.types.Expression - ) { - const props = comp.params[0]; - if (!props) return; - - if (props.type === 'AssignmentPattern') { - ctx!.propsTypeDecl = getTypeAnnotation(props.left); - ctx!.propsRuntimeDefaults = props.right; - } else { - ctx!.propsTypeDecl = getTypeAnnotation(props); + args[1] = addProperty(t, args[1], nameProperty); } - if (!ctx!.propsTypeDecl) return; + function processProps( + comp: BabelCore.types.Function, + options: + | BabelCore.types.ArgumentPlaceholder + | BabelCore.types.SpreadElement + | BabelCore.types.Expression + ) { + const props = comp.params[0]; + if (!props) return; - const runtimeProps = extractRuntimeProps(ctx!); - if (!runtimeProps) { - return; + if (props.type === 'AssignmentPattern') { + ctx!.propsTypeDecl = getTypeAnnotation(props.left); + ctx!.propsRuntimeDefaults = props.right; + } else { + ctx!.propsTypeDecl = getTypeAnnotation(props); + } + + if (!ctx!.propsTypeDecl) return; + + const runtimeProps = extractRuntimeProps(ctx!); + if (!runtimeProps) { + return; + } + + const ast = parseExpression(runtimeProps); + return addProperty( + t, + options, + t.objectProperty(t.identifier('props'), ast) + ); } - const ast = parseExpression(runtimeProps); - return addProperty( - t, - options, - t.objectProperty(t.identifier('props'), ast) - ); - } + function processEmits( + comp: BabelCore.types.Function, + options: + | BabelCore.types.ArgumentPlaceholder + | BabelCore.types.SpreadElement + | BabelCore.types.Expression + ) { + const setupCtx = comp.params[1] && getTypeAnnotation(comp.params[1]); + if ( + !setupCtx || + !t.isTSTypeReference(setupCtx) || + !t.isIdentifier(setupCtx.typeName, { name: 'SetupContext' }) + ) + return; - function processEmits( - comp: BabelCore.types.Function, - options: - | BabelCore.types.ArgumentPlaceholder - | BabelCore.types.SpreadElement - | BabelCore.types.Expression - ) { - const setupCtx = comp.params[1] && getTypeAnnotation(comp.params[1]); - if ( - !setupCtx || - !t.isTSTypeReference(setupCtx) || - !t.isIdentifier(setupCtx.typeName, { name: 'SetupContext' }) - ) - return; + const emitType = setupCtx.typeParameters?.params[0]; + if (!emitType) return; - const emitType = setupCtx.typeParameters?.params[0]; - if (!emitType) return; + ctx!.emitsTypeDecl = emitType; + const runtimeEmits = extractRuntimeEmits(ctx!); - ctx!.emitsTypeDecl = emitType; - const runtimeEmits = extractRuntimeEmits(ctx!); + const ast = t.arrayExpression( + Array.from(runtimeEmits).map((e) => t.stringLiteral(e)) + ); + return addProperty( + t, + options, + t.objectProperty(t.identifier('emits'), ast) + ); + } + }); - const ast = t.arrayExpression( - Array.from(runtimeEmits).map((e) => t.stringLiteral(e)) - ); - return addProperty( - t, - options, - t.objectProperty(t.identifier('emits'), ast) - ); - } -}); +export default plugin; function getTypeAnnotation(node: BabelCore.types.Node) { if ( diff --git a/tsdown.config.ts b/tsdown.config.ts index dee05e3..7ecd94c 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -1,10 +1,13 @@ import { defineConfig } from 'tsdown'; export default defineConfig({ - workspace: ['./packages/babel-plugin-jsx', './packages/babel-plugin-resolve-type'], + workspace: [ + './packages/babel-plugin-jsx', + './packages/babel-plugin-resolve-type', + ], entry: ['src/index.ts'], format: ['cjs', 'esm'], - dts: true, + dts: { oxc: true }, target: 'es2015', platform: 'neutral', });