From 0e9aa4613ecb5ed38e22c77e774b08353eb93e23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:43:14 -0500 Subject: [PATCH 01/14] chore(deps): update dependency globals to v17 (#506) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e1f94e..a97d64e 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "coveralls": "^3.0.0", "eslint": "^9.22.0", "eslint-config-problems": "9.0.0", - "globals": "^16.0.0", + "globals": "^17.0.0", "postcss": "^8.0.4", "postcss-import": "^16.0.0", "prettier": "~3.6.0", From c0f08222cff38fa200bf8f51001c9e6e2bbbc966 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:43:56 -0500 Subject: [PATCH 02/14] chore(deps): update dependency prettier to ~3.7.0 (#502) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a97d64e..3618369 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "globals": "^17.0.0", "postcss": "^8.0.4", "postcss-import": "^16.0.0", - "prettier": "~3.6.0", + "prettier": "~3.7.0", "sugarss": "^5.0.0", "uuid": "^11.0.0" }, From 6612d642f09fb0a3451ad32393fd49f63ca3f577 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:44:57 -0500 Subject: [PATCH 03/14] chore(deps): update actions/checkout action to v6 (#500) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c5a03f..2d6f6d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Use Node.js ${{ matrix.node }} uses: actions/setup-node@v4 with: From 4e844293f140df18046103aaeed7bad5ad33f24a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:46:11 -0500 Subject: [PATCH 04/14] chore(deps): update actions/setup-node action to v6 (#499) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d6f6d4..82ebade 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - run: git config --global core.autocrlf input - uses: actions/checkout@v6 - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node }} - run: npm install From 032efde0f7c342e08ab00559b3b1486493a78f22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:48:15 -0500 Subject: [PATCH 05/14] chore(deps): update dependency uuid to v13 (#498) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3618369..9556624 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "postcss-import": "^16.0.0", "prettier": "~3.7.0", "sugarss": "^5.0.0", - "uuid": "^11.0.0" + "uuid": "^13.0.0" }, "peerDependencies": { "postcss": "^8.0.0" From e600465736b904ebd8b650d54e7b71652b17d2ac Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Tue, 6 Jan 2026 11:50:31 -0500 Subject: [PATCH 06/14] Add Node v24 to CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82ebade..b31ce99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: test: strategy: matrix: - node: [18, 20, 22] + node: [18, 20, 22, 24] os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: From f2c2e8a119c03049be4ce7c92ab83f535a360a2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:20:37 -0500 Subject: [PATCH 07/14] chore(deps): update dependency prettier to ~3.8.0 (#507) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9556624..6e5981a 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "globals": "^17.0.0", "postcss": "^8.0.4", "postcss-import": "^16.0.0", - "prettier": "~3.7.0", + "prettier": "~3.8.0", "sugarss": "^5.0.0", "uuid": "^13.0.0" }, From 27e9fdd6b20115d0dc7044939a97c1dc4365c886 Mon Sep 17 00:00:00 2001 From: Roli Bosch Date: Fri, 6 Mar 2026 10:04:40 -0800 Subject: [PATCH 08/14] chore: replace fs-extra with native Node.js fs (#510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: replace fs-extra with native Node.js fs Replace fs-extra with native Node.js fs APIs (node:fs/promises, node:fs). All replacements are available on the supported Node >= 18: - fs.pathExists() → fs.access() with boolean resolution - fs.outputFile() → fs.mkdir({ recursive: true }) + fs.writeFile() - fs.copy() → fs.copyFile() (glob ensures file-only results) - fs.emptyDir() → fs.rm({ recursive, force }) + fs.mkdir({ recursive }) - fs.remove() → fs.rm({ recursive, force }) - fs.createReadStream() → named import from node:fs This removes 1 runtime dependency (fs-extra + 3 transitive deps). Co-Authored-By: Claude Opus 4.6 * refactor: convert env helper to async/await per review feedback --------- Co-authored-by: Claude Opus 4.6 --- index.js | 10 +++++++--- package.json | 1 - test/ext.js | 16 +++++++++++++--- test/helpers/clean.js | 8 +++++--- test/helpers/env.js | 24 +++++++++++++++--------- test/helpers/read.js | 2 +- test/map.js | 9 +++++++-- test/replace.js | 7 ++++--- test/stdin.js | 4 ++-- test/stdout.js | 4 ++-- test/unchanged.js | 2 +- test/watch.js | 2 +- 12 files changed, 58 insertions(+), 31 deletions(-) diff --git a/index.js b/index.js index 266c912..c7a0cd8 100755 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import fs from 'fs-extra' +import fs from 'node:fs/promises' import path from 'path' import prettyHrtime from 'pretty-hrtime' @@ -286,10 +286,14 @@ function css(css, file) { }) async function outputFile(file, string) { - const fileExists = await fs.pathExists(file) + const fileExists = await fs.access(file).then( + () => true, + () => false, + ) const currentValue = fileExists ? await fs.readFile(file, 'utf8') : null if (currentValue === string) return - return fs.outputFile(file, string) + await fs.mkdir(path.dirname(file), { recursive: true }) + return fs.writeFile(file, string) } } diff --git a/package.json b/package.json index 6e5981a..ccde399 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "dependencies": { "chokidar": "^3.3.0", "dependency-graph": "^1.0.0", - "fs-extra": "^11.0.0", "picocolors": "^1.0.0", "postcss-load-config": "^5.0.0", "postcss-reporter": "^7.0.0", diff --git a/test/ext.js b/test/ext.js index 329f2c4..675939d 100644 --- a/test/ext.js +++ b/test/ext.js @@ -1,6 +1,6 @@ import test from 'ava' -import fs from 'fs-extra' +import fs from 'node:fs/promises' import path from 'path' import cli from './helpers/cli.js' @@ -20,7 +20,12 @@ test('--ext works', async (t) => { ]) t.falsy(error, stderr) - t.truthy(await fs.pathExists(path.join(dir, 'a.css'))) + t.truthy( + await fs.access(path.join(dir, 'a.css')).then( + () => true, + () => false, + ), + ) }) test('--ext works with no leading dot', async (t) => { @@ -37,5 +42,10 @@ test('--ext works with no leading dot', async (t) => { ]) t.falsy(error, stderr) - t.truthy(await fs.pathExists(path.join(dir, 'a.css'))) + t.truthy( + await fs.access(path.join(dir, 'a.css')).then( + () => true, + () => false, + ), + ) }) diff --git a/test/helpers/clean.js b/test/helpers/clean.js index 73bb191..7591d7b 100644 --- a/test/helpers/clean.js +++ b/test/helpers/clean.js @@ -1,8 +1,10 @@ -import fs from 'fs-extra' +import fs from 'node:fs/promises' Promise.all([ - fs.emptyDir('./test/fixtures/.tmp/'), - fs.remove('./coverage'), + fs + .rm('./test/fixtures/.tmp/', { recursive: true, force: true }) + .then(() => fs.mkdir('./test/fixtures/.tmp/', { recursive: true })), + fs.rm('./coverage', { recursive: true, force: true }), ]).catch((err) => { console.error(err) process.exit(1) diff --git a/test/helpers/env.js b/test/helpers/env.js index 017df87..0fd9549 100644 --- a/test/helpers/env.js +++ b/test/helpers/env.js @@ -1,18 +1,24 @@ -import fs from 'fs-extra' +import fs from 'node:fs/promises' import path from 'path' import { glob } from 'tinyglobby' import tmp from './tmp.js' -export default function (config, fixtures = '**/*', extension = 'cjs') { +export default async function (config, fixtures = '**/*', extension = 'cjs') { const dir = tmp() - return Promise.all([ - glob(fixtures, { cwd: 'test/fixtures' }).then((list) => { - return list.map((item) => { - return fs.copy(path.join('test/fixtures', item), path.join(dir, item)) - }) + await fs.mkdir(dir, { recursive: true }) + + const list = await glob(fixtures, { cwd: 'test/fixtures' }) + + await Promise.all([ + ...list.map(async (item) => { + const dest = path.join(dir, item) + await fs.mkdir(path.dirname(dest), { recursive: true }) + await fs.copyFile(path.join('test/fixtures', item), dest) }), - fs.outputFile(path.join(dir, `postcss.config.${extension}`), config), - ]).then(() => dir) + fs.writeFile(path.join(dir, `postcss.config.${extension}`), config), + ]) + + return dir } diff --git a/test/helpers/read.js b/test/helpers/read.js index e9939aa..17d969f 100644 --- a/test/helpers/read.js +++ b/test/helpers/read.js @@ -1,4 +1,4 @@ -import fs from 'fs-extra' +import fs from 'node:fs/promises' export default function (path) { return fs.readFile(path, 'utf8').then( diff --git a/test/map.js b/test/map.js index 4783010..a3febb9 100644 --- a/test/map.js +++ b/test/map.js @@ -1,5 +1,5 @@ import test from 'ava' -import fs from 'fs-extra' +import fs from 'node:fs/promises' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' @@ -35,7 +35,12 @@ test('--map generates external sourcemaps', async (t) => { t.falsy(error, stderr) - t.truthy(await fs.pathExists(output.replace('.css', '.css.map'))) + t.truthy( + await fs.access(output.replace('.css', '.css.map')).then( + () => true, + () => false, + ), + ) }) test('--no-map disables internal sourcemaps', async (t) => { diff --git a/test/replace.js b/test/replace.js index dd24b98..7032493 100644 --- a/test/replace.js +++ b/test/replace.js @@ -1,6 +1,6 @@ import test from 'ava' -import fs from 'fs-extra' +import fs from 'node:fs/promises' import path from 'path' import cli from './helpers/cli.js' @@ -12,9 +12,10 @@ test('--replace works', async (t) => { const output = path.join(dir, 'output.css') + await fs.mkdir(dir, { recursive: true }) await Promise.all([ - fs.copy('test/fixtures/import.css', output), - fs.copy('test/fixtures/a.css', path.join(dir, 'a.css')), + fs.copyFile('test/fixtures/import.css', output), + fs.copyFile('test/fixtures/a.css', path.join(dir, 'a.css')), ]) const { error, stderr } = await cli([ diff --git a/test/stdin.js b/test/stdin.js index a682436..9cdf928 100644 --- a/test/stdin.js +++ b/test/stdin.js @@ -1,6 +1,6 @@ import test from 'ava' -import fs from 'fs-extra' +import { createReadStream } from 'node:fs' import path from 'path' import { exec } from 'child_process' @@ -24,5 +24,5 @@ test.cb('reads from stdin', (t) => { }, ) - fs.createReadStream('test/fixtures/a.css').pipe(cp.stdin) + createReadStream('test/fixtures/a.css').pipe(cp.stdin) }) diff --git a/test/stdout.js b/test/stdout.js index 260180f..b53de43 100644 --- a/test/stdout.js +++ b/test/stdout.js @@ -1,6 +1,6 @@ import test from 'ava' -import fs from 'fs-extra' +import { createReadStream } from 'node:fs' import path from 'path' import { exec } from 'child_process' @@ -23,5 +23,5 @@ test.cb('writes to stdout', (t) => { }, ) - fs.createReadStream('./test/fixtures/a.sss').pipe(cp.stdin) + createReadStream('./test/fixtures/a.sss').pipe(cp.stdin) }) diff --git a/test/unchanged.js b/test/unchanged.js index 4598530..beabdb1 100644 --- a/test/unchanged.js +++ b/test/unchanged.js @@ -1,4 +1,4 @@ -import fs from 'fs-extra' +import fs from 'node:fs/promises' import test from 'ava' import cli from './helpers/cli.js' diff --git a/test/watch.js b/test/watch.js index 357174a..ed0e873 100644 --- a/test/watch.js +++ b/test/watch.js @@ -1,6 +1,6 @@ import test from 'ava' -import fs from 'fs-extra' +import fs from 'node:fs/promises' import path from 'path' import { exec, spawn } from 'child_process' import chokidar from 'chokidar' From b227a930b1cd7f6e1476332273e6888dd54e2ca5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:14:50 -0400 Subject: [PATCH 09/14] Update actions/setup-node action to v7 (#518) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b31ce99..2046008 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - run: git config --global core.autocrlf input - uses: actions/checkout@v6 - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: ${{ matrix.node }} - run: npm install From c1ab734450d5090cf2265f51e5569ef9db112c07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:15:23 -0400 Subject: [PATCH 10/14] Update dependency prettier to ~3.9.0 (#517) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ccde399..70194cd 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "globals": "^17.0.0", "postcss": "^8.0.4", "postcss-import": "^16.0.0", - "prettier": "~3.8.0", + "prettier": "~3.9.0", "sugarss": "^5.0.0", "uuid": "^13.0.0" }, From 86501b0a24b176efa0d1751f58d2ef43590cdfad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:17:15 -0400 Subject: [PATCH 11/14] Update actions/checkout action to v7 (#516) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2046008..eb92fc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Use Node.js ${{ matrix.node }} uses: actions/setup-node@v7 with: From 0c6ddedeff69648f8ba478dbaa5eca25c163228a Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Tue, 11 Aug 2026 15:56:09 -0400 Subject: [PATCH 12/14] Simplify dependencies() to use flatMap() (#520) --- index.js | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index c7a0cd8..7cca0e5 100755 --- a/index.js +++ b/index.js @@ -300,30 +300,24 @@ function css(css, file) { function dependencies(results) { if (!Array.isArray(results)) results = [results] - const messages = [] + return results.flatMap((result) => { + if (result.messages.length <= 0) return [] - results.forEach((result) => { - if (result.messages <= 0) return - - result.messages - .filter((msg) => - msg.type === 'dependency' || msg.type === 'dir-dependency' ? msg : '', + return result.messages + .filter( + (msg) => msg.type === 'dependency' || msg.type === 'dir-dependency', ) .map(depGraph.add) - .forEach((dependency) => { + .map((dependency) => { if (dependency.type === 'dir-dependency') { - messages.push( - dependency.glob - ? path.join(dependency.dir, dependency.glob) - : dependency.dir, - ) - } else { - messages.push(dependency.file) + return dependency.glob + ? path.join(dependency.dir, dependency.glob) + : dependency.dir } + + return dependency.file }) }) - - return messages } function printVerbose(message) { From 1489c2f98f812cc6c6cff7bf078b54eab1aee34f Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Tue, 11 Aug 2026 16:02:12 -0400 Subject: [PATCH 13/14] Remove incorrect comments --- test/watch.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/watch.js b/test/watch.js index ed0e873..9895a19 100644 --- a/test/watch.js +++ b/test/watch.js @@ -278,7 +278,6 @@ testCb('--watch watches dependencies', (t) => { // Start postcss-cli: watcher.on('ready', () => { - // Using exec() and quoting "*.css" to test watch's glob handling: cp = exec( `node ${path.resolve( 'index.js', @@ -376,7 +375,6 @@ testCb('--watch watches directory dependencies', (t) => { // Start postcss-cli: watcher.on('ready', () => { - // Using exec() and quoting "*.css" to test watch's glob handling: cp = exec( `node ${path.resolve( 'index.js', From 35ae0858987436644d20bb58a9a67d951615909c Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Tue, 11 Aug 2026 16:03:21 -0400 Subject: [PATCH 14/14] Add test for directory as input (#521) --- test/glob.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/glob.js b/test/glob.js index 863fc39..38743f7 100644 --- a/test/glob.js +++ b/test/glob.js @@ -30,3 +30,29 @@ test('works with glob patterns', async (t) => { await read('test/fixtures/glob/s.css'), ) }) + +test('works with directory input', async (t) => { + const output = tmp() + + const { error, stderr } = await cli([ + 'test/fixtures/glob', + '-d', + output, + '--no-map', + ]) + + t.falsy(error, stderr) + + t.is( + await read(path.join(output, 'a.css')), + await read('test/fixtures/glob/a.css'), + ) + t.is( + await read(path.join(output, 'b.css')), + await read('test/fixtures/glob/b.css'), + ) + t.is( + await read(path.join(output, 's.css')), + await read('test/fixtures/glob/s.css'), + ) +})