diff --git a/packages/rstack/THIRD_PARTY_NOTICES.md b/packages/rstack/THIRD_PARTY_NOTICES.md index 65b0f21..c27e740 100644 --- a/packages/rstack/THIRD_PARTY_NOTICES.md +++ b/packages/rstack/THIRD_PARTY_NOTICES.md @@ -88,35 +88,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -## atomically - -This package includes bundled code from -[atomically](https://github.com/fabiospampinato/atomically). - -License: MIT - -The MIT License (MIT) - -Copyright (c) 2020-present Fabio Spampinato - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - ## fast-ignore This package includes bundled code from [fast-ignore](https://github.com/fabiospampinato/fast-ignore). diff --git a/packages/rstack/package.json b/packages/rstack/package.json index afda49c..7517fb7 100644 --- a/packages/rstack/package.json +++ b/packages/rstack/package.json @@ -69,7 +69,6 @@ "@rstest/adapter-rslib": "catalog:", "@types/micromatch": "catalog:", "@types/node": "catalog:", - "atomically": "catalog:", "fast-ignore": "catalog:", "ignore": "catalog:", "import-meta-resolve": "catalog:", diff --git a/packages/rstack/src/fmt/worker.ts b/packages/rstack/src/fmt/worker.ts index 7e50c74..0b115eb 100644 --- a/packages/rstack/src/fmt/worker.ts +++ b/packages/rstack/src/fmt/worker.ts @@ -1,25 +1,19 @@ // Derived from @prettier/cli, see THIRD_PARTY_NOTICES.md -import { readFile, writeFile } from 'atomically'; +import { readFileSync, writeFileSync } from 'node:fs'; import { format } from 'prettier'; import { getPrettierPlugins } from './prettierPlugins.ts'; import type { FmtFileRequest } from './types.ts'; /** - * Formatting output can be regenerated, so avoid waiting for a durability sync - * after every file, which is especially expensive during parallel formatting. - * `atomically` still uses a temporary file and rename for atomic replacement. + * Use synchronous direct I/O inside the dedicated worker to avoid libuv + * scheduling overhead. This prioritizes throughput over crash-safe replacement. */ -const atomicWriteOptions = { - encoding: 'utf8', - fsync: false, -} as const; - const formatFile = async ( { path, options }: FmtFileRequest, shouldWrite: boolean, ): Promise => { - const source = await readFile(path, 'utf8'); + const source = readFileSync(path, 'utf8'); const formatted = await format(source, { ...options, plugins: await getPrettierPlugins(options), @@ -30,7 +24,7 @@ const formatFile = async ( } if (shouldWrite) { - await writeFile(path, formatted, atomicWriteOptions); + writeFileSync(path, formatted, 'utf8'); } return true; diff --git a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts index 35fe392..298c0bb 100644 --- a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts +++ b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts @@ -8,14 +8,14 @@ const mocks = rs.hoisted(() => ({ rs.mock('../../src/fmt/parallel.ts', () => ({ createFmtWorker: () => Promise.resolve({ - formatFile: () => Promise.reject(new Error('atomic write failed')), + formatFile: () => Promise.reject(new Error('file write failed')), terminate: () => { mocks.terminateCalls++; }, }), })); -test('returns an error when the atomic write fails', async () => { +test('returns an error when a file write fails', async () => { const filePath = '/virtual/example.ts'; const result = await runFmtFiles({ @@ -38,7 +38,7 @@ test('returns an error when the atomic write fails', async () => { { path: filePath, status: 'error', - error: { message: 'atomic write failed' }, + error: { message: 'file write failed' }, }, ], }); diff --git a/packages/rstack/tests/fmt/worker.test.ts b/packages/rstack/tests/fmt/worker.test.ts index 6adaf89..d8a699e 100644 --- a/packages/rstack/tests/fmt/worker.test.ts +++ b/packages/rstack/tests/fmt/worker.test.ts @@ -1,35 +1,25 @@ -import { expect, rs, test } from 'rstack/test'; +import { readFileSync } from 'node:fs'; +import { expect, test } from 'rstack/test'; import { formatFile } from '../../src/fmt/worker.ts'; +import { withTempProject, writeProjectFile } from './helpers.ts'; -const mocks = rs.hoisted(() => ({ - writeFileCalls: [] as [string, string, unknown][], -})); +test('writes formatted files', async () => { + await withTempProject(async (rootPath) => { + const filePath = writeProjectFile(rootPath, 'example.ts', 'const value=1'); -rs.mock('atomically', () => ({ - readFile: () => Promise.resolve('const value=1'), - writeFile: (path: string, data: string, options: unknown) => { - mocks.writeFileCalls.push([path, data, options]); - return Promise.resolve(); - }, -})); - -test('disables fsync for atomic writes', async () => { - const filePath = '/virtual/example.ts'; - - await expect( - formatFile( - { - path: filePath, - options: { - filepath: filePath, - parser: 'typescript', + await expect( + formatFile( + { + path: filePath, + options: { + filepath: filePath, + parser: 'typescript', + }, }, - }, - true, - ), - ).resolves.toBe(true); + true, + ), + ).resolves.toBe(true); - expect(mocks.writeFileCalls).toEqual([ - [filePath, 'const value = 1;\n', { encoding: 'utf8', fsync: false }], - ]); + expect(readFileSync(filePath, 'utf8')).toBe('const value = 1;\n'); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c65f22..36869cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,9 +76,6 @@ catalogs: '@types/react-dom': specifier: ^19.2.4 version: 19.2.4 - atomically: - specifier: 2.1.1 - version: 2.1.1 cspell-ban-words: specifier: ^0.0.4 version: 0.0.4 @@ -379,9 +376,6 @@ importers: '@types/node': specifier: 'catalog:' version: 24.13.3 - atomically: - specifier: 'catalog:' - version: 2.1.1 fast-ignore: specifier: 'catalog:' version: 2.0.0 @@ -1363,9 +1357,6 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - atomically@2.1.1: - resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -2306,12 +2297,6 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - stubborn-fs@2.0.0: - resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} - - stubborn-utils@1.0.2: - resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -2414,9 +2399,6 @@ packages: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} - when-exit@2.1.5: - resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} - worktank@3.0.2: resolution: {integrity: sha512-ry5gPtWnakOnUBAAa2aiyWZwAFJuBtd/MwZH6o9DXnQHD4AZvidtl2uTLrb2d3Zjy9D04n84lHJNnIETQl7tuA==} @@ -3222,11 +3204,6 @@ snapshots: astring@1.9.0: {} - atomically@2.1.1: - dependencies: - stubborn-fs: 2.0.0 - when-exit: 2.1.5 - bail@2.0.2: {} big.js@5.2.2: {} @@ -4494,12 +4471,6 @@ snapshots: dependencies: min-indent: 1.0.1 - stubborn-fs@2.0.0: - dependencies: - stubborn-utils: 1.0.2 - - stubborn-utils@1.0.2: {} - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -4634,8 +4605,6 @@ snapshots: whatwg-mimetype@3.0.0: {} - when-exit@2.1.5: {} - worktank@3.0.2: dependencies: isoconcurrency: 1.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0bbe387..12fc222 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -35,7 +35,6 @@ catalog: '@types/react': '^19.2.18' '@types/react-dom': '^19.2.4' '@shikijs/transformers': '^4.3.1' - atomically: '2.1.1' 'cspell-ban-words': '^0.0.4' 'fast-ignore': '2.0.0' 'happy-dom': '^20.11.1'