587 lines
11 KiB
TypeScript
587 lines
11 KiB
TypeScript
import type { SourceLanguageCode, TargetLanguageCode } from 'deepl-node';
|
|
import type { MdNodeType } from './ast/mdast.js';
|
|
import { isBoolean } from './utils.js';
|
|
|
|
export interface ConfigBase {
|
|
/**
|
|
* Source's language code. Based on DeepL supported languages.
|
|
*/
|
|
sourceLanguage: SourceLanguageCode;
|
|
/**
|
|
* Output's languages code. Based on DeepL supported languages.
|
|
*/
|
|
outputLanguages: TargetLanguageCode[];
|
|
/**
|
|
* Sources and ouputs directories pairs. $langcode$ variable
|
|
* is provided to dynamically define directory.
|
|
*
|
|
* e.g. [ ["docs", "i18n/$langcode$/docs"], ["blog", "i18n/$langcode$/blog"] ]
|
|
*/
|
|
directories: [string, string][];
|
|
}
|
|
|
|
export interface Config extends ConfigBase {
|
|
/**
|
|
* Override current working directory, defaults to `process.cwd()`.
|
|
*/
|
|
cwd: string;
|
|
/**
|
|
* By default, all .md, .mdx, .json, and .yaml|.yml files inside
|
|
* source directories will be included.
|
|
*
|
|
* Define glob patterns to filter what files to include or exclude.
|
|
* But, the end result is still restricted by file types (.md, .mdx, .json).
|
|
*/
|
|
files: {
|
|
include?: string[];
|
|
exclude: string[];
|
|
};
|
|
/**
|
|
* Frontmatter fields.
|
|
*/
|
|
frontmatterFields: {
|
|
include: string[];
|
|
exclude: string[];
|
|
};
|
|
/**
|
|
* Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`.
|
|
*/
|
|
markdownNodes: {
|
|
default: boolean;
|
|
include: MdNodeType[];
|
|
exclude: MdNodeType[];
|
|
};
|
|
/**
|
|
* HTML elements to include and exlcude, down to the level of attributes
|
|
* and children. Include all HTML elements text content
|
|
* and some global attributes such as title and placeholder.
|
|
*/
|
|
htmlElements: {
|
|
include: Partial<{ [Tag in HtmlTag]: { children: boolean; attributes: string[] } }>;
|
|
exclude: HtmlTag[];
|
|
};
|
|
/**
|
|
* JSX components to include and exclude, down to the level of attributes
|
|
* and children. Include all JSX components text children
|
|
* and exclude all attributes by default.
|
|
*
|
|
* Support array, object, and jsx attribute value. For object and array value,
|
|
* you can specify the access path starting with the attribute name
|
|
* e.g. `items.description` to translate `items={[{description: "..."}]}.
|
|
*/
|
|
jsxComponents: {
|
|
default: boolean;
|
|
include: { [Name: string]: { children: boolean; attributes: string[] } };
|
|
exclude: string[];
|
|
};
|
|
/**
|
|
* JSON or YAML file properties to include and exclude.
|
|
* Exclude all properties by default.
|
|
*/
|
|
jsonOrYamlProperties: {
|
|
include: (string | number | symbol)[];
|
|
exclude: (string | number | symbol)[];
|
|
};
|
|
}
|
|
|
|
export interface UserConfig extends ConfigBase {
|
|
/**
|
|
* Override current working directory, defaults to `process.cwd()`.
|
|
*/
|
|
cwd?: string;
|
|
/**
|
|
* By default, all .md, .mdx, .json, and .yaml|.yml files inside
|
|
* source directories will be included.
|
|
*
|
|
* Define glob patterns to filter what files to include or exclude.
|
|
* But, the end result is still restricted by file types (.md, .mdx, .json).
|
|
*/
|
|
files?: {
|
|
include?: string[];
|
|
exclude?: string[];
|
|
};
|
|
/**
|
|
* Frontmatter fields.
|
|
*/
|
|
frontmatterFields?: {
|
|
include?: string[];
|
|
exclude?: string[];
|
|
};
|
|
/**
|
|
* Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`.
|
|
*/
|
|
markdownNodes?: {
|
|
default?: boolean;
|
|
include?: MdNodeType[];
|
|
exclude?: MdNodeType[];
|
|
};
|
|
/**
|
|
* HTML elements to include and exlcude, down to the level of attributes
|
|
* and children. Include all HTML elements text content
|
|
* and some global attributes such as title and placeholder.
|
|
*/
|
|
htmlElements?: {
|
|
default?: boolean;
|
|
include?: Partial<{ [Tag in HtmlTag]: { children: boolean; attributes: string[] } }>;
|
|
exclude?: HtmlTag[];
|
|
};
|
|
/**
|
|
* JSX components to include and exclude, down to the level of attributes
|
|
* and children. Include all JSX components text children
|
|
* and exclude all attributes by default.
|
|
*
|
|
* Support array, object, and jsx attribute value. For object and array value,
|
|
* you can specify the access path starting with the attribute name
|
|
* e.g. `items.description` to translate `items={[{description: "..."}]}.
|
|
*/
|
|
jsxComponents?: {
|
|
default?: boolean;
|
|
include?: { [Name: string]: { children: boolean; attributes: string[] } };
|
|
exclude?: string[];
|
|
};
|
|
/**
|
|
* JSON or YAML file properties to include and exclude.
|
|
* Exclude all properties by default.
|
|
*/
|
|
jsonOrYamlProperties?: {
|
|
include?: string[];
|
|
exclude?: string[];
|
|
};
|
|
}
|
|
|
|
|
|
|
|
export type HtmlElementsConfig = { [Tag in HtmlTag]: { children: boolean; attributes: string[] } };
|
|
|
|
export const HTML_ELEMENTS_CONFIG: HtmlElementsConfig = getHtmlElementsConfig();
|
|
|
|
function getHtmlElementsConfig(): HtmlElementsConfig {
|
|
const includeChildren: HtmlTag[] = [
|
|
'a',
|
|
'abbr',
|
|
'address',
|
|
'article',
|
|
'aside',
|
|
'audio',
|
|
'b',
|
|
'bdi',
|
|
'bdo',
|
|
'blockquote',
|
|
'body',
|
|
'button',
|
|
'canvas',
|
|
'caption',
|
|
'cite',
|
|
'col',
|
|
'colgroup',
|
|
'data',
|
|
'datalist',
|
|
'dd',
|
|
'del',
|
|
'details',
|
|
'dfn',
|
|
'dialog',
|
|
'div',
|
|
'dl',
|
|
'dt',
|
|
'em',
|
|
'fieldset',
|
|
'figcaption',
|
|
'figure',
|
|
'footer',
|
|
'form',
|
|
'h1',
|
|
'h2',
|
|
'h3',
|
|
'h4',
|
|
'h5',
|
|
'h6',
|
|
'header',
|
|
'html',
|
|
'i',
|
|
'input',
|
|
'ins',
|
|
'label',
|
|
'legend',
|
|
'li',
|
|
'main',
|
|
'mark',
|
|
'meter',
|
|
'nav',
|
|
'ol',
|
|
'optgroup',
|
|
'output',
|
|
'p',
|
|
'progress',
|
|
'q',
|
|
'rp',
|
|
's',
|
|
'samp',
|
|
'section',
|
|
'select',
|
|
'small',
|
|
'span',
|
|
'strong',
|
|
'sub',
|
|
'summary',
|
|
'sup',
|
|
'table',
|
|
'tbody',
|
|
'td',
|
|
'template',
|
|
'text-area',
|
|
'tfoot',
|
|
'th',
|
|
'thead',
|
|
'time',
|
|
'title',
|
|
'tr',
|
|
'track',
|
|
'u',
|
|
'ul'
|
|
];
|
|
|
|
const excludeChildren: HtmlTag[] = [
|
|
'area',
|
|
'base',
|
|
'br',
|
|
'code',
|
|
'embed',
|
|
'head',
|
|
'hr',
|
|
'iframe',
|
|
'img',
|
|
'kbd',
|
|
'link',
|
|
'meta',
|
|
'noscript',
|
|
'object',
|
|
'param',
|
|
'picture',
|
|
'pre',
|
|
'rt',
|
|
'ruby',
|
|
'script',
|
|
'source',
|
|
'style',
|
|
'svg',
|
|
'var',
|
|
'video',
|
|
'qbr'
|
|
];
|
|
|
|
const config: Partial<HtmlElementsConfig> = {};
|
|
|
|
for (const tag of includeChildren) {
|
|
config[tag] = {
|
|
children: true,
|
|
attributes: ['title']
|
|
};
|
|
}
|
|
|
|
for (const tag of excludeChildren) {
|
|
config[tag] = {
|
|
children: false,
|
|
attributes: ['title']
|
|
};
|
|
}
|
|
|
|
return config as HtmlElementsConfig;
|
|
}
|
|
|
|
export const HTML_TAGS = Object.keys(HTML_ELEMENTS_CONFIG) as HtmlTag[];
|
|
|
|
export function isHtmlTag(name: string): name is HtmlTag {
|
|
return HTML_TAGS.includes(name as HtmlTag);
|
|
}
|
|
|
|
export function resolveConfig({
|
|
sourceLanguage,
|
|
outputLanguages,
|
|
directories,
|
|
cwd,
|
|
files,
|
|
markdownNodes,
|
|
frontmatterFields,
|
|
htmlElements,
|
|
jsxComponents,
|
|
jsonOrYamlProperties
|
|
}: UserConfig): Config {
|
|
return {
|
|
sourceLanguage,
|
|
outputLanguages,
|
|
directories,
|
|
cwd: cwd ?? '',
|
|
files: files
|
|
? {
|
|
include: files.include,
|
|
exclude: files.exclude ?? []
|
|
}
|
|
: { exclude: [] },
|
|
markdownNodes: markdownNodes
|
|
? {
|
|
default: isBoolean(markdownNodes.default) ? markdownNodes.default : true,
|
|
include: markdownNodes.include ?? [],
|
|
exclude: markdownNodes.exclude ?? ['code']
|
|
}
|
|
: { default: true, include: [], exclude: ['code'] },
|
|
frontmatterFields: frontmatterFields
|
|
? {
|
|
include: frontmatterFields.include ?? [],
|
|
exclude: frontmatterFields.exclude ?? []
|
|
}
|
|
: { include: [], exclude: [] },
|
|
|
|
htmlElements: htmlElements
|
|
? {
|
|
include: htmlElements.include
|
|
? (isBoolean(htmlElements.default) && htmlElements.default) ||
|
|
htmlElements.default === undefined
|
|
? { ...HTML_ELEMENTS_CONFIG, ...htmlElements.include }
|
|
: htmlElements.include
|
|
: isBoolean(htmlElements.default) && !htmlElements.default
|
|
? {}
|
|
: HTML_ELEMENTS_CONFIG,
|
|
exclude: htmlElements.exclude ?? []
|
|
}
|
|
: { include: HTML_ELEMENTS_CONFIG, exclude: [] },
|
|
jsxComponents: jsxComponents
|
|
? {
|
|
default: isBoolean(jsxComponents.default) ? jsxComponents.default : true,
|
|
include: jsxComponents.include ?? {},
|
|
exclude: jsxComponents.exclude ?? []
|
|
}
|
|
: { default: true, include: {}, exclude: [] },
|
|
jsonOrYamlProperties: jsonOrYamlProperties
|
|
? { include: jsonOrYamlProperties.include ?? [], exclude: jsonOrYamlProperties.exclude ?? [] }
|
|
: { include: [], exclude: [] }
|
|
};
|
|
}
|
|
|
|
|
|
|
|
export function isFrontmatterFieldIncluded({
|
|
field,
|
|
config
|
|
}: {
|
|
field: string;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
!config.frontmatterFields.exclude.includes(field) &&
|
|
config.frontmatterFields.include.includes(field)
|
|
);
|
|
}
|
|
|
|
export function isMarkdownNodeIncluded({
|
|
type,
|
|
config
|
|
}: {
|
|
type: MdNodeType;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
!config.markdownNodes.exclude.includes(type) &&
|
|
(config.markdownNodes.default || config.markdownNodes.include.includes(type))
|
|
);
|
|
}
|
|
|
|
export function isHtmlElementIncluded({ tag, config }: { tag: HtmlTag; config: Config }): boolean {
|
|
return (
|
|
!config.htmlElements.exclude.includes(tag) &&
|
|
Object.keys(config.htmlElements.include).includes(tag)
|
|
);
|
|
}
|
|
|
|
export function isHtmlElementAttributeIncluded({
|
|
tag,
|
|
attribute,
|
|
config
|
|
}: {
|
|
tag: HtmlTag;
|
|
attribute: string;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
isHtmlElementIncluded({ tag, config }) &&
|
|
config.htmlElements.include[tag]!.attributes.includes(attribute)
|
|
);
|
|
}
|
|
|
|
export function isHtmlElementChildrenIncluded({
|
|
tag,
|
|
config
|
|
}: {
|
|
tag: HtmlTag;
|
|
config: Config;
|
|
}): boolean {
|
|
return isHtmlElementIncluded({ tag, config }) && config.htmlElements.include[tag]!.children;
|
|
}
|
|
|
|
export function isJsxComponentIncluded({
|
|
name,
|
|
config
|
|
}: {
|
|
name: string;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
!config.jsxComponents.exclude.includes(name) &&
|
|
(config.jsxComponents.default || Object.keys(config.jsxComponents.include).includes(name))
|
|
);
|
|
}
|
|
|
|
export function isJsxComponentAttributeIncluded({
|
|
name,
|
|
attribute,
|
|
config
|
|
}: {
|
|
name: string;
|
|
attribute: string;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
!config.jsxComponents.exclude.includes(name) &&
|
|
Object.keys(config.jsxComponents.include).includes(name) &&
|
|
config.jsxComponents.include[name].attributes.includes(attribute)
|
|
);
|
|
}
|
|
|
|
export function isJsxComponentChildrenIncluded({
|
|
name,
|
|
config
|
|
}: {
|
|
name: string;
|
|
config: Config;
|
|
}): boolean {
|
|
return (
|
|
!config.jsxComponents.exclude.includes(name) &&
|
|
((Object.keys(config.jsxComponents.include).includes(name) &&
|
|
config.jsxComponents.include[name].children) ||
|
|
(!Object.keys(config.jsxComponents.include).includes(name) && config.jsxComponents.default))
|
|
);
|
|
}
|
|
|
|
export function isJsonOrYamlPropertyIncluded({
|
|
property,
|
|
config
|
|
}: {
|
|
config: Config;
|
|
property: string | number | symbol;
|
|
}): boolean {
|
|
return (
|
|
!config.jsonOrYamlProperties.exclude.includes(property) &&
|
|
config.jsonOrYamlProperties.include.includes(property)
|
|
);
|
|
}
|
|
|
|
export type HtmlTag =
|
|
| 'a'
|
|
| 'abbr'
|
|
| 'address'
|
|
| 'article'
|
|
| 'aside'
|
|
| 'audio'
|
|
| 'b'
|
|
| 'bdi'
|
|
| 'bdo'
|
|
| 'blockquote'
|
|
| 'body'
|
|
| 'button'
|
|
| 'canvas'
|
|
| 'caption'
|
|
| 'cite'
|
|
| 'col'
|
|
| 'colgroup'
|
|
| 'data'
|
|
| 'datalist'
|
|
| 'dd'
|
|
| 'del'
|
|
| 'details'
|
|
| 'dfn'
|
|
| 'dialog'
|
|
| 'div'
|
|
| 'dl'
|
|
| 'dt'
|
|
| 'em'
|
|
| 'fieldset'
|
|
| 'figcaption'
|
|
| 'figure'
|
|
| 'footer'
|
|
| 'form'
|
|
| 'h1'
|
|
| 'h2'
|
|
| 'h3'
|
|
| 'h4'
|
|
| 'h5'
|
|
| 'h6'
|
|
| 'header'
|
|
| 'html'
|
|
| 'i'
|
|
| 'input'
|
|
| 'ins'
|
|
| 'label'
|
|
| 'legend'
|
|
| 'li'
|
|
| 'main'
|
|
| 'mark'
|
|
| 'meter'
|
|
| 'nav'
|
|
| 'ol'
|
|
| 'optgroup'
|
|
| 'output'
|
|
| 'p'
|
|
| 'progress'
|
|
| 'q'
|
|
| 'rp'
|
|
| 's'
|
|
| 'samp'
|
|
| 'section'
|
|
| 'select'
|
|
| 'small'
|
|
| 'span'
|
|
| 'strong'
|
|
| 'sub'
|
|
| 'summary'
|
|
| 'sup'
|
|
| 'table'
|
|
| 'tbody'
|
|
| 'td'
|
|
| 'template'
|
|
| 'text-area'
|
|
| 'tfoot'
|
|
| 'th'
|
|
| 'thead'
|
|
| 'time'
|
|
| 'title'
|
|
| 'tr'
|
|
| 'track'
|
|
| 'u'
|
|
| 'ul'
|
|
| 'area'
|
|
| 'base'
|
|
| 'br'
|
|
| 'code'
|
|
| 'embed'
|
|
| 'head'
|
|
| 'hr'
|
|
| 'iframe'
|
|
| 'img'
|
|
| 'kbd'
|
|
| 'link'
|
|
| 'meta'
|
|
| 'noscript'
|
|
| 'object'
|
|
| 'param'
|
|
| 'picture'
|
|
| 'pre'
|
|
| 'rt'
|
|
| 'ruby'
|
|
| 'script'
|
|
| 'source'
|
|
| 'style'
|
|
| 'svg'
|
|
| 'var'
|
|
| 'video'
|
|
| 'qbr';
|