This commit is contained in:
2025-05-19 06:39:52 +04:00
parent f9bdb22c67
commit 46898112f5
12 changed files with 374 additions and 406 deletions

View File

@@ -1,96 +0,0 @@
module docusaurus
import os
import strings
// clean removes temporary files and build artifacts from the site directory
pub fn (mut site DocSite) clean(args ...ErrorArgs) ! {
toclean := '
/node_modules
babel.config.js
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
bun.lockb
bun.lock
yarn.lock
build.sh
build_dev.sh
build-dev.sh
develop.sh
install.sh
package.json
package-lock.json
pnpm-lock.yaml
sidebars.ts
tsconfig.json
'
mut sb := strings.new_builder(200)
for line in toclean.split_into_lines() {
clean_line := line.trim_space()
if clean_line == '' || clean_line.starts_with('#') {
continue
}
// Remove leading slash if present to make path relative
path_to_clean := if clean_line.starts_with('/') {
clean_line[1..]
} else {
clean_line
}
full_path := os.join_path(site.path_src.path, path_to_clean)
// Handle glob patterns (files ending with *)
if path_to_clean.ends_with('*') {
base_pattern := path_to_clean#[..-1] // Remove the * at the end
base_dir := os.dir(full_path)
if os.exists(base_dir) {
files := os.ls(base_dir) or {
sb.writeln('Failed to list directory ${base_dir}: ${err}')
continue
}
for file in files {
if file.starts_with(base_pattern) {
file_path := os.join_path(base_dir, file)
os.rm(file_path) or { sb.writeln('Failed to remove ${file_path}: ${err}') }
}
}
}
continue
}
// Handle regular files and directories
if os.exists(full_path) {
if os.is_dir(full_path) {
os.rmdir_all(full_path) or {
sb.writeln('Failed to remove directory ${full_path}: ${err}')
}
} else {
os.rm(full_path) or { sb.writeln('Failed to remove file ${full_path}: ${err}') }
}
}
}
}

View File

