From 9138fb5c238daea4d556b7ab9e0030a47608ce04 Mon Sep 17 00:00:00 2001 From: jackwener Date: Wed, 25 Mar 2026 13:34:46 +0800 Subject: [PATCH] fix(sort): use localeCompare with natural numeric sort by default Replace manual < > comparison with localeCompare({ numeric: true }) so string-encoded numbers (e.g. "99" vs "1000") sort correctly without requiring an explicit flag. This is a one-line fix that makes sort just work for all YAML authors. --- src/pipeline/steps/transform.ts | 4 +--- src/pipeline/transform.test.ts | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/pipeline/steps/transform.ts b/src/pipeline/steps/transform.ts index 3b6a82c5..d070cc65 100644 --- a/src/pipeline/steps/transform.ts +++ b/src/pipeline/steps/transform.ts @@ -60,9 +60,7 @@ export async function stepSort(_page: IPage | null, params: unknown, data: unkno return [...data].sort((a, b) => { const left = isRecord(a) ? a[key] : undefined; const right = isRecord(b) ? b[key] : undefined; - const va = left ?? ''; - const vb = right ?? ''; - const cmp = va < vb ? -1 : va > vb ? 1 : 0; + const cmp = String(left ?? '').localeCompare(String(right ?? ''), undefined, { numeric: true }); return reverse ? -cmp : cmp; }); } diff --git a/src/pipeline/transform.test.ts b/src/pipeline/transform.test.ts index 7e00157b..e676b5dd 100644 --- a/src/pipeline/transform.test.ts +++ b/src/pipeline/transform.test.ts @@ -101,6 +101,26 @@ describe('stepSort', () => { await stepSort(null, 'score', SAMPLE_DATA, {}); expect(SAMPLE_DATA).toEqual(original); }); + + it('sorts string-encoded numbers naturally by default', async () => { + const data = [ + { name: 'A', volume: '99' }, + { name: 'B', volume: '1000' }, + { name: 'C', volume: '250' }, + ]; + const result = await stepSort(null, { by: 'volume', order: 'desc' }, data, {}); + expect((result as typeof data).map((r) => r.name)).toEqual(['B', 'C', 'A']); + }); + + it('handles missing fields gracefully', async () => { + const data = [ + { name: 'A', value: '10' }, + { name: 'B' }, + { name: 'C', value: '5' }, + ]; + const result = await stepSort(null, { by: 'value', order: 'asc' }, data, {}); + expect((result as typeof data).map((r) => r.name)).toEqual(['B', 'C', 'A']); + }); }); describe('stepLimit', () => {