diff --git a/src/transform/plugins/images/collect.ts b/src/transform/plugins/images/collect.ts index f67e4671..93b28084 100644 --- a/src/transform/plugins/images/collect.ts +++ b/src/transform/plugins/images/collect.ts @@ -5,17 +5,19 @@ import {isLocalUrl} from '../../utils'; import {resolveRelativePath} from '../../utilsFS'; import imsize from '../imsize'; import {MarkdownItPluginOpts} from '../typings'; +import {EnvApi} from '../../yfmlint'; type Options = MarkdownItPluginOpts & { destPath: string; copyFile: (path: string, dest: string) => void; singlePage: boolean; + envApi?: EnvApi; }; const collect = (input: string, options: Options) => { const md = new MarkdownIt().use(imsize); - const {root, path, destPath = '', copyFile, singlePage} = options; + const {root, path, destPath = '', copyFile, singlePage, envApi} = options; const tokens = md.parse(input, {}); let result = input; @@ -46,7 +48,14 @@ const collect = (input: string, options: Options) => { result = result.replace(src, newSrc); } - copyFile(targetPath, targetDestPath); + if (envApi) { + envApi.copyFileAsync( + relative(envApi.root, targetPath), + relative(envApi.distRoot, targetDestPath), + ); + } else { + copyFile(targetPath, targetDestPath); + } }); }); diff --git a/src/transform/plugins/images/index.ts b/src/transform/plugins/images/index.ts index b86b56c1..e0d23a04 100644 --- a/src/transform/plugins/images/index.ts +++ b/src/transform/plugins/images/index.ts @@ -1,5 +1,5 @@ import {readFileSync} from 'fs'; -import {join, sep} from 'path'; +import {join, relative, sep} from 'path'; import {bold} from 'chalk'; import {resolveRelativePath, isFileExists} from '../../utilsFS'; @@ -7,15 +7,17 @@ import {isLocalUrl, isExternalHref} from '../../utils'; import Token from 'markdown-it/lib/token'; import {MarkdownItPluginCb, MarkdownItPluginOpts} from '../typings'; import {StateCore} from '../../typings'; +import {EnvApi} from '../../yfmlint'; interface ImageOpts extends MarkdownItPluginOpts { assetsPublicPath: string; + envApi?: EnvApi; } function replaceImageSrc( token: Token, state: StateCore, - {assetsPublicPath = sep, root = '', path: optsPath, log}: ImageOpts, + {assetsPublicPath = sep, root = '', path: optsPath, log, envApi}: ImageOpts, ) { const src = token.attrGet('src') || ''; const currentPath = state.env.path || optsPath; @@ -26,7 +28,14 @@ function replaceImageSrc( const path = resolveRelativePath(currentPath, src); - if (isFileExists(path)) { + let pathExists: boolean; + if (envApi) { + pathExists = envApi.fileExists(relative(envApi.root, path)); + } else { + pathExists = isFileExists(path); + } + + if (pathExists) { state.md.assets?.push(path); } else { log.error(`Asset not found: ${bold(src)} in ${bold(currentPath)}`); @@ -40,18 +49,25 @@ function replaceImageSrc( interface SVGOpts extends MarkdownItPluginOpts { notFoundCb: (s: string) => void; + envApi?: EnvApi; } function convertSvg( token: Token, state: StateCore, - {path: optsPath, log, notFoundCb, root}: SVGOpts, + {path: optsPath, log, notFoundCb, root, envApi}: SVGOpts, ) { const currentPath = state.env.path || optsPath; const path = resolveRelativePath(currentPath, token.attrGet('src') || ''); try { - const content = readFileSync(path, 'utf8'); + let content: string; + if (envApi) { + content = envApi?.readFile(relative(root, path), 'utf8') as string; + } else { + content = readFileSync(path, 'utf8'); + } + const svgToken = new state.Token('image_svg', '', 0); svgToken.attrSet('content', content); diff --git a/src/transform/plugins/includes/collect.ts b/src/transform/plugins/includes/collect.ts index 72a62dce..12dc5896 100644 --- a/src/transform/plugins/includes/collect.ts +++ b/src/transform/plugins/includes/collect.ts @@ -3,6 +3,7 @@ import {bold} from 'chalk'; import {isFileExists, resolveRelativePath} from '../../utilsFS'; import {MarkdownItPluginOpts} from '../typings'; +import {EnvApi} from '../../yfmlint'; const includesPaths: string[] = []; @@ -10,10 +11,11 @@ type Opts = MarkdownItPluginOpts & { destPath: string; copyFile(path: string, dest: string, opts: Opts): void; singlePage: Boolean; + envApi?: EnvApi; }; const collect = (input: string, options: Opts) => { - const {root, path, destPath = '', log, copyFile, singlePage} = options; + const {root, path, destPath = '', log, copyFile, singlePage, envApi} = options; const INCLUDE_REGEXP = /{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}/g; let match, @@ -25,9 +27,19 @@ const collect = (input: string, options: Opts) => { let includePath = resolveRelativePath(path, relativePath); const hashIndex = relativePath.lastIndexOf('#'); - if (hashIndex > -1 && !isFileExists(includePath)) { - includePath = includePath.slice(0, includePath.lastIndexOf('#')); - relativePath = relativePath.slice(0, hashIndex); + + if (hashIndex > -1) { + let includePathExists: boolean; + if (envApi) { + includePathExists = envApi.fileExists(relative(envApi.root, includePath)); + } else { + includePathExists = isFileExists(includePath); + } + + if (!includePathExists) { + includePath = includePath.slice(0, includePath.lastIndexOf('#')); + relativePath = relativePath.slice(0, hashIndex); + } } const targetDestPath = resolveRelativePath(destPath, relativePath); diff --git a/src/transform/plugins/includes/index.ts b/src/transform/plugins/includes/index.ts index 534854be..61fdd778 100644 --- a/src/transform/plugins/includes/index.ts +++ b/src/transform/plugins/includes/index.ts @@ -5,6 +5,8 @@ import {findBlockTokens} from '../../utils'; import Token from 'markdown-it/lib/token'; import {MarkdownItPluginCb, MarkdownItPluginOpts} from '../typings'; import {StateCore} from 'src/transform/typings'; +import {EnvApi} from '../../yfmlint'; +import {relative} from 'path'; const INCLUDE_REGEXP = /^{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}$/; @@ -18,10 +20,11 @@ type Options = MarkdownItPluginOpts & GetFileTokensOpts & { notFoundCb: (v: string) => void; noReplaceInclude: boolean; + envApi?: EnvApi; }; function unfoldIncludes(state: StateCore, path: string, options: Options) { - const {root, notFoundCb, log, noReplaceInclude = false} = options; + const {root, notFoundCb, log, noReplaceInclude = false, envApi} = options; const {tokens} = state; let i = 0; @@ -44,9 +47,19 @@ function unfoldIncludes(state: StateCore, path: string, options: Options) { let pathname = fullIncludePath; let hash = ''; const hashIndex = fullIncludePath.lastIndexOf('#'); - if (hashIndex > -1 && !isFileExists(pathname)) { - pathname = fullIncludePath.slice(0, hashIndex); - hash = fullIncludePath.slice(hashIndex + 1); + + if (hashIndex > -1) { + let pathnameExists: boolean; + if (envApi) { + pathnameExists = envApi.fileExists(relative(envApi.root, pathname)); + } else { + pathnameExists = isFileExists(pathname); + } + + if (!pathnameExists) { + pathname = fullIncludePath.slice(0, hashIndex); + hash = fullIncludePath.slice(hashIndex + 1); + } } if (!pathname.startsWith(root)) { diff --git a/src/transform/plugins/links/index.ts b/src/transform/plugins/links/index.ts index ab4ea024..6c0cf838 100644 --- a/src/transform/plugins/links/index.ts +++ b/src/transform/plugins/links/index.ts @@ -8,6 +8,7 @@ import {Logger} from 'src/transform/log'; import {MarkdownItPluginCb, MarkdownItPluginOpts} from '../typings'; import path, {isAbsolute, parse, relative, resolve} from 'path'; import {StateCore} from 'src/transform/typings'; +import {EnvApi} from '../../yfmlint'; function defaultTransformLink(href: string) { const parsed = url.parse(href); @@ -49,6 +50,7 @@ type Options = { href: string; currentPath: string; log: Logger; + envApi?: EnvApi; }; const addTitle = (options: Options) => { @@ -86,11 +88,12 @@ interface ProcOpts extends MarkdownItPluginOpts { transformLink: (v: string) => string; notFoundCb: (v: string) => void; needSkipLinkFn: (v: string) => boolean; + envApi?: EnvApi; } // eslint-disable-next-line complexity function processLink(state: StateCore, tokens: Token[], idx: number, opts: ProcOpts) { - const {path: startPath, root, transformLink, notFoundCb, needSkipLinkFn, log} = opts; + const {path: startPath, root, transformLink, notFoundCb, needSkipLinkFn, log, envApi} = opts; const currentPath = state.env.path || startPath; const linkToken = tokens[idx]; const nextToken = tokens[idx + 1]; @@ -115,7 +118,13 @@ function processLink(state: StateCore, tokens: Token[], idx: number, opts: ProcO if (pathname) { file = resolve(path.parse(currentPath).dir, pathname); - fileExists = isFileExists(file); + + if (envApi) { + fileExists = envApi.fileExists(relative(envApi.root, file)); + } else { + fileExists = isFileExists(file); + } + isPageFile = PAGE_LINK_REGEXP.test(pathname); if (isPageFile && !fileExists) { diff --git a/src/transform/utilsFS.ts b/src/transform/utilsFS.ts index dcb84db8..70738d5f 100644 --- a/src/transform/utilsFS.ts +++ b/src/transform/utilsFS.ts @@ -1,10 +1,11 @@ import _, {Dictionary} from 'lodash'; import {readFileSync, statSync} from 'fs'; -import {parse, resolve, join, sep} from 'path'; +import {parse, resolve, join, sep, relative} from 'path'; import liquid from './liquid'; import {StateCore} from './typings'; +import {EnvApi} from './yfmlint'; const filesCache: Record = {}; @@ -34,6 +35,7 @@ export type GetFileTokensOpts = { disableCircularError?: boolean; inheritVars?: boolean; conditionsInCode?: boolean; + envApi?: EnvApi; }; export function getFileTokens(path: string, state: StateCore, options: GetFileTokensOpts) { @@ -47,12 +49,20 @@ export function getFileTokens(path: string, state: StateCore, options: GetFileTo disableCircularError, inheritVars = true, conditionsInCode, + envApi, } = options; let content; - const builtVars = (getVarsPerFile && !inheritVars ? getVarsPerFile(path) : vars) || {}; + let builtVars; + if (envApi) { + builtVars = (inheritVars ? vars : envApi.getFileVars(relative(envApi.root, path))) || {}; + } else { + builtVars = (getVarsPerFile && !inheritVars ? getVarsPerFile(path) : vars) || {}; + } - if (filesCache[path]) { + if (envApi) { + content = envApi.readFile(relative(envApi.root, path), 'utf-8') as string; + } else if (filesCache[path]) { content = filesCache[path]; } else { content = readFileSync(path, 'utf8'); diff --git a/src/transform/yfmlint/index.ts b/src/transform/yfmlint/index.ts index 012966be..97a00838 100644 --- a/src/transform/yfmlint/index.ts +++ b/src/transform/yfmlint/index.ts @@ -106,12 +106,25 @@ namespace yfmlint { sourceMap?: Dictionary; } + export interface EnvApi { + root: string; + distRoot: string; + copyFile: (from: string, to: string) => void; + copyFileAsync: (from: string, to: string) => void; + writeFile: (to: string, data: string | Uint8Array) => void; + writeFileAsync: (to: string, data: string | Uint8Array) => void; + readFile: (path: string, encoding: BufferEncoding) => string | Uint8Array; + fileExists: (path: string) => boolean; + getFileVars: (path: string) => Record; + } + export interface PluginOptions { log: Logger; path?: string; disableLint?: boolean; lintMarkdown?: (opts: LintMarkdownFunctionOptions) => void; [key: string]: unknown; + envApi?: EnvApi; } export interface LintConfig {