ttsc
Version:
General-purpose TypeScript-Go compiler, runtime, plugin host, and LSP host.
115 lines • 5.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startResidentTransform = startResidentTransform;
const node_path_1 = __importDefault(require("node:path"));
const loadProjectPlugins_1 = require("../../plugin/internal/loadProjectPlugins");
const readProjectConfig_1 = require("./project/readProjectConfig");
const residentTransformProcess_1 = require("./residentTransformProcess");
const resolveBinary_1 = require("./resolveBinary");
const resolveTsgo_1 = require("./resolveTsgo");
const sharedHostHelpers_1 = require("./sharedHostHelpers");
/**
* Start a resident `serve` host for the configured project.
*
* Mirrors the plugin spawn of `transformProjectInMemory`, but launches the
* shared host's `serve` subcommand as one long-lived process instead of a
* per-call `transform` subprocess. The host compiles the whole project once at
* startup and then answers per-file requests, so one caller pays the project
* compile once and reuses it across its own per-file requests.
*
* Resident mode runs through the linked-plugin shared host
* (`cmd/utility-host`), which is the only binary that exposes `serve`. It
* therefore requires at least one transform-stage plugin; executable transform
* hosts that own their own process are not served and must use the per-call
* transform path. Check-stage plugins are not run by the resident host.
*/
function startResidentTransform(context) {
const cwd = node_path_1.default.resolve(context.cwd ?? process.cwd());
const project = (0, readProjectConfig_1.readProjectConfig)({
cwd,
projectRoot: context.projectRoot,
tsconfig: context.tsconfig,
});
const loaded = (0, loadProjectPlugins_1.loadProjectPlugins)({
binary: (0, resolveBinary_1.resolveBinary)(context) ?? "",
cacheDir: context.cacheDir ?? context.env?.TTSC_CACHE_DIR,
cwd,
entries: context.plugins,
env: { ...process.env, ...context.env },
pluginConfigDir: context.pluginConfigDir,
projectRoot: context.projectRoot,
tsconfig: project.path,
});
const transformers = loaded.nativePlugins.filter((plugin) => plugin.stage === "transform");
if (transformers.length === 0) {
throw new Error("ttsc: TtscService resident mode requires at least one transform-stage plugin; " +
"use TtscCompiler.transform for projects with only check-stage plugins or none");
}
(0, sharedHostHelpers_1.assertSharedHostCompatibility)(transformers, "source-to-source");
const host = (0, sharedHostHelpers_1.selectSharedHostPlugin)(transformers);
const tsgoBinary = (0, resolveTsgo_1.resolveTsgo)({ ...context, cwd: project.root }).binary;
const resident = new residentTransformProcess_1.ResidentTransformProcess({
args: [
"serve",
`--tsconfig=${project.path}`,
`--plugins-json=${serializeNativePlugins(transformers)}`,
`--cwd=${project.root}`,
],
binary: host.binary,
cwd: project.root,
env: residentEnv(context, tsgoBinary, loaded.nativePlugins),
});
return { process: resident, projectRoot: project.root };
}
/**
* Build the environment for the resident host spawn. Matches the per-call
* transform spawn: injects the Node, tsgo, and ttsx binaries so the host never
* searches PATH, sets `TTSC_PLUGIN_CONFIG_DIR` when the caller declared a
* plugin config anchor (an embedder compiling through a generated wrapper
* tsconfig) so config-file discovery walks the real project instead of the
* wrapper's temp-dir ancestry, and forwards linked transform plugins via
* `TTSC_LINKED_PLUGINS_JSON`.
*/
function residentEnv(context, tsgoBinary, nativePlugins) {
const pluginConfigDir = (0, sharedHostHelpers_1.resolvePluginConfigDir)(context);
const env = {
...process.env,
TTSC_NODE_BINARY: process.env.TTSC_NODE_BINARY ?? process.execPath,
...(pluginConfigDir === undefined
? {}
: { TTSC_PLUGIN_CONFIG_DIR: pluginConfigDir }),
TTSC_TSGO_BINARY: process.env.TTSC_TSGO_BINARY ?? tsgoBinary,
TTSC_TTSX_BINARY: process.env.TTSC_TTSX_BINARY ??
node_path_1.default.join(__dirname, "..", "..", "launcher", "ttsx.js"),
...context.env,
};
// The anchor is per-invocation state owned by this host: when this run
// declared none (and the caller's env does not name one), drop any value
// inherited from an ancestor ttsc process so a nested build never
// mis-anchors its plugins at the outer project.
if (pluginConfigDir === undefined &&
context.env?.TTSC_PLUGIN_CONFIG_DIR === undefined) {
delete env.TTSC_PLUGIN_CONFIG_DIR;
}
const linked = (0, sharedHostHelpers_1.linkedTransformPlugins)(nativePlugins);
if (linked.length !== 0) {
env.TTSC_LINKED_PLUGINS_JSON = serializeNativePlugins(linked);
}
return env;
}
/**
* Serialize the plugin list to the `--plugins-json` /
* `TTSC_LINKED_PLUGINS_JSON` shape the native host reads: only the fields it
* needs, to keep the arg short.
*/
function serializeNativePlugins(plugins) {
return JSON.stringify(plugins.map((plugin) => ({
config: plugin.config,
name: plugin.name,
stage: plugin.stage,
})));
}
//# sourceMappingURL=startResidentTransform.js.map