From 0373744ab72a9556c1486fc99d93e74f8288962f Mon Sep 17 00:00:00 2001 From: GustaveWPM Date: Tue, 27 Feb 2024 18:58:51 +0100 Subject: [PATCH] fix: implemented breadcrumbs validation on build properly --- .../src/lib/__tests__/prebuild.etc.test.ts | 28 +++++++++---------- packages/prebuilder/src/lib/etc.ts | 6 ++-- .../retrieveLocaleFileInfosMetadatas.ts | 4 +-- src/components/ui/breadcrumbs/Breadcrumbs.tsx | 4 +++ src/i18n/locales/schema.ts | 14 ++++++---- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/prebuilder/src/lib/__tests__/prebuild.etc.test.ts b/packages/prebuilder/src/lib/__tests__/prebuild.etc.test.ts index 8e900cc51..2a53b6e9c 100644 --- a/packages/prebuilder/src/lib/__tests__/prebuild.etc.test.ts +++ b/packages/prebuilder/src/lib/__tests__/prebuild.etc.test.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from 'vitest'; import getRawDataFromBracesDeclaration from '../getRawDataFromBracesDeclaration'; -import { objInnerToObj } from '../etc'; +import { localesInfosInnerToObj } from '../etc'; -describe('objInnerToObj', () => { +describe('localesInfosInnerToObj', () => { it('should return an obj when parsing succeeds', () => { const objInner = '"foo": "bar"'; - expect(objInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar' }); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar' }); }); it('should return an obj, given a valid obj inner', () => { @@ -15,7 +15,7 @@ describe('objInnerToObj', () => { // eslint-disable-next-line @typescript-eslint/no-magic-numbers const objInner = getRawDataFromBracesDeclaration(JSON.stringify(initialObj), 0); expect(objInner).not.toBe(null); - expect(objInnerToObj(objInner as string)).toStrictEqual(initialObj); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual(initialObj); }); it('should return an obj, given a valid obj inner (nested)', () => { @@ -37,57 +37,57 @@ describe('objInnerToObj', () => { 0 ); expect(objInner).not.toBe(null); - expect(objInnerToObj(objInner as string)).toStrictEqual(initialObj); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual(initialObj); }); it('should return an obj, given a valid obj inner (literals)', () => { const objInner = "foo: 'bar', bar: 'foo'"; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(objInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar', bar: 'foo' }); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar', bar: 'foo' }); }); it('should return an obj, given a valid obj inner (string literals)', () => { const objInner = "'foo': 'bar', 'bar': 'foo'"; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(objInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar', bar: 'foo' }); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual({ foo: 'bar', bar: 'foo' }); }); it('should throw when Babel parsing fails', () => { const objInner = 'foo: bar'; - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); it('should throw, given an invalid obj inner (numeric literals)', () => { const objInner = "2172183: 'bar', 211838173: 'foo'"; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); it('should throw, given an invalid obj inner (int values)', () => { const objInner = 'foo: 45, bar: 12'; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); it('should throw when encountering unsupported value type', () => { const objInner = 'foo: /regex/'; - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); it('should throw, given stupid input (random number)', () => { const objInner = '88909988799'; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); it('should return empty object, given empty string input', () => { const objInner = ''; // eslint-disable-next-line @typescript-eslint/no-magic-numbers - expect(objInnerToObj(objInner as string)).toStrictEqual({}); + expect(localesInfosInnerToObj(objInner as string)).toStrictEqual({}); }); it('should throw when JSON parsing fails', () => { const objInner = 'invalid json'; - expect(() => objInnerToObj(objInner as string)).toThrow(); + expect(() => localesInfosInnerToObj(objInner as string)).toThrow(); }); }); diff --git a/packages/prebuilder/src/lib/etc.ts b/packages/prebuilder/src/lib/etc.ts index 06f0b7805..f8b53e98e 100644 --- a/packages/prebuilder/src/lib/etc.ts +++ b/packages/prebuilder/src/lib/etc.ts @@ -2,7 +2,7 @@ import { parse } from '@babel/parser'; import type { I18nJSONPart } from '../types/Metadatas'; -export function objInnerToObj(objInner: string): I18nJSONPart { +export function localesInfosInnerToObj(objInner: string): I18nJSONPart { let res = {}; try { const obj = JSON.parse('{\n' + objInner + '\n}'); @@ -20,7 +20,7 @@ export function objInnerToObj(objInner: string): I18nJSONPart { const objExpression = parsedObject.program.body[0].expression; const obj: I18nJSONPart = objExpression.properties.reduce((accumulator: I18nJSONPart, prop) => { if (prop.type === 'ObjectProperty') { - let key: string; + let key: string = ''; if (prop.key.type === 'Identifier') { key = prop.key.name; } else if (prop.key.type === 'StringLiteral') { @@ -29,7 +29,7 @@ export function objInnerToObj(objInner: string): I18nJSONPart { throw new Error(`Unsupported key type: ${prop.key.type}`); } - let value: string | null = null; + let value: string = ''; if (prop.value.type === 'StringLiteral') { value = prop.value.value; } else { diff --git a/packages/prebuilder/src/metadatas-builders/retrieveLocaleFileInfosMetadatas.ts b/packages/prebuilder/src/metadatas-builders/retrieveLocaleFileInfosMetadatas.ts index 9db851cf5..e60150b8c 100644 --- a/packages/prebuilder/src/metadatas-builders/retrieveLocaleFileInfosMetadatas.ts +++ b/packages/prebuilder/src/metadatas-builders/retrieveLocaleFileInfosMetadatas.ts @@ -4,8 +4,8 @@ import type { I18nJSONPart } from '../types/Metadatas'; import getRawDataFromBracesDeclaration from '../lib/getRawDataFromBracesDeclaration'; import { LOCALES_INFOS_OBJ_NEEDLE } from '../config'; import formatMessage from '../config/formatMessage'; +import { localesInfosInnerToObj } from '../lib/etc'; import BuilderError from '../errors/BuilderError'; -import { objInnerToObj } from '../lib/etc'; const ERROR_SUFFIX = formatMessage('interruptedThePrebuilder' satisfies VocabKey); @@ -24,7 +24,7 @@ async function buildLocaleFileMetadatasFromLocaleFile(localeFilePath: string): P if (!localeInfosInner) throw error; try { - const obj: I18nJSONPart = objInnerToObj(localeInfosInner); + const obj: I18nJSONPart = localesInfosInnerToObj(localeInfosInner); return obj; } catch { throw error; diff --git a/src/components/ui/breadcrumbs/Breadcrumbs.tsx b/src/components/ui/breadcrumbs/Breadcrumbs.tsx index bba7ac18a..a4d77c1e4 100644 --- a/src/components/ui/breadcrumbs/Breadcrumbs.tsx +++ b/src/components/ui/breadcrumbs/Breadcrumbs.tsx @@ -7,6 +7,7 @@ import type { getScopedI18n } from '@/i18n/server'; import buildAbsolutePathFromParts from '@rtm/shared-lib/portable/str/buildAbsolutePathFromParts'; import { useCurrentLocale, useScopedI18n } from '@/i18n/client'; +import { PAGES_TITLES } from '@/i18n/locales/schema'; import getPathParts from '@/lib/misc/getPathParts'; import { usePathname } from 'next/navigation'; import ROUTES_ROOTS from '##/config/routes'; @@ -53,6 +54,9 @@ function crumbsGenerator( } } + const missingLabel = !Object.keys(PAGES_TITLES).includes(pathParts[depth]); + if (missingLabel) throw new Error(`Missing pages-titles label: ${pathParts[depth]}`); + const label = scopedT(pathParts[depth] as PagesTitlesKey); return ; } diff --git a/src/i18n/locales/schema.ts b/src/i18n/locales/schema.ts index 1b58266bd..a9e41146f 100644 --- a/src/i18n/locales/schema.ts +++ b/src/i18n/locales/schema.ts @@ -7,6 +7,13 @@ import { blogCategories, pagesTitles } from '@rtm/generated'; const _: NotScanned = ''; +export const PAGES_TITLES = { + ...pagesTitles, + + homepage: _, + blog: _ +} as const; + export const SHARED_VOCAB_SCHEMA = { vocab: { breadcrumbs: _, @@ -16,12 +23,7 @@ export const SHARED_VOCAB_SCHEMA = { page: _ }, - 'pages-titles': { - ...pagesTitles, - - homepage: _, - blog: _ - } + 'pages-titles': PAGES_TITLES } as const satisfies TypedLeafsJSONData; export default {