Skip to content

Commit

Permalink
feat: Use different escaping strategy for Liquid-style substitutions …
Browse files Browse the repository at this point in the history
…in YAML format front matter (#804)

* Revert "fix: Revert "feat: implement an option to always provide `vcsPath` in metadata for md->md transformations (sans tests)""

This reverts commit a3455a5.

* Revert "Revert "feat: resolve `vcsPath` correctly when `sourcePath` was supplied beforehand, refactor metadata generation procedures""

This reverts commit 1c251e5.

* fix: use different escaping strategy for Liquid-style substitutions in YAML format front matter

* feat: add tests for metadata transformations with substitutions
  • Loading branch information
brotheroftux committed Jul 12, 2024
1 parent 8352edd commit 1115459
Show file tree
Hide file tree
Showing 29 changed files with 983 additions and 795 deletions.
39 changes: 29 additions & 10 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@ export type UserByLoginFunction = (login: string) => Promise<Contributor | null>
export type CollectionOfPluginsFunction = (output: string, options: PluginOptions) => string;
export type GetModifiedTimeByPathFunction = (filepath: string) => number | undefined;

/**
* VCS integration configuration object.
* Future VCS futures should be configured with this one, not with
* `VCSConnectorConfig`.
*/
interface VCSConfiguration {
/**
* Externally accessible base URI for a resource where a particular documentation
* source is hosted.
*
* This configuration parameter is used to directly control the Edit button behaviour
* in the Diplodoc documentation viewer(s).
*
* For example, if the following applies:
* - Repo with doc source is hosted on GitHub (say, https://github.com/foo-org/bar),
* - Within that particular repo, the directory that is being passed as an `--input`
* parameter to the CLI is located at `docs/`,
* - Whenever the Edit button is pressed, you wish to direct your readers to the
* respective document's source on `main` branch
*
* you should pass `https://github.com/foo-org/bar/tree/main/docs` as a value for this parameter.
*/
remoteBase: string;
}

interface YfmConfig {
varsPreset: VarsPreset;
ignore: string[];
Expand All @@ -41,6 +66,7 @@ interface YfmConfig {
ignoreStage: string;
singlePage: boolean;
removeHiddenTocItems: boolean;
vcs?: VCSConfiguration;
connector?: VCSConnectorConfig;
lang?: Lang;
langs?: Lang[];
Expand Down Expand Up @@ -199,20 +225,13 @@ export interface Contributors {
[email: string]: Contributor;
}

export interface FileData {
tmpInputFilePath: string;
inputFolderPathLength: number;
fileContent: string;
sourcePath?: string;
}

export interface MetaDataOptions {
fileData: FileData;
pathData: PathData;
isContributorsEnabled?: boolean;
vcsConnector?: VCSConnector;
addSystemMeta?: boolean;
addSourcePath?: boolean;
resources?: Resources;
shouldAlwaysAddVCSPath?: boolean;
}

export interface PluginOptions {
Expand All @@ -236,7 +255,7 @@ export interface Plugin {
export interface ResolveMd2MdOptions {
inputPath: string;
outputPath: string;
metadata?: MetaDataOptions;
metadata: MetaDataOptions;
}

export interface ResolverOptions {
Expand Down
21 changes: 12 additions & 9 deletions src/resolvers/md2md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,32 @@ import {ArgvService, PluginService} from '../services';
import {getVarsPerFile, logger} from '../utils';
import {PluginOptions, ResolveMd2MdOptions} from '../models';
import {PROCESSING_FINISHED} from '../constants';
import {getContentWithUpdatedMetadata} from '../services/metadata';
import {ChangelogItem} from '@diplodoc/transform/lib/plugins/changelog/types';
import {enrichWithFrontMatter} from '../services/metadata';

export async function resolveMd2Md(options: ResolveMd2MdOptions): Promise<void> {
const {inputPath, outputPath, metadata} = options;
const {inputPath, outputPath, metadata: metadataOptions} = options;
const {input, output, changelogs: changelogsSetting} = ArgvService.getConfig();
const resolvedInputPath = resolve(input, inputPath);

const vars = getVarsPerFile(inputPath);

const content = await getContentWithUpdatedMetadata(
readFileSync(resolvedInputPath, 'utf8'),
metadata,
vars.__system,
vars.__metadata,
);
const content = await enrichWithFrontMatter({
fileContent: readFileSync(resolvedInputPath, 'utf8'),
metadataOptions,
resolvedFrontMatterVars: {
systemVars: vars.__system as unknown,
metadataVars: vars.__metadata,
},
});

const {result, changelogs} = transformMd2Md(content, {
path: resolvedInputPath,
destPath: outputPath,
root: resolve(input),
destRoot: resolve(output),
collectOfPlugins: PluginService.getCollectOfPlugins(),
vars,
vars: vars,
log,
copyFile,
});
Expand Down
33 changes: 15 additions & 18 deletions src/services/authors.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {replaceDoubleToSingleQuotes} from '../utils';
import {Contributor} from '../models';
import {VCSConnector} from '../vcs-connector/connector-models';

async function updateAuthorMetadataStringByAuthorLogin(
authorLogin: string,
async function updateAuthorMetadataStringByAuthorData(
authorLogin: string | object,
vcsConnector?: VCSConnector,
): Promise<string> {
): Promise<Contributor | null> {
if (!vcsConnector) {
return '';
return null;
}

const user = await getAuthorDetails(vcsConnector, authorLogin);
Expand All @@ -15,52 +15,49 @@ async function updateAuthorMetadataStringByAuthorLogin(
return user;
}

return '';
return null;
}

async function updateAuthorMetadataStringByFilePath(
filePath: string,
vcsConnector?: VCSConnector,
): Promise<string> {
): Promise<Contributor | null> {
if (!vcsConnector) {
return '';
return null;
}

const user = vcsConnector.getExternalAuthorByPath(filePath);

if (user) {
const author = replaceDoubleToSingleQuotes(JSON.stringify(user));
return author;
return user;
}

return '';
return null;
}

async function getAuthorDetails(
vcsConnector: VCSConnector,
author: string | object,
): Promise<string | null> {
): Promise<Contributor | null> {
if (typeof author === 'object') {
// Avoiding problems when adding to html markup
return replaceDoubleToSingleQuotes(JSON.stringify(author));
return author as Contributor;
}

try {
JSON.parse(author);
return replaceDoubleToSingleQuotes(author);
return JSON.parse(author);
} catch {
const user = await vcsConnector.getUserByLogin(author);

if (user) {
return replaceDoubleToSingleQuotes(JSON.stringify(user));
return user;
}

return null;
}
}

export {
updateAuthorMetadataStringByAuthorLogin,
updateAuthorMetadataStringByAuthorData as updateAuthorMetadataStringByAuthorLogin,
updateAuthorMetadataStringByFilePath,
getAuthorDetails,
};
48 changes: 19 additions & 29 deletions src/services/contributors.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import {readFile} from 'fs/promises';
import {dirname, join} from 'path';

import {replaceDoubleToSingleQuotes} from '../utils';
import {REGEXP_INCLUDE_CONTENTS, REGEXP_INCLUDE_FILE_PATH} from '../constants';
import {Contributor, Contributors, FileData} from '../models';
import {Contributor, Contributors} from '../models';
import {FileContributors, VCSConnector} from '../vcs-connector/connector-models';

async function getFileContributorsMetadata(
fileData: FileData,
vcsConnector: VCSConnector,
): Promise<string> {
const contributors = await getFileContributorsString(fileData, vcsConnector);

return `contributors: ${contributors}`;
export interface ContributorsServiceFileData {
resolvedFilePath: string;
inputFolderPathLength: number;
fileContent: string;
}

async function getFileContributorsString(
fileData: FileData,
export async function getFileContributors(
fileData: ContributorsServiceFileData,
vcsConnector: VCSConnector,
): Promise<string> {
const {tmpInputFilePath, inputFolderPathLength} = fileData;
): Promise<Contributor[]> {
const {resolvedFilePath, inputFolderPathLength} = fileData;

const relativeFilePath = tmpInputFilePath.substring(inputFolderPathLength);
const relativeFilePath = resolvedFilePath.substring(inputFolderPathLength);
const fileContributors: FileContributors =
await vcsConnector.getContributorsByPath(relativeFilePath);
let nestedContributors: Contributors = {};
Expand All @@ -40,11 +35,11 @@ async function getFileContributorsString(
fileContributorsWithContributorsIncludedFiles,
).map(([, contributor]) => contributor);

return replaceDoubleToSingleQuotes(JSON.stringify(contributorsArray));
return contributorsArray;
}

async function getContributorsForNestedFiles(
fileData: FileData,
fileData: ContributorsServiceFileData,
vcsConnector: VCSConnector,
): Promise<Contributors> {
const {fileContent, inputFolderPathLength} = fileData;
Expand Down Expand Up @@ -77,10 +72,10 @@ async function getContributorsForNestedFiles(
throw err;
}

const newFileData: FileData = {
const newFileData: ContributorsServiceFileData = {
...fileData,
fileContent: contentIncludeFile,
tmpInputFilePath: relativeIncludeFilePath,
resolvedFilePath: relativeIncludeFilePath,
};

nestedContributors = await getContributorsForNestedFiles(newFileData, vcsConnector);
Expand All @@ -95,10 +90,9 @@ async function getContributorsForNestedFiles(
}

function getRelativeIncludeFilePaths(
fileData: Pick<FileData, 'tmpInputFilePath'>,
{resolvedFilePath: tmpInputFilePath}: ContributorsServiceFileData,
includeContents: string[],
): Set<string> {
const {tmpInputFilePath} = fileData;
const relativeIncludeFilePaths: Set<string> = new Set();

includeContents.forEach((includeContent: string) => {
Expand All @@ -118,10 +112,8 @@ function getRelativeIncludeFilePaths(
return relativeIncludeFilePaths;
}

async function getFileIncludes(
fileData: Pick<FileData, 'fileContent' | 'tmpInputFilePath' | 'inputFolderPathLength'>,
) {
const {fileContent, tmpInputFilePath, inputFolderPathLength} = fileData;
export async function getFileIncludes(fileData: ContributorsServiceFileData) {
const {fileContent, inputFolderPathLength} = fileData;

const results = new Set<string>();

Expand All @@ -130,7 +122,7 @@ async function getFileIncludes(
return [];
}
const relativeIncludeFilePaths: Set<string> = getRelativeIncludeFilePaths(
{tmpInputFilePath},
fileData,
includeContents,
);
for (const relativeIncludeFilePath of relativeIncludeFilePaths.values()) {
Expand All @@ -151,12 +143,10 @@ async function getFileIncludes(
const includedPaths = await getFileIncludes({
inputFolderPathLength,
fileContent: contentIncludeFile,
tmpInputFilePath: relativeIncludeFilePath,
resolvedFilePath: relativeIncludeFilePath,
});
includedPaths.forEach((path) => results.add(path));
}

return Array.from(results.values());
}

export {getFileContributorsMetadata, getFileContributorsString, getFileIncludes};
Loading

0 comments on commit 1115459

Please sign in to comment.