confinement 0.0.1 → 0.0.2
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/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
|