'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const unplugin$1 = require('unplugin'); const pathe = require('pathe'); const createDebug = require('debug'); const fg = require('fast-glob'); const shared = require('@intlify/shared'); const pluginutils = require('@rollup/pluginutils'); const bundleUtils = require('@intlify/bundle-utils'); const compilerSfc = require('@vue/compiler-sfc'); const JSON5 = require('json5'); const yaml = require('js-yaml'); const fs = require('fs'); const pc = require('picocolors'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const createDebug__default = /*#__PURE__*/_interopDefaultCompat(createDebug); const fg__default = /*#__PURE__*/_interopDefaultCompat(fg); const JSON5__default = /*#__PURE__*/_interopDefaultCompat(JSON5); const yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml); const pc__default = /*#__PURE__*/_interopDefaultCompat(pc); function parseVueRequest(id) { const [filename, rawQuery] = id.split(`?`, 2); const params = new URLSearchParams(rawQuery); const ret = {}; const langPart = Object.keys(Object.fromEntries(params)).find( (key) => /lang\./i.test(key) ); ret.vue = params.has("vue"); ret.global = params.has("global"); ret.src = params.has("src"); ret.raw = params.has("raw"); if (params.has("type")) { ret.type = params.get("type"); } if (params.has("blockType")) { ret.blockType = params.get("blockType"); } if (params.has("index")) { ret.index = Number(params.get("index")); } if (params.has("locale")) { ret.locale = params.get("locale"); } if (langPart) { const [, lang] = langPart.split("."); ret.lang = lang; } else if (params.has("lang")) { ret.lang = params.get("lang"); } if (params.has("issuerPath")) { ret.issuerPath = params.get("issuerPath"); } return { filename, query: ret }; } function createBridgeCodeGenerator(source, query) { return () => { const data = convert(source, query.lang); let value = JSON.parse(data); if (shared.isString(query.locale)) { value = Object.assign({}, { [query.locale]: value }); } return JSON.stringify(value).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/\\/g, "\\\\").replace(/\u0027/g, "\\u0027"); }; } function convert(source, lang) { switch (lang) { case "yaml": case "yml": const data = yaml__default.load(source); return JSON.stringify(data, void 0, " "); case "json5": return JSON.stringify(JSON5__default.parse(source)); default: return source; } } function warn(...args) { console.warn(pc__default.yellow(pc__default.bold(`[unplugin-vue-i18n] `)), ...args); } function error(...args) { console.error(pc__default.red(pc__default.bold(`[unplugin-vue-i18n] `)), ...args); } async function getRaw(path) { return fs.promises.readFile(path, { encoding: "utf-8" }); } function raiseError(message) { throw new Error(`[unplugin-vue-i18n] ${message}`); } const INTLIFY_BUNDLE_IMPORT_ID = "@intlify/unplugin-vue-i18n/messages"; const VIRTUAL_PREFIX = "\0"; const debug = createDebug__default("unplugin-vue-i18n"); const installedPkg = bundleUtils.checkInstallPackage("@intlify/unplugin-vue-i18n", debug); const installedVueI18nBridge = bundleUtils.checkVueI18nBridgeInstallPackage(debug); const vueI18nVersion = bundleUtils.getVueI18nVersion(debug); if (vueI18nVersion === "8") { warn(`vue-i18n@8 is not supported, since sinece Vue 2 was EOL on 2023.`); } const unplugin = unplugin$1.createUnplugin((options = {}, meta) => { debug("plugin options:", options, meta.framework); if (!["vite", "webpack"].includes(meta.framework)) { raiseError(`This plugin is supported 'vite' and 'webpack' only`); } let onlyLocales = []; if (options.onlyLocales) { onlyLocales = Array.isArray(options.onlyLocales) ? options.onlyLocales : [options.onlyLocales]; } let include = options.include; let exclude = null; if (include) { if (shared.isArray(include)) { include = include.map((item) => pathe.normalize(item)); } else if (shared.isString(include)) { include = pathe.normalize(include); } } else { exclude = "**/**"; } const filter = pluginutils.createFilter(include, exclude); const forceStringify = !!options.forceStringify; const defaultSFCLang = shared.isString(options.defaultSFCLang) ? options.defaultSFCLang : "json"; const globalSFCScope = !!options.globalSFCScope; const useClassComponent = !!options.useClassComponent; const bridge = !!options.bridge; debug("bridge", bridge); if (bridge) { warn( `'bridge' option is deprecated, since Vue 2 was EOL on 2023. that option will be removed in 4.0.` ); } const legacy = !!options.legacy; debug("legacy", legacy); if (legacy) { warn( `'legacy' option is deprecated, since Vue 2 was EOL on 2023. that option will be removed in 4.0.` ); } const vueVersion = shared.isString(options.vueVersion) ? options.vueVersion : void 0; if (vueVersion) { warn( `'vueVersion' option is deprecated, since Vue 2 was EOL on 2023. that option will be removed in 4.0.` ); } const runtimeOnly = shared.isBoolean(options.runtimeOnly) ? options.runtimeOnly : true; debug("runtimeOnly", runtimeOnly); const jitCompilation = shared.isBoolean(options.jitCompilation) ? options.jitCompilation : true; debug("jitCompilation", jitCompilation); const dropMessageCompiler = jitCompilation ? !!options.dropMessageCompiler : false; debug("dropMessageCompiler", dropMessageCompiler); const compositionOnly = installedPkg === "vue-i18n" ? shared.isBoolean(options.compositionOnly) ? options.compositionOnly : true : true; debug("compositionOnly", compositionOnly); const fullInstall = installedPkg === "vue-i18n" ? shared.isBoolean(options.fullInstall) ? options.fullInstall : true : false; debug("fullInstall", fullInstall); const ssrBuild = !!options.ssr; debug("ssr", ssrBuild); const useVueI18nImportName = options.useVueI18nImportName; if (useVueI18nImportName != null) { warn(`'useVueI18nImportName' option is experimental`); } debug("useVueI18nImportName", useVueI18nImportName); const getVueI18nAliasName = () => vueI18nVersion === "9" || vueI18nVersion === "8" ? "vue-i18n" : vueI18nVersion === "unknown" && installedPkg === "petite-vue-i18n" && shared.isBoolean(useVueI18nImportName) && useVueI18nImportName ? "vue-i18n" : installedPkg; const getVueI18nBridgeAliasPath = () => `vue-i18n-bridge/dist/vue-i18n-bridge.runtime.esm-bundler.js`; const getVueI18nAliasPath = (aliasName, { ssr = false, runtimeOnly: runtimeOnly2 = false }) => { return vueI18nVersion === "8" ? `${aliasName}/dist/${aliasName}.esm.js` : `${aliasName}/dist/${installedPkg}${runtimeOnly2 ? ".runtime" : ""}.${!ssr ? "esm-bundler.js" : "node.mjs"}`; }; const esm = shared.isBoolean(options.esm) ? options.esm : true; debug("esm", esm); const allowDynamic = !!options.allowDynamic; debug("allowDynamic", allowDynamic); const strictMessage = shared.isBoolean(options.strictMessage) ? options.strictMessage : true; debug("strictMessage", strictMessage); const escapeHtml = !!options.escapeHtml; debug("escapeHtml", escapeHtml); let isProduction = false; let sourceMap = false; const vueI18nAliasName = getVueI18nAliasName(); return { name: "unplugin-vue-i18n", /** * NOTE: * * For vite, If we have json (including SFC's custom block), * transform it first because it will be transformed into javascript code by `vite:json` plugin. * * For webpack, This plugin will handle with ‘post’, because vue-loader generate the request query. */ enforce: meta.framework === "vite" ? "pre" : "post", vite: { config(config, { command }) { config.resolve = normalizeConfigResolveAlias( config.resolve, meta.framework ); if (command === "build") { debug(`vue-i18n alias name: ${vueI18nAliasName}`); if (shared.isArray(config.resolve.alias)) { config.resolve.alias.push({ find: vueI18nAliasName, replacement: getVueI18nAliasPath(vueI18nAliasName, { ssr: ssrBuild, runtimeOnly }) }); if (installedVueI18nBridge) { config.resolve.alias.push({ find: "vue-i18n-bridge", replacement: getVueI18nBridgeAliasPath() }); } } else if (shared.isObject(config.resolve.alias)) { config.resolve.alias[vueI18nAliasName] = getVueI18nAliasPath(vueI18nAliasName, { ssr: ssrBuild, runtimeOnly }); if (installedVueI18nBridge) { config.resolve.alias["vue-i18n-bridge"] = getVueI18nBridgeAliasPath(); } } debug( `set ${vueI18nAliasName} runtime only: ${getVueI18nAliasPath( vueI18nAliasName, { ssr: ssrBuild, runtimeOnly } )}` ); if (installedVueI18nBridge) { debug( `set vue-i18n-bridge runtime only: ${getVueI18nBridgeAliasPath()}` ); } } else if (command === "serve" && installedPkg === "petite-vue-i18n" && useVueI18nImportName) { config.resolve = normalizeConfigResolveAlias( config.resolve, meta.framework ); if (shared.isArray(config.resolve.alias)) { config.resolve.alias.push({ find: vueI18nAliasName, replacement: `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js` }); } else { config.resolve.alias[vueI18nAliasName] = `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`; } debug(`petite-vue-i18n alias name: ${vueI18nAliasName}`); } config.define = config.define || {}; config.define["__VUE_I18N_LEGACY_API__"] = !compositionOnly; debug( `set __VUE_I18N_LEGACY_API__ is '${config.define["__VUE_I18N_LEGACY_API__"]}'` ); config.define["__VUE_I18N_FULL_INSTALL__"] = fullInstall; debug( `set __VUE_I18N_FULL_INSTALL__ is '${config.define["__VUE_I18N_FULL_INSTALL__"]}'` ); config.define["__INTLIFY_JIT_COMPILATION__"] = jitCompilation; debug( `set __INTLIFY_JIT_COMPILATION__ is '${config.define["__INTLIFY_JIT_COMPILATION__"]}'` ); config.define["__INTLIFY_DROP_MESSAGE_COMPILER__"] = dropMessageCompiler; debug( `set __INTLIFY_DROP_MESSAGE_COMPILER__ is '${config.define["__INTLIFY_DROP_MESSAGE_COMPILER__"]}'` ); config.define["__VUE_I18N_PROD_DEVTOOLS__"] = false; }, configResolved(config) { isProduction = config.isProduction; sourceMap = config.command === "build" ? !!config.build.sourcemap : false; debug( `configResolved: isProduction = ${isProduction}, sourceMap = ${sourceMap}` ); const jsonPlugin = config.plugins.find((p) => p.name === "vite:json"); if (jsonPlugin) { const orgTransform = jsonPlugin.transform; jsonPlugin.transform = async function(code, id) { if (!/\.json$/.test(id) || filter(id)) { return; } const { query } = parseVueRequest(id); if (query.vue) { return; } debug("org json plugin"); return orgTransform.apply(this, [code, id]); }; } const esbuildPlugin = config.plugins.find( (p) => p.name === "vite:esbuild" ); if (esbuildPlugin) { const orgTransform = esbuildPlugin.transform; esbuildPlugin.transform = async function(code, id) { const result = await orgTransform.apply(this, [ code, id ]); if (result == null) { return result; } const { filename, query } = parseVueRequest(id); if (!query.vue && filter(id) && /\.[c|m]?ts$/.test(id)) { const [_code, inSourceMap] = shared.isString(result) ? [result, void 0] : [result.code, result.map]; let langInfo = defaultSFCLang; langInfo = pathe.parse(filename).ext; const generate = getGenerator(langInfo); const parseOptions = getOptions( filename, isProduction, query, sourceMap, { inSourceMap, isGlobal: globalSFCScope, useClassComponent, allowDynamic, strictMessage, escapeHtml, bridge, legacy, vueVersion, jit: jitCompilation, onlyLocales, exportESM: esm, forceStringify } ); debug("parseOptions", parseOptions); const { code: generatedCode, map } = generate( _code, parseOptions, bridge ? createBridgeCodeGenerator(_code, query) : void 0 ); debug("generated code", generatedCode); debug("sourcemap", map, sourceMap); if (_code === generatedCode) return; return { code: generatedCode, // prettier-ignore map: jitCompilation ? { mappings: "" } : sourceMap ? map : { mappings: "" } // eslint-disable-line @typescript-eslint/no-explicit-any }; } else { return result; } }; } }, async handleHotUpdate({ file, server }) { if (/\.(json5?|ya?ml)$/.test(file)) { const module = server.moduleGraph.getModuleById( asVirtualId(INTLIFY_BUNDLE_IMPORT_ID, meta.framework) ); if (module) { server.moduleGraph.invalidateModule(module); return [module]; } } } }, webpack(compiler) { isProduction = compiler.options.mode !== "development"; sourceMap = !!compiler.options.devtool; debug(`webpack: isProduction = ${isProduction}, sourceMap = ${sourceMap}`); compiler.options.resolve = normalizeConfigResolveAlias( compiler.options.resolve, meta.framework ); if (isProduction) { compiler.options.resolve.alias[vueI18nAliasName] = getVueI18nAliasPath(vueI18nAliasName, { ssr: ssrBuild, runtimeOnly }); if (installedVueI18nBridge) { compiler.options.resolve.alias["vue-i18n-bridge"] = getVueI18nBridgeAliasPath(); } debug( `set ${vueI18nAliasName}: ${getVueI18nAliasPath(vueI18nAliasName, { ssr: ssrBuild, runtimeOnly })}` ); if (installedVueI18nBridge) { debug( `set vue-i18n-bridge runtime only: ${getVueI18nBridgeAliasPath()}` ); } } else if (!isProduction && installedPkg === "petite-vue-i18n" && useVueI18nImportName) { compiler.options.resolve.alias[vueI18nAliasName] = `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`; debug(`petite-vue-i18n alias name: ${vueI18nAliasName}`); } loadWebpack().then((webpack) => { if (webpack) { compiler.options.plugins.push( new webpack.DefinePlugin({ __VUE_I18N_LEGACY_API__: JSON.stringify(compositionOnly), __VUE_I18N_FULL_INSTALL__: JSON.stringify(fullInstall), __INTLIFY_PROD_DEVTOOLS__: "false" }) ); debug(`set __VUE_I18N_LEGACY_API__ is '${compositionOnly}'`); debug(`set __VUE_I18N_FULL_INSTALL__ is '${fullInstall}'`); } else { debug("ignore vue-i18n feature flags with webpack.DefinePlugin"); } }); if (compiler.options.module) { compiler.options.module.rules.push({ test: /\.(json5?|ya?ml)$/, type: "javascript/auto", include(resource) { return filter(resource); } }); } }, resolveId(id, importer) { debug("resolveId", id, importer); if (id === INTLIFY_BUNDLE_IMPORT_ID) { return asVirtualId(id, meta.framework); } }, // eslint-disable-next-line @typescript-eslint/no-unused-vars async load(id) { debug("load", id); const { query } = parseVueRequest(id); if (INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework) && include) { let resourcePaths = []; const includePaths = shared.isArray(include) ? include : [include]; for (const inc of includePaths) { resourcePaths = [...resourcePaths, ...await fg__default(inc)]; } resourcePaths = resourcePaths.filter( (el, pos) => resourcePaths.indexOf(el) === pos ); const code = await generateBundleResources( resourcePaths, query, isProduction, { forceStringify, bridge, strictMessage, escapeHtml, exportESM: esm, useClassComponent } ); return { code, map: { mappings: "" } }; } }, transformInclude(id) { debug("transformInclude", id); if (meta.framework === "vite") { return true; } else { const { filename } = parseVueRequest(id); return filename.endsWith("vue") || filename.endsWith(INTLIFY_BUNDLE_IMPORT_ID) || /\.(json5?|ya?ml)$/.test(filename) && filter(filename); } }, async transform(code, id) { const { filename, query } = parseVueRequest(id); debug("transform", id, JSON.stringify(query), filename); let langInfo = defaultSFCLang; let inSourceMap; if (!query.vue) { if (/\.(json5?|ya?ml|[c|m]?js)$/.test(id) && filter(id)) { langInfo = pathe.parse(filename).ext; const generate = getGenerator(langInfo); const parseOptions = getOptions( filename, isProduction, query, sourceMap, { inSourceMap, isGlobal: globalSFCScope, useClassComponent, allowDynamic, strictMessage, escapeHtml, bridge, jit: jitCompilation, onlyLocales, exportESM: esm, forceStringify } ); debug("parseOptions", parseOptions); const { code: generatedCode, map } = generate( code, parseOptions, bridge ? createBridgeCodeGenerator(code, query) : void 0 ); debug("generated code", generatedCode); debug("sourcemap", map, sourceMap); if (code === generatedCode) return; return { code: generatedCode, // prettier-ignore map: jitCompilation ? { mappings: "" } : sourceMap ? map : { mappings: "" } // eslint-disable-line @typescript-eslint/no-explicit-any }; } } else { if (isCustomBlock(query)) { if (shared.isString(query.lang)) { langInfo = query.src ? query.lang === "i18n" ? defaultSFCLang : query.lang : query.lang; } else if (defaultSFCLang) { langInfo = defaultSFCLang; } debug("langInfo", langInfo); const generate = /\.?json5?/.test(langInfo) ? bundleUtils.generateJSON : bundleUtils.generateYAML; const parseOptions = getOptions( filename, isProduction, query, sourceMap, { inSourceMap, isGlobal: globalSFCScope, useClassComponent, bridge, legacy, vueVersion, jit: jitCompilation, strictMessage, escapeHtml, onlyLocales, exportESM: esm, forceStringify } ); debug("parseOptions", parseOptions); const source = await getCode( code, filename, sourceMap, query, meta.framework ); const { code: generatedCode, map } = generate( source, parseOptions, bridge ? createBridgeCodeGenerator(source, query) : void 0 ); debug("generated code", generatedCode); debug("sourcemap", map, sourceMap); if (code === generatedCode) return; return { code: generatedCode, // prettier-ignore map: jitCompilation ? { mappings: "" } : sourceMap ? map : { mappings: "" } // eslint-disable-line @typescript-eslint/no-explicit-any }; } } } }; }); function getGenerator(ext, defaultGen = bundleUtils.generateJSON) { return /\.?json5?$/.test(ext) ? bundleUtils.generateJSON : /\.ya?ml$/.test(ext) ? bundleUtils.generateYAML : /\.([c|m]?js|[c|m]?ts)$/.test(ext) ? bundleUtils.generateJavaScript : defaultGen; } function normalizeConfigResolveAlias(resolve, framework) { if (resolve && resolve.alias) { return resolve; } if (!resolve) { if (framework === "vite") { return { alias: [] }; } else if (framework === "webpack") { return { alias: {} }; } } else if (!resolve.alias) { if (framework === "vite") { resolve.alias = []; return resolve; } else if (framework === "webpack") { resolve.alias = {}; return resolve; } } } async function loadWebpack() { let webpack = null; try { webpack = await import('webpack').then((m) => m.default || m); } catch (e) { warn(`webpack not found, please install webpack.`); } return webpack; } async function generateBundleResources(resources, query, isProduction, { forceStringify = false, isGlobal = false, bridge = false, onlyLocales = [], exportESM = true, strictMessage = true, escapeHtml = false, useClassComponent = false, jit = false }) { const codes = []; for (const res of resources) { debug(`${res} bundle loading ...`); if (/\.(json5?|ya?ml)$/.test(res)) { const { ext, name } = pathe.parse(res); const source = await getRaw(res); const generate = /json5?/.test(ext) ? bundleUtils.generateJSON : bundleUtils.generateYAML; const parseOptions = getOptions(res, isProduction, {}, false, { isGlobal, useClassComponent, bridge, jit, onlyLocales, exportESM, strictMessage, escapeHtml, forceStringify }); parseOptions.type = "bare"; const { code } = generate( source, parseOptions, bridge ? createBridgeCodeGenerator(source, query) : void 0 ); debug("generated code", code); codes.push(`${JSON.stringify(name)}: ${code}`); } } return `const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item); const mergeDeep = (target, ...sources) => { if (!sources.length) return target; const source = sources.shift(); if (isObject(target) && isObject(source)) { for (const key in source) { if (isObject(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); mergeDeep(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } } } return mergeDeep(target, ...sources); } export default mergeDeep({}, ${codes.map((code) => `{${code}}`).join(",\n")} );`; } async function getCode(source, filename, sourceMap, query, framework = "vite") { const { index, issuerPath } = query; if (!shared.isNumber(index)) { raiseError(`unexpected index: ${index}`); } if (framework === "webpack") { if (issuerPath) { debug(`getCode (webpack) ${index} via issuerPath`, issuerPath); return await getRaw(filename); } else { const result = compilerSfc.parse(await getRaw(filename), { sourceMap, filename }); const block = result.descriptor.customBlocks[index]; if (block) { const code = block.src ? await getRaw(block.src) : block.content; debug(`getCode (webpack) ${index} from SFC`, code); return code; } else { return source; } } } else { return source; } } function isCustomBlock(query) { return !shared.isEmptyObject(query) && "vue" in query && (query["type"] === "custom" || // for vite (@vite-plugin-vue) query["type"] === "i18n" || // for webpack (vue-loader) query["blockType"] === "i18n"); } function getOptions(filename, isProduction, query, sourceMap, { inSourceMap = void 0, forceStringify = false, isGlobal = false, bridge = false, legacy = false, vueVersion = "v2.6", onlyLocales = [], exportESM = true, useClassComponent = false, allowDynamic = false, strictMessage = true, escapeHtml = false, jit = false }) { const mode = isProduction ? "production" : "development"; const baseOptions = { filename, sourceMap, inSourceMap, forceStringify, useClassComponent, allowDynamic, strictMessage, escapeHtml, bridge, legacy, vueVersion, jit, onlyLocales, exportESM, env: mode, onWarn: (msg) => { warn(`${filename} ${msg}`); }, onError: (msg, extra) => { const codeFrame = shared.generateCodeFrame( extra?.source || extra?.location?.source || "", extra?.location?.start.column, extra?.location?.end.column ); const errMssage = `${msg} (error code: ${extra?.code}) in ${filename} target message: ${extra?.source} target message path: ${extra?.path} ${codeFrame} `; error(errMssage); throw new Error(errMssage); } }; if (isCustomBlock(query)) { return shared.assign(baseOptions, { type: "sfc", locale: shared.isString(query.locale) ? query.locale : "", isGlobal: isGlobal || !!query.global }); } else { return shared.assign(baseOptions, { type: "plain", isGlobal: false }); } } function getVirtualId(id, framework = "vite") { return framework === "vite" ? id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : "" : id; } function asVirtualId(id, framework = "vite") { return framework === "vite" ? VIRTUAL_PREFIX + id : id; } exports.default = unplugin; exports.unplugin = unplugin;