isomorfeus-asset-manager 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +12 -0
  4. data/lib/iso_opal.rb +10 -0
  5. data/lib/isomorfeus/asset_manager/asset.rb +67 -0
  6. data/lib/isomorfeus/asset_manager/config.rb +69 -0
  7. data/lib/isomorfeus/asset_manager/js_import.rb +59 -0
  8. data/lib/isomorfeus/asset_manager/portfolio.rb +46 -0
  9. data/lib/isomorfeus/asset_manager/rack_middleware.rb +85 -0
  10. data/lib/isomorfeus/asset_manager/ruby_import.rb +33 -0
  11. data/lib/isomorfeus/asset_manager/server_socket_processor.rb +55 -0
  12. data/lib/isomorfeus/asset_manager/version.rb +5 -0
  13. data/lib/isomorfeus/asset_manager/view_helper.rb +9 -0
  14. data/lib/isomorfeus/asset_manager.rb +145 -0
  15. data/lib/isomorfeus-asset-manager.rb +27 -0
  16. data/node_modules/.bin/esbuild +12 -0
  17. data/node_modules/.bin/esbuild.cmd +17 -0
  18. data/node_modules/.bin/esbuild.ps1 +28 -0
  19. data/node_modules/.package-lock.json +18 -0
  20. data/node_modules/esbuild-wasm/README.md +3 -0
  21. data/node_modules/esbuild-wasm/bin/esbuild +84 -0
  22. data/node_modules/esbuild-wasm/esbuild.wasm +0 -0
  23. data/node_modules/esbuild-wasm/esm/browser.d.ts +397 -0
  24. data/node_modules/esbuild-wasm/esm/browser.js +2341 -0
  25. data/node_modules/esbuild-wasm/esm/browser.min.js +8 -0
  26. data/node_modules/esbuild-wasm/exit0.js +11 -0
  27. data/node_modules/esbuild-wasm/lib/browser.d.ts +397 -0
  28. data/node_modules/esbuild-wasm/lib/browser.js +2371 -0
  29. data/node_modules/esbuild-wasm/lib/browser.min.js +10 -0
  30. data/node_modules/esbuild-wasm/lib/main.d.ts +397 -0
  31. data/node_modules/esbuild-wasm/lib/main.js +1948 -0
  32. data/node_modules/esbuild-wasm/package.json +16 -0
  33. data/node_modules/esbuild-wasm/wasm_exec.js +654 -0
  34. data/package.json +9 -0
  35. metadata +162 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e92ad4c34e5b1c973ca4c16b8604fe46148c8e2eaad20b3e7dbe1f380094b13f
