Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #37102 - webpack 5 #9834

Merged
merged 1 commit into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ gem 'sprockets-rails', '~> 3.0'
gem 'responders', '~> 3.0'
gem 'roadie-rails', '~> 3.0'
gem 'deacon', '~> 1.0'
gem 'webpack-rails', '~> 0.9.8'
gem 'mail', '~> 2.7'
gem 'sshkey', '~> 2.0'
gem 'dynflow', '>= 1.6.5', '< 2.0.0'
Expand Down
6 changes: 4 additions & 2 deletions Procfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Run Rails & Webpack concurrently
# If you wish to use a different server then the default, use e.g. `export RAILS_STARTUP='puma -w 3 -p 3000 --preload'`
rails: [ -n "$RAILS_STARTUP" ] && env PRY_WARNING=1 $RAILS_STARTUP || [ -n "$BIND" ] && bin/rails server -b $BIND || env PRY_WARNING=1 bin/rails server
# you can use WEBPACK_OPTS to customize webpack server, e.g. 'WEBPACK_OPTS='--https --key /path/to/key --cert /path/to/cert.pem --cacert /path/to/cacert.pem' foreman start '
webpack: [ -n "$NODE_ENV" ] && ./node_modules/.bin/webpack-dev-server-without-h2 --config config/webpack.config.js $WEBPACK_OPTS || env NODE_ENV=development ./node_modules/.bin/webpack-dev-server-without-h2 --config config/webpack.config.js $WEBPACK_OPTS

# you can use WEBPACK_OPTS to customize webpack server, e.g. 'WEBPACK_OPTS=--progress' foreman start '
# filter out webpack options that are commonly used but not supported by webpack 5 and not needed in the new configutation as webpack is not run as a server anymore
webpack: FILTERED_WEBPACK_OPTS=$(echo $WEBPACK_OPTS | sed -e 's/--key [^ ]*//g' -e 's/--public [^ ]*//g' -e 's/--https [^ ]*//g' -e 's/--cert [^ ]*//g' -e 's/--cacert [^ ]*//g') && [ -n "$NODE_ENV" ] && npx webpack --config config/webpack.config.js --watch $FILTERED_WEBPACK_OPTS || env NODE_ENV=development npx webpack --config config/webpack.config.js --watch $FILTERED_WEBPACK_OPTS
1 change: 1 addition & 0 deletions app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//= link late_load.js
//= link_tree ../../../vendor/assets/fonts
//= link_tree ../images
//= link application.css
Expand Down
16 changes: 14 additions & 2 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,22 @@
//= require lookup_keys

$(function() {
$(document).trigger('ContentLoad');
if(window.allJsLoaded){
$(document).trigger('ContentLoad');
}
else {
$(document).on('loadJS', function() {
Ron-Lavi marked this conversation as resolved.
Show resolved Hide resolved
$(document).trigger('ContentLoad');
});}
});


// Override jQuery's ready function to run only after all scripts are loaded instead of when the DOM is ready
$.fn.ready = function(fn) {
this.on('loadJS', fn);
return this;
};

