@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.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
|
}
|
|
@@ -1176,17 +1454,18 @@ var IncludeLoader = class {
|
|
|
1176
1454
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1177
1455
|
}
|
|
1178
1456
|
const tokens = tokenize(content);
|
|
1179
|
-
|
|
1457
|
+
if (!hasContentTokens(tokens)) {
|
|
1458
|
+
return makeResObj();
|
|
1459
|
+
}
|
|
1180
1460
|
const ast = parseTokens(tokens);
|
|
1181
1461
|
return this.onBuildResObj(ast, {
|
|
1182
|
-
|
|
1462
|
+
...this.opts,
|
|
1183
1463
|
baseDir: nodePath.dirname(candidate),
|
|
1184
|
-
readFileSync: readFileSync2,
|
|
1185
1464
|
includeStack: [...includeStack, candidate]
|
|
1186
1465
|
});
|
|
1187
1466
|
}
|
|
1188
1467
|
async loadSingleAsync(candidate) {
|
|
1189
|
-
const { readFile, readFileSync: readFileSync2, includeStack = []
|
|
1468
|
+
const { readFile, readFileSync: readFileSync2, includeStack = [] } = this.opts;
|
|
1190
1469
|
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1191
1470
|
if (includeStack.includes(candidate)) {
|
|
1192
1471
|
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
@@ -1202,13 +1481,13 @@ var IncludeLoader = class {
|
|
|
1202
1481
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1203
1482
|
}
|
|
1204
1483
|
const tokens = tokenize(content);
|
|
1205
|
-
|
|
1484
|
+
if (!hasContentTokens(tokens)) {
|
|
1485
|
+
return makeResObj();
|
|
1486
|
+
}
|
|
1206
1487
|
const ast = parseTokens(tokens);
|
|
1207
1488
|
return this.onBuildResObjAsync(ast, {
|
|
1208
|
-
|
|
1489
|
+
...this.opts,
|
|
1209
1490
|
baseDir: nodePath.dirname(candidate),
|
|
1210
|
-
readFileSync: readFileSync2,
|
|
1211
|
-
readFile,
|
|
1212
1491
|
includeStack: [...includeStack, candidate]
|
|
1213
1492
|
});
|
|
1214
1493
|
}
|
|
@@ -1217,7 +1496,6 @@ var IncludeLoader = class {
|
|
|
1217
1496
|
// src/internal/resolver/structure-builder.ts
|
|
1218
1497
|
var StructureBuilder = class _StructureBuilder {
|
|
1219
1498
|
constructor(opts) {
|
|
1220
|
-
this.opts = opts;
|
|
1221
1499
|
this.loader = new IncludeLoader(opts);
|
|
1222
1500
|
this.loader.onBuildResObj = (a, o) => new _StructureBuilder(o).build(a);
|
|
1223
1501
|
this.loader.onBuildResObjAsync = async (a, o) => new _StructureBuilder(o).buildAsync(a);
|
|
@@ -1244,7 +1522,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1244
1522
|
}
|
|
1245
1523
|
applyField(obj, field, pathPrefix) {
|
|
1246
1524
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1247
|
-
const
|
|
1525
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1526
|
+
let included;
|
|
1527
|
+
if (qualifier.kind === "package") {
|
|
1528
|
+
included = this.loader.loadPackage(qualifier.identifier, path2, required);
|
|
1529
|
+
} else {
|
|
1530
|
+
included = this.loader.load(path2, required, qualifier.kind === "file");
|
|
1531
|
+
}
|
|
1248
1532
|
if (pathPrefix.length > 0) {
|
|
1249
1533
|
this.relativizeResObj(included, pathPrefix);
|
|
1250
1534
|
}
|
|
@@ -1286,7 +1570,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1286
1570
|
}
|
|
1287
1571
|
async applyFieldAsync(obj, field, pathPrefix) {
|
|
1288
1572
|
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1289
|
-
const
|
|
1573
|
+
const { qualifier, path: path2, required } = field.value;
|
|
1574
|
+
let included;
|
|
1575
|
+
if (qualifier.kind === "package") {
|
|
1576
|
+
included = await this.loader.loadPackageAsync(qualifier.identifier, path2, required);
|
|
1577
|
+
} else {
|
|
1578
|
+
included = await this.loader.loadAsync(path2, required, qualifier.kind === "file");
|
|
1579
|
+
}
|
|
1290
1580
|
if (pathPrefix.length > 0) {
|
|
1291
1581
|
this.relativizeResObj(included, pathPrefix);
|
|
1292
1582
|
}
|
|
@@ -1454,6 +1744,10 @@ var SubstitutionResolver = class {
|
|
|
1454
1744
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1455
1745
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
1456
1746
|
}
|
|
1747
|
+
/** Returns a prefix for error messages, including origin info when set. */
|
|
1748
|
+
originPrefix() {
|
|
1749
|
+
return this.opts.originDescription ? `${this.opts.originDescription}: ` : "";
|
|
1750
|
+
}
|
|
1457
1751
|
resolve() {
|
|
1458
1752
|
return this.resolveResObj(this.root);
|
|
1459
1753
|
}
|
|
@@ -1494,7 +1788,8 @@ var SubstitutionResolver = class {
|
|
|
1494
1788
|
if (isConcat(v)) {
|
|
1495
1789
|
this.resolvingConcats.add(v);
|
|
1496
1790
|
try {
|
|
1497
|
-
|
|
1791
|
+
const concatResult = this.resolveConcat(v.nodes, scope, v.line, v.col);
|
|
1792
|
+
return concatResult;
|
|
1498
1793
|
} finally {
|
|
1499
1794
|
this.resolvingConcats.delete(v);
|
|
1500
1795
|
}
|
|
@@ -1539,7 +1834,7 @@ var SubstitutionResolver = class {
|
|
|
1539
1834
|
}
|
|
1540
1835
|
if (s.optional) return void 0;
|
|
1541
1836
|
throw new ResolveError(
|
|
1542
|
-
`circular substitution: ${key}`,
|
|
1837
|
+
this.originPrefix() + `circular substitution: ${key}`,
|
|
1543
1838
|
key,
|
|
1544
1839
|
s.line,
|
|
1545
1840
|
s.col
|
|
@@ -1569,7 +1864,7 @@ var SubstitutionResolver = class {
|
|
|
1569
1864
|
}
|
|
1570
1865
|
if (s.optional) return void 0;
|
|
1571
1866
|
throw new ResolveError(
|
|
1572
|
-
|
|
1867
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1573
1868
|
key,
|
|
1574
1869
|
s.line,
|
|
1575
1870
|
s.col
|
|
@@ -1602,7 +1897,7 @@ var SubstitutionResolver = class {
|
|
|
1602
1897
|
if (result !== void 0) this.cache.set(key, result);
|
|
1603
1898
|
return result;
|
|
1604
1899
|
}
|
|
1605
|
-
if (s.listSuffix) {
|
|
1900
|
+
if (s.listSuffix && this.opts.useSystemEnvironment !== false) {
|
|
1606
1901
|
const result = this.resolveEnvList(s);
|
|
1607
1902
|
if (result !== void 0) {
|
|
1608
1903
|
this.cache.set(key, result);
|
|
@@ -1611,22 +1906,36 @@ var SubstitutionResolver = class {
|
|
|
1611
1906
|
if (s.optional) return void 0;
|
|
1612
1907
|
const envBase = s.segments.map((seg) => seg.text).join(".");
|
|
1613
1908
|
throw new ResolveError(
|
|
1614
|
-
|
|
1909
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}} (no environment variables ${envBase}_0, ${envBase}_1, \u2026 set)`,
|
|
1910
|
+
key,
|
|
1911
|
+
s.line,
|
|
1912
|
+
s.col
|
|
1913
|
+
);
|
|
1914
|
+
} else if (s.listSuffix) {
|
|
1915
|
+
if (s.optional) return void 0;
|
|
1916
|
+
if (this.opts.allowUnresolved) return s;
|
|
1917
|
+
throw new ResolveError(
|
|
1918
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1615
1919
|
key,
|
|
1616
1920
|
s.line,
|
|
1617
1921
|
s.col
|
|
1618
1922
|
);
|
|
1619
1923
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1924
|
+
if (this.opts.useSystemEnvironment !== false) {
|
|
1925
|
+
const envKey = s.segments.map((seg) => seg.text).join(".");
|
|
1926
|
+
const envVal = this.opts.env[envKey] ?? (s.prefixLen > 0 ? this.opts.env[s.segments.slice(s.prefixLen).map((seg) => seg.text).join(".")] : void 0);
|
|
1927
|
+
if (envVal !== void 0) {
|
|
1928
|
+
const result = { kind: "scalar", raw: envVal, valueType: "string" };
|
|
1929
|
+
this.cache.set(key, result);
|
|
1930
|
+
return result;
|
|
1931
|
+
}
|
|
1626
1932
|
}
|
|
1627
1933
|
if (s.optional) return void 0;
|
|
1934
|
+
if (this.opts.allowUnresolved) {
|
|
1935
|
+
return s;
|
|
1936
|
+
}
|
|
1628
1937
|
throw new ResolveError(
|
|
1629
|
-
|
|
1938
|
+
`${this.originPrefix()}could not resolve substitution: \${${key}}`,
|
|
1630
1939
|
key,
|
|
1631
1940
|
s.line,
|
|
1632
1941
|
s.col
|
|
@@ -1671,8 +1980,16 @@ var SubstitutionResolver = class {
|
|
|
1671
1980
|
return [full, bare];
|
|
1672
1981
|
}
|
|
1673
1982
|
resolveConcat(nodes, scope, line = 0, col = 0) {
|
|
1674
|
-
const
|
|
1675
|
-
if (
|
|
1983
|
+
const rawResolved = nodes.map((n) => this.resolveVal(n, scope));
|
|
1984
|
+
if (this.opts.allowUnresolved) {
|
|
1985
|
+
for (const v of rawResolved) {
|
|
1986
|
+
if (v !== void 0 && isSubst(v)) {
|
|
1987
|
+
return v;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
const resolved = rawResolved.filter((v) => v !== void 0);
|
|
1992
|
+
if (resolved.length === 0) return void 0;
|
|
1676
1993
|
if (resolved.length === 1) return resolved[0];
|
|
1677
1994
|
const nonSep = resolved.filter((v) => !separatorValues.has(v));
|
|
1678
1995
|
if (nonSep.length === 0) {
|
|
@@ -1746,15 +2063,89 @@ var SubstitutionResolver = class {
|
|
|
1746
2063
|
};
|
|
1747
2064
|
|
|
1748
2065
|
// src/internal/resolver/resolver.ts
|
|
2066
|
+
function buildTree(ast, opts) {
|
|
2067
|
+
return new StructureBuilder(opts).build(ast);
|
|
2068
|
+
}
|
|
2069
|
+
function resolveTree(tree, opts) {
|
|
2070
|
+
return new SubstitutionResolver(tree, opts).resolve();
|
|
2071
|
+
}
|
|
2072
|
+
function containsPlaceholders(tree) {
|
|
2073
|
+
for (const val of tree.fields.values()) {
|
|
2074
|
+
if (valContainsPlaceholders(val)) return true;
|
|
2075
|
+
}
|
|
2076
|
+
return false;
|
|
2077
|
+
}
|
|
2078
|
+
function valContainsPlaceholders(v) {
|
|
2079
|
+
const rv = v;
|
|
2080
|
+
if (isSubst(rv) || isConcat(rv) || isAppend(rv)) return true;
|
|
2081
|
+
if (isResObj(rv)) return containsPlaceholders(rv);
|
|
2082
|
+
const hv = v;
|
|
2083
|
+
if (hv?.kind === "array") {
|
|
2084
|
+
return hv.items.some((item) => valContainsPlaceholders(item));
|
|
2085
|
+
}
|
|
2086
|
+
return false;
|
|
2087
|
+
}
|
|
2088
|
+
function buildPartialHoconFromResObj(tree) {
|
|
2089
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2090
|
+
for (const [k, v] of tree.fields) {
|
|
2091
|
+
if (!isSubst(v) && !isConcat(v) && !isAppend(v)) {
|
|
2092
|
+
if (isResObj(v)) {
|
|
2093
|
+
fields.set(k, buildPartialHoconFromResObj(v));
|
|
2094
|
+
} else {
|
|
2095
|
+
if (!valContainsPlaceholders(v)) {
|
|
2096
|
+
fields.set(k, v);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
return { kind: "object", fields };
|
|
2102
|
+
}
|
|
2103
|
+
function hoconValueToResObj2(value) {
|
|
2104
|
+
const obj = makeResObj();
|
|
2105
|
+
for (const [k, v] of value.fields) {
|
|
2106
|
+
if (v.kind === "object") {
|
|
2107
|
+
obj.fields.set(k, hoconValueToResObj2(v));
|
|
2108
|
+
} else {
|
|
2109
|
+
obj.fields.set(k, v);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return obj;
|
|
2113
|
+
}
|
|
1749
2114
|
function resolve2(ast, opts) {
|
|
1750
|
-
const
|
|
1751
|
-
return
|
|
2115
|
+
const tree = buildTree(ast, opts);
|
|
2116
|
+
return resolveTree(tree, { ...opts, useSystemEnvironment: true, allowUnresolved: false });
|
|
1752
2117
|
}
|
|
1753
2118
|
|
|
1754
2119
|
// src/config.ts
|
|
1755
2120
|
var Config = class _Config {
|
|
1756
|
-
constructor(root) {
|
|
2121
|
+
constructor(root, opts) {
|
|
1757
2122
|
this.root = root;
|
|
2123
|
+
this._resolved = opts?.resolved ?? true;
|
|
2124
|
+
this._parseBaseDir = opts?.parseBaseDir;
|
|
2125
|
+
this._originDescription = opts?.originDescription;
|
|
2126
|
+
this._resObjRoot = opts?.resObjRoot;
|
|
2127
|
+
this._resolveOpts = opts?.resolveOpts;
|
|
2128
|
+
}
|
|
2129
|
+
/** Returns true when the Config's value tree contains no unresolved
|
|
2130
|
+
* substitution placeholders. Whole-config granularity (E12 decision 11). */
|
|
2131
|
+
isResolved() {
|
|
2132
|
+
return this._resolved;
|
|
2133
|
+
}
|
|
2134
|
+
/** @internal Test-only: construct a resolved Config from a HoconValue. */
|
|
2135
|
+
static _fromResolvedValue(root, opts) {
|
|
2136
|
+
return new _Config(root, { resolved: true, originDescription: opts?.originDescription });
|
|
2137
|
+
}
|
|
2138
|
+
/** @internal Construct an unresolved Config carrying a ResObj tree.
|
|
2139
|
+
* Used by parseStringWithOptions (deferred path) in T6. */
|
|
2140
|
+
static _fromUnresolvedResObj(tree, opts) {
|
|
2141
|
+
const partialRoot = buildPartialHoconFromResObj(tree);
|
|
2142
|
+
return new _Config(partialRoot, {
|
|
2143
|
+
resolved: opts.resolved,
|
|
2144
|
+
parseBaseDir: opts.parseBaseDir,
|
|
2145
|
+
originDescription: opts.originDescription,
|
|
2146
|
+
resObjRoot: tree,
|
|
2147
|
+
resolveOpts: opts.resolveOpts
|
|
2148
|
+
});
|
|
1758
2149
|
}
|
|
1759
2150
|
get(path2) {
|
|
1760
2151
|
const v = this.lookupNode(path2);
|
|
@@ -1798,13 +2189,29 @@ var Config = class _Config {
|
|
|
1798
2189
|
}
|
|
1799
2190
|
getConfig(path2) {
|
|
1800
2191
|
const v = this.lookupNode(path2);
|
|
1801
|
-
if (v === void 0)
|
|
2192
|
+
if (v === void 0) {
|
|
2193
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2194
|
+
throw new NotResolvedError(path2);
|
|
2195
|
+
}
|
|
2196
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2197
|
+
}
|
|
1802
2198
|
if (v.kind !== "object") throw new ConfigError(`expected object at ${path2}`, path2);
|
|
1803
|
-
|
|
2199
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2200
|
+
throw new NotResolvedError(path2);
|
|
2201
|
+
}
|
|
2202
|
+
return new _Config(v, { resolved: this._resolved });
|
|
1804
2203
|
}
|
|
1805
2204
|
getList(path2) {
|
|
1806
2205
|
const v = this.lookupNode(path2);
|
|
1807
|
-
if (v === void 0)
|
|
2206
|
+
if (v === void 0) {
|
|
2207
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2208
|
+
throw new NotResolvedError(path2);
|
|
2209
|
+
}
|
|
2210
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2211
|
+
}
|
|
2212
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2213
|
+
throw new NotResolvedError(path2);
|
|
2214
|
+
}
|
|
1808
2215
|
if (v.kind === "object") {
|
|
1809
2216
|
const converted = numericObjectToArray(v);
|
|
1810
2217
|
if (converted !== null) return converted.map(hoconToJs);
|
|
@@ -1819,12 +2226,138 @@ var Config = class _Config {
|
|
|
1819
2226
|
return [...this.root.fields.keys()];
|
|
1820
2227
|
}
|
|
1821
2228
|
withFallback(fallback) {
|
|
1822
|
-
|
|
1823
|
-
|
|
2229
|
+
if (fallback == null) return this;
|
|
2230
|
+
const selfTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2231
|
+
const fbTree = fallback._resObjRoot ?? hoconValueToResObj2(fallback.root);
|
|
2232
|
+
const merged = mergeUnresolved(selfTree, fbTree);
|
|
2233
|
+
const hasPlaceholders = containsPlaceholders(merged);
|
|
2234
|
+
const partialRoot = buildPartialHoconFromResObj(merged);
|
|
2235
|
+
return new _Config(partialRoot, {
|
|
2236
|
+
resolved: !hasPlaceholders,
|
|
2237
|
+
parseBaseDir: this._parseBaseDir,
|
|
2238
|
+
originDescription: this._originDescription,
|
|
2239
|
+
resObjRoot: merged,
|
|
2240
|
+
resolveOpts: this._resolveOpts ?? fallback._resolveOpts
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Runs substitution resolution (phase 2) on the stored unresolved tree.
|
|
2245
|
+
* Idempotent: calling resolve() on an already-resolved Config returns an
|
|
2246
|
+
* equivalent resolved Config.
|
|
2247
|
+
*
|
|
2248
|
+
* opts.allowUnresolved (default false) — when true, leaves unresolved
|
|
2249
|
+
* non-optional placeholders in place rather than throwing.
|
|
2250
|
+
* opts.useSystemEnvironment (default true) — when false, env var lookups
|
|
2251
|
+
* are suppressed (hermetic resolution).
|
|
2252
|
+
*
|
|
2253
|
+
* E12 decision 3.
|
|
2254
|
+
*/
|
|
2255
|
+
resolve(opts = {}) {
|
|
2256
|
+
if (this._resolved) {
|
|
2257
|
+
return new _Config(this.root, {
|
|
2258
|
+
resolved: true,
|
|
2259
|
+
parseBaseDir: this._parseBaseDir,
|
|
2260
|
+
originDescription: this._originDescription
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
const tree = this._resObjRoot;
|
|
2264
|
+
if (!tree) {
|
|
2265
|
+
return new _Config(this.root, { resolved: true });
|
|
2266
|
+
}
|
|
2267
|
+
const resolveOpts = {
|
|
2268
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2269
|
+
throw new Error("no files");
|
|
2270
|
+
} },
|
|
2271
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2272
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2273
|
+
originDescription: this._originDescription
|
|
2274
|
+
};
|
|
2275
|
+
const resolved = resolveTree(tree, resolveOpts);
|
|
2276
|
+
if (resolved.kind !== "object") throw new Error("resolve: expected object root");
|
|
2277
|
+
if (opts.allowUnresolved) {
|
|
2278
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(resolved);
|
|
2279
|
+
return new _Config(stripped, {
|
|
2280
|
+
resolved: !hadPlaceholders,
|
|
2281
|
+
parseBaseDir: this._parseBaseDir,
|
|
2282
|
+
originDescription: this._originDescription,
|
|
2283
|
+
resObjRoot: hadPlaceholders ? tree : void 0,
|
|
2284
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
return new _Config(resolved, {
|
|
2288
|
+
resolved: true,
|
|
2289
|
+
parseBaseDir: this._parseBaseDir,
|
|
2290
|
+
originDescription: this._originDescription
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Resolves receiver substitutions using source as lookup context.
|
|
2295
|
+
* Source's keys are NOT merged into the result — differs from
|
|
2296
|
+
* `this.withFallback(source).resolve()` which includes source keys.
|
|
2297
|
+
*
|
|
2298
|
+
* Precondition (E12 decision 10): source must be resolved. If
|
|
2299
|
+
* source.isResolved() is false, throws NotResolvedError.
|
|
2300
|
+
*
|
|
2301
|
+
* On an already-resolved receiver, resolveWith is a no-op (returns an
|
|
2302
|
+
* equivalent resolved Config, source keys not included).
|
|
2303
|
+
*
|
|
2304
|
+
* E12 decisions 9, 10.
|
|
2305
|
+
*/
|
|
2306
|
+
resolveWith(source, opts = {}) {
|
|
2307
|
+
if (!source.isResolved()) {
|
|
2308
|
+
throw new NotResolvedError("source");
|
|
2309
|
+
}
|
|
2310
|
+
if (this._resolved) {
|
|
2311
|
+
return new _Config(this.root, {
|
|
2312
|
+
resolved: true,
|
|
2313
|
+
parseBaseDir: this._parseBaseDir,
|
|
2314
|
+
originDescription: this._originDescription
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
const receiverTree = this._resObjRoot ?? hoconValueToResObj2(this.root);
|
|
2318
|
+
const srcTree = hoconValueToResObj2(source.root);
|
|
2319
|
+
const merged = mergeUnresolved(receiverTree, srcTree);
|
|
2320
|
+
const resolveOpts = {
|
|
2321
|
+
...this._resolveOpts ?? { env: {}, baseDir: void 0, readFileSync: () => {
|
|
2322
|
+
throw new Error("no files");
|
|
2323
|
+
} },
|
|
2324
|
+
allowUnresolved: opts.allowUnresolved ?? false,
|
|
2325
|
+
useSystemEnvironment: opts.useSystemEnvironment ?? true,
|
|
2326
|
+
originDescription: this._originDescription
|
|
2327
|
+
};
|
|
2328
|
+
const resolved = resolveTree(merged, resolveOpts);
|
|
2329
|
+
if (resolved.kind !== "object") throw new Error("resolveWith: expected object root");
|
|
2330
|
+
const receiverShapeForFilter = resObjToKeyShape(receiverTree);
|
|
2331
|
+
const filtered = filterByReceiverShape(resolved, receiverShapeForFilter);
|
|
2332
|
+
if (opts.allowUnresolved) {
|
|
2333
|
+
const { stripped, hadPlaceholders } = stripPlaceholderFields(filtered);
|
|
2334
|
+
return new _Config(stripped, {
|
|
2335
|
+
resolved: !hadPlaceholders,
|
|
2336
|
+
parseBaseDir: this._parseBaseDir,
|
|
2337
|
+
originDescription: this._originDescription,
|
|
2338
|
+
resObjRoot: hadPlaceholders ? receiverTree : void 0,
|
|
2339
|
+
resolveOpts: hadPlaceholders ? resolveOpts : void 0
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
return new _Config(filtered, {
|
|
2343
|
+
resolved: true,
|
|
2344
|
+
parseBaseDir: this._parseBaseDir,
|
|
2345
|
+
originDescription: this._originDescription
|
|
2346
|
+
});
|
|
1824
2347
|
}
|
|
1825
2348
|
toObject() {
|
|
1826
2349
|
return hoconToJs(this.root);
|
|
1827
2350
|
}
|
|
2351
|
+
/**
|
|
2352
|
+
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
2353
|
+
* JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
|
|
2354
|
+
* against Lightbend ground truth. NOT part of the public API.
|
|
2355
|
+
*
|
|
2356
|
+
* Throws if the Config is unresolved (contains placeholders).
|
|
2357
|
+
*/
|
|
2358
|
+
_renderJSONForTest() {
|
|
2359
|
+
return renderHoconAsJSON(this.root);
|
|
2360
|
+
}
|
|
1828
2361
|
lookupNode(path2) {
|
|
1829
2362
|
const segments = splitConfigPath(path2);
|
|
1830
2363
|
let current = this.root;
|
|
@@ -1836,9 +2369,40 @@ var Config = class _Config {
|
|
|
1836
2369
|
}
|
|
1837
2370
|
return current;
|
|
1838
2371
|
}
|
|
2372
|
+
/**
|
|
2373
|
+
* _resObjRootSubtreeHasPlaceholders — walk _resObjRoot to the given path and
|
|
2374
|
+
* return true if the subtree there contains any unresolved placeholder.
|
|
2375
|
+
* Used by getConfig / getList (T2) to detect transitive unresolved placeholders
|
|
2376
|
+
* in object / array subtrees that would otherwise appear to be "resolved" in the
|
|
2377
|
+
* partial HoconValue root (because individual leaf fields are omitted rather than
|
|
2378
|
+
* the whole subtree when their value is a direct placeholder scalar, but object
|
|
2379
|
+
* sub-containers are retained in the partial root with the placeholder-keyed
|
|
2380
|
+
* fields omitted from them — so the object appears "present but empty-ish").
|
|
2381
|
+
*/
|
|
2382
|
+
_resObjRootSubtreeHasPlaceholders(path2) {
|
|
2383
|
+
const tree = this._resObjRoot;
|
|
2384
|
+
if (!tree) return false;
|
|
2385
|
+
const segments = splitConfigPath(path2);
|
|
2386
|
+
let cur = tree;
|
|
2387
|
+
for (const seg of segments) {
|
|
2388
|
+
const val = cur.fields.get(seg);
|
|
2389
|
+
if (val === void 0) return false;
|
|
2390
|
+
if (isResObj(val)) {
|
|
2391
|
+
cur = val;
|
|
2392
|
+
} else {
|
|
2393
|
+
return valContainsPlaceholders(val);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
return containsPlaceholders(cur);
|
|
2397
|
+
}
|
|
1839
2398
|
requireScalar(path2) {
|
|
1840
2399
|
const v = this.lookupNode(path2);
|
|
1841
|
-
if (v === void 0)
|
|
2400
|
+
if (v === void 0) {
|
|
2401
|
+
if (!this._resolved && this._resObjRootSubtreeHasPlaceholders(path2)) {
|
|
2402
|
+
throw new NotResolvedError(path2);
|
|
2403
|
+
}
|
|
2404
|
+
throw new ConfigError(`path not found: ${path2}`, path2);
|
|
2405
|
+
}
|
|
1842
2406
|
if (v.kind !== "scalar") throw new ConfigError(`expected scalar at ${path2}, got ${v.kind}`, path2);
|
|
1843
2407
|
return v;
|
|
1844
2408
|
}
|
|
@@ -1901,28 +2465,89 @@ function hoconToJs(v) {
|
|
|
1901
2465
|
case "array":
|
|
1902
2466
|
return v.items.map(hoconToJs);
|
|
1903
2467
|
case "object": {
|
|
1904
|
-
const obj =
|
|
2468
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1905
2469
|
for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
|
|
1906
|
-
return obj;
|
|
2470
|
+
return Object.assign({}, obj);
|
|
1907
2471
|
}
|
|
1908
2472
|
}
|
|
1909
2473
|
}
|
|
1910
|
-
function
|
|
1911
|
-
const
|
|
1912
|
-
for (const [k, v] of
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2474
|
+
function resObjToKeyShape(tree) {
|
|
2475
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2476
|
+
for (const [k, v] of tree.fields) {
|
|
2477
|
+
if (isSubst(v) || isConcat(v) || isAppend(v)) {
|
|
2478
|
+
fields.set(k, { kind: "scalar", raw: "", valueType: "null" });
|
|
2479
|
+
} else if (isResObj(v)) {
|
|
2480
|
+
fields.set(k, resObjToKeyShape(v));
|
|
1916
2481
|
} else {
|
|
1917
|
-
|
|
2482
|
+
fields.set(k, v);
|
|
1918
2483
|
}
|
|
1919
2484
|
}
|
|
1920
|
-
return { kind: "object", fields
|
|
2485
|
+
return { kind: "object", fields };
|
|
2486
|
+
}
|
|
2487
|
+
function stripPlaceholderFields(v) {
|
|
2488
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2489
|
+
let hadPlaceholders = false;
|
|
2490
|
+
for (const [k, val] of v.fields) {
|
|
2491
|
+
if (val._kind === "subst-placeholder" || val._kind === "concat-placeholder" || val._kind === "append-placeholder") {
|
|
2492
|
+
hadPlaceholders = true;
|
|
2493
|
+
} else if (val.kind === "object") {
|
|
2494
|
+
const inner = stripPlaceholderFields(val);
|
|
2495
|
+
if (inner.hadPlaceholders) hadPlaceholders = true;
|
|
2496
|
+
fields.set(k, inner.stripped);
|
|
2497
|
+
} else {
|
|
2498
|
+
fields.set(k, val);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
return { stripped: { kind: "object", fields }, hadPlaceholders };
|
|
2502
|
+
}
|
|
2503
|
+
function filterByReceiverShape(resolved, receiverShape) {
|
|
2504
|
+
const fields = /* @__PURE__ */ new Map();
|
|
2505
|
+
for (const [k, rv] of resolved.fields) {
|
|
2506
|
+
if (!receiverShape.fields.has(k)) continue;
|
|
2507
|
+
const receiverVal = receiverShape.fields.get(k);
|
|
2508
|
+
if (rv.kind === "object" && receiverVal.kind === "object") {
|
|
2509
|
+
fields.set(k, filterByReceiverShape(rv, receiverVal));
|
|
2510
|
+
} else {
|
|
2511
|
+
fields.set(k, rv);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
return { kind: "object", fields };
|
|
2515
|
+
}
|
|
2516
|
+
function renderHoconAsJSON(v) {
|
|
2517
|
+
switch (v.kind) {
|
|
2518
|
+
case "scalar": {
|
|
2519
|
+
switch (v.valueType) {
|
|
2520
|
+
case "null":
|
|
2521
|
+
return "null";
|
|
2522
|
+
case "boolean":
|
|
2523
|
+
return v.raw;
|
|
2524
|
+
case "number": {
|
|
2525
|
+
const n = Number(v.raw);
|
|
2526
|
+
if (Number.isFinite(n)) return v.raw;
|
|
2527
|
+
return JSON.stringify(v.raw);
|
|
2528
|
+
}
|
|
2529
|
+
case "string":
|
|
2530
|
+
return JSON.stringify(v.raw);
|
|
2531
|
+
}
|
|
2532
|
+
break;
|
|
2533
|
+
}
|
|
2534
|
+
case "array": {
|
|
2535
|
+
const items = v.items.map(renderHoconAsJSON);
|
|
2536
|
+
return `[${items.join(",")}]`;
|
|
2537
|
+
}
|
|
2538
|
+
case "object": {
|
|
2539
|
+
const keys = [...v.fields.keys()].sort();
|
|
2540
|
+
const pairs = keys.map((k) => {
|
|
2541
|
+
const val = v.fields.get(k);
|
|
2542
|
+
return `${JSON.stringify(k)}:${renderHoconAsJSON(val)}`;
|
|
2543
|
+
});
|
|
2544
|
+
return `{${pairs.join(",")}}`;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
throw new Error(`renderHoconAsJSON: unsupported value kind (unresolved placeholder?)`);
|
|
1921
2548
|
}
|
|
1922
2549
|
|
|
1923
2550
|
// src/parse.ts
|
|
1924
|
-
import * as fs from "fs";
|
|
1925
|
-
import * as path from "path";
|
|
1926
2551
|
function getEnv(opts) {
|
|
1927
2552
|
if (opts.env !== void 0) return opts.env;
|
|
1928
2553
|
if (typeof process !== "undefined" && process.env) return process.env;
|
|
@@ -1939,7 +2564,10 @@ function buildResolveContext(input, opts) {
|
|
|
1939
2564
|
env: getEnv(opts),
|
|
1940
2565
|
baseDir: opts.baseDir,
|
|
1941
2566
|
readFileSync: opts.readFileSync ?? defaultReadFileSync,
|
|
1942
|
-
readFile: opts.readFile
|
|
2567
|
+
readFile: opts.readFile,
|
|
2568
|
+
originDescription: opts.originDescription,
|
|
2569
|
+
resolveFrom: opts.resolveFrom,
|
|
2570
|
+
packageResolver: opts.packageResolver
|
|
1943
2571
|
};
|
|
1944
2572
|
return { ast, resolveOpts };
|
|
1945
2573
|
}
|
|
@@ -1947,7 +2575,7 @@ function parse(input, opts = {}) {
|
|
|
1947
2575
|
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
1948
2576
|
const value = resolve2(ast, resolveOpts);
|
|
1949
2577
|
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
1950
|
-
return new Config(value);
|
|
2578
|
+
return new Config(value, { resolved: true, originDescription: opts.originDescription });
|
|
1951
2579
|
}
|
|
1952
2580
|
function parseFile(filePath, opts = {}) {
|
|
1953
2581
|
if (typeof process === "undefined" && !opts.readFileSync) {
|