convex
Version:
Client for the Convex Cloud
179 lines (178 loc) • 5.65 kB
JavaScript
;
import path from "path";
import esbuild from "esbuild";
import {
logError,
changeSpinner,
logFailure,
logVerbose,
logMessage
} from "./log.js";
import { wasmPlugin } from "./wasm.js";
import dependencyTrackerPlugin from "./depgraph.js";
export async function innerEsbuild({
entryPoints,
platform,
dir,
extraConditions,
generateSourceMaps,
plugins,
chunksFolder,
logLevel
}) {
const result = await esbuild.build({
entryPoints,
bundle: true,
platform,
format: "esm",
target: "esnext",
jsx: "automatic",
outdir: "out",
outbase: dir,
conditions: ["convex", "module", ...extraConditions],
plugins,
write: false,
sourcemap: generateSourceMaps,
splitting: true,
chunkNames: path.join(chunksFolder, "[hash]"),
treeShaking: true,
minifySyntax: true,
minifyIdentifiers: true,
// Enabling minifyWhitespace breaks sourcemaps on convex backends.
// The sourcemaps produced are valid on https://evanw.github.io/source-map-visualization
// but something we're doing (perhaps involving https://github.com/getsentry/rust-sourcemap)
// makes everything map to the same line.
minifyWhitespace: false,
// false is the default, just showing for clarify.
keepNames: true,
define: {
"process.env.NODE_ENV": '"production"'
},
metafile: true,
logLevel: logLevel || "warning"
});
return result;
}
export function isEsbuildBuildError(e) {
return "errors" in e && "warnings" in e && Array.isArray(e.errors) && Array.isArray(e.warnings);
}
export async function debugIsolateBundlesSerially(ctx, {
entryPoints,
extraConditions,
dir
}) {
logMessage(
`Bundling convex entry points one at a time to track down things that can't be bundled for the Convex JS runtime.`
);
let i = 1;
for (const entryPoint of entryPoints) {
changeSpinner(
`bundling entry point ${entryPoint} (${i++}/${entryPoints.length})...`
);
const { plugin, tracer } = dependencyTrackerPlugin();
try {
await innerEsbuild({
entryPoints: [entryPoint],
platform: "browser",
generateSourceMaps: true,
chunksFolder: "_deps",
extraConditions,
dir,
plugins: [plugin, wasmPlugin],
logLevel: "silent"
});
} catch (error) {
if (!isEsbuildBuildError(error) || !error.errors[0]) {
return await ctx.crash({
exitCode: 1,
errorType: "invalid filesystem data",
printedMessage: null
});
}
const buildError = error.errors[0];
const errorFile = buildError.location?.file;
if (!errorFile) {
return await ctx.crash({
exitCode: 1,
errorType: "invalid filesystem data",
printedMessage: null
});
}
const importedPath = buildError.text.match(/"([^"]+)"/)?.[1];
if (!importedPath) continue;
const full = path.resolve(errorFile);
logError("");
logError(
`Bundling ${entryPoint} resulted in ${error.errors.length} esbuild errors.`
);
logError(`One of the bundling errors occurred while bundling ${full}:
`);
logError(
esbuild.formatMessagesSync([buildError], {
kind: "error",
color: true
}).join("\n")
);
logError("It would help to avoid importing this file.");
const chains = tracer.traceImportChains(entryPoint, full);
const chain = chains[0];
chain.reverse();
logError(``);
if (chain.length > 0) {
const problematicFileRelative = formatFilePath(dir, chain[0]);
if (chain.length === 1) {
logError(` ${problematicFileRelative}`);
} else {
logError(` ${problematicFileRelative} is imported by`);
for (let i2 = 1; i2 < chain.length - 1; i2++) {
const fileRelative = formatFilePath(dir, chain[i2]);
logError(` ${fileRelative}, which is imported by`);
}
const entryPointFile = chain[chain.length - 1];
const entryPointRelative = formatFilePath(dir, entryPointFile);
logError(` ${entryPointRelative}, which doesn't use "use node"
`);
logError(
` For registered action functions to use Node.js APIs in any code they run they must be defined
in a file with 'use node' at the top. See https://docs.convex.dev/functions/runtimes#nodejs-runtime
`
);
}
}
logFailure("Bundling failed");
return await ctx.crash({
exitCode: 1,
errorType: "invalid filesystem data",
printedMessage: "Bundling failed."
});
}
logVerbose(`${entryPoint} bundled`);
}
}
function formatFilePath(baseDir, filePath) {
if (!path.isAbsolute(filePath)) {
if (!filePath.startsWith("convex/")) {
const cleanPath2 = filePath.replace(/^\.\//, "");
return `convex/${cleanPath2}`;
}
return filePath;
}
const relativePath = path.relative(baseDir, filePath);
const cleanPath = relativePath.replace(/^\.\//, "");
const isConvexPath = cleanPath.startsWith("convex/") || cleanPath.includes("/convex/") || path.dirname(cleanPath) === "convex";
if (isConvexPath) {
if (cleanPath.startsWith("convex/")) {
return cleanPath;
}
if (path.dirname(cleanPath) === "convex") {
const filename = path.basename(cleanPath);
return `convex/${filename}`;
}
const convexIndex = cleanPath.indexOf("convex/");
if (convexIndex >= 0) {
return cleanPath.substring(convexIndex);
}
}
return `convex/${cleanPath}`;
}
//# sourceMappingURL=debugBundle.js.map