Skip to content

Commit

Permalink
Deprecate @ember/string when used from ember-source; point users to add
Browse files Browse the repository at this point in the history
the `@ember/string` addon

- Duplicate a portion of @ember/string in @ember/-internals/string for
internal use by ember-source without triggering deprecations

(cherry picked from commit b341648)
  • Loading branch information
kategengler committed Jan 12, 2023
1 parent d58ac53 commit f1988a3
Show file tree
Hide file tree
Showing 18 changed files with 277 additions and 20 deletions.
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 { InternalOwner } from '@ember/-internals/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 { InternalOwner } from '@ember/-internals/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
4 changes: 3 additions & 1 deletion packages/@ember/string/tests/camelize_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { camelize } from '@ember/string';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

function test(assert, given, expected, description) {
assert.deepEqual(camelize(given), expected, description);
expectDeprecation(() => {
assert.deepEqual(camelize(given), expected, description);
}, 'Importing from `@ember/string` without having the `@ember/string` package in your project is deprecated. Please add `@ember/string` to your `package.json');
}

moduleFor(
Expand Down
Loading

0 comments on commit f1988a3

Please sign in to comment.