Configuration Files
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.

nr 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.
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:
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 --scriptsTake 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:
{
"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. ✨
Complete documentation for nr can be found here.
TypeScript
TypeScriptVite (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:
{
"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
ViteVite 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
typefield inpackage.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
copyFilesfeature.Test files will be excluded from the build.
Output will not be minified.
A minimal configuration file would include the following:
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:
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:
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:
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.
})
]
}
})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.
For more information on configuring Vite, consult the Vite configuration documentation.
Vitest
VitestVitest 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:
import { vite } from '@darkobits/ts'
export default vite.node({
test: {
// Add custom Vitest configuration here.
coverage: {
lines: 90,
functions: 80
}
}
})For more information on configuring Vitest, consult the Vitest configuration documentation.
ESLint
ESLintESLint is used to reduce bugs by encouraging best practices and consistent code style. ESLint is configured using the new flat configuration format.
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 @darkobits/eslint-plugin:
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:
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.
}
])For more information on configuring ESLint, consult the ESLint configuration documentation.
Last updated