UNPKG

@hi18n/cli

Version:

Message internationalization meets immutability and type-safety - command line tool

282 lines (226 loc) 9.41 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.sync = sync; var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js")); var _utils = require("@typescript-eslint/utils"); var _nodeFs = _interopRequireDefault(require("node:fs")); var _glob = _interopRequireDefault(require("glob")); var _nodePath = _interopRequireDefault(require("node:path")); var _nodeUtil = _interopRequireDefault(require("node:util")); var _resolve = _interopRequireDefault(require("resolve")); var _eslintPlugin = require("@hi18n/eslint-plugin"); var _config = require("./config"); async function sync(options) { var _config$include, _config$exclude; const { cwd: projectPath, include: includeFromOpt, exclude: excludeFromOpt } = options; const config = await (0, _config.loadConfig)(projectPath); const include = (_config$include = config.include) !== null && _config$include !== void 0 ? _config$include : includeFromOpt; const exclude = (_config$exclude = config.exclude) !== null && _config$exclude !== void 0 ? _config$exclude : excludeFromOpt; if (include === undefined || include.length === 0) { throw new Error("No include specified"); } const linterConfig = { parser: config.parser, parserOptions: config.parserOptions }; const linter = new _utils.TSESLint.Linter({ cwd: projectPath }); const translationUsages = []; linter.defineRule("@hi18n/collect-translation-ids", _eslintPlugin.rules["collect-translation-ids"]); const bookDefs = []; linter.defineRule("@hi18n/collect-book-definitions", _eslintPlugin.rules["collect-book-definitions"]); const catalogDefs = []; linter.defineRule("@hi18n/collect-catalog-definitions", _eslintPlugin.rules["collect-catalog-definitions"]); linter.defineRule("@hi18n/no-missing-translation-ids", _eslintPlugin.rules["no-missing-translation-ids"]); linter.defineRule("@hi18n/no-unused-translation-ids", _eslintPlugin.rules["no-unused-translation-ids"]); linter.defineRule("@hi18n/no-missing-translation-ids-in-types", _eslintPlugin.rules["no-missing-translation-ids-in-types"]); linter.defineRule("@hi18n/no-unused-translation-ids-in-types", _eslintPlugin.rules["no-unused-translation-ids-in-types"]); const files = []; for (const includeGlob of include) { (0, _push.default)(files).call(files, ...(await _nodeUtil.default.promisify(_glob.default)(includeGlob, { cwd: projectPath, nodir: true, ignore: exclude }))); } for (const relative of files) { const filename = _nodePath.default.join(projectPath, relative); const source = await _nodeFs.default.promises.readFile(filename, "utf-8"); const messages = linter.verify(source, { ...linterConfig, rules: { "@hi18n/collect-translation-ids": ["error", u => { (0, _push.default)(translationUsages).call(translationUsages, u); }], "@hi18n/collect-book-definitions": ["error", b => { (0, _push.default)(bookDefs).call(bookDefs, b); }], "@hi18n/collect-catalog-definitions": ["error", c => { (0, _push.default)(catalogDefs).call(catalogDefs, c); }] } }, { filename }); checkMessages(relative, messages); } const linkage = {}; const usedTranslationIds = {}; const rewriteTargetFiles = new Set(); for (const u of translationUsages) { const loc = u.bookLocation; if (loc.path !== undefined) { const { resolved } = await resolveWithFallback(removeExtension(loc.path, config.extensionsToRemove), { basedir: _nodePath.default.dirname(loc.base), extensions: config.extensions }, config.baseUrl, config.paths); loc.path = resolved; } const locName = (0, _eslintPlugin.serializeReference)(loc); if (hasOwn(usedTranslationIds, locName)) { var _context; (0, _push.default)(_context = usedTranslationIds[locName]).call(_context, u.id); } else { setRecordValue(usedTranslationIds, locName, [u.id]); } rewriteTargetFiles.add(loc.path !== undefined ? loc.path : loc.base); } for (const bookDef of bookDefs) { const bookLocNames = (0, _eslintPlugin.serializedLocations)(bookDef.bookLocation); const concatenatedTranslationIds = bookLocNames.flatMap(locName => hasOwn(usedTranslationIds, locName) ? usedTranslationIds[locName] : []); const uniqueTranslationIds = Array.from(new Set(concatenatedTranslationIds)).sort(); for (const locName of bookLocNames) { setRecordValue(usedTranslationIds, locName, uniqueTranslationIds); } const primaryName = bookLocNames[0]; for (const catalogLink of bookDef.catalogLinks) { const loc = catalogLink.catalogLocation; if (loc.path !== undefined) { const { resolved } = await resolveWithFallback(removeExtension(loc.path, config.extensionsToRemove), { basedir: _nodePath.default.dirname(loc.base), extensions: config.extensions }, config.baseUrl, config.paths); loc.path = resolved; } setRecordValue(linkage, (0, _eslintPlugin.serializeReference)(loc), primaryName); rewriteTargetFiles.add(loc.path !== undefined ? loc.path : loc.base); } rewriteTargetFiles.add(bookDef.bookLocation.path); } const valueHints = {}; // Set up passive importing if (config.connector) { const c = config.connector.connector(config.configPath, config.connectorOptions); if (c.importData) { const data = await c.importData(); for (const [locale, catalog] of Object.entries(data.translations)) { var _valueHints$locale; const vhCatalog = (_valueHints$locale = valueHints[locale]) !== null && _valueHints$locale !== void 0 ? _valueHints$locale : valueHints[locale] = {}; for (const [id, msg] of Object.entries(catalog)) { var _vhCatalog$id; (_vhCatalog$id = vhCatalog[id]) !== null && _vhCatalog$id !== void 0 ? _vhCatalog$id : vhCatalog[id] = msg.raw; } } } } for (const rewriteTargetFile of Array.from(rewriteTargetFiles).sort()) { const source = await _nodeFs.default.promises.readFile(rewriteTargetFile, "utf-8"); const report = linter.verifyAndFix(source, { ...linterConfig, rules: { "@hi18n/no-missing-translation-ids": "warn", "@hi18n/no-unused-translation-ids": "warn", "@hi18n/no-missing-translation-ids-in-types": "warn", "@hi18n/no-unused-translation-ids-in-types": "warn" }, settings: { "@hi18n/linkage": linkage, "@hi18n/used-translation-ids": usedTranslationIds, "@hi18n/value-hints": valueHints } }, { filename: rewriteTargetFile }); checkMessages(rewriteTargetFile, report.messages); if (report.fixed) { if (options.checkOnly) { throw new Error(`Found diff in ${_nodePath.default.relative(projectPath, rewriteTargetFile)}`); } await _nodeFs.default.promises.writeFile(rewriteTargetFile, report.output, "utf-8"); } } } function checkMessages(filepath, messages) { for (const message of messages) { if (/^Definition for rule .* was not found\.$/.test(message.message)) { // We load ESLint with minimal rules. Ignore the "missing rule" error. continue; } if (message.severity >= 2) throw new Error(`Error on ${filepath}: ${message.message}`); } } function removeExtension(id, extensionsToRemove) { for (const ext of extensionsToRemove) { if (id.endsWith(ext)) { return id.substring(0, id.length - ext.length); } } return id; } async function resolveWithFallback(id, opts, baseUrl, paths) { if (baseUrl && isPackageLikePath(id)) { const matchers = Object.entries(paths !== null && paths !== void 0 ? paths : {}); (0, _push.default)(matchers).call(matchers, ["*", ["*"]]); for (const [matcher, candidates] of matchers) { let replacement = undefined; if (id === matcher) { replacement = ""; } else if (matcher.endsWith("*") && id.startsWith(matcher.substring(0, matcher.length - 1))) { replacement = id.substring(matcher.length - 1); } if (replacement === undefined) continue; for (const candidate of candidates) { try { return await resolveAsPromise(_nodePath.default.resolve(baseUrl, candidate.replace("*", replacement)), opts); } catch (_e) {// Likely MODULE_NOT_FOUND } } } } return await resolveAsPromise(id, opts); } function resolveAsPromise(id, opts) { return new Promise((resolvePromise, rejectPromise) => { (0, _resolve.default)(id, opts, (err, resolved, pkg) => { if (err) rejectPromise(err);else resolvePromise({ resolved: resolved, pkg }); }); }); } function isPackageLikePath(p) { const firstSegment = p.split(/[/\\]/)[0]; return firstSegment !== "." && firstSegment !== ".." && !_nodePath.default.isAbsolute(p); } function hasOwn(record, key) { return Object.prototype.hasOwnProperty.call(record, key); } function setRecordValue(record, key, value) { Object.defineProperty(record, key, { value, writable: true, configurable: true, enumerable: true }); } //# sourceMappingURL=sync.js.map