4
+ data.tar.gz: 4862aeaf75d7dc1394fc51c7948fc0c62d91b121c4ec971475b952057c21bf8e
5
+ SHA512:
6
+ metadata.gz: c746fdab0004a98d7a69d1b7e160b1334ba3a1c2e2eb9e8233ef524dd137b0bd45997c1f148168aae4afa27c6f92fc90d8aa2eabe2b69dbe7dfc971efd07d540
7
+ data.tar.gz: c76e75768ee2f5284a8e3b3745ef1e21a5799c4e7ff1134919fa8b963080398cabfb1717dfce18eaa9d569ceb738a0feb89f718fd65faf78cefa6504664e0d4d
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Jan Biedermann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # isomorfeus-asset-manager
2
+
3
+ Convenient and well performing on demand asset bundling for the isomorfeus framework project, internally using esbuild (esbuild-wasm).
4
+
5
+ Version follows esbuild version: 0.12.x comes with esbuild 0.12.y.
6
+
7
+ No need to install esbuild separately, everything bundled ready to go.
8
+ However, if within the project the 'esbuild' npm package is installed in node_modules, that version will be used instead.
9
+
10
+ ### Community and Support
11
+ At the [Isomorfeus Framework Project](http://isomorfeus.com)
12
+
data/lib/iso_opal.rb ADDED
@@ -0,0 +1,10 @@
1
+ module IsoOpal
2
+ # Just to prevent Opal from freezing Opal.paths
3
+ def self.paths
4
+ Opal.instance_variable_get(:@paths)
5
+ end
6
+
7
+ def self.paths_include?(path)
8
+ paths.include?(path)
9
+ end
10
+ end
@@ -0,0 +1,67 @@
1
+ module Isomorfeus
2
+ class AssetManager
3
+ class Asset
4
+ attr_reader :bundle, :bundle_size
5
+ attr_reader :bundle_gz, :bundle_gz_size
6
+ attr_reader :bundle_map
7
+ attr_reader :bundle_map_gz
8
+ attr_reader :mtime, :ruby_imports, :target
9
+
10
+ def initialize(target = :browser)
11
+ raise "Unknown asset target!" unless %i[browser node].include?(target)
12
+ @target = target
13
+ @bundled = false
14
+ @js_imports = []
15
+ @ruby_imports = []
16
+ end
17
+
18
+ def add_js_import(*args)
19
+ @js_imports << Isomorfeus::AssetManager::JsImport.new(*args)
20
+ end
21
+
22
+ def add_ruby_import(*args)
23
+ @ruby_imports << Isomorfeus::AssetManager::RubyImport.new(*args)
24
+ end
25
+
26
+ def bundle=(b)
27
+ @bundled = true
28
+ @bundle = b
29
+ @bundle_size = @bundle.size
30
+ unless @target == :node
31
+ @bundle_gz = Zlib::gzip(b, level: Zlib::BEST_COMPRESSION)
32
+ @bundle_gz_size = @bundle_gz.size
33
+ end
34
+ @mtime = Time.now
35
+ @bundle
36
+ end
37
+
38
+ def bundle_map=(m)
39
+ @bundled = true
40
+ @bundle_map = m
41
+ @bundle_map_gz = Zlib::gzip(m, level: Zlib::BEST_COMPRESSION) unless @target == :node
42
+ @mtime = Time.now unless @mtime
43
+ @bundle_map
44
+ end
45
+
46
+ def bundled?
47
+ @bundled
48
+ end
49
+
50
+ def ruby_modules
51
+ @ruby_imports.map(&:module_name)
52
+ end
53
+
54
+ def to_s
55
+ js = @target == :node ? '' : "if (typeof globalThis !== 'undefined' && typeof global === 'undefined') { globalThis.global = globalThis; }\n"
56
+ unless @js_imports.empty?
57
+ js << "#{@js_imports.map(&:to_s).join("\n")}"
58
+ end
59
+ js << "\n" if !@js_imports.empty? && !@ruby_imports.empty?
60
+ unless @ruby_imports.empty?
61
+ js << "#{@ruby_imports.map(&:to_s).join("\n")}"
62
+ end
63
+ js
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,69 @@
1
+ module Isomorfeus
2
+ # available settings
3
+ if RUBY_ENGINE == 'opal'
4
+ add_client_option(:assets_websocket_path)
5
+ add_client_option(:assets_path)
6
+ add_client_option(:opal_assets_path)
7
+ else
8
+ class << self
9
+ attr_reader :env
10
+ attr_accessor :root
11
+ attr_accessor :app_root
12
+ attr_accessor :assets_path
13
+ attr_accessor :node_paths
14
+ attr_accessor :assets
15
+
16
+ def add_web_js_import(*args)
17
+ Isomorfeus.assets['web.js'].add_js_import(*args)
18
+ end
19
+
20
+ def add_ssr_js_import(*args)
21
+ Isomorfeus.assets['ssr.js'].add_js_import(*args)
22
+ end
23
+
24
+ def add_common_js_import(*args)
25
+ Isomorfeus.assets['web.js'].add_js_import(*args)
26
+ Isomorfeus.assets['ssr.js'].add_js_import(*args)
27
+ end
28
+
29
+ def add_web_ruby_import(*args)
30
+ Isomorfeus.assets['web.js'].add_ruby_import(*args)
31
+ end
32
+
33
+ def add_ssr_ruby_import(*args)
34
+ Isomorfeus.assets['ssr.js'].add_ruby_import(*args)
35
+ end
36
+
37
+ def add_common_ruby_import(*args)
38
+ Isomorfeus.assets['web.js'].add_ruby_import(*args)
39
+ Isomorfeus.assets['ssr.js'].add_ruby_import(*args)
40
+ end
41
+
42
+ # client side env is initialized in isomorfeus-preact
43
+ def env=(env_string)
44
+ @env = env_string ? env_string.to_s : 'development'
45
+ @development = (@env == 'development') ? true : false
46
+ @production = (@env == 'production') ? true : false
47
+ @test = (@env == 'test') ? true : false
48
+ end
49
+
50
+ def development?
51
+ @development
52
+ end
53
+
54
+ def production?
55
+ @production
56
+ end
57
+
58
+ def test?
59
+ @test
60
+ end
61
+ end
62
+ end
63
+
64
+ self.assets_path = '/assets'
65
+ self.assets = {
66
+ 'web.js' => Isomorfeus::AssetManager::Asset.new(:browser),
67
+ 'ssr.js' => Isomorfeus::AssetManager::Asset.new(:node)
68
+ }
69
+ end
@@ -0,0 +1,59 @@
1
+ module Isomorfeus
2
+ class AssetManager
3
+ class JsImport
4
+ def initialize(package, js_constant = nil, imported_constants = nil, js_sub_constant = nil, import_assignment = nil)
5
+ @package = package
6
+ @js_constant = js_constant
7
+ @js_sub_constant = js_sub_constant
8
+ @imported_constants = imported_constants
9
+ @import_assignment = import_assignment
10
+ end
11
+
12
+ def to_s
13
+ js = ''
14
+ if @imported_constants
15
+ if @js_sub_constant
16
+ js << "import { #{@imported_constants} as #{@js_constant}_#{@js_sub_constant} } from \"#{@package}\";\n"
17
+ else
18
+ if @imported_constants.is_a?(String)
19
+ js << "import #{@imported_constants} from \"#{@package}\";\n"
20
+ else
21
+ js << "import { #{@imported_constants.join(", ")} } from \"#{@package}\";\n"
22
+ end
23
+ end
24
+ if @js_constant
25
+ js << "if (!global.#{@js_constant}) { global.#{@js_constant} = {}; }\n" if @js_sub_constant
26
+ if @js_constant
27
+ if @js_sub_constant
28
+ js << "global.#{@js_constant}.#{@js_sub_constant} = #{@js_constant}_#{@js_sub_constant};\n"
29
+ else
30
+ if @imported_constants.is_a?(String)
31
+ js << "global.#{@js_constant}.#{@import_assignment ? @import_assignment : @imported_constants} = #{@imported_constants};\n"
32
+ else
33
+ @imported_constants.each do |imported_constant|
34
+ js << "global.#{@js_constant}.#{@import_assignment ? @import_assignment : imported_constant} = #{imported_constant};\n"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ else
40
+ @imported_constants.each do |imported_constant|
41
+ js << "global.#{imported_constant} = #{imported_constant};\n"
42
+ end
43
+ end
44
+ elsif @js_constant.nil?
45
+ js << "import \"#{@package}\";\n"
46
+ else
47
+ if @js_constant[0] == @js_constant[0].upcase
48
+ js << "import * as #{@js_constant} from \"#{@package}\";\n"
49
+ js << "global.#{@js_constant} = #{@js_constant};\n"
50
+ else
51
+ js << "import #{@js_constant} from \"#{@package}\";\n"
52
+ js << "global.#{@js_constant} = #{@js_constant};\n"
53
+ end
54
+ end
55
+ js
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,46 @@
1
+ module Isomorfeus
2
+ class AssetManager
3
+ class Portfolio
4
+ def self.get_load_path_entries(path, filter)
5
+ path_entries = []
6
+ return [] unless Dir.exist?(path)
7
+ dir_entries = Dir.entries(path)
8
+ dir_entries.each do |entry|
9
+ next if entry == '.'
10
+ next if entry == '..'
11
+ next unless entry
12
+ absolute_path = File.join(path, entry)
13
+ if File.directory?(absolute_path)
14
+ more_path_entries = get_load_path_entries(absolute_path, filter)
15
+ path_entries.push(*more_path_entries) if more_path_entries.size > 0
16
+ elsif (absolute_path.end_with?('.rb') || absolute_path.end_with?('.js')) && File.file?(absolute_path)
17
+ push_entry = true
18
+ if filter && filter.size > 0
19
+ filter.each do |filter_entry|
20
+ push_entry = false if absolute_path.end_with?(filter_entry)
21
+ end
22
+ end
23
+ path_entries.push(absolute_path) if push_entry
24
+ end
25
+ end
26
+ path_entries
27
+ end
28
+
29
+ def self.get_load_paths_json(*filter)
30
+ cwd = Dir.pwd
31
+
32
+ load_paths = Opal.paths.uniq
33
+ load_path_entries = []
34
+
35
+ load_paths.each do |path|
36
+ next if path.start_with?(cwd)
37
+ more_path_entries = get_load_path_entries(path, filter)
38
+ load_path_entries.push(*more_path_entries) if more_path_entries.size > 0
39
+ end
40
+
41
+ cache_obj = { 'opal_load_paths' => load_paths, 'opal_load_path_entries' => load_path_entries }
42
+ Oj.dump(cache_obj, mode: :strict, indent: 2)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isomorfeus
4
+ class AssetManager
5
+ class RackMiddleware
6
+ WS_RESPONSE = [0, {}, []]
7
+ attr_reader :asset_manager
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ @asset_manager = Isomorfeus::AssetManager.new
12
+ @compressible_types = %w[application/javascript text/javascript]
13
+ if Isomorfeus.assets_path.end_with?('/')
14
+ @assets_path = Isomorfeus.assets_path
15
+ @assets_path_size = @assets_path.size
16
+ else
17
+ @assets_path = Isomorfeus.assets_path + '/'
18
+ @assets_path_size = @assets_path.size
19
+ end
20
+ end
21
+
22
+ def call(env)
23
+ begin
24
+ path_info = env['PATH_INFO']
25
+ if path_info.start_with?(@assets_path)
26
+ asset_key = path_info[@assets_path_size..-1]
27
+
28
+ # get js
29
+ if Isomorfeus.assets.has_key?(asset_key)
30
+ asset = Isomorfeus.assets[asset_key]
31
+ if asset && asset.target != :node
32
+ asset_manager.transition(asset_key, asset)
33
+ headers = {}
34
+ headers['Last-Modified'] = asset.mtime
35
+ headers[Rack::CONTENT_TYPE] = 'application/javascript'
36
+ if should_gzip?(env)
37
+ headers['Content-Encoding'] = "gzip"
38
+ headers[Rack::CONTENT_LENGTH] = asset.bundle_gz_size
39
+ return [200, headers, asset.bundle_gz]
40
+ else
41
+ headers[Rack::CONTENT_LENGTH] = asset.bundle_size
42
+ return [200, {}, asset.bundle]
43
+ end
44
+ end
45
+
46
+ # get source map
47
+ elsif asset_key.end_with?('.js.map')
48
+ asset = Isomorfeus.assets[asset_key[0..-5]]
49
+ if asset && asset.target != :node
50
+ asset_manager.transition(asset_key, asset) unless asset.bundled?
51
+ headers = {}
52
+ headers['Last-Modified'] = asset.mtime
53
+ headers[Rack::CONTENT_TYPE] = 'application/json'
54
+ if should_gzip?(env)
55
+ headers['Content-Encoding'] = "gzip"
56
+ headers[Rack::CONTENT_LENGTH] = asset.bundle_map_gz.size
57
+ return [200, headers, asset.bundle_map_gz]
58
+ else
59
+ headers[Rack::CONTENT_LENGTH] = asset.bundle_map.size
60
+ return [200, {}, asset.bundle_map]
61
+ end
62
+ end
63
+ end
64
+ # elsif Isomorfeus.development? && path_info == Isomorfeus.api_asset_hmr_socket_path
65
+ # if env['rack.upgrade?'] == :websocket
66
+ # env['rack.upgrade'] = Isomorfeus::AssetManager::ServerSocketProcessor.new
67
+ # end
68
+ # WS_RESPONSE
69
+ end
70
+ rescue Exception => e
71
+ STDERR.puts "#{e}\n#{e.backtrace.join("\n")}"
72
+ @app.call(env)
73
+ end
74
+ @app.call(env)
75
+ end
76
+
77
+ def should_gzip?(env)
78
+ return true if env.has_key?('HTTP_ACCEPT_ENCODING') && env['HTTP_ACCEPT_ENCODING'].match?(/\bgzip\b/)
79
+ return false if /\bno-transform\b/.match?(env[Rack::CACHE_CONTROL].to_s) || env['Content-Encoding']&.!~(/\bidentity\b/)
80
+ return false if @compressible_types && !(env.has_key?(Rack::CONTENT_TYPE) && @compressible_types.include?(env[Rack::CONTENT_TYPE][/[^;]*/]))
81
+ true
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,33 @@
1
+ module Isomorfeus
2
+ class AssetManager
3
+ class RubyImport
4
+ attr_reader :module_name
5
+
6
+ def initialize(ruby_module)
7
+ @ruby_module = ruby_module.end_with?('.rb') ? ruby_module : ruby_module + '.rb'
8
+ @module_name = @ruby_module[0..-4]
9
+ @import_name = @module_name.tr('/', '_')
10
+ end
11
+
12
+ def resolved_path
13
+ @resolved_path ||= resolve_path
14
+ end
15
+
16
+ def to_s
17
+ "import(\"./#{@module_name}.js\");\n"
18
+ end
19
+
20
+ private
21
+
22
+ def resolve_path
23
+ split_ruby_path = @ruby_module.split('/')
24
+ Opal.paths.each do |path|
25
+ file = File.expand_path(File.join(*path.split('/').concat(split_ruby_path)))
26
+ return file if File.exist?(file)
27
+ end
28
+ STDERR.puts "#{self.class.name}: unable to resolve ruby module #{@module_name}"
29
+ nil
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,55 @@
1
+ module Isomorfeus
2
+ class AssetManager
3
+ class ServerSocketProcessor
4
+ # include Isomorfeus::Transport::ServerProcessor
5
+
6
+ # def on_message(client, data)
7
+ # # TODO
8
+ # if Isomorfeus.development?
9
+ # write_lock = Isomorfeus.zeitwerk_lock.try_write_lock
10
+ # if write_lock
11
+ # Isomorfeus.zeitwerk.reload
12
+ # Isomorfeus.zeitwerk_lock.release_write_lock
13
+ # end
14
+ # Isomorfeus.zeitwerk_lock.acquire_read_lock
15
+ # end
16
+ # request_hash = Oj.load(data, mode: :strict)
17
+ # handler_instance_cache = {}
18
+ # response_agent_array = []
19
+ # Thread.current[:isomorfeus_user] = user(client)
20
+ # Thread.current[:isomorfeus_pub_sub_client] = client
21
+ # process_request(request_hash, handler_instance_cache, response_agent_array)
22
+ # handler_instance_cache.each_value do |handler|
23
+ # handler.resolve if handler.resolving?
24
+ # end
25
+ # result = {}
26
+ # response_agent_array.each do |response_agent|
27
+ # result.deep_merge!(response_agent.result)
28
+ # end
29
+ # client.write Oj.dump(result, mode: :strict) unless result.empty?
30
+ # ensure
31
+ # Thread.current[:isomorfeus_user] = nil
32
+ # Thread.current[:isomorfeus_pub_sub_client] = nil
33
+ # Isomorfeus.zeitwerk_lock.release_read_lock if Isomorfeus.development?
34
+ # end
35
+
36
+ # def on_close(client)
37
+ # # nothing for now
38
+ # end
39
+
40
+ # def on_open(client)
41
+ # # nothing for now
42
+ # end
43
+
44
+ # def on_shutdown(client)
45
+ # # nothing for now
46
+ # end
47
+
48
+ # def user(client)
49
+ # current_user = client.instance_variable_get(:@isomorfeus_user)
50
+ # return current_user if current_user
51
+ # Anonymous.new
52
+ # end
53
+ end
54
+ end
55
+ end