unplugin-essor 0.0.16-beta.1 → 0.0.16-beta.2
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.
- package/dist/astro.cjs +3 -3
- package/dist/astro.js +1 -1
- package/dist/{chunk-RU3ODIHJ.js → chunk-I3EHJE7P.js} +8 -6
- package/dist/chunk-I3EHJE7P.js.map +1 -0
- package/dist/{chunk-A5FJKKLT.cjs → chunk-OJG4QNXD.cjs} +8 -6
- package/dist/chunk-OJG4QNXD.cjs.map +1 -0
- package/dist/esbuild.cjs +2 -2
- package/dist/esbuild.js +1 -1
- package/dist/farm.cjs +2 -2
- package/dist/farm.js +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.js +1 -1
- package/dist/rolldown.cjs +2 -2
- package/dist/rolldown.js +1 -1
- package/dist/rollup.cjs +2 -2
- package/dist/rollup.js +1 -1
- package/dist/rspack.cjs +2 -2
- package/dist/rspack.js +1 -1
- package/dist/vite.cjs +2 -2
- package/dist/vite.js +1 -1
- package/dist/webpack.cjs +2 -2
- package/dist/webpack.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-A5FJKKLT.cjs.map +0 -1
- package/dist/chunk-RU3ODIHJ.js.map +0 -1
package/dist/astro.cjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
|
|
5
5
|
// src/astro.ts
|
|
6
6
|
var astro_default = (options) => ({
|
|
7
7
|
name: "unplugin-starter",
|
|
8
8
|
hooks: {
|
|
9
9
|
// eslint-disable-next-line require-await
|
|
10
|
-
"astro:config:setup": (astro) =>
|
|
10
|
+
"astro:config:setup": (astro) => chunkOJG4QNXD_cjs.__async(null, null, function* () {
|
|
11
11
|
var _a;
|
|
12
12
|
(_a = astro.config.vite).plugins || (_a.plugins = []);
|
|
13
|
-
astro.config.vite.plugins.push(
|
|
13
|
+
astro.config.vite.plugins.push(chunkOJG4QNXD_cjs.index_default.vite(options));
|
|
14
14
|
})
|
|
15
15
|
}
|
|
16
16
|
});
|
package/dist/astro.js
CHANGED
|
@@ -102,9 +102,9 @@ function generateHMRCode(bundlerType) {
|
|
|
102
102
|
var unpluginFactory = (options = {}, meta) => {
|
|
103
103
|
const filter = createFilter(options.include, options.exclude);
|
|
104
104
|
const bundlerType = detectBundler(meta);
|
|
105
|
+
let isProd = process.env.NODE_ENV === "production";
|
|
105
106
|
const finalOptions = __spreadProps(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options), {
|
|
106
107
|
bundler: bundlerType
|
|
107
|
-
// Pass bundler type to babel plugin
|
|
108
108
|
});
|
|
109
109
|
return {
|
|
110
110
|
name: "unplugin-essor",
|
|
@@ -120,6 +120,9 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
120
120
|
return {
|
|
121
121
|
[key]: { jsx: "preserve" }
|
|
122
122
|
};
|
|
123
|
+
},
|
|
124
|
+
configResolved(config) {
|
|
125
|
+
isProd = config.command === "build";
|
|
123
126
|
}
|
|
124
127
|
},
|
|
125
128
|
/**
|
|
@@ -161,7 +164,7 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
161
164
|
if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {
|
|
162
165
|
return null;
|
|
163
166
|
}
|
|
164
|
-
const babelOptions = __spreadValues({}, finalOptions);
|
|
167
|
+
const babelOptions = __spreadProps(__spreadValues({}, finalOptions), { hmr: !isProd && finalOptions.hmr });
|
|
165
168
|
let result;
|
|
166
169
|
try {
|
|
167
170
|
result = babel.transformSync(code, {
|
|
@@ -177,9 +180,8 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
177
180
|
if (!(result == null ? void 0 : result.code)) {
|
|
178
181
|
return code;
|
|
179
182
|
}
|
|
180
|
-
const hmrEnabled = babelOptions.hmr && (babelOptions.mode === "client" || babelOptions.mode === "ssr");
|
|
181
183
|
let finalCode = "";
|
|
182
|
-
if (
|
|
184
|
+
if (babelOptions.hmr) {
|
|
183
185
|
const hmrCode = generateHMRCode(bundlerType);
|
|
184
186
|
if (result.code.includes("__$createHMRComponent$__")) {
|
|
185
187
|
finalCode = `${hmrCode.importsCreateHMRComponent}
|
|
@@ -205,5 +207,5 @@ var unplugin = /* @__PURE__ */ createUnplugin(unpluginFactory);
|
|
|
205
207
|
var index_default = unplugin;
|
|
206
208
|
|
|
207
209
|
export { __async, index_default, unplugin, unpluginFactory };
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
210
|
+
//# sourceMappingURL=chunk-I3EHJE7P.js.map
|
|
211
|
+
//# sourceMappingURL=chunk-I3EHJE7P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["raw-loader:/home/runner/work/essor/essor/packages/unplugin/src/hmr-runtime.js?raw","../src/index.ts"],"names":[],"mappings":"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","file":"chunk-I3EHJE7P.js","sourcesContent":["/**\n * Essor HMR Runtime - Signal-based Hot Module Replacement\n *\n * This runtime enables precise component-level HMR updates without full page reloads:\n *\n * 1. **Signal Wrapping**: Each component is wrapped in a reactive signal\n * 2. **Signature Tracking**: Components are tracked by code signatures (from babel plugin)\n * 3. **Precise Updates**: Only components with changed signatures are updated\n * 4. **Effect Subscription**: Component instances subscribe to signal changes via effects\n * 5. **Bundler Agnostic**: Works with Vite, Webpack, Rspack, and other bundlers\n */\nimport { createComponent, effect, signal } from 'essor';\n\nconst isFunction = (value) => typeof value === 'function';\n/**\n * Global component registry for HMR tracking\n *\n * Maps hmrId -> ComponentInfo where:\n * - hmrId: Unique identifier for component (fileHash:componentName)\n * - componentSignal: Reactive signal holding the current component function\n * - signature: Hash of component code (changes when code changes)\n * - instances: Set of active component instances\n * - cleanups: Map of cleanup functions for each instance's effect\n */\nconst componentRegistry = new Map();\n\n/**\n * Utility function to handle invalidate or reload\n * @param hot - Hot module API object\n */\nfunction invalidateOrReload(hot) {\n if (isFunction(hot?.invalidate)) {\n hot.invalidate();\n } else if (typeof location !== 'undefined') {\n location.reload();\n }\n}\n\n/**\n * Create HMR-enabled component wrapper\n *\n * This function wraps a component to enable HMR:\n * 1. Creates or retrieves component registry entry\n * 2. Wraps component function to always read from latest signal value\n * 3. Sets up reactive effect to auto-update component when signal changes\n * 4. Returns regular component instance that auto-updates on HMR\n *\n * @param componentFn - Component function with __hmrId and __signature\n * @param props - Component props\n * @returns Component instance that responds to HMR updates\n */\nexport function createHMRComponent(componentFn, props) {\n const { __hmrId: hmrId, __signature: signature } = componentFn;\n\n if (!hmrId) {\n // If no hmrId, create normal component\n return createComponent(componentFn, props);\n }\n\n let info = componentRegistry.get(hmrId);\n\n if (!info) {\n // First registration: create signal wrapped component\n info = {\n componentSignal: signal(componentFn),\n signature,\n instances: new Set(), // Track all instances\n cleanups: new Map(), // Store cleanup for each instance\n };\n componentRegistry.set(hmrId, info);\n }\n\n // Create Component instance\n const component = createComponent(componentFn, props);\n // Track this instance\n info.instances.add(component);\n\n // Create effect for this Component instance\n // The effect subscribes to componentSignal and updates the component\n // We read the signal value immediately to establish the dependency,\n // but only trigger updates on subsequent changes\n let initialized = false;\n const cleanup = effect(() => {\n // Read signal value to establish dependency\n const currentComponentFn = info.componentSignal.value;\n // Skip the initialization run - only update on actual changes\n if (!initialized) {\n initialized = true;\n return;\n }\n\n // Update this specific instance when signal changes\n try {\n component.component = currentComponentFn;\n component.forceUpdate();\n } catch (error) {\n console.error(`[Essor HMR] Failed to update component instance:`, error);\n }\n });\n\n // Store cleanup function for this instance\n info.cleanups.set(component, cleanup);\n\n // Integrate with component lifecycle - cleanup when component is destroyed\n // Store the original onBeforeUnmount handler if it exists\n const originalOnBeforeUnmount = component.onBeforeUnmount;\n component.onBeforeUnmount = function () {\n // Call original handler first\n if (originalOnBeforeUnmount) {\n originalOnBeforeUnmount.call(this);\n }\n // Cleanup HMR effect\n const cleanupFn = info.cleanups.get(component);\n if (cleanupFn) {\n cleanupFn();\n info.cleanups.delete(component);\n }\n // Remove from instances tracking\n info.instances.delete(component);\n };\n\n return component;\n}\n\n/**\n * Determine if a component needs to be updated\n *\n * A component should update if:\n * 1. Function instance changed (indicates module was re-executed)\n * 2. Signature changed (indicates component code was modified)\n *\n * @param oldInfo - Existing component registry info\n * @param newComponentFn - New component function from updated module\n * @param newSignature - New signature hash from updated module\n * @returns true if component should update\n */\nfunction shouldUpdate(oldInfo, newComponentFn, newSignature) {\n if (!oldInfo) return true;\n\n // Check function instance (handles constant updates via module re-execution)\n const oldFn = oldInfo.componentSignal.value;\n if (oldFn !== newComponentFn) {\n return true;\n }\n\n // Check compile-time signature (handles component code changes)\n return oldInfo.signature !== newSignature;\n}\n\n/**\n * Check if a value is an HMR-enabled component\n *\n * @param value - Value to check\n * @returns true if value is a function with __hmrId property\n */\nfunction isHMRComponent(value) {\n return value && isFunction(value) && value.__hmrId;\n}\n\n/**\n * Apply HMR updates to components\n *\n * Iterates through the new component registry and updates signals\n * for components whose signatures have changed. This triggers\n * reactive effects in component instances, causing them to re-render.\n *\n * @param registry - Array of components from updated module\n * @returns true if reload needed (errors occurred), false otherwise\n */\nexport function applyUpdate(registry) {\n if (!Array.isArray(registry) || registry.length === 0) {\n return false;\n }\n\n let needsReload = false;\n\n for (const entry of registry) {\n const { __hmrId: hmrId, __signature: signature } = entry;\n const id = hmrId;\n const info = componentRegistry.get(hmrId);\n\n if (!info) {\n // New component, skip (will be registered on first render)\n continue;\n }\n\n // Use shouldUpdate to determine if update is needed\n if (!shouldUpdate(info, entry, signature)) {\n continue;\n }\n\n // Component changed, apply update\n try {\n info.signature = signature;\n info.componentSignal.value = entry;\n } catch (error) {\n console.error(`[Essor HMR] Failed to update ${id}:`, error);\n needsReload = true;\n }\n }\n\n return needsReload;\n}\n\n/**\n * Common handler for HMR updates across bundlers\n * @param hot - Hot module API\n * @param newModule - Updated module\n * @returns true if handled successfully\n */\nfunction handleHMRUpdate(hot, newModule) {\n if (!newModule) {\n invalidateOrReload(hot);\n return false;\n }\n\n // Extract HMR components from new module\n const newRegistry = extractHMRComponents(newModule);\n if (newRegistry.length === 0) {\n return true;\n }\n\n const needsReload = applyUpdate(newRegistry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n return true;\n}\n\n/**\n * Setup HMR for Vite bundler\n *\n * Vite provides import.meta.hot.accept() callback that receives the new module\n */\nfunction setupViteHMR(hot) {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n}\n\n/**\n * Setup HMR for Webpack/Rspack bundlers\n *\n * Webpack-style HMR uses module.hot.accept() and hot.data for state persistence\n */\nfunction setupWebpackHMR(hot, registry) {\n if (isFunction(hot.accept)) {\n hot.accept();\n }\n\n // Apply update if previous data exists from last hot reload\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n\n // Save current registry for next update\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n });\n }\n}\n\n/**\n * Setup HMR for other bundlers (fallback)\n *\n * Attempts to work with any bundler that provides a basic hot module API\n */\nfunction setupStandardHMR(hot, registry) {\n // Try accept callback mode first (more efficient)\n if (isFunction(hot.accept)) {\n try {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n } catch {\n // Some bundlers don't support accept with callback\n // Fall back to simple accept (for Webpack-style pattern)\n try {\n hot.accept();\n } catch (error_) {\n console.warn('[Essor HMR] Failed to setup hot.accept:', error_);\n }\n }\n }\n\n // Setup dispose handler for state persistence\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n data.__essor_timestamp__ = Date.now();\n });\n }\n\n // Apply update if previous data exists (for Webpack-style HMR)\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n}\n\n/**\n * Extract HMR components from a module\n *\n * Looks for __$registry$__ array first (generated by babel plugin),\n * then falls back to scanning all exports for HMR components\n *\n * @param module - Module object to scan\n * @returns Array of HMR component functions\n */\nfunction extractHMRComponents(module) {\n if (!module) return [];\n\n // Prefer __$registry$__\n if (Array.isArray(module.__$registry$__)) {\n return module.__$registry$__;\n }\n\n // Otherwise search in exports\n const components = [];\n for (const key of Object.keys(module)) {\n const value = module[key];\n if (isHMRComponent(value)) {\n components.push(value);\n }\n }\n return components;\n}\n\n/**\n * Main HMR entry point\n *\n * Called from transformed modules to set up HMR based on bundler type\n *\n * @param bundlerType - Type of bundler (vite, webpack5, rspack, etc.)\n * @param hot - Hot module API object (import.meta.hot or module.hot)\n * @param registry - Array of components from current module\n * @returns true if HMR setup succeeded, false otherwise\n */\nexport function hmrAccept(bundlerType, hot, registry) {\n if (!hot || !registry || registry.length === 0) {\n return false;\n }\n\n switch (bundlerType) {\n case 'vite':\n setupViteHMR(hot, registry);\n break;\n case 'webpack':\n case 'rspack':\n setupWebpackHMR(hot, registry);\n break;\n default:\n // Use standard HMR setup\n setupStandardHMR(hot, registry);\n break;\n }\n\n return true;\n}\n\n/**\n * Cleanup all instances of a component (utility function)\n *\n * @param hmrId - Component HMR ID\n * @returns Number of instances cleaned up\n */\nexport function unregisterAllInstances(hmrId) {\n const info = componentRegistry.get(hmrId);\n if (!info) return 0;\n\n let count = 0;\n for (const item of info.instances) {\n const cleanup = info.cleanups.get(item);\n if (cleanup) {\n try {\n cleanup();\n } catch (error) {\n console.error(`[Essor HMR] Failed to cleanup effect:`, error);\n }\n }\n count++;\n }\n\n info.instances.clear();\n\n return count;\n}\n\n/**\n * Get registry information for debugging\n *\n * @returns Object mapping hmrId to component info (signature, instance count)\n */\nexport function getRegistryInfo() {\n const info = {};\n for (const [id, data] of componentRegistry) {\n info[id] = {\n signature: data.signature,\n instanceCount: data.instances.size,\n };\n }\n return info;\n}\n","import { createUnplugin } from 'unplugin';\nimport * as babel from '@babel/core';\nimport essorBabelPlugin from 'babel-plugin-essor';\nimport { createFilter } from 'vite';\n// @ts-ignore - resolved by esbuild raw plugin at build time\nimport hmrRuntimeCode from './hmr-runtime.js?raw';\nimport type { UnpluginContextMeta, UnpluginFactory } from 'unplugin';\nimport type { Options } from './types';\n\n/**\n * Virtual module ID for HMR runtime\n * Injected as an import in transformed files that have HMR components\n */\nconst VIRTUAL_MODULE_ID = 'virtual:essor-hmr';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0virtual:essor-hmr';\n\n/**\n * Default plugin options\n */\nconst DEFAULT_OPTIONS = {\n symbol: '$',\n mode: 'client',\n props: true,\n hmr: true,\n enableFor: false,\n};\n\n/**\n * Performance: Pre-compiled regex and constants\n */\nconst FILE_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/i;\nconst SKIP_DIRECTORIES = ['node_modules', 'dist', 'public'];\n\ntype BundlerType = 'vite' | 'webpack5' | 'rspack' | 'rollup' | 'esbuild' | 'standard';\n\n/**\n * Detect bundler type from unplugin meta or environment variables\n *\n * This is important for HMR because different bundlers have different\n * HMR APIs (import.meta.hot.accept, module.hot.accept, etc.)\n */\nfunction detectBundler(meta: UnpluginContextMeta): BundlerType {\n // First, try to detect from unplugin meta\n if (meta?.framework) {\n switch (meta.framework) {\n case 'vite':\n return 'vite';\n case 'webpack':\n return 'webpack5';\n case 'rspack':\n return 'rspack';\n case 'rollup':\n return 'rollup';\n case 'esbuild':\n return 'esbuild';\n }\n }\n\n // Fallback: detect from environment variables\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.VITE || process.env.VITEST) return 'vite';\n if (process.env.WEBPACK_VERSION) return 'webpack5';\n if (process.env.RSPACK) return 'rspack';\n }\n\n // Default to standard if detection fails\n return 'standard';\n}\n\n/**\n * Generate HMR boilerplate code for a specific bundler\n *\n * @param bundlerType - The bundler type detected\n * @returns Object with imports and registration code\n */\nfunction generateHMRCode(bundlerType: BundlerType) {\n // Import HMR utilities from virtual module\n const imports = [\n `import { createHMRComponent as __$createHMRComponent$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n `import { hmrAccept as __$hmrAccept$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n ];\n\n // Generate HMR acceptance code\n // The registry array (__$registry$__) is generated by the babel plugin\n const register = [\n 'if (import.meta.hot) {',\n ` __$hmrAccept$__(\"${bundlerType}\", import.meta.hot, __$registry$__);`,\n ' import.meta.hot?.accept()',\n '}',\n ].join('\\n');\n\n return {\n importsCreateHMRComponent: `${imports[0]}\\n`,\n importHmrAccept: `${imports[1]}\\n`,\n register,\n };\n}\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (\n options: Options = {},\n meta,\n) => {\n // Create file filter based on include/exclude patterns\n const filter = createFilter(options.include, options.exclude);\n\n // Detect bundler type for HMR\n const bundlerType = detectBundler(meta);\n\n // NODE_ENV is set by most bundlers; fallback treats unknown env as dev (HMR on)\n let isProd = process.env.NODE_ENV === 'production';\n\n // Merge user options with defaults (hmr excluded — computed dynamically in transform)\n const finalOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n bundler: bundlerType,\n };\n\n return {\n name: 'unplugin-essor',\n /**\n * Vite-specific config to preserve JSX so the babel plugin can handle it.\n \n */\n vite: {\n config(this: unknown) {\n // Inside a vite hook, `this` is the rollup plugin context. On Vite 8 /\n // rolldown-vite it exposes `meta.rolldownVersion`.\n const ctx = this as { meta?: Record<string, unknown> } | undefined;\n const isRolldownVite = !!ctx?.meta && 'rolldownVersion' in ctx.meta;\n const key = (isRolldownVite ? 'oxc' : 'esbuild') as 'esbuild';\n return {\n [key]: { jsx: 'preserve' },\n };\n },\n configResolved(config: { command: string }) {\n // Vite's command is more reliable than NODE_ENV for detecting dev vs build\n isProd = config.command === 'build';\n },\n },\n\n /**\n * Resolve virtual HMR runtime module\n */\n resolveId(id: string) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n return null;\n },\n\n /**\n * Load virtual HMR runtime module\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return {\n code: hmrRuntimeCode,\n map: null,\n };\n }\n return null;\n },\n rolldown: {\n options(opts) {\n opts.transform ??= {\n jsx: 'preserve',\n };\n },\n },\n /**\n * Transform code with Babel plugin\n */\n transform(code, id) {\n // Skip node_modules, dist, and public directories\n if (SKIP_DIRECTORIES.some((p) => id.includes(p))) {\n return;\n }\n\n // Only transform JS/TS files\n if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {\n return null;\n }\n\n // options.hmr explicit value wins; otherwise enable only in dev\n const babelOptions = { ...finalOptions, hmr: !isProd && finalOptions.hmr };\n\n // Transform with Babel (with error handling)\n let result;\n try {\n result = babel.transformSync(code, {\n filename: id,\n sourceMaps: true,\n sourceType: 'module',\n plugins: [[essorBabelPlugin, babelOptions]],\n });\n } catch (error) {\n console.error(`[unplugin-essor] Transform failed for ${id}:`, error);\n return null;\n }\n\n if (!result?.code) {\n return code;\n }\n\n let finalCode = '';\n\n // Inject HMR code if enabled and components are present\n if (babelOptions.hmr) {\n const hmrCode = generateHMRCode(bundlerType);\n\n if (result.code.includes('__$createHMRComponent$__')) {\n finalCode = `${hmrCode.importsCreateHMRComponent}\\n${finalCode}`;\n }\n finalCode += result.code;\n if (result.code.includes('__$registry$__')) {\n finalCode = `${hmrCode.importHmrAccept}\\n${finalCode}\\n${hmrCode.register}`;\n }\n } else {\n finalCode += result.code;\n }\n\n return {\n code: finalCode,\n map: result.map,\n };\n },\n };\n};\n\nexport const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"]}
|
|
@@ -127,9 +127,9 @@ function generateHMRCode(bundlerType) {
|
|
|
127
127
|
var unpluginFactory = (options = {}, meta) => {
|
|
128
128
|
const filter = vite.createFilter(options.include, options.exclude);
|
|
129
129
|
const bundlerType = detectBundler(meta);
|
|
130
|
+
let isProd = process.env.NODE_ENV === "production";
|
|
130
131
|
const finalOptions = __spreadProps(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options), {
|
|
131
132
|
bundler: bundlerType
|
|
132
|
-
// Pass bundler type to babel plugin
|
|
133
133
|
});
|
|
134
134
|
return {
|
|
135
135
|
name: "unplugin-essor",
|
|
@@ -145,6 +145,9 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
145
145
|
return {
|
|
146
146
|
[key]: { jsx: "preserve" }
|
|
147
147
|
};
|
|
148
|
+
},
|
|
149
|
+
configResolved(config) {
|
|
150
|
+
isProd = config.command === "build";
|
|
148
151
|
}
|
|
149
152
|
},
|
|
150
153
|
/**
|
|
@@ -186,7 +189,7 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
186
189
|
if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {
|
|
187
190
|
return null;
|
|
188
191
|
}
|
|
189
|
-
const babelOptions = __spreadValues({}, finalOptions);
|
|
192
|
+
const babelOptions = __spreadProps(__spreadValues({}, finalOptions), { hmr: !isProd && finalOptions.hmr });
|
|
190
193
|
let result;
|
|
191
194
|
try {
|
|
192
195
|
result = babel__namespace.transformSync(code, {
|
|
@@ -202,9 +205,8 @@ var unpluginFactory = (options = {}, meta) => {
|
|
|
202
205
|
if (!(result == null ? void 0 : result.code)) {
|
|
203
206
|
return code;
|
|
204
207
|
}
|
|
205
|
-
const hmrEnabled = babelOptions.hmr && (babelOptions.mode === "client" || babelOptions.mode === "ssr");
|
|
206
208
|
let finalCode = "";
|
|
207
|
-
if (
|
|
209
|
+
if (babelOptions.hmr) {
|
|
208
210
|
const hmrCode = generateHMRCode(bundlerType);
|
|
209
211
|
if (result.code.includes("__$createHMRComponent$__")) {
|
|
210
212
|
finalCode = `${hmrCode.importsCreateHMRComponent}
|
|
@@ -233,5 +235,5 @@ exports.__async = __async;
|
|
|
233
235
|
exports.index_default = index_default;
|
|
234
236
|
exports.unplugin = unplugin;
|
|
235
237
|
exports.unpluginFactory = unpluginFactory;
|
|
236
|
-
//# sourceMappingURL=chunk-
|
|
237
|
-
//# sourceMappingURL=chunk-
|
|
238
|
+
//# sourceMappingURL=chunk-OJG4QNXD.cjs.map
|
|
239
|
+
//# sourceMappingURL=chunk-OJG4QNXD.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["raw-loader:/home/runner/work/essor/essor/packages/unplugin/src/hmr-runtime.js?raw","../src/index.ts"],"names":["createFilter","babel","essorBabelPlugin"],"mappings":"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","file":"chunk-OJG4QNXD.cjs","sourcesContent":["/**\n * Essor HMR Runtime - Signal-based Hot Module Replacement\n *\n * This runtime enables precise component-level HMR updates without full page reloads:\n *\n * 1. **Signal Wrapping**: Each component is wrapped in a reactive signal\n * 2. **Signature Tracking**: Components are tracked by code signatures (from babel plugin)\n * 3. **Precise Updates**: Only components with changed signatures are updated\n * 4. **Effect Subscription**: Component instances subscribe to signal changes via effects\n * 5. **Bundler Agnostic**: Works with Vite, Webpack, Rspack, and other bundlers\n */\nimport { createComponent, effect, signal } from 'essor';\n\nconst isFunction = (value) => typeof value === 'function';\n/**\n * Global component registry for HMR tracking\n *\n * Maps hmrId -> ComponentInfo where:\n * - hmrId: Unique identifier for component (fileHash:componentName)\n * - componentSignal: Reactive signal holding the current component function\n * - signature: Hash of component code (changes when code changes)\n * - instances: Set of active component instances\n * - cleanups: Map of cleanup functions for each instance's effect\n */\nconst componentRegistry = new Map();\n\n/**\n * Utility function to handle invalidate or reload\n * @param hot - Hot module API object\n */\nfunction invalidateOrReload(hot) {\n if (isFunction(hot?.invalidate)) {\n hot.invalidate();\n } else if (typeof location !== 'undefined') {\n location.reload();\n }\n}\n\n/**\n * Create HMR-enabled component wrapper\n *\n * This function wraps a component to enable HMR:\n * 1. Creates or retrieves component registry entry\n * 2. Wraps component function to always read from latest signal value\n * 3. Sets up reactive effect to auto-update component when signal changes\n * 4. Returns regular component instance that auto-updates on HMR\n *\n * @param componentFn - Component function with __hmrId and __signature\n * @param props - Component props\n * @returns Component instance that responds to HMR updates\n */\nexport function createHMRComponent(componentFn, props) {\n const { __hmrId: hmrId, __signature: signature } = componentFn;\n\n if (!hmrId) {\n // If no hmrId, create normal component\n return createComponent(componentFn, props);\n }\n\n let info = componentRegistry.get(hmrId);\n\n if (!info) {\n // First registration: create signal wrapped component\n info = {\n componentSignal: signal(componentFn),\n signature,\n instances: new Set(), // Track all instances\n cleanups: new Map(), // Store cleanup for each instance\n };\n componentRegistry.set(hmrId, info);\n }\n\n // Create Component instance\n const component = createComponent(componentFn, props);\n // Track this instance\n info.instances.add(component);\n\n // Create effect for this Component instance\n // The effect subscribes to componentSignal and updates the component\n // We read the signal value immediately to establish the dependency,\n // but only trigger updates on subsequent changes\n let initialized = false;\n const cleanup = effect(() => {\n // Read signal value to establish dependency\n const currentComponentFn = info.componentSignal.value;\n // Skip the initialization run - only update on actual changes\n if (!initialized) {\n initialized = true;\n return;\n }\n\n // Update this specific instance when signal changes\n try {\n component.component = currentComponentFn;\n component.forceUpdate();\n } catch (error) {\n console.error(`[Essor HMR] Failed to update component instance:`, error);\n }\n });\n\n // Store cleanup function for this instance\n info.cleanups.set(component, cleanup);\n\n // Integrate with component lifecycle - cleanup when component is destroyed\n // Store the original onBeforeUnmount handler if it exists\n const originalOnBeforeUnmount = component.onBeforeUnmount;\n component.onBeforeUnmount = function () {\n // Call original handler first\n if (originalOnBeforeUnmount) {\n originalOnBeforeUnmount.call(this);\n }\n // Cleanup HMR effect\n const cleanupFn = info.cleanups.get(component);\n if (cleanupFn) {\n cleanupFn();\n info.cleanups.delete(component);\n }\n // Remove from instances tracking\n info.instances.delete(component);\n };\n\n return component;\n}\n\n/**\n * Determine if a component needs to be updated\n *\n * A component should update if:\n * 1. Function instance changed (indicates module was re-executed)\n * 2. Signature changed (indicates component code was modified)\n *\n * @param oldInfo - Existing component registry info\n * @param newComponentFn - New component function from updated module\n * @param newSignature - New signature hash from updated module\n * @returns true if component should update\n */\nfunction shouldUpdate(oldInfo, newComponentFn, newSignature) {\n if (!oldInfo) return true;\n\n // Check function instance (handles constant updates via module re-execution)\n const oldFn = oldInfo.componentSignal.value;\n if (oldFn !== newComponentFn) {\n return true;\n }\n\n // Check compile-time signature (handles component code changes)\n return oldInfo.signature !== newSignature;\n}\n\n/**\n * Check if a value is an HMR-enabled component\n *\n * @param value - Value to check\n * @returns true if value is a function with __hmrId property\n */\nfunction isHMRComponent(value) {\n return value && isFunction(value) && value.__hmrId;\n}\n\n/**\n * Apply HMR updates to components\n *\n * Iterates through the new component registry and updates signals\n * for components whose signatures have changed. This triggers\n * reactive effects in component instances, causing them to re-render.\n *\n * @param registry - Array of components from updated module\n * @returns true if reload needed (errors occurred), false otherwise\n */\nexport function applyUpdate(registry) {\n if (!Array.isArray(registry) || registry.length === 0) {\n return false;\n }\n\n let needsReload = false;\n\n for (const entry of registry) {\n const { __hmrId: hmrId, __signature: signature } = entry;\n const id = hmrId;\n const info = componentRegistry.get(hmrId);\n\n if (!info) {\n // New component, skip (will be registered on first render)\n continue;\n }\n\n // Use shouldUpdate to determine if update is needed\n if (!shouldUpdate(info, entry, signature)) {\n continue;\n }\n\n // Component changed, apply update\n try {\n info.signature = signature;\n info.componentSignal.value = entry;\n } catch (error) {\n console.error(`[Essor HMR] Failed to update ${id}:`, error);\n needsReload = true;\n }\n }\n\n return needsReload;\n}\n\n/**\n * Common handler for HMR updates across bundlers\n * @param hot - Hot module API\n * @param newModule - Updated module\n * @returns true if handled successfully\n */\nfunction handleHMRUpdate(hot, newModule) {\n if (!newModule) {\n invalidateOrReload(hot);\n return false;\n }\n\n // Extract HMR components from new module\n const newRegistry = extractHMRComponents(newModule);\n if (newRegistry.length === 0) {\n return true;\n }\n\n const needsReload = applyUpdate(newRegistry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n return true;\n}\n\n/**\n * Setup HMR for Vite bundler\n *\n * Vite provides import.meta.hot.accept() callback that receives the new module\n */\nfunction setupViteHMR(hot) {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n}\n\n/**\n * Setup HMR for Webpack/Rspack bundlers\n *\n * Webpack-style HMR uses module.hot.accept() and hot.data for state persistence\n */\nfunction setupWebpackHMR(hot, registry) {\n if (isFunction(hot.accept)) {\n hot.accept();\n }\n\n // Apply update if previous data exists from last hot reload\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n\n // Save current registry for next update\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n });\n }\n}\n\n/**\n * Setup HMR for other bundlers (fallback)\n *\n * Attempts to work with any bundler that provides a basic hot module API\n */\nfunction setupStandardHMR(hot, registry) {\n // Try accept callback mode first (more efficient)\n if (isFunction(hot.accept)) {\n try {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n } catch {\n // Some bundlers don't support accept with callback\n // Fall back to simple accept (for Webpack-style pattern)\n try {\n hot.accept();\n } catch (error_) {\n console.warn('[Essor HMR] Failed to setup hot.accept:', error_);\n }\n }\n }\n\n // Setup dispose handler for state persistence\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n data.__essor_timestamp__ = Date.now();\n });\n }\n\n // Apply update if previous data exists (for Webpack-style HMR)\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n}\n\n/**\n * Extract HMR components from a module\n *\n * Looks for __$registry$__ array first (generated by babel plugin),\n * then falls back to scanning all exports for HMR components\n *\n * @param module - Module object to scan\n * @returns Array of HMR component functions\n */\nfunction extractHMRComponents(module) {\n if (!module) return [];\n\n // Prefer __$registry$__\n if (Array.isArray(module.__$registry$__)) {\n return module.__$registry$__;\n }\n\n // Otherwise search in exports\n const components = [];\n for (const key of Object.keys(module)) {\n const value = module[key];\n if (isHMRComponent(value)) {\n components.push(value);\n }\n }\n return components;\n}\n\n/**\n * Main HMR entry point\n *\n * Called from transformed modules to set up HMR based on bundler type\n *\n * @param bundlerType - Type of bundler (vite, webpack5, rspack, etc.)\n * @param hot - Hot module API object (import.meta.hot or module.hot)\n * @param registry - Array of components from current module\n * @returns true if HMR setup succeeded, false otherwise\n */\nexport function hmrAccept(bundlerType, hot, registry) {\n if (!hot || !registry || registry.length === 0) {\n return false;\n }\n\n switch (bundlerType) {\n case 'vite':\n setupViteHMR(hot, registry);\n break;\n case 'webpack':\n case 'rspack':\n setupWebpackHMR(hot, registry);\n break;\n default:\n // Use standard HMR setup\n setupStandardHMR(hot, registry);\n break;\n }\n\n return true;\n}\n\n/**\n * Cleanup all instances of a component (utility function)\n *\n * @param hmrId - Component HMR ID\n * @returns Number of instances cleaned up\n */\nexport function unregisterAllInstances(hmrId) {\n const info = componentRegistry.get(hmrId);\n if (!info) return 0;\n\n let count = 0;\n for (const item of info.instances) {\n const cleanup = info.cleanups.get(item);\n if (cleanup) {\n try {\n cleanup();\n } catch (error) {\n console.error(`[Essor HMR] Failed to cleanup effect:`, error);\n }\n }\n count++;\n }\n\n info.instances.clear();\n\n return count;\n}\n\n/**\n * Get registry information for debugging\n *\n * @returns Object mapping hmrId to component info (signature, instance count)\n */\nexport function getRegistryInfo() {\n const info = {};\n for (const [id, data] of componentRegistry) {\n info[id] = {\n signature: data.signature,\n instanceCount: data.instances.size,\n };\n }\n return info;\n}\n","import { createUnplugin } from 'unplugin';\nimport * as babel from '@babel/core';\nimport essorBabelPlugin from 'babel-plugin-essor';\nimport { createFilter } from 'vite';\n// @ts-ignore - resolved by esbuild raw plugin at build time\nimport hmrRuntimeCode from './hmr-runtime.js?raw';\nimport type { UnpluginContextMeta, UnpluginFactory } from 'unplugin';\nimport type { Options } from './types';\n\n/**\n * Virtual module ID for HMR runtime\n * Injected as an import in transformed files that have HMR components\n */\nconst VIRTUAL_MODULE_ID = 'virtual:essor-hmr';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0virtual:essor-hmr';\n\n/**\n * Default plugin options\n */\nconst DEFAULT_OPTIONS = {\n symbol: '$',\n mode: 'client',\n props: true,\n hmr: true,\n enableFor: false,\n};\n\n/**\n * Performance: Pre-compiled regex and constants\n */\nconst FILE_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/i;\nconst SKIP_DIRECTORIES = ['node_modules', 'dist', 'public'];\n\ntype BundlerType = 'vite' | 'webpack5' | 'rspack' | 'rollup' | 'esbuild' | 'standard';\n\n/**\n * Detect bundler type from unplugin meta or environment variables\n *\n * This is important for HMR because different bundlers have different\n * HMR APIs (import.meta.hot.accept, module.hot.accept, etc.)\n */\nfunction detectBundler(meta: UnpluginContextMeta): BundlerType {\n // First, try to detect from unplugin meta\n if (meta?.framework) {\n switch (meta.framework) {\n case 'vite':\n return 'vite';\n case 'webpack':\n return 'webpack5';\n case 'rspack':\n return 'rspack';\n case 'rollup':\n return 'rollup';\n case 'esbuild':\n return 'esbuild';\n }\n }\n\n // Fallback: detect from environment variables\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.VITE || process.env.VITEST) return 'vite';\n if (process.env.WEBPACK_VERSION) return 'webpack5';\n if (process.env.RSPACK) return 'rspack';\n }\n\n // Default to standard if detection fails\n return 'standard';\n}\n\n/**\n * Generate HMR boilerplate code for a specific bundler\n *\n * @param bundlerType - The bundler type detected\n * @returns Object with imports and registration code\n */\nfunction generateHMRCode(bundlerType: BundlerType) {\n // Import HMR utilities from virtual module\n const imports = [\n `import { createHMRComponent as __$createHMRComponent$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n `import { hmrAccept as __$hmrAccept$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n ];\n\n // Generate HMR acceptance code\n // The registry array (__$registry$__) is generated by the babel plugin\n const register = [\n 'if (import.meta.hot) {',\n ` __$hmrAccept$__(\"${bundlerType}\", import.meta.hot, __$registry$__);`,\n ' import.meta.hot?.accept()',\n '}',\n ].join('\\n');\n\n return {\n importsCreateHMRComponent: `${imports[0]}\\n`,\n importHmrAccept: `${imports[1]}\\n`,\n register,\n };\n}\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (\n options: Options = {},\n meta,\n) => {\n // Create file filter based on include/exclude patterns\n const filter = createFilter(options.include, options.exclude);\n\n // Detect bundler type for HMR\n const bundlerType = detectBundler(meta);\n\n // NODE_ENV is set by most bundlers; fallback treats unknown env as dev (HMR on)\n let isProd = process.env.NODE_ENV === 'production';\n\n // Merge user options with defaults (hmr excluded — computed dynamically in transform)\n const finalOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n bundler: bundlerType,\n };\n\n return {\n name: 'unplugin-essor',\n /**\n * Vite-specific config to preserve JSX so the babel plugin can handle it.\n \n */\n vite: {\n config(this: unknown) {\n // Inside a vite hook, `this` is the rollup plugin context. On Vite 8 /\n // rolldown-vite it exposes `meta.rolldownVersion`.\n const ctx = this as { meta?: Record<string, unknown> } | undefined;\n const isRolldownVite = !!ctx?.meta && 'rolldownVersion' in ctx.meta;\n const key = (isRolldownVite ? 'oxc' : 'esbuild') as 'esbuild';\n return {\n [key]: { jsx: 'preserve' },\n };\n },\n configResolved(config: { command: string }) {\n // Vite's command is more reliable than NODE_ENV for detecting dev vs build\n isProd = config.command === 'build';\n },\n },\n\n /**\n * Resolve virtual HMR runtime module\n */\n resolveId(id: string) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n return null;\n },\n\n /**\n * Load virtual HMR runtime module\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return {\n code: hmrRuntimeCode,\n map: null,\n };\n }\n return null;\n },\n rolldown: {\n options(opts) {\n opts.transform ??= {\n jsx: 'preserve',\n };\n },\n },\n /**\n * Transform code with Babel plugin\n */\n transform(code, id) {\n // Skip node_modules, dist, and public directories\n if (SKIP_DIRECTORIES.some((p) => id.includes(p))) {\n return;\n }\n\n // Only transform JS/TS files\n if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {\n return null;\n }\n\n // options.hmr explicit value wins; otherwise enable only in dev\n const babelOptions = { ...finalOptions, hmr: !isProd && finalOptions.hmr };\n\n // Transform with Babel (with error handling)\n let result;\n try {\n result = babel.transformSync(code, {\n filename: id,\n sourceMaps: true,\n sourceType: 'module',\n plugins: [[essorBabelPlugin, babelOptions]],\n });\n } catch (error) {\n console.error(`[unplugin-essor] Transform failed for ${id}:`, error);\n return null;\n }\n\n if (!result?.code) {\n return code;\n }\n\n let finalCode = '';\n\n // Inject HMR code if enabled and components are present\n if (babelOptions.hmr) {\n const hmrCode = generateHMRCode(bundlerType);\n\n if (result.code.includes('__$createHMRComponent$__')) {\n finalCode = `${hmrCode.importsCreateHMRComponent}\\n${finalCode}`;\n }\n finalCode += result.code;\n if (result.code.includes('__$registry$__')) {\n finalCode = `${hmrCode.importHmrAccept}\\n${finalCode}\\n${hmrCode.register}`;\n }\n } else {\n finalCode += result.code;\n }\n\n return {\n code: finalCode,\n map: result.map,\n };\n },\n };\n};\n\nexport const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"]}
|
package/dist/esbuild.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var esbuild_default = unplugin.createEsbuildPlugin(
|
|
6
|
+
var esbuild_default = unplugin.createEsbuildPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = esbuild_default;
|
|
9
9
|
//# sourceMappingURL=esbuild.cjs.map
|
package/dist/esbuild.js
CHANGED
package/dist/farm.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var farm_default = unplugin.createFarmPlugin(
|
|
6
|
+
var farm_default = unplugin.createFarmPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = farm_default;
|
|
9
9
|
//# sourceMappingURL=farm.cjs.map
|
package/dist/farm.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "default", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkOJG4QNXD_cjs.index_default; }
|
|
12
12
|
});
|
|
13
13
|
Object.defineProperty(exports, "unplugin", {
|
|
14
14
|
enumerable: true,
|
|
15
|
-
get: function () { return
|
|
15
|
+
get: function () { return chunkOJG4QNXD_cjs.unplugin; }
|
|
16
16
|
});
|
|
17
17
|
Object.defineProperty(exports, "unpluginFactory", {
|
|
18
18
|
enumerable: true,
|
|
19
|
-
get: function () { return
|
|
19
|
+
get: function () { return chunkOJG4QNXD_cjs.unpluginFactory; }
|
|
20
20
|
});
|
|
21
21
|
//# sourceMappingURL=index.cjs.map
|
|
22
22
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
package/dist/rolldown.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var rolldown_default = unplugin.createRolldownPlugin(
|
|
6
|
+
var rolldown_default = unplugin.createRolldownPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = rolldown_default;
|
|
9
9
|
//# sourceMappingURL=rolldown.cjs.map
|
package/dist/rolldown.js
CHANGED
package/dist/rollup.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var rollup_default = unplugin.createRollupPlugin(
|
|
6
|
+
var rollup_default = unplugin.createRollupPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = rollup_default;
|
|
9
9
|
//# sourceMappingURL=rollup.cjs.map
|
package/dist/rollup.js
CHANGED
package/dist/rspack.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var rspack_default = unplugin.createRspackPlugin(
|
|
6
|
+
var rspack_default = unplugin.createRspackPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = rspack_default;
|
|
9
9
|
//# sourceMappingURL=rspack.cjs.map
|
package/dist/rspack.js
CHANGED
package/dist/vite.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var vite_default = unplugin.createVitePlugin(
|
|
6
|
+
var vite_default = unplugin.createVitePlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = vite_default;
|
|
9
9
|
//# sourceMappingURL=vite.cjs.map
|
package/dist/vite.js
CHANGED
package/dist/webpack.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOJG4QNXD_cjs = require('./chunk-OJG4QNXD.cjs');
|
|
4
4
|
var unplugin = require('unplugin');
|
|
5
5
|
|
|
6
|
-
var webpack_default = unplugin.createWebpackPlugin(
|
|
6
|
+
var webpack_default = unplugin.createWebpackPlugin(chunkOJG4QNXD_cjs.unpluginFactory);
|
|
7
7
|
|
|
8
8
|
module.exports = webpack_default;
|
|
9
9
|
//# sourceMappingURL=webpack.cjs.map
|
package/dist/webpack.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unplugin-essor",
|
|
3
|
-
"version": "0.0.16-beta.
|
|
3
|
+
"version": "0.0.16-beta.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"dependencies": {
|
|
116
116
|
"@babel/core": "^7.29.0",
|
|
117
117
|
"unplugin": "2.3.11",
|
|
118
|
-
"babel-plugin-essor": "0.0.16-beta.
|
|
118
|
+
"babel-plugin-essor": "0.0.16-beta.2"
|
|
119
119
|
},
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@rspack/cli": "^2.0.1",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"tsup": "^8.5.1",
|
|
127
127
|
"typescript": "^6.0.3",
|
|
128
128
|
"vite": "^8.0.10",
|
|
129
|
-
"essor": "0.0.16-beta.
|
|
129
|
+
"essor": "0.0.16-beta.2"
|
|
130
130
|
},
|
|
131
131
|
"scripts": {
|
|
132
132
|
"build": "tsup",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["raw-loader:/home/runner/work/essor/essor/packages/unplugin/src/hmr-runtime.js?raw","../src/index.ts"],"names":["createFilter","babel","essorBabelPlugin"],"mappings":"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","file":"chunk-A5FJKKLT.cjs","sourcesContent":["/**\n * Essor HMR Runtime - Signal-based Hot Module Replacement\n *\n * This runtime enables precise component-level HMR updates without full page reloads:\n *\n * 1. **Signal Wrapping**: Each component is wrapped in a reactive signal\n * 2. **Signature Tracking**: Components are tracked by code signatures (from babel plugin)\n * 3. **Precise Updates**: Only components with changed signatures are updated\n * 4. **Effect Subscription**: Component instances subscribe to signal changes via effects\n * 5. **Bundler Agnostic**: Works with Vite, Webpack, Rspack, and other bundlers\n */\nimport { createComponent, effect, signal } from 'essor';\n\nconst isFunction = (value) => typeof value === 'function';\n/**\n * Global component registry for HMR tracking\n *\n * Maps hmrId -> ComponentInfo where:\n * - hmrId: Unique identifier for component (fileHash:componentName)\n * - componentSignal: Reactive signal holding the current component function\n * - signature: Hash of component code (changes when code changes)\n * - instances: Set of active component instances\n * - cleanups: Map of cleanup functions for each instance's effect\n */\nconst componentRegistry = new Map();\n\n/**\n * Utility function to handle invalidate or reload\n * @param hot - Hot module API object\n */\nfunction invalidateOrReload(hot) {\n if (isFunction(hot?.invalidate)) {\n hot.invalidate();\n } else if (typeof location !== 'undefined') {\n location.reload();\n }\n}\n\n/**\n * Create HMR-enabled component wrapper\n *\n * This function wraps a component to enable HMR:\n * 1. Creates or retrieves component registry entry\n * 2. Wraps component function to always read from latest signal value\n * 3. Sets up reactive effect to auto-update component when signal changes\n * 4. Returns regular component instance that auto-updates on HMR\n *\n * @param componentFn - Component function with __hmrId and __signature\n * @param props - Component props\n * @returns Component instance that responds to HMR updates\n */\nexport function createHMRComponent(componentFn, props) {\n const { __hmrId: hmrId, __signature: signature } = componentFn;\n\n if (!hmrId) {\n // If no hmrId, create normal component\n return createComponent(componentFn, props);\n }\n\n let info = componentRegistry.get(hmrId);\n\n if (!info) {\n // First registration: create signal wrapped component\n info = {\n componentSignal: signal(componentFn),\n signature,\n instances: new Set(), // Track all instances\n cleanups: new Map(), // Store cleanup for each instance\n };\n componentRegistry.set(hmrId, info);\n }\n\n // Create Component instance\n const component = createComponent(componentFn, props);\n // Track this instance\n info.instances.add(component);\n\n // Create effect for this Component instance\n // The effect subscribes to componentSignal and updates the component\n // We read the signal value immediately to establish the dependency,\n // but only trigger updates on subsequent changes\n let initialized = false;\n const cleanup = effect(() => {\n // Read signal value to establish dependency\n const currentComponentFn = info.componentSignal.value;\n // Skip the initialization run - only update on actual changes\n if (!initialized) {\n initialized = true;\n return;\n }\n\n // Update this specific instance when signal changes\n try {\n component.component = currentComponentFn;\n component.forceUpdate();\n } catch (error) {\n console.error(`[Essor HMR] Failed to update component instance:`, error);\n }\n });\n\n // Store cleanup function for this instance\n info.cleanups.set(component, cleanup);\n\n // Integrate with component lifecycle - cleanup when component is destroyed\n // Store the original onBeforeUnmount handler if it exists\n const originalOnBeforeUnmount = component.onBeforeUnmount;\n component.onBeforeUnmount = function () {\n // Call original handler first\n if (originalOnBeforeUnmount) {\n originalOnBeforeUnmount.call(this);\n }\n // Cleanup HMR effect\n const cleanupFn = info.cleanups.get(component);\n if (cleanupFn) {\n cleanupFn();\n info.cleanups.delete(component);\n }\n // Remove from instances tracking\n info.instances.delete(component);\n };\n\n return component;\n}\n\n/**\n * Determine if a component needs to be updated\n *\n * A component should update if:\n * 1. Function instance changed (indicates module was re-executed)\n * 2. Signature changed (indicates component code was modified)\n *\n * @param oldInfo - Existing component registry info\n * @param newComponentFn - New component function from updated module\n * @param newSignature - New signature hash from updated module\n * @returns true if component should update\n */\nfunction shouldUpdate(oldInfo, newComponentFn, newSignature) {\n if (!oldInfo) return true;\n\n // Check function instance (handles constant updates via module re-execution)\n const oldFn = oldInfo.componentSignal.value;\n if (oldFn !== newComponentFn) {\n return true;\n }\n\n // Check compile-time signature (handles component code changes)\n return oldInfo.signature !== newSignature;\n}\n\n/**\n * Check if a value is an HMR-enabled component\n *\n * @param value - Value to check\n * @returns true if value is a function with __hmrId property\n */\nfunction isHMRComponent(value) {\n return value && isFunction(value) && value.__hmrId;\n}\n\n/**\n * Apply HMR updates to components\n *\n * Iterates through the new component registry and updates signals\n * for components whose signatures have changed. This triggers\n * reactive effects in component instances, causing them to re-render.\n *\n * @param registry - Array of components from updated module\n * @returns true if reload needed (errors occurred), false otherwise\n */\nexport function applyUpdate(registry) {\n if (!Array.isArray(registry) || registry.length === 0) {\n return false;\n }\n\n let needsReload = false;\n\n for (const entry of registry) {\n const { __hmrId: hmrId, __signature: signature } = entry;\n const id = hmrId;\n const info = componentRegistry.get(hmrId);\n\n if (!info) {\n // New component, skip (will be registered on first render)\n continue;\n }\n\n // Use shouldUpdate to determine if update is needed\n if (!shouldUpdate(info, entry, signature)) {\n continue;\n }\n\n // Component changed, apply update\n try {\n info.signature = signature;\n info.componentSignal.value = entry;\n } catch (error) {\n console.error(`[Essor HMR] Failed to update ${id}:`, error);\n needsReload = true;\n }\n }\n\n return needsReload;\n}\n\n/**\n * Common handler for HMR updates across bundlers\n * @param hot - Hot module API\n * @param newModule - Updated module\n * @returns true if handled successfully\n */\nfunction handleHMRUpdate(hot, newModule) {\n if (!newModule) {\n invalidateOrReload(hot);\n return false;\n }\n\n // Extract HMR components from new module\n const newRegistry = extractHMRComponents(newModule);\n if (newRegistry.length === 0) {\n return true;\n }\n\n const needsReload = applyUpdate(newRegistry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n return true;\n}\n\n/**\n * Setup HMR for Vite bundler\n *\n * Vite provides import.meta.hot.accept() callback that receives the new module\n */\nfunction setupViteHMR(hot) {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n}\n\n/**\n * Setup HMR for Webpack/Rspack bundlers\n *\n * Webpack-style HMR uses module.hot.accept() and hot.data for state persistence\n */\nfunction setupWebpackHMR(hot, registry) {\n if (isFunction(hot.accept)) {\n hot.accept();\n }\n\n // Apply update if previous data exists from last hot reload\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n\n // Save current registry for next update\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n });\n }\n}\n\n/**\n * Setup HMR for other bundlers (fallback)\n *\n * Attempts to work with any bundler that provides a basic hot module API\n */\nfunction setupStandardHMR(hot, registry) {\n // Try accept callback mode first (more efficient)\n if (isFunction(hot.accept)) {\n try {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n } catch {\n // Some bundlers don't support accept with callback\n // Fall back to simple accept (for Webpack-style pattern)\n try {\n hot.accept();\n } catch (error_) {\n console.warn('[Essor HMR] Failed to setup hot.accept:', error_);\n }\n }\n }\n\n // Setup dispose handler for state persistence\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n data.__essor_timestamp__ = Date.now();\n });\n }\n\n // Apply update if previous data exists (for Webpack-style HMR)\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n}\n\n/**\n * Extract HMR components from a module\n *\n * Looks for __$registry$__ array first (generated by babel plugin),\n * then falls back to scanning all exports for HMR components\n *\n * @param module - Module object to scan\n * @returns Array of HMR component functions\n */\nfunction extractHMRComponents(module) {\n if (!module) return [];\n\n // Prefer __$registry$__\n if (Array.isArray(module.__$registry$__)) {\n return module.__$registry$__;\n }\n\n // Otherwise search in exports\n const components = [];\n for (const key of Object.keys(module)) {\n const value = module[key];\n if (isHMRComponent(value)) {\n components.push(value);\n }\n }\n return components;\n}\n\n/**\n * Main HMR entry point\n *\n * Called from transformed modules to set up HMR based on bundler type\n *\n * @param bundlerType - Type of bundler (vite, webpack5, rspack, etc.)\n * @param hot - Hot module API object (import.meta.hot or module.hot)\n * @param registry - Array of components from current module\n * @returns true if HMR setup succeeded, false otherwise\n */\nexport function hmrAccept(bundlerType, hot, registry) {\n if (!hot || !registry || registry.length === 0) {\n return false;\n }\n\n switch (bundlerType) {\n case 'vite':\n setupViteHMR(hot, registry);\n break;\n case 'webpack':\n case 'rspack':\n setupWebpackHMR(hot, registry);\n break;\n default:\n // Use standard HMR setup\n setupStandardHMR(hot, registry);\n break;\n }\n\n return true;\n}\n\n/**\n * Cleanup all instances of a component (utility function)\n *\n * @param hmrId - Component HMR ID\n * @returns Number of instances cleaned up\n */\nexport function unregisterAllInstances(hmrId) {\n const info = componentRegistry.get(hmrId);\n if (!info) return 0;\n\n let count = 0;\n for (const item of info.instances) {\n const cleanup = info.cleanups.get(item);\n if (cleanup) {\n try {\n cleanup();\n } catch (error) {\n console.error(`[Essor HMR] Failed to cleanup effect:`, error);\n }\n }\n count++;\n }\n\n info.instances.clear();\n\n return count;\n}\n\n/**\n * Get registry information for debugging\n *\n * @returns Object mapping hmrId to component info (signature, instance count)\n */\nexport function getRegistryInfo() {\n const info = {};\n for (const [id, data] of componentRegistry) {\n info[id] = {\n signature: data.signature,\n instanceCount: data.instances.size,\n };\n }\n return info;\n}\n","import { createUnplugin } from 'unplugin';\nimport * as babel from '@babel/core';\nimport essorBabelPlugin from 'babel-plugin-essor';\nimport { createFilter } from 'vite';\n// @ts-ignore - resolved by esbuild raw plugin at build time\nimport hmrRuntimeCode from './hmr-runtime.js?raw';\nimport type { UnpluginContextMeta, UnpluginFactory } from 'unplugin';\nimport type { Options } from './types';\n\n/**\n * Virtual module ID for HMR runtime\n * Injected as an import in transformed files that have HMR components\n */\nconst VIRTUAL_MODULE_ID = 'virtual:essor-hmr';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0virtual:essor-hmr';\n\n/**\n * Default plugin options\n */\nconst DEFAULT_OPTIONS = {\n symbol: '$',\n mode: 'client',\n props: true,\n hmr: true,\n enableFor: false,\n};\n\n/**\n * Performance: Pre-compiled regex and constants\n */\nconst FILE_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/i;\nconst SKIP_DIRECTORIES = ['node_modules', 'dist', 'public'];\n\ntype BundlerType = 'vite' | 'webpack5' | 'rspack' | 'rollup' | 'esbuild' | 'standard';\n\n/**\n * Detect bundler type from unplugin meta or environment variables\n *\n * This is important for HMR because different bundlers have different\n * HMR APIs (import.meta.hot.accept, module.hot.accept, etc.)\n */\nfunction detectBundler(meta: UnpluginContextMeta): BundlerType {\n // First, try to detect from unplugin meta\n if (meta?.framework) {\n switch (meta.framework) {\n case 'vite':\n return 'vite';\n case 'webpack':\n return 'webpack5';\n case 'rspack':\n return 'rspack';\n case 'rollup':\n return 'rollup';\n case 'esbuild':\n return 'esbuild';\n }\n }\n\n // Fallback: detect from environment variables\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.VITE || process.env.VITEST) return 'vite';\n if (process.env.WEBPACK_VERSION) return 'webpack5';\n if (process.env.RSPACK) return 'rspack';\n }\n\n // Default to standard if detection fails\n return 'standard';\n}\n\n/**\n * Generate HMR boilerplate code for a specific bundler\n *\n * @param bundlerType - The bundler type detected\n * @returns Object with imports and registration code\n */\nfunction generateHMRCode(bundlerType: BundlerType) {\n // Import HMR utilities from virtual module\n const imports = [\n `import { createHMRComponent as __$createHMRComponent$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n `import { hmrAccept as __$hmrAccept$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n ];\n\n // Generate HMR acceptance code\n // The registry array (__$registry$__) is generated by the babel plugin\n const register = [\n 'if (import.meta.hot) {',\n ` __$hmrAccept$__(\"${bundlerType}\", import.meta.hot, __$registry$__);`,\n ' import.meta.hot?.accept()',\n '}',\n ].join('\\n');\n\n return {\n importsCreateHMRComponent: `${imports[0]}\\n`,\n importHmrAccept: `${imports[1]}\\n`,\n register,\n };\n}\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (\n options: Options = {},\n meta,\n) => {\n // Create file filter based on include/exclude patterns\n const filter = createFilter(options.include, options.exclude);\n\n // Detect bundler type for HMR\n const bundlerType = detectBundler(meta);\n\n // Merge user options with defaults\n const finalOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n bundler: bundlerType, // Pass bundler type to babel plugin\n };\n\n return {\n name: 'unplugin-essor',\n\n /**\n * Vite-specific config to preserve JSX so the babel plugin can handle it.\n \n */\n vite: {\n config(this: unknown) {\n // Inside a vite hook, `this` is the rollup plugin context. On Vite 8 /\n // rolldown-vite it exposes `meta.rolldownVersion`.\n const ctx = this as { meta?: Record<string, unknown> } | undefined;\n const isRolldownVite = !!ctx?.meta && 'rolldownVersion' in ctx.meta;\n const key = (isRolldownVite ? 'oxc' : 'esbuild') as 'esbuild';\n return {\n [key]: { jsx: 'preserve' },\n };\n },\n },\n\n /**\n * Resolve virtual HMR runtime module\n */\n resolveId(id: string) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n return null;\n },\n\n /**\n * Load virtual HMR runtime module\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return {\n code: hmrRuntimeCode,\n map: null,\n };\n }\n return null;\n },\n rolldown: {\n options(opts) {\n opts.transform ??= {\n jsx: 'preserve',\n };\n },\n },\n /**\n * Transform code with Babel plugin\n */\n transform(code, id) {\n // Skip node_modules, dist, and public directories\n if (SKIP_DIRECTORIES.some((p) => id.includes(p))) {\n return;\n }\n\n // Only transform JS/TS files\n if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {\n return null;\n }\n\n const babelOptions = { ...finalOptions };\n\n // Transform with Babel (with error handling)\n let result;\n try {\n result = babel.transformSync(code, {\n filename: id,\n sourceMaps: true,\n sourceType: 'module',\n plugins: [[essorBabelPlugin, babelOptions]],\n });\n } catch (error) {\n console.error(`[unplugin-essor] Transform failed for ${id}:`, error);\n return null;\n }\n\n if (!result?.code) {\n return code;\n }\n\n // Determine if HMR should be enabled\n // HMR is only for client-side code with components\n const hmrEnabled =\n babelOptions.hmr && (babelOptions.mode === 'client' || babelOptions.mode === 'ssr');\n\n let finalCode = '';\n\n // Inject HMR code if enabled and components are present\n if (hmrEnabled) {\n const hmrCode = generateHMRCode(bundlerType);\n\n if (result.code.includes('__$createHMRComponent$__')) {\n finalCode = `${hmrCode.importsCreateHMRComponent}\\n${finalCode}`;\n }\n finalCode += result.code;\n if (result.code.includes('__$registry$__')) {\n finalCode = `${hmrCode.importHmrAccept}\\n${finalCode}\\n${hmrCode.register}`;\n }\n } else {\n finalCode += result.code;\n }\n\n return {\n code: finalCode,\n map: result.map,\n };\n },\n };\n};\n\nexport const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["raw-loader:/home/runner/work/essor/essor/packages/unplugin/src/hmr-runtime.js?raw","../src/index.ts"],"names":[],"mappings":"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","file":"chunk-RU3ODIHJ.js","sourcesContent":["/**\n * Essor HMR Runtime - Signal-based Hot Module Replacement\n *\n * This runtime enables precise component-level HMR updates without full page reloads:\n *\n * 1. **Signal Wrapping**: Each component is wrapped in a reactive signal\n * 2. **Signature Tracking**: Components are tracked by code signatures (from babel plugin)\n * 3. **Precise Updates**: Only components with changed signatures are updated\n * 4. **Effect Subscription**: Component instances subscribe to signal changes via effects\n * 5. **Bundler Agnostic**: Works with Vite, Webpack, Rspack, and other bundlers\n */\nimport { createComponent, effect, signal } from 'essor';\n\nconst isFunction = (value) => typeof value === 'function';\n/**\n * Global component registry for HMR tracking\n *\n * Maps hmrId -> ComponentInfo where:\n * - hmrId: Unique identifier for component (fileHash:componentName)\n * - componentSignal: Reactive signal holding the current component function\n * - signature: Hash of component code (changes when code changes)\n * - instances: Set of active component instances\n * - cleanups: Map of cleanup functions for each instance's effect\n */\nconst componentRegistry = new Map();\n\n/**\n * Utility function to handle invalidate or reload\n * @param hot - Hot module API object\n */\nfunction invalidateOrReload(hot) {\n if (isFunction(hot?.invalidate)) {\n hot.invalidate();\n } else if (typeof location !== 'undefined') {\n location.reload();\n }\n}\n\n/**\n * Create HMR-enabled component wrapper\n *\n * This function wraps a component to enable HMR:\n * 1. Creates or retrieves component registry entry\n * 2. Wraps component function to always read from latest signal value\n * 3. Sets up reactive effect to auto-update component when signal changes\n * 4. Returns regular component instance that auto-updates on HMR\n *\n * @param componentFn - Component function with __hmrId and __signature\n * @param props - Component props\n * @returns Component instance that responds to HMR updates\n */\nexport function createHMRComponent(componentFn, props) {\n const { __hmrId: hmrId, __signature: signature } = componentFn;\n\n if (!hmrId) {\n // If no hmrId, create normal component\n return createComponent(componentFn, props);\n }\n\n let info = componentRegistry.get(hmrId);\n\n if (!info) {\n // First registration: create signal wrapped component\n info = {\n componentSignal: signal(componentFn),\n signature,\n instances: new Set(), // Track all instances\n cleanups: new Map(), // Store cleanup for each instance\n };\n componentRegistry.set(hmrId, info);\n }\n\n // Create Component instance\n const component = createComponent(componentFn, props);\n // Track this instance\n info.instances.add(component);\n\n // Create effect for this Component instance\n // The effect subscribes to componentSignal and updates the component\n // We read the signal value immediately to establish the dependency,\n // but only trigger updates on subsequent changes\n let initialized = false;\n const cleanup = effect(() => {\n // Read signal value to establish dependency\n const currentComponentFn = info.componentSignal.value;\n // Skip the initialization run - only update on actual changes\n if (!initialized) {\n initialized = true;\n return;\n }\n\n // Update this specific instance when signal changes\n try {\n component.component = currentComponentFn;\n component.forceUpdate();\n } catch (error) {\n console.error(`[Essor HMR] Failed to update component instance:`, error);\n }\n });\n\n // Store cleanup function for this instance\n info.cleanups.set(component, cleanup);\n\n // Integrate with component lifecycle - cleanup when component is destroyed\n // Store the original onBeforeUnmount handler if it exists\n const originalOnBeforeUnmount = component.onBeforeUnmount;\n component.onBeforeUnmount = function () {\n // Call original handler first\n if (originalOnBeforeUnmount) {\n originalOnBeforeUnmount.call(this);\n }\n // Cleanup HMR effect\n const cleanupFn = info.cleanups.get(component);\n if (cleanupFn) {\n cleanupFn();\n info.cleanups.delete(component);\n }\n // Remove from instances tracking\n info.instances.delete(component);\n };\n\n return component;\n}\n\n/**\n * Determine if a component needs to be updated\n *\n * A component should update if:\n * 1. Function instance changed (indicates module was re-executed)\n * 2. Signature changed (indicates component code was modified)\n *\n * @param oldInfo - Existing component registry info\n * @param newComponentFn - New component function from updated module\n * @param newSignature - New signature hash from updated module\n * @returns true if component should update\n */\nfunction shouldUpdate(oldInfo, newComponentFn, newSignature) {\n if (!oldInfo) return true;\n\n // Check function instance (handles constant updates via module re-execution)\n const oldFn = oldInfo.componentSignal.value;\n if (oldFn !== newComponentFn) {\n return true;\n }\n\n // Check compile-time signature (handles component code changes)\n return oldInfo.signature !== newSignature;\n}\n\n/**\n * Check if a value is an HMR-enabled component\n *\n * @param value - Value to check\n * @returns true if value is a function with __hmrId property\n */\nfunction isHMRComponent(value) {\n return value && isFunction(value) && value.__hmrId;\n}\n\n/**\n * Apply HMR updates to components\n *\n * Iterates through the new component registry and updates signals\n * for components whose signatures have changed. This triggers\n * reactive effects in component instances, causing them to re-render.\n *\n * @param registry - Array of components from updated module\n * @returns true if reload needed (errors occurred), false otherwise\n */\nexport function applyUpdate(registry) {\n if (!Array.isArray(registry) || registry.length === 0) {\n return false;\n }\n\n let needsReload = false;\n\n for (const entry of registry) {\n const { __hmrId: hmrId, __signature: signature } = entry;\n const id = hmrId;\n const info = componentRegistry.get(hmrId);\n\n if (!info) {\n // New component, skip (will be registered on first render)\n continue;\n }\n\n // Use shouldUpdate to determine if update is needed\n if (!shouldUpdate(info, entry, signature)) {\n continue;\n }\n\n // Component changed, apply update\n try {\n info.signature = signature;\n info.componentSignal.value = entry;\n } catch (error) {\n console.error(`[Essor HMR] Failed to update ${id}:`, error);\n needsReload = true;\n }\n }\n\n return needsReload;\n}\n\n/**\n * Common handler for HMR updates across bundlers\n * @param hot - Hot module API\n * @param newModule - Updated module\n * @returns true if handled successfully\n */\nfunction handleHMRUpdate(hot, newModule) {\n if (!newModule) {\n invalidateOrReload(hot);\n return false;\n }\n\n // Extract HMR components from new module\n const newRegistry = extractHMRComponents(newModule);\n if (newRegistry.length === 0) {\n return true;\n }\n\n const needsReload = applyUpdate(newRegistry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n return true;\n}\n\n/**\n * Setup HMR for Vite bundler\n *\n * Vite provides import.meta.hot.accept() callback that receives the new module\n */\nfunction setupViteHMR(hot) {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n}\n\n/**\n * Setup HMR for Webpack/Rspack bundlers\n *\n * Webpack-style HMR uses module.hot.accept() and hot.data for state persistence\n */\nfunction setupWebpackHMR(hot, registry) {\n if (isFunction(hot.accept)) {\n hot.accept();\n }\n\n // Apply update if previous data exists from last hot reload\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n\n // Save current registry for next update\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n });\n }\n}\n\n/**\n * Setup HMR for other bundlers (fallback)\n *\n * Attempts to work with any bundler that provides a basic hot module API\n */\nfunction setupStandardHMR(hot, registry) {\n // Try accept callback mode first (more efficient)\n if (isFunction(hot.accept)) {\n try {\n hot.accept((newModule) => handleHMRUpdate(hot, newModule));\n } catch {\n // Some bundlers don't support accept with callback\n // Fall back to simple accept (for Webpack-style pattern)\n try {\n hot.accept();\n } catch (error_) {\n console.warn('[Essor HMR] Failed to setup hot.accept:', error_);\n }\n }\n }\n\n // Setup dispose handler for state persistence\n if (isFunction(hot.dispose)) {\n hot.dispose((data) => {\n data.__$registry$__ = registry;\n data.__essor_timestamp__ = Date.now();\n });\n }\n\n // Apply update if previous data exists (for Webpack-style HMR)\n if (hot.data?.__$registry$__) {\n const needsReload = applyUpdate(registry);\n if (needsReload) {\n invalidateOrReload(hot);\n }\n }\n}\n\n/**\n * Extract HMR components from a module\n *\n * Looks for __$registry$__ array first (generated by babel plugin),\n * then falls back to scanning all exports for HMR components\n *\n * @param module - Module object to scan\n * @returns Array of HMR component functions\n */\nfunction extractHMRComponents(module) {\n if (!module) return [];\n\n // Prefer __$registry$__\n if (Array.isArray(module.__$registry$__)) {\n return module.__$registry$__;\n }\n\n // Otherwise search in exports\n const components = [];\n for (const key of Object.keys(module)) {\n const value = module[key];\n if (isHMRComponent(value)) {\n components.push(value);\n }\n }\n return components;\n}\n\n/**\n * Main HMR entry point\n *\n * Called from transformed modules to set up HMR based on bundler type\n *\n * @param bundlerType - Type of bundler (vite, webpack5, rspack, etc.)\n * @param hot - Hot module API object (import.meta.hot or module.hot)\n * @param registry - Array of components from current module\n * @returns true if HMR setup succeeded, false otherwise\n */\nexport function hmrAccept(bundlerType, hot, registry) {\n if (!hot || !registry || registry.length === 0) {\n return false;\n }\n\n switch (bundlerType) {\n case 'vite':\n setupViteHMR(hot, registry);\n break;\n case 'webpack':\n case 'rspack':\n setupWebpackHMR(hot, registry);\n break;\n default:\n // Use standard HMR setup\n setupStandardHMR(hot, registry);\n break;\n }\n\n return true;\n}\n\n/**\n * Cleanup all instances of a component (utility function)\n *\n * @param hmrId - Component HMR ID\n * @returns Number of instances cleaned up\n */\nexport function unregisterAllInstances(hmrId) {\n const info = componentRegistry.get(hmrId);\n if (!info) return 0;\n\n let count = 0;\n for (const item of info.instances) {\n const cleanup = info.cleanups.get(item);\n if (cleanup) {\n try {\n cleanup();\n } catch (error) {\n console.error(`[Essor HMR] Failed to cleanup effect:`, error);\n }\n }\n count++;\n }\n\n info.instances.clear();\n\n return count;\n}\n\n/**\n * Get registry information for debugging\n *\n * @returns Object mapping hmrId to component info (signature, instance count)\n */\nexport function getRegistryInfo() {\n const info = {};\n for (const [id, data] of componentRegistry) {\n info[id] = {\n signature: data.signature,\n instanceCount: data.instances.size,\n };\n }\n return info;\n}\n","import { createUnplugin } from 'unplugin';\nimport * as babel from '@babel/core';\nimport essorBabelPlugin from 'babel-plugin-essor';\nimport { createFilter } from 'vite';\n// @ts-ignore - resolved by esbuild raw plugin at build time\nimport hmrRuntimeCode from './hmr-runtime.js?raw';\nimport type { UnpluginContextMeta, UnpluginFactory } from 'unplugin';\nimport type { Options } from './types';\n\n/**\n * Virtual module ID for HMR runtime\n * Injected as an import in transformed files that have HMR components\n */\nconst VIRTUAL_MODULE_ID = 'virtual:essor-hmr';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0virtual:essor-hmr';\n\n/**\n * Default plugin options\n */\nconst DEFAULT_OPTIONS = {\n symbol: '$',\n mode: 'client',\n props: true,\n hmr: true,\n enableFor: false,\n};\n\n/**\n * Performance: Pre-compiled regex and constants\n */\nconst FILE_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/i;\nconst SKIP_DIRECTORIES = ['node_modules', 'dist', 'public'];\n\ntype BundlerType = 'vite' | 'webpack5' | 'rspack' | 'rollup' | 'esbuild' | 'standard';\n\n/**\n * Detect bundler type from unplugin meta or environment variables\n *\n * This is important for HMR because different bundlers have different\n * HMR APIs (import.meta.hot.accept, module.hot.accept, etc.)\n */\nfunction detectBundler(meta: UnpluginContextMeta): BundlerType {\n // First, try to detect from unplugin meta\n if (meta?.framework) {\n switch (meta.framework) {\n case 'vite':\n return 'vite';\n case 'webpack':\n return 'webpack5';\n case 'rspack':\n return 'rspack';\n case 'rollup':\n return 'rollup';\n case 'esbuild':\n return 'esbuild';\n }\n }\n\n // Fallback: detect from environment variables\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.VITE || process.env.VITEST) return 'vite';\n if (process.env.WEBPACK_VERSION) return 'webpack5';\n if (process.env.RSPACK) return 'rspack';\n }\n\n // Default to standard if detection fails\n return 'standard';\n}\n\n/**\n * Generate HMR boilerplate code for a specific bundler\n *\n * @param bundlerType - The bundler type detected\n * @returns Object with imports and registration code\n */\nfunction generateHMRCode(bundlerType: BundlerType) {\n // Import HMR utilities from virtual module\n const imports = [\n `import { createHMRComponent as __$createHMRComponent$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n `import { hmrAccept as __$hmrAccept$__ } from \"${VIRTUAL_MODULE_ID}\";`,\n ];\n\n // Generate HMR acceptance code\n // The registry array (__$registry$__) is generated by the babel plugin\n const register = [\n 'if (import.meta.hot) {',\n ` __$hmrAccept$__(\"${bundlerType}\", import.meta.hot, __$registry$__);`,\n ' import.meta.hot?.accept()',\n '}',\n ].join('\\n');\n\n return {\n importsCreateHMRComponent: `${imports[0]}\\n`,\n importHmrAccept: `${imports[1]}\\n`,\n register,\n };\n}\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (\n options: Options = {},\n meta,\n) => {\n // Create file filter based on include/exclude patterns\n const filter = createFilter(options.include, options.exclude);\n\n // Detect bundler type for HMR\n const bundlerType = detectBundler(meta);\n\n // Merge user options with defaults\n const finalOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n bundler: bundlerType, // Pass bundler type to babel plugin\n };\n\n return {\n name: 'unplugin-essor',\n\n /**\n * Vite-specific config to preserve JSX so the babel plugin can handle it.\n \n */\n vite: {\n config(this: unknown) {\n // Inside a vite hook, `this` is the rollup plugin context. On Vite 8 /\n // rolldown-vite it exposes `meta.rolldownVersion`.\n const ctx = this as { meta?: Record<string, unknown> } | undefined;\n const isRolldownVite = !!ctx?.meta && 'rolldownVersion' in ctx.meta;\n const key = (isRolldownVite ? 'oxc' : 'esbuild') as 'esbuild';\n return {\n [key]: { jsx: 'preserve' },\n };\n },\n },\n\n /**\n * Resolve virtual HMR runtime module\n */\n resolveId(id: string) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n return null;\n },\n\n /**\n * Load virtual HMR runtime module\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return {\n code: hmrRuntimeCode,\n map: null,\n };\n }\n return null;\n },\n rolldown: {\n options(opts) {\n opts.transform ??= {\n jsx: 'preserve',\n };\n },\n },\n /**\n * Transform code with Babel plugin\n */\n transform(code, id) {\n // Skip node_modules, dist, and public directories\n if (SKIP_DIRECTORIES.some((p) => id.includes(p))) {\n return;\n }\n\n // Only transform JS/TS files\n if (!filter(id) || !FILE_EXTENSION_REGEX.test(id)) {\n return null;\n }\n\n const babelOptions = { ...finalOptions };\n\n // Transform with Babel (with error handling)\n let result;\n try {\n result = babel.transformSync(code, {\n filename: id,\n sourceMaps: true,\n sourceType: 'module',\n plugins: [[essorBabelPlugin, babelOptions]],\n });\n } catch (error) {\n console.error(`[unplugin-essor] Transform failed for ${id}:`, error);\n return null;\n }\n\n if (!result?.code) {\n return code;\n }\n\n // Determine if HMR should be enabled\n // HMR is only for client-side code with components\n const hmrEnabled =\n babelOptions.hmr && (babelOptions.mode === 'client' || babelOptions.mode === 'ssr');\n\n let finalCode = '';\n\n // Inject HMR code if enabled and components are present\n if (hmrEnabled) {\n const hmrCode = generateHMRCode(bundlerType);\n\n if (result.code.includes('__$createHMRComponent$__')) {\n finalCode = `${hmrCode.importsCreateHMRComponent}\\n${finalCode}`;\n }\n finalCode += result.code;\n if (result.code.includes('__$registry$__')) {\n finalCode = `${hmrCode.importHmrAccept}\\n${finalCode}\\n${hmrCode.register}`;\n }\n } else {\n finalCode += result.code;\n }\n\n return {\n code: finalCode,\n map: result.map,\n };\n },\n };\n};\n\nexport const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"]}
|