From 176963189428aa982726e7f5574ba68ea569e8a5 Mon Sep 17 00:00:00 2001 From: David LJ Date: Thu, 9 Nov 2023 14:01:15 +0100 Subject: [PATCH] refactor: use lodash for isEmpty/Undefined (#171) --- package-lock.json | 20 ++++++++++++++++++ package.json | 2 ++ scripts/src/imagekit.mts | 15 ++++++------- scripts/src/json-file.mts | 2 +- ...project-list-item-extra-data-generator.mts | 9 ++++---- .../resource-collection-list-generator.mts | 3 ++- scripts/src/routes-file-generator.mts | 3 ++- ...open-graph-profile-metadata-from-author.ts | 19 ++++++++--------- src/app/common/css/css-min-max-media-query.ts | 3 ++- .../html/html-image-sizes-single-attribute.ts | 8 ++++--- src/app/common/is-defined.ts | 3 --- .../common/routing/get-metadata-from-json.ts | 5 +---- src/app/common/routing/get-title.ts | 5 +++-- src/app/common/social/social.service.ts | 14 +++++++------ .../images-swiper/images-swiper.component.ts | 15 +++++-------- .../project-assets-collections.service.ts | 21 ++++++++++--------- .../project-page/project-page.component.ts | 3 ++- 17 files changed, 86 insertions(+), 64 deletions(-) delete mode 100644 src/app/common/is-defined.ts diff --git a/package-lock.json b/package-lock.json index a24da0c5..67eeab05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,10 +24,12 @@ "@fortawesome/free-solid-svg-icons": "^6.4.2", "@ngaox/seo": "^5.0.0", "@nguniversal/express-engine": "^16.2.0", + "@types/lodash-es": "^4.17.11", "@unpic/core": "^0.0.35", "compression": "^1.7.4", "express": "^4.15.2", "gardevoir": "^1.0.0", + "lodash-es": "^4.17.21", "rxjs": "~7.8.0", "swiper": "^11.0.3", "tslib": "^2.3.0", @@ -4350,6 +4352,19 @@ "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.201", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", + "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-eCw8FYAWHt2DDl77s+AMLLzPn310LKohruumpucZI4oOFJkIgnlaJcy23OKMJxx4r9PeTF13Gv6w+jqjWQaYUg==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", @@ -10972,6 +10987,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", diff --git a/package.json b/package.json index db4a0600..0265ab25 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,12 @@ "@fortawesome/free-solid-svg-icons": "^6.4.2", "@ngaox/seo": "^5.0.0", "@nguniversal/express-engine": "^16.2.0", + "@types/lodash-es": "^4.17.11", "@unpic/core": "^0.0.35", "compression": "^1.7.4", "express": "^4.15.2", "gardevoir": "^1.0.0", + "lodash-es": "^4.17.21", "rxjs": "~7.8.0", "swiper": "^11.0.3", "tslib": "^2.3.0", diff --git a/scripts/src/imagekit.mts b/scripts/src/imagekit.mts index 209fa020..7e8796a1 100644 --- a/scripts/src/imagekit.mts +++ b/scripts/src/imagekit.mts @@ -7,6 +7,7 @@ import { FileObject, ImageKitOptions } from 'imagekit/dist/libs/interfaces' import { ImageAsset } from '../../src/app/common/images/image-asset.js' import { ImageCdnApi } from './image-cdn-api.mjs' import { URLSearchParams } from 'url' +import { isEmpty } from 'lodash-es' const { IMAGEKIT_URL } = imagesCdnConfigPkg @@ -50,17 +51,17 @@ export class Imagekit implements ImageCdnApi { ): Promise> { Log.group('Searching for images inside "%s" path', path) const imageAssets = await this.listImageAssetsInPath(path) - imageAssets.length > 0 - ? Log.info('Found %d images', imageAssets.length) - : Log.info('No images found') + isEmpty(imageAssets) + ? Log.info('No images found') + : Log.info('Found %d images', imageAssets.length) const imagesFromDirectories: ImageAsset[] = [] if (includeSubdirectories) { const directoryNames = await this.listDirectoryNamesInPath(path) - if (directoryNames.length > 0) { + if (isEmpty(directoryNames)) { + Log.info('No directories found') + } else { Log.info('Found %d directories', directoryNames.length) Log.info('Scanning directories...') - } else { - Log.info('No directories found') } Log.groupEnd() for (const directoryName of directoryNames) { @@ -108,7 +109,7 @@ export class Imagekit implements ImageCdnApi { const alt = (fileObject.customMetadata as CustomMetadata)?.alt const altMetadata: Pick = {} // Avoid adding if empty string to save some space - if (!!alt && alt.length > 0) { + if (!isEmpty(alt?.trim())) { altMetadata.alt = alt } // Point to specific file version diff --git a/scripts/src/json-file.mts b/scripts/src/json-file.mts index 36a458a2..00d9f9ea 100644 --- a/scripts/src/json-file.mts +++ b/scripts/src/json-file.mts @@ -11,7 +11,7 @@ export class JsonFile implements FileReader, FileWriter { return JSON.parse(await readFile(this.filepath, 'utf-8')) } catch (error) { Log.warn('Unable to read file %s', this.filepath) - return undefined + return } } diff --git a/scripts/src/project-list-item-extra-data-generator.mts b/scripts/src/project-list-item-extra-data-generator.mts index 84181112..9c1abcab 100644 --- a/scripts/src/project-list-item-extra-data-generator.mts +++ b/scripts/src/project-list-item-extra-data-generator.mts @@ -6,6 +6,7 @@ import { Log } from './log.mjs' import { Project } from '../../src/app/projects/project.js' import previewJson from '../../src/data/assets-collections/preview.json' assert { type: 'json' } import projectImageAssetPkg from '../../src/app/projects/project-page/project-image-asset.js' +import { isEmpty, isUndefined } from 'lodash-es' const { ProjectImageAsset } = projectImageAssetPkg @@ -37,7 +38,7 @@ export class ProjectListItemExtraDataGenerator { } private async getImagesByGroups(): Promise { - if (!this._imagesByGroups) { + if (isUndefined(this._imagesByGroups)) { const images = await this.getImages() const preview: ImageAsset[] = [] const others: ImageAsset[] = [] @@ -61,7 +62,7 @@ export class ProjectListItemExtraDataGenerator { } private async getImagesResource(): Promise { - if (this._imagesResource === undefined) { + if (isUndefined(this._imagesResource)) { const imagesResource = (await this.resource.childCollection.getResource( this.resourceImagesGenerator.basename, @@ -88,12 +89,12 @@ export class ProjectListItemExtraDataGenerator { } private async hasOtherImagesApartFromPreview(): Promise { - return (await this.getImagesByGroups()).others.length > 0 + return !isEmpty((await this.getImagesByGroups()).others) } private async hasVideos(): Promise { const project = (await this.resource.getData()) as Project - return !!project.youtubePlaylistId + return !isEmpty(project.youtubePlaylistId?.trim()) } private async removePreviewImages(): Promise { diff --git a/scripts/src/resource-collection-list-generator.mts b/scripts/src/resource-collection-list-generator.mts index a49b40c3..87c4b735 100644 --- a/scripts/src/resource-collection-list-generator.mts +++ b/scripts/src/resource-collection-list-generator.mts @@ -2,6 +2,7 @@ import { ResourceCollection } from './resource-collection.mjs' import { join } from 'path' import { Resource } from './resource.mjs' import { Log } from './log.mjs' +import { isEmpty } from 'lodash-es' export class ResourceCollectionListGenerator { constructor( @@ -40,7 +41,7 @@ export class ResourceCollectionListGenerator { if (this.listItemExtraDataGenerator) { Log.info('Generating and appending extra data') const listItemExtraData = await this.listItemExtraDataGenerator(resource) - if (!listItemExtraData || Object.keys(listItemExtraData).length === 0) { + if (isEmpty(listItemExtraData)) { Log.warn('No extra data generated') } // Small trick given I know resource data is JSON diff --git a/scripts/src/routes-file-generator.mts b/scripts/src/routes-file-generator.mts index a99fc16d..164e3d7a 100644 --- a/scripts/src/routes-file-generator.mts +++ b/scripts/src/routes-file-generator.mts @@ -4,6 +4,7 @@ import path from 'path' import { getRepositoryRootDir } from './get-repository-root-dir.mjs' import { readdir, readFile, writeFile } from 'fs/promises' import directoriesPkg from '../../src/app/common/directories.js' +import { isEmpty } from 'lodash-es' const { CONTENTS_DIR, PROJECTS_DIR } = directoriesPkg @@ -36,7 +37,7 @@ export class RoutesFileGenerator { path.join(this.PROJECTS_PATH, projectDir.name), { withFileTypes: true }, ) - return projectDirFiles.length > 0 ? projectDir.name : '' + return !isEmpty(projectDirFiles) ? projectDir.name : '' }), ) ).filter((projectDir) => projectDir.length) diff --git a/src/app/about-page/add-open-graph-profile-metadata-from-author.ts b/src/app/about-page/add-open-graph-profile-metadata-from-author.ts index db0863ef..009f216e 100644 --- a/src/app/about-page/add-open-graph-profile-metadata-from-author.ts +++ b/src/app/about-page/add-open-graph-profile-metadata-from-author.ts @@ -1,5 +1,6 @@ import { IPageSeoData } from '@ngaox/seo' import { MetaDefinition } from '@angular/platform-browser' +import { isEmpty } from 'lodash-es' interface OpenGraphProfileMetadata { readonly firstName: string @@ -13,28 +14,30 @@ export function addOpenGraphProfileMetadata( openGraphProfileMetadata: OpenGraphProfileMetadata, ): IPageSeoData { const extras: MetaDefinition[] = [] - if (isNonEmptyString(openGraphProfileMetadata.firstName)) { + if (!isEmpty(openGraphProfileMetadata.firstName)) { extras.push({ property: 'og:profile:first_name', content: openGraphProfileMetadata.firstName, }) } - if (isNonEmptyString(openGraphProfileMetadata.lastName)) { + if (!isEmpty(openGraphProfileMetadata.lastName.trim())) { extras.push({ property: 'og:profile:last_name', content: openGraphProfileMetadata.lastName, }) } - if (isNonEmptyString(openGraphProfileMetadata.gender)) { + if (!isEmpty(openGraphProfileMetadata.gender?.trim())) { extras.push({ property: 'og:profile:gender', - content: openGraphProfileMetadata.gender, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + content: openGraphProfileMetadata.gender!, }) } - if (isNonEmptyString(openGraphProfileMetadata.username)) { + if (!isEmpty(openGraphProfileMetadata.username?.trim())) { extras.push({ property: 'og:profile:username', - content: openGraphProfileMetadata.username, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + content: openGraphProfileMetadata.username!, }) } @@ -44,7 +47,3 @@ export function addOpenGraphProfileMetadata( extra: [...(metadata.extra ?? []), ...extras], } } - -function isNonEmptyString(string: string | undefined): string is string { - return string !== undefined && string.trim().length > 0 -} diff --git a/src/app/common/css/css-min-max-media-query.ts b/src/app/common/css/css-min-max-media-query.ts index 4ab3cdab..68ff805e 100644 --- a/src/app/common/css/css-min-max-media-query.ts +++ b/src/app/common/css/css-min-max-media-query.ts @@ -2,6 +2,7 @@ import { CssMinWidthMediaFeature } from './css-min-width-media-feature' import { CssMaxWidthMediaFeature } from './css-max-width-media-feature' import { CssMediaQuery } from './css-media-query' import { CssUnit } from './unit/css-unit' +import { isUndefined } from 'lodash-es' export class CssMinMaxMediaQuery< MinUnit extends CssUnit, @@ -51,7 +52,7 @@ export class CssMinMaxMediaQuery< public toString(): string { const mediaFeatures = [this.min, this.max].filter( - (mediaFeature) => !!mediaFeature, + (mediaFeature) => !isUndefined(mediaFeature), ) if (mediaFeatures.length === 1) { return mediaFeatures.join('') diff --git a/src/app/common/html/html-image-sizes-single-attribute.ts b/src/app/common/html/html-image-sizes-single-attribute.ts index 87a48148..b2f42754 100644 --- a/src/app/common/html/html-image-sizes-single-attribute.ts +++ b/src/app/common/html/html-image-sizes-single-attribute.ts @@ -1,6 +1,6 @@ import { CssMediaQuery } from '../css/css-media-query' import { CssUnit } from '../css/unit/css-unit' -import { isDefined } from '../is-defined' +import { isUndefined } from 'lodash-es' export class HtmlImageSizesSingleAttribute { constructor( @@ -9,7 +9,9 @@ export class HtmlImageSizesSingleAttribute { ) {} public toString(): string { - const parts = [this.mediaQuery, this.width].filter(isDefined) - return parts.map((part) => part.toString()).join(' ') + if (isUndefined(this.mediaQuery)) { + return this.width.toString() + } + return [this.mediaQuery, this.width].join(' ') } } diff --git a/src/app/common/is-defined.ts b/src/app/common/is-defined.ts deleted file mode 100644 index 0a05411f..00000000 --- a/src/app/common/is-defined.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function isDefined(val: T | undefined | null): val is T { - return val !== undefined && val !== null -} diff --git a/src/app/common/routing/get-metadata-from-json.ts b/src/app/common/routing/get-metadata-from-json.ts index 0d4f9200..8bdbd2a0 100644 --- a/src/app/common/routing/get-metadata-from-json.ts +++ b/src/app/common/routing/get-metadata-from-json.ts @@ -11,9 +11,6 @@ export function getMetadataFromJson( ...jsonMetadata, url: getCanonicalUrlForPath(...pathSegments), title: getTitle(jsonMetadata.title), - keywords: - !!jsonMetadata.keywords && jsonMetadata.keywords.length - ? jsonMetadata.keywords.join(', ') - : undefined, + keywords: jsonMetadata.keywords?.join(', '), } } diff --git a/src/app/common/routing/get-title.ts b/src/app/common/routing/get-title.ts index a0ae1952..e1c991fe 100644 --- a/src/app/common/routing/get-title.ts +++ b/src/app/common/routing/get-title.ts @@ -1,7 +1,8 @@ import defaultMetadata from '../../../data/misc/metadata.json' +import { isEmpty } from 'lodash-es' -export function getTitle(pageTitle: string) { - if (!pageTitle || pageTitle.length === 0) { +export function getTitle(pageTitle: string | undefined | null) { + if (isEmpty(pageTitle?.trim())) { return defaultMetadata.siteName } return `${pageTitle} | ${defaultMetadata.siteName}` diff --git a/src/app/common/social/social.service.ts b/src/app/common/social/social.service.ts index c9045243..4955f994 100644 --- a/src/app/common/social/social.service.ts +++ b/src/app/common/social/social.service.ts @@ -6,6 +6,7 @@ import { faLinkedinIn, faTiktok, } from '@fortawesome/free-brands-svg-icons' +import { isEmpty } from 'lodash-es' @Injectable({ providedIn: 'root', @@ -30,10 +31,11 @@ export class SocialService { this.isSocialName(author.social.preferred) ) { const mainUsername = author.social[author.social.preferred] - if (!!mainUsername && mainUsername.trim().length > 0) + if (!isEmpty(mainUsername?.trim())) return this.mapFromNameAndUsername( author.social.preferred, - mainUsername, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + mainUsername!, ) } const sortedSocials = Array.from(socials).sort( @@ -41,8 +43,8 @@ export class SocialService { (this.mainLinkPreferences.get(a.name) ?? 0) - (this.mainLinkPreferences.get(b.name) ?? 0), ) - if (sortedSocials.length === 0) { - return undefined + if (isEmpty(sortedSocials)) { + return } return sortedSocials[0] } @@ -56,8 +58,8 @@ export class SocialService { ) return entries .filter( - ([name, username]) => - !!name && name.length > 0 && !!username && username.length > 0, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ([_, username]) => !isEmpty(username?.trim()), ) .map(([name, username]) => this.mapFromNameAndUsername(name, username as string), diff --git a/src/app/projects/images-swiper/images-swiper.component.ts b/src/app/projects/images-swiper/images-swiper.component.ts index 84e53416..cd815c6b 100644 --- a/src/app/projects/images-swiper/images-swiper.component.ts +++ b/src/app/projects/images-swiper/images-swiper.component.ts @@ -12,6 +12,7 @@ import { } from 'swiper/modules' import { ResponsiveImageAttributes } from '../../common/images/responsive-image-attributes' import { ImageAsset } from '../../common/images/image-asset' +import { isEmpty, isNumber } from 'lodash-es' // There's no fancier way to install Web Components in Angular :P // https://stackoverflow.com/a/75353889/3263250 @@ -75,11 +76,8 @@ export class ImagesSwiperComponent implements OnChanges { const defaultSlidesPerView = swiperOptions.slidesPerView const allSlidesPerView = breakpointSlidesPerViews .concat([defaultSlidesPerView]) - .filter( - (slidesPerView): slidesPerView is number => - slidesPerView !== undefined && slidesPerView !== 'auto', - ) - if (allSlidesPerView.length === 0) { + .filter(isNumber) + if (isEmpty(allSlidesPerView)) { return null } return Math.max(...allSlidesPerView) @@ -89,14 +87,11 @@ export class ImagesSwiperComponent implements OnChanges { swiperOptions: SwiperOptions, maxSlidesPerView: number | null, ): SwiperOptions { - if ( - swiperOptions.loop !== undefined || - swiperOptions.rewind !== undefined - ) { + if (!isEmpty(swiperOptions.loop) || !isEmpty(swiperOptions.rewind)) { return swiperOptions } const loop = - !!this.images && + !isEmpty(this.images) && maxSlidesPerView !== null && this.images.length > maxSlidesPerView * 2 return { diff --git a/src/app/projects/project-page/project-assets-collections.service.ts b/src/app/projects/project-page/project-assets-collections.service.ts index c67ce2ae..b28149f0 100644 --- a/src/app/projects/project-page/project-assets-collections.service.ts +++ b/src/app/projects/project-page/project-assets-collections.service.ts @@ -17,6 +17,7 @@ import { ProjectImageAsset } from './project-image-asset' import { LookbookNameAndSlug } from '../lookbook-name-and-slug' import { AssetsCollectionSize } from './assets-collection-size' import { IMAGES_FILENAME } from '../../common/files' +import { isEmpty } from 'lodash-es' @Injectable() export class ProjectAssetsCollectionsService { @@ -145,13 +146,14 @@ export class ProjectAssetsCollectionsService { const projectImageAssets = projectImageAssetsByCollectionSlug.get( assetCollection.slug, ) - if (projectImageAssets && projectImageAssets.length > 0) { + if (!isEmpty(projectImageAssets)) { if (assetCollection.slug === this.lookbookCollectionSlug) { const projectImageAssetsBySubcollectionSlug = new Map< string, ReadonlyArray >() - for (const projectImageAsset of projectImageAssets) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (const projectImageAsset of projectImageAssets!) { const subCollection = projectImageAsset.subCollection const assetsInCollection = projectImageAssetsBySubcollectionSlug.get(subCollection) ?? [] @@ -167,17 +169,15 @@ export class ProjectAssetsCollectionsService { projectImageAssetsBySubcollectionSlug.get( lookbookNameAndSlug.slug, ) - if ( - subcollectionImageAssets && - subcollectionImageAssets.length > 0 - ) { + if (!isEmpty(subcollectionImageAssets)) { lookbookCollections.push( new ImageAssetsCollection( { ...assetCollection, name: `${assetCollection.name} ${index} "${lookbookNameAndSlug.name}"`, }, - subcollectionImageAssets.map(({ asset }) => asset), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + subcollectionImageAssets!.map(({ asset }) => asset), ), ) projectImageAssetsBySubcollectionSlug.delete( @@ -191,7 +191,7 @@ export class ProjectAssetsCollectionsService { ) .flat() .map(({ asset }) => asset) - if (restOfLookbookImages.length > 0) { + if (!isEmpty(restOfLookbookImages)) { lookbookCollections.push( new ImageAssetsCollection( { @@ -208,7 +208,8 @@ export class ProjectAssetsCollectionsService { assetCollections.push( new ImageAssetsCollection( assetCollection, - projectImageAssets.map(({ asset }) => asset), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + projectImageAssets!.map(({ asset }) => asset), ), ) projectImageAssetsByCollectionSlug.delete(assetCollection.slug) @@ -218,7 +219,7 @@ export class ProjectAssetsCollectionsService { const restOfImages = Array.from(projectImageAssetsByCollectionSlug.values()) .flat() .map(({ asset }) => asset) - if (restOfImages.length > 0) { + if (!isEmpty(restOfImages)) { assetCollections.push( new ImageAssetsCollection( { diff --git a/src/app/projects/project-page/project-page.component.ts b/src/app/projects/project-page/project-page.component.ts index eb1d3bd7..ee2b69d6 100644 --- a/src/app/projects/project-page/project-page.component.ts +++ b/src/app/projects/project-page/project-page.component.ts @@ -18,6 +18,7 @@ import { CssPxUnit, Px } from '../../common/css/unit/px' import { Vw } from '../../common/css/unit/vw' import { CssMinMaxMediaQuery } from '../../common/css/css-min-max-media-query' import { Breakpoint } from '../../common/style/breakpoint' +import { isEmpty } from 'lodash-es' @Component({ selector: 'app-project-page', @@ -118,7 +119,7 @@ export class ProjectPageComponent { })), ), tap((assetsCollections) => { - if (assetsCollections.length === 0) { + if (isEmpty(assetsCollections)) { this.navigatorService.displayNotFoundPage() } }),