import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";import _classCallCheck from "@babel/runtime/helpers/classCallCheck";import _createClass from "@babel/runtime/helpers/createClass";import _inherits from "@babel/runtime/helpers/inherits";import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";function _createSuper(Derived) {var hasNativeReflectConstruct = _isNativeReflectConstruct();return function _createSuperInternal() {var Super = _getPrototypeOf(Derived),result;if (hasNativeReflectConstruct) {var NewTarget = _getPrototypeOf(this).constructor;result = Reflect.construct(Super, arguments, NewTarget);} else {result = Super.apply(this, arguments);}return _possibleConstructorReturn(this, result);};}function _isNativeReflectConstruct() {try {var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));} catch (t) {}return (_isNativeReflectConstruct = function _isNativeReflectConstruct() {return !!t;})();}import JavascriptLexer from './javascript-lexer.js'; import ts from 'typescript'; import { unescape } from '../helpers.js';var JsxLexer = /*#__PURE__*/function (_JavascriptLexer) {_inherits(JsxLexer, _JavascriptLexer);var _super = _createSuper(JsxLexer); function JsxLexer() {var _this;var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};_classCallCheck(this, JsxLexer); _this = _super.call(this, options); _this.componentFunctions = options.componentFunctions || ['Trans']; _this.transSupportBasicHtmlNodes = options.transSupportBasicHtmlNodes || false; _this.transKeepBasicHtmlNodesFor = options.transKeepBasicHtmlNodesFor || [ 'br', 'strong', 'i', 'p']; _this.omitAttributes = [_this.attr, 'ns', 'defaults']; _this.transIdentityFunctionsToIgnore = options.transIdentityFunctionsToIgnore || [];return _this; }_createClass(JsxLexer, [{ key: "extract", value: function extract(content) {var _this2 = this;var filename = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '__default.jsx'; var keys = []; var parseCommentNode = this.createCommentNodeParser(); var parseTree = function parseTree(node) { var entry; parseCommentNode(keys, node, content); switch (node.kind) { case ts.SyntaxKind.CallExpression: var entries = _this2.expressionExtractor.call(_this2, node); if (entries) { keys.push.apply(keys, _toConsumableArray(entries)); } break; case ts.SyntaxKind.TaggedTemplateExpression: entry = _this2.taggedTemplateExpressionExtractor.call(_this2, node); break; case ts.SyntaxKind.JsxElement: entry = _this2.jsxExtractor.call(_this2, node, content); break; case ts.SyntaxKind.JsxSelfClosingElement: entry = _this2.jsxExtractor.call(_this2, node, content); break; } if (entry) { keys.push(entry); } node.forEachChild(parseTree); }; var sourceFile = ts.createSourceFile( filename, content, ts.ScriptTarget.Latest ); parseTree(sourceFile); var keysWithNamespace = this.setNamespaces(keys); var keysWithPrefixes = this.setKeyPrefixes(keysWithNamespace); return keysWithPrefixes; } }, { key: "jsxExtractor", value: function jsxExtractor(node, sourceText) {var _this3 = this; var tagNode = node.openingElement || node; var getPropValue = function getPropValue(node, attributeName) {var _attribute$initialize; var attribute = node.attributes.properties.find( function (attr) {return attr.name !== undefined && attr.name.text === attributeName;} ); if (!attribute) { return undefined; } if (((_attribute$initialize = attribute.initializer.expression) === null || _attribute$initialize === void 0 ? void 0 : _attribute$initialize.kind) === ts.SyntaxKind.Identifier) { _this3.emit( 'warning', "\"".concat( attributeName, "\" prop is not a string literal: ").concat(attribute.initializer.expression.text) ); return undefined; } return attribute.initializer.expression ? attribute.initializer.expression.text : attribute.initializer.text; }; var getKey = function getKey(node) {return getPropValue(node, _this3.attr);}; if ( this.componentFunctions.includes(this.expressionToName(tagNode.tagName))) { var entry = {}; entry.key = getKey(tagNode); var namespace = getPropValue(tagNode, 'ns'); if (namespace) { entry.namespace = namespace; } tagNode.attributes.properties.forEach(function (property) { if (property.kind === ts.SyntaxKind.JsxSpreadAttribute) { _this3.emit( 'warning', "Component attribute is a JSX spread attribute : ".concat( property.expression.text) ); return; } if (_this3.omitAttributes.includes(property.name.text)) { return; } if (property.initializer) { if (property.initializer.expression) { if ( property.initializer.expression.kind === ts.SyntaxKind.TrueKeyword) { entry[property.name.text] = true; } else if ( property.initializer.expression.kind === ts.SyntaxKind.FalseKeyword) { entry[property.name.text] = false; } else { entry[property.name.text] = "{".concat( property.initializer.expression.text || _this3.cleanMultiLineCode( sourceText.slice( property.initializer.expression.pos, property.initializer.expression.end ) ), "}"); } } else { entry[property.name.text] = property.initializer.text; } } else entry[property.name.text] = true; }); var nodeAsString = this.nodeToString.call(this, node, sourceText); var defaultsProp = getPropValue(tagNode, 'defaults'); var defaultValue = defaultsProp || nodeAsString; // If `shouldUnescape` is not true, it means the value cannot contain HTML entities, // so we need to unescape these entities now so that they can be properly rendered later if (entry.shouldUnescape !== true) { defaultValue = unescape(defaultValue); } if (defaultValue !== '') { entry.defaultValue = defaultValue; if (!entry.key) { // If there's no key, default to the stringified unescaped node, then to the default value: // https://github.com/i18next/react-i18next/blob/95f9c6a7b602a7b1fd33c1ded6dcfc23a52b853b/src/TransWithoutContext.js#L337 entry.key = unescape(nodeAsString) || entry.defaultValue; } } return entry.key ? entry : null; } else if (tagNode.tagName.text === 'Interpolate') { var _entry = {}; _entry.key = getKey(tagNode); return _entry.key ? _entry : null; } else if (tagNode.tagName.text === 'Translation') { var _namespace = getPropValue(tagNode, 'ns'); if (_namespace) { this.defaultNamespace = _namespace; } } } }, { key: "nodeToString", value: function nodeToString(node, sourceText) {var _this4 = this; var children = this.parseChildren.call( this, node, node.children, sourceText ); var elemsToString = function elemsToString(children) {return ( children. map(function (child, index) { switch (child.type) { case 'js': case 'text': return child.content; case 'tag': var useTagName = child.isBasic && _this4.transSupportBasicHtmlNodes && _this4.transKeepBasicHtmlNodesFor.includes(child.name); var elementName = useTagName ? child.name : index; var childrenString = elemsToString(child.children); return childrenString || !(useTagName && child.selfClosing) ? "<".concat( elementName, ">").concat(childrenString, "") : "<".concat( elementName, " />"); default: throw new Error('Unknown parsed content: ' + child.type); } }). join(''));}; return elemsToString(children); } }, { key: "cleanMultiLineCode", value: function cleanMultiLineCode(text) { return text. replace(/(^(\n|\r)\s*)|((\n|\r)\s*$)/g, ''). replace(/(\n|\r)\s*/g, ' '); } }, { key: "parseChildren", value: function parseChildren(node) {var _this5 = this;var children = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];var sourceText = arguments.length > 2 ? arguments[2] : undefined; return children. map(function (child) { if (child.kind === ts.SyntaxKind.JsxText) { return { type: 'text', content: _this5.cleanMultiLineCode(child.text) }; } else if ( child.kind === ts.SyntaxKind.JsxElement || child.kind === ts.SyntaxKind.JsxSelfClosingElement) { var element = child.openingElement || child; var name = element.tagName.escapedText; var isBasic = !element.attributes.properties.length; var hasDynamicChildren = element.attributes.properties.find( function (prop) {return ( prop.kind === ts.SyntaxKind.JsxAttribute && prop.name.escapedText === 'i18nIsDynamicList');} ); return { type: 'tag', children: hasDynamicChildren ? [] : _this5.parseChildren(child, child.children, sourceText), name: name, isBasic: isBasic, selfClosing: child.kind === ts.SyntaxKind.JsxSelfClosingElement }; } else if (child.kind === ts.SyntaxKind.JsxExpression) { // strip empty expressions if (!child.expression) { return { type: 'text', content: '' }; } // simplify trivial expressions, like TypeScript typecasts while (child.expression.kind === ts.SyntaxKind.AsExpression) { child = child.expression; } // Sometimes, we might want to wrap ObjectExpressions in a function // for typechecker compatibility: e.g., // // Instead of // `Hello, {{ name }}` // we might want: // `Hello, {castToString({ name })}` // // because that way, we can have {castToString(...)} be typed // in a a way to return a string, which would be type-compatible // with `children?: React.ReactNode` // // In these cases, we want to look at the object expressions within // the function call to extract the variables if ( child.expression.kind === ts.SyntaxKind.CallExpression && child.expression.expression.kind === ts.SyntaxKind.Identifier && _this5.transIdentityFunctionsToIgnore.includes( child.expression.expression.escapedText ) && child.expression.arguments.length >= 1) { child = { expression: child.expression.arguments[0] }; } 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) { // i18next-react only accepts two props, any random single prop, and a format prop var nonFormatProperties = child.expression.properties.filter( function (prop) {return prop.name.text !== 'format';} ); var formatProperty = child.expression.properties.find( function (prop) {return prop.name.text === 'format';} ); // more than one property throw a warning in i18next-react, but still works as a key if (nonFormatProperties.length > 1) { _this5.emit( '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: '' }; } // This matches the behaviour of the Trans component in i18next as of v13.0.2: // https://github.com/i18next/react-i18next/blob/0a4681e428c888fe986bcc0109eb19eab6ff2eb3/src/TransWithoutContext.js#L88 var value = formatProperty ? "".concat( nonFormatProperties[0].name.text, ", ").concat(formatProperty.initializer.text) : nonFormatProperties[0].name.text; return { type: 'js', content: "{{".concat(value, "}}") }; } // slice on the expression so that we ignore comments around it var slicedExpression = sourceText.slice( child.expression.pos, child.expression.end ); var tagNode = node.openingElement || node; var attrValues = tagNode.attributes.properties. filter(function (attr) {var _attr$name;return [_this5.attr, 'defaults'].includes((_attr$name = attr.name) === null || _attr$name === void 0 ? void 0 : _attr$name.text);}). map( function (attr) {var _attr$initializer$exp, _attr$initializer$exp2;return (_attr$initializer$exp = (_attr$initializer$exp2 = attr.initializer.expression) === null || _attr$initializer$exp2 === void 0 ? void 0 : _attr$initializer$exp2.text) !== null && _attr$initializer$exp !== void 0 ? _attr$initializer$exp : attr.initializer.text;} ); if (attrValues.some(function (attr) {return !attr;})) { _this5.emit('warning', "Child is not literal: ".concat(slicedExpression)); } return { type: 'js', content: "{".concat(slicedExpression, "}") }; } else { throw new Error('Unknown ast element when parsing jsx: ' + child.kind); } }). filter(function (child) {return child.type !== 'text' || child.content;}); } }]);return JsxLexer;}(JavascriptLexer);export { JsxLexer as default }; //# sourceMappingURL=jsx-lexer.js.map