pax_global_header00006660000000000000000000000064132222224770014515gustar00rootroot0000000000000052 comment=2b17e7c8a5a9586f93483333babbbf53ec088f78 gulp-coffee-3.0.2/000077500000000000000000000000001322222247700137135ustar00rootroot00000000000000gulp-coffee-3.0.2/.gitignore000066400000000000000000000002161322222247700157020ustar00rootroot00000000000000.DS_Store build lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results npm-debug.log node_modules *.sublime* package-lock.json gulp-coffee-3.0.2/.npmignore000066400000000000000000000002161322222247700157110ustar00rootroot00000000000000.DS_Store build lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results npm-debug.log node_modules *.sublime* package-lock.json gulp-coffee-3.0.2/.travis.yml000066400000000000000000000000711322222247700160220ustar00rootroot00000000000000sudo: false language: node_js node_js: - 6 - 7 - 8 gulp-coffee-3.0.2/LICENSE000077500000000000000000000020511322222247700147210ustar00rootroot00000000000000Copyright (c) 2015 Contra 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. gulp-coffee-3.0.2/README.md000066400000000000000000000062041322222247700151740ustar00rootroot00000000000000[![Build Status](https://secure.travis-ci.org/contra/gulp-coffee.png?branch=master)](https://travis-ci.org/contra/gulp-coffee) ## Information
Packagegulp-coffee
Description Compiles CoffeeScript
Node Version >= 0.9
## Usage ```javascript var coffee = require('gulp-coffee'); gulp.task('coffee', function() { gulp.src('./src/*.coffee') .pipe(coffee({bare: true})) .pipe(gulp.dest('./public/')); }); ``` ## Options - `coffee` (optional): A reference to a custom CoffeeScript version/fork (eg. `coffee: require('my-name/coffeescript')`) Additionally, the options object supports all options that are supported by the standard CoffeeScript compiler. ## Source maps ### gulp 3.x gulp-coffee can be used in tandem with [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) to generate source maps for the coffee to javascript transition. You will need to initialize [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) prior to running the gulp-coffee compiler and write the source maps after. ```javascript var sourcemaps = require('gulp-sourcemaps'); gulp.src('./src/*.coffee') .pipe(sourcemaps.init()) .pipe(coffee()) .pipe(sourcemaps.write()) .pipe(gulp.dest('./dest/js')); // will write the source maps inline in the compiled javascript files ``` By default, [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) writes the source maps inline in the compiled javascript files. To write them to a separate file, specify a relative file path in the `sourcemaps.write()` function. ```javascript var sourcemaps = require('gulp-sourcemaps'); gulp.src('./src/*.coffee') .pipe(sourcemaps.init()) .pipe(coffee({ bare: true })) .pipe(sourcemaps.write('./maps')) .pipe(gulp.dest('./dest/js')); // will write the source maps to ./dest/js/maps ``` ### gulp 4.x In gulp 4, sourcemaps are built-in by default. ```js gulp.src('./src/*.coffee', { sourcemaps: true }) .pipe(coffee({ bare: true })) .pipe(gulp.dest('./dest/js')); ``` ## LICENSE (MIT License) Copyright (c) 2015 Fractal 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. gulp-coffee-3.0.2/index.js000066400000000000000000000026761322222247700153730ustar00rootroot00000000000000var through = require('through2'); var applySourceMap = require('vinyl-sourcemaps-apply'); var path = require('path'); var replaceExt = require('replace-ext'); var PluginError = require('plugin-error'); var merge = require('merge'); module.exports = function (opt) { function replaceExtension(path) { path = path.replace(/\.coffee\.md$/, '.litcoffee'); return replaceExt(path, '.js'); } function transform(file, enc, cb) { if (file.isNull()) return cb(null, file); if (file.isStream()) return cb(new PluginError('gulp-coffee', 'Streaming not supported')); var data; var str = file.contents.toString('utf8'); var dest = replaceExtension(file.path); var options = merge({ bare: false, coffee: require('coffeescript'), header: false, sourceMap: !!file.sourceMap, sourceRoot: false, literate: /\.(litcoffee|coffee\.md)$/.test(file.path), filename: file.path, sourceFiles: [file.relative], generatedFile: replaceExtension(file.relative) }, opt); try { data = options.coffee.compile(str, options); } catch (err) { return cb(new PluginError('gulp-coffee', err)); } if (data && data.v3SourceMap && file.sourceMap) { applySourceMap(file, data.v3SourceMap); file.contents = new Buffer(data.js); } else { file.contents = new Buffer(data); } file.path = dest; cb(null, file); } return through.obj(transform); }; gulp-coffee-3.0.2/package.json000066400000000000000000000013751322222247700162070ustar00rootroot00000000000000{ "name": "gulp-coffee", "description": "Compile CoffeeScript files", "version": "3.0.2", "homepage": "http://github.com/contra/gulp-coffee", "repository": "git://github.com/contra/gulp-coffee.git", "author": "Contra (http://contra.io/)", "main": "./index.js", "keywords": [ "gulpplugin" ], "dependencies": { "coffeescript": "^2.1.0", "merge": "^1.2.0", "plugin-error": "^0.1.2", "replace-ext": "^1.0.0", "through2": "^2.0.1", "vinyl-sourcemaps-apply": "^0.2.1" }, "devDependencies": { "gulp-sourcemaps": "^2.6.2", "mocha": "^4.0.0", "should": "^13.0.0", "vinyl": "^2.1.0" }, "scripts": { "test": "mocha" }, "engines": { "node": ">= 6.0.0" }, "license": "MIT" } gulp-coffee-3.0.2/test/000077500000000000000000000000001322222247700146725ustar00rootroot00000000000000gulp-coffee-3.0.2/test/fixtures/000077500000000000000000000000001322222247700165435ustar00rootroot00000000000000gulp-coffee-3.0.2/test/fixtures/grammar.coffee000066400000000000000000000576571322222247700213660ustar00rootroot00000000000000# The CoffeeScript parser is generated by [Jison](http://github.com/zaach/jison) # from this grammar file. Jison is a bottom-up parser generator, similar in # style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript. # It can recognize [LALR(1), LR(0), SLR(1), and LR(1)](http://en.wikipedia.org/wiki/LR_grammar) # type grammars. To create the Jison parser, we list the pattern to match # on the left-hand side, and the action to take (usually the creation of syntax # tree nodes) on the right. As the parser runs, it # shifts tokens from our token stream, from left to right, and # [attempts to match](http://en.wikipedia.org/wiki/Bottom-up_parsing) # the token sequence against the rules below. When a match can be made, it # reduces into the [nonterminal](http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols) # (the enclosing name at the top), and we proceed from there. # # If you run the `cake build:parser` command, Jison constructs a parse table # from our rules and saves it into `lib/parser.js`. # The only dependency is on the **Jison.Parser**. {Parser} = require 'jison' # Jison DSL # --------- # Since we're going to be wrapped in a function by Jison in any case, if our # action immediately returns a value, we can optimize by removing the function # wrapper and just returning the value directly. unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/ # Our handy DSL for Jison grammar generation, thanks to # [Tim Caswell](http://github.com/creationix). For every rule in the grammar, # we pass the pattern-defining string, the action to run, and extra options, # optionally. If no action is specified, we simply pass the value of the # previous nonterminal. o = (patternString, action, options) -> patternString = patternString.replace /\s{2,}/g, ' ' patternCount = patternString.split(' ').length return [patternString, '$$ = $1;', options] unless action action = if match = unwrap.exec action then match[1] else "(#{action}())" # All runtime functions we need are defined on "yy" action = action.replace /\bnew /g, '$&yy.' action = action.replace /\b(?:Block\.wrap|extend)\b/g, 'yy.$&' # Returns a function which adds location data to the first parameter passed # in, and returns the parameter. If the parameter is not a node, it will # just be passed through unaffected. addLocationDataFn = (first, last) -> if not last "yy.addLocationDataFn(@#{first})" else "yy.addLocationDataFn(@#{first}, @#{last})" action = action.replace /LOC\(([0-9]*)\)/g, addLocationDataFn('$1') action = action.replace /LOC\(([0-9]*),\s*([0-9]*)\)/g, addLocationDataFn('$1', '$2') [patternString, "$$ = #{addLocationDataFn(1, patternCount)}(#{action});", options] # Grammatical Rules # ----------------- # In all of the rules that follow, you'll see the name of the nonterminal as # the key to a list of alternative matches. With each match's action, the # dollar-sign variables are provided by Jison as references to the value of # their numeric position, so in this rule: # # "Expression UNLESS Expression" # # `$1` would be the value of the first `Expression`, `$2` would be the token # for the `UNLESS` terminal, and `$3` would be the value of the second # `Expression`. grammar = # The **Root** is the top-level node in the syntax tree. Since we parse bottom-up, # all parsing must end here. Root: [ o '', -> new Block o 'Body' ] # Any list of statements and expressions, separated by line breaks or semicolons. Body: [ o 'Line', -> Block.wrap [$1] o 'Body TERMINATOR Line', -> $1.push $3 o 'Body TERMINATOR' ] # Block and statements, which make up a line in a body. Line: [ o 'Expression' o 'Statement' ] # Pure statements which cannot be expressions. Statement: [ o 'Return' o 'Comment' o 'STATEMENT', -> new Literal $1 ] # All the different types of expressions in our language. The basic unit of # CoffeeScript is the **Expression** -- everything that can be an expression # is one. Blocks serve as the building blocks of many other rules, making # them somewhat circular. Expression: [ o 'Value' o 'Invocation' o 'Code' o 'Operation' o 'Assign' o 'If' o 'Try' o 'While' o 'For' o 'Switch' o 'Class' o 'Throw' ] # An indented block of expressions. Note that the [Rewriter](rewriter.html) # will convert some postfix forms into blocks for us, by adjusting the # token stream. Block: [ o 'INDENT OUTDENT', -> new Block o 'INDENT Body OUTDENT', -> $2 ] # A literal identifier, a variable name or property. Identifier: [ o 'IDENTIFIER', -> new Literal $1 ] # Alphanumerics are separated from the other **Literal** matchers because # they can also serve as keys in object literals. AlphaNumeric: [ o 'NUMBER', -> new Literal $1 o 'STRING', -> new Literal $1 ] # All of our immediate values. Generally these can be passed straight # through and printed to JavaScript. Literal: [ o 'AlphaNumeric' o 'JS', -> new Literal $1 o 'REGEX', -> new Literal $1 o 'DEBUGGER', -> new Literal $1 o 'UNDEFINED', -> new Undefined o 'NULL', -> new Null o 'BOOL', -> new Bool $1 ] # Assignment of a variable, property, or index to a value. Assign: [ o 'Assignable = Expression', -> new Assign $1, $3 o 'Assignable = TERMINATOR Expression', -> new Assign $1, $4 o 'Assignable = INDENT Expression OUTDENT', -> new Assign $1, $4 ] # Assignment when it happens within an object literal. The difference from # the ordinary **Assign** is that these allow numbers and strings as keys. AssignObj: [ o 'ObjAssignable', -> new Value $1 o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value($1)), $3, 'object' o 'ObjAssignable : INDENT Expression OUTDENT', -> new Assign LOC(1)(new Value($1)), $4, 'object' o 'Comment' ] ObjAssignable: [ o 'Identifier' o 'AlphaNumeric' o 'ThisProperty' ] # A return statement from a function body. Return: [ o 'RETURN Expression', -> new Return $2 o 'RETURN', -> new Return ] # A block comment. Comment: [ o 'HERECOMMENT', -> new Comment $1 ] # The **Code** node is the function literal. It's defined by an indented block # of **Block** preceded by a function arrow, with an optional parameter # list. Code: [ o 'PARAM_START ParamList PARAM_END FuncGlyph Block', -> new Code $2, $5, $4 o 'FuncGlyph Block', -> new Code [], $2, $1 ] # CoffeeScript has two different symbols for functions. `->` is for ordinary # functions, and `=>` is for functions bound to the current value of *this*. FuncGlyph: [ o '->', -> 'func' o '=>', -> 'boundfunc' ] # An optional, trailing comma. OptComma: [ o '' o ',' ] # The list of parameters that a function accepts can be of any length. ParamList: [ o '', -> [] o 'Param', -> [$1] o 'ParamList , Param', -> $1.concat $3 o 'ParamList OptComma TERMINATOR Param', -> $1.concat $4 o 'ParamList OptComma INDENT ParamList OptComma OUTDENT', -> $1.concat $4 ] # A single parameter in a function definition can be ordinary, or a splat # that hoovers up the remaining arguments. Param: [ o 'ParamVar', -> new Param $1 o 'ParamVar ...', -> new Param $1, null, on o 'ParamVar = Expression', -> new Param $1, $3 ] # Function Parameters ParamVar: [ o 'Identifier' o 'ThisProperty' o 'Array' o 'Object' ] # A splat that occurs outside of a parameter list. Splat: [ o 'Expression ...', -> new Splat $1 ] # Variables and properties that can be assigned to. SimpleAssignable: [ o 'Identifier', -> new Value $1 o 'Value Accessor', -> $1.add $2 o 'Invocation Accessor', -> new Value $1, [].concat $2 o 'ThisProperty' ] # Everything that can be assigned to. Assignable: [ o 'SimpleAssignable' o 'Array', -> new Value $1 o 'Object', -> new Value $1 ] # The types of things that can be treated as values -- assigned to, invoked # as functions, indexed into, named as a class, etc. Value: [ o 'Assignable' o 'Literal', -> new Value $1 o 'Parenthetical', -> new Value $1 o 'Range', -> new Value $1 o 'This' ] # The general group of accessors into an object, by property, by prototype # or by array index or slice. Accessor: [ o '. Identifier', -> new Access $2 o '?. Identifier', -> new Access $2, 'soak' o ':: Identifier', -> [LOC(1)(new Access new Literal('prototype')), LOC(2)(new Access $2)] o '?:: Identifier', -> [LOC(1)(new Access new Literal('prototype'), 'soak'), LOC(2)(new Access $2)] o '::', -> new Access new Literal 'prototype' o 'Index' ] # Indexing into an object or array using bracket notation. Index: [ o 'INDEX_START IndexValue INDEX_END', -> $2 o 'INDEX_SOAK Index', -> extend $2, soak : yes ] IndexValue: [ o 'Expression', -> new Index $1 o 'Slice', -> new Slice $1 ] # In CoffeeScript, an object literal is simply a list of assignments. Object: [ o '{ AssignList OptComma }', -> new Obj $2, $1.generated ] # Assignment of properties within an object literal can be separated by # comma, as in JavaScript, or simply by newline. AssignList: [ o '', -> [] o 'AssignObj', -> [$1] o 'AssignList , AssignObj', -> $1.concat $3 o 'AssignList OptComma TERMINATOR AssignObj', -> $1.concat $4 o 'AssignList OptComma INDENT AssignList OptComma OUTDENT', -> $1.concat $4 ] # Class definitions have optional bodies of prototype property assignments, # and optional references to the superclass. Class: [ o 'CLASS', -> new Class o 'CLASS Block', -> new Class null, null, $2 o 'CLASS EXTENDS Expression', -> new Class null, $3 o 'CLASS EXTENDS Expression Block', -> new Class null, $3, $4 o 'CLASS SimpleAssignable', -> new Class $2 o 'CLASS SimpleAssignable Block', -> new Class $2, null, $3 o 'CLASS SimpleAssignable EXTENDS Expression', -> new Class $2, $4 o 'CLASS SimpleAssignable EXTENDS Expression Block', -> new Class $2, $4, $5 ] # Ordinary function invocation, or a chained series of calls. Invocation: [ o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2 o 'Invocation OptFuncExist Arguments', -> new Call $1, $3, $2 o 'SUPER', -> new Call 'super', [new Splat new Literal 'arguments'] o 'SUPER Arguments', -> new Call 'super', $2 ] # An optional existence check on a function. OptFuncExist: [ o '', -> no o 'FUNC_EXIST', -> yes ] # The list of arguments to a function call. Arguments: [ o 'CALL_START CALL_END', -> [] o 'CALL_START ArgList OptComma CALL_END', -> $2 ] # A reference to the *this* current object. This: [ o 'THIS', -> new Value new Literal 'this' o '@', -> new Value new Literal 'this' ] # A reference to a property on *this*. ThisProperty: [ o '@ Identifier', -> new Value LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this' ] # The array literal. Array: [ o '[ ]', -> new Arr [] o '[ ArgList OptComma ]', -> new Arr $2 ] # Inclusive and exclusive range dots. RangeDots: [ o '..', -> 'inclusive' o '...', -> 'exclusive' ] # The CoffeeScript range literal. Range: [ o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3 ] # Array slice literals. Slice: [ o 'Expression RangeDots Expression', -> new Range $1, $3, $2 o 'Expression RangeDots', -> new Range $1, null, $2 o 'RangeDots Expression', -> new Range null, $2, $1 o 'RangeDots', -> new Range null, null, $1 ] # The **ArgList** is both the list of objects passed into a function call, # as well as the contents of an array literal # (i.e. comma-separated expressions). Newlines work as well. ArgList: [ o 'Arg', -> [$1] o 'ArgList , Arg', -> $1.concat $3 o 'ArgList OptComma TERMINATOR Arg', -> $1.concat $4 o 'INDENT ArgList OptComma OUTDENT', -> $2 o 'ArgList OptComma INDENT ArgList OptComma OUTDENT', -> $1.concat $4 ] # Valid arguments are Blocks or Splats. Arg: [ o 'Expression' o 'Splat' ] # Just simple, comma-separated, required arguments (no fancy syntax). We need # this to be separate from the **ArgList** for use in **Switch** blocks, where # having the newlines wouldn't make sense. SimpleArgs: [ o 'Expression' o 'SimpleArgs , Expression', -> [].concat $1, $3 ] # The variants of *try/catch/finally* exception handling blocks. Try: [ o 'TRY Block', -> new Try $2 o 'TRY Block Catch', -> new Try $2, $3[0], $3[1] o 'TRY Block FINALLY Block', -> new Try $2, null, null, $4 o 'TRY Block Catch FINALLY Block', -> new Try $2, $3[0], $3[1], $5 ] # A catch clause names its error and runs a block of code. Catch: [ o 'CATCH Identifier Block', -> [$2, $3] o 'CATCH Object Block', -> [LOC(2)(new Value($2)), $3] o 'CATCH Block', -> [null, $2] ] # Throw an exception object. Throw: [ o 'THROW Expression', -> new Throw $2 ] # Parenthetical expressions. Note that the **Parenthetical** is a **Value**, # not an **Expression**, so if you need to use an expression in a place # where only values are accepted, wrapping it in parentheses will always do # the trick. Parenthetical: [ o '( Body )', -> new Parens $2 o '( INDENT Body OUTDENT )', -> new Parens $3 ] # The condition portion of a while loop. WhileSource: [ o 'WHILE Expression', -> new While $2 o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4 o 'UNTIL Expression', -> new While $2, invert: true o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4 ] # The while loop can either be normal, with a block of expressions to execute, # or postfix, with a single expression. There is no do..while. While: [ o 'WhileSource Block', -> $1.addBody $2 o 'Statement WhileSource', -> $2.addBody LOC(1) Block.wrap([$1]) o 'Expression WhileSource', -> $2.addBody LOC(1) Block.wrap([$1]) o 'Loop', -> $1 ] Loop: [ o 'LOOP Block', -> new While(LOC(1) new Literal 'true').addBody $2 o 'LOOP Expression', -> new While(LOC(1) new Literal 'true').addBody LOC(2) Block.wrap [$2] ] # Array, object, and range comprehensions, at the most generic level. # Comprehensions can either be normal, with a block of expressions to execute, # or postfix, with a single expression. For: [ o 'Statement ForBody', -> new For $1, $2 o 'Expression ForBody', -> new For $1, $2 o 'ForBody Block', -> new For $2, $1 ] ForBody: [ o 'FOR Range', -> source: LOC(2) new Value($2) o 'ForStart ForSource', -> $2.own = $1.own; $2.name = $1[0]; $2.index = $1[1]; $2 ] ForStart: [ o 'FOR ForVariables', -> $2 o 'FOR OWN ForVariables', -> $3.own = yes; $3 ] # An array of all accepted values for a variable inside the loop. # This enables support for pattern matching. ForValue: [ o 'Identifier' o 'ThisProperty' o 'Array', -> new Value $1 o 'Object', -> new Value $1 ] # An array or range comprehension has variables for the current element # and (optional) reference to the current index. Or, *key, value*, in the case # of object comprehensions. ForVariables: [ o 'ForValue', -> [$1] o 'ForValue , ForValue', -> [$1, $3] ] # The source of a comprehension is an array or object with an optional guard # clause. If it's an array comprehension, you can also choose to step through # in fixed-size increments. ForSource: [ o 'FORIN Expression', -> source: $2 o 'FOROF Expression', -> source: $2, object: yes o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4 o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes o 'FORIN Expression BY Expression', -> source: $2, step: $4 o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6 o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6 ] Switch: [ o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4 o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6 o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3 o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5 ] Whens: [ o 'When' o 'Whens When', -> $1.concat $2 ] # An individual **When** clause, with action. When: [ o 'LEADING_WHEN SimpleArgs Block', -> [[$2, $3]] o 'LEADING_WHEN SimpleArgs Block TERMINATOR', -> [[$2, $3]] ] # The most basic form of *if* is a condition and an action. The following # if-related rules are broken up along these lines in order to avoid # ambiguity. IfBlock: [ o 'IF Expression Block', -> new If $2, $3, type: $1 o 'IfBlock ELSE IF Expression Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3 ] # The full complement of *if* expressions, including postfix one-liner # *if* and *unless*. If: [ o 'IfBlock' o 'IfBlock ELSE Block', -> $1.addElse $3 o 'Statement POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true o 'Expression POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true ] # Arithmetic and logical operators, working on one or more operands. # Here they are grouped by order of precedence. The actual precedence rules # are defined at the bottom of the page. It would be shorter if we could # combine most of these rules into a single generic *Operand OpSymbol Operand* # -type rule, but in order to make the precedence binding possible, separate # rules are necessary. Operation: [ o 'UNARY Expression', -> new Op $1 , $2 o '- Expression', (-> new Op '-', $2), prec: 'UNARY' o '+ Expression', (-> new Op '+', $2), prec: 'UNARY' o '-- SimpleAssignable', -> new Op '--', $2 o '++ SimpleAssignable', -> new Op '++', $2 o 'SimpleAssignable --', -> new Op '--', $1, null, true o 'SimpleAssignable ++', -> new Op '++', $1, null, true # [The existential operator](http://jashkenas.github.com/coffee-script/#existence). o 'Expression ?', -> new Existence $1 o 'Expression + Expression', -> new Op '+' , $1, $3 o 'Expression - Expression', -> new Op '-' , $1, $3 o 'Expression MATH Expression', -> new Op $2, $1, $3 o 'Expression SHIFT Expression', -> new Op $2, $1, $3 o 'Expression COMPARE Expression', -> new Op $2, $1, $3 o 'Expression LOGIC Expression', -> new Op $2, $1, $3 o 'Expression RELATION Expression', -> if $2.charAt(0) is '!' new Op($2[1..], $1, $3).invert() else new Op $2, $1, $3 o 'SimpleAssignable COMPOUND_ASSIGN Expression', -> new Assign $1, $3, $2 o 'SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT', -> new Assign $1, $4, $2 o 'SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression', -> new Assign $1, $4, $2 o 'SimpleAssignable EXTENDS Expression', -> new Extends $1, $3 ] # Precedence # ---------- # Operators at the top of this list have higher precedence than the ones lower # down. Following these rules is what makes `2 + 3 * 4` parse as: # # 2 + (3 * 4) # # And not: # # (2 + 3) * 4 operators = [ ['left', '.', '?.', '::', '?::'] ['left', 'CALL_START', 'CALL_END'] ['nonassoc', '++', '--'] ['left', '?'] ['right', 'UNARY'] ['left', 'MATH'] ['left', '+', '-'] ['left', 'SHIFT'] ['left', 'RELATION'] ['left', 'COMPARE'] ['left', 'LOGIC'] ['nonassoc', 'INDENT', 'OUTDENT'] ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'] ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'] ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'] ['left', 'POST_IF'] ] # Wrapping Up # ----------- # Finally, now that we have our **grammar** and our **operators**, we can create # our **Jison.Parser**. We do this by processing all of our rules, recording all # terminals (every symbol which does not appear as the name of a rule above) # as "tokens". tokens = [] for name, alternatives of grammar grammar[name] = for alt in alternatives for token in alt[0].split ' ' tokens.push token unless grammar[token] alt[1] = "return #{alt[1]}" if name is 'Root' alt # Initialize the **Parser** with our list of terminal **tokens**, our **grammar** # rules, and the name of the root. Reverse the operators because Jison orders # precedence from low to high, and we have it high to low # (as in [Yacc](http://dinosaur.compilertools.net/yacc/index.html)). exports.parser = new Parser tokens : tokens.join ' ' bnf : grammar operators : operators.reverse() startSymbol : 'Root' gulp-coffee-3.0.2/test/fixtures/journo.coffee.md000066400000000000000000000346261322222247700216420ustar00rootroot00000000000000Journo ====== Journo = module.exports = {} Journo is a blogging program, with a few basic goals. To wit: * Write in Markdown. * Publish to flat files. * Publish via Rsync. * Maintain a manifest file (what's published and what isn't, pub dates). * Retina ready. * Syntax highlight code. * Publish a feed. * Quickly bootstrap a new blog. * Preview via a local server. * Work without JavaScript, but default to a fluid JavaScript-enabled UI. You can install and use the `journo` command via npm: `sudo npm install -g journo` ... now, let's go through those features one at a time: Getting Started --------------- 1. Create a folder for your blog, and `cd` into it. 2. Type `journo init` to bootstrap a new empty blog. 3. Edit the `config.json`, `layout.html`, and `posts/index.md` files to suit. 4. Type `journo` to start the preview server, and have at it. Write in Markdown ----------------- We'll use the excellent **marked** module to compile Markdown into HTML, and Underscore for many of its goodies later on. Up top, create a namespace for shared values needed by more than one function. marked = require 'marked' _ = require 'underscore' shared = {} To render a post, we take its raw `source`, treat it as both an Underscore template (for HTML generation) and as Markdown (for formatting), and insert it into the layout as `content`. Journo.render = (post, source) -> catchErrors -> do loadLayout source or= fs.readFileSync postPath post variables = renderVariables post markdown = _.template(source.toString()) variables title = detectTitle markdown content = marked.parser marked.lexer markdown shared.layout _.extend variables, {title, content} A Journo site has a layout file, stored in `layout.html`, which is used to wrap every page. loadLayout = (force) -> return layout if not force and layout = shared.layout shared.layout = _.template(fs.readFileSync('layout.html').toString()) Determine the appropriate command to "open" a url in the browser for the current platform. opener = switch process.platform when 'darwin' then 'open' when 'win32' then 'start' else 'xdg-open' Publish to Flat Files --------------------- A blog is a folder on your hard drive. Within the blog, you have a `posts` folder for blog posts, a `public` folder for static content, a `layout.html` file for the layout which wraps every page, and a `journo.json` file for configuration. During a `build`, a static version of the site is rendered into the `site` folder, by **rsync**ing over all static files, rendering and writing every post, and creating an RSS feed. fs = require 'fs' path = require 'path' {spawn, exec} = require 'child_process' Journo.build = -> do loadManifest fs.mkdirSync('site') unless fs.existsSync('site') exec "rsync -vur --delete public/ site", (err, stdout, stderr) -> throw err if err for post in folderContents('posts') html = Journo.render post file = htmlPath post fs.mkdirSync path.dirname(file) unless fs.existsSync path.dirname(file) fs.writeFileSync file, html fs.writeFileSync "site/feed.rss", Journo.feed() The `config.json` configuration file is where you keep the configuration details of your blog, and how to connect to the server you'd like to publish it on. The valid settings are: `title`, `description`, `author` (for RSS), `url `, `publish` (the `user@host:path` location to **rsync** to), and `publishPort` (if your server doesn't listen to SSH on the usual one). An example `config.json` will be bootstrapped for you when you initialize a blog, so you don't need to remember any of that. loadConfig = -> return if shared.config try shared.config = JSON.parse fs.readFileSync 'config.json' catch err fatal "Unable to read config.json" shared.siteUrl = shared.config.url.replace(/\/$/, '') Publish via rsync ----------------- Publishing is nice and rudimentary. We build out an entirely static version of the site and **rysnc** it up to the server. Journo.publish = -> do Journo.build rsync 'site/images/', path.join(shared.config.publish, 'images/'), -> rsync 'site/', shared.config.publish A helper function for **rsync**ing, with logging, and the ability to wait for the rsync to continue before proceeding. This is useful for ensuring that our any new photos have finished uploading (very slowly) before the update to the feed is syndicated out. rsync = (from, to, callback) -> port = "ssh -p #{shared.config.publishPort or 22}" child = spawn "rsync", ['-vurz', '--delete', '-e', port, from, to] child.stdout.on 'data', (out) -> console.log out.toString() child.stderr.on 'data', (err) -> console.error err.toString() child.on 'exit', callback if callback Maintain a Manifest File ------------------------ The "manifest" is where Journo keeps track of metadata -- the title, description, publications date and last modified time of each post. Everything you need to render out an RSS feed ... and everything you need to know if a post has been updated or removed. manifestPath = 'journo-manifest.json' loadManifest = -> do loadConfig shared.manifest = if fs.existsSync manifestPath JSON.parse fs.readFileSync manifestPath else {} do updateManifest fs.writeFileSync manifestPath, JSON.stringify shared.manifest We update the manifest by looping through every post and every entry in the existing manifest, looking for differences in `mtime`, and recording those along with the title and description of each post. updateManifest = -> manifest = shared.manifest posts = folderContents 'posts' delete manifest[post] for post of manifest when post not in posts for post in posts stat = fs.statSync postPath post entry = manifest[post] if not entry or entry.mtime isnt stat.mtime entry or= {pubtime: stat.ctime} entry.mtime = stat.mtime content = fs.readFileSync(postPath post).toString() entry.title = detectTitle content entry.description = detectDescription content, post manifest[post] = entry yes Retina Ready ------------ In the future, it may make sense for Journo to have some sort of built-in facility for automatically downsizing photos from retina to regular sizes ... But for now, this bit is up to you. Syntax Highlight Code --------------------- We syntax-highlight blocks of code with the nifty **highlight** package that includes heuristics for auto-language detection, so you don't have to specify what you're coding in. highlight = require 'highlight.js' marked.setOptions highlight: (code, lang) -> if highlight.LANGUAGES[lang]? highlight.highlight(lang, code, true).value else highlight.highlightAuto(code).value Publish a Feed -------------- We'll use the **rss** module to build a simple feed of recent posts. Start with the basic `author`, blog `title`, `description` and `url` configured in the `config.json`. Then, each post's `title` is the first header present in the post, the `description` is the first paragraph, and the date is the date you first created the post file. Journo.feed = -> RSS = require 'rss' do loadConfig config = shared.config feed = new RSS title: config.title description: config.description feed_url: "#{shared.siteUrl}/rss.xml" site_url: shared.siteUrl author: config.author for post in sortedPosts().reverse()[0...20] entry = shared.manifest[post] feed.item title: entry.title description: entry.description url: postUrl post date: entry.pubtime feed.xml() Quickly Bootstrap a New Blog ---------------------------- We **init** a new blog into the current directory by copying over the contents of a basic `bootstrap` folder. Journo.init = -> here = fs.realpathSync '.' if fs.existsSync 'posts' fatal "A blog already exists in #{here}" bootstrap = path.join(__dirname, 'bootstrap/*') exec "rsync -vur --delete #{bootstrap} .", (err, stdout, stderr) -> throw err if err console.log "Initialized new blog in #{here}" Preview via a Local Server -------------------------- Instead of constantly rebuilding a purely static version of the site, Journo provides a preview server (which you can start by just typing `journo` from within your blog). Journo.preview = -> http = require 'http' mime = require 'mime' url = require 'url' util = require 'util' do loadManifest server = http.createServer (req, res) -> rawPath = url.parse(req.url).pathname.replace(/(^\/|\/$)/g, '') or 'index' If the request is for a preview of the RSS feed... if rawPath is 'feed.rss' res.writeHead 200, 'Content-Type': mime.lookup('.rss') res.end Journo.feed() If the request is for a static file that exists in our `public` directory... else publicPath = "public/" + rawPath fs.exists publicPath, (exists) -> if exists res.writeHead 200, 'Content-Type': mime.lookup(publicPath) fs.createReadStream(publicPath).pipe res If the request is for the slug of a valid post, we reload the layout, and render it... else post = "posts/#{rawPath}.md" fs.exists post, (exists) -> if exists loadLayout true fs.readFile post, (err, content) -> res.writeHead 200, 'Content-Type': 'text/html' res.end Journo.render post, content Anything else is a 404. else res.writeHead 404 res.end '404 Not Found' server.listen 1234 console.log "Journo is previewing at http://localhost:1234" exec "#{opener} http://localhost:1234" Work Without JavaScript, But Default to a Fluid JavaScript-Enabled UI --------------------------------------------------------------------- The best way to handle this bit seems to be entirely on the client-side. For example, when rendering a JavaScript slideshow of photographs, instead of having the server spit out the slideshow code, simply have the blog detect the list of images during page load and move them into a slideshow right then and there -- using `alt` attributes for captions, for example. Since the blog is public, it's nice if search engines can see all of the pieces as well as readers. Finally, Putting it all Together. Run Journo From the Terminal -------------------------------------------------------------- We'll do the simplest possible command-line interface. If a public function exists on the `Journo` object, you can run it. *Note that this lets you do silly things, like* `journo toString` *but no big deal.* Journo.run = -> command = process.argv[2] or 'preview' return do Journo[command] if Journo[command] console.error "Journo doesn't know how to '#{command}'" Let's also provide a help page that lists the available commands. Journo.help = Journo['--help'] = -> console.log """ Usage: journo [command] If called without a command, `journo` will preview your blog. init start a new blog in the current folder build build a static version of the blog into 'site' preview live preview the blog via a local server publish publish the blog to your remote server """ And we might as well do the version number, for completeness' sake. Journo.version = Journo['--version'] = -> console.log "Journo 0.0.1" Miscellaneous Bits and Utilities -------------------------------- Little utility functions that are useful up above. The file path to the source of a given `post`. postPath = (post) -> "posts/#{post}" The server-side path to the HTML for a given `post`. htmlPath = (post) -> name = postName post if name is 'index' 'site/index.html' else "site/#{name}/index.html" The name (or slug) of a post, taken from the filename. postName = (post) -> path.basename post, '.md' The full, absolute URL for a published post. postUrl = (post) -> "#{shared.siteUrl}/#{postName(post)}/" Starting with the string contents of a post, detect the title -- the first heading. detectTitle = (content) -> _.find(marked.lexer(content), (token) -> token.type is 'heading')?.text Starting with the string contents of a post, detect the description -- the first paragraph. detectDescription = (content, post) -> desc = _.find(marked.lexer(content), (token) -> token.type is 'paragraph')?.text marked.parser marked.lexer _.template("#{desc}...")(renderVariables(post)) Helper function to read in the contents of a folder, ignoring hidden files and directories. folderContents = (folder) -> fs.readdirSync(folder).filter (f) -> f.charAt(0) isnt '.' Return the list of posts currently in the manifest, sorted by their date of publication. sortedPosts = -> _.sortBy _.without(_.keys(shared.manifest), 'index.md'), (post) -> shared.manifest[post].pubtime The shared variables we want to allow our templates (both posts, and layout) to use in their evaluations. In the future, it would be nice to determine exactly what best belongs here, and provide an easier way for the blog author to add functions to it. renderVariables = (post) -> { _ fs path mapLink postName folderContents posts: sortedPosts() post: path.basename(post) manifest: shared.manifest } Quick function which creates a link to a Google Map search for the name of the place. mapLink = (place, additional = '', zoom = 15) -> query = encodeURIComponent("#{place}, #{additional}") "#{place}" Convenience function for catching errors (keeping the preview server from crashing while testing code), and printing them out. catchErrors = (func) -> try do func catch err console.error err.stack "
#{err.stack}
" Finally, for errors that you want the app to die on -- things that should break the site build. fatal = (message) -> console.error message process.exit 1 gulp-coffee-3.0.2/test/fixtures/journo.litcoffee000066400000000000000000000346261322222247700217540ustar00rootroot00000000000000Journo ====== Journo = module.exports = {} Journo is a blogging program, with a few basic goals. To wit: * Write in Markdown. * Publish to flat files. * Publish via Rsync. * Maintain a manifest file (what's published and what isn't, pub dates). * Retina ready. * Syntax highlight code. * Publish a feed. * Quickly bootstrap a new blog. * Preview via a local server. * Work without JavaScript, but default to a fluid JavaScript-enabled UI. You can install and use the `journo` command via npm: `sudo npm install -g journo` ... now, let's go through those features one at a time: Getting Started --------------- 1. Create a folder for your blog, and `cd` into it. 2. Type `journo init` to bootstrap a new empty blog. 3. Edit the `config.json`, `layout.html`, and `posts/index.md` files to suit. 4. Type `journo` to start the preview server, and have at it. Write in Markdown ----------------- We'll use the excellent **marked** module to compile Markdown into HTML, and Underscore for many of its goodies later on. Up top, create a namespace for shared values needed by more than one function. marked = require 'marked' _ = require 'underscore' shared = {} To render a post, we take its raw `source`, treat it as both an Underscore template (for HTML generation) and as Markdown (for formatting), and insert it into the layout as `content`. Journo.render = (post, source) -> catchErrors -> do loadLayout source or= fs.readFileSync postPath post variables = renderVariables post markdown = _.template(source.toString()) variables title = detectTitle markdown content = marked.parser marked.lexer markdown shared.layout _.extend variables, {title, content} A Journo site has a layout file, stored in `layout.html`, which is used to wrap every page. loadLayout = (force) -> return layout if not force and layout = shared.layout shared.layout = _.template(fs.readFileSync('layout.html').toString()) Determine the appropriate command to "open" a url in the browser for the current platform. opener = switch process.platform when 'darwin' then 'open' when 'win32' then 'start' else 'xdg-open' Publish to Flat Files --------------------- A blog is a folder on your hard drive. Within the blog, you have a `posts` folder for blog posts, a `public` folder for static content, a `layout.html` file for the layout which wraps every page, and a `journo.json` file for configuration. During a `build`, a static version of the site is rendered into the `site` folder, by **rsync**ing over all static files, rendering and writing every post, and creating an RSS feed. fs = require 'fs' path = require 'path' {spawn, exec} = require 'child_process' Journo.build = -> do loadManifest fs.mkdirSync('site') unless fs.existsSync('site') exec "rsync -vur --delete public/ site", (err, stdout, stderr) -> throw err if err for post in folderContents('posts') html = Journo.render post file = htmlPath post fs.mkdirSync path.dirname(file) unless fs.existsSync path.dirname(file) fs.writeFileSync file, html fs.writeFileSync "site/feed.rss", Journo.feed() The `config.json` configuration file is where you keep the configuration details of your blog, and how to connect to the server you'd like to publish it on. The valid settings are: `title`, `description`, `author` (for RSS), `url `, `publish` (the `user@host:path` location to **rsync** to), and `publishPort` (if your server doesn't listen to SSH on the usual one). An example `config.json` will be bootstrapped for you when you initialize a blog, so you don't need to remember any of that. loadConfig = -> return if shared.config try shared.config = JSON.parse fs.readFileSync 'config.json' catch err fatal "Unable to read config.json" shared.siteUrl = shared.config.url.replace(/\/$/, '') Publish via rsync ----------------- Publishing is nice and rudimentary. We build out an entirely static version of the site and **rysnc** it up to the server. Journo.publish = -> do Journo.build rsync 'site/images/', path.join(shared.config.publish, 'images/'), -> rsync 'site/', shared.config.publish A helper function for **rsync**ing, with logging, and the ability to wait for the rsync to continue before proceeding. This is useful for ensuring that our any new photos have finished uploading (very slowly) before the update to the feed is syndicated out. rsync = (from, to, callback) -> port = "ssh -p #{shared.config.publishPort or 22}" child = spawn "rsync", ['-vurz', '--delete', '-e', port, from, to] child.stdout.on 'data', (out) -> console.log out.toString() child.stderr.on 'data', (err) -> console.error err.toString() child.on 'exit', callback if callback Maintain a Manifest File ------------------------ The "manifest" is where Journo keeps track of metadata -- the title, description, publications date and last modified time of each post. Everything you need to render out an RSS feed ... and everything you need to know if a post has been updated or removed. manifestPath = 'journo-manifest.json' loadManifest = -> do loadConfig shared.manifest = if fs.existsSync manifestPath JSON.parse fs.readFileSync manifestPath else {} do updateManifest fs.writeFileSync manifestPath, JSON.stringify shared.manifest We update the manifest by looping through every post and every entry in the existing manifest, looking for differences in `mtime`, and recording those along with the title and description of each post. updateManifest = -> manifest = shared.manifest posts = folderContents 'posts' delete manifest[post] for post of manifest when post not in posts for post in posts stat = fs.statSync postPath post entry = manifest[post] if not entry or entry.mtime isnt stat.mtime entry or= {pubtime: stat.ctime} entry.mtime = stat.mtime content = fs.readFileSync(postPath post).toString() entry.title = detectTitle content entry.description = detectDescription content, post manifest[post] = entry yes Retina Ready ------------ In the future, it may make sense for Journo to have some sort of built-in facility for automatically downsizing photos from retina to regular sizes ... But for now, this bit is up to you. Syntax Highlight Code --------------------- We syntax-highlight blocks of code with the nifty **highlight** package that includes heuristics for auto-language detection, so you don't have to specify what you're coding in. highlight = require 'highlight.js' marked.setOptions highlight: (code, lang) -> if highlight.LANGUAGES[lang]? highlight.highlight(lang, code, true).value else highlight.highlightAuto(code).value Publish a Feed -------------- We'll use the **rss** module to build a simple feed of recent posts. Start with the basic `author`, blog `title`, `description` and `url` configured in the `config.json`. Then, each post's `title` is the first header present in the post, the `description` is the first paragraph, and the date is the date you first created the post file. Journo.feed = -> RSS = require 'rss' do loadConfig config = shared.config feed = new RSS title: config.title description: config.description feed_url: "#{shared.siteUrl}/rss.xml" site_url: shared.siteUrl author: config.author for post in sortedPosts().reverse()[0...20] entry = shared.manifest[post] feed.item title: entry.title description: entry.description url: postUrl post date: entry.pubtime feed.xml() Quickly Bootstrap a New Blog ---------------------------- We **init** a new blog into the current directory by copying over the contents of a basic `bootstrap` folder. Journo.init = -> here = fs.realpathSync '.' if fs.existsSync 'posts' fatal "A blog already exists in #{here}" bootstrap = path.join(__dirname, 'bootstrap/*') exec "rsync -vur --delete #{bootstrap} .", (err, stdout, stderr) -> throw err if err console.log "Initialized new blog in #{here}" Preview via a Local Server -------------------------- Instead of constantly rebuilding a purely static version of the site, Journo provides a preview server (which you can start by just typing `journo` from within your blog). Journo.preview = -> http = require 'http' mime = require 'mime' url = require 'url' util = require 'util' do loadManifest server = http.createServer (req, res) -> rawPath = url.parse(req.url).pathname.replace(/(^\/|\/$)/g, '') or 'index' If the request is for a preview of the RSS feed... if rawPath is 'feed.rss' res.writeHead 200, 'Content-Type': mime.lookup('.rss') res.end Journo.feed() If the request is for a static file that exists in our `public` directory... else publicPath = "public/" + rawPath fs.exists publicPath, (exists) -> if exists res.writeHead 200, 'Content-Type': mime.lookup(publicPath) fs.createReadStream(publicPath).pipe res If the request is for the slug of a valid post, we reload the layout, and render it... else post = "posts/#{rawPath}.md" fs.exists post, (exists) -> if exists loadLayout true fs.readFile post, (err, content) -> res.writeHead 200, 'Content-Type': 'text/html' res.end Journo.render post, content Anything else is a 404. else res.writeHead 404 res.end '404 Not Found' server.listen 1234 console.log "Journo is previewing at http://localhost:1234" exec "#{opener} http://localhost:1234" Work Without JavaScript, But Default to a Fluid JavaScript-Enabled UI --------------------------------------------------------------------- The best way to handle this bit seems to be entirely on the client-side. For example, when rendering a JavaScript slideshow of photographs, instead of having the server spit out the slideshow code, simply have the blog detect the list of images during page load and move them into a slideshow right then and there -- using `alt` attributes for captions, for example. Since the blog is public, it's nice if search engines can see all of the pieces as well as readers. Finally, Putting it all Together. Run Journo From the Terminal -------------------------------------------------------------- We'll do the simplest possible command-line interface. If a public function exists on the `Journo` object, you can run it. *Note that this lets you do silly things, like* `journo toString` *but no big deal.* Journo.run = -> command = process.argv[2] or 'preview' return do Journo[command] if Journo[command] console.error "Journo doesn't know how to '#{command}'" Let's also provide a help page that lists the available commands. Journo.help = Journo['--help'] = -> console.log """ Usage: journo [command] If called without a command, `journo` will preview your blog. init start a new blog in the current folder build build a static version of the blog into 'site' preview live preview the blog via a local server publish publish the blog to your remote server """ And we might as well do the version number, for completeness' sake. Journo.version = Journo['--version'] = -> console.log "Journo 0.0.1" Miscellaneous Bits and Utilities -------------------------------- Little utility functions that are useful up above. The file path to the source of a given `post`. postPath = (post) -> "posts/#{post}" The server-side path to the HTML for a given `post`. htmlPath = (post) -> name = postName post if name is 'index' 'site/index.html' else "site/#{name}/index.html" The name (or slug) of a post, taken from the filename. postName = (post) -> path.basename post, '.md' The full, absolute URL for a published post. postUrl = (post) -> "#{shared.siteUrl}/#{postName(post)}/" Starting with the string contents of a post, detect the title -- the first heading. detectTitle = (content) -> _.find(marked.lexer(content), (token) -> token.type is 'heading')?.text Starting with the string contents of a post, detect the description -- the first paragraph. detectDescription = (content, post) -> desc = _.find(marked.lexer(content), (token) -> token.type is 'paragraph')?.text marked.parser marked.lexer _.template("#{desc}...")(renderVariables(post)) Helper function to read in the contents of a folder, ignoring hidden files and directories. folderContents = (folder) -> fs.readdirSync(folder).filter (f) -> f.charAt(0) isnt '.' Return the list of posts currently in the manifest, sorted by their date of publication. sortedPosts = -> _.sortBy _.without(_.keys(shared.manifest), 'index.md'), (post) -> shared.manifest[post].pubtime The shared variables we want to allow our templates (both posts, and layout) to use in their evaluations. In the future, it would be nice to determine exactly what best belongs here, and provide an easier way for the blog author to add functions to it. renderVariables = (post) -> { _ fs path mapLink postName folderContents posts: sortedPosts() post: path.basename(post) manifest: shared.manifest } Quick function which creates a link to a Google Map search for the name of the place. mapLink = (place, additional = '', zoom = 15) -> query = encodeURIComponent("#{place}, #{additional}") "#{place}" Convenience function for catching errors (keeping the preview server from crashing while testing code), and printing them out. catchErrors = (func) -> try do func catch err console.error err.stack "
#{err.stack}
" Finally, for errors that you want the app to die on -- things that should break the site build. fatal = (message) -> console.error message process.exit 1 gulp-coffee-3.0.2/test/main.js000066400000000000000000000227741322222247700161700ustar00rootroot00000000000000var coffee = require('../'); var should = require('should'); var coffeescript = require('coffeescript'); var fs = require('fs'); var path = require('path'); var sourcemaps = require('gulp-sourcemaps'); var stream = require('stream'); var File = require('vinyl'); require('mocha'); var createFile = function (filepath, contents) { var base = path.dirname(filepath); return new File({ path: filepath, base: base, cwd: path.dirname(base), contents: contents }); }; describe('gulp-coffee', function() { describe('coffee()', function() { before(function() { this.testData = function (expected, newPath, done) { var newPaths = [newPath], expectedSourceMap; if (expected.v3SourceMap) { expectedSourceMap = JSON.parse(expected.v3SourceMap); expected = [expected.js]; } else { expected = [expected]; } return function (newFile) { this.expected = expected.shift(); this.newPath = newPaths.shift(); should.exist(newFile); should.exist(newFile.path); should.exist(newFile.relative); should.exist(newFile.contents); newFile.path.should.equal(this.newPath); newFile.relative.should.equal(path.basename(this.newPath)); String(newFile.contents).should.equal(this.expected); if (expectedSourceMap) { // check whether the sources from the coffee have been // applied to the files source map newFile.sourceMap.sources .should.containDeep(expectedSourceMap.sources); } if (done && !expected.length) { done.call(this); } }; }; }); it('should concat two files', function(done) { var filepath = '/home/contra/test/file.coffee'; var contents = new Buffer('a = 2'); var opts = {bare: true}; var expected = coffeescript.compile(String(contents), opts); coffee(opts) .on('error', done) .on('data', this.testData(expected, path.normalize('/home/contra/test/file.js'), done)) .write(createFile(filepath, contents)); }); it('should emit errors correctly', function(done) { var filepath = '/home/contra/test/file.coffee'; var contents = new Buffer('if a()\r\n then huh'); coffee({bare: true}) .on('error', function(err) { err.message.should.equal('unexpected then'); done(); }) .on('data', function(newFile) { throw new Error('no file should have been emitted!'); }) .write(createFile(filepath, contents)); }); it('should compile a file (no bare)', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents)); coffee() .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a file (with bare)', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var opts = {bare: true}; var expected = coffeescript.compile(String(contents), opts); coffee(opts) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a file with source map', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), { sourceMap: true, sourceFiles: ['grammar.coffee'], generatedFile: 'grammar.js' }); var stream = sourcemaps.init(); stream.write(createFile(filepath, contents)); stream .pipe(coffee({})) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)); }); it('should compile a file with bare and with source map', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), { bare: true, sourceMap: true, sourceFiles: ['grammar.coffee'], generatedFile: 'grammar.js' }); var stream = sourcemaps.init(); stream.write(createFile(filepath, contents)); stream .pipe(coffee({bare: true})) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)); }); it('should compile a file (no header)', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), {header: false}); coffee() .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a file (with header)', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), {header: true}); coffee({header: true}) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/grammar.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a literate file', function(done) { var filepath = 'test/fixtures/journo.litcoffee'; var contents = new Buffer(fs.readFileSync(filepath)); var opts = {literate: true}; var expected = coffeescript.compile(String(contents), opts); coffee(opts) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a literate file (implicit)', function(done) { var filepath = 'test/fixtures/journo.litcoffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), {literate: true}); coffee() .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a literate file (with bare)', function(done) { var filepath = 'test/fixtures/journo.litcoffee'; var contents = new Buffer(fs.readFileSync(filepath)); var opts = {literate: true, bare: true}; var expected = coffeescript.compile(String(contents), opts); coffee(opts) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)) .write(createFile(filepath, contents)); }); it('should compile a literate file with source map', function(done) { var filepath = 'test/fixtures/journo.litcoffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), { literate: true, sourceMap: true, sourceFiles: ['journo.litcoffee'], generatedFile: 'journo.js' }); var stream = sourcemaps.init(); stream.write(createFile(filepath, contents)); stream .pipe(coffee({literate: true})) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)); }); it('should compile a literate file with bare and with source map', function(done) { var filepath = 'test/fixtures/journo.litcoffee'; var contents = new Buffer(fs.readFileSync(filepath)); var expected = coffeescript.compile(String(contents), { literate: true, bare: true, sourceMap: true, sourceFiles: ['journo.litcoffee'], generatedFile: 'journo.js' }); var stream = sourcemaps.init(); stream.write(createFile(filepath, contents)); stream .pipe(coffee({literate: true, bare: true})) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)); }); it('should rename a literate markdown file', function(done) { var filepath = 'test/fixtures/journo.coffee.md'; var contents = new Buffer(fs.readFileSync(filepath)); var opts = {literate: true}; var expected = coffeescript.compile(String(contents), opts); coffee(opts) .on('error', done) .on('data', this.testData(expected, path.normalize('test/fixtures/journo.js'), done)) .write(createFile(filepath, contents)); }); it('should accept a custom coffeescript version', function(done) { var filepath = 'test/fixtures/grammar.coffee'; var contents = new Buffer(fs.readFileSync(filepath)); var wasSpyCalled = false; var opts = { coffee: { compile() { wasSpyCalled = true; return ''; } } }; function assertSpy() { should(wasSpyCalled).equal(true); done(); } coffee(opts) .on('error', assertSpy) .on('data', this.testData('', path.normalize('test/fixtures/grammar.js'), assertSpy)) .write(createFile(filepath, contents)); }) }); });