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

Deprecate @ember/string when used from ember-source; point users to add the @ember/string addon #20344

Merged
merged 3 commits into from
Jan 5, 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert } from '@ember/debug';
import { dasherize } from '@ember/string';
import { dasherize } from '@ember/-internals/string';
import type { CapturedArguments } from '@glimmer/interfaces';
import { createComputeRef, valueForRef } from '@glimmer/reference';
import { internalHelper } from './internal-helper';
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/utils/bindings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { get } from '@ember/-internals/metal';
import { assert } from '@ember/debug';
import { dasherize } from '@ember/string';
import { dasherize } from '@ember/-internals/string';
import type { ElementOperations } from '@glimmer/interfaces';
import type { Reference } from '@glimmer/reference';
import {
Expand Down
105 changes: 105 additions & 0 deletions packages/@ember/-internals/string/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
This module exists to separate the @ember/string methods used
internally in ember-source, from those public methods that are
now deprecated and to be removed.
*/

import { Cache } from '@ember/-internals/utils';

const STRING_DASHERIZE_REGEXP = /[ _]/g;

const STRING_DASHERIZE_CACHE = new Cache<string, string>(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<string, string>(1000, (str) => {
let replace1 = (_match: string, _separator: string, chr: string) =>
chr ? `_${chr.toUpperCase()}` : '';
let replace2 = (_match: string, initialChar: string, _separator: string, chr: string) =>
initialChar + (chr ? chr.toUpperCase() : '');
let 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_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g;

const DECAMELIZE_CACHE = new Cache<string, string>(1000, (str) =>
str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase()
);

/**
Defines string helper methods used internally in ember-source.

@class String
@private
*/

/**
Replaces underscores, spaces, or camelCase with dashes.

```javascript
import { dasherize } from '@ember/-internals/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.
@private
*/
export function dasherize(str: string): string {
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
@private
*/
export function classify(str: string): string {
return CLASSIFY_CACHE.get(str);
}

/**
Converts a camelized string into all lower case separated by underscores.

```javascript
decamelize('innerHTML'); // 'inner_html'
decamelize('action_name'); // 'action_name'
decamelize('css-class-name'); // 'css-class-name'
decamelize('my favorite items'); // 'my favorite items'
```
*/
function decamelize(str: string): string {
return DECAMELIZE_CACHE.get(str);
}
65 changes: 65 additions & 0 deletions packages/@ember/-internals/string/tests/classify_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable qunit/no-test-expect-argument */
import { classify } from '@ember/-internals/string';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

function test(assert, given, expected, description) {
assert.deepEqual(classify(given), expected, description);
}

moduleFor(
'EmberInternalsString.classify',
class extends AbstractTestCase {
['@test String classify tests'](assert) {
test(assert, 'my favorite items', 'MyFavoriteItems', 'classify normal string');
test(assert, 'css-class-name', 'CssClassName', 'classify dasherized string');
test(assert, 'action_name', 'ActionName', 'classify underscored string');
test(
assert,
'privateDocs/ownerInvoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced camelized string'
);
test(
assert,
'private_docs/owner_invoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced underscored string'
);
test(
assert,
'private-docs/owner-invoice',
'PrivateDocs/OwnerInvoice',
'classify namespaced dasherized string'
);
test(assert, '-view-registry', '_ViewRegistry', 'classify prefixed dasherized string');
test(
assert,
'components/-text-field',
'Components/_TextField',
'classify namespaced prefixed dasherized string'
);
test(assert, '_Foo_Bar', '_FooBar', 'classify underscore-prefixed underscored string');
test(assert, '_Foo-Bar', '_FooBar', 'classify underscore-prefixed dasherized string');
test(
assert,
'_foo/_bar',
'_Foo/_Bar',
'classify underscore-prefixed-namespaced underscore-prefixed string'
);
test(
assert,
'-foo/_bar',
'_Foo/_Bar',
'classify dash-prefixed-namespaced underscore-prefixed string'
);
test(
assert,
'-foo/-bar',
'_Foo/_Bar',
'classify dash-prefixed-namespaced dash-prefixed string'
);
test(assert, 'InnerHTML', 'InnerHTML', 'does nothing with classified string');
test(assert, '_FooBar', '_FooBar', 'does nothing with classified prefixed string');
}
}
);
43 changes: 43 additions & 0 deletions packages/@ember/-internals/string/tests/dasherize_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable qunit/no-test-expect-argument */
import { dasherize } from '@ember/-internals/string';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

function test(assert, given, expected, description) {
assert.deepEqual(dasherize(given), expected, description);
}

moduleFor(
'EmberInternalsString.dasherize',
class extends AbstractTestCase {
['@test String dasherize tests'](assert) {
test(assert, 'my favorite items', 'my-favorite-items', 'dasherize normal string');
test(assert, 'css-class-name', 'css-class-name', 'does nothing with dasherized string');
test(assert, 'action_name', 'action-name', 'dasherize underscored string');
test(assert, 'innerHTML', 'inner-html', 'dasherize camelcased string');
test(
assert,
'toString',
'to-string',
'dasherize string that is the property name of Object.prototype'
);
test(
assert,
'PrivateDocs/OwnerInvoice',
'private-docs/owner-invoice',
'dasherize namespaced classified string'
);
test(
assert,
'privateDocs/ownerInvoice',
'private-docs/owner-invoice',
'dasherize namespaced camelized string'
);
test(
assert,
'private_docs/owner_invoice',
'private-docs/owner-invoice',
'dasherize namespaced underscored string'
);
}
}
);
2 changes: 1 addition & 1 deletion packages/@ember/debug/container-debug-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { classify, dasherize } from '@ember/string';
import { classify, dasherize } from '@ember/-internals/string';
import EmberObject from '@ember/object';
import { typeOf } from '@ember/utils';
import type Owner from '@ember/owner';
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/debug/data-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type Owner from '@ember/owner';
import { getOwner } from '@ember/-internals/owner';
import { _backburner, next } from '@ember/runloop';
import { get } from '@ember/object';
import { dasherize } from '@ember/string';
import { dasherize } from '@ember/-internals/string';
import Namespace from '@ember/application/namespace';
import type { NativeArray } from '@ember/array';
import EmberObject from '@ember/object';
Expand Down
9 changes: 4 additions & 5 deletions packages/@ember/object/tests/observable_test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { context } from '@ember/-internals/environment';
import { run } from '@ember/runloop';
import { get, computed } from '@ember/object';
import { w } from '@ember/string';
import EmberObject, { observer } from '@ember/object';
import Observable from '@ember/object/observable';
import { A as emberA } from '@ember/array';
Expand Down Expand Up @@ -359,23 +358,23 @@ moduleFor(

['@test getting values should call function return value'](assert) {
// get each property twice. Verify return.
let keys = w('computed dependent');
let keys = ['computed', 'dependent'];

keys.forEach(function (key) {
assert.equal(object.get(key), key, `Try #1: object.get(${key}) should run function`);
assert.equal(object.get(key), key, `Try #2: object.get(${key}) should run function`);
});

// verify each call count. cached should only be called once
w('computedCalls dependentCalls').forEach((key) => {
['computedCalls', 'dependentCalls'].forEach((key) => {
assert.equal(object[key].length, 1, `non-cached property ${key} should be called 1x`);
});
}

['@test setting values should call function return value'](assert) {
// get each property twice. Verify return.
let keys = w('computed dependent');
let values = w('value1 value2');
let keys = ['computed', 'dependent'];
let values = ['value1', 'value2'];

keys.forEach((key) => {
assert.equal(
Expand Down
32 changes: 32 additions & 0 deletions packages/@ember/string/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const DECAMELIZE_CACHE = new Cache<string, string>(1000, (str) =>

@class String
@public
@deprecated Add the package `@ember/string` to your project to use in place of this module.
*/

/**
Expand All @@ -98,8 +99,10 @@ const DECAMELIZE_CACHE = new Cache<string, string>(1000, (str) =>
@param {String} str The string to split
@return {Array} array containing the split strings
@public
@deprecated Add `@ember/string` to your package.json
*/
export function w(str: string): string[] {
deprecateImportFromInternalString();
return str.split(/\s+/);
}

Expand All @@ -119,8 +122,10 @@ export function w(str: string): string[] {
@param {String} str The string to decamelize.
@return {String} the decamelized string.
@public
@deprecated Add `@ember/string` to your package.json
*/
export function decamelize(str: string): string {
deprecateImportFromInternalString();
return DECAMELIZE_CACHE.get(str);
}

Expand All @@ -141,8 +146,10 @@ export function decamelize(str: string): string {
@param {String} str The string to dasherize.
@return {String} the dasherized string.
@public
@deprecated Add `@ember/string` to your package.json
*/
export function dasherize(str: string): string {
deprecateImportFromInternalString();
return STRING_DASHERIZE_CACHE.get(str);
}

Expand All @@ -164,8 +171,10 @@ export function dasherize(str: string): string {
@param {String} str The string to camelize.
@return {String} the camelized string.
@public
@deprecated Add `@ember/string` to your package.json
*/
export function camelize(str: string): string {
deprecateImportFromInternalString();
return CAMELIZE_CACHE.get(str);
}

Expand All @@ -186,8 +195,10 @@ export function camelize(str: string): string {
@param {String} str the string to classify
@return {String} the classified string
@public
@deprecated Add `@ember/string` to your package.json
*/
export function classify(str: string): string {
deprecateImportFromInternalString();
return CLASSIFY_CACHE.get(str);
}

Expand All @@ -209,8 +220,10 @@ export function classify(str: string): string {
@param {String} str The string to underscore.
@return {String} the underscored string.
@public
@deprecated Add `@ember/string` to your package.json
*/
export function underscore(str: string): string {
deprecateImportFromInternalString();
return UNDERSCORE_CACHE.get(str);
}

Expand All @@ -231,11 +244,30 @@ export function underscore(str: string): string {
@param {String} str The string to capitalize.
@return {String} The capitalized string.
@public
@deprecated Add `@ember/string` to your package.json
*/
export function capitalize(str: string): string {
deprecateImportFromInternalString();
return CAPITALIZE_CACHE.get(str);
}

function deprecateImportFromInternalString() {
deprecate(
'Importing from `@ember/string` without having the `@ember/string` package in your project is deprecated. Please add `@ember/string` to your `package.json',
false,
{
id: 'ember-string.add-package',
for: 'ember-source',
since: {
available: '4.10',
enabled: '4.10',
},
until: '5.0.0',
url: 'https://deprecations.emberjs.com/v4.x/#toc_ember-string-add-package',
}
);
}

function deprecateImportFromString(
name: string,
message = `Importing ${name} from '@ember/string' is deprecated. Please import ${name} from '@ember/template' instead.`
Expand Down
Loading