Feat: Rebuild Configuration Generator (#1713)

* remove old config generator

* initial config generator

* Conditionally render properties

* change to module type

* set up options

* more stuff

* Add notes

* update ts target

* Big chunk o' changes

* temporarily disable link validation

* Cap headings to 6

* Fix frontmatter spacing

* rework

* commits

* even more, yay

* Fix validator formatting

* Remove config 2

* type formatting styles

* cleanup

* Enable broken link detection

* Cleanup tsconfig

* remove type for object

* Redo object properties heading

* Update array formatting

* update package metadata

* Add note for contributors

---------

Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com>
This commit is contained in:
Lorenzo Lewis
2023-12-19 14:05:50 +00:00
committed by GitHub
parent 2c834d0256
commit d4dcb120ee
9 changed files with 1088 additions and 1048 deletions

View File

@@ -3,12 +3,9 @@ import starlight from '@astrojs/starlight';
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import locales from './locales.json';
import configGenerator from './src/plugins/configGenerator';
import starlightLinksValidator from 'starlight-links-validator';
import starlightBlog from 'starlight-blog';
await configGenerator();
const authors = {
nothingismagick: {
name: 'Daniel Thompson-Yvetot',

View File

@@ -12,9 +12,10 @@
"dev": "astro dev",
"format": "prettier -w --cache --plugin prettier-plugin-astro .",
"build:reference": "pnpm --filter js-api-generator run build",
"build:config": "pnpm --filter config-generator run build",
"build:astro": "astro build",
"build:i18n": "pnpm --filter docs-i18n-tracker run build",
"build": "pnpm dev:setup && pnpm build:reference && pnpm build:astro && pnpm build:i18n",
"build": "pnpm dev:setup && pnpm build:reference && pnpm build:config && pnpm build:astro && pnpm build:i18n",
"preview": "astro preview"
},
"dependencies": {

View File

@@ -0,0 +1,496 @@
import { JSONSchema7, JSONSchema7Definition, JSONSchema7TypeName } from 'json-schema';
import { existsSync, writeFileSync } from 'node:fs';
import { slug } from 'github-slugger';
const schemaFile = '../tauri/core/tauri-config-schema/schema.json';
const outputFile = '../../src/content/docs/2/reference/config.md';
if (!existsSync(schemaFile)) {
throw Error('Could not find the Tauri config schema. Is the Tauri submodule initialized?');
}
let schema: JSONSchema7 = (await import(schemaFile)).default;
const output = ['---\n# NOTE: This file is auto-generated in packages/config-generator/build.ts\n# For corrections please edit https://github.com/tauri-apps/tauri/blob/dev/core/tauri-utils/src/config.rs directly\n\ntitle: Configuration\n---'];
output.push(
...buildSchemaDefinition(schema, {
headingLevel: 2,
renderTitle: false,
})
);
writeFileSync(outputFile, output.join('\n\n'));
interface Options {
headingLevel: number;
renderTitle: boolean;
leadWithType: boolean;
}
function buildSchemaDefinition(
schema: JSONSchema7Definition,
passedOptions: Partial<Options> = {}
): string[] {
// Note: $id, $schema, and $comment are explicitly not rendered
// Assign default values for any missing options
const opts = Object.assign(
{
headingLevel: 1,
renderTitle: true,
leadWithType: false,
},
passedOptions
);
if (typeof schema === 'boolean') {
return [`\`${schema}\``];
}
const out: string[] = [];
out.push(...buildType(schema, opts));
out.push(...buildExtendedItems(schema, opts));
out.push(...buildConditionalSubschemas(schema, opts));
out.push(...buildBooleanSubschemas(schema, opts));
out.push(...buildMetadata(schema, opts));
out.push(...buildProperties(schema, opts));
out.push(...buildExtendedMetadata(schema, opts));
out.push(...buildDefinitions(schema, opts));
return out;
}
/**
* Builds: title, readOnly, writeOnly, description
*
* Also see: buildExtendedMetadata()
*/
function buildMetadata(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
opts.renderTitle &&
schema.title &&
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} ${schema.title}`);
if (schema.readOnly || schema.writeOnly) {
const line = [];
schema.readOnly && line.push('Read only');
schema.writeOnly && line.push('Write only');
out.push(line.join(' & '));
}
schema.description &&
out.push(
schema.description
// Set headings to appropriate level
.replaceAll(/#{1,6}(?=.+[\n\\n])/g, '#'.repeat(Math.min(opts.headingLevel + 1, 6)))
// Fix improperly formatted heading links
.replaceAll(/#{1,6}(?=[^\s#])/g, '#')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
// Fix for link at https://github.com/tauri-apps/tauri/blob/713f84db2b5bf17e4217053a229f9c11cbb22c74/core/tauri-config-schema/schema.json#L1863-L1864
.replace('#SecurityConfig.devCsp', '#securityconfig')
);
return out;
}
/**
* Builds: $ref, type, enum, const, items
* Number validation: multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum
* String validation: maxLength, minLength, pattern
* Array validation: maxItems, minItems, uniqueItems
* Object validation: maxProperties, minProperties
* Semantic validation: format
* Non-JSON data validation: contentMediaType, contentEncoding
*/
function buildType(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
let line: string = '';
if (schema.type) {
if (Array.isArray(schema.type)) {
line += schema.type.map((value) => buildTypeName(value, schema, opts)).join(' | ');
} else {
line += buildTypeName(schema.type, schema, opts);
}
}
if (schema.$ref) {
const reference = schema.$ref.split('/').pop();
if (!reference) {
throw Error(`Invalid reference: ${schema.$ref}`);
}
line += `[\`${reference}\`](#${slug(reference)})`;
}
if (schema.const) {
switch (typeof schema.const) {
case 'string':
line += `\`"${schema.const}"\``;
break;
default:
line += `\`${schema.const}\``;
}
}
if (schema.enum) {
const enumValues: string[] = [];
schema.enum.forEach((value) => {
switch (typeof value) {
case 'string':
enumValues.push(`\`"${value}"\``);
break;
default:
enumValues.push(`\`${value}\``);
}
});
line += enumValues.join(' | ');
}
const validation = [];
// Number validation
schema.multipleOf && validation.push(`multiple of \`${schema.multipleOf}\``);
schema.maximum && validation.push(`maximum of \`${schema.maximum}\``);
schema.exclusiveMaximum && validation.push(`exclusive maximum of \`${schema.exclusiveMaximum}\``);
schema.minimum && validation.push(`minimum of \`${schema.minimum}\``);
schema.exclusiveMinimum && validation.push(`exclusive minimum of \`${schema.exclusiveMinimum}\``);
// String validation
schema.maxLength && validation.push(`maximum length of \`${schema.maxLength}\``);
schema.minLength && validation.push(`minimum length of \`${schema.minLength}\``);
schema.pattern && validation.push(`pattern of \`${schema.pattern}\``);
// Array validation
schema.maxItems && validation.push(`maximum of \`${schema.maxItems}\` items`);
schema.minItems && validation.push(`minimum of \`${schema.minItems}\` items`);
schema.uniqueItems && validation.push(`each item must be unique`);
// Object validation
schema.maxProperties && validation.push(`maximum of \`${schema.maxProperties}\` properties`);
schema.minProperties && validation.push(`minimum of \`${schema.minProperties}\` properties`);
// Semantic validation
schema.format && validation.push(`formatted as \`${schema.format}\``);
// Non-JSON data validation
schema.contentMediaType &&
validation.push(`content media type of \`${schema.contentMediaType}\``);
schema.contentEncoding && validation.push(`content encoding of \`${schema.contentEncoding}\``);
if (validation.length > 0) {
line += ' ' + validation.join(', ');
}
return [line];
function buildTypeName(
typeName: JSONSchema7TypeName,
parentSchema: JSONSchema7Definition,
opts: Options
): string {
// Rendering logic for enums and consts are handled separately
if (typeof parentSchema === 'object' && (parentSchema.enum || parentSchema.const)) {
return '';
}
let line = '';
switch (typeName) {
case 'object':
break;
case 'array':
if (typeof parentSchema !== 'object' || !parentSchema.items) {
throw Error('Invalid array');
}
if (Array.isArray(parentSchema.items)) {
line += parentSchema.items
.map((value) => {
const definition = buildSchemaDefinition(value, opts);
if (definition.length > 1) {
// Format additional information to be in parenthesis
const [first, ...rest] = definition;
return [first, ' (', rest.join(', '), ')'];
} else {
return definition.join();
}
})
.join(' | ');
} else {
line += buildSchemaDefinition(parentSchema.items, opts);
}
line += '[]';
break;
default:
line += '`' + typeName + '`';
}
return line;
}
}
/**
* Builds: additionalItems, contains
*/
function buildExtendedItems(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (schema.additionalItems) {
throw Error('Not implemented');
}
if (schema.contains) {
throw Error('Not implemented');
}
const out: string[] = [];
schema.additionalItems && out.push(`additionalItems: ${JSON.stringify(schema.additionalItems)}`);
schema.contains && out.push(`contains: ${JSON.stringify(schema.contains)}`);
return out;
}
/**
* Builds: required, properties, patternProperties, additionalProperties, dependencies, propertyNames
*/
function buildProperties(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
if (schema.properties) {
out.push(`**Object Properties**:`);
const properties = Object.entries(schema.properties)
.filter(([key]) => key !== '$schema')
.sort(([a], [b]) => a.localeCompare(b));
out.push(
properties
.map(
([key]) =>
`- ${key} ${schema.required && schema.required.includes(key) ? '(required)' : ''}`
)
.join('\n')
);
properties.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} ${key}`);
out.push(...buildSchemaDefinition(value, { ...opts, headingLevel: opts.headingLevel + 1 }));
});
}
schema.additionalProperties &&
out.push(
`**Allows additional properties**: ${buildSchemaDefinition(
schema.additionalProperties,
opts
)}`
);
if (schema.patternProperties || schema.dependencies || schema.propertyNames) {
throw Error('Not implemented');
}
schema.patternProperties &&
out.push(`patternProperties: ${JSON.stringify(schema.patternProperties)}`);
schema.dependencies && out.push(`dependencies: ${JSON.stringify(schema.dependencies)}`);
schema.propertyNames && out.push(`propertyNames: ${JSON.stringify(schema.propertyNames)}`);
return out;
}
/**
* Builds: if, then, else
*/
function buildConditionalSubschemas(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
if (schema.if || schema.then || schema.else) {
throw Error('Not implemented');
}
schema.if && out.push(`if: ${JSON.stringify(schema.if)}`);
schema.then && out.push(`then: ${JSON.stringify(schema.then)}`);
schema.else && out.push(`else: ${JSON.stringify(schema.else)}`);
return out;
}
/**
* Builds: allOf, anyOf, oneOf, not
*/
function buildBooleanSubschemas(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
if (schema.allOf) {
out.push(...buildXOf('All', schema.allOf, opts));
}
if (schema.anyOf) {
out.push(...buildXOf('Any', schema.anyOf, opts));
}
if (schema.oneOf) {
out.push(...buildXOf('One', schema.oneOf, opts));
}
if (schema.not) {
throw Error('Not implemented');
}
schema.not && out.push(`not: ${JSON.stringify(schema.not)}`);
return out;
function buildXOf(
label: 'One' | 'Any' | 'All',
schemas: JSONSchema7Definition[],
opts: Options
): string[] {
const definitions = schemas.map((value) =>
buildSchemaDefinition(value, { ...opts, leadWithType: true })
);
if (definitions.every((definition) => definition.length == 1)) {
// Short definition, can be rendered on a single line
return [definitions.join(' | ')];
}
const out: string[] = [];
if (definitions.length > 1) {
// Render as a list
out.push(`**${label} of the following**:`);
const list: string[] = [];
const additionalDefinitions: string[][] = [];
definitions.forEach((definition) => {
if (definition[0].startsWith('`object`')) {
// Is an object, need to render subdefinitions
const [first] = definition;
list.push(`- ${first}: Subdefinition ${additionalDefinitions.length + 1}`);
additionalDefinitions.push(definition);
} else {
// Render inline in list
list.push(`- ${definition.map((value) => value.replaceAll('\n', ' ')).join(' ')}`);
}
});
out.push(list.join('\n'));
if (additionalDefinitions.length > 0) {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel))} Subdefinitions`);
additionalDefinitions.forEach((definition, index) => {
// Render heading
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} Subdefinition ${index + 1}`);
// Render definition, giving additional heading indention as needed
definition.forEach((line) => {
out.push(
line.replaceAll(/#{1,6}\s(?=.+)/g, '#'.repeat(Math.min(opts.headingLevel + 2, 6)))
);
});
});
}
} else {
// Render as a block
// Mapping might need some fixing...
out.push(...definitions.map((definition) => definition.join()));
}
return out;
}
}
/**
* Builds: default, examples
*
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-8.2.4
* https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-9
*/
function buildExtendedMetadata(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
if (schema.default) {
if (typeof schema.default === 'string') {
out.push(`**Default**: \`"${schema.default}"\``);
} else if (typeof schema.default !== 'object') {
// Render on single line
out.push(`**Default**: \`${schema.default}\``);
} else if (Object.keys(schema.default).length == 0) {
// Empty object, render on a single line
out.push(`**Default**: \`${JSON.stringify(schema.default)}\``);
} else {
// Object with properties, render in code block
out.push(
`\n\`\`\`json title='Default'\n${JSON.stringify(schema.default, null, '\t')}\n\`\`\`\n`
);
}
}
if (schema.examples) {
throw Error('Examples not implemented');
}
return out;
}
/**
* Builds: $defs, definitions
*
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-8.2.4
* https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-9
*/
function buildDefinitions(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
// Combine definitions together
// `definitions` was renamed to `$defs` in Draft 7 but still allowed in either place
const definitions = { ...schema.$defs, ...schema.definitions };
if (Object.keys(definitions).length > 0) {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} Definitions`);
Object.entries(definitions)
.sort(([a], [b]) => a.localeCompare(b))
.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6) + 1)} ${key}`);
out.push(
...buildSchemaDefinition(value, {
...opts,
headingLevel: Math.min(opts.headingLevel, 6) + 2,
})
);
});
}
return out;
}

