npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

touch-all

v2.1.2

Published

CLI tool to create folder structures from markdown tree representations

Readme

touch-all

CLI tool to create folder structures from Markdown tree representations.

It behaves like mkdir -p and touch combined, creating directories and files as needed. It can be used to quickly scaffold a project structure or generate placeholder files.

Features

  • Accepts tree strings in box-drawing (├──, └──, ) or indentation (spaces) format
  • Trailing slash / marks a directory; no trailing / marks a file
  • Symlink creation with link-name -> target syntax
  • Inline comments stripped automatically (# ..., // ..., <- ..., ← ...)
  • --dry-run (-n) parses and validates without touching the file system
  • Prints a summary line on success (✓ Done. N items created.); --verbose adds per-item detail
  • Path traversal protection — no file, folder, or symlink target can escape the target directory
  • Importable as a Node.js library with full TypeScript types
  • Pipable interface to run as echo tree | touch-all in CI or scripts

Installation

npm install -g touch-all

or with npx without installing:

npx touch-all@latest "..."

Usage

Basic (current directory)

touch-all "
my-project/
├── src/
│   ├── index.ts
│   └── index.test.ts
├── package.json
└── README.md
"

Arguments

  • --path , -p – specifies target directory. By default, the current working directory is used. Can be an absolute path or a path relative to the current working directory.
touch-all "..." --path=./my-project
touch-all "..." -p ~/Documents/my-project
  • --dry-run , -n – parses and validates the tree string without creating any files or directories. Useful for testing and debugging.
touch-all "..." --dry-run
touch-all "..." -n
  • --verbose , -v – prints every created path to the console. Useful for seeing exactly what will be created, especially with complex structures. By default only a summary line is printed on success (✓ Done. N items created.). --verbose adds per-item detail.
touch-all "..." --verbose
touch-all "..." -v
  • --yes , -y – skips the confirmation prompt when symlinks point outside the project root. Required in non-interactive environments (scripts, CI).
touch-all "..." --yes
touch-all "..." -y
  • --completions – generates a completion script for a specific shell. Supported shells: sh, bash, fish, zsh.
  • --log-level – sets the minimum log level for a command. Supported levels: all, trace, debug, info, warning, error, fatal, none. The default log level is warning.
  • --help , -h – shows the help documentation for a command.
  • --wizard – starts wizard mode for a command, providing an interactive step-by-step interface.
  • --version – shows the version of the application.

Tree Format

Box-drawing characters

my-project/
├── .config/
│   ├── tsconfig.json
│   └── vite.config.ts
├── src/
│   ├── index.ts
│   └── index.test.ts
├── package.json
└── README.md

Indentation (spaces)

my-project/
  .config/
    tsconfig.json
    vite.config.ts
  src/
    index.ts
    index.test.ts
  package.json
  README.md

Both formats produce identical results.

Rules

| Syntax | Meaning | | ----------------- | -------------------------------- | | name/ | directory | | name | file | | dir/sub/ | directory at an explicit subpath | | dir/sub/file.ts | file at an explicit subpath | | ... # comment | ignored (stripped) | | ... // comment | ignored (stripped) | | ... <- comment | ignored (stripped) | | ... ← comment | ignored (stripped) | | name -> target | symlink pointing to target |

Items at the root level (no indentation / no parent) are created directly inside the target directory.

Symlinks

Use link-name -> target to create a symlink. The target is passed as-is to the OS — use paths relative to the symlink's location, just as you would in a shell.

my-project/
├─ src/
│  ├─ index.ts
│  └─ utils -> ../shared/utils.ts   # symlink to a sibling directory
└─ shared/
   └─ utils.ts

If target ends with /, the symlink is created as a directory symlink (relevant on Windows). The link name's suffix is ignored.

[!WARNING] If any symlink target resolves outside the project root (--path), touch-all will prompt for confirmation before proceeding. Use --yes to skip the prompt in scripts or CI.

When using fileStructureCreator directly as a library, outside-root symlinks are rejected by default with a PathTraversalError. Pass { allowOutsideSymlinks: true } as the third argument to allow them.

Library API

npm install touch-all
import {
  parserFolderStructure,
  fileStructureCreator,
  resolveProjectPathToBase,
  isSymlinkOutsideRoot,
  PathTraversalError,
} from 'touch-all'
import type { ParserResult, ParserResultLineItem } from 'touch-all'

parserFolderStructure(tree: string): ParserResult

Parses a tree string into a flat list of items. Pure function, no I/O.

Each item is one of:

type ParserResultLineItem =
  | { type: 'file'; path: string }
  | { type: 'folder'; path: string }
  | { type: 'symlink'; path: string; target: string }
const items = parserFolderStructure(`
  src/
    index.ts
    link -> ../shared.ts
`)
// [
//   { type: 'folder',  path: 'src' },
//   { type: 'file',    path: 'src/index.ts' },
//   { type: 'symlink', path: 'src/link', target: '../shared.ts' },
// ]

fileStructureCreator(items, basePath, options?): Effect<void, PathTraversalError>

Creates the parsed structure on disk under basePath. Returns an Effect.

By default, symlinks whose targets resolve outside basePath are rejected with PathTraversalError. Pass { allowOutsideSymlinks: true } to allow them.

import { Effect } from 'effect'
import { NodeContext, NodeRuntime } from '@effect/platform-node'

const projectDirectory = '/absolute/target/path'
const items = parserFolderStructure(tree)

// Default: outside-root symlinks are rejected
fileStructureCreator(items, projectDirectory).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain)

// Opt out of symlink containment check
fileStructureCreator(items, projectDirectory, { allowOutsideSymlinks: true }).pipe(
  Effect.provide(NodeContext.layer),
  NodeRuntime.runMain
)

isSymlinkOutsideRoot(linkPath, target, basePath, path): boolean

Pure function that returns true if a symlink target resolves outside basePath. Useful for pre-validating items before passing them to fileStructureCreator.

import { Path } from '@effect/platform'
import { Effect } from 'effect'
import { NodeContext } from '@effect/platform-node'
import { isSymlinkOutsideRoot } from 'touch-all'

Effect.gen(function* () {
  const path = yield* Path.Path
  const outside = isSymlinkOutsideRoot('src/link', '../../etc/passwd', '/project', path)
  // true — target escapes /project
}).pipe(Effect.provide(NodeContext.layer))

resolveProjectPathToBase(projectPath: string, basePath: string): Effect<string, PathTraversalError>

Resolves projectPath relative to basePath and rejects any path that would escape basePath (path traversal protection).

[!WARNING] projectPath cannot traverse outside of basePath. If projectPath is absolute, it treated as relative to basePath. If projectPath is relative, it is resolved against basePath. In either case, if the resulting path is outside of basePath, a PathTraversalError is thrown.

Error types

| Class | _tag | When thrown | | -------------------- | ---------------------- | -------------------------------------------------------------------------- | | PathTraversalError | 'PathTraversalError' | A file/folder path or symlink target escapes basePath (unless opted out) |

License

GPL-3.0-or-later