-
Notifications
You must be signed in to change notification settings - Fork 0
/
run-checks.mjs
152 lines (143 loc) · 5.07 KB
/
run-checks.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import * as fs from 'fs';
import grep from 'grep1';
if ( process.argv.length !== 3 ) {
console.error( 'To be called with 1 argument - extension name' );
process.exit( 1 );
}
const extName = process.argv[2];
const i18nPathBase = extName + '/i18n';
const checkKey = ( key ) => {
return new Promise(
( resolve, reject ) => {
const keyRegex = '(\\s|^|\'|\\")' + key + '(\s|$|,|\'|\\")';
grep(
// Ignore any external libraries that have been loaded with
// --exclude-dir
[ '-r', '--exclude=*.json',
'--exclude-dir=node_modules', '--exclude-dir=vendor',
'-E', keyRegex, extName + '/' ],
( err, stdout, stderr ) => {
if ( err !== null || !stdout.includes( key ) ) {
reject();
} else {
resolve();
}
}
);
}
);
};
const extractKnownKeys = ( extensionData ) => {
// Some keys are used automatically based on extension.json:
// - the action-* and right-* messages for any permissions declared in
// `AvailableRights`
// - the message declared in `descriptionmsg`
const extensionJSON = JSON.parse( extensionData );
const knownKeys = [];
if ( extensionJSON.descriptionmsg !== undefined ) {
knownKeys.push( extensionJSON.descriptionmsg );
}
if ( extensionJSON.namemsg !== undefined ) {
knownKeys.push( extensionJSON.namemsg );
}
if ( extensionJSON.AvailableRights !== undefined ) {
extensionJSON.AvailableRights.forEach(
( right ) => {
knownKeys.push( 'action-' + right );
knownKeys.push( 'right-' + right );
}
);
}
return knownKeys;
};
const extractPossiblyUsedKeys = ( extensionData ) => {
// Some keys are *sometimes* used automatically based on extension.json:
// - the name of any special page, when converted to lowercase, is the
// default message for that description of that special page. See
// SpecialPage::getDescription(). But, special pages are allowed to
// override that to return something else. Thus, if there are special
// pages, recognize the default description messages as "used", but
// don't also require that those default descriptions exist.
const extensionJSON = JSON.parse( extensionData );
const possiblyUnusedKeys = [];
if ( extensionJSON.SpecialPages !== undefined ) {
const pageNames = Object.keys( extensionJSON.SpecialPages );
possiblyUnusedKeys.push( ...pageNames.map( ( s ) => s.toLowerCase() ) );
}
return possiblyUnusedKeys;
};
const getI18nKeys = ( i18nData ) => {
const i18nJSON = JSON.parse( i18nData );
delete i18nJSON['@metadata'];
return Object.keys( i18nJSON );
};
const runWithData = ( knownKeys, possiblyUnusedKeys, i18nData ) => {
const keys = getI18nKeys( i18nData );
// console.log( keys );
const unfoundKeys = [];
const keyPromises = keys.map(
( k ) => { return new Promise( ( resolve ) => {
if ( knownKeys.includes( k ) ) {
// No need to `grep` for a key that gets used via extension.json
resolve();
return;
}
if ( possiblyUnusedKeys.includes( k ) ) {
// No need to `grep` for a key that *might* get automatically
// used via extension.json
resolve();
return;
}
checkKey( k ).then(
resolve,
() => {
unfoundKeys.push( k );
resolve();
}
)
} ); }
);
const undefinedKeys = knownKeys.filter( ( k ) => !keys.includes( k ) );
let hasError = false;
if ( undefinedKeys.length ) {
console.log( 'The following keys used by extension.json are undefined:' );
console.log( undefinedKeys );
hasError = true;
}
Promise.allSettled( keyPromises ).then( () => {
if ( unfoundKeys.length !== 0 ) {
console.log( 'Could not find uses of the following keys:' );
console.log( unfoundKeys );
hasError = true;
} else {
console.log( 'All keys found successfully!' );
}
process.exit( hasError ? 1 : 0 );
} );
};
console.log( 'Starting i18n key check for: ' + extName );
fs.readFile(
extName + '/extension.json',
'utf8',
( err, extData ) => {
if ( err ) {
console.error( err );
process.exit( 1 );
}
fs.readFile(
i18nPathBase + '/en.json',
'utf8',
( err2, i18nData ) => {
if ( err2 ) {
console.error( err2 );
process.exit( 1 );
}
runWithData(
extractKnownKeys( extData ),
extractPossiblyUsedKeys( extData ),
i18nData
);
}
)
}
);