@definitelytyped/dts-critic 0.0.95-next.1

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/develop.ts +446 -0
  4. package/dist/develop.d.ts +1 -0
  5. package/dist/develop.js +356 -0
  6. package/dist/develop.js.map +1 -0
  7. package/dist/dt.d.ts +1 -0
  8. package/dist/dt.js +79 -0
  9. package/dist/dt.js.map +1 -0
  10. package/dist/index.d.ts +85 -0
  11. package/dist/index.js +852 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/index.test.d.ts +1 -0
  14. package/dist/index.test.js +196 -0
  15. package/dist/index.test.js.map +1 -0
  16. package/dist/jest.config.d.ts +2 -0
  17. package/dist/jest.config.js +11 -0
  18. package/dist/jest.config.js.map +1 -0
  19. package/dt.ts +76 -0
  20. package/index.test.ts +278 -0
  21. package/index.ts +1038 -0
  22. package/jest.config.js +9 -0
  23. package/package.json +60 -0
  24. package/testsource/dts-critic.d.ts +8 -0
  25. package/testsource/dts-critic.js +1 -0
  26. package/testsource/missingDefault.d.ts +1 -0
  27. package/testsource/missingDefault.js +1 -0
  28. package/testsource/missingDtsProperty.d.ts +3 -0
  29. package/testsource/missingDtsProperty.js +4 -0
  30. package/testsource/missingDtsSignature.d.ts +7 -0
  31. package/testsource/missingDtsSignature.js +3 -0
  32. package/testsource/missingExportEquals.d.ts +1 -0
  33. package/testsource/missingExportEquals.js +5 -0
  34. package/testsource/missingJsProperty.d.ts +2 -0
  35. package/testsource/missingJsProperty.js +5 -0
  36. package/testsource/missingJsSignatureExportEquals.d.ts +6 -0
  37. package/testsource/missingJsSignatureExportEquals.js +3 -0
  38. package/testsource/missingJsSignatureNoExportEquals.d.ts +1 -0
  39. package/testsource/missingJsSignatureNoExportEquals.js +1 -0
  40. package/testsource/noErrors.d.ts +2 -0
  41. package/testsource/noErrors.js +2 -0
  42. package/testsource/parseltongue.d.ts +0 -0
  43. package/testsource/tslib.d.ts +4 -0
  44. package/testsource/typescript.d.ts +4 -0
  45. package/testsource/webpackPropertyNames.d.ts +1 -0
  46. package/testsource/webpackPropertyNames.js +12 -0
  47. package/tsconfig.json +20 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Nathan Shively-Sanders
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # dts-critic
2
+
3
+ Checks a new dts against the Javascript sources and tells you what
4
+ problems it has.
5
+
6
+ # Usage
7
+
8
+ Build the program:
9
+ ```sh
10
+ $ npm run build
11
+ ```
12
+
13
+ Run the program using node:
14
+ ```sh
15
+ $ node dist/index.js --dts=path-to-d.ts [--js=path-to-source] [--mode=mode] [--debug]
16
+ ```
17
+
18
+ If the d.ts path is to a file named `index.d.ts`, the name of the directory
19
+ will be used as the package name instead. For example
20
+ `~/dt/types/jquery/index.d.ts` will use `jquery` as the name.
21
+
22
+ `path-to-source` is optional; if you leave it off, the code will
23
+ check npm for a package with the same name as the d.ts.
24
+
25
+ ## Mode
26
+
27
+ You can run dts-critic in different modes that affect which checks will be performed:
28
+ 1. `name-only`: dts-critic will check your package name and [DefinitelyTyped header]
29
+ (https://github.com/Microsoft/definitelytyped-header-parser) (if present) against npm packages.
30
+ For example, if your declaration is for an npm package called 'cool-js-package', it will check if a
31
+ package named 'cool-js-package' actually exists in npm.
32
+
33
+ 2. `code`: in addition to the checks performed in `name-only` mode, dts-critic will check if your
34
+ declaration exports match the source JavaScript module exports.
35
+ For example, if your declaration has a default export, it will check if the JavaScript module also
36
+ has a default export.
37
+
38
+ # Current checks
39
+
40
+ ## Npm declaration
41
+ If your declaration is for an npm package:
42
+
43
+ 1. An npm package with the same name of your declaration's package must exist.
44
+ 2. If your declaration has a [Definitely Typed header](https://github.com/Microsoft/definitelytyped-header-parser)
45
+ and the header specifies a target version, the npm package must have
46
+ a matching version.
47
+ 3. If you are running under `code` mode, your declaration must also match the source JavaScript module.
48
+
49
+ ## Non-npm declaration
50
+ <!-- 2. If no local path to source is provided, an npm package with the
51
+ same name as the d.ts must exist. -->
52
+ If your declaration is for a non-npm package (in other words, if your declaration has a
53
+ [Definitely Typed header](https://github.com/Microsoft/definitelytyped-header-parser) *and*
54
+ the header specifies that the declaration file is for a non-npm package):
55
+
56
+ 1. An npm package with the same name of your declaration's package **cannot** exist.
57
+ 3. If you are running under `code` mode *and* a path to the JavaScript source file was provided, your
58
+ declaration must also match the source JavaScript module.
59
+
60
+ # Planned work
61
+
62
+ 1. Make sure your module structure fits the source.
63
+ 2. Make sure your exported symbols match the source.
64
+ 3. Make sure your types match the source types???
65
+ 6. Download source based on npm homepage (if it is github).
66
+
67
+ Note that for real use on Definitely Typed, a lot of these checks need to be pretty loose.
68
+
69
+ # Also
70
+
71
+ ```sh
72
+ $ node dist/dt.js
73
+ ```
74
+
75
+ Will run dts-critic on every directory inside `../DefinitelyTyped` and
76
+ print errors.
77
+
78
+ # Contributing
79
+
80
+ ## Testing
81
+
82
+ The tests use the [Jest](https://jestjs.io/) framework. To build and execute the tests, run:
83
+
84
+ ```sh
85
+ $ npm run test
86
+ ```
87
+
88
+ This will build the program and run jest.
package/develop.ts ADDED
@@ -0,0 +1,446 @@
1
+ import fs = require("fs");
2
+ import yargs = require("yargs");
3
+ import headerParser = require("@definitelytyped/header-parser");
4
+ import path = require("path");
5
+ import cp = require("child_process");
6
+ import {
7
+ dtsCritic,
8
+ dtToNpmName,
9
+ getNpmInfo,
10
+ parseExportErrorKind,
11
+ CriticError,
12
+ ExportErrorKind,
13
+ Mode,
14
+ checkSource,
15
+ findDtsName,
16
+ CheckOptions,
17
+ parseMode
18
+ } from "./index";
19
+
20
+ const sourcesDir = "sources";
21
+ const downloadsPath = path.join(sourcesDir, "dts-critic-internal/downloads.json");
22
+ const isNpmPath = path.join(sourcesDir, "dts-critic-internal/npm.json");
23
+
24
+ function getPackageDownloads(dtName: string): number {
25
+ const npmName = dtToNpmName(dtName);
26
+ const url = `https://api.npmjs.org/downloads/point/last-month/${npmName}`;
27
+ const result = JSON.parse(cp.execFileSync("curl", ["--silent", "-L", url], { encoding: "utf8" })) as {
28
+ downloads?: number;
29
+ };
30
+ return result.downloads || 0;
31
+ }
32
+
33
+ interface DownloadsJson {
34
+ [key: string]: number | undefined;
35
+ }
36
+
37
+ function getAllPackageDownloads(dtPath: string): DownloadsJson {
38
+ if (fs.existsSync(downloadsPath)) {
39
+ return JSON.parse(fs.readFileSync(downloadsPath, { encoding: "utf8" })) as DownloadsJson;
40
+ }
41
+
42
+ initDir(path.dirname(downloadsPath));
43
+ const downloads: DownloadsJson = {};
44
+ const dtTypesPath = getDtTypesPath(dtPath);
45
+ for (const item of fs.readdirSync(dtTypesPath)) {
46
+ const d = getPackageDownloads(item);
47
+ downloads[item] = d;
48
+ }
49
+ fs.writeFileSync(downloadsPath, JSON.stringify(downloads), { encoding: "utf8" });
50
+
51
+ return downloads;
52
+ }
53
+
54
+ function initDir(path: string): void {
55
+ if (!fs.existsSync(path)) {
56
+ fs.mkdirSync(path);
57
+ }
58
+ }
59
+
60
+ function getDtTypesPath(dtBasePath: string): string {
61
+ return path.join(dtBasePath, "types");
62
+ }
63
+
64
+ function compareDownloads(downloads: DownloadsJson, package1: string, package2: string): number {
65
+ const count1 = downloads[package1] || 0;
66
+ const count2 = downloads[package2] || 0;
67
+ return count1 - count2;
68
+ }
69
+
70
+ interface IsNpmJson {
71
+ [key: string]: boolean | undefined;
72
+ }
73
+
74
+ function getAllIsNpm(dtPath: string): IsNpmJson {
75
+ if (fs.existsSync(isNpmPath)) {
76
+ return JSON.parse(fs.readFileSync(isNpmPath, { encoding: "utf8" })) as IsNpmJson;
77
+ }
78
+ initDir(path.dirname(isNpmPath));
79
+ const isNpm: IsNpmJson = {};
80
+ const dtTypesPath = getDtTypesPath(dtPath);
81
+ for (const item of fs.readdirSync(dtTypesPath)) {
82
+ isNpm[item] = getNpmInfo(item).isNpm;
83
+ }
84
+ fs.writeFileSync(isNpmPath, JSON.stringify(isNpm), { encoding: "utf8" });
85
+ return isNpm;
86
+ }
87
+
88
+ function getPopularNpmPackages(count: number, dtPath: string): string[] {
89
+ const dtPackages = getDtNpmPackages(dtPath);
90
+ const downloads = getAllPackageDownloads(dtPath);
91
+ dtPackages.sort((a, b) => compareDownloads(downloads, a, b));
92
+ return dtPackages.slice(dtPackages.length - count);
93
+ }
94
+
95
+ function getUnpopularNpmPackages(count: number, dtPath: string): string[] {
96
+ const dtPackages = getDtNpmPackages(dtPath);
97
+ const downloads = getAllPackageDownloads(dtPath);
98
+ dtPackages.sort((a, b) => compareDownloads(downloads, a, b));
99
+ return dtPackages.slice(0, count);
100
+ }
101
+
102
+ function getDtNpmPackages(dtPath: string): string[] {
103
+ const dtPackages = fs.readdirSync(getDtTypesPath(dtPath));
104
+ const isNpmJson = getAllIsNpm(dtPath);
105
+ return dtPackages.filter(pkg => isNpmPackage(pkg, /* header */ undefined, isNpmJson));
106
+ }
107
+
108
+ function getNonNpm(args: { dtPath: string }): void {
109
+ const nonNpm: string[] = [];
110
+ const dtTypesPath = getDtTypesPath(args.dtPath);
111
+ const isNpmJson = getAllIsNpm(args.dtPath);
112
+ for (const item of fs.readdirSync(dtTypesPath)) {
113
+ const entry = path.join(dtTypesPath, item);
114
+ const dts = fs.readFileSync(entry + "/index.d.ts", "utf8");
115
+ let header;
116
+ try {
117
+ header = headerParser.parseHeaderOrFail(dts);
118
+ } catch (e) {
119
+ header = undefined;
120
+ }
121
+ if (!isNpmPackage(item, header, isNpmJson)) {
122
+ nonNpm.push(item);
123
+ }
124
+ }
125
+ console.log(`List of non-npm packages on DT:\n${nonNpm.map(name => `DT name: ${name}\n`).join("")}`);
126
+ }
127
+
128
+ interface CommonArgs {
129
+ dtPath: string;
130
+ mode: string;
131
+ enableError: string[] | undefined;
132
+ debug: boolean;
133
+ json: boolean;
134
+ }
135
+
136
+ function checkAll(args: CommonArgs): void {
137
+ const dtPackages = fs.readdirSync(getDtTypesPath(args.dtPath));
138
+ checkPackages({ packages: dtPackages, ...args });
139
+ }
140
+
141
+ function checkPopular(args: { count: number } & CommonArgs): void {
142
+ checkPackages({ packages: getPopularNpmPackages(args.count, args.dtPath), ...args });
143
+ }
144
+
145
+ function checkUnpopular(args: { count: number } & CommonArgs): void {
146
+ checkPackages({ packages: getUnpopularNpmPackages(args.count, args.dtPath), ...args });
147
+ }
148
+
149
+ function checkPackages(args: { packages: string[] } & CommonArgs): void {
150
+ const results = args.packages.map(pkg => doCheck({ package: pkg, ...args }));
151
+ printResults(results, args.json);
152
+ }
153
+
154
+ function checkPackage(args: { package: string } & CommonArgs): void {
155
+ printResults([doCheck(args)], args.json);
156
+ }
157
+
158
+ function doCheck(args: {
159
+ package: string;
160
+ dtPath: string;
161
+ mode: string;
162
+ enableError: string[] | undefined;
163
+ debug: boolean;
164
+ }): Result {
165
+ const dtPackage = args.package;
166
+ const opts = getOptions(args.mode, args.enableError || []);
167
+ try {
168
+ const dtsPath = path.join(getDtTypesPath(args.dtPath), dtPackage, "index.d.ts");
169
+ const errors = dtsCritic(dtsPath, /* sourcePath */ undefined, opts, args.debug);
170
+ return { package: args.package, output: errors };
171
+ } catch (e) {
172
+ return { package: args.package, output: e.toString() };
173
+ }
174
+ }
175
+
176
+ function getOptions(modeArg: string, enabledErrors: string[]): CheckOptions {
177
+ const mode = parseMode(modeArg);
178
+ if (!mode) {
179
+ throw new Error(`Could not find mode named '${modeArg}'.`);
180
+ }
181
+ switch (mode) {
182
+ case Mode.NameOnly:
183
+ return { mode };
184
+ case Mode.Code:
185
+ const errors = getEnabledErrors(enabledErrors);
186
+ return { mode, errors };
187
+ }
188
+ }
189
+
190
+ function getEnabledErrors(errorNames: string[]): Map<ExportErrorKind, boolean> {
191
+ const errors: ExportErrorKind[] = [];
192
+ for (const name of errorNames) {
193
+ const error = parseExportErrorKind(name);
194
+ if (error === undefined) {
195
+ throw new Error(`Could not find error named '${name}'.`);
196
+ }
197
+ errors.push(error);
198
+ }
199
+ return new Map(errors.map(err => [err, true]));
200
+ }
201
+
202
+ function checkFile(args: { jsFile: string; dtsFile: string; debug: boolean }): void {
203
+ console.log(`\tChecking JS file ${args.jsFile} and declaration file ${args.dtsFile}`);
204
+ try {
205
+ const errors = checkSource(findDtsName(args.dtsFile), args.dtsFile, args.jsFile, new Map(), args.debug);
206
+ console.log(formatErrors(errors));
207
+ } catch (e) {
208
+ console.log(e);
209
+ }
210
+ }
211
+
212
+ interface Result {
213
+ package: string;
214
+ output: CriticError[] | string;
215
+ }
216
+
217
+ function printResults(results: Result[], json: boolean): void {
218
+ if (json) {
219
+ console.log(JSON.stringify(results));
220
+ return;
221
+ }
222
+
223
+ for (const result of results) {
224
+ console.log(`\tChecking package ${result.package} ...`);
225
+ if (typeof result.output === "string") {
226
+ console.log(`Exception:\n${result.output}`);
227
+ } else {
228
+ console.log(formatErrors(result.output));
229
+ }
230
+ }
231
+ }
232
+
233
+ function formatErrors(errors: CriticError[]): string {
234
+ const lines: string[] = [];
235
+ for (const error of errors) {
236
+ lines.push("Error: " + error.message);
237
+ }
238
+ if (errors.length === 0) {
239
+ lines.push("No errors found! :)");
240
+ }
241
+ return lines.join("\n");
242
+ }
243
+
244
+ function isNpmPackage(name: string, header?: headerParser.Header, isNpmJson: IsNpmJson = {}): boolean {
245
+ if (header && header.nonNpm) return false;
246
+ const isNpm = isNpmJson[name];
247
+ if (isNpm !== undefined) {
248
+ return isNpm;
249
+ }
250
+ return getNpmInfo(name).isNpm;
251
+ }
252
+
253
+ function main() {
254
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
255
+ yargs
256
+ .usage("$0 <command>")
257
+ .command(
258
+ "check-all",
259
+ "Check source and declaration of all DT packages that are on NPM.",
260
+ {
261
+ dtPath: {
262
+ type: "string",
263
+ default: "../DefinitelyTyped",
264
+ describe: "Path of DT repository cloned locally."
265
+ },
266
+ mode: {
267
+ type: "string",
268
+ required: true,
269
+ choices: [Mode.NameOnly, Mode.Code],
270
+ describe: "Mode that defines which group of checks will be made."
271
+ },
272
+ enableError: {
273
+ type: "array",
274
+ string: true,
275
+ describe: "Enable checking for a specific export error."
276
+ },
277
+ debug: {
278
+ type: "boolean",
279
+ default: false,
280
+ describe: "Turn debug logging on."
281
+ },
282
+ json: {
283
+ type: "boolean",
284
+ default: false,
285
+ describe: "Format output result as json."
286
+ }
287
+ },
288
+ checkAll
289
+ )
290
+ .command(
291
+ "check-popular",
292
+ "Check source and declaration of most popular DT packages that are on NPM.",
293
+ {
294
+ count: {
295
+ alias: "c",
296
+ type: "number",
297
+ required: true,
298
+ describe: "Number of packages to be checked."
299
+ },
300
+ dtPath: {
301
+ type: "string",
302
+ default: "../DefinitelyTyped",
303
+ describe: "Path of DT repository cloned locally."
304
+ },
305
+ mode: {
306
+ type: "string",
307
+ required: true,
308
+ choices: [Mode.NameOnly, Mode.Code],
309
+ describe: "Mode that defines which group of checks will be made."
310
+ },
311
+ enableError: {
312
+ type: "array",
313
+ string: true,
314
+ describe: "Enable checking for a specific export error."
315
+ },
316
+ debug: {
317
+ type: "boolean",
318
+ default: false,
319
+ describe: "Turn debug logging on."
320
+ },
321
+ json: {
322
+ type: "boolean",
323
+ default: false,
324
+ describe: "Format output result as json."
325
+ }
326
+ },
327
+ checkPopular
328
+ )
329
+ .command(
330
+ "check-unpopular",
331
+ "Check source and declaration of least popular DT packages that are on NPM.",
332
+ {
333
+ count: {
334
+ alias: "c",
335
+ type: "number",
336
+ required: true,
337
+ describe: "Number of packages to be checked."
338
+ },
339
+ dtPath: {
340
+ type: "string",
341
+ default: "../DefinitelyTyped",
342
+ describe: "Path of DT repository cloned locally."
343
+ },
344
+ mode: {
345
+ type: "string",
346
+ required: true,
347
+ choices: [Mode.NameOnly, Mode.Code],
348
+ describe: "Mode that defines which group of checks will be made."
349
+ },
350
+ enableError: {
351
+ type: "array",
352
+ string: true,
353
+ describe: "Enable checking for a specific export error."
354
+ },
355
+ debug: {
356
+ type: "boolean",
357
+ default: false,
358
+ describe: "Turn debug logging on."
359
+ },
360
+ json: {
361
+ type: "boolean",
362
+ default: false,
363
+ describe: "Format output result as json."
364
+ }
365
+ },
366
+ checkUnpopular
367
+ )
368
+ .command(
369
+ "check-package",
370
+ "Check source and declaration of a DT package.",
371
+ {
372
+ package: {
373
+ alias: "p",
374
+ type: "string",
375
+ required: true,
376
+ describe: "DT name of a package."
377
+ },
378
+ dtPath: {
379
+ type: "string",
380
+ default: "../DefinitelyTyped",
381
+ describe: "Path of DT repository cloned locally."
382
+ },
383
+ mode: {
384
+ type: "string",
385
+ required: true,
386
+ choices: [Mode.NameOnly, Mode.Code],
387
+ describe: "Mode that defines which group of checks will be made."
388
+ },
389
+ enableError: {
390
+ type: "array",
391
+ string: true,
392
+ describe: "Enable checking for a specific export error."
393
+ },
394
+ debug: {
395
+ type: "boolean",
396
+ default: false,
397
+ describe: "Turn debug logging on."
398
+ },
399
+ json: {
400
+ type: "boolean",
401
+ default: false,
402
+ describe: "Format output result as json."
403
+ }
404
+ },
405
+ checkPackage
406
+ )
407
+ .command(
408
+ "check-file",
409
+ "Check a JavaScript file and its matching declaration file.",
410
+ {
411
+ jsFile: {
412
+ alias: "j",
413
+ type: "string",
414
+ required: true,
415
+ describe: "Path of JavaScript file."
416
+ },
417
+ dtsFile: {
418
+ alias: "d",
419
+ type: "string",
420
+ required: true,
421
+ describe: "Path of declaration file."
422
+ },
423
+ debug: {
424
+ type: "boolean",
425
+ default: false,
426
+ describe: "Turn debug logging on."
427
+ }
428
+ },
429
+ checkFile
430
+ )
431
+ .command(
432
+ "get-non-npm",
433
+ "Get list of DT packages whose source package is not on NPM",
434
+ {
435
+ dtPath: {
436
+ type: "string",
437
+ default: "../DefinitelyTyped",
438
+ describe: "Path of DT repository cloned locally."
439
+ }
440
+ },
441
+ getNonNpm
442
+ )
443
+ .demandCommand(1)
444
+ .help().argv;
445
+ }
446
+ main();
@@ -0,0 +1 @@
1
+ export {};