Skip to content

Commit 6ed047a

Browse files
authored
Merge pull request #108 from FredericEspiau/fix/decorator-removal
fix: prevent removing decorators on classes
2 parents 50b9261 + c1303ce commit 6ed047a

File tree

2 files changed

+74
-7
lines changed

2 files changed

+74
-7
lines changed

lib/util/edit.test.ts

+47
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,53 @@ function a2() {}`,
289289
});
290290

291291
describe('class declaration', () => {
292+
it('should not remove decorators when exports are deleted', () => {
293+
const fileService = new MemoryFileService();
294+
295+
fileService.set('/app/main.ts', ``);
296+
fileService.set(
297+
'/app/a.ts',
298+
`@myDecorator
299+
export class A {}`,
300+
);
301+
fileService.set(
302+
'/app/b.ts',
303+
`@myDecorator
304+
export default class B {}`,
305+
);
306+
fileService.set(
307+
'/app/c.ts',
308+
`@firstDecorator
309+
@secondDecorator(() => [WithArgument])
310+
export default class C {}`,
311+
);
312+
313+
edit({
314+
fileService,
315+
recursive,
316+
entrypoints: ['/app/main.ts'],
317+
});
318+
319+
assert.equal(
320+
fileService.get('/app/a.ts'),
321+
`@myDecorator
322+
class A {}`,
323+
);
324+
325+
assert.equal(
326+
fileService.get('/app/b.ts'),
327+
`@myDecorator
328+
class B {}`,
329+
);
330+
331+
assert.equal(
332+
fileService.get('/app/c.ts'),
333+
`@firstDecorator
334+
@secondDecorator(() => [WithArgument])
335+
class C {}`,
336+
);
337+
});
338+
292339
it('should not remove export for class if its used in some other file', () => {
293340
const fileService = new MemoryFileService();
294341

lib/util/parseFile.ts

+27-7
Original file line numberDiff line numberDiff line change
@@ -83,27 +83,47 @@ const getChange = (
8383
};
8484
}
8585

86-
// we want to correctly remove 'default' when its a default export so we get the syntaxList node instead of the exportKeyword node
87-
// note: the first syntaxList node should contain the export keyword
86+
/**
87+
* The syntax list contains the keywords that can be found before the actual declaration.
88+
* We want to remove everything that is not a decorator.
89+
*/
8890
const syntaxListIndex = node
8991
.getChildren()
9092
.findIndex((n) => n.kind === ts.SyntaxKind.SyntaxList);
9193

9294
const syntaxList = node.getChildren()[syntaxListIndex];
95+
96+
if (!syntaxList) {
97+
throw new Error('syntaxList missing');
98+
}
99+
100+
const firstKeywordToDeleteIndex = syntaxList
101+
.getChildren()
102+
.findIndex((n) => n.kind !== ts.SyntaxKind.Decorator);
103+
104+
const firstKeywordToDelete =
105+
syntaxList.getChildren()[firstKeywordToDeleteIndex];
106+
107+
if (!firstKeywordToDelete) {
108+
throw new Error(
109+
'Unexpected syntax list when looking for keywords after decorators',
110+
);
111+
}
112+
93113
const nextSibling = node.getChildren()[syntaxListIndex + 1];
94114

95-
if (!syntaxList || !nextSibling) {
96-
throw new Error('Unexpected syntax');
115+
if (!nextSibling) {
116+
throw new Error('No sibling after syntax list');
97117
}
98118

99119
return {
100120
code: node
101121
.getSourceFile()
102122
.getFullText()
103-
.slice(syntaxList.getStart(), nextSibling.getStart()),
123+
.slice(firstKeywordToDelete.getStart(), nextSibling.getStart()),
104124
span: {
105-
start: syntaxList.getStart(),
106-
length: nextSibling.getStart() - syntaxList.getStart(),
125+
start: firstKeywordToDelete.getStart(),
126+
length: nextSibling.getStart() - firstKeywordToDelete.getStart(),
107127
},
108128
};
109129
};

0 commit comments

Comments
 (0)