static-site-builder 0.1.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -64
- data/README.md +92 -286
- data/bin/generate +3 -40
- data/exe/static-site-builder +1 -40
- data/lib/generator.rb +627 -751
- data/lib/static_site_builder/builder.rb +265 -467
- data/lib/static_site_builder/dev_server.rb +119 -0
- data/lib/static_site_builder/version.rb +1 -1
- data/lib/static_site_builder/websocket_server.rb +97 -21
- data/lib/static_site_builder.rb +17 -4
- metadata +31 -17
- data/ARCHITECTURE.md +0 -61
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'listen'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'static_site_builder/websocket_server'
|
|
6
|
+
|
|
7
|
+
begin
|
|
8
|
+
require 'webrick'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
# webrick may not be available in all environments
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module StaticSiteBuilder
|
|
14
|
+
# Development server with file watching and live reload
|
|
15
|
+
#
|
|
16
|
+
# Watches for file changes using the listen gem, rebuilds the site automatically,
|
|
17
|
+
# and provides live reload via WebSocket server.
|
|
18
|
+
class DevServer
|
|
19
|
+
def initialize(root: Dir.pwd, port: nil, ws_port: nil)
|
|
20
|
+
@root = Pathname.new(root)
|
|
21
|
+
@port = port || ENV['PORT']&.to_i || DEFAULT_PORT
|
|
22
|
+
@ws_port = ws_port || ENV['WS_PORT']&.to_i || DEFAULT_WS_PORT
|
|
23
|
+
@dist_dir = @root.join('dist')
|
|
24
|
+
@reload_file = @root.join('.reload')
|
|
25
|
+
@listener = nil
|
|
26
|
+
@ws_server = nil
|
|
27
|
+
@http_server = nil
|
|
28
|
+
@running = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Start the development server
|
|
32
|
+
#
|
|
33
|
+
# Builds the site initially, starts file watcher, WebSocket server, and HTTP server
|
|
34
|
+
def start
|
|
35
|
+
puts 'Building site...'
|
|
36
|
+
build_site
|
|
37
|
+
|
|
38
|
+
puts "\nStarting development server..."
|
|
39
|
+
puts " HTTP server: http://localhost:#{@port}"
|
|
40
|
+
puts " WebSocket server: ws://localhost:#{@ws_port}"
|
|
41
|
+
puts " Watching for changes... (Ctrl+C to stop)\n"
|
|
42
|
+
|
|
43
|
+
start_websocket_server
|
|
44
|
+
start_file_watcher
|
|
45
|
+
start_http_server
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Stop the development server
|
|
49
|
+
def stop
|
|
50
|
+
@running = false
|
|
51
|
+
@listener&.stop
|
|
52
|
+
@ws_server&.stop
|
|
53
|
+
@http_server&.shutdown if @http_server
|
|
54
|
+
puts "\nShutting down..."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_site
|
|
60
|
+
ENV['LIVE_RELOAD'] = 'true'
|
|
61
|
+
ENV['WS_PORT'] = @ws_port.to_s
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
require 'rake'
|
|
65
|
+
Rake::Task['build:html'].invoke
|
|
66
|
+
rescue LoadError, RuntimeError
|
|
67
|
+
# If Rakefile not loaded or task not found, build directly
|
|
68
|
+
require 'static_site_builder'
|
|
69
|
+
builder = Builder.new(root: @root.to_s)
|
|
70
|
+
builder.build
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def start_websocket_server
|
|
75
|
+
@ws_server = WebSocketServer.new(port: @ws_port, reload_file: @reload_file)
|
|
76
|
+
@ws_server.start
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def start_file_watcher
|
|
80
|
+
@running = true
|
|
81
|
+
watched_dirs = ['app', 'config'].select { |dir| @root.join(dir).exist? }
|
|
82
|
+
return if watched_dirs.empty?
|
|
83
|
+
|
|
84
|
+
@listener = Listen.to(*watched_dirs.map { |dir| @root.join(dir).to_s }) do |modified, added, removed|
|
|
85
|
+
next if modified.empty? && added.empty? && removed.empty?
|
|
86
|
+
|
|
87
|
+
files_changed = (modified + added + removed).select do |file|
|
|
88
|
+
file.end_with?('.erb', '.rb', '.js')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if files_changed.any?
|
|
92
|
+
puts "\nFiles changed, rebuilding..."
|
|
93
|
+
build_site
|
|
94
|
+
puts 'Rebuild complete'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@listener.start
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def start_http_server
|
|
102
|
+
unless defined?(WEBrick)
|
|
103
|
+
raise 'webrick gem is required for the development server. Add "gem \'webrick\'" to your Gemfile.'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@http_server = WEBrick::HTTPServer.new(
|
|
107
|
+
Port: @port,
|
|
108
|
+
DocumentRoot: @dist_dir.to_s,
|
|
109
|
+
BindAddress: '127.0.0.1'
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
trap('INT') { stop }
|
|
113
|
+
trap('TERM') { stop }
|
|
114
|
+
|
|
115
|
+
@http_server.start
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
@@ -6,66 +6,126 @@ require "digest/sha1"
|
|
|
6
6
|
require "pathname"
|
|
7
7
|
|
|
8
8
|
module StaticSiteBuilder
|
|
9
|
-
#
|
|
9
|
+
# WebSocket server for live reload functionality during development.
|
|
10
|
+
#
|
|
11
|
+
# Watches for rebuild notifications via a reload file and broadcasts reload
|
|
12
|
+
# messages to connected browser clients, enabling automatic page refresh.
|
|
10
13
|
class WebSocketServer
|
|
11
|
-
|
|
14
|
+
# Sleep intervals for thread operations (in seconds)
|
|
15
|
+
ACCEPT_RETRY_INTERVAL = 0.1 # Retry delay when accepting connections fails
|
|
16
|
+
WATCH_POLL_INTERVAL = 0.3 # How often to check for rebuild notifications
|
|
17
|
+
CLIENT_KEEPALIVE_INTERVAL = 1 # Keep-alive check interval for client connections
|
|
18
|
+
|
|
19
|
+
# Initializes a new WebSocket server instance.
|
|
20
|
+
#
|
|
21
|
+
# @param port [Integer] Port number for the WebSocket server (default: 3001)
|
|
22
|
+
# @param reload_file [Pathname, nil] Path to the reload notification file. If nil, defaults to .reload in current directory.
|
|
23
|
+
def initialize(port: StaticSiteBuilder::DEFAULT_WS_PORT, reload_file: nil)
|
|
12
24
|
@port = port
|
|
13
25
|
@reload_file = reload_file || Pathname.new(Dir.pwd).join(".reload")
|
|
14
26
|
@clients = []
|
|
15
27
|
@running = false
|
|
16
28
|
end
|
|
17
29
|
|
|
30
|
+
# Starts the WebSocket server and begins watching for rebuild notifications.
|
|
31
|
+
#
|
|
32
|
+
# Spawns two background threads: one for accepting client connections and
|
|
33
|
+
# one for watching the reload file for changes. Returns immediately after
|
|
34
|
+
# starting the threads.
|
|
35
|
+
#
|
|
36
|
+
# @return [void]
|
|
18
37
|
def start
|
|
19
38
|
@running = true
|
|
20
39
|
@server = TCPServer.new("127.0.0.1", @port)
|
|
21
40
|
|
|
22
|
-
# Initialize reload file
|
|
23
|
-
File.write(@reload_file, Time.
|
|
41
|
+
# Initialize reload file if it doesn't exist
|
|
42
|
+
File.write(@reload_file, Time.current.to_f.to_s) unless @reload_file.exist?
|
|
24
43
|
@last_mtime = @reload_file.mtime
|
|
25
44
|
|
|
26
|
-
# Accept connections in background
|
|
45
|
+
# Accept client connections in background thread
|
|
27
46
|
@accept_thread = Thread.new do
|
|
28
47
|
while @running
|
|
29
48
|
begin
|
|
30
49
|
client = @server.accept
|
|
31
50
|
Thread.new { handle_client(client) }
|
|
32
|
-
rescue
|
|
33
|
-
|
|
51
|
+
rescue IOError, Errno::EBADF, Errno::ECONNABORTED
|
|
52
|
+
# Connection errors are expected during shutdown or network issues
|
|
53
|
+
# Retry accepting connections unless server is stopping
|
|
54
|
+
sleep ACCEPT_RETRY_INTERVAL
|
|
34
55
|
break unless @running
|
|
35
56
|
end
|
|
36
57
|
end
|
|
37
58
|
end
|
|
38
59
|
|
|
39
|
-
# Watch for
|
|
60
|
+
# Watch for rebuild notifications in background thread
|
|
40
61
|
@watch_thread = Thread.new do
|
|
41
62
|
while @running
|
|
42
63
|
begin
|
|
43
|
-
sleep
|
|
64
|
+
sleep WATCH_POLL_INTERVAL
|
|
44
65
|
if @reload_file.exist? && @reload_file.mtime > @last_mtime
|
|
45
66
|
@last_mtime = @reload_file.mtime
|
|
46
67
|
broadcast("reload")
|
|
47
68
|
end
|
|
48
|
-
rescue
|
|
49
|
-
|
|
69
|
+
rescue Errno::ENOENT, Errno::EACCES, SystemCallError
|
|
70
|
+
# File system errors during watch are non-fatal
|
|
71
|
+
# Continue watching unless server is stopping
|
|
72
|
+
sleep WATCH_POLL_INTERVAL
|
|
50
73
|
break unless @running
|
|
51
74
|
end
|
|
52
75
|
end
|
|
53
76
|
end
|
|
54
77
|
end
|
|
55
78
|
|
|
79
|
+
# Stops the WebSocket server gracefully.
|
|
80
|
+
#
|
|
81
|
+
# Closes all client connections, stops accepting new connections, stops
|
|
82
|
+
# watching for rebuilds, and closes the server socket.
|
|
83
|
+
#
|
|
84
|
+
# @return [void]
|
|
56
85
|
def stop
|
|
57
86
|
@running = false
|
|
58
|
-
@clients.each { |
|
|
59
|
-
@server
|
|
60
|
-
@accept_thread
|
|
61
|
-
@watch_thread
|
|
87
|
+
@clients.each { |client| safe_close(client) }
|
|
88
|
+
safe_close(@server) if @server
|
|
89
|
+
safe_kill_thread(@accept_thread)
|
|
90
|
+
safe_kill_thread(@watch_thread)
|
|
62
91
|
end
|
|
63
92
|
|
|
64
93
|
private
|
|
65
94
|
|
|
95
|
+
# Safely closes a socket or connection, ignoring common connection errors.
|
|
96
|
+
#
|
|
97
|
+
# Used to handle cases where connections may already be closed or reset,
|
|
98
|
+
# preventing exceptions during cleanup.
|
|
99
|
+
#
|
|
100
|
+
# @param io [IO] The IO object to close
|
|
101
|
+
def safe_close(io)
|
|
102
|
+
return if io.nil? || io.closed?
|
|
103
|
+
|
|
104
|
+
io.close
|
|
105
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::EBADF
|
|
106
|
+
# Connection already closed or reset - this is expected during shutdown
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Safely terminates a thread, ignoring errors if the thread is already dead.
|
|
110
|
+
#
|
|
111
|
+
# @param thread [Thread, nil] The thread to kill
|
|
112
|
+
def safe_kill_thread(thread)
|
|
113
|
+
return unless thread&.alive?
|
|
114
|
+
|
|
115
|
+
thread.kill
|
|
116
|
+
rescue ThreadError
|
|
117
|
+
# Thread already dead - this is expected during shutdown
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Handles a new client connection, performing WebSocket handshake.
|
|
121
|
+
#
|
|
122
|
+
# Reads the HTTP upgrade request, validates it's a WebSocket request, performs
|
|
123
|
+
# the handshake, and keeps the connection alive for receiving reload messages.
|
|
124
|
+
#
|
|
125
|
+
# @param client [TCPSocket] The client socket connection
|
|
66
126
|
def handle_client(client)
|
|
67
127
|
begin
|
|
68
|
-
# Read
|
|
128
|
+
# Read HTTP upgrade request headers
|
|
69
129
|
request = client.gets
|
|
70
130
|
headers = {}
|
|
71
131
|
while (line = client.gets.chomp) != ""
|
|
@@ -74,6 +134,7 @@ module StaticSiteBuilder
|
|
|
74
134
|
end
|
|
75
135
|
|
|
76
136
|
if headers["Upgrade"]&.downcase == "websocket"
|
|
137
|
+
# Perform WebSocket handshake
|
|
77
138
|
key = headers["Sec-WebSocket-Key"]
|
|
78
139
|
accept = Base64.strict_encode64(Digest::SHA1.digest(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
|
|
79
140
|
|
|
@@ -84,32 +145,47 @@ module StaticSiteBuilder
|
|
|
84
145
|
|
|
85
146
|
@clients << client
|
|
86
147
|
|
|
87
|
-
# Keep connection alive
|
|
148
|
+
# Keep connection alive, waiting for reload messages
|
|
88
149
|
loop do
|
|
89
|
-
sleep
|
|
150
|
+
sleep CLIENT_KEEPALIVE_INTERVAL
|
|
90
151
|
break unless @running
|
|
91
152
|
break if client.closed?
|
|
92
153
|
end
|
|
93
154
|
else
|
|
155
|
+
# Not a WebSocket request - close connection
|
|
94
156
|
client.close
|
|
95
157
|
end
|
|
96
|
-
rescue
|
|
158
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
|
|
159
|
+
# Client connection errors are expected (e.g., browser tab closed)
|
|
160
|
+
# Clean up and continue accepting other connections
|
|
97
161
|
@clients.delete(client)
|
|
98
|
-
client
|
|
162
|
+
safe_close(client)
|
|
99
163
|
end
|
|
100
164
|
end
|
|
101
165
|
|
|
166
|
+
# Broadcasts a reload message to all connected clients.
|
|
167
|
+
#
|
|
168
|
+
# @param message [String] The message to broadcast (typically "reload")
|
|
102
169
|
def broadcast(message)
|
|
103
170
|
frame = create_frame(message)
|
|
104
171
|
@clients.dup.each do |client|
|
|
105
172
|
begin
|
|
106
173
|
client.write(frame) unless client.closed?
|
|
107
|
-
rescue
|
|
174
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
|
|
175
|
+
# Broadcast errors are expected when clients disconnect
|
|
176
|
+
# Remove failed client and continue broadcasting to others
|
|
108
177
|
@clients.delete(client)
|
|
109
178
|
end
|
|
110
179
|
end
|
|
111
180
|
end
|
|
112
181
|
|
|
182
|
+
# Creates a WebSocket frame for the given message.
|
|
183
|
+
#
|
|
184
|
+
# Implements WebSocket frame encoding according to RFC 6455, supporting
|
|
185
|
+
# messages up to 2^32 bytes in length.
|
|
186
|
+
#
|
|
187
|
+
# @param message [String] The message to encode
|
|
188
|
+
# @return [String] Binary WebSocket frame data
|
|
113
189
|
def create_frame(message)
|
|
114
190
|
data = message.dup.force_encoding("BINARY")
|
|
115
191
|
length = data.bytesize
|
data/lib/static_site_builder.rb
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require_relative "static_site_builder/websocket_server"
|
|
6
|
-
require_relative "generator"
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext'
|
|
7
5
|
|
|
8
6
|
module StaticSiteBuilder
|
|
9
7
|
# Main module for the static site builder gem
|
|
8
|
+
|
|
9
|
+
# Default WebSocket port for live reload server
|
|
10
|
+
DEFAULT_WS_PORT = 3001
|
|
11
|
+
|
|
12
|
+
# Default HTTP port for development server
|
|
13
|
+
DEFAULT_PORT = 3000
|
|
14
|
+
|
|
15
|
+
# Default layout name
|
|
16
|
+
DEFAULT_LAYOUT_NAME = 'application'
|
|
10
17
|
end
|
|
18
|
+
|
|
19
|
+
require_relative "static_site_builder/version"
|
|
20
|
+
require_relative "static_site_builder/builder"
|
|
21
|
+
require_relative "static_site_builder/dev_server"
|
|
22
|
+
require_relative "static_site_builder/websocket_server"
|
|
23
|
+
require_relative "generator"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: static-site-builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lukasz Czapiewski
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '7.1'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '7.1'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: base64
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,33 +38,47 @@ dependencies:
|
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0.1'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: listen
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '3.8'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '3.8'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
55
|
+
name: meta-tags
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0
|
|
60
|
+
version: '2.0'
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0
|
|
67
|
+
version: '2.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rake
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '13.0'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: websocket
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,10 +107,10 @@ dependencies:
|
|
|
93
107
|
- - "~>"
|
|
94
108
|
- !ruby/object:Gem::Version
|
|
95
109
|
version: '3.0'
|
|
96
|
-
description:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
description: A Ruby gem for building static HTML sites. Uses ActionView to render
|
|
111
|
+
partials, layouts, and helpers using ERB. Compiles to static HTML with JavaScript
|
|
112
|
+
support. Simple and flexible - add your own JavaScript bundling and CSS processing
|
|
113
|
+
as needed. No backend required.
|
|
100
114
|
email:
|
|
101
115
|
- luke@mmtm.io
|
|
102
116
|
executables:
|
|
@@ -104,7 +118,6 @@ executables:
|
|
|
104
118
|
extensions: []
|
|
105
119
|
extra_rdoc_files: []
|
|
106
120
|
files:
|
|
107
|
-
- ARCHITECTURE.md
|
|
108
121
|
- CHANGELOG.md
|
|
109
122
|
- LICENSE
|
|
110
123
|
- README.md
|
|
@@ -113,6 +126,7 @@ files:
|
|
|
113
126
|
- lib/generator.rb
|
|
114
127
|
- lib/static_site_builder.rb
|
|
115
128
|
- lib/static_site_builder/builder.rb
|
|
129
|
+
- lib/static_site_builder/dev_server.rb
|
|
116
130
|
- lib/static_site_builder/version.rb
|
|
117
131
|
- lib/static_site_builder/websocket_server.rb
|
|
118
132
|
homepage: https://github.com/Ancez/static-site-builder
|
|
@@ -128,14 +142,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
128
142
|
requirements:
|
|
129
143
|
- - ">="
|
|
130
144
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '3.
|
|
145
|
+
version: '3.1'
|
|
132
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
147
|
requirements:
|
|
134
148
|
- - ">="
|
|
135
149
|
- !ruby/object:Gem::Version
|
|
136
150
|
version: '0'
|
|
137
151
|
requirements: []
|
|
138
|
-
rubygems_version:
|
|
152
|
+
rubygems_version: 4.0.0
|
|
139
153
|
specification_version: 4
|
|
140
|
-
summary: Build static HTML sites with working JavaScript
|
|
154
|
+
summary: Build static HTML sites from ERB with working JavaScript
|
|
141
155
|
test_files: []
|
data/ARCHITECTURE.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Architecture
|
|
2
|
-
|
|
3
|
-
This project is a **generator tool**, similar to `rails new`. It creates static site projects that use standard Ruby gems.
|
|
4
|
-
|
|
5
|
-
## Structure
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
static-site-generator/
|
|
9
|
-
├── lib/
|
|
10
|
-
│ └── generator.rb # Main generator logic
|
|
11
|
-
├── bin/
|
|
12
|
-
│ └── generate # CLI entry point
|
|
13
|
-
├── exe/
|
|
14
|
-
│ └── static-site-generator # Gem executable
|
|
15
|
-
└── templates/ # Template files (future)
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## How It Works
|
|
19
|
-
|
|
20
|
-
1. **Generator** (`static-site-generator` gem)
|
|
21
|
-
- Creates project structure
|
|
22
|
-
- Generates Gemfile with dependencies
|
|
23
|
-
- Creates config files
|
|
24
|
-
- Sets up build tasks
|
|
25
|
-
|
|
26
|
-
2. **Builder Gem** (`static-site-builder` gem) - **Separate gem**
|
|
27
|
-
- Handles ERB/Phlex compilation
|
|
28
|
-
- Manages asset copying
|
|
29
|
-
- Generates importmap JSON
|
|
30
|
-
- Outputs static HTML
|
|
31
|
-
|
|
32
|
-
3. **Standard Gems** - Used by generated sites
|
|
33
|
-
- `importmap-rails` - Importmap support
|
|
34
|
-
- `phlex-rails` - Phlex components
|
|
35
|
-
- `static-site-builder` - Core builder functionality
|
|
36
|
-
|
|
37
|
-
## Generated Site Structure
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
my-site/
|
|
41
|
-
├── Gemfile # Dependencies
|
|
42
|
-
├── package.json # JS dependencies (if needed)
|
|
43
|
-
├── Rakefile # Build tasks
|
|
44
|
-
├── config/
|
|
45
|
-
│ └── importmap.rb # Importmap config
|
|
46
|
-
├── app/
|
|
47
|
-
│ ├── views/
|
|
48
|
-
│ ├── javascript/
|
|
49
|
-
│ └── assets/
|
|
50
|
-
└── lib/
|
|
51
|
-
└── site_builder.rb # Thin wrapper using static-site-builder gem
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Separation of Concerns
|
|
55
|
-
|
|
56
|
-
- **Generator** - Creates projects (this repo)
|
|
57
|
-
- **Builder** - Compiles sites (`static-site-builder` gem)
|
|
58
|
-
- **Standard Gems** - Provide functionality (importmap-rails, etc.)
|
|
59
|
-
|
|
60
|
-
This keeps the generator lightweight and maintainable.
|
|
61
|
-
|