require_relative '../lib' class VueCreate def initialize @input = InputLoop.new @pwd = FileUtils.pwd @root = ::Rails.root @pack = @root.join('package.json') @src_dir = Pathname.new(__FILE__).dirname.join('..', '..', 'source') end def self.run! new.start end def start check_node! FileUtils.chdir(@root) @pending_install = false @pending_fixes = [] begin package_manager? install_vue_cli run_vue_create? install_dev_deps @pm.install if @pending_install delete_vue_sample? copy_demo? copy_config generate_vue_yml ensure_entry_point! fix_jest_config! eslint_fix! puts 'vue:create finished!' ensure FileUtils.chdir(@pwd) end end private def check_node! @pm = VueCli::Rails::NodeEnv.new abort('Cannot find node.js') unless @pm.node? end def package_manager? yarn = @pm.yarn_version npm = @pm.npm_version abort('Cannot find npm or yarn') unless yarn || npm input = if yarn && npm @input.gets( 'Which package manager to use?', @root.join('package-lock.json').exist? ? 'yN' : 'Yn', y: 'yarn', n: 'npm', ) else npm ? 'n' : 'y' end @pm.use!(input == 'n' ? :npm : :yarn) puts "Using package manager: #{@pm.package_manager}" end def install_vue_cli return if @pm.vue? puts "@vue/cli haven't been installed" pm.global_add('@vue/cli') end def run_vue_create? pack_input = 'y' if @pack.exist? puts 'Detected `package.json`!' pack_input = @input.gets( ' Do you want `vue create?` to overwrite your package.json', 'yAks', a: 'Auto', k: 'Keep', s: 'Skip vue create', ) end return if pack_input == 's' gi_input = 'y' gi = @root.join('.gitignore') if gi.exist? puts 'Detected `.gitignore`!' gi_input = @input.gets( ' Do you want `vue create?` to overwrite your .gitignore', 'yMk', m: 'Merge', k: 'Keep', ) end # backups vue_config = @root.join('vue.config.js') backup_vue_config = vue_config.exist? src_json = JSON.parse(@pack.read) if pack_input != 'y' gi_lines = gi.read.split("\n") if gi_input != 'y' if backup_vue_config FileUtils.mv(vue_config, "#{vue_config}.backup") # will be recreated FileUtils.rm_f(@root.join('vue.rails.js')) end pub_dir = @root.join('public') FileUtils.mv(pub_dir, "#{pub_dir}.backup") reademe = @root.join('README.md') reademe = reademe.exist? && reademe FileUtils.mv(reademe, "#{reademe}.backup") if reademe begin @pm.exec('vue create', '', "-n -m #{@pm.package_manager} .") ensure # restore backups FileUtils.rm_rf(pub_dir) if pub_dir.exist? FileUtils.mv("#{pub_dir}.backup", pub_dir) FileUtils.mv("#{vue_config}.backup", vue_config) if backup_vue_config if reademe FileUtils.rm_f(reademe) FileUtils.mv("#{reademe}.backup", reademe) end end # merge gitignore if gi_input == 'm' old_gi = Set.new(gi_lines.map(&:strip)) new_lines = gi.read.split("\n") new_lines.map(&:strip).find_all(&:present?).each do |ln| gi_lines << ln unless old_gi.include?(ln) end gi.write(gi_lines.map { |ln| "#{ln}\n" }.join('')) if gi_lines.size != new_lines.size elsif gi_input == 'k' gi.write(gi_lines.join('')) end # check packages.json return if pack_input == 'y' dst_json = JSON.parse(@pack.read) return if dst_json == src_json # merge packages.json src_json, dst_json = [dst_json, src_json] if pack_input == 'a' dst_json.deep_merge!(src_json) @pack.write(JSON.pretty_generate(dst_json)) @pending_install = true end def install_dev_deps package = JSON.parse(@pack.read) dev_deps = package['devDependencies'] dd = %w[webpack-assets-manifest js-yaml].find_all do |dep| !dev_deps.key?(dep) end if dd.any? @pm.add("-D #{dd.join(' ')}") @pending_install = false end end def delete_vue_sample? %w[src dist].each do |fn| file = @root.join(fn) next unless file.exist? puts "Detected `#{fn}` (should be generated by vue-cli)" FileUtils.rm_rf(file.to_s) if @input.gets(" Do you want to delete #{file}?") == 'y' end end def copy_demo? return unless @input.gets('Do you want to copy demo code?', 'yN') == 'y' FileUtils.cp_r(@src_dir.join('app'), @root) routes = @root.join('config', 'routes.rb') return unless routes.exist? route_lines = routes.read.split("\n") foo = false bar = false route_lines.each do |line| foo ||= line.include?('vue_demo#foo') bar ||= line.include?('vue_demo#bar') end return if foo && bar end_line = route_lines.rindex { |ln| ln =~ /^\s*end\b/ } return if end_line.blank? route_lines.insert(end_line, " get 'vue_demo/baz' => 'vue_demo#baz'") unless foo route_lines.insert(end_line, " get 'vue_demo/bar' => 'vue_demo#bar'") unless bar route_lines.insert(end_line, " get 'vue_demo/foo' => 'vue_demo#foo'") unless foo route_lines.insert(end_line, ' # Example of vue_cli-rails') routes.write(route_lines.join("\n")) end def copy_config puts 'Copying configuration files...' FileUtils.cp(@src_dir.join('vue.rails.js'), "#{@root}/") src_cfg = @src_dir.join('vue.config.js') dst_cfg = @root.join('vue.config.js') config = src_cfg.read if dst_cfg.exist? if dst_cfg.size > 500 puts 'Detected `vue.config.js`!' return if @input.gets(' Do you want to overwrite vue.config.js?', 'yN') == 'n' else old_cfg = %x`node -e "console.log(JSON.stringify(require('./vue.config.js'),null,2))"` config = config.sub('const oldConfig = {};', "const oldConfig = #{old_cfg};") end end dst_cfg.write(config) @pending_fixes << dst_cfg.to_s end def generate_vue_yml yml_dest = @root.join('config', 'vue.yml') if yml_dest.exist? puts 'Detected `config/vue.yml`!' return if @input.gets(' Do you want to overwrite config/vue.yml?') == 'n' end yml = @src_dir.join('vue.yml').read yml = yml.sub('${PACKAGE_MANAGER}', @pm.package_manager.to_s) yml_dest.write(yml) end def ensure_entry_point! %w[entry_points assets components utils views].each do |dir_name| dir = @root.join("app/assets/vue/#{dir_name}") FileUtils.mkdir_p(dir) unless dir.exist? end ep_dir = @root.join('app/assets/vue/entry_points') return if Dir[ep_dir.join('**/*.js')].any? ep_dir.join('entry_point_demo.js').write("console.log('This is a demo entry point')") end def fix_jest_config! jest_file = @root.join('jest.config.js') return unless jest_file.exist? jest_config = %x`node -e "console.log(JSON.stringify(require('./jest.config.js')))"` jest_config = JSON.parse(jest_config) if jest_config['transformIgnorePatterns'] && !jest_config['transformIgnorePatterns'].include?('/node_modules/') jest_config['transformIgnorePatterns'].unshift('/node_modules/') end jest_config.delete('moduleNameMapper') jest = <<~JS const { jestModuleNameMapper: moduleNameMapper } = require('./vue.rails'); module.exports = #{JSON.pretty_generate(jest_config)}_MODULE_NAME_MAPPER_; JS jest_file.write(jest.sub(/\n?}_MODULE_NAME_MAPPER_/, ",\n moduleNameMapper\n}")) @pending_fixes << jest_file.to_s rescue => e STDERR.puts e.message end def eslint_fix! dev_deps = JSON.parse(@pack.read)['devDependencies'] return unless dev_deps['eslint'].present? @pm.exec('eslint', @pending_fixes.map { |fn| "'#{fn}'" }.join(' '), '--fix') rescue => e STDERR.puts e.message end end