@@ -1,207 +0,0 @@
module docusaurus
import freeflowuniverse.herolib.core.pathlib
import json
import os
// THE FOLLOWING STRUCTS CAN BE SERIALIZED IN
// main.json
// Main
// {
// "title": "Internet Geek",
// "tagline": "Internet Geek",
// "favicon": "img/favicon.png",
// "url": "https://friends.threefold.info",
// "url_home": "docs/",
// "baseUrl": "/kristof/",
// "image": "img/tf_graph.png",
// "metadata": {
// "description": "ThreeFold is laying the foundation for a geo aware Web 4, the next generation of the Internet.",
// "image": "https://threefold.info/kristof/img/tf_graph.png",
// "title": "ThreeFold Technology Vision"
// },
// "buildDest":"root@info.ourworld.tf:/root/hero/www/info",
// "buildDestDev":"root@info.ourworld.tf:/root/hero/www/infodev"
// }
//
// navbar.json
// Navbar:
// {
// "title": "Kristof = Chief Executive Geek",
// "items": [
// {
// "href": "https://threefold.info/kristof/",
// "label": "ThreeFold Technology",
// "position": "right"
// },
// {
// "href": "https://threefold.io",
// "label": "ThreeFold.io",
// "position": "right"
// }
// ]
// }
//
// footer.json
// Footer:
// {
// "style": "dark",
// "links": [
// {
// "title": "Docs",
// "items": [
// {
// "label": "Introduction",
// "to": "/docs"
// },
// {
// "label": "TFGrid V4 Docs",
// "href": "https://docs.threefold.io/"
// }
// ]
// },
// {
// "title": "Community",
// "items": [
// {
// "label": "Telegram",
// "href": "https://t.me/threefold"
// },
// {
// "label": "X",
// "href": "https://x.com/threefold_io"
// }
// ]
// },
// {
// "title": "Links",
// "items": [
// {
// "label": "ThreeFold.io",
// "href": "https://threefold.io"
// }
// ]
// }
// ]
// }
// Combined config structure
pub struct Config {
pub mut:
footer Footer
main Main
navbar Navbar
build_destinations []BuildDest
import_sources []ImportSource
ssh_connections []SSHConnection
}
// THE SUBELEMENTS
pub struct Main {
pub mut:
name string
title string = 'Docusaurus'
tagline string
favicon string = 'img/favicon.png'
url string = 'http://localhost'
url_home string
base_url string = '/' @[json: 'baseUrl']
image string = 'img/tf_graph.png' @[required]
metadata MainMetadata
build_dest []string @[json: 'buildDest']
build_dest_dev []string @[json: 'buildDestDev']
copyright string = 'someone'
}
// Footer config structures
pub struct FooterItem {
pub mut:
label string
to string
href string
}
pub struct FooterLink {
pub mut:
title string
items []FooterItem
}
pub struct Footer {
pub mut:
style string = 'dark'
links []FooterLink
}
// Main config structure
pub struct MainMetadata {
pub mut:
description string = 'Docusaurus'
image string = 'Docusaurus'
title string = 'Docusaurus'
}
// Navbar config structures
pub struct NavbarItem {
pub mut:
href string
label string
position string
}
pub struct Navbar {
pub mut:
title string
items []NavbarItem
}
pub struct SSHConnection {
pub mut:
name string = 'main'
login string = 'root' // e.g. 'root'
host string // e.g. info.ourworld.tf
port int = 21 // default is std ssh port
key string
key_path string // location of the key (private ssh key to be able to connect over ssh)
}
pub struct BuildDest {
pub mut:
ssh_name string = 'main'
path string // can be on the ssh root or direct path e.g. /root/hero/www/info
}
pub struct ImportSource {
pub mut:
url string // http git url can be to specific path
path string
dest string // location in the docs folder of the place where we will build docusaurus
replace map[string]string // will replace ${NAME} in the imported content
visible bool
}
// Export config as JSON files (main.json, navbar.json, footer.json)
pub fn (config Config) export_json(path string) ! {
// Ensure directory exists
os.mkdir_all(path)!
// Export main.json
os.write_file('${path}/main.json', json.encode_pretty(config.main))!
// Export navbar.json
os.write_file('${path}/navbar.json', json.encode_pretty(config.navbar))!
// Export footer.json
os.write_file('${path}/footer.json', json.encode_pretty(config.footer))!
}
pub fn (c Config) write(path string) ! {
mut footer_file := pathlib.get_file(path: '${path}/footer.json', create: true)!
footer_file.write(json.encode(c.footer))!
mut main_file := pathlib.get_file(path: '${path}/main.json', create: true)!
main_file.write(json.encode(c.main))!
mut navbar_file := pathlib.get_file(path: '${path}/navbar.json', create: true)!
navbar_file.write(json.encode(c.navbar))!
}

View File

