import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; import fs from 'fs'; import path from 'path'; import { autoLiquidGenerator } from './vite-plugins/auto-liquid-generator'; import { autoComponentRegistry } from './vite-plugins/auto-component-registry'; /** * Custom plugin to copy Liquid files * Copies section.*.liquid → sections/ and snippet.*.liquid → snippets/ * Supports manual edits and overrides auto-generation when needed */ function liquidFileCopyPlugin() { return { name: 'liquid-file-copy', buildStart() { console.log('🚀 Copying Liquid files...'); copyLiquidFiles(); }, handleHotUpdate({ file }: { file: string }) { if (file.endsWith('.liquid')) { console.log(`📝 Liquid file changed: ${file}`); copyLiquidFiles(); } } }; } /** * Check if a file was manually edited (has custom content beyond auto-generation) */ function isManuallyEditedLiquid(filePath: string): boolean { if (!fs.existsSync(filePath)) return false; try { const content = fs.readFileSync(filePath, 'utf-8'); // 1. Check for explicit manual edit markers (highest priority) const hasManualMarker = content.includes('') || content.includes('{% comment %} MANUAL EDIT {% endcomment %}'); if (hasManualMarker) { console.log(`\x1b[91m📝 [MANUAL-MARK]\x1b[0m Detected manual edit by marker: ${filePath}`); return true; } // 2. Check if file has our auto-generation signature const isAutoGenerated = content.includes('Auto-generated with Metaobject Support'); // 3. If it doesn't have our signature, it's likely manually created/edited if (!isAutoGenerated) { console.log(`\x1b[93m📝 [MANUAL-CONTENT]\x1b[0m Detected manual edit (no auto-gen signature): ${filePath}`); return true; } // 4. Check for manual modifications in auto-generated files const hasManualModifications = content.includes('MODIFIED MANUALLY') || content.includes('CUSTOM LIQUID') || content.includes('manually customized') || content.includes('Manual edit') || content.includes('custom code') || // Check if standard comments were modified (content.includes('{% comment %}') && content.includes('FOR TEST') || content.includes('MANUAL') || content.includes('Custom')); if (hasManualModifications) { console.log(`\x1b[93m📝 [MANUAL-DETECT]\x1b[0m Detected manual modifications in content: ${filePath}`); return true; } return false; } catch (error) { return false; } } /** * Check if a file is a React component liquid file that should be auto-generated */ function isReactComponentFile(filePath: string): boolean { // Normalize path separators for cross-platform compatibility const normalizedPath = filePath.replace(/\\/g, '/'); // React component files are in src/components/ and have section.*.liquid pattern const isInComponentsDir = normalizedPath.includes('src/components/'); const isSectionFile = path.basename(filePath).startsWith('section.'); return isInComponentsDir && isSectionFile; } function copyLiquidFiles() { const srcDir = './src'; const sectionsDir = './sections'; const snippetsDir = './snippets'; if (!fs.existsSync(sectionsDir)) { fs.mkdirSync(sectionsDir, { recursive: true }); } if (!fs.existsSync(snippetsDir)) { fs.mkdirSync(snippetsDir, { recursive: true }); } function findLiquidFiles(dir: string): string[] { const files: string[] = []; const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { files.push(...findLiquidFiles(fullPath)); } else if (item.endsWith('.liquid')) { files.push(fullPath); } } return files; } const liquidFiles = findLiquidFiles(srcDir); for (const file of liquidFiles) { const fileName = path.basename(file); let destDir = ''; let destFileName = fileName; // Determine destination based on file type if (fileName.startsWith('section.')) { destDir = sectionsDir; destFileName = fileName.replace('section.', ''); } else if (fileName.startsWith('snippet.')) { destDir = snippetsDir; destFileName = fileName.replace('snippet.', ''); } else { // Default to sections for other liquid files destDir = sectionsDir; } const destPath = path.join(destDir, destFileName); // Check if this is a React component file that should be auto-generated const isReactComponent = isReactComponentFile(file); const wasManuallyEdited = isManuallyEditedLiquid(file); // Always copy files, but show different messages if (wasManuallyEdited) { console.log(`\x1b[91m📝 [MANUAL]\x1b[0m Copying manually edited file: ${file} → ${destPath}`); } else if (isReactComponent) { console.log(`🤖 Copying auto-generated React component: ${file} → ${destPath}`); } else { console.log(`✅ Copied: ${file} → ${destPath}`); } try { fs.copyFileSync(file, destPath); } catch (error) { console.error(`❌ Error copying ${file}:`, error); } } console.log(`📦 Liquid files copied successfully!`); } export default defineConfig(({ mode }) => ({ plugins: [ react(), autoLiquidGenerator(), autoComponentRegistry(), liquidFileCopyPlugin() ], define: { 'process.env': '{}', 'process.env.NODE_ENV': JSON.stringify(mode), 'global': 'globalThis', }, build: { rollupOptions: { input: { main: resolve(__dirname, 'src/main.tsx') }, output: { dir: 'assets', entryFileNames: 'reactpify.js', assetFileNames: (assetInfo) => { if (assetInfo.name?.endsWith('.css')) { return 'reactpify.css'; } return '[name].[ext]'; }, format: 'es' } }, outDir: 'assets', emptyOutDir: false, watch: mode === 'development' ? {} : null, minify: mode === 'development' ? false : 'esbuild', cssCodeSplit: false }, resolve: { alias: { '@components': resolve(__dirname, 'src/utils/components'), '@helpers': resolve(__dirname, 'src/utils/helpers'), '@interfaces': resolve(__dirname, 'src/utils/interfaces'), '@redux': resolve(__dirname, 'src/redux'), '@src': resolve(__dirname, 'src') } }, server: { port: 3000, host: true }, css: { modules: { localsConvention: 'camelCase' } } }));