|
1 | 1 | const path = require('path') |
| 2 | +const fs = require('fs') |
2 | 3 | const notifier = require('node-notifier') |
3 | 4 | const colors = require('chalk') |
4 | 5 | const messenger = require('@codedungeon/messenger') |
@@ -142,6 +143,63 @@ function getRollupConfig(options) { |
142 | 143 |
|
143 | 144 | const exportedFileVarName = options.bundleName || 'reactBundle' |
144 | 145 |
|
| 146 | + // Validate entry file exports before building |
| 147 | + const isRootBundle = exportedFileVarName.includes('Root') || exportedFileVarName.includes('RootBundle') |
| 148 | + try { |
| 149 | + const entryFileContent = fs.readFileSync(entryPointPath, 'utf8') |
| 150 | + |
| 151 | + if (isRootBundle) { |
| 152 | + // Root bundles should export React and ReactDOM |
| 153 | + const hasReact = /export\s+.*\bReact\b/.test(entryFileContent) || /export\s*\{[^}]*\bReact\b/.test(entryFileContent) |
| 154 | + const hasReactDOM = /export\s+.*\bReactDOM\b/.test(entryFileContent) || /export\s*\{[^}]*\bReactDOM\b/.test(entryFileContent) |
| 155 | + |
| 156 | + if (!hasReact || !hasReactDOM) { |
| 157 | + throw new Error( |
| 158 | + `\n❌ ROLLUP VALIDATION ERROR: Root bundle entry file "${opts.entryPointPath}" is missing required exports.\n\n` + |
| 159 | + `Root bundles must export React and ReactDOM for other bundles to use.\n\n` + |
| 160 | + `Expected exports in your entry file:\n` + |
| 161 | + ` - React (from 'react')\n` + |
| 162 | + ` - ReactDOM (from 'react-dom')\n\n` + |
| 163 | + `Example entry file:\n` + |
| 164 | + ` export { default as React } from 'react'\n` + |
| 165 | + ` export { default as ReactDOM } from 'react-dom'\n` + |
| 166 | + ` export { createRoot } from 'react-dom/client'\n` + |
| 167 | + ` // ... other exports\n\n` + |
| 168 | + `Entry file: ${entryPointPath}\n` |
| 169 | + ) |
| 170 | + } |
| 171 | + } else { |
| 172 | + // Non-Root bundles should export WebView |
| 173 | + const hasWebView = /export\s+.*\bWebView\b/.test(entryFileContent) || |
| 174 | + /export\s*\{[^}]*\bWebView\b/.test(entryFileContent) || |
| 175 | + /export\s*\{[^}]*as\s+WebView/.test(entryFileContent) |
| 176 | + |
| 177 | + if (!hasWebView) { |
| 178 | + throw new Error( |
| 179 | + `\n❌ ROLLUP VALIDATION ERROR: Entry file "${opts.entryPointPath}" is missing required WebView export.\n\n` + |
| 180 | + `All React component bundles (except Root) must export a component named "WebView".\n` + |
| 181 | + `This is what the Root component expects to load dynamically.\n\n` + |
| 182 | + `To fix this, update your entry file to export your component as WebView:\n\n` + |
| 183 | + `Option 1: Export your component as WebView directly:\n` + |
| 184 | + ` export { YourComponent as WebView } from './YourComponent.jsx'\n\n` + |
| 185 | + `Option 2: If your component is already named WebView:\n` + |
| 186 | + ` export { WebView } from './YourComponent.jsx'\n\n` + |
| 187 | + `Example entry file (rollup.YourComponent.entry.js):\n` + |
| 188 | + ` // Root expects a component called WebView\n` + |
| 189 | + ` export { YourComponent as WebView } from '../components/YourComponent.jsx'\n\n` + |
| 190 | + `Entry file: ${entryPointPath}\n` + |
| 191 | + `Bundle name: ${exportedFileVarName}\n` |
| 192 | + ) |
| 193 | + } |
| 194 | + } |
| 195 | + } catch (error) { |
| 196 | + if (error.code === 'ENOENT') { |
| 197 | + throw new Error(`Entry file not found: ${entryPointPath}`) |
| 198 | + } |
| 199 | + // Re-throw validation errors |
| 200 | + throw error |
| 201 | + } |
| 202 | + |
145 | 203 | // Map external module names to their global variable names |
146 | 204 | // React and ReactDOM are loaded by np.Shared's Root component |
147 | 205 | const externalGlobals = (externalModules || []).reduce((acc, cur) => { |
@@ -274,10 +332,9 @@ function getRollupConfig(options) { |
274 | 332 | // Assign bundle to global |
275 | 333 | footer = `Object.assign(typeof(globalThis) == "undefined" ? this : globalThis, ${exportedFileVarName});` |
276 | 334 |
|
277 | | - // Extract WebView to global scope (Root component expects it as a global) |
278 | | - if (exportedFileVarName.includes('FormView') || exportedFileVarName.includes('FormBuilderView')) { |
279 | | - footer += `\nif (typeof ${exportedFileVarName} !== 'undefined' && ${exportedFileVarName}.WebView) { typeof(globalThis) == "undefined" ? (this.WebView = ${exportedFileVarName}.WebView) : (globalThis.WebView = ${exportedFileVarName}.WebView); }` |
280 | | - } |
| 335 | + // Extract WebView to global scope if it exists (Root component expects it as a global) |
| 336 | + // This is generic - any bundle that exports WebView will have it extracted to global scope |
| 337 | + footer += `\nif (typeof ${exportedFileVarName} !== 'undefined' && ${exportedFileVarName}.WebView) { typeof(globalThis) == "undefined" ? (this.WebView = ${exportedFileVarName}.WebView) : (globalThis.WebView = ${exportedFileVarName}.WebView); }` |
281 | 338 |
|
282 | 339 | // Extract React and ReactDOM to global scope from Root bundle |
283 | 340 | // Root bundle now includes React and ReactDOM, and other bundles (like Forms) need them as globals |
|
0 commit comments