@@ -1,189 +0,0 @@
module docusaurus
import freeflowuniverse.herolib.core.playbook { PlayBook }
import time
import os
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then playbook will be made out of it
heroscript_path string // path to a file containing heroscript
plbook ?PlayBook
reset bool
}
// Process the heroscript and return a filled Config object
pub fn play(args_ PlayArgs) !Config {
mut heroscript_text := args_.heroscript
// If heroscript_path is provided, read the script from the file
if args_.heroscript_path != '' && heroscript_text == '' {
heroscript_text = os.read_file(args_.heroscript_path) or {
return error('Failed to read heroscript from ${args_.heroscript_path}: ${err}')
}
}
// If no heroscript is provided, return an empty config
if heroscript_text == '' && args_.plbook == none {
return Config{}
}
// Create playbook from the heroscript text
mut plbook := if pb := args_.plbook {
pb
} else {
playbook.new(text: heroscript_text)!
}
mut config := Config{}
play_config(mut plbook, mut config)!
play_config_meta(mut plbook, mut config)!
play_ssh_connection(mut plbook, mut config)!
play_import_source(mut plbook, mut config)!
play_build_dest(mut plbook, mut config)!
play_navbar(mut plbook, mut config)!
play_footer(mut plbook, mut config)!
return config
}
fn play_config(mut plbook PlayBook, mut config Config) ! {
config_actions := plbook.find(filter: 'docusaurus.config')!
for action in config_actions {
mut p := action.params
// Get optional name parameter or use base_url as fallback
name := p.get_default('name', 'docusaurus-site')!
config.main = Main{
name: name
title: p.get_default('title', 'Documentation Site')!
tagline: p.get_default('tagline', 'Your awesome documentation')!
favicon: p.get_default('favicon', 'img/favicon.png')!
url: p.get_default('url', 'https://docs.example.com')!
url_home: p.get_default('url_home', 'docs/')!
base_url: p.get_default('base_url', '/')!
image: p.get_default('image', 'img/hero.png')!
copyright: p.get_default('copyright', '© ' + time.now().year.str() +
' Example Organization')!
}
}
}
fn play_config_meta(mut plbook PlayBook, mut config Config) ! {
meta_actions := plbook.find(filter: 'docusaurus.config_meta')!
for action in meta_actions {
mut p := action.params
config.main.metadata = MainMetadata{
description: p.get_default('description', 'Comprehensive documentation built with Docusaurus.')!
image: p.get_default('image', 'https://docs.example.com/img/social-card.png')!
title: p.get_default('title', 'Documentation | ' + config.main.title)!
}
}
}
fn play_ssh_connection(mut plbook PlayBook, mut config Config) ! {
ssh_actions := plbook.find(filter: 'docusaurus.ssh_connection')!
for action in ssh_actions {
mut p := action.params
mut ssh := SSHConnection{
name: p.get_default('name', 'main')!
host: p.get_default('host', 'info.ourworld.tf')!
port: p.get_int_default('port', 21)!
login: p.get_default('login', 'root')!
key_path: p.get_default('key_path', '')!
key: p.get_default('key', '')!
}
config.ssh_connections << ssh
}
}
fn play_import_source(mut plbook PlayBook, mut config Config) ! {
import_actions := plbook.find(filter: 'docusaurus.import_source')!
for action in import_actions {
mut p := action.params
mut replace_map := map[string]string{}
if replace_str := p.get_default('replace', '') {
parts := replace_str.split(',')
for part in parts {
kv := part.split(':')
if kv.len == 2 {
replace_map[kv[0].trim_space()] = kv[1].trim_space()
}
}
}
mut import_ := ImportSource{
url: p.get('url')!
path: p.get_default('path', '')!
dest: p.get_default('dest', '')!
replace: replace_map
}
config.import_sources << import_
}
}
fn play_build_dest(mut plbook PlayBook, mut config Config) ! {
build_actions := plbook.find(filter: 'docusaurus.build_dest')!
for action in build_actions {
mut p := action.params
mut build := BuildDest{
ssh_name: p.get_default('ssh_name', 'main')!
path: p.get_default('path', '')!
}
config.build_destinations << build
}
}
fn play_navbar(mut plbook PlayBook, mut config Config) ! {
navbar_actions := plbook.find(filter: 'docusaurus.navbar')!
for action in navbar_actions {
mut p := action.params
config.navbar.title = p.get_default('title', config.main.title)!
}
navbar_item_actions := plbook.find(filter: 'docusaurus.navbar_item')!
for action in navbar_item_actions {
mut p := action.params
mut item := NavbarItem{
label: p.get_default('label', 'Documentation')!
href: p.get_default('href', '/docs')!
position: p.get_default('position', 'right')!
}
config.navbar.items << item
}
}
fn play_footer(mut plbook PlayBook, mut config Config) ! {
footer_actions := plbook.find(filter: 'docusaurus.footer')!
for action in footer_actions {
mut p := action.params
config.footer.style = p.get_default('style', 'dark')!
}
footer_item_actions := plbook.find(filter: 'docusaurus.footer_item')!
mut links_map := map[string][]FooterItem{}
for action in footer_item_actions {
mut p := action.params
title := p.get_default('title', 'Docs')!
mut item := FooterItem{
label: p.get_default('label', 'Introduction')!
to: p.get_default('to', '/docs')!
href: p.get_default('href', '')!
}
if title !in links_map {
links_map[title] = []FooterItem{}
}
links_map[title] << item
}
// Convert map to footer links array
for title, items in links_map {
config.footer.links << FooterLink{
title: title
items: items
}
}
}

View File

