ember-template-recast
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -0,1 +1,13 @@ | ||
## v3.1.0 (2019-07-11) | ||
#### :rocket: Enhancement | ||
* [#74](https://github.com/ember-template-lint/ember-template-recast/pull/74) Add recursive printing support ([@rwjblue](https://github.com/rwjblue)) | ||
#### :house: Internal | ||
* [#73](https://github.com/ember-template-lint/ember-template-recast/pull/73) Split tests out into multiple files. ([@rwjblue](https://github.com/rwjblue)) | ||
#### Committers: 2 | ||
- Robert Jackson ([@rwjblue](https://github.com/rwjblue)) | ||
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | ||
## v3.0.0 (2019-07-09) | ||
@@ -2,0 +14,0 @@ |
{ | ||
"name": "ember-template-recast", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Non-destructive template transformer.", | ||
@@ -39,3 +39,3 @@ "license": "MIT", | ||
"qunit": "^2.9.2", | ||
"release-it": "^12.2.1", | ||
"release-it": "^12.3.2", | ||
"release-it-lerna-changelog": "^1.0.3" | ||
@@ -42,0 +42,0 @@ }, |
const { preprocess, print: _print } = require('@glimmer/syntax'); | ||
const { sortByLoc } = require('./utils'); | ||
const { sortByLoc, compactJoin } = require('./utils'); | ||
@@ -10,2 +10,21 @@ const reLines = /(.*?(?:\r\n?|\n|$))/gm; | ||
const voidElements = new Set([ | ||
'area', | ||
'base', | ||
'br', | ||
'col', | ||
'command', | ||
'embed', | ||
'hr', | ||
'img', | ||
'input', | ||
'keygen', | ||
'link', | ||
'meta', | ||
'param', | ||
'source', | ||
'track', | ||
'wbr', | ||
]); | ||
module.exports = class ParseResult { | ||
@@ -33,2 +52,3 @@ constructor(template) { | ||
let nodeInfo = { | ||
node, | ||
original: JSON.parse(JSON.stringify(node)), | ||
@@ -289,3 +309,287 @@ source: this.sourceForLoc(node.loc), | ||
let nodeInfo = this.nodeInfo.get(ast); | ||
// TODO: this isn't quite right, it forces the whole subtree | ||
// to be reprinted which isn't correct | ||
if (this.nodeInfo.has(ast)) { | ||
return this._printKnownNode(ast); | ||
} else { | ||
return this._printNewNode(ast); | ||
} | ||
} | ||
/* | ||
TODO: this is _nearly_ copied from @glimmer/syntax directly (with some small changes) | ||
We should tweak the printer in @glimmer/syntax to allow a custom print function to be used instead of directly | ||
recursing. This would allow us to use our own custom "reprint" code but still do the "right thing" for new nodes. | ||
*/ | ||
_printNewNode(ast) { | ||
if (!ast) { | ||
return ''; | ||
} | ||
const parseResult = this; | ||
function buildEach(asts) { | ||
return asts.map(node => parseResult.print(node)); | ||
} | ||
function pathParams(ast) { | ||
let path; | ||
switch (ast.type) { | ||
case 'MustacheStatement': | ||
case 'SubExpression': | ||
case 'ElementModifierStatement': | ||
case 'BlockStatement': | ||
path = parseResult.print(ast.path); | ||
break; | ||
case 'PartialStatement': | ||
path = parseResult.print(ast.name); | ||
break; | ||
default: | ||
throw new Error('unreachable'); | ||
} | ||
return compactJoin([path, buildEach(ast.params).join(' '), parseResult.print(ast.hash)], ' '); | ||
} | ||
function blockParams(block) { | ||
const params = block.program.blockParams; | ||
if (params.length) { | ||
return ` as |${params.join(' ')}|`; | ||
} | ||
return null; | ||
} | ||
function openBlock(block) { | ||
return compactJoin([ | ||
'{{', | ||
block.openStrip.open ? '~' : null, | ||
'#', | ||
pathParams(block), | ||
blockParams(block), | ||
block.openStrip.close ? '~' : null, | ||
'}}', | ||
]); | ||
} | ||
function closeBlock(block) { | ||
return compactJoin([ | ||
'{{', | ||
block.closeStrip.open ? '~' : null, | ||
'/', | ||
parseResult.print(block.path), | ||
block.closeStrip.close ? '~' : null, | ||
'}}', | ||
]); | ||
} | ||
const output = []; | ||
switch (ast.type) { | ||
case 'Program': | ||
case 'Block': | ||
case 'Template': | ||
{ | ||
const chainBlock = ast.chained && ast.body[0]; | ||
if (chainBlock) { | ||
chainBlock.chained = true; | ||
} | ||
const body = buildEach(ast.body).join(''); | ||
output.push(body); | ||
} | ||
break; | ||
case 'ElementNode': | ||
output.push('<', ast.tag); | ||
if (ast.attributes.length) { | ||
output.push(' ', buildEach(ast.attributes).join(' ')); | ||
} | ||
if (ast.modifiers.length) { | ||
output.push(' ', buildEach(ast.modifiers).join(' ')); | ||
} | ||
if (ast.comments.length) { | ||
output.push(' ', buildEach(ast.comments).join(' ')); | ||
} | ||
if (ast.blockParams.length) { | ||
output.push(' ', 'as', ' ', `|${ast.blockParams.join(' ')}|`); | ||
} | ||
if (voidElements.has(ast.tag)) { | ||
if (ast.selfClosing) { | ||
output.push(' /'); | ||
} | ||
output.push('>'); | ||
} else if (ast.selfClosing) { | ||
output.push(' />'); | ||
} else { | ||
output.push('>'); | ||
output.push.apply(output, buildEach(ast.children)); | ||
output.push('</', ast.tag, '>'); | ||
} | ||
break; | ||
case 'AttrNode': | ||
if (ast.value.type === 'TextNode') { | ||
if (ast.value.chars !== '') { | ||
output.push(ast.name, '='); | ||
output.push('"', ast.value.chars, '"'); | ||
} else { | ||
output.push(ast.name); | ||
} | ||
} else { | ||
output.push(ast.name, '='); | ||
// ast.value is mustache or concat | ||
output.push(parseResult.print(ast.value)); | ||
} | ||
break; | ||
case 'ConcatStatement': | ||
output.push('"'); | ||
ast.parts.forEach(node => { | ||
if (node.type === 'TextNode') { | ||
output.push(node.chars); | ||
} else { | ||
output.push(parseResult.print(node)); | ||
} | ||
}); | ||
output.push('"'); | ||
break; | ||
case 'TextNode': | ||
output.push(ast.chars); | ||
break; | ||
case 'MustacheStatement': | ||
{ | ||
output.push( | ||
compactJoin([ | ||
ast.escaped ? '{{' : '{{{', | ||
ast.strip.open ? '~' : null, | ||
pathParams(ast), | ||
ast.strip.close ? '~' : null, | ||
ast.escaped ? '}}' : '}}}', | ||
]) | ||
); | ||
} | ||
break; | ||
case 'MustacheCommentStatement': | ||
{ | ||
output.push(compactJoin(['{{!--', ast.value, '--}}'])); | ||
} | ||
break; | ||
case 'ElementModifierStatement': | ||
{ | ||
output.push(compactJoin(['{{', pathParams(ast), '}}'])); | ||
} | ||
break; | ||
case 'PathExpression': | ||
output.push(ast.original); | ||
break; | ||
case 'SubExpression': | ||
{ | ||
output.push('(', pathParams(ast), ')'); | ||
} | ||
break; | ||
case 'BooleanLiteral': | ||
output.push(ast.value ? 'true' : 'false'); | ||
break; | ||
case 'BlockStatement': | ||
{ | ||
const lines = []; | ||
if (ast.chained) { | ||
lines.push( | ||
compactJoin([ | ||
'{{', | ||
ast.inverseStrip.open ? '~' : null, | ||
'else ', | ||
pathParams(ast), | ||
ast.inverseStrip.close ? '~' : null, | ||
'}}', | ||
]) | ||
); | ||
} else { | ||
lines.push(openBlock(ast)); | ||
} | ||
lines.push(parseResult.print(ast.program)); | ||
if (ast.inverse) { | ||
if (!ast.inverse.chained) { | ||
lines.push( | ||
compactJoin([ | ||
'{{', | ||
ast.inverseStrip.open ? '~' : null, | ||
'else', | ||
ast.inverseStrip.close ? '~' : null, | ||
'}}', | ||
]) | ||
); | ||
} | ||
lines.push(parseResult.print(ast.inverse)); | ||
} | ||
if (!ast.chained) { | ||
lines.push(closeBlock(ast)); | ||
} | ||
output.push(lines.join('')); | ||
} | ||
break; | ||
case 'PartialStatement': | ||
{ | ||
output.push(compactJoin(['{{>', pathParams(ast), '}}'])); | ||
} | ||
break; | ||
case 'CommentStatement': | ||
{ | ||
output.push(compactJoin(['<!--', ast.value, '-->'])); | ||
} | ||
break; | ||
case 'StringLiteral': | ||
{ | ||
output.push(`"${ast.value}"`); | ||
} | ||
break; | ||
case 'NumberLiteral': | ||
{ | ||
output.push(String(ast.value)); | ||
} | ||
break; | ||
case 'UndefinedLiteral': | ||
{ | ||
output.push('undefined'); | ||
} | ||
break; | ||
case 'NullLiteral': | ||
{ | ||
output.push('null'); | ||
} | ||
break; | ||
case 'Hash': | ||
{ | ||
output.push( | ||
ast.pairs | ||
.map(pair => { | ||
return parseResult.print(pair); | ||
}) | ||
.join(' ') | ||
); | ||
} | ||
break; | ||
case 'HashPair': | ||
{ | ||
output.push(`${ast.key}=${parseResult.print(ast.value)}`); | ||
} | ||
break; | ||
} | ||
return output.join(''); | ||
} | ||
_printKnownNode(_ast) { | ||
let nodeInfo = this.nodeInfo.get(_ast); | ||
// this ensures that we are operating on the actual node and not a | ||
// proxy (we can get Proxies here when transforms splice body/children) | ||
let ast = nodeInfo.node; | ||
// make a copy of the dirtyFields, so we can easily track | ||
@@ -298,8 +602,2 @@ // unhandled dirtied fields | ||
// TODO: this isn't quite right, it forces the whole subtree | ||
// to be reprinted which isn't correct | ||
if (nodeInfo === undefined) { | ||
return _print(ast, { entityEncoding: 'raw' }); | ||
} | ||
// TODO: splice the original source **excluding** "children" | ||
@@ -517,2 +815,16 @@ // based on dirtyFields | ||
if (dirtyFields.has('type')) { | ||
// we only support going from SubExpression -> MustacheStatement | ||
if (original.type !== 'SubExpression' || ast.type !== 'MustacheStatement') { | ||
throw new Error( | ||
`ember-template-recast only supports updating the 'type' for SubExpression to MustacheStatement (you attempted to change ${original.type} to ${ast.type})` | ||
); | ||
} | ||
openSource = `{{${ast.path.original}`; | ||
endSource = '}}'; | ||
dirtyFields.delete('type'); | ||
} | ||
this._rebuildParamsHash(ast, nodeInfo, dirtyFields); | ||
@@ -529,6 +841,2 @@ | ||
); | ||
if (dirtyFields.size > 0) { | ||
throw new Error(`Unhandled mutations for ${ast.type}: ${Array.from(dirtyFields)}`); | ||
} | ||
} | ||
@@ -560,6 +868,2 @@ break; | ||
output.push(openQuote, partsSource, endQuote); | ||
if (dirtyFields.size > 0) { | ||
throw new Error(`Unhandled mutations for ${ast.type}: ${Array.from(dirtyFields)}`); | ||
} | ||
} | ||
@@ -908,3 +1212,3 @@ break; | ||
throw new Error( | ||
`ember-template-recast does not have the ability to update ${ast.type}. Please open an issue so we can add support.` | ||
`ember-template-recast does not have the ability to update ${original.type}. Please open an issue so we can add support.` | ||
); | ||
@@ -917,3 +1221,3 @@ } | ||
dirtyFields | ||
)}\` on ${ast.type}` | ||
)}\` on ${original.type}` | ||
); | ||
@@ -920,0 +1224,0 @@ } |
@@ -35,4 +35,20 @@ function isSynthetic(node) { | ||
function compact(array) { | ||
const newArray = []; | ||
array.forEach(a => { | ||
if (typeof a !== 'undefined' && a !== null && a !== '') { | ||
newArray.push(a); | ||
} | ||
}); | ||
return newArray; | ||
} | ||
function compactJoin(array, delimeter = '') { | ||
return compact(array).join(delimeter); | ||
} | ||
module.exports = { | ||
sortByLoc, | ||
compact, | ||
compactJoin, | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
60073
1450
2