diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8ccea764e..224a1a16c9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: gemfile: [gemfiles/rails_7.0.gemfile] orm: [active_record] adapter: [sqlite3] - asset: [webpacker] + asset: [webpack] include: - ruby: 2.6 gemfile: gemfiles/rails_6.0.gemfile @@ -33,7 +33,7 @@ jobs: gemfile: gemfiles/rails_7.0.gemfile orm: active_record adapter: mysql2 - asset: sprockets + asset: importmap - ruby: "3.0" gemfile: gemfiles/rails_7.0.gemfile orm: active_record @@ -107,6 +107,7 @@ jobs: - name: Setup application env: BUNDLE_GEMFILE: ../../${{ matrix.gemfile }} + CI_ASSET: ${{ matrix.asset }} CI_DB_ADAPTER: ${{ matrix.adapter }} RAILS_ENV: test run: | @@ -114,6 +115,10 @@ jobs: cd spec/dummy_app bundle exec rake rails_admin:prepare_ci_env db:create db:migrate yarn install + case "$CI_ASSET" in + "webpack" ) yarn build && yarn build:css ;; + "importmap" ) yarn build:css ;; + esac cd ../../ - name: Run tests run: bundle exec rspec diff --git a/.gitignore b/.gitignore index 423c172d27..cce19e8d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,6 @@ spec/dummy_app/db/*.sqlite3-journal spec/dummy_app/db/schema.rb spec/dummy_app/log/*.log spec/dummy_app/public/uploads -spec/dummy_app/Gemfile.lock +spec/dummy_app/Gemfile*.lock tmp/**/* nbproject diff --git a/.prettierignore b/.prettierignore index 9f296e3fa2..aa6741b99c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,6 @@ coverage lib/generators/rails_admin/templates +spec/dummy_app/app/assets/builds spec/dummy_app/public spec/dummy_app/tmp spec/support/jquery.simulate.drag-sortable.js diff --git a/Appraisals b/Appraisals index 46355b0ba9..08b6a0a8a3 100644 --- a/Appraisals +++ b/Appraisals @@ -70,6 +70,7 @@ end appraise 'rails-7.0' do gem 'rails', '~> 7.0.0' + gem 'importmap-rails', require: false gem 'sassc-rails', '~> 2.1' gem 'devise', '~> 4.8' diff --git a/app/views/layouts/rails_admin/_head.html.erb b/app/views/layouts/rails_admin/_head.html.erb index 7cb0b6aa5c..01c2f1a92b 100644 --- a/app/views/layouts/rails_admin/_head.html.erb +++ b/app/views/layouts/rails_admin/_head.html.erb @@ -15,6 +15,15 @@ <% when :webpack %> <%= stylesheet_link_tag "rails_admin.css", media: :all, data: {'turbo-track': 'reload'} %> <%= javascript_include_tag "rails_admin.js", async: true, data: {'turbo-track': 'reload'} %> +<% when :importmap %> + <%= stylesheet_link_tag "rails_admin.css", media: :all, data: {'turbo-track': 'reload'} %> + <%= javascript_inline_importmap_tag(RailsAdmin::Engine.importmap.to_json(resolver: self)) %> + <%= javascript_importmap_module_preload_tags(RailsAdmin::Engine.importmap) %> + <%= javascript_importmap_shim_nonce_configuration_tag %> + <%= javascript_importmap_shim_tag %> + <%= # Preload jQuery and make it global, unless jQuery UI fails to initialize + tag.script "import jQuery from 'jquery'; window.jQuery = jQuery;".html_safe, type: "module" %> + <%= javascript_import_module_tag 'rails_admin' %> <% else raise "Unknown asset_source: #{RailsAdmin::config.asset_source}" end %> \ No newline at end of file diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index 420ab6a0e8..b5449a2d46 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -7,6 +7,7 @@ gem "devise", "~> 4.8" gem "rails", "~> 7.0.0" gem "webpacker", require: false gem "webrick", "~> 1.7" +gem "importmap-rails", require: false gem "sassc-rails", "~> 2.1" group :active_record do diff --git a/lib/generators/rails_admin/importmap_formatter.rb b/lib/generators/rails_admin/importmap_formatter.rb new file mode 100644 index 0000000000..6b40080f2b --- /dev/null +++ b/lib/generators/rails_admin/importmap_formatter.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'importmap/packager' + +module RailsAdmin + class ImportmapFormatter + attr_reader :packager + + def initialize(path = 'confing/importmap.rails_admin.rb') + @packager = Importmap::Packager.new(path) + end + + def format + imports = packager.import("rails_admin@#{RailsAdmin::Version.js}") + + # Use ESM compatible version to work around https://github.com/cljsjs/packages/issues/1579 + imports['@popperjs/core'].gsub!('lib/index.js', 'dist/esm/popper.js') + + # Tidy up jQuery UI dependencies + jquery_uis = imports.keys.filter { |key, _| key =~ /jquery-ui/ } + imports['jquery-ui/'] = imports[jquery_uis.first].gsub(%r{(@[^/@]+)/[^@]+$}, '\1/') + imports.reject! { |key, _| jquery_uis.include? key } + + pins = ['pin "rails_admin", preload: true', packager.pin_for('rails_admin/src/rails_admin/base', imports.delete('rails_admin'))] + (pins + imports.map { |package, url| packager.pin_for(package, url) }).join("\n") + end + end +end diff --git a/lib/generators/rails_admin/install_generator.rb b/lib/generators/rails_admin/install_generator.rb index db48214292..3e36a46b54 100644 --- a/lib/generators/rails_admin/install_generator.rb +++ b/lib/generators/rails_admin/install_generator.rb @@ -10,7 +10,7 @@ class InstallGenerator < Rails::Generators::Base include Generators::Utils::InstanceMethods argument :_namespace, type: :string, required: false, desc: 'RailsAdmin url namespace' - class_option :asset, type: :string, required: false, default: nil, desc: 'Asset delivery method [options: webpacker, sprockets]' + class_option :asset, type: :string, required: false, default: nil, desc: 'Asset delivery method [options: webpacker, webpack, sprockets, importmap]' desc 'RailsAdmin installation generator' def install @@ -29,10 +29,14 @@ def install case asset when 'webpack' configure_for_webpack + when 'importmap' + configure_for_importmap when 'webpacker' configure_for_webpacker5 when 'sprockets' configure_for_sprockets + else + raise "Unknown asset source: #{asset}" end end @@ -43,6 +47,10 @@ def asset if defined?(Webpacker) 'webpacker' + elsif Rails.root.join('webpack.config.js').exist? + 'webpack' + elsif Rails.root.join('config/importmap.rb').exist? + 'importmap' else 'sprockets' end @@ -54,17 +62,66 @@ def configure_for_sprockets def configure_for_webpacker5 run "yarn add rails_admin@#{RailsAdmin::Version.js}" - @scss_relative_dir = '../stylesheets/' - template 'rails_admin.js.erb', 'app/javascript/packs/rails_admin.js' - template 'rails_admin.scss', 'app/javascript/stylesheets/rails_admin.scss' + template 'rails_admin.webpacker.js', 'app/javascript/packs/rails_admin.js' + template 'rails_admin.scss.erb', 'app/javascript/stylesheets/rails_admin.scss' end def configure_for_webpack - run "yarn add rails_admin@#{RailsAdmin::Version.js} css-loader mini-css-extract-plugin sass sass-loader" - @scss_relative_dir = './' - template 'rails_admin.js.erb', 'app/javascript/rails_admin.js' - template 'rails_admin.scss', 'app/javascript/rails_admin.scss' - template 'webpack.config.js', 'webpack.config.js' + run "yarn add rails_admin@#{RailsAdmin::Version.js}" + template 'rails_admin.js', 'app/javascript/rails_admin.js' + webpack_config = File.join(destination_root, 'webpack.config.js') + marker = %r{application: ["']./app/javascript/application.js["']} + if File.exist?(webpack_config) && File.read(webpack_config) =~ marker + insert_into_file 'webpack.config.js', %(,\n rails_admin: "./app/javascript/rails_admin.js"), after: marker + else + say 'Add `rails_admin: "./app/javascript/rails_admin.js"` to the entry section in your webpack.config.js.', :red + end + setup_css({'build' => 'webpack --config webpack.config.js'}) + end + + def configure_for_importmap + run "yarn add rails_admin@#{RailsAdmin::Version.js}" + template 'rails_admin.js', 'app/javascript/rails_admin.js' + require_relative 'importmap_formatter' + add_file 'config/importmap.rails_admin.rb', ImportmapFormatter.new.format + setup_css + end + + def setup_css(additional_script_entries = {}) + gem 'cssbundling-rails' + rake 'css:install:sass' + + @fa_font_path = '.' + template 'rails_admin.scss.erb', 'app/assets/stylesheets/rails_admin.scss' + asset_config = %{Rails.application.config.assets.paths << Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts")\n} + if File.exist? File.join(destination_root, 'config/initializers/assets.rb') + append_to_file 'config/initializers/assets.rb', asset_config + else + add_file 'config/initializers/assets.rb', asset_config + end + add_scripts(additional_script_entries.merge({'build:css' => 'sass ./app/assets/stylesheets/rails_admin.scss:./app/assets/builds/rails_admin.css --no-source-map --load-path=node_modules'})) + end + + def add_scripts(entries) + display 'Add scripts to package.json' + package = begin + JSON.parse(File.read(File.join(destination_root, 'package.json'))) + rescue Errno::ENOENT, JSON::ParserError + {} + end + if package['scripts'] && (package['scripts'].keys & entries.keys).any? + say <<-MESSAGE.gsub(/^ {10}/, ''), :red + You need to merge "scripts": #{JSON.pretty_generate(entries)} into the existing scripts in your package.json . + Taking 'build:css' as an example, if you're already have application.sass.css for the sass build, the resulting script would look like: + sass ./app/assets/stylesheets/application.sass.scss:./app/assets/builds/application.css ./app/assets/stylesheets/rails_admin.scss:./app/assets/builds/rails_admin.css --no-source-map --load-path=node_modules + MESSAGE + else + package['scripts'] ||= {} + entries.each do |entry, build_script| + package['scripts'][entry] = build_script + end + add_file 'package.json', JSON.pretty_generate(package) + end end end end diff --git a/lib/generators/rails_admin/templates/rails_admin.js b/lib/generators/rails_admin/templates/rails_admin.js new file mode 100644 index 0000000000..d0c3beda57 --- /dev/null +++ b/lib/generators/rails_admin/templates/rails_admin.js @@ -0,0 +1 @@ +import "rails_admin/src/rails_admin/base"; diff --git a/lib/generators/rails_admin/templates/rails_admin.js.erb b/lib/generators/rails_admin/templates/rails_admin.js.erb deleted file mode 100644 index a6ebaffc11..0000000000 --- a/lib/generators/rails_admin/templates/rails_admin.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -import "rails_admin/src/rails_admin/base"; -import "<%= @scss_relative_dir %>rails_admin.scss"; diff --git a/lib/generators/rails_admin/templates/rails_admin.scss b/lib/generators/rails_admin/templates/rails_admin.scss deleted file mode 100644 index 2a7b2681c9..0000000000 --- a/lib/generators/rails_admin/templates/rails_admin.scss +++ /dev/null @@ -1 +0,0 @@ -@import "~rails_admin/src/rails_admin/styles/base"; diff --git a/lib/generators/rails_admin/templates/rails_admin.scss.erb b/lib/generators/rails_admin/templates/rails_admin.scss.erb new file mode 100644 index 0000000000..6411f72c57 --- /dev/null +++ b/lib/generators/rails_admin/templates/rails_admin.scss.erb @@ -0,0 +1 @@ +<%= instance_variable_defined?(:@fa_font_path) ? %{$fa-font-path: "#{@fa_font_path}";\n} : '' %>@import "rails_admin/src/rails_admin/styles/base"; diff --git a/lib/generators/rails_admin/templates/rails_admin.webpacker.js b/lib/generators/rails_admin/templates/rails_admin.webpacker.js new file mode 100644 index 0000000000..a5a3f4cd93 --- /dev/null +++ b/lib/generators/rails_admin/templates/rails_admin.webpacker.js @@ -0,0 +1,2 @@ +import "rails_admin/src/rails_admin/base"; +import "../stylesheets/rails_admin.scss"; \ No newline at end of file diff --git a/lib/generators/rails_admin/templates/webpack.config.js b/lib/generators/rails_admin/templates/webpack.config.js deleted file mode 100644 index 469802274c..0000000000 --- a/lib/generators/rails_admin/templates/webpack.config.js +++ /dev/null @@ -1,29 +0,0 @@ -const path = require("path") -const webpack = require('webpack') -const MiniCssExtractPlugin = require("mini-css-extract-plugin") - -module.exports = { - mode: "production", - entry: { - application: "./app/javascript/application.js", - rails_admin: "./app/javascript/rails_admin.js", - }, - output: { - filename: "[name].js", - path: path.resolve(__dirname, "app/assets/builds"), - }, - module: { - rules: [ - { - test: /.s?css$/, - use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"], - }, - ], - }, - plugins: [ - new webpack.optimize.LimitChunkCountPlugin({ - maxChunks: 1 - }), - new MiniCssExtractPlugin(), - ] -} diff --git a/lib/rails_admin/engine.rb b/lib/rails_admin/engine.rb index 788071eff6..54cdb03cbf 100644 --- a/lib/rails_admin/engine.rb +++ b/lib/rails_admin/engine.rb @@ -11,19 +11,9 @@ module RailsAdmin class Engine < Rails::Engine isolate_namespace RailsAdmin - config.action_dispatch.rescue_responses['RailsAdmin::ActionNotAllowed'] = :forbidden + attr_accessor :importmap - initializer 'RailsAdmin precompile hook', group: :all do |app| - if defined?(Sprockets) - app.config.assets.precompile += %w[ - rails_admin/application.js - rails_admin/application.css - ] - app.config.assets.paths << RailsAdmin::Engine.root.join('src') - require 'rails_admin/support/esmodule_preprocessor' - Sprockets.register_preprocessor 'application/javascript', RailsAdmin::ESModulePreprocessor - end - end + config.action_dispatch.rescue_responses['RailsAdmin::ActionNotAllowed'] = :forbidden initializer 'RailsAdmin reload config in development' do |app| config.initializer_path = app.root.join('config/initializers/rails_admin.rb') @@ -45,6 +35,30 @@ class Engine < Rails::Engine end end + initializer 'RailsAdmin apply configuration', after: :eager_load! do |app| + RailsAdmin::Config.initialize! + + # Force route reload, since it doesn't reflect RailsAdmin action configuration yet + app.reload_routes! + end + + initializer 'RailsAdmin precompile hook', group: :all, after: 'RailsAdmin apply configuration' do |app| + case RailsAdmin.config.asset_source + when :sprockets + if defined?(Sprockets) + app.config.assets.precompile += %w[ + rails_admin/application.js + rails_admin/application.css + ] + app.config.assets.paths << RailsAdmin::Engine.root.join('src') + require 'rails_admin/support/esmodule_preprocessor' + Sprockets.register_preprocessor 'application/javascript', RailsAdmin::ESModulePreprocessor + end + when :importmap + self.importmap = Importmap::Map.new.draw(app.root.join('config/importmap.rails_admin.rb')) + end + end + # Check for required middlewares, users may forget to use them in Rails API mode config.after_initialize do |app| has_session_store = app.config.middleware.to_a.any? do |m| @@ -68,11 +82,6 @@ class Engine < Rails::Engine ERROR end - RailsAdmin::Config.initialize! - - # Force route reload, since it doesn't reflect RailsAdmin action configuration yet - app.reload_routes! - RailsAdmin::Version.warn_with_js_version end end diff --git a/lib/rails_admin/support/esmodule_preprocessor.rb b/lib/rails_admin/support/esmodule_preprocessor.rb index 04a8658640..7614409671 100644 --- a/lib/rails_admin/support/esmodule_preprocessor.rb +++ b/lib/rails_admin/support/esmodule_preprocessor.rb @@ -13,6 +13,8 @@ def self.call(input) def initialize; end def call(input) + return unless RailsAdmin.config.asset_source == :sprockets + data = input[:data] if input[:filename].start_with? RailsAdmin::Engine.root.join('src').to_s diff --git a/spec/dummy_app/.gitignore b/spec/dummy_app/.gitignore index e0c3904638..5aaa2881a9 100644 --- a/spec/dummy_app/.gitignore +++ b/spec/dummy_app/.gitignore @@ -7,6 +7,9 @@ # Ignore bundler config /.bundle +/app/assets/builds/* +!/app/assets/builds/.keep + # Ignore the default SQLite database. /db/*.sqlite3 diff --git a/spec/dummy_app/Gemfile b/spec/dummy_app/Gemfile index 16dd596ce9..8080609f17 100644 --- a/spec/dummy_app/Gemfile +++ b/spec/dummy_app/Gemfile @@ -2,8 +2,7 @@ source 'https://rubygems.org' -gem 'rails', '>= 6.0.0' -gem 'webpacker', require: false +gem 'rails', '>= 7.0.0' group :active_record do platforms :jruby do @@ -22,22 +21,17 @@ group :active_record do gem 'paper_trail', '>= 12.0' end -group :mongoid do - gem 'carrierwave-mongoid', '>= 0.6.3', require: 'carrierwave/mongoid' - gem 'kaminari-mongoid' - gem 'mongoid', ['>= 6.0', '< 8'] - gem 'mongoid-paperclip', '>= 0.0.8', require: 'mongoid_paperclip' - gem 'shrine-mongoid', '~> 1.0' -end - gem 'carrierwave', '>= 2.0.0.rc', '< 3.0' -gem 'devise', '>= 3.2', github: 'heartcombo/devise', branch: 'main' +gem 'cssbundling-rails' +gem 'devise', '>= 3.2' gem 'dragonfly', '~> 1.0' +gem 'importmap-rails', require: false gem 'mini_magick', '>= 3.4' gem 'mlb', '>= 0.7', github: 'mshibuya/mlb', branch: 'ruby-3' gem 'paperclip', '>= 3.4' gem 'rails_admin', path: '../../' gem 'shrine', '~> 3.0' +gem 'webpacker', require: false gem 'webrick', '~> 1.7' # Gems used only for assets and not required diff --git a/spec/dummy_app/Gemfile.rails6 b/spec/dummy_app/Gemfile.rails6 new file mode 100644 index 0000000000..8d6da179c5 --- /dev/null +++ b/spec/dummy_app/Gemfile.rails6 @@ -0,0 +1,49 @@ +source 'https://rubygems.org' + +gem 'rails', '>= 6.0.0' +gem 'webpacker', require: false + +group :active_record do + platforms :jruby do + gem 'activerecord-jdbcmysql-adapter', '>= 1.2' + gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2' + gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql2', '>= 0.3.14' + gem 'pg', '>= 0.14' + gem 'sqlite3', '>= 1.3.0' + end + + gem 'paper_trail', '>= 12.0' +end + +group :mongoid do + gem 'carrierwave-mongoid', '>= 0.6.3', require: 'carrierwave/mongoid' + gem 'kaminari-mongoid' + gem 'mongoid', ['>= 6.0', '< 8'] + gem 'mongoid-paperclip', '>= 0.0.8', require: 'mongoid_paperclip' + gem 'shrine-mongoid', '~> 1.0' +end + +gem 'carrierwave', '>= 2.0.0.rc', '< 3.0' +gem 'devise', '>= 3.2' +gem 'dragonfly', '~> 1.0' +gem 'mini_magick', '>= 3.4' +gem 'mlb', '>= 0.7', github: 'mshibuya/mlb', branch: 'ruby-3' +gem 'paperclip', '>= 3.4' +gem 'rails_admin', path: '../../' +gem 'shrine', '~> 3.0' +gem 'webrick', '~> 1.7' + +# Gems used only for assets and not required +# in production environments by default. +group :assets do + gem 'sassc-rails', '~> 2.1' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + # gem 'therubyracer' + + gem 'uglifier', '>= 1.3' +end diff --git a/spec/dummy_app/Procfile.dev b/spec/dummy_app/Procfile.dev new file mode 100644 index 0000000000..0c4b7357b9 --- /dev/null +++ b/spec/dummy_app/Procfile.dev @@ -0,0 +1,2 @@ +web: bin/rails server -p 3000 +css: yarn build:css --watch diff --git a/spec/dummy_app/app/assets/builds/.keep b/spec/dummy_app/app/assets/builds/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/dummy_app/app/assets/config/manifest.js b/spec/dummy_app/app/assets/config/manifest.js index b16e53d6d5..04081559e3 100644 --- a/spec/dummy_app/app/assets/config/manifest.js +++ b/spec/dummy_app/app/assets/config/manifest.js @@ -1,3 +1,4 @@ //= link_tree ../images -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css +//= link_tree ../../../../../src .js +//= link application.js +//= link application.css diff --git a/spec/dummy_app/app/assets/stylesheets/application.css b/spec/dummy_app/app/assets/stylesheets/application.css index 3b5cc6648e..960f2c08fe 100644 --- a/spec/dummy_app/app/assets/stylesheets/application.css +++ b/spec/dummy_app/app/assets/stylesheets/application.css @@ -9,5 +9,4 @@ * compiled file, but it's generally better to create a new file per style scope. * *= require_self - *= require_tree . */ diff --git a/spec/dummy_app/app/assets/stylesheets/rails_admin.scss b/spec/dummy_app/app/assets/stylesheets/rails_admin.scss new file mode 100644 index 0000000000..d637854f64 --- /dev/null +++ b/spec/dummy_app/app/assets/stylesheets/rails_admin.scss @@ -0,0 +1,2 @@ +$fa-font-path: "."; +@import "rails_admin/src/rails_admin/styles/base.scss"; diff --git a/spec/dummy_app/app/javascript/application.js b/spec/dummy_app/app/javascript/application.js new file mode 100644 index 0000000000..6100af1565 --- /dev/null +++ b/spec/dummy_app/app/javascript/application.js @@ -0,0 +1,4 @@ +import Rails from "@rails/ujs"; +import "@hotwired/turbo-rails"; + +Rails.start(); diff --git a/spec/dummy_app/app/javascript/packs/application.js b/spec/dummy_app/app/javascript/packs/application.js deleted file mode 100644 index 90169eca9e..0000000000 --- a/spec/dummy_app/app/javascript/packs/application.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint no-console:0 */ -// This file is automatically compiled by Webpack, along with any other files -// present in this directory. You're encouraged to place your actual application logic in -// a relevant structure within app/javascript and only use these pack files to reference -// that code so it'll be compiled. -// -// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate -// layout file, like app/views/layouts/application.html.erb - -// Uncomment to copy all static images under ../images to the output folder and reference -// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) -// or the `imagePath` JavaScript helper below. -// -// const images = require.context('../images', true) -// const imagePath = (name) => images(name, true) - -console.log("Hello World from Webpacker"); diff --git a/spec/dummy_app/app/javascript/packs/rails_admin.js b/spec/dummy_app/app/javascript/packs/rails_admin.js deleted file mode 100644 index d5c524ad57..0000000000 --- a/spec/dummy_app/app/javascript/packs/rails_admin.js +++ /dev/null @@ -1,3 +0,0 @@ -import "rails_admin/src/rails_admin/base"; -import "../stylesheets/rails_admin.scss"; -import "flatpickr/dist/l10n/fr"; diff --git a/spec/dummy_app/app/javascript/rails_admin.js b/spec/dummy_app/app/javascript/rails_admin.js new file mode 100644 index 0000000000..1e0fc775c1 --- /dev/null +++ b/spec/dummy_app/app/javascript/rails_admin.js @@ -0,0 +1,2 @@ +import "rails_admin/src/rails_admin/base"; +import "flatpickr/dist/l10n/fr.js"; diff --git a/spec/dummy_app/app/javascript/rails_admin.scss b/spec/dummy_app/app/javascript/rails_admin.scss new file mode 100644 index 0000000000..3a122e60eb --- /dev/null +++ b/spec/dummy_app/app/javascript/rails_admin.scss @@ -0,0 +1 @@ +@import "rails_admin/src/rails_admin/styles/base.scss"; diff --git a/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss b/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss deleted file mode 100644 index 6ec27d11b3..0000000000 --- a/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss +++ /dev/null @@ -1 +0,0 @@ -@import "~rails_admin/src/rails_admin/styles/base.scss"; diff --git a/spec/dummy_app/app/views/layouts/application.html.erb b/spec/dummy_app/app/views/layouts/application.html.erb index 6f5cc0446b..3a85d9970e 100644 --- a/spec/dummy_app/app/views/layouts/application.html.erb +++ b/spec/dummy_app/app/views/layouts/application.html.erb @@ -2,8 +2,15 @@ DummyApp - <%= stylesheet_link_tag "application", media: "all" %> - <%= javascript_include_tag "application" %> + <% case CI_ASSET %> + <% when :webpacker %> + <%= javascript_pack_tag "application" %> + <% when :importmap %> + <%= javascript_importmap_tags %> + <% else %> + <%= stylesheet_link_tag "application", media: "all" %> + <%= javascript_include_tag "application" %> + <% end %> <%= csrf_meta_tags %> diff --git a/spec/dummy_app/bin/dev b/spec/dummy_app/bin/dev new file mode 100755 index 0000000000..a1104a50b1 --- /dev/null +++ b/spec/dummy_app/bin/dev @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +if ! command -v foreman &> /dev/null +then + echo "Installing foreman..." + gem install foreman +fi + +foreman start -f Procfile.dev "$@" diff --git a/spec/dummy_app/bin/importmap b/spec/dummy_app/bin/importmap new file mode 100755 index 0000000000..36502ab16c --- /dev/null +++ b/spec/dummy_app/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/spec/dummy_app/config/application.rb b/spec/dummy_app/config/application.rb index 6452e2a0b3..34129fd94e 100644 --- a/spec/dummy_app/config/application.rb +++ b/spec/dummy_app/config/application.rb @@ -18,8 +18,11 @@ case CI_ASSET when :webpacker require 'webpacker' -else - require "#{CI_ASSET}/railtie" +when :sprockets, :webpack + require 'sprockets/railtie' +when :importmap + require 'sprockets/railtie' + require 'importmap-rails' end # Require the gems listed in Gemfile, including any gems @@ -39,5 +42,23 @@ class Application < Rails::Application config.active_record.time_zone_aware_types = %i[datetime time] if CI_ORM == :active_record config.active_storage.service = :local if defined?(ActiveStorage) config.active_storage.replace_on_assign_to_many = false if defined?(ActiveStorage) && ActiveStorage.version < Gem::Version.create('6.1') + + case CI_ASSET + when :webpack + config.assets.precompile += %w[rails_admin.js rails_admin.css] + when :importmap + config.assets.paths << RailsAdmin::Engine.root.join('src') + config.assets.precompile += %w[rails_admin.js rails_admin.css] + config.importmap.cache_sweepers << RailsAdmin::Engine.root.join('src') + end + + initializer :ignore_unused_assets_path, after: :append_assets_path, group: :all do |app| + case CI_ASSET + when :webpack, :importmap + app.config.assets.paths.delete(Rails.root.join('app', 'assets', 'javascripts').to_s) + when :sprockets + app.config.assets.paths.delete(Rails.root.join('app', 'assets', 'builds').to_s) + end + end end end diff --git a/spec/dummy_app/config/importmap.rails_admin.rb b/spec/dummy_app/config/importmap.rails_admin.rb new file mode 100644 index 0000000000..668303f2d0 --- /dev/null +++ b/spec/dummy_app/config/importmap.rails_admin.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Pin npm packages by running ./bin/importmap + +pin 'rails_admin', preload: true +pin 'rails_admin/src/rails_admin/base', to: 'rails_admin/base.js' +pin '@hotwired/turbo', to: 'https://ga.jspm.io/npm:@hotwired/turbo@7.1.0/dist/turbo.es2017-esm.js' +pin '@hotwired/turbo-rails', to: 'https://ga.jspm.io/npm:@hotwired/turbo-rails@7.1.3/app/javascript/turbo/index.js' +pin '@popperjs/core', to: 'https://ga.jspm.io/npm:@popperjs/core@2.11.5/dist/esm/popper.js' +pin '@rails/actioncable/src', to: 'https://ga.jspm.io/npm:@rails/actioncable@7.0.2/src/index.js' +pin '@rails/ujs', to: 'https://ga.jspm.io/npm:@rails/ujs@6.1.4/lib/assets/compiled/rails-ujs.js' +pin 'bootstrap', to: 'https://ga.jspm.io/npm:bootstrap@5.1.3/dist/js/bootstrap.esm.js' +pin 'flatpickr', to: 'https://ga.jspm.io/npm:flatpickr@4.6.13/dist/flatpickr.js' +pin 'flatpickr/', to: 'https://ga.jspm.io/npm:flatpickr@4.6.13/' +pin 'jquery', to: 'https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js' +pin 'jquery-ui/', to: 'https://ga.jspm.io/npm:jquery-ui@1.13.1/' diff --git a/spec/dummy_app/config/importmap.rb b/spec/dummy_app/config/importmap.rb new file mode 100644 index 0000000000..a7309e8e9e --- /dev/null +++ b/spec/dummy_app/config/importmap.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# Pin npm packages by running ./bin/importmap + +pin 'application', preload: true +pin '@hotwired/turbo-rails', to: 'https://ga.jspm.io/npm:@hotwired/turbo-rails@7.1.3/app/javascript/turbo/index.js' +pin '@rails/ujs', to: 'https://ga.jspm.io/npm:@rails/ujs@6.0.5/lib/assets/compiled/rails-ujs.js' +pin '@hotwired/turbo', to: 'https://ga.jspm.io/npm:@hotwired/turbo@7.1.0/dist/turbo.es2017-esm.js' +pin '@rails/actioncable/src', to: 'https://ga.jspm.io/npm:@rails/actioncable@7.0.3/src/index.js' diff --git a/spec/dummy_app/config/initializers/assets.rb b/spec/dummy_app/config/initializers/assets.rb index d0e855fff0..3dd6c18505 100644 --- a/spec/dummy_app/config/initializers/assets.rb +++ b/spec/dummy_app/config/initializers/assets.rb @@ -7,6 +7,7 @@ # Add additional assets to the asset load path # Rails.application.config.assets.paths << Emoji.images_path +Rails.application.config.assets.paths << Rails.root.join('node_modules/@fortawesome/fontawesome-free/webfonts') if Rails.application.config.respond_to?(:assets) # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. diff --git a/spec/dummy_app/config/webpacker.yml b/spec/dummy_app/config/webpacker.yml index 5d73d59d26..1d6e322ed4 100644 --- a/spec/dummy_app/config/webpacker.yml +++ b/spec/dummy_app/config/webpacker.yml @@ -2,7 +2,7 @@ default: &default source_path: app/javascript - source_entry_path: packs + source_entry_path: . public_root_path: public public_output_path: packs cache_path: tmp/cache/webpacker @@ -16,7 +16,7 @@ default: &default cache_manifest: false # Extract and emit a css file - extract_css: false + extract_css: true static_assets_extensions: - .jpg diff --git a/spec/dummy_app/package.json b/spec/dummy_app/package.json index d849b38f48..1e99dff184 100644 --- a/spec/dummy_app/package.json +++ b/spec/dummy_app/package.json @@ -4,11 +4,15 @@ "version": "0.1.0", "dependencies": { "@rails/webpacker": "5.4.3", + "rails_admin": "file:../../", "webpack": "^4.46.0", - "webpack-cli": "^3.3.12", - "rails_admin": "file:../../" + "webpack-cli": "^3.3.12" }, "devDependencies": { "webpack-dev-server": "^3" + }, + "scripts": { + "build": "webpack --config webpack.config.js", + "build:css": "sass ./app/assets/stylesheets/rails_admin.scss:./app/assets/builds/rails_admin.css --no-source-map --load-path=node_modules" } } diff --git a/spec/dummy_app/vendor/javascript/.keep b/spec/dummy_app/vendor/javascript/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/dummy_app/webpack.config.js b/spec/dummy_app/webpack.config.js new file mode 100644 index 0000000000..a72aced932 --- /dev/null +++ b/spec/dummy_app/webpack.config.js @@ -0,0 +1,21 @@ +const path = require("path"); +const webpack = require("webpack"); + +module.exports = { + mode: "production", + devtool: "source-map", + entry: { + application: "./app/javascript/application.js", + rails_admin: "./app/javascript/rails_admin.js", + }, + output: { + filename: "[name].js", + sourceMapFilename: "[name].js.map", + path: path.resolve(__dirname, "app/assets/builds"), + }, + plugins: [ + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + ], +}; diff --git a/spec/rails_admin/install_generator_spec.rb b/spec/rails_admin/install_generator_spec.rb index 39820afccb..3d5333f3a0 100644 --- a/spec/rails_admin/install_generator_spec.rb +++ b/spec/rails_admin/install_generator_spec.rb @@ -5,7 +5,7 @@ RSpec.describe RailsAdmin::InstallGenerator, type: :generator do destination File.expand_path('../dummy_app/tmp/generator', __dir__) - arguments ['admin'] + arguments ['admin', "--asset=#{CI_ASSET}", '--force'] before do prepare_destination @@ -17,6 +17,10 @@ # empty end RUBY + File.write(File.join(destination_root, 'Rakefile'), <<-RUBY.gsub(/^ {4}/, '')) + desc 'Stub for testing' + task 'css:install:sass' + RUBY end after do @@ -46,12 +50,43 @@ contains 'import "rails_admin/src/rails_admin/base"' end file 'app/javascript/stylesheets/rails_admin.scss' do - contains '@import "~rails_admin/src/rails_admin/styles/base"' + contains '@import "rails_admin/src/rails_admin/styles/base"' end when :sprockets file 'Gemfile' do contains 'sassc-rails' end + when :importmap + file 'app/javascript/rails_admin.js' do + contains 'import "rails_admin/src/rails_admin/base"' + end + file 'app/assets/stylesheets/rails_admin.scss' do + contains '$fa-font-path: ".";' + contains '@import "rails_admin/src/rails_admin/styles/base"' + end + file 'config/importmap.rails_admin.rb' do + contains 'pin "rails_admin", preload: true' + contains 'pin "rails_admin/src/rails_admin/base", to: "https://ga.jspm.io/npm:rails_admin@' + contains 'pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@' + end + file 'config/initializers/assets.rb' do + contains 'Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts")' + end + file 'package.json' do + contains 'sass ./app/assets/stylesheets/rails_admin.scss:./app/assets/builds/rails_admin.css' + end + when :webpack + file 'app/javascript/rails_admin.js' do + contains 'import "rails_admin/src/rails_admin/base"' + end + file 'app/assets/stylesheets/rails_admin.scss' do + contains '$fa-font-path: ".";' + contains '@import "rails_admin/src/rails_admin/styles/base"' + end + file 'package.json' do + contains 'webpack --config webpack.config.js' + contains 'sass ./app/assets/stylesheets/rails_admin.scss:./app/assets/builds/rails_admin.css' + end end end, ) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 581c834848..bb0da02c86 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -79,7 +79,7 @@ end config.before(:all) do - Webpacker.instance.compiler.compile if defined?(Webpacker) + Webpacker.instance.compiler.compile if CI_ASSET == :webpacker end config.before do |example| diff --git a/src/rails_admin/base.js b/src/rails_admin/base.js index 478c48fa38..49020f81cc 100644 --- a/src/rails_admin/base.js +++ b/src/rails_admin/base.js @@ -3,7 +3,19 @@ import "@hotwired/turbo-rails"; import jQuery from "jquery"; import "./vendor/jquery_nested_form"; import "bootstrap"; -import "flatpickr/dist/flatpickr"; + +// These jQuery-UI indirect dependencies need to be preloaded to be used within Import maps +import "jquery-ui/ui/version.js"; +import "jquery-ui/ui/keycode.js"; +import "jquery-ui/ui/position.js"; +import "jquery-ui/ui/safe-active-element.js"; +import "jquery-ui/ui/data.js"; +import "jquery-ui/ui/ie.js"; +import "jquery-ui/ui/scroll-parent.js"; +import "jquery-ui/ui/unique-id.js"; +import "jquery-ui/ui/widget.js"; +import "jquery-ui/ui/widgets/menu.js"; +import "jquery-ui/ui/widgets/mouse.js"; import "./filter-box"; import "./filtering-multiselect"; diff --git a/src/rails_admin/filtering-multiselect.js b/src/rails_admin/filtering-multiselect.js index d25bce2a48..1eba540bc7 100644 --- a/src/rails_admin/filtering-multiselect.js +++ b/src/rails_admin/filtering-multiselect.js @@ -1,5 +1,5 @@ import jQuery from "jquery"; -import "jquery-ui/ui/widget"; +import "jquery-ui/ui/widget.js"; import I18n from "./i18n"; (function ($) { $.widget("ra.filteringMultiselect", { diff --git a/src/rails_admin/filtering-select.js b/src/rails_admin/filtering-select.js index 08ebc0ba93..3566d664bc 100644 --- a/src/rails_admin/filtering-select.js +++ b/src/rails_admin/filtering-select.js @@ -1,6 +1,6 @@ import jQuery from "jquery"; -import "jquery-ui/ui/widget"; -import "jquery-ui/ui/widgets/autocomplete"; +import "jquery-ui/ui/widget.js"; +import "jquery-ui/ui/widgets/autocomplete.js"; import I18n from "./i18n"; (function ($) { diff --git a/src/rails_admin/nested-form-hooks.js b/src/rails_admin/nested-form-hooks.js index 145dd93566..e6e6ea7381 100644 --- a/src/rails_admin/nested-form-hooks.js +++ b/src/rails_admin/nested-form-hooks.js @@ -1,5 +1,5 @@ import jQuery from "jquery"; -import * as bootstrap from "bootstrap/dist/js/bootstrap.esm"; +import * as bootstrap from "bootstrap"; (function ($) { $(document).ready(function () { diff --git a/src/rails_admin/remote-form.js b/src/rails_admin/remote-form.js index 7bc9d53783..4d5d2efeb2 100644 --- a/src/rails_admin/remote-form.js +++ b/src/rails_admin/remote-form.js @@ -1,7 +1,7 @@ import Rails from "@rails/ujs"; import jQuery from "jquery"; -import "jquery-ui/ui/widget"; -import * as bootstrap from "bootstrap/dist/js/bootstrap.esm"; +import "jquery-ui/ui/widget.js"; +import * as bootstrap from "bootstrap"; (function ($) { $.widget("ra.remoteForm", { diff --git a/src/rails_admin/styles/base.scss b/src/rails_admin/styles/base.scss index bfb7539153..1486f5c1ac 100644 --- a/src/rails_admin/styles/base.scss +++ b/src/rails_admin/styles/base.scss @@ -7,17 +7,17 @@ /*** Libraries ***/ -@import "~flatpickr/dist/flatpickr"; +@import "flatpickr/dist/flatpickr"; /*** Font-awesome ***/ -$fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; -@import "~@fortawesome/fontawesome-free/scss/solid"; -@import "~@fortawesome/fontawesome-free/scss/fontawesome"; +$fa-font-path: "~@fortawesome/fontawesome-free/webfonts" !default; +@import "@fortawesome/fontawesome-free/scss/solid"; +@import "@fortawesome/fontawesome-free/scss/fontawesome"; /*** Bootstrap ***/ -@import "~bootstrap/scss/bootstrap"; +@import "bootstrap/scss/bootstrap"; /*** RailsAdmin ***/ diff --git a/src/rails_admin/ui.js b/src/rails_admin/ui.js index e378884e5d..df55705f8c 100644 --- a/src/rails_admin/ui.js +++ b/src/rails_admin/ui.js @@ -1,5 +1,5 @@ import jQuery from "jquery"; -import "jquery-ui/ui/effect"; +import "jquery-ui/ui/effect.js"; import I18n from "./i18n"; (function ($) { diff --git a/src/rails_admin/widgets.js b/src/rails_admin/widgets.js index ed257b925e..f6669498d7 100644 --- a/src/rails_admin/widgets.js +++ b/src/rails_admin/widgets.js @@ -1,6 +1,6 @@ import jQuery from "jquery"; -import "jquery-ui/ui/widgets/sortable"; -import * as bootstrap from "bootstrap/dist/js/bootstrap.esm"; +import "jquery-ui/ui/widgets/sortable.js"; +import * as bootstrap from "bootstrap"; import flatpickr from "flatpickr"; import I18n from "./i18n";