Skip to content

Commit c62c62f

Browse files
committed
refactor: Created printers.ts (2)
1 parent 7d186bf commit c62c62f

6 files changed

Lines changed: 292 additions & 273 deletions

File tree

src/printers.ts

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,140 @@
1-
import type { Printer } from 'prettier';
1+
import { type AstPath, doc as AST, type Printer } from 'prettier';
2+
import { printers as prettierPrinters } from 'prettier/plugins/estree.js';
23

3-
import { printer } from './printers/index.js';
4+
import type { PluginOptions } from './options.js';
5+
import {
6+
checkPrettierIgnore,
7+
docMatchesString,
8+
fixPreviousPrint,
9+
printRawText,
10+
printTemplateContent,
11+
printTemplateTag,
12+
saveCurrentPrintOnSiblingNode,
13+
trimPrinted,
14+
} from './printers/index.js';
15+
import { isGlimmerTemplate, isGlimmerTemplateParent } from './types/glimmer.js';
16+
import { assert } from './utils/assert.js';
417
import { type NodeType, PRINTER_NAME } from './utils/index.js';
518

19+
const printer = prettierPrinters['estree'] as Printer<NodeType>;
20+
21+
function embed(path: AstPath<NodeType>, options: PluginOptions<NodeType>) {
22+
const { node } = path;
23+
24+
return async (
25+
textToDoc: (
26+
text: string,
27+
options: PluginOptions<NodeType>,
28+
) => Promise<AST.builders.Doc>,
29+
) => {
30+
if (node && isGlimmerTemplate(node)) {
31+
if (checkPrettierIgnore(path)) {
32+
return printRawText(path, options);
33+
}
34+
35+
try {
36+
const content = await printTemplateContent(
37+
node.extra.template.contents,
38+
textToDoc,
39+
options,
40+
);
41+
42+
const printed = printTemplateTag(content);
43+
saveCurrentPrintOnSiblingNode(path, printed);
44+
return printed;
45+
} catch (error) {
46+
console.error(error);
47+
const printed = [printRawText(path, options)];
48+
saveCurrentPrintOnSiblingNode(path, printed);
49+
return printed;
50+
}
51+
}
52+
53+
// Nothing to embed, so move on to the regular printer.
54+
return;
55+
};
56+
}
57+
58+
function getVisitorKeys(node: NodeType, nonTraversableKeys: Set<string>) {
59+
if (node && isGlimmerTemplate(node)) {
60+
return [];
61+
}
62+
return printer.getVisitorKeys?.(node, nonTraversableKeys) || [];
63+
}
64+
65+
function print(
66+
path: AstPath<NodeType>,
67+
options: PluginOptions<NodeType>,
68+
print: (path: AstPath<NodeType>) => AST.builders.Doc,
69+
args?: unknown,
70+
) {
71+
const { node } = path;
72+
73+
if (isGlimmerTemplateParent(node)) {
74+
if (checkPrettierIgnore(path)) {
75+
return printRawText(path, options);
76+
} else {
77+
let printed = printer.print(path, options, print, args);
78+
79+
assert('Expected Glimmer doc to be an array', Array.isArray(printed));
80+
trimPrinted(printed);
81+
82+
// Remove semicolons so we can manage them ourselves
83+
if (docMatchesString(printed[0], ';')) {
84+
printed.shift();
85+
}
86+
if (docMatchesString(printed.at(-1), ';')) {
87+
printed.pop();
88+
}
89+
90+
trimPrinted(printed);
91+
92+
// Always remove export default so we start with a blank slate
93+
if (
94+
docMatchesString(printed[0], 'export') &&
95+
docMatchesString(printed[1], 'default')
96+
) {
97+
printed = printed.slice(2);
98+
trimPrinted(printed);
99+
}
100+
101+
if (options.templateExportDefault) {
102+
printed.unshift('export ', 'default ');
103+
}
104+
105+
saveCurrentPrintOnSiblingNode(path, printed);
106+
107+
return printed;
108+
}
109+
}
110+
111+
if (options.semi && node?.extra?.['prevTemplatePrinted']) {
112+
fixPreviousPrint(
113+
node.extra['prevTemplatePrinted'] as AST.builders.Doc[],
114+
path,
115+
options,
116+
print,
117+
args,
118+
);
119+
}
120+
121+
return printer.print(path, options, print, args);
122+
}
123+
124+
function printPrettierIgnored(
125+
path: AstPath<NodeType>,
126+
options: PluginOptions<NodeType>,
127+
) {
128+
return printRawText(path, options);
129+
}
130+
6131
export const printers: Record<string, Printer<NodeType>> = {
7-
[PRINTER_NAME]: printer,
132+
[PRINTER_NAME]: {
133+
...printer,
134+
// @ts-expect-error: Type <...> is not assignable to <...>
135+
embed,
136+
getVisitorKeys,
137+
print,
138+
printPrettierIgnored,
139+
},
8140
};