@@ -78,101 +78,36 @@ fn (mut self DocusaurusFactory) generate_package_json() ! {
name = self.config.navbar.title.to_lower().replace(' ', '-')
}
// Create the JSON structure manually
package_json := '{
"name": "${name}",
"version": "0.0.1",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.1.0",
"@docusaurus/preset-classic": "^3.1.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.1.0",
"@docusaurus/tsconfig": "^3.1.0",
"@docusaurus/types": "^3.1.0",
"typescript": "^5.2.2"
},
"browserslist": {
"production": [">0.5%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
},
"engines": {
"node": ">=18.0"
}
}' // Write to file
// Load package.json from template
// The 'name' variable is defined in this function's scope and will be used by $tmpl.
content := $tmpl('templates/package.json')
mut package_file := pathlib.get_file(
path: os.join_path(self.path_build.path, 'package.json')
create: true
)!
package_file.write(package_json)!
package_file.write(content)!
}
// Generate tsconfig.json based on the configuration
fn (mut self DocusaurusFactory) generate_tsconfig_json() ! {
// Create tsconfig.json as a manual JSON string
tsconfig_json := '{
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": ".",
"resolveJsonModule": true
},
"include": ["src/**/*", "docusaurus.config.ts"]
}'
// Write to file
// Load tsconfig.json from template
content := $tmpl('templates/tsconfig.json')
mut tsconfig_file := pathlib.get_file(
path: os.join_path(self.path_build.path, 'tsconfig.json')
create: true
)!
tsconfig_file.write(tsconfig_json)!
tsconfig_file.write(content)!
}
// Generate sidebars.ts based on the configuration
fn (mut self DocusaurusFactory) generate_sidebars_ts() ! {
// Simple default sidebar structure
sidebar_content := "import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
};
export default sidebars;
"
// Load sidebars.ts from template
content := $tmpl('templates/sidebars.ts')
mut sidebars_file := pathlib.get_file(
path: os.join_path(self.path_build.path, 'sidebars.ts')
create: true
)!
sidebars_file.write(sidebar_content)!
sidebars_file.write(content)!
}
// Generate docusaurus.config.ts based on the configuration
@@ -192,135 +127,70 @@ fn (mut self DocusaurusFactory) generate_docusaurus_config_ts() ! {
'img/favicon.png'
}
// Define additional variables for the template
// Variables `title`, `tagline`, `favicon`, `url`, `base_url` are already defined above (lines 181-193)
projectName := self.config.main.name
navbarTitle := self.config.navbar.title
// Format navbar items from config
mut navbar_items := []string{}
mut navbar_items_list_temp := []string{}
for item in self.config.navbar.items {
navbar_items << "{
navbar_items_list_temp << "{
label: '${item.label}',
href: '${item.href}',
position: '${item.position}'
}"
}
navbar_items_str := navbar_items.join(',\n ')
navbarItems := navbar_items_list_temp.join(',\n ') // Matches ${navbarItems} in template
// Generate footer links if available
mut footer_links := []string{}
mut footer_links_list_temp := []string{}
for link in self.config.footer.links {
mut items := []string{}
mut items_temp := []string{}
for item in link.items {
mut item_str := '{'
if item.label != '' {
item_str += "label: '${item.label}', "
}
// Ensure only one of 'to', 'href', or 'html' is used
// Priority: href > to > html
if item.href != '' {
item_str += "href: '${item.href}'"
} else if item.to != '' {
item_str += "to: '${item.to}'"
} else {
// Default to linking to docs if nothing specified
item_str += "to: '/docs'"
item_str += "to: '/docs'" // Default link
}
item_str += '}'
items << item_str
items_temp << item_str
}
footer_links << "{
footer_links_list_temp << "{
title: '${link.title}',
items: [
${items.join(',\n ')}
${items_temp.join(',\n ')}
]
}"
}
footer_links_str := footer_links.join(',\n ')
footerLinks := footer_links_list_temp.join(',\n ') // Matches ${footerLinks} in template
// Year for copyright
year := time.now().year.str()
// Copyright string (variable `copyright` must be in scope for the template)
// `title` is defined at line 181, `year` is defined above.
copyright := if self.config.main.copyright != '' {
self.config.main.copyright
} else {
'Copyright © ${year} ${title}'
}
// Construct the full config file content
config_content := "import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
const config: Config = {
title: '${title}',
tagline: '${tagline}',
favicon: '${favicon}',
// Set the production url of your site here
url: '${url}',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '${base_url}',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: 'freeflowuniverse', // Usually your GitHub org/user name.
projectName: '${self.config.main.name}', // Usually your repo name.
onBrokenLinks: 'warn',
onBrokenMarkdownLinks: 'warn',
// Enable for i18n
// i18n: {
// defaultLocale: 'en',
// locales: ['en'],
// },
presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
},
theme: {
customCss: './src/css/custom.css',
},
} satisfies Preset.Options,
],
],
themeConfig: {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
navbar: {
title: '${self.config.navbar.title}',
items: [
${navbar_items_str}
],
},
footer: {
style: '${self.config.footer.style}',
links: [
${footer_links_str}
],
copyright: '${copyright}',
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
};
export default config;
"
// Load docusaurus.config.ts from template
// All required variables (title, tagline, favicon, url, base_url,
// projectName, navbarTitle, navbarItems, footerLinks, copyright)
// are in scope for $tmpl.
content := $tmpl('templates/docusaurus.config.ts')
mut config_file := pathlib.get_file(
path: os.join_path(self.path_build.path, 'docusaurus.config.ts')
create: true
)!
config_file.write(config_content)!
config_file.write(content)!
}

