This commit is contained in:
2025-05-20 07:00:00 +04:00
parent 8ffb8c8caf
commit fd8b8c8f42
20 changed files with 629 additions and 760 deletions

View File

@@ -1,63 +1,6 @@
# Redisclient
Getting started:
```v
// Connect to Redis (recommended way)
import freeflowuniverse.herolib.core.base
mut context := base.context()!
mut redis := context.redis()!
// String commands
redis.set('mykey', 'hello')!
println(redis.get('mykey')!) // Output: hello
redis.del('mykey')!
// Hash commands
redis.hset('myhash', 'field1', 'value1')!
println(redis.hget('myhash', 'field1')!) // Output: value1
println(redis.hgetall('myhash')!) // Output: {'field1': 'value1', 'field2': 'value2'}
redis.hdel('myhash', 'field1')!
// List commands
redis.lpush('mylist', 'item1')!
redis.rpush('mylist', 'item2')!
println(redis.lrange('mylist', 0, -1)!) // Output: ['item1', 'item2']
println(redis.lpop('mylist')!) // Output: item1
println(redis.rpop('mylist')!) // Output: item2
// Set commands
redis.sadd('myset', ['member1', 'member2', 'member3'])!
println(redis.smismember('myset', ['member1', 'member4'])!) // Output: [1, 0]
// Key commands
redis.set('key1', 'value1')!
redis.set('key2', 'value2')!
println(redis.keys('*')!) // Output: ['key1', 'key2'] (order may vary)
redis.expire('key1', 10)! // Set expiry to 10 seconds
// Increment/Decrement commands
redis.set('counter', '10')!
println(redis.incr('counter')!) // Output: 11
println(redis.decrby('counter', 5)!) // Output: 6
// Append command
redis.set('mytext', 'hello')!
println(redis.append('mytext', ' world')!) // Output: 11 (length of new string)
println(redis.get('mytext')!) // Output: hello world
// Type command
println(redis.type_of('mykey')!) // Output: string (or none if key doesn't exist)
// Flush commands (use with caution!)
// redis.flushdb()! // Flushes the current database
// redis.flushall()! // Flushes all databases
```
## archive
### non recommended example to connect to local redis on 127.0.0.1:6379
## basic example to connect to local redis on 127.0.0.1:6379
```v
@@ -73,3 +16,4 @@ if r != 'some data' {
```
> redis commands can be found on https://redis.io/commands/

View File

