New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More →

typedoc-plugin-markdown

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

typedoc-plugin-markdown - npm Package Compare versions

Comparing version

to
4.0.0-next.6

@@ -44,4 +44,4 @@ "use strict";

app.options.addDeclaration({
name: 'flattenOutput',
help: '[Markdown Plugin] Flatten output files.',
name: 'flattenOutputFiles',
help: '[Markdown Plugin] Flatten output files without directories.',
type: typedoc_1.ParameterType.Boolean,

@@ -51,4 +51,4 @@ defaultValue: false,

app.options.addDeclaration({
name: 'symbolsWithOwnFile',
help: "[Markdown Plugin] Specifies which symbols are contained in their own file. Values 'none', 'all' OR Array of ['class', 'interface', 'enum', 'function', 'variable', 'type']",
name: 'reflectionsWithOwnFile',
help: "[Markdown Plugin] Specifies which reflections are contained in their own file. Values 'none', 'all' OR Array of ['class', 'interface', 'enum', 'function', 'variable', 'type']",
type: typedoc_1.ParameterType.String | typedoc_1.ParameterType.Array,

@@ -58,8 +58,2 @@ defaultValue: 'all',

app.options.addDeclaration({
name: 'groupBySymbols',
help: '[Markdown Plugin] Groups symbols by headings if applicable e.g Classes, Functions. If set to false all symbols will render on the same level. Defaults to `true`',
type: typedoc_1.ParameterType.Boolean,
defaultValue: true,
});
app.options.addDeclaration({
name: 'hidePageTitle',

@@ -95,2 +89,8 @@ help: '[Markdown Plugin] Do not print page title.',

app.options.addDeclaration({
name: 'groupByReflections',
help: '[Markdown Plugin] Groups reflections by headings if applicable e.g Classes, Functions. If set to false all symbols will render on the same level. Defaults to `true`',
type: typedoc_1.ParameterType.Boolean,
defaultValue: true,
});
app.options.addDeclaration({
name: 'typeDeclarationStyle',

@@ -97,0 +97,0 @@ help: '[Markdown Plugin] Specify the render style of type declarations.',

import { ReflectionKind, TypeDocOptionMap } from 'typedoc';
export interface TypedocPluginMarkdownOptions extends TypeDocOptionMap {
baseUrl: string;
enableFrontmatter: boolean;
entryDocument: string;
hideBreadcrumbs: boolean;

@@ -7,14 +10,12 @@ hideInPageTOC: boolean;

hideHierarchy: boolean;
entryDocument: string;
indexTitle: string;
namedAnchors: boolean;
enableFrontmatter: boolean;
flattenOutputFiles: boolean;
frontmatterTags: string[];
frontmatterGlobals: FrontmatterGlobals;
frontmatterNamingConvention: FrontmatterNamingConvention;
baseUrl: string;
groupByReflections: boolean;
longTitles: boolean;
symbolsWithOwnFile: string | string[];
namedAnchors: boolean;
reflectionsWithOwnFile: string | string[];
typeDeclarationStyle: 'list' | 'table';
groupBySymbols: boolean;
}

@@ -26,4 +27,2 @@ export interface TemplateMapping {

kind: ReflectionKind;
labelSingular: string;
labelPlural: string;
}

@@ -30,0 +29,0 @@ export type Collapse = 'object' | 'function' | 'all' | 'none';

@@ -9,3 +9,3 @@ "use strict";

const md = [];
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(declaration, context.getOption('groupBySymbols')) + 1;
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(declaration, context.getOption('groupByReflections')) + 1;
const typeDeclaration = (_a = declaration.type) === null || _a === void 0 ? void 0 : _a.declaration;

@@ -12,0 +12,0 @@ if (declaration.type) {

@@ -9,3 +9,3 @@ "use strict";

const md = [];
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(reflection, context.getOption('groupBySymbols'));
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(reflection, context.getOption('groupByReflections'));
if (context.getOption('namedAnchors')) {

@@ -12,0 +12,0 @@ md.push(`<a id="${reflection.anchor}" name="${reflection.anchor}"></a>`);

@@ -9,4 +9,3 @@ "use strict";

const md = [];
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(reflection, context.getOption('groupBySymbols')) +
1;
const headingLevel = (0, helpers_1.getReflectionHeadingLevel)(reflection, context.getOption('groupByReflections')) + 1;
if (reflection.comment) {

@@ -13,0 +12,0 @@ md.push(context.partials.comment(reflection.comment, headingLevel));

@@ -22,3 +22,3 @@ "use strict";

? parentHeadingLevel
: (0, helpers_1.getReflectionHeadingLevel)(signature.parent, context.getOption('groupBySymbols'))) + 1;
: (0, helpers_1.getReflectionHeadingLevel)(signature.parent, context.getOption('groupByReflections'))) + 1;
if (signature.comment) {

@@ -25,0 +25,0 @@ md.push(context.partials.comment(signature.comment, headingLevel));

@@ -21,3 +21,3 @@ "use strict";

(_a = container.groups) === null || _a === void 0 ? void 0 : _a.filter((group) => !group.allChildrenHaveOwnDocument()).forEach((group) => {
const headingLevel = (0, helpers_1.getGroupHeadingLevel)(container, context.getOption('groupBySymbols'));
const headingLevel = (0, helpers_1.getGroupHeadingLevel)(container, context.getOption('groupByReflections'));
if (group.categories) {

@@ -30,3 +30,3 @@ md.push((0, els_1.heading)(headingLevel, group.title));

else {
if (context.getOption('groupBySymbols') ||
if (context.getOption('groupByReflections') ||
constants_1.SYMBOLS_WITH_DOCUMENTS.includes(container.kind)) {

@@ -33,0 +33,0 @@ md.push((0, els_1.heading)(headingLevel, group.title));

@@ -18,3 +18,3 @@ "use strict";

(isVisible && reflection.groups)) {
const headingLevel = (0, helpers_1.getIndexHeadingLevel)(reflection, context.getOption('groupBySymbols'));
const headingLevel = (0, helpers_1.getIndexHeadingLevel)(reflection, context.getOption('groupByReflections'));
const subHeadingLevel = headingLevel + 1;

@@ -21,0 +21,0 @@ md.push((0, els_1.heading)(headingLevel, 'Index') + '\n\n');

import { CommentTag, ContainerReflection, DeclarationReflection, ProjectReflection, SignatureReflection } from 'typedoc';
export declare function getIndexHeadingLevel(reflection: DeclarationReflection | ProjectReflection, groupBySymbols: boolean): 2 | 3 | 4;
export declare function getGroupHeadingLevel(container: ContainerReflection, groupBySymbols: boolean): 2 | 3 | 4;
export declare function getReflectionHeadingLevel(reflection: DeclarationReflection | SignatureReflection, groupBySymbols: boolean): 1 | 2 | 3 | 4 | 5;
export declare function getIndexHeadingLevel(reflection: DeclarationReflection | ProjectReflection, groupByReflections: boolean): 2 | 3 | 4;
export declare function getGroupHeadingLevel(container: ContainerReflection, groupByReflections: boolean): 2 | 3 | 4;
export declare function getReflectionHeadingLevel(reflection: DeclarationReflection | SignatureReflection, groupByReflections: boolean): 1 | 2 | 3 | 4 | 5;
export declare function getReflectionTitle(reflection: DeclarationReflection, fullname?: boolean, typeParams?: boolean): string;

@@ -6,0 +6,0 @@ export declare function getTypeParameters(reflection: DeclarationReflection): string;

@@ -7,3 +7,3 @@ "use strict";

const utils_1 = require("./utils");
function getIndexHeadingLevel(reflection, groupBySymbols) {
function getIndexHeadingLevel(reflection, groupByReflections) {
if (reflection.kindOf([

@@ -17,13 +17,13 @@ typedoc_1.ReflectionKind.Project,

}
return groupBySymbols ? 4 : 3;
return groupByReflections ? 4 : 3;
}
exports.getIndexHeadingLevel = getIndexHeadingLevel;
function getGroupHeadingLevel(container, groupBySymbols) {
function getGroupHeadingLevel(container, groupByReflections) {
if (container.kindOf(typedoc_1.ReflectionKind.Project)) {
return 2;
}
return container.hasOwnDocument ? 2 : groupBySymbols ? 4 : 3;
return container.hasOwnDocument ? 2 : groupByReflections ? 4 : 3;
}
exports.getGroupHeadingLevel = getGroupHeadingLevel;
function getReflectionHeadingLevel(reflection, groupBySymbols) {
function getReflectionHeadingLevel(reflection, groupByReflections) {
var _a, _b, _c, _d;

@@ -34,3 +34,3 @@ if (reflection.hasOwnDocument) {

if ((_a = reflection === null || reflection === void 0 ? void 0 : reflection.parent) === null || _a === void 0 ? void 0 : _a.kindOf(typedoc_1.ReflectionKind.Project)) {
return groupBySymbols ? 3 : 2;
return groupByReflections ? 3 : 2;
}

@@ -40,3 +40,3 @@ if (reflection.kindOf(typedoc_1.ReflectionKind.Constructor)) {

}
if (groupBySymbols) {
if (groupByReflections) {
return ((_c = reflection.parent) === null || _c === void 0 ? void 0 : _c.hasOwnDocument) ? 3 : 5;

@@ -43,0 +43,0 @@ }

@@ -7,6 +7,7 @@ import { DeclarationReflection, PageEvent, ProjectReflection, Reflection, RenderTemplate, Renderer, Theme, UrlMapping } from 'typedoc';

entryPoints: string[];
flattenOutputFiles: string;
groupByReflections: string[];
preserveAnchorCasing: boolean;
readme: string;
preserveAnchorCasing: boolean;
symbolsWithOwnFile: string | string[];
flattenOutput: string;
reflectionsWithOwnFile: string | string[];
private _renderContext?;

@@ -22,4 +23,5 @@ private anchors;

getUrls(project: ProjectReflection): UrlMapping<any>[];
getUrl(reflection: Reflection, relative?: Reflection, separator?: string): string;
buildUrls(reflection: DeclarationReflection, urls: UrlMapping[], parentFragments?: string[]): UrlMapping[];
buildUrls(reflection: DeclarationReflection, urls: UrlMapping[]): UrlMapping[];
getUrl(reflection: DeclarationReflection, mapping: TemplateMapping): string;
getNamespaces(reflection: DeclarationReflection, namespaces?: string[]): string[];
applyAnchorUrl(reflection: Reflection, container: Reflection, isSymbol?: boolean): void;

@@ -26,0 +28,0 @@ get mappings(): Record<number, TemplateMapping>;

@@ -36,3 +36,2 @@ "use strict";

const constants_1 = require("./support/constants");
const utils_1 = require("./support/utils");
const theme_context_1 = require("./theme-context");

@@ -88,55 +87,9 @@ class MarkdownTheme extends typedoc_1.Theme {

}
getUrl(reflection, relative, separator = '.') {
let url = (0, utils_1.slugify)(reflection.getAlias());
if (reflection.parent &&
reflection.parent !== relative &&
!(reflection.parent instanceof typedoc_1.ProjectReflection)) {
url =
this.getUrl(reflection.parent, relative, separator) + separator + url;
}
return url;
}
buildUrls(reflection, urls, parentFragments) {
var _a, _b;
buildUrls(reflection, urls) {
const mapping = this.mappings[reflection.kind];
let fragments = [];
if (mapping) {
const isModuleOrNamespace = reflection.kindOf([
typedoc_1.ReflectionKind.Module,
typedoc_1.ReflectionKind.Namespace,
]);
const isModule = reflection.kindOf([typedoc_1.ReflectionKind.Module]);
const isNamespace = reflection.kindOf([typedoc_1.ReflectionKind.Namespace]);
const hasDirectory = isNamespace
? Boolean(parentFragments) &&
!((_a = reflection === null || reflection === void 0 ? void 0 : reflection.parent) === null || _a === void 0 ? void 0 : _a.kindOf(typedoc_1.ReflectionKind.Namespace))
: !isModule;
const fragment = hasDirectory && this.symbolsWithOwnFile[0] !== 'none'
? `${mapping.directory}/${(0, utils_1.slugify)(reflection.getAlias())}`
: (0, utils_1.slugify)(reflection.getAlias());
if (!reflection.url || !constants_1.URL_PREFIX.test(reflection.url)) {
fragments = [fragment];
if (parentFragments) {
fragments.unshift(parentFragments.join('/'));
}
const hasNameSpace = (_b = reflection.children) === null || _b === void 0 ? void 0 : _b.some((child) => child.kindOf(typedoc_1.ReflectionKind.Namespace));
if (hasNameSpace ||
(!hasDirectory && this.symbolsWithOwnFile[0] !== 'none')) {
fragments.push(fragment);
}
let url = fragments.join('/');
if (isNamespace &&
Boolean(parentFragments) &&
this.symbolsWithOwnFile[0] !== 'none') {
const namespaceFragments = fragment.split('/');
url = url + '/' + namespaceFragments[namespaceFragments.length - 1];
}
if (this.flattenOutput) {
url = url.replace(/\//g, '.');
}
url = url.replace(/^\/|\/$/g, '') + '.md';
urls.push(new typedoc_1.UrlMapping(url, reflection, mapping.template));
reflection.url = url;
reflection.hasOwnDocument = true;
}
const url = this.getUrl(reflection, mapping);
urls.push(new typedoc_1.UrlMapping(url, reflection, mapping.template));
reflection.url = url;
reflection.hasOwnDocument = true;
for (const child of reflection.children || []) {

@@ -147,6 +100,3 @@ if (mapping.isLeaf) {

else {
if (fragments[fragments.length - 1] === fragments[fragments.length - 2]) {
fragments.pop();
}
this.buildUrls(child, urls, fragments);
this.buildUrls(child, urls);
}

@@ -160,2 +110,47 @@ }

}
getUrl(reflection, mapping) {
var _a;
const alias = reflection.getFriendlyFullName().replace(/\//g, '_');
if (this.flattenOutputFiles) {
return alias + '.md';
}
const parts = alias.split('.');
const includesNamespace = (_a = reflection.children) === null || _a === void 0 ? void 0 : _a.some((child) => child.kindOf(typedoc_1.ReflectionKind.Namespace));
const isModuleOrNamespace = reflection.kindOf([
typedoc_1.ReflectionKind.Module,
typedoc_1.ReflectionKind.Namespace,
]);
const namespaces = this.getNamespaces(reflection);
if (mapping.directory && !isModuleOrNamespace && this.groupByReflections) {
parts.splice(parts.length - 1, 0, mapping.directory);
}
if ((this.reflectionsWithOwnFile[0] !== 'none' && isModuleOrNamespace) ||
includesNamespace) {
parts.push(parts[parts.length - 1]);
}
if (namespaces.length > 0) {
namespaces.forEach((namespaceName) => {
const namespaceIndex = parts.findIndex((part) => part === namespaceName);
if (namespaceIndex > 0) {
parts.splice(namespaceIndex, 0, this.mappings[typedoc_1.ReflectionKind.Namespace].directory);
}
});
if (reflection.kindOf(typedoc_1.ReflectionKind.Namespace))
parts[parts.length - 1] = `${typedoc_1.ReflectionKind.singularString(typedoc_1.ReflectionKind.Namespace).toLowerCase()}.${parts[parts.length - 1]}`;
}
if (!isModuleOrNamespace) {
parts[parts.length - 1] = `${typedoc_1.ReflectionKind.singularString(reflection.kind).toLowerCase()}.${parts[parts.length - 1]}`;
}
return parts.join(this.flattenOutputFiles ? '.' : '/') + '.md';
}
getNamespaces(reflection, namespaces = []) {
var _a;
if (reflection === null || reflection === void 0 ? void 0 : reflection.kindOf(typedoc_1.ReflectionKind.Namespace)) {
namespaces.push(reflection.name);
}
if ((_a = reflection.parent) === null || _a === void 0 ? void 0 : _a.kindOf(typedoc_1.ReflectionKind.Namespace)) {
this.getNamespaces(reflection === null || reflection === void 0 ? void 0 : reflection.parent, namespaces);
}
return namespaces;
}
applyAnchorUrl(reflection, container, isSymbol = false) {

@@ -186,3 +181,3 @@ var _a, _b;

get mappings() {
const isAll = this.symbolsWithOwnFile.includes('all');
const isAll = this.reflectionsWithOwnFile.includes('all');
const mappings = {

@@ -194,4 +189,2 @@ [typedoc_1.ReflectionKind.Module]: {

kind: typedoc_1.ReflectionKind.Module,
labelSingular: 'Module',
labelPlural: 'Modules',
},

@@ -203,7 +196,5 @@ [typedoc_1.ReflectionKind.Namespace]: {

kind: typedoc_1.ReflectionKind.Namespace,
labelSingular: 'Namespace',
labelPlural: 'Namespaces',
},
};
if (isAll || this.symbolsWithOwnFile.includes('class')) {
if (isAll || this.reflectionsWithOwnFile.includes('class')) {
mappings[typedoc_1.ReflectionKind.Class] = {

@@ -214,7 +205,5 @@ isLeaf: false,

kind: typedoc_1.ReflectionKind.Class,
labelSingular: 'Class',
labelPlural: 'Classes',
};
}
if (isAll || this.symbolsWithOwnFile.includes('interface')) {
if (isAll || this.reflectionsWithOwnFile.includes('interface')) {
mappings[typedoc_1.ReflectionKind.Interface] = {

@@ -225,7 +214,5 @@ isLeaf: false,

kind: typedoc_1.ReflectionKind.Interface,
labelSingular: 'Interface',
labelPlural: 'Interfaces',
};
}
if (isAll || this.symbolsWithOwnFile.includes('enum')) {
if (isAll || this.reflectionsWithOwnFile.includes('enum')) {
mappings[typedoc_1.ReflectionKind.Enum] = {

@@ -236,7 +223,5 @@ isLeaf: false,

kind: typedoc_1.ReflectionKind.Enum,
labelSingular: 'Enum',
labelPlural: 'Enums',
};
}
if (isAll || this.symbolsWithOwnFile.includes('function')) {
if (isAll || this.reflectionsWithOwnFile.includes('function')) {
mappings[typedoc_1.ReflectionKind.Function] = {

@@ -247,7 +232,5 @@ isLeaf: true,

kind: typedoc_1.ReflectionKind.Function,
labelSingular: 'Function',
labelPlural: 'Functions',
};
}
if (isAll || this.symbolsWithOwnFile.includes('type')) {
if (isAll || this.reflectionsWithOwnFile.includes('type')) {
mappings[typedoc_1.ReflectionKind.TypeAlias] = {

@@ -258,7 +241,5 @@ isLeaf: true,

kind: typedoc_1.ReflectionKind.TypeAlias,
labelSingular: 'Type Aliases',
labelPlural: 'Type Alias',
};
}
if (isAll || this.symbolsWithOwnFile.includes('var')) {
if (isAll || this.reflectionsWithOwnFile.includes('var')) {
mappings[typedoc_1.ReflectionKind.Variable] = {

@@ -269,4 +250,2 @@ isLeaf: true,

kind: typedoc_1.ReflectionKind.Variable,
labelSingular: 'Variable',
labelPlural: 'Variables',
};

@@ -287,13 +266,16 @@ }

__decorate([
(0, typedoc_1.BindOption)('readme')
], MarkdownTheme.prototype, "readme", void 0);
(0, typedoc_1.BindOption)('flattenOutputFiles')
], MarkdownTheme.prototype, "flattenOutputFiles", void 0);
__decorate([
(0, typedoc_1.BindOption)('groupByReflections')
], MarkdownTheme.prototype, "groupByReflections", void 0);
__decorate([
(0, typedoc_1.BindOption)('preserveAnchorCasing')
], MarkdownTheme.prototype, "preserveAnchorCasing", void 0);
__decorate([
(0, typedoc_1.BindOption)('symbolsWithOwnFile')
], MarkdownTheme.prototype, "symbolsWithOwnFile", void 0);
(0, typedoc_1.BindOption)('readme')
], MarkdownTheme.prototype, "readme", void 0);
__decorate([
(0, typedoc_1.BindOption)('flattenOutput')
], MarkdownTheme.prototype, "flattenOutput", void 0);
(0, typedoc_1.BindOption)('reflectionsWithOwnFile')
], MarkdownTheme.prototype, "reflectionsWithOwnFile", void 0);
exports.MarkdownTheme = MarkdownTheme;
{
"name": "typedoc-plugin-markdown",
"version": "4.0.0-next.5",
"version": "4.0.0-next.6",
"description": "A plugin for TypeDoc that enables TypeScript API documentation to be generated in Markdown.",

@@ -20,3 +20,3 @@ "main": "dist/index.js",

"docs:md-1": "typedoc --plugin typedoc-plugin-markdown --enableFrontmatter --longTitles --options ../../stubs/typedoc.json --out ./out/md/md-1",
"docs:md-2": "typedoc --plugin typedoc-plugin-markdown --groupBySymbols false --hideInPageTOC --typeDeclarationStyle \"list\" --readme \"none\" --symbolsWithOwnFile none --options ../../stubs/typedoc.json --out ./out/md/md-2",
"docs:md-2": "typedoc --plugin typedoc-plugin-markdown --groupByReflections false --hideInPageTOC --typeDeclarationStyle \"list\" --readme \"none\" --reflectionsWithOwnFile none --options ../../stubs/typedoc.json --out ./out/md/md-2",
"docs:html": "typedoc --options ../../stubs/typedoc.json --out ./out/html"

@@ -23,0 +23,0 @@ },

# typedoc-plugin-markdown
A plugin for [TypeDoc](https://github.com/TypeStrong/typedoc) that renders TypeScript API documentation as Markdown.
A plugin for [TypeDoc](https://typedoc.org) that renders TypeScript API documentation as Markdown.

@@ -10,7 +10,5 @@ [![npm](https://img.shields.io/npm/v/typedoc-plugin-markdown.svg)](https://www.npmjs.com/package/typedoc-plugin-markdown)

The plugin replaces the default HTML theme with a built-in Markdown theme and exposes some additional options.
By default, TypeDoc will render API documentation as a webpage, e.g. HTML files.
The plugin is useful if documentation is required to be included in project README files, Wikis and static site generators.
The plugin replaces the default HTML theme with a built-in Markdown theme and exposes some additional options. This is useful if documentation is required to be included in project README files, Wikis and static site generators.

@@ -23,6 +21,6 @@ ## Installation

> Please note the `next` version may contain breaking changes within the same semantic version without warning.
## Usage
Usage is the same as documented at [TypeDoc](https://typedoc.org/guides/installation/#command-line-interface)
```bash

@@ -34,3 +32,3 @@ typedoc --plugin typedoc-plugin-markdown

The following options can be used in addition to relevant [TypeDoc options](https://typedoc.org/guides/options/)
The following options can be used in addition to relevant [TypeDoc options](https://typedoc.org/options/)
(please note that TypeDoc options specific to the HTML theme will be ignored).

@@ -44,4 +42,8 @@

The file name of the entry document. Defaults to `README.md`.
- **`--symbolsWithOwnFile`**<br>
Determines which symbols should be written to their own file. Expected values [`none`, `all`] OR Array of [`class`, `interface`, `enum`, `function`, `var`, `type`] Defaults to `all` (all symbols exported to an individual file).
- **`--reflectionsWithOwnFile`**<br>
Determines which reflections should be written to its own file. Expected values [`none`, `all`] OR Array of [`class`, `interface`, `enum`, `function`, `var`, `type`] Defaults to `all` (all reflections exported to an individual file).
- **`--groupByReflections`**<br>
Groups reflections by type (if applicable). If set to `false` all reflections will group at the same level. Note this effects directory stucture and UI organisation heading structure. Defaults to `true`.
- **`--flattenOutputFiles`**<br>
Flatten output files without directories. Note this does not effect the UI.

@@ -60,4 +62,2 @@ ### UI options

Display full name including module paths in page titles. Defaults to `false`.
- **`--groupBySymbols`**<br>
Groups symbols by headings (if applicable) e.g Classes, Functions. If set to `false` all symbols will render on the same level. Defaults to `true`.

@@ -75,3 +75,3 @@ ### Frontmatter options

- **`--frontmatterNamingConvention`**<br>
Specify the naming convention of front matter variables. Expected values [`camelCase`, `snakeCase`,`kebabCase`]. Defaults to `snakeCase`.
Specify the naming convention of front matter variables. Expected values [`camelCase`, `snakeCase`,`kebabCase`]. Defaults to `camelCase`.

@@ -78,0 +78,0 @@ ### Utility options