View File

@@ -1,14 +1,21 @@
build_dev_publish.sh
build_publish.sh
build.sh
develop.sh
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
static
src
package.json
package-lock.json
cfg/footer.json
cfg/main.json
cfg/navbar.json
cfg/sidebar.json
cfg/ssh.json
.pnp
.pnp.js
# build output
build
.docusaurus
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,23 +0,0 @@
#!/bin/bash
set -e
script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)"
cd "??{script_dir}"
echo "Docs directory: ??script_dir"
cd "${mydir}"
export PATH=/tmp/docusaurus_build/node_modules/.bin:??{HOME}/.bun/bin/:??PATH
rm -rf ${site.path_build.path}/build/
${profile_include}
bun docusaurus build
@for dest in cfg.main.build_dest_dev
rsync -rv --delete ${site.path_build.path}/build/ ${dest.trim_right("/")}/
@end

View File

@@ -0,0 +1,91 @@
import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
const config: Config = {
title: '${title}',
tagline: '${tagline}',
favicon: '${favicon}',
// Set the production url of your site here
url: '${url}',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '${base_url}',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: 'freeflowuniverse', // Usually your GitHub org/user name.
projectName: '${projectName}', // Usually your repo name.
onBrokenLinks: 'warn',
onBrokenMarkdownLinks: 'warn',
// Enable for i18n
// i18n: {
// defaultLocale: 'en',
// locales: ['en'],
// },
presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
},
theme: {
customCss: './src/css/custom.css',
},
} satisfies Preset.Options,
],
],
themeConfig: {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
navbar: {
title: '${navbarTitle}',
items: [
@for item in navbar_items {
{
label: '${item.label}',
href: '${item.href}',
position: '${item.position}'
},
}
],
},
footer: {
style: '${footerStyle}',
links: [
@for link_group in footer_links {
{
title: '${link_group.title}',
items: [
@for item in link_group.items {
{
label: '${item.label}',
@if item.href != '' {
href: '${item.href}'
} @else if item.to != '' {
to: '${item.to}'
} @else {
to: '/docs'
}
},
}
]
},
}
],
copyright: '${copyright}',
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
};
export default config;

View File

@@ -0,0 +1,39 @@
{
"name": "${name}",
"version": "0.0.1",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.1.0",
"@docusaurus/preset-classic": "^3.1.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.1.0",
"@docusaurus/tsconfig": "^3.1.0",
"@docusaurus/types": "^3.1.0",
"typescript": "^5.2.2"
},
"browserslist": {
"production": [">0.5%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
},
"engines": {
"node": ">=18.0"
}
}

View File

@@ -0,0 +1,18 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
};
export default sidebars;

View File

@@ -0,0 +1,8 @@
{
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": ".",
"resolveJsonModule": true
},
"include": ["src/**/*", "docusaurus.config.ts"]
}