jmd-format 0.2.1 → 0.2.2
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/package.json +1 -1
- package/src/parser.js +33 -33
- package/src/serializer.js +9 -12
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -127,10 +127,12 @@ export function createParser() {
|
|
|
127
127
|
return drain()
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
// Thematic break
|
|
131
|
-
//
|
|
130
|
+
// Thematic break `---` is pure decoration (§8.6): the level-pop (an
|
|
131
|
+
// anonymous heading) is the structural mechanism, so a `---` line is
|
|
132
|
+
// skipped. It consumes any pending blank, so a following `- ` item
|
|
133
|
+
// still joins the current array instead of triggering a root reset.
|
|
132
134
|
if (/^-{3,}$/.test(line)) {
|
|
133
|
-
|
|
135
|
+
blankPending = false
|
|
134
136
|
return drain()
|
|
135
137
|
}
|
|
136
138
|
|
|
@@ -153,34 +155,6 @@ export function createParser() {
|
|
|
153
155
|
if (stack[0] && stack[0].kind === 'array') closeItem(stack[0])
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
function onThematicBreak() {
|
|
157
|
-
blankPending = false
|
|
158
|
-
// A thematic break closes any sub-scope opened by the most-recent
|
|
159
|
-
// item, then signals the next item of the enclosing array (spec §8.6).
|
|
160
|
-
// We search outward for the innermost array whose last item opened a
|
|
161
|
-
// sub-structure and close down to it. If none qualifies, the break is
|
|
162
|
-
// a no-op — and that is correct, not lossy: a flat item opens no
|
|
163
|
-
// sub-scope, so the enclosing array is still current and the next
|
|
164
|
-
// `- ` item continues it. This is why a `---` after a flat item in a
|
|
165
|
-
// mixed array (canonical per the v0.3.4 §8.6 clarification) parses
|
|
166
|
-
// without dropping the following item.
|
|
167
|
-
let targetIdx = -1
|
|
168
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
169
|
-
const s = stack[i]
|
|
170
|
-
if (s.kind !== 'array') continue
|
|
171
|
-
const last = s.container[s.container.length - 1]
|
|
172
|
-
if (last && typeof last === 'object' && !Array.isArray(last)
|
|
173
|
-
&& Object.values(last).some(
|
|
174
|
-
v => v !== null && typeof v === 'object')) {
|
|
175
|
-
targetIdx = i
|
|
176
|
-
break
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (targetIdx === -1) return
|
|
180
|
-
while (stack.length - 1 > targetIdx) popScope()
|
|
181
|
-
closeItem(stack[targetIdx])
|
|
182
|
-
}
|
|
183
|
-
|
|
184
158
|
// --- Blockquote ----------------------------------------------------------
|
|
185
159
|
|
|
186
160
|
function startBlockquote(container, key) {
|
|
@@ -374,13 +348,25 @@ export function createParser() {
|
|
|
374
348
|
return
|
|
375
349
|
}
|
|
376
350
|
|
|
377
|
-
|
|
378
|
-
|
|
351
|
+
// Anonymous heading at depth D. If an array scope is open at exactly
|
|
352
|
+
// this depth, the heading is a level-pop (§8.6): return to that array
|
|
353
|
+
// — drop any deeper sub-scopes and close the record that opened them,
|
|
354
|
+
// so the next `- ` item joins THIS array. One marker pops arbitrary
|
|
355
|
+
// nesting in a single step. Otherwise it is a §3.2a anonymous object.
|
|
379
356
|
if (text === '' || text === undefined) {
|
|
357
|
+
const arr = arrayScopeAtDepth(depth)
|
|
358
|
+
if (arr) {
|
|
359
|
+
popToDepth(depth + 1)
|
|
360
|
+
closeItem(arr)
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
popToDepth(depth)
|
|
380
364
|
openObjectScope(depth, '')
|
|
381
365
|
return
|
|
382
366
|
}
|
|
383
367
|
|
|
368
|
+
popToDepth(depth)
|
|
369
|
+
|
|
384
370
|
// Anonymous sub-array: `### []` — handled below with the other array forms.
|
|
385
371
|
if (text === '[]') {
|
|
386
372
|
openSubArray(depth)
|
|
@@ -574,6 +560,20 @@ export function createParser() {
|
|
|
574
560
|
)
|
|
575
561
|
}
|
|
576
562
|
|
|
563
|
+
// Find an open array scope at exactly this depth, scanning from the top.
|
|
564
|
+
// Used to disambiguate an anonymous heading: an array at the heading's
|
|
565
|
+
// own depth means the heading is a level-pop (§8.6), not a §3.2a
|
|
566
|
+
// anonymous object. Returns null if the first scope at depth ≤ D is not
|
|
567
|
+
// such an array.
|
|
568
|
+
function arrayScopeAtDepth(depth) {
|
|
569
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
570
|
+
const s = stack[i]
|
|
571
|
+
if (s.depth > depth) continue
|
|
572
|
+
return (s.depth === depth && s.kind === 'array') ? s : null
|
|
573
|
+
}
|
|
574
|
+
return null
|
|
575
|
+
}
|
|
576
|
+
|
|
577
577
|
function parentContainerAndSeen(scope) {
|
|
578
578
|
if (scope.kind === 'object') {
|
|
579
579
|
return { container: scope.container, seen: scope.seen }
|
package/src/serializer.js
CHANGED
|
@@ -152,10 +152,8 @@ function writeArrayItems(lst, lines, depth) {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
if (allDicts) {
|
|
155
|
-
const hasNested = lst.some(item =>
|
|
156
|
-
Object.values(item).some(isNested))
|
|
157
155
|
for (let i = 0; i < lst.length; i++) {
|
|
158
|
-
writeDictItem(lst[i], lines, depth, i
|
|
156
|
+
writeDictItem(lst[i], lines, depth, i < lst.length - 1)
|
|
159
157
|
}
|
|
160
158
|
return
|
|
161
159
|
}
|
|
@@ -196,7 +194,7 @@ function writeArrayItems(lst, lines, depth) {
|
|
|
196
194
|
}
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
function writeDictItem(item, lines, depth,
|
|
197
|
+
function writeDictItem(item, lines, depth, moreFollow, qualifierPrefix = '') {
|
|
200
198
|
const scalarFields = []
|
|
201
199
|
const nestedFields = []
|
|
202
200
|
for (const [k, v] of Object.entries(item)) {
|
|
@@ -204,14 +202,6 @@ function writeDictItem(item, lines, depth, separatorNeeded, qualifierPrefix = ''
|
|
|
204
202
|
else scalarFields.push([k, v])
|
|
205
203
|
}
|
|
206
204
|
|
|
207
|
-
if (separatorNeeded) {
|
|
208
|
-
// Match the C-accelerated Python serializer (the default in jmd-format):
|
|
209
|
-
// blank line before the `---`, but the next `- ` follows immediately on
|
|
210
|
-
// the next line — no blank after the thematic break.
|
|
211
|
-
lines.push('')
|
|
212
|
-
lines.push('---')
|
|
213
|
-
}
|
|
214
|
-
|
|
215
205
|
if (scalarFields.length === 0) {
|
|
216
206
|
lines.push(qualifierPrefix + '-')
|
|
217
207
|
} else {
|
|
@@ -230,6 +220,13 @@ function writeDictItem(item, lines, depth, separatorNeeded, qualifierPrefix = ''
|
|
|
230
220
|
|
|
231
221
|
if (nestedFields.length > 0) {
|
|
232
222
|
writeObjectFields(Object.fromEntries(nestedFields), lines, depth)
|
|
223
|
+
// Level-pop (§8.6): this record opened a sub-structure (heading at
|
|
224
|
+
// depth+1). If more records follow, emit an anonymous heading at the
|
|
225
|
+
// array's own depth to pop back, so the next bare `-` item is read into
|
|
226
|
+
// THIS array. The last record needs no pop — end-of-scope closes it.
|
|
227
|
+
if (moreFollow) {
|
|
228
|
+
lines.push('#'.repeat(depth))
|
|
229
|
+
}
|
|
233
230
|
}
|
|
234
231
|
}
|
|
235
232
|
|