Skip to content

Commit 2936600

Browse files
committed
fix(toml): enhance regex for top-level TOML fields to support inline comments
- Update regex patterns in insertAfterVersionField and updateTopLevelTomlFields functions to handle inline comments for version and lastUpdated fields. - Add tests to ensure correct handling of TOML fields with inline comments and trailing spaces. - Improve overall robustness of TOML configuration handling. Refs: #277
1 parent 9bf3fb8 commit 2936600

File tree

4 files changed

+1175
-3
lines changed

4 files changed

+1175
-3
lines changed

src/utils/zcf-config.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ function insertAtTopLevelStart(topLevel: string, content: string): string {
9999
* @returns Updated top-level content
100100
*/
101101
function insertAfterVersionField(topLevel: string, content: string): string {
102-
const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*$/m
102+
// Support inline comments like: version = "1.0.0" # comment
103+
const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m
103104
const match = topLevel.match(versionRegex)
104105

105106
if (match && match.index !== undefined) {
@@ -139,7 +140,8 @@ function updateTopLevelTomlFields(content: string, version: string, lastUpdated:
139140

140141
// Update or add version field in top-level area only
141142
// Match version field at the start of a line (no indentation for top-level)
142-
const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*$/m
143+
// Support inline comments like: version = "1.0.0" # comment
144+
const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m
143145
const versionMatch = topLevel.match(versionRegex)
144146
if (versionMatch) {
145147
// Update existing version
@@ -151,7 +153,8 @@ function updateTopLevelTomlFields(content: string, version: string, lastUpdated:
151153
}
152154

153155
// Update or add lastUpdated field in top-level area only
154-
const lastUpdatedRegex = /^lastUpdated\s*=\s*["'][^"']*["'][ \t]*$/m
156+
// Support inline comments like: lastUpdated = "2024-01-01" # comment
157+
const lastUpdatedRegex = /^lastUpdated\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m
155158
const lastUpdatedMatch = topLevel.match(lastUpdatedRegex)
156159
if (lastUpdatedMatch) {
157160
// Update existing lastUpdated

tests/unit/utils/toml-edit.test.ts

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,5 +240,222 @@ count = 0
240240
ensureTomlInitSync()
241241
expect(isTomlInitialized()).toBe(true)
242242
})
243+
244+
it('should reset initialization state correctly', async () => {
245+
const { ensureTomlInit, isTomlInitialized, resetTomlInit } = await import('../../../src/utils/toml-edit')
246+
247+
// Ensure initialized first
248+
await ensureTomlInit()
249+
expect(isTomlInitialized()).toBe(true)
250+
251+
// Reset initialization
252+
resetTomlInit()
253+
expect(isTomlInitialized()).toBe(false)
254+
255+
// Re-initialize
256+
await ensureTomlInit()
257+
expect(isTomlInitialized()).toBe(true)
258+
})
259+
})
260+
261+
describe('async functions', () => {
262+
describe('parseTomlAsync', () => {
263+
it('should parse simple TOML asynchronously', async () => {
264+
const { parseTomlAsync } = await import('../../../src/utils/toml-edit')
265+
266+
const toml = `
267+
name = "test"
268+
version = "1.0.0"
269+
270+
[section]
271+
key = "value"
272+
`
273+
274+
const result = await parseTomlAsync<{
275+
name: string
276+
version: string
277+
section: { key: string }
278+
}>(toml)
279+
280+
expect(result.name).toBe('test')
281+
expect(result.version).toBe('1.0.0')
282+
expect(result.section.key).toBe('value')
283+
})
284+
285+
it('should parse nested structures asynchronously', async () => {
286+
const { parseTomlAsync } = await import('../../../src/utils/toml-edit')
287+
288+
const toml = `
289+
[parent]
290+
name = "parent"
291+
292+
[parent.child]
293+
name = "child"
294+
value = 42
295+
`
296+
297+
const result = await parseTomlAsync<{
298+
parent: {
299+
name: string
300+
child: { name: string, value: number }
301+
}
302+
}>(toml)
303+
304+
expect(result.parent.name).toBe('parent')
305+
expect(result.parent.child.name).toBe('child')
306+
expect(result.parent.child.value).toBe(42)
307+
})
308+
})
309+
310+
describe('stringifyTomlAsync', () => {
311+
it('should stringify objects asynchronously', async () => {
312+
const { stringifyTomlAsync } = await import('../../../src/utils/toml-edit')
313+
314+
const data = {
315+
name: 'test-async',
316+
version: '2.0.0',
317+
}
318+
319+
const result = await stringifyTomlAsync(data)
320+
321+
expect(result).toContain('name = "test-async"')
322+
expect(result).toContain('version = "2.0.0"')
323+
})
324+
325+
it('should stringify nested objects with sections asynchronously', async () => {
326+
const { stringifyTomlAsync } = await import('../../../src/utils/toml-edit')
327+
328+
const data = {
329+
config: {
330+
enabled: true,
331+
count: 100,
332+
},
333+
}
334+
335+
const result = await stringifyTomlAsync(data)
336+
337+
expect(result).toContain('[config]')
338+
expect(result).toContain('enabled = true')
339+
expect(result).toContain('count = 100')
340+
})
341+
})
342+
343+
describe('editTomlAsync', () => {
344+
it('should edit nested fields asynchronously', async () => {
345+
const { editTomlAsync } = await import('../../../src/utils/toml-edit')
346+
347+
const original = `
348+
[section]
349+
key = "old-value"
350+
other = "preserved"
351+
`
352+
353+
const result = await editTomlAsync(original, 'section.key', 'new-async-value')
354+
355+
expect(result).toContain('key = "new-async-value"')
356+
expect(result).toContain('other = "preserved"')
357+
expect(result).not.toContain('old-value')
358+
})
359+
360+
it('should preserve comments when editing asynchronously', async () => {
361+
const { editTomlAsync } = await import('../../../src/utils/toml-edit')
362+
363+
const original = `# This comment should be preserved
364+
[settings]
365+
# Important setting comment
366+
enabled = false
367+
`
368+
369+
const result = await editTomlAsync(original, 'settings.enabled', true)
370+
371+
expect(result).toContain('# This comment should be preserved')
372+
expect(result).toContain('# Important setting comment')
373+
expect(result).toContain('enabled = true')
374+
})
375+
376+
it('should handle various value types asynchronously', async () => {
377+
const { editTomlAsync } = await import('../../../src/utils/toml-edit')
378+
379+
// Test with number
380+
let original = `[config]
381+
value = 0`
382+
let result = await editTomlAsync(original, 'config.value', 999)
383+
expect(result).toContain('value = 999')
384+
385+
// Test with boolean
386+
original = `[config]
387+
flag = false`
388+
result = await editTomlAsync(original, 'config.flag', true)
389+
expect(result).toContain('flag = true')
390+
391+
// Test with array
392+
original = `[config]
393+
items = []`
394+
result = await editTomlAsync(original, 'config.items', ['a', 'b', 'c'])
395+
expect(result).toContain('a')
396+
expect(result).toContain('b')
397+
expect(result).toContain('c')
398+
})
399+
})
400+
401+
describe('batchEditTomlAsync', () => {
402+
it('should apply multiple edits asynchronously', async () => {
403+
const { batchEditTomlAsync } = await import('../../../src/utils/toml-edit')
404+
405+
const original = `# Configuration file
406+
[general]
407+
name = "old-name"
408+
version = "0.0.1"
409+
410+
[settings]
411+
enabled = false
412+
count = 0
413+
`
414+
415+
const edits: Array<[string, unknown]> = [
416+
['general.name', 'new-name'],
417+
['general.version', '1.0.0'],
418+
['settings.enabled', true],
419+
['settings.count', 42],
420+
]
421+
422+
const result = await batchEditTomlAsync(original, edits)
423+
424+
expect(result).toContain('# Configuration file')
425+
expect(result).toContain('name = "new-name"')
426+
expect(result).toContain('version = "1.0.0"')
427+
expect(result).toContain('enabled = true')
428+
expect(result).toContain('count = 42')
429+
})
430+
431+
it('should handle empty edits array asynchronously', async () => {
432+
const { batchEditTomlAsync } = await import('../../../src/utils/toml-edit')
433+
434+
const original = `[section]
435+
key = "value"`
436+
437+
const result = await batchEditTomlAsync(original, [])
438+
439+
expect(result).toBe(original)
440+
})
441+
442+
it('should apply edits in order asynchronously', async () => {
443+
const { batchEditTomlAsync } = await import('../../../src/utils/toml-edit')
444+
445+
const original = `[section]
446+
key = "initial"`
447+
448+
// Apply two edits to the same key - last one should win
449+
const edits: Array<[string, unknown]> = [
450+
['section.key', 'first'],
451+
['section.key', 'second'],
452+
]
453+
454+
const result = await batchEditTomlAsync(original, edits)
455+
456+
expect(result).toContain('key = "second"')
457+
expect(result).not.toContain('key = "first"')
458+
})
459+
})
243460
})
244461
})

0 commit comments

Comments
 (0)