Skip to content

Commit

Permalink
feat: 🎸 Support scanning arbitrary folders
Browse files Browse the repository at this point in the history
BREAKING CHANGE: 🧨 The source root won't be automatically prefixed to the `input`,
`output`and `translationsPath` anymore. for more inforamtion see the
BREAKING_CHANGES.md file.

✅ Closes: #160
  • Loading branch information
shaharkazaz committed Jul 26, 2024
1 parent fd9a93a commit 4979e83
Show file tree
Hide file tree
Showing 28 changed files with 250 additions and 169 deletions.
46 changes: 46 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
# Transloco Keys Manager V5

The source root is now only prefixed to the **default** config, which means you need to write the full path relative to
the project root.
If I had the following config:

```ts
import {TranslocoGlobalConfig} from "@jsverse/transloco-utils";

const config: TranslocoGlobalConfig = {
rootTranslationsPath: 'assets/i18n/',
langs: ['it', 'en'],
keysManager: {
input: ['app'],
output: 'assets/i18n/'
},
}

export default config;
```

When migrating to v5 I'll need to prefix the paths with the source root:
```ts
import {TranslocoGlobalConfig} from "@jsverse/transloco-utils";

const config: TranslocoGlobalConfig = {
// 👇
rootTranslationsPath: 'src/assets/i18n/',
langs: ['it', 'en'],
keysManager: {
input: [
// 👇
'src/app',
// 🥳 Scanning non buildable libs is now supported
'projects/ui-lib/src/lib'
],
// 👇
output: 'src/assets/i18n/'
},
}

export default config;
```

