SkillAgentSearch skills...

Core

A batteries-included step runner library, suitable for creating migration tooling, codemods, scaffolding CLIs, etc.

Install / Use

/learn @DubstepJS/Core
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Dubstep

Build status

A batteries-included step runner library, suitable for creating migration tooling, codemods, scaffolding CLIs, etc.

Dubstep has utility functions for file system operations, Babel-based codemodding, Git operations and others.

License: MIT

Installation | Usage | API | Recipes | Motivation


Installation

yarn add @dubstep/core

Usage

import {
  Stepper,
  step,
  gitClone,
  findFiles,
  withTextFile,
  getRestorePoint,
  removeFile,
  createRestorePoint,
} from '@dubstep/core';
import inquirer from 'inquirer';

async function run() {
  const state = {name: ''};
  const stepper = new Stepper([
    step('name', async () => {
      state.name = await inquirer.prompt({message: 'Name:', type: 'input'});
    }),
    step('clone', async () => {
      gitClone('some-scaffold-template.git', state.name);
    }),
    step('customize', async () => {
      const files = await findFiles('**/*.js', f => /src/.test(f));
      for (const file of files) {
        withTextFile(file, text => text.replace(/{{name}}/g, state.name));
      }
    }),
  ]);
  stepper
    .run({from: await getRestorePoint(restoreFile)})
    .then(() => removeFile(reportFile))
    .catch(e => createRestorePoint(reportFile, e));
}
run();

API

Core | Utilities | top

All API entities are available as non-default import specifiers, e.g. import {Stepper} from '@dubstep/core';

Utilities can also be imported individually, e.g. import {findFiles} from '@dubstep/core/find-files';

Core

Stepper

import {Stepper} from '@dubstep/core';

class Stepper {
  constructor(preset: Preset)
  run(options: StepperOptions): Promise<any> // rejects w/ StepperError
  on(type: 'progress', handler: StepperEventHandler)
  off(type: 'progress', handler: StepperEventHandler)
}

type Preset = Array<Step>
type StepperOptions = ?{from: ?number, to: ?number}
type StepperEventHandler = ({index: number, total: number, step: string}) => void

A stepper can take a list of steps, run them in series and emit progress events.

step

import {step} from '@dubstep/core';

type step = (name: string, step: AsyncFunction) => Step;

type Step = {name: string, step: AsyncFunction};
type AsyncFunction = () => Promise<any>;

A step consists of a descriptive name and an async function.

StepperError

import {StepperError} from '@dubstep/core';

class StepperError extends Error {
  constructor(error: Error, step: string, index: number),
  step: string,
  index: number,
  message: string,
  stack: string,
}

A stepper error indicates what step failed. It can be used for resuming execution via restore points.

Utilities

File system | Babel | Git | Restore points | Misc

File system

findFiles
import {findFiles} from '@dubstep/core';

type findFiles = (glob?: string, filter?: string => boolean) => Promise<Array<string>>;

Resolves to a list of file names that match glob and match the condition from the filter function. Respects .gitignore.

moveFile
import {moveFile} from '@dubstep/core';

type moveFile = (oldName: string, newName: string) => Promise<any>;

Moves an existing file or directory to the location specified by newName. If the file specified by oldName doesn't exist, it no-ops.

readFile
import {readFile} from '@dubstep/core';

type readFile = (file: string) => Promise<string>;

Reads the specified file into a UTF-8 string. If the file doesn't exist, the function throws a ENOENT error.

removeFile
import {removeFile} from '@dubstep/core';

type removeFile = (file: string) => Promise<any>;

Removes the specified file. If the file doesn't exist, it no-ops.

withIgnoreFile
import {withIgnoreFile} from '@dubstep/core';

type withIgnoreFile = (file: string, fn: IgnoreFileMutation) => Promise<any>;
type IgnoreFileMutation = (data: Array<string>) => Promise<?Array<string>>;

Opens a file, parses each line into a string, and calls fn with the array of lines. Then, writes the return value or the array back into the file.

If the file does not exist, fn is called with an empty array, and the file is created (including missing directories).

withJsFile
import {withJsFile} from '@dubstep/core';

type withJsFile = (file: string, fn: JsFileMutation, options: ParserOptions) => Promise<any>;
type JsFileMutation = (program: BabelPath, file: string) => Promise<any>;
type ParserOptions = ?{mode: ?('typescript' | 'flow')};

Opens a file, parses each line into a Babel BabelPath, and calls fn with BabelPath. Then, writes the modified AST back into the file.

