-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathcode.ts
More file actions
102 lines (89 loc) · 3.01 KB
/
Copy pathcode.ts
File metadata and controls
102 lines (89 loc) · 3.01 KB
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import type { Options as ParseOptions } from "recast";
import type {
ASTNode,
GenerateOptions,
ParsedFileNode,
Proxified,
ProxifiedModule,
} from "./types";
import { parse, print, types } from "recast";
import { getBabelParser } from "./babel";
import { detectCodeFormat } from "./format";
import { makeProxyUtils } from "./proxy/_utils";
import { proxifyModule } from "./proxy/module";
import { proxify } from "./proxy/proxify";
const b = types.builders;
export function parseModule<Exports extends object = any>(
code: string,
options?: ParseOptions,
): ProxifiedModule<Exports> {
const node: ParsedFileNode = parse(code, {
parser: options?.parser || getBabelParser(),
...options,
});
return proxifyModule(node, code);
}
export function parseExpression<T>(
code: string,
options?: ParseOptions,
): Proxified<T> {
// Wrapping a standalone comment in "()" makes Babel's parser interpret "/" in
// expression position as a regex literal, causing a SyntaxError. Detect this
// case and prepend `null` so the comment is parsed as a trailing remark on a
// null literal instead.
const isStandaloneComment = /^(?:\/\*[\s\S]*?\*\/|\/\/[^\n\r\u2028\u2029]*)$/.test(
code.trim(),
);
const parseCode = isStandaloneComment ? `${code}\nnull` : `(${code})`;
const root: ParsedFileNode = parse(parseCode, {
parser: options?.parser || getBabelParser(),
...options,
});
let body: ASTNode = root.program.body[0];
if (body.type === "ExpressionStatement") {
const expr = (body as any).expression;
if (isStandaloneComment && (body as any).comments?.length) {
// Transfer comments from ExpressionStatement to the expression node so
// they survive when the node is embedded into another AST.
expr.comments = (body as any).comments;
delete (body as any).comments;
}
body = expr;
}
if ((body as any).extra?.parenthesized) {
(body as any).extra.parenthesized = false;
}
if (isStandaloneComment) {
// proxify() for NullLiteral returns JS `undefined` (NullLiteral has no
// .value in Babel's AST), which loses the comment. Return a proxy with
// $ast pointing to the expression directly so that literalToAst() emits
// it — including the attached comment — when the node is inserted.
return makeProxyUtils(body, {
$type: "comment",
}) as unknown as Proxified<T>;
}
const mod = {
$ast: root,
$code: ` ${code} `,
$type: "module",
} as any as ProxifiedModule;
return proxify(body, mod);
}
export function generateCode(
node: { $ast: ASTNode } | ASTNode | ProxifiedModule<any>,
options: GenerateOptions = {},
): { code: string; map?: any } {
let ast = (node as Proxified).$ast || node;
if (ast.type === "FunctionExpression") {
ast = b.expressionStatement(ast);
}
const formatOptions
= options.format === false || !("$code" in node)
? {}
: detectCodeFormat(node.$code, options.format);
const { code, map } = print(ast, {
...options,
...formatOptions,
});
return { code, map };
}