[Pkg-javascript-commits] [node-split-string] 01/01: New upstream version 5.0.4
Julien Puydt
julien.puydt at laposte.net
Sun Mar 11 19:26:49 UTC 2018
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to annotated tag upstream/5.0.4
in repository node-split-string.
commit 2d851d388787c8f6b050ee2de18d7a1318d5fc79
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Fri Feb 23 12:09:49 2018 +0100
New upstream version 5.0.4
---
.eslintrc.json | 13 ++
.gitignore | 1 +
.travis.yml | 4 -
.verb.md | 96 ++++++++-------
CHANGELOG.md | 119 +++++++++++++++++++
LICENSE | 2 +-
README.md | 105 +++++++++-------
example.js | 42 +++++--
index.js | 309 ++++++++++++++++++++++++++++--------------------
package.json | 18 ++-
test.js | 178 ----------------------------
test/custom-function.js | 50 ++++++++
test/options.js | 121 +++++++++++++++++++
test/test.js | 62 ++++++++++
14 files changed, 708 insertions(+), 412 deletions(-)
diff --git a/.eslintrc.json b/.eslintrc.json
index 61e8895..24b8984 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,4 +1,8 @@
{
+ "extends": [
+ "eslint:recommended"
+ ],
+
"env": {
"browser": false,
"es6": true,
@@ -6,6 +10,15 @@
"mocha": true
},
+ "parserOptions":{
+ "ecmaVersion": 9,
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "modules": true,
+ "experimentalObjectRestSpread": true
+ }
+ },
+
"globals": {
"document": false,
"navigator": false,
diff --git a/.gitignore b/.gitignore
index 4bf0a60..f969a2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# always ignore files
*.DS_Store
.idea
+.vscode
*.sublime-*
# test related, or directories generated by tests
diff --git a/.travis.yml b/.travis.yml
index 1686664..1fd107d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,3 @@ node_js:
- '8'
- '7'
- '6'
- - '5'
- - '4'
- - '0.12'
- - '0.10'
diff --git a/.verb.md b/.verb.md
index 3179632..9d21d2a 100644
--- a/.verb.md
+++ b/.verb.md
@@ -13,7 +13,7 @@ console.log('a.b.c'.split('.'));
It's more challenging to split a string whilst respecting escaped or quoted characters.
-**Bad**
+**This is bad**
```js
console.log('a\\.b.c'.split('.'));
@@ -23,7 +23,7 @@ console.log('"a.b.c".d'.split('.'));
//=> ['"a', 'b', 'c"', 'd']
```
-**Good**
+**This is good**
```js
var split = require('{%= name %}');
@@ -105,29 +105,13 @@ split('a.{b.c}', {brackets: true});
split('a.{b.{c.d}.e}.f', {brackets: true});
//=> [ 'a', '{b.{c.d}.e}', 'f' ]
-// support only the specified brackets
-split('[a.b].(c.d)', {brackets: {'[': ']'}});
-//=> [ '[a.b]', '(c', 'd)' ]
+// 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' ]
```
-### options.sep
-
-**Type**: `string`
-
-**Default**: `.`
-
-The separator/character to split on.
-
-**Example**
-
-```js
-split('a.b,c', {sep: ','});
-//=> ['a.b', 'c']
-
-// you can also pass the separator as string as the last argument
-split('a.b,c', ',');
-//=> ['a.b', 'c']
-```
### options.keepEscaping
@@ -204,41 +188,73 @@ split('a.\'b.c.d\'.e', {keepSingleQuotes: true});
//=> ['a', '\'b.c.d\'', 'e']
```
+### options.separator
+
+**Type**: `string`
+
+**Default**: `.`
-## Customizer
+The separator/character to split on. Aliased as `options.sep` for backwards compatibility with versions <4.0.
+
+**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']
+```
+
+### options.split
**Type**: `function`
-**Default**: `undefined`
+**Default**: the default function returns `true`
-Pass a function as the last argument to customize how tokens are added to the array.
+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
-var arr = split('a.b', function(tok) {
- if (tok.arr[tok.arr.length - 1] === 'a') {
- tok.split = false;
+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']
+//=> ['a.b', 'c']
```
-**Properties**
+Note that the [snapdragon-lexer][] instance is exposed as `this` inside the function. See `snapdragon-lexer` for more information and complete API documentation.
+
+## Split function
-The `tok` object has the following properties:
+**Type**: `function`
-- `tok.val` (string) The current value about to be pushed onto the result array
-- `tok.idx` (number) the current index in the string
-- `tok.str` (string) the entire string
-- `tok.arr` (array) the result array
+**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.
-## Release history
+To avoid splitting on a specific separator, add a `token.split()` function to the token and return `false`.
-### v3.0.0 - 2017-06-17
-**Added**
+**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']
+```
-- adds support for brackets
+Note that the [snapdragon-lexer][] instance is exposed as `this` inside the function. See `snapdragon-lexer` for more information and complete API documentation.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..6094999
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,119 @@
+# Release history
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+<details>
+ <summary><strong>Guiding Principles</strong></summary>
+
+- Changelogs are for humans, not machines.
+- There should be an entry for every single version.
+- The same types of changes should be grouped.
+- Versions and sections should be linkable.
+- The latest version comes first.
+- The release date of each versions is displayed.
+- Mention whether you follow Semantic Versioning.
+
+</details>
+
+<details>
+ <summary><strong>Types of changes</strong></summary>
+
+Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
+
+- `Added` for new features.
+- `Changed` for changes in existing functionality.
+- `Deprecated` for soon-to-be removed features.
+- `Removed` for now removed features.
+- `Fixed` for any bug fixes.
+- `Security` in case of vulnerabilities.
+
+</details>
+
+## [5.0.1] - 2018-01-08
+
+- update engine versions
+
+## [5.0.0] - 2018-01-08
+
+- tweak quotes handling
+- refactor to use snapdragon-lexer
+- Merge pull request #5 from jonschlinkert/refactor
+
+## [4.0.0] - 2017-11-22
+
+- allow options.quotes to be an object
+- Merge pull request #4 from jonschlinkert/quotes-object
+
+## [3.1.0] - 2017-11-19
+
+- support `keepEscaping` as a function
+- run `lint-deps` to upgrade deps
+
+## [3.0.2] - 2017-06-21
+
+- fix examples
+
+## [3.0.1] - 2017-06-15
+
+- fix examples
+- remove unnecessary brackets
+
+## [3.0.0] - 2017-06-15
+
+- support brackets
+- support nested brackets
+- update release history, run verb
+
+## [2.1.1] - 2017-05-30
+
+- call custom function on escaped tokens
+
+## [2.1.0] - 2017-04-27
+
+- support backticks
+- update docs
+
+## [2.0.0] - 2017-04-11
+
+- example callback
+- adds support for a sync callback
+- update docs, run verb
+
+## [1.0.1] - 2017-04-11
+
+- fix error message for unclosed quote
+- allow disabling strict closing of quotes
+- add tests for errors and strict option
+- Merge pull request #2 from jonschlinkert/strict
+- add documentation on options.strict
+
+## [1.0.0] - 2017-02-21
+
+- refactor
+- update docs, run `verb`
+
+## [0.1.1] - 2015-08-27
+
+- first commit
+- only use 1 nonchar instead of entire array
+- Merge pull request #1 from doowb/master
+
+[5.0.1]: https://github.com/jonschlinkert/split-string/compare/5.0.0...5.0.1
+[5.0.0]: https://github.com/jonschlinkert/split-string/compare/4.0.0...5.0.0
+[4.0.0]: https://github.com/jonschlinkert/split-string/compare/3.1.0...4.0.0
+[3.1.0]: https://github.com/jonschlinkert/split-string/compare/3.0.2...3.1.0
+[3.0.2]: https://github.com/jonschlinkert/split-string/compare/3.0.1...3.0.2
+[3.0.1]: https://github.com/jonschlinkert/split-string/compare/3.0.0...3.0.1
+[3.0.0]: https://github.com/jonschlinkert/split-string/compare/2.1.1...3.0.0
+[2.1.1]: https://github.com/jonschlinkert/split-string/compare/2.1.0...2.1.1
+[2.1.0]: https://github.com/jonschlinkert/split-string/compare/2.0.0...2.1.0
+[2.0.0]: https://github.com/jonschlinkert/split-string/compare/1.0.1...2.0.0
+[1.0.1]: https://github.com/jonschlinkert/split-string/compare/1.0.0...1.0.1
+[1.0.0]: https://github.com/jonschlinkert/split-string/compare/0.1.1...1.0.0
+
+[Unreleased]: https://github.com/jonschlinkert/split-string/compare/0.1.1...HEAD
+[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog
+
diff --git a/LICENSE b/LICENSE
index e33d14b..f8de063 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2015-2017, Jon Schlinkert.
+Copyright (c) 2015-2018, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 26a6ae1..a6259d1 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ console.log('a.b.c'.split('.'));
It's more challenging to split a string whilst respecting escaped or quoted characters.
-**Bad**
+**This is bad**
```js
console.log('a\\.b.c'.split('.'));
@@ -38,7 +38,7 @@ console.log('"a.b.c".d'.split('.'));
//=> ['"a', 'b', 'c"', 'd']
```
-**Good**
+**This is good**
```js
var split = require('split-string');
@@ -119,28 +119,11 @@ split('a.{b.c}', {brackets: true});
split('a.{b.{c.d}.e}.f', {brackets: true});
//=> [ 'a', '{b.{c.d}.e}', 'f' ]
-// support only the specified brackets
-split('[a.b].(c.d)', {brackets: {'[': ']'}});
-//=> [ '[a.b]', '(c', 'd)' ]
-```
-
-### options.sep
-
-**Type**: `string`
-
-**Default**: `.`
-
-The separator/character to split on.
-
-**Example**
-
-```js
-split('a.b,c', {sep: ','});
-//=> ['a.b', 'c']
-
-// you can also pass the separator as string as the last argument
-split('a.b,c', ',');
-//=> ['a.b', 'c']
+// 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' ]
```
### options.keepEscaping
@@ -218,42 +201,75 @@ split('a.\'b.c.d\'.e', {keepSingleQuotes: true});
//=> ['a', '\'b.c.d\'', 'e']
```
-## Customizer
+### options.separator
+
+**Type**: `string`
+
+**Default**: `.`
+
+The separator/character to split on. Aliased as `options.sep` for backwards compatibility with versions <4.0.
+
+**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']
+```
+
+### options.split
**Type**: `function`
-**Default**: `undefined`
+**Default**: the default function returns `true`
-Pass a function as the last argument to customize how tokens are added to the array.
+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
-var arr = split('a.b', function(tok) {
- if (tok.arr[tok.arr.length - 1] === 'a') {
- tok.split = false;
+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']
+//=> ['a.b', 'c']
```
-**Properties**
+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 `tok` object has the following properties:
+## Split function
-* `tok.val` (string) The current value about to be pushed onto the result array
-* `tok.idx` (number) the current index in the string
-* `tok.str` (string) the entire string
-* `tok.arr` (array) the result array
+**Type**: `function`
-## Release history
+**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.
-### v3.0.0 - 2017-06-17
+To avoid splitting on a specific separator, add a `token.split()` function to the token and return `false`.
-**Added**
+**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']
+```
-* adds support for brackets
+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
@@ -301,21 +317,22 @@ You might also be interested in these projects:
| **Commits** | **Contributor** |
| --- | --- |
-| 34 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 49 | [jonschlinkert](https://github.com/jonschlinkert) |
| 10 | [doowb](https://github.com/doowb) |
### Author
**Jon Schlinkert**
+* [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert)
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
-Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
-_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on November 22, 2017._
\ No newline at end of file
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on February 16, 2018._
\ No newline at end of file
diff --git a/example.js b/example.js
index 37cd1ee..c4eb0b6 100644
--- a/example.js
+++ b/example.js
@@ -1,13 +1,29 @@
-var split = require('./');
-// var arr = split('a.b', function(tok) {
-// if (tok.arr[tok.arr.length - 1] === 'a') {
-// tok.split = false;
-// }
-// });
-// console.log(arr);
-//=> ['a.b']
-
-var brackets = split('a.{a.{b.c}.}.c', {brackets: true});
-var brackets = split('a.{a.{b.c.}.c', {brackets: true});
-console.log(brackets);
-//=> ['a.b']
+const split = require('./');
+
+console.log(split('a.{a.[{b.c}].d}.e', {brackets: {'[': ']'}}));
+//=> [ 'a', '{a', '[{b.c}]', 'd}', 'e' ]
+
+console.log(split('a.{a.{b.c.d}.c}', {brackets: true}));
+//=> [ 'a', '{a.{b.c.d}.c}' ]
+
+const stash1 = split('a.b.c', function(token) {
+ const prev = this.prev();
+ if (prev && prev.value === 'a') {
+ token.split = () => false;
+ }
+});
+console.log(stash1);
+//=> ['a.b', 'c']
+
+const stash2 = split('a.b.c', {
+ split: function(token) {
+ const prev = this.prev();
+ if (prev && prev.value === 'a') {
+ return false;
+ }
+ return true;
+ }
+});
+console.log(stash2);
+//=> ['a.b', 'c']
+
diff --git a/index.js b/index.js
index b440a82..1290587 100644
--- a/index.js
+++ b/index.js
@@ -1,13 +1,18 @@
/*!
* split-string <https://github.com/jonschlinkert/split-string>
*
- * Copyright (c) 2015-2017, Jon Schlinkert.
+ * Copyright (c) 2015-2018, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
-var extend = require('extend-shallow');
+const Lexer = require('snapdragon-lexer');
+const union = require('arr-union');
+const defaults = {
+ brackets: { '<': '>', '(': ')', '[': ']', '{': '}' },
+ quotes: { '"': '"', "'": "'", '`': '`', '“': '”' }
+};
module.exports = function(str, options, fn) {
if (typeof str !== 'string') {
@@ -19,165 +24,215 @@ module.exports = function(str, options, fn) {
options = null;
}
- // allow separator to be defined as a string
- if (typeof options === 'string') {
- options = { sep: options };
- }
+ const opts = Object.assign({ separator: '.' }, options);
+ const sep = opts.sep || opts.separator;
+ const lexer = new Lexer(str, opts);
- var opts = extend({sep: '.'}, options);
- var quotes = opts.quotes || {
- '"': '"',
- "'": "'",
- '`': '`',
- '“': '”'
- };
+ /**
+ * Setup brackets and quotes characters and regex based on options
+ */
- if (Array.isArray(quotes)) {
- quotes = quotes.reduce(function(acc, ele) {
- acc[ele] = ele;
- return acc;
- }, {});
- }
+ const brackets = opts.brackets === true ? defaults.brackets : opts.brackets;
+ const quotes = opts.quotes === true || typeof opts.quotes === 'undefined'
+ ? defaults.quotes
+ : opts.quotes;
- var brackets;
- if (opts.brackets === true) {
- brackets = {
- '<': '>',
- '(': ')',
- '[': ']',
- '{': '}'
- };
- } else if (opts.brackets) {
- brackets = opts.brackets;
- }
+ // brackets
+ const openChars = brackets ? Object.keys(brackets) : [];
+ const closeChars = brackets ? values(brackets) : [];
+ const openStr = brackets ? escape(openChars) : '';
+ const closeStr = brackets ? escape(closeChars) : '';
+
+ // quotes
+ const quoteChars = union(Object.keys(quotes), values(quotes));
+ const quoteStr = quotes ? escape(quoteChars) : '';
+
+ // regex for "text" handler
+ const textRegex = new RegExp('^[^\\\\' + sep + openStr + closeStr + quoteStr + ']+');
- var tokens = [];
- var stack = [];
- var arr = [''];
- var sep = opts.sep;
- var len = str.length;
- var idx = -1;
- var closeIdx;
-
- function expected() {
- if (brackets && stack.length) {
- return brackets[stack[stack.length - 1]];
+ /**
+ * Listener
+ */
+
+ lexer.on('token', token => fn && fn.call(lexer, token));
+ lexer.split = function(token) {
+ if (typeof token.split === 'function') {
+ return token.split.call(this);
}
- }
+ if (typeof this.options.split === 'function') {
+ return this.options.split.call(this, token);
+ }
+ return true;
+ };
- while (++idx < len) {
- var ch = str[idx];
- var next = str[idx + 1];
- var tok = { val: ch, idx: idx, arr: arr, str: str };
- tokens.push(tok);
-
- if (ch === '\\') {
- tok.val = keepEscaping(opts, str, idx) === true ? (ch + next) : next;
- tok.escaped = true;
- if (typeof fn === 'function') {
- fn(tok);
- }
- arr[arr.length - 1] += tok.val;
- idx++;
- continue;
+ /**
+ * 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);
}
+ return token;
+ });
- if (brackets && brackets[ch]) {
- stack.push(ch);
- var e = expected();
- var i = idx + 1;
+ lexer.capture('separator', toRegex(escape(sep.split(''))), function(token) {
+ if (this.split(token) === false || this.isInside('quote') || this.isInside('bracket')) {
+ return token;
+ }
- if (str.indexOf(e, i + 1) !== -1) {
- while (stack.length && i < len) {
- var s = str[++i];
- if (s === '\\') {
- s++;
- continue;
- }
+ const prev = this.prev();
+ if (prev && prev.type === 'separator') {
+ this.stash.push('');
+ }
- if (quotes[s]) {
- i = getClosingQuote(str, quotes[s], i + 1);
- continue;
- }
+ token.value = '';
+ if (!this.stack.length && this.stash.last() !== '') {
+ this.stash.push(token.value);
+ }
+ return token;
+ });
- e = expected();
- if (stack.length && str.indexOf(e, i + 1) === -1) {
- break;
- }
+ lexer.capture('text', textRegex);
- if (brackets[s]) {
- stack.push(s);
- continue;
- }
+ if (quotes) {
+ lexer.capture('quote', toRegex(quoteStr), function(token) {
+ if (this.isInside('bracket')) return token;
- if (e === s) {
- stack.pop();
- }
- }
- }
+ const val = token.match[0];
+ token.append = false;
- closeIdx = i;
- if (closeIdx === -1) {
- arr[arr.length - 1] += ch;
- continue;
+ if (!keepQuotes(val, opts)) {
+ token.value = '';
}
- ch = str.slice(idx, closeIdx + 1);
- tok.val = ch;
- tok.idx = idx = closeIdx;
- }
+ if (this.isClose(val)) {
+ const open = this.stack.pop();
+ open.closed = true;
+ this.unwind(open, true);
+ this.append(token.value);
- if (quotes[ch]) {
- closeIdx = getClosingQuote(str, quotes[ch], idx + 1);
- if (closeIdx === -1) {
- arr[arr.length - 1] += ch;
- continue;
+ } else {
+ token.queue = [];
+ token.isClose = value => value === quotes[val];
+ this.stack.push(token);
}
+ return token;
+ });
+ }
- if (keepQuotes(ch, opts) === true) {
- ch = str.slice(idx, closeIdx + 1);
- } else {
- ch = str.slice(idx + 1, closeIdx);
+ 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);
}
+ return token;
+ });
+ }
+
+ /**
+ * Custom lexer methods
+ */
- tok.val = ch;
- tok.idx = idx = closeIdx;
+ lexer.isClose = function(ch) {
+ const open = this.stack.last();
+ if (open && typeof open.isClose === 'function') {
+ return open.isClose(ch);
}
+ };
- if (typeof fn === 'function') {
- fn(tok, tokens);
- ch = tok.val;
- idx = tok.idx;
+ 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;
}
+ };
+
+ // 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());
+
+ while (token.queue.length) {
+ const val = token.queue.shift();
+ if (append) {
+ this.append(val);
+ continue;
+ }
- if (tok.val === sep && tok.split !== false) {
- arr.push('');
- continue;
+ if (val !== sep) {
+ this.stash.push(val);
+ }
+ }
+ break;
+ default: {
+ break;
+ }
}
+ };
- arr[arr.length - 1] += tok.val;
+ // start tokenizing
+ lexer.tokenize(str);
+
+ // ensure the stack is empty
+ if (lexer.options.strict === true) {
+ lexer.fail();
}
- return arr;
+ lexer.unwind(lexer.stack.pop());
+ lexer.fail();
+ return lexer.stash;
};
-function getClosingQuote(str, ch, i, brackets) {
- var idx = str.indexOf(ch, i);
- if (str.charAt(idx - 1) === '\\') {
- return getClosingQuote(str, ch, idx + 1);
- }
- return idx;
+function toRegex(str) {
+ return new RegExp('^(?=.)[' + str + ']');
}
-function keepQuotes(ch, opts) {
- if (opts.keepDoubleQuotes === true && (ch === '"' || ch === '“' || ch === '”')) return true;
- if (opts.keepSingleQuotes === true && ch === "'") return true;
- return opts.keepQuotes;
+function escape(arr) {
+ return '\\' + arr.join('\\');
}
-function keepEscaping(opts, str, idx) {
- if (typeof opts.keepEscaping === 'function') {
- return opts.keepEscaping(str, idx);
+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 opts.keepEscaping === true || str[idx + 1] === '\\';
+ return false;
}
diff --git a/package.json b/package.json
index c0f2cc3..eac658a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "split-string",
"description": "Split a string on a character except when the character is escaped.",
- "version": "4.0.0",
+ "version": "5.0.4",
"homepage": "https://github.com/jonschlinkert/split-string",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
@@ -18,17 +18,19 @@
],
"main": "index.js",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=8"
},
"scripts": {
- "test": "mocha"
+ "test": "nyc mocha"
},
"dependencies": {
- "extend-shallow": "^3.0.0"
+ "arr-union": "^3.1.0",
+ "snapdragon-lexer": "^3.1.0"
},
"devDependencies": {
"gulp-format-md": "^1.0.0",
- "mocha": "^3.5.3"
+ "mocha": "^3.5.3",
+ "nyc": "^11.4.1"
},
"keywords": [
"character",
@@ -36,6 +38,12 @@
"split",
"string"
],
+ "nyc": {
+ "reporter": [
+ "lcov",
+ "text-summary"
+ ]
+ },
"verb": {
"toc": false,
"layout": "default",
diff --git a/test.js b/test.js
deleted file mode 100644
index 02a3d07..0000000
--- a/test.js
+++ /dev/null
@@ -1,178 +0,0 @@
-'use strict';
-
-require('mocha');
-var assert = require('assert');
-var split = require('./');
-
-describe('split-string', function() {
- describe('split-string', function() {
- it('should split a string on the given character:', function() {
- assert.deepEqual(split('a/b/c', '/'), ['a', 'b', 'c']);
- });
-
- it('should not split on an escaped character:', function() {
- assert.deepEqual(split('a/b/c\\/d', '/'), ['a', 'b', 'c/d']);
- });
-
- it('should split a string on dots by default:', function() {
- assert.deepEqual(split('a.b.c'), ['a', 'b', 'c']);
- });
-
- it('should respect double-quoted strings', function() {
- assert.deepEqual(split('"b.c"'), ['b.c']);
- assert.deepEqual(split('a."b.c"'), ['a', 'b.c']);
- assert.deepEqual(split('a".b.c"'), ['a.b.c']);
- assert.deepEqual(split('a."b.c".d'), ['a', 'b.c', 'd']);
- assert.deepEqual(split('a."b.c".d.".e.f.g.".h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
- });
-
- it('should respect singlequoted strings', function() {
- assert.deepEqual(split('\'b.c\''), ['b.c']);
- assert.deepEqual(split('a.\'b.c\''), ['a', 'b.c']);
- assert.deepEqual(split('a.\'b.c\'.d'), ['a', 'b.c', 'd']);
- assert.deepEqual(split('a.\'b.c\'.d.\'.e.f.g.\'.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
- });
-
- it('should respect strings in backticks', function() {
- assert.deepEqual(split('`b.c`'), ['b.c']);
- assert.deepEqual(split('a.`b.c`'), ['a', 'b.c']);
- assert.deepEqual(split('a.`b.c`.d'), ['a', 'b.c', 'd']);
- assert.deepEqual(split('a.`b.c`.d.`.e.f.g.`.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
- });
-
- it('should respect strings in “” double quotes', function() {
- assert.deepEqual(split('“b.c”'), ['b.c']);
- assert.deepEqual(split('a.“b.c”'), ['a', 'b.c']);
- assert.deepEqual(split('a.“b.c”.d'), ['a', 'b.c', 'd']);
- assert.deepEqual(split('a.“b.c”.d.“.e.f.g.”.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
- });
-
- it('should not split on escaped dots:', function() {
- assert.deepEqual(split('a.b.c\\.d'), ['a', 'b', 'c.d']);
- });
-
- it('should keep escaping when followed by a backslash:', function() {
- assert.deepEqual(split('a.b.c\\\\.d'), ['a', 'b', 'c\\\\', 'd']);
- assert.deepEqual(split('a.b.c\\\\d'), ['a', 'b', 'c\\\\d']);
- });
-
- it('should retain unclosed double quotes in the results', function() {
- assert.deepEqual(split('a."b.c'), ['a', '"b', 'c']);
- });
-
- it('should retain unclosed single quotes in the results', function() {
- assert.deepEqual(split('brian\'s'), ['brian\'s']);
- assert.deepEqual(split('a.\'b.c'), ['a', '\'b', 'c']);
- });
- });
-
- describe('options', function() {
- it('should keep double quotes', function() {
- assert.deepEqual(split('a."b.c".d', {keepDoubleQuotes: true}), ['a', '"b.c"', 'd']);
- });
-
- it('should keep “” double quotes', function() {
- assert.deepEqual(split('a.“b.c”.d', {keepDoubleQuotes: true}), ['a', '“b.c”', 'd']);
- });
-
- it('should not split inside brackets', function() {
- var opts = { brackets: true };
- assert.deepEqual(split('a.(b.c).d', opts), ['a', '(b.c)', 'd']);
- assert.deepEqual(split('a.[(b.c)].d', opts), ['a', '[(b.c)]', 'd']);
- assert.deepEqual(split('a.[b.c].d', opts), ['a', '[b.c]', 'd']);
- assert.deepEqual(split('a.{b.c}.d', opts), ['a', '{b.c}', 'd']);
- assert.deepEqual(split('a.<b.c>.d', opts), ['a', '<b.c>', 'd']);
- });
-
- it('should support nested brackets', function() {
- var opts = { brackets: true };
- assert.deepEqual(split('a.{b.{c}.d}.e', opts), ['a', '{b.{c}.d}', 'e']);
- assert.deepEqual(split('a.{b.{c.d}.e}.f', opts), ['a', '{b.{c.d}.e}', 'f']);
- assert.deepEqual(split('a.{[b.{{c.d}}.e]}.f', opts), ['a', '{[b.{{c.d}}.e]}', 'f']);
- });
-
- it('should support escaped brackets', function() {
- var opts = { brackets: true };
- assert.deepEqual(split('a.\\{b.{c.c}.d}.e', opts), ['a', '{b', '{c.c}', 'd}', 'e']);
- assert.deepEqual(split('a.{b.c}.\\{d.e}.f', opts), ['a', '{b.c}', '{d', 'e}', 'f']);
- });
-
- it('should support quoted brackets', function() {
- var opts = { brackets: true };
- assert.deepEqual(split('a.{b.c}."{d.e}".f', opts), ['a', '{b.c}', '{d.e}', 'f']);
- assert.deepEqual(split('a.{b.c}.{"d.e"}.f', opts), ['a', '{b.c}', '{"d.e"}', 'f']);
- });
-
- it('should ignore imbalanced brackets', function() {
- var opts = { brackets: true };
- assert.deepEqual(split('a.{b.c', opts), ['a', '{b', 'c']);
- assert.deepEqual(split('a.{a.{b.c}.d', opts), ['a', '{a.{b.c}', 'd']);
- });
-
- it('should keep single quotes', function() {
- assert.deepEqual(split('a.\'b.c\'.d', {keepSingleQuotes: true}), ['a', '\'b.c\'', 'd']);
- });
-
- it('should keep escape characters', function() {
- assert.deepEqual(split('a.b\\.c', {keepEscaping: true}), ['a', 'b\\.c']);
- });
-
- it('should split on a custom separator', function() {
- assert.deepEqual(split('a,b,c', {sep: ','}), ['a', 'b', 'c']);
- });
-
- it('should allow custom quotes array', function() {
- assert.deepEqual(split('a.^b.c^', {quotes: ['^']}), ['a', 'b.c']);
- });
-
- it('should allow custom quotes object', function() {
- assert.deepEqual(split('a.^b.c$', {quotes: {'^': '$'}}), ['a', 'b.c']);
- });
- });
-
- describe('function', function() {
- it('should call a custom function on every token', function() {
- function fn(tok, tokens) {
- if (tok.escaped && tok.val === 'b') {
- tok.val = '\\b';
- return;
- }
-
- if (!/[@!*+]/.test(tok.val)) return;
- var stack = [];
- var val = tok.val;
- var str = tok.str;
- var i = tok.idx;
-
- while (++i < str.length) {
- var ch = str[i];
- if (ch === '(') {
- stack.push(ch);
- }
-
- if (ch === ')') {
- stack.pop();
- if (!stack.length) {
- val += ch;
- break;
- }
- }
- val += ch;
- }
-
- tok.split = false;
- tok.val = val;
- tok.idx = i;
- }
-
- var opts = {sep: ',', brackets: null};
- assert.deepEqual(split('a,(\\b,c)', opts, fn), ['a', '(\\b', 'c)']);
- assert.deepEqual(split('a,(b,c)', opts, fn), ['a', '(b', 'c)']);
- assert.deepEqual(split('a,@(b,c)', opts, fn), ['a', '@(b,c)']);
- assert.deepEqual(split('a,@(b,(a,b)c)', opts, fn), ['a', '@(b,(a,b)c)']);
- assert.deepEqual(split('a,@(b,(a,b)c),z', opts, fn), ['a', '@(b,(a,b)c)', 'z']);
- assert.deepEqual(split('a,+(b,c)', opts, fn), ['a', '+(b,c)']);
- assert.deepEqual(split('a,*(b|c,d)', opts, fn), ['a', '*(b|c,d)']);
- });
- });
-});
diff --git a/test/custom-function.js b/test/custom-function.js
new file mode 100644
index 0000000..d096fd5
--- /dev/null
+++ b/test/custom-function.js
@@ -0,0 +1,50 @@
+'use strict';
+
+require('mocha');
+const assert = require('assert');
+const split = require('..');
+
+describe('function', function() {
+ it('should call a custom function on every token', function() {
+ function fn(tok) {
+ if (tok.type === 'escape' && tok.match[0] === '\\b') {
+ tok.keepEscaping = true;
+ return;
+ }
+
+ if (!/[@!*+]/.test(tok.value)) return;
+ const stack = [];
+ const str = this.string;
+ let i = -1;
+
+ while (++i < str.length) {
+ const ch = str[i];
+ if (ch === '(') {
+ stack.push(ch);
+ }
+
+ if (ch === ')') {
+ stack.pop();
+ if (!stack.length) {
+ this.consume(1);
+ tok.value += ch;
+ break;
+ }
+ }
+ this.consume(1);
+ tok.value += ch;
+ }
+ }
+
+ const opts = { separator: ',', brackets: false };
+ assert.deepEqual(split('a.@(b,c)', fn), ['a', '@(b,c)']);
+ assert.deepEqual(split('a,@(b,c)', opts, fn), ['a', '@(b,c)']);
+ assert.deepEqual(split('a,(\\b,c)', opts, fn), ['a', '(\\b', 'c)']);
+ assert.deepEqual(split('a,(b,c)', opts, fn), ['a', '(b', 'c)']);
+ assert.deepEqual(split('a,@(b,(c,d)e)', opts, fn), ['a', '@(b,(c,d)e)']);
+ assert.deepEqual(split('a,@(b,(c,d)e)z', opts, fn), ['a', '@(b,(c,d)e)z']);
+ assert.deepEqual(split('a,@(b,(a,b)c),z', opts, fn), ['a', '@(b,(a,b)c)', 'z']);
+ assert.deepEqual(split('a,+(b,c)', opts, fn), ['a', '+(b,c)']);
+ assert.deepEqual(split('a,*(b|c,d)', opts, fn), ['a', '*(b|c,d)']);
+ });
+});
diff --git a/test/options.js b/test/options.js
new file mode 100644
index 0000000..4790ccf
--- /dev/null
+++ b/test/options.js
@@ -0,0 +1,121 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var split = require('..');
+
+describe('options', function() {
+ describe('separator', function() {
+ it('should split on a custom separator', function() {
+ assert.deepEqual(split('a/b/c', {separator: '/'}), ['a', 'b', 'c']);
+ assert.deepEqual(split('a,b,c', {separator: ','}), ['a', 'b', 'c']);
+ });
+
+ it('should not split on an escaped custom separator:', function() {
+ assert.deepEqual(split('a/b/c\\/d', {separator: '/'}), ['a', 'b', 'c/d']);
+ });
+
+ it('should take a custom function for splitting', function() {
+ const stash = split('a.b.c', function(token) {
+ const prev = this.prev();
+ if (prev && prev.value === 'a') {
+ token.split = () => false;
+ }
+ });
+ assert.deepEqual(stash, ['a.b', 'c']);
+ });
+
+ it('should take a custom options.split function for splitting', function() {
+ const stash = split('a.b.c', {
+ split: function(token) {
+ const prev = this.prev();
+ if (prev && prev.value === 'a') {
+ return false;
+ }
+ return true;
+ }
+ });
+ assert.deepEqual(stash, ['a.b', 'c']);
+ });
+ });
+
+ describe('quotes', function() {
+ it('should disable quotes support', function() {
+ assert.deepEqual(split('a.\'b.c\'."d"', {quotes: false}), ['a', '\'b', 'c\'', '"d"']);
+ });
+
+ it('should keep single quotes', function() {
+ assert.deepEqual(split('a.\'b.c\'."d"', {keepSingleQuotes: true}), ['a', '\'b.c\'', 'd']);
+ assert.deepEqual(split('a.\'b.c\'."d"', {keepQuotes: true}), ['a', '\'b.c\'', '"d"']);
+ });
+
+ it('should keep double quotes', function() {
+ assert.deepEqual(split('a."b.c".d', {keepDoubleQuotes: true}), ['a', '"b.c"', 'd']);
+ assert.deepEqual(split('a."b.c".d', {keepQuotes: true}), ['a', '"b.c"', 'd']);
+ });
+
+ it('should keep “” double quotes', function() {
+ assert.deepEqual(split('a.“b.c”.d', {keepSmartQuotes: true}), ['a', '“b.c”', 'd']);
+ assert.deepEqual(split('a.“b.c”.d', {keepQuotes: true}), ['a', '“b.c”', 'd']);
+ });
+
+ it('should keep backticks', function() {
+ assert.deepEqual(split('a.`b.c`.d', {keepBackticks: true}), ['a', '`b.c`', 'd']);
+ assert.deepEqual(split('a.`b.c`.d', {keepQuotes: true}), ['a', '`b.c`', 'd']);
+ });
+
+ it('should allow custom quotes object', function() {
+ assert.deepEqual(split('a.^b.c$', {quotes: {'^': '$'}}), ['a', 'b.c']);
+ assert.deepEqual(split('a.^b.c^', {quotes: {'^': '^'}}), ['a', 'b.c']);
+ assert.deepEqual(split('a.~b.c~', {quotes: {'~': '~'}}), ['a', 'b.c']);
+ });
+ });
+
+ describe('keepEscaping', function() {
+ it('should keep escape characters', function() {
+ assert.deepEqual(split('a.b\\.c', {keepEscaping: true}), ['a', 'b\\.c']);
+ });
+ });
+
+ describe('brackets', function() {
+ it('should throw when brackets are unclosed', function() {
+ assert.throws(function() {
+ split('a.{a.{b.c.}.c', {brackets: true, strict: true});
+ }, /unclosed/);
+ });
+
+ it('should not split inside brackets', function() {
+ var opts = { brackets: true };
+ assert.deepEqual(split('a.(b.c).d', opts), ['a', '(b.c)', 'd']);
+ assert.deepEqual(split('a.[(b.c)].d', opts), ['a', '[(b.c)]', 'd']);
+ assert.deepEqual(split('a.[b.c].d', opts), ['a', '[b.c]', 'd']);
+ assert.deepEqual(split('a.{b.c}.d', opts), ['a', '{b.c}', 'd']);
+ assert.deepEqual(split('a.<b.c>.d', opts), ['a', '<b.c>', 'd']);
+ });
+
+ it('should support nested brackets', function() {
+ var opts = { brackets: true };
+ assert.deepEqual(split('a.{b.{c}.d}.e', opts), ['a', '{b.{c}.d}', 'e']);
+ assert.deepEqual(split('a.{b.{c.d}.e}.f', opts), ['a', '{b.{c.d}.e}', 'f']);
+ assert.deepEqual(split('a.{[b.{{c.d}}.e]}.f', opts), ['a', '{[b.{{c.d}}.e]}', 'f']);
+ });
+
+ it('should support escaped brackets', function() {
+ var opts = { brackets: true };
+ assert.deepEqual(split('a.\\{b.{c.c}.d}.e', opts), ['a', '{b', '{c.c}', 'd}', 'e']);
+ assert.deepEqual(split('a.{b.c}.\\{d.e}.f', opts), ['a', '{b.c}', '{d', 'e}', 'f']);
+ });
+
+ it('should support quoted brackets', function() {
+ var opts = { brackets: true };
+ assert.deepEqual(split('a.{b.c}."{d.e}".f', opts), ['a', '{b.c}', '{d.e}', 'f']);
+ assert.deepEqual(split('a.{b.c}.{"d.e"}.f', opts), ['a', '{b.c}', '{"d.e"}', 'f']);
+ });
+
+ it('should ignore imbalanced brackets', function() {
+ var opts = { brackets: true };
+ assert.deepEqual(split('a.{b.c', opts), ['a', '{b', 'c']);
+ assert.deepEqual(split('a.{a.{b.c}.d', opts), ['a', '{a', '{b', 'c}', 'd']);
+ });
+ });
+});
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..e1d4eb0
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,62 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var split = require('..');
+
+describe('defaults', function() {
+ it('should throw an error when arguments are invalid', function() {
+ assert.throws(() => split(), /expected/);
+ });
+
+ it('should not split on escaped dots:', function() {
+ assert.deepEqual(split('a.b.c\\.d'), ['a', 'b', 'c.d']);
+ });
+
+ it('should keep escaping when followed by a backslash:', function() {
+ assert.deepEqual(split('a.b.c\\\\.d'), ['a', 'b', 'c\\\\', 'd']);
+ assert.deepEqual(split('a.b.c\\\\d'), ['a', 'b', 'c\\\\d']);
+ });
+
+ it('should split a string on dots by default:', function() {
+ assert.deepEqual(split('a.b.c'), ['a', 'b', 'c']);
+ });
+
+ it('should respect double-quoted strings', function() {
+ assert.deepEqual(split('"b.c"'), ['b.c']);
+ assert.deepEqual(split('a."b.c"'), ['a', 'b.c']);
+ assert.deepEqual(split('a".b.c"'), ['a.b.c']);
+ assert.deepEqual(split('a."b.c".d'), ['a', 'b.c', 'd']);
+ assert.deepEqual(split('a."b.c".d.".e.f.g.".h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
+ });
+
+ it('should respect singlequoted strings', function() {
+ assert.deepEqual(split('\'b.c\''), ['b.c']);
+ assert.deepEqual(split('a.\'b.c\''), ['a', 'b.c']);
+ assert.deepEqual(split('a.\'b.c\'.d'), ['a', 'b.c', 'd']);
+ assert.deepEqual(split('a.\'b.c\'.d.\'.e.f.g.\'.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
+ });
+
+ it('should respect strings in backticks', function() {
+ assert.deepEqual(split('`b.c`'), ['b.c']);
+ assert.deepEqual(split('a.`b.c`'), ['a', 'b.c']);
+ assert.deepEqual(split('a.`b.c`.d'), ['a', 'b.c', 'd']);
+ assert.deepEqual(split('a.`b.c`.d.`.e.f.g.`.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
+ });
+
+ it('should respect strings in double smart-quotes: “”', function() {
+ assert.deepEqual(split('“b.c”'), ['b.c']);
+ assert.deepEqual(split('a.“b.c”'), ['a', 'b.c']);
+ assert.deepEqual(split('a.“b.c”.d'), ['a', 'b.c', 'd']);
+ assert.deepEqual(split('a.“b.c”.d.“.e.f.g.”.h'), ['a', 'b.c', 'd', '.e.f.g.', 'h']);
+ });
+
+ it('should retain unclosed double quotes in the results', function() {
+ assert.deepEqual(split('a."b.c'), ['a', '"b', 'c']);
+ });
+
+ it('should retain unclosed single quotes in the results', function() {
+ assert.deepEqual(split('brian\'s'), ['brian\'s']);
+ assert.deepEqual(split('a.\'b.c'), ['a', '\'b', 'c']);
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-split-string.git
More information about the Pkg-javascript-commits
mailing list