From ea233714bc92dda34e63a52a4dc81b2e6b3647e5 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 11 Aug 2019 19:44:18 +0100 Subject: [PATCH] Sort reference line numbers as numbers So that "source-file.js:20" appears before "source-file.js:100". Also sort references line by line, so that e.g. path.js:122 path.js:98 is now written as path.js:98 path.js:122 and sorted after path.js:90 and before path.js:100. --- index.js | 4 +- test/test.js | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ utils.js | 56 +++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index e805aa4..b3d3753 100644 --- a/index.js +++ b/index.js @@ -168,7 +168,9 @@ module.exports = function() { var refs = currentRef.split('\n'); if (refs.indexOf(newRef) === -1) { refs.push(newRef); - context[translate.msgid].comments.reference = refs.sort().join('\n'); + context[translate.msgid].comments.reference = refs + .sort(utils.compareReferenceLines) + .join('\n'); } } else if (typeof translate.msgid !== 'undefined') { // Do not add translation if msgid is undefined. diff --git a/test/test.js b/test/test.js index 44a23cb..ddc0729 100644 --- a/test/test.js +++ b/test/test.js @@ -3,6 +3,7 @@ var babel = require('@babel/core'); var fs = require('fs'); var plugin = require('../index.js'); +var utils = require('../utils.js'); describe('babel-gettext-extractor', function() { describe('#extract()', function() { @@ -212,3 +213,115 @@ describe('babel-gettext-extractor', function() { }); }); }); + +describe('utils', function() { + describe('#sortObjectKeysByRef()', function() { + it('Should sort source file line numbers as numbers', function() { + const unordered = { + a: { comments: { reference: 'path.js:20' } }, + b: { comments: { reference: 'path.js:100' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should read line number from reference after the last colon', function() { + const unordered = { + a: { comments: { reference: 'path:with-colon.js:20' } }, + b: { comments: { reference: 'path:with-colon.js:100' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should compare paths before line numbers', function() { + const unordered = { + a: { comments: { reference: 'path-a.js:20' } }, + b: { comments: { reference: 'path-b.js:10' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should compare paths case-insensitively', function() { + const unordered = { + a: { comments: { reference: 'path-a.js:10' } }, + b: { comments: { reference: 'path-B.js:10' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should treat reference with no colon as all path', function() { + const unordered = { + a: { comments: { reference: 'path-a.js' } }, + b: { comments: { reference: 'path-b.js' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should treat reference with a colon suffix as all path', function() { + const unordered = { + a: { comments: { reference: 'path-a.js:' } }, + b: { comments: { reference: 'path-b.js:' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should treat reference with a non-numeric part after the colon as all path', function() { + const unordered = { + a: { comments: { reference: 'path-a.js:not-a-number' } }, + b: { comments: { reference: 'path-b.js:not-a-number' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'a'); + assert(keys[1] === 'b'); + }); + + it('Should compare references line by line', function() { + const unordered = { + a: { comments: { reference: 'path.js:20\npath.js:820' } }, + b: { comments: { reference: 'path.js:20\npath.js:453' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'b'); + assert(keys[1] === 'a'); + }); + + it('Should sort the reference with more lines first if common lines are all equal', function() { + const unordered = { + a: { comments: { reference: 'path.js:20\npath.js:30' } }, + b: { comments: { reference: 'path.js:20\npath.js:30\npath.js:40' } }, + }; + + const ordered = utils.sortObjectKeysByRef(unordered); + const keys = Array.from(Object.keys(ordered)); + assert(keys[0] === 'b'); + assert(keys[1] === 'a'); + }); + }); +}); diff --git a/utils.js b/utils.js index ac976e4..a281087 100644 --- a/utils.js +++ b/utils.js @@ -1,3 +1,39 @@ +function splitReferenceLine(reference) { + const index = reference.lastIndexOf(':'); + if (index === -1 || index === reference.length - 1) { + return { path: reference.toLowerCase(), lineNumber: 0 }; + } + + const lineNumber = parseInt(reference.substring(index + 1), 10); + if (Number.isNaN(lineNumber)) { + return { path: reference.toLowerCase(), lineNumber: 0 }; + } + + return { + path: reference.substring(0, index).toLowerCase(), + lineNumber, + }; +} + +function compareReferenceLines(a, b) { + const refA = splitReferenceLine(a); + const refB = splitReferenceLine(b); + if (refA.path < refB.path) { + return -1; + } + if (refA.path > refB.path) { + return 1; + } + if (refA.lineNumber < refB.lineNumber) { + return -1; + } + if (refA.lineNumber > refB.lineNumber) { + return 1; + } + + return 0; +} + /* * Sorts the object keys by the file reference. * There's no guarantee of key iteration in order prior to es6 @@ -6,14 +42,25 @@ function sortObjectKeysByRef(unordered) { const ordered = {}; Object.keys(unordered).sort((a, b) => { - const refA = unordered[a].comments.reference.toLowerCase(); - const refB = unordered[b].comments.reference.toLowerCase(); - if (refA < refB) { + const refALines = unordered[a].comments.reference.split('\n'); + const refBLines = unordered[b].comments.reference.split('\n'); + + let line = 0; + while (line < refALines.length && line < refBLines.length) { + const result = compareReferenceLines(refALines[line], refBLines[line]); + if (result !== 0) { + return result; + } + line += 1; + } + + if (line < refALines.length) { return -1; } - if (refA > refB) { + if (line < refBLines.length) { return 1; } + return 0; }).forEach(function(key) { ordered[key] = unordered[key]; @@ -31,4 +78,5 @@ function stripIndent(str) { module.exports = { stripIndent: stripIndent, sortObjectKeysByRef: sortObjectKeysByRef, + compareReferenceLines: compareReferenceLines, };