DEV Community

Cover image for Config in TypeScript, and why there are no named inputs
VX
VX

Posted on Edited on Originally published at vznjs.github.io

Config in TypeScript, and why there are no named inputs

A vx.config.ts is TypeScript. Share settings with an import, not a
special config key. Here is what bunx @vzn/vx-migrate writes from two
package.json scripts:

$ cat packages/ui/package.json
{"name":"ui","scripts":{"build":"tsc -b","test":"vitest run"}}

$ bunx @vzn/vx-migrate
  from      package.json scripts
  tasks     1 clean · 1 todo
  files     packages/ui/vx.config.ts
…
$ cat packages/ui/vx.config.ts
import type { ProjectConfig } from '@vzn/vx/config'

export default {
  tasks: {
    // TODO(vx-migrate): cache: add `inputs: { files: ['src/**'] }, cache: { outputs: { files: ['dist/**'] } }` with this package's real inputs and outputs — …
    build: {
      // inputs: { files: ['src/**'] }, cache: { outputs: { files: ['dist/**'] } },
      exec: {
        command: 'tsc -b',
      },
      dependsOn: ['^build'],
    },
    test: {
      exec: {
        command: 'vitest run',
      },
      dependsOn: ['build'],
    },
  },
} satisfies ProjectConfig
Enter fullscreen mode Exit fullscreen mode

Each script became a task with the same command. The tool suggests
inputs and outputs in a comment, and doesn't guess them.

vx-migrate 69617eb · 2026-10-23

satisfies ProjectConfig gives autocomplete and type checks with no
runtime import. defineProject works too.

Composition is an import

A shared preset is a file:

// vx-preset.ts (workspace root)
export const lib = (entry: string) => ({
  build: {
    exec: { command: `tsup ${entry}` },
    inputs: { files: ['src/**', 'tsup.config.ts'] }, cache: { outputs: { files: ['dist/**'] } },
  },
})
Enter fullscreen mode Exit fullscreen mode
// packages/ui/vx.config.ts
import type { ProjectConfig } from '@vzn/vx/config'
import { lib } from '../../vx-preset.ts'
export default { tasks: { ...lib('src/index.ts') } } satisfies ProjectConfig
Enter fullscreen mode Exit fullscreen mode

The key hashes the config after it runs.
Edit vx-preset.ts, run vx lock, and every task that uses it gets a
new key.

No list of global dependencies to keep up to date: the import is the
dependency.

JSON configs need In a TypeScript module
globalDependencies the import
namedInputs a constant
targetDefaults a function returning a task
extends ...spread

Your compiler checks it, your editor refactors it.

What a config may not do

A config can't read another project. Globs stay inside the project
folder, and ../shared/** is an error. inputs.workspaceFiles is the
one exception, for files at the workspace root.

A config may read process.env, the clock or the network, but only
once, when vx lock runs it. No run runs the
config again.

bunx @vzn/vx-migrate also reads turbo.json or an Nx graph. A
globalDependencies list becomes a preset file and its import. Schema:
Configuration schema.

More at vznjs.github.io/vx.


Originally published on the vx blog. vx is open-source (MIT) task orchestration with a build cache: vznjs.github.io/vx · GitHub.

Written with AI assistance.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.