Skip to content

Commit c79caaf

Browse files
committed
feat: add getProject
1 parent 6017349 commit c79caaf

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

src/get-project.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { goTry } from "go-go-try";
2+
import fs from "node:fs/promises";
3+
import { join } from "pathe";
4+
import { expect, test } from "vitest";
5+
import { getProject } from "./get-project.ts";
6+
import { tempDir } from "./temp-dir.ts";
7+
8+
test("no cwd", async () => {
9+
await using dir = await tempDir();
10+
const [err, _] = goTry(() =>
11+
getProject({
12+
indexFilePath: "./this-file-does-not-exist.ts",
13+
typeRoots: join(dir.path, "this-dir-does-not-exist"),
14+
}),
15+
);
16+
expect(err).toBeDefined();
17+
});
18+
19+
test("no index file", async () => {
20+
await using dir = await tempDir();
21+
const [err, _] = goTry(() =>
22+
getProject({
23+
indexFilePath: "./this-file-does-not-exist.ts",
24+
typeRoots: dir.path,
25+
}),
26+
);
27+
expect(err).toBeDefined();
28+
});
29+
30+
test("with index file", async () => {
31+
await using dir = await tempDir();
32+
const indexFilePath = join(dir.path, "./index.ts");
33+
await fs.writeFile(indexFilePath, "export {};");
34+
const [err, res] = goTry(() =>
35+
getProject({
36+
indexFilePath,
37+
typeRoots: dir.path,
38+
}),
39+
);
40+
expect(err).toBeUndefined();
41+
expect(res).toBeDefined();
42+
expect(res!.project.getSourceFiles().map((sf) => sf.getBaseName())).toStrictEqual(["index.ts"]);
43+
});
44+
45+
test("with index file and other file", async () => {
46+
await using dir = await tempDir();
47+
const indexFilePath = join(dir.path, "./index.ts");
48+
const otherFilePath = join(dir.path, "./other.ts");
49+
await fs.writeFile(indexFilePath, "export * from './other';");
50+
await fs.writeFile(otherFilePath, "export const a = 1;");
51+
const [err, res] = goTry(() =>
52+
getProject({
53+
indexFilePath,
54+
typeRoots: dir.path,
55+
}),
56+
);
57+
expect(err).toBeUndefined();
58+
expect(res).toBeDefined();
59+
expect(res!.project.getSourceFiles().map((sf) => sf.getBaseName())).toStrictEqual([
60+
"index.ts",
61+
"other.ts",
62+
]);
63+
});

src/get-project.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { ModuleKind, ModuleResolutionKind, Project, ScriptTarget, type SourceFile } from "ts-morph";
2+
3+
/** `GetProjectOptions` contains the options for calling {@link getProject}. */
4+
export interface GetProjectOptions {
5+
/** Path to the types entry point file. */
6+
indexFilePath: string;
7+
8+
/**
9+
Directory where the `@types` packages are installed.
10+
11+
@see {@link https://www.typescriptlang.org/tsconfig/#typeRoots}
12+
*/
13+
typeRoots: string;
14+
}
15+
16+
/** `GetProjectReturn` contains the data returned by {@link getProject}. */
17+
export interface GetProjectReturn {
18+
/** `Project` created with `ts-morph`. */
19+
project: Project;
20+
21+
/** `SourceFile` created with `ts-morph` representing the index file. */
22+
indexFile: SourceFile;
23+
}
24+
25+
/** `getProject` returns a `ts-morph` TypeScript compiler project. */
26+
export function getProject({ indexFilePath, typeRoots }: GetProjectOptions): GetProjectReturn {
27+
const project = new Project({
28+
compilerOptions: {
29+
// Include esnext types.
30+
// See https://github.com/dsherret/ts-morph/issues/938
31+
// and https://github.com/microsoft/TypeScript/blob/master/lib/lib.esnext.full.d.ts
32+
lib: ["lib.esnext.full.d.ts"],
33+
34+
// Set modern target and module resolutions options.
35+
target: ScriptTarget.ESNext,
36+
module: ModuleKind.ESNext,
37+
moduleResolution: ModuleResolutionKind.Bundler,
38+
39+
// By default, `ts-morph` creates a project rooted in the current working directory
40+
// (that is, where this library or an app using it is running).
41+
// We must change the `typeRoots` directory to the temporary directory where
42+
// the analyzed package is installed, otherwise TypeScript will discover
43+
// `@types` packages from our local `node_modules` directory giving
44+
// inconsistent analysis results due to available type definitions.
45+
// See https://www.typescriptlang.org/tsconfig#typeRoots.
46+
typeRoots: [typeRoots],
47+
},
48+
});
49+
50+
// Add index file and resolve module imports.
51+
const indexFile = project.addSourceFileAtPath(indexFilePath);
52+
project.resolveSourceFileDependencies();
53+
54+
return { project, indexFile };
55+
}

0 commit comments

Comments
 (0)