AST: "CSX" -> "JSX" (#5188)

* updated grammar

* restore JSXIdentifier::astType()
This commit is contained in:
Julian Rosse 2019-03-31 15:13:05 -04:00 committed by Geoffrey Booth
parent 0c2d3673d3
commit 28a1a1d304
14 changed files with 299 additions and 331 deletions

View File

@ -162,13 +162,13 @@
function() { function() {
return new IdentifierLiteral($1); return new IdentifierLiteral($1);
}), }),
o('CSX_TAG', o('JSX_TAG',
function() { function() {
var ref, var ref,
ref1, ref1,
ref2, ref2,
ref3; ref3;
return new CSXTag($1.toString(), return new JSXTag($1.toString(),
{ {
tagNameLocationData: $1.tagNameToken[2], tagNameLocationData: $1.tagNameToken[2],
closingTagOpeningBracketLocationData: (ref = $1.closingTagOpeningBracketToken) != null ? ref[2] : void 0, closingTagOpeningBracketLocationData: (ref = $1.closingTagOpeningBracketToken) != null ? ref[2] : void 0,

View File

@ -10,7 +10,7 @@
// where locationData is {first_line, first_column, last_line, last_column, last_line_exclusive, last_column_exclusive}, which is a // where locationData is {first_line, first_column, last_line, last_column, last_line_exclusive, last_column_exclusive}, which is a
// format that can be fed directly into [Jison](https://github.com/zaach/jison). These // format that can be fed directly into [Jison](https://github.com/zaach/jison). These
// are read by jison in the `parser.lexer` function defined in coffeescript.coffee. // are read by jison in the `parser.lexer` function defined in coffeescript.coffee.
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARABLE_LEFT_SIDE, COMPARE, COMPOUND_ASSIGN, CSX_ATTRIBUTE, CSX_FRAGMENT_IDENTIFIER, CSX_IDENTIFIER, CSX_INTERPOLATION, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INSIDE_CSX, INVERSES, JSTOKEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, REGEX_INVALID_ESCAPE, RELATION, RESERVED, Rewriter, SHIFT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_INVALID_ESCAPE, STRING_SINGLE, STRING_START, TRAILING_SPACES, UNARY, UNARY_MATH, UNFINISHED, VALID_FLAGS, WHITESPACE, addTokenData, attachCommentsToNode, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, replaceUnicodeCodePointEscapes, starts, throwSyntaxError, var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARABLE_LEFT_SIDE, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INSIDE_JSX, INVERSES, JSTOKEN, JSX_ATTRIBUTE, JSX_FRAGMENT_IDENTIFIER, JSX_IDENTIFIER, JSX_INTERPOLATION, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, REGEX_INVALID_ESCAPE, RELATION, RESERVED, Rewriter, SHIFT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_INVALID_ESCAPE, STRING_SINGLE, STRING_START, TRAILING_SPACES, UNARY, UNARY_MATH, UNFINISHED, VALID_FLAGS, WHITESPACE, addTokenData, attachCommentsToNode, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, replaceUnicodeCodePointEscapes, starts, throwSyntaxError,
indexOf = [].indexOf, indexOf = [].indexOf,
slice = [].slice; slice = [].slice;
@ -58,8 +58,8 @@
this.seenExport = false; // Used to recognize `EXPORT FROM? AS?` tokens. this.seenExport = false; // Used to recognize `EXPORT FROM? AS?` tokens.
this.importSpecifierList = false; // Used to identify when in an `IMPORT {...} FROM? ...`. this.importSpecifierList = false; // Used to identify when in an `IMPORT {...} FROM? ...`.
this.exportSpecifierList = false; // Used to identify when in an `EXPORT {...} FROM? ...`. this.exportSpecifierList = false; // Used to identify when in an `EXPORT {...} FROM? ...`.
this.csxDepth = 0; // Used to optimize CSX checks, how deep in CSX we are. this.jsxDepth = 0; // Used to optimize JSX checks, how deep in JSX we are.
this.csxObjAttribute = {}; // Used to detect if CSX attributes is wrapped in {} (<div {props...} />). this.jsxObjAttribute = {}; // Used to detect if JSX attributes is wrapped in {} (<div {props...} />).
this.chunkLine = opts.line || 0; // The start line for the current @chunk. this.chunkLine = opts.line || 0; // The start line for the current @chunk.
this.chunkColumn = opts.column || 0; // The start column of the current @chunk. this.chunkColumn = opts.column || 0; // The start column of the current @chunk.
this.chunkOffset = opts.offset || 0; // The start offset for the current @chunk. this.chunkOffset = opts.offset || 0; // The start offset for the current @chunk.
@ -70,7 +70,7 @@
// `@literalToken` is the fallback catch-all. // `@literalToken` is the fallback catch-all.
i = 0; i = 0;
while (this.chunk = code.slice(i)) { while (this.chunk = code.slice(i)) {
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.csxToken() || this.regexToken() || this.jsToken() || this.literalToken(); consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.jsxToken() || this.regexToken() || this.jsToken() || this.literalToken();
// Update position. // Update position.
[this.chunkLine, this.chunkColumn, this.chunkOffset] = this.getLineAndColumnFromChunk(consumed); [this.chunkLine, this.chunkColumn, this.chunkOffset] = this.getLineAndColumnFromChunk(consumed);
i += consumed; i += consumed;
@ -119,9 +119,9 @@
// referenced as property names here, so you can still do `jQuery.is()` even // referenced as property names here, so you can still do `jQuery.is()` even
// though `is` means `===` otherwise. // though `is` means `===` otherwise.
identifierToken() { identifierToken() {
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regExSuper, regex, sup, tag, tagToken, tokenData; var alias, colon, colonOffset, colonToken, id, idLength, inJSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regExSuper, regex, sup, tag, tagToken, tokenData;
inCSXTag = this.atCSXTag(); inJSXTag = this.atJSXTag();
regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER; regex = inJSXTag ? JSX_ATTRIBUTE : IDENTIFIER;
if (!(match = regex.exec(this.chunk))) { if (!(match = regex.exec(this.chunk))) {
return 0; return 0;
} }
@ -263,16 +263,16 @@
[tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] = [poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]]; [tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] = [poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]];
} }
if (colon) { if (colon) {
colonOffset = input.lastIndexOf(inCSXTag ? '=' : ':'); colonOffset = input.lastIndexOf(inJSXTag ? '=' : ':');
colonToken = this.token(':', ':', { colonToken = this.token(':', ':', {
offset: colonOffset, offset: colonOffset,
length: colon.length length: colon.length
}); });
if (inCSXTag) { // used by rewriter if (inJSXTag) { // used by rewriter
colonToken.csxColon = true; colonToken.jsxColon = true;
} }
} }
if (inCSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') { if (inJSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
this.token(',', ',', { this.token(',', ',', {
length: 0, length: 0,
origin: tagToken origin: tagToken
@ -386,7 +386,7 @@
delimiter: quote delimiter: quote
}); });
}); });
if (this.atCSXTag()) { if (this.atJSXTag()) {
this.token(',', ',', { this.token(',', ',', {
length: 0, length: 0,
origin: this.prev origin: this.prev
@ -771,16 +771,15 @@
return this; return this;
} }
// CSX is like JSX but for CoffeeScript. jsxToken() {
csxToken() { var afterTag, end, endToken, firstChar, fullId, fullTagName, id, input, j, jsxTag, len, match, offset, openingTagToken, prev, prevChar, properties, property, ref, tagToken, token, tokens;
var afterTag, csxTag, end, endToken, firstChar, fullId, fullTagName, id, input, j, len, match, offset, openingTagToken, prev, prevChar, properties, property, ref, tagToken, token, tokens;
firstChar = this.chunk[0]; firstChar = this.chunk[0];
// Check the previous token to detect if attribute is spread. // Check the previous token to detect if attribute is spread.
prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : ''; prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : '';
if (firstChar === '<') { if (firstChar === '<') {
match = CSX_IDENTIFIER.exec(this.chunk.slice(1)) || CSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(1)); match = JSX_IDENTIFIER.exec(this.chunk.slice(1)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(1));
// Not the right hand side of an unspaced comparison (i.e. `a<b`). // Not the right hand side of an unspaced comparison (i.e. `a<b`).
if (!(match && (this.csxDepth > 0 || !(prev = this.prev()) || prev.spaced || (ref = prev[0], indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0)))) { if (!(match && (this.jsxDepth > 0 || !(prev = this.prev()) || prev.spaced || (ref = prev[0], indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0)))) {
return 0; return 0;
} }
[input, id] = match; [input, id] = match;
@ -790,7 +789,7 @@
} else { } else {
properties = []; properties = [];
} }
tagToken = this.token('CSX_TAG', id, { tagToken = this.token('JSX_TAG', id, {
length: id.length + 1, length: id.length + 1,
data: { data: {
openingBracketToken: this.makeToken('<', '<'), openingBracketToken: this.makeToken('<', '<'),
@ -819,9 +818,9 @@
name: id, name: id,
properties properties
}); });
this.csxDepth++; this.jsxDepth++;
return fullId.length + 1; return fullId.length + 1;
} else if (csxTag = this.atCSXTag()) { } else if (jsxTag = this.atJSXTag()) {
if (this.chunk.slice(0, 2) === '/>') { // Self-closing tag. if (this.chunk.slice(0, 2) === '/>') { // Self-closing tag.
this.pair('/>'); this.pair('/>');
this.token(']', ']', { this.token(']', ']', {
@ -838,15 +837,15 @@
}) })
} }
}); });
this.csxDepth--; this.jsxDepth--;
return 2; return 2;
} else if (firstChar === '{') { } else if (firstChar === '{') {
if (prevChar === ':') { if (prevChar === ':') {
token = this.token('(', '('); token = this.token('(', '(');
this.csxObjAttribute[this.csxDepth] = false; this.jsxObjAttribute[this.jsxDepth] = false;
} else { } else {
token = this.token('{', '{'); token = this.token('{', '{');
this.csxObjAttribute[this.csxDepth] = true; this.jsxObjAttribute[this.jsxDepth] = true;
} }
this.ends.push({ this.ends.push({
tag: '}', tag: '}',
@ -870,7 +869,7 @@
({ ({
tokens, tokens,
index: end index: end
} = this.matchWithInterpolations(INSIDE_CSX, '>', '</', CSX_INTERPOLATION)); } = this.matchWithInterpolations(INSIDE_JSX, '>', '</', JSX_INTERPOLATION));
this.mergeInterpolationTokens(tokens, { this.mergeInterpolationTokens(tokens, {
endOffset: end endOffset: end
}, (value) => { }, (value) => {
@ -878,10 +877,10 @@
delimiter: '>' delimiter: '>'
}); });
}); });
match = CSX_IDENTIFIER.exec(this.chunk.slice(end)) || CSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end)); match = JSX_IDENTIFIER.exec(this.chunk.slice(end)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
if (!match || match[1] !== `${csxTag.name}${((function() { if (!match || match[1] !== `${jsxTag.name}${((function() {
var k, len1, ref1, results; var k, len1, ref1, results;
ref1 = csxTag.properties; ref1 = jsxTag.properties;
results = []; results = [];
for (k = 0, len1 = ref1.length; k < len1; k++) { for (k = 0, len1 = ref1.length; k < len1; k++) {
property = ref1[k]; property = ref1[k];
@ -889,7 +888,7 @@
} }
return results; return results;
})()).join('')}`) { })()).join('')}`) {
this.error(`expected corresponding CSX closing tag for ${csxTag.name}`, csxTag.origin.data.tagNameToken[2]); this.error(`expected corresponding JSX closing tag for ${jsxTag.name}`, jsxTag.origin.data.tagNameToken[2]);
} }
[, fullTagName] = match; [, fullTagName] = match;
afterTag = end + fullTagName.length; afterTag = end + fullTagName.length;
@ -922,17 +921,17 @@
}); });
// make the closing tag location data more easily accessible to the grammar // make the closing tag location data more easily accessible to the grammar
addTokenData(openingTagToken, endToken.data); addTokenData(openingTagToken, endToken.data);
this.csxDepth--; this.jsxDepth--;
return afterTag + 1; return afterTag + 1;
} else { } else {
return 0; return 0;
} }
} else if (this.atCSXTag(1)) { } else if (this.atJSXTag(1)) {
if (firstChar === '}') { if (firstChar === '}') {
this.pair(firstChar); this.pair(firstChar);
if (this.csxObjAttribute[this.csxDepth]) { if (this.jsxObjAttribute[this.jsxDepth]) {
this.token('}', '}'); this.token('}', '}');
this.csxObjAttribute[this.csxDepth] = false; this.jsxObjAttribute[this.jsxDepth] = false;
} else { } else {
this.token(')', ')'); this.token(')', ')');
} }
@ -946,9 +945,9 @@
} }
} }
atCSXTag(depth = 0) { atJSXTag(depth = 0) {
var i, last, ref; var i, last, ref;
if (this.csxDepth === 0) { if (this.jsxDepth === 0) {
return false; return false;
} }
i = this.ends.length - 1; i = this.ends.length - 1;
@ -1132,9 +1131,9 @@
// `#{` if interpolations are desired). // `#{` if interpolations are desired).
// - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`, // - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
// `"""` and `///`. // `"""` and `///`.
// - `closingDelimiter` is different from `delimiter` only in CSX // - `closingDelimiter` is different from `delimiter` only in JSX
// - `interpolators` matches the start of an interpolation, for CSX it's both // - `interpolators` matches the start of an interpolation, for JSX it's both
// `{` and `<` (i.e. nested CSX tag) // `{` and `<` (i.e. nested JSX tag)
// This method allows us to have strings within interpolations within strings, // This method allows us to have strings within interpolations within strings,
// ad infinitum. // ad infinitum.
@ -1600,13 +1599,13 @@
// Token matching regexes. // Token matching regexes.
IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/; // Is this a property name? IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/; // Is this a property name?
CSX_IDENTIFIER = /^(?![\d<])((?:(?!\s)[\.\-$\w\x7f-\uffff])+)/; // Must not start with `<`. JSX_IDENTIFIER = /^(?![\d<])((?:(?!\s)[\.\-$\w\x7f-\uffff])+)/; // Must not start with `<`.
// Like `IDENTIFIER`, but includes `-`s and `.`s. // Like `IDENTIFIER`, but includes `-`s and `.`s.
// Fragment: <></> // Fragment: <></>
CSX_FRAGMENT_IDENTIFIER = /^()>/; // Ends immediately with `>`. JSX_FRAGMENT_IDENTIFIER = /^()>/; // Ends immediately with `>`.
CSX_ATTRIBUTE = /^(?!\d)((?:(?!\s)[\-$\w\x7f-\uffff])+)([^\S]*=(?!=))?/; // Like `IDENTIFIER`, but includes `-`s. JSX_ATTRIBUTE = /^(?!\d)((?:(?!\s)[\-$\w\x7f-\uffff])+)([^\S]*=(?!=))?/; // Like `IDENTIFIER`, but includes `-`s.
// Is this an attribute with a value? // Is this an attribute with a value?
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; // binary NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; // binary
@ -1645,11 +1644,11 @@
HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/; HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/;
INSIDE_CSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping. INSIDE_JSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping.
// Maybe CSX tag (`<` not allowed even if bare). // Maybe JSX tag (`<` not allowed even if bare).
CSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation. JSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation.
// CSX opening tag. // JSX opening tag.
HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g; HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g;

View File

@ -4,7 +4,7 @@
// nodes are created as the result of actions in the [grammar](grammar.html), // nodes are created as the result of actions in the [grammar](grammar.html),
// but some are created by other nodes as a method of code generation. To convert // but some are created by other nodes as a method of code generation. To convert
// the syntax tree into a string of JavaScript code, call `compile()` on the root. // the syntax tree into a string of JavaScript code, call `compile()` on the root.
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXAttribute, CSXAttributes, CSXElement, CSXEmptyExpression, CSXExpressionContainer, CSXIdentifier, CSXTag, CSXText, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JSXAttribute, JSXAttributes, JSXElement, JSXEmptyExpression, JSXExpressionContainer, JSXIdentifier, JSXTag, JSXText, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
indexOf = [].indexOf, indexOf = [].indexOf,
splice = [].splice, splice = [].splice,
slice1 = [].slice; slice1 = [].slice;
@ -796,7 +796,7 @@
len = this.expressions.length; len = this.expressions.length;
ref1 = this.expressions, [lastExp] = slice1.call(ref1, -1); ref1 = this.expressions, [lastExp] = slice1.call(ref1, -1);
lastExp = (lastExp != null ? lastExp.unwrap() : void 0) || false; lastExp = (lastExp != null ? lastExp.unwrap() : void 0) || false;
// We also need to check that were not returning a CSX tag if theres an // We also need to check that were not returning a JSX tag if theres an
// adjacent one at the same level; JSX doesnt allow that. // adjacent one at the same level; JSX doesnt allow that.
if (lastExp && lastExp instanceof Parens && lastExp.body.expressions.length > 1) { if (lastExp && lastExp instanceof Parens && lastExp.body.expressions.length > 1) {
({ ({
@ -805,7 +805,7 @@
[penult, last] = slice1.call(expressions, -2); [penult, last] = slice1.call(expressions, -2);
penult = penult.unwrap(); penult = penult.unwrap();
last = last.unwrap(); last = last.unwrap();
if (penult instanceof CSXElement && last instanceof CSXElement) { if (penult instanceof JSXElement && last instanceof JSXElement) {
expressions[expressions.length - 1].error('Adjacent JSX elements must be wrapped in an enclosing tag'); expressions[expressions.length - 1].error('Adjacent JSX elements must be wrapped in an enclosing tag');
} }
} }
@ -1335,19 +1335,19 @@
} }
compileNode(o) { compileNode(o) {
if (this.csx) { if (this.jsx) {
return [this.makeCode(this.unquote(true, true))]; return [this.makeCode(this.unquote(true, true))];
} }
return super.compileNode(o); return super.compileNode(o);
} }
unquote(doubleQuote = false, csx = false) { unquote(doubleQuote = false, jsx = false) {
var unquoted; var unquoted;
unquoted = this.value.slice(1, -1); unquoted = this.value.slice(1, -1);
if (doubleQuote) { if (doubleQuote) {
unquoted = unquoted.replace(/\\"/g, '"'); unquoted = unquoted.replace(/\\"/g, '"');
} }
if (csx) { if (jsx) {
unquoted = unquoted.replace(/\\n/g, '\n'); unquoted = unquoted.replace(/\\n/g, '\n');
} }
return unquoted; return unquoted;
@ -1458,7 +1458,7 @@
} }
astType() { astType() {
if (this.csx) { if (this.jsx) {
return 'JSXIdentifier'; return 'JSXIdentifier';
} else { } else {
return 'Identifier'; return 'Identifier';
@ -1479,32 +1479,10 @@
}).call(this); }).call(this);
exports.CSXTag = CSXTag = class CSXTag extends IdentifierLiteral {
constructor(value, {tagNameLocationData, closingTagOpeningBracketLocationData, closingTagSlashLocationData, closingTagNameLocationData, closingTagClosingBracketLocationData}) {
super(value);
this.tagNameLocationData = tagNameLocationData;
this.closingTagOpeningBracketLocationData = closingTagOpeningBracketLocationData;
this.closingTagSlashLocationData = closingTagSlashLocationData;
this.closingTagNameLocationData = closingTagNameLocationData;
this.closingTagClosingBracketLocationData = closingTagClosingBracketLocationData;
}
astType() {
return 'JSXIdentifier';
}
astProperties() {
return {
name: this.value
};
}
};
exports.PropertyName = PropertyName = (function() { exports.PropertyName = PropertyName = (function() {
class PropertyName extends Literal { class PropertyName extends Literal {
astType() { astType() {
if (this.csx) { if (this.jsx) {
return 'JSXIdentifier'; return 'JSXIdentifier';
} else { } else {
return 'Identifier'; return 'Identifier';
@ -1883,8 +1861,8 @@
return !this.properties.length && this.base.isStatement(o); return !this.properties.length && this.base.isStatement(o);
} }
isCSXTag() { isJSXTag() {
return this.base instanceof CSXTag; return this.base instanceof JSXTag;
} }
assigns(name) { assigns(name) {
@ -2061,7 +2039,7 @@
} }
astType() { astType() {
if (this.isCSXTag()) { if (this.isJSXTag()) {
return 'JSXMemberExpression'; return 'JSXMemberExpression';
} else { } else {
return 'MemberExpression'; return 'MemberExpression';
@ -2074,8 +2052,8 @@
astProperties(o) { astProperties(o) {
var computed, property, ref1, ref2; var computed, property, ref1, ref2;
ref1 = this.properties, [property] = slice1.call(ref1, -1); ref1 = this.properties, [property] = slice1.call(ref1, -1);
if (this.isCSXTag()) { if (this.isJSXTag()) {
property.name.csx = true; property.name.jsx = true;
} }
computed = property instanceof Index || !(((ref2 = property.name) != null ? ref2.unwrap() : void 0) instanceof PropertyName); computed = property instanceof Index || !(((ref2 = property.name) != null ? ref2.unwrap() : void 0) instanceof PropertyName);
return { return {
@ -2088,7 +2066,7 @@
} }
astLocationData() { astLocationData() {
if (!this.isCSXTag()) { if (!this.isJSXTag()) {
return super.astLocationData(); return super.astLocationData();
} }
// don't include leading < of JSX tag in location data // don't include leading < of JSX tag in location data
@ -2223,20 +2201,38 @@
}; };
//### CSX //### JSX
exports.CSXIdentifier = CSXIdentifier = class CSXIdentifier extends IdentifierLiteral { exports.JSXIdentifier = JSXIdentifier = class JSXIdentifier extends IdentifierLiteral {
astType() { astType() {
return 'JSXIdentifier'; return 'JSXIdentifier';
} }
}; };
exports.CSXExpressionContainer = CSXExpressionContainer = (function() { exports.JSXTag = JSXTag = class JSXTag extends JSXIdentifier {
class CSXExpressionContainer extends Base { constructor(value, {tagNameLocationData, closingTagOpeningBracketLocationData, closingTagSlashLocationData, closingTagNameLocationData, closingTagClosingBracketLocationData}) {
super(value);
this.tagNameLocationData = tagNameLocationData;
this.closingTagOpeningBracketLocationData = closingTagOpeningBracketLocationData;
this.closingTagSlashLocationData = closingTagSlashLocationData;
this.closingTagNameLocationData = closingTagNameLocationData;
this.closingTagClosingBracketLocationData = closingTagClosingBracketLocationData;
}
astProperties() {
return {
name: this.value
};
}
};
exports.JSXExpressionContainer = JSXExpressionContainer = (function() {
class JSXExpressionContainer extends Base {
constructor(expression1, {locationData} = {}) { constructor(expression1, {locationData} = {}) {
super(); super();
this.expression = expression1; this.expression = expression1;
this.expression.csxAttribute = true; this.expression.jsxAttribute = true;
this.locationData = locationData != null ? locationData : this.expression.locationData; this.locationData = locationData != null ? locationData : this.expression.locationData;
} }
@ -2244,10 +2240,6 @@
return this.expression.compileNode(o); return this.expression.compileNode(o);
} }
astType() {
return 'JSXExpressionContainer';
}
astProperties() { astProperties() {
return { return {
expression: this.expression.ast() expression: this.expression.ast()
@ -2256,30 +2248,21 @@
}; };
CSXExpressionContainer.prototype.children = ['expression']; JSXExpressionContainer.prototype.children = ['expression'];
return CSXExpressionContainer; return JSXExpressionContainer;
}).call(this); }).call(this);
exports.CSXEmptyExpression = CSXEmptyExpression = class CSXEmptyExpression extends Base { exports.JSXEmptyExpression = JSXEmptyExpression = class JSXEmptyExpression extends Base {};
astType() {
return 'JSXEmptyExpression';
}
}; exports.JSXText = JSXText = class JSXText extends Base {
exports.CSXText = CSXText = class CSXText extends Base {
constructor(stringLiteral) { constructor(stringLiteral) {
super(); super();
this.value = stringLiteral.unquote(true, true); this.value = stringLiteral.unquote(true, true);
this.locationData = stringLiteral.locationData; this.locationData = stringLiteral.locationData;
} }
astType() {
return 'JSXText';
}
astProperties() { astProperties() {
return { return {
value: this.value, value: this.value,
@ -2291,8 +2274,8 @@
}; };
exports.CSXAttribute = CSXAttribute = (function() { exports.JSXAttribute = JSXAttribute = (function() {
class CSXAttribute extends Base { class JSXAttribute extends Base {
constructor({ constructor({
name: name1, name: name1,
value value
@ -2300,7 +2283,7 @@
var ref1; var ref1;
super(); super();
this.name = name1; this.name = name1;
this.value = value != null ? (value = value.base, value instanceof StringLiteral ? value : new CSXExpressionContainer(value)) : null; this.value = value != null ? (value = value.base, value instanceof StringLiteral ? value : new JSXExpressionContainer(value)) : null;
if ((ref1 = this.value) != null) { if ((ref1 = this.value) != null) {
ref1.comments = value.comments; ref1.comments = value.comments;
} }
@ -2316,10 +2299,6 @@
return compiledName.concat(this.makeCode('='), val); return compiledName.concat(this.makeCode('='), val);
} }
astType() {
return 'JSXAttribute';
}
astProperties() { astProperties() {
var ref1, ref2; var ref1, ref2;
return { return {
@ -2330,14 +2309,14 @@
}; };
CSXAttribute.prototype.children = ['name', 'value']; JSXAttribute.prototype.children = ['name', 'value'];
return CSXAttribute; return JSXAttribute;
}).call(this); }).call(this);
exports.CSXAttributes = CSXAttributes = (function() { exports.JSXAttributes = JSXAttributes = (function() {
class CSXAttributes extends Base { class JSXAttributes extends Base {
constructor(arr) { constructor(arr) {
var attribute, base, j, k, len1, len2, object, property, ref1, ref2, value, variable; var attribute, base, j, k, len1, len2, object, property, ref1, ref2, value, variable;
super(); super();
@ -2349,15 +2328,15 @@
({base} = object); ({base} = object);
if (base instanceof IdentifierLiteral) { if (base instanceof IdentifierLiteral) {
// attribute with no value eg disabled // attribute with no value eg disabled
attribute = new CSXAttribute({ attribute = new JSXAttribute({
name: new CSXIdentifier(base.value).withLocationDataAndCommentsFrom(base) name: new JSXIdentifier(base.value).withLocationDataAndCommentsFrom(base)
}); });
attribute.locationData = base.locationData; attribute.locationData = base.locationData;
this.attributes.push(attribute); this.attributes.push(attribute);
} else if (!base.generated) { } else if (!base.generated) {
// object spread attribute eg {...props} // object spread attribute eg {...props}
attribute = base.properties[0]; attribute = base.properties[0];
attribute.csx = true; attribute.jsx = true;
attribute.locationData = base.locationData; attribute.locationData = base.locationData;
this.attributes.push(attribute); this.attributes.push(attribute);
} else { } else {
@ -2366,8 +2345,8 @@
for (k = 0, len2 = ref2.length; k < len2; k++) { for (k = 0, len2 = ref2.length; k < len2; k++) {
property = ref2[k]; property = ref2[k];
({variable, value} = property); ({variable, value} = property);
attribute = new CSXAttribute({ attribute = new JSXAttribute({
name: new CSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom(variable.base), name: new JSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom(variable.base),
value value
}); });
attribute.locationData = property.locationData; attribute.locationData = property.locationData;
@ -2386,7 +2365,7 @@
} = object); } = object);
properties = (attribute != null ? attribute.properties : void 0) || []; properties = (attribute != null ? attribute.properties : void 0) || [];
if (!(attribute instanceof Obj || attribute instanceof IdentifierLiteral) || (attribute instanceof Obj && !attribute.generated && (properties.length > 1 || !(properties[0] instanceof Splat)))) { if (!(attribute instanceof Obj || attribute instanceof IdentifierLiteral) || (attribute instanceof Obj && !attribute.generated && (properties.length > 1 || !(properties[0] instanceof Splat)))) {
return object.error("Unexpected token. Allowed CSX attributes are: id=\"val\", src={source}, {props...} or attribute."); return object.error("Unexpected token. Allowed JSX attributes are: id=\"val\", src={source}, {props...} or attribute.");
} }
} }
@ -2415,15 +2394,15 @@
}; };
CSXAttributes.prototype.children = ['attributes']; JSXAttributes.prototype.children = ['attributes'];
return CSXAttributes; return JSXAttributes;
}).call(this); }).call(this);
// Node for a CSX element // Node for a JSX element
exports.CSXElement = CSXElement = (function() { exports.JSXElement = JSXElement = (function() {
class CSXElement extends Base { class JSXElement extends Base {
constructor({ constructor({
tagName: tagName1, tagName: tagName1,
attributes, attributes,
@ -2438,7 +2417,7 @@
compileNode(o) { compileNode(o) {
var fragments, ref1, tag; var fragments, ref1, tag;
if ((ref1 = this.content) != null) { if ((ref1 = this.content) != null) {
ref1.base.csx = true; ref1.base.jsx = true;
} }
fragments = [this.makeCode('<')]; fragments = [this.makeCode('<')];
fragments.push(...(tag = this.tagName.compileToFragments(o, LEVEL_ACCESS))); fragments.push(...(tag = this.tagName.compileToFragments(o, LEVEL_ACCESS)));
@ -2541,7 +2520,7 @@
children = (function() { children = (function() {
var j, len1, ref1, results; var j, len1, ref1, results;
if (content instanceof StringLiteral) { if (content instanceof StringLiteral) {
return [new CSXText(content)]; // StringWithInterpolations return [new JSXText(content)]; // StringWithInterpolations
} else { } else {
ref1 = this.content.unwrapAll().extractElements(o, { ref1 = this.content.unwrapAll().extractElements(o, {
includeInterpolationWrappers: true includeInterpolationWrappers: true
@ -2550,17 +2529,17 @@
for (j = 0, len1 = ref1.length; j < len1; j++) { for (j = 0, len1 = ref1.length; j < len1; j++) {
element = ref1[j]; element = ref1[j];
if (element instanceof StringLiteral) { if (element instanceof StringLiteral) {
results.push(new CSXText(element)); // Interpolation results.push(new JSXText(element)); // Interpolation
} else { } else {
({expression} = element); ({expression} = element);
if (expression == null) { if (expression == null) {
results.push(new CSXEmptyExpression().withLocationDataFrom(element)); results.push(new JSXEmptyExpression().withLocationDataFrom(element));
} else { } else {
unwrapped = expression.unwrapAll(); unwrapped = expression.unwrapAll();
if (unwrapped instanceof CSXElement) { if (unwrapped instanceof JSXElement) {
results.push(unwrapped); results.push(unwrapped);
} else { } else {
results.push(new CSXExpressionContainer(unwrapped, { results.push(new JSXExpressionContainer(unwrapped, {
locationData: element.locationData locationData: element.locationData
})); }));
} }
@ -2573,7 +2552,7 @@
results = []; results = [];
for (j = 0, len1 = children.length; j < len1; j++) { for (j = 0, len1 = children.length; j < len1; j++) {
child = children[j]; child = children[j];
if (!(child instanceof CSXText && child.value.length === 0)) { if (!(child instanceof JSXText && child.value.length === 0)) {
results.push(child.ast(o)); results.push(child.ast(o));
} }
} }
@ -2596,9 +2575,9 @@
}; };
CSXElement.prototype.children = ['tagName', 'attributes', 'content']; JSXElement.prototype.children = ['tagName', 'attributes', 'content'];
return CSXElement; return JSXElement;
}).call(this); }).call(this);
@ -2619,10 +2598,10 @@
if (this.variable instanceof Value && this.variable.isNotCallable()) { if (this.variable instanceof Value && this.variable.isNotCallable()) {
this.variable.error("literal is not a function"); this.variable.error("literal is not a function");
} }
if (this.variable.base instanceof CSXTag) { if (this.variable.base instanceof JSXTag) {
return new CSXElement({ return new JSXElement({
tagName: this.variable, tagName: this.variable,
attributes: new CSXAttributes(this.args[0].base), attributes: new JSXAttributes(this.args[0].base),
content: this.args[1] content: this.args[1]
}); });
} }
@ -5836,7 +5815,7 @@
compileNode(o) { compileNode(o) {
var compiledSplat; var compiledSplat;
compiledSplat = [this.makeCode('...'), ...this.name.compileToFragments(o, LEVEL_OP)]; compiledSplat = [this.makeCode('...'), ...this.name.compileToFragments(o, LEVEL_OP)];
if (!this.csx) { if (!this.jsx) {
return compiledSplat; return compiledSplat;
} }
return [this.makeCode('{'), ...compiledSplat, this.makeCode('}')]; return [this.makeCode('{'), ...compiledSplat, this.makeCode('}')];
@ -5847,7 +5826,7 @@
} }
astType() { astType() {
if (this.csx) { if (this.jsx) {
return 'JSXSpreadAttribute'; return 'JSXSpreadAttribute';
} else if (this.lhs) { } else if (this.lhs) {
return 'RestElement'; return 'RestElement';
@ -6781,13 +6760,13 @@
shouldWrapComment = (ref1 = expr.comments) != null ? ref1.some(function(comment) { shouldWrapComment = (ref1 = expr.comments) != null ? ref1.some(function(comment) {
return comment.here && !comment.unshift && !comment.newLine; return comment.here && !comment.unshift && !comment.newLine;
}) : void 0; }) : void 0;
if (expr instanceof Value && expr.isAtomic() && !this.csxAttribute && !shouldWrapComment) { if (expr instanceof Value && expr.isAtomic() && !this.jsxAttribute && !shouldWrapComment) {
expr.front = this.front; expr.front = this.front;
return expr.compileToFragments(o); return expr.compileToFragments(o);
} }
fragments = expr.compileToFragments(o, LEVEL_PAREN); fragments = expr.compileToFragments(o, LEVEL_PAREN);
bare = o.level < LEVEL_OP && !shouldWrapComment && (expr instanceof Op && !expr.isInOperator() || expr.unwrap() instanceof Call || (expr instanceof For && expr.returns)) && (o.level < LEVEL_COND || fragments.length <= 3); bare = o.level < LEVEL_OP && !shouldWrapComment && (expr instanceof Op && !expr.isInOperator() || expr.unwrap() instanceof Call || (expr instanceof For && expr.returns)) && (o.level < LEVEL_COND || fragments.length <= 3);
if (this.csxAttribute) { if (this.jsxAttribute) {
return this.wrapInBraces(fragments); return this.wrapInBraces(fragments);
} }
if (bare) { if (bare) {
@ -6863,7 +6842,7 @@
} }
attachCommentsToNode(salvagedComments, node); attachCommentsToNode(salvagedComments, node);
} }
if ((unwrapped = (ref1 = node.expression) != null ? ref1.unwrapAll() : void 0) instanceof PassthroughLiteral && unwrapped.generated && !this.csx) { if ((unwrapped = (ref1 = node.expression) != null ? ref1.unwrapAll() : void 0) instanceof PassthroughLiteral && unwrapped.generated && !this.jsx) {
commentPlaceholder = new StringLiteral('').withLocationDataFrom(node); commentPlaceholder = new StringLiteral('').withLocationDataFrom(node);
commentPlaceholder.comments = unwrapped.comments; commentPlaceholder.comments = unwrapped.comments;
if (node.comments) { if (node.comments) {
@ -6902,21 +6881,21 @@
if (this.comments == null) { if (this.comments == null) {
this.comments = (ref1 = this.startQuote) != null ? ref1.comments : void 0; this.comments = (ref1 = this.startQuote) != null ? ref1.comments : void 0;
} }
if (this.csxAttribute) { if (this.jsxAttribute) {
wrapped = new Parens(new StringWithInterpolations(this.body)); wrapped = new Parens(new StringWithInterpolations(this.body));
wrapped.csxAttribute = true; wrapped.jsxAttribute = true;
return wrapped.compileNode(o); return wrapped.compileNode(o);
} }
elements = this.extractElements(o); elements = this.extractElements(o);
fragments = []; fragments = [];
if (!this.csx) { if (!this.jsx) {
fragments.push(this.makeCode('`')); fragments.push(this.makeCode('`'));
} }
for (j = 0, len1 = elements.length; j < len1; j++) { for (j = 0, len1 = elements.length; j < len1; j++) {
element = elements[j]; element = elements[j];
if (element instanceof StringLiteral) { if (element instanceof StringLiteral) {
element.value = element.unquote(true, this.csx); element.value = element.unquote(true, this.jsx);
if (!this.csx) { if (!this.jsx) {
// Backticks and `${` inside template literals must be escaped. // Backticks and `${` inside template literals must be escaped.
element.value = element.value.replace(/(\\*)(`|\$\{)/g, function(match, backslashes, toBeEscaped) { element.value = element.value.replace(/(\\*)(`|\$\{)/g, function(match, backslashes, toBeEscaped) {
if (backslashes.length % 2 === 0) { if (backslashes.length % 2 === 0) {
@ -6928,7 +6907,7 @@
} }
fragments.push(...element.compileToFragments(o)); fragments.push(...element.compileToFragments(o));
} else { } else {
if (!this.csx) { if (!this.jsx) {
fragments.push(this.makeCode('$')); fragments.push(this.makeCode('$'));
} }
code = element.compileToFragments(o, LEVEL_PAREN); code = element.compileToFragments(o, LEVEL_PAREN);
@ -6951,7 +6930,7 @@
fragments.push(...code); fragments.push(...code);
} }
} }
if (!this.csx) { if (!this.jsx) {
fragments.push(this.makeCode('`')); fragments.push(this.makeCode('`'));
} }
return fragments; return fragments;
@ -6960,7 +6939,7 @@
isNestedTag(element) { isNestedTag(element) {
var call; var call;
call = typeof element.unwrapAll === "function" ? element.unwrapAll() : void 0; call = typeof element.unwrapAll === "function" ? element.unwrapAll() : void 0;
return this.csx && call instanceof CSXElement; return this.jsx && call instanceof JSXElement;
} }
astType() { astType() {

File diff suppressed because one or more lines are too long

View File

@ -89,7 +89,7 @@
this.addImplicitBracesAndParens(); this.addImplicitBracesAndParens();
this.rescueStowawayComments(); this.rescueStowawayComments();
this.addLocationDataToGeneratedTokens(); this.addLocationDataToGeneratedTokens();
this.enforceValidCSXAttributes(); this.enforceValidJSXAttributes();
this.fixOutdentLocationData(); this.fixOutdentLocationData();
this.exposeTokenDataToGrammar(); this.exposeTokenDataToGrammar();
if (typeof process !== "undefined" && process !== null ? (ref1 = process.env) != null ? ref1.DEBUG_REWRITTEN_TOKEN_STREAM : void 0 : void 0) { if (typeof process !== "undefined" && process !== null ? (ref1 = process.env) != null ? ref1.DEBUG_REWRITTEN_TOKEN_STREAM : void 0 : void 0) {
@ -578,11 +578,11 @@
}); });
} }
// Make sure only strings and wrapped expressions are used in CSX attributes. // Make sure only strings and wrapped expressions are used in JSX attributes.
enforceValidCSXAttributes() { enforceValidJSXAttributes() {
return this.scanTokens(function(token, i, tokens) { return this.scanTokens(function(token, i, tokens) {
var next, ref; var next, ref;
if (token.csxColon) { if (token.jsxColon) {
next = tokens[i + 1]; next = tokens[i + 1];
if ((ref = next[0]) !== 'STRING_START' && ref !== 'STRING' && ref !== '(') { if ((ref = next[0]) !== 'STRING_START' && ref !== 'STRING' && ref !== '(') {
throwSyntaxError('expected wrapped or quoted JSX attribute', next[2]); throwSyntaxError('expected wrapped or quoted JSX attribute', next[2]);
@ -993,7 +993,7 @@
IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']; IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
// If preceded by an `IMPLICIT_FUNC`, indicates a function invocation. // If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL = ['IDENTIFIER', 'CSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'UNDEFINED', 'NULL', 'BOOL', 'UNARY', 'DO', 'DO_IIFE', 'YIELD', 'AWAIT', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++']; IMPLICIT_CALL = ['IDENTIFIER', 'JSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'UNDEFINED', 'NULL', 'BOOL', 'UNARY', 'DO', 'DO_IIFE', 'YIELD', 'AWAIT', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
IMPLICIT_UNSPACED_CALL = ['+', '-']; IMPLICIT_UNSPACED_CALL = ['+', '-'];

View File

@ -153,7 +153,7 @@ grammar =
Identifier: [ Identifier: [
o 'IDENTIFIER', -> new IdentifierLiteral $1 o 'IDENTIFIER', -> new IdentifierLiteral $1
o 'CSX_TAG', -> new CSXTag $1.toString(), o 'JSX_TAG', -> new JSXTag $1.toString(),
tagNameLocationData: $1.tagNameToken[2] tagNameLocationData: $1.tagNameToken[2]
closingTagOpeningBracketLocationData: $1.closingTagOpeningBracketToken?[2] closingTagOpeningBracketLocationData: $1.closingTagOpeningBracketToken?[2]
closingTagSlashLocationData: $1.closingTagSlashToken?[2] closingTagSlashLocationData: $1.closingTagSlashToken?[2]

View File

@ -49,8 +49,8 @@ exports.Lexer = class Lexer
@seenExport = no # Used to recognize `EXPORT FROM? AS?` tokens. @seenExport = no # Used to recognize `EXPORT FROM? AS?` tokens.
@importSpecifierList = no # Used to identify when in an `IMPORT {...} FROM? ...`. @importSpecifierList = no # Used to identify when in an `IMPORT {...} FROM? ...`.
@exportSpecifierList = no # Used to identify when in an `EXPORT {...} FROM? ...`. @exportSpecifierList = no # Used to identify when in an `EXPORT {...} FROM? ...`.
@csxDepth = 0 # Used to optimize CSX checks, how deep in CSX we are. @jsxDepth = 0 # Used to optimize JSX checks, how deep in JSX we are.
@csxObjAttribute = {} # Used to detect if CSX attributes is wrapped in {} (<div {props...} />). @jsxObjAttribute = {} # Used to detect if JSX attributes is wrapped in {} (<div {props...} />).
@chunkLine = @chunkLine =
opts.line or 0 # The start line for the current @chunk. opts.line or 0 # The start line for the current @chunk.
@ -72,7 +72,7 @@ exports.Lexer = class Lexer
@lineToken() or @lineToken() or
@stringToken() or @stringToken() or
@numberToken() or @numberToken() or
@csxToken() or @jsxToken() or
@regexToken() or @regexToken() or
@jsToken() or @jsToken() or
@literalToken() @literalToken()
@ -111,8 +111,8 @@ exports.Lexer = class Lexer
# referenced as property names here, so you can still do `jQuery.is()` even # referenced as property names here, so you can still do `jQuery.is()` even
# though `is` means `===` otherwise. # though `is` means `===` otherwise.
identifierToken: -> identifierToken: ->
inCSXTag = @atCSXTag() inJSXTag = @atJSXTag()
regex = if inCSXTag then CSX_ATTRIBUTE else IDENTIFIER regex = if inJSXTag then JSX_ATTRIBUTE else IDENTIFIER
return 0 unless match = regex.exec @chunk return 0 unless match = regex.exec @chunk
[input, id, colon] = match [input, id, colon] = match
@ -233,10 +233,10 @@ exports.Lexer = class Lexer
[tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] = [tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] =
[poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]] [poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]]
if colon if colon
colonOffset = input.lastIndexOf if inCSXTag then '=' else ':' colonOffset = input.lastIndexOf if inJSXTag then '=' else ':'
colonToken = @token ':', ':', offset: colonOffset, length: colon.length colonToken = @token ':', ':', offset: colonOffset, length: colon.length
colonToken.csxColon = yes if inCSXTag # used by rewriter colonToken.jsxColon = yes if inJSXTag # used by rewriter
if inCSXTag and tag is 'IDENTIFIER' and prev[0] isnt ':' if inJSXTag and tag is 'IDENTIFIER' and prev[0] isnt ':'
@token ',', ',', length: 0, origin: tagToken @token ',', ',', length: 0, origin: tagToken
input.length input.length
@ -304,7 +304,7 @@ exports.Lexer = class Lexer
@mergeInterpolationTokens tokens, {quote, indent, endOffset: end}, (value) => @mergeInterpolationTokens tokens, {quote, indent, endOffset: end}, (value) =>
@validateUnicodeCodePointEscapes value, delimiter: quote @validateUnicodeCodePointEscapes value, delimiter: quote
if @atCSXTag() if @atJSXTag()
@token ',', ',', length: 0, origin: @prev @token ',', ',', length: 0, origin: @prev
end end
@ -543,15 +543,14 @@ exports.Lexer = class Lexer
@tokens.pop() @tokens.pop()
this this
# CSX is like JSX but for CoffeeScript. jsxToken: ->
csxToken: ->
firstChar = @chunk[0] firstChar = @chunk[0]
# Check the previous token to detect if attribute is spread. # Check the previous token to detect if attribute is spread.
prevChar = if @tokens.length > 0 then @tokens[@tokens.length - 1][0] else '' prevChar = if @tokens.length > 0 then @tokens[@tokens.length - 1][0] else ''
if firstChar is '<' if firstChar is '<'
match = CSX_IDENTIFIER.exec(@chunk[1...]) or CSX_FRAGMENT_IDENTIFIER.exec(@chunk[1...]) match = JSX_IDENTIFIER.exec(@chunk[1...]) or JSX_FRAGMENT_IDENTIFIER.exec(@chunk[1...])
return 0 unless match and ( return 0 unless match and (
@csxDepth > 0 or @jsxDepth > 0 or
# Not the right hand side of an unspaced comparison (i.e. `a<b`). # Not the right hand side of an unspaced comparison (i.e. `a<b`).
not (prev = @prev()) or not (prev = @prev()) or
prev.spaced or prev.spaced or
@ -563,7 +562,7 @@ exports.Lexer = class Lexer
[id, properties...] = id.split '.' [id, properties...] = id.split '.'
else else
properties = [] properties = []
tagToken = @token 'CSX_TAG', id, tagToken = @token 'JSX_TAG', id,
length: id.length + 1 length: id.length + 1
data: data:
openingBracketToken: @makeToken '<', '<' openingBracketToken: @makeToken '<', '<'
@ -577,9 +576,9 @@ exports.Lexer = class Lexer
@token 'CALL_START', '(', generated: yes @token 'CALL_START', '(', generated: yes
@token '[', '[', generated: yes @token '[', '[', generated: yes
@ends.push {tag: '/>', origin: tagToken, name: id, properties} @ends.push {tag: '/>', origin: tagToken, name: id, properties}
@csxDepth++ @jsxDepth++
return fullId.length + 1 return fullId.length + 1
else if csxTag = @atCSXTag() else if jsxTag = @atJSXTag()
if @chunk[...2] is '/>' # Self-closing tag. if @chunk[...2] is '/>' # Self-closing tag.
@pair '/>' @pair '/>'
@token ']', ']', @token ']', ']',
@ -591,15 +590,15 @@ exports.Lexer = class Lexer
data: data:
selfClosingSlashToken: @makeToken '/', '/' selfClosingSlashToken: @makeToken '/', '/'
closingBracketToken: @makeToken '>', '>', offset: 1 closingBracketToken: @makeToken '>', '>', offset: 1
@csxDepth-- @jsxDepth--
return 2 return 2
else if firstChar is '{' else if firstChar is '{'
if prevChar is ':' if prevChar is ':'
token = @token '(', '(' token = @token '(', '('
@csxObjAttribute[@csxDepth] = no @jsxObjAttribute[@jsxDepth] = no
else else
token = @token '{', '{' token = @token '{', '{'
@csxObjAttribute[@csxDepth] = yes @jsxObjAttribute[@jsxDepth] = yes
@ends.push {tag: '}', origin: token} @ends.push {tag: '}', origin: token}
return 1 return 1
else if firstChar is '>' # end of opening tag else if firstChar is '>' # end of opening tag
@ -611,13 +610,13 @@ exports.Lexer = class Lexer
closingBracketToken: @makeToken '>', '>' closingBracketToken: @makeToken '>', '>'
@token ',', 'JSX_COMMA', generated: yes @token ',', 'JSX_COMMA', generated: yes
{tokens, index: end} = {tokens, index: end} =
@matchWithInterpolations INSIDE_CSX, '>', '</', CSX_INTERPOLATION @matchWithInterpolations INSIDE_JSX, '>', '</', JSX_INTERPOLATION
@mergeInterpolationTokens tokens, {endOffset: end}, (value) => @mergeInterpolationTokens tokens, {endOffset: end}, (value) =>
@validateUnicodeCodePointEscapes value, delimiter: '>' @validateUnicodeCodePointEscapes value, delimiter: '>'
match = CSX_IDENTIFIER.exec(@chunk[end...]) or CSX_FRAGMENT_IDENTIFIER.exec(@chunk[end...]) match = JSX_IDENTIFIER.exec(@chunk[end...]) or JSX_FRAGMENT_IDENTIFIER.exec(@chunk[end...])
if not match or match[1] isnt "#{csxTag.name}#{(".#{property}" for property in csxTag.properties).join ''}" if not match or match[1] isnt "#{jsxTag.name}#{(".#{property}" for property in jsxTag.properties).join ''}"
@error "expected corresponding CSX closing tag for #{csxTag.name}", @error "expected corresponding JSX closing tag for #{jsxTag.name}",
csxTag.origin.data.tagNameToken[2] jsxTag.origin.data.tagNameToken[2]
[, fullTagName] = match [, fullTagName] = match
afterTag = end + fullTagName.length afterTag = end + fullTagName.length
if @chunk[afterTag] isnt '>' if @chunk[afterTag] isnt '>'
@ -635,16 +634,16 @@ exports.Lexer = class Lexer
closingTagClosingBracketToken: @makeToken '>', '>', offset: end + fullTagName.length closingTagClosingBracketToken: @makeToken '>', '>', offset: end + fullTagName.length
# make the closing tag location data more easily accessible to the grammar # make the closing tag location data more easily accessible to the grammar
addTokenData openingTagToken, endToken.data addTokenData openingTagToken, endToken.data
@csxDepth-- @jsxDepth--
return afterTag + 1 return afterTag + 1
else else
return 0 return 0
else if @atCSXTag 1 else if @atJSXTag 1
if firstChar is '}' if firstChar is '}'
@pair firstChar @pair firstChar
if @csxObjAttribute[@csxDepth] if @jsxObjAttribute[@jsxDepth]
@token '}', '}' @token '}', '}'
@csxObjAttribute[@csxDepth] = no @jsxObjAttribute[@jsxDepth] = no
else else
@token ')', ')' @token ')', ')'
@token ',', ',' @token ',', ','
@ -654,8 +653,8 @@ exports.Lexer = class Lexer
else else
return 0 return 0
atCSXTag: (depth = 0) -> atJSXTag: (depth = 0) ->
return no if @csxDepth is 0 return no if @jsxDepth is 0
i = @ends.length - 1 i = @ends.length - 1
i-- while @ends[i]?.tag is 'OUTDENT' or depth-- > 0 # Ignore indents. i-- while @ends[i]?.tag is 'OUTDENT' or depth-- > 0 # Ignore indents.
last = @ends[i] last = @ends[i]
@ -782,9 +781,9 @@ exports.Lexer = class Lexer
# `#{` if interpolations are desired). # `#{` if interpolations are desired).
# - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`, # - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
# `"""` and `///`. # `"""` and `///`.
# - `closingDelimiter` is different from `delimiter` only in CSX # - `closingDelimiter` is different from `delimiter` only in JSX
# - `interpolators` matches the start of an interpolation, for CSX it's both # - `interpolators` matches the start of an interpolation, for JSX it's both
# `{` and `<` (i.e. nested CSX tag) # `{` and `<` (i.e. nested JSX tag)
# #
# This method allows us to have strings within interpolations within strings, # This method allows us to have strings within interpolations within strings,
# ad infinitum. # ad infinitum.
@ -1163,17 +1162,17 @@ IDENTIFIER = /// ^
( [^\n\S]* : (?!:) )? # Is this a property name? ( [^\n\S]* : (?!:) )? # Is this a property name?
/// ///
CSX_IDENTIFIER = /// ^ JSX_IDENTIFIER = /// ^
(?![\d<]) # Must not start with `<`. (?![\d<]) # Must not start with `<`.
( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s and `.`s. ( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s and `.`s.
/// ///
# Fragment: <></> # Fragment: <></>
CSX_FRAGMENT_IDENTIFIER = /// ^ JSX_FRAGMENT_IDENTIFIER = /// ^
()> # Ends immediately with `>`. ()> # Ends immediately with `>`.
/// ///
CSX_ATTRIBUTE = /// ^ JSX_ATTRIBUTE = /// ^
(?!\d) (?!\d)
( (?: (?!\s)[\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s. ( (?: (?!\s)[\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s.
( [^\S]* = (?!=) )? # Is this an attribute with a value? ( [^\S]* = (?!=) )? # Is this an attribute with a value?
@ -1215,15 +1214,15 @@ STRING_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | \#(?!\{) )* ///
HEREDOC_SINGLE = /// ^(?: [^\\'] | \\[\s\S] | '(?!'') )* /// HEREDOC_SINGLE = /// ^(?: [^\\'] | \\[\s\S] | '(?!'') )* ///
HEREDOC_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | "(?!"") | \#(?!\{) )* /// HEREDOC_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | "(?!"") | \#(?!\{) )* ///
INSIDE_CSX = /// ^(?: INSIDE_JSX = /// ^(?:
[^ [^
\{ # Start of CoffeeScript interpolation. \{ # Start of CoffeeScript interpolation.
< # Maybe CSX tag (`<` not allowed even if bare). < # Maybe JSX tag (`<` not allowed even if bare).
] ]
)* /// # Similar to `HEREDOC_DOUBLE` but there is no escaping. )* /// # Similar to `HEREDOC_DOUBLE` but there is no escaping.
CSX_INTERPOLATION = /// ^(?: JSX_INTERPOLATION = /// ^(?:
\{ # CoffeeScript interpolation. \{ # CoffeeScript interpolation.
| <(?!/) # CSX opening tag. | <(?!/) # JSX opening tag.
)/// )///
HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g

View File

@ -563,14 +563,14 @@ exports.Block = class Block extends Base
len = @expressions.length len = @expressions.length
[..., lastExp] = @expressions [..., lastExp] = @expressions
lastExp = lastExp?.unwrap() or no lastExp = lastExp?.unwrap() or no
# We also need to check that were not returning a CSX tag if theres an # We also need to check that were not returning a JSX tag if theres an
# adjacent one at the same level; JSX doesnt allow that. # adjacent one at the same level; JSX doesnt allow that.
if lastExp and lastExp instanceof Parens and lastExp.body.expressions.length > 1 if lastExp and lastExp instanceof Parens and lastExp.body.expressions.length > 1
{body:{expressions}} = lastExp {body:{expressions}} = lastExp
[..., penult, last] = expressions [..., penult, last] = expressions
penult = penult.unwrap() penult = penult.unwrap()
last = last.unwrap() last = last.unwrap()
if penult instanceof CSXElement and last instanceof CSXElement if penult instanceof JSXElement and last instanceof JSXElement
expressions[expressions.length - 1].error 'Adjacent JSX elements must be wrapped in an enclosing tag' expressions[expressions.length - 1].error 'Adjacent JSX elements must be wrapped in an enclosing tag'
while len-- while len--
expr = @expressions[len] expr = @expressions[len]
@ -940,13 +940,13 @@ exports.StringLiteral = class StringLiteral extends Literal
} }
compileNode: (o) -> compileNode: (o) ->
return [@makeCode @unquote(yes, yes)] if @csx return [@makeCode @unquote(yes, yes)] if @jsx
super o super o
unquote: (doubleQuote = no, csx = no) -> unquote: (doubleQuote = no, jsx = no) ->
unquoted = @value[1...-1] unquoted = @value[1...-1]
unquoted = unquoted.replace /\\"/g, '"' if doubleQuote unquoted = unquoted.replace /\\"/g, '"' if doubleQuote
unquoted = unquoted.replace /\\n/g, '\n' if csx unquoted = unquoted.replace /\\n/g, '\n' if jsx
unquoted unquoted
# `StringLiteral`s can represent either entire literal strings # `StringLiteral`s can represent either entire literal strings
@ -1025,7 +1025,7 @@ exports.IdentifierLiteral = class IdentifierLiteral extends Literal
iterator @ iterator @
astType: -> astType: ->
if @csx if @jsx
'JSXIdentifier' 'JSXIdentifier'
else else
'Identifier' 'Identifier'
@ -1034,27 +1034,11 @@ exports.IdentifierLiteral = class IdentifierLiteral extends Literal
return return
name: @value name: @value
exports.CSXTag = class CSXTag extends IdentifierLiteral
constructor: (value, {
@tagNameLocationData
@closingTagOpeningBracketLocationData
@closingTagSlashLocationData
@closingTagNameLocationData
@closingTagClosingBracketLocationData
}) ->
super value
astType: -> 'JSXIdentifier'
astProperties: ->
return
name: @value
exports.PropertyName = class PropertyName extends Literal exports.PropertyName = class PropertyName extends Literal
isAssignable: YES isAssignable: YES
astType: -> astType: ->
if @csx if @jsx
'JSXIdentifier' 'JSXIdentifier'
else else
'Identifier' 'Identifier'
@ -1269,7 +1253,7 @@ exports.Value = class Value extends Base
@isUndefined() or @isNull() or @isBoolean() @isUndefined() or @isNull() or @isBoolean()
isStatement : (o) -> not @properties.length and @base.isStatement o isStatement : (o) -> not @properties.length and @base.isStatement o
isCSXTag : -> @base instanceof CSXTag isJSXTag : -> @base instanceof JSXTag
assigns : (name) -> not @properties.length and @base.assigns name assigns : (name) -> not @properties.length and @base.assigns name
jumps : (o) -> not @properties.length and @base.jumps o jumps : (o) -> not @properties.length and @base.jumps o
@ -1395,7 +1379,7 @@ exports.Value = class Value extends Base
super o, level super o, level
astType: -> astType: ->
if @isCSXTag() if @isJSXTag()
'JSXMemberExpression' 'JSXMemberExpression'
else else
'MemberExpression' 'MemberExpression'
@ -1405,7 +1389,7 @@ exports.Value = class Value extends Base
# a child `Value` node assigned to the `object` property. # a child `Value` node assigned to the `object` property.
astProperties: (o) -> astProperties: (o) ->
[..., property] = @properties [..., property] = @properties
property.name.csx = yes if @isCSXTag() property.name.jsx = yes if @isJSXTag()
computed = property instanceof Index or property.name?.unwrap() not instanceof PropertyName computed = property instanceof Index or property.name?.unwrap() not instanceof PropertyName
return { return {
object: @object().ast o, LEVEL_ACCESS object: @object().ast o, LEVEL_ACCESS
@ -1416,7 +1400,7 @@ exports.Value = class Value extends Base
} }
astLocationData: -> astLocationData: ->
return super() unless @isCSXTag() return super() unless @isJSXTag()
# don't include leading < of JSX tag in location data # don't include leading < of JSX tag in location data
mergeAstLocationData( mergeAstLocationData(
jisonLocationDataToAstLocationData(@base.tagNameLocationData), jisonLocationDataToAstLocationData(@base.tagNameLocationData),
@ -1497,15 +1481,29 @@ exports.LineComment = class LineComment extends Base
fragment.isComment = fragment.isLineComment = yes fragment.isComment = fragment.isLineComment = yes
fragment fragment
#### CSX #### JSX
exports.CSXIdentifier = class CSXIdentifier extends IdentifierLiteral exports.JSXIdentifier = class JSXIdentifier extends IdentifierLiteral
astType: -> 'JSXIdentifier' astType: -> 'JSXIdentifier'
exports.CSXExpressionContainer = class CSXExpressionContainer extends Base exports.JSXTag = class JSXTag extends JSXIdentifier
constructor: (value, {
@tagNameLocationData
@closingTagOpeningBracketLocationData
@closingTagSlashLocationData
@closingTagNameLocationData
@closingTagClosingBracketLocationData
}) ->
super value
astProperties: ->
return
name: @value
exports.JSXExpressionContainer = class JSXExpressionContainer extends Base
constructor: (@expression, {locationData} = {}) -> constructor: (@expression, {locationData} = {}) ->
super() super()
@expression.csxAttribute = yes @expression.jsxAttribute = yes
@locationData = locationData ? @expression.locationData @locationData = locationData ? @expression.locationData
children: ['expression'] children: ['expression']
@ -1513,23 +1511,18 @@ exports.CSXExpressionContainer = class CSXExpressionContainer extends Base
compileNode: (o) -> compileNode: (o) ->
@expression.compileNode(o) @expression.compileNode(o)
astType: -> 'JSXExpressionContainer'
astProperties: -> astProperties: ->
return return
expression: @expression.ast() expression: @expression.ast()
exports.CSXEmptyExpression = class CSXEmptyExpression extends Base exports.JSXEmptyExpression = class JSXEmptyExpression extends Base
astType: -> 'JSXEmptyExpression'
exports.CSXText = class CSXText extends Base exports.JSXText = class JSXText extends Base
constructor: (stringLiteral) -> constructor: (stringLiteral) ->
super() super()
@value = stringLiteral.unquote yes, yes @value = stringLiteral.unquote yes, yes
@locationData = stringLiteral.locationData @locationData = stringLiteral.locationData
astType: -> 'JSXText'
astProperties: -> astProperties: ->
return { return {
@value @value
@ -1537,7 +1530,7 @@ exports.CSXText = class CSXText extends Base
raw: @value raw: @value
} }
exports.CSXAttribute = class CSXAttribute extends Base exports.JSXAttribute = class JSXAttribute extends Base
constructor: ({@name, value}) -> constructor: ({@name, value}) ->
super() super()
@value = @value =
@ -1546,7 +1539,7 @@ exports.CSXAttribute = class CSXAttribute extends Base
if value instanceof StringLiteral if value instanceof StringLiteral
value value
else else
new CSXExpressionContainer value new JSXExpressionContainer value
else else
null null
@value?.comments = value.comments @value?.comments = value.comments
@ -1559,14 +1552,12 @@ exports.CSXAttribute = class CSXAttribute extends Base
val = @value.compileToFragments o, LEVEL_LIST val = @value.compileToFragments o, LEVEL_LIST
compiledName.concat @makeCode('='), val compiledName.concat @makeCode('='), val
astType: -> 'JSXAttribute'
astProperties: -> astProperties: ->
return return
name: @name.ast() name: @name.ast()
value: @value?.ast() ? null value: @value?.ast() ? null
exports.CSXAttributes = class CSXAttributes extends Base exports.JSXAttributes = class JSXAttributes extends Base
constructor: (arr) -> constructor: (arr) ->
super() super()
@attributes = [] @attributes = []
@ -1575,21 +1566,21 @@ exports.CSXAttributes = class CSXAttributes extends Base
{base} = object {base} = object
if base instanceof IdentifierLiteral if base instanceof IdentifierLiteral
# attribute with no value eg disabled # attribute with no value eg disabled
attribute = new CSXAttribute name: new CSXIdentifier(base.value).withLocationDataAndCommentsFrom base attribute = new JSXAttribute name: new JSXIdentifier(base.value).withLocationDataAndCommentsFrom base
attribute.locationData = base.locationData attribute.locationData = base.locationData
@attributes.push attribute @attributes.push attribute
else if not base.generated else if not base.generated
# object spread attribute eg {...props} # object spread attribute eg {...props}
attribute = base.properties[0] attribute = base.properties[0]
attribute.csx = yes attribute.jsx = yes
attribute.locationData = base.locationData attribute.locationData = base.locationData
@attributes.push attribute @attributes.push attribute
else else
# Obj containing attributes with values eg a="b" c={d} # Obj containing attributes with values eg a="b" c={d}
for property in base.properties for property in base.properties
{variable, value} = property {variable, value} = property
attribute = new CSXAttribute { attribute = new JSXAttribute {
name: new CSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom variable.base name: new JSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom variable.base
value value
} }
attribute.locationData = property.locationData attribute.locationData = property.locationData
@ -1604,7 +1595,7 @@ exports.CSXAttributes = class CSXAttributes extends Base
properties = attribute?.properties or [] properties = attribute?.properties or []
if not (attribute instanceof Obj or attribute instanceof IdentifierLiteral) or (attribute instanceof Obj and not attribute.generated and (properties.length > 1 or not (properties[0] instanceof Splat))) if not (attribute instanceof Obj or attribute instanceof IdentifierLiteral) or (attribute instanceof Obj and not attribute.generated and (properties.length > 1 or not (properties[0] instanceof Splat)))
object.error """ object.error """
Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
""" """
compileNode: (o) -> compileNode: (o) ->
@ -1617,15 +1608,15 @@ exports.CSXAttributes = class CSXAttributes extends Base
ast: (o) -> ast: (o) ->
attribute.ast(o) for attribute in @attributes attribute.ast(o) for attribute in @attributes
# Node for a CSX element # Node for a JSX element
exports.CSXElement = class CSXElement extends Base exports.JSXElement = class JSXElement extends Base
constructor: ({@tagName, @attributes, @content}) -> constructor: ({@tagName, @attributes, @content}) ->
super() super()
children: ['tagName', 'attributes', 'content'] children: ['tagName', 'attributes', 'content']
compileNode: (o) -> compileNode: (o) ->
@content?.base.csx = yes @content?.base.jsx = yes
fragments = [@makeCode('<')] fragments = [@makeCode('<')]
fragments.push (tag = @tagName.compileToFragments(o, LEVEL_ACCESS))... fragments.push (tag = @tagName.compileToFragments(o, LEVEL_ACCESS))...
fragments.push @attributes.compileToFragments(o)... fragments.push @attributes.compileToFragments(o)...
@ -1719,23 +1710,23 @@ exports.CSXElement = class CSXElement extends Base
content = @content.unwrapAll() content = @content.unwrapAll()
children = children =
if content instanceof StringLiteral if content instanceof StringLiteral
[new CSXText content] [new JSXText content]
else # StringWithInterpolations else # StringWithInterpolations
for element in @content.unwrapAll().extractElements o, includeInterpolationWrappers: yes for element in @content.unwrapAll().extractElements o, includeInterpolationWrappers: yes
if element instanceof StringLiteral if element instanceof StringLiteral
new CSXText element new JSXText element
else # Interpolation else # Interpolation
{expression} = element {expression} = element
unless expression? unless expression?
new CSXEmptyExpression().withLocationDataFrom element new JSXEmptyExpression().withLocationDataFrom element
else else
unwrapped = expression.unwrapAll() unwrapped = expression.unwrapAll()
if unwrapped instanceof CSXElement if unwrapped instanceof JSXElement
unwrapped unwrapped
else else
new CSXExpressionContainer unwrapped, locationData: element.locationData new JSXExpressionContainer unwrapped, locationData: element.locationData
child.ast(o) for child in children when not (child instanceof CSXText and child.value.length is 0) child.ast(o) for child in children when not (child instanceof JSXText and child.value.length is 0)
astProperties: (o) -> astProperties: (o) ->
Object.assign( Object.assign(
@ -1765,10 +1756,10 @@ exports.Call = class Call extends Base
if @variable instanceof Value and @variable.isNotCallable() if @variable instanceof Value and @variable.isNotCallable()
@variable.error "literal is not a function" @variable.error "literal is not a function"
if @variable.base instanceof CSXTag if @variable.base instanceof JSXTag
return new CSXElement( return new JSXElement(
tagName: @variable tagName: @variable
attributes: new CSXAttributes @args[0].base attributes: new JSXAttributes @args[0].base
content: @args[1] content: @args[1]
) )
@ -3905,13 +3896,13 @@ exports.Splat = class Splat extends Base
compileNode: (o) -> compileNode: (o) ->
compiledSplat = [@makeCode('...'), @name.compileToFragments(o, LEVEL_OP)...] compiledSplat = [@makeCode('...'), @name.compileToFragments(o, LEVEL_OP)...]
return compiledSplat unless @csx return compiledSplat unless @jsx
return [@makeCode('{'), compiledSplat..., @makeCode('}')] return [@makeCode('{'), compiledSplat..., @makeCode('}')]
unwrap: -> @name unwrap: -> @name
astType: -> astType: ->
if @csx if @jsx
'JSXSpreadAttribute' 'JSXSpreadAttribute'
else if @lhs else if @lhs
'RestElement' 'RestElement'
@ -4527,7 +4518,7 @@ exports.Parens = class Parens extends Base
# by comment-based type annotations from JavaScript labels. # by comment-based type annotations from JavaScript labels.
shouldWrapComment = expr.comments?.some( shouldWrapComment = expr.comments?.some(
(comment) -> comment.here and not comment.unshift and not comment.newLine) (comment) -> comment.here and not comment.unshift and not comment.newLine)
if expr instanceof Value and expr.isAtomic() and not @csxAttribute and not shouldWrapComment if expr instanceof Value and expr.isAtomic() and not @jsxAttribute and not shouldWrapComment
expr.front = @front expr.front = @front
return expr.compileToFragments o return expr.compileToFragments o
fragments = expr.compileToFragments o, LEVEL_PAREN fragments = expr.compileToFragments o, LEVEL_PAREN
@ -4535,7 +4526,7 @@ exports.Parens = class Parens extends Base
expr instanceof Op and not expr.isInOperator() or expr.unwrap() instanceof Call or expr instanceof Op and not expr.isInOperator() or expr.unwrap() instanceof Call or
(expr instanceof For and expr.returns) (expr instanceof For and expr.returns)
) and (o.level < LEVEL_COND or fragments.length <= 3) ) and (o.level < LEVEL_COND or fragments.length <= 3)
return @wrapInBraces fragments if @csxAttribute return @wrapInBraces fragments if @jsxAttribute
if bare then fragments else @wrapInParentheses fragments if bare then fragments else @wrapInParentheses fragments
ast: (o) -> @body.unwrap().ast o, LEVEL_PAREN ast: (o) -> @body.unwrap().ast o, LEVEL_PAREN
@ -4580,7 +4571,7 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
comment.unshift = yes comment.unshift = yes
comment.newLine = yes comment.newLine = yes
attachCommentsToNode salvagedComments, node attachCommentsToNode salvagedComments, node
if (unwrapped = node.expression?.unwrapAll()) instanceof PassthroughLiteral and unwrapped.generated and not @csx if (unwrapped = node.expression?.unwrapAll()) instanceof PassthroughLiteral and unwrapped.generated and not @jsx
commentPlaceholder = new StringLiteral('').withLocationDataFrom node commentPlaceholder = new StringLiteral('').withLocationDataFrom node
commentPlaceholder.comments = unwrapped.comments commentPlaceholder.comments = unwrapped.comments
(commentPlaceholder.comments ?= []).push node.comments... if node.comments (commentPlaceholder.comments ?= []).push node.comments... if node.comments
@ -4606,19 +4597,19 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
compileNode: (o) -> compileNode: (o) ->
@comments ?= @startQuote?.comments @comments ?= @startQuote?.comments
if @csxAttribute if @jsxAttribute
wrapped = new Parens new StringWithInterpolations @body wrapped = new Parens new StringWithInterpolations @body
wrapped.csxAttribute = yes wrapped.jsxAttribute = yes
return wrapped.compileNode o return wrapped.compileNode o
elements = @extractElements o elements = @extractElements o
fragments = [] fragments = []
fragments.push @makeCode '`' unless @csx fragments.push @makeCode '`' unless @jsx
for element in elements for element in elements
if element instanceof StringLiteral if element instanceof StringLiteral
element.value = element.unquote yes, @csx element.value = element.unquote yes, @jsx
unless @csx unless @jsx
# Backticks and `${` inside template literals must be escaped. # Backticks and `${` inside template literals must be escaped.
element.value = element.value.replace /(\\*)(`|\$\{)/g, (match, backslashes, toBeEscaped) -> element.value = element.value.replace /(\\*)(`|\$\{)/g, (match, backslashes, toBeEscaped) ->
if backslashes.length % 2 is 0 if backslashes.length % 2 is 0
@ -4627,7 +4618,7 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
match match
fragments.push element.compileToFragments(o)... fragments.push element.compileToFragments(o)...
else else
fragments.push @makeCode '$' unless @csx fragments.push @makeCode '$' unless @jsx
code = element.compileToFragments(o, LEVEL_PAREN) code = element.compileToFragments(o, LEVEL_PAREN)
if not @isNestedTag(element) or if not @isNestedTag(element) or
code.some((fragment) -> fragment.comments?.some((comment) -> comment.here is no)) code.some((fragment) -> fragment.comments?.some((comment) -> comment.here is no))
@ -4641,12 +4632,12 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
code[0].isStringWithInterpolations = yes code[0].isStringWithInterpolations = yes
code[code.length - 1].isStringWithInterpolations = yes code[code.length - 1].isStringWithInterpolations = yes
fragments.push code... fragments.push code...
fragments.push @makeCode '`' unless @csx fragments.push @makeCode '`' unless @jsx
fragments fragments
isNestedTag: (element) -> isNestedTag: (element) ->
call = element.unwrapAll?() call = element.unwrapAll?()
@csx and call instanceof CSXElement @jsx and call instanceof JSXElement
astType: -> 'TemplateLiteral' astType: -> 'TemplateLiteral'

View File

@ -55,7 +55,7 @@ exports.Rewriter = class Rewriter
@addImplicitBracesAndParens() @addImplicitBracesAndParens()
@rescueStowawayComments() @rescueStowawayComments()
@addLocationDataToGeneratedTokens() @addLocationDataToGeneratedTokens()
@enforceValidCSXAttributes() @enforceValidJSXAttributes()
@fixOutdentLocationData() @fixOutdentLocationData()
@exposeTokenDataToGrammar() @exposeTokenDataToGrammar()
if process?.env?.DEBUG_REWRITTEN_TOKEN_STREAM if process?.env?.DEBUG_REWRITTEN_TOKEN_STREAM
@ -409,10 +409,10 @@ exports.Rewriter = class Rewriter
endImplicitObject i + offset endImplicitObject i + offset
return forward(1) return forward(1)
# Make sure only strings and wrapped expressions are used in CSX attributes. # Make sure only strings and wrapped expressions are used in JSX attributes.
enforceValidCSXAttributes: -> enforceValidJSXAttributes: ->
@scanTokens (token, i, tokens) -> @scanTokens (token, i, tokens) ->
if token.csxColon if token.jsxColon
next = tokens[i + 1] next = tokens[i + 1]
if next[0] not in ['STRING_START', 'STRING', '('] if next[0] not in ['STRING_START', 'STRING', '(']
throwSyntaxError 'expected wrapped or quoted JSX attribute', next[2] throwSyntaxError 'expected wrapped or quoted JSX attribute', next[2]
@ -722,7 +722,7 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'IN
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation. # If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL = [ IMPLICIT_CALL = [
'IDENTIFIER', 'CSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN' 'IDENTIFIER', 'JSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN'
'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS' 'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS'
'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS' 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS'
'UNDEFINED', 'NULL', 'BOOL' 'UNDEFINED', 'NULL', 'BOOL'

View File

@ -200,7 +200,7 @@ test "AST as expected for IdentifierLiteral node", ->
type: 'Identifier' type: 'Identifier'
name: 'id' name: 'id'
test "AST as expected for CSXTag node", -> test "AST as expected for JSXTag node", ->
testExpression '<CSXY />', testExpression '<CSXY />',
type: 'JSXElement' type: 'JSXElement'
openingElement: openingElement:

View File

@ -2241,7 +2241,7 @@ test "AST location data as expected for Existence node", ->
line: 1 line: 1
column: 7 column: 7
test "AST location data as expected for CSXTag node", -> test "AST location data as expected for JSXTag node", ->
testAstLocationData '<CSXY />', testAstLocationData '<CSXY />',
type: 'JSXElement' type: 'JSXElement'
openingElement: openingElement:

View File

@ -725,7 +725,7 @@ test "Block comment in an interpolated string", ->
eqJS '"a#{### Comment ###}b"', '`a${/* Comment */""}b`;' eqJS '"a#{### Comment ###}b"', '`a${/* Comment */""}b`;'
eqJS '"a#{### 1 ###}b#{### 2 ###}c"', '`a${/* 1 */""}b${/* 2 */""}c`;' eqJS '"a#{### 1 ###}b#{### 2 ###}c"', '`a${/* 1 */""}b${/* 2 */""}c`;'
test "#4629: Block comment in CSX interpolation", -> test "#4629: Block comment in JSX interpolation", ->
eqJS '<div>{### Comment ###}</div>', '<div>{/* Comment */}</div>;' eqJS '<div>{### Comment ###}</div>', '<div>{/* Comment */}</div>;'
eqJS ''' eqJS '''
<div> <div>

View File

@ -1589,17 +1589,17 @@ test "#4248: Unicode code point escapes", ->
\ ^\^^^^^^^^^^^^^ \ ^\^^^^^^^^^^^^^
''' '''
test "CSX error: non-matching tag names", -> test "JSX error: non-matching tag names", ->
assertErrorFormat ''' assertErrorFormat '''
<div><span></div></span> <div><span></div></span>
''', ''',
''' '''
[stdin]:1:7: error: expected corresponding CSX closing tag for span [stdin]:1:7: error: expected corresponding JSX closing tag for span
<div><span></div></span> <div><span></div></span>
^^^^ ^^^^
''' '''
test "CSX error: bare expressions not allowed", -> test "JSX error: bare expressions not allowed", ->
assertErrorFormat ''' assertErrorFormat '''
<div x=3 /> <div x=3 />
''', ''',
@ -1609,7 +1609,7 @@ test "CSX error: bare expressions not allowed", ->
^ ^
''' '''
test "CSX error: unescaped opening tag angle bracket disallowed", -> test "JSX error: unescaped opening tag angle bracket disallowed", ->
assertErrorFormat ''' assertErrorFormat '''
<Person><<</Person> <Person><<</Person>
''', ''',
@ -1619,7 +1619,7 @@ test "CSX error: unescaped opening tag angle bracket disallowed", ->
^^ ^^
''' '''
test "CSX error: ambiguous tag-like expression", -> test "JSX error: ambiguous tag-like expression", ->
assertErrorFormat ''' assertErrorFormat '''
x = a <b > c x = a <b > c
''', ''',
@ -1629,51 +1629,51 @@ test "CSX error: ambiguous tag-like expression", ->
^ ^
''' '''
test 'CSX error: invalid attributes', -> test 'JSX error: invalid attributes', ->
assertErrorFormatAst ''' assertErrorFormatAst '''
<div a="b" {props} /> <div a="b" {props} />
''', ''' ''', '''
[stdin]:1:12: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:12: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div a="b" {props} /> <div a="b" {props} />
^^^^^^^ ^^^^^^^
''' '''
assertErrorFormatAst ''' assertErrorFormatAst '''
<div a={b} {a:{b}} /> <div a={b} {a:{b}} />
''', ''' ''', '''
[stdin]:1:12: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:12: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div a={b} {a:{b}} /> <div a={b} {a:{b}} />
^^^^^^^ ^^^^^^^
''' '''
assertErrorFormatAst ''' assertErrorFormatAst '''
<div {"#{a}"} /> <div {"#{a}"} />
''', ''' ''', '''
[stdin]:1:6: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div {"#{a}"} /> <div {"#{a}"} />
^^^^^^^^ ^^^^^^^^
''' '''
assertErrorFormatAst ''' assertErrorFormatAst '''
<div props... /> <div props... />
''', ''' ''', '''
[stdin]:1:11: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:11: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div props... /> <div props... />
^^^ ^^^
''' '''
assertErrorFormatAst ''' assertErrorFormatAst '''
<div {a:"b", props..., c:d()} /> <div {a:"b", props..., c:d()} />
''', ''' ''', '''
[stdin]:1:6: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div {a:"b", props..., c:d()} /> <div {a:"b", props..., c:d()} />
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
''' '''
assertErrorFormatAst ''' assertErrorFormatAst '''
<div {props..., a, b} /> <div {props..., a, b} />
''', ''' ''', '''
[stdin]:1:6: error: Unexpected token. Allowed CSX attributes are: id="val", src={source}, {props...} or attribute. [stdin]:1:6: error: Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
<div {props..., a, b} /> <div {props..., a, b} />
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
''' '''
test '#5034: CSX error: Adjacent JSX elements must be wrapped in an enclosing tag', -> test '#5034: JSX error: Adjacent JSX elements must be wrapped in an enclosing tag', ->
assertErrorFormat ''' assertErrorFormat '''
render = -> ( render = -> (
<Row>a</Row> <Row>a</Row>

View File

@ -184,7 +184,7 @@ test 'escaped CoffeeScript attribute over multiple lines', ->
<Person name={test() ? 'yes' : 'no'} />; <Person name={test() ? 'yes' : 'no'} />;
''' '''
test 'multiple line escaped CoffeeScript with nested CSX', -> test 'multiple line escaped CoffeeScript with nested JSX', ->
eqJS ''' eqJS '''
<Person name={ <Person name={
if test() if test()
@ -222,7 +222,7 @@ test 'multiple line escaped CoffeeScript with nested CSX', ->
</Person>; </Person>;
''' '''
test 'nested CSX within an attribute, with object attr value', -> test 'nested JSX within an attribute, with object attr value', ->
eqJS ''' eqJS '''
<Company> <Company>
<Person name={<NameComponent attr3={ {'a': {}, b: '{'} } />} /> <Person name={<NameComponent attr3={ {'a': {}, b: '{'} } />} />
@ -248,7 +248,7 @@ test 'complex nesting', ->
})} />; })} />;
''' '''
test 'multiline tag with nested CSX within an attribute', -> test 'multiline tag with nested JSX within an attribute', ->
eqJS ''' eqJS '''
<Person <Person
name={ name={
@ -299,7 +299,7 @@ test 'lots of attributes', ->
<Person eyes={2} friends={getFriends()} popular="yes" active={isActive() ? 'active' : 'inactive'} data-attr='works' checked check={me_out} />; <Person eyes={2} friends={getFriends()} popular="yes" active={isActive() ? 'active' : 'inactive'} data-attr='works' checked check={me_out} />;
''' '''
# TODO: fix partially indented CSX # TODO: fix partially indented JSX
# test 'multiline elements', -> # test 'multiline elements', ->
# eqJS ''' # eqJS '''
# <div something={ # <div something={
@ -378,7 +378,7 @@ test 'heregex', ->
<Person />; <Person />;
''' '''
test 'comment within CSX is not treated as comment', -> test 'comment within JSX is not treated as comment', ->
eqJS ''' eqJS '''
<Person> <Person>
# i am not a comment # i am not a comment
@ -389,7 +389,7 @@ test 'comment within CSX is not treated as comment', ->
</Person>; </Person>;
''' '''
test 'comment at start of CSX escape', -> test 'comment at start of JSX escape', ->
eqJS ''' eqJS '''
<Person> <Person>
{# i am a comment {# i am a comment
@ -403,7 +403,7 @@ test 'comment at start of CSX escape', ->
</Person>; </Person>;
''' '''
test 'comment at end of CSX escape', -> test 'comment at end of JSX escape', ->
eqJS ''' eqJS '''
<Person> <Person>
{"i am a string" {"i am a string"
@ -418,7 +418,7 @@ test 'comment at end of CSX escape', ->
</Person>; </Person>;
''' '''
test 'CSX comment cannot be used inside interpolation', -> test 'JSX comment cannot be used inside interpolation', ->
throws -> CoffeeScript.compile ''' throws -> CoffeeScript.compile '''
<Person> <Person>
{# i am a comment} {# i am a comment}
@ -430,21 +430,21 @@ test 'comment syntax cannot be used inline', ->
<Person>{#comment inline}</Person> <Person>{#comment inline}</Person>
''' '''
test 'string within CSX is ignored', -> test 'string within JSX is ignored', ->
eqJS ''' eqJS '''
<Person> "i am not a string" 'nor am i' </Person> <Person> "i am not a string" 'nor am i' </Person>
''', ''' ''', '''
<Person> "i am not a string" 'nor am i' </Person>; <Person> "i am not a string" 'nor am i' </Person>;
''' '''
test 'special chars within CSX are ignored', -> test 'special chars within JSX are ignored', ->
eqJS """ eqJS """
<Person> a,/';][' a\''@$%^&˚¬˜˚å¬˚*()*&^%$>> '"''"'''\'\'m' i </Person> <Person> a,/';][' a\''@$%^&˚¬˜˚å¬˚*()*&^%$>> '"''"'''\'\'m' i </Person>
""", """ """, """
<Person> a,/';][' a''@$%^&˚¬˜˚å¬˚*()*&^%$>> '"''"'''''m' i </Person>; <Person> a,/';][' a''@$%^&˚¬˜˚å¬˚*()*&^%$>> '"''"'''''m' i </Person>;
""" """
test 'html entities (name, decimal, hex) within CSX', -> test 'html entities (name, decimal, hex) within JSX', ->
eqJS ''' eqJS '''
<Person> &&&&euro; &#8364; &#x20AC;;; </Person> <Person> &&&&euro; &#8364; &#x20AC;;; </Person>
''', ''' ''', '''
@ -619,55 +619,55 @@ test 'closing tags must be closed', ->
<a></a <a></a
''' '''
# Tests for allowing less than operator without spaces when ther is no CSX # Tests for allowing less than operator without spaces when ther is no JSX
test 'unspaced less than without CSX: identifier', -> test 'unspaced less than without JSX: identifier', ->
a = 3 a = 3
div = 5 div = 5
ok a<div ok a<div
test 'unspaced less than without CSX: number', -> test 'unspaced less than without JSX: number', ->
div = 5 div = 5
ok 3<div ok 3<div
test 'unspaced less than without CSX: paren', -> test 'unspaced less than without JSX: paren', ->
div = 5 div = 5
ok (3)<div ok (3)<div
test 'unspaced less than without CSX: index', -> test 'unspaced less than without JSX: index', ->
div = 5 div = 5
a = [3] a = [3]
ok a[0]<div ok a[0]<div
test 'tag inside CSX works following: identifier', -> test 'tag inside JSX works following: identifier', ->
eqJS ''' eqJS '''
<span>a<div /></span> <span>a<div /></span>
''', ''' ''', '''
<span>a<div /></span>; <span>a<div /></span>;
''' '''
test 'tag inside CSX works following: number', -> test 'tag inside JSX works following: number', ->
eqJS ''' eqJS '''
<span>3<div /></span> <span>3<div /></span>
''', ''' ''', '''
<span>3<div /></span>; <span>3<div /></span>;
''' '''
test 'tag inside CSX works following: paren', -> test 'tag inside JSX works following: paren', ->
eqJS ''' eqJS '''
<span>(3)<div /></span> <span>(3)<div /></span>
''', ''' ''', '''
<span>(3)<div /></span>; <span>(3)<div /></span>;
''' '''
test 'tag inside CSX works following: square bracket', -> test 'tag inside JSX works following: square bracket', ->
eqJS ''' eqJS '''
<span>]<div /></span> <span>]<div /></span>
''', ''' ''', '''
<span>]<div /></span>; <span>]<div /></span>;
''' '''
test 'unspaced less than inside CSX works but is not encouraged', -> test 'unspaced less than inside JSX works but is not encouraged', ->
eqJS ''' eqJS '''
a = 3 a = 3
div = 5 div = 5
@ -682,7 +682,7 @@ test 'unspaced less than inside CSX works but is not encouraged', ->
html = <span>{a < div}</span>; html = <span>{a < div}</span>;
''' '''
test 'unspaced less than before CSX works but is not encouraged', -> test 'unspaced less than before JSX works but is not encouraged', ->
eqJS ''' eqJS '''
div = 5 div = 5
res = 2<div res = 2<div
@ -697,7 +697,7 @@ test 'unspaced less than before CSX works but is not encouraged', ->
html = <span />; html = <span />;
''' '''
test 'unspaced less than after CSX works but is not encouraged', -> test 'unspaced less than after JSX works but is not encouraged', ->
eqJS ''' eqJS '''
div = 5 div = 5
html = <span /> html = <span />
@ -712,7 +712,7 @@ test 'unspaced less than after CSX works but is not encouraged', ->
res = 2 < div; res = 2 < div;
''' '''
test '#4686: comments inside interpolations that also contain CSX tags', -> test '#4686: comments inside interpolations that also contain JSX tags', ->
eqJS ''' eqJS '''
<div> <div>
{ {
@ -727,7 +727,7 @@ test '#4686: comments inside interpolations that also contain CSX tags', ->
</div>; </div>;
''' '''
test '#4686: comments inside interpolations that also contain CSX attributes', -> test '#4686: comments inside interpolations that also contain JSX attributes', ->
eqJS ''' eqJS '''
<div> <div>
<div anAttr={ <div anAttr={
@ -742,7 +742,7 @@ test '#4686: comments inside interpolations that also contain CSX attributes', -
</div>; </div>;
''' '''
test '#5086: comments inside CSX tags but outside interpolations', -> test '#5086: comments inside JSX tags but outside interpolations', ->
eqJS ''' eqJS '''
<div> <div>
<div ###comment### attribute={value} /> <div ###comment### attribute={value} />
@ -753,7 +753,7 @@ test '#5086: comments inside CSX tags but outside interpolations', ->
</div>; </div>;
''' '''
test '#5086: comments inside CSX attributes but outside interpolations', -> test '#5086: comments inside JSX attributes but outside interpolations', ->
eqJS ''' eqJS '''
<div> <div>
<div attribute={###attr comment### value} /> <div attribute={###attr comment### value} />
@ -764,7 +764,7 @@ test '#5086: comments inside CSX attributes but outside interpolations', ->
</div>; </div>;
''' '''
test '#5086: comments inside nested CSX tags and attributes but outside interpolations', -> test '#5086: comments inside nested JSX tags and attributes but outside interpolations', ->
eqJS ''' eqJS '''
<div> <div>
<div> <div>