From ca0559444c28bc8bb4136d19f78c0688a2954c56 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 2 Oct 2021 14:01:50 +0200 Subject: [PATCH] avoid usage of to*Case + add project lint rule Signed-off-by: Patrik Oldsberg --- .changeset/warm-balloons-peel.md | 21 +++++++++++++++++++ .eslintrc.js | 17 +++++++++++++++ .../scaffolder/customScaffolderExtensions.tsx | 2 +- packages/catalog-model/src/entity/ref.ts | 12 +++++++---- packages/cli-common/src/paths.ts | 2 +- .../src/layout/Sidebar/Items.tsx | 4 ++-- .../src/github/GithubCredentialsProvider.ts | 4 +++- .../test-utils-core/src/testUtils/Keyboard.js | 2 +- plugins/badges/src/api/BadgesClient.ts | 5 +++-- .../src/components/EntityRefLink/format.ts | 7 +++++-- .../src/hooks/useRelatedEntities.ts | 10 ++++++--- plugins/catalog-react/src/routes.ts | 5 +++-- .../src/utils/getEntityRelations.ts | 4 +++- .../src/components/EntitySwitch/conditions.ts | 4 +++- plugins/cost-insights/src/utils/grammar.ts | 2 +- plugins/home/package.json | 1 + .../RandomJoke/Settings.tsx | 3 ++- .../UptimeMonitorCheckType.tsx | 3 +++ plugins/kubernetes/src/utils/owner.ts | 4 ++-- .../LegacySearchPage/LegacySearchResult.tsx | 5 +++-- plugins/shortcuts/src/ShortcutItem.tsx | 2 ++ plugins/sonarqube/src/api/SonarQubeClient.ts | 6 ++++-- .../FeatureFlags/UserSettingsFeatureFlags.tsx | 4 ++-- plugins/xcmetrics/package.json | 1 + plugins/xcmetrics/src/utils/format.ts | 4 ++-- 25 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 .changeset/warm-balloons-peel.md diff --git a/.changeset/warm-balloons-peel.md b/.changeset/warm-balloons-peel.md new file mode 100644 index 0000000000..fe1c390697 --- /dev/null +++ b/.changeset/warm-balloons-peel.md @@ -0,0 +1,21 @@ +--- +'@backstage/catalog-model': patch +'@backstage/cli-common': patch +'@backstage/core-components': patch +'@backstage/integration': patch +'@backstage/test-utils-core': patch +'@backstage/plugin-badges': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-home': patch +'@backstage/plugin-ilert': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-search': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-xcmetrics': patch +--- + +Avoid usage of `.to*Case()`, preferring `.toLocale*Case('en-US')` instead. diff --git a/.eslintrc.js b/.eslintrc.js index 6c8b62e4aa..ec7b4f9130 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,5 +34,22 @@ module.exports = { onNonMatchingHeader: 'replace', }, ], + 'no-restricted-syntax': [ + 'error', + { + message: + "Avoid using .toLowerCase(), use .toLocaleLowerCase('en-US') instead. " + + 'This rule can sometimes be ignored when converting text to be displayed to the user.', + selector: + "CallExpression[arguments.length=0] > MemberExpression[property.name='toLowerCase']", + }, + { + message: + "Avoid using .toUpperCase(), use .toLocaleUpperCase('en-US') instead. " + + 'This rule can sometimes be ignored when converting text to be displayed to the user.', + selector: + "CallExpression[arguments.length=0] > MemberExpression[property.name='toUpperCase']", + }, + ], }, }; diff --git a/packages/app/src/components/scaffolder/customScaffolderExtensions.tsx b/packages/app/src/components/scaffolder/customScaffolderExtensions.tsx index 5ef4f617a7..1c96e9325c 100644 --- a/packages/app/src/components/scaffolder/customScaffolderExtensions.tsx +++ b/packages/app/src/components/scaffolder/customScaffolderExtensions.tsx @@ -26,7 +26,7 @@ export const LowerCaseValuePickerFieldExtension = scaffolderPlugin.provide( name: 'LowerCaseValuePicker', component: TextValuePicker, validation: (value: string, validation: FieldValidation) => { - if (value.toLowerCase() !== value) { + if (value.toLocaleLowerCase('en-US') !== value) { validation.addError('Only lowercase values are allowed.'); } }, diff --git a/packages/catalog-model/src/entity/ref.ts b/packages/catalog-model/src/entity/ref.ts index c1b9383eeb..6a38e80dfe 100644 --- a/packages/catalog-model/src/entity/ref.ts +++ b/packages/catalog-model/src/entity/ref.ts @@ -248,7 +248,9 @@ export function stringifyEntityRef( name = ref.name; } - return `${kind.toLowerCase()}:${namespace.toLowerCase()}/${name.toLowerCase()}`; + return `${kind.toLocaleLowerCase('en-US')}:${namespace.toLocaleLowerCase( + 'en-US', + )}/${name.toLocaleLowerCase('en-US')}`; } /** @@ -296,8 +298,10 @@ export function compareEntityToRef( } return ( - entityKind.toLowerCase() === refKind.toLowerCase() && - entityNamespace.toLowerCase() === refNamespace.toLowerCase() && - entityName.toLowerCase() === refName.toLowerCase() + entityKind.toLocaleLowerCase('en-US') === + refKind.toLocaleLowerCase('en-US') && + entityNamespace.toLocaleLowerCase('en-US') === + refNamespace.toLocaleLowerCase('en-US') && + entityName.toLocaleLowerCase('en-US') === refName.toLocaleLowerCase('en-US') ); } diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 90239dfa4a..4aa9d4b76e 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -115,7 +115,7 @@ export function findPaths(searchDir: string): Paths { // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency const targetDir = fs .realpathSync(process.cwd()) - .replace(/^[a-z]:/, str => str.toUpperCase()); + .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US')); // Lazy load this as it will throw an error if we're not inside the Backstage repo. let ownRoot = ''; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index fbe1ffbb82..8bd80b6825 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -193,8 +193,8 @@ export const WorkaroundNavLink = React.forwardRef< let { pathname: toPathname } = useResolvedPath(to); if (!caseSensitive) { - locationPathname = locationPathname.toLowerCase(); - toPathname = toPathname.toLowerCase(); + locationPathname = locationPathname.toLocaleLowerCase('en-US'); + toPathname = toPathname.toLocaleLowerCase('en-US'); } let isActive = locationPathname === toPathname; diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index 62c7cd7a57..7e6057fc8d 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -141,7 +141,9 @@ class GithubAppManager { private async getInstallationData(owner: string): Promise { const allInstallations = await this.getInstallations(); const installation = allInstallations.find( - inst => inst.account?.login?.toLowerCase() === owner.toLowerCase(), + inst => + inst.account?.login?.toLocaleLowerCase('en-US') === + owner.toLocaleLowerCase('en-US'), ); if (installation) { return { diff --git a/packages/test-utils-core/src/testUtils/Keyboard.js b/packages/test-utils-core/src/testUtils/Keyboard.js index 2f8e218762..3f45ca90f6 100644 --- a/packages/test-utils-core/src/testUtils/Keyboard.js +++ b/packages/test-utils-core/src/testUtils/Keyboard.js @@ -87,7 +87,7 @@ export class Keyboard { const attrs = [...element.attributes] .map(attr => `${attr.name}="${attr.value}"`) .join(' '); - return `<${element.nodeName.toLowerCase()} ${attrs}>`; + return `<${element.nodeName.toLocaleLowerCase('en-US')} ${attrs}>`; } get focused() { diff --git a/plugins/badges/src/api/BadgesClient.ts b/plugins/badges/src/api/BadgesClient.ts index 8f65e1b9de..d81b042ce5 100644 --- a/plugins/badges/src/api/BadgesClient.ts +++ b/plugins/badges/src/api/BadgesClient.ts @@ -61,9 +61,10 @@ export class BadgesClient implements BadgesApi { private getEntityRouteParams(entity: Entity) { return { - kind: entity.kind.toLowerCase(), + kind: entity.kind.toLocaleLowerCase('en-US'), namespace: - entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE, + entity.metadata.namespace?.toLocaleLowerCase('en-US') ?? + ENTITY_DEFAULT_NAMESPACE, name: entity.metadata.name, }; } diff --git a/plugins/catalog-react/src/components/EntityRefLink/format.ts b/plugins/catalog-react/src/components/EntityRefLink/format.ts index ebb1313b88..f3977ccc95 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/format.ts +++ b/plugins/catalog-react/src/components/EntityRefLink/format.ts @@ -44,10 +44,13 @@ export function formatEntityRefTitle( namespace = undefined; } - kind = kind.toLowerCase(); + kind = kind.toLocaleLowerCase('en-US'); return `${serializeEntityRef({ - kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind, + kind: + defaultKind && defaultKind.toLocaleLowerCase('en-US') === kind + ? undefined + : kind, name, namespace, })}`; diff --git a/plugins/catalog-react/src/hooks/useRelatedEntities.ts b/plugins/catalog-react/src/hooks/useRelatedEntities.ts index 6d6553c9e2..d15211f158 100644 --- a/plugins/catalog-react/src/hooks/useRelatedEntities.ts +++ b/plugins/catalog-react/src/hooks/useRelatedEntities.ts @@ -39,8 +39,12 @@ export function useRelatedEntities( entity.relations && entity.relations.filter( r => - (!type || r.type.toLowerCase() === type.toLowerCase()) && - (!kind || r.target.kind.toLowerCase() === kind.toLowerCase()), + (!type || + r.type.toLocaleLowerCase('en-US') === + type.toLocaleLowerCase('en-US')) && + (!kind || + r.target.kind.toLocaleLowerCase('en-US') === + kind.toLocaleLowerCase('en-US')), ); if (!relations) { @@ -54,7 +58,7 @@ export function useRelatedEntities( // `filter=kind=component,namespace=default,name=example1,example2` const relationsByKindAndNamespace: EntityRelation[][] = Object.values( groupBy(relations, ({ target }) => { - return `${target.kind}:${target.namespace}`.toLowerCase(); + return `${target.kind}:${target.namespace}`.toLocaleLowerCase('en-US'); }), ); diff --git a/plugins/catalog-react/src/routes.ts b/plugins/catalog-react/src/routes.ts index 14ab140fce..42f394ef61 100644 --- a/plugins/catalog-react/src/routes.ts +++ b/plugins/catalog-react/src/routes.ts @@ -39,9 +39,10 @@ export const entityRouteRef = entityRoute; // entity instance export function entityRouteParams(entity: Entity) { return { - kind: entity.kind.toLowerCase(), + kind: entity.kind.toLocaleLowerCase('en-US'), namespace: - entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE, + entity.metadata.namespace?.toLocaleLowerCase('en-US') ?? + ENTITY_DEFAULT_NAMESPACE, name: entity.metadata.name, } as const; } diff --git a/plugins/catalog-react/src/utils/getEntityRelations.ts b/plugins/catalog-react/src/utils/getEntityRelations.ts index 3e6a957eff..0045fac4f5 100644 --- a/plugins/catalog-react/src/utils/getEntityRelations.ts +++ b/plugins/catalog-react/src/utils/getEntityRelations.ts @@ -31,7 +31,9 @@ export function getEntityRelations( if (filter?.kind) { entityNames = entityNames?.filter( - e => e.kind.toLowerCase() === filter.kind.toLowerCase(), + e => + e.kind.toLocaleLowerCase('en-US') === + filter.kind.toLocaleLowerCase('en-US'), ); } diff --git a/plugins/catalog/src/components/EntitySwitch/conditions.ts b/plugins/catalog/src/components/EntitySwitch/conditions.ts index b03d1dac04..dd95a256bc 100644 --- a/plugins/catalog/src/components/EntitySwitch/conditions.ts +++ b/plugins/catalog/src/components/EntitySwitch/conditions.ts @@ -17,7 +17,9 @@ import { Entity, ComponentEntity } from '@backstage/catalog-model'; function strCmp(a: string | undefined, b: string | undefined): boolean { - return Boolean(a && a?.toLowerCase() === b?.toLowerCase()); + return Boolean( + a && a?.toLocaleLowerCase('en-US') === b?.toLocaleLowerCase('en-US'), + ); } export function isKind(kind: string) { diff --git a/plugins/cost-insights/src/utils/grammar.ts b/plugins/cost-insights/src/utils/grammar.ts index 81e9fde80c..321e99c841 100644 --- a/plugins/cost-insights/src/utils/grammar.ts +++ b/plugins/cost-insights/src/utils/grammar.ts @@ -26,7 +26,7 @@ export const indefiniteArticleOf = ( articles: [string, string], word: string, ) => { - const firstChar = word.charAt(0).toLowerCase(); + const firstChar = word.charAt(0).toLocaleLowerCase('en-US'); return firstChar in vowels ? `${articles[1]} ${word}` : `${articles[0]} ${word}`; diff --git a/plugins/home/package.json b/plugins/home/package.json index 60ec7161e2..cd04f67ff5 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -28,6 +28,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "@types/react": "*", + "lodash": "^4.17.21", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/home/src/homePageComponents/RandomJoke/Settings.tsx b/plugins/home/src/homePageComponents/RandomJoke/Settings.tsx index ee5de69dc6..20e7f0b813 100644 --- a/plugins/home/src/homePageComponents/RandomJoke/Settings.tsx +++ b/plugins/home/src/homePageComponents/RandomJoke/Settings.tsx @@ -22,6 +22,7 @@ import { } from '@material-ui/core'; import React from 'react'; import { useRandomJoke, JokeType } from './Context'; +import upperFirst from 'lodash/upperFirst'; export const Settings = () => { const { type, handleChangeType } = useRandomJoke(); @@ -39,7 +40,7 @@ export const Settings = () => { key={t} value={t} control={} - label={`${t.slice(0, 1).toUpperCase()}${t.slice(1)}`} + label={upperFirst(t)} /> ))} diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx index 400adbd0d0..a39901836f 100644 --- a/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx +++ b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { UptimeMonitor } from '../../types'; import Typography from '@material-ui/core/Typography'; @@ -27,12 +28,14 @@ export const UptimeMonitorCheckType = ({ return ( {`${uptimeMonitor.checkType.toUpperCase()} πŸ‡©πŸ‡ͺ`} ); default: return ( {`${uptimeMonitor.checkType.toUpperCase()} πŸ‡ΊπŸ‡Έ`} ); } diff --git a/plugins/kubernetes/src/utils/owner.ts b/plugins/kubernetes/src/utils/owner.ts index f793ad565c..b30beb7fad 100644 --- a/plugins/kubernetes/src/utils/owner.ts +++ b/plugins/kubernetes/src/utils/owner.ts @@ -61,8 +61,8 @@ export const getMatchingHpa = ( ): V1HorizontalPodAutoscaler | undefined => { return hpas.find(hpa => { return ( - (hpa.spec?.scaleTargetRef?.kind ?? '').toLowerCase() === - ownerKind.toLowerCase() && + (hpa.spec?.scaleTargetRef?.kind ?? '').toLocaleLowerCase('en-US') === + ownerKind.toLocaleLowerCase('en-US') && (hpa.spec?.scaleTargetRef?.name ?? '') === (ownerName ?? 'unknown-deployment') ); diff --git a/plugins/search/src/components/LegacySearchPage/LegacySearchResult.tsx b/plugins/search/src/components/LegacySearchPage/LegacySearchResult.tsx index 5efce583fd..c5c3783819 100644 --- a/plugins/search/src/components/LegacySearchPage/LegacySearchResult.tsx +++ b/plugins/search/src/components/LegacySearchPage/LegacySearchResult.tsx @@ -155,8 +155,9 @@ export const SearchResult = ({ searchQuery }: SearchResultProps) => { ? entity.spec?.lifecycle : undefined, url: `/catalog/${ - entity.metadata.namespace?.toLowerCase() || ENTITY_DEFAULT_NAMESPACE - }/${entity.kind.toLowerCase()}/${entity.metadata.name}`, + entity.metadata.namespace?.toLocaleLowerCase('en-US') || + ENTITY_DEFAULT_NAMESPACE + }/${entity.kind.toLocaleLowerCase('en-US')}/${entity.metadata.name}`, })); }, []); diff --git a/plugins/shortcuts/src/ShortcutItem.tsx b/plugins/shortcuts/src/ShortcutItem.tsx index ce0af3d08b..d2ac716737 100644 --- a/plugins/shortcuts/src/ShortcutItem.tsx +++ b/plugins/shortcuts/src/ShortcutItem.tsx @@ -41,8 +41,10 @@ const useStyles = makeStyles({ const getIconText = (title: string) => title.split(' ').length === 1 ? // If there's only one word, keep the first two characters + // eslint-disable-next-line no-restricted-syntax title[0].toUpperCase() + title[1].toLowerCase() : // If there's more than one word, take the first character of the first two words + // eslint-disable-next-line no-restricted-syntax title .replace(/\B\W/g, '') .split(' ') diff --git a/plugins/sonarqube/src/api/SonarQubeClient.ts b/plugins/sonarqube/src/api/SonarQubeClient.ts index d2283693ea..85da65ecf3 100644 --- a/plugins/sonarqube/src/api/SonarQubeClient.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.ts @@ -138,11 +138,13 @@ export class SonarQubeClient implements SonarQubeApi { getIssuesUrl: identifier => `${this.baseUrl}project/issues?id=${encodeURIComponent( componentKey, - )}&types=${identifier.toUpperCase()}&resolved=false`, + )}&types=${identifier.toLocaleUpperCase('en-US')}&resolved=false`, getComponentMeasuresUrl: identifier => `${this.baseUrl}component_measures?id=${encodeURIComponent( componentKey, - )}&metric=${identifier.toLowerCase()}&resolved=false&view=list`, + )}&metric=${identifier.toLocaleLowerCase( + 'en-US', + )}&resolved=false&view=list`, getSecurityHotspotsUrl: () => `${this.baseUrl}project/security_hotspots?id=${encodeURIComponent( componentKey, diff --git a/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx b/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx index a92ef43d32..e8f488de85 100644 --- a/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx @@ -77,12 +77,12 @@ export const UserSettingsFeatureFlags = () => { const filterInputParts = filterInput .split(/\s/) - .map(part => part.trim().toLowerCase()); + .map(part => part.trim().toLocaleLowerCase('en-US')); filterInputParts.forEach( part => (filteredFeatureFlags = filteredFeatureFlags.filter(featureFlag => - featureFlag.name.toLowerCase().includes(part), + featureFlag.name.toLocaleLowerCase('en-US').includes(part), )), ); diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 1ab89c9b39..2053aa0e45 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -28,6 +28,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", + "lodash": "^4.17.21", "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/plugins/xcmetrics/src/utils/format.ts b/plugins/xcmetrics/src/utils/format.ts index e21e95a7b2..b89cf97e42 100644 --- a/plugins/xcmetrics/src/utils/format.ts +++ b/plugins/xcmetrics/src/utils/format.ts @@ -15,6 +15,7 @@ */ import { DateTime, Duration } from 'luxon'; import { BuildStatus } from '../api'; +import upperFirst from 'lodash/upperFirst'; export const formatDuration = (seconds: number) => { const duration = Duration.fromObject({ @@ -48,5 +49,4 @@ export const formatPercentage = (number: number) => { return `${Math.round(number * 100)} %`; }; -export const formatStatus = (status: BuildStatus) => - status[0].toUpperCase() + status.slice(1); +export const formatStatus = (status: BuildStatus) => upperFirst(status);