ilib-lint 2.17.0 → 2.18.0
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/package.json +6 -4
- package/src/DirItem.js +40 -21
- package/src/FileType.js +91 -72
- package/src/LintableFile.js +44 -128
- package/src/LintingStrategy.js +197 -0
- package/src/ParserManager.js +19 -30
- package/src/Project.js +272 -186
- package/src/RuleManager.js +56 -32
- package/src/RuleSet.js +11 -10
- package/src/plugins/BuiltinPlugin.js +17 -2
- package/src/plugins/LineSerializer.js +10 -0
- package/src/plugins/XliffSerializer.js +35 -1
- package/src/plugins/byte/ByteFix.js +55 -0
- package/src/plugins/byte/ByteFixer.js +65 -0
- package/src/plugins/byte/ByteParser.js +62 -0
- package/src/plugins/byte/ByteSerializer.js +59 -0
- package/src/plugins/positional/PositionalFixCommand.js +192 -0
- package/src/plugins/string/StringFixCommand.js +32 -99
- package/src/plugins/string/StringFixer.js +11 -5
- package/src/plugins/string/StringSerializer.js +10 -0
- package/src/rules/ResourceCamelCase.js +3 -2
- package/src/rules/ResourceCompleteness.js +2 -1
- package/src/rules/ResourceEdgeWhitespace.js +2 -1
- package/src/rules/ResourceGNUPrintfMatch.js +2 -0
- package/src/rules/ResourceICUPlurals.js +24 -17
- package/src/rules/ResourceKebabCase.js +3 -2
- package/src/rules/ResourceNoTranslation.js +2 -2
- package/src/rules/ResourceQuoteStyle.js +3 -4
- package/src/rules/ResourceSentenceEnding.js +303 -196
- package/src/rules/ResourceSnakeCase.js +3 -2
- package/src/rules/ResourceSourceICUPluralCategories.js +2 -1
- package/src/rules/ResourceSourceICUPluralParams.js +2 -1
- package/src/rules/ResourceSourceICUUnexplainedParams.js +3 -2
- package/src/rules/ResourceUniqueKeys.js +17 -18
- package/src/rules/ResourceXML.js +9 -8
- package/src/rules/byte/BOMRule.js +101 -0
- package/src/rules/byte/FileEncodingRule.js +113 -0
- package/src/rules/string/XliffHeaderEncoding.js +121 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ilib-lint",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"module": "./src/index.js",
|
|
@@ -51,10 +51,12 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@tsconfig/node14": "^14.1.2",
|
|
53
53
|
"@types/jest": "^29.5.14",
|
|
54
|
+
"@types/micromatch": "^4.0.9",
|
|
54
55
|
"@types/node": "^14.0.0",
|
|
55
56
|
"dedent": "^1.5.3",
|
|
56
57
|
"docdash": "^2.0.2",
|
|
57
58
|
"i18nlint-plugin-test-old": "file:test/i18nlint-plugin-test-old",
|
|
59
|
+
"ilib-es6": "^14.21.0",
|
|
58
60
|
"ilib-lint-plugin-obsolete": "file:test/ilib-lint-plugin-obsolete",
|
|
59
61
|
"ilib-lint-plugin-test": "file:test/ilib-lint-plugin-test",
|
|
60
62
|
"jest": "^29.7.0",
|
|
@@ -73,10 +75,10 @@
|
|
|
73
75
|
"options-parser": "^0.4.0",
|
|
74
76
|
"xml-js": "^1.6.11",
|
|
75
77
|
"ilib-common": "^1.1.6",
|
|
76
|
-
"ilib-locale": "^1.2.4",
|
|
77
|
-
"ilib-lint-common": "^3.4.0",
|
|
78
78
|
"ilib-ctype": "^1.2.2",
|
|
79
|
-
"ilib-
|
|
79
|
+
"ilib-lint-common": "^3.6.0",
|
|
80
|
+
"ilib-tools-common": "^1.19.0",
|
|
81
|
+
"ilib-locale": "^1.2.4"
|
|
80
82
|
},
|
|
81
83
|
"scripts": {
|
|
82
84
|
"coverage": "pnpm test -- --coverage",
|
package/src/DirItem.js
CHANGED
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import
|
|
20
|
+
import { Result } from "ilib-lint-common";
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// type imports
|
|
23
|
+
/** @ignore @typedef {import("./Project.js").default} Project */
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* @class Represent a directory item.
|
|
@@ -30,15 +31,33 @@ const logger = log4js.getLogger("ilib-lint.DirItem");
|
|
|
30
31
|
* @abstract
|
|
31
32
|
*/
|
|
32
33
|
class DirItem {
|
|
34
|
+
/**
|
|
35
|
+
* The file path for this directory item
|
|
36
|
+
* @type {String}
|
|
37
|
+
*/
|
|
38
|
+
filePath;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The settings from the ilib-lint config that apply to this file
|
|
42
|
+
* @type {Record<string, unknown> | undefined}
|
|
43
|
+
*/
|
|
44
|
+
settings;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The project that this directory item is part of
|
|
48
|
+
* @type {Project|undefined}
|
|
49
|
+
*/
|
|
50
|
+
project;
|
|
51
|
+
|
|
33
52
|
/**
|
|
34
53
|
* Construct a new directory item
|
|
35
54
|
* The options parameter can contain any of the following properties:
|
|
36
55
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
56
|
+
* @param {String} filePath path to the file
|
|
57
|
+
* @param {Object} options options for constructing this directory item
|
|
58
|
+
* @param {Record<string, unknown>} [options.settings] the settings from the ilib-lint config that
|
|
39
59
|
* apply to this file
|
|
40
|
-
*
|
|
41
|
-
* the ilib-lint tool
|
|
60
|
+
* @param {Project} [project] the project that this directory item is part of
|
|
42
61
|
*/
|
|
43
62
|
constructor(filePath, options, project) {
|
|
44
63
|
if (!options || !filePath) {
|
|
@@ -46,36 +65,36 @@ class DirItem {
|
|
|
46
65
|
}
|
|
47
66
|
this.filePath = filePath;
|
|
48
67
|
this.settings = options.settings;
|
|
49
|
-
this.pluginMgr = options.pluginManager;
|
|
50
68
|
this.project = project;
|
|
51
69
|
}
|
|
52
70
|
|
|
53
71
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @returns {String} the file path for this source file
|
|
72
|
+
* Initialize this directory item.
|
|
73
|
+
* @returns {Promise<void>} a promise to initialize the directory item
|
|
57
74
|
*/
|
|
58
|
-
|
|
59
|
-
return
|
|
75
|
+
async init() {
|
|
76
|
+
return Promise.resolve();
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
/**
|
|
63
|
-
*
|
|
80
|
+
* Return the file path for this directory item.
|
|
64
81
|
*
|
|
65
|
-
* @returns {
|
|
66
|
-
* representations of this file
|
|
67
|
-
* @abstract
|
|
82
|
+
* @returns {String} the file path for this directory item
|
|
68
83
|
*/
|
|
69
|
-
|
|
84
|
+
getFilePath() {
|
|
85
|
+
return this.filePath;
|
|
86
|
+
}
|
|
70
87
|
|
|
71
88
|
/**
|
|
72
89
|
* Check the directory item and return a list of issues found in it.
|
|
73
90
|
*
|
|
74
|
-
* @param {Array.<
|
|
75
|
-
* @returns {Array.<Result>} a list of
|
|
91
|
+
* @param {Array.<string>} locales a set of locales to apply
|
|
92
|
+
* @returns {Array.<Result>} a list of match results
|
|
76
93
|
* @abstract
|
|
77
94
|
*/
|
|
78
|
-
findIssues(locales) {
|
|
79
|
-
|
|
95
|
+
findIssues(locales) {
|
|
96
|
+
throw new Error("Not implemented");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
80
99
|
|
|
81
100
|
export default DirItem;
|
package/src/FileType.js
CHANGED
|
@@ -17,9 +17,14 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import log4js from
|
|
20
|
+
import log4js from "log4js";
|
|
21
21
|
|
|
22
|
-
import RuleSet from
|
|
22
|
+
import RuleSet from "./RuleSet.js";
|
|
23
|
+
import { Fixer, Parser, Rule, Serializer, Transformer } from "ilib-lint-common";
|
|
24
|
+
import Project from "./Project.js";
|
|
25
|
+
|
|
26
|
+
// type imports
|
|
27
|
+
/** @ignore @typedef {import("./RuleManager.js").RuleSetDefinition} RuleSetDefinition */
|
|
23
28
|
|
|
24
29
|
const logger = log4js.getLogger("ilib-lint.FileType");
|
|
25
30
|
|
|
@@ -35,74 +40,65 @@ class FileType {
|
|
|
35
40
|
/**
|
|
36
41
|
* The lint project that this file is a part of.
|
|
37
42
|
* @type {Project}
|
|
43
|
+
* @readonly
|
|
38
44
|
*/
|
|
39
45
|
project;
|
|
40
46
|
|
|
41
47
|
/**
|
|
42
48
|
* The name or glob spec for this file type
|
|
43
49
|
* @type {String|undefined}
|
|
50
|
+
* @readonly
|
|
44
51
|
*/
|
|
45
52
|
name;
|
|
46
53
|
|
|
47
54
|
/**
|
|
48
55
|
* The list of locales to use with this file type
|
|
49
56
|
* @type {Array.<String>|undefined}
|
|
57
|
+
* @readonly
|
|
50
58
|
*/
|
|
51
59
|
locales;
|
|
52
60
|
|
|
53
61
|
/**
|
|
54
62
|
* The intermediate representation type of this file type.
|
|
55
63
|
* @type {String}
|
|
64
|
+
* @readonly
|
|
56
65
|
*/
|
|
57
66
|
type;
|
|
58
67
|
|
|
59
|
-
/**
|
|
60
|
-
* The array of names of classes of parsers to use with this file type.
|
|
61
|
-
* @type {Array.<String>|undefined}
|
|
62
|
-
*/
|
|
63
|
-
parsers = undefined;
|
|
64
|
-
|
|
65
68
|
/**
|
|
66
69
|
* The array of classes of parsers to use with this file type.
|
|
67
|
-
* @type {Array.<
|
|
70
|
+
* @type {Array.<Parser>|undefined}
|
|
71
|
+
* @readonly
|
|
68
72
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* The array of names of transformers to use with this file type.
|
|
73
|
-
* @type {Array.<String>|undefined}
|
|
74
|
-
*/
|
|
75
|
-
transformers = undefined;
|
|
73
|
+
parsers;
|
|
76
74
|
|
|
77
75
|
/**
|
|
78
76
|
* The array of instances of transformers to use with this file type.
|
|
79
77
|
* @type {Array.<Transformer>|undefined}
|
|
78
|
+
* @readonly
|
|
80
79
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The serializer to use with this file type.
|
|
85
|
-
* @type {String|undefined}
|
|
86
|
-
*/
|
|
87
|
-
serializer = undefined;
|
|
80
|
+
transformers;
|
|
88
81
|
|
|
89
82
|
/**
|
|
90
83
|
* The instance of the serializer to use with this file type.
|
|
91
84
|
* @type {Serializer|undefined}
|
|
85
|
+
* @readonly
|
|
92
86
|
*/
|
|
93
|
-
|
|
87
|
+
serializer;
|
|
94
88
|
|
|
95
89
|
/**
|
|
96
90
|
* The array of rule sets to apply to files of this type.
|
|
97
91
|
* @type {Array<String>|undefined}
|
|
92
|
+
* @readonly
|
|
98
93
|
*/
|
|
99
|
-
ruleset
|
|
94
|
+
ruleset;
|
|
100
95
|
|
|
101
96
|
/**
|
|
102
97
|
* The path template for this file type.
|
|
103
98
|
* @type {String|undefined}
|
|
99
|
+
* @readonly
|
|
104
100
|
*/
|
|
105
|
-
template
|
|
101
|
+
template;
|
|
106
102
|
|
|
107
103
|
/**
|
|
108
104
|
* Contructor a new instance of a file type.
|
|
@@ -131,14 +127,14 @@ class FileType {
|
|
|
131
127
|
* type of the rules, and the type of the transformers and serializer. If no
|
|
132
128
|
* parsers are specified, then the parser manager will be asked to find all
|
|
133
129
|
* parsers that can parse files of this type.
|
|
134
|
-
* @param {Array.<String
|
|
130
|
+
* @param {Array.<String>|String|RuleSetDefinition} [options.ruleset] a list of rule set names
|
|
135
131
|
* to use with this file type. Only rules in these rule sets that operate
|
|
136
132
|
* on the same type of intermediate representation as the parsers will
|
|
137
133
|
* be applied to the file.
|
|
138
134
|
* @param {Array.<String>} [options.transformers] an array of transformer names
|
|
139
135
|
* to apply to files of this type after the rules have been applied. Every transformer
|
|
140
136
|
* must operate on the same type of intermediate representation as the parser.
|
|
141
|
-
* @param {String
|
|
137
|
+
* @param {String} [options.serializer] the name of the serializer to use if
|
|
142
138
|
* the file has been modified by a transformer or a fixer. The serializer must
|
|
143
139
|
* operate on the same type of intermediate representation as the parser.
|
|
144
140
|
* @constructor
|
|
@@ -150,80 +146,93 @@ class FileType {
|
|
|
150
146
|
if (!options || !options.name || !options.project) {
|
|
151
147
|
throw "Missing required options to the FileType constructor";
|
|
152
148
|
}
|
|
153
|
-
["name", "project", "locales", "ruleset", "template", "parsers", "transformers", "serializer"].forEach(prop => {
|
|
154
|
-
if (typeof(options[prop]) !== 'undefined') {
|
|
155
|
-
this[prop] = options[prop];
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
149
|
|
|
159
|
-
|
|
150
|
+
/** @type {String|undefined} */
|
|
151
|
+
let inferredType = undefined;
|
|
152
|
+
|
|
153
|
+
this.name = options.name;
|
|
154
|
+
this.project = options.project;
|
|
155
|
+
this.locales = options.locales;
|
|
156
|
+
this.template = options.template;
|
|
157
|
+
|
|
158
|
+
const parserNames = options.parsers;
|
|
159
|
+
if (parserNames) {
|
|
160
160
|
const parserMgr = this.project.getParserManager();
|
|
161
|
-
this.
|
|
161
|
+
this.parsers = parserNames.map((parserName) => {
|
|
162
162
|
const parser = parserMgr.getByName(parserName);
|
|
163
163
|
if (!parser) {
|
|
164
164
|
throw `Could not find parser ${parserName} named in the configuration for filetype ${this.name}`;
|
|
165
165
|
}
|
|
166
|
-
if (!
|
|
167
|
-
|
|
166
|
+
if (!inferredType) {
|
|
167
|
+
inferredType = parserMgr.getType(parserName);
|
|
168
168
|
}
|
|
169
169
|
return parser;
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
const unknownRuleset = options.ruleset;
|
|
174
|
+
if (unknownRuleset) {
|
|
175
|
+
if (Array.isArray(unknownRuleset)) {
|
|
176
|
+
this.ruleset = unknownRuleset;
|
|
177
|
+
} else if (typeof unknownRuleset === "string") {
|
|
175
178
|
// single string -> convert to an array with a single element
|
|
176
|
-
this.ruleset = [
|
|
177
|
-
} else
|
|
179
|
+
this.ruleset = [unknownRuleset];
|
|
180
|
+
} else {
|
|
178
181
|
// rule set definition instead of a ruleset name. Save a new
|
|
179
182
|
// rule set definition in the rule manager and give it a
|
|
180
183
|
// temp name so we can refer to it and make sure that this.ruleset
|
|
181
184
|
// always points to an array of rule set names
|
|
182
185
|
const ruleMgr = this.project.getRuleManager();
|
|
183
186
|
const setName = `${this.name}-unnamed-ruleset`;
|
|
184
|
-
ruleMgr.addRuleSetDefinition(setName,
|
|
185
|
-
this.ruleset = [
|
|
187
|
+
ruleMgr.addRuleSetDefinition(setName, unknownRuleset);
|
|
188
|
+
this.ruleset = [setName];
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
const transformerNames = options.transformers;
|
|
193
|
+
if (transformerNames) {
|
|
194
|
+
const names = Array.isArray(transformerNames) ? transformerNames : [transformerNames];
|
|
191
195
|
const transformerMgr = this.project.getTransformerManager();
|
|
192
|
-
this.
|
|
196
|
+
this.transformers = names.map((transformerName) => {
|
|
193
197
|
const transformer = transformerMgr.get(transformerName);
|
|
194
198
|
if (!transformer) {
|
|
195
199
|
throw `Could not find transformer ${transformerName} named in the configuration for filetype ${this.name}`;
|
|
196
200
|
}
|
|
197
201
|
const transformerType = transformer.getType();
|
|
198
|
-
if (!
|
|
199
|
-
|
|
200
|
-
} else if (transformerType !==
|
|
201
|
-
|
|
202
|
+
if (!inferredType) {
|
|
203
|
+
inferredType = transformerType;
|
|
204
|
+
} else if (transformerType !== inferredType) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`The transformer ${transformerName} processes representations of type ${transformerType}, but the filetype ${this.name} handles representations of type ${inferredType}. The two types must match.`
|
|
207
|
+
);
|
|
202
208
|
}
|
|
203
209
|
return transformer;
|
|
204
210
|
});
|
|
205
211
|
}
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// then it the name and the settings to pass to the the serializer constructor.
|
|
210
|
-
const name = typeof(this.serializer) === 'string' ? this.serializer : this.serializer.name;
|
|
213
|
+
const serializerName = options.serializer;
|
|
214
|
+
if (serializerName) {
|
|
211
215
|
const serializerMgr = this.project.getSerializerManager();
|
|
212
|
-
this.
|
|
213
|
-
if (!this.
|
|
214
|
-
throw new Error(
|
|
216
|
+
this.serializer = serializerMgr.get(serializerName);
|
|
217
|
+
if (!this.serializer) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`Could not find or instantiate serializer ${serializerName} named in the configuration for filetype ${this.name}`
|
|
220
|
+
);
|
|
215
221
|
}
|
|
216
|
-
const serializerType = this.
|
|
217
|
-
if (!
|
|
218
|
-
|
|
219
|
-
} else if (serializerType !==
|
|
220
|
-
throw new Error(
|
|
222
|
+
const serializerType = this.serializer.getType();
|
|
223
|
+
if (!inferredType) {
|
|
224
|
+
inferredType = serializerType;
|
|
225
|
+
} else if (serializerType !== inferredType) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`The serializer ${serializerName} processes representations of type ${serializerType}, but the filetype ${this.name} handles representations of type ${inferredType}. The two types must match.`
|
|
228
|
+
);
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
|
|
224
|
-
if (!
|
|
225
|
-
|
|
232
|
+
if (!inferredType) {
|
|
233
|
+
inferredType = "string";
|
|
226
234
|
}
|
|
235
|
+
this.type = inferredType;
|
|
227
236
|
}
|
|
228
237
|
|
|
229
238
|
getName() {
|
|
@@ -253,11 +262,11 @@ class FileType {
|
|
|
253
262
|
* that can parse files with the given file name extension. If there
|
|
254
263
|
* are none available, this method returned undefined;
|
|
255
264
|
* @param {String} extension file name extension of the file being parsed
|
|
256
|
-
* @returns {
|
|
265
|
+
* @returns {Parser[]} an array of parser classes to use with
|
|
257
266
|
* files of this type.
|
|
258
267
|
*/
|
|
259
|
-
|
|
260
|
-
if (this.
|
|
268
|
+
getParsers(extension) {
|
|
269
|
+
if (this.parsers) return this.parsers;
|
|
261
270
|
const pm = this.project.getParserManager();
|
|
262
271
|
return pm.get(extension);
|
|
263
272
|
}
|
|
@@ -268,7 +277,7 @@ class FileType {
|
|
|
268
277
|
* @returns {Array.<String>} a list of rule set names
|
|
269
278
|
*/
|
|
270
279
|
getRuleSetNames() {
|
|
271
|
-
return this.ruleset;
|
|
280
|
+
return this.ruleset || [];
|
|
272
281
|
}
|
|
273
282
|
|
|
274
283
|
/**
|
|
@@ -284,14 +293,14 @@ class FileType {
|
|
|
284
293
|
|
|
285
294
|
const ruleMgr = this.project.getRuleManager();
|
|
286
295
|
const set = new RuleSet();
|
|
287
|
-
this.ruleset.forEach(ruleSetName => {
|
|
296
|
+
this.ruleset.forEach((ruleSetName) => {
|
|
288
297
|
const definitions = ruleMgr.getRuleSetDefinition(ruleSetName);
|
|
289
298
|
if (!definitions) {
|
|
290
299
|
logger.error(`Could not find rule set ${ruleSetName}`);
|
|
291
300
|
return;
|
|
292
301
|
}
|
|
293
302
|
for (let ruleName in definitions) {
|
|
294
|
-
if (typeof
|
|
303
|
+
if (typeof definitions[ruleName] === "boolean") {
|
|
295
304
|
if (definitions[ruleName]) {
|
|
296
305
|
set.addRule(ruleMgr.get(ruleName));
|
|
297
306
|
} else {
|
|
@@ -317,7 +326,7 @@ class FileType {
|
|
|
317
326
|
* with this file type, or undefined if there are none.
|
|
318
327
|
*/
|
|
319
328
|
getTransformers() {
|
|
320
|
-
return this.
|
|
329
|
+
return this.transformers;
|
|
321
330
|
}
|
|
322
331
|
|
|
323
332
|
/**
|
|
@@ -327,7 +336,17 @@ class FileType {
|
|
|
327
336
|
* file type or undefined if there is no serializer for this file type.
|
|
328
337
|
*/
|
|
329
338
|
getSerializer() {
|
|
330
|
-
return this.
|
|
339
|
+
return this.serializer;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Return an instance of the fixer class for this file type.
|
|
344
|
+
*
|
|
345
|
+
* @returns {Fixer|undefined} an instance of the fixer class for this
|
|
346
|
+
* file type or undefined if there is no fixer for this file type.
|
|
347
|
+
*/
|
|
348
|
+
getFixer() {
|
|
349
|
+
return this.project.getFixerManager().get(this.type);
|
|
331
350
|
}
|
|
332
351
|
}
|
|
333
352
|
|