file.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import path from 'path';
  6. import fs from 'fs';
  7. import ignore, { Ignore } from 'ignore';
  8. export class File {
  9. content: string;
  10. isUtf8: boolean;
  11. relativePath: string;
  12. path: string;
  13. suffix: string;
  14. constructor(filePath: string, public root: string = '/') {
  15. this.path = filePath;
  16. this.relativePath = path.relative(this.root, this.path);
  17. this.suffix = path.extname(this.path);
  18. // Check if exists
  19. if (!fs.existsSync(this.path)) {
  20. throw Error(`File ${path} Not Exists`);
  21. }
  22. // If no utf-8, skip
  23. try {
  24. this.content = fs.readFileSync(this.path, 'utf-8');
  25. this.isUtf8 = true;
  26. } catch (e) {
  27. this.isUtf8 = false;
  28. return;
  29. }
  30. }
  31. replace(updater: (content: string) => string) {
  32. if (!this.isUtf8) {
  33. console.warn('Not UTF-8 file skipped: ', this.path);
  34. return;
  35. }
  36. this.content = updater(this.content);
  37. fs.writeFileSync(this.path, this.content, 'utf-8');
  38. }
  39. write(nextContent: string) {
  40. this.content = nextContent;
  41. fs.writeFileSync(this.path, this.content, 'utf-8');
  42. }
  43. }
  44. export function* traverseRecursiveFilePaths(
  45. folder: string,
  46. ig: Ignore = ignore().add('.git'),
  47. root: string = folder
  48. ): Generator<string> {
  49. const files = fs.readdirSync(folder);
  50. // add .gitignore to ignore if exists
  51. if (fs.existsSync(path.join(folder, '.gitignore'))) {
  52. ig.add(fs.readFileSync(path.join(folder, '.gitignore'), 'utf-8'));
  53. }
  54. for (const file of files) {
  55. const filePath = path.join(folder, file);
  56. if (ig.ignores(path.relative(root, filePath))) {
  57. continue;
  58. }
  59. if (fs.statSync(filePath).isDirectory()) {
  60. yield* traverseRecursiveFilePaths(filePath, ig, root);
  61. } else {
  62. yield filePath;
  63. }
  64. }
  65. }
  66. export function* traverseRecursiveFiles(folder: string): Generator<File> {
  67. for (const filePath of traverseRecursiveFilePaths(folder)) {
  68. yield new File(filePath, folder);
  69. }
  70. }