Skip to content

Commit

Permalink
fix(ast-vue): Correctly parse en prefix vue3 template classes
Browse files Browse the repository at this point in the history
  • Loading branch information
lgollut committed Oct 11, 2023
1 parent 408bcc9 commit 3def68a
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
TemplateLiteral,
ObjectExpression,
ArrayExpression,
CallExpression,
} from 'estree';

describe('parseClassArguments', () => {
Expand Down Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Prefixer } from '@liip/class-prefixer-core';

import { updateLiterals } from '../update-literals';

import type { Literal, TemplateLiteral } from 'estree';
Expand All @@ -23,7 +25,7 @@ describe('parseSelectorArguments', () => {

const options = { prefix };

updateLiterals(originalNode, prefixer, options);
updateLiterals(originalNode, prefixer as Prefixer, options);

expect(prefixer).toHaveBeenCalledWith(originalNode.value, options);
});
Expand All @@ -48,7 +50,7 @@ describe('parseSelectorArguments', () => {

const options = { prefix };

updateLiterals(originalNode, prefixer, options);
updateLiterals(originalNode, prefixer as Prefixer, options);

expect(prefixer).toHaveBeenNthCalledWith(
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ export function parseClassArguments<N extends Node>(
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(
Expand Down
4 changes: 2 additions & 2 deletions packages/esbuild-plugin-ast-vue/src/__tests__/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('astPluginVue', () => {
const virtualPackage = 'ast-plugin-vue';

const pluginOptions: AstParserVueOptions = {
visitor: {
visitors: {
enter(node) {
if (
node.type === 'CallExpression' &&
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/esbuild-plugin-ast-vue/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
29 changes: 27 additions & 2 deletions packages/esbuild-plugin-ast-vue/src/script.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -7,6 +11,8 @@ import { getTemplateOptions } from './template';

import type { AstParserVueOptions } from './plugin';

export const scriptCache = new WeakMap<SFCDescriptor, SFCScriptBlock | null>();

export function resolveScript({
filename,
scriptOptions = {},
Expand All @@ -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 =
Expand All @@ -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,
Expand Down
35 changes: 23 additions & 12 deletions packages/esbuild-plugin-ast-vue/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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();
Expand Down Expand Up @@ -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<SFCTemplateCompileOptions, 'source'> | 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,
};
Expand Down
8 changes: 7 additions & 1 deletion tsconfig.eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": ["."]
}

0 comments on commit 3def68a

Please sign in to comment.