diff --git a/.changeset/good-glasses-wait.md b/.changeset/good-glasses-wait.md
new file mode 100644
index 0000000..2582e1c
--- /dev/null
+++ b/.changeset/good-glasses-wait.md
@@ -0,0 +1,11 @@
+---
+"@arethetypeswrong/core": minor
+---
+
+Add support for DefinitelyTyped analysis.
+
+- `createPackageFromNpm` now takes an options parameter that can control DefinitelyTyped inclusion.
+- The `Package` type is now a class, with new properties and methods:
+ - `typesPackage` contains the package name and version for the included DefinitelyTyped package, if any.
+ - `mergedWithTypes(typesPackage: Package)` returns a new `Package` instance with all files from both packages and the `typesPackage` instance property metadata filled in.
+- `createPackageFromTarballData` is no longer asynchronous.
diff --git a/.changeset/loud-hairs-relate.md b/.changeset/loud-hairs-relate.md
new file mode 100644
index 0000000..b3c04fc
--- /dev/null
+++ b/.changeset/loud-hairs-relate.md
@@ -0,0 +1,9 @@
+---
+"@arethetypeswrong/cli": minor
+---
+
+Add support for DefinitelyTyped analysis.
+
+- `@types` packages will be fetched by default for implementation packages that do not contain any TypeScript files.
+- `--definitely-typed` can be used to set the version of the `@types` package fetched. By default, the version is inferred from the implementation package version.
+- `--no-definitely-typed` can be used to prevent `@types` package inclusion.
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 5e3a45c..38ec420 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -75,7 +75,7 @@ attw --pack .
#### From NPM
-Specify the name (and, optionally, version range) of a package from the NPM registry instead of a local tarball filename.
+Specify the name (and, optionally, version or SemVer range) of a package from the NPM registry instead of a local tarball filename.
In the CLI: `--from-npm`, `-p`
@@ -85,6 +85,17 @@ attw --from-npm
In the config file, `fromNpm` can be a boolean value.
+#### DefinitelyTyped
+
+When a package does not contain types, specifies the version or SemVer range of the DefinitelyTyped `@types` package to use. Defaults to inferring the best version match from the implementation package version.
+
+In the CLI: `--definitely-typed`, `--no-definitely-typed`
+
+```shell
+attw -p --definitely-typed
+attw -p --no-definitely-typed
+```
+
#### Format
The format to print the output in. Defaults to `auto`.
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index b2955d7..40c686e 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -25,6 +25,7 @@ type Format = (typeof formats)[number];
export interface Opts {
pack?: boolean;
fromNpm?: boolean;
+ definitelyTyped?: boolean | string;
summary?: boolean;
emoji?: boolean;
color?: boolean;
@@ -55,6 +56,8 @@ particularly ESM-related module resolution issues.`
)
.option("-P, --pack", "Run `npm pack` in the specified directory and delete the resulting .tgz file afterwards")
.option("-p, --from-npm", "Read from the npm registry instead of a local file")
+ .addOption(new Option("--definitely-typed [version]", "Specify the version range of @types to use").default(true))
+ .option("--no-definitely-typed", "Don't include @types")
.addOption(new Option("-f, --format ", "Specify the print format").choices(formats).default("auto"))
.option("-q, --quiet", "Don't print anything to STDOUT (overrides all other options)")
.option(
@@ -92,6 +95,13 @@ particularly ESM-related module resolution issues.`
let analysis: core.CheckResult;
let deleteTgz;
+ const dtIsPath =
+ typeof opts.definitelyTyped === "string" &&
+ (opts.definitelyTyped.includes("/") ||
+ opts.definitelyTyped.includes("\\") ||
+ opts.definitelyTyped.endsWith(".tgz") ||
+ opts.definitelyTyped.endsWith(".tar.gz"));
+
if (opts.fromNpm) {
if (opts.pack) {
program.error("--pack and --from-npm cannot be used together");
@@ -101,14 +111,18 @@ particularly ESM-related module resolution issues.`
if (result.status === "error") {
program.error(result.error);
} else {
- analysis = await core.checkPackage(
- await core.createPackageFromNpm(`${result.data.name}@${result.data.version}`),
- {
- entrypoints: opts.entrypoints,
- includeEntrypoints: opts.includeEntrypoints,
- excludeEntrypoints: opts.excludeEntrypoints,
- }
- );
+ const pkg = dtIsPath
+ ? (await core.createPackageFromNpm(`${result.data.name}@${result.data.version}`)).mergedWithTypes(
+ core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped as string)))
+ )
+ : await core.createPackageFromNpm(`${result.data.name}@${result.data.version}`, {
+ definitelyTyped: opts.definitelyTyped,
+ });
+ analysis = await core.checkPackage(pkg, {
+ entrypoints: opts.entrypoints,
+ includeEntrypoints: opts.includeEntrypoints,
+ excludeEntrypoints: opts.excludeEntrypoints,
+ });
}
} catch (error) {
if (error instanceof FetchError) {
@@ -156,7 +170,14 @@ particularly ESM-related module resolution issues.`
}
const file = await readFile(fileName);
const data = new Uint8Array(file);
- analysis = await core.checkPackage(await core.createPackageFromTarballData(data), {
+ const pkg = dtIsPath
+ ? core
+ .createPackageFromTarballData(data)
+ .mergedWithTypes(
+ core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped as string)))
+ )
+ : core.createPackageFromTarballData(data);
+ analysis = await core.checkPackage(pkg, {
entrypoints: opts.entrypoints,
includeEntrypoints: opts.includeEntrypoints,
excludeEntrypoints: opts.excludeEntrypoints,
diff --git a/packages/cli/src/render/typed.ts b/packages/cli/src/render/typed.ts
index be61df1..ed8877c 100644
--- a/packages/cli/src/render/typed.ts
+++ b/packages/cli/src/render/typed.ts
@@ -31,6 +31,12 @@ export async function typed(analysis: core.Analysis, opts: Opts) {
);
}
+ console.log(`${analysis.packageName} v${analysis.packageVersion}`);
+ if (analysis.types.kind === "@types") {
+ console.log(`${analysis.types.packageName} v${analysis.types.packageVersion}`);
+ }
+ console.log();
+
if (opts.summary) {
const defaultSummary = marked(!opts.emoji ? " No problems found" : " No problems found ๐");
const summaryTexts = Object.keys(grouped).map((kind) => {
diff --git a/packages/cli/test/snapshots.test.ts b/packages/cli/test/snapshots.test.ts
index 51d8986..d0119c6 100644
--- a/packages/cli/test/snapshots.test.ts
+++ b/packages/cli/test/snapshots.test.ts
@@ -28,6 +28,15 @@ const tests = [
["vue@3.3.4.tgz", "--entrypoints . jsx-runtime"],
["vue@3.3.4.tgz", "--exclude-entrypoints macros -f ascii"],
["vue@3.3.4.tgz", "--include-entrypoints ./foo -f ascii"],
+
+ [
+ "big.js@6.2.1.tgz",
+ `--definitely-typed ${new URL("../../../core/test/fixtures/@types__big.js@6.2.0.tgz", import.meta.url).pathname}`,
+ ],
+ [
+ "react@18.2.0.tgz",
+ `--definitely-typed ${new URL("../../../core/test/fixtures/@types__react@18.2.21.tgz", import.meta.url).pathname}`,
+ ],
];
const defaultOpts = "-f table-flipped";
diff --git a/packages/cli/test/snapshots/@apollo__client-3.7.16.tgz.md b/packages/cli/test/snapshots/@apollo__client-3.7.16.tgz.md
index 685cbdc..96effc4 100644
--- a/packages/cli/test/snapshots/@apollo__client-3.7.16.tgz.md
+++ b/packages/cli/test/snapshots/@apollo__client-3.7.16.tgz.md
@@ -4,6 +4,8 @@
$ attw @apollo__client-3.7.16.tgz -f table-flipped
+@apollo/client v3.7.16
+
๐บ Import resolved to an ESM type declaration file, but a CommonJS JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseESM.md
โ ๏ธ A require call resolved to an ESM JavaScript file, which is an error in Node and some bundlers. CommonJS consumers will need to use a dynamic import. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSResolvesToESM.md
diff --git a/packages/cli/test/snapshots/@ice__app@3.2.6.tgz.md b/packages/cli/test/snapshots/@ice__app@3.2.6.tgz.md
index b87b5a8..d75c6ad 100644
--- a/packages/cli/test/snapshots/@ice__app@3.2.6.tgz.md
+++ b/packages/cli/test/snapshots/@ice__app@3.2.6.tgz.md
@@ -4,6 +4,8 @@
$ attw @ice__app@3.2.6.tgz -f table-flipped
+@ice/app v3.2.6
+
โ ๏ธ A require call resolved to an ESM JavaScript file, which is an error in Node and some bundlers. CommonJS consumers will need to use a dynamic import. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSResolvesToESM.md
๐ Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md
diff --git a/packages/cli/test/snapshots/@reduxjs__toolkit@2.0.0-beta.0.tgz.md b/packages/cli/test/snapshots/@reduxjs__toolkit@2.0.0-beta.0.tgz.md
index 9eba234..eaa0119 100644
--- a/packages/cli/test/snapshots/@reduxjs__toolkit@2.0.0-beta.0.tgz.md
+++ b/packages/cli/test/snapshots/@reduxjs__toolkit@2.0.0-beta.0.tgz.md
@@ -4,6 +4,8 @@
$ attw @reduxjs__toolkit@2.0.0-beta.0.tgz -f table-flipped
+@reduxjs/toolkit v2.0.0-beta.0
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ฅด Import found in a type declaration file failed to resolve. Either this indicates that runtime resolution errors will occur, or (more likely) the types misrepresent the contents of the JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/InternalResolutionError.md
diff --git a/packages/cli/test/snapshots/@vitejs__plugin-react@3.1.0.tgz.md b/packages/cli/test/snapshots/@vitejs__plugin-react@3.1.0.tgz.md
index cadfdf2..839db41 100644
--- a/packages/cli/test/snapshots/@vitejs__plugin-react@3.1.0.tgz.md
+++ b/packages/cli/test/snapshots/@vitejs__plugin-react@3.1.0.tgz.md
@@ -4,6 +4,8 @@
$ attw @vitejs__plugin-react@3.1.0.tgz -f table-flipped
+@vitejs/plugin-react v3.1.0
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
diff --git a/packages/cli/test/snapshots/ajv@8.12.0.tgz.md b/packages/cli/test/snapshots/ajv@8.12.0.tgz.md
index 62c4ba8..900fa61 100644
--- a/packages/cli/test/snapshots/ajv@8.12.0.tgz.md
+++ b/packages/cli/test/snapshots/ajv@8.12.0.tgz.md
@@ -4,6 +4,8 @@
$ attw ajv@8.12.0.tgz -f table-flipped
+ajv v8.12.0
+
No problems found ๐
diff --git a/packages/cli/test/snapshots/astring@1.8.6.tgz.md b/packages/cli/test/snapshots/astring@1.8.6.tgz.md
index c4b5592..025d997 100644
--- a/packages/cli/test/snapshots/astring@1.8.6.tgz.md
+++ b/packages/cli/test/snapshots/astring@1.8.6.tgz.md
@@ -4,6 +4,8 @@
$ attw astring@1.8.6.tgz -f table-flipped
+astring v1.8.6
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
diff --git a/packages/cli/test/snapshots/axios@1.4.0.tgz.md b/packages/cli/test/snapshots/axios@1.4.0.tgz.md
index a17b923..31435de 100644
--- a/packages/cli/test/snapshots/axios@1.4.0.tgz.md
+++ b/packages/cli/test/snapshots/axios@1.4.0.tgz.md
@@ -4,6 +4,8 @@
$ attw axios@1.4.0.tgz -f table-flipped
+axios v1.4.0
+
โ Wildcard subpaths cannot yet be analyzed by this tool. https://github.com/arethetypeswrong/arethetypeswrong.github.io/issues/40
๐ Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md
diff --git a/packages/cli/test/snapshots/big.js@6.2.1.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__big.js@6.2.0.tgz.md b/packages/cli/test/snapshots/big.js@6.2.1.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__big.js@6.2.0.tgz.md
new file mode 100644
index 0000000..eff546a
--- /dev/null
+++ b/packages/cli/test/snapshots/big.js@6.2.1.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__big.js@6.2.0.tgz.md
@@ -0,0 +1,33 @@
+# big.js@6.2.1.tgz --definitely-typed /Users/andrew/Developer/arethetypeswrong.github.io/packages/core/test/fixtures/@types__big.js@6.2.0.tgz
+
+```
+$ attw big.js@6.2.1.tgz --definitely-typed /Users/andrew/Developer/arethetypeswrong.github.io/packages/core/test/fixtures/@types__big.js@6.2.0.tgz
+
+
+big.js v6.2.1
+@types/big.js v6.2.0
+
+๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
+
+โ Import resolved to JavaScript files, but no type declarations were found. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/UntypedResolution.md
+
+โ ๏ธ A require call resolved to an ESM JavaScript file, which is an error in Node and some bundlers. CommonJS consumers will need to use a dynamic import. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSResolvesToESM.md
+
+
+โโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ
+โ โ "big.js" โ "big.js/big.mjs" โ "big.js/big.js" โ "big.js/package.json" โ
+โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโค
+โ node10 โ ๐ข โ โ No types โ โ No types โ ๐ข (JSON) โ
+โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโค
+โ node16 (from CJS) โ ๐ข (CJS) โ โ No types โ โ No types โ ๐ข (JSON) โ
+โ โ โ โ ๏ธ ESM (dynamic import only) โ โ โ
+โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโค
+โ node16 (from ESM) โ ๐ญ Masquerading as CJS โ โ No types โ โ No types โ ๐ข (JSON) โ
+โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโค
+โ bundler โ ๐ข โ โ No types โ โ No types โ ๐ข (JSON) โ
+โโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+```
+
+Exit code: 1
\ No newline at end of file
diff --git a/packages/cli/test/snapshots/commander@10.0.1.tgz -f table.md b/packages/cli/test/snapshots/commander@10.0.1.tgz -f table.md
index 2bcecd1..4a7cc93 100644
--- a/packages/cli/test/snapshots/commander@10.0.1.tgz -f table.md
+++ b/packages/cli/test/snapshots/commander@10.0.1.tgz -f table.md
@@ -4,6 +4,8 @@
$ attw commander@10.0.1.tgz -f table
+commander v10.0.1
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
โ Import resolved to JavaScript files, but no type declarations were found. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/UntypedResolution.md
diff --git a/packages/cli/test/snapshots/hexoid@1.0.0.tgz.md b/packages/cli/test/snapshots/hexoid@1.0.0.tgz.md
index 81c17ca..0a9545f 100644
--- a/packages/cli/test/snapshots/hexoid@1.0.0.tgz.md
+++ b/packages/cli/test/snapshots/hexoid@1.0.0.tgz.md
@@ -4,6 +4,8 @@
$ attw hexoid@1.0.0.tgz -f table-flipped
+hexoid v1.0.0
+
โ๏ธ The resolved types use export default where the JavaScript file appears to use module.exports =. This will cause TypeScript under the node16 module mode to think an extra .default property access is required, but that will likely fail at runtime. These types should use export = instead of export default. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseExportDefault.md
diff --git a/packages/cli/test/snapshots/klona@2.0.6.tgz -f ascii.md b/packages/cli/test/snapshots/klona@2.0.6.tgz -f ascii.md
index a247192..8f1da2a 100644
--- a/packages/cli/test/snapshots/klona@2.0.6.tgz -f ascii.md
+++ b/packages/cli/test/snapshots/klona@2.0.6.tgz -f ascii.md
@@ -4,6 +4,8 @@
$ attw klona@2.0.6.tgz -f ascii
+klona v2.0.6
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
diff --git a/packages/cli/test/snapshots/node-html-parser@6.1.5.tgz.md b/packages/cli/test/snapshots/node-html-parser@6.1.5.tgz.md
index 00534df..98b91c5 100644
--- a/packages/cli/test/snapshots/node-html-parser@6.1.5.tgz.md
+++ b/packages/cli/test/snapshots/node-html-parser@6.1.5.tgz.md
@@ -4,6 +4,8 @@
$ attw node-html-parser@6.1.5.tgz -f table-flipped
+node-html-parser v6.1.5
+
๐คจ CommonJS module simulates a default export with exports.default and exports.__esModule, but does not also set module.exports for compatibility with Node. Node, and some bundlers under certain conditions (https://andrewbranch.github.io/interop-test/#synthesizing-default-exports-for-cjs-modules), do not respect the __esModule marker, so accessing the intended default export will require a .default property access on the default import. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSOnlyExportsDefault.md
diff --git a/packages/cli/test/snapshots/postcss@8.4.21.tgz.md b/packages/cli/test/snapshots/postcss@8.4.21.tgz.md
index 221bd21..de320ed 100644
--- a/packages/cli/test/snapshots/postcss@8.4.21.tgz.md
+++ b/packages/cli/test/snapshots/postcss@8.4.21.tgz.md
@@ -4,6 +4,8 @@
$ attw postcss@8.4.21.tgz -f table-flipped
+postcss v8.4.21
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ Import resolved to types through a conditional package.json export, but only after failing to resolve through an earlier condition. This behavior is a TypeScript bug (https://github.com/microsoft/TypeScript/issues/50762). It may misrepresent the runtime behavior of this import and should not be relied upon. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FallbackCondition.md
diff --git a/packages/cli/test/snapshots/react-chartjs-2@5.2.0.tgz.md b/packages/cli/test/snapshots/react-chartjs-2@5.2.0.tgz.md
index 6be800c..740c069 100644
--- a/packages/cli/test/snapshots/react-chartjs-2@5.2.0.tgz.md
+++ b/packages/cli/test/snapshots/react-chartjs-2@5.2.0.tgz.md
@@ -4,6 +4,8 @@
$ attw react-chartjs-2@5.2.0.tgz -f table-flipped
+react-chartjs-2 v5.2.0
+
๐บ Import resolved to an ESM type declaration file, but a CommonJS JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseESM.md
โ ๏ธ A require call resolved to an ESM JavaScript file, which is an error in Node and some bundlers. CommonJS consumers will need to use a dynamic import. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSResolvesToESM.md
diff --git a/packages/cli/test/snapshots/react@18.2.0.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__react@18.2.21.tgz.md b/packages/cli/test/snapshots/react@18.2.0.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__react@18.2.21.tgz.md
new file mode 100644
index 0000000..96dd4cc
--- /dev/null
+++ b/packages/cli/test/snapshots/react@18.2.0.tgz --definitely-typed UsersandrewDeveloperarethetypeswrong.github.iopackagescoretestfixtures@types__react@18.2.21.tgz.md
@@ -0,0 +1,32 @@
+# react@18.2.0.tgz --definitely-typed /Users/andrew/Developer/arethetypeswrong.github.io/packages/core/test/fixtures/@types__react@18.2.21.tgz
+
+```
+$ attw react@18.2.0.tgz --definitely-typed /Users/andrew/Developer/arethetypeswrong.github.io/packages/core/test/fixtures/@types__react@18.2.21.tgz
+
+
+react v18.2.0
+@types/react v18.2.21
+
+ No problems found ๐
+
+
+โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโ
+โ โ node10 โ node16 (from CJS) โ node16 (from ESM) โ bundler โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react" โ ๐ข โ ๐ข (CJS) โ ๐ข (CJS) โ ๐ข โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react/package.json" โ ๐ข (JSON) โ ๐ข (JSON) โ ๐ข (JSON) โ ๐ข (JSON) โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react/jsx-runtime" โ ๐ข โ ๐ข (CJS) โ ๐ข (CJS) โ ๐ข โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react/jsx-dev-runtime" โ ๐ข โ ๐ข (CJS) โ ๐ข (CJS) โ ๐ข โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react/canary" โ ๐ข โ ๐ข (CJS) โ ๐ข (CJS) โ ๐ข โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
+โ "react/experimental" โ ๐ข โ ๐ข (CJS) โ ๐ข (CJS) โ ๐ข โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโ
+
+
+```
+
+Exit code: 0
\ No newline at end of file
diff --git a/packages/cli/test/snapshots/rfdc@1.3.0.tgz.md b/packages/cli/test/snapshots/rfdc@1.3.0.tgz.md
index 44b9a37..cb319af 100644
--- a/packages/cli/test/snapshots/rfdc@1.3.0.tgz.md
+++ b/packages/cli/test/snapshots/rfdc@1.3.0.tgz.md
@@ -4,6 +4,8 @@
$ attw rfdc@1.3.0.tgz -f table-flipped
+rfdc v1.3.0
+
โ Import resolved to JavaScript files, but no type declarations were found. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/UntypedResolution.md
diff --git a/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints . jsx-runtime.md b/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints . jsx-runtime.md
index 482aa2f..d763b4b 100644
--- a/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints . jsx-runtime.md
+++ b/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints . jsx-runtime.md
@@ -4,6 +4,8 @@
$ attw vue@3.3.4.tgz --entrypoints . jsx-runtime
+vue v3.3.4
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ฅด Import found in a type declaration file failed to resolve. Either this indicates that runtime resolution errors will occur, or (more likely) the types misrepresent the contents of the JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/InternalResolutionError.md
diff --git a/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints vue.md b/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints vue.md
index fce301d..0179660 100644
--- a/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints vue.md
+++ b/packages/cli/test/snapshots/vue@3.3.4.tgz --entrypoints vue.md
@@ -4,6 +4,8 @@
$ attw vue@3.3.4.tgz --entrypoints vue
+vue v3.3.4
+
๐ฅด Import found in a type declaration file failed to resolve. Either this indicates that runtime resolution errors will occur, or (more likely) the types misrepresent the contents of the JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/InternalResolutionError.md
diff --git a/packages/cli/test/snapshots/vue@3.3.4.tgz --exclude-entrypoints macros -f ascii.md b/packages/cli/test/snapshots/vue@3.3.4.tgz --exclude-entrypoints macros -f ascii.md
index 473e225..65f8b6e 100644
--- a/packages/cli/test/snapshots/vue@3.3.4.tgz --exclude-entrypoints macros -f ascii.md
+++ b/packages/cli/test/snapshots/vue@3.3.4.tgz --exclude-entrypoints macros -f ascii.md
@@ -4,6 +4,8 @@
$ attw vue@3.3.4.tgz --exclude-entrypoints macros -f ascii
+vue v3.3.4
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md
diff --git a/packages/cli/test/snapshots/vue@3.3.4.tgz --include-entrypoints .foo -f ascii.md b/packages/cli/test/snapshots/vue@3.3.4.tgz --include-entrypoints .foo -f ascii.md
index 8333bb1..926827b 100644
--- a/packages/cli/test/snapshots/vue@3.3.4.tgz --include-entrypoints .foo -f ascii.md
+++ b/packages/cli/test/snapshots/vue@3.3.4.tgz --include-entrypoints .foo -f ascii.md
@@ -4,6 +4,8 @@
$ attw vue@3.3.4.tgz --include-entrypoints ./foo -f ascii
+vue v3.3.4
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md
diff --git a/packages/cli/test/snapshots/vue@3.3.4.tgz.md b/packages/cli/test/snapshots/vue@3.3.4.tgz.md
index 549caf9..9974d36 100644
--- a/packages/cli/test/snapshots/vue@3.3.4.tgz.md
+++ b/packages/cli/test/snapshots/vue@3.3.4.tgz.md
@@ -4,6 +4,8 @@
$ attw vue@3.3.4.tgz -f table-flipped
+vue v3.3.4
+
๐ญ Import resolved to a CommonJS type declaration file, but an ESM JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
๐ Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md
diff --git a/packages/core/src/checkPackage.ts b/packages/core/src/checkPackage.ts
index 1b3badb..ed5d5a9 100644
--- a/packages/core/src/checkPackage.ts
+++ b/packages/core/src/checkPackage.ts
@@ -4,7 +4,14 @@ import { getFileProblems } from "./checks/fileProblems.js";
import { getResolutionBasedFileProblems } from "./checks/resolutionBasedFileProblems.js";
import type { Package } from "./createPackage.js";
import { createCompilerHosts, type CompilerHosts, CompilerHostWrapper } from "./multiCompilerHost.js";
-import type { CheckResult, EntrypointInfo, EntrypointResolutionAnalysis, Resolution, ResolutionKind } from "./types.js";
+import type {
+ AnalysisTypes,
+ CheckResult,
+ EntrypointInfo,
+ EntrypointResolutionAnalysis,
+ Resolution,
+ ResolutionKind,
+} from "./types.js";
export interface CheckPackageOptions {
/**
@@ -24,15 +31,17 @@ export interface CheckPackageOptions {
}
export async function checkPackage(pkg: Package, options?: CheckPackageOptions): Promise {
- const files = pkg.listFiles();
- const types = files.some(ts.hasTSFileExtension) ? "included" : false;
- const parts = files[0].split("/");
- let packageName = parts[2];
- if (packageName.startsWith("@")) {
- packageName = parts.slice(2, 4).join("/");
- }
- const packageJsonContent = JSON.parse(pkg.readFile(`/node_modules/${packageName}/package.json`));
- const packageVersion = packageJsonContent.version;
+ const types: AnalysisTypes | false = pkg.typesPackage
+ ? {
+ kind: "@types",
+ ...pkg.typesPackage,
+ definitelyTypedUrl: JSON.parse(pkg.readFile(`/node_modules/${pkg.typesPackage.packageName}/package.json`))
+ .homepage,
+ }
+ : pkg.containsTypes()
+ ? { kind: "included" }
+ : false;
+ const { packageName, packageVersion } = pkg;
if (!types) {
return { packageName, packageVersion, types };
}
@@ -131,7 +140,12 @@ function getEntrypointInfo(
options: CheckPackageOptions | undefined
): Record {
const packageJson = JSON.parse(fs.readFile(`/node_modules/${packageName}/package.json`));
- const entrypoints = getEntrypoints(fs, packageJson.exports, options);
+ let entrypoints = getEntrypoints(fs, packageJson.exports, options);
+ if (fs.typesPackage) {
+ const typesPackageJson = JSON.parse(fs.readFile(`/node_modules/${fs.typesPackage.packageName}/package.json`));
+ const typesEntrypoints = getEntrypoints(fs, typesPackageJson.exports, options);
+ entrypoints = unique([...entrypoints, ...typesEntrypoints]);
+ }
const result: Record = {};
for (const entrypoint of entrypoints) {
const resolutions: Record = {
diff --git a/packages/core/src/createPackage.ts b/packages/core/src/createPackage.ts
index 88adb7f..03d7c11 100644
--- a/packages/core/src/createPackage.ts
+++ b/packages/core/src/createPackage.ts
@@ -1,50 +1,197 @@
import { untar } from "@andrewbranch/untar.js";
import { gunzipSync } from "fflate";
import ts from "typescript";
-import { parsePackageSpec } from "./utils.js";
-import { maxSatisfying } from "semver";
-
-export interface Package {
- packageName: string;
- packageVersion: string;
- readFile: (path: string) => string;
- fileExists: (path: string) => boolean;
- directoryExists: (path: string) => boolean;
- listFiles: () => string[];
+import { parsePackageSpec, type ParsedPackageSpec } from "./utils.js";
+import { maxSatisfying, major, minor, valid, validRange } from "semver";
+
+export class Package {
+ #files: Record = {};
+ readonly packageName: string;
+ readonly packageVersion: string;
+ readonly typesPackage?: {
+ packageName: string;
+ packageVersion: string;
+ };
+
+ constructor(
+ files: Record,
+ packageName: string,
+ packageVersion: string,
+ typesPackage?: {
+ packageName: string;
+ packageVersion: string;
+ }
+ ) {
+ this.#files = files;
+ this.packageName = packageName;
+ this.packageVersion = packageVersion;
+ this.typesPackage = typesPackage;
+ }
+
+ readFile(path: string): string {
+ const file = this.#files[path];
+ if (!file) {
+ throw new Error(`File not found: ${path}`);
+ }
+ if (typeof file === "string") {
+ return file;
+ }
+ const content = new TextDecoder().decode(file);
+ this.#files[path] = content;
+ return content;
+ }
+
+ fileExists(path: string): boolean {
+ return path in this.#files;
+ }
+
+ directoryExists(path: string): boolean {
+ path = ts.ensureTrailingDirectorySeparator(path);
+ for (const file in this.#files) {
+ if (file.startsWith(path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ containsTypes(directory = "/"): boolean {
+ return this.listFiles(directory).some(ts.hasTSFileExtension);
+ }
+
+ listFiles(directory = "/"): string[] {
+ directory = ts.ensureTrailingDirectorySeparator(directory);
+ return directory === "/"
+ ? Object.keys(this.#files)
+ : Object.keys(this.#files).filter((f) => f.startsWith(directory));
+ }
+
+ mergedWithTypes(typesPackage: Package): Package {
+ const files = { ...this.#files, ...typesPackage.#files };
+ return new Package(files, this.packageName, this.packageVersion, {
+ packageName: typesPackage.packageName,
+ packageVersion: typesPackage.packageVersion,
+ });
+ }
+}
+
+export interface CreatePackageFromNpmOptions {
+ /**
+ * Controls inclusion of a corresponding `@types` package. Ignored if the implementation
+ * package contains TypeScript files. The value is the version or SemVer range of the
+ * `@types` package to include, `true` to infer the version from the implementation
+ * package version, or `false` to prevent inclusion of a `@types` package.
+ * @default true
+ */
+ definitelyTyped?: string | boolean;
}
-export async function createPackageFromNpm(packageSpec: string): Promise {
+export async function createPackageFromNpm(
+ packageSpec: string,
+ { definitelyTyped = true }: CreatePackageFromNpmOptions = {}
+): Promise {
const parsed = parsePackageSpec(packageSpec);
if (parsed.status === "error") {
throw new Error(parsed.error);
}
const packageName = parsed.data.name;
- const packageVersion = parsed.data.version || "latest";
+ const spec =
+ parsed.data.versionKind === "none" && typeof definitelyTyped === "string"
+ ? parsePackageSpec(`${packageName}@${definitelyTyped}`)
+ : parsed;
+ const { tarballUrl, version } = await getNpmTarballUrl(spec.data || parsed.data);
+ const pkg = await createPackageFromTarballUrl(tarballUrl);
+ if (!definitelyTyped || pkg.containsTypes()) {
+ return pkg;
+ }
+
+ const typesPackageName = ts.getTypesPackageName(packageName);
+ let typesPackageData;
+ if (definitelyTyped === true) {
+ try {
+ typesPackageData = await getNpmTarballUrl({
+ name: typesPackageName,
+ versionKind: "range",
+ version: `${major(version)}.${minor(version)}`,
+ });
+ } catch {
+ try {
+ typesPackageData = await getNpmTarballUrl({
+ name: typesPackageName,
+ versionKind: "range",
+ version: `${major(version)}`,
+ });
+ } catch {
+ try {
+ typesPackageData = await getNpmTarballUrl({
+ name: typesPackageName,
+ versionKind: "tag",
+ version: "latest",
+ });
+ } catch {
+ typesPackageData = undefined;
+ }
+ }
+ }
+ } else {
+ typesPackageData = await getNpmTarballUrl({
+ name: typesPackageName,
+ versionKind: valid(definitelyTyped) ? "exact" : validRange(definitelyTyped) ? "range" : "tag",
+ version: definitelyTyped,
+ });
+ }
+
+ if (typesPackageData) {
+ return pkg.mergedWithTypes(await createPackageFromTarballUrl(typesPackageData.tarballUrl));
+ }
+ return pkg;
+}
+
+async function getNpmTarballUrl(packageSpec: ParsedPackageSpec): Promise<{ tarballUrl: string; version: string }> {
const registryUrl =
- parsed.data.versionKind === "range"
- ? `https://registry.npmjs.org/${packageName}`
- : `https://registry.npmjs.org/${packageName}/${packageVersion}`;
- const Accept = parsed.data.versionKind === "range" ? "application/vnd.npm.install-v1+json" : "application/json";
+ packageSpec.versionKind === "range" || (packageSpec.versionKind === "tag" && packageSpec.version !== "latest")
+ ? `https://registry.npmjs.org/${packageSpec.name}`
+ : `https://registry.npmjs.org/${packageSpec.name}/${packageSpec.version || "latest"}`;
+ const Accept =
+ packageSpec.versionKind === "range" || (packageSpec.versionKind === "tag" && packageSpec.version !== "latest")
+ ? "application/vnd.npm.install-v1+json"
+ : "application/json";
const doc = await fetch(registryUrl, { headers: { Accept } }).then((r) => r.json());
- let tarballUrl;
- if (parsed.data.versionKind === "range") {
- const version = maxSatisfying(Object.keys(doc.versions), parsed.data.version);
+ let tarballUrl, version;
+ if (packageSpec.versionKind === "range") {
+ version = maxSatisfying(Object.keys(doc.versions), packageSpec.version);
if (!version) {
- throw new Error(`No version found matching '${packageVersion}'`);
+ throw new Error(`No version found matching '${packageSpec.version}'`);
+ }
+ tarballUrl = doc.versions[version].dist.tarball;
+ } else if (packageSpec.versionKind === "tag" && packageSpec.version !== "latest") {
+ version = doc["dist-tags"][packageSpec.version];
+ if (!version) {
+ throw new Error(`No version found matching '${packageSpec.version}'`);
}
tarballUrl = doc.versions[version].dist.tarball;
} else {
+ version = doc.version;
tarballUrl = doc.dist.tarball;
}
- return createPackageFromTarballUrl(tarballUrl);
+ return { version, tarballUrl };
}
export async function createPackageFromTarballUrl(tarballUrl: string): Promise {
- const tarball = new Uint8Array((await fetch(tarballUrl).then((r) => r.arrayBuffer())) satisfies ArrayBuffer);
+ const tarball = await fetchTarball(tarballUrl);
return createPackageFromTarballData(tarball);
}
-export async function createPackageFromTarballData(tarball: Uint8Array): Promise {
+async function fetchTarball(tarballUrl: string) {
+ return new Uint8Array((await fetch(tarballUrl).then((r) => r.arrayBuffer())) satisfies ArrayBuffer);
+}
+
+export function createPackageFromTarballData(tarball: Uint8Array): Package {
+ const { files, packageName, packageVersion } = extractTarball(tarball);
+ return new Package(files, packageName, packageVersion);
+}
+
+function extractTarball(tarball: Uint8Array) {
const data = untar(gunzipSync(tarball));
const prefix = data[0].filename.substring(0, data[0].filename.indexOf("/") + 1);
const packageJsonText = data.find((f) => f.filename === `${prefix}package.json`)?.fileData;
@@ -55,27 +202,5 @@ export async function createPackageFromTarballData(tarball: Uint8Array): Promise
acc[ts.combinePaths("/node_modules/" + packageName, file.filename.substring(prefix.length))] = file.fileData;
return acc;
}, {});
-
- return {
- packageName,
- packageVersion,
- readFile: (path: string) => {
- const file = files[path];
- if (!file) {
- throw new Error(`File not found: ${path}`);
- }
- return new TextDecoder().decode(file);
- },
- fileExists: (path: string) => path in files,
- directoryExists: (path: string) => {
- path = ts.ensureTrailingDirectorySeparator(path);
- for (const file in files) {
- if (file.startsWith(path)) {
- return true;
- }
- }
- return false;
- },
- listFiles: () => Object.keys(files),
- };
+ return { files, packageName, packageVersion };
}
diff --git a/packages/core/src/multiCompilerHost.ts b/packages/core/src/multiCompilerHost.ts
index fc33d2f..6e1a0b2 100644
--- a/packages/core/src/multiCompilerHost.ts
+++ b/packages/core/src/multiCompilerHost.ts
@@ -128,7 +128,9 @@ export class CompilerHostWrapper {
private createCompilerHost(fs: Package): ts.CompilerHost {
const sourceFileCache = new Map();
return {
- ...fs,
+ fileExists: fs.fileExists.bind(fs),
+ readFile: fs.readFile.bind(fs),
+ directoryExists: fs.directoryExists.bind(fs),
getSourceFile: (fileName) => {
const path = toPath(fileName);
const cached = sourceFileCache.get(path);
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 4b777c9..7542536 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -9,10 +9,21 @@ export interface EntrypointInfo {
isWildcard: boolean;
}
+export interface IncludedTypes {
+ kind: "included";
+}
+export interface TypesPackage {
+ kind: "@types";
+ packageName: string;
+ packageVersion: string;
+ definitelyTypedUrl?: string;
+}
+export type AnalysisTypes = IncludedTypes | TypesPackage;
+
export interface Analysis {
packageName: string;
packageVersion: string;
- types: "included";
+ types: AnalysisTypes;
entrypoints: Record;
problems: Problem[];
}
@@ -98,10 +109,10 @@ export type ProblemKind = Problem["kind"];
export type FileProblemKind = FileProblem["kind"];
export type ResolutionBasedFileProblemKind = ResolutionBasedFileProblem["kind"];
-export type Failable = { status: "error"; error: string } | { status: "success"; data: T };
+export type Failable = { status: "error"; error: string; data?: never } | { status: "success"; data: T };
export interface ParsedPackageSpec {
name: string;
- versionKind: "none" | "exact" | "range";
+ versionKind: "none" | "exact" | "range" | "tag";
version: string;
}
diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts
index 051059f..5485dca 100644
--- a/packages/core/src/utils.ts
+++ b/packages/core/src/utils.ts
@@ -160,12 +160,6 @@ export function parsePackageSpec(input: string): Failable {
error: "Invalid package name",
};
}
- if (input.substring(0, i) === "@types") {
- return {
- status: "error",
- error: "@types packages are not supported",
- };
- }
i++;
}
i = input.indexOf("@", i);
@@ -201,7 +195,7 @@ export function parsePackageSpec(input: string): Failable {
};
}
return {
- status: "error",
- error: "Invalid version",
+ status: "success",
+ data: { versionKind: "tag", name, version },
};
}
diff --git a/packages/core/test/fixtures/@types__big.js@6.2.0.tgz b/packages/core/test/fixtures/@types__big.js@6.2.0.tgz
new file mode 100644
index 0000000..1346ca6
Binary files /dev/null and b/packages/core/test/fixtures/@types__big.js@6.2.0.tgz differ
diff --git a/packages/core/test/fixtures/@types__react@18.2.21.tgz b/packages/core/test/fixtures/@types__react@18.2.21.tgz
new file mode 100644
index 0000000..256624d
Binary files /dev/null and b/packages/core/test/fixtures/@types__react@18.2.21.tgz differ
diff --git a/packages/core/test/fixtures/big.js@6.2.1.tgz b/packages/core/test/fixtures/big.js@6.2.1.tgz
new file mode 100644
index 0000000..2debf14
Binary files /dev/null and b/packages/core/test/fixtures/big.js@6.2.1.tgz differ
diff --git a/packages/core/test/fixtures/react@18.2.0.tgz b/packages/core/test/fixtures/react@18.2.0.tgz
new file mode 100644
index 0000000..6cb2cb9
Binary files /dev/null and b/packages/core/test/fixtures/react@18.2.0.tgz differ
diff --git a/packages/core/test/snapshots.test.ts b/packages/core/test/snapshots.test.ts
index 665b222..d9eaa96 100644
--- a/packages/core/test/snapshots.test.ts
+++ b/packages/core/test/snapshots.test.ts
@@ -21,13 +21,24 @@ describe("snapshots", async () => {
}
});
+ const typesPackages: Record = {
+ "big.js@6.2.1.tgz": "@types__big.js@6.2.0.tgz",
+ "react@18.2.0.tgz": "@types__react@18.2.21.tgz",
+ };
+
for (const fixture of fs.readdirSync(new URL("../fixtures", import.meta.url))) {
- if (fixture === ".DS_Store") {
+ if (fixture === ".DS_Store" || fixture.startsWith("@types__")) {
continue;
}
test(fixture, async () => {
const tarball = await readFile(new URL(`../fixtures/${fixture}`, import.meta.url));
- const analysis = await checkPackage(await createPackageFromTarballData(tarball));
+ const typesTarball = typesPackages[fixture]
+ ? await readFile(new URL(`../fixtures/${typesPackages[fixture]}`, import.meta.url))
+ : undefined;
+ const pkg = createPackageFromTarballData(tarball);
+ const analysis = await checkPackage(
+ typesTarball ? pkg.mergedWithTypes(createPackageFromTarballData(typesTarball)) : pkg
+ );
const snapshotURL = new URL(`../snapshots/${fixture}.md`, import.meta.url);
const expectedSnapshot = [
`# ${fixture}`,
diff --git a/packages/core/test/snapshots/big.js@6.2.1.tgz.md b/packages/core/test/snapshots/big.js@6.2.1.tgz.md
new file mode 100644
index 0000000..ac48955
--- /dev/null
+++ b/packages/core/test/snapshots/big.js@6.2.1.tgz.md
@@ -0,0 +1,58 @@
+# big.js@6.2.1.tgz
+
+## Problems
+
+```json
+[
+ {
+ "kind": "FalseCJS",
+ "entrypoint": ".",
+ "resolutionKind": "node16-esm"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.mjs",
+ "resolutionKind": "node10"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.mjs",
+ "resolutionKind": "node16-cjs"
+ },
+ {
+ "kind": "CJSResolvesToESM",
+ "entrypoint": "./big.mjs",
+ "resolutionKind": "node16-cjs"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.mjs",
+ "resolutionKind": "node16-esm"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.mjs",
+ "resolutionKind": "bundler"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.js",
+ "resolutionKind": "node10"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.js",
+ "resolutionKind": "node16-cjs"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.js",
+ "resolutionKind": "node16-esm"
+ },
+ {
+ "kind": "UntypedResolution",
+ "entrypoint": "./big.js",
+ "resolutionKind": "bundler"
+ }
+]
+```
\ No newline at end of file
diff --git a/packages/core/test/snapshots/react@18.2.0.tgz.md b/packages/core/test/snapshots/react@18.2.0.tgz.md
new file mode 100644
index 0000000..1b51016
--- /dev/null
+++ b/packages/core/test/snapshots/react@18.2.0.tgz.md
@@ -0,0 +1,7 @@
+# react@18.2.0.tgz
+
+## Problems
+
+```json
+[]
+```
\ No newline at end of file
diff --git a/packages/web/src/index.html b/packages/web/src/index.html
index 4e166c0..1041a47 100644
--- a/packages/web/src/index.html
+++ b/packages/web/src/index.html
@@ -43,7 +43,7 @@ Are the types wrong?
-
+
diff --git a/packages/web/src/renderer.ts b/packages/web/src/renderer.ts
index c26be33..8705a15 100644
--- a/packages/web/src/renderer.ts
+++ b/packages/web/src/renderer.ts
@@ -59,10 +59,7 @@ export function subscribeRenderer(events: Events) {
function render(prevState: State) {
const state = getState();
updateView(messageElement, Message, { isError: state.message?.isError, text: state.message?.text || "" });
- updateView(packageInfoElement, PackageInfo, {
- name: state.analysis?.packageName,
- version: state.analysis?.packageVersion,
- });
+ updateView(packageInfoElement, PackageInfo, { analysis: state.analysis?.types ? state.analysis : undefined });
updateView(problemsElement, ProblemList, { analysis: state.analysis });
updateView(resolutionsElement, ChecksTable, { analysis: state.analysis });
updateView(checkButton, CheckButton, { disabled: !state.packageInfo.parsed });
diff --git a/packages/web/src/views/PackageInfo.ts b/packages/web/src/views/PackageInfo.ts
index d46bf2d..4de60d5 100644
--- a/packages/web/src/views/PackageInfo.ts
+++ b/packages/web/src/views/PackageInfo.ts
@@ -1,14 +1,32 @@
-export function PackageInfo(props: { name?: string; version?: string }) {
- if (!props.name || !props.version) {
+import type { Analysis } from "@arethetypeswrong/core/types";
+
+export function PackageInfo({ analysis }: { analysis?: Analysis }) {
+ if (!analysis) {
return { className: "display-none" };
}
return {
className: "",
innerHTML: `
- ${props.name} v${props.version}
-
- (npm,
- unpkg)
- `,
+
+ ${analysis.packageName} v${analysis.packageVersion}
+
+ (npm,
+ unpkg)
+
+
+ ${
+ analysis.types.kind === "@types"
+ ? `
+
+ ${analysis.types.packageName} v${analysis.types.packageVersion}
+
+ (npm,
+ unpkg,
+ DefinitelyTyped)
+
+
+ `
+ : ""
+ }`,
};
}
diff --git a/packages/web/worker/worker.ts b/packages/web/worker/worker.ts
index 0510143..9defa0c 100644
--- a/packages/web/worker/worker.ts
+++ b/packages/web/worker/worker.ts
@@ -4,6 +4,7 @@ import {
createPackageFromTarballData,
type CheckResult,
} from "@arethetypeswrong/core";
+import { parsePackageSpec } from "@arethetypeswrong/core/utils";
export interface CheckPackageEventData {
kind: "check-package";
@@ -25,7 +26,11 @@ export interface ResultMessage {
onmessage = async (event: MessageEvent) => {
const result = await checkPackage(
event.data.kind === "check-file"
- ? await createPackageFromTarballData(event.data.file)
+ ? createPackageFromTarballData(event.data.file)
+ : event.data.packageSpec.startsWith("@types/")
+ ? await createPackageFromNpm(unmangleScopedPackageName(event.data.packageSpec), {
+ definitelyTyped: parsePackageSpec(event.data.packageSpec).data?.version,
+ })
: await createPackageFromNpm(event.data.packageSpec)
);
postMessage({
@@ -35,3 +40,7 @@ onmessage = async (event: MessageEvent