Skip to content

Commit

Permalink
Merge pull request #933 from lifeart/embed-ember-string
Browse files Browse the repository at this point in the history
feat: embed @ember/string utils
  • Loading branch information
ef4 authored Jul 25, 2023
2 parents c821b41 + 619249c commit fe81c36
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 30 deletions.
2 changes: 1 addition & 1 deletion addon/addon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Ember from 'ember';
import { assert, deprecate, warn } from '@ember/debug';
import EmberObject from '@ember/object';
import { dasherize, classify, underscore } from '@ember/string';
import { dasherize, classify, underscore } from './string';
import { DEBUG } from '@glimmer/env';
import classFactory from './utils/class-factory';

Expand Down
36 changes: 36 additions & 0 deletions addon/addon/string/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export default class Cache {
constructor(limit, func, store) {
this.limit = limit;
this.func = func;
this.store = store;
this.size = 0;
this.misses = 0;
this.hits = 0;
this.store = store || new Map();
}
get(key) {
let value = this.store.get(key);
if (this.store.has(key)) {
this.hits++;
return this.store.get(key);
}
else {
this.misses++;
value = this.set(key, this.func(key));
}
return value;
}
set(key, value) {
if (this.limit > this.size) {
this.size++;
this.store.set(key, value);
}
return value;
}
purge() {
this.store.clear();
this.size = 0;
this.hits = 0;
this.misses = 0;
}
}
122 changes: 122 additions & 0 deletions addon/addon/string/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* eslint-disable no-useless-escape */
import Cache from './cache';
let STRINGS = {};
export function setStrings(strings) {
STRINGS = strings;
}
export function getStrings() {
return STRINGS;
}
export function getString(name) {
return STRINGS[name];
}
const STRING_DASHERIZE_REGEXP = /[ _]/g;
const STRING_DASHERIZE_CACHE = new Cache(1000, (key) => decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'));
const STRING_CLASSIFY_REGEXP_1 = /^(\-|_)+(.)?/;
const STRING_CLASSIFY_REGEXP_2 = /(.)(\-|\_|\.|\s)+(.)?/g;
const STRING_CLASSIFY_REGEXP_3 = /(^|\/|\.)([a-z])/g;
const CLASSIFY_CACHE = new Cache(1000, (str) => {
const replace1 = (_match, _separator, chr) => chr ? `_${chr.toUpperCase()}` : '';
const replace2 = (_match, initialChar, _separator, chr) => initialChar + (chr ? chr.toUpperCase() : '');
const parts = str.split('/');
for (let i = 0; i < parts.length; i++) {
parts[i] = parts[i]
.replace(STRING_CLASSIFY_REGEXP_1, replace1)
.replace(STRING_CLASSIFY_REGEXP_2, replace2);
}
return parts
.join('/')
.replace(STRING_CLASSIFY_REGEXP_3, (match /*, separator, chr */) => match.toUpperCase());
});
const STRING_UNDERSCORE_REGEXP_1 = /([a-z\d])([A-Z]+)/g;
const STRING_UNDERSCORE_REGEXP_2 = /\-|\s+/g;
const UNDERSCORE_CACHE = new Cache(1000, (str) => str
.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2')
.replace(STRING_UNDERSCORE_REGEXP_2, '_')
.toLowerCase());
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g;
const DECAMELIZE_CACHE = new Cache(1000, (str) => str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase());
/**
Converts a camelized string into all lower case separated by underscores.
```javascript
import { decamelize } from '@ember/string';
decamelize('innerHTML'); // 'inner_html'
decamelize('action_name'); // 'action_name'
decamelize('css-class-name'); // 'css-class-name'
decamelize('my favorite items'); // 'my favorite items'
```
@method decamelize
@param {String} str The string to decamelize.
@return {String} the decamelized string.
@public
*/
export function decamelize(str) {
return DECAMELIZE_CACHE.get(str);
}
/**
Replaces underscores, spaces, or camelCase with dashes.
```javascript
import { dasherize } from '@ember/string';
dasherize('innerHTML'); // 'inner-html'
dasherize('action_name'); // 'action-name'
dasherize('css-class-name'); // 'css-class-name'
dasherize('my favorite items'); // 'my-favorite-items'
dasherize('privateDocs/ownerInvoice'; // 'private-docs/owner-invoice'
```
@method dasherize
@param {String} str The string to dasherize.
@return {String} the dasherized string.
@public
*/
export function dasherize(str) {
return STRING_DASHERIZE_CACHE.get(str);
}
/**
Returns the UpperCamelCase form of a string.
```javascript
import { classify } from '@ember/string';
classify('innerHTML'); // 'InnerHTML'
classify('action_name'); // 'ActionName'
classify('css-class-name'); // 'CssClassName'
classify('my favorite items'); // 'MyFavoriteItems'
classify('private-docs/owner-invoice'); // 'PrivateDocs/OwnerInvoice'
```
@method classify
@param {String} str the string to classify
@return {String} the classified string
@public
*/
export function classify(str) {
return CLASSIFY_CACHE.get(str);
}
/**
More general than decamelize. Returns the lower\_case\_and\_underscored
form of a string.
```javascript
import { underscore } from '@ember/string';
underscore('innerHTML'); // 'inner_html'
underscore('action_name'); // 'action_name'
underscore('css-class-name'); // 'css_class_name'
underscore('my favorite items'); // 'my_favorite_items'
underscore('privateDocs/ownerInvoice'); // 'private_docs/owner_invoice'
```
@method underscore
@param {String} str The string to underscore.
@return {String} the underscored string.
@public
*/
export function underscore(str) {
return UNDERSCORE_CACHE.get(str);
}
1 change: 0 additions & 1 deletion addon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
},
"devDependencies": {},
"peerDependencies": {
"@ember/string": "^3.0.1",
"ember-source": "^4.8.3 || >= 5.0.0"
},
"peerDependenciesMeta": {
Expand Down
32 changes: 5 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"dependencies": {},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^2.8.1",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
Expand Down
9 changes: 9 additions & 0 deletions test-app/tests/helpers/create-test-function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { test } from 'qunit';

export default function (fn) {
return function (given, expected, description) {
test(description, function (assert) {
assert.deepEqual(fn(given), expected);
});
};
}
51 changes: 51 additions & 0 deletions test-app/tests/unit/classify_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { module } from 'qunit';
import { classify } from 'ember-resolver/string/index';
import createTestFunction from '../helpers/create-test-function';

module('classify');

const test = createTestFunction(classify);

test('my favorite items', 'MyFavoriteItems', 'classify normal string');
test('css-class-name', 'CssClassName', 'classify dasherized string');
test('action_name', 'ActionName', 'classify underscored string');
test(
'privateDocs/ownerInvoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced camelized string'
);
test(
'private_docs/owner_invoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced underscored string'
);
test(
'private-docs/owner-invoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced dasherized string'
);
test('-view-registry', '_ViewRegistry', 'classify prefixed dasherized string');
test(
'components/-text-field',
'Components/_TextField',
'classify namespaced prefixed dasherized string'
);
test('_Foo_Bar', '_FooBar', 'classify underscore-prefixed underscored string');
test('_Foo-Bar', '_FooBar', 'classify underscore-prefixed dasherized string');
test(
'_foo/_bar',
'_Foo/_Bar',
'classify underscore-prefixed-namespaced underscore-prefixed string'
);
test(
'-foo/_bar',
'_Foo/_Bar',
'classify dash-prefixed-namespaced underscore-prefixed string'
);
test(
'-foo/-bar',
'_Foo/_Bar',
'classify dash-prefixed-namespaced dash-prefixed string'
);
test('InnerHTML', 'InnerHTML', 'does nothing with classified string');
test('_FooBar', '_FooBar', 'does nothing with classified prefixed string');
32 changes: 32 additions & 0 deletions test-app/tests/unit/dasherize_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { module } from 'qunit';
import { dasherize } from 'ember-resolver/string/index';
import createTestFunction from '../helpers/create-test-function';

module('dasherize');

const test = createTestFunction(dasherize);

test('my favorite items', 'my-favorite-items', 'dasherize normal string');
test('css-class-name', 'css-class-name', 'does nothing with dasherized string');
test('action_name', 'action-name', 'dasherize underscored string');
test('innerHTML', 'inner-html', 'dasherize camelcased string');
test(
'toString',
'to-string',
'dasherize string that is the property name of Object.prototype'
);
test(
'PrivateDocs/OwnerInvoice',
'private-docs/owner-invoice',
'dasherize namespaced classified string'
);
test(
'privateDocs/ownerInvoice',
'private-docs/owner-invoice',
'dasherize namespaced camelized string'
);
test(
'private_docs/owner_invoice',
'private-docs/owner-invoice',
'dasherize namespaced underscored string'
);
Loading

0 comments on commit fe81c36

Please sign in to comment.