tldr-lint 0.0.21 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/lib/tldr-lint.js +36 -2
- package/lib/tldr-parser.js +5 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -71,10 +71,10 @@ TLDR009 | Page should contain a newline at end of file
|
|
|
71
71
|
TLDR010 | Only Unix-style line endings allowed
|
|
72
72
|
TLDR011 | Page never contains more than a single empty line
|
|
73
73
|
TLDR012 | Page should contain no tabs
|
|
74
|
-
TLDR013 | Title should be alphanumeric with dashes, underscores or
|
|
74
|
+
TLDR013 | Title should be alphanumeric with dashes, underscores, spaces or allowed characters
|
|
75
75
|
TLDR014 | Page should contain no trailing whitespace
|
|
76
76
|
TLDR015 | Example descriptions should start with a capital letter
|
|
77
|
-
TLDR016 | Label for information link should be spelled exactly `More information
|
|
77
|
+
TLDR016 | Label for information link should be spelled exactly `More information:` (with a succeeding whitespace)
|
|
78
78
|
TLDR017 | Information link should be surrounded with angle brackets
|
|
79
79
|
TLDR018 | Page should only include a single information link
|
|
80
80
|
TLDR019 | Page should only include a maximum of 8 examples
|
|
@@ -91,7 +91,8 @@ TLDR108 | File name should not contain whitespace
|
|
|
91
91
|
TLDR109 | File name should be lowercase
|
|
92
92
|
TLDR110 | Command example should not be empty
|
|
93
93
|
TLDR111 | File name should not contain any Windows-forbidden character
|
|
94
|
-
TLDR112 | Terms `stdin`, `stdout`, `stderr`, and `
|
|
94
|
+
TLDR112 | Terms `stdin`, `stdout`, `stderr`, `regex`, and `glob` (including longer forms like "standard input", "regular expression", "glob pattern") should be lowercase and wrapped in backticks
|
|
95
|
+
TLDR113 | Translated line must not be identical to English line (only lines starting with `>` and `-` are checked)
|
|
95
96
|
|
|
96
97
|
[npm-url]: https://www.npmjs.com/package/tldr-lint
|
|
97
98
|
[npm-image]: https://img.shields.io/npm/v/tldr-lint.svg
|
package/lib/tldr-lint.js
CHANGED
|
@@ -4,6 +4,8 @@ const parser = require('./tldr-parser.js').parser;
|
|
|
4
4
|
const util = require('util');
|
|
5
5
|
|
|
6
6
|
const MAX_EXAMPLES = 8;
|
|
7
|
+
const MIN_WORDS_PER_LINE = 2;
|
|
8
|
+
const ALLOWED_LOWERCASE_WORDS = ['macOS'];
|
|
7
9
|
|
|
8
10
|
module.exports.ERRORS = parser.ERRORS = {
|
|
9
11
|
'TLDR001': 'File should contain no leading whitespace',
|
|
@@ -28,7 +30,6 @@ module.exports.ERRORS = parser.ERRORS = {
|
|
|
28
30
|
'TLDR020': 'Label for additional notes should be spelled exactly `Note: `',
|
|
29
31
|
'TLDR021': 'Command example should not begin or end in whitespace',
|
|
30
32
|
|
|
31
|
-
|
|
32
33
|
'TLDR101': 'Command description probably not properly annotated',
|
|
33
34
|
'TLDR102': 'Example description probably not properly annotated',
|
|
34
35
|
'TLDR103': 'Command example is missing its closing backtick',
|
|
@@ -40,7 +41,8 @@ module.exports.ERRORS = parser.ERRORS = {
|
|
|
40
41
|
'TLDR109': 'File name should be lowercase',
|
|
41
42
|
'TLDR110': 'Command example should not be empty',
|
|
42
43
|
'TLDR111': 'File name should not contain any Windows-forbidden character',
|
|
43
|
-
'TLDR112': 'Terms `stdin`, `stdout`, `stderr`, and `
|
|
44
|
+
'TLDR112': 'Terms `stdin`, `stdout`, `stderr`, `regex`, and `glob` should be lowercase and wrapped in backticks',
|
|
45
|
+
'TLDR113': 'Translated line must not be identical to English line'
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
(function(parser) {
|
|
@@ -217,6 +219,38 @@ linter.processFile = function(file, verbose, alsoFormat, ignoreErrors) {
|
|
|
217
219
|
result.errors.push({ locinfo: { first_line: '0' }, code: 'TLDR111', description: this.ERRORS.TLDR111 });
|
|
218
220
|
}
|
|
219
221
|
|
|
222
|
+
const parts = file.split(path.sep);
|
|
223
|
+
const pagesIdx = parts.findIndex(seg => seg && seg.indexOf('pages.') === 0 && seg.length > 6);
|
|
224
|
+
if (pagesIdx !== -1 && parts[pagesIdx].slice(6) !== 'en') {
|
|
225
|
+
const englishParts = parts.slice();
|
|
226
|
+
englishParts[pagesIdx] = 'pages';
|
|
227
|
+
const englishPath = englishParts.join(path.sep);
|
|
228
|
+
if (fs.existsSync(englishPath)) {
|
|
229
|
+
const englishLines = fs.readFileSync(englishPath, 'utf8').split(/\r?\n/).map(l => l.trim());
|
|
230
|
+
const englishSet = new Set(englishLines);
|
|
231
|
+
const translatedLines = fs.readFileSync(file, 'utf8').split(/\r?\n/).map(l => l.trim());
|
|
232
|
+
translatedLines.forEach(function(line, idx) {
|
|
233
|
+
if (!line.match(/^[>-]/)) return;
|
|
234
|
+
if (!englishSet.has(line)) return;
|
|
235
|
+
|
|
236
|
+
const content = line.replace(/^[>-]\s/, '').trim();
|
|
237
|
+
const words = content.split(/\s/).filter(w => w.length > 0);
|
|
238
|
+
if (!(words.length > MIN_WORDS_PER_LINE)) return;
|
|
239
|
+
|
|
240
|
+
const everyWordCapitalized = words.length > 0 && words.every(word => {
|
|
241
|
+
const stripped = word.replace(/[()]/g, '');
|
|
242
|
+
if (!/[a-zA-Z]/.test(stripped)) return true;
|
|
243
|
+
if (ALLOWED_LOWERCASE_WORDS.includes(stripped.replace(/\W+$/, ''))) return true;
|
|
244
|
+
|
|
245
|
+
return /^[A-Z]/.test(stripped);
|
|
246
|
+
});
|
|
247
|
+
if (!everyWordCapitalized) {
|
|
248
|
+
result.errors.push({ locinfo: { first_line: (idx+1).toString() }, code: 'TLDR113', description: this.ERRORS.TLDR113 });
|
|
249
|
+
}
|
|
250
|
+
}, this);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
220
254
|
if (ignoreErrors) {
|
|
221
255
|
ignoreErrors = ignoreErrors.split(',').map(function(val) {
|
|
222
256
|
return val.trim();
|
package/lib/tldr-parser.js
CHANGED
|
@@ -707,7 +707,7 @@ case 7:
|
|
|
707
707
|
if (this.topState() === "title") {
|
|
708
708
|
yy_.yytext = this.matches[1];
|
|
709
709
|
if (
|
|
710
|
-
/[^\w+\[\]{}!%,^~$:><|?. -]/.test(yy_.yytext) ||
|
|
710
|
+
/[^\w+\[\]{}\(\)!%,^~$:><|?. -]/.test(yy_.yytext) ||
|
|
711
711
|
(yy_.yytext.endsWith('.') && yy_.yytext !== '.' && yy_.yytext !== ' .')
|
|
712
712
|
) {
|
|
713
713
|
yy.error(yy_.yylloc, 'TLDR013');
|
|
@@ -751,7 +751,7 @@ case 8:
|
|
|
751
751
|
console.warn(`Description ends in \'${punctuation}\'. Consider writing your sentence on one line.`);
|
|
752
752
|
}
|
|
753
753
|
// Check for incorrect stdout/stdin/stderr/regex usage
|
|
754
|
-
var termMatch = yy_.yytext.match(/(?:^|[^`-\w])(stdout|stdin|stderr|regex|regular\s+expression|standard\s+(?:in|input|out|output|err|error))(?=[^`\w]|$)/i);
|
|
754
|
+
var termMatch = yy_.yytext.match(/(?:^|[^`-\w])(stdout|stdin|stderr|regex|regular\s+expression|standard\s+(?:in|input|out|output|err|error)|glob(?:\s+pattern)?)(?=[^`\w]|$)/i);
|
|
755
755
|
if (termMatch) {
|
|
756
756
|
// Skip if the term is inside angle brackets (URL)
|
|
757
757
|
var beforeTerm = yy_.yytext.substring(0, termMatch.index + termMatch[0].indexOf(termMatch[1]));
|
|
@@ -765,6 +765,7 @@ case 8:
|
|
|
765
765
|
else if (foundTerm.match(/^standard (in|input)$/)) term = 'stdin';
|
|
766
766
|
else if (foundTerm.match(/^standard (out|output)$/)) term = 'stdout';
|
|
767
767
|
else if (foundTerm.match(/^standard (err|error)$/)) term = 'stderr';
|
|
768
|
+
else if (foundTerm === 'glob pattern') term = 'glob';
|
|
768
769
|
yy.errors.push({
|
|
769
770
|
locinfo: yy_.yylloc,
|
|
770
771
|
code: 'TLDR112',
|
|
@@ -796,7 +797,7 @@ case 9:
|
|
|
796
797
|
yy.error(yy_.yylloc, 'TLDR104');
|
|
797
798
|
}
|
|
798
799
|
// Check for incorrect stdout/stdin/stderr/regex usage
|
|
799
|
-
var termMatch = yy_.yytext.match(/(?:^|[^`-\w])(stdout|stdin|stderr|regex|regular\s+expression|standard\s+(?:in|input|out|output|err|error))(?=[^`\w]|$)/i);
|
|
800
|
+
var termMatch = yy_.yytext.match(/(?:^|[^`-\w])(stdout|stdin|stderr|regex|regular\s+expression|standard\s+(?:in|input|out|output|err|error)|glob(?:\s+pattern)?)(?=[^`\w]|$)/i);
|
|
800
801
|
if (termMatch) {
|
|
801
802
|
// Skip if the term is inside angle brackets (URL)
|
|
802
803
|
var beforeTerm = yy_.yytext.substring(0, termMatch.index + termMatch[0].indexOf(termMatch[1]));
|
|
@@ -810,6 +811,7 @@ case 9:
|
|
|
810
811
|
else if (foundTerm.match(/^standard (in|input)$/)) term = 'stdin';
|
|
811
812
|
else if (foundTerm.match(/^standard (out|output)$/)) term = 'stdout';
|
|
812
813
|
else if (foundTerm.match(/^standard (err|error)$/)) term = 'stderr';
|
|
814
|
+
else if (foundTerm === 'glob pattern') term = 'glob';
|
|
813
815
|
yy.errors.push({
|
|
814
816
|
locinfo: yy_.yylloc,
|
|
815
817
|
code: 'TLDR112',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldr-lint",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "A linting tool to validate tldr pages",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"commander": "^
|
|
45
|
+
"commander": "^15.0.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"concurrently": "^
|
|
49
|
-
"eslint": "^
|
|
50
|
-
"eslint-config-eslint": "^
|
|
48
|
+
"concurrently": "^10.0.1",
|
|
49
|
+
"eslint": "^10.1.0",
|
|
50
|
+
"eslint-config-eslint": "^14.0.0",
|
|
51
51
|
"eslint-plugin-jest": "^29.0.1",
|
|
52
52
|
"husky": "^9.1.7",
|
|
53
53
|
"jest": "^30.0.0",
|