# App Scale* JavaScript Or how to achieve server-client *synergy* in a single-language development environment. *Mind the buzzwords.
## Development Tools - Module Loading - `require()` in [node.js][] - [Require.js][] in the browser - Package Management - `npm` in [node.js][] - [Jam.js][] in the browser - `npm install` -> `jam install`. [node.js]: http://nodejs.org/ [Require.js]: http://requirejs.org/ [Jam.js]: http://jamjs.org/
## Development Tools - Runtime Dependencies - [Architect][] provides the concept of services and plugins. - Server-Client Communication - [Smith][] provides a duplex rpc channel. [Architect]: https://github.com/c9/architect [Smith]: https://github.com/c9/smith
# Let's Make an App! We'll make a [luvit][] repl in three simple steps. ![Luvit Logo](images/logo.png) [luvit]: http://luvit.io/
[![Luvit Logo](images/repl.png)](http://luvit.io:1337?debug) The final product.
## Step 1, The File Structure ``` . ├── node_modules/ │ └── ... ├── public/ │ ├── jam/ │ │ ├── ... │ │ └── require.js │ ├── client.js │ ├── index.html │ └── style.css ├── package.json └── server.js ```
### package.json ```javascript { "name": "try.luvit.io", "author": "Tim Caswell", "version": "0.0.1", "private": true, "dependencies": { "jamjs": "~0.2.8" }, "jam": { "packageDir": "public/jam", "baseUrl": "public", "dependencies": { } }, "scripts": { "install": "jam install" } } ```

public/index.html

<!doctype html>
<html lang="en">
<head>
  <title>try.luvit.io</title>
  <link rel="stylesheet" href="style.css"/>
</head>
<body>
  <h1>try.luvit.io</h1>
  <script src="jam/require.js"></script>
  <script src="client.js"></script>
</body>
</html>

A plain HTML5 page

## Step 2, Architect and Smith ``` . ├── node_modules/ ├── plugins/ │ ├── http.js │ ├── smith.js │ └── static.js ├── public/ │ ├── jam/ │ ├── plugins/ │ │ └── smith.js │ ├── client.js │ ├── index.html │ └── style.css ├── config.js ├── package.json └── server.js ```
### package.json ```javascript { ... "dependencies": { "jamjs": "~0.2.8", "architect": "~0.1.6", "smith": "~0.1.13", "ws": "~0.4.22", "send": "~0.1.0" }, "jam": { "packageDir": "public/jam", "baseUrl": "public", "dependencies": { "architect": "~0.1.6", "smith": "~0.1.13" } }, ... } ```
### server.js ```javascript var architect = require('architect'); var configPath = __dirname + "/config.js"; var config = architect.loadConfig(configPath); architect.createApp(config, function (err, app) { if (err) throw err; var address = app.services.http.address(); console.log("App listening at", address); }); ```
### config.js ```javascript var root = __dirname + "/public"; module.exports = [ { packagePath: "./plugins/http.js", port: process.env.PORT, host: process.env.IP }, { packagePath: "./plugins/static.js", folders: [ {mount: "/", root: root } ] }, { packagePath: "./plugins/smith.js", debug: process.env.DEBUG, url: "/smith" } ]; ```
### public/client.js ```javascript require(['architect'], function (architect) { var config = [ { packagePath: "plugins/smith.js", debug: (/debug/).test(window.location.search), url: "/smith" } ]; architect.resolveConfig(config, function (err, config) { if (err) throw err; window.app = architect.createApp(config); }); }); ```
![browser repl](images/step2-node.png) ![browser repl](images/step2-browser.png)
## Step 3, Add a Terminal! ``` . ├── plugins/ │ ├── http.js │ ├── smith.js │ ├── static.js │ └── tty.js ├── public/ │ ├── plugins/ │ │ ├── smith.js │ │ └── tty.js │ ├── client.js │ ├── index.html │ └── style.css ├── config.js ├── package.json └── server.js ```
### package.json ```javascript { ... "dependencies": { "jamjs": "~0.2.8", "architect": "~0.1.6", "smith": "~0.1.13", "ws": "~0.4.22", "send": "~0.1.0", "pty.js": "~0.1.2" }, "jam": { ... "dependencies": { "architect": "~0.1.6", "smith": "~0.1.13", "tty": "~0.0.1" } }, ... } ```
### config.js ```javascript module.exports = [ { packagePath: "./plugins/http.js", ... }, { packagePath: "./plugins/static.js", ... }, { packagePath: "./plugins/smith.js", ... }, { packagePath: "./plugins/tty.js", command: "luvit", options: { name: "xterm-color", cols: 80, rows: 30, cwd: home, env: { HOME: home } } } ]; ```
### plugins/tty.js ```javascript module.exports = setup; setup.consumes = ["smith"]; function setup(config, imports, register) { imports.smith.on("setup", function (api) { api.spawn = spawn; api.write = write; api.end = end; api.resize = resize; api.life = life; }); function life(callback) {} function spawn(callback) { ... } function write(fd, chunk) { ... } function end(fd, chunk) { ... } function resize(fd, width, height) { ... } register(); } ```
### public/plugins/tty.js ```javascript define(function () { setup.consumes = ["smith"]; setup.provides = ["tty"]; function setup(config, imports, register) { imports.smith.on("setup", function (api) { api.onData = onData; api.onExit = onExit; api.life = life; }); function life(callback) {} function onData(fd, chunk) { ... } function onExit(fd, code) { ... } imports.smith.once("connect", function (remote) { ... }); ... } return setup; }); ```
### public/plugins/gui.js ```javascript define(["tty"], function (tty) { setup.consumes = ["tty"]; function setup(config, imports, register) { var termDiv = document.getElementById("terminal"); imports.tty.spawn(function (err, terminal) { if (err) throw err; var box = new tty.Terminal(80, 24, function (chunk) { terminal.write(chunk); }); box.open(termDiv); ... }); register(); } return setup; }); ```
### Terminal Server! ![browser repl](images/step3-node.png)
![browser repl](images/step3-browser.png)
# Questions?