Skip to content

Commit cea078e

Browse files
authored
Merge branch 'next' into chore/fullname-name-patterns
2 parents c3ccd26 + 9c3618d commit cea078e

File tree

8 files changed

+206
-15
lines changed

8 files changed

+206
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ versions.json
7878
/docs/.vitepress/cache
7979
/docs/.vitepress/dist
8080
/docs/api/typedoc.json
81+
/docs/public/api-diff-index.json
8182
/lib
8283
/locale
8384

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"docs:dev": "run-s docs:prepare docs:dev:run",
6767
"docs:dev:run": "vitepress dev docs",
6868
"docs:serve": "vitepress serve docs --port 5173",
69+
"docs:diff": "tsx ./scripts/diff.ts",
6970
"format": "prettier --write .",
7071
"lint": "eslint --cache --cache-strategy content .",
7172
"ts-check:scripts": "tsc --project tsconfig.check-scripts.json",

scripts/apidoc/apiDocsWriter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import {
99
selectApiMethods,
1010
selectApiModules,
1111
} from './typedoc';
12-
import type { PageIndex } from './utils';
13-
import { pathDocsDir, pathOutputDir } from './utils';
12+
import type { DocsApiDiffIndex, PageIndex } from './utils';
13+
import { pathDocsDiffIndexFile, pathDocsDir, pathOutputDir } from './utils';
1414

