potassium 5.2.1 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +84 -0
  3. data/.gitignore +2 -1
  4. data/.node-version +1 -0
  5. data/.rubocop.yml +530 -0
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +91 -0
  8. data/README.md +41 -47
  9. data/docs/DSL.md +5 -5
  10. data/lib/potassium/assets/.buildpacks +0 -1
  11. data/lib/potassium/assets/.circleci/config.yml.erb +102 -0
  12. data/lib/potassium/assets/.eslintrc.json +352 -0
  13. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  14. data/lib/potassium/assets/.pryrc +1 -1
  15. data/lib/potassium/assets/.rubocop.yml +528 -0
  16. data/lib/potassium/assets/.stylelintrc.json +46 -0
  17. data/lib/potassium/assets/Dockerfile.ci +1 -1
  18. data/lib/potassium/assets/Makefile.erb +21 -32
  19. data/lib/potassium/assets/Procfile +1 -0
  20. data/lib/potassium/assets/README.yml +58 -11
  21. data/lib/potassium/assets/active_admin/admin-component.vue +35 -0
  22. data/lib/potassium/assets/active_admin/admin_application.js +14 -0
  23. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +10 -0
  24. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  25. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  26. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  27. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  28. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  29. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  30. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  31. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  32. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  33. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  34. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  35. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  36. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  37. data/lib/potassium/assets/app/javascript/app.spec.js +14 -0
  38. data/lib/potassium/assets/app/uploaders/base_uploader.rb +11 -0
  39. data/lib/potassium/assets/app/uploaders/image_uploader.rb +5 -0
  40. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  41. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  42. data/lib/potassium/assets/bin/cibuild.erb +24 -7
  43. data/lib/potassium/assets/bin/release +9 -0
  44. data/lib/potassium/assets/bin/setup.erb +1 -1
  45. data/lib/potassium/assets/config/database_mysql.yml.erb +2 -2
  46. data/lib/potassium/assets/config/database_postgresql.yml.erb +2 -2
  47. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  48. data/lib/potassium/assets/config/puma.rb +1 -1
  49. data/lib/potassium/assets/config/shrine.rb +36 -0
  50. data/lib/potassium/assets/docker-compose.ci.yml +2 -1
  51. data/lib/potassium/assets/lib/tasks/auto_annotate_models.rake +34 -33
  52. data/lib/potassium/assets/package.json +4 -1
  53. data/lib/potassium/assets/redis.yml +1 -2
  54. data/lib/potassium/assets/testing/rails_helper.rb +4 -2
  55. data/lib/potassium/cli/commands/create.rb +12 -19
  56. data/lib/potassium/cli_options.rb +77 -26
  57. data/lib/potassium/helpers/gem-helpers.rb +1 -1
  58. data/lib/potassium/helpers/template-helpers.rb +4 -0
  59. data/lib/potassium/newest_version_ensurer.rb +19 -36
  60. data/lib/potassium/node_version_ensurer.rb +30 -0
  61. data/lib/potassium/recipes/admin.rb +3 -3
  62. data/lib/potassium/recipes/annotate.rb +1 -1
  63. data/lib/potassium/recipes/api.rb +93 -21
  64. data/lib/potassium/recipes/background_processor.rb +66 -19
  65. data/lib/potassium/recipes/ci.rb +6 -4
  66. data/lib/potassium/recipes/data_migrate.rb +44 -0
  67. data/lib/potassium/recipes/database.rb +4 -0
  68. data/lib/potassium/recipes/database_container.rb +7 -5
  69. data/lib/potassium/recipes/draper.rb +1 -10
  70. data/lib/potassium/recipes/file_storage.rb +66 -0
  71. data/lib/potassium/recipes/front_end.rb +219 -9
  72. data/lib/potassium/recipes/github.rb +93 -15
  73. data/lib/potassium/recipes/google_tag_manager.rb +90 -0
  74. data/lib/potassium/recipes/heroku.rb +44 -30
  75. data/lib/potassium/recipes/mailer.rb +27 -4
  76. data/lib/potassium/recipes/node.rb +21 -0
  77. data/lib/potassium/recipes/rack_cors.rb +18 -15
  78. data/lib/potassium/recipes/schedule.rb +17 -2
  79. data/lib/potassium/recipes/style.rb +21 -3
  80. data/lib/potassium/recipes/vue_admin.rb +124 -0
  81. data/lib/potassium/templates/application.rb +9 -7
  82. data/lib/potassium/version.rb +6 -4
  83. data/potassium.gemspec +11 -6
  84. data/spec/features/api_spec.rb +25 -0
  85. data/spec/features/background_processor_spec.rb +19 -6
  86. data/spec/features/data_migrate_spec.rb +14 -0
  87. data/spec/features/database_container_spec.rb +2 -5
  88. data/spec/features/draper_spec.rb +1 -6
  89. data/spec/features/file_storage_spec.rb +75 -0
  90. data/spec/features/front_end_spec.rb +88 -0
  91. data/spec/features/github_spec.rb +53 -8
  92. data/spec/features/google_tag_manager_spec.rb +59 -0
  93. data/spec/features/graphql_spec.rb +71 -0
  94. data/spec/features/heroku_spec.rb +8 -5
  95. data/spec/features/mailer_spec.rb +58 -0
  96. data/spec/features/new_project_spec.rb +6 -14
  97. data/spec/features/node_spec.rb +28 -0
  98. data/spec/features/power_types_spec.rb +5 -16
  99. data/spec/features/schedule_spec.rb +11 -4
  100. data/spec/features/vue_admin_spec.rb +47 -0
  101. data/spec/spec_helper.rb +5 -0
  102. data/spec/support/fake_octokit.rb +31 -0
  103. data/spec/support/potassium_test_helpers.rb +26 -9
  104. data/tmp/.keep +0 -0
  105. metadata +157 -48
  106. data/circle.yml +0 -12
  107. data/lib/potassium/assets/.circleci/config.yml +0 -20
  108. data/lib/potassium/assets/active_admin/active_admin.js.coffee +0 -4
  109. data/lib/potassium/assets/active_admin/init_activeadmin_angular.rb +0 -8
  110. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  111. data/lib/potassium/assets/api/base_controller.rb +0 -9
  112. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  113. data/lib/potassium/assets/api/responder.rb +0 -41
  114. data/lib/potassium/assets/aws.rb +0 -1
  115. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
  116. data/lib/potassium/assets/testing/paperclip.rb +0 -59
  117. data/lib/potassium/recipes/active_storage.rb +0 -40
  118. data/lib/potassium/recipes/angular_admin.rb +0 -56
  119. data/lib/potassium/recipes/aws_sdk.rb +0 -7
  120. data/lib/potassium/recipes/paperclip.rb +0 -47
  121. data/spec/features/active_storage_spec.rb +0 -30
  122. data/spec/features/front_end.rb +0 -30
