UNPKG

@stackbit/sdk

Version:
235 lines 10.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadStackbitConfigFromJs = void 0; const esbuild = __importStar(require("esbuild")); const path = __importStar(require("path")); const fs = __importStar(require("fs-extra")); const os = __importStar(require("os")); const config_errors_1 = require("./config-errors"); const config_loader_static_1 = require("./config-loader-static"); async function loadStackbitConfigFromJs({ configPath, watch, callback, logger, outDir }) { let buildContext; try { // resolve config relative to cwd if it is not absolute configPath = path.resolve(configPath); logger = logger?.createLogger({ label: 'config-loader-esbuilt' }); logger?.debug(`building stackbit config from ${configPath}`); const projectDir = path.dirname(configPath); const fileName = path.basename(configPath); outDir = outDir ? path.resolve(projectDir, outDir) : outDir; // clean previously cached files if (outDir && (await fs.pathExists(outDir))) { // delete only files starting with "stackbit.config." as there may be other cached files const files = await fs.readdir(outDir); for (const file of files) { if (file.startsWith('stackbit.config.')) { await fs.remove(path.join(outDir, file)); } } } let isFirstBuild = true; let isReloading = false; const tempDir = outDir || (await fs.mkdtemp(path.join(os.tmpdir(), 'stackbit-config-'))); const useEsm = await configHasEsmFlag(configPath); const configExtension = useEsm ? `mjs` : `cjs`; const outfilePath = path.join(tempDir, 'stackbit.config.' + configExtension); buildContext = await esbuild.context({ entryPoints: [configPath], entryNames: '[name].[hash]', bundle: true, platform: 'node', target: 'es2021', outfile: outfilePath, sourcemap: true, format: useEsm ? 'esm' : 'cjs', jsx: 'transform', logLevel: 'silent', metafile: true, absWorkingDir: projectDir, packages: 'external', define: { __dirname: JSON.stringify(projectDir), __filename: JSON.stringify(configPath) }, plugins: watch ? [ { name: 'stackbit-esbuild-watch-plugin', setup(build) { build.onEnd(async (result) => { // The plugin's onEnd() function is called for first and successive builds, // including when calling buildContext.rebuild(). // But we don't want to invoke the callback for the first build or when // the rebuild() is called manually, because the result is returned from // the loadStackbitConfigFromJs() and rebuild() functions. if (isFirstBuild || isReloading) { return; } logger?.debug(`${fileName} was changed and rebuilt`); const configResult = await loadConfigFromBuildResult(result, fileName, projectDir, logger, useEsm); callback?.(configResult); }); } } ] : [] }); if (watch) { await buildContext.watch(); } const result = await buildContext.rebuild(); const configResult = await loadConfigFromBuildResult(result, fileName, projectDir, logger, useEsm); isFirstBuild = false; let destroyed = false; return { ...configResult, destroy: async () => { if (destroyed) { return; } destroyed = true; await buildContext.dispose(); }, reload: async (result) => { if (destroyed) { const message = `Error reloading Stackbit configuration, 'reload' called after 'destroy'`; logger?.debug(message); return { config: null, error: new config_errors_1.ConfigLoadError(message) }; } logger?.debug('reload stackbit config'); isReloading = true; try { result = result ?? (await buildContext.rebuild()); } catch (error) { logger?.error('error reloading stackbit config', { error }); return { config: null, error: new config_errors_1.ConfigLoadError(`Error reloading stackbit config: ${error.message}`, { originalError: error }) }; } finally { isReloading = false; } return loadConfigFromBuildResult(result, fileName, projectDir, logger, useEsm); } }; } catch (error) { buildContext?.dispose(); return { config: null, error: new config_errors_1.ConfigLoadError(`Error loading Stackbit configuration: ${error.message}`, { originalError: error }), destroy: async () => void 0 }; } } exports.loadStackbitConfigFromJs = loadStackbitConfigFromJs; async function loadConfigFromBuildResult(result, fileName, projectDir, logger, useEsm) { try { if (result.errors.length > 0) { const message = result.errors.reduce((message, error) => { const loc = error.location; if (loc) { message += `\n${loc.file}:${loc.line}:${loc.column}: ERROR: ${error.text}`; } else { message += `\n${error.text}`; } return message; }, `Error loading Stackbit configuration from ${fileName}. Build failed with ${result.errors.length} error${result.errors.length > 1 ? 's' : ''}:`); return { config: null, error: new config_errors_1.ConfigLoadError(message) }; } // TODO: if the loaded code has error it will provide sourcemaps; // (await import('source-map-support')).install() const importFresh = async (modulePath) => { if (useEsm) { return import(path.join('file://', `${modulePath}?c=${Math.random()}`)); } else { const resolvedModulePath = require.resolve(modulePath); delete require.cache[resolvedModulePath]; return require(resolvedModulePath); } }; const outfilePath = Object.keys(result.metafile.outputs).find((outputFilePath) => outputFilePath.match(/stackbit\.config\.[^.]+\.[cm]?js$/) !== null); const absOutputFilePath = path.join(projectDir, outfilePath); logger?.debug(`loading compiled ${fileName} from ${outfilePath}`); const exports = await importFresh(absOutputFilePath); if ('default' in exports) { // esm compiled config return { config: exports.default, error: null }; } else if ('__esModule' in exports && exports.__esModule) { if (!('default' in exports)) { return { config: null, error: new config_errors_1.ConfigLoadError(`Error loading Stackbit configuration, no default export found in ${fileName}`) }; } return { config: exports.default, error: null }; } return { config: exports, error: null }; } catch (error) { if (error.code === 'ERR_REQUIRE_ESM') { const message = `It appears that one of the external dependencies in ${fileName} is an ES Module. ` + `However, ${fileName} is compiled into a CommonJS module by default, preventing it from importing ES Modules. ` + `To compile stackbit.config.ts into ES Module, please add "\x1b[32museESM: true\x1b[0m" to the config. `; if (error.stack && typeof error.stack === 'string') { error.stack = message + error.stack; } if (error.stack && typeof error.message === 'string') { error.message = message + error.message; } } return { config: null, error: new config_errors_1.ConfigLoadError(`Error loading Stackbit configuration from ${fileName}: ${error.message}`, { originalError: error }) }; } } async function configHasEsmFlag(filePath) { const jsConfigString = await fs.readFile(filePath, 'utf-8'); const useEsm = (0, config_loader_static_1.parseInlineProperty)(jsConfigString, 'useESM'); return !!useEsm; } //# sourceMappingURL=config-loader-esbuild.js.map