Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: embed @ember/string utils #933

Merged
merged 2 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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