TypeScript language services with support for custom module resolution
npm install @rnx-kit/typescript-service

@rnx-kit/typescript-service gives you access to TypeScript's language
services, and lets you customize how module resolution occurs.
The starting point for working with TypeScript is reading configuration from the
command line, or from a
configuration file
like tsconfig.json.
Both methods yeild a ParedCommandLine object, offering the same level of
control over how TypeScript behaves.
``typescript
import ts from "typescript";
// Read configuration from a NodeJS command-line
const cmdLine = ts.parseCommandLine(process.argv.slice(2));
// Read configuration from a project file (parsed into a TypeScript command-line object)
const configFileName = findConfigFile(searchPath);
if (!configFileName) {
throw new Error(Failed to find config file under ${searchPath});Failed to read config file ${configFileName}
}
const cmdLine = readConfigFile(configFileName);
if (!cmdLine) {
throw new Error();
}
// For either method, handle errors
if (cmdLine.errors.length > 0) {
...
}
`
TypeScript's language service allows you to work with source code continuously,
unlike the TypeScript compiler, which makes a single pass through the code. The
language service tends to load only what is needed to fulfill the current
request, such as getting diagnostics for a particular source file, or re-loading
a changed file being watched. This saves time and memory, when full source
validation isn't needed.
The language service is accessible through the Service and Project classes.Service manages shared state across all projects, and is meant to be aProject
singleton. contains a TypeScript configuration, which includes a listtsconfig.json
of source files. TypeScript configuration comes from either the command line or
a file like .
You can use a Project to validate code, and emit transpiled JavaScript:
`typescript
const service = new Service();
const project = service.openProject(cmdLine);
// validate
const fileHasErrors = project.validateFile(fileName);
const projectHasErrors = project.validate();
// emit
const fileEmitted = project.emitFile(fileName);
const projectEmitted = project.emit();
`
You can also change which files are in a project. This is typically done in
response to an external event, like a callback notifying you that a file has
been added, updated or removed:
`typescript
import ts from "typescript";
function onFileEvent(eventType: string, fileName: string, payload?: string) {
if (eventType === "add") {
project.addFile(fileName);
} else if (eventType === "modify") {
project.updateFile(
fileName,
payload && ts.ScriptSnapshot.fromString(payload)
);
} else if (eventType === "delete") {
project.deleteFile(fileName);
}
}
`
When you're finished working with a Project, you must dispose of it to
properly release all internal resources:
`typescript`
project.dispose();
The language service is initialized using a host interface. You can customize
the host interface to change the way TypeScript works:
`typescript
const enhanceLanguageServiceHost = (host: ts.LanguageServiceHost): void => {
// change host functions in here
};
const service = new Service();
const project = service.openProject(cmdLine, enhanceLanguageServiceHost);
`
For example, you can replace the functions which control how modules and type
references are resolved to files:
`typescript
function resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions
): (ResolvedModule | undefined)[] {
/ ... /
}
function resolveTypeReferenceDirectives(
typeDirectiveNames: string[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions
): (ResolvedTypeReferenceDirective | undefined)[] {
/ ... /
}
const enhanceLanguageServiceHost = (host: ts.LanguageServiceHost): void => {
host.resolveModuleNames = resolveModuleNames;
host.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives;
};
``