create-file.ts 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import { asyncWriteToStream } from 'foxts/async-write-to-stream';
  2. import { fastStringArrayJoin } from 'foxts/fast-string-array-join';
  3. import fs from 'node:fs';
  4. import picocolors from 'picocolors';
  5. import type { Span } from '../trace';
  6. import { readFileByLine } from './fetch-text-by-line';
  7. import { writeFile } from './misc';
  8. import { createCompareSource, fileEqualWithCommentComparator } from 'foxts/compare-source';
  9. import { promisify } from 'node:util';
  10. export const fileEqual = createCompareSource(fileEqualWithCommentComparator);
  11. export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) {
  12. // readFileByLine will not include last empty line. So we always pop the linesA for comparison purpose
  13. if (linesA.length > 0 && linesA[linesA.length - 1] === '') {
  14. linesA.pop();
  15. }
  16. const isEqual = await span.traceChildAsync(`compare ${filePath}`, async () => {
  17. if (fs.existsSync(filePath)) {
  18. return fileEqual(linesA, readFileByLine(filePath));
  19. }
  20. console.log(`${filePath} does not exists, writing...`);
  21. return false;
  22. });
  23. if (isEqual) {
  24. console.log(picocolors.gray(picocolors.dim(`same content, bail out writing: ${filePath}`)));
  25. return;
  26. }
  27. return span.traceChildAsync<void>(`writing ${filePath}`, async () => {
  28. const linesALen = linesA.length;
  29. console.log('writing', { linesA: fastStringArrayJoin(linesA, '\n').slice(-100) });
  30. // The default highwater mark is normally 16384,
  31. // So we make sure direct write to file if the content is
  32. // most likely less than 250 lines
  33. if (linesALen < 250) {
  34. return writeFile(filePath, fastStringArrayJoin(linesA, '\n'));
  35. }
  36. const writeStream = fs.createWriteStream(filePath);
  37. let p;
  38. for (let i = 0; i < linesALen; i++) {
  39. p = asyncWriteToStream(writeStream, linesA[i] + '\n');
  40. // eslint-disable-next-line no-await-in-loop -- stream high water mark
  41. if (p) await p;
  42. }
  43. await new Promise<void>(resolve => {
  44. // Since we previously poped the last empty line for comparison, we need to add it back here to ensure final EOF line
  45. writeStream.end('\n', resolve);
  46. });
  47. await promisify(writeStream.close.bind(writeStream))();
  48. });
  49. }