Skip to content

Commit

Permalink
fix(#621) fix after rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
ben12 committed Oct 26, 2023
1 parent 33ab971 commit 7690e54
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 328 deletions.
1 change: 1 addition & 0 deletions libs/transloco-validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import commandLineArgs from 'command-line-args';

import validator from './lib/transloco-validator';


Expand Down
5 changes: 3 additions & 2 deletions libs/transloco-validator/src/lib/transloco-validator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import validator from './transloco-validator';
import fs from 'fs';

import validator from './transloco-validator';

jest.mock('fs');

describe('transloco-validator', () => {
Expand All @@ -23,7 +24,7 @@ describe('transloco-validator', () => {
jest.mocked(fs.readFileSync).mockImplementation(() => '{"test":{"erreur"}}');

const callValidator = () => validator('', ['mytest.json']);
expect(callValidator).toThrowError(new SyntaxError("Unexpected token } in JSON at position 17 (mytest.json)"));
expect(callValidator).toThrowError(SyntaxError);
})

it('should return success', () => {
Expand Down
110 changes: 55 additions & 55 deletions libs/transloco-validator/src/lib/transloco-validator.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
import fs from 'fs';

import findDuplicatedPropertyKeys from 'find-duplicated-property-keys';

export default function (interpolationForbiddenChars: string, translationFilePaths: string[]) {
translationFilePaths.forEach((path) => {
const translation = fs.readFileSync(path, 'utf-8');

// Verify that we can parse the JSON
let parsedTranslation;
try {
parsedTranslation = JSON.parse(translation);
} catch(error) {
throw new SyntaxError(
`${error.message} (${path})`
);
}

// Verify that we don't have any duplicate keys
const duplicatedKeys = findDuplicatedPropertyKeys(translation);
if (duplicatedKeys.length) {
throw new Error(
`Found duplicate keys: ${duplicatedKeys.map(dupl => dupl.toString())} (${path})`
);
}

const forbiddenKeys = findPropertyKeysContaining(parsedTranslation, interpolationForbiddenChars);
if (forbiddenKeys.length) {
throw new Error(
`Found forbidden characters [${interpolationForbiddenChars}] in keys: ${forbiddenKeys} (${path})`
);
}
});
}

function findPropertyKeysContaining(object: unknown, chars: string, parent = '<instance>') {
const found = [];
if (Array.isArray(object)) {
for(let i = 0; i < object.length; i++) {
const value = object[i];
found.push(...findPropertyKeysContaining(value, chars, `${parent}[${i}]`));
}
} else if (typeof object === 'object') {
for(const key in object) {
const value = object[key];
for (const char of chars) {
if (key.includes(char)) {
found.push(parent + '.' + key);
break;
}
}
found.push(...findPropertyKeysContaining(value, chars, `${parent}.${key}`));
}
}
return found;
import fs from 'fs';

import findDuplicatedPropertyKeys from 'find-duplicated-property-keys';

export default function (interpolationForbiddenChars: string, translationFilePaths: string[]) {
translationFilePaths.forEach((path) => {
const translation = fs.readFileSync(path, 'utf-8');

// Verify that we can parse the JSON
let parsedTranslation;
try {
parsedTranslation = JSON.parse(translation);
} catch(error) {
throw new SyntaxError(
`${error.message} (${path})`
);
}

// Verify that we don't have any duplicate keys
const duplicatedKeys = findDuplicatedPropertyKeys(translation);
if (duplicatedKeys.length) {
throw new Error(
`Found duplicate keys: ${duplicatedKeys.map(dupl => dupl.toString())} (${path})`
);
}

const forbiddenKeys = findPropertyKeysContaining(parsedTranslation, interpolationForbiddenChars);
if (forbiddenKeys.length) {
throw new Error(
`Found forbidden characters [${interpolationForbiddenChars}] in keys: ${forbiddenKeys} (${path})`
);
}
});
}

function findPropertyKeysContaining(object: unknown, chars: string, parent = '<instance>') {
const found = [];
if (Array.isArray(object)) {
for(let i = 0; i < object.length; i++) {
const value = object[i];
found.push(...findPropertyKeysContaining(value, chars, `${parent}[${i}]`));
}
} else if (typeof object === 'object') {
for(const key in object) {
const value = object[key];
for (const char of chars) {
if (key.includes(char)) {
found.push(parent + '.' + key);
break;
}
}
found.push(...findPropertyKeysContaining(value, chars, `${parent}.${key}`));
}
}
return found;
}
16 changes: 15 additions & 1 deletion libs/transloco/src/lib/tests/transpiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,26 @@ describe('TranslocoTranspiler', () => {

function testDefaultBehaviour(
parser: TranslocoTranspiler,
[start, end]: [string, string] = defaultConfig.interpolation
[start, end, forbiddenChars]: [string, string, string?] = defaultConfig.interpolation
) {
function wrapParam(param: string) {
return `${start} ${param} ${end}`;
}

it('should skip if forbidden chars are used', () => {
if (forbiddenChars?.length) {
for (const char of forbiddenChars) {
const parsed = parser.transpile(
`Hello ${wrapParam('value ' + char)}`,
{ value: 'World' },
{},
'key'
);
expect(parsed).toEqual(`Hello ${wrapParam('value ' + char)}`);
}
}
});

it('should translate simple string from params', () => {
const parsed = parser.transpile(
`Hello ${wrapParam('value')}`,
Expand Down
144 changes: 72 additions & 72 deletions libs/transloco/src/lib/transloco.config.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,72 @@
import { InjectionToken } from '@angular/core';

import { AvailableLangs } from './types';

export interface TranslocoConfig {
defaultLang: string;
reRenderOnLangChange: boolean;
prodMode: boolean;
fallbackLang?: string | string[];
failedRetries: number;
availableLangs: AvailableLangs;
flatten: {
aot: boolean;
};
missingHandler: {
logMissingKey: boolean;
useFallbackTranslation: boolean;
allowEmpty: boolean;
};
interpolation: [start: string, end: string, forbiddenChars?: string];
}

export const TRANSLOCO_CONFIG = new InjectionToken<TranslocoConfig>(
'TRANSLOCO_CONFIG',
{
providedIn: 'root',
factory: () => defaultConfig,
}
);

export const defaultConfig: TranslocoConfig = {
defaultLang: 'en',
reRenderOnLangChange: false,
prodMode: false,
failedRetries: 2,
fallbackLang: [],
availableLangs: [],
missingHandler: {
logMissingKey: true,
useFallbackTranslation: false,
allowEmpty: false,
},
flatten: {
aot: false,
},
interpolation: ['{{', '}}', '{}'],
};

type DeepPartial<T> = T extends Array<any>
? T
: T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;

export type PartialTranslocoConfig = DeepPartial<TranslocoConfig>;

export function translocoConfig(
config: PartialTranslocoConfig = {}
): TranslocoConfig {
return {
...defaultConfig,
...config,
missingHandler: {
...defaultConfig.missingHandler,
...config.missingHandler,
},
flatten: {
...defaultConfig.flatten,
...config.flatten,
},
};
}
import { InjectionToken } from '@angular/core';

import { AvailableLangs } from './types';

export interface TranslocoConfig {
defaultLang: string;
reRenderOnLangChange: boolean;
prodMode: boolean;
fallbackLang?: string | string[];
failedRetries: number;
availableLangs: AvailableLangs;
flatten: {
aot: boolean;
};
missingHandler: {
logMissingKey: boolean;
useFallbackTranslation: boolean;
allowEmpty: boolean;
};
interpolation: [start: string, end: string, forbiddenChars?: string];
}

export const TRANSLOCO_CONFIG = new InjectionToken<TranslocoConfig>(
'TRANSLOCO_CONFIG',
{
providedIn: 'root',
factory: () => defaultConfig,
}
);

export const defaultConfig: TranslocoConfig = {
defaultLang: 'en',
reRenderOnLangChange: false,
prodMode: false,
failedRetries: 2,
fallbackLang: [],
availableLangs: [],
missingHandler: {
logMissingKey: true,
useFallbackTranslation: false,
allowEmpty: false,
},
flatten: {
aot: false,
},
interpolation: ['{{', '}}', '{}'],
};

type DeepPartial<T> = T extends Array<any>

Check warning on line 49 in libs/transloco/src/lib/transloco.config.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
? T
: T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;

export type PartialTranslocoConfig = DeepPartial<TranslocoConfig>;

export function translocoConfig(
config: PartialTranslocoConfig = {}
): TranslocoConfig {
return {
...defaultConfig,
...config,
missingHandler: {
...defaultConfig.missingHandler,
...config.missingHandler,
},
flatten: {
...defaultConfig.flatten,
...config.flatten,
},
};
}
Loading

0 comments on commit 7690e54

Please sign in to comment.