diff --git a/README.md b/README.md index 4bbefd4..6fe6fc9 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,12 @@ build({ The following show the default values of the configuration ```ts -interface Options { +export interface Options { + /** @default '**\/*.{vue,jsx,tsx}' */ + include?: string | RegExp | (string | RegExp)[]; + /** @default 'node_modules/**' */ + exclude?: string | RegExp | (string | RegExp)[]; + /** * source root path * @@ -226,10 +231,12 @@ interface Options { */ sourceMap?: boolean; - /** @default '**\/*.{vue,jsx,tsx}' */ - include?: string | RegExp | (string | RegExp)[]; - /** @default 'node_modules/**' */ - exclude?: string | RegExp | (string | RegExp)[]; + /** + * Array containing the plugins that you want to enable. + * + * @default ['jsx', 'typescript'] + */ + babelParserPlugins?: ParserPlugin[]; } ``` diff --git a/examples/rollup/src/components/Github.vue b/examples/rollup/src/components/Github.vue new file mode 100644 index 0000000..b7787c9 --- /dev/null +++ b/examples/rollup/src/components/Github.vue @@ -0,0 +1,16 @@ + diff --git a/examples/rollup/src/components/HelloWorld.tsx b/examples/rollup/src/components/HelloWorld.tsx index 9902618..2ee292d 100644 --- a/examples/rollup/src/components/HelloWorld.tsx +++ b/examples/rollup/src/components/HelloWorld.tsx @@ -1,4 +1,5 @@ import { defineComponent } from 'vue'; +import Github from './Github.vue'; export default defineComponent({ props: { msg: String }, @@ -10,12 +11,7 @@ export default defineComponent({ Inspect the element to see the __source

- - Github - +

); diff --git a/examples/vite/src/components/Github.vue b/examples/vite/src/components/Github.vue new file mode 100644 index 0000000..b7787c9 --- /dev/null +++ b/examples/vite/src/components/Github.vue @@ -0,0 +1,16 @@ + diff --git a/examples/vite/src/components/HelloWorld.tsx b/examples/vite/src/components/HelloWorld.tsx index 5f6c5e6..b09dbcc 100644 --- a/examples/vite/src/components/HelloWorld.tsx +++ b/examples/vite/src/components/HelloWorld.tsx @@ -1,3 +1,5 @@ +import Github from './Github.vue'; + export default function HelloWorld({ msg }: { msg: string }) { return ( <> @@ -6,12 +8,7 @@ export default function HelloWorld({ msg }: { msg: string }) { Inspect the element to see the __source

- - Github - +

); diff --git a/examples/vite/vite.config.ts b/examples/vite/vite.config.ts index 106ea9f..79778ce 100644 --- a/examples/vite/vite.config.ts +++ b/examples/vite/vite.config.ts @@ -11,4 +11,7 @@ export default defineConfig({ jsxFragment: 'Fragment', jsxInject: "import { h, Fragment } from 'vue';", }, + server: { + port: 3000, + }, }); diff --git a/examples/webpack/src/components/Github.vue b/examples/webpack/src/components/Github.vue new file mode 100644 index 0000000..b7787c9 --- /dev/null +++ b/examples/webpack/src/components/Github.vue @@ -0,0 +1,16 @@ + diff --git a/examples/webpack/src/components/HelloWorld.tsx b/examples/webpack/src/components/HelloWorld.tsx index 5f6c5e6..b09dbcc 100644 --- a/examples/webpack/src/components/HelloWorld.tsx +++ b/examples/webpack/src/components/HelloWorld.tsx @@ -1,3 +1,5 @@ +import Github from './Github.vue'; + export default function HelloWorld({ msg }: { msg: string }) { return ( <> @@ -6,12 +8,7 @@ export default function HelloWorld({ msg }: { msg: string }) { Inspect the element to see the __source

- - Github - +

); diff --git a/examples/webpack/vue.config.js b/examples/webpack/vue.config.js index 108b8ab..3d2c4a3 100644 --- a/examples/webpack/vue.config.js +++ b/examples/webpack/vue.config.js @@ -1,9 +1,12 @@ -const { defineConfig } = require("@vue/cli-service"); +const { defineConfig } = require('@vue/cli-service'); module.exports = defineConfig({ configureWebpack: { plugins: [ - require("unplugin-vue-source/webpack")(), - require("unplugin-vue-jsx/webpack")(), + require('unplugin-vue-source/webpack')(), + require('unplugin-vue-jsx/webpack')(), ], }, + devServer: { + port: 3000, + }, }); diff --git a/src/core/index.ts b/src/core/index.ts index 379fecb..648a0fc 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,7 +1,6 @@ import { type UnpluginFactory, createUnplugin } from 'unplugin'; import { createFilter } from '@rollup/pluginutils'; import { type ResolvedOptions, type Options } from '../types'; -import { TRACE_ID } from './constants'; import { isDev } from './isDev'; import { parse_ID } from './parse_ID'; import { transform } from './transform'; @@ -21,21 +20,8 @@ export const unpluginFactory: UnpluginFactory = (options = {}) => { enforce: 'pre', transformInclude(id) { - const { file, isSfc, query } = parse_ID(id); - - if (query.raw == null && filter(file)) { - if (isSfc && query.type !== 'template') { - return ( - // vite-plugin-vue - !query[TRACE_ID] && - // rollup-plugin-vue - !query['rollup-plugin-vue'] - ); - } - - // vue cli | vue-loader | tsx | jsx - return true; - } + const { file, query } = parse_ID(id); + return query.raw == null && filter(file); }, transform(code, id) { return transform(code, id, opts); @@ -45,10 +31,11 @@ export const unpluginFactory: UnpluginFactory = (options = {}) => { function resolveOptions(opts: Options): ResolvedOptions { return { - root: opts.root ?? process.cwd(), - sourceMap: opts.sourceMap ?? false, include: opts.include ?? '**/*.{vue,jsx,tsx}', exclude: opts.exclude ?? 'node_modules/**', + root: opts.root ?? process.cwd(), + sourceMap: opts.sourceMap ?? false, + babelParserPlugins: opts.babelParserPlugins ?? [], }; } diff --git a/src/core/parse_ID.ts b/src/core/parse_ID.ts index fc5039c..befb97c 100644 --- a/src/core/parse_ID.ts +++ b/src/core/parse_ID.ts @@ -12,18 +12,10 @@ export function parse_ID(id: string, root = '') { const ext = extname(file).slice(1); const query = Object.fromEntries(new URLSearchParams(rawQuery)) as VueQuery; - if (ext === 'vue') { - return { - file: file.replace(root, ''), - isSfc: true, - query, - }; - } - return { file: file.replace(root, ''), - isJsx: true, - isTsx: ext.includes('ts'), + isSfc: ext === 'vue', + isTsx: ext.startsWith('ts'), query, }; } diff --git a/src/core/transform.ts b/src/core/transform.ts index f1cc962..1142272 100644 --- a/src/core/transform.ts +++ b/src/core/transform.ts @@ -6,16 +6,20 @@ import { parse_ID } from './parse_ID'; import { transform_SFC } from './transform_SFC'; import { transform_JSX } from './transform_JSX'; -export function transform(code: string, id: string, options: ResolvedOptions) { - const { root, sourceMap } = options; +const skipRE = new RegExp(` ${TRACE_ID}=['"].+:[0-9]+:[0-9]+['"]`); + +export function transform(code: string, id: string, opts: ResolvedOptions) { + if (skipRE.test(code)) return; + + const { root, sourceMap } = opts; const s = new MagicString(code); const parsed = parse_ID(id, root); if (parsed.isSfc) { - transform_SFC(code, replace); + transform_SFC(code, replace, opts); } else { - transform_JSX(code, replace, parsed); + transform_JSX(code, replace, parsed, opts); } function replace(pos: Position) { diff --git a/src/core/transform_JSX.ts b/src/core/transform_JSX.ts index bbbba1e..0c66c47 100644 --- a/src/core/transform_JSX.ts +++ b/src/core/transform_JSX.ts @@ -1,6 +1,7 @@ -import type { Position } from '@vue/compiler-dom'; +import { type Position } from '@vue/compiler-dom'; import { traverse, types as t } from '@babel/core'; import { parse, ParserPlugin } from '@babel/parser'; +import { type ResolvedOptions } from '../types'; export function transform_JSX( code: string, @@ -11,17 +12,19 @@ export function transform_JSX( startLine?: number; startColumn?: number; }, + opts: ResolvedOptions, ) { const { isTsx, startIndex = 0, startLine = 1, startColumn = 1 } = options; - const plugins: ParserPlugin[] = ['jsx']; + const pluginSet = new Set(opts.babelParserPlugins); + pluginSet.add('jsx'); if (isTsx) { - plugins.push('typescript'); + pluginSet.add('typescript'); } const ast = parse(code, { sourceType: 'unambiguous', - plugins, + plugins: [...pluginSet] as ParserPlugin[], startLine, // babel start at 0 startColumn: startColumn - 1, diff --git a/src/core/transform_SFC.ts b/src/core/transform_SFC.ts index 4518657..5e2b6ae 100644 --- a/src/core/transform_SFC.ts +++ b/src/core/transform_SFC.ts @@ -1,15 +1,21 @@ -import type { - ElementNode, - AttributeNode, - Position, - RootNode, - TextNode, +import { + type ElementNode, + type AttributeNode, + type Position, + type RootNode, + type TextNode, + parse, + transform, } from '@vue/compiler-dom'; -import { parse, transform } from '@vue/compiler-dom'; +import { type ResolvedOptions } from '../types'; import { NodeTypes, TagTypes } from './constants'; import { transform_JSX } from './transform_JSX'; -export function transform_SFC(code: string, cb: (pos: Position) => void) { +export function transform_SFC( + code: string, + cb: (pos: Position) => void, + opts: ResolvedOptions, +) { const ast = parse(code); transform(ast, { nodeTransforms: [ @@ -27,13 +33,13 @@ export function transform_SFC(code: string, cb: (pos: Position) => void) { ], }); - const jsxOpts = resolveJsxOptions(ast); + const jsxOpts = resolveJsxOptsByScript(ast); if (jsxOpts) { - transform_JSX(jsxOpts.code, cb, jsxOpts); + transform_JSX(jsxOpts.code, cb, jsxOpts, opts); } } -function resolveJsxOptions(ast: RootNode) { +function resolveJsxOptsByScript(ast: RootNode) { const scriptNode = (ast.children as ElementNode[]).find( (node) => node.tag === 'script', ); diff --git a/src/types.ts b/src/types.ts index 5371bc9..9bc3dda 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,11 @@ +import { ParserPlugin } from '@babel/parser'; + export interface Options { + /** @default '**\/*.{vue,jsx,tsx}' */ + include?: string | RegExp | (string | RegExp)[]; + /** @default 'node_modules/**' */ + exclude?: string | RegExp | (string | RegExp)[]; + /** * source root path * @@ -12,10 +19,12 @@ export interface Options { */ sourceMap?: boolean; - /** @default '**\/*.{vue,jsx,tsx}' */ - include?: string | RegExp | (string | RegExp)[]; - /** @default 'node_modules/**' */ - exclude?: string | RegExp | (string | RegExp)[]; + /** + * Array containing the plugins that you want to enable. + * + * @default ['jsx', 'typescript'] + */ + babelParserPlugins?: ParserPlugin[]; } export type ResolvedOptions = Required; diff --git a/test/fixtures.test.ts b/test/fixtures.test.ts index e02b09d..07274c8 100644 --- a/test/fixtures.test.ts +++ b/test/fixtures.test.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'node:fs'; -import { extname, join, resolve } from 'node:path'; +import { basename, dirname, extname, join, resolve } from 'node:path'; import { expect, test } from 'vitest'; import { format, Options } from 'prettier'; import fg from 'fast-glob'; @@ -19,7 +19,11 @@ test.each(fixtures)('transform %s', async (name) => { const input = readCodeString(filename); const output = readCodeString(filename, `output${extname(filename)}`); - const result = transform(input, filename, options); + const result = transform(input, filename, options)!; + + if (basename(dirname(name)) === 'vue-skip') { + return expect(result).toBeUndefined(); + } expect( await formatCode(result.code, { @@ -35,7 +39,7 @@ test.each(fixtures)('transform %s', async (name) => { }), ).toEqual(sourceMap); } else { - expect(result.map).toBe(null); + expect(result.map).toBeNull(); } }); diff --git a/test/fixtures/vue-skip/input.vue b/test/fixtures/vue-skip/input.vue new file mode 100644 index 0000000..a1364ce --- /dev/null +++ b/test/fixtures/vue-skip/input.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/vue-skip/options.ts b/test/fixtures/vue-skip/options.ts new file mode 100644 index 0000000..19d8e2b --- /dev/null +++ b/test/fixtures/vue-skip/options.ts @@ -0,0 +1,4 @@ +export default { + root: process.cwd(), + sourceMap: false, +}; diff --git a/test/fixtures/vue-skip/output.vue b/test/fixtures/vue-skip/output.vue new file mode 100644 index 0000000..a1364ce --- /dev/null +++ b/test/fixtures/vue-skip/output.vue @@ -0,0 +1,3 @@ +