@transifex/native 3.2.2 → 4.1.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/README.md +1 -1
- package/dist/browser.native.js +2 -8
- package/dist/browser.native.js.LICENSE.txt +6 -0
- package/dist/browser.native.js.map +1 -1
- package/dist/node.native.d.ts +2 -0
- package/dist/node.native.js +2 -8
- package/dist/node.native.js.LICENSE.txt +20 -0
- package/dist/node.native.js.map +1 -1
- package/package.json +13 -14
- package/src/TxNative.js +2 -1
- package/src/index.d.ts +2 -0
- package/src/index.js +1 -0
- package/src/plurals.js +204 -0
- package/src/utils.js +0 -132
- package/webpack.browser.js +0 -16
- package/webpack.node.js +1 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transifex/native",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "i18n framework using Transifex Native",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"transifex",
|
|
@@ -31,31 +31,30 @@
|
|
|
31
31
|
"url": "https://github.com/transifex/transifex-javascript/issues"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
|
-
"node": ">=
|
|
34
|
+
"node": ">=14.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@babel/core": "^7.17.
|
|
38
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
39
|
-
"@babel/preset-env": "^7.
|
|
40
|
-
"@babel/runtime": "^7.17.
|
|
37
|
+
"@babel/core": "^7.17.10",
|
|
38
|
+
"@babel/plugin-transform-runtime": "^7.17.10",
|
|
39
|
+
"@babel/preset-env": "^7.17.10",
|
|
40
|
+
"@babel/runtime": "^7.17.9",
|
|
41
41
|
"babel-eslint": "^10.1.0",
|
|
42
|
-
"babel-loader": "^8.
|
|
42
|
+
"babel-loader": "^8.2.5",
|
|
43
43
|
"chai": "^4.3.4",
|
|
44
44
|
"eslint": "^7.28.0",
|
|
45
45
|
"eslint-config-airbnb-base": "^14.2.0",
|
|
46
|
-
"eslint-
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"mocha": "^9.1.3",
|
|
46
|
+
"eslint-plugin-import": "^2.26.0",
|
|
47
|
+
"glob": "^8.0.1",
|
|
48
|
+
"mocha": "^10.0.0",
|
|
50
49
|
"nock": "^13.1.0",
|
|
51
50
|
"nyc": "^15.1.0",
|
|
52
51
|
"source-map-support": "^0.5.19",
|
|
53
|
-
"webpack": "^
|
|
54
|
-
"webpack-cli": "^
|
|
52
|
+
"webpack": "^5.72.0",
|
|
53
|
+
"webpack-cli": "^4.9.2"
|
|
55
54
|
},
|
|
56
55
|
"dependencies": {
|
|
57
56
|
"@messageformat/core": "^3.0.0",
|
|
58
|
-
"axios": "^0.
|
|
57
|
+
"axios": "^0.27.2",
|
|
59
58
|
"md5": "^2.3.0"
|
|
60
59
|
}
|
|
61
60
|
}
|
package/src/TxNative.js
CHANGED
|
@@ -7,7 +7,7 @@ import SourceErrorPolicy from './policies/SourceErrorPolicy';
|
|
|
7
7
|
import SourceStringPolicy from './policies/SourceStringPolicy';
|
|
8
8
|
import MessageFormatRenderer from './renderers/MessageFormatRenderer';
|
|
9
9
|
import {
|
|
10
|
-
generateKey, isString,
|
|
10
|
+
generateKey, isString, escape, generateHashedKey, sleep,
|
|
11
11
|
} from './utils';
|
|
12
12
|
import {
|
|
13
13
|
sendEvent,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
FETCHING_LOCALES, LOCALES_FETCHED, LOCALES_FETCH_FAILED,
|
|
16
16
|
LOCALE_CHANGED,
|
|
17
17
|
} from './events';
|
|
18
|
+
import { isPluralized } from './plurals';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Native instance, combines functionality from
|
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
package/src/plurals.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const PLURAL_RULES = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
2
|
+
|
|
3
|
+
// The `_consumeFOO` functions take an input, "consume" a part of it to
|
|
4
|
+
// produce the first return value and return the "unconsumed" part of the input
|
|
5
|
+
// as the second return value
|
|
6
|
+
|
|
7
|
+
// '{cnt, plural, one {ONE} other {OTHER}}' =>
|
|
8
|
+
// ['cnt', 'one {ONE} other {OTHER}']
|
|
9
|
+
function consumePreamble(string) {
|
|
10
|
+
if (!(string.length > 1
|
|
11
|
+
&& string[0] === '{'
|
|
12
|
+
&& string[string.length - 1] === '}')
|
|
13
|
+
) {
|
|
14
|
+
return [null, null];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
18
|
+
// ^^^^^^^^^^^
|
|
19
|
+
const firstCommaPos = string.indexOf(',');
|
|
20
|
+
if (firstCommaPos === -1) { return [null, null]; }
|
|
21
|
+
const secondCommaPos = string.indexOf(',', firstCommaPos + 1);
|
|
22
|
+
if (secondCommaPos === -1) { return [null, null]; }
|
|
23
|
+
|
|
24
|
+
const varName = string.substring(1, firstCommaPos).trim();
|
|
25
|
+
const keyword = string
|
|
26
|
+
.substring(firstCommaPos + 1, secondCommaPos)
|
|
27
|
+
.trim();
|
|
28
|
+
|
|
29
|
+
// Make sure `varName` is a proper variable and that `keyword` is 'plural'
|
|
30
|
+
if (!/^[\w_]+$/.test(varName) || keyword !== 'plural') {
|
|
31
|
+
return [null, null];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
varName,
|
|
36
|
+
string.substring(secondCommaPos + 1, string.length - 1).trim()];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 'one {ONE} other {OTHER}' => ['one', '{ONE} other {OTHER}']
|
|
40
|
+
// 'other {OTHER}' => ['other', '{OTHER}']
|
|
41
|
+
function consumeRule(string) {
|
|
42
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
43
|
+
// ^^^^^
|
|
44
|
+
const leftBracketPos = string.indexOf('{');
|
|
45
|
+
if (leftBracketPos === -1) { return [null, null]; }
|
|
46
|
+
let rule = string.substring(0, leftBracketPos).trim();
|
|
47
|
+
if (rule[0] === '=') {
|
|
48
|
+
rule = rule.substring(1);
|
|
49
|
+
if (!Number.isNaN(parseInt(rule, 10)) && parseInt(rule, 10) === parseFloat(rule)) {
|
|
50
|
+
rule = parseInt(rule, 10);
|
|
51
|
+
rule = PLURAL_RULES[rule];
|
|
52
|
+
if (rule === undefined) { return [null, null]; }
|
|
53
|
+
} else { return [null, null]; }
|
|
54
|
+
} else if (PLURAL_RULES.indexOf(rule) === -1) {
|
|
55
|
+
return [null, null];
|
|
56
|
+
}
|
|
57
|
+
return [rule, string.substring(leftBracketPos)];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// '{ONE} other {OTHER}' => ['ONE', 'other {OTHER}']
|
|
61
|
+
// '{OTHER}' => ['OTHER', '']
|
|
62
|
+
function consumePlural(string) {
|
|
63
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
64
|
+
// ^^^^^
|
|
65
|
+
let [bracketCount, escaping] = [0, false];
|
|
66
|
+
let ptr = 0;
|
|
67
|
+
while (ptr < string.length) {
|
|
68
|
+
const char = string[ptr];
|
|
69
|
+
if (char === "'") {
|
|
70
|
+
const peek = string[ptr + 1];
|
|
71
|
+
if (peek === "'") {
|
|
72
|
+
ptr += 1;
|
|
73
|
+
} else if (escaping) {
|
|
74
|
+
escaping = false;
|
|
75
|
+
} else if (peek === '{' || peek === '}') {
|
|
76
|
+
escaping = true;
|
|
77
|
+
}
|
|
78
|
+
} else if (char === '{') {
|
|
79
|
+
if (!escaping) {
|
|
80
|
+
bracketCount += 1;
|
|
81
|
+
}
|
|
82
|
+
} else if (char === '}') {
|
|
83
|
+
if (!escaping) {
|
|
84
|
+
bracketCount -= 1;
|
|
85
|
+
}
|
|
86
|
+
if (bracketCount === 0) {
|
|
87
|
+
return [string.substring(1, ptr), string.substring(ptr + 1).trim()];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
ptr += 1;
|
|
91
|
+
}
|
|
92
|
+
return [null, null];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Make an effort to split (explode) an ICU message format string into
|
|
96
|
+
* plurals. Works on only the simplest case, when a gettext-like statement
|
|
97
|
+
* like:
|
|
98
|
+
*
|
|
99
|
+
* ngettext("You have %d new message", "You have %d new messages", cnt)
|
|
100
|
+
*
|
|
101
|
+
* was converted into an ICU message like:
|
|
102
|
+
*
|
|
103
|
+
* {cnt, plural,
|
|
104
|
+
* one {You have # new message}
|
|
105
|
+
* other {You have # new messages}}
|
|
106
|
+
*
|
|
107
|
+
* The return value is a size-2 array with the name of the variable used and
|
|
108
|
+
* an object mapping the plural rule to the plural version, eg:
|
|
109
|
+
*
|
|
110
|
+
* ['cnt',
|
|
111
|
+
* {one: 'You have # new message',
|
|
112
|
+
* other: 'You have # new messages'}]
|
|
113
|
+
*
|
|
114
|
+
* If the string can't be converted for any reason, `null` will be used in
|
|
115
|
+
* place of the variable name and only rule=5 (other) will be returned.
|
|
116
|
+
*
|
|
117
|
+
* @function
|
|
118
|
+
* @param {String} string
|
|
119
|
+
* @returns {[String, {rule: String}]} variable name and plural forms pair
|
|
120
|
+
*
|
|
121
|
+
* */
|
|
122
|
+
export function explodePlurals(string) {
|
|
123
|
+
const defaultResult = [null, { other: string }];
|
|
124
|
+
|
|
125
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
126
|
+
// ^^^^^^^^^^^^
|
|
127
|
+
// eslint-disable-next-line prefer-const
|
|
128
|
+
let [varName, remaining] = consumePreamble(string);
|
|
129
|
+
if (varName == null) { return defaultResult; }
|
|
130
|
+
|
|
131
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
132
|
+
// ^^^^^^^^^
|
|
133
|
+
const plurals = {};
|
|
134
|
+
let rule;
|
|
135
|
+
let plural;
|
|
136
|
+
[rule, remaining] = consumeRule(remaining);
|
|
137
|
+
if (rule == null) { return defaultResult; }
|
|
138
|
+
|
|
139
|
+
[plural, remaining] = consumePlural(remaining.trim());
|
|
140
|
+
if (plural == null) { return defaultResult; }
|
|
141
|
+
plurals[rule] = plural;
|
|
142
|
+
|
|
143
|
+
while (remaining.trim()) {
|
|
144
|
+
// {cnt, plural, one {foo} other {foos}}
|
|
145
|
+
// ^^^^^^^^^^^^
|
|
146
|
+
[rule, remaining] = consumeRule(remaining.trim());
|
|
147
|
+
if (rule == null) { return defaultResult; }
|
|
148
|
+
[plural, remaining] = consumePlural(remaining.trim());
|
|
149
|
+
if (plural == null) { return defaultResult; }
|
|
150
|
+
plurals[rule] = plural;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if ((Object.keys(plurals).length === 1 && !('other' in plurals))
|
|
154
|
+
|| (!('one' in plurals && 'other' in plurals))
|
|
155
|
+
) {
|
|
156
|
+
return defaultResult;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return [varName, plurals];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Convert a plurals object into an ICU plural statement, eg:
|
|
163
|
+
*
|
|
164
|
+
* {one: "ONE", other: "OTHER"} => '{???, plural, one {ONE} other {OTHER}}'
|
|
165
|
+
*
|
|
166
|
+
* @function
|
|
167
|
+
* @param {Object} plurals
|
|
168
|
+
* @param {string} [count='???']
|
|
169
|
+
* @returns {String}
|
|
170
|
+
*/
|
|
171
|
+
export function implodePlurals(plurals, count = '???') {
|
|
172
|
+
const result = [`{${count}, plural,`];
|
|
173
|
+
// Lets get the rules in order
|
|
174
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
175
|
+
for (const rule of PLURAL_RULES) {
|
|
176
|
+
if (rule in plurals) {
|
|
177
|
+
const plural = plurals[rule];
|
|
178
|
+
result.push(` ${rule} {${plural}}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
result.push('}');
|
|
182
|
+
return result.join('');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Determine if a string is an ICU pluralized string. Only works for the
|
|
187
|
+
* simplest possible case, where the string starts and ends in '{' and '}'
|
|
188
|
+
* respectively and the root ICU directive is a plural statement. ie This
|
|
189
|
+
*
|
|
190
|
+
* {cnt, plural, one {ONE} other {OTHER}}
|
|
191
|
+
*
|
|
192
|
+
* will return 'true' while this
|
|
193
|
+
*
|
|
194
|
+
* hello {cnt, plural, one {ONE} other {OTHER}}
|
|
195
|
+
*
|
|
196
|
+
* will return 'false'.
|
|
197
|
+
*
|
|
198
|
+
* @param {String} string
|
|
199
|
+
* @returns {Boolean}
|
|
200
|
+
*/
|
|
201
|
+
export function isPluralized(string) {
|
|
202
|
+
const plurals = explodePlurals(string)[1];
|
|
203
|
+
return Object.keys(plurals).length > 1;
|
|
204
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -84,138 +84,6 @@ export function normalizeLocale(locale) {
|
|
|
84
84
|
return normalizedLocale;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const PLURAL_RULES = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
88
|
-
|
|
89
|
-
function _consumePreamble(string) {
|
|
90
|
-
if (!(string.length > 1
|
|
91
|
-
&& string[0] === '{'
|
|
92
|
-
&& string[string.length - 1] === '}')
|
|
93
|
-
) {
|
|
94
|
-
return [null, null];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// {cnt, plural, one {foo} other {foos}}
|
|
98
|
-
// ^^^^^^^^^^^
|
|
99
|
-
const firstCommaPos = string.indexOf(',');
|
|
100
|
-
if (firstCommaPos === -1) { return [null, null]; }
|
|
101
|
-
const secondCommaPos = string.indexOf(',', firstCommaPos + 1);
|
|
102
|
-
if (secondCommaPos === -1) { return [null, null]; }
|
|
103
|
-
|
|
104
|
-
const varName = string.substring(1, firstCommaPos).trim();
|
|
105
|
-
const keyword = string
|
|
106
|
-
.substring(firstCommaPos + 1, secondCommaPos)
|
|
107
|
-
.trim();
|
|
108
|
-
|
|
109
|
-
// Make sure `keyword` is 'plural'
|
|
110
|
-
if (keyword !== 'plural') { return [null, null]; }
|
|
111
|
-
|
|
112
|
-
return [
|
|
113
|
-
varName,
|
|
114
|
-
string.substring(secondCommaPos + 1, string.length - 1).trim()];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function _consumeRule(string) {
|
|
118
|
-
// {cnt, plural, one {foo} other {foos}}
|
|
119
|
-
// ^^^^^
|
|
120
|
-
const leftBracketPos = string.indexOf('{');
|
|
121
|
-
if (leftBracketPos === -1) { return [null, null]; }
|
|
122
|
-
let rule = string.substring(0, leftBracketPos).trim();
|
|
123
|
-
if (rule[0] === '=') {
|
|
124
|
-
rule = rule.substring(1);
|
|
125
|
-
if (!Number.isNaN(parseInt(rule, 10)) && parseInt(rule, 10) === parseFloat(rule)) {
|
|
126
|
-
rule = parseInt(rule, 10);
|
|
127
|
-
rule = PLURAL_RULES[rule];
|
|
128
|
-
if (rule === undefined) { return [null, null]; }
|
|
129
|
-
} else { return [null, null]; }
|
|
130
|
-
} else if (PLURAL_RULES.indexOf(rule) === -1) {
|
|
131
|
-
return [null, null];
|
|
132
|
-
}
|
|
133
|
-
return [rule, string.substring(leftBracketPos)];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function _consumePlural(string) {
|
|
137
|
-
// {cnt, plural, one {foo} other {foos}}
|
|
138
|
-
// ^^^^^
|
|
139
|
-
let [bracketCount, escaping] = [0, false];
|
|
140
|
-
let ptr = 0;
|
|
141
|
-
while (ptr < string.length) {
|
|
142
|
-
const char = string[ptr];
|
|
143
|
-
if (char === "'") {
|
|
144
|
-
const peek = string[ptr + 1];
|
|
145
|
-
if (peek === "'") {
|
|
146
|
-
ptr += 1;
|
|
147
|
-
} else if (escaping) {
|
|
148
|
-
escaping = false;
|
|
149
|
-
} else if (peek === '{' || peek === '}') {
|
|
150
|
-
escaping = true;
|
|
151
|
-
}
|
|
152
|
-
} else if (char === '{') {
|
|
153
|
-
if (!escaping) {
|
|
154
|
-
bracketCount += 1;
|
|
155
|
-
}
|
|
156
|
-
} else if (char === '}') {
|
|
157
|
-
if (!escaping) {
|
|
158
|
-
bracketCount -= 1;
|
|
159
|
-
}
|
|
160
|
-
if (bracketCount === 0) {
|
|
161
|
-
return [string.substring(1, ptr), string.substring(ptr + 1).trim()];
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
ptr += 1;
|
|
165
|
-
}
|
|
166
|
-
return [null, null];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Determine if a string is an ICU pluralized string. Only works for the
|
|
171
|
-
* simplest possible case, where the string starts and ends in '{' and '}'
|
|
172
|
-
* respectively and the root ICU directive is a plural statement. ie This
|
|
173
|
-
*
|
|
174
|
-
* {cnt, plural, one {ONE} other {OTHER}}
|
|
175
|
-
*
|
|
176
|
-
* will return 'true' while this
|
|
177
|
-
*
|
|
178
|
-
* hello {cnt, plural, one {ONE} other {OTHER}}
|
|
179
|
-
*
|
|
180
|
-
* will return 'false'.
|
|
181
|
-
*
|
|
182
|
-
* @param {String} string
|
|
183
|
-
* @returns {Boolean}
|
|
184
|
-
*/
|
|
185
|
-
export function isPluralized(string) {
|
|
186
|
-
const [varName, rest] = _consumePreamble(string);
|
|
187
|
-
let remaining = rest;
|
|
188
|
-
if (varName == null) { return false; }
|
|
189
|
-
|
|
190
|
-
const plurals = {};
|
|
191
|
-
let rule;
|
|
192
|
-
let plural;
|
|
193
|
-
[rule, remaining] = _consumeRule(remaining);
|
|
194
|
-
if (rule == null) { return false; }
|
|
195
|
-
|
|
196
|
-
[plural, remaining] = _consumePlural(remaining.trim());
|
|
197
|
-
if (plural == null) { return false; }
|
|
198
|
-
plurals[rule] = plural;
|
|
199
|
-
|
|
200
|
-
while (remaining.trim()) {
|
|
201
|
-
// {cnt, plural, one {foo} other {foos}}
|
|
202
|
-
// ^^^^^^^^^^^^
|
|
203
|
-
[rule, remaining] = _consumeRule(remaining.trim());
|
|
204
|
-
if (rule == null) { return false; }
|
|
205
|
-
[plural, remaining] = _consumePlural(remaining.trim());
|
|
206
|
-
if (plural == null) { return false; }
|
|
207
|
-
plurals[rule] = plural;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if ((Object.keys(plurals).length === 1 && !('other' in plurals))
|
|
211
|
-
|| (!('one' in plurals && 'other' in plurals))
|
|
212
|
-
) {
|
|
213
|
-
return false;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
87
|
/**
|
|
220
88
|
* Sleep in msec
|
|
221
89
|
*
|
package/webpack.browser.js
CHANGED
|
@@ -21,22 +21,6 @@ module.exports = {
|
|
|
21
21
|
],
|
|
22
22
|
module: {
|
|
23
23
|
rules: [
|
|
24
|
-
{
|
|
25
|
-
test: /\.(js|jsx)$/,
|
|
26
|
-
enforce: 'pre',
|
|
27
|
-
use: [
|
|
28
|
-
{
|
|
29
|
-
options: {
|
|
30
|
-
cache: true,
|
|
31
|
-
configFile: '.eslintrc.json',
|
|
32
|
-
formatter: 'eslint-formatter-pretty',
|
|
33
|
-
emitWarning: true,
|
|
34
|
-
failOnError: true,
|
|
35
|
-
},
|
|
36
|
-
loader: 'eslint-loader',
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
},
|
|
40
24
|
{
|
|
41
25
|
test: /\.(js|jsx)$/,
|
|
42
26
|
exclude: /(node_modules)/,
|
package/webpack.node.js
CHANGED
|
@@ -20,23 +20,6 @@ module.exports = {
|
|
|
20
20
|
}),
|
|
21
21
|
],
|
|
22
22
|
module: {
|
|
23
|
-
rules: [
|
|
24
|
-
{
|
|
25
|
-
test: /\.(js|jsx)$/,
|
|
26
|
-
enforce: 'pre',
|
|
27
|
-
use: [
|
|
28
|
-
{
|
|
29
|
-
options: {
|
|
30
|
-
cache: true,
|
|
31
|
-
configFile: '.eslintrc.json',
|
|
32
|
-
formatter: 'eslint-formatter-pretty',
|
|
33
|
-
emitWarning: true,
|
|
34
|
-
failOnError: true,
|
|
35
|
-
},
|
|
36
|
-
loader: 'eslint-loader',
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
},
|
|
40
|
-
],
|
|
23
|
+
rules: [],
|
|
41
24
|
},
|
|
42
25
|
};
|