Skip to content

Commit 4847a3d

Browse files
SamVerschuerensindresorhus
authored andcommitted
Fix custom error definition when exporting - fixes #127 (#132)
1 parent 53dc7c1 commit 4847a3d

File tree

2 files changed

+164
-77
lines changed

2 files changed

+164
-77
lines changed

rules/custom-error-definition.js

Lines changed: 80 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -42,85 +42,88 @@ const isAssignmentExpression = (node, name) => {
4242
return lhs.property.name === name;
4343
};
4444

45+
const customErrorDefinition = (context, node) => {
46+
if (!hasValidSuperClass(node)) {
47+
return;
48+
}
49+
50+
const name = node.id.name;
51+
const className = getClassName(name);
52+
53+
if (name !== className) {
54+
context.report({
55+
node: node.id,
56+
message: `Invalid class name, use \`${className}\`.`
57+
});
58+
}
59+
60+
const body = node.body.body;
61+
62+
const constructor = body.find(x => x.kind === 'constructor');
63+
64+
if (!constructor) {
65+
context.report({
66+
node,
67+
message: 'Add a constructor to your error.',
68+
fix: fixer => fixer.insertTextAfterRange([
69+
node.body.start,
70+
node.body.start + 1
71+
], getConstructorMethod(name))
72+
});
73+
return;
74+
}
75+
76+
const constructorBodyNode = constructor.value.body;
77+
const constructorBody = constructorBodyNode.body;
78+
79+
const superExpression = constructorBody.find(isSuperExpression);
80+
const messageExpressionIndex = constructorBody.findIndex(x => isAssignmentExpression(x, 'message'));
81+
82+
if (!superExpression) {
83+
context.report({
84+
node: constructorBodyNode,
85+
message: 'Missing call to `super()` in constructor.'
86+
});
87+
} else if (messageExpressionIndex !== -1 && superExpression.expression.arguments.length === 0) {
88+
const rhs = constructorBody[messageExpressionIndex].expression.right;
89+
90+
context.report({
91+
node: superExpression,
92+
message: 'Pass the error message to `super()`.',
93+
fix: fixer => fixer.insertTextAfterRange([
94+
superExpression.start,
95+
superExpression.start + 6
96+
], rhs.raw || rhs.name)
97+
});
98+
}
99+
100+
if (messageExpressionIndex !== -1) {
101+
const expression = constructorBody[messageExpressionIndex];
102+
103+
context.report({
104+
node: expression,
105+
message: 'Pass the error message to `super()` instead of setting `this.message`.',
106+
fix: fixer => fixer.removeRange([
107+
messageExpressionIndex === 0 ? constructorBodyNode.start : constructorBody[messageExpressionIndex - 1].end,
108+
expression.end
109+
])
110+
});
111+
}
112+
113+
const nameExpression = constructorBody.find(x => isAssignmentExpression(x, 'name'));
114+
115+
if (!nameExpression || nameExpression.expression.right.value !== name) {
116+
context.report({
117+
node: nameExpression ? nameExpression.expression.right : constructorBodyNode,
118+
message: `The \`name\` property should be set to \`${name}\`.`
119+
});
120+
}
121+
};
122+
45123
const create = context => {
46124
return {
47-
ClassDeclaration: node => {
48-
if (!hasValidSuperClass(node)) {
49-
return;
50-
}
51-
52-
const name = node.id.name;
53-
const className = getClassName(name);
54-
55-
if (name !== className) {
56-
context.report({
57-
node: node.id,
58-
message: `Invalid class name, use \`${className}\`.`
59-
});
60-
}
61-
62-
const body = node.body.body;
63-
64-
const constructor = body.find(x => x.kind === 'constructor');
65-
66-
if (!constructor) {
67-
context.report({
68-
node,
69-
message: 'Add a constructor to your error.',
70-
fix: fixer => fixer.insertTextAfterRange([
71-
node.body.start,
72-
node.body.start + 1
73-
], getConstructorMethod(name))
74-
});
75-
return;
76-
}
77-
78-
const constructorBodyNode = constructor.value.body;
79-
const constructorBody = constructorBodyNode.body;
80-
81-
const superExpression = constructorBody.find(isSuperExpression);
82-
const messageExpressionIndex = constructorBody.findIndex(x => isAssignmentExpression(x, 'message'));
83-
84-
if (!superExpression) {
85-
context.report({
86-
node: constructorBodyNode,
87-
message: 'Missing call to `super()` in constructor.'
88-
});
89-
} else if (messageExpressionIndex !== -1 && superExpression.expression.arguments.length === 0) {
90-
const rhs = constructorBody[messageExpressionIndex].expression.right;
91-
92-
context.report({
93-
node: superExpression,
94-
message: 'Pass the error message to `super()`.',
95-
fix: fixer => fixer.insertTextAfterRange([
96-
superExpression.start,
97-
superExpression.start + 6
98-
], rhs.raw || rhs.name)
99-
});
100-
}
101-
102-
if (messageExpressionIndex !== -1) {
103-
const expression = constructorBody[messageExpressionIndex];
104-
105-
context.report({
106-
node: expression,
107-
message: 'Pass the error message to `super()` instead of setting `this.message`.',
108-
fix: fixer => fixer.removeRange([
109-
messageExpressionIndex === 0 ? constructorBodyNode.start : constructorBody[messageExpressionIndex - 1].end,
110-
expression.end
111-
])
112-
});
113-
}
114-
115-
const nameExpression = constructorBody.find(x => isAssignmentExpression(x, 'name'));
116-
117-
if (!nameExpression || nameExpression.expression.right.value !== name) {
118-
context.report({
119-
node: nameExpression ? nameExpression.expression.right : constructorBodyNode,
120-
message: `The \`name\` property should be set to \`${name}\`.`
121-
});
122-
}
123-
}
125+
ClassDeclaration: node => customErrorDefinition(context, node),
126+
'AssignmentExpression[right.type="ClassExpression"]': node => customErrorDefinition(context, node.right)
124127
};
125128
};
126129

