Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: introduce poc cli #45

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,71 @@ Cached if `NODE_ENV=production`. Takes the following options:
Return a `js` stream. Uses `watchify` for incremental builds if
`NODE_ENV=development`. `src` is the bundle entry file. Cached by default.

## CLI

```
$ bankai --help

DIY asset server

Usage
$ bankai <command> [options]

Commands
start Start a bankai server

Options
-e, --entry=<id> Resolve <id> from cwd and use as entry module [default: .]
Entry module is expected to export `() -> app`
-p, --port=<n> Bind bankai to <n> [default: 1337]
-o, --open=<app> Open the page served by bankai with <app> [default: false]
--html.entry=<uri> Serve client js at <uri> [default: bundle.js]
--html.css=<uri> Serve client css at <uri> [default: bundle.css]
--html.favicon Disable favicon [default: true]
--html.title Title to use for page
--html.lang Lang attribute to use [default: en]
--css.use sheetify plugins to use
--js.<opt>=<value> Pass key <opt> with <value> to browserify

Examples
$ bankai start
Started bankai for index.js on http://localhost:1337

$ bankai start --entry=basic
Started bankai fro basic/index.js on http://localhost:1337

$ bankai start --port=3000
Started bankai for index.js on http://localhost:3000

$ bankai start --open
Started bankai for index.js on http://localhost:1337
Opening http://localhost:1337 with default browser

$ bankai start --open Safari
Started bankai for index.js on http://localhost:1337
Opening http://localhost:1337 with system browser

$ bankai start --html.title bankai
Started bankai for index.js on http://localhost:1337

$ bankai start --css.use sheetify-cssnext
Started bankai for index.js on http://localhost:1337

$ bankai start --js.fullPaths=false
```

## Examples
Projects showing exemplary usage are provided. Install root project dependencies,
example project dependencies and execute `npm start` to start an example.

- [Basic](./example/basic) - Showing default settings, Hot Module Replacement
```
npm install
cd example/basic
npm install
npm start
```

