Skip to content

jaredly/dosido

Repository files navigation

dosido

Move functions, types, and files around in a typescript project, while keeping imports happy.

It's VSCode/Intellij's "Move to a new file", but cli.

Additional features:

  • remove exports that are unused
  • detect & fix "mixed component and non-component exports" which deopt hot-module reloading
  • "disentangle directories" by moving shared definitions to a third "shared" directory
  • create directory barrel files and rewrite incoming imports through them
  • split a package into pnpm workspace packages incrementally

Usage

npx dosido --help
pnpm dlx dosido --help

The package exposes one binary:

dosido <command> [options]

Global Options

  • --root <path>: project root. Defaults to process.cwd().
  • --dry-run: return or print planned file changes without writing them.
  • --plain: use plain output. CI=true or CI=1 also enables plain output.
  • --json: emit a structured result to stdout.
  • --help
  • --version

Commands

move

    move <source-file> <target-file>
      Rename or move a file, preserving imports in both directions

    move <source-file-1> <source-file-2> ... <target-directory>
      Move one or more files to a new directory, fixing all impacted imports

extract

    extract <source-file> <name[,name...]> <target-file>
      Extract one or more definitions (function, variable, class, type, interface, enum)
      to a target file, fixing all impacted imports. If a definition that's being
      extracted depends on an un-exported definition, that dependency becomes exported.

incoming

    incoming <path> [--include <glob>] [--ignore <glob>] [--json]
      Find all "usages of imports from <path>", where <path> can be a file or
      directory. Helpful when trying to decouple different parts of a project.

      --include <glob>: repeatable file include glob for dependency analysis.
      --ignore <glob>: repeatable ignore glob for dependency analysis.

barrel

    barrel <directory> [--include <glob>] [--ignore <glob>] [--json]
      Create or update <directory>/index.ts, rewrite supported incoming imports
      from files inside <directory> to the directory barrel.

      --include <glob>: repeatable file include glob for dependency analysis.
      --ignore <glob>: repeatable ignore glob for dependency analysis.
      --specifier-style <preserve|js|extensionless>: generated barrel export style.

barrel rewrites references from outside the target directory only. It exports the names needed by those incoming references, rewrites default imports to named imports using the local default name, and rewrites namespace imports through a generated namespace export. Dynamic imports and side-effect imports are skipped with warnings. Existing one-hop re-exports are treated as satisfying generated barrel exports when they already expose the same public name. By default, generated barrel exports preserve explicit JS-family import extensions when the incoming reference uses them; --specifier-style js forces generated exports to use .js specifiers and --specifier-style extensionless emits extensionless specifiers.

decouple

    decouple <folder-a> <folder-b> <shared-dir>
      Move all definitions within either <folder-a> or <folder-b> that are depended on
      by files in *both* <folder-a> and <folder-b> to a new <shared-dir> location.

      --include <glob>: repeatable file include glob for dependency analysis.
      --ignore <glob>: repeatable ignore glob for dependency analysis.
      --no-extract: always plan moves instead of definition extraction.
      --min-lines <n>: only consider extraction for files with at least this many lines.
      --max-extract-defs <n>: maximum named imports to extract from one crossed file.

workspace

    workspace init-package <source-root> <target-package-dir> [--name <name>]
      Convert a package root into a pnpm workspace root plus one legacy package.

    workspace graph <source-package-dir> --folders <glob> [--json]
      Analyze folder/package dependency boundaries and report which folders can
      be safely extracted into workspace packages.

      --folders <glob>: repeatable candidate folder glob, relative to source package.
      --include <glob>: repeatable file include glob for dependency analysis.
      --ignore <glob>: repeatable ignore glob for dependency analysis.

    workspace extract <source-folder> <target-package-dir> --from-package <source-package-dir> --name <name>
      Extract one folder into a new pnpm workspace package and rewrite imports
      to the new package entry.

      --from-package <dir>: source package directory containing the folder.
      --name <name>: required new workspace package name.
      --include <glob>: repeatable file include glob for dependency analysis.
      --ignore <glob>: repeatable ignore glob for dependency analysis.
      --no-derive-exports: create only the package entry export.

    workspace compat-package <source-package-dir> --packages <pattern>
      Create legacy package wrapper files for existing export-map subpaths.

    workspace cleanup-manifest <package-dir> [--keep <name>]
      Remove unused dependency entries from a package manifest.

    workspace audit <package-dir>
      Report likely stale export-map, files, and script entries.

workspace graph is the safe first step. It reports candidate folders as extractable only when they do not import files that would remain in the source package and do not create package dependency cycles.

workspace extract requires the extracted folder to have an index.ts or index.tsx entry and rewrites supported outside imports to the package entry, for example @acme/core. Dynamic imports crossing the extraction boundary are blocked. Side-effect imports into the extracted folder are only allowed when they target the folder entry file.

When possible, workspace extract derives package subpath exports and matching bin entries from the source package export map. workspace graph classifies test-only extraction blockers separately and includes suggested dosido move commands for the common “move tests/support first” cleanup. workspace compat-package can create compatibility wrapper exports in the legacy package after subpackages have been extracted, and workspace cleanup-manifest removes manifest dependencies no longer referenced by a package.

unmix-react

    unmix-react [--include <glob>] [--ignore <glob>]
      Extract non-component toplevel exports from .tsx files, to make the file work
      with hot-module-reloading (HMR).

      --include <glob>: repeatable file include glob.
      --ignore <glob>: repeatable ignore glob.
      --group-types: instead of breaking each type into its own file, make a single
                     'types.ts' file
      --group-consts: same as group-types, but for consts
      --group-related: group exports that depend on each other into the same files

cleanup-exports

    cleanup-exports [--json]
      Find exported things that are never imported, and remove the 'export' keyword.
      Preserves default exports when they are the only export in a file.
      Treats namespace imports, dynamic imports, and export-all declarations as whole-module use.

      --entry <paths>: comma-separated entry points whose exports should be preserved.
      --ignore <patterns>: comma-separated ignore globs.
      --verbose
      --json: write JSON to stdout.

Public API

The package exports typed command functions:

import {
  moveFile,
  extractDefinition,
  listIncomingDeps,
  makeBarrel,
  analyzeWorkspaceGraph,
  initWorkspacePackage,
  extractWorkspacePackage,
  createCompatibilityPackageWrappers,
  cleanupWorkspaceManifest,
  auditWorkspacePackage,
  decoupleDirectories,
  fixNonComponentExports,
  removeUnusedExports,
} from 'dosido';

Command APIs accept explicit options and return structured results with planned or applied file changes. CLI printing is kept separate from the command logic.

Resolution

dosido resolves relative imports, extensionless imports, index modules, TypeScript rootDirs, baseUrl, and paths aliases from tsconfig.json.

Alias rewriting is conservative: tests cover alias resolution, while source rewrite paths favor relative paths when moving/extracting code or creating barrels.

Development Checks

pnpm build
pnpm test:run
pnpm lint
pnpm pack:dry-run

AI Agents

There's a SKILL.md file included to help agents use dosido.

Releasing

User-facing changes should include a changeset:

pnpm changeset

Merging changesets to main makes the release workflow open or update a version PR. Merging that version PR runs the checks again and publishes to npm with:

pnpm release

The GitHub repository needs an NPM_TOKEN secret with permission to publish the package.

Releases

No releases published

Packages

 
 
 

Contributors