@eleventy-plugin-themer/build-vite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Utilities for merging Vite configuration objects
3
+ *
4
+ * Provides deep merge functionality for Vite config, handling nested
5
+ * objects like resolve.alias, css.preprocessorOptions, and plugins arrays.
6
+ */
7
+
8
+ import { UNSAFE_KEYS } from '@eleventy-plugin-themer/core/internal/safe-keys';
9
+
10
+ /**
11
+ * Deep merge Vite configuration objects.
12
+ *
13
+ * Merges a base theme config with user-provided overrides.
14
+ * Handles special cases for Vite's nested config structure:
15
+ * - resolve.alias: Merged (user aliases override theme)
16
+ * - css.preprocessorOptions.scss: Merged deeply
17
+ * - plugins: Concatenated (theme first, then user)
18
+ *
19
+ * NOTE: Distinct from `deepMergeConfig` in core by design. This variant is
20
+ * shallow with an explicit set of deep-merge keys (`resolve`, `css`,
21
+ * `build` incl. `rollupOptions.input`, `server`) matched to Vite's config
22
+ * shape. Other top-level keys use shallow spread. If you add a new
23
+ * deep-merge key, update both this function and its tests.
24
+ *
25
+ * @param {Object} themeConfig - Base theme Vite configuration
26
+ * @param {Object} userConfig - User's Vite configuration overrides
27
+ * @returns {Object} Merged Vite configuration
28
+ *
29
+ * @example
30
+ * const mergedConfig = deepMergeViteConfig(themeConfig, {
31
+ * resolve: { alias: { '@custom': '/path/to/custom' } },
32
+ * css: { preprocessorOptions: { scss: { additionalData: '$color: red;' } } },
33
+ * });
34
+ */
35
+ export function deepMergeViteConfig(themeConfig, userConfig = {}) {
36
+ // Strip unsafe keys to prevent prototype pollution
37
+ const safeTheme = Object.fromEntries(
38
+ Object.entries(themeConfig).filter(([k]) => !UNSAFE_KEYS.has(k)),
39
+ );
40
+ const safeUser = Object.fromEntries(
41
+ Object.entries(userConfig).filter(([k]) => !UNSAFE_KEYS.has(k)),
42
+ );
43
+
44
+ // Extract plugins for special handling (concatenation, not merge)
45
+ const themePlugins = safeTheme.plugins || [];
46
+ const userPlugins = safeUser.plugins || [];
47
+
48
+ return {
49
+ ...safeTheme,
50
+ ...safeUser,
51
+
52
+ // Deep merge resolve options
53
+ resolve: {
54
+ ...safeTheme.resolve,
55
+ ...(safeUser.resolve || {}),
56
+ alias: {
57
+ ...(safeTheme.resolve?.alias || {}),
58
+ ...(safeUser.resolve?.alias || {}),
59
+ },
60
+ },
61
+
62
+ // Deep merge CSS options
63
+ css: {
64
+ ...safeTheme.css,
65
+ ...(safeUser.css || {}),
66
+ preprocessorOptions: {
67
+ ...(safeTheme.css?.preprocessorOptions || {}),
68
+ ...(safeUser.css?.preprocessorOptions || {}),
69
+ scss: {
70
+ ...(safeTheme.css?.preprocessorOptions?.scss || {}),
71
+ ...(safeUser.css?.preprocessorOptions?.scss || {}),
72
+ },
73
+ },
74
+ },
75
+
76
+ // Deep merge build options
77
+ build: {
78
+ ...(safeTheme.build || {}),
79
+ ...(safeUser.build || {}),
80
+ rollupOptions: {
81
+ ...(safeTheme.build?.rollupOptions || {}),
82
+ ...(safeUser.build?.rollupOptions || {}),
83
+ input: {
84
+ ...(safeTheme.build?.rollupOptions?.input || {}),
85
+ ...(safeUser.build?.rollupOptions?.input || {}),
86
+ },
87
+ },
88
+ },
89
+
90
+ // Deep merge server options
91
+ server: {
92
+ ...(safeTheme.server || {}),
93
+ ...(safeUser.server || {}),
94
+ },
95
+
96
+ // Concatenate plugins (theme first, then user)
97
+ plugins: [...themePlugins, ...userPlugins],
98
+ };
99
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Plugin orchestration utility
3
+ * Maps config keys to plugin functions via explicit registry
4
+ */
5
+
6
+ import { logger } from '@eleventy-plugin-themer/core/logger';
7
+
8
+ import {
9
+ purgeCSSFiles,
10
+ generateCriticalCSS,
11
+ minifyHTML,
12
+ validateLinks,
13
+ preserveNonHtmlFiles,
14
+ } from '../plugins/index.mjs';
15
+
16
+ /**
17
+ * Explicit mapping from optimization config keys to plugin functions.
18
+ * Add new plugins here when they are created.
19
+ */
20
+ const PLUGIN_REGISTRY = {
21
+ purgeCSS: purgeCSSFiles,
22
+ criticalCSS: generateCriticalCSS,
23
+ minifyHTML: minifyHTML,
24
+ validateLinks: validateLinks,
25
+ preserveNonHtml: preserveNonHtmlFiles,
26
+ };
27
+
28
+ /**
29
+ * Set of valid optimization keys, exposed so consumers (and `eleventyPluginThemerVite`)
30
+ * can fail-fast on typos in user-supplied `optimizations` rather than silently no-op.
31
+ */
32
+ export const KNOWN_OPTIMIZATIONS = new Set(Object.keys(PLUGIN_REGISTRY));
33
+
34
+ /**
35
+ * Run optimization plugins based on configuration
36
+ *
37
+ * All plugins follow a uniform signature: (outputDir, options) => Promise<void>
38
+ * The dirs object is merged into options so plugins can access temp, output, etc.
39
+ *
40
+ * @param {Object} optimizations - Optimization configuration (key: true|false|function|object)
41
+ * @param {Object} dirs - Directory configuration
42
+ * @param {string} dirs.output - Output directory (required)
43
+ * @param {string} [dirs.temp] - Temp directory (optional, passed to plugins via options)
44
+ */
45
+ export async function runOptimizations(optimizations, dirs) {
46
+ for (const [name, config] of Object.entries(optimizations)) {
47
+ // Skip if optimization is disabled
48
+ if (!config) continue;
49
+
50
+ // Custom function provided
51
+ if (typeof config === 'function') {
52
+ await config();
53
+ continue;
54
+ }
55
+
56
+ // Built-in plugin
57
+ if (config === true || typeof config === 'object') {
58
+ const pluginFn = PLUGIN_REGISTRY[name];
59
+
60
+ if (!pluginFn) {
61
+ logger.warn(
62
+ `⚠️ No plugin found for optimization: "${name}". Available: ${Object.keys(PLUGIN_REGISTRY).join(', ')}`,
63
+ );
64
+ continue;
65
+ }
66
+
67
+ // Merge dirs into options so plugins have access to all paths
68
+ const userOptions = typeof config === 'object' ? config : {};
69
+ const options = { ...dirs, ...userOptions };
70
+
71
+ await pluginFn(dirs.output, options);
72
+ }
73
+ }
74
+ }