Socket
Socket
Sign inDemoInstall

split-string

Package Overview
Dependencies
Maintainers
2
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

split-string - npm Package Compare versions

Comparing version 5.0.4 to 6.0.0

4

CHANGELOG.md

@@ -35,2 +35,6 @@ # Release history

## [6.0.0] - 2018-08-14
- Refactor to use faster scanner
## [5.0.1] - 2018-01-08

@@ -37,0 +41,0 @@

306

index.js

@@ -1,238 +0,148 @@

/*!
* split-string <https://github.com/jonschlinkert/split-string>
*
* Copyright (c) 2015-2018, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
const Lexer = require('snapdragon-lexer');
const union = require('arr-union');
const defaults = {
brackets: { '<': '>', '(': ')', '[': ']', '{': '}' },
quotes: { '"': '"', "'": "'", '`': '`', '“': '”' }
};
module.exports = (input, options = {}, fn) => {
if (typeof input !== 'string') throw new TypeError('expected a string');
module.exports = function(str, options, fn) {
if (typeof str !== 'string') {
throw new TypeError('expected a string');
}
if (typeof options === 'function') {
fn = options;
options = null;
options = {};
}
const opts = Object.assign({ separator: '.' }, options);
const sep = opts.sep || opts.separator;
const lexer = new Lexer(str, opts);
let separator = options.separator || '.';
let ast = { type: 'root', nodes: [], stash: [''] };
let stack = [ast];
let state = { input, separator, stack };
let string = input;
let value, node;
let i = -1;
/**
* Setup brackets and quotes characters and regex based on options
*/
state.bos = () => i === 0;
state.eos = () => i === string.length;
state.prev = () => string[i - 1];
state.next = () => string[i + 1];
const brackets = opts.brackets === true ? defaults.brackets : opts.brackets;
const quotes = opts.quotes === true || typeof opts.quotes === 'undefined'
? defaults.quotes
: opts.quotes;
let quotes = options.quotes || [];
let openers = options.brackets || {};
// brackets
const openChars = brackets ? Object.keys(brackets) : [];
const closeChars = brackets ? values(brackets) : [];
const openStr = brackets ? escape(openChars) : '';
const closeStr = brackets ? escape(closeChars) : '';
if (options.brackets === true) {
openers = { '[': ']', '(': ')', '{': '}', '<': '>' };
}
if (options.quotes === true) {
quotes = ['"', '\'', '`'];
}
// quotes
const quoteChars = union(Object.keys(quotes), values(quotes));
const quoteStr = quotes ? escape(quoteChars) : '';
let closers = invert(openers);
let keep = options.keep || (value => value !== '\\');
// regex for "text" handler
const textRegex = new RegExp('^[^\\\\' + sep + openStr + closeStr + quoteStr + ']+');
/**
* Listener
*/
lexer.on('token', token => fn && fn.call(lexer, token));
lexer.split = function(token) {
if (typeof token.split === 'function') {
return token.split.call(this);
const block = () => (state.block = stack[stack.length - 1]);
const peek = () => string[i + 1];
const next = () => string[++i];
const append = value => {
state.value = value;
if (value && keep(value, state) !== false) {
state.block.stash[state.block.stash.length - 1] += value;
}
if (typeof this.options.split === 'function') {
return this.options.split.call(this, token);
}
return true;
};
/**
* Handlers
*/
lexer.capture('escape', /^\\(.)/, function(token) {
const keep = token.keepEscaping === true || opts.keepEscaping === true;
if (keep === false && token.value !== '\\\\') {
token.value = token.value.slice(1);
const closeIndex = (value, startIdx) => {
let idx = string.indexOf(value, startIdx);
if (idx > -1 && string[idx - 1] === '\\') {
idx = closeIndex(value, idx + 1);
}
return token;
});
return idx;
};
lexer.capture('separator', toRegex(escape(sep.split(''))), function(token) {
if (this.split(token) === false || this.isInside('quote') || this.isInside('bracket')) {
return token;
}
for (; i < string.length - 1;) {
state.value = value = next();
state.index = i;
block();
const prev = this.prev();
if (prev && prev.type === 'separator') {
this.stash.push('');
// handle escaped characters
if (value === '\\') {
if (peek() === '\\') {
append(value + next());
} else {
// if the next char is not '\\', allow the "append" function
// to determine if the backslashes should be added
append(value);
append(next());
}
continue;
}
token.value = '';
if (!this.stack.length && this.stash.last() !== '') {
this.stash.push(token.value);
}
return token;
});
// handle quoted strings
if (quotes.includes(value)) {
let pos = i + 1;
let idx = closeIndex(value, pos);
lexer.capture('text', textRegex);
if (quotes) {
lexer.capture('quote', toRegex(quoteStr), function(token) {
if (this.isInside('bracket')) return token;
const val = token.match[0];
token.append = false;
if (!keepQuotes(val, opts)) {
token.value = '';
if (idx > -1) {
append(value); // append opening quote
append(string.slice(pos, idx)); // append quoted string
append(string[idx]); // append closing quote
i = idx;
continue;
}
if (this.isClose(val)) {
const open = this.stack.pop();
open.closed = true;
this.unwind(open, true);
this.append(token.value);
append(value);
continue;
}
} else {
token.queue = [];
token.isClose = value => value === quotes[val];
this.stack.push(token);
}
return token;
});
}
// handle opening brackets, if not disabled
if (options.brackets !== false && openers[value]) {
node = { type: 'bracket', nodes: [] };
node.stash = keep(value) !== false ? [value] : [''];
node.parent = state.block;
state.block.nodes.push(node);
stack.push(node);
continue;
}
if (brackets) {
lexer.capture('bracket', toRegex(openStr), function(token) {
token.append = false;
token.queue = [];
token.isClose = value => value === brackets[token.value];
this.stack.push(token);
return token;
});
lexer.capture('bracket.close', toRegex(closeStr), function(token) {
if (this.isClose(token.value)) {
const open = this.stack.pop();
open.value += open.queue.join('');
this.append(open.value);
// handle closing brackets, if not disabled
if (options.brackets !== false && closers[value]) {
if (stack.length === 1) {
append(value);
continue;
}
return token;
});
}
/**
* Custom lexer methods
*/
lexer.isClose = function(ch) {
const open = this.stack.last();
if (open && typeof open.isClose === 'function') {
return open.isClose(ch);
append(value);
node = stack.pop();
block();
append(node.stash.join(''));
continue;
}
};
lexer.append = function(val) {
if (!val) return;
const last = this.stack.last();
if (last && Array.isArray(last.queue)) {
last.queue.push(val);
} else {
this.stash[this.stash.length - 1] += val;
// push separator onto stash
if (value === separator && state.block.type === 'root') {
if (typeof fn === 'function' && fn(state) === false) {
append(value);
continue;
}
state.block.stash.push('');
continue;
}
};
// add queued strings back to the stash
lexer.unwind = function(token, append) {
switch (token && token.type) {
case 'bracket':
const segs = token.queue.join('').split(sep);
this.append(token.value);
this.append(segs.shift());
this.stash = this.stash.concat(segs);
break;
case 'quote':
const quote = token.closed && !keepQuotes(token.match[0], opts) ? '' : token.match[0];
this.append(quote);
this.append(token.queue.shift());
// append value onto the last string on the stash
append(value);
}
while (token.queue.length) {
const val = token.queue.shift();
if (append) {
this.append(val);
continue;
}
node = stack.pop();
if (val !== sep) {
this.stash.push(val);
}
}
break;
default: {
break;
}
while (node !== ast) {
if (options.strict === true) {
let column = i - node.stash.length + 1;
throw new SyntaxError(`Unmatched: "${node.stash[0]}", at column ${column}`);
}
};
// start tokenizing
lexer.tokenize(str);
// ensure the stack is empty
if (lexer.options.strict === true) {
lexer.fail();
value = (node.parent.stash.pop() + node.stash.join('.'));
node.parent.stash = node.parent.stash.concat(value.split('.'));
node = stack.pop();
}
lexer.unwind(lexer.stack.pop());
lexer.fail();
return lexer.stash;
return node.stash;
};
function toRegex(str) {
return new RegExp('^(?=.)[' + str + ']');
function invert(obj) {
let inverted = {};
for (const key of Object.keys(obj)) inverted[obj[key]] = key;
return inverted;
}
function escape(arr) {
return '\\' + arr.join('\\');
}
function values(obj) {
const arr = [];
for (const key of Object.keys(obj)) arr.push(obj[key]);
return arr;
}
function keepQuotes(ch, opts) {
if (opts.keepQuotes === true) return true;
if (opts.keepSmartQuotes === true && (ch === '“' || ch === '”')) {
return true;
}
if (opts.keepDoubleQuotes === true && ch === '"') {
return true;
}
if (opts.keepSingleQuotes === true && ch === '\'') {
return true;
}
if (opts.keepBackticks === true && ch === '`') {
return true;
}
return false;
}
{
"name": "split-string",
"description": "Split a string on a character except when the character is escaped.",
"version": "5.0.4",
"description": "Easy way to split a string on a given character unless it's quoted or escaped.",
"version": "6.0.0",
"homepage": "https://github.com/jonschlinkert/split-string",

@@ -21,3 +21,3 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)",

"engines": {
"node": ">=8"
"node": ">=8.6"
},

@@ -27,10 +27,6 @@ "scripts": {

},
"dependencies": {
"arr-union": "^3.1.0",
"snapdragon-lexer": "^3.1.0"
},
"devDependencies": {
"gulp-format-md": "^1.0.0",
"mocha": "^3.5.3",
"nyc": "^11.4.1"
"mocha": "^5.2.0",
"nyc": "^12.0.2"
},

@@ -37,0 +33,0 @@ "keywords": [

# split-string [![NPM version](https://img.shields.io/npm/v/split-string.svg?style=flat)](https://www.npmjs.com/package/split-string) [![NPM monthly downloads](https://img.shields.io/npm/dm/split-string.svg?style=flat)](https://npmjs.org/package/split-string) [![NPM total downloads](https://img.shields.io/npm/dt/split-string.svg?style=flat)](https://npmjs.org/package/split-string) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/split-string.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/split-string)
> Split a string on a character except when the character is escaped.
> Easy way to split a string on a given character unless it's quoted or escaped.

@@ -15,71 +15,52 @@ Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.

<!-- section: Why use this? -->
## Usage
<details>
<summary><strong>Why use this?</strong></summary>
```js
const split = require('split-string');
<br>
Although it's easy to split on a string:
```js
console.log('a.b.c'.split('.'));
console.log(split('a.b.c'));
//=> ['a', 'b', 'c']
```
It's more challenging to split a string whilst respecting escaped or quoted characters.
// respects escaped characters
console.log(split('a.b.c\\.d'));
//=> ['a', 'b', 'c.d']
**This is bad**
```js
console.log('a\\.b.c'.split('.'));
//=> ['a\\', 'b', 'c']
console.log('"a.b.c".d'.split('.'));
//=> ['"a', 'b', 'c"', 'd']
// respects double-quoted strings
console.log(split('a."b.c.d".e'));
//=> ['a', '"b.c.d"', 'e']
```
**This is good**
## Options
```js
var split = require('split-string');
console.log(split('a\\.b.c'));
//=> ['a.b', 'c']
### options.quotes
console.log(split('"a.b.c".d'));
//=> ['a.b.c', 'd']
```
**Type**: `Array|Boolean`
See the [options](#options) to learn how to choose the separator or retain quotes or escaping.
**Default**: `[]
<br>
**Description**
</details>
Tell split-string not to split inside any of the quote characters specified on the quotes option. Each character signifies both the "opening" and "closing" character to use.
## Usage
```js
var split = require('split-string');
// default behavior
console.log(split('a.b."c.d.e.f.g".h.i'));
//=> [ 'a', 'b', '"c', 'd', 'e', 'f', 'g"', 'h', 'i' ]
split('a.b.c');
//=> ['a', 'b', 'c']
// with quotes
console.log(split('a.b."c.d.e.f.g".h.i', { quotes: ['"'] }));
//=> [ 'a', 'b', '"c.d.e.f.g"', 'h', 'i' ]
// respects escaped characters
split('a.b.c\\.d');
//=> ['a', 'b', 'c.d']
// escaped quotes will be ignored
console.log(split('a.b.\\"c.d."e.f.g".h.i', { quotes: ['"'] }));
//=> [ 'a', 'b', '"c', 'd', '"e.f.g"', 'h', 'i' ]
// respects double-quoted strings
split('a."b.c.d".e');
//=> ['a', 'b.c.d', 'e']
// example of how to exclude non-escaped quotes from the result
let keep = (value, state) => {
return value !== '\\' && (value !== '"' || state.prev() === '\\');
};
console.log(split('a.b.\\"c.d."e.f.g".h.i', { quotes: ['"'], keep }));
//=> [ 'a', 'b', '"c', 'd', 'e.f.g', 'h', 'i' ]
```
**Brackets**
Also respects brackets [unless disabled](#optionsbrackets):
```js
split('a (b c d) e', ' ');
//=> ['a', '(b c d)', 'e']
```
## Options

@@ -89,11 +70,19 @@

**Type**: `object|boolean`
**Type**: `Object|Boolean`
**Default**: `undefined`
**Default**: `{}`
**Description**
If enabled, split-string will not split inside brackets. The following brackets types are supported when `options.brackets` is `true`,
By default, no special significance is given to bracket-like characters (such as square brackets, curly braces, angle brackets, and so on).
```js
// default behavior
console.log(split('a.{b.c}.{d.e}'));
//=> [ 'a', '{b', 'c}', '{d', 'e}' ]
```
When `options.brackets` is `true`, the following brackets types are supported:
```js
{

@@ -107,72 +96,42 @@ '<': '>',

Or, if object of brackets must be passed, each property on the object must be a bracket type, where the property key is the opening delimiter and property value is the closing delimiter.
For example:
**Examples**
```js
// no bracket support by default
split('a.{b.c}');
//=> [ 'a', '{b', 'c}' ]
// support all basic bracket types: "<>{}[]()"
split('a.{b.c}', {brackets: true});
//=> [ 'a', '{b.c}' ]
// also supports nested brackets
split('a.{b.{c.d}.e}.f', {brackets: true});
//=> [ 'a', '{b.{c.d}.e}', 'f' ]
// support only the specified bracket types
split('«a.b».⟨c.d⟩', {brackets: {'«': '»', '⟨': '⟩'}});
//=> [ '«a.b»', '⟨c.d⟩' ]
split('a.{a.[{b.c}].d}.e', {brackets: {'[': ']'}});
//=> [ 'a', '{a', '[{b.c}]', 'd}', 'e' ]
console.log(split('a.{b.c}.{d.e}', { brackets: true }));
//=> [ 'a', '{b.c}', '{d.e}' ]
```
### options.keepEscaping
Alternatively, an object of brackets may be passed, where each key is the _opening bracket_ and each value is the corresponding _closing bracket_. Note that the key and value **must be different characters**. If you want to use the same character for both open and close, use the [quotes option](#optionsquotes).
**Type**: `boolean`
**Examples**
**Default**: `undefined`
Keep backslashes in the result.
**Example**
```js
split('a.b\\.c');
//=> ['a', 'b.c']
// no bracket support by default
console.log(split('a.{b.c}.[d.e].f'));
//=> [ 'a', '{b', 'c}', '[d', 'e]', 'f' ]
split('a.b.\\c', {keepEscaping: true});
//=> ['a', 'b\.c']
```
// tell split-string not to split inside curly braces
console.log(split('a.{b.c}.[d.e].f', { brackets: { '{': '}' }}));
//=> [ 'a', '{b.c}', '[d', 'e]', 'f' ]
### options.keepQuotes
// tell split-string not to split inside any of these types: "<>{}[]()"
console.log(split('a.{b.c}.[d.e].f', { brackets: true }));
//=> [ 'a', '{b.c}', '[d.e]', 'f' ]
**Type**: `boolean`
// ...nested brackets are also supported
console.log(split('a.{b.{c.d}.e}.f', { brackets: true }));
//=> [ 'a', '{b.{c.d}.e}', 'f' ]
**Default**: `undefined`
Keep single- or double-quotes in the result.
**Example**
```js
split('a."b.c.d".e');
//=> ['a', 'b.c.d', 'e']
split('a."b.c.d".e', {keepQuotes: true});
//=> ['a', '"b.c.d"', 'e']
split('a.\'b.c.d\'.e', {keepQuotes: true});
//=> ['a', '\'b.c.d\'', 'e']
// tell split-string not to split inside the given custom types
console.log(split('«a.b».⟨c.d⟩.[e.f]', { brackets: { '«': '»', '⟨': '⟩' } }));
//=> [ '«a.b»', '⟨c.d⟩', '[e', 'f]' ]
```
### options.keepDoubleQuotes
### options.keep
**Type**: `boolean`
**Type**: `function`
**Default**: `undefined`
**Default**: Function that returns true if the character is not `\\`.
Keep double-quotes in the result.
Function that returns true when a character should be retained in the result.

@@ -182,27 +141,8 @@ **Example**

```js
split('a."b.c.d".e');
//=> ['a', 'b.c.d', 'e']
console.log(split('a.b\\.c')); //=> ['a', 'b.c']
split('a."b.c.d".e', {keepDoubleQuotes: true});
//=> ['a', '"b.c.d"', 'e']
// keep all characters
console.log(split('a.b.\\c', { keep: () => true })); //=> ['a', 'b\.c']
```
### options.keepSingleQuotes
**Type**: `boolean`
**Default**: `undefined`
Keep single-quotes in the result.
**Example**
```js
split('a.\'b.c.d\'.e');
//=> ['a', 'b.c.d', 'e']
split('a.\'b.c.d\'.e', {keepSingleQuotes: true});
//=> ['a', '\'b.c.d\'', 'e']
```
### options.separator

@@ -214,3 +154,3 @@

The separator/character to split on. Aliased as `options.sep` for backwards compatibility with versions <4.0.
The character to split on.

@@ -220,61 +160,30 @@ **Example**

```js
split('a.b,c', {separator: ','});
//=> ['a.b', 'c']
// you can also pass the separator as a string as the last argument
split('a.b,c', ',');
//=> ['a.b', 'c']
console.log(split('a.b,c', { separator: ',' })); //=> ['a.b', 'c']
```
### options.split
## Split function
**Type**: `function`
Optionally pass a function as the last argument to tell split-string whether or not to split when the specified separator is encountered.
**Default**: the default function returns `true`
Pass a custom function to be called each time a separator is encountered. If the function returns `false` the string will not be split on that separator.
**Example**
```js
const arr = split('a.b.c', {
split: function() {
const prev = this.prev();
if (prev && prev.value === 'a') {
return false;
}
return true;
}
});
console.log(arr);
//=> ['a.b', 'c']
// only split on "." when the "previous" character is "a"
console.log(split('a.b.c.a.d.e', state => state.prev() === 'a'));
//=> [ 'a', 'b.c.a', 'd.e' ]
```
Note that the [snapdragon-lexer](https://github.com/here-be/snapdragon-lexer) instance is exposed as `this` inside the function. See `snapdragon-lexer` for more information and complete API documentation.
The `state` object exposes the following properties:
## Split function
* `input` - (String) The un-modified, user-defined input string
* `separator` - (String) the specified separator to split on.
* `index` - (Number) The current cursor position
* `value` - (String) The character at the current index
* `bos` - (Function) Returns true if position is at the beginning-of-string
* `eos` - (Function) Returns true if position is at the end-of-string
* `prev` - (Function) Returns the previously scanned character
* `next` - (Function) Returns the next character after the current position
* `block` - (Object) The "current" AST node.
* `stack` - (Array) AST nodes
**Type**: `function`
**Default**: `undefined`
Pass a custom function as the last argument to customize how and when the string should be split. The function will be called each time a separator is encountered.
To avoid splitting on a specific separator, add a `token.split()` function to the token and return `false`.
**Example**
```js
const arr = split('a.b.c', function(token) {
const prev = this.prev();
if (prev && prev.value === 'a') {
token.split = () => false;
}
});
console.log(arr);
//=> ['a.b', 'c']
```
Note that the [snapdragon-lexer](https://github.com/here-be/snapdragon-lexer) instance is exposed as `this` inside the function. See `snapdragon-lexer` for more information and complete API documentation.
## About

@@ -326,3 +235,3 @@

| --- | --- |
| 49 | [jonschlinkert](https://github.com/jonschlinkert) |
| 52 | [jonschlinkert](https://github.com/jonschlinkert) |
| 10 | [doowb](https://github.com/doowb) |

@@ -334,5 +243,5 @@

* [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert)
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)

@@ -346,2 +255,2 @@ ### License

_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on February 16, 2018._
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on August 14, 2018._

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