Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: migrate to node:fs/promises #8310

Closed
wants to merge 11 commits into from
40 changes: 19 additions & 21 deletions build/cli.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import zlib from "node:zlib";

import chalk from "chalk";
import cliProgress from "cli-progress";
import caporal from "@caporal/core";
import fse from "fs-extra";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of using an extra library here (I see we already use it in main, we should remove it from there too) - the inbuilt node lib should be sufficient.

import inquirer from "inquirer";

import { Document, slugToFolder, translationsOf } from "../content/index.js";
Expand Down Expand Up @@ -181,24 +182,24 @@ async function buildDocuments(
} = result;

const outPath = path.join(BUILD_OUT_ROOT, slugToFolder(document.url));
fs.mkdirSync(outPath, { recursive: true });
await fs.mkdir(outPath, { recursive: true });

if (builtDocument.flaws) {
appendTotalFlaws(builtDocument.flaws);
}

if (!noHTML) {
fs.writeFileSync(
await fs.writeFile(
path.join(outPath, "index.html"),
renderHTML(document.url, { doc: builtDocument })
await renderHTML(document.url, { doc: builtDocument })
);
}

// This is exploiting the fact that renderHTML has the side-effect of
// mutating the built document which makes this not great and refactor-worthy.
const docString = JSON.stringify({ doc: builtDocument });
fs.writeFileSync(path.join(outPath, "index.json"), docString);
fs.writeFileSync(
await fs.writeFile(path.join(outPath, "index.json"), docString);
await fs.writeFile(
path.join(outPath, "contributors.txt"),
renderContributorsTxt(
document.metadata.contributors,
Expand All @@ -208,13 +209,13 @@ async function buildDocuments(

for (const { id, html } of liveSamples) {
const liveSamplePath = path.join(outPath, `_sample_.${id}.html`);
fs.writeFileSync(liveSamplePath, html);
await fs.writeFile(liveSamplePath, html);
}

for (const filePath of fileAttachments) {
// We *could* use symlinks instead. But, there's no point :)
// Yes, a symlink is less disk I/O but it's nominal.
fs.copyFileSync(filePath, path.join(outPath, path.basename(filePath)));
await fs.copyFile(filePath, path.join(outPath, path.basename(filePath)));
}

// Collect active documents' slugs to be used in sitemap building and
Expand Down Expand Up @@ -242,10 +243,7 @@ async function buildDocuments(
} = builtDocument;
builtMetadata.hash = hash;

fs.writeFileSync(
path.join(outPath, "metadata.json"),
JSON.stringify(builtMetadata)
);
await fse.writeJson(path.join(outPath, "metadata.json"), builtMetadata);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the prior JSON.stringify, but use await fs.writeFile - this applies to all introductions of fse.writeJson.

if (metadata[document.metadata.locale]) {
metadata[document.metadata.locale].push(builtMetadata);
} else {
Expand Down Expand Up @@ -273,26 +271,26 @@ async function buildDocuments(
"sitemaps",
locale.toLowerCase()
);
fs.mkdirSync(sitemapDir, { recursive: true });
await fs.mkdir(sitemapDir, { recursive: true });
const sitemapFilePath = path.join(sitemapDir, "sitemap.xml.gz");
fs.writeFileSync(
await fs.writeFile(
sitemapFilePath,
zlib.gzipSync(makeSitemapXML(locale, docs))
);
}

searchIndex.sort();
for (const [locale, items] of Object.entries(searchIndex.getItems())) {
fs.writeFileSync(
await fse.writeJson(
path.join(BUILD_OUT_ROOT, locale.toLowerCase(), "search-index.json"),
JSON.stringify(items)
items
);
}

for (const [locale, meta] of Object.entries(metadata)) {
fs.writeFileSync(
await fse.writeJson(
path.join(BUILD_OUT_ROOT, locale.toLowerCase(), "metadata.json"),
JSON.stringify(meta)
meta
);
}

Expand All @@ -302,7 +300,7 @@ async function buildDocuments(
doc.browserCompat?.forEach((query) => allBrowserCompat.add(query))
)
);
fs.writeFileSync(
await fs.writeFile(
path.join(BUILD_OUT_ROOT, "allBrowserCompat.txt"),
[...allBrowserCompat].join(" ")
);
Expand Down Expand Up @@ -390,14 +388,14 @@ program
locale,
"sitemap.xml.gz"
);
if (fs.existsSync(sitemapFilePath)) {
if (await fse.pathExists(sitemapFilePath)) {
sitemapsBuilt.push(sitemapFilePath);
locales.push(locale);
}
Comment on lines +391 to 394
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

access should work here instead, and all similar checks:

Suggested change
if (await fse.pathExists(sitemapFilePath)) {
sitemapsBuilt.push(sitemapFilePath);
locales.push(locale);
}
try {
await fs.access(sitemapFilePath);
sitemapsBuilt.push(sitemapFilePath);
locales.push(locale);
} catch {
if (e.code !== "ENOENT") throw e;
}

}

const sitemapIndexFilePath = path.join(BUILD_OUT_ROOT, "sitemap.xml");
fs.writeFileSync(
await fs.writeFile(
sitemapIndexFilePath,
makeSitemapIndexXML(
sitemapsBuilt.map((fp) => fp.replace(BUILD_OUT_ROOT, ""))
Expand Down
14 changes: 8 additions & 6 deletions build/git-history.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import fs from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";

import fse from "fs-extra";

import { execGit } from "../content/index.js";
import { CONTENT_ROOT } from "../libs/env/index.js";

function getFromGit(contentRoot = CONTENT_ROOT) {
async function getFromGit(contentRoot = CONTENT_ROOT) {
// If `contentRoot` was a symlink, the `repoRoot` won't be. That'll make it
// impossible to compute the relative path for files within when we get
// output back from `git log ...`.
// So, always normalize to the real path.
const realContentRoot = fs.realpathSync(contentRoot);
const realContentRoot = await fs.realpath(contentRoot);

const repoRoot = execGit(["rev-parse", "--show-toplevel"], {
cwd: realContentRoot,
Expand Down Expand Up @@ -64,17 +66,17 @@ function getFromGit(contentRoot = CONTENT_ROOT) {
return [map, parents];
}

export function gather(contentRoots, previousFile = null) {
export async function gather(contentRoots, previousFile = null) {
const map = new Map();
if (previousFile) {
const previous = JSON.parse(fs.readFileSync(previousFile, "utf-8"));
const previous = await fse.readJson(previousFile, "utf-8");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep JSON.parse for this and all other additions of fse.ReadJson:

Suggested change
const previous = await fse.readJson(previousFile, "utf-8");
const previous = JSON.parse(await fs.readFile(previousFile, "utf-8"));

for (const [key, value] of Object.entries(previous)) {
map.set(key, value);
}
}
// Every key in this map is a path, relative to root.
for (const contentRoot of contentRoots) {
const [commits, parents] = getFromGit(contentRoot);
const [commits, parents] = await getFromGit(contentRoot);
for (const [key, value] of commits) {
// Because CONTENT_*_ROOT isn't necessarily the same as the path relative to
// the git root. For example "../README.md" and since those aren't documents
Expand Down
11 changes: 6 additions & 5 deletions build/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fs from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";

import chalk from "chalk";
Expand Down Expand Up @@ -263,8 +263,8 @@ function makeTOC(doc) {
*
* @param {Document} document
*/
function getAdjacentImages(documentDirectory) {
const dirents = fs.readdirSync(documentDirectory, { withFileTypes: true });
async function getAdjacentImages(documentDirectory) {
const dirents = await fs.readdir(documentDirectory, { withFileTypes: true });
return dirents
.filter((dirent) => {
// This needs to match what we do in filecheck/checker.py
Expand Down Expand Up @@ -501,9 +501,10 @@ export async function buildDocument(
// The checkImageReferences() does 2 things. Checks image *references* and
// it returns which images it checked. But we'll need to complement any
// other images in the folder.
getAdjacentImages(path.dirname(document.fileInfo.path)).forEach((fp) =>
fileAttachments.add(fp)
const adjacentImages = await getAdjacentImages(
path.dirname(document.fileInfo.path)
);
adjacentImages.forEach((fp) => fileAttachments.add(fp));

// Check the img tags for possible flaws and possible build-time rewrites
checkImageWidths(doc, $, options, document);
Expand Down
60 changes: 32 additions & 28 deletions build/spas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fs from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

Expand Down Expand Up @@ -45,8 +45,8 @@ async function buildContributorSpotlight(
const prefix = "community/spotlight";
const profileImg = "profile-image.jpg";

for (const contributor of fs.readdirSync(contributorSpotlightRoot)) {
const markdown = fs.readFileSync(
for (const contributor of await fs.readdir(contributorSpotlightRoot)) {
const markdown = await fs.readFile(
`${contributorSpotlightRoot}/${contributor}/index.md`,
"utf-8"
);
Expand All @@ -68,7 +68,10 @@ async function buildContributorSpotlight(
};
const context = { hyData };

const html = renderHTML(`/${locale}/${prefix}/${contributor}`, context);
const html = await renderHTML(
`/${locale}/${prefix}/${contributor}`,
context
);
const outPath = path.join(
BUILD_OUT_ROOT,
locale.toLowerCase(),
Expand All @@ -79,10 +82,10 @@ async function buildContributorSpotlight(
const imgFileDestPath = path.join(outPath, profileImg);
const jsonFilePath = path.join(outPath, "index.json");

fs.mkdirSync(outPath, { recursive: true });
fs.writeFileSync(filePath, html);
fs.copyFileSync(imgFilePath, imgFileDestPath);
fs.writeFileSync(jsonFilePath, JSON.stringify(context));
await fs.mkdir(outPath, { recursive: true });
await fs.writeFile(filePath, html);
await fs.copyFile(imgFilePath, imgFileDestPath);
await fs.writeFile(jsonFilePath, JSON.stringify(context));

if (options.verbose) {
console.log("Wrote", filePath);
Expand All @@ -105,14 +108,14 @@ export async function buildSPAs(options: {

// The URL isn't very important as long as it triggers the right route in the <App/>
const url = `/${DEFAULT_LOCALE}/404.html`;
const html = renderHTML(url, { pageNotFound: true });
const html = await renderHTML(url, { pageNotFound: true });
const outPath = path.join(
BUILD_OUT_ROOT,
DEFAULT_LOCALE.toLowerCase(),
"_spas"
);
fs.mkdirSync(outPath, { recursive: true });
fs.writeFileSync(path.join(outPath, path.basename(url)), html);
await fs.mkdir(outPath, { recursive: true });
await fs.writeFile(path.join(outPath, path.basename(url)), html);
buildCount++;
if (options.verbose) {
console.log("Wrote", path.join(outPath, path.basename(url)));
Expand All @@ -124,8 +127,8 @@ export async function buildSPAs(options: {
if (!root) {
continue;
}
for (const pathLocale of fs.readdirSync(root)) {
if (!fs.statSync(path.join(root, pathLocale)).isDirectory()) {
for (const pathLocale of await fs.readdir(root)) {
if (!(await fs.stat(path.join(root, pathLocale))).isDirectory()) {
continue;
}

Expand Down Expand Up @@ -169,11 +172,11 @@ export async function buildSPAs(options: {
noIndexing,
};

const html = renderHTML(url, context);
const html = await renderHTML(url, context);
const outPath = path.join(BUILD_OUT_ROOT, pathLocale, prefix);
fs.mkdirSync(outPath, { recursive: true });
await fs.mkdir(outPath, { recursive: true });
const filePath = path.join(outPath, "index.html");
fs.writeFileSync(filePath, html);
await fs.writeFile(filePath, html);
buildCount++;
if (options.verbose) {
console.log("Wrote", filePath);
Expand All @@ -200,14 +203,14 @@ export async function buildSPAs(options: {
.withErrors()
.filter((path) => path.endsWith(".md"))
.crawl(dirpath);
const filepaths = [...(crawler.sync() as PathsOutput)];
const filepaths: PathsOutput = await crawler.withPromise();

for (const filepath of filepaths) {
const file = filepath.replace(dirpath, "");
const page = file.split(".")[0];

const locale = DEFAULT_LOCALE.toLowerCase();
const markdown = fs.readFileSync(filepath, "utf-8");
const markdown = await fs.readFile(filepath, "utf-8");

const frontMatter = frontmatter<DocFrontmatter>(markdown);
const rawHTML = await m2h(frontMatter.body, { locale });
Expand All @@ -226,22 +229,22 @@ export async function buildSPAs(options: {
pageTitle: `${frontMatter.attributes.title || ""} | ${title}`,
};

const html = renderHTML(url, context);
const html = await renderHTML(url, context);
const outPath = path.join(
BUILD_OUT_ROOT,
locale,
...slug.split("/"),
page
);
fs.mkdirSync(outPath, { recursive: true });
await fs.mkdir(outPath, { recursive: true });
const filePath = path.join(outPath, "index.html");
fs.writeFileSync(filePath, html);
await fs.writeFile(filePath, html);
buildCount++;
if (options.verbose) {
console.log("Wrote", filePath);
}
const filePathContext = path.join(outPath, "index.json");
fs.writeFileSync(filePathContext, JSON.stringify(context));
await fs.writeFile(filePathContext, JSON.stringify(context));
}
}

Expand All @@ -262,12 +265,13 @@ export async function buildSPAs(options: {
if (!root) {
continue;
}
for (const localeLC of fs.readdirSync(root)) {

for (const localeLC of await fs.readdir(root)) {
const locale = VALID_LOCALES.get(localeLC) || localeLC;
if (!isValidLocale(locale)) {
continue;
}
if (!fs.statSync(path.join(root, localeLC)).isDirectory()) {
if (!(await fs.statSync(path.join(root, localeLC)).isDirectory())) {
continue;
}

Expand Down Expand Up @@ -304,11 +308,11 @@ export async function buildSPAs(options: {
featuredArticles,
};
const context = { hyData };
const html = renderHTML(url, context);
const html = await renderHTML(url, context);
const outPath = path.join(BUILD_OUT_ROOT, localeLC);
fs.mkdirSync(outPath, { recursive: true });
await fs.mkdir(outPath, { recursive: true });
const filePath = path.join(outPath, "index.html");
fs.writeFileSync(filePath, html);
await fs.writeFile(filePath, html);
buildCount++;
if (options.verbose) {
console.log("Wrote", filePath);
Expand All @@ -317,7 +321,7 @@ export async function buildSPAs(options: {
// Also, dump the recent pull requests in a file so the data can be gotten
// in client-side rendering.
const filePathContext = path.join(outPath, "index.json");
fs.writeFileSync(filePathContext, JSON.stringify(context));
await fs.writeFile(filePathContext, JSON.stringify(context));
buildCount++;
if (options.verbose) {
console.log("Wrote", filePathContext);
Expand Down
6 changes: 3 additions & 3 deletions build/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fs from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";

import * as cheerio from "cheerio";
Expand Down Expand Up @@ -112,10 +112,10 @@ export async function downloadAndResizeImage(src, out, basePath) {
imageBuffer.length
)} Compressed: ${humanFileSize(compressedImageBuffer.length)}`
);
fs.writeFileSync(destination, compressedImageBuffer);
await fs.writeFile(destination, compressedImageBuffer);
} else {
console.log(`Raw image size: ${humanFileSize(imageBuffer.length)}`);
fs.writeFileSync(destination, imageBuffer);
await fs.writeFile(destination, imageBuffer);
}
return destination;
}
Expand Down
Loading