diff --git a/packages/class-prefixer-ast-visitor/src/__tests__/parse-class-arguments.spec.ts b/packages/class-prefixer-ast-visitor/src/__tests__/parse-class-arguments.spec.ts index 308e3ea..c03209a 100644 --- a/packages/class-prefixer-ast-visitor/src/__tests__/parse-class-arguments.spec.ts +++ b/packages/class-prefixer-ast-visitor/src/__tests__/parse-class-arguments.spec.ts @@ -5,6 +5,7 @@ import type { TemplateLiteral, ObjectExpression, ArrayExpression, + CallExpression, } from 'estree'; describe('parseClassArguments', () => { @@ -262,4 +263,125 @@ describe('parseClassArguments', () => { expect(result).toEqual(expectedNode); }); + + it('should handle CallExpression node and update required value', () => { + const originalNode: CallExpression = { + type: 'CallExpression', + optional: false, + callee: { + type: 'Identifier', + name: 'calleeName', + }, + arguments: [ + { + type: 'ArrayExpression', + elements: [ + { + type: 'Literal', + value: 'value second-value', + raw: "'value second-value'", + }, + { + type: 'ObjectExpression', + properties: [ + { + type: 'Property', + key: { + type: 'Literal', + value: 'third-value', + raw: "'third-value'", + }, + value: { + type: 'MemberExpression', + object: { type: 'ThisExpression' }, + property: { + type: 'Identifier', + name: 'actionable', + }, + computed: false, + optional: false, + }, + kind: 'init', + method: false, + shorthand: false, + computed: false, + }, + ], + }, + { + type: 'MemberExpression', + object: { type: 'ThisExpression' }, + property: { + type: 'Identifier', + name: 'rootClass', + }, + computed: false, + optional: false, + }, + ], + }, + ], + }; + + const expectedNode: CallExpression = { + type: 'CallExpression', + optional: false, + callee: { + type: 'Identifier', + name: 'calleeName', + }, + arguments: [ + { + type: 'ArrayExpression', + elements: [ + { + type: 'Literal', + value: `${prefix.value}value ${prefix.value}second-value`, + raw: `'${prefix.value}value ${prefix.value}second-value'`, + }, + { + type: 'ObjectExpression', + properties: [ + { + type: 'Property', + key: { + type: 'Literal', + value: `${prefix.value}third-value`, + raw: `'${prefix.value}third-value'`, + }, + value: { + type: 'MemberExpression', + object: { type: 'ThisExpression' }, + property: { + type: 'Identifier', + name: 'actionable', + }, + computed: false, + optional: false, + }, + kind: 'init', + method: false, + shorthand: false, + computed: false, + }, + ], + }, + { + type: 'MemberExpression', + object: { type: 'ThisExpression' }, + property: { + type: 'Identifier', + name: 'rootClass', + }, + computed: false, + optional: false, + }, + ], + }, + ], + }; + const result = parseClassArguments(originalNode, { prefix }); + + expect(result).toEqual(expectedNode); + }); }); diff --git a/packages/class-prefixer-ast-visitor/src/create-vue-template-visitor.ts b/packages/class-prefixer-ast-visitor/src/create-vue-template-visitor.ts index 9d0420e..a5d6657 100644 --- a/packages/class-prefixer-ast-visitor/src/create-vue-template-visitor.ts +++ b/packages/class-prefixer-ast-visitor/src/create-vue-template-visitor.ts @@ -12,7 +12,7 @@ export const createVueTemplateVisitor = (config: PrefixerOptions): Visitor => ({ if ( node.type === 'Property' && node.key.type === 'Identifier' && - ['staticClass', 'class'].includes(node.key.name) + node.key.name === 'class' ) { return { ...node, diff --git a/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts b/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts index ecf4adb..2df76ba 100644 --- a/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts +++ b/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts @@ -14,7 +14,14 @@ export function parseClassArguments( node: N, options?: PrefixerOptions, ): N { - if (node.type === 'ArrayExpression' && node.elements) { + if (node.type === 'CallExpression' && node.arguments) { + return { + ...node, + arguments: node.arguments.map( + (argument) => argument && parseClassArguments(argument, options), + ), + }; + } else if (node.type === 'ArrayExpression' && node.elements) { return { ...node, elements: node.elements.map( diff --git a/packages/esbuild-plugin-ast-vue/src/__tests__/plugin.spec.ts b/packages/esbuild-plugin-ast-vue/src/__tests__/plugin.spec.ts index 61a5ef7..89696ff 100644 --- a/packages/esbuild-plugin-ast-vue/src/__tests__/plugin.spec.ts +++ b/packages/esbuild-plugin-ast-vue/src/__tests__/plugin.spec.ts @@ -12,7 +12,7 @@ describe('astPluginVue', () => { const virtualPackage = 'ast-plugin-vue'; const pluginOptions: AstParserVueOptions = { - visitor: { + visitors: { enter(node) { if ( node.type === 'CallExpression' && @@ -29,7 +29,7 @@ describe('astPluginVue', () => { if ( node.type === 'Property' && node.key.type === 'Identifier' && - ['staticClass', 'class'].includes(node.key.name) + node.key.name === 'class' ) { return { ...node, diff --git a/packages/esbuild-plugin-ast-vue/src/plugin.ts b/packages/esbuild-plugin-ast-vue/src/plugin.ts index 871e9c8..2477e9e 100644 --- a/packages/esbuild-plugin-ast-vue/src/plugin.ts +++ b/packages/esbuild-plugin-ast-vue/src/plugin.ts @@ -90,7 +90,7 @@ export function astParserVue({ }); return { - contents: parser(code, visitors), + contents: parser(code, [visitors, templateVisitor].flat()), errors: error, resolveDir: dirname, loader: isTs ? 'ts' : 'js', diff --git a/packages/esbuild-plugin-ast-vue/src/script.ts b/packages/esbuild-plugin-ast-vue/src/script.ts index 0f64e47..1b86008 100644 --- a/packages/esbuild-plugin-ast-vue/src/script.ts +++ b/packages/esbuild-plugin-ast-vue/src/script.ts @@ -1,4 +1,8 @@ -import { compileScript } from '@vue/compiler-sfc'; +import { + SFCDescriptor, + SFCScriptBlock, + compileScript, +} from '@vue/compiler-sfc'; import { fromObject } from 'convert-source-map'; import { PartialMessage } from 'esbuild'; @@ -7,6 +11,8 @@ import { getTemplateOptions } from './template'; import type { AstParserVueOptions } from './plugin'; +export const scriptCache = new WeakMap(); + export function resolveScript({ filename, scriptOptions = {}, @@ -21,6 +27,17 @@ export function resolveScript({ sourcemap: boolean; }) { const descriptor = getDescriptorCache(filename); + + const cached = scriptCache.get(descriptor); + + if (cached) { + return { + code: cached.content, + isTs: cached.lang === 'ts', + error: [], + }; + } + const error: PartialMessage[] = []; const { script, scriptSetup } = descriptor; const isTs = @@ -43,14 +60,22 @@ export function resolveScript({ inlineTemplate: true, babelParserPlugins: scriptOptions.babelParserPlugins, templateOptions: descriptor.template - ? getTemplateOptions({ descriptor, options: templateOptions, isProd }) + ? getTemplateOptions({ + descriptor, + options: templateOptions, + isProd, + scopeId, + }) : {}, }); code = res.content; + if (res.map) { code += fromObject(res.map).toComment(); } + + scriptCache.set(descriptor, res); } catch (e: any) { error.push({ text: e.message, diff --git a/packages/esbuild-plugin-ast-vue/src/template.ts b/packages/esbuild-plugin-ast-vue/src/template.ts index 8a98a3e..eefb55f 100644 --- a/packages/esbuild-plugin-ast-vue/src/template.ts +++ b/packages/esbuild-plugin-ast-vue/src/template.ts @@ -7,6 +7,7 @@ import { fromObject } from 'convert-source-map'; import { PartialMessage } from 'esbuild'; import { getDescriptorCache, getId } from './cache'; +import { scriptCache } from './script'; import type { AstParserVueOptions } from './plugin'; @@ -20,10 +21,14 @@ export function resolveTemplate({ isProd: boolean; }) { const descriptor = getDescriptorCache(filename); + const scopeId = getId(filename); - let { code, errors, map } = compileTemplate( - getTemplateOptions({ descriptor, options, isProd }), - ); + let { code, errors, map } = compileTemplate({ + ...getTemplateOptions({ descriptor, options, isProd, scopeId }), + id: scopeId, + source: descriptor.template?.content || '', + filename: descriptor.filename, + }); if (map) { code += fromObject(map).toComment(); @@ -51,27 +56,33 @@ export function getTemplateOptions({ descriptor, options, isProd, + scopeId, }: { descriptor: SFCDescriptor; options: AstParserVueOptions['templateOptions']; isProd: boolean; -}): SFCTemplateCompileOptions { - const filename = descriptor.filename; - const scopeId = getId(filename); + scopeId: string; +}): Omit | undefined { + const block = descriptor.template; + + if (!block) { + return; + } + + const hasScoped = descriptor.styles.some((s) => s.scoped); + const resolvedScript = scriptCache.get(descriptor); return { - source: descriptor.template?.content || '', - filename, id: scopeId, - scoped: descriptor.styles.some((s) => s.scoped), + scoped: hasScoped, isProd, - inMap: descriptor.template?.map, + filename: descriptor.filename, + inMap: block.src ? undefined : block.map, compiler: options?.compiler, - preprocessLang: options?.preprocessLang, - preprocessOptions: options?.preprocessOptions, compilerOptions: { ...options?.compilerOptions, scopeId, + bindingMetadata: resolvedScript ? resolvedScript.bindings : undefined, }, transformAssetUrls: options?.transformAssetUrls, }; diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index ffb2afa..085d3c3 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -2,7 +2,13 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true, - "types": ["@liip/esbuild-plugin-ast"] + "types": [ + "@liip/class-prefixer-ast-visitor", + "@liip/class-prefixer-core", + "@liip/esbuild-plugin-ast", + "@liip/esbuild-plugin-ast-vue", + "@liip/postcss-class-prefixer" + ] }, "include": ["."] }