diff --git a/packages/prettier-plugin/package.json b/packages/prettier-plugin/package.json index 1cb81f7..c585d3f 100644 --- a/packages/prettier-plugin/package.json +++ b/packages/prettier-plugin/package.json @@ -15,15 +15,18 @@ ], "scripts": { "build": "tsc --build", - "prepack": "tsc --build --clean && tsc --build" + "prepack": "tsc --build --clean && tsc --build", + "test:lib": "vitest" }, "dependencies": { "tempblot-parser": "workspace:*" }, "devDependencies": { "@types/node": "^24.5.2", + "cli-testing-library": "^3.0.0", "prettier": "^3.6.2", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "vitest": "^4.1.6" }, "peerDependencies": { "prettier": ">=3.0.0" diff --git a/packages/prettier-plugin/tests/__snapshots__/index.spec.ts.snap b/packages/prettier-plugin/tests/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000..b2b3433 --- /dev/null +++ b/packages/prettier-plugin/tests/__snapshots__/index.spec.ts.snap @@ -0,0 +1,75 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`formats .blot files through Prettier CLI 1`] = ` +" +const value = { hello: "world" }; + + + +{ "hello": "world" } +" +`; + +exports[`formats css output 1`] = ` +" +.card { + color: red; + background: white; +} + +" +`; + +exports[`formats html output 1`] = ` +" +
+

Hello

+

World

+
+
+" +`; + +exports[`formats javascript output 1`] = ` +" +const run = () => ({ hello: "world" }); + +" +`; + +exports[`formats setup and json output 1`] = ` +" + + +const value = { hello: "world", items: [1, 2, 3] }; + + + +{ "hello": "world", "items": [1, 2, 3] } + +" +`; + +exports[`formats typescript output 1`] = ` +" +const value: Record = { hello: "world" }; + +" +`; + +exports[`formats unknown output language 1`] = ` +" +leave this alone + +" +`; + +exports[`formats yaml output 1`] = ` +" +hello: world +items: + - 1 + - 2 + +" +`; diff --git a/packages/prettier-plugin/tests/fixtures/.gitignore b/packages/prettier-plugin/tests/fixtures/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/packages/prettier-plugin/tests/fixtures/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/packages/prettier-plugin/tests/index.spec.ts b/packages/prettier-plugin/tests/index.spec.ts new file mode 100644 index 0000000..58a3831 --- /dev/null +++ b/packages/prettier-plugin/tests/index.spec.ts @@ -0,0 +1,125 @@ +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { render } from "cli-testing-library"; +import * as prettier from "prettier"; +import { expect, test } from "vitest"; + +import plugin from "../src/index.js"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const packageRoot = path.resolve(__dirname, ".."); + +test.each([ + [ + "setup and json output", + ` + + +const value={hello:"world",items:[1,2,3]} + + + +{"hello":"world","items":[1,2,3]} + +`, + ], + [ + "yaml output", + ` + +hello: world +items: +- 1 +- 2 + +`, + ], + [ + "html output", + ` + +

Hello

World

+
+`, + ], + [ + "css output", + ` + +.card{color:red;background:white} + +`, + ], + [ + "javascript output", + ` + +const run=()=>({hello:"world"}) + +`, + ], + [ + "typescript output", + ` + +const value:Record={hello:"world"} + +`, + ], + [ + "unknown output language", + ` + + leave this alone + +`, + ], +])("formats %s", async (_name, source) => { + await expect(format(source.trim())).resolves.toMatchSnapshot(); +}); + +test("formats .blot files through Prettier CLI", async () => { + const fixturePath = path.join(packageRoot, "tests", "fixtures", "cli.blot"); + const fixtureArg = "tests/fixtures/cli.blot"; + + try { + await fs.writeFile( + fixturePath, + ` + +const value={hello:"world"} + + +{"hello":"world"} + +`.trim(), + ); + + const cli = await render(`pnpm exec prettier --plugin ./lib/index.js ${fixtureArg}`, [], { + cwd: packageRoot, + }); + + await waitForExit(cli); + + expect(cli.getStdallStr()).toMatchSnapshot(); + expect(cli.hasExit()).toEqual({ exitCode: 0 }); + } finally { + await fs.rm(fixturePath, { force: true }); + } +}); + +function format(source: string) { + return prettier.format(source, { + parser: "tempblot", + plugins: [plugin], + }); +} + +async function waitForExit(cli: Awaited>) { + while (!cli.hasExit()) { + await new Promise((resolve) => setTimeout(resolve, 10)); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 366af16..3393364 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,12 +193,18 @@ importers: '@types/node': specifier: ^24.5.2 version: 24.12.4 + cli-testing-library: + specifier: ^3.0.0 + version: 3.0.1(vitest@4.1.6(@types/node@24.12.4)(vite@8.0.12(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(yaml@2.9.0))) prettier: specifier: ^3.6.2 version: 3.8.3 typescript: specifier: ^6.0.3 version: 6.0.3 + vitest: + specifier: ^4.1.6 + version: 4.1.6(@types/node@24.12.4)(vite@8.0.12(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(yaml@2.9.0)) packages/typescript-plugin: dependencies: @@ -369,6 +375,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -1666,6 +1676,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -1830,6 +1844,21 @@ packages: resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} + cli-testing-library@3.0.1: + resolution: {integrity: sha512-fkQ8D2hQS53RP3s0yuCMHmTfPUMEqtVtJG0rs13MNE2khnkSaY8MsNxN7rSJZAzOOVsSg2I2F2XjEITJwf5dFg==} + engines: {node: '>=16'} + peerDependencies: + '@jest/expect': ^29.0.0 + '@jest/globals': ^29.0.0 + vitest: ^3.0.0 + peerDependenciesMeta: + '@jest/expect': + optional: true + '@jest/globals': + optional: true + vitest: + optional: true + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -2185,6 +2214,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2330,6 +2363,10 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2369,6 +2406,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} @@ -3023,6 +3064,10 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -3226,6 +3271,10 @@ packages: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} @@ -3267,6 +3316,18 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -3344,6 +3405,10 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4081,6 +4146,8 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/runtime@7.29.2': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -5035,6 +5102,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + any-promise@1.3.0: {} anymatch@3.1.3: @@ -5294,6 +5363,19 @@ snapshots: optionalDependencies: '@colors/colors': 1.5.0 + cli-testing-library@3.0.1(vitest@4.1.6(@types/node@24.12.4)(vite@8.0.12(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(yaml@2.9.0))): + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + picocolors: 1.1.1 + redent: 4.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + strip-final-newline: 4.0.0 + tree-kill: 1.2.2 + optionalDependencies: + vitest: 4.1.6(@types/node@24.12.4)(vite@8.0.12(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(yaml@2.9.0)) + cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -5652,6 +5734,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5935,6 +6019,8 @@ snapshots: ieee754@1.2.1: {} + indent-string@5.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -5963,6 +6049,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + is-hexadecimal@2.0.1: {} is-inside-container@1.0.0: @@ -6956,6 +7046,11 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + redent@4.0.0: + dependencies: + indent-string: 5.0.0 + strip-indent: 4.1.1 + regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -7294,6 +7389,11 @@ snapshots: dependencies: unicode-emoji-modifier-base: 1.0.0 + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + smol-toml@1.6.1: {} source-map-js@1.2.1: {} @@ -7329,6 +7429,14 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-final-newline@4.0.0: {} + + strip-indent@4.1.1: {} + strip-json-comments@2.0.1: {} strip-json-comments@5.0.3: {} @@ -7408,6 +7516,8 @@ snapshots: tmp@0.2.5: {} + tree-kill@1.2.2: {} + trim-lines@3.0.1: {} trough@2.2.0: {}