Skip to content

Commit

Permalink
Convert to using tsconfig.json
Browse files Browse the repository at this point in the history
Swap over to `tsconfig.json` for a bunch of reasons:

  * VSCode shows erros when using `replaceAll` unless it finds a
    `tsconfig.json` with a recent enough library version
  * TypeDoc (and probably other tools) require a `tsconfig.json` file to
    work
  * Several things like the `paths` `tsc` option can only be supplied
    through `tsconfig.json` and using this is necessary for future
    changes where test files can import the library as
    `@ninjutsu-build/foo` rather than `./foo.js`
  • Loading branch information
elliotgoodrich committed Jun 29, 2024
1 parent 01ccc0d commit 857c484
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 153 deletions.
132 changes: 58 additions & 74 deletions configure/configure.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import {
relative as relativeNative,
sep,
} from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { globSync } from "glob";
import { platform } from "os";
import isCi from "is-ci";

Expand Down Expand Up @@ -160,44 +159,27 @@ function addBiomeConfig(rule) {
};
}

// Tell TypeScript to look for `@types/node` package installed in the
// workspace `node_modules` directory, otherwise it'll fail to find it
const typeRoots = [
relativeNative(
process.cwd(),
fileURLToPath(import.meta.resolve("@types/node/package.json")),
)
.split(sep)
.slice(0, -2)
.join("/"),
];

const compilerOptions = {
target: "ES2018",
lib: ["ES2021"],
module: "nodenext",
moduleResolution: "nodenext",
typeRoots,
declaration: true,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
strict: true,
noImplicitAny: true,
strictNullChecks: true,
strictFunctionTypes: true,
strictBindCallApply: true,
strictPropertyInitialization: true,
noImplicitThis: true,
useUnknownInCatchVariables: true,
alwaysStrict: true,
noUnusedLocals: true,
noUnusedParameters: true,
noImplicitReturns: true,
noFallthroughCasesInSwitch: true,
skipDefaultLibCheck: true,
skipLibCheck: true,
isolatedModules: true,
};
async function loadSourcesFromTSConfig(tsConfig) {
tsConfig = getInput(tsConfig);
try {
const buffer = readFileSync(tsConfig);
let tsConfigObj = JSON.parse(buffer.toString());
if (Array.isArray(tsConfigObj.include) && tsConfigObj.include.length > 0) {
const { stdout } = await execFile(node, [
getTSCPath(ninja),
"--showConfig",
"--project",
tsConfigPath,
]);
tsConfigObj = JSON.parse(stdout);
}

const directory = dirname(tsConfig);
return tsConfigObj.files.map((f) => join(directory, f));
} catch (e) {
throw new Error(`${tsConfig}: ${e}`);
}
}

const ninja = new NinjaBuilder({
builddir: ".builddir",
Expand Down Expand Up @@ -263,6 +245,8 @@ const transpile = inject(makeSWCRule(ninja), {

format({ in: "configure/configure.mjs" });

const baseConfig = format({ in: "tsconfig.json" });

const scope = "@ninjutsu-build/";
for (const cwd of workspaceJSON.workspaces) {
const localPKGJSON = JSON.parse(
Expand Down Expand Up @@ -295,12 +279,8 @@ for (const cwd of workspaceJSON.workspaces) {
const packageJSON = format({ in: join(cwd, "package.json") });

// Grab all TypeScript source files and format them
const sources = globSync(join(cwd, "src", "*.{cts,mts,ts}"), {
posix: true,
ignore: {
ignored: (f) => basename(f.name, extname(f.name)).endsWith(".test"),
},
}).map(formatAndLint);
const tsconfig = format({ in: join(cwd, "tsconfig.json") });
const sources = (await loadSourcesFromTSConfig(tsconfig)).map(formatAndLint);

const outDir = join(cwd, "dist");

Expand All @@ -322,14 +302,14 @@ for (const cwd of workspaceJSON.workspaces) {
});

// Create the TypeScript type declaration files and do typechecking
const typeDeclarations = tsc({
in: sources,
const typeDeclarations = await tsc({
tsConfig: tsconfig,
compilerOptions: {
...compilerOptions,
declaration: true,
emitDeclarationOnly: true,
outDir,
},
[orderOnlyDeps]: dependenciesTyped,
[orderOnlyDeps]: [...dependenciesTyped, baseConfig],
});

// Create a phony target for when the package has its types generated and
Expand All @@ -340,33 +320,37 @@ for (const cwd of workspaceJSON.workspaces) {
in: [packageJSON, ...typeDeclarations].map(getOrderOnlyDeps),
});

// Grab all TypeScript tests files and format them
const tests = globSync(join(cwd, "src", "*.test.mts"), {
posix: true,
}).map(formatAndLint);

// Type check all the tests
const testTargets = (() => {
if (tests.length !== 0) {
return typecheck({
in: tests,
out: join(cwd, "dist", "typechecked.stamp"),
compilerOptions,
[orderOnlyDeps]: packageHasTypes,
}).map((t) => {
const js = transpile({
in: t,
outDir,
});
return test({
in: js,
out: join("$builddir", cwd, `${js}.result.txt`),
[orderOnlyDeps]: packageRunnable,
});
});
} else {
const testTargets = await (async () => {
if (!existsSync(join(cwd, "tsconfig.tests.json"))) {
return [];
}
const testTSConfig = format({ in: join(cwd, "tsconfig.tests.json") });
const tests = await loadSourcesFromTSConfig(testTSConfig);
if (tests.length === 0) {
return [];
}

const testsFormatted = tests.map(formatAndLint);

return (
await typecheck({
tsConfig: testTSConfig,
out: join(cwd, "dist", "typechecked.stamp"),
[orderOnlyDeps]: [packageHasTypes, baseConfig, ...testsFormatted],
})
).map((t) => {
const js = transpile({
in: t,
outDir,
[orderOnlyDeps]: testsFormatted,
});
return test({
in: js,
out: join("$builddir", cwd, `${js}.result.txt`),
[orderOnlyDeps]: packageRunnable,
});
});
})();

const createTar = (() => {
Expand Down
Loading

0 comments on commit 857c484

Please sign in to comment.