Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,17 @@ Hello, world!

## How It Works

We can write a program like this:
When `ctts script.ts` is executed, compile-time-typescript creates a caller like this:

```typescript
type Main<Input extends string> = `Hello, ${Input}\n`;
export default Main;
```

Compile-time-typescript saves the program as `callee.ts` and creates `caller.ts`:

```typescript
import Main from './callee';
import Main from '/path/to/script';
type Input = "HERE COMES THE INPUT";
type Output = Main<Input>;
```

So the program must have a default export of a generic type that takes a type parameter and constructs a string literal type.

Then compile-time-typescript type-checks `caller.ts` and extracts the type information of `Output`.
Then compile-time-typescript type-checks the caller and extracts the type information of `Output`.

If `Output` is a string literal type, its content is printed. Otherwise, an error occurs.

Expand Down
29 changes: 11 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { promises as fs } from 'fs';
import * as tmp from 'tmp-promise';
import * as path from 'path';

tmp.setGracefulCleanup();

interface RunOptions {
input: Buffer;
}
Expand All @@ -12,24 +14,19 @@ interface RunResult {
}

export async function run(fileName: string, { input = Buffer.of() }: Partial<RunOptions> = {}): Promise<RunResult> {
const { path: tmpDirPath } = await tmp.dir({ prefix: `compile-time-typescript` });
const calleeFileName = `callee.ts`;
const callerFileName = `caller.ts`;
await Promise.all([
fs.copyFile(fileName, path.join(tmpDirPath, calleeFileName)),
fs.writeFile(path.join(tmpDirPath, callerFileName), `
import Main from './${path.basename(calleeFileName, path.extname(calleeFileName))}';
type Input = ${JSON.stringify(input.toString('binary'))};
type Output = Main<Input>;
`),
]);
const { path: callerFileName, cleanup } = await tmp.file({ prefix: `compile-time-typescript`, postfix: `caller.ts` });
await fs.writeFile(callerFileName, `
import Main from ${JSON.stringify(path.resolve(fileName).replace(/\.ts$/, ''))};
type Input = ${JSON.stringify(input.toString('binary'))};
type Output = Main<Input>;
`);
const program = ts.createProgram({
rootNames: [path.join(tmpDirPath, callerFileName)],
rootNames: [callerFileName],
options: {
strict: true,
},
});
const source = program.getSourceFile(path.join(tmpDirPath, callerFileName))!;
const source = program.getSourceFile(callerFileName)!;
const checker = program.getTypeChecker();
const outputList: Buffer[] = [];

Expand All @@ -47,11 +44,7 @@ export async function run(fileName: string, { input = Buffer.of() }: Partial<Run
}

source.forEachChild(visit);
await Promise.all([
fs.unlink(path.join(tmpDirPath, calleeFileName)),
fs.unlink(path.join(tmpDirPath, callerFileName)),
]);
await fs.rmdir(tmpDirPath);
await cleanup();
return {
output: Buffer.concat(outputList),
};
Expand Down