UNPKG

webpack-angular-translate

Version:

Webpack plugin that extracts the translation-ids with the default texts.

326 lines 14.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isCommentedWithSuppressError = void 0; var path = __importStar(require("path")); var ast_types_1 = require("ast-types"); var translation_1 = __importDefault(require("../translation")); var TRANSLATE_SERVICE_NAME = "$translate"; function createTranslateVisitor(loader, parserOptions) { if (parserOptions === void 0) { parserOptions = { ecmaVersion: "latest" }; } var context = null; var comments = []; var options = __assign(__assign({}, parserOptions), { locations: true, onComment: comments, // onToken: tokens, ranges: true }); /** * Handles a $translate(translateId, interpolateParams, interpolationId, defaultText) call. * @param path the path to the call expression */ function visitTranslate(path) { var call = path.node; var args = call.arguments; if (args.length < 1) { throwSuppressableError("A call to " + TRANSLATE_SERVICE_NAME + " requires at least one argument that is the translation id", path); } var translationIds = getTranslationIdFromTranslateCall(path); var defaultText = getDefaultTextFromTranslateCall(path); for (var _i = 0, translationIds_1 = translationIds; _i < translationIds_1.length; _i++) { var translationId = translationIds_1[_i]; var translation = createTranslation(translationId, defaultText, call); loader.registerTranslation(translation); } } function getTranslationIdFromTranslateCall(path) { var args = path.node.arguments; if (ast_types_1.namedTypes.Literal.check(args[0])) { return [args[0].value]; } if (ast_types_1.namedTypes.ArrayExpression.check(args[0])) { var arrayExpression = args[0]; return arrayExpression.elements.map(function (element) { if (ast_types_1.namedTypes.Literal.check(element)) { return element.value; } throwSuppressableError("The array with the translation ids should only contain literals", path); }); } throwSuppressableError("The translation id should either be a string literal or an array containing string literals", path); } function getDefaultTextFromTranslateCall(path) { var args = path.node.arguments; if (args.length > 3) { if (ast_types_1.namedTypes.Literal.check(args[3])) { return args[3].value; } throwSuppressableError("The default text should be a string literal", path); } return undefined; } /** * Handles a call to i18n.registerTranslation(translationId, defaultText?). * Evaluates the expression and registers a translation. The call expression itself is replaced with the id of the * translation id. * @param path of the call expression. */ function visitRegisterTranslation(path) { var call = path.node, args = call.arguments; if (args.length === 0 || !ast_types_1.namedTypes.Literal.check(args[0])) { throwError("Illegal argument for call to 'i18n.registerTranslation'. The call requires at least the 'translationId' argument that needs to be a literal", call); } var translationId = args[0].value; var defaultText; if (args.length > 1) { if (ast_types_1.namedTypes.Literal.check(args[1])) { defaultText = args[1].value; } else { throwError("Illegal argument for call to i18n.registerTranslation: the default text has to be a literal", call); } } var translation = createTranslation(translationId, defaultText, call); loader.registerTranslation(translation); path.replace(ast_types_1.builders.literal(translation.id)); context.reportChanged(); } /** * Handles a call to i18n.registerTranslations({ translationId: defaultText }). * @param path the path to the call expression */ function visitRegisterTranslations(path) { var call = path.node, args = call.arguments, translationsArgument = args.length === 0 ? null : args[0]; if (translationsArgument === null || !ast_types_1.namedTypes.ObjectExpression.check(translationsArgument)) { throwError("Illegal argument for call to i18n.registerTranslations: requires a single argument that is an object where the key is the translationId and the value is the default text", call); } var translations = (translationsArgument).properties.map(function (property) { var translationId; var defaultText; if (property.type === "SpreadElement" || property.type === "SpreadProperty" || property.type === "ObjectMethod") { throwError("Illegal argument for call to i18n.registerTranslations: The passed object contains a spread property, spread element, or method. This is not supported.", property); return; } if (ast_types_1.namedTypes.Identifier.check(property.key)) { translationId = property.key.name; } else if (ast_types_1.namedTypes.Literal.check(property.key)) { translationId = property.key.value; } else { throwError("Illegal argument for call to i18n.registerTranslations: The key needs to be a literal or an identifier.", call); } if (ast_types_1.namedTypes.Literal.check(property.value)) { defaultText = property.value.value; } else { throwError("Illegal argument for call to i18n.registerTranslations: The value for the key '" + translationId + "' needs to be a literal", call); } return createTranslation(translationId, defaultText, call); }); for (var _i = 0, translations_1 = translations; _i < translations_1.length; _i++) { var translation = translations_1[_i]; loader.registerTranslation(translation); } var ids = ast_types_1.builders.arrayExpression(translations.map(function (translation) { return ast_types_1.builders.literal(translation.id); })); path.replace(ids); context.reportChanged(); } function createTranslation(translationId, defaultText, node) { var idAsString = valueToString(translationId, ""); var defaultTextAsString = valueToString(defaultText, undefined); return new translation_1.default(idAsString, defaultTextAsString, { resource: path.relative(loader.context, loader.resourcePath), loc: node.loc.start }); } /** * Gets the function name from a call expression * @param call the call expression * @returns {string} the name of the function */ function getFunctionName(call) { var callee = call.callee; if (ast_types_1.namedTypes.Identifier.check(callee)) { return callee.name; } else if (ast_types_1.namedTypes.MemberExpression.check(callee)) { var property = callee.property; if (ast_types_1.namedTypes.Identifier.check(property)) { return property.name; } return "[expression]"; } else if (ast_types_1.namedTypes.FunctionExpression.check(callee)) { return "(function () { ... })"; } } /** * Gets the name of the callee of a function. * Returns the name of the object before the dot (.) in a function call, * e.g this for this.$translate or i18n for i18n.registerTranslation * @param call the call expression * @returns {string} the name of the callee or null if the name could not be determined */ function getCalleeName(call) { // this.method() or object.method() if (call.callee.type === "MemberExpression") { var member = call.callee; if (member.object.type === "Identifier") { return member.object.name; } else if (member.object.type === "ThisExpression") { return "this"; } else if (member.object.type == "MemberExpression") { var parent = member.object; if (parent.property.type === "Identifier") { return parent.property.name; } } } return null; } /** * Emits an error to webpack and throws an error to abort the processing of the node. * * @param message the message to emit * @param node the node for which a message is emitted */ function throwError(message, node) { var relativePath = path.relative(loader.context, loader.resourcePath); var start = node.loc.start, completeMessage = message + " (" + relativePath + ":" + start.line + ":" + start.column + ")"; loader.emitError(new Error(completeMessage)); throw context.abort(); } /** * Emits an error to webpack if no comment with suppress-dynamic-translation-error: true is found * in the scope of the passed in path * * @param message the message to emit * @param path the path of the node to which the error belongs */ function throwSuppressableError(message, path) { var call = path.node, calleeName = getCalleeName(call), functionName = getFunctionName(call), completeFunctionName = (calleeName ? calleeName + "." : "") + functionName, completeMessage = "Illegal argument for call to " + completeFunctionName + ": " + message + ". If you have registered the translation manually, you can use a /* suppress-dynamic-translation-error: true */ comment in the block of the function call to suppress this error."; if (!isCommentedWithSuppressErrors(path)) { throwError(completeMessage, call); } context.abort(); } /** * Tests if a {@code suppress-dynamic-translation-error: true } comment exists in the scope of the passed in path. * @param path the path to check * @returns {boolean} {@code true} if the current block contains such a comment, otherwise false */ function isCommentedWithSuppressErrors(path) { return isCommentedWithSuppressError(path, comments); } function valueToString(value, fallback) { if (value === null || typeof value === "undefined") { return fallback; } return "" + value; } var visitor = ast_types_1.PathVisitor.fromMethodsObject({ visitCallExpression: function (path) { context = this; var call = path.node, functionName = getFunctionName(call), calleeName = getCalleeName(call); try { if (functionName === TRANSLATE_SERVICE_NAME) { visitTranslate(path); } else if (functionName === "registerTranslation" && calleeName === "i18n") { visitRegisterTranslation(path); } else if (functionName === "registerTranslations" && calleeName === "i18n") { visitRegisterTranslations(path); } else if (functionName === "instant" && calleeName === TRANSLATE_SERVICE_NAME) { visitTranslate(path); } else { context.traverse(path); } } catch (e) { if (e instanceof context.AbortRequest) { e.cancel(); } else { throw e; } } return false; } }); return { get changedAst() { return visitor.wasChangeReported(); }, comments: comments, options: options, visit: function (ast) { return visitor.visit(ast); } }; } exports.default = createTranslateVisitor; function isCommentedWithSuppressError(path, comments) { var blockStartPath = path; while (blockStartPath.parentPath && !(ast_types_1.namedTypes.BlockStatement.check(blockStartPath.node) || ast_types_1.namedTypes.Program.check(blockStartPath.node))) { blockStartPath = blockStartPath.parentPath; } var blockStart = blockStartPath.node; var suppressCommentExpression = /suppress-dynamic-translation-error:\s*true/; for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) { var comment = comments_1[_i]; if (comment.loc.end.line > path.node.loc.start.line) { return false; } if (comment.loc.start.line >= blockStart.loc.start.line && suppressCommentExpression.test(comment.value)) { return true; } } return false; } exports.isCommentedWithSuppressError = isCommentedWithSuppressError; //# sourceMappingURL=translate-visitor.js.map