@localazy/vue-i18n-extract
Version:
Localazy's fork of vue-i18n-extract
1 lines • 78.2 kB
Source Map (JSON)
{"version":3,"file":"vue-i18n-extract.modern.mjs","sources":["../src/config-file/vue-i18n-extract.config.ts","../src/config-file/index.ts","../src/types.ts","../src/create-report/vue-files.ts","../node_modules/jsonrepair/lib/esm/JsonRepairError.js","../node_modules/jsonrepair/lib/esm/stringUtils.js","../node_modules/jsonrepair/lib/esm/jsonrepair.js","../src/create-report/language-files.ts","../src/create-report/report.ts","../src/create-report/index.ts","../src/index.ts"],"sourcesContent":["export default {\n // Options documented in vue-i18n-extract readme.\n vueFiles: './src/**/*.?(js|vue)',\n languageFiles: './lang/**/*.?(json|yaml|yml|js)',\n exclude: [],\n output: false,\n add: false,\n remove: false,\n ci: false,\n separator: '.',\n noEmptyTranslation: '',\n missingTranslationString: '',\n};\n","import cac from 'cac';\nimport fs from 'fs';\nimport path from 'path';\nimport defaultConfig from './vue-i18n-extract.config';\n\nexport function initCommand(): void {\n fs.writeFileSync(\n path.resolve(process.cwd(), './vue-i18n-extract.config.ts'),\n `module.exports = ${JSON.stringify(defaultConfig, null, 2)}`,\n );\n}\n\nexport function resolveConfig (): Record<string, string> {\n const argvOptions = cac().parse(process.argv, { run: false }).options;\n\n let options;\n\n try {\n const pathToConfigFile = path.resolve(process.cwd(), './vue-i18n-extract.config.ts');\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const configOptions = (require(pathToConfigFile)).default;\n\n console.info(`\\nUsing config file found at ${pathToConfigFile}`);\n\n options = {\n ...configOptions,\n ...argvOptions\n };\n } catch {\n options = argvOptions;\n }\n\n options.exclude = Array.isArray(options.exclude) ? options.exclude : [options.exclude];\n\n return options;\n}\n","export type ReportOptions = {\n vueFiles: string;\n languageFiles: string;\n output?: string;\n exclude?: string[];\n add?: boolean;\n remove?: boolean;\n ci?: boolean;\n separator?: string;\n noEmptyTranslation?: string;\n missingTranslationString?: string;\n detect?: DetectionType[];\n}\n\nexport enum DetectionType {\n Missing = \"missing\",\n Unused = \"unused\",\n Dynamic = \"dynamic\"\n}\n\nexport type SimpleFile = {\n fileName: string;\n path: string;\n content: string;\n}\n\nexport type I18NItem = {\n line?: number;\n path: string;\n file?: string;\n language?: string;\n}\n\nexport type I18NItemWithBounding = I18NItem & {\n previousCharacter: string;\n nextCharacter: string;\n}\n\nexport type I18NLanguage = {\n [language: string]: I18NItem[];\n}\n\nexport type I18NReport = {\n missingKeys: I18NItem[];\n unusedKeys: I18NItem[];\n maybeDynamicKeys: I18NItem[];\n}\n","import { SimpleFile, I18NItemWithBounding } from '../types';\nimport isValidGlob from 'is-valid-glob';\nimport glob from 'glob';\nimport fs from 'fs';\n\nexport function readVueFiles (src: string): SimpleFile[] {\n // Replace backslash path segments to make the path work with the glob package.\n // https://github.com/Spittal/vue-i18n-extract/issues/159\n const normalizedSrc = src.replace(/\\\\/g, '/');\n if (!isValidGlob(normalizedSrc)) {\n throw new Error(`vueFiles isn't a valid glob pattern.`);\n }\n\n const targetFiles = glob.sync(normalizedSrc);\n\n if (targetFiles.length === 0) {\n throw new Error('vueFiles glob has no files.');\n }\n\n return targetFiles.map((f) => {\n const fileName = f.replace(process.cwd(), '.');\n return { fileName, path: f, content: fs.readFileSync(f, 'utf8') };\n });\n}\n\nfunction* getMatches (file: SimpleFile, regExp: RegExp, captureGroup = 1): IterableIterator<I18NItemWithBounding> {\n while (true) {\n const match = regExp.exec(file.content);\n if (match === null) {\n break;\n }\n const path = match[captureGroup];\n\n const pathAtIndex = file.content.indexOf(path);\n const previousCharacter = file.content.charAt(pathAtIndex - 1);\n const nextCharacter = file.content.charAt(pathAtIndex + path.length);\n\n const line = (file.content.substring(0, match.index).match(/\\n/g) || []).length + 1;\n yield {\n path,\n previousCharacter,\n nextCharacter,\n file: file.fileName,\n line,\n };\n }\n}\n\n/**\n * Extracts translation keys from methods such as `$t` and `$tc`.\n *\n * - **regexp pattern**: (?:[$\\s.:\"'`+\\(\\[\\{]t[cm]?)\\(\n *\n * **description**: Matches the sequence t(, tc( or tm(, optionally with either “$”, SPACE, “.”, “:”, “\"”, “'”,\n * “`”, \"+\", \"(\", \"[\" or \"{\" in front of it.\n *\n * - **regexp pattern**: ([\"'`])\n *\n * **description**: 1. capturing group. Matches either “\"”, “'”, or “`”.\n *\n * - **regexp pattern**: ((?:[^\\\\]|\\\\.)*?)\n *\n * **description**: 2. capturing group. Matches anything except a backslash\n * *or* matches any backslash followed by any character (e.g. “\\\"”, “\\`”, “\\t”, etc.)\n *\n * - **regexp pattern**: \\1\n *\n * **description**: matches whatever was matched by capturing group 1 (e.g. the starting string character)\n *\n * @param file a file object\n * @returns a list of translation keys found in `file`.\n */\n function extractMethodMatches (file: SimpleFile): I18NItemWithBounding[] {\n const methodRegExp = /(?:[$\\s.:\"'`+\\(\\[\\{]t[cm]?)\\(\\s*?([\"'`])((?:[^\\\\]|\\\\.)*?)\\1/g;\n return [ ...getMatches(file, methodRegExp, 2) ];\n}\n\nfunction extractComponentMatches (file: SimpleFile): I18NItemWithBounding[] {\n // Updated: It excludes paths with \"/\"\n // My modification\n const componentRegExp = /(?:(?:<|h\\()(?:i18n|Translation))(?:.|\\n)*?(?:[^:]path(?:=|: )(\"|'))([^/\\\\]*?)\\1/gi;\n\n // Their updated original\n // const componentRegExp = /(?:(?:<|h\\()(?:i18n|Translation))(?:.|\\n)*?(?:\\s(?:(?:key)?)path(?:=|: )(\"|'))([^/\\\\]*?)\\1/gi;\n\n // Their updated with my modification\n // const componentRegExp = /(?:(?:<|h\\()(?:i18n|Translation))(?:.|\\n)*?(?:\\s(?:(?:key)?)path(?:=|: )(\"|'))((?:[^\\\\]|\\\\.)*?)\\1/gi;\n\n return [ ...getMatches(file, componentRegExp, 2) ];\n}\n\nfunction extractDirectiveMatches (file: SimpleFile): I18NItemWithBounding[] {\n const directiveRegExp = /\\bv-t(?:\\.[\\w-]+)?=\"'((?:[^\\\\]|\\\\.)*?)'\"/g;\n return [ ...getMatches(file, directiveRegExp) ];\n}\n\nexport function extractI18NItemsFromVueFiles (sourceFiles: SimpleFile[]): I18NItemWithBounding[] {\n return sourceFiles.reduce((accumulator, file) => {\n const methodMatches = extractMethodMatches(file);\n const componentMatches = extractComponentMatches(file);\n const directiveMatches = extractDirectiveMatches(file);\n return [\n ...accumulator,\n ...methodMatches,\n ...componentMatches,\n ...directiveMatches,\n ];\n }, [] as I18NItemWithBounding[]);\n}\n\n// This is a convenience function for users implementing in their own projects, and isn't used internally\nexport function parseVueFiles (vueFiles: string): I18NItemWithBounding[] {\n return extractI18NItemsFromVueFiles(readVueFiles(vueFiles));\n}\n","export default function JsonRepairError(message, char) {\n if (!(this instanceof JsonRepairError)) {\n throw new SyntaxError('Constructor must be called with the new operator');\n }\n\n this.message = message + ' (char ' + char + ')';\n this.char = char;\n this.stack = new Error().stack;\n}\nJsonRepairError.prototype = new Error();\nJsonRepairError.prototype.constructor = Error;\n//# sourceMappingURL=JsonRepairError.js.map","var SINGLE_QUOTES = {\n '\\'': true,\n // quote\n \"\\u2018\": true,\n // quote left\n \"\\u2019\": true,\n // quote right\n \"`\": true,\n // grave accent\n \"\\xB4\": true // acute accent\n\n};\nvar DOUBLE_QUOTES = {\n '\"': true,\n \"\\u201C\": true,\n // double quote left\n \"\\u201D\": true // double quote right\n\n};\n/**\n * Check if the given character contains an alpha character, a-z, A-Z, _\n * @param {string} c\n * @return {boolean}\n */\n\nexport function isAlpha(c) {\n return ALPHA_REGEX.test(c);\n}\nvar ALPHA_REGEX = /^[a-zA-Z_]$/;\n/**\n * Check if the given character contains a hexadecimal character 0-9, a-f, A-F\n * @param {string} c\n * @return {boolean}\n */\n\nexport function isHex(c) {\n return HEX_REGEX.test(c);\n}\nvar HEX_REGEX = /^[0-9a-fA-F]$/;\n/**\n * checks if the given char c is a digit\n * @param {string} c\n * @return {boolean}\n */\n\nexport function isDigit(c) {\n return DIGIT_REGEX.test(c);\n}\nvar DIGIT_REGEX = /^[0-9]$/;\n/**\n * Check if the given character is a whitespace character like space, tab, or\n * newline\n * @param {string} c\n * @return {boolean}\n */\n\nexport function isWhitespace(c) {\n return c === ' ' || c === '\\t' || c === '\\n' || c === '\\r';\n}\n/**\n * Check if the given character is a special whitespace character, some\n * unicode variant\n * @param {string} c\n * @return {boolean}\n */\n\nexport function isSpecialWhitespace(c) {\n return c === \"\\xA0\" || c >= \"\\u2000\" && c <= \"\\u200A\" || c === \"\\u202F\" || c === \"\\u205F\" || c === \"\\u3000\";\n}\n/**\n * Replace speical whitespace characters with regular spaces\n * @param {string} text\n * @returns {string}\n */\n\nexport function normalizeWhitespace(text) {\n var normalized = '';\n\n for (var i = 0; i < text.length; i++) {\n var char = text[i];\n normalized += isSpecialWhitespace(char) ? ' ' : char;\n }\n\n return normalized;\n}\n/**\n * Test whether the given character is a quote or double quote character.\n * Also tests for special variants of quotes.\n * @param {string} c\n * @returns {boolean}\n */\n\nexport function isQuote(c) {\n return SINGLE_QUOTES[c] === true || DOUBLE_QUOTES[c] === true;\n}\n/**\n * Test whether the given character is a single quote character.\n * Also tests for special variants of single quotes.\n * @param {string} c\n * @returns {boolean}\n */\n\nexport function isSingleQuote(c) {\n return SINGLE_QUOTES[c] === true;\n}\n/**\n * Test whether the given character is a double quote character.\n * Also tests for special variants of double quotes.\n * @param {string} c\n * @returns {boolean}\n */\n\nexport function isDoubleQuote(c) {\n return DOUBLE_QUOTES[c] === true;\n}\n/**\n * Normalize special double or single quote characters to their regular\n * variant ' or \"\n * @param {string} c\n * @returns {string}\n */\n\nexport function normalizeQuote(c) {\n if (SINGLE_QUOTES[c] === true) {\n return '\\'';\n }\n\n if (DOUBLE_QUOTES[c] === true) {\n return '\"';\n }\n\n return c;\n}\n/**\n * Strip last occurrence of textToStrip from text\n * @param {string} text\n * @param {string} textToStrip\n * @returns {string}\n */\n\nexport function stripLastOccurrence(text, textToStrip) {\n var index = text.lastIndexOf(textToStrip);\n return index !== -1 ? text.substring(0, index) + text.substring(index + 1) : text;\n}\n/**\n * Insert textToInsert into text before the last whitespace in text\n * @param {string} text\n * @param {string} textToInsert\n * @returns {string}\n */\n\nexport function insertBeforeLastWhitespace(text, textToInsert) {\n var index = text.length;\n\n if (!isWhitespace(text[index - 1])) {\n // no trailing whitespaces\n return text + textToInsert;\n }\n\n while (isWhitespace(text[index - 1])) {\n index--;\n }\n\n return text.substring(0, index) + textToInsert + text.substring(index);\n}\n/**\n * Insert textToInsert at index in text\n * @param {string} text\n * @param {string} textToInsert\n * @param {number} index\n * @returns {string}\n */\n\nexport function insertAtIndex(text, textToInsert, index) {\n return text.substring(0, index) + textToInsert + text.substring(index);\n}\n//# sourceMappingURL=stringUtils.js.map","import JsonRepairError from './JsonRepairError.js';\nimport { insertAtIndex, insertBeforeLastWhitespace, isAlpha, isDigit, isDoubleQuote, isHex, isQuote, isSingleQuote, isSpecialWhitespace, isWhitespace, normalizeQuote, normalizeWhitespace, stripLastOccurrence } from './stringUtils.js'; // token types enumeration\n\nvar DELIMITER = 0;\nvar NUMBER = 1;\nvar STRING = 2;\nvar SYMBOL = 3;\nvar WHITESPACE = 4;\nvar COMMENT = 5;\nvar UNKNOWN = 6;\n/**\n * @typedef {DELIMITER | NUMBER | STRING | SYMBOL | WHITESPACE | COMMENT | UNKNOWN} TokenType\n */\n// map with all delimiters\n\nvar DELIMITERS = {\n '': true,\n '{': true,\n '}': true,\n '[': true,\n ']': true,\n ':': true,\n ',': true,\n // for JSONP and MongoDB data type notation\n '(': true,\n ')': true,\n ';': true,\n // for string concatenation\n '+': true\n}; // map with all escape characters\n\nvar ESCAPE_CHARACTERS = {\n '\"': '\"',\n '\\\\': '\\\\',\n '/': '/',\n b: '\\b',\n f: '\\f',\n n: '\\n',\n r: '\\r',\n t: '\\t' // \\u is handled by getToken()\n\n}; // TODO: can we unify CONTROL_CHARACTERS and ESCAPE_CHARACTERS?\n\nvar CONTROL_CHARACTERS = {\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t'\n};\nvar SYMBOLS = {\n null: 'null',\n true: 'true',\n false: 'false'\n};\nvar PYTHON_SYMBOLS = {\n None: 'null',\n True: 'true',\n False: 'false'\n};\nvar input = ''; // current json text\n\nvar output = ''; // generated output\n\nvar index = 0; // current index in text\n\nvar c = ''; // current token character in text\n\nvar token = ''; // current token\n\nvar tokenType = UNKNOWN; // type of current token\n\n/**\n * Repair a string containing an invalid JSON document.\n * For example changes JavaScript notation into JSON notation.\n *\n * Example:\n *\n * jsonrepair('{name: \\'John\\'}\") // '{\"name\": \"John\"}'\n *\n * @param {string} text\n * @return {string}\n */\n\nexport default function jsonrepair(text) {\n // initialize\n input = text;\n output = '';\n index = 0;\n c = input.charAt(0);\n token = '';\n tokenType = UNKNOWN; // get first token\n\n processNextToken();\n var rootLevelTokenType = tokenType; // parse everything\n\n parseObject(); // ignore trailing comma\n\n skipComma();\n\n if (token === '') {\n // reached the end of the document properly\n return output;\n }\n\n if (rootLevelTokenType === tokenType && tokenIsStartOfValue()) {\n // start of a new value after end of the root level object: looks like\n // newline delimited JSON -> turn into a root level array\n var stashedOutput = '';\n\n while (rootLevelTokenType === tokenType && tokenIsStartOfValue()) {\n output = insertBeforeLastWhitespace(output, ',');\n stashedOutput += output;\n output = ''; // parse next newline delimited item\n\n parseObject(); // ignore trailing comma\n\n skipComma();\n } // wrap the output in an array\n\n\n return \"[\\n\".concat(stashedOutput).concat(output, \"\\n]\");\n }\n\n throw new JsonRepairError('Unexpected characters', index - token.length);\n}\n/**\n * Get the next character from the expression.\n * The character is stored into the char c. If the end of the expression is\n * reached, the function puts an empty string in c.\n */\n\nfunction next() {\n index++;\n c = input.charAt(index); // Note: not using input[index] because that returns undefined when index is out of range\n}\n/**\n * Special version of the function next, used to parse escaped strings\n */\n\n\nfunction nextSkipEscape() {\n next();\n\n if (c === '\\\\') {\n next();\n }\n}\n/**\n * check whether the current token is the start of a value:\n * object, array, number, string, or symbol\n * @returns {boolean}\n */\n\n\nfunction tokenIsStartOfValue() {\n return tokenType === DELIMITER && (token === '[' || token === '{') || tokenType === STRING || tokenType === NUMBER || tokenType === SYMBOL;\n}\n/**\n * check whether the current token is the start of a key (or possible key):\n * number, string, or symbol\n * @returns {boolean}\n */\n\n\nfunction tokenIsStartOfKey() {\n return tokenType === STRING || tokenType === NUMBER || tokenType === SYMBOL;\n}\n/**\n * Process the previous token, and get next token in the current text\n */\n\n\nfunction processNextToken() {\n output += token;\n tokenType = UNKNOWN;\n token = '';\n getTokenDelimiter();\n\n if (tokenType === WHITESPACE) {\n // we leave the whitespace as it is, except replacing special white\n // space character\n token = normalizeWhitespace(token);\n processNextToken();\n }\n\n if (tokenType === COMMENT) {\n // ignore comments\n tokenType = UNKNOWN;\n token = '';\n processNextToken();\n }\n}\n\nfunction skipComma() {\n if (token === ',') {\n token = '';\n tokenType = UNKNOWN;\n processNextToken();\n }\n} // check for delimiters like ':', '{', ']'\n\n\nfunction getTokenDelimiter() {\n if (DELIMITERS[c]) {\n tokenType = DELIMITER;\n token = c;\n next();\n return;\n }\n\n getTokenNumber();\n} // check for a number like \"2.3e+5\"\n\n\nfunction getTokenNumber() {\n if (isDigit(c) || c === '-') {\n tokenType = NUMBER;\n\n if (c === '-') {\n token += c;\n next();\n\n if (!isDigit(c)) {\n throw new JsonRepairError('Invalid number, digit expected', index);\n }\n } else if (c === '0') {\n token += c;\n next();\n } else {// digit 1-9, nothing extra to do\n }\n\n while (isDigit(c)) {\n token += c;\n next();\n }\n\n if (c === '.') {\n token += c;\n next();\n\n if (!isDigit(c)) {\n throw new JsonRepairError('Invalid number, digit expected', index);\n }\n\n while (isDigit(c)) {\n token += c;\n next();\n }\n }\n\n if (c === 'e' || c === 'E') {\n token += c;\n next();\n\n if (c === '+' || c === '-') {\n token += c;\n next();\n }\n\n if (!isDigit(c)) {\n throw new JsonRepairError('Invalid number, digit expected', index);\n }\n\n while (isDigit(c)) {\n token += c;\n next();\n }\n }\n\n return;\n }\n\n getTokenEscapedString();\n} // get a token string like '\\\"hello world\\\"'\n\n\nfunction getTokenEscapedString() {\n if (c === '\\\\' && input.charAt(index + 1) === '\"') {\n // an escaped piece of JSON\n next();\n getTokenString(nextSkipEscape);\n } else {\n getTokenString(next);\n }\n} // get a token string like '\"hello world\"'\n\n\nfunction getTokenString(getNext) {\n if (isQuote(c)) {\n var quote = normalizeQuote(c);\n var isEndQuote = isSingleQuote(c) ? isSingleQuote : isDoubleQuote;\n token += '\"'; // output valid double quote\n\n tokenType = STRING;\n getNext(); // eslint-disable-next-line no-unmodified-loop-condition\n\n while (c !== '' && !isEndQuote(c)) {\n if (c === '\\\\') {\n // handle escape characters\n getNext();\n var unescaped = ESCAPE_CHARACTERS[c];\n\n if (unescaped !== undefined) {\n token += '\\\\' + c;\n getNext();\n } else if (c === 'u') {\n // parse escaped unicode character, like '\\\\u260E'\n token += \"\\\\u\";\n getNext();\n\n for (var u = 0; u < 4; u++) {\n if (!isHex(c)) {\n throw new JsonRepairError('Invalid unicode character', index - token.length);\n }\n\n token += c;\n getNext();\n }\n } else if (c === '\\'') {\n // escaped single quote character -> remove the escape character\n token += '\\'';\n getNext();\n } else {\n throw new JsonRepairError('Invalid escape character \"\\\\' + c + '\"', index);\n }\n } else if (CONTROL_CHARACTERS[c]) {\n // unescaped special character\n // fix by adding an escape character\n token += CONTROL_CHARACTERS[c];\n getNext();\n } else if (c === '\"') {\n // unescaped double quote -> escape it\n token += '\\\\\"';\n getNext();\n } else {\n // a regular character\n token += c;\n getNext();\n }\n }\n\n if (normalizeQuote(c) !== quote) {\n throw new JsonRepairError('End of string expected', index - token.length);\n }\n\n token += '\"'; // output valid double quote\n\n getNext();\n return;\n }\n\n getTokenAlpha();\n} // check for symbols (true, false, null)\n\n\nfunction getTokenAlpha() {\n if (isAlpha(c)) {\n tokenType = SYMBOL;\n\n while (isAlpha(c) || isDigit(c) || c === '$') {\n token += c;\n next();\n }\n\n return;\n }\n\n getTokenWhitespace();\n} // get whitespaces: space, tab, newline, and carriage return\n\n\nfunction getTokenWhitespace() {\n if (isWhitespace(c) || isSpecialWhitespace(c)) {\n tokenType = WHITESPACE;\n\n while (isWhitespace(c) || isSpecialWhitespace(c)) {\n token += c;\n next();\n }\n\n return;\n }\n\n getTokenComment();\n}\n\nfunction getTokenComment() {\n // find a block comment '/* ... */'\n if (c === '/' && input[index + 1] === '*') {\n tokenType = COMMENT;\n\n while (c !== '' && (c !== '*' || c === '*' && input[index + 1] !== '/')) {\n token += c;\n next();\n }\n\n if (c === '*' && input[index + 1] === '/') {\n token += c;\n next();\n token += c;\n next();\n }\n\n return;\n } // find a comment '// ...'\n\n\n if (c === '/' && input[index + 1] === '/') {\n tokenType = COMMENT;\n\n while (c !== '' && c !== '\\n') {\n token += c;\n next();\n }\n\n return;\n }\n\n getTokenUnknown();\n} // something unknown is found, wrong characters -> a syntax error\n\n\nfunction getTokenUnknown() {\n tokenType = UNKNOWN;\n\n while (c !== '') {\n token += c;\n next();\n }\n\n throw new JsonRepairError('Syntax error in part \"' + token + '\"', index - token.length);\n}\n/**\n * Parse an object like '{\"key\": \"value\"}'\n * @return {*}\n */\n\n\nfunction parseObject() {\n if (tokenType === DELIMITER && token === '{') {\n processNextToken(); // TODO: can we make this redundant?\n\n if (tokenType === DELIMITER && token === '}') {\n // empty object\n processNextToken();\n return;\n }\n\n while (true) {\n // parse key\n if (tokenType === SYMBOL || tokenType === NUMBER) {\n // unquoted key -> add quotes around it, change it into a string\n tokenType = STRING;\n token = \"\\\"\".concat(token, \"\\\"\");\n }\n\n if (tokenType !== STRING) {\n // TODO: handle ambiguous cases like '[{\"a\":1,{\"b\":2}]' which could be an array with two objects or one\n throw new JsonRepairError('Object key expected', index - token.length);\n }\n\n processNextToken(); // parse colon (key/value separator)\n\n if (tokenType === DELIMITER && token === ':') {\n processNextToken();\n } else {\n if (tokenIsStartOfValue()) {\n // we expect a colon here, but got the start of a value\n // -> insert a colon before any inserted whitespaces at the end of output\n output = insertBeforeLastWhitespace(output, ':');\n } else {\n throw new JsonRepairError('Colon expected', index - token.length);\n }\n } // parse value\n\n\n parseObject(); // parse comma (key/value pair separator)\n\n if (tokenType === DELIMITER && token === ',') {\n processNextToken();\n\n if (tokenType === DELIMITER && token === '}') {\n // we've just passed a trailing comma -> remove the trailing comma\n output = stripLastOccurrence(output, ',');\n break;\n }\n\n if (token === '') {\n // end of json reached, but missing }\n // Strip the missing comma (the closing bracket will be added later)\n output = stripLastOccurrence(output, ',');\n break;\n }\n } else {\n if (tokenIsStartOfKey()) {\n // we expect a comma here, but got the start of a new key\n // -> insert a comma before any inserted whitespaces at the end of output\n output = insertBeforeLastWhitespace(output, ',');\n } else {\n break;\n }\n }\n }\n\n if (tokenType === DELIMITER && token === '}') {\n processNextToken();\n } else {\n // missing end bracket -> insert the missing bracket\n output = insertBeforeLastWhitespace(output, '}');\n }\n\n return;\n }\n\n parseArray();\n}\n/**\n * Parse an object like '[\"item1\", \"item2\", ...]'\n */\n\n\nfunction parseArray() {\n if (tokenType === DELIMITER && token === '[') {\n processNextToken();\n\n if (tokenType === DELIMITER && token === ']') {\n // empty array\n processNextToken();\n return;\n }\n\n while (true) {\n // parse item\n parseObject(); // parse comma (item separator)\n\n if (tokenType === DELIMITER && token === ',') {\n processNextToken();\n\n if (tokenType === DELIMITER && token === ']') {\n // we've just passed a trailing comma -> remove the trailing comma\n output = stripLastOccurrence(output, ',');\n break;\n }\n\n if (token === '') {\n // end of json reached, but missing ]\n // Strip the missing comma (the closing bracket will be added later)\n output = stripLastOccurrence(output, ',');\n break;\n }\n } else {\n if (tokenIsStartOfValue()) {\n // we expect a comma here, but got the start of a new item\n // -> insert a comma before any inserted whitespaces at the end of output\n output = insertBeforeLastWhitespace(output, ',');\n } else {\n break;\n }\n }\n }\n\n if (tokenType === DELIMITER && token === ']') {\n processNextToken();\n } else {\n // missing end bracket -> insert the missing bracket\n output = insertBeforeLastWhitespace(output, ']');\n }\n\n return;\n }\n\n parseString();\n}\n/**\n * Parse a string enclosed by double quotes \"...\". Can contain escaped quotes\n */\n\n\nfunction parseString() {\n if (tokenType === STRING) {\n processNextToken();\n\n while (tokenType === DELIMITER && token === '+') {\n // string concatenation like \"hello\" + \"world\"\n token = ''; // don't output the concatenation\n\n processNextToken();\n\n if (tokenType === STRING) {\n // concatenate with the previous string\n var endIndex = output.lastIndexOf('\"');\n output = output.substring(0, endIndex) + token.substring(1);\n token = '';\n processNextToken();\n }\n }\n\n return;\n }\n\n parseNumber();\n}\n/**\n * Parse a number\n */\n\n\nfunction parseNumber() {\n if (tokenType === NUMBER) {\n processNextToken();\n return;\n }\n\n parseSymbol();\n}\n/**\n * Parse constants true, false, null\n */\n\n\nfunction parseSymbol() {\n if (tokenType === SYMBOL) {\n // a supported symbol: true, false, null\n if (SYMBOLS[token]) {\n processNextToken();\n return;\n } // for example replace None with null\n\n\n if (PYTHON_SYMBOLS[token]) {\n token = PYTHON_SYMBOLS[token];\n processNextToken();\n return;\n } // make a copy of the symbol, let's see what comes next\n\n\n var symbol = token;\n var symbolIndex = output.length;\n token = '';\n processNextToken(); // if (tokenType === DELIMITER && token === '(') {\n\n if (tokenType === DELIMITER && token === '(') {\n // a MongoDB function call or JSONP call\n // Can be a MongoDB data type like in {\"_id\": ObjectId(\"123\")}\n // token = '' // do not output the function name\n // processNextToken()\n // next()\n token = ''; // do not output the ( character\n\n processNextToken(); // process the part inside the brackets\n\n parseObject(); // skip the closing bracket \")\" and \");\"\n\n if (tokenType === DELIMITER && token === ')') {\n token = ''; // do not output the ) character\n\n processNextToken();\n\n if (tokenType === DELIMITER && token === ';') {\n token = ''; // do not output the semicolon character\n\n processNextToken();\n }\n }\n\n return;\n } // unknown symbol => turn into in a string\n // it is possible that by reading the next token we already inserted\n // extra spaces in the output which should be inside the string,\n // hence the symbolIndex\n\n\n output = insertAtIndex(output, \"\\\"\".concat(symbol), symbolIndex);\n\n while (tokenType === SYMBOL || tokenType === NUMBER) {\n processNextToken();\n }\n\n output += '\"';\n return;\n }\n\n parseEnd();\n}\n/**\n * Evaluated when the expression is not yet ended but expected to end\n */\n\n\nfunction parseEnd() {\n if (token === '') {\n // syntax error or unexpected end of expression\n throw new JsonRepairError('Unexpected end of json string', index - token.length);\n } else {\n throw new JsonRepairError('Value expected', index - token.length);\n }\n}\n//# sourceMappingURL=jsonrepair.js.map","import path from 'path';\nimport fs from 'fs';\nimport glob from 'glob';\nimport Dot from 'dot-object';\nimport yaml from 'js-yaml';\nimport isValidGlob from 'is-valid-glob';\nimport { SimpleFile, I18NLanguage, I18NItem } from '../types';\nimport jsonrepair from \"jsonrepair\"\n\nexport function readLanguageFiles (src: string): SimpleFile[] {\n // Replace backslash path segments to make the path work with the glob package.\n // https://github.com/Spittal/vue-i18n-extract/issues/159\n const normalizedSrc = src.replace(/\\\\/g, '/');\n if (!isValidGlob(normalizedSrc)) {\n throw new Error(`languageFiles isn't a valid glob pattern.`);\n }\n\n const targetFiles = glob.sync(normalizedSrc);\n\n if (targetFiles.length === 0) {\n throw new Error('languageFiles glob has no files.');\n }\n\n return targetFiles.map(f => {\n const langPath = path.resolve(process.cwd(), f);\n const currentFolder = langPath.substring(0, langPath.lastIndexOf(\"/\"))\n const localeIndexFile = `${currentFolder}/index.ts`;\n\n const extension = langPath.substring(langPath.lastIndexOf('.')).toLowerCase();\n const isJSON = extension === '.json';\n const isTS = extension === '.ts';\n const isYAML = extension === '.yaml' || extension === '.yml';\n\n let langObj;\n if (isJSON) {\n langObj = JSON.parse(fs.readFileSync(langPath, 'utf8'));\n } else if (isYAML) {\n langObj = yaml.load(fs.readFileSync(langPath, 'utf8'));\n } else if (isTS) {\n const content = fs.readFileSync(langPath, 'utf8');\n const indexFile = fs.readFileSync(localeIndexFile, 'utf8');\n const cleanContent = content\n .replace(\"export default\", \"\")\n .replace(/};/g, \"}\")\n .replace(/} ;/g, \"}\")\n .replace(/`/g, '\"')\n // JSONRepair doesn't like apostrophes\n .replace(/’/g, \"\\\\'\")\n // const keyPrefixRegex = new RegExp(/\"xxa\", { \"?([@\\w]*)\"?:/g)\n const keyPrefixRegex = new RegExp(/module: '(.+)',/gm)\n const keyPrefix = keyPrefixRegex.exec(indexFile)\n if (keyPrefix && keyPrefix[1]) {\n langObj = JSON.parse(jsonrepair(cleanContent));\n langObj = {\n [keyPrefix[1]]: langObj\n }\n } else {\n throw new Error(\"Could not read key prefix from locale index file\");\n }\n } else {\n langObj = eval(fs.readFileSync(langPath, 'utf8'));\n }\n\n const fileName = f.replace(process.cwd(), '.');\n\n return { path: f, fileName, content: JSON.stringify(langObj) };\n });\n}\n\nexport function extractI18NLanguageFromLanguageFiles (languageFiles: SimpleFile[], dot: DotObject.Dot = Dot): I18NLanguage {\n return languageFiles.reduce((accumulator, file) => {\n const language = file.fileName.substring(file.fileName.lastIndexOf('/') + 1, file.fileName.lastIndexOf('.'));\n\n if (!accumulator[language]) {\n accumulator[language] = [];\n }\n\n const flattenedObject = dot.dot(JSON.parse(file.content));\n Object.keys(flattenedObject).forEach((key) => {\n accumulator[language].push({\n path: key,\n file: file.fileName,\n });\n });\n\n return accumulator;\n }, {});\n}\n\nexport function writeMissingToLanguageFiles (parsedLanguageFiles: SimpleFile[], missingKeys: I18NItem[], dot: DotObject.Dot = Dot, noEmptyTranslation = '', missingTranslationString = ''): void {\n parsedLanguageFiles.forEach(languageFile => {\n const languageFileContent = JSON.parse(languageFile.content);\n\n missingKeys.forEach(item => {\n if (item.language && languageFile.fileName.includes(item.language) || !item.language) {\n const addDefaultTranslation = (noEmptyTranslation) && ((noEmptyTranslation === '*') || (noEmptyTranslation === item.language));\n dot.str(item.path, addDefaultTranslation ? item.path : missingTranslationString === 'null' ? null : missingTranslationString, languageFileContent);\n }\n });\n\n writeLanguageFile(languageFile, languageFileContent);\n });\n}\n\nexport function removeUnusedFromLanguageFiles (parsedLanguageFiles: SimpleFile[], unusedKeys: I18NItem[], dot: DotObject.Dot = Dot): void {\n parsedLanguageFiles.forEach(languageFile => {\n const languageFileContent = JSON.parse(languageFile.content);\n\n unusedKeys.forEach(item => {\n if (item.language && languageFile.fileName.includes(item.language)) {\n dot.delete(item.path, languageFileContent);\n }\n });\n\n writeLanguageFile(languageFile, languageFileContent);\n });\n}\n\nfunction writeLanguageFile (languageFile: SimpleFile, newLanguageFileContent: unknown) {\n const fileExtension = languageFile.fileName.substring(languageFile.fileName.lastIndexOf('.') + 1);\n const filePath = languageFile.path;\n const nestedContent = Object.values(newLanguageFileContent as Record<string, Record<string, string>>)[0];\n const stringifiedContent = JSON.stringify(newLanguageFileContent, null, 2);\n\n if (fileExtension === 'json') {\n fs.writeFileSync(filePath, stringifiedContent);\n } else if (fileExtension === 'js') {\n const jsFile = `module.exports = ${stringifiedContent}; \\n`;\n fs.writeFileSync(filePath, jsFile);\n } else if (fileExtension === 'yaml' || fileExtension === 'yml') {\n const yamlFile = yaml.dump(newLanguageFileContent);\n fs.writeFileSync(filePath, yamlFile);\n } else if (fileExtension === 'ts') {\n const nestedStringifiedContent = JSON.stringify(nestedContent, null, 2);\n const unquotedContent = unquotePropertiesFromJSONString(nestedStringifiedContent);\n const tsFile = `export default ${unquotedContent};`;\n fs.writeFileSync(filePath, tsFile);\n } else {\n throw new Error(`Language filetype of ${fileExtension} not supported.`)\n }\n}\n\nfunction unquotePropertiesFromJSONString(stringifiedObject: string) {\n return stringifiedObject.replace(/^[\\t ]*\"[^:\\n\\r]+(?<!\\\\)\":/gm, function (match) {\n return match.replace(/\"/g, \"\");\n });\n}\n\n// This is a convenience function for users implementing in their own projects, and isn't used internally\nexport function parselanguageFiles (languageFiles: string, dot: DotObject.Dot = Dot): I18NLanguage {\n return extractI18NLanguageFromLanguageFiles(readLanguageFiles(languageFiles), dot);\n}\n","import fs from 'fs';\nimport { DetectionType, I18NItem, I18NItemWithBounding, I18NLanguage, I18NReport } from '../types';\n\nfunction stripBounding (item: I18NItemWithBounding): I18NItem {\n return {\n path: item.path,\n file: item.file,\n line: item.line,\n }\n}\n\nfunction mightBeDynamic (item: I18NItemWithBounding): boolean {\n return item.path.includes('${') && !!item.previousCharacter.match(/`/g) && !!item.nextCharacter.match(/`/g);\n}\n\n// Looping through the arays multiple times might not be the most effecient, but it's the easiest to read and debug. Which at this scale is an accepted trade-off.\nexport function extractI18NReport (vueItems: I18NItemWithBounding[], languageFiles: I18NLanguage, detect: DetectionType[]): I18NReport {\n const missingKeys: I18NItem[] = [];\n const unusedKeys: I18NItem[] = [];\n const maybeDynamicKeys: I18NItem[] = [];\n\n if (detect.includes(DetectionType.Dynamic)) {\n maybeDynamicKeys. push( ...vueItems\n .filter(vueItem => mightBeDynamic(vueItem))\n .map(vueItem => stripBounding(vueItem)));\n }\n\n Object.keys(languageFiles).forEach(language => {\n const languageItems = languageFiles[language];\n\n if (detect.includes(DetectionType.Missing)) {\n const missingKeysInLanguage = vueItems\n .filter(vueItem => !mightBeDynamic(vueItem))\n .filter(vueItem => !languageItems.some(languageItem => vueItem.path === languageItem.path))\n .map(vueItem => ({ ...stripBounding(vueItem), language }));\n\n missingKeys.push(...missingKeysInLanguage);\n }\n\n if (detect.includes(DetectionType.Unused)) {\n const unusedKeysInLanguage = languageItems\n .filter(languageItem => !vueItems.some(vueItem => languageItem.path === vueItem.path || languageItem.path.startsWith(vueItem.path + '.')))\n .map(languageItem => ({ ...languageItem, language }));\n\n unusedKeys.push(...unusedKeysInLanguage);\n }\n });\n\n return {\n missingKeys,\n unusedKeys,\n maybeDynamicKeys,\n };\n}\n\nexport async function writeReportToFile (report: I18NReport, writePath: string): Promise<NodeJS.ErrnoException | void> {\n const reportString = JSON.stringify(report, null, 2);\n return new Promise((resolve, reject) => {\n fs.writeFile(\n writePath,\n reportString,\n (err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n },\n );\n });\n}\n\n","import path from 'path';\nimport { ReportOptions, I18NReport, DetectionType } from '../types';\nimport { readVueFiles, extractI18NItemsFromVueFiles } from './vue-files';\nimport { readLanguageFiles, extractI18NLanguageFromLanguageFiles, removeUnusedFromLanguageFiles, writeMissingToLanguageFiles } from './language-files';\nimport { extractI18NReport, writeReportToFile } from './report';\nimport Dot from 'dot-object';\n\nexport async function createI18NReport (options: ReportOptions): Promise<I18NReport> {\n const {\n vueFiles: vueFilesGlob,\n languageFiles: languageFilesGlob,\n output,\n add,\n remove,\n exclude = [],\n ci,\n separator,\n noEmptyTranslation = '',\n missingTranslationString = '',\n detect = [DetectionType.Missing, DetectionType.Unused, DetectionType.Dynamic]\n } = options;\n\n if (!vueFilesGlob) throw new Error('Required configuration vueFiles is missing.');\n if (!languageFilesGlob) throw new Error('Required configuration languageFiles is missing.');\n\n let issuesToDetect = Array.isArray(detect) ? detect : [detect];\n const invalidDetectOptions = issuesToDetect.filter(item => !Object.values(DetectionType).includes(item));\n if (invalidDetectOptions.length) {\n throw new Error(`Invalid 'detect' value(s): ${invalidDetectOptions}`);\n }\n\n const dot = typeof separator === 'string' ? new Dot(separator) : Dot;\n const vueFiles = readVueFiles(path.resolve(process.cwd(), vueFilesGlob));\n const languageFiles = readLanguageFiles(path.resolve(process.cwd(), languageFilesGlob));\n\n const I18NItems = extractI18NItemsFromVueFiles(vueFiles);\n const I18NLanguage = extractI18NLanguageFromLanguageFiles(languageFiles, dot);\n\n const report = extractI18NReport(I18NItems, I18NLanguage, issuesToDetect);\n\n report.unusedKeys = report.unusedKeys.filter(key =>\n !exclude.filter(excluded => key.path.startsWith(excluded)).length)\n\n if (report.missingKeys.length) console.info('\\nMissing Keys'), console.table(report.missingKeys);\n if (report.unusedKeys.length) console.info('\\nUnused Keys'), console.table(report.unusedKeys);\n if (report.maybeDynamicKeys.length) console.warn('\\nSuspected Dynamic Keys Found\\nvue-i18n-extract does not compile Vue templates and therefore can not infer the correct key for the following keys.'), console.table(report.maybeDynamicKeys);\n\n if (output) {\n await writeReportToFile(report, path.resolve(process.cwd(), output));\n console.info(`\\nThe report has been has been saved to ${output}`);\n }\n\n if (remove && report.unusedKeys.length) {\n removeUnusedFromLanguageFiles(languageFiles, report.unusedKeys, dot);\n console.info('\\nThe unused keys have been removed from your language files.');\n }\n\n if (add && report.missingKeys.length) {\n writeMissingToLanguageFiles(languageFiles, report.missingKeys, dot, noEmptyTranslation, missingTranslationString);\n console.info('\\nThe missing keys have been added to your language files.');\n }\n\n if (ci && report.missingKeys.length) {\n throw new Error(`${report.missingKeys.length} missing keys found.`);\n }\n\n if (ci && report.unusedKeys.length) {\n throw new Error(`${report.unusedKeys.length} unused keys found.`);\n }\n\n return report;\n}\n\nexport * from './vue-files';\nexport * from './language-files';\nexport * from './report';\n","export * from './config-file';\nexport * from './create-report';\nexport * from './types';\n\nprocess.on('uncaughtException', (err) => {\n console.error('[vue-i18n-extract]', err);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (err) => {\n console.error('[vue-i18n-extract]', err);\n process.exit(1);\n});\n"],"names":["vueFiles","languageFiles","exclude","output","add","remove","ci","separator","noEmptyTranslation","missingTranslationString","initCommand","fs","writeFileSync","path","resolve","process","cwd","JSON","stringify","defaultConfig","resolveConfig","argvOptions","cac","parse","argv","run","options","pathToConfigFile","configOptions","require","default","console","info","Array","isArray","DetectionType","readVueFiles","src","normalizedSrc","replace","isValidGlob","Error","targetFiles","glob","sync","length","map","f","fileName","content","readFileSync","getMatches","file","regExp","captureGroup","match","exec","pathAtIndex","indexOf","previousCharacter","charAt","nextCharacter","line","substring","index","extractMethodMatches","methodRegExp","extractComponentMatches","componentRegExp","extractDirectiveMatches","directiveRegExp","extractI18NItemsFromVueFiles","sourceFiles","reduce","accumulator","methodMatches","componentMatches","directiveMatches","parseVueFiles","readLanguageFiles","langPath","currentFolder","lastIndexOf","localeIndexFile","extension","toLowerCase","isJSON","isTS","isYAML","langObj","yaml","load","indexFile","cleanContent","keyPrefixRegex","RegExp","keyPrefix","jsonrepair","eval","extractI18NLanguageFromLanguageFiles","dot","Dot","language","flattenedObject","Object","keys","forEach","key","push","writeMissingToLanguageFiles","parsedLanguageFiles","missingKeys","languageFile","languageFileContent","item","includes","addDefaultTranslation","str","writeLanguageFile","removeUnusedFromLanguageFiles","unusedKeys","delete","newLanguageFileContent","fileExtension","filePath","nestedContent","values","stringifiedContent","jsFile","yamlFile","dump","nestedStringifiedContent","unquotedContent","unquotePropertiesFromJSONString","tsFile","stringifiedObject","parselanguageFiles","stripBounding","mightBeDynamic","extractI18NReport","vueItems","detect","maybeDynamicKeys","Dynamic","filter","vueItem","languageItems","Missing","missingKeysInLanguage","some","languageItem","Unused","unusedKeysInLanguage","startsWith","writeReportToFile","report","writePath","reportString","Promise","reject","writeFile","err","createI18NReport","vueFilesGlob","languageFilesGlob","issuesToDetect","invalidDetectOptions","I18NItems","I18NLanguage","excluded","table","warn","on","error","exit"],"mappings":"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