612 lines
No EOL
18 KiB
Text
Executable file
612 lines
No EOL
18 KiB
Text
Executable file
#!/usr/bin/env nu
|
|
# Web Builder Framework CLI
|
|
# Multi-page web builder with shared assets and optimization
|
|
|
|
# Framework paths
|
|
let FRAMEWORK_ROOT = ($env.PWD)
|
|
let FRAMEWORK_CORE = ($FRAMEWORK_ROOT | path join "framework" "core")
|
|
let FRAMEWORK_OPTIMIZERS = ($FRAMEWORK_ROOT | path join "framework" "optimizers")
|
|
let FRAMEWORK_TEMPLATES = ($FRAMEWORK_ROOT | path join "framework" "templates")
|
|
let SHARED_ASSETS = ($FRAMEWORK_ROOT | path join "shared")
|
|
let PAGES_DIR = ($FRAMEWORK_ROOT | path join "pages")
|
|
let SITES_DIR = ($FRAMEWORK_ROOT | path join "sites")
|
|
|
|
# Import framework modules will be done dynamically in functions
|
|
|
|
# Main CLI entry point
|
|
def main [command?: string = "help"] {
|
|
match $command {
|
|
"help" => { show_help }
|
|
"page" => { print "Use: wb page <subcommand>. Run 'wb page help' for options." }
|
|
"site" => { print "Use: wb site <subcommand>. Run 'wb site help' for options." }
|
|
"shared" => { print "Use: wb shared <subcommand>. Run 'wb shared help' for options." }
|
|
"init" => { print "Use: wb init <project-name>" }
|
|
"dev" => { print "Use: wb dev <page> [--port N]. Shortcut for 'wb page dev <page> --watch'" }
|
|
"build-all" => { build_all_pages }
|
|
"clean" => { clean_all_builds }
|
|
_ => { show_help }
|
|
}
|
|
}
|
|
|
|
# Page management commands
|
|
def "main page" [
|
|
subcommand: string = "help",
|
|
name?: string,
|
|
--template(-t): string = "single",
|
|
--dev(-d),
|
|
--prod(-p),
|
|
--watch(-w),
|
|
--port: int = 8080
|
|
] {
|
|
match $subcommand {
|
|
"create" => {
|
|
if ($name | is-empty) {
|
|
print "Error: Page name required"
|
|
return
|
|
}
|
|
create_page $name $template
|
|
}
|
|
"build" => {
|
|
if ($name | is-empty) {
|
|
print "Error: Page name required"
|
|
return
|
|
}
|
|
if $dev {
|
|
build_page_dev $name
|
|
} else if $prod {
|
|
build_page_prod $name
|
|
} else {
|
|
build_page_dev $name
|
|
}
|
|
}
|
|
"dev" => {
|
|
if ($name | is-empty) {
|
|
print "Error: Page name required"
|
|
return
|
|
}
|
|
dev_page $name $port $watch
|
|
}
|
|
"list" => { list_pages }
|
|
"help" => { show_page_help }
|
|
_ => { show_page_help }
|
|
}
|
|
}
|
|
|
|
# Site management commands
|
|
def "main site" [
|
|
subcommand: string = "help",
|
|
name?: string
|
|
] {
|
|
match $subcommand {
|
|
"create" => {
|
|
if ($name | is-empty) {
|
|
print "Error: Site name required"
|
|
return
|
|
}
|
|
create_site $name
|
|
}
|
|
"build" => {
|
|
if ($name | is-empty) {
|
|
print "Error: Site name required"
|
|
return
|
|
}
|
|
build_site $name
|
|
}
|
|
"list" => { list_sites }
|
|
"help" => { show_site_help }
|
|
_ => { show_site_help }
|
|
}
|
|
}
|
|
|
|
# Shared asset management
|
|
def "main shared" [
|
|
subcommand: string = "help",
|
|
file?: string,
|
|
--type(-t): string = "css"
|
|
] {
|
|
match $subcommand {
|
|
"add" => {
|
|
if ($file | is-empty) {
|
|
print "Error: File path required"
|
|
return
|
|
}
|
|
add_shared_asset $file $type
|
|
}
|
|
"list" => { list_shared_assets }
|
|
"help" => { show_shared_help }
|
|
_ => { show_shared_help }
|
|
}
|
|
}
|
|
|
|
# Initialize new project
|
|
def "main init" [project_name: string] {
|
|
print $"🚀 Initializing Web Builder project: ($project_name)"
|
|
|
|
# Create project structure
|
|
mkdir $project_name
|
|
cd $project_name
|
|
|
|
# Copy framework
|
|
cp -r $FRAMEWORK_ROOT $project_name
|
|
|
|
print $"✅ Project ($project_name) initialized successfully!"
|
|
print $"📁 Navigate to the project: cd ($project_name)"
|
|
print $"🎯 Create your first page: ./wb page create homepage"
|
|
}
|
|
|
|
# Load page configuration with asset resolution
|
|
export def load_page_config [page_name: string] {
|
|
let page_config_path = ($PAGES_DIR | path join $page_name "config.toml")
|
|
|
|
if not ($page_config_path | path exists) {
|
|
error make {msg: $"Page config not found: ($page_config_path)"}
|
|
}
|
|
|
|
let config = (open $page_config_path)
|
|
|
|
# Return config as-is for now (asset resolution will be done in build scripts)
|
|
$config
|
|
}
|
|
|
|
# Resolve asset paths (@shared, @page references)
|
|
export def resolve_asset_path [asset_path: string] {
|
|
if ($asset_path | str starts-with "@shared/") {
|
|
let relative_path = ($asset_path | str replace "@shared/" "")
|
|
($SHARED_ASSETS | path join $relative_path)
|
|
} else if ($asset_path | str starts-with "@page/") {
|
|
let parts = ($asset_path | str replace "@page/" "" | split row "/")
|
|
let page_name = ($parts | first)
|
|
let asset_path = ($parts | skip 1 | str join "/")
|
|
($PAGES_DIR | path join $page_name "src" "assets" $asset_path)
|
|
} else {
|
|
$asset_path
|
|
}
|
|
}
|
|
|
|
# Create new page from template
|
|
def create_page [name: string, template: string] {
|
|
print $"📝 Creating page: ($name) with template: ($template)"
|
|
|
|
let page_dir = ($PAGES_DIR | path join $name)
|
|
|
|
if ($page_dir | path exists) {
|
|
print $"❌ Page ($name) already exists"
|
|
return
|
|
}
|
|
|
|
# Create page structure
|
|
mkdir ($page_dir | path join "src" "assets" "css")
|
|
mkdir ($page_dir | path join "src" "assets" "svg")
|
|
mkdir ($page_dir | path join "dist")
|
|
|
|
# Copy and customize config template
|
|
let template_path = ($FRAMEWORK_TEMPLATES | path join "page.config.toml.template")
|
|
let config_content = (open $template_path)
|
|
|
|
let customized_config = ($config_content
|
|
| str replace "{{PAGE_NAME}}" $name
|
|
| str replace "{{PAGE_TITLE}}" $name
|
|
| str replace "{{PAGE_TYPE}}" $template
|
|
| str replace "{{PAGE_DESCRIPTION}}" $"Page created with ($template) template"
|
|
)
|
|
|
|
$customized_config | save ($page_dir | path join "config.toml")
|
|
|
|
# Create basic HTML file
|
|
let html_content = $'<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>($name)</title>
|
|
<link rel="stylesheet" href="assets/css/main.min.css">
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1 class="animated-title">($name)</h1>
|
|
<p class="text-gray-400">Created with Web Builder Framework</p>
|
|
</div>
|
|
</body>
|
|
</html>'
|
|
|
|
$html_content | save ($page_dir | path join "src" "index.html")
|
|
|
|
# Create basic CSS
|
|
let css_content = $'/* Page-specific styles for ($name) */
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
body {
|
|
background: #0a0a0a;
|
|
color: #ffffff;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}'
|
|
|
|
$css_content | save ($page_dir | path join "src" "assets" "css" "main.css")
|
|
|
|
print $"✅ Page ($name) created successfully!"
|
|
print $"📁 Location: ($page_dir)"
|
|
print $"🎯 Build it: wb page build ($name)"
|
|
print $"🔥 Develop: wb page dev ($name) --watch"
|
|
}
|
|
|
|
# Build page in development mode
|
|
def build_page_dev [page_name: string] {
|
|
print $"🔨 Building page: ($page_name) \(development mode\)"
|
|
|
|
let config = (load_page_config $page_name)
|
|
let page_dir = ($PAGES_DIR | path join $page_name)
|
|
|
|
cd $page_dir
|
|
nu ($FRAMEWORK_CORE | path join "build.nu") --dev --verbose
|
|
}
|
|
|
|
# Build page in production mode
|
|
def build_page_prod [page_name: string] {
|
|
print $"🏭 Building page: ($page_name) \(production mode\)"
|
|
|
|
let config = (load_page_config $page_name)
|
|
let page_dir = ($PAGES_DIR | path join $page_name)
|
|
|
|
cd $page_dir
|
|
nu ($FRAMEWORK_CORE | path join "prod.nu") --analyze --compress --verbose
|
|
}
|
|
|
|
# Start development server for page
|
|
def dev_page [page_name: string, port: int, watch: bool] {
|
|
print $"🚀 Starting development server for: ($page_name)"
|
|
|
|
let page_dir = ($PAGES_DIR | path join $page_name)
|
|
cd $page_dir
|
|
|
|
if $watch {
|
|
nu ($FRAMEWORK_CORE | path join "dev.nu") --watch --port $port
|
|
} else {
|
|
nu ($FRAMEWORK_CORE | path join "dev.nu") --port $port
|
|
}
|
|
}
|
|
|
|
# List all pages
|
|
def list_pages [] {
|
|
print "📄 Available pages:"
|
|
|
|
if not ($PAGES_DIR | path exists) {
|
|
print " No pages found"
|
|
return
|
|
}
|
|
|
|
ls $PAGES_DIR | where type == dir | each { |page|
|
|
let page_name = ($page.name | path basename)
|
|
let config_path = ($PAGES_DIR | path join $page_name "config.toml")
|
|
|
|
if ($config_path | path exists) {
|
|
let config = (open $config_path)
|
|
print $" 📝 ($page_name) - ($config.page.title) [($config.page.type)]"
|
|
} else {
|
|
print $" 📝 ($page_name) - No config found"
|
|
}
|
|
}
|
|
}
|
|
|
|
# List shared assets
|
|
def list_shared_assets [] {
|
|
print "🔗 Shared assets:"
|
|
|
|
if not ($SHARED_ASSETS | path exists) {
|
|
print " No shared assets found"
|
|
return
|
|
}
|
|
|
|
# List CSS assets
|
|
let css_dir = ($SHARED_ASSETS | path join "css")
|
|
if ($css_dir | path exists) {
|
|
print " 📄 CSS assets:"
|
|
ls -la $css_dir | each { |item|
|
|
if $item.type == "dir" {
|
|
let subdir = ($item.name | path basename)
|
|
print $" 📁 ($subdir)/"
|
|
let css_files = (glob ($css_dir | path join $subdir "*.css"))
|
|
$css_files | each { |file|
|
|
let basename = ($file | path basename)
|
|
let size = (ls $file | get size | first | into int)
|
|
let size_kb = ($size / 1024 | math round -p 1)
|
|
print $" 📝 ($basename) - ($size_kb) KB"
|
|
}
|
|
} else if ($item.name | str ends-with ".css") {
|
|
let basename = ($item.name | path basename)
|
|
let size = ($item.size | into int)
|
|
let size_kb = ($size / 1024 | math round -p 1)
|
|
print $" 📝 ($basename) - ($size_kb) KB"
|
|
}
|
|
}
|
|
}
|
|
|
|
# List SVG assets
|
|
let svg_dir = ($SHARED_ASSETS | path join "svg")
|
|
if ($svg_dir | path exists) {
|
|
print " 🖼️ SVG assets:"
|
|
let svg_files = (glob ($svg_dir | path join "*.svg"))
|
|
$svg_files | each { |file|
|
|
let basename = ($file | path basename)
|
|
let size = (ls $file | get size | first | into int)
|
|
let size_kb = ($size / 1024 | math round -p 1)
|
|
print $" 📝 ($basename) - ($size_kb) KB"
|
|
}
|
|
}
|
|
|
|
# List JS assets
|
|
let js_dir = ($SHARED_ASSETS | path join "js")
|
|
if ($js_dir | path exists) {
|
|
print " 📜 JavaScript assets:"
|
|
let js_files = (glob ($js_dir | path join "*.js"))
|
|
$js_files | each { |file|
|
|
let basename = ($file | path basename)
|
|
let size = (ls $file | get size | first | into int)
|
|
let size_kb = ($size / 1024 | math round -p 1)
|
|
print $" 📝 ($basename) - ($size_kb) KB"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Add shared asset
|
|
def add_shared_asset [file_path: string, asset_type: string] {
|
|
print $"📦 Adding shared asset: ($file_path) as ($asset_type)"
|
|
|
|
if not ($file_path | path exists) {
|
|
print $"❌ File not found: ($file_path)"
|
|
return
|
|
}
|
|
|
|
# Create shared assets directory structure
|
|
mkdir ($SHARED_ASSETS | path join $asset_type)
|
|
|
|
# Copy file to shared assets
|
|
let filename = ($file_path | path basename)
|
|
let dest_path = ($SHARED_ASSETS | path join $asset_type $filename)
|
|
|
|
cp $file_path $dest_path
|
|
|
|
print $"✅ Added shared asset: @shared/($asset_type)/($filename)"
|
|
print $" Use in page config: \"@shared/($asset_type)/($filename)\""
|
|
}
|
|
|
|
# Create multi-page site
|
|
def create_site [name: string] {
|
|
print $"🌐 Creating site: ($name)"
|
|
|
|
let site_dir = ($SITES_DIR | path join $name)
|
|
|
|
if ($site_dir | path exists) {
|
|
print $"❌ Site ($name) already exists"
|
|
return
|
|
}
|
|
|
|
# Create site structure
|
|
mkdir ($site_dir | path join "pages")
|
|
mkdir ($site_dir | path join "shared")
|
|
mkdir ($site_dir | path join "dist")
|
|
|
|
# Create site configuration
|
|
let site_config = $'[site]
|
|
name = "($name)"
|
|
title = "($name)"
|
|
description = "Multi-page site created with Web Builder Framework"
|
|
|
|
[build]
|
|
output_dir = "dist"
|
|
shared_assets = true
|
|
|
|
[pages]
|
|
# Add page references here
|
|
# homepage = { path = "homepage", template = "landing" }
|
|
'
|
|
|
|
$site_config | save ($site_dir | path join "site.toml")
|
|
|
|
print $"✅ Site ($name) created successfully!"
|
|
print $"📁 Location: ($site_dir)"
|
|
print $"🎯 Add pages to the site configuration"
|
|
}
|
|
|
|
# Build multi-page site
|
|
def build_site [name: string] {
|
|
print $"🏗️ Building site: ($name)"
|
|
|
|
let site_dir = ($SITES_DIR | path join $name)
|
|
let site_config_path = ($site_dir | path join "site.toml")
|
|
|
|
if not ($site_config_path | path exists) {
|
|
print $"❌ Site config not found: ($site_config_path)"
|
|
return
|
|
}
|
|
|
|
let site_config = (open $site_config_path)
|
|
|
|
print $"📦 Building all pages for site: ($site_config.site.title)"
|
|
print " (Site building not fully implemented yet - build individual pages for now)"
|
|
|
|
# TODO: Implement multi-page site building
|
|
# This would iterate through all pages defined in site.toml
|
|
# and build them into a unified site structure
|
|
}
|
|
|
|
# List all sites
|
|
def list_sites [] {
|
|
print "🌐 Available sites:"
|
|
|
|
if not ($SITES_DIR | path exists) {
|
|
print " No sites found"
|
|
return
|
|
}
|
|
|
|
ls $SITES_DIR | where type == dir | each { |site|
|
|
let site_name = ($site.name | path basename)
|
|
let config_path = ($SITES_DIR | path join $site_name "site.toml")
|
|
|
|
if ($config_path | path exists) {
|
|
let config = (open $config_path)
|
|
print $" 🌐 ($site_name) - ($config.site.title)"
|
|
} else {
|
|
print $" 🌐 ($site_name) - No config found"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Help functions
|
|
def show_help [] {
|
|
print "🌐 Web Builder Framework"
|
|
print "========================"
|
|
print ""
|
|
print "Commands:"
|
|
print " wb page <subcommand> - Manage individual pages"
|
|
print " wb site <subcommand> - Manage multi-page sites"
|
|
print " wb shared <subcommand> - Manage shared assets"
|
|
print " wb init <name> - Initialize new project"
|
|
print " wb dev <page> [--port N] - Quick dev server (shortcut)"
|
|
print " wb build-all [--prod] - Build all pages"
|
|
print " wb clean [--all] - Clean build artifacts"
|
|
print " wb help - Show this help"
|
|
print ""
|
|
print "Examples:"
|
|
print " wb page create homepage"
|
|
print " wb dev homepage - Quick development server"
|
|
print " wb page build homepage --prod"
|
|
print " wb build-all --prod - Build all pages for production"
|
|
print " wb clean --all - Deep clean all artifacts"
|
|
print " wb shared list"
|
|
print ""
|
|
print "💡 For faster JS minification, install SWC:"
|
|
print " npm install -g @swc/cli @swc/core"
|
|
}
|
|
|
|
def show_page_help [] {
|
|
print "📄 Page Management"
|
|
print "=================="
|
|
print ""
|
|
print "Commands:"
|
|
print " wb page create <name> [--template <type>] - Create new page"
|
|
print " wb page build <name> [--dev|--prod] - Build page"
|
|
print " wb page dev <name> [--watch] [--port N] - Start dev server"
|
|
print " wb page list - List all pages"
|
|
print ""
|
|
print "Templates: single, landing, article, poster"
|
|
print ""
|
|
print "Examples:"
|
|
print " wb page create about --template landing"
|
|
print " wb page dev homepage --watch --port 3000"
|
|
}
|
|
|
|
def show_site_help [] {
|
|
print "🌐 Site Management"
|
|
print "=================="
|
|
print " wb site create <name> - Create multi-page site"
|
|
print " wb site build <name> - Build entire site"
|
|
print " wb site list - List all sites"
|
|
}
|
|
|
|
def show_shared_help [] {
|
|
print "🔗 Shared Assets"
|
|
print "================"
|
|
print " wb shared add <file> --type <css|svg|js> - Add shared asset"
|
|
print " wb shared list - List shared assets"
|
|
}
|
|
|
|
# Shortcut commands
|
|
def "main dev" [page?: string = "homepage", --port: int = 8080] {
|
|
if ($page | is-empty) {
|
|
print "Error: Page name required"
|
|
print "Usage: wb dev <page> [--port N]"
|
|
return
|
|
}
|
|
|
|
print $"🚀 Starting development server for: ($page)"
|
|
dev_page $page $port true # true for watch mode
|
|
}
|
|
|
|
def "main build-all" [--prod(-p)] {
|
|
print "🏗️ Building all pages..."
|
|
|
|
if not ($PAGES_DIR | path exists) {
|
|
print "❌ No pages directory found"
|
|
return
|
|
}
|
|
|
|
let pages = (ls $PAGES_DIR | where type == dir | get name)
|
|
|
|
if ($pages | length) == 0 {
|
|
print "📄 No pages found to build"
|
|
return
|
|
}
|
|
|
|
print $"📦 Found ($pages | length) pages to build"
|
|
|
|
for page_path in $pages {
|
|
let page_name = ($page_path | path basename)
|
|
let config_path = ($page_path | path join "config.toml")
|
|
|
|
if ($config_path | path exists) {
|
|
print $"🔨 Building ($page_name)..."
|
|
|
|
if $prod {
|
|
build_page_prod $page_name
|
|
} else {
|
|
build_page_dev $page_name
|
|
}
|
|
|
|
print $"✅ ($page_name) completed"
|
|
} else {
|
|
print $"⚠️ Skipping ($page_name) - no config found"
|
|
}
|
|
}
|
|
|
|
print "🎉 All pages built successfully!"
|
|
}
|
|
|
|
def "main clean" [--all(-a)] {
|
|
print "🧹 Cleaning build artifacts..."
|
|
|
|
if not ($PAGES_DIR | path exists) {
|
|
print "❌ No pages directory found"
|
|
return
|
|
}
|
|
|
|
let pages = (ls $PAGES_DIR | where type == dir | get name)
|
|
mut cleaned_count = 0
|
|
|
|
for page_path in $pages {
|
|
let page_name = ($page_path | path basename)
|
|
let dist_dir = ($page_path | path join "dist")
|
|
|
|
if ($dist_dir | path exists) {
|
|
print $"🗑️ Cleaning ($page_name)/dist/"
|
|
rm -rf $dist_dir
|
|
$cleaned_count = ($cleaned_count + 1)
|
|
}
|
|
}
|
|
|
|
if $all {
|
|
# Clean additional artifacts
|
|
print "🧽 Deep cleaning..."
|
|
|
|
# Clean compressed files
|
|
for page_path in $pages {
|
|
let page_name = ($page_path | path basename)
|
|
glob ($page_path | path join "**/*.gz") | each { |file|
|
|
if ($file | path exists) {
|
|
print $"🗑️ Removing ($file | path basename)"
|
|
rm $file
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
print $"✅ Cleaned ($cleaned_count) pages"
|
|
} |