Skip to content

Commit a1fbafc

Browse files
convert fix versions script to esm and updated script
1 parent f8788cf commit a1fbafc

File tree

3 files changed

+229
-156
lines changed

3 files changed

+229
-156
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"scripts": {
2525
"prebuild": "rm -rf dist",
2626
"build": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json && echo '{\"type\": \"commonjs\"}'> dist/cjs/package.json",
27-
"docs": "typedoc --options typedoc.cjs && node scripts/fix-versions.cjs",
27+
"docs": "node scripts/fix-versions.js --pre-build && typedoc --options typedoc.cjs && node scripts/fix-versions.js",
2828
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest",
2929
"test:ci": "c8 npm run test -- --maxWorkers=1",
3030
"precommit": "pretty-quick --staged",

scripts/fix-versions.cjs

Lines changed: 0 additions & 155 deletions
This file was deleted.

scripts/fix-versions.js

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Documentation Version Management Script
5+
*
6+
* Enforces our documentation versioning policy:
7+
* - Maximum of 2 major version lines at any time
8+
* - Stable cycle: Latest major + Previous major
9+
* - Pre-release cycle: Latest major + New pre-release (removes old previous major or current pre-release)
10+
*/
11+
12+
import fs from 'fs';
13+
import path from 'path';
14+
import semver from 'semver';
15+
import { readFileSync } from 'fs';
16+
import { fileURLToPath } from 'url';
17+
18+
const __filename = fileURLToPath(import.meta.url);
19+
const __dirname = path.dirname(__filename);
20+
21+
const DOCS_DIR = './docs';
22+
const VERSIONS_JS_FILE = path.join(DOCS_DIR, 'versions.js');
23+
24+
/**
25+
* Get current version from package.json
26+
*/
27+
function getCurrentVersion() {
28+
const packageJsonPath = path.join(__dirname, '../package.json');
29+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
30+
return packageJson.version;
31+
}
32+
33+
/**
34+
* Get all existing version directories
35+
*/
36+
function getExistingVersions() {
37+
if (!fs.existsSync(DOCS_DIR)) return [];
38+
39+
return fs
40+
.readdirSync(DOCS_DIR, { withFileTypes: true })
41+
.filter(
42+
(entry) =>
43+
entry.isDirectory() && semver.valid(entry.name.replace(/^v/, ''))
44+
)
45+
.map((entry) => entry.name.replace(/^v/, ''))
46+
.sort(semver.rcompare);
47+
}
48+
49+
/**
50+
* Determine which versions to keep based on our policy
51+
*/
52+
function getVersionsToKeep(currentVersion, existingVersions) {
53+
const allVersions = [...new Set([currentVersion, ...existingVersions])].sort(
54+
semver.rcompare
55+
);
56+
const isCurrentPrerelease = semver.prerelease(currentVersion);
57+
58+
// Get current branch from environment (GitHub Actions) or git
59+
const currentBranch = process.env.GITHUB_REF_NAME ||
60+
process.env.GITHUB_HEAD_REF ||
61+
'master';
62+
63+
console.log(`🌿 Current branch: ${currentBranch}`);
64+
65+
if (isCurrentPrerelease || currentBranch.startsWith('v')) {
66+
// Scenario B: Pre-release or version branch - Keep more versions for development
67+
const currentMajor = semver.major(currentVersion);
68+
const stableVersions = allVersions.filter((v) => !semver.prerelease(v));
69+
70+
// Keep current version + latest stable major + previous major
71+
const latestStableMajor = stableVersions.find(
72+
(v) => semver.major(v) < currentMajor
73+
);
74+
const previousMajor = stableVersions.find(
75+
(v) => semver.major(v) < semver.major(latestStableMajor || currentVersion)
76+
);
77+
78+
return [currentVersion, latestStableMajor, previousMajor].filter(Boolean);
79+
} else {
80+
// Scenario A: Stable - Keep latest 2 major versions (stable only)
81+
const stableVersions = allVersions.filter((v) => !semver.prerelease(v));
82+
const majorVersions = [...new Set(stableVersions.map(semver.major))].slice(
83+
0,
84+
2
85+
);
86+
87+
return majorVersions.map((major) =>
88+
stableVersions.find((v) => semver.major(v) === major)
89+
);
90+
}
91+
}
92+
93+
/**
94+
* Clean up old version directories
95+
*/
96+
function cleanupOldVersions(versionsToKeep, existingVersions) {
97+
const toRemove = existingVersions.filter((v) => !versionsToKeep.includes(v));
98+
99+
toRemove.forEach((version) => {
100+
const versionDir = path.join(DOCS_DIR, `v${version}`);
101+
if (fs.existsSync(versionDir)) {
102+
console.log(`🗑️ Removing old documentation: v${version}`);
103+
fs.rmSync(versionDir, { recursive: true, force: true });
104+
}
105+
});
106+
}
107+
108+
/**
109+
* Clean up symlinks and directories that TypeDoc versioning plugin will manage
110+
* This prevents EEXIST errors when the plugin tries to create new symlinks
111+
*/
112+
function cleanupVersionSymlinks() {
113+
const symlinkNames = ['stable', 'dev', 'latest', 'next'];
114+
115+
// Also clean up potential version alias symlinks (like v4.29, v5.0, etc.)
116+
const existingVersions = getExistingVersions();
117+
existingVersions.forEach(version => {
118+
const majorMinor = `v${semver.major(version)}.${semver.minor(version)}`;
119+
const major = `v${semver.major(version)}`;
120+
symlinkNames.push(majorMinor, major);
121+
});
122+
123+
symlinkNames.forEach((name) => {
124+
const linkPath = path.join(DOCS_DIR, name);
125+
if (fs.existsSync(linkPath)) {
126+
const stats = fs.lstatSync(linkPath);
127+
if (stats.isSymbolicLink()) {
128+
console.log(`🔗 Removing existing symlink: ${name}`);
129+
fs.unlinkSync(linkPath);
130+
} else if (stats.isDirectory()) {
131+
// Only remove directories that match version patterns or known aliases
132+
if (name.match(/^v\d+(\.\d+)?$/) || ['stable', 'dev', 'latest', 'next'].includes(name)) {
133+
console.log(`📁 Removing conflicting directory: ${name}`);
134+
fs.rmSync(linkPath, { recursive: true, force: true });
135+
}
136+
}
137+
}
138+
});
139+
}
140+
141+
/**
142+
* Update versions.js for TypeDoc plugin
143+
* Orders versions with stable versions first, then pre-release versions
144+
*/
145+
function updateVersionsFile(versionsToKeep) {
146+
// Sort versions: stable versions first (descending), then pre-release versions (descending)
147+
const sortedVersions = [...versionsToKeep].sort((a, b) => {
148+
const aIsPrerelease = semver.prerelease(a);
149+
const bIsPrerelease = semver.prerelease(b);
150+
151+
// If one is stable and the other is pre-release, stable comes first
152+
if (!aIsPrerelease && bIsPrerelease) return -1;
153+
if (aIsPrerelease && !bIsPrerelease) return 1;
154+
155+
// If both are the same type (both stable or both pre-release), sort by version descending
156+
return semver.rcompare(a, b);
157+
});
158+
159+
const versionNames = sortedVersions.map((version) => `'v${version}'`);
160+
const versionsJsContent = `"use strict"
161+
export const DOC_VERSIONS = [
162+
\t${versionNames.join(',\n\t')},
163+
];
164+
`;
165+
166+
fs.mkdirSync(DOCS_DIR, { recursive: true });
167+
fs.writeFileSync(VERSIONS_JS_FILE, versionsJsContent);
168+
console.log(
169+
`📝 Updated versions.js with ${versionsToKeep.length} versions in order: ${sortedVersions.join(', ')}`
170+
);
171+
}
172+
173+
/**
174+
* Pre-build cleanup function - call this before running TypeDoc
175+
*/
176+
function preBuildCleanup() {
177+
console.log('🧹 Pre-build cleanup...');
178+
cleanupVersionSymlinks();
179+
console.log('✅ Pre-build cleanup complete!');
180+
}
181+
182+
/**
183+
* Main function - call this after TypeDoc has generated documentation
184+
*/
185+
function main() {
186+
console.log('🚀 Managing documentation versions...');
187+
188+
const currentVersion = getCurrentVersion();
189+
const isPrerelease = semver.prerelease(currentVersion)
190+
? 'pre-release'
191+
: 'stable';
192+
console.log(`📦 Current version: ${currentVersion} (${isPrerelease})`);
193+
194+
const existingVersions = getExistingVersions();
195+
console.log(
196+
`📚 Found ${existingVersions.length} existing documentation versions`
197+
);
198+
199+
const versionsToKeep = getVersionsToKeep(currentVersion, existingVersions);
200+
console.log(`✅ Keeping ${versionsToKeep.length} versions:`, versionsToKeep);
201+
202+
cleanupOldVersions(versionsToKeep, existingVersions);
203+
updateVersionsFile(versionsToKeep);
204+
205+
console.log('✨ Documentation version management complete!');
206+
}
207+
208+
// Check if this file is being run directly
209+
if (import.meta.url === `file://${process.argv[1]}`) {
210+
// Check if we should run pre-build cleanup
211+
const args = process.argv.slice(2);
212+
if (args.includes('--pre-build')) {
213+
preBuildCleanup();
214+
} else {
215+
main();
216+
}
217+
}
218+
219+
export {
220+
getCurrentVersion,
221+
getExistingVersions,
222+
getVersionsToKeep,
223+
cleanupOldVersions,
224+
cleanupVersionSymlinks,
225+
updateVersionsFile,
226+
preBuildCleanup,
227+
main,
228+
};

0 commit comments

Comments
 (0)