From 2ce3fceaa66ed287a37c3c40427294fd6fdd1cb5 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Sun, 26 Oct 2025 11:41:55 +0100 Subject: [PATCH 1/2] chore: convert package to typescript --- index.js => index.ts | 159 +++--- package-lock.json | 500 +++++++++++++++--- package.json | 9 +- test/{api.test.js => api.test.ts} | 14 +- test/{atrules.test.js => atrules.test.ts} | 72 ++- test/{comments.test.js => comments.test.ts} | 88 ++- ...larations.test.js => declarations.test.ts} | 21 +- test/{minify.test.js => minify.test.ts} | 29 +- test/{rules.test.js => rules.test.ts} | 49 +- test/{selectors.test.js => selectors.test.ts} | 46 +- test/{tab-size.test.js => tab-size.test.ts} | 24 +- test/{values.test.js => values.test.ts} | 47 +- tsconfig.json | 2 +- vite.config.js | 52 +- 14 files changed, 716 insertions(+), 396 deletions(-) rename index.js => index.ts (78%) rename test/{api.test.js => api.test.ts} (82%) rename test/{atrules.test.js => atrules.test.ts} (87%) rename test/{comments.test.js => comments.test.ts} (87%) rename test/{declarations.test.js => declarations.test.ts} (82%) rename test/{minify.test.js => minify.test.ts} (82%) rename test/{rules.test.js => rules.test.ts} (81%) rename test/{selectors.test.js => selectors.test.ts} (83%) rename test/{tab-size.test.js => tab-size.test.ts} (61%) rename test/{values.test.js => values.test.ts} (89%) diff --git a/index.js b/index.ts similarity index 78% rename from index.js rename to index.ts index a015d9b..eebde46 100644 --- a/index.js +++ b/index.ts @@ -1,24 +1,22 @@ -// @ts-expect-error Typing of css-tree is incomplete -import parse from 'css-tree/parser' - -/** - * @typedef {import('css-tree').CssNode} CssNode - * @typedef {import('css-tree').List} List - * @typedef {import('css-tree').CssLocation} CssLocation - * @typedef {import('css-tree').Raw} Raw - * @typedef {import('css-tree').StyleSheet} StyleSheet - * @typedef {import('css-tree').Atrule} Atrule - * @typedef {import('css-tree').AtrulePrelude} AtrulePrelude - * @typedef {import('css-tree').Rule} Rule - * @typedef {import('css-tree').SelectorList} SelectorList - * @typedef {import('css-tree').Selector} Selector - * @typedef {import('css-tree').PseudoClassSelector} PseudoClassSelector - * @typedef {import('css-tree').PseudoElementSelector} PseudoElementSelector - * @typedef {import('css-tree').Block} Block - * @typedef {import('css-tree').Declaration} Declaration - * @typedef {import('css-tree').Value} Value - * @typedef {import('css-tree').Operator} Operator - */ +import { + parse, + type CssNode, + type List, + type CssLocation, + type Raw, + type StyleSheet, + type Atrule, + type AtrulePrelude, + type Rule, + type SelectorList, + type Selector, + type PseudoClassSelector, + type PseudoElementSelector, + type Block, + type Declaration, + type Value, + type Operator, +} from 'css-tree' const SPACE = ' ' const EMPTY_STRING = '' @@ -42,8 +40,7 @@ const TYPE_PSEUDO_ELEMENT_SELECTOR = 'PseudoElementSelector' const TYPE_DECLARATION = 'Declaration' const TYPE_OPERATOR = 'Operator' -/** @param {string} str */ -function lowercase(str) { +function lowercase(str: string) { // Only create new strings in memory if we need to if (/[A-Z]/.test(str)) { return str.toLowerCase() @@ -51,44 +48,35 @@ function lowercase(str) { return str } +export type FormatOptions = { + /** Whether to minify the CSS or keep it formatted */ + minify?: boolean + /** Tell the formatter to use N spaces instead of tabs */ + tab_size?: number +} + /** - * @typedef {Object} Options - * @property {boolean} [minify] Whether to minify the CSS or keep it formatted - * @property {number} [tab_size] Tell the formatter to use N spaces instead of tabs - * * Format a string of CSS using some simple rules - * @param {string} css The original CSS - * @param {Options} [options] - * @returns {string} The formatted CSS */ -export function format(css, { - minify = false, - tab_size = undefined, -} = Object.create(null)) { - +export function format(css: string, { minify = false, tab_size = undefined }: FormatOptions = Object.create(null)): string { if (tab_size !== undefined && Number(tab_size) < 1) { throw new TypeError('tab_size must be a number greater than 0') } - /** @type {number[]} [start0, end0, start1, end1, etc.]*/ - let comments = [] + /** [start0, end0, start1, end1, etc.]*/ + let comments: number[] = [] - /** - * @param {string} _ The comment text - * @param {CssLocation} position - */ - function on_comment(_, position) { + function on_comment(_: string, position: CssLocation) { comments.push(position.start.offset, position.end.offset) } - /** @type {StyleSheet} */ let ast = parse(css, { positions: true, parseAtrulePrelude: false, parseCustomProperty: true, parseValue: true, onComment: on_comment, - }) + }) as StyleSheet const NEWLINE = minify ? EMPTY_STRING : '\n' const OPTIONAL_SPACE = minify ? EMPTY_STRING : SPACE @@ -96,12 +84,7 @@ export function format(css, { let indent_level = 0 - /** - * Indent a string - * @param {number} size - * @returns {string} A string with [size] tabs/spaces - */ - function indent(size) { + function indent(size: number) { if (minify === true) return EMPTY_STRING if (tab_size !== undefined) { @@ -111,8 +94,7 @@ export function format(css, { return '\t'.repeat(size) } - /** @param {CssNode} node */ - function substr(node) { + function substr(node: CssNode) { let loc = node.loc // If the node has no location, return an empty string // This is necessary for space toggles @@ -120,25 +102,21 @@ export function format(css, { return css.slice(loc.start.offset, loc.end.offset) } - /** @param {CssNode} node */ - function start_offset(node) { - let loc = /** @type {CssLocation} */(node.loc) - return loc.start.offset + function start_offset(node: CssNode) { + return node.loc?.start.offset } - /** @param {CssNode} node */ - function end_offset(node) { - let loc = /** @type {CssLocation} */(node.loc) - return loc.end.offset + function end_offset(node: CssNode) { + return node.loc?.end.offset } /** * Get a comment from the CSS string after the first offset and before the second offset - * @param {number | undefined} after After which offset to look for comments - * @param {number | undefined} before Before which offset to look for comments - * @returns {string | undefined} The comment string, if found + * @param after After which offset to look for comments + * @param before Before which offset to look for comments + * @returns The comment string, if found */ - function print_comment(after, before) { + function print_comment(after?: number, before?: number): string | undefined { if (minify === true || after === undefined || before === undefined) { return EMPTY_STRING } @@ -160,9 +138,8 @@ export function format(css, { return buffer } - /** @param {Rule} node */ - function print_rule(node) { - let buffer + function print_rule(node: Rule) { + let buffer = '' let prelude = node.prelude let block = node.block @@ -182,8 +159,7 @@ export function format(css, { return buffer } - /** @param {SelectorList} node */ - function print_selectorlist(node) { + function print_selectorlist(node: SelectorList) { let buffer = EMPTY_STRING node.children.forEach((selector, item) => { @@ -205,12 +181,11 @@ export function format(css, { return buffer } - /** @param {Selector | PseudoClassSelector | PseudoElementSelector} node */ - function print_simple_selector(node) { + function print_simple_selector(node: Selector | PseudoClassSelector | PseudoElementSelector) { let buffer = EMPTY_STRING - let children = node.children || [] + let children = node.children - children.forEach((child) => { + children?.forEach((child) => { switch (child.type) { case 'TypeSelector': { buffer += lowercase(child.name) @@ -327,8 +302,7 @@ export function format(css, { return buffer } - /** @param {Block} node */ - function print_block(node) { + function print_block(node: Block) { let children = node.children let buffer = OPTIONAL_SPACE @@ -348,7 +322,7 @@ export function format(css, { indent_level++ - let opening_comment = print_comment(start_offset(node), start_offset(/** @type {CssNode} */(children.first))) + let opening_comment = print_comment(start_offset(node), start_offset(children.first!)) if (opening_comment) { buffer += indent(indent_level) + opening_comment + NEWLINE } @@ -392,7 +366,7 @@ export function format(css, { } }) - let closing_comment = print_comment(end_offset(/** @type {CssNode} */(children.last)), end_offset(node)) + let closing_comment = print_comment(end_offset(children.last!), end_offset(node)) if (closing_comment) { buffer += NEWLINE + indent(indent_level) + closing_comment } @@ -403,8 +377,7 @@ export function format(css, { return buffer } - /** @param {Atrule} node */ - function print_atrule(node) { + function print_atrule(node: Atrule) { let buffer = indent(indent_level) + '@' let prelude = node.prelude let block = node.block @@ -431,9 +404,8 @@ export function format(css, { * here to force some nice formatting. * Should be OK perf-wise, since the amount of atrules in most * stylesheets are limited, so this won't be called too often. - * @param {AtrulePrelude | Raw} node */ - function print_prelude(node) { + function print_prelude(node: AtrulePrelude | Raw) { let buffer = substr(node) return buffer @@ -447,11 +419,10 @@ export function format(css, { let space = operator === '+' || operator === '-' ? SPACE : OPTIONAL_SPACE return `calc(${left.trim()}${space}${operator}${space}${right.trim()})` }) - .replace(/selector|url|supports|layer\(/ig, (match) => lowercase(match)) // lowercase function names + .replace(/selector|url|supports|layer\(/gi, (match) => lowercase(match)) // lowercase function names } - /** @param {Declaration} node */ - function print_declaration(node) { + function print_declaration(node: Declaration) { let property = node.property // Lowercase the property, unless it's a custom property (starts with --) @@ -481,8 +452,7 @@ export function format(css, { return indent(indent_level) + property + COLON + OPTIONAL_SPACE + value } - /** @param {List} children */ - function print_list(children) { + function print_list(children: List) { let buffer = EMPTY_STRING children.forEach((node, item) => { @@ -519,8 +489,7 @@ export function format(css, { return buffer } - /** @param {Operator} node */ - function print_operator(node) { + function print_operator(node: Operator) { let buffer = EMPTY_STRING // https://developer.mozilla.org/en-US/docs/Web/CSS/calc#notes // The + and - operators must be surrounded by whitespace @@ -555,8 +524,7 @@ export function format(css, { return buffer } - /** @param {Value | Raw} node */ - function print_value(node) { + function print_value(node: Value | Raw) { if (node.type === 'Raw') { return print_unknown(node, 0) } @@ -564,12 +532,7 @@ export function format(css, { return print_list(node.children) } - /** - * @param {CssNode} node - * @param {number} indent_level - * @returns {string} A formatted unknown CSS string - */ - function print_unknown(node, indent_level) { + function print_unknown(node: CssNode, indent_level: number) { return indent(indent_level) + substr(node).trim() } @@ -603,7 +566,7 @@ export function format(css, { } }) - let closing_comment = print_comment(end_offset(/** @type {CssNode} */(children.last)), end_offset(ast)) + let closing_comment = print_comment(end_offset(children.last!), end_offset(ast)) if (closing_comment) { buffer += NEWLINE + closing_comment } @@ -619,6 +582,6 @@ export function format(css, { * @param {string} css The original CSS * @returns {string} The minified CSS */ -export function minify(css) { +export function minify(css: string): string { return format(css, { minify: true }) } diff --git a/package-lock.json b/package-lock.json index 29c9a1a..c235e0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,14 +14,15 @@ "devDependencies": { "@codecov/vite-plugin": "^1.9.1", "@types/css-tree": "^2.3.11", + "@vitest/coverage-v8": "^4.0.3", "c8": "^10.1.3", "oxlint": "^1.24.0", "prettier": "^3.6.2", "publint": "^0.3.15", "typescript": "^5.9.3", - "uvu": "^0.5.6", "vite": "^6.2.6", - "vite-plugin-dts": "^4.5.4" + "vite-plugin-dts": "^4.5.4", + "vitest": "^4.0.3" }, "engines": { "node": ">=18.0.0" @@ -736,16 +737,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1610,6 +1611,13 @@ "string-argv": "~0.3.1" } }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", @@ -1617,6 +1625,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/css-tree": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-2.3.11.tgz", @@ -1624,6 +1643,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1638,6 +1664,159 @@ "dev": true, "license": "MIT" }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.3.tgz", + "integrity": "sha512-I+MlLwyJRBjmJr1kFYSxoseINbIdpxIAeK10jmXgB0FUtIfdYsvM3lGAvBu5yk8WPyhefzdmbCHCc1idFbNRcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.3", + "ast-v8-to-istanbul": "^0.3.5", + "debug": "^4.4.3", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.3", + "vitest": "4.0.3" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.3.tgz", + "integrity": "sha512-v3eSDx/bF25pzar6aEJrrdTXJduEBU3uSGXHslIdGIpJVP8tQQHV6x1ZfzbFQ/bLIomLSbR/2ZCfnaEGkWkiVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.3", + "@vitest/utils": "4.0.3", + "chai": "^6.0.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.3.tgz", + "integrity": "sha512-evZcRspIPbbiJEe748zI2BRu94ThCBE+RkjCpVF8yoVYuTV7hMe+4wLF/7K86r8GwJHSmAPnPbZhpXWWrg1qbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.3", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.19" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.3.tgz", + "integrity": "sha512-N7gly/DRXzxa9w9sbDXwD9QNFYP2hw90LLLGDobPNwiWgyW95GMxsCt29/COIKKh3P7XJICR38PSDePenMBtsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.3.tgz", + "integrity": "sha512-1/aK6fPM0lYXWyGKwop2Gbvz1plyTps/HDbIIJXYtJtspHjpXIeB3If07eWpVH4HW7Rmd3Rl+IS/+zEAXrRtXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.3", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.3.tgz", + "integrity": "sha512-amnYmvZ5MTjNCP1HZmdeczAPLRD6iOm9+2nMRUGxbe/6sQ0Ymur0NnR9LIrWS8JA3wKE71X25D6ya/3LN9YytA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.3", + "magic-string": "^0.30.19", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.3.tgz", + "integrity": "sha512-82vVL8Cqz7rbXaNUl35V2G7xeNMAjBdNOVaHbrzznT9BmiCiPOzhf0FhU3eP41nP1bLDm/5wWKZqkG4nyU95DQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.3.tgz", + "integrity": "sha512-qV6KJkq8W3piW6MDIbGOmn1xhvcW4DuA07alqaQ+vdx7YA49J85pnwnxigZVQFQw3tWnQNRKWwhz5wbP6iv/GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.3", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@volar/language-core": { "version": "2.4.12", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.12.tgz", @@ -1841,6 +2020,38 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz", + "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1988,6 +2199,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/chai": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.0.tgz", + "integrity": "sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2103,9 +2324,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2127,26 +2348,6 @@ "dev": true, "license": "ISC" }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2174,6 +2375,13 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -2232,6 +2440,16 @@ "dev": true, "license": "MIT" }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exsolve": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.4.tgz", @@ -2467,10 +2685,25 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2504,6 +2737,13 @@ "dev": true, "license": "MIT" }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -2524,16 +2764,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -2580,13 +2810,25 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" } }, "node_modules/make-dir": { @@ -2849,7 +3091,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3010,6 +3251,7 @@ "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -3095,6 +3337,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -3133,6 +3382,20 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -3255,6 +3518,20 @@ "node": ">=18" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3272,6 +3549,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -3358,25 +3645,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -3495,6 +3763,85 @@ } } }, + "node_modules/vitest": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.3.tgz", + "integrity": "sha512-IUSop8jgaT7w0g1yOM/35qVtKjr/8Va4PrjzH1OUb0YH4c3OXB2lCZDkMAB6glA8T5w8S164oJGsbcmAecr4sA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/expect": "4.0.3", + "@vitest/mocker": "4.0.3", + "@vitest/pretty-format": "4.0.3", + "@vitest/runner": "4.0.3", + "@vitest/snapshot": "4.0.3", + "@vitest/spy": "4.0.3", + "@vitest/utils": "4.0.3", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.19", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.3", + "@vitest/browser-preview": "4.0.3", + "@vitest/browser-webdriverio": "4.0.3", + "@vitest/ui": "4.0.3", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", @@ -3525,6 +3872,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index d1a5ace..6c61d77 100644 --- a/package.json +++ b/package.json @@ -22,23 +22,24 @@ "types": "./dist/index.d.ts", "scripts": { "build": "vite build", - "test": "c8 --reporter=lcov uvu", + "test": "vitest --coverage", "check": "tsc", - "prettier": "prettier --check index.js test/**/*.js", + "prettier": "prettier --check index.ts test/**/*.ts", "lint": "oxlint", "lint-package": "publint" }, "devDependencies": { "@codecov/vite-plugin": "^1.9.1", "@types/css-tree": "^2.3.11", + "@vitest/coverage-v8": "^4.0.3", "c8": "^10.1.3", "oxlint": "^1.24.0", "prettier": "^3.6.2", "publint": "^0.3.15", "typescript": "^5.9.3", - "uvu": "^0.5.6", "vite": "^6.2.6", - "vite-plugin-dts": "^4.5.4" + "vite-plugin-dts": "^4.5.4", + "vitest": "^4.0.3" }, "dependencies": { "css-tree": "^3.1.0" diff --git a/test/api.test.js b/test/api.test.ts similarity index 82% rename from test/api.test.js rename to test/api.test.ts index c5ef7f5..6ec1152 100644 --- a/test/api.test.js +++ b/test/api.test.ts @@ -1,18 +1,16 @@ -import { test } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' test('empty input', () => { let actual = format(``) let expected = `` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('handles invalid input', () => { let actual = format(`;`) let expected = `;` - - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('Vadim Makeevs example works', () => { @@ -40,7 +38,7 @@ test('Vadim Makeevs example works', () => { } } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('minified Vadims example', () => { @@ -55,7 +53,5 @@ test('minified Vadims example', () => { } } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/atrules.test.js b/test/atrules.test.ts similarity index 87% rename from test/atrules.test.js rename to test/atrules.test.ts index 9980dcc..c543fae 100644 --- a/test/atrules.test.js +++ b/test/atrules.test.ts @@ -1,9 +1,6 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format, minify } from '../index.js' -let test = suite('Atrules') - test('AtRules start on a new line', () => { let actual = format(` @media (min-width: 1000px) { @@ -25,7 +22,7 @@ test('AtRules start on a new line', () => { } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('Atrule blocks are surrounded by {} with correct spacing and indentation', () => { @@ -51,49 +48,49 @@ test('Atrule blocks are surrounded by {} with correct spacing and indentation', } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('adds whitespace between prelude and {', () => { let actual = format(`@media all{}`) let expected = `@media all {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('collapses whitespaces in prelude', () => { let actual = format(`@media all and (min-width: 1000px) {}`) let expected = `@media all and (min-width: 1000px) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('adds whitespace to @media (min-width:1000px)', () => { let actual = format(`@media (min-width:1000px) {}`) let expected = `@media (min-width: 1000px) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('removes excess whitespace around min-width : 1000px', () => { let actual = format(`@media (min-width : 1000px) {}`) let expected = `@media (min-width: 1000px) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats @layer with excess whitespace', () => { let actual = format(`@layer test;`) let expected = `@layer test;` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('adds whitespace to @layer tbody,thead', () => { let actual = format(`@layer tbody,thead;`) let expected = `@layer tbody, thead;` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('adds whitespace to @supports (display:grid)', () => { let actual = format(`@supports (display:grid){}`) let expected = `@supports (display: grid) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('@media prelude formatting', () => { @@ -120,7 +117,7 @@ test('@media prelude formatting', () => { for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -132,7 +129,7 @@ test('lowercases functions inside atrule preludes', () => { let expected = `@import url("style.css") layer(test) supports(display: grid); @supports selector([popover]:open) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats @scope', () => { @@ -143,7 +140,7 @@ test('formats @scope', () => { let expected = `@scope (.light-scheme) {} @scope (.media-object) to (.content > *) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('calc() inside @media', () => { @@ -163,37 +160,40 @@ test('calc() inside @media', () => { @media (min-width: calc(4px * 4)) {} @media (min-width: calc(5px * 5)) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('minify: calc(*) inside @media', () => { let actual = minify(`@media (min-width: calc(1px*1)) {}`) let expected = `@media (min-width:calc(1px*1)){}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('minify: calc(+) inside @media', () => { let actual = minify(`@media (min-width: calc(1px + 1em)) {}`) let expected = `@media (min-width:calc(1px + 1em)){}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('minify: calc(-) inside @media', () => { let actual = minify(`@media (min-width: calc(1em - 1px)) {}`) let expected = `@media (min-width:calc(1em - 1px)){}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('@import prelude formatting', () => { let fixtures = [ ['@import url("fineprint.css") print;', '@import url("fineprint.css") print;'], ['@import url("style.css") layer;', '@import url("style.css") layer;'], - ['@import url("style.css") layer(test.first) supports(display:grid);', '@import url("style.css") layer(test.first) supports(display: grid);'], + [ + '@import url("style.css") layer(test.first) supports(display:grid);', + '@import url("style.css") layer(test.first) supports(display: grid);', + ], ] for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -206,7 +206,7 @@ test('@supports prelude formatting', () => { for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -218,7 +218,7 @@ test('@layer prelude formatting', () => { for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -230,7 +230,7 @@ test('minify: @layer prelude formatting', () => { for (let [css, expected] of fixtures) { let actual = minify(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -250,7 +250,7 @@ test('single empty line after a rule, before atrule', () => { property: value; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('single empty line in between atrules', () => { @@ -267,7 +267,7 @@ test('single empty line in between atrules', () => { property: value; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('newline between last declaration and nested atrule', () => { @@ -286,19 +286,19 @@ test('newline between last declaration and nested atrule', () => { property2: value2; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('lowercases the atrule name', () => { let actual = format(`@LAYER test {}`) let expected = `@layer test {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not lowercase the atrule value', () => { let actual = format('@keyframes TEST {}') let expected = '@keyframes TEST {}' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('Atrules w/o Block are terminated with a semicolon', () => { @@ -309,7 +309,7 @@ test('Atrules w/o Block are terminated with a semicolon', () => { let expected = `@layer test; @import url('test');` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('Empty atrule braces are placed on the same line', () => { @@ -321,7 +321,7 @@ test('Empty atrule braces are placed on the same line', () => { let expected = `@media all {} @supports (display: grid) {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('new-fangled comparators (width > 1000px)', () => { @@ -341,13 +341,13 @@ test('new-fangled comparators (width > 1000px)', () => { @media (width <= 1000px) {} @media (200px < width < 1000px) {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('minify: new-fangled comparators (width > 1000px)', () => { let actual = minify(`@container (width>1000px) {}`) let expected = `@container (width>1000px){}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test.skip('preserves comments', () => { @@ -368,7 +368,5 @@ test.skip('preserves comments', () => { @layer /* comment */ {} ` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/comments.test.js b/test/comments.test.ts similarity index 87% rename from test/comments.test.js rename to test/comments.test.ts index 8b3a7c5..a7a4330 100644 --- a/test/comments.test.js +++ b/test/comments.test.ts @@ -1,13 +1,10 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Comments') - test('only comment', () => { let actual = format(`/* comment */`) let expected = `/* comment */` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('bang comment before rule', () => { @@ -18,7 +15,7 @@ test('bang comment before rule', () => { let expected = `/*! comment */ selector {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('before selectors', () => { @@ -34,7 +31,7 @@ selector1, selector2 { property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('before nested selectors', () => { @@ -54,7 +51,7 @@ test('before nested selectors', () => { property: value; } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('after selectors', () => { @@ -70,7 +67,7 @@ selector2 /* comment */ { property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in between selectors', () => { @@ -86,7 +83,7 @@ test('in between selectors', () => { selector2 { property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in between nested selectors', () => { @@ -106,7 +103,7 @@ test('in between nested selectors', () => { property: value; } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as first child in rule', () => { @@ -120,7 +117,7 @@ test('as first child in rule', () => { /* comment */ property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as last child in rule', () => { @@ -134,7 +131,7 @@ test('as last child in rule', () => { property: value; /* comment */ }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as last child in nested rule', () => { @@ -152,7 +149,7 @@ test('as last child in nested rule', () => { /* comment */ } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as only child in rule', () => { @@ -164,7 +161,7 @@ test('as only child in rule', () => { let expected = `selector { /* comment */ }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as only child in nested rule', () => { @@ -178,7 +175,7 @@ test('as only child in nested rule', () => { /* comment */ } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in between declarations', () => { @@ -194,7 +191,7 @@ test('in between declarations', () => { /* comment */ property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in between nested declarations', () => { @@ -214,7 +211,7 @@ test('in between nested declarations', () => { property: value; } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as first child in atrule', () => { @@ -232,7 +229,7 @@ test('as first child in atrule', () => { property: value; } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as first child in nested atrule', () => { @@ -254,7 +251,7 @@ test('as first child in nested atrule', () => { } } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as last child in atrule', () => { @@ -272,7 +269,7 @@ test('as last child in atrule', () => { } /* comment */ }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as last child in nested atrule', () => { @@ -294,7 +291,7 @@ test('as last child in nested atrule', () => { /* comment */ } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as only child in atrule', () => { @@ -306,7 +303,7 @@ test('as only child in atrule', () => { let expected = `@media (min-width: 1000px) { /* comment */ }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('as only child in nested atrule', () => { @@ -322,7 +319,7 @@ test('as only child in nested atrule', () => { /* comment */ } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in between rules and atrules', () => { @@ -346,7 +343,7 @@ selector {} /* comment 4 */ } /* comment 5 */` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('comment before rule and atrule should not be separated by newline', () => { @@ -370,7 +367,7 @@ selector {} selector {} /* comment 4 */ }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('a declaration after multiple comments starts on a new line', () => { @@ -400,7 +397,7 @@ test('a declaration after multiple comments starts on a new line', () => { /* comment 6 */ --custom-property: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('multiple comments in between rules and atrules', () => { @@ -434,7 +431,7 @@ selector {} } /* comment 5 */ /* comment 5.1 */` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('puts every comment on a new line', () => { @@ -449,33 +446,33 @@ test('puts every comment on a new line', () => { /*--font-style: normal;*/ --border-top-color: var(--root-color--support); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in @media prelude', () => { // from CSSTree https://github.com/csstree/csstree/blob/ba6dfd8bb0e33055c05f13803d04825d98dd2d8d/fixtures/ast/mediaQuery/MediaQuery.json#L147 let actual = format('@media all /*0*/ (/*1*/foo/*2*/:/*3*/1/*4*/) {}') let expected = '@media all /*0*/ (/*1*/foo/*2*/: /*3*/1/*4*/) {}' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in @supports prelude', () => { // from CSSTree https://github.com/csstree/csstree/blob/ba6dfd8bb0e33055c05f13803d04825d98dd2d8d/fixtures/ast/atrule/atrule/supports.json#L119 let actual = format('@supports not /*0*/(/*1*/flex :/*3*/1/*4*/)/*5*/{}') let expected = '@supports not /*0*/(/*1*/flex: /*3*/1/*4*/)/*5*/ {}' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('skip in @import prelude before specifier', () => { let actual = format('@import /*test*/"foo";') let expected = '@import "foo";' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in @import prelude after specifier', () => { let actual = format('@import "foo"/*test*/;') let expected = '@import "foo"/*test*/;' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('skip in selector combinator', () => { @@ -485,13 +482,13 @@ test('skip in selector combinator', () => { `) let expected = `a b, a + b {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('in attribute selector', () => { let actual = format(`[/*test*/a/*test*/=/*test*/'b'/*test*/i/*test*/]`) let expected = `[/*test*/a/*test*/=/*test*/'b'/*test*/i/*test*/]` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('skip in var() with fallback', () => { @@ -499,7 +496,7 @@ test('skip in var() with fallback', () => { let expected = `a { prop: var(--name, 1); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('skip in custom property declaration (space toggle)', () => { @@ -507,7 +504,7 @@ test('skip in custom property declaration (space toggle)', () => { let expected = `a { --test: ; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('before value', () => { @@ -515,7 +512,7 @@ test('before value', () => { let expected = `a { prop: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('after value', () => { @@ -525,7 +522,7 @@ test('after value', () => { let expected = `a { prop: value; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('skip in value functions', () => { @@ -543,11 +540,12 @@ test('skip in value functions', () => { background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('strips comments in minification mode', () => { - let actual = format(` + let actual = format( + ` /* comment 1 */ selector {} /* comment 2 */ @@ -557,9 +555,9 @@ test('strips comments in minification mode', () => { /* comment 4 */ } /* comment 5 */ - `, { minify: true }) + `, + { minify: true }, + ) let expected = `selector{}@media (min-width:1000px){selector{}}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/declarations.test.js b/test/declarations.test.ts similarity index 82% rename from test/declarations.test.js rename to test/declarations.test.ts index 3e9cd6d..5f26df7 100644 --- a/test/declarations.test.js +++ b/test/declarations.test.ts @@ -1,9 +1,6 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Declarations') - test('Declarations end with a semicolon (;)', () => { let actual = format(` @font-face { @@ -52,7 +49,7 @@ css { } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('lowercases properties', () => { @@ -60,7 +57,7 @@ test('lowercases properties', () => { let expected = `a { color: green; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not lowercase custom properties', () => { @@ -70,7 +67,7 @@ test('does not lowercase custom properties', () => { let expected = `a { --myVar: 1; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('!important is added', () => { @@ -78,7 +75,7 @@ test('!important is added', () => { let expected = `a { color: green !important; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('!important is lowercase', () => { @@ -86,7 +83,7 @@ test('!important is lowercase', () => { let expected = `a { color: green !important; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('browserhack !ie is printed', () => { @@ -94,7 +91,7 @@ test('browserhack !ie is printed', () => { let expected = `a { color: green !ie; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('browserhack !IE is lowercased', () => { @@ -102,7 +99,5 @@ test('browserhack !IE is lowercased', () => { let expected = `a { color: green !ie; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/minify.test.js b/test/minify.test.ts similarity index 82% rename from test/minify.test.js rename to test/minify.test.ts index 99957e3..a67754b 100644 --- a/test/minify.test.js +++ b/test/minify.test.ts @@ -1,31 +1,28 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { minify } from '../index.js' -let test = suite('Minify') - test('empty rule', () => { let actual = minify(`a {}`) let expected = `a{}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('simple declaration', () => { let actual = minify(`:root { --color: red; }`) let expected = `:root{--color:red}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('simple atrule', () => { let actual = minify(`@media (min-width: 100px) { body { color: red; } }`) let expected = `@media (min-width: 100px){body{color:red}}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('empty atrule', () => { let actual = minify(`@media (min-width: 100px) {}`) let expected = `@media (min-width: 100px){}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats multiline values on a single line', () => { @@ -38,19 +35,19 @@ a { } `) let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('correctly minifies operators', () => { let actual = minify(`a { width: calc(100% - 10px); height: calc(100 * 1%); }`) let expected = `a{width:calc(100% - 10px);height:calc(100*1%)}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('correctly minifiers modern colors', () => { let actual = minify(`a { color: rgb(0 0 0 / 0.1); }`) let expected = `a{color:rgb(0 0 0/0.1)}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('Vadim Makeevs example works', () => { @@ -68,25 +65,23 @@ test('Vadim Makeevs example works', () => { } `) let expected = `@layer what{@container (width > 0){ul:has(:nth-child(1 of li)){@media (height > 0){&:hover{--is:this}}}}}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('minified Vadims example', () => { let actual = minify(`@layer what{@container (width>0){@media (min-height:.001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}`) let expected = `@layer what{@container (width > 0){@media (min-height: .001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('removes whitespace before !important', () => { let actual = minify(`a { color: green !important }`) let expected = `a{color:green!important}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test.only('minifies complex selectors', () => { let actual = minify(`:is(a, b) { color: green }`) let expected = `:is(a,b){color:green}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/rules.test.js b/test/rules.test.ts similarity index 81% rename from test/rules.test.js rename to test/rules.test.ts index 821a1de..9524a2a 100644 --- a/test/rules.test.js +++ b/test/rules.test.ts @@ -1,9 +1,6 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Rules') - test('AtRules and Rules start on a new line', () => { let actual = format(` selector { property: value; } @@ -35,7 +32,7 @@ selector { } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('An empty line is rendered in between Rules', () => { @@ -50,7 +47,7 @@ test('An empty line is rendered in between Rules', () => { rule2 { property: value; }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('single empty line after a rule, before atrule', () => { @@ -69,7 +66,7 @@ test('single empty line after a rule, before atrule', () => { property: value; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('newline between last declaration and nested ruleset', () => { @@ -95,7 +92,7 @@ test('newline between last declaration and nested ruleset', () => { } } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('newline between last declaration and nested atrule', () => { @@ -114,7 +111,7 @@ test('newline between last declaration and nested atrule', () => { property2: value2; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('no trailing newline on empty nested rule', () => { @@ -126,7 +123,7 @@ test('no trailing newline on empty nested rule', () => { let expected = `@layer test { empty {} }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats nested rules with selectors starting with', () => { @@ -142,7 +139,7 @@ test('formats nested rules with selectors starting with', () => { property: value; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('newlines between declarations, nested rules and more declarations', () => { @@ -156,7 +153,7 @@ test('newlines between declarations, nested rules and more declarations', () => color: green; }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats nested rules with a selector starting with &', () => { @@ -170,7 +167,7 @@ test('formats nested rules with a selector starting with &', () => { color: red; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats unknown stuff in curly braces', () => { @@ -182,7 +179,7 @@ test('formats unknown stuff in curly braces', () => { let expected = `selector { { color: red; } }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('[check broken test] Relaxed nesting: formats nested rules with a selector with a &', () => { @@ -194,7 +191,7 @@ test('[check broken test] Relaxed nesting: formats nested rules with a selector let expected = `selector { a & { color:red } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test.skip('Relaxed nesting: formats nested rules with a selector with a &', () => { @@ -208,7 +205,7 @@ test.skip('Relaxed nesting: formats nested rules with a selector with a &', () = color: red; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('[check broken test] Relaxed nesting: formats nested rules with a selector without a &', () => { @@ -220,7 +217,7 @@ test('[check broken test] Relaxed nesting: formats nested rules with a selector let expected = `selector { a { color:red } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test.skip('Relaxed nesting: formats nested rules with a selector without a &', () => { @@ -234,7 +231,7 @@ test.skip('Relaxed nesting: formats nested rules with a selector without a &', ( color: red; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('[check broken test] Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { @@ -250,7 +247,7 @@ test('[check broken test] Relaxed nesting: formats nested rules with a selector ~ a { color:red } + a { color:red } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test.skip('Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { @@ -274,7 +271,17 @@ test.skip('Relaxed nesting: formats nested rules with a selector starting with a color: red; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) +}) + +test('handles syntax errors: unclosed block', () => { + let actual = format(`a { mumblejumble`) + let expected = 'a {\n\tmumblejumble\n}' + expect(actual).toEqual(expected) }) -test.run() +test('handles syntax errors: premature closed block', () => { + let actual = format(`a { mumblejumble: }`) + let expected = 'a {\n\tmumblejumble: ;\n}' + expect(actual).toEqual(expected) +}) diff --git a/test/selectors.test.js b/test/selectors.test.ts similarity index 83% rename from test/selectors.test.js rename to test/selectors.test.ts index 3f6fb94..dd877bf 100644 --- a/test/selectors.test.js +++ b/test/selectors.test.ts @@ -1,13 +1,10 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Selectors') - test('A single selector is rendered without a trailing comma', () => { let actual = format('a {}') let expected = 'a {}' - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('Multiple selectors are placed on a new line, separated by commas', () => { @@ -28,7 +25,7 @@ selector1aa, selector2, selector3 {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats multiline selectors on a single line', () => { @@ -41,7 +38,7 @@ color: green } let expected = `a.b .c .d .e .f { color: green; }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats simple selector combinators', () => { @@ -53,7 +50,7 @@ test('formats simple selector combinators', () => { let expected = `a > b, a > b ~ c d, .article-content ol li > * {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('lowercases type selectors', () => { @@ -65,7 +62,7 @@ test('lowercases type selectors', () => { let expected = `a, b, c {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats nested selector combinators', () => { @@ -78,7 +75,7 @@ test('formats nested selector combinators', () => { for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -97,7 +94,7 @@ b::after, c::first-letter {}` let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats pseudo elements with odd casing', () => { @@ -117,7 +114,7 @@ c::after, d::first-letter {}` let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats selectors with Nth', () => { @@ -133,7 +130,7 @@ test('formats selectors with Nth', () => { for (let [css, expected] of fixtures) { let actual = format(css) - assert.equal(actual, expected) + expect(actual).toEqual(expected) } }) @@ -146,7 +143,7 @@ test('formats multiline selectors', () => { ) {} `) let expected = `a:is(a, b, c) {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('format nesting selectors', () => { @@ -157,7 +154,7 @@ test('format nesting selectors', () => { let expected = `& a {} b & c {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('forces attribute selectors to have quoted values', () => { @@ -169,7 +166,7 @@ test('forces attribute selectors to have quoted values', () => { let expected = `[title="foo"], [title="bar"], [title="baz"] {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('adds a space before attribute selector flags', () => { @@ -181,14 +178,14 @@ test('adds a space before attribute selector flags', () => { let expected = `[title="foo" i], [title="baz" i], [title="foo" i] {}` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats :lang correctly', () => { let actual = format(`:lang("nl","de"),li:nth-child() {}`) let expected = `:lang("nl","de"), li:nth-child() {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test(`formats ::highlight and ::highlight(Name) correctly`, () => { @@ -196,7 +193,7 @@ test(`formats ::highlight and ::highlight(Name) correctly`, () => { let expected = `::highlight, ::highlight(Name), ::highlight(my-thing) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats unknown pseudos correctly', () => { @@ -208,7 +205,14 @@ test('formats unknown pseudos correctly', () => { let expected = `::foo-bar, :unkown-thing(), :unnowkn(kjsa.asddk,asd) {}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) -test.run() +test('handles syntax errors', () => { + let actual = format(` + test, + @test {} + `) + let expected = ` {}` + expect(actual).toEqual(expected) +}) diff --git a/test/tab-size.test.js b/test/tab-size.test.ts similarity index 61% rename from test/tab-size.test.js rename to test/tab-size.test.ts index 931a4c7..6532ce6 100644 --- a/test/tab-size.test.js +++ b/test/tab-size.test.ts @@ -1,9 +1,6 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Tab Size') - let fixture = ` selector { color: red; @@ -11,7 +8,8 @@ let fixture = ` ` test('tab_size: 2', () => { - let actual = format(` + let actual = format( + ` selector { color: red; } @@ -21,7 +19,9 @@ test('tab_size: 2', () => { color: blue; } } - `, { tab_size: 2 }) + `, + { tab_size: 2 }, + ) let expected = `selector { color: red; } @@ -31,24 +31,22 @@ test('tab_size: 2', () => { color: blue; } }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('invalid tab_size: 0', () => { - assert.throws(() => format(fixture, { tab_size: 0 })) + expect(() => format(fixture, { tab_size: 0 })).toThrow() }) test('invalid tab_size: negative', () => { - assert.throws(() => format(fixture, { tab_size: -1 })) + expect(() => format(fixture, { tab_size: -1 })).toThrow() }) test('combine tab_size and minify', () => { let actual = format(fixture, { tab_size: 2, - minify: true + minify: true, }) let expected = `selector{color:red}` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/test/values.test.js b/test/values.test.ts similarity index 89% rename from test/values.test.js rename to test/values.test.ts index 2bd20fa..c261175 100644 --- a/test/values.test.js +++ b/test/values.test.ts @@ -1,9 +1,6 @@ -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, expect } from 'vitest' import { format } from '../index.js' -let test = suite('Values') - test('collapses abundant whitespace', () => { let actual = format(`a { transition: all 100ms ease; @@ -15,7 +12,7 @@ test('collapses abundant whitespace', () => { color: rgb(0, 0, 0); color: red; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats simple value lists', () => { @@ -39,7 +36,7 @@ test('formats simple value lists', () => { content: 'Test'; background-image: url("EXAMPLE.COM"); }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats nested value lists', () => { @@ -51,7 +48,7 @@ test('formats nested value lists', () => { let expected = `a { background: red, linear-gradient(to bottom, red 10%, green 50%, blue 100%); }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats nested var()', () => { @@ -65,7 +62,7 @@ test('formats nested var()', () => { color: var(--test1, var(--test2, green)); color: var(--test3, rgb(0, 0, 0)); }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('formats multiline values on a single line', () => { @@ -86,7 +83,7 @@ a { background: linear-gradient(red, 10% blue, 20% green, 100% yellow); color: rgb(0, 0, 0); }` - assert.equal(actual, expected) + expect(actual).toEqual(expected) }) test('does not break font shorthand', () => { @@ -100,7 +97,7 @@ test('does not break font shorthand', () => { font: 2em/2 sans-serif; font: 2em/2 sans-serif; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats whitespace around operators (*/+-) correctly', () => { @@ -118,7 +115,7 @@ test('formats whitespace around operators (*/+-) correctly', () => { font-size: calc(2em + 2px); font-size: calc(2em - 2px); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats whitespace around operators (*/+-) correctly in nested parenthesis', () => { @@ -134,7 +131,7 @@ test('formats whitespace around operators (*/+-) correctly in nested parenthesis width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats parenthesis correctly', () => { @@ -150,7 +147,7 @@ test('formats parenthesis correctly', () => { width: calc(100% - (var(--x))); width: calc((100% - (var(--x)))); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not lowercase grid-area names', () => { @@ -158,7 +155,7 @@ test('does not lowercase grid-area names', () => { let expected = `a { grid-area: emailInputBox; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not lowercase custom properties in var()', () => { @@ -166,7 +163,7 @@ test('does not lowercase custom properties in var()', () => { let expected = `a { color: var(--MyColor); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('lowercases CSS functions', () => { @@ -178,7 +175,7 @@ test('lowercases CSS functions', () => { color: rgb(0, 0, 0); transform: translatex(100px); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('relative colors', () => { @@ -194,7 +191,7 @@ test('relative colors', () => { color: hwb(from var(--base-color) h w b / var(--standard-opacity)); color: lch(from var(--base-color) calc(l + 20) c h); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not change casing of `NaN`', () => { @@ -204,7 +201,7 @@ test('does not change casing of `NaN`', () => { let expected = `a { height: calc(1 * NaN); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not change casing of URLs', () => { @@ -214,7 +211,7 @@ test('does not change casing of URLs', () => { let expected = `a { background-image: url("My-Url.png"); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('lowercases dimensions', () => { @@ -226,7 +223,7 @@ test('lowercases dimensions', () => { font-size: 12px; width: var(--test, 33rem); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('formats unknown content in value', () => { @@ -236,7 +233,7 @@ test('formats unknown content in value', () => { let expected = `a { content: 'Test' : counter(page); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not break space toggles', () => { @@ -248,7 +245,7 @@ test('does not break space toggles', () => { --ON: initial; --OFF: ; }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('does not break space toggles (minified)', () => { @@ -260,7 +257,7 @@ test('does not break space toggles (minified)', () => { { minify: true }, ) let expected = `a{--ON:initial;--OFF: }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) test('adds quotes around strings in url()', () => { @@ -284,7 +281,5 @@ test('adds quotes around strings in url()', () => { offset-path: url("#path"); mask-image: url("masks.svg#mask1"); }` - assert.is(actual, expected) + expect(actual).toEqual(expected) }) - -test.run() diff --git a/tsconfig.json b/tsconfig.json index 90da5d7..0a0723e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ "lib": ["ES2022", "DOM", "DOM.Iterable"], }, "include": [ - "index.js" + "index.ts" ], "exclude": ["node_modules"] } \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index a96f635..54c661d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,26 +1,32 @@ -import { resolve } from "path" -import { defineConfig } from "vite" -import dts from "vite-plugin-dts" -import { codecovVitePlugin } from "@codecov/vite-plugin" +/// +import { resolve } from 'path' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import { codecovVitePlugin } from '@codecov/vite-plugin' export default defineConfig({ - build: { - lib: { - entry: resolve(__dirname, "index.js"), - formats: ["es"], - }, - rollupOptions: { - // make sure to externalize deps that shouldn't be bundled - // into your library - external: ["css-tree/parser"], - }, - }, - plugins: [ - dts(), - codecovVitePlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: "formatCss", - uploadToken: process.env.CODECOV_TOKEN, - }), - ], + build: { + lib: { + entry: resolve(__dirname, 'index.ts'), + formats: ['es'], + }, + rollupOptions: { + // make sure to externalize deps that shouldn't be bundled + // into your library + external: ['css-tree'], + }, + }, + plugins: [ + dts(), + codecovVitePlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCss', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + test: { + coverage: { + provider: 'v8', + }, + }, }) From 873213be44b0e17ca246a5d38784fe9d17fb254e Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Sun, 26 Oct 2025 11:46:32 +0100 Subject: [PATCH 2/2] fix tests --- test/minify.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/minify.test.ts b/test/minify.test.ts index a67754b..f316cb6 100644 --- a/test/minify.test.ts +++ b/test/minify.test.ts @@ -15,13 +15,13 @@ test('simple declaration', () => { test('simple atrule', () => { let actual = minify(`@media (min-width: 100px) { body { color: red; } }`) - let expected = `@media (min-width: 100px){body{color:red}}` + let expected = `@media (min-width:100px){body{color:red}}` expect(actual).toEqual(expected) }) test('empty atrule', () => { let actual = minify(`@media (min-width: 100px) {}`) - let expected = `@media (min-width: 100px){}` + let expected = `@media (min-width:100px){}` expect(actual).toEqual(expected) }) @@ -64,13 +64,13 @@ test('Vadim Makeevs example works', () => { } } `) - let expected = `@layer what{@container (width > 0){ul:has(:nth-child(1 of li)){@media (height > 0){&:hover{--is:this}}}}}` + let expected = `@layer what{@container (width>0){ul:has(:nth-child(1 of li)){@media (height>0){&:hover{--is:this}}}}}` expect(actual).toEqual(expected) }) test('minified Vadims example', () => { let actual = minify(`@layer what{@container (width>0){@media (min-height:.001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}`) - let expected = `@layer what{@container (width > 0){@media (min-height: .001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}` + let expected = `@layer what{@container (width>0){@media (min-height:.001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}` expect(actual).toEqual(expected) }) @@ -80,7 +80,7 @@ test('removes whitespace before !important', () => { expect(actual).toEqual(expected) }) -test.only('minifies complex selectors', () => { +test('minifies complex selectors', () => { let actual = minify(`:is(a, b) { color: green }`) let expected = `:is(a,b){color:green}` expect(actual).toEqual(expected)