@@ -2,6 +2,7 @@
The TextTools module provides a comprehensive set of utilities for text manipulation and processing in V. It includes functions for cleaning, parsing, formatting, and transforming text in various ways.
## Features
### Array Operations
- `to_array(r string) []string` - Converts a comma or newline separated list to an array of strings
@@ -36,12 +37,6 @@ The TextTools module provides a comprehensive set of utilities for text manipula
- Handles comments, code blocks, and preserves formatting
### Name/Path Processing
```v
import freeflowuniverse.herolib.core.texttools
texttools.name_fix(sometext)
```
- `name_fix(name string) string` - Normalizes filenames and paths
- `name_fix_keepspace(name string) !string` - Like name_fix but preserves spaces
- `name_fix_no_ext(name_ string) string` - Removes file extension
@@ -126,3 +121,26 @@ ver := texttools.version("v1.4.36")
// Result: 1004036
```
## Error Handling
Many functions in the module return a Result type (indicated by `!` in the function signature). These functions can return errors that should be handled appropriately:
```v
// Example of error handling
name := texttools.name_fix_keepspace("some@name") or {
println("Error: ${err}")
return
}
```
## Best Practices
1. Always use appropriate error handling for functions that return Results
2. Consider using `dedent()` before processing multiline text to ensure consistent formatting
3. When working with filenames, use the appropriate name_fix variant based on your needs
4. For command line parsing, be aware of quote handling and escaping rules
5. When using tokenization, consider the context and whether smart splitting is needed
## Contributing
The TextTools module is part of the heroLib project. Contributions are welcome through pull requests.

View File

@@ -1,95 +0,0 @@
module docusaurus
import os
import strings
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,214 +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'
to_import []MyImport @[json: 'import']
}
// 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'
}
pub struct MyImport {
pub mut:
url string
dest string
visible bool
replace map[string]string
}
// 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
}
// 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,59 +0,0 @@
module docusaurus
import json
import os
// load_config loads all configuration from the specified directory
pub fn load_config(cfg_dir string) !Config {
// Ensure the config directory exists
if !os.exists(cfg_dir) {
return error('Config directory ${cfg_dir} does not exist')
}
// Load and parse footer config
footer_content := os.read_file(os.join_path(cfg_dir, 'footer.json'))!
footer := json.decode(Footer, footer_content)!
// Load and parse main config
main_config_path := os.join_path(cfg_dir, 'main.json')
main_content := os.read_file(main_config_path)!
main := json.decode(Main, main_content) or {
eprintln('${main_config_path} is not in the right format please fix.')
println('
## EXAMPLE OF A GOOD ONE:
- note the list for buildDest and buildDestDev
- note its the full path where the html is pushed too
{
"title": "ThreeFold Web4",
"tagline": "ThreeFold Web4",
"favicon": "img/favicon.png",
"url": "https://docs.threefold.io",
"url_home": "docs/introduction",
"baseUrl": "/",
"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 Docs"
},
"buildDest":["root@info.ourworld.tf:/root/hero/www/info/tfgrid4"],
"buildDestDev":["root@info.ourworld.tf:/root/hero/www/infodev/tfgrid4"]
}
')
exit(99)
}
// Load and parse navbar config
navbar_content := os.read_file(os.join_path(cfg_dir, 'navbar.json'))!
navbar := json.decode(Navbar, navbar_content)!
return Config{
footer: footer
main: main
navbar: navbar
}
}

View File

@@ -21,7 +21,7 @@ pub mut:
// path_publish pathlib.Path
args DSiteGetArgs
errors []SiteError
config Config
config Configuration
factory &DocusaurusFactory @[skip; str: skip] // Reference to the parent
}
@@ -58,8 +58,14 @@ pub fn (mut s DocSite) build_publish() ! {
)!
}
pub fn (mut s DocSite) open() ! {
// Print instructions for user
console.print_item('open browser: ${s.url}')
osal.exec(cmd: 'open https://localhost:3000')!
}
pub fn (mut s DocSite) dev() ! {
s.clean()!
s.generate()!
// Create screen session for docusaurus development server
@@ -76,8 +82,9 @@ pub fn (mut s DocSite) dev() ! {
)!
// Send commands to the screen session
console.print_item('To view the server output:: cd ${s.path_build.path}')
scr.cmd_send('cd ${s.path_build.path}')!
scr.cmd_send('bash develop.sh')!
scr.cmd_send('bun start')!
// Print instructions for user
console.print_header(' Docusaurus Development Server')
@@ -98,6 +105,10 @@ pub fn (mut s DocSite) dev() ! {
// tf.wait()!
println('\n')
if s.args.open {
s.open()!
}
if s.args.watch_changes {
docs_path := '${s.path_src.path}/docs'
watch_docs(docs_path, s.path_src.path, s.path_build.path)!
@@ -139,76 +150,10 @@ fn (mut site DocSite) check() ! {
}
}
pub fn (mut site DocSite) generate() ! {
console.print_header(' site generate: ${site.name} on ${site.path_build.path}')
console.print_header(' site source on ${site.path_src.path}')
site.check()!
site.template_install()!
// Now copy all directories that exist in src to build
for item in ['src', 'static', 'cfg'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}')!
}
}
for item in ['docs'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}', delete: true)!
}
}
mut gs := gittools.new()!
for item in site.config.main.to_import {
mypath := gs.get_path(
pull: false
reset: false
url: item.url
)!
mut mypatho := pathlib.get(mypath)
site.process_md(mut mypatho, item)!
}
}
fn (mut site DocSite) process_md(mut path pathlib.Path, args MyImport) ! {
if path.is_dir() {
mut pathlist_images := path.list(
regex: [r'.*\.png$', r'.*\.jpg$', r'.*\.svg$', r'.*\.jpeg$']
recursive: true
)!
for mut mypatho_img in pathlist_images.paths {
// now copy the image to the dest
dest := '${site.path_build.path}/docs/${args.dest}/img/${texttools.name_fix(mypatho_img.name())}'
// println("image copy: ${dest}")
mypatho_img.copy(dest: dest, rsync: false)!
}
mut pathlist := path.list(regex: [r'.*\.md$'], recursive: true)!
for mut mypatho2 in pathlist.paths {
site.process_md(mut mypatho2, args)!
}
return
}
mydest := '${site.path_build.path}/docs/${args.dest}/${texttools.name_fix(path.name())}'
mut mydesto := pathlib.get_file(path: mydest, create: true)!
mut mymd := markdownparser.new(path: path.path)!
mut myfm := mymd.frontmatter2()!
if !args.visible {
myfm.args['draft'] = 'true'
}
println(myfm)
println(mymd.markdown()!)
mydesto.write(mymd.markdown()!)!
exit(0)
}
fn (mut site DocSite) template_install() ! {
mut gs := gittools.new()!
site.factory.template_install(template_update: false, install: false, delete: false)!
site.factory.template_install(template_update: false, install: true, delete: false)!
cfg := site.config
@@ -225,7 +170,7 @@ fn (mut site DocSite) template_install() ! {
develop := $tmpl('templates/develop.sh')
build := $tmpl('templates/build.sh')
build_dev_publish := $tmpl('templates/build_dev_publish.sh')
// build_dev_publish := $tmpl('templates/build_dev_publish.sh')
build_publish := $tmpl('templates/build_publish.sh')
mut develop_ := site.path_build.file_get_new('develop.sh')!
@@ -240,9 +185,10 @@ fn (mut site DocSite) template_install() ! {
build_publish_.template_write(build_publish, true)!
build_publish_.chmod(0o700)!
mut build_dev_publish_ := site.path_build.file_get_new('build_dev_publish.sh')!
build_dev_publish_.template_write(build_dev_publish, true)!
build_dev_publish_.chmod(0o700)!
// TODO: implement
// mut build_dev_publish_ := site.path_build.file_get_new('build_dev_publish.sh')!
// build_dev_publish_.template_write(build_dev_publish, true)!
// build_dev_publish_.chmod(0o700)!
develop_templ := $tmpl('templates/develop_src.sh')
mut develop2_ := site.path_src.file_get_new('develop.sh')!

View File

@@ -18,9 +18,10 @@ pub mut:
production bool
watch_changes bool = true
update bool
open bool
init bool // means create new one if needed
deploykey string
config ?Config
config ?Configuration
}
pub fn (mut f DocusaurusFactory) get(args_ DSiteGetArgs) !&DocSite {
@@ -50,9 +51,13 @@ pub fn (mut f DocusaurusFactory) get(args_ DSiteGetArgs) !&DocSite {
)!
mut template_path := r.patho()!
// First, check if the new site args provides a configuration that can be written instead of template cfg dir
// First, check if the new site args provides a configuration
if cfg := args.config {
cfg.write('${args.path}/cfg')!
// Use the provided config
generate_configuration(args.path, cfg)!
} else if f.config.main.title != '' {
// Use the factory's config from heroscript if available
generate_configuration(args.path, f.config)!
} else {
// Then ensure cfg directory exists in src,
if !os.exists('${args.path}/cfg') {
@@ -65,17 +70,31 @@ pub fn (mut f DocusaurusFactory) get(args_ DSiteGetArgs) !&DocSite {
}
}
}
if !os.exists('${args.path}/docs') {
if args.init {
mut template_cfg := template_path.dir_get('docs')!
template_cfg.copy(dest: '${args.path}/docs')!
// Create docs directory if it doesn't exist in template or site
os.mkdir_all('${args.path}/docs')!
// Create a default docs/intro.md file
intro_content := '---
title: Introduction
slug: /
sidebar_position: 1
---
# Introduction
Welcome to the documentation site.
This is a default page created by the Docusaurus site generator.
'
os.write_file('${args.path}/docs/intro.md', intro_content)!
} else {
return error("Can't find docs dir in chosen docusaurus location: ${args.path}")
}
}
mut myconfig := load_config('${args.path}/cfg')!
mut myconfig := load_configuration('${args.path}/cfg')!
if myconfig.main.name.len == 0 {
myconfig.main.name = myconfig.main.base_url.trim_space().trim('/').trim_space()

View File

@@ -5,7 +5,8 @@ import os
import freeflowuniverse.herolib.core.pathlib
// import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.develop.gittools
// import freeflowuniverse.herolib.develop.gittools
// import freeflowuniverse.herolib.ui.console
@[heap]
pub struct DocusaurusFactory {
@@ -13,16 +14,19 @@ pub mut:
sites []&DocSite @[skip; str: skip]
path_build pathlib.Path
// path_publish pathlib.Path
args DocusaurusArgs
args DocusaurusArgs
config Configuration // Stores configuration from HeroScript if provided
}
@[params]
pub struct DocusaurusArgs {
pub mut:
// publish_path string
build_path string
production bool
update bool
build_path string
production bool
update bool
heroscript string
heroscript_path string
}
pub fn new(args_ DocusaurusArgs) !&DocusaurusFactory {
@@ -32,7 +36,9 @@ pub fn new(args_ DocusaurusArgs) !&DocusaurusFactory {
}
// if args.publish_path == ""{
// args.publish_path = "${os.home_dir()}/hero/var/docusaurus/publish"
// }
// }
// Create the factory instance
mut ds := &DocusaurusFactory{
args: args_
path_build: pathlib.get_dir(path: args.build_path, create: true)!

View File

@@ -0,0 +1,229 @@
module docusaurus
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.installers.web.bun
import freeflowuniverse.herolib.core.pathlib
import json
import os
import freeflowuniverse.herolib.ui.console
@[params]
struct TemplateInstallArgs {
template_update bool = true
install bool = true
delete bool = true
}
pub fn (mut site DocSite) generate() ! {
console.print_header(' site generate: ${site.name} on ${site.path_build.path}')
console.print_header(' site source on ${site.path_src.path}')
site.check()!
site.template_install()!
site.config = fix_configuration(site.config)!
generate_configuration(site.path_build.path, site.config)!
generate_docusaurus_config_ts(site.path_build.path, site.config)!
// Now copy all directories that exist in src to build
for item in ['src', 'static', 'cfg'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}')!
}
}
for item in ['docs'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}', delete: true)!
}
}
mut gs := gittools.new()!
// for item in site.config.import_sources {
// mypath := gs.get_path(
// pull: false
// reset: false
// url: item.url
// )!
// mut mypatho := pathlib.get(mypath)
// site.process_md(mut mypatho, item)!
// }
}
fn generate_configuration(path string, config Configuration) ! {
cfg_path := os.join_path(path, 'cfg')
mut main_file := pathlib.get_file(path: '${cfg_path}/main.json', create: true)!
main_file.write(json.encode(config.main))!
mut navbar_file := pathlib.get_file(path: '${cfg_path}/navbar.json', create: true)!
navbar_file.write(json.encode(config.navbar))!
mut footer_file := pathlib.get_file(path: '${cfg_path}/footer.json', create: true)!
footer_file.write(json.encode(config.footer))!
}
fn generate_docusaurus_config_ts(path string, config Configuration) ! {
mut config_file := pathlib.get_file(
path: os.join_path(path, 'docusaurus.config.ts')
create: true
)!
content := $tmpl('templates/docusaurus.config.ts')
config_file.write(content)!
}
fn (mut self DocusaurusFactory) template_install(args TemplateInstallArgs) ! {
mut gs := gittools.new()!
mut r := gs.get_repo(
url: 'https://github.com/freeflowuniverse/docusaurus_template.git'
pull: args.template_update
)!
mut template_path := r.patho()!
// always start from template first for static assets and source files
for item in ['src', 'static'] {
mut aa := template_path.dir_get(item)!
aa.copy(dest: '${self.path_build.path}/${item}', delete: args.delete)!
}
// Generate config files dynamically from config
self.generate_package_json()!
self.generate_tsconfig_json()!
self.generate_sidebars_ts()!
self.generate_gitignore()!
if args.install {
// install bun
mut installer := bun.get()!
installer.install()!
osal.exec(
cmd: '
${osal.profile_path_source_and()!}
export PATH=/tmp/docusaurus_build/node_modules/.bin:${os.home_dir()}/.bun/bin/:??PATH
cd ${self.path_build.path}
bun install
'
)!
}
}
fn (mut self DocusaurusFactory) generate_gitignore() ! {
mut gitignore := pathlib.get_file(
path: os.join_path(self.path_build.path, '.gitignore')
create: true
)!
content := $tmpl('templates/.gitignore')
gitignore.write(content)!
}
// Generate package.json based on the configuration
fn (mut self DocusaurusFactory) generate_package_json() ! {
// Build package.json content as a structured JSON string
mut name := 'docusaurus-site'
if self.config.main.name != '' {
name = self.config.main.name
} else if self.config.navbar.title != '' {
name = self.config.navbar.title.to_lower().replace(' ', '-')
}
// 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(content)!
}
// Generate tsconfig.json based on the configuration
fn (mut self DocusaurusFactory) generate_tsconfig_json() ! {
// 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(content)!
}
// Generate sidebars.ts based on the configuration
fn (mut self DocusaurusFactory) generate_sidebars_ts() ! {
// 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(content)!
}
// // Generate docusaurus.config.ts based on the configuration
// fn (mut self DocusaurusFactory) generate_docusaurus_config_ts() ! {
// // Use config values with fallbacks
// title := if self.config.main.title != '' { self.config.main.title } else { 'Docusaurus Site' }
// // Format navbar items from config
// mut navbar_items_list_temp := []string{}
// for item in self.config.navbar.items {
// navbar_items_list_temp << "{
// label: '${item.label}',
// href: '${item.href}',
// position: '${item.position}'
// }"
// }
// // Generate footer links if available
// mut footer_links_list_temp := []string{}
// for link in self.config.footer.links {
// mut items_temp := []string{}
// for item in link.items {
// mut item_str := '{'
// if item.label != '' {
// item_str += "label: '${item.label}', "
// }
// if item.href != '' {
// item_str += "href: '${item.href}'"
// } else if item.to != '' {
// item_str += "to: '${item.to}'"
// } else {
// item_str += "to: '/docs'" // Default link
// }
// item_str += '}'
// items_temp << item_str
// }
// footer_links_list_temp << "{
// title: '${link.title}',
// items: [
// ${items_temp.join(',\n ')}
// ]
// }"
// }
// // 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}'
// }
// // 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(content)!
// }

View File

@@ -0,0 +1,101 @@
module docusaurus
import os
import json
import freeflowuniverse.herolib.core.pathlib
pub struct Configuration {
pub mut:
main Main
navbar Navbar
footer Footer
}
pub struct Main {
pub mut:
title string
tagline string
favicon string
url string
base_url string @[json: 'baseUrl']
url_home string
image string
metadata Metadata
build_dest []string @[json: 'buildDest']
build_dest_dev []string @[json: 'buildDestDev']
copyright string
name string
}
pub struct Metadata {
pub mut:
description string
image string
title string
}
pub struct Navbar {
pub mut:
title string
logo Logo
items []NavbarItem
}
pub struct Logo {
pub mut:
alt string
src string
src_dark string @[json: 'srcDark']
}
pub struct NavbarItem {
pub mut:
label string
href string
position string
to string
}
pub struct Footer {
pub mut:
style string
links []FooterLink
}
pub struct FooterLink {
pub mut:
title string
items []FooterItem
}
pub struct FooterItem {
pub mut:
label string
href string
to string
}
pub fn load_configuration(cfg_path string) !Configuration {
mut main_json := pathlib.get_file(path: os.join_path(cfg_path, 'main.json'))!
mut navbar_json := pathlib.get_file(path: os.join_path(cfg_path, 'navbar.json'))!
mut footer_json := pathlib.get_file(path: os.join_path(cfg_path, 'footer.json'))!
mut cfg := Configuration{
main: json.decode(Main, main_json.read()!)!,
navbar: json.decode(Navbar, navbar_json.read()!)!,
footer: json.decode(Footer, footer_json.read()!)!
}
return cfg
}
pub fn fix_configuration(config Configuration) !Configuration {
return Configuration {
...config,
main: Main {
...config.main,
title: if config.main.title == "" { "Docusaurus" } else { config.main.title },
favicon: if config.main.favicon == "" { "img/favicon.ico" } else { config.main.favicon },
url: if config.main.url == "" { "https://example.com" } else { config.main.url },
base_url: if config.main.base_url == "" { "/" } else { config.main.base_url },
}
}
}

View File

@@ -1,158 +0,0 @@
module docusaurus
import freeflowuniverse.herolib.core.playbook { PlayBook }
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then playbook will be made out of it
plbook ?PlayBook
reset bool
}
// Process the heroscript and return a filled Config object
pub fn play(args_ PlayArgs) ! {
mut plbook := playbook.new(text: args_.heroscript)!
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)!
}
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
config.main = Main{
title: p.get_default('title', 'Internet Geek')!
tagline: p.get_default('tagline', 'Internet Geek')!
favicon: p.get_default('favicon', 'img/favicon.png')!
url: p.get_default('url', 'https://friends.threefold.info')!
url_home: p.get_default('url_home', 'docs/')!
base_url: p.get_default('base_url', '/testsite/')!
image: p.get_default('image', 'img/tf_graph.png')!
}
}
}
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', 'ThreeFold is laying the foundation for a geo aware Web 4, the next generation of the Internet.')!
image: p.get_default('image', 'https://threefold.info/something/img/tf_graph.png')!
title: p.get_default('title', 'ThreeFold Technology Vision')!
}
}
}
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', 'Chief Executive Geek')!
}
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', 'ThreeFold Technology')!
href: p.get_default('href', 'https://threefold.info/tech')!
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

@@ -0,0 +1,35 @@
module docusaurus
// fn (mut site DocSite) process_md(mut path pathlib.Path, args ImportSource) ! {
// if path.is_dir() {
// mut pathlist_images := path.list(
// regex: [r'.*\.png$', r'.*\.jpg$', r'.*\.svg$', r'.*\.jpeg$']
// recursive: true
// )!
// for mut mypatho_img in pathlist_images.paths {
// // now copy the image to the dest
// dest := '${site.path_build.path}/docs/${args.dest}/img/${texttools.name_fix(mypatho_img.name())}'
// // println("image copy: ${dest}")
// mypatho_img.copy(dest: dest, rsync: false)!
// }
// mut pathlist := path.list(regex: [r'.*\.md$'], recursive: true)!
// for mut mypatho2 in pathlist.paths {
// site.process_md(mut mypatho2, args)!
// }
// return
// }
// mydest := '${site.path_build.path}/docs/${args.dest}/${texttools.name_fix(path.name())}'
// mut mydesto := pathlib.get_file(path: mydest, create: true)!
// mut mymd := markdownparser.new(path: path.path)!
// mut myfm := mymd.frontmatter2()!
// if !args.visible {
// myfm.args['draft'] = 'true'
// }
// // println(myfm)
// // println(mymd.markdown()!)
// mydesto.write(mymd.markdown()!)!
// // Note: exit(0) was removed to prevent unexpected program termination
// }

View File

@@ -1,60 +0,0 @@
module docusaurus
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.installers.web.bun
import os
@[params]
struct TemplateInstallArgs {
template_update bool = true
install bool
delete bool = true
}
fn (mut self DocusaurusFactory) template_install(args TemplateInstallArgs) ! {
mut gs := gittools.new()!
mut r := gs.get_repo(
url: 'https://github.com/freeflowuniverse/docusaurus_template.git'
pull: args.template_update
)!
mut template_path := r.patho()!
for item in ['package.json', 'sidebars.ts', 'tsconfig.json'] {
mut aa := template_path.file_get(item)!
aa.copy(dest: '${self.path_build.path}/${item}')!
}
// always start from template first
for item in ['src', 'static'] {
mut aa := template_path.dir_get(item)!
aa.copy(dest: '${self.path_build.path}/${item}', delete: args.delete)!
}
for item in ['package.json', 'sidebars.ts', 'tsconfig.json', 'docusaurus.config.ts'] {
src_path := os.join_path(template_path.path, item)
dest_path := os.join_path(self.path_build.path, item)
os.cp(src_path, dest_path) or {
return error('Failed to copy ${item} to build path: ${err}')
}
}
if args.install {
// install bun
mut installer := bun.get()!
installer.install()!
osal.exec(
cmd: '
${osal.profile_path_source_and()!}
export PATH=/tmp/docusaurus_build/node_modules/.bin:${os.home_dir()}/.bun/bin/:??PATH
cd ${self.path_build.path}
bun install
'
)!
}
mut aa := template_path.dir_get('docs') or { return }
aa.delete()!
}

21
lib/web/docusaurus/templates/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
.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,94 @@
import {themes as prismThemes} from 'prism-react-renderer';
import type {Configuration} from '@@docusaurus/types';
import type * as Preset from '@@docusaurus/preset-classic';
const config: Configuration = {
title: '@{config.main.title}',
tagline: '@{config.main.tagline}',
favicon: '@{config.main.favicon}',
// Set the production url of your site here
url: '@{config.main.url}',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '@{config.main.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: '@{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',
colorMode: {
defaultMode: 'dark',
},
navbar: {
title: '@{config.navbar.title}',
items: [
@for item in config.navbar.items
{
label: '@{item.label}',
href: '@{item.href}',
position: '@{item.position}'
},
@end
],
},
footer: {
style: '@{config.footer.style}',
links: [
@for link_group in config.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'
@end
},
@end
]
},
@end
],
copyright: '@{config.main.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"]
}