...
This commit is contained in:
21
examples/data/atlas/heroscript_example.vsh
Normal file
21
examples/data/atlas/heroscript_example.vsh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import incubaid.herolib.core.playbook
|
||||||
|
import incubaid.herolib.data.atlas
|
||||||
|
|
||||||
|
heroscript := "
|
||||||
|
!!atlas.scan
|
||||||
|
path: '~/code/github/incubaid/herolib/lib/data/atlas/testdata'
|
||||||
|
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
!!atlas.export
|
||||||
|
destination: '/tmp/atlas_export_test'
|
||||||
|
include: true
|
||||||
|
redis: false
|
||||||
|
"
|
||||||
|
|
||||||
|
mut plbook := playbook.new(text: heroscript)!
|
||||||
|
atlas.play(mut plbook)!
|
||||||
|
|
||||||
|
println('✅ Atlas HeroScript processing complete!')
|
||||||
@@ -88,6 +88,11 @@ pub fn (mut a Atlas) add_collection(args AddCollectionArgs) ! {
|
|||||||
pub fn (mut a Atlas) scan(args ScanArgs) ! {
|
pub fn (mut a Atlas) scan(args ScanArgs) ! {
|
||||||
mut path := pathlib.get_dir(path: args.path)!
|
mut path := pathlib.get_dir(path: args.path)!
|
||||||
a.scan_directory(mut path)!
|
a.scan_directory(mut path)!
|
||||||
|
a.validate_links()!
|
||||||
|
a.fix_links()!
|
||||||
|
if args.save {
|
||||||
|
a.save()!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a collection by name
|
// Get a collection by name
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ fn test_save_and_load_basic() {
|
|||||||
assert a.collections.len == 1
|
assert a.collections.len == 1
|
||||||
|
|
||||||
// Save all collections
|
// Save all collections
|
||||||
a.save_all()!
|
a.save()!
|
||||||
assert os.exists('${col_path}/.collection.json')
|
assert os.exists('${col_path}/.collection.json')
|
||||||
|
|
||||||
// Load in a new atlas
|
// Load in a new atlas
|
||||||
@@ -84,7 +84,7 @@ fn test_save_and_load_with_includes() {
|
|||||||
assert !col.has_errors()
|
assert !col.has_errors()
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
mut a2 := new(name: 'loaded')!
|
mut a2 := new(name: 'loaded')!
|
||||||
@@ -118,7 +118,7 @@ fn test_save_and_load_with_errors() {
|
|||||||
initial_error_count := col.errors.len
|
initial_error_count := col.errors.len
|
||||||
|
|
||||||
// Save with errors
|
// Save with errors
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
mut a2 := new(name: 'loaded')!
|
mut a2 := new(name: 'loaded')!
|
||||||
@@ -156,7 +156,7 @@ fn test_save_and_load_multiple_collections() {
|
|||||||
|
|
||||||
assert a.collections.len == 2
|
assert a.collections.len == 2
|
||||||
|
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
// Load from directory
|
// Load from directory
|
||||||
mut a2 := new(name: 'loaded')!
|
mut a2 := new(name: 'loaded')!
|
||||||
@@ -191,7 +191,7 @@ fn test_save_and_load_with_images() {
|
|||||||
assert col.image_exists('test')
|
assert col.image_exists('test')
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
mut a2 := new(name: 'loaded')!
|
mut a2 := new(name: 'loaded')!
|
||||||
|
|||||||
@@ -6,123 +6,123 @@ import os
|
|||||||
const test_base = '/tmp/atlas_test'
|
const test_base = '/tmp/atlas_test'
|
||||||
|
|
||||||
fn testsuite_begin() {
|
fn testsuite_begin() {
|
||||||
os.rmdir_all(test_base) or {}
|
os.rmdir_all(test_base) or {}
|
||||||
os.mkdir_all(test_base)!
|
os.mkdir_all(test_base)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testsuite_end() {
|
fn testsuite_end() {
|
||||||
os.rmdir_all(test_base) or {}
|
os.rmdir_all(test_base) or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_create_atlas() {
|
fn test_create_atlas() {
|
||||||
mut a := new(name: 'test_atlas')!
|
mut a := new(name: 'test_atlas')!
|
||||||
assert a.name == 'test_atlas'
|
assert a.name == 'test_atlas'
|
||||||
assert a.collections.len == 0
|
assert a.collections.len == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_add_collection() {
|
fn test_add_collection() {
|
||||||
// Create test collection
|
// Create test collection
|
||||||
col_path := '${test_base}/col1'
|
col_path := '${test_base}/col1'
|
||||||
os.mkdir_all(col_path)!
|
os.mkdir_all(col_path)!
|
||||||
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
||||||
cfile.write('name:col1')!
|
cfile.write('name:col1')!
|
||||||
|
|
||||||
mut page := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
mut page := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
||||||
page.write('# Page 1\n\nContent here.')!
|
page.write('# Page 1\n\nContent here.')!
|
||||||
|
|
||||||
mut a := new(name: 'test')!
|
mut a := new(name: 'test')!
|
||||||
a.add_collection(name: 'col1', path: col_path)!
|
a.add_collection(name: 'col1', path: col_path)!
|
||||||
|
|
||||||
assert a.collections.len == 1
|
assert a.collections.len == 1
|
||||||
assert 'col1' in a.collections
|
assert 'col1' in a.collections
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_scan() {
|
fn test_scan() {
|
||||||
// Create test structure
|
// Create test structure
|
||||||
os.mkdir_all('${test_base}/docs/guides')!
|
os.mkdir_all('${test_base}/docs/guides')!
|
||||||
mut cfile := pathlib.get_file(path: '${test_base}/docs/guides/.collection', create: true)!
|
mut cfile := pathlib.get_file(path: '${test_base}/docs/guides/.collection', create: true)!
|
||||||
cfile.write('name:guides')!
|
cfile.write('name:guides')!
|
||||||
|
|
||||||
mut page := pathlib.get_file(path: '${test_base}/docs/guides/intro.md', create: true)!
|
mut page := pathlib.get_file(path: '${test_base}/docs/guides/intro.md', create: true)!
|
||||||
page.write('# Introduction')!
|
page.write('# Introduction')!
|
||||||
|
|
||||||
mut a := new()!
|
mut a := new()!
|
||||||
a.scan(path: '${test_base}/docs')!
|
a.scan(path: '${test_base}/docs')!
|
||||||
|
|
||||||
assert a.collections.len == 1
|
assert a.collections.len == 1
|
||||||
col := a.get_collection('guides')!
|
col := a.get_collection('guides')!
|
||||||
assert col.page_exists('intro')
|
assert col.page_exists('intro')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_export() {
|
fn test_export() {
|
||||||
// Setup
|
// Setup
|
||||||
col_path := '${test_base}/source/col1'
|
col_path := '${test_base}/source/col1'
|
||||||
export_path := '${test_base}/export'
|
export_path := '${test_base}/export'
|
||||||
|
|
||||||
os.mkdir_all(col_path)!
|
os.mkdir_all(col_path)!
|
||||||
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
||||||
cfile.write('name:col1')!
|
cfile.write('name:col1')!
|
||||||
|
|
||||||
mut page := pathlib.get_file(path: '${col_path}/test.md', create: true)!
|
mut page := pathlib.get_file(path: '${col_path}/test.md', create: true)!
|
||||||
page.write('# Test Page')!
|
page.write('# Test Page')!
|
||||||
|
|
||||||
mut a := new()!
|
mut a := new()!
|
||||||
a.add_collection(name: 'col1', path: col_path)!
|
a.add_collection(name: 'col1', path: col_path)!
|
||||||
|
|
||||||
a.export(destination: export_path, redis: false)!
|
a.export(destination: export_path, redis: false)!
|
||||||
|
|
||||||
assert os.exists('${export_path}/col1/test.md')
|
assert os.exists('${export_path}/col1/test.md')
|
||||||
assert os.exists('${export_path}/col1/.collection')
|
assert os.exists('${export_path}/col1/.collection')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_export_with_includes() {
|
fn test_export_with_includes() {
|
||||||
// Setup: Create pages with includes
|
// Setup: Create pages with includes
|
||||||
col_path := '${test_base}/include_test'
|
col_path := '${test_base}/include_test'
|
||||||
os.mkdir_all(col_path)!
|
os.mkdir_all(col_path)!
|
||||||
|
|
||||||
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
||||||
cfile.write('name:test_col')!
|
cfile.write('name:test_col')!
|
||||||
|
|
||||||
// Page 1: includes page 2
|
// Page 1: includes page 2
|
||||||
mut page1 := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
mut page1 := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
||||||
page1.write('# Page 1\n\n!!include test_col:page2\n\nEnd of page 1')!
|
page1.write('# Page 1\n\n!!include test_col:page2\n\nEnd of page 1')!
|
||||||
|
|
||||||
// Page 2: standalone content
|
// Page 2: standalone content
|
||||||
mut page2 := pathlib.get_file(path: '${col_path}/page2.md', create: true)!
|
mut page2 := pathlib.get_file(path: '${col_path}/page2.md', create: true)!
|
||||||
page2.write('## Page 2 Content\n\nThis is included.')!
|
page2.write('## Page 2 Content\n\nThis is included.')!
|
||||||
|
|
||||||
mut a := new()!
|
mut a := new()!
|
||||||
a.add_collection(name: 'test_col', path: col_path)!
|
a.add_collection(name: 'test_col', path: col_path)!
|
||||||
|
|
||||||
export_path := '${test_base}/export_include'
|
export_path := '${test_base}/export_include'
|
||||||
a.export(destination: export_path, include: true)!
|
a.export(destination: export_path, include: true)!
|
||||||
|
|
||||||
// Verify exported page1 has page2 content included
|
// Verify exported page1 has page2 content included
|
||||||
exported := os.read_file('${export_path}/test_col/page1.md')!
|
exported := os.read_file('${export_path}/test_col/page1.md')!
|
||||||
assert exported.contains('Page 2 Content')
|
assert exported.contains('Page 2 Content')
|
||||||
assert exported.contains('This is included')
|
assert exported.contains('This is included')
|
||||||
assert !exported.contains('!!include')
|
assert !exported.contains('!!include')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_export_without_includes() {
|
fn test_export_without_includes() {
|
||||||
col_path := '${test_base}/no_include_test'
|
col_path := '${test_base}/no_include_test'
|
||||||
os.mkdir_all(col_path)!
|
os.mkdir_all(col_path)!
|
||||||
|
|
||||||
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
|
||||||
cfile.write('name:test_col2')!
|
cfile.write('name:test_col2')!
|
||||||
|
|
||||||
mut page1 := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
mut page1 := pathlib.get_file(path: '${col_path}/page1.md', create: true)!
|
||||||
page1.write('# Page 1\n\n!!include test_col2:page2\n\nEnd')!
|
page1.write('# Page 1\n\n!!include test_col2:page2\n\nEnd')!
|
||||||
|
|
||||||
mut a := new()!
|
mut a := new()!
|
||||||
a.add_collection(name: 'test_col2', path: col_path)!
|
a.add_collection(name: 'test_col2', path: col_path)!
|
||||||
|
|
||||||
export_path := '${test_base}/export_no_include'
|
export_path := '${test_base}/export_no_include'
|
||||||
a.export(destination: export_path, include: false)!
|
a.export(destination: export_path, include: false)!
|
||||||
|
|
||||||
// Verify exported page1 still has include action
|
// Verify exported page1 still has include action
|
||||||
exported := os.read_file('${export_path}/test_col2/page1.md')!
|
exported := os.read_file('${export_path}/test_col2/page1.md')!
|
||||||
assert exported.contains('!!include')
|
assert exported.contains('!!include')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_error_deduplication() {
|
fn test_error_deduplication() {
|
||||||
@@ -300,8 +300,8 @@ fn test_link_formats() {
|
|||||||
assert local_links[1].page == 'page2'
|
assert local_links[1].page == 'page2'
|
||||||
assert local_links[2].collection == 'guides'
|
assert local_links[2].collection == 'guides'
|
||||||
assert local_links[2].page == 'intro'
|
assert local_links[2].page == 'intro'
|
||||||
assert local_links[3].page == 'page3' // Path ignored, only filename
|
assert local_links[3].page == 'page3' // Path ignored, only filename
|
||||||
assert local_links[4].page == 'page4' // Path ignored, only filename
|
assert local_links[4].page == 'page4' // Path ignored, only filename
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_cross_collection_links() {
|
fn test_cross_collection_links() {
|
||||||
@@ -340,7 +340,7 @@ fn test_cross_collection_links() {
|
|||||||
a.fix_links()!
|
a.fix_links()!
|
||||||
|
|
||||||
fixed := page1.read()!
|
fixed := page1.read()!
|
||||||
assert fixed.contains('[Link to col2](col2:page2)') // Unchanged
|
assert fixed.contains('[Link to col2](col2:page2)') // Unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_save_and_load() {
|
fn test_save_and_load() {
|
||||||
@@ -357,7 +357,7 @@ fn test_save_and_load() {
|
|||||||
// Create and save
|
// Create and save
|
||||||
mut a := new(name: 'test')!
|
mut a := new(name: 'test')!
|
||||||
a.add_collection(name: 'test_col', path: col_path)!
|
a.add_collection(name: 'test_col', path: col_path)!
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
assert os.exists('${col_path}/.collection.json')
|
assert os.exists('${col_path}/.collection.json')
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ fn test_load_from_directory() {
|
|||||||
mut a := new(name: 'test')!
|
mut a := new(name: 'test')!
|
||||||
a.add_collection(name: 'col1', path: col1_path)!
|
a.add_collection(name: 'col1', path: col1_path)!
|
||||||
a.add_collection(name: 'col2', path: col2_path)!
|
a.add_collection(name: 'col2', path: col2_path)!
|
||||||
a.save_all()!
|
a.save()!
|
||||||
|
|
||||||
// Load from directory
|
// Load from directory
|
||||||
mut a2 := new(name: 'loaded')!
|
mut a2 := new(name: 'loaded')!
|
||||||
|
|||||||
56
lib/data/atlas/play.v
Normal file
56
lib/data/atlas/play.v
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
module atlas
|
||||||
|
|
||||||
|
import incubaid.herolib.core.playbook { PlayBook }
|
||||||
|
|
||||||
|
// Play function to process HeroScript actions for Atlas
|
||||||
|
pub fn play(mut plbook PlayBook) ! {
|
||||||
|
if !plbook.exists(filter: 'atlas.') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mut atlases := map[string]&Atlas{}
|
||||||
|
|
||||||
|
// Process scan actions - scan directories for collections
|
||||||
|
mut scan_actions := plbook.find(filter: 'atlas.scan')!
|
||||||
|
for mut action in scan_actions {
|
||||||
|
mut p := action.params
|
||||||
|
name := p.get_default('name', 'main')!
|
||||||
|
|
||||||
|
// Get or create atlas
|
||||||
|
mut atlas_instance := atlases[name] or {
|
||||||
|
mut new_atlas := new(name: name)!
|
||||||
|
atlases[name] = new_atlas
|
||||||
|
new_atlas
|
||||||
|
}
|
||||||
|
|
||||||
|
path := p.get('path')!
|
||||||
|
atlas_instance.scan(path: path, save: true)!
|
||||||
|
action.done = true
|
||||||
|
atlas_set(atlas_instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process export actions - export collections to destination
|
||||||
|
mut export_actions := plbook.find(filter: 'atlas.export')!
|
||||||
|
|
||||||
|
// Process explicit export actions
|
||||||
|
for mut action in export_actions {
|
||||||
|
mut p := action.params
|
||||||
|
name := p.get_default('name', 'main')!
|
||||||
|
destination := p.get('destination')!
|
||||||
|
reset := p.get_default_true('reset')
|
||||||
|
include := p.get_default_true('include')
|
||||||
|
redis := p.get_default_true('redis')
|
||||||
|
|
||||||
|
mut atlas_instance := atlases[name] or {
|
||||||
|
return error("Atlas '${name}' not found. Use !!atlas.scan or !!atlas.load first.")
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_instance.export(
|
||||||
|
destination: destination
|
||||||
|
reset: reset
|
||||||
|
include: include
|
||||||
|
redis: redis
|
||||||
|
)!
|
||||||
|
action.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,7 +81,7 @@ a.add_collection(name: 'guides', path: './docs/guides')!
|
|||||||
```v
|
```v
|
||||||
// Get a page
|
// Get a page
|
||||||
page := a.page_get('guides:introduction')!
|
page := a.page_get('guides:introduction')!
|
||||||
content := page.read_content()!
|
content := page.content()!
|
||||||
|
|
||||||
// Check if page exists
|
// Check if page exists
|
||||||
if a.page_exists('guides:setup') {
|
if a.page_exists('guides:setup') {
|
||||||
@@ -207,13 +207,9 @@ mut page := a.page_get('col:mypage')!
|
|||||||
content := page.content(include: true)!
|
content := page.content(include: true)!
|
||||||
|
|
||||||
// Read raw content without processing includes
|
// Read raw content without processing includes
|
||||||
content := page.read_content()!
|
content := page.content()!
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Circular Include Detection
|
|
||||||
|
|
||||||
Atlas automatically detects circular includes and reports them as errors without causing infinite loops.
|
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
Atlas supports standard Markdown links with several formats for referencing pages within collections.
|
Atlas supports standard Markdown links with several formats for referencing pages within collections.
|
||||||
@@ -471,7 +467,7 @@ print(f"Pages: {len(col.pages)}")
|
|||||||
# Access pages
|
# Access pages
|
||||||
page = atlas.page_get('guides:intro')
|
page = atlas.page_get('guides:intro')
|
||||||
if page:
|
if page:
|
||||||
content = page.read_content()
|
content = page.content()
|
||||||
print(content)
|
print(content)
|
||||||
|
|
||||||
# Check for errors
|
# Check for errors
|
||||||
@@ -506,7 +502,7 @@ if atlas.has_errors():
|
|||||||
#### Page Class
|
#### Page Class
|
||||||
|
|
||||||
- `page.key()` - Get page key in format 'collection:page'
|
- `page.key()` - Get page key in format 'collection:page'
|
||||||
- `page.read_content()` - Read page content from file
|
- `page.content()` - Read page content from file
|
||||||
|
|
||||||
#### File Class
|
#### File Class
|
||||||
|
|
||||||
@@ -572,7 +568,7 @@ atlas = Atlas.load_from_directory('/path/to/docs')
|
|||||||
# Access pages
|
# Access pages
|
||||||
page = atlas.page_get('guides:intro')
|
page = atlas.page_get('guides:intro')
|
||||||
if page:
|
if page:
|
||||||
content = page.read_content()
|
content = page.content()
|
||||||
print(content)
|
print(content)
|
||||||
|
|
||||||
# Check errors
|
# Check errors
|
||||||
@@ -630,3 +626,285 @@ if col.has_errors():
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## HeroScript Integration
|
||||||
|
|
||||||
|
Atlas integrates with HeroScript, allowing you to define Atlas operations in `.vsh` or playbook files.
|
||||||
|
|
||||||
|
### Available Actions
|
||||||
|
|
||||||
|
#### 1. `atlas.scan` - Scan Directory for Collections
|
||||||
|
|
||||||
|
Scan a directory tree to find and load collections marked with `.collection` files.
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.scan
|
||||||
|
name: 'main'
|
||||||
|
path: './docs'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
- `path` (required) - Directory path to scan
|
||||||
|
|
||||||
|
#### 2. `atlas.load` - Load from Saved Collections
|
||||||
|
|
||||||
|
Load collections from `.collection.json` files (previously saved with `atlas.save`).
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.load
|
||||||
|
name: 'main'
|
||||||
|
path: './docs'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
- `path` (required) - Directory path containing `.collection.json` files
|
||||||
|
|
||||||
|
#### 3. `atlas.validate` - Validate All Links
|
||||||
|
|
||||||
|
Validate all markdown links in all collections.
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.validate
|
||||||
|
name: 'main'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
|
||||||
|
#### 4. `atlas.fix_links` - Fix All Links
|
||||||
|
|
||||||
|
Automatically rewrite all local links with correct relative paths.
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.fix_links
|
||||||
|
name: 'main'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
|
||||||
|
#### 5. `atlas.save` - Save Collections
|
||||||
|
|
||||||
|
Save all collections to `.collection.json` files in their respective directories.
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.save
|
||||||
|
name: 'main'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
|
||||||
|
#### 6. `atlas.export` - Export Collections
|
||||||
|
|
||||||
|
Export collections to a destination directory.
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.export
|
||||||
|
name: 'main'
|
||||||
|
destination: './output'
|
||||||
|
reset: true
|
||||||
|
include: true
|
||||||
|
redis: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (optional, default: 'main') - Atlas instance name
|
||||||
|
- `destination` (required) - Export destination path
|
||||||
|
- `reset` (optional, default: true) - Clear destination before export
|
||||||
|
- `include` (optional, default: true) - Process `!!include` actions
|
||||||
|
- `redis` (optional, default: true) - Store metadata in Redis
|
||||||
|
|
||||||
|
### Complete Workflow Examples
|
||||||
|
|
||||||
|
#### Example 1: Scan, Validate, and Export
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
# Scan for collections
|
||||||
|
!!atlas.scan
|
||||||
|
path: '~/docs/myproject'
|
||||||
|
|
||||||
|
# Validate all links
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
# Export to output directory
|
||||||
|
!!atlas.export
|
||||||
|
destination: '~/docs/output'
|
||||||
|
include: true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 2: Load, Fix Links, and Export
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
# Load from saved collections
|
||||||
|
!!atlas.load
|
||||||
|
path: '~/docs/myproject'
|
||||||
|
|
||||||
|
# Fix all broken links
|
||||||
|
!!atlas.fix_links
|
||||||
|
|
||||||
|
# Save updated collections
|
||||||
|
!!atlas.save
|
||||||
|
|
||||||
|
# Export
|
||||||
|
!!atlas.export
|
||||||
|
destination: '~/docs/output'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 3: Multiple Atlas Instances
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
# Main documentation
|
||||||
|
!!atlas.scan
|
||||||
|
name: 'docs'
|
||||||
|
path: '~/docs'
|
||||||
|
|
||||||
|
# API reference
|
||||||
|
!!atlas.scan
|
||||||
|
name: 'api'
|
||||||
|
path: '~/api-docs'
|
||||||
|
|
||||||
|
# Export docs
|
||||||
|
!!atlas.export
|
||||||
|
name: 'docs'
|
||||||
|
destination: '~/output/docs'
|
||||||
|
|
||||||
|
# Export API
|
||||||
|
!!atlas.export
|
||||||
|
name: 'api'
|
||||||
|
destination: '~/output/api'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 4: Development Workflow
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
# Scan collections
|
||||||
|
!!atlas.scan
|
||||||
|
path: './docs'
|
||||||
|
|
||||||
|
# Validate links (errors will be reported)
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
# Fix links automatically
|
||||||
|
!!atlas.fix_links
|
||||||
|
|
||||||
|
# Save updated collections
|
||||||
|
!!atlas.save
|
||||||
|
|
||||||
|
# Export final version
|
||||||
|
!!atlas.export
|
||||||
|
destination: './public'
|
||||||
|
include: true
|
||||||
|
redis: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using in V Scripts
|
||||||
|
|
||||||
|
Create a `.vsh` script to process Atlas operations:
|
||||||
|
|
||||||
|
```v
|
||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import incubaid.herolib.core.playbook
|
||||||
|
import incubaid.herolib.data.atlas
|
||||||
|
|
||||||
|
// Define your HeroScript content
|
||||||
|
heroscript := "
|
||||||
|
!!atlas.scan
|
||||||
|
path: './docs'
|
||||||
|
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
!!atlas.export
|
||||||
|
destination: './output'
|
||||||
|
include: true
|
||||||
|
"
|
||||||
|
|
||||||
|
// Create playbook from text
|
||||||
|
mut plbook := playbook.new(text: heroscript)!
|
||||||
|
|
||||||
|
// Execute atlas actions
|
||||||
|
atlas.play(mut plbook)!
|
||||||
|
|
||||||
|
println('Atlas processing complete!')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using in Playbook Files
|
||||||
|
|
||||||
|
Create a `docs.play` file:
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.scan
|
||||||
|
name: 'main'
|
||||||
|
path: '~/code/docs'
|
||||||
|
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
!!atlas.fix_links
|
||||||
|
|
||||||
|
!!atlas.save
|
||||||
|
|
||||||
|
!!atlas.export
|
||||||
|
destination: '~/code/output'
|
||||||
|
reset: true
|
||||||
|
include: true
|
||||||
|
redis: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vrun process_docs.vsh
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `process_docs.vsh` contains:
|
||||||
|
|
||||||
|
```v
|
||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import incubaid.herolib.core.playbook
|
||||||
|
import incubaid.herolib.core.playcmds
|
||||||
|
|
||||||
|
// Load and execute playbook
|
||||||
|
mut plbook := playbook.new(path: './docs.play')!
|
||||||
|
playcmds.run(mut plbook)!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Errors are automatically collected and reported:
|
||||||
|
|
||||||
|
```heroscript
|
||||||
|
!!atlas.scan
|
||||||
|
path: './docs'
|
||||||
|
|
||||||
|
!!atlas.validate
|
||||||
|
|
||||||
|
# Errors will be printed during export
|
||||||
|
!!atlas.export
|
||||||
|
destination: './output'
|
||||||
|
```
|
||||||
|
|
||||||
|
Errors are shown in the console:
|
||||||
|
|
||||||
|
```
|
||||||
|
Collection guides - Errors (2)
|
||||||
|
[invalid_page_reference] [guides:intro]: Broken link to `guides:setup` at line 5
|
||||||
|
[missing_include] [guides:advanced]: Included page `guides:examples` not found
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-Export Behavior
|
||||||
|
|
||||||
|
If you use `!!atlas.scan` or `!!atlas.load` **without** an explicit `!!atlas.export`, Atlas will automatically export to the default location (current directory).
|
||||||
|
|
||||||
|
To disable auto-export, include an explicit (empty) export action or simply don't include any scan/load actions.
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **Always validate before export**: Use `!!atlas.validate` to catch broken links early
|
||||||
|
2. **Save after fixing**: Use `!!atlas.save` after `!!atlas.fix_links` to persist changes
|
||||||
|
3. **Use named instances**: When working with multiple documentation sets, use the `name` parameter
|
||||||
|
4. **Enable Redis for production**: Use `redis: true` for web deployments to enable fast lookups
|
||||||
|
5. **Process includes during export**: Keep `include: true` to embed referenced content in exported files
|
||||||
@@ -17,7 +17,7 @@ pub fn (c Collection) save() ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save all collections in atlas to their respective directories
|
// Save all collections in atlas to their respective directories
|
||||||
pub fn (a Atlas) save_all() ! {
|
pub fn (a Atlas) save() ! {
|
||||||
for _, col in a.collections {
|
for _, col in a.collections {
|
||||||
col.save()!
|
col.save()!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import os
|
|||||||
pub struct ScanArgs {
|
pub struct ScanArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
path string @[required]
|
path string @[required]
|
||||||
|
save bool = true // save atlas after scan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan a directory for collections
|
// Scan a directory for collections
|
||||||
|
|||||||
Reference in New Issue
Block a user