Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions __tests__/mdx-lint.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { spawnSync } from "child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import * as path from "path";
import lintWorker from "../src/utils/lint-worker";

const CLI = path.resolve(__dirname, "../lib/src/lint-md.js");
const RULES = { "space-around-alphabet": 2 };
const MDX = [
'import Widget from "中文English";',
"",
'export const title = "中文English";',
"",
'<Widget label="中文English" count={"中文English"} />',
"",
'{"中文English"}',
"",
"<Widget>正文English内容。</Widget>",
"",
].join("\n");

describe("MDX lint", () => {
let tmpDir: string;
let configPath: string;

beforeEach(() => {
tmpDir = mkdtempSync(path.join(tmpdir(), "lint-md-mdx-"));
configPath = path.join(tmpDir, ".lintmdrc");
writeFileSync(configPath, JSON.stringify({ rules: RULES }), "utf8");
});

afterEach(() => {
rmSync(tmpDir, { recursive: true, force: true });
});

test("lints Markdown text", async () => {
const filePath = path.join(tmpDir, "document.md");
writeFileSync(filePath, "正文English内容。\n", "utf8");

const result = await lintWorker({
filePath,
rules: RULES,
isFixMode: false,
});

expect(result.diagnostics).toHaveLength(2);
expect(
result.diagnostics.map(({ ruleId, line }) => [ruleId, line])
).toEqual([
["space-around-alphabet", 1],
["space-around-alphabet", 1],
]);
});

test.each([".mdx", ".MDX"])(
"lints %s through the CLI and skips JSX, ESM, and expressions",
(extension) => {
const filePath = path.join(tmpDir, `document${extension}`);
writeFileSync(filePath, MDX, "utf8");

const result = spawnSync(
process.execPath,
[CLI, filePath, "--config", configPath],
{ encoding: "utf8" }
);

expect(result.error).toBeUndefined();
expect(result.status).toBe(1);
expect(result.stdout).toContain(filePath);
expect(result.stdout).toContain("9:10");
expect(result.stdout).toContain("9:17");
expect(result.stdout).toContain("2 problems");
}
);

test("routes Markdown and MDX through the CLI", () => {
const markdownPath = path.join(tmpDir, "document.md");
const mdxPath = path.join(tmpDir, "document.mdx");
writeFileSync(markdownPath, "正文English内容。\n", "utf8");
writeFileSync(mdxPath, MDX, "utf8");

const result = spawnSync(
process.execPath,
[CLI, markdownPath, mdxPath, "--config", configPath],
{ encoding: "utf8" }
);

expect(result.error).toBeUndefined();
expect(result.status).toBe(1);
expect(result.stdout).toContain(markdownPath);
expect(result.stdout).toContain(mdxPath);
expect(result.stdout).toContain("4 problems");
});

test("rejects a direct worker fix request for MDX", async () => {
const filePath = path.join(tmpDir, "document.mdx");
writeFileSync(filePath, MDX, "utf8");

await expect(
lintWorker({ filePath, rules: RULES, isFixMode: true })
).rejects.toThrow("--fix does not support .mdx files");
expect(readFileSync(filePath, "utf8")).toBe(MDX);
});

test.each([".mdx", ".MDX"])(
"rejects %s fix requests before changing any file",
(extension) => {
const markdownPath = path.join(tmpDir, "document.md");
const mdxPath = path.join(tmpDir, `document${extension}`);
const markdown = "正文English内容。\n";
writeFileSync(markdownPath, markdown, "utf8");
writeFileSync(mdxPath, MDX, "utf8");

const result = spawnSync(
process.execPath,
[CLI, markdownPath, mdxPath, "--fix", "--config", configPath],
{ encoding: "utf8" }
);

expect(result.error).toBeUndefined();
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"--fix does not support .mdx files. No files were changed."
);
expect(readFileSync(markdownPath, "utf8")).toBe(markdown);
expect(readFileSync(mdxPath, "utf8")).toBe(MDX);
}
);
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
]
},
"dependencies": {
"@lint-md/core": "^2.5.2",
"@lint-md/core": "^2.6.0",
"chalk": "^4",
"commander": "^9.4.1",
"glob": "^13.0.6",
Expand Down
3 changes: 3 additions & 0 deletions scripts/test-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ try {

const fixtureDirectory = join(temporaryDirectory, "fixtures");
const cleanFile = join(fixtureDirectory, "clean.md");
const cleanMdxFile = join(fixtureDirectory, "clean.mdx");
const fixableFile = join(fixtureDirectory, "fixable.md");
mkdirSync(fixtureDirectory);
cpSync(join(projectRoot, "examples", "space-around.md"), fixableFile, {
recursive: false,
});
writeFileSync(cleanFile, cleanMarkdown, "utf8");
writeFileSync(cleanMdxFile, '# Hello\n\n<Card label="text" />\n', "utf8");

run(lintMd, [cleanFile]);
run(lintMd, [cleanMdxFile]);
const originalFixableContent = readFileSync(fixableFile, "utf8");
run(lintMd, [fixableFile, "--fix"]);
const fixedContent = readFileSync(fixableFile, "utf8");
Expand Down
20 changes: 17 additions & 3 deletions src/utils/lint-worker.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import { readFile } from "fs/promises";
import { extname } from "path";
import { fixMarkdown, lintMarkdown } from "@lint-md/core";
import type { LintMdResult } from "@lint-md/core";
import type { LintWorkerOptions } from "../types";
import { toBatchLintItem } from "./to-batch-lint-item";

const lintWorker = async (options: LintWorkerOptions) => {
const { filePath, rules, isFixMode } = options;
const isMdx = extname(filePath).toLowerCase() === ".mdx";

if (isMdx && isFixMode) {
throw new Error("--fix does not support .mdx files");
}

const content = await readFile(filePath, "utf8");
const result = isFixMode
? fixMarkdown(content, { rules })
: lintMarkdown(content, { rules: rules ?? {} });
let result: LintMdResult;
if (isMdx) {
const { lintMdx } =
require("@lint-md/core/mdx") as typeof import("@lint-md/core/mdx");
result = lintMdx(content, { rules: rules ?? {} });
} else {
result = isFixMode
? fixMarkdown(content, { rules })
: lintMarkdown(content, { rules: rules ?? {} });
}

// Core returns fixedResult even when no rule changed the text. Compare with
// the content we already read so the main thread writes only real changes.
Expand Down
8 changes: 7 additions & 1 deletion src/utils/load-md-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ export const loadMdFiles = async (
for (const entry of entries) {
if (entry.isSymbolicLink()) continue;
const fullPath = entry.fullpath();
if (extensions.some((ext) => fullPath.endsWith(ext))) files.add(fullPath);
if (
extensions.some((ext) =>
fullPath.toLowerCase().endsWith(ext.toLowerCase())
)
) {
files.add(fullPath);
}
}

// Sort so lint report order stays stable across OS, filesystem, and
Expand Down
Loading