/** * Copyright (c) Since 2024 InnoShop - All Rights Reserved * * @link https://www.innoshop.com * @author InnoShop * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) * * Vite Configuration for InnoShop * * This config is designed for single-entry builds driven by environment variables, * invoked once per entry by build.js. It does NOT use Vite's multi-input or dev server. * * Environment variables: * BUILD_INPUT — Source file path (e.g. innopacks/front/resources/assets/scss/app.scss) * BUILD_OUTDIR — Output directory (e.g. public/build/front/css) * BUILD_OUTPUT_NAME — Output filename for JS entries (default: 'app') * THEME — Theme name, used to resolve @theme alias (default: 'scandihome') * * Alias resolution: * @front → innopacks/front/resources/ * @theme → themes/{THEME}/ * ~ → node_modules/ (for legacy SCSS imports like ~bootstrap) * * Build modes: * CSS build — Uses rollupOptions.input, outputs unhashed assets via assetFileNames * JS build — Uses lib mode (IIFE), outputs via fileName, build.js renames index.js */ import { defineConfig } from 'vite'; import { resolve } from 'path'; import { pathToFileURL, fileURLToPath } from 'url'; import fs from 'fs'; const theme = process.env.THEME || 'scandihome'; const frontResources = resolve('innopacks/front/resources'); function tildeImporter() { const aliases = { '@front': frontResources, '@theme': resolve(`themes/${theme}`), }; return { canonicalize(url) { if (url.startsWith('~')) { const stripped = url.slice(1); for (const [alias, target] of Object.entries(aliases)) { if (stripped === alias || stripped.startsWith(alias + '/')) { return pathToFileURL(target + stripped.slice(alias.length)); } } return pathToFileURL(resolve('node_modules', stripped)); } if (url.startsWith('node_modules/')) { return pathToFileURL(resolve(url)); } return null; }, load(canonicalUrl) { const filePath = fileURLToPath(canonicalUrl); let resolved = filePath; if (!fs.existsSync(resolved)) { for (const ext of ['.scss', '.sass', '.css']) { if (fs.existsSync(resolved + ext)) { resolved = resolved + ext; break; } const dir = resolved.substring(0, resolved.lastIndexOf('/') + 1); const base = resolved.substring(resolved.lastIndexOf('/') + 1); if (fs.existsSync(dir + '_' + base + ext)) { resolved = dir + '_' + base + ext; break; } } } return { contents: fs.readFileSync(resolved, 'utf-8'), syntax: resolved.endsWith('.sass') ? 'indented' : 'scss' }; }, }; } // Config for single entry build, driven by env vars: // BUILD_INPUT — source file path // BUILD_OUTDIR — output directory const input = process.env.BUILD_INPUT; const outDir = process.env.BUILD_OUTDIR; if (!input || !outDir) { throw new Error('BUILD_INPUT and BUILD_OUTDIR env vars required. Use build.js for full builds.'); } const isJS = input.endsWith('.js'); const outputName = process.env.BUILD_OUTPUT_NAME || 'app'; export default defineConfig({ publicDir: false, build: { emptyOutDir: false, lib: isJS ? { entry: resolve(input), name: 'app', formats: ['iife'], fileName: () => outputName, } : undefined, rollupOptions: { input: isJS ? undefined : resolve(input), output: { dir: outDir, entryFileNames: '[name].js', assetFileNames: (info) => { // For CSS-only builds, output the main asset without hash const name = info.name || 'app'; return `${name}${info.ext || ''}`; }, }, }, }, resolve: { // Themes under enterprise.test/themes/ are often symlinks to external // git repos (e.g. D:/PublicSource/themes/*). By default Vite dereferences // symlinks to their real path, which breaks Node module resolution for // bare imports like `axios`/`jquery` (the real path has no node_modules // ancestors). Preserving symlinks keeps the theme at its linked path so // the host project's node_modules resolves correctly. No-op for real dirs. preserveSymlinks: true, alias: { '@front': frontResources, '@theme': resolve(`themes/${theme}`), }, }, css: { preprocessorOptions: { scss: { api: 'modern-compiler', loadPaths: [resolve('node_modules')], importers: [tildeImporter()], }, }, }, });