From c6f06868aa61ee543f2917d271e7fb4726d59afa Mon Sep 17 00:00:00 2001 From: Ravi Gairola Date: Fri, 1 Apr 2016 10:47:00 -0700 Subject: [PATCH] Added optional caching --- index.js | 51 ++++--- package.json | 2 +- readme.md | 11 +- test/index.test.js | 371 ++++++++++++++++++++++++--------------------- 4 files changed, 245 insertions(+), 190 deletions(-) diff --git a/index.js b/index.js index 2fb8e11..a6350ed 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,12 @@ var through = require('through2'); */ var dictionaries = {}; +/** + * A cache for previously loaded dictionaries so we don't have to load them again + * @type {Array} + */ +var cache = []; + /** * Defauls options that are used if they are not overwritten by the user. * @type {Object} @@ -23,11 +29,12 @@ var defaults = { locales: './locales', delimiter: { prefix: 'R.', - stopCondition: /[;,<>\{}()\[\]"'\s$]/ + stopCondition: /[\-;,<>\{}()\[\]"'\s$]/ }, filename: '${path}/${name}-${lang}.${ext}', blacklist: [], - warn: true + warn: true, + cache: true }; /** @@ -35,25 +42,31 @@ var defaults = { * @param options */ function load(options) { - var files = fs.readdirSync(options.locales); - for (var i in files) { - var file = files[i]; - switch(path.extname(file)) { - case '.json': - case '.js': - dictionaries[path.basename(file, path.extname(file))] = flat(require(path.join(process.cwd(), options.locales, file))); - break; - case '.ini': - var iniData = fs.readFileSync(path.join(process.cwd(), options.locales, file)); - dictionaries[path.basename(file, path.extname(file))] = flat(ini2json(iniData)); - break; - case '.csv': - var csvData = fs.readFileSync(path.join(process.cwd(), options.locales, file)); - dictionaries[path.basename(file, path.extname(file))] = csv2json(csvData); - break; + if (cache[options.locales]) { + dictionaries = cache[options.locales]; + } else { + var files = fs.readdirSync(options.locales); + for (var i in files) { + var file = files[i]; + switch (path.extname(file)) { + case '.json': + case '.js': + dictionaries[path.basename(file, path.extname(file))] = flat(require(path.join(process.cwd(), options.locales, file))); + break; + case '.ini': + var iniData = fs.readFileSync(path.join(process.cwd(), options.locales, file)); + dictionaries[path.basename(file, path.extname(file))] = flat(ini2json(iniData)); + break; + case '.csv': + var csvData = fs.readFileSync(path.join(process.cwd(), options.locales, file)); + dictionaries[path.basename(file, path.extname(file))] = csv2json(csvData); + break; + } + if (options.cache) { + cache[options.locales] = dictionaries; + } } } - if (!Object.keys(dictionaries).length) { throw new Error('No translation dictionaries have been found!'); } diff --git a/package.json b/package.json index 539ab30..11e5459 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gulp-international", - "version": "0.0.4", + "version": "0.0.5", "description": "A gulp plugin that creates multi language versions of your source files", "license": "Apache-2.0", "homepage": "http://github.com/mallocator/gulp-international", diff --git a/readme.md b/readme.md index 823fe5a..6c65106 100644 --- a/readme.md +++ b/readme.md @@ -68,7 +68,7 @@ Default: ``` { prefix: 'R.', - stopCondition: /[;,\.<>\{}()\[\]"'\s$]/ + stopCondition: /[\-;,\.<>\{}()\[\]"'\s$]/ } ``` @@ -118,6 +118,15 @@ Default: ```true``` This enables warning to be printed out if any tokens are missing. +### cache + +Type: boolean +Default: ```true``` + +This enables caching of dictionaries, so that reruns of the plugin will not have to read in the translation tokens again. Depending +on your configuration you might want to disable caching based on how your livereloads are configured. + + ## Feature Ideas for the future Maybe I'll implement these one day, maybe not. diff --git a/test/index.test.js b/test/index.test.js index 76fc914..4f14195 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -47,200 +47,179 @@ function helper() { describe('gulp-international', () => { - - it('should be able to replace token with a default configuration', done => { - var options = { - locales: 'test/locales' - }; - - helper(options, (files, options) => { - expect(options.delimiter.prefix.length).to.be.gt(0); - expect(options.delimiter.stopCondition).to.be.a('RegExp'); - expect(files.length).to.equal(4); - expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); - expect(files[0].path).to.equal('test/helloworld-de_DE.html'); - done(); + describe('Usage Examples', () => { + + it('should be able to replace token with a default configuration', done => { + var options = { locales: 'test/locales' }; + helper(options, (files, options) => { + expect(options.delimiter.prefix.length).to.be.gt(0); + expect(options.delimiter.stopCondition).to.be.a('RegExp'); + expect(files.length).to.equal(4); + expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); + expect(files[0].path).to.equal('test/helloworld-de_DE.html'); + done(); + }); }); - }); - it('should be able to replace token with custom suffix delimiters', done => { - var content = '

${token1}

'; - var options = { - locales: 'test/locales', - delimiter: { - prefix: '${', - suffix: '}' - } - }; - helper(options, content , files => { - expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); - done(); + it('should be able to replace token with custom suffix delimiters', done => { + var content = '

${token1}

'; + var options = { + locales: 'test/locales', + delimiter: { prefix: '${', suffix: '}' } + }; + helper(options, content , files => { + expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); + done(); + }); }); - }); - it('should be able to limit languages to a whitelist', done => { - var options = { - locales: 'test/locales', - whitelist: 'en_US' - }; - helper(options, files => { - expect(files.length).to.equal(1); - expect(files[0].contents.toString('utf8')).to.equal('

content1

'); - expect(files[0].path).to.equal('test/helloworld-en_US.html'); - done(); + it('should be able to limit languages to a whitelist', done => { + var options = { locales: 'test/locales', whitelist: 'en_US' }; + helper(options, files => { + expect(files.length).to.equal(1); + expect(files[0].contents.toString('utf8')).to.equal('

content1

'); + expect(files[0].path).to.equal('test/helloworld-en_US.html'); + done(); + }); }); - }); - it('should not process languages on the blacklist', done => { - var options = { - locales: 'test/locales', - blacklist: 'en_US' - }; - helper(options, files => { - expect(files.length).to.equal(3); - expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); - expect(files[0].path).to.equal('test/helloworld-de_DE.html'); - expect(files[2].contents.toString('utf8')).to.equal('

conteúdo1

'); - expect(files[2].path).to.equal('test/helloworld-pt-BR.html'); - done(); + it('should not process languages on the blacklist', done => { + var options = { locales: 'test/locales', blacklist: 'en_US' }; + helper(options, files => { + expect(files.length).to.equal(3); + expect(files[0].contents.toString('utf8')).to.equal('

Inhalt1

'); + expect(files[0].path).to.equal('test/helloworld-de_DE.html'); + expect(files[2].contents.toString('utf8')).to.equal('

conteúdo1

'); + expect(files[2].path).to.equal('test/helloworld-pt-BR.html'); + done(); + }); }); - }); - it('should leave files without tokens unprocessed', done => { - var content = 'Not replaced'; - var options = { - locales: 'test/locales' - }; - helper(options, content, files => { - expect(files.length).to.equal(4); - expect(files[0].contents.toString('utf8')).to.equal('Not replaced'); - expect(files[0].path).to.equal('test/helloworld-de_DE.html'); - done(); + it('should be able to processes nested keys', done => { + var content = '

R.section1.token2

'; + var options = { locales: 'test/locales' }; + helper(options, content, files => { + expect(files.length).to.equal(4); + expect(files[0].contents.toString('utf8')).to.equal('

Inhalt2

'); + expect(files[0].path).to.equal('test/helloworld-de_DE.html'); + expect(files[1].contents.toString('utf8')).to.equal('

content2

'); + expect(files[1].path).to.equal('test/helloworld-en_US.html'); + done(); + }); }); - }); - it('should be able to process an empty file', done => { - var content = ''; - var options = { - locales: 'test/locales' - }; - helper(options, content, files => { - expect(files.length).to.equal(4); - expect(files[0].contents.toString('utf8')).to.equal(''); - expect(files[0].path).to.equal('test/helloworld-de_DE.html'); - done(); + it('should be able to use csv data as translation files', done => { + var options = { locales: 'test/locales', whitelist: 'fr_FR' }; + helper(options, files => { + expect(files.length).to.equal(1); + expect(files[0].contents.toString('utf8')).to.equal('

contenu1

'); + expect(files[0].path).to.equal('test/helloworld-fr_FR.html'); + done(); + }); }); - }); - it('should be able to processes nested keys', done => { - var content = '

R.section1.token2

'; - var options = { - locales: 'test/locales' - }; - helper(options, content, files => { - expect(files.length).to.equal(4); - expect(files[0].contents.toString('utf8')).to.equal('

Inhalt2

'); - expect(files[0].path).to.equal('test/helloworld-de_DE.html'); - expect(files[1].contents.toString('utf8')).to.equal('

content2

'); - expect(files[1].path).to.equal('test/helloworld-en_US.html'); - done(); + it('should support a custom filename format', done => { + var options = { locales: 'test/locales', filename: '${path}/${lang}/${name}.${ext}' }; + helper(options, files => { + expect(files[0].path).to.equal('test/de_DE/helloworld.html'); + expect(files[1].path).to.equal('test/en_US/helloworld.html'); + expect(files[2].path).to.equal('test/fr_FR/helloworld.html'); + expect(files[3].path).to.equal('test/pt-BR/helloworld.html'); + done(); + }); }); }); - - it('should be able to use csv data as translation files', done => { - var options = { - locales: 'test/locales', - whitelist: 'fr_FR' - }; - helper(options, files => { - expect(files.length).to.equal(1); - expect(files[0].contents.toString('utf8')).to.equal('

contenu1

'); - expect(files[0].path).to.equal('test/helloworld-fr_FR.html'); - done(); + describe('Error cases', () => { + + it('should leave files without tokens unprocessed', done => { + var content = 'Not replaced'; + var options = { locales: 'test/locales' }; + helper(options, content, files => { + expect(files.length).to.equal(4); + expect(files[0].contents.toString('utf8')).to.equal('Not replaced'); + expect(files[0].path).to.equal('test/helloworld-de_DE.html'); + done(); + }); }); - }); - it('should support a custom filename format', done => { - var options = { - locales: 'test/locales', - filename: '${path}/${lang}/${name}.${ext}' - }; - helper(options, files => { - expect(files[0].path).to.equal('test/de_DE/helloworld.html'); - expect(files[1].path).to.equal('test/en_US/helloworld.html'); - expect(files[2].path).to.equal('test/fr_FR/helloworld.html'); - expect(files[3].path).to.equal('test/pt-BR/helloworld.html'); - done(); + it('should be able to process an empty file', done => { + var content = ''; + var options = { locales: 'test/locales' }; + helper(options, content, files => { + expect(files.length).to.equal(4); + expect(files[0].contents.toString('utf8')).to.equal(''); + expect(files[0].path).to.equal('test/helloworld-de_DE.html'); + done(); + }); }); - }); - it('should throw an error if a file is null', () => { - try { - plugin().write(null); - expect.fail(); - } catch (e) { - expect(e).to.be.an('Error'); - } - }); + it('should throw an error if a file is null', () => { + try { + plugin().write(null); + expect.fail(); + } catch (e) { + expect(e).to.be.an('Error'); + expect(e.code).to.equal('ENOENT'); + } + }); - it('should throw an error if a file is a stream', () => { - var stream = plugin({ - locales: 'test/locales' + it('should throw an error if a file is a stream', () => { + var stream = plugin({ locales: 'test/locales' }); + try { + stream.write(new File({ + path: 'test/helloworld.html', + cwd: 'test/', + base: 'test/', + contents: new fs.createReadStream('locales/de_DE.ini') + })); + expect.fail(); + } catch (e) { + expect(e.message).to.equal('Streaming not supported'); + } }); - try { - stream.write(new File({ - path: 'test/helloworld.html', - cwd: 'test/', - base: 'test/', - contents: new fs.createReadStream('locales/de_DE.ini') - })); - expect.fail(); - } catch (e) { - expect(e.message).to.equal('Streaming not supported'); - } - }); - it('should throw an error if no dictionaries have been found', () => { - var options = { - locales: 'test/notlocales' - }; - try { - helper(options, files => {}); - expect.fail(); - } catch (e) { - expect(e).to.be.an('Error'); - } - }); + it('should throw an error if no dictionaries have been found', () => { + var options = { + locales: 'test/notlocales' + }; + try { + helper(options, files => {}); + expect.fail(); + } catch (e) { + expect(e).to.be.an('Error'); + expect(e.code).to.equal('ENOENT'); + } + }); - it('should work on source files that have no tokens', done => { - var content = '

notatoken

'; - var options = { - locales: 'test/locales', - whitelist: 'en_US' - }; - helper(options, content, files => { - expect(files.length).to.equal(1); - expect(files[0].contents.toString('utf8')).to.equal('

notatoken

'); - expect(files[0].path).to.equal('test/helloworld-en_US.html'); - done(); + it('should work on source files that have no tokens', done => { + var content = '

notatoken

'; + var options = { + locales: 'test/locales', + whitelist: 'en_US' + }; + helper(options, content, files => { + expect(files.length).to.equal(1); + expect(files[0].contents.toString('utf8')).to.equal('

notatoken

'); + expect(files[0].path).to.equal('test/helloworld-en_US.html'); + done(); + }); }); - }); - it('should be able to process a larger file with multiple replacements', done => { - var content = ` + it('should be able to process a larger file with multiple replacements', done => { + var content = `

R.section1.token2

@@ -251,15 +230,67 @@ describe('gulp-international', () => { `; - var options = { - locales: 'test/locales', - whitelist: ['en_US', 'pt-BR'] - }; - helper(options, content, files => { - expect(files.length).to.equal(2); - - expect(files[0].path).to.equal('test/helloworld-en_US.html'); - expect(files[0].contents.toString('utf8')).to.equal(` + var options = { + locales: 'test/locales', + whitelist: ['en_US', 'pt-BR'] + }; + helper(options, content, files => { + expect(files.length).to.equal(2); + + expect(files[0].path).to.equal('test/helloworld-en_US.html'); + expect(files[0].contents.toString('utf8')).to.equal(` + + +

content2

+ Our funny mascot +
content1
+
+

content3

+ + +`); + + expect(files[1].path).to.equal('test/helloworld-pt-BR.html'); + expect(files[1].contents.toString('utf8')).to.equal(` + + +

conteúdo2

+ Our funny mascot +
conteúdo1
+
+

conteúdo3

+ + +`); + done(); + }); + + + it('should be able to process a larger file with multiple replacements with a suffix delimiter', done => { + var content = ` + + +

${section1.token2}

+ Our funny mascot +
${R.token1}
+
+

${R.section1.subsection2.token3}

+ + +`; + var options = { + locales: 'test/locales', + whitelist: ['en_US', 'pt-BR'], + delimiter: { + prefix: '${', + suffix: '}' + } + }; + helper(options, content, files => { + expect(files.length).to.equal(2); + + expect(files[0].path).to.equal('test/helloworld-en_US.html'); + expect(files[0].contents.toString('utf8')).to.equal(`

content2

@@ -271,8 +302,8 @@ describe('gulp-international', () => { `); - expect(files[1].path).to.equal('test/helloworld-pt-BR.html'); - expect(files[1].contents.toString('utf8')).to.equal(` + expect(files[1].path).to.equal('test/helloworld-pt-BR.html'); + expect(files[1].contents.toString('utf8')).to.equal(`

conteúdo2

@@ -283,7 +314,9 @@ describe('gulp-international', () => { `); - done(); + done(); + }); + }); }); }); });