├── .gitignore
├── README.md
├── gulpfile.coffee
├── package.json
├── src
├── comp
│ ├── container.coffee
│ ├── counter.coffee
│ ├── home.coffee
│ ├── page.coffee
│ ├── panel.coffee
│ └── piece.vue
├── main.coffee
├── main.css
├── router.coffee
└── store.coffee
├── tasks
├── settings.coffee
└── template.coffee
├── webpack-build.config.coffee
├── webpack.config.coffee
└── webpack.server.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | /build
3 | /node_modules
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Vue CoffeeScript Workflow
3 | ----
4 |
5 | > Templating is tricky, use language based DSL.
6 |
7 | This project demonstrates writing Vue.js 2 in plain CoffeeScript.
8 | The goal is to find an easy solution to render HTML during building.
9 |
10 | ### Usage
11 |
12 | * Develop
13 |
14 | ```bash
15 | gulp ssr
16 | webpack-dev-server --hot --inline
17 | ```
18 |
19 | Open `build/index.html` to debug.
20 |
21 | There are actually 3 entries, and I'm trying to make it isomorphic:
22 |
23 | ```text
24 | build/index.html
25 | build/page/a.html
26 | build/page/b.html
27 | ```
28 |
29 | * Build for production
30 |
31 | ```bash
32 | webpack --config webpack-build.config.coffee
33 | webpack --config webpack.server.coffee
34 | env=prod gulp ssr
35 | ```
36 |
37 | ### TODO
38 |
39 | Still need to work on:
40 |
41 | * [x] build with revisions
42 | * [x] support `*.vue` files and build code
43 | * [ ] hot code swapping
44 |
45 | ### License
46 |
47 | MIT
48 |
--------------------------------------------------------------------------------
/gulpfile.coffee:
--------------------------------------------------------------------------------
1 |
2 | fs = require 'fs'
3 | gulp = require 'gulp'
4 | path = require 'path'
5 |
6 | settings = require './tasks/settings'
7 |
8 | gulp.task 'ssr', (cb) ->
9 | process.env.VUE_ENV = 'server'
10 | Vue = require 'vue'
11 | Vuex = require 'vuex'
12 | VueRouter = require 'vue-router'
13 | mkpath = require 'mkpath'
14 | # need to put it before store initialization
15 | Vue.use Vuex
16 | store = require './src/store'
17 | router = require './src/router'
18 | renderer = require('vue-server-renderer').createRenderer()
19 | # Container = require './src/comp/container'
20 | Container = require './build/container'
21 | template = require './tasks/template'
22 | Vue.use VueRouter
23 |
24 | # this is the initial address
25 | entries = [
26 | 'index.html'
27 | 'page/a.html'
28 | 'page/b.html'
29 | ]
30 | entries.forEach (address) ->
31 | app = new Vue
32 | router: router
33 | store: store
34 | components:
35 | container: Container
36 | render: (h) ->
37 | h 'container'
38 | router.push address
39 | renderer.renderToString app, (err, appHtml) ->
40 | if err?
41 | throw err
42 | else
43 | html = template.render appHtml, store.state, settings
44 | htmlPath = path.join 'build', address
45 | console.log 'render entry:', htmlPath
46 | mkpath.sync path.dirname(htmlPath)
47 | fs.writeFileSync htmlPath, html
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-coffee-workflow",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "jiyinyiyong",
10 | "license": "MIT",
11 | "dependencies": {
12 | "gulp": "^3.9.1",
13 | "vue": "^2.0.3",
14 | "vue-router": "^2.0.1",
15 | "vuex": "^2.0.0"
16 | },
17 | "devDependencies": {
18 | "coffee-loader": "^0.7.2",
19 | "coffee-script": "^1.11.1",
20 | "css-loader": "^0.25.0",
21 | "extract-text-webpack-plugin": "^1.0.1",
22 | "mkpath": "^1.0.0",
23 | "stir-template": "^0.1.6",
24 | "style-loader": "^0.13.1",
25 | "vue-loader": "^9.7.0",
26 | "vue-server-renderer": "^2.0.3",
27 | "webpack": "^1.13.2"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/comp/container.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 |
4 | Panel = require './panel'
5 | Counter = require './counter'
6 | Piece = require './piece.vue'
7 |
8 | module.exports = Vue.component 'container',
9 | props: {}
10 | components:
11 | panel: Panel
12 | counter: Counter
13 | piece: Piece
14 | render: (h) ->
15 | h 'div', attrs: {id: 'app'}, [
16 | h 'div', [
17 | 'app!'
18 | ]
19 | h 'panel'
20 | h 'counter'
21 | h 'router-view', class: 'view'
22 | h 'piece'
23 | ]
24 |
--------------------------------------------------------------------------------
/src/comp/counter.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 |
4 | module.exports = Vue.component 'counter',
5 | computed:
6 | count: ->
7 | @$store.state.count
8 | methods:
9 | onClick: (event) ->
10 | console.log @$store.dispatch('inc')
11 | render: (h) ->
12 | h 'div', [
13 | 'counter:'
14 | JSON.stringify(@count)
15 | h 'div', on: {click: @onClick}, [
16 | 'add '
17 | ]
18 | ]
19 |
--------------------------------------------------------------------------------
/src/comp/home.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 |
4 | module.exports = Vue.component 'home',
5 | props: {}
6 | render: (h) ->
7 | h 'div', attrs: {}, [
8 | 'routed page home'
9 | ]
10 |
--------------------------------------------------------------------------------
/src/comp/page.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 |
4 | module.exports = Vue.component 'home',
5 | props: {}
6 | render: (h) ->
7 | h 'div', attrs: {}, [
8 | "routed page #{JSON.stringify(this.$route.params)}"
9 | ]
10 |
--------------------------------------------------------------------------------
/src/comp/panel.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 |
4 | module.exports = Vue.component 'home',
5 | props: {}
6 | render: (h) ->
7 | h 'div', attrs: {}, [
8 | h 'div', [
9 | h 'router-link', props: {to: '/index.html'}, ['/index.html']
10 | ]
11 | h 'div', [
12 | h 'router-link', props: {to: '/page/a.html'}, ['/page/a.html']
13 | ]
14 | h 'div', [
15 | h 'router-link', props: {to: '/page/b.html'}, ['/page/b.html']
16 | ]
17 | ]
18 |
--------------------------------------------------------------------------------
/src/comp/piece.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | a piece in .vue files
4 |
5 |
6 |
15 |
--------------------------------------------------------------------------------
/src/main.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vue = require 'vue'
3 | Vuex = require 'vuex'
4 | VueRouter = require 'vue-router'
5 |
6 | Vue.use VueRouter
7 | Vue.use Vuex
8 |
9 | Container = require './comp/container'
10 | router = require './router'
11 | store = require './store'
12 |
13 | storeEl = document.querySelector('#store')
14 | if storeEl?
15 | rawData = storeEl.getAttribute('content')
16 | store.replaceState JSON.parse(rawData)
17 |
18 | console.log 'store:', store
19 |
20 | new Vue
21 | el: '#app'
22 | router: router
23 | store: store
24 | components:
25 | container: Container
26 | render: (h) ->
27 | h 'container', {}
28 |
--------------------------------------------------------------------------------
/src/main.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | background-color: hsl(200,80%,90%);
4 | }
5 |
--------------------------------------------------------------------------------
/src/router.coffee:
--------------------------------------------------------------------------------
1 |
2 | VueRouter = require 'vue-router'
3 |
4 | Home = require './comp/home'
5 | Page = require './comp/page'
6 |
7 | module.exports = new VueRouter
8 | mode: 'history'
9 | base: __dirname # what's this for?
10 | routes: [
11 | path: '/index.html', component: Home
12 | ,
13 | path: '/page/:x', component: Page
14 | ]
15 |
--------------------------------------------------------------------------------
/src/store.coffee:
--------------------------------------------------------------------------------
1 |
2 | Vuex = require 'vuex'
3 |
4 | state = count: 0
5 |
6 | mutations =
7 | increment: (state) ->
8 | state.count = state.count + 1
9 | decrement: (state) ->
10 | state.count = state.count - 1
11 |
12 | getters =
13 | count: (state) -> state.count
14 |
15 | actions =
16 | inc: ({commit}) ->
17 | commit 'increment'
18 | dec: ({commit}) ->
19 | commit 'increment'
20 |
21 | module.exports = new Vuex.Store {state, mutations, actions, getters}
22 |
--------------------------------------------------------------------------------
/tasks/settings.coffee:
--------------------------------------------------------------------------------
1 |
2 | env = process.env.env
3 |
4 | module.exports = switch env
5 | when 'prod'
6 | assets = require '../build/assets.json'
7 | env: 'prod'
8 | mainJs: "/#{assets.main}"
9 | mainCss: "/#{assets.style[1]}"
10 | else
11 | env: 'dev'
12 | mainJs: 'http://localhost:8080/main.js'
13 | mainCss: 'http://localhost:8080/style.js'
14 |
--------------------------------------------------------------------------------
/tasks/template.coffee:
--------------------------------------------------------------------------------
1 |
2 | stir = require 'stir-template'
3 |
4 | {html, body, head, meta, div, link, script} = stir
5 |
6 | exports.render = (appHtml, storeData, settings) ->
7 | storeContent = JSON.stringify(storeData).replace(/"/g, '"')
8 | stir.render stir.doctype(),
9 | html null,
10 | head null,
11 | script defer: true, src: settings.mainJs
12 | if settings.env is 'prod'
13 | link rel: 'stylesheet', href: settings.mainCss
14 | else
15 | script defer: true, src: settings.mainCss
16 | if storeData?
17 | meta id: 'store', content: storeContent
18 | body null,
19 | if appHtml?
20 | appHtml
21 | else
22 | div id: 'app'
23 |
--------------------------------------------------------------------------------
/webpack-build.config.coffee:
--------------------------------------------------------------------------------
1 | fs = require 'fs'
2 | ExtractTextPlugin = require 'extract-text-webpack-plugin'
3 |
4 | module.exports =
5 | entry:
6 | style: './src/main.css'
7 | main: './src/main.coffee'
8 | output:
9 | path: 'build/'
10 | filename: '[name].[hash:8].js'
11 | module:
12 | loaders: [
13 | test: /\.coffee$/, loader: 'coffee-loader'
14 | ,
15 | test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
16 | ,
17 | test: /\.vue$/, loader: 'vue'
18 | ]
19 | resolve:
20 | extensions: ['', '.coffee', '.js']
21 | plugins: [
22 | new ExtractTextPlugin '[name].[hash:8].css'
23 | ,
24 | ->
25 | @plugin "done", (stats) ->
26 | content = JSON.stringify stats.toJson().assetsByChunkName
27 | fs.writeFileSync 'build/assets.json', content
28 | ]
29 |
--------------------------------------------------------------------------------
/webpack.config.coffee:
--------------------------------------------------------------------------------
1 |
2 | module.exports =
3 | entry:
4 | style: './src/main.css'
5 | main: './src/main.coffee'
6 | output:
7 | filename: '[name].js'
8 | module:
9 | loaders: [
10 | test: /\.coffee$/, loader: 'coffee-loader'
11 | ,
12 | test: /\.css$/, loader: 'style!css'
13 | ,
14 | test: /\.vue$/, loader: 'vue'
15 | ]
16 | resolve:
17 | extensions: ['', '.coffee', '.js']
18 |
--------------------------------------------------------------------------------
/webpack.server.coffee:
--------------------------------------------------------------------------------
1 |
2 | webpack = require 'webpack'
3 |
4 | module.exports =
5 | target: 'node'
6 | entry:
7 | container: './src/comp/container.coffee'
8 | output:
9 | path: 'build/'
10 | filename: 'container.js'
11 | libraryTarget: 'commonjs2'
12 | externals: Object.keys(require('./package.json').dependencies),
13 | module:
14 | loaders: [
15 | test: /\.coffee$/, loader: 'coffee-loader'
16 | ,
17 | test: /\.css$/, loader: 'style!css'
18 | ,
19 | test: /\.vue$/, loader: 'vue'
20 | ]
21 | resolve:
22 | extensions: ['', '.coffee', '.js']
23 | plugins: []
24 |
--------------------------------------------------------------------------------