frails 0.1.0 → 0.3.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/.gitignore +3 -0
- data/.rubocop.yml +1 -3
- data/Gemfile +12 -1
- data/Gemfile.lock +101 -33
- data/README.md +166 -16
- data/bin/test +5 -0
- data/frails.gemspec +5 -8
- data/index.js +35 -0
- data/lib/frails.rb +17 -13
- data/lib/frails/component.rb +11 -0
- data/lib/frails/component/abstract_component.rb +58 -0
- data/lib/frails/component/component_renderer.rb +74 -0
- data/lib/frails/component/plain_component.rb +19 -0
- data/lib/frails/component/react_component.rb +17 -0
- data/lib/frails/component/react_component_renderer.rb +78 -0
- data/lib/frails/component/renderer_concerns.rb +36 -0
- data/lib/frails/dev_server.rb +2 -2
- data/lib/frails/dev_server_proxy.rb +3 -10
- data/lib/frails/helper.rb +73 -6
- data/lib/frails/log_subscriber.rb +35 -0
- data/lib/frails/manifest.rb +40 -3
- data/lib/frails/manifest_manager.rb +22 -0
- data/lib/frails/monkey/action_view/abstract_renderer.rb +54 -0
- data/lib/frails/monkey/action_view/partial_renderer.rb +50 -0
- data/lib/frails/monkey/action_view/renderer.rb +38 -0
- data/lib/frails/monkey/action_view/template_renderer.rb +24 -0
- data/lib/frails/railtie.rb +18 -14
- data/lib/frails/version.rb +1 -1
- data/lib/tasks/frails.rake +31 -0
- data/package.json +16 -0
- data/package/components.js +47 -0
- data/package/side_load.js +25 -0
- data/yarn.lock +80 -0
- metadata +34 -45
- data/lib/frails/instance.rb +0 -11
- data/lib/frails/server_manifest.rb +0 -7
data/lib/frails/helper.rb
CHANGED
@@ -1,21 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Frails::Helper
|
4
|
+
def render(options = {}, locals = {}, &block)
|
5
|
+
case options
|
6
|
+
when Hash
|
7
|
+
if Rails::VERSION::MAJOR >= 6
|
8
|
+
in_rendering_context(options) do |_renderer|
|
9
|
+
if block_given?
|
10
|
+
# MONKEY PATCH! simply renders the component with the given block.
|
11
|
+
return view_renderer.render_component(self, options, &block) if options.key?(:component)
|
12
|
+
|
13
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
14
|
+
else
|
15
|
+
view_renderer.render(self, options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
else
|
19
|
+
if block_given?
|
20
|
+
# MONKEY PATCH! simply renders the component with the given block.
|
21
|
+
return view_renderer.render_component(self, options, &block) if options.key?(:component)
|
22
|
+
|
23
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
24
|
+
else
|
25
|
+
view_renderer.render(self, options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
4
33
|
def javascript_pack_tag(*names, **options)
|
5
|
-
|
34
|
+
return if Rails.env.test?
|
35
|
+
|
36
|
+
@included_javascripts ||= []
|
37
|
+
|
38
|
+
soft_lookup = options.delete(:soft_lookup) { false }
|
39
|
+
sources = sources_from_manifest_entries(names, :javascript, manifest: options.delete(:manifest),
|
40
|
+
soft_lookup: soft_lookup)
|
41
|
+
|
42
|
+
# Make sure that we don't include an asset that has already been included.
|
43
|
+
sources -= @included_javascripts
|
44
|
+
sources.compact!
|
45
|
+
|
46
|
+
# Concatenate the sources to be included to those already included.
|
47
|
+
@included_javascripts.concat sources
|
48
|
+
|
49
|
+
sources.empty? ? nil : javascript_include_tag(*sources, **options)
|
6
50
|
end
|
7
51
|
|
8
52
|
def stylesheet_pack_tag(*names, **options)
|
9
|
-
|
53
|
+
return if Rails.env.test?
|
54
|
+
|
55
|
+
soft_lookup = options.delete(:soft_lookup) { false }
|
56
|
+
sources = sources_from_manifest_entries(names, :stylesheet, manifest: options.delete(:manifest),
|
57
|
+
soft_lookup: soft_lookup)
|
58
|
+
|
59
|
+
sources.compact!
|
60
|
+
stylesheet_link_tag(*sources, **options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def image_pack_tag(name, **options)
|
64
|
+
return if Rails.env.test?
|
65
|
+
|
66
|
+
image_tag(pack_path("images/#{name}", manifest: options.delete(:manifest)), **options)
|
67
|
+
end
|
68
|
+
|
69
|
+
def pack_path(name, type: nil, manifest: nil)
|
70
|
+
manifest_manager[manifest].lookup! name, type: type
|
10
71
|
end
|
11
72
|
|
12
73
|
private
|
13
74
|
|
14
|
-
def sources_from_manifest_entries(names, type)
|
15
|
-
names.map
|
75
|
+
def sources_from_manifest_entries(names, type, manifest: nil, soft_lookup: false)
|
76
|
+
names.map do |name|
|
77
|
+
if soft_lookup
|
78
|
+
manifest_manager[manifest].lookup name, type: type
|
79
|
+
else
|
80
|
+
manifest_manager[manifest].lookup! name, type: type
|
81
|
+
end
|
82
|
+
end.flatten
|
16
83
|
end
|
17
84
|
|
18
|
-
def
|
19
|
-
Frails.
|
85
|
+
def manifest_manager
|
86
|
+
Frails.manifest
|
20
87
|
end
|
21
88
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/log_subscriber'
|
4
|
+
|
5
|
+
module Frails
|
6
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
7
|
+
VIEWS_PATTERN = %r{^app/views/}.freeze
|
8
|
+
|
9
|
+
def render_side_loaded_assets(event)
|
10
|
+
return if (asset_types = event.payload[:asset_types]).empty?
|
11
|
+
|
12
|
+
identifier_from_root = from_rails_root(event.payload[:identifier])
|
13
|
+
|
14
|
+
info do
|
15
|
+
message = +" Side loaded #{asset_types.join(',')} for #{identifier_from_root}"
|
16
|
+
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
EMPTY = ''
|
23
|
+
def from_rails_root(string)
|
24
|
+
string = string.sub(rails_root, EMPTY)
|
25
|
+
string.sub!(VIEWS_PATTERN, EMPTY)
|
26
|
+
string
|
27
|
+
end
|
28
|
+
|
29
|
+
def rails_root
|
30
|
+
@root ||= "#{Rails.root}/"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Frails::LogSubscriber.attach_to :action_view
|
data/lib/frails/manifest.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'open-uri'
|
4
|
+
|
3
5
|
class Frails::Manifest
|
6
|
+
class MissingManifestError < StandardError; end
|
4
7
|
class MissingEntryError < StandardError; end
|
5
8
|
|
9
|
+
attr_reader :manifest_path
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@manifest_path = Rails.public_path.join(Frails.public_output_path, path)
|
13
|
+
|
14
|
+
return if @manifest_path.exist?
|
15
|
+
|
16
|
+
raise Frails::Manifest::MissingManifestError, "Frails can't find manifest #{manifest_path}"
|
17
|
+
end
|
18
|
+
|
6
19
|
def refresh
|
7
20
|
@data = load
|
8
21
|
end
|
@@ -11,7 +24,7 @@ class Frails::Manifest
|
|
11
24
|
# returns nil.
|
12
25
|
#
|
13
26
|
# Example:
|
14
|
-
# Frails.manifest.lookup('calendar.js') # => "/
|
27
|
+
# Frails.manifest.lookup('calendar.js') # => "/assets/calendar-1016838bab065ae1e122.js"
|
15
28
|
def lookup(name, type: nil)
|
16
29
|
# When using SplitChunks or RuntimeChunks the manifest hash will contain an extra object called
|
17
30
|
# "entrypoints". When the entrypoints key is not present in the manifest, or the name is not
|
@@ -32,12 +45,36 @@ class Frails::Manifest
|
|
32
45
|
lookup(name, type: type) || handle_missing_entry(name)
|
33
46
|
end
|
34
47
|
|
35
|
-
def
|
36
|
-
|
48
|
+
def read!(name, type)
|
49
|
+
sources = *lookup!(name, type: type)
|
50
|
+
sources.map do |path|
|
51
|
+
yield path, read_source(path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def read(name, type)
|
56
|
+
sources = *lookup(name, type: type)
|
57
|
+
sources.map do |path|
|
58
|
+
yield path, read_source(path)
|
59
|
+
end
|
37
60
|
end
|
38
61
|
|
39
62
|
private
|
40
63
|
|
64
|
+
def read_source(path)
|
65
|
+
unless Frails.dev_server.running?
|
66
|
+
host = ActionController::Base.helpers.compute_asset_host
|
67
|
+
new_path = host && path.start_with?(host) ? path.delete_prefix(host) : path
|
68
|
+
return Rails.public_path.join(new_path.gsub(%r{^\/}, '')).read
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
open("http://#{Frails.dev_server.host_with_port}#{path}").read
|
73
|
+
rescue OpenURI::HTTPError => e
|
74
|
+
handle_missing_entry path
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
41
78
|
def load
|
42
79
|
manifest_path.exist? ? JSON.parse(manifest_path.read) : {}
|
43
80
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'frails/manifest'
|
4
|
+
|
5
|
+
class Frails::ManifestManager
|
6
|
+
delegate :lookup, :lookup!, :read, :read!, to: :default_instance
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@instances = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](path = nil)
|
13
|
+
path ||= Frails.manifest_path
|
14
|
+
@instances[path] ||= Frails::Manifest.new(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def default_instance
|
20
|
+
@instances[Frails.manifest_path] ||= Frails::Manifest.new(Frails.manifest_path)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Frails
|
4
|
+
module Monkey
|
5
|
+
module ActionView
|
6
|
+
module AbstractRenderer
|
7
|
+
def side_load_assets(view, tpl)
|
8
|
+
path = tpl.short_identifier.delete_prefix('app/').delete_suffix('.html.erb')
|
9
|
+
|
10
|
+
instrument :side_loaded_assets, identifier: tpl.identifier, asset_types: [] do |payload|
|
11
|
+
side_load_javascript path, view, payload
|
12
|
+
side_load_stylesheet path, view, payload
|
13
|
+
end
|
14
|
+
|
15
|
+
path
|
16
|
+
end
|
17
|
+
|
18
|
+
def side_load_javascript(path, view, payload)
|
19
|
+
# Render the JS - if any.
|
20
|
+
view.content_for :side_loaded_js do
|
21
|
+
view.javascript_pack_tag(path, soft_lookup: true).tap do |tag|
|
22
|
+
!tag.nil? && (payload[:asset_types] << :js)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def side_load_stylesheet(path, view, payload)
|
28
|
+
loaded = side_loaded_stylesheets(view)
|
29
|
+
|
30
|
+
# Don't inline the styles if already inlined.
|
31
|
+
return if loaded.include?(path)
|
32
|
+
|
33
|
+
# Render the CSS inline - if any.
|
34
|
+
Frails.manifest.read(path, :stylesheet) do |href, src|
|
35
|
+
view.content_for :side_loaded_css do
|
36
|
+
view.content_tag :style, src, { data: { href: href } }, false
|
37
|
+
end
|
38
|
+
|
39
|
+
loaded << path
|
40
|
+
payload[:asset_types] << :css
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def side_loaded_stylesheets(view)
|
45
|
+
if view.instance_variable_defined?(:@side_loaded_stylesheets)
|
46
|
+
view.instance_variable_get(:@side_loaded_stylesheets)
|
47
|
+
else
|
48
|
+
view.instance_variable_set :@side_loaded_stylesheets, []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Frails
|
4
|
+
module Monkey
|
5
|
+
module ActionView
|
6
|
+
module PartialRenderer
|
7
|
+
def render_partial(view, template)
|
8
|
+
# Side load partial assets - if any.
|
9
|
+
@asset_path = side_load_assets(view, template)
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_rendered_template(content, template, layout = nil)
|
15
|
+
content = transform_css_modules(content).html_safe
|
16
|
+
::ActionView::AbstractRenderer::RenderedTemplate.new content, layout, template
|
17
|
+
end
|
18
|
+
|
19
|
+
def transform_css_modules(content)
|
20
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(content)
|
21
|
+
|
22
|
+
return content if (modules = doc.css('[css_module]')).empty?
|
23
|
+
|
24
|
+
modules.each do |ele|
|
25
|
+
classes = class_name_for_style(ele.delete('css_module'))
|
26
|
+
ele['class'] = (ele['class'].nil? ? classes : classes << ele['class']).join(' ')
|
27
|
+
end
|
28
|
+
|
29
|
+
doc.to_html
|
30
|
+
end
|
31
|
+
|
32
|
+
def class_name_for_style(class_names)
|
33
|
+
class_names.to_s.split.map { |class_name| build_ident class_name }
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_ident(local_name)
|
37
|
+
path = Rails.root.join('app', "#{@asset_path}.css").relative_path_from(Rails.root)
|
38
|
+
hash_digest = Digest::MD5.hexdigest("#{path}+#{local_name}")[0, 6]
|
39
|
+
|
40
|
+
return "#{local_name}-#{hash_digest}" unless Frails.dev_server.running?
|
41
|
+
|
42
|
+
name = path.basename.sub(path.extname, '').sub('.', '-')
|
43
|
+
ident = +"#{name}__#{local_name}___#{hash_digest}"
|
44
|
+
ident.prepend("#{path.dirname.to_s.tr('/', '-')}-")
|
45
|
+
ident
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Frails
|
4
|
+
module Monkey
|
5
|
+
module ActionView
|
6
|
+
module Renderer
|
7
|
+
def render_to_object(context, options) # :nodoc:
|
8
|
+
if options.key?(:partial)
|
9
|
+
render_partial_to_object(context, options)
|
10
|
+
elsif options.key?(:component)
|
11
|
+
render_component_to_object(context, options)
|
12
|
+
else
|
13
|
+
render_template_to_object(context, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Direct access to partial rendering.
|
18
|
+
def render_component(context, options, &block) #:nodoc:
|
19
|
+
render_component_to_object(context, options, &block).body
|
20
|
+
end
|
21
|
+
|
22
|
+
def render_component_to_object(context, options, &block)
|
23
|
+
component = options[:component].to_s
|
24
|
+
|
25
|
+
result = if Rails.root.join('app', 'components', component, 'index.entry.jsx').exist?
|
26
|
+
Frails::Component::ReactComponentRenderer.new.render(context, options, &block)
|
27
|
+
else
|
28
|
+
options[:partial] = "#{component}/index"
|
29
|
+
Frails::Component::ComponentRenderer.new(@lookup_context)
|
30
|
+
.render(context, options, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
::ActionView::AbstractRenderer::RenderedTemplate.new result, nil, nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Frails
|
4
|
+
module Monkey
|
5
|
+
module ActionView
|
6
|
+
module TemplateRenderer
|
7
|
+
def render_template(view, template, layout_name, locals)
|
8
|
+
return super if template.type != :html
|
9
|
+
|
10
|
+
# Side load layout assets - if any.
|
11
|
+
if layout_name
|
12
|
+
layout = find_layout(layout_name, locals.keys, [formats.first])
|
13
|
+
layout && side_load_assets(view, layout)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Side load view assets - if any.
|
17
|
+
side_load_assets view, template
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/frails/railtie.rb
CHANGED
@@ -1,24 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rails/railtie'
|
4
|
-
|
5
4
|
require 'frails/helper'
|
6
5
|
require 'frails/dev_server_proxy'
|
7
6
|
|
8
7
|
class Frails::Engine < ::Rails::Engine
|
9
|
-
# Allows Webpacker config values to be set via Rails env config files
|
10
|
-
config.frails = ActiveSupport::OrderedOptions.new
|
11
|
-
|
12
|
-
initializer 'frails.default_config' do |app|
|
13
|
-
assign_config app, :public_output_path, '/assets'
|
14
|
-
assign_config app, :dev_server_port, 8080
|
15
|
-
assign_config app, :dev_server_host, 'localhost'
|
16
|
-
end
|
17
|
-
|
18
8
|
initializer 'frails.proxy' do |app|
|
19
9
|
app.middleware.insert_before 0, Frails::DevServerProxy, ssl_verify_none: true
|
20
10
|
end
|
21
11
|
|
12
|
+
initializer 'frails.rendering' do |_conf|
|
13
|
+
ActiveSupport.on_load :action_controller do
|
14
|
+
ActionController::Base.prepend_view_path Rails.root.join('app', 'components')
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveSupport.on_load :action_view do
|
18
|
+
require 'frails/monkey/action_view/abstract_renderer'
|
19
|
+
require 'frails/monkey/action_view/template_renderer'
|
20
|
+
require 'frails/monkey/action_view/partial_renderer'
|
21
|
+
require 'frails/monkey/action_view/renderer'
|
22
|
+
|
23
|
+
ActionView::AbstractRenderer.send :prepend, Frails::Monkey::ActionView::AbstractRenderer
|
24
|
+
ActionView::TemplateRenderer.send :prepend, Frails::Monkey::ActionView::TemplateRenderer
|
25
|
+
ActionView::PartialRenderer.send :prepend, Frails::Monkey::ActionView::PartialRenderer
|
26
|
+
ActionView::Renderer.send :prepend, Frails::Monkey::ActionView::Renderer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
initializer 'frails.helper' do
|
23
31
|
ActiveSupport.on_load :action_controller do
|
24
32
|
ActionController::Base.helper Frails::Helper
|
@@ -29,7 +37,3 @@ class Frails::Engine < ::Rails::Engine
|
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
32
|
-
|
33
|
-
def assign_config(app, name, default_value)
|
34
|
-
app.config.frails[name] = app.config.frails.fetch(name, default_value)
|
35
|
-
end
|
data/lib/frails/version.rb
CHANGED