@@ -1,7 +1,7 @@
1
1
  class Recipes::Ci < Rails::AppBuilder
2
2
  def create
3
3
  copy_file '../assets/Dockerfile.ci', 'Dockerfile.ci'
4
- copy_file '../assets/.circleci/config.yml', '.circleci/config.yml'
4
+ template '../assets/.circleci/config.yml.erb', '.circleci/config.yml'
5
5
 
6
6
  template '../assets/bin/cibuild.erb', 'bin/cibuild'
7
7
  run "chmod a+x bin/cibuild"
@@ -36,11 +36,13 @@ class Recipes::Ci < Rails::AppBuilder
36
36
  YAML
37
37
  compose.add_service("postgresql", srv)
38
38
  compose.add_link('test', 'postgresql')
39
- compose.add_env('test', 'POSTGRESQL_USER', 'postgres')
40
- compose.add_env('test', 'POSTGRESQL_HOST', 'postgresql')
41
- compose.add_env('test', 'POSTGRESQL_PORT', '5432')
39
+ compose.add_env('test', 'DB_USER', 'postgres')
40
+ compose.add_env('test', 'DB_HOST', 'postgresql')
41
+ compose.add_env('test', 'DB_PORT', '5432')
42
42
  end
43
43
 
44
+ compose.add_volume('test_data')
45
+
44
46
  add_readme_header :ci
