webpack.plugins.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import fs from "fs"
  2. import crypto from "crypto"
  3. import path from "path"
  4. import { validate } from "schema-utils"
  5. import { access as accessCps } from "fs"
  6. import { execFile as execFileCps } from "child_process"
  7. import { promisify } from "util"
  8. class SRIPlugin {
  9. static defaultOptions = {
  10. algorithm: "sha512",
  11. sourceFile: "assets.json"
  12. }
  13. constructor(options = {}) {
  14. this.options = { ...SRIPlugin.defaultOptions, ...options }
  15. validate(
  16. {
  17. type: "object",
  18. properties: {
  19. sourceFile: { type: "string" },
  20. outputFile: { type: "string" },
  21. algorithm: { type: "string" }
  22. }
  23. },
  24. options,
  25. {
  26. name: "SRI Plugin",
  27. baseDataPath: "options"
  28. }
  29. )
  30. }
  31. apply(compiler) {
  32. compiler.hooks.done.tap("SRIPlugin", () => {
  33. const data = JSON.parse(fs.readFileSync(this.options.sourceFile, "utf8"))
  34. const outputFile = this.options.outputFile || this.options.sourceFile
  35. const { algorithm } = this.options
  36. const calculateSRI = (file) => {
  37. const fileContent = fs.readFileSync(path.join(".", "static", file))
  38. const hash = crypto.createHash(algorithm).update(fileContent).digest("base64")
  39. return `${algorithm}-${hash}`
  40. }
  41. Object.keys(data).forEach((key) => {
  42. data[key].integrity = calculateSRI(data[key].src)
  43. })
  44. fs.writeFileSync(outputFile, JSON.stringify(data, null, 2), { encoding: "utf8", flag: "w" })
  45. })
  46. }
  47. }
  48. class GitVersionPlugin {
  49. static defaultOptions = {
  50. outputFile: "VERSION"
  51. }
  52. constructor(options = {}) {
  53. this.options = { ...GitVersionPlugin.defaultOptions, ...options }
  54. validate(
  55. {
  56. type: "object",
  57. properties: {
  58. outputFile: { type: "string" }
  59. }
  60. },
  61. options,
  62. {
  63. baseDataPath: "options",
  64. name: "GitVersion Plugin"
  65. }
  66. )
  67. }
  68. apply(compiler) {
  69. const { webpack, hooks, context } = compiler
  70. const { Compilation } = webpack
  71. hooks.beforeCompile.tapPromise("GitVersionPlugin", async () => {
  72. const access = promisify(accessCps)
  73. try {
  74. await access(".git")
  75. this.dependsOnGit = true
  76. } catch {
  77. this.dependsOnGit = false
  78. }
  79. })
  80. hooks.compilation.tap("GitVersionPlugin", (compilation) => {
  81. if (this.dependsOnGit) {
  82. compilation.fileDependencies.add(path.join(context, ".git/logs/HEAD"))
  83. compilation.contextDependencies.add(path.join(context, ".git/refs/tags"))
  84. }
  85. compilation.hooks.processAssets.tapPromise(
  86. {
  87. name: "GitVersionPlugin",
  88. stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
  89. },
  90. async (assets) => {
  91. try {
  92. const v = await this.version()
  93. assets[this.options.outputFile] = {
  94. source: () => `${v}\n`,
  95. size: () => v.length + 1
  96. }
  97. } catch {
  98. assets[this.options.outputFile] = {
  99. source: () => "",
  100. size: () => 0
  101. }
  102. }
  103. }
  104. )
  105. })
  106. }
  107. async version() {
  108. const execFile = promisify(execFileCps)
  109. try {
  110. const { stdout: describe } = await execFile("git", ["describe", "--long", "--tags"])
  111. const [, tag, offset] = describe.trim().match(/^(.*)-(\d+)-g[0-9a-f]+$/)
  112. return parseInt(offset) === 0 ? tag : this.getBranchAndHash()
  113. } catch {
  114. return this.getBranchAndHash()
  115. }
  116. }
  117. async getBranchAndHash() {
  118. const execFile = promisify(execFileCps)
  119. const [{ stdout: branch }, { stdout: hash }] = await Promise.all([
  120. execFile("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
  121. execFile("git", ["rev-parse", "HEAD"])
  122. ])
  123. return `${branch.trim()}@${hash.substring(0, 7)}`
  124. }
  125. }
  126. export { SRIPlugin, GitVersionPlugin }