UNPKG

@ngneat/transloco

Version:

The internationalization (i18n) library for Angular

183 lines (169 loc) 6.02 kB
const ora = require('ora'); const replace = require('replace-in-file'); const p = require('path'); // Example: `./src/ng2/**/*.html`; export function run(path) { console.log('\x1b[4m%s\x1b[0m', '\nStarting migration script'); const dir = p.resolve(process.cwd()); 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') ].map(regex => ({ files: `${path}.html`, from: regex, to: match => match.replace('translate', 'transloco') })); const moduleMultiImport = { files: `${path}.ts`, from: /import\s*{((([^,}]*,)+\s*(TranslateModule)\s*(,[^}]*)*)|(([^,{}]*,)*\s*(TranslateModule)\s*,\s*[a-zA-Z0-9]+(,[^}]*)*))\s*}\s*from\s*('|").?ngx-translate(\/[^'"]+)?('|");?/g, to: match => match .replace('TranslateModule', '') .replace(/,\s*,/, ',') .replace(/{\s*,/, '{') .replace(/,\s*}/, '}') .concat(`\nimport { TranslocoModule } from '@ngneat/transloco';`) }; const moduleSingleImport = { files: `${path}.ts`, from: /import\s*{\s*(TranslateModule),?\s*}\s*from\s*('|").?ngx-translate(\/[^'"]+)?('|");?/g, to: `import { TranslocoModule } from '@ngneat/transloco';` }; const modules = { files: `${path}.ts`, from: /(?<![a-zA-Z])TranslateModule(?![^]*from)(\.(forRoot|forChild)\(({[^}]*})*[^)]*\))?/g, to: 'TranslocoModule' }; const serviceMultiImport = { files: `${path}.ts`, from: /import\s*{((([^,}]*,)+\s*(TranslateService)\s*(,[^}]*)*)|(([^,{}]*,)*\s*(TranslateService)\s*,\s*[a-zA-Z0-9]+(,[^}]*)*))\s*}\s*from\s*('|").?ngx-translate(\/[^'"]+)?('|");?/g, to: match => match .replace('TranslateService', '') .replace(/,\s*,/, ',') .replace(/{\s*,/, '{') .replace(/,\s*}/, '}') .concat(`\nimport { TranslocoService } from '@ngneat/transloco';`) }; const [serviceSingleImport, pipeImport] = [ /import\s*{\s*(TranslateService),?\s*}\s*from\s*('|").?ngx-translate(\/[^'"]+)?('|");?/g, /import\s*{\s*(TranslatePipe),?\s*}\s*from\s*('|")[^'"]+('|");?/g ].map(regex => ({ ...noSpecFiles, from: regex, to: `import { TranslocoService } from '@ngneat/transloco';` })); const constructorInjection = { ...noSpecFiles, from: /(?:private|protected|public)\s+(.*?)\s*:\s*(?:TranslateService|TranslatePipe\s*(?:,|\)))/g, to: match => match.replace(/TranslateService|TranslatePipe/g, 'TranslocoService') }; const serviceUsage = { ...noSpecFiles, from: /(?=([^]+(?:private|protected|public)\s+([^,:()]+)\s*:\s*(?:TranslocoService\s*(?:,|\)))))\1[^]*/gm, to: (match, _, serviceName) => { const sanitizedName = serviceName .split('') .map(char => (['$', '^'].includes(char) ? `\\${char}` : char)) .join(''); const functionsMap = { instant: 'translate', transform: 'translate', get: 'selectTranslate', stream: 'selectTranslate', use: 'setActiveLang', set: 'setTranslation' }; const propsMap = { currentLang: 'getActiveLang()', onLangChange: 'langChanges$' }; const serviceCallRgx = ({ map, func }) => new RegExp( `(?:(?:\\s*|this\\.)${sanitizedName})(?:\\s*\\t*\\r*\\n*)*\\.(?:\\s*\\t*\\r*\\n*)*(${getTarget( map )})[\\r\\t\\n\\s]*${func ? '\\(' : '(?!\\()'}`, 'g' ); const getTarget = t => Object.keys(t).join('|'); return [{ func: true, map: functionsMap }, { func: false, map: propsMap }].reduce((acc, curr) => { return acc.replace(serviceCallRgx(curr), str => str.replace(new RegExp(getTarget(curr.map)), func => curr.map[func]) ); }, match); } }; const specs = { files: `${path}spec.ts`, from: /TranslateService|TranslatePipe/g, to: 'TranslocoService' }; const htmlReplacements = [ { matchers: [directive], step: 'directives' }, { matchers: [pipe, pipeInBinding], step: 'pipes' } ]; const tsReplacements = [ { matchers: [modules, moduleMultiImport, moduleSingleImport], step: 'modules' }, { matchers: [serviceMultiImport, serviceSingleImport, pipeImport], step: 'service imports' }, { matchers: [constructorInjection], step: 'constructor injections' }, { matchers: [serviceUsage], step: 'service usage' }, { matchers: [specs], step: 'specs' } ]; async function migrate(matchersArr, filesType) { console.log(`\nMigrating ${filesType} files 📜`); let spinner; for (let i = 0; i < matchersArr.length; i++) { let { step, matchers } = matchersArr[i]; const msg = `Step ${i + 1}/${matchersArr.length}: Migrating ${step}`; spinner = ora().start(msg); const noFilesFound = []; for (let matcher of matchers) { try { await replace(matcher); } catch (e) { if (e.message.includes('No files match the pattern')) { noFilesFound.push(e.message); } else { throw e; } } } spinner.succeed(msg); noFilesFound.forEach(pattern => console.log('\x1b[33m%s\x1b[0m', `⚠️ ${pattern}`)); } } return migrate(htmlReplacements, 'HTML') .then(() => migrate(tsReplacements, 'TS')) .then(() => { console.log('\n 🌵 Done! 🌵'); console.log('Welcome to a better translation experience 🌐'); console.log( '\nFor more information about this script please visit 👉 https://ngneat.github.io/transloco/docs/migration/ngx\n' ); }); }