fix(ngcc): better detection of end of decorator expression (#33192)

for removal of decorator from __decorate calls.

FW-1629 #resolve

PR Close #33192
This commit is contained in:
Alex Rickabaugh 2019-10-02 10:45:47 +03:00 committed by Matias Niemelä
parent 4da2dda647
commit 50710838bf
2 changed files with 38 additions and 3 deletions

View File

@ -99,9 +99,17 @@ export class EsmRenderingFormatter implements RenderingFormatter {
} else { } else {
nodesToRemove.forEach(node => { nodesToRemove.forEach(node => {
// remove any trailing comma // remove any trailing comma
const end = (output.slice(node.getEnd(), node.getEnd() + 1) === ',') ? const nextSibling = getNextSiblingInArray(node, items);
node.getEnd() + 1 : let end: number;
node.getEnd();
if (nextSibling !== null &&
output.slice(nextSibling.getFullStart() - 1, nextSibling.getFullStart()) === ',') {
end = nextSibling.getFullStart() - 1 + nextSibling.getLeadingTriviaWidth();
} else if (output.slice(node.getEnd(), node.getEnd() + 1) === ',') {
end = node.getEnd() + 1;
} else {
end = node.getEnd();
}
output.remove(node.getFullStart(), end); output.remove(node.getFullStart(), end);
}); });
} }
@ -214,3 +222,8 @@ function generateImportString(
const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null; const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null;
return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`; return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`;
} }
function getNextSiblingInArray<T extends ts.Node>(node: T, array: ts.NodeArray<T>): T|null {
const index = array.indexOf(node);
return index !== -1 && array.length > index + 1 ? array[index + 1] : null;
}

View File

@ -288,6 +288,28 @@ A.decorators = [
.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`); .toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
}); });
it('should handle a decorator with a trailing comment', () => {
const text = `
import {Directive} from '@angular/core';
export class A {}
A.decorators = [
{ type: Directive, args: [{ selector: '[a]' }] },
{ type: OtherA }
];
`;
const file = {name: _('/node_modules/test-package/index.js'), contents: text};
const {decorationAnalyses, sourceFile, renderer} = setup([file]);
const output = new MagicString(text);
const compiledClass =
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
const decorator = compiledClass.decorators ![0];
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
// The decorator should have been removed correctly.
expect(output.toString()).toContain('A.decorators = [ { type: OtherA }');
});
it('should delete the decorator (and its container if there are no other decorators left) that was matched in the analysis', it('should delete the decorator (and its container if there are no other decorators left) that was matched in the analysis',
() => { () => {