From 71aade968fc3435522b1cde3481a76ca7e31baee Mon Sep 17 00:00:00 2001 From: Tomas Rimkus Date: Mon, 16 Sep 2024 22:25:25 +0200 Subject: [PATCH] fix(transloco): fixed file matching on Windows OS (#797) --- .../src/migrate/ngx-translate-migration.ts | 22 ++- .../src/tests/ngx-translate-migration.spec.ts | 134 ++++++++++-------- 2 files changed, 93 insertions(+), 63 deletions(-) diff --git a/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts b/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts index 2976cf02b..beb96c45b 100644 --- a/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts +++ b/libs/transloco-schematics/src/migrate/ngx-translate-migration.ts @@ -100,9 +100,9 @@ export function run(path) { const serviceCallRgx = ({ map, func }) => new RegExp( `(?:(?:\\s*|this\\.)${sanitizedName})(?:\\s*\\t*\\r*\\n*)*\\.(?:\\s*\\t*\\r*\\n*)*(${getTarget( - map + map, )})[\\r\\t\\n\\s]*${func ? '\\(' : '(?!\\()'}`, - 'g' + 'g', ); const getTarget = (t) => Object.keys(t).join('|'); return [ @@ -110,7 +110,10 @@ export function run(path) { { func: false, map: propsMap }, ].reduce((acc, curr) => { return acc.replace(serviceCallRgx(curr), (str) => - str.replace(new RegExp(getTarget(curr.map)), (func) => curr.map[func]) + str.replace( + new RegExp(getTarget(curr.map)), + (func) => curr.map[func], + ), ); }, match); }, @@ -158,6 +161,8 @@ export function run(path) { async function migrate(matchersArr, filesType) { console.log(`\nMigrating ${filesType} files 📜`); let spinner; + const isWindows = process.platform === 'win32'; + for (let i = 0; i < matchersArr.length; i++) { const { step, matchers } = matchersArr[i]; const msg = `Step ${i + 1}/${matchersArr.length}: Migrating ${step}`; @@ -165,7 +170,12 @@ export function run(path) { const noFilesFound = []; for (const matcher of matchers) { try { - await replaceInFile(matcher); + await replaceInFile({ + ...matcher, + glob: { + windowsPathsNoEscape: isWindows, + }, + }); } catch (e) { if (e.message.includes('No files match the pattern')) { noFilesFound.push(e.message); @@ -176,7 +186,7 @@ export function run(path) { } spinner.succeed(msg); noFilesFound.forEach((pattern) => - console.log('\x1b[33m%s\x1b[0m', `⚠️ ${pattern}`) + console.log('\x1b[33m%s\x1b[0m', `⚠️ ${pattern}`), ); } } @@ -187,7 +197,7 @@ export function run(path) { console.log('\n 🌵 Done! 🌵'); console.log('Welcome to a better translation experience 🌐'); console.log( - '\nFor more information about this script please visit 👉 https://jsverse.github.io/transloco/docs/migration/ngx\n' + '\nFor more information about this script please visit 👉 https://jsverse.github.io/transloco/docs/migration/ngx\n', ); }); } diff --git a/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts b/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts index 58d3ab2c7..e2ca8011c 100644 --- a/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts +++ b/libs/transloco-schematics/src/tests/ngx-translate-migration.spec.ts @@ -1,50 +1,57 @@ // noinspection AngularUndefinedBinding import * as nodePath from 'node:path'; -import {readFile} from 'node:fs/promises'; +import { readFile } from 'node:fs/promises'; -import {replaceInFile, ReplaceInFileConfig} from 'replace-in-file'; -import {glob} from 'glob'; +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 { + PIPE_IN_BINDING_REGEX, + PIPE_REGEX, + run, +} from '../migrate/ngx-translate-migration'; jest.mock('replace-in-file'); describe('ngx-translate migration', () => { - describe('Positive regex tests', () => { - describe('Pipe in binding', () => { test.each([ { testCase: ``, - match: [`]="'hello.world' | translate"`] + match: [`]="'hello.world' | translate"`], }, { testCase: ``, - match: [`]="'hello.world' | translate | anotherPipe"`] + match: [`]="'hello.world' | translate | anotherPipe"`], }, { testCase: ``, - match: [`]="'hello' | translate:params | anotherPipe"`] + match: [`]="'hello' | translate:params | anotherPipe"`], }, { testCase: ``, - match: [`]="titleMap[reportType] | translate"`] + match: [`]="titleMap[reportType] | translate"`], }, { testCase: ``, - match: [`]="('foo.bar' | translate) + ': ' + (value | number: '1.0-2')"`] + match: [ + `]="('foo.bar' | translate) + ': ' + (value | number: '1.0-2')"`, + ], }, { testCase: ``, - match: [`]="'Hello, ' + ('mom' | translate) | fooBar"`] + match: [`]="'Hello, ' + ('mom' | translate) | fooBar"`], }, { testCase: ` { + match: [ + `]="'Restore Options' | translate"`, + `]="'Processing archive...'|translate"`, + ], + }, + ])('Case: $testCase; Match: $match', ({ testCase, match }) => { const regex = new RegExp(PIPE_IN_BINDING_REGEX, 'gm'); const result = testCase.match(regex); @@ -56,33 +63,35 @@ describe('ngx-translate migration', () => { test.each([ { testCase: `{{ "hello.world" | translate }}`, - match: [`{{ "hello.world" | translate }}`] + match: [`{{ "hello.world" | translate }}`], }, { testCase: `{{ "hello.world" | translate | anotherPipe | oneMore }}`, - match: [`{{ "hello.world" | translate | anotherPipe | oneMore }}`] + match: [`{{ "hello.world" | translate | anotherPipe | oneMore }}`], }, { testCase: `{{ "hello" | translate: { name: 'John' } }}`, - match: [`{{ "hello" | translate: { name: 'John' } }}`] + match: [`{{ "hello" | translate: { name: 'John' } }}`], }, { testCase: `{{ titleMap[reportType] | translate }}`, - match: [`{{ 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') }}`] + match: [ + `{{ ('foo.bar' | translate) + ': ' + (value | number: '1.0-2') }}`, + ], }, { testCase: `{{ 'Hello, ' + ('mom' | translate) | fooBar }}`, - match: [`{{ '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}) => { + match: [`{{"1" | translate}}`, `{{"2" | translate}}`], + }, + ])('Case: $testCase; Match: $match', ({ testCase, match }) => { const regex = new RegExp(PIPE_REGEX, 'gm'); const result = testCase.match(regex); @@ -92,106 +101,117 @@ describe('ngx-translate migration', () => { }); describe('Negative regex tests', () => { - describe('Pipe in binding', () => { test.each([ { - testCase: `` + testCase: ``, }, { - testCase: `` + testCase: ``, }, { - testCase: `` + testCase: ``, }, { - testCase: `` + testCase: ``, }, { - testCase: `` + testCase: ``, }, { - testCase: `` + testCase: ``, }, { testCase: `` - } - ])('Case: $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 }}`, }, { - testCase: `{{ "hello.world" | transloco | anotherPipe | oneMore }}` + testCase: `{{ "hello.world" | transloco | anotherPipe | oneMore }}`, }, { - testCase: `{{ "hello" | transloco: { name: 'John' } }}` + testCase: `{{ "hello" | transloco: { name: 'John' } }}`, }, { - testCase: `{{ titleMap[reportType] | somePipe }}` + testCase: `{{ titleMap[reportType] | somePipe }}`, }, { - testCase: `{{ ('foo.bar' | transloco) + ': ' + (value | number: '1.0-2') }}` + testCase: `{{ ('foo.bar' | transloco) + ': ' + (value | number: '1.0-2') }}`, }, { - testCase: `{{ 'Hello, ' + ('mom' | transloco) | fooBar }}` - } - ])('Case: $testCase', ({testCase}) => { + 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 unknown as jest.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}); - + + 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'}); - + content = + replacements[filename] ?? + (await readFile(fullPath, { encoding: 'utf-8' })); + replacements[filename] = content.replace(regex, replacer); } - } + }, ); - const ngxTranslateTemplatePath = './src/tests/templates/pipes/ngx-translate'; + const ngxTranslateTemplatePath = + './src/tests/templates/pipes/ngx-translate'; await run(ngxTranslateTemplatePath); + expect(replaceInFile).toHaveBeenLastCalledWith( + expect.objectContaining({ + glob: { + windowsPathsNoEscape: isWindows, + }, + }), + ); + 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'}); + 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); } }); - }); });