2024-08-27 12:23:49 +00:00
|
|
|
import fs from "fs"
|
|
|
|
import crypto from "crypto"
|
|
|
|
import path from "path"
|
|
|
|
import { validate } from "schema-utils"
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
import { access as accessCps } from "fs"
|
|
|
|
import { execFile as execFileCps } from "child_process"
|
|
|
|
import { promisify } from "util"
|
|
|
|
|
|
|
|
class SRIPlugin {
|
2022-01-06 12:58:10 +00:00
|
|
|
static defaultOptions = {
|
|
|
|
algorithm: "sha512",
|
|
|
|
sourceFile: "assets.json"
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(options = {}) {
|
2024-09-01 12:28:02 +00:00
|
|
|
this.options = { ...SRIPlugin.defaultOptions, ...options }
|
|
|
|
|
|
|
|
validate(
|
|
|
|
{
|
|
|
|
type: "object",
|
|
|
|
properties: {
|
|
|
|
sourceFile: { type: "string" },
|
|
|
|
outputFile: { type: "string" },
|
|
|
|
algorithm: { type: "string" }
|
2022-01-06 12:58:10 +00:00
|
|
|
}
|
2024-09-01 12:28:02 +00:00
|
|
|
},
|
|
|
|
options,
|
|
|
|
{
|
|
|
|
name: "SRI Plugin",
|
|
|
|
baseDataPath: "options"
|
2022-01-06 12:58:10 +00:00
|
|
|
}
|
2024-09-01 12:28:02 +00:00
|
|
|
)
|
|
|
|
}
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
apply(compiler) {
|
|
|
|
compiler.hooks.done.tap("SRIPlugin", () => {
|
|
|
|
const data = JSON.parse(fs.readFileSync(this.options.sourceFile, "utf8"))
|
|
|
|
const outputFile = this.options.outputFile || this.options.sourceFile
|
|
|
|
const { algorithm } = this.options
|
|
|
|
|
|
|
|
const calculateSRI = (file) => {
|
|
|
|
const fileContent = fs.readFileSync(path.join(".", "static", file))
|
|
|
|
const hash = crypto.createHash(algorithm).update(fileContent).digest("base64")
|
|
|
|
return `${algorithm}-${hash}`
|
|
|
|
}
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
Object.keys(data).forEach((key) => {
|
|
|
|
data[key].integrity = calculateSRI(data[key].src)
|
|
|
|
})
|
|
|
|
|
|
|
|
fs.writeFileSync(outputFile, JSON.stringify(data, null, 2), { encoding: "utf8", flag: "w" })
|
2022-01-06 12:58:10 +00:00
|
|
|
})
|
|
|
|
}
|
2024-09-01 12:28:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class GitVersionPlugin {
|
|
|
|
static defaultOptions = {
|
|
|
|
outputFile: "VERSION"
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(options = {}) {
|
|
|
|
this.options = { ...GitVersionPlugin.defaultOptions, ...options }
|
|
|
|
|
|
|
|
validate(
|
|
|
|
{
|
|
|
|
type: "object",
|
|
|
|
properties: {
|
|
|
|
outputFile: { type: "string" }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
options,
|
|
|
|
{
|
|
|
|
baseDataPath: "options",
|
|
|
|
name: "GitVersion Plugin"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2022-01-06 12:58:10 +00:00
|
|
|
|
|
|
|
apply(compiler) {
|
2024-09-01 12:28:02 +00:00
|
|
|
const { webpack, hooks, context } = compiler
|
|
|
|
const { Compilation } = webpack
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
hooks.beforeCompile.tapPromise("GitVersionPlugin", async () => {
|
|
|
|
const access = promisify(accessCps)
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
try {
|
|
|
|
await access(".git")
|
|
|
|
this.dependsOnGit = true
|
|
|
|
} catch {
|
|
|
|
this.dependsOnGit = false
|
|
|
|
}
|
|
|
|
})
|
2022-01-06 12:58:10 +00:00
|
|
|
|
2024-09-01 12:28:02 +00:00
|
|
|
hooks.compilation.tap("GitVersionPlugin", (compilation) => {
|
|
|
|
if (this.dependsOnGit) {
|
|
|
|
compilation.fileDependencies.add(path.join(context, ".git/logs/HEAD"))
|
|
|
|
compilation.contextDependencies.add(path.join(context, ".git/refs/tags"))
|
|
|
|
}
|
|
|
|
|
|
|
|
compilation.hooks.processAssets.tapPromise(
|
|
|
|
{
|
|
|
|
name: "GitVersionPlugin",
|
|
|
|
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
|
|
|
},
|
|
|
|
async (assets) => {
|
|
|
|
try {
|
|
|
|
const v = await this.version()
|
|
|
|
|
|
|
|
assets[this.options.outputFile] = {
|
|
|
|
source: () => `${v}\n`,
|
|
|
|
size: () => v.length + 1
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
assets[this.options.outputFile] = {
|
|
|
|
source: () => "",
|
|
|
|
size: () => 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2022-01-06 12:58:10 +00:00
|
|
|
})
|
|
|
|
}
|
2024-09-01 12:28:02 +00:00
|
|
|
|
|
|
|
async version() {
|
|
|
|
const execFile = promisify(execFileCps)
|
|
|
|
|
|
|
|
try {
|
|
|
|
const { stdout: describe } = await execFile("git", ["describe", "--long", "--tags"])
|
|
|
|
const [, tag, offset] = describe.trim().match(/^(.*)-(\d+)-g[0-9a-f]+$/)
|
|
|
|
return parseInt(offset) === 0 ? tag : this.getBranchAndHash()
|
|
|
|
} catch {
|
|
|
|
return this.getBranchAndHash()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async getBranchAndHash() {
|
|
|
|
const execFile = promisify(execFileCps)
|
|
|
|
const [{ stdout: branch }, { stdout: hash }] = await Promise.all([
|
|
|
|
execFile("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
|
|
execFile("git", ["rev-parse", "HEAD"])
|
|
|
|
])
|
|
|
|
return `${branch.trim()}@${hash.substring(0, 7)}`
|
|
|
|
}
|
2022-01-06 12:58:10 +00:00
|
|
|
}
|
2024-09-01 12:28:02 +00:00
|
|
|
|
|
|
|
export { SRIPlugin, GitVersionPlugin }
|