@definitelytyped/dts-critic 0.1.2 → 0.1.3
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/CHANGELOG.md +8 -0
- package/README.md +0 -9
- package/dist/index.d.ts +2 -32
- package/dist/index.js +15 -245
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +0 -50
- package/dist/index.test.js.map +1 -1
- package/index.ts +16 -266
- package/package.json +3 -15
- package/develop.ts +0 -446
- package/dist/develop.d.ts +0 -1
- package/dist/develop.js +0 -354
- package/dist/develop.js.map +0 -1
- package/dist/dt.d.ts +0 -1
- package/dist/dt.js +0 -79
- package/dist/dt.js.map +0 -1
- package/dt.ts +0 -76
package/index.ts
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import yargs = require("yargs");
|
|
2
2
|
import headerParser = require("@definitelytyped/header-parser");
|
|
3
3
|
import fs = require("fs");
|
|
4
|
-
import cp = require("child_process");
|
|
5
4
|
import path = require("path");
|
|
6
|
-
import semver = require("semver");
|
|
7
|
-
import { sync as commandExistsSync } from "command-exists";
|
|
8
5
|
import ts from "typescript";
|
|
9
|
-
import * as tmp from "tmp";
|
|
10
|
-
import which from "which";
|
|
11
6
|
|
|
12
7
|
export enum ErrorKind {
|
|
13
|
-
/** Declaration is marked as npm in header and has no matching npm package. */
|
|
14
|
-
NoMatchingNpmPackage = "NoMatchingNpmPackage",
|
|
15
|
-
/** Declaration has no npm package matching specified version. */
|
|
16
|
-
NoMatchingNpmVersion = "NoMatchingNpmVersion",
|
|
17
|
-
/** Declaration is not for an npm package, but has a name that conflicts with an existing npm package. */
|
|
18
|
-
NonNpmHasMatchingPackage = "NonNpmHasMatchingPackage",
|
|
19
8
|
/** Declaration needs to use `export =` to match the JavaScript module's behavior. */
|
|
20
9
|
NeedsExportEquals = "NeedsExportEquals",
|
|
21
10
|
/** Declaration has a default export, but JavaScript module does not have a default export. */
|
|
@@ -30,70 +19,31 @@ export enum ErrorKind {
|
|
|
30
19
|
DtsSignatureNotInJs = "DtsSignatureNotInJs",
|
|
31
20
|
}
|
|
32
21
|
|
|
33
|
-
export
|
|
34
|
-
/** Checks based only on the package name and on the declaration's DefinitelyTyped header. */
|
|
35
|
-
NameOnly = "name-only",
|
|
36
|
-
/** Checks based on the source JavaScript code, in addition to the checks performed in name-only mode. */
|
|
37
|
-
Code = "code",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function parseMode(mode: string): Mode | undefined {
|
|
41
|
-
switch (mode) {
|
|
42
|
-
case Mode.NameOnly:
|
|
43
|
-
return Mode.NameOnly;
|
|
44
|
-
case Mode.Code:
|
|
45
|
-
return Mode.Code;
|
|
46
|
-
}
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type CheckOptions = NameOnlyOptions | CodeOptions;
|
|
51
|
-
export interface NameOnlyOptions {
|
|
52
|
-
mode: Mode.NameOnly;
|
|
53
|
-
}
|
|
54
|
-
export interface CodeOptions {
|
|
55
|
-
mode: Mode.Code;
|
|
22
|
+
export interface CheckOptions {
|
|
56
23
|
errors: Map<ExportErrorKind, boolean>;
|
|
57
24
|
}
|
|
58
25
|
|
|
59
26
|
export type ExportErrorKind = ExportError["kind"];
|
|
60
27
|
|
|
61
|
-
const defaultOpts: CheckOptions = { mode: Mode.NameOnly };
|
|
62
|
-
|
|
63
28
|
export function dtsCritic(
|
|
64
29
|
dtsPath: string,
|
|
65
|
-
sourcePath
|
|
66
|
-
options: CheckOptions =
|
|
30
|
+
sourcePath: string,
|
|
31
|
+
options: CheckOptions = { errors: new Map() },
|
|
67
32
|
debug = false,
|
|
68
33
|
): CriticError[] {
|
|
69
|
-
if (!
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
if (!commandExistsSync("npm")) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
"You need to have npm installed to run dts-critic, you can get it from https://www.npmjs.com/get-npm",
|
|
77
|
-
);
|
|
34
|
+
if (!sourcePath && require.main !== module) {
|
|
35
|
+
// dtslint will issue an error.
|
|
36
|
+
return [];
|
|
78
37
|
}
|
|
79
|
-
|
|
80
38
|
const name = findDtsName(dtsPath);
|
|
81
39
|
const packageJsonPath = path.join(path.dirname(path.resolve(dtsPath)), "package.json");
|
|
82
|
-
const npmInfo = getNpmInfo(name);
|
|
83
40
|
const header = parsePackageJson(name, JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")), path.dirname(dtsPath));
|
|
84
41
|
if (header === undefined) {
|
|
85
42
|
return [];
|
|
86
43
|
} else if (header.nonNpm) {
|
|
87
44
|
const errors: CriticError[] = [];
|
|
88
|
-
const nonNpmError = checkNonNpm(name, npmInfo);
|
|
89
|
-
if (nonNpmError) {
|
|
90
|
-
errors.push(nonNpmError);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
45
|
if (sourcePath) {
|
|
94
|
-
|
|
95
|
-
errors.push(...checkSource(name, dtsPath, sourcePath, options.errors, debug));
|
|
96
|
-
}
|
|
46
|
+
errors.push(...checkSource(name, dtsPath, sourcePath, options.errors, debug));
|
|
97
47
|
} else if (require.main === module) {
|
|
98
48
|
console.log(`Warning: declaration provided is for a non-npm package.
|
|
99
49
|
If you want to check the declaration against the JavaScript source code, you must provide a path to the source file.`);
|
|
@@ -101,30 +51,13 @@ If you want to check the declaration against the JavaScript source code, you mus
|
|
|
101
51
|
|
|
102
52
|
return errors;
|
|
103
53
|
} else {
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (options.mode === Mode.Code) {
|
|
110
|
-
let sourceEntry;
|
|
111
|
-
let packagePath;
|
|
112
|
-
if (sourcePath) {
|
|
113
|
-
sourceEntry = sourcePath;
|
|
114
|
-
} else {
|
|
115
|
-
const tempDirName = tmp.dirSync({ unsafeCleanup: true }).name;
|
|
116
|
-
packagePath = downloadNpmPackage(name, npmVersion, tempDirName);
|
|
117
|
-
sourceEntry = require.resolve(path.resolve(packagePath));
|
|
118
|
-
}
|
|
119
|
-
const errors = checkSource(name, dtsPath, sourceEntry, options.errors, debug);
|
|
120
|
-
if (packagePath) {
|
|
121
|
-
// Delete the source afterward to avoid running out of space
|
|
122
|
-
fs.rmSync(packagePath, { recursive: true, force: true });
|
|
123
|
-
}
|
|
124
|
-
return errors;
|
|
54
|
+
let sourceEntry;
|
|
55
|
+
if (!fs.statSync(sourcePath).isDirectory()) {
|
|
56
|
+
sourceEntry = sourcePath;
|
|
57
|
+
} else {
|
|
58
|
+
sourceEntry = require.resolve(path.resolve(sourcePath));
|
|
125
59
|
}
|
|
126
|
-
|
|
127
|
-
return [];
|
|
60
|
+
return checkSource(name, dtsPath, sourceEntry, options.errors, debug);
|
|
128
61
|
}
|
|
129
62
|
}
|
|
130
63
|
|
|
@@ -143,7 +76,7 @@ export const defaultErrors: ExportErrorKind[] = [ErrorKind.NeedsExportEquals, Er
|
|
|
143
76
|
function main() {
|
|
144
77
|
const argv = yargs
|
|
145
78
|
.usage(
|
|
146
|
-
"$0 --dts path-to-d.ts
|
|
79
|
+
"$0 --dts path-to-d.ts --js path-to-source [--debug]\n\nIf source-folder is not provided, I will look for a matching package on npm.",
|
|
147
80
|
)
|
|
148
81
|
.option("dts", {
|
|
149
82
|
describe: "Path of declaration file to be critiqued.",
|
|
@@ -154,12 +87,7 @@ function main() {
|
|
|
154
87
|
describe: "Path of JavaScript file to be used as source.",
|
|
155
88
|
type: "string",
|
|
156
89
|
})
|
|
157
|
-
.
|
|
158
|
-
describe: "Mode defines what checks will be performed.",
|
|
159
|
-
type: "string",
|
|
160
|
-
default: Mode.NameOnly,
|
|
161
|
-
choices: [Mode.NameOnly, Mode.Code],
|
|
162
|
-
})
|
|
90
|
+
.demandOption("js", "Please provide a path to a JavaScript file for me to compare the d.ts against.")
|
|
163
91
|
.option("debug", {
|
|
164
92
|
describe: "Turn debug logging on.",
|
|
165
93
|
type: "boolean",
|
|
@@ -168,14 +96,7 @@ function main() {
|
|
|
168
96
|
.help()
|
|
169
97
|
.parseSync();
|
|
170
98
|
|
|
171
|
-
|
|
172
|
-
switch (argv.mode) {
|
|
173
|
-
case Mode.NameOnly:
|
|
174
|
-
opts = { mode: argv.mode };
|
|
175
|
-
break;
|
|
176
|
-
case Mode.Code:
|
|
177
|
-
opts = { mode: argv.mode, errors: new Map() };
|
|
178
|
-
}
|
|
99
|
+
const opts = { mode: argv.mode, errors: new Map() };
|
|
179
100
|
const errors = dtsCritic(argv.dts, argv.js, opts, argv.debug);
|
|
180
101
|
if (errors.length === 0) {
|
|
181
102
|
console.log("No errors!");
|
|
@@ -186,104 +107,6 @@ function main() {
|
|
|
186
107
|
}
|
|
187
108
|
}
|
|
188
109
|
|
|
189
|
-
const npmNotFound = "E404";
|
|
190
|
-
|
|
191
|
-
export function getNpmInfo(name: string): NpmInfo {
|
|
192
|
-
const npmName = dtToNpmName(name);
|
|
193
|
-
const infoResult = cp.spawnSync(which.sync("npm"), ["info", npmName, "--json", "--silent", "versions", "dist-tags"], {
|
|
194
|
-
encoding: "utf8",
|
|
195
|
-
env: { ...process.env, COREPACK_ENABLE_STRICT: "0" },
|
|
196
|
-
});
|
|
197
|
-
const info = JSON.parse(infoResult.stdout || infoResult.stderr);
|
|
198
|
-
if (info.error !== undefined) {
|
|
199
|
-
const error = info.error as { code?: string; summary?: string };
|
|
200
|
-
if (error.code === npmNotFound) {
|
|
201
|
-
return { isNpm: false };
|
|
202
|
-
} else {
|
|
203
|
-
throw new Error(`Command 'npm info' for package ${npmName} returned an error. Reason: ${error.summary}.`);
|
|
204
|
-
}
|
|
205
|
-
} else if (infoResult.status !== 0) {
|
|
206
|
-
throw new Error(`Command 'npm info' failed for package ${npmName} with status ${infoResult.status}.`);
|
|
207
|
-
}
|
|
208
|
-
return {
|
|
209
|
-
isNpm: true,
|
|
210
|
-
versions: Array.isArray(info.versions) ? info.versions : [info.versions],
|
|
211
|
-
tags: info["dist-tags"] as { [tag: string]: string | undefined },
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Checks DefinitelyTyped non-npm package.
|
|
217
|
-
*/
|
|
218
|
-
function checkNonNpm(name: string, npmInfo: NpmInfo): NonNpmError | undefined {
|
|
219
|
-
if (npmInfo.isNpm && !isExistingSquatter(name)) {
|
|
220
|
-
return {
|
|
221
|
-
kind: ErrorKind.NonNpmHasMatchingPackage,
|
|
222
|
-
message: `The non-npm package '${name}' conflicts with the existing npm package '${dtToNpmName(name)}'.
|
|
223
|
-
Try adding -browser to the end of the name to get
|
|
224
|
-
|
|
225
|
-
${name}-browser
|
|
226
|
-
`,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
return undefined;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Checks DefinitelyTyped npm package.
|
|
234
|
-
* If all checks are successful, returns the npm version that matches the header.
|
|
235
|
-
*/
|
|
236
|
-
function checkNpm(name: string, npmInfo: NpmInfo, header: headerParser.Header): NpmError | string {
|
|
237
|
-
if (!npmInfo.isNpm) {
|
|
238
|
-
return {
|
|
239
|
-
kind: ErrorKind.NoMatchingNpmPackage,
|
|
240
|
-
message: `Declaration file must have a matching npm package.
|
|
241
|
-
To resolve this error, either:
|
|
242
|
-
1. Change the name to match an npm package.
|
|
243
|
-
2. Add \`"nonNpm": true\` to the package.json to indicate that this is not an npm package.
|
|
244
|
-
Ensure the package name is descriptive enough to avoid conflicts with future npm packages.`,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
const target =
|
|
248
|
-
header.libraryMajorVersion === 0 && header.libraryMinorVersion === 0
|
|
249
|
-
? undefined
|
|
250
|
-
: `${header.libraryMajorVersion}.${header.libraryMinorVersion}`;
|
|
251
|
-
const npmVersion = getMatchingVersion(target, npmInfo);
|
|
252
|
-
if (!npmVersion) {
|
|
253
|
-
const versions = npmInfo.versions;
|
|
254
|
-
const verstring = versions.join(", ");
|
|
255
|
-
const lateststring = versions[versions.length - 1];
|
|
256
|
-
return {
|
|
257
|
-
kind: ErrorKind.NoMatchingNpmVersion,
|
|
258
|
-
message: `The types for '${name}' must match a version that exists on npm.
|
|
259
|
-
You should copy the major and minor version from the package on npm.
|
|
260
|
-
|
|
261
|
-
To resolve this error, change the version in the package.json, ${target},
|
|
262
|
-
to match one on npm: ${verstring}.
|
|
263
|
-
|
|
264
|
-
For example, if you're trying to match the latest version, use ${lateststring}.`,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
return npmVersion;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Finds an npm version that matches the target version specified, if it exists.
|
|
272
|
-
* If the target version is undefined, returns the latest version.
|
|
273
|
-
* The npm version returned might be a prerelease version.
|
|
274
|
-
*/
|
|
275
|
-
function getMatchingVersion(target: string | undefined, npmInfo: Npm): string | undefined {
|
|
276
|
-
const versions = npmInfo.versions;
|
|
277
|
-
if (target) {
|
|
278
|
-
const matchingVersion = semver.maxSatisfying(versions, target, { includePrerelease: true });
|
|
279
|
-
return matchingVersion || undefined;
|
|
280
|
-
}
|
|
281
|
-
if (npmInfo.tags.latest) {
|
|
282
|
-
return npmInfo.tags.latest;
|
|
283
|
-
}
|
|
284
|
-
return versions[versions.length - 1];
|
|
285
|
-
}
|
|
286
|
-
|
|
287
110
|
/**
|
|
288
111
|
* If dtsName is 'index' (as with DT) then look to the parent directory for the name.
|
|
289
112
|
*/
|
|
@@ -296,47 +119,6 @@ export function findDtsName(dtsPath: string) {
|
|
|
296
119
|
return path.basename(path.dirname(resolved));
|
|
297
120
|
}
|
|
298
121
|
|
|
299
|
-
/** Returns path of downloaded npm package. */
|
|
300
|
-
function downloadNpmPackage(name: string, version: string, outDir: string): string {
|
|
301
|
-
const npmName = dtToNpmName(name);
|
|
302
|
-
const fullName = `${npmName}@${version}`;
|
|
303
|
-
const cpOpts = {
|
|
304
|
-
encoding: "utf8",
|
|
305
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
306
|
-
env: { ...process.env, COREPACK_ENABLE_STRICT: "0" },
|
|
307
|
-
} as const;
|
|
308
|
-
const npmPack = cp.execFileSync(which.sync("npm"), ["pack", fullName, "--json", "--silent"], cpOpts).trim();
|
|
309
|
-
// https://github.com/npm/cli/issues/3405
|
|
310
|
-
const tarballName = (npmPack.endsWith(".tgz") ? npmPack : (JSON.parse(npmPack)[0].filename as string))
|
|
311
|
-
.replace(/^@/, "")
|
|
312
|
-
.replace(/\//, "-");
|
|
313
|
-
const outPath = path.join(outDir, name);
|
|
314
|
-
initDir(outPath);
|
|
315
|
-
const isBsdTar = cp.execFileSync(which.sync("tar"), ["--version"], cpOpts).includes("bsdtar");
|
|
316
|
-
const args = isBsdTar
|
|
317
|
-
? ["-xz", "-f", tarballName, "-C", outPath]
|
|
318
|
-
: ["-xz", "-f", tarballName, "-C", outPath, "--warning=none"];
|
|
319
|
-
cp.execFileSync(which.sync("tar"), args, cpOpts);
|
|
320
|
-
fs.unlinkSync(tarballName);
|
|
321
|
-
return path.join(outPath, getPackageDir(outPath));
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function getPackageDir(outPath: string): string {
|
|
325
|
-
const dirs = fs.readdirSync(outPath, { encoding: "utf8", withFileTypes: true });
|
|
326
|
-
for (const dirent of dirs) {
|
|
327
|
-
if (dirent.isDirectory()) {
|
|
328
|
-
return dirent.name;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return "package";
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function initDir(dirPath: string): void {
|
|
335
|
-
if (!fs.existsSync(dirPath)) {
|
|
336
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
122
|
export function checkSource(
|
|
341
123
|
name: string,
|
|
342
124
|
dtsPath: string,
|
|
@@ -839,18 +621,6 @@ function matches(srcFile: ts.SourceFile, predicate: (n: ts.Node) => boolean): bo
|
|
|
839
621
|
return matchesNode(srcFile);
|
|
840
622
|
}
|
|
841
623
|
|
|
842
|
-
function isExistingSquatter(name: string) {
|
|
843
|
-
return (
|
|
844
|
-
name === "atom" ||
|
|
845
|
-
name === "ember__string" ||
|
|
846
|
-
name === "fancybox" ||
|
|
847
|
-
name === "jsqrcode" ||
|
|
848
|
-
name === "node" ||
|
|
849
|
-
name === "geojson" ||
|
|
850
|
-
name === "titanium"
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
624
|
function isRealExportDefault(name: string) {
|
|
855
625
|
return name.indexOf("react-native") > -1 || name === "ember-feature-flags" || name === "material-ui-datatables";
|
|
856
626
|
}
|
|
@@ -894,14 +664,6 @@ export interface CriticError {
|
|
|
894
664
|
position?: Position;
|
|
895
665
|
}
|
|
896
666
|
|
|
897
|
-
interface NpmError extends CriticError {
|
|
898
|
-
kind: ErrorKind.NoMatchingNpmPackage | ErrorKind.NoMatchingNpmVersion;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
interface NonNpmError extends CriticError {
|
|
902
|
-
kind: ErrorKind.NonNpmHasMatchingPackage;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
667
|
interface ExportEqualsError extends CriticError {
|
|
906
668
|
kind: ErrorKind.NeedsExportEquals;
|
|
907
669
|
}
|
|
@@ -967,18 +729,6 @@ interface DtsExportDiagnostics {
|
|
|
967
729
|
defaultExport?: Position;
|
|
968
730
|
}
|
|
969
731
|
|
|
970
|
-
type NpmInfo = NonNpm | Npm;
|
|
971
|
-
|
|
972
|
-
interface NonNpm {
|
|
973
|
-
isNpm: false;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
interface Npm {
|
|
977
|
-
isNpm: true;
|
|
978
|
-
versions: string[];
|
|
979
|
-
tags: { [tag: string]: string | undefined };
|
|
980
|
-
}
|
|
981
|
-
|
|
982
732
|
type InferenceResult<T> = InferenceError | InferenceSuccess<T>;
|
|
983
733
|
|
|
984
734
|
enum InferenceResultKind {
|
package/package.json
CHANGED
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@definitelytyped/dts-critic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"author": "Nathan Shively-Sanders",
|
|
5
5
|
"description": "Checks a new .d.ts against the Javascript source and tells you what problems it has",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"command-exists": "^1.2.9",
|
|
11
|
-
"semver": "^7.5.4",
|
|
12
|
-
"tmp": "^0.2.1",
|
|
13
10
|
"typescript": "^5.3.3",
|
|
14
|
-
"which": "^4.0.0",
|
|
15
11
|
"yargs": "^17.7.2",
|
|
16
|
-
"@definitelytyped/header-parser": "0.2.
|
|
12
|
+
"@definitelytyped/header-parser": "0.2.2"
|
|
17
13
|
},
|
|
18
14
|
"peerDependencies": {
|
|
19
15
|
"typescript": "*"
|
|
20
16
|
},
|
|
21
|
-
"devDependencies": {
|
|
22
|
-
"@types/command-exists": "^1.2.3",
|
|
23
|
-
"@types/semver": "^7.5.6",
|
|
24
|
-
"@types/strip-json-comments": "^3.0.0",
|
|
25
|
-
"@types/tmp": "^0.2.6",
|
|
26
|
-
"@types/which": "^3.0.3",
|
|
27
|
-
"strip-json-comments": "^3.1.1"
|
|
28
|
-
},
|
|
17
|
+
"devDependencies": {},
|
|
29
18
|
"main": "dist/index.js",
|
|
30
19
|
"types": "dist/index.d.ts",
|
|
31
20
|
"repository": {
|
|
@@ -50,7 +39,6 @@
|
|
|
50
39
|
},
|
|
51
40
|
"scripts": {
|
|
52
41
|
"build": "tsc -b .",
|
|
53
|
-
"dt": "node dist/dt.js",
|
|
54
42
|
"test": "../../node_modules/.bin/jest --config ../../jest.config.js packages/dts-critic"
|
|
55
43
|
}
|
|
56
44
|
}
|