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