Ctrl K
ComunidadGuías…

nuxt.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import tailwindcss from '@tailwindcss/vite'
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { LOUIS_ENV_BINDINGS, louisRuntimeConfigDefaults } from './shared/louis-env.mjs'

const packageJson = JSON.parse(
  readFileSync(fileURLToPath(new URL('./package.json', import.meta.url)), 'utf8'),
) as { version?: string }

const louisEnv = louisRuntimeConfigDefaults()

/** Injected into nitro.mjs immediately before _sharedRuntimeConfig is frozen. */
const louisEnvAliasPreamble = `(()=>{try{const b=${JSON.stringify(
  LOUIS_ENV_BINDINGS.map(({ louis, nuxt }) => [louis, nuxt]),
)};for(const[l,n]of b){const v=process.env[l];if(v!=null&&String(v).trim()!=="")process.env[n]=String(v).trim()}}catch(_){}})();\n`

/**
 * Nitro emits the server bundle under chunks/nitro/ on macOS/Linux, but often
 * chunks/_/ on Windows (Rollup getChunkName path-separator mismatch).
 */
function resolveNitroBundlePath(outputDir: string): string | null {
  const chunksDir = join(outputDir, 'server/chunks')
  const preferred = [
    join(chunksDir, 'nitro/nitro.mjs'),
    join(chunksDir, '_/nitro.mjs'),
  ]
  for (const candidate of preferred) {
    if (existsSync(candidate)) return candidate
  }
  if (!existsSync(chunksDir)) return null
  for (const entry of readdirSync(chunksDir, { withFileTypes: true })) {
    if (!entry.isDirectory()) continue
    const candidate = join(chunksDir, entry.name, 'nitro.mjs')
    if (existsSync(candidate)) return candidate
  }
  return null
}

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: '2025-07-15',
  devtools: { enabled: false },
  nitro: {
    hooks: {
      compiled(nitro) {
        // Must patch the nitro chunk: index.mjs import hoisting would run freeze before a preamble.
        const nitroPath = resolveNitroBundlePath(nitro.options.output.dir)
        if (!nitroPath) {
          nitro.logger.warn('[louis-env] could not find nitro.mjs to inject LOUIS_* alias')
          return
        }
        const source = readFileSync(nitroPath, 'utf8')
        const marker = 'const _sharedRuntimeConfig'
        if (source.includes('/*louis-env-alias*/')) return
        const idx = source.indexOf(marker)
        if (idx === -1) {
          nitro.logger.warn('[louis-env] could not find _sharedRuntimeConfig to inject LOUIS_* alias')
          return
        }
        writeFileSync(
          nitroPath,
          `${source.slice(0, idx)}/*louis-env-alias*/${louisEnvAliasPreamble}${source.slice(idx)}`,
        )
      },
    },
  },
  // Production builds don't need client sourcemaps; disabling avoids noisy
  // SOURCEMAP_BROKEN warnings from @tailwindcss/vite and nuxt internals.
  sourcemap: false,
  css: ['./app/assets/css/main.css'],
  app: {
    head: {
      title: 'Louis',
      link: [
        { rel: 'icon', href: '/favicons/favicon.ico', sizes: 'any' },
        { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicons/favicon-32x32.png' },
        { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicons/favicon-16x16.png' },
        { rel: 'apple-touch-icon', sizes: '180x180', href: '/favicons/apple-touch-icon.png' },
        { rel: 'manifest', href: '/favicons/manifest.json' },
      ],
      meta: [
        {
          name: 'viewport',
          content: 'width=device-width, initial-scale=1, viewport-fit=cover',
        },
        { name: 'theme-color', content: '#fff5f0' },
        { name: 'mobile-web-app-capable', content: 'yes' },
        { name: 'apple-mobile-web-app-capable', content: 'yes' },
        { name: 'apple-mobile-web-app-title', content: 'Louis' },
        {
          name: 'apple-mobile-web-app-status-bar-style',
          content: 'default',
        },
        { name: 'application-name', content: 'Louis' },
      ],
      // Paint splash ground before Vue boots so the app never flashes underneath.
      style: [
        {
          key: 'app-splash-pending',
          textContent:
            'html.app-splash-pending{background:#fff5f0}'
            + 'html.app-splash-pending body{background:#fff5f0}'
            + 'html.app-splash-pending::before{content:"";position:fixed;inset:0;z-index:114;background:#fff5f0;pointer-events:none}',
        },
      ],
      script: [
        {
          key: 'app-vvh',
          textContent:
            '(function(){try{var h=(window.visualViewport&&window.visualViewport.height)||window.innerHeight;'
            + 'if(h>0)document.documentElement.style.setProperty("--app-vvh",Math.round(h)+"px");'
            + '}catch(e){}})();',
        },
        {
          key: 'app-splash-pending',
          // Runs before body parse; mirrors useAppSplash session/debug rules.
          // TEMP: `force=true` matches FORCE_SPLASH_EVERY_REFRESH in useAppSplash.
          textContent:
            '(function(){try{var d=document.documentElement;'
            + 'var force=true;'
            + 'var debug=new URLSearchParams(location.search).get("splash")==="debug";'
            + 'var seen=sessionStorage.getItem("louis.splash.seen")==="1";'
            + 'var reduced=window.matchMedia("(prefers-reduced-motion: reduce)").matches;'
            + 'if(force||debug||(!seen&&!reduced))d.classList.add("app-splash-pending");'
            + '}catch(e){document.documentElement.classList.add("app-splash-pending")}})();',
        },
      ],
    },
  },
  components: [
    {
      path: '~/components/ui',
      pathPrefix: false,
    },
    {
      path: '~/components',
      ignore: ['ui'],
    },
  ],
  devServer: {
    port: 4000,
  },
  // Prefer LOUIS_* at runtime (see .env.example); legacy NUXT_* still works.
  // Production: nitro compiled hook aliases LOUIS_* → NUXT_* in nitro.mjs before freeze.
  runtimeConfig: {
    youtubeApiKey: louisEnv.youtubeApiKey,
    youtubeSafeSearch: louisEnv.youtubeSafeSearch || 'moderate',
    yotoClientId: louisEnv.yotoClientId,
    yotoClientSecret: louisEnv.yotoClientSecret,
    yotoRedirectUri: louisEnv.yotoRedirectUri,
    ytdlpPath: louisEnv.ytdlpPath,
    // Optional Netscape cookies.txt for yt-dlp (LOUIS_YTDLP_COOKIES_FILE). Anon-first; used on escalate.
    ytdlpCookiesFile: louisEnv.ytdlpCookiesFile,
    /** yt-dlp `--js-runtimes` value (`node` or `node:/abs/shim` on desktop). */
    ytdlpJsRuntime: louisEnv.ytdlpJsRuntime,
    audioWorkDir: louisEnv.audioWorkDir,
    audioJobMaxAgeMs: louisEnv.audioJobMaxAgeMs,
    audioCacheMaxAgeMs: louisEnv.audioCacheMaxAgeMs,
    audioCacheMaxBytes: louisEnv.audioCacheMaxBytes,
    enableDebugRoutes: louisEnv.enableDebugRoutes,
    public: {
      appVersion: packageJson.version || '0.0.0',
      /** Set LOUIS_PUBLIC_DESKTOP=1 by the Electron host when spawning Nitro. */
      desktop: louisEnv.publicDesktop,
    },
  },
  vite: {
    plugins: [
      tailwindcss(),
      {
        name: 'suppress-sourcemap-broken-warnings',
        apply: 'build',
        configResolved(config) {
          const previous = config.build.rollupOptions.onwarn
          config.build.rollupOptions.onwarn = (warning, warn) => {
            if (warning.code === 'SOURCEMAP_BROKEN') return
            if (previous) previous(warning, warn)
            else warn(warning)
          }
        },
      },
    ],
    build: {
      sourcemap: false,
    },
  },
})