Skip to content

Commit

Permalink
fix: implemented breadcrumbs validation on build properly
Browse files Browse the repository at this point in the history
  • Loading branch information
gustaveWPM committed Feb 27, 2024
1 parent 905a4cd commit faa5664
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 23 deletions.
28 changes: 14 additions & 14 deletions packages/prebuilder/src/lib/__tests__/prebuild.etc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
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', () => {
const initialObj = { foo: 'bar', bar: 'foo' };
// 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)', () => {
Expand All @@ -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();
});
});
2 changes: 1 addition & 1 deletion packages/prebuilder/src/lib/etc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/components/ui/breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 <Crumb isLeaf={isLeaf} label={label} href={href} />;
}
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: _,
Expand All @@ -16,12 +23,7 @@ export const SHARED_VOCAB_SCHEMA = {
page: _
},

'pages-titles': {
...pagesTitles,

homepage: _,
blog: _
}
'pages-titles': PAGES_TITLES
} as const satisfies TypedLeafsJSONData<NotScanned>;

export default {
Expand Down

0 comments on commit faa5664

Please sign in to comment.