src/printers/ambiguity.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,54 @@
1-
import type { Node } from '@babel/types';
2-
import type { AstPath, doc, Printer } from 'prettier';
3-
import { printers as estreePrinters } from 'prettier/plugins/estree.js';
1+
import type { AstPath, doc as AST, Printer } from 'prettier';
2+
import { printers as prettierPrinters } from 'prettier/plugins/estree.js';
43

54
import type { PluginOptions } from '../options.js';
65
import { flattenDoc } from '../utils/doc.js';
6+
import type { NodeType } from '../utils/index.js';
77

8-
const estreePrinter = estreePrinters['estree'] as Printer<Node | undefined>;
8+
const printer = prettierPrinters['estree'] as Printer<NodeType>;
99

1010
/**
1111
* Search next non EmptyStatement node and set current print, so we can fix it
1212
* later if its ambiguous
1313
*/
1414
export function saveCurrentPrintOnSiblingNode(
15-
path: AstPath<Node | undefined>,
16-
printed: doc.builders.Doc[],
15+
path: AstPath<NodeType>,
16+
printed: AST.builders.Doc[],
1717
): void {
1818
const { index, siblings } = path;
19-
if (index !== null) {
20-
const nextNode = siblings
21-
?.slice(index + 1)
22-
.find((n) => n?.type !== 'EmptyStatement');
23-
if (nextNode) {
24-
nextNode.extra = nextNode.extra || {};
25-
nextNode.extra['prevTemplatePrinted'] = printed;
26-
}
19+
20+
if (index === null) {
21+
return;
22+
}
23+
24+
const nextNode = siblings
25+
?.slice(index + 1)
26+
.find((n) => n?.type !== 'EmptyStatement');
27+
28+
if (nextNode) {
29+
nextNode.extra = nextNode.extra || {};
30+
nextNode.extra['prevTemplatePrinted'] = printed;
2731
}
2832
}
2933

3034
/** HACK to fix ASI semi-colons. */
3135
export function fixPreviousPrint(
32-
previousTemplatePrinted: doc.builders.Doc[],
33-
path: AstPath<Node | undefined>,
36+
previousTemplatePrinted: AST.builders.Doc[],
37+
path: AstPath<NodeType>,
3438
options: PluginOptions,
35-
print: (path: AstPath<Node | undefined>) => doc.builders.Doc,
39+
print: (path: AstPath<NodeType>) => AST.builders.Doc,
3640
args: unknown,
3741
): void {
38-
const printedSemiFalse = estreePrinter.print(
42+
const printedSemiFalse = printer.print(
3943
path,
4044
{ ...options, semi: false },
4145
print,
4246
args,
4347
);
48+
4449
const flat = flattenDoc(printedSemiFalse);
4550
const previousFlat = flattenDoc(previousTemplatePrinted);
51+
4652
if (flat[0]?.startsWith(';') && previousFlat.at(-1) !== ';') {
4753
previousTemplatePrinted.push(';');
4854
}

src/printers/ignore.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { AstPath } from 'prettier';
2+
3+
import type { NodeType } from '../utils/index.js';
4+
5+
export function checkPrettierIgnore(path: AstPath<NodeType>): boolean {
6+
if (hasPrettierIgnore(path)) {
7+
return true;
8+
}
9+
10+
return (
11+
Boolean(path.getParentNode()) &&
12+
path.callParent((parent) => checkPrettierIgnore(parent))
13+
);
14+
}
15+
16+
export function hasPrettierIgnore(path: AstPath<NodeType>): boolean {
17+
let possibleComment = path.node?.leadingComments?.at(-1)?.value.trim();
18+
19+
// @ts-expect-error Comments exist on node sometimes
20+
if (!path.node?.leadingComments && path.node?.comments) {
21+
// @ts-expect-error Comments exist on node sometimes
22+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
23+
possibleComment = path.node.comments?.at(-1)?.value.trim();
24+
}
25+
26+
return possibleComment === 'prettier-ignore';
27+
}

0 commit comments

Comments
 (0)