UNPKG

next-translate-scanner

Version:

Scan next-translate code for translations and update json files.

162 lines (161 loc) 7.25 kB
"use strict"; 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 }); const javascript_lexer_1 = __importDefault(require("./javascript-lexer")); const ts = __importStar(require("typescript")); const logger_1 = __importDefault(require("../utils/logger")); const spread_options_to_entry_1 = __importDefault(require("../utils/spread-options-to-entry")); const extract_namespace_from_key_1 = __importDefault(require("../utils/extract-namespace-from-key")); class JsxLexer extends javascript_lexer_1.default { constructor(options = {}) { super(options); } extract(content, filename = '__default.jsx') { const keys = []; const parseCommentNode = this.createCommentNodeParser(); const parseTree = (node) => { let entry = null; parseCommentNode(keys, node, content); switch (node.kind) { case ts.SyntaxKind.CallExpression: entry = this.expressionExtractor(node); break; case ts.SyntaxKind.TaggedTemplateExpression: entry = this.taggedTemplateExpressionExtractor(node); break; case ts.SyntaxKind.JsxElement: case ts.SyntaxKind.JsxSelfClosingElement: entry = this.jsxExtractor(node); break; } if (entry) { keys.push(entry); } node.forEachChild(parseTree); }; const sourceFile = ts.createSourceFile(filename, content, ts.ScriptTarget.Latest); parseTree(sourceFile); return this.setNamespaces(keys); } jsxExtractor(node) { const tagNode = node.openingElement || node; const getPropValue = (node, tagName) => { const attribute = node.attributes.properties.find((attr) => attr.name.text === tagName); if (!attribute) { return null; } return attribute.initializer.expression ? attribute.initializer.expression.text : attribute.initializer.text; }; const getKey = (node) => getPropValue(node, 'i18nKey'); if (tagNode.tagName.text === 'Trans') { const entry = {}; entry.key = getKey(tagNode); const defaultValue = getPropValue(tagNode, 'defaultTrans'); if (defaultValue && defaultValue !== '') { entry.default = defaultValue; } const ns = getPropValue(tagNode, 'ns'); if (ns) { entry.ns = ns; } for (const property of tagNode.attributes.properties) { if (property.initializer) { if (property.name.text === 'values') { try { (0, spread_options_to_entry_1.default)(entry, property.initializer.expression, (p) => `{${p.name.text}}`); } catch (e) { logger_1.default.warning(e.message); } } } } (0, extract_namespace_from_key_1.default)(entry, this.nsSeparator); return entry.key ? entry : null; } return null; } parseChildren(children, sourceText) { return children?.map((child) => { if (child.kind === ts.SyntaxKind.JsxText) { return { type: 'text', content: child.text.replace(/(^(\n|\r)\s*)|((\n|\r)\s*$)/g, '').replace(/(\n|\r)\s*/g, ' ') }; } else if (child.kind === ts.SyntaxKind.JsxElement || child.kind === ts.SyntaxKind.JsxSelfClosingElement) { const element = child.openingElement || child; const name = element.tagName.escapedText; const isBasic = !element.attributes.properties.length; return { type: 'tag', children: this.parseChildren(child.children, sourceText), name, isBasic, selfClosing: child.kind === ts.SyntaxKind.JsxSelfClosingElement }; } else if (child.kind === ts.SyntaxKind.JsxExpression) { // strip empty expressions if (!child.expression) { return { type: 'text', content: '' }; } else if (child.expression.kind === ts.SyntaxKind.StringLiteral) { return { type: 'text', content: child.expression.text }; } // strip properties from ObjectExpressions // annoying (and who knows how many other exceptions we'll need to write) but necessary else if (child.expression.kind === ts.SyntaxKind.ObjectLiteralExpression) { let nonFormatProperties = child.expression.properties.filter((prop) => prop.name.text !== 'format'); if (nonFormatProperties.length > 1) { logger_1.default.warning(`The passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.`); return { type: 'text', content: '' }; } return { type: 'js', content: `{{${nonFormatProperties[0].name.text}}}` }; } // slice on the expression so that we ignore comments around it return { type: 'js', content: `{${sourceText.slice(child.expression.pos, child.expression.end)}}` }; } else { throw new Error('Unknown ast element when parsing jsx: ' + child.kind); } }).filter((child) => child.type !== 'text' || child.content); } } exports.default = JsxLexer;