writeLibrary

tcmts.cli.test.e2e.import.writeLibrary · #higc26a6 · term · runs on the server

uses node:fs

A library written the way real ones are: bundler-style imports and the TypeScript features Node can't strip.

import fs from "node:fs";
import path from "node:path";

export function writeLibrary(lib: string) {
  const write = (file: string, text: string) => {
    fs.mkdirSync(path.dirname(path.join(lib, file)), { recursive: true });
    fs.writeFileSync(path.join(lib, file), text);
  };
  write(
    "tsconfig.json",
    JSON.stringify({
      compilerOptions: {
        strict: true,
        module: "ESNext",
        moduleResolution: "node",
        declaration: true,
        isolatedDeclarations: true,
        noEmit: true,
        lib: ["ESNext"],
        target: "ES2022",
      },
    }),
  );
  write(
    "src/symbols.ts",
    `export const tag = Symbol("tag");
// two ambient brands with identical text are still two distinct symbols
export declare const BrandA: unique symbol;
export declare const BrandB: unique symbol;
export type A = string & { [BrandA]: true };
export type B = string & { [BrandB]: true };
export type infer<T> = T extends { value: infer V } ? V : never;
`,
  );
  write(
    "src/shapes.ts",
    `import * as symbols from "./symbols";

export enum Kind { Circle = "circle", Square = "square" }

export namespace Geometry {
  export const tau = 2 * Math.PI;
}

export class Shape {
  constructor(public readonly kind: Kind, private size: number) {}
  area(): number {
    return this.kind === Kind.Circle ? (Geometry.tau / 2) * this.size ** 2 : this.size ** 2;
  }
  get [symbols.tag](): string {
    return this.kind;
  }
}

// a type named like a type keyword, through a namespace import, in a type position
export function unwrap<T extends { value: unknown }>(x: T): symbols.infer<T> {
  return x.value as symbols.infer<T>;
}
`,
  );
  write(
    "src/config.ts",
    `// module state: a lazily created cell, written by one function and read by another
let config: { verbose: boolean } | undefined;
let calls = 0;

export function setConfig(c: { verbose: boolean }): void {
  config = c;
  calls++;
}
export function getConfig(): { verbose: boolean } {
  return config ?? { verbose: false };
}
export const callCount = (): number => calls;

// @ts-expect-error the implementation is deliberately looser than this overload
// (the explanation continues on a second line)
export function pick(x: number): string;
export function pick(x: string): string;
export function pick(x: string | boolean): string {
  return String(x);
}
`,
  );
  write(
    "src/tables.ts",
    `// constants set up by the statements after them: a table a loop fills, a constructor's prototype, a frozen object
const alphabet = "abc";
const codes = new Uint8Array(128).fill(255);
for (let i = 0; i < alphabet.length; i++) codes[alphabet.charCodeAt(i)] = i;

const Proto = {
  describe(this: { n: number }): string {
    return "n=" + this.n;
  },
};
const Impl = function (this: { n: number }, n: number) {
  this.n = n;
} as unknown as { new (n: number): { n: number; describe(): string }; prototype: unknown };
Impl.prototype = Proto;

export const limits = { max: 3 };
Object.freeze(limits);

export const code = (c: string): number => codes[c.charCodeAt(0)]!;
// another name for the same function
export const codeOf: (c: string) => number = code;
export const make = (n: number) => new Impl(n);

// an interface with a namespace of types beside it, as Effect writes them
export interface Box<A> {
  readonly value: A;
}
export declare namespace Box {
  type Value<B> = B extends Box<infer A> ? A : never;
}
export const box = <A>(value: A): Box<A> => ({ value });

// a function, an interface and a namespace of types sharing a name, in one cycle, as Effect's Struct
export declare namespace Pair {
  type Of<A> = readonly [A, A];
}
export function Pair<A>(a: A, b: A): Pair<A> {
  return { items: [a, b], make: Pair };
}
export interface Pair<A> {
  readonly items: Pair.Of<A>;
  readonly make: typeof Pair;
}
export const firstOf = <A>(p: Pair<A>): Pair.Of<A>[0] => p.items[0];
export const unbox = <B extends Box<unknown>>(b: B): Box.Value<B> => b.value as Box.Value<B>;
`,
  );
  // what the host provides, declared rather than imported, as code that runs on several hosts does
  write(
    "src/host.ts",
    `declare const process: { readonly platform: string };\nexport const platform = (): string => process.platform;\n`,
  );
  // a name both imported (as a type) and declared here (as a value), as Effect's internal Stream is
  write(
    "src/merged.ts",
    `import type { Box } from "./tables";
const Box = function (this: { value: number }, value: number) {
  this.value = value;
} as unknown as new (value: number) => Box<number>;
export const boxed = (n: number): Box<number> => new Box(n);
`,
  );
  write("src/index.ts", `export * from "./shapes";\nexport * from "./config";\n`);
  // test helpers that need a dev dependency nobody installed: excluded
  write(
    "src/testing/helpers.ts",
    `import { expect } from "vitest";\nexport const ok = () => expect(1).toBe(1);\n`,
  );
}

used by