From 0ccfc6414f80c74e8ea8426a653b14f046ec66b5 Mon Sep 17 00:00:00 2001 From: Babayaga Date: Mon, 2 Mar 2026 23:03:52 +0100 Subject: [PATCH] bullet proof :) --- dist/ast/mdast.js | 7 ++++ experiments/test-fix.mjs | 38 ++++++++++++++++++++++ experiments/test-prettier-list.mjs | 51 ++++++++++++++++++++++++++++++ experiments/test-spread.mjs | 50 +++++++++++++++++++++++++++++ package.json | 1 + src/.gitignore | 0 src/ast/mdast.ts | 9 ++++++ 7 files changed, 156 insertions(+) create mode 100644 experiments/test-fix.mjs create mode 100644 experiments/test-prettier-list.mjs create mode 100644 experiments/test-spread.mjs delete mode 100644 src/.gitignore diff --git a/dist/ast/mdast.js b/dist/ast/mdast.js index b08d375..29ec771 100644 --- a/dist/ast/mdast.js +++ b/dist/ast/mdast.js @@ -23,11 +23,18 @@ function getMdast(markdown) { function getMarkdown(mdast) { return toMarkdown(mdast, { extensions: [frontmatterToMarkdown("yaml"), mdxToMarkdown(), gfmTableToMarkdown(), htmlCommentToMarkdown()], + listItemIndent: "one", join: [ (__, _, parent) => { if (mdNodeIsJsxElement(parent)) { return 0; } + if (mdNodeIs(parent, "list")) { + return 0; + } + if (mdNodeIs(parent, "listItem")) { + return 0; + } return 1; } ] diff --git a/experiments/test-fix.mjs b/experiments/test-fix.mjs new file mode 100644 index 0000000..3cefe6b --- /dev/null +++ b/experiments/test-fix.mjs @@ -0,0 +1,38 @@ +import prettier from 'prettier'; +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `The panel includes the following settings: + +* ON/OFF Toggle: A main switch. +* Min Heating Time (1-60s): Minimum duration. +* Mode: Selects the sequential heating algorithm: + * 0 - All: Cycles through all devices with time-based control. + * 1 - SP: Cycles through devices in groups. + * 2 - SP Any: Heats any devices that need heating. +* Post-Heatup Mode: Mode to switch to after initial heatup phase. +* Current Status: Display field showing the current state.`; + +const prettified = await prettier.format(md, { + parser: 'mdx', + printWidth: Infinity, + proseWrap: 'never', + useTabs: true +}); + +const tree = fromMarkdown(prettified); + +// FIX: also handle listItem parent in the join rule +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); + +console.log('=== FIXED output ==='); +console.log(result); diff --git a/experiments/test-prettier-list.mjs b/experiments/test-prettier-list.mjs new file mode 100644 index 0000000..75a8ada --- /dev/null +++ b/experiments/test-prettier-list.mjs @@ -0,0 +1,51 @@ +import prettier from 'prettier'; +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `The panel includes the following settings: + +* ON/OFF Toggle: A main switch. +* Min Heating Time (1-60s): Minimum duration. +* Mode: Selects the sequential heating algorithm: + * 0 - All: Cycles through all devices with time-based control. + * 1 - SP: Cycles through devices in groups. + * 2 - SP Any: Heats any devices that need heating. +* Post-Heatup Mode: Mode to switch to after initial heatup phase. +* Current Status: Display field showing the current state.`; + +console.log('=== Input ==='); +console.log(md); + +const prettified = await prettier.format(md, { + parser: 'mdx', + printWidth: Infinity, + proseWrap: 'never', + useTabs: true +}); + +console.log('\n=== After Prettier ==='); +console.log(JSON.stringify(prettified)); +console.log(prettified); + +const tree = fromMarkdown(prettified); +const list = tree.children[1]; // after the paragraph + +console.log('=== AST Analysis of list ==='); +console.log('list spread:', list.spread); +for (const [i, item] of list.children.entries()) { + console.log(`item[${i}] spread:`, item.spread, 'children:', item.children.length, item.children.map(c => c.type)); +} + +// Now serialize with the join rule: +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + return 1; + } + ] +}); + +console.log('\n=== Serialized output ==='); +console.log(result); diff --git a/experiments/test-spread.mjs b/experiments/test-spread.mjs new file mode 100644 index 0000000..0816037 --- /dev/null +++ b/experiments/test-spread.mjs @@ -0,0 +1,50 @@ +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `* Mode: Selects the algorithm: + * 0 - All: Cycles through all devices. + * 1 - SP: Cycles through devices in groups. +* Post-Mode: blah`; + +const tree = fromMarkdown(md); +const list = tree.children[0]; + +console.log('=== AST Analysis ==='); +console.log('list spread:', list.spread); +for (const [i, item] of list.children.entries()) { + console.log(`item[${i}] spread:`, item.spread, 'children count:', item.children.length, 'children types:', item.children.map(c => c.type)); +} + +console.log('\n=== Default toMarkdown ==='); +console.log(JSON.stringify(toMarkdown(tree))); + +console.log('\n=== With join rule ==='); +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); +console.log(JSON.stringify(result)); + +console.log('\n=== With join rule + forced spread=false ==='); +function clearSpread(node) { + if (node.spread !== undefined) node.spread = false; + if (node.children) node.children.forEach(clearSpread); +} +clearSpread(tree); +const result2 = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); +console.log(JSON.stringify(result2)); diff --git a/package.json b/package.json index 1d73902..b48bfa6 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "scripts": { "build": "tsc && node build.js", + "dev": "tsc -w & node --watch build.js", "test": "vitest run --reporter verbose", "test:watch": "vitest watch --reporter verbose", "test:tables": "vitest run src/__test__/e2e.test.ts --reporter verbose" diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/ast/mdast.ts b/src/ast/mdast.ts index a186db6..6a2178c 100644 --- a/src/ast/mdast.ts +++ b/src/ast/mdast.ts @@ -153,12 +153,21 @@ export function getMdast(markdown: string): MdRoot { export function getMarkdown(mdast: MdRoot): string { return toMarkdown(mdast, { extensions: [frontmatterToMarkdown('yaml'), mdxToMarkdown(), gfmTableToMarkdown(), htmlCommentToMarkdown()], + listItemIndent: 'one', join: [ (__, _, parent) => { if (mdNodeIsJsxElement(parent)) { return 0; } + // Keep list items tight (no blank lines between them) + if (mdNodeIs(parent, 'list')) { + return 0; + } + // Keep content within a list item tight (e.g. paragraph + nested sub-list) + if (mdNodeIs(parent, 'listItem')) { + return 0; + } return 1; } ]