From cf22160b8f15486cc932bdff0b9cc5d2c92232ac Mon Sep 17 00:00:00 2001 From: Louis Gollut Date: Fri, 16 Aug 2024 11:01:05 +0200 Subject: [PATCH] fix(rollup): Correctly generate source maps --- package-lock.json | 2 +- .../src/__tests__/js-parser.spec.ts | 7 +++--- packages/ast-parsers/src/js-parser.ts | 25 ++++++++++++------- .../src/__tests__/update-literals.spec.ts | 12 +++++++-- .../src/create-visitor.ts | 4 +-- .../src/parse-class-arguments.ts | 5 ++-- .../src/parse-selector-arguments.ts | 15 ++++++++--- .../src/update-literals.ts | 22 +++++++++++----- packages/esbuild-plugin-ast-vue/package.json | 2 +- packages/esbuild-plugin-ast-vue/src/plugin.ts | 12 +++++++-- packages/esbuild-plugin-ast/src/plugin.ts | 2 +- .../src/__tests__/plugin.spec.ts | 3 ++- .../rollup-plugin-ast-transform/src/plugin.ts | 20 ++++++--------- 13 files changed, 86 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index a3a6d52..d63f458 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14195,7 +14195,7 @@ "esbuild": "^0.19.12" }, "peerDependencies": { - "@liip/esbuild-plugin-ast": "0.3.x", + "@liip/esbuild-plugin-ast": "0.4.x", "esbuild": "0.19.x" } }, diff --git a/packages/ast-parsers/src/__tests__/js-parser.spec.ts b/packages/ast-parsers/src/__tests__/js-parser.spec.ts index e0a9cf2..da0f3ba 100644 --- a/packages/ast-parsers/src/__tests__/js-parser.spec.ts +++ b/packages/ast-parsers/src/__tests__/js-parser.spec.ts @@ -4,8 +4,9 @@ import { jsParser } from '../js-parser'; describe('jsParser', () => { const source = 'const value = 1;'; + const file = 'test.js'; it('should return the source if no valid visitor is provided', () => { - expect(jsParser(source, 'test.js', {}).code).toContain(source); + expect(jsParser({ source, file, visitors: {} }).code).toContain(source); }); it('should transform the source if visitor is a valid visitor object', () => { @@ -23,7 +24,7 @@ describe('jsParser', () => { }, }; - expect(jsParser(source, 'test.js', visitor).code).toMatch( + expect(jsParser({ source, file, visitors: visitor }).code).toMatch( 'const value = 2;', ); }); @@ -57,7 +58,7 @@ describe('jsParser', () => { }, ]; - expect(jsParser(source, 'test.js', visitors).code).toMatch( + expect(jsParser({ source, file, visitors }).code).toMatch( 'const expected = 2;', ); }); diff --git a/packages/ast-parsers/src/js-parser.ts b/packages/ast-parsers/src/js-parser.ts index 870a1ac..35a8d13 100644 --- a/packages/ast-parsers/src/js-parser.ts +++ b/packages/ast-parsers/src/js-parser.ts @@ -8,46 +8,50 @@ import { isFunction } from './utils'; import type { Node } from 'estree'; export type AstParserVisitors = Visitor | Visitor[] | undefined; +export type JsParserOptions = { + source: string; + file: string; + visitors?: AstParserVisitors; +}; /** * Create an AST representation of the provided source and use the * visitor object to transform it if provided */ -export function jsParser( - source: string, - file: string, - visitors?: AstParserVisitors, -) { +export function jsParser({ source, file, visitors }: JsParserOptions) { if (!visitors) { /* eslint-disable-next-line no-console */ console.warn( '[esbuildPluginAst]: No javascript visitor provided, the plugin will have no effect.', ); - return { code: source }; + return { code: source, ast: null, map: null }; } const ast = Parser.parse(source, { ecmaVersion: 'latest', sourceType: 'module', + locations: true, }) as Node; - return transformer({ ast, file, visitors }); + return transformer({ ast, file, visitors, source }); } type TransformerOptions = { ast: N; file: string; visitors?: AstParserVisitors; + source: string; }; export function transformer({ ast, file, visitors, + source, }: TransformerOptions) { const visitorArray = Array.isArray(visitors) ? visitors : [visitors]; - let newAst = ast; + let newAst = structuredClone(ast); for (const visitor of visitorArray) { if ( @@ -61,6 +65,9 @@ export function transformer({ } const map = new SourceMapGenerator({ file }); + map.setSourceContent(file, source); + + const code = generate(newAst, { sourceMap: map }); - return { code: generate(newAst, { sourceMap: map }), ast: newAst, map }; + return { code, ast: newAst, map }; } diff --git a/packages/class-prefixer-ast-visitor/src/__tests__/update-literals.spec.ts b/packages/class-prefixer-ast-visitor/src/__tests__/update-literals.spec.ts index 065c58d..cb4098d 100644 --- a/packages/class-prefixer-ast-visitor/src/__tests__/update-literals.spec.ts +++ b/packages/class-prefixer-ast-visitor/src/__tests__/update-literals.spec.ts @@ -25,7 +25,11 @@ describe('parseSelectorArguments', () => { const options = { prefix }; - updateLiterals(originalNode, prefixer as Prefixer, options); + updateLiterals({ + node: originalNode, + prefixer: prefixer as Prefixer, + options, + }); expect(prefixer).toHaveBeenCalledWith(originalNode.value, options); }); @@ -50,7 +54,11 @@ describe('parseSelectorArguments', () => { const options = { prefix }; - updateLiterals(originalNode, prefixer as Prefixer, options); + updateLiterals({ + node: originalNode, + prefixer: prefixer as Prefixer, + options, + }); expect(prefixer).toHaveBeenNthCalledWith( 1, diff --git a/packages/class-prefixer-ast-visitor/src/create-visitor.ts b/packages/class-prefixer-ast-visitor/src/create-visitor.ts index e829621..2f3cf67 100644 --- a/packages/class-prefixer-ast-visitor/src/create-visitor.ts +++ b/packages/class-prefixer-ast-visitor/src/create-visitor.ts @@ -15,7 +15,7 @@ export const createVisitor = (config?: PrefixerOptions): Visitor => ({ * Handle `classPrefixer` expression */ if (node.callee.name === 'classPrefixer') { - return parseClassArguments(node.arguments[0], config); + return parseClassArguments(node.arguments[0], config, node.loc); } /** @@ -31,7 +31,7 @@ export const createVisitor = (config?: PrefixerOptions): Visitor => ({ ); } - return parseSelectorArguments(node.arguments[0], config); + return parseSelectorArguments(node.arguments[0], config, node.loc); } } }, 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 2df76ba..5ca5fb8 100644 --- a/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts +++ b/packages/class-prefixer-ast-visitor/src/parse-class-arguments.ts @@ -2,7 +2,7 @@ import { prefixer, PrefixerOptions } from '@liip/class-prefixer-core'; import { updateLiterals } from './update-literals'; -import type { Node } from 'estree'; +import type { Node, SourceLocation } from 'estree'; const selectorRegex = /[^a-zA-Z\d\s:_-]/; @@ -13,6 +13,7 @@ const selectorRegex = /[^a-zA-Z\d\s:_-]/; export function parseClassArguments( node: N, options?: PrefixerOptions, + loc?: SourceLocation | null, ): N { if (node.type === 'CallExpression' && node.arguments) { return { @@ -53,7 +54,7 @@ export function parseClassArguments( ); } - return updateLiterals(node, prefixer, options) as N; + return updateLiterals({ node, prefixer, options, loc }) as N; } return node; diff --git a/packages/class-prefixer-ast-visitor/src/parse-selector-arguments.ts b/packages/class-prefixer-ast-visitor/src/parse-selector-arguments.ts index 9aaab95..9396dc5 100644 --- a/packages/class-prefixer-ast-visitor/src/parse-selector-arguments.ts +++ b/packages/class-prefixer-ast-visitor/src/parse-selector-arguments.ts @@ -2,18 +2,27 @@ import { selectorPrefixer, PrefixerOptions } from '@liip/class-prefixer-core'; import { updateLiterals } from './update-literals'; -import type { Node } from 'estree'; +import type { Node, SourceLocation } from 'estree'; /** * Parse the argument to produce prefixed selectors. We only accept * string argument as selectors */ -export function parseSelectorArguments(node: Node, options?: PrefixerOptions) { +export function parseSelectorArguments( + node: Node, + options?: PrefixerOptions, + loc?: SourceLocation | null, +) { if (node.type !== 'TemplateLiteral' && node.type !== 'Literal') { throw new Error( `"selectorPrefixer" only accept string or template literal argument. You passed ${node.type}`, ); } - return updateLiterals(node, selectorPrefixer, options); + return updateLiterals({ + node, + prefixer: selectorPrefixer, + options, + loc, + }); } diff --git a/packages/class-prefixer-ast-visitor/src/update-literals.ts b/packages/class-prefixer-ast-visitor/src/update-literals.ts index 822a3d7..e90301f 100644 --- a/packages/class-prefixer-ast-visitor/src/update-literals.ts +++ b/packages/class-prefixer-ast-visitor/src/update-literals.ts @@ -1,14 +1,22 @@ import type { Prefixer, PrefixerOptions } from '@liip/class-prefixer-core'; -import type { Literal, TemplateLiteral } from 'estree'; +import type { Literal, SourceLocation, TemplateLiteral } from 'estree'; + +export type UpdateLiteralsOptions = { + node: Literal | TemplateLiteral; + prefixer: Prefixer; + options?: PrefixerOptions; + loc?: SourceLocation | null; +}; /** * Update `Literal` and `TemplateLiteral` nodes with prefixed value */ -export function updateLiterals( - node: Literal | TemplateLiteral, - prefixer: Prefixer, - options?: PrefixerOptions, -) { +export function updateLiterals({ + node, + prefixer, + options, + loc, +}: UpdateLiteralsOptions) { if (node.type === 'Literal' && typeof node.value === 'string') { const value: string = prefixer(node.value, options); @@ -16,12 +24,14 @@ export function updateLiterals( ...node, value, raw: `'${value}'`, + loc, }; } if (node.type === 'TemplateLiteral') { const newNode: TemplateLiteral = { ...node, + loc, quasis: node.quasis.map((node) => ({ ...node, value: { diff --git a/packages/esbuild-plugin-ast-vue/package.json b/packages/esbuild-plugin-ast-vue/package.json index 28e4af2..bb57820 100644 --- a/packages/esbuild-plugin-ast-vue/package.json +++ b/packages/esbuild-plugin-ast-vue/package.json @@ -49,7 +49,7 @@ "hash-sum": "^2.0.0" }, "peerDependencies": { - "@liip/esbuild-plugin-ast": "0.3.x", + "@liip/esbuild-plugin-ast": "0.4.x", "esbuild": "0.19.x" } } diff --git a/packages/esbuild-plugin-ast-vue/src/plugin.ts b/packages/esbuild-plugin-ast-vue/src/plugin.ts index ab89e0f..4ed2ea0 100644 --- a/packages/esbuild-plugin-ast-vue/src/plugin.ts +++ b/packages/esbuild-plugin-ast-vue/src/plugin.ts @@ -134,7 +134,11 @@ export function esbuildAstParserVue({ } return { - contents: jsParser(code, args.path, availableVisitors).code, + contents: jsParser({ + source: code, + file: args.path, + visitors: availableVisitors, + }).code, errors: error, resolveDir: dirname, loader: 'js', @@ -158,7 +162,11 @@ export function esbuildAstParserVue({ }); return { - contents: jsParser(code, args.path, templateVisitor).code, + contents: jsParser({ + source: code, + file: args.path, + visitors: templateVisitor, + }).code, pluginData: { code }, errors, resolveDir: dirname, diff --git a/packages/esbuild-plugin-ast/src/plugin.ts b/packages/esbuild-plugin-ast/src/plugin.ts index a7aca02..0de3629 100644 --- a/packages/esbuild-plugin-ast/src/plugin.ts +++ b/packages/esbuild-plugin-ast/src/plugin.ts @@ -135,7 +135,7 @@ export function esbuildAstParser({ } return { - contents: jsParser(source, args.path, visitors).code, + contents: jsParser({ source, file: args.path, visitors }).code, loader: 'js', }; }); diff --git a/packages/rollup-plugin-ast-transform/src/__tests__/plugin.spec.ts b/packages/rollup-plugin-ast-transform/src/__tests__/plugin.spec.ts index fb93688..f0165ef 100644 --- a/packages/rollup-plugin-ast-transform/src/__tests__/plugin.spec.ts +++ b/packages/rollup-plugin-ast-transform/src/__tests__/plugin.spec.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { rollup } from 'rollup'; -import astTransform, { PluginAstTransformOptions } from '../plugin'; +import { astTransform, PluginAstTransformOptions } from '../plugin'; describe('astTransform Plugin', () => { const placeholder = 'astParser'; @@ -63,6 +63,7 @@ describe('astTransform Plugin', () => { type: 'Literal', value: argument, raw: `"${argument}"`, + loc: node.loc, }; } diff --git a/packages/rollup-plugin-ast-transform/src/plugin.ts b/packages/rollup-plugin-ast-transform/src/plugin.ts index f04ea68..7d8947c 100644 --- a/packages/rollup-plugin-ast-transform/src/plugin.ts +++ b/packages/rollup-plugin-ast-transform/src/plugin.ts @@ -1,4 +1,6 @@ -import { AstParserVisitors, transformer } from '@liip/ast-parsers'; +import { basename } from 'node:path'; + +import { AstParserVisitors, jsParser } from '@liip/ast-parsers'; import { createFilter, FilterPattern } from '@rollup/pluginutils'; import { Plugin } from 'rollup'; @@ -8,7 +10,7 @@ export type PluginAstTransformOptions = { visitors: AstParserVisitors; }; -export default function astTransform({ +export function astTransform({ include, exclude, visitors, @@ -22,19 +24,13 @@ export default function astTransform({ return null; } - const ast = this.parse(source, { allowReturnOutsideFunction: true }); - - const { - code, - ast: transformedAst, - map, - } = transformer({ - ast, - file: id, + const { code, map } = jsParser({ + source, + file: basename(id), visitors, }); - return { code, ast: transformedAst, map: map.toString() }; + return { code, map: map?.toString() ?? null }; }, }; }