'use strict'; var cheerio = require('cheerio'); var Po = require('pofile'); var babylon = require('babylon'); var tsParser = require('typescript-eslint-parser'); var search = require('binary-search'); var _ = require('lodash'); var escapeRegex = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; var noContext = '$$noContext'; function mkAttrRegex(startDelim, endDelim, attribute) { var start = startDelim.replace(escapeRegex, '\\$&'); var end = endDelim.replace(escapeRegex, '\\$&'); if (start === '' && end === '') { start = '^'; } else { // match optional :: (Angular 1.3's bind once syntax) without capturing start += '(?:\\s*\\:\\:\\s*)?'; } if (!_.isString(attribute) || attribute.length === 0) { attribute = 'translate'; } return new RegExp(start + '\\s*(\'|"|"|')(.*?)\\1\\s*\\|\\s*' + attribute + '\\s*:?\\s?(?:(\'|"|"|')\\s*(.*?)\\3)?\\s*(?:' + end + '|\\|)', 'g'); } function stringCompare(a, b) { return a === b ? 0 : a > b ? 1 : -1; } function contextCompare(a, b) { if (a !== null && b === null) { return -1; } else if (a === null && b !== null) { return 1; } return stringCompare(a, b); } function comments2String(comments) { return comments.join(', '); } function walkJs(node, fn, parentComment) { fn(node, parentComment); // Handle ts comments if (node && node.comments) { parentComment = node; parentComment.comments.reverse(); } for (var key in node) { var obj = node[key]; if (node && node.leadingComments) { parentComment = node; } if (typeof obj === 'object') { walkJs(obj, fn, parentComment); } } } function isStringLiteral(node) { return node.type === 'StringLiteral' || (node.type === 'Literal' && typeof(node.value) === 'string'); } function getJSExpression(node) { var res = ''; if (isStringLiteral(node)) { res = node.value; } if (node.type === 'TemplateLiteral') { node.quasis.forEach(function (elem) { res += elem.value.raw; }); } if (node.type === 'BinaryExpression' && node.operator === '+') { res += getJSExpression(node.left); res += getJSExpression(node.right); } return res; } var Extractor = (function () { function Extractor(options) { this.options = _.extend({ startDelim: '{{', endDelim: '}}', markerName: 'gettext', markerNames: [], moduleName: 'gettextCatalog', moduleMethodString: 'getString', moduleMethodPlural: 'getPlural', attribute: 'translate', attributes: [], lineNumbers: true, extensions: { htm: 'html', html: 'html', php: 'html', phtml: 'html', tml: 'html', ejs: 'html', erb: 'html', js: 'js', tag: 'html', jsp: 'html', ts: 'js', tsx: 'js', }, postProcess: function (po) {} }, options); this.options.markerNames.unshift(this.options.markerName); this.options.attributes.unshift(this.options.attribute); this.strings = {}; this.attrRegex = mkAttrRegex(this.options.startDelim, this.options.endDelim, this.options.attribute); this.noDelimRegex = mkAttrRegex('', '', this.options.attribute); } Extractor.isValidStrategy = function (strategy) { return strategy === 'html' || strategy === 'js'; }; Extractor.mkAttrRegex = mkAttrRegex; Extractor.prototype.addString = function (reference, string, plural, extractedComment, context) { // maintain backwards compatibility if (_.isString(reference)) { reference = { file: reference }; } string = string.trim(); if (string.length === 0) { return; } if (!context) { context = noContext; } if (!this.strings[string]) { this.strings[string] = {}; } if (!this.strings[string][context]) { this.strings[string][context] = new Po.Item(); } var item = this.strings[string][context]; item.msgid = string; var refString = reference.file; if (this.options.lineNumbers && reference.location && reference.location.start) { var line = reference.location.start.line; if (line || line === 0) { refString += ':' + reference.location.start.line; } } var refIndex = search(item.references, refString, stringCompare); if (refIndex < 0) { // don't add duplicate references // when not found, binary-search returns -(index_where_it_should_be + 1) item.references.splice(Math.abs(refIndex + 1), 0, refString); } if (context !== noContext) { item.msgctxt = context; } if (plural && plural !== '') { if (item.msgid_plural && item.msgid_plural !== plural) { throw new Error('Incompatible plural definitions for ' + string + ': ' + item.msgid_plural + ' / ' + plural + ' (in: ' + (item.references.join(', ')) + ')'); } item.msgid_plural = plural; item.msgstr = ['', '']; } if (extractedComment) { var commentIndex = search(item.extractedComments, extractedComment, stringCompare); if (commentIndex < 0) { // don't add duplicate comments item.extractedComments.splice(Math.abs(commentIndex + 1), 0, extractedComment); } } }; Extractor.prototype.extractJs = function (filename, src, lineNumber) { // used for line number of JS in HTML