-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathindex.ts
More file actions
115 lines (95 loc) · 3.11 KB
/
index.ts
File metadata and controls
115 lines (95 loc) · 3.11 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
103
104
105
106
107
108
109
110
111
112
113
114
115
import { traverse } from '@babel/core';
import type {
BlockStatement,
File,
Node,
ObjectExpression,
StaticBlock,
} from '@babel/types';
import type { Parser } from 'prettier';
import { parsers as babelParsers } from 'prettier/plugins/babel.js';
import { PRINTER_NAME } from '../config.js';
import type { Options } from '../options.js';
import { assert } from '../utils/assert.js';
import { preprocess, type Template } from './preprocess.js';
const typescript = babelParsers['babel-ts'] as Parser<Node | undefined>;
/** Converts a node into a GlimmerTemplate node */
function convertNode(
node: BlockStatement | ObjectExpression | StaticBlock,
rawTemplate: Template,
): void {
node.innerComments = [];
node.extra = Object.assign(node.extra ?? {}, {
isGlimmerTemplate: true as const,
template: rawTemplate,
});
}
/** Traverses the AST and replaces the transformed template parts with other AST */
function convertAst(ast: File, templates: Template[]): void {
traverse(ast, {
enter(path) {
const { node } = path;
switch (node.type) {
case 'BlockStatement':
case 'ObjectExpression':
case 'StaticBlock': {
if (
!node.range &&
typeof node.start === 'number' &&
typeof node.end === 'number'
) {
// prettier 3.6.0 onwards doesn't have `node.range`
// as it was removed in babel
node.range = [node.start, node.end];
}
assert('expected range', node.range);
const [start, end] = node.range;
const templateIndex = templates.findIndex((template) => {
const { utf16Range } = template;
if (utf16Range.start === start && utf16Range.end === end) {
return true;
}
return (
node.type === 'ObjectExpression' &&
utf16Range.start === start - 1 &&
utf16Range.end === end + 1
);
});
if (templateIndex === -1) {
return null;
}
const rawTemplate = templates.splice(templateIndex, 1)[0];
if (!rawTemplate) {
throw new Error(
'expected raw template because splice index came from findIndex',
);
}
const index =
node.innerComments?.[0] &&
ast.comments?.indexOf(node.innerComments[0]);
if (ast.comments && index !== undefined && index >= 0) {
ast.comments.splice(index, 1);
}
convertNode(node, rawTemplate);
}
}
return null;
},
});
if (templates.length > 0) {
throw new Error(
`failed to process all templates, ${templates.length} remaining`,
);
}
}
export const parser: Parser<Node | undefined> = {
...typescript,
astFormat: PRINTER_NAME,
async parse(code: string, options: Options): Promise<Node> {
const preprocessed = preprocess(code, options.filepath);
const ast = await typescript.parse(preprocessed.code, options);
assert('expected ast', ast);
convertAst(ast as File, preprocessed.templates);
return ast;
},
};