Skip to content

Commit 65e3b8a

Browse files
Update name-format to cover all cases (#537)
* Update format-name rule to check for all name errors * Update rule documentation Co-authored-by: Thomas Lindner <[email protected]>
1 parent e2f2f0d commit 65e3b8a

File tree

9 files changed

+158
-50
lines changed

9 files changed

+158
-50
lines changed

docs/rules/format/name-format.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ id: name-format
33
title: name-format
44
---
55

6-
Enabling this rule will result in an error being generated if `name` is not lowercase.
6+
Enabling this rule will result in an error being generated if `name` does not meet the [naming constraints](https://github.com/npm/validate-npm-package-name#naming-rules).
77

88
## Example .npmpackagejsonlintrc configuration
99

@@ -25,6 +25,18 @@ Enabling this rule will result in an error being generated if `name` is not lowe
2525
}
2626
```
2727

28+
```json
29+
{
30+
"name": "npm package json lint"
31+
}
32+
```
33+
34+
```json
35+
{
36+
"name": ".npm-package-json-lint"
37+
}
38+
```
39+
2840
### *Correct* example(s)
2941

3042
```json
@@ -35,4 +47,6 @@ Enabling this rule will result in an error being generated if `name` is not lowe
3547

3648
## History
3749

50+
* Augmented with all name checks in `validate-npm-package-name` in version 6.0.0
51+
* Added checks for name length and not starting with . or _ in version 4.0.0
3852
* Introduced in version 1.0.0

package-lock.json

Lines changed: 28 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"plur": "^4.0.0",
5252
"semver": "^7.3.5",
5353
"slash": "^3.0.0",
54-
"strip-json-comments": "^3.1.1"
54+
"strip-json-comments": "^3.1.1",
55+
"validate-npm-package-name": "^3.0.0"
5556
},
5657
"devDependencies": {
5758
"eslint": "^7.32.0",

src/rules/name-format.js

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
1-
const {isLowercase} = require('../validators/format');
1+
const validateName = require('validate-npm-package-name');
22
const LintIssue = require('../LintIssue');
3+
const getNameError = require('../utils/getNameError');
34

45
const lintId = 'name-format';
56
const nodeName = 'name';
67
const ruleType = 'standard';
7-
const maxLength = 214;
88

99
const lint = (packageJsonData, severity) => {
1010
if (!packageJsonData.hasOwnProperty(nodeName)) {
1111
return true;
1212
}
1313

1414
const name = packageJsonData[nodeName];
15+
const results = validateName(name);
1516

16-
if (!isLowercase(name)) {
17-
return new LintIssue(lintId, severity, nodeName, 'Format should be all lowercase');
18-
}
19-
20-
if (name.length > maxLength) {
21-
return new LintIssue(lintId, severity, nodeName, `name should be less than or equal to ${maxLength} characters.`);
22-
}
23-
24-
if (name.startsWith('.') || name.startsWith('_')) {
25-
return new LintIssue(lintId, severity, nodeName, 'name should not start with . or _');
17+
if (!results.validForNewPackages) {
18+
return new LintIssue(lintId, severity, nodeName, getNameError(results));
2619
}
2720

2821
return true;

src/utils/getNameError.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Gets the error message indicating why the name is invalid.
3+
*
4+
* @param {object} results Results from validate-npm-package-name
5+
* @returns {string} Error/warning message
6+
*/
7+
const getNameError = (results) => {
8+
// Errors are returned for names that were never valid
9+
if (results.errors && results.errors.length > 0) {
10+
return results.errors[0];
11+
}
12+
13+
// Warnings are returned for names that are no longer valid
14+
if (results.warnings && results.warnings.length > 0) {
15+
return results.warnings[0];
16+
}
17+
18+
// Ensure that an error message is returned in any case
19+
return 'name invalid';
20+
};
21+
22+
module.exports = getNameError;

src/validators/format.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
const semver = require('semver');
22

3-
/**
4-
* Determines whether or not the string is lowercase
5-
* @param {string} name Name
6-
* @return {boolean} True if the string is lowercase or is missing. False if it is not.
7-
*/
8-
const isLowercase = (name) => name === name.toLowerCase();
9-
103
/**
114
* Determines whether or not the node's value is a valid semantic version
125
* @param {object} packageJsonData Valid JSON
@@ -22,6 +15,5 @@ const isValidVersionNumber = (packageJsonData, nodeName) => {
2215
};
2316

2417
module.exports = {
25-
isLowercase,
2618
isValidVersionNumber,
2719
};

test/unit/rules/name-format.test.js

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,34 @@ describe('name-format Unit Tests', () => {
1919
expect(response.lintId).toStrictEqual('name-format');
2020
expect(response.severity).toStrictEqual('error');
2121
expect(response.node).toStrictEqual('name');
22-
expect(response.lintMessage).toStrictEqual('Format should be all lowercase');
22+
expect(response.lintMessage).toStrictEqual('name can no longer contain capital letters');
23+
});
24+
25+
test('contains space - LintIssue object should be returned', () => {
26+
const packageJsonData = {
27+
name: 'contains space',
28+
};
29+
const response = lint(packageJsonData, 'error');
30+
31+
expect(response.lintId).toStrictEqual('name-format');
32+
expect(response.severity).toStrictEqual('error');
33+
expect(response.node).toStrictEqual('name');
34+
expect(response.lintMessage).toStrictEqual('name can only contain URL-friendly characters');
2335
});
2436

2537
test('exceeds max length - LintIssue object should be returned', () => {
2638
const packageJsonData = {
27-
name: 'a'.padStart(215),
39+
name: 'a'.padStart(215, 'a'),
2840
};
2941
const response = lint(packageJsonData, 'error');
3042

3143
expect(response.lintId).toStrictEqual('name-format');
3244
expect(response.severity).toStrictEqual('error');
3345
expect(response.node).toStrictEqual('name');
34-
expect(response.lintMessage).toStrictEqual('name should be less than or equal to 214 characters.');
46+
expect(response.lintMessage).toStrictEqual('name can no longer contain more than 214 characters');
3547
});
3648

37-
test('starts with . - LintIssue object should be returned', () => {
49+
test('starts with . and no scope - LintIssue object should be returned', () => {
3850
const packageJsonData = {
3951
name: '.lowercase',
4052
};
@@ -43,10 +55,10 @@ describe('name-format Unit Tests', () => {
4355
expect(response.lintId).toStrictEqual('name-format');
4456
expect(response.severity).toStrictEqual('error');
4557
expect(response.node).toStrictEqual('name');
46-
expect(response.lintMessage).toStrictEqual('name should not start with . or _');
58+
expect(response.lintMessage).toStrictEqual('name cannot start with a period');
4759
});
4860

49-
test('starts with _ - LintIssue object should be returned', () => {
61+
test('starts with _ and no scope - LintIssue object should be returned', () => {
5062
const packageJsonData = {
5163
name: '_lowercase',
5264
};
@@ -55,7 +67,36 @@ describe('name-format Unit Tests', () => {
5567
expect(response.lintId).toStrictEqual('name-format');
5668
expect(response.severity).toStrictEqual('error');
5769
expect(response.node).toStrictEqual('name');
58-
expect(response.lintMessage).toStrictEqual('name should not start with . or _');
70+
expect(response.lintMessage).toStrictEqual('name cannot start with an underscore');
71+
});
72+
});
73+
74+
describe('when package.json has node with correct format', () => {
75+
test('all valid characters - true should be returned', () => {
76+
const packageJsonData = {
77+
name: 'lowercase-name',
78+
};
79+
const response = lint(packageJsonData, 'error');
80+
81+
expect(response).toBe(true);
82+
});
83+
84+
test('starts with . and has scope - true should be returned', () => {
85+
const packageJsonData = {
86+
name: '@foo/.lowercase',
87+
};
88+
const response = lint(packageJsonData, 'error');
89+
90+
expect(response).toBe(true);
91+
});
92+
93+
test('starts with _ and has scope - true should be returned', () => {
94+
const packageJsonData = {
95+
name: '@foo/_lowercase',
96+
};
97+
const response = lint(packageJsonData, 'error');
98+
99+
expect(response).toBe(true);
59100
});
60101
});
61102

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const getNameError = require('../../../src/utils/getNameError');
2+
3+
const genericErrorMessage = 'name invalid';
4+
const getResults = (includeErrors = true, includeWarnings = true) => {
5+
const results = {
6+
validForNewPackages: false,
7+
validForOldPackages: !includeErrors,
8+
warnings: ['first warning', 'second warning'],
9+
errors: ['first error', 'second error'],
10+
};
11+
12+
if (!includeErrors) {
13+
delete results.errors;
14+
}
15+
16+
if (!includeWarnings) {
17+
delete results.warnings;
18+
}
19+
20+
return results;
21+
};
22+
23+
describe('getNameError Unit Tests', () => {
24+
test('if errors - returns first error', () => {
25+
const results = getResults(true, true);
26+
expect(getNameError(results)).toBe(results.errors[0]);
27+
});
28+
29+
test('if warnings and no errors - returns first warning', () => {
30+
const results = getResults(false, true);
31+
expect(getNameError(results)).toBe(results.warnings[0]);
32+
});
33+
34+
test('if no warnings and no errors - returns generic error message', () => {
35+
const results = getResults(false, false);
36+
expect(getNameError(results)).toBe(genericErrorMessage);
37+
});
38+
});

test/unit/validators/format.test.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,6 @@
11
const format = require('../../../src/validators/format');
22

33
describe('format Unit Tests', () => {
4-
describe('isLowercase method', () => {
5-
describe('when the string is lowercase', () => {
6-
test('true should be returned', () => {
7-
const string = 'awesome-module';
8-
const response = format.isLowercase(string);
9-
10-
expect(response).toBe(true);
11-
});
12-
});
13-
14-
describe('when the string is not lowercase', () => {
15-
test('false should be returned', () => {
16-
const string = 'aweSome-moDule';
17-
const response = format.isLowercase(string);
18-
19-
expect(response).toBe(false);
20-
});
21-
});
22-
});
23-
244
describe('isValidVersionNumber method', () => {
255
describe('when the node does not exist in the package.json file', () => {
266
test('true should be returned', () => {

0 commit comments

Comments
 (0)