mirror of
https://github.com/LukeHagar/prettier-plugin-openapi.git
synced 2025-12-06 04:21:03 +00:00
chore: update dependencies and full integrate markdown formatting with official prettier implementations
This commit is contained in:
110
bun.lock
110
bun.lock
@@ -4,20 +4,20 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "prettier-plugin-openapi",
|
"name": "prettier-plugin-openapi",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/js-yaml": "^4.0.0",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.2.4",
|
"@biomejs/biome": "^2.3.2",
|
||||||
"@rollup/plugin-alias": "^5.1.1",
|
"@rollup/plugin-alias": "^6.0.0",
|
||||||
"@rollup/plugin-commonjs": "^28.0.6",
|
"@rollup/plugin-commonjs": "^29.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/node": "^20.0.0",
|
"@types/node": "^24.9.2",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.6.2",
|
||||||
"rollup": "^4.52.2",
|
"rollup": "^4.52.5",
|
||||||
"rollup-plugin-typescript": "^1.0.1",
|
"rollup-plugin-typescript": "^1.0.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.9.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
@@ -25,93 +25,93 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="],
|
"@biomejs/biome": ["@biomejs/biome@2.3.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.2", "@biomejs/cli-darwin-x64": "2.3.2", "@biomejs/cli-linux-arm64": "2.3.2", "@biomejs/cli-linux-arm64-musl": "2.3.2", "@biomejs/cli-linux-x64": "2.3.2", "@biomejs/cli-linux-x64-musl": "2.3.2", "@biomejs/cli-win32-arm64": "2.3.2", "@biomejs/cli-win32-x64": "2.3.2" }, "bin": { "biome": "bin/biome" } }, "sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA=="],
|
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg=="],
|
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw=="],
|
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ=="],
|
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ=="],
|
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg=="],
|
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ=="],
|
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="],
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.2", "", { "os": "win32", "cpu": "x64" }, "sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ=="],
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
"@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.1", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ=="],
|
"@rollup/plugin-alias": ["@rollup/plugin-alias@6.0.0", "", { "peerDependencies": { "rollup": ">=4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g=="],
|
||||||
|
|
||||||
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.6", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw=="],
|
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@29.0.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ=="],
|
||||||
|
|
||||||
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA=="],
|
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="],
|
||||||
|
|
||||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.2", "", { "os": "android", "cpu": "arm" }, "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA=="],
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ=="],
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw=="],
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng=="],
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA=="],
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w=="],
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg=="],
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA=="],
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw=="],
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A=="],
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w=="],
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q=="],
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ=="],
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA=="],
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ=="],
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
|
||||||
|
|
||||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.2", "", { "os": "none", "cpu": "arm64" }, "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q=="],
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA=="],
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A=="],
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA=="],
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@20.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ=="],
|
"@types/node": ["@types/node@24.9.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||||
|
|
||||||
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
|
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
|
||||||
|
|
||||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
|
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||||
|
|
||||||
@@ -145,9 +145,9 @@
|
|||||||
|
|
||||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||||
|
|
||||||
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.52.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.2", "@rollup/rollup-android-arm64": "4.52.2", "@rollup/rollup-darwin-arm64": "4.52.2", "@rollup/rollup-darwin-x64": "4.52.2", "@rollup/rollup-freebsd-arm64": "4.52.2", "@rollup/rollup-freebsd-x64": "4.52.2", "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", "@rollup/rollup-linux-arm-musleabihf": "4.52.2", "@rollup/rollup-linux-arm64-gnu": "4.52.2", "@rollup/rollup-linux-arm64-musl": "4.52.2", "@rollup/rollup-linux-loong64-gnu": "4.52.2", "@rollup/rollup-linux-ppc64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-musl": "4.52.2", "@rollup/rollup-linux-s390x-gnu": "4.52.2", "@rollup/rollup-linux-x64-gnu": "4.52.2", "@rollup/rollup-linux-x64-musl": "4.52.2", "@rollup/rollup-openharmony-arm64": "4.52.2", "@rollup/rollup-win32-arm64-msvc": "4.52.2", "@rollup/rollup-win32-ia32-msvc": "4.52.2", "@rollup/rollup-win32-x64-gnu": "4.52.2", "@rollup/rollup-win32-x64-msvc": "4.52.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA=="],
|
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
||||||
|
|
||||||
"rollup-plugin-typescript": ["rollup-plugin-typescript@1.0.1", "", { "dependencies": { "resolve": "^1.10.0", "rollup-pluginutils": "^2.5.0" }, "peerDependencies": { "tslib": "*", "typescript": ">=2.1.0" } }, "sha512-rwJDNn9jv/NsKZuyBb/h0jsclP4CJ58qbvZt2Q9zDIGILF2LtdtvCqMOL+Gq9IVq5MTrTlHZNrn8h7VjQgd8tw=="],
|
"rollup-plugin-typescript": ["rollup-plugin-typescript@1.0.1", "", { "dependencies": { "resolve": "^1.10.0", "rollup-pluginutils": "^2.5.0" }, "peerDependencies": { "tslib": "*", "typescript": ">=2.1.0" } }, "sha512-rwJDNn9jv/NsKZuyBb/h0jsclP4CJ58qbvZt2Q9zDIGILF2LtdtvCqMOL+Gq9IVq5MTrTlHZNrn8h7VjQgd8tw=="],
|
||||||
|
|
||||||
@@ -157,14 +157,10 @@
|
|||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
|
||||||
"bun-types/@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="],
|
|
||||||
|
|
||||||
"rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
|
"rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
|
||||||
|
|
||||||
"bun-types/@types/node/undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
package.json
20
package.json
@@ -71,19 +71,19 @@
|
|||||||
"prettier": "^3.0.0"
|
"prettier": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.2.4",
|
"@biomejs/biome": "^2.3.2",
|
||||||
"@rollup/plugin-alias": "^5.1.1",
|
"@rollup/plugin-alias": "^6.0.0",
|
||||||
"@rollup/plugin-commonjs": "^28.0.6",
|
"@rollup/plugin-commonjs": "^29.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/node": "^20.0.0",
|
"@types/node": "^24.9.2",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.6.2",
|
||||||
"rollup": "^4.52.2",
|
"rollup": "^4.52.5",
|
||||||
"rollup-plugin-typescript": "^1.0.1",
|
"rollup-plugin-typescript": "^1.0.1",
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/js-yaml": "^4.0.0",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"js-yaml": "^4.1.0"
|
"js-yaml": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1305
src/index.ts
1305
src/index.ts
File diff suppressed because it is too large
Load Diff
54
src/prettier-markdown/README.md
Normal file
54
src/prettier-markdown/README.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Prettier Markdown Integration
|
||||||
|
|
||||||
|
This directory contains Prettier's markdown language implementation, adapted for use within this plugin.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
### Core Files (from Prettier)
|
||||||
|
These files are copied from Prettier's `src/language-markdown` directory:
|
||||||
|
- `parser-markdown.js` - Markdown parser
|
||||||
|
- `printer-markdown.js` - Markdown printer
|
||||||
|
- `clean.js` - AST cleaning utilities
|
||||||
|
- `utils.js` - Utility functions
|
||||||
|
- `constants.evaluate.js` - Constants
|
||||||
|
- `print/` - Printing utilities
|
||||||
|
- `unified-plugins/` - Unified/Remark plugins
|
||||||
|
- Other supporting files
|
||||||
|
|
||||||
|
### Adapter Files (created for this plugin)
|
||||||
|
These files adapt Prettier's internal dependencies:
|
||||||
|
- `adapter-document-builders.js` - Adapts Prettier's document builders
|
||||||
|
- `adapter-document-utils.js` - Adapts Prettier's document utilities
|
||||||
|
- `adapter-document-constants.js` - Adapts Prettier's document constants
|
||||||
|
- `adapter-prettier-utils.js` - Adapts Prettier's utility functions
|
||||||
|
- `adapter-pragma.js` - Adapts pragma handling (simplified)
|
||||||
|
|
||||||
|
### Integration Files
|
||||||
|
- `format-markdown.ts` - Type-safe wrapper for formatting markdown
|
||||||
|
- `options.js` - Markdown formatting options (adapted from Prettier)
|
||||||
|
|
||||||
|
## Updating from Prettier
|
||||||
|
|
||||||
|
When Prettier updates its markdown implementation:
|
||||||
|
|
||||||
|
1. **Copy updated files** from `prettier/src/language-markdown/` to this directory
|
||||||
|
2. **Update adapter files** if Prettier's internal structure changed:
|
||||||
|
- Check if document builders path changed → update `adapter-document-builders.js`
|
||||||
|
- Check if document utils path changed → update `adapter-document-utils.js`
|
||||||
|
- Check if utility functions changed → update `adapter-prettier-utils.js`
|
||||||
|
3. **Update imports** in copied files to use adapter files:
|
||||||
|
- `../document/builders.js` → `./adapter-document-builders.js`
|
||||||
|
- `../document/utils.js` → `./adapter-document-utils.js`
|
||||||
|
- `../document/constants.js` → `./adapter-document-constants.js`
|
||||||
|
- `../utils/*` → `./adapter-prettier-utils.js`
|
||||||
|
- `../common/common-options.evaluate.js` → update `options.js`
|
||||||
|
- `../main/front-matter/index.js` → update `adapter-pragma.js` or `clean.js`
|
||||||
|
- `../utils/pragma/pragma.evaluate.js` → update `adapter-pragma.js`
|
||||||
|
4. **Test** that markdown formatting still works correctly
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
The adapter files attempt to access Prettier's internal APIs at runtime. If Prettier's internal structure changes significantly, you may need to update the adapter files to match.
|
||||||
|
|
||||||
|
The interfaces in the adapter files are designed to be similar to Prettier's actual structure, making updates easier.
|
||||||
|
|
||||||
55
src/prettier-markdown/adapter-document-builders.js
Normal file
55
src/prettier-markdown/adapter-document-builders.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's document builders
|
||||||
|
*
|
||||||
|
* This file attempts to import Prettier's document builders from internal APIs.
|
||||||
|
* Update this file when Prettier's internal structure changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let builders: any = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
// Try multiple paths to access document builders
|
||||||
|
if (prettier.__internal?.document?.builders) {
|
||||||
|
builders = prettier.__internal.document.builders;
|
||||||
|
} else {
|
||||||
|
// Try to require directly (may work in plugin context)
|
||||||
|
try {
|
||||||
|
builders = require("prettier/internal/document/builders");
|
||||||
|
} catch {
|
||||||
|
// Fallback: try alternative paths
|
||||||
|
try {
|
||||||
|
const doc = require("prettier/doc");
|
||||||
|
if (doc) {
|
||||||
|
builders = doc;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Not accessible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Builders not accessible
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export what we found, or throw if not available
|
||||||
|
if (!builders) {
|
||||||
|
throw new Error(
|
||||||
|
"Prettier document builders not accessible. " +
|
||||||
|
"Markdown formatting requires Prettier's internal document builders."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const {
|
||||||
|
align,
|
||||||
|
fill,
|
||||||
|
group,
|
||||||
|
hardline,
|
||||||
|
indent,
|
||||||
|
line,
|
||||||
|
literalline,
|
||||||
|
markAsRoot,
|
||||||
|
softline,
|
||||||
|
} = builders;
|
||||||
|
|
||||||
33
src/prettier-markdown/adapter-document-constants.js
Normal file
33
src/prettier-markdown/adapter-document-constants.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's document constants
|
||||||
|
*
|
||||||
|
* This file attempts to import Prettier's document constants from internal APIs.
|
||||||
|
* Update this file when Prettier's internal structure changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let constants: any = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.document?.constants) {
|
||||||
|
constants = prettier.__internal.document.constants;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
constants = require("prettier/internal/document/constants");
|
||||||
|
} catch {
|
||||||
|
// Constants may not be accessible, provide a fallback
|
||||||
|
constants = {
|
||||||
|
DOC_TYPE_STRING: "doc-type-string",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Provide fallback
|
||||||
|
constants = {
|
||||||
|
DOC_TYPE_STRING: "doc-type-string",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { DOC_TYPE_STRING } = constants;
|
||||||
|
|
||||||
39
src/prettier-markdown/adapter-document-utils.js
Normal file
39
src/prettier-markdown/adapter-document-utils.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's document utils
|
||||||
|
*
|
||||||
|
* This file attempts to import Prettier's document utilities from internal APIs.
|
||||||
|
* Update this file when Prettier's internal structure changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let utils: any = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.document?.utils) {
|
||||||
|
utils = prettier.__internal.document.utils;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
utils = require("prettier/internal/document/utils");
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const docUtils = require("prettier/doc");
|
||||||
|
utils = docUtils;
|
||||||
|
} catch {
|
||||||
|
// Not accessible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Utils not accessible
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!utils) {
|
||||||
|
throw new Error(
|
||||||
|
"Prettier document utils not accessible. " +
|
||||||
|
"Markdown formatting requires Prettier's internal document utilities."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { getDocType, replaceEndOfLine } = utils;
|
||||||
|
|
||||||
48
src/prettier-markdown/adapter-pragma.js
Normal file
48
src/prettier-markdown/adapter-pragma.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's pragma utilities
|
||||||
|
*
|
||||||
|
* Simplified version that doesn't depend on Prettier's internal pragma system.
|
||||||
|
* Update this file when Prettier's pragma behavior changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Simplified pragma regexes based on Prettier's implementation
|
||||||
|
const MARKDOWN_HAS_PRAGMA_REGEXP = /^<!--\s*@(prettier|format)\s*-->$/m;
|
||||||
|
const MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP = /^<!--\s*prettier-ignore(?:-(start|end))?\s*-->$/m;
|
||||||
|
const FORMAT_PRAGMA_TO_INSERT = "format";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple front matter parser (minimal implementation)
|
||||||
|
*/
|
||||||
|
function parseFrontMatter(text: string) {
|
||||||
|
const yamlMatch = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
||||||
|
if (yamlMatch) {
|
||||||
|
return {
|
||||||
|
content: text.slice(yamlMatch[0].length),
|
||||||
|
frontMatter: {
|
||||||
|
raw: yamlMatch[0],
|
||||||
|
end: { index: yamlMatch[0].length },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { content: text, frontMatter: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPragma = (text: string) =>
|
||||||
|
parseFrontMatter(text).content.trimStart().match(MARKDOWN_HAS_PRAGMA_REGEXP)
|
||||||
|
?.index === 0;
|
||||||
|
|
||||||
|
const hasIgnorePragma = (text: string) =>
|
||||||
|
parseFrontMatter(text)
|
||||||
|
.content.trimStart()
|
||||||
|
.match(MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP)?.index === 0;
|
||||||
|
|
||||||
|
const insertPragma = (text: string) => {
|
||||||
|
const { frontMatter } = parseFrontMatter(text);
|
||||||
|
const pragma = `<!-- @${FORMAT_PRAGMA_TO_INSERT} -->`;
|
||||||
|
return frontMatter
|
||||||
|
? `${frontMatter.raw}\n\n${pragma}\n\n${text.slice(frontMatter.end.index)}`
|
||||||
|
: `${pragma}\n\n${text}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { hasIgnorePragma, hasPragma, insertPragma };
|
||||||
|
|
||||||
130
src/prettier-markdown/adapter-prettier-internals.js
Normal file
130
src/prettier-markdown/adapter-prettier-internals.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's internal document builders and utilities
|
||||||
|
*
|
||||||
|
* This file attempts to access Prettier's internal APIs that are needed
|
||||||
|
* by the markdown parser and printer. These adapters provide a type-safe
|
||||||
|
* way to access Prettier internals when available.
|
||||||
|
*
|
||||||
|
* Note: This file can be updated when Prettier's internal structure changes.
|
||||||
|
* The interfaces should remain similar to allow easy updates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to import Prettier's document builders
|
||||||
|
* @returns {Promise<typeof import('prettier/internal/document/builders')> | null>}
|
||||||
|
*/
|
||||||
|
export function getDocumentBuilders() {
|
||||||
|
try {
|
||||||
|
// Try to require Prettier's document builders
|
||||||
|
// Prettier 3.x structure
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
// Path 1: Check if accessible via __internal
|
||||||
|
if (prettier.__internal?.document?.builders) {
|
||||||
|
return prettier.__internal.document.builders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path 2: Try to require directly (may work in some contexts)
|
||||||
|
try {
|
||||||
|
return require("prettier/internal/document/builders");
|
||||||
|
} catch {
|
||||||
|
// Not accessible
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to import Prettier's document utils
|
||||||
|
*/
|
||||||
|
export function getDocumentUtils() {
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.document?.utils) {
|
||||||
|
return prettier.__internal.document.utils;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return require("prettier/internal/document/utils");
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to import Prettier's document constants
|
||||||
|
*/
|
||||||
|
export function getDocumentConstants() {
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.document?.constants) {
|
||||||
|
return prettier.__internal.document.constants;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return require("prettier/internal/document/constants");
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to import Prettier's utility functions
|
||||||
|
*/
|
||||||
|
export function getPrettierUtils() {
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.utils) {
|
||||||
|
return prettier.__internal.utils;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to get Prettier's doc-to-string printer
|
||||||
|
* This is needed to convert Doc objects to strings
|
||||||
|
*/
|
||||||
|
export function getDocPrinter() {
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
// Try multiple paths
|
||||||
|
if (prettier.__internal?.doc?.printDocToString) {
|
||||||
|
return prettier.__internal.doc.printDocToString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prettier.__internal?.docPrinter?.formatDoc) {
|
||||||
|
return prettier.__internal.docPrinter.formatDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const docUtils = require("prettier/internal/doc");
|
||||||
|
if (docUtils?.printDocToString) {
|
||||||
|
return docUtils.printDocToString;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Not accessible
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
72
src/prettier-markdown/adapter-prettier-utils.js
Normal file
72
src/prettier-markdown/adapter-prettier-utils.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Adapter for Prettier's utility functions
|
||||||
|
*
|
||||||
|
* This file attempts to import Prettier's utility functions from internal APIs.
|
||||||
|
* Update this file when Prettier's internal structure changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let utils: any = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prettier = require("prettier");
|
||||||
|
|
||||||
|
if (prettier.__internal?.utils) {
|
||||||
|
utils = prettier.__internal.utils;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// Try alternative paths
|
||||||
|
utils = require("prettier/internal/utils");
|
||||||
|
} catch {
|
||||||
|
// Utils may not be accessible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Utils not accessible
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide fallback implementations if utils aren't accessible
|
||||||
|
if (!utils) {
|
||||||
|
// Minimal fallback implementations
|
||||||
|
utils = {
|
||||||
|
getMaxContinuousCount: (str: string, char: string) => {
|
||||||
|
let max = 0;
|
||||||
|
let current = 0;
|
||||||
|
for (const c of str) {
|
||||||
|
if (c === char) {
|
||||||
|
current++;
|
||||||
|
max = Math.max(max, current);
|
||||||
|
} else {
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
},
|
||||||
|
getMinNotPresentContinuousCount: (str: string, char: string) => {
|
||||||
|
let count = 1;
|
||||||
|
while (str.includes(char.repeat(count))) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
getPreferredQuote: (str: string, singleQuote: boolean) => {
|
||||||
|
if (singleQuote) return "'";
|
||||||
|
const hasSingle = str.includes("'");
|
||||||
|
const hasDouble = str.includes('"');
|
||||||
|
if (hasSingle && !hasDouble) return '"';
|
||||||
|
return "'";
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMaxContinuousCount = utils.getMaxContinuousCount;
|
||||||
|
export const getMinNotPresentContinuousCount = utils.getMinNotPresentContinuousCount;
|
||||||
|
export const getPreferredQuote = utils.getPreferredQuote;
|
||||||
|
|
||||||
|
// UnexpectedNodeError may be in utils or separate
|
||||||
|
export class UnexpectedNodeError extends Error {
|
||||||
|
constructor(node: any, language: string) {
|
||||||
|
super(`Unexpected node type: ${node.type} in ${language}`);
|
||||||
|
this.name = "UnexpectedNodeError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
91
src/prettier-markdown/clean.js
Normal file
91
src/prettier-markdown/clean.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import collapseWhiteSpace from "collapse-white-space";
|
||||||
|
import { hasPragma } from "./adapter-pragma.js";
|
||||||
|
|
||||||
|
// Simplified front matter check
|
||||||
|
function isFrontMatter(node: any): boolean {
|
||||||
|
return node?.type === "yaml" || node?.type === "toml";
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoredProperties = new Set([
|
||||||
|
"position",
|
||||||
|
"raw", // front-matter
|
||||||
|
]);
|
||||||
|
function clean(original, cloned, parent) {
|
||||||
|
// for codeblock
|
||||||
|
if (
|
||||||
|
original.type === "code" ||
|
||||||
|
original.type === "yaml" ||
|
||||||
|
original.type === "import" ||
|
||||||
|
original.type === "export" ||
|
||||||
|
original.type === "jsx"
|
||||||
|
) {
|
||||||
|
delete cloned.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.type === "list") {
|
||||||
|
delete cloned.isAligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.type === "list" || original.type === "listItem") {
|
||||||
|
delete cloned.spread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// texts can be splitted or merged
|
||||||
|
if (original.type === "text") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.type === "inlineCode") {
|
||||||
|
cloned.value = original.value.replaceAll("\n", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.type === "wikiLink") {
|
||||||
|
cloned.value = original.value.trim().replaceAll(/[\t\n]+/gu, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
original.type === "definition" ||
|
||||||
|
original.type === "linkReference" ||
|
||||||
|
original.type === "imageReference"
|
||||||
|
) {
|
||||||
|
cloned.label = collapseWhiteSpace(original.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(original.type === "link" || original.type === "image") &&
|
||||||
|
original.url &&
|
||||||
|
original.url.includes("(")
|
||||||
|
) {
|
||||||
|
for (const character of "<>") {
|
||||||
|
cloned.url = original.url.replaceAll(
|
||||||
|
character,
|
||||||
|
encodeURIComponent(character),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(original.type === "definition" ||
|
||||||
|
original.type === "link" ||
|
||||||
|
original.type === "image") &&
|
||||||
|
original.title
|
||||||
|
) {
|
||||||
|
cloned.title = original.title.replaceAll(/\\(?=["')])/gu, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// for insert pragma
|
||||||
|
if (
|
||||||
|
parent?.type === "root" &&
|
||||||
|
parent.children.length > 0 &&
|
||||||
|
(parent.children[0] === original ||
|
||||||
|
(isFrontMatter(parent.children[0]) && parent.children[1] === original)) &&
|
||||||
|
original.type === "html" &&
|
||||||
|
hasPragma(original.value)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean.ignoredProperties = ignoredProperties;
|
||||||
|
|
||||||
|
export default clean;
|
||||||
86
src/prettier-markdown/constants.evaluate.js
Normal file
86
src/prettier-markdown/constants.evaluate.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { all as getCjkCharset } from "cjk-regex";
|
||||||
|
import { Charset } from "regexp-util";
|
||||||
|
import unicodeRegex from "unicode-regex";
|
||||||
|
|
||||||
|
const cjkCharset = new Charset(
|
||||||
|
getCjkCharset(),
|
||||||
|
unicodeRegex({
|
||||||
|
Script_Extensions: ["Han", "Katakana", "Hiragana", "Hangul", "Bopomofo"],
|
||||||
|
General_Category: [
|
||||||
|
"Other_Letter",
|
||||||
|
"Letter_Number",
|
||||||
|
"Other_Symbol",
|
||||||
|
"Modifier_Letter",
|
||||||
|
"Modifier_Symbol",
|
||||||
|
"Nonspacing_Mark",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const variationSelectorsCharset = unicodeRegex({
|
||||||
|
Block: ["Variation_Selectors", "Variation_Selectors_Supplement"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const CJK_REGEXP = new RegExp(
|
||||||
|
`(?:${cjkCharset.toString("u")})(?:${variationSelectorsCharset.toString("u")})?`,
|
||||||
|
"u",
|
||||||
|
);
|
||||||
|
|
||||||
|
const asciiPunctuationCharacters = [
|
||||||
|
"!",
|
||||||
|
'"',
|
||||||
|
"#",
|
||||||
|
"$",
|
||||||
|
"%",
|
||||||
|
"&",
|
||||||
|
"'",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
"*",
|
||||||
|
"+",
|
||||||
|
",",
|
||||||
|
"-",
|
||||||
|
".",
|
||||||
|
"/",
|
||||||
|
":",
|
||||||
|
";",
|
||||||
|
"<",
|
||||||
|
"=",
|
||||||
|
">",
|
||||||
|
"?",
|
||||||
|
"@",
|
||||||
|
"[",
|
||||||
|
"\\",
|
||||||
|
"]",
|
||||||
|
"^",
|
||||||
|
"_",
|
||||||
|
"`",
|
||||||
|
"{",
|
||||||
|
"|",
|
||||||
|
"}",
|
||||||
|
"~",
|
||||||
|
];
|
||||||
|
|
||||||
|
// https://spec.commonmark.org/0.25/#punctuation-character
|
||||||
|
// https://unicode.org/Public/5.1.0/ucd/UCD.html#General_Category_Values
|
||||||
|
const unicodePunctuationClasses = [
|
||||||
|
/* Pc */ "Connector_Punctuation",
|
||||||
|
/* Pd */ "Dash_Punctuation",
|
||||||
|
/* Pe */ "Close_Punctuation",
|
||||||
|
/* Pf */ "Final_Punctuation",
|
||||||
|
/* Pi */ "Initial_Punctuation",
|
||||||
|
/* Po */ "Other_Punctuation",
|
||||||
|
/* Ps */ "Open_Punctuation",
|
||||||
|
];
|
||||||
|
|
||||||
|
const PUNCTUATION_REGEXP = new RegExp(
|
||||||
|
`(?:${[
|
||||||
|
new Charset(...asciiPunctuationCharacters).toRegExp("u").source,
|
||||||
|
...unicodePunctuationClasses.map(
|
||||||
|
(charset) => String.raw`\p{General_Category=${charset}}`,
|
||||||
|
"\u{ff5e}", // Used as a substitute for U+301C in Windows
|
||||||
|
),
|
||||||
|
].join("|")})`,
|
||||||
|
"u",
|
||||||
|
);
|
||||||
|
|
||||||
|
export { CJK_REGEXP, PUNCTUATION_REGEXP };
|
||||||
87
src/prettier-markdown/embed.js
Normal file
87
src/prettier-markdown/embed.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { hardline, markAsRoot } from "../document/builders.js";
|
||||||
|
import { replaceEndOfLine } from "../document/utils.js";
|
||||||
|
import getMaxContinuousCount from "../utils/get-max-continuous-count.js";
|
||||||
|
import inferParser from "../utils/infer-parser.js";
|
||||||
|
import { getFencedCodeBlockValue } from "./utils.js";
|
||||||
|
|
||||||
|
function embed(path, options) {
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
if (node.type === "code" && node.lang !== null) {
|
||||||
|
const parser = inferParser(options, { language: node.lang });
|
||||||
|
if (parser) {
|
||||||
|
return async (textToDoc) => {
|
||||||
|
const styleUnit = options.__inJsTemplate ? "~" : "`";
|
||||||
|
const style = styleUnit.repeat(
|
||||||
|
Math.max(3, getMaxContinuousCount(node.value, styleUnit) + 1),
|
||||||
|
);
|
||||||
|
const newOptions = { parser };
|
||||||
|
|
||||||
|
// Override the filepath option.
|
||||||
|
// This is because whether the trailing comma of type parameters
|
||||||
|
// should be printed depends on whether it is `*.ts` or `*.tsx`.
|
||||||
|
// https://github.com/prettier/prettier/issues/15282
|
||||||
|
if (node.lang === "ts" || node.lang === "typescript") {
|
||||||
|
newOptions.filepath = "dummy.ts";
|
||||||
|
} else if (node.lang === "tsx") {
|
||||||
|
newOptions.filepath = "dummy.tsx";
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = await textToDoc(
|
||||||
|
getFencedCodeBlockValue(node, options.originalText),
|
||||||
|
newOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
return markAsRoot([
|
||||||
|
style,
|
||||||
|
node.lang,
|
||||||
|
node.meta ? " " + node.meta : "",
|
||||||
|
hardline,
|
||||||
|
replaceEndOfLine(doc),
|
||||||
|
hardline,
|
||||||
|
style,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (node.type) {
|
||||||
|
// MDX
|
||||||
|
case "import":
|
||||||
|
case "export":
|
||||||
|
return (textToDoc) =>
|
||||||
|
textToDoc(node.value, {
|
||||||
|
// TODO: Rename this option since it's not used in HTML
|
||||||
|
__onHtmlBindingRoot: (ast) => validateImportExport(ast, node.type),
|
||||||
|
parser: "babel",
|
||||||
|
});
|
||||||
|
case "jsx":
|
||||||
|
return (textToDoc) =>
|
||||||
|
textToDoc(`<$>${node.value}</$>`, {
|
||||||
|
parser: "__js_expression",
|
||||||
|
rootMarker: "mdx",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateImportExport(ast, type) {
|
||||||
|
const {
|
||||||
|
program: { body },
|
||||||
|
} = ast;
|
||||||
|
|
||||||
|
// https://github.com/mdx-js/mdx/blob/3430138958c9c0344ecad9d59e0d6b5d72bedae3/packages/remark-mdx/extract-imports-and-exports.js#L16
|
||||||
|
if (
|
||||||
|
!body.every(
|
||||||
|
(node) =>
|
||||||
|
node.type === "ImportDeclaration" ||
|
||||||
|
node.type === "ExportDefaultDeclaration" ||
|
||||||
|
node.type === "ExportNamedDeclaration",
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(`Unexpected '${type}' in MDX.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default embed;
|
||||||
123
src/prettier-markdown/format-markdown.js
Normal file
123
src/prettier-markdown/format-markdown.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* Type-safe wrapper for Prettier's markdown formatting
|
||||||
|
*
|
||||||
|
* This module provides a synchronous interface to Prettier's markdown
|
||||||
|
* parser and printer, adapted to work within a Prettier plugin context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { markdown: markdownParser } = require("./parser-markdown.js");
|
||||||
|
const printer = require("./printer-markdown.js");
|
||||||
|
const { getDocPrinter } = require("./adapter-prettier-internals.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a markdown string using Prettier's markdown parser and printer
|
||||||
|
*
|
||||||
|
* @param {string} markdown - The markdown string to format
|
||||||
|
* @param {Object} options - Formatting options
|
||||||
|
* @param {number} [options.printWidth=80] - Maximum line width
|
||||||
|
* @param {number} [options.tabWidth=2] - Tab width
|
||||||
|
* @param {string} [options.proseWrap='preserve'] - Prose wrapping mode
|
||||||
|
* @param {boolean} [options.singleQuote=false] - Use single quotes
|
||||||
|
* @returns {string} The formatted markdown string, or the original if formatting fails
|
||||||
|
*/
|
||||||
|
function formatMarkdown(markdown, options = {}) {
|
||||||
|
if (!markdown || typeof markdown !== "string") {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = markdown.trim();
|
||||||
|
if (trimmed.length === 0) {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse markdown to AST
|
||||||
|
const ast = markdownParser.parse(trimmed, {
|
||||||
|
originalText: trimmed,
|
||||||
|
filepath: "temp.md",
|
||||||
|
printWidth: options.printWidth || 80,
|
||||||
|
tabWidth: options.tabWidth || 2,
|
||||||
|
proseWrap: options.proseWrap || "preserve",
|
||||||
|
singleQuote: options.singleQuote || false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create an AstPath-like object for the printer
|
||||||
|
const astPath = {
|
||||||
|
getNode: () => ast,
|
||||||
|
stack: [ast],
|
||||||
|
node: ast,
|
||||||
|
callParent: (fn) => fn(astPath),
|
||||||
|
each: (fn) => {
|
||||||
|
if (ast.children) {
|
||||||
|
ast.children.forEach((child, index) => {
|
||||||
|
const childPath = {
|
||||||
|
getNode: () => child,
|
||||||
|
stack: [...astPath.stack, child],
|
||||||
|
node: child,
|
||||||
|
index,
|
||||||
|
previous: index > 0 ? ast.children[index - 1] : null,
|
||||||
|
next: index < ast.children.length - 1 ? ast.children[index + 1] : null,
|
||||||
|
parent: ast,
|
||||||
|
isFirst: index === 0,
|
||||||
|
isLast: index === ast.children.length - 1,
|
||||||
|
callParent: (fn) => fn(childPath),
|
||||||
|
};
|
||||||
|
fn(childPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a print function for recursive printing
|
||||||
|
const createPrintFn = (path) => {
|
||||||
|
return (printPath) => {
|
||||||
|
return printer.print(printPath, {
|
||||||
|
printWidth: options.printWidth || 80,
|
||||||
|
tabWidth: options.tabWidth || 2,
|
||||||
|
proseWrap: options.proseWrap || "preserve",
|
||||||
|
singleQuote: options.singleQuote || false,
|
||||||
|
originalText: trimmed,
|
||||||
|
}, createPrintFn);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print the AST to a Doc object
|
||||||
|
const doc = printer.print(astPath, {
|
||||||
|
printWidth: options.printWidth || 80,
|
||||||
|
tabWidth: options.tabWidth || 2,
|
||||||
|
proseWrap: options.proseWrap || "preserve",
|
||||||
|
singleQuote: options.singleQuote || false,
|
||||||
|
originalText: trimmed,
|
||||||
|
}, createPrintFn);
|
||||||
|
|
||||||
|
// Convert Doc to string
|
||||||
|
if (typeof doc === "string") {
|
||||||
|
return doc.trimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to convert Doc object to string using Prettier's doc printer
|
||||||
|
const docPrinter = getDocPrinter();
|
||||||
|
if (docPrinter && typeof docPrinter === "function") {
|
||||||
|
try {
|
||||||
|
const formattedString = docPrinter(doc, {
|
||||||
|
printWidth: options.printWidth || 80,
|
||||||
|
tabWidth: options.tabWidth || 2,
|
||||||
|
useTabs: false,
|
||||||
|
});
|
||||||
|
return typeof formattedString === "string" ? formattedString.trimEnd() : markdown;
|
||||||
|
} catch {
|
||||||
|
// Doc printing failed
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't convert Doc to string, return original
|
||||||
|
return markdown;
|
||||||
|
} catch (error) {
|
||||||
|
// Parsing or printing failed, return original
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { formatMarkdown };
|
||||||
|
|
||||||
110
src/prettier-markdown/format-markdown.ts
Normal file
110
src/prettier-markdown/format-markdown.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Type-safe wrapper for Prettier's markdown formatting
|
||||||
|
*
|
||||||
|
* This module provides a synchronous interface to Prettier's markdown
|
||||||
|
* parser and printer, adapted to work within a Prettier plugin context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ParserOptions } from "prettier";
|
||||||
|
import {
|
||||||
|
getDocPrinter,
|
||||||
|
getDocumentBuilders,
|
||||||
|
getDocumentConstants,
|
||||||
|
getDocumentUtils,
|
||||||
|
} from "./adapter-prettier-internals.js";
|
||||||
|
import { markdown as markdownParser } from "./parser-markdown.js";
|
||||||
|
import printer from "./printer-markdown.js";
|
||||||
|
|
||||||
|
interface MarkdownFormatOptions {
|
||||||
|
printWidth?: number;
|
||||||
|
tabWidth?: number;
|
||||||
|
proseWrap?: "always" | "never" | "preserve";
|
||||||
|
singleQuote?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a markdown string using Prettier's markdown parser and printer
|
||||||
|
*
|
||||||
|
* @param markdown - The markdown string to format
|
||||||
|
* @param options - Formatting options
|
||||||
|
* @returns The formatted markdown string, or the original if formatting fails
|
||||||
|
*/
|
||||||
|
export function formatMarkdown(markdown: string, options: MarkdownFormatOptions = {}): string {
|
||||||
|
if (!markdown || typeof markdown !== "string") {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = markdown.trim();
|
||||||
|
if (trimmed.length === 0) {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse markdown to AST
|
||||||
|
const ast = markdownParser.parse(trimmed, {
|
||||||
|
originalText: trimmed,
|
||||||
|
filepath: "temp.md",
|
||||||
|
} as ParserOptions);
|
||||||
|
|
||||||
|
// Create an AstPath-like object for the printer
|
||||||
|
const astPath = {
|
||||||
|
getNode: () => ast,
|
||||||
|
stack: [ast],
|
||||||
|
callParent: (fn: (path: any) => any) => fn(astPath),
|
||||||
|
each: (fn: (path: any) => void) => {
|
||||||
|
if (ast.children) {
|
||||||
|
ast.children.forEach((child: any, index: number) => {
|
||||||
|
const childPath = {
|
||||||
|
getNode: () => child,
|
||||||
|
stack: [...astPath.stack, child],
|
||||||
|
index,
|
||||||
|
previous: index > 0 ? ast.children[index - 1] : null,
|
||||||
|
next: index < ast.children.length - 1 ? ast.children[index + 1] : null,
|
||||||
|
parent: ast,
|
||||||
|
isFirst: index === 0,
|
||||||
|
isLast: index === ast.children.length - 1,
|
||||||
|
};
|
||||||
|
fn(childPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a print function for recursive printing
|
||||||
|
const createPrintFn = (path: any): any => {
|
||||||
|
return (printPath: any) => {
|
||||||
|
return printer.print(printPath, options as ParserOptions, createPrintFn);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print the AST to a Doc object
|
||||||
|
const doc = printer.print(astPath, options as ParserOptions, createPrintFn);
|
||||||
|
|
||||||
|
// Convert Doc to string
|
||||||
|
if (typeof doc === "string") {
|
||||||
|
return doc.trimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to convert Doc object to string using Prettier's doc printer
|
||||||
|
const docPrinter = getDocPrinter();
|
||||||
|
if (docPrinter && typeof docPrinter === "function") {
|
||||||
|
try {
|
||||||
|
const formattedString = docPrinter(doc, {
|
||||||
|
printWidth: options.printWidth || 80,
|
||||||
|
tabWidth: options.tabWidth || 2,
|
||||||
|
useTabs: false,
|
||||||
|
});
|
||||||
|
return typeof formattedString === "string" ? formattedString.trimEnd() : markdown;
|
||||||
|
} catch {
|
||||||
|
// Doc printing failed
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't convert Doc to string, return original
|
||||||
|
return markdown;
|
||||||
|
} catch (error) {
|
||||||
|
// Parsing or printing failed, return original
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/prettier-markdown/get-visitor-keys.js
Normal file
6
src/prettier-markdown/get-visitor-keys.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import createGetVisitorKeys from "../utils/create-get-visitor-keys.js";
|
||||||
|
import visitorKeys from "./visitor-keys.js";
|
||||||
|
|
||||||
|
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||||
|
|
||||||
|
export default getVisitorKeys;
|
||||||
8
src/prettier-markdown/index.js
Normal file
8
src/prettier-markdown/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import printer from "./printer-markdown.js";
|
||||||
|
|
||||||
|
export const printers = {
|
||||||
|
mdast: printer,
|
||||||
|
};
|
||||||
|
export { default as languages } from "./languages.evaluate.js";
|
||||||
|
export { default as options } from "./options.js";
|
||||||
|
export * as parsers from "./parser-markdown.js";
|
||||||
20
src/prettier-markdown/languages.evaluate.js
Normal file
20
src/prettier-markdown/languages.evaluate.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import * as linguistLanguages from "linguist-languages";
|
||||||
|
import createLanguage from "../utils/create-language.js";
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
createLanguage(linguistLanguages.Markdown, (data) => ({
|
||||||
|
parsers: ["markdown"],
|
||||||
|
vscodeLanguageIds: ["markdown"],
|
||||||
|
filenames: [...data.filenames, "README"],
|
||||||
|
extensions: data.extensions.filter((extension) => extension !== ".mdx"),
|
||||||
|
})),
|
||||||
|
createLanguage(linguistLanguages.Markdown, () => ({
|
||||||
|
name: "MDX",
|
||||||
|
parsers: ["mdx"],
|
||||||
|
vscodeLanguageIds: ["mdx"],
|
||||||
|
filenames: [],
|
||||||
|
extensions: [".mdx"],
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default languages;
|
||||||
4
src/prettier-markdown/loc.js
Normal file
4
src/prettier-markdown/loc.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const locStart = (node) => node.position.start.offset;
|
||||||
|
const locEnd = (node) => node.position.end.offset;
|
||||||
|
|
||||||
|
export { locEnd, locStart };
|
||||||
83
src/prettier-markdown/mdx.js
Normal file
83
src/prettier-markdown/mdx.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* modified from https://github.com/mdx-js/mdx/blob/c91b00c673bcf3e7c28b861fd692b69016026c45/packages/remark-mdx/index.js
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2018 Compositor and Zeit, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const IMPORT_REGEX = /^import\s/u;
|
||||||
|
const EXPORT_REGEX = /^export\s/u;
|
||||||
|
const BLOCKS_REGEX = String.raw`[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*|`;
|
||||||
|
const COMMENT_REGEX = /<!---->|<!---?[^>-](?:-?[^-])*-->/u;
|
||||||
|
const ES_COMMENT_REGEX = /^\{\s*\/\*(.*)\*\/\s*\}/u;
|
||||||
|
const EMPTY_NEWLINE = "\n\n";
|
||||||
|
|
||||||
|
const isImport = (text) => IMPORT_REGEX.test(text);
|
||||||
|
const isExport = (text) => EXPORT_REGEX.test(text);
|
||||||
|
const isImportOrExport = (text) => isImport(text) || isExport(text);
|
||||||
|
|
||||||
|
const tokenizeEsSyntax = (eat, value) => {
|
||||||
|
const index = value.indexOf(EMPTY_NEWLINE);
|
||||||
|
const subvalue = index === -1 ? value : value.slice(0, index);
|
||||||
|
|
||||||
|
if (isImportOrExport(subvalue)) {
|
||||||
|
return eat(subvalue)({
|
||||||
|
type: isExport(subvalue) ? "export" : "import",
|
||||||
|
value: subvalue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokenizeEsSyntax.notInBlock = true;
|
||||||
|
|
||||||
|
tokenizeEsSyntax.locator = (value /* , fromIndex*/) =>
|
||||||
|
isImportOrExport(value) ? -1 : 1;
|
||||||
|
|
||||||
|
const tokenizeEsComment = (eat, value) => {
|
||||||
|
const match = ES_COMMENT_REGEX.exec(value);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return eat(match[0])({
|
||||||
|
type: "esComment",
|
||||||
|
value: match[1].trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokenizeEsComment.locator = (value, fromIndex) => value.indexOf("{", fromIndex);
|
||||||
|
|
||||||
|
/** @import {Plugin, Settings} from "unified" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Plugin<[], Settings>}
|
||||||
|
*/
|
||||||
|
const esSyntax = function () {
|
||||||
|
const { Parser } = this;
|
||||||
|
const { blockTokenizers, blockMethods, inlineTokenizers, inlineMethods } =
|
||||||
|
Parser.prototype;
|
||||||
|
|
||||||
|
blockTokenizers.esSyntax = tokenizeEsSyntax;
|
||||||
|
inlineTokenizers.esComment = tokenizeEsComment;
|
||||||
|
|
||||||
|
blockMethods.splice(blockMethods.indexOf("paragraph"), 0, "esSyntax");
|
||||||
|
inlineMethods.splice(inlineMethods.indexOf("text"), 0, "esComment");
|
||||||
|
};
|
||||||
|
|
||||||
|
export { BLOCKS_REGEX, COMMENT_REGEX, esSyntax };
|
||||||
20
src/prettier-markdown/options.js
Normal file
20
src/prettier-markdown/options.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Options for markdown formatting
|
||||||
|
// These match Prettier's default markdown options
|
||||||
|
// Update this file if Prettier's markdown options change
|
||||||
|
const options = {
|
||||||
|
proseWrap: {
|
||||||
|
type: "choice",
|
||||||
|
default: "preserve",
|
||||||
|
choices: [
|
||||||
|
{ value: "always", description: "Wrap prose if it exceeds the print width" },
|
||||||
|
{ value: "never", description: "Don't wrap prose" },
|
||||||
|
{ value: "preserve", description: "Preserve the original wrapping" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
singleQuote: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default options;
|
||||||
57
src/prettier-markdown/parser-markdown.js
Normal file
57
src/prettier-markdown/parser-markdown.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import footnotes from "remark-footnotes";
|
||||||
|
import remarkMath from "remark-math";
|
||||||
|
import remarkParse from "remark-parse";
|
||||||
|
import unified from "unified";
|
||||||
|
import { locEnd, locStart } from "./loc.js";
|
||||||
|
import { BLOCKS_REGEX, esSyntax } from "./mdx.js";
|
||||||
|
import { hasIgnorePragma, hasPragma } from "./pragma.js";
|
||||||
|
import frontMatter from "./unified-plugins/front-matter.js";
|
||||||
|
import htmlToJsx from "./unified-plugins/html-to-jsx.js";
|
||||||
|
import liquid from "./unified-plugins/liquid.js";
|
||||||
|
import wikiLink from "./unified-plugins/wiki-link.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* based on [MDAST](https://github.com/syntax-tree/mdast) with following modifications:
|
||||||
|
*
|
||||||
|
* 1. restore unescaped character (Text)
|
||||||
|
* 2. merge continuous Texts
|
||||||
|
* 3. replace whitespaces in InlineCode#value with one whitespace
|
||||||
|
* reference: http://spec.commonmark.org/0.25/#example-605
|
||||||
|
* 4. split Text into Sentence
|
||||||
|
*
|
||||||
|
* interface Word { value: string }
|
||||||
|
* interface Whitespace { value: string }
|
||||||
|
* interface Sentence { children: Array<Word | Whitespace> }
|
||||||
|
* interface InlineCode { children: Array<Sentence> }
|
||||||
|
*/
|
||||||
|
function createParse({ isMDX }) {
|
||||||
|
return (text) => {
|
||||||
|
const processor = unified()
|
||||||
|
.use(remarkParse, {
|
||||||
|
commonmark: true,
|
||||||
|
...(isMDX && { blocks: [BLOCKS_REGEX] }),
|
||||||
|
})
|
||||||
|
.use(footnotes)
|
||||||
|
.use(frontMatter)
|
||||||
|
.use(remarkMath)
|
||||||
|
.use(isMDX ? esSyntax : noop)
|
||||||
|
.use(liquid)
|
||||||
|
.use(isMDX ? htmlToJsx : noop)
|
||||||
|
.use(wikiLink);
|
||||||
|
return processor.run(processor.parse(text));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function noop() {}
|
||||||
|
|
||||||
|
const baseParser = {
|
||||||
|
astFormat: "mdast",
|
||||||
|
hasPragma,
|
||||||
|
hasIgnorePragma,
|
||||||
|
locStart,
|
||||||
|
locEnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const markdown = { ...baseParser, parse: createParse({ isMDX: false }) };
|
||||||
|
export const mdx = { ...baseParser, parse: createParse({ isMDX: true }) };
|
||||||
|
export { markdown as remark };
|
||||||
25
src/prettier-markdown/pragma.js
Normal file
25
src/prettier-markdown/pragma.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { parseFrontMatter } from "../main/front-matter/index.js";
|
||||||
|
import {
|
||||||
|
FORMAT_PRAGMA_TO_INSERT,
|
||||||
|
MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP,
|
||||||
|
MARKDOWN_HAS_PRAGMA_REGEXP,
|
||||||
|
} from "../utils/pragma/pragma.evaluate.js";
|
||||||
|
|
||||||
|
const hasPragma = (text) =>
|
||||||
|
parseFrontMatter(text).content.trimStart().match(MARKDOWN_HAS_PRAGMA_REGEXP)
|
||||||
|
?.index === 0;
|
||||||
|
|
||||||
|
const hasIgnorePragma = (text) =>
|
||||||
|
parseFrontMatter(text)
|
||||||
|
.content.trimStart()
|
||||||
|
.match(MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP)?.index === 0;
|
||||||
|
|
||||||
|
const insertPragma = (text) => {
|
||||||
|
const { frontMatter } = parseFrontMatter(text);
|
||||||
|
const pragma = `<!-- @${FORMAT_PRAGMA_TO_INSERT} -->`;
|
||||||
|
return frontMatter
|
||||||
|
? `${frontMatter.raw}\n\n${pragma}\n\n${text.slice(frontMatter.end.index)}`
|
||||||
|
: `${pragma}\n\n${text}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { hasIgnorePragma, hasPragma, insertPragma };
|
||||||
55
src/prettier-markdown/print-paragraph.js
Normal file
55
src/prettier-markdown/print-paragraph.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { fill } from "../document/builders.js";
|
||||||
|
import { DOC_TYPE_ARRAY, DOC_TYPE_FILL } from "../document/constants.js";
|
||||||
|
import { getDocType } from "../document/utils.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import AstPath from "../common/ast-path.js"
|
||||||
|
* @import {Doc} from "../document/builders.js"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @param {*} options
|
||||||
|
* @param {*} print
|
||||||
|
* @returns {Doc}
|
||||||
|
*/
|
||||||
|
function printParagraph(path, options, print) {
|
||||||
|
const parts = path.map(print, "children");
|
||||||
|
return flattenFill(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Doc[]} docs
|
||||||
|
* @returns {Doc}
|
||||||
|
*/
|
||||||
|
function flattenFill(docs) {
|
||||||
|
/*
|
||||||
|
* We assume parts always meet following conditions:
|
||||||
|
* - parts.length is odd
|
||||||
|
* - odd elements are line-like doc that comes from odd element off inner fill
|
||||||
|
*/
|
||||||
|
/** @type {Doc[]} */
|
||||||
|
const parts = [""];
|
||||||
|
|
||||||
|
(function rec(/** @type {*} */ docArray) {
|
||||||
|
for (const doc of docArray) {
|
||||||
|
const docType = getDocType(doc);
|
||||||
|
if (docType === DOC_TYPE_ARRAY) {
|
||||||
|
rec(doc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let head = doc;
|
||||||
|
let rest = [];
|
||||||
|
if (docType === DOC_TYPE_FILL) {
|
||||||
|
[head, ...rest] = doc.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.push([parts.pop(), head], ...rest);
|
||||||
|
}
|
||||||
|
})(docs);
|
||||||
|
|
||||||
|
return fill(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { printParagraph };
|
||||||
256
src/prettier-markdown/print-preprocess.js
Normal file
256
src/prettier-markdown/print-preprocess.js
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js";
|
||||||
|
import { getOrderedListItemInfo, mapAst, splitText } from "./utils.js";
|
||||||
|
|
||||||
|
// 0x0 ~ 0x10ffff
|
||||||
|
const isSingleCharRegex = /^\\?.$/su;
|
||||||
|
const isNewLineBlockquoteRegex = /^\n *>[ >]*$/u;
|
||||||
|
|
||||||
|
function preprocess(ast, options) {
|
||||||
|
ast = restoreUnescapedCharacter(ast, options);
|
||||||
|
ast = mergeContinuousTexts(ast);
|
||||||
|
ast = transformIndentedCodeblockAndMarkItsParentList(ast, options);
|
||||||
|
ast = markAlignedList(ast, options);
|
||||||
|
ast = splitTextIntoSentences(ast);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreUnescapedCharacter(ast, options) {
|
||||||
|
return mapAst(ast, (node) => {
|
||||||
|
if (node.type !== "text") {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value } = node;
|
||||||
|
|
||||||
|
if (
|
||||||
|
value === "*" ||
|
||||||
|
value === "_" || // handle these cases in printer
|
||||||
|
!isSingleCharRegex.test(value) ||
|
||||||
|
node.position.end.offset - node.position.start.offset === value.length
|
||||||
|
) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = options.originalText.slice(
|
||||||
|
node.position.start.offset,
|
||||||
|
node.position.end.offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isNewLineBlockquoteRegex.test(text)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...node, value: text };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeChildren(ast, shouldMerge, mergeNode) {
|
||||||
|
return mapAst(ast, (node) => {
|
||||||
|
if (!node.children) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = [];
|
||||||
|
let lastChild;
|
||||||
|
let changed;
|
||||||
|
for (let child of node.children) {
|
||||||
|
if (lastChild && shouldMerge(lastChild, child)) {
|
||||||
|
child = mergeNode(lastChild, child);
|
||||||
|
// Replace the previous node
|
||||||
|
children.splice(-1, 1, child);
|
||||||
|
changed ||= true;
|
||||||
|
} else {
|
||||||
|
children.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastChild = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? { ...node, children } : node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeContinuousTexts(ast) {
|
||||||
|
return mergeChildren(
|
||||||
|
ast,
|
||||||
|
(prevNode, node) => prevNode.type === "text" && node.type === "text",
|
||||||
|
(prevNode, node) => ({
|
||||||
|
type: "text",
|
||||||
|
value: prevNode.value + node.value,
|
||||||
|
position: {
|
||||||
|
start: prevNode.position.start,
|
||||||
|
end: node.position.end,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitTextIntoSentences(ast) {
|
||||||
|
return mapAst(ast, (node, index, [parentNode]) => {
|
||||||
|
if (node.type !== "text") {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { value } = node;
|
||||||
|
|
||||||
|
if (parentNode.type === "paragraph") {
|
||||||
|
// CommonMark doesn't remove trailing/leading \f, but it should be
|
||||||
|
// removed in the HTML rendering process
|
||||||
|
if (index === 0) {
|
||||||
|
value = htmlWhitespaceUtils.trimStart(value);
|
||||||
|
}
|
||||||
|
if (index === parentNode.children.length - 1) {
|
||||||
|
value = htmlWhitespaceUtils.trimEnd(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "sentence",
|
||||||
|
position: node.position,
|
||||||
|
children: splitText(value),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformIndentedCodeblockAndMarkItsParentList(ast, options) {
|
||||||
|
return mapAst(ast, (node, index, parentStack) => {
|
||||||
|
if (node.type === "code") {
|
||||||
|
// the first char may point to `\n`, e.g. `\n\t\tbar`, just ignore it
|
||||||
|
const isIndented = /^\n?(?: {4,}|\t)/u.test(
|
||||||
|
options.originalText.slice(
|
||||||
|
node.position.start.offset,
|
||||||
|
node.position.end.offset,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
node.isIndented = isIndented;
|
||||||
|
|
||||||
|
if (isIndented) {
|
||||||
|
for (let i = 0; i < parentStack.length; i++) {
|
||||||
|
const parent = parentStack[i];
|
||||||
|
|
||||||
|
// no need to check checked items
|
||||||
|
if (parent.hasIndentedCodeblock) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.type === "list") {
|
||||||
|
parent.hasIndentedCodeblock = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function markAlignedList(ast, options) {
|
||||||
|
return mapAst(ast, (node, index, parentStack) => {
|
||||||
|
if (node.type === "list" && node.children.length > 0) {
|
||||||
|
// if one of its parents is not aligned, it's not possible to be aligned in sub-lists
|
||||||
|
for (let i = 0; i < parentStack.length; i++) {
|
||||||
|
const parent = parentStack[i];
|
||||||
|
if (parent.type === "list" && !parent.isAligned) {
|
||||||
|
node.isAligned = false;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.isAligned = isAligned(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
function getListItemStart(listItem) {
|
||||||
|
return listItem.children.length === 0
|
||||||
|
? -1
|
||||||
|
: listItem.children[0].position.start.column - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAligned(list) {
|
||||||
|
if (!list.ordered) {
|
||||||
|
/**
|
||||||
|
* - 123
|
||||||
|
* - 123
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [firstItem, secondItem] = list.children;
|
||||||
|
|
||||||
|
const firstInfo = getOrderedListItemInfo(firstItem, options);
|
||||||
|
|
||||||
|
if (firstInfo.leadingSpaces.length > 1) {
|
||||||
|
/**
|
||||||
|
* 1. 123
|
||||||
|
*
|
||||||
|
* 1. 123
|
||||||
|
* 1. 123
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstStart = getListItemStart(firstItem);
|
||||||
|
|
||||||
|
if (firstStart === -1) {
|
||||||
|
/**
|
||||||
|
* 1.
|
||||||
|
*
|
||||||
|
* 1.
|
||||||
|
* 1.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.children.length === 1) {
|
||||||
|
/**
|
||||||
|
* aligned:
|
||||||
|
*
|
||||||
|
* 11. 123
|
||||||
|
*
|
||||||
|
* not aligned:
|
||||||
|
*
|
||||||
|
* 1. 123
|
||||||
|
*/
|
||||||
|
return firstStart % options.tabWidth === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondStart = getListItemStart(secondItem);
|
||||||
|
|
||||||
|
if (firstStart !== secondStart) {
|
||||||
|
/**
|
||||||
|
* 11. 123
|
||||||
|
* 1. 123
|
||||||
|
*
|
||||||
|
* 1. 123
|
||||||
|
* 11. 123
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstStart % options.tabWidth === 0) {
|
||||||
|
/**
|
||||||
|
* 11. 123
|
||||||
|
* 12. 123
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aligned:
|
||||||
|
*
|
||||||
|
* 11. 123
|
||||||
|
* 1. 123
|
||||||
|
*
|
||||||
|
* not aligned:
|
||||||
|
*
|
||||||
|
* 1. 123
|
||||||
|
* 2. 123
|
||||||
|
*/
|
||||||
|
const secondInfo = getOrderedListItemInfo(secondItem, options);
|
||||||
|
return secondInfo.leadingSpaces.length > 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default preprocess;
|
||||||
37
src/prettier-markdown/print-sentence.js
Normal file
37
src/prettier-markdown/print-sentence.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @import AstPath from "../common/ast-path.js"
|
||||||
|
* @import {Doc} from "../document/builders.js"
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fill } from "../document/builders.js";
|
||||||
|
import { DOC_TYPE_STRING } from "../document/constants.js";
|
||||||
|
import { getDocType } from "../document/utils.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @param {*} print
|
||||||
|
* @returns {Doc}
|
||||||
|
*/
|
||||||
|
function printSentence(path, print) {
|
||||||
|
/** @type {Doc[]} */
|
||||||
|
const parts = [""];
|
||||||
|
|
||||||
|
path.each(() => {
|
||||||
|
const { node } = path;
|
||||||
|
const doc = print();
|
||||||
|
switch (node.type) {
|
||||||
|
case "whitespace":
|
||||||
|
if (getDocType(doc) !== DOC_TYPE_STRING) {
|
||||||
|
parts.push(doc, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fallthrough
|
||||||
|
default:
|
||||||
|
parts.push([parts.pop(), doc]);
|
||||||
|
}
|
||||||
|
}, "children");
|
||||||
|
|
||||||
|
return fill(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { printSentence };
|
||||||
266
src/prettier-markdown/print-whitespace.js
Normal file
266
src/prettier-markdown/print-whitespace.js
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
import { hardline, line, softline } from "../document/builders.js";
|
||||||
|
import {
|
||||||
|
KIND_CJ_LETTER,
|
||||||
|
KIND_CJK_PUNCTUATION,
|
||||||
|
KIND_K_LETTER,
|
||||||
|
KIND_NON_CJK,
|
||||||
|
} from "./utils.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {WordNode, WhitespaceValue, WordKind} from "./utils.js"
|
||||||
|
* @import AstPath from "../common/ast-path.js"
|
||||||
|
* @typedef {"always" | "never" | "preserve"} ProseWrap
|
||||||
|
* @typedef {{ next?: WordNode | null, previous?: WordNode | null }}
|
||||||
|
* AdjacentNodes Nodes adjacent to a `whitespace` node. Are always of type
|
||||||
|
* `word`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SINGLE_LINE_NODE_TYPES = new Set([
|
||||||
|
"heading",
|
||||||
|
"tableCell",
|
||||||
|
"link",
|
||||||
|
"wikiLink",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A line break between a character from this set and CJ can be converted to a
|
||||||
|
* space. Includes only ASCII punctuation marks for now.
|
||||||
|
*/
|
||||||
|
const lineBreakBetweenTheseAndCJConvertsToSpace = new Set(
|
||||||
|
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the preferred style of spacing between Chinese or Japanese and non-CJK
|
||||||
|
* characters in the parent `sentence` node.
|
||||||
|
*
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @returns {boolean} `true` if Space tends to be inserted between CJ and
|
||||||
|
* non-CJK, `false` otherwise.
|
||||||
|
*/
|
||||||
|
function isInSentenceWithCJSpaces({ parent: sentenceNode }) {
|
||||||
|
if (sentenceNode.usesCJSpaces === undefined) {
|
||||||
|
const stats = { " ": 0, "": 0 };
|
||||||
|
const { children } = sentenceNode;
|
||||||
|
|
||||||
|
for (let i = 1; i < children.length - 1; ++i) {
|
||||||
|
const node = children[i];
|
||||||
|
if (
|
||||||
|
node.type === "whitespace" &&
|
||||||
|
(node.value === " " || node.value === "")
|
||||||
|
) {
|
||||||
|
const previousKind = children[i - 1].kind;
|
||||||
|
const nextKind = children[i + 1].kind;
|
||||||
|
if (
|
||||||
|
(previousKind === KIND_CJ_LETTER && nextKind === KIND_NON_CJK) ||
|
||||||
|
(previousKind === KIND_NON_CJK && nextKind === KIND_CJ_LETTER)
|
||||||
|
) {
|
||||||
|
++stats[node.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject a property to cache the result.
|
||||||
|
sentenceNode.usesCJSpaces = stats[" "] > stats[""];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sentenceNode.usesCJSpaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given `"\n"` node can be converted to a space.
|
||||||
|
*
|
||||||
|
* For example, if you would like to squash English text
|
||||||
|
*
|
||||||
|
* "You might want\nto use Prettier."
|
||||||
|
*
|
||||||
|
* into a single line, you would replace `"\n"` with `" "`:
|
||||||
|
*
|
||||||
|
* "You might want to use Prettier."
|
||||||
|
*
|
||||||
|
* However, Chinese and Japanese don't use U+0020 Space to divide words, so line
|
||||||
|
* breaks shouldn't be replaced with spaces for those languages.
|
||||||
|
*
|
||||||
|
* PRs are welcome to support line breaking rules for other languages.
|
||||||
|
*
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @param {boolean} isLink
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function lineBreakCanBeConvertedToSpace(path, isLink) {
|
||||||
|
if (isLink) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {AdjacentNodes} */
|
||||||
|
const { previous, next } = path;
|
||||||
|
|
||||||
|
// e.g. " \nletter"
|
||||||
|
if (!previous || !next) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousKind = previous.kind;
|
||||||
|
const nextKind = next.kind;
|
||||||
|
|
||||||
|
if (
|
||||||
|
// "\n" between non-CJK or Korean characters always can be converted to a
|
||||||
|
// space. Korean Hangul simulates Latin words. See
|
||||||
|
// https://github.com/prettier/prettier/issues/6516
|
||||||
|
(isNonCJKOrKoreanLetter(previousKind) &&
|
||||||
|
isNonCJKOrKoreanLetter(nextKind)) ||
|
||||||
|
// Han & Hangul: same way preferred
|
||||||
|
(previousKind === KIND_K_LETTER && nextKind === KIND_CJ_LETTER) ||
|
||||||
|
(nextKind === KIND_K_LETTER && previousKind === KIND_CJ_LETTER)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not convert \n to a space:
|
||||||
|
if (
|
||||||
|
// around CJK punctuation
|
||||||
|
previousKind === KIND_CJK_PUNCTUATION ||
|
||||||
|
nextKind === KIND_CJK_PUNCTUATION ||
|
||||||
|
// between CJ
|
||||||
|
(previousKind === KIND_CJ_LETTER && nextKind === KIND_CJ_LETTER)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest of this function deals only with line breaks between CJ and
|
||||||
|
// non-CJK characters.
|
||||||
|
|
||||||
|
// Convert a line break between CJ and certain non-letter characters (e.g.
|
||||||
|
// ASCII punctuation) to a space.
|
||||||
|
//
|
||||||
|
// E.g. :::\n句子句子句子\n::: → ::: 句子句子句子 :::
|
||||||
|
//
|
||||||
|
// Note: line breaks like "(\n句子句子\n)" or "句子\n." are suppressed in
|
||||||
|
// `isBreakable(...)`.
|
||||||
|
if (
|
||||||
|
lineBreakBetweenTheseAndCJConvertsToSpace.has(next.value[0]) ||
|
||||||
|
lineBreakBetweenTheseAndCJConvertsToSpace.has(previous.value.at(-1))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converting a line break between CJ and non-ASCII punctuation to a space is
|
||||||
|
// undesired in many cases. PRs are welcome to fine-tune this logic.
|
||||||
|
//
|
||||||
|
// Examples where \n must not be converted to a space:
|
||||||
|
//
|
||||||
|
// 1. "〜" (U+301C, belongs to Pd) in
|
||||||
|
//
|
||||||
|
// "ア〜\nエの中から1つ選べ。"
|
||||||
|
//
|
||||||
|
// 2. "…" (U+2026, belongs to Po) in
|
||||||
|
//
|
||||||
|
// "これはひどい……\nなんと汚いコミットログなんだ……"
|
||||||
|
if (previous.hasTrailingPunctuation || next.hasLeadingPunctuation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the sentence uses the style with spaces between CJ and non-CJK, "\n" can
|
||||||
|
// be converted to a space.
|
||||||
|
return isInSentenceWithCJSpaces(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WordKind | undefined} kind
|
||||||
|
* @returns {boolean} `true` if `kind` is Korean letter or non-CJK
|
||||||
|
*/
|
||||||
|
function isNonCJKOrKoreanLetter(kind) {
|
||||||
|
return kind === KIND_NON_CJK || kind === KIND_K_LETTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether whitespace can be printed as a line break.
|
||||||
|
*
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @param {WhitespaceValue} value
|
||||||
|
* @param {ProseWrap} proseWrap
|
||||||
|
* @param {boolean} isLink
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isBreakable(path, value, proseWrap, isLink) {
|
||||||
|
if (
|
||||||
|
proseWrap !== "always" ||
|
||||||
|
path.hasAncestor((node) => SINGLE_LINE_NODE_TYPES.has(node.type))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLink) {
|
||||||
|
return value !== "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {AdjacentNodes} */
|
||||||
|
const { previous, next } = path;
|
||||||
|
|
||||||
|
// [1]: We will make a breaking change to the rule to convert spaces between
|
||||||
|
// a Chinese or Japanese character and another character in the future.
|
||||||
|
// Such a space must have been always interchangeable with a line break.
|
||||||
|
// https://wpt.fyi/results/css/css-text/line-breaking?label=master&label=experimental&aligned&q=segment-break-transformation-rules-
|
||||||
|
// [2]: we should not break lines even between Chinese/Japanese characters because Chrome & Safari replaces "\n" between such characters with " " now.
|
||||||
|
// [3]: Hangul (Korean) must simulate Latin words; see https://github.com/prettier/prettier/issues/6516
|
||||||
|
// [printable][""][Hangul] & vice versa => Don't break
|
||||||
|
// [printable][\n][Hangul] will be interchangeable to [printable][" "][Hangul] in the future
|
||||||
|
// (will be compatible with Firefox's behavior)
|
||||||
|
|
||||||
|
if (!previous || !next) {
|
||||||
|
// empty side is Latin ASCII symbol (e.g. *, [, ], or `)
|
||||||
|
// value is " " or "\n" (not "")
|
||||||
|
// [1] & [2]? No, it's the only exception because " " & "\n" have been always interchangeable only here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === "") {
|
||||||
|
// [1] & [2] & [3]
|
||||||
|
// At least either of previous or next is non-Latin (=CJK)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// See the same product terms as the following in lineBreakCanBeConvertedToSpace
|
||||||
|
// The behavior is consistent between browsers and Prettier in that line breaks between Korean and Chinese/Japanese letters are equivalent to spaces.
|
||||||
|
// Currently, [CJK punctuation][\n][Hangul] is interchangeable to [CJK punctuation][""][Hangul],
|
||||||
|
// but this is not compatible with Firefox's behavior.
|
||||||
|
// Will be changed to [CJK punctuation][" "][Hangul] in the future
|
||||||
|
(previous.kind === KIND_K_LETTER && next.kind === KIND_CJ_LETTER) ||
|
||||||
|
(next.kind === KIND_K_LETTER && previous.kind === KIND_CJ_LETTER)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [1] & [2]
|
||||||
|
if (previous.isCJ || next.isCJ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AstPath} path
|
||||||
|
* @param {WhitespaceValue} value
|
||||||
|
* @param {ProseWrap} proseWrap
|
||||||
|
* @param {boolean} [isLink] Special mode of (un)wrapping that preserves the
|
||||||
|
* normalized form of link labels. https://spec.commonmark.org/0.30/#matches
|
||||||
|
*/
|
||||||
|
function printWhitespace(path, value, proseWrap, isLink) {
|
||||||
|
if (proseWrap === "preserve" && value === "\n") {
|
||||||
|
return hardline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canBeSpace =
|
||||||
|
value === " " ||
|
||||||
|
(value === "\n" && lineBreakCanBeConvertedToSpace(path, isLink));
|
||||||
|
|
||||||
|
if (isBreakable(path, value, proseWrap, isLink)) {
|
||||||
|
return canBeSpace ? line : softline;
|
||||||
|
}
|
||||||
|
|
||||||
|
return canBeSpace ? " " : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export { printWhitespace };
|
||||||
81
src/prettier-markdown/print/table.js
Normal file
81
src/prettier-markdown/print/table.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
breakParent,
|
||||||
|
group,
|
||||||
|
hardlineWithoutBreakParent,
|
||||||
|
ifBreak,
|
||||||
|
join,
|
||||||
|
} from "../../document/builders.js";
|
||||||
|
import { printDocToString } from "../../document/printer.js";
|
||||||
|
import getStringWidth from "../../utils/get-string-width.js";
|
||||||
|
|
||||||
|
function printTable(path, options, print) {
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
const columnMaxWidths = [];
|
||||||
|
// { [rowIndex: number]: { [columnIndex: number]: {text: string, width: number} } }
|
||||||
|
const contents = path.map(
|
||||||
|
() =>
|
||||||
|
path.map(({ index: columnIndex }) => {
|
||||||
|
const text = printDocToString(print(), options).formatted;
|
||||||
|
const width = getStringWidth(text);
|
||||||
|
columnMaxWidths[columnIndex] = Math.max(
|
||||||
|
columnMaxWidths[columnIndex] ?? 3, // minimum width = 3 (---, :--, :-:, --:)
|
||||||
|
width,
|
||||||
|
);
|
||||||
|
return { text, width };
|
||||||
|
}, "children"),
|
||||||
|
"children",
|
||||||
|
);
|
||||||
|
|
||||||
|
const alignedTable = printTableContents(/* isCompact */ false);
|
||||||
|
if (options.proseWrap !== "never") {
|
||||||
|
return [breakParent, alignedTable];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only if the --prose-wrap never is set and it exceeds the print width.
|
||||||
|
const compactTable = printTableContents(/* isCompact */ true);
|
||||||
|
return [breakParent, group(ifBreak(compactTable, alignedTable))];
|
||||||
|
|
||||||
|
function printTableContents(isCompact) {
|
||||||
|
return join(
|
||||||
|
hardlineWithoutBreakParent,
|
||||||
|
[
|
||||||
|
printRow(contents[0], isCompact),
|
||||||
|
printAlign(isCompact),
|
||||||
|
...contents
|
||||||
|
.slice(1)
|
||||||
|
.map((rowContents) => printRow(rowContents, isCompact)),
|
||||||
|
].map((columns) => `| ${columns.join(" | ")} |`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function printAlign(isCompact) {
|
||||||
|
return columnMaxWidths.map((width, index) => {
|
||||||
|
const align = node.align[index];
|
||||||
|
const first = align === "center" || align === "left" ? ":" : "-";
|
||||||
|
const last = align === "center" || align === "right" ? ":" : "-";
|
||||||
|
const middle = isCompact ? "-" : "-".repeat(width - 2);
|
||||||
|
return `${first}${middle}${last}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function printRow(columns, isCompact) {
|
||||||
|
return columns.map(({ text, width }, columnIndex) => {
|
||||||
|
if (isCompact) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
const spaces = columnMaxWidths[columnIndex] - width;
|
||||||
|
const align = node.align[columnIndex];
|
||||||
|
let before = 0;
|
||||||
|
if (align === "right") {
|
||||||
|
before = spaces;
|
||||||
|
} else if (align === "center") {
|
||||||
|
before = Math.floor(spaces / 2);
|
||||||
|
}
|
||||||
|
const after = spaces - before;
|
||||||
|
return `${" ".repeat(before)}${text}${" ".repeat(after)}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { printTable };
|
||||||
810
src/prettier-markdown/printer-markdown.js
Normal file
810
src/prettier-markdown/printer-markdown.js
Normal file
@@ -0,0 +1,810 @@
|
|||||||
|
import collapseWhiteSpace from "collapse-white-space";
|
||||||
|
import escapeStringRegexp from "escape-string-regexp";
|
||||||
|
import {
|
||||||
|
align,
|
||||||
|
fill,
|
||||||
|
group,
|
||||||
|
hardline,
|
||||||
|
indent,
|
||||||
|
line,
|
||||||
|
literalline,
|
||||||
|
markAsRoot,
|
||||||
|
softline,
|
||||||
|
} from "./adapter-document-builders.js";
|
||||||
|
import { DOC_TYPE_STRING } from "./adapter-document-constants.js";
|
||||||
|
import { getDocType, replaceEndOfLine } from "./adapter-document-utils.js";
|
||||||
|
import {
|
||||||
|
getMaxContinuousCount,
|
||||||
|
getMinNotPresentContinuousCount,
|
||||||
|
getPreferredQuote,
|
||||||
|
UnexpectedNodeError,
|
||||||
|
} from "./adapter-prettier-utils.js";
|
||||||
|
import clean from "./clean.js";
|
||||||
|
import { PUNCTUATION_REGEXP } from "./constants.evaluate.js";
|
||||||
|
import embed from "./embed.js";
|
||||||
|
import getVisitorKeys from "./get-visitor-keys.js";
|
||||||
|
import { locEnd, locStart } from "./loc.js";
|
||||||
|
import { insertPragma } from "./pragma.js";
|
||||||
|
import { printTable } from "./print/table.js";
|
||||||
|
import { printParagraph } from "./print-paragraph.js";
|
||||||
|
import preprocess from "./print-preprocess.js";
|
||||||
|
import { printSentence } from "./print-sentence.js";
|
||||||
|
import { printWhitespace } from "./print-whitespace.js";
|
||||||
|
import {
|
||||||
|
getFencedCodeBlockValue,
|
||||||
|
hasGitDiffFriendlyOrderedList,
|
||||||
|
INLINE_NODE_TYPES,
|
||||||
|
INLINE_NODE_WRAPPER_TYPES,
|
||||||
|
isAutolink,
|
||||||
|
splitText,
|
||||||
|
} from "./utils.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {Doc} from "../document/builders.js"
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SIBLING_NODE_TYPES = new Set(["listItem", "definition"]);
|
||||||
|
|
||||||
|
function prevOrNextWord(path) {
|
||||||
|
const { previous, next } = path;
|
||||||
|
const hasPrevOrNextWord =
|
||||||
|
(previous?.type === "sentence" &&
|
||||||
|
previous.children.at(-1)?.type === "word" &&
|
||||||
|
!previous.children.at(-1).hasTrailingPunctuation) ||
|
||||||
|
(next?.type === "sentence" &&
|
||||||
|
next.children[0]?.type === "word" &&
|
||||||
|
!next.children[0].hasLeadingPunctuation);
|
||||||
|
return hasPrevOrNextWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genericPrint(path, options, print) {
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
if (shouldRemainTheSameContent(path)) {
|
||||||
|
/*
|
||||||
|
* We assume parts always meet following conditions:
|
||||||
|
* - parts.length is odd
|
||||||
|
* - odd (0-indexed) elements are line-like doc
|
||||||
|
*/
|
||||||
|
/** @type {Doc[]} */
|
||||||
|
const parts = [""];
|
||||||
|
const textsNodes = splitText(
|
||||||
|
options.originalText.slice(
|
||||||
|
node.position.start.offset,
|
||||||
|
node.position.end.offset,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
for (const node of textsNodes) {
|
||||||
|
if (node.type === "word") {
|
||||||
|
parts.push([parts.pop(), node.value]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const doc = printWhitespace(path, node.value, options.proseWrap, true);
|
||||||
|
if (getDocType(doc) === DOC_TYPE_STRING) {
|
||||||
|
parts.push([parts.pop(), doc]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// In this path, doc is line. To meet the condition, we need additional element "".
|
||||||
|
parts.push(doc, "");
|
||||||
|
}
|
||||||
|
return fill(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (node.type) {
|
||||||
|
case "root":
|
||||||
|
/* c8 ignore next 3 */
|
||||||
|
if (node.children.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return [printRoot(path, options, print), hardline];
|
||||||
|
case "paragraph":
|
||||||
|
return printParagraph(path, options, print);
|
||||||
|
case "sentence":
|
||||||
|
return printSentence(path, print);
|
||||||
|
case "word": {
|
||||||
|
let escapedValue = node.value
|
||||||
|
.replaceAll("*", String.raw`\*`) // escape all `*`
|
||||||
|
.replaceAll(
|
||||||
|
new RegExp(
|
||||||
|
[
|
||||||
|
`(^|${PUNCTUATION_REGEXP.source})(_+)`,
|
||||||
|
`(_+)(${PUNCTUATION_REGEXP.source}|$)`,
|
||||||
|
].join("|"),
|
||||||
|
"gu",
|
||||||
|
),
|
||||||
|
(_, text1, underscore1, underscore2, text2) =>
|
||||||
|
(underscore1
|
||||||
|
? `${text1}${underscore1}`
|
||||||
|
: `${underscore2}${text2}`
|
||||||
|
).replaceAll("_", String.raw`\_`),
|
||||||
|
); // escape all `_` except concating with non-punctuation, e.g. `1_2_3` is not considered emphasis
|
||||||
|
|
||||||
|
const isFirstSentence = (node, name, index) =>
|
||||||
|
node.type === "sentence" && index === 0;
|
||||||
|
const isLastChildAutolink = (node, name, index) =>
|
||||||
|
isAutolink(node.children[index - 1]);
|
||||||
|
|
||||||
|
if (
|
||||||
|
escapedValue !== node.value &&
|
||||||
|
(path.match(undefined, isFirstSentence, isLastChildAutolink) ||
|
||||||
|
path.match(
|
||||||
|
undefined,
|
||||||
|
isFirstSentence,
|
||||||
|
(node, name, index) => node.type === "emphasis" && index === 0,
|
||||||
|
isLastChildAutolink,
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
// backslash is parsed as part of autolinks, so we need to remove it
|
||||||
|
escapedValue = escapedValue.replace(/^(\\?[*_])+/u, (prefix) =>
|
||||||
|
prefix.replaceAll("\\", ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return escapedValue;
|
||||||
|
}
|
||||||
|
case "whitespace": {
|
||||||
|
const { next } = path;
|
||||||
|
|
||||||
|
const proseWrap =
|
||||||
|
// leading char that may cause different syntax
|
||||||
|
next && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/u.test(next.value)
|
||||||
|
? "never"
|
||||||
|
: options.proseWrap;
|
||||||
|
|
||||||
|
return printWhitespace(path, node.value, proseWrap);
|
||||||
|
}
|
||||||
|
case "emphasis": {
|
||||||
|
let style;
|
||||||
|
if (isAutolink(node.children[0])) {
|
||||||
|
style = options.originalText[node.position.start.offset];
|
||||||
|
} else {
|
||||||
|
const hasPrevOrNextWord = prevOrNextWord(path); // `1*2*3` is considered emphasis but `1_2_3` is not
|
||||||
|
const inStrongAndHasPrevOrNextWord = // `1***2***3` is considered strong emphasis but `1**_2_**3` is not
|
||||||
|
path.callParent(
|
||||||
|
({ node }) => node.type === "strong" && prevOrNextWord(path),
|
||||||
|
);
|
||||||
|
style =
|
||||||
|
hasPrevOrNextWord ||
|
||||||
|
inStrongAndHasPrevOrNextWord ||
|
||||||
|
path.hasAncestor((node) => node.type === "emphasis")
|
||||||
|
? "*"
|
||||||
|
: "_";
|
||||||
|
}
|
||||||
|
return [style, printChildren(path, options, print), style];
|
||||||
|
}
|
||||||
|
case "strong":
|
||||||
|
return ["**", printChildren(path, options, print), "**"];
|
||||||
|
case "delete":
|
||||||
|
return ["~~", printChildren(path, options, print), "~~"];
|
||||||
|
case "inlineCode": {
|
||||||
|
const code =
|
||||||
|
options.proseWrap === "preserve"
|
||||||
|
? node.value
|
||||||
|
: node.value.replaceAll("\n", " ");
|
||||||
|
const backtickCount = getMinNotPresentContinuousCount(code, "`");
|
||||||
|
const backtickString = "`".repeat(backtickCount);
|
||||||
|
const padding =
|
||||||
|
code.startsWith("`") ||
|
||||||
|
code.endsWith("`") ||
|
||||||
|
(/^[\n ]/u.test(code) && /[\n ]$/u.test(code) && /[^\n ]/u.test(code))
|
||||||
|
? " "
|
||||||
|
: "";
|
||||||
|
return [backtickString, padding, code, padding, backtickString];
|
||||||
|
}
|
||||||
|
case "wikiLink": {
|
||||||
|
let contents = "";
|
||||||
|
if (options.proseWrap === "preserve") {
|
||||||
|
contents = node.value;
|
||||||
|
} else {
|
||||||
|
contents = node.value.replaceAll(/[\t\n]+/gu, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["[[", contents, "]]"];
|
||||||
|
}
|
||||||
|
case "link":
|
||||||
|
switch (options.originalText[node.position.start.offset]) {
|
||||||
|
case "<": {
|
||||||
|
const mailto = "mailto:";
|
||||||
|
const url =
|
||||||
|
// <hello@example.com> is parsed as { url: "mailto:hello@example.com" }
|
||||||
|
node.url.startsWith(mailto) &&
|
||||||
|
options.originalText.slice(
|
||||||
|
node.position.start.offset + 1,
|
||||||
|
node.position.start.offset + 1 + mailto.length,
|
||||||
|
) !== mailto
|
||||||
|
? node.url.slice(mailto.length)
|
||||||
|
: node.url;
|
||||||
|
return ["<", url, ">"];
|
||||||
|
}
|
||||||
|
case "[":
|
||||||
|
return [
|
||||||
|
"[",
|
||||||
|
printChildren(path, options, print),
|
||||||
|
"](",
|
||||||
|
printUrl(node.url, ")"),
|
||||||
|
printTitle(node.title, options),
|
||||||
|
")",
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return options.originalText.slice(
|
||||||
|
node.position.start.offset,
|
||||||
|
node.position.end.offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "image":
|
||||||
|
return [
|
||||||
|
""),
|
||||||
|
printTitle(node.title, options),
|
||||||
|
")",
|
||||||
|
];
|
||||||
|
case "blockquote":
|
||||||
|
return ["> ", align("> ", printChildren(path, options, print))];
|
||||||
|
case "heading":
|
||||||
|
return [
|
||||||
|
"#".repeat(node.depth) + " ",
|
||||||
|
printChildren(path, options, print),
|
||||||
|
];
|
||||||
|
case "code": {
|
||||||
|
if (node.isIndented) {
|
||||||
|
// indented code block
|
||||||
|
const alignment = " ".repeat(4);
|
||||||
|
return align(alignment, [
|
||||||
|
alignment,
|
||||||
|
replaceEndOfLine(node.value, hardline),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fenced code block
|
||||||
|
const styleUnit = options.__inJsTemplate ? "~" : "`";
|
||||||
|
const style = styleUnit.repeat(
|
||||||
|
Math.max(3, getMaxContinuousCount(node.value, styleUnit) + 1),
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
style,
|
||||||
|
node.lang || "",
|
||||||
|
node.meta ? " " + node.meta : "",
|
||||||
|
hardline,
|
||||||
|
replaceEndOfLine(
|
||||||
|
getFencedCodeBlockValue(node, options.originalText),
|
||||||
|
hardline,
|
||||||
|
),
|
||||||
|
hardline,
|
||||||
|
style,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
case "html": {
|
||||||
|
const { parent, isLast } = path;
|
||||||
|
const value =
|
||||||
|
parent.type === "root" && isLast ? node.value.trimEnd() : node.value;
|
||||||
|
const isHtmlComment = /^<!--.*-->$/su.test(value);
|
||||||
|
|
||||||
|
return replaceEndOfLine(
|
||||||
|
value,
|
||||||
|
// @ts-expect-error
|
||||||
|
isHtmlComment ? hardline : markAsRoot(literalline),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "list": {
|
||||||
|
const nthSiblingIndex = getNthListSiblingIndex(node, path.parent);
|
||||||
|
|
||||||
|
const isGitDiffFriendlyOrderedList = hasGitDiffFriendlyOrderedList(
|
||||||
|
node,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return printChildren(path, options, print, {
|
||||||
|
processor() {
|
||||||
|
const prefix = getPrefix();
|
||||||
|
const { node: childNode } = path;
|
||||||
|
|
||||||
|
if (
|
||||||
|
childNode.children.length === 2 &&
|
||||||
|
childNode.children[1].type === "html" &&
|
||||||
|
childNode.children[0].position.start.column !==
|
||||||
|
childNode.children[1].position.start.column
|
||||||
|
) {
|
||||||
|
return [prefix, printListItem(path, options, print, prefix)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
prefix,
|
||||||
|
align(
|
||||||
|
" ".repeat(prefix.length),
|
||||||
|
printListItem(path, options, print, prefix),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
function getPrefix() {
|
||||||
|
const rawPrefix = node.ordered
|
||||||
|
? (path.isFirst
|
||||||
|
? node.start
|
||||||
|
: isGitDiffFriendlyOrderedList
|
||||||
|
? 1
|
||||||
|
: node.start + path.index) +
|
||||||
|
(nthSiblingIndex % 2 === 0 ? ". " : ") ")
|
||||||
|
: nthSiblingIndex % 2 === 0
|
||||||
|
? "- "
|
||||||
|
: "* ";
|
||||||
|
|
||||||
|
return (node.isAligned ||
|
||||||
|
/* workaround for https://github.com/remarkjs/remark/issues/315 */ node.hasIndentedCodeblock) &&
|
||||||
|
node.ordered
|
||||||
|
? alignListPrefix(rawPrefix, options)
|
||||||
|
: rawPrefix;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case "thematicBreak": {
|
||||||
|
const { ancestors } = path;
|
||||||
|
const counter = ancestors.findIndex((node) => node.type === "list");
|
||||||
|
if (counter === -1) {
|
||||||
|
return "---";
|
||||||
|
}
|
||||||
|
const nthSiblingIndex = getNthListSiblingIndex(
|
||||||
|
ancestors[counter],
|
||||||
|
ancestors[counter + 1],
|
||||||
|
);
|
||||||
|
return nthSiblingIndex % 2 === 0 ? "***" : "---";
|
||||||
|
}
|
||||||
|
case "linkReference":
|
||||||
|
return [
|
||||||
|
"[",
|
||||||
|
printChildren(path, options, print),
|
||||||
|
"]",
|
||||||
|
node.referenceType === "full"
|
||||||
|
? printLinkReference(node)
|
||||||
|
: node.referenceType === "collapsed"
|
||||||
|
? "[]"
|
||||||
|
: "",
|
||||||
|
];
|
||||||
|
case "imageReference":
|
||||||
|
switch (node.referenceType) {
|
||||||
|
case "full":
|
||||||
|
return ["![", node.alt || "", "]", printLinkReference(node)];
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
"![",
|
||||||
|
node.alt,
|
||||||
|
"]",
|
||||||
|
node.referenceType === "collapsed" ? "[]" : "",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
case "definition": {
|
||||||
|
const lineOrSpace = options.proseWrap === "always" ? line : " ";
|
||||||
|
return group([
|
||||||
|
printLinkReference(node),
|
||||||
|
":",
|
||||||
|
indent([
|
||||||
|
lineOrSpace,
|
||||||
|
printUrl(node.url),
|
||||||
|
node.title === null
|
||||||
|
? ""
|
||||||
|
: [lineOrSpace, printTitle(node.title, options, false)],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// `footnote` requires `.use(footnotes, {inlineNotes: true})`, we are not using this option
|
||||||
|
// https://github.com/remarkjs/remark-footnotes#optionsinlinenotes
|
||||||
|
/* c8 ignore next 2 */
|
||||||
|
case "footnote":
|
||||||
|
return ["[^", printChildren(path, options, print), "]"];
|
||||||
|
case "footnoteReference":
|
||||||
|
return printFootnoteReference(node);
|
||||||
|
case "footnoteDefinition": {
|
||||||
|
const shouldInlineFootnote =
|
||||||
|
node.children.length === 1 &&
|
||||||
|
node.children[0].type === "paragraph" &&
|
||||||
|
(options.proseWrap === "never" ||
|
||||||
|
(options.proseWrap === "preserve" &&
|
||||||
|
node.children[0].position.start.line ===
|
||||||
|
node.children[0].position.end.line));
|
||||||
|
return [
|
||||||
|
printFootnoteReference(node),
|
||||||
|
": ",
|
||||||
|
shouldInlineFootnote
|
||||||
|
? printChildren(path, options, print)
|
||||||
|
: group([
|
||||||
|
align(
|
||||||
|
" ".repeat(4),
|
||||||
|
printChildren(path, options, print, {
|
||||||
|
processor: ({ isFirst }) =>
|
||||||
|
isFirst ? group([softline, print()]) : print(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
case "table":
|
||||||
|
return printTable(path, options, print);
|
||||||
|
case "tableCell":
|
||||||
|
return printChildren(path, options, print);
|
||||||
|
case "break":
|
||||||
|
return /\s/u.test(options.originalText[node.position.start.offset])
|
||||||
|
? [" ", markAsRoot(literalline)]
|
||||||
|
: ["\\", hardline];
|
||||||
|
case "liquidNode":
|
||||||
|
return replaceEndOfLine(node.value, hardline);
|
||||||
|
// MDX
|
||||||
|
// fallback to the original text if multiparser failed
|
||||||
|
// or `embeddedLanguageFormatting: "off"`
|
||||||
|
case "import":
|
||||||
|
case "export":
|
||||||
|
case "jsx":
|
||||||
|
return node.value.trimEnd();
|
||||||
|
case "esComment":
|
||||||
|
return ["{/* ", node.value, " */}"];
|
||||||
|
case "math":
|
||||||
|
return [
|
||||||
|
"$$",
|
||||||
|
hardline,
|
||||||
|
node.value ? [replaceEndOfLine(node.value, hardline), hardline] : "",
|
||||||
|
"$$",
|
||||||
|
];
|
||||||
|
case "inlineMath":
|
||||||
|
// remark-math trims content but we don't want to remove whitespaces
|
||||||
|
// since it's very possible that it's recognized as math accidentally
|
||||||
|
return options.originalText.slice(locStart(node), locEnd(node));
|
||||||
|
|
||||||
|
case "frontMatter": // Handled in core
|
||||||
|
case "tableRow": // handled in "table"
|
||||||
|
case "listItem": // handled in "list"
|
||||||
|
case "text": // handled in other types
|
||||||
|
default:
|
||||||
|
/* c8 ignore next */
|
||||||
|
throw new UnexpectedNodeError(node, "Markdown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printListItem(path, options, print, listPrefix) {
|
||||||
|
const { node } = path;
|
||||||
|
const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] ";
|
||||||
|
return [
|
||||||
|
prefix,
|
||||||
|
printChildren(path, options, print, {
|
||||||
|
processor({ node, isFirst }) {
|
||||||
|
if (isFirst && node.type !== "list") {
|
||||||
|
return align(" ".repeat(prefix.length), print());
|
||||||
|
}
|
||||||
|
|
||||||
|
const alignment = " ".repeat(
|
||||||
|
clamp(options.tabWidth - listPrefix.length, 0, 3), // 4+ will cause indented code block
|
||||||
|
);
|
||||||
|
return [alignment, align(alignment, print())];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function alignListPrefix(prefix, options) {
|
||||||
|
const additionalSpaces = getAdditionalSpaces();
|
||||||
|
return (
|
||||||
|
prefix +
|
||||||
|
" ".repeat(
|
||||||
|
additionalSpaces >= 4 ? 0 : additionalSpaces, // 4+ will cause indented code block
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function getAdditionalSpaces() {
|
||||||
|
const restSpaces = prefix.length % options.tabWidth;
|
||||||
|
return restSpaces === 0 ? 0 : options.tabWidth - restSpaces;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNthListSiblingIndex(node, parentNode) {
|
||||||
|
return getNthSiblingIndex(
|
||||||
|
node,
|
||||||
|
parentNode,
|
||||||
|
(siblingNode) => siblingNode.ordered === node.ordered,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNthSiblingIndex(node, parentNode, condition) {
|
||||||
|
let index = -1;
|
||||||
|
|
||||||
|
for (const childNode of parentNode.children) {
|
||||||
|
if (childNode.type === node.type && condition(childNode)) {
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode === node) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printRoot(path, options, print) {
|
||||||
|
/** @typedef {{ index: number, offset: number }} IgnorePosition */
|
||||||
|
/** @type {Array<{start: IgnorePosition, end: IgnorePosition}>} */
|
||||||
|
const ignoreRanges = [];
|
||||||
|
|
||||||
|
/** @type {IgnorePosition | null} */
|
||||||
|
let ignoreStart = null;
|
||||||
|
|
||||||
|
const { children } = path.node;
|
||||||
|
for (const [index, childNode] of children.entries()) {
|
||||||
|
switch (isPrettierIgnore(childNode)) {
|
||||||
|
case "start":
|
||||||
|
if (ignoreStart === null) {
|
||||||
|
ignoreStart = { index, offset: childNode.position.end.offset };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "end":
|
||||||
|
if (ignoreStart !== null) {
|
||||||
|
ignoreRanges.push({
|
||||||
|
start: ignoreStart,
|
||||||
|
end: { index, offset: childNode.position.start.offset },
|
||||||
|
});
|
||||||
|
ignoreStart = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return printChildren(path, options, print, {
|
||||||
|
processor({ index }) {
|
||||||
|
if (ignoreRanges.length > 0) {
|
||||||
|
const ignoreRange = ignoreRanges[0];
|
||||||
|
|
||||||
|
if (index === ignoreRange.start.index) {
|
||||||
|
return [
|
||||||
|
printIgnoreComment(children[ignoreRange.start.index]),
|
||||||
|
options.originalText.slice(
|
||||||
|
ignoreRange.start.offset,
|
||||||
|
ignoreRange.end.offset,
|
||||||
|
),
|
||||||
|
printIgnoreComment(children[ignoreRange.end.index]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreRange.start.index < index && index < ignoreRange.end.index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index === ignoreRange.end.index) {
|
||||||
|
ignoreRanges.shift();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return print();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function printChildren(path, options, print, events = {}) {
|
||||||
|
const { processor = print } = events;
|
||||||
|
|
||||||
|
const parts = [];
|
||||||
|
|
||||||
|
path.each(() => {
|
||||||
|
const result = processor(path);
|
||||||
|
if (result !== false) {
|
||||||
|
if (parts.length > 0 && shouldPrePrintHardline(path)) {
|
||||||
|
parts.push(hardline);
|
||||||
|
|
||||||
|
if (
|
||||||
|
shouldPrePrintDoubleHardline(path, options) ||
|
||||||
|
shouldPrePrintTripleHardline(path)
|
||||||
|
) {
|
||||||
|
parts.push(hardline);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldPrePrintTripleHardline(path)) {
|
||||||
|
parts.push(hardline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.push(result);
|
||||||
|
}
|
||||||
|
}, "children");
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printIgnoreComment(node) {
|
||||||
|
if (node.type === "html") {
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.type === "paragraph" &&
|
||||||
|
Array.isArray(node.children) &&
|
||||||
|
node.children.length === 1 &&
|
||||||
|
node.children[0].type === "esComment"
|
||||||
|
) {
|
||||||
|
return ["{/* ", node.children[0].value, " */}"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {false | 'next' | 'start' | 'end'} */
|
||||||
|
function isPrettierIgnore(node) {
|
||||||
|
let match;
|
||||||
|
|
||||||
|
if (node.type === "html") {
|
||||||
|
match = node.value.match(
|
||||||
|
/^<!--\s*prettier-ignore(?:-(start|end))?\s*-->$/u,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let comment;
|
||||||
|
|
||||||
|
if (node.type === "esComment") {
|
||||||
|
comment = node;
|
||||||
|
} else if (
|
||||||
|
node.type === "paragraph" &&
|
||||||
|
node.children.length === 1 &&
|
||||||
|
node.children[0].type === "esComment"
|
||||||
|
) {
|
||||||
|
comment = node.children[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
match = comment.value.match(/^prettier-ignore(?:-(start|end))?$/u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match ? match[1] || "next" : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPrePrintHardline({ node, parent }) {
|
||||||
|
const isInlineNode = INLINE_NODE_TYPES.has(node.type);
|
||||||
|
|
||||||
|
const isInlineHTML =
|
||||||
|
node.type === "html" && INLINE_NODE_WRAPPER_TYPES.has(parent.type);
|
||||||
|
|
||||||
|
return !isInlineNode && !isInlineHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLooseListItem(node, options) {
|
||||||
|
return (
|
||||||
|
node.type === "listItem" &&
|
||||||
|
(node.spread ||
|
||||||
|
// Check if `listItem` ends with `\n`
|
||||||
|
// since it can't be empty, so we only need check the last character
|
||||||
|
options.originalText.charAt(node.position.end.offset - 1) === "\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPrePrintDoubleHardline({ node, previous, parent }, options) {
|
||||||
|
if (
|
||||||
|
isLooseListItem(previous, options) ||
|
||||||
|
(node.type === "list" &&
|
||||||
|
parent.type === "listItem" &&
|
||||||
|
previous.type === "code")
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSequence = previous.type === node.type;
|
||||||
|
const isSiblingNode = isSequence && SIBLING_NODE_TYPES.has(node.type);
|
||||||
|
const isInTightListItem =
|
||||||
|
parent.type === "listItem" &&
|
||||||
|
(node.type === "list" || !isLooseListItem(parent, options));
|
||||||
|
const isPrevNodePrettierIgnore = isPrettierIgnore(previous) === "next";
|
||||||
|
const isBlockHtmlWithoutBlankLineBetweenPrevHtml =
|
||||||
|
node.type === "html" &&
|
||||||
|
previous.type === "html" &&
|
||||||
|
previous.position.end.line + 1 === node.position.start.line;
|
||||||
|
const isHtmlDirectAfterListItem =
|
||||||
|
node.type === "html" &&
|
||||||
|
parent.type === "listItem" &&
|
||||||
|
previous.type === "paragraph" &&
|
||||||
|
previous.position.end.line + 1 === node.position.start.line;
|
||||||
|
|
||||||
|
return !(
|
||||||
|
isSiblingNode ||
|
||||||
|
isInTightListItem ||
|
||||||
|
isPrevNodePrettierIgnore ||
|
||||||
|
isBlockHtmlWithoutBlankLineBetweenPrevHtml ||
|
||||||
|
isHtmlDirectAfterListItem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPrePrintTripleHardline({ node, previous }) {
|
||||||
|
const isPrevNodeList = previous.type === "list";
|
||||||
|
const isIndentedCode = node.type === "code" && node.isIndented;
|
||||||
|
|
||||||
|
return isPrevNodeList && isIndentedCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRemainTheSameContent(path) {
|
||||||
|
const node = path.findAncestor(
|
||||||
|
(node) => node.type === "linkReference" || node.type === "imageReference",
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
node && (node.type !== "linkReference" || node.referenceType !== "full")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodeUrl = (url, characters) => {
|
||||||
|
for (const character of characters) {
|
||||||
|
url = url.replaceAll(character, encodeURIComponent(character));
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string[] | string} [dangerousCharOrChars]
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function printUrl(url, dangerousCharOrChars = []) {
|
||||||
|
const dangerousChars = [
|
||||||
|
" ",
|
||||||
|
...(Array.isArray(dangerousCharOrChars)
|
||||||
|
? dangerousCharOrChars
|
||||||
|
: [dangerousCharOrChars]),
|
||||||
|
];
|
||||||
|
|
||||||
|
return new RegExp(
|
||||||
|
dangerousChars.map((x) => escapeStringRegexp(x)).join("|"),
|
||||||
|
"u",
|
||||||
|
).test(url)
|
||||||
|
? `<${encodeUrl(url, "<>")}>`
|
||||||
|
: url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printTitle(title, options, printSpace = true) {
|
||||||
|
if (!title) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (printSpace) {
|
||||||
|
return " " + printTitle(title, options, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// title is escaped after `remark-parse` v7
|
||||||
|
title = title.replaceAll(/\\(?=["')])/gu, "");
|
||||||
|
|
||||||
|
if (title.includes('"') && title.includes("'") && !title.includes(")")) {
|
||||||
|
return `(${title})`; // avoid escaped quotes
|
||||||
|
}
|
||||||
|
const quote = getPreferredQuote(title, options.singleQuote);
|
||||||
|
title = title.replaceAll("\\", "\\\\");
|
||||||
|
title = title.replaceAll(quote, `\\${quote}`);
|
||||||
|
return `${quote}${title}${quote}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(value, min, max) {
|
||||||
|
return Math.max(min, Math.min(value, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPrettierIgnore(path) {
|
||||||
|
return path.index > 0 && isPrettierIgnore(path.previous) === "next";
|
||||||
|
}
|
||||||
|
|
||||||
|
// `remark-parse` lowercase the `label` as `identifier`, we don't want do that
|
||||||
|
// https://github.com/remarkjs/remark/blob/daddcb463af2d5b2115496c395d0571c0ff87d15/packages/remark-parse/lib/tokenize/reference.js
|
||||||
|
function printLinkReference(node) {
|
||||||
|
return `[${collapseWhiteSpace(node.label)}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printFootnoteReference(node) {
|
||||||
|
return `[^${node.label}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const printer = {
|
||||||
|
features: {
|
||||||
|
experimental_frontMatterSupport: {
|
||||||
|
massageAstNode: true,
|
||||||
|
embed: true,
|
||||||
|
print: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preprocess,
|
||||||
|
print: genericPrint,
|
||||||
|
embed,
|
||||||
|
massageAstNode: clean,
|
||||||
|
hasPrettierIgnore,
|
||||||
|
insertPragma,
|
||||||
|
getVisitorKeys,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default printer;
|
||||||
23
src/prettier-markdown/unified-plugins/front-matter.js
Normal file
23
src/prettier-markdown/unified-plugins/front-matter.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { parseFrontMatter } from "../../main/front-matter/index.js";
|
||||||
|
|
||||||
|
/** @import {Plugin, Settings} from "unified" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Plugin<[], Settings>}
|
||||||
|
*/
|
||||||
|
const frontMatter = function () {
|
||||||
|
const proto = this.Parser.prototype;
|
||||||
|
proto.blockMethods = ["frontMatter", ...proto.blockMethods];
|
||||||
|
proto.blockTokenizers.frontMatter = tokenizer;
|
||||||
|
|
||||||
|
function tokenizer(eat, value) {
|
||||||
|
const { frontMatter } = parseFrontMatter(value);
|
||||||
|
|
||||||
|
if (frontMatter) {
|
||||||
|
return eat(frontMatter.raw)({ ...frontMatter, type: "frontMatter" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokenizer.onlyAtStart = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default frontMatter;
|
||||||
19
src/prettier-markdown/unified-plugins/html-to-jsx.js
Normal file
19
src/prettier-markdown/unified-plugins/html-to-jsx.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { COMMENT_REGEX } from "../mdx.js";
|
||||||
|
import { INLINE_NODE_WRAPPER_TYPES, mapAst } from "../utils.js";
|
||||||
|
|
||||||
|
function htmlToJsx() {
|
||||||
|
return (ast) =>
|
||||||
|
mapAst(ast, (node, _index, [parent]) => {
|
||||||
|
if (
|
||||||
|
node.type !== "html" ||
|
||||||
|
// Keep HTML-style comments (legacy MDX)
|
||||||
|
COMMENT_REGEX.test(node.value) ||
|
||||||
|
INLINE_NODE_WRAPPER_TYPES.has(parent.type)
|
||||||
|
) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return { ...node, type: "jsx" };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default htmlToJsx;
|
||||||
27
src/prettier-markdown/unified-plugins/liquid.js
Normal file
27
src/prettier-markdown/unified-plugins/liquid.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/** @import {Plugin, Settings} from "unified" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Plugin<[], Settings>}
|
||||||
|
*/
|
||||||
|
const liquid = function () {
|
||||||
|
const proto = this.Parser.prototype;
|
||||||
|
const methods = proto.inlineMethods;
|
||||||
|
methods.splice(methods.indexOf("text"), 0, "liquid");
|
||||||
|
proto.inlineTokenizers.liquid = tokenizer;
|
||||||
|
|
||||||
|
function tokenizer(eat, value) {
|
||||||
|
const match = value.match(/^(\{%.*?%\}|\{\{.*?\}\})/su);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return eat(match[0])({
|
||||||
|
type: "liquidNode",
|
||||||
|
value: match[0],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokenizer.locator = function (value, fromIndex) {
|
||||||
|
return value.indexOf("{", fromIndex);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default liquid;
|
||||||
32
src/prettier-markdown/unified-plugins/wiki-link.js
Normal file
32
src/prettier-markdown/unified-plugins/wiki-link.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/** @import {Plugin, Settings} from "unified" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Plugin<[], Settings>}
|
||||||
|
*/
|
||||||
|
const wikiLink = function () {
|
||||||
|
const entityType = "wikiLink";
|
||||||
|
const wikiLinkRegex = /^\[\[(?<linkContents>.+?)\]\]/su;
|
||||||
|
const proto = this.Parser.prototype;
|
||||||
|
const methods = proto.inlineMethods;
|
||||||
|
methods.splice(methods.indexOf("link"), 0, entityType);
|
||||||
|
proto.inlineTokenizers.wikiLink = tokenizer;
|
||||||
|
|
||||||
|
function tokenizer(eat, value) {
|
||||||
|
const match = wikiLinkRegex.exec(value);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const linkContents = match.groups.linkContents.trim();
|
||||||
|
|
||||||
|
return eat(match[0])({
|
||||||
|
type: entityType,
|
||||||
|
value: linkContents,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenizer.locator = function (value, fromIndex) {
|
||||||
|
return value.indexOf("[", fromIndex);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default wikiLink;
|
||||||
268
src/prettier-markdown/utils.js
Normal file
268
src/prettier-markdown/utils.js
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
import * as assert from "#universal/assert";
|
||||||
|
import { CJK_REGEXP, PUNCTUATION_REGEXP } from "./constants.evaluate.js";
|
||||||
|
import { locEnd, locStart } from "./loc.js";
|
||||||
|
|
||||||
|
const INLINE_NODE_TYPES = new Set([
|
||||||
|
"liquidNode",
|
||||||
|
"inlineCode",
|
||||||
|
"emphasis",
|
||||||
|
"esComment",
|
||||||
|
"strong",
|
||||||
|
"delete",
|
||||||
|
"wikiLink",
|
||||||
|
"link",
|
||||||
|
"linkReference",
|
||||||
|
"image",
|
||||||
|
"imageReference",
|
||||||
|
"footnote",
|
||||||
|
"footnoteReference",
|
||||||
|
"sentence",
|
||||||
|
"whitespace",
|
||||||
|
"word",
|
||||||
|
"break",
|
||||||
|
"inlineMath",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const INLINE_NODE_WRAPPER_TYPES = new Set([
|
||||||
|
...INLINE_NODE_TYPES,
|
||||||
|
"tableCell",
|
||||||
|
"paragraph",
|
||||||
|
"heading",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const KIND_NON_CJK = "non-cjk";
|
||||||
|
const KIND_CJ_LETTER = "cj-letter";
|
||||||
|
const KIND_K_LETTER = "k-letter";
|
||||||
|
const KIND_CJK_PUNCTUATION = "cjk-punctuation";
|
||||||
|
|
||||||
|
const K_REGEXP = /\p{Script_Extensions=Hangul}/u;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {" " | "\n" | ""} WhitespaceValue
|
||||||
|
* @typedef { KIND_NON_CJK | KIND_CJ_LETTER | KIND_K_LETTER | KIND_CJK_PUNCTUATION } WordKind
|
||||||
|
* @typedef {{
|
||||||
|
* type: "whitespace",
|
||||||
|
* value: WhitespaceValue,
|
||||||
|
* kind?: never
|
||||||
|
* }} WhitespaceNode
|
||||||
|
* @typedef {{
|
||||||
|
* type: "word",
|
||||||
|
* value: string,
|
||||||
|
* kind: WordKind,
|
||||||
|
* isCJ: boolean,
|
||||||
|
* hasLeadingPunctuation: boolean,
|
||||||
|
* hasTrailingPunctuation: boolean,
|
||||||
|
* }} WordNode
|
||||||
|
* Node for a single CJK character or a sequence of non-CJK characters
|
||||||
|
* @typedef {WhitespaceNode | WordNode} TextNode
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* split text into whitespaces and words
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
|
function splitText(text) {
|
||||||
|
/** @type {Array<TextNode>} */
|
||||||
|
const nodes = [];
|
||||||
|
|
||||||
|
const tokens = text.split(/([\t\n ]+)/u);
|
||||||
|
for (const [index, token] of tokens.entries()) {
|
||||||
|
// whitespace
|
||||||
|
if (index % 2 === 1) {
|
||||||
|
nodes.push({
|
||||||
|
type: "whitespace",
|
||||||
|
value: /\n/u.test(token) ? "\n" : " ",
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// word separated by whitespace
|
||||||
|
|
||||||
|
if ((index === 0 || index === tokens.length - 1) && token === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const innerTokens = token.split(new RegExp(`(${CJK_REGEXP.source})`, "u"));
|
||||||
|
for (const [innerIndex, innerToken] of innerTokens.entries()) {
|
||||||
|
if (
|
||||||
|
(innerIndex === 0 || innerIndex === innerTokens.length - 1) &&
|
||||||
|
innerToken === ""
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-CJK word
|
||||||
|
if (innerIndex % 2 === 0) {
|
||||||
|
if (innerToken !== "") {
|
||||||
|
appendNode({
|
||||||
|
type: "word",
|
||||||
|
value: innerToken,
|
||||||
|
kind: KIND_NON_CJK,
|
||||||
|
isCJ: false,
|
||||||
|
hasLeadingPunctuation: PUNCTUATION_REGEXP.test(innerToken[0]),
|
||||||
|
hasTrailingPunctuation: PUNCTUATION_REGEXP.test(innerToken.at(-1)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CJK character
|
||||||
|
|
||||||
|
// punctuation for CJ(K)
|
||||||
|
// Korean doesn't use them in horizontal writing usually
|
||||||
|
if (PUNCTUATION_REGEXP.test(innerToken)) {
|
||||||
|
appendNode({
|
||||||
|
type: "word",
|
||||||
|
value: innerToken,
|
||||||
|
kind: KIND_CJK_PUNCTUATION,
|
||||||
|
isCJ: true,
|
||||||
|
hasLeadingPunctuation: true,
|
||||||
|
hasTrailingPunctuation: true,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Korean uses space to divide words, but Chinese & Japanese do not
|
||||||
|
// This is why Korean should be treated like non-CJK
|
||||||
|
if (K_REGEXP.test(innerToken)) {
|
||||||
|
appendNode({
|
||||||
|
type: "word",
|
||||||
|
value: innerToken,
|
||||||
|
kind: KIND_K_LETTER,
|
||||||
|
isCJ: false,
|
||||||
|
hasLeadingPunctuation: false,
|
||||||
|
hasTrailingPunctuation: false,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendNode({
|
||||||
|
type: "word",
|
||||||
|
value: innerToken,
|
||||||
|
kind: KIND_CJ_LETTER,
|
||||||
|
isCJ: true,
|
||||||
|
hasLeadingPunctuation: false,
|
||||||
|
hasTrailingPunctuation: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for `canBeConvertedToSpace` in ./print-whitespace.js etc.
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
for (let i = 1; i < nodes.length; i++) {
|
||||||
|
assert.ok(
|
||||||
|
!(nodes[i - 1].type === "whitespace" && nodes[i].type === "whitespace"),
|
||||||
|
"splitText should not create consecutive whitespace nodes",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
|
||||||
|
function appendNode(node) {
|
||||||
|
const lastNode = nodes.at(-1);
|
||||||
|
if (
|
||||||
|
lastNode?.type === "word" &&
|
||||||
|
!isBetween(KIND_NON_CJK, KIND_CJK_PUNCTUATION) &&
|
||||||
|
// disallow leading/trailing full-width whitespace
|
||||||
|
![lastNode.value, node.value].some((value) => /\u3000/u.test(value))
|
||||||
|
) {
|
||||||
|
nodes.push({ type: "whitespace", value: "" });
|
||||||
|
}
|
||||||
|
nodes.push(node);
|
||||||
|
|
||||||
|
function isBetween(kind1, kind2) {
|
||||||
|
return (
|
||||||
|
(lastNode.kind === kind1 && node.kind === kind2) ||
|
||||||
|
(lastNode.kind === kind2 && node.kind === kind1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderedListItemInfo(orderListItem, options) {
|
||||||
|
const text = options.originalText.slice(
|
||||||
|
orderListItem.position.start.offset,
|
||||||
|
orderListItem.position.end.offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { numberText, leadingSpaces } = text.match(
|
||||||
|
/^\s*(?<numberText>\d+)(\.|\))(?<leadingSpaces>\s*)/u,
|
||||||
|
).groups;
|
||||||
|
|
||||||
|
return { number: Number(numberText), leadingSpaces };
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasGitDiffFriendlyOrderedList(node, options) {
|
||||||
|
if (!node.ordered || node.children.length < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondNumber = getOrderedListItemInfo(node.children[1], options).number;
|
||||||
|
|
||||||
|
if (secondNumber !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstNumber = getOrderedListItemInfo(node.children[0], options).number;
|
||||||
|
|
||||||
|
if (firstNumber !== 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
node.children.length > 2 &&
|
||||||
|
getOrderedListItemInfo(node.children[2], options).number === 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The final new line should not include in value
|
||||||
|
// https://github.com/remarkjs/remark/issues/512
|
||||||
|
function getFencedCodeBlockValue(node, originalText) {
|
||||||
|
const { value } = node;
|
||||||
|
if (
|
||||||
|
node.position.end.offset === originalText.length &&
|
||||||
|
value.endsWith("\n") &&
|
||||||
|
// Code block has no end mark
|
||||||
|
originalText.endsWith("\n")
|
||||||
|
) {
|
||||||
|
return value.slice(0, -1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapAst(ast, handler) {
|
||||||
|
return (function preorder(node, index, parentStack) {
|
||||||
|
const newNode = { ...handler(node, index, parentStack) };
|
||||||
|
if (newNode.children) {
|
||||||
|
newNode.children = newNode.children.map((child, index) =>
|
||||||
|
preorder(child, index, [newNode, ...parentStack]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
})(ast, null, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAutolink(node) {
|
||||||
|
if (node?.type !== "link" || node.children.length !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const [child] = node.children;
|
||||||
|
return locStart(node) === locStart(child) && locEnd(node) === locEnd(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getFencedCodeBlockValue,
|
||||||
|
getOrderedListItemInfo,
|
||||||
|
hasGitDiffFriendlyOrderedList,
|
||||||
|
INLINE_NODE_TYPES,
|
||||||
|
INLINE_NODE_WRAPPER_TYPES,
|
||||||
|
isAutolink,
|
||||||
|
KIND_CJ_LETTER,
|
||||||
|
KIND_CJK_PUNCTUATION,
|
||||||
|
KIND_K_LETTER,
|
||||||
|
KIND_NON_CJK,
|
||||||
|
mapAst,
|
||||||
|
splitText,
|
||||||
|
};
|
||||||
41
src/prettier-markdown/visitor-keys.js
Normal file
41
src/prettier-markdown/visitor-keys.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
const visitorKeys = {
|
||||||
|
root: ["children"],
|
||||||
|
paragraph: ["children"],
|
||||||
|
sentence: ["children"],
|
||||||
|
word: [],
|
||||||
|
whitespace: [],
|
||||||
|
emphasis: ["children"],
|
||||||
|
strong: ["children"],
|
||||||
|
delete: ["children"],
|
||||||
|
inlineCode: [],
|
||||||
|
wikiLink: [],
|
||||||
|
link: ["children"],
|
||||||
|
image: [],
|
||||||
|
blockquote: ["children"],
|
||||||
|
heading: ["children"],
|
||||||
|
code: [],
|
||||||
|
html: [],
|
||||||
|
list: ["children"],
|
||||||
|
thematicBreak: [],
|
||||||
|
linkReference: ["children"],
|
||||||
|
imageReference: [],
|
||||||
|
definition: [],
|
||||||
|
footnote: ["children"],
|
||||||
|
footnoteReference: [],
|
||||||
|
footnoteDefinition: ["children"],
|
||||||
|
table: ["children"],
|
||||||
|
tableCell: ["children"],
|
||||||
|
break: [],
|
||||||
|
liquidNode: [],
|
||||||
|
import: [],
|
||||||
|
export: [],
|
||||||
|
esComment: [],
|
||||||
|
jsx: [],
|
||||||
|
math: [],
|
||||||
|
inlineMath: [],
|
||||||
|
tableRow: ["children"],
|
||||||
|
listItem: ["children"],
|
||||||
|
text: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default visitorKeys;
|
||||||
176
test/markdown-formatting.test.ts
Normal file
176
test/markdown-formatting.test.ts
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
import { printers } from "../src/index.js";
|
||||||
|
|
||||||
|
describe("Markdown Formatting in Descriptions", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
|
||||||
|
describe("Basic markdown formatting", () => {
|
||||||
|
it("should format description fields with markdown", () => {
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: {
|
||||||
|
title: "Test API",
|
||||||
|
version: "1.0.0",
|
||||||
|
description: "This is a test description\n\n\nWith multiple spaces ",
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
summary: "Get endpoint ",
|
||||||
|
description: "Endpoint description\n\n\nwith extra spaces",
|
||||||
|
operationId: "getTest",
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Success response",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Check that multiple spaces are normalized in the original content
|
||||||
|
// Note: YAML may format this differently, but the content should be processed
|
||||||
|
// The description field should exist and be formatted
|
||||||
|
expect(resultString).toContain("description:");
|
||||||
|
|
||||||
|
// Check that multiple blank lines are normalized
|
||||||
|
expect(resultString).not.toMatch(/\n{4,}/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should preserve code blocks in descriptions", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: {
|
||||||
|
title: "Test API",
|
||||||
|
version: "1.0.0",
|
||||||
|
description:
|
||||||
|
"Here is some code:\n\n const x = 1;\n const y = 2;\n\nAnd more text.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Code blocks (4+ spaces) should be preserved
|
||||||
|
expect(resultString).toContain(" const x = 1;");
|
||||||
|
expect(resultString).toContain(" const y = 2;");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should format markdown in nested objects", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: {
|
||||||
|
title: "Test API",
|
||||||
|
version: "1.0.0",
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
operationId: "test",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "filter",
|
||||||
|
in: "query",
|
||||||
|
description: "Filter parameter with spaces",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Success response",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Both parameter and response descriptions should be formatted
|
||||||
|
expect(resultString).toContain("description:");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Summary field formatting", () => {
|
||||||
|
it("should format summary fields", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: {
|
||||||
|
title: "Test API",
|
||||||
|
version: "1.0.0",
|
||||||
|
summary: "API summary with spaces",
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
summary: "Get endpoint summary",
|
||||||
|
operationId: "test",
|
||||||
|
responses: { "200": { description: "OK" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Summary fields should be processed
|
||||||
|
expect(resultString).toContain("summary:");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user