lib/generators/hyperstack/install_generator_base.rb in rails-hyperstack-1.0.alpha1.5 vs lib/generators/hyperstack/install_generator_base.rb in rails-hyperstack-1.0.alpha1.6

- old
+ new

@@ -4,81 +4,122 @@ module Generators class Base < Thor::Group protected + def warnings + @warnings ||= [] + end + def create_component_file(template) clear_cache insure_hyperstack_loader_installed + check_javascript_link_directory + added = webpack_check insure_base_component_class_exists @no_help = options.key?('no-help') self.components.each do |component| component_array = component.split('::') @modules = component_array[0..-2] @file_name = component_array.last @indent = 0 template template, - File.join('app', 'hyperstack', 'components', + Rails.root.join('app', 'hyperstack', 'components', *@modules.map(&:downcase), "#{@file_name.underscore}.rb") end add_route + return unless added + + say '📦 Webpack integrated with Hyperstack. '\ + 'Add javascript assets to app/javascript/packs/client_only.js and /client_and_server.js 📦', :green end def clear_cache - run 'rm -rf tmp/cache' unless Dir.exist?(File.join('app', 'hyperstack')) + run 'rm -rf tmp/cache' unless Dir.exist?(Rails.root.join('app', 'hyperstack')) end def insure_hyperstack_loader_installed - application_js = File.join( + hyperstack_loader = %r{//=\s+require\s+hyperstack-loader\s+} + application_js = Rails.root.join( 'app', 'assets', 'javascripts', 'application.js' ) - require_tree = %r{//=\s+require_tree\s+} - hyperstack_loader = %r{//=\s+require\s+hyperstack-loader\s+} - unless File.foreach(application_js).any? { |l| l =~ hyperstack_loader } - if File.foreach(application_js).any? { |l| l =~ require_tree } - inject_into_file 'app/assets/javascripts/application.js', before: require_tree do - "//= require hyperstack-loader\n" + if File.exist? application_js + unless File.foreach(application_js).any? { |l| l =~ hyperstack_loader } + require_tree = %r{//=\s+require_tree\s+} + if File.foreach(application_js).any? { |l| l =~ require_tree } + inject_into_file 'app/assets/javascripts/application.js', verbose: false, before: require_tree do + "//= require hyperstack-loader\n" + end + else + warnings << + " ***********************************************************\n"\ + " * Could not add `//= require hyperstack-loader` directive *\n"\ + " * to the app/assets/application.js file. *\n"\ + " * Normally this directive is added just before the *\n"\ + " * `//= require_tree .` directive at the end of the file, *\n"\ + " * but no require_tree directive was found. You need to *\n"\ + " * manually add `//= require hyperstack-loader` to the *\n"\ + " * app/assets/application.js file. *\n"\ + " ***********************************************************\n" end - else - puts " ***********************************************************\n"\ - " * Could not add `//= require hyperstack-loader` directive *\n"\ - " * to the app/assets/application.js file. *\n"\ - " * Normally this directive is added just before the *\n"\ - " * `//= require_tree .` directive at the end of the file, *\n"\ - " * but no require_tree directive was found. You need to *\n"\ - " * manually add `//= require hyperstack-loader` to the *\n"\ - " * app/assets/application.js file. *\n"\ - " ***********************************************************\n" end + else + create_file application_js, "//= require hyperstack-loader\n" + warnings << + " ***********************************************************\n"\ + " * Could not find the app/assets/application.js file. *\n"\ + " * We created one for you, and added the *\n"\ + " * `<%= javascript_include_tag 'application' %>` to your *\n"\ + " * `html.erb` files immediately after any *\n"\ + " * `<%= javascript_pack 'application' %>` tags we found. *\n"\ + " ***********************************************************\n" + application_pack_tag = + /\s*\<\%\=\s+javascript_pack_tag\s+(\'|\")application(\'|\").*\%\>.*$/ + Dir.glob(Rails.root.join('app', 'views', '**', '*.erb')) do |file| + if File.foreach(file).any? { |l| l =~ application_pack_tag } + inject_into_file file, verbose: false, after: application_pack_tag do + "\n <%= javascript_include_tag 'application' %>" + end + end + end end end + def check_javascript_link_directory + manifest_js_file = Rails.root.join("app", "assets", "config", "manifest.js") + return unless File.exist? manifest_js_file + return unless File.readlines(manifest_js_file).grep(/javascripts \.js/).empty? + + append_file manifest_js_file, "//= link_directory ../javascripts .js\n", verbose: false + end + def insure_base_component_class_exists @component_base_class = options['base-class'] || Hyperstack.component_base_class - file_name = File.join( + file_name = Rails.root.join( 'app', 'hyperstack', 'components', "#{@component_base_class.underscore}.rb" ) template 'hyper_component_template.rb', file_name unless File.exist? file_name end def add_to_manifest(manifest, &block) if File.exist? "app/javascript/packs/#{manifest}" - append_file "app/javascript/packs/#{manifest}", &block + append_file "app/javascript/packs/#{manifest}", verbose: false, &block else - create_file "app/javascript/packs/#{manifest}", &block + create_file "app/javascript/packs/#{manifest}", verbose: false, &block end end def add_route return unless options['add-route'] if self.components.count > 1 - puts " ***********************************************************\n"\ - " * The add-route option ignored because more than one *\n"\ - " * component is being generated. *\n"\ - " ***********************************************************\n" + warnings << + " ***********************************************************\n"\ + " * The add-route option ignored because more than one *\n"\ + " * component is being generated. *\n"\ + " ***********************************************************\n" return end action_name = (@modules+[@file_name.underscore]).join('__') path = options['add-route'] == 'add-route' ? '/(*others)' : options['add-route'] routing_code = "get '#{path}', to: 'hyperstack##{action_name}'\n" @@ -91,9 +132,157 @@ end def yarn(package, version = nil) return if system("yarn add #{package}#{'@' + version if version}") raise Thor::Error.new("yarn failed to install #{package} with version #{version}") + end + + def install_webpack + insure_yarn_loaded + add_webpacker_manifests + add_webpacks + cancel_react_source_import + install_webpacker + end + + def inject_into_initializer(s) + file_name = Rails.root.join('config', 'initializers', 'hyperstack.rb') + if File.exist?(file_name) + prepend_to_file(file_name, verbose: false) { "#{s}\n" } + else + create_file file_name, <<-RUBY +#{s} + +# server_side_auto_require will patch the ActiveSupport Dependencies module +# so that you can define classes and modules with files in both the +# app/hyperstack/xxx and app/xxx directories. For example you can split +# a Todo model into server and client related definitions and place this +# in `app/hyperstack/models/todo.rb`, and place any server only definitions in +# `app/models/todo.rb`. + +require "hyperstack/server_side_auto_require.rb" + +# set the component base class + +Hyperstack.component_base_class = 'HyperComponent' # i.e. 'ApplicationComponent' + +# prerendering is default :off, you should wait until your +# application is relatively well debugged before turning on. + +Hyperstack.prerendering = :off # or :on + +# add this line if you need jQuery AND ARE NOT USING WEBPACK +# Hyperstack.import 'hyperstack/component/jquery', client_only: true + +# change definition of on_error to control how errors such as validation +# exceptions are reported on the server +module Hyperstack + def self.on_error(operation, err, params, formatted_error_message) + ::Rails.logger.debug( + "\#{formatted_error_message}\\n\\n" + + Pastel.new.red( + 'To further investigate you may want to add a debugging '\\ + 'breakpoint to the on_error method in config/initializers/hyperstack.rb' + ) + ) + end +end if Rails.env.development? + RUBY + end + # whenever we modify the initializer its best to empty the cache, BUT + # we only need to it once per generator execution + run 'rm -rf tmp/cache' unless @cache_emptied_already + @cache_emptied_already = true + end + + private + + def webpack_check + return unless defined? ::Webpacker + + client_and_server = Rails.root.join("app", "javascript", "packs", "client_only.js") + return if File.exist? client_and_server + + # Dir.chdir(Rails.root.join.to_s) { run 'bundle exec rails hyperstack:install:webpack' } + + # say "warning: you are running webpacker, but the hyperstack webpack files have not been created.\n"\ + # " Suggest you run bundle exec rails hyperstack:install:webpack soon.\n"\ + # " Or to avoid this warning create an empty file named app/javascript/packs/client_only.js", + # :red + install_webpack + true + end + + def insure_yarn_loaded + begin + yarn_version = `yarn --version` + raise Errno::ENOENT if yarn_version.blank? + rescue Errno::ENOENT + raise Thor::Error.new("please insure nodejs is installed and the yarn command is available if using webpacker") + end + end + + def add_webpacker_manifests + create_file 'app/javascript/packs/client_and_server.js', <<-JAVASCRIPT +//app/javascript/packs/client_and_server.js +// these packages will be loaded both during prerendering and on the client +React = require('react'); // react-js library +createReactClass = require('create-react-class'); // backwards compatibility with ECMA5 +History = require('history'); // react-router history library +ReactRouter = require('react-router'); // react-router js library +ReactRouterDOM = require('react-router-dom'); // react-router DOM interface +ReactRailsUJS = require('react_ujs'); // interface to react-rails +// to add additional NPM packages run `yarn add package-name@version` +// then add the require here. + JAVASCRIPT + create_file 'app/javascript/packs/client_only.js', <<-JAVASCRIPT +//app/javascript/packs/client_only.js +// add any requires for packages that will run client side only +ReactDOM = require('react-dom'); // react-js client side code +jQuery = require('jquery'); // remove if you don't need jQuery +// to add additional NPM packages call run yarn add package-name@version +// then add the require here. + JAVASCRIPT + append_file 'config/initializers/assets.rb', verbose: false do + <<-RUBY + Rails.application.config.assets.paths << Rails.root.join('public', 'packs', 'js').to_s + RUBY + end + inject_into_file 'config/environments/test.rb', verbose: false, before: /^end/ do + <<-RUBY + + # added by hyperstack installer + config.assets.paths << Rails.root.join('public', 'packs-test', 'js').to_s + RUBY + end + end + + def add_webpacks + yarn 'react', '16' + yarn 'react-dom', '16' + yarn 'react-router', '^5.0.0' + yarn 'react-router-dom', '^5.0.0' + yarn 'react_ujs', '^2.5.0' + yarn 'jquery', '^3.4.1' + yarn 'create-react-class' + end + + def cancel_react_source_import + inject_into_initializer( + "# Hyperstack.import 'react/react-source-browser' "\ + "# uncomment this line if you want hyperstack to use its copy of react" + ) + end + + def install_webpacker + return if defined?(::Webpacker) + + gem "webpacker" + Bundler.with_unbundled_env do + run "bundle install" + end + `spring stop` + Dir.chdir(Rails.root.join.to_s) { run 'bundle exec rails webpacker:install' } end end end end