diff --git a/lib/install/angular.rb b/lib/install/angular.rb new file mode 100644 index 000000000..ddf8e00be --- /dev/null +++ b/lib/install/angular.rb @@ -0,0 +1,18 @@ +require "webpacker/configuration" + +puts "Copying angular loader to #{Webpacker::Configuration.config_path}/loaders" +copy_file "#{__dir__}/config/loaders/installers/angular.js", "config/webpack/loaders/angular.js" + +puts "Copying angular example entry file to #{Webpacker::Configuration.entry_path}" +copy_file "#{__dir__}/examples/angular/hello_angular.js", "#{Webpacker::Configuration.entry_path}/hello_angular.js" + +puts "Copying hello_angular app to #{Webpacker::Configuration.source_path}" +directory "#{__dir__}/examples/angular/hello_angular", "#{Webpacker::Configuration.source_path}/hello_angular" + +puts "Copying tsconfig.json to the Rails root directory for typescript" +copy_file "#{__dir__}/examples/angular/tsconfig.json", "tsconfig.json" + +puts "Installing all angular dependencies" +run "./bin/yarn add typescript ts-loader core-js zone.js rxjs @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic" + +puts "Webpacker now supports angular.js and typescript 🎉" diff --git a/lib/install/config/.postcssrc.yml b/lib/install/config/.postcssrc.yml new file mode 100644 index 000000000..bc4f02ab3 --- /dev/null +++ b/lib/install/config/.postcssrc.yml @@ -0,0 +1,4 @@ +plugins: + postcss-smart-import: {} + precss: {} + autoprefixer: {} diff --git a/lib/install/config/loaders/core/assets.js b/lib/install/config/loaders/core/assets.js new file mode 100644 index 000000000..c859daf0b --- /dev/null +++ b/lib/install/config/loaders/core/assets.js @@ -0,0 +1,12 @@ +const { env, publicPath } = require('../configuration.js') + +module.exports = { + test: /\.(jpeg|png|gif|svg|eot|ttf|woff|woff2)$/i, + use: [{ + loader: 'file-loader', + options: { + publicPath, + name: env.NODE_ENV === 'production' ? '[name]-[hash].[ext]' : '[name].[ext]' + } + }] +} diff --git a/lib/install/config/loaders/core/babel.js b/lib/install/config/loaders/core/babel.js new file mode 100644 index 000000000..82481e15e --- /dev/null +++ b/lib/install/config/loaders/core/babel.js @@ -0,0 +1,10 @@ +module.exports = { + test: /\.js(\.erb)?$/, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + presets: [ + ['env', { modules: false }] + ] + } +} diff --git a/lib/install/config/loaders/core/coffee.js b/lib/install/config/loaders/core/coffee.js new file mode 100644 index 000000000..dae874249 --- /dev/null +++ b/lib/install/config/loaders/core/coffee.js @@ -0,0 +1,4 @@ +module.exports = { + test: /\.coffee(\.erb)?$/, + loader: 'coffee-loader' +} diff --git a/lib/install/config/loaders/core/erb.js b/lib/install/config/loaders/core/erb.js new file mode 100644 index 000000000..c1a6cc158 --- /dev/null +++ b/lib/install/config/loaders/core/erb.js @@ -0,0 +1,9 @@ +module.exports = { + test: /\.erb$/, + enforce: 'pre', + exclude: /node_modules/, + loader: 'rails-erb-loader', + options: { + runner: 'DISABLE_SPRING=1 bin/rails runner' + } +} diff --git a/lib/install/config/loaders/core/sass.js b/lib/install/config/loaders/core/sass.js new file mode 100644 index 000000000..faba9d5b0 --- /dev/null +++ b/lib/install/config/loaders/core/sass.js @@ -0,0 +1,9 @@ +const ExtractTextPlugin = require('extract-text-webpack-plugin') + +module.exports = { + test: /\.(scss|sass|css)$/i, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'postcss-loader', 'sass-loader'] + }) +} diff --git a/lib/install/config/loaders/installers/angular.js b/lib/install/config/loaders/installers/angular.js new file mode 100644 index 000000000..cbb916e33 --- /dev/null +++ b/lib/install/config/loaders/installers/angular.js @@ -0,0 +1,4 @@ +module.exports = { + test: /.ts$/, + loader: 'ts-loader' +} diff --git a/lib/install/config/loaders/installers/react.js b/lib/install/config/loaders/installers/react.js new file mode 100644 index 000000000..065dc4e07 --- /dev/null +++ b/lib/install/config/loaders/installers/react.js @@ -0,0 +1,11 @@ +module.exports = { + test: /\.(js|jsx)?(\.erb)?$/, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + presets: [ + 'react', + ['env', { modules: false }] + ] + } +} diff --git a/lib/install/config/loaders/installers/vue.js b/lib/install/config/loaders/installers/vue.js new file mode 100644 index 000000000..7dbab0409 --- /dev/null +++ b/lib/install/config/loaders/installers/vue.js @@ -0,0 +1,10 @@ +module.exports = { + test: /.vue$/, + loader: 'vue-loader', + options: { + loaders: { + scss: 'vue-style-loader!css-loader!sass-loader', + sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' + } + } +} diff --git a/lib/install/config/webpack/configuration.js b/lib/install/config/webpack/configuration.js index 607c3e52c..3617eab1c 100644 --- a/lib/install/config/webpack/configuration.js +++ b/lib/install/config/webpack/configuration.js @@ -6,6 +6,7 @@ const { safeLoad } = require('js-yaml') const { readFileSync } = require('fs') const configPath = resolve('config', 'webpack') +const loadersDir = join(__dirname, 'loaders') const paths = safeLoad(readFileSync(join(configPath, 'paths.yml'), 'utf8')) const devServer = safeLoad(readFileSync(join(configPath, 'development.server.yml'), 'utf8')) const publicPath = env.NODE_ENV !== 'production' && devServer.enabled ? @@ -15,5 +16,6 @@ module.exports = { devServer, env, paths, + loadersDir, publicPath } diff --git a/lib/install/config/webpack/development.js b/lib/install/config/webpack/development.js index aec3ebec6..d98ec5b13 100644 --- a/lib/install/config/webpack/development.js +++ b/lib/install/config/webpack/development.js @@ -1,4 +1,3 @@ -/* eslint global-require: 0 */ // Note: You must restart bin/webpack-watcher for changes to take effect const merge = require('webpack-merge') diff --git a/lib/install/config/webpack/paths.yml b/lib/install/config/webpack/paths.yml index 586f77728..818d71fc8 100644 --- a/lib/install/config/webpack/paths.yml +++ b/lib/install/config/webpack/paths.yml @@ -4,3 +4,15 @@ entry: packs output: public node_modules: node_modules source: app/javascript +extensions: + - .coffee + - .js + - .jsx + - .ts + - .vue + - .sass + - .css + - .png + - .svg + - .gif + - .jpeg diff --git a/lib/install/config/webpack/shared.js b/lib/install/config/webpack/shared.js index 1ae61656e..39492aa44 100644 --- a/lib/install/config/webpack/shared.js +++ b/lib/install/config/webpack/shared.js @@ -1,15 +1,17 @@ // Note: You must restart bin/webpack-watcher for changes to take effect +/* eslint global-require: 0 */ +/* eslint import/no-dynamic-require: 0 */ const webpack = require('webpack') const { basename, join, resolve } = require('path') const { sync } = require('glob') +const { readdirSync } = require('fs') const ExtractTextPlugin = require('extract-text-webpack-plugin') const ManifestPlugin = require('webpack-manifest-plugin') const extname = require('path-complete-extname') -const { env, paths, publicPath } = require('./configuration.js') +const { env, paths, publicPath, loadersDir } = require('./configuration.js') -const extensions = ['.js', '.coffee'] -const extensionGlob = `*{${extensions.join(',')}}*` +const extensionGlob = `*{${paths.extensions.join(',')}}*` const packPaths = sync(join(paths.source, paths.entry, extensionGlob)) module.exports = { @@ -24,45 +26,9 @@ module.exports = { output: { filename: '[name].js', path: resolve(paths.output, paths.entry) }, module: { - rules: [ - { test: /\.coffee(\.erb)?$/, loader: 'coffee-loader' }, - { - test: /\.js(\.erb)?$/, - exclude: /node_modules/, - loader: 'babel-loader', - options: { - presets: [ - ['env', { modules: false }] - ] - } - }, - { - test: /\.erb$/, - enforce: 'pre', - exclude: /node_modules/, - loader: 'rails-erb-loader', - options: { - runner: 'DISABLE_SPRING=1 bin/rails runner' - } - }, - { - test: /\.(scss|sass|css)$/i, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: ['css-loader', 'sass-loader'] - }) - }, - { - test: /\.(jpeg|png|gif|svg|eot|svg|ttf|woff|woff2)$/i, - use: [{ - loader: 'file-loader', - options: { - publicPath, - name: env.NODE_ENV === 'production' ? '[name]-[hash].[ext]' : '[name].[ext]' - } - }] - } - ] + rules: readdirSync(loadersDir).map(file => ( + require(join(loadersDir, file)) + )) }, plugins: [ @@ -72,7 +38,7 @@ module.exports = { ], resolve: { - extensions, + extensions: paths.extensions, modules: [ resolve(paths.source), resolve(paths.node_modules) diff --git a/lib/install/examples/vue/hello_vue.js b/lib/install/examples/vue/hello_vue.js index 5ae2e3605..6141a86ec 100644 --- a/lib/install/examples/vue/hello_vue.js +++ b/lib/install/examples/vue/hello_vue.js @@ -4,7 +4,7 @@ // like app/views/layouts/application.html.erb. // All it does is render
Hello Vue
at the bottom of the page. -import Vue from 'vue' +import Vue from 'vue/dist/vue.esm' import App from './app.vue' document.addEventListener('DOMContentLoaded', () => { diff --git a/lib/install/react.rb b/lib/install/react.rb new file mode 100644 index 000000000..b5f3b737f --- /dev/null +++ b/lib/install/react.rb @@ -0,0 +1,15 @@ +require "webpacker/configuration" + +puts "Copying react loader to #{Webpacker::Configuration.config_path}/loaders" +copy_file "#{__dir__}/config/loaders/installers/react.js", "config/webpack/loaders/react.js" + +puts "Copying .babelrc to app root directory" +copy_file "#{__dir__}/examples/react/.babelrc", ".babelrc" + +puts "Copying react example entry file to #{Webpacker::Configuration.entry_path}" +copy_file "#{__dir__}/examples/react/hello_react.jsx", "#{Webpacker::Configuration.entry_path}/hello_react.jsx" + +puts "Installing all react dependencies" +run "./bin/yarn add react react-dom babel-preset-react" + +puts "Webpacker now supports react.js 🎉" diff --git a/lib/install/template.rb b/lib/install/template.rb index 36ae1eaea..03217f3ec 100644 --- a/lib/install/template.rb +++ b/lib/install/template.rb @@ -1,19 +1,29 @@ -# Setup webpacker +# Install webpacker +puts "Creating javascript app source directory" directory "#{__dir__}/javascript", "app/javascript" +puts "Copying binstubs" directory "#{__dir__}/bin", "bin" chmod "bin", 0755 & ~File.umask, verbose: false +puts "Copying webpack core config and loaders" directory "#{__dir__}/config/webpack", "config/webpack" +directory "#{__dir__}/config/loaders/core", "config/webpack/loaders" +copy_file "#{__dir__}/config/.postcssrc.yml", ".postcssrc.yml" append_to_file ".gitignore", <<-EOS /public/packs /node_modules EOS +puts "Installing all JavaScript dependencies" run "./bin/yarn add webpack webpack-merge js-yaml path-complete-extname " \ "webpack-manifest-plugin babel-loader coffee-loader coffee-script " \ "babel-core babel-preset-env compression-webpack-plugin rails-erb-loader glob " \ -"extract-text-webpack-plugin node-sass file-loader sass-loader css-loader style-loader" +"extract-text-webpack-plugin node-sass file-loader sass-loader css-loader style-loader " \ +"postcss-loader autoprefixer postcss-smart-import precss" +puts "Installing dev server for live reloading" run "./bin/yarn add --dev webpack-dev-server" + +puts "Webpacker successfully installed 🎉 🍰" diff --git a/lib/install/vue.rb b/lib/install/vue.rb new file mode 100644 index 000000000..512c1b998 --- /dev/null +++ b/lib/install/vue.rb @@ -0,0 +1,15 @@ +require "webpacker/configuration" + +puts "Copying vue loader to #{Webpacker::Configuration.config_path}/loaders" +copy_file "#{__dir__}/config/loaders/installers/vue.js", "config/webpack/loaders/vue.js" + +puts "Copying the example entry file to #{Webpacker::Configuration.entry_path}" +copy_file "#{__dir__}/examples/vue/hello_vue.js", "#{Webpacker::Configuration.entry_path}/hello_vue.js" + +puts "Copying vue app file to #{Webpacker::Configuration.entry_path}" +copy_file "#{__dir__}/examples/vue/app.vue", "#{Webpacker::Configuration.entry_path}/app.vue" + +puts "Installing all vue dependencies" +run "./bin/yarn add vue vue-loader vue-template-compiler sass-loader node-sass css-loader" + +puts "Webpacker now supports vue.js 🎉" diff --git a/lib/tasks/installers/angular.rake b/lib/tasks/installers/angular.rake index dd985705c..9bac3eb6c 100644 --- a/lib/tasks/installers/angular.rake +++ b/lib/tasks/installers/angular.rake @@ -1,41 +1,14 @@ -require "webpacker/configuration" +ANGULAR_TEMPLATE_PATH = File.expand_path("../../install/angular.rb", __dir__) namespace :webpacker do namespace :install do desc "Install everything needed for Angular" task angular: ["webpacker:verify_install"] do - shared_config_path = Webpacker::Configuration.shared_config_path - config = File.read(shared_config_path) - - if config.include?("ts-loader") - puts "The configuration file already has a reference to ts-loader, skipping the test rule..." + if Rails::VERSION::MAJOR >= 5 + exec "./bin/rails app:template LOCATION=#{ANGULAR_TEMPLATE_PATH}" else - puts "Adding a loader rule to include ts-loader for .ts files in #{shared_config_path}..." - config.gsub!(/rules:(\s*\[)(\s*\{)/, "rules:\\1\\2 test: /\.ts$/, loader: 'ts-loader' },\\2") + exec "./bin/rake rails:template LOCATION=#{ANGULAR_TEMPLATE_PATH}" end - - if config =~ /["'].ts["']/ - puts "The configuration file already has a reference to .ts extension, skipping the addition of this extension to the list..." - else - puts "Adding '.ts' in loader extensions in #{shared_config_path}..." - config.gsub!(/extensions = (.*')(\s*\])/, "extensions = \\1, '.ts'\\2") - end - - File.write shared_config_path, config - - puts "Copying Angular example to #{Webpacker::Configuration.entry_path}" - FileUtils.copy File.expand_path("../../install/examples/angular/hello_angular.js", __dir__), - Rails.root.join(Webpacker::Configuration.entry_path, "hello_angular.js") - - puts "Copying Angular Hello app to #{Webpacker::Configuration.source_path}" - FileUtils.copy_entry File.expand_path("../../install/examples/angular/hello_angular", __dir__), - Rails.root.join(Webpacker::Configuration.source_path, "hello_angular") - - puts "Copying tsconfig.json to the Rails root directory" - FileUtils.copy File.expand_path("../../install/examples/angular/tsconfig.json", __dir__), - Rails.root.join("tsconfig.json") - - exec "./bin/yarn add typescript ts-loader core-js zone.js rxjs @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic" end end end diff --git a/lib/tasks/installers/react.rake b/lib/tasks/installers/react.rake index a37d9b719..9b934403d 100644 --- a/lib/tasks/installers/react.rake +++ b/lib/tasks/installers/react.rake @@ -1,44 +1,14 @@ -require "webpacker/configuration" +REACT_TEMPLATE_PATH = File.expand_path("../../install/react.rb", __dir__) namespace :webpacker do namespace :install do desc "Install everything needed for react" task react: ["webpacker:verify_install"] do - shared_config_path = Webpacker::Configuration.shared_config_path - config = File.read(shared_config_path) - - if config =~ /presets:\s*\[\s*\[\s*'env'/ - puts "Replacing loader presets to include react in #{shared_config_path}" - config.gsub!(/presets:(\s*\[)(\s*)\[(\s)*'env'/, "presets:\\1\\2'react',\\2[\\3'env'") - else - puts "Couldn't automatically update loader presets in #{shared_config_path}. Please set presets: [ 'react', [ 'env', { 'modules': false } ] ]." - end - - if config.include?("test: /\\.js(\\.erb)?$/") - puts "Replacing loader test to include react in #{shared_config_path}" - config.gsub!("test: /\\.js(\\.erb)?$/", "test: /\\.(js|jsx)?(\\.erb)?$/") + if Rails::VERSION::MAJOR >= 5 + exec "./bin/rails app:template LOCATION=#{REACT_TEMPLATE_PATH}" else - puts "Couldn't automatically update loader test in #{shared_config_path}. Please set test: /\\.jsx?(\\.erb)?$/." + exec "./bin/rake rails:template LOCATION=#{REACT_TEMPLATE_PATH}" end - - if config =~ /["'].jsx["']/ - puts "The configuration file already has a reference to .jsx extension, skipping the addition of this extension to the list..." - else - puts "Adding '.jsx' in loader extensions in #{shared_config_path}..." - config.gsub!(/extensions = (.*')(\s*\])/, "extensions = \\1, '.jsx'\\2") - end - - File.write shared_config_path, config - - puts "Copying .babelrc to #{Rails.root}" - FileUtils.copy File.expand_path("../../install/examples/react/.babelrc", __dir__), - Rails.root - - puts "Copying react example to #{Webpacker::Configuration.entry_path}" - FileUtils.copy File.expand_path("../../install/examples/react/hello_react.jsx", __dir__), - Rails.root.join(Webpacker::Configuration.entry_path, "hello_react.jsx") - - exec "./bin/yarn add react react-dom babel-preset-react" end end end diff --git a/lib/tasks/installers/vue.rake b/lib/tasks/installers/vue.rake index 460fc4a8c..0f612e16d 100644 --- a/lib/tasks/installers/vue.rake +++ b/lib/tasks/installers/vue.rake @@ -1,35 +1,14 @@ -require "webpacker/configuration" +VUE_TEMPLATE_PATH = File.expand_path("../../install/vue.rb", __dir__) namespace :webpacker do namespace :install do desc "Install everything needed for Vue" task vue: ["webpacker:verify_install"] do - shared_config_path = Webpacker::Configuration.shared_config_path - config = File.read(shared_config_path) - - # Module resolution https://webpack.js.org/concepts/module-resolution/ - if config.include?("'vue$':'vue/dist/vue.esm.js'") - puts "Couldn't automatically update module resolution in #{shared_config_path}. Please set resolve { alias:{ 'vue$':'vue/dist/vue.esm.js' } }." - else - config.gsub!(/resolve:(\s*\{)(\s*)extensions/, "resolve:\\1\\2alias: { 'vue$':'vue/dist/vue.esm.js' },\\2extensions") - end - - if config.include?("loader: 'vue-loader',") - puts "Couldn't automatically update vue-loader in #{shared_config_path}. Please set { test: /.vue$/, loader: 'vue-loader', options: { loaders: { 'scss': 'vue-style-loader!css-loader!sass-loader', 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'}}}." + if Rails::VERSION::MAJOR >= 5 + exec "./bin/rails app:template LOCATION=#{VUE_TEMPLATE_PATH}" else - config.gsub!(/module:(\s*\{)(\s*)rules:(\s*)\[/, "module:\\1\\2rules:\\3[\\2 {\\2 test: /\.vue$/, loader: 'vue-loader',\\2 options: {\\2 loaders: { 'scss': 'vue-style-loader!css-loader!sass-loader', 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'}\\2 }\\2 },") + exec "./bin/rake rails:template LOCATION=#{VUE_TEMPLATE_PATH}" end - - File.write shared_config_path, config - - puts "Copying the Vue example to #{Webpacker::Configuration.entry_path}" - FileUtils.copy File.expand_path("../../install/examples/vue/hello_vue.js", File.dirname(__FILE__)), - Rails.root.join(Webpacker::Configuration.entry_path, "hello_vue.js") - - FileUtils.copy File.expand_path("../../install/examples/vue/app.vue", File.dirname(__FILE__)), - Rails.root.join(Webpacker::Configuration.entry_path, "app.vue") - - exec "./bin/yarn add vue vue-loader vue-template-compiler sass-loader node-sass css-loader axios" end end end diff --git a/lib/webpacker/configuration.rb b/lib/webpacker/configuration.rb index dbd3f2232..ac96a89e6 100644 --- a/lib/webpacker/configuration.rb +++ b/lib/webpacker/configuration.rb @@ -3,6 +3,10 @@ class Webpacker::Configuration < Webpacker::FileLoader class << self + def config_path + Rails.root.join(paths.fetch(:config, "config/webpack")) + end + def entry_path Rails.root.join(source_path, paths.fetch(:entry, "packs")) end @@ -25,10 +29,6 @@ def paths instance.data end - def shared_config_path - Rails.root.join(paths.fetch(:config, "config/webpack"), "shared.js") - end - def source_path Rails.root.join(paths.fetch(:source, "app/javascript")) end