rman 1.0.12 → 1.2.1

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.
Files changed (102) hide show
  1. package/README.md +90 -70
  2. package/cli.js +226 -14
  3. package/commands/build.command.js +1 -0
  4. package/commands/changed.command.js +2 -2
  5. package/commands/changelog.command.js +13 -15
  6. package/commands/config.command.js +61 -0
  7. package/commands/diff.command.js +9 -4
  8. package/commands/exec.command.js +2 -8
  9. package/commands/github-release.command.js +1 -0
  10. package/commands/info.command.d.ts +9 -0
  11. package/commands/info.command.js +12 -2
  12. package/commands/run.command.js +5 -8
  13. package/commands/test.command.js +1 -0
  14. package/commands/version.command.js +53 -14
  15. package/constants.js +1 -1
  16. package/core/config.d.ts +265 -17
  17. package/core/config.js +651 -76
  18. package/core/custom-command.d.ts +133 -0
  19. package/core/custom-command.js +99 -0
  20. package/core/extends-config.d.ts +27 -0
  21. package/core/extends-config.js +89 -0
  22. package/core/manifest.d.ts +222 -0
  23. package/core/manifest.js +150 -0
  24. package/core/merge-config.d.ts +70 -0
  25. package/core/merge-config.js +193 -0
  26. package/core/package.d.ts +73 -7
  27. package/core/package.js +86 -24
  28. package/core/plugin.d.ts +112 -0
  29. package/core/plugin.js +189 -0
  30. package/core/repository.d.ts +91 -1
  31. package/core/repository.js +277 -132
  32. package/core/resolve-target.d.ts +12 -0
  33. package/core/resolve-target.js +33 -0
  34. package/core/run-step.d.ts +75 -0
  35. package/core/run-step.js +1 -0
  36. package/core/version-scheme.d.ts +134 -0
  37. package/core/version-scheme.js +148 -0
  38. package/core/workspace.d.ts +68 -0
  39. package/core/workspace.js +83 -0
  40. package/index.d.ts +55 -8
  41. package/index.js +42 -7
  42. package/interfaces/rman-config.interface.d.ts +222 -46
  43. package/package.json +16 -7
  44. package/services/change-hash.service.d.ts +88 -0
  45. package/services/change-hash.service.js +112 -0
  46. package/services/changelog.service.d.ts +8 -13
  47. package/services/changelog.service.js +12 -11
  48. package/services/conventional-commits.service.d.ts +73 -0
  49. package/services/conventional-commits.service.js +116 -0
  50. package/services/docker-publish.service.js +1 -1
  51. package/services/exec.service.js +1 -1
  52. package/services/github-release.service.d.ts +2 -2
  53. package/services/github-release.service.js +10 -5
  54. package/services/list.service.js +5 -2
  55. package/services/run.service.d.ts +112 -6
  56. package/services/run.service.js +265 -89
  57. package/services/system-info.d.ts +22 -7
  58. package/services/system-info.js +8 -23
  59. package/services/version-plan.service.d.ts +244 -0
  60. package/services/version-plan.service.js +414 -0
  61. package/services/version.service.d.ts +102 -82
  62. package/services/version.service.js +226 -434
  63. package/services.d.ts +5 -3
  64. package/services.js +5 -3
  65. package/utils/bin-path.d.ts +59 -0
  66. package/utils/bin-path.js +82 -0
  67. package/utils/child-tracker.d.ts +16 -0
  68. package/utils/child-tracker.js +30 -0
  69. package/utils/exec.d.ts +13 -2
  70. package/utils/exec.js +17 -17
  71. package/utils/git.d.ts +9 -3
  72. package/utils/git.js +10 -2
  73. package/utils/package-filter.d.ts +33 -2
  74. package/utils/package-filter.js +47 -7
  75. package/utils/printable-config.d.ts +15 -0
  76. package/utils/printable-config.js +42 -0
  77. package/utils/release-version.js +3 -3
  78. package/utils/run-bin.d.ts +46 -0
  79. package/utils/run-bin.js +63 -0
  80. package/utils/version-stamp.d.ts +14 -6
  81. package/utils/version-stamp.js +25 -13
  82. package/commands/ci.command.js +0 -30
  83. package/commands/clean.command.d.ts +0 -3
  84. package/commands/clean.command.js +0 -36
  85. package/commands/publish.command.d.ts +0 -3
  86. package/commands/publish.command.js +0 -225
  87. package/rmanrc.schema.json +0 -392
  88. package/services/ci.service.d.ts +0 -40
  89. package/services/ci.service.js +0 -204
  90. package/services/clean.service.d.ts +0 -42
  91. package/services/clean.service.js +0 -226
  92. package/services/publish.service.d.ts +0 -79
  93. package/services/publish.service.js +0 -273
  94. package/utils/change-hash.d.ts +0 -68
  95. package/utils/change-hash.js +0 -98
  96. package/utils/conventional-commits.d.ts +0 -52
  97. package/utils/conventional-commits.js +0 -90
  98. package/utils/npm-run-path.d.ts +0 -67
  99. package/utils/npm-run-path.js +0 -63
  100. package/utils/workspace-range.d.ts +0 -17
  101. package/utils/workspace-range.js +0 -28
  102. /package/commands/{ci.command.d.ts → config.command.d.ts} +0 -0
