-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathflow-seq.ts
More file actions
149 lines (132 loc) · 4.71 KB
/
flow-seq.ts
File metadata and controls
149 lines (132 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import * as YAML from "yaml";
import * as YAML_CST from "../cst.js";
import { createFlowMappingItem } from "../factories/flow-mapping-item.js";
import { createFlowSequence } from "../factories/flow-sequence.js";
import { createFlowSequenceItem } from "../factories/flow-sequence-item.js";
import { createPosition } from "../factories/position.js";
import type { FlowSequence } from "../types.js";
import { extractComments } from "../utils/extract-comments.js";
import { getLast } from "../utils/get-last.js";
import type Context from "./context.js";
import { transformPair } from "./pair.js";
import type { TransformNodeProperties } from "./transform.js";
export function transformFlowSeq(
flowSeq: YAML.YAMLSeq.Parsed<
YAML.ParsedNode | YAML.Pair<YAML.ParsedNode, YAML.ParsedNode | null>
>,
context: Context,
props: TransformNodeProperties,
): FlowSequence {
const srcToken = flowSeq.srcToken;
// istanbul ignore if -- @preserve
if (!srcToken || srcToken.type !== "flow-collection") {
throw new Error("Expected flow-collection CST node for flow sequence");
}
const flowSequenceItems = flowSeq.items.map((item, index) => {
const srcItem = srcToken.items[index];
if (isBlockMappingOfImmediateChildOfFlowSequence(item, srcItem)) {
const pair = getLast(item.items)!;
return transformPair(pair, srcItem, context, createFlowMappingItem);
}
if (!YAML.isPair(item)) {
const propTokens: YAML_CST.ContentPropertyToken[] = [];
for (const token of YAML_CST.tokens(srcItem.start)) {
if (YAML_CST.maybeContentPropertyToken(token)) {
propTokens.push(token);
continue;
}
// istanbul ignore else -- @preserve
if (token.type === "comma") {
// skip
continue;
}
// istanbul ignore next -- @preserve
throw new Error(
`Unexpected token type in sequence item start: ${token.type}`,
);
}
const node = context.transformNode(item, { tokens: propTokens });
return createFlowSequenceItem(
createPosition(node.position.start, node.position.end),
node,
);
} else {
return transformPair(item, srcItem, context, createFlowMappingItem);
}
});
if (flowSeq.items.length < srcToken.items.length) {
// Handle extra comments
for (let i = flowSeq.items.length; i < srcToken.items.length; i++) {
const srcItem = srcToken.items[i];
for (const token of extractComments(srcItem.start, context)) {
// istanbul ignore else -- @preserve
if (token.type === "comma") {
// skip
continue;
}
// istanbul ignore next -- @preserve
throw new Error(
`Unexpected token type in collection item start: ${token.type}`,
);
}
}
}
let flowSeqEndToken: YAML_CST.FlowSeqEndSourceToken | null = null;
for (const token of YAML_CST.tokens(srcToken.end)) {
if (token.type === "comment") {
context.transformComment(token);
continue;
}
// istanbul ignore else -- @preserve
if (token.type === "flow-seq-end") {
flowSeqEndToken = token;
continue;
}
// istanbul ignore next -- @preserve
throw new Error(`Unexpected token type in flow seq end: ${token.type}`);
}
// istanbul ignore if -- @preserve
if (!flowSeqEndToken) {
throw new Error("Expected flow-seq-end token");
}
return createFlowSequence(
context.transformRange([
srcToken.start.offset,
flowSeqEndToken.offset + flowSeqEndToken.source.length,
]),
context.transformContentProperties(flowSeq, props.tokens),
flowSequenceItems,
);
}
/**
* Checks whether the given item is a block mapping that is an immediate child of a flow sequence.
* This is determined by checking if the item does not have a source token and contains exactly one item.
*
* e.g.
*
* ```yaml
* [ key: value ]
* ```
*/
function isBlockMappingOfImmediateChildOfFlowSequence(
item: YAML.ParsedNode | YAML.Pair<YAML.ParsedNode, YAML.ParsedNode | null>,
srcItem: YAML.CST.CollectionItem,
): item is YAML.YAMLMap.Parsed & {
items: [YAML.Pair<YAML.ParsedNode, YAML.ParsedNode | null>];
} {
if (item.srcToken) {
// If the item has a source token, it is not a block mapping immediate child of a flow sequence
// because it is associated with a source token indicating it is not an immediate child.
return false;
}
// istanbul ignore if -- @preserve
if (!YAML.isMap(item)) return false;
// istanbul ignore if -- @preserve
if (item.items.length !== 1) {
// If the block mapping does not contain exactly one item, it is not considered
// an immediate child of a flow sequence.
return false;
}
const child = item.items[0];
return child.srcToken === srcItem;
}