Skip to content

Commit c2d4ab1

Browse files
committed
fix(core): require user-installed esbuild for config.ts; show install hint if missing
1 parent c994365 commit c2d4ab1

File tree

1 file changed

+39
-95
lines changed

1 file changed

+39
-95
lines changed

core/config/load.ts

Lines changed: 39 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
ModelRole,
1111
} from "@continuedev/config-yaml";
1212
import * as JSONC from "comment-json";
13-
import * as tar from "tar";
1413

1514
import {
1615
BrowserSerializedContinueConfig,
@@ -696,102 +695,43 @@ function escapeSpacesInPath(p: string): string {
696695
return p.replace(/ /g, "\\ ");
697696
}
698697

699-
async function handleEsbuildInstallation(ide: IDE, ideType: IdeType) {
700-
// JetBrains is currently the only IDE that we've reached the plugin size limit and
701-
// therefore need to install esbuild manually to reduce the size
702-
if (ideType !== "jetbrains") {
703-
return;
704-
}
705-
706-
const globalContext = new GlobalContext();
707-
if (globalContext.get("hasDismissedConfigTsNoticeJetBrains")) {
708-
return;
709-
}
710-
711-
const esbuildPath = getEsbuildBinaryPath();
712-
713-
if (fs.existsSync(esbuildPath)) {
714-
return;
715-
}
716-
717-
console.debug("No esbuild binary detected");
718-
719-
const shouldInstall = await promptEsbuildInstallation(ide);
720-
721-
if (shouldInstall) {
722-
await downloadAndInstallEsbuild(ide);
723-
}
724-
}
725-
726-
async function promptEsbuildInstallation(ide: IDE): Promise<boolean> {
727-
const installMsg = "Install esbuild";
728-
const dismissMsg = "Dismiss";
729-
730-
const res = await ide.showToast(
731-
"warning",
732-
"You're using a custom 'config.ts' file, which requires 'esbuild' to be installed. Would you like to install it now?",
733-
dismissMsg,
734-
installMsg,
735-
);
736-
737-
if (res === dismissMsg) {
738-
const globalContext = new GlobalContext();
739-
globalContext.update("hasDismissedConfigTsNoticeJetBrains", true);
740-
return false;
741-
}
742-
743-
return res === installMsg;
744-
}
745-
746-
/**
747-
* The download logic is adapted from here: https://esbuild.github.io/getting-started/#download-a-build
748-
*/
749-
async function downloadAndInstallEsbuild(ide: IDE) {
750-
const esbuildPath = getEsbuildBinaryPath();
751-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "esbuild-"));
698+
async function handleEsbuildInstallation(
699+
ide: IDE,
700+
_ideType: IdeType,
701+
): Promise<boolean> {
702+
// Only check when config.ts is going to be used; never auto-install.
703+
const installCmd = "npm i [email protected] --prefix ~/.continue";
752704

705+
// Try to detect a user-installed esbuild (normal resolution)
753706
try {
754-
const target = `${os.platform()}-${os.arch()}`;
755-
const version = "0.19.11";
756-
const url = `https://registry.npmjs.org/@esbuild/${target}/-/${target}-${version}.tgz`;
757-
const tgzPath = path.join(tempDir, `esbuild-${version}.tgz`);
758-
759-
console.debug(`Downloading esbuild from: ${url}`);
760-
execSync(`curl -fo "${tgzPath}" "${url}"`);
761-
762-
console.debug(`Extracting tgz file to: ${tempDir}`);
763-
await tar.x({
764-
file: tgzPath,
765-
cwd: tempDir,
766-
strip: 2, // Remove the top two levels of directories
767-
});
768-
769-
// Ensure the destination directory exists
770-
const destDir = path.dirname(esbuildPath);
771-
if (!fs.existsSync(destDir)) {
772-
fs.mkdirSync(destDir, { recursive: true });
773-
}
774-
775-
// Move the file
776-
const extractedBinaryPath = path.join(tempDir, "esbuild");
777-
fs.renameSync(extractedBinaryPath, esbuildPath);
778-
779-
// Ensure the binary is executable (not needed on Windows)
780-
if (os.platform() !== "win32") {
781-
fs.chmodSync(esbuildPath, 0o755);
707+
await import("esbuild");
708+
return true; // available
709+
} catch {
710+
// Try resolving from ~/.continue/node_modules as a courtesy
711+
try {
712+
const userEsbuild = path.join(
713+
os.homedir(),
714+
".continue",
715+
"node_modules",
716+
"esbuild",
717+
);
718+
const candidate = require.resolve("esbuild", { paths: [userEsbuild] });
719+
// eslint-disable-next-line @typescript-eslint/no-var-requires
720+
require(candidate);
721+
return true; // available via ~/.continue
722+
} catch {
723+
// Not available → show friendly instructions and opt out of building
724+
await ide.showToast(
725+
"error",
726+
[
727+
"config.ts has been deprecated and esbuild is no longer automatically installed by Continue.",
728+
"To use config.ts, install esbuild manually:",
729+
"",
730+
` ${installCmd}`,
731+
].join("\n"),
732+
);
733+
return false;
782734
}
783-
784-
// Clean up
785-
fs.unlinkSync(tgzPath);
786-
fs.rmSync(tempDir, { recursive: true });
787-
788-
await ide.showToast(
789-
"info",
790-
`'esbuild' successfully installed to ${esbuildPath}`,
791-
);
792-
} catch (error) {
793-
console.error("Error downloading or saving esbuild binary:", error);
794-
throw error;
795735
}
796736
}
797737

@@ -866,7 +806,11 @@ async function buildConfigTsandReadConfigJs(ide: IDE, ideType: IdeType) {
866806
return;
867807
}
868808

869-
await handleEsbuildInstallation(ide, ideType);
809+
const ok = await handleEsbuildInstallation(ide, ideType);
810+
if (!ok) {
811+
// esbuild not available → we already showed a friendly message; skip building
812+
return;
813+
}
870814
await tryBuildConfigTs();
871815

872816
return readConfigJs();

0 commit comments

Comments
 (0)