omg-actiontext 8.0.0.alpha3
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 +7 -0
- data/CHANGELOG.md +42 -0
- data/MIT-LICENSE +21 -0
- data/README.md +13 -0
- data/app/assets/javascripts/actiontext.esm.js +911 -0
- data/app/assets/javascripts/actiontext.js +884 -0
- data/app/assets/javascripts/trix.js +12165 -0
- data/app/assets/stylesheets/trix.css +412 -0
- data/app/helpers/action_text/content_helper.rb +76 -0
- data/app/helpers/action_text/tag_helper.rb +106 -0
- data/app/javascript/actiontext/attachment_upload.js +62 -0
- data/app/javascript/actiontext/index.js +10 -0
- data/app/models/action_text/encrypted_rich_text.rb +11 -0
- data/app/models/action_text/record.rb +11 -0
- data/app/models/action_text/rich_text.rb +93 -0
- data/app/views/action_text/attachables/_content_attachment.html.erb +3 -0
- data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
- data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
- data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
- data/app/views/action_text/contents/_content.html.erb +1 -0
- data/app/views/active_storage/blobs/_blob.html.erb +14 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/db/migrate/20180528164100_create_action_text_tables.rb +25 -0
- data/lib/action_text/attachable.rb +156 -0
- data/lib/action_text/attachables/content_attachment.rb +42 -0
- data/lib/action_text/attachables/missing_attachable.rb +29 -0
- data/lib/action_text/attachables/remote_image.rb +48 -0
- data/lib/action_text/attachment.rb +148 -0
- data/lib/action_text/attachment_gallery.rb +72 -0
- data/lib/action_text/attachments/caching.rb +18 -0
- data/lib/action_text/attachments/minification.rb +19 -0
- data/lib/action_text/attachments/trix_conversion.rb +38 -0
- data/lib/action_text/attribute.rb +105 -0
- data/lib/action_text/content.rb +197 -0
- data/lib/action_text/deprecator.rb +9 -0
- data/lib/action_text/encryption.rb +40 -0
- data/lib/action_text/engine.rb +94 -0
- data/lib/action_text/fixture_set.rb +68 -0
- data/lib/action_text/fragment.rb +62 -0
- data/lib/action_text/gem_version.rb +19 -0
- data/lib/action_text/html_conversion.rb +26 -0
- data/lib/action_text/plain_text_conversion.rb +114 -0
- data/lib/action_text/rendering.rb +35 -0
- data/lib/action_text/serialization.rb +38 -0
- data/lib/action_text/system_test_helper.rb +61 -0
- data/lib/action_text/trix_attachment.rb +94 -0
- data/lib/action_text/version.rb +12 -0
- data/lib/action_text.rb +59 -0
- data/lib/generators/action_text/install/install_generator.rb +84 -0
- data/lib/generators/action_text/install/templates/actiontext.css +440 -0
- data/lib/rails/generators/test_unit/install_generator.rb +15 -0
- data/lib/rails/generators/test_unit/templates/fixtures.yml +4 -0
- data/lib/tasks/actiontext.rake +6 -0
- data/package.json +39 -0
- metadata +190 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionText
|
6
|
+
module PlainTextConversion
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def node_to_plain_text(node)
|
10
|
+
remove_trailing_newlines(plain_text_for_node(node))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def plain_text_for_node(node, index = 0)
|
15
|
+
if respond_to?(plain_text_method_for_node(node), true)
|
16
|
+
send(plain_text_method_for_node(node), node, index)
|
17
|
+
else
|
18
|
+
plain_text_for_node_children(node)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def plain_text_for_node_children(node)
|
23
|
+
texts = []
|
24
|
+
node.children.each_with_index do |child, index|
|
25
|
+
texts << plain_text_for_node(child, index)
|
26
|
+
end
|
27
|
+
texts.join
|
28
|
+
end
|
29
|
+
|
30
|
+
def plain_text_method_for_node(node)
|
31
|
+
:"plain_text_for_#{node.name}_node"
|
32
|
+
end
|
33
|
+
|
34
|
+
def plain_text_for_block(node, index = 0)
|
35
|
+
"#{remove_trailing_newlines(plain_text_for_node_children(node))}\n\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
%i[ h1 p ].each do |element|
|
39
|
+
alias_method :"plain_text_for_#{element}_node", :plain_text_for_block
|
40
|
+
end
|
41
|
+
|
42
|
+
def plain_text_for_list(node, index)
|
43
|
+
"#{break_if_nested_list(node, plain_text_for_block(node))}"
|
44
|
+
end
|
45
|
+
|
46
|
+
%i[ ul ol ].each do |element|
|
47
|
+
alias_method :"plain_text_for_#{element}_node", :plain_text_for_list
|
48
|
+
end
|
49
|
+
|
50
|
+
def plain_text_for_br_node(node, index)
|
51
|
+
"\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def plain_text_for_text_node(node, index)
|
55
|
+
remove_trailing_newlines(node.text)
|
56
|
+
end
|
57
|
+
|
58
|
+
def plain_text_for_div_node(node, index)
|
59
|
+
"#{remove_trailing_newlines(plain_text_for_node_children(node))}\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def plain_text_for_figcaption_node(node, index)
|
63
|
+
"[#{remove_trailing_newlines(plain_text_for_node_children(node))}]"
|
64
|
+
end
|
65
|
+
|
66
|
+
def plain_text_for_blockquote_node(node, index)
|
67
|
+
text = plain_text_for_block(node)
|
68
|
+
text.sub(/\A(\s*)(.+?)(\s*)\Z/m, '\1“\2”\3')
|
69
|
+
end
|
70
|
+
|
71
|
+
def plain_text_for_li_node(node, index)
|
72
|
+
bullet = bullet_for_li_node(node, index)
|
73
|
+
text = remove_trailing_newlines(plain_text_for_node_children(node))
|
74
|
+
indentation = indentation_for_li_node(node)
|
75
|
+
|
76
|
+
"#{indentation}#{bullet} #{text}\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_trailing_newlines(text)
|
80
|
+
text.chomp("")
|
81
|
+
end
|
82
|
+
|
83
|
+
def bullet_for_li_node(node, index)
|
84
|
+
if list_node_name_for_li_node(node) == "ol"
|
85
|
+
"#{index + 1}."
|
86
|
+
else
|
87
|
+
"•"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def list_node_name_for_li_node(node)
|
92
|
+
node.ancestors.lazy.map(&:name).grep(/^[uo]l$/).first
|
93
|
+
end
|
94
|
+
|
95
|
+
def indentation_for_li_node(node)
|
96
|
+
depth = list_node_depth_for_node(node)
|
97
|
+
if depth > 1
|
98
|
+
" " * (depth - 1)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def list_node_depth_for_node(node)
|
103
|
+
node.ancestors.map(&:name).grep(/^[uo]l$/).count
|
104
|
+
end
|
105
|
+
|
106
|
+
def break_if_nested_list(node, text)
|
107
|
+
if list_node_depth_for_node(node) > 0
|
108
|
+
"\n#{text}"
|
109
|
+
else
|
110
|
+
text
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "active_support/concern"
|
6
|
+
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
7
|
+
|
8
|
+
module ActionText
|
9
|
+
module Rendering # :nodoc:
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
thread_cattr_accessor :renderer, instance_accessor: false
|
14
|
+
delegate :render, to: :class
|
15
|
+
end
|
16
|
+
|
17
|
+
class_methods do
|
18
|
+
def action_controller_renderer
|
19
|
+
@action_controller_renderer ||= Class.new(ActionController::Base).renderer
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_renderer(renderer)
|
23
|
+
previous_renderer = self.renderer
|
24
|
+
self.renderer = renderer
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
self.renderer = previous_renderer
|
28
|
+
end
|
29
|
+
|
30
|
+
def render(*args, &block)
|
31
|
+
(renderer || action_controller_renderer).render_to_string(*args, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionText
|
6
|
+
module Serialization
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def load(content)
|
11
|
+
new(content) if content
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump(content)
|
15
|
+
case content
|
16
|
+
when nil
|
17
|
+
nil
|
18
|
+
when self
|
19
|
+
content.to_html
|
20
|
+
when ActionText::RichText
|
21
|
+
content.body.to_html
|
22
|
+
else
|
23
|
+
new(content).to_html
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Marshal compatibility
|
29
|
+
|
30
|
+
class_methods do
|
31
|
+
alias_method :_load, :load
|
32
|
+
end
|
33
|
+
|
34
|
+
def _dump(*)
|
35
|
+
self.class.dump(self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionText
|
6
|
+
module SystemTestHelper
|
7
|
+
# Locates a Trix editor and fills it in with the given HTML.
|
8
|
+
#
|
9
|
+
# The editor can be found by:
|
10
|
+
# * its `id`
|
11
|
+
# * its `placeholder`
|
12
|
+
# * the text from its `label` element
|
13
|
+
# * its `aria-label`
|
14
|
+
# * the `name` of its input
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
#
|
19
|
+
# # <trix-editor id="message_content" ...></trix-editor>
|
20
|
+
# fill_in_rich_textarea "message_content", with: "Hello <em>world!</em>"
|
21
|
+
#
|
22
|
+
# # <trix-editor placeholder="Your message here" ...></trix-editor>
|
23
|
+
# fill_in_rich_textarea "Your message here", with: "Hello <em>world!</em>"
|
24
|
+
#
|
25
|
+
# # <label for="message_content">Message content</label>
|
26
|
+
# # <trix-editor id="message_content" ...></trix-editor>
|
27
|
+
# fill_in_rich_textarea "Message content", with: "Hello <em>world!</em>"
|
28
|
+
#
|
29
|
+
# # <trix-editor aria-label="Message content" ...></trix-editor>
|
30
|
+
# fill_in_rich_textarea "Message content", with: "Hello <em>world!</em>"
|
31
|
+
#
|
32
|
+
# # <input id="trix_input_1" name="message[content]" type="hidden">
|
33
|
+
# # <trix-editor input="trix_input_1"></trix-editor>
|
34
|
+
# fill_in_rich_textarea "message[content]", with: "Hello <em>world!</em>"
|
35
|
+
def fill_in_rich_textarea(locator = nil, with:)
|
36
|
+
find(:rich_textarea, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
|
37
|
+
end
|
38
|
+
alias_method :fill_in_rich_text_area, :fill_in_rich_textarea
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
%i[rich_textarea rich_text_area].each do |rich_textarea|
|
43
|
+
Capybara.add_selector rich_textarea do
|
44
|
+
label "rich-text area"
|
45
|
+
xpath do |locator|
|
46
|
+
if locator.nil?
|
47
|
+
XPath.descendant(:"trix-editor")
|
48
|
+
else
|
49
|
+
input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
|
50
|
+
input_located_by_label = XPath.anywhere(:label).where(XPath.string.n.is(locator)).attr(:for)
|
51
|
+
|
52
|
+
XPath.descendant(:"trix-editor").where \
|
53
|
+
XPath.attr(:id).equals(locator) |
|
54
|
+
XPath.attr(:placeholder).equals(locator) |
|
55
|
+
XPath.attr(:"aria-label").equals(locator) |
|
56
|
+
XPath.attr(:input).equals(input_located_by_name) |
|
57
|
+
XPath.attr(:id).equals(input_located_by_label)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionText
|
6
|
+
class TrixAttachment
|
7
|
+
TAG_NAME = "figure"
|
8
|
+
SELECTOR = "[data-trix-attachment]"
|
9
|
+
|
10
|
+
COMPOSED_ATTRIBUTES = %w( caption presentation )
|
11
|
+
ATTRIBUTES = %w( sgid contentType url href filename filesize width height previewable content ) + COMPOSED_ATTRIBUTES
|
12
|
+
ATTRIBUTE_TYPES = {
|
13
|
+
"previewable" => ->(value) { value.to_s == "true" },
|
14
|
+
"filesize" => ->(value) { Integer(value.to_s, exception: false) || value },
|
15
|
+
"width" => ->(value) { Integer(value.to_s, exception: false) },
|
16
|
+
"height" => ->(value) { Integer(value.to_s, exception: false) },
|
17
|
+
:default => ->(value) { value.to_s }
|
18
|
+
}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def from_attributes(attributes)
|
22
|
+
attributes = process_attributes(attributes)
|
23
|
+
|
24
|
+
trix_attachment_attributes = attributes.except(*COMPOSED_ATTRIBUTES)
|
25
|
+
trix_attributes = attributes.slice(*COMPOSED_ATTRIBUTES)
|
26
|
+
|
27
|
+
node = ActionText::HtmlConversion.create_element(TAG_NAME)
|
28
|
+
node["data-trix-attachment"] = JSON.generate(trix_attachment_attributes)
|
29
|
+
node["data-trix-attributes"] = JSON.generate(trix_attributes) if trix_attributes.any?
|
30
|
+
|
31
|
+
new(node)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def process_attributes(attributes)
|
36
|
+
typecast_attribute_values(transform_attribute_keys(attributes))
|
37
|
+
end
|
38
|
+
|
39
|
+
def transform_attribute_keys(attributes)
|
40
|
+
attributes.transform_keys { |key| key.to_s.underscore.camelize(:lower) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def typecast_attribute_values(attributes)
|
44
|
+
attributes.to_h do |key, value|
|
45
|
+
typecast = ATTRIBUTE_TYPES[key] || ATTRIBUTE_TYPES[:default]
|
46
|
+
[key, typecast.call(value)]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :node
|
52
|
+
|
53
|
+
def initialize(node)
|
54
|
+
@node = node
|
55
|
+
end
|
56
|
+
|
57
|
+
def attributes
|
58
|
+
@attributes ||= attachment_attributes.merge(composed_attributes).slice(*ATTRIBUTES)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_html
|
62
|
+
ActionText::HtmlConversion.node_to_html(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
to_html
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def attachment_attributes
|
71
|
+
read_json_object_attribute("data-trix-attachment")
|
72
|
+
end
|
73
|
+
|
74
|
+
def composed_attributes
|
75
|
+
read_json_object_attribute("data-trix-attributes")
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_json_object_attribute(name)
|
79
|
+
read_json_attribute(name) || {}
|
80
|
+
end
|
81
|
+
|
82
|
+
def read_json_attribute(name)
|
83
|
+
if value = node[name]
|
84
|
+
begin
|
85
|
+
JSON.parse(value)
|
86
|
+
rescue => e
|
87
|
+
Rails.logger.error "[#{self.class.name}] Couldn't parse JSON #{value} from NODE #{node.inspect}"
|
88
|
+
Rails.logger.error "[#{self.class.name}] Failed with #{e.class}: #{e.backtrace}"
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/action_text.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/rails"
|
5
|
+
|
6
|
+
require "action_text/version"
|
7
|
+
require "action_text/deprecator"
|
8
|
+
|
9
|
+
require "nokogiri"
|
10
|
+
|
11
|
+
# :markup: markdown
|
12
|
+
# :include: ../README.md
|
13
|
+
module ActionText
|
14
|
+
extend ActiveSupport::Autoload
|
15
|
+
|
16
|
+
autoload :Attachable
|
17
|
+
autoload :AttachmentGallery
|
18
|
+
autoload :Attachment
|
19
|
+
autoload :Attribute
|
20
|
+
autoload :Content
|
21
|
+
autoload :Encryption
|
22
|
+
autoload :Fragment
|
23
|
+
autoload :FixtureSet
|
24
|
+
autoload :HtmlConversion
|
25
|
+
autoload :PlainTextConversion
|
26
|
+
autoload :Rendering
|
27
|
+
autoload :Serialization
|
28
|
+
autoload :TrixAttachment
|
29
|
+
|
30
|
+
module Attachables
|
31
|
+
extend ActiveSupport::Autoload
|
32
|
+
|
33
|
+
autoload :ContentAttachment
|
34
|
+
autoload :MissingAttachable
|
35
|
+
autoload :RemoteImage
|
36
|
+
end
|
37
|
+
|
38
|
+
module Attachments
|
39
|
+
extend ActiveSupport::Autoload
|
40
|
+
|
41
|
+
autoload :Caching
|
42
|
+
autoload :Minification
|
43
|
+
autoload :TrixConversion
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def html_document_class
|
48
|
+
return @html_document_class if defined?(@html_document_class)
|
49
|
+
@html_document_class =
|
50
|
+
defined?(Nokogiri::HTML5) ? Nokogiri::HTML5::Document : Nokogiri::HTML4::Document
|
51
|
+
end
|
52
|
+
|
53
|
+
def html_document_fragment_class
|
54
|
+
return @html_document_fragment_class if defined?(@html_document_fragment_class)
|
55
|
+
@html_document_fragment_class =
|
56
|
+
defined?(Nokogiri::HTML5) ? Nokogiri::HTML5::DocumentFragment : Nokogiri::HTML4::DocumentFragment
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "pathname"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module ActionText
|
9
|
+
module Generators
|
10
|
+
class InstallGenerator < ::Rails::Generators::Base
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
|
+
|
13
|
+
def install_javascript_dependencies
|
14
|
+
say "Installing JavaScript dependencies", :green
|
15
|
+
if using_bun?
|
16
|
+
run "bun add @rails/actiontext trix"
|
17
|
+
elsif using_node?
|
18
|
+
run "yarn add @rails/actiontext trix"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def append_javascript_dependencies
|
23
|
+
destination = Pathname(destination_root)
|
24
|
+
|
25
|
+
if (application_javascript_path = destination.join("app/javascript/application.js")).exist?
|
26
|
+
insert_into_file application_javascript_path.to_s, %(\nimport "trix"\nimport "@rails/actiontext"\n)
|
27
|
+
else
|
28
|
+
say <<~INSTRUCTIONS, :green
|
29
|
+
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
|
30
|
+
INSTRUCTIONS
|
31
|
+
end
|
32
|
+
|
33
|
+
if (importmap_path = destination.join("config/importmap.rb")).exist?
|
34
|
+
append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.esm.js"\n)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_actiontext_files
|
39
|
+
template "actiontext.css", "app/assets/stylesheets/actiontext.css"
|
40
|
+
|
41
|
+
gem_root = "#{__dir__}/../../../.."
|
42
|
+
|
43
|
+
copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb",
|
44
|
+
"app/views/active_storage/blobs/_blob.html.erb"
|
45
|
+
|
46
|
+
copy_file "#{gem_root}/app/views/layouts/action_text/contents/_content.html.erb",
|
47
|
+
"app/views/layouts/action_text/contents/_content.html.erb"
|
48
|
+
end
|
49
|
+
|
50
|
+
def enable_image_processing_gem
|
51
|
+
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
|
52
|
+
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
|
53
|
+
image_processing_regex = /gem ["']image_processing["']/
|
54
|
+
if File.readlines(gemfile_path).grep(image_processing_regex).any?
|
55
|
+
uncomment_lines gemfile_path, image_processing_regex
|
56
|
+
else
|
57
|
+
run "bundle add --skip-install image_processing"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_migrations
|
63
|
+
rails_command "railties:install:migrations FROM=active_storage,action_text", inline: true
|
64
|
+
end
|
65
|
+
|
66
|
+
def using_js_runtime?
|
67
|
+
@using_js_runtime ||= Pathname(destination_root).join("package.json").exist?
|
68
|
+
end
|
69
|
+
|
70
|
+
def using_bun?
|
71
|
+
# Cannot assume yarn.lock has been generated yet so we look for a file known to
|
72
|
+
# be generated by the jsbundling-rails gem
|
73
|
+
@using_bun ||= using_js_runtime? && Pathname(destination_root).join("bun.config.js").exist?
|
74
|
+
end
|
75
|
+
|
76
|
+
def using_node?
|
77
|
+
# Bun is the only runtime that _isn't_ node.
|
78
|
+
@using_node ||= using_js_runtime? && !Pathname(destination_root).join("bun.config.js").exist?
|
79
|
+
end
|
80
|
+
|
81
|
+
hook_for :test_framework
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|