Skip to content

Commit

Permalink
feat: Implement new build configuration systemcon
Browse files Browse the repository at this point in the history
  • Loading branch information
3y3 committed Nov 27, 2024
1 parent cff3f6c commit baf8f6b
Show file tree
Hide file tree
Showing 24 changed files with 2,108 additions and 0 deletions.
120 changes: 120 additions & 0 deletions src/commands/build/__tests__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import type {Run} from '../run';
import type {BuildConfig, BuildRawConfig} from '..';

import {Mock, describe, expect, it, vi} from 'vitest';
import {Build} from '..';
import {handler as originalHandler} from '../handler';
import {withConfigUtils} from '~/config';

export const handler = originalHandler as Mock;

// eslint-disable-next-line no-var
var resolveConfig: Mock;

vi.mock('shelljs');
vi.mock('../handler');
vi.mock('~/config', async (importOriginal) => {
resolveConfig = vi.fn((_path, {defaults, fallback}) => {
return defaults || fallback;
});

return {
...((await importOriginal()) as {}),
resolveConfig,
};
});

export async function runBuild(args: string) {
const build = new Build();

build.apply();

await build.parse(['node', 'index'].concat(args.split(' ')));
}

export function testConfig(name: string, args: string, result: DeepPartial<BuildConfig>): void;
export function testConfig(name: string, args: string, error: Error): void;
export function testConfig(
name: string,
args: string,
config: DeepPartial<BuildRawConfig>,
result: DeepPartial<BuildConfig>,
): void;
export function testConfig(
name: string,
args: string,
config: DeepPartial<BuildRawConfig>,
error: Error,
): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function testConfig(name: string, args: string, config: any, result?: any): void {
it(name, async () => {
if (!result) {
result = config;
config = {};
}

resolveConfig.mockImplementation((path, {defaults}) => {
if (path.endsWith('.yfmlint')) {
return withConfigUtils(path, {});
}

if (path.endsWith('redirects.yaml')) {
return withConfigUtils(null, {});
}

return withConfigUtils(path, {
...defaults,
...config,
});
});

handler.mockImplementation((run: Run) => {
expect(run.config).toMatchObject(result as Partial<BuildRawConfig>);
});

if (result instanceof Error) {
await expect(() =>
runBuild('--input ./input --output ./output ' + args),
).rejects.toThrow(result);
} else {
await runBuild('--input ./input --output ./output ' + args);

expect(handler).toBeCalled();
}
});
}

export function testBooleanFlag(name: string, arg: string, defaults: boolean) {
describe(name, () => {
testConfig('should handle default', '', {
[name]: defaults,
});

testConfig('should handle arg', arg, {
[name]: true,
});

testConfig(
'should handle config enabled',
'',
{
[name]: true,
},
{
[name]: true,
},
);

testConfig(
'should handle config disabled',
'',
{
[name]: false,
},
{
[name]: false,
},
);
});
}
148 changes: 148 additions & 0 deletions src/commands/build/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {bold, underline} from 'chalk';
import {options as globalOptions} from '~/program/config';
import {option} from '~/config';
import {Stage} from '~/constants';

export enum OutputFormat {
md = 'md',
html = 'html',
}

const outputFormat = option({
flags: '-f, --output-format <value>',
defaultInfo: 'html',
choices: ['html', 'md'],
desc: `
Format of output files. (html or md)
If ${bold('html')} is selected, then renders md to static html files.
(See also ${underline('--static-content')} option)
If ${bold('md')} is selected, then renders md to prepared md files
enriched by additional metadata.
(Useful for complex documentation servers with runtime rendering)
`,
});

const langs = option({
flags: '--lang, --langs <value...>',
desc: 'Allow loading custom resources into statically generated pages.',
// parser: toArray,
});

const vars = option({
flags: '-v, --vars <json>',
desc: `
Pass list of variables directly to build.
Variables should be passed in JSON format.
Passed variables overrides the same in presets.yaml
Example:
{{PROGRAM}} -i ./ -o ./build -v '{"name":"test"}'
`,
parser: (value) => JSON.parse(value),
});

const varsPreset = option({
flags: '--vars-preset <value>',
desc: `
Select vars preset of documentation.
Selected preset will be merged with default section of presets.yaml
`,
});

const allowHtml = option({
flags: '--allow-html',
desc: 'Allow to use HTML in Markdown files.',
defaultInfo: true,
});

