importmapify 1.3.0 → 1.4.0
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/README.md +51 -17
- package/deno.json +1 -1
- package/dist/mod.d.mts +34 -4
- package/dist/mod.mjs +50 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ npx importmapify --root . --out import_map.json
|
|
|
43
43
|
| `--no-config` | | Skip config file discovery | off |
|
|
44
44
|
| `--check` | | Exit 1 if the output file is stale, without writing it | off |
|
|
45
45
|
| `--stdout` | | Print the map instead of writing it | off |
|
|
46
|
+
| `--quiet` | `-q` | Suppress the `Wrote <path>` confirmation (written to stderr) | off |
|
|
46
47
|
|
|
47
48
|
Add global and test-scoped dependencies from the CLI:
|
|
48
49
|
|
|
@@ -61,9 +62,21 @@ order (first match wins): `importmapify.config.{mjs,cjs,js,ts,mts,cts}`, then `.
|
|
|
61
62
|
|
|
62
63
|
```js
|
|
63
64
|
// importmapify.config.mjs
|
|
65
|
+
import { defineConfig } from 'importmapify';
|
|
66
|
+
|
|
67
|
+
export default defineConfig({
|
|
68
|
+
packages: { dreamcli: 'jsr:@kjanat/dreamcli@^3' },
|
|
69
|
+
extensions: ['ts'],
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or annotate a plain object, no import:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
/** @type {import('importmapify').Config} */
|
|
64
77
|
export default {
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
packages: { dreamcli: 'jsr:@kjanat/dreamcli@^3' },
|
|
79
|
+
extensions: ['ts'],
|
|
67
80
|
};
|
|
68
81
|
```
|
|
69
82
|
|
|
@@ -72,9 +85,8 @@ Then `npx importmapify` reads it automatically. Point at a specific file with `-
|
|
|
72
85
|
|
|
73
86
|
Explicitly-passed flags override the config, which overrides built-in defaults: `--import`/`--package`/`--scope` merge
|
|
74
87
|
onto the config's records with flag keys winning; `--condition`/`--ext` replace; `--root`/`--manifest`/`--out` win when
|
|
75
|
-
passed. Configs are imported natively; a TypeScript config needs a runtime
|
|
76
|
-
|
|
77
|
-
> = 22.6), so on older Node use a `.mjs`/`.cjs`/`.js` config.
|
|
88
|
+
passed. Configs are imported natively; a TypeScript config needs a type-stripping runtime (Bun, Deno, or Node \>= 22.6);
|
|
89
|
+
on older Node use a `.mjs`/`.cjs`/`.js` config.
|
|
78
90
|
|
|
79
91
|
## Library
|
|
80
92
|
|
|
@@ -95,13 +107,13 @@ const out = writeImportMap({
|
|
|
95
107
|
});
|
|
96
108
|
```
|
|
97
109
|
|
|
98
|
-
| Export | Signature
|
|
99
|
-
| ----------------- |
|
|
100
|
-
| `defineConfig` |
|
|
101
|
-
| `createImportMap` | `(options: CreateImportMapOptions) => ImportMapDocument`
|
|
102
|
-
| `formatImportMap` | `(map: ImportMapDocument) => string`
|
|
103
|
-
| `writeImportMap` | `(options: WriteImportMapOptions) => string`
|
|
104
|
-
| `packageEntries` | `(name: string, target: string) => Record<string, string>`
|
|
110
|
+
| Export | Signature | Purpose |
|
|
111
|
+
| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
112
|
+
| `defineConfig` | `<T extends Config>(config: T) => T` | Identity helper that types a config object for export and reuse. |
|
|
113
|
+
| `createImportMap` | `(options: CreateImportMapOptions) => ImportMapDocument` | Build the import map in memory. |
|
|
114
|
+
| `formatImportMap` | `(map: ImportMapDocument) => string` | Serialize to the canonical sorted, tab-indented JSON text. |
|
|
115
|
+
| `writeImportMap` | `(options: WriteImportMapOptions) => string` | Build, serialize, and write to disk; returns the written path. |
|
|
116
|
+
| `packageEntries` | `(name: string, target: string) => Record<string, string>` | Build the bare and trailing-slash entry pair a package needs to resolve its subpaths. |
|
|
105
117
|
|
|
106
118
|
`CreateImportMapOptions`:
|
|
107
119
|
|
|
@@ -121,9 +133,18 @@ resolved against `root` and accepts a relative path, an absolute path, or a `fil
|
|
|
121
133
|
automatically against `out`'s directory, so a nested `out` (for example `.cache/maps/import_map.json`) still produces
|
|
122
134
|
targets that resolve correctly from the map's own location.
|
|
123
135
|
|
|
136
|
+
`Config` extends `Partial<WriteImportMapOptions>` with an optional `hooks` field — the shape a config file's default
|
|
137
|
+
export and `defineConfig` take, with every field optional. An omitted `root` defaults to the config file's own
|
|
138
|
+
directory; the CLI supplies the remaining defaults.
|
|
139
|
+
|
|
124
140
|
`defineConfig` returns its argument unchanged; it exists only to type a config object for export and reuse without a
|
|
125
141
|
manual annotation.
|
|
126
142
|
|
|
143
|
+
`hooks` are lifecycle functions the CLI runs around generation, modeled on tsdown's hooks. `generate:before` runs before
|
|
144
|
+
the filesystem is scanned, so building generated targets there keeps them out of a stale map; `generate:done` runs after
|
|
145
|
+
the map is emitted. Each receives `{ root, out }` (`generate:done` also gets the finished `map`) and may be async. The
|
|
146
|
+
synchronous library functions ignore `hooks`.
|
|
147
|
+
|
|
127
148
|
### Recipes
|
|
128
149
|
|
|
129
150
|
Add dependencies with `packages`. Each entry expands to the bare specifier plus its conformant trailing-slash entry
|
|
@@ -170,6 +191,20 @@ const map = createImportMap(config);
|
|
|
170
191
|
const written = writeImportMap(config);
|
|
171
192
|
```
|
|
172
193
|
|
|
194
|
+
Build before scanning with a `generate:before` hook, so a map that includes build output does not drop entries when it
|
|
195
|
+
runs before the build:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { execSync } from 'node:child_process';
|
|
199
|
+
import { defineConfig } from 'importmapify';
|
|
200
|
+
|
|
201
|
+
export default defineConfig({
|
|
202
|
+
hooks: {
|
|
203
|
+
'generate:before': () => execSync('bun run build', { stdio: 'inherit' }),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
173
208
|
`root`, `relativeTo`, and `out` accept a path or a `file://` URL, so `import.meta.url` needs no `.pathname`:
|
|
174
209
|
|
|
175
210
|
```ts
|
|
@@ -398,11 +433,10 @@ import { writeImportMap } from 'importmapify';
|
|
|
398
433
|
const root = '/path/to/source-tree';
|
|
399
434
|
const importMap = writeImportMap({ root, out: 'import_map.json' });
|
|
400
435
|
|
|
401
|
-
const documentation = execFileSync(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
);
|
|
436
|
+
const documentation = execFileSync('deno', ['doc', '--import-map', importMap, '--json', 'src/index.ts'], {
|
|
437
|
+
cwd: root,
|
|
438
|
+
encoding: 'utf8',
|
|
439
|
+
});
|
|
406
440
|
```
|
|
407
441
|
|
|
408
442
|
### `@deno/doc`
|
package/deno.json
CHANGED
package/dist/mod.d.mts
CHANGED
|
@@ -33,6 +33,35 @@ interface WriteImportMapOptions extends CreateImportMapOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
readonly out?: string | URL;
|
|
35
35
|
}
|
|
36
|
+
/** Resolved paths shared by every generation hook. */
|
|
37
|
+
interface HookContext {
|
|
38
|
+
/** Absolute project root that gets scanned. */
|
|
39
|
+
readonly root: string;
|
|
40
|
+
/** Absolute output path the map resolves against. */
|
|
41
|
+
readonly out: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Lifecycle hooks the CLI runs around import map generation, modeled on tsdown's hooks.
|
|
45
|
+
*
|
|
46
|
+
* Each hook may be async; the CLI awaits it. The synchronous library functions ignore hooks.
|
|
47
|
+
*/
|
|
48
|
+
interface ImportMapHooks {
|
|
49
|
+
/** Runs before the filesystem is scanned. Build pattern targets here so they exist when patterns expand. */
|
|
50
|
+
readonly "generate:before": (context: HookContext) => void | Promise<void>;
|
|
51
|
+
/** Runs after the map is generated and emitted. */
|
|
52
|
+
readonly "generate:done": (context: HookContext & {
|
|
53
|
+
readonly map: ImportMapDocument;
|
|
54
|
+
}) => void | Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* An importmapify config file: the shape a config file's default export and {@linkcode defineConfig} take.
|
|
58
|
+
* Every field is optional; the config loader and CLI supply {@linkcode CreateImportMapOptions.root | root}
|
|
59
|
+
* and the remaining defaults.
|
|
60
|
+
*/
|
|
61
|
+
interface Config extends Partial<WriteImportMapOptions> {
|
|
62
|
+
/** Lifecycle hooks the CLI runs around generation. Ignored by {@link writeImportMap} and {@link createImportMap}. */
|
|
63
|
+
readonly hooks?: Partial<ImportMapHooks>;
|
|
64
|
+
}
|
|
36
65
|
/**
|
|
37
66
|
* Expand a package's exact and patterned `imports` into a Deno import map.
|
|
38
67
|
*
|
|
@@ -129,9 +158,10 @@ declare function packageEntries(name: string, target: string): Record<string, st
|
|
|
129
158
|
* writeImportMap(config);
|
|
130
159
|
* ```
|
|
131
160
|
*
|
|
132
|
-
* @param
|
|
133
|
-
* @returns The same `
|
|
161
|
+
* @param config Import map configuration; every field is optional.
|
|
162
|
+
* @returns The same `config` value with its exact type preserved, so a config that includes `root` stays
|
|
163
|
+
* assignable to {@link writeImportMap} while one that omits it is still a valid config file.
|
|
134
164
|
*/
|
|
135
|
-
declare function defineConfig(
|
|
165
|
+
declare function defineConfig<T extends Config>(config: T): T;
|
|
136
166
|
//#endregion
|
|
137
|
-
export { type CreateImportMapOptions, type ImportMapDocument, type WriteImportMapOptions, createImportMap, defineConfig, formatImportMap, packageEntries, writeImportMap };
|
|
167
|
+
export { type Config, type CreateImportMapOptions, type HookContext, type ImportMapDocument, type ImportMapHooks, type WriteImportMapOptions, createImportMap, defineConfig, formatImportMap, packageEntries, writeImportMap };
|
package/dist/mod.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { bold, cyan, detectHyperlinkSupport } from "ansispeck";
|
|
3
3
|
import { CLIError, cli, command, flag } from "dreamcli";
|
|
4
|
-
import fs, { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import path, { dirname } from "node:path";
|
|
6
|
-
import { cwd } from "node:process";
|
|
4
|
+
import fs, { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import path, { dirname, resolve } from "node:path";
|
|
6
|
+
import { argv, cwd } from "node:process";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
//#region src/expand.ts
|
|
9
9
|
function escapeRegExp(value) {
|
|
@@ -191,20 +191,34 @@ function asScopes(value) {
|
|
|
191
191
|
}
|
|
192
192
|
return scopes;
|
|
193
193
|
}
|
|
194
|
+
function isHook(value) {
|
|
195
|
+
return typeof value === "function";
|
|
196
|
+
}
|
|
197
|
+
function asHooks(value) {
|
|
198
|
+
if (!isRecord(value)) return;
|
|
199
|
+
const hooks = {};
|
|
200
|
+
const before = value["generate:before"];
|
|
201
|
+
if (isHook(before)) hooks["generate:before"] = before;
|
|
202
|
+
const done = value["generate:done"];
|
|
203
|
+
if (isHook(done)) hooks["generate:done"] = done;
|
|
204
|
+
return hooks["generate:before"] === void 0 && hooks["generate:done"] === void 0 ? void 0 : hooks;
|
|
205
|
+
}
|
|
194
206
|
/**
|
|
195
207
|
* Parse a raw config object into typed import map options.
|
|
196
208
|
*
|
|
197
|
-
* Only known fields are read
|
|
198
|
-
*
|
|
209
|
+
* Only known fields are read. An omitted `root` defaults to the config file's own directory; a relative
|
|
210
|
+
* string `root` resolves against it, so a config can anchor itself with `root: '.'`.
|
|
199
211
|
*
|
|
200
212
|
* @param config Raw default-exported config object.
|
|
201
213
|
* @param configDir Directory containing the config file.
|
|
202
|
-
* @returns The subset of {@link WriteImportMapOptions} the config declares.
|
|
214
|
+
* @returns The subset of {@link WriteImportMapOptions} the config declares, plus any hooks.
|
|
203
215
|
*/
|
|
204
216
|
function configToOptions(config, configDir) {
|
|
205
217
|
const result = {};
|
|
206
218
|
const root = asPath(config.root);
|
|
207
|
-
if (root
|
|
219
|
+
if (root === void 0) result.root = configDir;
|
|
220
|
+
else if (typeof root === "string" && !path.isAbsolute(root) && !root.startsWith("file://")) result.root = path.resolve(configDir, root);
|
|
221
|
+
else result.root = root;
|
|
208
222
|
const manifest = asString(config.manifest);
|
|
209
223
|
if (manifest !== void 0) result.manifest = manifest;
|
|
210
224
|
const out = asPath(config.out);
|
|
@@ -221,6 +235,8 @@ function configToOptions(config, configDir) {
|
|
|
221
235
|
if (additionalImports !== void 0) result.additionalImports = additionalImports;
|
|
222
236
|
const scopes = asScopes(config.scopes);
|
|
223
237
|
if (scopes !== void 0) result.scopes = scopes;
|
|
238
|
+
const hooks = asHooks(config.hooks);
|
|
239
|
+
if (hooks !== void 0) result.hooks = hooks;
|
|
224
240
|
return result;
|
|
225
241
|
}
|
|
226
242
|
/**
|
|
@@ -471,16 +487,19 @@ function packageEntries(name, target) {
|
|
|
471
487
|
* writeImportMap(config);
|
|
472
488
|
* ```
|
|
473
489
|
*
|
|
474
|
-
* @param
|
|
475
|
-
* @returns The same `
|
|
490
|
+
* @param config Import map configuration; every field is optional.
|
|
491
|
+
* @returns The same `config` value with its exact type preserved, so a config that includes `root` stays
|
|
492
|
+
* assignable to {@link writeImportMap} while one that omits it is still a valid config file.
|
|
476
493
|
*/
|
|
477
|
-
function defineConfig(
|
|
478
|
-
return
|
|
494
|
+
function defineConfig(config) {
|
|
495
|
+
return config;
|
|
479
496
|
}
|
|
480
497
|
//#endregion
|
|
481
498
|
//#region src/cli.ts
|
|
482
499
|
const EXAMPLE_TOKEN = /(?:'[^']*'|"[^"]*"|\S)+/g;
|
|
500
|
+
const JSON_MODE = argv.includes("--json");
|
|
483
501
|
function highlightExample(example) {
|
|
502
|
+
if (JSON_MODE) return example;
|
|
484
503
|
const tokens = example.match(EXAMPLE_TOKEN);
|
|
485
504
|
if (tokens === null) return example;
|
|
486
505
|
return tokens.map((token, index) => index === 0 ? bold(token) : token.startsWith("-") ? cyan(token) : token).join(" ");
|
|
@@ -572,6 +591,7 @@ async function resolveGenerateOptions(flags) {
|
|
|
572
591
|
if (base.relativeTo !== void 0) options.relativeTo = base.relativeTo;
|
|
573
592
|
if (conditions !== void 0) options.conditions = conditions;
|
|
574
593
|
if (extensions !== void 0) options.extensions = extensions;
|
|
594
|
+
if (base.hooks !== void 0) options.hooks = base.hooks;
|
|
575
595
|
return options;
|
|
576
596
|
}
|
|
577
597
|
/** DreamCLI command that writes, checks, or prints an expanded Deno import map. */
|
|
@@ -591,22 +611,28 @@ const generateCommand = command("generate").description("Expand package.json sub
|
|
|
591
611
|
...options,
|
|
592
612
|
relativeTo: options.relativeTo ?? dirname(outPath)
|
|
593
613
|
};
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
614
|
+
const hookContext = {
|
|
615
|
+
root: resolve(toPath(options.root)),
|
|
616
|
+
out: outPath
|
|
617
|
+
};
|
|
618
|
+
await options.hooks?.["generate:before"]?.(hookContext);
|
|
619
|
+
const map = createImportMap(createOptions);
|
|
620
|
+
const text = formatImportMap(map);
|
|
621
|
+
if (stdout) log(text);
|
|
622
|
+
else if (check) {
|
|
623
|
+
if ((existsSync(outPath) ? readFileSync(outPath, "utf8") : void 0) !== text) {
|
|
601
624
|
error(`${outPath} is stale`);
|
|
602
625
|
setExitCode(1);
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
626
|
+
} else if (!quiet) warn(`${outPath} is up to date`);
|
|
627
|
+
} else {
|
|
628
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
629
|
+
writeFileSync(outPath, text);
|
|
630
|
+
if (!quiet) warn(`Wrote ${outPath}`);
|
|
607
631
|
}
|
|
608
|
-
|
|
609
|
-
|
|
632
|
+
await options.hooks?.["generate:done"]?.({
|
|
633
|
+
...hookContext,
|
|
634
|
+
map
|
|
635
|
+
});
|
|
610
636
|
});
|
|
611
637
|
//#endregion
|
|
612
638
|
//#region src/mod.ts
|