Skip to content

Commit

Permalink
chore: the code is now runnable
Browse files Browse the repository at this point in the history
  • Loading branch information
Sec-ant committed Oct 2, 2023
1 parent 12edc5a commit 3cf60dc
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 100 deletions.
8 changes: 0 additions & 8 deletions main.ts

This file was deleted.

37 changes: 37 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"devDependencies": {
"@commitlint/cli": "^17.7.2",
"@commitlint/config-conventional": "^17.7.0",
"@prettier/plugin-xml": "^3.2.1",
"@semantic-release/git": "^10.0.1",
"@types/estree": "^1.0.2",
"@types/uuid": "^9.0.4",
Expand Down
127 changes: 66 additions & 61 deletions src/embedders/xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,77 +24,82 @@ async function printEmbedXml(
path: AstPath<TemplateLiteral>,
options: PrettierPluginXmlOptions,
): Promise<Doc> {
const { node } = path;
const text = node.quasis
.map((quasi, index, { length }) =>
index === length - 1
? quasi.value.cooked
: quasi.value.cooked + makePlaceholder(index),
)
.join("");
try {
const { node } = path;
const text = node.quasis
.map((quasi, index, { length }) =>
index === length - 1
? quasi.value.cooked
: quasi.value.cooked + makePlaceholder(index),
)
.join("");

const expressionDocs = printTemplateExpressions(path, print);
const expressionDocs = printTemplateExpressions(path, print);

// todo: topLevelCount
const topLevelCount = 0;
const doc = await textToDoc(text, {
parser: "xml",
});
// todo: do we need topLevelCount?
const topLevelCount = 0;
const doc = await textToDoc(text, {
parser: "xml",
});

const contentDoc = mapDoc(doc, (doc) => {
if (typeof doc !== "string") {
return doc;
}
const parts = [];
const components = doc.split(PLACEHOLDER_REGEX);
for (let i = 0; i < components.length; i++) {
let component = components[i];
if (i % 2 == 0) {
if (!component) {
const contentDoc = mapDoc(doc, (doc) => {
if (typeof doc !== "string") {
return doc;
}
const parts = [];
const components = doc.split(PLACEHOLDER_REGEX);
for (let i = 0; i < components.length; i++) {
let component = components[i];
if (i % 2 == 0) {
if (component) {
component = component.replaceAll(/([\\`]|\${)/g, "\\$1");
// todo: what about __embeddedInHtml in xml?
if (options.__embeddedInHtml) {
component = component.replaceAll(/<\/(?=script\b)/gi, "<\\/");
}
parts.push(component);
}
continue;
}
component = component.replaceAll(/([\\`]|\${)/g, "\\$1");
// todo: __embeddedInHtml
if (options.__embeddedInHtml) {
component = component.replaceAll(/<\/(?=script\b)/gi, "<\\/");
}
parts.push(component);
}
const placeholderIndex = Number(
component.match(PLACEHOLDER_REGEX)?.[1] ?? "NaN",
);
if (Number.isNaN(placeholderIndex)) {
throw new TypeError("Cannot find component index number.");
const placeholderIndex = Number(component);
parts.push(expressionDocs[placeholderIndex]);
}
parts.push(expressionDocs[placeholderIndex]);
}
return parts;
});
return parts;
});

const leadingWhitespace = /^\s/.test(text) ? " " : "";
const trailingWhitespace = /\s$/.test(text) ? " " : "";
const leadingWhitespace = /^\s/.test(text) ? " " : "";
const trailingWhitespace = /\s$/.test(text) ? " " : "";

const linebreak =
options.xmlWhitespaceSensitivity === "ignore"
? hardline
: leadingWhitespace && trailingWhitespace
? line
: null;
const linebreak =
options.xmlWhitespaceSensitivity === "ignore"
? hardline
: leadingWhitespace && trailingWhitespace
? line
: null;

if (linebreak) {
return group(["`", indent([linebreak, group(contentDoc)]), linebreak, "`"]);
}
if (linebreak) {
return group([
"`",
indent([linebreak, group(contentDoc)]),
linebreak,
"`",
]);
}

return label(
{ hug: false },
group([
"`",
leadingWhitespace,
topLevelCount > 1 ? indent(group(contentDoc)) : group(contentDoc),
trailingWhitespace,
"`",
]),
);
return label(
{ hug: false },
group([
"`",
leadingWhitespace,
topLevelCount > 1 ? indent(group(contentDoc)) : group(contentDoc),
trailingWhitespace,
"`",
]),
);
} catch (e) {
console.error(e);
throw e;
}
}

function makePlaceholder(index: number) {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { parsers } from "./parsers.js";
export { printers } from "./printers.js";
export { options } from "./options.js";
export type * from "./options.js";
15 changes: 7 additions & 8 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import type { CoreCategoryType, SupportOptions } from "prettier";
import type { Embedder } from "./embedders/index.js";

export const options: SupportOptions = {
embeddedLanguages: {
type: "path",
category: "Global" satisfies CoreCategoryType,
array: true,
default: [{ value: [] }],
type: "string",
array: false,
default: "[]",
description:
"Config embedded languages formatting supported by this plugin.",
},
};

interface EmbeddedLanguage {
export interface EmbeddedLanguage {
tag: string | string[];
comment: string | string[];
embedder: string | Embedder | null;
embedder?: string | null;
}

export interface PluginOptions {
embeddedLanguages?: EmbeddedLanguage[];
export interface PrettierPluginEmbedOptions {
embeddedLanguages?: string;
}
12 changes: 12 additions & 0 deletions src/parsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import parserBabel from "prettier/parser-babel";
import parserEspree from "prettier/parser-espree";
import parserFlow from "prettier/parser-flow";
import parserTypescript from "prettier/parser-typescript";

// todo: find out which parsers are required and which are optional
export const parsers = {
...parserBabel.parsers,
...parserEspree.parsers,
...parserFlow.parsers,
...parserTypescript.parsers,
};
23 changes: 14 additions & 9 deletions src/printers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import type { Plugin, Printer, AstPath, Options } from "prettier";
import { printers as estreePrinters } from "prettier/plugins/estree.mjs";
import type { Node, TemplateLiteral } from "estree";

import type { PluginOptions } from "./options.js";
import type {
EmbeddedLanguage,
PrettierPluginEmbedOptions,
} from "./options.js";
import { Embedder, embedders } from "./embedders/index.js";

const { estree: estreePrinter } = estreePrinters;

interface PrettierOptions extends Options, PluginOptions {}
interface PrettierOptions extends Options, PrettierPluginEmbedOptions {}

const embed: Printer["embed"] = function (
path: AstPath<Node>,
Expand All @@ -24,7 +27,9 @@ const embed: Printer["embed"] = function (
}

// test against registered options
for (const { comment, tag, embedder } of options.embeddedLanguages ?? []) {
for (const { comment, tag, embedder } of JSON.parse(
options.embeddedLanguages ?? "[]",
) as EmbeddedLanguage[]) {
const comments = typeof comment === "string" ? [comment] : comment;
const tags = typeof tag === "string" ? [tag] : tag;
if (
Expand All @@ -36,12 +41,12 @@ const embed: Printer["embed"] = function (
let embedderFun: Embedder | null;
if (typeof embedder === "string") {
embedderFun = embedders[embedder];
const node = path.node as TemplateLiteral;
if (node.quasis.length === 1 && node.quasis[0].value.raw.trim() === "") {
return "``";
}
} else {
embedderFun = embedder;
}
const node = path.node as TemplateLiteral;
if (node.quasis.length === 1 && node.quasis[0].value.raw.trim() === "") {
return "``";
embedderFun = embedder ?? null;
}
// todo: should we label the doc as in https://github.com/prettier/prettier/blob/f4491b1274d0697f38f9110116a7dd8d7c295e84/src/language-js/embed/index.js#L39
return embedderFun;
Expand All @@ -51,7 +56,7 @@ const embed: Printer["embed"] = function (
return estreePrinter.embed?.(path, options) ?? null;
};

// todo: add check against comments
// todo: implement check against comments
function checkAgainstComments(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_path: AstPath<Node>,
Expand Down
16 changes: 10 additions & 6 deletions tests/code/0.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { html } from "code-tag";
import { any } from "code-tag";
const xml = any;

() => html`
<div>
<div></div>
</div>
<div></div>
xml`
<svg
xmlns = "http://www.w3.org/2000/svg"
xmlns:xlink = "http://www.w3.org/1999/xlink"
width = "200"
height="100"
viewBox="0 0 200 100"
>${"123"}</svg>
`;
25 changes: 23 additions & 2 deletions tests/plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
/// <reference types="vite/client" />
import { test } from "vitest";
import { format, type Options } from "prettier";
import { fileURLToPath } from "node:url";
import type { EmbeddedLanguage, PrettierPluginEmbedOptions } from "../src";

const PLUGIN_EMBED = fileURLToPath(
new URL("../dist/index.js", import.meta.url),
);

// todo: add tests
test.skip(() => {
/* void */
test("0", async () => {
const code = (await import("./code/0.ts?raw")).default;
const formattedCode = await format(code, {
plugins: ["@prettier/plugin-xml", PLUGIN_EMBED],
filepath: "0.js",
parser: "babel",
embeddedLanguages: JSON.stringify([
{
tag: "xml",
comment: "xml",
embedder: "xml",
},
] as EmbeddedLanguage[]),
} as Options & PrettierPluginEmbedOptions);
console.log(formattedCode);
});
6 changes: 0 additions & 6 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ export default defineConfig({
},
test: {
passWithNoTests: true,
browser: {
enabled: true,
headless: true,
name: "chromium",
provider: "playwright",
},
coverage: {
provider: "istanbul",
},
Expand Down

0 comments on commit 3cf60dc

Please sign in to comment.