Skip to content

Commit

Permalink
fix: add support for sub toc files
Browse files Browse the repository at this point in the history
  • Loading branch information
makamekm committed May 17, 2024
1 parent b3de09b commit bfc23e3
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 52 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@diplodoc/mermaid-extension": "^1.2.1",
"@diplodoc/openapi-extension": "^2.1.0",
"@diplodoc/prettier-config": "^2.0.0",
"@diplodoc/transform": "^4.15.0",
"@diplodoc/transform": "^4.16.1",
"@diplodoc/tsconfig": "^1.0.2",
"@octokit/core": "4.2.4",
"@types/async": "^3.2.15",
Expand Down
2 changes: 2 additions & 0 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,14 @@ export interface YfmToc extends Filter {
items: YfmToc[];
stage?: Stage;
base?: string;
deepBase?: number;
title?: TextItems;
include?: YfmTocInclude;
id?: string;
singlePage?: boolean;
hidden?: boolean;
label?: YfmTocLabel[] | YfmTocLabel;
root?: YfmToc;
}

export interface YfmTocInclude {
Expand Down
50 changes: 44 additions & 6 deletions src/resolvers/md2html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import transform, {Output} from '@diplodoc/transform';
import liquid from '@diplodoc/transform/lib/liquid';
import log from '@diplodoc/transform/lib/log';
import {MarkdownItPluginCb} from '@diplodoc/transform/lib/plugins/typings';
import {getPublicPath} from '@diplodoc/transform/lib/utilsFS';
import {getPublicPath, isFileExists} from '@diplodoc/transform/lib/utilsFS';
import yaml from 'js-yaml';

import {Lang, PROCESSING_FINISHED} from '../constants';
Expand Down Expand Up @@ -79,7 +79,7 @@ const getFileProps = async (options: ResolverOptions) => {

const pathToDir: string = dirname(inputPath);
const toc: YfmToc | null = TocService.getForPath(inputPath) || null;
const tocBase: string = toc?.base ?? '';
const tocBase: string = toc?.root?.base || toc?.base || '';
const pathToFileDir: string =
pathToDir === tocBase ? '' : pathToDir.replace(`${tocBase}${sep}`, '');

Expand All @@ -92,14 +92,16 @@ const getFileProps = async (options: ResolverOptions) => {
const lang = tocLang || configLang || configLangs?.[0] || Lang.RU;
const langs = configLangs?.length ? configLangs : [lang];

const pathname = join(pathToFileDir, basename(outputPath));

const props = {
data: {
leading: inputPath.endsWith('.yaml'),
toc: transformToc(toc) || {},
...meta,
},
router: {
pathname: join(pathToFileDir, basename(outputPath)),
pathname,
},
lang,
langs,
Expand All @@ -119,6 +121,35 @@ export async function resolveMd2HTML(options: ResolverOptions): Promise<DocInner
return props;
}

function getHref(path: string, href: string) {
if (!href.includes('//')) {
const {input} = ArgvService.getConfig();
const root = resolve(input);
const assetRootPath = getAssetsRootPath(path) || '';

let filePath = resolve(input, dirname(path), href);

if (href.startsWith('/')) {
filePath = resolve(input, assetRootPath, href.replace(/^\/+/gi, ''));
}

href = getPublicPath(
{
root,
rootPublicPath: assetRootPath,
},
filePath,
);

if (isFileExists(filePath) || isFileExists(filePath + '.md')) {
href = href.replace(/\.md$/gi, '.html');
} else if (!/.+\.\w+$/gi.test(href)) {
href = href + '/';
}
}
return href;
}

function YamlFileTransformer(content: string, transformOptions: FileTransformOptions): Object {
let data: LeadingPage | null = null;

Expand Down Expand Up @@ -149,9 +180,16 @@ function YamlFileTransformer(content: string, transformOptions: FileTransformOpt
return result?.html;
});
} else {
const links = data?.links?.map((link) =>
link.href ? {...link, href: link.href.replace(/.md$/gmu, '.html')} : link,
);
const links = data?.links?.map((link) => {
if (link.href) {
const href = getHref(transformOptions.path, link.href);
return {
...link,
href,
};
}
return link;
});

if (links) {
data.links = links;
Expand Down
6 changes: 2 additions & 4 deletions src/services/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import {isObject} from './utils';
import {сarriage} from '../utils';
import {REGEXP_AUTHOR, metadataBorder} from '../constants';
import {sep} from 'path';
import {TocService} from './index';

async function getContentWithUpdatedMetadata(
Expand Down Expand Up @@ -329,8 +328,7 @@ function getSystemVarsMetadataString(systemVars: object) {
function getAssetsPublicPath(filePath: string) {
const toc: YfmToc | null = TocService.getForPath(filePath) || null;

const basePath = toc?.base?.split(sep)?.filter((str) => str !== '.') || [];
const deepBase = basePath.length;
const deepBase = toc?.root?.deepBase || toc?.deepBase || 0;
const deepBasePath = deepBase > 0 ? Array(deepBase).fill('../').join('') : './';

/* Relative path from folder of .md file to root of user' output folder */
Expand All @@ -340,7 +338,7 @@ function getAssetsPublicPath(filePath: string) {
function getAssetsRootPath(filePath: string) {
const toc: YfmToc | null = TocService.getForPath(filePath) || null;

return toc?.base;
return toc?.root?.base || toc?.base;
}

export {
Expand Down
69 changes: 69 additions & 0 deletions src/services/tocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ let navigationPaths: TocServiceData['navigationPaths'] = [];
const includedTocPaths: TocServiceData['includedTocPaths'] = new Set();
const tocFileCopyMap = new Map<string, string>();

async function init(tocFilePaths: string[]) {
for (const path of tocFilePaths) {
logger.proc(path);

await add(path);
}

for (const path of tocFilePaths) {
logger.proc(path);

await addRoot(path);
}

for (const path of tocFilePaths) {
logger.proc(path);

await addNavigation(path);
}
}

async function add(path: string) {
const {
input: inputFolderPath,
Expand Down Expand Up @@ -83,6 +103,7 @@ async function add(path: string) {

/* Store path to toc file to handle relative paths in navigation */
parsedToc.base = pathToDir;
parsedToc.deepBase = pathToDir?.split(sep)?.filter((str) => str !== '.')?.length || 0;

if (outputFormat === 'md') {
/* Should copy resolved and filtered toc to output folder */
Expand All @@ -91,7 +112,37 @@ async function add(path: string) {
shell.mkdir('-p', dirname(outputPath));
writeFileSync(outputPath, outputToc);
}
}

// To collect root toc.yaml we need to move from root into deep
async function addRoot(path: string) {
const dirPath = dirname(path).split(sep);

const currentToc = getForPath(path);

if (!currentToc) {
return;
}

for (let index = 1; index < dirPath.length; index++) {
const dir = dirPath.slice(0, index);
const rootToc = getForPath(join(dir.join(sep), 'toc.yaml'));
if (rootToc) {
currentToc.root = rootToc;
return;
}
}
}

// To collect root toc.yaml we need to move from root into deep
async function addNavigation(path: string) {
const parsedToc = getForPath(path);

if (!parsedToc) {
return;
}

const pathToDir = dirname(path);
prepareNavigationPaths(parsedToc, pathToDir);
}

Expand Down Expand Up @@ -126,6 +177,21 @@ function getForPath(path: string): YfmToc | undefined {
return storage.get(path);
}

function getDeepForPath(path: string): {
deepBase: number;
deep: number;
} {
const toc: YfmToc | null = getForPath(path) || null;

const deepBase = toc?.root?.deepBase || toc?.deepBase || 0;
const deep = path.split(sep).length - 1 - deepBase;

return {
deepBase,
deep,
};
}

function getNavigationPaths(): string[] {
return [...navigationPaths];
}
Expand Down Expand Up @@ -421,8 +487,11 @@ function getAllTocs() {
}

export default {
init,
add,
process,
getForPath,
getDeepForPath,
getNavigationPaths,
getTocDir,
getIncludedTocPaths,
Expand Down
16 changes: 6 additions & 10 deletions src/steps/processPages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {DocInnerProps} from '@diplodoc/client';
import {basename, dirname, extname, join, relative, resolve, sep} from 'path';
import {basename, dirname, extname, join, relative, resolve} from 'path';
import {existsSync, readFileSync, writeFileSync} from 'fs';
import log from '@diplodoc/transform/lib/log';
import {asyncify, mapLimit} from 'async';
Expand Down Expand Up @@ -171,13 +171,13 @@ async function saveSinglePages() {
langs,
};

const basePath = toc?.base?.split(sep)?.filter((str) => str !== '.') || [];
const deepBase = basePath.length;

// Save the full single page for viewing locally
const singlePageFn = join(tocDir, SINGLE_PAGE_FILENAME);
const singlePageDataFn = join(tocDir, SINGLE_PAGE_DATA_FILENAME);
const singlePageContent = generateStaticMarkup(pageData, deepBase);
const singlePageContent = generateStaticMarkup(
pageData,
toc?.root?.deepBase || toc?.deepBase || 0,
);

writeFileSync(singlePageFn, singlePageContent);
writeFileSync(singlePageDataFn, JSON.stringify(pageData));
Expand Down Expand Up @@ -358,11 +358,7 @@ async function processingFileToHtml(
metaDataOptions: MetaDataOptions,
): Promise<DocInnerProps> {
const {outputBundlePath, filename, fileExtension, outputPath, pathToFile} = path;
const toc: YfmToc | null = TocService.getForPath(pathToFile) || null;

const basePath = toc?.base?.split(sep)?.filter((str) => str !== '.') || [];
const deepBase = basePath.length;
const deep = pathToFile.split(sep).length - 1 - deepBase;
const {deepBase, deep} = TocService.getDeepForPath(pathToFile);

return resolveMd2HTML({
inputPath: pathToFile,
Expand Down
7 changes: 1 addition & 6 deletions src/steps/processServiceFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,7 @@ async function preparingTocFiles(
): Promise<void> {
try {
const tocFilePaths = getFilePathsByGlobals(['**/toc.yaml']);

for (const path of tocFilePaths) {
logger.proc(path);

await TocService.add(path);
}
await TocService.init(tocFilePaths);
} catch (error) {
log.error(`Preparing toc.yaml files failed. Error: ${error}`);
throw error;
Expand Down
13 changes: 9 additions & 4 deletions src/utils/toc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {basename, dirname, extname, format, join} from 'path';
import {basename, dirname, extname, format, join, relative} from 'path';

import {YfmToc} from '../models';
import {filterFiles} from '../services/utils';
Expand Down Expand Up @@ -39,14 +39,19 @@ export function transformToc(toc: YfmToc | null): YfmToc | null {
}

if (href && !isExternalHref(href)) {
const fileExtension: string = extname(href);
const filename: string = basename(href, fileExtension);
const relativeHref = join(
relative(toc.root?.base || toc.base || '', toc.base || ''),
href,
);

const fileExtension: string = extname(relativeHref);
const filename: string = basename(relativeHref, fileExtension);
const transformedFilename: string = format({
name: filename,
ext: '.html',
});

navigationItem.href = join(dirname(href), transformedFilename);
navigationItem.href = join(dirname(relativeHref), transformedFilename);
}
}

Expand Down
5 changes: 5 additions & 0 deletions tests/e2e/__snapshots__/include-toc.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ items:
- name: Article1
href: article1.md
base: product1
deepBase: 1
"
`;

Expand Down Expand Up @@ -107,6 +108,7 @@ items:
- name: Article1
href: overlay3/article1.md
base: product2
deepBase: 1
"
`;

Expand Down Expand Up @@ -140,6 +142,7 @@ exports[`Include toc Toc is included in link mode 5`] = `
- name: A1
href: folder1/folder2/a1.md
base: .
deepBase: 0
"
`;

Expand Down Expand Up @@ -254,6 +257,7 @@ exports[`Include toc Toc is included inline, not as a new section 12`] = `
- name: NameX
href: fileX.md
base: .
deepBase: 0
"
`;

Expand Down Expand Up @@ -284,5 +288,6 @@ items:
- name: A1
href: a1.md
base: .
deepBase: 0
"
`;
Loading

0 comments on commit bfc23e3

Please sign in to comment.