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
npx dosido --help
pnpm dlx dosido --helpThe package exposes one binary:
dosido <command> [options]--root <path>: project root. Defaults toprocess.cwd().--dry-run: return or print planned file changes without writing them.--plain: use plain output.CI=trueorCI=1also enables plain output.--json: emit a structured result to stdout.--help--version
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 <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 <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 <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 <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 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 [--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 [--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.
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.
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.
pnpm build
pnpm test:run
pnpm lint
pnpm pack:dry-runThere's a SKILL.md file included to help agents use dosido.
User-facing changes should include a changeset:
pnpm changesetMerging 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 releaseThe GitHub repository needs an NPM_TOKEN secret with permission to publish the
package.