View File

@@ -0,0 +1,21 @@
{
"name": "config-generator",
"version": "1.0.0",
"private": "true",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"build": "tsm ./build.ts"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.15",
"@types/node": "^20.10.0",
"github-slugger": "^2.0.0",
"tsm": "^2.3.0",
"typescript": "^5.3.2"
}
}

View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2021",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}

View File

@@ -8,7 +8,8 @@
},
"keywords": [],
"author": "",
"license": "ISC",
"license": "MIT",
"private": "true",
"dependencies": {
"github-slugger": "^2.0.0",
"tsm": "^2.3.0",

1131
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
packages:
- 'packages/i18n-tracker'
- 'packages/js-api-generator'
- 'packages/config-generator'
- 'packages/tauri-typedoc-theme'

View File

@@ -1,470 +0,0 @@
import fs from 'node:fs';
export default async function configGenerator() {
if (fs.existsSync('packages/tauri/tooling/api/node_modules')) {
const schemaPath = 'packages/tauri/core/tauri-config-schema/schema.json';
const schemaString = fs
.readFileSync(schemaPath)
.toString()
// Fixes any angle brackets that aren't escaped propertly
.replaceAll('(?<!\\)<', '<')
.replaceAll('(?!\\)>', '>');
const schema = JSON.parse(schemaString);
const targetPath = 'src/content/docs/2/reference/config.md';
const nullMarkdown = '_null_';
const header = `---
title: Tauri Configuration
---
`;
const builtDefinitions = [];
function buildObject(key, value, headerLevel) {
let out = [];
headerLevel = Math.min(headerLevel, 6);
var headerTitle = value.title ? 'Configuration' : key;
var header = `${'#'.repeat(headerLevel)} ${headerTitle}\n`;
if (headerLevel === 1) {
headerLevel = 2;
}
out.push(header);
out.push(`${descriptionConstructor(value.description)}\n`);
out.push(`${longFormTypeConstructor(key, value, headerLevel)}\n`);
out = out.concat(buildProperties(headerTitle, value, headerLevel));
out = out.concat(inspectRef(value, headerLevel + 1));
return out;
}
function buildDef(name, headerLevel) {
const def = name.replace('#/definitions/', '');
if (!builtDefinitions.includes(def)) {
builtDefinitions.push(def);
const obj = schema.definitions[def];
return buildObject(def, obj, headerLevel);
}
return [];
}
function inspectRef(object, headerLevel) {
let out = [];
if (object.$ref) {
out = out.concat(buildDef(object.$ref, headerLevel));
}
if (object.additionalProperties && object.additionalProperties.$ref) {
out = out.concat(buildDef(object.additionalProperties.$ref, headerLevel));
}
if (object.items && object.items.$ref) {
out = out.concat(buildDef(object.items.$ref, headerLevel));
}
for (const opt of object.allOf || []) {
out = out.concat(inspectRef(opt, headerLevel));
}
for (const opt of object.anyOf || []) {
out = out.concat(inspectRef(opt, headerLevel));
}
return out;
}
function buildProperties(parentName, object, headerLevel) {
const out = [];
if (!object.properties) return out;
const required = object.required || [];
// Build table header
out.push('| Name | Type | Default | Description |');
out.push('| ---- | ---- | ------- | ----------- |');
let definitions = [];
// Populate table
Object.entries(object.properties).forEach(([key, value]) => {
if (key == '$schema') return;
let propertyType = typeConstructor(value, true);
let propertyDefault = defaultConstructor(value);
if (required.includes(key)) {
propertyType += ' (required)';
if (propertyDefault === nullMarkdown) {
propertyDefault = '';
}
}
const url = `${parentName.toLowerCase()}.${key.toLowerCase()}`;
const name = `<div className="anchor-with-padding" id="${url}">\`${key}\`<a class="hash-link" href="#${url}"></a></div>`;
out.push(
`| ${name} | ${propertyType} | ${propertyDefault} | ${descriptionConstructor(
value.description,
true
)} |`
);
definitions = definitions.concat(inspectRef(value, headerLevel + 1));
});
out.push('\n');
return out.concat(definitions);
}
function descriptionConstructor(description, fixNewlines = false) {
if (!description) return;
// Remove links to current page
description = description.replaceAll(
/\n\nSee more: https:\/\/tauri\.app\/v[0-9]\/api\/config.*$/g,
''
);
// fix Rust doc style links
description = description.replaceAll(/\[`Self::(\S+)`\]/g, '`$1`');
// Fix bullet points not being on a newline
description = description.replaceAll(' - ', '\n- ');
// Parse any json code blocks
if (description.includes('```json ')) {
let newDescription = '';
const s = description.split('```');
for (const text of s) {
if (text.startsWith('json')) {
const description = text.match(/([^{]+)/)[0];
const json = JSON.stringify(JSON.parse(text.replace(description, '')), null, 2);
newDescription += `${description}\n${json}\n`;
} else {
newDescription += text + '```';
}
}
description = newDescription;
}
const referenceStyleLinksRegex = /(\[[A-Za-z0-9 ]+\]): (.+)/g;
const referenceStyleLinksMatches = referenceStyleLinksRegex.exec(description);
if (referenceStyleLinksMatches) {
let link = referenceStyleLinksMatches[2];
// strip `<` and `>` from `<$url>`
if (link.startsWith('<')) {
link = link.substring(1, link.length - 1);
}
description = description
.replace(referenceStyleLinksMatches[0], '')
.replace(referenceStyleLinksMatches[1], `${referenceStyleLinksMatches[1]}(${link})`)
.trim();
}
// Fix any embedded new lines
if (fixNewlines) {
description = description.replaceAll('\n', '<br />');
}
const markdownLinkRegex = /\[([^\[]+)\]\((.*)\)/gm;
const markdownLinkMatches = markdownLinkRegex.exec(description);
if (markdownLinkMatches) {
const url = markdownLinkMatches[2];
if (!url.startsWith('http')) {
description = description.replace(url, url.toLowerCase().replaceAll('_', ''));
}
}
return description;
}
function typeConstructor(object, describeObject = false) {
const canBeNull =
(object.type && object.type.includes('null')) ||
(object.anyOf && object.anyOf.some((item) => item.type === 'null'));
if (object.$ref) {
return refLinkConstructor(object.$ref, canBeNull);
}
if (object.additionalProperties && object.additionalProperties.$ref) {
return refLinkConstructor(object.additionalProperties.$ref, canBeNull);
}
if (object.items && object.items.$ref) {
return refLinkConstructor(object.items.$ref, canBeNull);
}
if (object.anyOf) {
// Removes any null values
const items = object.anyOf.filter((item) => !(item.type && item.type == 'null'));
if (canBeNull && items.length == 1) {
return `${items.map((t) => typeConstructor(t, describeObject))}?`;
}
return items.map((t) => typeConstructor(t, describeObject)).join(' \\| ');
}
if (object.allOf) {
return refLinkConstructor(object.allOf[0].$ref);
}
if (object.oneOf) {
return object.oneOf.map((t) => typeConstructor(t, describeObject)).join(' | ');
}
const m = describeObject ? '' : '`';
if (object.type) {
var typeString = '';
// See what the type is
switch (typeof object.type) {
case 'string':
// See if referencing a different type
switch (object.type) {
case 'string':
typeString = object.enum
? object.enum.map((e) => `"${e}"`).join(', ')
: `${m}${object.type}${m}`;
break;
case 'number':
case 'integer':
case 'boolean':
typeString = `${m}${object.type}${m}`;
break;
case 'object':
if (describeObject && object.properties) {
typeString = `${m}{`;
const len = Object.keys(object.properties).length;
let i = 0;
for (const prop in object.properties) {
typeString += ` "${prop}": ${typeConstructor(
object.properties[prop],
describeObject
)}`;
i++;
if (i < len) typeString += ',';
}
typeString += ` }${m}`;
} else {
typeString = `${m}${object.type}${m}`;
}
break;
case 'array':
if (object.items) {
if (describeObject) {
typeString = `${typeConstructor(object.items, describeObject)}[]`;
} else {
const type = typeConstructor(object.items, true);
const hasLink = type.includes('(#');
typeString = hasLink
? type.replace(/\[`(.*)`\]/, '[`$1[]`]')
: `${m}${type}[]${m}`;
}
break;
}
default:
break;
}
break;
case 'undefined':
typeString = nullMarkdown;
break;
case 'object':
if (Array.isArray(object.type)) {
// Check if it should just be an optional value
if (object.type.length == 2 && object.type.includes('null')) {
typeString = `${m}${object.type.filter((item) => item != 'null')}${m}?`;
break;
}
}
default:
break;
}
var additionalProperties = [];
if (object.format) {
additionalProperties.push(`format: \`${object.format}\``);
}
if (object.multipleOf) {
additionalProperties.push(`multiple of: \`${object.multipleOf}\``);
}
if (object.minimum) {
additionalProperties.push(`minimum: \`${object.minimum}\``);
}
if (object.exclusiveMinimum) {
additionalProperties.push(`exclusive minimum: \`${object.exclusiveMinimum}\``);
}
if (object.maximum) {
additionalProperties.push(`maximum: \`${object.maximum}\``);
}
if (object.exclusiveMaximum) {
additionalProperties.push(`exclusive maximum: \`${object.exclusiveMaximum}\``);
}
if (typeString != '') {
if (additionalProperties.length > 0) {
const props = `_(${additionalProperties.join(', ')})_`;
return `${typeString} ${props}`;
}
return typeString;
}
}
if (object.enum) {
return `${m}${object.enum.map((e) => `"${e}"`).join(', ')}${m}`;
}
if (Array.isArray(object)) {
if (describeObject) {
const type = [];
for (const obj of object) {
type.push(typeConstructor(obj));
}
if (type.every((t) => t === type[0])) {
return `${m}${type[0]}${m}`;
}
return `${m}[${type.join(', ')}]${m}`;
} else {
return `${m}array${m}`;
}
}
console.log('A type was not able to be parsed:', object);
return JSON.stringify(object);
}
/** prepares a description to be added to a markdown bullet point list */
function listDescription(description) {
return description.replace('\n\n', '\n\n\t');
}
function longFormTypeConstructor(key, object, headerLevel) {
if (object.enum) {
var buffer = [];
buffer.push(`Can be any of the following \`${object.type}\` values:`);
object.enum.forEach((item) => {
buffer.push(`- ${item}`);
});
return buffer.join('\n');
}
if (object.anyOf) {
var buffer = [];
buffer.push('Can be any of the following types:\n');
object.anyOf.forEach((item) => {
var description = ':';
if (item.description) {
description = `: ${descriptionConstructor(item.description)}`;
}
const hasProperties = 'properties' in item;
let typeDef = typeConstructor(item, hasProperties);
if (hasProperties) {
typeDef = '`' + typeDef + '`';
}
buffer.push(`- ${typeDef}${listDescription(description)}`);
if (hasProperties) {
buffer.push('\n\t');
buffer.push(
buildProperties(key, item, headerLevel)
.map((line) => `\t${line}`)
.join('\n')
);
}
});
return buffer.join(`\n`);
}
if (object.oneOf) {
var buffer = [];
buffer.push('Can be any **ONE** of the following types:\n');
object.oneOf.forEach((item) => {
var description = ':';
if (item.description) {
description = `: ${descriptionConstructor(item.description)}`;
}
const hasProperties = 'properties' in item;
let typeDef = typeConstructor(item, hasProperties);
if (hasProperties) {
typeDef = '`' + typeDef + '`';
}
buffer.push(`- ${typeDef}${listDescription(description)}`);
if ('properties' in item) {
buffer.push('\n\t');
buffer.push(
buildProperties(key, item, headerLevel)
.map((line) => `\t${line}`)
.join('\n')
);
}
});
return buffer.join(`\n`);
}
return `Type: ${typeConstructor(object)}`;
}
function defaultConstructor(object) {
switch (typeof object.default) {
case 'boolean':
case 'number':
return `\`${object.default}\``;
case 'object':
// Check if empty array
if (Array.isArray(object.default) && object.default.length == 0) {
return '[]';
}
default:
}
if (object.$ref) {
console.error('Found $ref default:', object.$ref);
}
if (object.anyOf) {
const link = object.anyOf[0].$ref.replace('#/definitions/', '').toLowerCase();
return `[view](#${link})`;
}
if (object.allOf) {
const link = object.allOf[0].$ref.replace('#/definitions/', '').toLowerCase();
return `[view](#${link})`;
}
if (object.oneOf) {
console.error('Found oneOf default:', object.oneOf);
}
return nullMarkdown;
}
function refLinkConstructor(string, nullable = false) {
const name = string.replace('#/definitions/', '');
return `[\`${name}\`](#${name.toLowerCase()})${nullable ? '?' : ''}`;
}
const config = buildObject(null, schema, 1).join('\n');
fs.writeFileSync(targetPath, `${header}${config}`);
} else {
console.log(
'Tauri submodule is not initialized, respective core config routes will not be rendered.'
);
}
}