diff --git a/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts b/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts index 9113c8985..6f711b335 100644 --- a/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts +++ b/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts @@ -3,6 +3,10 @@ import * as p from 'node:path'; import * as ora from 'ora'; import { replaceInFile } from 'replace-in-file'; +const PIPE_CONTENT_REGEX = `\\s*([^}\\r\\n]*?\\|)\\s*(translate)[^\\r\\n]*?`; +export const PIPE_REGEX = `{{${PIPE_CONTENT_REGEX}}}`; +export const PIPE_IN_BINDING_REGEX = `\\]=('|")${PIPE_CONTENT_REGEX}\\1`; + // Example: `./src/ng2/**/*.html`; export function run(path) { console.log('\x1b[4m%s\x1b[0m', '\nStarting migration script'); @@ -11,15 +15,14 @@ export function run(path) { path = p.join(dir, path, '/**/*'); const noSpecFiles = { ignore: `${path}spec.ts`, files: `${path}.ts` }; - const pipeContent = `\\s*([^}\\r\\n]*?\\|)\\s*(translate)\\s*(?::\\s*{[^}\\r\\n]+})?\\s*(\\s*\\|[\\s\\r\\t\\n]*\\w*)*\\s*`; const [directive, pipe, pipeInBinding] = [ /(translate|\[translate(?:Params)?\])=("|')[^"']*\2/gm, - new RegExp(`{{${pipeContent}}}`, 'gm'), - new RegExp(`\\]=('|")${pipeContent}\\1`, 'gm'), + new RegExp(PIPE_REGEX, 'gm'), + new RegExp(PIPE_IN_BINDING_REGEX, 'gm'), ].map((regex) => ({ files: `${path}.html`, from: regex, - to: (match) => match.replace('translate', 'transloco'), + to: (match) => match.replace(/translate/g, 'transloco'), })); const moduleMultiImport = { diff --git a/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts b/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts new file mode 100644 index 000000000..ce62d2c11 --- /dev/null +++ b/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts @@ -0,0 +1,199 @@ +// noinspection AngularUndefinedBinding + +import * as nodePath from 'node:path'; +import {readFile} from 'node:fs/promises'; + +import {replaceInFile, ReplaceInFileConfig} from 'replace-in-file'; +import {glob} from 'glob'; + +import {PIPE_IN_BINDING_REGEX, PIPE_REGEX, run} from '../migrate/ngx-translate-migration'; + +import Mock = jest.Mock; + +jest.mock('replace-in-file'); + +describe('ngx-translate migration', () => { + + describe('Positive regex tests', () => { + + describe('Pipe in binding', () => { + test.each([ + { + testCase: ``, + match: [`]="'hello.world' | translate"`] + }, + { + testCase: ``, + match: [`]="'hello.world' | translate | anotherPipe"`] + }, + { + testCase: ``, + match: [`]="'hello' | translate:params | anotherPipe"`] + }, + { + testCase: ``, + match: [`]="titleMap[reportType] | translate"`] + }, + { + testCase: ``, + match: [`]="('foo.bar' | translate) + ': ' + (value | number: '1.0-2')"`] + }, + { + testCase: ``, + match: [`]="'Hello, ' + ('mom' | translate) | fooBar"`] + }, + { + testCase: ` { + const regex = new RegExp(PIPE_IN_BINDING_REGEX, 'gm'); + const result = testCase.match(regex); + + expect(result).toMatchObject(match); + }); + }); + + describe('Pipe', () => { + test.each([ + { + testCase: `{{ "hello.world" | translate }}`, + match: [`{{ "hello.world" | translate }}`] + }, + { + testCase: `{{ "hello.world" | translate | anotherPipe | oneMore }}`, + match: [`{{ "hello.world" | translate | anotherPipe | oneMore }}`] + }, + { + testCase: `{{ "hello" | translate: { name: 'John' } }}`, + match: [`{{ "hello" | translate: { name: 'John' } }}`] + }, + { + testCase: `{{ titleMap[reportType] | translate }}`, + match: [`{{ titleMap[reportType] | translate }}`] + }, + { + testCase: `{{ ('foo.bar' | translate) + ': ' + (value | number: '1.0-2') }}`, + match: [`{{ ('foo.bar' | translate) + ': ' + (value | number: '1.0-2') }}`] + }, + { + testCase: `{{ 'Hello, ' + ('mom' | translate) | fooBar }}`, + match: [`{{ 'Hello, ' + ('mom' | translate) | fooBar }}`] + }, + { + testCase: `{{"1" | translate}} {{errorCounter}} {{"2" | translate}}`, + match: [`{{"1" | translate}}`, `{{"2" | translate}}`] + } + ])('Case: $testCase; Match: $match', ({testCase, match}) => { + const regex = new RegExp(PIPE_REGEX, 'gm'); + const result = testCase.match(regex); + + expect(result).toMatchObject(match); + }); + }); + }); + + describe('Negative regex tests', () => { + + describe('Pipe in binding', () => { + test.each([ + { + testCase: `` + }, + { + testCase: `` + }, + { + testCase: `` + }, + { + testCase: `` + }, + { + testCase: `` + }, + { + testCase: `` + }, + { + testCase: `` + } + ])('Case: $testCase', ({testCase}) => { + const regex = new RegExp(PIPE_IN_BINDING_REGEX, 'gm'); + const result = testCase.match(regex); + + expect(result).toBeNull(); + }); + }); + + describe('Pipe', () => { + test.each([ + { + testCase: `{{ "hello.world" | transloco }}` + }, + { + testCase: `{{ "hello.world" | transloco | anotherPipe | oneMore }}` + }, + { + testCase: `{{ "hello" | transloco: { name: 'John' } }}` + }, + { + testCase: `{{ titleMap[reportType] | somePipe }}` + }, + { + testCase: `{{ ('foo.bar' | transloco) + ': ' + (value | number: '1.0-2') }}` + }, + { + testCase: `{{ 'Hello, ' + ('mom' | transloco) | fooBar }}` + } + ])('Case: $testCase', ({testCase}) => { + const regex = new RegExp(PIPE_REGEX, 'gm'); + const result = testCase.match(regex); + + expect(result).toBeNull(); + }); + }); + + }); + + describe('HTML template', () => { + + it('should replace html template content', async () => { + const replacements: Record = {}, + isWindows = process.platform === 'win32'; + + (replaceInFile as Mock).mockImplementation( + async (config: ReplaceInFileConfig): Promise => { + const path = config.files as string, + regex = config.from as RegExp, + replacer = config.to as (match: string) => string; + + const files = await glob(path, {windowsPathsNoEscape: isWindows}); + + for (const fullPath of files) { + const filename = nodePath.parse(fullPath).base, + content = replacements[filename] ?? await readFile(fullPath, {encoding: 'utf-8'}); + + replacements[filename] = content.replace(regex, replacer); + } + } + ); + + const ngxTranslateTemplatePath = './src/tests/templates/pipes/ngx-translate'; + + await run(ngxTranslateTemplatePath); + + const filenames = Object.keys(replacements); + + for(const filename of filenames) { + const resultPath = nodePath.join(__dirname, './templates/pipes/transloco', filename), + resultContent = await readFile(resultPath, {encoding: 'utf-8'}); + + expect(replacements[filename]).toBe(resultContent); + } + }); + + }); +}); diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/1.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/1.html new file mode 100644 index 000000000..d0506e2e7 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/1.html @@ -0,0 +1,32 @@ +
+
+ +
+ +
+ + + + + + + + +
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/2.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/2.html new file mode 100644 index 000000000..569d95a4c --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/2.html @@ -0,0 +1,120 @@ +
+ + + +
+
+ +
+ +
+ + +
+ +
+ + + +
+
+
+
+ {{"8" | translate}} +
+
+ + + +
{{"9" | translate}}
+ + + {{"10" | translate}} + + + {{"11" | translate}} + + + +
+
+ +
+
+ + +
+
+ {{"12" | translate}} +
+
+ +
+ +
+ + +
{{_dateRangeLabelText}} +
+ + +
+ +
+ {{"13" | translate}} + +
+ +
+ {{"14" | translate}} +
+ + {{"15" | translate}} + + + +
+ +
+
+ + +
{{"16" | translate}}
+
+
+ +
+ {{"17" | translate}} {{"18" | translate}} +
+ +
{{"19" | translate}}
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/3.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/3.html new file mode 100644 index 000000000..837a41061 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/3.html @@ -0,0 +1,38 @@ +
+ + + + + + + + + + + + + +
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/4.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/4.html new file mode 100644 index 000000000..fb56d1d0e --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/4.html @@ -0,0 +1,28 @@ +
+
+ +
+ + +
+ + +
+
+ + {{ '26' | translate}} +
+
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/5.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/5.html new file mode 100644 index 000000000..b01cb3d75 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/5.html @@ -0,0 +1,85 @@ +
+
+
{{"28" | translate}}
+
+
+
+ +
+
{{"29" | translate: {id: 1}}
+ + +
{{"30" | translate: { name: 'Avi' } }} +
+
+ + + + + +
+
{{"31" | translate}}
+ + {{ "32" | translate }} + +
+
+
+ + +
+ + + {{'37' | translate}} + {{ (condition ? "38" : '39') | translate: { name: "ccc"} }} + + + +
+ {{ (condition ? '40' : + + '41') | translate: { name: "ccc"} }} +
+ +
+ {{ (condition ? '42' : + + '43') | translate: { name: "ccc"} }} +
+ +
+
+ + +
+ {{ '48' | translate }} + {{ '49.50.51.52' | translate }} +
+ {{ 'not' + 4 | translate }} + + {{ 'numer' ++ 4 | translate }} + +
+ diff --git a/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/6.html b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/6.html new file mode 100644 index 000000000..ebbde9ada --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/ngx-translate/6.html @@ -0,0 +1,51 @@ +
+
+ + + +
+ {{ '59' | translate | another }} + {{ '60' | another | translate | other }} + {{ '61' | another | other | translate }} +
+ {{ var + 'not' | translate }} + +
+ + + +
{{ '63.64.65' | another | other | translate }}
+
+ + + + + + +{{ '66' | translate }} +{{ condition ? '67' | translate : 'dont take' }} + +{{ var | another : {foo: '68' | translate} | other : ('69' | translate) }} +{{ var | another : ('70' | translate) | lowercase }} + + + + {{ (('77' | translate) + 'a').split(('78' | translate) || ',') }} + + diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/1.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/1.html new file mode 100644 index 000000000..51b5c4caa --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/1.html @@ -0,0 +1,32 @@ +
+
+ +
+ +
+ + + + + + + + +
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/2.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/2.html new file mode 100644 index 000000000..442629759 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/2.html @@ -0,0 +1,120 @@ +
+ + + +
+
+ +
+ +
+ + +
+ +
+ + + +
+
+
+
+ {{"8" | transloco}} +
+
+ + + +
{{"9" | transloco}}
+ + + {{"10" | transloco}} + + + {{"11" | transloco}} + + + +
+
+ +
+
+ + +
+
+ {{"12" | transloco}} +
+
+ +
+ +
+ + +
{{_dateRangeLabelText}} +
+ + +
+ +
+ {{"13" | transloco}} + +
+ +
+ {{"14" | transloco}} +
+ + {{"15" | transloco}} + + + +
+ +
+
+ + +
{{"16" | transloco}}
+
+
+ +
+ {{"17" | transloco}} {{"18" | transloco}} +
+ +
{{"19" | transloco}}
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/3.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/3.html new file mode 100644 index 000000000..44b3fc661 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/3.html @@ -0,0 +1,38 @@ +
+ + + + + + + + + + + + + +
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/4.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/4.html new file mode 100644 index 000000000..609a1ba6a --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/4.html @@ -0,0 +1,28 @@ +
+
+ +
+ + +
+ + +
+
+ + {{ '26' | transloco}} +
+
+
diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/5.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/5.html new file mode 100644 index 000000000..52fade9e3 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/5.html @@ -0,0 +1,85 @@ +
+
+
{{"28" | transloco}}
+
+
+
+ +
+
{{"29" | transloco: {id: 1}}
+ + +
{{"30" | transloco: { name: 'Avi' } }} +
+
+ + + + + +
+
{{"31" | transloco}}
+ + {{ "32" | transloco }} + +
+
+
+ + +
+ + + {{'37' | transloco}} + {{ (condition ? "38" : '39') | transloco: { name: "ccc"} }} + + + +
+ {{ (condition ? '40' : + + '41') | translate: { name: "ccc"} }} +
+ +
+ {{ (condition ? '42' : + + '43') | translate: { name: "ccc"} }} +
+ +
+
+ + +
+ {{ '48' | transloco }} + {{ '49.50.51.52' | transloco }} +
+ {{ 'not' + 4 | transloco }} + + {{ 'numer' ++ 4 | transloco }} + +
+ diff --git a/libs/transloco-schematics/src/tests/templates/pipes/transloco/6.html b/libs/transloco-schematics/src/tests/templates/pipes/transloco/6.html new file mode 100644 index 000000000..7ab13b5e5 --- /dev/null +++ b/libs/transloco-schematics/src/tests/templates/pipes/transloco/6.html @@ -0,0 +1,51 @@ +
+
+ + + +
+ {{ '59' | transloco | another }} + {{ '60' | another | transloco | other }} + {{ '61' | another | other | transloco }} +
+ {{ var + 'not' | transloco }} + +
+ + + +
{{ '63.64.65' | another | other | transloco }}
+ + + + + + + +{{ '66' | transloco }} +{{ condition ? '67' | transloco : 'dont take' }} + +{{ var | another : {foo: '68' | transloco} | other : ('69' | transloco) }} +{{ var | another : ('70' | transloco) | lowercase }} + + + + {{ (('77' | transloco) + 'a').split(('78' | transloco) || ',') }} + +