signed-file-generator.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /* eslint-env node */
  2. /* eslint import/no-nodejs-modules:off */
  3. import fs from 'fs';
  4. import {glob} from 'glob';
  5. import prettier from 'prettier';
  6. import SignedSource from 'signedsource';
  7. import webpack from 'webpack';
  8. type GlobPattern = Parameters<typeof glob>[0];
  9. type Options = {
  10. cwd: string;
  11. output: string;
  12. pattern: GlobPattern;
  13. };
  14. abstract class SignedFileGenerator<Data> {
  15. name: string;
  16. isWatchMode: boolean = false;
  17. cwd: string;
  18. pattern: GlobPattern;
  19. output: string;
  20. constructor(name: string, {cwd, pattern, output}: Options) {
  21. this.name = name;
  22. this.cwd = cwd;
  23. this.pattern = pattern;
  24. this.output = output;
  25. }
  26. apply(compiler: webpack.Compiler) {
  27. compiler.hooks.watchRun.tapAsync(this.name, async (_, callback) => {
  28. this.isWatchMode = true;
  29. await this.build();
  30. callback();
  31. });
  32. compiler.hooks.beforeRun.tapAsync(this.name, async (_, callback) => {
  33. if (this.isWatchMode) {
  34. callback();
  35. return;
  36. }
  37. await this.build();
  38. callback();
  39. });
  40. }
  41. async build() {
  42. const files = await this.findFiles();
  43. const data = await this.generateData(files);
  44. const content = this.sourceTemplate(data, SignedSource.getSigningToken());
  45. const formatted = await this.formatSource(content);
  46. const signed = SignedSource.signFile(formatted);
  47. if (this.isChanged(signed)) {
  48. this.writeFile(signed);
  49. }
  50. }
  51. async findFiles() {
  52. const files = await glob(this.pattern, {
  53. cwd: this.cwd,
  54. });
  55. return files;
  56. }
  57. abstract generateData(files: string[]): Promise<Data> | Data;
  58. abstract sourceTemplate(data: Data, signingToken: string): string;
  59. async formatSource(unformatted: string) {
  60. const config = await prettier.resolveConfig(this.output);
  61. if (config) {
  62. return prettier.format(unformatted, {...config, parser: 'babel'});
  63. }
  64. return unformatted;
  65. }
  66. isChanged(signed: string) {
  67. try {
  68. const origContent = fs.readFileSync(this.output, 'utf8');
  69. return origContent !== signed;
  70. } catch {
  71. return true;
  72. }
  73. }
  74. writeFile(content: string) {
  75. const tmpFile = this.output + '.tmp';
  76. fs.writeFileSync(tmpFile, content);
  77. fs.renameSync(tmpFile, this.output);
  78. }
  79. }
  80. export default SignedFileGenerator;