Repository: i18next/i18next-parser
Pull request #948 (2023-12-18 12:44:24)

fix: keepRemoved option makes the failOnUpdate fails without any update
### Why am I submitting this PR

Fixes the issue 666

### Does it fix an existing ticket?

Yes/No #666




Patch:
diff --git a/src/transform.js b/src/transform.js
index 1545764..0fcd7a8 100644
--- a/src/transform.js
+++ b/src/transform.js
@@ -278,7 +278,9 @@ export default class i18nTransform extends Transform {
 
         if (this.options.failOnUpdate) {
           const addCount = uniqueCount[namespace] - mergeCount
-          if (addCount + restoreCount + oldCount !== 0) {
+          const refCount =
+            addCount + restoreCount + (this.options.keepRemoved ? 0 : oldCount)
+          if (refCount !== 0) {
             this.parserHadUpdate = true
             continue
           }


Test patch:
diff --git a/test/locales/en/test_fail_on_update.json b/test/locales/en/test_fail_on_update.json
new file mode 100644
index 0000000..9b3cf49
--- /dev/null
+++ b/test/locales/en/test_fail_on_update.json
@@ -0,0 +1,5 @@
+{
+  "key_existing_1": "exists",
+  "key_existing_2": "exists"
+}
+
diff --git a/test/parser.test.js b/test/parser.test.js
index 9901185..5881b7b 100644
--- a/test/parser.test.js
+++ b/test/parser.test.js
@@ -1990,6 +1990,188 @@ describe('parser', () => {
       i18nextParser.end(fakeFile)
     })
 
+    describe('failOnUpdate', () => {
+      it('fails if a translation is updated', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+        const fakeFile = new Vinyl({
+          contents: Buffer.from(
+            't("key_existing_1") t("key_existing_2") t("key_not_existing")'
+          ),
+          path: 'file.js',
+        })
+
+        let actualError
+        i18nextParser.once('error', (error) => {
+          actualError = error
+        })
+
+        const realExit = process.exit
+        process.exit = (exitCode) => {
+          process.exit = realExit
+          assert.equal(exitCode, 1)
+          assert.equal(
+            actualError,
+            'Some translations was updated and failOnUpdate option is enabled. Exiting...'
+          )
+          done()
+        }
+        i18nextParser.end(fakeFile)
+      })
+
+      it('fails if a translation is updated and keepRemoved is true', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          keepRemoved: true,
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1") t("key_not_existing")'),
+          path: 'file.js',
+        })
+
+        let actualError
+        i18nextParser.once('error', (error) => {
+          actualError = error
+        })
+
+        const realExit = process.exit
+        process.exit = (exitCode) => {
+          process.exit = realExit
+          assert.equal(exitCode, 1)
+          assert.equal(
+            actualError,
+            'Some translations was updated and failOnUpdate option is enabled. Exiting...'
+          )
+          done()
+        }
+        i18nextParser.end(fakeFile)
+      })
+
+      it('fails if a translation is updated and keepRemoved is a regex', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          keepRemoved: [/test_fail_on_update:s.*/],
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1") t("key_not_existing")'),
+          path: 'file.js',
+        })
+
+        let actualError
+        i18nextParser.once('error', (error) => {
+          actualError = error
+        })
+
+        const realExit = process.exit
+        process.exit = (exitCode) => {
+          process.exit = realExit
+          assert.equal(exitCode, 1)
+          assert.equal(
+            actualError,
+            'Some translations was updated and failOnUpdate option is enabled. Exiting...'
+          )
+          done()
+        }
+        i18nextParser.end(fakeFile)
+      })
+
+      it('passes when no translations are updated', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1") t("key_existing_2")'),
+          path: 'file.js',
+        })
+
+        i18nextParser.on('error', (error) => {
+          assert.fail(error)
+        })
+
+        i18nextParser.end(fakeFile)
+        done()
+      })
+
+      it('passes when no translations are updated and keepRemoved is true', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          keepRemoved: true,
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1") t("key_existing_2")'),
+          path: 'file.js',
+        })
+
+        i18nextParser.on('error', (error) => {
+          assert.fail(error)
+        })
+
+        i18nextParser.end(fakeFile)
+        done()
+      })
+
+      it('passes when no translations are updated and keepRemoved is a regex', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          keepRemoved: [/test_fail_on_update:s.*/],
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1") t("key_existing_2")'),
+          path: 'file.js',
+        })
+
+        i18nextParser.on('error', (error) => {
+          assert.fail(error)
+        })
+
+        i18nextParser.end(fakeFile)
+        done()
+      })
+
+      it('passes when translations are kept and keepRemoved is true', (done) => {
+        const i18nextParser = new i18nTransform({
+          output: 'test/locales/$LOCALE/$NAMESPACE.json',
+          failOnUpdate: true,
+          keepRemoved: true,
+          locales: ['en'],
+          defaultNamespace: 'test_fail_on_update',
+        })
+
+        const fakeFile = new Vinyl({
+          contents: Buffer.from('t("key_existing_1")'),
+          path: 'file.js',
+        })
+
+        i18nextParser.on('error', (error) => {
+          assert.fail(error)
+        })
+
+        i18nextParser.end(fakeFile)
+        done()
+      })
+    })
+
     describe('lexers', () => {
       it('supports custom lexers options', (done) => {
         let result