diff --git a/lib/coffeescript/lexer.js b/lib/coffeescript/lexer.js index e71e2219..16df5ee2 100644 --- a/lib/coffeescript/lexer.js +++ b/lib/coffeescript/lexer.js @@ -111,7 +111,7 @@ // referenced as property names here, so you can still do `jQuery.is()` even // though `is` means `===` otherwise. identifierToken() { - var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regExSuper, regex, sup, tag, tagToken; + var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, regExSuper, regex, sup, tag, tagToken; inCSXTag = this.atCSXTag(); regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER; if (!(match = regex.exec(this.chunk))) { @@ -132,19 +132,28 @@ if (id === 'as' && this.seenImport) { if (this.value() === '*') { this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL'; - } else if (ref = this.value(), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) { - this.tokens[this.tokens.length - 1][0] = 'IDENTIFIER'; + } else if (ref = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) { + prev = this.prev(); + [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)]; } if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') { this.token('AS', id); return id.length; } } - if (id === 'as' && this.seenExport && ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT')) { - this.token('AS', id); - return id.length; + if (id === 'as' && this.seenExport) { + if ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT') { + this.token('AS', id); + return id.length; + } + if (ref3 = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref3) >= 0) { + prev = this.prev(); + [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)]; + this.token('AS', id); + return id.length; + } } - if (id === 'default' && this.seenExport && ((ref3 = this.tag()) === 'EXPORT' || ref3 === 'AS')) { + if (id === 'default' && this.seenExport && ((ref4 = this.tag()) === 'EXPORT' || ref4 === 'AS')) { this.token('DEFAULT', id); return id.length; } @@ -156,10 +165,10 @@ return sup.length + 3; } prev = this.prev(); - tag = colon || (prev != null) && (((ref4 = prev[0]) === '.' || ref4 === '?.' || ref4 === '::' || ref4 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER'; + tag = colon || (prev != null) && (((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER'; if (tag === 'IDENTIFIER' && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) && !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)) { tag = id.toUpperCase(); - if (tag === 'WHEN' && (ref5 = this.tag(), indexOf.call(LINE_BREAK, ref5) >= 0)) { + if (tag === 'WHEN' && (ref6 = this.tag(), indexOf.call(LINE_BREAK, ref6) >= 0)) { tag = 'LEADING_WHEN'; } else if (tag === 'FOR') { this.seenFor = true; @@ -190,11 +199,11 @@ // what CoffeeScript would normally interpret as calls to functions named // `get` or `set`, i.e. `get({foo: function () {}})`. } else if (tag === 'PROPERTY' && prev) { - if (prev.spaced && (ref6 = prev[0], indexOf.call(CALLABLE, ref6) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens[this.tokens.length - 2][0] !== '.') { + if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens[this.tokens.length - 2][0] !== '.') { this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]); } else { prevprev = this.tokens[this.tokens.length - 2]; - if (((ref7 = prev[0]) === '@' || ref7 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && this.tokens[this.tokens.length - 3][0] !== '.') { + if (((ref8 = prev[0]) === '@' || ref8 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && this.tokens[this.tokens.length - 3][0] !== '.') { this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]); } } @@ -204,7 +213,7 @@ length: id.length }); } - if (tag !== 'PROPERTY') { + if (!(tag === 'PROPERTY' || this.exportSpecifierList)) { if (indexOf.call(COFFEE_ALIASES, id) >= 0) { alias = id; id = COFFEE_ALIAS_MAP[id]; @@ -1275,10 +1284,14 @@ } // Peek at the last value in the token stream. - value() { - var ref, token; + value(useOrigin = false) { + var ref, ref1, token; ref = this.tokens, token = ref[ref.length - 1]; - return token != null ? token[1] : void 0; + if (useOrigin && ((token != null ? token.origin : void 0) != null)) { + return (ref1 = token.origin) != null ? ref1[1] : void 0; + } else { + return token != null ? token[1] : void 0; + } } // Get the previous token in the token stream. diff --git a/package-lock.json b/package-lock.json index 7c2c98b3..d7ca616d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "coffeescript", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/lexer.coffee b/src/lexer.coffee index 90648a2d..bcdb579c 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -116,7 +116,6 @@ exports.Lexer = class Lexer # Preserve length of id for location data idLength = id.length poppedToken = undefined - if id is 'own' and @tag() is 'FOR' @token 'OWN', id return id.length @@ -126,14 +125,21 @@ exports.Lexer = class Lexer if id is 'as' and @seenImport if @value() is '*' @tokens[@tokens.length - 1][0] = 'IMPORT_ALL' - else if @value() in COFFEE_KEYWORDS - @tokens[@tokens.length - 1][0] = 'IDENTIFIER' + else if @value(yes) in COFFEE_KEYWORDS + prev = @prev() + [prev[0], prev[1]] = ['IDENTIFIER', @value(yes)] if @tag() in ['DEFAULT', 'IMPORT_ALL', 'IDENTIFIER'] @token 'AS', id return id.length - if id is 'as' and @seenExport and @tag() in ['IDENTIFIER', 'DEFAULT'] - @token 'AS', id - return id.length + if id is 'as' and @seenExport + if @tag() in ['IDENTIFIER', 'DEFAULT'] + @token 'AS', id + return id.length + if @value(yes) in COFFEE_KEYWORDS + prev = @prev() + [prev[0], prev[1]] = ['IDENTIFIER', @value(yes)] + @token 'AS', id + return id.length if id is 'default' and @seenExport and @tag() in ['EXPORT', 'AS'] @token 'DEFAULT', id return id.length @@ -197,7 +203,7 @@ exports.Lexer = class Lexer if tag is 'IDENTIFIER' and id in RESERVED @error "reserved word '#{id}'", length: id.length - unless tag is 'PROPERTY' + unless tag is 'PROPERTY' or @exportSpecifierList if id in COFFEE_ALIASES alias = id id = COFFEE_ALIAS_MAP[id] @@ -965,9 +971,12 @@ exports.Lexer = class Lexer token?[0] # Peek at the last value in the token stream. - value: -> + value: (useOrigin = no) -> [..., token] = @tokens - token?[1] + if useOrigin and token?.origin? + token.origin?[1] + else + token?[1] # Get the previous token in the token stream. prev: -> diff --git a/test/modules.coffee b/test/modules.coffee index 3d1a1e71..fb6f106d 100644 --- a/test/modules.coffee +++ b/test/modules.coffee @@ -171,6 +171,7 @@ test "multiline simple import", -> bar as baz } from 'lib';""" + test "multiline complex import", -> eqJS """ import foo, { @@ -497,18 +498,21 @@ test "export as aliases members imported from another module", -> } from 'lib';""" test "export list can contain CoffeeScript keywords", -> - eqJS "export { unless } from 'lib'", + eqJS "export { unless, and } from 'lib'", """ export { - unless + unless, + and } from 'lib';""" test "export list can contain CoffeeScript keywords when aliasing", -> - eqJS "export { when as bar, baz as unless } from 'lib'", + eqJS "export { when as bar, baz as unless, and as foo, booze as not } from 'lib'", """ export { when as bar, - baz as unless + baz as unless, + and as foo, + booze as not } from 'lib';""" @@ -602,11 +606,12 @@ test "`as` can be used as an alias name", -> test "CoffeeScript keywords can be used as imported names in import lists", -> eqJS """ - import { unless as bar } from 'lib' + import { unless as bar, and as computedAnd } from 'lib' bar.barMethod()""", """ import { - unless as bar + unless as bar, + and as computedAnd } from 'lib'; bar.barMethod();"""