chenzhaoyang
2025-12-17 d3e5a4b7658ece4f845bbc0c4f95acf3fbdf8a61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import path from "node:path";
import { spawn } from "node:child_process";
import fs from "node:fs/promises";
 
const git = async (command, options) => {
  // create a promise based git wrapper around a spawned process
  return new Promise((resolve, reject) => {
    const currentPwd = process.cwd();
    const child = spawn("git", [command, ...options], {
      cwd: currentPwd,
      stdio: ["ignore", "pipe", "pipe"],
    });
    let stdout = "";
    let stderr = "";
 
    child.stdout.on("data", (data) => {
      stdout += data;
    });
 
    child.stderr.on("data", (data) => {
      stderr += data;
    });
 
    child.on("close", (code) => {
      if (code === 0) {
        resolve(stdout);
      } else {
        reject(stderr);
      }
    });
  });
};
 
/**
 * Get the commits affecting the current project
 * @param options
 * @returns {Promise<string>}
 */
const gitLog = async (options = []) => {
  const log = await git("log", options);
  return log.trim();
};
 
/**
 * Get the branch info of the current project
 * @param options
 * @returns {Promise<string>}
 */
const gitBranch = async (options = []) => {
  const branch = await git("branch", options);
  return branch.trim();
};
 
/**
 * @typedef {Object} CommitVersion
 * @property {string} message - The commit message of the latest commit to affect the current project
 * @property {string} commit - The commit hash of the latest commit to affect the current project
 * @property {string} date - The date of the latest commit to affect the current project
 * @property {string} branch - The current branch
 */
 
/**
 * Get the last commit data to have affected the current project
 * @returns {Promise<CommitVersion>}
 */
const getVersionData = async () => {
  const latestCommitInfo = await gitLog(["-n 1", "-p", "src/*"]);
  const commitInfo = latestCommitInfo.split("\n");
  const commit =
    commitInfo
      .find((line) => line.startsWith("commit"))
      ?.trim()
      .replace("commit", "")
      .trim() ?? "";
  let date = commitInfo.find((line) => line.startsWith("Date:")) ?? "";
  // First non-empty line after the Date: line is the commit message
  const message =
    commitInfo
      .slice(commitInfo.indexOf(date) + 1)
      .find((line) => line.trim().length > 0)
      ?.trim() ?? "";
  // Remove the Date: prefix from the date
  date = date.replace("Date:", "").trim();
 
  // Get the current branch of the latest commit
  const contains = (await gitBranch(["--contains", commit])).split("\n");
  let branch = (contains.find((line) => line.startsWith("develop") || line.startsWith("*")) ?? "")
    .replace("*", "")
    .trim();
 
  if (branch === "" || branch.includes("HEAD")) {
    branch = "develop";
  }
 
  return {
    message,
    commit,
    date: new Date(date).toISOString(),
    branch,
  };
};
 
const versionLib = async () => {
  const currentPwd = process.cwd();
  // if the currentPwd includes 'node_modules', we are running from within the monorepo package itself
  // and we have to account for the difference
  let workspaceRoot;
  let currentProjectPath;
  if (currentPwd.includes("node_modules")) {
    const [_workspaceRoot, nodeModulesPath, _currentProjectPath] = currentPwd.split("web");
    workspaceRoot = path.join(_workspaceRoot, "web", nodeModulesPath);
    currentProjectPath = _currentProjectPath;
  } else {
    const [_workspaceRoot, _currentProjectPath] = currentPwd.split("web");
    workspaceRoot = _workspaceRoot;
    currentProjectPath = _currentProjectPath;
  }
  const distPath = path.join(workspaceRoot, "web", "dist", currentProjectPath);
 
  try {
    await fs.mkdir(distPath, { recursive: true });
  } catch {
    /* ignore */
  }
 
  const versionData = await getVersionData();
  const versionJson = JSON.stringify(versionData, null, 2);
  const versionFile = path.join(distPath, "version.json");
  await fs.writeFile(versionFile, versionJson);
};
 
versionLib().then(() => {
  console.log("Versioning complete");
});