New Upstream Release - node-pascalcase

Ready changes

Summary

Merged new upstream version: 2.0.0 (was: 1.0.0).

Diff

diff --git a/.eslintrc.json b/.eslintrc.json
index 24b8984..6c1dbda 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,29 +4,18 @@
   ],
 
   "env": {
-    "browser": false,
-    "es6": true,
-    "node": true,
-    "mocha": true
+    "es2021": true,
+    "node": true
   },
 
-  "parserOptions":{
-    "ecmaVersion": 9,
-    "sourceType": "module",
-    "ecmaFeatures": {
-      "modules": true,
-      "experimentalObjectRestSpread": true
-    }
-  },
-
-  "globals": {
-    "document": false,
-    "navigator": false,
-    "window": false
+  "parserOptions": {
+    "ecmaVersion": 12,
+    "sourceType": "module"
   },
 
   "rules": {
     "accessor-pairs": 2,
+    "arrow-parens": [2, "as-needed"],
     "arrow-spacing": [2, { "before": true, "after": true }],
     "block-spacing": [2, "always"],
     "brace-style": [2, "1tbs", { "allowSingleLine": true }],
@@ -39,7 +28,7 @@
     "eol-last": 2,
     "eqeqeq": [2, "allow-null"],
     "generator-star-spacing": [2, { "before": true, "after": true }],
-    "handle-callback-err": [2, "^(err|error)$" ],
+    "handle-callback-err": [2, "^(err|error)$"],
     "indent": [2, 2, { "SwitchCase": 1 }],
     "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
     "keyword-spacing": [2, { "before": true, "after": true }],
@@ -49,6 +38,7 @@
     "no-caller": 2,
     "no-class-assign": 2,
     "no-cond-assign": 2,
+    "no-console": 0,
     "no-const-assign": 2,
     "no-control-regex": 2,
     "no-debugger": 2,
@@ -68,6 +58,7 @@
     "no-floating-decimal": 2,
     "no-func-assign": 2,
     "no-implied-eval": 2,
+    "no-implicit-coercion": 2,
     "no-inner-declarations": [2, "functions"],
     "no-invalid-regexp": 2,
     "no-irregular-whitespace": 2,
@@ -75,11 +66,12 @@
     "no-label-var": 2,
     "no-labels": 2,
     "no-lone-blocks": 2,
+    "no-lonely-if": 2,
     "no-mixed-spaces-and-tabs": 2,
-    "no-multi-spaces": 2,
+    "no-multi-spaces": 0,
     "no-multi-str": 2,
     "no-multiple-empty-lines": [2, { "max": 1 }],
-    "no-native-reassign": 0,
+    "no-native-reassign": 2,
     "no-negated-in-lhs": 2,
     "no-new": 2,
     "no-new-func": 2,
@@ -89,7 +81,7 @@
     "no-obj-calls": 2,
     "no-octal": 2,
     "no-octal-escape": 2,
-    "no-proto": 0,
+    "no-proto": 2,
     "no-redeclare": 2,
     "no-regex-spaces": 2,
     "no-return-assign": 2,
@@ -100,28 +92,32 @@
     "no-sparse-arrays": 2,
     "no-this-before-super": 2,
     "no-throw-literal": 2,
-    "no-trailing-spaces": 0,
+    "no-trailing-spaces": 2,
     "no-undef": 2,
     "no-undef-init": 2,
     "no-unexpected-multiline": 2,
     "no-unneeded-ternary": [2, { "defaultAssignment": false }],
     "no-unreachable": 2,
-    "no-unused-vars": [2, { "vars": "all", "args": "none" }],
-    "no-useless-call": 0,
+    "no-unused-expressions": 2,
+    "no-unused-vars": [2, { "vars": "all", "args": "all" }],
+    "no-useless-call": 2,
     "no-with": 2,
-    "one-var": [0, { "initialized": "never" }],
+    "object-curly-spacing": ["error", "always", { "objectsInObjects": true }],
+    "one-var": [2, { "initialized": "never" }],
     "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
     "padded-blocks": [0, "never"],
+    "prefer-const": [2, { "destructuring": "all", "ignoreReadBeforeAssign": false }],
     "quotes": [2, "single", "avoid-escape"],
     "radix": 2,
     "semi": [2, "always"],
     "semi-spacing": [2, { "before": false, "after": true }],
     "space-before-blocks": [2, "always"],
-    "space-before-function-paren": [2, "never"],
+    "space-before-function-paren": [2, { "anonymous": "never", "named": "never", "asyncArrow": "always" }],
     "space-in-parens": [2, "never"],
     "space-infix-ops": 2,
     "space-unary-ops": [2, { "words": true, "nonwords": false }],
     "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
+    "strict": 2,
     "use-isnan": 2,
     "valid-typeof": 2,
     "wrap-iife": [2, "any"],
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..660957e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# Enforce Unix newlines
+* text eol=lf
+
+# binaries
+*.ai binary
+*.psd binary
+*.jpg binary
+*.gif binary
+*.png binary
+*.jpeg binary
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..e0b5360
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,23 @@
+name: Tests
+on: [push, pull_request]
+
+env:
+  CI: true
+
+jobs:
+  test:
+    name: Node.js ${{ matrix.node-version }} @ ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
+        node-version: [14, 16, 17]
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+      - run: npm install
+      - run: npm test
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..223f13b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# always ignore files
+*.DS_Store
+.idea
+.vscode
+*.sublime-*
+
+# test related, or directories generated by tests
+test/actual
+actual
+coverage
+.nyc*
+
+# npm
+node_modules
+npm-debug.log
+
+# yarn
+yarn.lock
+yarn-error.log
+
+# misc
+_gh_pages
+_draft
+_drafts
+bower_components
+vendor
+temp
+tmp
+package-lock.json
diff --git a/.verb.md b/.verb.md
index 7cae3ec..f301328 100644
--- a/.verb.md
+++ b/.verb.md
@@ -1,7 +1,13 @@
+## Prerequisites
+
+Requires [Node.js](https://nodejs.org/en/) version >= 14.
+
 ## Usage
 
 ```js
-const pascalcase = require('{%= name %}');
+import pascalcase from '{%= name %}';
+// or the following if you prefer
+import { pascalcase } from '{%= name %}';
 
 console.log(pascalcase('a')); //=> 'A'
 console.log(pascalcase('foo bar baz')); //=> 'FooBarBaz'
@@ -16,3 +22,19 @@ console.log(pascalcase('_foo_bar-baz-')); //=> 'FooBarBaz'
 console.log(pascalcase('foo 2 bar 5 baz')); //=> 'Foo2Bar5Baz'
 console.log(pascalcase('foo2bar5baz')); //=> 'Foo2bar5baz'
 ```
+
+## Uppercase character sequences
+
+If you wish to preserve upper case character sequences, you may pass an options object with the `preserveConsecutiveUppercase` option set to `true`:
+
+```js
+console.log(pascalcase('The IRS Is Mean')); //=> 'TheIrsIsMean
+console.log(pascalcase('The IRS Is Mean', { preserveConsecutiveUppercase: true })); //=> TheIRSIsMean
+
+console.log(pascalcase('We saw a UFO')); //=> WeSawAUfo
+console.log(pascalcase('We saw a UFO', { preserveConsecutiveUppercase: true })); //=> WeSawAUFO
+```
+
+## Options
+
+All options are passed to [sindresorhus/camelcase](https://github.com/sindresorhus/camelcase), please visit that project to learn about all available options.
diff --git a/LICENSE b/LICENSE
index 7cccaf9..4701ed7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2015-present, Jon Schlinkert.
+Copyright (c) Jon Schlinkert <jon.schlinkert@gmail.com> (https://jonschlinkert.com)
 
 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 ac4b99e..c39fb2b 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,27 @@
-# pascalcase [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/pascalcase.svg?style=flat)](https://www.npmjs.com/package/pascalcase) [![NPM monthly downloads](https://img.shields.io/npm/dm/pascalcase.svg?style=flat)](https://npmjs.org/package/pascalcase) [![NPM total downloads](https://img.shields.io/npm/dt/pascalcase.svg?style=flat)](https://npmjs.org/package/pascalcase) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/pascalcase.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/pascalcase)
+# pascalcase [![NPM version](https://img.shields.io/npm/v/pascalcase.svg?style=flat)](https://www.npmjs.com/package/pascalcase) [![NPM monthly downloads](https://img.shields.io/npm/dm/pascalcase.svg?style=flat)](https://npmjs.org/package/pascalcase) [![NPM total downloads](https://img.shields.io/npm/dt/pascalcase.svg?style=flat)](https://npmjs.org/package/pascalcase)  [![Tests](https://github.com/jonschlinkert/pascalcase/actions/workflows/main.yml/badge.svg)](https://github.com/jonschlinkert/pascalcase/actions/workflows/main.yml)
 
-> Convert a string to pascal-case.
+> Convert a string to pascal case (upper camelcase).
 
 Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
 
 ## Install
 
-Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=8):
+Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=14):
 
 ```sh
 $ npm install --save pascalcase
 ```
 
+## Prerequisites
+
+Requires [Node.js](https://nodejs.org/en/) version >= 14.
+
 ## Usage
 
 ```js
-const pascalcase = require('pascalcase');
+import pascalcase from 'pascalcase';
+// or the following if you prefer
+import { pascalcase } from 'pascalcase';
 
 console.log(pascalcase('a')); //=> 'A'
 console.log(pascalcase('foo bar baz')); //=> 'FooBarBaz'
@@ -31,6 +37,22 @@ console.log(pascalcase('foo 2 bar 5 baz')); //=> 'Foo2Bar5Baz'
 console.log(pascalcase('foo2bar5baz')); //=> 'Foo2bar5baz'
 ```
 
+## Uppercase character sequences
+
+If you wish to preserve upper case character sequences, you may pass an options object with the `preserveConsecutiveUppercase` option set to `true`:
+
+```js
+console.log(pascalcase('The IRS Is Mean')); //=> 'TheIrsIsMean
+console.log(pascalcase('The IRS Is Mean', { preserveConsecutiveUppercase: true })); //=> TheIRSIsMean
+
+console.log(pascalcase('We saw a UFO')); //=> WeSawAUfo
+console.log(pascalcase('We saw a UFO', { preserveConsecutiveUppercase: true })); //=> WeSawAUFO
+```
+
+## Options
+
+All options are passed to [sindresorhus/camelcase](https://github.com/sindresorhus/camelcase), please visit that project to learn about all available options.
+
 ## About
 
 <details>
@@ -83,9 +105,9 @@ You might also be interested in these projects:
 
 ### License
 
-Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
+Copyright © 2021, [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.8.0, on August 08, 2019._
\ No newline at end of file
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on December 29, 2021._
\ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
index 8316c6a..ff6b563 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,9 @@
-node-pascalcase (1.0.0-2) UNRELEASED; urgency=medium
+node-pascalcase (2.0.0-1) UNRELEASED; urgency=medium
 
   * Update standards version to 4.6.2, no changes needed.
+  * New upstream release.
 
- -- Debian Janitor <janitor@jelmer.uk>  Sun, 08 Jan 2023 14:18:56 -0000
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 11 Jun 2023 07:44:34 -0000
 
 node-pascalcase (1.0.0-1) unstable; urgency=medium
 
diff --git a/index.js b/index.js
index 908532a..6541fd0 100644
--- a/index.js
+++ b/index.js
@@ -1,24 +1,30 @@
-/*!
- * pascalcase <https://github.com/jonschlinkert/pascalcase>
- *
- * Copyright (c) 2015-present, Jon ("Schlink") Schlinkert.
- * Licensed under the MIT License.
- */
-
-const titlecase = input => input[0].toLocaleUpperCase() + input.slice(1);
-
-module.exports = value => {
-  if (value === null || value === void 0) return '';
-  if (typeof value.toString !== 'function') return '';
-
-  let input = value.toString().trim();
-  if (input === '') return '';
-  if (input.length === 1) return input.toLocaleUpperCase();
-
-  let match = input.match(/[a-zA-Z0-9]+/g);
-  if (match) {
-    return match.map(m => titlecase(m)).join('');
+import camelcase from 'camelcase';
+
+const PUNCTUATION = /[^\p{L}\p{N}]+/ug;
+
+const toString = input => {
+  if (input == null) return '';
+
+  if (Array.isArray(input)) {
+    return input.map(s => s.toString().trim()).filter(s => s.length > 0).join(' ');
+  }
+
+  if (typeof input === 'function') {
+    return input.name ? input.name : '';
   }
 
-  return input;
+  if (typeof input.toString !== 'function') {
+    return '';
+  }
+
+  return input.toString().trim();
 };
+
+export const pascalcase = (value, options = {}) => {
+  const input = toString(value);
+  const regex = options.punctuationRegex ?? PUNCTUATION;
+  const output = input ? camelcase(regex ? input.replace(regex, ' ') : input, options) : '';
+  return output ? output[0].toLocaleUpperCase(options.locale) + output.slice(1) : '';
+};
+
+export default pascalcase;
diff --git a/package.json b/package.json
index 6544025..055cb54 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "pascalcase",
-  "description": "Convert a string to pascal-case.",
-  "version": "1.0.0",
+  "description": "Convert a string to pascal case (upper camelcase).",
+  "version": "2.0.0",
   "homepage": "https://github.com/jonschlinkert/pascalcase",
   "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
   "repository": "jonschlinkert/pascalcase",
@@ -9,21 +9,24 @@
     "url": "https://github.com/jonschlinkert/pascalcase/issues"
   },
   "license": "MIT",
+  "type": "module",
+  "main": "index.js",
   "files": [
     "index.js"
   ],
-  "main": "index.js",
   "engines": {
-    "node": ">=8"
+    "node": ">=14"
   },
   "scripts": {
     "test": "mocha"
   },
   "devDependencies": {
     "gulp-format-md": "^2.0.0",
-    "mocha": "^6.2.0"
+    "mocha": "^9.1.3"
   },
   "keywords": [
+    "change case",
+    "camel",
     "camelcase",
     "case",
     "casing",
@@ -32,6 +35,7 @@
     "pascal case",
     "pascalcase",
     "string",
+    "upper camel case",
     "UpperCamelCase"
   ],
   "verb": {
@@ -54,5 +58,8 @@
     "lint": {
       "reflinks": true
     }
+  },
+  "dependencies": {
+    "camelcase": "^6.2.1"
   }
 }
diff --git a/test/.eslintrc.json b/test/.eslintrc.json
new file mode 100644
index 0000000..c0312cc
--- /dev/null
+++ b/test/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+  "extends": ["../.eslintrc.json"],
+  "env": {
+    "mocha": true
+  }
+}
diff --git a/test/from-camelcase-lib.js b/test/from-camelcase-lib.js
new file mode 100644
index 0000000..ac60a20
--- /dev/null
+++ b/test/from-camelcase-lib.js
@@ -0,0 +1,218 @@
+import { strict as assert } from 'assert';
+import pascal from '../index.js';
+
+/**
+ * These tests were ported from sindresorhus/camelcase, since pascalcase
+ * depends on that lib and we need to test for regressions and compat.
+ */
+
+const pascalcase = (input, options = {}) => {
+  return pascal(input, { ...options, punctuationRegex: false });
+};
+
+describe('pascalcase (tests ported from sindresorhus/camelcase)', () => {
+  it('should pascalcase a string', () => {
+    assert.equal(pascalcase('foo'), 'Foo');
+    assert.equal(pascalcase('foo-bar'), 'FooBar');
+    assert.equal(pascalcase('foo-bar-baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foo--bar'), 'FooBar');
+    assert.equal(pascalcase('--foo-bar'), 'FooBar');
+    assert.equal(pascalcase('--foo--bar'), 'FooBar');
+    assert.equal(pascalcase('FOO-BAR'), 'FooBar');
+    assert.equal(pascalcase('FOÈ-BAR'), 'FoèBar');
+    assert.equal(pascalcase('-foo-bar-'), 'FooBar');
+    assert.equal(pascalcase('--foo--bar--'), 'FooBar');
+    assert.equal(pascalcase('foo-1'), 'Foo1');
+    assert.equal(pascalcase('foo.bar'), 'FooBar');
+    assert.equal(pascalcase('foo..bar'), 'FooBar');
+    assert.equal(pascalcase('..foo..bar..'), 'FooBar');
+    assert.equal(pascalcase('foo_bar'), 'FooBar');
+    assert.equal(pascalcase('__foo__bar__'), 'FooBar');
+    assert.equal(pascalcase('foo bar'), 'FooBar');
+    assert.equal(pascalcase('  foo  bar  '), 'FooBar');
+    assert.equal(pascalcase('-'), '-');
+    assert.equal(pascalcase(' - '), '-');
+    assert.equal(pascalcase('fooBar'), 'FooBar');
+    assert.equal(pascalcase('fooBar-baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foìBar-baz'), 'FoìBarBaz');
+    assert.equal(pascalcase('fooBarBaz-bazzy'), 'FooBarBazBazzy');
+    assert.equal(pascalcase('FBBazzy'), 'FbBazzy');
+    assert.equal(pascalcase('F'), 'F');
+    assert.equal(pascalcase('FooBar'), 'FooBar');
+    assert.equal(pascalcase('Foo'), 'Foo');
+    assert.equal(pascalcase('FOO'), 'Foo');
+    assert.equal(pascalcase(['foo', 'bar']), 'FooBar');
+    assert.equal(pascalcase(['foo', '-bar']), 'FooBar');
+    assert.equal(pascalcase(['foo', '-bar', 'baz']), 'FooBarBaz');
+    assert.equal(pascalcase(['', '']), '');
+    assert.equal(pascalcase('--'), '');
+    assert.equal(pascalcase(''), '');
+    assert.equal(pascalcase('--__--_--_'), '');
+    assert.equal(pascalcase(['---_', '--', '', '-_- ']), '');
+    assert.equal(pascalcase('..foo..bar..'), 'FooBar');
+    assert.equal(pascalcase('foo bar?'), 'FooBar?');
+    assert.equal(pascalcase('foo bar!'), 'FooBar!');
+    assert.equal(pascalcase('foo bar$'), 'FooBar$');
+    assert.equal(pascalcase('foo-bar#'), 'FooBar#');
+    assert.equal(pascalcase('XMLHttpRequest'), 'XmlHttpRequest');
+    assert.equal(pascalcase('AjaxXMLHttpRequest'), 'AjaxXmlHttpRequest');
+    assert.equal(pascalcase('Ajax-XMLHttpRequest'), 'AjaxXmlHttpRequest');
+    assert.equal(pascalcase([]), '');
+    assert.equal(pascalcase('mGridCol6@md'), 'MGridCol6@md');
+    assert.equal(pascalcase('A::a'), 'A::a');
+    assert.equal(pascalcase('Hello1World'), 'Hello1World');
+    assert.equal(pascalcase('Hello11World'), 'Hello11World');
+    assert.equal(pascalcase('hello1world'), 'Hello1World');
+    assert.equal(pascalcase('Hello1World11foo'), 'Hello1World11Foo');
+    assert.equal(pascalcase('Hello1'), 'Hello1');
+    assert.equal(pascalcase('hello1'), 'Hello1');
+    assert.equal(pascalcase('1Hello'), '1Hello');
+    assert.equal(pascalcase('1hello'), '1Hello');
+    assert.equal(pascalcase('h2w'), 'H2W');
+    assert.equal(pascalcase('розовый_пушистый-единороги'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('розовый_пушистый-единороги'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('РОЗОВЫЙ_ПУШИСТЫЙ-ЕДИНОРОГИ'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('桑德在这里。'), '桑德在这里。');
+    assert.equal(pascalcase('桑德在这里。'), '桑德在这里。');
+    assert.equal(pascalcase('桑德_在这里。'), '桑德在这里。');
+  });
+
+  it('pascalcase with pascalCase option', () => {
+    assert.equal(pascalcase('foo'), 'Foo');
+    assert.equal(pascalcase('foo-bar'), 'FooBar');
+    assert.equal(pascalcase('foo-bar-baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foo--bar'), 'FooBar');
+    assert.equal(pascalcase('--foo-bar'), 'FooBar');
+    assert.equal(pascalcase('--foo--bar'), 'FooBar');
+    assert.equal(pascalcase('FOO-BAR'), 'FooBar');
+    assert.equal(pascalcase('FOÈ-BAR'), 'FoèBar');
+    assert.equal(pascalcase('-foo-bar-'), 'FooBar');
+    assert.equal(pascalcase('--foo--bar--'), 'FooBar');
+    assert.equal(pascalcase('foo-1'), 'Foo1');
+    assert.equal(pascalcase('foo.bar'), 'FooBar');
+    assert.equal(pascalcase('foo..bar'), 'FooBar');
+    assert.equal(pascalcase('..foo..bar..'), 'FooBar');
+    assert.equal(pascalcase('foo_bar'), 'FooBar');
+    assert.equal(pascalcase('__foo__bar__'), 'FooBar');
+    assert.equal(pascalcase('__foo__bar__'), 'FooBar');
+    assert.equal(pascalcase('foo bar'), 'FooBar');
+    assert.equal(pascalcase('  foo  bar  '), 'FooBar');
+    assert.equal(pascalcase('-'), '-');
+    assert.equal(pascalcase(' - '), '-');
+    assert.equal(pascalcase('fooBar'), 'FooBar');
+    assert.equal(pascalcase('fooBar-baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foìBar-baz'), 'FoìBarBaz');
+    assert.equal(pascalcase('fooBarBaz-bazzy'), 'FooBarBazBazzy');
+    assert.equal(pascalcase('FBBazzy'), 'FbBazzy');
+    assert.equal(pascalcase('F'), 'F');
+    assert.equal(pascalcase('FooBar'), 'FooBar');
+    assert.equal(pascalcase('Foo'), 'Foo');
+    assert.equal(pascalcase('FOO'), 'Foo');
+    assert.equal(pascalcase(['foo', 'bar']), 'FooBar');
+    assert.equal(pascalcase(['foo', '-bar']), 'FooBar');
+    assert.equal(pascalcase(['foo', '-bar', 'baz']), 'FooBarBaz');
+    assert.equal(pascalcase(['', '']), '');
+    assert.equal(pascalcase('--'), '');
+    assert.equal(pascalcase(''), '');
+    assert.equal(pascalcase('--__--_--_'), '');
+    assert.equal(pascalcase(['---_', '--', '', '-_- ']), '');
+    assert.equal(pascalcase('foo bar?'), 'FooBar?');
+    assert.equal(pascalcase('foo bar!'), 'FooBar!');
+    assert.equal(pascalcase('foo bar$'), 'FooBar$');
+    assert.equal(pascalcase('foo-bar#'), 'FooBar#');
+    assert.equal(pascalcase('XMLHttpRequest'), 'XmlHttpRequest');
+    assert.equal(pascalcase('AjaxXMLHttpRequest'), 'AjaxXmlHttpRequest');
+    assert.equal(pascalcase('Ajax-XMLHttpRequest'), 'AjaxXmlHttpRequest');
+    assert.equal(pascalcase([]), '');
+    assert.equal(pascalcase('mGridCol6@md'), 'MGridCol6@md');
+    assert.equal(pascalcase('A::a'), 'A::a');
+    assert.equal(pascalcase('Hello1World'), 'Hello1World');
+    assert.equal(pascalcase('Hello11World'), 'Hello11World');
+    assert.equal(pascalcase('hello1world'), 'Hello1World');
+    assert.equal(pascalcase('hello1World'), 'Hello1World');
+    assert.equal(pascalcase('hello1'), 'Hello1');
+    assert.equal(pascalcase('Hello1'), 'Hello1');
+    assert.equal(pascalcase('1hello'), '1Hello');
+    assert.equal(pascalcase('1Hello'), '1Hello');
+    assert.equal(pascalcase('h1W'), 'H1W');
+    assert.equal(pascalcase('РозовыйПушистыйЕдинороги'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('розовый_пушистый-единороги'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('РОЗОВЫЙ_ПУШИСТЫЙ-ЕДИНОРОГИ'), 'РозовыйПушистыйЕдинороги');
+    assert.equal(pascalcase('桑德在这里。'), '桑德在这里。');
+    assert.equal(pascalcase('桑德_在这里。'), '桑德在这里。');
+  });
+
+  it('pascalcase with preserveConsecutiveUppercase option', () => {
+    assert.equal(pascalcase('foo-BAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase('Foo-BAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase('fooBAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase('fooBaR', { preserveConsecutiveUppercase: true }), 'FooBaR');
+    assert.equal(pascalcase('FOÈ-BAR', { preserveConsecutiveUppercase: true }), 'FOÈBAR');
+    assert.equal(pascalcase(['foo', 'BAR'], { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase(['foo', '-BAR'], { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase(['foo', '-BAR', 'baz'], { preserveConsecutiveUppercase: true }), 'FooBARBaz');
+    assert.equal(pascalcase(['', ''], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('--', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('--__--_--_', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase(['---_', '--', '', '-_- '], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('foo BAR?', { preserveConsecutiveUppercase: true }), 'FooBAR?');
+    assert.equal(pascalcase('foo BAR!', { preserveConsecutiveUppercase: true }), 'FooBAR!');
+    assert.equal(pascalcase('foo BAR$', { preserveConsecutiveUppercase: true }), 'FooBAR$');
+    assert.equal(pascalcase('foo-BAR#', { preserveConsecutiveUppercase: true }), 'FooBAR#');
+    assert.equal(pascalcase('XMLHttpRequest', { preserveConsecutiveUppercase: true }), 'XMLHttpRequest');
+    assert.equal(pascalcase('AjaxXMLHttpRequest', { preserveConsecutiveUppercase: true }), 'AjaxXMLHttpRequest');
+    assert.equal(pascalcase('Ajax-XMLHttpRequest', { preserveConsecutiveUppercase: true }), 'AjaxXMLHttpRequest');
+    assert.equal(pascalcase([], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('mGridCOl6@md'), 'MGridCOl6@md');
+    assert.equal(pascalcase('mGridCOl6@md', { preserveConsecutiveUppercase: true }), 'MGridCOl6@md');
+    assert.equal(pascalcase('A::a', { preserveConsecutiveUppercase: true }), 'A::a');
+    assert.equal(pascalcase('Hello1WORLD', { preserveConsecutiveUppercase: true }), 'Hello1WORLD');
+    assert.equal(pascalcase('Hello11WORLD', { preserveConsecutiveUppercase: true }), 'Hello11WORLD');
+    assert.equal(pascalcase('РозовыйПушистыйFOOдинорогиf', { preserveConsecutiveUppercase: true }), 'РозовыйПушистыйFOOдинорогиf');
+    assert.equal(pascalcase('桑德在这里。', { preserveConsecutiveUppercase: true }), '桑德在这里。');
+    assert.equal(pascalcase('桑德_在这里。', { preserveConsecutiveUppercase: true }), '桑德在这里。');
+  });
+
+  it('pascalcase with both pascalCase and preserveConsecutiveUppercase option', () => {
+    assert.equal(pascalcase('foo-BAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase('fooBAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase('fooBaR', { preserveConsecutiveUppercase: true }), 'FooBaR');
+    assert.equal(pascalcase('fOÈ-BAR', { preserveConsecutiveUppercase: true }), 'FOÈBAR');
+    assert.equal(pascalcase('--foo.BAR', { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase(['Foo', 'BAR'], { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase(['foo', '-BAR'], { preserveConsecutiveUppercase: true }), 'FooBAR');
+    assert.equal(pascalcase(['foo', '-BAR', 'baz'], { preserveConsecutiveUppercase: true }), 'FooBARBaz');
+    assert.equal(pascalcase(['', ''], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('--', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('--__--_--_', { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase(['---_', '--', '', '-_- '], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('foo BAR?', { preserveConsecutiveUppercase: true }), 'FooBAR?');
+    assert.equal(pascalcase('foo BAR!', { preserveConsecutiveUppercase: true }), 'FooBAR!');
+    assert.equal(pascalcase('Foo BAR$', { preserveConsecutiveUppercase: true }), 'FooBAR$');
+    assert.equal(pascalcase('foo-BAR#', { preserveConsecutiveUppercase: true }), 'FooBAR#');
+    assert.equal(pascalcase('xMLHttpRequest', { preserveConsecutiveUppercase: true }), 'XMLHttpRequest');
+    assert.equal(pascalcase('ajaxXMLHttpRequest', { preserveConsecutiveUppercase: true }), 'AjaxXMLHttpRequest');
+    assert.equal(pascalcase('Ajax-XMLHttpRequest', { preserveConsecutiveUppercase: true }), 'AjaxXMLHttpRequest');
+    assert.equal(pascalcase([], { preserveConsecutiveUppercase: true }), '');
+    assert.equal(pascalcase('mGridCOl6@md', { preserveConsecutiveUppercase: true }), 'MGridCOl6@md');
+    assert.equal(pascalcase('A::a', { preserveConsecutiveUppercase: true }), 'A::a');
+    assert.equal(pascalcase('Hello1WORLD', { preserveConsecutiveUppercase: true }), 'Hello1WORLD');
+    assert.equal(pascalcase('Hello11WORLD', { preserveConsecutiveUppercase: true }), 'Hello11WORLD');
+    assert.equal(pascalcase('pозовыйПушистыйFOOдинорогиf', { preserveConsecutiveUppercase: true }), 'PозовыйПушистыйFOOдинорогиf');
+    assert.equal(pascalcase('桑德在这里。', { preserveConsecutiveUppercase: true }), '桑德在这里。');
+    assert.equal(pascalcase('桑德_在这里。', { preserveConsecutiveUppercase: true }), '桑德在这里。');
+  });
+
+  it('pascalcase with locale option', () => {
+    assert.equal(pascalcase('lorem-ipsum', { locale: 'tr-TR' }), 'Loremİpsum');
+    assert.equal(pascalcase('lorem-ipsum', { locale: 'en-EN' }), 'LoremIpsum');
+    assert.equal(pascalcase('lorem-ipsum', { locale: ['tr', 'TR', 'tr-TR'] }), 'Loremİpsum');
+    assert.equal(pascalcase('lorem-ipsum', { locale: ['en-EN', 'en-GB'] }), 'LoremIpsum');
+    assert.equal(pascalcase('ipsum-dolor', { locale: 'tr-TR' }), 'İpsumDolor');
+    assert.equal(pascalcase('ipsum-dolor', { locale: 'en-EN' }), 'IpsumDolor');
+    assert.equal(pascalcase('ipsum-dolor', { locale: ['tr', 'TR', 'tr-TR'] }), 'İpsumDolor');
+    assert.equal(pascalcase('ipsum-dolor', { locale: ['en-EN', 'en-GB'] }), 'IpsumDolor');
+  });
+});
diff --git a/test.js b/test/test.js
similarity index 57%
rename from test.js
rename to test/test.js
index f716d50..ebaf8a5 100644
--- a/test.js
+++ b/test/test.js
@@ -1,8 +1,5 @@
-'use strict';
-
-require('mocha');
-const assert = require('assert').strict;
-const pascalcase = require('.');
+import { strict as assert } from 'assert';
+import pascalcase from '../index.js';
 
 describe('pascalcase', () => {
   it('should uppercase a single character string', () => {
@@ -10,6 +7,12 @@ describe('pascalcase', () => {
     assert.equal(pascalcase('Z'), 'Z');
   });
 
+  it('should transform multiple single characters', () => {
+    assert.equal(pascalcase('a b c d'), 'ABCD');
+    assert.equal(pascalcase('a bb c ddd'), 'ABbCDdd');
+    assert.equal(pascalcase('Z'), 'Z');
+  });
+
   it('should work with spaces', () => {
     assert.equal(pascalcase('foo bar baz'), 'FooBarBaz');
     assert.equal(pascalcase('foo     baz'), 'FooBaz');
@@ -18,10 +21,21 @@ describe('pascalcase', () => {
 
   it('should work with numbers', () => {
     assert.equal(pascalcase(3), '3');
-    assert.equal(pascalcase('foo2bar5baz'), 'Foo2bar5baz');
+    assert.equal(pascalcase('foo2bar5baz'), 'Foo2Bar5Baz');
     assert.equal(pascalcase('foo 2 bar 5 baz'), 'Foo2Bar5Baz');
   });
 
+  it('should work with upper case characters', () => {
+    assert.equal(pascalcase('fooBARbaz'), 'FooBaRbaz');
+    assert.equal(pascalcase('fooBARbaz', { preserveConsecutiveUppercase: true }), 'FooBARbaz');
+    assert.equal(pascalcase('FOO_BAR_BAZ'), 'FooBarBaz');
+    assert.equal(pascalcase('FOO_BAR_BAZ', { preserveConsecutiveUppercase: true }), 'FOOBARBAZ');
+    assert.equal(pascalcase('The IRS Is Mean'), 'TheIrsIsMean');
+    assert.equal(pascalcase('The IRS Is Mean', { preserveConsecutiveUppercase: true }), 'TheIRSIsMean');
+    assert.equal(pascalcase('We saw a UFO'), 'WeSawAUfo');
+    assert.equal(pascalcase('We saw a UFO', { preserveConsecutiveUppercase: true }), 'WeSawAUFO');
+  });
+
   it('should not lowercase existing camelcasing', () => {
     assert.equal(pascalcase('fooBarBaz'), 'FooBarBaz');
     assert.equal(pascalcase('FooBarBaz'), 'FooBarBaz');
@@ -33,7 +47,8 @@ describe('pascalcase', () => {
     assert.equal(pascalcase('foo_bar-baz'), 'FooBarBaz');
     assert.equal(pascalcase('foo.bar.baz'), 'FooBarBaz');
     assert.equal(pascalcase('foo/bar/baz'), 'FooBarBaz');
-    assert.equal(pascalcase('foo[bar)baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foo[bar]baz'), 'FooBarBaz');
+    assert.equal(pascalcase('foo(bar)baz'), 'FooBarBaz');
     assert.equal(pascalcase('#foo+bar*baz'), 'FooBarBaz');
     assert.equal(pascalcase('$foo~bar`baz'), 'FooBarBaz');
     assert.equal(pascalcase('_foo_bar-baz-'), 'FooBarBaz');
@@ -48,7 +63,8 @@ describe('pascalcase', () => {
   it('should call .toString() when value is not a primitive', () => {
     assert.equal(pascalcase(['one', 'two', 'three']), 'OneTwoThree');
     assert.equal(pascalcase([]), '');
-    assert.equal(pascalcase(function foo_bar() {}), 'FunctionFooBar');
+    assert.equal(pascalcase(function foo_bar() {}), 'FooBar');
+    assert.equal(pascalcase(function foo_bar(a, b) { return a + b; }), 'FooBar');
     assert.equal(pascalcase({}), 'ObjectObject');
     assert.equal(pascalcase(/foo/), 'Foo');
   });

More details

Full run details

Historical runs