Skip to content

Commit

Permalink
feat: add separate tag parser
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 committed Apr 14, 2023
1 parent f8bd213 commit e72cf03
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 178 deletions.
30 changes: 0 additions & 30 deletions packages/core/src/TagParser/TagParserError.ts

This file was deleted.

42 changes: 0 additions & 42 deletions packages/core/src/TagParser/parser.test.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ export { TolgeeCore } from './TolgeeCore';
export * from './types';
export { getTranslateProps } from './TranslateParams';
export { FormatSimple } from './FormatSimple/FormatSimple';
export { TagParser } from './TagParser/TagParser';
2 changes: 1 addition & 1 deletion packages/format-icu/src/FormatIcu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { TolgeePlugin } from '@tolgee/core';
import { createFormatIcu } from './createFormatIcu';

export const FormatIcu = (): TolgeePlugin => (tolgee, tools) => {
tools.setFinalFormatter(createFormatIcu());
tools.addFormatter(createFormatIcu());
return tolgee;
};
6 changes: 1 addition & 5 deletions packages/format-icu/src/createFormatIcu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,10 @@ export const createFormatIcu = (): FinalFormatterMiddleware => {
language,
params,
}) => {
const ignoreTag = !Object.values(params || {}).find(
(p) => typeof p === 'function'
);

const locale = getLocale(language);

return new IntlMessageFormat(translation, locale, undefined, {
ignoreTag,
ignoreTag: true,
}).format(params);
};

Expand Down
10 changes: 8 additions & 2 deletions packages/react/src/__integration/T.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { act } from 'react-dom/test-utils';
import React from 'react';
import '@testing-library/jest-dom';
import { render, screen, waitFor } from '@testing-library/react';
import { TolgeeProvider, DevTools, TolgeeInstance, Tolgee } from '../index';
import {
TolgeeProvider,
DevTools,
TolgeeInstance,
Tolgee,
TagParser,
} from '../index';
import { FormatIcu } from '@tolgee/format-icu';
import { mockCoreFetch } from '@tolgee/testing/fetchMock';

