├── .gitignore
├── .npmignore
├── Cakefile
├── LICENCE
├── README.md
├── bin
└── cjsx
├── example
├── car.cjsx
├── cool-component.cjsx
├── cool-component.coffee
├── log-component.coffee
├── named-component.coffee
├── neat-component.cjsx
├── neat-component.js
├── some-class.coffee
└── watch.cjsx
├── package.json
├── register.js
├── src
├── browser.coffee
├── coffee-react-script.coffee
├── command.coffee
├── helpers.coffee
└── register.coffee
├── test
├── compile.coffee
├── expected
│ ├── compiled-output.js
│ └── rendered-output.html
├── require.coffee
└── run.coffee
└── update.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore docs files
2 | _gh_pages
3 | _site
4 | .ruby-version
5 |
6 | # Numerous always-ignore extensions
7 | *.diff
8 | *.err
9 | *.orig
10 | *.log
11 | *.rej
12 | *.swo
13 | *.swp
14 | *.zip
15 | *.vi
16 | *~
17 |
18 | # OS or Editor folders
19 | .DS_Store
20 | ._*
21 | Thumbs.db
22 | .cache
23 | .project
24 | .settings
25 | .tmproj
26 | *.esproj
27 | nbproject
28 | *.sublime-project
29 | *.sublime-workspace
30 | .idea
31 |
32 | # Komodo
33 | *.komodoproject
34 | .komodotools
35 |
36 | # Folders to ignore
37 | node_modules
38 | bower_components
39 |
40 | lib/
41 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | test/
3 | examples/
4 | Cakefile
5 | *.sh
--------------------------------------------------------------------------------
/Cakefile:
--------------------------------------------------------------------------------
1 | fs = require 'fs'
2 | path = require 'path'
3 | {spawn, exec} = require 'child_process'
4 |
5 | {DEBUG} = process.env
6 |
7 | COFFEE_EXECUTABLE = require.resolve('coffee-script/bin/coffee')
8 |
9 | # ANSI Terminal Colors.
10 | bold = red = green = reset = ''
11 | unless process.env.NODE_DISABLE_COLORS
12 | bold = '\x1B[0;1m'
13 | red = '\x1B[0;31m'
14 | green = '\x1B[0;32m'
15 | reset = '\x1B[0m'
16 |
17 | # Log a message with a color.
18 | log = (message, color, explanation) ->
19 | console.log color + message + reset + ' ' + (explanation or '')
20 |
21 | # Build transformer from source.
22 | build = (cb) ->
23 | files = fs.readdirSync('./src')
24 | .filter((f) -> /\.coffee$/.test(f))
25 | .map(((f) -> path.basename(f, '.coffee')))
26 |
27 | run 'mkdir', ['-p','bin', 'lib'], ->
28 | compile files, 'src/', 'lib/', cb
29 |
30 | compile = (srcFiles, srcDir, destDir, cb) ->
31 | srcFilePaths = srcFiles.map (filename) -> path.join(srcDir, "#{filename}.coffee")
32 | args = ['--bare', '-o', destDir, '--compile'].concat srcFilePaths
33 | coffee args, cb
34 |
35 | # Run CoffeeScript command
36 | coffee = (args, cb) -> run COFFEE_EXECUTABLE, args, cb
37 |
38 | run = (executable, args = [], cb) ->
39 | console.log "#{path.basename executable} #{args.join(' ')}" if DEBUG
40 | proc = spawn executable, args
41 | proc.stdout.on 'data', (buffer) -> log buffer.toString(), green
42 | proc.stderr.on 'data', (buffer) -> log buffer.toString(), red
43 | proc.on 'exit', (status) -> cb(status) if typeof cb is 'function'
44 |
45 | test = ->
46 | run 'tap', ['test/*.coffee'], (status) ->
47 | if status == 0
48 | log 'pass', green
49 | else
50 | log 'fail', red
51 |
52 | task 'build', 'build coffee-react from source', build
53 |
54 | task 'test', 'test coffee-react', test
55 |
56 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 James Friend
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Coffee-React
2 |
3 | **[CoffeeScript 2 has JSX built in. You should use that instead](http://coffeescript.org/v2/#jsx)**
4 |
5 | # STATUS: DEPRECATED
6 |
7 | This tool is no longer maintained. If you need to transition your codebase from
8 | it, a codemod is available to do so: [cjsx-codemod](https://github.com/jsdf/cjsx-codemod). You can also use [CoffeeScript 2, which has JSX built in](http://coffeescript.org/v2/#jsx).
9 |
10 | This project started as a way for me to explore how JSX could fit into
11 | Coffeescript syntax, as a quickly hacked together prototype. While I never
12 | really promoted it, it quickly took on a life of its own, and before long people
13 | were asking for it to support all kinds of different use cases. On top of that I
14 | had no experience writing parsers, so the result is something with
15 | [insurmountable limitations](https://github.com/jsdf/coffee-react/issues/32).
16 |
17 | As I eventually stopped using Coffeescript I ended up neglecting this project,
18 | but as people were using it I didn't want to kill it. I really should have,
19 | however, because it meant that people were using a crappy, ill-conceived,
20 | unmaintained tool. Now, long overdue, I'm putting it out to pasture.
21 |
22 | Original readme follows:
23 |
24 | Coffee-React provides a JSX-like syntax for building [React](http://facebook.github.io/react/) components with the full awesomeness of CoffeeScript.
25 |
26 | [Try it out](https://jsdf.github.io/coffee-react-transform/).
27 |
28 | Included is the `cjsx` executable, which is wrapper for `coffee`, using
29 | [coffee-react-transform](https://github.com/jsdf/coffee-react-transform) and
30 | [coffee-script](https://github.com/jashkenas/coffeescript) to transform CJSX to Javascript.
31 | You can also `require()` CJSX components under [node](http://nodejs.org) for server-side rendering.
32 |
33 | ### Example
34 |
35 | neat-component.cjsx
36 | ```coffee
37 | NeatComponent = React.createClass
38 | render: ->
39 |
40 | {
A Component is I
if @props.showTitle}
41 |
42 | {
This line has been printed {n} times
for n in [1..5]}
43 |
44 | ```
45 |
46 | compile it
47 | ```bash
48 | $ cjsx -cb neat-component.cjsx
49 | ```
50 |
51 | neat-component.js
52 | ```js
53 | // Generated by CoffeeScript 1.9.1
54 | var NeatComponent;
55 |
56 | NeatComponent = React.createClass({displayName: "NeatComponent",
57 | render: function() {
58 | var n;
59 | return React.createElement("div", {
60 | "className": "neat-component"
61 | }, (this.props.showTitle ? React.createElement("h1", null, "A Component is I") : void 0), React.createElement("hr", null), (function() {
62 | var i, results;
63 | results = [];
64 | for (n = i = 1; i <= 5; n = ++i) {
65 | results.push(React.createElement("p", {
66 | "key": n
67 | }, "This line has been printed ", n, " times"));
68 | }
69 | return results;
70 | })());
71 | }
72 | });
73 | ```
74 |
75 | ### Installation
76 | ```bash
77 | npm install -g coffee-react
78 | ```
79 |
80 | #### Version compatibility
81 | - 5.x - React 0.13.x - 0.15.x
82 | - 4.x - React 0.13.x - 0.14.x
83 | - 3.x - React 0.13.x - 0.14.x
84 | - 2.1.x - React 0.12.1
85 | - 2.x - React 0.12
86 | - 1.x - React 0.11.2
87 | - 0.x - React 0.11 and below
88 |
89 | ### Usage
90 |
91 | ```
92 | $ cjsx -h
93 |
94 | Usage: cjsx [options] path/to/script.cjsx -- [args]
95 |
96 | If called without options, `cjsx` will run your script.
97 |
98 | -b, --bare compile without a top-level function wrapper
99 | -c, --compile compile to JavaScript and save as .js files
100 | -e, --eval pass a string from the command line as input
101 | -h, --help display this help message
102 | -j, --join concatenate the source CoffeeScript before compiling
103 | -m, --map generate source map and save as .map files
104 | -n, --nodes print out the parse tree that the parser produces
105 | --nodejs pass options directly to the "node" binary
106 | --no-header suppress the "Generated by" header
107 | -o, --output set the output directory for compiled JavaScript
108 | -p, --print print out the compiled JavaScript
109 | -s, --stdio listen for and compile scripts over stdio
110 | -l, --literate treat stdio as literate style coffee-script
111 | -t, --tokens print out the tokens that the lexer/rewriter produce
112 | -v, --version display the version number
113 | -w, --watch watch scripts for changes and rerun commands
114 |
115 | ```
116 |
117 | Output compiled JS to a file of the same name:
118 | ```bash
119 | $ cjsx -c my-component.cjsx
120 | ```
121 |
122 | #### Require .cjsx files under node
123 | As with the `coffee-script` module, you need to register `.cjsx` with the module loader:
124 | ```coffee
125 | require('coffee-react/register')
126 |
127 | Component = require('./component.cjsx')
128 |
129 | ```
130 |
131 | ### Spread attributes
132 | JSX/CJSX 'spread attributes' allow merging in an object of props when creating an element, eg:
133 | ```coffee
134 | extraProps = color: 'red', speed: 'fast'
135 |
136 | ```
137 | which is transformed to:
138 | ```coffee
139 | extraProps = color: 'red', speed: 'fast'
140 | React.createElement("div", Object.assign({"color": "blue"}, extraProps)
141 | ```
142 |
143 | If you use this syntax in your code, be sure to include a shim for `Object.assign` for browsers/environments which don't yet support it. [object.assign](https://www.npmjs.org/package/object.assign), [core-js](https://github.com/zloirock/core-js) and
144 | [es6-shim](https://github.com/es-shims/es6-shim) are some possible choices.
145 |
146 | ### React.createElement
147 |
148 | React 0.12 introduced changes to the way component descriptors are constructed, where the return value of `React.createClass` is not a descriptor factory but simply the component class itself, and descriptors must be created manually using `React.createElement` or by wrapping the component class with `React.createDescriptor`.
149 |
150 | coffee-react-transform (and as a result, coffee-react) now outputs calls to `React.createElement` to construct element descriptors from component classes for you, so you won't need to [wrap your classes using `React.createFactory`](https://gist.github.com/sebmarkbage/ae327f2eda03bf165261). However, for this to work you will need to be using at least React 0.11.2, which adds `React.createElement`.
151 |
152 | If you want the older style JSX output (which just desugars into function calls) then you need to use the 0.x branch, eg. 0.5.1.
153 |
154 | Additionally, as of 1.0.0, all input files will be CJSX transformed, even if they don't have a `.cjsx` extension or `# @cjsx` pragma.
155 |
156 | ### Related projects
157 | - [coffee-react-transform](https://github.com/jsdf/coffee-react-transform), the underlying parser/transformer package.
158 | - [node-cjsx](https://github.com/SimonDegraeve/node-cjsx): `require` CJSX files on the server (also possible with [coffee-react/register](https://github.com/jsdf/coffee-react)).
159 | - [coffee-reactify](https://github.com/jsdf/coffee-reactify): bundle CJSX files via [browserify](https://github.com/substack/node-browserify), see also [cjsxify](https://github.com/SimonDegraeve/cjsxify).
160 | - [react-coffee-quickstart](https://github.com/SimonDegraeve/react-coffee-quickstart): equivalent to [react-quickstart](https://github.com/andreypopp/react-quickstart).
161 | - [sprockets preprocessor](https://github.com/jsdf/sprockets-coffee-react): use CJSX with Rails/Sprockets
162 | - [ruby coffee-react gem](https://github.com/jsdf/ruby-coffee-react): transform CJSX to Coffeescript under Ruby
163 | - [vim plugin](https://github.com/mtscout6/vim-cjsx) for syntax highlighting
164 | - [sublime text package](https://github.com/Guidebook/sublime-cjsx) for syntax highlighting
165 | - [mimosa plugin](https://github.com/mtscout6/mimosa-cjsx) for the mimosa build tool
166 | - [gulp plugin](https://github.com/mtscout6/gulp-cjsx) for the gulp build tool
167 | - [karma preprocessor](https://github.com/mtscout6/karma-cjsx-preprocessor) for karma test runner
168 |
--------------------------------------------------------------------------------
/bin/cjsx:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var path = require('path');
4 | var fs = require('fs');
5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
6 |
7 | require(lib + '/command').run();
8 |
--------------------------------------------------------------------------------
/example/car.cjsx:
--------------------------------------------------------------------------------
1 | # @cjsx React.DOM
2 |
3 | React = require('react')
4 |
5 | Car = React.createClass
6 | render: ->
7 |
8 |
9 |
10 | Which seat can I take? {@props.seat}
11 |
12 |
13 | React.renderComponent ,
14 | document.getElementById 'container'
15 |
--------------------------------------------------------------------------------
/example/cool-component.cjsx:
--------------------------------------------------------------------------------
1 | React = require 'react'
2 |
3 | CoolComponent = React.createClass
4 | render: ->
5 |
6 |
is this component neat? {@props.neat}
7 |
8 |
9 | module.exports = CoolComponent
--------------------------------------------------------------------------------
/example/cool-component.coffee:
--------------------------------------------------------------------------------
1 | React = require 'react'
2 |
3 | CoolComponent = React.createClass
4 | render: ->
5 |
6 |
is this component neat? {@props.neat}
7 |
8 |
9 | module.exports = CoolComponent
--------------------------------------------------------------------------------
/example/log-component.coffee:
--------------------------------------------------------------------------------
1 | util = require('util')
2 |
3 | SomeClass = require './some-class.coffee'
4 | React = require 'react'
5 |
6 | process.stdout.write React.renderToStaticMarkup(React.createElement(SomeClass))
7 |
--------------------------------------------------------------------------------
/example/named-component.coffee:
--------------------------------------------------------------------------------
1 | React = require 'react'
2 |
3 | NamedComponent = React.createClass
4 | render: ->
5 | extraProps =
6 | style:
7 | color: 'red'
8 | width: 10
9 |
12 |
--------------------------------------------------------------------------------
/example/neat-component.cjsx:
--------------------------------------------------------------------------------
1 | NeatComponent = React.createClass
2 | render: ->
3 |
4 | {
A Component is I
if @props.showTitle}
5 |
6 | {
This line has been printed {n} times
for n in [1..5]}
7 |
--------------------------------------------------------------------------------
/example/neat-component.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.8.0
2 | var NeatComponent;
3 |
4 | NeatComponent = React.createClass({
5 | render: function() {
6 | var n;
7 | return React.createElement("div", {
8 | "className": "neat-component"
9 | }, (this.props.showTitle ? React.createElement("h1", null, "A Component is I") : void 0), React.createElement("hr", null), (function() {
10 | var _i, _results;
11 | _results = [];
12 | for (n = _i = 1; _i <= 10; n = ++_i) {
13 | _results.push(React.createElement("p", null, "This line has been printed ", n, " times"));
14 | }
15 | return _results;
16 | })());
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/example/some-class.coffee:
--------------------------------------------------------------------------------
1 | React = require 'react'
2 |
3 | module.exports = React.createClass
4 | render: ->
5 | extraProps =
6 | style:
7 | color: 'red'
8 | width: 10
9 |
12 |
--------------------------------------------------------------------------------
/example/watch.cjsx:
--------------------------------------------------------------------------------
1 | {classSet} = React.addons
2 |
3 | Watch = React.createClass
4 | componentDidMount: -> @tick new Date
5 |
6 | tick: (prevTime) ->
7 | return unless @isMounted()
8 | currentTime = new Date
9 | @setState {currentTime}
10 | timeTilNextTick = currentTime.getTime() - prevTime.getTime() - 1000
11 | setInterval (=> @tick currentTime), timeTilNextTick
12 |
13 | renderDate: ->
14 | {@state.currentTime.toLocaleDateString()}
15 |
16 | render: ->
17 | {currentTime} = @state
18 |
19 | {currentTime.getHours()}
20 | : {currentTime.getMinutes()}
21 | { : {currentTime.getSeconds()} if @props.showSeconds}
22 | {@renderDate()}
23 |
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "coffee-react",
3 | "description": "Coffeescript compiler wrapper adding coffee-react-transform CJSX support",
4 | "keywords": [
5 | "coffeescript",
6 | "react",
7 | "jsx",
8 | "cjsx",
9 | "coffee-react"
10 | ],
11 | "author": "James Friend",
12 | "version": "5.0.1",
13 | "licenses": [
14 | {
15 | "type": "MIT",
16 | "url": "https://raw.github.com/jsdf/coffee-react/master/LICENSE"
17 | }
18 | ],
19 | "files": [
20 | "register.js",
21 | "bin/",
22 | "lib/"
23 | ],
24 | "main": "./lib/coffee-react-script",
25 | "bin": {
26 | "cjsx": "./bin/cjsx"
27 | },
28 | "scripts": {
29 | "prepublish": "cake build",
30 | "test": "tap test/*.coffee"
31 | },
32 | "homepage": "https://github.com/jsdf/coffee-react",
33 | "bugs": "https://github.com/jsdf/coffee-react/issues",
34 | "repository": {
35 | "type": "git",
36 | "url": "git://github.com/jsdf/coffee-react.git"
37 | },
38 | "dependencies": {
39 | "coffee-react-jstransform": "^1.0.0",
40 | "coffee-react-transform": "^4.0.0",
41 | "coffee-script": "^1.10.0",
42 | "mkdirp": "^0.5.0"
43 | },
44 | "devDependencies": {
45 | "concat-stream": "^1.4.5",
46 | "react": "^0.13.1",
47 | "tap": "^0.7.1"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/register.js:
--------------------------------------------------------------------------------
1 | require('./lib/register');
2 |
--------------------------------------------------------------------------------
/src/browser.coffee:
--------------------------------------------------------------------------------
1 | # This **Browser** compatibility layer extends core CoffeeScript functions
2 | # to make things work smoothly when compiling code directly in the browser.
3 | # We add support for loading remote Coffee scripts via **XHR**, and
4 | # `text/coffeescript` script tags, source maps via data-URLs, and so on.
5 |
6 | CoffeeScript = require './coffee-react-script'
7 | CoffeeScript.require = require
8 | compile = CoffeeScript.compile
9 |
10 | # Use standard JavaScript `eval` to eval code.
11 | CoffeeScript.eval = (code, options = {}) ->
12 | options.bare ?= on
13 | eval compile code, options
14 |
15 | # Running code does not provide access to this scope.
16 | CoffeeScript.run = (code, options = {}) ->
17 | options.bare = on
18 | options.shiftLine = on
19 | Function(compile code, options)()
20 |
21 | # If we're not in a browser environment, we're finished with the public API.
22 | return unless window?
23 |
24 | # Include source maps where possible. If we've got a base64 encoder, a
25 | # JSON serializer, and tools for escaping unicode characters, we're good to go.
26 | # Ported from https://developer.mozilla.org/en-US/docs/DOM/window.btoa
27 | if btoa? and JSON? and unescape? and encodeURIComponent?
28 | compile = (code, options = {}) ->
29 | options.sourceMap = true
30 | options.inline = true
31 | {js, v3SourceMap} = CoffeeScript.compile code, options
32 | "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=coffeescript"
33 |
34 | # Load a remote script from the current domain via XHR.
35 | CoffeeScript.load = (url, callback, options = {}, hold = false) ->
36 | options.sourceFiles = [url]
37 | xhr = if window.ActiveXObject
38 | new window.ActiveXObject('Microsoft.XMLHTTP')
39 | else
40 | new window.XMLHttpRequest()
41 | xhr.open 'GET', url, true
42 | xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr
43 | xhr.onreadystatechange = ->
44 | if xhr.readyState is 4
45 | if xhr.status in [0, 200]
46 | param = [xhr.responseText, options]
47 | CoffeeScript.run param... unless hold
48 | else
49 | throw new Error "Could not load #{url}"
50 | callback param if callback
51 | xhr.send null
52 |
53 | # Activate CoffeeScript in the browser by having it compile and evaluate
54 | # all script tags with a content-type of `text/coffeescript`.
55 | # This happens on page load.
56 | runScripts = ->
57 | scripts = window.document.getElementsByTagName 'script'
58 | coffeetypes = ['text/cjsx', 'text/coffeescript', 'text/literate-coffeescript']
59 | coffees = (s for s in scripts when s.type in coffeetypes)
60 | index = 0
61 |
62 | execute = ->
63 | param = coffees[index]
64 | if param instanceof Array
65 | CoffeeScript.run param...
66 | index++
67 | execute()
68 |
69 | for script, i in coffees
70 | do (script, i) ->
71 | options = literate: script.type is coffeetypes[1]
72 | source = script.src or script.getAttribute('data-src')
73 | if source
74 | CoffeeScript.load source,
75 | (param) ->
76 | coffees[i] = param
77 | execute()
78 | options
79 | true
80 | else
81 | options.sourceFiles = ['embedded']
82 | coffees[i] = [script.innerHTML, options]
83 |
84 | execute()
85 |
86 | # Listen for window load, both in decent browsers and in IE.
87 | if window.addEventListener
88 | window.addEventListener 'DOMContentLoaded', runScripts, no
89 | else
90 | window.attachEvent 'onload', runScripts
--------------------------------------------------------------------------------
/src/coffee-react-script.coffee:
--------------------------------------------------------------------------------
1 | # patches coffee-script module
2 |
3 | fs = require 'fs'
4 | transform = require 'coffee-react-transform'
5 |
6 | helpers = require './helpers'
7 |
8 | CoffeeScript = require 'coffee-script/lib/coffee-script/coffee-script'
9 |
10 | jsSyntaxTransform = require 'coffee-react-jstransform'
11 |
12 | unless CoffeeScript._cjsx
13 |
14 | CoffeeScript._cjsx = yes
15 |
16 | CoffeeScript.FILE_EXTENSIONS.push '.cjsx'
17 |
18 | CoffeeScript.register = -> require './register'
19 |
20 | # real coffeescript compile func, which we're wrapping
21 | CoffeeScript._csCompile = CoffeeScript.compile
22 |
23 | CoffeeScript.compile = (code, options = {}) ->
24 | input = transform(code, options)
25 |
26 | output = CoffeeScript._csCompile input, options
27 |
28 | return output if options.noJSTransforms
29 |
30 | if typeof output == 'string'
31 | jsSyntaxTransform(output)
32 | else
33 | output.js = jsSyntaxTransform(output.js)
34 | output
35 |
36 | CoffeeScript._compileFile = (filename, sourceMap = no) ->
37 | raw = fs.readFileSync filename, 'utf8'
38 | stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
39 |
40 | try
41 | answer = CoffeeScript.compile(stripped, {filename, sourceMap, literate: helpers.isLiterate filename})
42 | catch err
43 | # As the filename and code of a dynamically loaded file will be different
44 | # from the original file compiled with CoffeeScript.run, add that
45 | # information to error so it can be pretty-printed later.
46 | throw helpers.updateSyntaxError err, stripped, filename
47 |
48 | answer
49 |
50 | CoffeeScript.hasCJSXPragma = helpers.hasCJSXPragma
51 | CoffeeScript.hasCJSXExtension = helpers.hasCJSXExtension
52 | CoffeeScript.transform = transform
53 |
54 | module.exports = CoffeeScript
55 |
--------------------------------------------------------------------------------
/src/command.coffee:
--------------------------------------------------------------------------------
1 | # The `cjsx` utility. Handles command-line compilation of CoffeeScript
2 | # into various forms: saved into `.js` files or printed to stdout
3 | # or recompiled every time the source is saved,
4 | # printed as a token stream or as the syntax tree, or launch an
5 | # interactive REPL.
6 |
7 | # External dependencies.
8 | fs = require 'fs'
9 | path = require 'path'
10 | helpers = require './helpers'
11 | optparse = require 'coffee-script/lib/coffee-script/optparse'
12 | CoffeeScript = require './coffee-react-script'
13 | mkdirp = require 'mkdirp'
14 | {spawn, exec} = require 'child_process'
15 | {EventEmitter} = require 'events'
16 | cjsxTransform = require 'coffee-react-transform'
17 |
18 | useWinPathSep = path.sep is '\\'
19 |
20 | # Allow CoffeeScript to emit Node.js events.
21 | helpers.extend CoffeeScript, new EventEmitter
22 |
23 | printLine = (line) -> process.stdout.write line + '\n'
24 | printWarn = (line) -> process.stderr.write line + '\n'
25 |
26 | hidden = (file) -> /^\.|~$/.test file
27 |
28 | # The help banner that is printed in conjunction with `-h`/`--help`.
29 | BANNER = '''
30 | Usage: cjsx [options] path/to/script.cjsx -- [args]
31 |
32 | If called without options, `cjsx` will run your script.
33 | '''
34 |
35 | # The list of all the valid option flags that `cjsx` knows how to handle.
36 | SWITCHES = [
37 | ['-b', '--bare', 'compile without a top-level function wrapper']
38 | ['-c', '--compile', 'compile to JavaScript and save as .js files']
39 | ['-e', '--eval', 'pass a string from the command line as input']
40 | ['-h', '--help', 'display this help message']
41 | # ['-i', '--interactive', 'run an interactive CoffeeScript REPL']
42 | ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling']
43 | ['-m', '--map', 'generate source map and save as .map files']
44 | ['-n', '--nodes', 'print out the parse tree that the parser produces']
45 | [ '--nodejs [ARGS]', 'pass options directly to the "node" binary']
46 | [ '--no-header', 'suppress the "Generated by" header']
47 | ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript']
48 | ['-p', '--print', 'print out the compiled JavaScript']
49 | ['-s', '--stdio', 'listen for and compile scripts over stdio']
50 | ['-l', '--literate', 'treat stdio as literate style coffee-script']
51 | ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce']
52 | ['-v', '--version', 'display the version number']
53 | ['-w', '--watch', 'watch scripts for changes and rerun commands']
54 | ]
55 |
56 | # Top-level objects shared by all the functions.
57 | opts = {}
58 | sources = []
59 | sourceCode = []
60 | notSources = {}
61 | watchedDirs = {}
62 | optionParser = null
63 |
64 | # Run `cjsx` by parsing passed options and determining what action to take.
65 | # Many flags cause us to divert before compiling anything. Flags passed after
66 | # `--` will be passed verbatim to your script as arguments in `process.argv`
67 | exports.run = ->
68 | parseOptions()
69 | # Make the REPL *CLI* use the global context so as to (a) be consistent with the
70 | # `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
71 | # (such as 'colors' and 'sugar') work as expected.
72 | replCliOpts = useGlobal: yes
73 | return forkNode() if opts.nodejs
74 | return usage() if opts.help
75 | return version() if opts.version
76 | # return require('./repl').start(replCliOpts) if opts.interactive
77 | return compileStdio() if opts.stdio
78 | return compileScript null, opts.arguments[0] if opts.eval
79 | # return require('./repl').start(replCliOpts) unless opts.arguments.length
80 | return usage() unless opts.arguments.length
81 | literals = if opts.run then opts.arguments.splice 1 else []
82 | process.argv = process.argv[0..1].concat literals
83 | process.argv[0] = 'cjsx'
84 |
85 | opts.output = path.resolve opts.output if opts.output
86 | opts.join = path.resolve opts.join if opts.join
87 | for source in opts.arguments
88 | source = path.resolve source
89 | compilePath source, yes, source
90 |
91 | # Compile a path, which could be a script or a directory. If a directory
92 | # is passed, recursively compile all '.coffee', '.litcoffee', and '.coffee.md'
93 | # extension source files in it and all subdirectories.
94 | compilePath = (source, topLevel, base) ->
95 | return if source in sources or
96 | watchedDirs[source] or
97 | not topLevel and (notSources[source] or hidden source)
98 | try
99 | stats = fs.statSync source
100 | catch err
101 | if err.code is 'ENOENT'
102 | console.error "File not found: #{source}"
103 | process.exit 1
104 | throw err
105 | if stats.isDirectory()
106 | if path.basename(source) is 'node_modules'
107 | notSources[source] = yes
108 | return
109 | if opts.run
110 | compilePath findDirectoryIndex(source), topLevel, base
111 | return
112 | watchDir source, base if opts.watch
113 | try
114 | files = fs.readdirSync source
115 | catch err
116 | if err.code is 'ENOENT' then return else throw err
117 | for file in files
118 | compilePath (path.join source, file), no, base
119 | else if topLevel or helpers.isCoffee source
120 | sources.push source
121 | sourceCode.push null
122 | delete notSources[source]
123 | watch source, base if opts.watch
124 | try
125 | code = fs.readFileSync source
126 | catch err
127 | if err.code is 'ENOENT' then return else throw err
128 | compileScript(source, code.toString(), base)
129 | else
130 | notSources[source] = yes
131 |
132 | findDirectoryIndex = (source) ->
133 | for ext in CoffeeScript.FILE_EXTENSIONS
134 | index = path.join source, "index#{ext}"
135 | try
136 | return index if (fs.statSync index).isFile()
137 | catch err
138 | throw err unless err.code is 'ENOENT'
139 | console.error "Missing index.coffee or index.litcoffee in #{source}"
140 | process.exit 1
141 |
142 | # Compile a single source script, containing the given code, according to the
143 | # requested options. If evaluating the script directly sets `__filename`,
144 | # `__dirname` and `module.filename` to be correct relative to the script's path.
145 | compileScript = (file, cjsxinput, base = null) ->
146 | o = opts
147 | options = compileOptions file, base
148 | try
149 | # detect and transform cjsx
150 | if (file? and helpers.hasCJSXExtension file) or helpers.hasCJSXPragma cjsxinput
151 | input = cjsxTransform cjsxinput
152 | else
153 | input = cjsxinput
154 |
155 | t = task = {file, input, options}
156 | CoffeeScript.emit 'compile', task
157 | if o.tokens
158 | printTokens CoffeeScript.tokens t.input, t.options
159 | else if o.nodes
160 | printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
161 | else if o.run
162 | CoffeeScript.register()
163 | CoffeeScript.run t.input, t.options
164 | else if o.join and t.file isnt o.join
165 | t.input = helpers.invertLiterate t.input if helpers.isLiterate file
166 | sourceCode[sources.indexOf(t.file)] = t.input
167 | compileJoin()
168 | else
169 | compiled = CoffeeScript.compile t.input, t.options
170 | t.output = compiled
171 | if o.map
172 | t.output = compiled.js
173 | t.sourceMap = compiled.v3SourceMap
174 |
175 | CoffeeScript.emit 'success', task
176 | if o.print
177 | printLine t.output.trim()
178 | else if o.compile or o.map
179 | writeJs base, t.file, t.output, options.jsPath, t.sourceMap
180 | catch err
181 | CoffeeScript.emit 'failure', err, task
182 | return if CoffeeScript.listeners('failure').length
183 | message = err.stack or "#{err}"
184 | if o.watch
185 | printLine message + '\x07'
186 | else
187 | printWarn message
188 | process.exit 1
189 |
190 | # Attach the appropriate listeners to compile scripts incoming over **stdin**,
191 | # and write them back to **stdout**.
192 | compileStdio = ->
193 | code = ''
194 | stdin = process.openStdin()
195 | stdin.on 'data', (buffer) ->
196 | code += buffer.toString() if buffer
197 | stdin.on 'end', ->
198 | compileScript null, code
199 |
200 | # If all of the source files are done being read, concatenate and compile
201 | # them together.
202 | joinTimeout = null
203 | compileJoin = ->
204 | return unless opts.join
205 | unless sourceCode.some((code) -> code is null)
206 | clearTimeout joinTimeout
207 | joinTimeout = wait 100, ->
208 | compileScript opts.join, sourceCode.join('\n'), opts.join
209 |
210 | # Watch a source CoffeeScript file using `fs.watch`, recompiling it every
211 | # time the file is updated. May be used in combination with other options,
212 | # such as `--print`.
213 | watch = (source, base) ->
214 | watcher = null
215 | prevStats = null
216 | compileTimeout = null
217 |
218 | watchErr = (err) ->
219 | throw err unless err.code is 'ENOENT'
220 | return unless source in sources
221 | try
222 | rewatch()
223 | compile()
224 | catch
225 | removeSource source, base
226 | compileJoin()
227 |
228 | compile = ->
229 | clearTimeout compileTimeout
230 | compileTimeout = wait 25, ->
231 | fs.stat source, (err, stats) ->
232 | return watchErr err if err
233 | return rewatch() if prevStats and
234 | stats.size is prevStats.size and
235 | stats.mtime.getTime() is prevStats.mtime.getTime()
236 | prevStats = stats
237 | fs.readFile source, (err, code) ->
238 | return watchErr err if err
239 | compileScript(source, code.toString(), base)
240 | rewatch()
241 |
242 | startWatcher = ->
243 | watcher = fs.watch source
244 | .on 'change', compile
245 | .on 'error', (err) ->
246 | throw err unless err.code is 'EPERM'
247 | removeSource source, base
248 |
249 | rewatch = ->
250 | watcher?.close()
251 | startWatcher()
252 |
253 | try
254 | startWatcher()
255 | catch err
256 | watchErr err
257 |
258 | # Watch a directory of files for new additions.
259 | watchDir = (source, base) ->
260 | watcher = null
261 | readdirTimeout = null
262 |
263 | startWatcher = ->
264 | watcher = fs.watch source
265 | .on 'error', (err) ->
266 | throw err unless err.code is 'EPERM'
267 | stopWatcher()
268 | .on 'change', ->
269 | clearTimeout readdirTimeout
270 | readdirTimeout = wait 25, ->
271 | try
272 | files = fs.readdirSync source
273 | catch err
274 | throw err unless err.code is 'ENOENT'
275 | return stopWatcher()
276 | for file in files
277 | compilePath (path.join source, file), no, base
278 |
279 | stopWatcher = ->
280 | watcher.close()
281 | removeSourceDir source, base
282 |
283 | watchedDirs[source] = yes
284 | try
285 | startWatcher()
286 | catch err
287 | throw err unless err.code is 'ENOENT'
288 |
289 | removeSourceDir = (source, base) ->
290 | delete watchedDirs[source]
291 | sourcesChanged = no
292 | for file in sources when source is path.dirname file
293 | removeSource file, base
294 | sourcesChanged = yes
295 | compileJoin() if sourcesChanged
296 |
297 | # Remove a file from our source list, and source code cache. Optionally remove
298 | # the compiled JS version as well.
299 | removeSource = (source, base) ->
300 | index = sources.indexOf source
301 | sources.splice index, 1
302 | sourceCode.splice index, 1
303 | unless opts.join
304 | silentUnlink outputPath source, base
305 | silentUnlink outputPath source, base, '.map'
306 | timeLog "removed #{source}"
307 |
308 | silentUnlink = (path) ->
309 | try
310 | fs.unlinkSync path
311 | catch err
312 | throw err unless err.code in ['ENOENT', 'EPERM']
313 |
314 | # Get the corresponding output JavaScript path for a source file.
315 | outputPath = (source, base, extension=".js") ->
316 | basename = helpers.baseFileName source, yes, useWinPathSep
317 | srcDir = path.dirname source
318 | if not opts.output
319 | dir = srcDir
320 | else if source is base
321 | dir = opts.output
322 | else
323 | dir = path.join opts.output, path.relative base, srcDir
324 | path.join dir, basename + extension
325 |
326 | # Write out a JavaScript source file with the compiled code. By default, files
327 | # are written out in `cwd` as `.js` files with the same name, but the output
328 | # directory can be customized with `--output`.
329 | #
330 | # If `generatedSourceMap` is provided, this will write a `.map` file into the
331 | # same directory as the `.js` file.
332 | writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
333 | sourceMapPath = outputPath sourcePath, base, ".map"
334 | jsDir = path.dirname jsPath
335 | compile = ->
336 | if opts.compile
337 | js = ' ' if js.length <= 0
338 | if generatedSourceMap then js = "#{js}\n//# sourceMappingURL=#{helpers.baseFileName sourceMapPath, no, useWinPathSep}\n"
339 | fs.writeFile jsPath, js, (err) ->
340 | if err
341 | printLine err.message
342 | else if opts.compile and opts.watch
343 | timeLog "compiled #{sourcePath}"
344 | if generatedSourceMap
345 | fs.writeFile sourceMapPath, generatedSourceMap, (err) ->
346 | if err
347 | printLine "Could not write source map: #{err.message}"
348 | fs.exists jsDir, (itExists) ->
349 | if itExists then compile() else mkdirp jsDir, compile
350 |
351 | # Convenience for cleaner setTimeouts.
352 | wait = (milliseconds, func) -> setTimeout func, milliseconds
353 |
354 | # When watching scripts, it's useful to log changes with the timestamp.
355 | timeLog = (message) ->
356 | console.log "#{(new Date).toLocaleTimeString()} - #{message}"
357 |
358 | # Pretty-print a stream of tokens, sans location data.
359 | printTokens = (tokens) ->
360 | strings = for token in tokens
361 | tag = token[0]
362 | value = token[1].toString().replace(/\n/, '\\n')
363 | "[#{tag} #{value}]"
364 | printLine strings.join(' ')
365 |
366 | # Use the [OptionParser module](optparse.html) to extract all options from
367 | # `process.argv` that are specified in `SWITCHES`.
368 | parseOptions = ->
369 | optionParser = new optparse.OptionParser SWITCHES, BANNER
370 | o = opts = optionParser.parse process.argv[2..]
371 | o.compile or= !!o.output
372 | o.run = not (o.compile or o.print or o.map)
373 | o.print = !! (o.print or (o.eval or o.stdio and o.compile))
374 |
375 | # The compile-time options to pass to the CoffeeScript compiler.
376 | compileOptions = (filename, base) ->
377 | answer = {
378 | filename
379 | literate: opts.literate or helpers.isLiterate(filename)
380 | bare: opts.bare
381 | header: opts.compile and not opts['no-header']
382 | sourceMap: opts.map
383 | }
384 | if filename
385 | if base
386 | cwd = process.cwd()
387 | jsPath = outputPath filename, base
388 | jsDir = path.dirname jsPath
389 | answer = helpers.merge answer, {
390 | jsPath
391 | sourceRoot: path.relative jsDir, cwd
392 | sourceFiles: [path.relative cwd, filename]
393 | generatedFile: helpers.baseFileName(jsPath, no, useWinPathSep)
394 | }
395 | else
396 | answer = helpers.merge answer,
397 | sourceRoot: ""
398 | sourceFiles: [helpers.baseFileName filename, no, useWinPathSep]
399 | generatedFile: helpers.baseFileName(filename, yes, useWinPathSep) + ".js"
400 | answer
401 |
402 | # Start up a new Node.js instance with the arguments in `--nodejs` passed to
403 | # the `node` binary, preserving the other options.
404 | forkNode = ->
405 | nodeArgs = opts.nodejs.split /\s+/
406 | args = process.argv[1..]
407 | args.splice args.indexOf('--nodejs'), 2
408 | p = spawn process.execPath, nodeArgs.concat(args),
409 | cwd: process.cwd()
410 | env: process.env
411 | customFds: [0, 1, 2]
412 | p.on 'exit', (code) -> process.exit code
413 |
414 | # Print the `--help` usage message and exit. Deprecated switches are not
415 | # shown.
416 | usage = ->
417 | printLine (new optparse.OptionParser SWITCHES, BANNER).help()
418 |
419 | # Print the `--version` message and exit.
420 | version = ->
421 | version = require('../package.json').version
422 | cjsxversion = require('coffee-react-transform/package.json').version
423 | printLine "coffee-react version #{version}"
424 | printLine "coffee-react-transform version #{cjsxversion}"
425 | printLine "coffee-script version #{CoffeeScript.VERSION}"
426 |
427 |
--------------------------------------------------------------------------------
/src/helpers.coffee:
--------------------------------------------------------------------------------
1 | helpers = require 'coffee-script/lib/coffee-script/helpers'
2 |
3 | helpers.isCoffee = (filepath) -> /\.((lit)?coffee|coffee\.md|cjsx)$/.test filepath
4 |
5 | helpers.hasCJSXExtension = (filepath) -> /\.(cjsx)$/.test filepath
6 |
7 | helpers.hasCJSXPragma = (src) -> /^\s*#\s*@(cjsx)/.test src
8 |
9 | module.exports = helpers
--------------------------------------------------------------------------------
/src/register.coffee:
--------------------------------------------------------------------------------
1 | CoffeeScript = require './coffee-react-script'
2 | child_process = require 'child_process'
3 | helpers = require './helpers'
4 | path = require 'path'
5 |
6 | # Load and run a CoffeeScript file for Node, stripping any `BOM`s.
7 | loadFile = (module, filename) ->
8 | answer = CoffeeScript._compileFile filename, false
9 | module._compile answer, filename
10 |
11 | # If the installed version of Node supports `require.extensions`, register
12 | # CoffeeScript as an extension.
13 | if require.extensions
14 | for ext in CoffeeScript.FILE_EXTENSIONS
15 | require.extensions[ext] = loadFile
16 |
17 | # Patch Node's module loader to be able to handle mult-dot extensions.
18 | # This is a horrible thing that should not be required. Perhaps, one day,
19 | # when a truly benevolent dictator comes to rule over the Republik of Node,
20 | # it won't be.
21 | Module = require 'module'
22 |
23 | findExtension = (filename) ->
24 | extensions = path.basename(filename).split '.'
25 | # Remove the initial dot from dotfiles.
26 | extensions.shift() if extensions[0] is ''
27 | # Start with the longest possible extension and work our way shortwards.
28 | while extensions.shift()
29 | curExtension = '.' + extensions.join '.'
30 | return curExtension if Module._extensions[curExtension]
31 | '.js'
32 |
33 | Module::load = (filename) ->
34 | @filename = filename
35 | @paths = Module._nodeModulePaths path.dirname filename
36 | extension = findExtension filename
37 | Module._extensions[extension](this, filename)
38 | @loaded = true
39 |
40 | # If we're on Node, patch `child_process.fork` so that Coffee scripts are able
41 | # to fork both CoffeeScript files, and JavaScript files, directly.
42 | if child_process
43 | {fork} = child_process
44 | binary = require.resolve '../bin/cjsx'
45 | child_process.fork = (path, args, options) ->
46 | if helpers.isCoffee path
47 | unless Array.isArray args
48 | options = args or {}
49 | args = []
50 | args = [path].concat args
51 | path = binary
52 | fork path, args, options
--------------------------------------------------------------------------------
/test/compile.coffee:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env coffee
2 |
3 | fs = require 'fs'
4 | {spawn, exec} = require 'child_process'
5 | {test} = require('tap')
6 | concat = require 'concat-stream'
7 |
8 | run = (executable, args = [], outStream, errStream, cb) ->
9 | proc = spawn executable, args
10 | proc.stdout.pipe outStream
11 | proc.stderr.pipe errStream
12 | proc.on 'err', (err) -> cb(err)
13 | proc.on 'exit', (status) -> cb(null, status)
14 |
15 | test 'compile cjsx file', (t) ->
16 | t.plan(1)
17 |
18 | file = '../example/named-component.coffee'
19 | expected = fs.readFileSync './expected/compiled-output.js', encoding: 'utf8'
20 |
21 | outStream = concat {encoding: 'buffer'}, (output) ->
22 | t.equal output.toString(), expected, 'got output'
23 |
24 | errStream = concat {encoding: 'buffer'}, (output) ->
25 | if output.toString().length
26 | t.fail output.toString()
27 |
28 | run require.resolve('../bin/cjsx'), ['-cp', file], outStream, errStream, (err, status) ->
29 | t.fail(err) if err
30 | t.fail(status) if status isnt 0
31 | t.end()
32 |
--------------------------------------------------------------------------------
/test/expected/compiled-output.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.10.0
2 | (function() {
3 | var NamedComponent, React;
4 |
5 | React = require('react');
6 |
7 | NamedComponent = React.createClass({displayName: "NamedComponent",
8 | render: function() {
9 | var extraProps, i;
10 | extraProps = {
11 | style: {
12 | color: 'red'
13 | },
14 | width: 10
15 | };
16 | return React.createElement("ul", Object.assign({}, extraProps, {
17 | "width": 12.
18 | }), (function() {
19 | var j, len, ref, results;
20 | ref = [2, 3, 5];
21 | results = [];
22 | for (j = 0, len = ref.length; j < len; j++) {
23 | i = ref[j];
24 | results.push(React.createElement("li", {
25 | "key": i
26 | }, i, " ", React.createElement("input", {
27 | "type": "checkbox",
28 | "defaultChecked": true
29 | })));
30 | }
31 | return results;
32 | })());
33 | }
34 | });
35 |
36 | }).call(this);
37 |
--------------------------------------------------------------------------------
/test/expected/rendered-output.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/require.coffee:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env coffee
2 |
3 | fs = require 'fs'
4 | {test} = require('tap')
5 |
6 | require('../register')
7 |
8 | React = require 'react'
9 |
10 | test 'require cjsx coffee file', (t) ->
11 | t.plan(1)
12 |
13 | expected = fs.readFileSync './expected/rendered-output.html', encoding: 'utf8'
14 |
15 | SomeClass = require '../example/some-class'
16 | rendered = React.renderToStaticMarkup(React.createElement(SomeClass))
17 |
18 | t.equal rendered, expected, 'correct output'
19 | t.end()
20 |
--------------------------------------------------------------------------------
/test/run.coffee:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env coffee
2 |
3 | fs = require 'fs'
4 | {spawn, exec} = require 'child_process'
5 | {test} = require('tap')
6 | concat = require 'concat-stream'
7 |
8 | run = (executable, args = [], outStream, errStream, cb) ->
9 | proc = spawn executable, args
10 | proc.stdout.pipe outStream
11 | proc.stderr.pipe errStream
12 | proc.on 'err', (err) -> cb(err)
13 | proc.on 'exit', (status) -> cb(null, status)
14 |
15 | test 'run cjsx file', (t) ->
16 | t.plan(1)
17 |
18 | file = '../example/log-component.coffee'
19 | expected = fs.readFileSync './expected/rendered-output.html', encoding: 'utf8'
20 |
21 | outStream = concat {encoding: 'buffer'}, (output) ->
22 | t.equal output.toString(), expected, 'got output'
23 |
24 | errStream = concat {encoding: 'buffer'}, (output) ->
25 | if output.toString().length
26 | t.fail output.toString()
27 |
28 | run require.resolve('../bin/cjsx'), [file], outStream, errStream, (err, status) ->
29 | t.fail(err) if err
30 | t.fail(status) if status isnt 0
31 | t.end()
32 |
--------------------------------------------------------------------------------
/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | git pull origin master
4 | DEPENDENCY="coffee-react-transform"
5 | VERSION=`npm view ${DEPENDENCY} version`
6 | echo "updating to $VERSION"
7 | npm install --save "${DEPENDENCY}@${VERSION}"
8 | npm test
9 | git add ./package.json
10 | git commit -m "updated ${DEPENDENCY} to v${VERSION}"
11 | npm version $VERSION
12 | read -p "will publish $VERSION. are you sure? " -n 1 -r
13 | echo
14 | if [[ $REPLY =~ ^[Yy]$ ]]
15 | then
16 | git push origin master
17 | npm publish .
18 | fi
--------------------------------------------------------------------------------