package/core/config.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import path from 'path';
1
2
  import semver from 'semver';
2
3
  import type { RmanConfig } from '../interfaces/rman-config.interface.js';
3
4
  /**
@@ -32,9 +33,10 @@ export declare function readDirConfig(dirname: string): Promise<RmanConfig>;
32
33
  * `.rmanrc` therefore configures the *root package* - which is where repo-wide settings
33
34
  * (`packageManager`, `allowBranch`, `version.*`, `githubRelease.*`) are read from anyway - and
34
35
  * not, silently, every package under it.
35
- * - **A `"[selector]"` block configures the packages it names** (`"[*]"` for all of them,
36
- * `"[*-dialect]"` for a glob over package names). This is the only way a directory speaks about
37
- * anything but its own package.
36
+ * - **A `"[selector]"` block configures the packages it names** - `"[*]"` for all of them (the root
37
+ * included), `"[ws:*]"` for every one but the root, `"[/]"` for the root alone, `"[*-dialect]"`
38
+ * for a glob over package names. See `parseSelector`. This is the only way a directory speaks
39
+ * about anything but its own package.
38
40
  *
39
41
  * Splitting the two matters because the same key means different things to the two audiences. The
40
42
  * clearest case is `run.<script>.postScript`: on a package it's that package's build hook, run in
@@ -42,21 +44,98 @@ export declare function readDirConfig(dirname: string): Promise<RmanConfig>;
42
44
  * cascade that fed one declaration to both ran a package-relative command (`node
43
45
  * ../../support/postbuild.cjs`) at the root, where it cannot resolve.
44
46
  *
45
- * `packageName` is what selectors match against; without it (resolving the root's own config, say)
46
- * selector blocks contribute nothing at all.
47
+ * `packageName` is what selectors match against; without it, selector blocks contribute nothing at
48
+ * all. The root package passes its own, since `"[/]"` and `"[*]"` speak to it.
47
49
  */
48
50
  export declare function resolveConfig(rootDir: string, targetDir: string, cache?: Map<string, RmanConfig>, packageName?: string): Promise<RmanConfig>;
49
- /** A config key naming packages rather than settings: `"[*]"`, `"[*-dialect]"`, `"[pkg-a]"`. The
51
+ /** A config key naming packages rather than settings: `"[*]"`, `"[/]"`, `"[ws:*]"`, `"[pkg-a]"`. The
50
52
  * brackets are what keep this space from colliding with real config keys - no setting starts with
51
53
  * one - and in YAML they also mean the key always needs quoting (`"[*]":`), since a bare `[*]`
52
54
  * parses as a flow sequence. */
53
55
  export declare function isSelectorKey(key: string): boolean;
56
+ /**
57
+ * **Which packages a selector speaks for.** Three audiences, because a repository has three:
58
+ *
59
+ * | | |
60
+ * | --- | --- |
61
+ * | `"[/]"` | the **root package** only |
62
+ * | `"[*]"`, `"[pkg-a]"`, `"[*-dialect]"` | **every** package the glob matches, root included |
63
+ * | `"[ws:*]"`, `"[workspace:pkg-*]"` | every **non-root** package the glob matches |
64
+ *
65
+ * `/` for the root because that is what a repository root is called everywhere else, and it cannot
66
+ * collide with a package name. `ws:` is a qualifier on the glob rather than a separate spelling of
67
+ * `*`, so `"[ws:pkg-*]"` means what it looks like.
68
+ *
69
+ * **`"[*]"` includes the root, and that is a change from how it used to read.** Before, selectors
70
+ * were not applied to the root at all, so `"[*]"` silently meant "the workspace packages" - a
71
+ * catch-all with an exception nothing in the syntax mentioned. The three names above say which
72
+ * audience is meant; `"[ws:*]"` is the old behaviour, now spelled.
73
+ */
74
+ export declare function parseSelector(key: string): {
75
+ scope: 'root' | 'all' | 'workspace';
76
+ test: (name: string) => boolean;
77
+ };
54
78
  /** The glob inside a selector key, as a `RegExp` anchored at both ends - so `"[*-dialect]"` matches
55
79
  * `mysql-dialect` but not `my-dialect-helper`. Glob rather than regex, to match every other
56
80
  * pattern in rman (`allowBranch`, `changelog.tagPattern`, `clean.include`). */
