update plugin to support prettier v3

This commit is contained in:
davidodenwald 2023-08-12 20:32:28 +02:00
parent ac512dd0b3
commit be28ec949f
6 changed files with 1107 additions and 1059 deletions

1980
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,19 +25,19 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"watch": "npm run build -- --watch", "watch": "npm run build -- --watch",
"test": "jest --verbose", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --verbose",
"test:watch": "jest --watch --verbose", "test:watch": "jest --watch --verbose",
"publish:release": "npm run test && npm run build && npm publish" "publish:release": "npm run test && npm run build && npm publish"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-typescript": "^7.21.5", "@babel/preset-typescript": "^7.22.5",
"@types/jest": "^29.5.1", "@types/jest": "^29.5.3",
"jest": "^29.5.0", "jest": "^29.6.2",
"prettier": "^2.8.8", "prettier": "^3.0.1",
"ts-jest": "^29.1.0", "ts-jest": "^29.1.1",
"typescript": "^5.0.4" "typescript": "^5.1.6"
}, },
"peerDependencies": { "peerDependencies": {
"prettier": "^2.0.0" "prettier": "^3.0.0"
} }
} }

View file

@ -1,6 +1,6 @@
import { Node } from "./jinja"; import { Node } from "./jinja";
import { parse } from "./parser"; import { parse } from "./parser";
import { print, embed } from "./printer"; import { print, embed, getVisitorKeys } from "./printer";
import { Parser, Printer, SupportLanguage } from "prettier"; import { Parser, Printer, SupportLanguage } from "prettier";
const PLUGIN_KEY = "jinja-template"; const PLUGIN_KEY = "jinja-template";
@ -27,5 +27,6 @@ export const printers = {
[PLUGIN_KEY]: <Printer<Node>>{ [PLUGIN_KEY]: <Printer<Node>>{
print, print,
embed, embed,
getVisitorKeys,
}, },
}; };

View file

