Skip to content

Commit c7ac603

Browse files
committed
WIP
1 parent ba8ca43 commit c7ac603

File tree

17 files changed

+647
-245
lines changed

17 files changed

+647
-245
lines changed

README.md

Lines changed: 194 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# VueCli::Rails
22

3-
Get `vue-cli` working on Rails.
4-
5-
Currently, I am still actively developing this gem. It's usable but lacks of documentation. I will update it once the main features are ready and well-tested.
3+
Rails is cool. Vue is a cool boy too. Let's make them even cooler!
64

75
## Installation
86

@@ -17,23 +15,206 @@ And then execute:
1715
$ bundle install
1816
$ bundle exec rake vue:create
1917

20-
Follow the steps and copy demo codes, than you can add lines below to your `config/routes.rb`:
18+
Follow the steps and copy demo codes.
2119

22-
```ruby
23-
get 'vue/foo' => 'vue#foo'
24-
get 'vue/bar' => 'vue#bar'
25-
```
20+
## Requirements
21+
22+
- Ruby: 2.3.8, 2.4.5, 2.5.3, 2.6.1
23+
- Rails: 4.2.11, 5.2,2, 6.0beta2
24+
- Node: [8.9+](https://cli.vuejs.org/guide/installation.html)
25+
26+
## Features
27+
28+
- Feel free to use `npm` or `yarn`
29+
- Full features of [@vue/cli](https://cli.vuejs.org/)
30+
- Run `webpack-dev-server` together with Rails dev server
31+
- Just use `RAILS_ENV`. Get rid of `NODE_ENV`.
2632

2733
## Usage
2834

29-
This gem is fully depends on `vue-cli`. You can do anything with [`vue.config.js`](https://cli.vuejs.org/config/) just don't break `manifest` plugin which required by `vue_cli-rails`.
35+
- `rake vue:create`
36+
37+
Install required packages and configurations.
38+
39+
1. Select package manager: Y=`yarn`, N=`npm`
40+
41+
- Directly use npm if yarn has not been installed.
42+
- Prefer yarn by default unless detect `package-lock.json`
43+
44+
2. Auto install `@vue/cli` globally
45+
3. Invoke `vue create` to initialize Vue project
46+
47+
When detected `package.json`
48+
- Y (Yes): Fully overwrite
49+
- N (No): Skip
50+
- A (Auto): You won't loss anything (`old_config.merge(new_config)`)
51+
- K (Keep): Keep all your settings already have (`new_config.merge(old_config)`)
52+
53+
4. Install `js-yaml` and `webpack-assets-manifest`
54+
5. Deleting Vue demo code under `src` folder
55+
6. Copy demo code to under `app` folder and update `config/routes.rb`
56+
7. Copy `vue.rails.js` and `vue.config.js`
57+
58+
- Do not change `vue.rails.js`! This rake task will always restore `vue.rails.js` back.
59+
- Yes you can update `vue.config.js`. Just make sure you know what are you won't break the configuration. You can chance `config/vue.yml` instead.
60+
61+
8. Generate `config/vue.yml`
62+
63+
- The convention is: `camelCase` for regular `vue.config.js`, `snake_case` for special usage.
64+
- You can find a full list of [Vue CLI config options below](#valid-vue-cli-config-options).
65+
- And special options [here](#special-options)
66+
67+
- Files structure
68+
69+
Put ONLY your entry-point files under `app/assets/vue/views` folder. Entry-point is a `.js` file. Webpack sees JavaScript files as the center of entry-point rather than HTML. Thus all style files, images, fonts or other assets are related with JS files. This gem will find all `.js` and pass them as entry-points to webpack.
70+
71+
You may have interest of path alias in `config/vue.yml`.
72+
73+
- Helper `vue_entry`
74+
75+
It works like `javascript_include_tag`, or combination of `stylesheet_pack_tag` and `javascript_packs_with_chunks_tag` in Webpacker. I will explain why only one tag rather than 3 tags.
76+
77+
A typical usage will be like:
78+
79+
```html
80+
<!-- file - app/views/layout/vue.html.erb -->
81+
<!DOCTYPE html>
82+
<html>
83+
<head>
84+
<title>Vue</title>
85+
<%= csrf_meta_tags %>
86+
</head>
87+
<body>
88+
<div id="app"></div><%= yield %>
89+
</body>
90+
</html>
91+
```
92+
93+
```haml
94+
-# file - app/views/views/foo.html.erb
95+
<%= vue_entry('foo') %>
96+
```
97+
98+
```js
99+
// file - app/assets/vue/views/foo.js
100+
import Vue from 'vue';
101+
102+
import Foo from '~views/Foo.vue';
103+
104+
new Vue({
105+
render: h => h(Foo),
106+
}).$mount('#app');
107+
```
108+
109+
Suppose `~views: app/assets/vue/components/views` is configured in `config/vue.yml`
110+
111+
```js
112+
// file - app/assets/vue/views/foo.js
113+
import Vue from 'vue';
114+
115+
import Foo from '~/Foo.vue';
116+
117+
new Vue({
118+
render: h => h(Foo),
119+
}).$mount('#app');
120+
```
121+
122+
```vue
123+
# file - app/assets/vue/components/views/Foo.vue
124+
<template>
125+
<div id="foo">
126+
<h1>Foo</h1>
127+
</div>
128+
</template>
129+
130+
<script>
131+
export default {
132+
name: 'Foo',
133+
};
134+
</script>
135+
136+
<style scoped>
137+
#foo {
138+
color: green;
139+
}
140+
</style>
141+
```
142+
143+
- `rake rake vue:compile`
144+
145+
Compile Vue assets. Please specify `RAILS_ENV=production` to compile assets for production.
146+
147+
However, you can invoke `vue-cli-service build` (if `vue-cli-service` installed globally, or you can use `npx vue-cli-service build` or `yarn exec vue-cli-service build`) with `RAILS_ENV=production` to build assets.
148+
149+
> A good practice is to use [`cross-env`](https://www.npmjs.com/package/cross-env) to pass `RAILS_ENV=production`. So `cross-env RAILS_ENV=production vue-cli-service build` will work on any platform and shell.
150+
151+
- `rake vue:json_config`
152+
153+
Converts `config/vue.yml` to JSON to be used by `vue.rails.js`.
154+
155+
`vue.rails.js` prefers parsing `config/vue.yml` with `js-yaml`. So this is just in case. You may suffer performance issue when your Rails app grow big.
156+
157+
- `rake vue:support[formats]`
158+
159+
Adds template or style language support. Vue ships with supporting `pug`, `sass`, `less` and `stylus` out-of-box. How ever, you still have to install some loaders manually if you did not select that language with `vue:create`.
160+
161+
You can add multiple languages at the same time: `rake vue:support[pug,stylus]`
162+
163+
> You may need to invoke with `bundle exec rake vue:...`. Rails 5 and above also supports `rails vue:...`.
164+
165+
## Special Options
166+
167+
### `manifest_output`
168+
169+
Where to put `manifest.json` which required by Rails.
170+
171+
All entry-points will be compiled into assets files. Rails needs `manifest.json` to know what are the files and will serve all its JS/CSS tags.
172+
173+
### `package_manager`
174+
175+
Pretty straightforward, which package manager will be used. Valid value: `npm` or `yarn`. It does NOT support `pnpm` or other package managers. You can find the reason in [Q&A](#Q&A).
176+
177+
### `public_output_path`
178+
179+
This is
180+
181+
## Valid Vue CLI config Options
182+
183+
You can check the full list on [Vue CLI website](https://cli.vuejs.org/config/).
184+
185+
- Special
186+
187+
- publicPath - see [`public_output_path`](#public_output_path)
188+
- outputDir - see [`public_output_path`](#public_output_path)
189+
190+
- Supported
191+
192+
- [x] filenameHashing
193+
- [x] lintOnSave
194+
- [x] runtimeCompiler
195+
- [x] transpileDependencies
196+
- [x] productionSourceMap
197+
- [x] crossorigin
198+
- [x] css
199+
- [x] devServer
200+
- [x] parallel
201+
- [x] pwa
202+
- [x] pluginOptions
203+
204+
- Unsupported
30205

31-
When you starting `rails server` with development mode, `vue-cli-service serve` will be running at the same time.
206+
- [ ] baseUrl - Deprecated
207+
- [ ] assetsDir - ignored
208+
- [ ] indexPath - N/A
209+
- [ ] pages - N/A
210+
- [ ] integrity - N/A
211+
- [ ] configureWebpack - directly edit `vue.config.js`
212+
- [ ] chainWebpack - directly edit `vue.config.js`
32213

33-
Please use `RAILS_ENV=production` to build your production assets. `NODE_ENV` will be ignored!
214+
## Q&A
34215

35-
You can put `app/assets/vue/manifest.dev.json` into your VCS ignore list.
216+
- Why not webpacker?
36217

37-
## Warning
218+
1. WIP
38219

39220
Currently `vue.config.js` is reading configurations from `vue.rails.js` which depends on `js-yaml`. It will fallback to `bundle exec rake vue:json_config` without `js-yaml` installed. You may suffer performance issue if your rake tasks are slow.

lib/helpers/lib.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
require_relative 'lib/common'
2+
require_relative 'lib/cmd'
3+
require_relative 'lib/input_loop'

lib/helpers/lib/cmd.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Cmd
2+
def self.run(cmd)
3+
puts cmd
4+
system(cmd)
5+
end
6+
end

lib/helpers/lib/common.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
unless Object.instance_methods.include?(:blank?)
2+
class Object
3+
def blank?
4+
respond_to?(:empty?) ? !!empty? : !self
5+
end
6+
end
7+
end

lib/helpers/lib/input_loop.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class InputLoop
2+
DEFAULT_HINT = { 'y' => 'Yes', 'n' => 'No' }.freeze
3+
4+
def gets(message, list = 'Yn', **hint)
5+
default, list = build_list(list)
6+
keys_hint, hint = build_hint(list, hint)
7+
8+
print "#{message} (#{hint}) #{keys_hint}"
9+
wait_valid_input(keys_hint, Set.new(list.map(&:downcase)), default)
10+
end
11+
12+
private
13+
14+
def wait_valid_input(keys_hint, keys, default)
15+
loop do
16+
r = STDIN.gets.chop.downcase
17+
break default if r == ''
18+
break r if keys.include?(r)
19+
20+
print " [INVALID!] Please retry: #{keys_hint}:"
21+
end
22+
end
23+
24+
def build_list(list)
25+
list = list.chars
26+
default = list.find { |c| c.upcase == c }
27+
[default.downcase, list.map { |c| c == default ? c : c.downcase }.uniq]
28+
end
29+
30+
def build_hint(list, hint)
31+
valid = "[#{list.join('')}]"
32+
hint = DEFAULT_HINT.merge(hint.map { |k, v| [k.to_s.downcase, v] }.to_h)
33+
hint = list.map do |c|
34+
h = hint[c.downcase]
35+
next if h.blank?
36+
37+
"#{c.upcase}=#{h}"
38+
end
39+
[valid, hint.join(', ')]
40+
end
41+
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env ruby
2+
require_relative 'common'
3+
4+
ver = ENV['RAILS_VERSION']
5+
abort('RAILS_VERSION not found!') if ver.blank?
6+
7+
versions = `gem list -r -a -e rails`.scan(/\b((\d+\.)+\d+)\b/).map { |m| m[0] }
8+
ver = versions.find { |v| v.start_with? ver }
9+
abort("Version #{ver} not found!") if ver.blank?
10+
11+
run("gem install rails -v #{ver}")
12+
13+
RAILS_NEW_SCRIPT = {
14+
'5' => {
15+
args: %w[
16+
database=sqlite3
17+
skip-yarn
18+
skip-git
19+
skip-keeps
20+
skip-sprockets
21+
skip-spring
22+
skip-listen
23+
skip-turbolinks
24+
skip-javascript
25+
skip-test
26+
skip-bundle
27+
],
28+
keep_gems: /^\s*gem\s+['"](rails|puma|bootsnap)/,
29+
append: <<~RUBY
30+
gem 'sqlite3', '~> 1.3.10'
31+
RUBY
32+
},
33+
}
34+
35+
require 'pathname'
36+
require 'fileutils'
37+
38+
scirpt = RAILS_NEW_SCRIPT[ver[0]]
39+
run "rails new test_vcr #{scirpt[:args].map { |a| "--#{a}" }.join(' ')}"
40+
FileUtils.chdir 'test_vcr'
41+
42+
root = Pathname.new(FileUtils.pwd)
43+
gemfile = root.join('Gemfile').read.split("\n")
44+
.reject(&:empty?).reject { |s| s =~ /^\s*#/ }
45+
.reject { |s| s =~ /^\s*gem/ && s !~ scirpt[:keep_gems] }
46+
root.join('Gemfile').write("#{(gemfile + [scirpt[:append]]).join("\n")}\n")
47+
run 'bundle install'
48+
# rails new test_vcr --database=sqlite3 --skip-yarn --skip-git --skip-sprockets --skip-spring --skip-listen --skip-turbolinks --skip-javascript --skip-test --skip-bundle

0 commit comments

Comments
 (0)