/jsmod

An AST-based JavaScript refactoring tool.

Primary LanguageJavaScriptMIT LicenseMIT

JSMod Build Status

An AST-based JavaScript refactoring tool. The guiding philosophy behind jsmod is that traversing and manipulating an abstract syntax tree should be as familiar as traversing and manipulating the DOM tree. Heavily influenced by jQuery and jscodeshift, JSMod features a powerful CSS-like selector engine alongside a chainable API for traversal and manipulation.

Use case

TODO

Example usage

Update all object literals to use the shorthand notation.

Input

({foo: foo, bar: bar, baz: baz})

Expected output

({foo, bar, baz})

Input AST

└── ObjectExpression,
    └── properties
        ├── Property
        │   ├── shorthand: false
        │   ├── key
        │   │   └── Identifier
        │   │        └── name: "foo"
        │   └── value
        │       └── Identifier
        │            └── name: "foo"
        ├── Property
        │   ├── shorthand: false
        │   ├── key
        │   │   └── Identifier
        │   │        └── name: "bar"
        │   └── value
        │       └── Identifier
        │            └── name: "bar"
        └── Property
            ├── shorthand: false
            ├── key
            │   └── Identifier
            │        └── name: "baz"
            └── value
                └── Identifier
                     └── name: "baz"

Example jsmod transform

// Parse code into an AST.
const program = recast.parse(input).program;

// Helper functions
const isLongHand = el => el.attr(['shorthand']) === false;
const hasIdenticalKeyValue = el => el.attr(['key', 'name']) === el.attr(['value', 'name']);

// Apply transformation
const newProgram = jsmod(program)
	  .find('ObjectExpression > Property')
	  .filter(isLongHand)
	  .filter(hasIdenticalKeyValue)
	  .attr('shorthand', true);

const code = recast.print(newProgram.getAst()).code;
// TODO: Save to disk.

Features

  • selectors
    • element e.g. Identifier
    • combinators
      • descendant combinator e.g. ArrayExpression Identifier
      • child combinator e.g. ArrayExpression > Identifier
      • adjacent sibling combinator e.g. Identifier + Literal
      • general sibling combinator e.g. Identifier ~ Literal
    • attribute descent e.g. ClassDeclaration.id > Identifier
    • attribute selectors
      • has attribute - e.g. [name]
      • exact match - e.g. [attribute="value"]
      • fuzzy match - e.g. [attribute~="value"]
      • begins with - e.g. [attribute^="value"]
      • ends with - e.g. [attribute$="value"]
      • contains - e.g. [attribute*="value"]
    • universal selector e.g. *
    • pseudo selectors
      • :first
      • :first-child
      • :first-of-type
      • :has()
      • :last
      • :last-child
      • :last-of-type
      • :not()
      • :nth-child
      • :nth-last-child
      • :nth-last-of-type
      • :nth-of-type
      • :root
  • traversal
    • .children()
    • .closest()
    • .end()
    • .find()
    • .next()
    • .nextAll()
    • .nextUtil()
    • .parent()
    • .parents()
    • .parentsUtil()
    • .prev()
    • .prevAll()
    • .prevUtil()
    • .siblings()
  • filtering
    • .eq()
    • .filter()
    • .first()
    • .has()
    • .is()
    • .last()
    • .map()
    • .not()
    • .slice()
  • manipulation
    • .after()
    • .append()
    • .appendTo()
    • .attr()
    • .before()
    • .forEach()
    • .insertAfter()
    • .insertBefore()
    • .prepend()
    • .prependTo()
    • .remove()
    • .replaceWith()
    • .replaceAll()
    • .sort()
  • misc
    • .get()
    • .toArray()