45
47
 
46
48
  application 'config.assets.js_compressor = :uglifier', env: 'test'
@@ -0,0 +1,44 @@
1
+ class Recipes::DataMigrate < Rails::AppBuilder
2
+ def create
3
+ gather_gem('data_migrate')
4
+ annotate_task = 'lib/tasks/auto_annotate_models.rake'
5
+ insert_into_file annotate_task, annotate_config, after: "Annotate.load_tasks\n"
6
+ end
7
+
8
+ def install
9
+ create
10
+ end
11
+
12
+ def installed?
13
+ gem_exists?(/data_migrate/)
14
+ end
15
+
16
+ private
17
+
18
+ def annotate_config
19
+ <<-RUBY
20
+
21
+ data_migrate_tasks = %w(
22
+ db:migrate:with_data
23
+ db:migrate:up:with_data
24
+ db:migrate:down:with_data
25
+ db:migrate:redo:with_data
26
+ db:rollback:with_data
27
+ )
28
+
29
+ data_migrate_tasks.each do |task|
30
+ Rake::Task[task].enhance do
31
+ Rake::Task[Rake.application.top_level_tasks.last].enhance do
32
+ annotation_options_task = if Rake::Task.task_defined?('app:set_annotation_options')
33
+ 'app:set_annotation_options'
34
+ else
35
+ 'set_annotation_options'
36
+ end
37
+ Rake::Task[annotation_options_task].invoke
38
+ Annotate::Migration.update_annotations
39
+ end
40
+ end
41
+ end
42
+ RUBY
43
+ end
44
+ end
@@ -46,5 +46,9 @@ class Recipes::Database < Rails::AppBuilder
46
46
  else
47
47
  gather_gem db[:gem_name]
48
48
  end
49
+
50
+ after(:gem_install) do
51
+ generate 'strong_migrations:install'
52
+ end
49
53
  end
50
54
  end
@@ -24,20 +24,21 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
24
24
  environment:
25
25
  MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
26
26
  volumes:
27
- - mysql_data:/var/lib/mysql
27
+ - mysql_data:/var/lib/mysql
28
28
  YAML
29
29
 
30
30
  def create
31
- copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
31
+ db_type = get(:database)
32
+ return if [:None, :none].include? db_type.to_sym
32
33
 
34
+ copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
33
35
  compose = DockerHelpers.new('docker-compose.yml')
34
36
 
35
- db_type = get(:database)
36
37
  compose.add_service(db_type.to_s, self.class.const_get("#{db_type}_service".upcase))
37
38
  compose.add_volume("#{db_type}_data")
38
39
  template '../assets/Makefile.erb', 'Makefile'
39
40
 
40
- run 'make services-up'
41
+ run "docker-compose up -d"
41
42
 
42
43
  set_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
43
44
  set_dot_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
@@ -53,11 +54,12 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
53
54
  setup_text = # setup file is templated on project creation, manual install is needed
54
55
  <<~TEXT
55
56
  # Set up required services
56
- make services-up
57
+ docker-compose up -d
57
58
 
58
59
  TEXT
59
60
 
60
61
  insert_into_file 'bin/setup', setup_text, before: "# Set up database"
62
+ create
61
63
  run 'bin/setup'
62
64
  info "A new container with a #{get(:database)} database has been created."
63
65
  end
@@ -7,7 +7,6 @@ class Recipes::Draper < Rails::AppBuilder
7
7
  def create
8
8
  return unless selected?(:draper)
9
9
  add_draper
10
- add_api_responder if selected?(:api_support)
11
10
  end
12
11
 
13
12
  def installed?
@@ -16,19 +15,11 @@ class Recipes::Draper < Rails::AppBuilder
16
15
 
17
16
  def install
18
17
  add_draper
