UNPKG

@wp-forge/vite-plugin

Version:

Vite plugin for WordPress theme development with WP-Forge

1,076 lines (1,025 loc) 32.1 kB
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); // src/plugins/blocks.ts import fg from "fast-glob"; import { readFile, writeFile } from "fs/promises"; import { join, dirname, relative } from "path"; import pc from "picocolors"; function wpForgeBlocks(options = {}) { const { dir = "src/blocks", autoRegister = true, namespace = "wp-forge", pattern = "**/block.json" } = options; let blocks = []; let rootDir; return { name: "wp-forge:blocks", async configResolved(config) { rootDir = config.root; }, async buildStart() { const blockJsonFiles = await fg(join(dir, pattern), { cwd: rootDir, absolute: true }); blocks = []; for (const blockJsonPath of blockJsonFiles) { try { const content = await readFile(blockJsonPath, "utf-8"); const blockMeta = JSON.parse(content); blocks.push({ ...blockMeta, _path: relative(rootDir, blockJsonPath), _dir: relative(rootDir, dirname(blockJsonPath)) }); this.addWatchFile(blockJsonPath); } catch (error) { this.warn(`Failed to parse block.json at ${blockJsonPath}`); } } if (blocks.length > 0) { console.log(pc.cyan(` \u2692\uFE0F Found ${blocks.length} block(s):`)); blocks.forEach((block) => { console.log(pc.dim(` \u2022 ${block.name}`)); }); console.log(); } }, async generateBundle() { if (!autoRegister || blocks.length === 0) return; const phpCode = generateBlockRegistration(blocks, namespace); const outputPath = join(rootDir, "inc/blocks-generated.php"); await writeFile(outputPath, phpCode, "utf-8"); console.log(pc.green(` \u2713 Generated block registration: inc/blocks-generated.php`)); }, // Hot reload blocks during development configureServer(server) { server.watcher.add(join(rootDir, dir, "**/*.json")); server.watcher.on("change", async (file) => { if (file.includes("block.json")) { console.log(pc.cyan(" \u2692\uFE0F Block configuration changed, reloading...")); server.ws.send({ type: "full-reload" }); } }); } }; } function generateBlockRegistration(blocks, _namespace) { const blockDirs = blocks.map((block) => { const blockData = block; return blockData._dir; }); return `<?php /** * Auto-generated block registration * * Generated by WP-Forge Vite Plugin * Do not edit this file manually * * @package wp-forge */ namespace WPForge\\Blocks; /** * Register all blocks */ function register_blocks() { ${blockDirs.map((dir) => `register_block_type( get_template_directory() . '/${dir}' );`).join("\n ")} } add_action( 'init', __NAMESPACE__ . '\\register_blocks' ); `; } // src/plugins/manifest.ts import { writeFile as writeFile2 } from "fs/promises"; import { join as join2, dirname as dirname2 } from "path"; import { mkdir } from "fs/promises"; function wpForgeManifest(options = {}) { const { enabled = true, output = "dist/.vite/manifest.json", wordpress = true } = options; let rootDir; return { name: "wp-forge:manifest", configResolved(config) { rootDir = config.root; }, async generateBundle(_, bundle) { if (!enabled) return; const manifest = {}; for (const [fileName, chunk] of Object.entries(bundle)) { if (chunk.type === "chunk") { const entry = { file: fileName, src: chunk.facadeModuleId || fileName, isEntry: chunk.isEntry }; if (chunk.viteMetadata?.importedCss?.size) { entry.css = Array.from(chunk.viteMetadata.importedCss); } if (wordpress) { const wpDeps = extractWordPressDependencies(chunk.code); if (wpDeps.length > 0) { entry.dependencies = wpDeps; } entry.version = chunk.fileName.match(/\.([a-f0-9]{8})\./)?.[1] || "1.0.0"; } manifest[chunk.name || fileName] = entry; } if (chunk.type === "asset") { manifest[fileName] = { file: fileName }; } } const outputPath = join2(rootDir, output); await mkdir(dirname2(outputPath), { recursive: true }); await writeFile2(outputPath, JSON.stringify(manifest, null, 2), "utf-8"); console.log(` \u2713 Generated WordPress manifest: ${output}`); } }; } function extractWordPressDependencies(code) { const deps = /* @__PURE__ */ new Set(); const wpPackagePattern = /@wordpress\/([\w-]+)/g; let match; while ((match = wpPackagePattern.exec(code)) !== null) { deps.add(`wp-${match[1]}`); } if (code.includes("wp.")) { if (code.includes("wp.element")) deps.add("wp-element"); if (code.includes("wp.blocks")) deps.add("wp-blocks"); if (code.includes("wp.blockEditor")) deps.add("wp-block-editor"); if (code.includes("wp.components")) deps.add("wp-components"); if (code.includes("wp.data")) deps.add("wp-data"); if (code.includes("wp.i18n")) deps.add("wp-i18n"); } return Array.from(deps); } // src/plugins/php-hmr.ts import { watch } from "chokidar"; import pc2 from "picocolors"; function wpForgePhpHmr(options = {}) { const { enabled = true, watch: patterns = ["**/*.php", "theme.json"], debounce = 100 } = options; let server; let watcher = null; let timeout = null; return { name: "wp-forge:php-hmr", configureServer(devServer) { if (!enabled) return; server = devServer; watcher = watch(patterns, { ignored: ["**/node_modules/**", "**/vendor/**", "**/dist/**"], persistent: true, ignoreInitial: true }); watcher.on("change", (path5) => { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { console.log(pc2.cyan(` \u2692\uFE0F PHP file changed: ${pc2.dim(path5)}`)); console.log(pc2.cyan(" Reloading page...\n")); server.ws.send({ type: "full-reload", path: "*" }); }, debounce); }); watcher.on("add", (path5) => { console.log(pc2.green(` \u2692\uFE0F New PHP file: ${pc2.dim(path5)} `)); }); watcher.on("unlink", (path5) => { console.log(pc2.yellow(` \u2692\uFE0F PHP file removed: ${pc2.dim(path5)} `)); }); server.httpServer?.once("listening", () => { setTimeout(() => { console.log(pc2.dim(" \u{1F4DD} Watching PHP files for changes...")); }, 150); }); }, async closeBundle() { if (watcher) { await watcher.close(); watcher = null; } if (timeout) { clearTimeout(timeout); timeout = null; } } }; } // src/plugins/assets.ts function wpForgeAssets(options = {}) { const { publicDir = "dist", baseUrl } = options; return { name: "wp-forge:assets", config() { return { publicDir, base: baseUrl || "./", build: { // Output to WordPress theme structure outDir: publicDir, emptyOutDir: false, // Don't delete PHP files // Asset handling assetsDir: "", assetsInlineLimit: 0, // Don't inline assets // Sourcemaps for debugging sourcemap: true } }; } }; } // src/plugins/design-system.ts import path from "path"; import fs from "fs"; function wpForgeDesignSystem(options = {}) { const { enabled = false, framework = "none", wordpressPresets = true } = options; if (!enabled || framework === "none") { return { name: "wp-forge:design-system", apply: () => false }; } let config; return { name: "wp-forge:design-system", configResolved(resolvedConfig) { config = resolvedConfig; }, async config() { const root = process.cwd(); const tailwindConfig = path.join(root, "tailwind.config.js"); const tailwindConfigTs = path.join(root, "tailwind.config.ts"); const unoConfig = path.join(root, "uno.config.ts"); const hasTailwindConfig = fs.existsSync(tailwindConfig) || fs.existsSync(tailwindConfigTs); const hasUnoConfig = fs.existsSync(unoConfig); if (framework === "tailwind" && !hasTailwindConfig) { console.warn( "\u26A0\uFE0F Tailwind CSS enabled but no config file found. Run: wp-forge design-system:setup tailwind" ); } if (framework === "unocss" && !hasUnoConfig) { console.warn( "\u26A0\uFE0F UnoCSS enabled but no config file found. Run: wp-forge design-system:setup unocss" ); } if (framework === "tailwind") { try { const tailwindcss = __require("tailwindcss"); const autoprefixer = __require("autoprefixer"); return { css: { postcss: { plugins: [tailwindcss, autoprefixer] } } }; } catch { console.warn("\u26A0\uFE0F Tailwind CSS dependencies not found"); } } return {}; }, configureServer(server) { const { logger } = config; server.httpServer?.once("listening", () => { setTimeout(() => { if (framework === "tailwind") { logger.info(" \u{1F3A8} Tailwind CSS integration active"); if (wordpressPresets) { logger.info(" \u{1F4DD} WordPress preset mappings enabled"); } } else if (framework === "unocss") { logger.info(" \u{1F3A8} UnoCSS integration active"); if (wordpressPresets) { logger.info(" \u{1F4DD} WordPress preset mappings enabled"); } } }, 150); }); }, async transform(code, id) { if (wordpressPresets && id.endsWith(".css") && (framework === "tailwind" || framework === "unocss")) { const themeJsonPath = path.join(process.cwd(), "theme.json"); if (fs.existsSync(themeJsonPath)) { try { const themeJson = JSON.parse(fs.readFileSync(themeJsonPath, "utf-8")); if (themeJson.settings?.color?.palette) { let injectedVars = "\n/* WordPress Color Variables */\n:root {\n"; themeJson.settings.color.palette.forEach((color) => { injectedVars += ` --wp--preset--color--${color.slug}: ${color.color}; `; }); injectedVars += "}\n"; code = injectedVars + code; } } catch { } } } return code; } }; } // src/plugins/critical-css.ts import path2 from "path"; import fs2 from "fs"; function wpForgeCriticalCSS(options = {}) { const { enabled = true, templates = ["index", "single", "page", "archive"], dimensions = { width: 1300, height: 900 }, inline = true, output = "dist/critical", minSize = 0 } = options; if (!enabled) { return { name: "wp-forge:critical-css", apply: () => false }; } return { name: "wp-forge:critical-css", apply: "build", async closeBundle() { const root = process.cwd(); const distDir = path2.join(root, "dist"); const templatesDir = path2.join(root, "templates"); const outputDir = path2.join(root, output); let critical; try { critical = __require("critical"); } catch { console.warn( '\u26A0\uFE0F Critical CSS extraction requires the "critical" package. Install it with: npm install --save-dev critical' ); return; } if (!fs2.existsSync(outputDir)) { fs2.mkdirSync(outputDir, { recursive: true }); } console.log("\u{1F4E6} Extracting critical CSS..."); const cssFiles = fs2.readdirSync(path2.join(distDir, "css")).filter((f) => f.endsWith(".css")).map((f) => path2.join(distDir, "css", f)); if (cssFiles.length === 0) { console.warn(" No CSS files found to extract critical CSS from"); return; } const mainCss = cssFiles[0]; const criticalResults = {}; for (const template of templates) { const templatePath = path2.join(templatesDir, `${template}.html`); if (!fs2.existsSync(templatePath)) { console.warn(` Template ${template}.html not found, skipping`); continue; } try { const result = await critical.generate({ inline: false, src: templatePath, css: [mainCss], dimensions: [dimensions], penthouse: { timeout: 3e4 } }); if (result.css && result.css.length > minSize) { criticalResults[template] = result.css; const criticalPath = path2.join(outputDir, `${template}-critical.css`); fs2.writeFileSync(criticalPath, result.css); console.log(` \u2713 ${template}: ${(result.css.length / 1024).toFixed(2)}KB`); } } catch (error) { console.warn(` \u2717 ${template}: Failed to extract critical CSS`); } } await generatePHPLoader(root, criticalResults, inline); } }; } async function generatePHPLoader(root, criticalResults, inline) { const incDir = path2.join(root, "inc"); if (!fs2.existsSync(incDir)) { fs2.mkdirSync(incDir, { recursive: true }); } let phpContent = `<?php /** * Critical CSS Loader * * Auto-generated by WP-Forge * DO NOT EDIT MANUALLY */ namespace WPForge; /** * Load critical CSS for template */ function load_critical_css(): void { $template = get_page_template_slug(); // Normalize template name $template_name = basename($template, '.html'); if (empty($template_name)) { $template_name = is_singular() ? 'single' : 'index'; } $critical_css_map = array( `; Object.keys(criticalResults).forEach((template) => { phpContent += ` '${template}' => '${template}-critical.css', `; }); phpContent += ` ); if (isset($critical_css_map[$template_name])) { $critical_file = get_template_directory() . '/dist/critical/' . $critical_css_map[$template_name]; if (file_exists($critical_file)) { `; if (inline) { phpContent += ` // Inline critical CSS echo '<style id="critical-css">'; echo file_get_contents($critical_file); echo '</style>'; `; } else { phpContent += ` // Link to critical CSS $critical_url = get_template_directory_uri() . '/dist/critical/' . $critical_css_map[$template_name]; printf( '<link rel="stylesheet" href="%s" id="critical-css">', esc_url($critical_url) ); `; } phpContent += ` } } } // Hook into wp_head with high priority add_action('wp_head', __NAMESPACE__ . '\\load_critical_css', 1); `; fs2.writeFileSync(path2.join(incDir, "critical-css-generated.php"), phpContent); console.log(" \u2713 Generated inc/critical-css-generated.php"); } // src/plugins/lazy-loading.ts import path3 from "path"; import fs3 from "fs"; function wpForgeLazyLoading(options = {}) { const { enabled = true, images = "native", css = true, chunks = true } = options; if (!enabled) { return { name: "wp-forge:lazy-loading", apply: () => false }; } return { name: "wp-forge:lazy-loading", apply: "build", config() { if (chunks) { return { build: { rollupOptions: { output: { manualChunks: (id) => { if (id.includes("node_modules")) { return "vendor"; } if (id.includes("@wordpress/")) { return "wordpress"; } } } } } }; } return {}; }, async closeBundle() { const root = process.cwd(); await generatePHPUtilities(root, images, css); } }; } async function generatePHPUtilities(root, images, css) { const incDir = path3.join(root, "inc"); if (!fs3.existsSync(incDir)) { fs3.mkdirSync(incDir, { recursive: true }); } let phpContent = `<?php /** * Lazy Loading Utilities * * Auto-generated by WP-Forge * DO NOT EDIT MANUALLY */ namespace WPForge; `; if (images !== "none") { phpContent += `/** * Add loading="lazy" to images */ function add_lazy_loading_to_images( $content ) { if ( is_admin() || is_feed() ) { return $content; } // Add loading="lazy" to img tags that don't have it $content = preg_replace( '/<img(?![^>]*loading=)([^>]*)>/i', '<img loading="lazy"$1>', $content ); return $content; } add_filter( 'the_content', __NAMESPACE__ . '\\add_lazy_loading_to_images', 20 ); add_filter( 'post_thumbnail_html', __NAMESPACE__ . '\\add_lazy_loading_to_images', 20 ); `; } if (css) { phpContent += `/** * Lazy load non-critical CSS */ function lazy_load_css( $html, $handle, $href, $media ) { // Skip critical CSS if ( strpos( $handle, 'critical' ) !== false ) { return $html; } // Change media to "print" and add onload to switch to "all" $html = str_replace( "media='{$media}'", "media='print' onload=\\"this.media='all'; this.onload=null;\\"", $html ); // Add noscript fallback $noscript = sprintf( '<noscript><link rel="stylesheet" href="%s" media="%s"></noscript>', esc_url( $href ), esc_attr( $media ) ); return $html . $noscript; } // Uncomment to enable CSS lazy loading // add_filter( 'style_loader_tag', __NAMESPACE__ . '\\lazy_load_css', 10, 4 ); `; } phpContent += `/** * Preconnect to external domains */ function add_resource_hints( $urls, $relation_type ) { if ( 'preconnect' === $relation_type ) { $urls[] = array( 'href' => 'https://fonts.googleapis.com', 'crossorigin', ); $urls[] = array( 'href' => 'https://fonts.gstatic.com', 'crossorigin', ); } return $urls; } add_filter( 'wp_resource_hints', __NAMESPACE__ . '\\add_resource_hints', 10, 2 ); `; fs3.writeFileSync(path3.join(incDir, "lazy-loading-generated.php"), phpContent); console.log(" \u2713 Generated inc/lazy-loading-generated.php"); } // src/plugins/preload.ts import path4 from "path"; import fs4 from "fs"; function wpForgePreload(options = {}) { const { enabled = true, assets = ["fonts", "critical-css"], strategy = "link-tag" } = options; if (!enabled) { return { name: "wp-forge:preload", apply: () => false }; } return { name: "wp-forge:preload", apply: "build", async closeBundle() { const root = process.cwd(); await generatePHPPreloader(root, assets, strategy); } }; } async function generatePHPPreloader(root, assets, strategy) { const incDir = path4.join(root, "inc"); if (!fs4.existsSync(incDir)) { fs4.mkdirSync(incDir, { recursive: true }); } let phpContent = `<?php /** * Asset Preloader * * Auto-generated by WP-Forge * DO NOT EDIT MANUALLY */ namespace WPForge; /** * Preload critical assets */ function preload_assets(): void { $preloads = array(); `; if (assets.includes("fonts")) { phpContent += ` // Preload fonts $font_dir = get_template_directory() . '/dist/fonts'; $font_url = get_template_directory_uri() . '/dist/fonts'; if ( is_dir( $font_dir ) ) { $fonts = glob( $font_dir . '/*.{woff2,woff}', GLOB_BRACE ); foreach ( $fonts as $font ) { $font_name = basename( $font ); $preloads[] = sprintf( '<link rel="preload" href="%s/%s" as="font" type="font/%s" crossorigin>', esc_url( $font_url ), esc_attr( $font_name ), esc_attr( pathinfo( $font, PATHINFO_EXTENSION ) ) ); } } `; } if (assets.includes("critical-css")) { phpContent += ` // Preload critical CSS $critical_dir = get_template_directory() . '/dist/critical'; $critical_url = get_template_directory_uri() . '/dist/critical'; if ( is_dir( $critical_dir ) ) { $template_name = is_singular() ? 'single' : 'index'; $critical_file = $critical_dir . '/' . $template_name . '-critical.css'; if ( file_exists( $critical_file ) ) { $preloads[] = sprintf( '<link rel="preload" href="%s/%s-critical.css" as="style">', esc_url( $critical_url ), esc_attr( $template_name ) ); } } `; } if (assets.includes("critical-js")) { phpContent += ` // Preload critical JavaScript $manifest_file = get_template_directory() . '/dist/.vite/manifest.json'; if ( file_exists( $manifest_file ) ) { $manifest = json_decode( file_get_contents( $manifest_file ), true ); foreach ( $manifest as $entry ) { if ( isset( $entry['isEntry'] ) && $entry['isEntry'] ) { $preloads[] = sprintf( '<link rel="modulepreload" href="%s/%s">', esc_url( get_template_directory_uri() . '/dist' ), esc_attr( $entry['file'] ) ); break; // Only preload main entry } } } `; } phpContent += ` // Output preload tags foreach ( $preloads as $preload ) { echo $preload . " "; } } add_action( 'wp_head', __NAMESPACE__ . '\\preload_assets', 2 ); /** * DNS prefetch for external resources */ function dns_prefetch(): void { $domains = array( '//fonts.googleapis.com', '//fonts.gstatic.com', ); foreach ( $domains as $domain ) { printf( '<link rel="dns-prefetch" href="%s">' . " ", esc_url( $domain ) ); } } add_action( 'wp_head', __NAMESPACE__ . '\\dns_prefetch', 1 ); `; fs4.writeFileSync(path4.join(incDir, "preload-generated.php"), phpContent); console.log(" \u2713 Generated inc/preload-generated.php"); } // src/plugins/performance.ts function wpForgePerformance(options = {}) { const plugins = []; if (options.criticalCSS !== false) { const criticalOptions = typeof options.criticalCSS === "object" ? options.criticalCSS : { enabled: true }; plugins.push(wpForgeCriticalCSS(criticalOptions)); } if (options.lazyLoading !== false) { const lazyOptions = typeof options.lazyLoading === "object" ? options.lazyLoading : { enabled: true }; plugins.push(wpForgeLazyLoading(lazyOptions)); } if (options.preload !== false) { const preloadOptions = typeof options.preload === "object" ? options.preload : { enabled: true }; plugins.push(wpForgePreload(preloadOptions)); } return plugins; } // src/integrations/tailwind-preset.ts var wpForgeTailwindPreset = { theme: { extend: { colors: { // WordPress color palette mappings "wp-primary": "var(--wp--preset--color--primary)", "wp-secondary": "var(--wp--preset--color--secondary)", "wp-accent": "var(--wp--preset--color--accent)", "wp-text": "var(--wp--preset--color--text)", "wp-text-muted": "var(--wp--preset--color--text-muted)", "wp-background": "var(--wp--preset--color--background)", "wp-background-secondary": "var(--wp--preset--color--background-secondary)", "wp-border": "var(--wp--preset--color--border)", "wp-success": "var(--wp--preset--color--success)", "wp-warning": "var(--wp--preset--color--warning)", "wp-error": "var(--wp--preset--color--error)" }, spacing: { // WordPress spacing scale mappings "wp-xs": "var(--wp--preset--spacing--xs)", "wp-sm": "var(--wp--preset--spacing--sm)", "wp-md": "var(--wp--preset--spacing--md)", "wp-lg": "var(--wp--preset--spacing--lg)", "wp-xl": "var(--wp--preset--spacing--xl)", "wp-2xl": "var(--wp--preset--spacing--2xl)" }, fontFamily: { // WordPress font family mappings "wp-sans": "var(--wp--preset--font-family--sans)", "wp-serif": "var(--wp--preset--font-family--serif)", "wp-mono": "var(--wp--preset--font-family--mono)" }, fontSize: { // WordPress font size mappings "wp-xs": "var(--wp--preset--font-size--xs)", "wp-sm": "var(--wp--preset--font-size--sm)", "wp-base": "var(--wp--preset--font-size--base)", "wp-lg": "var(--wp--preset--font-size--lg)", "wp-xl": "var(--wp--preset--font-size--xl)", "wp-2xl": "var(--wp--preset--font-size--2xl)", "wp-3xl": "var(--wp--preset--font-size--3xl)", "wp-4xl": "var(--wp--preset--font-size--4xl)" }, borderRadius: { // WordPress border radius mappings "wp-sm": "var(--wp--preset--border-radius--sm)", "wp-md": "var(--wp--preset--border-radius--md)", "wp-lg": "var(--wp--preset--border-radius--lg)" }, maxWidth: { // WordPress content width "wp-container": "var(--wp--style--global--content-size)", "wp-wide": "var(--wp--style--global--wide-size)" } } }, content: [ "./src/**/*.{js,jsx,ts,tsx}", "./inc/**/*.php", "./templates/**/*.html", "./parts/**/*.{php,html}", "./blocks/**/*.{js,jsx,ts,tsx,php}" ], safelist: [ // Safelist common WordPress block classes { pattern: /^wp-block-.+/ }, { pattern: /^has-.+-color$/ }, { pattern: /^has-.+-background-color$/ }, { pattern: /^has-.+-border-color$/ }, { pattern: /^has-.+-font-size$/ }, { pattern: /^has-.+-font-family$/ } ] }; // src/integrations/unocss-preset.ts var wpForgeUnoPreset = { name: "@wp-forge/uno-preset", theme: { colors: { // WordPress color palette mappings "wp-primary": "var(--wp--preset--color--primary)", "wp-secondary": "var(--wp--preset--color--secondary)", "wp-accent": "var(--wp--preset--color--accent)", "wp-text": "var(--wp--preset--color--text)", "wp-text-muted": "var(--wp--preset--color--text-muted)", "wp-background": "var(--wp--preset--color--background)", "wp-background-secondary": "var(--wp--preset--color--background-secondary)", "wp-border": "var(--wp--preset--color--border)", "wp-success": "var(--wp--preset--color--success)", "wp-warning": "var(--wp--preset--color--warning)", "wp-error": "var(--wp--preset--color--error)" }, spacing: { // WordPress spacing scale mappings "wp-xs": "var(--wp--preset--spacing--xs)", "wp-sm": "var(--wp--preset--spacing--sm)", "wp-md": "var(--wp--preset--spacing--md)", "wp-lg": "var(--wp--preset--spacing--lg)", "wp-xl": "var(--wp--preset--spacing--xl)", "wp-2xl": "var(--wp--preset--spacing--2xl)" }, fontFamily: { // WordPress font family mappings "wp-sans": "var(--wp--preset--font-family--sans)", "wp-serif": "var(--wp--preset--font-family--serif)", "wp-mono": "var(--wp--preset--font-family--mono)" }, fontSize: { // WordPress font size mappings "wp-xs": "var(--wp--preset--font-size--xs)", "wp-sm": "var(--wp--preset--font-size--sm)", "wp-base": "var(--wp--preset--font-size--base)", "wp-lg": "var(--wp--preset--font-size--lg)", "wp-xl": "var(--wp--preset--font-size--xl)", "wp-2xl": "var(--wp--preset--font-size--2xl)", "wp-3xl": "var(--wp--preset--font-size--3xl)", "wp-4xl": "var(--wp--preset--font-size--4xl)" }, borderRadius: { // WordPress border radius mappings "wp-sm": "var(--wp--preset--border-radius--sm)", "wp-md": "var(--wp--preset--border-radius--md)", "wp-lg": "var(--wp--preset--border-radius--lg)" }, maxWidth: { // WordPress content width "wp-container": "var(--wp--style--global--content-size)", "wp-wide": "var(--wp--style--global--wide-size)" } }, rules: [ // Custom WordPress utility rules [ "wp-container", { "max-width": "var(--wp--style--global--content-size)", "margin-left": "auto", "margin-right": "auto", "padding-left": "1rem", "padding-right": "1rem" } ], [ "wp-wide", { "max-width": "var(--wp--style--global--wide-size)", "margin-left": "auto", "margin-right": "auto" } ] ], safelist: [ // Safelist common WordPress block classes /^wp-block-.+/, /^has-.+-color$/, /^has-.+-background-color$/, /^has-.+-border-color$/, /^has-.+-font-size$/, /^has-.+-font-family$/ ] }; // src/index.ts function wpForge(options = {}) { const { blocks = { dir: "src/blocks", autoRegister: true }, manifest = { enabled: true, output: "dist/.vite/manifest.json" }, phpHmr = { enabled: true, watch: ["**/*.php", "theme.json"] }, assets = { publicDir: "dist" }, designSystem = { enabled: false, framework: "none" }, performance = { criticalCSS: true, lazyLoading: true, preload: true } } = options; const plugins = [ wpForgeCore(options), wpForgeBlocks(blocks), wpForgeManifest(manifest), wpForgePhpHmr(phpHmr), wpForgeAssets(assets) ]; if (designSystem.enabled) { plugins.push(wpForgeDesignSystem(designSystem)); } plugins.push(...wpForgePerformance(performance)); return plugins; } function wpForgeCore(_options) { let config; return { name: "wp-forge", config() { return { // WordPress-friendly build config build: { manifest: true, rollupOptions: { output: { entryFileNames: "js/[name].[hash].js", chunkFileNames: "js/[name].[hash].js", assetFileNames: (assetInfo) => { const name = assetInfo.name || ""; if (/\.(css)$/.test(name)) { return "css/[name].[hash][extname]"; } if (/\.(png|jpe?g|svg|gif|webp|avif)$/.test(name)) { return "images/[name].[hash][extname]"; } if (/\.(woff2?|eot|ttf|otf)$/.test(name)) { return "fonts/[name].[hash][extname]"; } return "assets/[name].[hash][extname]"; } } } }, // Optimize deps for WordPress optimizeDeps: { include: [ "@wordpress/blocks", "@wordpress/block-editor", "@wordpress/components", "@wordpress/data", "@wordpress/element", "@wordpress/i18n" ] } }; }, configResolved(resolvedConfig) { config = resolvedConfig; }, configureServer(server) { server.httpServer?.once("listening", () => { setTimeout(() => { const { logger } = config; logger.info("\n \u2692\uFE0F WP-Forge Dev Server Ready!\n"); logger.info(" WordPress integration active"); logger.info(" Block auto-discovery enabled"); logger.info(" PHP HMR watching for changes\n"); }, 100); }); } }; } export { wpForge, wpForgeAssets, wpForgeBlocks, wpForgeDesignSystem, wpForgeManifest, wpForgePerformance, wpForgePhpHmr, wpForgeTailwindPreset, wpForgeUnoPreset };