Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

i18next-parser

Package Overview
Dependencies
Maintainers
1
Versions
145
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

i18next-parser - npm Package Compare versions

Comparing version 0.13.0 to 1.0.0-beta1

.babelrc

217

bin/cli.js
#!/usr/bin/env node
var colors = require('colors');
var program = require('commander');
var fs = require('fs');
var path = require('path');
var Readable = require('stream').Readable;
var readdirp = require('readdirp');
var through = require('through2');
var File = require('vinyl');
var mkdirp = require('mkdirp');
var Parser = require('../index');
var colors = require('colors')
var fs = require('fs')
var i18nTransform = require('../dist').default
var path = require('path')
var pkg = require('../package.json')
var program = require('commander')
var sort = require('gulp-sort')
var vfs = require('vinyl-fs')
// Configure the command line
// ==========================
program
.version('0.13.0')
.option( '-r, --recursive' , 'Parse sub directories' )
.option( '-p, --parser <string>' , 'A custom regex to use to parse your code' )
.option( '-a, --attributes <list>' , 'The html attributes to parse' )
.option( '-o, --output <directory>' , 'The directory to output parsed keys' )
.option( '-f, --functions <list>' , 'The function names to parse in your code' )
.option( '--prefix <string>' , 'Prefix filename for each locale, eg.: \'pre-$LOCALE-\' will yield \'pre-en-default.json\'')
.option( '--suffix <string>' , 'Suffix filename for each locale, eg.: \'-$suf-LOCALE\' will yield \'default-suf-en.json\'')
.option( '--extension <string>' , 'Specify extension for filename for each locale, eg.: \'.$LOCALE.i18n\' will yield \'default.en.i18n\'')
.option( '-n, --namespace <string>' , 'The default namespace (translation by default)' )
.option( '-s, --namespace-separator <string>' , 'The default namespace separator (: by default)' )
.option( '-k, --key-separator <string>' , 'The default key separator (. by default)' )
.option( '-c, --context-separator <string>' , 'The default context separator (_ by default)' )
.option( '-l, --locales <list>' , 'The locales in your application' )
.option( '--directoryFilter <list>' , 'Filter directories' )
.option( '--fileFilter <list>' , 'Filter files' )
.option( '--keep-removed' , 'Prevent keys no longer found from being removed' )
.option( '--write-old <string>' , 'Save (or don\'t if false) _old files' )
.option( '--ignore-variables' , 'Don\'t fail when a variable is found' )
.option( '--default-values' , 'Extract default values' )
.parse( process.argv );
.version(pkg.version)
.option('-c, --config <path>', 'Path to the config file (default: i18next-scanner.config.js)')
.option('-o, --output <path>', 'Path to the output directory (default: locales)')
.option('-s, --silent', 'Disable logging to stdout')
program.on('--help', function() {
console.log(' Examples:')
console.log('')
console.log(' $ i18next "src/**/*.{js,jsx}"')
console.log(' $ i18next "/path/to/src/app.js" "/path/to/assets/index.html"')
console.log(' $ i18next --config i18next-parser.config.js --output /path/to/output \'src/**/*.{js,jsx}\'')
console.log('')
})
program.parse(process.argv)
// Define the target directory
// ===========================
var firstArgument = process.argv[2];
var input;
var output;
var args = program.args || []
if ( firstArgument && firstArgument.charAt(0) !== '-' ) {
var parts = firstArgument.split(':');
var globs = args.map(function (s) {
s = s.trim()
if (s.match(/(^'.*'$|^".*"$)/)) {
s = s.slice(1, -1)
}
return s
})
if ( parts.length > 1 ) {
input = path.resolve(process.cwd(), parts[0]);
output = path.resolve(process.cwd(), parts[1]);
var config = {}
if (program.config) {
try {
config = require(path.resolve(program.config))
} catch (err) {
console.log(' [error] '.red + 'Config file does not exist: ' + program.config)
return
}
else {
input = path.resolve(process.cwd(), firstArgument);
}
}
else {
input = process.cwd();
}
output = output || program.output || 'locales';
var output = config.output || program.output
if ( ! fs.existsSync(input) ) {
console.log( '\n' + 'Error: '.red + input + ' is not a file or directory\n' );
process.exit( 1 );
if (!output) {
console.log(' [error] '.red + 'an `output` is required via --config or --output option')
program.help()
program.exit(1)
}
if (!globs.length) {
console.log(' [error] '.red + 'missing argument: ')
program.help()
return
}
// Parse passed values
// ===================
program.locales = program.locales && program.locales.split(',');
program.attributes = program.attributes && program.attributes.split(',');
program.functions = program.functions && program.functions.split(',');
program.writeOld = program.writeOld !== 'false';
program.directoryFilter = program.directoryFilter && program.directoryFilter.split(',');
program.fileFilter = program.fileFilter && program.fileFilter.split(',');
program.output = path.resolve(process.cwd(), output);
// Welcome message
// ===============
var intro = '\n'+
'i18next Parser'.yellow + '\n' +
'--------------'.yellow + '\n' +
'Input: '.green + input + '\n' +
'Output: '.green + program.output + '\n\n';
console.log(intro);
// Create a stream from the input
// ==============================
var stat = fs.statSync(input);
var stream;
if ( stat.isDirectory() ) {
var args = { root: input };
if( program.directoryFilter ) {
args.directoryFilter = program.directoryFilter;
}
if( program.fileFilter ) {
args.fileFilter = program.fileFilter;
}
if ( ! program.recursive) {
args.depth = 0;
}
stream = readdirp( args );
console.log()
console.log(' i18next Parser'.yellow)
console.log(' --------------'.yellow)
console.log(' Input: '.yellow + args.join(', '))
console.log(' Output: '.yellow + program.output)
if (!program.silent) {
console.log()
}
else {
stream = new Readable( { objectMode: true } );
var file = new File({
path: input,
stat: stat
});
stream._read = function() {
stream.push( file );
stream.push( null );
};
}
var count = 0
// Parse the stream
// ================
var parser = Parser(program);
parser.on('error', function (message, region) {
console.log('[error] '.red + message + ': ' + region.trim());
});
stream
.pipe(through( { objectMode: true }, function (data, encoding, done) {
if ( data instanceof File ) {
this.push( data );
vfs.src(globs)
.pipe(sort())
.pipe(
new i18nTransform(config)
.on('reading', function (file) {
if (!program.silent) {
console.log(' [read] '.green + file.path)
}
else if ( data.fullPath ) {
var file = new File({
path: data.fullPath,
stat: data.stat
});
this.push( file );
count++
})
.on('data', function (file) {
if (!program.silent) {
console.log(' [write] '.green + file.path)
}
done();
}))
.pipe(parser.on('reading', function(path) { console.log('[parse] '.green + path) }))
.pipe(through( { objectMode: true }, function (file, encoding, done) {
mkdirp.sync( path.dirname(file.path) );
fs.writeFileSync( file.path, file.contents + "\n" );
this.push( file );
done();
}));
})
.on('error', function (message, region) {
console.log(' [error] '.red + message + ': ' + region.trim())
})
.on('finish', function () {
if (!program.silent) {
console.log()
}
console.log(' Stats: '.yellow + count + ' files were parsed')
})
)
.pipe(vfs.dest(output))
# Changelog
## 1.0.0-beta1
## 0.13.0 - latest

@@ -4,0 +8,0 @@

{
"author": "Karel Ledru-Mathe",
"author": "Karel Ledru",
"description": "Command Line tool for i18next",
"name": "i18next-parser",
"version": "0.13.0",
"version": "1.0.0-beta1",
"license": "MIT",
"main": "src/index.js",
"bin": {

@@ -11,3 +12,4 @@ "i18next": "./bin/cli.js"

"scripts": {
"test": "mocha --reporter nyan test/test.js"
"test": "mocha --require babel-register --require babel-polyfill test/**/*.test.js",
"watch": "babel src -d dist -w"
},

@@ -19,11 +21,12 @@ "repository": {

"dependencies": {
"lodash": "~4.17.3",
"readdirp": "~2.1.0",
"colors": "~1.2.0-rc0",
"commander": "~2.9.0",
"concat-stream": "~1.6.0",
"commander": "~2.9.0",
"mkdirp": "~0.5.1",
"colors": "~1.1.2",
"eol": "^0.9.1",
"gulp-sort": "^2.0.0",
"lodash": "~4.17.4",
"through2": "~2.0.3",
"vinyl": "~2.0.1",
"gulp-util": "~3.0.7"
"vinyl-fs": "^3.0.2",
"yamljs": "^0.3.0"
},

@@ -34,4 +37,9 @@ "engines": {

"devDependencies": {
"mocha": "~3.2.0",
"assert": "~1.4.1"
"babel-cli": "^6.26.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-register": "^6.26.0",
"chai": "^4.1.2",
"mocha": "^5.0.0"
},

@@ -38,0 +46,0 @@ "keywords": [

@@ -5,314 +5,171 @@ # i18next Parser [![Build Status](https://travis-ci.org/i18next/i18next-parser.svg?branch=master)](https://travis-ci.org/i18next/i18next-parser)

A simple command line and gulp plugin that lets you parse your code and extract the translations keys in it.
When translating an application, maintaining the catalog by hand is painful. This package automate the process. Don't let the name fool you, it was originally built with i18next in mind but it works well with other i18n libraries.
The idea is to parse code files to retrieve the translation keys and create a catalog. You can use the command line or run in the background with Gulp while coding. It removes the pain of maintaining your translation catalog.
## Features
- Parses a single file or a directory (recursively or not)
- Parses template files (support for jade, handlebars and data-i18n attribute in html)
- Creates one json file per locale and per namespace.
- Remove old keys your code doesn't use anymore and place them in a `namespace_old.json` file. It is useful to avoid losing translations you may want to reuse.
- Restore keys from the `_old` file if the one in the translation file is empty.
- Support most i18next features:
- Handles context keys of the form `key_context`
- Handles plural keys of the form `key_plural` and `key_plural_0`
- Handles multiline array in catalog
- Is a stream transform (so it works with gulp)
- Choose your weapon: A CLI, a standalone parser or a stream transform
- Three built in lexers: Javascript, HTML and Handlebars
- 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.
- Supports i18next features:
- **Context**: keys of the form `key_context`
- **Plural**: keys of the form `key_plural` and `key_plural_0`
- Behind the hood, it's a stream transform (so it works with gulp)
- Supports es6 template strings (in addition to single/double quoted strings) with ${expression} placeholders
## `1.x`
---
`1.x` is currently in beta. 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. 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).
## Installation
```
npm install i18next-parser -g
```
## Usage
## Tests
### CLI
You can use the CLI with the package installed locally but if you want to use it from anywhere, you better install it globally:
```
mocha --reporter nyan test/test.js
yarn global add i18next-parser
npm install -g i18next-parser
i18next 'app/**/*.{js,hbs}' 'lib/**/*.{js,hbs}' [-oc]
```
---
Multiple globbing patterns are supported to specify complex file selections. You can learn how to write globs [here](https://github.com/isaacs/node-glob). Note that glob must be wrapped with single quotes when passed as arguments.
## Contribute
- **-o, --output <path>**: Where to write the locale files.
- **-c, --config <path>**: The config file with all the options
- **-S, --silent**: The config file with all the options
Any contribution is welcome. Just follow those quick guidelines:
### Gulp
1. Fork and clone the project
2. Create a branch `git checkout -b feature/my-feature` (use feature/ or hotfix/ branch prefixes accordingly)
3. Push to your fork
4. Write tests and documentation (I won't merge a PR without it)
5. Make a pull request from your repository `feature/my-feature` branch to this repository `master`. Do not create both an issue ticket and a Pull Request.
6. Wait, I am usually pretty fast to merge PRs :)
Save the package to your devDependencies:
Thanks a lot to all the previous [contributors](https://github.com/i18next/i18next-parser/graphs/contributors).
```
yarn add -D i18next-parser
npm install --save-dev i18next-parser
```
---
## CLI Usage
`i18next /path/to/file/or/dir [-orapfnl]`
- **-o, --output <directory>**: Where to write the locale files.
- **-r, --recursive**: Is --output is a directory, parses files in sub directories.
- **-f, --functions <list>**: Function names to parse. Defaults to `t`
- **-a, --attributes <list>**: HTML attributes to parse. Defaults to `data-i18n`
- **-p, --parser <string>**: A custom regex for the parser to use.
- **-n, --namespace <string>**: Default namespace used in your i18next config. Defaults to `translation`
- **-s, --namespace-separator <string>**: Namespace separator used in your translation keys. Defaults to `:`
- **-k, --key-separator <string>**: Key separator used in your translation keys. Defaults to `.`
- **-c, --context-separator <string>**: Context separator used in your translation keys. Defaults to `_`
- **-l, --locales <list>**: The locales in your applications. Defaults to `en,fr`
- **--directoryFilter**: Globs of folders to filter
- **--fileFilter**: Globs of files to filter
- **--keep-removed**: Prevent keys no longer found from being removed
- **--write-old false**: Avoid saving the \_old files
- **--ignore-variables**: Don't fail when a variable is found
- **--prefix <string>**: Prefix filename for each locale, eg.: 'pre-$LOCALE-' will yield 'pre-en-default.json'
- **--suffix <string>**: Suffix filename for each locale, eg.: '-$suf-LOCALE' will yield 'default-suf-en.json'
- **--extension <string>**: Specify extension for filename for each locale, eg.: '.$LOCALE.i18n' will yield 'default.en.i18n'
---
## Gulp Usage
[Gulp](http://gulpjs.com/) defines itself as the streaming build system. Put simply, it is like Grunt, but performant and elegant.
```javascript
var i18next = require('i18next-parser');
const i18next = require('i18next-parser');
gulp.task('i18next', function() {
gulp.src('app/**')
.pipe(i18next({
locales: ['en', 'de'],
functions: ['__', '_e'],
output: '../locales'
}))
.pipe(gulp.dest('locales'));
gulp.src('app/**')
.pipe(i18next({
locales: ['en', 'de'],
output: '../locales'
}))
.pipe(gulp.dest('locales'));
});
```
- **output**: Where to write the locale files relative to the base (here `app/`). Defaults to `locales`
- **functions**: An array of functions names to parse. Defaults to `['t']`
- **attributes**: An array of html attributes to parse. Defaults to `['data-i18n']`
- **namespace**: Default namespace used in your i18next config. Defaults to `translation`
- **namespaceSeparator**: Namespace separator used in your translation keys. Defaults to `:`
- **keySeparator**: Key separator used in your translation keys. Defaults to `.`
- **locales**: An array of the locales in your applications. Defaults to `['en','fr']`
- **parser**: A custom regex for the parser to use.
- **writeOld**: Save the \_old files. Defaults to `true`
- **prefix**: Add a custom prefix in front of the file name.
- **suffix**: Add a custom suffix at the end of the file name.
- **extension**: Edit the extension of the files. Defaults to `.json`
- **keepRemoved**: Prevent keys no longer found from being removed
- **ignoreVariables**: Don't fail when a variable is found
**IMPORTANT**: `output` is required to know where to read the catalog from. You might think that `gulp.dest()` is enough though it does not inform the transform where to read the existing catalog from.
You can inject the locale tag in either the prefix, suffix or extension using the `$LOCALE` variable.
### Note on paths (why your translations are not saved)
## Options
The way gulp works, it take a `src()`, applies some transformations to the files matched and then render the transformation using the `dest()` command to a path of your choice. With `i18next-parser`, the `src()` takes the path to the files to parse and the `dest()` takes the path where you want the catalogs of translation keys.
Option | Description | Default
---------------------- | ----------------------------------------------------- | ---
**contextSeparator** | Key separator used in your translation keys | `_`
**createOldLibraries** | Save the \_old files | `true`
**defaultNamespace** | Default namespace used in your i18next config | `translation`
**defaultValue** | Default value to give to empty keys | `''`
**extension** | Edit the extension of the locale files | `.json`
**filename** | Edit the filename of the locale files | `'$NAMESPACE'`
**indentation** | Indentation of the catalog files | `2`
**keepRemoved** | Keep keys from the catalog that are no longer in code | `false`
**keySeparator** | Key separator used in your translation keys | `.`
**lexers** | See below for details | `{}`
**lineEnding** | Control the line ending. See options at [eol](https://github.com/ryanve/eol) | `auto`
**locales** | An array of the locales in your applications | `['en','fr']`
**namespaceSeparator** | Namespace separator used in your translation keys | `:`
**output** | Where to write the locale files relative to the base | `locales`
**sort** | Whether or not to sort the catalog | `false`
The problem is that the `i18next()` transform doesn't know about the path you specify in `dest()`. So it doesn't know where the catalogs are. So it can't merge the result of the parsing with the existing catalogs you may have there.
### Catalog filenames
```
gulp.src('app/**')
.pipe(i18next())
.pipe(gulp.dest('custom/path'));
```
Both `filename` and `extension` options support injection of `$LOCALE` and `$NAMESPACE` variables.
If you consider the code above, any file that match the `app/**` pattern will have of base set to `app/`. *As per the vinyl-fs [documentation](https://github.com/wearefractal/vinyl-fs#srcglobs-opt) (which powers gulp), the base is the folder relative to the cwd and defaults is where the glob begins.*
### Lexers
Bare with me, the `output` option isn't defined, it defaults to `locales`. So the `i18next()` transform will look for files in the `app/locales` directory (the base plus the output directory). But in reality they are located in `custom/path`. So for the `i18next-parser` to find your catalogs, you need the `output` option:
The `lexers` option let you configure which Lexer to use for which extension. Here is the default:
```
gulp.src('app/**')
.pipe(i18next({output: '../custom/path'}))
.pipe(gulp.dest('custom/path'));
```
{
lexers: {
hbs: ['HandlebarsLexer'],
handlebars: ['HandlebarsLexer'],
The `output` option is relative to the base. In our case, we have `app/` as a base and we want `custom/path`. So the `output` option must be `../custom/path`.
htm: ['HTMLLexer'],
html: ['HTMLLexer'],
js: ['JavascriptLexer'],
mjs: ['JavascriptLexer'],
### Events
The transform emit a `reading` event for each file it parses:
`.pipe( i18next().on('reading', function(path) { }) )`
The transform emit a `writing` event for each file it passes to the stream:
`.pipe( i18next().on('reading', function(path) { }) )`
The transform emit a `json_error` event if the JSON.parse on json files fail. It is passed the error name (like `SyntaxError`) and the error message (like `Unexpected token }`):
`.pipe( i18next().on('reading', function(name, message) { }) )`
### Errors
- `i18next-parser does not support variables in translation functions, use a string literal`: i18next-parser can't parse keys if you use variables like `t(variable)`, you need to use strings like `t('string')`.
---
## Examples
**Change the output directory (cli and gulp)**
Command line (the options are identical):
`i18next /path/to/file/or/dir -o /output/directory`
`i18next /path/to/file/or/dir:/output/directory`
Gulp:
`.pipe(i18next({output: 'translations'}))`
It will create the file in the specified folder (in case of gulp it doesn't actually create the files until you call `dest()`):
default: ['JavascriptLexer']
}
}
```
/output/directory/en/translation.json
...
```
Note the presence of a `default` which will catch any extension that is not listed. There are 3 lexers available: `HandlebarsLexer`, `HTMLLexer` and `JavascriptLexer`. Each has configurations of its own. If you need to change the defaults, you can do it like so:
**Change the locales (cli and gulp)**
Command line:
`i18next /path/to/file/or/dir -l en,de,sp`
Gulp:
`.pipe(i18next({locales: ['en', 'de', 'sp']}))`
This will create a directory per locale in the output folder:
```
locales/en/...
locales/de/...
locales/sp/...
```
**Change the default namespace (cli and gulp)**
Command line:
`i18next /path/to/file/or/dir -n my_default_namespace`
Gulp:
`.pipe(i18next({namespace: 'my_default_namespace'}))`
This will add all the translation from the default namespace in the following file:
```
locales/en/my_default_namespace.json
...
```
**Change the namespace and key separators (cli and gulp)**
Command line:
`i18next /path/to/file/or/dir -s '?' -k '_'`
Gulp:
`.pipe(i18next({namespaceSeparator: '?', keySeparator: '_'}))`
This parse the translation keys as follow:
```
namespace?key_subkey
namespace.json
{
key: {
subkey: ''
}
lexers: {
hbs: [
{
lexer: 'HandlebarsLexer',
functions: ['translate', '__']
}
],
...
}
}
...
```
**`HandlebarsLexer` options**
Option | Description | Default
------------- | --------------------------- | -------
**functions** | Array of functions to match | `['t']`
**Change the translation functions (cli and gulp)**
**`HTMLLexer` options**
Command line:
Option | Description | Default
-------------- | --------------------------- | -------
**attr** | Attribute for the keys | `'data-i18n'`
**optionAttr** | Attribute for the options | `'data-i18n-options'`
`i18next /path/to/file/or/dir -f __,_e`
**`JavscriptLexer` options**
Gulp:
Option | Description | Default
------------- | --------------------------- | -------
**functions** | Array of functions to match | `['t']`
`.pipe(i18next({functions: ['__', '_e']}))`
This will parse any of the following function calls in your code and extract the key:
```
__('key'
__ 'key'
__("key"
__ "key"
_e('key'
_e 'key'
_e("key"
_e "key"
```
## Events
Note1: we don't match the closing parenthesis as you may want to pass arguments to your translation function.
The transform emits a `reading` event for each file it parses:
Note2: the parser is smart about escaped single or double quotes you may have in your key.
`.pipe( i18next().on('reading', (file) => {}) )`
The transform emits a `error:json` event if the JSON.parse on json files fail:
`.pipe( i18next().on('error:json', (path, error) => {}) )`
**Change the regex (cli and gulp)**
The transform emits a `warning:variable` event if the file has a key that contains a variable:
Command line:
`.pipe( i18next().on('warning:variable', (path, key) => {}) )`
`i18next /path/to/file/or/dir -p "(.*)"`
Gulp:
`.pipe(i18next({parser: '(.*)'}))`
## Contribute
If you use a custom regex, the `functions` option will be ignored. You need to write you regex to parse the functions you want parsed.
Any contribution is welcome. Please [read the guidelines](doc/development.md) first.
You must pass the regex as a string. That means that you will have to properly escape it. Also, the parser will consider the translation key to be the first truthy match of the regex; it means that you must use non capturing blocks `(?:)` for anything that is not the translation key.
The regex used by default is:
`[^a-zA-Z0-9_](?:t)(?:\\(|\\s)\\s*(?:(?:\'((?:(?:\\\\\')?[^\']+)+[^\\\\])\')|(?:"((?:(?:\\\\")?[^"]+)+[^\\\\])"))/g`
**Filter files and folders (cli)**
`i18next /path/to/file/or/dir --fileFilter '*.hbs,*.js' --directoryFilter '!.git'`
In recursive mode, it will parse `*.hbs` and `*.js` files and skip `.git` folder. This options is passed to readdirp. To learn more, read [their documentation](https://github.com/thlorenz/readdirp#filters).
**Work with Meteor TAP-i18N (gulp)**
`.pipe(i18next({
output: "i18n",
locales: ['en', 'de', 'fr', 'es'],
functions: ['_'],
namespace: 'client',
suffix: '.$LOCALE',
extension: ".i18n.json",
writeOld: false
}))`
This will output your files in the format `$LOCALE/client.$LOCALE.i18n.json` in a `i18n/` directory.
Thanks a lot to all the previous [contributors](https://github.com/i18next/i18next-parser/graphs/contributors).

@@ -0,1 +1,3 @@

import _ from 'lodash'
// Takes a `path` of the form 'foo.bar' and

@@ -5,26 +7,23 @@ // turn it into a hash {foo: {bar: ""}}.

// optional `hash`.
function hashFromString(path, separator, value, hash) {
separator = separator || '.';
function dotPathToHash(path, separator = '.', value = '', target = {}) {
if (path.endsWith(separator)) {
path = path.slice(0, -separator.length)
}
if ( path.indexOf( separator, path.length - separator.length ) >= 0 ) {
path = path.slice( 0, -separator.length );
let result = {}
const segments = path.split(separator)
segments.reduce((hash, segment, index) => {
if (index === segments.length - 1) {
hash[segment] = value
}
else {
hash[segment] = {}
}
return hash[segment]
}, result)
var parts = path.split( separator );
var tmp_obj = hash || {};
var obj = tmp_obj;
for( var x = 0; x < parts.length; x++ ) {
if ( x == parts.length - 1 ) {
tmp_obj[parts[x]] = value || '';
}
else if ( ! tmp_obj[parts[x]] ) {
tmp_obj[parts[x]] = {};
}
tmp_obj = tmp_obj[parts[x]];
}
return obj;
return _.merge(target, result)
}
// Takes a `source` hash and make sure its value

@@ -34,75 +33,77 @@ // are pasted in the `target` hash, if the target

// If not, the value is added to an `old` hash.
function mergeHash(source, target, old, keepRemoved) {
target = target || {};
old = old || {};
function mergeHashes(source, target = {}, old, keepRemoved = false) {
old = old || {}
Object.keys(source).forEach(key => {
const hasNestedEntries =
typeof target[key] === 'object' && !Array.isArray(target[key])
Object.keys(source).forEach(function (key) {
if ( target[key] !== undefined ) {
if (typeof source[key] === 'object' && source[key].constructor !== Array) {
var nested = mergeHash( source[key], target[key], old[key], keepRemoved );
target[key] = nested.new;
old[key] = nested.old;
}
else {
target[key] = source[key];
}
}
else {
// support for plural in keys
pluralMatch = /_plural(_\d+)?$/.test( key );
singularKey = key.replace( /_plural(_\d+)?$/, '' );
if (hasNestedEntries) {
const nested = mergeHashes(
source[key],
target[key],
old[key],
keepRemoved
)
target[key] = nested.new
old[key] = nested.old
}
else if (target[key] !== undefined) {
if (typeof source[key] === 'string' || Array.isArray(source[key])) {
target[key] = source[key]
}
else {
old[key] = source[key]
}
}
else {
// support for plural in keys
const pluralMatch = /_plural(_\d+)?$/.test(key)
const singularKey = key.replace(/_plural(_\d+)?$/, '')
// support for context in keys
contextMatch = /_([^_]+)?$/.test( singularKey );
rawKey = singularKey.replace( /_([^_]+)?$/, '' );
// support for context in keys
const contextMatch = /_([^_]+)?$/.test(singularKey)
const rawKey = singularKey.replace(/_([^_]+)?$/, '')
if (
( contextMatch && target[rawKey] !== undefined ) ||
( pluralMatch && target[singularKey] !== undefined )
) {
target[key] = source[key];
}
else {
if (keepRemoved) {
target[key] = source[key];
}
old[key] = source[key];
}
}
});
if (
(contextMatch && target[rawKey] !== undefined) ||
(pluralMatch && target[singularKey] !== undefined)
) {
target[key] = source[key]
}
else if (keepRemoved) {
target[key] = source[key]
old[key] = source[key]
}
else {
old[key] = source[key]
}
}
})
return {
'new': target,
'old': old
};
return { old, new: target }
}
// Takes a `target` hash and replace its empty
// values with the `source` hash ones if they
// exist
function replaceEmpty(source, target) {
target = target || {};
function populateHash(source, target = {}) {
Object.keys(source).forEach(key => {
if (target[key] !== undefined) {
if (typeof source[key] === 'object') {
target[key] = populateHash(source[key], target[key])
}
else if (target[key] === '') {
target[key] = source[key]
}
}
})
Object.keys(source).forEach(function (key) {
if ( target[key] !== undefined ) {
if (typeof source[key] === 'object') {
var nested = replaceEmpty( source[key], target[key] );
target[key] = nested;
}
else if ( target[key] === '' ) {
target[key] = source[key];
}
}
});
return target;
return target
}
module.exports = {
hashFromString: hashFromString,
mergeHash: mergeHash,
replaceEmpty: replaceEmpty
};
export {
dotPathToHash,
mergeHashes,
populateHash
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc