Files
notesnook/docs/help/scripts/check-strings.mjs
Ammar Ahmed 52d6044e73 docs(help): rebuild the help site with VitePress (#10169)
* docs: new help built with vitepress.

* docs: improve docs home ui

Added a Go to Docs button on homepage
Added a searchbar for directly searching what you are looking for in the docs

* docs(help): wire up with build system & setup proper ci

* Clean up new documentation (#10174)

* Docs: Cleanup pass 1

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* docs: cleanup pass 2

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* docs: Fix vscode's manglement that I missed.

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* Update docs/help/contents/plans-and-limits.md

Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* Update docs/help/contents/rich-text-editor/outline-lists.md

Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* docs: Remove self-hosting guide from sidebar, and comments for reviewer.

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

---------

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>

* docs: some fixes

* ci: do not publish help on push

* docs(help): improve regional pricing & free trials

* docs: improve sidebar

* docs: some more fixes after re-review

* docs: improve search and nav

* docs: a few more fixes

* docs: fix tabs formatting compat with prettier

* docs: fix tabs formatting in various places

* docs: show correct plus button image for mobile

* docs: disable notesnook self hosting docs

* docs: update help docs with fixes

---------

Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
Co-authored-by: Chloe Oletto <NeedsChloesure@riseup.net>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2026-08-14 08:57:51 +05:00

116 lines
3.9 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Keeps quoted UI labels in the docs tied to the app's string catalogue.
*
* npm run strings report only
* npm run strings -- --fix rewrite unambiguous labels into `{{key}}`
*
* A label written as `Archive` is a snapshot that goes stale silently. Written
* as `{{archive}}` it is resolved from packages/intl at build time, so renaming
* the string in the app updates the docs.
*
* The script never invents strings: it only matches text that an existing
* zero-argument key already produces.
*/
import { readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { pathToFileURL } from "node:url";
const HELP = new URL("..", import.meta.url).pathname;
const CONTENTS = join(HELP, "contents");
const FIX = process.argv.includes("--fix");
const { buildReverseIndex, resolveString } = await import(
pathToFileURL(join(HELP, ".vitepress/strings.mts")).href
).catch(async () => {
// .mts needs a TS-aware loader; fall back to the transpiled copy VitePress uses.
return await import(pathToFileURL(join(HELP, ".vitepress/strings.mjs")).href);
});
const byValue = buildReverseIndex();
function walk(dir, out = []) {
for (const entry of readdirSync(dir)) {
if (["public", "_versions"].includes(entry) || /^v\d/.test(entry)) continue;
const p = join(dir, entry);
if (statSync(p).isDirectory()) walk(p, out);
else if (p.endsWith(".md")) out.push(p);
}
return out;
}
/** Split frontmatter off; we only touch the body. */
function splitFrontmatter(text) {
if (!text.startsWith("---\n")) return ["", text];
const end = text.indexOf("\n---\n", 3);
return end === -1 ? ["", text] : [text.slice(0, end + 5), text.slice(end + 5)];
}
const stats = {
files: 0,
alreadyKeys: 0,
converted: 0,
ambiguous: new Map(),
unmatched: new Map()
};
for (const file of walk(CONTENTS)) {
const original = readFileSync(file, "utf8");
const [frontmatter, body] = splitFrontmatter(original);
const rel = relative(CONTENTS, file);
stats.alreadyKeys += (body.match(/\{\{[A-Za-z][A-Za-z0-9_]*\}\}/g) || []).length;
let changed = false;
const fences = [];
// Protect fenced code blocks from rewriting.
const masked = body.replace(/```[\s\S]*?```/g, (m) => {
fences.push(m);
return `FENCE${fences.length - 1}`;
});
const next = masked.replace(/`([^`\n]{2,60})`/g, (match, label) => {
if (label.startsWith("{{")) return match;
const keys = byValue.get(label);
if (!keys) {
if (/^[A-Z]/.test(label) && label.split(" ").length <= 4)
stats.unmatched.set(label, (stats.unmatched.get(label) || new Set()).add(rel));
return match;
}
if (keys.length > 1) {
stats.ambiguous.set(label, keys);
return match;
}
changed = true;
stats.converted++;
return `\`{{${keys[0]}}}\``;
});
if (changed && FIX) {
const restored = next.replace(/FENCE(\d+)/g, (_, i) => fences[Number(i)]);
writeFileSync(file, frontmatter + restored);
stats.files++;
}
}
console.log(
FIX
? `Rewrote ${stats.converted} labels into string keys across ${stats.files} files.`
: `${stats.converted} labels could be rewritten as string keys (run with --fix).`
);
console.log(`${stats.alreadyKeys} labels are already keys.`);
if (stats.ambiguous.size) {
console.log(`\n${stats.ambiguous.size} labels map to more than one key — left alone:`);
for (const [label, keys] of [...stats.ambiguous].slice(0, 15))
console.log(` "${label}" -> ${keys.join(", ")}`);
}
if (stats.unmatched.size) {
const sorted = [...stats.unmatched].sort((a, b) => b[1].size - a[1].size).slice(0, 20);
console.log(
`\n${stats.unmatched.size} capitalised labels have no matching string (they may be third-party UI, or genuinely absent from the catalogue):`
);
for (const [label, files] of sorted)
console.log(` "${label}" — ${[...files].slice(0, 2).join(", ")}`);
}