Skip to content

Commit cb4b8f3

Browse files
committed
feat(autofix): add feat to overwrite sources files
1 parent 45ff9ae commit cb4b8f3

8 files changed

+96
-18
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"@jridgewell/trace-mapping": "0.3.25",
5151
"ansi-colors": "4.1.3",
5252
"fancy-log": "2.0.0",
53-
"text-table": "0.2.0"
53+
"text-table": "0.2.0",
54+
"write-file-atomic": "6.0.0"
5455
},
5556
"devDependencies": {
5657
"@babel/core": "7.25.2",

src/index.mjs

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const { lint } = stylelint;
1111
import { Transform } from 'node:stream';
1212

1313
import applySourcemap from './apply-sourcemap.mjs';
14+
import { overwriteSource } from '../src/writer.mjs';
1415
import reporterFactory from './reporter-factory.mjs';
1516

1617
/**
@@ -254,8 +255,14 @@ export default function gStylelintEsm(options) {
254255
* If fix option is enabled and there are fixes in the lint result, update file contents.
255256
* @type {boolean}
256257
*/
257-
if (pluginOptions.fix && lintResult.code) {
258+
if (pluginOptions.fix && lintResult.code !== file.contents.toString()) {
258259
file.contents = Buffer.from(lintResult.code);
260+
261+
/**
262+
* Overwrite the source file with the fixed code.
263+
* @type {Function}
264+
*/
265+
await overwriteSource(file, lintResult.code);
259266
}
260267

261268
/**

src/reporter-factory.mjs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
import { gFormatters } from './formatters.mjs';
88
import stylishFormatter from './stylish-formatter.mjs';
9-
import writeOutputLog from './writer.mjs';
9+
10+
import { writeOutputLog } from './writer.mjs';
1011

1112
/**
1213
* @typedef {import('stylelint').LintResult} LintResult
@@ -78,7 +79,7 @@ export default function reporterFactory(config = {}) {
7879
*/
7980
if (config.log) {
8081
asyncTasks.push(
81-
writeOutputLog(config.log, formattedText.trim())
82+
await writeOutputLog(config.log, formattedText.trim())
8283
);
8384
}
8485

src/utils.mjs

+1-3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,4 @@ export function formatMessageText(message) {
103103
* @param {number} count
104104
* @returns {string}
105105
*/
106-
export function pluralize(count) {
107-
return count === 1 ? '' : 's';
108-
}
106+
export const pluralize = (count) => count === 1 ? '' : 's';

src/writer.mjs

+21-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
* License under MIT
55
* ========================================================================== */
66

7-
import { dirname, normalize } from 'node:path';
8-
import { mkdir, writeFile } from 'node:fs/promises';
7+
import { dirname, normalize, relative } from 'node:path';
8+
import { mkdir } from 'node:fs/promises';
9+
import writeFileAtomic from 'write-file-atomic';
910

1011
import colors from 'ansi-colors';
11-
const { unstyle } = colors;
12+
const { blue, green, unstyle } = colors;
1213

1314
/**
1415
* Writes the given text to a log file at the specified destination.
@@ -17,10 +18,25 @@ const { unstyle } = colors;
1718
* @param {string} content - The text content to write to the file
1819
* @returns {Promise<void>} A promise that resolves when the file is written
1920
*/
20-
export default async function writeOutputLog(filePath, content) {
21+
export async function writeOutputLog(filePath, content) {
2122
// Ensure the directory exists
2223
await mkdir(dirname(filePath), { recursive: true });
2324

2425
// Write the output log
25-
await writeFile(normalize(filePath), unstyle(content));
26+
await writeFileAtomic(normalize(filePath), unstyle(content));
27+
}
28+
29+
/**
30+
* Overwrites the source file with the given content.
31+
*
32+
* @param {string} file - The source file
33+
* @param {string} content - The content to overwrite the source with
34+
* @returns {Promise<void>} A promise that resolves when the file is overwritten
35+
*/
36+
export async function overwriteSource(file, content) {
37+
process.stdout.write(
38+
`${blue(normalize(relative('.', file.path)))} >> ${green('fixed and overwrote')}\n`
39+
);
40+
41+
await writeFileAtomic(normalize(file.path), unstyle(content));
2642
}

test/index.test.js

+52-5
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,58 @@ describe('Plugin Functionality', () => {
112112
throw new Error(`Unexpected error: ${error}`);
113113
}
114114
});
115+
it('should overwrite the source file when `fix` option is set', async () => {
116+
stub(process.stdout, 'write');
117+
const outputDir = path.resolve(__dirname, '../tmpa');
118+
119+
const file = createVinylFile('invalid.css', '.foo {\n color: #ffffff;\n}\n');
120+
const content = file.contents.toString('utf8');
121+
const outputFilePath = path.join(outputDir, 'invalid.css');
122+
123+
fs.mkdirSync(outputDir, { recursive: true });
124+
fs.writeFileSync(outputFilePath, content, 'utf8');
125+
126+
const stream = src(outputFilePath);
127+
128+
expect.assertions(1);
129+
130+
try {
131+
await new Promise((resolve, reject) => {
132+
stream
133+
.pipe(gStylelintEsm({
134+
config: { rules: { 'color-hex-length': 'short' } },
135+
fix: true,
136+
}))
137+
.on('error', reject)
138+
.on('finish', resolve);
139+
});
140+
141+
const outputFileContents = fs.readFileSync(outputFilePath, 'utf8');
142+
143+
expect(outputFileContents).toBe('.foo {\n color: #fff;\n}\n');
144+
} catch (error) {
145+
throw new Error(`Unexpected error: ${error}`);
146+
} finally {
147+
fs.unlinkSync(outputFilePath);
148+
fs.rmdirSync(outputDir);
149+
process.stdout.write.restore();
150+
}
151+
});
115152
it('should fix the file without emitting errors', async () => {
116-
const inputFilePath = fixtures('invalid.css');
117-
const outputDir = path.resolve(__dirname, '../tmp');
153+
stub(process.stdout, 'write');
154+
const inputDir = path.resolve(__dirname, '../tmpb');
155+
const outputDir = path.resolve(__dirname, '../tmpc');
156+
157+
const file = createVinylFile('invalid.css', '.foo {\n color: #ffffff;\n}\n');
158+
const content = file.contents.toString('utf8');
159+
160+
const inputFilePath = path.join(inputDir, 'invalid.css');
118161
const outputFilePath = path.join(outputDir, 'invalid.css');
119162

120-
const stream = src(inputFilePath, {
121-
sourcemaps: true,
122-
});
163+
fs.mkdirSync(inputDir, { recursive: true });
164+
fs.writeFileSync(inputFilePath, content, 'utf8');
165+
166+
const stream = src(inputFilePath);
123167

124168
expect.assertions(1);
125169

@@ -141,8 +185,11 @@ describe('Plugin Functionality', () => {
141185
} catch (error) {
142186
throw new Error(`Unexpected error: ${error}`);
143187
} finally {
188+
fs.unlinkSync(inputFilePath);
144189
fs.unlinkSync(outputFilePath);
190+
fs.rmdirSync(inputDir);
145191
fs.rmdirSync(outputDir);
192+
process.stdout.write.restore();
146193
}
147194
});
148195
});

test/writer.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import path from 'node:path';
1010

1111
import colors from 'ansi-colors';
1212

13-
import writeOutputLog from '../src/writer.mjs';
13+
import { writeOutputLog } from '../src/writer.mjs';
1414

1515
const __dirname = fileURLToPath(new URL('../', import.meta.url));
1616

yarn.lock

+8
Original file line numberDiff line numberDiff line change
@@ -4850,6 +4850,14 @@ wrappy@1:
48504850
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
48514851
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
48524852

4853+
write-file-atomic@6.0.0:
4854+
version "6.0.0"
4855+
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-6.0.0.tgz#e9c89c8191b3ef0606bc79fb92681aa1aa16fa93"
4856+
integrity sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==
4857+
dependencies:
4858+
imurmurhash "^0.1.4"
4859+
signal-exit "^4.0.1"
4860+
48534861
write-file-atomic@^4.0.2:
48544862
version "4.0.2"
48554863
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"

0 commit comments

Comments
 (0)