const sanitizeHtml = option({
flags: '--sanitize-html',
desc: 'Toggle transformed HTML sanitizing. (Slow but secure feature)',
defaultInfo: true,
});

const ignore = option({
flags: '--ignore <glob...>',
desc: `
Do not process paths matched by glob.
Example:
{{PROGRAM}} -i ./input -o ./output --ignore *.bad.md
or
{{PROGRAM}} -i ./ -o ./build --ignore ./build
`,
});

// TODO: options below are not beautified.
const addMapFile = option({
flags: '--add-map-file',
desc: 'Should add all paths of documentation into file.json.',
});

const removeHiddenTocItems = option({
flags: '--remove-hidden-toc-items',
desc: 'Remove from Toc all items marked as hidden.',
});

const mergeIncludes = option({
flags: '--merge-includes',
desc: 'Merge includes syntax during md to md processing.',
});

const resources = option({
flags: '--resource, --resources <value...>',
desc: 'Allow loading custom resources into statically generated pages.',
// parser: toArray,
});

const allowCustomResources = option({
flags: '--allow-custom-resources',
desc: 'Allow loading custom resources into statically generated pages.',
});

const staticContent = option({
flags: '--static-content',
desc: 'Allow loading custom resources into statically generated pages.',
});

const ignoreStage = option({
flags: '--ignore-stage <value>',
defaultInfo: Stage.SKIP,
desc: 'Ignore tocs with stage.',
});

const addSystemMeta = option({
flags: '--add-system-meta',
desc: 'Should add system section variables form presets into files meta data.',
});

const buildDisabled = option({
flags: '--build-disabled',
desc: 'Disable building.',
});

export const options = {
input: globalOptions.input,
output: globalOptions.output,
config: globalOptions.config,
langs,
outputFormat,
varsPreset,
vars,
allowHtml,
sanitizeHtml,
addMapFile,
removeHiddenTocItems,
mergeIncludes,
resources,
allowCustomResources,
staticContent,
ignore,
ignoreStage,
addSystemMeta,
buildDisabled,
};
10 changes: 10 additions & 0 deletions src/commands/build/features/changelogs/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {option} from '~/config';

const changelogs = option({
flags: '--changelogs',
desc: 'Beta functionality: Toggle processing of experimental changelogs syntax',
});

export const options = {
changelogs,
};
26 changes: 26 additions & 0 deletions src/commands/build/features/changelogs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {Build} from '~/commands';
import type {Command} from '~/config';
import {defined} from '~/config';
import {options} from './config';

export type ChangelogsArgs = {
changelogs: boolean | string;
};

export type ChangelogsConfig = {
changelogs: boolean | string;
};

export class Changelogs {
apply(program: Build) {
program.hooks.Command.tap('Changelogs', (command: Command) => {
command.addOption(options.changelogs);
});

program.hooks.Config.tap('Changelogs', (config, args) => {
config.changelogs = defined('changelogs', args, config) || false;

return config;
});
}
}
16 changes: 16 additions & 0 deletions src/commands/build/features/contributors/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {option} from '~/config';

const contributors = option({
flags: '--contributors',
desc: 'Should attach contributors into files',
});

const ignoreAuthorPatterns = option({
flags: '--ignore-author-patterns <string>',
desc: 'Ignore authors if they contain passed string',
});

export const options = {
contributors,
ignoreAuthorPatterns,
};
55 changes: 55 additions & 0 deletions src/commands/build/features/contributors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type {Build} from '../..';
import type {Command} from '~/config';
import type {VCSConnectorConfig} from '~/vcs-connector/connector-models';

import {defined} from '~/config';
import {options} from './config';

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;
connector?: VCSConnectorConfig;
}

export type ContributorsArgs = {
contributors?: boolean;
ignoreAuthorPatterns?: string[];
};

export type ContributorsConfig = {
vcs: VCSConfiguration;
contributors: boolean;
ignoreAuthorPatterns: string[];
};

export class Contributors {
apply(program: Build) {
program.hooks.Command.tap('Contributors', (command: Command) => {
command.addOption(options.contributors);
command.addOption(options.ignoreAuthorPatterns);
});

program.hooks.Config.tap('Contributors', (config, args) => {
config.vcs = defined('vcs', config) || {};
config.contributors = defined('contributors', args, config) || false;
config.ignoreAuthorPatterns = defined('ignoreAuthorPatterns', args, config) || [];

return config;
});
}
}
Loading

0 comments on commit baf8f6b

Please sign in to comment.