@ -1,4 +1,4 @@
import { AstPath, Printer } from "prettier"; import { AstPath, Printer, Options, Doc } from "prettier";
import { builders, utils } from "prettier/doc"; import { builders, utils } from "prettier/doc";
import { Placeholder, Node, Expression, Statement, Block } from "./jinja"; import { Placeholder, Node, Expression, Statement, Block } from "./jinja";
@ -6,6 +6,19 @@ const NOT_FOUND = -1;
process.env.PRETTIER_DEBUG = "true"; process.env.PRETTIER_DEBUG = "true";
export const getVisitorKeys = (
ast: Node | { [id: string]: Node },
): string[] => {
if ("type" in ast) {
return ast.type === "root" ? ["nodes"] : [];
}
return Object.values(ast)
.filter((node) => {
return node.type === "block";
})
.map((e) => e.id);
};
export const print: Printer<Node>["print"] = (path) => { export const print: Printer<Node>["print"] = (path) => {
const node = path.getNode(); const node = path.getNode();
if (!node) { if (!node) {
@ -40,7 +53,7 @@ const printExpression = (node: Expression): builders.Doc => {
]), ]),
{ {
shouldBreak: node.preNewLines > 0, shouldBreak: node.preNewLines > 0,
} },
); );
return node.preNewLines > 1 return node.preNewLines > 1
@ -61,7 +74,7 @@ const printStatement = (node: Statement): builders.Doc => {
? [builders.hardline, node.delimiter, "%}"] ? [builders.hardline, node.delimiter, "%}"]
: [node.delimiter, "%}"], : [node.delimiter, "%}"],
]), ]),
{ shouldBreak: node.preNewLines > 0 } { shouldBreak: node.preNewLines > 0 },
); );
if ( if (
@ -87,76 +100,80 @@ const printIgnoreBlock = (node: Node): builders.Doc => {
return node.content; return node.content;
}; };
export const embed: Printer<Node>["embed"] = ( export const embed: Printer<Node>["embed"] = () => {
path, return _embed;
print, };
textToDoc,
options const _embed = async (
) => { textToDoc: (text: string, options: Options) => Promise<Doc>,
print: (selector?: string | number | Array<string | number> | AstPath) => Doc,
path: AstPath,
options: Options,
): Promise<Doc | undefined> => {
const node = path.getNode(); const node = path.getNode();
if (!node || !["root", "block"].includes(node.type)) { if (!node || !["root", "block"].includes(node.type)) {
return null; return undefined;
} }
const mapped = splitAtElse(node).map((content) => { const mapped = await Promise.all(
let doc; splitAtElse(node).map(async (content) => {
if (content in node.nodes) { let doc;
doc = content; if (content in node.nodes) {
} else { doc = content;
doc = utils.stripTrailingHardline( } else {
textToDoc(content, { doc = await textToDoc(content, {
...options, ...options,
parser: "html", parser: "html",
}) });
);
}
let ignoreDoc = false;
return utils.mapDoc(doc, (currentDoc) => {
if (typeof currentDoc !== "string") {
return currentDoc;
} }
if (currentDoc === "<!-- prettier-ignore -->") { let ignoreDoc = false;
ignoreDoc = true;
return currentDoc; return utils.mapDoc(doc, (currentDoc) => {
} if (typeof currentDoc !== "string") {
return currentDoc;
}
if (currentDoc === "<!-- prettier-ignore -->") {
ignoreDoc = true;
return currentDoc;
}
const idxs = findPlaceholders(currentDoc).filter(
([start, end]) => currentDoc.slice(start, end + 1) in node.nodes,
);
if (!idxs.length) {
ignoreDoc = false;
return currentDoc;
}
const res: builders.Doc = [];
let lastEnd = 0;
for (const [start, end] of idxs) {
if (lastEnd < start) {
res.push(currentDoc.slice(lastEnd, start));
}
const p = currentDoc.slice(start, end + 1) as string;
if (ignoreDoc) {
res.push(node.nodes[p].originalText);
} else {
res.push(path.call(print, "nodes", p));
}
lastEnd = end + 1;
}
if (lastEnd > 0 && currentDoc.length > lastEnd) {
res.push(currentDoc.slice(lastEnd));
}
const idxs = findPlaceholders(currentDoc).filter(
([start, end]) => currentDoc.slice(start, end + 1) in node.nodes
);
if (!idxs.length) {
ignoreDoc = false; ignoreDoc = false;
return currentDoc; return res;
} });
}),
const res: builders.Doc = []; );
let lastEnd = 0;
for (const [start, end] of idxs) {
if (lastEnd < start) {
res.push(currentDoc.slice(lastEnd, start));
}
const p = currentDoc.slice(start, end + 1) as string;
if (ignoreDoc) {
res.push(node.nodes[p].originalText);
} else {
res.push(path.call(print, "nodes", p));
}
lastEnd = end + 1;
}
if (lastEnd > 0 && currentDoc.length > lastEnd) {
res.push(currentDoc.slice(lastEnd));
}
ignoreDoc = false;
return res;
});
});
if (node.type === "block") { if (node.type === "block") {
const block = buildBlock(path, print, node as Block, mapped); const block = buildBlock(path, print, node as Block, mapped);
@ -172,7 +189,7 @@ const getMultilineGroup = (content: String): builders.Group => {
return builders.group( return builders.group(
content.split("\n").map((line, i) => { content.split("\n").map((line, i) => {
return [builders.hardline, line.trim()]; return [builders.hardline, line.trim()];
}) }),
); );
}; };
@ -181,7 +198,7 @@ const splitAtElse = (node: Node): string[] => {
(n) => (n) =>
n.type === "statement" && n.type === "statement" &&
["else", "elif"].includes((n as Statement).keyword) && ["else", "elif"].includes((n as Statement).keyword) &&
node.content.search(n.id) !== NOT_FOUND node.content.search(n.id) !== NOT_FOUND,
); );
if (!elseNodes.length) { if (!elseNodes.length) {
return [node.content]; return [node.content];
@ -218,7 +235,7 @@ export const findPlaceholders = (text: string): [number, number][] => {
export const surroundingBlock = (node: Node): Block | undefined => { export const surroundingBlock = (node: Node): Block | undefined => {
return Object.values(node.nodes).find( return Object.values(node.nodes).find(
(n) => n.type === "block" && n.content.search(node.id) !== NOT_FOUND (n) => n.type === "block" && n.content.search(node.id) !== NOT_FOUND,
) as Block; ) as Block;
}; };
@ -226,7 +243,7 @@ const buildBlock = (
path: AstPath<Node>, path: AstPath<Node>,
print: (path: AstPath<Node>) => builders.Doc, print: (path: AstPath<Node>) => builders.Doc,
block: Block, block: Block,
mapped: (string | builders.Doc[] | builders.DocCommand)[] mapped: (string | builders.Doc[] | builders.DocCommand)[],
): builders.Doc => { ): builders.Doc => {
// if the content is empty or whitespace only. // if the content is empty or whitespace only.
if (block.content.match(/^\s*$/)) { if (block.content.match(/^\s*$/)) {
@ -239,14 +256,14 @@ const buildBlock = (
if (block.containsNewLines) { if (block.containsNewLines) {
return builders.group([ return builders.group([
path.call(print, "nodes", block.start.id), path.call(print, "nodes", block.start.id),
builders.indent([builders.softline, utils.stripTrailingHardline(mapped)]), builders.indent([builders.softline, mapped]),
builders.hardline, builders.hardline,
path.call(print, "nodes", block.end.id), path.call(print, "nodes", block.end.id),
]); ]);
} }
return builders.group([ return builders.group([
path.call(print, "nodes", block.start.id), path.call(print, "nodes", block.start.id),
utils.stripTrailingHardline(mapped), mapped,
path.call(print, "nodes", block.end.id), path.call(print, "nodes", block.end.id),
]); ]);
}; };

View file

@ -17,7 +17,7 @@ tests.forEach((test) => {
if (test.startsWith("_")) { if (test.startsWith("_")) {
return; return;
} }
return it(test, () => { return it(test, async () => {
const path = join(testFolder, test); const path = join(testFolder, test);
const input = readFileSync(join(path, "input.html")).toString(); const input = readFileSync(join(path, "input.html")).toString();
const expected = readFileSync(join(path, "expected.html")).toString(); const expected = readFileSync(join(path, "expected.html")).toString();
@ -34,11 +34,11 @@ tests.forEach((test) => {
if (expectedError) { if (expectedError) {
jest.spyOn(console, "error").mockImplementation(() => {}); jest.spyOn(console, "error").mockImplementation(() => {});
expect(format).toThrow(expectedError); await expect(format()).rejects.toThrow(expectedError);
} else { } else {
const result = format(); const result = await format();
expect(result).toEqual(expected); expect(result).toEqual(expected);
expect(prettify(result, configObject)).toEqual(expected); expect(await prettify(result, configObject)).toEqual(expected);
} }
}); });
}); });

View file

@ -1,8 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"outDir": "./lib", "outDir": "./lib",
"target": "ES6", "target": "esnext",
"module": "commonjs", "module": "NodeNext",
"moduleResolution": "node", "moduleResolution": "node",
"lib": ["esnext"], "lib": ["esnext"],
"declaration": true, "declaration": true,