57
81
  export declare function selectorToRegExp(key: string): RegExp;
58
82
  /** One package, as an expression sees it - the same shape for the package the config belongs to
59
83
  * and for the repository itself, so `${{ repository.basename }}` reads the way `${{ pkg.basename }}` does. */
84
+ /**
85
+ * `file` in a `${{ ... }}` expression: what is actually on disk, resolved against **the package
86
+ * the config was resolved for** - so one declaration in a `"[*]"` block asks each package about
87
+ * its own directory.
88
+ *
89
+ * The pair exists because a config has two different questions about a path, and answering both
90
+ * with one function would mean picking a wrong default for the other:
91
+ *
92
+ * ```yaml
93
+ * "[*]":
94
+ * run:
95
+ * build:
96
+ * # first of these that exists, and an error naming the config path if none do
97
+ * exec: 'tsc -b ${{ file.exists("tsconfig-build.json") || file.resolve("tsconfig.json") }}'
98
+ * ```
99
+ *
100
+ * **Every member asks a question, and none may ever change anything - no `copy`, no `write`, no
101
+ * `mkdir`.** Not a matter of taste: this is evaluated when the config *resolves*, which every
102
+ * command does, so a member that acted would act on `rman list`, `rman info` and `rman config`.
103
+ *
104
+ * That has been tried, in the only way a missing function can be: a shared config reaching for a
105
+ * `file.copyMany(...)` that does not exist made **every** rman command exit 1 - and had it existed,
106
+ * the quieter outcome would have been files copied by `rman list`. Work belongs in a step
107
+ * (`run.<script>`'s slots, `version`'s hooks), which is the one place rman runs anything, and a
108
+ * step can now be a function - so there is nothing this would enable that is not already possible
109
+ * at the right moment.
110
+ */
111
+ export interface FileScope {
112
+ /**
113
+ * The absolute path if it exists, **`''` if it does not** - so `a || b || c` picks the first one
114
+ * present, and so a miss never reaches the "nullish inside a string" guard that `undefined` would
115
+ * trip. Accepts a relative path (against the package directory) or an absolute one.
116
+ *
117
+ * It returns a path rather than a boolean on purpose: the caller almost always wants the path,
118
+ * and a separate `file.path()` to fetch it after a boolean test would read the disk twice and
119
+ * invite the two calls to disagree.
120
+ */
121
+ exists(target: string): string;
122
+ /** The absolute path, or **throws** - for a file whose absence is a mistake rather than a case to
123
+ * handle. The error names the config path holding the expression, like any other. */
124
+ resolve(target: string): string;
125
+ /**
126
+ * The first of several that exists, or **throws** naming every candidate it tried:
127
+ *
128
+ * ```yaml
129
+ * exec: 'tsc -b ${{ file.resolveFirst("tsconfig-build.json", "tsconfig.build.json", "tsconfig.json") }}'
130
+ * ```
131
+ *
132
+ * The same thing an `exists() || exists() || resolve()` chain does, said once - and it cannot be
133
+ * got subtly wrong the way that chain can: ending it in `exists()` leaves `tsc -b ` with no
134
+ * argument when nothing matches, and tsc then silently falls back to the directory's default
135
+ * rather than reporting that the package has no build config.
136
+ */
137
+ resolveFirst(...targets: string[]): string;
138
+ }
60
139
  export interface PackageScope {
61
140
  /** The package's own name, scope included (`@sqb/builder`). */
62
141
  name: string;
@@ -73,9 +152,29 @@ export interface PackageScope {
73
152
  /** That directory relative to the repository root (`packages/builder`), which is what a command
74
153
  * addressing another package from the root usually needs. Empty string for the root itself. */
75
154
  relativeDir: string;
76
- /** The whole `package.json`, as a copy - so an expression can reach a field rman itself has no
77
- * opinion about (`pkg.json.engines.node`). */
78
- json: Record<string, unknown>;
155
+ /**
156
+ * Which ecosystem this package belongs to - `'node'` for one read by `rman-node`, empty when no
157
+ * plugin claimed it. The same `Package.provider`, so one declaration can address a single
158
+ * ecosystem in a polyglot repository (`if: "${{ pkg.provider === 'node' }}"`).
159
+ */
160
+ provider: string;
161
+ /**
162
+ * The whole manifest, as a copy - so an expression can reach a field rman itself has no opinion
163
+ * about (`${{ pkg.manifest.engines.node }}`).
164
+ *
165
+ * Named `manifest`, not `json`: which file a package's identity lives in is the ecosystem's
166
+ * business now (see `ManifestProvider`), and `json` was that assumption showing through the one
167
+ * remaining user-facing name. A config written against `${{ pkg.json... }}` needs the rename.
168
+ */
169
+ manifest: Record<string, unknown>;
170
+ /**
171
+ * The version this run is about to write - **bound only during `version`**, and only once its
172
+ * plan is computed. Reading it anywhere else throws rather than yielding `undefined`: no other
173
+ * command has a target version, so an expression asking for one has been put in the wrong place,
174
+ * and a config that quietly evaluates to "undefined" is the failure this evaluator exists to
175
+ * prevent.
176
+ */
177
+ targetVersion: string;
79
178
  }
80
179
  /** Facts about the repository, on top of the root package's own - because the repository root *is*
81
180
  * a package (`repository.name` is what its `package.json` says, `repository.basename` the directory
@@ -87,10 +186,6 @@ export interface RepositoryScope extends PackageScope {
87
186
  packages: PackageScope[];
88
187
  /** One package by name, or `undefined` - for reaching a sibling's directory. */
89
188
  package(name: string): PackageScope | undefined;
90
- /** Read from git only if an expression actually asks for it, then remembered: a repository that
91
- * never mentions these pays nothing, and every command resolves config. All `undefined` outside
92
- * a git checkout, which is a legitimate state rather than an error. */
93
- git: GitScope;
94
189
  }
