tldr-lint 0.0.12 → 0.0.14
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/LICENSE.md +3 -1
- package/README.md +17 -13
- package/lib/tldr-lint-cli.js +21 -30
- package/lib/tldr-lint.js +9 -8
- package/lib/tldr-parser.js +10 -9
- package/package.json +21 -9
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2016 Ruben Vereecken
|
|
3
|
+
Copyright (c) 2016 Ruben Vereecken
|
|
4
|
+
Copyright (c) 2016-present The [tldr-pages team](https://github.com/orgs/tldr-pages/people)
|
|
5
|
+
and [contributors](https://github.com/tldr-pages/tldr-lint/graphs/contributors)
|
|
4
6
|
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
8
|
|
package/README.md
CHANGED
|
@@ -2,17 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
[![npm version][npm-image]][npm-url]
|
|
4
4
|
[![Build Status][github-actions-image]][github-actions-url]
|
|
5
|
-
[![
|
|
6
|
-
[![Gitter chat][gitter-image]][gitter-url]
|
|
5
|
+
[![Matrix chat][matrix-image]][matrix-url]
|
|
7
6
|
|
|
8
7
|
`tldr-lint` is a linting tool for validating [tldr](https://github.com/tldr-pages/tldr) pages.
|
|
9
8
|
It can also format your pages for you!
|
|
10
9
|
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
`tldr-lint` and its alias `tldrl` can be installed via `npm`:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install --global tldr-lint
|
|
16
|
+
```
|
|
17
|
+
|
|
11
18
|
## Usage
|
|
12
19
|
|
|
13
20
|
It's really simple.
|
|
14
21
|
|
|
15
|
-
```
|
|
22
|
+
```txt
|
|
16
23
|
Usage: tldr-lint [options] <file|dir>
|
|
17
24
|
|
|
18
25
|
Options:
|
|
@@ -20,8 +27,10 @@ Options:
|
|
|
20
27
|
-f, --format also attempt formatting (to stdout, or as specified by -o)
|
|
21
28
|
-o, --output <file> output to formatted file
|
|
22
29
|
-i, --in-place formats in place
|
|
30
|
+
-t, --tabular format errors in a tabular format
|
|
23
31
|
-v, --verbose print verbose output
|
|
24
|
-
-
|
|
32
|
+
-I, --ignore <codes> ignore comma separated tldr-lint error codes (e.g. "TLDR001,TLDR0014")
|
|
33
|
+
-h, --help display help for command
|
|
25
34
|
```
|
|
26
35
|
|
|
27
36
|
## Linter errors
|
|
@@ -58,18 +67,13 @@ TLDR106 | Page title should start with a hash (`#`)
|
|
|
58
67
|
TLDR107 | File name should end with `.md` extension
|
|
59
68
|
TLDR108 | File name should not contain whitespace
|
|
60
69
|
TLDR109 | File name should be lowercase
|
|
70
|
+
TLDR110 | Command example should not be empty
|
|
61
71
|
|
|
62
72
|
[npm-url]: https://www.npmjs.com/package/tldr-lint
|
|
63
73
|
[npm-image]: https://img.shields.io/npm/v/tldr-lint.svg
|
|
64
74
|
|
|
65
75
|
[github-actions-url]: https://github.com/tldr-pages/tldr-lint/actions
|
|
66
|
-
[github-actions-image]: https://img.shields.io/github/workflow/status/tldr-pages/tldr-lint/
|
|
67
|
-
|
|
68
|
-
[dep-url]: https://david-dm.org/tldr-pages/tldr-lint
|
|
69
|
-
[dep-image]: https://img.shields.io/david/tldr-pages/tldr-lint.svg
|
|
70
|
-
|
|
71
|
-
[dev-dep-url]: https://david-dm.org/tldr-pages/tldr-lint#info=devDependencies
|
|
72
|
-
[dev-dep-image]: https://img.shields.io/david/dev/tldr-pages/tldr-lint.svg
|
|
76
|
+
[github-actions-image]: https://img.shields.io/github/actions/workflow/status/tldr-pages/tldr-lint/test.yml?branch=main
|
|
73
77
|
|
|
74
|
-
[
|
|
75
|
-
[
|
|
78
|
+
[matrix-url]: https://matrix.to/#/#tldr-pages:matrix.org
|
|
79
|
+
[matrix-image]: https://img.shields.io/matrix/tldr-pages:matrix.org?label=chat+on+matrix
|
package/lib/tldr-lint-cli.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const linter = require('./tldr-lint.js');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const cli = module.exports;
|
|
7
|
+
const util = require('util');
|
|
8
8
|
|
|
9
9
|
cli.writeErrors = function(file, linterResult, args) {
|
|
10
|
-
|
|
11
|
-
if (args.tabular) {
|
|
12
|
-
format = '%s\t%s\t%s\t%s\t';
|
|
13
|
-
} else {
|
|
14
|
-
format = '%s:%s: %s %s';
|
|
15
|
-
}
|
|
10
|
+
const format = args.tabular ? '%s\t%s\t%s\t%s\t' : '%s:%s: %s %s';
|
|
16
11
|
linterResult.errors.forEach(function(error) {
|
|
17
12
|
console.error(util.format(format, file, (error.locinfo.first_line ||
|
|
18
13
|
error.locinfo.last_line - 1),
|
|
@@ -22,8 +17,8 @@ cli.writeErrors = function(file, linterResult, args) {
|
|
|
22
17
|
if (!linterResult.success) {
|
|
23
18
|
console.error('Refraining from formatting because of fatal error');
|
|
24
19
|
} else {
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const formattedPage = linterResult.formatted;
|
|
21
|
+
let err;
|
|
27
22
|
if (args.output) {
|
|
28
23
|
err = fs.writeFileSync(args.output, formattedPage, 'utf8');
|
|
29
24
|
if (err) throw err;
|
|
@@ -38,15 +33,15 @@ cli.writeErrors = function(file, linterResult, args) {
|
|
|
38
33
|
};
|
|
39
34
|
|
|
40
35
|
cli.processFile = function(file, args) {
|
|
41
|
-
|
|
36
|
+
const linterResult = linter.processFile(file, args.verbose, args.format, args.ignore);
|
|
42
37
|
cli.writeErrors(file, linterResult, args);
|
|
43
38
|
return linterResult;
|
|
44
39
|
};
|
|
45
40
|
|
|
46
41
|
cli.processDirectory = function(dir, args) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
const files = fs.readdirSync(dir);
|
|
43
|
+
let stats;
|
|
44
|
+
const result = {
|
|
50
45
|
success: true,
|
|
51
46
|
errors: []
|
|
52
47
|
};
|
|
@@ -61,11 +56,11 @@ cli.processDirectory = function(dir, args) {
|
|
|
61
56
|
if (stats.isFile()) {
|
|
62
57
|
// Only treat files ending in .md
|
|
63
58
|
if (!file.match(/\.md$/)) return;
|
|
64
|
-
|
|
59
|
+
const linterResult = cli.processFile(file, args);
|
|
65
60
|
result.success &= linterResult.success;
|
|
66
61
|
result.errors = result.errors.concat(linterResult.errors);
|
|
67
62
|
} else {
|
|
68
|
-
|
|
63
|
+
const aggregateResult = cli.processDirectory(file, args);
|
|
69
64
|
result.success &= aggregateResult.success;
|
|
70
65
|
result.errors = result.errors.concat(aggregateResult.errors);
|
|
71
66
|
}
|
|
@@ -78,28 +73,24 @@ cli.process = function(file, args) {
|
|
|
78
73
|
console.error('--output only makes sense when used with --format');
|
|
79
74
|
process.exit(1);
|
|
80
75
|
}
|
|
76
|
+
let stats;
|
|
81
77
|
try {
|
|
82
|
-
|
|
78
|
+
stats = fs.statSync(file);
|
|
83
79
|
} catch(err) {
|
|
84
80
|
console.error(err.toString());
|
|
85
81
|
process.exit(1);
|
|
86
82
|
}
|
|
87
|
-
|
|
83
|
+
const isdir = stats.isDirectory();
|
|
88
84
|
if (args.output && isdir) {
|
|
89
85
|
console.error('--output only makes sense when used with a file');
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
if (!isdir) {
|
|
93
|
-
result = cli.processFile(file, args);
|
|
94
|
-
} else {
|
|
95
|
-
result = cli.processDirectory(file, args);
|
|
96
|
-
}
|
|
87
|
+
const result = isdir ? cli.processDirectory(file, args) : cli.processFile(file, args);
|
|
97
88
|
if (!result.success || result.errors.length >= 1) return process.exit(1);
|
|
98
89
|
};
|
|
99
90
|
|
|
100
91
|
if (require.main === module) {
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
const { program } = require('commander');
|
|
93
|
+
const package = require('../package.json');
|
|
103
94
|
program
|
|
104
95
|
.version(package.version)
|
|
105
96
|
.description(package.description)
|
|
@@ -109,7 +100,7 @@ if (require.main === module) {
|
|
|
109
100
|
.option('-i, --in-place', 'formats in place')
|
|
110
101
|
.option('-t, --tabular', 'format errors in a tabular format')
|
|
111
102
|
.option('-v, --verbose', 'print verbose output')
|
|
112
|
-
.option('-
|
|
103
|
+
.option('-I, --ignore <codes>', 'ignore comma separated tldr-lint error codes (e.g. "TLDR001,TLDR0014")')
|
|
113
104
|
.parse(process.argv);
|
|
114
105
|
|
|
115
106
|
if (program.args.length !== 1) {
|
package/lib/tldr-lint.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const parser = require('./tldr-parser.js').parser;
|
|
4
|
+
const util = require('util');
|
|
5
5
|
|
|
6
6
|
const MAX_EXAMPLES = 8;
|
|
7
7
|
|
|
@@ -35,6 +35,7 @@ module.exports.ERRORS = parser.ERRORS = {
|
|
|
35
35
|
'TLDR107': 'File name should end with .md extension',
|
|
36
36
|
'TLDR108': 'File name should not contain whitespace',
|
|
37
37
|
'TLDR109': 'File name should be lowercase',
|
|
38
|
+
'TLDR110': 'Command example should not be empty',
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
(function(parser) {
|
|
@@ -117,7 +118,7 @@ module.exports.ERRORS = parser.ERRORS = {
|
|
|
117
118
|
};
|
|
118
119
|
})(parser);
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
const linter = module.exports;
|
|
121
122
|
|
|
122
123
|
linter.parse = function(page) {
|
|
123
124
|
parser.init();
|
|
@@ -135,7 +136,7 @@ linter.formatExampleDescription = function(str) {
|
|
|
135
136
|
};
|
|
136
137
|
|
|
137
138
|
linter.format = function(parsedPage) {
|
|
138
|
-
|
|
139
|
+
let str = '';
|
|
139
140
|
str += util.format('# %s', parsedPage.title);
|
|
140
141
|
str += '\n\n';
|
|
141
142
|
parsedPage.description.forEach(function(line) {
|
|
@@ -163,7 +164,7 @@ linter.format = function(parsedPage) {
|
|
|
163
164
|
};
|
|
164
165
|
|
|
165
166
|
linter.process = function(file, page, verbose, alsoFormat) {
|
|
166
|
-
|
|
167
|
+
let success, result;
|
|
167
168
|
try {
|
|
168
169
|
linter.parse(page);
|
|
169
170
|
success = true;
|
|
@@ -195,7 +196,7 @@ linter.process = function(file, page, verbose, alsoFormat) {
|
|
|
195
196
|
};
|
|
196
197
|
|
|
197
198
|
linter.processFile = function(file, verbose, alsoFormat, ignoreErrors) {
|
|
198
|
-
|
|
199
|
+
const result = linter.process(file, fs.readFileSync(file, 'utf8'), verbose, alsoFormat);
|
|
199
200
|
if (path.extname(file) !== '.md') {
|
|
200
201
|
result.errors.push({ locinfo: { first_line: '0' }, code: 'TLDR107', description: this.ERRORS.TLDR107 });
|
|
201
202
|
}
|
package/lib/tldr-parser.js
CHANGED
|
@@ -77,7 +77,7 @@ var parser = {trace: function trace () { },
|
|
|
77
77
|
yy: {},
|
|
78
78
|
symbols_: {"error":2,"page":3,"title":4,"NEWLINE":5,"info":6,"examples":7,"TEXT":8,"HASH":9,"TITLE":10,"description":11,"information_link":12,"GREATER_THAN":13,"DESCRIPTION_LINE":14,"INFORMATION_LINK":15,"ANGLE_BRACKETED_URL":16,"END_INFORMATION_LINK_URL":17,"END_INFORMATION_LINK":18,"example":19,"maybe_newline":20,"example_description":21,"example_commands":22,"DASH":23,"EXAMPLE_DESCRIPTION":24,"example_command":25,"BACKTICK":26,"example_command_inner":27,"COMMAND_TEXT":28,"COMMAND_TOKEN":29,"$accept":0,"$end":1},
|
|
79
79
|
terminals_: {2:"error",5:"NEWLINE",8:"TEXT",9:"HASH",10:"TITLE",13:"GREATER_THAN",14:"DESCRIPTION_LINE",15:"INFORMATION_LINK",16:"ANGLE_BRACKETED_URL",17:"END_INFORMATION_LINK_URL",18:"END_INFORMATION_LINK",23:"DASH",24:"EXAMPLE_DESCRIPTION",26:"BACKTICK",28:"COMMAND_TEXT",29:"COMMAND_TOKEN"},
|
|
80
|
-
productions_: [0,[3,4],[3,3],[3,4],[4,2],[4,1],[6,1],[6,2],[11,2],[11,3],[12,4],[12,3],[12,5],[7,0],[7,2],[19,4],[20,0],[20,1],[21,2],[21,1],[22,1],[22,2],[25,3],[27,
|
|
80
|
+
productions_: [0,[3,4],[3,3],[3,4],[4,2],[4,1],[6,1],[6,2],[11,2],[11,3],[12,4],[12,3],[12,5],[7,0],[7,2],[19,4],[20,0],[20,1],[21,2],[21,1],[22,1],[22,2],[25,2],[25,3],[27,1],[27,1],[27,2],[27,2]],
|
|
81
81
|
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
|
82
82
|
/* this == yyval */
|
|
83
83
|
|
|
@@ -117,7 +117,7 @@ case 15:
|
|
|
117
117
|
yy.error(_$[$0], 'TLDR007');
|
|
118
118
|
|
|
119
119
|
break;
|
|
120
|
-
case 18:
|
|
120
|
+
case 18: case 24: case 25:
|
|
121
121
|
this.$ = $$[$0];
|
|
122
122
|
break;
|
|
123
123
|
case 19:
|
|
@@ -130,20 +130,20 @@ case 21:
|
|
|
130
130
|
this.$ = yy.error(_$[$0], 'TLDR105') || $$[$0-1];
|
|
131
131
|
break;
|
|
132
132
|
case 22:
|
|
133
|
-
this.$ =
|
|
133
|
+
this.$ = yy.error(this._$, 'TLDR110');
|
|
134
134
|
break;
|
|
135
135
|
case 23:
|
|
136
|
-
this.$ = [];
|
|
136
|
+
this.$ = $$[$0-1];
|
|
137
137
|
break;
|
|
138
|
-
case
|
|
138
|
+
case 26:
|
|
139
139
|
this.$ = [].concat($$[$0-1], yy.createCommandText($$[$0]));
|
|
140
140
|
break;
|
|
141
|
-
case
|
|
141
|
+
case 27:
|
|
142
142
|
this.$ = [].concat($$[$0-1], yy.createToken($$[$0]));
|
|
143
143
|
break;
|
|
144
144
|
}
|
|
145
145
|
},
|
|
146
|
-
table: [{3:1,4:2,8:[1,4],9:[1,3]},{1:[3]},{5:[1,5],6:6,11:7,13:$V0},{10:[1,9]},o($V1,[2,5]),{6:10,8:[1,11],11:7,13:$V0},o($V2,$V3,{7:12}),o($V2,[2,6],{12:13,13:[1,14]}),{14:[1,15]},o($V1,[2,4]),o($V2,$V3,{7:16}),o($V2,$V3,{7:17}),o($V4,$V5,{19:18,20:19,1:[2,2],5:$V6}),o($V2,[2,7],{13:[1,21]}),{14:[1,22],15:[1,23]},o($V7,[2,8]),o($V4,$V5,{19:18,20:19,1:[2,1],5:$V6}),o($V4,$V5,{19:18,20:19,1:[2,3],5:$V6}),o($V2,[2,14]),{8:[1,26],21:24,23:[1,25]},o([8,23,26],[2,17]),{15:[1,27]},o($V7,[2,9]),{16:[1,28],18:[1,29]},{5:$V6,20:30,26:$V5},{24:[1,31]},o($V8,[2,19]),{16:[1,32]},{17:[1,33]},o($V7,[2,11]),{22:34,25:35,26:$V9},o($V8,[2,18]),{17:[1,37]},o($V7,[2,10]),o($V2,[2,15],{25:38,26:$V9}),o($Va,[2,20]),
|
|
146
|
+
table: [{3:1,4:2,8:[1,4],9:[1,3]},{1:[3]},{5:[1,5],6:6,11:7,13:$V0},{10:[1,9]},o($V1,[2,5]),{6:10,8:[1,11],11:7,13:$V0},o($V2,$V3,{7:12}),o($V2,[2,6],{12:13,13:[1,14]}),{14:[1,15]},o($V1,[2,4]),o($V2,$V3,{7:16}),o($V2,$V3,{7:17}),o($V4,$V5,{19:18,20:19,1:[2,2],5:$V6}),o($V2,[2,7],{13:[1,21]}),{14:[1,22],15:[1,23]},o($V7,[2,8]),o($V4,$V5,{19:18,20:19,1:[2,1],5:$V6}),o($V4,$V5,{19:18,20:19,1:[2,3],5:$V6}),o($V2,[2,14]),{8:[1,26],21:24,23:[1,25]},o([8,23,26],[2,17]),{15:[1,27]},o($V7,[2,9]),{16:[1,28],18:[1,29]},{5:$V6,20:30,26:$V5},{24:[1,31]},o($V8,[2,19]),{16:[1,32]},{17:[1,33]},o($V7,[2,11]),{22:34,25:35,26:$V9},o($V8,[2,18]),{17:[1,37]},o($V7,[2,10]),o($V2,[2,15],{25:38,26:$V9}),o($Va,[2,20]),{26:[1,39],27:40,28:[1,41],29:[1,42]},o($V7,[2,12]),o($Va,[2,21]),o($Va,[2,22]),{26:[1,43],28:[1,44],29:[1,45]},o($Vb,[2,24]),o($Vb,[2,25]),o($Va,[2,23]),o($Vb,[2,26]),o($Vb,[2,27])],
|
|
147
147
|
defaultActions: {},
|
|
148
148
|
parseError: function parseError (str, hash) {
|
|
149
149
|
if (hash.recoverable) {
|
|
@@ -725,7 +725,8 @@ case 8:
|
|
|
725
725
|
if (this.topState() === 'description') {
|
|
726
726
|
this.popState();
|
|
727
727
|
yy_.yytext = this.matches[1];
|
|
728
|
-
|
|
728
|
+
var exceptions = ['npm', 'pnpm'];
|
|
729
|
+
if (!exceptions.includes(yy_.yytext.replace(/ .*/,'')) && yy_.yytext.match(/^[a-z]/)) {
|
|
729
730
|
yy.error(yy_.yylloc, 'TLDR003');
|
|
730
731
|
}
|
|
731
732
|
var punctuation = this.matches[2];
|
|
@@ -733,7 +734,7 @@ case 8:
|
|
|
733
734
|
yy.error(yy_.yylloc, 'TLDR004');
|
|
734
735
|
}
|
|
735
736
|
if (punctuation.match(/[,;]/)) {
|
|
736
|
-
console.warn(
|
|
737
|
+
console.warn(`Description ends in \'${punctuation}\'. Consider writing your sentence on one line.`);
|
|
737
738
|
}
|
|
738
739
|
this.checkTrailingWhitespace(this.matches[3], yy_.yylloc);
|
|
739
740
|
this.checkNewline(this.matches[4], yy_.yylloc);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldr-lint",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "A linting tool to validate tldr pages",
|
|
5
5
|
"repository": "tldr-pages/tldr-lint",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"jison": "jison tldr.yy tldr.l -o lib/tldr-parser.js",
|
|
8
8
|
"lint": "eslint lib specs",
|
|
9
|
-
"prepare": "husky
|
|
10
|
-
"test": "
|
|
9
|
+
"prepare": "husky",
|
|
10
|
+
"test": "jest specs",
|
|
11
11
|
"watch": "concurrently 'npm run watch:jison' 'npm run watch:specs'",
|
|
12
12
|
"watch:jison": "onchange '*.l' '*.yy' -- npm run jison",
|
|
13
13
|
"watch:specs": "onchange 'specs/*.js' 'lib/*.js' '*.l' '*.yy' -- npm run test"
|
|
@@ -27,19 +27,31 @@
|
|
|
27
27
|
"name": "Ruben Vereecken",
|
|
28
28
|
"email": "rubenvereecken@gmail.com"
|
|
29
29
|
},
|
|
30
|
+
"maintainers": [
|
|
31
|
+
{
|
|
32
|
+
"name": "tldr-pages team"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
30
38
|
"license": "MIT",
|
|
31
39
|
"dependencies": {
|
|
32
|
-
"commander": "^
|
|
40
|
+
"commander": "^12.0.0"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
35
|
-
"concurrently": "^
|
|
36
|
-
"eslint": "^
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
43
|
+
"concurrently": "^8.0.1",
|
|
44
|
+
"eslint": "^8.0.0",
|
|
45
|
+
"eslint-plugin-jest": "^27.0.4",
|
|
46
|
+
"husky": "^9.0.6",
|
|
47
|
+
"jest": "^29.1.2",
|
|
40
48
|
"jison": "^0.4.18",
|
|
41
49
|
"onchange": "^7.1.0"
|
|
42
50
|
},
|
|
51
|
+
"funding": {
|
|
52
|
+
"type": "liberapay",
|
|
53
|
+
"url": "https://liberapay.com/tldr-pages"
|
|
54
|
+
},
|
|
43
55
|
"files": [
|
|
44
56
|
"lib/"
|
|
45
57
|
]
|