From 3d96209d037620a49962d79d6a54b214b57e874e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:39:58 +0000 Subject: [PATCH 1/7] chore(deps-dev): bump typescript from 5.9.3 to 6.0.2 Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2) --- updated-dependencies: - dependency-name: typescript dependency-version: 6.0.2 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4b89873..89946ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "oxlint": "^1.24.0", "publint": "^0.3.15", "tsdown": "^0.21.0", - "typescript": "^5.9.3", + "typescript": "^6.0.2", "vitest": "^4.0.3" }, "engines": { @@ -3438,9 +3438,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index be1d537..68fbfbd 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "oxlint": "^1.24.0", "publint": "^0.3.15", "tsdown": "^0.21.0", - "typescript": "^5.9.3", + "typescript": "^6.0.2", "vitest": "^4.0.3" }, "engines": { From 98af0b099a89e4f9f55300c86067f2ac92f28586 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 11:54:06 +0200 Subject: [PATCH 2/7] fix types --- test/api.test.js | 52 ++++ test/atrules.test.js | 351 +++++++++++++++++++++++++ test/comments.test.js | 525 ++++++++++++++++++++++++++++++++++++++ test/declarations.test.js | 95 +++++++ test/minify.test.js | 81 ++++++ test/rules.test.js | 245 ++++++++++++++++++ test/selectors.test.js | 236 +++++++++++++++++ test/tab-size.test.js | 44 ++++ test/values.test.js | 313 +++++++++++++++++++++++ tsconfig.json | 48 ++-- tsdown.config.js | 32 +++ vitest.config.js | 14 + 12 files changed, 2012 insertions(+), 24 deletions(-) create mode 100644 test/api.test.js create mode 100644 test/atrules.test.js create mode 100644 test/comments.test.js create mode 100644 test/declarations.test.js create mode 100644 test/minify.test.js create mode 100644 test/rules.test.js create mode 100644 test/selectors.test.js create mode 100644 test/tab-size.test.js create mode 100644 test/values.test.js create mode 100644 tsdown.config.js create mode 100644 vitest.config.js diff --git a/test/api.test.js b/test/api.test.js new file mode 100644 index 0000000..1cf0bcc --- /dev/null +++ b/test/api.test.js @@ -0,0 +1,52 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +test('empty input', () => { + let actual = format(``); + let expected = ``; + expect(actual).toEqual(expected); +}); +test('handles invalid input', () => { + let actual = format(`;`); + let expected = ``; + expect(actual).toEqual(expected); +}); +test('Vadim Makeevs example works', () => { + let actual = format(` + @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 = format(`@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); +}); diff --git a/test/atrules.test.js b/test/atrules.test.js new file mode 100644 index 0000000..c580233 --- /dev/null +++ b/test/atrules.test.js @@ -0,0 +1,351 @@ +import { test, expect } from 'vitest'; +import { format, minify } from '../src/lib/index.js'; +test('AtRules start on a new line', () => { + let actual = format(` + @media (min-width: 1000px) { + selector { property: value; } + } + @layer test { + selector { property: value; } + } + `); + let expected = `@media (min-width: 1000px) { + selector { + property: value; + } +} + +@layer test { + selector { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('Atrule blocks are surrounded by {} with correct spacing and indentation', () => { + let actual = format(` + @media (min-width:1000px){selector{property:value1}} + + @media (min-width:1000px) + { + selector + { + property:value2 + } +}`); + let expected = `@media (min-width: 1000px) { + selector { + property: value1; + } +} + +@media (min-width: 1000px) { + selector { + property: value2; + } +}`; + expect(actual).toEqual(expected); +}); +test('adds whitespace between prelude and {', () => { + let actual = format(`@media all{}`); + let expected = `@media all {}`; + 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) {}`; + expect(actual).toEqual(expected); +}); +test('removes newlines in prelude', () => { + let actual = format(`@media + all, + screen, + print, + (min-width: 1000px) {}`); + let expected = `@media all, screen, print, (min-width: 1000px) {}`; + 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) {}`; + 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) {}`; + expect(actual).toEqual(expected); +}); +test('formats @layer with excess whitespace', () => { + let actual = format(`@layer test;`); + let expected = `@layer test;`; + expect(actual).toEqual(expected); +}); +test('adds whitespace to @layer tbody,thead', () => { + let actual = format(`@layer tbody,thead;`); + let expected = `@layer tbody, thead;`; + expect(actual).toEqual(expected); +}); +test('adds whitespace to @supports (display:grid)', () => { + let actual = format(`@supports (display:grid){}`); + let expected = `@supports (display: grid) {}`; + expect(actual).toEqual(expected); +}); +test('@media prelude formatting', () => { + let fixtures = [ + [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], + [ + `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, + `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, + ], + [ + `@media (min-width: 1024px)or (max-width: 1439px) {}`, + `@media (min-width: 1024px) or (max-width: 1439px) {}`, + ], + [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], + [ + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + ], + [`@media screen or print {}`, `@media screen or print {}`], + [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [ + `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, + `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, + ], + [ + `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, + `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, + ], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); +test('lowercases functions inside atrule preludes', () => { + let actual = format(` +@import URL("style.css") LAYER(test) SUPPORTS(display:grid); +@supports SELECTOR([popover]:open) {} +`); + let expected = `@import url("style.css") layer(test) supports(display: grid); + +@supports selector([popover]:open) {}`; + expect(actual).toEqual(expected); +}); +test('formats @scope', () => { + let actual = format(` + @scope (.light-scheme) {} + @scope (.media-object) to (.content > *) {} +`); + let expected = `@scope (.light-scheme) {} + +@scope (.media-object) to (.content > *) {}`; + expect(actual).toEqual(expected); +}); +test('calc() inside @media', () => { + let actual = format(` + @media (min-width: calc(1px*1)) {} + @media (min-width: calc(2px* 2)) {} + @media (min-width: calc(3px *3)) {} + @media (min-width: calc(4px * 4)) {} + @media (min-width: calc(5px * 5)) {} + `); + let expected = `@media (min-width: calc(1px * 1)) {} + +@media (min-width: calc(2px * 2)) {} + +@media (min-width: calc(3px * 3)) {} + +@media (min-width: calc(4px * 4)) {} + +@media (min-width: calc(5px * 5)) {}`; + 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)){}`; + 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)){}`; + 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)){}`; + 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);', + ], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); +test('@supports prelude formatting', () => { + let fixtures = [ + [`@supports (display:grid){}`, `@supports (display: grid) {}`], + [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], + ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); +test('@layer prelude formatting', () => { + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody, thead;`], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); +test('minify: @layer prelude formatting', () => { + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody,thead;`], + ]; + for (let [css, expected] of fixtures) { + let actual = minify(css); + expect(actual).toEqual(expected); + } +}); +test('single empty line after a rule, before atrule', () => { + let actual = format(` + rule1 { property: value } + @media (min-width: 1000px) { + rule2 { property: value } + } + `); + let expected = `rule1 { + property: value; +} + +@media (min-width: 1000px) { + rule2 { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('single empty line in between atrules', () => { + let actual = format(` + @layer test1; + @media (min-width: 1000px) { + rule2 { property: value } + } + `); + let expected = `@layer test1; + +@media (min-width: 1000px) { + rule2 { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('newline between last declaration and nested atrule', () => { + let actual = format(` + test { + property1: value1; + @media all { + property2: value2; + } + } + `); + let expected = `test { + property1: value1; + + @media all { + property2: value2; + } +}`; + expect(actual).toEqual(expected); +}); +test('lowercases the atrule name', () => { + let actual = format(`@LAYER test {}`); + let expected = `@layer test {}`; + expect(actual).toEqual(expected); +}); +test('does not lowercase the atrule value', () => { + let actual = format('@keyframes TEST {}'); + let expected = '@keyframes TEST {}'; + expect(actual).toEqual(expected); +}); +test('Atrules w/o Block are terminated with a semicolon', () => { + let actual = format(` + @layer test; + @import url('test'); + `); + let expected = `@layer test; + +@import url('test');`; + expect(actual).toEqual(expected); +}); +test('Empty atrule braces are placed on the same line', () => { + let actual = format(`@media all { + + } + + @supports (display: grid) {}`); + let expected = `@media all {} + +@supports (display: grid) {}`; + expect(actual).toEqual(expected); +}); +test('new-fangled comparators (width > 1000px)', () => { + let actual = format(` + @container (width>1000px) {} + @media (width>1000px) {} + @media (width=>1000px) {} + @media (width<=1000px) {} + @media (200px 1000px) {} + +@media (width > 1000px) {} + +@media (width => 1000px) {} + +@media (width <= 1000px) {} + +@media (200px < width < 1000px) {}`; + expect(actual).toEqual(expected); +}); +test('minify: new-fangled comparators (width > 1000px)', () => { + let actual = minify(`@container (width>1000px) {}`); + let expected = `@container (width>1000px){}`; + expect(actual).toEqual(expected); +}); +test.skip('preserves comments', () => { + let actual = format(` + @media /* comment */ all {} + @media all /* comment */ {} + @media (min-width: 1000px /* comment */) {} + @media (/* comment */ min-width: 1000px) {} + @layer /* comment */ {} + `); + let expected = `@media /* comment */ all {} + +@media all /* comment */ {} + +@media (min-width: 1000px /* comment */) {} + +@media (/* comment */ min-width: 1000px) {} + +@layer /* comment */ {} +`; + expect(actual).toEqual(expected); +}); diff --git a/test/comments.test.js b/test/comments.test.js new file mode 100644 index 0000000..c953e93 --- /dev/null +++ b/test/comments.test.js @@ -0,0 +1,525 @@ +import { describe, test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +describe('comments', () => { + test('only comment', () => { + let actual = format(`/* comment */`); + let expected = `/* comment */`; + expect(actual).toEqual(expected); + }); + test('bang comment before rule', () => { + let actual = format(` + /*! comment */ + selector {} + `); + let expected = `/*! comment */ +selector {}`; + expect(actual).toEqual(expected); + }); + test('before selectors', () => { + let actual = format(` + /* comment */ + selector1, + selector2 { + property: value; + } + `); + let expected = `/* comment */ +selector1, +selector2 { + property: value; +}`; + expect(actual).toEqual(expected); + }); + test('before nested selectors', () => { + let actual = format(` + a { + /* comment */ + & nested1, + & nested2 { + property: value; + } + } + `); + let expected = `a { + /* comment */ + & nested1, + & nested2 { + property: value; + } +}`; + expect(actual).toEqual(expected); + }); + test('after selectors', () => { + let actual = format(` + selector1, + selector2 + /* comment */ { + property: value; + } + `); + let expected = `selector1, +selector2 +/* comment */ { + property: value; +}`; + expect(actual).toEqual(expected); + }); + test('in between selectors', () => { + let actual = format(` + selector1, + /* comment */ + selector2 { + property: value; + } + `); + let expected = `selector1, +/* comment */ +selector2 { + property: value; +}`; + expect(actual).toEqual(expected); + }); + test('in between nested selectors', () => { + let actual = format(` + a { + & nested1, + /* comment */ + & nested2 { + property: value; + } + } + `); + let expected = `a { + & nested1, + /* comment */ + & nested2 { + property: value; + } +}`; + expect(actual).toEqual(expected); + }); + test('as first child in rule', () => { + let actual = format(` + selector { + /* comment */ + property: value; + } + `); + let expected = `selector { + /* comment */ + property: value; +}`; + expect(actual).toEqual(expected); + }); + test('as last child in rule', () => { + let actual = format(` + selector { + property: value; + /* comment */ + } + `); + let expected = `selector { + property: value; + /* comment */ +}`; + expect(actual).toEqual(expected); + }); + test('as last child in nested rule', () => { + let actual = format(` + a { + & selector { + property: value; + /* comment */ + } + } + `); + let expected = `a { + & selector { + property: value; + /* comment */ + } +}`; + expect(actual).toEqual(expected); + }); + test('as only child in rule', () => { + let actual = format(` + selector { + /* comment */ + } + `); + let expected = `selector { + /* comment */ +}`; + expect(actual).toEqual(expected); + }); + test('as only child in nested rule', () => { + let actual = format(`a { + & selector { + /* comment */ + } +}`); + let expected = `a { + & selector { + /* comment */ + } +}`; + expect(actual).toEqual(expected); + }); + test('in between declarations', () => { + let actual = format(` + selector { + property: value; + /* comment */ + property: value; + } + `); + let expected = `selector { + property: value; + /* comment */ + property: value; +}`; + expect(actual).toEqual(expected); + }); + test('in between nested declarations', () => { + let actual = format(` + a { + & selector { + property: value; + /* comment */ + property: value; + } + } + `); + let expected = `a { + & selector { + property: value; + /* comment */ + property: value; + } +}`; + expect(actual).toEqual(expected); + }); + test('as first child in atrule', () => { + let actual = format(` + @media (min-width: 1000px) { + /* comment */ + selector { + property: value; + } + } + `); + let expected = `@media (min-width: 1000px) { + /* comment */ + selector { + property: value; + } +}`; + expect(actual).toEqual(expected); + }); + test('as first child in nested atrule', () => { + let actual = format(` + @media all { + @media (min-width: 1000px) { + /* comment */ + selector { + property: value; + } + } + } + `); + let expected = `@media all { + @media (min-width: 1000px) { + /* comment */ + selector { + property: value; + } + } +}`; + expect(actual).toEqual(expected); + }); + test('as last child in atrule', () => { + let actual = format(` + @media (min-width: 1000px) { + selector { + property: value; + } + /* comment */ + } + `); + let expected = `@media (min-width: 1000px) { + selector { + property: value; + } + /* comment */ +}`; + expect(actual).toEqual(expected); + }); + test('as last child in nested atrule', () => { + let actual = format(` + @media all { + @media (min-width: 1000px) { + selector { + property: value; + } + /* comment */ + } + } + `); + let expected = `@media all { + @media (min-width: 1000px) { + selector { + property: value; + } + /* comment */ + } +}`; + expect(actual).toEqual(expected); + }); + test('as only child in atrule', () => { + let actual = format(` + @media (min-width: 1000px) { + /* comment */ + } + `); + let expected = `@media (min-width: 1000px) { + /* comment */ +}`; + expect(actual).toEqual(expected); + }); + test('as only child in nested atrule', () => { + let actual = format(` + @media all { + @media (min-width: 1000px) { + /* comment */ + } + } + `); + let expected = `@media all { + @media (min-width: 1000px) { + /* comment */ + } +}`; + expect(actual).toEqual(expected); + }); + test('in between rules and atrules', () => { + let actual = format(` + /* comment 1 */ + selector {} + /* comment 2 */ + @media (min-width: 1000px) { + /* comment 3 */ + selector {} + /* comment 4 */ + } + /* comment 5 */ + `); + let expected = `/* comment 1 */ +selector {} +/* comment 2 */ +@media (min-width: 1000px) { + /* comment 3 */ + selector {} + /* comment 4 */ +} +/* comment 5 */`; + expect(actual).toEqual(expected); + }); + test('comment before rule and atrule should not be separated by newline', () => { + let actual = format(` + /* comment 1 */ + selector {} + + /* comment 2 */ + @media (min-width: 1000px) { + /* comment 3 */ + + selector {} + /* comment 4 */ + } + `); + let expected = `/* comment 1 */ +selector {} +/* comment 2 */ +@media (min-width: 1000px) { + /* comment 3 */ + selector {} + /* comment 4 */ +}`; + expect(actual).toEqual(expected); + }); + test('a declaration after multiple comments starts on a new line', () => { + let actual = format(` + selector { + /* comment 1 */ + /* comment 2 */ + --custom-property: value; + + /* comment 3 */ + /* comment 4 */ + --custom-property: value; + + /* comment 5 */ + /* comment 6 */ + --custom-property: value; + } + `); + let expected = `selector { + /* comment 1 */ + /* comment 2 */ + --custom-property: value; + /* comment 3 */ + /* comment 4 */ + --custom-property: value; + /* comment 5 */ + /* comment 6 */ + --custom-property: value; +}`; + expect(actual).toEqual(expected); + }); + test('multiple comments in between rules and atrules', () => { + let actual = format(` + /* comment 1 */ + /* comment 1.1 */ + selector {} + /* comment 2 */ + /* comment 2.1 */ + @media (min-width: 1000px) { + /* comment 3 */ + /* comment 3.1 */ + selector {} + /* comment 4 */ + /* comment 4.1 */ + } + /* comment 5 */ + /* comment 5.1 */ + `); + let expected = `/* comment 1 */ +/* comment 1.1 */ +selector {} +/* comment 2 */ +/* comment 2.1 */ +@media (min-width: 1000px) { + /* comment 3 */ + /* comment 3.1 */ + selector {} + /* comment 4 */ + /* comment 4.1 */ +} +/* comment 5 */ +/* comment 5.1 */`; + expect(actual).toEqual(expected); + }); + test('puts every comment on a new line', () => { + let actual = format(` + x { + /*--font-family: inherit;*/ /*--font-style: normal;*/ + --border-top-color: var(--root-color--support); + } +`); + let expected = `x { + /*--font-family: inherit;*/ + /*--font-style: normal;*/ + --border-top-color: var(--root-color--support); +}`; + 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*/) {}'; + 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*/) {}'; + expect(actual).toEqual(expected); + }); + test('skip in @import prelude before specifier', () => { + let actual = format('@import /*test*/"foo";'); + let expected = '@import "foo";'; + expect(actual).toEqual(expected); + }); + test('skip in @import prelude after specifier', () => { + let actual = format('@import "foo"/*test*/;'); + let expected = '@import "foo";'; + expect(actual).toEqual(expected); + }); + test('skip in selector combinator', () => { + let actual = format(` + a/*test*/ /*test*/b, + a/*test*/+/*test*/b {} + `); + let expected = `a /*test*/ /*test*/ b, +a + b {}`; + expect(actual).toEqual(expected); + }); + test('in attribute selector', () => { + let actual = format(`[/*test*/a='b' i/*test*/] {}`); + let expected = `[a="b" i] {}`; + expect(actual).toEqual(expected); + }); + test('skip in var() with fallback', () => { + let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`); + let expected = `a { + prop: var(--name, 1); +}`; + expect(actual).toEqual(expected); + }); + test('skip in custom property declaration (space toggle)', () => { + let actual = format(`a { --test: /*test*/; }`); + let expected = `a { + --test: ; +}`; + expect(actual).toEqual(expected); + }); + test('before value', () => { + let actual = format(`a { prop: /*test*/value; }`); + let expected = `a { + prop: value; +}`; + expect(actual).toEqual(expected); + }); + test('after value', () => { + let actual = format(`a { + prop: value/*test*/; + }`); + let expected = `a { + prop: value; +}`; + expect(actual).toEqual(expected); + }); + test('skip in value functions', () => { + let actual = format(` + a { + background-image: linear-gradient(/* comment */red, green); + background-image: linear-gradient(red/* comment */, green); + background-image: linear-gradient(red, green/* comment */); + background-image: linear-gradient(red, green)/* comment */ + } + `); + let expected = `a { + background-image: linear-gradient(red, green); + background-image: linear-gradient(red, green); + background-image: linear-gradient(red, green); + background-image: linear-gradient(red, green); + /* comment */ +}`; + expect(actual).toEqual(expected); + }); + test('strips comments in minification mode', () => { + let actual = format(` + /* comment 1 */ + selector {} + /* comment 2 */ + @media (min-width: 1000px) { + /* comment 3 */ + selector {} + /* comment 4 */ + } + /* comment 5 */ + `, { minify: true }); + let expected = `selector{}@media (min-width:1000px){selector{}}`; + expect(actual).toEqual(expected); + }); +}); diff --git a/test/declarations.test.js b/test/declarations.test.js new file mode 100644 index 0000000..fb56f49 --- /dev/null +++ b/test/declarations.test.js @@ -0,0 +1,95 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +test('Declarations end with a semicolon (;)', () => { + let actual = format(` + @font-face { + src: url('test'); + font-family: Test + } + + css { + property1: value1; + property2: value2; + + & .nested { + property1: value3; + property2: value4 + } + } + + @media (min-width: 1000px) { + @layer test { + css { + property1: value5 + } + } + } + `); + let expected = `@font-face { + src: url("test"); + font-family: Test; +} + +css { + property1: value1; + property2: value2; + + & .nested { + property1: value3; + property2: value4; + } +} + +@media (min-width: 1000px) { + @layer test { + css { + property1: value5; + } + } +}`; + expect(actual).toEqual(expected); +}); +test('lowercases properties', () => { + let actual = format(`a { COLOR: green }`); + let expected = `a { + color: green; +}`; + expect(actual).toEqual(expected); +}); +test('does not lowercase custom properties', () => { + let actual = format(`a { + --myVar: 1; + }`); + let expected = `a { + --myVar: 1; +}`; + expect(actual).toEqual(expected); +}); +test('!important is added', () => { + let actual = format(`a { color: green !important}`); + let expected = `a { + color: green !important; +}`; + expect(actual).toEqual(expected); +}); +test('!important is lowercase', () => { + let actual = format(`a { color: green !IMPORTANT }`); + let expected = `a { + color: green !important; +}`; + expect(actual).toEqual(expected); +}); +test('browserhack !ie is printed', () => { + let actual = format(`a { color: green !ie}`); + let expected = `a { + color: green !ie; +}`; + expect(actual).toEqual(expected); +}); +test('browserhack !IE is lowercased', () => { + let actual = format(`a { color: green !IE}`); + let expected = `a { + color: green !ie; +}`; + expect(actual).toEqual(expected); +}); diff --git a/test/minify.test.js b/test/minify.test.js new file mode 100644 index 0000000..aea635b --- /dev/null +++ b/test/minify.test.js @@ -0,0 +1,81 @@ +import { test, expect } from 'vitest'; +import { minify } from '../src/lib/index.js'; +test('empty rule', () => { + let actual = minify(`a {}`); + let expected = `a{}`; + expect(actual).toEqual(expected); +}); +test('simple declaration', () => { + let actual = minify(`:root { --color: red; }`); + let expected = `:root{--color:red}`; + 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}}`; + expect(actual).toEqual(expected); +}); +test('empty atrule', () => { + let actual = minify(`@media (min-width: 100px) {}`); + let expected = `@media (min-width:100px){}`; + expect(actual).toEqual(expected); +}); +test('formats multiline values on a single line', () => { + let actual = minify(` +a { + background: linear-gradient( + red, + 10% blue, +20% green,100% yellow); +} + `); + let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}`; + 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%)}`; + 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)}`; + expect(actual).toEqual(expected); +}); +test('Vadim Makeevs example works', () => { + let actual = minify(` + @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}}}}`; + expect(actual).toEqual(expected); +}); +test('removes whitespace before !important', () => { + let actual = minify(`a { color: green !important }`); + let expected = `a{color:green!important}`; + expect(actual).toEqual(expected); +}); +test('minifies complex selectors', () => { + let actual = minify(`:is(a, b) { color: green }`); + let expected = `:is(a,b){color:green}`; + expect(actual).toEqual(expected); +}); +test('removes whitespace around non-whitespace selector combinators', () => { + let actual = minify(`a + b {} c d {}`); + let expected = `a+b{}c d{}`; + expect(actual).toEqual(expected); +}); diff --git a/test/rules.test.js b/test/rules.test.js new file mode 100644 index 0000000..047eb27 --- /dev/null +++ b/test/rules.test.js @@ -0,0 +1,245 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +test('AtRules and Rules start on a new line', () => { + let actual = format(` + selector { property: value; } + @media (min-width: 1000px) { + selector { property: value; } + } + selector { property: value; } + @layer test { + selector { property: value; } + } + `); + let expected = `selector { + property: value; +} + +@media (min-width: 1000px) { + selector { + property: value; + } +} + +selector { + property: value; +} + +@layer test { + selector { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('An empty line is rendered in between Rules', () => { + let actual = format(` + rule1 { property: value } + rule2 { property: value } + `); + let expected = `rule1 { + property: value; +} + +rule2 { + property: value; +}`; + expect(actual).toEqual(expected); +}); +test('single empty line after a rule, before atrule', () => { + let actual = format(` + rule1 { property: value } + @media (min-width: 1000px) { + rule2 { property: value } + } + `); + let expected = `rule1 { + property: value; +} + +@media (min-width: 1000px) { + rule2 { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('newline between last declaration and nested ruleset', () => { + let actual = format(` + test { + property1: value1; + & > item { + property2: value2; + & + another { + property3: value3; + } + } + } + `); + let expected = `test { + property1: value1; + + & > item { + property2: value2; + + & + another { + property3: value3; + } + } +}`; + expect(actual).toEqual(expected); +}); +test('newline between last declaration and nested atrule', () => { + let actual = format(` + test { + property1: value1; + @media all { + property2: value2; + } + } + `); + let expected = `test { + property1: value1; + + @media all { + property2: value2; + } +}`; + expect(actual).toEqual(expected); +}); +test('no trailing newline on empty nested rule', () => { + let actual = format(` + @layer test { + empty {} + } + `); + let expected = `@layer test { + empty {} +}`; + expect(actual).toEqual(expected); +}); +test('formats nested rules with selectors starting with', () => { + let actual = format(` + selector { + & > item { + property: value; + } + } + `); + let expected = `selector { + & > item { + property: value; + } +}`; + expect(actual).toEqual(expected); +}); +test('newlines between declarations, nested rules and more declarations', () => { + let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`); + let expected = `a { + font: 0/0; + + & b { + color: red; + } + color: green; +}`; + expect(actual).toEqual(expected); +}); +test('formats nested rules with a selector starting with &', () => { + let actual = format(` + selector { + & a { color: red; } + } + `); + let expected = `selector { + & a { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('formats unknown stuff in curly braces', () => { + let actual = format(` + selector { + { color: red; } + } + `); + let expected = `selector { + { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('Relaxed nesting: formats nested rules with a selector with a &', () => { + let actual = format(` + selector { + a & { color:red } + } + `); + let expected = `selector { + a & { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('Relaxed nesting: formats nested rules with a selector with a &', () => { + let actual = format(` + selector { + a & { color:red } + } + `); + let expected = `selector { + a & { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('Relaxed nesting: formats nested rules with a selector without a &', () => { + let actual = format(` + selector { + a { color:red } + } + `); + let expected = `selector { + a { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { + let actual = format(` + selector { + > a { color:red } + ~ a { color:red } + + a { color:red } + } + `); + let expected = `selector { + > a { + color: red; + } + + ~ a { + color: red; + } + + + a { + color: red; + } +}`; + expect(actual).toEqual(expected); +}); +test('handles syntax errors: unclosed block', () => { + let actual = format(`a { mumblejumble`); + let expected = 'a {}'; + expect(actual).toEqual(expected); +}); +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.js new file mode 100644 index 0000000..8cd9017 --- /dev/null +++ b/test/selectors.test.js @@ -0,0 +1,236 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +test('A single selector is rendered without a trailing comma', () => { + let actual = format('a {}'); + let expected = 'a {}'; + expect(actual).toEqual(expected); +}); +test('Multiple selectors are placed on a new line, separated by commas', () => { + let actual = format(` + selector1, + selector1a, + selector1b, + selector1aa, + selector2, + + selector3 { + } + `); + let expected = `selector1, +selector1a, +selector1b, +selector1aa, +selector2, +selector3 {}`; + expect(actual).toEqual(expected); +}); +test('formats multiline selectors on a single line', () => { + let actual = format(` +a.b + .c .d + .e .f { +color: green } + `); + let expected = `a.b .c .d .e .f { + color: green; +}`; + expect(actual).toEqual(expected); +}); +test('formats simple selector combinators', () => { + let actual = format(` + a>b, + a>b~c d, + .article-content ol li>* {} + `); + let expected = `a > b, +a > b ~ c d, +.article-content ol li > * {}`; + expect(actual).toEqual(expected); +}); +test('lowercases type selectors', () => { + let actual = format(` + A, + B, + C {} + `); + let expected = `a, +b, +c {}`; + expect(actual).toEqual(expected); +}); +test('formats nested selector combinators', () => { + let fixtures = [ + [`:where(a+b) {}`, `:where(a + b) {}`], + [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], + [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], + [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); +test('formats pseudo selectors', () => { + let css = ` + a::before, + a::after, + b:before, + b:after, + c::first-letter {} + `; + let expected = `a::before, +a::after, +b::before, +b::after, +c::first-letter {}`; + let actual = format(css); + expect(actual).toEqual(expected); +}); +test('formats pseudo elements with odd casing', () => { + let css = ` + a::Before, + a::After, + b:Before, + b:After, + c:After, + d::First-letter {} + `; + let expected = `a::before, +a::after, +b::before, +b::after, +c::after, +d::first-letter {}`; + let actual = format(css); + expect(actual).toEqual(expected); +}); +test.each([ + [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], +])('formats nth selector: %s', (css, expected) => { + let actual = format(css); + expect(actual).toEqual(expected); +}); +test.each([ + [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], +])('minifies nth selector: %s', (css, expected) => { + let actual = format(css, { minify: true }); + expect(actual).toEqual(expected); +}); +test('formats multiline selectors', () => { + let actual = format(` + a:is( + a, + b, + c + ) {} + `); + let expected = `a:is(a, b, c) {}`; + expect(actual).toEqual(expected); +}); +test('format nesting selectors', () => { + let actual = format(` + & a {} + b & c {} + `); + let expected = `& a {} + +b & c {}`; + expect(actual).toEqual(expected); +}); +test('prints all possible attribute selectors', () => { + let actual = format(` + [title="test"], + [title|="test"], + [title^="test"], + [title*="test"], + [title$="test"], + [title~="test"] {} + `); + let expected = `[title="test"], +[title|="test"], +[title^="test"], +[title*="test"], +[title$="test"], +[title~="test"] {}`; + expect(actual).toEqual(expected); +}); +test('forces attribute selectors to have quoted values', () => { + let actual = format(` + [title=foo], + [title="bar"], + [title='baz'] {} + `); + let expected = `[title="foo"], +[title="bar"], +[title="baz"] {}`; + expect(actual).toEqual(expected); +}); +test('adds a space before attribute selector flags', () => { + let actual = format(` + [title="foo" i], + [title="baz"i], + [title=foo S] {} + `); + let expected = `[title="foo" i], +[title="baz" i], +[title="foo" s] {}`; + 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() {}`; + expect(actual).toEqual(expected); +}); +test(`formats ::highlight and ::highlight(Name) correctly`, () => { + let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`); + let expected = `::highlight, +::highlight(Name), +::highlight(my-thing) {}`; + expect(actual).toEqual(expected); +}); +test('formats keyframes selectors (50%) correctly', () => { + let actual = format(`@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`); + let expected = `@keyframes Toastify__bounceInUp { + 0% { + animation-timing-function: cubic-bezier(.215, .61, .355, 1); + } + + 50% {} + + 80%, + to {} +}`; + expect(actual).toBe(expected); +}); +test('formats unknown pseudos correctly', () => { + let actual = format(` + ::foo-bar, + :unkown-thing(), + :unnowkn(kjsa.asddk,asd) {} + `); + let expected = `::foo-bar, +:unkown-thing(), +:unnowkn(kjsa.asddk, asd) {}`; + expect(actual).toEqual(expected); +}); +test('handles syntax errors', () => { + let actual = format(` + test, + @test {} + `); + let expected = `test {}`; + expect(actual).toEqual(expected); +}); diff --git a/test/tab-size.test.js b/test/tab-size.test.js new file mode 100644 index 0000000..6eb2a23 --- /dev/null +++ b/test/tab-size.test.js @@ -0,0 +1,44 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +let fixture = ` + selector { + color: red; + } +`; +test('tab_size: 2', () => { + let actual = format(` + selector { + color: red; + } + + @media (min-width: 100px) { + selector { + color: blue; + } + } + `, { tab_size: 2 }); + let expected = `selector { + color: red; +} + +@media (min-width: 100px) { + selector { + color: blue; + } +}`; + expect(actual).toEqual(expected); +}); +test('invalid tab_size: 0', () => { + expect(() => format(fixture, { tab_size: 0 })).toThrow(); +}); +test('invalid tab_size: negative', () => { + expect(() => format(fixture, { tab_size: -1 })).toThrow(); +}); +test('combine tab_size and minify', () => { + let actual = format(fixture, { + tab_size: 2, + minify: true, + }); + let expected = `selector{color:red}`; + expect(actual).toEqual(expected); +}); diff --git a/test/values.test.js b/test/values.test.js new file mode 100644 index 0000000..4388212 --- /dev/null +++ b/test/values.test.js @@ -0,0 +1,313 @@ +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; +test('collapses abundant whitespace', () => { + let actual = format(`a { + transition: all 100ms ease; + color: rgb( 0 , 0 , 0 ); + color: red ; + }`); + let expected = `a { + transition: all 100ms ease; + color: rgb(0, 0, 0); + color: red; +}`; + expect(actual).toEqual(expected); +}); +test('formats simple value lists', () => { + let actual = format(` + a { + transition-property: all,opacity; + transition: all 100ms ease,opacity 10ms 20ms linear; + ANIMATION: COLOR 123MS EASE-OUT; + color: rgb(0,0,0); + color: HSL(0%,10%,50%); + content: 'Test'; + background-image: url("EXAMPLE.COM"); + } + `); + let expected = `a { + transition-property: all, opacity; + transition: all 100ms ease, opacity 10ms 20ms linear; + animation: COLOR 123ms EASE-OUT; + color: rgb(0, 0, 0); + color: hsl(0%, 10%, 50%); + content: "Test"; + background-image: url("EXAMPLE.COM"); +}`; + expect(actual).toEqual(expected); +}); +test('formats nested value lists', () => { + let actual = format(` + a { + background: red,linear-gradient(to bottom,red 10%,green 50%,blue 100%); + } + `); + let expected = `a { + background: red, linear-gradient(to bottom, red 10%, green 50%, blue 100%); +}`; + expect(actual).toEqual(expected); +}); +test('formats nested var()', () => { + let actual = format(` + a { + color: var(--test1,var(--test2,green)); + color: var(--test3,rgb(0,0,0)); + } + `); + let expected = `a { + color: var(--test1, var(--test2, green)); + color: var(--test3, rgb(0, 0, 0)); +}`; + expect(actual).toEqual(expected); +}); +test('formats multiline values on a single line', () => { + let actual = format(` +a { + background: linear-gradient( + red, + 10% blue, +20% green,100% yellow); + color: rgb( + 0, + 0, + 0 + ); +} + `); + let expected = `a { + background: linear-gradient(red, 10% blue, 20% green, 100% yellow); + color: rgb(0, 0, 0); +}`; + expect(actual).toEqual(expected); +}); +test('does not break font shorthand', () => { + let actual = format(`a { + font: 2em/2 sans-serif; + font: 2em/ 2 sans-serif; + font: 2em / 2 sans-serif; + }`); + let expected = `a { + font: 2em/2 sans-serif; + font: 2em/2 sans-serif; + font: 2em/2 sans-serif; +}`; + expect(actual).toEqual(expected); +}); +test('formats whitespace around operators (*/+-) correctly', () => { + let actual = format(`a { + font: 2em/2 sans-serif; + font-size: calc(2em/2); + font-size: calc(2em * 2); + font-size: calc(2em + 2px); + font-size: calc(2em - 2px); +}`); + let expected = `a { + font: 2em/2 sans-serif; + font-size: calc(2em / 2); + font-size: calc(2em * 2); + font-size: calc(2em + 2px); + font-size: calc(2em - 2px); +}`; + expect(actual).toEqual(expected); +}); +test('formats whitespace around operators (*/+-) correctly in nested parenthesis', () => { + let actual = format(`a { + width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); +}`); + let expected = `a { + width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); + width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); +}`; + expect(actual).toEqual(expected); +}); +test('formats parenthesis correctly', () => { + let actual = format(`a { + width: calc(100% - var(--x)); + width: calc((100% - var(--x))); + width: calc(100% - (var(--x))); + width: calc((100% - (var(--x)))); +}`); + let expected = `a { + width: calc(100% - var(--x)); + width: calc((100% - var(--x))); + width: calc(100% - (var(--x))); + width: calc((100% - (var(--x)))); +}`; + expect(actual).toEqual(expected); +}); +test('does not lowercase grid-area names', () => { + let actual = format(`a { grid-area: emailInputBox; }`); + let expected = `a { + grid-area: emailInputBox; +}`; + expect(actual).toEqual(expected); +}); +test('does not lowercase custom properties in var()', () => { + let actual = format(`a { color: var(--MyColor); }`); + let expected = `a { + color: var(--MyColor); +}`; + expect(actual).toEqual(expected); +}); +test('lowercases CSS functions', () => { + let actual = format(`a { + color: RGB(0, 0, 0); + transform: translateX(100px); + }`); + let expected = `a { + color: rgb(0, 0, 0); + transform: translatex(100px); +}`; + expect(actual).toEqual(expected); +}); +test('relative colors', () => { + let actual = format(`a { + color: rgb( from red 0 0 255); + color: rgb( from rgb( 200 0 0 ) r r r ) ; + color: hwb( from var( --base-color ) h w b / var( --standard-opacity ) ) ; + color: lch(from var(--base-color) calc(l + 20) c h); + }`); + let expected = `a { + color: rgb(from red 0 0 255); + color: rgb(from rgb(200 0 0) r r r); + color: hwb(from var(--base-color) h w b / var(--standard-opacity)); + color: lch(from var(--base-color) calc(l + 20) c h); +}`; + expect(actual).toEqual(expected); +}); +test('does not change casing of `NaN`', () => { + let actual = format(`a { + height: calc(1 * NaN); + }`); + let expected = `a { + height: calc(1 * NaN); +}`; + expect(actual).toEqual(expected); +}); +test('does not change casing of URLs', () => { + let actual = format(`a { + background-image: url("My-Url.png"); + }`); + let expected = `a { + background-image: url("My-Url.png"); +}`; + expect(actual).toEqual(expected); +}); +test('lowercases dimensions', () => { + let actual = format(`a { + font-size: 12PX; + width: var(--test, 33REM); + }`); + let expected = `a { + font-size: 12px; + width: var(--test, 33rem); +}`; + expect(actual).toEqual(expected); +}); +test('formats unknown content in value', () => { + let actual = format(`a { + content: 'Test' counter(page); + }`); + let expected = `a { + content: "Test" counter(page); +}`; + expect(actual).toEqual(expected); +}); +test('does not break space toggles', () => { + let actual = format(`a { + --ON: initial; + --OFF: ; + }`); + let expected = `a { + --ON: initial; + --OFF: ; +}`; + expect(actual).toEqual(expected); +}); +test('does not break space toggles (minified)', () => { + let actual = format(`a { + --ON: initial; + --OFF: ; + }`, { minify: true }); + let expected = `a{--ON:initial;--OFF: }`; + expect(actual).toEqual(expected); +}); +test('adds quotes around strings in url()', () => { + let actual = format(`a { + background-image: url("star.gif"); + list-style-image: url('../images/bullet.jpg'); + content: url("pdficon.jpg"); + cursor: url(mycursor.cur); + border-image-source: url(/media/diamonds.png); + src: url('fantasticfont.woff'); + offset-path: url(#path); + mask-image: url("masks.svg#mask1"); + }`); + let expected = `a { + background-image: url("star.gif"); + list-style-image: url("../images/bullet.jpg"); + content: url("pdficon.jpg"); + cursor: url("mycursor.cur"); + border-image-source: url("/media/diamonds.png"); + src: url("fantasticfont.woff"); + offset-path: url("#path"); + mask-image: url("masks.svg#mask1"); +}`; + expect(actual).toEqual(expected); +}); +test.each([ + `data:image/svg+xml;utf8,`, + `data:image/svg+xml;utf8,`, +])('Does not mess up URLs with inlined SVG', (input) => { + let actual = format(`test { + background-image: url('${input}'); + background-image: url(${input}); + }`); + let expected = `test { + background-image: url(${input}); + background-image: url(${input}); +}`; + expect(actual).toEqual(expected); +}); +test.each([ + // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data + 'data:,Hello%2C%20World%21', + 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', + 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', + 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', + // from https://github.com/projectwallace/format-css/issues/144 + `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, +])('Does not mess up URLs with encoded inlined content: %s', (input) => { + let actual = format(`test { + background-image: url(${input}); + }`); + let expected = `test { + background-image: url(${input}); +}`; + expect(actual).toBe(expected); +}); +test.each([ + `U+26`, // single code point + `U+0-7F`, + `U+0025-00FF`, // code point range + `U+4??`, // wildcard range + `U+0025-00FF, U+4??`, // multiple values +])('Formats unicode-range: %s', (unicode_range) => { + let actual = format(`test { unicode-range: ${unicode_range}; }`); + let expected = `test { + unicode-range: ${unicode_range}; +}`; + expect(actual).toBe(expected); +}); +test('formats multi-value unicode range', () => { + let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`); + let expected = `test { + unicode-range: U+0025-00FF, U+4??; +}`; + expect(actual).toBe(expected); +}); diff --git a/tsconfig.json b/tsconfig.json index 0ed86e1..70de516 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,26 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "declaration": true, - "rootDirs": ["src/lib", "src/cli"], - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + "types": ["node"], + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "rootDir": "src", + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] } diff --git a/tsdown.config.js b/tsdown.config.js new file mode 100644 index 0000000..5e421b3 --- /dev/null +++ b/tsdown.config.js @@ -0,0 +1,32 @@ +import { defineConfig } from 'tsdown'; +import { codecovRollupPlugin } from '@codecov/rollup-plugin'; +export default defineConfig([ + { + entry: 'src/lib/index.ts', + platform: 'neutral', + publint: true, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCss', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, + { + entry: 'src/cli/cli.ts', + platform: 'node', + dts: false, + // Reference the lib via its package name to avoid bundling it twice + deps: { + neverBundle: ['@projectwallace/format-css'], + }, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCssCli', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, +]); diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 0000000..cd426a2 --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config'; +import { resolve } from 'node:path'; +export default defineConfig({ + resolve: { + alias: { + '@projectwallace/format-css': resolve('./src/lib/index.ts'), + }, + }, + test: { + coverage: { + provider: 'v8', + }, + }, +}); From a564c2a8f09a6f6851682ad02510d7dfcf211295 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 12:05:36 +0200 Subject: [PATCH 3/7] fix formatting --- test/api.test.js | 44 ++-- test/atrules.test.js | 408 +++++++++++++++---------------- test/comments.test.js | 499 +++++++++++++++++++------------------- test/declarations.test.js | 78 +++--- test/minify.test.js | 106 ++++---- test/rules.test.js | 186 +++++++------- test/selectors.test.js | 262 ++++++++++---------- test/tab-size.test.js | 43 ++-- test/values.test.js | 313 ++++++++++++------------ tsconfig.json | 48 ++-- tsdown.config.js | 62 ++--- vitest.config.js | 26 +- 12 files changed, 1045 insertions(+), 1030 deletions(-) diff --git a/test/api.test.js b/test/api.test.js index 1cf0bcc..97f2b7c 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,17 +1,17 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' test('empty input', () => { - let actual = format(``); - let expected = ``; - expect(actual).toEqual(expected); -}); + let actual = format(``) + let expected = `` + expect(actual).toEqual(expected) +}) test('handles invalid input', () => { - let actual = format(`;`); - let expected = ``; - expect(actual).toEqual(expected); -}); + let actual = format(`;`) + let expected = `` + expect(actual).toEqual(expected) +}) test('Vadim Makeevs example works', () => { - let actual = format(` + let actual = format(` @layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @@ -23,8 +23,8 @@ test('Vadim Makeevs example works', () => { } } } - `); - let expected = `@layer what { + `) + let expected = `@layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @media (height > 0) { @@ -34,12 +34,14 @@ test('Vadim Makeevs example works', () => { } } } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('minified Vadims example', () => { - let actual = format(`@layer what{@container (width>0){@media (min-height:.001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}`); - let expected = `@layer what { + let actual = format( + `@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 { @@ -47,6 +49,6 @@ test('minified Vadims example', () => { } } } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) diff --git a/test/atrules.test.js b/test/atrules.test.js index c580233..d3406cc 100644 --- a/test/atrules.test.js +++ b/test/atrules.test.js @@ -1,15 +1,15 @@ -import { test, expect } from 'vitest'; -import { format, minify } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format, minify } from '../src/lib/index.js' test('AtRules start on a new line', () => { - let actual = format(` + let actual = format(` @media (min-width: 1000px) { selector { property: value; } } @layer test { selector { property: value; } } - `); - let expected = `@media (min-width: 1000px) { + `) + let expected = `@media (min-width: 1000px) { selector { property: value; } @@ -19,11 +19,11 @@ test('AtRules start on a new line', () => { selector { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('Atrule blocks are surrounded by {} with correct spacing and indentation', () => { - let actual = format(` + let actual = format(` @media (min-width:1000px){selector{property:value1}} @media (min-width:1000px) @@ -32,8 +32,8 @@ test('Atrule blocks are surrounded by {} with correct spacing and indentation', { property:value2 } -}`); - let expected = `@media (min-width: 1000px) { +}`) + let expected = `@media (min-width: 1000px) { selector { property: value1; } @@ -43,115 +43,115 @@ test('Atrule blocks are surrounded by {} with correct spacing and indentation', selector { property: value2; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('adds whitespace between prelude and {', () => { - let actual = format(`@media all{}`); - let expected = `@media all {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@media all{}`) + let expected = `@media all {}` + 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) {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@media all and (min-width: 1000px) {}`) + let expected = `@media all and (min-width: 1000px) {}` + expect(actual).toEqual(expected) +}) test('removes newlines in prelude', () => { - let actual = format(`@media + let actual = format(`@media all, screen, print, - (min-width: 1000px) {}`); - let expected = `@media all, screen, print, (min-width: 1000px) {}`; - expect(actual).toEqual(expected); -}); + (min-width: 1000px) {}`) + let expected = `@media all, screen, print, (min-width: 1000px) {}` + 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) {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@media (min-width:1000px) {}`) + let expected = `@media (min-width: 1000px) {}` + 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) {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@media (min-width : 1000px) {}`) + let expected = `@media (min-width: 1000px) {}` + expect(actual).toEqual(expected) +}) test('formats @layer with excess whitespace', () => { - let actual = format(`@layer test;`); - let expected = `@layer test;`; - expect(actual).toEqual(expected); -}); + let actual = format(`@layer test;`) + let expected = `@layer test;` + expect(actual).toEqual(expected) +}) test('adds whitespace to @layer tbody,thead', () => { - let actual = format(`@layer tbody,thead;`); - let expected = `@layer tbody, thead;`; - expect(actual).toEqual(expected); -}); + let actual = format(`@layer tbody,thead;`) + let expected = `@layer tbody, thead;` + expect(actual).toEqual(expected) +}) test('adds whitespace to @supports (display:grid)', () => { - let actual = format(`@supports (display:grid){}`); - let expected = `@supports (display: grid) {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@supports (display:grid){}`) + let expected = `@supports (display: grid) {}` + expect(actual).toEqual(expected) +}) test('@media prelude formatting', () => { - let fixtures = [ - [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], - [ - `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, - `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, - ], - [ - `@media (min-width: 1024px)or (max-width: 1439px) {}`, - `@media (min-width: 1024px) or (max-width: 1439px) {}`, - ], - [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], - [ - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - ], - [`@media screen or print {}`, `@media screen or print {}`], - [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [ - `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, - `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, - ], - [ - `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, - `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, - ], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); + let fixtures = [ + [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], + [ + `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, + `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, + ], + [ + `@media (min-width: 1024px)or (max-width: 1439px) {}`, + `@media (min-width: 1024px) or (max-width: 1439px) {}`, + ], + [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], + [ + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + ], + [`@media screen or print {}`, `@media screen or print {}`], + [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [ + `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, + `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, + ], + [ + `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, + `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, + ], + ] + for (let [css, expected] of fixtures) { + let actual = format(css) + expect(actual).toEqual(expected) + } +}) test('lowercases functions inside atrule preludes', () => { - let actual = format(` + let actual = format(` @import URL("style.css") LAYER(test) SUPPORTS(display:grid); @supports SELECTOR([popover]:open) {} -`); - let expected = `@import url("style.css") layer(test) supports(display: grid); +`) + let expected = `@import url("style.css") layer(test) supports(display: grid); -@supports selector([popover]:open) {}`; - expect(actual).toEqual(expected); -}); +@supports selector([popover]:open) {}` + expect(actual).toEqual(expected) +}) test('formats @scope', () => { - let actual = format(` + let actual = format(` @scope (.light-scheme) {} @scope (.media-object) to (.content > *) {} -`); - let expected = `@scope (.light-scheme) {} +`) + let expected = `@scope (.light-scheme) {} -@scope (.media-object) to (.content > *) {}`; - expect(actual).toEqual(expected); -}); +@scope (.media-object) to (.content > *) {}` + expect(actual).toEqual(expected) +}) test('calc() inside @media', () => { - let actual = format(` + let actual = format(` @media (min-width: calc(1px*1)) {} @media (min-width: calc(2px* 2)) {} @media (min-width: calc(3px *3)) {} @media (min-width: calc(4px * 4)) {} @media (min-width: calc(5px * 5)) {} - `); - let expected = `@media (min-width: calc(1px * 1)) {} + `) + let expected = `@media (min-width: calc(1px * 1)) {} @media (min-width: calc(2px * 2)) {} @@ -159,77 +159,77 @@ test('calc() inside @media', () => { @media (min-width: calc(4px * 4)) {} -@media (min-width: calc(5px * 5)) {}`; - expect(actual).toEqual(expected); -}); +@media (min-width: calc(5px * 5)) {}` + 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)){}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@media (min-width: calc(1px*1)) {}`) + let expected = `@media (min-width:calc(1px*1)){}` + 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)){}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@media (min-width: calc(1px + 1em)) {}`) + let expected = `@media (min-width:calc(1px + 1em)){}` + 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)){}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@media (min-width: calc(1em - 1px)) {}`) + let expected = `@media (min-width:calc(1em - 1px)){}` + 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);', - ], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); + 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);', + ], + ] + for (let [css, expected] of fixtures) { + let actual = format(css) + expect(actual).toEqual(expected) + } +}) test('@supports prelude formatting', () => { - let fixtures = [ - [`@supports (display:grid){}`, `@supports (display: grid) {}`], - [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], - ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); + let fixtures = [ + [`@supports (display:grid){}`, `@supports (display: grid) {}`], + [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], + ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], + ] + for (let [css, expected] of fixtures) { + let actual = format(css) + expect(actual).toEqual(expected) + } +}) test('@layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody, thead;`], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody, thead;`], + ] + for (let [css, expected] of fixtures) { + let actual = format(css) + expect(actual).toEqual(expected) + } +}) test('minify: @layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody,thead;`], - ]; - for (let [css, expected] of fixtures) { - let actual = minify(css); - expect(actual).toEqual(expected); - } -}); + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody,thead;`], + ] + for (let [css, expected] of fixtures) { + let actual = minify(css) + expect(actual).toEqual(expected) + } +}) test('single empty line after a rule, before atrule', () => { - let actual = format(` + let actual = format(` rule1 { property: value } @media (min-width: 1000px) { rule2 { property: value } } - `); - let expected = `rule1 { + `) + let expected = `rule1 { property: value; } @@ -237,83 +237,83 @@ test('single empty line after a rule, before atrule', () => { rule2 { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('single empty line in between atrules', () => { - let actual = format(` + let actual = format(` @layer test1; @media (min-width: 1000px) { rule2 { property: value } } - `); - let expected = `@layer test1; + `) + let expected = `@layer test1; @media (min-width: 1000px) { rule2 { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('newline between last declaration and nested atrule', () => { - let actual = format(` + let actual = format(` test { property1: value1; @media all { property2: value2; } } - `); - let expected = `test { + `) + let expected = `test { property1: value1; @media all { property2: value2; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('lowercases the atrule name', () => { - let actual = format(`@LAYER test {}`); - let expected = `@layer test {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`@LAYER test {}`) + let expected = `@layer test {}` + expect(actual).toEqual(expected) +}) test('does not lowercase the atrule value', () => { - let actual = format('@keyframes TEST {}'); - let expected = '@keyframes TEST {}'; - expect(actual).toEqual(expected); -}); + let actual = format('@keyframes TEST {}') + let expected = '@keyframes TEST {}' + expect(actual).toEqual(expected) +}) test('Atrules w/o Block are terminated with a semicolon', () => { - let actual = format(` + let actual = format(` @layer test; @import url('test'); - `); - let expected = `@layer test; + `) + let expected = `@layer test; -@import url('test');`; - expect(actual).toEqual(expected); -}); +@import url('test');` + expect(actual).toEqual(expected) +}) test('Empty atrule braces are placed on the same line', () => { - let actual = format(`@media all { + let actual = format(`@media all { } - @supports (display: grid) {}`); - let expected = `@media all {} + @supports (display: grid) {}`) + let expected = `@media all {} -@supports (display: grid) {}`; - expect(actual).toEqual(expected); -}); +@supports (display: grid) {}` + expect(actual).toEqual(expected) +}) test('new-fangled comparators (width > 1000px)', () => { - let actual = format(` + let actual = format(` @container (width>1000px) {} @media (width>1000px) {} @media (width=>1000px) {} @media (width<=1000px) {} @media (200px 1000px) {} + `) + let expected = `@container (width > 1000px) {} @media (width > 1000px) {} @@ -321,23 +321,23 @@ test('new-fangled comparators (width > 1000px)', () => { @media (width <= 1000px) {} -@media (200px < width < 1000px) {}`; - expect(actual).toEqual(expected); -}); +@media (200px < width < 1000px) {}` + expect(actual).toEqual(expected) +}) test('minify: new-fangled comparators (width > 1000px)', () => { - let actual = minify(`@container (width>1000px) {}`); - let expected = `@container (width>1000px){}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@container (width>1000px) {}`) + let expected = `@container (width>1000px){}` + expect(actual).toEqual(expected) +}) test.skip('preserves comments', () => { - let actual = format(` + let actual = format(` @media /* comment */ all {} @media all /* comment */ {} @media (min-width: 1000px /* comment */) {} @media (/* comment */ min-width: 1000px) {} @layer /* comment */ {} - `); - let expected = `@media /* comment */ all {} + `) + let expected = `@media /* comment */ all {} @media all /* comment */ {} @@ -346,6 +346,6 @@ test.skip('preserves comments', () => { @media (/* comment */ min-width: 1000px) {} @layer /* comment */ {} -`; - expect(actual).toEqual(expected); -}); +` + expect(actual).toEqual(expected) +}) diff --git a/test/comments.test.js b/test/comments.test.js index c953e93..13ec3bc 100644 --- a/test/comments.test.js +++ b/test/comments.test.js @@ -1,37 +1,37 @@ -import { describe, test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { describe, test, expect } from 'vitest' +import { format } from '../src/lib/index.js' describe('comments', () => { - test('only comment', () => { - let actual = format(`/* comment */`); - let expected = `/* comment */`; - expect(actual).toEqual(expected); - }); - test('bang comment before rule', () => { - let actual = format(` + test('only comment', () => { + let actual = format(`/* comment */`) + let expected = `/* comment */` + expect(actual).toEqual(expected) + }) + test('bang comment before rule', () => { + let actual = format(` /*! comment */ selector {} - `); - let expected = `/*! comment */ -selector {}`; - expect(actual).toEqual(expected); - }); - test('before selectors', () => { - let actual = format(` + `) + let expected = `/*! comment */ +selector {}` + expect(actual).toEqual(expected) + }) + test('before selectors', () => { + let actual = format(` /* comment */ selector1, selector2 { property: value; } - `); - let expected = `/* comment */ + `) + let expected = `/* comment */ selector1, selector2 { property: value; -}`; - expect(actual).toEqual(expected); - }); - test('before nested selectors', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('before nested selectors', () => { + let actual = format(` a { /* comment */ & nested1, @@ -39,48 +39,48 @@ selector2 { property: value; } } - `); - let expected = `a { + `) + let expected = `a { /* comment */ & nested1, & nested2 { property: value; } -}`; - expect(actual).toEqual(expected); - }); - test('after selectors', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('after selectors', () => { + let actual = format(` selector1, selector2 /* comment */ { property: value; } - `); - let expected = `selector1, + `) + let expected = `selector1, selector2 /* comment */ { property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between selectors', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('in between selectors', () => { + let actual = format(` selector1, /* comment */ selector2 { property: value; } - `); - let expected = `selector1, + `) + let expected = `selector1, /* comment */ selector2 { property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between nested selectors', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('in between nested selectors', () => { + let actual = format(` a { & nested1, /* comment */ @@ -88,100 +88,100 @@ selector2 { property: value; } } - `); - let expected = `a { + `) + let expected = `a { & nested1, /* comment */ & nested2 { property: value; } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in rule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as first child in rule', () => { + let actual = format(` selector { /* comment */ property: value; } - `); - let expected = `selector { + `) + let expected = `selector { /* comment */ property: value; -}`; - expect(actual).toEqual(expected); - }); - test('as last child in rule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as last child in rule', () => { + let actual = format(` selector { property: value; /* comment */ } - `); - let expected = `selector { + `) + let expected = `selector { property: value; /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as last child in nested rule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as last child in nested rule', () => { + let actual = format(` a { & selector { property: value; /* comment */ } } - `); - let expected = `a { + `) + let expected = `a { & selector { property: value; /* comment */ } -}`; - expect(actual).toEqual(expected); - }); - test('as only child in rule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as only child in rule', () => { + let actual = format(` selector { /* comment */ } - `); - let expected = `selector { + `) + let expected = `selector { /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as only child in nested rule', () => { - let actual = format(`a { +}` + expect(actual).toEqual(expected) + }) + test('as only child in nested rule', () => { + let actual = format(`a { & selector { /* comment */ } -}`); - let expected = `a { +}`) + let expected = `a { & selector { /* comment */ } -}`; - expect(actual).toEqual(expected); - }); - test('in between declarations', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('in between declarations', () => { + let actual = format(` selector { property: value; /* comment */ property: value; } - `); - let expected = `selector { + `) + let expected = `selector { property: value; /* comment */ property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between nested declarations', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('in between nested declarations', () => { + let actual = format(` a { & selector { property: value; @@ -189,35 +189,35 @@ selector2 { property: value; } } - `); - let expected = `a { + `) + let expected = `a { & selector { property: value; /* comment */ property: value; } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as first child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { /* comment */ selector { property: value; } } - `); - let expected = `@media (min-width: 1000px) { + `) + let expected = `@media (min-width: 1000px) { /* comment */ selector { property: value; } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in nested atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as first child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { /* comment */ @@ -226,36 +226,36 @@ selector2 { } } } - `); - let expected = `@media all { + `) + let expected = `@media all { @media (min-width: 1000px) { /* comment */ selector { property: value; } } -}`; - expect(actual).toEqual(expected); - }); - test('as last child in atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as last child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { selector { property: value; } /* comment */ } - `); - let expected = `@media (min-width: 1000px) { + `) + let expected = `@media (min-width: 1000px) { selector { property: value; } /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as last child in nested atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as last child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { selector { @@ -264,45 +264,45 @@ selector2 { /* comment */ } } - `); - let expected = `@media all { + `) + let expected = `@media all { @media (min-width: 1000px) { selector { property: value; } /* comment */ } -}`; - expect(actual).toEqual(expected); - }); - test('as only child in atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as only child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { /* comment */ } - `); - let expected = `@media (min-width: 1000px) { + `) + let expected = `@media (min-width: 1000px) { /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as only child in nested atrule', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('as only child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { /* comment */ } } - `); - let expected = `@media all { + `) + let expected = `@media all { @media (min-width: 1000px) { /* comment */ } -}`; - expect(actual).toEqual(expected); - }); - test('in between rules and atrules', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('in between rules and atrules', () => { + let actual = format(` /* comment 1 */ selector {} /* comment 2 */ @@ -312,8 +312,8 @@ selector2 { /* comment 4 */ } /* comment 5 */ - `); - let expected = `/* comment 1 */ + `) + let expected = `/* comment 1 */ selector {} /* comment 2 */ @media (min-width: 1000px) { @@ -321,11 +321,11 @@ selector {} selector {} /* comment 4 */ } -/* comment 5 */`; - expect(actual).toEqual(expected); - }); - test('comment before rule and atrule should not be separated by newline', () => { - let actual = format(` +/* comment 5 */` + expect(actual).toEqual(expected) + }) + test('comment before rule and atrule should not be separated by newline', () => { + let actual = format(` /* comment 1 */ selector {} @@ -336,19 +336,19 @@ selector {} selector {} /* comment 4 */ } - `); - let expected = `/* comment 1 */ + `) + let expected = `/* comment 1 */ selector {} /* comment 2 */ @media (min-width: 1000px) { /* comment 3 */ selector {} /* comment 4 */ -}`; - expect(actual).toEqual(expected); - }); - test('a declaration after multiple comments starts on a new line', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('a declaration after multiple comments starts on a new line', () => { + let actual = format(` selector { /* comment 1 */ /* comment 2 */ @@ -362,8 +362,8 @@ selector {} /* comment 6 */ --custom-property: value; } - `); - let expected = `selector { + `) + let expected = `selector { /* comment 1 */ /* comment 2 */ --custom-property: value; @@ -373,11 +373,11 @@ selector {} /* comment 5 */ /* comment 6 */ --custom-property: value; -}`; - expect(actual).toEqual(expected); - }); - test('multiple comments in between rules and atrules', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('multiple comments in between rules and atrules', () => { + let actual = format(` /* comment 1 */ /* comment 1.1 */ selector {} @@ -392,8 +392,8 @@ selector {} } /* comment 5 */ /* comment 5.1 */ - `); - let expected = `/* comment 1 */ + `) + let expected = `/* comment 1 */ /* comment 1.1 */ selector {} /* comment 2 */ @@ -406,109 +406,110 @@ selector {} /* comment 4.1 */ } /* comment 5 */ -/* comment 5.1 */`; - expect(actual).toEqual(expected); - }); - test('puts every comment on a new line', () => { - let actual = format(` +/* comment 5.1 */` + expect(actual).toEqual(expected) + }) + test('puts every comment on a new line', () => { + let actual = format(` x { /*--font-family: inherit;*/ /*--font-style: normal;*/ --border-top-color: var(--root-color--support); } -`); - let expected = `x { +`) + let expected = `x { /*--font-family: inherit;*/ /*--font-style: normal;*/ --border-top-color: var(--root-color--support); -}`; - 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*/) {}'; - 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*/) {}'; - expect(actual).toEqual(expected); - }); - test('skip in @import prelude before specifier', () => { - let actual = format('@import /*test*/"foo";'); - let expected = '@import "foo";'; - expect(actual).toEqual(expected); - }); - test('skip in @import prelude after specifier', () => { - let actual = format('@import "foo"/*test*/;'); - let expected = '@import "foo";'; - expect(actual).toEqual(expected); - }); - test('skip in selector combinator', () => { - let actual = format(` +}` + 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*/) {}' + 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*/) {}' + expect(actual).toEqual(expected) + }) + test('skip in @import prelude before specifier', () => { + let actual = format('@import /*test*/"foo";') + let expected = '@import "foo";' + expect(actual).toEqual(expected) + }) + test('skip in @import prelude after specifier', () => { + let actual = format('@import "foo"/*test*/;') + let expected = '@import "foo";' + expect(actual).toEqual(expected) + }) + test('skip in selector combinator', () => { + let actual = format(` a/*test*/ /*test*/b, a/*test*/+/*test*/b {} - `); - let expected = `a /*test*/ /*test*/ b, -a + b {}`; - expect(actual).toEqual(expected); - }); - test('in attribute selector', () => { - let actual = format(`[/*test*/a='b' i/*test*/] {}`); - let expected = `[a="b" i] {}`; - expect(actual).toEqual(expected); - }); - test('skip in var() with fallback', () => { - let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`); - let expected = `a { + `) + let expected = `a /*test*/ /*test*/ b, +a + b {}` + expect(actual).toEqual(expected) + }) + test('in attribute selector', () => { + let actual = format(`[/*test*/a='b' i/*test*/] {}`) + let expected = `[a="b" i] {}` + expect(actual).toEqual(expected) + }) + test('skip in var() with fallback', () => { + let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`) + let expected = `a { prop: var(--name, 1); -}`; - expect(actual).toEqual(expected); - }); - test('skip in custom property declaration (space toggle)', () => { - let actual = format(`a { --test: /*test*/; }`); - let expected = `a { +}` + expect(actual).toEqual(expected) + }) + test('skip in custom property declaration (space toggle)', () => { + let actual = format(`a { --test: /*test*/; }`) + let expected = `a { --test: ; -}`; - expect(actual).toEqual(expected); - }); - test('before value', () => { - let actual = format(`a { prop: /*test*/value; }`); - let expected = `a { +}` + expect(actual).toEqual(expected) + }) + test('before value', () => { + let actual = format(`a { prop: /*test*/value; }`) + let expected = `a { prop: value; -}`; - expect(actual).toEqual(expected); - }); - test('after value', () => { - let actual = format(`a { +}` + expect(actual).toEqual(expected) + }) + test('after value', () => { + let actual = format(`a { prop: value/*test*/; - }`); - let expected = `a { + }`) + let expected = `a { prop: value; -}`; - expect(actual).toEqual(expected); - }); - test('skip in value functions', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('skip in value functions', () => { + let actual = format(` a { background-image: linear-gradient(/* comment */red, green); background-image: linear-gradient(red/* comment */, green); background-image: linear-gradient(red, green/* comment */); background-image: linear-gradient(red, green)/* comment */ } - `); - let expected = `a { + `) + let expected = `a { background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('strips comments in minification mode', () => { - let actual = format(` +}` + expect(actual).toEqual(expected) + }) + test('strips comments in minification mode', () => { + let actual = format( + ` /* comment 1 */ selector {} /* comment 2 */ @@ -518,8 +519,10 @@ a + b {}`; /* comment 4 */ } /* comment 5 */ - `, { minify: true }); - let expected = `selector{}@media (min-width:1000px){selector{}}`; - expect(actual).toEqual(expected); - }); -}); + `, + { minify: true }, + ) + let expected = `selector{}@media (min-width:1000px){selector{}}` + expect(actual).toEqual(expected) + }) +}) diff --git a/test/declarations.test.js b/test/declarations.test.js index fb56f49..5059d7c 100644 --- a/test/declarations.test.js +++ b/test/declarations.test.js @@ -1,7 +1,7 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' test('Declarations end with a semicolon (;)', () => { - let actual = format(` + let actual = format(` @font-face { src: url('test'); font-family: Test @@ -24,8 +24,8 @@ test('Declarations end with a semicolon (;)', () => { } } } - `); - let expected = `@font-face { + `) + let expected = `@font-face { src: url("test"); font-family: Test; } @@ -46,50 +46,50 @@ css { property1: value5; } } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('lowercases properties', () => { - let actual = format(`a { COLOR: green }`); - let expected = `a { + let actual = format(`a { COLOR: green }`) + let expected = `a { color: green; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not lowercase custom properties', () => { - let actual = format(`a { + let actual = format(`a { --myVar: 1; - }`); - let expected = `a { + }`) + let expected = `a { --myVar: 1; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('!important is added', () => { - let actual = format(`a { color: green !important}`); - let expected = `a { + let actual = format(`a { color: green !important}`) + let expected = `a { color: green !important; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('!important is lowercase', () => { - let actual = format(`a { color: green !IMPORTANT }`); - let expected = `a { + let actual = format(`a { color: green !IMPORTANT }`) + let expected = `a { color: green !important; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('browserhack !ie is printed', () => { - let actual = format(`a { color: green !ie}`); - let expected = `a { + let actual = format(`a { color: green !ie}`) + let expected = `a { color: green !ie; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('browserhack !IE is lowercased', () => { - let actual = format(`a { color: green !IE}`); - let expected = `a { + let actual = format(`a { color: green !IE}`) + let expected = `a { color: green !ie; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) diff --git a/test/minify.test.js b/test/minify.test.js index aea635b..ab40852 100644 --- a/test/minify.test.js +++ b/test/minify.test.js @@ -1,49 +1,49 @@ -import { test, expect } from 'vitest'; -import { minify } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { minify } from '../src/lib/index.js' test('empty rule', () => { - let actual = minify(`a {}`); - let expected = `a{}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`a {}`) + let expected = `a{}` + expect(actual).toEqual(expected) +}) test('simple declaration', () => { - let actual = minify(`:root { --color: red; }`); - let expected = `:root{--color:red}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`:root { --color: red; }`) + let expected = `:root{--color:red}` + 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}}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@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){}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`@media (min-width: 100px) {}`) + let expected = `@media (min-width:100px){}` + expect(actual).toEqual(expected) +}) test('formats multiline values on a single line', () => { - let actual = minify(` + let actual = minify(` a { background: linear-gradient( red, 10% blue, 20% green,100% yellow); } - `); - let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}`; - expect(actual).toEqual(expected); -}); + `) + let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}` + 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%)}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`a { width: calc(100% - 10px); height: calc(100 * 1%); }`) + let expected = `a{width:calc(100% - 10px);height:calc(100*1%)}` + 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)}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`a { color: rgb(0 0 0 / 0.1); }`) + let expected = `a{color:rgb(0 0 0/0.1)}` + expect(actual).toEqual(expected) +}) test('Vadim Makeevs example works', () => { - let actual = minify(` + let actual = minify(` @layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @@ -55,27 +55,29 @@ 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}}}}}`; - expect(actual).toEqual(expected); -}); + `) + 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}}}}`; - expect(actual).toEqual(expected); -}); + 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}}}}` + expect(actual).toEqual(expected) +}) test('removes whitespace before !important', () => { - let actual = minify(`a { color: green !important }`); - let expected = `a{color:green!important}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`a { color: green !important }`) + let expected = `a{color:green!important}` + expect(actual).toEqual(expected) +}) test('minifies complex selectors', () => { - let actual = minify(`:is(a, b) { color: green }`); - let expected = `:is(a,b){color:green}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`:is(a, b) { color: green }`) + let expected = `:is(a,b){color:green}` + expect(actual).toEqual(expected) +}) test('removes whitespace around non-whitespace selector combinators', () => { - let actual = minify(`a + b {} c d {}`); - let expected = `a+b{}c d{}`; - expect(actual).toEqual(expected); -}); + let actual = minify(`a + b {} c d {}`) + let expected = `a+b{}c d{}` + expect(actual).toEqual(expected) +}) diff --git a/test/rules.test.js b/test/rules.test.js index 047eb27..86c0fb8 100644 --- a/test/rules.test.js +++ b/test/rules.test.js @@ -1,7 +1,7 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' test('AtRules and Rules start on a new line', () => { - let actual = format(` + let actual = format(` selector { property: value; } @media (min-width: 1000px) { selector { property: value; } @@ -10,8 +10,8 @@ test('AtRules and Rules start on a new line', () => { @layer test { selector { property: value; } } - `); - let expected = `selector { + `) + let expected = `selector { property: value; } @@ -29,31 +29,31 @@ selector { selector { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('An empty line is rendered in between Rules', () => { - let actual = format(` + let actual = format(` rule1 { property: value } rule2 { property: value } - `); - let expected = `rule1 { + `) + let expected = `rule1 { property: value; } rule2 { property: value; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('single empty line after a rule, before atrule', () => { - let actual = format(` + let actual = format(` rule1 { property: value } @media (min-width: 1000px) { rule2 { property: value } } - `); - let expected = `rule1 { + `) + let expected = `rule1 { property: value; } @@ -61,11 +61,11 @@ test('single empty line after a rule, before atrule', () => { rule2 { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('newline between last declaration and nested ruleset', () => { - let actual = format(` + let actual = format(` test { property1: value1; & > item { @@ -75,8 +75,8 @@ test('newline between last declaration and nested ruleset', () => { } } } - `); - let expected = `test { + `) + let expected = `test { property1: value1; & > item { @@ -86,139 +86,139 @@ test('newline between last declaration and nested ruleset', () => { property3: value3; } } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('newline between last declaration and nested atrule', () => { - let actual = format(` + let actual = format(` test { property1: value1; @media all { property2: value2; } } - `); - let expected = `test { + `) + let expected = `test { property1: value1; @media all { property2: value2; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('no trailing newline on empty nested rule', () => { - let actual = format(` + let actual = format(` @layer test { empty {} } - `); - let expected = `@layer test { + `) + let expected = `@layer test { empty {} -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats nested rules with selectors starting with', () => { - let actual = format(` + let actual = format(` selector { & > item { property: value; } } - `); - let expected = `selector { + `) + let expected = `selector { & > item { property: value; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('newlines between declarations, nested rules and more declarations', () => { - let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`); - let expected = `a { + let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`) + let expected = `a { font: 0/0; & b { color: red; } color: green; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats nested rules with a selector starting with &', () => { - let actual = format(` + let actual = format(` selector { & a { color: red; } } - `); - let expected = `selector { + `) + let expected = `selector { & a { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats unknown stuff in curly braces', () => { - let actual = format(` + let actual = format(` selector { { color: red; } } - `); - let expected = `selector { + `) + let expected = `selector { { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` + let actual = format(` selector { a & { color:red } } - `); - let expected = `selector { + `) + let expected = `selector { a & { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` + let actual = format(` selector { a & { color:red } } - `); - let expected = `selector { + `) + let expected = `selector { a & { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('Relaxed nesting: formats nested rules with a selector without a &', () => { - let actual = format(` + let actual = format(` selector { a { color:red } } - `); - let expected = `selector { + `) + let expected = `selector { a { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { - let actual = format(` + let actual = format(` selector { > a { color:red } ~ a { color:red } + a { color:red } } - `); - let expected = `selector { + `) + let expected = `selector { > a { color: red; } @@ -230,16 +230,16 @@ test('Relaxed nesting: formats nested rules with a selector starting with a sele + a { color: red; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('handles syntax errors: unclosed block', () => { - let actual = format(`a { mumblejumble`); - let expected = 'a {}'; - expect(actual).toEqual(expected); -}); + let actual = format(`a { mumblejumble`) + let expected = 'a {}' + expect(actual).toEqual(expected) +}) test('handles syntax errors: premature closed block', () => { - let actual = format(`a { mumblejumble: }`); - let expected = 'a {\n\tmumblejumble: ;\n}'; - expect(actual).toEqual(expected); -}); + 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.js index 8cd9017..0424d47 100644 --- a/test/selectors.test.js +++ b/test/selectors.test.js @@ -1,12 +1,12 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' test('A single selector is rendered without a trailing comma', () => { - let actual = format('a {}'); - let expected = 'a {}'; - expect(actual).toEqual(expected); -}); + let actual = format('a {}') + let expected = 'a {}' + expect(actual).toEqual(expected) +}) test('Multiple selectors are placed on a new line, separated by commas', () => { - let actual = format(` + let actual = format(` selector1, selector1a, selector1b, @@ -15,195 +15,197 @@ test('Multiple selectors are placed on a new line, separated by commas', () => { selector3 { } - `); - let expected = `selector1, + `) + let expected = `selector1, selector1a, selector1b, selector1aa, selector2, -selector3 {}`; - expect(actual).toEqual(expected); -}); +selector3 {}` + expect(actual).toEqual(expected) +}) test('formats multiline selectors on a single line', () => { - let actual = format(` + let actual = format(` a.b .c .d .e .f { color: green } - `); - let expected = `a.b .c .d .e .f { + `) + let expected = `a.b .c .d .e .f { color: green; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats simple selector combinators', () => { - let actual = format(` + let actual = format(` a>b, a>b~c d, .article-content ol li>* {} - `); - let expected = `a > b, + `) + let expected = `a > b, a > b ~ c d, -.article-content ol li > * {}`; - expect(actual).toEqual(expected); -}); +.article-content ol li > * {}` + expect(actual).toEqual(expected) +}) test('lowercases type selectors', () => { - let actual = format(` + let actual = format(` A, B, C {} - `); - let expected = `a, + `) + let expected = `a, b, -c {}`; - expect(actual).toEqual(expected); -}); +c {}` + expect(actual).toEqual(expected) +}) test('formats nested selector combinators', () => { - let fixtures = [ - [`:where(a+b) {}`, `:where(a + b) {}`], - [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], - [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], - [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); + let fixtures = [ + [`:where(a+b) {}`, `:where(a + b) {}`], + [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], + [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], + [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], + ] + for (let [css, expected] of fixtures) { + let actual = format(css) + expect(actual).toEqual(expected) + } +}) test('formats pseudo selectors', () => { - let css = ` + let css = ` a::before, a::after, b:before, b:after, c::first-letter {} - `; - let expected = `a::before, + ` + let expected = `a::before, a::after, b::before, b::after, -c::first-letter {}`; - let actual = format(css); - expect(actual).toEqual(expected); -}); +c::first-letter {}` + let actual = format(css) + expect(actual).toEqual(expected) +}) test('formats pseudo elements with odd casing', () => { - let css = ` + let css = ` a::Before, a::After, b:Before, b:After, c:After, d::First-letter {} - `; - let expected = `a::before, + ` + let expected = `a::before, a::after, b::before, b::after, c::after, -d::first-letter {}`; - let actual = format(css); - expect(actual).toEqual(expected); -}); +d::first-letter {}` + let actual = format(css) + expect(actual).toEqual(expected) +}) test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], + [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], ])('formats nth selector: %s', (css, expected) => { - let actual = format(css); - expect(actual).toEqual(expected); -}); + let actual = format(css) + expect(actual).toEqual(expected) +}) test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], + [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], ])('minifies nth selector: %s', (css, expected) => { - let actual = format(css, { minify: true }); - expect(actual).toEqual(expected); -}); + let actual = format(css, { minify: true }) + expect(actual).toEqual(expected) +}) test('formats multiline selectors', () => { - let actual = format(` + let actual = format(` a:is( a, b, c ) {} - `); - let expected = `a:is(a, b, c) {}`; - expect(actual).toEqual(expected); -}); + `) + let expected = `a:is(a, b, c) {}` + expect(actual).toEqual(expected) +}) test('format nesting selectors', () => { - let actual = format(` + let actual = format(` & a {} b & c {} - `); - let expected = `& a {} + `) + let expected = `& a {} -b & c {}`; - expect(actual).toEqual(expected); -}); +b & c {}` + expect(actual).toEqual(expected) +}) test('prints all possible attribute selectors', () => { - let actual = format(` + let actual = format(` [title="test"], [title|="test"], [title^="test"], [title*="test"], [title$="test"], [title~="test"] {} - `); - let expected = `[title="test"], + `) + let expected = `[title="test"], [title|="test"], [title^="test"], [title*="test"], [title$="test"], -[title~="test"] {}`; - expect(actual).toEqual(expected); -}); +[title~="test"] {}` + expect(actual).toEqual(expected) +}) test('forces attribute selectors to have quoted values', () => { - let actual = format(` + let actual = format(` [title=foo], [title="bar"], [title='baz'] {} - `); - let expected = `[title="foo"], + `) + let expected = `[title="foo"], [title="bar"], -[title="baz"] {}`; - expect(actual).toEqual(expected); -}); +[title="baz"] {}` + expect(actual).toEqual(expected) +}) test('adds a space before attribute selector flags', () => { - let actual = format(` + let actual = format(` [title="foo" i], [title="baz"i], [title=foo S] {} - `); - let expected = `[title="foo" i], + `) + let expected = `[title="foo" i], [title="baz" i], -[title="foo" s] {}`; - expect(actual).toEqual(expected); -}); +[title="foo" s] {}` + 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() {}`; - expect(actual).toEqual(expected); -}); + let actual = format(`:lang("nl","de"),li:nth-child() {}`) + let expected = `:lang("nl", "de"), +li:nth-child() {}` + expect(actual).toEqual(expected) +}) test(`formats ::highlight and ::highlight(Name) correctly`, () => { - let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`); - let expected = `::highlight, + let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`) + let expected = `::highlight, ::highlight(Name), -::highlight(my-thing) {}`; - expect(actual).toEqual(expected); -}); +::highlight(my-thing) {}` + expect(actual).toEqual(expected) +}) test('formats keyframes selectors (50%) correctly', () => { - let actual = format(`@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`); - let expected = `@keyframes Toastify__bounceInUp { + let actual = format( + `@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`, + ) + let expected = `@keyframes Toastify__bounceInUp { 0% { animation-timing-function: cubic-bezier(.215, .61, .355, 1); } @@ -212,25 +214,25 @@ test('formats keyframes selectors (50%) correctly', () => { 80%, to {} -}`; - expect(actual).toBe(expected); -}); +}` + expect(actual).toBe(expected) +}) test('formats unknown pseudos correctly', () => { - let actual = format(` + let actual = format(` ::foo-bar, :unkown-thing(), :unnowkn(kjsa.asddk,asd) {} - `); - let expected = `::foo-bar, + `) + let expected = `::foo-bar, :unkown-thing(), -:unnowkn(kjsa.asddk, asd) {}`; - expect(actual).toEqual(expected); -}); +:unnowkn(kjsa.asddk, asd) {}` + expect(actual).toEqual(expected) +}) test('handles syntax errors', () => { - let actual = format(` + let actual = format(` test, @test {} - `); - let expected = `test {}`; - expect(actual).toEqual(expected); -}); + `) + let expected = `test {}` + expect(actual).toEqual(expected) +}) diff --git a/test/tab-size.test.js b/test/tab-size.test.js index 6eb2a23..9d3f816 100644 --- a/test/tab-size.test.js +++ b/test/tab-size.test.js @@ -1,12 +1,13 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' let fixture = ` selector { color: red; } -`; +` test('tab_size: 2', () => { - let actual = format(` + let actual = format( + ` selector { color: red; } @@ -16,8 +17,10 @@ test('tab_size: 2', () => { color: blue; } } - `, { tab_size: 2 }); - let expected = `selector { + `, + { tab_size: 2 }, + ) + let expected = `selector { color: red; } @@ -25,20 +28,20 @@ test('tab_size: 2', () => { selector { color: blue; } -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('invalid tab_size: 0', () => { - expect(() => format(fixture, { tab_size: 0 })).toThrow(); -}); + expect(() => format(fixture, { tab_size: 0 })).toThrow() +}) test('invalid tab_size: negative', () => { - expect(() => format(fixture, { tab_size: -1 })).toThrow(); -}); + expect(() => format(fixture, { tab_size: -1 })).toThrow() +}) test('combine tab_size and minify', () => { - let actual = format(fixture, { - tab_size: 2, - minify: true, - }); - let expected = `selector{color:red}`; - expect(actual).toEqual(expected); -}); + let actual = format(fixture, { + tab_size: 2, + minify: true, + }) + let expected = `selector{color:red}` + expect(actual).toEqual(expected) +}) diff --git a/test/values.test.js b/test/values.test.js index 4388212..ee175d6 100644 --- a/test/values.test.js +++ b/test/values.test.js @@ -1,20 +1,20 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; +import { test, expect } from 'vitest' +import { format } from '../src/lib/index.js' test('collapses abundant whitespace', () => { - let actual = format(`a { + let actual = format(`a { transition: all 100ms ease; color: rgb( 0 , 0 , 0 ); color: red ; - }`); - let expected = `a { + }`) + let expected = `a { transition: all 100ms ease; color: rgb(0, 0, 0); color: red; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats simple value lists', () => { - let actual = format(` + let actual = format(` a { transition-property: all,opacity; transition: all 100ms ease,opacity 10ms 20ms linear; @@ -24,8 +24,8 @@ test('formats simple value lists', () => { content: 'Test'; background-image: url("EXAMPLE.COM"); } - `); - let expected = `a { + `) + let expected = `a { transition-property: all, opacity; transition: all 100ms ease, opacity 10ms 20ms linear; animation: COLOR 123ms EASE-OUT; @@ -33,35 +33,35 @@ test('formats simple value lists', () => { color: hsl(0%, 10%, 50%); content: "Test"; background-image: url("EXAMPLE.COM"); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats nested value lists', () => { - let actual = format(` + let actual = format(` a { background: red,linear-gradient(to bottom,red 10%,green 50%,blue 100%); } - `); - let expected = `a { + `) + let expected = `a { background: red, linear-gradient(to bottom, red 10%, green 50%, blue 100%); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats nested var()', () => { - let actual = format(` + let actual = format(` a { color: var(--test1,var(--test2,green)); color: var(--test3,rgb(0,0,0)); } - `); - let expected = `a { + `) + let expected = `a { color: var(--test1, var(--test2, green)); color: var(--test3, rgb(0, 0, 0)); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats multiline values on a single line', () => { - let actual = format(` + let actual = format(` a { background: linear-gradient( red, @@ -73,172 +73,175 @@ a { 0 ); } - `); - let expected = `a { + `) + let expected = `a { background: linear-gradient(red, 10% blue, 20% green, 100% yellow); color: rgb(0, 0, 0); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not break font shorthand', () => { - let actual = format(`a { + let actual = format(`a { font: 2em/2 sans-serif; font: 2em/ 2 sans-serif; font: 2em / 2 sans-serif; - }`); - let expected = `a { + }`) + let expected = `a { font: 2em/2 sans-serif; font: 2em/2 sans-serif; font: 2em/2 sans-serif; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats whitespace around operators (*/+-) correctly', () => { - let actual = format(`a { + let actual = format(`a { font: 2em/2 sans-serif; font-size: calc(2em/2); font-size: calc(2em * 2); font-size: calc(2em + 2px); font-size: calc(2em - 2px); -}`); - let expected = `a { +}`) + let expected = `a { font: 2em/2 sans-serif; font-size: calc(2em / 2); font-size: calc(2em * 2); font-size: calc(2em + 2px); font-size: calc(2em - 2px); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats whitespace around operators (*/+-) correctly in nested parenthesis', () => { - let actual = format(`a { + let actual = format(`a { width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); -}`); - let expected = `a { +}`) + let expected = `a { width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats parenthesis correctly', () => { - let actual = format(`a { + let actual = format(`a { width: calc(100% - var(--x)); width: calc((100% - var(--x))); width: calc(100% - (var(--x))); width: calc((100% - (var(--x)))); -}`); - let expected = `a { +}`) + let expected = `a { width: calc(100% - var(--x)); width: calc((100% - var(--x))); width: calc(100% - (var(--x))); width: calc((100% - (var(--x)))); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not lowercase grid-area names', () => { - let actual = format(`a { grid-area: emailInputBox; }`); - let expected = `a { + let actual = format(`a { grid-area: emailInputBox; }`) + let expected = `a { grid-area: emailInputBox; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not lowercase custom properties in var()', () => { - let actual = format(`a { color: var(--MyColor); }`); - let expected = `a { + let actual = format(`a { color: var(--MyColor); }`) + let expected = `a { color: var(--MyColor); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('lowercases CSS functions', () => { - let actual = format(`a { + let actual = format(`a { color: RGB(0, 0, 0); transform: translateX(100px); - }`); - let expected = `a { + }`) + let expected = `a { color: rgb(0, 0, 0); transform: translatex(100px); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('relative colors', () => { - let actual = format(`a { + let actual = format(`a { color: rgb( from red 0 0 255); color: rgb( from rgb( 200 0 0 ) r r r ) ; color: hwb( from var( --base-color ) h w b / var( --standard-opacity ) ) ; color: lch(from var(--base-color) calc(l + 20) c h); - }`); - let expected = `a { + }`) + let expected = `a { color: rgb(from red 0 0 255); color: rgb(from rgb(200 0 0) r r r); color: hwb(from var(--base-color) h w b / var(--standard-opacity)); color: lch(from var(--base-color) calc(l + 20) c h); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not change casing of `NaN`', () => { - let actual = format(`a { + let actual = format(`a { height: calc(1 * NaN); - }`); - let expected = `a { + }`) + let expected = `a { height: calc(1 * NaN); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not change casing of URLs', () => { - let actual = format(`a { + let actual = format(`a { background-image: url("My-Url.png"); - }`); - let expected = `a { + }`) + let expected = `a { background-image: url("My-Url.png"); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('lowercases dimensions', () => { - let actual = format(`a { + let actual = format(`a { font-size: 12PX; width: var(--test, 33REM); - }`); - let expected = `a { + }`) + let expected = `a { font-size: 12px; width: var(--test, 33rem); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('formats unknown content in value', () => { - let actual = format(`a { + let actual = format(`a { content: 'Test' counter(page); - }`); - let expected = `a { + }`) + let expected = `a { content: "Test" counter(page); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not break space toggles', () => { - let actual = format(`a { + let actual = format(`a { --ON: initial; --OFF: ; - }`); - let expected = `a { + }`) + let expected = `a { --ON: initial; --OFF: ; -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test('does not break space toggles (minified)', () => { - let actual = format(`a { + let actual = format( + `a { --ON: initial; --OFF: ; - }`, { minify: true }); - let expected = `a{--ON:initial;--OFF: }`; - expect(actual).toEqual(expected); -}); + }`, + { minify: true }, + ) + let expected = `a{--ON:initial;--OFF: }` + expect(actual).toEqual(expected) +}) test('adds quotes around strings in url()', () => { - let actual = format(`a { + let actual = format(`a { background-image: url("star.gif"); list-style-image: url('../images/bullet.jpg'); content: url("pdficon.jpg"); @@ -247,8 +250,8 @@ test('adds quotes around strings in url()', () => { src: url('fantasticfont.woff'); offset-path: url(#path); mask-image: url("masks.svg#mask1"); - }`); - let expected = `a { + }`) + let expected = `a { background-image: url("star.gif"); list-style-image: url("../images/bullet.jpg"); content: url("pdficon.jpg"); @@ -257,57 +260,57 @@ test('adds quotes around strings in url()', () => { src: url("fantasticfont.woff"); offset-path: url("#path"); mask-image: url("masks.svg#mask1"); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test.each([ - `data:image/svg+xml;utf8,`, - `data:image/svg+xml;utf8,`, + `data:image/svg+xml;utf8,`, + `data:image/svg+xml;utf8,`, ])('Does not mess up URLs with inlined SVG', (input) => { - let actual = format(`test { + let actual = format(`test { background-image: url('${input}'); background-image: url(${input}); - }`); - let expected = `test { + }`) + let expected = `test { background-image: url(${input}); background-image: url(${input}); -}`; - expect(actual).toEqual(expected); -}); +}` + expect(actual).toEqual(expected) +}) test.each([ - // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data - 'data:,Hello%2C%20World%21', - 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', - 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', - 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', - // from https://github.com/projectwallace/format-css/issues/144 - `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, + // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data + 'data:,Hello%2C%20World%21', + 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', + 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', + 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', + // from https://github.com/projectwallace/format-css/issues/144 + `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, ])('Does not mess up URLs with encoded inlined content: %s', (input) => { - let actual = format(`test { + let actual = format(`test { background-image: url(${input}); - }`); - let expected = `test { + }`) + let expected = `test { background-image: url(${input}); -}`; - expect(actual).toBe(expected); -}); +}` + expect(actual).toBe(expected) +}) test.each([ - `U+26`, // single code point - `U+0-7F`, - `U+0025-00FF`, // code point range - `U+4??`, // wildcard range - `U+0025-00FF, U+4??`, // multiple values + `U+26`, // single code point + `U+0-7F`, + `U+0025-00FF`, // code point range + `U+4??`, // wildcard range + `U+0025-00FF, U+4??`, // multiple values ])('Formats unicode-range: %s', (unicode_range) => { - let actual = format(`test { unicode-range: ${unicode_range}; }`); - let expected = `test { + let actual = format(`test { unicode-range: ${unicode_range}; }`) + let expected = `test { unicode-range: ${unicode_range}; -}`; - expect(actual).toBe(expected); -}); +}` + expect(actual).toBe(expected) +}) test('formats multi-value unicode range', () => { - let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`); - let expected = `test { + let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`) + let expected = `test { unicode-range: U+0025-00FF, U+4??; -}`; - expect(actual).toBe(expected); -}); +}` + expect(actual).toBe(expected) +}) diff --git a/tsconfig.json b/tsconfig.json index 70de516..e3b1b20 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,26 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - "types": ["node"], - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "rootDir": "src", - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + "types": ["node"], + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "rootDir": "src", + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] } diff --git a/tsdown.config.js b/tsdown.config.js index 5e421b3..2a97b23 100644 --- a/tsdown.config.js +++ b/tsdown.config.js @@ -1,32 +1,32 @@ -import { defineConfig } from 'tsdown'; -import { codecovRollupPlugin } from '@codecov/rollup-plugin'; +import { defineConfig } from 'tsdown' +import { codecovRollupPlugin } from '@codecov/rollup-plugin' export default defineConfig([ - { - entry: 'src/lib/index.ts', - platform: 'neutral', - publint: true, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCss', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, - { - entry: 'src/cli/cli.ts', - platform: 'node', - dts: false, - // Reference the lib via its package name to avoid bundling it twice - deps: { - neverBundle: ['@projectwallace/format-css'], - }, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCssCli', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, -]); + { + entry: 'src/lib/index.ts', + platform: 'neutral', + publint: true, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCss', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, + { + entry: 'src/cli/cli.ts', + platform: 'node', + dts: false, + // Reference the lib via its package name to avoid bundling it twice + deps: { + neverBundle: ['@projectwallace/format-css'], + }, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCssCli', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, +]) diff --git a/vitest.config.js b/vitest.config.js index cd426a2..6f9ea30 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,14 +1,14 @@ -import { defineConfig } from 'vitest/config'; -import { resolve } from 'node:path'; +import { defineConfig } from 'vitest/config' +import { resolve } from 'node:path' export default defineConfig({ - resolve: { - alias: { - '@projectwallace/format-css': resolve('./src/lib/index.ts'), - }, - }, - test: { - coverage: { - provider: 'v8', - }, - }, -}); + resolve: { + alias: { + '@projectwallace/format-css': resolve('./src/lib/index.ts'), + }, + }, + test: { + coverage: { + provider: 'v8', + }, + }, +}) From 8e7af79fed987d00a3c2ed2128b9dfedf639acc5 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 13:29:24 +0200 Subject: [PATCH 4/7] Revert "fix formatting" This reverts commit a564c2a8f09a6f6851682ad02510d7dfcf211295. --- test/api.test.js | 44 ++-- test/atrules.test.js | 408 +++++++++++++++---------------- test/comments.test.js | 499 +++++++++++++++++++------------------- test/declarations.test.js | 78 +++--- test/minify.test.js | 106 ++++---- test/rules.test.js | 186 +++++++------- test/selectors.test.js | 262 ++++++++++---------- test/tab-size.test.js | 43 ++-- test/values.test.js | 313 ++++++++++++------------ tsconfig.json | 48 ++-- tsdown.config.js | 62 ++--- vitest.config.js | 26 +- 12 files changed, 1030 insertions(+), 1045 deletions(-) diff --git a/test/api.test.js b/test/api.test.js index 97f2b7c..1cf0bcc 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,17 +1,17 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; test('empty input', () => { - let actual = format(``) - let expected = `` - expect(actual).toEqual(expected) -}) + let actual = format(``); + let expected = ``; + expect(actual).toEqual(expected); +}); test('handles invalid input', () => { - let actual = format(`;`) - let expected = `` - expect(actual).toEqual(expected) -}) + let actual = format(`;`); + let expected = ``; + expect(actual).toEqual(expected); +}); test('Vadim Makeevs example works', () => { - let actual = format(` + let actual = format(` @layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @@ -23,8 +23,8 @@ test('Vadim Makeevs example works', () => { } } } - `) - let expected = `@layer what { + `); + let expected = `@layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @media (height > 0) { @@ -34,14 +34,12 @@ test('Vadim Makeevs example works', () => { } } } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('minified Vadims example', () => { - let actual = format( - `@layer what{@container (width>0){@media (min-height:.001px){ul:has(:nth-child(1 of li)):hover{--is:this}}}}`, - ) - let expected = `@layer what { + let actual = format(`@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 { @@ -49,6 +47,6 @@ test('minified Vadims example', () => { } } } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); diff --git a/test/atrules.test.js b/test/atrules.test.js index d3406cc..c580233 100644 --- a/test/atrules.test.js +++ b/test/atrules.test.js @@ -1,15 +1,15 @@ -import { test, expect } from 'vitest' -import { format, minify } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format, minify } from '../src/lib/index.js'; test('AtRules start on a new line', () => { - let actual = format(` + let actual = format(` @media (min-width: 1000px) { selector { property: value; } } @layer test { selector { property: value; } } - `) - let expected = `@media (min-width: 1000px) { + `); + let expected = `@media (min-width: 1000px) { selector { property: value; } @@ -19,11 +19,11 @@ test('AtRules start on a new line', () => { selector { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('Atrule blocks are surrounded by {} with correct spacing and indentation', () => { - let actual = format(` + let actual = format(` @media (min-width:1000px){selector{property:value1}} @media (min-width:1000px) @@ -32,8 +32,8 @@ test('Atrule blocks are surrounded by {} with correct spacing and indentation', { property:value2 } -}`) - let expected = `@media (min-width: 1000px) { +}`); + let expected = `@media (min-width: 1000px) { selector { property: value1; } @@ -43,115 +43,115 @@ test('Atrule blocks are surrounded by {} with correct spacing and indentation', selector { property: value2; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('adds whitespace between prelude and {', () => { - let actual = format(`@media all{}`) - let expected = `@media all {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@media all{}`); + let expected = `@media all {}`; + 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) {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@media all and (min-width: 1000px) {}`); + let expected = `@media all and (min-width: 1000px) {}`; + expect(actual).toEqual(expected); +}); test('removes newlines in prelude', () => { - let actual = format(`@media + let actual = format(`@media all, screen, print, - (min-width: 1000px) {}`) - let expected = `@media all, screen, print, (min-width: 1000px) {}` - expect(actual).toEqual(expected) -}) + (min-width: 1000px) {}`); + let expected = `@media all, screen, print, (min-width: 1000px) {}`; + 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) {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@media (min-width:1000px) {}`); + let expected = `@media (min-width: 1000px) {}`; + 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) {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@media (min-width : 1000px) {}`); + let expected = `@media (min-width: 1000px) {}`; + expect(actual).toEqual(expected); +}); test('formats @layer with excess whitespace', () => { - let actual = format(`@layer test;`) - let expected = `@layer test;` - expect(actual).toEqual(expected) -}) + let actual = format(`@layer test;`); + let expected = `@layer test;`; + expect(actual).toEqual(expected); +}); test('adds whitespace to @layer tbody,thead', () => { - let actual = format(`@layer tbody,thead;`) - let expected = `@layer tbody, thead;` - expect(actual).toEqual(expected) -}) + let actual = format(`@layer tbody,thead;`); + let expected = `@layer tbody, thead;`; + expect(actual).toEqual(expected); +}); test('adds whitespace to @supports (display:grid)', () => { - let actual = format(`@supports (display:grid){}`) - let expected = `@supports (display: grid) {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@supports (display:grid){}`); + let expected = `@supports (display: grid) {}`; + expect(actual).toEqual(expected); +}); test('@media prelude formatting', () => { - let fixtures = [ - [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], - [ - `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, - `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, - ], - [ - `@media (min-width: 1024px)or (max-width: 1439px) {}`, - `@media (min-width: 1024px) or (max-width: 1439px) {}`, - ], - [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], - [ - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - ], - [`@media screen or print {}`, `@media screen or print {}`], - [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [ - `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, - `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, - ], - [ - `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, - `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, - ], - ] - for (let [css, expected] of fixtures) { - let actual = format(css) - expect(actual).toEqual(expected) - } -}) + let fixtures = [ + [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], + [ + `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, + `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, + ], + [ + `@media (min-width: 1024px)or (max-width: 1439px) {}`, + `@media (min-width: 1024px) or (max-width: 1439px) {}`, + ], + [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], + [ + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + `@media all and (transform-3d), (-webkit-transform-3d) {}`, + ], + [`@media screen or print {}`, `@media screen or print {}`], + [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], + [ + `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, + `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, + ], + [ + `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, + `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, + ], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); test('lowercases functions inside atrule preludes', () => { - let actual = format(` + let actual = format(` @import URL("style.css") LAYER(test) SUPPORTS(display:grid); @supports SELECTOR([popover]:open) {} -`) - let expected = `@import url("style.css") layer(test) supports(display: grid); +`); + let expected = `@import url("style.css") layer(test) supports(display: grid); -@supports selector([popover]:open) {}` - expect(actual).toEqual(expected) -}) +@supports selector([popover]:open) {}`; + expect(actual).toEqual(expected); +}); test('formats @scope', () => { - let actual = format(` + let actual = format(` @scope (.light-scheme) {} @scope (.media-object) to (.content > *) {} -`) - let expected = `@scope (.light-scheme) {} +`); + let expected = `@scope (.light-scheme) {} -@scope (.media-object) to (.content > *) {}` - expect(actual).toEqual(expected) -}) +@scope (.media-object) to (.content > *) {}`; + expect(actual).toEqual(expected); +}); test('calc() inside @media', () => { - let actual = format(` + let actual = format(` @media (min-width: calc(1px*1)) {} @media (min-width: calc(2px* 2)) {} @media (min-width: calc(3px *3)) {} @media (min-width: calc(4px * 4)) {} @media (min-width: calc(5px * 5)) {} - `) - let expected = `@media (min-width: calc(1px * 1)) {} + `); + let expected = `@media (min-width: calc(1px * 1)) {} @media (min-width: calc(2px * 2)) {} @@ -159,77 +159,77 @@ test('calc() inside @media', () => { @media (min-width: calc(4px * 4)) {} -@media (min-width: calc(5px * 5)) {}` - expect(actual).toEqual(expected) -}) +@media (min-width: calc(5px * 5)) {}`; + 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)){}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@media (min-width: calc(1px*1)) {}`); + let expected = `@media (min-width:calc(1px*1)){}`; + 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)){}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@media (min-width: calc(1px + 1em)) {}`); + let expected = `@media (min-width:calc(1px + 1em)){}`; + 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)){}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@media (min-width: calc(1em - 1px)) {}`); + let expected = `@media (min-width:calc(1em - 1px)){}`; + 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);', - ], - ] - for (let [css, expected] of fixtures) { - let actual = format(css) - expect(actual).toEqual(expected) - } -}) + 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);', + ], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); test('@supports prelude formatting', () => { - let fixtures = [ - [`@supports (display:grid){}`, `@supports (display: grid) {}`], - [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], - ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], - ] - for (let [css, expected] of fixtures) { - let actual = format(css) - expect(actual).toEqual(expected) - } -}) + let fixtures = [ + [`@supports (display:grid){}`, `@supports (display: grid) {}`], + [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], + ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); test('@layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody, thead;`], - ] - for (let [css, expected] of fixtures) { - let actual = format(css) - expect(actual).toEqual(expected) - } -}) + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody, thead;`], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); test('minify: @layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody,thead;`], - ] - for (let [css, expected] of fixtures) { - let actual = minify(css) - expect(actual).toEqual(expected) - } -}) + let fixtures = [ + [`@layer test;`, `@layer test;`], + [`@layer tbody,thead;`, `@layer tbody,thead;`], + ]; + for (let [css, expected] of fixtures) { + let actual = minify(css); + expect(actual).toEqual(expected); + } +}); test('single empty line after a rule, before atrule', () => { - let actual = format(` + let actual = format(` rule1 { property: value } @media (min-width: 1000px) { rule2 { property: value } } - `) - let expected = `rule1 { + `); + let expected = `rule1 { property: value; } @@ -237,83 +237,83 @@ test('single empty line after a rule, before atrule', () => { rule2 { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('single empty line in between atrules', () => { - let actual = format(` + let actual = format(` @layer test1; @media (min-width: 1000px) { rule2 { property: value } } - `) - let expected = `@layer test1; + `); + let expected = `@layer test1; @media (min-width: 1000px) { rule2 { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('newline between last declaration and nested atrule', () => { - let actual = format(` + let actual = format(` test { property1: value1; @media all { property2: value2; } } - `) - let expected = `test { + `); + let expected = `test { property1: value1; @media all { property2: value2; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('lowercases the atrule name', () => { - let actual = format(`@LAYER test {}`) - let expected = `@layer test {}` - expect(actual).toEqual(expected) -}) + let actual = format(`@LAYER test {}`); + let expected = `@layer test {}`; + expect(actual).toEqual(expected); +}); test('does not lowercase the atrule value', () => { - let actual = format('@keyframes TEST {}') - let expected = '@keyframes TEST {}' - expect(actual).toEqual(expected) -}) + let actual = format('@keyframes TEST {}'); + let expected = '@keyframes TEST {}'; + expect(actual).toEqual(expected); +}); test('Atrules w/o Block are terminated with a semicolon', () => { - let actual = format(` + let actual = format(` @layer test; @import url('test'); - `) - let expected = `@layer test; + `); + let expected = `@layer test; -@import url('test');` - expect(actual).toEqual(expected) -}) +@import url('test');`; + expect(actual).toEqual(expected); +}); test('Empty atrule braces are placed on the same line', () => { - let actual = format(`@media all { + let actual = format(`@media all { } - @supports (display: grid) {}`) - let expected = `@media all {} + @supports (display: grid) {}`); + let expected = `@media all {} -@supports (display: grid) {}` - expect(actual).toEqual(expected) -}) +@supports (display: grid) {}`; + expect(actual).toEqual(expected); +}); test('new-fangled comparators (width > 1000px)', () => { - let actual = format(` + let actual = format(` @container (width>1000px) {} @media (width>1000px) {} @media (width=>1000px) {} @media (width<=1000px) {} @media (200px 1000px) {} + `); + let expected = `@container (width > 1000px) {} @media (width > 1000px) {} @@ -321,23 +321,23 @@ test('new-fangled comparators (width > 1000px)', () => { @media (width <= 1000px) {} -@media (200px < width < 1000px) {}` - expect(actual).toEqual(expected) -}) +@media (200px < width < 1000px) {}`; + expect(actual).toEqual(expected); +}); test('minify: new-fangled comparators (width > 1000px)', () => { - let actual = minify(`@container (width>1000px) {}`) - let expected = `@container (width>1000px){}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@container (width>1000px) {}`); + let expected = `@container (width>1000px){}`; + expect(actual).toEqual(expected); +}); test.skip('preserves comments', () => { - let actual = format(` + let actual = format(` @media /* comment */ all {} @media all /* comment */ {} @media (min-width: 1000px /* comment */) {} @media (/* comment */ min-width: 1000px) {} @layer /* comment */ {} - `) - let expected = `@media /* comment */ all {} + `); + let expected = `@media /* comment */ all {} @media all /* comment */ {} @@ -346,6 +346,6 @@ test.skip('preserves comments', () => { @media (/* comment */ min-width: 1000px) {} @layer /* comment */ {} -` - expect(actual).toEqual(expected) -}) +`; + expect(actual).toEqual(expected); +}); diff --git a/test/comments.test.js b/test/comments.test.js index 13ec3bc..c953e93 100644 --- a/test/comments.test.js +++ b/test/comments.test.js @@ -1,37 +1,37 @@ -import { describe, test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { describe, test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; describe('comments', () => { - test('only comment', () => { - let actual = format(`/* comment */`) - let expected = `/* comment */` - expect(actual).toEqual(expected) - }) - test('bang comment before rule', () => { - let actual = format(` + test('only comment', () => { + let actual = format(`/* comment */`); + let expected = `/* comment */`; + expect(actual).toEqual(expected); + }); + test('bang comment before rule', () => { + let actual = format(` /*! comment */ selector {} - `) - let expected = `/*! comment */ -selector {}` - expect(actual).toEqual(expected) - }) - test('before selectors', () => { - let actual = format(` + `); + let expected = `/*! comment */ +selector {}`; + expect(actual).toEqual(expected); + }); + test('before selectors', () => { + let actual = format(` /* comment */ selector1, selector2 { property: value; } - `) - let expected = `/* comment */ + `); + let expected = `/* comment */ selector1, selector2 { property: value; -}` - expect(actual).toEqual(expected) - }) - test('before nested selectors', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('before nested selectors', () => { + let actual = format(` a { /* comment */ & nested1, @@ -39,48 +39,48 @@ selector2 { property: value; } } - `) - let expected = `a { + `); + let expected = `a { /* comment */ & nested1, & nested2 { property: value; } -}` - expect(actual).toEqual(expected) - }) - test('after selectors', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('after selectors', () => { + let actual = format(` selector1, selector2 /* comment */ { property: value; } - `) - let expected = `selector1, + `); + let expected = `selector1, selector2 /* comment */ { property: value; -}` - expect(actual).toEqual(expected) - }) - test('in between selectors', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('in between selectors', () => { + let actual = format(` selector1, /* comment */ selector2 { property: value; } - `) - let expected = `selector1, + `); + let expected = `selector1, /* comment */ selector2 { property: value; -}` - expect(actual).toEqual(expected) - }) - test('in between nested selectors', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('in between nested selectors', () => { + let actual = format(` a { & nested1, /* comment */ @@ -88,100 +88,100 @@ selector2 { property: value; } } - `) - let expected = `a { + `); + let expected = `a { & nested1, /* comment */ & nested2 { property: value; } -}` - expect(actual).toEqual(expected) - }) - test('as first child in rule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as first child in rule', () => { + let actual = format(` selector { /* comment */ property: value; } - `) - let expected = `selector { + `); + let expected = `selector { /* comment */ property: value; -}` - expect(actual).toEqual(expected) - }) - test('as last child in rule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as last child in rule', () => { + let actual = format(` selector { property: value; /* comment */ } - `) - let expected = `selector { + `); + let expected = `selector { property: value; /* comment */ -}` - expect(actual).toEqual(expected) - }) - test('as last child in nested rule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as last child in nested rule', () => { + let actual = format(` a { & selector { property: value; /* comment */ } } - `) - let expected = `a { + `); + let expected = `a { & selector { property: value; /* comment */ } -}` - expect(actual).toEqual(expected) - }) - test('as only child in rule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as only child in rule', () => { + let actual = format(` selector { /* comment */ } - `) - let expected = `selector { + `); + let expected = `selector { /* comment */ -}` - expect(actual).toEqual(expected) - }) - test('as only child in nested rule', () => { - let actual = format(`a { +}`; + expect(actual).toEqual(expected); + }); + test('as only child in nested rule', () => { + let actual = format(`a { & selector { /* comment */ } -}`) - let expected = `a { +}`); + let expected = `a { & selector { /* comment */ } -}` - expect(actual).toEqual(expected) - }) - test('in between declarations', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('in between declarations', () => { + let actual = format(` selector { property: value; /* comment */ property: value; } - `) - let expected = `selector { + `); + let expected = `selector { property: value; /* comment */ property: value; -}` - expect(actual).toEqual(expected) - }) - test('in between nested declarations', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('in between nested declarations', () => { + let actual = format(` a { & selector { property: value; @@ -189,35 +189,35 @@ selector2 { property: value; } } - `) - let expected = `a { + `); + let expected = `a { & selector { property: value; /* comment */ property: value; } -}` - expect(actual).toEqual(expected) - }) - test('as first child in atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as first child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { /* comment */ selector { property: value; } } - `) - let expected = `@media (min-width: 1000px) { + `); + let expected = `@media (min-width: 1000px) { /* comment */ selector { property: value; } -}` - expect(actual).toEqual(expected) - }) - test('as first child in nested atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as first child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { /* comment */ @@ -226,36 +226,36 @@ selector2 { } } } - `) - let expected = `@media all { + `); + let expected = `@media all { @media (min-width: 1000px) { /* comment */ selector { property: value; } } -}` - expect(actual).toEqual(expected) - }) - test('as last child in atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as last child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { selector { property: value; } /* comment */ } - `) - let expected = `@media (min-width: 1000px) { + `); + let expected = `@media (min-width: 1000px) { selector { property: value; } /* comment */ -}` - expect(actual).toEqual(expected) - }) - test('as last child in nested atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as last child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { selector { @@ -264,45 +264,45 @@ selector2 { /* comment */ } } - `) - let expected = `@media all { + `); + let expected = `@media all { @media (min-width: 1000px) { selector { property: value; } /* comment */ } -}` - expect(actual).toEqual(expected) - }) - test('as only child in atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as only child in atrule', () => { + let actual = format(` @media (min-width: 1000px) { /* comment */ } - `) - let expected = `@media (min-width: 1000px) { + `); + let expected = `@media (min-width: 1000px) { /* comment */ -}` - expect(actual).toEqual(expected) - }) - test('as only child in nested atrule', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('as only child in nested atrule', () => { + let actual = format(` @media all { @media (min-width: 1000px) { /* comment */ } } - `) - let expected = `@media all { + `); + let expected = `@media all { @media (min-width: 1000px) { /* comment */ } -}` - expect(actual).toEqual(expected) - }) - test('in between rules and atrules', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('in between rules and atrules', () => { + let actual = format(` /* comment 1 */ selector {} /* comment 2 */ @@ -312,8 +312,8 @@ selector2 { /* comment 4 */ } /* comment 5 */ - `) - let expected = `/* comment 1 */ + `); + let expected = `/* comment 1 */ selector {} /* comment 2 */ @media (min-width: 1000px) { @@ -321,11 +321,11 @@ selector {} selector {} /* comment 4 */ } -/* comment 5 */` - expect(actual).toEqual(expected) - }) - test('comment before rule and atrule should not be separated by newline', () => { - let actual = format(` +/* comment 5 */`; + expect(actual).toEqual(expected); + }); + test('comment before rule and atrule should not be separated by newline', () => { + let actual = format(` /* comment 1 */ selector {} @@ -336,19 +336,19 @@ selector {} selector {} /* comment 4 */ } - `) - let expected = `/* comment 1 */ + `); + let expected = `/* comment 1 */ selector {} /* comment 2 */ @media (min-width: 1000px) { /* comment 3 */ selector {} /* comment 4 */ -}` - expect(actual).toEqual(expected) - }) - test('a declaration after multiple comments starts on a new line', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('a declaration after multiple comments starts on a new line', () => { + let actual = format(` selector { /* comment 1 */ /* comment 2 */ @@ -362,8 +362,8 @@ selector {} /* comment 6 */ --custom-property: value; } - `) - let expected = `selector { + `); + let expected = `selector { /* comment 1 */ /* comment 2 */ --custom-property: value; @@ -373,11 +373,11 @@ selector {} /* comment 5 */ /* comment 6 */ --custom-property: value; -}` - expect(actual).toEqual(expected) - }) - test('multiple comments in between rules and atrules', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('multiple comments in between rules and atrules', () => { + let actual = format(` /* comment 1 */ /* comment 1.1 */ selector {} @@ -392,8 +392,8 @@ selector {} } /* comment 5 */ /* comment 5.1 */ - `) - let expected = `/* comment 1 */ + `); + let expected = `/* comment 1 */ /* comment 1.1 */ selector {} /* comment 2 */ @@ -406,110 +406,109 @@ selector {} /* comment 4.1 */ } /* comment 5 */ -/* comment 5.1 */` - expect(actual).toEqual(expected) - }) - test('puts every comment on a new line', () => { - let actual = format(` +/* comment 5.1 */`; + expect(actual).toEqual(expected); + }); + test('puts every comment on a new line', () => { + let actual = format(` x { /*--font-family: inherit;*/ /*--font-style: normal;*/ --border-top-color: var(--root-color--support); } -`) - let expected = `x { +`); + let expected = `x { /*--font-family: inherit;*/ /*--font-style: normal;*/ --border-top-color: var(--root-color--support); -}` - 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*/) {}' - 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*/) {}' - expect(actual).toEqual(expected) - }) - test('skip in @import prelude before specifier', () => { - let actual = format('@import /*test*/"foo";') - let expected = '@import "foo";' - expect(actual).toEqual(expected) - }) - test('skip in @import prelude after specifier', () => { - let actual = format('@import "foo"/*test*/;') - let expected = '@import "foo";' - expect(actual).toEqual(expected) - }) - test('skip in selector combinator', () => { - let actual = format(` +}`; + 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*/) {}'; + 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*/) {}'; + expect(actual).toEqual(expected); + }); + test('skip in @import prelude before specifier', () => { + let actual = format('@import /*test*/"foo";'); + let expected = '@import "foo";'; + expect(actual).toEqual(expected); + }); + test('skip in @import prelude after specifier', () => { + let actual = format('@import "foo"/*test*/;'); + let expected = '@import "foo";'; + expect(actual).toEqual(expected); + }); + test('skip in selector combinator', () => { + let actual = format(` a/*test*/ /*test*/b, a/*test*/+/*test*/b {} - `) - let expected = `a /*test*/ /*test*/ b, -a + b {}` - expect(actual).toEqual(expected) - }) - test('in attribute selector', () => { - let actual = format(`[/*test*/a='b' i/*test*/] {}`) - let expected = `[a="b" i] {}` - expect(actual).toEqual(expected) - }) - test('skip in var() with fallback', () => { - let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`) - let expected = `a { + `); + let expected = `a /*test*/ /*test*/ b, +a + b {}`; + expect(actual).toEqual(expected); + }); + test('in attribute selector', () => { + let actual = format(`[/*test*/a='b' i/*test*/] {}`); + let expected = `[a="b" i] {}`; + expect(actual).toEqual(expected); + }); + test('skip in var() with fallback', () => { + let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`); + let expected = `a { prop: var(--name, 1); -}` - expect(actual).toEqual(expected) - }) - test('skip in custom property declaration (space toggle)', () => { - let actual = format(`a { --test: /*test*/; }`) - let expected = `a { +}`; + expect(actual).toEqual(expected); + }); + test('skip in custom property declaration (space toggle)', () => { + let actual = format(`a { --test: /*test*/; }`); + let expected = `a { --test: ; -}` - expect(actual).toEqual(expected) - }) - test('before value', () => { - let actual = format(`a { prop: /*test*/value; }`) - let expected = `a { +}`; + expect(actual).toEqual(expected); + }); + test('before value', () => { + let actual = format(`a { prop: /*test*/value; }`); + let expected = `a { prop: value; -}` - expect(actual).toEqual(expected) - }) - test('after value', () => { - let actual = format(`a { +}`; + expect(actual).toEqual(expected); + }); + test('after value', () => { + let actual = format(`a { prop: value/*test*/; - }`) - let expected = `a { + }`); + let expected = `a { prop: value; -}` - expect(actual).toEqual(expected) - }) - test('skip in value functions', () => { - let actual = format(` +}`; + expect(actual).toEqual(expected); + }); + test('skip in value functions', () => { + let actual = format(` a { background-image: linear-gradient(/* comment */red, green); background-image: linear-gradient(red/* comment */, green); background-image: linear-gradient(red, green/* comment */); background-image: linear-gradient(red, green)/* comment */ } - `) - let expected = `a { + `); + let expected = `a { background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); background-image: linear-gradient(red, green); /* comment */ -}` - expect(actual).toEqual(expected) - }) - test('strips comments in minification mode', () => { - let actual = format( - ` +}`; + expect(actual).toEqual(expected); + }); + test('strips comments in minification mode', () => { + let actual = format(` /* comment 1 */ selector {} /* comment 2 */ @@ -519,10 +518,8 @@ a + b {}` /* comment 4 */ } /* comment 5 */ - `, - { minify: true }, - ) - let expected = `selector{}@media (min-width:1000px){selector{}}` - expect(actual).toEqual(expected) - }) -}) + `, { minify: true }); + let expected = `selector{}@media (min-width:1000px){selector{}}`; + expect(actual).toEqual(expected); + }); +}); diff --git a/test/declarations.test.js b/test/declarations.test.js index 5059d7c..fb56f49 100644 --- a/test/declarations.test.js +++ b/test/declarations.test.js @@ -1,7 +1,7 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; test('Declarations end with a semicolon (;)', () => { - let actual = format(` + let actual = format(` @font-face { src: url('test'); font-family: Test @@ -24,8 +24,8 @@ test('Declarations end with a semicolon (;)', () => { } } } - `) - let expected = `@font-face { + `); + let expected = `@font-face { src: url("test"); font-family: Test; } @@ -46,50 +46,50 @@ css { property1: value5; } } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('lowercases properties', () => { - let actual = format(`a { COLOR: green }`) - let expected = `a { + let actual = format(`a { COLOR: green }`); + let expected = `a { color: green; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not lowercase custom properties', () => { - let actual = format(`a { + let actual = format(`a { --myVar: 1; - }`) - let expected = `a { + }`); + let expected = `a { --myVar: 1; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('!important is added', () => { - let actual = format(`a { color: green !important}`) - let expected = `a { + let actual = format(`a { color: green !important}`); + let expected = `a { color: green !important; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('!important is lowercase', () => { - let actual = format(`a { color: green !IMPORTANT }`) - let expected = `a { + let actual = format(`a { color: green !IMPORTANT }`); + let expected = `a { color: green !important; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('browserhack !ie is printed', () => { - let actual = format(`a { color: green !ie}`) - let expected = `a { + let actual = format(`a { color: green !ie}`); + let expected = `a { color: green !ie; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('browserhack !IE is lowercased', () => { - let actual = format(`a { color: green !IE}`) - let expected = `a { + let actual = format(`a { color: green !IE}`); + let expected = `a { color: green !ie; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); diff --git a/test/minify.test.js b/test/minify.test.js index ab40852..aea635b 100644 --- a/test/minify.test.js +++ b/test/minify.test.js @@ -1,49 +1,49 @@ -import { test, expect } from 'vitest' -import { minify } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { minify } from '../src/lib/index.js'; test('empty rule', () => { - let actual = minify(`a {}`) - let expected = `a{}` - expect(actual).toEqual(expected) -}) + let actual = minify(`a {}`); + let expected = `a{}`; + expect(actual).toEqual(expected); +}); test('simple declaration', () => { - let actual = minify(`:root { --color: red; }`) - let expected = `:root{--color:red}` - expect(actual).toEqual(expected) -}) + let actual = minify(`:root { --color: red; }`); + let expected = `:root{--color:red}`; + 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}}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@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){}` - expect(actual).toEqual(expected) -}) + let actual = minify(`@media (min-width: 100px) {}`); + let expected = `@media (min-width:100px){}`; + expect(actual).toEqual(expected); +}); test('formats multiline values on a single line', () => { - let actual = minify(` + let actual = minify(` a { background: linear-gradient( red, 10% blue, 20% green,100% yellow); } - `) - let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}` - expect(actual).toEqual(expected) -}) + `); + let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}`; + 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%)}` - expect(actual).toEqual(expected) -}) + let actual = minify(`a { width: calc(100% - 10px); height: calc(100 * 1%); }`); + let expected = `a{width:calc(100% - 10px);height:calc(100*1%)}`; + 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)}` - expect(actual).toEqual(expected) -}) + let actual = minify(`a { color: rgb(0 0 0 / 0.1); }`); + let expected = `a{color:rgb(0 0 0/0.1)}`; + expect(actual).toEqual(expected); +}); test('Vadim Makeevs example works', () => { - let actual = minify(` + let actual = minify(` @layer what { @container (width > 0) { ul:has(:nth-child(1 of li)) { @@ -55,29 +55,27 @@ 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}}}}}` - expect(actual).toEqual(expected) -}) + `); + 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}}}}` - expect(actual).toEqual(expected) -}) + 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}}}}`; + expect(actual).toEqual(expected); +}); test('removes whitespace before !important', () => { - let actual = minify(`a { color: green !important }`) - let expected = `a{color:green!important}` - expect(actual).toEqual(expected) -}) + let actual = minify(`a { color: green !important }`); + let expected = `a{color:green!important}`; + expect(actual).toEqual(expected); +}); test('minifies complex selectors', () => { - let actual = minify(`:is(a, b) { color: green }`) - let expected = `:is(a,b){color:green}` - expect(actual).toEqual(expected) -}) + let actual = minify(`:is(a, b) { color: green }`); + let expected = `:is(a,b){color:green}`; + expect(actual).toEqual(expected); +}); test('removes whitespace around non-whitespace selector combinators', () => { - let actual = minify(`a + b {} c d {}`) - let expected = `a+b{}c d{}` - expect(actual).toEqual(expected) -}) + let actual = minify(`a + b {} c d {}`); + let expected = `a+b{}c d{}`; + expect(actual).toEqual(expected); +}); diff --git a/test/rules.test.js b/test/rules.test.js index 86c0fb8..047eb27 100644 --- a/test/rules.test.js +++ b/test/rules.test.js @@ -1,7 +1,7 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; test('AtRules and Rules start on a new line', () => { - let actual = format(` + let actual = format(` selector { property: value; } @media (min-width: 1000px) { selector { property: value; } @@ -10,8 +10,8 @@ test('AtRules and Rules start on a new line', () => { @layer test { selector { property: value; } } - `) - let expected = `selector { + `); + let expected = `selector { property: value; } @@ -29,31 +29,31 @@ selector { selector { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('An empty line is rendered in between Rules', () => { - let actual = format(` + let actual = format(` rule1 { property: value } rule2 { property: value } - `) - let expected = `rule1 { + `); + let expected = `rule1 { property: value; } rule2 { property: value; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('single empty line after a rule, before atrule', () => { - let actual = format(` + let actual = format(` rule1 { property: value } @media (min-width: 1000px) { rule2 { property: value } } - `) - let expected = `rule1 { + `); + let expected = `rule1 { property: value; } @@ -61,11 +61,11 @@ test('single empty line after a rule, before atrule', () => { rule2 { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('newline between last declaration and nested ruleset', () => { - let actual = format(` + let actual = format(` test { property1: value1; & > item { @@ -75,8 +75,8 @@ test('newline between last declaration and nested ruleset', () => { } } } - `) - let expected = `test { + `); + let expected = `test { property1: value1; & > item { @@ -86,139 +86,139 @@ test('newline between last declaration and nested ruleset', () => { property3: value3; } } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('newline between last declaration and nested atrule', () => { - let actual = format(` + let actual = format(` test { property1: value1; @media all { property2: value2; } } - `) - let expected = `test { + `); + let expected = `test { property1: value1; @media all { property2: value2; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('no trailing newline on empty nested rule', () => { - let actual = format(` + let actual = format(` @layer test { empty {} } - `) - let expected = `@layer test { + `); + let expected = `@layer test { empty {} -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats nested rules with selectors starting with', () => { - let actual = format(` + let actual = format(` selector { & > item { property: value; } } - `) - let expected = `selector { + `); + let expected = `selector { & > item { property: value; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('newlines between declarations, nested rules and more declarations', () => { - let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`) - let expected = `a { + let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`); + let expected = `a { font: 0/0; & b { color: red; } color: green; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats nested rules with a selector starting with &', () => { - let actual = format(` + let actual = format(` selector { & a { color: red; } } - `) - let expected = `selector { + `); + let expected = `selector { & a { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats unknown stuff in curly braces', () => { - let actual = format(` + let actual = format(` selector { { color: red; } } - `) - let expected = `selector { + `); + let expected = `selector { { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` + let actual = format(` selector { a & { color:red } } - `) - let expected = `selector { + `); + let expected = `selector { a & { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` + let actual = format(` selector { a & { color:red } } - `) - let expected = `selector { + `); + let expected = `selector { a & { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('Relaxed nesting: formats nested rules with a selector without a &', () => { - let actual = format(` + let actual = format(` selector { a { color:red } } - `) - let expected = `selector { + `); + let expected = `selector { a { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { - let actual = format(` + let actual = format(` selector { > a { color:red } ~ a { color:red } + a { color:red } } - `) - let expected = `selector { + `); + let expected = `selector { > a { color: red; } @@ -230,16 +230,16 @@ test('Relaxed nesting: formats nested rules with a selector starting with a sele + a { color: red; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('handles syntax errors: unclosed block', () => { - let actual = format(`a { mumblejumble`) - let expected = 'a {}' - expect(actual).toEqual(expected) -}) + let actual = format(`a { mumblejumble`); + let expected = 'a {}'; + expect(actual).toEqual(expected); +}); test('handles syntax errors: premature closed block', () => { - let actual = format(`a { mumblejumble: }`) - let expected = 'a {\n\tmumblejumble: ;\n}' - expect(actual).toEqual(expected) -}) + 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.js index 0424d47..8cd9017 100644 --- a/test/selectors.test.js +++ b/test/selectors.test.js @@ -1,12 +1,12 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; test('A single selector is rendered without a trailing comma', () => { - let actual = format('a {}') - let expected = 'a {}' - expect(actual).toEqual(expected) -}) + let actual = format('a {}'); + let expected = 'a {}'; + expect(actual).toEqual(expected); +}); test('Multiple selectors are placed on a new line, separated by commas', () => { - let actual = format(` + let actual = format(` selector1, selector1a, selector1b, @@ -15,197 +15,195 @@ test('Multiple selectors are placed on a new line, separated by commas', () => { selector3 { } - `) - let expected = `selector1, + `); + let expected = `selector1, selector1a, selector1b, selector1aa, selector2, -selector3 {}` - expect(actual).toEqual(expected) -}) +selector3 {}`; + expect(actual).toEqual(expected); +}); test('formats multiline selectors on a single line', () => { - let actual = format(` + let actual = format(` a.b .c .d .e .f { color: green } - `) - let expected = `a.b .c .d .e .f { + `); + let expected = `a.b .c .d .e .f { color: green; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats simple selector combinators', () => { - let actual = format(` + let actual = format(` a>b, a>b~c d, .article-content ol li>* {} - `) - let expected = `a > b, + `); + let expected = `a > b, a > b ~ c d, -.article-content ol li > * {}` - expect(actual).toEqual(expected) -}) +.article-content ol li > * {}`; + expect(actual).toEqual(expected); +}); test('lowercases type selectors', () => { - let actual = format(` + let actual = format(` A, B, C {} - `) - let expected = `a, + `); + let expected = `a, b, -c {}` - expect(actual).toEqual(expected) -}) +c {}`; + expect(actual).toEqual(expected); +}); test('formats nested selector combinators', () => { - let fixtures = [ - [`:where(a+b) {}`, `:where(a + b) {}`], - [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], - [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], - [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], - ] - for (let [css, expected] of fixtures) { - let actual = format(css) - expect(actual).toEqual(expected) - } -}) + let fixtures = [ + [`:where(a+b) {}`, `:where(a + b) {}`], + [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], + [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], + [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], + ]; + for (let [css, expected] of fixtures) { + let actual = format(css); + expect(actual).toEqual(expected); + } +}); test('formats pseudo selectors', () => { - let css = ` + let css = ` a::before, a::after, b:before, b:after, c::first-letter {} - ` - let expected = `a::before, + `; + let expected = `a::before, a::after, b::before, b::after, -c::first-letter {}` - let actual = format(css) - expect(actual).toEqual(expected) -}) +c::first-letter {}`; + let actual = format(css); + expect(actual).toEqual(expected); +}); test('formats pseudo elements with odd casing', () => { - let css = ` + let css = ` a::Before, a::After, b:Before, b:After, c:After, d::First-letter {} - ` - let expected = `a::before, + `; + let expected = `a::before, a::after, b::before, b::after, c::after, -d::first-letter {}` - let actual = format(css) - expect(actual).toEqual(expected) -}) +d::first-letter {}`; + let actual = format(css); + expect(actual).toEqual(expected); +}); test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], + [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], ])('formats nth selector: %s', (css, expected) => { - let actual = format(css) - expect(actual).toEqual(expected) -}) + let actual = format(css); + expect(actual).toEqual(expected); +}); test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], + [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], + [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], + [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], + [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], + [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], + [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], + [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], ])('minifies nth selector: %s', (css, expected) => { - let actual = format(css, { minify: true }) - expect(actual).toEqual(expected) -}) + let actual = format(css, { minify: true }); + expect(actual).toEqual(expected); +}); test('formats multiline selectors', () => { - let actual = format(` + let actual = format(` a:is( a, b, c ) {} - `) - let expected = `a:is(a, b, c) {}` - expect(actual).toEqual(expected) -}) + `); + let expected = `a:is(a, b, c) {}`; + expect(actual).toEqual(expected); +}); test('format nesting selectors', () => { - let actual = format(` + let actual = format(` & a {} b & c {} - `) - let expected = `& a {} + `); + let expected = `& a {} -b & c {}` - expect(actual).toEqual(expected) -}) +b & c {}`; + expect(actual).toEqual(expected); +}); test('prints all possible attribute selectors', () => { - let actual = format(` + let actual = format(` [title="test"], [title|="test"], [title^="test"], [title*="test"], [title$="test"], [title~="test"] {} - `) - let expected = `[title="test"], + `); + let expected = `[title="test"], [title|="test"], [title^="test"], [title*="test"], [title$="test"], -[title~="test"] {}` - expect(actual).toEqual(expected) -}) +[title~="test"] {}`; + expect(actual).toEqual(expected); +}); test('forces attribute selectors to have quoted values', () => { - let actual = format(` + let actual = format(` [title=foo], [title="bar"], [title='baz'] {} - `) - let expected = `[title="foo"], + `); + let expected = `[title="foo"], [title="bar"], -[title="baz"] {}` - expect(actual).toEqual(expected) -}) +[title="baz"] {}`; + expect(actual).toEqual(expected); +}); test('adds a space before attribute selector flags', () => { - let actual = format(` + let actual = format(` [title="foo" i], [title="baz"i], [title=foo S] {} - `) - let expected = `[title="foo" i], + `); + let expected = `[title="foo" i], [title="baz" i], -[title="foo" s] {}` - expect(actual).toEqual(expected) -}) +[title="foo" s] {}`; + 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() {}` - expect(actual).toEqual(expected) -}) + let actual = format(`:lang("nl","de"),li:nth-child() {}`); + let expected = `:lang("nl", "de"), +li:nth-child() {}`; + expect(actual).toEqual(expected); +}); test(`formats ::highlight and ::highlight(Name) correctly`, () => { - let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`) - let expected = `::highlight, + let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`); + let expected = `::highlight, ::highlight(Name), -::highlight(my-thing) {}` - expect(actual).toEqual(expected) -}) +::highlight(my-thing) {}`; + expect(actual).toEqual(expected); +}); test('formats keyframes selectors (50%) correctly', () => { - let actual = format( - `@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`, - ) - let expected = `@keyframes Toastify__bounceInUp { + let actual = format(`@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`); + let expected = `@keyframes Toastify__bounceInUp { 0% { animation-timing-function: cubic-bezier(.215, .61, .355, 1); } @@ -214,25 +212,25 @@ test('formats keyframes selectors (50%) correctly', () => { 80%, to {} -}` - expect(actual).toBe(expected) -}) +}`; + expect(actual).toBe(expected); +}); test('formats unknown pseudos correctly', () => { - let actual = format(` + let actual = format(` ::foo-bar, :unkown-thing(), :unnowkn(kjsa.asddk,asd) {} - `) - let expected = `::foo-bar, + `); + let expected = `::foo-bar, :unkown-thing(), -:unnowkn(kjsa.asddk, asd) {}` - expect(actual).toEqual(expected) -}) +:unnowkn(kjsa.asddk, asd) {}`; + expect(actual).toEqual(expected); +}); test('handles syntax errors', () => { - let actual = format(` + let actual = format(` test, @test {} - `) - let expected = `test {}` - expect(actual).toEqual(expected) -}) + `); + let expected = `test {}`; + expect(actual).toEqual(expected); +}); diff --git a/test/tab-size.test.js b/test/tab-size.test.js index 9d3f816..6eb2a23 100644 --- a/test/tab-size.test.js +++ b/test/tab-size.test.js @@ -1,13 +1,12 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; let fixture = ` selector { color: red; } -` +`; test('tab_size: 2', () => { - let actual = format( - ` + let actual = format(` selector { color: red; } @@ -17,10 +16,8 @@ test('tab_size: 2', () => { color: blue; } } - `, - { tab_size: 2 }, - ) - let expected = `selector { + `, { tab_size: 2 }); + let expected = `selector { color: red; } @@ -28,20 +25,20 @@ test('tab_size: 2', () => { selector { color: blue; } -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('invalid tab_size: 0', () => { - expect(() => format(fixture, { tab_size: 0 })).toThrow() -}) + expect(() => format(fixture, { tab_size: 0 })).toThrow(); +}); test('invalid tab_size: negative', () => { - expect(() => format(fixture, { tab_size: -1 })).toThrow() -}) + expect(() => format(fixture, { tab_size: -1 })).toThrow(); +}); test('combine tab_size and minify', () => { - let actual = format(fixture, { - tab_size: 2, - minify: true, - }) - let expected = `selector{color:red}` - expect(actual).toEqual(expected) -}) + let actual = format(fixture, { + tab_size: 2, + minify: true, + }); + let expected = `selector{color:red}`; + expect(actual).toEqual(expected); +}); diff --git a/test/values.test.js b/test/values.test.js index ee175d6..4388212 100644 --- a/test/values.test.js +++ b/test/values.test.js @@ -1,20 +1,20 @@ -import { test, expect } from 'vitest' -import { format } from '../src/lib/index.js' +import { test, expect } from 'vitest'; +import { format } from '../src/lib/index.js'; test('collapses abundant whitespace', () => { - let actual = format(`a { + let actual = format(`a { transition: all 100ms ease; color: rgb( 0 , 0 , 0 ); color: red ; - }`) - let expected = `a { + }`); + let expected = `a { transition: all 100ms ease; color: rgb(0, 0, 0); color: red; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats simple value lists', () => { - let actual = format(` + let actual = format(` a { transition-property: all,opacity; transition: all 100ms ease,opacity 10ms 20ms linear; @@ -24,8 +24,8 @@ test('formats simple value lists', () => { content: 'Test'; background-image: url("EXAMPLE.COM"); } - `) - let expected = `a { + `); + let expected = `a { transition-property: all, opacity; transition: all 100ms ease, opacity 10ms 20ms linear; animation: COLOR 123ms EASE-OUT; @@ -33,35 +33,35 @@ test('formats simple value lists', () => { color: hsl(0%, 10%, 50%); content: "Test"; background-image: url("EXAMPLE.COM"); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats nested value lists', () => { - let actual = format(` + let actual = format(` a { background: red,linear-gradient(to bottom,red 10%,green 50%,blue 100%); } - `) - let expected = `a { + `); + let expected = `a { background: red, linear-gradient(to bottom, red 10%, green 50%, blue 100%); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats nested var()', () => { - let actual = format(` + let actual = format(` a { color: var(--test1,var(--test2,green)); color: var(--test3,rgb(0,0,0)); } - `) - let expected = `a { + `); + let expected = `a { color: var(--test1, var(--test2, green)); color: var(--test3, rgb(0, 0, 0)); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats multiline values on a single line', () => { - let actual = format(` + let actual = format(` a { background: linear-gradient( red, @@ -73,175 +73,172 @@ a { 0 ); } - `) - let expected = `a { + `); + let expected = `a { background: linear-gradient(red, 10% blue, 20% green, 100% yellow); color: rgb(0, 0, 0); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not break font shorthand', () => { - let actual = format(`a { + let actual = format(`a { font: 2em/2 sans-serif; font: 2em/ 2 sans-serif; font: 2em / 2 sans-serif; - }`) - let expected = `a { + }`); + let expected = `a { font: 2em/2 sans-serif; font: 2em/2 sans-serif; font: 2em/2 sans-serif; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats whitespace around operators (*/+-) correctly', () => { - let actual = format(`a { + let actual = format(`a { font: 2em/2 sans-serif; font-size: calc(2em/2); font-size: calc(2em * 2); font-size: calc(2em + 2px); font-size: calc(2em - 2px); -}`) - let expected = `a { +}`); + let expected = `a { font: 2em/2 sans-serif; font-size: calc(2em / 2); font-size: calc(2em * 2); font-size: calc(2em + 2px); font-size: calc(2em - 2px); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats whitespace around operators (*/+-) correctly in nested parenthesis', () => { - let actual = format(`a { + let actual = format(`a { width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); -}`) - let expected = `a { +}`); + let expected = `a { width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats parenthesis correctly', () => { - let actual = format(`a { + let actual = format(`a { width: calc(100% - var(--x)); width: calc((100% - var(--x))); width: calc(100% - (var(--x))); width: calc((100% - (var(--x)))); -}`) - let expected = `a { +}`); + let expected = `a { width: calc(100% - var(--x)); width: calc((100% - var(--x))); width: calc(100% - (var(--x))); width: calc((100% - (var(--x)))); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not lowercase grid-area names', () => { - let actual = format(`a { grid-area: emailInputBox; }`) - let expected = `a { + let actual = format(`a { grid-area: emailInputBox; }`); + let expected = `a { grid-area: emailInputBox; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not lowercase custom properties in var()', () => { - let actual = format(`a { color: var(--MyColor); }`) - let expected = `a { + let actual = format(`a { color: var(--MyColor); }`); + let expected = `a { color: var(--MyColor); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('lowercases CSS functions', () => { - let actual = format(`a { + let actual = format(`a { color: RGB(0, 0, 0); transform: translateX(100px); - }`) - let expected = `a { + }`); + let expected = `a { color: rgb(0, 0, 0); transform: translatex(100px); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('relative colors', () => { - let actual = format(`a { + let actual = format(`a { color: rgb( from red 0 0 255); color: rgb( from rgb( 200 0 0 ) r r r ) ; color: hwb( from var( --base-color ) h w b / var( --standard-opacity ) ) ; color: lch(from var(--base-color) calc(l + 20) c h); - }`) - let expected = `a { + }`); + let expected = `a { color: rgb(from red 0 0 255); color: rgb(from rgb(200 0 0) r r r); color: hwb(from var(--base-color) h w b / var(--standard-opacity)); color: lch(from var(--base-color) calc(l + 20) c h); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not change casing of `NaN`', () => { - let actual = format(`a { + let actual = format(`a { height: calc(1 * NaN); - }`) - let expected = `a { + }`); + let expected = `a { height: calc(1 * NaN); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not change casing of URLs', () => { - let actual = format(`a { + let actual = format(`a { background-image: url("My-Url.png"); - }`) - let expected = `a { + }`); + let expected = `a { background-image: url("My-Url.png"); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('lowercases dimensions', () => { - let actual = format(`a { + let actual = format(`a { font-size: 12PX; width: var(--test, 33REM); - }`) - let expected = `a { + }`); + let expected = `a { font-size: 12px; width: var(--test, 33rem); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('formats unknown content in value', () => { - let actual = format(`a { + let actual = format(`a { content: 'Test' counter(page); - }`) - let expected = `a { + }`); + let expected = `a { content: "Test" counter(page); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not break space toggles', () => { - let actual = format(`a { + let actual = format(`a { --ON: initial; --OFF: ; - }`) - let expected = `a { + }`); + let expected = `a { --ON: initial; --OFF: ; -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test('does not break space toggles (minified)', () => { - let actual = format( - `a { + let actual = format(`a { --ON: initial; --OFF: ; - }`, - { minify: true }, - ) - let expected = `a{--ON:initial;--OFF: }` - expect(actual).toEqual(expected) -}) + }`, { minify: true }); + let expected = `a{--ON:initial;--OFF: }`; + expect(actual).toEqual(expected); +}); test('adds quotes around strings in url()', () => { - let actual = format(`a { + let actual = format(`a { background-image: url("star.gif"); list-style-image: url('../images/bullet.jpg'); content: url("pdficon.jpg"); @@ -250,8 +247,8 @@ test('adds quotes around strings in url()', () => { src: url('fantasticfont.woff'); offset-path: url(#path); mask-image: url("masks.svg#mask1"); - }`) - let expected = `a { + }`); + let expected = `a { background-image: url("star.gif"); list-style-image: url("../images/bullet.jpg"); content: url("pdficon.jpg"); @@ -260,57 +257,57 @@ test('adds quotes around strings in url()', () => { src: url("fantasticfont.woff"); offset-path: url("#path"); mask-image: url("masks.svg#mask1"); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test.each([ - `data:image/svg+xml;utf8,`, - `data:image/svg+xml;utf8,`, + `data:image/svg+xml;utf8,`, + `data:image/svg+xml;utf8,`, ])('Does not mess up URLs with inlined SVG', (input) => { - let actual = format(`test { + let actual = format(`test { background-image: url('${input}'); background-image: url(${input}); - }`) - let expected = `test { + }`); + let expected = `test { background-image: url(${input}); background-image: url(${input}); -}` - expect(actual).toEqual(expected) -}) +}`; + expect(actual).toEqual(expected); +}); test.each([ - // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data - 'data:,Hello%2C%20World%21', - 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', - 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', - 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', - // from https://github.com/projectwallace/format-css/issues/144 - `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, + // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data + 'data:,Hello%2C%20World%21', + 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', + 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', + 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', + // from https://github.com/projectwallace/format-css/issues/144 + `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, ])('Does not mess up URLs with encoded inlined content: %s', (input) => { - let actual = format(`test { + let actual = format(`test { background-image: url(${input}); - }`) - let expected = `test { + }`); + let expected = `test { background-image: url(${input}); -}` - expect(actual).toBe(expected) -}) +}`; + expect(actual).toBe(expected); +}); test.each([ - `U+26`, // single code point - `U+0-7F`, - `U+0025-00FF`, // code point range - `U+4??`, // wildcard range - `U+0025-00FF, U+4??`, // multiple values + `U+26`, // single code point + `U+0-7F`, + `U+0025-00FF`, // code point range + `U+4??`, // wildcard range + `U+0025-00FF, U+4??`, // multiple values ])('Formats unicode-range: %s', (unicode_range) => { - let actual = format(`test { unicode-range: ${unicode_range}; }`) - let expected = `test { + let actual = format(`test { unicode-range: ${unicode_range}; }`); + let expected = `test { unicode-range: ${unicode_range}; -}` - expect(actual).toBe(expected) -}) +}`; + expect(actual).toBe(expected); +}); test('formats multi-value unicode range', () => { - let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`) - let expected = `test { + let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`); + let expected = `test { unicode-range: U+0025-00FF, U+4??; -}` - expect(actual).toBe(expected) -}) +}`; + expect(actual).toBe(expected); +}); diff --git a/tsconfig.json b/tsconfig.json index e3b1b20..70de516 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,26 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - "types": ["node"], - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "rootDir": "src", - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + "types": ["node"], + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "rootDir": "src", + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] } diff --git a/tsdown.config.js b/tsdown.config.js index 2a97b23..5e421b3 100644 --- a/tsdown.config.js +++ b/tsdown.config.js @@ -1,32 +1,32 @@ -import { defineConfig } from 'tsdown' -import { codecovRollupPlugin } from '@codecov/rollup-plugin' +import { defineConfig } from 'tsdown'; +import { codecovRollupPlugin } from '@codecov/rollup-plugin'; export default defineConfig([ - { - entry: 'src/lib/index.ts', - platform: 'neutral', - publint: true, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCss', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, - { - entry: 'src/cli/cli.ts', - platform: 'node', - dts: false, - // Reference the lib via its package name to avoid bundling it twice - deps: { - neverBundle: ['@projectwallace/format-css'], - }, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCssCli', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, -]) + { + entry: 'src/lib/index.ts', + platform: 'neutral', + publint: true, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCss', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, + { + entry: 'src/cli/cli.ts', + platform: 'node', + dts: false, + // Reference the lib via its package name to avoid bundling it twice + deps: { + neverBundle: ['@projectwallace/format-css'], + }, + plugins: [ + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'formatCssCli', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + }, +]); diff --git a/vitest.config.js b/vitest.config.js index 6f9ea30..cd426a2 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,14 +1,14 @@ -import { defineConfig } from 'vitest/config' -import { resolve } from 'node:path' +import { defineConfig } from 'vitest/config'; +import { resolve } from 'node:path'; export default defineConfig({ - resolve: { - alias: { - '@projectwallace/format-css': resolve('./src/lib/index.ts'), - }, - }, - test: { - coverage: { - provider: 'v8', - }, - }, -}) + resolve: { + alias: { + '@projectwallace/format-css': resolve('./src/lib/index.ts'), + }, + }, + test: { + coverage: { + provider: 'v8', + }, + }, +}); From 4866569b61d954ff81bde4ec162ab16ca1f3d6c1 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 13:29:40 +0200 Subject: [PATCH 5/7] Revert "fix types" This reverts commit 98af0b099a89e4f9f55300c86067f2ac92f28586. --- test/api.test.js | 52 ---- test/atrules.test.js | 351 ------------------------- test/comments.test.js | 525 -------------------------------------- test/declarations.test.js | 95 ------- test/minify.test.js | 81 ------ test/rules.test.js | 245 ------------------ test/selectors.test.js | 236 ----------------- test/tab-size.test.js | 44 ---- test/values.test.js | 313 ----------------------- tsconfig.json | 48 ++-- tsdown.config.js | 32 --- vitest.config.js | 14 - 12 files changed, 24 insertions(+), 2012 deletions(-) delete mode 100644 test/api.test.js delete mode 100644 test/atrules.test.js delete mode 100644 test/comments.test.js delete mode 100644 test/declarations.test.js delete mode 100644 test/minify.test.js delete mode 100644 test/rules.test.js delete mode 100644 test/selectors.test.js delete mode 100644 test/tab-size.test.js delete mode 100644 test/values.test.js delete mode 100644 tsdown.config.js delete mode 100644 vitest.config.js diff --git a/test/api.test.js b/test/api.test.js deleted file mode 100644 index 1cf0bcc..0000000 --- a/test/api.test.js +++ /dev/null @@ -1,52 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -test('empty input', () => { - let actual = format(``); - let expected = ``; - expect(actual).toEqual(expected); -}); -test('handles invalid input', () => { - let actual = format(`;`); - let expected = ``; - expect(actual).toEqual(expected); -}); -test('Vadim Makeevs example works', () => { - let actual = format(` - @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 = format(`@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); -}); diff --git a/test/atrules.test.js b/test/atrules.test.js deleted file mode 100644 index c580233..0000000 --- a/test/atrules.test.js +++ /dev/null @@ -1,351 +0,0 @@ -import { test, expect } from 'vitest'; -import { format, minify } from '../src/lib/index.js'; -test('AtRules start on a new line', () => { - let actual = format(` - @media (min-width: 1000px) { - selector { property: value; } - } - @layer test { - selector { property: value; } - } - `); - let expected = `@media (min-width: 1000px) { - selector { - property: value; - } -} - -@layer test { - selector { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('Atrule blocks are surrounded by {} with correct spacing and indentation', () => { - let actual = format(` - @media (min-width:1000px){selector{property:value1}} - - @media (min-width:1000px) - { - selector - { - property:value2 - } -}`); - let expected = `@media (min-width: 1000px) { - selector { - property: value1; - } -} - -@media (min-width: 1000px) { - selector { - property: value2; - } -}`; - expect(actual).toEqual(expected); -}); -test('adds whitespace between prelude and {', () => { - let actual = format(`@media all{}`); - let expected = `@media all {}`; - 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) {}`; - expect(actual).toEqual(expected); -}); -test('removes newlines in prelude', () => { - let actual = format(`@media - all, - screen, - print, - (min-width: 1000px) {}`); - let expected = `@media all, screen, print, (min-width: 1000px) {}`; - 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) {}`; - 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) {}`; - expect(actual).toEqual(expected); -}); -test('formats @layer with excess whitespace', () => { - let actual = format(`@layer test;`); - let expected = `@layer test;`; - expect(actual).toEqual(expected); -}); -test('adds whitespace to @layer tbody,thead', () => { - let actual = format(`@layer tbody,thead;`); - let expected = `@layer tbody, thead;`; - expect(actual).toEqual(expected); -}); -test('adds whitespace to @supports (display:grid)', () => { - let actual = format(`@supports (display:grid){}`); - let expected = `@supports (display: grid) {}`; - expect(actual).toEqual(expected); -}); -test('@media prelude formatting', () => { - let fixtures = [ - [`@media all and (transform-3d) {}`, `@media all and (transform-3d) {}`], - [ - `@media only screen and (min-width: 1024px)and (max-width: 1439px), only screen and (min-width: 768px)and (max-width: 1023px) {}`, - `@media only screen and (min-width: 1024px) and (max-width: 1439px), only screen and (min-width: 768px) and (max-width: 1023px) {}`, - ], - [ - `@media (min-width: 1024px)or (max-width: 1439px) {}`, - `@media (min-width: 1024px) or (max-width: 1439px) {}`, - ], - [`@media (width>=44rem)or (width<=33rem) {}`, `@media (width >= 44rem) or (width <= 33rem) {}`], - [ - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - `@media all and (transform-3d), (-webkit-transform-3d) {}`, - ], - [`@media screen or print {}`, `@media screen or print {}`], - [`@media (update: slow) or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [`@media (update: slow)or (hover: none) {}`, `@media (update: slow) or (hover: none) {}`], - [ - `@media all and (-moz-images-in-menus:0) and (min-resolution:.001dpcm) {}`, - `@media all and (-moz-images-in-menus: 0) and (min-resolution: .001dpcm) {}`, - ], - [ - `@media all and (-webkit-min-device-pixel-ratio: 10000),not all and (-webkit-min-device-pixel-ratio: 0) {}`, - `@media all and (-webkit-min-device-pixel-ratio: 10000), not all and (-webkit-min-device-pixel-ratio: 0) {}`, - ], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); -test('lowercases functions inside atrule preludes', () => { - let actual = format(` -@import URL("style.css") LAYER(test) SUPPORTS(display:grid); -@supports SELECTOR([popover]:open) {} -`); - let expected = `@import url("style.css") layer(test) supports(display: grid); - -@supports selector([popover]:open) {}`; - expect(actual).toEqual(expected); -}); -test('formats @scope', () => { - let actual = format(` - @scope (.light-scheme) {} - @scope (.media-object) to (.content > *) {} -`); - let expected = `@scope (.light-scheme) {} - -@scope (.media-object) to (.content > *) {}`; - expect(actual).toEqual(expected); -}); -test('calc() inside @media', () => { - let actual = format(` - @media (min-width: calc(1px*1)) {} - @media (min-width: calc(2px* 2)) {} - @media (min-width: calc(3px *3)) {} - @media (min-width: calc(4px * 4)) {} - @media (min-width: calc(5px * 5)) {} - `); - let expected = `@media (min-width: calc(1px * 1)) {} - -@media (min-width: calc(2px * 2)) {} - -@media (min-width: calc(3px * 3)) {} - -@media (min-width: calc(4px * 4)) {} - -@media (min-width: calc(5px * 5)) {}`; - 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)){}`; - 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)){}`; - 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)){}`; - 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);', - ], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); -test('@supports prelude formatting', () => { - let fixtures = [ - [`@supports (display:grid){}`, `@supports (display: grid) {}`], - [`@supports (-webkit-appearance: none) {}`, `@supports (-webkit-appearance: none) {}`], - ['@supports selector([popover]:open) {}', '@supports selector([popover]:open) {}'], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); -test('@layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody, thead;`], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); -test('minify: @layer prelude formatting', () => { - let fixtures = [ - [`@layer test;`, `@layer test;`], - [`@layer tbody,thead;`, `@layer tbody,thead;`], - ]; - for (let [css, expected] of fixtures) { - let actual = minify(css); - expect(actual).toEqual(expected); - } -}); -test('single empty line after a rule, before atrule', () => { - let actual = format(` - rule1 { property: value } - @media (min-width: 1000px) { - rule2 { property: value } - } - `); - let expected = `rule1 { - property: value; -} - -@media (min-width: 1000px) { - rule2 { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('single empty line in between atrules', () => { - let actual = format(` - @layer test1; - @media (min-width: 1000px) { - rule2 { property: value } - } - `); - let expected = `@layer test1; - -@media (min-width: 1000px) { - rule2 { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('newline between last declaration and nested atrule', () => { - let actual = format(` - test { - property1: value1; - @media all { - property2: value2; - } - } - `); - let expected = `test { - property1: value1; - - @media all { - property2: value2; - } -}`; - expect(actual).toEqual(expected); -}); -test('lowercases the atrule name', () => { - let actual = format(`@LAYER test {}`); - let expected = `@layer test {}`; - expect(actual).toEqual(expected); -}); -test('does not lowercase the atrule value', () => { - let actual = format('@keyframes TEST {}'); - let expected = '@keyframes TEST {}'; - expect(actual).toEqual(expected); -}); -test('Atrules w/o Block are terminated with a semicolon', () => { - let actual = format(` - @layer test; - @import url('test'); - `); - let expected = `@layer test; - -@import url('test');`; - expect(actual).toEqual(expected); -}); -test('Empty atrule braces are placed on the same line', () => { - let actual = format(`@media all { - - } - - @supports (display: grid) {}`); - let expected = `@media all {} - -@supports (display: grid) {}`; - expect(actual).toEqual(expected); -}); -test('new-fangled comparators (width > 1000px)', () => { - let actual = format(` - @container (width>1000px) {} - @media (width>1000px) {} - @media (width=>1000px) {} - @media (width<=1000px) {} - @media (200px 1000px) {} - -@media (width > 1000px) {} - -@media (width => 1000px) {} - -@media (width <= 1000px) {} - -@media (200px < width < 1000px) {}`; - expect(actual).toEqual(expected); -}); -test('minify: new-fangled comparators (width > 1000px)', () => { - let actual = minify(`@container (width>1000px) {}`); - let expected = `@container (width>1000px){}`; - expect(actual).toEqual(expected); -}); -test.skip('preserves comments', () => { - let actual = format(` - @media /* comment */ all {} - @media all /* comment */ {} - @media (min-width: 1000px /* comment */) {} - @media (/* comment */ min-width: 1000px) {} - @layer /* comment */ {} - `); - let expected = `@media /* comment */ all {} - -@media all /* comment */ {} - -@media (min-width: 1000px /* comment */) {} - -@media (/* comment */ min-width: 1000px) {} - -@layer /* comment */ {} -`; - expect(actual).toEqual(expected); -}); diff --git a/test/comments.test.js b/test/comments.test.js deleted file mode 100644 index c953e93..0000000 --- a/test/comments.test.js +++ /dev/null @@ -1,525 +0,0 @@ -import { describe, test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -describe('comments', () => { - test('only comment', () => { - let actual = format(`/* comment */`); - let expected = `/* comment */`; - expect(actual).toEqual(expected); - }); - test('bang comment before rule', () => { - let actual = format(` - /*! comment */ - selector {} - `); - let expected = `/*! comment */ -selector {}`; - expect(actual).toEqual(expected); - }); - test('before selectors', () => { - let actual = format(` - /* comment */ - selector1, - selector2 { - property: value; - } - `); - let expected = `/* comment */ -selector1, -selector2 { - property: value; -}`; - expect(actual).toEqual(expected); - }); - test('before nested selectors', () => { - let actual = format(` - a { - /* comment */ - & nested1, - & nested2 { - property: value; - } - } - `); - let expected = `a { - /* comment */ - & nested1, - & nested2 { - property: value; - } -}`; - expect(actual).toEqual(expected); - }); - test('after selectors', () => { - let actual = format(` - selector1, - selector2 - /* comment */ { - property: value; - } - `); - let expected = `selector1, -selector2 -/* comment */ { - property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between selectors', () => { - let actual = format(` - selector1, - /* comment */ - selector2 { - property: value; - } - `); - let expected = `selector1, -/* comment */ -selector2 { - property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between nested selectors', () => { - let actual = format(` - a { - & nested1, - /* comment */ - & nested2 { - property: value; - } - } - `); - let expected = `a { - & nested1, - /* comment */ - & nested2 { - property: value; - } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in rule', () => { - let actual = format(` - selector { - /* comment */ - property: value; - } - `); - let expected = `selector { - /* comment */ - property: value; -}`; - expect(actual).toEqual(expected); - }); - test('as last child in rule', () => { - let actual = format(` - selector { - property: value; - /* comment */ - } - `); - let expected = `selector { - property: value; - /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as last child in nested rule', () => { - let actual = format(` - a { - & selector { - property: value; - /* comment */ - } - } - `); - let expected = `a { - & selector { - property: value; - /* comment */ - } -}`; - expect(actual).toEqual(expected); - }); - test('as only child in rule', () => { - let actual = format(` - selector { - /* comment */ - } - `); - let expected = `selector { - /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as only child in nested rule', () => { - let actual = format(`a { - & selector { - /* comment */ - } -}`); - let expected = `a { - & selector { - /* comment */ - } -}`; - expect(actual).toEqual(expected); - }); - test('in between declarations', () => { - let actual = format(` - selector { - property: value; - /* comment */ - property: value; - } - `); - let expected = `selector { - property: value; - /* comment */ - property: value; -}`; - expect(actual).toEqual(expected); - }); - test('in between nested declarations', () => { - let actual = format(` - a { - & selector { - property: value; - /* comment */ - property: value; - } - } - `); - let expected = `a { - & selector { - property: value; - /* comment */ - property: value; - } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in atrule', () => { - let actual = format(` - @media (min-width: 1000px) { - /* comment */ - selector { - property: value; - } - } - `); - let expected = `@media (min-width: 1000px) { - /* comment */ - selector { - property: value; - } -}`; - expect(actual).toEqual(expected); - }); - test('as first child in nested atrule', () => { - let actual = format(` - @media all { - @media (min-width: 1000px) { - /* comment */ - selector { - property: value; - } - } - } - `); - let expected = `@media all { - @media (min-width: 1000px) { - /* comment */ - selector { - property: value; - } - } -}`; - expect(actual).toEqual(expected); - }); - test('as last child in atrule', () => { - let actual = format(` - @media (min-width: 1000px) { - selector { - property: value; - } - /* comment */ - } - `); - let expected = `@media (min-width: 1000px) { - selector { - property: value; - } - /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as last child in nested atrule', () => { - let actual = format(` - @media all { - @media (min-width: 1000px) { - selector { - property: value; - } - /* comment */ - } - } - `); - let expected = `@media all { - @media (min-width: 1000px) { - selector { - property: value; - } - /* comment */ - } -}`; - expect(actual).toEqual(expected); - }); - test('as only child in atrule', () => { - let actual = format(` - @media (min-width: 1000px) { - /* comment */ - } - `); - let expected = `@media (min-width: 1000px) { - /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('as only child in nested atrule', () => { - let actual = format(` - @media all { - @media (min-width: 1000px) { - /* comment */ - } - } - `); - let expected = `@media all { - @media (min-width: 1000px) { - /* comment */ - } -}`; - expect(actual).toEqual(expected); - }); - test('in between rules and atrules', () => { - let actual = format(` - /* comment 1 */ - selector {} - /* comment 2 */ - @media (min-width: 1000px) { - /* comment 3 */ - selector {} - /* comment 4 */ - } - /* comment 5 */ - `); - let expected = `/* comment 1 */ -selector {} -/* comment 2 */ -@media (min-width: 1000px) { - /* comment 3 */ - selector {} - /* comment 4 */ -} -/* comment 5 */`; - expect(actual).toEqual(expected); - }); - test('comment before rule and atrule should not be separated by newline', () => { - let actual = format(` - /* comment 1 */ - selector {} - - /* comment 2 */ - @media (min-width: 1000px) { - /* comment 3 */ - - selector {} - /* comment 4 */ - } - `); - let expected = `/* comment 1 */ -selector {} -/* comment 2 */ -@media (min-width: 1000px) { - /* comment 3 */ - selector {} - /* comment 4 */ -}`; - expect(actual).toEqual(expected); - }); - test('a declaration after multiple comments starts on a new line', () => { - let actual = format(` - selector { - /* comment 1 */ - /* comment 2 */ - --custom-property: value; - - /* comment 3 */ - /* comment 4 */ - --custom-property: value; - - /* comment 5 */ - /* comment 6 */ - --custom-property: value; - } - `); - let expected = `selector { - /* comment 1 */ - /* comment 2 */ - --custom-property: value; - /* comment 3 */ - /* comment 4 */ - --custom-property: value; - /* comment 5 */ - /* comment 6 */ - --custom-property: value; -}`; - expect(actual).toEqual(expected); - }); - test('multiple comments in between rules and atrules', () => { - let actual = format(` - /* comment 1 */ - /* comment 1.1 */ - selector {} - /* comment 2 */ - /* comment 2.1 */ - @media (min-width: 1000px) { - /* comment 3 */ - /* comment 3.1 */ - selector {} - /* comment 4 */ - /* comment 4.1 */ - } - /* comment 5 */ - /* comment 5.1 */ - `); - let expected = `/* comment 1 */ -/* comment 1.1 */ -selector {} -/* comment 2 */ -/* comment 2.1 */ -@media (min-width: 1000px) { - /* comment 3 */ - /* comment 3.1 */ - selector {} - /* comment 4 */ - /* comment 4.1 */ -} -/* comment 5 */ -/* comment 5.1 */`; - expect(actual).toEqual(expected); - }); - test('puts every comment on a new line', () => { - let actual = format(` - x { - /*--font-family: inherit;*/ /*--font-style: normal;*/ - --border-top-color: var(--root-color--support); - } -`); - let expected = `x { - /*--font-family: inherit;*/ - /*--font-style: normal;*/ - --border-top-color: var(--root-color--support); -}`; - 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*/) {}'; - 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*/) {}'; - expect(actual).toEqual(expected); - }); - test('skip in @import prelude before specifier', () => { - let actual = format('@import /*test*/"foo";'); - let expected = '@import "foo";'; - expect(actual).toEqual(expected); - }); - test('skip in @import prelude after specifier', () => { - let actual = format('@import "foo"/*test*/;'); - let expected = '@import "foo";'; - expect(actual).toEqual(expected); - }); - test('skip in selector combinator', () => { - let actual = format(` - a/*test*/ /*test*/b, - a/*test*/+/*test*/b {} - `); - let expected = `a /*test*/ /*test*/ b, -a + b {}`; - expect(actual).toEqual(expected); - }); - test('in attribute selector', () => { - let actual = format(`[/*test*/a='b' i/*test*/] {}`); - let expected = `[a="b" i] {}`; - expect(actual).toEqual(expected); - }); - test('skip in var() with fallback', () => { - let actual = format(`a { prop: var( /* 1 */ --name /* 2 */ , /* 3 */ 1 /* 4 */ ) }`); - let expected = `a { - prop: var(--name, 1); -}`; - expect(actual).toEqual(expected); - }); - test('skip in custom property declaration (space toggle)', () => { - let actual = format(`a { --test: /*test*/; }`); - let expected = `a { - --test: ; -}`; - expect(actual).toEqual(expected); - }); - test('before value', () => { - let actual = format(`a { prop: /*test*/value; }`); - let expected = `a { - prop: value; -}`; - expect(actual).toEqual(expected); - }); - test('after value', () => { - let actual = format(`a { - prop: value/*test*/; - }`); - let expected = `a { - prop: value; -}`; - expect(actual).toEqual(expected); - }); - test('skip in value functions', () => { - let actual = format(` - a { - background-image: linear-gradient(/* comment */red, green); - background-image: linear-gradient(red/* comment */, green); - background-image: linear-gradient(red, green/* comment */); - background-image: linear-gradient(red, green)/* comment */ - } - `); - let expected = `a { - background-image: linear-gradient(red, green); - background-image: linear-gradient(red, green); - background-image: linear-gradient(red, green); - background-image: linear-gradient(red, green); - /* comment */ -}`; - expect(actual).toEqual(expected); - }); - test('strips comments in minification mode', () => { - let actual = format(` - /* comment 1 */ - selector {} - /* comment 2 */ - @media (min-width: 1000px) { - /* comment 3 */ - selector {} - /* comment 4 */ - } - /* comment 5 */ - `, { minify: true }); - let expected = `selector{}@media (min-width:1000px){selector{}}`; - expect(actual).toEqual(expected); - }); -}); diff --git a/test/declarations.test.js b/test/declarations.test.js deleted file mode 100644 index fb56f49..0000000 --- a/test/declarations.test.js +++ /dev/null @@ -1,95 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -test('Declarations end with a semicolon (;)', () => { - let actual = format(` - @font-face { - src: url('test'); - font-family: Test - } - - css { - property1: value1; - property2: value2; - - & .nested { - property1: value3; - property2: value4 - } - } - - @media (min-width: 1000px) { - @layer test { - css { - property1: value5 - } - } - } - `); - let expected = `@font-face { - src: url("test"); - font-family: Test; -} - -css { - property1: value1; - property2: value2; - - & .nested { - property1: value3; - property2: value4; - } -} - -@media (min-width: 1000px) { - @layer test { - css { - property1: value5; - } - } -}`; - expect(actual).toEqual(expected); -}); -test('lowercases properties', () => { - let actual = format(`a { COLOR: green }`); - let expected = `a { - color: green; -}`; - expect(actual).toEqual(expected); -}); -test('does not lowercase custom properties', () => { - let actual = format(`a { - --myVar: 1; - }`); - let expected = `a { - --myVar: 1; -}`; - expect(actual).toEqual(expected); -}); -test('!important is added', () => { - let actual = format(`a { color: green !important}`); - let expected = `a { - color: green !important; -}`; - expect(actual).toEqual(expected); -}); -test('!important is lowercase', () => { - let actual = format(`a { color: green !IMPORTANT }`); - let expected = `a { - color: green !important; -}`; - expect(actual).toEqual(expected); -}); -test('browserhack !ie is printed', () => { - let actual = format(`a { color: green !ie}`); - let expected = `a { - color: green !ie; -}`; - expect(actual).toEqual(expected); -}); -test('browserhack !IE is lowercased', () => { - let actual = format(`a { color: green !IE}`); - let expected = `a { - color: green !ie; -}`; - expect(actual).toEqual(expected); -}); diff --git a/test/minify.test.js b/test/minify.test.js deleted file mode 100644 index aea635b..0000000 --- a/test/minify.test.js +++ /dev/null @@ -1,81 +0,0 @@ -import { test, expect } from 'vitest'; -import { minify } from '../src/lib/index.js'; -test('empty rule', () => { - let actual = minify(`a {}`); - let expected = `a{}`; - expect(actual).toEqual(expected); -}); -test('simple declaration', () => { - let actual = minify(`:root { --color: red; }`); - let expected = `:root{--color:red}`; - 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}}`; - expect(actual).toEqual(expected); -}); -test('empty atrule', () => { - let actual = minify(`@media (min-width: 100px) {}`); - let expected = `@media (min-width:100px){}`; - expect(actual).toEqual(expected); -}); -test('formats multiline values on a single line', () => { - let actual = minify(` -a { - background: linear-gradient( - red, - 10% blue, -20% green,100% yellow); -} - `); - let expected = `a{background:linear-gradient(red,10% blue,20% green,100% yellow)}`; - 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%)}`; - 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)}`; - expect(actual).toEqual(expected); -}); -test('Vadim Makeevs example works', () => { - let actual = minify(` - @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}}}}`; - expect(actual).toEqual(expected); -}); -test('removes whitespace before !important', () => { - let actual = minify(`a { color: green !important }`); - let expected = `a{color:green!important}`; - expect(actual).toEqual(expected); -}); -test('minifies complex selectors', () => { - let actual = minify(`:is(a, b) { color: green }`); - let expected = `:is(a,b){color:green}`; - expect(actual).toEqual(expected); -}); -test('removes whitespace around non-whitespace selector combinators', () => { - let actual = minify(`a + b {} c d {}`); - let expected = `a+b{}c d{}`; - expect(actual).toEqual(expected); -}); diff --git a/test/rules.test.js b/test/rules.test.js deleted file mode 100644 index 047eb27..0000000 --- a/test/rules.test.js +++ /dev/null @@ -1,245 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -test('AtRules and Rules start on a new line', () => { - let actual = format(` - selector { property: value; } - @media (min-width: 1000px) { - selector { property: value; } - } - selector { property: value; } - @layer test { - selector { property: value; } - } - `); - let expected = `selector { - property: value; -} - -@media (min-width: 1000px) { - selector { - property: value; - } -} - -selector { - property: value; -} - -@layer test { - selector { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('An empty line is rendered in between Rules', () => { - let actual = format(` - rule1 { property: value } - rule2 { property: value } - `); - let expected = `rule1 { - property: value; -} - -rule2 { - property: value; -}`; - expect(actual).toEqual(expected); -}); -test('single empty line after a rule, before atrule', () => { - let actual = format(` - rule1 { property: value } - @media (min-width: 1000px) { - rule2 { property: value } - } - `); - let expected = `rule1 { - property: value; -} - -@media (min-width: 1000px) { - rule2 { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('newline between last declaration and nested ruleset', () => { - let actual = format(` - test { - property1: value1; - & > item { - property2: value2; - & + another { - property3: value3; - } - } - } - `); - let expected = `test { - property1: value1; - - & > item { - property2: value2; - - & + another { - property3: value3; - } - } -}`; - expect(actual).toEqual(expected); -}); -test('newline between last declaration and nested atrule', () => { - let actual = format(` - test { - property1: value1; - @media all { - property2: value2; - } - } - `); - let expected = `test { - property1: value1; - - @media all { - property2: value2; - } -}`; - expect(actual).toEqual(expected); -}); -test('no trailing newline on empty nested rule', () => { - let actual = format(` - @layer test { - empty {} - } - `); - let expected = `@layer test { - empty {} -}`; - expect(actual).toEqual(expected); -}); -test('formats nested rules with selectors starting with', () => { - let actual = format(` - selector { - & > item { - property: value; - } - } - `); - let expected = `selector { - & > item { - property: value; - } -}`; - expect(actual).toEqual(expected); -}); -test('newlines between declarations, nested rules and more declarations', () => { - let actual = format(`a { font: 0/0; & b { color: red; } color: green;}`); - let expected = `a { - font: 0/0; - - & b { - color: red; - } - color: green; -}`; - expect(actual).toEqual(expected); -}); -test('formats nested rules with a selector starting with &', () => { - let actual = format(` - selector { - & a { color: red; } - } - `); - let expected = `selector { - & a { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('formats unknown stuff in curly braces', () => { - let actual = format(` - selector { - { color: red; } - } - `); - let expected = `selector { - { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` - selector { - a & { color:red } - } - `); - let expected = `selector { - a & { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('Relaxed nesting: formats nested rules with a selector with a &', () => { - let actual = format(` - selector { - a & { color:red } - } - `); - let expected = `selector { - a & { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('Relaxed nesting: formats nested rules with a selector without a &', () => { - let actual = format(` - selector { - a { color:red } - } - `); - let expected = `selector { - a { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('Relaxed nesting: formats nested rules with a selector starting with a selector combinator', () => { - let actual = format(` - selector { - > a { color:red } - ~ a { color:red } - + a { color:red } - } - `); - let expected = `selector { - > a { - color: red; - } - - ~ a { - color: red; - } - - + a { - color: red; - } -}`; - expect(actual).toEqual(expected); -}); -test('handles syntax errors: unclosed block', () => { - let actual = format(`a { mumblejumble`); - let expected = 'a {}'; - expect(actual).toEqual(expected); -}); -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.js deleted file mode 100644 index 8cd9017..0000000 --- a/test/selectors.test.js +++ /dev/null @@ -1,236 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -test('A single selector is rendered without a trailing comma', () => { - let actual = format('a {}'); - let expected = 'a {}'; - expect(actual).toEqual(expected); -}); -test('Multiple selectors are placed on a new line, separated by commas', () => { - let actual = format(` - selector1, - selector1a, - selector1b, - selector1aa, - selector2, - - selector3 { - } - `); - let expected = `selector1, -selector1a, -selector1b, -selector1aa, -selector2, -selector3 {}`; - expect(actual).toEqual(expected); -}); -test('formats multiline selectors on a single line', () => { - let actual = format(` -a.b - .c .d - .e .f { -color: green } - `); - let expected = `a.b .c .d .e .f { - color: green; -}`; - expect(actual).toEqual(expected); -}); -test('formats simple selector combinators', () => { - let actual = format(` - a>b, - a>b~c d, - .article-content ol li>* {} - `); - let expected = `a > b, -a > b ~ c d, -.article-content ol li > * {}`; - expect(actual).toEqual(expected); -}); -test('lowercases type selectors', () => { - let actual = format(` - A, - B, - C {} - `); - let expected = `a, -b, -c {}`; - expect(actual).toEqual(expected); -}); -test('formats nested selector combinators', () => { - let fixtures = [ - [`:where(a+b) {}`, `:where(a + b) {}`], - [`:where(:is(ol,ul)) {}`, `:where(:is(ol, ul)) {}`], - [`li:nth-of-type(1) {}`, `li:nth-of-type(1) {}`], - [`li:nth-of-type(2n) {}`, `li:nth-of-type(2n) {}`], - ]; - for (let [css, expected] of fixtures) { - let actual = format(css); - expect(actual).toEqual(expected); - } -}); -test('formats pseudo selectors', () => { - let css = ` - a::before, - a::after, - b:before, - b:after, - c::first-letter {} - `; - let expected = `a::before, -a::after, -b::before, -b::after, -c::first-letter {}`; - let actual = format(css); - expect(actual).toEqual(expected); -}); -test('formats pseudo elements with odd casing', () => { - let css = ` - a::Before, - a::After, - b:Before, - b:After, - c:After, - d::First-letter {} - `; - let expected = `a::before, -a::after, -b::before, -b::after, -c::after, -d::first-letter {}`; - let actual = format(css); - expect(actual).toEqual(expected); -}); -test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n -2) {}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n + 1) {}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted) {}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted) {}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n + 3 of .noted) {}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n + 3 of li.important) {}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n + 8):nth-child(-n + 15) {}`], -])('formats nth selector: %s', (css, expected) => { - let actual = format(css); - expect(actual).toEqual(expected); -}); -test.each([ - [`li:nth-child(3n-2) {}`, `li:nth-child(3n-2){}`], - [`li:nth-child(0n+1) {}`, `li:nth-child(0n+1){}`], - [`li:nth-child(even of .noted) {}`, `li:nth-child(even of .noted){}`], - [`li:nth-child(2n of .noted) {}`, `li:nth-child(2n of .noted){}`], - [`li:nth-child(-n + 3 of .noted) {}`, `li:nth-child(-n+3 of .noted){}`], - [`li:nth-child(-n+3 of li.important) {}`, `li:nth-child(-n+3 of li.important){}`], - [`p:nth-child(n+8):nth-child(-n+15) {}`, `p:nth-child(n+8):nth-child(-n+15){}`], -])('minifies nth selector: %s', (css, expected) => { - let actual = format(css, { minify: true }); - expect(actual).toEqual(expected); -}); -test('formats multiline selectors', () => { - let actual = format(` - a:is( - a, - b, - c - ) {} - `); - let expected = `a:is(a, b, c) {}`; - expect(actual).toEqual(expected); -}); -test('format nesting selectors', () => { - let actual = format(` - & a {} - b & c {} - `); - let expected = `& a {} - -b & c {}`; - expect(actual).toEqual(expected); -}); -test('prints all possible attribute selectors', () => { - let actual = format(` - [title="test"], - [title|="test"], - [title^="test"], - [title*="test"], - [title$="test"], - [title~="test"] {} - `); - let expected = `[title="test"], -[title|="test"], -[title^="test"], -[title*="test"], -[title$="test"], -[title~="test"] {}`; - expect(actual).toEqual(expected); -}); -test('forces attribute selectors to have quoted values', () => { - let actual = format(` - [title=foo], - [title="bar"], - [title='baz'] {} - `); - let expected = `[title="foo"], -[title="bar"], -[title="baz"] {}`; - expect(actual).toEqual(expected); -}); -test('adds a space before attribute selector flags', () => { - let actual = format(` - [title="foo" i], - [title="baz"i], - [title=foo S] {} - `); - let expected = `[title="foo" i], -[title="baz" i], -[title="foo" s] {}`; - 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() {}`; - expect(actual).toEqual(expected); -}); -test(`formats ::highlight and ::highlight(Name) correctly`, () => { - let actual = format(`::highlight,::highlight(Name),::highlight(my-thing) {}`); - let expected = `::highlight, -::highlight(Name), -::highlight(my-thing) {}`; - expect(actual).toEqual(expected); -}); -test('formats keyframes selectors (50%) correctly', () => { - let actual = format(`@keyframes Toastify__bounceInUp { 0% {animation-timing-function: cubic-bezier(.215, .61, .355, 1);} 50% {} 80%,to {} }`); - let expected = `@keyframes Toastify__bounceInUp { - 0% { - animation-timing-function: cubic-bezier(.215, .61, .355, 1); - } - - 50% {} - - 80%, - to {} -}`; - expect(actual).toBe(expected); -}); -test('formats unknown pseudos correctly', () => { - let actual = format(` - ::foo-bar, - :unkown-thing(), - :unnowkn(kjsa.asddk,asd) {} - `); - let expected = `::foo-bar, -:unkown-thing(), -:unnowkn(kjsa.asddk, asd) {}`; - expect(actual).toEqual(expected); -}); -test('handles syntax errors', () => { - let actual = format(` - test, - @test {} - `); - let expected = `test {}`; - expect(actual).toEqual(expected); -}); diff --git a/test/tab-size.test.js b/test/tab-size.test.js deleted file mode 100644 index 6eb2a23..0000000 --- a/test/tab-size.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -let fixture = ` - selector { - color: red; - } -`; -test('tab_size: 2', () => { - let actual = format(` - selector { - color: red; - } - - @media (min-width: 100px) { - selector { - color: blue; - } - } - `, { tab_size: 2 }); - let expected = `selector { - color: red; -} - -@media (min-width: 100px) { - selector { - color: blue; - } -}`; - expect(actual).toEqual(expected); -}); -test('invalid tab_size: 0', () => { - expect(() => format(fixture, { tab_size: 0 })).toThrow(); -}); -test('invalid tab_size: negative', () => { - expect(() => format(fixture, { tab_size: -1 })).toThrow(); -}); -test('combine tab_size and minify', () => { - let actual = format(fixture, { - tab_size: 2, - minify: true, - }); - let expected = `selector{color:red}`; - expect(actual).toEqual(expected); -}); diff --git a/test/values.test.js b/test/values.test.js deleted file mode 100644 index 4388212..0000000 --- a/test/values.test.js +++ /dev/null @@ -1,313 +0,0 @@ -import { test, expect } from 'vitest'; -import { format } from '../src/lib/index.js'; -test('collapses abundant whitespace', () => { - let actual = format(`a { - transition: all 100ms ease; - color: rgb( 0 , 0 , 0 ); - color: red ; - }`); - let expected = `a { - transition: all 100ms ease; - color: rgb(0, 0, 0); - color: red; -}`; - expect(actual).toEqual(expected); -}); -test('formats simple value lists', () => { - let actual = format(` - a { - transition-property: all,opacity; - transition: all 100ms ease,opacity 10ms 20ms linear; - ANIMATION: COLOR 123MS EASE-OUT; - color: rgb(0,0,0); - color: HSL(0%,10%,50%); - content: 'Test'; - background-image: url("EXAMPLE.COM"); - } - `); - let expected = `a { - transition-property: all, opacity; - transition: all 100ms ease, opacity 10ms 20ms linear; - animation: COLOR 123ms EASE-OUT; - color: rgb(0, 0, 0); - color: hsl(0%, 10%, 50%); - content: "Test"; - background-image: url("EXAMPLE.COM"); -}`; - expect(actual).toEqual(expected); -}); -test('formats nested value lists', () => { - let actual = format(` - a { - background: red,linear-gradient(to bottom,red 10%,green 50%,blue 100%); - } - `); - let expected = `a { - background: red, linear-gradient(to bottom, red 10%, green 50%, blue 100%); -}`; - expect(actual).toEqual(expected); -}); -test('formats nested var()', () => { - let actual = format(` - a { - color: var(--test1,var(--test2,green)); - color: var(--test3,rgb(0,0,0)); - } - `); - let expected = `a { - color: var(--test1, var(--test2, green)); - color: var(--test3, rgb(0, 0, 0)); -}`; - expect(actual).toEqual(expected); -}); -test('formats multiline values on a single line', () => { - let actual = format(` -a { - background: linear-gradient( - red, - 10% blue, -20% green,100% yellow); - color: rgb( - 0, - 0, - 0 - ); -} - `); - let expected = `a { - background: linear-gradient(red, 10% blue, 20% green, 100% yellow); - color: rgb(0, 0, 0); -}`; - expect(actual).toEqual(expected); -}); -test('does not break font shorthand', () => { - let actual = format(`a { - font: 2em/2 sans-serif; - font: 2em/ 2 sans-serif; - font: 2em / 2 sans-serif; - }`); - let expected = `a { - font: 2em/2 sans-serif; - font: 2em/2 sans-serif; - font: 2em/2 sans-serif; -}`; - expect(actual).toEqual(expected); -}); -test('formats whitespace around operators (*/+-) correctly', () => { - let actual = format(`a { - font: 2em/2 sans-serif; - font-size: calc(2em/2); - font-size: calc(2em * 2); - font-size: calc(2em + 2px); - font-size: calc(2em - 2px); -}`); - let expected = `a { - font: 2em/2 sans-serif; - font-size: calc(2em / 2); - font-size: calc(2em * 2); - font-size: calc(2em + 2px); - font-size: calc(2em - 2px); -}`; - expect(actual).toEqual(expected); -}); -test('formats whitespace around operators (*/+-) correctly in nested parenthesis', () => { - let actual = format(`a { - width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x))/ 12 * 6) + (-1 * var(--y))); -}`); - let expected = `a { - width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); - width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); -}`; - expect(actual).toEqual(expected); -}); -test('formats parenthesis correctly', () => { - let actual = format(`a { - width: calc(100% - var(--x)); - width: calc((100% - var(--x))); - width: calc(100% - (var(--x))); - width: calc((100% - (var(--x)))); -}`); - let expected = `a { - width: calc(100% - var(--x)); - width: calc((100% - var(--x))); - width: calc(100% - (var(--x))); - width: calc((100% - (var(--x)))); -}`; - expect(actual).toEqual(expected); -}); -test('does not lowercase grid-area names', () => { - let actual = format(`a { grid-area: emailInputBox; }`); - let expected = `a { - grid-area: emailInputBox; -}`; - expect(actual).toEqual(expected); -}); -test('does not lowercase custom properties in var()', () => { - let actual = format(`a { color: var(--MyColor); }`); - let expected = `a { - color: var(--MyColor); -}`; - expect(actual).toEqual(expected); -}); -test('lowercases CSS functions', () => { - let actual = format(`a { - color: RGB(0, 0, 0); - transform: translateX(100px); - }`); - let expected = `a { - color: rgb(0, 0, 0); - transform: translatex(100px); -}`; - expect(actual).toEqual(expected); -}); -test('relative colors', () => { - let actual = format(`a { - color: rgb( from red 0 0 255); - color: rgb( from rgb( 200 0 0 ) r r r ) ; - color: hwb( from var( --base-color ) h w b / var( --standard-opacity ) ) ; - color: lch(from var(--base-color) calc(l + 20) c h); - }`); - let expected = `a { - color: rgb(from red 0 0 255); - color: rgb(from rgb(200 0 0) r r r); - color: hwb(from var(--base-color) h w b / var(--standard-opacity)); - color: lch(from var(--base-color) calc(l + 20) c h); -}`; - expect(actual).toEqual(expected); -}); -test('does not change casing of `NaN`', () => { - let actual = format(`a { - height: calc(1 * NaN); - }`); - let expected = `a { - height: calc(1 * NaN); -}`; - expect(actual).toEqual(expected); -}); -test('does not change casing of URLs', () => { - let actual = format(`a { - background-image: url("My-Url.png"); - }`); - let expected = `a { - background-image: url("My-Url.png"); -}`; - expect(actual).toEqual(expected); -}); -test('lowercases dimensions', () => { - let actual = format(`a { - font-size: 12PX; - width: var(--test, 33REM); - }`); - let expected = `a { - font-size: 12px; - width: var(--test, 33rem); -}`; - expect(actual).toEqual(expected); -}); -test('formats unknown content in value', () => { - let actual = format(`a { - content: 'Test' counter(page); - }`); - let expected = `a { - content: "Test" counter(page); -}`; - expect(actual).toEqual(expected); -}); -test('does not break space toggles', () => { - let actual = format(`a { - --ON: initial; - --OFF: ; - }`); - let expected = `a { - --ON: initial; - --OFF: ; -}`; - expect(actual).toEqual(expected); -}); -test('does not break space toggles (minified)', () => { - let actual = format(`a { - --ON: initial; - --OFF: ; - }`, { minify: true }); - let expected = `a{--ON:initial;--OFF: }`; - expect(actual).toEqual(expected); -}); -test('adds quotes around strings in url()', () => { - let actual = format(`a { - background-image: url("star.gif"); - list-style-image: url('../images/bullet.jpg'); - content: url("pdficon.jpg"); - cursor: url(mycursor.cur); - border-image-source: url(/media/diamonds.png); - src: url('fantasticfont.woff'); - offset-path: url(#path); - mask-image: url("masks.svg#mask1"); - }`); - let expected = `a { - background-image: url("star.gif"); - list-style-image: url("../images/bullet.jpg"); - content: url("pdficon.jpg"); - cursor: url("mycursor.cur"); - border-image-source: url("/media/diamonds.png"); - src: url("fantasticfont.woff"); - offset-path: url("#path"); - mask-image: url("masks.svg#mask1"); -}`; - expect(actual).toEqual(expected); -}); -test.each([ - `data:image/svg+xml;utf8,`, - `data:image/svg+xml;utf8,`, -])('Does not mess up URLs with inlined SVG', (input) => { - let actual = format(`test { - background-image: url('${input}'); - background-image: url(${input}); - }`); - let expected = `test { - background-image: url(${input}); - background-image: url(${input}); -}`; - expect(actual).toEqual(expected); -}); -test.each([ - // Examples from https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data - 'data:,Hello%2C%20World%21', - 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==', - 'data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E', - 'data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E', - // from https://github.com/projectwallace/format-css/issues/144 - `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgaGVpZ2h0PSIyNHB4IiB3aWR0aD0iMjRweCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIyMi4zMSIgeTE9IjIzLjYyIiB4Mj0iMy43MyIgeTI9IjMuMDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOTM3MjIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmODZmMjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+TWFnbmlmaWVyPC90aXRsZT48cGF0aCBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiIGQ9Ik0yMy4zMyAyMC4xbC00LjczLTQuNzRhMTAuMDYgMTAuMDYgMCAxIDAtMy4yMyAzLjIzbDQuNzQgNC43NGEyLjI5IDIuMjkgMCAxIDAgMy4yMi0zLjIzem0tMTcuNDgtNS44NGE1Ljk0IDUuOTQgMCAxIDEgOC40MiAwIDYgNiAwIDAgMS04LjQyIDB6Ii8+PC9zdmc+`, -])('Does not mess up URLs with encoded inlined content: %s', (input) => { - let actual = format(`test { - background-image: url(${input}); - }`); - let expected = `test { - background-image: url(${input}); -}`; - expect(actual).toBe(expected); -}); -test.each([ - `U+26`, // single code point - `U+0-7F`, - `U+0025-00FF`, // code point range - `U+4??`, // wildcard range - `U+0025-00FF, U+4??`, // multiple values -])('Formats unicode-range: %s', (unicode_range) => { - let actual = format(`test { unicode-range: ${unicode_range}; }`); - let expected = `test { - unicode-range: ${unicode_range}; -}`; - expect(actual).toBe(expected); -}); -test('formats multi-value unicode range', () => { - let actual = format(`test { unicode-range: U+0025-00FF,U+4??; }`); - let expected = `test { - unicode-range: U+0025-00FF, U+4??; -}`; - expect(actual).toBe(expected); -}); diff --git a/tsconfig.json b/tsconfig.json index 70de516..0ed86e1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,26 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - "types": ["node"], - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "rootDir": "src", - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "declaration": true, + "rootDirs": ["src/lib", "src/cli"], + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] } diff --git a/tsdown.config.js b/tsdown.config.js deleted file mode 100644 index 5e421b3..0000000 --- a/tsdown.config.js +++ /dev/null @@ -1,32 +0,0 @@ -import { defineConfig } from 'tsdown'; -import { codecovRollupPlugin } from '@codecov/rollup-plugin'; -export default defineConfig([ - { - entry: 'src/lib/index.ts', - platform: 'neutral', - publint: true, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCss', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, - { - entry: 'src/cli/cli.ts', - platform: 'node', - dts: false, - // Reference the lib via its package name to avoid bundling it twice - deps: { - neverBundle: ['@projectwallace/format-css'], - }, - plugins: [ - codecovRollupPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'formatCssCli', - uploadToken: process.env.CODECOV_TOKEN, - }), - ], - }, -]); diff --git a/vitest.config.js b/vitest.config.js deleted file mode 100644 index cd426a2..0000000 --- a/vitest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -import { defineConfig } from 'vitest/config'; -import { resolve } from 'node:path'; -export default defineConfig({ - resolve: { - alias: { - '@projectwallace/format-css': resolve('./src/lib/index.ts'), - }, - }, - test: { - coverage: { - provider: 'v8', - }, - }, -}); From b62a81698b63237687840d0e95d4704f50b80f14 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 13:31:38 +0200 Subject: [PATCH 6/7] fix types --- tsconfig.json | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 0ed86e1..4bd5ac7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,28 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "declaration": true, - "rootDirs": ["src/lib", "src/cli"], - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "types": ["node"], + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "declaration": true, + "rootDir": "src", + "rootDirs": ["src/lib", "src/cli"], + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] } From 80fafdda104850ae8a333cb71250f039b2209e34 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Wed, 1 Apr 2026 13:32:42 +0200 Subject: [PATCH 7/7] fix formatting --- tsconfig.json | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 4bd5ac7..7a3d3c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,28 +1,28 @@ { - "compilerOptions": { - // Base options: - "skipLibCheck": true, - "target": "es2024", - "verbatimModuleSyntax": true, - "allowJs": false, - "moduleDetection": "force", - // Strictness - "strict": true, - "noUncheckedIndexedAccess": true, - // Type checking, not transpiling - "types": ["node"], - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["es2024", "DOM"], - "declaration": true, - "rootDir": "src", - "rootDirs": ["src/lib", "src/cli"], - "outDir": "dist", - "paths": { - // So we can import like an external in the CLI - "@projectwallace/format-css": ["./src/lib/index.ts"] - } - }, - "include": ["src/lib/index.ts", "src/cli/cli.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + // Base options: + "skipLibCheck": true, + "target": "es2024", + "verbatimModuleSyntax": true, + "allowJs": false, + "moduleDetection": "force", + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + // Type checking, not transpiling + "types": ["node"], + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["es2024", "DOM"], + "declaration": true, + "rootDir": "src", + "rootDirs": ["src/lib", "src/cli"], + "outDir": "dist", + "paths": { + // So we can import like an external in the CLI + "@projectwallace/format-css": ["./src/lib/index.ts"] + } + }, + "include": ["src/lib/index.ts", "src/cli/cli.ts"], + "exclude": ["node_modules"] }