@o3co/ts.hocon 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 +55 -6
- package/dist/config-BwjvKeZ5.d.cts +271 -0
- package/dist/config-BwjvKeZ5.d.ts +271 -0
- package/dist/index.cjs +1105 -360
- package/dist/index.d.cts +45 -2
- package/dist/index.d.ts +45 -2
- package/dist/index.js +1094 -359
- package/dist/zod.cjs +823 -198
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +822 -198
- package/package.json +3 -1
- package/dist/parse-nNbyqnOh.d.cts +0 -53
- package/dist/parse-nNbyqnOh.d.ts +0 -53
package/dist/zod.js
CHANGED
|
@@ -195,6 +195,10 @@ function parseBytes(value, outputUnit = "B") {
|
|
|
195
195
|
return outputUnit === "B" ? Math.round(result) : result;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// src/parse.ts
|
|
199
|
+
import * as fs from "fs";
|
|
200
|
+
import * as path from "path";
|
|
201
|
+
|
|
198
202
|
// src/errors.ts
|
|
199
203
|
var ParseError = class extends Error {
|
|
200
204
|
constructor(message, line, col, file) {
|
|
@@ -215,6 +219,14 @@ var ResolveError = class extends Error {
|
|
|
215
219
|
this.name = "ResolveError";
|
|
216
220
|
}
|
|
217
221
|
};
|
|
222
|
+
var PackageLookupError = class extends ResolveError {
|
|
223
|
+
constructor(message, identifier, packageFile, line, col) {
|
|
224
|
+
super(message, `${identifier}/${packageFile}`, line, col);
|
|
225
|
+
this.identifier = identifier;
|
|
226
|
+
this.packageFile = packageFile;
|
|
227
|
+
this.name = "PackageLookupError";
|
|
228
|
+
}
|
|
229
|
+
};
|
|
218
230
|
var ConfigError = class extends Error {
|
|
219
231
|
constructor(message, path2) {
|
|
220
232
|
super(message);
|
|
@@ -222,6 +234,156 @@ var ConfigError = class extends Error {
|
|
|
222
234
|
this.name = "ConfigError";
|
|
223
235
|
}
|
|
224
236
|
};
|
|
237
|
+
var NotResolvedError = class extends ConfigError {
|
|
238
|
+
constructor(path2) {
|
|
239
|
+
super(`value at path "${path2}" is not resolved (call resolve() first)`, path2);
|
|
240
|
+
this.name = "NotResolvedError";
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/internal/resolver/types.ts
|
|
245
|
+
var separatorValues = /* @__PURE__ */ new WeakSet();
|
|
246
|
+
function isSubst(v) {
|
|
247
|
+
return v._kind === "subst-placeholder";
|
|
248
|
+
}
|
|
249
|
+
function isConcat(v) {
|
|
250
|
+
return v._kind === "concat-placeholder";
|
|
251
|
+
}
|
|
252
|
+
function isAppend(v) {
|
|
253
|
+
return v._kind === "append-placeholder";
|
|
254
|
+
}
|
|
255
|
+
function isResObj(v) {
|
|
256
|
+
return v._kind === "res-obj";
|
|
257
|
+
}
|
|
258
|
+
function makeResObj() {
|
|
259
|
+
return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
|
|
260
|
+
}
|
|
261
|
+
function mergeUnresolved(receiver, fallback) {
|
|
262
|
+
const result = makeResObj();
|
|
263
|
+
for (const [k, v] of fallback.fields) {
|
|
264
|
+
result.fields.set(k, v);
|
|
265
|
+
}
|
|
266
|
+
for (const [k, v] of fallback.priorValues) {
|
|
267
|
+
result.priorValues.set(k, v);
|
|
268
|
+
}
|
|
269
|
+
for (const [k, rv] of receiver.fields) {
|
|
270
|
+
const existing = result.fields.get(k);
|
|
271
|
+
if (existing !== void 0) {
|
|
272
|
+
const recPrior = receiver.priorValues.get(k);
|
|
273
|
+
if (recPrior !== void 0 && !isResObj(recPrior)) {
|
|
274
|
+
result.fields.set(k, rv);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (isResObj(rv) && isResObj(existing)) {
|
|
278
|
+
result.fields.set(k, mergeUnresolved(rv, existing));
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
result.priorValues.set(k, existing);
|
|
282
|
+
}
|
|
283
|
+
result.fields.set(k, rv);
|
|
284
|
+
}
|
|
285
|
+
for (const [k, v] of receiver.priorValues) {
|
|
286
|
+
if (receiver.fields.has(k)) {
|
|
287
|
+
result.priorValues.set(k, v);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/internal/resolver/utils.ts
|
|
294
|
+
function segmentsToKey(segments) {
|
|
295
|
+
return segments.map((s) => {
|
|
296
|
+
const t = s.text;
|
|
297
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
298
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
299
|
+
}
|
|
300
|
+
return t;
|
|
301
|
+
}).join(".");
|
|
302
|
+
}
|
|
303
|
+
function lookupPath(root, segments) {
|
|
304
|
+
const [head, ...tail] = segments;
|
|
305
|
+
if (head === void 0) return void 0;
|
|
306
|
+
const key = head.text;
|
|
307
|
+
const val = root.fields.get(key);
|
|
308
|
+
if (val === void 0) return void 0;
|
|
309
|
+
if (tail.length === 0) return val;
|
|
310
|
+
if (isResObj(val)) return lookupPath(val, tail);
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
function lookupResObj(root, segments) {
|
|
314
|
+
let cur = root;
|
|
315
|
+
for (const seg of segments) {
|
|
316
|
+
const val = cur.fields.get(seg.text);
|
|
317
|
+
if (val === void 0 || !isResObj(val)) return void 0;
|
|
318
|
+
cur = val;
|
|
319
|
+
}
|
|
320
|
+
return cur;
|
|
321
|
+
}
|
|
322
|
+
function deepMergeHoconValues(base, overlay) {
|
|
323
|
+
const merged = new Map(base.fields);
|
|
324
|
+
for (const [k, v] of overlay.fields) {
|
|
325
|
+
const existing = merged.get(k);
|
|
326
|
+
if (existing?.kind === "object" && v.kind === "object") {
|
|
327
|
+
merged.set(k, deepMergeHoconValues(existing, v));
|
|
328
|
+
} else {
|
|
329
|
+
merged.set(k, v);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return { kind: "object", fields: merged };
|
|
333
|
+
}
|
|
334
|
+
function deepMergeResObjInto(dst, src) {
|
|
335
|
+
for (const [k, srcVal] of src.fields) {
|
|
336
|
+
const dstVal = dst.fields.get(k);
|
|
337
|
+
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
338
|
+
deepMergeResObjInto(dstVal, srcVal);
|
|
339
|
+
} else {
|
|
340
|
+
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
341
|
+
dst.fields.set(k, srcVal);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const [k, srcPrior] of src.priorValues) {
|
|
345
|
+
if (!dst.priorValues.has(k)) {
|
|
346
|
+
dst.priorValues.set(k, srcPrior);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function hoconValueToResObj(hv) {
|
|
351
|
+
const obj = makeResObj();
|
|
352
|
+
if (hv.kind !== "object") return obj;
|
|
353
|
+
for (const [key, val] of hv.fields) {
|
|
354
|
+
if (val.kind === "object") {
|
|
355
|
+
obj.fields.set(key, hoconValueToResObj(val));
|
|
356
|
+
} else {
|
|
357
|
+
obj.fields.set(key, val);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return obj;
|
|
361
|
+
}
|
|
362
|
+
function isFileNotFoundError(e) {
|
|
363
|
+
if (!(e instanceof Error)) return false;
|
|
364
|
+
const code = e.code;
|
|
365
|
+
if (code === "ENOENT" || code === "MODULE_NOT_FOUND") return true;
|
|
366
|
+
const msg = e.message.toLowerCase();
|
|
367
|
+
return msg.includes("not found") || msg.includes("no such file") || msg.includes("enoent");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/internal/resolver/include-loader.ts
|
|
371
|
+
import { createRequire } from "module";
|
|
372
|
+
import * as nodePath from "path";
|
|
373
|
+
|
|
374
|
+
// src/internal/parser/empty-check.ts
|
|
375
|
+
function hasContentTokens(tokens) {
|
|
376
|
+
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
377
|
+
}
|
|
378
|
+
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
379
|
+
if (!hasContentTokens(tokens)) {
|
|
380
|
+
throw new ParseError(
|
|
381
|
+
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
382
|
+
1,
|
|
383
|
+
1
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
225
387
|
|
|
226
388
|
// src/internal/lexer/lexer.ts
|
|
227
389
|
function isHoconWhitespace(ch) {
|
|
@@ -786,58 +948,137 @@ var Parser = class {
|
|
|
786
948
|
const p = this.currentPos();
|
|
787
949
|
this.skip("newline");
|
|
788
950
|
const t = this.peek();
|
|
789
|
-
let path2;
|
|
790
|
-
let required = false;
|
|
791
|
-
let isFile = false;
|
|
792
951
|
if (t.kind === "unquoted" && (t.value === "required(" || t.value === "required" || t.value.startsWith("required("))) {
|
|
793
952
|
if (t.value === "required") {
|
|
794
|
-
const
|
|
795
|
-
if (
|
|
953
|
+
const next = this.peek(1);
|
|
954
|
+
if (next.kind !== "unquoted" || !next.value.startsWith("(")) {
|
|
796
955
|
throw new ParseError("include required must be followed by (", t.line, t.col);
|
|
797
956
|
}
|
|
798
957
|
}
|
|
799
|
-
required = true;
|
|
800
958
|
this.advance();
|
|
801
959
|
const innerPrefix = t.value.startsWith("required(") ? t.value.slice("required(".length) : "";
|
|
802
|
-
if (innerPrefix.startsWith("url") || innerPrefix.startsWith("classpath")) {
|
|
960
|
+
if (innerPrefix === "url" || innerPrefix.startsWith("url(") || innerPrefix === "classpath" || innerPrefix.startsWith("classpath(")) {
|
|
803
961
|
throw new ParseError("include url(...) and classpath(...) are not supported", t.line, t.col);
|
|
804
962
|
}
|
|
805
|
-
|
|
806
|
-
|
|
963
|
+
const nextTok = this.peek();
|
|
964
|
+
if (innerPrefix === "package" || innerPrefix.startsWith("package(") || innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "package(" || nextTok.value.startsWith("package("))) {
|
|
965
|
+
const consumePackageToken = innerPrefix === "";
|
|
966
|
+
const { qualifier, path: path3 } = this.parsePackageArgs(consumePackageToken);
|
|
967
|
+
return this.makeIncludeField(qualifier, path3, true, p);
|
|
807
968
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
969
|
+
if (innerPrefix === "file" || innerPrefix.startsWith("file(") || innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "file(" || nextTok.value === "file")) {
|
|
970
|
+
if (innerPrefix === "") {
|
|
971
|
+
this.advance();
|
|
972
|
+
}
|
|
973
|
+
const path3 = this.parseQuotedPathSkipWrapper(t);
|
|
974
|
+
return this.makeIncludeField({ kind: "file" }, path3, true, p);
|
|
975
|
+
}
|
|
976
|
+
if (innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "url" || nextTok.value.startsWith("url(") || nextTok.value === "classpath" || nextTok.value.startsWith("classpath("))) {
|
|
977
|
+
throw new ParseError("include url(...) and classpath(...) are not supported", nextTok.line, nextTok.col);
|
|
978
|
+
}
|
|
979
|
+
if (innerPrefix !== "") {
|
|
980
|
+
if (innerPrefix === ")") {
|
|
981
|
+
throw new ParseError("include required(...) must contain a path", t.line, t.col);
|
|
982
|
+
}
|
|
983
|
+
const name = innerPrefix.replace(/\(.*$/, "").replace(/\).*$/, "");
|
|
984
|
+
throw new ParseError(`unknown include qualifier inside required(): "${name}"`, t.line, t.col);
|
|
985
|
+
}
|
|
986
|
+
const path2 = this.parseQuotedPathSkipWrapper(t);
|
|
987
|
+
return this.makeIncludeField({ kind: "bare" }, path2, true, p);
|
|
988
|
+
}
|
|
989
|
+
if (t.kind === "string") {
|
|
990
|
+
const path2 = this.advance().value;
|
|
991
|
+
return this.makeIncludeField({ kind: "bare" }, path2, false, p);
|
|
992
|
+
}
|
|
993
|
+
if (t.kind === "unquoted" && (t.value === "file(" || t.value === "file")) {
|
|
823
994
|
this.advance();
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
995
|
+
const path2 = this.parseQuotedPathSkipWrapper(t);
|
|
996
|
+
return this.makeIncludeField({ kind: "file" }, path2, false, p);
|
|
997
|
+
}
|
|
998
|
+
if (t.kind === "unquoted" && (t.value === "package(" || t.value.startsWith("package("))) {
|
|
999
|
+
const { qualifier, path: path2 } = this.parsePackageArgs();
|
|
1000
|
+
return this.makeIncludeField(qualifier, path2, false, p);
|
|
1001
|
+
}
|
|
1002
|
+
if (t.kind === "unquoted" && (t.value === "url" || t.value.startsWith("url("))) {
|
|
829
1003
|
throw new ParseError("include url(...) is not supported", t.line, t.col);
|
|
830
|
-
}
|
|
1004
|
+
}
|
|
1005
|
+
if (t.kind === "unquoted" && (t.value === "classpath" || t.value.startsWith("classpath("))) {
|
|
831
1006
|
throw new ParseError("include classpath(...) is not supported", t.line, t.col);
|
|
832
|
-
} else {
|
|
833
|
-
throw new ParseError(`expected include path, got ${t.kind}`, t.line, t.col);
|
|
834
1007
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1008
|
+
throw new ParseError(`expected include path, got ${t.kind}`, t.line, t.col);
|
|
1009
|
+
}
|
|
1010
|
+
makeIncludeField(qualifier, path2, required, pos) {
|
|
1011
|
+
return { key: [], value: { kind: "include", qualifier, path: path2, required, pos }, append: false, pos };
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Skip past include-syntax wrapper tokens to find the quoted path string.
|
|
1015
|
+
* Strict: only allows `(`, `)`, and bare qualifier keywords (file / file( /
|
|
1016
|
+
* package / package( / url / url( / classpath / classpath() between the
|
|
1017
|
+
* directive and the quoted path. Anything else is a parse error rather than
|
|
1018
|
+
* silently swallowed (see ts#113).
|
|
1019
|
+
*/
|
|
1020
|
+
parseQuotedPathSkipWrapper(errTok) {
|
|
1021
|
+
while (this.isIncludeWrapperToken(this.peek())) this.advance();
|
|
1022
|
+
if (this.peek().kind !== "string") {
|
|
1023
|
+
const tok = this.peek();
|
|
1024
|
+
if (tok.kind === "eof") throw new ParseError("expected include path", errTok.line, errTok.col);
|
|
1025
|
+
const desc = tok.kind === "unquoted" ? `unquoted "${tok.value}"` : tok.kind;
|
|
1026
|
+
throw new ParseError(`expected quoted include path, got ${desc}`, tok.line, tok.col);
|
|
1027
|
+
}
|
|
1028
|
+
const path2 = this.advance().value;
|
|
1029
|
+
while (this.peek().kind === "unquoted" && (this.peek().value === ")" || this.peek().value === "))")) {
|
|
1030
|
+
this.advance();
|
|
1031
|
+
}
|
|
1032
|
+
const after = this.peek();
|
|
1033
|
+
if (after.kind !== "newline" && after.kind !== "rbrace" && after.kind !== "eof" && after.kind !== "comma") {
|
|
1034
|
+
const desc = after.kind === "unquoted" ? `unquoted "${after.value}"` : after.kind;
|
|
1035
|
+
throw new ParseError(`unexpected token after include path: ${desc}`, after.line, after.col);
|
|
1036
|
+
}
|
|
1037
|
+
return path2;
|
|
1038
|
+
}
|
|
1039
|
+
isIncludeWrapperToken(tok) {
|
|
1040
|
+
return tok.kind === "unquoted" && tok.value === "(";
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Parse the body of `package("identifier", "file")`.
|
|
1044
|
+
*
|
|
1045
|
+
* @param consumePackageToken - When true (default), the current token is `package(` and
|
|
1046
|
+
* must be consumed first. When false, the lexer already bundled `package(` into the
|
|
1047
|
+
* preceding token (e.g. `required(package("`), so we are already positioned at the
|
|
1048
|
+
* first string argument.
|
|
1049
|
+
*/
|
|
1050
|
+
parsePackageArgs(consumePackageToken = true) {
|
|
1051
|
+
const pkgTok = consumePackageToken ? this.advance() : this.peek();
|
|
1052
|
+
this.skip("newline");
|
|
1053
|
+
const idTok = this.peek();
|
|
1054
|
+
if (idTok.kind !== "string") {
|
|
1055
|
+
throw new ParseError("include package: expected quoted identifier as first argument", pkgTok.line, pkgTok.col);
|
|
1056
|
+
}
|
|
1057
|
+
const identifier = this.advance().value;
|
|
1058
|
+
this.skip("newline");
|
|
1059
|
+
if (this.peek().kind !== "comma") {
|
|
1060
|
+
const after = this.peek();
|
|
1061
|
+
throw new ParseError(
|
|
1062
|
+
after.kind === "string" ? "include package: missing comma between identifier and file arguments" : "include package: requires exactly two arguments (identifier, file) \u2014 one-arg form is not supported",
|
|
1063
|
+
pkgTok.line,
|
|
1064
|
+
pkgTok.col
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
this.advance();
|
|
1068
|
+
this.skip("newline");
|
|
1069
|
+
const fileTok = this.peek();
|
|
1070
|
+
if (fileTok.kind !== "string") {
|
|
1071
|
+
throw new ParseError(
|
|
1072
|
+
"include package: requires exactly two arguments (identifier, file) \u2014 one-arg form is not supported",
|
|
1073
|
+
pkgTok.line,
|
|
1074
|
+
pkgTok.col
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
const filePath = this.advance().value;
|
|
1078
|
+
while (this.peek().kind !== "newline" && this.peek().kind !== "rbrace" && this.peek().kind !== "eof" && this.peek().kind !== "comma") {
|
|
1079
|
+
this.advance();
|
|
1080
|
+
}
|
|
1081
|
+
return { qualifier: { kind: "package", identifier }, path: filePath };
|
|
841
1082
|
}
|
|
842
1083
|
parseValue() {
|
|
843
1084
|
const p = this.currentPos();
|
|
@@ -878,7 +1119,9 @@ var Parser = class {
|
|
|
878
1119
|
parts.push(node);
|
|
879
1120
|
}
|
|
880
1121
|
if (parts.length === 0) throw new ParseError("expected value", this.peek().line, this.peek().col);
|
|
881
|
-
if (parts.length === 1)
|
|
1122
|
+
if (parts.length === 1) {
|
|
1123
|
+
return parts[0];
|
|
1124
|
+
}
|
|
882
1125
|
return { kind: "concat", nodes: parts, pos: p };
|
|
883
1126
|
}
|
|
884
1127
|
parseArray() {
|
|
@@ -910,118 +1153,6 @@ function parseTokens(tokens) {
|
|
|
910
1153
|
return new Parser(tokens).parse();
|
|
911
1154
|
}
|
|
912
1155
|
|
|
913
|
-
// src/internal/parser/empty-check.ts
|
|
914
|
-
function hasContentTokens(tokens) {
|
|
915
|
-
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
916
|
-
}
|
|
917
|
-
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
918
|
-
if (!hasContentTokens(tokens)) {
|
|
919
|
-
throw new ParseError(
|
|
920
|
-
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
921
|
-
1,
|
|
922
|
-
1
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// src/internal/resolver/types.ts
|
|
928
|
-
var separatorValues = /* @__PURE__ */ new WeakSet();
|
|
929
|
-
function isSubst(v) {
|
|
930
|
-
return v._kind === "subst-placeholder";
|
|
931
|
-
}
|
|
932
|
-
function isConcat(v) {
|
|
933
|
-
return v._kind === "concat-placeholder";
|
|
934
|
-
}
|
|
935
|
-
function isAppend(v) {
|
|
936
|
-
return v._kind === "append-placeholder";
|
|
937
|
-
}
|
|
938
|
-
function isResObj(v) {
|
|
939
|
-
return v._kind === "res-obj";
|
|
940
|
-
}
|
|
941
|
-
function makeResObj() {
|
|
942
|
-
return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// src/internal/resolver/utils.ts
|
|
946
|
-
function segmentsToKey(segments) {
|
|
947
|
-
return segments.map((s) => {
|
|
948
|
-
const t = s.text;
|
|
949
|
-
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
950
|
-
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
951
|
-
}
|
|
952
|
-
return t;
|
|
953
|
-
}).join(".");
|
|
954
|
-
}
|
|
955
|
-
function lookupPath(root, segments) {
|
|
956
|
-
const [head, ...tail] = segments;
|
|
957
|
-
if (head === void 0) return void 0;
|
|
958
|
-
const key = head.text;
|
|
959
|
-
const val = root.fields.get(key);
|
|
960
|
-
if (val === void 0) return void 0;
|
|
961
|
-
if (tail.length === 0) return val;
|
|
962
|
-
if (isResObj(val)) return lookupPath(val, tail);
|
|
963
|
-
return void 0;
|
|
964
|
-
}
|
|
965
|
-
function lookupResObj(root, segments) {
|
|
966
|
-
let cur = root;
|
|
967
|
-
for (const seg of segments) {
|
|
968
|
-
const val = cur.fields.get(seg.text);
|
|
969
|
-
if (val === void 0 || !isResObj(val)) return void 0;
|
|
970
|
-
cur = val;
|
|
971
|
-
}
|
|
972
|
-
return cur;
|
|
973
|
-
}
|
|
974
|
-
function deepMergeHoconValues(base, overlay) {
|
|
975
|
-
const merged = new Map(base.fields);
|
|
976
|
-
for (const [k, v] of overlay.fields) {
|
|
977
|
-
const existing = merged.get(k);
|
|
978
|
-
if (existing?.kind === "object" && v.kind === "object") {
|
|
979
|
-
merged.set(k, deepMergeHoconValues(existing, v));
|
|
980
|
-
} else {
|
|
981
|
-
merged.set(k, v);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
return { kind: "object", fields: merged };
|
|
985
|
-
}
|
|
986
|
-
function deepMergeResObjInto(dst, src) {
|
|
987
|
-
for (const [k, srcVal] of src.fields) {
|
|
988
|
-
const dstVal = dst.fields.get(k);
|
|
989
|
-
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
990
|
-
deepMergeResObjInto(dstVal, srcVal);
|
|
991
|
-
} else {
|
|
992
|
-
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
993
|
-
dst.fields.set(k, srcVal);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
for (const [k, srcPrior] of src.priorValues) {
|
|
997
|
-
if (!dst.priorValues.has(k)) {
|
|
998
|
-
dst.priorValues.set(k, srcPrior);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
function hoconValueToResObj(hv) {
|
|
1003
|
-
const obj = makeResObj();
|
|
1004
|
-
if (hv.kind !== "object") return obj;
|
|
1005
|
-
for (const [key, val] of hv.fields) {
|
|
1006
|
-
if (val.kind === "object") {
|
|
1007
|
-
obj.fields.set(key, hoconValueToResObj(val));
|
|
1008
|
-
} else {
|
|
1009
|
-
obj.fields.set(key, val);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
return obj;
|
|
1013
|
-
}
|
|
1014
|
-
function isFileNotFoundError(e) {
|
|
1015
|
-
if (!(e instanceof Error)) return false;
|
|
1016
|
-
const code = e.code;
|
|
1017
|
-
if (code === "ENOENT" || code === "MODULE_NOT_FOUND") return true;
|
|
1018
|
-
const msg = e.message.toLowerCase();
|
|
1019
|
-
return msg.includes("not found") || msg.includes("no such file") || msg.includes("enoent");
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
// src/internal/resolver/include-loader.ts
|
|
1023
|
-
import * as nodePath from "path";
|
|
1024
|
-
|
|
1025
1156
|
// src/internal/properties/properties.ts
|
|
1026
1157
|
function parseProperties(input) {
|
|
1027
1158
|
const root = /* @__PURE__ */ Object.create(null);
|
|
@@ -1081,6 +1212,84 @@ function recordToHoconValue(obj) {
|
|
|
1081
1212
|
}
|
|
1082
1213
|
|
|
1083
1214
|
// src/internal/resolver/include-loader.ts
|
|
1215
|
+
var _require = createRequire(import.meta.url);
|
|
1216
|
+
function isPnpPath(p) {
|
|
1217
|
+
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1218
|
+
}
|
|
1219
|
+
function validatePackageFile(file, identifier) {
|
|
1220
|
+
if (file.length === 0) {
|
|
1221
|
+
throw new ParseError(`include package("${identifier}", ...): file argument must be non-empty`, 0, 0);
|
|
1222
|
+
}
|
|
1223
|
+
if (file.includes("\\")) {
|
|
1224
|
+
throw new ParseError(
|
|
1225
|
+
`include package("${identifier}", "${file}"): backslash not allowed in file argument (use forward slash)`,
|
|
1226
|
+
0,
|
|
1227
|
+
0
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
if (file.startsWith("/")) {
|
|
1231
|
+
throw new ParseError(
|
|
1232
|
+
`include package("${identifier}", "${file}"): absolute path not allowed in file argument`,
|
|
1233
|
+
0,
|
|
1234
|
+
0
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
if (/(?:^|\/)\.\.?(?:\/|$)/u.test(file)) {
|
|
1238
|
+
throw new ParseError(
|
|
1239
|
+
`include package("${identifier}", "${file}"): path traversal (. or ..) not allowed in file argument`,
|
|
1240
|
+
0,
|
|
1241
|
+
0
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
if (file.includes("//")) {
|
|
1245
|
+
throw new ParseError(
|
|
1246
|
+
`include package("${identifier}", "${file}"): consecutive slashes not allowed in file argument`,
|
|
1247
|
+
0,
|
|
1248
|
+
0
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function validatePackageIdentifier(identifier) {
|
|
1253
|
+
if (identifier.length === 0) {
|
|
1254
|
+
throw new ParseError("include package: identifier argument must be non-empty", 0, 0);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
function defaultPackageResolver(identifier, file, includingFile, baseDir, resolveFrom) {
|
|
1258
|
+
const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
|
|
1259
|
+
let resolved;
|
|
1260
|
+
try {
|
|
1261
|
+
resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
|
|
1262
|
+
} catch {
|
|
1263
|
+
throw new PackageLookupError(
|
|
1264
|
+
`include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
|
|
1265
|
+
identifier,
|
|
1266
|
+
file,
|
|
1267
|
+
0,
|
|
1268
|
+
0
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
if (isPnpPath(resolved)) {
|
|
1272
|
+
throw new ResolveError(
|
|
1273
|
+
`include package("${identifier}", "${file}"): Yarn Berry PnP store paths are not supported. Provide a custom packageResolver option to handle PnP resolution.`,
|
|
1274
|
+
`${identifier}/${file}`,
|
|
1275
|
+
0,
|
|
1276
|
+
0
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
const fileParts = file.split("/");
|
|
1280
|
+
const expectedBasename = fileParts[fileParts.length - 1] ?? file;
|
|
1281
|
+
const resolvedBasename = nodePath.basename(resolved);
|
|
1282
|
+
if (resolvedBasename !== expectedBasename) {
|
|
1283
|
+
throw new PackageLookupError(
|
|
1284
|
+
`include package("${identifier}", "${file}"): case mismatch \u2014 resolved "${resolvedBasename}" but expected "${expectedBasename}" (E11 decision 5: case-sensitive)`,
|
|
1285
|
+
identifier,
|
|
1286
|
+
file,
|
|
1287
|
+
0,
|
|
1288
|
+
0
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
return resolved;
|
|
1292
|
+
}
|
|
1084
1293
|
var IncludeLoader = class {
|
|
1085
1294
|
constructor(opts) {
|
|
1086
1295
|
this.opts = opts;
|
|
@@ -1160,8 +1369,77 @@ var IncludeLoader = class {
|
|
|
1160
1369
|
}
|
|
1161
1370
|
return merged;
|
|
1162
1371
|
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Resolve and load `include package("identifier", "file")`.
|
|
1374
|
+
* Validates the identifier and file arguments, resolves via the package resolver,
|
|
1375
|
+
* performs cycle detection using a JSON-serialised cycle key, and parses the content.
|
|
1376
|
+
*/
|
|
1377
|
+
// `required` is reserved for future optional-package semantics (E11 decision 7);
|
|
1378
|
+
// currently all package lookup failures throw regardless of this flag.
|
|
1379
|
+
loadPackage(identifier, file, _required) {
|
|
1380
|
+
validatePackageIdentifier(identifier);
|
|
1381
|
+
validatePackageFile(file, identifier);
|
|
1382
|
+
const { includeStack = [], baseDir, resolveFrom, packageResolver, readFileSync: readFileSync2 } = this.opts;
|
|
1383
|
+
const cycleKey = JSON.stringify(["package", identifier, file]);
|
|
1384
|
+
if (includeStack.includes(cycleKey)) {
|
|
1385
|
+
throw new ResolveError(
|
|
1386
|
+
`circular include: package("${identifier}", "${file}")`,
|
|
1387
|
+
`${identifier}/${file}`,
|
|
1388
|
+
0,
|
|
1389
|
+
0
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
if (includeStack.length >= 50) {
|
|
1393
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, `${identifier}/${file}`, 0, 0);
|
|
1394
|
+
}
|
|
1395
|
+
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1396
|
+
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1397
|
+
const content = readFileSync2(resolvedPath);
|
|
1398
|
+
if (content.length === 0) return makeResObj();
|
|
1399
|
+
const tokens = tokenize(content);
|
|
1400
|
+
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1401
|
+
const ast = parseTokens(tokens);
|
|
1402
|
+
return this.onBuildResObj(ast, {
|
|
1403
|
+
...this.opts,
|
|
1404
|
+
baseDir: nodePath.dirname(resolvedPath),
|
|
1405
|
+
includeStack: [...includeStack, cycleKey]
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Async variant of `loadPackage`.
|
|
1410
|
+
*/
|
|
1411
|
+
async loadPackageAsync(identifier, file, _required) {
|
|
1412
|
+
validatePackageIdentifier(identifier);
|
|
1413
|
+
validatePackageFile(file, identifier);
|
|
1414
|
+
const { includeStack = [], baseDir, resolveFrom, packageResolver, readFile, readFileSync: readFileSync2 } = this.opts;
|
|
1415
|
+
const cycleKey = JSON.stringify(["package", identifier, file]);
|
|
1416
|
+
if (includeStack.includes(cycleKey)) {
|
|
1417
|
+
throw new ResolveError(
|
|
1418
|
+
`circular include: package("${identifier}", "${file}")`,
|
|
1419
|
+
`${identifier}/${file}`,
|
|
1420
|
+
0,
|
|
1421
|
+
0
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
if (includeStack.length >= 50) {
|
|
1425
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, `${identifier}/${file}`, 0, 0);
|
|
1426
|
+
}
|
|
1427
|
+
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1428
|
+
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1429
|
+
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1430
|
+
const content = await read(resolvedPath);
|
|
1431
|
+
if (content.length === 0) return makeResObj();
|
|
1432
|
+
const tokens = tokenize(content);
|
|
1433
|
+
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1434
|
+
const ast = parseTokens(tokens);
|
|
1435
|
+
return this.onBuildResObjAsync(ast, {
|
|
1436
|
+
...this.opts,
|
|
1437
|
+
baseDir: nodePath.dirname(resolvedPath),
|
|
1438
|
+
includeStack: [...includeStack, cycleKey]
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1163
1441
|
loadSingle(candidate) {
|
|
1164
|
-
const { readFileSync: readFileSync2, includeStack = []
|
|
1442
|
+
const { readFileSync: readFileSync2, includeStack = [] } = this.opts;
|
|
1165
1443
|
if (includeStack.includes(candidate)) {
|
|
1166
1444
|
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
1167
1445
|
}
|
|
@@ -1179,14 +1457,13 @@ var IncludeLoader = class {
|
|
|
1179
1457
|
assertNonEmptyDocument(tokens, candidate);
|
|
1180
1458
|
const ast = parseTokens(tokens);
|
|
1181
1459
|
return this.onBuildResObj(ast, {
|
|
1182
|
-
|
|
1460
|
+
...this.opts,
|
|
1183
1461
|
baseDir: nodePath.dirname(candidate),
|
|
1184
|
-
readFileSync: readFileSync2,
|
|
1185
1462
|
includeStack: [...includeStack, candidate]
|
|
1186
1463
|
});
|
|
1187
1464
|
}
|
|
1188
1465
|
async loadSingleAsync(candidate) {
|
|
1189
|
-
const { readFile, readFileSync: readFileSync2, includeStack = []
|
|
1466
|
+
const { readFile, readFileSync: readFileSync2, includeStack = [] } = this.opts;
|
|
1190
1467
|
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1191
1468
|
if (includeStack.includes(candidate)) {
|
|
1192
1469
|
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
@@ -1205,10 +1482,8 @@ var IncludeLoader = class {
|
|
|
1205
1482
|
assertNonEmptyDocument(tokens, candidate);
|
|
1206
1483
|
const ast = parseTokens(tokens);
|
|
1207
1484
|
return this.onBuildResObjAsync(ast, {
|
|
1208
|
-
|
|
1485
|
+
...this.opts,
|
|
1209
1486
|
baseDir: nodePath.dirname(candidate),
|
|
1210
|
-
readFileSync: readFileSync2,
|
|
1211
|
-
readFile,
|
|
1212
1487
|
includeStack: [...includeStack, candidate]
|
|
1213
1488
|
});
|
|
1214
1489
|
}
|
|
@@ -1217,7 +1492,6 @@ var IncludeLoader = class {
|
|
|
1217
1492
|
// src/internal/resolver/structure-builder.ts
|
|
1218
1493
|
var StructureBuilder = class _StructureBuilder {
|
|
1219
1494
|
constructor(opts) {
|
|
1220
|
-
this.opts = opts;
|
|
1221
1495
|
this.loader = new IncludeLoader(opts);
|
|
1222
1496
|
this.loader.onBuildResObj = (a, o) => new _StructureBuilder(o).build(a);
|
|
1223
1497
|
this.loader.onBuildResObjAsync = async (a, o) => new _StructureBuilder(o).buildAsync(a);
|
|
@@ -1244,7 +1518,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1244
1518
|
}
|
|
1245
1519
|
applyField(obj, field, pathPrefix) {
|
|
1246
1520
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1247
|
-
const
|
|
1521
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1522
|
+
let included;
|
|
1523
|
+
if (qualifier.kind === "package") {
|
|
1524
|
+
included = this.loader.loadPackage(qualifier.identifier, path2, required);
|
|
1525
|
+
} else {
|
|
1526
|
+
included = this.loader.load(path2, required, qualifier.kind === "file");
|
|
1527
|
+
}
|
|
1248
1528
|
if (pathPrefix.length > 0) {
|
|
1249
1529
|
this.relativizeResObj(included, pathPrefix);
|
|
1250
1530
|
}
|
|
@@ -1286,7 +1566,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1286
1566
|
}
|
|
1287
1567
|
async applyFieldAsync(obj, field, pathPrefix) {
|
|
1288
1568
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1289
|
-
const
|
|
1569
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1570
|
+
let included;
|
|
1571
|
+
if (qualifier.kind === "package") {
|
|
1572
|
+
included = await this.loader.loadPackageAsync(qualifier.identifier, path2, required);
|
|
1573
|
+
} else {
|
|
1574
|
+
included = await this.loader.loadAsync(path2, required, qualifier.kind === "file");
|
|
1575
|
+
}
|
|
1290
1576
|
if (pathPrefix.length > 0) {
|
|
1291
1577
|
this.relativizeResObj(included, pathPrefix);
|
|
1292
1578
|
}
|
|
@@ -1454,6 +1740,10 @@ var SubstitutionResolver = class {
|
|
|
1454
1740
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1455
1741
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
1456
1742
|
}
|
|
1743
|
+
/** Returns a prefix for error messages, including origin info when set. */
|
|
1744
|
+
originPrefix() {
|
|
1745
|
+
return this.opts.originDescription ? `${this.opts.originDescription}: ` : "";
|
|
1746
|
+
}
|
|
1457
1747
|
resolve() {
|
|
1458
1748
|
return this.resolveResObj(this.root);
|
|
1459
1749
|
}
|
|
@@ -1494,7 +1784,8 @@ var SubstitutionResolver = class {
|
|
|
1494
1784
|
if (isConcat(v)) {
|
|
1495
1785
|
this.resolvingConcats.add(v);
|
|
1496
1786
|
try {
|
|
1497
|
-
|
|
1787
|
+
const concatResult = this.resolveConcat(v.nodes, scope, v.line, v.col);
|
|
1788
|
+
return concatResult;
|
|
1498
1789
|
} finally {
|
|
1499
1790
|
this.resolvingConcats.delete(v);
|
|
1500
1791
|
}
|
|
@@ -1539,7 +1830,7 @@ var SubstitutionResolver = class {
|
|
|
1539
1830
|
}
|
|
1540
1831
|
if (s.optional) return void 0;
|
|
1541
1832
|
throw new ResolveError(
|
|
1542
|
-
`circular substitution: ${key}`,
|
|
1833
|
+
this.originPrefix() + `circular substitution: ${key}`,
|
|
1543
1834
|
key,
|
|
1544
1835
|
s.line,
|
|
1545
1836
|
s.col
|
|
@@ -1569,7 +1860,7 @@ var SubstitutionResolver = class {
|
|
|
1569
1860
|
}
|
|
1570
1861
|
if (s.optional) return void 0;
|
|
1571
1862
|
throw new ResolveError(
|
|
1572
|
-
|
|
1863
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1573
1864
|
key,
|
|
1574
1865
|
s.line,
|
|
1575
1866
|
s.col
|
|
@@ -1602,7 +1893,7 @@ var SubstitutionResolver = class {
|
|
|
1602
1893
|
if (result !== void 0) this.cache.set(key, result);
|
|
1603
1894
|
return result;
|
|
1604
1895
|
}
|
|
1605
|
-
if (s.listSuffix) {
|
|
1896
|
+
if (s.listSuffix && this.opts.useSystemEnvironment !== false) {
|
|
1606
1897
|
const result = this.resolveEnvList(s);
|
|
1607
1898
|
if (result !== void 0) {
|
|
1608
1899
|
this.cache.set(key, result);
|
|
@@ -1611,22 +1902,36 @@ var SubstitutionResolver = class {
|
|
|
1611
1902
|
if (s.optional) return void 0;
|
|
1612
1903
|
const envBase = s.segments.map((seg) => seg.text).join(".");
|
|
1613
1904
|
throw new ResolveError(
|
|
1614
|
-
|
|
1905
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}} (no environment variables ${envBase}_0, ${envBase}_1, \u2026 set)`,
|
|
1906
|
+
key,
|
|
1907
|
+
s.line,
|
|
1908
|
+
s.col
|
|
1909
|
+
);
|
|
1910
|
+
} else if (s.listSuffix) {
|
|
1911
|
+
if (s.optional) return void 0;
|
|
1912
|
+
if (this.opts.allowUnresolved) return s;
|
|
1913
|
+
throw new ResolveError(
|
|
1914
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1615
1915
|
key,
|
|
1616
1916
|
s.line,
|
|
1617
1917
|
s.col
|
|
1618
1918
|
);
|
|
1619
1919
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1920
|
+
if (this.opts.useSystemEnvironment !== false) {
|
|
1921
|
+
const envKey = s.segments.map((seg) => seg.text).join(".");
|
|
1922
|
+
const envVal = this.opts.env[envKey] ?? (s.prefixLen > 0 ? this.opts.env[s.segments.slice(s.prefixLen).map((seg) => seg.text).join(".")] : void 0);
|
|
1923
|
+
if (envVal !== void 0) {
|
|
1924
|
+
const result = { kind: "scalar", raw: envVal, valueType: "string" };
|
|
1925
|
+
this.cache.set(key, result);
|
|
1926
|
+
return result;
|
|
1927
|
+
}
|
|
1626
1928
|
}
|
|
1627
1929
|
if (s.optional) return void 0;
|
|
1930
|
+
if (this.opts.allowUnresolved) {
|
|
1931
|
+
return s;
|
|
1932
|
+
}
|
|
1628
1933
|
throw new ResolveError(
|
|
1629
|
-
|
|
1934
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1630
1935
|
key,
|
|
1631
1936
|
s.line,
|
|
1632
1937
|
s.col
|
|
@@ -1671,8 +1976,16 @@ var SubstitutionResolver = class {
|
|
|
1671
1976
|
return [full, bare];
|
|
1672
1977
|
}
|
|
1673
1978
|
resolveConcat(nodes, scope, line = 0, col = 0) {
|
|
1674
|
-
const
|
|
1675
|
-
if (
|
|
1979
|
+
const rawResolved = nodes.map((n) => this.resolveVal(n, scope));
|
|
1980
|
+
if (this.opts.allowUnresolved) {
|
|
1981
|
+
for (const v of rawResolved) {
|
|
1982
|
+
if (v !== void 0 && isSubst(v)) {
|
|
1983
|
+
return v;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
const resolved = rawResolved.filter((v) => v !== void 0);
|
|
1988
|
+
if (resolved.length === 0) return void 0;
|
|
1676
1989
|
if (resolved.length === 1) return resolved[0];
|
|
1677
1990
|
const nonSep = resolved.filter((v) => !separatorValues.has(v));
|
|
1678
1991
|
if (nonSep.length === 0) {
|
|
@@ -1746,15 +2059,89 @@ var SubstitutionResolver = class {
|
|
|
1746
2059
|
};
|
|
1747
2060
|
|
|
1748
2061
|
// src/internal/resolver/resolver.ts
|
|
2062
|
+
function buildTree(ast, opts) {
|
|
2063
|
+
return new StructureBuilder(opts).build(ast);
|
|
2064
|
+
}
|
|
2065
|
+
function resolveTree(tree, opts) {
|
|
2066
|
+
return new SubstitutionResolver(tree, opts).resolve();
|
|
2067
|
+
}
|
|
2068
|
+
function containsPlaceholders(tree) {
|
|
2069
|
+
for (const val of tree.fields.values()) {
|
|
2070
|
+
if (valContainsPlaceholders(val)) return true;
|
|
2071
|
+
}
|
|
2072
|
+
return false;
|
|
2073
|
+
}
|
|
2074
|
+
function valContainsPlaceholders(v) {
|
|
2075
|
+
const rv = v;
|
|
2076
|
+
if (isSubst(rv) || isConcat(rv) || isAppend(rv)) return true;
|
|
2077
|
+
if (isResObj(rv)) return containsPlaceholders(rv);
|
|
2078
|
+
const hv = v;
|
|
2079
|
+
if (hv?.kind === "array") {
|
|
2080
|
+
return hv.items.some((item) => valContainsPlaceholders(item));
|
|
2081
|
+
}
|
|
2082
|
+
return false;
|
|
2083
|
+
}
|
|
2084
|
+
function buildPartialHoconFromResObj(tree) {
|
|
2085
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2086
|
+
for (const [k, v] of tree.fields) {
|
|
2087
|
+
if (!isSubst(v) && !isConcat(v) && !isAppend(v)) {
|
|
2088
|
+
if (isResObj(v)) {
|
|
2089
|
+
fields.set(k, buildPartialHoconFromResObj(v));
|
|
2090
|
+
} else {
|
|
2091
|
+
if (!valContainsPlaceholders(v)) {
|
|
2092
|
+
fields.set(k, v);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return { kind: "object", fields };
|
|
2098
|
+
}
|
|
2099
|
+
function hoconValueToResObj2(value) {
|
|
2100
|
+
const obj = makeResObj();
|
|
2101
|
+
for (const [k, v] of value.fields) {
|
|
2102
|
+
if (v.kind === "object") {
|
|
2103
|
+
obj.fields.set(k, hoconValueToResObj2(v));
|
|
2104
|
+
} else {
|
|
2105
|
+
obj.fields.set(k, v);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
return obj;
|
|
2109
|
+
}
|
|
1749
2110
|
function resolve2(ast, opts) {
|
|
1750
|
-
const
|
|
1751
|
-
return
|
|
2111
|
+
const tree = buildTree(ast, opts);
|
|
2112
|
+
return resolveTree(tree, { ...opts, useSystemEnvironment: true, allowUnresolved: false });
|
|
1752
2113
|
}
|
|
1753
2114
|
|
|
1754
2115
|
// src/config.ts
|
|
1755
2116
|
var Config = class _Config {
|
|
1756
|
-
constructor(root) {
|
|
2117
|
+
constructor(root, opts) {
|
|
1757
2118
|
this.root = root;
|
|
2119
|
+
this._resolved = opts?.resolved ?? true;
|
|
2120
|
+
this._parseBaseDir = opts?.parseBaseDir;
|
|
2121
|
+
this._originDescription = opts?.originDescription;
|
|
2122
|
+
this._resObjRoot = opts?.resObjRoot;
|
|
2123
|
+
this._resolveOpts = opts?.resolveOpts;
|
|
2124
|
+
}
|
|
2125
|
+
/** Returns true when the Config's value tree contains no unresolved
|
|
2126
|
+
* substitution placeholders. Whole-config granularity (E12 decision 11). */
|
|
2127
|
+
isResolved() {
|
|
2128
|
+
return this._resolved;
|
|
2129
|
+
}
|
|
2130
|
+
/** @internal Test-only: construct a resolved Config from a HoconValue. */
|
|
2131
|
+
static _fromResolvedValue(root, opts) {
|
|
2132
|
+
return new _Config(root, { resolved: true, originDescription: opts?.originDescription });
|
|
2133
|
+
}
|
|
2134
|
+
/** @internal Construct an unresolved Config carrying a ResObj tree.
|
|
2135
|
+
* Used by parseStringWithOptions (deferred path) in T6. */
|
|
2136
|
+
static _fromUnresolvedResObj(tree, opts) {
|
|
2137
|
+
const partialRoot = buildPartialHoconFromResObj(tree);
|
|
2138
|
+
return new _Config(partialRoot, {
|
|
2139
|
+
resolved: opts.resolved,
|
|
2140
|
+
parseBaseDir: opts.parseBaseDir,
|
|
2141
|
+
originDescription: opts.originDescription,
|
|
2142
|
+
resObjRoot: tree,
|
|
2143
|
+
resolveOpts: opts.resolveOpts
|
|
2144
|
+
});
|
|
1758
2145
|
}
|
|
1759
2146
|
get(path2) {
|
|
1760
2147
|
const v = this.lookupNode(path2);
|
|
@@ -1798,13 +2185,29 @@ var Config = class _Config {
|
|
|
1798
2185
|
}
|
|
1799
2186
|
getConfig(path2) {
|
|
1800
2187
|
const v = this.lookupNode(path2);
|
|
1801
|
-
if (v === void 0)
|
|
2188
|
+
if (v === void 0) {
|
|
2189
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2190
|
+
throw new NotResolvedError(path2);
|
|
2191
|
+
}
|
|
2192
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2193
|
+
}
|
|
1802
2194
|
if (v.kind !== "object") throw new ConfigError(`expected object at ${path2}`, path2);
|
|
1803
|
-
|
|
2195
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2196
|
+
throw new NotResolvedError(path2);
|
|
2197
|
+
}
|
|
2198
|
+
return new _Config(v, { resolved: this._resolved });
|
|
1804
2199
|
}
|
|
1805
2200
|
getList(path2) {
|
|
1806
2201
|
const v = this.lookupNode(path2);
|
|
1807
|
-
if (v === void 0)
|
|
2202
|
+
if (v === void 0) {
|
|
2203
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2204
|
+
throw new NotResolvedError(path2);
|
|
2205
|
+
}
|
|
2206
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2207
|
+
}
|
|
2208
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2209
|
+
throw new NotResolvedError(path2);
|
|
2210
|
+
}
|
|
1808
2211
|
if (v.kind === "object") {
|
|
1809
2212
|
const converted = numericObjectToArray(v);
|
|
1810
2213
|
if (converted !== null) return converted.map(hoconToJs);
|
|
@@ -1819,12 +2222,138 @@ var Config = class _Config {
|
|
|
1819
2222
|
return [...this.root.fields.keys()];
|
|
1820
2223
|
}
|
|
1821
2224
|
withFallback(fallback) {
|
|
1822
|
-
|
|
1823
|
-
|
|
2225
|
+
if (fallback == null) return this;
|
|
2226
|
+
const selfTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2227
|
+
const fbTree = fallback._resObjRoot ?? hoconValueToResObj2(fallback.root);
|
|
2228
|
+
const merged = mergeUnresolved(selfTree, fbTree);
|
|
2229
|
+
const hasPlaceholders = containsPlaceholders(merged);
|
|
2230
|
+
const partialRoot = buildPartialHoconFromResObj(merged);
|
|
2231
|
+
return new _Config(partialRoot, {
|
|
2232
|
+
resolved: !hasPlaceholders,
|
|
2233
|
+
parseBaseDir: this._parseBaseDir,
|
|
2234
|
+
originDescription: this._originDescription,
|
|
2235
|
+
resObjRoot: merged,
|
|
2236
|
+
resolveOpts: this._resolveOpts ?? fallback._resolveOpts
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Runs substitution resolution (phase 2) on the stored unresolved tree.
|
|
2241
|
+
* Idempotent: calling resolve() on an already-resolved Config returns an
|
|
2242
|
+
* equivalent resolved Config.
|
|
2243
|
+
*
|
|
2244
|
+
* opts.allowUnresolved (default false) — when true, leaves unresolved
|
|
2245
|
+
* non-optional placeholders in place rather than throwing.
|
|
2246
|
+
* opts.useSystemEnvironment (default true) — when false, env var lookups
|
|
2247
|
+
* are suppressed (hermetic resolution).
|
|
2248
|
+
*
|
|
2249
|
+
* E12 decision 3.
|
|
2250
|
+
*/
|
|
2251
|
+
resolve(opts = {}) {
|
|
2252
|
+
if (this._resolved) {
|
|
2253
|
+
return new _Config(this.root, {
|
|
2254
|
+
resolved: true,
|
|
2255
|
+
parseBaseDir: this._parseBaseDir,
|
|
2256
|
+
originDescription: this._originDescription
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
const tree = this._resObjRoot;
|
|
2260
|
+
if (!tree) {
|
|
2261
|
+
return new _Config(this.root, { resolved: true });
|
|
2262
|
+
}
|
|
2263
|
+
const resolveOpts = {
|
|
2264
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2265
|
+
throw new Error("no files");
|
|
2266
|
+
} },
|
|
2267
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2268
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2269
|
+
originDescription: this._originDescription
|
|
2270
|
+
};
|
|
2271
|
+
const resolved = resolveTree(tree, resolveOpts);
|
|
2272
|
+
if (resolved.kind !== "object") throw new Error("resolve: expected object root");
|
|
2273
|
+
if (opts.allowUnresolved) {
|
|
2274
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(resolved);
|
|
2275
|
+
return new _Config(stripped, {
|
|
2276
|
+
resolved: !hadPlaceholders,
|
|
2277
|
+
parseBaseDir: this._parseBaseDir,
|
|
2278
|
+
originDescription: this._originDescription,
|
|
2279
|
+
resObjRoot: hadPlaceholders ? tree : void 0,
|
|
2280
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
return new _Config(resolved, {
|
|
2284
|
+
resolved: true,
|
|
2285
|
+
parseBaseDir: this._parseBaseDir,
|
|
2286
|
+
originDescription: this._originDescription
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Resolves receiver substitutions using source as lookup context.
|
|
2291
|
+
* Source's keys are NOT merged into the result — differs from
|
|
2292
|
+
* `this.withFallback(source).resolve()` which includes source keys.
|
|
2293
|
+
*
|
|
2294
|
+
* Precondition (E12 decision 10): source must be resolved. If
|
|
2295
|
+
* source.isResolved() is false, throws NotResolvedError.
|
|
2296
|
+
*
|
|
2297
|
+
* On an already-resolved receiver, resolveWith is a no-op (returns an
|
|
2298
|
+
* equivalent resolved Config, source keys not included).
|
|
2299
|
+
*
|
|
2300
|
+
* E12 decisions 9, 10.
|
|
2301
|
+
*/
|
|
2302
|
+
resolveWith(source, opts = {}) {
|
|
2303
|
+
if (!source.isResolved()) {
|
|
2304
|
+
throw new NotResolvedError("source");
|
|
2305
|
+
}
|
|
2306
|
+
if (this._resolved) {
|
|
2307
|
+
return new _Config(this.root, {
|
|
2308
|
+
resolved: true,
|
|
2309
|
+
parseBaseDir: this._parseBaseDir,
|
|
2310
|
+
originDescription: this._originDescription
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
const receiverTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2314
|
+
const srcTree = hoconValueToResObj2(source.root);
|
|
2315
|
+
const merged = mergeUnresolved(receiverTree, srcTree);
|
|
2316
|
+
const resolveOpts = {
|
|
2317
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2318
|
+
throw new Error("no files");
|
|
2319
|
+
} },
|
|
2320
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2321
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2322
|
+
originDescription: this._originDescription
|
|
2323
|
+
};
|
|
2324
|
+
const resolved = resolveTree(merged, resolveOpts);
|
|
2325
|
+
if (resolved.kind !== "object") throw new Error("resolveWith: expected object root");
|
|
2326
|
+
const receiverShapeForFilter = resObjToKeyShape(receiverTree);
|
|
2327
|
+
const filtered = filterByReceiverShape(resolved, receiverShapeForFilter);
|
|
2328
|
+
if (opts.allowUnresolved) {
|
|
2329
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(filtered);
|
|
2330
|
+
return new _Config(stripped, {
|
|
2331
|
+
resolved: !hadPlaceholders,
|
|
2332
|
+
parseBaseDir: this._parseBaseDir,
|
|
2333
|
+
originDescription: this._originDescription,
|
|
2334
|
+
resObjRoot: hadPlaceholders ? receiverTree : void 0,
|
|
2335
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2336
|
+
});
|
|
2337
|
+
}
|
|
2338
|
+
return new _Config(filtered, {
|
|
2339
|
+
resolved: true,
|
|
2340
|
+
parseBaseDir: this._parseBaseDir,
|
|
2341
|
+
originDescription: this._originDescription
|
|
2342
|
+
});
|
|
1824
2343
|
}
|
|
1825
2344
|
toObject() {
|
|
1826
2345
|
return hoconToJs(this.root);
|
|
1827
2346
|
}
|
|
2347
|
+
/**
|
|
2348
|
+
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
2349
|
+
* JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
|
|
2350
|
+
* against Lightbend ground truth. NOT part of the public API.
|
|
2351
|
+
*
|
|
2352
|
+
* Throws if the Config is unresolved (contains placeholders).
|
|
2353
|
+
*/
|
|
2354
|
+
_renderJSONForTest() {
|
|
2355
|
+
return renderHoconAsJSON(this.root);
|
|
2356
|
+
}
|
|
1828
2357
|
lookupNode(path2) {
|
|
1829
2358
|
const segments = splitConfigPath(path2);
|
|
1830
2359
|
let current = this.root;
|
|
@@ -1836,9 +2365,40 @@ var Config = class _Config {
|
|
|
1836
2365
|
}
|
|
1837
2366
|
return current;
|
|
1838
2367
|
}
|
|
2368
|
+
/**
|
|
2369
|
+
* _resObjRootSubtreeHasPlaceholders — walk _resObjRoot to the given path and
|
|
2370
|
+
* return true if the subtree there contains any unresolved placeholder.
|
|
2371
|
+
* Used by getConfig / getList (T2) to detect transitive unresolved placeholders
|
|
2372
|
+
* in object / array subtrees that would otherwise appear to be "resolved" in the
|
|
2373
|
+
* partial HoconValue root (because individual leaf fields are omitted rather than
|
|
2374
|
+
* the whole subtree when their value is a direct placeholder scalar, but object
|
|
2375
|
+
* sub-containers are retained in the partial root with the placeholder-keyed
|
|
2376
|
+
* fields omitted from them — so the object appears "present but empty-ish").
|
|
2377
|
+
*/
|
|
2378
|
+
_resObjRootSubtreeHasPlaceholders(path2) {
|
|
2379
|
+
const tree = this._resObjRoot;
|
|
2380
|
+
if (!tree) return false;
|
|
2381
|
+
const segments = splitConfigPath(path2);
|
|
2382
|
+
let cur = tree;
|
|
2383
|
+
for (const seg of segments) {
|
|
2384
|
+
const val = cur.fields.get(seg);
|
|
2385
|
+
if (val === void 0) return false;
|
|
2386
|
+
if (isResObj(val)) {
|
|
2387
|
+
cur = val;
|
|
2388
|
+
} else {
|
|
2389
|
+
return valContainsPlaceholders(val);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
return containsPlaceholders(cur);
|
|
2393
|
+
}
|
|
1839
2394
|
requireScalar(path2) {
|
|
1840
2395
|
const v = this.lookupNode(path2);
|
|
1841
|
-
if (v === void 0)
|
|
2396
|
+
if (v === void 0) {
|
|
2397
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2398
|
+
throw new NotResolvedError(path2);
|
|
2399
|
+
}
|
|
2400
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2401
|
+
}
|
|
1842
2402
|
if (v.kind !== "scalar") throw new ConfigError(`expected scalar at ${path2}, got ${v.kind}`, path2);
|
|
1843
2403
|
return v;
|
|
1844
2404
|
}
|
|
@@ -1901,28 +2461,89 @@ function hoconToJs(v) {
|
|
|
1901
2461
|
case "array":
|
|
1902
2462
|
return v.items.map(hoconToJs);
|
|
1903
2463
|
case "object": {
|
|
1904
|
-
const obj =
|
|
2464
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1905
2465
|
for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
|
|
1906
|
-
return obj;
|
|
2466
|
+
return Object.assign({}, obj);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
function resObjToKeyShape(tree) {
|
|
2471
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2472
|
+
for (const [k, v] of tree.fields) {
|
|
2473
|
+
if (isSubst(v) || isConcat(v) || isAppend(v)) {
|
|
2474
|
+
fields.set(k, { kind: "scalar", raw: "", valueType: "null" });
|
|
2475
|
+
} else if (isResObj(v)) {
|
|
2476
|
+
fields.set(k, resObjToKeyShape(v));
|
|
2477
|
+
} else {
|
|
2478
|
+
fields.set(k, v);
|
|
1907
2479
|
}
|
|
1908
2480
|
}
|
|
2481
|
+
return { kind: "object", fields };
|
|
1909
2482
|
}
|
|
1910
|
-
function
|
|
1911
|
-
const
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
if (
|
|
1915
|
-
|
|
2483
|
+
function stripPlaceholderFields(v) {
|
|
2484
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2485
|
+
let hadPlaceholders = false;
|
|
2486
|
+
for (const [k, val] of v.fields) {
|
|
2487
|
+
if (val._kind === "subst-placeholder" || val._kind === "concat-placeholder" || val._kind === "append-placeholder") {
|
|
2488
|
+
hadPlaceholders = true;
|
|
2489
|
+
} else if (val.kind === "object") {
|
|
2490
|
+
const inner = stripPlaceholderFields(val);
|
|
2491
|
+
if (inner.hadPlaceholders) hadPlaceholders = true;
|
|
2492
|
+
fields.set(k, inner.stripped);
|
|
1916
2493
|
} else {
|
|
1917
|
-
|
|
2494
|
+
fields.set(k, val);
|
|
1918
2495
|
}
|
|
1919
2496
|
}
|
|
1920
|
-
return { kind: "object", fields
|
|
2497
|
+
return { stripped: { kind: "object", fields }, hadPlaceholders };
|
|
2498
|
+
}
|
|
2499
|
+
function filterByReceiverShape(resolved, receiverShape) {
|
|
2500
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2501
|
+
for (const [k, rv] of resolved.fields) {
|
|
2502
|
+
if (!receiverShape.fields.has(k)) continue;
|
|
2503
|
+
const receiverVal = receiverShape.fields.get(k);
|
|
2504
|
+
if (rv.kind === "object" && receiverVal.kind === "object") {
|
|
2505
|
+
fields.set(k, filterByReceiverShape(rv, receiverVal));
|
|
2506
|
+
} else {
|
|
2507
|
+
fields.set(k, rv);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return { kind: "object", fields };
|
|
2511
|
+
}
|
|
2512
|
+
function renderHoconAsJSON(v) {
|
|
2513
|
+
switch (v.kind) {
|
|
2514
|
+
case "scalar": {
|
|
2515
|
+
switch (v.valueType) {
|
|
2516
|
+
case "null":
|
|
2517
|
+
return "null";
|
|
2518
|
+
case "boolean":
|
|
2519
|
+
return v.raw;
|
|
2520
|
+
case "number": {
|
|
2521
|
+
const n = Number(v.raw);
|
|
2522
|
+
if (Number.isFinite(n)) return v.raw;
|
|
2523
|
+
return JSON.stringify(v.raw);
|
|
2524
|
+
}
|
|
2525
|
+
case "string":
|
|
2526
|
+
return JSON.stringify(v.raw);
|
|
2527
|
+
}
|
|
2528
|
+
break;
|
|
2529
|
+
}
|
|
2530
|
+
case "array": {
|
|
2531
|
+
const items = v.items.map(renderHoconAsJSON);
|
|
2532
|
+
return `[${items.join(",")}]`;
|
|
2533
|
+
}
|
|
2534
|
+
case "object": {
|
|
2535
|
+
const keys = [...v.fields.keys()].sort();
|
|
2536
|
+
const pairs = keys.map((k) => {
|
|
2537
|
+
const val = v.fields.get(k);
|
|
2538
|
+
return `${JSON.stringify(k)}:${renderHoconAsJSON(val)}`;
|
|
2539
|
+
});
|
|
2540
|
+
return `{${pairs.join(",")}}`;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
throw new Error(`renderHoconAsJSON: unsupported value kind (unresolved placeholder?)`);
|
|
1921
2544
|
}
|
|
1922
2545
|
|
|
1923
2546
|
// src/parse.ts
|
|
1924
|
-
import * as fs from "fs";
|
|
1925
|
-
import * as path from "path";
|
|
1926
2547
|
function getEnv(opts) {
|
|
1927
2548
|
if (opts.env !== void 0) return opts.env;
|
|
1928
2549
|
if (typeof process !== "undefined" && process.env) return process.env;
|
|
@@ -1939,7 +2560,10 @@ function buildResolveContext(input, opts) {
|
|
|
1939
2560
|
env: getEnv(opts),
|
|
1940
2561
|
baseDir: opts.baseDir,
|
|
1941
2562
|
readFileSync: opts.readFileSync ?? defaultReadFileSync,
|
|
1942
|
-
readFile: opts.readFile
|
|
2563
|
+
readFile: opts.readFile,
|
|
2564
|
+
originDescription: opts.originDescription,
|
|
2565
|
+
resolveFrom: opts.resolveFrom,
|
|
2566
|
+
packageResolver: opts.packageResolver
|
|
1943
2567
|
};
|
|
1944
2568
|
return { ast, resolveOpts };
|
|
1945
2569
|
}
|
|
@@ -1947,7 +2571,7 @@ function parse(input, opts = {}) {
|
|
|
1947
2571
|
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
1948
2572
|
const value = resolve2(ast, resolveOpts);
|
|
1949
2573
|
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
1950
|
-
return new Config(value);
|
|
2574
|
+
return new Config(value, { resolved: true, originDescription: opts.originDescription });
|
|
1951
2575
|
}
|
|
1952
2576
|
function parseFile(filePath, opts = {}) {
|
|
1953
2577
|
if (typeof process === "undefined" && !opts.readFileSync) {
|