├── lib
├── README.md
├── coffee-script
│ ├── index.js
│ ├── helpers.js
│ ├── browser.js
│ ├── cake.js
│ ├── scope.js
│ ├── optparse.js
│ ├── coffee-script.js
│ ├── repl.js
│ ├── rewriter.js
│ └── command.js
├── consts.js
├── pool.js
├── utils.js
├── command_line.js
├── errorHandler.js
└── engine.js
├── .yarnrc
├── .gitignore
├── test
├── cases
│ ├── indent_attack
│ │ ├── vars.js
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── plaintext
│ │ ├── input.toffee
│ │ └── output.toffee
│ ├── include_techniques
│ │ ├── vars.js
│ │ ├── message.toffee
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── hello_world
│ │ ├── output.toffee
│ │ ├── input.toffee
│ │ ├── vars.js
│ │ └── temp.toffee
│ ├── lambda_fns
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── multiline_interpolation
│ │ ├── foo.toffee
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── render_no_args
│ │ ├── input.toffee
│ │ └── output.toffee
│ ├── comments
│ │ ├── vars.js
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── post_process
│ │ ├── signature.toffee
│ │ ├── output.toffee
│ │ ├── vars.coffee
│ │ ├── buncha_junk.toffee
│ │ └── input.toffee
│ ├── include_order
│ │ ├── vars.js
│ │ ├── child.toffee
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── include_recursion
│ │ ├── vars.js
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── passback
│ │ ├── vars.js
│ │ ├── const3.toffee
│ │ ├── const4_sub.toffee
│ │ ├── const5_sub.toffee
│ │ ├── const4.toffee
│ │ ├── const2.toffee
│ │ ├── const1.toffee
│ │ ├── const5.toffee
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── custom_escape
│ │ ├── vars.coffee
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── junk
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── snippets
│ │ ├── foo
│ │ │ ├── bar
│ │ │ │ └── body.toffee
│ │ │ └── message.toffee
│ │ ├── vars.js
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── bad_unicode
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── special_cases
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── eco_compare
│ │ ├── output.toffee
│ │ └── input.toffee
│ ├── json_formatting
│ │ ├── input.toffee
│ │ └── output.toffee
│ ├── escape
│ │ ├── input.toffee
│ │ └── output.toffee
│ └── big_file
│ │ ├── output.toffee
│ │ └── input.toffee
├── express4
│ ├── views
│ │ ├── subdir2
│ │ │ ├── foo
│ │ │ │ └── goodbye.toffee
│ │ │ └── goodbye_world.toffee
│ │ └── subdir1
│ │ │ └── hello_world.toffee
│ ├── public
│ │ ├── stylesheets
│ │ │ └── style.css
│ │ └── javascripts
│ │ │ └── toffee.js
│ └── app.coffee
├── express4_error_handling
│ ├── views
│ │ ├── test_bad_str_interpolate.toffee
│ │ ├── test_bad_toffee_syntax.toffee
│ │ ├── test_bad_runtime.toffee
│ │ ├── index.toffee
│ │ └── test_bad_coffee_syntax.toffee
│ ├── public
│ │ ├── stylesheets
│ │ │ └── style.css
│ │ └── javascripts
│ │ │ └── toffee.js
│ ├── package.json
│ ├── routes
│ │ └── index.js
│ └── app.coffee
├── generate_express_test.coffee
└── run_cases.iced
├── bin
└── toffee
├── src
├── consts.coffee
├── pool.coffee
├── utils.coffee
├── toffee.jison
├── command_line.coffee
├── errorHandler.coffee
└── engine.coffee
├── LICENSE
├── package.json
├── Cakefile
├── index.js
├── index.coffee
├── toffee.js
└── README.md
/lib/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | save-prefix ""
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.DS_Store
--------------------------------------------------------------------------------
/test/cases/indent_attack/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/test/cases/plaintext/input.toffee:
--------------------------------------------------------------------------------
1 | Hi there.
--------------------------------------------------------------------------------
/test/cases/plaintext/output.toffee:
--------------------------------------------------------------------------------
1 | Hi there.
--------------------------------------------------------------------------------
/test/cases/include_techniques/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/test/cases/hello_world/output.toffee:
--------------------------------------------------------------------------------
1 | Hello, world.
--------------------------------------------------------------------------------
/test/cases/lambda_fns/output.toffee:
--------------------------------------------------------------------------------
1 | PassPassPassPass
--------------------------------------------------------------------------------
/test/cases/hello_world/input.toffee:
--------------------------------------------------------------------------------
1 | #{greeting}, world.
--------------------------------------------------------------------------------
/test/cases/multiline_interpolation/foo.toffee:
--------------------------------------------------------------------------------
1 | #{a} #{b}
--------------------------------------------------------------------------------
/test/cases/render_no_args/input.toffee:
--------------------------------------------------------------------------------
1 | No arguments passed.
--------------------------------------------------------------------------------
/test/cases/comments/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "greeting": "Hello"
3 | }
--------------------------------------------------------------------------------
/test/cases/hello_world/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "greeting": "Hello"
3 | }
--------------------------------------------------------------------------------
/test/cases/post_process/signature.toffee:
--------------------------------------------------------------------------------
1 | Goodbye, cruel world.
--------------------------------------------------------------------------------
/test/cases/render_no_args/output.toffee:
--------------------------------------------------------------------------------
1 | No arguments passed.
--------------------------------------------------------------------------------
/test/cases/include_order/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "greeting": "Hello"
3 | }
--------------------------------------------------------------------------------
/test/cases/include_recursion/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "countdown" : 10
3 | }
--------------------------------------------------------------------------------
/test/express4/views/subdir2/foo/goodbye.toffee:
--------------------------------------------------------------------------------
1 | Goodbye!
--------------------------------------------------------------------------------
/bin/toffee:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('../lib/command_line').run();
--------------------------------------------------------------------------------
/test/cases/include_order/child.toffee:
--------------------------------------------------------------------------------
1 | a
2 | {#
3 | say_hi()
4 | #}
5 | b
--------------------------------------------------------------------------------
/test/cases/passback/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "vx": "vx0",
3 | "x" : "hi"
4 | }
--------------------------------------------------------------------------------
/test/cases/include_order/output.toffee:
--------------------------------------------------------------------------------
1 | 1
2 | 2
3 | hia
4 |
5 | b
6 | 3
7 | 4
--------------------------------------------------------------------------------
/test/cases/comments/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | Pass 1
3 |
4 | Pass 2
5 |
6 | Pass 3
7 |
--------------------------------------------------------------------------------
/test/cases/hello_world/temp.toffee:
--------------------------------------------------------------------------------
1 | a
2 | b
3 | c
4 | #{passed_fn 100}
5 | d
6 | e
7 | f
--------------------------------------------------------------------------------
/test/cases/include_recursion/output.toffee:
--------------------------------------------------------------------------------
1 | 10...9...8...7...6...5...4...3...2...1...blastoff!
--------------------------------------------------------------------------------
/test/cases/multiline_interpolation/output.toffee:
--------------------------------------------------------------------------------
1 | Hello, world
2 |
3 | Goodbye, world
--------------------------------------------------------------------------------
/test/cases/custom_escape/vars.coffee:
--------------------------------------------------------------------------------
1 | {
2 | escape: (s) -> "#{s}".replace /[^a-z0-9]/gi, ''
3 | }
--------------------------------------------------------------------------------
/test/cases/junk/output.toffee:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/test/cases/snippets/foo/bar/body.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | msg = msg or "Unknown message"
3 | print msg
4 | #}
--------------------------------------------------------------------------------
/test/cases/snippets/vars.js:
--------------------------------------------------------------------------------
1 | {
2 | "from": "Preloaded sender",
3 | "msg" : "Preloaded message."
4 | }
--------------------------------------------------------------------------------
/test/cases/post_process/output.toffee:
--------------------------------------------------------------------------------
1 | This-is-a-hidden-message
2 |
3 | .dlrow leurc ,eybdooG
4 | .dlrow ,olleH
--------------------------------------------------------------------------------
/test/cases/post_process/vars.coffee:
--------------------------------------------------------------------------------
1 | {
2 | greeting: 'Hello'
3 | postProcess: (s) -> (c for c in s by -1).join ''
4 | }
--------------------------------------------------------------------------------
/test/cases/include_techniques/message.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | from = from or "Unknown"
3 | #}From: #{from}
4 | Msg: Hello, world
5 |
--------------------------------------------------------------------------------
/test/cases/post_process/buncha_junk.toffee:
--------------------------------------------------------------------------------
1 | T3246h354is345-i3245s345-534a534-h534i543d534d534e534n543-m534e543s543s543ag5e534.543
--------------------------------------------------------------------------------
/test/cases/bad_unicode/output.toffee:
--------------------------------------------------------------------------------
1 | \u2028:
2 |
3 | \u2029:
4 |
5 |
6 | HI 2028:
7 | HI 2028:
8 |
--------------------------------------------------------------------------------
/test/cases/passback/const3.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback["vx"] = "vx3"
3 | passback["vy"] = "vy3"
4 | #}
5 | This should not output (3).
--------------------------------------------------------------------------------
/test/express4_error_handling/views/test_bad_str_interpolate.toffee:
--------------------------------------------------------------------------------
1 |
2 | This is a bad variable.
3 |
4 | foo.bar = #{foo.bar}
5 |
6 | Hah.
--------------------------------------------------------------------------------
/test/cases/include_order/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | say_hi = ->
3 | {:hi:}
4 | #}1
5 | 2
6 | #{partial "child.toffee", say_hi: say_hi}
7 | 3
8 | 4
--------------------------------------------------------------------------------
/test/cases/passback/const4_sub.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vx = "vx4_sub"
3 | passback.vy = "vy4_sub"
4 | #}
5 | This should not output (4_sub).
--------------------------------------------------------------------------------
/test/cases/passback/const5_sub.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vx = "vx5_sub"
3 | passback.vy = "vy5_sub"
4 | #}
5 | This should not output (5_sub).
--------------------------------------------------------------------------------
/test/cases/junk/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | supplies = ["broom", "mop", "vacuum"]
3 | #}
4 | {# for supply in supplies {:- #{supply}
:} #}
5 |
6 |
--------------------------------------------------------------------------------
/test/cases/passback/const4.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vx = "vx4"
3 | passback.vy = "vy4"
4 | partial "./const4_sub.toffee"
5 | #}
6 | This should not output (4).
--------------------------------------------------------------------------------
/test/cases/custom_escape/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | custom x = Helloworldscriptvarx100script
3 | custom y = td
4 | custom z = clickclack
5 | custom w = 12objectObject
6 |
--------------------------------------------------------------------------------
/test/express4_error_handling/views/test_bad_toffee_syntax.toffee:
--------------------------------------------------------------------------------
1 |
2 | {#
3 | x = "Foo"
4 | #}
5 |
6 | {#
7 | {: :}
8 | y = "Bar"
9 | {:
10 | #}
11 |
--------------------------------------------------------------------------------
/test/cases/bad_unicode/input.toffee:
--------------------------------------------------------------------------------
1 | \u2028:
2 | \u2029:
3 | {#
4 | u2028 = "
"
5 | u2029 = "
"
6 | #}
7 | HI 2028: #{"
"}
8 | HI 2028: #{"
"}
9 |
--------------------------------------------------------------------------------
/test/cases/passback/const2.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vz = "vz2"
3 | vx = "Should not set."
4 | vy = "Should not set."
5 | #}
6 | This should not output (2).
--------------------------------------------------------------------------------
/test/cases/passback/const1.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vx = "vx1"
3 | passback.vy = "vy1"
4 | passback.x = "oh shit"
5 | passback.y = "oh noze"
6 | #}
7 | This should output (1).
--------------------------------------------------------------------------------
/test/express4/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
--------------------------------------------------------------------------------
/test/cases/include_recursion/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | if (countdown is 0) {:blastoff!:}
3 | else
4 | print "#{countdown}...#{partial 'input.toffee', countdown: (countdown-1)}"
5 | #}
--------------------------------------------------------------------------------
/test/cases/snippets/output.toffee:
--------------------------------------------------------------------------------
1 | From: Preloaded sender
2 | Preloaded message.
3 | From: Unknown sender
4 | Unknown message.
5 | From: Sam
6 | Preloaded message.
7 | From: Max
8 | Unknown message.
--------------------------------------------------------------------------------
/test/cases/special_cases/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | "PASSED"
3 |
4 |
5 | click & clack
6 |
7 |
8 | A backslash is a \
9 |
--------------------------------------------------------------------------------
/test/express4_error_handling/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
--------------------------------------------------------------------------------
/test/cases/multiline_interpolation/input.toffee:
--------------------------------------------------------------------------------
1 | #{
2 | "Hello, " +
3 | "world"
4 | }
5 |
6 | #{
7 | partial "foo.toffee",
8 | a: "Goodbye#{','}"
9 | b: "world"
10 | }
--------------------------------------------------------------------------------
/test/cases/snippets/input.toffee:
--------------------------------------------------------------------------------
1 | #{ partial "./foo/message.toffee"}
2 | #{ snippet "./foo/message.toffee"}
3 | #{ partial "./foo/message.toffee", from: "Sam"}
4 | #{ snippet "./foo/message.toffee", from: "Max"}
--------------------------------------------------------------------------------
/test/cases/passback/const5.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | passback.vx = "vx5"
3 | passback.vy = "vy5"
4 | print load "./const5_sub.toffee"
5 | passback.vx = vx
6 | passback.vy = vy
7 | #}
8 | This should not output (5).
--------------------------------------------------------------------------------
/test/cases/snippets/foo/message.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | from = from or "Unknown sender"
3 | msg = msg or "Unknown message."
4 | print """
5 | From: #{from}
6 | #{snippet './bar/body.toffee', msg: msg}
7 | """
8 | #}
--------------------------------------------------------------------------------
/src/consts.coffee:
--------------------------------------------------------------------------------
1 | exports.states =
2 | TOFFEE: 1
3 | COFFEE: 2
4 |
5 | exports.TAB_SPACES = 2
6 |
7 | exports.tweakables =
8 | MISSING_FILE_RECHECK: 1000 # ms before checking if a file has been introduced/changed if fs.watch() fails
9 |
--------------------------------------------------------------------------------
/test/cases/passback/output.toffee:
--------------------------------------------------------------------------------
1 | vx,vy,vz = vx0,,
2 |
3 | This should output (1).
4 | vx,vy,vz = vx1,vy1,
5 |
6 | vx,vy,vz = vx1,vy1,vz2
7 |
8 | vx,vy,vz = vx3,vy3,vz2
9 |
10 | vx,vy,vz = vx4,vy4,vz2
11 |
12 | vx,vy,vz = vx5_sub,vy5_sub,vz2
--------------------------------------------------------------------------------
/test/cases/eco_compare/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | okcupid
3 | A site for singles
4 |
5 | tallygram
6 | A site for anyone
7 |
8 |
9 | You have 3 female friends.
--------------------------------------------------------------------------------
/lib/coffee-script/index.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var key, val, _ref;
4 |
5 | _ref = require('./coffee-script');
6 | for (key in _ref) {
7 | val = _ref[key];
8 | exports[key] = val;
9 | }
10 |
11 | }).call(this);
12 |
--------------------------------------------------------------------------------
/test/express4_error_handling/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "application-name",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node app"
7 | },
8 | "dependencies": {
9 | "express": "3.0.0beta7",
10 | "jade": "*"
11 | }
12 | }
--------------------------------------------------------------------------------
/test/express4_error_handling/views/test_bad_runtime.toffee:
--------------------------------------------------------------------------------
1 |
2 | About to convert a circular JSON structure
3 |
4 | {#
5 | x = [1,2,3]
6 | x.push x
7 | {:
8 | x as JSON
9 | {#
10 | print JSON.stringify x
11 | #}
12 | :}
13 | #}
14 |
--------------------------------------------------------------------------------
/lib/consts.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | exports.states = {
4 | TOFFEE: 1,
5 | COFFEE: 2
6 | };
7 |
8 | exports.TAB_SPACES = 2;
9 |
10 | exports.tweakables = {
11 | MISSING_FILE_RECHECK: 1000
12 | };
13 |
14 | }).call(this);
15 |
--------------------------------------------------------------------------------
/test/cases/custom_escape/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | x = '"Hello world"'
3 | y = ''
4 | z = 'click&clack'
5 | w = [1, 2, {"place": "The Dreadfort"}]
6 | #}
7 | custom x = #{x}
8 | custom y = #{y}
9 | custom z = #{z}
10 | custom w = #{w}
11 |
--------------------------------------------------------------------------------
/test/express4/views/subdir1/hello_world.toffee:
--------------------------------------------------------------------------------
1 | Hello, world!
2 |
3 | {#
4 | i = 1
5 | for j in [0...rows] {:
6 |
7 | {#
8 | for k in [0...cols] {:| #{i++} | :}
9 | #}
10 |
11 | :}
12 | #}
13 |
--------------------------------------------------------------------------------
/test/cases/include_techniques/output.toffee:
--------------------------------------------------------------------------------
1 | From: Chris <ccoyne77@gmail>
2 | Msg: Hello, world
3 |
4 | From: Max & Sam
5 | Msg: Hello, world
6 |
7 | From: Christian
8 | Msg: Hello, world
9 | From: Jennie
10 | Msg: Hello, world
11 | From: Unknown
12 | Msg: Hello, world
13 |
--------------------------------------------------------------------------------
/test/cases/indent_attack/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | Pass1Pass2
3 |
4 |
5 | Pass3Pass4
6 |
7 |
8 | Pass5Pass6
9 |
10 | Pass7Pass8
11 |
12 | ...passed with flying colors.
13 |
14 | Pass12Pass13Pass14Pass15Pass16(a perfect square)Pass17Pass18Pass19
15 | Pass20
16 |
--------------------------------------------------------------------------------
/test/cases/post_process/input.toffee:
--------------------------------------------------------------------------------
1 | #{greeting}, world.
2 | #{partial './signature.toffee'}
3 | {#
4 | reverse = (s) -> (c for c in s by -1).join ""
5 | clean = (s) -> (c for c in s when c.match /[a-z\-]/gi).join ""
6 | #}
7 | #{partial './buncha_junk.toffee', {postProcess: (s) -> reverse(clean(s))}}
--------------------------------------------------------------------------------
/test/cases/json_formatting/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | x =
3 | foo: [1,2,3]
4 | bar:
5 | car: [4,5,"<\/html",{zar: [6,7,null]}]
6 | y = [1,2,"<\/script>\""]
7 | #}
8 | #{x}
9 | #{json x, {indent: ' '}}
10 | #{json x, {indent: 2}}
11 | #{json x, {indent: '\t'}}
12 | #{__toffee.json y, {indent:3}}
13 |
--------------------------------------------------------------------------------
/test/cases/include_techniques/input.toffee:
--------------------------------------------------------------------------------
1 | #{partial "message.toffee", from: "Chris "}
2 | #{partial "message.toffee", from: "Max & Sam"}
3 | {#
4 | print partial "message.toffee", from: "Christian"
5 | {:#{ partial "message.toffee", from: "Jennie"}:}
6 | print partial "message.toffee", sender: "The enemy"
7 | #}
--------------------------------------------------------------------------------
/test/cases/lambda_fns/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | print_it = (msg) -> {:#{msg}:}
3 |
4 | print_it_twice = (msg) ->
5 | {:#{msg}:}
6 | m = msg
7 | {:#{m}:}
8 |
9 | echo_it = (msg) ->
10 | v = msg
11 | v
12 |
13 | print_it "Pass"
14 | print_it_twice "Pass"
15 | print echo_it "Pass"
16 | #}
--------------------------------------------------------------------------------
/src/pool.coffee:
--------------------------------------------------------------------------------
1 | class Pool
2 | constructor: (cons, size) ->
3 | @_consfn = cons
4 | @_size = size
5 | @_pool = []
6 |
7 | get: () ->
8 | if @_pool.length > 0
9 | @_pool.pop()
10 | else
11 | @_consfn()
12 |
13 | release: (x) ->
14 | if @_pool.length < @_size
15 | @_pool.push x
16 |
17 | exports.Pool = Pool
18 |
--------------------------------------------------------------------------------
/test/cases/passback/input.toffee:
--------------------------------------------------------------------------------
1 | vx,vy,vz = #{vx},#{vy},#{vz}
2 | #{partial "./const1.toffee"}
3 | vx,vy,vz = #{vx},#{vy},#{vz}
4 | #{load "./const2.toffee"}
5 | vx,vy,vz = #{vx},#{vy},#{vz}
6 | #{load "./const3.toffee"}
7 | vx,vy,vz = #{vx},#{vy},#{vz}
8 | #{load "./const4.toffee"}
9 | vx,vy,vz = #{vx},#{vy},#{vz}
10 | #{load "./const5.toffee"}
11 | vx,vy,vz = #{vx},#{vy},#{vz}
--------------------------------------------------------------------------------
/test/express4/views/subdir2/goodbye_world.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | #
3 | # let's test some recursion.
4 | #
5 | # ----------------------------
6 |
7 | if not countdown? then countdown = 32
8 | if countdown is 0
9 | print partial "foo/goodbye.toffee"
10 | else {:
11 | #{countdown} #{partial './goodbye_world.toffee', {countdown: countdown - 1}}
12 | :}
13 |
14 | #}
--------------------------------------------------------------------------------
/test/express4_error_handling/routes/index.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * GET home page.
4 | */
5 |
6 | exports.index = function(req, res){
7 | var vars = { }
8 | res.render('index.toffee', vars);
9 | };
10 |
11 | /*
12 | * individual test cases
13 | */
14 |
15 | exports.path = function(req, res){
16 | var vars = { };
17 | vars.path = req.params.path
18 | res.render("./" + vars.path + ".toffee", vars);
19 | };
20 |
--------------------------------------------------------------------------------
/test/cases/special_cases/input.toffee:
--------------------------------------------------------------------------------
1 | {##
2 |
3 | Make sure leading a trailing quotes don't mess with string printing.
4 |
5 | ##}
6 | {#
7 | {:"PASSED":}
8 | #}
9 | {##
10 |
11 | Make sure `print` inside toffee mode still works and is kept raw
12 |
13 | ##}
14 |
15 | #{print "#{'click & clack'}"}
16 |
17 | {##
18 |
19 | Make backslashes in text are handled ok.
20 |
21 | ##}
22 | A backslash is a \
23 |
--------------------------------------------------------------------------------
/test/cases/comments/input.toffee:
--------------------------------------------------------------------------------
1 | {## Fail ##}
2 | Pass 1
3 | {##
4 |
5 | Fail
6 | #{x}
7 |
8 | ##}
9 | Pass 2
10 | {#
11 |
12 | # print "Fail"
13 |
14 | ###
15 | print "FAIL FAIL FAIL"
16 | #{ foo }
17 | ###
18 |
19 | #}
20 | Pass 3{## Fail ##}{## Fail ##}
21 | {##
22 | Fail
23 | #{"Fail"}
24 | {#
25 | for x in [1...100] {:
26 | #{"Fail"}
27 | {#
28 | print "Fail"
29 | #}
30 | :}
31 | #}
32 | ##}
--------------------------------------------------------------------------------
/lib/pool.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var Pool;
4 |
5 | Pool = (function() {
6 | function Pool(cons, size) {
7 | this._consfn = cons;
8 | this._size = size;
9 | this._pool = [];
10 | }
11 |
12 | Pool.prototype.get = function() {
13 | if (this._pool.length > 0) {
14 | return this._pool.pop();
15 | } else {
16 | return this._consfn();
17 | }
18 | };
19 |
20 | Pool.prototype.release = function(x) {
21 | if (this._pool.length < this._size) {
22 | return this._pool.push(x);
23 | }
24 | };
25 |
26 | return Pool;
27 |
28 | })();
29 |
30 | exports.Pool = Pool;
31 |
32 | }).call(this);
33 |
--------------------------------------------------------------------------------
/test/cases/eco_compare/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | @projects = [
3 | {url: "http://localhost:3000", name: "okcupid", description: "A site for singles"}
4 | {url: "http://localhost:3001", name: "tallygram", description: "A site for anyone"}
5 | ]
6 |
7 | if @projects.length
8 | for project in @projects {:
9 | #{project.name}
10 | #{project.description}
11 | :}
12 | else {: No projects :}
13 |
14 | friends = [
15 | { gender: "f", name: "Jennie" }
16 | { gender: "f", name: "Rachel" }
17 | { gender: "m", name: "Petar" }
18 | { gender: "f", name: "Marissa" }
19 | ]
20 | #}
21 |
22 | You have #{(f for f in friends when f.gender is "f").length} female friends.
--------------------------------------------------------------------------------
/test/express4_error_handling/views/index.toffee:
--------------------------------------------------------------------------------
1 |
2 |
3 | Express 3 Test
4 |
5 |
10 |
11 | {#
12 | ports = [3034, 3035]
13 | percent = ~~(100 / (ports.length)) - 2
14 | tests = [
15 | ["test_bad_str_interpolate", 300]
16 | ["test_bad_toffee_syntax", 300]
17 | ["test_bad_coffee_syntax", 300]
18 | ["test_bad_runtime", 300]
19 | ]
20 | #}
21 |
22 |
23 |
24 | {#
25 | for [t,height] in tests
26 | {:| #{t} | :}
27 | for p in ports
28 | {:
29 |
30 |
31 | |
32 | :}
33 | {: :}
34 | #}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/test/cases/json_formatting/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | {"foo":[1,2,3],"bar":{"car":[4,5,"\u003C/html",{"zar":[6,7,null]}]}}
3 | {
4 | "foo": [
5 | 1,
6 | 2,
7 | 3
8 | ],
9 | "bar": {
10 | "car": [
11 | 4,
12 | 5,
13 | "\u003C/html",
14 | {
15 | "zar": [
16 | 6,
17 | 7,
18 | null
19 | ]
20 | }
21 | ]
22 | }
23 | }
24 | {
25 | "foo": [
26 | 1,
27 | 2,
28 | 3
29 | ],
30 | "bar": {
31 | "car": [
32 | 4,
33 | 5,
34 | "\u003C/html",
35 | {
36 | "zar": [
37 | 6,
38 | 7,
39 | null
40 | ]
41 | }
42 | ]
43 | }
44 | }
45 | {
46 | "foo": [
47 | 1,
48 | 2,
49 | 3
50 | ],
51 | "bar": {
52 | "car": [
53 | 4,
54 | 5,
55 | "\u003C/html",
56 | {
57 | "zar": [
58 | 6,
59 | 7,
60 | null
61 | ]
62 | }
63 | ]
64 | }
65 | }
66 | [
67 | 1,
68 | 2,
69 | "\u003C/script\u003E\""
70 | ]
71 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) <2012> Chris Coyne
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/test/express4/app.coffee:
--------------------------------------------------------------------------------
1 |
2 | run = (cb) ->
3 | toffee = require '../../index.js'
4 | express = require 'express'
5 | http = require 'http'
6 |
7 | app = express()
8 |
9 |
10 | app_configure = ->
11 |
12 | toffee.expressEngine.verbose = not module.parent
13 | toffee.expressEngine.prettyPrintErrors = false
14 |
15 | app.set 'port', process.env.PORT or 3033
16 | app.set 'views', __dirname + '/views'
17 | app.engine 'toffee', toffee.__express
18 | app.use express.static __dirname + '/public'
19 | app.route('/').get (req, res) =>
20 | circular_obj = [1,2,3]
21 | circular_obj.push circular_obj
22 | title = 'Express'
23 | a_bad_test_function = -> return JSON.stringify circular_obj
24 | vars = {title, a_bad_test_function}
25 | res.render 'index.toffee', vars
26 |
27 | http.createServer(app).listen app.get('port'), ->
28 | console.log "Express server listening on port #{app.get('port')}"
29 | if cb? then cb()
30 |
31 | app_configure()
32 |
33 | if not module.parent
34 | run()
35 |
36 | else
37 | exports.run = (cb) -> run cb
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toffee",
3 | "description": "A NodeJs and browser-side templating language based on CoffeeScript with slicker tokens and syntax.",
4 | "version": "0.3.7",
5 | "directories": {
6 | "lib": "./lib"
7 | },
8 | "main": "index.js",
9 | "author": "Chris Coyne ",
10 | "bin": "./bin/toffee",
11 | "dependencies": {
12 | "coffee-script": "1.12.7",
13 | "commander": "10.0.0",
14 | "highlight.js": "11.7.0",
15 | "iced-lock": "2.0.1",
16 | "mkdirp": "2.1.3"
17 | },
18 | "devDependencies": {
19 | "assert": "2.0.0",
20 | "colors": "1.4.0",
21 | "diff": "5.1.0",
22 | "express": "4.18.2",
23 | "iced-coffee-script": "108.0.14",
24 | "jison": "0.4.18",
25 | "tablify": "0.1.5",
26 | "zombie": "6.1.4"
27 | },
28 | "files": [
29 | "index.js",
30 | "lib/*",
31 | "bin/*"
32 | ],
33 | "repository": {
34 | "type": "git",
35 | "url": "http://github.com/malgorithms/toffee"
36 | },
37 | "licenses": [
38 | {
39 | "type": "MIT",
40 | "url": "http://github.com/malgorithms/toffee/raw/master/LICENSE"
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/test/express4_error_handling/views/test_bad_coffee_syntax.toffee:
--------------------------------------------------------------------------------
1 |
2 | {#
3 | x = "Foo"
4 | #}
5 |
6 | {#
7 | y = "Bar"
8 | {:
9 | Hello there
10 | {#
11 | var x = 100
12 | #}
13 | :}
14 | #}
15 | Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah Bleah bleah bleah Bleah
--------------------------------------------------------------------------------
/src/utils.coffee:
--------------------------------------------------------------------------------
1 | lexer = require './coffee-script/lexer'
2 | lex = new lexer.Lexer()
3 |
4 | exports.interpolateString = (str) ->
5 | ###
6 | Similar to the interpolateString function in CoffeeScript,
7 | except that it doesn't actually work on anything inside an outer #{};
8 | we're just looking to recognize them.
9 | ###
10 | tokens = []
11 | res = []
12 | pi = 0
13 | i = -1
14 | while letter = str.charAt i += 1
15 | if letter is '\\'
16 | i += 1
17 | continue
18 | unless letter is '#' and str.charAt(i+1) is '{' and
19 | (expr = lex.balancedString str[i + 1..], '}')
20 | continue
21 | tokens.push ['NEOSTRING', str[pi...i]] if pi < i
22 | inner = expr[1...-1]
23 | if inner.length
24 | tokens.push ['TOKENS', inner]
25 | i += expr.length
26 | pi = i + 1
27 | tokens.push ['NEOSTRING', str[pi..]] if i > pi < str.length
28 | return res.push 'STRING', '""' unless tokens.length
29 | tokens.unshift ['', ''] unless tokens[0][0] is 'NEOSTRING'
30 | res.push '(', '(' if interpolated = tokens.length > 1
31 | for [tag, value], i in tokens
32 | res.push '+', '+' if i
33 | if tag is 'TOKENS'
34 | res.push [tag, value]
35 | else
36 | res.push ['STRING', value]
37 | #res.push 'STRING', @makeString value, '"', heredoc
38 | res.push ')', ')' if interpolated
39 | tokens
40 |
--------------------------------------------------------------------------------
/test/cases/escape/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | x = '"Hello world"'
3 | y = ' '
4 | z = 'click&clack'
5 | w = [1, 2, {"place": "The Dreadfort", "evil \"code\"": "italic"}]
6 | v = ["\u2028", "\u2029"]
7 | dir = ["hi\u{202e}there"] # ltr type things
8 | #}
9 | default x = #{x}
10 | default y = #{y}
11 | default z = #{z}
12 | default w = #{w}
13 | default r = #{r}eol
14 | default w.foo = #{w.foo}eol
15 | default v = #{v}
16 | default dir = #{dir}
17 | default dir0 = #{dir[0]}
18 |
19 |
20 | raw x = #{raw x}
21 | raw y = #{raw y}
22 | raw z = #{raw z}
23 | raw w = #{raw w}
24 |
25 |
26 | w_as_json_stringify=#{JSON.stringify w}
27 | w_as_json_stringify_raw=#{raw JSON.stringify w}
28 |
29 |
36 |
37 | {#
38 | print " raw printed x = #{x}\n"
39 | print " raw printed y = #{y}\n"
40 | print " raw printed z = #{z}\n"
41 | print " raw printed w = #{w}"
42 | #}
43 |
44 |
45 | {#
46 | print " json printed x = #{ raw raw raw raw json x }\n"
47 | print " json printed y = #{ raw raw raw raw json y }\n"
48 | print " json printed z = #{ raw raw raw raw json z }\n"
49 | print " json printed w = #{ raw raw raw raw json w }\n"
50 | print " json printed v = #{ raw raw raw raw json v }"
51 | #}
52 |
53 |
54 | {#
55 | print " html printed longhand x = #{ __toffee.html x }\n"
56 | print " html printed longhand y = #{ __toffee.html y }\n"
57 | print " html printed longhand z = #{ __toffee.html z }\n"
58 | print " html printed longhand w = #{ __toffee.html w }"
59 | #}
60 |
61 |
--------------------------------------------------------------------------------
/test/express4_error_handling/app.coffee:
--------------------------------------------------------------------------------
1 | toffee = require '../../index.js'
2 |
3 | run = (port, express_engine, cb) ->
4 | express = require 'express'
5 | routes = require './routes'
6 | http = require 'http'
7 |
8 | app = express()
9 |
10 |
11 | app.configure ->
12 |
13 | app.set 'port', port
14 | app.set 'views', __dirname + '/views'
15 | app.engine 'toffee', express_engine
16 | app.use express.favicon()
17 | app.use express.logger 'dev'
18 | app.use express.bodyParser()
19 | app.use express.methodOverride()
20 | app.use app.router
21 | app.use express.static __dirname + '/public'
22 |
23 | app.configure 'development', ->
24 | app.use express.errorHandler()
25 |
26 | app.get '/', routes.index
27 | app.get '/:path([^ ]+)', routes.path
28 |
29 | http.createServer(app).listen app.get('port'), ->
30 | console.log "Express server listening on port #{app.get('port')}"
31 | if cb? then cb()
32 |
33 | # -----------------------------------------------------------------------
34 |
35 | run_all = (cb) ->
36 |
37 | # run a standard version on port 3034
38 | # -----------------------------------
39 | run 3034, toffee.__express, ->
40 |
41 | # run a version that doesn't catch errors on port 3035
42 | # ----------------------------------------------------
43 | e2 = new toffee.engine {
44 | prettyPrintErrors: false
45 | }
46 | run 3035, toffee.toExpress(e2), ->
47 |
48 | cb()
49 |
50 | # -----------------------------------------------------------------------
51 |
52 |
53 | if not module.parent
54 | run_all ->
55 | console.log "All running"
56 |
57 | else
58 | exports.run = (cb) -> run_all cb
--------------------------------------------------------------------------------
/test/cases/escape/output.toffee:
--------------------------------------------------------------------------------
1 |
2 | default x = "Hello world"
3 | default y = <hr />
4 | default z = click&clack
5 | default w = [1,2,{"place":"The Dreadfort","evil \u003Cb\u003E\"code\"\u003C/b\u003E":"\u003Ci\u003Eitalic\u003C/i\u003E"}]
6 | default r = eol
7 | default w.foo = eol
8 | default v = ["\u2028","\u2029"]
9 | default dir = ["hi\u202ethere"]
10 | default dir0 = hithere
11 |
12 |
13 | raw x = "Hello world"
14 | raw y =
15 | raw z = click&clack
16 | raw w = 1,2,[object Object]
17 |
18 |
19 | w_as_json_stringify=[1,2,{"place":"The Dreadfort","evil <b>\"code\"</b>":"<i>italic</i>"}]
20 | w_as_json_stringify_raw=[1,2,{"place":"The Dreadfort","evil \"code\"":"italic"}]
21 |
22 |
29 |
30 | raw printed x = "Hello world"
31 | raw printed y =
32 | raw printed z = click&clack
33 | raw printed w = 1,2,[object Object]
34 |
35 |
36 | json printed x = "\"Hello world\""
37 | json printed y = "\u003Chr /\u003E"
38 | json printed z = "click\u0026clack"
39 | json printed w = [1,2,{"place":"The Dreadfort","evil \u003Cb\u003E\"code\"\u003C/b\u003E":"\u003Ci\u003Eitalic\u003C/i\u003E"}]
40 | json printed v = ["\u2028","\u2029"]
41 |
42 |
43 | html printed longhand x = "Hello world"
44 | html printed longhand y = <hr />
45 | html printed longhand z = click&clack
46 | html printed longhand w = 1,2,[object Object]
47 |
48 |
--------------------------------------------------------------------------------
/Cakefile:
--------------------------------------------------------------------------------
1 | require 'iced-coffee-script/register'
2 | {spawn, exec} = require 'child_process'
3 | fs = require 'fs'
4 | jison = require 'jison'
5 | path = require 'path'
6 | express_test = require './test/generate_express_test'
7 |
8 | task 'build', 'build the whole jam', (cb) ->
9 | console.log "Building"
10 | files = fs.readdirSync 'src'
11 | files = ('src/' + file for file in files when file.match(/\.coffee$/))
12 | clearLibJs ->
13 | buildParser ->
14 | runCoffee ['-c', '-o', 'lib/'].concat(files), ->
15 | runCoffee ['-c', 'index.coffee'], ->
16 | buildCommonBrowserHeaders ->
17 | express_test.generate ->
18 | console.log "Done building."
19 | cb() if typeof cb is 'function'
20 |
21 | task 'test', 'test server and browser support', (cb) ->
22 | run_cases = require './test/run_cases.iced'
23 | run_cases.test ->
24 | console.log "Done."
25 |
26 | runCoffee = (args, cb) ->
27 | proc = spawn './node_modules/.bin/coffee', args
28 | proc.stderr.on 'data', (buffer) -> console.log buffer.toString()
29 | proc.on 'exit', (status) ->
30 | process.exit(1) if status isnt 0
31 | cb() if typeof cb is 'function'
32 |
33 | clearLibJs = (cb) ->
34 | files = fs.readdirSync 'lib'
35 | files = ("lib/#{file}" for file in files when file.match(/\.js$/))
36 | fs.unlinkSync f for f in files
37 | cb()
38 |
39 | buildParser = (cb) ->
40 | grammar = fs.readFileSync './src/toffee.jison', 'utf8'
41 | generator = new jison.Generator grammar
42 | file_name = "toffee_lang.js"
43 | source = generator.generate {
44 | moduleType: 'commonjs'
45 | moduleName: 'toffee_lang'
46 | }
47 | fs.writeFileSync "./lib/#{file_name}", source
48 | cb()
49 |
50 | buildCommonBrowserHeaders = (cb) ->
51 | {getCommonHeadersJs} = require './lib/view'
52 | headers = getCommonHeadersJs true, true
53 | fs.writeFileSync "./toffee.js", headers, "utf8"
54 | cb()
55 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var lex, lexer;
4 |
5 | lexer = require('./coffee-script/lexer');
6 |
7 | lex = new lexer.Lexer();
8 |
9 | exports.interpolateString = function(str) {
10 |
11 | /*
12 | Similar to the interpolateString function in CoffeeScript,
13 | except that it doesn't actually work on anything inside an outer #{};
14 | we're just looking to recognize them.
15 | */
16 | var expr, i, inner, interpolated, j, len, letter, pi, ref, res, tag, tokens, value;
17 | tokens = [];
18 | res = [];
19 | pi = 0;
20 | i = -1;
21 | while (letter = str.charAt(i += 1)) {
22 | if (letter === '\\') {
23 | i += 1;
24 | continue;
25 | }
26 | if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = lex.balancedString(str.slice(i + 1), '}')))) {
27 | continue;
28 | }
29 | if (pi < i) {
30 | tokens.push(['NEOSTRING', str.slice(pi, i)]);
31 | }
32 | inner = expr.slice(1, -1);
33 | if (inner.length) {
34 | tokens.push(['TOKENS', inner]);
35 | }
36 | i += expr.length;
37 | pi = i + 1;
38 | }
39 | if ((i > pi && pi < str.length)) {
40 | tokens.push(['NEOSTRING', str.slice(pi)]);
41 | }
42 | if (!tokens.length) {
43 | return res.push('STRING', '""');
44 | }
45 | if (tokens[0][0] !== 'NEOSTRING') {
46 | tokens.unshift(['', '']);
47 | }
48 | if (interpolated = tokens.length > 1) {
49 | res.push('(', '(');
50 | }
51 | for (i = j = 0, len = tokens.length; j < len; i = ++j) {
52 | ref = tokens[i], tag = ref[0], value = ref[1];
53 | if (i) {
54 | res.push('+', '+');
55 | }
56 | if (tag === 'TOKENS') {
57 | res.push([tag, value]);
58 | } else {
59 | res.push(['STRING', value]);
60 | }
61 | }
62 | if (interpolated) {
63 | res.push(')', ')');
64 | }
65 | return tokens;
66 | };
67 |
68 | }).call(this);
69 |
--------------------------------------------------------------------------------
/lib/coffee-script/helpers.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var extend, flatten;
4 |
5 | exports.starts = function(string, literal, start) {
6 | return literal === string.substr(start, literal.length);
7 | };
8 |
9 | exports.ends = function(string, literal, back) {
10 | var len;
11 | len = literal.length;
12 | return literal === string.substr(string.length - len - (back || 0), len);
13 | };
14 |
15 | exports.compact = function(array) {
16 | var item, _i, _len, _results;
17 | _results = [];
18 | for (_i = 0, _len = array.length; _i < _len; _i++) {
19 | item = array[_i];
20 | if (item) {
21 | _results.push(item);
22 | }
23 | }
24 | return _results;
25 | };
26 |
27 | exports.count = function(string, substr) {
28 | var num, pos;
29 | num = pos = 0;
30 | if (!substr.length) {
31 | return 1 / 0;
32 | }
33 | while (pos = 1 + string.indexOf(substr, pos)) {
34 | num++;
35 | }
36 | return num;
37 | };
38 |
39 | exports.merge = function(options, overrides) {
40 | return extend(extend({}, options), overrides);
41 | };
42 |
43 | extend = exports.extend = function(object, properties) {
44 | var key, val;
45 | for (key in properties) {
46 | val = properties[key];
47 | object[key] = val;
48 | }
49 | return object;
50 | };
51 |
52 | exports.flatten = flatten = function(array) {
53 | var element, flattened, _i, _len;
54 | flattened = [];
55 | for (_i = 0, _len = array.length; _i < _len; _i++) {
56 | element = array[_i];
57 | if (element instanceof Array) {
58 | flattened = flattened.concat(flatten(element));
59 | } else {
60 | flattened.push(element);
61 | }
62 | }
63 | return flattened;
64 | };
65 |
66 | exports.del = function(obj, key) {
67 | var val;
68 | val = obj[key];
69 | delete obj[key];
70 | return val;
71 | };
72 |
73 | exports.last = function(array, back) {
74 | return array[array.length - (back || 0) - 1];
75 | };
76 |
77 | }).call(this);
78 |
--------------------------------------------------------------------------------
/test/cases/big_file/output.toffee:
--------------------------------------------------------------------------------
1 | 0... 1... 2... 3... 4... 5... 6... 7... 8... 9... 10... 11... 12... 13... 14... 15... 16... 17... 18... 19... 20... 21... 22... 23... 24... 25... 26... 27... 28... 29... 30... 31... 32... 33... 34... 35... 36... 37... 38... 39... 40... 41... 42... 43... 44... 45... 46... 47... 48... 49... 50... 51... 52... 53... 54... 55... 56... 57... 58... 59... 60... 61... 62... 63... 64... 65... 66... 67... 68... 70... 70... 71... 72... 73... 74... 75... 76... 77... 78... 79... 80... 81... 82... 83... 84... 85... 86... 87... 88... 89... 90... 91... 92... 93... 94... 95... 96... 97... 98... 99... 100... 101... 102... 103... 104... 105... 106... 107... 108... 109... 110... 111... 112... 113... 114... 115... 116... 117... 118... 119... 120... 121... 122... 123... 124... 125... 126... 127... 128... 129... 130... 131... 132... 133... 134... 135... 136... 137... 138... 139... 140... 141... 142... 143... 144... 145... 146... 147... 148... 149... 150... 151... 152... 153... 154... 155... 156... 157... 158... 159... 160... 162...162... 163... 164... 165... 166... 167... 168... 169... 170... 171... 172... 173... 174... 175... 176... 177... 178... 179... 180... 181... 182... 183... 184... 185... 186... 187... 188... 189... 190... 191... 192... 193... 194... 195... 196... 197... 198... 199... 200... 201... 202... 203... 204... 205... 206... 207... 208... 209... 210... 211... 212... 213... 214... 215... 216... 217... 218... 219... 220... 221... 222... 223... 224... 225... 226... 227... 228... 229... 230... 232... 232... 233... 234... 235... 236... 237... 238... 239... 240... 241... 242... 243... 244... 245... 246... 247... 248... 249... 250... 251... 252... 253... 254... 255... 256... 257... 258... 259... 260... 261... 262... 263... 264... 265... 266... 267... 268... 269... 270... 271... 272... 273... 274... 275... 276... 277... 278... 279... 280... 281... 282... 283... 284... 285... 286... 287... 288... 289... 290... 291... 292... 293... 294... 295... 296... 297... 298... 299... 300... 301... 302... 303... 304... 305... 306... 307... 308... 309... 310... 311... 312... 313... 314... 315... 316... 317... 318... 319... 320... 321... 322... 324...
--------------------------------------------------------------------------------
/test/cases/indent_attack/input.toffee:
--------------------------------------------------------------------------------
1 |
2 | {#
3 | if 1 is 1
4 | if 2 is 2
5 | if 3 is 3 {:Pass1:}
6 | if 1 is 1
7 | if 2 is 3
8 | if 3 is 3
9 | {:Fail:}
10 | else
11 | {:Fail:}
12 | else
13 | if 2 is 2
14 | if 3 is 3 {:Pass2:}
15 | #}
16 |
17 |
18 | {#
19 | if 1 is 1
20 | if 2 is 2
21 | if 3 is 3 {:Pass3:}
22 | if 1 is 1
23 | if 2 is 3
24 | if 3 is 3
25 | {:Fail:}
26 | else
27 | {:Fail:}
28 | else
29 | if 2 is 2
30 | if 3 is 3 {:Pass4:}
31 | #}
32 |
33 |
34 | {#
35 | if 10 is 10
36 | if 20 is 20
37 | if 30 is 30 {:Pass5:}
38 | if 10 is 10
39 | if 20 is 30
40 | if 30 is 30
41 | {:Fail:}
42 | else
43 | {:Fail:}
44 | else
45 | if 20 is 20
46 | if 30 is 30 {:Pass6:}
47 | #}
48 |
49 | {#
50 | if 99 is 99
51 | print 'Pass7'
52 | else
53 | print 'Fail'
54 | {:Fail8:}
55 | {:Pass8:}
56 | #}
57 |
58 | {#
59 |
60 | {:...passed with flying colors.:}
61 | #}{##
62 | {#
63 | if true and 10 is 10
64 | {:
65 | Pass9
66 | :}
67 | print "Pass10"
68 | if 11 is 12
69 | print "Fail"
70 | if 12 is 13 {:
71 | Fail
72 | :}
73 | else
74 | {:
75 | Pass11
76 | :}
77 | else if 11 is 12
78 | {:
79 | Fail
80 | :}
81 | #}
82 | ##}
83 |
84 | {#
85 | x = 20
86 | if x > 1
87 | for i in [12...x]
88 | square = 16
89 | {:Pass#{i}{#
90 | if i is square {:(a perfect square):}
91 | #}:}
92 | #}
93 | {#
94 | x = 20
95 | if x > 1
96 | for i in [12...x]
97 | square = 16
98 | {:Pass#{i}{#
99 | if i is square {:(a perfect square):}
100 | #}:}
101 | #}
102 |
--------------------------------------------------------------------------------
/test/cases/big_file/input.toffee:
--------------------------------------------------------------------------------
1 | {#
2 | count = 0
3 | for i in [0...2] {:#{
4 | count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
5 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
6 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
7 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
8 | }... #{count++}... #{count++}... #{count++}... #{count++
9 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
10 | }...{#
11 | count += 1
12 | print " #{count}..."
13 | #} #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
14 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
15 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
16 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
17 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
18 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
19 | }... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++}... #{count++
20 | }...{#
21 | count += 1
22 | print " #{count}..."
23 | #}:}
24 | #}
--------------------------------------------------------------------------------
/lib/coffee-script/browser.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var CoffeeScript, runScripts;
4 |
5 | CoffeeScript = require('./coffee-script');
6 |
7 | CoffeeScript.require = require;
8 |
9 | CoffeeScript["eval"] = function(code, options) {
10 | if (options == null) {
11 | options = {};
12 | }
13 | if (options.bare == null) {
14 | options.bare = true;
15 | }
16 | return eval(CoffeeScript.compile(code, options));
17 | };
18 |
19 | CoffeeScript.run = function(code, options) {
20 | if (options == null) {
21 | options = {};
22 | }
23 | options.bare = true;
24 | return Function(CoffeeScript.compile(code, options))();
25 | };
26 |
27 | if (typeof window === "undefined" || window === null) {
28 | return;
29 | }
30 |
31 | CoffeeScript.load = function(url, callback) {
32 | var xhr;
33 | xhr = new (window.ActiveXObject || XMLHttpRequest)('Microsoft.XMLHTTP');
34 | xhr.open('GET', url, true);
35 | if ('overrideMimeType' in xhr) {
36 | xhr.overrideMimeType('text/plain');
37 | }
38 | xhr.onreadystatechange = function() {
39 | var _ref;
40 | if (xhr.readyState === 4) {
41 | if ((_ref = xhr.status) === 0 || _ref === 200) {
42 | CoffeeScript.run(xhr.responseText);
43 | } else {
44 | throw new Error("Could not load " + url);
45 | }
46 | if (callback) {
47 | return callback();
48 | }
49 | }
50 | };
51 | return xhr.send(null);
52 | };
53 |
54 | runScripts = function() {
55 | var coffees, execute, index, length, s, scripts;
56 | scripts = document.getElementsByTagName('script');
57 | coffees = (function() {
58 | var _i, _len, _results;
59 | _results = [];
60 | for (_i = 0, _len = scripts.length; _i < _len; _i++) {
61 | s = scripts[_i];
62 | if (s.type === 'text/coffeescript') {
63 | _results.push(s);
64 | }
65 | }
66 | return _results;
67 | })();
68 | index = 0;
69 | length = coffees.length;
70 | (execute = function() {
71 | var script;
72 | script = coffees[index++];
73 | if ((script != null ? script.type : void 0) === 'text/coffeescript') {
74 | if (script.src) {
75 | return CoffeeScript.load(script.src, execute);
76 | } else {
77 | CoffeeScript.run(script.innerHTML);
78 | return execute();
79 | }
80 | }
81 | })();
82 | return null;
83 | };
84 |
85 | if (window.addEventListener) {
86 | addEventListener('DOMContentLoaded', runScripts, false);
87 | } else {
88 | attachEvent('onload', runScripts);
89 | }
90 |
91 | }).call(this);
92 |
--------------------------------------------------------------------------------
/src/toffee.jison:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // grammar file for Toffee Templating
4 | //
5 | //
6 |
7 | %lex
8 | %%
9 |
10 | "{##" return 'START_TOFFEE_COMMENT';
11 | "##}" return 'END_TOFFEE_COMMENT';
12 | ":}" return 'END_TOFFEE';
13 | "{:" return 'START_TOFFEE';
14 | "{#" return 'START_COFFEE';
15 | "#}" return 'END_COFFEE';
16 | [^{}#\\:\-]+|[\\{}#:\-] return 'CODE';
17 | <> return 'EOF';
18 |
19 | /lex
20 |
21 | %start starter
22 |
23 | %%
24 |
25 | starter
26 | :
27 | toffee_zone EOF { $$ = ["TOFFEE_ZONE", $1]; return $$;}
28 | ;
29 |
30 |
31 | toffee_zone
32 | :
33 | toffee_code { $$ = [$1]; }
34 | |
35 | toffee_code flip_to_coffee toffee_zone { $$ = $3; $3.splice(0,0,$1,$2); }
36 | |
37 | flip_to_coffee toffee_zone { $$ = $2; $2.splice(0,0,$1); }
38 | |
39 | toffee_code flip_to_toffee_comment toffee_zone { $$ = $3; $3.splice(0,0,$1); }
40 | |
41 | flip_to_toffee_comment toffee_zone { $$ = $2; }
42 | |
43 | { $$ = []; }
44 | ;
45 |
46 | flip_to_toffee_comment
47 | :
48 | START_TOFFEE_COMMENT toffee_commented_region END_TOFFEE_COMMENT {}
49 | ;
50 |
51 | toffee_commented_region
52 | :
53 | toffee_commented_region START_COFFEE
54 | |
55 | toffee_commented_region END_COFFEE
56 | |
57 | toffee_commented_region START_TOFFEE
58 | |
59 | toffee_commented_region END_TOFFEE
60 | |
61 | toffee_commented_region CODE
62 | |
63 | ;
64 |
65 | flip_to_coffee
66 | :
67 | START_COFFEE coffee_zone END_COFFEE { $$ = ["COFFEE_ZONE", $2]; }
68 | ;
69 |
70 | coffee_zone
71 | :
72 | coffee_code { $$ = [$1]; }
73 | |
74 | coffee_code flip_to_toffee coffee_zone { $$ = $3; $3.splice(0,0,$1,$2); }
75 | |
76 | flip_to_toffee coffee_zone { $$ = $2; $2.splice(0,0,$1); }
77 | |
78 | { $$ = []; }
79 | ;
80 |
81 | flip_to_toffee
82 | :
83 | START_TOFFEE toffee_zone END_TOFFEE { $$ = ["TOFFEE_ZONE", $2]; }
84 | ;
85 |
86 |
87 | toffee_code
88 | :
89 | code { $$ = ["TOFFEE", $1[0], $1[1] ]; }
90 | ;
91 |
92 | coffee_code
93 | :
94 | code { $$ = ["COFFEE", $1[0], $1[1] ]; }
95 | ;
96 |
97 |
98 | code
99 | :
100 | CODE { var ln = yylineno + 1 - $1.split("\n").length + 1;
101 | $$ = [$1, ln];
102 | }
103 | |
104 | code CODE { var c = $1[0] + $2;
105 | var ln = yylineno + 1 - c.split("\n").length + 1;
106 | $$ = [c, ln];
107 | }
108 | ;
109 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var __express, cacheless_engine, e, engine, getCommonHeaders, getCommonHeadersJs, ref, to_express, view;
4 |
5 | engine = require('./lib/engine').engine;
6 |
7 | ref = require('./lib/view'), view = ref.view, getCommonHeaders = ref.getCommonHeaders, getCommonHeadersJs = ref.getCommonHeadersJs;
8 |
9 | exports.engine = engine;
10 |
11 | exports.view = view;
12 |
13 | exports.getCommonHeaders = getCommonHeaders;
14 |
15 | exports.getCommonHeadersJs = getCommonHeadersJs;
16 |
17 | exports.expressEngine = e = new engine({
18 | verbose: false,
19 | prettyPrintErrors: true
20 | });
21 |
22 | exports.render = e.run;
23 |
24 | cacheless_engine = new engine({
25 | verbose: false,
26 | prettyPrintErrors: true,
27 | cache: false
28 | });
29 |
30 | exports.compileStr = function(template_str, options) {
31 | var v;
32 | v = new view(template_str, options);
33 | return function(x) {
34 | return v.run(x);
35 | };
36 | };
37 |
38 | to_express = exports.toExpress = function(eng) {
39 | return function(filename, options, cb) {
40 | return eng.run(filename, options, function(err, res) {
41 | if (err) {
42 | if (typeof err === "string") {
43 | err = new Error(err);
44 | }
45 | return cb(err);
46 | } else {
47 | return cb(null, res);
48 | }
49 | });
50 | };
51 | };
52 |
53 | __express = exports.__express = to_express(e);
54 |
55 | exports.__consolidate_engine_render = function(filename, options, cb) {
56 | var eng;
57 | eng = options.cache ? e : cacheless_engine;
58 | return eng.run(filename, options, function(err, res) {
59 | return cb(err, res);
60 | });
61 | };
62 |
63 | exports.str_render = exports.strRender = function(template_str, options, cb) {
64 | var err, ref1, res, v;
65 | v = new view(template_str, options);
66 | ref1 = v.run(options), err = ref1[0], res = ref1[1];
67 | return cb(err, res);
68 | };
69 |
70 | exports.compile = require('./lib/view').expressCompile;
71 |
72 | exports.configurable_compile = function(source, opts) {
73 | var err, header, output, v;
74 | opts = opts || {};
75 | opts.headers = opts.headers != null ? opts.headers : true;
76 | opts.filename = opts.filename || null;
77 | opts.to_coffee = opts.to_coffee || false;
78 | err = null;
79 | v = new view(source, {
80 | filename: opts.filename,
81 | bundlePath: opts.filename,
82 | browserMode: true
83 | });
84 | if (opts.to_coffee) {
85 | output = v.toCoffee();
86 | } else {
87 | output = v.toJavaScript();
88 | }
89 | if (v.error) {
90 | throw v.error.e;
91 | }
92 | if (opts.headers) {
93 | header = getCommonHeadersJs(true, true, true);
94 | if (opts.coffee) {
95 | output = "`" + header + "`\n\n" + output;
96 | } else {
97 | output = header + "\n;\n" + output;
98 | }
99 | }
100 | return output;
101 | };
102 |
103 | }).call(this);
104 |
--------------------------------------------------------------------------------
/index.coffee:
--------------------------------------------------------------------------------
1 | # expose the render function
2 | {engine} = require('./lib/engine')
3 | {view, getCommonHeaders, getCommonHeadersJs} = require('./lib/view')
4 |
5 | exports.engine = engine
6 | exports.view = view
7 | exports.getCommonHeaders = getCommonHeaders
8 | exports.getCommonHeadersJs = getCommonHeadersJs
9 |
10 | exports.expressEngine = e = new engine { verbose: false, prettyPrintErrors: true }
11 | exports.render = e.run
12 | cacheless_engine = new engine { verbose: false, prettyPrintErrors: true, cache: false}
13 |
14 | # given a template string, returns a function that can be called
15 | # on an object to render it.
16 | # --------------------------------------------
17 |
18 | exports.compileStr = (template_str, options) ->
19 | v = new view template_str, options
20 | return (x) -> v.run x
21 |
22 | # express 3.x support from a custom engine;
23 | # this function takes a toffee engine
24 | # and returns a function that matches the __express
25 | # standard.
26 | # --------------------------------------------
27 |
28 | to_express = exports.toExpress = (eng) ->
29 | return (filename, options, cb) ->
30 | eng.run filename, options, (err, res) ->
31 | if err
32 | if typeof(err) is "string"
33 | err = new Error err
34 | cb err
35 | else
36 | cb null, res
37 |
38 | # express 3.x support using the default engine
39 | # --------------------------------------------
40 |
41 | __express = exports.__express = to_express e
42 |
43 | # consolidate.js support, which doesn't want caching on by default
44 | # --------------------------------------------
45 |
46 | exports.__consolidate_engine_render = (filename, options, cb) ->
47 | eng = if options.cache then e else cacheless_engine
48 | eng.run filename, options, (err, res) ->
49 | cb err, res
50 |
51 | # consolidate.js wants this, but it might generally be useful
52 | # --------------------------------------------
53 |
54 | exports.str_render = exports.strRender = (template_str, options, cb) ->
55 | v = new view template_str, options
56 | [err, res] = v.run options
57 | cb err, res
58 |
59 | # express 2.x support
60 | # --------------------------------------------
61 |
62 | exports.compile = require('./lib/view').expressCompile
63 |
64 | # better support for string compiling
65 | # --------------------------------------------
66 |
67 | exports.configurable_compile = (source, opts) ->
68 | opts = opts or {}
69 | opts.headers = if opts.headers? then opts.headers else true
70 | opts.filename = opts.filename or null
71 | opts.to_coffee = opts.to_coffee or false
72 | err = null
73 |
74 | # this compiles an individual template that you've read while recursing:
75 | v = new view source, {
76 | filename: opts.filename
77 | bundlePath: opts.filename
78 | browserMode: true
79 | }
80 | if opts.to_coffee
81 | output = v.toCoffee()
82 | else
83 | output = v.toJavaScript()
84 | if v.error then throw v.error.e
85 | if opts.headers
86 | header = getCommonHeadersJs true, true, true
87 | if opts.coffee then output = "`#{header}`\n\n#{output}"
88 | else output = "#{header}\n;\n#{output}"
89 | return output
90 |
--------------------------------------------------------------------------------
/lib/coffee-script/cake.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
4 |
5 | fs = require('fs');
6 |
7 | path = require('path');
8 |
9 | helpers = require('./helpers');
10 |
11 | optparse = require('./optparse');
12 |
13 | CoffeeScript = require('./coffee-script');
14 |
15 | tasks = {};
16 |
17 | options = {};
18 |
19 | switches = [];
20 |
21 | oparse = null;
22 |
23 | helpers.extend(global, {
24 | task: function(name, description, action) {
25 | var _ref;
26 | if (!action) {
27 | _ref = [description, action], action = _ref[0], description = _ref[1];
28 | }
29 | return tasks[name] = {
30 | name: name,
31 | description: description,
32 | action: action
33 | };
34 | },
35 | option: function(letter, flag, description) {
36 | return switches.push([letter, flag, description]);
37 | },
38 | invoke: function(name) {
39 | if (!tasks[name]) {
40 | missingTask(name);
41 | }
42 | return tasks[name].action(options);
43 | }
44 | });
45 |
46 | exports.run = function() {
47 | var arg, args, _i, _len, _ref, _results;
48 | global.__originalDirname = fs.realpathSync('.');
49 | process.chdir(cakefileDirectory(__originalDirname));
50 | args = process.argv.slice(2);
51 | CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
52 | filename: 'Cakefile'
53 | });
54 | oparse = new optparse.OptionParser(switches);
55 | if (!args.length) {
56 | return printTasks();
57 | }
58 | try {
59 | options = oparse.parse(args);
60 | } catch (e) {
61 | return fatalError("" + e);
62 | }
63 | _ref = options["arguments"];
64 | _results = [];
65 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
66 | arg = _ref[_i];
67 | _results.push(invoke(arg));
68 | }
69 | return _results;
70 | };
71 |
72 | printTasks = function() {
73 | var cakefilePath, desc, name, relative, spaces, task;
74 | relative = path.relative || path.resolve;
75 | cakefilePath = path.join(relative(__originalDirname, process.cwd()), 'Cakefile');
76 | console.log("" + cakefilePath + " defines the following tasks:\n");
77 | for (name in tasks) {
78 | task = tasks[name];
79 | spaces = 20 - name.length;
80 | spaces = spaces > 0 ? Array(spaces + 1).join(' ') : '';
81 | desc = task.description ? "# " + task.description : '';
82 | console.log("cake " + name + spaces + " " + desc);
83 | }
84 | if (switches.length) {
85 | return console.log(oparse.help());
86 | }
87 | };
88 |
89 | fatalError = function(message) {
90 | console.error(message + '\n');
91 | console.log('To see a list of all tasks/options, run "cake"');
92 | return process.exit(1);
93 | };
94 |
95 | missingTask = function(task) {
96 | return fatalError("No such task: " + task);
97 | };
98 |
99 | cakefileDirectory = function(dir) {
100 | var parent;
101 | if (path.existsSync(path.join(dir, 'Cakefile'))) {
102 | return dir;
103 | }
104 | parent = path.normalize(path.join(dir, '..'));
105 | if (parent !== dir) {
106 | return cakefileDirectory(parent);
107 | }
108 | throw new Error("Cakefile not found in " + (process.cwd()));
109 | };
110 |
111 | }).call(this);
112 |
--------------------------------------------------------------------------------
/test/generate_express_test.coffee:
--------------------------------------------------------------------------------
1 | {spawn, exec} = require 'child_process'
2 | fs = require 'fs'
3 | path = require 'path'
4 | coffee = require 'coffee-script'
5 |
6 |
7 | generateExpressTest = (cb) ->
8 |
9 | proc = spawn path.join(__dirname,"../node_modules/.bin/coffee"), ['./src/command_line.coffee', '-n', './test/cases', '-o', './test/express4/public/javascripts/test_cases.js']
10 | proc.stderr.on 'data', (buffer) -> console.log buffer.toString()
11 | proc.stdout.on 'data', (buffer) -> console.log buffer.toString()
12 | proc.on 'exit', (status) ->
13 | if status isnt 0
14 | console.log "Error running command line. #{status}"
15 | process.exit 1
16 | cb() if typeof cb is 'function'
17 |
18 | {getCommonHeadersJs} = require '../lib/view'
19 | headers = getCommonHeadersJs true, true
20 | fs.writeFileSync "./test/express4/public/javascripts/toffee.js", headers, "utf8"
21 |
22 | # generate an index page that tests them all
23 |
24 | test_page = """
25 |
26 |
27 | Testing Toffee in the Browser
28 |
29 |
30 |
31 |
43 |
49 |
50 |
51 |
52 | | FILE | EXPECTED OUTPUT | SERVER RENDER | BROWSER RENDER |
53 | """
54 |
55 | case_dirs = fs.readdirSync "./test/cases/"
56 |
57 | for dir,i in case_dirs
58 | expected_output = fs.readFileSync "./test/cases/#{dir}/output.toffee", "utf8"
59 | if fs.existsSync "./test/cases/#{dir}/vars.coffee"
60 | coffee_vars = fs.readFileSync "./test/cases/#{dir}/vars.coffee", "utf8"
61 | js_vars = coffee.compile(coffee_vars, {bare: true}).replace(/;[ \n]*$/,'')
62 | else if fs.existsSync "./test/cases/#{dir}/vars.js"
63 | coffee_vars = fs.readFileSync "./test/cases/#{dir}/vars.js", "utf8"
64 | js_vars = coffee_vars;
65 | else
66 | if dir == "render_no_args"
67 | coffee_vars = ""
68 | js_vars = ""
69 | else
70 | coffee_vars = "{}"
71 | js_vars = "{}"
72 | rid = i
73 | test_page += """
74 | \n\n\n
75 |
76 | | #{dir} |
77 | #{expected_output} |
78 | \#{partial '../../cases/#{dir}/input.toffee', #{coffee_vars}} |
79 | |
80 |
81 |
85 | \n\n\n
86 | """
87 |
88 | test_page += """
89 |
90 |
91 |
92 | """
93 | fs.writeFileSync "./test/express4/views/index.toffee", test_page, "utf8"
94 |
95 | exports.generate = generateExpressTest
96 |
97 |
--------------------------------------------------------------------------------
/test/run_cases.iced:
--------------------------------------------------------------------------------
1 | {engine} = require '../lib/engine'
2 | fs = require 'fs'
3 | path = require 'path'
4 | Browser = require 'zombie'
5 | coffee = require 'coffee-script'
6 | tablify = require 'tablify'
7 | colors = require 'colors'
8 | jsdiff = require 'diff'
9 |
10 | regular_engine = new engine({
11 | verbose: false
12 | prettyPrintErrors: false
13 | })
14 |
15 | # ---------------------------------------------------------------
16 |
17 | MULTI_RUNS = 50
18 |
19 | file_cache = {}
20 |
21 | # ---------------------------------------------------------------
22 |
23 | read_file_sync = (fname) ->
24 | if not file_cache[fname]?
25 | file_cache[fname] = fs.readFileSync fname, "utf8"
26 | return file_cache[fname]
27 |
28 | # ---------------------------------------------------------------
29 |
30 | run_case_dir = (eng, dir, cb) ->
31 | start = Date.now()
32 | expected = read_file_sync "#{dir}/output.toffee"
33 | existsSync = if path.existsSync? then path.existsSync else fs.existsSync
34 | if existsSync "#{dir}/vars.coffee"
35 | txt = read_file_sync "#{dir}/vars.coffee"
36 | vars = coffee.compile(txt, {bare: true})
37 | vars = eval "#{vars}"
38 | else if existsSync "#{dir}/vars.js"
39 | vars = read_file_sync "#{dir}/vars.js"
40 | vars = eval "(#{vars})"
41 | else
42 | vars = {}
43 | vars["rand_#{Math.random()}"] = ("foo" for i in [0...(~~(20000*Math.random()))]).join ""
44 | await eng.run "#{dir}/input.toffee", vars, defer err, res
45 | time_ms = Date.now() - start
46 | if err
47 | cb err, time_ms
48 | else
49 | if res isnt expected
50 | diff = jsdiff.diffLines res, expected
51 | delta = ""
52 | diff.forEach (part) ->
53 | c = if part.added then 'green' else if part.removed then 'red' else 'grey'
54 | v = part.value
55 | delta += v[c]
56 | cb "Failure in case #{dir}." +
57 | "#{delta}", time_ms
58 | else
59 | cb null, time_ms
60 |
61 | run_all_case_dirs = (eng, cb) ->
62 | start = Date.now()
63 | case_dirs = fs.readdirSync "#{__dirname}/cases/"
64 | for dir in case_dirs
65 | await run_case_dir eng, "#{__dirname}/cases/#{dir}", defer err, ms
66 | if err
67 | console.log err
68 | process.exit 1
69 | cb null, (Date.now() - start), case_dirs.length
70 |
71 | run_multiple_runs = (eng, num_runs, cb) ->
72 | total_tests = 0
73 | start = Date.now()
74 | for i in [0...num_runs]
75 | await setTimeout defer(), 1
76 | await run_all_case_dirs regular_engine, defer err, time, tests_run
77 | total_tests += tests_run
78 | cb null, (Date.now() - start), total_tests
79 |
80 | run_express_test = (cb) ->
81 | require('./express4/app').run ->
82 | browser = new Browser()
83 | browser.visit 'http://127.0.0.1:3033', (e) ->
84 | if e
85 | console.log e
86 | $ = browser.window.$
87 | successes = $('.success').length
88 | fails = $('.fail').length
89 | if (fails is 0) and (successes > 0)
90 | console.log "Express SUCCESS: #{successes} succeeded, #{fails} failed"
91 | return cb()
92 | console.log "BROWSER ERROR! Server left running at http://localhost:3033 for your convenience"
93 |
94 | # ----------------------------------------------------------------
95 | go = ->
96 | await run_all_case_dirs regular_engine, defer err, time, tests_run
97 | console.log "Regular Engine: SUCCESS for #{tests_run} cold tests in #{time}ms (#{(time/tests_run).toFixed 2}ms/test)"
98 | await run_multiple_runs regular_engine, MULTI_RUNS, defer err, time, tests_run
99 | console.log "Regular Engine: SUCCESS for #{tests_run} hot tests in #{time}ms (#{(time/tests_run).toFixed 2}ms/test)"
100 | await run_express_test defer()
101 | process.exit 0
102 |
103 | if not module.parent?
104 | go()
105 |
106 | else exports.test = go
107 |
--------------------------------------------------------------------------------
/lib/coffee-script/scope.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var Scope, extend, last, _ref;
4 |
5 | _ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
6 |
7 | exports.Scope = Scope = (function() {
8 |
9 | Scope.name = 'Scope';
10 |
11 | Scope.root = null;
12 |
13 | function Scope(parent, expressions, method) {
14 | this.parent = parent;
15 | this.expressions = expressions;
16 | this.method = method;
17 | this.variables = [
18 | {
19 | name: 'arguments',
20 | type: 'arguments'
21 | }
22 | ];
23 | this.positions = {};
24 | if (!this.parent) {
25 | Scope.root = this;
26 | }
27 | }
28 |
29 | Scope.prototype.add = function(name, type, immediate) {
30 | if (this.shared && !immediate) {
31 | return this.parent.add(name, type, immediate);
32 | }
33 | if (Object.prototype.hasOwnProperty.call(this.positions, name)) {
34 | return this.variables[this.positions[name]].type = type;
35 | } else {
36 | return this.positions[name] = this.variables.push({
37 | name: name,
38 | type: type
39 | }) - 1;
40 | }
41 | };
42 |
43 | Scope.prototype.find = function(name, options) {
44 | if (this.check(name, options)) {
45 | return true;
46 | }
47 | this.add(name, 'var');
48 | return false;
49 | };
50 |
51 | Scope.prototype.parameter = function(name) {
52 | if (this.shared && this.parent.check(name, true)) {
53 | return;
54 | }
55 | return this.add(name, 'param');
56 | };
57 |
58 | Scope.prototype.check = function(name, immediate) {
59 | var found, _ref1;
60 | found = !!this.type(name);
61 | if (found || immediate) {
62 | return found;
63 | }
64 | return !!((_ref1 = this.parent) != null ? _ref1.check(name) : void 0);
65 | };
66 |
67 | Scope.prototype.temporary = function(name, index) {
68 | if (name.length > 1) {
69 | return '_' + name + (index > 1 ? index - 1 : '');
70 | } else {
71 | return '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a');
72 | }
73 | };
74 |
75 | Scope.prototype.type = function(name) {
76 | var v, _i, _len, _ref1;
77 | _ref1 = this.variables;
78 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
79 | v = _ref1[_i];
80 | if (v.name === name) {
81 | return v.type;
82 | }
83 | }
84 | return null;
85 | };
86 |
87 | Scope.prototype.freeVariable = function(name, reserve) {
88 | var index, temp;
89 | if (reserve == null) {
90 | reserve = true;
91 | }
92 | index = 0;
93 | while (this.check((temp = this.temporary(name, index)))) {
94 | index++;
95 | }
96 | if (reserve) {
97 | this.add(temp, 'var', true);
98 | }
99 | return temp;
100 | };
101 |
102 | Scope.prototype.assign = function(name, value) {
103 | this.add(name, {
104 | value: value,
105 | assigned: true
106 | }, true);
107 | return this.hasAssignments = true;
108 | };
109 |
110 | Scope.prototype.hasDeclarations = function() {
111 | return !!this.declaredVariables().length;
112 | };
113 |
114 | Scope.prototype.declaredVariables = function() {
115 | var realVars, tempVars, v, _i, _len, _ref1;
116 | realVars = [];
117 | tempVars = [];
118 | _ref1 = this.variables;
119 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
120 | v = _ref1[_i];
121 | if (v.type === 'var') {
122 | (v.name.charAt(0) === '_' ? tempVars : realVars).push(v.name);
123 | }
124 | }
125 | return realVars.sort().concat(tempVars.sort());
126 | };
127 |
128 | Scope.prototype.assignedVariables = function() {
129 | var v, _i, _len, _ref1, _results;
130 | _ref1 = this.variables;
131 | _results = [];
132 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
133 | v = _ref1[_i];
134 | if (v.type.assigned) {
135 | _results.push("" + v.name + " = " + v.type.value);
136 | }
137 | }
138 | return _results;
139 | };
140 |
141 | return Scope;
142 |
143 | })();
144 |
145 | }).call(this);
146 |
--------------------------------------------------------------------------------
/lib/coffee-script/optparse.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;
4 |
5 | exports.OptionParser = OptionParser = (function() {
6 |
7 | OptionParser.name = 'OptionParser';
8 |
9 | function OptionParser(rules, banner) {
10 | this.banner = banner;
11 | this.rules = buildRules(rules);
12 | }
13 |
14 | OptionParser.prototype.parse = function(args) {
15 | var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, seenNonOptionArg, skippingArgument, value, _i, _j, _len, _len1, _ref;
16 | options = {
17 | "arguments": []
18 | };
19 | skippingArgument = false;
20 | originalArgs = args;
21 | args = normalizeArguments(args);
22 | for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) {
23 | arg = args[i];
24 | if (skippingArgument) {
25 | skippingArgument = false;
26 | continue;
27 | }
28 | if (arg === '--') {
29 | pos = originalArgs.indexOf('--');
30 | options["arguments"] = options["arguments"].concat(originalArgs.slice(pos + 1));
31 | break;
32 | }
33 | isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG));
34 | seenNonOptionArg = options["arguments"].length > 0;
35 | if (!seenNonOptionArg) {
36 | matchedRule = false;
37 | _ref = this.rules;
38 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
39 | rule = _ref[_j];
40 | if (rule.shortFlag === arg || rule.longFlag === arg) {
41 | value = true;
42 | if (rule.hasArgument) {
43 | skippingArgument = true;
44 | value = args[i + 1];
45 | }
46 | options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value;
47 | matchedRule = true;
48 | break;
49 | }
50 | }
51 | if (isOption && !matchedRule) {
52 | throw new Error("unrecognized option: " + arg);
53 | }
54 | }
55 | if (seenNonOptionArg || !isOption) {
56 | options["arguments"].push(arg);
57 | }
58 | }
59 | return options;
60 | };
61 |
62 | OptionParser.prototype.help = function() {
63 | var letPart, lines, rule, spaces, _i, _len, _ref;
64 | lines = [];
65 | if (this.banner) {
66 | lines.unshift("" + this.banner + "\n");
67 | }
68 | _ref = this.rules;
69 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
70 | rule = _ref[_i];
71 | spaces = 15 - rule.longFlag.length;
72 | spaces = spaces > 0 ? Array(spaces + 1).join(' ') : '';
73 | letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' ';
74 | lines.push(' ' + letPart + rule.longFlag + spaces + rule.description);
75 | }
76 | return "\n" + (lines.join('\n')) + "\n";
77 | };
78 |
79 | return OptionParser;
80 |
81 | })();
82 |
83 | LONG_FLAG = /^(--\w[\w\-]*)/;
84 |
85 | SHORT_FLAG = /^(-\w)$/;
86 |
87 | MULTI_FLAG = /^-(\w{2,})/;
88 |
89 | OPTIONAL = /\[(\w+(\*?))\]/;
90 |
91 | buildRules = function(rules) {
92 | var tuple, _i, _len, _results;
93 | _results = [];
94 | for (_i = 0, _len = rules.length; _i < _len; _i++) {
95 | tuple = rules[_i];
96 | if (tuple.length < 3) {
97 | tuple.unshift(null);
98 | }
99 | _results.push(buildRule.apply(null, tuple));
100 | }
101 | return _results;
102 | };
103 |
104 | buildRule = function(shortFlag, longFlag, description, options) {
105 | var match;
106 | if (options == null) {
107 | options = {};
108 | }
109 | match = longFlag.match(OPTIONAL);
110 | longFlag = longFlag.match(LONG_FLAG)[1];
111 | return {
112 | name: longFlag.substr(2),
113 | shortFlag: shortFlag,
114 | longFlag: longFlag,
115 | description: description,
116 | hasArgument: !!(match && match[1]),
117 | isList: !!(match && match[2])
118 | };
119 | };
120 |
121 | normalizeArguments = function(args) {
122 | var arg, l, match, result, _i, _j, _len, _len1, _ref;
123 | args = args.slice(0);
124 | result = [];
125 | for (_i = 0, _len = args.length; _i < _len; _i++) {
126 | arg = args[_i];
127 | if (match = arg.match(MULTI_FLAG)) {
128 | _ref = match[1].split('');
129 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
130 | l = _ref[_j];
131 | result.push('-' + l);
132 | }
133 | } else {
134 | result.push(arg);
135 | }
136 | }
137 | return result;
138 | };
139 |
140 | }).call(this);
141 |
--------------------------------------------------------------------------------
/lib/coffee-script/coffee-script.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var Lexer, RESERVED, compile, fs, lexer, parser, path, vm, _ref,
4 | __hasProp = {}.hasOwnProperty;
5 |
6 | fs = require('fs');
7 |
8 | path = require('path');
9 |
10 | _ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED;
11 |
12 | parser = require('./parser').parser;
13 |
14 | vm = require('vm');
15 |
16 | if (require.extensions) {
17 | require.extensions['.coffee'] = function(module, filename) {
18 | var content;
19 | content = compile(fs.readFileSync(filename, 'utf8'), {
20 | filename: filename
21 | });
22 | return module._compile(content, filename);
23 | };
24 | } else if (require.registerExtension) {
25 | require.registerExtension('.coffee', function(content) {
26 | return compile(content);
27 | });
28 | }
29 |
30 | exports.VERSION = '1.3.1';
31 |
32 | exports.RESERVED = RESERVED;
33 |
34 | exports.helpers = require('./helpers');
35 |
36 | exports.compile = compile = function(code, options) {
37 | var header, js, merge;
38 | if (options == null) {
39 | options = {};
40 | }
41 | merge = exports.helpers.merge;
42 | try {
43 | js = (parser.parse(lexer.tokenize(code))).compile(options);
44 | if (!options.header) {
45 | return js;
46 | }
47 | } catch (err) {
48 | if (options.filename) {
49 | err.message = "In " + options.filename + ", " + err.message;
50 | }
51 | throw err;
52 | }
53 | header = "Generated by CoffeeScript " + this.VERSION;
54 | return "// " + header + "\n" + js;
55 | };
56 |
57 | exports.tokens = function(code, options) {
58 | return lexer.tokenize(code, options);
59 | };
60 |
61 | exports.nodes = function(source, options) {
62 | if (typeof source === 'string') {
63 | return parser.parse(lexer.tokenize(source, options));
64 | } else {
65 | return parser.parse(source);
66 | }
67 | };
68 |
69 | exports.run = function(code, options) {
70 | var mainModule;
71 | if (options == null) {
72 | options = {};
73 | }
74 | mainModule = require.main;
75 | mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
76 | mainModule.moduleCache && (mainModule.moduleCache = {});
77 | mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename)));
78 | if (path.extname(mainModule.filename) !== '.coffee' || require.extensions) {
79 | return mainModule._compile(compile(code, options), mainModule.filename);
80 | } else {
81 | return mainModule._compile(code, mainModule.filename);
82 | }
83 | };
84 |
85 | exports["eval"] = function(code, options) {
86 | var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref1, _ref2, _require;
87 | if (options == null) {
88 | options = {};
89 | }
90 | if (!(code = code.trim())) {
91 | return;
92 | }
93 | Script = vm.Script;
94 | if (Script) {
95 | if (options.sandbox != null) {
96 | if (options.sandbox instanceof Script.createContext().constructor) {
97 | sandbox = options.sandbox;
98 | } else {
99 | sandbox = Script.createContext();
100 | _ref1 = options.sandbox;
101 | for (k in _ref1) {
102 | if (!__hasProp.call(_ref1, k)) continue;
103 | v = _ref1[k];
104 | sandbox[k] = v;
105 | }
106 | }
107 | sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox;
108 | } else {
109 | sandbox = global;
110 | }
111 | sandbox.__filename = options.filename || 'eval';
112 | sandbox.__dirname = path.dirname(sandbox.__filename);
113 | if (!(sandbox !== global || sandbox.module || sandbox.require)) {
114 | Module = require('module');
115 | sandbox.module = _module = new Module(options.modulename || 'eval');
116 | sandbox.require = _require = function(path) {
117 | return Module._load(path, _module, true);
118 | };
119 | _module.filename = sandbox.__filename;
120 | _ref2 = Object.getOwnPropertyNames(require);
121 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
122 | r = _ref2[_i];
123 | if (r !== 'paths') {
124 | _require[r] = require[r];
125 | }
126 | }
127 | _require.paths = _module.paths = Module._nodeModulePaths(process.cwd());
128 | _require.resolve = function(request) {
129 | return Module._resolveFilename(request, _module);
130 | };
131 | }
132 | }
133 | o = {};
134 | for (k in options) {
135 | if (!__hasProp.call(options, k)) continue;
136 | v = options[k];
137 | o[k] = v;
138 | }
139 | o.bare = true;
140 | js = compile(code, o);
141 | if (sandbox === global) {
142 | return vm.runInThisContext(js);
143 | } else {
144 | return vm.runInContext(js, sandbox);
145 | }
146 | };
147 |
148 | lexer = new Lexer;
149 |
150 | parser.lexer = {
151 | lex: function() {
152 | var tag, _ref1;
153 | _ref1 = this.tokens[this.pos++] || [''], tag = _ref1[0], this.yytext = _ref1[1], this.yylineno = _ref1[2];
154 | return tag;
155 | },
156 | setInput: function(tokens) {
157 | this.tokens = tokens;
158 | return this.pos = 0;
159 | },
160 | upcomingInput: function() {
161 | return "";
162 | }
163 | };
164 |
165 | parser.yy = require('./nodes');
166 |
167 | }).call(this);
168 |
--------------------------------------------------------------------------------
/src/command_line.coffee:
--------------------------------------------------------------------------------
1 | fs = require 'fs'
2 | path = require 'path'
3 | {view, getCommonHeadersJs} = require '../lib/view'
4 | program = require 'commander'
5 | mkdirp = require 'mkdirp'
6 |
7 | # -----------------------------------------------------------------------------
8 |
9 | getVersionNumber = ->
10 | p = fs.readFileSync "#{__dirname}/../package.json", "utf8"
11 | o = JSON.parse p
12 | o.version
13 |
14 | # -----------------------------------------------------------------------------
15 |
16 | program.on '--help', ->
17 | console.log "
18 | \n Examples:
19 | \n
20 | \n toffee views # recurses through views and builds views.js
21 | \n toffee foo.toffee # builds foo.js
22 | \n toffee views -o templates # builds templates.js
23 | \n toffee -p foo.toffee # outputs JS to stdout
24 | \n
25 | \n
26 | \n Then use in your :
27 | \n
28 | \n
29 | \n
33 | \n
34 | "
35 |
36 | program.version(getVersionNumber())
37 | .option('-o, --output [path]', 'file (bundles all output into a single .js)')
38 | .option('-d, --output_dir [path]', 'compiles templates into parallel .js files')
39 | .option('-p, --print', 'print to stdout')
40 | .option('-c, --coffee', 'output to CoffeeScript (not JS)')
41 | .option('-b, --bundle_path [path]', 'bundle_path (instead of "/") for templates')
42 | .option('-n, --no_headers', 'exclude boilerplate toffee (requires toffee.js included separately)')
43 | .parse process.argv
44 |
45 | # -----------------------------------------------------------------------------
46 |
47 | compile = (start_path, full_path) ->
48 | ###
49 | e.g., if start_path is /foo/bar
50 | and path is /foo/bar/car/thing.toffee
51 | ###
52 | source = fs.readFileSync full_path, 'utf8'
53 | bundle_path = full_path[start_path.length...]
54 |
55 | if start_path is full_path
56 | bundle_path = "/" + path.basename full_path
57 |
58 | if program.bundle_path
59 | bundle_path = path.normalize "#{program.bundle_path}/#{bundle_path}"
60 |
61 | v = new view source,
62 | fileName: full_path
63 | bundlePath: bundle_path
64 | browserMode: true
65 | if program.coffee
66 | output = v.toCoffee()
67 | else
68 | output = v.toJavaScript()
69 | if v.error
70 | process.stderr.write v.error.getPrettyPrintText()
71 | process.exit 1
72 |
73 | [output, bundle_path]
74 |
75 | # -----------------------------------------------------------------------------
76 |
77 | recurseRun = (start_path, curr_path, out_text) ->
78 | stats = fs.statSync curr_path
79 | if stats.isDirectory()
80 | files = fs.readdirSync curr_path
81 | for file in files
82 | sub_path = path.normalize "#{curr_path}/#{file}"
83 | if file.match /\.toffee$/
84 | out_text = recurseRun start_path, sub_path, out_text
85 | else if not (file in ['.','..'])
86 | sub_stats = fs.statSync sub_path
87 | if sub_stats.isDirectory()
88 | out_text = recurseRun start_path, sub_path, out_text
89 | else
90 | comp = compile start_path, curr_path
91 | out_text += "\n;\n" + comp[0]
92 | if program.output_dir
93 | file_out_path = path.normalize "#{program.output_dir}/#{comp[1]}"
94 | file_out_path = "#{path.dirname file_out_path}/#{path.basename file_out_path, '.toffee'}"
95 | file_out_path += if program.coffee then '.coffee' else '.js'
96 | if not program.print
97 | console.log "Outputting #{file_out_path}"
98 | mkdirp.sync path.dirname file_out_path
99 | fs.writeFileSync file_out_path, maybeAttachHeaders(comp[0]), "utf8"
100 |
101 | return out_text
102 |
103 | # -----------------------------------------------------------------------------
104 |
105 | maybeAttachHeaders = (pre_output) ->
106 | if program.no_headers
107 | return pre_output
108 | else
109 | header_out = getCommonHeadersJs true, true, true
110 | if program.coffee
111 | return "`#{header_out}`\n\n#{pre_output}"
112 | else
113 | return "#{header_out}\n;\n#{pre_output}"
114 |
115 | # -----------------------------------------------------------------------------
116 |
117 | run = exports.run = ->
118 |
119 | if program.args.length isnt 1
120 | console.log "Unexpected input. toffee --help for examples"
121 | console.log program.args
122 | process.exit 1
123 | else
124 | try
125 | start_path = fs.realpathSync program.args[0]
126 | catch e
127 | console.log "Input file/path not found. toffee --help for examples"
128 | process.exit 1
129 |
130 | if program.output_dir
131 | try
132 | mkdirp.sync program.output_dir
133 | catch e
134 | console.log "Couldn't make/use #{program.output_dir}; #{e}"
135 | process.exit 1
136 |
137 | start_path = path.normalize start_path
138 | template_out = recurseRun start_path, start_path, ''
139 | out_text = maybeAttachHeaders template_out
140 |
141 | if program.print
142 | console.log out_text
143 |
144 | if program.output
145 | try
146 | console.log "Writing #{program.output}"
147 | fs.writeFileSync program.output, out_text, "utf8"
148 | catch e
149 | console.log e
150 | process.exit 1
151 |
152 |
153 | # -----------------------------------------------------------------------------
154 |
155 | if require.main is module
156 | run()
157 |
--------------------------------------------------------------------------------
/test/express4_error_handling/public/javascripts/toffee.js:
--------------------------------------------------------------------------------
1 | var toffee;
2 |
3 |
4 |
5 | if (typeof toffee === "undefined" || toffee === null) {
6 | toffee = {};
7 | }
8 |
9 | if (!toffee.templates) {
10 | toffee.templates = {};
11 | }
12 |
13 | toffee.states = {
14 | "TOFFEE": 1,
15 | "COFFEE": 2
16 | };
17 |
18 | toffee.__json = function(locals, o) {
19 | if (o == null) {
20 | return "null";
21 | } else {
22 | return "" + JSON.stringify(o).replace(//g, '\\u003E').replace(/&/g, '\\u0026');
23 | }
24 | };
25 |
26 | toffee.__raw = function(locals, o) {
27 | return o;
28 | };
29 |
30 | toffee.__html = function(locals, o) {
31 | return ("" + o).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
32 | };
33 |
34 | toffee.__escape = function(locals, o) {
35 | var ae;
36 | if (locals.__toffee.autoEscape != null) {
37 | ae = locals.__toffee.autoEscape;
38 | } else if (true) {
39 | ae = true;
40 | } else {
41 | ae = true;
42 | }
43 | if (ae) {
44 | if (o === void 0) {
45 | return '';
46 | }
47 | if ((o != null) && (typeof o) === "object") {
48 | return locals.json(o);
49 | }
50 | return locals.html(o);
51 | }
52 | return o;
53 | };
54 |
55 | toffee.__augmentLocals = function(locals, bundle_path) {
56 | var _l, _t;
57 | _l = locals;
58 | _t = _l.__toffee = {
59 | out: []
60 | };
61 | if (_l.print == null) {
62 | _l.print = function(o) {
63 | return toffee.__print(_l, o);
64 | };
65 | }
66 | if (_l.json == null) {
67 | _l.json = function(o) {
68 | return toffee.__json(_l, o);
69 | };
70 | }
71 | if (_l.raw == null) {
72 | _l.raw = function(o) {
73 | return toffee.__raw(_l, o);
74 | };
75 | }
76 | if (_l.html == null) {
77 | _l.html = function(o) {
78 | return toffee.__html(_l, o);
79 | };
80 | }
81 | if (_l.escape == null) {
82 | _l.escape = function(o) {
83 | return toffee.__escape(_l, o);
84 | };
85 | }
86 | if (_l.partial == null) {
87 | _l.partial = function(path, vars) {
88 | return toffee.__partial(toffee.templates["" + bundle_path], _l, path, vars);
89 | };
90 | }
91 | if (_l.snippet == null) {
92 | _l.snippet = function(path, vars) {
93 | return toffee.__snippet(toffee.templates["" + bundle_path], _l, path, vars);
94 | };
95 | }
96 | if (_l.load == null) {
97 | _l.load = function(path, vars) {
98 | return toffee.__load(toffee.templates["" + bundle_path], _l, path, vars);
99 | };
100 | }
101 | _t.print = _l.print;
102 | _t.json = _l.json;
103 | _t.raw = _l.raw;
104 | _t.html = _l.html;
105 | _t.escape = _l.escape;
106 | _t.partial = _l.partial;
107 | _t.snippet = _l.snippet;
108 | return _t.load = _l.load;
109 | };
110 |
111 | toffee.__print = function(locals, o) {
112 | if (locals.__toffee.state === toffee.states.COFFEE) {
113 | locals.__toffee.out.push(o);
114 | return '';
115 | } else {
116 | return "" + o;
117 | }
118 | };
119 |
120 | toffee.__normalize = function(path) {
121 | var np, part, parts, _i, _len;
122 | if ((path == null) || path === "/") {
123 | return path;
124 | } else {
125 | parts = path.split("/");
126 | np = [];
127 | if (parts[0]) {
128 | np.push('');
129 | }
130 | for (_i = 0, _len = parts.length; _i < _len; _i++) {
131 | part = parts[_i];
132 | if (part === "..") {
133 | if (np.length > 1) {
134 | np.pop();
135 | } else {
136 | np.push(part);
137 | }
138 | } else {
139 | if (part !== ".") {
140 | np.push(part);
141 | }
142 | }
143 | }
144 | path = np.join("/");
145 | if (!path) {
146 | path = "/";
147 | }
148 | return path;
149 | }
150 | };
151 |
152 | toffee.__partial = function(parent_tmpl, parent_locals, path, vars) {
153 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
154 | return toffee.__inlineInclude(path, vars, parent_locals);
155 | };
156 |
157 | toffee.__snippet = function(parent_tmpl, parent_locals, path, vars) {
158 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
159 | vars = vars != null ? vars : {};
160 | vars.__toffee = vars.__toffee || {};
161 | vars.__toffee.noInheritance = true;
162 | return toffee.__inlineInclude(path, vars, parent_locals);
163 | };
164 |
165 | toffee.__load = function(parent_tmpl, parent_locals, path, vars) {
166 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
167 | vars = vars != null ? vars : {};
168 | vars.__toffee = vars.__toffee || {};
169 | vars.__toffee.repress = true;
170 | return toffee.__inlineInclude(path, vars, parent_locals);
171 | };
172 |
173 | toffee.__inlineInclude = function(path, locals, parent_locals) {
174 | var k, options, res, reserved, v, _i, _len, _ref, _ref1;
175 | options = locals || {};
176 | options.passback = {};
177 | options.__toffee = options.__toffee || {};
178 | reserved = {};
179 | _ref = ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"];
180 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
181 | k = _ref[_i];
182 | reserved[k] = true;
183 | }
184 | if (!options.__toffee.noInheritance) {
185 | for (k in parent_locals) {
186 | v = parent_locals[k];
187 | if ((locals != null ? locals[k] : void 0) == null) {
188 | if (reserved[k] == null) {
189 | options[k] = v;
190 | }
191 | }
192 | }
193 | }
194 | if (!toffee.templates[path]) {
195 | return "Inline toffee include: Could not find " + path;
196 | } else {
197 | res = toffee.templates[path].pub(options);
198 | _ref1 = options.passback;
199 | for (k in _ref1) {
200 | v = _ref1[k];
201 | parent_locals[k] = v;
202 | }
203 | return res;
204 | }
205 | };
206 |
--------------------------------------------------------------------------------
/lib/command_line.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var compile, fs, getCommonHeadersJs, getVersionNumber, maybeAttachHeaders, mkdirp, path, program, recurseRun, ref, run, view;
4 |
5 | fs = require('fs');
6 |
7 | path = require('path');
8 |
9 | ref = require('../lib/view'), view = ref.view, getCommonHeadersJs = ref.getCommonHeadersJs;
10 |
11 | program = require('commander');
12 |
13 | mkdirp = require('mkdirp');
14 |
15 | getVersionNumber = function() {
16 | var o, p;
17 | p = fs.readFileSync(__dirname + "/../package.json", "utf8");
18 | o = JSON.parse(p);
19 | return o.version;
20 | };
21 |
22 | program.on('--help', function() {
23 | return console.log("\n Examples: \n \n toffee views # recurses through views and builds views.js \n toffee foo.toffee # builds foo.js \n toffee views -o templates # builds templates.js \n toffee -p foo.toffee # outputs JS to stdout \n \n \n Then use in your : \n \n \n \n");
24 | });
25 |
26 | program.version(getVersionNumber()).option('-o, --output [path]', 'file (bundles all output into a single .js)').option('-d, --output_dir [path]', 'compiles templates into parallel .js files').option('-p, --print', 'print to stdout').option('-c, --coffee', 'output to CoffeeScript (not JS)').option('-b, --bundle_path [path]', 'bundle_path (instead of "/") for templates').option('-n, --no_headers', 'exclude boilerplate toffee (requires toffee.js included separately)').parse(process.argv);
27 |
28 | compile = function(start_path, full_path) {
29 |
30 | /*
31 | e.g., if start_path is /foo/bar
32 | and path is /foo/bar/car/thing.toffee
33 | */
34 | var bundle_path, output, source, v;
35 | source = fs.readFileSync(full_path, 'utf8');
36 | bundle_path = full_path.slice(start_path.length);
37 | if (start_path === full_path) {
38 | bundle_path = "/" + path.basename(full_path);
39 | }
40 | if (program.bundle_path) {
41 | bundle_path = path.normalize(program.bundle_path + "/" + bundle_path);
42 | }
43 | v = new view(source, {
44 | fileName: full_path,
45 | bundlePath: bundle_path,
46 | browserMode: true
47 | });
48 | if (program.coffee) {
49 | output = v.toCoffee();
50 | } else {
51 | output = v.toJavaScript();
52 | }
53 | if (v.error) {
54 | process.stderr.write(v.error.getPrettyPrintText());
55 | process.exit(1);
56 | }
57 | return [output, bundle_path];
58 | };
59 |
60 | recurseRun = function(start_path, curr_path, out_text) {
61 | var comp, file, file_out_path, files, i, len, stats, sub_path, sub_stats;
62 | stats = fs.statSync(curr_path);
63 | if (stats.isDirectory()) {
64 | files = fs.readdirSync(curr_path);
65 | for (i = 0, len = files.length; i < len; i++) {
66 | file = files[i];
67 | sub_path = path.normalize(curr_path + "/" + file);
68 | if (file.match(/\.toffee$/)) {
69 | out_text = recurseRun(start_path, sub_path, out_text);
70 | } else if (!(file === '.' || file === '..')) {
71 | sub_stats = fs.statSync(sub_path);
72 | if (sub_stats.isDirectory()) {
73 | out_text = recurseRun(start_path, sub_path, out_text);
74 | }
75 | }
76 | }
77 | } else {
78 | comp = compile(start_path, curr_path);
79 | out_text += "\n;\n" + comp[0];
80 | if (program.output_dir) {
81 | file_out_path = path.normalize(program.output_dir + "/" + comp[1]);
82 | file_out_path = (path.dirname(file_out_path)) + "/" + (path.basename(file_out_path, '.toffee'));
83 | file_out_path += program.coffee ? '.coffee' : '.js';
84 | if (!program.print) {
85 | console.log("Outputting " + file_out_path);
86 | }
87 | mkdirp.sync(path.dirname(file_out_path));
88 | fs.writeFileSync(file_out_path, maybeAttachHeaders(comp[0]), "utf8");
89 | }
90 | }
91 | return out_text;
92 | };
93 |
94 | maybeAttachHeaders = function(pre_output) {
95 | var header_out;
96 | if (program.no_headers) {
97 | return pre_output;
98 | } else {
99 | header_out = getCommonHeadersJs(true, true, true);
100 | if (program.coffee) {
101 | return "`" + header_out + "`\n\n" + pre_output;
102 | } else {
103 | return header_out + "\n;\n" + pre_output;
104 | }
105 | }
106 | };
107 |
108 | run = exports.run = function() {
109 | var e, out_text, start_path, template_out;
110 | if (program.args.length !== 1) {
111 | console.log("Unexpected input. toffee --help for examples");
112 | console.log(program.args);
113 | return process.exit(1);
114 | } else {
115 | try {
116 | start_path = fs.realpathSync(program.args[0]);
117 | } catch (error) {
118 | e = error;
119 | console.log("Input file/path not found. toffee --help for examples");
120 | process.exit(1);
121 | }
122 | if (program.output_dir) {
123 | try {
124 | mkdirp.sync(program.output_dir);
125 | } catch (error) {
126 | e = error;
127 | console.log("Couldn't make/use " + program.output_dir + "; " + e);
128 | process.exit(1);
129 | }
130 | }
131 | start_path = path.normalize(start_path);
132 | template_out = recurseRun(start_path, start_path, '');
133 | out_text = maybeAttachHeaders(template_out);
134 | if (program.print) {
135 | console.log(out_text);
136 | }
137 | if (program.output) {
138 | try {
139 | console.log("Writing " + program.output);
140 | return fs.writeFileSync(program.output, out_text, "utf8");
141 | } catch (error) {
142 | e = error;
143 | console.log(e);
144 | return process.exit(1);
145 | }
146 | }
147 | }
148 | };
149 |
150 | if (require.main === module) {
151 | run();
152 | }
153 |
154 | }).call(this);
155 |
--------------------------------------------------------------------------------
/toffee.js:
--------------------------------------------------------------------------------
1 | var toffee;
2 |
3 | if (typeof toffee === "undefined" || toffee === null) {
4 | toffee = {};
5 | }
6 |
7 | if (!toffee.templates) {
8 | toffee.templates = {};
9 | }
10 |
11 | toffee.states = {
12 | "TOFFEE": 1,
13 | "COFFEE": 2
14 | };
15 |
16 | toffee.__json = function(locals, o, opts) {
17 | opts || (opts = {});
18 | opts.indent || (opts.indent = "");
19 | if (o == null) {
20 | return "null";
21 | } else {
22 | return "" + JSON.stringify(o, null, opts.indent).replace(//g, '\\u003E').replace(/&/g, '\\u0026').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029').replace(/\u200e/g, '\\u200e').replace(/\u200f/g, '\\u200f').replace(/\u202a/g, '\\u202a').replace(/\u202b/g, '\\u202b').replace(/\u202c/g, '\\u202c').replace(/\u202d/g, '\\u202d').replace(/\u202e/g, '\\u202e').replace(/\u206a/g, '\\u206a').replace(/\u206b/g, '\\u206b').replace(/\u206c/g, '\\u206c').replace(/\u206d/g, '\\u206d').replace(/\u206e/g, '\\u206e').replace(/\u206f/g, '\\u206f').replace(/\u2066/g, '\\u2066').replace(/\u2067/g, '\\u2067').replace(/\u2068/g, '\\u2068').replace(/\u2069/g, '\\u2069');
23 | }
24 | };
25 |
26 | toffee.__raw = function(locals, o) {
27 | return o;
28 | };
29 |
30 | toffee.__html = function(locals, o) {
31 | return ("" + o).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/\u200e/g, '').replace(/\u200f/g, '').replace(/\u202a/g, '').replace(/\u202b/g, '').replace(/\u202c/g, '').replace(/\u202d/g, '').replace(/\u202e/g, '').replace(/\u206a/g, '').replace(/\u206b/g, '').replace(/\u206c/g, '').replace(/\u206d/g, '').replace(/\u206e/g, '').replace(/\u206f/g, '').replace(/\u2066/g, '').replace(/\u2067/g, '').replace(/\u2068/g, '').replace(/\u2069/g, '');
32 | };
33 |
34 | toffee.__escape = function(locals, o) {
35 | var ae;
36 | if (locals.__toffee.autoEscape != null) {
37 | ae = locals.__toffee.autoEscape;
38 | } else if (true) {
39 | ae = true;
40 | } else {
41 | ae = true;
42 | }
43 | if (ae) {
44 | if (o === void 0) {
45 | return '';
46 | }
47 | if ((o != null) && (typeof o) === "object") {
48 | return locals.json(o);
49 | }
50 | return locals.html(o);
51 | }
52 | return o;
53 | };
54 |
55 | toffee.__augmentLocals = function(locals, bundle_path) {
56 | var _l, _t;
57 | _l = locals;
58 | _t = _l.__toffee = {
59 | out: []
60 | };
61 | if (_l.print == null) {
62 | _l.print = function(o) {
63 | return toffee.__print(_l, o);
64 | };
65 | }
66 | if (_l.json == null) {
67 | _l.json = function(o, opts) {
68 | return toffee.__json(_l, o, opts);
69 | };
70 | }
71 | if (_l.raw == null) {
72 | _l.raw = function(o) {
73 | return toffee.__raw(_l, o);
74 | };
75 | }
76 | if (_l.html == null) {
77 | _l.html = function(o) {
78 | return toffee.__html(_l, o);
79 | };
80 | }
81 | if (_l.escape == null) {
82 | _l.escape = function(o) {
83 | return toffee.__escape(_l, o);
84 | };
85 | }
86 | if (_l.partial == null) {
87 | _l.partial = function(path, vars) {
88 | return toffee.__partial(toffee.templates["" + bundle_path], _l, path, vars);
89 | };
90 | }
91 | if (_l.snippet == null) {
92 | _l.snippet = function(path, vars) {
93 | return toffee.__snippet(toffee.templates["" + bundle_path], _l, path, vars);
94 | };
95 | }
96 | if (_l.load == null) {
97 | _l.load = function(path, vars) {
98 | return toffee.__load(toffee.templates["" + bundle_path], _l, path, vars);
99 | };
100 | }
101 | _t.print = _l.print;
102 | _t.json = _l.json;
103 | _t.raw = _l.raw;
104 | _t.html = _l.html;
105 | _t.escape = _l.escape;
106 | _t.partial = _l.partial;
107 | _t.snippet = _l.snippet;
108 | return _t.load = _l.load;
109 | };
110 |
111 | toffee.__print = function(locals, o) {
112 | if (locals.__toffee.state === toffee.states.COFFEE) {
113 | locals.__toffee.out.push(o);
114 | return '';
115 | } else {
116 | return "" + o;
117 | }
118 | };
119 |
120 | toffee.__normalize = function(path) {
121 | var np, part, parts, _i, _len;
122 | if ((path == null) || path === "/") {
123 | return path;
124 | } else {
125 | parts = path.split("/");
126 | np = [];
127 | if (parts[0]) {
128 | np.push('');
129 | }
130 | for (_i = 0, _len = parts.length; _i < _len; _i++) {
131 | part = parts[_i];
132 | if (part === "..") {
133 | if (np.length > 1) {
134 | np.pop();
135 | } else {
136 | np.push(part);
137 | }
138 | } else {
139 | if (part !== ".") {
140 | np.push(part);
141 | }
142 | }
143 | }
144 | path = np.join("/");
145 | if (!path) {
146 | path = "/";
147 | }
148 | return path;
149 | }
150 | };
151 |
152 | toffee.__partial = function(parent_tmpl, parent_locals, path, vars) {
153 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
154 | return toffee.__inlineInclude(path, vars, parent_locals);
155 | };
156 |
157 | toffee.__snippet = function(parent_tmpl, parent_locals, path, vars) {
158 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
159 | vars = vars != null ? vars : {};
160 | vars.__toffee = vars.__toffee || {};
161 | vars.__toffee.noInheritance = true;
162 | return toffee.__inlineInclude(path, vars, parent_locals);
163 | };
164 |
165 | toffee.__load = function(parent_tmpl, parent_locals, path, vars) {
166 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
167 | vars = vars != null ? vars : {};
168 | vars.__toffee = vars.__toffee || {};
169 | vars.__toffee.repress = true;
170 | return toffee.__inlineInclude(path, vars, parent_locals);
171 | };
172 |
173 | toffee.__inlineInclude = function(path, locals, parent_locals) {
174 | var k, options, res, reserved, v, _i, _len, _ref, _ref1;
175 | options = locals || {};
176 | options.passback = {};
177 | options.__toffee = options.__toffee || {};
178 | reserved = {};
179 | _ref = ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"];
180 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
181 | k = _ref[_i];
182 | reserved[k] = true;
183 | }
184 | if (!options.__toffee.noInheritance) {
185 | for (k in parent_locals) {
186 | v = parent_locals[k];
187 | if ((locals != null ? locals[k] : void 0) == null) {
188 | if (reserved[k] == null) {
189 | options[k] = v;
190 | }
191 | }
192 | }
193 | }
194 | if (!toffee.templates[path]) {
195 | return "Inline toffee include: Could not find " + path;
196 | } else {
197 | res = toffee.templates[path].pub(options);
198 | _ref1 = options.passback;
199 | for (k in _ref1) {
200 | v = _ref1[k];
201 | parent_locals[k] = v;
202 | }
203 | return res;
204 | }
205 | };
206 |
--------------------------------------------------------------------------------
/test/express4/public/javascripts/toffee.js:
--------------------------------------------------------------------------------
1 | var toffee;
2 |
3 | if (typeof toffee === "undefined" || toffee === null) {
4 | toffee = {};
5 | }
6 |
7 | if (!toffee.templates) {
8 | toffee.templates = {};
9 | }
10 |
11 | toffee.states = {
12 | "TOFFEE": 1,
13 | "COFFEE": 2
14 | };
15 |
16 | toffee.__json = function(locals, o, opts) {
17 | opts || (opts = {});
18 | opts.indent || (opts.indent = "");
19 | if (o == null) {
20 | return "null";
21 | } else {
22 | return "" + JSON.stringify(o, null, opts.indent).replace(//g, '\\u003E').replace(/&/g, '\\u0026').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029').replace(/\u200e/g, '\\u200e').replace(/\u200f/g, '\\u200f').replace(/\u202a/g, '\\u202a').replace(/\u202b/g, '\\u202b').replace(/\u202c/g, '\\u202c').replace(/\u202d/g, '\\u202d').replace(/\u202e/g, '\\u202e').replace(/\u206a/g, '\\u206a').replace(/\u206b/g, '\\u206b').replace(/\u206c/g, '\\u206c').replace(/\u206d/g, '\\u206d').replace(/\u206e/g, '\\u206e').replace(/\u206f/g, '\\u206f').replace(/\u2066/g, '\\u2066').replace(/\u2067/g, '\\u2067').replace(/\u2068/g, '\\u2068').replace(/\u2069/g, '\\u2069');
23 | }
24 | };
25 |
26 | toffee.__raw = function(locals, o) {
27 | return o;
28 | };
29 |
30 | toffee.__html = function(locals, o) {
31 | return ("" + o).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/\u200e/g, '').replace(/\u200f/g, '').replace(/\u202a/g, '').replace(/\u202b/g, '').replace(/\u202c/g, '').replace(/\u202d/g, '').replace(/\u202e/g, '').replace(/\u206a/g, '').replace(/\u206b/g, '').replace(/\u206c/g, '').replace(/\u206d/g, '').replace(/\u206e/g, '').replace(/\u206f/g, '').replace(/\u2066/g, '').replace(/\u2067/g, '').replace(/\u2068/g, '').replace(/\u2069/g, '');
32 | };
33 |
34 | toffee.__escape = function(locals, o) {
35 | var ae;
36 | if (locals.__toffee.autoEscape != null) {
37 | ae = locals.__toffee.autoEscape;
38 | } else if (true) {
39 | ae = true;
40 | } else {
41 | ae = true;
42 | }
43 | if (ae) {
44 | if (o === void 0) {
45 | return '';
46 | }
47 | if ((o != null) && (typeof o) === "object") {
48 | return locals.json(o);
49 | }
50 | return locals.html(o);
51 | }
52 | return o;
53 | };
54 |
55 | toffee.__augmentLocals = function(locals, bundle_path) {
56 | var _l, _t;
57 | _l = locals;
58 | _t = _l.__toffee = {
59 | out: []
60 | };
61 | if (_l.print == null) {
62 | _l.print = function(o) {
63 | return toffee.__print(_l, o);
64 | };
65 | }
66 | if (_l.json == null) {
67 | _l.json = function(o, opts) {
68 | return toffee.__json(_l, o, opts);
69 | };
70 | }
71 | if (_l.raw == null) {
72 | _l.raw = function(o) {
73 | return toffee.__raw(_l, o);
74 | };
75 | }
76 | if (_l.html == null) {
77 | _l.html = function(o) {
78 | return toffee.__html(_l, o);
79 | };
80 | }
81 | if (_l.escape == null) {
82 | _l.escape = function(o) {
83 | return toffee.__escape(_l, o);
84 | };
85 | }
86 | if (_l.partial == null) {
87 | _l.partial = function(path, vars) {
88 | return toffee.__partial(toffee.templates["" + bundle_path], _l, path, vars);
89 | };
90 | }
91 | if (_l.snippet == null) {
92 | _l.snippet = function(path, vars) {
93 | return toffee.__snippet(toffee.templates["" + bundle_path], _l, path, vars);
94 | };
95 | }
96 | if (_l.load == null) {
97 | _l.load = function(path, vars) {
98 | return toffee.__load(toffee.templates["" + bundle_path], _l, path, vars);
99 | };
100 | }
101 | _t.print = _l.print;
102 | _t.json = _l.json;
103 | _t.raw = _l.raw;
104 | _t.html = _l.html;
105 | _t.escape = _l.escape;
106 | _t.partial = _l.partial;
107 | _t.snippet = _l.snippet;
108 | return _t.load = _l.load;
109 | };
110 |
111 | toffee.__print = function(locals, o) {
112 | if (locals.__toffee.state === toffee.states.COFFEE) {
113 | locals.__toffee.out.push(o);
114 | return '';
115 | } else {
116 | return "" + o;
117 | }
118 | };
119 |
120 | toffee.__normalize = function(path) {
121 | var np, part, parts, _i, _len;
122 | if ((path == null) || path === "/") {
123 | return path;
124 | } else {
125 | parts = path.split("/");
126 | np = [];
127 | if (parts[0]) {
128 | np.push('');
129 | }
130 | for (_i = 0, _len = parts.length; _i < _len; _i++) {
131 | part = parts[_i];
132 | if (part === "..") {
133 | if (np.length > 1) {
134 | np.pop();
135 | } else {
136 | np.push(part);
137 | }
138 | } else {
139 | if (part !== ".") {
140 | np.push(part);
141 | }
142 | }
143 | }
144 | path = np.join("/");
145 | if (!path) {
146 | path = "/";
147 | }
148 | return path;
149 | }
150 | };
151 |
152 | toffee.__partial = function(parent_tmpl, parent_locals, path, vars) {
153 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
154 | return toffee.__inlineInclude(path, vars, parent_locals);
155 | };
156 |
157 | toffee.__snippet = function(parent_tmpl, parent_locals, path, vars) {
158 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
159 | vars = vars != null ? vars : {};
160 | vars.__toffee = vars.__toffee || {};
161 | vars.__toffee.noInheritance = true;
162 | return toffee.__inlineInclude(path, vars, parent_locals);
163 | };
164 |
165 | toffee.__load = function(parent_tmpl, parent_locals, path, vars) {
166 | path = toffee.__normalize(parent_tmpl.bundlePath + "/../" + path);
167 | vars = vars != null ? vars : {};
168 | vars.__toffee = vars.__toffee || {};
169 | vars.__toffee.repress = true;
170 | return toffee.__inlineInclude(path, vars, parent_locals);
171 | };
172 |
173 | toffee.__inlineInclude = function(path, locals, parent_locals) {
174 | var k, options, res, reserved, v, _i, _len, _ref, _ref1;
175 | options = locals || {};
176 | options.passback = {};
177 | options.__toffee = options.__toffee || {};
178 | reserved = {};
179 | _ref = ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"];
180 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
181 | k = _ref[_i];
182 | reserved[k] = true;
183 | }
184 | if (!options.__toffee.noInheritance) {
185 | for (k in parent_locals) {
186 | v = parent_locals[k];
187 | if ((locals != null ? locals[k] : void 0) == null) {
188 | if (reserved[k] == null) {
189 | options[k] = v;
190 | }
191 | }
192 | }
193 | }
194 | if (!toffee.templates[path]) {
195 | return "Inline toffee include: Could not find " + path;
196 | } else {
197 | res = toffee.templates[path].pub(options);
198 | _ref1 = options.passback;
199 | for (k in _ref1) {
200 | v = _ref1[k];
201 | parent_locals[k] = v;
202 | }
203 | return res;
204 | }
205 | };
206 |
--------------------------------------------------------------------------------
/lib/coffee-script/repl.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, REPL_PROMPT_MULTILINE, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, multilineMode, pipedInput, readline, repl, run, stdin, stdout;
4 |
5 | stdin = process.openStdin();
6 |
7 | stdout = process.stdout;
8 |
9 | CoffeeScript = require('./coffee-script');
10 |
11 | readline = require('readline');
12 |
13 | inspect = require('util').inspect;
14 |
15 | Script = require('vm').Script;
16 |
17 | Module = require('module');
18 |
19 | REPL_PROMPT = 'coffee> ';
20 |
21 | REPL_PROMPT_MULTILINE = '------> ';
22 |
23 | REPL_PROMPT_CONTINUATION = '......> ';
24 |
25 | enableColours = false;
26 |
27 | if (process.platform !== 'win32') {
28 | enableColours = !process.env.NODE_DISABLE_COLORS;
29 | }
30 |
31 | error = function(err) {
32 | return stdout.write((err.stack || err.toString()) + '\n');
33 | };
34 |
35 | ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
36 |
37 | SIMPLEVAR = /(\w+)$/i;
38 |
39 | autocomplete = function(text) {
40 | return completeAttribute(text) || completeVariable(text) || [[], text];
41 | };
42 |
43 | completeAttribute = function(text) {
44 | var all, completions, match, obj, prefix, val;
45 | if (match = text.match(ACCESSOR)) {
46 | all = match[0], obj = match[1], prefix = match[2];
47 | try {
48 | val = Script.runInThisContext(obj);
49 | } catch (error) {
50 | return;
51 | }
52 | completions = getCompletions(prefix, Object.getOwnPropertyNames(Object(val)));
53 | return [completions, prefix];
54 | }
55 | };
56 |
57 | completeVariable = function(text) {
58 | var completions, free, keywords, possibilities, r, vars, _ref;
59 | free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0;
60 | if (text === "") {
61 | free = "";
62 | }
63 | if (free != null) {
64 | vars = Script.runInThisContext('Object.getOwnPropertyNames(Object(this))');
65 | keywords = (function() {
66 | var _i, _len, _ref1, _results;
67 | _ref1 = CoffeeScript.RESERVED;
68 | _results = [];
69 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
70 | r = _ref1[_i];
71 | if (r.slice(0, 2) !== '__') {
72 | _results.push(r);
73 | }
74 | }
75 | return _results;
76 | })();
77 | possibilities = vars.concat(keywords);
78 | completions = getCompletions(free, possibilities);
79 | return [completions, free];
80 | }
81 | };
82 |
83 | getCompletions = function(prefix, candidates) {
84 | var el, _i, _len, _results;
85 | _results = [];
86 | for (_i = 0, _len = candidates.length; _i < _len; _i++) {
87 | el = candidates[_i];
88 | if (el.indexOf(prefix) === 0) {
89 | _results.push(el);
90 | }
91 | }
92 | return _results;
93 | };
94 |
95 | process.on('uncaughtException', error);
96 |
97 | backlog = '';
98 |
99 | run = function(buffer) {
100 | var code, returnValue, _;
101 | buffer = buffer.replace(/[\r\n]+$/, "");
102 | if (multilineMode) {
103 | backlog += "" + buffer + "\n";
104 | repl.setPrompt(REPL_PROMPT_CONTINUATION);
105 | repl.prompt();
106 | return;
107 | }
108 | if (!buffer.toString().trim() && !backlog) {
109 | repl.prompt();
110 | return;
111 | }
112 | code = backlog += buffer;
113 | if (code[code.length - 1] === '\\') {
114 | backlog = "" + backlog.slice(0, -1) + "\n";
115 | repl.setPrompt(REPL_PROMPT_CONTINUATION);
116 | repl.prompt();
117 | return;
118 | }
119 | repl.setPrompt(REPL_PROMPT);
120 | backlog = '';
121 | try {
122 | _ = global._;
123 | returnValue = CoffeeScript["eval"]("_=(undefined\n;" + code + "\n)", {
124 | filename: 'repl',
125 | modulename: 'repl'
126 | });
127 | if (returnValue === void 0) {
128 | global._ = _;
129 | }
130 | repl.output.write("" + (inspect(returnValue, false, 2, enableColours)) + "\n");
131 | } catch (err) {
132 | error(err);
133 | }
134 | return repl.prompt();
135 | };
136 |
137 | if (stdin.readable) {
138 | pipedInput = '';
139 | repl = {
140 | prompt: function() {
141 | return stdout.write(this._prompt);
142 | },
143 | setPrompt: function(p) {
144 | return this._prompt = p;
145 | },
146 | input: stdin,
147 | output: stdout,
148 | on: function() {}
149 | };
150 | stdin.on('data', function(chunk) {
151 | return pipedInput += chunk;
152 | });
153 | stdin.on('end', function() {
154 | var line, _i, _len, _ref;
155 | _ref = pipedInput.trim().split("\n");
156 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
157 | line = _ref[_i];
158 | stdout.write("" + line + "\n");
159 | run(line);
160 | }
161 | stdout.write('\n');
162 | return process.exit(0);
163 | });
164 | } else {
165 | if (readline.createInterface.length < 3) {
166 | repl = readline.createInterface(stdin, autocomplete);
167 | stdin.on('data', function(buffer) {
168 | return repl.write(buffer);
169 | });
170 | } else {
171 | repl = readline.createInterface(stdin, stdout, autocomplete);
172 | }
173 | }
174 |
175 | multilineMode = false;
176 |
177 | repl.input.on('keypress', function(char, key) {
178 | var cursorPos, newPrompt;
179 | if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'v')) {
180 | return;
181 | }
182 | cursorPos = repl.cursor;
183 | repl.output.cursorTo(0);
184 | repl.output.clearLine(1);
185 | multilineMode = !multilineMode;
186 | if (!multilineMode && backlog) {
187 | repl._line();
188 | }
189 | backlog = '';
190 | repl.setPrompt((newPrompt = multilineMode ? REPL_PROMPT_MULTILINE : REPL_PROMPT));
191 | repl.prompt();
192 | return repl.output.cursorTo(newPrompt.length + (repl.cursor = cursorPos));
193 | });
194 |
195 | repl.input.on('keypress', function(char, key) {
196 | if (!(multilineMode && repl.line)) {
197 | return;
198 | }
199 | if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'd')) {
200 | return;
201 | }
202 | multilineMode = false;
203 | return repl._line();
204 | });
205 |
206 | repl.on('attemptClose', function() {
207 | if (multilineMode) {
208 | multilineMode = false;
209 | repl.output.cursorTo(0);
210 | repl.output.clearLine(1);
211 | repl._onLine(repl.line);
212 | return;
213 | }
214 | if (backlog) {
215 | backlog = '';
216 | repl.output.write('\n');
217 | repl.setPrompt(REPL_PROMPT);
218 | return repl.prompt();
219 | } else {
220 | return repl.close();
221 | }
222 | });
223 |
224 | repl.on('close', function() {
225 | repl.output.write('\n');
226 | return repl.input.destroy();
227 | });
228 |
229 | repl.on('line', run);
230 |
231 | repl.setPrompt(REPL_PROMPT);
232 |
233 | repl.prompt();
234 |
235 | }).call(this);
236 |
--------------------------------------------------------------------------------
/src/errorHandler.coffee:
--------------------------------------------------------------------------------
1 | path = require "path"
2 | util = require "util"
3 |
4 | errorTypes = exports.errorTypes =
5 | PARSER: 0
6 | STR_INTERPOLATE: 1
7 | COFFEE_COMPILE: 2
8 | RUNTIME: 3
9 |
10 | class toffeeError extends Error
11 |
12 | constructor: (view, err_type, e) ->
13 | @errType = err_type
14 | @view = view
15 | @e = e
16 | @toffeeSrc = view.txt
17 | switch @errType
18 | when errorTypes.PARSER then @offensiveSrc = @toffeeSrc
19 | when errorTypes.STR_INTERPOLATE then @offensiveSrc = @toffeeSrc
20 | when errorTypes.COFFEE_COMPILE then @offensiveSrc = @view.coffeeScript
21 | when errorTypes.RUNTIME then @offensiveSrc = @view.javaScript
22 | @toffeeSrcLines = @toffeeSrc.split "\n"
23 | @offensiveSrcLines = @offensiveSrc.split "\n"
24 |
25 |
26 | getConvertedError: ->
27 |
28 | ### --------------------------------------
29 | returns a JS style error, but with some extras
30 | {
31 | stack: array of lines
32 | message: error message
33 | line_range: line range in the toffee file
34 | filename: filename, if available; or null
35 | ...etc...
36 | }
37 | ------------------------------------------
38 | ###
39 | res = {
40 | stack: []
41 | message: ""
42 | type: @errType
43 | full_path: @view.fileName
44 | dir_name: path.dirname @view.fileName
45 | file: path.basename @view.fileName
46 | line_range: null # will be a pair
47 | }
48 |
49 | if @e?.message? then res.message = @e.message
50 |
51 | # Error objects now support line numbers in certain cases.
52 | if @e?.location?.first_line?
53 | res.line_range = @_convertJsErrorRangeToToffeeRange @e.location
54 |
55 | switch @errType
56 |
57 | when errorTypes.PARSER
58 | if not res.line_range?
59 | line = @_extractOffensiveLineNo @e.message, /on line ([0-9]+)/
60 | res.line_range = [line, line + 1]
61 |
62 | when errorTypes.STR_INTERPOLATE
63 | res.line_range = [@e.relayed_line_range[0], @e.relayed_line_range[1]]
64 | res.message = res.message.replace 'starting on line NaN', @_lineRangeToPhrase res.line_range
65 | res.message = res.message.replace 'missing }', 'unclosed `\#{}`'
66 |
67 | when errorTypes.COFFEE_COMPILE
68 | if not res.line_range?
69 | line = @_extractOffensiveLineNo @e.message, /on line ([0-9]+)/
70 | res.line_range = @_convertOffensiveLineToToffeeRange line
71 | if res.message.indexOf('on line') isnt -1
72 | res.message = res.message.replace /on line [0-9]+/, @_lineRangeToPhrase res.line_range
73 | else
74 | res.message += " " + @_lineRangeToPhrase res.line_range
75 |
76 |
77 | when errorTypes.RUNTIME
78 | if not res.line_range?
79 | res.line_range = [0,0]
80 | if @e.stack
81 | res.stack = @e.stack.split "\n"
82 | @_convertRuntimeStackLines res
83 | res
84 |
85 | _convertRuntimeStackLines: (converted_err)->
86 | ###
87 | a little more complicated, so extracted. Returns an array
88 | of dictionaries where there's extra info on each line in the stack.
89 | ###
90 | hit_pub_yet = false # beyond this the stack isn't so important
91 | stack = converted_err.stack
92 | for line, i in stack
93 |
94 | rxx_pub = ///
95 | Object[\.].*?pub[\s]\(undefined\:([0-9]+)\:[0-9]+
96 | |
97 | tmpl[\.]render[\.]tmpl[\.]pub.*\(.*\:([0-9]+)\:[0-9]+
98 | ///
99 | m = line.match rxx_pub
100 | in_src_file = false
101 | lrange = [null, null]
102 | at_pub_call = false
103 | if m?.length >= 2
104 | line = line.replace "undefined", converted_err.full_path
105 | lineno = @_extractOffensiveLineNo line, /([0-9]+)\:[0-9]+/
106 | lrange = @_convertOffensiveLineToToffeeRange lineno
107 | line = line.replace /\:[0-9]+\:[0-9]+/, ""
108 | hit_pub_yet = true
109 | in_src_file = true
110 | at_pub_call = true
111 |
112 | rxx_inline = /// at[\s]undefined\:([0-9]+)\:[0-9]+ ///
113 | m = line.match rxx_inline
114 | if m?.length >= 2
115 | line = line.replace "undefined", converted_err.full_path
116 | lineno = @_extractOffensiveLineNo line, /([0-9]+)\:[0-9]+/
117 | lrange = @_convertOffensiveLineToToffeeRange lineno
118 | line = line.replace /\:[0-9]+\:[0-9]+/, ""
119 | in_src_file = true
120 |
121 | stack[i] =
122 | line: line
123 | above_pub_call: not hit_pub_yet
124 | at_pub_call: at_pub_call
125 | in_src_file: in_src_file
126 | line_range: lrange
127 |
128 | if stack[i].line_range[0] and not converted_err.line_range[0]
129 | converted_err.line_range = stack[i].line_range
130 |
131 | getPrettyPrintText: ->
132 | ###
133 | returns a TEXT only blob explaining the error
134 | ###
135 | cerr = @getConvertedError()
136 | header = "#{cerr.dir_name}/#{cerr.file}: #{cerr.message}"
137 | res = """
138 | ERROR
139 | =====
140 | #{header}
141 | """
142 | if cerr.stack?.length
143 | res += """\n
144 | STACK
145 | =====\n
146 | """
147 | count = 0
148 | for item,i in cerr.stack
149 | if i is 0
150 | res += "#{count++} #{item.line}"
151 | else if item.in_src_file and (item.above_pub_call or item.at_pub_call)
152 | res += "#{count++} [#{@_lineRangeToPhrase item.line_range}] #{cerr.dir_name}/#{cerr.file}"
153 | else if item.in_src_file
154 | continue
155 | else
156 | res += "#{count++}#{item.line}"
157 | if i < cerr.stack.length - 1
158 | res += "\n"
159 | res += """\n"""
160 | res
161 |
162 | getPrettyPrint: ->
163 | ###
164 | returns an HTML blob explaining the error
165 | with lines highlighted
166 | ###
167 | cerr = @getConvertedError()
168 | res = ""
169 | header = "#{cerr.dir_name}/#{cerr.file}: #{_ppEscape cerr.message}"
170 | res += """
171 |
172 | \n #{header}
173 | \n
174 | \n
175 | """
176 | if cerr.stack?.length
177 | res += " "
178 | count = 0
179 | for item,i in cerr.stack
180 | if i is 0
181 | res += " #{count++} #{item.line} "
182 | else if item.in_src_file and (item.above_pub_call or item.at_pub_call)
183 | res += " #{count++} [#{@_lineRangeToPhrase item.line_range}] #{cerr.dir_name}/#{cerr.file} "
184 | else if item.in_src_file
185 | continue
186 | else
187 | res += " #{count++}#{item.line} "
188 | res += " "
189 |
190 | for i in [(cerr.line_range[0]-3)...(cerr.line_range[1]+1)]
191 | if (i < 0) or i > @toffeeSrcLines.length - 1
192 | continue
193 | line = _ppEscape @toffeeSrcLines[i]
194 | padding_len = 5 - ("#{i+1}").length
195 | padding = (" " for j in [0...padding_len]).join ""
196 | if (cerr.line_range[0] - 1) <= (i) < cerr.line_range[1]
197 | extra = " "
198 | else
199 | extra = ""
200 | res+= "#{extra}\n#{i+1}: #{padding} #{line} "
201 | res += """
202 | \n
203 | \n
204 | """
205 | res
206 |
207 | _lineRangeToPhrase: (lrange) ->
208 | if lrange[0] is lrange[1]
209 | "on line #{lrange[0]}"
210 | else
211 | "between lines #{lrange[0]} and #{lrange[1]}"
212 |
213 | _extractOffensiveLineNo: (msg, rxx) ->
214 | m = msg.match rxx
215 | if not (m?.length >= 2) then return null
216 | return parseInt m[1]
217 |
218 | _convertJsErrorRangeToToffeeRange: (loc) ->
219 | range = @_convertOffensiveLineToToffeeRange loc.first_line
220 | if loc.last_line?
221 | range2 = @_convertOffensiveLineToToffeeRange loc.last_line
222 | range[1] = range2[1]
223 | return range
224 |
225 | _convertOffensiveLineToToffeeRange: (lineno) ->
226 | ###
227 | Given the error line in a converted file, hunts for surrounding
228 | __toffee.lineno calls and returns a pair array with the error position
229 | range in the original toffee file.
230 | ###
231 | ol = @offensiveSrcLines
232 | tl = @toffeeSrcLines
233 |
234 | if (not lineno?) or isNaN lineno
235 | return [1,tl.length]
236 |
237 | prev = ol[0...lineno].join "\n"
238 | next = ol[lineno...].join "\n"
239 | prev_matches = prev.match /_ln[ ]*\(?[ ]*([0-9]+)/g
240 | next_matches = next.match /_ln[ ]*\(?[ ]*([0-9]+)/g
241 | res = [1,tl.length]
242 |
243 | if prev_matches?.length
244 | res[0] = parseInt prev_matches[prev_matches.length-1].match(/[0-9]+/)[0]
245 | if next_matches?.length
246 | res[1] = parseInt next_matches[0].match(/[0-9]+/)[0]
247 | res
248 |
249 |
250 | exports.toffeeError = toffeeError
251 |
252 | _ppEscape = (txt) ->
253 | txt = txt.replace(/&/g, '&').replace(/
13 | vm.createContext({})
14 |
15 | class engine
16 |
17 | constructor: (options) ->
18 | options = options or {}
19 | @verbose = options.verbose or false
20 | @pool = new Pool(sandboxCons, options.poolSize or MAX_CACHED_SANDBOXES)
21 | @prettyPrintErrors = if options.prettyPrintErrors? then options.prettyPrintErrors else true
22 | @prettyLogErrors = if options.prettyLogErrors? then options.prettyLogErrors else true
23 | @autoEscape = if options.autoEscape? then options.autoEscape else true
24 | @cache = if options.cache? then options.cache else true
25 | @additionalErrorHandler = options.additionalErrorHandler or null
26 |
27 | @viewCache = {} # filename -> view
28 | @fsErrorCache = {} # filename -> timestamp last failed
29 |
30 | @filenameCache = {} # caches dir -> filename -> path.normalize path.resolve dir, filename
31 | @fileLockTable = new LockTable()
32 |
33 | _log: (o) ->
34 | if @verbose
35 | if (typeof o) in ["string","number","boolean"]
36 | console.log "toffee: #{o}"
37 | else
38 | console.log "toffee: #{util.inspect o}"
39 |
40 | # basically returns `path.normalize path.resolve dir, filename`, but caches it to speed up multiple inclusions
41 | normalizeFilename: (dir, filename) ->
42 | cache = @filenameCache[dir]
43 | if not cache?
44 | @filenameCache[dir] = {}
45 | cache = {}
46 | normalized = cache[filename]
47 | if not normalized?
48 | normalized = path.normalize path.resolve dir, filename
49 | @filenameCache[dir][filename] = normalized
50 | return normalized
51 |
52 |
53 | render: (filename, options, cb) => @run filename, options, cb
54 |
55 | run: (filename, options, cb) =>
56 | ###
57 | "options" contains the pub vars and may contain special items:
58 | layout: path to a template expecting a body var (express 2.x style, but for use with express 3.x)
59 | postProcess: a function which takes the string of output and post processes it (returning new string)
60 | __toffee.dir: path to look relative to
61 | __toffee.parent: parent file
62 | __toffee.noInheritance: if true, don't pass variables through unless explicitly passed
63 | __toffee.repress if true, don't output anything; useful with including definition files with passback of vars
64 | __toffee.autoEscape: if set as false, don't escape output of #{} vars by default
65 | ###
66 |
67 | if not options.prettyPrintErrors? then options.prettyPrintErrors = @prettyPrintErrors
68 | if not options.prettyLogErrors? then options.prettyLogErrors = @prettyLogErrors
69 | if not options.additionalErrorHandler? then options.additionalErrorHandler = @additionalErrorHandler
70 | if not options.autoEscape? then options.autoEscape = @autoEscape
71 |
72 | # we only want to pass post_process into the layout
73 | post_process = options.postProcess
74 | options.postProcess = null
75 |
76 | if options?.layout
77 | layout_options = {}
78 | layout_options[k] = v for k,v of options when k isnt "layout"
79 |
80 | [err, res] = @runSync filename, options
81 |
82 | # if we got an error but want to pretty-print by faking ok result
83 | if err and @prettyPrintErrors
84 | [err, res] = [null, err]
85 |
86 | # if we're using a layout, pub into that
87 | if (not err) and layout_options?
88 | layout_options.body = res
89 | [err, res] = @runSync options.layout, layout_options
90 | if err and @prettyPrintErrors
91 | [err, res] = [null, err]
92 |
93 | # post processing
94 | if (not err) and (typeof(post_process) is "function")
95 | [err, res] = @postProcess post_process, res
96 |
97 | cb err, res
98 |
99 | postProcess: (fn, res) ->
100 | err = null
101 | try
102 | res = fn res
103 | catch e
104 | err = e
105 | return [err, res]
106 |
107 | runSync: (filename, options) ->
108 | ###
109 | "options" the same as run() above
110 | ###
111 |
112 | start_time = Date.now()
113 |
114 | options = options or {}
115 | options.__toffee = options.__toffee or {}
116 | options.__toffee.dir = options.__toffee.dir or process.cwd()
117 | realpath = @normalizeFilename options.__toffee.dir, filename
118 |
119 | if @cache
120 | v = (@_viewCacheGet realpath) or (@_loadCacheAndMonitor realpath, options)
121 | else
122 | v = @_loadWithoutCache realpath, options
123 |
124 | if v
125 | if @fsErrorCache[realpath]
126 | [err, res] = [new Error("Couldn't load #{realpath}"), null]
127 | else
128 | options.__toffee.parent = realpath
129 | options.partial = options.partial or (fname, lvars) => @_fn_partial fname, lvars, realpath, options
130 | options.snippet = options.snippet or (fname, lvars) => @_fn_snippet fname, lvars, realpath, options
131 | options.load = options.load or (fname, lvars) => @_fn_load fname, lvars, realpath, options
132 | options.print = options.print or (txt) => @_fn_print txt, options
133 | if not options.console? then options.console = log: console.log
134 | ctx = @pool.get()
135 | [err, res] = v.run options, ctx
136 | @pool.release(ctx)
137 | else
138 | [err, res] = [new Error("Couldn't load #{realpath}"), null]
139 |
140 | @_log "#{realpath} run in #{Date.now() - start_time}ms"
141 | return [err, res]
142 |
143 | _viewCacheGet: (filename) ->
144 | if not @viewCache[filename]?
145 | return null
146 | else if not @fsErrorCache[filename]?
147 | return @viewCache[filename]
148 | else if (Date.now() - @fsErrorCache[filename]) < tweakables.MISSING_FILE_RECHECK
149 | return @viewCache[filename]
150 | else
151 | return null
152 |
153 | _inlineInclude: (filename, local_vars, parent_realpath, parent_options) =>
154 | options = local_vars or {}
155 | options.passback = {}
156 | options.__toffee = options.__toffee or {}
157 | options.__toffee.dir = path.dirname parent_realpath
158 | options.__toffee.parent = parent_realpath
159 | noInheritance = options.__toffee.noInheritance
160 | repress = options.__toffee.repress
161 |
162 | # we need to make a shallow copy of parent variables
163 | reserved = {}
164 | reserved[k] = true for k in ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"]
165 | if not noInheritance
166 | for k,v of parent_options when not local_vars?[k]?
167 | if not reserved[k]?
168 | options[k] = v
169 |
170 | [err, res] = @runSync filename, options
171 |
172 | for k,v of options.passback
173 | parent_options[k] = v
174 |
175 | return err or res
176 |
177 | _fn_load: (fname, lvars, realpath, options) =>
178 | lvars = if lvars? then lvars else {}
179 | lvars.__toffee = lvars.__toffee or {}
180 | lvars.__toffee.repress = true
181 | @_inlineInclude fname, lvars, realpath, options
182 |
183 | _fn_snippet: (fname, lvars, realpath, options) =>
184 | lvars = if lvars? then lvars else {}
185 | lvars.__toffee = lvars.__toffee or {}
186 | lvars.__toffee.noInheritance = true
187 | @_inlineInclude fname, lvars, realpath, options
188 |
189 | _fn_partial: (fname, lvars, realpath, options) =>
190 | @_inlineInclude fname, lvars, realpath, options
191 |
192 | _fn_print: (txt, options) ->
193 | if options.__toffee.state is states.COFFEE
194 | options.__toffee.out.push txt
195 | return ''
196 | else
197 | return txt
198 |
199 | _loadWithoutCache: (filename, options) ->
200 | try
201 | txt = fs.readFileSync filename, 'utf8'
202 | catch e
203 | txt = "Error: Could not read #{filename}"
204 | if options.__toffee?.parent? then txt += " first requested in #{options.__toffee.parent}"
205 |
206 | view_options = @_generateViewOptions filename
207 | v = new view txt, view_options
208 | return v
209 |
210 | _loadCacheAndMonitor: (filename, options) ->
211 | previous_fs_err = @fsErrorCache[filename]?
212 | try
213 | txt = fs.readFileSync filename, 'utf8'
214 | if @fsErrorCache[filename]? then delete @fsErrorCache[filename]
215 | catch e
216 | txt = "Error: Could not read #{filename}"
217 | if options.__toffee?.parent? then txt += " first requested in #{options.__toffee.parent}"
218 | @fsErrorCache[filename] = Date.now()
219 |
220 | # if we hit an fs error and it already happened, just return that
221 | if (@fsErrorCache[filename] and previous_fs_err and @viewCache[filename])
222 | return @viewCache[filename]
223 | else
224 | view_options = @_generateViewOptions filename
225 | v = new view txt, view_options
226 | @viewCache[filename] = v
227 | @_monitorForChanges filename, options
228 | return v
229 |
230 | _reloadFileInBkg: (filename, options) ->
231 | @_log "#{filename} acquiring lock to read"
232 | @fileLockTable.acquire2 {name: filename}, (lock) =>
233 | fs.readFile filename, 'utf8', (err, txt) =>
234 | @_log "#{Date.now()} - #{filename} changed to #{txt?.length} bytes. #{txt?.replace?(/\n/g , '')[...80]}" if not err
235 | waiting_for_view = false
236 | if err or (txt isnt @viewCache[filename].txt)
237 | if err
238 | @fsErrorCache[filename] = Date.now()
239 | txt = "Error: Could not read #{filename}"
240 | if options.__toffee?.parent? then txt += " requested in #{options.__toffee.parent}"
241 | if not (err and @viewCache[filename].fsError) # i.e., don't just create a new error view
242 | view_options = @_generateViewOptions filename
243 | ctx = @pool.get()
244 | view_options.ctx = ctx
245 | view_options.cb = (v) =>
246 | @_log "#{filename} updated and ready"
247 | @viewCache[filename] = v
248 | @pool.release(ctx)
249 | @_log "#{filename} lock releasing (view_options.cb)"
250 | lock.release()
251 | waiting_for_view = true # do not release lock instantly
252 | if err
253 | view_options.fsError = true
254 | v = new view txt, view_options
255 | if not waiting_for_view
256 | @_log "#{filename} lock releasing (not waiting for view)"
257 | lock.release()
258 |
259 | _generateViewOptions: (filename) ->
260 | return {
261 | fileName: filename
262 | verbose: @verbose
263 | prettyPrintErrors: @prettyPrintErrors
264 | prettyLogErrors: @prettyLogErrors
265 | autoEscape: @autoEscape
266 | additionalErrorHandler: @additionalErrorHandler
267 | }
268 |
269 | _monitorForChanges: (filename, options) ->
270 | ###
271 | we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
272 | event and we'll end up following the wrong, old 'file' as a new one
273 | is dropped in its place.
274 |
275 | Files that are missing are ignored here because they get picked up by new calls to _loadCacheAndMonitor
276 | ###
277 | if not @fsErrorCache[filename]? # if there's an fsError, this will get rechecked on-demand occasionally
278 | fsw = null
279 | try
280 | @_log "#{filename} starting fs.watch()"
281 | fsw = fs.watch filename, {persistent: true}, (change) =>
282 | @_log "#{filename} closing fs.watch()"
283 | fsw.close()
284 | @_monitorForChanges filename, options
285 | @_reloadFileInBkg filename, options
286 | catch e
287 | @_log "fs.watch() failed for #{filename}; settings fsErrorCache = true"
288 | @fsErrorCache[filename] = Date.now()
289 |
290 | exports.engine = engine
291 |
--------------------------------------------------------------------------------
/lib/errorHandler.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var _ppEscape, errorTypes, path, toffeeError, util,
4 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
5 | hasProp = {}.hasOwnProperty;
6 |
7 | path = require("path");
8 |
9 | util = require("util");
10 |
11 | errorTypes = exports.errorTypes = {
12 | PARSER: 0,
13 | STR_INTERPOLATE: 1,
14 | COFFEE_COMPILE: 2,
15 | RUNTIME: 3
16 | };
17 |
18 | toffeeError = (function(superClass) {
19 | extend(toffeeError, superClass);
20 |
21 | function toffeeError(view, err_type, e) {
22 | this.errType = err_type;
23 | this.view = view;
24 | this.e = e;
25 | this.toffeeSrc = view.txt;
26 | switch (this.errType) {
27 | case errorTypes.PARSER:
28 | this.offensiveSrc = this.toffeeSrc;
29 | break;
30 | case errorTypes.STR_INTERPOLATE:
31 | this.offensiveSrc = this.toffeeSrc;
32 | break;
33 | case errorTypes.COFFEE_COMPILE:
34 | this.offensiveSrc = this.view.coffeeScript;
35 | break;
36 | case errorTypes.RUNTIME:
37 | this.offensiveSrc = this.view.javaScript;
38 | }
39 | this.toffeeSrcLines = this.toffeeSrc.split("\n");
40 | this.offensiveSrcLines = this.offensiveSrc.split("\n");
41 | }
42 |
43 | toffeeError.prototype.getConvertedError = function() {
44 |
45 | /* --------------------------------------
46 | returns a JS style error, but with some extras
47 | {
48 | stack: array of lines
49 | message: error message
50 | line_range: line range in the toffee file
51 | filename: filename, if available; or null
52 | ...etc...
53 | }
54 | ------------------------------------------
55 | */
56 | var line, ref, ref1, ref2, res;
57 | res = {
58 | stack: [],
59 | message: "",
60 | type: this.errType,
61 | full_path: this.view.fileName,
62 | dir_name: path.dirname(this.view.fileName),
63 | file: path.basename(this.view.fileName),
64 | line_range: null
65 | };
66 | if (((ref = this.e) != null ? ref.message : void 0) != null) {
67 | res.message = this.e.message;
68 | }
69 | if (((ref1 = this.e) != null ? (ref2 = ref1.location) != null ? ref2.first_line : void 0 : void 0) != null) {
70 | res.line_range = this._convertJsErrorRangeToToffeeRange(this.e.location);
71 | }
72 | switch (this.errType) {
73 | case errorTypes.PARSER:
74 | if (res.line_range == null) {
75 | line = this._extractOffensiveLineNo(this.e.message, /on line ([0-9]+)/);
76 | res.line_range = [line, line + 1];
77 | }
78 | break;
79 | case errorTypes.STR_INTERPOLATE:
80 | res.line_range = [this.e.relayed_line_range[0], this.e.relayed_line_range[1]];
81 | res.message = res.message.replace('starting on line NaN', this._lineRangeToPhrase(res.line_range));
82 | res.message = res.message.replace('missing }', 'unclosed `\#{}`');
83 | break;
84 | case errorTypes.COFFEE_COMPILE:
85 | if (res.line_range == null) {
86 | line = this._extractOffensiveLineNo(this.e.message, /on line ([0-9]+)/);
87 | res.line_range = this._convertOffensiveLineToToffeeRange(line);
88 | }
89 | if (res.message.indexOf('on line') !== -1) {
90 | res.message = res.message.replace(/on line [0-9]+/, this._lineRangeToPhrase(res.line_range));
91 | } else {
92 | res.message += " " + this._lineRangeToPhrase(res.line_range);
93 | }
94 | break;
95 | case errorTypes.RUNTIME:
96 | if (res.line_range == null) {
97 | res.line_range = [0, 0];
98 | }
99 | if (this.e.stack) {
100 | res.stack = this.e.stack.split("\n");
101 | this._convertRuntimeStackLines(res);
102 | }
103 | }
104 | return res;
105 | };
106 |
107 | toffeeError.prototype._convertRuntimeStackLines = function(converted_err) {
108 |
109 | /*
110 | a little more complicated, so extracted. Returns an array
111 | of dictionaries where there's extra info on each line in the stack.
112 | */
113 | var at_pub_call, hit_pub_yet, i, in_src_file, k, len, line, lineno, lrange, m, results, rxx_inline, rxx_pub, stack;
114 | hit_pub_yet = false;
115 | stack = converted_err.stack;
116 | results = [];
117 | for (i = k = 0, len = stack.length; k < len; i = ++k) {
118 | line = stack[i];
119 | rxx_pub = /Object[\.].*?pub[\s]\(undefined\:([0-9]+)\:[0-9]+|tmpl[\.]render[\.]tmpl[\.]pub.*\(.*\:([0-9]+)\:[0-9]+/;
120 | m = line.match(rxx_pub);
121 | in_src_file = false;
122 | lrange = [null, null];
123 | at_pub_call = false;
124 | if ((m != null ? m.length : void 0) >= 2) {
125 | line = line.replace("undefined", converted_err.full_path);
126 | lineno = this._extractOffensiveLineNo(line, /([0-9]+)\:[0-9]+/);
127 | lrange = this._convertOffensiveLineToToffeeRange(lineno);
128 | line = line.replace(/\:[0-9]+\:[0-9]+/, "");
129 | hit_pub_yet = true;
130 | in_src_file = true;
131 | at_pub_call = true;
132 | }
133 | rxx_inline = /at[\s]undefined\:([0-9]+)\:[0-9]+/;
134 | m = line.match(rxx_inline);
135 | if ((m != null ? m.length : void 0) >= 2) {
136 | line = line.replace("undefined", converted_err.full_path);
137 | lineno = this._extractOffensiveLineNo(line, /([0-9]+)\:[0-9]+/);
138 | lrange = this._convertOffensiveLineToToffeeRange(lineno);
139 | line = line.replace(/\:[0-9]+\:[0-9]+/, "");
140 | in_src_file = true;
141 | }
142 | stack[i] = {
143 | line: line,
144 | above_pub_call: !hit_pub_yet,
145 | at_pub_call: at_pub_call,
146 | in_src_file: in_src_file,
147 | line_range: lrange
148 | };
149 | if (stack[i].line_range[0] && !converted_err.line_range[0]) {
150 | results.push(converted_err.line_range = stack[i].line_range);
151 | } else {
152 | results.push(void 0);
153 | }
154 | }
155 | return results;
156 | };
157 |
158 | toffeeError.prototype.getPrettyPrintText = function() {
159 |
160 | /*
161 | returns a TEXT only blob explaining the error
162 | */
163 | var cerr, count, header, i, item, k, len, ref, ref1, res;
164 | cerr = this.getConvertedError();
165 | header = cerr.dir_name + "/" + cerr.file + ": " + cerr.message;
166 | res = "ERROR\n=====\n" + header;
167 | if ((ref = cerr.stack) != null ? ref.length : void 0) {
168 | res += "\n\nSTACK\n=====\n";
169 | count = 0;
170 | ref1 = cerr.stack;
171 | for (i = k = 0, len = ref1.length; k < len; i = ++k) {
172 | item = ref1[i];
173 | if (i === 0) {
174 | res += (count++) + " " + item.line;
175 | } else if (item.in_src_file && (item.above_pub_call || item.at_pub_call)) {
176 | res += (count++) + " [" + (this._lineRangeToPhrase(item.line_range)) + "] " + cerr.dir_name + "/" + cerr.file;
177 | } else if (item.in_src_file) {
178 | continue;
179 | } else {
180 | res += "" + (count++) + item.line;
181 | }
182 | if (i < cerr.stack.length - 1) {
183 | res += "\n";
184 | }
185 | }
186 | }
187 | res += "\n";
188 | return res;
189 | };
190 |
191 | toffeeError.prototype.getPrettyPrint = function() {
192 |
193 | /*
194 | returns an HTML blob explaining the error
195 | with lines highlighted
196 | */
197 | var cerr, count, extra, header, i, item, j, k, l, len, line, padding, padding_len, ref, ref1, ref2, ref3, ref4, res;
198 | cerr = this.getConvertedError();
199 | res = "";
200 | header = cerr.dir_name + "/" + cerr.file + ": " + (_ppEscape(cerr.message)) + "";
201 | res += "\n \n " + header + " \n \n
\n \n ";
202 | if ((ref = cerr.stack) != null ? ref.length : void 0) {
203 | res += " ";
204 | count = 0;
205 | ref1 = cerr.stack;
206 | for (i = k = 0, len = ref1.length; k < len; i = ++k) {
207 | item = ref1[i];
208 | if (i === 0) {
209 | res += " " + (count++) + " " + item.line + " ";
210 | } else if (item.in_src_file && (item.above_pub_call || item.at_pub_call)) {
211 | res += " " + (count++) + " [" + (this._lineRangeToPhrase(item.line_range)) + "] " + cerr.dir_name + "/" + cerr.file + " ";
212 | } else if (item.in_src_file) {
213 | continue;
214 | } else {
215 | res += " " + (count++) + item.line + " ";
216 | }
217 | }
218 | res += " ";
219 | }
220 | for (i = l = ref2 = cerr.line_range[0] - 3, ref3 = cerr.line_range[1] + 1; ref2 <= ref3 ? l < ref3 : l > ref3; i = ref2 <= ref3 ? ++l : --l) {
221 | if ((i < 0) || i > this.toffeeSrcLines.length - 1) {
222 | continue;
223 | }
224 | line = _ppEscape(this.toffeeSrcLines[i]);
225 | padding_len = 5 - ("" + (i + 1)).length;
226 | padding = ((function() {
227 | var n, ref4, results;
228 | results = [];
229 | for (j = n = 0, ref4 = padding_len; 0 <= ref4 ? n < ref4 : n > ref4; j = 0 <= ref4 ? ++n : --n) {
230 | results.push(" ");
231 | }
232 | return results;
233 | })()).join("");
234 | if (((cerr.line_range[0] - 1) <= (ref4 = i) && ref4 < cerr.line_range[1])) {
235 | extra = " ";
236 | } else {
237 | extra = "";
238 | }
239 | res += extra + "\n" + (i + 1) + ": " + padding + " " + line + " ";
240 | }
241 | res += " \n\n\n ";
242 | return res;
243 | };
244 |
245 | toffeeError.prototype._lineRangeToPhrase = function(lrange) {
246 | if (lrange[0] === lrange[1]) {
247 | return "on line " + lrange[0];
248 | } else {
249 | return "between lines " + lrange[0] + " and " + lrange[1];
250 | }
251 | };
252 |
253 | toffeeError.prototype._extractOffensiveLineNo = function(msg, rxx) {
254 | var m;
255 | m = msg.match(rxx);
256 | if (!((m != null ? m.length : void 0) >= 2)) {
257 | return null;
258 | }
259 | return parseInt(m[1]);
260 | };
261 |
262 | toffeeError.prototype._convertJsErrorRangeToToffeeRange = function(loc) {
263 | var range, range2;
264 | range = this._convertOffensiveLineToToffeeRange(loc.first_line);
265 | if (loc.last_line != null) {
266 | range2 = this._convertOffensiveLineToToffeeRange(loc.last_line);
267 | range[1] = range2[1];
268 | }
269 | return range;
270 | };
271 |
272 | toffeeError.prototype._convertOffensiveLineToToffeeRange = function(lineno) {
273 |
274 | /*
275 | Given the error line in a converted file, hunts for surrounding
276 | __toffee.lineno calls and returns a pair array with the error position
277 | range in the original toffee file.
278 | */
279 | var next, next_matches, ol, prev, prev_matches, res, tl;
280 | ol = this.offensiveSrcLines;
281 | tl = this.toffeeSrcLines;
282 | if ((lineno == null) || isNaN(lineno)) {
283 | return [1, tl.length];
284 | }
285 | prev = ol.slice(0, lineno).join("\n");
286 | next = ol.slice(lineno).join("\n");
287 | prev_matches = prev.match(/_ln[ ]*\(?[ ]*([0-9]+)/g);
288 | next_matches = next.match(/_ln[ ]*\(?[ ]*([0-9]+)/g);
289 | res = [1, tl.length];
290 | if (prev_matches != null ? prev_matches.length : void 0) {
291 | res[0] = parseInt(prev_matches[prev_matches.length - 1].match(/[0-9]+/)[0]);
292 | }
293 | if (next_matches != null ? next_matches.length : void 0) {
294 | res[1] = parseInt(next_matches[0].match(/[0-9]+/)[0]);
295 | }
296 | return res;
297 | };
298 |
299 | return toffeeError;
300 |
301 | })(Error);
302 |
303 | exports.toffeeError = toffeeError;
304 |
305 | _ppEscape = function(txt) {
306 | var i, m;
307 | txt = txt.replace(/&/g, '&').replace(/ ref; i = 0 <= ref ? ++k : --k) {
313 | results.push(" ");
314 | }
315 | return results;
316 | })()).join(""));
317 | return txt;
318 | };
319 |
320 | }).call(this);
321 |
--------------------------------------------------------------------------------
/lib/coffee-script/rewriter.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref,
4 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
5 | __slice = [].slice;
6 |
7 | exports.Rewriter = (function() {
8 |
9 | Rewriter.name = 'Rewriter';
10 |
11 | function Rewriter() {}
12 |
13 | Rewriter.prototype.rewrite = function(tokens) {
14 | this.tokens = tokens;
15 | this.removeLeadingNewlines();
16 | this.removeMidExpressionNewlines();
17 | this.closeOpenCalls();
18 | this.closeOpenIndexes();
19 | this.addImplicitIndentation();
20 | this.tagPostfixConditionals();
21 | this.addImplicitBraces();
22 | this.addImplicitParentheses();
23 | return this.tokens;
24 | };
25 |
26 | Rewriter.prototype.scanTokens = function(block) {
27 | var i, token, tokens;
28 | tokens = this.tokens;
29 | i = 0;
30 | while (token = tokens[i]) {
31 | i += block.call(this, token, i, tokens);
32 | }
33 | return true;
34 | };
35 |
36 | Rewriter.prototype.detectEnd = function(i, condition, action) {
37 | var levels, token, tokens, _ref, _ref1;
38 | tokens = this.tokens;
39 | levels = 0;
40 | while (token = tokens[i]) {
41 | if (levels === 0 && condition.call(this, token, i)) {
42 | return action.call(this, token, i);
43 | }
44 | if (!token || levels < 0) {
45 | return action.call(this, token, i - 1);
46 | }
47 | if (_ref = token[0], __indexOf.call(EXPRESSION_START, _ref) >= 0) {
48 | levels += 1;
49 | } else if (_ref1 = token[0], __indexOf.call(EXPRESSION_END, _ref1) >= 0) {
50 | levels -= 1;
51 | }
52 | i += 1;
53 | }
54 | return i - 1;
55 | };
56 |
57 | Rewriter.prototype.removeLeadingNewlines = function() {
58 | var i, tag, _i, _len, _ref;
59 | _ref = this.tokens;
60 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
61 | tag = _ref[i][0];
62 | if (tag !== 'TERMINATOR') {
63 | break;
64 | }
65 | }
66 | if (i) {
67 | return this.tokens.splice(0, i);
68 | }
69 | };
70 |
71 | Rewriter.prototype.removeMidExpressionNewlines = function() {
72 | return this.scanTokens(function(token, i, tokens) {
73 | var _ref;
74 | if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) {
75 | return 1;
76 | }
77 | tokens.splice(i, 1);
78 | return 0;
79 | });
80 | };
81 |
82 | Rewriter.prototype.closeOpenCalls = function() {
83 | var action, condition;
84 | condition = function(token, i) {
85 | var _ref;
86 | return ((_ref = token[0]) === ')' || _ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')';
87 | };
88 | action = function(token, i) {
89 | return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END';
90 | };
91 | return this.scanTokens(function(token, i) {
92 | if (token[0] === 'CALL_START') {
93 | this.detectEnd(i + 1, condition, action);
94 | }
95 | return 1;
96 | });
97 | };
98 |
99 | Rewriter.prototype.closeOpenIndexes = function() {
100 | var action, condition;
101 | condition = function(token, i) {
102 | var _ref;
103 | return (_ref = token[0]) === ']' || _ref === 'INDEX_END';
104 | };
105 | action = function(token, i) {
106 | return token[0] = 'INDEX_END';
107 | };
108 | return this.scanTokens(function(token, i) {
109 | if (token[0] === 'INDEX_START') {
110 | this.detectEnd(i + 1, condition, action);
111 | }
112 | return 1;
113 | });
114 | };
115 |
116 | Rewriter.prototype.addImplicitBraces = function() {
117 | var action, condition, sameLine, stack, start, startIndent, startsLine;
118 | stack = [];
119 | start = null;
120 | startsLine = null;
121 | sameLine = true;
122 | startIndent = 0;
123 | condition = function(token, i) {
124 | var one, tag, three, two, _ref, _ref1;
125 | _ref = this.tokens.slice(i + 1, (i + 3) + 1 || 9e9), one = _ref[0], two = _ref[1], three = _ref[2];
126 | if ('HERECOMMENT' === (one != null ? one[0] : void 0)) {
127 | return false;
128 | }
129 | tag = token[0];
130 | if (__indexOf.call(LINEBREAKS, tag) >= 0) {
131 | sameLine = false;
132 | }
133 | return (((tag === 'TERMINATOR' || tag === 'OUTDENT') || (__indexOf.call(IMPLICIT_END, tag) >= 0 && sameLine)) && ((!startsLine && this.tag(i - 1) !== ',') || !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':'))) || (tag === ',' && one && ((_ref1 = one[0]) !== 'IDENTIFIER' && _ref1 !== 'NUMBER' && _ref1 !== 'STRING' && _ref1 !== '@' && _ref1 !== 'TERMINATOR' && _ref1 !== 'OUTDENT'));
134 | };
135 | action = function(token, i) {
136 | var tok;
137 | tok = this.generate('}', '}', token[2]);
138 | return this.tokens.splice(i, 0, tok);
139 | };
140 | return this.scanTokens(function(token, i, tokens) {
141 | var ago, idx, prevTag, tag, tok, value, _ref, _ref1;
142 | if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
143 | stack.push([(tag === 'INDENT' && this.tag(i - 1) === '{' ? '{' : tag), i]);
144 | return 1;
145 | }
146 | if (__indexOf.call(EXPRESSION_END, tag) >= 0) {
147 | start = stack.pop();
148 | return 1;
149 | }
150 | if (!(tag === ':' && ((ago = this.tag(i - 2)) === ':' || ((_ref1 = stack[stack.length - 1]) != null ? _ref1[0] : void 0) !== '{'))) {
151 | return 1;
152 | }
153 | sameLine = true;
154 | stack.push(['{']);
155 | idx = ago === '@' ? i - 2 : i - 1;
156 | while (this.tag(idx - 2) === 'HERECOMMENT') {
157 | idx -= 2;
158 | }
159 | prevTag = this.tag(idx - 1);
160 | startsLine = !prevTag || (__indexOf.call(LINEBREAKS, prevTag) >= 0);
161 | value = new String('{');
162 | value.generated = true;
163 | tok = this.generate('{', value, token[2]);
164 | tokens.splice(idx, 0, tok);
165 | this.detectEnd(i + 2, condition, action);
166 | return 2;
167 | });
168 | };
169 |
170 | Rewriter.prototype.addImplicitParentheses = function() {
171 | var action, condition, noCall, seenControl, seenSingle;
172 | noCall = seenSingle = seenControl = false;
173 | condition = function(token, i) {
174 | var post, tag, _ref, _ref1;
175 | tag = token[0];
176 | if (!seenSingle && token.fromThen) {
177 | return true;
178 | }
179 | if (tag === 'IF' || tag === 'ELSE' || tag === 'CATCH' || tag === '->' || tag === '=>' || tag === 'CLASS') {
180 | seenSingle = true;
181 | }
182 | if (tag === 'IF' || tag === 'ELSE' || tag === 'SWITCH' || tag === 'TRY' || tag === '=') {
183 | seenControl = true;
184 | }
185 | if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') {
186 | return true;
187 | }
188 | return !token.generated && this.tag(i - 1) !== ',' && (__indexOf.call(IMPLICIT_END, tag) >= 0 || (tag === 'INDENT' && !seenControl)) && (tag !== 'INDENT' || (((_ref = this.tag(i - 2)) !== 'CLASS' && _ref !== 'EXTENDS') && (_ref1 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref1) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
189 | };
190 | action = function(token, i) {
191 | return this.tokens.splice(i, 0, this.generate('CALL_END', ')', token[2]));
192 | };
193 | return this.scanTokens(function(token, i, tokens) {
194 | var callObject, current, next, prev, tag, _ref, _ref1, _ref2;
195 | tag = token[0];
196 | if (tag === 'CLASS' || tag === 'IF' || tag === 'FOR' || tag === 'WHILE') {
197 | noCall = true;
198 | }
199 | _ref = tokens.slice(i - 1, (i + 1) + 1 || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
200 | callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref1 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref1) >= 0);
201 | seenSingle = false;
202 | seenControl = false;
203 | if (__indexOf.call(LINEBREAKS, tag) >= 0) {
204 | noCall = false;
205 | }
206 | if (prev && !prev.spaced && tag === '?') {
207 | token.call = true;
208 | }
209 | if (token.fromThen) {
210 | return 1;
211 | }
212 | if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) {
213 | return 1;
214 | }
215 | tokens.splice(i, 0, this.generate('CALL_START', '(', token[2]));
216 | this.detectEnd(i + 1, condition, action);
217 | if (prev[0] === '?') {
218 | prev[0] = 'FUNC_EXIST';
219 | }
220 | return 2;
221 | });
222 | };
223 |
224 | Rewriter.prototype.addImplicitIndentation = function() {
225 | var action, condition, indent, outdent, starter;
226 | starter = indent = outdent = null;
227 | condition = function(token, i) {
228 | var _ref;
229 | return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN'));
230 | };
231 | action = function(token, i) {
232 | return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
233 | };
234 | return this.scanTokens(function(token, i, tokens) {
235 | var tag, _ref, _ref1;
236 | tag = token[0];
237 | if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') {
238 | tokens.splice(i, 1);
239 | return 0;
240 | }
241 | if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
242 | tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation(token))));
243 | return 2;
244 | }
245 | if (tag === 'CATCH' && ((_ref = this.tag(i + 2)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) {
246 | tokens.splice.apply(tokens, [i + 2, 0].concat(__slice.call(this.indentation(token))));
247 | return 4;
248 | }
249 | if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
250 | starter = tag;
251 | _ref1 = this.indentation(token, true), indent = _ref1[0], outdent = _ref1[1];
252 | if (starter === 'THEN') {
253 | indent.fromThen = true;
254 | }
255 | tokens.splice(i + 1, 0, indent);
256 | this.detectEnd(i + 2, condition, action);
257 | if (tag === 'THEN') {
258 | tokens.splice(i, 1);
259 | }
260 | return 1;
261 | }
262 | return 1;
263 | });
264 | };
265 |
266 | Rewriter.prototype.tagPostfixConditionals = function() {
267 | var action, condition, original;
268 | original = null;
269 | condition = function(token, i) {
270 | var _ref;
271 | return (_ref = token[0]) === 'TERMINATOR' || _ref === 'INDENT';
272 | };
273 | action = function(token, i) {
274 | if (token[0] !== 'INDENT' || (token.generated && !token.fromThen)) {
275 | return original[0] = 'POST_' + original[0];
276 | }
277 | };
278 | return this.scanTokens(function(token, i) {
279 | if (token[0] !== 'IF') {
280 | return 1;
281 | }
282 | original = token;
283 | this.detectEnd(i + 1, condition, action);
284 | return 1;
285 | });
286 | };
287 |
288 | Rewriter.prototype.indentation = function(token, implicit) {
289 | var indent, outdent;
290 | if (implicit == null) {
291 | implicit = false;
292 | }
293 | indent = ['INDENT', 2, token[2]];
294 | outdent = ['OUTDENT', 2, token[2]];
295 | if (implicit) {
296 | indent.generated = outdent.generated = true;
297 | }
298 | return [indent, outdent];
299 | };
300 |
301 | Rewriter.prototype.generate = function(tag, value, line) {
302 | var tok;
303 | tok = [tag, value, line];
304 | tok.generated = true;
305 | return tok;
306 | };
307 |
308 | Rewriter.prototype.tag = function(i) {
309 | var _ref;
310 | return (_ref = this.tokens[i]) != null ? _ref[0] : void 0;
311 | };
312 |
313 | return Rewriter;
314 |
315 | })();
316 |
317 | BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']];
318 |
319 | exports.INVERSES = INVERSES = {};
320 |
321 | EXPRESSION_START = [];
322 |
323 | EXPRESSION_END = [];
324 |
325 | for (_i = 0, _len = BALANCED_PAIRS.length; _i < _len; _i++) {
326 | _ref = BALANCED_PAIRS[_i], left = _ref[0], rite = _ref[1];
327 | EXPRESSION_START.push(INVERSES[rite] = left);
328 | EXPRESSION_END.push(INVERSES[left] = rite);
329 | }
330 |
331 | EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
332 |
333 | IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
334 |
335 | IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++'];
336 |
337 | IMPLICIT_UNSPACED_CALL = ['+', '-'];
338 |
339 | IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
340 |
341 | IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR'];
342 |
343 | SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN'];
344 |
345 | SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
346 |
347 | LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
348 |
349 | }).call(this);
350 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TOFFEE
2 |
3 | **_Toffee_ is barely maintained. Please consider another templating language.**
4 |
5 | _Toffee_ is a templating language, based on the simplicity and beauty of CoffeeScript.
6 |
7 | - it works with Node.js
8 | - it works in the browser, too -- even the advanced features.
9 |
10 | Newest feature:
11 |
12 | - post-processing! You can let Toffee do your server-side code highighting, and other magic.
13 |
14 | Toffee has many cool features. Keep on reading.
15 |
16 | # Table of Contents
17 |
18 | - [1. Language Basics](#section_1)
19 | - [2. Notes on Escaping](#section_2)
20 | - [3. Common Questions](#section_3)
21 | - [4. Installation & Usage (Node, Express, and the browser)](#section_4)
22 |
23 | ## Language Basics
24 |
25 | Printing variables in Toffee is easy. Just use CoffeeScript's #{} syntax:
26 |
27 | ```html
28 |
29 | Hey, #{user.name}. #{flirty_welcome_msg}
30 |
31 | ```
32 |
33 | The `#{}` syntax is powerful, so be responsible.
34 |
35 | ```html
36 |
37 | You have #{(sheep for sheep in flock when sheep.color is 'black').length}
38 | black sheep in the flock.
39 |
40 | ```
41 |
42 | Including other files is possible thanks to the function `partial`. This works in both Express and the browser.
43 |
44 | ```html
45 |
46 | #{partial "navigation.toffee", {username: user.name, age: 22} }
47 |
48 | ```
49 |
50 | But the greatest pleasure arises when you enter
51 | `coffee mode`. Note the `{# ... #}` region, where you can write multiple lines of CoffeeScript.
52 |
53 | ```html
54 |
55 | {# ten_numbers = [1,3,2,4,5,8,6,7,69, Math.random()] ten_numbers.sort (a,b) ->
56 | b - a #} The largest number I can even think of is #{ten_numbers[0]}.
57 |
58 | ```
59 |
60 | Against all odds, inside `coffee mode`, you can switch back to `toffee mode` with `{: ... :}`. It's endlessly nestable.
61 |
62 | ```html
63 |
64 |
65 | {# if projects.length for project in projects {:
66 |
69 | :} #}
70 |
71 |
72 | ```
73 |
74 | This bracket and nesting syntax avoids a lot of large, ugly regions, such
75 | as EJS's unethical `<% } %>`. It's been wrong for thousands of years
76 | to have control markers surrounded by other control
77 | markers, and it is still wrong. Witness:
78 |
79 | EJS, verbose and weak.
80 |
81 | ```
82 | <% for(var i=0; i
83 | <%= supplies[i] %>
84 | <% } %>
85 | ```
86 |
87 | TOFFEE, so elegant and proud.
88 |
89 | ```html
90 | {# for supply in supplies {:
91 | #{supply}
92 | :} #}
93 | ```
94 |
95 | Or, using Toffee's `print`:
96 |
97 | ```html
98 | {# for supply in supplies print "
99 | #{supply}
100 | " #}
101 | ```
102 |
103 | These are slightly different, as `print` outputs raw text, while `#{}` used in toffee mode safely escapes HTML or JSON. This escaping
104 | escaping is customizable. More on that below.
105 |
106 | With nested code, indentation of your CoffeeScript is magically maintained.
107 |
108 | ```html
109 | {# if user.is_drunk for name, profile of friends when profile.is_responsible {:
110 |
111 | You know, #{name} would make a great designated driver. And she only lives
112 | #{profile.distance}km away. {# if profile.car? {: And wow, she drives a
113 | #{profile.car.model} :} else {: But, alas, she has no wheels. :} #}
114 |
115 | :} #}
116 | ```
117 |
118 | ### Partials (including other files), both for output and configuration
119 |
120 | Including other files in Toffee is easy with the `partial` function, which includes another template file.
121 |
122 | ```html
123 |
124 | #{partial '../main_navigation.toffee'}
125 |
126 | ```
127 |
128 | Shallow copies of variables are passed through from the parent document, however you can pass additional variables with a dictionary.
129 |
130 | ```html
131 |
132 | #{partial '../main_navigation.toffee', {user: elon_musk, iq: 180} }
133 |
134 | ```
135 |
136 | Again, toffee's `print` function allows you to use partials when in coffeescript mode:
137 |
138 | ```html
139 | {# if user? print partial "logged_in_template.toffee" #}
140 | ```
141 |
142 | For your safety and convenience, variables are shallow-copied into a template. This means if you redefine or create a variable in a
143 | child template, it won't be available back in the parent template. However, you can relay variables by modifying the special `passback` dictionary
144 | in a the child template.
145 |
146 | ```html
147 |
148 | {# partial './config.toffee' #}
149 | Our site's name is #{site_name}.
150 |
151 |
152 | {# passback.site_name = "gittub.com" #}
153 | ```
154 |
155 | For your naming convenience, you can also use the `load` function, which is identical to `partial` but withholds output.
156 |
157 | ### Post-processing
158 |
159 | New in the latest version of Toffee, you can pass a `postProcess` function to Toffee. This works for individual partials or even
160 | an entire document. The `postProcess` function performs a final transformation on your output.
161 |
162 | One smart use of postProcess is to find anything inside triple tick marks and perform a code higlighting.
163 |
164 | ```html
165 | {# print partial './something.toffee', { foo: 1000 postProcess: (s) ->
166 | find_and_higlight_code_in s } #}
167 | ```
168 |
169 | You could define `find_and_highlight_code_in` anywhere in your publishing stack. You can pass it from your webserver, define it above, whatever. If you're
170 | doing this server-side, consider the popular Node module _highlight.js_. In that case, define a highlight function that hunts for
171 | triple tick marks and then uses highlight's highlighter to transform it.
172 |
173 | Your users shouldn't have to wait for client-side JS to re-process your pages.
174 |
175 | ### Indentation
176 |
177 | Since CoffeeScript is sensitive to indenting, so is Toffee.
178 |
179 | But...Toffee doesn't care where you start your CoffeeScript. When you want to create a coffee block,
180 | you can indent it however you like, and all that matters is that the internal,
181 | relative indenting is correct. For example, these are identical:
182 |
183 | ```html
184 |
185 | {# if user.is_awesome {: YAY! :} #}
186 |
187 |
188 | {# if user.is_awesome {: YAY! :} #}
189 |
190 | ```
191 |
192 | In other words, feel free to pick whatever indentation baseline you want when entering a region of CoffeeScript.
193 |
194 | Note that where you put your toffee mode tokens (`{:`) is important, as the following illustrates:
195 |
196 | ```html
197 |
198 | {# if x is true if y is true if z is true w = true {: x is true! Dunno
199 | anything about y or z, though. :} #}
200 |
201 | ```
202 |
203 | Why? Because this is roughly the same as saying:
204 |
205 | ```html
206 |
207 | {# if x is true if y is true if z is true w = true print "\n x is true! Dunno
208 | anything about y or z, though.\n " #}
209 |
210 | ```
211 |
212 | One syntactic convenience: if you start a `{:` on the same line as some preceeding CoffeeScript, it's
213 | treated the same as putting it on a new line and indenting one level.
214 | So the following three conditionals are the same:
215 |
216 | ```html
217 | {# if x is true {:yay:} #}
218 | ```
219 |
220 | ```html
221 | {# if x is true {:yay:} #}
222 | ```
223 |
224 | ```html
225 | {# if x is true {: yay :} #}
226 | ```
227 |
228 | The third example has extra whitespace around the "yay," but otherwise the three are logically identical.
229 |
230 | ### One gotcha with indenting
231 |
232 | THIS IS AN ERROR
233 |
234 | ```html
235 | {# if x is 0 {:Yay!:} else {:Burned:} #}
236 | ```
237 |
238 | Note that the indentations before the 'if' and the 'else' are technically different,
239 | as the `if` has only 1 space before it, and the `else` has 2. This is better style anyway:
240 |
241 | GOOD
242 |
243 | ```html
244 | {# if x is 0 {:Yay!:} else {:Burned:} #}
245 | ```
246 |
247 | With a single line of CoffeeScript, feel free to keep it all on one line:
248 |
249 | GOOD
250 |
251 | ```html
252 | {# foo = "bar" #}
253 | ```
254 |
255 | ## Commenting out a block of toffee
256 |
257 | In toffee mode, you can comment out a region with `{##` and `##}`.
258 |
259 | ```html
260 |
261 | I don't want to output this anymore... {##
262 | An ode to Ruby on Rails
263 | #{partial 'ode.toffee'}
264 | ##}
265 |
266 | ```
267 |
268 | ## Escaping
269 |
270 | In your CoffeeScript, the `print` function lets you print the raw value of a variable:
271 |
272 | ```
273 | {#
274 | danger_code = ""
275 | print danger_code
276 | #}
277 | ```
278 |
279 | But in toffee mode, `#{some_expression}` output is escaped intelligently by default:
280 |
281 | ```html
282 |
283 | #{danger_code}
284 | ```
285 |
286 | You can control the escaping, but here are the defaults:
287 |
288 | - if it's a string or scalar, it is escaped for HTML safety.
289 | - it's an array or object, it is converted to JSON.
290 |
291 | ### Custom Escaping
292 |
293 | You can bypass the above rules.
294 |
295 | - `#{json foo}`: this outputs foo as JSON.
296 | - `#{raw foo}`: this outputs foo in raw text.
297 | - `#{html foo}`: this outputs foo, escaped as HTML. For a scalar, it's the same as `#{foo}`, but it's available in case you
298 | (1) override the default escaping or (2) turn off auto-escaping (both explained below).
299 | - `#{partial "foo.toffee"}` and `#{snippet "foo.toffee"}`: unescaped, since you don't want to escape your own templates
300 |
301 | When any of the functions mentioned above are leftmost in a `#{}` token in toffee mode, their output is left untouched by the
302 | built in escape function.
303 |
304 | These functions are also available to you in coffee mode.
305 |
306 | ```html
307 |
308 | Want to read some JSON, human? {# foo = [1,2,3, {bar: "none"}]
309 | foo_as_json_as_html = html json foo print foo_as_json_as_html #}
310 |
311 | ```
312 |
313 | _Note!_ if you pass a variable to the template called `json`, `raw`, or `html`, Toffee won't create these helper functions, which would override your vars.
314 | In this case, you can access the escape functions through their official titles, `__toffee.raw`, etc.
315 |
316 | Overriding the default `escape`:
317 |
318 | - If you pass a variable to your template called `escape`, this will be used as the default escape. In toffee mode, everything inside `#{}` that isn't subject to an above-mentioned exception will go through your `escape` function.
319 |
320 | Turning off autoescaping entirely:
321 |
322 | - If you set `autoEscape: false` when creating the engine, the default will be raw across your project. (See more on that below under Express 3.x settings.)
323 | - Alternatively, you could pass the var `escape: (x) -> x` to turn off escaping for a given template.
324 |
325 | ## Common Questions
326 |
327 | #### How does it compare to eco?
328 |
329 | Eco is another CoffeeScript templating language and inspiration for Toffee.
330 | The syntaxes are pretty different, so pick the one you prefer.
331 |
332 | One big Toffee advantage: multiple lines of CoffeeScript just look like CoffeeScript. Compare:
333 |
334 | ECO
335 |
336 | ```
337 | <% if @projects.length: %>
338 | <% for project in @projects: %>
339 | <% if project.is_active: %>
340 | <%= project.name %> | <%= project.description %>
341 | <% end %>
342 | <% end %>
343 | <% end %>
344 | ```
345 |
346 | TOFFEE
347 |
348 | ```html
349 | {# if projects.length for project in projects if project.is_active {:
350 | #{project.name} | #{project.description}
351 | :} #}
352 | ```
353 |
354 | With Toffee's syntax, brackets enclose regions not directives, so your editor
355 | will let you collapse and expand sections of code. And if you click on one of the brackets in most
356 | editors, it will highlight the matching bracket.
357 |
358 | #### Does it cache templates?
359 |
360 | In Express 2.0, that's up to Express. When used in Express 3.0, Toffee asynchronously monitors known templates and recompiles them in the background when necessary. So you don't need to restart your production webserver whenever you edit a template.
361 |
362 | #### Does it find line numbers in errors?
363 |
364 | Yes, Toffee does a very good job of that. There are 3 possible places you can hit an error in Toffee:
365 |
366 | - in the language itself, say a parse error
367 | - in the CoffeeScript, preventing it from compiling to JS
368 | - runtime, in the final JS
369 |
370 | Stack traces are converted to lines in Toffee and show you where the problem is.
371 | By default when Toffee hits an error it replies with some pretty-printed HTML showing you the problem.
372 | This can be overridden, as explained below in the Express 3.0 section.
373 |
374 | ### Does it support partials? (a.k.a includes)
375 |
376 | Yes. In Express 2.0, Express itself is responsible for including other files, and they call this system "partials." In Express 3.0 and in the browser,
377 | Toffee defines the `partial` function, and it works as you'd expect.
378 |
379 | ```html
380 | #{partial '../foo/bar.toffee', name: "Chris"}
381 | ```
382 |
383 | Inside a region of CoffeeScript, you can print or capture the result of a partial.
384 |
385 | ```html
386 |
387 | {# if session print partial 'user_menu.toffee', info: session.info else print
388 | partial 'guest_menu.toffee' #}
389 |
390 | ```
391 |
392 | Like Express's `partial` function, Toffee's function passes all available vars to the child template.
393 | For example, in the above code, `session` would also be available in the user_menu.toffee file. If you don't want this scoping, use Toffee's `snippet` function, which sandboxes it:
394 |
395 | ```
396 | {#
397 | if session
398 | print partial 'user.toffee', info: session.info # session will also be passed
399 | print snippet 'user.toffee', info: session.info # session will not be passed
400 | #}
401 | ```
402 |
403 | #### Does it support `layout`?
404 |
405 | Yes, this works in NodeJS and Express 3.0, emulating the Express 2.0 way. The var `layout` is considered special, and should
406 | be the path to your layout file.
407 |
408 | ## Installation & Usage
409 |
410 | - [Using Toffee in NodeJS](https://github.com/malgorithms/toffee/wiki/NodeJS-Usage)
411 | - [Using Toffee in Express 3](https://github.com/malgorithms/toffee/wiki/Express3-Usage)
412 | - [Using Toffee in Express 2](https://github.com/malgorithms/toffee/wiki/Express2-Usage)
413 | - [Using Toffee in the Browser](https://github.com/malgorithms/toffee/wiki/Browser-Usage)
414 |
415 | # contributing & asking for fixes.
416 |
417 | If you have a problem with Toffee let me know, and I'll fix it ASAP.
418 |
419 | Also, I'm likely to accept good pull requests.
420 |
421 | If you'd like to edit code for this project, note that you should always edit the `.coffee` files,
422 | as the `.js` files are generated automatically by building.
423 |
424 | To build and test your changes
425 |
426 | ```
427 | # icake is iced-coffee-script's version of cake
428 | > icake build
429 | > icake test
430 | ```
431 |
--------------------------------------------------------------------------------
/lib/engine.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.12.7
2 | (function() {
3 | var LockTable, MAX_CACHED_SANDBOXES, Pool, engine, fs, path, ref, sandboxCons, states, tweakables, util, utils, view, vm,
4 | bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
5 |
6 | view = require('./view').view;
7 |
8 | ref = require('./consts'), states = ref.states, tweakables = ref.tweakables;
9 |
10 | Pool = require('./pool').Pool;
11 |
12 | utils = require('./utils');
13 |
14 | fs = require('fs');
15 |
16 | path = require('path');
17 |
18 | util = require('util');
19 |
20 | vm = require('vm');
21 |
22 | LockTable = require('iced-lock').Table;
23 |
24 | MAX_CACHED_SANDBOXES = 100;
25 |
26 | sandboxCons = function() {
27 | return vm.createContext({});
28 | };
29 |
30 | engine = (function() {
31 | function engine(options) {
32 | this._fn_partial = bind(this._fn_partial, this);
33 | this._fn_snippet = bind(this._fn_snippet, this);
34 | this._fn_load = bind(this._fn_load, this);
35 | this._inlineInclude = bind(this._inlineInclude, this);
36 | this.run = bind(this.run, this);
37 | this.render = bind(this.render, this);
38 | options = options || {};
39 | this.verbose = options.verbose || false;
40 | this.pool = new Pool(sandboxCons, options.poolSize || MAX_CACHED_SANDBOXES);
41 | this.prettyPrintErrors = options.prettyPrintErrors != null ? options.prettyPrintErrors : true;
42 | this.prettyLogErrors = options.prettyLogErrors != null ? options.prettyLogErrors : true;
43 | this.autoEscape = options.autoEscape != null ? options.autoEscape : true;
44 | this.cache = options.cache != null ? options.cache : true;
45 | this.additionalErrorHandler = options.additionalErrorHandler || null;
46 | this.viewCache = {};
47 | this.fsErrorCache = {};
48 | this.filenameCache = {};
49 | this.fileLockTable = new LockTable();
50 | }
51 |
52 | engine.prototype._log = function(o) {
53 | var ref1;
54 | if (this.verbose) {
55 | if ((ref1 = typeof o) === "string" || ref1 === "number" || ref1 === "boolean") {
56 | return console.log("toffee: " + o);
57 | } else {
58 | return console.log("toffee: " + (util.inspect(o)));
59 | }
60 | }
61 | };
62 |
63 | engine.prototype.normalizeFilename = function(dir, filename) {
64 | var cache, normalized;
65 | cache = this.filenameCache[dir];
66 | if (cache == null) {
67 | this.filenameCache[dir] = {};
68 | cache = {};
69 | }
70 | normalized = cache[filename];
71 | if (normalized == null) {
72 | normalized = path.normalize(path.resolve(dir, filename));
73 | this.filenameCache[dir][filename] = normalized;
74 | }
75 | return normalized;
76 | };
77 |
78 | engine.prototype.render = function(filename, options, cb) {
79 | return this.run(filename, options, cb);
80 | };
81 |
82 | engine.prototype.run = function(filename, options, cb) {
83 |
84 | /*
85 | "options" contains the pub vars and may contain special items:
86 | layout: path to a template expecting a body var (express 2.x style, but for use with express 3.x)
87 | postProcess: a function which takes the string of output and post processes it (returning new string)
88 | __toffee.dir: path to look relative to
89 | __toffee.parent: parent file
90 | __toffee.noInheritance: if true, don't pass variables through unless explicitly passed
91 | __toffee.repress if true, don't output anything; useful with including definition files with passback of vars
92 | __toffee.autoEscape: if set as false, don't escape output of #{} vars by default
93 | */
94 | var err, k, layout_options, post_process, ref1, ref2, ref3, ref4, ref5, res, v;
95 | if (options.prettyPrintErrors == null) {
96 | options.prettyPrintErrors = this.prettyPrintErrors;
97 | }
98 | if (options.prettyLogErrors == null) {
99 | options.prettyLogErrors = this.prettyLogErrors;
100 | }
101 | if (options.additionalErrorHandler == null) {
102 | options.additionalErrorHandler = this.additionalErrorHandler;
103 | }
104 | if (options.autoEscape == null) {
105 | options.autoEscape = this.autoEscape;
106 | }
107 | post_process = options.postProcess;
108 | options.postProcess = null;
109 | if (options != null ? options.layout : void 0) {
110 | layout_options = {};
111 | for (k in options) {
112 | v = options[k];
113 | if (k !== "layout") {
114 | layout_options[k] = v;
115 | }
116 | }
117 | }
118 | ref1 = this.runSync(filename, options), err = ref1[0], res = ref1[1];
119 | if (err && this.prettyPrintErrors) {
120 | ref2 = [null, err], err = ref2[0], res = ref2[1];
121 | }
122 | if ((!err) && (layout_options != null)) {
123 | layout_options.body = res;
124 | ref3 = this.runSync(options.layout, layout_options), err = ref3[0], res = ref3[1];
125 | if (err && this.prettyPrintErrors) {
126 | ref4 = [null, err], err = ref4[0], res = ref4[1];
127 | }
128 | }
129 | if ((!err) && (typeof post_process === "function")) {
130 | ref5 = this.postProcess(post_process, res), err = ref5[0], res = ref5[1];
131 | }
132 | return cb(err, res);
133 | };
134 |
135 | engine.prototype.postProcess = function(fn, res) {
136 | var e, err;
137 | err = null;
138 | try {
139 | res = fn(res);
140 | } catch (error) {
141 | e = error;
142 | err = e;
143 | }
144 | return [err, res];
145 | };
146 |
147 | engine.prototype.runSync = function(filename, options) {
148 |
149 | /*
150 | "options" the same as run() above
151 | */
152 | var ctx, err, realpath, ref1, ref2, ref3, res, start_time, v;
153 | start_time = Date.now();
154 | options = options || {};
155 | options.__toffee = options.__toffee || {};
156 | options.__toffee.dir = options.__toffee.dir || process.cwd();
157 | realpath = this.normalizeFilename(options.__toffee.dir, filename);
158 | if (this.cache) {
159 | v = (this._viewCacheGet(realpath)) || (this._loadCacheAndMonitor(realpath, options));
160 | } else {
161 | v = this._loadWithoutCache(realpath, options);
162 | }
163 | if (v) {
164 | if (this.fsErrorCache[realpath]) {
165 | ref1 = [new Error("Couldn't load " + realpath), null], err = ref1[0], res = ref1[1];
166 | } else {
167 | options.__toffee.parent = realpath;
168 | options.partial = options.partial || (function(_this) {
169 | return function(fname, lvars) {
170 | return _this._fn_partial(fname, lvars, realpath, options);
171 | };
172 | })(this);
173 | options.snippet = options.snippet || (function(_this) {
174 | return function(fname, lvars) {
175 | return _this._fn_snippet(fname, lvars, realpath, options);
176 | };
177 | })(this);
178 | options.load = options.load || (function(_this) {
179 | return function(fname, lvars) {
180 | return _this._fn_load(fname, lvars, realpath, options);
181 | };
182 | })(this);
183 | options.print = options.print || (function(_this) {
184 | return function(txt) {
185 | return _this._fn_print(txt, options);
186 | };
187 | })(this);
188 | if (options.console == null) {
189 | options.console = {
190 | log: console.log
191 | };
192 | }
193 | ctx = this.pool.get();
194 | ref2 = v.run(options, ctx), err = ref2[0], res = ref2[1];
195 | this.pool.release(ctx);
196 | }
197 | } else {
198 | ref3 = [new Error("Couldn't load " + realpath), null], err = ref3[0], res = ref3[1];
199 | }
200 | this._log(realpath + " run in " + (Date.now() - start_time) + "ms");
201 | return [err, res];
202 | };
203 |
204 | engine.prototype._viewCacheGet = function(filename) {
205 | if (this.viewCache[filename] == null) {
206 | return null;
207 | } else if (this.fsErrorCache[filename] == null) {
208 | return this.viewCache[filename];
209 | } else if ((Date.now() - this.fsErrorCache[filename]) < tweakables.MISSING_FILE_RECHECK) {
210 | return this.viewCache[filename];
211 | } else {
212 | return null;
213 | }
214 | };
215 |
216 | engine.prototype._inlineInclude = function(filename, local_vars, parent_realpath, parent_options) {
217 | var err, i, k, len, noInheritance, options, ref1, ref2, ref3, repress, res, reserved, v;
218 | options = local_vars || {};
219 | options.passback = {};
220 | options.__toffee = options.__toffee || {};
221 | options.__toffee.dir = path.dirname(parent_realpath);
222 | options.__toffee.parent = parent_realpath;
223 | noInheritance = options.__toffee.noInheritance;
224 | repress = options.__toffee.repress;
225 | reserved = {};
226 | ref1 = ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"];
227 | for (i = 0, len = ref1.length; i < len; i++) {
228 | k = ref1[i];
229 | reserved[k] = true;
230 | }
231 | if (!noInheritance) {
232 | for (k in parent_options) {
233 | v = parent_options[k];
234 | if ((local_vars != null ? local_vars[k] : void 0) == null) {
235 | if (reserved[k] == null) {
236 | options[k] = v;
237 | }
238 | }
239 | }
240 | }
241 | ref2 = this.runSync(filename, options), err = ref2[0], res = ref2[1];
242 | ref3 = options.passback;
243 | for (k in ref3) {
244 | v = ref3[k];
245 | parent_options[k] = v;
246 | }
247 | return err || res;
248 | };
249 |
250 | engine.prototype._fn_load = function(fname, lvars, realpath, options) {
251 | lvars = lvars != null ? lvars : {};
252 | lvars.__toffee = lvars.__toffee || {};
253 | lvars.__toffee.repress = true;
254 | return this._inlineInclude(fname, lvars, realpath, options);
255 | };
256 |
257 | engine.prototype._fn_snippet = function(fname, lvars, realpath, options) {
258 | lvars = lvars != null ? lvars : {};
259 | lvars.__toffee = lvars.__toffee || {};
260 | lvars.__toffee.noInheritance = true;
261 | return this._inlineInclude(fname, lvars, realpath, options);
262 | };
263 |
264 | engine.prototype._fn_partial = function(fname, lvars, realpath, options) {
265 | return this._inlineInclude(fname, lvars, realpath, options);
266 | };
267 |
268 | engine.prototype._fn_print = function(txt, options) {
269 | if (options.__toffee.state === states.COFFEE) {
270 | options.__toffee.out.push(txt);
271 | return '';
272 | } else {
273 | return txt;
274 | }
275 | };
276 |
277 | engine.prototype._loadWithoutCache = function(filename, options) {
278 | var e, ref1, txt, v, view_options;
279 | try {
280 | txt = fs.readFileSync(filename, 'utf8');
281 | } catch (error) {
282 | e = error;
283 | txt = "Error: Could not read " + filename;
284 | if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) {
285 | txt += " first requested in " + options.__toffee.parent;
286 | }
287 | }
288 | view_options = this._generateViewOptions(filename);
289 | v = new view(txt, view_options);
290 | return v;
291 | };
292 |
293 | engine.prototype._loadCacheAndMonitor = function(filename, options) {
294 | var e, previous_fs_err, ref1, txt, v, view_options;
295 | previous_fs_err = this.fsErrorCache[filename] != null;
296 | try {
297 | txt = fs.readFileSync(filename, 'utf8');
298 | if (this.fsErrorCache[filename] != null) {
299 | delete this.fsErrorCache[filename];
300 | }
301 | } catch (error) {
302 | e = error;
303 | txt = "Error: Could not read " + filename;
304 | if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) {
305 | txt += " first requested in " + options.__toffee.parent;
306 | }
307 | this.fsErrorCache[filename] = Date.now();
308 | }
309 | if (this.fsErrorCache[filename] && previous_fs_err && this.viewCache[filename]) {
310 | return this.viewCache[filename];
311 | } else {
312 | view_options = this._generateViewOptions(filename);
313 | v = new view(txt, view_options);
314 | this.viewCache[filename] = v;
315 | this._monitorForChanges(filename, options);
316 | return v;
317 | }
318 | };
319 |
320 | engine.prototype._reloadFileInBkg = function(filename, options) {
321 | this._log(filename + " acquiring lock to read");
322 | return this.fileLockTable.acquire2({
323 | name: filename
324 | }, (function(_this) {
325 | return function(lock) {
326 | return fs.readFile(filename, 'utf8', function(err, txt) {
327 | var ctx, ref1, v, view_options, waiting_for_view;
328 | if (!err) {
329 | _this._log((Date.now()) + " - " + filename + " changed to " + (txt != null ? txt.length : void 0) + " bytes. " + (txt != null ? typeof txt.replace === "function" ? txt.replace(/\n/g, '').slice(0, 80) : void 0 : void 0));
330 | }
331 | waiting_for_view = false;
332 | if (err || (txt !== _this.viewCache[filename].txt)) {
333 | if (err) {
334 | _this.fsErrorCache[filename] = Date.now();
335 | txt = "Error: Could not read " + filename;
336 | if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) {
337 | txt += " requested in " + options.__toffee.parent;
338 | }
339 | }
340 | if (!(err && _this.viewCache[filename].fsError)) {
341 | view_options = _this._generateViewOptions(filename);
342 | ctx = _this.pool.get();
343 | view_options.ctx = ctx;
344 | view_options.cb = function(v) {
345 | _this._log(filename + " updated and ready");
346 | _this.viewCache[filename] = v;
347 | _this.pool.release(ctx);
348 | _this._log(filename + " lock releasing (view_options.cb)");
349 | return lock.release();
350 | };
351 | waiting_for_view = true;
352 | if (err) {
353 | view_options.fsError = true;
354 | }
355 | v = new view(txt, view_options);
356 | }
357 | }
358 | if (!waiting_for_view) {
359 | _this._log(filename + " lock releasing (not waiting for view)");
360 | return lock.release();
361 | }
362 | });
363 | };
364 | })(this));
365 | };
366 |
367 | engine.prototype._generateViewOptions = function(filename) {
368 | return {
369 | fileName: filename,
370 | verbose: this.verbose,
371 | prettyPrintErrors: this.prettyPrintErrors,
372 | prettyLogErrors: this.prettyLogErrors,
373 | autoEscape: this.autoEscape,
374 | additionalErrorHandler: this.additionalErrorHandler
375 | };
376 | };
377 |
378 | engine.prototype._monitorForChanges = function(filename, options) {
379 |
380 | /*
381 | we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
382 | event and we'll end up following the wrong, old 'file' as a new one
383 | is dropped in its place.
384 |
385 | Files that are missing are ignored here because they get picked up by new calls to _loadCacheAndMonitor
386 | */
387 | var e, fsw;
388 | if (this.fsErrorCache[filename] == null) {
389 | fsw = null;
390 | try {
391 | this._log(filename + " starting fs.watch()");
392 | return fsw = fs.watch(filename, {
393 | persistent: true
394 | }, (function(_this) {
395 | return function(change) {
396 | _this._log(filename + " closing fs.watch()");
397 | fsw.close();
398 | _this._monitorForChanges(filename, options);
399 | return _this._reloadFileInBkg(filename, options);
400 | };
401 | })(this));
402 | } catch (error) {
403 | e = error;
404 | this._log("fs.watch() failed for " + filename + "; settings fsErrorCache = true");
405 | return this.fsErrorCache[filename] = Date.now();
406 | }
407 | }
408 | };
409 |
410 | return engine;
411 |
412 | })();
413 |
414 | exports.engine = engine;
415 |
416 | }).call(this);
417 |
--------------------------------------------------------------------------------
/lib/coffee-script/command.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, hidden, joinTimeout, lint, loadRequires, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref;
4 |
5 | fs = require('fs');
6 |
7 | path = require('path');
8 |
9 | helpers = require('./helpers');
10 |
11 | optparse = require('./optparse');
12 |
13 | CoffeeScript = require('./coffee-script');
14 |
15 | _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
16 |
17 | EventEmitter = require('events').EventEmitter;
18 |
19 | helpers.extend(CoffeeScript, new EventEmitter);
20 |
21 | printLine = function(line) {
22 | return process.stdout.write(line + '\n');
23 | };
24 |
25 | printWarn = function(line) {
26 | return process.stderr.write(line + '\n');
27 | };
28 |
29 | hidden = function(file) {
30 | return /^\.|~$/.test(file);
31 | };
32 |
33 | BANNER = 'Usage: coffee [options] path/to/script.coffee -- [args]\n\nIf called without options, `coffee` will run your script.';
34 |
35 | SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
36 |
37 | opts = {};
38 |
39 | sources = [];
40 |
41 | sourceCode = [];
42 |
43 | notSources = {};
44 |
45 | watchers = {};
46 |
47 | optionParser = null;
48 |
49 | exports.run = function() {
50 | var literals, source, _i, _len, _results;
51 | parseOptions();
52 | if (opts.nodejs) {
53 | return forkNode();
54 | }
55 | if (opts.help) {
56 | return usage();
57 | }
58 | if (opts.version) {
59 | return version();
60 | }
61 | if (opts.require) {
62 | loadRequires();
63 | }
64 | if (opts.interactive) {
65 | return require('./repl');
66 | }
67 | if (opts.watch && !fs.watch) {
68 | return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + ".");
69 | }
70 | if (opts.stdio) {
71 | return compileStdio();
72 | }
73 | if (opts["eval"]) {
74 | return compileScript(null, sources[0]);
75 | }
76 | if (!sources.length) {
77 | return require('./repl');
78 | }
79 | literals = opts.run ? sources.splice(1) : [];
80 | process.argv = process.argv.slice(0, 2).concat(literals);
81 | process.argv[0] = 'coffee';
82 | process.execPath = require.main.filename;
83 | _results = [];
84 | for (_i = 0, _len = sources.length; _i < _len; _i++) {
85 | source = sources[_i];
86 | _results.push(compilePath(source, true, path.normalize(source)));
87 | }
88 | return _results;
89 | };
90 |
91 | compilePath = function(source, topLevel, base) {
92 | return fs.stat(source, function(err, stats) {
93 | if (err && err.code !== 'ENOENT') {
94 | throw err;
95 | }
96 | if ((err != null ? err.code : void 0) === 'ENOENT') {
97 | if (topLevel && source.slice(-7) !== '.coffee') {
98 | source = sources[sources.indexOf(source)] = "" + source + ".coffee";
99 | return compilePath(source, topLevel, base);
100 | }
101 | if (topLevel) {
102 | console.error("File not found: " + source);
103 | process.exit(1);
104 | }
105 | return;
106 | }
107 | if (stats.isDirectory()) {
108 | if (opts.watch) {
109 | watchDir(source, base);
110 | }
111 | return fs.readdir(source, function(err, files) {
112 | var file, index, _i, _len, _ref1, _ref2, _results;
113 | if (err && err.code !== 'ENOENT') {
114 | throw err;
115 | }
116 | if ((err != null ? err.code : void 0) === 'ENOENT') {
117 | return;
118 | }
119 | index = sources.indexOf(source);
120 | [].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (function() {
121 | var _i, _len, _results;
122 | _results = [];
123 | for (_i = 0, _len = files.length; _i < _len; _i++) {
124 | file = files[_i];
125 | _results.push(path.join(source, file));
126 | }
127 | return _results;
128 | })())), _ref1;
129 | [].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
130 | return null;
131 | }))), _ref2;
132 | _results = [];
133 | for (_i = 0, _len = files.length; _i < _len; _i++) {
134 | file = files[_i];
135 | if (!hidden(file)) {
136 | _results.push(compilePath(path.join(source, file), false, base));
137 | }
138 | }
139 | return _results;
140 | });
141 | } else if (topLevel || path.extname(source) === '.coffee') {
142 | if (opts.watch) {
143 | watch(source, base);
144 | }
145 | return fs.readFile(source, function(err, code) {
146 | if (err && err.code !== 'ENOENT') {
147 | throw err;
148 | }
149 | if ((err != null ? err.code : void 0) === 'ENOENT') {
150 | return;
151 | }
152 | return compileScript(source, code.toString(), base);
153 | });
154 | } else {
155 | notSources[source] = true;
156 | return removeSource(source, base);
157 | }
158 | });
159 | };
160 |
161 | compileScript = function(file, input, base) {
162 | var o, options, t, task;
163 | o = opts;
164 | options = compileOptions(file);
165 | try {
166 | t = task = {
167 | file: file,
168 | input: input,
169 | options: options
170 | };
171 | CoffeeScript.emit('compile', task);
172 | if (o.tokens) {
173 | return printTokens(CoffeeScript.tokens(t.input));
174 | } else if (o.nodes) {
175 | return printLine(CoffeeScript.nodes(t.input).toString().trim());
176 | } else if (o.run) {
177 | return CoffeeScript.run(t.input, t.options);
178 | } else if (o.join && t.file !== o.join) {
179 | sourceCode[sources.indexOf(t.file)] = t.input;
180 | return compileJoin();
181 | } else {
182 | t.output = CoffeeScript.compile(t.input, t.options);
183 | CoffeeScript.emit('success', task);
184 | if (o.print) {
185 | return printLine(t.output.trim());
186 | } else if (o.compile) {
187 | return writeJs(t.file, t.output, base);
188 | } else if (o.lint) {
189 | return lint(t.file, t.output);
190 | }
191 | }
192 | } catch (err) {
193 | CoffeeScript.emit('failure', err, task);
194 | if (CoffeeScript.listeners('failure').length) {
195 | return;
196 | }
197 | if (o.watch) {
198 | return printLine(err.message + '\x07');
199 | }
200 | printWarn(err instanceof Error && err.stack || ("ERROR: " + err));
201 | return process.exit(1);
202 | }
203 | };
204 |
205 | compileStdio = function() {
206 | var code, stdin;
207 | code = '';
208 | stdin = process.openStdin();
209 | stdin.on('data', function(buffer) {
210 | if (buffer) {
211 | return code += buffer.toString();
212 | }
213 | });
214 | return stdin.on('end', function() {
215 | return compileScript(null, code);
216 | });
217 | };
218 |
219 | joinTimeout = null;
220 |
221 | compileJoin = function() {
222 | if (!opts.join) {
223 | return;
224 | }
225 | if (!sourceCode.some(function(code) {
226 | return code === null;
227 | })) {
228 | clearTimeout(joinTimeout);
229 | return joinTimeout = wait(100, function() {
230 | return compileScript(opts.join, sourceCode.join('\n'), opts.join);
231 | });
232 | }
233 | };
234 |
235 | loadRequires = function() {
236 | var realFilename, req, _i, _len, _ref1;
237 | realFilename = module.filename;
238 | module.filename = '.';
239 | _ref1 = opts.require;
240 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
241 | req = _ref1[_i];
242 | require(req);
243 | }
244 | return module.filename = realFilename;
245 | };
246 |
247 | watch = function(source, base) {
248 | var compile, compileTimeout, prevStats, rewatch, watchErr, watcher;
249 | prevStats = null;
250 | compileTimeout = null;
251 | watchErr = function(e) {
252 | if (e.code === 'ENOENT') {
253 | if (sources.indexOf(source) === -1) {
254 | return;
255 | }
256 | try {
257 | rewatch();
258 | return compile();
259 | } catch (e) {
260 | removeSource(source, base, true);
261 | return compileJoin();
262 | }
263 | } else {
264 | throw e;
265 | }
266 | };
267 | compile = function() {
268 | clearTimeout(compileTimeout);
269 | return compileTimeout = wait(25, function() {
270 | return fs.stat(source, function(err, stats) {
271 | if (err) {
272 | return watchErr(err);
273 | }
274 | if (prevStats && stats.size === prevStats.size && stats.mtime.getTime() === prevStats.mtime.getTime()) {
275 | return rewatch();
276 | }
277 | prevStats = stats;
278 | return fs.readFile(source, function(err, code) {
279 | if (err) {
280 | return watchErr(err);
281 | }
282 | compileScript(source, code.toString(), base);
283 | return rewatch();
284 | });
285 | });
286 | });
287 | };
288 | try {
289 | watcher = fs.watch(source, compile);
290 | } catch (e) {
291 | watchErr(e);
292 | }
293 | return rewatch = function() {
294 | if (watcher != null) {
295 | watcher.close();
296 | }
297 | return watcher = fs.watch(source, compile);
298 | };
299 | };
300 |
301 | watchDir = function(source, base) {
302 | var readdirTimeout, watcher;
303 | readdirTimeout = null;
304 | try {
305 | return watcher = fs.watch(source, function() {
306 | clearTimeout(readdirTimeout);
307 | return readdirTimeout = wait(25, function() {
308 | return fs.readdir(source, function(err, files) {
309 | var file, _i, _len, _results;
310 | if (err) {
311 | if (err.code !== 'ENOENT') {
312 | throw err;
313 | }
314 | watcher.close();
315 | return unwatchDir(source, base);
316 | }
317 | _results = [];
318 | for (_i = 0, _len = files.length; _i < _len; _i++) {
319 | file = files[_i];
320 | if (!(!hidden(file) && !notSources[file])) {
321 | continue;
322 | }
323 | file = path.join(source, file);
324 | if (sources.some(function(s) {
325 | return s.indexOf(file) >= 0;
326 | })) {
327 | continue;
328 | }
329 | sources.push(file);
330 | sourceCode.push(null);
331 | _results.push(compilePath(file, false, base));
332 | }
333 | return _results;
334 | });
335 | });
336 | });
337 | } catch (e) {
338 | if (e.code !== 'ENOENT') {
339 | throw e;
340 | }
341 | }
342 | };
343 |
344 | unwatchDir = function(source, base) {
345 | var file, prevSources, toRemove, _i, _len;
346 | prevSources = sources.slice(0);
347 | toRemove = (function() {
348 | var _i, _len, _results;
349 | _results = [];
350 | for (_i = 0, _len = sources.length; _i < _len; _i++) {
351 | file = sources[_i];
352 | if (file.indexOf(source) >= 0) {
353 | _results.push(file);
354 | }
355 | }
356 | return _results;
357 | })();
358 | for (_i = 0, _len = toRemove.length; _i < _len; _i++) {
359 | file = toRemove[_i];
360 | removeSource(file, base, true);
361 | }
362 | if (!sources.some(function(s, i) {
363 | return prevSources[i] !== s;
364 | })) {
365 | return;
366 | }
367 | return compileJoin();
368 | };
369 |
370 | removeSource = function(source, base, removeJs) {
371 | var index, jsPath;
372 | index = sources.indexOf(source);
373 | sources.splice(index, 1);
374 | sourceCode.splice(index, 1);
375 | if (removeJs && !opts.join) {
376 | jsPath = outputPath(source, base);
377 | return path.exists(jsPath, function(exists) {
378 | if (exists) {
379 | return fs.unlink(jsPath, function(err) {
380 | if (err && err.code !== 'ENOENT') {
381 | throw err;
382 | }
383 | return timeLog("removed " + source);
384 | });
385 | }
386 | });
387 | }
388 | };
389 |
390 | outputPath = function(source, base) {
391 | var baseDir, dir, filename, srcDir;
392 | filename = path.basename(source, path.extname(source)) + '.js';
393 | srcDir = path.dirname(source);
394 | baseDir = base === '.' ? srcDir : srcDir.substring(base.length);
395 | dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
396 | return path.join(dir, filename);
397 | };
398 |
399 | writeJs = function(source, js, base) {
400 | var compile, jsDir, jsPath;
401 | jsPath = outputPath(source, base);
402 | jsDir = path.dirname(jsPath);
403 | compile = function() {
404 | if (js.length <= 0) {
405 | js = ' ';
406 | }
407 | return fs.writeFile(jsPath, js, function(err) {
408 | if (err) {
409 | return printLine(err.message);
410 | } else if (opts.compile && opts.watch) {
411 | return timeLog("compiled " + source);
412 | }
413 | });
414 | };
415 | return path.exists(jsDir, function(exists) {
416 | if (exists) {
417 | return compile();
418 | } else {
419 | return exec("mkdir -p " + jsDir, compile);
420 | }
421 | });
422 | };
423 |
424 | wait = function(milliseconds, func) {
425 | return setTimeout(func, milliseconds);
426 | };
427 |
428 | timeLog = function(message) {
429 | return console.log("" + ((new Date).toLocaleTimeString()) + " - " + message);
430 | };
431 |
432 | lint = function(file, js) {
433 | var conf, jsl, printIt;
434 | printIt = function(buffer) {
435 | return printLine(file + ':\t' + buffer.toString().trim());
436 | };
437 | conf = __dirname + '/../../extras/jsl.conf';
438 | jsl = spawn('jsl', ['-nologo', '-stdin', '-conf', conf]);
439 | jsl.stdout.on('data', printIt);
440 | jsl.stderr.on('data', printIt);
441 | jsl.stdin.write(js);
442 | return jsl.stdin.end();
443 | };
444 |
445 | printTokens = function(tokens) {
446 | var strings, tag, token, value;
447 | strings = (function() {
448 | var _i, _len, _ref1, _results;
449 | _results = [];
450 | for (_i = 0, _len = tokens.length; _i < _len; _i++) {
451 | token = tokens[_i];
452 | _ref1 = [token[0], token[1].toString().replace(/\n/, '\\n')], tag = _ref1[0], value = _ref1[1];
453 | _results.push("[" + tag + " " + value + "]");
454 | }
455 | return _results;
456 | })();
457 | return printLine(strings.join(' '));
458 | };
459 |
460 | parseOptions = function() {
461 | var i, o, source, _i, _len;
462 | optionParser = new optparse.OptionParser(SWITCHES, BANNER);
463 | o = opts = optionParser.parse(process.argv.slice(2));
464 | o.compile || (o.compile = !!o.output);
465 | o.run = !(o.compile || o.print || o.lint);
466 | o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
467 | sources = o["arguments"];
468 | for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) {
469 | source = sources[i];
470 | sourceCode[i] = null;
471 | }
472 | };
473 |
474 | compileOptions = function(filename) {
475 | return {
476 | filename: filename,
477 | bare: opts.bare,
478 | header: opts.compile
479 | };
480 | };
481 |
482 | forkNode = function() {
483 | var args, nodeArgs;
484 | nodeArgs = opts.nodejs.split(/\s+/);
485 | args = process.argv.slice(1);
486 | args.splice(args.indexOf('--nodejs'), 2);
487 | return spawn(process.execPath, nodeArgs.concat(args), {
488 | cwd: process.cwd(),
489 | env: process.env,
490 | customFds: [0, 1, 2]
491 | });
492 | };
493 |
494 | usage = function() {
495 | return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help());
496 | };
497 |
498 | version = function() {
499 | return printLine("CoffeeScript version " + CoffeeScript.VERSION);
500 | };
501 |
502 | }).call(this);
503 |
--------------------------------------------------------------------------------
|