@stackbit/sdk
Version:
235 lines • 10.5 kB
JavaScript
;
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