mirror of
https://github.com/danog/sass-site.git
synced 2024-11-27 04:24:50 +01:00
124 lines
3.2 KiB
JavaScript
124 lines
3.2 KiB
JavaScript
const { spawn: nodeSpawn } = require('node:child_process');
|
|
const fs = require('node:fs/promises');
|
|
|
|
const deepEqual = require('deep-equal');
|
|
const chalk = require('kleur');
|
|
|
|
const VERSION_CACHE_PATH = './source/_data/versionCache.json';
|
|
|
|
/**
|
|
* Promise version of `spawn` to avoid blocking the main thread while waiting
|
|
* for the child processes.
|
|
*/
|
|
const spawn = (cmd, args, options) => {
|
|
return new Promise((resolve, reject) => {
|
|
const child = nodeSpawn(cmd, args, options);
|
|
const stderr = [];
|
|
const stdout = [];
|
|
child.stdout.on('data', (data) => {
|
|
stdout.push(data.toString());
|
|
});
|
|
child.on('error', (e) => {
|
|
stderr.push(e.toString());
|
|
});
|
|
child.on('close', () => {
|
|
if (stderr.length) {
|
|
reject(stderr.join(''));
|
|
} else {
|
|
resolve(stdout.join(''));
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Retrieves cached version object from cache file.
|
|
*/
|
|
const getCacheFile = async () => {
|
|
if (process.env.NETLIFY || process.env.REBUILD_VERSION_CACHE) {
|
|
return {};
|
|
}
|
|
let versionCache;
|
|
try {
|
|
versionCache = JSON.parse(await fs.readFile(VERSION_CACHE_PATH));
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') {
|
|
versionCache = {}; // Cache is missing and needs to be created
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
return versionCache;
|
|
};
|
|
|
|
/**
|
|
* Writes version object to cache file.
|
|
*/
|
|
const writeCacheFile = async (cache) => {
|
|
// eslint-disable-next-line no-console
|
|
console.info(chalk.green(`[11ty] Writing version cache file...`));
|
|
await fs.writeFile(VERSION_CACHE_PATH, JSON.stringify(cache));
|
|
};
|
|
|
|
/**
|
|
* Retrieves the highest stable version of `repo`, based on its git tags.
|
|
*/
|
|
const getLatestVersion = async (repo) => {
|
|
// eslint-disable-next-line no-console
|
|
console.info(chalk.cyan(`[11ty] Fetching version information for ${repo}`));
|
|
const { parseSemVer, compareSemVer } = await import('semver-parser');
|
|
let stdout;
|
|
try {
|
|
stdout = await spawn(
|
|
'git',
|
|
['ls-remote', '--tags', '--refs', `https://github.com/${repo}`],
|
|
{ env: { ...process.env, GIT_TERMINAL_PROMPT: 0 } },
|
|
);
|
|
} catch (err) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(chalk.red(`[11ty] Failed to fetch git tags for ${repo}`));
|
|
throw err;
|
|
}
|
|
const isNotPreRelease = (version) => {
|
|
const parsed = parseSemVer(version);
|
|
return parsed.matches && !parsed.pre;
|
|
};
|
|
const version = stdout
|
|
.split('\n')
|
|
.map((line) => line.split('refs/tags/').at(-1))
|
|
.filter(isNotPreRelease)
|
|
.sort(compareSemVer)
|
|
.at(-1);
|
|
|
|
return version;
|
|
};
|
|
|
|
/**
|
|
* Returns the version and URL for the latest release of the given
|
|
* implementation.
|
|
*/
|
|
module.exports = async () => {
|
|
const repos = ['sass/libsass', 'sass/dart-sass', 'sass/migrator'];
|
|
const cache = await getCacheFile();
|
|
|
|
const versions = await Promise.all(
|
|
repos.map(async (repo) => [
|
|
repo,
|
|
cache[repo] ?? (await getLatestVersion(repo)),
|
|
]),
|
|
);
|
|
const data = Object.fromEntries(
|
|
versions.map(([repo, version]) => [
|
|
repo.replace('sass/', ''),
|
|
{ version, url: `https://github.com/${repo}/releases/tag/${version}` },
|
|
]),
|
|
);
|
|
|
|
const nextCache = Object.fromEntries(versions);
|
|
if (!deepEqual(cache, nextCache)) {
|
|
await writeCacheFile(nextCache);
|
|
}
|
|
|
|
return data;
|
|
};
|