diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f89871d..64cb83aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased Features - Update power api gem to use v2.0.0. Install "internal" API mode [#394](https://github.com/platanus/potassium/pull/394) + - Updates Webpacker to Shakapacker, upgrading Vue and TailwindCSS to their latest versions [#395](https://github.com/platanus/potassium/pull/395) ## 6.5.0 Features diff --git a/lib/potassium/assets/.eslintrc.json b/lib/potassium/assets/.eslintrc.json index f29a436f..eb6b623a 100644 --- a/lib/potassium/assets/.eslintrc.json +++ b/lib/potassium/assets/.eslintrc.json @@ -1,12 +1,16 @@ { "env": { - "es6": true + "browser": true, + "es2021": true, + "node": true, + "jest/globals": true, + "vue/setup-compiler-macros": true }, "parserOptions": { - "ecmaVersion": 2018, + "ecmaVersion": 2020, "sourceType": "module" }, - "plugins": ["import"], + "plugins": ["import", "jest", "tailwindcss"], "settings": { "import/resolver": { "node": { @@ -15,7 +19,10 @@ } }, "extends": [ - "plugin:vue/strongly-recommended" + "plugin:vue/vue3-recommended", + "@vue/typescript/recommended", + "@vue/eslint-config-typescript", + "plugin:tailwindcss/recommended" ], "rules": { "accessor-pairs": 0, @@ -338,14 +345,13 @@ "vue/max-len": ["error", { "code": 120, "ignoreHTMLAttributeValues": true - }] + }] }, "overrides": [ { - "files": ["**/*.js"], - "excludedFiles": "app/**/*.js", - "env": { - "node": true + "files": ["*.ts", "*.vue"], + "rules": { + "no-undef": "off" } } ] diff --git a/lib/potassium/assets/README.yml b/lib/potassium/assets/README.yml index 0379c2f1..bda3cf84 100644 --- a/lib/potassium/assets/README.yml +++ b/lib/potassium/assets/README.yml @@ -27,7 +27,7 @@ readme: body: | For hot-reloading and fast webpacker compilation you need to run webpack's dev server along with the rails server: - $ ./bin/webpack-dev-server + $ ./bin/webpacker-dev-server Running the dev server will also solve problems with the cache not refreshing between changes and provide better error messages if something fails to compile. @@ -136,7 +136,7 @@ readme: This project uses [Active Admin](https://github.com/activeadmin/activeadmin) which is a Ruby on Rails framework for creating elegant backends for website administration. <% if get(:vue_admin) %> This project supports Vue inside ActiveAdmin - - The main package is located in `app/javascript/packs/admin_application.js`, here you will declare the components you want to include in your ActiveAdmin views as you would in a normal Vue App. + - The main package is located in `app/javascript/active_admin.js`, here you will declare the components you want to include in your ActiveAdmin views as you would in a normal Vue App. - Additionally, to be able to use Vue components as [Arbre](https://github.com/activeadmin/arbre) Nodes the component names are also declared in `config/initializers/active_admin.rb` - The generator includes an example component called `admin_component`, you can use this component inside any ActiveAdmin view by just writing `admin_component` as you would with any `html` tag. - For example: diff --git a/lib/potassium/assets/active_admin/admin-component.vue b/lib/potassium/assets/active_admin/admin-component.vue index a75a6fb4..cdf691d8 100644 --- a/lib/potassium/assets/active_admin/admin-component.vue +++ b/lib/potassium/assets/active_admin/admin-component.vue @@ -1,35 +1,27 @@ + + - - diff --git a/lib/potassium/assets/active_admin/admin_application.js b/lib/potassium/assets/active_admin/admin_application.js deleted file mode 100644 index 77b330e4..00000000 --- a/lib/potassium/assets/active_admin/admin_application.js +++ /dev/null @@ -1,14 +0,0 @@ -import Vue from 'vue/dist/vue.esm'; -import AdminComponent from '../components/admin-component'; - -Vue.component('admin_component', AdminComponent); - -document.addEventListener('DOMContentLoaded', () => { - if (document.getElementById('wrapper') !== null) { - return new Vue({ - el: '#wrapper', - }); - } - - return null; -}); diff --git a/lib/potassium/assets/active_admin/init_activeadmin_vue.rb b/lib/potassium/assets/active_admin/init_activeadmin_vue.rb deleted file mode 100644 index 5f0c1deb..00000000 --- a/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +++ /dev/null @@ -1,10 +0,0 @@ -module AdminPageLayoutOverride - def build_page(*args) - within head do - text_node(javascript_packs_with_chunks_tag('admin_application')) - end - super - end -end - -ActiveAdmin::Views::Pages::Base.send :prepend, AdminPageLayoutOverride diff --git a/lib/potassium/assets/app/javascript/app.spec.js b/lib/potassium/assets/app/javascript/components/app.spec.ts similarity index 92% rename from lib/potassium/assets/app/javascript/app.spec.js rename to lib/potassium/assets/app/javascript/components/app.spec.ts index fefca344..0409dc45 100644 --- a/lib/potassium/assets/app/javascript/app.spec.js +++ b/lib/potassium/assets/app/javascript/components/app.spec.ts @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import App from 'app'; +import App from './app.vue'; describe('App', () => { test('is a Vue instance', () => { diff --git a/lib/potassium/assets/app/javascript/components/app.vue b/lib/potassium/assets/app/javascript/components/app.vue new file mode 100644 index 00000000..27a3070d --- /dev/null +++ b/lib/potassium/assets/app/javascript/components/app.vue @@ -0,0 +1,9 @@ + + + diff --git a/lib/potassium/assets/app/javascript/types/vue.d.ts b/lib/potassium/assets/app/javascript/types/vue.d.ts new file mode 100644 index 00000000..2b97bd96 --- /dev/null +++ b/lib/potassium/assets/app/javascript/types/vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/lib/potassium/assets/config/webpack/rules/css.js b/lib/potassium/assets/config/webpack/rules/css.js new file mode 100644 index 00000000..1a8a7a25 --- /dev/null +++ b/lib/potassium/assets/config/webpack/rules/css.js @@ -0,0 +1,5 @@ +module.exports = { + resolve: { + extensions: ['.css', '.scss'] + } +} diff --git a/lib/potassium/assets/config/webpack/rules/index.js b/lib/potassium/assets/config/webpack/rules/index.js new file mode 100644 index 00000000..fcc931ba --- /dev/null +++ b/lib/potassium/assets/config/webpack/rules/index.js @@ -0,0 +1,11 @@ +const vueConfig = require('./vue'); +const cssConfig = require('./css'); +const jQueryConfig = require('./jquery'); +const typescriptConfig = require('./typescript'); + +module.exports = { + vueConfig, + cssConfig, + jQueryConfig, + typescriptConfig, +}; diff --git a/lib/potassium/assets/config/webpack/rules/jquery.js b/lib/potassium/assets/config/webpack/rules/jquery.js new file mode 100644 index 00000000..a531ae79 --- /dev/null +++ b/lib/potassium/assets/config/webpack/rules/jquery.js @@ -0,0 +1,11 @@ +const webpack = require('webpack'); + +module.exports = { + plugins: [ + new webpack.ProvidePlugin({ + $: 'jquery', + jQuery: 'jquery', + "window.jQuery":"jquery" + }) + ], +} diff --git a/lib/potassium/assets/config/webpack/rules/typescript.js b/lib/potassium/assets/config/webpack/rules/typescript.js new file mode 100644 index 00000000..9262d1ca --- /dev/null +++ b/lib/potassium/assets/config/webpack/rules/typescript.js @@ -0,0 +1,32 @@ +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); + +module.exports = { + module: { + rules: [ + { + test: /\.ts$/, + loader: 'ts-loader', + exclude: /node_modules/, + options: { + appendTsSuffixTo: [/\.vue$/], + transpileOnly: true, + }, + }, + ], + }, + resolve: { + extensions: ['.ts'], + }, + plugins: [ + new ForkTsCheckerWebpackPlugin({ + typescript: { + extensions: { + vue: { + enabled: true, + compiler: '@vue/compiler-sfc', + }, + }, + }, + }), + ], +}; diff --git a/lib/potassium/assets/config/webpack/rules/vue.js b/lib/potassium/assets/config/webpack/rules/vue.js new file mode 100644 index 00000000..e4c4a0b6 --- /dev/null +++ b/lib/potassium/assets/config/webpack/rules/vue.js @@ -0,0 +1,19 @@ +const { VueLoaderPlugin } = require('vue-loader') + +module.exports = { + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader' + } + ] + }, + plugins: [new VueLoaderPlugin()], + resolve: { + extensions: ['.vue'], + alias: { + 'vue$': 'vue/dist/vue.esm-bundler.js', + } + }, +} diff --git a/lib/potassium/assets/config/webpack/webpack.config.js b/lib/potassium/assets/config/webpack/webpack.config.js new file mode 100644 index 00000000..6f262a81 --- /dev/null +++ b/lib/potassium/assets/config/webpack/webpack.config.js @@ -0,0 +1,4 @@ +const { webpackConfig, merge } = require('shakapacker'); +const { vueConfig, cssConfig, jQueryConfig, typescriptConfig } = require('./rules'); + +module.exports = merge(typescriptConfig, cssConfig, jQueryConfig, webpackConfig); diff --git a/lib/potassium/assets/tsconfig.json b/lib/potassium/assets/tsconfig.json new file mode 100644 index 00000000..532efa78 --- /dev/null +++ b/lib/potassium/assets/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "useDefineForClassFields": true, + "jsx": "preserve", + "noImplicitThis": true, + "strict": true, + "isolatedModules": true, + "preserveValueImports": true, + "importsNotUsedAsValues": "error", + "target": "esnext", + "allowJs": true, + + // Recommended + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + // See + "skipLibCheck": true, + "baseUrl": "app/javascript/", + "types": ["@types/jest", "@types/node"], + }, + "include": [ + "app/javascript/**/*.ts", + "app/javascript/**/*.vue" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/lib/potassium/cli/commands/create.rb b/lib/potassium/cli/commands/create.rb index 1938968c..c1490b45 100644 --- a/lib/potassium/cli/commands/create.rb +++ b/lib/potassium/cli/commands/create.rb @@ -22,7 +22,7 @@ module Potassium::CLI template = template_finder.default_template template.cli_options = options template.source_paths << Rails::Generators::AppGenerator.source_root - ARGV.push('--skip-webpack-install', '--skip-bundle') + ARGV.push('--skip-javascript', '--skip-bundle') template.start rescue VersionError => e print "\nError: #{e.message}" # rubocop:disable Rails/Output diff --git a/lib/potassium/recipes/admin.rb b/lib/potassium/recipes/admin.rb index 961b0a79..bcd690e2 100644 --- a/lib/potassium/recipes/admin.rb +++ b/lib/potassium/recipes/admin.rb @@ -37,6 +37,7 @@ def add_active_admin add_readme_section :internal_dependencies, :active_admin after(:gem_install, wrap_in_action: :admin_install) do generate "active_admin:install --use_webpacker" + run 'bin/yarn add @activeadmin/activeadmin' line = "ActiveAdmin.setup do |config|" initializer = "config/initializers/active_admin.rb" gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match| @@ -80,6 +81,16 @@ def build _arg import 'arctic_admin'; HERE ) + + run "mv app/javascript/packs/active_admin.js app/javascript/active_admin.js" + gsub_file( + "app/javascript/active_admin.js", + 'import "../stylesheets/active_admin";', + 'import "./stylesheets/active_admin.scss";' + ) + + run 'rm -rf config/webpack/plugins' + run 'rm -rf app/javascript/packs/active_admin' end end end diff --git a/lib/potassium/recipes/front_end.rb b/lib/potassium/recipes/front_end.rb index 9e4f8605..2238dd1f 100644 --- a/lib/potassium/recipes/front_end.rb +++ b/lib/potassium/recipes/front_end.rb @@ -1,10 +1,15 @@ class Recipes::FrontEnd < Rails::AppBuilder VUE_LOADER_VERSION = Potassium::VUE_LOADER_VERSION + VUE_VERSION = Potassium::VUE_VERSION + VUE_TEST_UTILS_VERSION = Potassium::VUE_TEST_UTILS_VERSION + POSTCSS_VERSION = Potassium::POSTCSS_VERSION + TAILWINDCSS_VERSION = Potassium::TAILWINDCSS_VERSION + AUTOPREFIXER_VERSION = Potassium::AUTOPREFIXER_VERSION + VUE_JEST_VERSION = Potassium::VUE_JEST_VERSION def ask frameworks = { vue: "Vue", - angular: "Angular 2", none: "None" } @@ -17,12 +22,16 @@ def ask end def create + gather_gem('shakapacker', '~> 6.0') recipe = self - after(:gem_install) do - value = get(:front_end) + after(:gem_install, wrap_in_action: :webpacker_install) do run "rails webpacker:install" - run "rails webpacker:install:#{value}" unless [:none, :None].include? value.to_sym - + end + after(:webpacker_install) do + value = get(:front_end) + recipe.copy_webpack_rules + recipe.add_assets_path + recipe.setup_typescript recipe.setup_vue if value == :vue recipe.add_responsive_meta_tag recipe.setup_tailwind @@ -38,13 +47,37 @@ def install def installed? package_file = 'package.json' return false unless file_exist?(package_file) + package_content = read_file(package_file) - package_content.include?("\"@angular/core\"") || package_content.include?("\"vue\"") + package_content.include?("\"vue\"") + end + + def copy_webpack_rules + copy_file '../assets/config/webpack/webpack.config.js', + 'config/webpack/webpack.config.js', + force: true + copy_file '../assets/config/webpack/rules/index.js', 'config/webpack/rules/index.js' + copy_file '../assets/config/webpack/rules/css.js', 'config/webpack/rules/css.js' + copy_file '../assets/config/webpack/rules/vue.js', 'config/webpack/rules/vue.js' + copy_file '../assets/config/webpack/rules/jquery.js', 'config/webpack/rules/jquery.js' + copy_file '../assets/config/webpack/rules/typescript.js', 'config/webpack/rules/typescript.js' + end + + def add_assets_path + gsub_file( + 'config/webpacker.yml', + 'additional_paths: []', + "additional_paths: ['app/assets']" + ) + end + + def setup_typescript + run "bin/yarn add typescript fork-ts-checker-webpack-plugin ts-loader @types/node" + copy_file '../assets/tsconfig.json', 'tsconfig.json' end def setup_vue_with_compiler_build - application_js = 'app/javascript/packs/application.js' - remove_file "app/javascript/packs/hello_vue.js" + application_js = 'app/javascript/application.js' create_file application_js, application_js_content, force: true layout_file = "app/views/layouts/application.html.erb" @@ -63,51 +96,65 @@ def add_responsive_meta_tag end def setup_tailwind - run "bin/yarn add tailwindcss@#{Potassium::TAILWINDCSS}" - specify_autoprefixer_postcss_compatibility_versions + run "bin/yarn add css-loader style-loader mini-css-extract-plugin @types/tailwindcss "\ + "css-minimizer-webpack-plugin postcss@#{POSTCSS_VERSION} postcss-loader "\ + "tailwindcss@#{TAILWINDCSS_VERSION} autoprefixer@#{AUTOPREFIXER_VERSION} sass sass-loader "\ + "eslint-plugin-tailwindcss" + run "npx tailwindcss init -p" setup_client_css remove_server_css_requires setup_tailwind_requirements end def setup_jest - run 'bin/yarn add jest vue-jest babel-jest @vue/test-utils jest-serializer-vue babel-core@^7.0.0-bridge.0 --dev' + run "bin/yarn add jest @vue/vue3-jest@#{VUE_JEST_VERSION} babel-jest "\ + "@vue/test-utils@#{VUE_TEST_UTILS_VERSION} ts-jest" json_file = File.read(Pathname.new("package.json")) js_package = JSON.parse(json_file) js_package = js_package.merge(jest_config) json_string = JSON.pretty_generate(js_package) create_file 'package.json', json_string, force: true - copy_file '../assets/app/javascript/app.spec.js', 'app/javascript/app.spec.js' + copy_file '../assets/app/javascript/components/app.spec.ts', + 'app/javascript/components/app.spec.ts' end def setup_apollo run 'bin/yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag' inject_into_file( - 'app/javascript/packs/application.js', + 'app/javascript/application.js', apollo_imports, - after: "import App from '../app.vue';" + after: "import { createApp } from 'vue';" ) inject_into_file( - 'app/javascript/packs/application.js', + 'app/javascript/application.js', apollo_loading, after: "import VueApollo from 'vue-apollo';" ) inject_into_file( - 'app/javascript/packs/application.js', + 'app/javascript/application.js', "\n apolloProvider,", after: "components: { App }," ) - end - - def foce_vue_loader_version - run "bin/yarn add vue-loader@#{VUE_LOADER_VERSION}" + inject_into_file( + 'app/javascript/application.js', + apollo_config, + before: "app.mount('#vue-app');" + ) end def setup_vue - foce_vue_loader_version + run "bin/yarn add vue@#{VUE_VERSION} vue-loader@#{VUE_LOADER_VERSION} "\ + "babel-preset-typescript-vue3" + gsub_file( + 'config/webpack/webpack.config.js', + ' merge(typescriptConfig, cssConfig, jQueryConfig, webpackConfig);', + ' merge(vueConfig, typescriptConfig, cssConfig, jQueryConfig, webpackConfig);' + ) + copy_file '../assets/app/javascript/components/app.vue', 'app/javascript/components/app.vue' + copy_file '../assets/app/javascript/types/vue.d.ts', 'app/javascript/types/vue.d.ts' setup_vue_with_compiler_build setup_jest if get(:api) == :graphql @@ -120,7 +167,6 @@ def setup_vue def frameworks(framework) frameworks = { vue: "vue", - angular: "angular", none: nil } frameworks[framework] @@ -147,18 +193,20 @@ def apollo_loading link: httpLink, cache, }) + JS + end - Vue.use(VueApollo) + def apollo_config + <<~JS + \n + app.use(VueApollo) const apolloProvider = new VueApollo({ defaultClient: apolloClient, }) + \n JS end - def specify_autoprefixer_postcss_compatibility_versions - run 'bin/yarn -D add postcss@^7 autoprefixer@^9' - end - def setup_client_css application_css = 'app/javascript/css/application.css' create_file application_css, "", force: true @@ -167,14 +215,14 @@ def setup_client_css layout_file = "app/views/layouts/application.html.erb" insert_into_file layout_file, stylesheet_pack_tag, before: "" - application_js = 'app/javascript/packs/application.js' + application_js = 'app/javascript/application.js' if get(:front_end) != :vue - create_file application_js, "import '../css/application.css';\n", force: true + create_file application_js, "import './css/application.css';\n", force: true else insert_into_file( application_js, - "\nimport '../css/application.css';", - after: "import App from '../app.vue';" + "\nimport './css/application.css';", + after: "import App from './components/app.vue';" ) end end @@ -185,9 +233,6 @@ def setup_tailwind_requirements tailwind_config = 'tailwind.config.js' create_file tailwind_config, tailwind_config_content, force: true - - postcss_file = 'postcss.config.js' - insert_into_file postcss_file, postcss_require_tailwind, after: "plugins: [\n" end def remove_server_css_requires @@ -197,14 +242,14 @@ def remove_server_css_requires def application_js_content <<~JS - import Vue from 'vue/dist/vue.esm'; - import App from '../app.vue'; + import { createApp } from 'vue'; + import App from './components/app.vue'; document.addEventListener('DOMContentLoaded', () => { - const app = new Vue({ - el: '#vue-app', + const app = createApp({ components: { App }, }); + app.mount('#vue-app'); return app; }); @@ -213,9 +258,9 @@ def application_js_content def tailwind_client_css <<~CSS - @import 'tailwindcss/base'; - @import 'tailwindcss/components'; - @import 'tailwindcss/utilities'; + @tailwind base; + @tailwind components; + @tailwind utilities; CSS end @@ -228,26 +273,16 @@ def tailwind_config_content }, variants: {}, plugins: [], - purge: { - enabled: process.env.NODE_ENV === 'production', - content: [ - './app/**/*.html', - './app/**/*.vue', - './app/**/*.js', - './app/**/*.erb', - ], - } + content: [ + './app/**/*.html', + './app/**/*.vue', + './app/**/*.js', + './app/**/*.erb', + ], }; JS end - def postcss_require_tailwind - <<-JS.gsub(/^ {4}/, ' ') - require('tailwindcss'), - require('autoprefixer'), - JS - end - def jest_config { "scripts": { @@ -267,16 +302,14 @@ def jest_config }, "moduleFileExtensions": [ "js", + "ts", "json", "vue" ], "transform": { - "^.+\\.js$": "/node_modules/babel-jest", - ".*\\.(vue)$": "/node_modules/vue-jest" + "^.+\\.ts$": "ts-jest", + ".*\\.(vue)$": "@vue/vue3-jest" }, - "snapshotSerializers": [ - "/node_modules/jest-serializer-vue" - ], "testEnvironment": "jsdom" } } diff --git a/lib/potassium/recipes/google_tag_manager.rb b/lib/potassium/recipes/google_tag_manager.rb index 0a4c4165..d19a8ffb 100644 --- a/lib/potassium/recipes/google_tag_manager.rb +++ b/lib/potassium/recipes/google_tag_manager.rb @@ -87,7 +87,7 @@ def content_security_policy_code 'https://www.google-analytics.com', 'https://ssl.google-analytics.com' ) - policy.img_src :self, :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com' + policy.img_src :self, 'data:', :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com' end HERE end diff --git a/lib/potassium/recipes/node.rb b/lib/potassium/recipes/node.rb index 64a05f78..6626cb01 100644 --- a/lib/potassium/recipes/node.rb +++ b/lib/potassium/recipes/node.rb @@ -3,19 +3,17 @@ require 'json' class Recipes::Node < Rails::AppBuilder - def create - info "Using node version LTS #{version}" - create_file '.node-version', version, force: true - json_file = File.read(Pathname.new("package.json")) - js_package = JSON.parse(json_file) - js_package["engines"] = { "node" => "#{version}.x" } - json_string = JSON.pretty_generate(js_package) - create_file 'package.json', json_string, force: true - end + NODE_VERSION = Potassium::NODE_VERSION - private - - def version - Potassium::NODE_VERSION + def create + info "Using node version LTS #{NODE_VERSION}" + create_file '.node-version', NODE_VERSION, force: true + after(:webpacker_install) do + json_file = File.read(Pathname.new("package.json")) + js_package = JSON.parse(json_file) + js_package["engines"] = { "node" => "#{NODE_VERSION}.x" } + json_string = JSON.pretty_generate(js_package) + create_file 'package.json', json_string, force: true + end end end diff --git a/lib/potassium/recipes/style.rb b/lib/potassium/recipes/style.rb index 2d044ed8..b2456574 100644 --- a/lib/potassium/recipes/style.rb +++ b/lib/potassium/recipes/style.rb @@ -18,8 +18,14 @@ def add_linters gather_gem 'rubocop-rails' gather_gem 'rubocop-rspec', Potassium::RUBOCOP_RSPEC_VERSION end - run 'bin/yarn add --dev stylelint eslint eslint-plugin-import' - run 'bin/yarn add --dev eslint-plugin-vue' if selected?(:front_end, :vue) + + after(:webpacker_install) do + run "yarn add --dev stylelint eslint eslint-plugin-import "\ + "@typescript-eslint/eslint-plugin @types/jest @typescript-eslint/parser eslint-plugin-jest" + if selected?(:front_end, :vue) + run 'yarn add --dev eslint-plugin-vue @vue/eslint-config-typescript' + end + end end def add_config_files diff --git a/lib/potassium/recipes/vue_admin.rb b/lib/potassium/recipes/vue_admin.rb index 9420e655..68aca48a 100644 --- a/lib/potassium/recipes/vue_admin.rb +++ b/lib/potassium/recipes/vue_admin.rb @@ -35,15 +35,18 @@ def installed? def add_vue_admin add_vue_component_library add_component_integration - copy_file '../assets/active_admin/init_activeadmin_vue.rb', - 'config/initializers/init_activeadmin_vue.rb' - copy_file '../assets/active_admin/admin_application.js', - 'app/javascript/packs/admin_application.js', - force: true - empty_directory 'app/javascript/components' + js_line = 'import "activeadmin_addons"' + gsub_file( + 'app/javascript/active_admin.js', + js_line, + <<~HERE + #{js_line} + #{active_admin_js} + HERE + ) copy_file '../assets/active_admin/admin-component.vue', - 'app/javascript/components/admin-component.vue', - force: true + 'app/javascript/components/admin-component.vue', + force: true end def add_component_integration @@ -121,4 +124,31 @@ def tag_name end HERE end + + def active_admin_js + <<~HERE + import { createApp } from 'vue'; + import AdminComponent from './components/admin-component.vue'; + + function onLoad() { + if (document.getElementById('wrapper') !== null) { + const app = createApp({ + mounted() { + // We need to re-trigger DOMContentLoaded for ArcticAdmin after Vue replaces DOM elements + window.document.dispatchEvent(new Event('DOMContentLoaded', { + bubbles: true, + cancelable: true, + })); + }, + }); + app.component('AdminComponent', AdminComponent); + app.mount('#wrapper'); + } + + return null; + } + + document.addEventListener('DOMContentLoaded', onLoad, { once: true }); + HERE + end end diff --git a/lib/potassium/version.rb b/lib/potassium/version.rb index 52012737..1eccc836 100644 --- a/lib/potassium/version.rb +++ b/lib/potassium/version.rb @@ -7,6 +7,11 @@ module Potassium POSTGRES_VERSION = "11.3" MYSQL_VERSION = "5.7" NODE_VERSION = "14" - TAILWINDCSS = "npm:@tailwindcss/postcss7-compat" - VUE_LOADER_VERSION = "^15.9.7" + TAILWINDCSS_VERSION = "^3" + POSTCSS_VERSION = "^8" + AUTOPREFIXER_VERSION = "^10" + VUE_VERSION = "^3" + VUE_LOADER_VERSION = "^16" + VUE_TEST_UTILS_VERSION = "^2.0.0-rc.18" + VUE_JEST_VERSION = "^27.0.0-alpha.1" end diff --git a/spec/features/front_end_spec.rb b/spec/features/front_end_spec.rb index b7101434..ed6ba2b5 100644 --- a/spec/features/front_end_spec.rb +++ b/spec/features/front_end_spec.rb @@ -9,7 +9,7 @@ let(:application_css_path) { "#{project_path}/app/javascript/css/application.css" } let(:gemfile) { IO.read("#{project_path}/Gemfile") } let(:node_modules_file) { IO.read("#{project_path}/package.json") } - let(:application_js_file) { IO.read("#{project_path}/app/javascript/packs/application.js") } + let(:application_js_file) { IO.read("#{project_path}/app/javascript/application.js") } let(:layout_file) { IO.read("#{project_path}/app/views/layouts/application.html.erb") } let(:application_css_file) { IO.read(application_css_path) } let(:tailwind_config_file) { IO.read("#{project_path}/tailwind.config.js") } @@ -18,13 +18,13 @@ it "creates a project without a front end framework" do remove_project_directory create_dummy_project("front_end" => "None") - expect(gemfile).to include('webpacker') + expect(gemfile).to include('shakapacker') end - def expect_to_have_tailwind_compatibility_build - expect(node_modules_file).to include("\"tailwindcss\": \"npm:@tailwindcss/postcss7-compat\"") - expect(node_modules_file).to include("\"autoprefixer\": \"^9\"") - expect(node_modules_file).to include("\"postcss\": \"^7\"") + def expect_to_have_tailwind_package_versions + expect(node_modules_file).to include("\"tailwindcss\": \"^3\"") + expect(node_modules_file).to include("\"autoprefixer\": \"^10\"") + expect(node_modules_file).to include("\"postcss\": \"^8\"") end context "with vue" do @@ -33,11 +33,11 @@ def expect_to_have_tailwind_compatibility_build create_dummy_project("front_end" => "vue") end - it "creates a project with vue in compiler mode as frontend framework" do - expect(gemfile).to include('webpacker') + it "creates a project with vue as frontend framework" do + expect(gemfile).to include('shakapacker') expect(node_modules_file).to include("\"vue\"") - expect(application_js_file).to include('vue/dist/vue.esm') - expect(application_js_file).to include("el: '#vue-app'") + expect(application_js_file).to include('vue') + expect(application_js_file).to include("app.mount('#vue-app')") expect(layout_file).to include('id="vue-app"') end @@ -46,7 +46,7 @@ def expect_to_have_tailwind_compatibility_build end it "creates a vue project with client css" do - expect(application_js_file).to include("import '../css/application.css';") + expect(application_js_file).to include("import './css/application.css';") expect(layout_file).to include("<%= stylesheet_pack_tag 'application' %>") expect(rails_css_file).not_to include('*= require_tree', '*= require_self') end @@ -54,14 +54,14 @@ def expect_to_have_tailwind_compatibility_build it "creates a vue project with tailwindcss" do expect(node_modules_file).to include("\"tailwindcss\"") expect(application_css_file).to include( - "@import 'tailwindcss/base';", - "@import 'tailwindcss/components';" + "@tailwind base;", + "@tailwind components;" ) expect(tailwind_config_file).to include('module.exports') end it 'includes correct packages for tailwind, postcss and autoprefixer compatibility build' do - expect_to_have_tailwind_compatibility_build + expect_to_have_tailwind_package_versions end it 'includes correct version of vue-loader in package' do @@ -77,29 +77,9 @@ def expect_to_have_tailwind_compatibility_build it "creates a vue project with apollo" do expect(node_modules_file).to include("\"vue-apollo\"") expect(application_js_file).to include("import { ApolloClient } from 'apollo-client';") - expect(application_js_file).to include("Vue.use(VueApollo)") + expect(application_js_file).to include("app.use(VueApollo)") expect(application_js_file).to include("apolloProvider,") end end end - - context "with angular" do - before(:all) do - remove_project_directory - create_dummy_project("front_end" => "angular") - end - - it "creates a project without vue as front end framework" do - expect(gemfile).to include('webpacker') - expect(node_modules_file).to include("\"@angular/core\"") - end - - it "creates application_js_file for tailwind without vue" do - expect(application_js_file).to include("import '../css/application.css';") - end - - it 'includes correct packages for tailwind, postcss and autoprefixer compatibility build' do - expect_to_have_tailwind_compatibility_build - end - end end diff --git a/spec/features/vue_admin_spec.rb b/spec/features/vue_admin_spec.rb index a9b65bf3..da49bc36 100644 --- a/spec/features/vue_admin_spec.rb +++ b/spec/features/vue_admin_spec.rb @@ -14,11 +14,6 @@ expect(initializer_file).to include("AUTO_BUILD_ELEMENTS") end - it "adds layout tags to active admin layout initializer" do - initializer_path = "#{project_path}/config/initializers/init_activeadmin_vue.rb" - expect(File).to be_file(initializer_path) - end - it "adds vue_component to library" do vue_component_lib_path = "#{project_path}/lib/vue_component.rb" expect(File).to be_file(vue_component_lib_path) @@ -26,11 +21,6 @@ expect(vue_component_lib).to include("VueComponent") end - it "adds admin_application pack to packs" do - application_pack_path = "#{project_path}/app/javascript/packs/admin_application.js" - expect(File).to be_file(application_pack_path) - end - it "adds admin_component to vue components" do application_pack_path = "#{project_path}/app/javascript/components/admin-component.vue" expect(File).to be_file(application_pack_path)