If the file does not exist, fn is called with a empty program BabelPath, and the file is created (including missing directories).

See the Babel handbook for more information on BabelPath's API.

withJsFiles
import {withJsFiles} from '@dubstep/core';

type withJsFiles = (glob: string, fn: JsFileMutation, options: ParserOptions) => Promise<any>;
type JsFileMutation = (program: BabelPath, file: string) => Promise<any>;
type ParserOptions = ?{mode: ?('typescript' | 'flow')};

Runs withJsFile only on files that match glob.

See the Babel handbook for more information on BabelPath's API.

withJsonFile
import {withJsonFile} from '@dubstep/core';

type withJsonFile = (file: string, fn: JsonFileMutation) => Promise<any>;
type JsonFileMutation = (data: any) => Promise<any>;

Opens a file, parses each line into a Javascript data structure, and calls fn with it. Then, writes the return value or modified data structure back into the file.

If the file does not exist, fn is called with an empty object, and the file is created (including missing directories).

withTextFile
import {withTextFile} from '@dubstep/core';

type withTextFile = (file: string, fn: TextFileMutation) => Promise<any>;
type TextFileMutation = (data: string) => Promise<?string>;

Opens a file, parses each line into a string, and calls fn with it. Then, writes the return value back into the file.

If the file does not exist, fn is called with an empty string, and the file is created.

writeFile
import {writeFile} from '@dubstep/core';

type writeFile = (file: string, data: string) => Promise<any>;

Writes data to file. If the file doesn't exist, it's created (including missing directories)


Babel

ensureJsImports
import {ensureJsImports} from '@dubstep/core';

type ensureJsImports = (path: BabelPath, code: string, options: ParserOptions) => Array<Object<string, string>>;
type ParserOptions = ?{mode: ?('typescript' | 'flow')};

If an import declaration in code is missing in the program, it's added. If it's already present, specifiers are added if not present. Note that the BabelPath should be for a Program node, and that it is mutated in-place.

Returns a list of maps of specifier local names. The default specifier is bound to the key default.

If a specifier is already declared in path, but there's a conflicting specifier in code, the one in path is retained and returned in the output map. For example:

// default specifier is already declared as `a`, but trying to redeclare it as `foo`
ensureJsImports(parseJs(`import a from 'a';`), `import foo from 'a'`);
// > {default: 'a'};

A BabelPath can be obtained from withJsFile, withJsFiles or parseJs.

visitJsImport
import {visitJsImport} from '@dubstep/core';

type visitJsImport = (
  path: BabelPath,
  code: string,
  handler: (importPath: BabelPath, refPaths: Array<BabelPath>) => void),
  options: ParserOptions,
: void
type ParserOptions = ?{mode: ?('typescript' | 'flow')};

This function is useful when applying codemods to specific modules which requires modifying the ast surrounding specific modules and their usage. This module works robustly across various styles of importing. For example:

visitJsImport(
  parseJs(`
    import {a} from 'a';
    a('test')
    console.log(a);
  `),
  `import {a} from 'a';`,
  (importPath, refPaths) => {
    // importPath corresponds to the ImportDeclaration from 'a';
    // refPaths is a list of BabelPaths corresponding to the usage of the a variable
  }
);
hasImport
import {hasImport} from '@dubstep/core';

type hasImport = (path: BabelPath<Program>, code: string, options: ParserOptions) => boolean
type ParserOptions = ?{mode: ?('typescript' | 'flow')};

Checks if a given program node contains an import matching a string.

hasImport(
  parseJs(`
    import {a} from 'a';
    console.log(a);
  `),
  `import {a} from 'a';`
); // true
collapseImports
import {collapseImports} from '@dubstep/core';

type collapseImports = (path: BabelPath<Program>) => BabelPath<Program>

This function collapses multiple import declarations with the same source into a single import statement by combining the specifiers. For example:

import A, {B} from 'a';
import {C, D} from 'a';

// => 

import A, {B, C, D} from 'a';
generateJs
import {generateJs} from '@dubstep/core';

type generateJs = (path: BabelPath) => string;

Converts a Program BabelPath into a Javascript code string. A BabelPath can be obtained from withJsFile, withJsFiles or parseJs.

insertJsAfter
import {insertJsAfter} from '@dubstep/core';

type insertJsAfter = (path: Bab
View on GitHub
GitHub Stars40
CategoryDevelopment
Updated1mo ago
Forks11

Languages

JavaScript

Security Score

90/100

Audited on Feb 19, 2026

No findings