⚡
ts
  • About
  • Tooling
  • Conventions
  • Install
  • Configuration Files
  • Running Scripts
  • Advanced
Powered by GitBook
On this page
  • TypeScript
  • Vite
  • Preset: Node
  • Build Targets
  • Vitest
  • ESLint

Configuration Files

PreviousInstallNextRunning Scripts

Last updated 5 months ago

This section will walk you through the configuration files required by the various tools used by ts. If you are already familiar with configuring these tools, this process should be straightforward.

ts takes one of two approaches to configuration, depending on the tool:

For tools that support JavaScript configuration files that export an object or function, ts will provide a higher-order function which uses a base configuration with reasonable defaults, but will allow you to invoke it with your own configuration function to set any overrides you may need. The two configurations are then recursively merged.

For tools that leverage an extends option (ie: ESLint), ts provides an appropriate plugin/preset, and additional configuration may be applied according to the tool's configuration schema.


is used to manage and coordinate tasks, keeping your package.json "scripts" section terse while still giving you the flexibility to write custom scripts using a JavaScript or TypeScript configuration file. To configure NR, create nr.config.ts in your project root. ts provides several useful default package scripts which you can choose to overwrite or add to per your project's needs.

nr.config.ts
export { defaultPackageScripts as default } from '@darkobits/ts'

To define additional package scripts or to overwrite one of the defaults, use the defineConfig helper from nr in conjunction with defaultPackageScripts:

nr.config.ts
import { defineConfig } from '@darkobits/nr'
import { defaultPackageScripts } from '@darkobits/ts'

export default defineConfig([
  defaultPackageScripts,
  ({ command, script }) => {
    script('test', [
      command('vitest', {
        args: ['run', { passWithNoTests: true }],
        preserveArgumentCasing: true
      })
    ], {
      description: 'Run unit tests with Vitest.'
    })
  }
])

Once you have created this file, you can produce a list of all package scripts by running:

npx nr --scripts

Take a moment to familiarize yourself with the base scripts provided by ts. It is also possible to create an alias to an nr script in package.json. At the very least, it is recommended that you alias the prepare script provided by ts, which will npm install:

package.json
{
  "scripts": {
    "prepare": "nr prepare"
  }
}

nr supports partial string matching for script names. For example, to run the script build.watch, you could issue the command nr b.w. ✨


Vite (and likely your IDE) need a TypeScript configuration file to be present for language features to work. In your project root, create tsconfig.json. Then, extend the base TypeScript configuration from ts:

tsconfig.json
{
  "extends": "@darkobits/ts/tsconfig.json",
  "compilerOptions": {
    "baseUrl": "src",
    "outDir": "dist"
  }
}

ts uses baseUrl and outDir to configure other tools, including Vite and ESLint. It is therefore crucial that these two options are set correctly.


Vite is used to compile your project. ts also uses plugins that type-check and lint your project as well. To configure Vite, create vite.config.ts in your project root. Then, default-export one of the Vite configuration presets from ts.

Preset: Node

The Node configuration preset is suitable for backend servers, CLIs, and Node libraries.

  • Source files will not be bundled. Instead, the output directory structure will resemble that of the source directory.

  • Dependencies will be externalized.

  • Output format (CJS or ESM) will be inferred from the type field in package.json.

  • Source and output directories will be inferred from tsconfig.json.

  • Shebangs will be preserved in files that have them.

  • Any other files in the source directory will be copied to the output directory as-is, similar to Babel's copyFiles feature.

  • Test files will be excluded from the build.

  • Output will not be minified.

A minimal configuration file would include the following:

vite.config.ts
import { vite } from '@darkobits/ts'
export default vite.node()

To customize Vite's configuration, you may pass either an object or a function to the preset. The object form is suitable for simple, synchronous configuration modifications:

vite.config.ts
import { vite } from '@darkobits/ts';

export default vite.node({
  clearScreen: true,
  publicDir: 'assets'
});

If more advanced configuration is necessary, use a function. This function may be asynchronous. It will be passed a context object with the following properties:

vite.config.ts
import { vite } from '@darkobits/ts';

export default vite.node(async context => {
  const {
    // Vite configuration object created by the Node preset.
    config,
    // Forwarded directly from Vite.
    // See: https://vitejs.dev/config/#conditional-config
    command, mode, ssrBuild,
    // Computed project root directory.
    root,
    // Root directory + srcDir from tsconfig.json.
    srcDir,
    // Root directory + outDir from tsconfig.json.
    outDir,
    // Parsed/normalized package.json.
    packageJson,
    // Parsed tsconfig.json.
    tsConfig,
    // Path to the tsconfig file being used.
    tsConfigPath
    // Common glob patterns for different file types.
    patterns,
  } = context;
});

When your configuration function is called, ts will have already applied the preset's configuration. This entire configuration object is available to you to modify in-place. Or, you may prefer to return a new configuration object. If that's the case, the value you return will be recursively merged with the config object provided by the preset.

Whether you decide to modify context.config in-place or return a new object is up to you. Below is an example of using both approaches to add a new plugin:

vite.config.ts
import { vite } from '@darkobits/ts'
import awesomeVitePlugin from 'awesome-vite-plugin'

// Option A: Modify config.plugins in-place.
export default vite.library(({ config }) => {
  config.plugins.push(awesomeVitePlugin({
    // Optional plugin configuration.
  }))
})

// Option B: Return a new configuration object.
export default vite.library(() => {
  return {
    plugins: [
      awesomeVitePlugin({
        // Optional plugin configuration.
      })
    ]
  }
})

When returning a new object, arrays are merged by concatenating them.

Build Targets

ts will configure Vite's config.build.lib.formats setting to output ESM when "type": "module" is set in the project's package.json. Otherwise, Vite will be configured to output CommonJS. If necessary, you may override this behavior using the methods described above.


vite.config.ts
import { vite } from '@darkobits/ts'

export default vite.node({
  test: {
    // Add custom Vitest configuration here.
    coverage: {
      lines: 90,
      functions: 80
    }
  }
})

eslint.config.js
export { ts as default } from '@darkobits/eslint-plugin'

If you need to add your own rules or other configuration, you may use the defineFlatConfig helper for a type-safe experience:

eslint.config.js
import { defineFlatConfig, ts } from '@darkobits/eslint-plugin'

export default defineFlatConfig([
  {
    // This config will be applied before the preset.
  },
  ...ts,
  {
    // This config will be applied after the preset.
  }
])

Complete documentation for nr can be found .

TypeScript

Vite

For more information on configuring Vite, consult the .

Vitest

is used for unit-testing your project. By default, Vitest will use your existing Vite configuration file with additional options for Vitest under the test key. Configuration presets provided by ts ship with sensible defaults for Vitest, but these settings may be customized:

For more information on configuring Vitest, consult the .

ESLint

is used to reduce bugs by encouraging best practices and consistent code style. ESLint is configured using the new .

Create eslint.config.js in your project root. This will be an ESM file, so if your project does not have "type": "module" set in package.json, you will need to use the .mjs extension. Then, export the preset provided by :

For more information on configuring ESLint, consult the .

here
Vite configuration documentation
Vitest
Vitest configuration documentation
ESLint
flat configuration format
@darkobits/eslint-plugin
ESLint configuration documentation
nr
Vite
Vitest
TypeScript
ESLint