1.0.0 • Published 2 years ago

js-code-match v1.0.0

Weekly downloads
-
License
ISC
Repository
github
Last release
2 years ago

js-code-match

Simple JS Code Manipulation via AST

This is a javascript source-transformation utility which uses jscodeshift under the hood but exposes a much simpler API that matches the mental model that a developer may have.

Transformation is done using ASTs, but driven by code strings instead of directly manipulating the AST structure itself.

Example

Source

let x=1;
let y = x*2;

Transformation

Placeholder values with format $$VAR are used to match against the source and substitute into the replace.

let codeMatch = require('js-code-match');
let cm = codeMatchMatch(source);
cm.replace(
    'let $$1 = $$2',
    'const $$1 = $$2;// Changed $$1 to const for $$2'
);
console.log(cm.toSource())

Output

Because code is transformed at the AST level, whitespace is ignored in matches. Code is matched as a structure.

const x = 1;// Changed x to const for 1
const y = x*2;// Changed y to const for x * 2

Transformation Function

A third parameter can be passed to the replace() function that can manipulate matches or swap out the replace template depending on the source match.

Transformation

cm.replace(
    'let $$1 = $$2',
    'const $$1 = $$2;// Changed $$1 to const for $$2',
    function(node,matches) {
      if (matches.$$1.name=='y') {
        matches.$$1.name='UPDATED'
      }
    }
);

Output

const x = 1;// Changed x to const for 1
const UPDATED = x*2;// Changed UPDATED to const for x * 2

Transforming Blocks

Entire block statements can be matched with a placeholder, which will match N expressions inside the block.

Source

if (x==1) {
  x++;
}

Transformation

cm.replace(
    `if ($$1) { $$2 }`,
    `if ($$1) { console.log("start"); $$2; console.log("end"); }`
);

Output

if (x==1) {
  console.log("start");
  x++;
  console.log("end");
}

$$COUNT

The special replace placeholder $$COUNT can be used for an auto-incrementing global integer starting at 0. With each use, it increments.

Source

func('a');
func('b');
func('c');

Transformation

cm.replace(
    `$$1($$2)`,
    `$$1($$2,$$COUNT,$$COUNT)`
);

Output

func('a', 0, 1);
func('b', 2, 3);
func('c', 4, 5);

Find

You can find matches and return them, without replacing.

Source

func('a');
func('b');
func('c');

Find

The "logFindResults" method provides a convenient way to visualize the results of a find operation, for debugging or inspection.

let matches = cm.find('$$1($$2)')
cm.logFindResults(matches);

Results

The find operation returns an array of matches with the following structure:

{
    node,
    matches,
    values,
    line,
    toSource(),
    getValue()
}

Output

+------+-----------+------------------+-----------------------------+
| LINE | SOURCE    | MATCHES          | AST                         |
+------+-----------+------------------+-----------------------------+
| 1    | func('a') | {                | {                           |
|      |           |   "$$1": "func", |   "type": "CallExpression", |
|      |           |   "$$2": "'a'"   |   "callee": {               |
|      |           | }                |     "type": "Identifier",   |
|      |           |                  |     "name": "func",         |
|      |           |                  |     "optional": false       |
|      |           |                  |   },                        |
|      |           |                  |   "typeArguments": null,    |
|      |           |                  |   "arguments": [            |
|      |           |                  |     {                       |
|      |           |                  |       "type": "Literal",    |
|      |           |                  |       "value": "a",         |
|      |           |                  |       "raw": "'a'"          |
|      |           |                  |     }                       |
|      |           |                  |   ],                        |
|      |           |                  |   "optional": false         |
|      |           |                  | }                           |
+------+-----------+------------------+-----------------------------+
| 2    | func('b') | {                | {                           |
|      |           |   "$$1": "func", |   "type": "CallExpression", |
|      |           |   "$$2": "'b'"   |   "callee": {               |
|      |           | }                |     "type": "Identifier",   |
|      |           |                  |     "name": "func",         |
|      |           |                  |     "optional": false       |
|      |           |                  |   },                        |
|      |           |                  |   "typeArguments": null,    |
|      |           |                  |   "arguments": [            |
|      |           |                  |     {                       |
|      |           |                  |       "type": "Literal",    |
|      |           |                  |       "value": "b",         |
|      |           |                  |       "raw": "'b'"          |
|      |           |                  |     }                       |
|      |           |                  |   ],                        |
|      |           |                  |   "optional": false         |
|      |           |                  | }                           |
+------+-----------+------------------+-----------------------------+
| 3    | func('c') | {                | {                           |
|      |           |   "$$1": "func", |   "type": "CallExpression", |
|      |           |   "$$2": "'c'"   |   "callee": {               |
|      |           | }                |     "type": "Identifier",   |
|      |           |                  |     "name": "func",         |
|      |           |                  |     "optional": false       |
|      |           |                  |   },                        |
|      |           |                  |   "typeArguments": null,    |
|      |           |                  |   "arguments": [            |
|      |           |                  |     {                       |
|      |           |                  |       "type": "Literal",    |
|      |           |                  |       "value": "c",         |
|      |           |                  |       "raw": "'c'"          |
|      |           |                  |     }                       |
|      |           |                  |   ],                        |
|      |           |                  |   "optional": false         |
|      |           |                  | }                           |
+------+-----------+------------------+-----------------------------+

Other Functionality

Additional functionality exists but is not yet documented.