@lukasbach/scripts

node/setup-tsconfig

Set's up a build configuration with Typescript by configuring the tsconfig.json file and adjusting package.json and supporting files.

Usage

npx @lukasbach/scripts node/setup-tsconfig

You can call the script directly if you have installed it globally:

npm i -g @lukasbach/scripts
ldo node/setup-tsconfig

Options

  • --module, -m: What module system do you want to use?
  • --esm: Do you want to use pure ESM?
  • --resolution, -r: What module resolution do you want to use?
  • --target, -t: Which target to compile to? Most browsers today support ES6. Node 16 supports ES2021.
  • --lib, -l: Which libraries to include?
  • --emit: What to emit?
  • --strict, -s: Strict mode?
  • --output, -o: Output directory?
  • -v, --verbose: Verbose logging

You can also omit options, and will be asked for them interactively.

Add --yes to skip all confirmations.

Referenced scripts

Script source

View Source on GitHub

/** Set's up a build configuration with Typescript by configuring the tsconfig.json file and adjusting package.json
 * and supporting files. */

await utils.cd(await utils.node.getPackageRoot());

const parentTsconfig = await utils.goUpTree(
  (dir) => dir !== process.cwd() && fs.pathExists(path.join(dir, "tsconfig.json"))
);
if (parentTsconfig) {
  if (await ask.bool("", `A parent tsconfig exists at ${parentTsconfig}, reference that?`, true)) {
    await fs.writeJSON("./tsconfig.json", {
      $schema: "https://json.schemastore.org/tsconfig",
      extends: path.join(path.relative(process.cwd(), parentTsconfig), "tsconfig.json"),
      include: ["src"],
    });
    process.exit(0);
  }
}

const module = await ask.choice(
  "module,m",
  "What module system do you want to use?",
  [
    { name: "ESNext - Use for Web projects", value: "esnext" },
    { name: "NodeNext - Use for node.js projects", value: "nodenext" },
  ],
  "esnext"
);
const pureEsm = await ask.bool("esm", "Do you want to use pure ESM?", false);

const moduleResolution = await ask.choice(
  "resolution,r",
  "What module resolution do you want to use?",
  [
    { name: "Bundler - Use for projects using a seperate bundler", value: "bundler" },
    { name: "NodeNext - Use for node.js projects", value: "nodenext" },
  ],
  "bundler"
);

const target = await ask.choice(
  "target,t",
  "Which target to compile to? Most browsers today support ES6. Node 16 supports ES2021.",
  ["es3", "es5", "es6", "es2016", "es2017", "es2018", "es2019", "es2020", "es2021", "es2022", "esnext"],
  module === "nodenext" ? "es2021" : "es6"
);

const lib = await ask.choice(
  "lib,l",
  "Which libraries to include?",
  [`${target}`, `${target}, DOM`, `${target}, DOM, WebWorker`],
  target
);

const emit = await ask.choice(
  "emit",
  "What to emit?",
  ["Nothing", "Source", "Source and Declaration", "Source, Declaration and Source Maps"],
  "Source and Declaration"
);

const strict = await ask.bool("strict,s", "Strict mode?", true);

const outDir = await ask.text("output,o", "Output directory?", "lib");

await fs.writeJSON(
  "./tsconfig.json",
  {
    $schema: "https://json.schemastore.org/tsconfig",
    compilerOptions: {
      module,
      moduleResolution,
      target,
      esModuleInterop: true,
      allowSyntheticDefaultImports: true,
      lib: lib.split(", "),
      outDir,
      ...(strict ? { strict: true } : {}),
      ...(emit === "Nothing" ? { noEmit: true } : {}),
      ...(emit === "Source and Declaration" ? { declaration: true } : {}),
      ...(emit === "Declaration and Source Maps" ? { declaration: true, sourceMap: true } : {}),
    },
    include: ["src"],
  },
  { spaces: 2 }
);

if (pureEsm) {
  await utils.node.amendPackageJson({
    type: "module",
    main: undefined,
    exports: `./${path.join(outDir, "index.js")}`,
    typings: path.join(outDir, "index.d.ts"),
    scripts: {
      build: "tsc",
    },
    engines: {
      node: ">=16",
    },
  });

  log.info("Updating imports...");
  await utils.runScript("node/add-js-extensions-in-imports");
} else {
  await utils.node.amendPackageJson({
    type: "commonjs",
    main: path.join(outDir, "index.js"),
    typings: path.join(outDir, "index.d.ts"),
    exports: undefined,
    scripts: {
      build: "tsc",
    },
    engines: {
      node: ">=16",
    },
    files: [path.join(outDir, "**/*"), `!${path.join(outDir, "**/*.spec.js")}`, "src/**/*"],
  });
}

await utils.amendFile("./.gitignore", (old) => `${old}\n${outDir}`);
await utils.runScript("deduplicate-gitignore", { file: "./.gitignore" });

await utils.node.addDevDependency("typescript@latest");
if (module === "nodenext") {
  await utils.node.addDevDependency("@types/node");
}