Skip to content

Commit

Permalink
fix: make collect async
Browse files Browse the repository at this point in the history
  • Loading branch information
makamekm committed Oct 22, 2024
1 parent b3cf724 commit 9694660
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 49 deletions.
17 changes: 16 additions & 1 deletion src/transform/fsContext.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {readFileSync, writeFileSync} from 'fs';
import {readFile, writeFile} from 'fs/promises';

import {FsContext} from './typings';
import {isFileExists} from './utilsFS';
import {isFileExists, isFileExistsAsync} from './utilsFS';

export class DefaultFsContext implements FsContext {
exist(path: string): boolean {
Expand All @@ -17,6 +18,20 @@ export class DefaultFsContext implements FsContext {
encoding: 'utf8',
});
}

async existAsync(path: string): Promise<boolean> {
return await isFileExistsAsync(path);
}

async readAsync(path: string): Promise<string> {
return readFile(path, 'utf8');
}

async writeAsync(path: string, content: string): Promise<void> {
writeFile(path, content, {
encoding: 'utf8',
});
}
}

export const defaultFsContext = new DefaultFsContext();
12 changes: 6 additions & 6 deletions src/transform/plugins/images/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ type Options = MarkdownItPluginOpts & {
singlePage: boolean;
};

const collect = (input: string, options: Options) => {
const collect = async (input: string, options: Options) => {
const md = new MarkdownIt().use(imsize);

const {root, path, destPath = '', copyFile, singlePage, deps} = options;
const tokens = md.parse(input, {});
let result = input;

tokens.forEach((token) => {
for (const token of tokens) {
if (token.type !== 'inline') {
return;
}

const children = token.children || [];

children.forEach((childToken) => {
for (const childToken of children) {
if (childToken.type !== 'image') {
return;
}
Expand All @@ -47,9 +47,9 @@ const collect = (input: string, options: Options) => {
result = result.replace(src, newSrc);
}

copyFile(targetPath, targetDestPath);
});
});
await copyFile(targetPath, targetDestPath);
}
}

if (singlePage) {
return result;
Expand Down
74 changes: 37 additions & 37 deletions src/transform/plugins/includes/collect.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import {relative} from 'path';
import {bold} from 'chalk';
import {readFileSync} from 'fs';

import {getRelativePath, resolveRelativePath} from '../../utilsFS';
import {defaultFsContext} from '../../fsContext';

import {IncludeCollectOpts} from './types';

const includesPaths: string[] = [];

function processRecursive(
async function processRecursive(
includePath: string,
targetDestPath: string,
options: IncludeCollectOpts,
appendix: Map<string, string>,
) {
const {path, log, copyFile, includedParentPath: includedParentPathNullable, included} = options;
const {
path,
log,
copyFile,
includedParentPath: includedParentPathNullable,
included,
fs,
} = options;
const includedParentPath = includedParentPathNullable || path;

const includeOptions = {
Expand All @@ -25,30 +28,26 @@ function processRecursive(
};

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

// To reduce file reading we can include the file content into the generated content
if (included) {
const content = contentProcessed ?? readFileSync(targetDestPath, 'utf8');
const content = contentProcessed ?? (await fs?.readAsync(targetDestPath));

if (content) {
const includedRelativePath = getRelativePath(includedParentPath, includePath);

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

// Add to appendix set structure
appendix.set(
options.appendix?.set(
includedRelativePath,
`{% included (${includedRelativePath}) %}\n${includeContent}\n{% endincluded %}`,
);
Expand All @@ -60,11 +59,7 @@ function processRecursive(
}
}

function collectRecursive(
result: string,
options: IncludeCollectOpts,
appendix: Map<string, string>,
) {
async function collectRecursive(result: string, options: IncludeCollectOpts) {
const {root, path, destPath = '', log, singlePage, fs = defaultFsContext, deps} = options;

const INCLUDE_REGEXP = /{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}/g;
Expand All @@ -80,19 +75,21 @@ function collectRecursive(

deps?.markDep?.(path, includePath, 'include');

if (hashIndex > -1 && !fs.exist(includePath)) {
if (hashIndex > -1 && !(await fs.existAsync(includePath))) {
includePath = includePath.slice(0, includePath.lastIndexOf('#'));
relativePath = relativePath.slice(0, hashIndex);
}

const targetDestPath = resolveRelativePath(destPath, relativePath);

if (includesPaths.includes(includePath)) {
log.error(`Circular includes: ${bold(includesPaths.concat(path).join(' ▶ '))}`);
if (options.includesPaths?.includes(includePath)) {
log.error(
`Circular includes: ${bold(options.includesPaths?.concat(path).join(' ▶ '))}`,
);
break;
}

if (singlePage && !includesPaths.length) {
if (singlePage && !options.includesPaths?.length) {
const newRelativePath = relative(root, includePath);
const newInclude = matchedInclude.replace(relativePath, newRelativePath);

Expand All @@ -102,25 +99,28 @@ function collectRecursive(
INCLUDE_REGEXP.lastIndex = INCLUDE_REGEXP.lastIndex - delta;
}

includesPaths.push(includePath);
options.includesPaths?.push(includePath);

processRecursive(includePath, targetDestPath, options, appendix);
await processRecursive(includePath, targetDestPath, options);

includesPaths.pop();
options.includesPaths?.pop();
}

return result;
}

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

options.includesPaths = options.includesPaths ?? [];
options.appendix = options.appendix ?? new Map();

input = collectRecursive(input, options, appendix);
input = await collectRecursive(input, options);

if (!options.path.includes('_includes')) {
if (shouldWriteAppendix) {
// 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) {
input += '\n' + [...appendix.values()].join('\n');
if (options.appendix.size > 0) {
input += '\n' + [...options.appendix.values()].join('\n');
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/transform/plugins/includes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ export interface MarkdownItIncluded extends MarkdownIt {

export type IncludeCollectOpts = MarkdownItPluginOpts & {
destPath: string;
copyFile(path: string, dest: string, opts: IncludeCollectOpts): string | null | undefined;
copyFile(
path: string,
dest: string,
opts: IncludeCollectOpts,
): Promise<string | null | undefined>;
singlePage: Boolean;
included: Boolean;
includedParentPath?: string;
additionalIncludedList?: string[];
includesPaths?: string[];
appendix?: Map<string, string>;
};
3 changes: 3 additions & 0 deletions src/transform/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface FsContext {
read(path: string | null): string;
exist(path: string | null): boolean;
write(path: string | null, content: string): void;
readAsync(path: string | null): Promise<string>;
existAsync(path: string | null): Promise<boolean>;
writeAsync(path: string | null, content: string): Promise<void>;
}

export interface DependencyContext {
Expand Down
11 changes: 11 additions & 0 deletions src/transform/utilsFS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {Dictionary} from 'lodash';
import escapeRegExp from 'lodash/escapeRegExp';
import {join, parse, relative, resolve, sep} from 'path';
import {statSync} from 'fs';
import {stat} from 'fs/promises';

import liquidSnippet from './liquid';
import {FsContext, StateCore} from './typings';
Expand Down Expand Up @@ -37,6 +38,16 @@ export function isFileExists(file: string) {
}
}

export async function isFileExistsAsync(file: string) {
try {
const stats = await stat(file);

return stats.isFile();
} catch (e) {
return false;
}
}

export function getFileTokens(
fs: FsContext,
path: string,
Expand Down
7 changes: 3 additions & 4 deletions test/include-included.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {resolve} from 'path';
import {readFileSync} from 'fs';
import {readFile} from 'node:fs/promises';

import transform from '../src/transform';
Expand All @@ -26,7 +25,7 @@ const collectIncluded = (text: string, path: string) => {
included: true,
path: path,
root: resolve(path, '../'),
copyFile: (includePath) => readFileSync(includePath, 'utf-8'),
copyFile: (includePath) => readFile(includePath, 'utf-8'),
singlePage: false,
destPath: '',
isLintRun: false,
Expand All @@ -46,7 +45,7 @@ describe('Included to md', () => {
const expectPath = resolve(__dirname, './mocks/include-included-3.expect.md');
const expectContent = await readFile(expectPath, 'utf8');

const result = collectIncluded(input, inputPath);
const result = await collectIncluded(input, inputPath);

expect(result).toBe(expectContent);
});
Expand All @@ -70,7 +69,7 @@ describe('Included to md', () => {
const expectPath = resolve(__dirname, './mocks/include-included-3-deep.expect.md');
const expectContent = await readFile(expectPath, 'utf8');

const result = collectIncluded(input, inputPath);
const result = await collectIncluded(input, inputPath);

expect(result).toBe(expectContent);
});
Expand Down

0 comments on commit 9694660

Please sign in to comment.