Skip to content

Commit

Permalink
Add support for import maps.
Browse files Browse the repository at this point in the history
Fixes #4 .

Note that some newer supported Node.js versions have a regression where `deepStrictEquals` fails to compare the URL HREF, but a fix has recently been merged:

- nodejs/node#50836
- nodejs/node#50853 (comment)

Until the fix has been published in new Node.js releases, we can rely on the GitHub Actions CI workflow testing with Node.js v18 which doesn’t have the regression.
  • Loading branch information
jaydenseric committed Oct 15, 2024
1 parent 2f357d8 commit 216774f
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 81 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Next

### Minor

- Added support for [import maps](https://github.com/WICG/import-maps), fixing [#4](https://github.com/jaydenseric/find-unused-exports/issues/4):
- Added the CLI command `find-unused-exports` argument `--import-map`.
- Added the function `findUnusedExports` option `importMap`.

### Patch

- Updated dependencies.
Expand Down
19 changes: 19 additions & 0 deletions directoryPathToFileURL.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-check

import { pathToFileURL } from "node:url";

/**
* Converts a directory path to a file URL that always ends with `/` so it can
* be safely used as a base URL for constructing file URLs with relative paths.
* @param {string} directoryPath Directory path to convert.
* @returns {URL} Directory file URL.
*/
export default function directoryPathToFileURL(directoryPath) {
if (typeof directoryPath !== "string")
throw new TypeError("Argument 1 `directoryPath` must be a string.");

// @ts-ignore https://github.com/microsoft/TypeScript/issues/59996
return pathToFileURL(
directoryPath.endsWith("/") ? directoryPath : `${directoryPath}/`,
);
}
35 changes: 35 additions & 0 deletions directoryPathToFileURL.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @ts-check

import { deepStrictEqual, throws } from "node:assert";
import { describe, it } from "node:test";

import directoryPathToFileURL from "./directoryPathToFileURL.mjs";

describe("Function `directoryPathToFileURL`.", { concurrency: true }, () => {
it("Argument 1 `directoryPath` not a string.", () => {
throws(() => {
directoryPathToFileURL(
// @ts-expect-error Testing invalid.
true,
);
}, new TypeError("Argument 1 `directoryPath` must be a string."));
});

it("Directory path ends with `/`.", () => {
const directoryPath = "/a/b/c/";

deepStrictEqual(
directoryPathToFileURL(directoryPath),
new URL(`file://${directoryPath}`),
);
});

it("Directory path doesn’t end with `/`.", () => {
const directoryPath = "/a/b/c";

deepStrictEqual(
directoryPathToFileURL(directoryPath),
new URL(`file://${directoryPath}/`),
);
});
});
16 changes: 16 additions & 0 deletions find-unused-exports.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env node
// @ts-check

/** @import { ImportMap } from "@import-maps/resolve" */

import { relative } from "node:path";

import arg from "arg";
Expand All @@ -18,21 +20,35 @@ import reportCliError from "./reportCliError.mjs";
async function findUnusedExportsCli() {
try {
const {
"--import-map": importMapJson,
"--module-glob": moduleGlob,
"--resolve-file-extensions": resolveFileExtensionsList,
"--resolve-index-files": resolveIndexFiles,
} = arg({
"--import-map": String,
"--module-glob": String,
"--resolve-file-extensions": String,
"--resolve-index-files": Boolean,
});

/** @type {ImportMap | undefined} */
let importMap;

if (importMapJson) {
try {
importMap = JSON.parse(importMapJson);
} catch {
throw new CliError(`The \`--import-map\` argument must be JSON.`);
}
}

if (resolveIndexFiles && !resolveFileExtensionsList)
throw new CliError(
"The `--resolve-index-files` flag can only be used with the `--resolve-file-extensions` argument.",
);

const unusedExports = await findUnusedExports({
importMap,
moduleGlob,
resolveFileExtensions: resolveFileExtensionsList
? resolveFileExtensionsList.split(",")
Expand Down
59 changes: 59 additions & 0 deletions find-unused-exports.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,65 @@ describe("CLI command `find-unused-exports`.", { concurrency: true }, () => {
strictEqual(status, 1);
});

describe("Arg `--import-map`.", { concurrency: true }, () => {
it("Invalid.", async () => {
const { stdout, stderr, status, error } = spawnSync(
"node",
[FIND_UNUSED_EXPORTS_CLI_PATH, "--import-map", "_"],
{
cwd: new URL("./test/fixtures/import-map", import.meta.url),
env: {
...process.env,
FORCE_COLOR: "1",
},
},
);

if (error) throw error;

strictEqual(stdout.toString(), "");
await assertSnapshot(
stderr.toString(),
new URL(
"./test/snapshots/find-unused-exports/import-map-invalid-stderr.ans",
import.meta.url,
),
);
strictEqual(status, 1);
});

it("Valid.", async () => {
const { stdout, stderr, status, error } = spawnSync(
"node",
[
FIND_UNUSED_EXPORTS_CLI_PATH,
"--import-map",
'"$(cat import-map.json)"',
],
{
cwd: new URL("./test/fixtures/import-map", import.meta.url),
env: {
...process.env,
FORCE_COLOR: "1",
},
shell: true,
},
);

if (error) throw error;

strictEqual(stdout.toString(), "");
await assertSnapshot(
stderr.toString(),
new URL(
"./test/snapshots/find-unused-exports/import-map-valid-stderr.ans",
import.meta.url,
),
);
strictEqual(status, 1);
});
});

it("Arg `--module-glob`.", async () => {
const { stdout, stderr, status, error } = spawnSync(
"node",
Expand Down
Loading

0 comments on commit 216774f

Please sign in to comment.