Repository: i18next/i18next-parser
Pull request #198 (2020-01-13 16:45:50)

Update Node test versions


Patch:
diff --git a/.travis.yml b/.travis.yml
index c433729..82ce08c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,11 @@
 language: node_js
 
 node_js:
-  - '8'
-  - '7'
-  - '6'
-
-before_install:
-  - npm install -g npm
-  - npm --version
+  - 6
+  - 8
+  - 10
+  - 12
+  - 13
 
 after_success:
   - npm run test
diff --git a/README.md b/README.md
index 8aa96b8..22b10ad 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Finally, if you want to make this process even less painful, I invite you to che
 ## Features
 
 - Choose your weapon: A CLI, a standalone parser or a stream transform
-- 5 built in lexers: Javascript, JSX, HTML, Handlebars, and TypeScript+tsx
+- 6 built in lexers: Javascript, JSX, HTML, Handlebars, TypeScript+tsx and Vue
 - Creates one catalog file per locale and per namespace
 - Backs up the old keys your code doesn't use anymore in `namespace_old.json` catalog
 - Restores keys from the `_old` file if the one in the translation file is empty
@@ -24,9 +24,9 @@ Finally, if you want to make this process even less painful, I invite you to che
   - **Plural**: keys of the form `key_plural` and `key_0`, `key_1` as described [here](https://www.i18next.com/translation-function/plurals)
 - Tested on Node 6+
 
-## DISCLAIMER: `1.0.0-beta`
+## Versions
 
-`1.x` is currently in beta. You can follow the pre-releases [here](https://github.com/i18next/i18next-parser/releases). It is a deep rewrite of this package that solves many issues, the main one being that it was slowly becoming unmaintainable. The [migration](docs/migration.md) contains all the breaking changes. Everything that follows is related to `1.x`. If you rely on a `0.x.x` version, you can still find the old documentation on its dedicated [branch](https://github.com/i18next/i18next-parser/tree/0.x.x).
+`1.x` has been in beta for a good while. You can follow the pre-releases [here](https://github.com/i18next/i18next-parser/releases). It is a deep rewrite of this package that solves many issues, the main one being that it was slowly becoming unmaintainable. The [migration](docs/migration.md) from `0.x` contains all the breaking changes. Everything that follows is related to `1.x`. You can still find the old `0.x` documentation on its dedicated [branch](https://github.com/i18next/i18next-parser/tree/0.x.x).
 
 
 ## Usage
@@ -36,8 +36,8 @@ Finally, if you want to make this process even less painful, I invite you to che
 You can use the CLI with the package installed locally but if you want to use it from anywhere, you better install it globally:
 
 ```
-yarn global add i18next-parser@next
-npm install -g i18next-parser@next
+yarn global add i18next-parser
+npm install -g i18next-parser
 i18next 'app/**/*.{js,hbs}' 'lib/**/*.{js,hbs}' [-oc]
 ```
 
@@ -54,8 +54,8 @@ Multiple globbing patterns are supported to specify complex file selections. You
 Save the package to your devDependencies:
 
 ```
-yarn add -D i18next-parser@next
-npm install --save-dev i18next-parser@next
+yarn add -D i18next-parser
+npm install --save-dev i18next-parser
 ```
 
 [Gulp](http://gulpjs.com/) defines itself as the streaming build system. Put simply, it is like Grunt, but performant and elegant.
@@ -80,8 +80,8 @@ gulp.task('i18next', function() {
 Save the package to your devDependencies:
 
 ```
-yarn add -D i18next-parser@next
-npm install --save-dev i18next-parser@next
+yarn add -D i18next-parser
+npm install --save-dev i18next-parser
 ```
 
 [Broccoli.js](https://github.com/broccolijs/broccoli) defines itself as a fast, reliable asset pipeline, supporting constant-time rebuilds and compact build definitions.
@@ -350,4 +350,4 @@ If you use this package and like it, supporting me on [Patreon](https://www.patr
   <a href="https://locize.com/" target="_blank">
     <img src="https://raw.githubusercontent.com/i18next/i18next/master/assets/locize_sponsor_240.gif" width="240px">
   </a>
-</p>
\ No newline at end of file
+</p>
diff --git a/package.json b/package.json
index c8886e4..e8d5bd8 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "author": "Karel Ledru",
   "description": "Command Line tool for i18next",
   "name": "i18next-parser",
-  "version": "1.0.0-beta38",
+  "version": "1.0.0",
   "license": "MIT",
   "main": "dist/index.js",
   "bin": {
@@ -31,11 +31,14 @@
     "typescript": "^3.6.4",
     "vinyl": "~2.0.1",
     "vinyl-fs": "^3.0.2",
+    "vue-template-compiler": "^2.6.11",
     "yamljs": "^0.3.0"
   },
   "optionalDependencies": {},
   "engines": {
-    "node": ">=0.10.22"
+    "node": ">=6",
+    "npm": ">=6",
+    "yarn": ">=1"
   },
   "devDependencies": {
     "babel-cli": "^6.26.0",
diff --git a/src/lexers/vue-lexer.js b/src/lexers/vue-lexer.js
new file mode 100644
index 0000000..de682ec
--- /dev/null
+++ b/src/lexers/vue-lexer.js
@@ -0,0 +1,25 @@
+import BaseLexer from './base-lexer'
+import JavascriptLexer from './javascript-lexer.js'
+
+export default class VueLexer extends BaseLexer {
+  constructor(options = {}) {
+    super(options)
+
+    this.functions = options.functions || ['$t']
+  }
+
+  extract(content, filename) {
+    let keys = []
+
+    const Lexer = new JavascriptLexer()
+    Lexer.on('warning', warning => this.emit('warning', warning))
+    keys = keys.concat(Lexer.extract(content))
+
+    const compiledTemplate = require('vue-template-compiler').compile(content).render
+    const Lexer2 = new JavascriptLexer({ functions: this.functions })
+    Lexer2.on('warning', warning => this.emit('warning', warning))
+    keys = keys.concat(Lexer2.extract(compiledTemplate))
+
+    return keys
+  }
+}
diff --git a/src/parser.js b/src/parser.js
index 4ac79cb..736fdb9 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -4,6 +4,7 @@ import HandlebarsLexer from './lexers/handlebars-lexer'
 import HTMLLexer from './lexers/html-lexer'
 import JavascriptLexer from './lexers/javascript-lexer'
 import JsxLexer from './lexers/jsx-lexer'
+import VueLexer from './lexers/vue-lexer'
 
 const lexers = {
   hbs: ['HandlebarsLexer'],
@@ -18,6 +19,8 @@ const lexers = {
   jsx: ['JsxLexer'],
   tsx: ['JsxLexer'],
 
+  vue: ['VueLexer'],
+
   default: ['JavascriptLexer']
 }
 
@@ -25,7 +28,8 @@ const lexersMap = {
   HandlebarsLexer,
   HTMLLexer,
   JavascriptLexer,
-  JsxLexer
+  JsxLexer,
+  VueLexer
 }
 
 export default class Parser extends EventEmitter {
diff --git a/yarn.lock b/yarn.lock
index 53df72e..b14c6ae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1293,6 +1293,11 @@ d@1, d@^1.0.1:
     es5-ext "^0.10.50"
     type "^1.0.1"
 
+de-indent@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+  integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
+
 debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -2173,6 +2178,11 @@ he@1.1.1:
   resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
   integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
 
+he@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
 heimdalljs-logger@^0.1.7:
   version "0.1.10"
   resolved "https://registry.yarnpkg.com/heimdalljs-logger/-/heimdalljs-logger-0.1.10.tgz#90cad58aabb1590a3c7e640ddc6a4cd3a43faaf7"
@@ -4245,6 +4255,14 @@ vinyl@~2.0.1:
     remove-trailing-separator "^1.0.1"
     replace-ext "^1.0.0"
 
+vue-template-compiler@^2.6.11:
+  version "2.6.11"
+  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080"
+  integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==
+  dependencies:
+    de-indent "^1.0.2"
+    he "^1.1.0"
+
 walk-sync@^0.3.1:
   version "0.3.4"
   resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.4.tgz#cf78486cc567d3a96b5b2237c6108017a5ffb9a4"


Test patch:
diff --git a/test/lexers/vue-lexer.test.js b/test/lexers/vue-lexer.test.js
new file mode 100644
index 0000000..9681f1e
--- /dev/null
+++ b/test/lexers/vue-lexer.test.js
@@ -0,0 +1,44 @@
+import { assert } from 'chai'
+import VueLexer from '../../src/lexers/vue-lexer'
+
+describe('VueLexer', () => {
+  it('extracts keys from template & js', (done) => {
+    const Lexer = new VueLexer()
+    const content =
+      "<template><p>{{ $t('first') }}</p><template><script>export default " +
+      "{ mounted() { this.$i18n.t('second'); } }</script>"
+    assert.deepEqual(Lexer.extract(content), [{ key: 'second' },{ key: 'first' }])
+    done()
+  })
+
+  it('extracts keys with interpolation from template & js', (done) => {
+    const Lexer = new VueLexer()
+    const content =
+      "<template><p>{{ $t('first {test}', {test: 'station'}) }}</p><template>" +
+      "<script>export default { mounted() { this.$i18n.t('second {test}', " +
+      "{test: 'interpol'}); } }</script>"
+    assert.deepEqual(Lexer.extract(content), [{
+      key: 'second {test}',
+      test: 'interpol'
+    }, {
+      key: 'first {test}',
+      test: 'station'
+    }])
+    done()
+  })
+
+  it('extracts keys with plural from template & js', (done) => {
+    const Lexer = new VueLexer()
+    const content =
+      "<template><p>{{ $t('first', {count: 5}) }}</p><template><script>export default " +
+      "{ mounted() { this.$i18n.t('second', {count: 2}); } }</script>"
+    assert.deepEqual(Lexer.extract(content), [{
+      key: 'second',
+      count: '2'
+    }, {
+      key: 'first',
+      count: '5'
+    }])
+    done()
+  })
+})