@kya-os/mcp-i
Version:
The TypeScript MCP framework with identity features built-in
212 lines (211 loc) • 8.88 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = compile;
const webpack_1 = require("webpack");
const get_webpack_config_1 = require("./get-webpack-config");
// Simplified - remove chalk styling for now
const chalk = {
bold: {
green: (text) => text,
},
};
const parse_xmcp_config_1 = require("./parse-xmcp-config");
const generate_import_code_1 = require("./generate-import-code");
const fs_1 = __importDefault(require("fs"));
const constants_1 = require("../utils/constants");
const fs_utils_1 = require("../utils/fs-utils");
const path_1 = __importDefault(require("path"));
// Use fs.rmSync for cleanup (Node.js 14.14+)
const deleteSync = (path) => {
try {
if (fs_1.default.existsSync(path)) {
fs_1.default.rmSync(path, { recursive: true, force: true });
console.log(`Cleaned: ${path}`);
}
}
catch (error) {
console.log(`Could not clean ${path}:`, error instanceof Error ? error.message : String(error));
}
};
const dotenv_1 = __importDefault(require("dotenv"));
const generate_env_code_1 = require("./generate-env-code");
const file_watcher_1 = require("../utils/file-watcher");
const on_first_build_1 = require("./on-first-build");
const cli_icons_1 = require("../utils/cli-icons");
const compiler_context_1 = require("./compiler-context");
const start_http_server_1 = require("./start-http-server");
const path_validation_1 = require("../utils/path-validation");
const utils_1 = require("./config/utils");
dotenv_1.default.config();
async function compile({ onBuild } = {}) {
// Initialize compiler context if not already set
const mode = process.env.NODE_ENV === "production" ? "production" : "development";
// Use compiler context provider to set up the context properly
return new Promise((resolve, reject) => {
(0, compiler_context_1.compilerContextProvider)({
mode,
projectRoot: process.cwd(),
platforms: {},
}, () => {
compileInternal({ onBuild }).then(resolve).catch(reject);
});
});
}
async function compileInternal({ onBuild } = {}) {
const { mode } = compiler_context_1.compilerContext.getContext();
const { toolPaths } = compiler_context_1.compilerContext.getContext();
const startTime = Date.now();
let compilerStarted = false;
const xmpcConfig = await (0, parse_xmcp_config_1.getConfig)();
compiler_context_1.compilerContext.setContext({
xmcpConfig: xmpcConfig,
});
let webpackConfig = (0, get_webpack_config_1.getWebpackConfig)(xmpcConfig);
if (xmpcConfig.webpack) {
webpackConfig = xmpcConfig.webpack(webpackConfig);
}
return new Promise((resolve, reject) => {
const watcher = new file_watcher_1.Watcher({
// keep the watcher running on dev mode after "onReady"
persistent: mode === "development",
ignored: /(^|[\/\\])\../,
ignoreInitial: false,
});
let toolsPath = (0, path_validation_1.isValidPath)((0, utils_1.getResolvedPathsConfig)(xmpcConfig).tools, "tools");
// handle tools
watcher.watch(`${toolsPath}/**/*.ts`, {
onAdd: (path) => {
toolPaths.add(path);
if (compilerStarted) {
generateCode();
}
},
onUnlink: (path) => {
toolPaths.delete(path);
if (compilerStarted) {
generateCode();
}
},
});
// if adapter is not enabled, handle middleware
if (!xmpcConfig.experimental?.adapter) {
// handle middleware
watcher.watch("./src/middleware.ts", {
onAdd: () => {
compiler_context_1.compilerContext.setContext({
hasMiddleware: true,
});
if (compilerStarted) {
generateCode();
}
},
onUnlink: () => {
compiler_context_1.compilerContext.setContext({
hasMiddleware: false,
});
if (compilerStarted) {
generateCode();
}
},
});
}
// start compiler
watcher.onReady(() => {
let firstBuild = true;
compilerStarted = true;
// delete existing runtime folder
deleteSync(constants_1.runtimeFolderPath);
(0, fs_utils_1.createFolder)(constants_1.runtimeFolderPath);
generateCode();
const compiler = (0, webpack_1.webpack)(webpackConfig);
// Webpack returns null if config is invalid
if (!compiler) {
reject(new Error("Failed to create webpack compiler - invalid config"));
return;
}
const handleCompilation = (err, stats) => {
if (err) {
console.error(err);
reject(err);
return;
}
if (stats?.hasErrors()) {
const errorMsg = stats.toString({
colors: true,
chunks: false,
});
console.error(errorMsg);
reject(new Error('Webpack compilation failed'));
return;
}
if (firstBuild) {
firstBuild = false;
const endTime = Date.now();
const duration = endTime - startTime;
console.log(`${cli_icons_1.greenCheck} Compiled in ${chalk.bold.green(`${duration}ms`)}`);
(0, on_first_build_1.onFirstBuild)(mode, xmpcConfig);
onBuild?.();
// In production mode, close watcher and compiler, then resolve
if (mode === "production") {
watcher.close().then(() => {
// Close webpack compiler to release all resources
compiler.close((compilerCloseErr) => {
if (compilerCloseErr) {
console.error('Error closing webpack compiler:', compilerCloseErr);
reject(compilerCloseErr);
}
else {
resolve();
}
});
}).catch((closeErr) => {
console.error('Error closing watcher:', closeErr);
reject(closeErr);
});
}
}
else {
// on dev mode, webpack will recompile the code, so we need to start the http server after the first one
if (mode === "development" &&
xmpcConfig["http"] &&
!xmpcConfig.experimental?.adapter) {
(0, start_http_server_1.startHttpServer)();
}
}
};
// Use compiler.run() for production (single build) or compiler.watch() for development (continuous builds)
if (mode === "production") {
compiler.run(handleCompilation);
}
else {
const watching = compiler.watch({}, handleCompilation);
// Store watching instance for proper cleanup
process.on('SIGINT', () => {
if (watching) {
watching.close(() => {
process.exit(0);
});
}
else {
process.exit(0);
}
});
}
});
});
}
function generateCode() {
const fileContent = (0, generate_import_code_1.generateImportCode)();
fs_1.default.writeFileSync(path_1.default.join(constants_1.runtimeFolderPath, "import-map.js"), fileContent);
// Generate runtime exports for global access
const runtimeExportsCode = (0, generate_env_code_1.generateEnvCode)();
const envFilePath = path_1.default.join(constants_1.rootFolder, "xmcp-env.d.ts");
// Delete existing file if it exists
if (fs_1.default.existsSync(envFilePath)) {
fs_1.default.unlinkSync(envFilePath);
}
fs_1.default.writeFileSync(envFilePath, runtimeExportsCode);
}