importmapify 1.3.1 → 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 +23 -2
- package/deno.json +1 -1
- package/dist/mod.d.mts +34 -6
- package/dist/mod.mjs +45 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ const out = writeImportMap({
|
|
|
109
109
|
|
|
110
110
|
| Export | Signature | Purpose |
|
|
111
111
|
| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
112
|
-
| `defineConfig` |
|
|
112
|
+
| `defineConfig` | `<T extends Config>(config: T) => T` | Identity helper that types a config object for export and reuse. |
|
|
113
113
|
| `createImportMap` | `(options: CreateImportMapOptions) => ImportMapDocument` | Build the import map in memory. |
|
|
114
114
|
| `formatImportMap` | `(map: ImportMapDocument) => string` | Serialize to the canonical sorted, tab-indented JSON text. |
|
|
115
115
|
| `writeImportMap` | `(options: WriteImportMapOptions) => string` | Build, serialize, and write to disk; returns the written path. |
|
|
@@ -133,11 +133,18 @@ resolved against `root` and accepts a relative path, an absolute path, or a `fil
|
|
|
133
133
|
automatically against `out`'s directory, so a nested `out` (for example `.cache/maps/import_map.json`) still produces
|
|
134
134
|
targets that resolve correctly from the map's own location.
|
|
135
135
|
|
|
136
|
-
`Config`
|
|
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.
|
|
137
139
|
|
|
138
140
|
`defineConfig` returns its argument unchanged; it exists only to type a config object for export and reuse without a
|
|
139
141
|
manual annotation.
|
|
140
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
|
+
|
|
141
148
|
### Recipes
|
|
142
149
|
|
|
143
150
|
Add dependencies with `packages`. Each entry expands to the bare specifier plus its conformant trailing-slash entry
|
|
@@ -184,6 +191,20 @@ const map = createImportMap(config);
|
|
|
184
191
|
const written = writeImportMap(config);
|
|
185
192
|
```
|
|
186
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
|
+
|
|
187
208
|
`root`, `relativeTo`, and `out` accept a path or a `file://` URL, so `import.meta.url` needs no `.pathname`:
|
|
188
209
|
|
|
189
210
|
```ts
|
package/deno.json
CHANGED
package/dist/mod.d.mts
CHANGED
|
@@ -33,8 +33,35 @@ interface WriteImportMapOptions extends CreateImportMapOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
readonly out?: string | URL;
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
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
|
+
}
|
|
38
65
|
/**
|
|
39
66
|
* Expand a package's exact and patterned `imports` into a Deno import map.
|
|
40
67
|
*
|
|
@@ -131,9 +158,10 @@ declare function packageEntries(name: string, target: string): Record<string, st
|
|
|
131
158
|
* writeImportMap(config);
|
|
132
159
|
* ```
|
|
133
160
|
*
|
|
134
|
-
* @param config Import map configuration
|
|
135
|
-
* @returns The same `config` value,
|
|
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.
|
|
136
164
|
*/
|
|
137
|
-
declare function defineConfig(config:
|
|
165
|
+
declare function defineConfig<T extends Config>(config: T): T;
|
|
138
166
|
//#endregion
|
|
139
|
-
export { type Config, 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,8 +1,8 @@
|
|
|
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";
|
|
4
|
+
import fs, { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import path, { dirname, resolve } from "node:path";
|
|
6
6
|
import { argv, cwd } from "node:process";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
//#region src/expand.ts
|
|
@@ -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,8 +487,9 @@ function packageEntries(name, target) {
|
|
|
471
487
|
* writeImportMap(config);
|
|
472
488
|
* ```
|
|
473
489
|
*
|
|
474
|
-
* @param config Import map configuration
|
|
475
|
-
* @returns The same `config` value,
|
|
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
494
|
function defineConfig(config) {
|
|
478
495
|
return config;
|
|
@@ -574,6 +591,7 @@ async function resolveGenerateOptions(flags) {
|
|
|
574
591
|
if (base.relativeTo !== void 0) options.relativeTo = base.relativeTo;
|
|
575
592
|
if (conditions !== void 0) options.conditions = conditions;
|
|
576
593
|
if (extensions !== void 0) options.extensions = extensions;
|
|
594
|
+
if (base.hooks !== void 0) options.hooks = base.hooks;
|
|
577
595
|
return options;
|
|
578
596
|
}
|
|
579
597
|
/** DreamCLI command that writes, checks, or prints an expanded Deno import map. */
|
|
@@ -593,22 +611,28 @@ const generateCommand = command("generate").description("Expand package.json sub
|
|
|
593
611
|
...options,
|
|
594
612
|
relativeTo: options.relativeTo ?? dirname(outPath)
|
|
595
613
|
};
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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) {
|
|
603
624
|
error(`${outPath} is stale`);
|
|
604
625
|
setExitCode(1);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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}`);
|
|
609
631
|
}
|
|
610
|
-
|
|
611
|
-
|
|
632
|
+
await options.hooks?.["generate:done"]?.({
|
|
633
|
+
...hookContext,
|
|
634
|
+
map
|
|
635
|
+
});
|
|
612
636
|
});
|
|
613
637
|
//#endregion
|
|
614
638
|
//#region src/mod.ts
|