proscenium 0.19.0.beta6-arm64-darwin → 0.19.0.beta8-arm64-darwin
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/README.md +3 -26
- data/lib/proscenium/builder.rb +12 -36
- data/lib/proscenium/bundled_gems.rb +37 -0
- data/lib/proscenium/css_module/rewriter.rb +2 -2
- data/lib/proscenium/css_module/transformer.rb +1 -1
- data/lib/proscenium/css_module.rb +4 -1
- data/lib/proscenium/ext/proscenium +0 -0
- data/lib/proscenium/ext/proscenium.h +1 -7
- data/lib/proscenium/helper.rb +3 -9
- data/lib/proscenium/importer.rb +34 -16
- data/lib/proscenium/log_subscriber.rb +11 -8
- data/lib/proscenium/middleware/base.rb +10 -8
- data/lib/proscenium/middleware/esbuild.rb +1 -6
- data/lib/proscenium/middleware/ruby_gems.rb +23 -0
- data/lib/proscenium/middleware.rb +26 -22
- data/lib/proscenium/monkey.rb +3 -5
- data/lib/proscenium/phlex/asset_inclusions.rb +0 -1
- data/lib/proscenium/phlex/css_modules.rb +1 -1
- data/lib/proscenium/railtie.rb +0 -27
- data/lib/proscenium/react_componentable.rb +0 -1
- data/lib/proscenium/registry/bundled_package.rb +29 -0
- data/lib/proscenium/registry/package.rb +95 -0
- data/lib/proscenium/registry/ruby_gem_package.rb +28 -0
- data/lib/proscenium/registry.rb +29 -0
- data/lib/proscenium/resolver.rb +23 -18
- data/lib/proscenium/ruby_gems.rb +67 -0
- data/lib/proscenium/side_load.rb +37 -69
- data/lib/proscenium/ui/flash/bun.lock +19 -0
- data/lib/proscenium/ui/flash/index.js +6 -2
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.d.ts +33 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.js +44 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/license +9 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/package.json +59 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/readme.md +125 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/LICENSE +20 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/README.md +11 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/package.json +44 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.css +697 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.js +537 -0
- data/lib/proscenium/ui/flash/package.json +11 -0
- data/lib/proscenium/ui/react-manager/index.jsx +3 -22
- data/lib/proscenium/ui/ujs/index.js +1 -1
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium.rb +4 -11
- metadata +21 -3
- data/lib/proscenium/middleware/engines.rb +0 -41
data/lib/proscenium/railtie.rb
CHANGED
@@ -14,42 +14,15 @@ module Proscenium
|
|
14
14
|
config.proscenium.bundle = true
|
15
15
|
config.proscenium.side_load = true
|
16
16
|
config.proscenium.code_splitting = true
|
17
|
-
config.proscenium.external_node_modules = false
|
18
17
|
|
19
|
-
# Cache asset paths when building to path. Enabled by default in production.
|
20
|
-
# @see Proscenium::Builder#build_to_path
|
21
|
-
config.proscenium.cache = ActiveSupport::Cache::MemoryStore.new if Rails.env.production?
|
22
|
-
|
23
|
-
# TODO: implement!
|
24
18
|
config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
|
25
19
|
config.proscenium.cache_max_age = 2_592_000 # 30 days
|
26
20
|
|
27
|
-
# A proc that will be given the path to build, and should return a boolean indicating whether to
|
28
|
-
# cache the response.
|
29
|
-
#
|
30
|
-
# Example:
|
31
|
-
# cache_middleware_response = ->(path) { path.start_with?('node_modules/') }
|
32
|
-
config.proscenium.cache_middleware_response = nil
|
33
|
-
|
34
21
|
# List of environment variable names that should be passed to the builder, which will then be
|
35
22
|
# passed to esbuild's `Define` option. Being explicit about which environment variables are
|
36
23
|
# defined means a faster build, as esbuild will have less to do.
|
37
24
|
config.proscenium.env_vars = Set.new
|
38
25
|
|
39
|
-
# Rails engines to expose and allow Proscenium to serve their assets.
|
40
|
-
#
|
41
|
-
# A Rails engine that has assets, can add Proscenium as a gem dependency, and then add itself
|
42
|
-
# to this list. Proscenium will then serve the engine's assets at the URL path beginning with
|
43
|
-
# the engine name.
|
44
|
-
#
|
45
|
-
# Example:
|
46
|
-
# class Gem1::Engine < ::Rails::Engine
|
47
|
-
# config.proscenium.engines[:gem1] = root
|
48
|
-
# end
|
49
|
-
config.proscenium.engines = {
|
50
|
-
proscenium: Proscenium.ui_path
|
51
|
-
}
|
52
|
-
|
53
26
|
config.action_dispatch.rescue_templates = {
|
54
27
|
'Proscenium::Builder::BuildError' => 'build_error'
|
55
28
|
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems/package'
|
4
|
+
|
5
|
+
class Proscenium::Registry
|
6
|
+
class BundledPackage < Package
|
7
|
+
def version = @version ||= spec.version.to_s
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def package_json
|
12
|
+
@package_json ||= begin
|
13
|
+
unless (gem_path = Proscenium::BundledGems.pathname_for(gem_name))
|
14
|
+
raise PackageNotInstalledError, name
|
15
|
+
end
|
16
|
+
|
17
|
+
if (package_path = gem_path.join('package.json')).exist?
|
18
|
+
JSON.parse(package_path.read)
|
19
|
+
else
|
20
|
+
default_package_json
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def spec
|
26
|
+
@spec ||= Bundler.load.specs[gem_name].first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Proscenium::Registry
|
4
|
+
# 1. Fetch the gem metadata from RubyGems API.
|
5
|
+
# 2. Extract any package.json from the gem, and populate the response with it.
|
6
|
+
# 3. Create a tarball containing the fetched package.json. This will be downloaded by the npm
|
7
|
+
# client, and unpacked into node_modules. Proscenium ignores this, as it will pull contents
|
8
|
+
# directly from location of the installed gem.
|
9
|
+
# 4. Return a valid npm response listing package details, tarball location, and its dependencies.
|
10
|
+
#
|
11
|
+
# See https://wiki.commonjs.org/wiki/Packages/Registry
|
12
|
+
class Package
|
13
|
+
extend Literal::Properties
|
14
|
+
|
15
|
+
prop :name, String, :positional, reader: :private
|
16
|
+
prop :version, _String?
|
17
|
+
prop :host, String
|
18
|
+
|
19
|
+
def as_json
|
20
|
+
{
|
21
|
+
name:,
|
22
|
+
'dist-tags': {
|
23
|
+
latest: version
|
24
|
+
},
|
25
|
+
versions: {
|
26
|
+
version => {
|
27
|
+
name:,
|
28
|
+
version:,
|
29
|
+
dependencies: package_json['dependencies'] || {},
|
30
|
+
dist: {
|
31
|
+
tarball:,
|
32
|
+
integrity:,
|
33
|
+
shasum:
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate!
|
41
|
+
return self if name.start_with?('@rubygems/')
|
42
|
+
|
43
|
+
raise PackageUnsupportedError, name
|
44
|
+
end
|
45
|
+
|
46
|
+
def gem_name = @gem_name ||= name.gsub('@rubygems/', '')
|
47
|
+
def version = @version # rubocop:disable Style/TrivialAccessors
|
48
|
+
def shasum = Digest::SHA1.file(tarball_path).hexdigest
|
49
|
+
def integrity = "sha512-#{Digest::SHA512.file(tarball_path).base64digest}"
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def tarball
|
54
|
+
create_tarball unless tarball_path.exist?
|
55
|
+
|
56
|
+
"#{@host}/#{tarball_path.relative_path_from(Rails.public_path)}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def tarball_name
|
60
|
+
@tarball_name ||= "#{gem_name}-#{version}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def tarball_path
|
64
|
+
@tarball_path ||= Rails.public_path.join('proscenium_registry_tarballs')
|
65
|
+
.join("@rubygems/#{gem_name}/#{tarball_name}.tgz")
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_tarball
|
69
|
+
FileUtils.mkdir_p(File.dirname(tarball_path))
|
70
|
+
|
71
|
+
File.open(tarball_path, 'wb') do |file|
|
72
|
+
Zlib::GzipWriter.wrap(file) do |gz|
|
73
|
+
Gem::Package::TarWriter.new(gz) do |tar|
|
74
|
+
contents = package_json.to_json
|
75
|
+
tar.add_file_simple('package/package.json', 0o444, contents.length) do |io|
|
76
|
+
io.write contents
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def package_json
|
84
|
+
@package_json ||= default_package_json
|
85
|
+
end
|
86
|
+
|
87
|
+
def default_package_json
|
88
|
+
{
|
89
|
+
name:,
|
90
|
+
version:,
|
91
|
+
dependencies: {}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Proscenium::Registry
|
4
|
+
class RubyGemPackage < Package
|
5
|
+
def version = spec['version']
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def package_json
|
10
|
+
@package_json ||= begin
|
11
|
+
package_path = Proscenium::RubyGems.path_for(gem_name, version).join('package.json')
|
12
|
+
if package_path.exist?
|
13
|
+
JSON.parse path.read
|
14
|
+
else
|
15
|
+
default_package_json
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def spec
|
21
|
+
@spec ||= if @version.present?
|
22
|
+
Gems::V2.info gem_name, @version
|
23
|
+
else
|
24
|
+
Gems.info(gem_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Proscenium::Registry
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :Package
|
7
|
+
autoload :BundledPackage
|
8
|
+
autoload :RubyGemPackage
|
9
|
+
|
10
|
+
class PackageUnsupportedError < StandardError
|
11
|
+
def initialize(name)
|
12
|
+
super("Package `#{name}` is not valid; only Ruby gems are supported via the @rubygems scope.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class PackageNotInstalledError < StandardError
|
17
|
+
def initialize(name)
|
18
|
+
super("Package `#{name}` is not found in your bundle; have you installed the Ruby gem?")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.bundled_package(name, host:)
|
23
|
+
BundledPackage.new(name, host:).validate!
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.ruby_gem_package(name, version, host:)
|
27
|
+
RubyGemPackage.new(name, version:, host:).validate!
|
28
|
+
end
|
29
|
+
end
|
data/lib/proscenium/resolver.rb
CHANGED
@@ -4,31 +4,36 @@ require 'active_support/current_attributes'
|
|
4
4
|
|
5
5
|
module Proscenium
|
6
6
|
class Resolver < ActiveSupport::CurrentAttributes
|
7
|
-
|
8
|
-
|
7
|
+
attribute :resolved unless Rails.env.production?
|
8
|
+
mattr_accessor :resolved if Rails.env.production?
|
9
9
|
|
10
|
-
# Resolve the given `path` to a URL path.
|
10
|
+
# Resolve the given `path` to a fully qualified URL path.
|
11
11
|
#
|
12
|
-
# @param path [String]
|
12
|
+
# @param path [String] URL path, file system path, or bare specifier (ie. NPM package).
|
13
13
|
# @return [String] URL path.
|
14
14
|
def self.resolve(path)
|
15
15
|
self.resolved ||= {}
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
raise ArgumentError, 'path must be an absolute file system or URL path'
|
20
|
-
end
|
21
|
-
|
22
|
-
if path.start_with?('proscenium/')
|
23
|
-
"/#{path}"
|
24
|
-
elsif (engine = Proscenium.config.engines.find { |_, v| path.start_with? "#{v}/" })
|
25
|
-
path.sub(/^#{engine.last}/, "/#{engine.first}")
|
26
|
-
elsif path.start_with?("#{Rails.root}/")
|
27
|
-
path.delete_prefix Rails.root.to_s
|
28
|
-
else
|
29
|
-
Builder.resolve path
|
30
|
-
end
|
17
|
+
if path.start_with?('./', '../')
|
18
|
+
raise ArgumentError, '`path` must be an absolute file system or URL path'
|
31
19
|
end
|
20
|
+
|
21
|
+
self.resolved[path] ||= if (gem = BundledGems.paths.find { |_, v| path.start_with? "#{v}/" })
|
22
|
+
# If the path is a rubygem, and it is installed with npm via the
|
23
|
+
# @rubygems scope, then resolve the path to the symlinked location
|
24
|
+
# in node_modules.
|
25
|
+
# npm_path = Rails.root.join("node_modules/@rubygems/#{gem.first}")
|
26
|
+
# if npm_path.symlink?
|
27
|
+
# npm_path.realpath.join(path.sub(/^#{gem.last}/, '.')).to_s
|
28
|
+
# .delete_prefix(Rails.root.to_s)
|
29
|
+
# else
|
30
|
+
path.sub(/^#{gem.last}/, "/node_modules/@rubygems/#{gem.first}")
|
31
|
+
# end
|
32
|
+
elsif path.start_with?("#{Rails.root}/")
|
33
|
+
path.delete_prefix Rails.root.to_s
|
34
|
+
else
|
35
|
+
Builder.resolve path
|
36
|
+
end
|
32
37
|
end
|
33
38
|
end
|
34
39
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems/package'
|
4
|
+
require 'rubygems/remote_fetcher'
|
5
|
+
|
6
|
+
module Proscenium
|
7
|
+
class RubyGems
|
8
|
+
def self.path_for(name, version = nil)
|
9
|
+
Pathname new(name, version).path
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name, version = nil)
|
13
|
+
@name = name
|
14
|
+
@version = version
|
15
|
+
end
|
16
|
+
|
17
|
+
def path
|
18
|
+
dependency = Gem::Dependency.new @name, @version
|
19
|
+
path = gem_path dependency
|
20
|
+
|
21
|
+
raise "Gem '#{@name}' not installed nor fetchable." unless path
|
22
|
+
|
23
|
+
basename = File.basename path, '.gem'
|
24
|
+
target_dir = File.expand_path basename, Rails.root.join('tmp', 'unpacked_gems')
|
25
|
+
|
26
|
+
Gem::Package.new(path).extract_files target_dir
|
27
|
+
|
28
|
+
target_dir
|
29
|
+
end
|
30
|
+
|
31
|
+
# Find cached filename in Gem.path. Returns nil if the file cannot be found.
|
32
|
+
def find_in_cache(filename)
|
33
|
+
Gem.path.each do |path|
|
34
|
+
this_path = File.join(path, 'cache', filename)
|
35
|
+
return this_path if File.exist? this_path
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return the full path to the cached gem file matching the given
|
42
|
+
# name and version requirement. Returns 'nil' if no match.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# get_path 'rake', '> 0.4' # "/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem"
|
47
|
+
# get_path 'rake', '< 0.1' # nil
|
48
|
+
# get_path 'rak' # nil (exact name required)
|
49
|
+
def gem_path(dependency)
|
50
|
+
return dependency.name if /\.gem$/i.match?(dependency.name)
|
51
|
+
|
52
|
+
specs = dependency.matching_specs
|
53
|
+
selected = specs.max_by(&:version)
|
54
|
+
|
55
|
+
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless selected
|
56
|
+
return unless /^#{selected.name}$/i.match?(dependency.name)
|
57
|
+
|
58
|
+
# We expect to find (basename).gem in the 'cache' directory. Furthermore,
|
59
|
+
# the name match must be exact (ignoring case).
|
60
|
+
path = find_in_cache File.basename selected.cache_file
|
61
|
+
|
62
|
+
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path
|
63
|
+
|
64
|
+
path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/proscenium/side_load.rb
CHANGED
@@ -4,7 +4,6 @@ module Proscenium
|
|
4
4
|
class SideLoad
|
5
5
|
JS_COMMENT = '<!-- [PROSCENIUM_JAVASCRIPTS] -->'
|
6
6
|
CSS_COMMENT = '<!-- [PROSCENIUM_STYLESHEETS] -->'
|
7
|
-
LAZY_COMMENT = '<!-- [PROSCENIUM_LAZY_SCRIPTS] -->'
|
8
7
|
|
9
8
|
module Controller
|
10
9
|
def self.included(child)
|
@@ -35,28 +34,16 @@ module Proscenium
|
|
35
34
|
|
36
35
|
return if !fragments && !included_comment
|
37
36
|
|
38
|
-
imports = Proscenium::Importer.imported.dup
|
39
|
-
paths_to_build = []
|
40
|
-
Proscenium::Importer.each_stylesheet(delete: true) do |x, _|
|
41
|
-
paths_to_build << x.delete_prefix('/')
|
42
|
-
end
|
43
|
-
|
44
|
-
result = Proscenium::Builder.build_to_path(paths_to_build.join(';'))
|
45
|
-
|
46
37
|
out = []
|
47
|
-
|
48
|
-
|
49
|
-
inpath.prepend '/'
|
50
|
-
outpath.delete_prefix! 'public'
|
51
|
-
|
52
|
-
next unless imports.key?(inpath)
|
53
|
-
|
54
|
-
import = imports[inpath]
|
55
|
-
opts = import[:css].is_a?(Hash) ? import[:css] : {}
|
38
|
+
Proscenium::Importer.each_stylesheet(delete: true) do |path, opts|
|
39
|
+
opts = opts[:css].is_a?(Hash) ? opts[:css] : {}
|
56
40
|
opts[:preload_links_header] = false if fragments
|
57
41
|
opts[:data] ||= {}
|
58
|
-
|
59
|
-
|
42
|
+
|
43
|
+
if Proscenium.config.cache_query_string.present?
|
44
|
+
path += "?#{Proscenium.config.cache_query_string}"
|
45
|
+
end
|
46
|
+
out << helpers.stylesheet_link_tag(path, extname: false, **opts)
|
60
47
|
end
|
61
48
|
|
62
49
|
if fragments
|
@@ -70,72 +57,43 @@ module Proscenium
|
|
70
57
|
return if response_body.nil?
|
71
58
|
return if response_body.first.blank? || !Proscenium::Importer.js_imported?
|
72
59
|
|
73
|
-
|
74
|
-
paths_to_build = []
|
75
|
-
Proscenium::Importer.each_javascript(delete: true) do |x, _|
|
76
|
-
paths_to_build << x.delete_prefix('/')
|
77
|
-
end
|
78
|
-
|
79
|
-
result = Proscenium::Builder.build_to_path(paths_to_build.join(';'))
|
80
|
-
|
81
|
-
included_js_comment = response_body.first.include?(JS_COMMENT)
|
82
|
-
included_lazy_comment = response_body.first.include?(LAZY_COMMENT)
|
60
|
+
included_comment = response_body.first.include?(JS_COMMENT)
|
83
61
|
fragments = if (fragment_header = request.headers['X-Fragment'])
|
84
62
|
fragment_header.split
|
85
63
|
end
|
86
64
|
|
87
|
-
if fragments
|
88
|
-
out = []
|
89
|
-
scripts = {}
|
90
|
-
result.split(';').each do |x|
|
91
|
-
inpath, outpath = x.split('::')
|
92
|
-
inpath.prepend '/'
|
93
|
-
outpath.delete_prefix! 'public'
|
94
|
-
|
95
|
-
next unless imports.key?(inpath)
|
96
|
-
|
97
|
-
if (import = imports[inpath]).delete(:lazy)
|
98
|
-
scripts[inpath] = import.merge(outpath:)
|
99
|
-
else
|
100
|
-
opts = import[:js].is_a?(Hash) ? import[:js] : {}
|
101
|
-
opts[:preload_links_header] = false if fragments
|
102
|
-
out << helpers.javascript_include_tag(outpath, extname: false, **opts)
|
103
|
-
end
|
104
|
-
end
|
65
|
+
return if !fragments && !included_comment
|
105
66
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
response_body.first.gsub! JS_COMMENT, out.join.html_safe
|
110
|
-
end
|
111
|
-
end
|
67
|
+
out = []
|
68
|
+
Proscenium::Importer.each_javascript(delete: true) do |path, opts|
|
69
|
+
next if opts.delete(:lazy)
|
112
70
|
|
113
|
-
|
71
|
+
opts = opts[:js].is_a?(Hash) ? opts[:js] : {}
|
72
|
+
opts[:preload_links_header] = false if fragments
|
114
73
|
|
115
|
-
|
116
|
-
|
117
|
-
lazy_script = helpers.content_tag 'script', type: 'application/json',
|
118
|
-
id: 'prosceniumLazyScripts' do
|
119
|
-
scripts.to_json.html_safe
|
74
|
+
if Proscenium.config.cache_query_string.present?
|
75
|
+
path += "?#{Proscenium.config.cache_query_string}"
|
120
76
|
end
|
77
|
+
out << helpers.javascript_include_tag(path, extname: false, **opts)
|
121
78
|
end
|
122
79
|
|
123
80
|
if fragments
|
124
|
-
response_body.first.prepend
|
125
|
-
elsif
|
126
|
-
response_body.first.gsub!
|
81
|
+
response_body.first.prepend out.join.html_safe
|
82
|
+
elsif included_comment
|
83
|
+
response_body.first.gsub! JS_COMMENT, out.join.html_safe
|
127
84
|
end
|
128
85
|
end
|
129
86
|
end
|
130
87
|
|
131
88
|
class << self
|
132
|
-
# Side loads the class, and its super classes that respond to `.source_path
|
89
|
+
# Side loads assets for the class, and its super classes that respond to `.source_path`, which
|
90
|
+
# should return a Pathname of the class source file.
|
133
91
|
#
|
134
92
|
# Set the `abstract_class` class variable to true in any class, and it will not be side
|
135
93
|
# loaded.
|
136
94
|
#
|
137
|
-
# If the class responds to `.sideload`, it will be called
|
138
|
-
#
|
95
|
+
# If the class responds to `.sideload`, it will be called after the regular side loading. You
|
96
|
+
# can use this to customise what is side loaded.
|
139
97
|
def sideload_inheritance_chain(obj, options)
|
140
98
|
return unless Proscenium.config.side_load
|
141
99
|
|
@@ -159,18 +117,28 @@ module Proscenium
|
|
159
117
|
|
160
118
|
klass = obj.class
|
161
119
|
while klass.respond_to?(:source_path) && klass.source_path && !klass.abstract_class
|
162
|
-
if
|
163
|
-
klass.sideload options
|
164
|
-
elsif options[:css] == false
|
120
|
+
if options[:css] == false
|
165
121
|
Importer.sideload klass.source_path, **options
|
166
122
|
else
|
167
123
|
Importer.sideload_js klass.source_path, **options
|
168
124
|
css_imports << klass.source_path
|
169
125
|
end
|
170
126
|
|
127
|
+
klass.sideload options if klass.respond_to?(:sideload)
|
128
|
+
|
171
129
|
klass = klass.superclass
|
172
130
|
end
|
173
131
|
|
132
|
+
# All regular CSS files (*.css) are ancestrally sideloaded. However, the first CSS module
|
133
|
+
# in the ancestry is also sideloaded in addition to the regular CSS files. This is because
|
134
|
+
# the CSS module digest will be different for each file, so we only sideload the first CSS
|
135
|
+
# module.
|
136
|
+
css_imports.each do |it|
|
137
|
+
break if Importer.sideload_css_module(it, **options).present?
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sideload regular CSS files in reverse order.
|
141
|
+
#
|
174
142
|
# The reason why we sideload CSS after JS is because the order of CSS is important.
|
175
143
|
# Basically, the layout should be loaded before the view so that CSS cascading works in the
|
176
144
|
# right direction.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"lockfileVersion": 1,
|
3
|
+
"workspaces": {
|
4
|
+
"": {
|
5
|
+
"name": "@proscenium/flash",
|
6
|
+
"dependencies": {
|
7
|
+
"dom-mutations": "^1.0.0",
|
8
|
+
},
|
9
|
+
"devDependencies": {
|
10
|
+
"sourdough-toast": "latest",
|
11
|
+
},
|
12
|
+
},
|
13
|
+
},
|
14
|
+
"packages": {
|
15
|
+
"dom-mutations": ["dom-mutations@1.0.0", "", {}, "sha512-qr4ufk/qu+JKwtz7NPbu6TxpTM/7nqburohI07J+mKSM20USvhcUjEb8hWY6g2a3QYp3LtlGpi+mAZLPuxTi7g=="],
|
16
|
+
|
17
|
+
"sourdough-toast": ["sourdough-toast@0.1.0", "", {}, "sha512-ianhWqaaA5a0n9TRg6dLEt5DWflXqsQUMxEAGZi8JcHGqsPRm7XTWEfhhDKSi/zJ11CBbvh8b8oRcgxjCeYZeg=="],
|
18
|
+
}
|
19
|
+
}
|
@@ -1,5 +1,9 @@
|
|
1
|
-
import domMutations from "
|
2
|
-
import { Sourdough, toast } from "
|
1
|
+
import domMutations from "dom-mutations";
|
2
|
+
import { Sourdough, toast } from "sourdough-toast";
|
3
|
+
|
4
|
+
export function foo() {
|
5
|
+
console.log("foo");
|
6
|
+
}
|
3
7
|
|
4
8
|
class HueFlash extends HTMLElement {
|
5
9
|
static observedAttributes = ["data-flash-alert", "data-flash-notice"];
|
@@ -0,0 +1,33 @@
|
|
1
|
+
export type Options = MutationObserverInit & {signal?: AbortSignal};
|
2
|
+
|
3
|
+
/**
|
4
|
+
@returns An async iterable that yields [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects representing individual mutations.
|
5
|
+
|
6
|
+
@example
|
7
|
+
```
|
8
|
+
import domMutations from 'dom-mutations';
|
9
|
+
|
10
|
+
const target = document.querySelector('#unicorn');
|
11
|
+
|
12
|
+
for await (const mutation of domMutations(target, {childList: true})) {
|
13
|
+
console.log('Mutation:', mutation);
|
14
|
+
}
|
15
|
+
```
|
16
|
+
*/
|
17
|
+
export default function domMutations(target: Node, options?: Options): AsyncIterable<MutationRecord>;
|
18
|
+
|
19
|
+
/**
|
20
|
+
Similar to `domMutations()`, but yields batches of [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects, each batch representing a group of mutations captured together. This method is less convenient, but can be useful in some cases when you need to handle mutations together as a group.
|
21
|
+
|
22
|
+
@example
|
23
|
+
```
|
24
|
+
import {batchedDomMutations} from 'dom-mutations';
|
25
|
+
|
26
|
+
const target = document.querySelector('#unicorn');
|
27
|
+
|
28
|
+
for await (const mutations of batchedDomMutations(target, {childList: true})) {
|
29
|
+
console.log('Batch of mutations:', mutations);
|
30
|
+
}
|
31
|
+
```
|
32
|
+
*/
|
33
|
+
export function batchedDomMutations(target: Node, options?: Options): AsyncIterable<MutationRecord[]>;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
export default function domMutations(target, options = {}) {
|
2
|
+
return {
|
3
|
+
async * [Symbol.asyncIterator]() {
|
4
|
+
for await (const mutations of batchedDomMutations(target, options)) {
|
5
|
+
yield * mutations;
|
6
|
+
}
|
7
|
+
},
|
8
|
+
};
|
9
|
+
}
|
10
|
+
|
11
|
+
export function batchedDomMutations(target, {signal, ...options} = {}) {
|
12
|
+
return {
|
13
|
+
async * [Symbol.asyncIterator]() {
|
14
|
+
signal?.throwIfAborted();
|
15
|
+
|
16
|
+
let resolveMutations;
|
17
|
+
let rejectMutations;
|
18
|
+
|
19
|
+
const observer = new globalThis.MutationObserver(mutations => {
|
20
|
+
resolveMutations?.(mutations);
|
21
|
+
});
|
22
|
+
|
23
|
+
observer.observe(target, options);
|
24
|
+
|
25
|
+
signal?.addEventListener('abort', () => {
|
26
|
+
rejectMutations?.(signal.reason);
|
27
|
+
observer.disconnect();
|
28
|
+
}, {once: true});
|
29
|
+
|
30
|
+
try {
|
31
|
+
while (true) {
|
32
|
+
signal?.throwIfAborted();
|
33
|
+
|
34
|
+
yield await new Promise((resolve, reject) => { // eslint-disable-line no-await-in-loop
|
35
|
+
resolveMutations = resolve;
|
36
|
+
rejectMutations = reject;
|
37
|
+
});
|
38
|
+
}
|
39
|
+
} finally {
|
40
|
+
observer.disconnect();
|
41
|
+
}
|
42
|
+
},
|
43
|
+
};
|
44
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|