diff --git a/README.md b/README.md index c0c4d67..ef0154b 100644 --- a/README.md +++ b/README.md @@ -212,13 +212,25 @@ build({ The following show the default values of the configuration ```ts -VueSource({ - // source root path - root: process.cwd(), - - // generate sourceMap - sourceMap: false, -}); +interface Options { + /** + * source root path + * + * @default process.cwd() + */ + root?: string; + /** + * generate sourceMap + * + * @default false + */ + sourceMap?: boolean; + + /** @default '**\/*.{vue,jsx.tsx}' */ + include?: string | RegExp | (string | RegExp)[]; + /** @default 'node_modules/**' */ + exclude?: string | RegExp | (string | RegExp)[]; +} ``` ## Playgrounds diff --git a/package.json b/package.json index 5b736a5..8ef9fd3 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@babel/parser": "^7.22.16", "@babel/plugin-syntax-jsx": "^7.22.5", "@babel/plugin-syntax-typescript": "^7.22.5", + "@rollup/pluginutils": "^5.0.5", "@vue/compiler-dom": "^3.3.4", "magic-string": "^0.30.3", "unplugin": "^1.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8440705..ea7ee5e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@babel/plugin-syntax-typescript': specifier: ^7.22.5 version: 7.22.5(@babel/core@7.22.17) + '@rollup/pluginutils': + specifier: ^5.0.5 + version: 5.0.5 '@vue/compiler-dom': specifier: ^3.3.4 version: 3.3.4 @@ -2783,6 +2786,20 @@ packages: rollup: 3.29.1 dev: true + /@rollup/pluginutils@5.0.5: + resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.1 + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: false + /@sideway/address@4.1.4: resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==} dependencies: @@ -2904,7 +2921,6 @@ packages: /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - dev: true /@types/express-serve-static-core@4.17.36: resolution: {integrity: sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==} diff --git a/src/core/filter_ID.ts b/src/core/filter_ID.ts deleted file mode 100644 index 3f7bcaf..0000000 --- a/src/core/filter_ID.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { TRACE_ID } from './constants'; -import { parse_ID } from './parse_ID'; - -export function filter_ID(id: string) { - const parsed = parse_ID(id); - - if (parsed.isJsx) { - return true; - } - - if (parsed.isSfc) { - const { query } = parsed; - // vue cli | vue-loader - if (query.type === 'template') { - return true; - } - return ( - // vite-plugin-vue - !query[TRACE_ID] && - // rollup-plugin-vue - !query['rollup-plugin-vue'] - ); - } - - return false; -} diff --git a/src/core/index.ts b/src/core/index.ts index 9ed92fc..2788708 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,8 +1,9 @@ -import type { UnpluginFactory } from 'unplugin'; -import { createUnplugin } from 'unplugin'; -import type { Options } from '../types'; -import { filter_ID } from './filter_ID'; +import { type UnpluginFactory, createUnplugin } from 'unplugin'; +import { createFilter } from '@rollup/pluginutils'; +import { type ResolvedOptions, type Options } from '../types'; +import { TRACE_ID } from './constants'; import { transform } from './transform'; +import { parse_ID } from './parse_ID'; export const unpluginFactory: UnpluginFactory = (options = {}) => { if (process.env.NODE_ENV !== 'development') { @@ -12,23 +13,44 @@ export const unpluginFactory: UnpluginFactory = (options = {}) => { } const opts = resolveOptions(options); + const filter = createFilter(opts.include, opts.exclude); return { name: 'unplugin-vue-source', enforce: 'pre', - transformInclude: filter_ID, + transformInclude(id) { + if (filter(id)) { + const parsed = parse_ID(id); + + if (parsed.isSfc) { + const { query } = parsed; + // vue cli | vue-loader + if (query.type === 'template') { + return true; + } + return ( + // vite-plugin-vue + !query[TRACE_ID] && + // rollup-plugin-vue + !query['rollup-plugin-vue'] + ); + } + + return true; + } + }, transform(code, id) { return transform(code, id, opts); }, }; }; -function resolveOptions(options: Options): Required { - const { root = process.cwd(), sourceMap = false } = options; - +function resolveOptions(opts: Options): ResolvedOptions { return { - root, - sourceMap, + root: opts.root ?? process.cwd(), + sourceMap: opts.sourceMap ?? false, + include: opts.include ?? '**/*.{vue,jsx.tsx}', + exclude: opts.exclude ?? 'node_modules/**', }; } diff --git a/src/core/parse_ID.ts b/src/core/parse_ID.ts index 6ba18be..bbf2d64 100644 --- a/src/core/parse_ID.ts +++ b/src/core/parse_ID.ts @@ -2,29 +2,25 @@ import { extname } from 'path'; import { TRACE_ID } from './constants'; export interface VueQuery extends Record { - vue?: boolean; type?: 'script' | 'template' | 'style' | 'custom'; [TRACE_ID]?: string; } export function parse_ID(id: string, root = '') { const [file, rawQuery] = id.split('?', 2); - const ext = extname(file).slice(1); - const isSfc = ext === 'vue'; - const isTsx = ext === 'tsx'; - const isJsx = isTsx || ext === 'jsx'; - const query = Object.fromEntries(new URLSearchParams(rawQuery)) as VueQuery; - if (query.vue != null) { - query.vue = true; + if (ext === 'vue') { + return { + file: file.replace(root, ''), + isSfc: true, + query: Object.fromEntries(new URLSearchParams(rawQuery)) as VueQuery, + }; } return { file: file.replace(root, ''), - isSfc, - isTsx, - isJsx, - query, + isJsx: true, + isTsx: ext.includes('ts'), }; } diff --git a/src/core/transform.ts b/src/core/transform.ts index dc5c38b..f1cc962 100644 --- a/src/core/transform.ts +++ b/src/core/transform.ts @@ -1,24 +1,20 @@ -import type { Position } from '@vue/compiler-dom'; +import { type Position } from '@vue/compiler-dom'; import MagicString from 'magic-string'; -import type { Options } from '../types'; +import { type ResolvedOptions } from '../types'; import { TRACE_ID } from './constants'; 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: Required, -) { +export function transform(code: string, id: string, options: ResolvedOptions) { const { root, sourceMap } = options; const s = new MagicString(code); - const parsed = parse_ID(id, root); + if (parsed.isSfc) { transform_SFC(code, replace); - } else if (parsed.isJsx) { + } else { transform_JSX(code, replace, parsed); } diff --git a/src/core/transform_JSX.ts b/src/core/transform_JSX.ts index a5d03dc..bbbba1e 100644 --- a/src/core/transform_JSX.ts +++ b/src/core/transform_JSX.ts @@ -35,6 +35,7 @@ export function transform_JSX( const { start } = node.loc!; const name = getJSXElementName(nameNode); const offset = start.index + startIndex + name.length + 1; + cb({ ...start, // babel starts at 0, so we need to add 1 diff --git a/src/core/transform_SFC.ts b/src/core/transform_SFC.ts index f9fdbd5..4518657 100644 --- a/src/core/transform_SFC.ts +++ b/src/core/transform_SFC.ts @@ -20,10 +20,8 @@ export function transform_SFC(code: string, cb: (pos: Position) => void) { ) { const { start } = node.loc; const offset = start.offset + node.tag.length + 1; - cb({ - ...start, - offset, - }); + + cb({ ...start, offset }); } }, ], @@ -49,6 +47,8 @@ function resolveJsxOptions(ast: RootNode) { ) as AttributeNode; if (!langProp) return; + // + // const lang = langProp.value?.content; const isTsx = lang === 'tsx'; const isJsx = isTsx || lang === 'jsx'; diff --git a/src/types.ts b/src/types.ts index f749929..c85d956 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,4 +11,11 @@ export interface Options { * @default false */ sourceMap?: boolean; + + /** @default '**\/*.{vue,jsx.tsx}' */ + include?: string | RegExp | (string | RegExp)[]; + /** @default 'node_modules/**' */ + exclude?: string | RegExp | (string | RegExp)[]; } + +export type ResolvedOptions = Required;