update plugin to support prettier v3
This commit is contained in:
parent
ac512dd0b3
commit
be28ec949f
6 changed files with 1107 additions and 1059 deletions
1980
package-lock.json
generated
1980
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
155
src/printer.ts
155
src/printer.ts
|
@ -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),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue