-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3c31fb7
commit b5e090c
Showing
6 changed files
with
373 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,73 @@ | ||
# typescript-library-skeleton | ||
# print-highlighted-ast | ||
|
||
[![CircleCI](https://circleci.com/gh/jedwards1211/typescript-library-skeleton.svg?style=svg)](https://circleci.com/gh/jedwards1211/typescript-library-skeleton) | ||
[![Coverage Status](https://codecov.io/gh/jedwards1211/typescript-library-skeleton/branch/master/graph/badge.svg)](https://codecov.io/gh/jedwards1211/typescript-library-skeleton) | ||
[![CircleCI](https://circleci.com/gh/codemodsquad/print-highlighted-ast.svg?style=svg)](https://circleci.com/gh/codemodsquad/print-highlighted-ast) | ||
[![Coverage Status](https://codecov.io/gh/codemodsquad/print-highlighted-ast/branch/master/graph/badge.svg)](https://codecov.io/gh/codemodsquad/print-highlighted-ast) | ||
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) | ||
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) | ||
[![npm version](https://badge.fury.io/js/typescript-library-skeleton.svg)](https://badge.fury.io/js/typescript-library-skeleton) | ||
[![npm version](https://badge.fury.io/js/print-highlighted-ast.svg)](https://badge.fury.io/js/print-highlighted-ast) | ||
|
||
This is my personal skeleton for creating an typescript library npm package. You are welcome to use it. | ||
print a babel AST with specific nodes highlighted, for debugging codemods | ||
|
||
## Quick start | ||
# Example | ||
|
||
```sh | ||
npx 0-60 clone https://github.com/jedwards1211/typescript-library-skeleton.git | ||
## Script | ||
|
||
```ts | ||
import printHighlightedAst from 'print-highlighted-ast' | ||
import { parse } from '@babel/parser' | ||
import traverse, { NodePath } from '@babel/traverse' | ||
import chalk from 'chalk' | ||
|
||
const code = ` | ||
const foo = () => 2 | ||
const bar = 500 + 600 | ||
` | ||
const ast = parse(code) | ||
const highlights: Array<[NodePath<any>, (code: string) => string]> = [] | ||
traverse(ast, { | ||
NumericLiteral: path => { | ||
highlights.push([path, chalk.bgBlue]) | ||
}, | ||
}) | ||
|
||
console.log(printHighlightedAst(code, { highlights }) | ||
``` | ||
## Tools used | ||
|
||
- babel 7 | ||
- typescript | ||
- mocha | ||
- chai | ||
- istanbul | ||
- nyc | ||
- eslint | ||
- prettier | ||
- husky | ||
- semantic-release | ||
- renovate | ||
- Circle CI | ||
- Codecov.io | ||
## Output | ||
<code> | ||
const foo = () => <span style="background-color: blue;">2</span><br /> | ||
const bar = <span style="background-color: blue;">500</span> + <span style="background-color: blue;">600</span> | ||
<code> | ||
# API | ||
You can pass the source code or a parsed AST as the first argument. If you pass an AST it will be | ||
converted to code with `@babel/generator`. | ||
Each element of `highlights` is a tuple of `[0]` path you want to highlight, `[1]` highlighting function. | ||
You can pass options to `@babel/parser` and `@babel/generator` as shown below. | ||
The code will be parsed with `@babel/parser` to determine the source range of the paths you want to | ||
highlight. If you're using language extensions like Flow, Typescript, or JSX, you'll need to pass | ||
those plugins in `parseOptions`. | ||
```ts | ||
import { Node } from '@babel/types' | ||
import { NodePath } from '@babel/traverse' | ||
import generate from '@babel/generator' | ||
import { parse } from '@babel/parser' | ||
|
||
function printHighlightedAst( | ||
codeOrAst: string | Node, | ||
{ | ||
highlights, | ||
generateOptions, | ||
parseOptions, | ||
}: { | ||
highlights: Iterable<[NodePath<any>, (code: string) => string]> | ||
generateOptions?: Parameters<typeof generate>[1] | ||
parseOptions?: Parameters<typeof parse>[1] | ||
} | ||
): string | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,46 @@ | ||
/* eslint-env mocha */ | ||
|
||
describe('test setup', () => { | ||
it('works', () => { | ||
// fill me out! | ||
import printHighlightedAst from './' | ||
import { parse } from '@babel/parser' | ||
import { expect } from 'chai' | ||
import traverse, { NodePath } from '@babel/traverse' | ||
import chalk from 'chalk' | ||
|
||
describe('printHighlightedAst', () => { | ||
it('passing code', () => { | ||
const code = ` | ||
const foo = () => 2 | ||
const bar = 500 + 600 | ||
` | ||
const ast = parse(code) | ||
const highlights: Array<[NodePath<any>, (code: string) => string]> = [] | ||
traverse(ast, { | ||
NumericLiteral: path => { | ||
highlights.push([path, chalk.bgBlue]) | ||
}, | ||
}) | ||
|
||
expect(printHighlightedAst(code, { highlights })).to.equal(chalk` | ||
const foo = () => {bgBlue 2} | ||
const bar = {bgBlue 500} + {bgBlue 600} | ||
`) | ||
}) | ||
it('passing ast', () => { | ||
const code = ` | ||
const foo = () => 2 | ||
const bar = 500 + 600 | ||
` | ||
const ast = parse(code) | ||
const highlights: Array<[NodePath<any>, (code: string) => string]> = [] | ||
traverse(ast, { | ||
NumericLiteral: path => { | ||
highlights.push([path, chalk.bgBlue]) | ||
}, | ||
}) | ||
|
||
expect(printHighlightedAst(ast, { highlights })).to | ||
.equal(chalk`const foo = () => {bgBlue 2}; | ||
const bar = {bgBlue 500} + {bgBlue 600};`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Node } from '@babel/types' | ||
import { NodePath } from '@babel/traverse' | ||
import generate from '@babel/generator' | ||
import { parse } from '@babel/parser' | ||
import get from 'lodash/get' | ||
|
||
function getPathKeys(path: NodePath<any>): Array<string | number> { | ||
const result: Array<string | number> = [] | ||
while (path) { | ||
result.push(path.key) | ||
if (path.parentKey && path.parentKey !== path.key) | ||
result.push(path.parentKey) | ||
path = path.parentPath | ||
} | ||
return result.reverse() | ||
} | ||
|
||
function transformRange( | ||
code: string, | ||
start: number, | ||
end: number, | ||
transform: (code: string) => string | ||
): string { | ||
return `${code.substring(0, start)}${transform( | ||
code.substring(start, end) | ||
)}${code.substring(end)}` | ||
} | ||
|
||
export default function printHighlightedAst( | ||
codeOrAst: string | Node, | ||
{ | ||
highlights, | ||
generateOptions, | ||
parseOptions, | ||
}: { | ||
highlights: Iterable<[NodePath<any>, (code: string) => string]> | ||
generateOptions?: Parameters<typeof generate>[1] | ||
parseOptions?: Parameters<typeof parse>[1] | ||
} | ||
): string { | ||
const convertedHighlights: Array<[ | ||
Array<string | number>, | ||
(code: string) => string | ||
]> = [...highlights] | ||
.sort(([a], [b]): number => { | ||
const aIndex = a.node.start | ||
const bIndex = b.node.start | ||
// descending order | ||
return aIndex > bIndex ? -1 : aIndex < bIndex ? 1 : 0 | ||
}) | ||
.map(([path, highlighter]) => [getPathKeys(path), highlighter]) | ||
let printed = | ||
typeof codeOrAst === 'string' | ||
? codeOrAst | ||
: generate(codeOrAst as any, generateOptions).code | ||
const reparsed = parse(printed, parseOptions) | ||
for (const [pathKeys, highlight] of convertedHighlights) { | ||
const node = get(reparsed, pathKeys) | ||
if (!node) continue | ||
const { start, end } = node | ||
if (start == null || end == null) continue | ||
printed = transformRange(printed, start, end, highlight) | ||
} | ||
return printed | ||
} |
Oops, something went wrong.