dts-critic
Version:
Checks a new .d.ts against the Javascript source and tells you what problems it has
260 lines (248 loc) • 9.4 kB
text/typescript
import {
findDtsName,
getNpmInfo,
dtToNpmName,
parseExportErrorKind,
dtsCritic,
checkSource,
ErrorKind,
ExportErrorKind } from "./index";
function suite(description: string, tests: { [s: string]: () => void; }) {
describe(description, () => {
for (const k in tests) {
test(k, tests[k], 10 * 1000);
}
});
}
suite("findDtsName", {
absolutePath() {
expect(findDtsName("~/dt/types/jquery/index.d.ts")).toBe("jquery");
},
relativePath() {
expect(findDtsName("jquery/index.d.ts")).toBe("jquery");
},
currentDirectory() {
expect(findDtsName("index.d.ts")).toBe("dts-critic");
},
relativeCurrentDirectory() {
expect(findDtsName("./index.d.ts")).toBe("dts-critic");
},
emptyDirectory() {
expect(findDtsName("")).toBe("dts-critic");
},
});
suite("getNpmInfo", {
nonNpm() {
expect(getNpmInfo("parseltongue")).toEqual({ isNpm: false });
},
npm() {
expect(getNpmInfo("typescript")).toEqual({
isNpm: true,
versions: expect.arrayContaining(["3.7.5"]),
tags: expect.objectContaining({ latest: expect.stringContaining("") }),
});
},
});
suite("dtToNpmName", {
nonScoped() {
expect(dtToNpmName("content-type")).toBe("content-type");
},
scoped() {
expect(dtToNpmName("babel__core")).toBe("@babel/core");
},
});
suite("parseExportErrorKind", {
existent() {
expect(parseExportErrorKind("NoDefaultExport")).toBe(ErrorKind.NoDefaultExport);
},
existentDifferentCase() {
expect(parseExportErrorKind("JspropertyNotinDTS")).toBe(ErrorKind.JsPropertyNotInDts);
},
nonexistent() {
expect(parseExportErrorKind("FakeError")).toBe(undefined);
}
});
const allErrors: Map<ExportErrorKind, true> = new Map([
[ErrorKind.NeedsExportEquals, true],
[ErrorKind.NoDefaultExport, true],
[ErrorKind.JsSignatureNotInDts, true],
[ErrorKind.DtsSignatureNotInJs, true],
[ErrorKind.DtsPropertyNotInJs, true],
[ErrorKind.JsPropertyNotInDts, true],
]);
suite("checkSource", {
noErrors() {
expect(checkSource(
"noErrors",
"testsource/noErrors.d.ts",
"testsource/noErrors.js",
allErrors,
false,
)).toEqual([]);
},
missingJsProperty() {
expect(checkSource(
"missingJsProperty",
"testsource/missingJsProperty.d.ts",
"testsource/missingJsProperty.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.JsPropertyNotInDts,
message: `The declaration doesn't match the JavaScript module 'missingJsProperty'. Reason:
The JavaScript module exports a property named 'foo', which is missing from the declaration module.`
}
]));
},
noMissingWebpackProperty() {
expect(checkSource(
"missingJsProperty",
"testsource/webpackPropertyNames.d.ts",
"testsource/webpackPropertyNames.js",
allErrors,
false,
)).toHaveLength(0);
},
missingDtsProperty() {
expect(checkSource(
"missingDtsProperty",
"testsource/missingDtsProperty.d.ts",
"testsource/missingDtsProperty.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.DtsPropertyNotInJs,
message: `The declaration doesn't match the JavaScript module 'missingDtsProperty'. Reason:
The declaration module exports a property named 'foo', which is missing from the JavaScript module.`,
position: {
start: 67,
length: 11,
},
}
]));
},
missingDefaultExport() {
expect(checkSource(
"missingDefault",
"testsource/missingDefault.d.ts",
"testsource/missingDefault.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.NoDefaultExport,
message: `The declaration doesn't match the JavaScript module 'missingDefault'. Reason:
The declaration specifies 'export default' but the JavaScript source does not mention 'default' anywhere.
The most common way to resolve this error is to use 'export =' syntax instead of 'export default'.
To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`,
position: {
start: 0,
length: 32,
},
}
]));
},
missingJsSignatureExportEquals() {
expect(checkSource(
"missingJsSignatureExportEquals",
"testsource/missingJsSignatureExportEquals.d.ts",
"testsource/missingJsSignatureExportEquals.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.JsSignatureNotInDts,
message: `The declaration doesn't match the JavaScript module 'missingJsSignatureExportEquals'. Reason:
The JavaScript module can be called or constructed, but the declaration module cannot.`,
}
]));
},
missingJsSignatureNoExportEquals() {
expect(checkSource(
"missingJsSignatureNoExportEquals",
"testsource/missingJsSignatureNoExportEquals.d.ts",
"testsource/missingJsSignatureNoExportEquals.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.JsSignatureNotInDts,
message: `The declaration doesn't match the JavaScript module 'missingJsSignatureNoExportEquals'. Reason:
The JavaScript module can be called or constructed, but the declaration module cannot.
The most common way to resolve this error is to use 'export =' syntax.
To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`,
}
]));
},
missingDtsSignature() {
expect(checkSource(
"missingDtsSignature",
"testsource/missingDtsSignature.d.ts",
"testsource/missingDtsSignature.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.DtsSignatureNotInJs,
message: `The declaration doesn't match the JavaScript module 'missingDtsSignature'. Reason:
The declaration module can be called or constructed, but the JavaScript module cannot.`,
}
]));
},
missingExportEquals() {
expect(checkSource(
"missingExportEquals",
"testsource/missingExportEquals.d.ts",
"testsource/missingExportEquals.js",
allErrors,
false,
)).toEqual(expect.arrayContaining([
{
kind: ErrorKind.NeedsExportEquals,
message: `The declaration doesn't match the JavaScript module 'missingExportEquals'. Reason:
The declaration should use 'export =' syntax because the JavaScript source uses 'module.exports =' syntax and 'module.exports' can be called or constructed.
To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`,
}
]));
},
});
suite("dtsCritic", {
noErrors() {
expect(dtsCritic("testsource/dts-critic.d.ts", "testsource/dts-critic.js")).toEqual([]);
},
noMatchingNpmPackage() {
expect(dtsCritic("testsource/parseltongue.d.ts")).toEqual([
{
kind: ErrorKind.NoMatchingNpmPackage,
message: `Declaration file must have a matching npm package.
To resolve this error, either:
1. Change the name to match an npm package.
2. Add a Definitely Typed header with the first line
// Type definitions for non-npm package parseltongue-browser
Add -browser to the end of your name to make sure it doesn't conflict with existing npm packages.`,
},
]);
},
noMatchingNpmVersion() {
expect(dtsCritic("testsource/typescript.d.ts")).toEqual([
{
kind: ErrorKind.NoMatchingNpmVersion,
message: expect.stringContaining(`The types for 'typescript' must match a version that exists on npm.
You should copy the major and minor version from the package on npm.`),
},
]);
},
nonNpmHasMatchingPackage() {
expect(dtsCritic("testsource/tslib.d.ts")).toEqual([
{
kind: ErrorKind.NonNpmHasMatchingPackage,
message: `The non-npm package 'tslib' conflicts with the existing npm package 'tslib'.
Try adding -browser to the end of the name to get
tslib-browser
`,
},
]);
}
});