## See Also
- [budo](https://www.npmjs.com/package/budo)
- [tiny-lr](https://github.com/mklabs/tiny-lr)
Expand Down
19 changes: 19 additions & 0 deletions bin/client-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function main () {
var el = document.querySelector('[data-bankai]')
var id = el.dataset.bankai
var application = require(id)

var app = application(window.__BANKAI_GLOBAL_STATE_HOOK__, {
onStateChange: function (data, state) {
window.__BANKAI_GLOBAL_STATE_HOOK__ = state
}
})

var tree = app.start('[data-bankai="' + id + '"]')

if (tree) {
document.body.appendChild(tree)
}
}

main()
44 changes: 44 additions & 0 deletions bin/help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Usage
$ bankai <command> [options]

Commands
start Start a bankai server

Options
-e, --entry=<id> Resolve <id> from cwd and use as entry module [default: .]
Entry module is expected to export `() -> app`
-p, --port=<n> Bind bankai to <n> [default: 1337]
-o, --open=<app> Open the page served by bankai with <app> [default: false]
--html.entry=<uri> Serve client js at <uri> [default: bundle.js]
--html.css=<uri> Serve client css at <uri> [default: bundle.css]
--html.favicon Disable favicon [default: true]
--html.title Title to use for page
--html.lang Lang attribute to use [default: en]
--css.use sheetify plugins to use
--js.<opt>=<value> Pass key <opt> with <value> to browserify

Examples
$ bankai start
Started bankai for index.js on http://localhost:1337

$ bankai start --entry=basic
Started bankai fro basic/index.js on http://localhost:1337

$ bankai start --port=3000
Started bankai for index.js on http://localhost:3000

$ bankai start --open
Started bankai for index.js on http://localhost:1337
Opening http://localhost:1337 with default browser

$ bankai start --open Safari
Started bankai for index.js on http://localhost:1337
Opening http://localhost:1337 with system browser

$ bankai start --html.title bankai
Started bankai for index.js on http://localhost:1337

$ bankai start --css.use sheetify-cssnext
Started bankai for index.js on http://localhost:1337

$ bankai start --js.fullPaths=false
102 changes: 102 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env node
'use strict'

const fs = require('fs')
const path = require('path')
const meow = require('meow')

const commands = {
start: require('./start')
}

const commandNames = Object.keys(commands)
const commandList = commandNames.join(', ')
const help = fs.readFileSync(path.resolve(__dirname, 'help.md'), 'utf-8')
const unknowns = []
const alias = {
entry: ['e'],
open: ['o'],
port: ['p']
}

const cli = meow(help, {
alias: alias,
string: [
'entry',
'html.entry',
'html.css',
'html.title',
'css.use',
'js.noParse',
'js.transform',
'js.ignoreTransform',
'js.plugin',
'js.extensions',
'js.basedir',
'js.paths',
'js.commondir',
'js.builtins',
'js.bundleExternal',
'js.browserField',
'js.insertGlobals',
'js.standalone',
'js.externalRequireName'
],
boolean: [
'js.fullPaths',
'js.debug'
],
unknown: function (flag) {
if (flag in commands) {
return
}
unknowns.push(flag)
}
})

const aliasNames = Object.keys(alias)
.reduce(function (r, i) {
return r.concat(alias[i])
}, [])

function main (commandName, options, cb) {
let error

if (typeof commandName !== 'string') {
error = new Error('Missing command parameter. Available commands: ' + commandList)
error.cli = true
return cb(error)
}

if ((commandName in commands) === false) {
error = new Error('Unknown command ' + commandName + '.', 'Available commands: ' + commandList)
error.cli = true
return cb(error)
}

if (unknowns.length > 0) {
error = new Error('Unkown flags detected: ' + unknowns.join(', '))
error.cli = true
return cb(error)
}

// Remove short hand pointers
aliasNames.forEach(function (aliasName) {
delete options[aliasName]
})

const command = commands[commandName]
command(options, cb)
}

main(cli.input[0], cli.flags, function (error) {
if (error) {
if (error.cli) {
console.error(cli.help)
console.error('')
console.error(error.message)
return process.exit(1)
}
throw error
}
})
143 changes: 143 additions & 0 deletions bin/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const http = require('http')
const path = require('path')
const bankai = require('../')
const browserify = require('browserify')
const getServerPort = require('get-server-port')
const hyperstream = require('hyperstream')
const opn = require('opn')
const projectNameGenerator = require('project-name-generator')
const resolve = require('resolve')
const serverRouter = require('server-router')
const stringToStream = require('string-to-stream')
const stream = require('stream')
const xtend = require('xtend')

const defaults = {
port: 1337,
open: false,
entry: '.',
html: {},
css: {},
js: {}
}

const cwd = process.cwd()

// resolve a path according to require.resolve algorithm
// string -> string
function resolveEntryFile (relativePath) {
const entry = relativePath[0] === '.' || relativePath[0] === '/'
? relativePath
: './' + relativePath
return resolve.sync(entry, {basedir: cwd})
}

function injectScript () {
const b = browserify(path.resolve(__dirname, 'client-start.js'))
const script$ = new stream.PassThrough()
script$.push('<script>')

b.on('bundle', function (bundle$) {
bundle$.pipe(script$)
})

b.bundle(function (error) {
if (error) {
console.error(error)
}
script$.end('</script>')
})

return hyperstream({
body: { _appendHtml: script$ }
})
}

function injectContent (content, id) {
const addId = hyperstream({
'*:first': {'data-bankai': id}
})
return hyperstream({
body: {
_prependHtml: content.pipe(addId)
}
})
}

// get a html request handler for settings, entryFile and id
// (object, string, string) -> rstream
function getHtmlHandler (htmlSettings, entryFile, id) {
const entryModule = require(entryFile)

const entryApp = entryModule()
const layout = bankai.html(htmlSettings)

return function (req, res) {
const content = stringToStream(entryApp.toString(req.url))

return layout(req, res)
.pipe(injectContent(content, id))
.pipe(injectScript())
}
}

// Start a development server
function start (options, cb) {
const settings = xtend({}, defaults, options)
const callback = cb || function () {}

const entryFile = resolveEntryFile(settings.entry)
const relativeEntry = path.relative(cwd, entryFile)
const router = serverRouter('/404')
const id = ['bankai'].concat(projectNameGenerator().raw).join('-')

router.on('/404', function (req, res) {
res.statusCode = 404
return stringToStream('Not found')
})

if (settings.html) {
const htmlOpts = xtend({id: id}, settings.html)
const html = getHtmlHandler(htmlOpts, entryFile, id)
router.on('/:path', html)
router.on('/', html)
}

if (settings.css) {
const css = bankai.css(settings.css)
router.on(settings.html.css || '/bundle.css', css)
}

const jsOpts = xtend({id: id}, settings.js)
const js = bankai.js(browserify, entryFile, jsOpts)
router.on(settings.html.entry || '/bundle.js', js)

const server = http.createServer(function (req, res) {
router(req, res).pipe(res)
})

server.listen(settings.port, function () {
const port = getServerPort(server)

const address = ['http://localhost', port].join(':')
console.log('Started bankai for', relativeEntry, 'on', address)

if (settings.open) {
const app = typeof settings.open === 'string' ? settings.open : null

const appName = typeof settings.open === 'string'
? settings.open
: 'system browser'
console.log('Opening', address, 'with', appName)

opn(address, {app: app})
.catch(function (error) {
console.error(error)
})
}

callback()
})
}

module.exports = start
Loading