From 0b50987584120e0c0e549f9ff838dc8879dbfa30 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 21 Dec 2022 11:05:18 +0800 Subject: [PATCH] Use acorn to postprocess Astro globs (#5652) --- .changeset/five-cups-battle.md | 5 ++ .../fixtures/pass-js/src/components/React.tsx | 4 +- .../fixtures/pass-js/src/pages/index.astro | 3 +- packages/astro/e2e/pass-js.test.js | 3 +- packages/astro/package.json | 3 +- .../vite-plugin-astro-postprocess/index.ts | 84 ++++++++----------- pnpm-lock.yaml | 7 +- 7 files changed, 52 insertions(+), 57 deletions(-) create mode 100644 .changeset/five-cups-battle.md diff --git a/.changeset/five-cups-battle.md b/.changeset/five-cups-battle.md new file mode 100644 index 0000000000..f4a92050a3 --- /dev/null +++ b/.changeset/five-cups-battle.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Use acorn to postprocess Astro globs diff --git a/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx b/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx index 169e1a61f4..02023fc9d6 100644 --- a/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx +++ b/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx @@ -3,9 +3,7 @@ import { useState } from 'react'; interface Props { obj: BigNestedObject; - // TODO: support BigInt in `astro:postprocess` - // num: bigint; - num: number; + num: bigint; arr: any[]; map: Map; set: Set; diff --git a/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro b/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro index 830fb9c876..be40948d82 100644 --- a/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro +++ b/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro @@ -30,8 +30,7 @@ set.add('test2');
- - +
diff --git a/packages/astro/e2e/pass-js.test.js b/packages/astro/e2e/pass-js.test.js index 741c888005..503f8274bf 100644 --- a/packages/astro/e2e/pass-js.test.js +++ b/packages/astro/e2e/pass-js.test.js @@ -32,8 +32,7 @@ test.describe('Passing JS into client components', () => { await expect(regExpValue).toHaveText('ok'); }); - // TODO: support BigInt in `astro:postprocess` - test.skip('BigInts', async ({ page }) => { + test('BigInts', async ({ page }) => { await page.goto('/'); const bigIntType = await page.locator('#bigint-type'); diff --git a/packages/astro/package.json b/packages/astro/package.json index de90434824..070ef94dd7 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -124,6 +124,7 @@ "@types/babel__core": "^7.1.19", "@types/html-escaper": "^3.0.0", "@types/yargs-parser": "^21.0.0", + "acorn": "^8.8.1", "boxen": "^6.2.1", "ci-info": "^3.3.1", "common-ancestor-path": "^1.0.1", @@ -133,6 +134,7 @@ "devalue": "^4.2.0", "diff": "^5.1.0", "es-module-lexer": "^1.1.0", + "estree-walker": "^3.0.1", "execa": "^6.1.0", "fast-glob": "^3.2.11", "github-slugger": "^1.4.0", @@ -192,7 +194,6 @@ "@types/rimraf": "^3.0.2", "@types/send": "^0.17.1", "@types/unist": "^2.0.6", - "ast-types": "^0.14.2", "astro-scripts": "workspace:*", "chai": "^4.3.6", "cheerio": "^1.0.0-rc.11", diff --git a/packages/astro/src/vite-plugin-astro-postprocess/index.ts b/packages/astro/src/vite-plugin-astro-postprocess/index.ts index c140ad72e3..435457a5d5 100644 --- a/packages/astro/src/vite-plugin-astro-postprocess/index.ts +++ b/packages/astro/src/vite-plugin-astro-postprocess/index.ts @@ -1,7 +1,6 @@ -import { parse as babelParser } from '@babel/parser'; -import type { ArrowFunctionExpressionKind, CallExpressionKind } from 'ast-types/gen/kinds'; -import type { NodePath } from 'ast-types/lib/node-path'; -import { parse, print, types, visit } from 'recast'; +import { parse } from 'acorn'; +import { walk } from 'estree-walker'; +import MagicString from 'magic-string'; import type { Plugin } from 'vite'; import type { AstroSettings } from '../@types/astro'; import { isMarkdownFile } from '../core/util.js'; @@ -28,57 +27,48 @@ export default function astro(_opts: AstroPluginOptions): Plugin { return null; } + let s: MagicString | undefined; const ast = parse(code, { - // We need to use the babel parser because `import.meta.hot` is not - // supported by esprima (default parser). In the future, we should - // experiment with other parsers if Babel is too slow or heavy. - parser: { parse: babelParser }, + ecmaVersion: 'latest', + sourceType: 'module', }); - - visit(ast, { - visitCallExpression: function (path) { - // Filter out anything that isn't `Astro.glob()` or `Astro2.glob()` + walk(ast, { + enter(node: any) { + // Transform `Astro.glob("./pages/*.astro")` to `Astro.glob(import.meta.glob("./pages/*.astro"), () => "./pages/*.astro")` + // Also handle for `Astro2.glob()` if ( - !types.namedTypes.MemberExpression.check(path.node.callee) || - !types.namedTypes.Identifier.check(path.node.callee.property) || - !(path.node.callee.property.name === 'glob') || - !types.namedTypes.Identifier.check(path.node.callee.object) || - !(path.node.callee.object.name === 'Astro' || path.node.callee.object.name === 'Astro2') + node.type === 'CallExpression' && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'glob' && + (node.callee.object.name === 'Astro' || node.callee.object.name === 'Astro2') && + node.arguments.length ) { - this.traverse(path); - return; + const firstArgStart = node.arguments[0].start; + const firstArgEnd = node.arguments[0].end; + const lastArgEnd = node.arguments[node.arguments.length - 1].end; + let firstArg = code.slice(firstArgStart, firstArgEnd); + // If argument is template literal, try convert to a normal string. + // This is needed for compat with previous recast strategy. + // TODO: Remove in Astro 2.0 + if (firstArg.startsWith('`') && firstArg.endsWith('`') && !firstArg.includes('${')) { + firstArg = JSON.stringify(firstArg.slice(1, -1)); + } + s ??= new MagicString(code); + s.overwrite( + firstArgStart, + lastArgEnd, + `import.meta.glob(${firstArg}), () => ${firstArg}` + ); } - - // Wrap the `Astro.glob()` argument with `import.meta.glob`. - const argsPath = path.get('arguments', 0) as NodePath; - const args = argsPath.value; - argsPath.replace( - { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { - type: 'MetaProperty', - meta: { type: 'Identifier', name: 'import' }, - property: { type: 'Identifier', name: 'meta' }, - }, - property: { type: 'Identifier', name: 'glob' }, - computed: false, - }, - arguments: [args], - } as CallExpressionKind, - { - type: 'ArrowFunctionExpression', - body: args, - params: [], - } as ArrowFunctionExpressionKind - ); - return false; }, }); - const result = print(ast); - return { code: result.code, map: result.map }; + if (s) { + return { + code: s.toString(), + map: s.generateMap(), + }; + } }, }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf302cdf7f..9abcaf0a39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -422,7 +422,7 @@ importers: '@types/send': ^0.17.1 '@types/unist': ^2.0.6 '@types/yargs-parser': ^21.0.0 - ast-types: ^0.14.2 + acorn: ^8.8.1 astro-scripts: workspace:* boxen: ^6.2.1 chai: ^4.3.6 @@ -436,6 +436,7 @@ importers: diff: ^5.1.0 eol: ^0.9.1 es-module-lexer: ^1.1.0 + estree-walker: ^3.0.1 execa: ^6.1.0 fast-glob: ^3.2.11 github-slugger: ^1.4.0 @@ -500,6 +501,7 @@ importers: '@types/babel__core': 7.1.20 '@types/html-escaper': 3.0.0 '@types/yargs-parser': 21.0.0 + acorn: 8.8.1 boxen: 6.2.1 ci-info: 3.6.1 common-ancestor-path: 1.0.1 @@ -509,6 +511,7 @@ importers: devalue: 4.2.0 diff: 5.1.0 es-module-lexer: 1.1.0 + estree-walker: 3.0.1 execa: 6.1.0 fast-glob: 3.2.12 github-slugger: 1.5.0 @@ -567,7 +570,6 @@ importers: '@types/rimraf': 3.0.2 '@types/send': 0.17.1 '@types/unist': 2.0.6 - ast-types: 0.14.2 astro-scripts: link:../../scripts chai: 4.3.7 cheerio: 1.0.0-rc.12 @@ -10703,6 +10705,7 @@ packages: engines: {node: '>=4'} dependencies: tslib: 2.4.1 + dev: false /astring/1.8.3: resolution: {integrity: sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==}