@o3co/ts.hocon 1.3.0 → 1.4.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.
- 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 +1109 -360
- package/dist/index.d.cts +45 -2
- package/dist/index.d.ts +45 -2
- package/dist/index.js +1098 -359
- package/dist/zod.cjs +829 -200
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +828 -200
- package/package.json +3 -1
- package/dist/parse-nNbyqnOh.d.cts +0 -53
- package/dist/parse-nNbyqnOh.d.ts +0 -53
package/dist/zod.cjs
CHANGED
|
@@ -234,6 +234,10 @@ function parseBytes(value, outputUnit = "B") {
|
|
|
234
234
|
return outputUnit === "B" ? Math.round(result) : result;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
// src/parse.ts
|
|
238
|
+
var fs = __toESM(require("fs"), 1);
|
|
239
|
+
var path = __toESM(require("path"), 1);
|
|
240
|
+
|
|
237
241
|
// src/errors.ts
|
|
238
242
|
var ParseError = class extends Error {
|
|
239
243
|
constructor(message, line, col, file) {
|
|
@@ -254,6 +258,14 @@ var ResolveError = class extends Error {
|
|
|
254
258
|
this.name = "ResolveError";
|
|
255
259
|
}
|
|
256
260
|
};
|
|
261
|
+
var PackageLookupError = class extends ResolveError {
|
|
262
|
+
constructor(message, identifier, packageFile, line, col) {
|
|
263
|
+
super(message, `${identifier}/${packageFile}`, line, col);
|
|
264
|
+
this.identifier = identifier;
|
|
265
|
+
this.packageFile = packageFile;
|
|
266
|
+
this.name = "PackageLookupError";
|
|
267
|
+
}
|
|
268
|
+
};
|
|
257
269
|
var ConfigError = class extends Error {
|
|
258
270
|
constructor(message, path2) {
|
|
259
271
|
super(message);
|
|
@@ -261,6 +273,156 @@ var ConfigError = class extends Error {
|
|
|
261
273
|
this.name = "ConfigError";
|
|
262
274
|
}
|
|
263
275
|
};
|
|
276
|
+
var NotResolvedError = class extends ConfigError {
|
|
277
|
+
constructor(path2) {
|
|
278
|
+
super(`value at path "${path2}" is not resolved (call resolve() first)`, path2);
|
|
279
|
+
this.name = "NotResolvedError";
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/internal/resolver/types.ts
|
|
284
|
+
var separatorValues = /* @__PURE__ */ new WeakSet();
|
|
285
|
+
function isSubst(v) {
|
|
286
|
+
return v._kind === "subst-placeholder";
|
|
287
|
+
}
|
|
288
|
+
function isConcat(v) {
|
|
289
|
+
return v._kind === "concat-placeholder";
|
|
290
|
+
}
|
|
291
|
+
function isAppend(v) {
|
|
292
|
+
return v._kind === "append-placeholder";
|
|
293
|
+
}
|
|
294
|
+
function isResObj(v) {
|
|
295
|
+
return v._kind === "res-obj";
|
|
296
|
+
}
|
|
297
|
+
function makeResObj() {
|
|
298
|
+
return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
|
|
299
|
+
}
|
|
300
|
+
function mergeUnresolved(receiver, fallback) {
|
|
301
|
+
const result = makeResObj();
|
|
302
|
+
for (const [k, v] of fallback.fields) {
|
|
303
|
+
result.fields.set(k, v);
|
|
304
|
+
}
|
|
305
|
+
for (const [k, v] of fallback.priorValues) {
|
|
306
|
+
result.priorValues.set(k, v);
|
|
307
|
+
}
|
|
308
|
+
for (const [k, rv] of receiver.fields) {
|
|
309
|
+
const existing = result.fields.get(k);
|
|
310
|
+
if (existing !== void 0) {
|
|
311
|
+
const recPrior = receiver.priorValues.get(k);
|
|
312
|
+
if (recPrior !== void 0 && !isResObj(recPrior)) {
|
|
313
|
+
result.fields.set(k, rv);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (isResObj(rv) && isResObj(existing)) {
|
|
317
|
+
result.fields.set(k, mergeUnresolved(rv, existing));
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
result.priorValues.set(k, existing);
|
|
321
|
+
}
|
|
322
|
+
result.fields.set(k, rv);
|
|
323
|
+
}
|
|
324
|
+
for (const [k, v] of receiver.priorValues) {
|
|
325
|
+
if (receiver.fields.has(k)) {
|
|
326
|
+
result.priorValues.set(k, v);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/internal/resolver/utils.ts
|
|
333
|
+
function segmentsToKey(segments) {
|
|
334
|
+
return segments.map((s) => {
|
|
335
|
+
const t = s.text;
|
|
336
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
337
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
338
|
+
}
|
|
339
|
+
return t;
|
|
340
|
+
}).join(".");
|
|
341
|
+
}
|
|
342
|
+
function lookupPath(root, segments) {
|
|
343
|
+
const [head, ...tail] = segments;
|
|
344
|
+
if (head === void 0) return void 0;
|
|
345
|
+
const key = head.text;
|
|
346
|
+
const val = root.fields.get(key);
|
|
347
|
+
if (val === void 0) return void 0;
|
|
348
|
+
if (tail.length === 0) return val;
|
|
349
|
+
if (isResObj(val)) return lookupPath(val, tail);
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
function lookupResObj(root, segments) {
|
|
353
|
+
let cur = root;
|
|
354
|
+
for (const seg of segments) {
|
|
355
|
+
const val = cur.fields.get(seg.text);
|
|
356
|
+
if (val === void 0 || !isResObj(val)) return void 0;
|
|
357
|
+
cur = val;
|
|
358
|
+
}
|
|
359
|
+
return cur;
|
|
360
|
+
}
|
|
361
|
+
function deepMergeHoconValues(base, overlay) {
|
|
362
|
+
const merged = new Map(base.fields);
|
|
363
|
+
for (const [k, v] of overlay.fields) {
|
|
364
|
+
const existing = merged.get(k);
|
|
365
|
+
if (existing?.kind === "object" && v.kind === "object") {
|
|
366
|
+
merged.set(k, deepMergeHoconValues(existing, v));
|
|
367
|
+
} else {
|
|
368
|
+
merged.set(k, v);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return { kind: "object", fields: merged };
|
|
372
|
+
}
|
|
373
|
+
function deepMergeResObjInto(dst, src) {
|
|
374
|
+
for (const [k, srcVal] of src.fields) {
|
|
375
|
+
const dstVal = dst.fields.get(k);
|
|
376
|
+
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
377
|
+
deepMergeResObjInto(dstVal, srcVal);
|
|
378
|
+
} else {
|
|
379
|
+
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
380
|
+
dst.fields.set(k, srcVal);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
for (const [k, srcPrior] of src.priorValues) {
|
|
384
|
+
if (!dst.priorValues.has(k)) {
|
|
385
|
+
dst.priorValues.set(k, srcPrior);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function hoconValueToResObj(hv) {
|
|
390
|
+
const obj = makeResObj();
|
|
391
|
+
if (hv.kind !== "object") return obj;
|
|
392
|
+
for (const [key, val] of hv.fields) {
|
|
393
|
+
if (val.kind === "object") {
|
|
394
|
+
obj.fields.set(key, hoconValueToResObj(val));
|
|
395
|
+
} else {
|
|
396
|
+
obj.fields.set(key, val);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return obj;
|
|
400
|
+
}
|
|
401
|
+
function isFileNotFoundError(e) {
|
|
402
|
+
if (!(e instanceof Error)) return false;
|
|
403
|
+
const code = e.code;
|
|
404
|
+
if (code === "ENOENT" || code === "MODULE_NOT_FOUND") return true;
|
|
405
|
+
const msg = e.message.toLowerCase();
|
|
406
|
+
return msg.includes("not found") || msg.includes("no such file") || msg.includes("enoent");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/internal/resolver/include-loader.ts
|
|
410
|
+
var import_node_module = require("module");
|
|
411
|
+
var nodePath = __toESM(require("path"), 1);
|
|
412
|
+
|
|
413
|
+
// src/internal/parser/empty-check.ts
|
|
414
|
+
function hasContentTokens(tokens) {
|
|
415
|
+
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
416
|
+
}
|
|
417
|
+
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
418
|
+
if (!hasContentTokens(tokens)) {
|
|
419
|
+
throw new ParseError(
|
|
420
|
+
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
421
|
+
1,
|
|
422
|
+
1
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
264
426
|
|
|
265
427
|
// src/internal/lexer/lexer.ts
|
|
266
428
|
function isHoconWhitespace(ch) {
|
|
@@ -825,58 +987,137 @@ var Parser = class {
|
|
|
825
987
|
const p = this.currentPos();
|
|
826
988
|
this.skip("newline");
|
|
827
989
|
const t = this.peek();
|
|
828
|
-
let path2;
|
|
829
|
-
let required = false;
|
|
830
|
-
let isFile = false;
|
|
831
990
|
if (t.kind === "unquoted" && (t.value === "required(" || t.value === "required" || t.value.startsWith("required("))) {
|
|
832
991
|
if (t.value === "required") {
|
|
833
|
-
const
|
|
834
|
-
if (
|
|
992
|
+
const next = this.peek(1);
|
|
993
|
+
if (next.kind !== "unquoted" || !next.value.startsWith("(")) {
|
|
835
994
|
throw new ParseError("include required must be followed by (", t.line, t.col);
|
|
836
995
|
}
|
|
837
996
|
}
|
|
838
|
-
required = true;
|
|
839
997
|
this.advance();
|
|
840
998
|
const innerPrefix = t.value.startsWith("required(") ? t.value.slice("required(".length) : "";
|
|
841
|
-
if (innerPrefix.startsWith("url") || innerPrefix.startsWith("classpath")) {
|
|
999
|
+
if (innerPrefix === "url" || innerPrefix.startsWith("url(") || innerPrefix === "classpath" || innerPrefix.startsWith("classpath(")) {
|
|
842
1000
|
throw new ParseError("include url(...) and classpath(...) are not supported", t.line, t.col);
|
|
843
1001
|
}
|
|
844
|
-
|
|
845
|
-
|
|
1002
|
+
const nextTok = this.peek();
|
|
1003
|
+
if (innerPrefix === "package" || innerPrefix.startsWith("package(") || innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "package(" || nextTok.value.startsWith("package("))) {
|
|
1004
|
+
const consumePackageToken = innerPrefix === "";
|
|
1005
|
+
const { qualifier, path: path3 } = this.parsePackageArgs(consumePackageToken);
|
|
1006
|
+
return this.makeIncludeField(qualifier, path3, true, p);
|
|
846
1007
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1008
|
+
if (innerPrefix === "file" || innerPrefix.startsWith("file(") || innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "file(" || nextTok.value === "file")) {
|
|
1009
|
+
if (innerPrefix === "") {
|
|
1010
|
+
this.advance();
|
|
1011
|
+
}
|
|
1012
|
+
const path3 = this.parseQuotedPathSkipWrapper(t);
|
|
1013
|
+
return this.makeIncludeField({ kind: "file" }, path3, true, p);
|
|
1014
|
+
}
|
|
1015
|
+
if (innerPrefix === "" && nextTok.kind === "unquoted" && (nextTok.value === "url" || nextTok.value.startsWith("url(") || nextTok.value === "classpath" || nextTok.value.startsWith("classpath("))) {
|
|
1016
|
+
throw new ParseError("include url(...) and classpath(...) are not supported", nextTok.line, nextTok.col);
|
|
1017
|
+
}
|
|
1018
|
+
if (innerPrefix !== "") {
|
|
1019
|
+
if (innerPrefix === ")") {
|
|
1020
|
+
throw new ParseError("include required(...) must contain a path", t.line, t.col);
|
|
1021
|
+
}
|
|
1022
|
+
const name = innerPrefix.replace(/\(.*$/, "").replace(/\).*$/, "");
|
|
1023
|
+
throw new ParseError(`unknown include qualifier inside required(): "${name}"`, t.line, t.col);
|
|
1024
|
+
}
|
|
1025
|
+
const path2 = this.parseQuotedPathSkipWrapper(t);
|
|
1026
|
+
return this.makeIncludeField({ kind: "bare" }, path2, true, p);
|
|
1027
|
+
}
|
|
1028
|
+
if (t.kind === "string") {
|
|
1029
|
+
const path2 = this.advance().value;
|
|
1030
|
+
return this.makeIncludeField({ kind: "bare" }, path2, false, p);
|
|
1031
|
+
}
|
|
1032
|
+
if (t.kind === "unquoted" && (t.value === "file(" || t.value === "file")) {
|
|
862
1033
|
this.advance();
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1034
|
+
const path2 = this.parseQuotedPathSkipWrapper(t);
|
|
1035
|
+
return this.makeIncludeField({ kind: "file" }, path2, false, p);
|
|
1036
|
+
}
|
|
1037
|
+
if (t.kind === "unquoted" && (t.value === "package(" || t.value.startsWith("package("))) {
|
|
1038
|
+
const { qualifier, path: path2 } = this.parsePackageArgs();
|
|
1039
|
+
return this.makeIncludeField(qualifier, path2, false, p);
|
|
1040
|
+
}
|
|
1041
|
+
if (t.kind === "unquoted" && (t.value === "url" || t.value.startsWith("url("))) {
|
|
868
1042
|
throw new ParseError("include url(...) is not supported", t.line, t.col);
|
|
869
|
-
}
|
|
1043
|
+
}
|
|
1044
|
+
if (t.kind === "unquoted" && (t.value === "classpath" || t.value.startsWith("classpath("))) {
|
|
870
1045
|
throw new ParseError("include classpath(...) is not supported", t.line, t.col);
|
|
871
|
-
} else {
|
|
872
|
-
throw new ParseError(`expected include path, got ${t.kind}`, t.line, t.col);
|
|
873
1046
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1047
|
+
throw new ParseError(`expected include path, got ${t.kind}`, t.line, t.col);
|
|
1048
|
+
}
|
|
1049
|
+
makeIncludeField(qualifier, path2, required, pos) {
|
|
1050
|
+
return { key: [], value: { kind: "include", qualifier, path: path2, required, pos }, append: false, pos };
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Skip past include-syntax wrapper tokens to find the quoted path string.
|
|
1054
|
+
* Strict: only allows `(`, `)`, and bare qualifier keywords (file / file( /
|
|
1055
|
+
* package / package( / url / url( / classpath / classpath() between the
|
|
1056
|
+
* directive and the quoted path. Anything else is a parse error rather than
|
|
1057
|
+
* silently swallowed (see ts#113).
|
|
1058
|
+
*/
|
|
1059
|
+
parseQuotedPathSkipWrapper(errTok) {
|
|
1060
|
+
while (this.isIncludeWrapperToken(this.peek())) this.advance();
|
|
1061
|
+
if (this.peek().kind !== "string") {
|
|
1062
|
+
const tok = this.peek();
|
|
1063
|
+
if (tok.kind === "eof") throw new ParseError("expected include path", errTok.line, errTok.col);
|
|
1064
|
+
const desc = tok.kind === "unquoted" ? `unquoted "${tok.value}"` : tok.kind;
|
|
1065
|
+
throw new ParseError(`expected quoted include path, got ${desc}`, tok.line, tok.col);
|
|
1066
|
+
}
|
|
1067
|
+
const path2 = this.advance().value;
|
|
1068
|
+
while (this.peek().kind === "unquoted" && (this.peek().value === ")" || this.peek().value === "))")) {
|
|
1069
|
+
this.advance();
|
|
1070
|
+
}
|
|
1071
|
+
const after = this.peek();
|
|
1072
|
+
if (after.kind !== "newline" && after.kind !== "rbrace" && after.kind !== "eof" && after.kind !== "comma") {
|
|
1073
|
+
const desc = after.kind === "unquoted" ? `unquoted "${after.value}"` : after.kind;
|
|
1074
|
+
throw new ParseError(`unexpected token after include path: ${desc}`, after.line, after.col);
|
|
1075
|
+
}
|
|
1076
|
+
return path2;
|
|
1077
|
+
}
|
|
1078
|
+
isIncludeWrapperToken(tok) {
|
|
1079
|
+
return tok.kind === "unquoted" && tok.value === "(";
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Parse the body of `package("identifier", "file")`.
|
|
1083
|
+
*
|
|
1084
|
+
* @param consumePackageToken - When true (default), the current token is `package(` and
|
|
1085
|
+
* must be consumed first. When false, the lexer already bundled `package(` into the
|
|
1086
|
+
* preceding token (e.g. `required(package("`), so we are already positioned at the
|
|
1087
|
+
* first string argument.
|
|
1088
|
+
*/
|
|
1089
|
+
parsePackageArgs(consumePackageToken = true) {
|
|
1090
|
+
const pkgTok = consumePackageToken ? this.advance() : this.peek();
|
|
1091
|
+
this.skip("newline");
|
|
1092
|
+
const idTok = this.peek();
|
|
1093
|
+
if (idTok.kind !== "string") {
|
|
1094
|
+
throw new ParseError("include package: expected quoted identifier as first argument", pkgTok.line, pkgTok.col);
|
|
1095
|
+
}
|
|
1096
|
+
const identifier = this.advance().value;
|
|
1097
|
+
this.skip("newline");
|
|
1098
|
+
if (this.peek().kind !== "comma") {
|
|
1099
|
+
const after = this.peek();
|
|
1100
|
+
throw new ParseError(
|
|
1101
|
+
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",
|
|
1102
|
+
pkgTok.line,
|
|
1103
|
+
pkgTok.col
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
this.advance();
|
|
1107
|
+
this.skip("newline");
|
|
1108
|
+
const fileTok = this.peek();
|
|
1109
|
+
if (fileTok.kind !== "string") {
|
|
1110
|
+
throw new ParseError(
|
|
1111
|
+
"include package: requires exactly two arguments (identifier, file) \u2014 one-arg form is not supported",
|
|
1112
|
+
pkgTok.line,
|
|
1113
|
+
pkgTok.col
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
const filePath = this.advance().value;
|
|
1117
|
+
while (this.peek().kind !== "newline" && this.peek().kind !== "rbrace" && this.peek().kind !== "eof" && this.peek().kind !== "comma") {
|
|
1118
|
+
this.advance();
|
|
1119
|
+
}
|
|
1120
|
+
return { qualifier: { kind: "package", identifier }, path: filePath };
|
|
880
1121
|
}
|
|
881
1122
|
parseValue() {
|
|
882
1123
|
const p = this.currentPos();
|
|
@@ -917,7 +1158,9 @@ var Parser = class {
|
|
|
917
1158
|
parts.push(node);
|
|
918
1159
|
}
|
|
919
1160
|
if (parts.length === 0) throw new ParseError("expected value", this.peek().line, this.peek().col);
|
|
920
|
-
if (parts.length === 1)
|
|
1161
|
+
if (parts.length === 1) {
|
|
1162
|
+
return parts[0];
|
|
1163
|
+
}
|
|
921
1164
|
return { kind: "concat", nodes: parts, pos: p };
|
|
922
1165
|
}
|
|
923
1166
|
parseArray() {
|
|
@@ -949,118 +1192,6 @@ function parseTokens(tokens) {
|
|
|
949
1192
|
return new Parser(tokens).parse();
|
|
950
1193
|
}
|
|
951
1194
|
|
|
952
|
-
// src/internal/parser/empty-check.ts
|
|
953
|
-
function hasContentTokens(tokens) {
|
|
954
|
-
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
955
|
-
}
|
|
956
|
-
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
957
|
-
if (!hasContentTokens(tokens)) {
|
|
958
|
-
throw new ParseError(
|
|
959
|
-
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
960
|
-
1,
|
|
961
|
-
1
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
// src/internal/resolver/types.ts
|
|
967
|
-
var separatorValues = /* @__PURE__ */ new WeakSet();
|
|
968
|
-
function isSubst(v) {
|
|
969
|
-
return v._kind === "subst-placeholder";
|
|
970
|
-
}
|
|
971
|
-
function isConcat(v) {
|
|
972
|
-
return v._kind === "concat-placeholder";
|
|
973
|
-
}
|
|
974
|
-
function isAppend(v) {
|
|
975
|
-
return v._kind === "append-placeholder";
|
|
976
|
-
}
|
|
977
|
-
function isResObj(v) {
|
|
978
|
-
return v._kind === "res-obj";
|
|
979
|
-
}
|
|
980
|
-
function makeResObj() {
|
|
981
|
-
return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
// src/internal/resolver/utils.ts
|
|
985
|
-
function segmentsToKey(segments) {
|
|
986
|
-
return segments.map((s) => {
|
|
987
|
-
const t = s.text;
|
|
988
|
-
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
989
|
-
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
990
|
-
}
|
|
991
|
-
return t;
|
|
992
|
-
}).join(".");
|
|
993
|
-
}
|
|
994
|
-
function lookupPath(root, segments) {
|
|
995
|
-
const [head, ...tail] = segments;
|
|
996
|
-
if (head === void 0) return void 0;
|
|
997
|
-
const key = head.text;
|
|
998
|
-
const val = root.fields.get(key);
|
|
999
|
-
if (val === void 0) return void 0;
|
|
1000
|
-
if (tail.length === 0) return val;
|
|
1001
|
-
if (isResObj(val)) return lookupPath(val, tail);
|
|
1002
|
-
return void 0;
|
|
1003
|
-
}
|
|
1004
|
-
function lookupResObj(root, segments) {
|
|
1005
|
-
let cur = root;
|
|
1006
|
-
for (const seg of segments) {
|
|
1007
|
-
const val = cur.fields.get(seg.text);
|
|
1008
|
-
if (val === void 0 || !isResObj(val)) return void 0;
|
|
1009
|
-
cur = val;
|
|
1010
|
-
}
|
|
1011
|
-
return cur;
|
|
1012
|
-
}
|
|
1013
|
-
function deepMergeHoconValues(base, overlay) {
|
|
1014
|
-
const merged = new Map(base.fields);
|
|
1015
|
-
for (const [k, v] of overlay.fields) {
|
|
1016
|
-
const existing = merged.get(k);
|
|
1017
|
-
if (existing?.kind === "object" && v.kind === "object") {
|
|
1018
|
-
merged.set(k, deepMergeHoconValues(existing, v));
|
|
1019
|
-
} else {
|
|
1020
|
-
merged.set(k, v);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
return { kind: "object", fields: merged };
|
|
1024
|
-
}
|
|
1025
|
-
function deepMergeResObjInto(dst, src) {
|
|
1026
|
-
for (const [k, srcVal] of src.fields) {
|
|
1027
|
-
const dstVal = dst.fields.get(k);
|
|
1028
|
-
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
1029
|
-
deepMergeResObjInto(dstVal, srcVal);
|
|
1030
|
-
} else {
|
|
1031
|
-
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
1032
|
-
dst.fields.set(k, srcVal);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
for (const [k, srcPrior] of src.priorValues) {
|
|
1036
|
-
if (!dst.priorValues.has(k)) {
|
|
1037
|
-
dst.priorValues.set(k, srcPrior);
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
function hoconValueToResObj(hv) {
|
|
1042
|
-
const obj = makeResObj();
|
|
1043
|
-
if (hv.kind !== "object") return obj;
|
|
1044
|
-
for (const [key, val] of hv.fields) {
|
|
1045
|
-
if (val.kind === "object") {
|
|
1046
|
-
obj.fields.set(key, hoconValueToResObj(val));
|
|
1047
|
-
} else {
|
|
1048
|
-
obj.fields.set(key, val);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
return obj;
|
|
1052
|
-
}
|
|
1053
|
-
function isFileNotFoundError(e) {
|
|
1054
|
-
if (!(e instanceof Error)) return false;
|
|
1055
|
-
const code = e.code;
|
|
1056
|
-
if (code === "ENOENT" || code === "MODULE_NOT_FOUND") return true;
|
|
1057
|
-
const msg = e.message.toLowerCase();
|
|
1058
|
-
return msg.includes("not found") || msg.includes("no such file") || msg.includes("enoent");
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// src/internal/resolver/include-loader.ts
|
|
1062
|
-
var nodePath = __toESM(require("path"), 1);
|
|
1063
|
-
|
|
1064
1195
|
// src/internal/properties/properties.ts
|
|
1065
1196
|
function parseProperties(input) {
|
|
1066
1197
|
const root = /* @__PURE__ */ Object.create(null);
|
|
@@ -1120,6 +1251,85 @@ function recordToHoconValue(obj) {
|
|
|
1120
1251
|
}
|
|
1121
1252
|
|
|
1122
1253
|
// src/internal/resolver/include-loader.ts
|
|
1254
|
+
var import_meta = {};
|
|
1255
|
+
var _require = (0, import_node_module.createRequire)(import_meta.url);
|
|
1256
|
+
function isPnpPath(p) {
|
|
1257
|
+
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1258
|
+
}
|
|
1259
|
+
function validatePackageFile(file, identifier) {
|
|
1260
|
+
if (file.length === 0) {
|
|
1261
|
+
throw new ParseError(`include package("${identifier}", ...): file argument must be non-empty`, 0, 0);
|
|
1262
|
+
}
|
|
1263
|
+
if (file.includes("\\")) {
|
|
1264
|
+
throw new ParseError(
|
|
1265
|
+
`include package("${identifier}", "${file}"): backslash not allowed in file argument (use forward slash)`,
|
|
1266
|
+
0,
|
|
1267
|
+
0
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
if (file.startsWith("/")) {
|
|
1271
|
+
throw new ParseError(
|
|
1272
|
+
`include package("${identifier}", "${file}"): absolute path not allowed in file argument`,
|
|
1273
|
+
0,
|
|
1274
|
+
0
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
if (/(?:^|\/)\.\.?(?:\/|$)/u.test(file)) {
|
|
1278
|
+
throw new ParseError(
|
|
1279
|
+
`include package("${identifier}", "${file}"): path traversal (. or ..) not allowed in file argument`,
|
|
1280
|
+
0,
|
|
1281
|
+
0
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
if (file.includes("//")) {
|
|
1285
|
+
throw new ParseError(
|
|
1286
|
+
`include package("${identifier}", "${file}"): consecutive slashes not allowed in file argument`,
|
|
1287
|
+
0,
|
|
1288
|
+
0
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
function validatePackageIdentifier(identifier) {
|
|
1293
|
+
if (identifier.length === 0) {
|
|
1294
|
+
throw new ParseError("include package: identifier argument must be non-empty", 0, 0);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
function defaultPackageResolver(identifier, file, includingFile, baseDir, resolveFrom) {
|
|
1298
|
+
const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
|
|
1299
|
+
let resolved;
|
|
1300
|
+
try {
|
|
1301
|
+
resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
|
|
1302
|
+
} catch {
|
|
1303
|
+
throw new PackageLookupError(
|
|
1304
|
+
`include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
|
|
1305
|
+
identifier,
|
|
1306
|
+
file,
|
|
1307
|
+
0,
|
|
1308
|
+
0
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
if (isPnpPath(resolved)) {
|
|
1312
|
+
throw new ResolveError(
|
|
1313
|
+
`include package("${identifier}", "${file}"): Yarn Berry PnP store paths are not supported. Provide a custom packageResolver option to handle PnP resolution.`,
|
|
1314
|
+
`${identifier}/${file}`,
|
|
1315
|
+
0,
|
|
1316
|
+
0
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
const fileParts = file.split("/");
|
|
1320
|
+
const expectedBasename = fileParts[fileParts.length - 1] ?? file;
|
|
1321
|
+
const resolvedBasename = nodePath.basename(resolved);
|
|
1322
|
+
if (resolvedBasename !== expectedBasename) {
|
|
1323
|
+
throw new PackageLookupError(
|
|
1324
|
+
`include package("${identifier}", "${file}"): case mismatch \u2014 resolved "${resolvedBasename}" but expected "${expectedBasename}" (E11 decision 5: case-sensitive)`,
|
|
1325
|
+
identifier,
|
|
1326
|
+
file,
|
|
1327
|
+
0,
|
|
1328
|
+
0
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
return resolved;
|
|
1332
|
+
}
|
|
1123
1333
|
var IncludeLoader = class {
|
|
1124
1334
|
constructor(opts) {
|
|
1125
1335
|
this.opts = opts;
|
|
@@ -1199,8 +1409,77 @@ var IncludeLoader = class {
|
|
|
1199
1409
|
}
|
|
1200
1410
|
return merged;
|
|
1201
1411
|
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Resolve and load `include package("identifier", "file")`.
|
|
1414
|
+
* Validates the identifier and file arguments, resolves via the package resolver,
|
|
1415
|
+
* performs cycle detection using a JSON-serialised cycle key, and parses the content.
|
|
1416
|
+
*/
|
|
1417
|
+
// `required` is reserved for future optional-package semantics (E11 decision 7);
|
|
1418
|
+
// currently all package lookup failures throw regardless of this flag.
|
|
1419
|
+
loadPackage(identifier, file, _required) {
|
|
1420
|
+
validatePackageIdentifier(identifier);
|
|
1421
|
+
validatePackageFile(file, identifier);
|
|
1422
|
+
const { includeStack = [], baseDir, resolveFrom, packageResolver, readFileSync: readFileSync2 } = this.opts;
|
|
1423
|
+
const cycleKey = JSON.stringify(["package", identifier, file]);
|
|
1424
|
+
if (includeStack.includes(cycleKey)) {
|
|
1425
|
+
throw new ResolveError(
|
|
1426
|
+
`circular include: package("${identifier}", "${file}")`,
|
|
1427
|
+
`${identifier}/${file}`,
|
|
1428
|
+
0,
|
|
1429
|
+
0
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
if (includeStack.length >= 50) {
|
|
1433
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, `${identifier}/${file}`, 0, 0);
|
|
1434
|
+
}
|
|
1435
|
+
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1436
|
+
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1437
|
+
const content = readFileSync2(resolvedPath);
|
|
1438
|
+
if (content.length === 0) return makeResObj();
|
|
1439
|
+
const tokens = tokenize(content);
|
|
1440
|
+
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1441
|
+
const ast = parseTokens(tokens);
|
|
1442
|
+
return this.onBuildResObj(ast, {
|
|
1443
|
+
...this.opts,
|
|
1444
|
+
baseDir: nodePath.dirname(resolvedPath),
|
|
1445
|
+
includeStack: [...includeStack, cycleKey]
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Async variant of `loadPackage`.
|
|
1450
|
+
*/
|
|
1451
|
+
async loadPackageAsync(identifier, file, _required) {
|
|
1452
|
+
validatePackageIdentifier(identifier);
|
|
1453
|
+
validatePackageFile(file, identifier);
|
|
1454
|
+
const { includeStack = [], baseDir, resolveFrom, packageResolver, readFile, readFileSync: readFileSync2 } = this.opts;
|
|
1455
|
+
const cycleKey = JSON.stringify(["package", identifier, file]);
|
|
1456
|
+
if (includeStack.includes(cycleKey)) {
|
|
1457
|
+
throw new ResolveError(
|
|
1458
|
+
`circular include: package("${identifier}", "${file}")`,
|
|
1459
|
+
`${identifier}/${file}`,
|
|
1460
|
+
0,
|
|
1461
|
+
0
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
if (includeStack.length >= 50) {
|
|
1465
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, `${identifier}/${file}`, 0, 0);
|
|
1466
|
+
}
|
|
1467
|
+
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1468
|
+
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1469
|
+
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1470
|
+
const content = await read(resolvedPath);
|
|
1471
|
+
if (content.length === 0) return makeResObj();
|
|
1472
|
+
const tokens = tokenize(content);
|
|
1473
|
+
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1474
|
+
const ast = parseTokens(tokens);
|
|
1475
|
+
return this.onBuildResObjAsync(ast, {
|
|
1476
|
+
...this.opts,
|
|
1477
|
+
baseDir: nodePath.dirname(resolvedPath),
|
|
1478
|
+
includeStack: [...includeStack, cycleKey]
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1202
1481
|
loadSingle(candidate) {
|
|
1203
|
-
const { readFileSync: readFileSync2, includeStack = []
|
|
1482
|
+
const { readFileSync: readFileSync2, includeStack = [] } = this.opts;
|
|
1204
1483
|
if (includeStack.includes(candidate)) {
|
|
1205
1484
|
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
1206
1485
|
}
|
|
@@ -1215,17 +1494,18 @@ var IncludeLoader = class {
|
|
|
1215
1494
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1216
1495
|
}
|
|
1217
1496
|
const tokens = tokenize(content);
|
|
1218
|
-
|
|
1497
|
+
if (!hasContentTokens(tokens)) {
|
|
1498
|
+
return makeResObj();
|
|
1499
|
+
}
|
|
1219
1500
|
const ast = parseTokens(tokens);
|
|
1220
1501
|
return this.onBuildResObj(ast, {
|
|
1221
|
-
|
|
1502
|
+
...this.opts,
|
|
1222
1503
|
baseDir: nodePath.dirname(candidate),
|
|
1223
|
-
readFileSync: readFileSync2,
|
|
1224
1504
|
includeStack: [...includeStack, candidate]
|
|
1225
1505
|
});
|
|
1226
1506
|
}
|
|
1227
1507
|
async loadSingleAsync(candidate) {
|
|
1228
|
-
const { readFile, readFileSync: readFileSync2, includeStack = []
|
|
1508
|
+
const { readFile, readFileSync: readFileSync2, includeStack = [] } = this.opts;
|
|
1229
1509
|
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1230
1510
|
if (includeStack.includes(candidate)) {
|
|
1231
1511
|
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
@@ -1241,13 +1521,13 @@ var IncludeLoader = class {
|
|
|
1241
1521
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1242
1522
|
}
|
|
1243
1523
|
const tokens = tokenize(content);
|
|
1244
|
-
|
|
1524
|
+
if (!hasContentTokens(tokens)) {
|
|
1525
|
+
return makeResObj();
|
|
1526
|
+
}
|
|
1245
1527
|
const ast = parseTokens(tokens);
|
|
1246
1528
|
return this.onBuildResObjAsync(ast, {
|
|
1247
|
-
|
|
1529
|
+
...this.opts,
|
|
1248
1530
|
baseDir: nodePath.dirname(candidate),
|
|
1249
|
-
readFileSync: readFileSync2,
|
|
1250
|
-
readFile,
|
|
1251
1531
|
includeStack: [...includeStack, candidate]
|
|
1252
1532
|
});
|
|
1253
1533
|
}
|
|
@@ -1256,7 +1536,6 @@ var IncludeLoader = class {
|
|
|
1256
1536
|
// src/internal/resolver/structure-builder.ts
|
|
1257
1537
|
var StructureBuilder = class _StructureBuilder {
|
|
1258
1538
|
constructor(opts) {
|
|
1259
|
-
this.opts = opts;
|
|
1260
1539
|
this.loader = new IncludeLoader(opts);
|
|
1261
1540
|
this.loader.onBuildResObj = (a, o) => new _StructureBuilder(o).build(a);
|
|
1262
1541
|
this.loader.onBuildResObjAsync = async (a, o) => new _StructureBuilder(o).buildAsync(a);
|
|
@@ -1283,7 +1562,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1283
1562
|
}
|
|
1284
1563
|
applyField(obj, field, pathPrefix) {
|
|
1285
1564
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1286
|
-
const
|
|
1565
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1566
|
+
let included;
|
|
1567
|
+
if (qualifier.kind === "package") {
|
|
1568
|
+
included = this.loader.loadPackage(qualifier.identifier, path2, required);
|
|
1569
|
+
} else {
|
|
1570
|
+
included = this.loader.load(path2, required, qualifier.kind === "file");
|
|
1571
|
+
}
|
|
1287
1572
|
if (pathPrefix.length > 0) {
|
|
1288
1573
|
this.relativizeResObj(included, pathPrefix);
|
|
1289
1574
|
}
|
|
@@ -1325,7 +1610,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1325
1610
|
}
|
|
1326
1611
|
async applyFieldAsync(obj, field, pathPrefix) {
|
|
1327
1612
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1328
|
-
const
|
|
1613
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1614
|
+
let included;
|
|
1615
|
+
if (qualifier.kind === "package") {
|
|
1616
|
+
included = await this.loader.loadPackageAsync(qualifier.identifier, path2, required);
|
|
1617
|
+
} else {
|
|
1618
|
+
included = await this.loader.loadAsync(path2, required, qualifier.kind === "file");
|
|
1619
|
+
}
|
|
1329
1620
|
if (pathPrefix.length > 0) {
|
|
1330
1621
|
this.relativizeResObj(included, pathPrefix);
|
|
1331
1622
|
}
|
|
@@ -1493,6 +1784,10 @@ var SubstitutionResolver = class {
|
|
|
1493
1784
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1494
1785
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
1495
1786
|
}
|
|
1787
|
+
/** Returns a prefix for error messages, including origin info when set. */
|
|
1788
|
+
originPrefix() {
|
|
1789
|
+
return this.opts.originDescription ? `${this.opts.originDescription}: ` : "";
|
|
1790
|
+
}
|
|
1496
1791
|
resolve() {
|
|
1497
1792
|
return this.resolveResObj(this.root);
|
|
1498
1793
|
}
|
|
@@ -1533,7 +1828,8 @@ var SubstitutionResolver = class {
|
|
|
1533
1828
|
if (isConcat(v)) {
|
|
1534
1829
|
this.resolvingConcats.add(v);
|
|
1535
1830
|
try {
|
|
1536
|
-
|
|
1831
|
+
const concatResult = this.resolveConcat(v.nodes, scope, v.line, v.col);
|
|
1832
|
+
return concatResult;
|
|
1537
1833
|
} finally {
|
|
1538
1834
|
this.resolvingConcats.delete(v);
|
|
1539
1835
|
}
|
|
@@ -1578,7 +1874,7 @@ var SubstitutionResolver = class {
|
|
|
1578
1874
|
}
|
|
1579
1875
|
if (s.optional) return void 0;
|
|
1580
1876
|
throw new ResolveError(
|
|
1581
|
-
`circular substitution: ${key}`,
|
|
1877
|
+
this.originPrefix() + `circular substitution: ${key}`,
|
|
1582
1878
|
key,
|
|
1583
1879
|
s.line,
|
|
1584
1880
|
s.col
|
|
@@ -1608,7 +1904,7 @@ var SubstitutionResolver = class {
|
|
|
1608
1904
|
}
|
|
1609
1905
|
if (s.optional) return void 0;
|
|
1610
1906
|
throw new ResolveError(
|
|
1611
|
-
|
|
1907
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1612
1908
|
key,
|
|
1613
1909
|
s.line,
|
|
1614
1910
|
s.col
|
|
@@ -1641,7 +1937,7 @@ var SubstitutionResolver = class {
|
|
|
1641
1937
|
if (result !== void 0) this.cache.set(key, result);
|
|
1642
1938
|
return result;
|
|
1643
1939
|
}
|
|
1644
|
-
if (s.listSuffix) {
|
|
1940
|
+
if (s.listSuffix && this.opts.useSystemEnvironment !== false) {
|
|
1645
1941
|
const result = this.resolveEnvList(s);
|
|
1646
1942
|
if (result !== void 0) {
|
|
1647
1943
|
this.cache.set(key, result);
|
|
@@ -1650,22 +1946,36 @@ var SubstitutionResolver = class {
|
|
|
1650
1946
|
if (s.optional) return void 0;
|
|
1651
1947
|
const envBase = s.segments.map((seg) => seg.text).join(".");
|
|
1652
1948
|
throw new ResolveError(
|
|
1653
|
-
|
|
1949
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}} (no environment variables ${envBase}_0, ${envBase}_1, \u2026 set)`,
|
|
1950
|
+
key,
|
|
1951
|
+
s.line,
|
|
1952
|
+
s.col
|
|
1953
|
+
);
|
|
1954
|
+
} else if (s.listSuffix) {
|
|
1955
|
+
if (s.optional) return void 0;
|
|
1956
|
+
if (this.opts.allowUnresolved) return s;
|
|
1957
|
+
throw new ResolveError(
|
|
1958
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1654
1959
|
key,
|
|
1655
1960
|
s.line,
|
|
1656
1961
|
s.col
|
|
1657
1962
|
);
|
|
1658
1963
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1964
|
+
if (this.opts.useSystemEnvironment !== false) {
|
|
1965
|
+
const envKey = s.segments.map((seg) => seg.text).join(".");
|
|
1966
|
+
const envVal = this.opts.env[envKey] ?? (s.prefixLen > 0 ? this.opts.env[s.segments.slice(s.prefixLen).map((seg) => seg.text).join(".")] : void 0);
|
|
1967
|
+
if (envVal !== void 0) {
|
|
1968
|
+
const result = { kind: "scalar", raw: envVal, valueType: "string" };
|
|
1969
|
+
this.cache.set(key, result);
|
|
1970
|
+
return result;
|
|
1971
|
+
}
|
|
1665
1972
|
}
|
|
1666
1973
|
if (s.optional) return void 0;
|
|
1974
|
+
if (this.opts.allowUnresolved) {
|
|
1975
|
+
return s;
|
|
1976
|
+
}
|
|
1667
1977
|
throw new ResolveError(
|
|
1668
|
-
|
|
1978
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1669
1979
|
key,
|
|
1670
1980
|
s.line,
|
|
1671
1981
|
s.col
|
|
@@ -1710,8 +2020,16 @@ var SubstitutionResolver = class {
|
|
|
1710
2020
|
return [full, bare];
|
|
1711
2021
|
}
|
|
1712
2022
|
resolveConcat(nodes, scope, line = 0, col = 0) {
|
|
1713
|
-
const
|
|
1714
|
-
if (
|
|
2023
|
+
const rawResolved = nodes.map((n) => this.resolveVal(n, scope));
|
|
2024
|
+
if (this.opts.allowUnresolved) {
|
|
2025
|
+
for (const v of rawResolved) {
|
|
2026
|
+
if (v !== void 0 && isSubst(v)) {
|
|
2027
|
+
return v;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
const resolved = rawResolved.filter((v) => v !== void 0);
|
|
2032
|
+
if (resolved.length === 0) return void 0;
|
|
1715
2033
|
if (resolved.length === 1) return resolved[0];
|
|
1716
2034
|
const nonSep = resolved.filter((v) => !separatorValues.has(v));
|
|
1717
2035
|
if (nonSep.length === 0) {
|
|
@@ -1785,15 +2103,89 @@ var SubstitutionResolver = class {
|
|
|
1785
2103
|
};
|
|
1786
2104
|
|
|
1787
2105
|
// src/internal/resolver/resolver.ts
|
|
2106
|
+
function buildTree(ast, opts) {
|
|
2107
|
+
return new StructureBuilder(opts).build(ast);
|
|
2108
|
+
}
|
|
2109
|
+
function resolveTree(tree, opts) {
|
|
2110
|
+
return new SubstitutionResolver(tree, opts).resolve();
|
|
2111
|
+
}
|
|
2112
|
+
function containsPlaceholders(tree) {
|
|
2113
|
+
for (const val of tree.fields.values()) {
|
|
2114
|
+
if (valContainsPlaceholders(val)) return true;
|
|
2115
|
+
}
|
|
2116
|
+
return false;
|
|
2117
|
+
}
|
|
2118
|
+
function valContainsPlaceholders(v) {
|
|
2119
|
+
const rv = v;
|
|
2120
|
+
if (isSubst(rv) || isConcat(rv) || isAppend(rv)) return true;
|
|
2121
|
+
if (isResObj(rv)) return containsPlaceholders(rv);
|
|
2122
|
+
const hv = v;
|
|
2123
|
+
if (hv?.kind === "array") {
|
|
2124
|
+
return hv.items.some((item) => valContainsPlaceholders(item));
|
|
2125
|
+
}
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
function buildPartialHoconFromResObj(tree) {
|
|
2129
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2130
|
+
for (const [k, v] of tree.fields) {
|
|
2131
|
+
if (!isSubst(v) && !isConcat(v) && !isAppend(v)) {
|
|
2132
|
+
if (isResObj(v)) {
|
|
2133
|
+
fields.set(k, buildPartialHoconFromResObj(v));
|
|
2134
|
+
} else {
|
|
2135
|
+
if (!valContainsPlaceholders(v)) {
|
|
2136
|
+
fields.set(k, v);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
return { kind: "object", fields };
|
|
2142
|
+
}
|
|
2143
|
+
function hoconValueToResObj2(value) {
|
|
2144
|
+
const obj = makeResObj();
|
|
2145
|
+
for (const [k, v] of value.fields) {
|
|
2146
|
+
if (v.kind === "object") {
|
|
2147
|
+
obj.fields.set(k, hoconValueToResObj2(v));
|
|
2148
|
+
} else {
|
|
2149
|
+
obj.fields.set(k, v);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return obj;
|
|
2153
|
+
}
|
|
1788
2154
|
function resolve2(ast, opts) {
|
|
1789
|
-
const
|
|
1790
|
-
return
|
|
2155
|
+
const tree = buildTree(ast, opts);
|
|
2156
|
+
return resolveTree(tree, { ...opts, useSystemEnvironment: true, allowUnresolved: false });
|
|
1791
2157
|
}
|
|
1792
2158
|
|
|
1793
2159
|
// src/config.ts
|
|
1794
2160
|
var Config = class _Config {
|
|
1795
|
-
constructor(root) {
|
|
2161
|
+
constructor(root, opts) {
|
|
1796
2162
|
this.root = root;
|
|
2163
|
+
this._resolved = opts?.resolved ?? true;
|
|
2164
|
+
this._parseBaseDir = opts?.parseBaseDir;
|
|
2165
|
+
this._originDescription = opts?.originDescription;
|
|
2166
|
+
this._resObjRoot = opts?.resObjRoot;
|
|
2167
|
+
this._resolveOpts = opts?.resolveOpts;
|
|
2168
|
+
}
|
|
2169
|
+
/** Returns true when the Config's value tree contains no unresolved
|
|
2170
|
+
* substitution placeholders. Whole-config granularity (E12 decision 11). */
|
|
2171
|
+
isResolved() {
|
|
2172
|
+
return this._resolved;
|
|
2173
|
+
}
|
|
2174
|
+
/** @internal Test-only: construct a resolved Config from a HoconValue. */
|
|
2175
|
+
static _fromResolvedValue(root, opts) {
|
|
2176
|
+
return new _Config(root, { resolved: true, originDescription: opts?.originDescription });
|
|
2177
|
+
}
|
|
2178
|
+
/** @internal Construct an unresolved Config carrying a ResObj tree.
|
|
2179
|
+
* Used by parseStringWithOptions (deferred path) in T6. */
|
|
2180
|
+
static _fromUnresolvedResObj(tree, opts) {
|
|
2181
|
+
const partialRoot = buildPartialHoconFromResObj(tree);
|
|
2182
|
+
return new _Config(partialRoot, {
|
|
2183
|
+
resolved: opts.resolved,
|
|
2184
|
+
parseBaseDir: opts.parseBaseDir,
|
|
2185
|
+
originDescription: opts.originDescription,
|
|
2186
|
+
resObjRoot: tree,
|
|
2187
|
+
resolveOpts: opts.resolveOpts
|
|
2188
|
+
});
|
|
1797
2189
|
}
|
|
1798
2190
|
get(path2) {
|
|
1799
2191
|
const v = this.lookupNode(path2);
|
|
@@ -1837,13 +2229,29 @@ var Config = class _Config {
|
|
|
1837
2229
|
}
|
|
1838
2230
|
getConfig(path2) {
|
|
1839
2231
|
const v = this.lookupNode(path2);
|
|
1840
|
-
if (v === void 0)
|
|
2232
|
+
if (v === void 0) {
|
|
2233
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2234
|
+
throw new NotResolvedError(path2);
|
|
2235
|
+
}
|
|
2236
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2237
|
+
}
|
|
1841
2238
|
if (v.kind !== "object") throw new ConfigError(`expected object at ${path2}`, path2);
|
|
1842
|
-
|
|
2239
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2240
|
+
throw new NotResolvedError(path2);
|
|
2241
|
+
}
|
|
2242
|
+
return new _Config(v, { resolved: this._resolved });
|
|
1843
2243
|
}
|
|
1844
2244
|
getList(path2) {
|
|
1845
2245
|
const v = this.lookupNode(path2);
|
|
1846
|
-
if (v === void 0)
|
|
2246
|
+
if (v === void 0) {
|
|
2247
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2248
|
+
throw new NotResolvedError(path2);
|
|
2249
|
+
}
|
|
2250
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2251
|
+
}
|
|
2252
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2253
|
+
throw new NotResolvedError(path2);
|
|
2254
|
+
}
|
|
1847
2255
|
if (v.kind === "object") {
|
|
1848
2256
|
const converted = numericObjectToArray(v);
|
|
1849
2257
|
if (converted !== null) return converted.map(hoconToJs);
|
|
@@ -1858,12 +2266,138 @@ var Config = class _Config {
|
|
|
1858
2266
|
return [...this.root.fields.keys()];
|
|
1859
2267
|
}
|
|
1860
2268
|
withFallback(fallback) {
|
|
1861
|
-
|
|
1862
|
-
|
|
2269
|
+
if (fallback == null) return this;
|
|
2270
|
+
const selfTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2271
|
+
const fbTree = fallback._resObjRoot ?? hoconValueToResObj2(fallback.root);
|
|
2272
|
+
const merged = mergeUnresolved(selfTree, fbTree);
|
|
2273
|
+
const hasPlaceholders = containsPlaceholders(merged);
|
|
2274
|
+
const partialRoot = buildPartialHoconFromResObj(merged);
|
|
2275
|
+
return new _Config(partialRoot, {
|
|
2276
|
+
resolved: !hasPlaceholders,
|
|
2277
|
+
parseBaseDir: this._parseBaseDir,
|
|
2278
|
+
originDescription: this._originDescription,
|
|
2279
|
+
resObjRoot: merged,
|
|
2280
|
+
resolveOpts: this._resolveOpts ?? fallback._resolveOpts
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* Runs substitution resolution (phase 2) on the stored unresolved tree.
|
|
2285
|
+
* Idempotent: calling resolve() on an already-resolved Config returns an
|
|
2286
|
+
* equivalent resolved Config.
|
|
2287
|
+
*
|
|
2288
|
+
* opts.allowUnresolved (default false) — when true, leaves unresolved
|
|
2289
|
+
* non-optional placeholders in place rather than throwing.
|
|
2290
|
+
* opts.useSystemEnvironment (default true) — when false, env var lookups
|
|
2291
|
+
* are suppressed (hermetic resolution).
|
|
2292
|
+
*
|
|
2293
|
+
* E12 decision 3.
|
|
2294
|
+
*/
|
|
2295
|
+
resolve(opts = {}) {
|
|
2296
|
+
if (this._resolved) {
|
|
2297
|
+
return new _Config(this.root, {
|
|
2298
|
+
resolved: true,
|
|
2299
|
+
parseBaseDir: this._parseBaseDir,
|
|
2300
|
+
originDescription: this._originDescription
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
const tree = this._resObjRoot;
|
|
2304
|
+
if (!tree) {
|
|
2305
|
+
return new _Config(this.root, { resolved: true });
|
|
2306
|
+
}
|
|
2307
|
+
const resolveOpts = {
|
|
2308
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2309
|
+
throw new Error("no files");
|
|
2310
|
+
} },
|
|
2311
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2312
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2313
|
+
originDescription: this._originDescription
|
|
2314
|
+
};
|
|
2315
|
+
const resolved = resolveTree(tree, resolveOpts);
|
|
2316
|
+
if (resolved.kind !== "object") throw new Error("resolve: expected object root");
|
|
2317
|
+
if (opts.allowUnresolved) {
|
|
2318
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(resolved);
|
|
2319
|
+
return new _Config(stripped, {
|
|
2320
|
+
resolved: !hadPlaceholders,
|
|
2321
|
+
parseBaseDir: this._parseBaseDir,
|
|
2322
|
+
originDescription: this._originDescription,
|
|
2323
|
+
resObjRoot: hadPlaceholders ? tree : void 0,
|
|
2324
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
return new _Config(resolved, {
|
|
2328
|
+
resolved: true,
|
|
2329
|
+
parseBaseDir: this._parseBaseDir,
|
|
2330
|
+
originDescription: this._originDescription
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Resolves receiver substitutions using source as lookup context.
|
|
2335
|
+
* Source's keys are NOT merged into the result — differs from
|
|
2336
|
+
* `this.withFallback(source).resolve()` which includes source keys.
|
|
2337
|
+
*
|
|
2338
|
+
* Precondition (E12 decision 10): source must be resolved. If
|
|
2339
|
+
* source.isResolved() is false, throws NotResolvedError.
|
|
2340
|
+
*
|
|
2341
|
+
* On an already-resolved receiver, resolveWith is a no-op (returns an
|
|
2342
|
+
* equivalent resolved Config, source keys not included).
|
|
2343
|
+
*
|
|
2344
|
+
* E12 decisions 9, 10.
|
|
2345
|
+
*/
|
|
2346
|
+
resolveWith(source, opts = {}) {
|
|
2347
|
+
if (!source.isResolved()) {
|
|
2348
|
+
throw new NotResolvedError("source");
|
|
2349
|
+
}
|
|
2350
|
+
if (this._resolved) {
|
|
2351
|
+
return new _Config(this.root, {
|
|
2352
|
+
resolved: true,
|
|
2353
|
+
parseBaseDir: this._parseBaseDir,
|
|
2354
|
+
originDescription: this._originDescription
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
const receiverTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2358
|
+
const srcTree = hoconValueToResObj2(source.root);
|
|
2359
|
+
const merged = mergeUnresolved(receiverTree, srcTree);
|
|
2360
|
+
const resolveOpts = {
|
|
2361
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2362
|
+
throw new Error("no files");
|
|
2363
|
+
} },
|
|
2364
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2365
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2366
|
+
originDescription: this._originDescription
|
|
2367
|
+
};
|
|
2368
|
+
const resolved = resolveTree(merged, resolveOpts);
|
|
2369
|
+
if (resolved.kind !== "object") throw new Error("resolveWith: expected object root");
|
|
2370
|
+
const receiverShapeForFilter = resObjToKeyShape(receiverTree);
|
|
2371
|
+
const filtered = filterByReceiverShape(resolved, receiverShapeForFilter);
|
|
2372
|
+
if (opts.allowUnresolved) {
|
|
2373
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(filtered);
|
|
2374
|
+
return new _Config(stripped, {
|
|
2375
|
+
resolved: !hadPlaceholders,
|
|
2376
|
+
parseBaseDir: this._parseBaseDir,
|
|
2377
|
+
originDescription: this._originDescription,
|
|
2378
|
+
resObjRoot: hadPlaceholders ? receiverTree : void 0,
|
|
2379
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
return new _Config(filtered, {
|
|
2383
|
+
resolved: true,
|
|
2384
|
+
parseBaseDir: this._parseBaseDir,
|
|
2385
|
+
originDescription: this._originDescription
|
|
2386
|
+
});
|
|
1863
2387
|
}
|
|
1864
2388
|
toObject() {
|
|
1865
2389
|
return hoconToJs(this.root);
|
|
1866
2390
|
}
|
|
2391
|
+
/**
|
|
2392
|
+
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
2393
|
+
* JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
|
|
2394
|
+
* against Lightbend ground truth. NOT part of the public API.
|
|
2395
|
+
*
|
|
2396
|
+
* Throws if the Config is unresolved (contains placeholders).
|
|
2397
|
+
*/
|
|
2398
|
+
_renderJSONForTest() {
|
|
2399
|
+
return renderHoconAsJSON(this.root);
|
|
2400
|
+
}
|
|
1867
2401
|
lookupNode(path2) {
|
|
1868
2402
|
const segments = splitConfigPath(path2);
|
|
1869
2403
|
let current = this.root;
|
|
@@ -1875,9 +2409,40 @@ var Config = class _Config {
|
|
|
1875
2409
|
}
|
|
1876
2410
|
return current;
|
|
1877
2411
|
}
|
|
2412
|
+
/**
|
|
2413
|
+
* _resObjRootSubtreeHasPlaceholders — walk _resObjRoot to the given path and
|
|
2414
|
+
* return true if the subtree there contains any unresolved placeholder.
|
|
2415
|
+
* Used by getConfig / getList (T2) to detect transitive unresolved placeholders
|
|
2416
|
+
* in object / array subtrees that would otherwise appear to be "resolved" in the
|
|
2417
|
+
* partial HoconValue root (because individual leaf fields are omitted rather than
|
|
2418
|
+
* the whole subtree when their value is a direct placeholder scalar, but object
|
|
2419
|
+
* sub-containers are retained in the partial root with the placeholder-keyed
|
|
2420
|
+
* fields omitted from them — so the object appears "present but empty-ish").
|
|
2421
|
+
*/
|
|
2422
|
+
_resObjRootSubtreeHasPlaceholders(path2) {
|
|
2423
|
+
const tree = this._resObjRoot;
|
|
2424
|
+
if (!tree) return false;
|
|
2425
|
+
const segments = splitConfigPath(path2);
|
|
2426
|
+
let cur = tree;
|
|
2427
|
+
for (const seg of segments) {
|
|
2428
|
+
const val = cur.fields.get(seg);
|
|
2429
|
+
if (val === void 0) return false;
|
|
2430
|
+
if (isResObj(val)) {
|
|
2431
|
+
cur = val;
|
|
2432
|
+
} else {
|
|
2433
|
+
return valContainsPlaceholders(val);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
return containsPlaceholders(cur);
|
|
2437
|
+
}
|
|
1878
2438
|
requireScalar(path2) {
|
|
1879
2439
|
const v = this.lookupNode(path2);
|
|
1880
|
-
if (v === void 0)
|
|
2440
|
+
if (v === void 0) {
|
|
2441
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2442
|
+
throw new NotResolvedError(path2);
|
|
2443
|
+
}
|
|
2444
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2445
|
+
}
|
|
1881
2446
|
if (v.kind !== "scalar") throw new ConfigError(`expected scalar at ${path2}, got ${v.kind}`, path2);
|
|
1882
2447
|
return v;
|
|
1883
2448
|
}
|
|
@@ -1940,28 +2505,89 @@ function hoconToJs(v) {
|
|
|
1940
2505
|
case "array":
|
|
1941
2506
|
return v.items.map(hoconToJs);
|
|
1942
2507
|
case "object": {
|
|
1943
|
-
const obj =
|
|
2508
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1944
2509
|
for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
|
|
1945
|
-
return obj;
|
|
2510
|
+
return Object.assign({}, obj);
|
|
1946
2511
|
}
|
|
1947
2512
|
}
|
|
1948
2513
|
}
|
|
1949
|
-
function
|
|
1950
|
-
const
|
|
1951
|
-
for (const [k, v] of
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2514
|
+
function resObjToKeyShape(tree) {
|
|
2515
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2516
|
+
for (const [k, v] of tree.fields) {
|
|
2517
|
+
if (isSubst(v) || isConcat(v) || isAppend(v)) {
|
|
2518
|
+
fields.set(k, { kind: "scalar", raw: "", valueType: "null" });
|
|
2519
|
+
} else if (isResObj(v)) {
|
|
2520
|
+
fields.set(k, resObjToKeyShape(v));
|
|
1955
2521
|
} else {
|
|
1956
|
-
|
|
2522
|
+
fields.set(k, v);
|
|
1957
2523
|
}
|
|
1958
2524
|
}
|
|
1959
|
-
return { kind: "object", fields
|
|
2525
|
+
return { kind: "object", fields };
|
|
2526
|
+
}
|
|
2527
|
+
function stripPlaceholderFields(v) {
|
|
2528
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2529
|
+
let hadPlaceholders = false;
|
|
2530
|
+
for (const [k, val] of v.fields) {
|
|
2531
|
+
if (val._kind === "subst-placeholder" || val._kind === "concat-placeholder" || val._kind === "append-placeholder") {
|
|
2532
|
+
hadPlaceholders = true;
|
|
2533
|
+
} else if (val.kind === "object") {
|
|
2534
|
+
const inner = stripPlaceholderFields(val);
|
|
2535
|
+
if (inner.hadPlaceholders) hadPlaceholders = true;
|
|
2536
|
+
fields.set(k, inner.stripped);
|
|
2537
|
+
} else {
|
|
2538
|
+
fields.set(k, val);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
return { stripped: { kind: "object", fields }, hadPlaceholders };
|
|
2542
|
+
}
|
|
2543
|
+
function filterByReceiverShape(resolved, receiverShape) {
|
|
2544
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2545
|
+
for (const [k, rv] of resolved.fields) {
|
|
2546
|
+
if (!receiverShape.fields.has(k)) continue;
|
|
2547
|
+
const receiverVal = receiverShape.fields.get(k);
|
|
2548
|
+
if (rv.kind === "object" && receiverVal.kind === "object") {
|
|
2549
|
+
fields.set(k, filterByReceiverShape(rv, receiverVal));
|
|
2550
|
+
} else {
|
|
2551
|
+
fields.set(k, rv);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
return { kind: "object", fields };
|
|
2555
|
+
}
|
|
2556
|
+
function renderHoconAsJSON(v) {
|
|
2557
|
+
switch (v.kind) {
|
|
2558
|
+
case "scalar": {
|
|
2559
|
+
switch (v.valueType) {
|
|
2560
|
+
case "null":
|
|
2561
|
+
return "null";
|
|
2562
|
+
case "boolean":
|
|
2563
|
+
return v.raw;
|
|
2564
|
+
case "number": {
|
|
2565
|
+
const n = Number(v.raw);
|
|
2566
|
+
if (Number.isFinite(n)) return v.raw;
|
|
2567
|
+
return JSON.stringify(v.raw);
|
|
2568
|
+
}
|
|
2569
|
+
case "string":
|
|
2570
|
+
return JSON.stringify(v.raw);
|
|
2571
|
+
}
|
|
2572
|
+
break;
|
|
2573
|
+
}
|
|
2574
|
+
case "array": {
|
|
2575
|
+
const items = v.items.map(renderHoconAsJSON);
|
|
2576
|
+
return `[${items.join(",")}]`;
|
|
2577
|
+
}
|
|
2578
|
+
case "object": {
|
|
2579
|
+
const keys = [...v.fields.keys()].sort();
|
|
2580
|
+
const pairs = keys.map((k) => {
|
|
2581
|
+
const val = v.fields.get(k);
|
|
2582
|
+
return `${JSON.stringify(k)}:${renderHoconAsJSON(val)}`;
|
|
2583
|
+
});
|
|
2584
|
+
return `{${pairs.join(",")}}`;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
throw new Error(`renderHoconAsJSON: unsupported value kind (unresolved placeholder?)`);
|
|
1960
2588
|
}
|
|
1961
2589
|
|
|
1962
2590
|
// src/parse.ts
|
|
1963
|
-
var fs = __toESM(require("fs"), 1);
|
|
1964
|
-
var path = __toESM(require("path"), 1);
|
|
1965
2591
|
function getEnv(opts) {
|
|
1966
2592
|
if (opts.env !== void 0) return opts.env;
|
|
1967
2593
|
if (typeof process !== "undefined" && process.env) return process.env;
|
|
@@ -1978,7 +2604,10 @@ function buildResolveContext(input, opts) {
|
|
|
1978
2604
|
env: getEnv(opts),
|
|
1979
2605
|
baseDir: opts.baseDir,
|
|
1980
2606
|
readFileSync: opts.readFileSync ?? defaultReadFileSync,
|
|
1981
|
-
readFile: opts.readFile
|
|
2607
|
+
readFile: opts.readFile,
|
|
2608
|
+
originDescription: opts.originDescription,
|
|
2609
|
+
resolveFrom: opts.resolveFrom,
|
|
2610
|
+
packageResolver: opts.packageResolver
|
|
1982
2611
|
};
|
|
1983
2612
|
return { ast, resolveOpts };
|
|
1984
2613
|
}
|
|
@@ -1986,7 +2615,7 @@ function parse(input, opts = {}) {
|
|
|
1986
2615
|
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
1987
2616
|
const value = resolve2(ast, resolveOpts);
|
|
1988
2617
|
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
1989
|
-
return new Config(value);
|
|
2618
|
+
return new Config(value, { resolved: true, originDescription: opts.originDescription });
|
|
1990
2619
|
}
|
|
1991
2620
|
function parseFile(filePath, opts = {}) {
|
|
1992
2621
|
if (typeof process === "undefined" && !opts.readFileSync) {
|