forked from withastro/astro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
84 lines (76 loc) · 2.77 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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 type { Plugin } from 'vite';
import type { AstroSettings } from '../@types/astro';
import { isMarkdownFile } from '../core/util.js';
// Check for `Astro.glob()`. Be very forgiving of whitespace. False positives are okay.
const ASTRO_GLOB_REGEX = /Astro2?\s*\.\s*glob\s*\(/;
interface AstroPluginOptions {
settings: AstroSettings;
}
export default function astro(_opts: AstroPluginOptions): Plugin {
return {
name: 'astro:postprocess',
async transform(code, id) {
// Currently only supported in ".astro" and ".md" (or any alternative markdown file extension like `.markdown`) files
if (!id.endsWith('.astro') && !isMarkdownFile(id, { criteria: 'endsWith' })) {
return null;
}
// Optimization: Detect usage with a quick string match.
// Only perform the transform if this function is found
if (!ASTRO_GLOB_REGEX.test(code)) {
return null;
}
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 },
});
visit(ast, {
visitCallExpression: function (path) {
// Filter out anything that isn't `Astro.glob()` or `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')
) {
this.traverse(path);
return;
}
// 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 };
},
};
}