// Prevents all links with the disabled attribute set to "disabled"
// from being clicked.
var handleDisabledClick = function(event, element){
Expand All @@ -21,7 +34,6 @@ var handleDisabledClick = function(event, element){
$(document).on('click', 'a[disabled="disabled"]', function(event) {
return handleDisabledClick(event, this);
});

function onContentLoad() {
if ($('input[focus_on_load=true]').length > 0) {
$('input[focus_on_load]')
Expand Down
53 changes: 53 additions & 0 deletions app/assets/javascripts/late_load.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function load_dynamic_javascripts(html) {
function waitForAllLoaded() {
// Wait for all plugins js modules to be loaded before loading the javascript content
return new Promise(function(resolve) {
// window.allPluginsLoaded is set to {} when plugins are starting to load
// if there are no plugins window.allPluginsLoaded is never defined
if (window.allPluginsLoaded === undefined || Object.values(window.allPluginsLoaded).every(Boolean)) {
resolve();
} else {
function handleLoad() {
if (window.allPluginsLoaded === undefined || Object.values(window.allPluginsLoaded).every(Boolean)) {
resolve();
// Remove the event listener
document.removeEventListener('loadPlugin', handleLoad);
}
}
document.addEventListener('loadPlugin', handleLoad);
}
});
}
waitForAllLoaded().then(async function() {
// parse html string
var template = document.createElement('template');
template.innerHTML = html;
var doc = new DOMParser().parseFromString(html, 'text/html');
var copyChildren = [...doc.head.children];
const loadScript = async scripts => {
if (scripts.length === 0) {
// All scripts are loaded
window.allJsLoaded = true;
const loadJS = new Event('loadJS');
document.dispatchEvent(loadJS);
return;
}
const script = scripts.shift();
if (script.src) {
// if script is just a link, add it to the head
const scriptTag = document.createElement('script');
scriptTag.src = script.src;
scriptTag.onload = function() {
// To load the next script only after the current one is loaded
loadScript(scripts);
};
document.head.appendChild(scriptTag);
} else {
// if the script is a script tag, evaluate it and load the next one
await eval(script.innerHTML);
loadScript(scripts);
}
};
loadScript(copyChildren);
});
}
16 changes: 0 additions & 16 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class ApplicationController < ActionController::Base
before_action :set_taxonomy, :require_mail, :check_empty_taxonomy
before_action :authorize
before_action :welcome, :find_selected_columns, :only => :index, :unless => :api_request?
prepend_before_action :allow_webpack, if: -> { Rails.configuration.webpack.dev_server.enabled }
around_action :set_timezone

attr_reader :original_search_parameter
Expand Down Expand Up @@ -399,21 +398,6 @@ def parameter_filter_context
Foreman::ParameterFilter::Context.new(:ui, controller_name, params[:action])
end

def allow_webpack
webpack_csp = {
script_src: [webpack_server], connect_src: [webpack_server],
style_src: [webpack_server], img_src: [webpack_server],
font_src: ["data: #{webpack_server}"], default_src: [webpack_server]
}

append_content_security_policy_directives(webpack_csp)
end

def webpack_server
port = Rails.configuration.webpack.dev_server.port
@dev_server ||= "#{request.protocol}#{request.host}:#{port}"
end

class << self
def parameter_filter_context
Foreman::ParameterFilter::Context.new(:ui, controller_name, nil)
Expand Down
5 changes: 0 additions & 5 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,6 @@ def hosts_count(resource_name = controller.resource_name)
@hosts_count ||= HostCounter.new(resource_name)
end

def webpack_dev_server
return unless Rails.configuration.webpack.dev_server.enabled
javascript_include_tag "#{@dev_server}/webpack-dev-server.js"
end

def accessible_resource_records(resource, order = :name)
klass = resource.to_s.classify.constantize
klass = klass.with_taxonomy_scope_override(@location, @organization) if klass.include? Taxonomix
Expand Down
23 changes: 23 additions & 0 deletions app/helpers/layout_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,29 @@ def javascript(*args)
content_for(:javascripts) { javascript_include_tag(*args) }
end

def javascript_include_tag(*params, **kwargs)
# Workaround for overriding javascript load with webpack_asset_paths, should be removed when webpack_asset_paths is removed
if kwargs[:source] == "webpack_asset_paths"
kwargs[:webpacked]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no super call here, so it does nothing. Is that intentional, or did you mean to unconditionally call super(...)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont want to call super cause it returns a script tag,
and the webpacked key contains for example: webpacked=>"<script>\n//<![CDATA[\nwindow.tfm.tools.loadPluginModule('/webpack/foreman_remote_execution','foreman_remote_execution','./index');\n//]]>\n</script>"
which we declare in: webpacked: webpacked_plugins_js_for(plugin_name.to_sym)

else
super(*params, **kwargs)
end
end

# @deprecated Previously provided by webpack-rails
def webpack_asset_paths(plugin_name, extension: 'js')
MariaAga marked this conversation as resolved.
Show resolved Hide resolved
if extension == 'js'
Foreman::Deprecation.deprecation_warning('3.12', '`webpack_asset_paths` is deprecated, use `content_for(:javascripts) { webpacked_plugins_js_for(plugin_name) }` instead.')
[{
source: 'webpack_asset_paths',
webpacked: webpacked_plugins_js_for(plugin_name.to_sym),
}]
elsif extension == 'css'
Foreman::Deprecation.deprecation_warning('3.12', '`webpack_asset_paths` is deprecated and not needed for css assets.')
nil
end
end

# The target should have class="collapse [out|in]" out means collapsed on load and in means expanded.
# Target must also have a unique id.
def collapsing_header(title, target, collapsed = '')
Expand Down
47 changes: 28 additions & 19 deletions app/helpers/reactjs_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'webpack-rails'
require 'json'

module ReactjsHelper
# Mount react component in views
# Params:
Expand All @@ -11,10 +12,6 @@ def react_component(name, props = {})
content_tag('foreman-react-component', '', :name => name, :data => { props: props })
end

def webpacked_plugins_with_global_css
global_css_tags(global_plugins_list).join.html_safe
end

def webpacked_plugins_js_for(*plugin_names)
js_tags_for(select_requested_plugins(plugin_names)).join.html_safe
end
Expand All @@ -24,7 +21,26 @@ def webpacked_plugins_with_global_js
end

def webpacked_plugins_css_for(*plugin_names)
css_tags_for(select_requested_plugins(plugin_names)).join.html_safe
Foreman::Deprecation.deprecation_warning('3.12', '`webpacked_plugins_css_for` is deprecated, plugin css is already loaded.')
MariaAga marked this conversation as resolved.
Show resolved Hide resolved
nil
end

def read_webpack_manifest
JSON.parse(Rails.root.join('public/webpack/manifest.json').read)
end

def get_webpack_foreman_vendor_js
Rails.cache.fetch('webpack_foreman_vendor_js', expires_in: 1.minute) do
data = read_webpack_manifest
foreman_vendor_js = data['assetsByChunkName']['foreman-vendor'].find { |value| value.end_with?('.js') }
javascript_include_tag("/webpack/#{foreman_vendor_js}")
end
end

def get_webpack_foreman_vendor_css
data = read_webpack_manifest
foreman_vendor_css = data['assetsByChunkName']['foreman-vendor'].find { |value| value.end_with?('.css') }
stylesheet_link_tag("/webpack/#{foreman_vendor_css}")
end

def select_requested_plugins(plugin_names)
Expand All @@ -39,30 +55,23 @@ def select_requested_plugins(plugin_names)

def js_tags_for(requested_plugins)
requested_plugins.map do |plugin|
javascript_include_tag(*webpack_asset_paths(plugin.to_s, :extension => 'js'))
name = plugin.to_s.tr('-', '_')
javascript_tag("window.tfm.tools.loadPluginModule('/webpack/#{name}','#{name}','./index');".html_safe)
end
end

def global_js_tags(requested_plugins)
requested_plugins.map do |plugin|
plugin[:files].map do |file|
javascript_include_tag(*webpack_asset_paths("#{plugin[:id]}:#{file}", :extension => 'js'))
end
end
end

def global_css_tags(requested_plugins)
requested_plugins.map do |plugin|
plugin[:files].map do |file|
stylesheet_link_tag(*webpack_asset_paths("#{plugin[:id]}:#{file}", :extension => 'css'))
name = plugin[:id].to_s.tr('-', '_')
javascript_tag("window.tfm.tools.loadPluginModule('/webpack/#{name}','#{name}','./#{file}_index');".html_safe)
end
end
end

def css_tags_for(requested_plugins)
requested_plugins.map do |plugin|
stylesheet_link_tag(*webpack_asset_paths(plugin.to_s, :extension => 'css'))
end
Foreman::Deprecation.deprecation_warning('3.12', '`css_tags_for` is deprecated, No need to load CSS separately, since it should be referenced from the corresponding JS file.')
[]
end

def locale_js_tags
Expand Down
2 changes: 0 additions & 2 deletions app/services/foreman/env_settings_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ def settings_map
'FOREMAN_REQUIRE_SSL' => [:boolean, :require_ssl],
'FOREMAN_SUPPORT_JSONP' => [:boolean, :support_jsonp],
'FOREMAN_MARK_TRANSLATED' => [:boolean, :mark_translated],
'FOREMAN_WEBPACK_DEV_SERVER' => [:boolean, :webpack_dev_server],
'FOREMAN_WEBPACK_DEV_SERVER_HTTPS' => [:boolean, :webpack_dev_server_https],
'FOREMAN_ASSETS_DEBUG' => [:boolean, :assets_debug],
'FOREMAN_HSTS_ENABLED' => [:boolean, :hsts_enabled],
'FOREMAN_DOMAIN' => [:string, :domain],
Expand Down
18 changes: 10 additions & 8 deletions app/views/layouts/base.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@

<%= favicon_link_tag "favicon.ico"%>

<%= stylesheet_link_tag *webpack_asset_paths('foreman-vendor', :extension => 'css') %>
<%= stylesheet_link_tag *webpack_asset_paths('bundle', :extension => 'css') %>
<%= get_webpack_foreman_vendor_css %>
<%= stylesheet_link_tag 'application' %>
<%= webpacked_plugins_with_global_css %>
<%= yield(:stylesheets) %>

<%= csrf_meta_tags %>
Expand All @@ -38,13 +36,17 @@
</head>

<body class='<%= body_css_classes %>'>
<%= javascript_include_tag *webpack_asset_paths('foreman-vendor', :extension => 'js') %>
<%= javascript_include_tag *webpack_asset_paths('vendor', :extension => 'js') %>
<%= javascript_include_tag *webpack_asset_paths('bundle', :extension => 'js') %>
<%= get_webpack_foreman_vendor_js %>
<%= javascript_include_tag('/webpack/vendor.js') %>
<%= javascript_include_tag('/webpack/bundle.js') %>

<%= javascript_include_tag 'application' %>
<%= webpacked_plugins_with_global_js %>
<%= webpack_dev_server %>
<%= yield(:javascripts) %>
<%= javascript_include_tag('late_load') %>
<script type="text/javascript">
const html = "<%= escape_javascript(yield(:javascripts)) %>"
load_dynamic_javascripts(html);
</script>
<script type="text/javascript">
<%= yield(:inline_javascripts) %>
</script>
Expand Down
4 changes: 0 additions & 4 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@
end
end

# Allow disabling the webpack dev server from the settings
config.webpack.dev_server.enabled = SETTINGS.fetch(:webpack_dev_server, true)
config.webpack.dev_server.https = SETTINGS.fetch(:webpack_dev_server_https, false)

config.hosts += SETTINGS[:hosts]
config.hosts << SETTINGS[:fqdn]
# Backporting from Rails 7.0
Expand Down
2 changes: 0 additions & 2 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false

config.webpack.dev_server.enabled = false

# Log denied attributes into logger
config.action_controller.action_on_unpermitted_parameters = :log

Expand Down
2 changes: 0 additions & 2 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@
# Randomize the order test cases are executed.
config.active_support.test_order = :random

config.webpack.dev_server.enabled = false

# Whitelist all plugin engines by default from raising errors on deprecation warnings for
# compatibility, allow them to override it by adding an ASDT configuration file.
config.after_initialize do
Expand Down
40 changes: 0 additions & 40 deletions config/initializers/assets.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
module Webpack
module Rails
class Manifest
class << self
attr_writer :manifest
end
end
end
end

# Be sure to restart your server when you modify this file.
Foreman::Application.configure do |app|
# Version of your assets, change this if you want to expire all your assets.
Expand Down Expand Up @@ -53,35 +43,5 @@ class << self
ActionView::Base.assets_manifest = app.assets_manifest
end
end

# When the dev server is enabled, this static manifest file is ignored and
# always retrieved from the dev server.
#
# Otherwise we need to combine all the chunks from the various webpack
# manifests. This is the main foreman manifest and all plugins that may
# have one. We then store this in the webpack-rails manifest using our
# monkey patched function.
unless config.webpack.dev_server.enabled
if (webpack_manifest_file = Dir.glob("#{Rails.root}/public/webpack/manifest.json").first)
webpack_manifest = JSON.parse(File.read(webpack_manifest_file))

Foreman::Plugin.with_webpack.each do |plugin|
manifest_path = plugin.webpack_manifest_path
next unless manifest_path

Rails.logger.debug { "Loading #{plugin.id} webpack asset manifest from #{manifest_path}" }
assets = JSON.parse(File.read(manifest_path))

plugin_id = plugin.id.to_s
assets['assetsByChunkName'].each do |chunk, filename|
if chunk == plugin_id || chunk.start_with?("#{plugin_id}:")
webpack_manifest['assetsByChunkName'][chunk] = filename
end
end
end

Webpack::Rails::Manifest.manifest = webpack_manifest
end
end
end
end
Loading