19
- api_recipe = load_recipe(:api)
20
- add_api_responder if api_recipe.installed?
21
18
  end
22
19
 
23
20
  def add_draper
24
- gather_gem 'draper', '3.0.1'
21
+ gather_gem 'draper', '~> 3.1'
25
22
  add_readme_section :internal_dependencies, :draper
26
23
  create_file 'app/decorators/.keep'
27
24
  end
28
-
29
- def add_api_responder
30
- after(:gem_install) do
31
- copy_file '../assets/api/draper_responder.rb', 'app/responders/api_responder.rb', force: true
32
- end
33
- end
34
25
  end
@@ -0,0 +1,66 @@
1
+ class Recipes::FileStorage < Rails::AppBuilder
2
+ def ask
3
+ storages = {
4
+ active_storage: 'ActiveStorage',
5
+ shrine: 'Shrine',
6
+ none: 'None, thanks'
7
+ }
8
+
9
+ storage = answer(:storage) do
10
+ storages.keys[Ask.list('Which storage are you going to use?', storages.values)]
11
+ end
12
+
13
+ set(:storage, storage.to_sym)
14
+ end
15
+
16
+ def create
17
+ add_chosen_storage
18
+ end
19
+
20
+ def install
21
+ ask
22
+ add_chosen_storage
23
+ end
24
+
25
+ def installed?
26
+ file_exist?('config/storage.yml')
27
+ end
28
+
29
+ private
30
+
31
+ def add_active_storage
32
+ after(:gem_install) { run('bundle exec rails active_storage:install') }
33
+ copy_file('../assets/config/storage.yml', 'config/storage.yml', force: true)
34
+ active_storage_service_regexp = /config.active_storage.service = :local\n/
35
+ gsub_file 'config/environments/production.rb', active_storage_service_regexp do
36
+ 'config.active_storage.service = :amazon'
37
+ end
38
+ end
39
+
40
+ def add_shrine
41
+ gather_gem('shrine', '~> 3.0')
42
+ gather_gem('marcel', '~> 0.3.3')
43
+ copy_file('../assets/config/shrine.rb', 'config/initializers/shrine.rb', force: true)
44
+ copy_file('../assets/app/uploaders/image_uploader.rb', 'app/uploaders/image_uploader.rb')
45
+ copy_file('../assets/app/uploaders/base_uploader.rb', 'app/uploaders/base_uploader.rb')
46
+ append_to_file('.gitignore', "/public/uploads\n")
47
+ end
48
+
49
+ def common_setup
50
+ gather_gem 'aws-sdk-s3', '~> 1.0'
51
+ add_readme_section :internal_dependencies, get(:storage)
52
+ append_to_file '.env.development', "S3_BUCKET=\n"
53
+ end
54
+
55
+ def add_chosen_storage
56
+ return if [:none, :None].include? get(:storage).to_sym
57
+
58
+ common_setup
59
+ case get(:storage)
60
+ when :active_storage
61
+ add_active_storage
62
+ when :shrine
63
+ add_shrine
64
+ end
65
+ end
66
+ end
@@ -17,22 +17,22 @@ class Recipes::FrontEnd < Rails::AppBuilder
17
17
  def create
18
18
  return if [:none, :None].include? get(:front_end).to_sym
19
19
 
20
- gather_gem 'webpacker', github: 'rails/webpacker'
21
-
20
+ recipe = self
22
21
  after(:gem_install) do
23
22
  value = get(:front_end)
24
23
  run "rails webpacker:install"
25
24
  run "rails webpacker:install:#{value}" if value
26
25
 
27
26
  if value == :vue
28
- application_js_file = "app/javascript/packs/application.js"
29
- FileUtils.move "app/javascript/packs/hello_vue.js", application_js_file
30
- gsub_file application_js_file, %r{\/\/.*\n}, ""
31
-
32
- js_pack_tag = "\n <%= javascript_pack_tag 'application' %>\n"
33
- layout_file = "app/views/layouts/application.html.erb"
34
- insert_into_file layout_file, js_pack_tag, after: "<%= csrf_meta_tags %>"
27
+ recipe.setup_vue_with_compiler_build
28
+ recipe.setup_jest
29
+ if get(:api) == :graphql
30
+ recipe.setup_apollo
31
+ end
35
32
  end
