Skip to content

Commit

Permalink
fix: prevent removing decorators on classes
Browse files Browse the repository at this point in the history
  • Loading branch information
FredericEspiau committed Dec 16, 2024
1 parent 50b9261 commit 02c3421
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 8 deletions.
59 changes: 59 additions & 0 deletions lib/util/edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,65 @@ function a2() {}`,
});

describe('class declaration', () => {
it('should not remove decorators when exports are deleted', () => {
const fileService = new MemoryFileService();

fileService.set('/app/main.ts', ``);
fileService.set(
'/app/a.ts',
`@myDecorator
export class A {}
const a = new A();
console.log(a);`,
);
fileService.set(
'/app/b.ts',
`@myDecorator
export default class B {}
const b = new B();
console.log(b);`,
);
fileService.set(
'/app/c.ts',
`@firstDecorator
@secondDecorator(() => [WithArgument])
export default class C {}
const c = new C();
console.log(c);`,
);

edit({
fileService,
recursive,
entrypoints: ['/app/main.ts'],
});

assert.equal(
fileService.get('/app/a.ts'),
`@myDecorator
class A {}
const a = new A();
console.log(a);`,
);

assert.equal(
fileService.get('/app/b.ts'),
`@myDecorator
class B {}
const b = new B();
console.log(b);`,
);

assert.equal(
fileService.get('/app/c.ts'),
`@firstDecorator
@secondDecorator(() => [WithArgument])
class C {}
const c = new C();
console.log(c);`,
);
});

it('should not remove export for class if its used in some other file', () => {
const fileService = new MemoryFileService();

Expand Down
36 changes: 28 additions & 8 deletions lib/util/parseFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,47 @@ const getChange = (
};
}

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

const syntaxList = node.getChildren()[syntaxListIndex];
const syntaxList = node?.getChildren()[syntaxListIndex];

if (!syntaxList) {
throw new Error('Syntaxlist missing');
}

const firstKeywordToDeleteIndex = syntaxList
.getChildren()
.findIndex((n) => n.kind !== ts.SyntaxKind.Decorator);

const firstKeywordToDelete =
syntaxList.getChildren()[firstKeywordToDeleteIndex];

if (!firstKeywordToDelete) {
throw new Error(
'Unexpected syntax list when looking for keywords after decorators',
);
}

const nextSibling = node.getChildren()[syntaxListIndex + 1];

if (!syntaxList || !nextSibling) {
throw new Error('Unexpected syntax');
if (!nextSibling) {
throw new Error('No sibling after syntax list');
}

return {
code: node
.getSourceFile()
.getFullText()
.slice(syntaxList.getStart(), nextSibling.getStart()),
.slice(firstKeywordToDelete.getStart(), nextSibling.getStart()),
span: {
start: syntaxList.getStart(),
length: nextSibling.getStart() - syntaxList.getStart(),
start: firstKeywordToDelete.getStart(),
length: nextSibling.getStart() - firstKeywordToDelete.getStart(),
},
};
};
Expand Down

0 comments on commit 02c3421

Please sign in to comment.