Skip to content

Commit

Permalink
feat(localization): generate translations with readonly and as const
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-crouzet committed Dec 2, 2024
1 parent 0279d8b commit cea1241
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 19 deletions.
5 changes: 5 additions & 0 deletions packages/@o3r/localization/migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"version": "10.0.0-alpha.0",
"description": "Updates of @o3r/localization to v10.0.*",
"factory": "./schematics/ng-update/v10-0/index#updateV10_0"
},
"migration-v11_6": {
"version": "11.6.0-prerelease.0",
"description": "Updates of @o3r/localization to v11.6.*",
"factory": "./schematics/ng-update/v11-6/index#updateV116"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('Add Localization', () => {
export interface TestTranslation extends Translation {}
export const translations: TestTranslation = {}
export const translations: Readonly<TestTranslation> = {} as const;
`);
initialTree.create('.eslintrc.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', '__dot__eslintrc.mocks.json')));
});
Expand Down
75 changes: 58 additions & 17 deletions packages/@o3r/localization/schematics/add-localization-key/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,27 +195,68 @@ export function ngAddLocalizationKeyFn(options: NgAddLocalizationKeySchematicsSc
}
if (
ts.isVariableDeclaration(node)
&& node.type && ts.isTypeReferenceNode(node.type)
&& ts.isIdentifier(node.type.typeName)
&& node.type.typeName.escapedText.toString() === translationsVariableType
&& node.type
&& (
(
ts.isTypeReferenceNode(node.type)
&& ts.isIdentifier(node.type.typeName)
&& node.type.typeName.escapedText.toString() === translationsVariableType
)
|| (
ts.isTypeReferenceNode(node.type)
&& ts.isIdentifier(node.type.typeName)
&& node.type.typeName.escapedText.toString() === 'Readonly'
&& node.type.typeArguments?.[0]
&& ts.isTypeReferenceNode(node.type.typeArguments[0])
&& ts.isIdentifier(node.type.typeArguments[0].typeName)
&& node.type.typeArguments[0].typeName.escapedText.toString() === translationsVariableType
)
)
&& node.initializer
&& ts.isObjectLiteralExpression(node.initializer)
) {
return factory.updateVariableDeclaration(
node,
node.name,
node.exclamationToken,
node.type,
factory.updateObjectLiteralExpression(
node.initializer,
node.initializer.properties.concat(
factory.createPropertyAssignment(
factory.createIdentifier(properties.keyName),
factory.createStringLiteral(properties.keyValue, true)
if (ts.isObjectLiteralExpression(node.initializer)) {
return factory.updateVariableDeclaration(

Check warning on line 218 in packages/@o3r/localization/schematics/add-localization-key/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/localization/schematics/add-localization-key/index.ts#L218

Added line #L218 was not covered by tests
node,
node.name,
node.exclamationToken,
node.type,
factory.updateObjectLiteralExpression(
node.initializer,
node.initializer.properties.concat(
factory.createPropertyAssignment(
factory.createIdentifier(properties.keyName),
factory.createStringLiteral(properties.keyValue, true)
)
)
)
)
);
);
} else if (
ts.isAsExpression(node.initializer)
&& ts.isTypeReferenceNode(node.initializer.type)
&& ts.isIdentifier(node.initializer.type.typeName)
&& node.initializer.type.typeName.escapedText.toString() === 'const'
&& ts.isObjectLiteralExpression(node.initializer.expression)
) {
return factory.updateVariableDeclaration(
node,
node.name,
node.exclamationToken,
node.type,
factory.updateAsExpression(
node.initializer,
factory.updateObjectLiteralExpression(
node.initializer.expression,
node.initializer.expression.properties.concat(
factory.createPropertyAssignment(
factory.createIdentifier(properties.keyName),
factory.createStringLiteral(properties.keyValue, true)
)
)
),
node.initializer.type
)
);
}
}
return ts.visitEachChild(node, visit, ctx);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import type {Translation} from '@o3r/core';

export interface <%= componentTranslation %> extends Translation {}

export const translations: <%= componentTranslation %> = {};
export const translations: Readonly<<%= componentTranslation %>> = {} as const;
137 changes: 137 additions & 0 deletions packages/@o3r/localization/schematics/ng-update/v11-6/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import {
Tree,
} from '@angular-devkit/schematics';
import {
SchematicTestRunner,
type UnitTestTree,
} from '@angular-devkit/schematics/testing';

const migrationPath = path.join(__dirname, '..', '..', '..', 'migration.json');

const notMigrated = `import type {
Translation,
} from '@o3r/core';
export interface LocalizationPresTranslation extends Translation {
/**
* Key for the message in the speech bubble until a destination is selected
*/
welcome: string;
/**
* Key for the message in the speech bubble when a destination is selected, you can use \`{ cityName }\` to display the name of the city selected
*/
welcomeWithCityName: string;
/**
* Key for the question at the top of the form
*/
question: string;
/**
* Key for the label for the destination input
*/
destinationLabel: string;
/**
* Key for the label for the departure date input
*/
departureLabel: string;
/**
* Key for the placeholder for the destination input
*/
destinationPlaceholder: string;
/**
* Key for the city names' dictionary
*/
cityName: string;
}
export const translations: LocalizationPresTranslation = {
welcome: 'o3r-localization-pres.welcome',
welcomeWithCityName: 'o3r-localization-pres.welcomeWithCityName',
question: 'o3r-localization-pres.question',
destinationLabel: 'o3r-localization-pres.destinationLabel',
departureLabel: 'o3r-localization-pres.departureLabel',
cityName: 'o3r-localization-pres.cityName',
destinationPlaceholder: 'o3r-localization-pres.destinationPlaceholder'
};
`;

const migrated = `import type {
Translation,
} from '@o3r/core';
export interface LocalizationPresTranslation extends Translation {
/**
* Key for the message in the speech bubble until a destination is selected
*/
welcome: string;
/**
* Key for the message in the speech bubble when a destination is selected, you can use \`{ cityName }\` to display the name of the city selected
*/
welcomeWithCityName: string;
/**
* Key for the question at the top of the form
*/
question: string;
/**
* Key for the label for the destination input
*/
destinationLabel: string;
/**
* Key for the label for the departure date input
*/
departureLabel: string;
/**
* Key for the placeholder for the destination input
*/
destinationPlaceholder: string;
/**
* Key for the city names' dictionary
*/
cityName: string;
}
export const translations: Readonly<LocalizationPresTranslation> = {
welcome: 'o3r-localization-pres.welcome',
welcomeWithCityName: 'o3r-localization-pres.welcomeWithCityName',
question: 'o3r-localization-pres.question',
destinationLabel: 'o3r-localization-pres.destinationLabel',
departureLabel: 'o3r-localization-pres.departureLabel',
cityName: 'o3r-localization-pres.cityName',
destinationPlaceholder: 'o3r-localization-pres.destinationPlaceholder'
} as const;
`;

describe('Update', () => {
let initialTree: Tree;
let runner: SchematicTestRunner;
beforeEach(() => {
initialTree = Tree.empty();
initialTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'angular.mocks.json')));
initialTree.create('package.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'package.mocks.json')));
initialTree.create('.eslintrc.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', '__dot__eslintrc.mocks.json')));
runner = new SchematicTestRunner('schematics', migrationPath);
});

describe('Update v11.6', () => {
let tree: UnitTestTree;
const notMigratedPath = 'src/components/not-migrated.translation.ts';
const migratedPath = 'src/components/migrated.translation.ts';

beforeEach(async () => {
initialTree.create(notMigratedPath, notMigrated);
initialTree.create(migratedPath, migrated);
tree = await runner.runSchematic('migration-v11_6', {}, initialTree);
});

it('should migrate the not migrated file', () => {
const newText = tree.readText(notMigratedPath);
expect(newText).not.toEqual(notMigrated);
expect(newText).toEqual(migrated);
});

it('should not change the file already migrated', () => {
expect(tree.readText(migratedPath)).toEqual(migrated);
});
});
});
28 changes: 28 additions & 0 deletions packages/@o3r/localization/schematics/ng-update/v11-6/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type {
Rule,
Tree,
} from '@angular-devkit/schematics';
import {
createSchematicWithMetricsIfInstalled,
findFilesInTree,
} from '@o3r/schematics';

const regexp = /translations\s*:\s*(?:Readonly<)?([A-Z][\w]*)(?:>)?\s*=\s*({[^;]+})\s*(as\s*const)?\s*;/g;

function updateV116Fn(): Rule {
return (tree: Tree) => {
const files = findFilesInTree(tree.getDir(''), (filePath) => /translation\.ts$/.test(filePath));
files.forEach(({ content, path }) => {
const str = content.toString();
const newContent = str.replaceAll(regexp, 'translations: Readonly<$1> = $2 as const;');
if (newContent !== str) {
tree.overwrite(path, newContent);
}
});
};
}

/**
* Update of Otter configuration V11.6
*/
export const updateV116 = createSchematicWithMetricsIfInstalled(updateV116Fn);

0 comments on commit cea1241

Please sign in to comment.