Skip to content

Commit

Permalink
feat: add merge included optimisation (#504)
Browse files Browse the repository at this point in the history
  • Loading branch information
makamekm authored Sep 11, 2024
1 parent 62e861b commit b30899e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 74 deletions.
134 changes: 70 additions & 64 deletions src/transform/plugins/includes/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,68 @@ import {relative} from 'path';
import {bold} from 'chalk';

import {getRelativePath, isFileExists, resolveRelativePath} from '../../utilsFS';
import {MarkdownItPluginOpts} from '../typings';

import {IncludeCollectOpts} from './types';

const includesPaths: string[] = [];

type Opts = MarkdownItPluginOpts & {
destPath: string;
copyFile(path: string, dest: string, opts: Opts): string | null | undefined;
singlePage: Boolean;
included: Boolean;
includedParentPath?: string;
};

const collect = (input: string, options: Opts) => {
const {
root,
path,
destPath = '',
log,
copyFile,
singlePage,
includedParentPath: includedParentPathNullable,
included,
} = options;
function processRecursive(
includePath: string,
targetDestPath: string,
options: IncludeCollectOpts,
appendix: Map<string, string>,
) {
const {path, log, copyFile, includedParentPath: includedParentPathNullable, included} = options;
const includedParentPath = includedParentPathNullable || path;

const INCLUDE_REGEXP = /{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}/g;
const includeOptions = {
...options,
path: includePath,
destPath: targetDestPath,
};

try {
const content = copyFile(includePath, targetDestPath, includeOptions);

// To reduce file reading we can include the file content into the generated content
if (included && content) {
const includedRelativePath = getRelativePath(includedParentPath, includePath);

// The appendix is the map that protects from multiple include files
if (!appendix.has(includedRelativePath)) {
// Recursive function to include the depth structure
const includeContent = collectRecursive(
content,
{
...options,
path: includePath,
includedParentPath,
},
appendix,
);

// Add to appendix set structure
appendix.set(
includedRelativePath,
`{% included (${includedRelativePath}) %}\n${includeContent}\n{% endincluded %}`,
);
}
}
} catch (e) {
log.error(`No such file or has no access to ${bold(includePath)} in ${bold(path)}`);
}
}

let match,
result = input;
function collectRecursive(
result: string,
options: IncludeCollectOpts,
appendix: Map<string, string>,
) {
const {root, path, destPath = '', log, singlePage} = options;

const appendix: Map<string, string> = new Map();
const INCLUDE_REGEXP = /{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}/g;

let match: RegExpExecArray | null;

while ((match = INCLUDE_REGEXP.exec(result)) !== null) {
let [, , , relativePath] = match;
Expand Down Expand Up @@ -63,51 +94,26 @@ const collect = (input: string, options: Opts) => {
}

includesPaths.push(includePath);
const includeOptions = {
...options,
path: includePath,
destPath: targetDestPath,
};

try {
const content = copyFile(includePath, targetDestPath, includeOptions);

// To reduce file reading we can include the file content into the generated content
if (included && content) {
const includedRelativePath = getRelativePath(includedParentPath, includePath);

// The appendix is the map that protects from multiple include files
if (!appendix.has(includedRelativePath)) {
// Recursive function to include the depth structure
const includeContent = collect(content, {
...options,
path: includePath,
includedParentPath,
});
// Add to appendix set structure
appendix.set(
includedRelativePath,
`{% included (${includedRelativePath}) %}\n${includeContent}\n{% endincluded %}`,
);
}
}
} catch (e) {
log.error(`No such file or has no access to ${bold(includePath)} in ${bold(path)}`);
} finally {
includesPaths.pop();
}

processRecursive(includePath, targetDestPath, options, appendix);

includesPaths.pop();
}

return result;
}

function collect(input: string, options: IncludeCollectOpts) {
const appendix: Map<string, string> = new Map();

input = collectRecursive(input, options, appendix);

// Appendix should be appended to the end of the file (it supports depth structure, so the included files will have included as well)
if (appendix.size > 0) {
result += '\n' + [...appendix.values()].join('\n');
input += '\n' + [...appendix.values()].join('\n');
}

if (singlePage) {
return result;
}

return result;
};
return input;
}

export = collect;
10 changes: 10 additions & 0 deletions src/transform/plugins/includes/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import {MarkdownIt} from '../../typings';
import {MarkdownItPluginOpts} from '../typings';

export interface MarkdownItIncluded extends MarkdownIt {
included?: {
[key: string]: string;
};
}

export type IncludeCollectOpts = MarkdownItPluginOpts & {
destPath: string;
copyFile(path: string, dest: string, opts: IncludeCollectOpts): string | null | undefined;
singlePage: Boolean;
included: Boolean;
includedParentPath?: string;
additionalIncludedList?: string[];
};
20 changes: 10 additions & 10 deletions test/mocks/include-included-3-deep.expect.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ start main
{% include [Text](included/file-1-deep.md) %}

end main
{% included (included/file-1-deep.md) %}
start file 1

{% include [Text](file-2-deep.md) %}
{% included (included/file-3.md) %}
start file 3

end file 1
end file 3
{% endincluded %}
{% included (included/file-2-deep.md) %}
start file 2

{% include [Text](file-3.md) %}

end file 2
{% included (included/file-3.md) %}
start file 3

end file 3
{% endincluded %}
{% endincluded %}
{% included (included/file-1-deep.md) %}
start file 1

{% include [Text](file-2-deep.md) %}

end file 1
{% endincluded %}

0 comments on commit b30899e

Please sign in to comment.