1515
const pathDocsApiPages = resolve(pathDocsDir, '.vitepress', 'api-pages.ts');
1616
const pathDocsApiSearchIndex = resolve(
@@ -120,6 +120,10 @@ export function writeApiPagesIndex(pages: PageIndex): void {
120120
writeFileSync(pathDocsApiPages, apiPagesContent);
121121
}
122122

123+
export function writeApiDiffIndex(diffIndex: DocsApiDiffIndex): void {
124+
writeFileSync(pathDocsDiffIndexFile, JSON.stringify(diffIndex));
125+
}
126+
123127
export function writeApiSearchIndex(project: ProjectReflection): void {
124128
const apiIndex: APIGroup[] = [];
125129

scripts/apidoc/diff.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import type { DocsApiDiffIndex } from './utils';
2+
import { nameDocsDiffIndexFile, pathDocsDiffIndexFile } from './utils';
3+
4+
/**
5+
* Loads the diff index from the given source url.
6+
*
7+
* @param url The url to load the diff index from.
8+
*/
9+
async function loadRemote(url: string): Promise<DocsApiDiffIndex> {
10+
return fetch(url).then((res) => {
11+
if (!res.ok) {
12+
throw new Error(
13+
`Failed to load remote diff index from ${url}: ${res.statusText}`
14+
);
15+
} else {
16+
return res.json() as Promise<DocsApiDiffIndex>;
17+
}
18+
});
19+
}
20+
21+
/**
22+
* Loads the diff index from the given local path.
23+
*
24+
* @param path The path to load the diff index from. Should start with `file://` for cross platform compatibility.
25+
*/
26+
async function loadLocal(path: string): Promise<DocsApiDiffIndex> {
27+
return import(path).then((imp) => imp.default as DocsApiDiffIndex);
28+
}
29+
30+
/**
31+
* Loads the diff index from the given source.
32+
* If the source starts with `https://` it will be loaded from the remote url.
33+
* Otherwise it will be loaded from the local path.
34+
*
35+
* @param source The source to load the diff index from.
36+
*/
37+
async function load(source: string): Promise<DocsApiDiffIndex> {
38+
if (source.startsWith('https://')) {
39+
return loadRemote(source);
40+
} else {
41+
return loadLocal(source);
42+
}
43+
}
44+
45+
/**
46+
* Returns a set of all keys from the given entries.
47+
*
48+
* @param entries The entries to get the keys from.
49+
*/
50+
function allKeys(
51+
...entries: ReadonlyArray<Record<string, unknown>>
52+
): Set<string> {
53+
return new Set(entries.map(Object.keys).flat());
54+
}
55+
56+
/**
57+
* Compares the target (reference) and source (changed) diff index and returns the differences.
58+
* The returned object contains the module names as keys and the method names as values.
59+
* If the module name is `ADDED` or `REMOVED` it means that the module was added or removed in the local diff index.
60+
*
61+
* @param targetDiffIndex The url to the target (reference) diff index. Defaults to the next.fakerjs.dev diff index.
62+
* @param sourceDiffIndex The path to the source (changed) index. Defaults to the local diff index.
63+
*/
64+
export async function diff(
65+
targetDiffIndex = `https://next.fakerjs.dev/${nameDocsDiffIndexFile}`,
66+
sourceDiffIndex = `file://${pathDocsDiffIndexFile}`
67+
): Promise<Record<string, ['ADDED'] | ['REMOVED'] | string[]>> {
68+
const target = await load(targetDiffIndex);
69+
const source = await load(sourceDiffIndex);
70+
71+
const diff: Record<string, string[]> = {};
72+
73+
for (const moduleName of allKeys(target, source)) {
74+
const remoteModule = target[moduleName];
75+
const localModule = source[moduleName];
76+
77+
if (!remoteModule) {
78+
diff[moduleName] = ['ADDED'];
79+
continue;
80+
}
81+
82+
if (!localModule) {
83+
diff[moduleName] = ['REMOVED'];
84+
continue;
85+
}
86+
87+
for (const methodName of allKeys(remoteModule, localModule)) {
88+
const remoteMethod = remoteModule[methodName];
89+
const localMethod = localModule[methodName];
90+
91+
if (remoteMethod !== localMethod) {
92+
(diff[moduleName] ??= []).push(methodName);
93+
}
94+
}
95+
}
96+
97+
return diff;
98+
}

scripts/apidoc/generate.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { resolve } from 'path';
2-
import { writeApiPagesIndex, writeApiSearchIndex } from './apiDocsWriter';
2+
import {
3+
writeApiDiffIndex,
4+
writeApiPagesIndex,
5+
writeApiSearchIndex,
6+
} from './apiDocsWriter';
37
import { processModuleMethods } from './moduleMethods';
48
import { loadProject } from './typedoc';
59
import { pathOutputDir } from './utils';
@@ -16,7 +20,10 @@ export async function generate(): Promise<void> {
1620
await app.generateJson(project, pathOutputJson);
1721

1822
const modules = processModuleMethods(project);
19-
writeApiPagesIndex(modules);
23+
writeApiPagesIndex(modules.map(({ text, link }) => ({ text, link })));
24+
writeApiDiffIndex(
25+
modules.reduce((data, { text, diff }) => ({ ...data, [text]: diff }), {})
26+
);
2027

2128
writeApiSearchIndex(project);
2229
}

scripts/apidoc/moduleMethods.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ import {
88
selectApiMethodSignatures,
99
selectApiModules,
1010
} from './typedoc';
11-
import type { PageIndex } from './utils';
11+
import type { PageAndDiffIndex } from './utils';
12+
import { diffHash } from './utils';
1213

1314
/**
1415
* Analyzes and writes the documentation for modules and their methods such as `faker.animal.cat()`.
1516
*
1617
* @param project The project used to extract the modules.
1718
* @returns The generated pages.
1819
*/
19-
export function processModuleMethods(project: ProjectReflection): PageIndex {
20-
const pages: PageIndex = [];
20+
export function processModuleMethods(
21+
project: ProjectReflection
22+
): PageAndDiffIndex {
23+
const pages: PageAndDiffIndex = [];
2124

2225
// Generate module files
2326
for (const module of selectApiModules(project)) {
@@ -33,10 +36,11 @@ export function processModuleMethods(project: ProjectReflection): PageIndex {
3336
* @param module The module to process.
3437
* @returns The generated pages.
3538
*/
36-
function processModuleMethod(module: DeclarationReflection): PageIndex {
39+
function processModuleMethod(module: DeclarationReflection): PageAndDiffIndex {
3740
const moduleName = extractModuleName(module);
3841
const moduleFieldName = extractModuleFieldName(module);
3942
console.log(`Processing Module ${moduleName}`);
43+
const comment = toBlock(module.comment);
4044

4145
const methods: Method[] = [];
4246

@@ -45,22 +49,29 @@ function processModuleMethod(module: DeclarationReflection): PageIndex {
4549
selectApiMethodSignatures(module)
4650
)) {
4751
console.debug(`- ${methodName}`);
48-
4952
methods.push(analyzeSignature(signature, moduleFieldName, methodName));
5053
}
5154

52-
writeApiDocsModulePage(
53-
moduleName,
54-
moduleFieldName,
55-
toBlock(module.comment),
56-
methods
57-
);
55+
writeApiDocsModulePage(moduleName, moduleFieldName, comment, methods);
5856
writeApiDocsData(moduleFieldName, methods);
5957

6058
return [
6159
{
6260
text: moduleName,
6361
link: `/api/${moduleFieldName}.html`,
62+
diff: methods.reduce(
63+
(data, method) => ({
64+
...data,
65+
[method.name]: diffHash(method),
66+
}),
67+
{
68+
moduleHash: diffHash({
69+
name: moduleName,
70+
field: moduleFieldName,
71+
comment,
72+
}),
73+
}
74+
),
6475
},
6576
];
6677
}

scripts/apidoc/utils.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,44 @@
1+
import { createHash } from 'node:crypto';
12
import { resolve } from 'node:path';
23

34
// Types
45

56
export type Page = { text: string; link: string };
67
export type PageIndex = Page[];
78

9+
export type PageAndDiff = Page & {
10+
diff: DocsApiDiff;
11+
};
12+
export type PageAndDiffIndex = PageAndDiff[];
13+
14+
export interface DocsApiDiffIndex {
15+
/**
16+
* The methods in the module by name.
17+
*/
18+
[module: string]: DocsApiDiff;
19+
}
20+
21+
export interface DocsApiDiff {
22+
/**
23+
* The checksum of the entire module.
24+
*/
25+
moduleHash: string;
26+
/**
27+
* The checksum of the method by name.
28+
*/
29+
[method: string]: string;
30+
}
31+
832
// Paths
933

1034
const pathRoot = resolve(__dirname, '..', '..');
1135
export const pathDocsDir = resolve(pathRoot, 'docs');
36+
const pathPublicDir = resolve(pathDocsDir, 'public');
37+
export const nameDocsDiffIndexFile = 'api-diff-index.json';
38+
export const pathDocsDiffIndexFile = resolve(
39+
pathPublicDir,
40+
nameDocsDiffIndexFile
41+
);
1242
export const pathOutputDir = resolve(pathDocsDir, 'api');
1343

1444
// Functions
@@ -22,3 +52,12 @@ export function mapByName<T extends { name: string }, V>(
2252
{}
2353
);
2454
}
55+
56+
/**
57+
* Creates a diff hash for the given object.
58+
*
59+
* @param object The object to create a hash for.
60+
*/
61+
export function diffHash(object: unknown): string {
62+
return createHash('md5').update(JSON.stringify(object)).digest('hex');
63+
}

scripts/diff.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { existsSync } from 'node:fs';
2+
import { argv } from 'node:process';
3+
import { diff } from './apidoc/diff';
4+
import { pathDocsDiffIndexFile } from './apidoc/utils';
5+
6+
const [target, source] = argv.slice(2);
7+
8+
if (!source && !existsSync(pathDocsDiffIndexFile)) {
9+
throw new Error(
10+
`Unable to find local diff index file at: ${pathDocsDiffIndexFile}\n
11+
You can run \`pnpm run generate:api-docs\` to generate it.`
12+
);
13+
}
14+
15+
diff(target, source)
16+
.then((delta) => {
17+
if (Object.keys(delta).length === 0) {
18+
console.log('No documentation changes detected');
19+
return;
20+
}
21+
22+
console.log('Documentation changes detected:');
23+
for (const [module, methods] of Object.entries(delta)) {
24+
console.log(`- ${module}`);
25+
for (const method of methods) {
26+
console.log(` - ${method}`);
27+
}
28+
}
29+
})
30+
.catch(console.error);

0 commit comments

Comments
 (0)