33
+ recipe.add_responsive_meta_tag
34
+ recipe.setup_tailwind
35
+ add_readme_header :webpack
36
36
  end
37
37
  end
38
38
 
@@ -48,6 +48,65 @@ class Recipes::FrontEnd < Rails::AppBuilder
48
48
  package_content.include?("\"@angular/core\"") || package_content.include?("\"vue\"")
49
49
  end
50
50
 
51
+ def setup_vue_with_compiler_build
52
+ application_js = 'app/javascript/packs/application.js'
53
+ remove_file "app/javascript/packs/hello_vue.js"
54
+ create_file application_js, application_js_content, force: true
55
+
56
+ layout_file = "app/views/layouts/application.html.erb"
57
+ insert_into_file(
58
+ layout_file,
59
+ "<div id=\"vue-app\">\n <app></app>\n ",
60
+ before: "<%= yield %>"
61
+ )
62
+ insert_into_file layout_file, "\n </div>", after: "<%= yield %>"
63
+ end
64
+
65
+ def add_responsive_meta_tag
66
+ tag = "\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
67
+ layout_file = "app/views/layouts/application.html.erb"
68
+ insert_into_file layout_file, tag, after: "<%= csrf_meta_tags %>"
69
+ end
70
+
71
+ def setup_tailwind
72
+ run 'bin/yarn add tailwindcss'
73
+ setup_client_css
74
+ remove_server_css_requires
75
+ setup_tailwind_requirements
76
+ end
77
+
78
+ def setup_jest
79
+ run 'bin/yarn add jest vue-jest babel-jest @vue/test-utils jest-serializer-vue babel-core@^7.0.0-bridge.0 --dev'
80
+ json_file = File.read(Pathname.new("package.json"))
81
+ js_package = JSON.parse(json_file)
82
+ js_package = js_package.merge(jest_config)
83
+ json_string = JSON.pretty_generate(js_package)
84
+ create_file 'package.json', json_string, force: true
85
+
86
+ copy_file '../assets/app/javascript/app.spec.js', 'app/javascript/app.spec.js'
87
+ end
88
+
89
+ def setup_apollo
90
+ run 'bin/yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag'
91
+
92
+ inject_into_file(
93
+ 'app/javascript/packs/application.js',
94
+ apollo_imports,
95
+ after: "import App from '../app.vue';"
96
+ )
97
+
98
+ inject_into_file(
99
+ 'app/javascript/packs/application.js',
100
+ apollo_loading,
101
+ after: "import VueApollo from 'vue-apollo';"
102
+ )
103
+ inject_into_file(
104
+ 'app/javascript/packs/application.js',
105
+ "\n apolloProvider,",
106
+ after: "components: { App },"
107
+ )
108
+ end
109
+
51
110
  private
52
111
 
53
112
  def frameworks(framework)
@@ -58,4 +117,155 @@ class Recipes::FrontEnd < Rails::AppBuilder
58
117
  }
59
118
  frameworks[framework]
60
119
  end
