confinement 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/exe/confinement +38 -33
- data/lib/confinement.rb +312 -223
- data/lib/confinement/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a991bdb8646d092772f6ae7154bc34c7c89ac4faf8d51822e7b1361ed8f7ec05
|
4
|
+
data.tar.gz: 073003314b979b4bbf03dc3baf487030e6a07199feef361b5addad3928a7b06c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72cffafd98539f4655a039c674d9324e998ef508610bad89f9f6e21a5d06cd158487413b60bdeb6b961d76f338c44013bb4ff9f35ea0733ffba74856f01046d8
|
7
|
+
data.tar.gz: 1d6076cc1b834d0a3bf3cd9fcaee3dc7cabd6e048e3f5dc298a7d201c6b60d44f63f0a5f164378c0a9883d9a19e6c83e422a0b9b07d2727164e6e7f2a2493343
|
data/Gemfile.lock
CHANGED
data/exe/confinement
CHANGED
@@ -91,7 +91,7 @@ module Confinement
|
|
91
91
|
|
92
92
|
root = Pathname.new(argv.first).expand_path
|
93
93
|
|
94
|
-
templates =
|
94
|
+
templates = data.split(/^==> /)[1..-1]
|
95
95
|
templates = templates.filter_map do |template|
|
96
96
|
path, *body = template.lines
|
97
97
|
body = body.join("")
|
@@ -109,8 +109,6 @@ module Confinement
|
|
109
109
|
end
|
110
110
|
|
111
111
|
Dir.chdir(root) do
|
112
|
-
run(["yarn", "init", "--yes"])
|
113
|
-
|
114
112
|
run(["yarn", "add", "--dev", "parcel@nightly"])
|
115
113
|
end
|
116
114
|
end
|
@@ -165,6 +163,11 @@ module Confinement
|
|
165
163
|
|
166
164
|
/^y/i.match?(answer)
|
167
165
|
end
|
166
|
+
|
167
|
+
def data
|
168
|
+
contents = File.read(__FILE__)
|
169
|
+
contents.split(/^__END__$/)[1]
|
170
|
+
end
|
168
171
|
end
|
169
172
|
end
|
170
173
|
end
|
@@ -177,7 +180,7 @@ require "confinement"
|
|
177
180
|
|
178
181
|
Confinement.root = __dir__
|
179
182
|
|
180
|
-
Confinement.site = Confinement::
|
183
|
+
Confinement.site = Confinement::Site.new(
|
181
184
|
root: Confinement.root,
|
182
185
|
assets: "assets",
|
183
186
|
contents: "contents",
|
@@ -190,48 +193,50 @@ Confinement.site = Confinement::Builder.new(
|
|
190
193
|
==> build.rb
|
191
194
|
require_relative "boot"
|
192
195
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
dest["/"] = Confinement::Content.new(
|
197
|
-
layout: layouts.join("default.html.erb"),
|
198
|
-
input_path: contents.join("index.html.erb"),
|
199
|
-
renderers: [Confinement::Renderer::Erb.new]
|
200
|
-
)
|
201
|
-
end
|
202
|
-
|
203
|
-
Confinement.site.assets do |assets, dest|
|
204
|
-
dest["/assets/application.js"] = Confinement::Asset.new(
|
205
|
-
input_path: assets.join("application.js"),
|
206
|
-
entrypoint: true,
|
207
|
-
)
|
196
|
+
Confinement.site.build do |assets:, layouts:, contents:, routes:|
|
197
|
+
assets.init("application.js", entrypoint: true)
|
198
|
+
assets.init("application.css", entrypoint: false)
|
208
199
|
|
209
|
-
|
210
|
-
input_path: assets.join("application.css"),
|
211
|
-
entrypoint: false,
|
212
|
-
)
|
213
|
-
end
|
200
|
+
layouts.init("default.html.erb")
|
214
201
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
)
|
202
|
+
routes["/"] = contents.init("index.html.erb") do |content|
|
203
|
+
content.layout = layouts["default.html.erb"]
|
204
|
+
end
|
219
205
|
end
|
220
206
|
|
221
207
|
==> write.rb
|
222
208
|
require_relative "build"
|
223
209
|
|
224
|
-
Confinement::Publish
|
225
|
-
|
226
|
-
|
210
|
+
Confinement::Publish.new(Confinement.site).write
|
211
|
+
|
212
|
+
==> package.json
|
213
|
+
{
|
214
|
+
"name": "website",
|
215
|
+
"version": "1.0.0",
|
216
|
+
"license": "UNLICENSED",
|
217
|
+
"devDependencies": {
|
218
|
+
},
|
219
|
+
"dependencies": {
|
220
|
+
}
|
221
|
+
}
|
227
222
|
|
228
223
|
==> assets/application.js
|
229
224
|
import "./application.css";
|
230
225
|
|
231
226
|
==> assets/application.css
|
227
|
+
body { color: blue }
|
232
228
|
|
233
229
|
==> layouts/default.html.erb
|
230
|
+
<!DOCTYPE HTML>
|
231
|
+
<html lang="en">
|
232
|
+
<head>
|
233
|
+
<meta charset="utf-8">
|
234
|
+
<link rel="stylesheet" href="<%= assets["application.css"].url_path %>" charset="utf-8">
|
235
|
+
</head>
|
236
|
+
<body>
|
237
|
+
<%= yield %>
|
238
|
+
</body>
|
239
|
+
</html>
|
234
240
|
|
235
241
|
==> contents/index.html.erb
|
236
|
-
|
237
|
-
|
242
|
+
hello!
|
data/lib/confinement.rb
CHANGED
@@ -14,6 +14,7 @@ require "yaml"
|
|
14
14
|
|
15
15
|
# gems
|
16
16
|
require "erubi"
|
17
|
+
require "erubi/capture_end"
|
17
18
|
|
18
19
|
# internal
|
19
20
|
require_relative "confinement/version"
|
@@ -58,7 +59,7 @@ module Confinement
|
|
58
59
|
|
59
60
|
return [{}, self] if matches["frontmatter"].nil?
|
60
61
|
|
61
|
-
frontmatter = YAML.
|
62
|
+
frontmatter = YAML.load(matches["frontmatter"], symbolize_names: true)
|
62
63
|
body = matches["body"] || ""
|
63
64
|
body = body.strip if strip
|
64
65
|
|
@@ -66,6 +67,10 @@ module Confinement
|
|
66
67
|
rescue ArgumentError
|
67
68
|
[{}, self]
|
68
69
|
end
|
70
|
+
|
71
|
+
def normalize_for_route
|
72
|
+
"/#{self}".squeeze("/")
|
73
|
+
end
|
69
74
|
end
|
70
75
|
end
|
71
76
|
|
@@ -88,12 +93,24 @@ module Confinement
|
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
91
|
-
class
|
92
|
-
def initialize(
|
96
|
+
class Site
|
97
|
+
def initialize(
|
98
|
+
root:,
|
99
|
+
assets:,
|
100
|
+
contents:,
|
101
|
+
layouts:,
|
102
|
+
view_context_helpers: [],
|
103
|
+
guesses: Renderer.guesses,
|
104
|
+
config: {}
|
105
|
+
)
|
93
106
|
@root = root
|
94
107
|
@assets = assets
|
95
108
|
@contents = contents
|
96
109
|
@layouts = layouts
|
110
|
+
|
111
|
+
@view_context_helpers = view_context_helpers
|
112
|
+
@guessing_registry = guesses
|
113
|
+
|
97
114
|
@config = {
|
98
115
|
index: config.fetch(:index, "index.html")
|
99
116
|
}
|
@@ -101,167 +118,204 @@ module Confinement
|
|
101
118
|
@output_root = root.concat(config.fetch(:destination_root, "public"))
|
102
119
|
@assets_root = @output_root.concat(config.fetch(:assets_subdirectory, "assets"))
|
103
120
|
|
104
|
-
@
|
121
|
+
@route_identifiers = RouteIdentifiers.new
|
122
|
+
@asset_blobs = Blobs.new(scoped_root: assets_path, file_abstraction_class: Asset)
|
123
|
+
@content_blobs = Blobs.new(scoped_root: contents_path, file_abstraction_class: Content)
|
124
|
+
@layout_blobs = Blobs.new(scoped_root: layouts_path, file_abstraction_class: Layout)
|
105
125
|
end
|
106
126
|
|
107
127
|
attr_reader :root
|
108
128
|
attr_reader :output_root
|
109
129
|
attr_reader :assets_root
|
110
|
-
attr_reader :representation
|
111
130
|
attr_reader :config
|
112
131
|
|
113
|
-
|
114
|
-
|
115
|
-
|
132
|
+
attr_reader :route_identifiers
|
133
|
+
attr_reader :asset_blobs
|
134
|
+
attr_reader :content_blobs
|
135
|
+
attr_reader :layout_blobs
|
116
136
|
|
117
|
-
|
118
|
-
|
119
|
-
end
|
137
|
+
attr_reader :view_context_helpers
|
138
|
+
attr_reader :guessing_registry
|
120
139
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
140
|
+
def build
|
141
|
+
yield(
|
142
|
+
assets: @asset_blobs,
|
143
|
+
layouts: @layout_blobs,
|
144
|
+
contents: @content_blobs,
|
145
|
+
routes: @route_identifiers
|
146
|
+
)
|
124
147
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
148
|
+
guesser = Rendering::Guesser.new(guessing_registry)
|
149
|
+
guess_renderers(guesser, @layout_blobs)
|
150
|
+
guess_renderers(guesser, @content_blobs)
|
151
|
+
|
152
|
+
@asset_blobs.done!
|
153
|
+
@layout_blobs.done!
|
154
|
+
@content_blobs.done!
|
155
|
+
@route_identifiers.done!
|
129
156
|
|
130
|
-
|
157
|
+
nil
|
131
158
|
end
|
132
159
|
|
133
|
-
|
134
|
-
|
135
|
-
|
160
|
+
private
|
161
|
+
|
162
|
+
def guess_renderers(guesser, blobs)
|
163
|
+
blobs.send(:lookup).values.each do |blob|
|
164
|
+
blob.renderers = blob.renderers.flat_map do |renderer|
|
165
|
+
if renderer == :guess
|
166
|
+
guesser.call(blob.input_path)
|
167
|
+
else
|
168
|
+
renderer
|
169
|
+
end
|
170
|
+
end
|
136
171
|
end
|
172
|
+
end
|
137
173
|
|
138
|
-
|
174
|
+
def contents_path
|
175
|
+
@contents_path ||= @root.concat(@contents).cleanpath
|
139
176
|
end
|
140
177
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
end
|
178
|
+
def layouts_path
|
179
|
+
@layouts_path ||= @root.concat(@layouts).cleanpath
|
180
|
+
end
|
145
181
|
|
146
|
-
|
182
|
+
def assets_path
|
183
|
+
@assets_path ||= @root.concat(@assets).cleanpath
|
147
184
|
end
|
148
185
|
end
|
149
186
|
|
150
|
-
|
151
|
-
|
152
|
-
|
187
|
+
# RouteIdentifiers is called such because it doesn't hold the actual
|
188
|
+
# content's route. The content's own `url_path` does.
|
189
|
+
#
|
190
|
+
# This is mainly so that assets could be referenced internally with a static
|
191
|
+
# identifier even though it could have a hashed route.
|
192
|
+
class RouteIdentifiers
|
153
193
|
def initialize
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
194
|
+
self.lookup = {}
|
195
|
+
end
|
196
|
+
|
197
|
+
def done!
|
198
|
+
@done = true
|
158
199
|
end
|
159
200
|
|
160
|
-
|
161
|
-
|
162
|
-
attr_reader :only_contents
|
201
|
+
def [](route)
|
202
|
+
route = route.normalize_for_route
|
163
203
|
|
164
|
-
|
165
|
-
|
166
|
-
raise "Not represented!"
|
204
|
+
if !lookup.key?(route)
|
205
|
+
raise "Route is not defined"
|
167
206
|
end
|
168
207
|
|
169
|
-
|
208
|
+
self.lookup[route]
|
170
209
|
end
|
171
210
|
|
172
|
-
def
|
173
|
-
if
|
174
|
-
return enum_for(:each)
|
175
|
-
end
|
211
|
+
def []=(route, content)
|
212
|
+
raise "Can't add more routes after initial setup" if @done
|
176
213
|
|
177
|
-
|
178
|
-
|
214
|
+
route = route.normalize_for_route
|
215
|
+
|
216
|
+
if lookup.key?(route)
|
217
|
+
raise "Route already defined!"
|
179
218
|
end
|
219
|
+
|
220
|
+
content.url_path = route
|
221
|
+
|
222
|
+
lookup[route] = content
|
180
223
|
end
|
181
224
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
225
|
+
private
|
226
|
+
|
227
|
+
attr_accessor :lookup
|
228
|
+
end
|
229
|
+
|
230
|
+
class Blobs
|
231
|
+
def initialize(scoped_root:, file_abstraction_class:)
|
232
|
+
self.scoped_root = scoped_root
|
233
|
+
self.file_abstraction_class = file_abstraction_class
|
234
|
+
self.lookup = {}
|
235
|
+
@done = false
|
186
236
|
end
|
187
237
|
|
188
|
-
def
|
189
|
-
|
190
|
-
@only_assets.push(asset)
|
191
|
-
asset.url_path = identifier
|
192
|
-
end
|
238
|
+
def done!
|
239
|
+
@done = true
|
193
240
|
end
|
194
241
|
|
195
|
-
def
|
196
|
-
|
197
|
-
|
198
|
-
|
242
|
+
def [](relpath)
|
243
|
+
abspath = into_abspath(relpath)
|
244
|
+
|
245
|
+
if !lookup.key?(abspath)
|
246
|
+
raise "Don't know about this blob: #{abspath.inspect}"
|
199
247
|
end
|
200
|
-
end
|
201
248
|
|
202
|
-
|
203
|
-
LookupGetter.new(@lookup)
|
249
|
+
lookup[abspath]
|
204
250
|
end
|
205
251
|
|
206
|
-
def
|
207
|
-
|
252
|
+
def init(relpath, **initializer_options)
|
253
|
+
raise "Can't add more #{file_abstraction_class}s after the initial setup!" if @done
|
254
|
+
|
255
|
+
abspath = into_abspath(relpath)
|
256
|
+
lookup[abspath] ||= file_abstraction_class.new(input_path: abspath, **initializer_options)
|
257
|
+
yield lookup[abspath] if block_given?
|
258
|
+
lookup[abspath]
|
208
259
|
end
|
209
260
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
261
|
+
def init_many(pattern)
|
262
|
+
files_lookup.filter_map do |relpath, abspath|
|
263
|
+
if pattern.match?(relpath)
|
264
|
+
lookup[abspath.to_s] ||= file_abstraction_class.new(input_path: abspath)
|
265
|
+
end
|
214
266
|
end
|
267
|
+
end
|
215
268
|
|
216
|
-
|
217
|
-
@block&.call(key, value)
|
269
|
+
private
|
218
270
|
|
219
|
-
|
220
|
-
|
271
|
+
def into_abspath(relpath)
|
272
|
+
scoped_root.concat(relpath).cleanpath
|
221
273
|
end
|
222
274
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
@lookup.fetch(key)
|
230
|
-
end
|
275
|
+
def files_lookup
|
276
|
+
@files_lookup ||=
|
277
|
+
scoped_root
|
278
|
+
.glob("**/*")
|
279
|
+
.map { |path| [path.relative_path_from(scoped_root).to_s, path] }
|
280
|
+
.to_h
|
231
281
|
end
|
282
|
+
|
283
|
+
attr_accessor :scoped_root
|
284
|
+
attr_accessor :file_abstraction_class
|
285
|
+
attr_accessor :lookup
|
232
286
|
end
|
233
287
|
|
234
288
|
module Blob
|
235
289
|
attr_accessor :input_path
|
290
|
+
end
|
291
|
+
|
292
|
+
module RouteableBlob
|
236
293
|
attr_accessor :output_path
|
237
294
|
attr_reader :url_path
|
238
295
|
|
239
|
-
def url_path=(
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
end
|
296
|
+
def url_path=(new_url_path)
|
297
|
+
@url_path = new_url_path.normalize_for_route
|
298
|
+
end
|
299
|
+
end
|
244
300
|
|
245
|
-
|
246
|
-
|
247
|
-
path = "/#{path}"
|
248
|
-
end
|
301
|
+
module RenderableBlob
|
302
|
+
attr_reader :renderers
|
249
303
|
|
250
|
-
|
304
|
+
def renderers=(new_renderers)
|
305
|
+
@renderers = Array(new_renderers)
|
251
306
|
end
|
252
307
|
end
|
253
308
|
|
254
309
|
class Asset
|
255
310
|
include Blob
|
311
|
+
include RouteableBlob
|
256
312
|
|
257
313
|
def initialize(input_path:, entrypoint:)
|
258
314
|
self.input_path = input_path
|
259
|
-
|
260
315
|
@entrypoint = entrypoint
|
261
|
-
@url_path = nil
|
262
316
|
end
|
263
317
|
|
264
|
-
attr_accessor :
|
318
|
+
attr_accessor :body
|
265
319
|
|
266
320
|
def entrypoint?
|
267
321
|
!!@entrypoint
|
@@ -270,48 +324,90 @@ module Confinement
|
|
270
324
|
|
271
325
|
class Content
|
272
326
|
include Blob
|
327
|
+
include RouteableBlob
|
328
|
+
include RenderableBlob
|
273
329
|
|
274
|
-
def initialize(layout: nil,
|
330
|
+
def initialize(input_path:, layout: nil, locals: {}, renderers: :guess)
|
275
331
|
self.input_path = input_path
|
276
332
|
|
277
|
-
|
278
|
-
|
279
|
-
|
333
|
+
self.layout = layout
|
334
|
+
self.locals = locals
|
335
|
+
self.renderers = renderers
|
280
336
|
end
|
281
337
|
|
282
|
-
|
283
|
-
|
284
|
-
|
338
|
+
attr_accessor :locals
|
339
|
+
attr_accessor :layout
|
340
|
+
|
341
|
+
def body
|
342
|
+
parse_body_and_frontmatter
|
343
|
+
@body
|
344
|
+
end
|
345
|
+
|
346
|
+
def frontmatter
|
347
|
+
parse_body_and_frontmatter
|
348
|
+
@frontmatter
|
349
|
+
end
|
350
|
+
|
351
|
+
private
|
285
352
|
|
286
|
-
|
353
|
+
def parse_body_and_frontmatter
|
354
|
+
return if defined?(@frontmatter) && defined?(@body)
|
355
|
+
@frontmatter, @body = input_path.read.frontmatter_and_body
|
356
|
+
end
|
287
357
|
end
|
288
358
|
|
289
359
|
class Layout
|
290
|
-
|
291
|
-
|
360
|
+
include Blob
|
361
|
+
include RenderableBlob
|
362
|
+
|
363
|
+
def initialize(input_path:, renderers: :guess)
|
364
|
+
self.input_path = input_path
|
365
|
+
self.renderers = renderers
|
292
366
|
end
|
293
367
|
|
294
|
-
|
295
|
-
|
368
|
+
def body
|
369
|
+
@body ||= input_path.read
|
370
|
+
end
|
296
371
|
end
|
297
372
|
|
298
373
|
class Renderer
|
374
|
+
def self.guesses
|
375
|
+
@guesses ||= {
|
376
|
+
"erb" => -> { Erb.new }
|
377
|
+
}
|
378
|
+
end
|
379
|
+
|
299
380
|
class Erb
|
300
|
-
def call(source, view_context, &block)
|
301
|
-
method_name =
|
381
|
+
def call(source, view_context, path:, &block)
|
382
|
+
method_name =
|
383
|
+
if path
|
384
|
+
"_#{Digest::MD5.hexdigest(source)}__#{path.to_s.tr("^A-Za-z", "_")}"
|
385
|
+
else
|
386
|
+
"_#{Digest::MD5.hexdigest(source)}"
|
387
|
+
end
|
302
388
|
|
303
|
-
compile(method_name, source, view_context)
|
389
|
+
compile(method_name, source, view_context, path: path)
|
304
390
|
|
305
391
|
view_context.public_send(method_name, &block)
|
306
392
|
end
|
307
393
|
|
308
394
|
private
|
309
395
|
|
310
|
-
def compile(method_name, source, view_context)
|
396
|
+
def compile(method_name, source, view_context, path:)
|
311
397
|
if !view_context.respond_to?(method_name)
|
312
|
-
compiled_erb =
|
398
|
+
compiled_erb =
|
399
|
+
Erubi::CaptureEndEngine
|
400
|
+
.new(source, bufvar: :@_buf, ensure: true, yield_returns_buffer: true)
|
401
|
+
.src
|
402
|
+
|
403
|
+
eval_location =
|
404
|
+
if path
|
405
|
+
[path.to_s, 0]
|
406
|
+
else
|
407
|
+
[]
|
408
|
+
end
|
313
409
|
|
314
|
-
view_context.instance_eval(<<~RUBY,
|
410
|
+
view_context.instance_eval(<<~RUBY, *eval_location)
|
315
411
|
def #{method_name}
|
316
412
|
#{compiled_erb}
|
317
413
|
end
|
@@ -322,56 +418,73 @@ module Confinement
|
|
322
418
|
end
|
323
419
|
|
324
420
|
class Rendering
|
421
|
+
class Guesser
|
422
|
+
def initialize(guessing_registry)
|
423
|
+
@guessing_registry = guessing_registry
|
424
|
+
end
|
425
|
+
|
426
|
+
def call(path)
|
427
|
+
basename = path.basename.to_s
|
428
|
+
extensions = basename.split(".")[1..-1]
|
429
|
+
|
430
|
+
extensions.reverse.filter_map do |extension|
|
431
|
+
next if !@guessing_registry.key?(extension)
|
432
|
+
|
433
|
+
guess = @guessing_registry[extension]
|
434
|
+
guess = guess.call if guess.is_a?(Proc)
|
435
|
+
|
436
|
+
guess
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
325
441
|
class ViewContext
|
326
|
-
def initialize(routes:, layouts:,
|
442
|
+
def initialize(routes:, layouts:, assets:, contents:, locals:, frontmatter:)
|
327
443
|
@routes = routes
|
328
444
|
@layouts = layouts
|
445
|
+
@assets = assets
|
446
|
+
@contents = contents
|
329
447
|
|
330
448
|
@locals = locals
|
331
449
|
@frontmatter = frontmatter
|
332
|
-
|
333
|
-
@contents_path = contents_path
|
334
|
-
@layouts_path = layouts_path
|
335
450
|
end
|
336
451
|
|
337
452
|
attr_reader :routes
|
338
453
|
attr_reader :layouts
|
454
|
+
attr_reader :assets
|
455
|
+
attr_reader :contents
|
339
456
|
attr_reader :locals
|
340
457
|
attr_reader :frontmatter
|
341
|
-
attr_reader :contents_path
|
342
|
-
attr_reader :layouts_path
|
343
|
-
|
344
|
-
def render(path = nil, inline: nil, layout: nil, renderers:, &block)
|
345
|
-
body =
|
346
|
-
if inline
|
347
|
-
inline
|
348
|
-
elsif path
|
349
|
-
path.read
|
350
|
-
else
|
351
|
-
raise %(Must pass in either a Pathname or `inline: 'text'`)
|
352
|
-
end
|
353
458
|
|
459
|
+
def capture
|
460
|
+
original_buffer = @_buf
|
461
|
+
@_buf = +""
|
462
|
+
yield
|
463
|
+
@_buf
|
464
|
+
ensure
|
465
|
+
@_buf = original_buffer
|
466
|
+
end
|
467
|
+
|
468
|
+
def render(blob, layout: nil, &block)
|
354
469
|
render_chain = RenderChain.new(
|
355
|
-
body: body,
|
356
|
-
|
357
|
-
renderers: renderers,
|
470
|
+
body: blob.body,
|
471
|
+
path: blob.input_path,
|
472
|
+
renderers: blob.renderers,
|
358
473
|
view_context: self
|
359
474
|
)
|
360
|
-
rendered_body =
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
if layout.is_a?(Layout)
|
365
|
-
layout
|
366
|
-
elsif layout.is_a?(Pathname)
|
367
|
-
layouts[layout]
|
368
|
-
else
|
369
|
-
raise "Expected layout to be a Layout or Pathname"
|
475
|
+
rendered_body =
|
476
|
+
if block_given?
|
477
|
+
render_chain.call do
|
478
|
+
capture { yield }
|
370
479
|
end
|
480
|
+
else
|
481
|
+
render_chain.call
|
482
|
+
end
|
371
483
|
|
484
|
+
if layout
|
372
485
|
layout_render_chain = RenderChain.new(
|
373
|
-
body: layout.
|
374
|
-
|
486
|
+
body: layout.body,
|
487
|
+
path: layout.input_path,
|
375
488
|
renderers: layout.renderers,
|
376
489
|
view_context: self
|
377
490
|
)
|
@@ -386,22 +499,22 @@ module Confinement
|
|
386
499
|
end
|
387
500
|
|
388
501
|
class RenderChain
|
389
|
-
def initialize(body:,
|
502
|
+
def initialize(body:, path:, renderers:, view_context:)
|
390
503
|
@body = body
|
391
|
-
@
|
504
|
+
@path = path
|
392
505
|
@renderers = renderers
|
393
506
|
@view_context = view_context
|
394
507
|
end
|
395
508
|
|
396
509
|
def call(&block)
|
397
510
|
@renderers.reduce(@body) do |memo, renderer|
|
398
|
-
renderer.call(memo, @view_context, &block)
|
511
|
+
renderer.call(memo, @view_context, path: @path, &block)
|
399
512
|
end
|
400
513
|
end
|
401
514
|
end
|
402
515
|
end
|
403
516
|
|
404
|
-
class
|
517
|
+
class Compiler
|
405
518
|
def initialize(site)
|
406
519
|
@site = site
|
407
520
|
@lock = Mutex.new
|
@@ -414,8 +527,8 @@ module Confinement
|
|
414
527
|
# to worry about deadlocks or anything
|
415
528
|
@lock.synchronize do
|
416
529
|
# Assets first since it's almost always a dependency of contents
|
417
|
-
compile_assets(site.
|
418
|
-
compile_contents(site.
|
530
|
+
compile_assets(site.asset_blobs.send(:lookup))
|
531
|
+
compile_contents(site.route_identifiers.send(:lookup).values)
|
419
532
|
end
|
420
533
|
end
|
421
534
|
|
@@ -424,79 +537,73 @@ module Confinement
|
|
424
537
|
PARCEL_FILES_OUTPUT_REGEX = /^✨[^\n]+\n\n(.*)Done in(?:.*)\z/m
|
425
538
|
PARCEL_FILE_OUTPUT_REGEX = /^(?<page>.*?)\s+(?<size>[0-9\.]+\s*[A-Z]?B)\s+(?<time>[0-9\.]+[a-z]?s)$/
|
426
539
|
|
427
|
-
def compile_assets(
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
raise "Asset compilation failed"
|
441
|
-
end
|
540
|
+
def compile_assets(asset_files)
|
541
|
+
asset_paths = asset_files.values
|
542
|
+
|
543
|
+
out, status = Open3.capture2(
|
544
|
+
"yarn",
|
545
|
+
"run",
|
546
|
+
"parcel",
|
547
|
+
"build",
|
548
|
+
"--no-cache",
|
549
|
+
"--dist-dir", site.assets_root.to_s,
|
550
|
+
"--public-url", site.assets_root.basename.to_s,
|
551
|
+
*asset_paths.select(&:entrypoint?).map(&:input_path).map(&:to_s)
|
552
|
+
)
|
442
553
|
|
443
|
-
|
554
|
+
if !status.success?
|
555
|
+
raise "Asset compilation failed"
|
556
|
+
end
|
444
557
|
|
445
|
-
|
446
|
-
raise "Asset parsing failed"
|
447
|
-
end
|
558
|
+
matches = PARCEL_FILES_OUTPUT_REGEX.match(out)[1]
|
448
559
|
|
449
|
-
|
560
|
+
if !matches
|
561
|
+
raise "Asset compilation output parsing failed"
|
562
|
+
end
|
450
563
|
|
451
|
-
|
452
|
-
site.representation.only_assets.filter_map do |page|
|
453
|
-
next if page.input_path.nil?
|
564
|
+
processed_file_paths = matches.split("\n\n")
|
454
565
|
|
455
|
-
|
456
|
-
|
457
|
-
.to_h
|
566
|
+
processed_file_paths.map do |file|
|
567
|
+
output_file, *input_files = file.strip.split(/\n(?:└|├)── /)
|
458
568
|
|
459
|
-
|
460
|
-
output_file, input_file = file.strip.split("\n└── ")
|
569
|
+
output_path = site.root.concat(output_file[PARCEL_FILE_OUTPUT_REGEX, 1])
|
461
570
|
|
462
|
-
|
571
|
+
input_files.each do |input_file|
|
463
572
|
input_path = site.root.concat(input_file[PARCEL_FILE_OUTPUT_REGEX, 1])
|
464
573
|
|
465
|
-
if !
|
574
|
+
if !asset_files.key?(input_path)
|
466
575
|
next
|
467
576
|
end
|
468
577
|
|
469
578
|
url_path = output_path.relative_path_from(site.output_root)
|
470
|
-
|
471
|
-
|
472
|
-
|
579
|
+
asset_files[input_path].url_path = url_path.to_s
|
580
|
+
asset_files[input_path].output_path = output_path
|
581
|
+
asset_files[input_path].body = output_path.read
|
473
582
|
end
|
474
583
|
end
|
475
584
|
end
|
476
585
|
|
477
|
-
def
|
478
|
-
|
479
|
-
|
586
|
+
def compile_contents(contents)
|
587
|
+
contents.each do |content|
|
588
|
+
compile_content(content)
|
480
589
|
end
|
590
|
+
end
|
481
591
|
|
482
|
-
|
483
|
-
frontmatter, content_body = content_body.frontmatter_and_body
|
484
|
-
|
592
|
+
def compile_content(content)
|
485
593
|
view_context = Rendering::ViewContext.new(
|
486
|
-
routes: site.
|
487
|
-
layouts: site.
|
594
|
+
routes: site.route_identifiers,
|
595
|
+
layouts: site.layout_blobs,
|
596
|
+
assets: site.asset_blobs,
|
597
|
+
contents: site.content_blobs,
|
488
598
|
locals: content.locals,
|
489
|
-
frontmatter: frontmatter
|
490
|
-
contents_path: site.contents_path,
|
491
|
-
layouts_path: site.layouts_path
|
599
|
+
frontmatter: content.frontmatter
|
492
600
|
)
|
493
601
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
)
|
499
|
-
content.rendered_body ||= ""
|
602
|
+
site.view_context_helpers.each do |helper|
|
603
|
+
view_context.extend(helper)
|
604
|
+
end
|
605
|
+
|
606
|
+
rendered_body = view_context.render(content, layout: content.layout) || ""
|
500
607
|
|
501
608
|
content.output_path =
|
502
609
|
if content.url_path[-1] == "/"
|
@@ -506,7 +613,7 @@ module Confinement
|
|
506
613
|
end
|
507
614
|
|
508
615
|
if content.output_path.exist?
|
509
|
-
if content.output_path.read ==
|
616
|
+
if content.output_path.read == rendered_body
|
510
617
|
return
|
511
618
|
end
|
512
619
|
end
|
@@ -519,38 +626,20 @@ module Confinement
|
|
519
626
|
content.output_path.dirname.mkpath
|
520
627
|
end
|
521
628
|
|
522
|
-
content.output_path.write(
|
629
|
+
content.output_path.write(rendered_body)
|
523
630
|
|
524
631
|
nil
|
525
632
|
end
|
526
|
-
|
527
|
-
def compile_contents(contents)
|
528
|
-
return if !contents_dirty?
|
529
|
-
|
530
|
-
contents.each do |content|
|
531
|
-
compile_content(content)
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
private
|
536
|
-
|
537
|
-
def contents_dirty?
|
538
|
-
true
|
539
|
-
end
|
540
|
-
|
541
|
-
def assets_dirty?
|
542
|
-
true
|
543
|
-
end
|
544
633
|
end
|
545
634
|
|
546
635
|
class Publish
|
547
636
|
def initialize(site)
|
548
637
|
@site = site
|
549
|
-
@compiler =
|
638
|
+
@compiler = Compiler.new(@site)
|
550
639
|
end
|
551
640
|
|
552
|
-
def write
|
553
|
-
find_or_raise_or_mkdir(
|
641
|
+
def write
|
642
|
+
find_or_raise_or_mkdir(@site.output_root)
|
554
643
|
|
555
644
|
@compiler.compile_everything
|
556
645
|
end
|
data/lib/confinement/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: confinement
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Ahn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|