Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use acorn to postprocess Astro globs #5652

Merged
merged 4 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/five-cups-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Use acorn to postprocess Astro globs
4 changes: 1 addition & 3 deletions packages/astro/e2e/fixtures/pass-js/src/components/React.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;
set: Set<string>;
Expand Down
3 changes: 1 addition & 2 deletions packages/astro/e2e/fixtures/pass-js/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ set.add('test2');
</head>
<body>
<main>
<!-- TODO: support BigInt in `astro:postprocess` -->
<Component client:load obj={obj} num={11} arr={[0, "foo"]} map={map} set={set} />
<Component client:load obj={obj} num={11n} arr={[0, "foo"]} map={map} set={set} />
</main>
</body>
</html>
3 changes: 1 addition & 2 deletions packages/astro/e2e/pass-js.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
3 changes: 2 additions & 1 deletion packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
84 changes: 37 additions & 47 deletions packages/astro/src/vite-plugin-astro-postprocess/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(),
};
}
},
};
}
7 changes: 5 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.