120
+
121
+ def apollo_imports
122
+ <<~JS
123
+ \n
124
+ import { ApolloClient } from 'apollo-client';
125
+ import { createHttpLink } from 'apollo-link-http';
126
+ import { InMemoryCache } from 'apollo-cache-inmemory';
127
+ import VueApollo from 'vue-apollo';
128
+ JS
129
+ end
130
+
131
+ def apollo_loading
132
+ <<~JS
133
+ \n
134
+ const httpLink = createHttpLink({
135
+ uri: `${window.location.origin}/graphql`,
136
+ })
137
+ const cache = new InMemoryCache()
138
+ const apolloClient = new ApolloClient({
139
+ link: httpLink,
140
+ cache,
141
+ })
142
+
143
+ Vue.use(VueApollo)
144
+ const apolloProvider = new VueApollo({
145
+ defaultClient: apolloClient,
146
+ })
147
+ JS
148
+ end
149
+
150
+ def setup_client_css
151
+ application_css = 'app/javascript/css/application.css'
152
+ create_file application_css, "", force: true
153
+
154
+ stylesheet_pack_tag = "\n <%= stylesheet_pack_tag 'application' %>\n "
155
+ layout_file = "app/views/layouts/application.html.erb"
156
+ insert_into_file layout_file, stylesheet_pack_tag, before: "</head>"
157
+
158
+ application_js = 'app/javascript/packs/application.js'
159
+ if get(:front_end) != :vue
160
+ create_file application_js, "import '../css/application.css';\n", force: true
161
+ else
162
+ insert_into_file(
163
+ application_js,
164
+ "\nimport '../css/application.css';",
165
+ after: "import App from '../app.vue';"
166
+ )
167
+ end
168
+ end
169
+
170
+ def setup_tailwind_requirements
171
+ application_css = 'app/javascript/css/application.css'
172
+ insert_into_file application_css, tailwind_client_css
173
+
174
+ tailwind_config = 'tailwind.config.js'
175
+ create_file tailwind_config, tailwind_config_content, force: true
176
+
177
+ postcss_file = 'postcss.config.js'
178
+ insert_into_file postcss_file, postcss_require_tailwind, after: "plugins: [\n"
179
+ end
180
+
181
+ def remove_server_css_requires
182
+ assets_css_file = 'app/assets/stylesheets/application.css'
183
+ gsub_file(assets_css_file, " *= require_tree .\n *= require_self\n", "")
184
+ end
185
+
186
+ def application_js_content
187
+ <<~JS
188
+ import Vue from 'vue/dist/vue.esm';
189
+ import App from '../app.vue';
190
+
191
+ document.addEventListener('DOMContentLoaded', () => {
192
+ const app = new Vue({
193
+ el: '#vue-app',
194
+ components: { App },
195
+ });
196
+
197
+ return app;
198
+ });
199
+ JS
200
+ end
201
+
202
+ def tailwind_client_css
203
+ <<~CSS
204
+ @import 'tailwindcss/base';
205
+ @import 'tailwindcss/components';
206
+ @import 'tailwindcss/utilities';
207
+ CSS
208
+ end
209
+
210
+ def tailwind_config_content
211
+ <<~JS
212
+ /* eslint-disable no-undef */
213
+ module.exports = {
214
+ theme: {
215
+ extend: {},
216
+ },
217
+ variants: {},
218
+ plugins: [],
219
+ purge: {
220
+ enabled: process.env.NODE_ENV === 'production',
221
+ content: [
222
+ './app/**/*.html',
223
+ './app/**/*.vue',
224
+ './app/**/*.js',
225
+ './app/**/*.erb',
226
+ ],
227
+ }
228
+ };
229
+ JS
230
+ end
231
+
232
+ def postcss_require_tailwind
233
+ <<-JS.gsub(/^ {4}/, ' ')
234
+ require('tailwindcss'),
235
+ require('autoprefixer'),
236
+ JS
237
+ end
238
+
239
+ def jest_config
240
+ {
241
+ "scripts": {
242
+ "test": "jest",
243
+ "test:watch": "jest --watch"
244
+ },
245
+ "jest": {
246
+ "roots": [
247
+ "app/javascript"
248
+ ],
249
+ "moduleDirectories": [
250
+ "node_modules",
251
+ "app/javascript"
252
+ ],
253
+ "moduleNameMapper": {
254
+ "^@/(.*)$": "app/javascript/$1"
255
+ },
256
+ "moduleFileExtensions": [
257
+ "js",
258
+ "json",
259
+ "vue"
260
+ ],
261
+ "transform": {
262
+ "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
263
+ ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
264
+ },
265
+ "snapshotSerializers": [
266
+ "<rootDir>/node_modules/jest-serializer-vue"
267
+ ]
268
+ }
269
+ }
270
+ end
61
271
  end