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() {
return new IdentifierLiteral($1);
}),
o('CSX_TAG',
o('JSX_TAG',
function() {
var ref,
ref1,
ref2,
ref3;
return new CSXTag($1.toString(),
return new JSXTag($1.toString(),
{
tagNameLocationData: $1.tagNameToken[2],
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
// 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.
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,
slice = [].slice;
@ -58,8 +58,8 @@
this.seenExport = false; // Used to recognize `EXPORT FROM? AS?` tokens.
this.importSpecifierList = false; // Used to identify when in an `IMPORT {...} 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.csxObjAttribute = {}; // Used to detect if CSX attributes is wrapped in {} (<div {props...} />).
this.jsxDepth = 0; // Used to optimize JSX checks, how deep in JSX we are.
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.chunkColumn = opts.column || 0; // The start column of 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.
i = 0;
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.
[this.chunkLine, this.chunkColumn, this.chunkOffset] = this.getLineAndColumnFromChunk(consumed);
i += consumed;
@ -119,9 +119,9 @@
// 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, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regExSuper, regex, sup, tag, tagToken, tokenData;
inCSXTag = this.atCSXTag();
regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER;
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;
inJSXTag = this.atJSXTag();
regex = inJSXTag ? JSX_ATTRIBUTE : IDENTIFIER;
if (!(match = regex.exec(this.chunk))) {
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]];
}
if (colon) {
colonOffset = input.lastIndexOf(inCSXTag ? '=' : ':');
colonOffset = input.lastIndexOf(inJSXTag ? '=' : ':');
colonToken = this.token(':', ':', {
offset: colonOffset,
length: colon.length
});
if (inCSXTag) { // used by rewriter
colonToken.csxColon = true;
if (inJSXTag) { // used by rewriter
colonToken.jsxColon = true;
}
}
if (inCSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
if (inJSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
this.token(',', ',', {
length: 0,
origin: tagToken
@ -386,7 +386,7 @@
delimiter: quote
});
});
if (this.atCSXTag()) {
if (this.atJSXTag()) {
this.token(',', ',', {
length: 0,
origin: this.prev
@ -771,16 +771,15 @@
return this;
}
// CSX is like JSX but for CoffeeScript.
csxToken() {
var afterTag, csxTag, end, endToken, firstChar, fullId, fullTagName, id, input, j, len, match, offset, openingTagToken, prev, prevChar, properties, property, ref, tagToken, token, tokens;
jsxToken() {
var afterTag, end, endToken, firstChar, fullId, fullTagName, id, input, j, jsxTag, len, match, offset, openingTagToken, prev, prevChar, properties, property, ref, tagToken, token, tokens;
firstChar = this.chunk[0];
// Check the previous token to detect if attribute is spread.
prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : '';
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`).
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;
}
[input, id] = match;
@ -790,7 +789,7 @@
} else {
properties = [];
}
tagToken = this.token('CSX_TAG', id, {
tagToken = this.token('JSX_TAG', id, {
length: id.length + 1,
data: {
openingBracketToken: this.makeToken('<', '<'),
@ -819,9 +818,9 @@
name: id,
properties
});
this.csxDepth++;
this.jsxDepth++;
return fullId.length + 1;
} else if (csxTag = this.atCSXTag()) {
} else if (jsxTag = this.atJSXTag()) {
if (this.chunk.slice(0, 2) === '/>') { // Self-closing tag.
this.pair('/>');
this.token(']', ']', {
@ -838,15 +837,15 @@
})
}
});
this.csxDepth--;
this.jsxDepth--;
return 2;
} else if (firstChar === '{') {
if (prevChar === ':') {
token = this.token('(', '(');
this.csxObjAttribute[this.csxDepth] = false;
this.jsxObjAttribute[this.jsxDepth] = false;
} else {
token = this.token('{', '{');
this.csxObjAttribute[this.csxDepth] = true;
this.jsxObjAttribute[this.jsxDepth] = true;
}
this.ends.push({
tag: '}',
@ -870,7 +869,7 @@
({
tokens,
index: end
} = this.matchWithInterpolations(INSIDE_CSX, '>', '</', CSX_INTERPOLATION));
} = this.matchWithInterpolations(INSIDE_JSX, '>', '</', JSX_INTERPOLATION));
this.mergeInterpolationTokens(tokens, {
endOffset: end
}, (value) => {
@ -878,10 +877,10 @@
delimiter: '>'
});
});
match = CSX_IDENTIFIER.exec(this.chunk.slice(end)) || CSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
if (!match || match[1] !== `${csxTag.name}${((function() {
match = JSX_IDENTIFIER.exec(this.chunk.slice(end)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
if (!match || match[1] !== `${jsxTag.name}${((function() {
var k, len1, ref1, results;
ref1 = csxTag.properties;
ref1 = jsxTag.properties;
results = [];
for (k = 0, len1 = ref1.length; k < len1; k++) {
property = ref1[k];
@ -889,7 +888,7 @@
}
return results;
})()).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;
afterTag = end + fullTagName.length;
@ -922,17 +921,17 @@
});
// make the closing tag location data more easily accessible to the grammar
addTokenData(openingTagToken, endToken.data);
this.csxDepth--;
this.jsxDepth--;
return afterTag + 1;
} else {
return 0;
}
} else if (this.atCSXTag(1)) {
} else if (this.atJSXTag(1)) {
if (firstChar === '}') {
this.pair(firstChar);
if (this.csxObjAttribute[this.csxDepth]) {
if (this.jsxObjAttribute[this.jsxDepth]) {
this.token('}', '}');
this.csxObjAttribute[this.csxDepth] = false;
this.jsxObjAttribute[this.jsxDepth] = false;
} else {
this.token(')', ')');
}
@ -946,9 +945,9 @@
}
}
atCSXTag(depth = 0) {
atJSXTag(depth = 0) {
var i, last, ref;
if (this.csxDepth === 0) {
if (this.jsxDepth === 0) {
return false;
}
i = this.ends.length - 1;
@ -1132,9 +1131,9 @@
// `#{` if interpolations are desired).
// - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
// `"""` and `///`.
// - `closingDelimiter` is different from `delimiter` only in CSX
// - `interpolators` matches the start of an interpolation, for CSX it's both
// `{` and `<` (i.e. nested CSX tag)
// - `closingDelimiter` is different from `delimiter` only in JSX
// - `interpolators` matches the start of an interpolation, for JSX it's both
// `{` and `<` (i.e. nested JSX tag)
// This method allows us to have strings within interpolations within strings,
// ad infinitum.
@ -1600,13 +1599,13 @@
// Token matching regexes.
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.
// 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?
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; // binary
@ -1645,11 +1644,11 @@
HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/;
INSIDE_CSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping.
// Maybe CSX tag (`<` not allowed even if bare).
INSIDE_JSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping.
// Maybe JSX tag (`<` not allowed even if bare).
CSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation.
// CSX opening tag.
JSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation.
// JSX opening tag.
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),
// 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.
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,
splice = [].splice,
slice1 = [].slice;
@ -796,7 +796,7 @@
len = this.expressions.length;
ref1 = this.expressions, [lastExp] = slice1.call(ref1, -1);
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.
if (lastExp && lastExp instanceof Parens && lastExp.body.expressions.length > 1) {
({
@ -805,7 +805,7 @@
[penult, last] = slice1.call(expressions, -2);
penult = penult.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');
}
}
@ -1335,19 +1335,19 @@
}
compileNode(o) {
if (this.csx) {
if (this.jsx) {
return [this.makeCode(this.unquote(true, true))];
}
return super.compileNode(o);
}
unquote(doubleQuote = false, csx = false) {
unquote(doubleQuote = false, jsx = false) {
var unquoted;
unquoted = this.value.slice(1, -1);
if (doubleQuote) {
unquoted = unquoted.replace(/\\"/g, '"');
}
if (csx) {
if (jsx) {
unquoted = unquoted.replace(/\\n/g, '\n');
}
return unquoted;
@ -1458,7 +1458,7 @@
}
astType() {
if (this.csx) {
if (this.jsx) {
return 'JSXIdentifier';
} else {
return 'Identifier';
@ -1479,32 +1479,10 @@
}).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() {
class PropertyName extends Literal {
astType() {
if (this.csx) {
if (this.jsx) {
return 'JSXIdentifier';
} else {
return 'Identifier';
@ -1883,8 +1861,8 @@
return !this.properties.length && this.base.isStatement(o);
}
isCSXTag() {
return this.base instanceof CSXTag;
isJSXTag() {
return this.base instanceof JSXTag;
}
assigns(name) {
@ -2061,7 +2039,7 @@
}
astType() {
if (this.isCSXTag()) {
if (this.isJSXTag()) {
return 'JSXMemberExpression';
} else {
return 'MemberExpression';
@ -2074,8 +2052,8 @@
astProperties(o) {
var computed, property, ref1, ref2;
ref1 = this.properties, [property] = slice1.call(ref1, -1);
if (this.isCSXTag()) {
property.name.csx = true;
if (this.isJSXTag()) {
property.name.jsx = true;
}
computed = property instanceof Index || !(((ref2 = property.name) != null ? ref2.unwrap() : void 0) instanceof PropertyName);
return {
@ -2088,7 +2066,7 @@
}
astLocationData() {
if (!this.isCSXTag()) {
if (!this.isJSXTag()) {
return super.astLocationData();
}
// don't include leading < of JSX tag in location data
@ -2223,20 +2201,38 @@
};
//### CSX
exports.CSXIdentifier = CSXIdentifier = class CSXIdentifier extends IdentifierLiteral {
//### JSX
exports.JSXIdentifier = JSXIdentifier = class JSXIdentifier extends IdentifierLiteral {
astType() {
return 'JSXIdentifier';
}
};
exports.CSXExpressionContainer = CSXExpressionContainer = (function() {
class CSXExpressionContainer extends Base {
exports.JSXTag = JSXTag = class JSXTag extends JSXIdentifier {
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} = {}) {
super();
this.expression = expression1;
this.expression.csxAttribute = true;
this.expression.jsxAttribute = true;
this.locationData = locationData != null ? locationData : this.expression.locationData;
}
@ -2244,10 +2240,6 @@
return this.expression.compileNode(o);
}
astType() {
return 'JSXExpressionContainer';
}
astProperties() {
return {
expression: this.expression.ast()
@ -2256,30 +2248,21 @@
};
CSXExpressionContainer.prototype.children = ['expression'];
JSXExpressionContainer.prototype.children = ['expression'];
return CSXExpressionContainer;
return JSXExpressionContainer;
}).call(this);
exports.CSXEmptyExpression = CSXEmptyExpression = class CSXEmptyExpression extends Base {
astType() {
return 'JSXEmptyExpression';
}
exports.JSXEmptyExpression = JSXEmptyExpression = class JSXEmptyExpression extends Base {};
};
exports.CSXText = CSXText = class CSXText extends Base {
exports.JSXText = JSXText = class JSXText extends Base {
constructor(stringLiteral) {
super();
this.value = stringLiteral.unquote(true, true);
this.locationData = stringLiteral.locationData;
}
astType() {
return 'JSXText';
}
astProperties() {
return {
value: this.value,
@ -2291,8 +2274,8 @@
};
exports.CSXAttribute = CSXAttribute = (function() {
class CSXAttribute extends Base {
exports.JSXAttribute = JSXAttribute = (function() {
class JSXAttribute extends Base {
constructor({
name: name1,
value
@ -2300,7 +2283,7 @@
var ref1;
super();
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) {
ref1.comments = value.comments;
}
@ -2316,10 +2299,6 @@
return compiledName.concat(this.makeCode('='), val);
}
astType() {
return 'JSXAttribute';
}
astProperties() {
var ref1, ref2;
return {
@ -2330,14 +2309,14 @@
};
CSXAttribute.prototype.children = ['name', 'value'];
JSXAttribute.prototype.children = ['name', 'value'];
return CSXAttribute;
return JSXAttribute;
}).call(this);
exports.CSXAttributes = CSXAttributes = (function() {
class CSXAttributes extends Base {
exports.JSXAttributes = JSXAttributes = (function() {
class JSXAttributes extends Base {
constructor(arr) {
var attribute, base, j, k, len1, len2, object, property, ref1, ref2, value, variable;
super();
@ -2349,15 +2328,15 @@
({base} = object);
if (base instanceof IdentifierLiteral) {
// 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;
this.attributes.push(attribute);
} else if (!base.generated) {
// object spread attribute eg {...props}
attribute = base.properties[0];
attribute.csx = true;
attribute.jsx = true;
attribute.locationData = base.locationData;
this.attributes.push(attribute);
} else {
@ -2366,8 +2345,8 @@
for (k = 0, len2 = ref2.length; k < len2; k++) {
property = ref2[k];
({variable, value} = property);
attribute = new CSXAttribute({
name: new CSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom(variable.base),
attribute = new JSXAttribute({
name: new JSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom(variable.base),
value
});
attribute.locationData = property.locationData;
@ -2386,7 +2365,7 @@
} = object);
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)))) {
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);
// Node for a CSX element
exports.CSXElement = CSXElement = (function() {
class CSXElement extends Base {
// Node for a JSX element
exports.JSXElement = JSXElement = (function() {
class JSXElement extends Base {
constructor({
tagName: tagName1,
attributes,
@ -2438,7 +2417,7 @@
compileNode(o) {
var fragments, ref1, tag;
if ((ref1 = this.content) != null) {
ref1.base.csx = true;
ref1.base.jsx = true;
}
fragments = [this.makeCode('<')];
fragments.push(...(tag = this.tagName.compileToFragments(o, LEVEL_ACCESS)));
@ -2541,7 +2520,7 @@
children = (function() {
var j, len1, ref1, results;
if (content instanceof StringLiteral) {
return [new CSXText(content)]; // StringWithInterpolations
return [new JSXText(content)]; // StringWithInterpolations
} else {
ref1 = this.content.unwrapAll().extractElements(o, {
includeInterpolationWrappers: true
@ -2550,17 +2529,17 @@
for (j = 0, len1 = ref1.length; j < len1; j++) {
element = ref1[j];
if (element instanceof StringLiteral) {
results.push(new CSXText(element)); // Interpolation
results.push(new JSXText(element)); // Interpolation
} else {
({expression} = element);
if (expression == null) {
results.push(new CSXEmptyExpression().withLocationDataFrom(element));
results.push(new JSXEmptyExpression().withLocationDataFrom(element));
} else {
unwrapped = expression.unwrapAll();
if (unwrapped instanceof CSXElement) {
if (unwrapped instanceof JSXElement) {
results.push(unwrapped);
} else {
results.push(new CSXExpressionContainer(unwrapped, {
results.push(new JSXExpressionContainer(unwrapped, {
locationData: element.locationData
}));
}
@ -2573,7 +2552,7 @@
results = [];
for (j = 0, len1 = children.length; j < len1; 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));
}
}
@ -2596,9 +2575,9 @@
};
CSXElement.prototype.children = ['tagName', 'attributes', 'content'];
JSXElement.prototype.children = ['tagName', 'attributes', 'content'];
return CSXElement;
return JSXElement;
}).call(this);
@ -2619,10 +2598,10 @@
if (this.variable instanceof Value && this.variable.isNotCallable()) {
this.variable.error("literal is not a function");
}
if (this.variable.base instanceof CSXTag) {
return new CSXElement({
if (this.variable.base instanceof JSXTag) {
return new JSXElement({
tagName: this.variable,
attributes: new CSXAttributes(this.args[0].base),
attributes: new JSXAttributes(this.args[0].base),
content: this.args[1]
});
}
@ -5836,7 +5815,7 @@
compileNode(o) {
var compiledSplat;
compiledSplat = [this.makeCode('...'), ...this.name.compileToFragments(o, LEVEL_OP)];
if (!this.csx) {
if (!this.jsx) {
return compiledSplat;
}
return [this.makeCode('{'), ...compiledSplat, this.makeCode('}')];
@ -5847,7 +5826,7 @@
}
astType() {
if (this.csx) {
if (this.jsx) {
return 'JSXSpreadAttribute';
} else if (this.lhs) {
return 'RestElement';
@ -6781,13 +6760,13 @@
shouldWrapComment = (ref1 = expr.comments) != null ? ref1.some(function(comment) {
return comment.here && !comment.unshift && !comment.newLine;
}) : void 0;
if (expr instanceof Value && expr.isAtomic() && !this.csxAttribute && !shouldWrapComment) {
if (expr instanceof Value && expr.isAtomic() && !this.jsxAttribute && !shouldWrapComment) {
expr.front = this.front;
return expr.compileToFragments(o);
}
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);
if (this.csxAttribute) {
if (this.jsxAttribute) {
return this.wrapInBraces(fragments);
}
if (bare) {
@ -6863,7 +6842,7 @@
}
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.comments = unwrapped.comments;
if (node.comments) {
@ -6902,21 +6881,21 @@
if (this.comments == null) {
this.comments = (ref1 = this.startQuote) != null ? ref1.comments : void 0;
}
if (this.csxAttribute) {
if (this.jsxAttribute) {
wrapped = new Parens(new StringWithInterpolations(this.body));
wrapped.csxAttribute = true;
wrapped.jsxAttribute = true;
return wrapped.compileNode(o);
}
elements = this.extractElements(o);
fragments = [];
if (!this.csx) {
if (!this.jsx) {
fragments.push(this.makeCode('`'));
}
for (j = 0, len1 = elements.length; j < len1; j++) {
element = elements[j];
if (element instanceof StringLiteral) {
element.value = element.unquote(true, this.csx);
if (!this.csx) {
element.value = element.unquote(true, this.jsx);
if (!this.jsx) {
// Backticks and `${` inside template literals must be escaped.
element.value = element.value.replace(/(\\*)(`|\$\{)/g, function(match, backslashes, toBeEscaped) {
if (backslashes.length % 2 === 0) {
@ -6928,7 +6907,7 @@
}
fragments.push(...element.compileToFragments(o));
} else {
if (!this.csx) {
if (!this.jsx) {
fragments.push(this.makeCode('$'));
}
code = element.compileToFragments(o, LEVEL_PAREN);
@ -6951,7 +6930,7 @@
fragments.push(...code);
}
}
if (!this.csx) {
if (!this.jsx) {
fragments.push(this.makeCode('`'));
}
return fragments;
@ -6960,7 +6939,7 @@
isNestedTag(element) {
var call;
call = typeof element.unwrapAll === "function" ? element.unwrapAll() : void 0;
return this.csx && call instanceof CSXElement;
return this.jsx && call instanceof JSXElement;
}
astType() {

File diff suppressed because one or more lines are too long

View File

@ -89,7 +89,7 @@
this.addImplicitBracesAndParens();
this.rescueStowawayComments();
this.addLocationDataToGeneratedTokens();
this.enforceValidCSXAttributes();
this.enforceValidJSXAttributes();
this.fixOutdentLocationData();
this.exposeTokenDataToGrammar();
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.
enforceValidCSXAttributes() {
// Make sure only strings and wrapped expressions are used in JSX attributes.
enforceValidJSXAttributes() {
return this.scanTokens(function(token, i, tokens) {
var next, ref;
if (token.csxColon) {
if (token.jsxColon) {
next = tokens[i + 1];
if ((ref = next[0]) !== 'STRING_START' && ref !== 'STRING' && ref !== '(') {
throwSyntaxError('expected wrapped or quoted JSX attribute', next[2]);
@ -993,7 +993,7 @@
IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
// 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 = ['+', '-'];

View File

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

View File

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

View File

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

View File

@ -55,7 +55,7 @@ exports.Rewriter = class Rewriter
@addImplicitBracesAndParens()
@rescueStowawayComments()
@addLocationDataToGeneratedTokens()
@enforceValidCSXAttributes()
@enforceValidJSXAttributes()
@fixOutdentLocationData()
@exposeTokenDataToGrammar()
if process?.env?.DEBUG_REWRITTEN_TOKEN_STREAM
@ -409,10 +409,10 @@ exports.Rewriter = class Rewriter
endImplicitObject i + offset
return forward(1)
# Make sure only strings and wrapped expressions are used in CSX attributes.
enforceValidCSXAttributes: ->
# Make sure only strings and wrapped expressions are used in JSX attributes.
enforceValidJSXAttributes: ->
@scanTokens (token, i, tokens) ->
if token.csxColon
if token.jsxColon
next = tokens[i + 1]
if next[0] not in ['STRING_START', 'STRING', '(']
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.
IMPLICIT_CALL = [
'IDENTIFIER', 'CSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN'
'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'

View File

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

View File

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

View File

@ -725,7 +725,7 @@ test "Block comment in an interpolated string", ->
eqJS '"a#{### Comment ###}b"', '`a${/* Comment */""}b`;'
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>

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 '''
<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>
^^^^
'''
test "CSX error: bare expressions not allowed", ->
test "JSX error: bare expressions not allowed", ->
assertErrorFormat '''
<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 '''
<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 '''
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 '''
<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} />
^^^^^^^
'''
assertErrorFormatAst '''
<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}} />
^^^^^^^
'''
assertErrorFormatAst '''
<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}"} />
^^^^^^^^
'''
assertErrorFormatAst '''
<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... />
^^^
'''
assertErrorFormatAst '''
<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()} />
^^^^^^^^^^^^^^^^^^^^^^^^
'''
assertErrorFormatAst '''
<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} />
^^^^^^^^^^^^^^^^
'''
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 '''
render = -> (
<Row>a</Row>

View File

@ -184,7 +184,7 @@ test 'escaped CoffeeScript attribute over multiple lines', ->
<Person name={test() ? 'yes' : 'no'} />;
'''
test 'multiple line escaped CoffeeScript with nested CSX', ->
test 'multiple line escaped CoffeeScript with nested JSX', ->
eqJS '''
<Person name={
if test()
@ -222,7 +222,7 @@ test 'multiple line escaped CoffeeScript with nested CSX', ->
</Person>;
'''
test 'nested CSX within an attribute, with object attr value', ->
test 'nested JSX within an attribute, with object attr value', ->
eqJS '''
<Company>
<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 '''
<Person
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} />;
'''
# TODO: fix partially indented CSX
# TODO: fix partially indented JSX
# test 'multiline elements', ->
# eqJS '''
# <div something={
@ -378,7 +378,7 @@ test 'heregex', ->
<Person />;
'''
test 'comment within CSX is not treated as comment', ->
test 'comment within JSX is not treated as comment', ->
eqJS '''
<Person>
# i am not a comment
@ -389,7 +389,7 @@ test 'comment within CSX is not treated as comment', ->
</Person>;
'''
test 'comment at start of CSX escape', ->
test 'comment at start of JSX escape', ->
eqJS '''
<Person>
{# i am a comment
@ -403,7 +403,7 @@ test 'comment at start of CSX escape', ->
</Person>;
'''
test 'comment at end of CSX escape', ->
test 'comment at end of JSX escape', ->
eqJS '''
<Person>
{"i am a string"
@ -418,7 +418,7 @@ test 'comment at end of CSX escape', ->
</Person>;
'''
test 'CSX comment cannot be used inside interpolation', ->
test 'JSX comment cannot be used inside interpolation', ->
throws -> CoffeeScript.compile '''
<Person>
{# i am a comment}
@ -430,21 +430,21 @@ test 'comment syntax cannot be used inline', ->
<Person>{#comment inline}</Person>
'''
test 'string within CSX is ignored', ->
test 'string within JSX is ignored', ->
eqJS '''
<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 """
<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 '''
<Person> &&&&euro; &#8364; &#x20AC;;; </Person>
''', '''
@ -619,55 +619,55 @@ test 'closing tags must be closed', ->
<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
div = 5
ok a<div
test 'unspaced less than without CSX: number', ->
test 'unspaced less than without JSX: number', ->
div = 5
ok 3<div
test 'unspaced less than without CSX: paren', ->
test 'unspaced less than without JSX: paren', ->
div = 5
ok (3)<div
test 'unspaced less than without CSX: index', ->
test 'unspaced less than without JSX: index', ->
div = 5
a = [3]
ok a[0]<div
test 'tag inside CSX works following: identifier', ->
test 'tag inside JSX works following: identifier', ->
eqJS '''
<span>a<div /></span>
''', '''
<span>a<div /></span>;
'''
test 'tag inside CSX works following: number', ->
test 'tag inside JSX works following: number', ->
eqJS '''
<span>3<div /></span>
''', '''
<span>3<div /></span>;
'''
test 'tag inside CSX works following: paren', ->
test 'tag inside JSX works following: paren', ->
eqJS '''
<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 '''
<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 '''
a = 3
div = 5
@ -682,7 +682,7 @@ test 'unspaced less than inside CSX works but is not encouraged', ->
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 '''
div = 5
res = 2<div
@ -697,7 +697,7 @@ test 'unspaced less than before CSX works but is not encouraged', ->
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 '''
div = 5
html = <span />
@ -712,7 +712,7 @@ test 'unspaced less than after CSX works but is not encouraged', ->
res = 2 < div;
'''
test '#4686: comments inside interpolations that also contain CSX tags', ->
test '#4686: comments inside interpolations that also contain JSX tags', ->
eqJS '''
<div>
{
@ -727,7 +727,7 @@ test '#4686: comments inside interpolations that also contain CSX tags', ->
</div>;
'''
test '#4686: comments inside interpolations that also contain CSX attributes', ->
test '#4686: comments inside interpolations that also contain JSX attributes', ->
eqJS '''
<div>
<div anAttr={
@ -742,7 +742,7 @@ test '#4686: comments inside interpolations that also contain CSX attributes', -
</div>;
'''
test '#5086: comments inside CSX tags but outside interpolations', ->
test '#5086: comments inside JSX tags but outside interpolations', ->
eqJS '''
<div>
<div ###comment### attribute={value} />
@ -753,7 +753,7 @@ test '#5086: comments inside CSX tags but outside interpolations', ->
</div>;
'''
test '#5086: comments inside CSX attributes but outside interpolations', ->
test '#5086: comments inside JSX attributes but outside interpolations', ->
eqJS '''
<div>
<div attribute={###attr comment### value} />
@ -764,7 +764,7 @@ test '#5086: comments inside CSX attributes but outside interpolations', ->
</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 '''
<div>
<div>