UNPKG

@o3r/localization

Version:

This module provides a runtime dynamic language/translation support and debug tools.

215 lines • 13.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ngAddLocalizationKey = void 0; exports.ngAddLocalizationKeyFn = ngAddLocalizationKeyFn; const node_path_1 = require("node:path"); const prompt_1 = require("@angular/cli/src/utilities/prompt"); const strings_1 = require("@angular-devkit/core/src/utils/strings"); const schematics_1 = require("@angular-devkit/schematics"); const schematics_2 = require("@o3r/schematics"); const ts = require("typescript"); class NoLocalizationArchitecture extends Error { constructor(componentPath) { super(`This component is not localized. If you want to localize this component you can run the following command:\nng g @o3r/localization:localization-to-component --path="${componentPath}"`); } } class KeyAlreadyExists extends Error { constructor(key, file) { super(`There is already a key named ${key} in ${file}.`); } } const getLocalizationInformation = (componentPath, tree) => { const componentSourceFile = ts.createSourceFile(componentPath, tree.readText(componentPath), ts.ScriptTarget.ES2015, true); const o3rClass = componentSourceFile.statements.find((statement) => ts.isClassDeclaration(statement) && (0, schematics_2.isO3rClassComponent)(statement)); let localizationJsonPath; let defaultTranslationVariableName; let translationsVariableName; let translationsVariableType; o3rClass.members.forEach((member) => { if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name) && member.type && ts.isTypeReferenceNode(member.type) && ts.isIdentifier(member.type.typeName)) { const decorators = [...(ts.getDecorators(member) || [])]; const localizationDecorator = decorators.find((decorator) => ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression) && decorator.expression.expression.escapedText.toString() === 'Localization' && ts.isStringLiteral(decorator.expression.arguments[0])); if (localizationDecorator) { localizationJsonPath = node_path_1.posix.join((0, node_path_1.dirname)(componentPath), localizationDecorator.expression.arguments[0].text); translationsVariableName = member.name.escapedText.toString(); translationsVariableType = member.type.typeName.escapedText.toString(); if (member.initializer && ts.isIdentifier(member.initializer)) { defaultTranslationVariableName = member.initializer.escapedText.toString(); } } } }); if (!localizationJsonPath || !tree.exists(localizationJsonPath)) { throw new NoLocalizationArchitecture(componentPath); } if (!defaultTranslationVariableName) { const constructor = o3rClass.members.find((member) => ts.isConstructorDeclaration(member)); if (constructor?.body) { constructor.body.statements.forEach((statement) => { if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && ts.isPropertyAccessExpression(statement.expression.left) && statement.expression.left.name.escapedText.toString() === translationsVariableName && ts.isIdentifier(statement.expression.right)) { defaultTranslationVariableName = statement.expression.right.escapedText.toString(); } }); } } if (!defaultTranslationVariableName) { throw new schematics_2.O3rCliError(`Unable to find initialization of ${translationsVariableName} in ${componentPath}.`); } const importDeclaration = componentSourceFile.statements.find((statement) => ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) && !!statement.importClause && !!statement.importClause.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some((element) => ts.isIdentifier(element.name) && element.name.escapedText.toString() === defaultTranslationVariableName)); if (!importDeclaration) { throw new schematics_2.O3rCliError(`Unable to find import declaration of ${defaultTranslationVariableName} in ${componentPath}`); } const translationsPath = node_path_1.posix.join((0, node_path_1.dirname)(componentPath), `${importDeclaration.moduleSpecifier.text}.ts`); return { localizationJsonPath, translationsPath, translationsVariableType }; }; /** * Add localization key to an existing component * @param options */ function ngAddLocalizationKeyFn(options) { return async (tree, context) => { try { const { selector, templateRelativePath } = (0, schematics_2.getO3rComponentInfoOrThrowIfNotFound)(tree, options.path); const { localizationJsonPath, translationsPath, translationsVariableType } = getLocalizationInformation(options.path, tree); const keyName = options.key.charAt(0).toLowerCase() + (0, strings_1.classify)(options.key).slice(1); const properties = { ...options, keyName, keyValue: `${selector}.${options.keyToKebabCase ? (0, strings_1.dasherize)(options.key) : keyName}`, defaultValue: options.value }; const localizationJson = tree.readJson(localizationJsonPath) || {}; if (localizationJson[properties.keyValue]) { throw new KeyAlreadyExists(properties.keyValue, localizationJsonPath); } const translationFileContent = tree.readText(translationsPath); if (new RegExp(`${properties.keyName}:`).test(translationFileContent)) { throw new KeyAlreadyExists(properties.keyName, translationsPath); } const updateLocalizationFileRule = () => { localizationJson[properties.keyValue] = { ...(properties.dictionary ? { dictionary: true } : { defaultValue: properties.defaultValue }), description: properties.description }; tree.overwrite(localizationJsonPath, JSON.stringify(localizationJson, null, 2)); return tree; }; const updateTranslationFileRule = () => { const translationSourceFile = ts.createSourceFile(translationsPath, tree.readText(translationsPath), ts.ScriptTarget.ES2020, true); const result = ts.transform(translationSourceFile, [(ctx) => (rootNode) => { const { factory } = ctx; const visit = (node) => { if (ts.isInterfaceDeclaration(node) && node.name.escapedText.toString() === translationsVariableType) { return factory.updateInterfaceDeclaration(node, ts.getModifiers(node), node.name, node.typeParameters, node.heritageClauses, node.members.concat(factory.createPropertySignature(undefined, factory.createIdentifier(properties.keyName), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)))); } if (ts.isVariableDeclaration(node) && 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) { if (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))))); } 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); }; return ts.visitNode(rootNode, visit); }]); const printer = ts.createPrinter({ removeComments: false, newLine: ts.NewLineKind.LineFeed }); tree.overwrite(translationsPath, printer.printFile(result.transformed[0])); return tree; }; const updateTemplateFile = () => { const templatePath = templateRelativePath && node_path_1.posix.join((0, node_path_1.dirname)(options.path), templateRelativePath); if (templatePath) { tree.overwrite(templatePath, tree.readText(templatePath).replaceAll(options.value, `{{ translations.${properties.keyName} | o3rTranslate }}`)); } }; return (0, schematics_1.chain)([ updateLocalizationFileRule, updateTranslationFileRule, options.value && options.updateTemplate ? updateTemplateFile : (0, schematics_1.noop)(), options.skipLinter ? (0, schematics_1.noop)() : (0, schematics_2.applyEsLintFix)() ]); } catch (e) { if (e instanceof schematics_2.NoOtterComponent && context.interactive) { const shouldConvertComponent = await (0, schematics_2.askConfirmationToConvertComponent)(); if (shouldConvertComponent) { return (0, schematics_1.chain)([ (0, schematics_1.externalSchematic)('@o3r/core', 'convert-component', { path: options.path }), ngAddLocalizationKeyFn(options) ]); } } else if (e instanceof NoLocalizationArchitecture && context.interactive) { const shouldAddLocalization = await (0, prompt_1.askConfirmation)('This component is not localized. Would you like to add the localization architecture?', true); if (shouldAddLocalization) { return (0, schematics_1.chain)([ (0, schematics_1.schematic)('localization-to-component', { path: options.path }), ngAddLocalizationKeyFn(options) ]); } } else if (e instanceof KeyAlreadyExists && context.interactive) { const newKey = await (0, schematics_2.askUserInput)(`${e.message}\nPlease choose another key name:`); return ngAddLocalizationKeyFn({ ...options, key: newKey }); } throw e; } }; } /** * Add localization key to an existing component * @param options */ exports.ngAddLocalizationKey = (0, schematics_2.createOtterSchematic)(ngAddLocalizationKeyFn); //# sourceMappingURL=index.js.map