diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..571030d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.idea/
+node_modules/
+yarn.lock
+package-lock.json
+.*
+!.csscomb.json
+!.gitignore
+!.gitattributes
+!.hound.yml
+!.scss-lint.yml
+!.stylelintrc.json
+temp
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b514823
--- /dev/null
+++ b/README.md
@@ -0,0 +1,196 @@
+# angular-package
+
+
+
+
+
+The angular-package supports the development process of [angular](https://angular.io)-based applications in varied ways through the thoughtful, reusable, easy-to-use small pieces of code called packages.
+
+
+
+
+
+## Sass String
+
+Sass String - Modified string Sass module.
+
+[![Gitter][gitter-badge]][gitter-chat]
+[![Discord][discord-badge]][discord-channel]
+[![Twitter][twitter-badge]][twitter-follow]
+
+
+[![npm version][sass-string-npm-badge-svg]][sass-string-npm-badge]
+
+
+[![GitHub issues][sass-string-badge-issues]][sass-string-issues]
+[![GitHub forks][sass-string-badge-forks]][sass-string-forks]
+[![GitHub stars][sass-string-badge-stars]][sass-string-stars]
+[![GitHub license][sass-string-badge-license]][sass-string-license]
+
+
+[![GitHub Sponsors][github-badge-sponsor]][github-sponsor-link]
+[![Patreon Sponsors][patreon-badge]][patreon-link]
+
+Extended sass modules:
+
+* The [`sass:string`](https://sass-lang.com/documentation/modules/string/) is extended by [`@angular-package/sass-string`](https://docs.angular-package.dev/v/sass/string/overview) - module makes it easy to combine, search, or split apart strings.
+
+
+
+Sass extension is **free** to use. If you enjoy it, please consider donating via [fiat](https://docs.angular-package.dev/v/sass/donate/fiat), [Revolut platform](https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29) or [cryptocurrency](https://spectrecss.angular-package.dev/donate/thb-cryptocurrency) the [@angular-package](https://github.com/sponsors/angular-package) for further development. ♥
+
+> Feel **free** to submit a pull request. Help is always appreciated.
+
+
+
+## Table of contents
+
+* [Skeleton](#skeleton)
+* [Code scaffolding](#code-scaffolding)
+* [Documentation](#documentation)
+* [Changelog](#changelog)
+* [Git](#git)
+ * [Commit](#commit)
+ * [Versioning](#versioning)
+* [License](#license)
+
+
+
+## Skeleton
+
+This package was generated by the [skeleton workspace][skeleton] with [Angular CLI](https://github.com/angular/angular-cli) version `14.2.0`.
+
+Copy this package to the `packages/sass-string` folder of the [skeleton workspace][skeleton] then run the commands below.
+
+
+
+## Code scaffolding
+
+Run `ng generate component component-name --project sass-string` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project sass-string`.
+> Note: Don't forget to add `--project sass-string` or else it will be added to the default project in your `angular.json` file.
+
+### Build
+
+Run `ng build sass-string` to build the project. The build artifacts will be stored in the `dist/` directory.
+
+### Publishing
+
+After building your library with `ng build sass-string`, go to the dist folder `cd dist/sass-string` and run `npm publish`.
+
+## Running unit tests
+
+Run `ng test sass-string` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
+
+
+
+## Documentation
+
+The documentation is in construction and it's available at [https://docs.angular-package.dev/v/sass-string](https://docs.angular-package.dev/v/sass-string/)
+
+
+
+## Changelog
+
+To read it, click on the [CHANGELOG.md][sass-string-github-changelog] link.
+
+
+
+## GIT
+
+### Commit
+
+* [AngularJS Git Commit Message Conventions][git-commit-angular]
+* [Karma Git Commit Msg][git-commit-karma]
+* [Conventional Commits][git-commit-conventional]
+
+### Versioning
+
+[Semantic Versioning 2.0.0][git-semver]
+
+**Given a version number MAJOR.MINOR.PATCH, increment the:**
+
+* MAJOR version when you make incompatible API changes,
+* MINOR version when you add functionality in a backwards-compatible manner, and
+* PATCH version when you make backwards-compatible bug fixes.
+
+Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
+
+**FAQ**
+How should I deal with revisions in the 0.y.z initial development phase?
+
+> The simplest thing to do is start your initial development release at 0.1.0 and then increment the minor version for each subsequent release.
+
+How do I know when to release 1.0.0?
+
+> If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0.
+
+
+
+## License
+
+MIT © angular-package ([license][sass-string-license])
+
+
+[github-badge-sponsor]: https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/angular-package
+[github-sponsor-link]: https://github.com/sponsors/angular-package
+[patreon-badge]: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dangularpackage%26type%3Dpatrons&style=flat
+[patreon-link]: https://www.patreon.com/join/angularpackage/checkout?fan_landing=true&rid=0
+
+[angulario]: https://angular.io
+[skeleton]: https://github.com/angular-package/skeleton
+
+
+[experimental]: https://img.shields.io/badge/-Experimental-orange
+[fix]: https://img.shields.io/badge/-Fix-red
+[new]: https://img.shields.io/badge/-eNw-green
+[update]: https://img.shields.io/badge/-Update-red
+[documentation]: https://img.shields.io/badge/-Documentation-informational
+[demonstration]: https://img.shields.io/badge/-Demonstration-green
+
+
+[discord-badge]: https://img.shields.io/discord/925168966098386944?style=social&logo=discord&label=Discord
+[discord-channel]: https://discord.com/invite/rUCR2CW75G
+
+
+[gitter-badge]: https://img.shields.io/gitter/room/angular-package/ap-sass?style=social&logo=gitter
+[gitter-chat]: https://app.gitter.im/#/room/#ap-sass:gitter.im
+
+
+[twitter-badge]: https://img.shields.io/twitter/follow/angularpackage?label=%40angularpackage&style=social
+[twitter-follow]: https://twitter.com/angularpackage
+
+
+[git-semver]: http://semver.org/
+
+
+[git-commit-angular]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153
+[git-commit-karma]: http://karma-runner.github.io/0.10/dev/git-commit-msg.html
+[git-commit-conventional]: https://www.conventionalcommits.org/en/v1.0.0/
+
+
+
+ [sass-string-badge-issues]: https://img.shields.io/github/issues/angular-package/sass-string
+ [sass-string-badge-forks]: https://img.shields.io/github/forks/angular-package/sass-string
+ [sass-string-badge-stars]: https://img.shields.io/github/stars/angular-package/sass-string
+ [sass-string-badge-license]: https://img.shields.io/github/license/angular-package/sass-string
+
+ [sass-string-issues]: https://github.com/angular-package/sass-string/issues
+ [sass-string-forks]: https://github.com/angular-package/sass-string/network
+ [sass-string-license]: https://github.com/angular-package/sass-string/blob/master/LICENSE
+ [sass-string-stars]: https://github.com/angular-package/sass-string/stargazers
+
+ [sass-string-github-changelog]: https://github.com/angular-package/sass-string/blob/main/CHANGELOG.md
+
+
+
+ [sass-string-npm-badge-svg]: https://badge.fury.io/js/%40angular-package%2Fsass-string.svg
+ [sass-string-npm-badge-png]: https://badge.fury.io/js/%40angular-package%2Fsass-string.png
+ [sass-string-npm-badge]: https://badge.fury.io/js/%40angular-package%2Fsass-string
+ [sass-string-npm-readme]: https://www.npmjs.com/package/@angular-package/sass-string#readme
+
+
+ [sass-string-github-readme]: https://github.com/angular-package/sass-string#readme
diff --git a/index.scss b/index.scss
new file mode 100644
index 0000000..99e41a8
--- /dev/null
+++ b/index.scss
@@ -0,0 +1 @@
+@forward "src/lib";
diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000..7f6d23d
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,44 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage'),
+ require('@angular-devkit/build-angular/plugins/karma')
+ ],
+ client: {
+ jasmine: {
+ // you can add configuration options for Jasmine here
+ // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
+ // for example, you can disable the random execution with `random: false`
+ // or set a specific seed with `seed: 4321`
+ },
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ jasmineHtmlReporter: {
+ suppressAll: true // removes the duplicated traces
+ },
+ coverageReporter: {
+ dir: require('path').join(__dirname, '../../coverage/sass-string'),
+ subdir: '.',
+ reporters: [
+ { type: 'html' },
+ { type: 'text-summary' }
+ ]
+ },
+ reporters: ['progress', 'kjhtml'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false,
+ restartOnFileChange: true
+ });
+};
diff --git a/ng-package.json b/ng-package.json
new file mode 100644
index 0000000..7551c4d
--- /dev/null
+++ b/ng-package.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/sass-string",
+ "lib": {
+ "entryFile": "src/public-api.ts"
+ },
+ "assets": [
+ "./src/lib/**/*.scss",
+ "*.scss"
+ ]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d6b1ebe
--- /dev/null
+++ b/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "@angular-package/sass-string",
+ "version": "1.0.0-beta",
+ "author": "@angular-package ",
+ "homepage": "https://angular-package.github.io/spectre.css",
+ "description": "Modified string Sass module.",
+ "main": "./index.scss",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org"
+ },
+ "devDependencies": {
+ "stylelint": "^15.10.3",
+ "stylelint-config-prettier-scss": "^1.0.0",
+ "stylelint-config-standard-scss": "^11.0.0",
+ "stylelint-order": "^6.0.3"
+ },
+ "peerDependencies": {
+ "sass": "^1.78.0"
+ },
+ "scripts": {},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular-package/sass-string"
+ },
+ "bugs": {
+ "url": "https://github.com/angular-package/sass-string/issues"
+ },
+ "keywords": [
+ "@angular-package",
+ "sass:string",
+ "scss",
+ "string"
+ ],
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29"
+ },
+ {
+ "type": "individual",
+ "url": "https://docs.angular-package.dev/donate/cryptocurrency"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/angularpackage"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/angular-package"
+ }
+ ]
+}
diff --git a/spec.scss b/spec.scss
new file mode 100644
index 0000000..2fbbff7
--- /dev/null
+++ b/spec.scss
@@ -0,0 +1,10 @@
+// Not active.
+
+// @use "./src/lib/spec";
+// @use "../sass-string";
+
+// Distribution: works.
+// @use "../../dist/sass-string";
+
+// @debug sass-string.ends-with("primary dark", dark); // true
+
diff --git a/src/lib/_index.scss b/src/lib/_index.scss
new file mode 100644
index 0000000..5b51d0c
--- /dev/null
+++ b/src/lib/_index.scss
@@ -0,0 +1,14 @@
+@forward "string.ends-with.function";
+@forward "string.index.function";
+@forward "string.is-lower-case.function";
+@forward "string.is-upper-case.function";
+@forward "string.is.function";
+@forward "string.join.function";
+@forward "string.of.function";
+@forward "string.replace-multiple.function";
+@forward "string.replace.function";
+@forward "string.split.function";
+@forward "string.starts-with.function";
+@forward "string.to-map.function";
+@forward "string.unquote.function";
+@forward "sass:string" hide index, split, unquote;
diff --git a/src/lib/_string.ends-with.function.scss b/src/lib/_string.ends-with.function.scss
new file mode 100644
index 0000000..cc871dd
--- /dev/null
+++ b/src/lib/_string.ends-with.function.scss
@@ -0,0 +1,33 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.ends-with()` function determines whether `$string` ends with `$search-string` at `$position`.
+// @param `$string` String to determine that it ends with `$search-string` at `$position`.
+// @param `$search-string` String to determine that ends in `$string` at `$position`.
+// @param `$position` Position at which `$search-string` is expected to be found.
+// @returns The returned value is a `bool` indicating whether `$string` ends with `$search-string` at `$position`.
+@function ends-with($string, $search-string, $position: null) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ @if not (meta.type-of($search-string) == string) {
+ @error "$search-string: #{$search-string} is not string";
+ }
+ $index: string.index($string, $search-string);
+ @if if($position, $position and $index == $position, true) {
+ @return ($index - 1) + string.length($search-string) == string.length($string);
+ }
+ @return false;
+}
+
+// Examples.
+// $-text: 'Primary color red';
+
+// @debug ends-with($-text, 'red'); // true
+// @debug ends-with($-text, 'color red'); // true
+
+// Position.
+// @debug ends-with($-text, 'color red', 9); // true
+// @debug ends-with($-text, 'color red', 4); // false
diff --git a/src/lib/_string.index.function.scss b/src/lib/_string.index.function.scss
new file mode 100644
index 0000000..e5aefb1
--- /dev/null
+++ b/src/lib/_string.index.function.scss
@@ -0,0 +1,48 @@
+// Sass.
+@use 'sass:list';
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.index()` function returns index of required `$substring` and additional indexes of `$substrings`.
+// @param `$string` String to check whether it contains required `$substring` and additional `$substrings`.
+// @param `$substring` A substring to find in `$string`.
+// @arbitrary `$substrings...` Additional substrings to find in `$string`.
+// @returns The returned value is index of `$substring` and additional indexes of `$substrings`.
+@function index($string, $substring, $substrings...) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ $result: ();
+ @each $substring in list.join($substring, $substrings, comma) {
+ $result: list.append(
+ $result,
+ meta.type-of($substring) == string
+ and string.index($string, $substring)
+ or null,
+ comma
+ );
+ }
+ @if list.length($result) > 0 {
+ @return list.length($result) > 1
+ and $result
+ or list.nth($result, 1);
+ }
+ @return null;
+}
+
+// Examples.
+// Single
+// @debug index("Helvetica Neue", "Helvetica"); // 1
+// @debug index("Helvetica Neue", "Neue"); // 11
+
+// Multiple
+// @debug index("Helvetica Neue", "Helvetica", "Neue"); // 1, 11
+// @debug index("Helvetica Neue", "Helvetica", "Neue", "Wrong"); // 1, 11, null
+// @debug index("Helvetica Neue", "Helvetica", "Neue", "Wrong", true); // 1, 11, null, null
+
+// null
+// @debug index("Helvetica Neue", "d"); // null
+
+// Different type of the `$string`
+// @debug index(true, "Helvetica"); // error
diff --git a/src/lib/_string.is-lower-case.function.scss b/src/lib/_string.is-lower-case.function.scss
new file mode 100644
index 0000000..c0ad828
--- /dev/null
+++ b/src/lib/_string.is-lower-case.function.scss
@@ -0,0 +1,25 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.is-lower-case()` function determines `$string` is written in small letters.
+// @param `$string` String to determine is written in small letters.
+// @returns The returned value is `bool` indicating `$string` is written in small letters.
+@function is-lower-case($string) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ @for $i from 1 through string.length($string) {
+ $letter: string.slice($string, $i, $i);
+ @if ($letter != string.to-lower-case($letter)) {
+ @return false;
+ }
+ }
+ @return true;
+}
+
+// Examples.
+// @debug is-lower-case('test test test'); // true
+// @debug is-lower-case('TEST TEST TEST'); // false
+// @debug is-lower-case('TEST test TEST'); // false
diff --git a/src/lib/_string.is-upper-case.function.scss b/src/lib/_string.is-upper-case.function.scss
new file mode 100644
index 0000000..c96e6f9
--- /dev/null
+++ b/src/lib/_string.is-upper-case.function.scss
@@ -0,0 +1,25 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.is-upper-case()` function determines `$string` is written in capital letters.
+// @param `$string` String to determine is written in capital letters.
+// @returns The returned value is `bool` indicating `$string` is written in capital letters.
+@function is-upper-case($string) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ @for $i from 1 through string.length($string) {
+ $letter: string.slice($string, $i, $i);
+ @if ($letter != string.to-upper-case($letter)) {
+ @return false;
+ }
+ }
+ @return true;
+}
+
+// Examples.
+// @debug is-upper-case('TEST TEST TEST'); // true
+// @debug is-upper-case('test test test'); // false
+// @debug is-upper-case('TEST test TEST'); // false
diff --git a/src/lib/_string.is.function.scss b/src/lib/_string.is.function.scss
new file mode 100644
index 0000000..ab15771
--- /dev/null
+++ b/src/lib/_string.is.function.scss
@@ -0,0 +1,25 @@
+// Sass.
+@use 'sass:list';
+@use 'sass:meta';
+
+// Completed
+// The `string.is()` function checks whether `$value` is `string` type.
+// @param `$value` Any value to check against `string` type.
+// @arbitrary `$values...` Additional values to check against `string` type.
+// @return The returned value is `bool` indicating whether `$value` is `string` type.
+@function is($value, $values...) {
+ @each $value in list.join(($value,), $values, comma) {
+ @if not (meta.type-of($value) == string) {
+ @return false;
+ }
+ }
+ @return true;
+}
+
+// Examples.
+// @debug is(test); // true
+// @debug is(test, test1, test2); // true
+// @debug is(test, test1, 2); // false
+
+// @debug is(1); // false
+// @debug is(1, 2); // false
diff --git a/src/lib/_string.join.function.scss b/src/lib/_string.join.function.scss
new file mode 100644
index 0000000..29ecc21
--- /dev/null
+++ b/src/lib/_string.join.function.scss
@@ -0,0 +1,36 @@
+// Sass.
+@use 'sass:list';
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.join()` function returns the `string` built from `$elements` separated by `$delimiter` in original elements order.
+// @param `$delimiter` The delimiter to add between `$elements`.
+// @param `$elements...` Elements to join to returned string of `color`, `list`, `number` or `string` type with the `$delimiter`.
+// @returns The returned value is a `string` built from `$elements` with `$delimiter`.
+@function join($delimiter, $elements...) {
+ $string: '';
+ @each $element in $elements {
+ @if list.index(color list number string, meta.type-of($element)) {
+ @each $element in $element {
+ $string: string.insert($string, #{$element}#{$delimiter or ''}, -1);
+ }
+ }
+ }
+ @return string.slice($string, 1, calc((string.length($delimiter) + 1) * -1));
+}
+
+// Examples.
+// @debug join('-');
+// @debug join('-', 'a', 'b', 'c'); // a-b-c
+// @debug join('-', '', 'a', 'b', 'c'); // -a-b-c
+// @debug join('-', 'a', 'b', 'c', ''); // a-b-c-
+// @debug join('-', '', 'a', 'b', 'c', ''); // a-b-c-
+// @debug join('-', 'a', ('b', 'c')); // a-b-c
+// @debug join('-', a, b, c); // a-b-c
+
+// Join number.
+// @debug join('-', a, 1, b, 2, c, 3); // a-1-b-2-c-3
+
+// Join color.
+// @debug join('-', silver, orange, green, red, blue, yellow); // silver-orange-green-red-blue-yellow
diff --git a/src/lib/_string.of.function.scss b/src/lib/_string.of.function.scss
new file mode 100644
index 0000000..87ce324
--- /dev/null
+++ b/src/lib/_string.of.function.scss
@@ -0,0 +1,86 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:list';
+@use 'sass:string';
+
+// Functions.
+@use 'string.is-lower-case.function' as string-is-lower-case;
+@use 'string.is-upper-case.function' as string-is-upper-case;
+
+// Completed
+// The `string.of()` function returns `$string` specification of `$spec` separated by `$separator`.
+// @param `$string` The string to determine its specification `$spec` - letter capital(xcase), length, lower case(lowercase), upper case(uppercase) in any order.
+// @param `$spec` List of string specification - letter capital(xcase), length, lower case(lowercase), upper case(uppercase) in any order.
+// @param `$separator` Separator used between returned details of specification `$spec`.
+// @param `$type` Whether to return `string` as first in specification.
+// @returns The returned value is `$string` specification of `$spec`.
+@function of($string, $spec, $separator: null, $type: false) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ $result: '';
+ $spec: if(
+ $type,
+ list.index($spec, type) and $spec or list.join(type, $spec),
+ $spec
+ );
+ @each $name in $spec {
+ $result: string.insert(
+ $result,
+ string.insert(
+ $name == xcase
+ and (
+ string-is-lower-case.is-lower-case($string)
+ and lowercase
+ or string-is-upper-case.is-upper-case($string)
+ and uppercase
+ or variouslycase
+ )
+ or $name == length and #{string.length($string)}
+ or $name == lowercase
+ and (
+ string-is-lower-case.is-lower-case($string)
+ and lowercase
+ or notlowercase
+ )
+ or $name == type and meta.type-of($string)
+ or $name == uppercase
+ and (
+ string-is-upper-case.is-upper-case($string)
+ and uppercase
+ or notuppercase
+ )
+ or '',
+ $separator and $separator or '',
+ -1
+ ),
+ -1
+ );
+ }
+ @return string.slice($result, 1, $separator and -2 or -1);
+}
+
+// Examples.
+// lowercase
+// @debug of('test test test', lowercase); // lowercase
+// @debug of('TEST TEST TEST', lowercase); // notlowercase
+// @debug of('test TEST test', lowercase); // notlowercase
+
+// uppercase
+// @debug of('TEST TEST TEST', uppercase); // uppercase
+// @debug of('test test test', uppercase); // notuppercase
+// @debug of('test TEST test', uppercase); // notuppercase
+
+// lowercase uppercase
+// @debug of('TEST TEST TEST', type lowercase uppercase, '.'); // string.notlowercase.uppercase
+
+// xcase
+// @debug of('test TEST test', xcase); // variouslycase
+// @debug of('TEST TEST TEST', xcase); // uppercase
+// @debug of('test test test', xcase); // lowercase
+
+// @debug of('TEST TEST TEST', type xcase, '.'); // string.uppercase
+
+// length
+// @debug of('TEST TEST TEST', length xcase); // 14uppercase
+// @debug of('TEST TEST TEST', length xcase, '.'); // 14.uppercase
diff --git a/src/lib/_string.replace-multiple.function.scss b/src/lib/_string.replace-multiple.function.scss
new file mode 100644
index 0000000..a8bd255
--- /dev/null
+++ b/src/lib/_string.replace-multiple.function.scss
@@ -0,0 +1,39 @@
+// Functions.
+@use 'string.replace.function' as string-replace;
+
+// Completed
+// The `string.replace-multiple()` function handles multiple string replaces, using `string.replace()` function.
+// @param `$string` The string to do multiple `$replaces`.
+// @arbitrary `$replaces...` Arbitrary replaces (occurrence, substring, replacement) to do on `$string`.
+// @returns The returned value is a `string` with done `$replaces`.
+@function replace-multiple($string, $replaces...) {
+ @each $occurrence, $substring, $replacement in $replaces {
+ $string: string-replace.replace(
+ $string,
+ $occurrence,
+ $substring,
+ $replacement
+ );
+ }
+ @return $string;
+}
+
+// Examples.
+// single replacement first occurrence
+// @debug replace-multiple('bold king is hairy', first 'bold' 'baloon'); // baloon king is hairy
+// @debug replace-multiple('bold king is hairy', first 'bold' ''); // king is hairy
+// @debug replace-multiple(':==', first ':' '');
+
+// single replacement all occurrences
+// @debug replace-multiple('bold king is bold hairy', all 'bold' ''); // king is hairy
+// @debug replace-multiple('bold king is bold hairy', all 'bold' 'test'); // test king is test hairy
+
+// multiple replacements
+// @debug replace-multiple('bold king is hairy', first (bold is) ''); // king hairy
+// @debug replace-multiple('bold king is hairy', first bold baloon, first king baloon); // baloon baloon is hairy
+// @debug replace-multiple('bold king is hairy', first (bold king) baloon, first (is hairy) bold); // baloon baloon bold bold
+// @debug replace-multiple('atmosphere bold bold bold king is hairy', first (king bold) baloon); // atmosphere baloon bold bold baloon is hairy
+// @debug replace-multiple('atmosphere bold bold bold king is hairy', all (king bold) baloon); // atmosphere baloon baloon baloon baloon is hairy
+
+// multiple replacements different occurrences
+// @debug replace-multiple('atmosphere bold bold bold king is hairy', first bold clean, all (king bold) baloon); // atmosphere clean baloon baloon baloon is hairy
diff --git a/src/lib/_string.replace.function.scss b/src/lib/_string.replace.function.scss
new file mode 100644
index 0000000..33ac792
--- /dev/null
+++ b/src/lib/_string.replace.function.scss
@@ -0,0 +1,64 @@
+// Sass.
+@use "sass:list";
+@use "sass:meta";
+@use "sass:string";
+
+// Completed
+// The `string.replace()` function replaces the first or all `$substring` occurrences in the string with `$replacement`.
+// @param `$string` A `string` to replace first or all `$substring` in it by `$replacement`.
+// @param `$occurrence` First or all occurrences of `$substring` to replace in `$string`.
+// @param `$substring` A `string` or list of strings to replace by `$replacement`.
+// @param `$replacement` A `string` to replace `$substring`.
+// @returns The returned value is a `string` with a first/all replaced `$substring` by `$replacement`.
+@function replace($string, $occurrence, $substring, $replacement) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not `string`";
+ }
+ @if not (list.index(all first, $occurrence)) {
+ @error "$occurrence: #{$occurrence} is not string equal to `first` or `all`";
+ }
+ @if not (meta.type-of($replacement) == string) {
+ @error "$replacement: #{$replacement} is not `string`";
+ }
+ @each $value in $substring {
+ @if not (meta.type-of($value) == string) {
+ @error "$substring: #{$value} is not `string`";
+ }
+ $index: string.index($string, $value);
+ @while not ($index == null) {
+ $string: string.insert(
+ string.slice($string, 1, $index - 1) +
+ string.slice(
+ $string,
+ $index + string.length($value),
+ string.length($string)
+ ),
+ $replacement,
+ $index
+ );
+ @if $occurrence == first {
+ $index: null;
+ }
+ @if $occurrence == all or ($occurrence == first and not ($index == null)) {
+ $index: string.index($string, $value);
+ }
+ }
+ }
+ @return $string;
+}
+
+// Examples.
+// single replacement first occurrence
+// @debug replace('bold king is hairy', first, 'bold', 'baloon'); // baloon king is hairy
+// @debug replace('bold king is hairy', first, 'bold', ''); // king is hairy
+// @debug replace(':==', first, ':', '');
+
+// single replacement all occurrences
+// @debug replace('bold king is hairy', all, 'bold', 'baloon'); // baloon king is hairy
+// @debug replace('bold king is hairy', all, 'king', 'baloon'); // baloon king is hairy
+// @debug replace('bold king is bold hairy', all, 'bold', ''); // king is hairy
+// @debug replace('bold king is bold hairy', all, 'bold', 'test'); // test king is test hairy
+
+// multiple replacements
+// @debug replace('bold king is hairy', all, ('bold', 'king'), 'baloon'); // baloon baloon is hairy
+// @debug replace('bold king is hairy', first, (bold is), ''); // king hairy
diff --git a/src/lib/_string.split.function.scss b/src/lib/_string.split.function.scss
new file mode 100644
index 0000000..b4aded3
--- /dev/null
+++ b/src/lib/_string.split.function.scss
@@ -0,0 +1,108 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:list';
+@use 'sass:math';
+@use 'sass:string';
+
+// Functions.
+@use 'string.index.function' as string-index;
+@use 'string.unquote.function' as string-unquote;
+
+// Completed
+// The `string.split()` function returns comma-separated list of substrings of `$string` that are separated by `$separator`.
+// The separators aren’t included in these substrings..
+// If `$limit` is a number `1` or higher, this splits on at most that many $separators (and so returns at most $limit + 1 strings).
+// The last substring contains the rest of the string, including any remaining separators.
+// @param `$string` The `string` that is split by `$separator`.
+// @param `$separator` The separator that splits the `$string` into the returned list.
+// @param `$limit` Limit split of `$string`.
+// @param `$bracketed` Returns bracketed list.
+// @param `$unquote` Whether to unquote returned elements of list.
+// @returns The returned value is a list separated by `$separator` limited by `$limit` times, and/or is `$bracketed`.
+@function split(
+ $string,
+ $separator,
+ $limit: null,
+ $bracketed: false,
+ $unquote: false
+) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ $result: list.join((), (), comma, $bracketed);
+ $index: 0;
+ $i: 1;
+ @while not ($index == null) {
+ // Set `$index` to `null` to finish loop by default.
+ $index: null;
+
+ // Separator index in
+ $separator-index: null;
+
+ // Find indexes of `$separator` in `$string`.
+ $separators: string-index.index($string, $separator...);
+
+ $-separators: ();
+ @each $separator in $separators {
+ @if $separator {
+ $-separators: list.append($-separators, $separator);
+ }
+ }
+ $separators: $-separators;
+
+ @if list.length($separators) > 0 {
+ $index: math.min($separators...);
+ $separator-index: list.index($separators, $index);
+ }
+
+ @if $separator-index {
+ $result: list.append(
+ $result,
+ string-unquote.unquote(
+ string.slice($string, 1, $index - 1),
+ $unquote
+ )
+ );
+ @if $limit and $limit == $i {
+ $result: list.append(
+ $result,
+ string-unquote.unquote(
+ string.slice($string, $index + string.length(list.nth($separator, $separator-index)), -1),
+ $unquote
+ )
+ );
+ $index: null;
+ } @else {
+ $string: string.slice(
+ $string,
+ $index + string.length(list.nth($separator, $separator-index))
+ );
+ }
+ } @else {
+ $result: list.append(
+ $result,
+ string-unquote.unquote($string, $unquote)
+ );
+ }
+
+ $i: $i + 1;
+ }
+ @return $result;
+}
+
+// Examples.
+// single character
+// @debug split('aaa bbb ccc', ' '); // "aaa", "bbb", "ccc"
+// @debug split('aaa-bbb-ccc', '-'); // "aaa", "bbb", "ccc"
+// @debug split('aaa/bbb/ccc', '/'); // "aaa", "bbb", "ccc"
+// @debug split("Segoe UI Emoji", " "); // "Segoe", "UI", "Emoji"
+// @debug split("Segoe UI Emoji", " ", $limit: 1); // "Segoe", "UI Emoji"
+// @debug split("Segoe UI Emoji", " ", $limit: 1, $bracketed: true); // ["Segoe", "UI Emoji"]
+// @debug split("SF Mono Segoe UI Mono Roboto Mono", " ", $limit: 2, $bracketed: true); // ["SF", "Mono", "Segoe UI Mono Roboto Mono"]
+
+// word `$separator`
+// @debug split('aaa_DELIMITER_bbb_DELIMITER_ccc', '_DELIMITER_'); // "aaa", "bbb", "ccc"
+// @debug split('aaa[separator]bbb[separator]ccc', '[separator]'); // "aaa", "bbb", "ccc"
+
+// multiple separators
+// @debug split('aaa_SEPARATOR_bbb_DELIMITER_ccc_ADD_eee_SEPARATOR_ddd', ('_DELIMITER_', '_SEPARATOR_', '_ADD_')); // "aaa", "bbb", "ccc", "eee", "ddd"
diff --git a/src/lib/_string.starts-with.function.scss b/src/lib/_string.starts-with.function.scss
new file mode 100644
index 0000000..ba61186
--- /dev/null
+++ b/src/lib/_string.starts-with.function.scss
@@ -0,0 +1,33 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// Low
+// The `string.starts-with()` function determines whether `$string` begins with `$search-string` at `$position`.
+// @param `$string` String to determine that it begins with `$search-string` at `$position`.
+// @param `$search-string` String to determine that begins in `$string` at `$position`.
+// @param `$position` Position at which `$search-string` is expected to be found.
+// @returns The returned value is a `bool` indicating whether `$string` begins with `$search-string` at `$position`.
+@function starts-with($string, $search-string, $position: 1) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ @if not (meta.type-of($search-string) == string) {
+ @error "$search-string: #{$search-string} is not string";
+ }
+ @if string.index($string, $search-string) == $position {
+ @return true;
+ }
+ @return false;
+}
+
+// Examples.
+// $-text: 'Primary color red';
+
+// @debug starts-with($-text, 'Primary color'); // true
+// @debug starts-with($-text, 'color red'); // false
+
+// Position.
+// @debug starts-with($-text, 'color red', 9); // true
+// @debug starts-with($-text, 'color red', 4); // false
diff --git a/src/lib/_string.to-map.function.scss b/src/lib/_string.to-map.function.scss
new file mode 100644
index 0000000..ab6ef72
--- /dev/null
+++ b/src/lib/_string.to-map.function.scss
@@ -0,0 +1,71 @@
+// Sass.
+@use 'sass:list';
+@use 'sass:map';
+@use 'sass:meta';
+@use 'sass:string';
+
+// Functions.
+@use 'string.split.function' as string-split;
+
+// Completed
+// Low
+// The `string.to-map()` function returns map build of `$key-value` and additional `$key-value-strings`, if they contain `:`.
+// @param `$key-value` A string type value of `key:value` pattern to build `map`.
+// Allowed formats: 'key1,key2:value1,value2' converts to (key1: (value1, value2), key2: (value1, value2))
+// or 'key1,key2:value1;value2' converts to (key1: value1, key2: value2).
+// @arbitrary `$key-value-strings...` Additional strings of `key:value` pattern to add to returned `map`.
+// @returns The returned value is a `map` built from `$key-value` and additional `$key-value-strings`, if arguments are strings, otherwise `null`.
+@function to-map($key-value, $key-value-strings...) {
+ @if not (meta.type-of($key-value) == string) {
+ @error "$key-value: #{$key-value} is not string";
+ }
+ $result: ();
+ @each $key-value in list.join($key-value, $key-value-strings, comma) {
+ @if meta.type-of($key-value) == string and string.index($key-value, ':') {
+ $key-value: string-split.split($key-value, ':');
+
+ // Key.
+ $key: list.nth($key-value, 1);
+ @if string.index($key, ',') {
+ $key: string-split.split($key, ',');
+ }
+
+ // Value.
+ $value: list.nth($key-value, list.length($key-value));
+
+ // Split value.
+ $value: string.index($value, ';')
+ and string-split.split($value, ';', $bracketed: true)
+ or string.index($value, ',')
+ and string-split.split($value, ',')
+ or $value;
+
+ // Each key.
+ $i: 1;
+ @each $key in $key {
+ $result: map.deep-merge(
+ $result,
+ ($key: list.is-bracketed($value) and list.nth($value, $i) or $value)
+ );
+ $i: $i + 1;
+ }
+ }
+ }
+ @return list.length($result) > 0 and $result or null;
+}
+
+// Examples.
+// Input data
+// 'key:number,value:string'
+// 'key;value:number;string'
+//
+
+// @debug to-map('separator:string,number'); // ("separator": ("string", "number"))
+// @debug to-map('separator:bool'); // ("separator": "bool")
+
+// multiple key
+// @debug to-map('key,value:string,number'); // ("key": ("string", "number"), "value": ("string", "number"))
+// @debug to-map('key,value:string;number'); // ("key": "string", "value": "number")
+
+// add multiple with `$key-value-strings`
+// @debug to-map('key1,key2:string;number', 'key3,key4:string;number', 'key5,key6:string;number'); // ("key1": "string", "key2": "number", "key3": "string", "key4": "number", "key5": "string", "key6": "number")
diff --git a/src/lib/_string.unquote.function.scss b/src/lib/_string.unquote.function.scss
new file mode 100644
index 0000000..e409c55
--- /dev/null
+++ b/src/lib/_string.unquote.function.scss
@@ -0,0 +1,15 @@
+// Sass.
+@use 'sass:meta';
+@use 'sass:string';
+
+// Completed
+// The `string.unquote()` function unquotes `$string` on `$execute` set to `true`.
+// @param `$string` String to unquote.
+// @param `$execute` Whether to execute unquote on `$string`.
+// @returns The returned value is unquoted `$string` if unquote is allowed.
+@function unquote($string, $execute: true) {
+ @if not (meta.type-of($string) == string) {
+ @error "$string: #{$string} is not string";
+ }
+ @return if($execute, string.unquote($string), $string);
+}
diff --git a/src/lib/spec/_index.scss b/src/lib/spec/_index.scss
new file mode 100644
index 0000000..e3bb46d
--- /dev/null
+++ b/src/lib/spec/_index.scss
@@ -0,0 +1,514 @@
+// Sass.
+@use "sass:map";
+
+// Modules.
+@use "../";
+@use "../../test";
+
+// Examples.
+$execute: true;
+$spec: (
+ ends-with: $execute,
+ index: $execute,
+ is-upper-case: $execute,
+ is-lower-case: $execute,
+ is: $execute,
+ join: $execute,
+ of: $execute,
+ replace-multiple: $execute,
+ replace: $execute,
+ split: $execute,
+ starts-with: $execute,
+ to-map: $execute,
+ unquote: $execute,
+);
+
+// SECTION: string.ends-with()
+$-text: "Primary color red";
+
+@include test.it(
+ "string.ends-with()",
+ (
+ equal,
+ 'string.ends-with($-text, "red")',
+ string.ends-with($-text, "red"),
+ true,
+ map.get($spec, ends-with),
+ ),
+
+ (
+ equal,
+ 'string.ends-with($-text, "color red")',
+ string.ends-with($-text, "color red"),
+ true,
+ map.get($spec, ends-with),
+ ),
+
+ (
+ equal,
+ 'string.ends-with($-text, "color red", 9)',
+ string.ends-with($-text, "color red", 9),
+ true,
+ map.get($spec, ends-with),
+ ),
+
+ (
+ equal,
+ 'string.ends-with($-text, "color red", 4)',
+ string.ends-with($-text, "color red", 4),
+ false,
+ map.get($spec, ends-with),
+ ),
+);
+
+
+// SECTION: string.index()
+
+@include test.it(
+ "string.index()",
+ (
+ equal,
+ 'string.index("Helvetica Neue", "Helvetica")',
+ string.index("Helvetica Neue", "Helvetica"),
+ 1,
+ map.get($spec, index)
+ ),
+
+ (
+ equal,
+ 'string.index("Helvetica Neue", "Neue")',
+ string.index("Helvetica Neue", "Neue"),
+ 11,
+ map.get($spec, index)
+ ),
+
+ (
+ equal,
+ 'string.index("Helvetica Neue", "Helvetica", "Neue")',
+ string.index("Helvetica Neue", "Helvetica", "Neue"),
+ (1, 11),
+ map.get($spec, index)
+ ),
+
+ (
+ equal,
+ 'string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong")',
+ string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong"),
+ (1, 11, null),
+ map.get($spec, index)
+ ),
+
+ (
+ equal,
+ 'string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong", true)',
+ string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong", true),
+ (1, 11, null, null),
+ map.get($spec, index)
+ ),
+
+);
+
+
+// @include test.equal(
+// "string.index()",
+// (
+// string.index("Helvetica Neue", "Helvetica"),
+// string.index("Helvetica Neue", "Neue"),
+// string.index("Helvetica Neue", "Helvetica", "Neue"),
+// string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong"),
+// string.index("Helvetica Neue", "Helvetica", "Neue", "Wrong", true),
+// // string.index(true, "Helvetica")
+// ),
+// (
+// 1, 11, (1, 11), (1, 11, null), (1, 11, null, null),
+// ),
+// map.get($spec, index)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+
+// SECTION: string.is-lower-case()
+
+@include test.it(
+ "string.is-lower-case()",
+
+ (
+ equal,
+ 'string.is-lower-case("test test test")',
+ string.is-lower-case("test test test"),
+ true,
+ map.get($spec, is-lower-case)
+ ),
+
+ (
+ equal,
+ 'string.is-lower-case("TEST TEST TEST")',
+ string.is-lower-case("TEST TEST TEST"),
+ false,
+ map.get($spec, is-lower-case)
+ ),
+
+ (
+ equal,
+ 'string.is-lower-case("TEST test TEST")',
+ string.is-lower-case("TEST test TEST"),
+ false,
+ map.get($spec, is-lower-case)
+ ),
+
+);
+
+
+// @include test.equal(
+// "string.is-lower-case()",
+// (
+// string.is-lower-case("test test test"),
+// string.is-lower-case("TEST TEST TEST"),
+// string.is-lower-case("TEST test TEST"),
+// ),
+// (
+// true, false, false
+// ),
+// map.get($spec, is-lower-case)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+
+// SECTION: string.is-upper-case()
+
+@include test.it(
+ "string.is-upper-case()",
+
+ (
+ equal,
+ 'string.is-upper-case("test test test")',
+ string.is-upper-case("test test test"),
+ false,
+ map.get($spec, is-upper-case)
+ ),
+
+ (
+ equal,
+ 'string.is-upper-case("TEST TEST TEST")',
+ string.is-upper-case("TEST TEST TEST"),
+ true,
+ map.get($spec, is-upper-case)
+ ),
+
+ (
+ equal,
+ 'string.is-upper-case("TEST test TEST")',
+ string.is-upper-case("TEST test TEST"),
+ false,
+ map.get($spec, is-upper-case)
+ ),
+
+);
+
+// @include test.equal(
+// "string.is-upper-case()",
+// (
+// string.is-upper-case("test test test"),
+// string.is-upper-case("TEST TEST TEST"),
+// string.is-upper-case("TEST test TEST"),
+// ),
+// (
+// false, true, false
+// ),
+// map.get($spec, is-upper-case)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+
+// SECTION: string.is()
+
+@include test.it(
+ "string.is-upper-case()",
+
+ (
+ equal,
+ 'string.is-upper-case("test test test")',
+ string.is-upper-case("test test test"),
+ false,
+ map.get($spec, is-upper-case)
+ ),
+
+ (
+ equal,
+ 'string.is-upper-case("TEST TEST TEST")',
+ string.is-upper-case("TEST TEST TEST"),
+ true,
+ map.get($spec, is-upper-case)
+ ),
+
+ (
+ equal,
+ 'string.is-upper-case("TEST test TEST")',
+ string.is-upper-case("TEST test TEST"),
+ false,
+ map.get($spec, is-upper-case)
+ ),
+
+);
+
+// @include test.equal(
+// "string.is()",
+// (
+// string.is(test),
+// string.is(test, test1, test2),
+// string.is(1),
+// string.is(1, 2),
+// ),
+// (
+// true, true, false, false
+// ),
+// map.get($spec, is)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+
+// // SECTION: string.join()
+// @include test.equal(
+// "string.join()",
+// (
+// string.join("-"),
+// string.join("-", "a", "b", "c"),
+// string.join("-", "", "a", "b", "c"),
+// string.join("-", "a", "b", "c", ""),
+// string.join("-", "", "a", "b", "c", ""),
+// string.join("-", "a", ("b", "c")),
+// string.join("-", a, b, c),
+
+// // Join number.
+// string.join("-", a, 1, b, 2, c, 3),
+
+// // Join color.
+// string.join("-", silver, orange, green, red, blue, yellow)
+// ),
+// (
+// "", a-b-c, -a-b-c, a-b-c-, -a-b-c-, a-b-c, a-b-c, a-1-b-2-c-3, silver-orange-green-red-blue-yellow
+// ),
+// map.get($spec, join)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+
+// // SECTION: string.of()
+// @include test.equal(
+// "string.of()",
+// (
+// // lowercase
+// string.of("test test test", lowercase), // lowercase
+// string.of("TEST TEST TEST", lowercase), // notlowercase
+// string.of("test TEST test", lowercase), // notlowercase
+
+// // uppercase
+// string.of("TEST TEST TEST", uppercase), // uppercase
+// string.of("test test test", uppercase), // notuppercase
+// string.of("test TEST test", uppercase), // notuppercase
+
+// // lowercase uppercase
+// string.of("TEST TEST TEST", type lowercase uppercase, "."), // string.notlowercase.uppercase
+
+// // xcase
+// string.of("test TEST test", xcase), // variouslycase
+// string.of("TEST TEST TEST", xcase), // uppercase
+// string.of("test test test", xcase), // lowercase
+
+// string.of("TEST TEST TEST", type xcase, "."), // string.uppercase
+
+// // length
+// string.of("TEST TEST TEST", length xcase), // 14uppercase
+// string.of("TEST TEST TEST", length xcase, "."), // 14.uppercase
+// ),
+// (
+// lowercase, notlowercase, notlowercase, uppercase, notuppercase, notuppercase,
+// "string.notlowercase.uppercase", variouslycase, uppercase, lowercase, "string.uppercase",
+// "14uppercase", "14.uppercase"
+// ),
+// map.get($spec, of)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.replace-multiple()
+// @include test.equal(
+// "string. ()",
+// (
+// ),
+// (
+// ),
+// map.get($spec, is)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.replace()
+// @include test.equal(
+// "string.replace()",
+// (
+// // single replacement first occurrence
+// string.replace("bold king is hairy", first, "bold", "baloon"), // baloon king is hairy
+// string.replace("bold king is hairy", first, "bold", ""), // king is hairy
+// string.replace(":==", first, ":", ""),
+
+// // single replacement all occurrences
+// string.replace("bold king is hairy", all, "bold", "baloon"), // baloon king is hairy
+// string.replace("bold king is hairy", all, "king", "baloon"), // baloon king is hairy
+// string.replace("bold king is bold hairy", all, "bold", ""), // king is hairy
+// string.replace("bold king is bold hairy", all, "bold", "test"), // test king is test hairy
+
+// // multiple replacements
+// string.replace("bold king is hairy", all, ("bold", "king"), "baloon"), // baloon baloon is hairy
+// string.replace("bold king is hairy", first, (bold is), ""), // king hairy
+// ),
+// (
+// "baloon king is hairy", " king is hairy", "==", "baloon king is hairy", "bold baloon is hairy", " king is hairy",
+// "test king is test hairy", "baloon baloon is hairy", " king hairy"
+// ),
+// map.get($spec, replace)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.split()
+// @include test.equal(
+// "string.split()",
+// (
+// // single character
+// string.split("aaa bbb ccc", " "), // "aaa", "bbb", "ccc"
+// string.split("aaa-bbb-ccc", "-"), // "aaa", "bbb", "ccc"
+// string.split("aaa/bbb/ccc", "/"), // "aaa", "bbb", "ccc"
+// string.split("Segoe UI Emoji", " "), // "Segoe", "UI", "Emoji"
+// string.split("Segoe UI Emoji", " ", $limit: 1), // "Segoe", "UI Emoji"
+// string.split("Segoe UI Emoji", " ", $limit: 1, $bracketed: true), // ["Segoe", "UI Emoji"]
+// string.split("SF Mono Segoe UI Mono Roboto Mono", " ", $limit: 2, $bracketed: true), // ["SF", "Mono", "Segoe UI Mono Roboto Mono"]
+
+// // word `$separator`
+// string.split("aaa_DELIMITER_bbb_DELIMITER_ccc", "_DELIMITER_"), // "aaa", "bbb", "ccc"
+// string.split("aaa[separator]bbb[separator]ccc", "[separator]"), // "aaa", "bbb", "ccc"
+
+// // multiple separators
+// string.split("aaa_SEPARATOR_bbb_DELIMITER_ccc_ADD_eee_SEPARATOR_ddd", ("_DELIMITER_", "_SEPARATOR_", "_ADD_")), // "aaa", "bbb", "ccc", "eee", "ddd"
+// ),
+// (
+// ("aaa", "bbb", "ccc"), ("aaa", "bbb", "ccc"), ("aaa", "bbb", "ccc"), ("Segoe", "UI", "Emoji"), ("Segoe", "UI Emoji"),
+// ["Segoe", "UI Emoji"], ["SF", "Mono", "Segoe UI Mono Roboto Mono"], ("aaa", "bbb", "ccc"), ("aaa", "bbb", "ccc"),
+// ("aaa", "bbb", "ccc", "eee", "ddd")
+// ),
+// map.get($spec, split)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.split()
+// // @debug string.split("aaa bbb ccc", " "); // "aaa", "bbb", "ccc"
+
+// // SECTION: string.starts-with()
+// @include test.equal(
+// "string.starts-with()",
+// (
+// string.starts-with($-text, "Primary color"), // true
+// string.starts-with($-text, "color red"), // false
+
+// // Position.
+// string.starts-with($-text, "color red", 9), // true
+// string.starts-with($-text, "color red", 4), // false
+// ),
+// (true, false, true, false),
+// map.get($spec, starts-with)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.to-map()
+// @include test.equal(
+// "string.to-map()",
+// (
+// string.to-map("separator:string,number"), // ("separator": ("string", "number"))
+// string.to-map("separator:bool"), // ("separator": "bool")
+
+// // multiple key
+// string.to-map("key,value:string,number"), // ("key": ("string", "number"), "value": ("string", "number"))
+// string.to-map("key,value:string,number"), // ("key": ("string", "number"), "value": ("string", "number"))
+
+// // add multiple with `$key-value-strings`
+// string.to-map("key1,key2:string;number", "key3,key4:string;number", "key5,key6:string;number") // ("key1": "string", "key2": "number", "key3": "string", "key4": "number", "key5": "string", "key6": "number")
+// ),
+// (
+// ("separator": ("string", "number")),
+// ("separator": "bool"),
+// ("key": ("string", "number"), "value": ("string", "number")),
+// ("key": ("string", "number"), "value": ("string", "number")),
+// ("key1": "string", "key2": "number", "key3": "string", "key4": "number", "key5": "string", "key6": "number")
+// ),
+// map.get($spec, to-map)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
+
+// // SECTION: string.unquote()
+// @include test.equal(
+// "string.unquote()",
+// (
+// string.unquote("test test"),
+// string.unquote(testTest),
+// ),
+// ("test test", testTest),
+// map.get($spec, unquote)
+// ) using($id, $passed, $failed, $max, $spec, $summary, $done) {
+// @debug $spec;
+// @if $done {
+// @debug $summary;
+// @debug "---";
+// }
+// }
diff --git a/src/public-api.ts b/src/public-api.ts
new file mode 100644
index 0000000..b1b348d
--- /dev/null
+++ b/src/public-api.ts
@@ -0,0 +1,5 @@
+/*
+ * Public API Surface of sass-string
+ */
+
+export const SASS_STRING = true;
diff --git a/src/test.ts b/src/test.ts
new file mode 100644
index 0000000..5775317
--- /dev/null
+++ b/src/test.ts
@@ -0,0 +1,27 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js';
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(path: string, deep?: boolean, filter?: RegExp): {
+ (id: string): T;
+ keys(): string[];
+ };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting(),
+);
+
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().forEach(context);
diff --git a/tsconfig.lib.json b/tsconfig.lib.json
new file mode 100644
index 0000000..b77b13c
--- /dev/null
+++ b/tsconfig.lib.json
@@ -0,0 +1,15 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/lib",
+ "declaration": true,
+ "declarationMap": true,
+ "inlineSources": true,
+ "types": []
+ },
+ "exclude": [
+ "src/test.ts",
+ "**/*.spec.ts"
+ ]
+}
diff --git a/tsconfig.lib.prod.json b/tsconfig.lib.prod.json
new file mode 100644
index 0000000..06de549
--- /dev/null
+++ b/tsconfig.lib.prod.json
@@ -0,0 +1,10 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.lib.json",
+ "compilerOptions": {
+ "declarationMap": false
+ },
+ "angularCompilerOptions": {
+ "compilationMode": "partial"
+ }
+}
diff --git a/tsconfig.spec.json b/tsconfig.spec.json
new file mode 100644
index 0000000..715dd0a
--- /dev/null
+++ b/tsconfig.spec.json
@@ -0,0 +1,17 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "files": [
+ "src/test.ts"
+ ],
+ "include": [
+ "**/*.spec.ts",
+ "**/*.d.ts"
+ ]
+}