Expand Down Expand Up @@ -69,7 +75,7 @@ describe('T component integration', () => {

beforeEach(async () => {
fetch.enableMocks();
tolgee = Tolgee().use(DevTools()).use(FormatIcu()).init({
tolgee = Tolgee().use(DevTools()).use(TagParser()).use(FormatIcu()).init({
apiUrl: API_URL,
apiKey: API_KEY,
language: 'cs',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FinalFormatterMiddleware, TolgeePlugin } from '../types';
import { FinalFormatterMiddleware, TolgeePlugin } from '@tolgee/core';
import { parser } from './parser';

function createTagParser(): FinalFormatterMiddleware {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import {
ERROR_UNCLOSED_TAG,
ERROR_UNEXPECTED_TAG,
ErrorCode,
TagParserError,
} from './TagParserError';
import { Token } from './tokenizer';

export function handleTag(
Expand All @@ -13,7 +7,7 @@ export function handleTag(
fullText: string
) {
let token: Token | undefined;
const content: any[] = [];
let content: any[] = [];

function addToContent(item: any) {
if (
Expand All @@ -26,6 +20,14 @@ export function handleTag(
}
}

function prependContent(item: any) {
if (typeof content[0] === 'string' && typeof item === 'string') {
content[0] = item + content[0];
} else {
content = [item, ...content];
}
}

function simplifyContent() {
if (content.length === 0) {
return undefined;
Expand All @@ -36,37 +38,46 @@ export function handleTag(
}
}

function parsingError(code: ErrorCode): never {
throw new TagParserError(code, token!.position, fullText);
function getParamFunc(name: string) {
const func = params?.[name];
if (typeof func === 'function') {
return func;
}
return undefined;
}

while ((token = stack.shift())) {
if (
token.type === 'tag' &&
token.closing &&
startToken !== undefined &&
token.data === startToken.data
token.name === startToken.name
) {
// matching tag to startToken - closing
const fun = params?.[startToken.data];
const fun = getParamFunc(startToken.name);
return fun(simplifyContent());
} else if (token.type === 'tag' && token.selfClosing) {
} else if (
token.type === 'tag' &&
token.selfClosing &&
getParamFunc(token.name)
) {
// self-closing - solve in-place
const fun = params?.[token.data];
const fun = getParamFunc(token.name);
addToContent(fun());
} else if (token.type === 'tag' && !token.closing) {
} else if (
token.type === 'tag' &&
!token.closing &&
getParamFunc(token.name)
) {
// opening tag - call recursively
addToContent(handleTag(token, stack, params, fullText));
} else if (token.type === 'text') {
// text
addToContent(token.data);
} else {
parsingError(ERROR_UNEXPECTED_TAG);
// treat everything else as text
addToContent(token.text);
}
}
if (startToken === undefined) {
// we are in the root, return content itself
return simplifyContent();
if (startToken !== undefined) {
prependContent(startToken.text);
}
parsingError(ERROR_UNCLOSED_TAG);
return simplifyContent();
}
131 changes: 131 additions & 0 deletions packages/web/src/TagParser/parser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { parser } from './parser';

function getText() {
return expect.getState().currentTestName.replace('parser ', '');
}

function textElement(name: string) {
return (content: string) => `<${name}>${content}</${name}>`;
}

function objectElement(name: string) {
return (content: string) => ({ name, content });
}

describe('parser', () => {
it('Text <a>element</a>!', () => {
expect(
parser(getText(), {
a: textElement('A'),
})
).toEqual('Text <A>element</A>!');
});

it('Object <a>element</a>!', () => {
expect(
parser(getText(), {
a: objectElement('A'),
})
).toEqual(['Object ', { name: 'A', content: 'element' }, '!']);
});

it('Ignored <a>element</a>!', () => {
expect(parser(getText())).toEqual('Ignored <a>element</a>!');
});

it('Text <br /> self closing', () => {
expect(
parser(getText(), {
br: textElement('BR'),
})
).toEqual('Text <BR>undefined</BR> self closing');
});

it('Object <br /> self closing', () => {
expect(
parser(getText(), {
br: objectElement('BR'),
})
).toEqual(['Object ', { name: 'BR', content: undefined }, ' self closing']);
});

it('Ignored <br /> self closing', () => {
expect(parser(getText())).toEqual('Ignored <br /> self closing');
});

it('Text <a>nested <b>element</b> and</a> more', () => {
expect(
parser(getText(), {
a: textElement('A'),
b: textElement('B'),
})
).toEqual('Text <A>nested <B>element</B> and</A> more');
});

it('Object <a>nested <b>element</b> and</a> more', () => {
expect(
parser(getText(), {
a: objectElement('A'),
b: objectElement('B'),
})
).toEqual([
'Object ',
{
name: 'A',
content: ['nested ', { name: 'B', content: 'element' }, ' and'],
},
' more',
]);
});

it('Ignored <a>nested <b>element</b> and</a> more', () => {
expect(parser(getText())).toEqual(
'Ignored <a>nested <b>element</b> and</a> more'
);
});

it('<a >test</a >', () => {
expect(parser(getText(), { a: textElement('A') })).toEqual('<A>test</A>');
});

it('<a>ignored when no matching parameter</a>', () => {
expect(parser(getText())).toEqual(
'<a>ignored when no matching parameter</a>'
);
});

it('Ignored when no function param <a> tag', () => {
expect(parser(getText())).toEqual('Ignored when no function param <a> tag');
});

it('Ignored when unclosed <a> tag', () => {
expect(parser(getText(), { a: textElement('A') })).toEqual(
'Ignored when unclosed <a> tag'
);
});

it('Text handles white at allowed places <a >test</a >', () => {
expect(parser(getText(), { a: textElement('A') })).toEqual(
'Text handles white at allowed places <A>test</A>'
);
});

it('Ignored with spaces <a >test</a >', () => {
expect(parser(getText())).toEqual('Ignored with spaces <a >test</a >');
});

it('Ignored partly when crossed <a>first <b>second</a> third</b>', () => {
expect(
parser(getText(), {
a: textElement('A'),
b: textElement('B'),
})
).toEqual('Ignored partly when crossed <a>first <B>second</a> third</B>');
});

it('Ignored with params <a href="/">test</a>', () => {
expect(parser(getText(), { a: textElement('A') })).toEqual(
'Ignored with params <a href="/">test</a>'
);
});
});
File renamed without changes.
Loading

0 comments on commit e72cf03

Please sign in to comment.