This change is necessary to [allow scanning arbitrary folders](https://github.com/jsverse/transloco-keys-manager/issues/160) and will open support for a more dynamic features.

# Transloco Keys Manager V4

The library is now ESM only in order to use the newer versions of the angular compiler.
Expand Down
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,8 @@ transloco-keys-manager extract -c src/my/path
```
- `project`*: The targeted project (default is `defaultProject`). The `sourceRoot` of this project will be extracted
from the `angular.json` file and will prefix the `input`, `output`, and `translationPath` properties.
from the `angular.json` file and will prefix **the default** `input`, `output`, and `translationPath` properties, So
when overriding these options make sure you provide the full path.
In addition, the transloco config file will be searched in the project's `sourceRoot` (unless the `config` option is passed):
```bash
Expand All @@ -359,8 +360,8 @@ transloco-keys-manager extract --project first-app
- `translationsPath`: The path for the root directory of the translation files (default is `${sourceRoot}/assets/i18n`)
```bash
transloco-keys-manager find --translations-path my/path
transloco-keys-manager find -p my/path
transloco-keys-manager find --translations-path src/assets/my/path
transloco-keys-manager find -p src/assets/my/path
```
- `input`: The source directory for all files using the translation keys: (default is `[${sourceRoot}/app']`)
Expand All @@ -371,7 +372,7 @@ transloco-keys-manager extract --input src/my/path,project/another/path
transloco-keys-manager extract -i src/my/path
```

**Note:** If a `project` is provided the default input value will be determined by the `projectType`, when given a library the default input value will be `['lib']`.
**Note:** If a `project` is provided the default input value will be determined by the `projectType`, when given a library the default input value will be `['${sourceRoot}/lib']`.

- `output`: The target directory for all generated translation files: (default is `${sourceRoot}/assets/i18n`)

Expand Down Expand Up @@ -468,11 +469,13 @@ transloco-keys-manager -h

### Transloco Config File

One more option to define the `config` object for this library is to create a `transloco.config.js` file in the project's root folder and add the configuration in it:
If you installed transloco via the schematics, a `transloco.config.ts` should have been created.
Otherwise, you can just create a `transloco.config.ts` in the project's root folder and add the configuration in it:

```ts
// transloco.config.js
module.exports = {
import {TranslocoGlobalConfig} from "@jsverse/transloco-utils";
const config: TranslocoGlobalConfig = {
rootTranslationsPath?: string;
langs?: string[];
keysManager: {
Expand All @@ -485,8 +488,10 @@ module.exports = {
replace?: boolean;
defaultValue?: string | undefined;
unflat?: boolean;
};
}
}
};
export default config;
```

### 🐞 Debugging
Expand Down
80 changes: 28 additions & 52 deletions __tests__/buildTranslationFiles/build-translation-utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Config } from '../../src/types';
import fs from 'fs-extra';
import nodePath from 'node:path';
import { defaultValue } from '../spec-utils';

/**
* With ESM modules, you need to mock the modules beforehand (with jest.unstable_mockModule) and import them ashynchronously afterwards.
* This thing is still in WIP at Jest, so keep an eye on it.
* @see https://jestjs.io/docs/ecmascript-modules#module-mocking-in-esm
*/
const { getCurrentTranslation } = await import(
'../../src/keys-builder/utils/get-current-translation'
);
import {
BuildConfigOptions,
defaultValue,
buildConfig as _buildConfig,
removeI18nFolder as _removeI18nFolder,
assertPartialTranslation as _assertPartialTranslation,
assertTranslation as _assertTranslation,
AssertTranslationParams,
} from '../spec-utils';

export const sourceRoot = '__tests__/buildTranslationFiles';

Expand Down Expand Up @@ -48,56 +45,35 @@ export type TranslationTestCase =
| 'config-options/multi-input'
| 'config-options/scope-mapping'
| 'config-options/remove-extra-keys'
| 'add-missing-keys'
| 'comments';

type WithTestCase<T> = T & { type: TranslationTestCase };

export function buildConfig(
type: TranslationTestCase,
config: Partial<Config> = {},
options: WithTestCase<Omit<BuildConfigOptions, 'sourceRoot'>>,
) {
return {
input: [nodePath.join(type, 'src' + '')],
output: nodePath.join(type, `i18n`),
translationsPath: nodePath.join(type, `i18n`),
langs: ['en', 'es', 'it'],
defaultValue,
...config,
} as Config;
}

interface assertTranslationParams extends Pick<Config, 'fileFormat'> {
type: TranslationTestCase;
expected: object;
path?: string;
root?: string;
return _buildConfig({
...options,
sourceRoot: nodePath.join(sourceRoot, options.type),
});
}

export function assertTranslation({
expected,
...rest
}: assertTranslationParams) {
expect(loadTranslationFile(rest)).toEqual(expected);
}
type AssertParams = WithTestCase<Omit<AssertTranslationParams, 'root'>>;

export function assertPartialTranslation({
expected,
...rest
}: assertTranslationParams) {
expect(loadTranslationFile(rest)).toMatchObject(expected);
export function assertTranslation({ type, ...params }: AssertParams) {
_assertTranslation({
...params,
root: nodePath.join(sourceRoot, type),
});
}

function loadTranslationFile({
type,
path,
fileFormat,
root = sourceRoot,
}: Omit<assertTranslationParams, 'expected'>) {
return getCurrentTranslation({
path: nodePath.join(root, type, 'i18n', `${path || ''}en.${fileFormat}`),
fileFormat,
export function assertPartialTranslation({ type, ...params }: AssertParams) {
_assertPartialTranslation({
...params,
root: nodePath.join(sourceRoot, type),
});
}

export function removeI18nFolder(type: TranslationTestCase, root = sourceRoot) {
fs.removeSync(nodePath.join(root, type, 'i18n'));
export function removeI18nFolder(type: TranslationTestCase) {
_removeI18nFolder(nodePath.join(sourceRoot, type));
}
2 changes: 1 addition & 1 deletion __tests__/buildTranslationFiles/comments/comments-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../src/keys-builder');
export function testCommentsExtraction(fileFormat: Config['fileFormat']) {
describe('comments', () => {
const type: TranslationTestCase = 'comments';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ export function testMultiInputsConfig(fileFormat: Config['fileFormat']) {
describe('Multi Inputs', () => {
const type: TranslationTestCase = 'config-options/multi-input';
const basePath = nodePath.join(type, 'src');
const config = buildConfig(type, {
fileFormat,
input: [`${basePath}/folder-1`, `${basePath}/folder-2`],
const config = buildConfig({
type,
config: {
fileFormat,
input: [1, 2].map((v) =>
nodePath.join(sourceRoot, basePath, `folder-${v}`),
),
},
});

beforeEach(() => removeI18nFolder(type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ export function testRemoveExtraKeysConfig(fileFormat: Config['fileFormat']) {
group3: { '2': 'missing' },
};

const baseConfig = buildConfig(type, { unflat: true, fileFormat });
const baseConfig = buildConfig({
type: type,
config: { unflat: true, fileFormat },
});
testRemoveExtraKeys({
baseConfig,
expected: {
Expand Down Expand Up @@ -114,7 +117,10 @@ export function testRemoveExtraKeysConfig(fileFormat: Config['fileFormat']) {
'group3.2': 'missing',
};

const baseConfig = buildConfig(type, { unflat: false, fileFormat });
const baseConfig = buildConfig({
type,
config: { unflat: false, fileFormat },
});
testRemoveExtraKeys({
baseConfig,
expected: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testScopeMappingConfig(fileFormat: Config['fileFormat']) {
describe('Scope mapping', () => {
const type: TranslationTestCase = 'config-options/scope-mapping';
const config = buildConfig(type, {
fileFormat,
scopePathMap: {
scope1: `./${sourceRoot}/${type}/i18n/scopes/mapped`,
const config = buildConfig({
type,
config: {
fileFormat,
scopePathMap: {
scope1: `./${sourceRoot}/${type}/i18n/scopes/mapped`,
},
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function testUnflatProblomaticKeysConfig(
) {
describe('Unflat problematic keys', () => {
const type: TranslationTestCase = 'config-options/unflat-problematic-keys';
const config = buildConfig(type, { unflat: true, fileFormat });
const config = buildConfig({ type, config: { unflat: true, fileFormat } });
const spy = jest.spyOn(messages, 'problematicKeysForUnflat');

beforeEach(() => removeI18nFolder(type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testUnflatSortExtraction(fileFormat: Config['fileFormat']) {
describe('unflat-sort', () => {
const type: TranslationTestCase = 'config-options/unflat-sort';
const config = buildConfig(type, { unflat: true, sort: true, fileFormat });
const config = buildConfig({
type,
config: { unflat: true, sort: true, fileFormat },
});

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testUnflatExtraction(fileFormat: Config['fileFormat']) {
describe('unflat', () => {
const type: TranslationTestCase = 'config-options/unflat';
const config = buildConfig(type, { unflat: true, fileFormat });
const config = buildConfig({ type, config: { unflat: true, fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testControlFlowExtraction(fileFormat: Config['fileFormat']) {
describe('Control flow', () => {
const type: TranslationTestCase = 'template-extraction/control-flow';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testDirectiveExtraction(fileFormat: Config['fileFormat']) {
describe('Directive', () => {
const type: TranslationTestCase = 'template-extraction/directive';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testNgContainerExtraction(fileFormat: Config['fileFormat']) {
describe('ng-container', () => {
const type: TranslationTestCase = 'template-extraction/ng-container';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testNgTemplateExtraction(fileFormat: Config['fileFormat']) {
describe('ng-template', () => {
const type: TranslationTestCase = 'template-extraction/ng-template';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testPipeExtraction(fileFormat: Config['fileFormat']) {
describe('Pipe', () => {
const type: TranslationTestCase = 'template-extraction/pipe';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testPrefixExtraction(fileFormat: Config['fileFormat']) {
describe('prefix', () => {
const type: TranslationTestCase = 'template-extraction/prefix';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testScopeExtraction(fileFormat: Config['fileFormat']) {
describe('scope', () => {
const type: TranslationTestCase = 'template-extraction/scope';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const { buildTranslationFiles } = await import('../../../../src/keys-builder');
export function testInlineTemplateExtraction(fileFormat: Config['fileFormat']) {
describe('inline template', () => {
const type: TranslationTestCase = 'ts-extraction/inline-template';
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function testMarkerExtraction(fileFormat: Config['fileFormat']) {
beforeEach(() => removeI18nFolder(type));

it('should work with marker', () => {
const config = buildConfig(type, { fileFormat });
const config = buildConfig({ type, config: { fileFormat } });

const expected = {
username4: defaultValue,
Expand Down
Loading

0 comments on commit 4979e83

Please sign in to comment.