diff --git a/lib/core/redisclient/readme.md b/lib/core/redisclient/readme.md index b5259be1..703f4414 100644 --- a/lib/core/redisclient/readme.md +++ b/lib/core/redisclient/readme.md @@ -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/ + diff --git a/lib/core/texttools/readme.md b/lib/core/texttools/readme.md index 74643e3c..828f34fc 100644 --- a/lib/core/texttools/readme.md +++ b/lib/core/texttools/readme.md @@ -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. diff --git a/lib/web/docusaurus/clean.v b/lib/web/docusaurus/clean.v deleted file mode 100644 index b0d7674e..00000000 --- a/lib/web/docusaurus/clean.v +++ /dev/null @@ -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}') } - } - } - } -} diff --git a/lib/web/docusaurus/config.v b/lib/web/docusaurus/config.v deleted file mode 100644 index 22509189..00000000 --- a/lib/web/docusaurus/config.v +++ /dev/null @@ -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))! -} diff --git a/lib/web/docusaurus/config_load.v b/lib/web/docusaurus/config_load.v deleted file mode 100644 index 3f7100fd..00000000 --- a/lib/web/docusaurus/config_load.v +++ /dev/null @@ -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 - } -} diff --git a/lib/web/docusaurus/dsite.v b/lib/web/docusaurus/dsite.v index 5b87b520..02292645 100644 --- a/lib/web/docusaurus/dsite.v +++ b/lib/web/docusaurus/dsite.v @@ -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')! diff --git a/lib/web/docusaurus/dsite_get.v b/lib/web/docusaurus/dsite_get.v index 024f7615..34246f72 100644 --- a/lib/web/docusaurus/dsite_get.v +++ b/lib/web/docusaurus/dsite_get.v @@ -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() diff --git a/lib/web/docusaurus/factory.v b/lib/web/docusaurus/factory.v index 7a25e2fa..ddf9e7e7 100644 --- a/lib/web/docusaurus/factory.v +++ b/lib/web/docusaurus/factory.v @@ -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)! diff --git a/lib/web/docusaurus/generate.v b/lib/web/docusaurus/generate.v new file mode 100644 index 00000000..86fbde36 --- /dev/null +++ b/lib/web/docusaurus/generate.v @@ -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)! +// } diff --git a/lib/web/docusaurus/model_configuration.v b/lib/web/docusaurus/model_configuration.v new file mode 100644 index 00000000..26e8f984 --- /dev/null +++ b/lib/web/docusaurus/model_configuration.v @@ -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 }, + } + } +} \ No newline at end of file diff --git a/lib/web/docusaurus/model.v b/lib/web/docusaurus/model_errors.v similarity index 100% rename from lib/web/docusaurus/model.v rename to lib/web/docusaurus/model_errors.v diff --git a/lib/web/docusaurus/play.v b/lib/web/docusaurus/play.v deleted file mode 100644 index fb560fea..00000000 --- a/lib/web/docusaurus/play.v +++ /dev/null @@ -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 - } - } -} diff --git a/lib/web/docusaurus/process.v b/lib/web/docusaurus/process.v new file mode 100644 index 00000000..52cae2a4 --- /dev/null +++ b/lib/web/docusaurus/process.v @@ -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 +// } \ No newline at end of file diff --git a/lib/web/docusaurus/template.v b/lib/web/docusaurus/template.v deleted file mode 100644 index 46fc4b1c..00000000 --- a/lib/web/docusaurus/template.v +++ /dev/null @@ -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()! -} diff --git a/lib/web/docusaurus/templates/.gitignore b/lib/web/docusaurus/templates/.gitignore new file mode 100644 index 00000000..77c2532e --- /dev/null +++ b/lib/web/docusaurus/templates/.gitignore @@ -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* diff --git a/lib/web/docusaurus/templates/build_dev_publish.sh b/lib/web/docusaurus/templates/build_dev_publish.sh deleted file mode 100755 index e66c4741..00000000 --- a/lib/web/docusaurus/templates/build_dev_publish.sh +++ /dev/null @@ -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 diff --git a/lib/web/docusaurus/templates/docusaurus.config.ts b/lib/web/docusaurus/templates/docusaurus.config.ts new file mode 100644 index 00000000..2eb1a59d --- /dev/null +++ b/lib/web/docusaurus/templates/docusaurus.config.ts @@ -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 // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + 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; \ No newline at end of file diff --git a/lib/web/docusaurus/templates/package.json b/lib/web/docusaurus/templates/package.json new file mode 100644 index 00000000..936e58d9 --- /dev/null +++ b/lib/web/docusaurus/templates/package.json @@ -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" + } +} \ No newline at end of file diff --git a/lib/web/docusaurus/templates/sidebars.ts b/lib/web/docusaurus/templates/sidebars.ts new file mode 100644 index 00000000..a2b53bb4 --- /dev/null +++ b/lib/web/docusaurus/templates/sidebars.ts @@ -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; \ No newline at end of file diff --git a/lib/web/docusaurus/templates/tsconfig.json b/lib/web/docusaurus/templates/tsconfig.json new file mode 100644 index 00000000..fb364589 --- /dev/null +++ b/lib/web/docusaurus/templates/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "resolveJsonModule": true + }, + "include": ["src/**/*", "docusaurus.config.ts"] +} \ No newline at end of file