Skip to content

Commit

Permalink
feat: 🎸 Add support for self closing tag and control flow (jsverse#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
kekel87 authored and nyffellare committed Jun 14, 2024
1 parent 678be20 commit f9a23fd
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 6 deletions.
24 changes: 20 additions & 4 deletions __tests__/buildTranslationFiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ type TranslationCategory =
| 'multi-input'
| 'scope-mapping'
| 'comments'
| 'remove-extra-keys';
| 'remove-extra-keys'
| 'self-closing'
| 'control-flow';

interface assertTranslationParams extends Pick<Config, 'fileFormat'> {
type: TranslationCategory;
Expand Down Expand Up @@ -128,7 +130,8 @@ describe.each(formats)('buildTranslationFiles in %s', (fileFormat) => {
'49.50.51.52': defaultValue,
...generateKeys({ start: 53, end: 62 }),
'63.64.65': defaultValue,
...generateKeys({ start: 66, end: 78 }),
...generateKeys({ start: 66, end: 79 }),
'{{count}} items': defaultValue,
};
[
'Restore Options',
Expand All @@ -152,7 +155,7 @@ describe.each(formats)('buildTranslationFiles in %s', (fileFormat) => {
beforeEach(() => removeI18nFolder(type));

it('should work with directive', () => {
const expected = generateKeys({ end: 23 });
const expected = generateKeys({ end: 24 });
['Processing archive...', 'Restore Options'].forEach(
(nonNumericKey) => {
expected[nonNumericKey] = defaultValue;
Expand Down Expand Up @@ -206,7 +209,7 @@ describe.each(formats)('buildTranslationFiles in %s', (fileFormat) => {
beforeEach(() => removeI18nFolder(type));

it('should work with ngTemplate', () => {
let expected = generateKeys({ end: 41 });
let expected = generateKeys({ end: 42 });
createTranslations(config);
assertTranslation({ type, expected, fileFormat });
});
Expand All @@ -230,6 +233,19 @@ describe.each(formats)('buildTranslationFiles in %s', (fileFormat) => {
});
});

describe('Control flow', () => {
const type: TranslationCategory = 'control-flow';
const config = gConfig(type);

beforeEach(() => removeI18nFolder(type));

it('should work with control flow', () => {
let expected = generateKeys({ end: 26 });
createTranslations(config);
assertTranslation({ type, expected, fileFormat });
});
});

describe('read', () => {
const type: TranslationCategory = 'read';
const config = gConfig(type);
Expand Down
79 changes: 79 additions & 0 deletions __tests__/control-flow/1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<span>{{ '1' | transloco }}</span>
<span transloco="2"></span>
<ng-container *transloco="let t">
<span>{{ t('3') }}</span>
</ng-container>
<ng-template transloco let-t>
<span>{{ t('4') }}</span>
</ng-template>


@if (a > b) {
<span>{{ '5' | transloco }}</span>
<span transloco="6">
</span>
<ng-container *transloco="let t">
<span>{{ t('7') }}</span>
</ng-container>
<ng-template transloco let-t>
<span>{{ t('8') }}</span>
</ng-template>
} @else if (b > a) {
<span>{{ '9' | transloco }}</span>
} @else {
<span [innerHtml]="'10' | transloco"></span>
}

@for (item of items; track item.id) {
{{ '11' | transloco }}
} @empty {
<span>{{ '12' | transloco }}</span>
}

@switch (condition) {
@case (caseA) {
<span>{{ '13' | transloco }}</span>
}
@default {
<span>{{ '14' | transloco }}</span>
}
}

@defer {
<span>{{ '15' | transloco }}</span>
} @error {
<span>{{ '16' | transloco }}</span>
} @placeholder {
<span>{{ '17' | transloco }}</span>
} @loading {
<span>{{ '18' | transloco }}</span>
}

<ng-container *transloco="let t">
@if (a > b) {
<span>{{ t('19') }}</span>
} @else if (b > a) {
<span [innerHtml]="t('20')"></span>
} @else {
@for (item of items; track item.id) {
<span>{{ t('21') }}</span>
} @empty {
@switch (condition) {
@case (caseA) {
<span>{{ t('22') }}</span>
}
@default {
@defer {
<span>{{ '23' | transloco }}</span>
} @error {
<span transloco="24"></span>
} @placeholder {
<span>{{ t('25') }}</span>
} @loading {
<span>{{ t('26') }}</span>
}
}
}
}
}
</ng-container>
1 change: 1 addition & 0 deletions __tests__/directive/2.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@
<div class="my-agenda-header d-flex" transloco="{{'20'}}"></div>
<div class="my-agenda-header d-flex" transloco="{{condition ? '21' : dontTake}}"></div>
<div class="my-agenda-header d-flex" transloco="{{condition ? dontTake : (condition2 ? '22' : '23')}}"></div>
<self-closing transloco="24" />
</div>
3 changes: 3 additions & 0 deletions __tests__/ngTemplate/5.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ <h1>ddsds</h1>
<my-comp [aa]="t('39') + 'a'">
<comp a="{{t('40') + 'a'}}">{{t('41') + 'a'}}</comp>
</my-comp>
<ng-template>
<self-closing [value]="t('42')" />
</ng-template>
</ng-container>
2 changes: 2 additions & 0 deletions __tests__/pipe/6.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@
{{ (('77' | transloco) + 'a').split(('78' | transloco) || ',') }}
</comp>
</my-comp>

<self-closing [value]="'79' | transloco" />
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nyffels/transloco-keys-manager",
"version": "1.1.0",
"description": "Fork of transloco-keys-manager with extra functions",
"version": "1.2.0",
"description": "Fork of jsverse/transloco-keys-manager with extra functions",
"main": "public_api.js",
"scripts": {
"release": "standard-version --infile ./CHANGELOG.md",
Expand Down
7 changes: 7 additions & 0 deletions src/keys-builder/template/directive.extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { resolveAliasAndKey } from '../utils/resolvers.utils';

import { TemplateExtractorConfig } from './types';
import {
isBlockNode,
isBoundAttribute,
isConditionalExpression,
isElement,
Expand All @@ -21,6 +22,7 @@ import {
isTemplate,
isTextAttribute,
parseTemplate,
resolveBlockChildNodes,
} from './utils';

export function directiveExtractor(config: TemplateExtractorConfig) {
Expand All @@ -30,6 +32,11 @@ export function directiveExtractor(config: TemplateExtractorConfig) {

function traverse(nodes: TmplAstNode[], config: ExtractorConfig) {
for (const node of nodes) {
if (isBlockNode(node)) {
traverse(resolveBlockChildNodes(node), config);
continue;
}

if (!isSupportedNode(node, [isTemplate, isElement])) {
continue;
}
Expand Down
7 changes: 7 additions & 0 deletions src/keys-builder/template/pipe.extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
isMethodCall,
isTemplate,
parseTemplate,
isBlockNode,
resolveBlockChildNodes,
} from './utils';

export function pipeExtractor(config: TemplateExtractorConfig) {
Expand All @@ -27,6 +29,11 @@ export function pipeExtractor(config: TemplateExtractorConfig) {

function traverse(nodes: TmplAstNode[], config: ExtractorConfig) {
for (const node of nodes) {
if (isBlockNode(node)) {
traverse(resolveBlockChildNodes(node), config);
continue;
}

let astTrees: AST[] = [];

if (isElement(node) || isTemplate(node)) {
Expand Down
7 changes: 7 additions & 0 deletions src/keys-builder/template/structural-directive.extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
isSupportedNode,
isTemplate,
parseTemplate,
isBlockNode,
resolveBlockChildNodes,
} from './utils';

interface ContainerMetaData {
Expand All @@ -47,6 +49,11 @@ export function traverse(
config: TemplateExtractorConfig
) {
for (const node of nodes) {
if (isBlockNode(node)) {
traverse(resolveBlockChildNodes(node), containers, config);
continue;
}

let methodUsages: ContainerMetaData[] = [];

if (isBoundText(node)) {
Expand Down
93 changes: 93 additions & 0 deletions src/keys-builder/template/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ import {
TmplAstElement,
TmplAstTemplate,
TmplAstTextAttribute,
TmplAstNode,
TmplAstDeferredBlock,
TmplAstDeferredBlockError,
TmplAstDeferredBlockLoading,
TmplAstDeferredBlockPlaceholder,
TmplAstForLoopBlock,
TmplAstForLoopBlockEmpty,
TmplAstIfBlockBranch,
TmplAstSwitchBlockCase,
TmplAstIfBlock,
TmplAstSwitchBlock,
} from '@angular/compiler';

import { readFile } from '../../utils/file.utils';
Expand Down Expand Up @@ -94,6 +105,88 @@ export function isSupportedNode<Predicates extends any[]>(
return predicates.some((predicate) => predicate(node));
}

type BlockNode =
| TmplAstDeferredBlockError
| TmplAstDeferredBlockLoading
| TmplAstDeferredBlockPlaceholder
| TmplAstForLoopBlockEmpty
| TmplAstIfBlockBranch
| TmplAstSwitchBlockCase
| TmplAstForLoopBlock
| TmplAstDeferredBlock
| TmplAstIfBlock
| TmplAstSwitchBlock;

export function isBlockWithChildren(
node: unknown
): node is { children: TmplAstNode[] } {
return (
node instanceof TmplAstDeferredBlockError ||
node instanceof TmplAstDeferredBlockLoading ||
node instanceof TmplAstDeferredBlockPlaceholder ||
node instanceof TmplAstForLoopBlockEmpty ||
node instanceof TmplAstIfBlockBranch ||
node instanceof TmplAstSwitchBlockCase
);
}

export function isTmplAstForLoopBlock(
node: unknown
): node is TmplAstForLoopBlock {
return node instanceof TmplAstForLoopBlock;
}

export function isTmplAstDeferredBlock(
node: unknown
): node is TmplAstDeferredBlock {
return node instanceof TmplAstDeferredBlock;
}

export function isTmplAstIfBlock(node: unknown): node is TmplAstIfBlock {
return node instanceof TmplAstIfBlock;
}

export function isTmplAstSwitchBlock(
node: unknown
): node is TmplAstSwitchBlock {
return node instanceof TmplAstSwitchBlock;
}

export function isBlockNode(node: TmplAstNode): node is BlockNode {
return (
isTmplAstIfBlock(node) ||
isTmplAstForLoopBlock(node) ||
isTmplAstDeferredBlock(node) ||
isTmplAstSwitchBlock(node) ||
isBlockWithChildren(node)
);
}

export function resolveBlockChildNodes(node: BlockNode): TmplAstNode[] {
if (isTmplAstIfBlock(node)) {
return node.branches;
}

if (isTmplAstForLoopBlock(node)) {
return node.empty ? [...node.children, node.empty] : node.children;
}

if (isTmplAstDeferredBlock(node)) {
return [
...node.children,
...([node.loading, node.error, node.placeholder].filter(
Boolean
) as TmplAstNode[]),
];
}

if (isTmplAstSwitchBlock(node)) {
return node.cases;
}

return node.children;
}

export function extractDefaultFromPipeExp(obj): string {
return Object.keys(obj).includes('exp')
? extractDefaultFromPipeExp(obj['exp'])
Expand Down

0 comments on commit f9a23fd

Please sign in to comment.