diff --git a/README.md b/README.md index d32e7647..ae670dea 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,24 @@ The CLI supports the following options: - `--entrypoint `: Confirm the specified file and its transitive dependencies can be resolved using the generated import map. Auto-enabled when importmap is written into an HTML file. Can be specified multiple times. - `--dev`: Include devDependencies from `package.json`. Also favor `"development"` in [package exports](https://nodejs.org/docs/latest-v16.x/api/packages.html#packages_conditions_definitions). - `--keep-unused`: Keep all mappings, even if they are not currently used by entry file or its transitive dependencies. +- `--config `: Read additional settings from the given JSON configuration file. + +#### Configuration file + +A configuration file in JSON format can be specified with the `--config` CLI option. The following example shows such a JSON file with all supported properties set to their default values: + +```json +{ + "dir": ".", + "dev": false, + "keepUnused": false, + "entryPoints": [], + "manualImportmap": {}, + "packagesManualOverrides": {} +} +``` + +All properties are optional and CLI options like `--dev`, `--entrypoint` and `--keep-unused` will override the values read from the config. The objects `manualImportmap` and `packagesManualOverrides` have the same format as the corresponding objects in the API. ### API diff --git a/package.json b/package.json index 386fed94..da605589 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/importmap-node-module", - "version": "7.2.2", + "version": "7.3.0", "description": "Generate importmap for node_modules", "license": "MIT", "repository": { diff --git a/src/cli.mjs b/src/cli.mjs index 5f2189be..1ace50b9 100755 --- a/src/cli.mjs +++ b/src/cli.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +import { readFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; import { parseArgs } from "node:util"; import { writeImportmaps } from "./main.js"; @@ -21,10 +22,12 @@ const options = { "keep-unused": { type: "boolean", }, + "config": { + type: "string", + }, }; const { values, positionals } = parseArgs({ options, allowPositionals: true }); const outfile = positionals[0]; -values.entrypoint ??= []; if (values.help || positionals.length === 0) { usage(); @@ -46,21 +49,26 @@ if ( process.exit(1); } +const config = values.config ? JSON.parse(await readFile(values.config, "utf-8")) : {}; +const dev = values.dev ?? config.dev; + const currentDirectoryUrl = pathToFileURL(`${process.cwd()}/`); await writeImportmaps({ - directoryUrl: new URL(values.dir || ".", currentDirectoryUrl), + directoryUrl: new URL(values.dir ?? config.dir ?? ".", currentDirectoryUrl), importmaps: { [outfile]: { nodeMappings: { - devDependencies: values.dev, - packageUserConditions: values.dev ? ["development"] : [], + devDependencies: dev, + packageUserConditions: dev ? ["development"] : [], }, importResolution: { - entryPoints: values.entryPoints, + entryPoints: values.entrypoint ?? config.entryPoints ?? [], }, - keepUnusedMappings: values["keep-unused"], + keepUnusedMappings: values["keep-unused"] ?? config.keepUnused, + manualImportmap: config.manualImportmap, }, }, + packagesManualOverrides: config.packagesManualOverrides, }); function usage() { @@ -76,6 +84,7 @@ Options: --entrypoint file.js Confirm the specified file and its transitive dependencies can be resolved using the generated import map. Can be specified multiple times. --dev Include devDependencies from package.json and pick "developement" in package conditions. --keep-unused Remove mappings not used by any entrypoint or their transitive dependencies. Requires --entrypoint. + --config config.json Read additional settings from the given JSON configuration file. For more advanced options, see the API.`); } diff --git a/tests/cli/cli.test.mjs b/tests/cli/basic/cli_basic.test.mjs similarity index 91% rename from tests/cli/cli.test.mjs rename to tests/cli/basic/cli_basic.test.mjs index 866224c9..e911da46 100644 --- a/tests/cli/cli.test.mjs +++ b/tests/cli/basic/cli_basic.test.mjs @@ -9,7 +9,7 @@ replaceFileStructureSync({ from: import.meta.resolve("./fixtures/"), to: import.meta.resolve("./git_ignored/"), }); -execSync("node ../../../src/cli.mjs ./index.html", { +execSync("node ../../../../src/cli.mjs ./index.html", { cwd: new URL(import.meta.resolve("./git_ignored/")), }); copyFileSync({ diff --git a/tests/cli/fixtures/index.html b/tests/cli/basic/fixtures/index.html similarity index 100% rename from tests/cli/fixtures/index.html rename to tests/cli/basic/fixtures/index.html diff --git a/tests/cli/fixtures/main.js b/tests/cli/basic/fixtures/main.js similarity index 100% rename from tests/cli/fixtures/main.js rename to tests/cli/basic/fixtures/main.js diff --git a/tests/cli/fixtures/node_modules/foo/foo.js b/tests/cli/basic/fixtures/node_modules/foo/foo.js similarity index 100% rename from tests/cli/fixtures/node_modules/foo/foo.js rename to tests/cli/basic/fixtures/node_modules/foo/foo.js diff --git a/tests/cli/fixtures/node_modules/foo/package.json b/tests/cli/basic/fixtures/node_modules/foo/package.json similarity index 100% rename from tests/cli/fixtures/node_modules/foo/package.json rename to tests/cli/basic/fixtures/node_modules/foo/package.json diff --git a/tests/cli/fixtures/package.json b/tests/cli/basic/fixtures/package.json similarity index 100% rename from tests/cli/fixtures/package.json rename to tests/cli/basic/fixtures/package.json diff --git a/tests/cli/snapshots/index.html b/tests/cli/basic/snapshots/index.html similarity index 100% rename from tests/cli/snapshots/index.html rename to tests/cli/basic/snapshots/index.html diff --git a/tests/cli/config_file/cli_config_file.test.mjs b/tests/cli/config_file/cli_config_file.test.mjs new file mode 100644 index 00000000..ae223a81 --- /dev/null +++ b/tests/cli/config_file/cli_config_file.test.mjs @@ -0,0 +1,35 @@ +import { assert } from "@jsenv/assert"; +import { startBuildServer } from "@jsenv/core"; +import { copyFileSync, replaceFileStructureSync } from "@jsenv/filesystem"; +import { takeFileSnapshot } from "@jsenv/snapshot"; +import { execSync } from "node:child_process"; +import { executeHtml } from "../../execute_html.js"; + +const indexHtmlFileSnapshot = takeFileSnapshot( + import.meta.resolve("./snapshots/index.html"), +); +replaceFileStructureSync({ + from: import.meta.resolve("./fixtures/"), + to: import.meta.resolve("./git_ignored/"), +}); +execSync( + "node ../../../../src/cli.mjs ./index.html --config=importmap.config.json", + { + cwd: new URL(import.meta.resolve("./git_ignored/")), + }, +); +copyFileSync({ + from: import.meta.resolve("./git_ignored/index.html"), + to: import.meta.resolve("./snapshots/index.html"), +}); +indexHtmlFileSnapshot.compare(); + +const buildServer = await startBuildServer({ + buildDirectoryUrl: import.meta.resolve("./git_ignored/"), + keepProcessAlive: false, + port: 0, + logLevel: "warn", +}); +const actual = await executeHtml(`${buildServer.origin}/index.html`); +const expect = "axios"; +assert({ actual, expect }); diff --git a/tests/cli/config_file/fixtures/importmap.config.json b/tests/cli/config_file/fixtures/importmap.config.json new file mode 100644 index 00000000..c0b3881d --- /dev/null +++ b/tests/cli/config_file/fixtures/importmap.config.json @@ -0,0 +1,7 @@ +{ + "packagesManualOverrides": { + "axios": { + "exports": "./dist/esm/axios.js" + } + } +} diff --git a/tests/cli/config_file/fixtures/index.html b/tests/cli/config_file/fixtures/index.html new file mode 100644 index 00000000..071ec4a0 --- /dev/null +++ b/tests/cli/config_file/fixtures/index.html @@ -0,0 +1,17 @@ + + + + Title + + + + + + + + + diff --git a/tests/cli/config_file/fixtures/main.js b/tests/cli/config_file/fixtures/main.js new file mode 100644 index 00000000..6091e77c --- /dev/null +++ b/tests/cli/config_file/fixtures/main.js @@ -0,0 +1,6 @@ +/* globals window */ + +// eslint-disable-next-line import-x/no-unresolved +import { axios } from "axios"; + +window.resolveResultPromise(axios); diff --git a/tests/cli/config_file/fixtures/node_modules/axios/dist/esm/axios.js b/tests/cli/config_file/fixtures/node_modules/axios/dist/esm/axios.js new file mode 100644 index 00000000..dc03e499 --- /dev/null +++ b/tests/cli/config_file/fixtures/node_modules/axios/dist/esm/axios.js @@ -0,0 +1 @@ +export const axios = 'axios'; \ No newline at end of file diff --git a/tests/cli/config_file/fixtures/node_modules/axios/package.json b/tests/cli/config_file/fixtures/node_modules/axios/package.json new file mode 100644 index 00000000..3a8534bc --- /dev/null +++ b/tests/cli/config_file/fixtures/node_modules/axios/package.json @@ -0,0 +1,5 @@ +{ + "name": "axios", + "main": "./foo.js", + "private": true +} \ No newline at end of file diff --git a/tests/cli/config_file/fixtures/package.json b/tests/cli/config_file/fixtures/package.json new file mode 100644 index 00000000..d1e33047 --- /dev/null +++ b/tests/cli/config_file/fixtures/package.json @@ -0,0 +1,7 @@ +{ + "name": "root", + "private": true, + "dependencies": { + "axios": "*" + } +} diff --git a/tests/cli/config_file/snapshots/index.html b/tests/cli/config_file/snapshots/index.html new file mode 100644 index 00000000..48253f2a --- /dev/null +++ b/tests/cli/config_file/snapshots/index.html @@ -0,0 +1,25 @@ + + + + Title + + + + + + + + + + \ No newline at end of file