@ttsc/banner 0.30.3 → 0.31.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.
@@ -0,0 +1,256 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ import type { ITtscBannerPluginConfig } from "./structures";
6
+
7
+ /**
8
+ * The shape returned by a ttsc plugin factory function.
9
+ *
10
+ * Mirrors the ttsc plugin descriptor contract. The `source` field points to the
11
+ * Go source directory that the host will compile and cache as either an
12
+ * executable sidecar or linked native source.
13
+ */
14
+ type TtscPluginDescriptor = {
15
+ /** The bundled driver host records actual writes and their physical sources. */
16
+ capabilities: { emitProvenance: true };
17
+
18
+ /** Universal config-discovery inputs consumed by the native transform. */
19
+ hostInputs?: string[];
20
+
21
+ /** Evaluation-time fingerprints paired with {@link hostInputs}. */
22
+ hostInputHashes?: Record<string, string | null>;
23
+
24
+ /** Evaluation-time physical targets paired with {@link hostInputs}. */
25
+ hostInputRealpaths?: Record<string, string | null>;
26
+
27
+ /** Human-readable plugin name used in logs and error messages. */
28
+ name: string;
29
+
30
+ /** Absolute path to the Go source directory for this plugin. */
31
+ source: string;
32
+
33
+ /**
34
+ * Pipeline stage. `"transform"` plugins may rewrite source files; `"check"`
35
+ * plugins only produce diagnostics. The framework default is `"transform"`.
36
+ */
37
+ stage?: "check" | "transform";
38
+ };
39
+
40
+ /**
41
+ * Context object passed by the ttsc host to every plugin factory function.
42
+ *
43
+ * Module anchors locate shipped Go source. Project anchors locate user
44
+ * configuration, including when the host uses a generated wrapper tsconfig.
45
+ */
46
+ type TtscPluginFactoryContext<TConfig> = {
47
+ /** Absolute path to the selected ttsc native helper, not a plugin binary. */
48
+ binary: string;
49
+
50
+ /** Working directory of the ttsc invocation. */
51
+ cwd: string;
52
+
53
+ /**
54
+ * Absolute path to the directory holding this descriptor module, the
55
+ * load-mode-independent replacement for `__dirname`.
56
+ */
57
+ dirname: string;
58
+
59
+ /**
60
+ * Absolute path to this descriptor module, the load-mode-independent
61
+ * replacement for `__filename`.
62
+ */
63
+ filename: string;
64
+
65
+ /** Host-declared anchor for implicit plugin config discovery. */
66
+ pluginConfigDir?: string;
67
+
68
+ /** The raw plugin entry from `compilerOptions.plugins[]`. */
69
+ plugin: TConfig;
70
+
71
+ /** Absolute path to the project root (directory containing tsconfig). */
72
+ projectRoot: string;
73
+
74
+ /** Absolute path to the resolved tsconfig. */
75
+ tsconfig: string;
76
+ };
77
+
78
+ /**
79
+ * Keys that the ttsc plugin host injects into every plugin entry and are not
80
+ * owned by `@ttsc/banner`. The host validates registration; this factory
81
+ * rejects unknown banner options without rejecting those registration keys.
82
+ */
83
+ const FRAMEWORK_KEYS = new Set<string>([
84
+ "enabled",
85
+ "name",
86
+ "stage",
87
+ "transform",
88
+ ]);
89
+
90
+ /**
91
+ * Plugin factory called by the ttsc host to obtain the `@ttsc/banner`
92
+ * descriptor.
93
+ *
94
+ * The only banner-specific key accepted in the tsconfig plugin entry is
95
+ * `configFile`. Any other key that is not a known framework key is rejected
96
+ * with a specific error so users discover the correct configuration surface
97
+ * (the dedicated config file) rather than silently receiving no banner.
98
+ *
99
+ * @evidence contracts/common.md#principled-implementation
100
+ * The default-export factory maps host module anchors to the shipped driver
101
+ * and project anchors to config candidates. Discovery records all seven
102
+ * candidate names in every directory from the anchor through the first one
103
+ * holding a non-directory match, including absent paths and directory markers,
104
+ * so creating a nearer config or a second file beside the match changes the
105
+ * descriptor inputs. Native evaluation validates ambiguous or invalid config;
106
+ * this factory supplies observations rather than treating existence as a
107
+ * valid banner. SHA-256 covers candidate bytes and a distinct directory marker.
108
+ *
109
+ * @evidence contracts/common.md#clear-and-simple-design
110
+ * Registration validation, discovery observations and descriptor construction
111
+ * have separate responsibilities. Private helpers isolate path discovery and
112
+ * filesystem observations; they do not implement another config evaluator.
113
+ * The driver path follows the installed package layout, while config anchors
114
+ * follow the project because those two directories have different owners.
115
+ *
116
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts
117
+ * Framework keys, candidate filenames, package identity and stage encode the
118
+ * supported registration contract. The descriptor neither evaluates user
119
+ * JavaScript nor changes a foreign loader; it records a failed probe (an absent
120
+ * or unreadable candidate) as a null hash or physical target, and native
121
+ * evaluation reports the actual error.
122
+ *
123
+ * @evidence contracts/common.md#meaningful-documentation
124
+ * JSDoc separates registration from config evaluation and explains why
125
+ * unknown inline keys are rejected. Context and descriptor fields document
126
+ * host anchoring and observed inputs. Paragraphs and reasons follow the
127
+ * documentation skill; the exported factory is documented as a host entry.
128
+ */
129
+ export default function createTtscBanner(
130
+ context: TtscPluginFactoryContext<ITtscBannerPluginConfig>,
131
+ ): TtscPluginDescriptor {
132
+ const entry = context.plugin as Record<string, unknown>;
133
+ for (const key of Object.keys(entry)) {
134
+ if (!FRAMEWORK_KEYS.has(key) && key !== "configFile") {
135
+ throw new Error(
136
+ `@ttsc/banner: tsconfig plugin entry contains unsupported key ${JSON.stringify(key)}. ` +
137
+ `Banner options must be placed in a banner.config.{ts,cts,mts,js,cjs,mjs,json} file. ` +
138
+ `The only accepted key in the tsconfig entry is "configFile" (optional path to the config file).`,
139
+ );
140
+ }
141
+ }
142
+
143
+ const configInputs = bannerConfigInputs(context);
144
+ return {
145
+ capabilities: { emitProvenance: true },
146
+ hostInputHashes: configInputs.hashes,
147
+ hostInputRealpaths: configInputs.realpaths,
148
+ hostInputs: configInputs.inputs,
149
+ name: "@ttsc/banner",
150
+ // Point at the `driver/` directory one level above `lib/` in the
151
+ // installed package tree (where the Go sources live). `context.dirname`
152
+ // is this descriptor's own directory in every load mode, unlike the
153
+ // CommonJS `__dirname`, which is undefined under a `.ts`-source or ESM load.
154
+ source: path.resolve(context.dirname, "..", "driver"),
155
+ stage: "transform",
156
+ };
157
+ }
158
+
159
+ const BANNER_CONFIG_FILENAMES = [
160
+ "banner.config.json",
161
+ "banner.config.js",
162
+ "banner.config.cjs",
163
+ "banner.config.mjs",
164
+ "banner.config.ts",
165
+ "banner.config.cts",
166
+ "banner.config.mts",
167
+ ];
168
+
169
+ /**
170
+ * Mirror native config resolution for a nonblank string `configFile` or for
171
+ * upward discovery, retaining the absent candidates that could supersede or
172
+ * make ambiguous the match. Native evaluation rejects any other `configFile`.
173
+ */
174
+ function bannerConfigInputs(
175
+ context: TtscPluginFactoryContext<ITtscBannerPluginConfig>,
176
+ ): {
177
+ hashes: Record<string, string | null>;
178
+ inputs: string[];
179
+ realpaths: Record<string, string | null>;
180
+ } {
181
+ const configFile = (context.plugin as { configFile?: unknown }).configFile;
182
+ const base = path.resolve(
183
+ context.pluginConfigDir ?? path.dirname(context.tsconfig),
184
+ );
185
+ if (typeof configFile === "string" && configFile.trim() !== "") {
186
+ const file = path.isAbsolute(configFile)
187
+ ? path.resolve(configFile)
188
+ : path.resolve(base, configFile);
189
+ return {
190
+ hashes: { [file]: hostInputHash(file) },
191
+ inputs: [file],
192
+ realpaths: { [file]: hostInputRealpath(file) },
193
+ };
194
+ }
195
+ return configDiscoveryInputs(base, BANNER_CONFIG_FILENAMES);
196
+ }
197
+
198
+ function configDiscoveryInputs(
199
+ base: string,
200
+ names: readonly string[],
201
+ ): {
202
+ hashes: Record<string, string | null>;
203
+ inputs: string[];
204
+ realpaths: Record<string, string | null>;
205
+ } {
206
+ const inputs: string[] = [];
207
+ const hashes: Record<string, string | null> = {};
208
+ const realpaths: Record<string, string | null> = {};
209
+ for (let directory = base; ; directory = path.dirname(directory)) {
210
+ const candidates = names.map((name) => path.join(directory, name));
211
+ inputs.push(...candidates);
212
+ for (const candidate of candidates) {
213
+ hashes[candidate] = hostInputHash(candidate);
214
+ realpaths[candidate] = hostInputRealpath(candidate);
215
+ }
216
+ if (candidates.some(configCandidateExists)) break;
217
+ const parent = path.dirname(directory);
218
+ if (parent === directory) break;
219
+ }
220
+ return { hashes, inputs, realpaths };
221
+ }
222
+
223
+ function hostInputRealpath(file: string): string | null {
224
+ try {
225
+ return fs.realpathSync.native(file);
226
+ } catch {
227
+ return null;
228
+ }
229
+ }
230
+
231
+ /** Hash the exact candidate state observed before discovery selects a file. */
232
+ function hostInputHash(file: string): string | null {
233
+ try {
234
+ if (fs.statSync(file).isDirectory()) {
235
+ return crypto
236
+ .createHash("sha256")
237
+ .update("ttsc:host-input:directory\0")
238
+ .digest("hex");
239
+ }
240
+ return crypto
241
+ .createHash("sha256")
242
+ .update(fs.readFileSync(file))
243
+ .digest("hex");
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+
249
+ /** Match the native discovery rule: a directory is never a config file. */
250
+ function configCandidateExists(file: string): boolean {
251
+ try {
252
+ return !fs.statSync(file).isDirectory();
253
+ } catch {
254
+ return false;
255
+ }
256
+ }
package/src/index.ts CHANGED
@@ -1,211 +1,3 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
-
5
- import type { ITtscBannerPluginConfig } from "./structures";
6
-
7
1
  export * from "./structures/index";
8
2
 
9
- /**
10
- * The shape returned by a ttsc plugin factory function.
11
- *
12
- * Mirrors the ttsc plugin descriptor contract. The `source` field points to the
13
- * Go source directory that the host will compile and cache as either an
14
- * executable sidecar or linked native source.
15
- */
16
- type TtscPluginDescriptor = {
17
- /** Universal config-discovery inputs consumed by the native transform. */
18
- hostInputs?: string[];
19
- /** Evaluation-time fingerprints paired with {@link hostInputs}. */
20
- hostInputHashes?: Record<string, string | null>;
21
- /** Evaluation-time physical targets paired with {@link hostInputs}. */
22
- hostInputRealpaths?: Record<string, string | null>;
23
- /** Human-readable plugin name used in logs and error messages. */
24
- name: string;
25
- /** Absolute path to the Go source directory for this plugin. */
26
- source: string;
27
- /**
28
- * Pipeline stage. `"transform"` plugins may rewrite source files; `"check"`
29
- * plugins only produce diagnostics. The framework default is `"transform"`.
30
- */
31
- stage?: "check" | "transform";
32
- };
33
-
34
- /**
35
- * Context object passed by the ttsc host to every plugin factory function.
36
- *
37
- * The factory may inspect the context to customise the descriptor — for example
38
- * selecting a different Go source directory based on `plugin` config — but most
39
- * factories ignore it.
40
- */
41
- type TtscPluginFactoryContext<TConfig> = {
42
- /** Absolute path to the selected ttsc native helper, not a plugin binary. */
43
- binary: string;
44
- /** Working directory of the ttsc invocation. */
45
- cwd: string;
46
- /**
47
- * Absolute path to the directory holding this descriptor module — the
48
- * load-mode-independent replacement for `__dirname`.
49
- */
50
- dirname: string;
51
- /**
52
- * Absolute path to this descriptor module — the load-mode-independent
53
- * replacement for `__filename`.
54
- */
55
- filename: string;
56
- /** Host-declared anchor for implicit plugin config discovery. */
57
- pluginConfigDir?: string;
58
- /** The raw plugin entry from `compilerOptions.plugins[]`. */
59
- plugin: TConfig;
60
- /** Absolute path to the project root (directory containing tsconfig). */
61
- projectRoot: string;
62
- /** Absolute path to the resolved tsconfig. */
63
- tsconfig: string;
64
- };
65
-
66
- /**
67
- * Keys that the ttsc plugin host injects into every plugin entry and are not
68
- * owned by `@ttsc/banner`. These pass through the factory without validation so
69
- * the host can freely add new framework keys in the future.
70
- */
71
- const FRAMEWORK_KEYS = new Set<string>([
72
- "enabled",
73
- "name",
74
- "stage",
75
- "transform",
76
- ]);
77
-
78
- /**
79
- * Plugin factory for `@ttsc/banner` — called by the ttsc host to obtain the
80
- * plugin descriptor.
81
- *
82
- * The only banner-specific key accepted in the tsconfig plugin entry is
83
- * `configFile`. Any other key that is not a known framework key is rejected
84
- * with a specific error so users discover the correct configuration surface
85
- * (the dedicated config file) rather than silently receiving no banner.
86
- *
87
- * @internal
88
- */
89
- export default function createTtscBanner(
90
- context: TtscPluginFactoryContext<ITtscBannerPluginConfig>,
91
- ): TtscPluginDescriptor {
92
- const entry = context.plugin as Record<string, unknown>;
93
- for (const key of Object.keys(entry)) {
94
- if (!FRAMEWORK_KEYS.has(key) && key !== "configFile") {
95
- throw new Error(
96
- `@ttsc/banner: tsconfig plugin entry contains unsupported key ${JSON.stringify(key)}. ` +
97
- `Banner options must be placed in a banner.config.{ts,cts,mts,js,cjs,mjs,json} file. ` +
98
- `The only accepted key in the tsconfig entry is "configFile" (optional path to the config file).`,
99
- );
100
- }
101
- }
102
-
103
- const configInputs = bannerConfigInputs(context);
104
- return {
105
- hostInputHashes: configInputs.hashes,
106
- hostInputRealpaths: configInputs.realpaths,
107
- hostInputs: configInputs.inputs,
108
- name: "@ttsc/banner",
109
- // Point at the `driver/` directory one level above `lib/` in the
110
- // installed package tree (where the Go sources live). `context.dirname`
111
- // is this descriptor's own directory in every load mode, unlike the
112
- // CommonJS `__dirname`, which is undefined under a `.ts`-source or ESM load.
113
- source: path.resolve(context.dirname, "..", "driver"),
114
- stage: "transform",
115
- };
116
- }
117
-
118
- const BANNER_CONFIG_FILENAMES = [
119
- "banner.config.json",
120
- "banner.config.js",
121
- "banner.config.cjs",
122
- "banner.config.mjs",
123
- "banner.config.ts",
124
- "banner.config.cts",
125
- "banner.config.mts",
126
- ];
127
-
128
- /** Mirror native config resolution while retaining missing priority probes. */
129
- function bannerConfigInputs(
130
- context: TtscPluginFactoryContext<ITtscBannerPluginConfig>,
131
- ): {
132
- hashes: Record<string, string | null>;
133
- inputs: string[];
134
- realpaths: Record<string, string | null>;
135
- } {
136
- const configFile = (context.plugin as { configFile?: unknown }).configFile;
137
- const base = path.resolve(
138
- context.pluginConfigDir ?? path.dirname(context.tsconfig),
139
- );
140
- if (typeof configFile === "string" && configFile.trim() !== "") {
141
- const file = path.isAbsolute(configFile)
142
- ? path.resolve(configFile)
143
- : path.resolve(base, configFile);
144
- return {
145
- hashes: { [file]: hostInputHash(file) },
146
- inputs: [file],
147
- realpaths: { [file]: hostInputRealpath(file) },
148
- };
149
- }
150
- return configDiscoveryInputs(base, BANNER_CONFIG_FILENAMES);
151
- }
152
-
153
- function configDiscoveryInputs(
154
- base: string,
155
- names: readonly string[],
156
- ): {
157
- hashes: Record<string, string | null>;
158
- inputs: string[];
159
- realpaths: Record<string, string | null>;
160
- } {
161
- const inputs: string[] = [];
162
- const hashes: Record<string, string | null> = {};
163
- const realpaths: Record<string, string | null> = {};
164
- for (let directory = base; ; directory = path.dirname(directory)) {
165
- const candidates = names.map((name) => path.join(directory, name));
166
- inputs.push(...candidates);
167
- for (const candidate of candidates) {
168
- hashes[candidate] = hostInputHash(candidate);
169
- realpaths[candidate] = hostInputRealpath(candidate);
170
- }
171
- if (candidates.some(configCandidateExists)) break;
172
- const parent = path.dirname(directory);
173
- if (parent === directory) break;
174
- }
175
- return { hashes, inputs, realpaths };
176
- }
177
-
178
- function hostInputRealpath(file: string): string | null {
179
- try {
180
- return fs.realpathSync.native(file);
181
- } catch {
182
- return null;
183
- }
184
- }
185
-
186
- /** Hash the exact candidate state observed before discovery selects a file. */
187
- function hostInputHash(file: string): string | null {
188
- try {
189
- if (fs.statSync(file).isDirectory()) {
190
- return crypto
191
- .createHash("sha256")
192
- .update("ttsc:host-input:directory\0")
193
- .digest("hex");
194
- }
195
- return crypto
196
- .createHash("sha256")
197
- .update(fs.readFileSync(file))
198
- .digest("hex");
199
- } catch {
200
- return null;
201
- }
202
- }
203
-
204
- /** Match the native discovery rule: a directory is never a config file. */
205
- function configCandidateExists(file: string): boolean {
206
- try {
207
- return !fs.statSync(file).isDirectory();
208
- } catch {
209
- return false;
210
- }
211
- }
3
+ export { default } from "./createTtscBanner";
@@ -1,5 +1,43 @@
1
- /** Standalone `banner.config.*` file shape consumed by `@ttsc/banner`. */
1
+ /**
2
+ * Object exported by a standalone `banner.config.*` file.
3
+ *
4
+ * The native loader requires a nonblank text string. Keeping banner contents in
5
+ * this object gives JSON, JavaScript and TypeScript configuration the same
6
+ * value contract; the tsconfig plugin entry selects the file instead.
7
+ *
8
+ * @evidence contracts/common.md#principled-implementation
9
+ * A required string member expresses the loader's object payload. TypeScript
10
+ * cannot express nonblank contents through this string type, so the native
11
+ * loader validates that condition before formatting. The formatter preserves
12
+ * content lines while normalizing CRLF and escaping closing delimiters; it
13
+ * changes comment representation without treating text as executable source.
14
+ *
15
+ * @evidence contracts/common.md#clear-and-simple-design
16
+ * Banner content has one field in a dedicated config value. Registration and
17
+ * config discovery stay in ITtscBannerPluginConfig because they select the
18
+ * content's source rather than change its representation.
19
+ *
20
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts
21
+ * The text is consumer content, not a fixture-selected banner or a hardcoded
22
+ * expected output. Required separators and the annotation are formatting
23
+ * constants in the driver rather than hidden options in this value.
24
+ *
25
+ * @evidence contracts/common.md#meaningful-documentation
26
+ * Interface and field JSDoc explain standalone config ownership, required
27
+ * nonblank text, CRLF and trailing-line handling, terminator escaping and
28
+ * removeComments behavior. Purpose, validation and formatting rationale are
29
+ * separated into native paragraphs under the documentation skill; the type
30
+ * acknowledgment covers its field without duplicating a checklist on that
31
+ * property.
32
+ */
2
33
  export interface ITtscBannerConfig {
3
- /** Text inserted into the generated `@packageDocumentation` block. */
34
+ /**
35
+ * Text placed before the generated `@packageDocumentation` annotation.
36
+ *
37
+ * Empty or whitespace-only values are rejected. The formatter preserves the
38
+ * supplied lines, removes trailing blank lines, normalizes CRLF and escapes
39
+ * comment terminators so the value cannot close the generated JSDoc block.
40
+ * The resulting comment follows the compiler's `removeComments` policy.
41
+ */
4
42
  text: string;
5
43
  }
@@ -1,16 +1,66 @@
1
- /** `compilerOptions.plugins[]` entry shape consumed by `@ttsc/banner`. */
1
+ /**
2
+ * Host registration and config-file selection for `@ttsc/banner`.
3
+ *
4
+ * This entry belongs in `compilerOptions.plugins[]`. Banner text belongs in the
5
+ * separate `ITtscBannerConfig` value so discovery and config evaluation have
6
+ * one supported home instead of an additional inline option surface.
7
+ *
8
+ * @evidence contracts/common.md#principled-implementation
9
+ * Optional enabled distinguishes omission from an explicit false host switch;
10
+ * transform carries the module specifier and configFile carries a separate
11
+ * filesystem path. Those values belong to different resolution contracts, so
12
+ * a binary path or inline text is not represented as banner configuration.
13
+ * String typing does not establish path existence or nonblankness; the loader
14
+ * validates an explicitly supplied configFile before evaluation.
15
+ *
16
+ * @evidence contracts/common.md#clear-and-simple-design
17
+ * The type separates two host registration fields from one banner-owned file
18
+ * selector. Text belongs to the loaded ITtscBannerConfig object, leaving one
19
+ * evaluation path instead of parallel inline and file configuration policies.
20
+ *
21
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts
22
+ * Host switches and configFile represent the supported registration
23
+ * contract. Banner text has one typed config-file home, preventing a second
24
+ * inline option shape from bypassing that loader's validation.
25
+ *
26
+ * @evidence contracts/common.md#meaningful-documentation
27
+ * Interface and member JSDoc distinguish host registration from banner
28
+ * text, disabled and omitted enabled states, module specifiers from config
29
+ * or binary paths, and configFile anchoring, discovery, validation and
30
+ * failure behavior. Separate paragraphs explain why generated wrapper
31
+ * directories cannot become the discovery base, applying the documentation
32
+ * skill's clear prose and rationale guidance to the complete type contract.
33
+ */
2
34
  export interface ITtscBannerPluginConfig {
3
- /** Set to `false` to keep the entry while disabling this plugin. */
35
+ /**
36
+ * Set to `false` to retain the entry without running the plugin.
37
+ *
38
+ * Omission leaves the entry enabled. The ttsc host owns this switch; it is
39
+ * not a banner option passed through the standalone config file.
40
+ */
4
41
  enabled?: boolean;
5
42
 
6
- /** Plugin module specifier. */
43
+ /**
44
+ * Module specifier the host resolves to the plugin factory.
45
+ *
46
+ * Use `@ttsc/banner` to register this package through the transform key. This
47
+ * names the factory module, not the banner config file or Go binary.
48
+ */
7
49
  transform?: string;
8
50
 
9
51
  /**
10
- * Path to a `banner.config.*` file, resolved from the directory that contains
11
- * the tsconfig. When omitted, `@ttsc/banner` walks up from that directory and
12
- * uses the first `banner.config.{ts,cts,mts,js,cjs,mjs,json}` file it finds.
13
- * Any other key in this entry is rejected with an error.
52
+ * Explicit banner config path, absolute or relative to the host's plugin
53
+ * config directory. Without that explicit anchor, resolution uses the
54
+ * tsconfig directory, or the invocation directory when no tsconfig is set.
55
+ *
56
+ * Omission searches upward from the same anchor for `banner.config.*`.
57
+ * Missing or ambiguous discovery is an error. An explicit value must be a
58
+ * nonblank string naming a supported config file. Banner-specific inline
59
+ * options such as text are rejected; host-owned registration keys remain
60
+ * permitted.
61
+ *
62
+ * The host anchor matters when the tsconfig is a generated wrapper: its
63
+ * temporary directory must not become the user's config-resolution base.
14
64
  */
15
65
  configFile?: string;
16
66
  }