Skip to content

Commit

Permalink
Decouple SPA client from base project
Browse files Browse the repository at this point in the history
  • Loading branch information
gilbert committed Dec 3, 2016
1 parent 9a61ea4 commit fd73e43
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 40 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ The code structure that Concatapult generates is opinionated without apology. Wi

`pult` supports adding several modules to your base project. For any of the following modules, you can run `pult add X`, where `X` is the name of the module.

### Module Dependency Tree Overview

- [api](#pult-add-api)
- [knex](#pult-add-knex)
- [knex-model](#pult-add-knex-model)
- [spa](#pult-add-spa)

### pult add knex

[Knex.js](http://knexjs.org/) is a solid library for constructing SQL queries for [many different SQL databases](https://github.com/tgriesser/knex/tree/master/src/dialects).
Expand Down
1 change: 0 additions & 1 deletion base-template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"dependencies": {
"bluebird": "^3.4.6",
"body-parser": "^1.15.2",
"browserify-middleware": "^7.1.0",
"express": "^4.14.0"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions base-template/server/config/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"routerPipeline": []
}
56 changes: 24 additions & 32 deletions base-template/server/index.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,36 @@
var browserify = require('browserify-middleware')
var express = require('express')
var Path = require('path')

var routes = express.Router()

//
// Provide a browserified file at a specified path
//
var vendorLibs = []

routes.get('/vendor-bundle.js', browserify(vendorLibs))

routes.get('/app-bundle.js', browserify('./client/index.js', {
external: vendorLibs
}))
var router = express.Router()

//
// Example endpoint (also tested in test/server/index_test.js)
//
routes.get('/api/tags-example', function(req, res) {
router.get('/api/tags-example', function(req, res) {
res.send(['node', 'express', 'browserify', 'mithril'])
})

//
// Static assets (html, etc.)
//
var assetFolder = Path.resolve(__dirname, '../client/public')
routes.use(express.static(assetFolder))

global.CONFIG = require('./config/index.json')

if (process.env.NODE_ENV !== 'test') {
//
// The Catch-all Route
// This is for supporting browser history pushstate.
// NOTE: Make sure this route is always LAST.
//
routes.get('/*', function(req, res){
res.sendFile( assetFolder + '/index.html' )
})
for (var item of CONFIG.routerPipeline) {
require(item).mount(router)
}

router.get('/', function (req, res) {
res.send(`
<h1>Welcome to Concatapult!</h1>
<p>
If you're seeing this, it's because you have not yet added a client.
If you're looking to build a <b>Singe Page App</b>, start by running
<code>pult add spa</code>
in your terminal.
</p>
<p>For more information, visit
<a href="https://github.com/Concatapult/pult" target="_blank">the docs</a>.</p>
<p>Happy pulting!</p>
`)
})

if (process.env.NODE_ENV !== 'test') {
//
// We're in development or production mode;
// create and run a real server.
Expand All @@ -49,7 +41,7 @@ if (process.env.NODE_ENV !== 'test') {
app.use( require('body-parser').json() )

// Mount our main router
app.use('/', routes)
app.use('/', router)

// Start the server!
var port = process.env.PORT || 4000
Expand All @@ -58,5 +50,5 @@ if (process.env.NODE_ENV !== 'test') {
}
else {
// We're in test mode; make this file importable instead.
module.exports = routes
module.exports = router
}
10 changes: 9 additions & 1 deletion commands/add-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ module.exports = function addModule (vfs, baseConfig, moduleName, moduleArgs) {
})
}

vfs.write( baseConfig.projectRoot + '/package.json', JSON.stringify(newPackage, null, ' ') + '\n' )
if ( moduleConfig.server ) {
writeJSON(vfs, baseConfig.projectRoot + '/server/config/index.json', moduleConfig.server)
}

writeJSON(vfs, baseConfig.projectRoot + '/package.json', newPackage)

var totalConfig = Object.assign({}, baseConfig, moduleConfig)
vfs.copyTpl( $(`modules/${moduleName}/template/{**,.*}`), baseConfig.projectRoot, totalConfig )
Expand Down Expand Up @@ -73,3 +77,7 @@ function mapValues (obj, fn) {
}
return result
}

function writeJSON(vfs, path, value) {
vfs.write( path, JSON.stringify(value, null, ' ') + '\n' )
}
5 changes: 2 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ program
// Wrap everything in co to easily catch errors
co(function * () {

var package = require( Path.resolve(process.cwd(), 'package.json'))

var config = {
package: package,
package: require( Path.resolve(process.cwd(), 'package.json')),
projectRoot: process.cwd(),
server: require( Path.resolve(process.cwd(), 'server/config/index.json')),
}

var result = require('./commands/add-module.js')(vfs, config, module, moduleArgs)
Expand Down
20 changes: 20 additions & 0 deletions lib/clone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Simple clone that only considers Objects and Arrays
//
module.exports = function clone (item) {
if ( Array.isArray(item) ) {
return item.slice().map(clone)
}
else if ( isObject(item) ) {
var cloned = {}
for (var prop in item) {
cloned[prop] = clone( item[prop] )
}
return cloned
}
else {
return item
}
}

var isObject = (item) => Object.prototype.toString.call(item)
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ exports.ejs = require('ejs')
exports.colors = require('./colors')
exports.c = require('./colors') // alias

exports.clone = require('./clone')


exports.fail = (message) => { throw new exports.errors.PultError(message) }
30 changes: 30 additions & 0 deletions modules/spa/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
var lib = require('../../lib')


module.exports = function configClient (vfs, baseConfig, moduleArgs) {
if ( moduleArgs.length >= 2 ) {
throw new lib.errors.ModuleError('Module `spa` only takes 1 argument')
}

var serverConfig = lib.clone(baseConfig.server)

serverConfig["spa"] = {
"browserify": {
"external": []
}
}

serverConfig.routerPipeline.unshift('./config/client-bundles.js')
serverConfig.routerPipeline.push('./config/catch-all-index-page.js')

var config = {
dependencies: {
"browserify-middleware": "^7.1.0",
},
server: serverConfig,
}

config.installs = Object.keys(config.dependencies)

return config
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
//
// See all options here: https://github.com/Concatapult/pult
//

document.querySelector('h1').innerHTML += ' (SPA style!)'
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<html>
<head>
<title><%= projectName %></title>
<title><%= package.name %></title>
</head>
<body>

<h1><%= projectName %></h1>
<p>It begins.</p>
<h1>Hello, <%= package.name %>!</h1>

<div id="app"></div>

Expand Down
14 changes: 14 additions & 0 deletions modules/spa/template/server/config/catch-all-index-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var Path = require('path')
var clientConfig = require('./client-bundles')


exports.mount = function clientBundleSetup (router) {
//
// The Catch-all Route
// This is for supporting browser history pushstate.
// NOTE: Make sure this route is always LAST in routerPipeline.
//
router.get('/*', function(req, res){
res.sendFile( Path.resolve(clientConfig.getAssetFolder(), './index.html') )
})
}
22 changes: 22 additions & 0 deletions modules/spa/template/server/config/client-bundles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
var express = require('express')
var browserify = require('browserify-middleware')
var Path = require('path')

var assetFolder = Path.resolve(__dirname, '../../client/public')


exports.mount = function clientBundleSetup (router) {
//
// Provide browserified files at specified paths
//
router.get('/vendor-bundle.js', browserify(CONFIG.spa.browserify.external))

router.get('/app-bundle.js', browserify('./client/index.js', CONFIG.spa.browserify))

//
// Static assets (html, etc.)
//
router.use(express.static(assetFolder))
}

exports.getAssetFolder = function () { return assetFolder }

0 comments on commit fd73e43

Please sign in to comment.