{"version":3,"sources":["/home/runner/work/unplugin-fluent-vue/unplugin-fluent-vue/dist/chunk-IRLTR5UU.cjs","../src/plugins/external-plugin.ts","../src/loader-query.ts","../src/plugins/ftl/inject.ts","../src/plugins/ftl/parse.ts","../src/plugins/sfc-plugin.ts"],"names":["createUnplugin","vitePlugin","rollupPlugin","webpackPlugin"],"mappings":"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","file":"/home/runner/work/unplugin-fluent-vue/unplugin-fluent-vue/dist/chunk-IRLTR5UU.cjs","sourcesContent":[null,"import type { ExternalPluginOptions } from '../types'\nimport { stat as fsStat } from 'node:fs/promises'\nimport { dirname, join, relative } from 'node:path'\n\nimport { createFilter, makeLegalIdentifier } from '@rollup/pluginutils'\nimport MagicString from 'magic-string'\n\nimport { createUnplugin } from 'unplugin'\nimport { isCustomBlock, parseVueRequest } from '../loader-query'\nimport { getInjectFtl } from './ftl/inject'\n\nconst isVue = createFilter(['**/*.vue'])\nconst isFtl = createFilter(['**/*.ftl'])\n\ninterface Dependency {\n  locale: string\n  ftlPath: string\n  relativeFtlPath: string\n  importVariable: string\n}\n\nasync function fileExists(filename: string): Promise<boolean> {\n  try {\n    const stat = await fsStat(filename, { throwIfNoEntry: false } as any)\n    return !!stat\n  }\n  catch {\n    return false\n  }\n}\n\nfunction normalizePath(path: string) {\n  return path.replace(/\\\\/g, '/')\n}\n\nfunction isFluentCustomBlock(id: string) {\n  const request = parseVueRequest(id)\n  return isCustomBlock(request.query, { blockType: 'fluent' })\n}\n\nexport const unplugin = createUnplugin((options: ExternalPluginOptions) => {\n  const resolvedOptions = {\n    checkSyntax: true,\n    parseFtl: false,\n    virtualModuleName: 'virtual:ftl-for-file',\n    getFtlPath: undefined as ((locale: string, vuePath: string) => string) | undefined,\n    ...options,\n  }\n\n  let getFtlPath\n  if ('getFtlPath' in options) {\n    getFtlPath = options.getFtlPath\n  }\n  else {\n    getFtlPath = (locale: string, vuePath: string) => {\n      return join(options.ftlDir, locale, `${relative(options.baseDir, vuePath)}.ftl`)\n    }\n  }\n\n  const getTranslationsForFile = async (id: string) => {\n    const dependencies: Dependency[] = []\n    for (const locale of options.locales) {\n      const ftlPath = normalizePath(getFtlPath(locale, id))\n      const ftlExists = await fileExists(ftlPath)\n      let relativeFtlPath = normalizePath(relative(dirname(id), ftlPath))\n      if (!relativeFtlPath.startsWith('.'))\n        relativeFtlPath = `./${relativeFtlPath}`\n\n      if (ftlExists) {\n        dependencies.push({\n          locale,\n          ftlPath,\n          relativeFtlPath,\n          importVariable: `${makeLegalIdentifier(locale)}_ftl`,\n        })\n      }\n    }\n\n    return dependencies\n  }\n\n  return {\n    name: 'unplugin-fluent-vue-external',\n    enforce: 'pre',\n    resolveId(id, importer) {\n      if (id === resolvedOptions.virtualModuleName)\n        return `${id}?importer=${importer}`\n    },\n    loadInclude(id: string) {\n      return id.startsWith(resolvedOptions.virtualModuleName)\n    },\n    async load(id) {\n      if (!id.startsWith(resolvedOptions.virtualModuleName))\n        return\n\n      const importer = id.split('?importer=')[1]\n\n      const translations = await getTranslationsForFile(importer)\n\n      for (const { ftlPath } of translations)\n        this.addWatchFile(ftlPath)\n\n      let code = ''\n      for (const { ftlPath, importVariable } of translations)\n        code += `import ${importVariable} from '${ftlPath}';\\n`\n\n      code += `export default { ${translations\n        .map(({ locale, importVariable }) => `'${locale}': ${importVariable}`)\n        .join(', ')} }\\n`\n\n      return code\n    },\n    transformInclude(id: string) {\n      return isVue(id) || isFtl(id) || isFluentCustomBlock(id)\n    },\n    async transform(source: string, id: string) {\n      if (isVue(id)) {\n        const magic = new MagicString(source, { filename: id })\n\n        const translations = await getTranslationsForFile(id)\n\n        if (translations.length === 0)\n          return\n\n        for (const { relativeFtlPath, locale } of translations)\n          magic.append(`<fluent locale=\"${locale}\" src=\"${relativeFtlPath}\"></fluent>\\n`)\n\n        return {\n          code: magic.toString(),\n          map: magic.generateMap(),\n        }\n      }\n\n      if (isFtl(id)) {\n        const injectFtl = getInjectFtl(resolvedOptions, true)\n        const result = injectFtl`\nexport default ${source}\n`\n\n        if (result.error)\n          this.error(result.error)\n\n        return result.code\n      }\n\n      const query = parseVueRequest(id).query\n      if (isFluentCustomBlock(id)) {\n        const injectFtl = getInjectFtl(resolvedOptions)\n        const result = injectFtl`\nexport default function (Component) {\n  const target = Component.options || Component\n  target.fluent = target.fluent || {}\n  target.fluent['${query.locale}'] = ${source}\n}\n`\n\n        if (result.error)\n          this.error(result.error)\n\n        return result.code\n      }\n\n      return undefined\n    },\n  }\n})\n\nexport const vitePlugin = unplugin.vite\nexport const rollupPlugin = unplugin.rollup\nexport const webpackPlugin = unplugin.webpack\n","export interface VueQuery {\n  vue?: boolean\n  type?: 'script' | 'template' | 'style' | 'custom' | 'fluent'\n  blockType?: string\n  index?: number\n  locale?: string\n}\n\nexport function parseVueRequest(id: string) {\n  const [filename, rawQuery] = id.split('?', 2)\n  const params = new URLSearchParams(rawQuery)\n  const ret: VueQuery = {}\n\n  ret.vue = params.has('vue')\n\n  if (params.has('type'))\n    ret.type = params.get('type') as VueQuery['type']\n\n  if (params.has('blockType'))\n    ret.blockType = params.get('blockType') ?? undefined\n\n  if (params.has('index'))\n    ret.index = Number(params.get('index'))\n\n  if (params.has('locale'))\n    ret.locale = params.get('locale') ?? undefined\n\n  return {\n    filename,\n    query: ret,\n  }\n}\n\nexport function isCustomBlock(query: VueQuery, options: { blockType: string }): boolean {\n  return (\n    'vue' in query\n    && (query.type === 'custom' // for vite (@vite-plugin-vue)\n      || query.type === options.blockType // for webpack (vue-loader)\n      || query.blockType === options.blockType) // for webpack (vue-loader)\n  )\n}\n","import type { SourceMap } from 'magic-string'\nimport { FluentResource } from '@fluent/bundle'\nimport MagicString from 'magic-string'\nimport { getSyntaxErrors } from './parse'\n\ntype InjectFtlFn = (template: TemplateStringsArray, locale?: string, source?: string) => { code?: { code: string, map: SourceMap }, error?: string }\n\nfunction normalize(str: string) {\n  return str.replace(/\\r\\n/g, '\\n').trim()\n}\n\nexport function getInjectFtl(options: { checkSyntax: boolean, parseFtl: boolean }, addPureAnotation = false): InjectFtlFn {\n  return (template, locale, source) => {\n    if (source == null) {\n      source = locale\n      locale = undefined\n    }\n\n    const pureAnotation = addPureAnotation ? '/*#__PURE__*/ ' : ''\n\n    if (source == null)\n      throw new Error('Missing source')\n\n    if (options.checkSyntax) {\n      const errorsText = getSyntaxErrors(normalize(source))\n\n      if (errorsText) {\n        return {\n          error: errorsText,\n        }\n      }\n    }\n\n    const magic = new MagicString(source)\n    const importString = options.parseFtl === true ? '' : '\\nimport { FluentResource } from \\'@fluent/bundle\\'\\n'\n\n    if (source.length === 0) {\n      magic.append('{\"body\":[]}')\n    }\n    else if (options.parseFtl === true) {\n      const resource = new FluentResource(normalize(source))\n      magic.overwrite(0, source.length, JSON.stringify(resource))\n    }\n    else {\n      magic.overwrite(0, source.length, `${pureAnotation}new FluentResource(${JSON.stringify(normalize(source))})`)\n    }\n\n    if (template.length === 2) {\n      magic.prepend(importString + template[0])\n      magic.append(template[1])\n    }\n    else if (template.length === 3) {\n      magic.prepend(importString + template[0] + locale + template[1])\n      magic.append(template[2])\n    }\n\n    return {\n      code: {\n        code: magic.toString(),\n        map: magic.generateMap(),\n      },\n    }\n  }\n}\n","import type { Junk } from '@fluent/syntax'\nimport { columnOffset, lineOffset, parse } from '@fluent/syntax'\n\nfunction padRight(str: string | number, len: number) {\n  return str + ' '.repeat(len - String(str).length)\n}\n\nconst RANGE = 2\n\n/**\n * Generate a string that highlights the position of the error in the source\n * @param source The source string\n * @param line The line number of the error (1-indexed)\n * @param column The column number of the error (1-indexed)\n * Example:\n * |  proper-key = Value\n * |  key-with-error = error  {->\n * |                            ^\n * |  continuation = Value\n */\nexport function generateCodeFrame(\n  source: string,\n  line: number,\n  column: number,\n): string {\n  const lines = source.split(/\\r?\\n/)\n  const start = Math.max(line - RANGE - 1, 0)\n  const end = Math.min(lines.length, line + RANGE)\n\n  const result = []\n\n  const lineNumberLength = String(end).length\n\n  for (let i = start; i < end; i++) {\n    result.push(`${padRight(i + 1, lineNumberLength)} |  ${lines[i]}`)\n\n    if (i + 1 === line)\n      result.push(`${padRight(' ', lineNumberLength)} |  ${' '.repeat(column - 1)}^`)\n  }\n\n  return result.join('\\n')\n}\n\nexport function getSyntaxErrors(\n  source: string,\n): string | undefined {\n  const parsed = parse(source, { withSpans: true })\n  const junks = parsed.body.filter(x => x.type === 'Junk') as Junk[]\n  const errors = junks.map(x => x.annotations).flat()\n  if (errors.length > 0) {\n    const errorsText = errors.map((x) => {\n      const line = lineOffset(source, x.span.start) + 1\n      const column = columnOffset(source, x.span.start) + 1\n      return `    ${x.code}: ${x.message} (${line}:${column})\n${generateCodeFrame(source, line, column)}`\n    }).join('\\n')\n\n    return `Fluent parse errors:\\n${errorsText}`\n  }\n\n  return undefined\n}\n","import type { VitePlugin } from 'unplugin'\nimport type { SFCPluginOptions } from '../types'\n\nimport { createUnplugin } from 'unplugin'\n\nimport { isCustomBlock, parseVueRequest } from '../loader-query'\nimport { getInjectFtl } from './ftl/inject'\n\nexport const unplugin = createUnplugin((options: SFCPluginOptions, meta) => {\n  const resolvedOptions = {\n    blockType: 'fluent',\n    checkSyntax: true,\n    parseFtl: false,\n    ...options,\n  }\n\n  return {\n    name: 'unplugin-fluent-vue-sfc',\n    enforce: meta.framework === 'webpack' ? 'post' : undefined,\n    transformInclude(id: string) {\n      const { query } = parseVueRequest(id)\n      return isCustomBlock(query, resolvedOptions)\n    },\n    async transform(source: string, id: string) {\n      const { query } = parseVueRequest(id)\n      if (!isCustomBlock(query, resolvedOptions)) {\n        return undefined\n      }\n\n      const locale = query.locale\n      if (locale == null) {\n        this.error('Custom block does not have locale attribute')\n        return\n      }\n\n      // I have no idea why webpack processes this file multiple times\n      if (source.includes('FluentResource') || source.includes('unplugin-fluent-vue-sfc') || source.includes('target.fluent'))\n        return undefined\n\n      const injectFtl = getInjectFtl(resolvedOptions)\n      const result = injectFtl`\nexport default function (Component) {\n  const target = Component.options || Component\n  target.fluent = target.fluent || {}\n  target.fluent['${locale}'] = ${source}\n}\n`\n\n      if (result.error)\n        this.error(result.error)\n\n      return result.code\n    },\n  }\n})\n\nexport const vitePlugin = unplugin.vite as (options?: SFCPluginOptions) => VitePlugin\nexport const rollupPlugin = unplugin.rollup\nexport const webpackPlugin = unplugin.webpack\n"]}