@pieceful/ravel-core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -3
- package/README.md +10 -4
- package/package.json +26 -7
- package/src/directive-syntax.js +354 -0
- package/src/index.d.ts +112 -4
- package/src/index.js +294 -25
- package/src/live.js +406 -0
package/src/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
const componentPattern = /^[a-z][a-z0-9-]*$/;
|
|
2
|
+
const chunkPathPattern = /^[a-z][a-z0-9-]*(?:\/(?:[a-z][a-z0-9-]*)?)*$/;
|
|
3
|
+
const relativeChunkPathPattern = /^(?:\.\.?)(?:\/(?:\.\.?|[a-z][a-z0-9-]*))*$/;
|
|
2
4
|
|
|
3
5
|
export { directiveKinds, compose, append, newline, pipe, pass, createDirective, aliasDirective } from "./directives.js";
|
|
6
|
+
export { parseRavelDirectiveBlock } from "./directive-syntax.js";
|
|
7
|
+
export {
|
|
8
|
+
cloneRavelValue,
|
|
9
|
+
executeLiveProgram,
|
|
10
|
+
planLiveExecutions,
|
|
11
|
+
ravelValueIssue,
|
|
12
|
+
serializeRavelValue
|
|
13
|
+
} from "./live.js";
|
|
4
14
|
|
|
5
15
|
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
6
16
|
|
|
@@ -198,6 +208,7 @@ const diagnostic = (code, message, source, related = []) => ({
|
|
|
198
208
|
});
|
|
199
209
|
|
|
200
210
|
const validComponent = (value) => value === null || (typeof value === "string" && componentPattern.test(value));
|
|
211
|
+
const validChunkPath = (value) => value === null || (typeof value === "string" && chunkPathPattern.test(value));
|
|
201
212
|
|
|
202
213
|
export const formatChunkId = (identity) => {
|
|
203
214
|
const prefix = identity.document === null ? "" : identity.document + "::";
|
|
@@ -223,7 +234,8 @@ export const parseChunkId = (input, { reference = false } = {}) => {
|
|
|
223
234
|
|
|
224
235
|
let type = null;
|
|
225
236
|
const typeIndex = remainder.lastIndexOf(".");
|
|
226
|
-
|
|
237
|
+
const pathIndex = remainder.lastIndexOf("/");
|
|
238
|
+
if (typeIndex > pathIndex) {
|
|
227
239
|
type = remainder.slice(typeIndex + 1);
|
|
228
240
|
remainder = remainder.slice(0, typeIndex);
|
|
229
241
|
if (!componentPattern.test(type)) return null;
|
|
@@ -236,11 +248,16 @@ export const parseChunkId = (input, { reference = false } = {}) => {
|
|
|
236
248
|
chunk = remainder.slice(0, minorIndex);
|
|
237
249
|
minor = remainder.slice(minorIndex + 1);
|
|
238
250
|
if (!componentPattern.test(minor)) return null;
|
|
251
|
+
if (chunk.endsWith("/") && /^\.{1,2}(?:\/|$)/.test(chunk)) chunk = chunk.slice(0, -1);
|
|
239
252
|
}
|
|
240
253
|
|
|
241
254
|
if (chunk === "") chunk = null;
|
|
242
|
-
|
|
255
|
+
const relativePath = reference && typeof chunk === "string" && relativeChunkPathPattern.test(chunk)
|
|
256
|
+
? chunk
|
|
257
|
+
: null;
|
|
258
|
+
if (relativePath === null && !validChunkPath(chunk)) return null;
|
|
243
259
|
if (!validComponent(document) || !validComponent(minor) || !validComponent(type)) return null;
|
|
260
|
+
if (relativePath !== null && explicitDocument) return null;
|
|
244
261
|
|
|
245
262
|
if (!reference && document === null && chunk === null) return null;
|
|
246
263
|
if (!reference && document === null && chunk !== null && input.includes("::")) return null;
|
|
@@ -253,7 +270,14 @@ export const parseChunkId = (input, { reference = false } = {}) => {
|
|
|
253
270
|
}
|
|
254
271
|
if (reference && !explicitDocument && chunk === null && minor === null && type === null) return null;
|
|
255
272
|
|
|
256
|
-
return {
|
|
273
|
+
return {
|
|
274
|
+
document,
|
|
275
|
+
chunk,
|
|
276
|
+
minor,
|
|
277
|
+
type,
|
|
278
|
+
explicitDocument,
|
|
279
|
+
...(relativePath === null ? {} : { relativePath })
|
|
280
|
+
};
|
|
257
281
|
};
|
|
258
282
|
|
|
259
283
|
const validateIdentity = (identity) => {
|
|
@@ -267,7 +291,7 @@ const validateIdentity = (identity) => {
|
|
|
267
291
|
type: identity.type,
|
|
268
292
|
explicitDocument: identity.document !== null
|
|
269
293
|
};
|
|
270
|
-
if (!validComponent(result.document) || !
|
|
294
|
+
if (!validComponent(result.document) || !validChunkPath(result.chunk) ||
|
|
271
295
|
!validComponent(result.minor) || !validComponent(result.type)) {
|
|
272
296
|
return null;
|
|
273
297
|
}
|
|
@@ -483,6 +507,27 @@ const parseExpression = (expression, source, expressionOffset, diagnostics, { al
|
|
|
483
507
|
};
|
|
484
508
|
};
|
|
485
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Parse a definition-time transform pipeline without executing it.
|
|
512
|
+
* Adapters use this entry point so every markup dialect shares one grammar.
|
|
513
|
+
*/
|
|
514
|
+
export const parseDefinitionPipeline = (text, source) => {
|
|
515
|
+
const diagnostics = [];
|
|
516
|
+
if (typeof text !== "string" || !text.trim()) return { pipeline: [], diagnostics };
|
|
517
|
+
const expression = text.trim();
|
|
518
|
+
const pipeline = [];
|
|
519
|
+
for (const part of splitTopLevel(expression, "|")) {
|
|
520
|
+
const step = parsePipeStep(part, expression, source, 0, diagnostics);
|
|
521
|
+
if (!step) continue;
|
|
522
|
+
if (step.type !== "transform") {
|
|
523
|
+
diagnostics.push(diagnostic("RV120", "Definition pipelines accept transform calls only.", step.source ?? source));
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
pipeline.push({ name: step.name, arguments: step.arguments });
|
|
527
|
+
}
|
|
528
|
+
return { pipeline, diagnostics };
|
|
529
|
+
};
|
|
530
|
+
|
|
486
531
|
// An embedded reference inherits the indentation of its containing source line
|
|
487
532
|
// on continuation lines. The first line is already positioned by the literal
|
|
488
533
|
// text before the reference, so it deliberately remains untouched.
|
|
@@ -513,18 +558,103 @@ const applyContinuationIndentMapped = (value, indentation, source, chunk) => {
|
|
|
513
558
|
return concatMapped(...parts);
|
|
514
559
|
};
|
|
515
560
|
|
|
516
|
-
|
|
561
|
+
const firstUnescapedPipe = (value) => {
|
|
562
|
+
let escaped = false;
|
|
563
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
564
|
+
if (escaped) escaped = false;
|
|
565
|
+
else if (value[index] === "\\") escaped = true;
|
|
566
|
+
else if (value[index] === "|") return index;
|
|
567
|
+
}
|
|
568
|
+
return -1;
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const nowebExpression = (value, options) => {
|
|
572
|
+
const pipe = options.nowebPlus ? firstUnescapedPipe(value) : -1;
|
|
573
|
+
const authoredName = (pipe === -1 ? value : value.slice(0, pipe)).trim().replace(/\\\|/g, "|");
|
|
574
|
+
const canonicalName = options.referenceAliases?.[authoredName] ?? authoredName;
|
|
575
|
+
return pipe === -1
|
|
576
|
+
? canonicalName
|
|
577
|
+
: canonicalName + " | " + value.slice(pipe + 1).trim();
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
export const parseChunk = (body, source, options = {}) => {
|
|
517
581
|
const nodes = [];
|
|
518
582
|
const diagnostics = [];
|
|
519
583
|
let literalStart = 0;
|
|
520
584
|
let index = 0;
|
|
521
585
|
|
|
522
586
|
while (index < body.length) {
|
|
587
|
+
if (options.nowebReferences && body.startsWith("<<", index)) {
|
|
588
|
+
const end = body.indexOf(">>", index + 2);
|
|
589
|
+
if (end === -1) {
|
|
590
|
+
diagnostics.push(diagnostic("RV110", "Unterminated noweb chunk reference.", span(source, body, index, body.length)));
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
if (literalStart < index) {
|
|
594
|
+
nodes.push({ type: "literal", value: body.slice(literalStart, index), source: span(source, body, literalStart, index) });
|
|
595
|
+
}
|
|
596
|
+
const authored = body.slice(index + 2, end);
|
|
597
|
+
const expression = nowebExpression(authored, options);
|
|
598
|
+
const parsed = parseExpression(expression, source, index + 2, diagnostics);
|
|
599
|
+
if (parsed) {
|
|
600
|
+
nodes.push({
|
|
601
|
+
...parsed,
|
|
602
|
+
authoredReference: authored,
|
|
603
|
+
source: span(source, body, index + 2, end),
|
|
604
|
+
continuationIndent: continuationIndentAt(body, index)
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
index = end + 2;
|
|
608
|
+
literalStart = index;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
const counted = options.underscoreReferences !== false && body[index] === "\\"
|
|
612
|
+
? /^\\([1-9][0-9]*)_(["'`])/.exec(body.slice(index))
|
|
613
|
+
: null;
|
|
614
|
+
if (counted) {
|
|
615
|
+
const quote = counted[2];
|
|
616
|
+
const expressionStart = index + counted[0].length;
|
|
617
|
+
let end = expressionStart;
|
|
618
|
+
let escaped = false;
|
|
619
|
+
while (end < body.length) {
|
|
620
|
+
if (escaped) escaped = false;
|
|
621
|
+
else if (body[end] === "\\") escaped = true;
|
|
622
|
+
else if (body[end] === quote) break;
|
|
623
|
+
end += 1;
|
|
624
|
+
}
|
|
625
|
+
if (end >= body.length) {
|
|
626
|
+
diagnostics.push(diagnostic("RV110", "Unterminated counted chunk reference.", span(source, body, index, body.length)));
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
if (literalStart < index) {
|
|
630
|
+
nodes.push({ type: "literal", value: body.slice(literalStart, index), source: span(source, body, literalStart, index) });
|
|
631
|
+
}
|
|
632
|
+
const expression = body.slice(expressionStart, end);
|
|
633
|
+
const parsed = parseExpression(expression, source, expressionStart, diagnostics, { allowDelay: false });
|
|
634
|
+
if (parsed) {
|
|
635
|
+
nodes.push({
|
|
636
|
+
type: "delay",
|
|
637
|
+
value: {
|
|
638
|
+
kind: "ravel-command-argument",
|
|
639
|
+
command: { type: "chunk", expression, value: parsed, source: span(source, body, expressionStart, end) }
|
|
640
|
+
},
|
|
641
|
+
expression,
|
|
642
|
+
phase: Number(counted[1]),
|
|
643
|
+
safeSymbol: undefined,
|
|
644
|
+
source: span(source, body, index, end + 1),
|
|
645
|
+
continuationIndent: continuationIndentAt(body, index)
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
index = end + 1;
|
|
649
|
+
literalStart = index;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
523
652
|
if (body[index] === "\\" && body[index + 1] === "_" && ["\"", "'", "`"].includes(body[index + 2])) {
|
|
524
653
|
index += 3;
|
|
525
654
|
continue;
|
|
526
655
|
}
|
|
527
|
-
if (
|
|
656
|
+
if (options.underscoreReferences === false ||
|
|
657
|
+
body[index] !== "_" || !["\"", "'", "`"].includes(body[index + 1])) {
|
|
528
658
|
index += 1;
|
|
529
659
|
continue;
|
|
530
660
|
}
|
|
@@ -565,8 +695,16 @@ export const parseChunk = (body, source) => {
|
|
|
565
695
|
};
|
|
566
696
|
|
|
567
697
|
const parseChunkFragments = (raw) => {
|
|
568
|
-
const
|
|
569
|
-
|
|
698
|
+
const referenceSyntax = raw.metadata?.data?.ravel?.referenceSyntax;
|
|
699
|
+
const parsed = parseChunk(raw.body, raw.source, referenceSyntax && typeof referenceSyntax === "object"
|
|
700
|
+
? {
|
|
701
|
+
nowebReferences: referenceSyntax.noweb === true,
|
|
702
|
+
nowebPlus: referenceSyntax.dialect === "noweb-plus",
|
|
703
|
+
underscoreReferences: referenceSyntax.underscore !== false,
|
|
704
|
+
referenceAliases: referenceSyntax.aliases
|
|
705
|
+
}
|
|
706
|
+
: {});
|
|
707
|
+
if (!Array.isArray(raw.fragments) || raw.fragments.length === 0 ||
|
|
570
708
|
raw.fragments.some((fragment) => typeof fragment?.body !== "string" || !fragment.source) ||
|
|
571
709
|
raw.fragments.map((fragment) => fragment.body).join("") !== raw.body) {
|
|
572
710
|
return parsed;
|
|
@@ -583,16 +721,41 @@ const parseChunkFragments = (raw) => {
|
|
|
583
721
|
start: source.range.start.offset - syntheticStart,
|
|
584
722
|
end: source.range.end.offset - syntheticStart
|
|
585
723
|
});
|
|
724
|
+
const fragmentSlice = (fragment, start, end) =>
|
|
725
|
+
fragment.precision === "coarse"
|
|
726
|
+
? fragment.source
|
|
727
|
+
: span(
|
|
728
|
+
fragment.source,
|
|
729
|
+
fragment.body,
|
|
730
|
+
start - fragment.start,
|
|
731
|
+
end - fragment.start
|
|
732
|
+
);
|
|
586
733
|
const remapLocation = (source) => {
|
|
587
734
|
const range = relativeRange(source);
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
735
|
+
const overlapping = fragments.filter((entry) =>
|
|
736
|
+
range.start < entry.end && range.end > entry.start
|
|
737
|
+
);
|
|
738
|
+
if (!overlapping.length) return source;
|
|
739
|
+
const first = overlapping[0];
|
|
740
|
+
const last = overlapping.at(-1);
|
|
741
|
+
const firstSlice = fragmentSlice(
|
|
742
|
+
first,
|
|
743
|
+
Math.max(range.start, first.start),
|
|
744
|
+
Math.min(range.end, first.end)
|
|
745
|
+
);
|
|
746
|
+
const lastSlice = fragmentSlice(
|
|
747
|
+
last,
|
|
748
|
+
Math.max(range.start, last.start),
|
|
749
|
+
Math.min(range.end, last.end)
|
|
595
750
|
);
|
|
751
|
+
if (firstSlice.uri !== lastSlice.uri) return firstSlice;
|
|
752
|
+
return {
|
|
753
|
+
uri: firstSlice.uri,
|
|
754
|
+
range: {
|
|
755
|
+
start: firstSlice.range.start,
|
|
756
|
+
end: lastSlice.range.end
|
|
757
|
+
}
|
|
758
|
+
};
|
|
596
759
|
};
|
|
597
760
|
const remapSources = (value) => {
|
|
598
761
|
if (!value || typeof value !== "object") return value;
|
|
@@ -617,7 +780,8 @@ const parseChunkFragments = (raw) => {
|
|
|
617
780
|
nodes.push({
|
|
618
781
|
type: "literal",
|
|
619
782
|
value: raw.body.slice(start, end),
|
|
620
|
-
source:
|
|
783
|
+
source: fragmentSlice(fragment, start, end),
|
|
784
|
+
precision: fragment.precision ?? "exact"
|
|
621
785
|
});
|
|
622
786
|
}
|
|
623
787
|
}
|
|
@@ -709,6 +873,30 @@ const applyTransform = (value, step, diagnostics, transforms, context = {}) => {
|
|
|
709
873
|
if (step.name === "quote-reference") {
|
|
710
874
|
return value.replace(/_(["'\x60])/g, "\\_$1");
|
|
711
875
|
}
|
|
876
|
+
if (step.name === "jsontext") {
|
|
877
|
+
if (step.arguments.length > 1 ||
|
|
878
|
+
(step.arguments.length === 1 && typeof step.arguments[0] !== "string")) {
|
|
879
|
+
diagnostics.push(diagnostic("RV120", "jsontext accepts no arguments or one string key.", step.source));
|
|
880
|
+
return value;
|
|
881
|
+
}
|
|
882
|
+
let parsed;
|
|
883
|
+
try {
|
|
884
|
+
parsed = JSON.parse(value);
|
|
885
|
+
} catch {
|
|
886
|
+
diagnostics.push(diagnostic("RV120", "jsontext requires a live-result JSON value.", step.source));
|
|
887
|
+
return value;
|
|
888
|
+
}
|
|
889
|
+
if (step.arguments.length === 0) return JSON.stringify(parsed);
|
|
890
|
+
|
|
891
|
+
const key = step.arguments[0];
|
|
892
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) ||
|
|
893
|
+
!Object.hasOwn(parsed, key)) {
|
|
894
|
+
diagnostics.push(diagnostic("RV120", "jsontext could not find object key " + JSON.stringify(key) + ".", step.source));
|
|
895
|
+
return value;
|
|
896
|
+
}
|
|
897
|
+
const selected = parsed[key];
|
|
898
|
+
return typeof selected === "string" ? selected : JSON.stringify(selected);
|
|
899
|
+
}
|
|
712
900
|
|
|
713
901
|
const transform = customTransform(transforms, step.name);
|
|
714
902
|
if (typeof transform === "function") {
|
|
@@ -797,9 +985,22 @@ const definitionFromComposeEmission = (owner, capture, emit) => {
|
|
|
797
985
|
};
|
|
798
986
|
|
|
799
987
|
const resolveTarget = (target, owner, definitions) => {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
988
|
+
let withOwnerChunk;
|
|
989
|
+
if (target.relativePath) {
|
|
990
|
+
const segments = (owner.identity.chunk ?? "").split("/").filter((segment, index, entries) =>
|
|
991
|
+
segment || (index > 0 && index < entries.length - 1)
|
|
992
|
+
);
|
|
993
|
+
for (const segment of target.relativePath.split("/")) {
|
|
994
|
+
if (segment === ".") continue;
|
|
995
|
+
if (segment === "..") segments.pop();
|
|
996
|
+
else segments.push(segment);
|
|
997
|
+
}
|
|
998
|
+
withOwnerChunk = segments.join("/");
|
|
999
|
+
} else {
|
|
1000
|
+
withOwnerChunk = target.chunk === null && !target.explicitDocument
|
|
1001
|
+
? owner.identity.chunk
|
|
1002
|
+
: target.chunk;
|
|
1003
|
+
}
|
|
803
1004
|
const parts = {
|
|
804
1005
|
chunk: withOwnerChunk,
|
|
805
1006
|
minor: target.minor,
|
|
@@ -829,6 +1030,11 @@ export const transformGraph = (pretransform, options = {}) => {
|
|
|
829
1030
|
const delayTokenCorpus = JSON.stringify(pretransform);
|
|
830
1031
|
const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
831
1032
|
const orderedEntries = (value) => Object.entries(value).sort(([left], [right]) => compareText(left, right));
|
|
1033
|
+
const liveExecutions = options.liveResults?.executions ?? options.liveResults;
|
|
1034
|
+
const liveExecution = (id) => liveExecutions instanceof Map
|
|
1035
|
+
? liveExecutions.get(id)
|
|
1036
|
+
: liveExecutions?.[id];
|
|
1037
|
+
const isExecutable = (definition) => definition?.metadata?.data?.ravel?.run === true;
|
|
832
1038
|
|
|
833
1039
|
const normalizeDefinitionPipeline = (steps, source) => {
|
|
834
1040
|
if (steps === undefined) return [];
|
|
@@ -1067,10 +1273,47 @@ export const transformGraph = (pretransform, options = {}) => {
|
|
|
1067
1273
|
to: resolved,
|
|
1068
1274
|
source: clone(node.source)
|
|
1069
1275
|
};
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1276
|
+
const resolvedDefinition = definitions.get(resolved);
|
|
1277
|
+
const ownerIsExecutable = isExecutable(owner.definition);
|
|
1278
|
+
const shouldUseLiveResult = isExecutable(resolvedDefinition) && !ownerIsExecutable;
|
|
1279
|
+
if (shouldUseLiveResult && options.deferLiveResults && !liveExecutions) {
|
|
1280
|
+
return mappedValue();
|
|
1281
|
+
}
|
|
1282
|
+
let value;
|
|
1283
|
+
if (shouldUseLiveResult && liveExecutions) {
|
|
1284
|
+
const execution = liveExecution(resolved);
|
|
1285
|
+
if (execution?.status !== "succeeded") {
|
|
1286
|
+
diagnostics.push(diagnostic("RV140", "Live result is unavailable for reference " + node.reference + ".", node.source));
|
|
1287
|
+
return mappedValue();
|
|
1288
|
+
}
|
|
1289
|
+
const first = node.pipeline[0];
|
|
1290
|
+
const hasJsonTextBoundary = first?.type === "transform" && first.name === "jsontext";
|
|
1291
|
+
const serializeWholeValue = hasJsonTextBoundary && first.arguments.length === 0;
|
|
1292
|
+
if (typeof execution.value === "string" && !serializeWholeValue) {
|
|
1293
|
+
value = coarseMapped(execution.value, resolvedDefinition.source, resolved, "live-result", [reference]);
|
|
1294
|
+
} else if (hasJsonTextBoundary) {
|
|
1295
|
+
value = coarseMapped(
|
|
1296
|
+
execution.serialized ?? JSON.stringify(execution.value),
|
|
1297
|
+
resolvedDefinition.source,
|
|
1298
|
+
resolved,
|
|
1299
|
+
"live-result",
|
|
1300
|
+
[reference]
|
|
1301
|
+
);
|
|
1302
|
+
} else {
|
|
1303
|
+
diagnostics.push(diagnostic(
|
|
1304
|
+
"RV140",
|
|
1305
|
+
"Non-string live result " + resolved +
|
|
1306
|
+
" requires jsontext() or jsontext(\"key\") before ordinary Ravel processing.",
|
|
1307
|
+
node.source
|
|
1308
|
+
));
|
|
1309
|
+
return mappedValue();
|
|
1310
|
+
}
|
|
1311
|
+
} else {
|
|
1312
|
+
value = mappedValue(dependency.value, dependency.segments.map((segment) => ({
|
|
1313
|
+
...clone(segment),
|
|
1314
|
+
via: [...(segment.via ?? []), reference]
|
|
1315
|
+
})));
|
|
1316
|
+
}
|
|
1074
1317
|
return evaluatePipeline(value, node.pipeline, owner);
|
|
1075
1318
|
};
|
|
1076
1319
|
|
|
@@ -1136,7 +1379,13 @@ export const transformGraph = (pretransform, options = {}) => {
|
|
|
1136
1379
|
|
|
1137
1380
|
const evaluateNode = (node, owner) => {
|
|
1138
1381
|
if (node.type === "literal") {
|
|
1139
|
-
return sourceSegment(
|
|
1382
|
+
return sourceSegment(
|
|
1383
|
+
node.value,
|
|
1384
|
+
node.source,
|
|
1385
|
+
owner.definition.id,
|
|
1386
|
+
"literal",
|
|
1387
|
+
node.precision ?? "exact"
|
|
1388
|
+
);
|
|
1140
1389
|
}
|
|
1141
1390
|
if (node.type === "delay") return evaluateDelay(node, owner);
|
|
1142
1391
|
const value = evaluateExpression(node, owner);
|
|
@@ -1295,6 +1544,7 @@ export const transformGraph = (pretransform, options = {}) => {
|
|
|
1295
1544
|
value: value.text,
|
|
1296
1545
|
segments: value.segments,
|
|
1297
1546
|
metadata: definition.metadata,
|
|
1547
|
+
source: definition.source,
|
|
1298
1548
|
dependencies: [...owner.dependencies].sort(),
|
|
1299
1549
|
// References retain authored source order, which is deterministic and is
|
|
1300
1550
|
// more useful for explaining a chunk than lexical target order.
|
|
@@ -1327,7 +1577,26 @@ export const transformGraph = (pretransform, options = {}) => {
|
|
|
1327
1577
|
diagnostics.push(diagnostic("RV130", "out requires a fully qualified existing source chunk ID.", directive.source));
|
|
1328
1578
|
continue;
|
|
1329
1579
|
}
|
|
1330
|
-
|
|
1580
|
+
let chunk = evaluate(id, directive.source);
|
|
1581
|
+
const definition = definitions.get(id);
|
|
1582
|
+
if (isExecutable(definition) && liveExecutions) {
|
|
1583
|
+
const execution = liveExecution(id);
|
|
1584
|
+
if (execution?.status !== "succeeded") {
|
|
1585
|
+
diagnostics.push(diagnostic("RV140", "Live result is unavailable for output " + name + ".", directive.source));
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
if (typeof execution.value !== "string") {
|
|
1589
|
+
diagnostics.push(diagnostic(
|
|
1590
|
+
"RV140",
|
|
1591
|
+
"Output " + name +
|
|
1592
|
+
" requires a live string; use jsontext() or jsontext(\"key\") in a derived chunk.",
|
|
1593
|
+
directive.source
|
|
1594
|
+
));
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
const mapped = coarseMapped(execution.value, definition.source, id, "live-result");
|
|
1598
|
+
chunk = { ...chunk, value: mapped.text, segments: mapped.segments };
|
|
1599
|
+
}
|
|
1331
1600
|
if (deliverables[name]) {
|
|
1332
1601
|
diagnostics.push(diagnostic("RV101", "Duplicate out deliverable: " + name, directive.source));
|
|
1333
1602
|
continue;
|