next-translate-scanner
Version:
Scan next-translate code for translations and update json files.
205 lines (204 loc) • 8.69 kB
JavaScript
"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 base_lexer_1 = __importDefault(require("./base-lexer"));
const ts = __importStar(require("typescript"));
const logger_1 = __importDefault(require("../utils/logger"));
const extract_namespace_from_key_1 = __importDefault(require("../utils/extract-namespace-from-key"));
const spread_options_to_entry_1 = __importDefault(require("../utils/spread-options-to-entry"));
class JavascriptLexer extends base_lexer_1.default {
constructor(options = {}) {
super(options);
this.callPattern = '(?<=^|\\s|\\.)' + this.functionPattern() + '\\(.*\\)';
}
createCommentNodeParser() {
const visitedComments = new Set();
return (keys, node, content) => {
ts.forEachLeadingCommentRange(content, node.getFullStart(), (pos, end, kind) => {
const commentId = `${pos}_${end}`;
if ((kind === ts.SyntaxKind.MultiLineCommentTrivia || kind === ts.SyntaxKind.SingleLineCommentTrivia) && !visitedComments.has(commentId)) {
visitedComments.add(commentId);
const text = content.slice(pos, end);
const commentKeys = this.commentExtractor.call(this, text);
if (commentKeys) {
keys.push(...commentKeys);
}
}
});
};
}
setNamespaces(keys) {
if (this.defaultNS) {
keys = keys.map((entry) => ({
...entry,
ns: entry.ns || this.defaultNS
}));
}
keys.filter((entry) => {
if (entry.ns) {
return true;
}
logger_1.default.warning(`No namespace found for '${entry.key}'`);
return false;
});
return keys;
}
extract(content, filename = '__default.js') {
const keys = [];
const parseCommentNode = this.createCommentNodeParser();
const parseTree = (node) => {
let entry = null;
parseCommentNode(keys, node, content);
if (node.kind === ts.SyntaxKind.TaggedTemplateExpression) {
entry = this.taggedTemplateExpressionExtractor(node);
}
if (node.kind === ts.SyntaxKind.CallExpression) {
entry = this.expressionExtractor.call(this, node);
}
if (entry) {
keys.push(entry);
}
node.forEachChild(parseTree);
};
const sourceFile = ts.createSourceFile(filename, content, ts.ScriptTarget.Latest);
parseTree(sourceFile);
return this.setNamespaces(keys);
}
taggedTemplateExpressionExtractor(node) {
const entry = {};
const { tag, template } = node;
const isTranslationFunction = (tag.text && this.functions.includes(tag.text)) || (tag.name && this.functions.includes(tag.name.text));
if (!isTranslationFunction) {
return null;
}
if (template.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
entry.key = template.text;
}
else if (template.kind === ts.SyntaxKind.TemplateExpression) {
logger_1.default.warning('A key that is a template string must not have any interpolations.');
return null;
}
(0, extract_namespace_from_key_1.default)(entry, this.nsSeparator);
return entry;
}
expressionExtractor(node) {
const entry = {};
const expression = node.expression;
const nodeArguments = [...node.arguments];
if ((expression.escapedText === 'useTranslation' || expression.escapedText === 'withTranslation' || expression.escapedText === 'getT') && node.arguments.length) {
const index = expression.escapedText === 'useTranslation' ? 0 : 1;
const { text } = node.arguments[index];
if (text) {
this.defaultNS = text;
}
}
const isTranslationFunction = (expression.text && this.functions.includes(expression.text)) || (expression.name && this.functions.includes(expression.name.text));
if (isTranslationFunction) {
const keyArgument = nodeArguments.shift();
if (!keyArgument) {
return null;
}
if (keyArgument.kind === ts.SyntaxKind.StringLiteral || keyArgument.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
entry.key = keyArgument.text;
}
else if (keyArgument.kind === ts.SyntaxKind.BinaryExpression) {
const concatenatedString = this.concatenateString(keyArgument);
if (!concatenatedString) {
logger_1.default.warning(`Key is not a string literal: ${keyArgument.text}`);
return null;
}
entry.key = concatenatedString;
}
else {
logger_1.default.warning(keyArgument.kind === ts.SyntaxKind.Identifier ? `Key is not a string literal: ${keyArgument.text}` : 'Key is not a string literal');
return null;
}
const query = nodeArguments[0];
const options = nodeArguments[1];
if (query && query.kind === ts.SyntaxKind.ObjectLiteralExpression) {
try {
(0, spread_options_to_entry_1.default)(entry, query);
}
catch (e) {
logger_1.default.warning(e.message);
}
}
if (options && options.kind === ts.SyntaxKind.ObjectLiteralExpression) {
for (const p of options.properties) {
if (p.initializer) {
if (p.name.text === 'default') {
entry.default = p.initializer.text || '';
}
if (p.name.text === 'ns') {
entry.ns = p.initializer.text || '';
}
}
}
}
(0, extract_namespace_from_key_1.default)(entry, this.nsSeparator);
return entry;
}
return null;
}
commentExtractor(commentText) {
const regexp = new RegExp(this.callPattern, 'g');
const expressions = commentText.match(regexp);
if (!expressions) {
return null;
}
const keys = [];
expressions.forEach((expression) => {
const expressionKeys = this.extract(expression);
if (expressionKeys) {
keys.push(...expressionKeys);
}
});
return keys;
}
concatenateString(binaryExpression, string = '') {
if (binaryExpression.operatorToken.kind !== ts.SyntaxKind.PlusToken) {
return;
}
if (binaryExpression.left.kind === ts.SyntaxKind.BinaryExpression) {
string += this.concatenateString(binaryExpression.left, string);
}
else if (binaryExpression.left.kind === ts.SyntaxKind.StringLiteral) {
string += binaryExpression.left.text;
}
else {
return;
}
if (binaryExpression.right.kind === ts.SyntaxKind.BinaryExpression) {
string += this.concatenateString(binaryExpression.right, string);
}
else if (binaryExpression.right.kind === ts.SyntaxKind.StringLiteral) {
string += binaryExpression.right.text;
}
else {
return;
}
return string;
}
}
exports.default = JavascriptLexer;