95
190
  export interface GitScope {
96
191
  branch: string | undefined;
@@ -99,18 +194,74 @@ export interface GitScope {
99
194
  /** Whether the working tree has uncommitted changes. */
100
195
  dirty: boolean | undefined;
101
196
  }
102
- /** What a `${{ ... }}` expression can see - the bindings of the fresh global it is evaluated in.
103
- * Namespaced rather than a flat bag of loose names: one obvious place per fact, and room to add
104
- * helpers to `pkg`/`repository` later without crowding the global. */
197
+ /**
198
+ * What a `${{ ... }}` expression can see - the bindings of the fresh global it is evaluated in.
199
+ * Namespaced rather than a flat bag of loose names: one obvious place per fact, and room to add
200
+ * helpers to `pkg`/`repository` later without crowding the global.
201
+ *
202
+ * Alongside these, **the config's own top-level keys are bound bare** (`${{ publish.directory }}`,
203
+ * `${{ clean.include }}`) - see `interpolateConfig`. They are not listed here because they come
204
+ * from the config being interpolated, not from this object; a name here wins over a config key of
205
+ * the same name.
206
+ *
207
+ * **Trap: bare `${{ version }}` is the `version` *options block*, not the package's version
208
+ * string** - that is `${{ pkg.version }}`. Same word, two different things, and the plain one
209
+ * belongs to the config because every other config key is reachable that way.
210
+ */
105
211
  export interface ConfigScope {
106
212
  /** The package the config was resolved for - which is what lets one declaration at the root
107
213
  * still say something package-specific. */
108
214
  pkg: PackageScope;
109
215
  repository: RepositoryScope;
216
+ /** Paths, resolved against the package the config was resolved for. */
217
+ file: FileScope;
218
+ /**
219
+ * The **contents** of a structured file - `${{ read('tsconfig.json').compilerOptions.outDir }}` -
220
+ * where `file` answers only where one is. Resolved against `pkg.dirname` like `file`, so a
221
+ * `"[*]"` block asks each package about its own; a repository-level file is reached through
222
+ * `read(path.join(repository.dirname, ...))`.
223
+ *
224
+ * `.json`, `.yml`/`.yaml` and `.ini` by extension, or name it for a file that does not say
225
+ * (`read('.npmrc', 'ini')`). **Throws** when the file is absent, as `file.resolve` does - compose
226
+ * with `file.exists` when its absence is a case to handle.
227
+ *
228
+ * **A manifest is `pkg.manifest`, not this.** `read('package.json')` works and is the wrong
229
+ * answer: which file a package's identity lives in belongs to the ecosystem, so that expression
230
+ * is already wrong in a Cargo package sitting beside a Node one.
231
+ *
232
+ * The result is **deeply frozen and shared** - see `readStructuredFile`. Spread it to change it.
233
+ */
234
+ read: ReadFile;
110
235
  env: Record<string, string | undefined>;
111
236
  /** rman's own `semver`, for the arithmetic every release config eventually wants
112
237
  * (`semver.major(pkg.version)`). */
113
238
  semver: typeof semver;
239
+ /**
240
+ * Node's own `node:path` - `${{ path.join(pkg.dirname, 'LICENSE') }}`, rather than gluing
241
+ * strings with `+ "/" +` and getting a double separator or none.
242
+ *
243
+ * The platform's flavour, not `path.posix`, so a joined path is the one the shell on *this*
244
+ * machine understands; `path.posix` and `path.win32` are reachable through it when a config
245
+ * genuinely needs one of them (a Docker image path, say, which is always posix).
246
+ */
247
+ path: typeof path;
248
+ /**
249
+ * The checkout: branch, sha, whether the tree is dirty.
250
+ *
251
+ * **Top level, not `repository.git`** - which is where it used to be, and the move is the point.
252
+ * `repository` shares its shape with `pkg` because the repository root *is* a package, and its
253
+ * only other members (`monorepo`, `packages`, `package()`) say something about the repository as
254
+ * a container of packages. A branch name says nothing about any package; it describes the
255
+ * working tree every one of them happens to be sitting in - the same kind of ambient fact as
256
+ * `env`, and it belongs beside it.
257
+ *
258
+ * **Read from git only if an expression actually asks**, then remembered for the whole run: every
259
+ * command resolves config, and a repository that never mentions git must not pay for one. See
260
+ * `Repository.configScope` for the getter, and `interpolateConfig` for why the context is built
261
+ * from property descriptors rather than a spread - a spread would fire this getter on every
262
+ * command, which is exactly what moving it up here risked.
263
+ */
264
+ git: GitScope;
114
265
  }
115
266
  /**
116
267
  * Evaluates every `${{ ... }}` expression in **every** string value of a resolved config, against
@@ -121,8 +272,13 @@ export interface ConfigScope {
121
272
  * clean:
122
273
  * include: ["build", "../../coverage/${{ pkg.basename }}"]
123
274
  * publish:
275
+ * directory: build
124
276
  * docker:
125
277
  * image: "panates/${{ pkg.basename }}:${{ semver.major(pkg.version) }}"
278
+ * run:
279
+ * build:
280
+ * # the config's own keys are in scope, so this is not a second copy of "build"
281
+ * after: "cp README.md ${{ publish.directory }}/"
126
282
  * ```
127
283
  *
128
284
  * Every string, with no list of "interpolated keys" to memorize - a rule with exceptions is a rule
@@ -149,4 +305,96 @@ export interface ConfigScope {
149
305
  * A failing expression throws with the config path that holds it, rather than being left in place:
150
306
  * silently passing through a mistake is how a config ends up quietly doing nothing.
151
307
  */
152
- export declare function interpolateConfig<T>(config: T, scope: ConfigScope): T;
308
+ export interface InterpolateOptions {
309
+ /** Config paths to leave entirely untouched - `DEFERRED_PATHS`, when the whole config is walked. */
310
+ skip?: string[];
311
+ /** Where `config` sits in the whole config, for a caller interpolating a fragment. */
312
+ at?: string[];
313
+ }
314
+ export declare function interpolateConfig<T>(config: T, scope: ConfigScope, options?: InterpolateOptions): T;
315
+ /**
316
+ * Config paths left untouched when a repository's config is first resolved, and evaluated only by
317
+ * the command that runs them.
318
+ *
319
+ * `version`'s own hooks are the one place `${{ pkg.targetVersion }}` makes sense, and the version
320
+ * being written is not known until `version` has computed its plan - long after the config was
321
+ * resolved. Evaluating these eagerly would throw while merely *loading* the repository, so any
322
+ * command at all would fail on a config that mentions it.
323
+ */
324
+ export declare const DEFERRED_PATHS: string[];
325
+ /**
326
+ * Paths whose value is a **step** - something to run later - rather than a setting to compute now.
327
+ * `*` matches one path segment (`run.<script>.exec`).
328
+ *
329
+ * This is what tells a step function from a value function, and the two live side by side in one
330
+ * config:
331
+ *
332
+ * ```js
333
+ * '[ws:*]': {
334
+ * clean: { include: ({ vars, value }) => [...value, vars.buildDir] }, // a value: called here
335
+ * run: { build: { after: ({ pkg }) => copyDocs(pkg) } }, // a step: called by `run`
336
+ * }
337
+ * ```
338
+ *
339
+ * **The key decides, and it already did.** `run.build.exec: 'tsc -b'` is a shell command and
340
+ * `publish.directory: 'build'` is a path - not because of anything about the strings, but because of
341
+ * where they sit. A function inherits the same rule, so nothing new has to be learned and no marker
342
+ * has to be remembered. The alternative was inspecting the function (arity, parameter names), which
343
+ * is the kind of guess `loadPlugins` refuses to make about a module's export for the same reason:
344
+ * guessing wrong here means running build-time code while merely loading the repository, or
345
+ * silently never running it.
346
+ *
347
+ * A **string** at one of these paths is still interpolated - `exec: 'tsc -b ${{ file.resolve(...) }}'`
348
+ * has to keep working - so this is narrower than `DEFERRED_PATHS`, which skips its paths entirely.
349
+ */
350
+ export declare const STEP_PATHS: string[];
351
+ /**
352
+ * Keys whose **whole subtree** is code rather than config, so no function under them is a value to
353
+ * compute. `plugins` is the only one, and it has to be here: an entry may be the plugin *object*
354
+ * itself, and an `RmanPlugin` is almost entirely functions - `manifest.read`, `workspace.resolve`,
355
+ * `versionPlanner`, `binPaths`, and every command's `builder` and `handler`.
356
+ *
357
+ * Measured, and it is why this exists: with `plugins` walked like any other key, resolving the
358
+ * config of a repository that named a plugin called that plugin's yargs builder with the config
359
+ * scope - `Config function in "plugins[0].commands[0].builder" failed: cmd.option is not a
360
+ * function`. A `plugins` entry is loaded by `loadPlugins`, never read as a setting.
361
+ */
362
+ export declare const CODE_SUBTREES: string[];
363
+ /** The `file` namespace for one package's directory - see `FileScope`. */
364
+ /**
365
+ * `read` in a `${{ ... }}` expression (and in a value function): a structured file's **contents**,
366
+ * parsed - where `file` answers only where a path is.
367
+ *
368
+ * ```yaml
369
+ * "[*]":
370
+ * run:
371
+ * build:
372
+ * exec: 'tsc --outDir ${{ read("tsconfig.json").compilerOptions.outDir }}'
373
+ * ```
374
+ *
375
+ * `cache` is shared across every package (see `Repository.configScope`) and keyed by what the file
376
+ * *is*, not merely where - so the same file read by twenty packages is parsed once, and a file rman
377
+ * itself rewrites mid-run is re-read rather than remembered. See `readStructuredFile`.
378
+ */
379
+ export declare function createReadScope(dirname: string, cache: Map<string, CachedFile>): ReadFile;
380
+ /**
381
+ * What `read` can parse.
382
+ *
383
+ * **`.env` is deliberately absent, and that is the durable part of this list**: `env` is already in
384
+ * scope, and a `.env` file exists to be loaded *into* an environment by something else - a config
385
+ * reading one as data would mean two different things called the environment.
386
+ *
387
+ * Nothing else is excluded on principle. `xml` arrived because a `pom.xml` or a `.csproj` holds a
388
+ * version exactly the way a `package.json` does, and rman is language-agnostic; the earlier line
389
+ * ("no new parsers") did not survive it, since xmldom *is* a new one.
390
+ */
391
+ export type FileFormat = 'json' | 'yaml' | 'ini' | 'xml';
392
+ /** `read(path)`, or `read(path, 'ini')` for a file whose name does not say what it is (`.npmrc`). */
393
+ export type ReadFile = (target: string, format?: FileFormat) => unknown;
394
+ /** One parsed file, kept against the identity of the bytes it came from - see `readStructuredFile`. */
395
+ export interface CachedFile {
396
+ /** `mtimeNs:size`. */
397
+ stamp: string;
398
+ value: unknown;
399
+ }
400
+ export declare function createFileScope(dirname: string): FileScope;