jmd-format 0.2.0 → 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 -29
- 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,30 +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 is consumed by the innermost enclosing array whose
|
|
159
|
-
// most-recent item is a dict containing nested structures — this is
|
|
160
|
-
// the only context where jmd-format emits the break, and the parser
|
|
161
|
-
// mirrors that rule (spec §8.6). Inner scopes are closed; if no array
|
|
162
|
-
// on the stack qualifies, the line is tolerated as decoration.
|
|
163
|
-
let targetIdx = -1
|
|
164
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
165
|
-
const s = stack[i]
|
|
166
|
-
if (s.kind !== 'array') continue
|
|
167
|
-
const last = s.container[s.container.length - 1]
|
|
168
|
-
if (last && typeof last === 'object' && !Array.isArray(last)
|
|
169
|
-
&& Object.values(last).some(
|
|
170
|
-
v => v !== null && typeof v === 'object')) {
|
|
171
|
-
targetIdx = i
|
|
172
|
-
break
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (targetIdx === -1) return
|
|
176
|
-
while (stack.length - 1 > targetIdx) popScope()
|
|
177
|
-
closeItem(stack[targetIdx])
|
|
178
|
-
}
|
|
179
|
-
|
|
180
158
|
// --- Blockquote ----------------------------------------------------------
|
|
181
159
|
|
|
182
160
|
function startBlockquote(container, key) {
|
|
@@ -370,13 +348,25 @@ export function createParser() {
|
|
|
370
348
|
return
|
|
371
349
|
}
|
|
372
350
|
|
|
373
|
-
|
|
374
|
-
|
|
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.
|
|
375
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)
|
|
376
364
|
openObjectScope(depth, '')
|
|
377
365
|
return
|
|
378
366
|
}
|
|
379
367
|
|
|
368
|
+
popToDepth(depth)
|
|
369
|
+
|
|
380
370
|
// Anonymous sub-array: `### []` — handled below with the other array forms.
|
|
381
371
|
if (text === '[]') {
|
|
382
372
|
openSubArray(depth)
|
|
@@ -570,6 +560,20 @@ export function createParser() {
|
|
|
570
560
|
)
|
|
571
561
|
}
|
|
572
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
|
+
|
|
573
577
|
function parentContainerAndSeen(scope) {
|
|
574
578
|
if (scope.kind === 'object') {
|
|
575
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
|
|