Skip to content

Commit

Permalink
feat: group changelogs by tocs
Browse files Browse the repository at this point in the history
  • Loading branch information
v8tenko committed Apr 25, 2024
1 parent 1382c8a commit de4d9bc
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 35 deletions.
8 changes: 2 additions & 6 deletions src/resolvers/md2md.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {existsSync, readFileSync, writeFileSync} from 'fs';
import {readFileSync, writeFileSync} from 'fs';
import {basename, dirname, extname, join, resolve} from 'path';
import shell from 'shelljs';
import log from '@diplodoc/transform/lib/log';
Expand Down Expand Up @@ -57,11 +57,7 @@ export async function resolveMd2Md(options: ResolveMd2MdOptions): Promise<void>
)}`;
}

const changesPath = join(outputDir, `changes-${changesName}.json`);

if (existsSync(changesPath)) {
throw new Error(`Changelog ${changesPath} already exists!`);
}
const changesPath = join(outputDir, `__changes-${changesName}.json`);

writeFileSync(
changesPath,
Expand Down
10 changes: 10 additions & 0 deletions src/services/tocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import {IncludersError, applyIncluders} from './includers';

export interface TocServiceData {
storage: Map<string, YfmToc>;
tocs: Map<string, YfmToc>;
navigationPaths: string[];
includedTocPaths: Set<string>;
}

const storage: TocServiceData['storage'] = new Map();
const tocs: TocServiceData['tocs'] = new Map();
let navigationPaths: TocServiceData['navigationPaths'] = [];
const includedTocPaths: TocServiceData['includedTocPaths'] = new Set();
const tocFileCopyMap = new Map<string, string>();
Expand Down Expand Up @@ -76,6 +78,9 @@ async function add(path: string) {
/* Store parsed toc for .md output format */
storage.set(path, parsedToc);

/* save toc in distinct set, storage includes .md files too */
tocs.set(path, parsedToc);

/* Store path to toc file to handle relative paths in navigation */
parsedToc.base = pathToDir;

Expand Down Expand Up @@ -411,6 +416,10 @@ function getCopyFileMap() {
return tocFileCopyMap;
}

function getAllTocs() {
return [...tocs.keys()];
}

export default {
add,
getForPath,
Expand All @@ -419,4 +428,5 @@ export default {
getIncludedTocPaths,
setNavigationPaths,
getCopyFileMap,
getAllTocs,
};
124 changes: 95 additions & 29 deletions src/steps/processChangelogs.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,99 @@
import {glob} from '../utils/glob';
import {join} from 'node:path';
import {ArgvService} from '../services';
import {ArgvService, TocService} from '../services';
import {readFile, unlink, writeFile} from 'node:fs/promises';
import {Lang} from '../constants';

type Language = string;
type MergedChangelogs = {
[language: Language]: Record<string, Record<string, unknown>>;
[pathToProjectToc: string]: {
[language: string]: {
[pathToFile: string]: unknown;
};
};
};

type ParsedPath = {
language: string;
path: string;
level: number;
};

const hasSingleLanguage = () => {
const {langs} = ArgvService.getConfig();
return typeof langs === 'undefined' || langs.length === 1;
};

const project = (path: string): ParsedPath => {
const parts = path.split('/').slice(0, -1);
const language = hasSingleLanguage() ? Lang.EN : parts.shift()!;
const level = parts.length;

return {
path: '/' + parts.join('/'),
language,
level,
};
};

const file = (path: string): ParsedPath => {
const parts = path.split('/');
const language = hasSingleLanguage() ? Lang.EN : parts.shift()!;

return {
path: '/' + parts.join('/'),
language,
level: -1,
};
};

type Project = {
path: string;
languages: string[];
level: number;
};

/*
{
"ru": {
"/": {
"12314": <changelog>
},
"/plugins": {
"213312": <changelog>
}
}
}
This function collects all the project's subprojects and sorts them by depth level.
This is done to make it easier to find which toc.yaml file is responsible
for the necessary changes file: the earlier the project is in the list, the deeper it is.
The first project whose path to toc.yaml shares a common prefix with the path to changes
will be considered responsible for it.
*/
const uniqueProjects = (tocs: string[]): [string, Project][] => {
const projects = tocs.map(project);
const composed = projects.reduce(
(acc, curr) => {
if (acc[curr.path]) {
acc[curr.path].languages.push(curr.language);

return acc;
}

acc[curr.path] = {
languages: [curr.language],
path: curr.path,
level: curr.level,
};

return acc;
},
{} as Record<string, Project>,
);

const entries = Object.entries(composed).sort((a, b) => {
return b[1].level - a[1].level;
});

return entries;
};

export async function processChangelogs() {
const {output: outputFolderPath, langs} = ArgvService.getConfig();
const {output: outputFolderPath} = ArgvService.getConfig();
const tocs = TocService.getAllTocs();
const projects = uniqueProjects(tocs);

const result = await glob('**/**/changes-*', {
const result = await glob('**/**/__changes-*.json', {
cwd: outputFolderPath,
});

Expand All @@ -48,25 +116,23 @@ export async function processChangelogs() {
);

changes.forEach(([path, value]) => {
if (!langs) {
path = `${Lang.EN}/${path}`;
}

const [lang, ...rest] = path.split('/');
const [, hash] = rest.pop()!.split(/[-.]/);
const {language, path: pathToFile} = file(path);

const fullPath = '/' + rest.join('/');
const project = projects.find(([pathToProject, project]) => {
return pathToFile.startsWith(pathToProject) && project.languages.includes(language);
});

if (!merged[lang]) {
merged[lang] = {};
if (!project) {
return;
}

if (!merged[lang][fullPath]) {
merged[lang][fullPath] = {};
}
const [projectPath] = project;

merged[projectPath] ??= {};
merged[projectPath][language] ??= {};

Object.assign(merged[lang][fullPath], {
[hash]: value,
Object.assign(merged[projectPath][language], {
[path]: value,
});
});

Expand Down

0 comments on commit de4d9bc

Please sign in to comment.