test/custom-error-definition.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,38 @@ ruleTester.run('custom-error-definition', rule, {
6363
this.name = 'FooError';
6464
}
6565
}
66+
`,
67+
`
68+
export class FooError extends TypeError {
69+
constructor() {
70+
super();
71+
this.name = 'FooError';
72+
}
73+
};
74+
`,
75+
`
76+
export default class FooError extends TypeError {
77+
constructor() {
78+
super();
79+
this.name = 'FooError';
80+
}
81+
};
82+
`,
83+
`
84+
module.exports = class FooError extends TypeError {
85+
constructor() {
86+
super();
87+
this.name = 'FooError';
88+
}
89+
};
90+
`,
91+
`
92+
exports.FooError = class FooError extends TypeError {
93+
constructor() {
94+
super();
95+
this.name = 'FooError';
96+
}
97+
};
6698
`
6799
],
68100
invalid: [
@@ -295,6 +327,58 @@ ruleTester.run('custom-error-definition', rule, {
295327
errors: [
296328
invalidNameError('FooError')
297329
]
330+
},
331+
{
332+
code: `
333+
module.exports = class FooError extends TypeError {
334+
constructor() {
335+
super();
336+
this.name = 'foo';
337+
}
338+
};
339+
`,
340+
errors: [
341+
invalidNameError('FooError')
342+
]
343+
},
344+
{
345+
code: `
346+
exports.FooError = class FooError extends TypeError {
347+
constructor() {
348+
super();
349+
this.name = 'foo';
350+
}
351+
};
352+
`,
353+
errors: [
354+
invalidNameError('FooError')
355+
]
356+
},
357+
{
358+
code: `
359+
export class FooError extends TypeError {
360+
constructor() {
361+
super();
362+
this.name = 'foo';
363+
}
364+
};
365+
`,
366+
errors: [
367+
invalidNameError('FooError')
368+
]
369+
},
370+
{
371+
code: `
372+
export default class FooError extends TypeError {
373+
constructor() {
374+
super();
375+
this.name = 'foo';
376+
}
377+
};
378+
`,
379+
errors: [
380+
invalidNameError('FooError')
381+
]
298382
}
299383
]
300384
});

0 commit comments

Comments
 (0)