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
16 changes: 15 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const AST_FORMAT = 'heading-case-mdast';

type MarkdownNode = {
children?: MarkdownNode[];
referenceType?: string;
type?: string;
value?: string;
};
Expand All @@ -29,7 +30,20 @@ const collectWordNodes = (node: MarkdownNode, words: MarkdownNode[]) => {

const formatHeading = (heading: MarkdownNode) => {
const wordNodes: MarkdownNode[] = [];
collectWordNodes(heading, wordNodes);
const children = heading.children ?? [];

// A leading `[Label]` can be parsed as a shortcut link reference when the
// document contains a matching reference definition. Treat it as a heading
// prefix either way, so the word after it remains the first English word.
const contentNodes =
children[0]?.type === 'linkReference' &&
children[0].referenceType === 'shortcut'
? children.slice(1)
Comment thread
chenjiahan marked this conversation as resolved.
: children;

for (const child of contentNodes) {
collectWordNodes(child, wordNodes);
}

const words: WordMeta[] = wordNodes.map((node) => ({
type: node.type === 'whitespace' ? 'space' : 'word',
Expand Down
6 changes: 6 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ test('should format line as expected', () => {
// Number
assert.deepStrictEqual(formatLine('# 1. Hello World'), '# 1. Hello world');

// Prefix
assert.deepStrictEqual(
formatLine('## [Important] Package name'),
'## [Important] Package name',
);

// Chinese
assert.deepStrictEqual(
formatLine('# 你好 Hello World'),
Expand Down
28 changes: 28 additions & 0 deletions test/prettier.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ test('should format Markdown and MDX headings with Prettier', async () => {
'',
'# Hello **Bold World** and React Router',
'',
'## [Important] Package name',
'',
'Setext Heading',
'==============',
'',
Expand All @@ -29,6 +31,8 @@ test('should format Markdown and MDX headings with Prettier', async () => {
'',
'# Hello **bold world** and React Router',
'',
'## [Important] Package name',
'',
'Setext heading',
'==============',
'',
Expand Down Expand Up @@ -67,6 +71,30 @@ test('should format Markdown and MDX headings with Prettier', async () => {
);
});

test('should preserve the first word after a bracketed heading prefix', async () => {
const markdown = [
'## [Important] Package Name',
'',
'[Important]: https://example.com',
'',
].join('\n');

const expected = [
'## [Important] Package name',
'',
'[Important]: https://example.com',
'',
].join('\n');

assert.strictEqual(
await prettier.format(markdown, {
parser: 'markdown',
plugins: [plugin],
}),
expected,
);
});

test('should load the package entry as a Prettier plugin through rs fmt', async () => {
const projectRoot = path.resolve(import.meta.dirname, '..');
const cwd = await fs.mkdtemp(path.join(projectRoot, '.heading-case-rs-fmt-'));
Expand Down