Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed Apr 7, 2020
1 parent 3c31fb7 commit b5e090c
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 135 deletions.
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
"extends": [
"@jedwards1211/eslint-config-typescript",
"eslint-config-prettier"
]
],
"rules": {
"@typescript-eslint/no-explicit-any": 0
}
}
88 changes: 65 additions & 23 deletions README.md
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
```
28 changes: 20 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "typescript-library-skeleton",
"name": "print-highlighted-ast",
"version": "0.0.0-development",
"description": "my personal ES2015 library project skeleton",
"description": "print a babel AST with specific nodes highlighted, for debugging codemods",
"main": "index.js",
"sideEffects": false,
"scripts": {
Expand Down Expand Up @@ -69,20 +69,22 @@
},
"repository": {
"type": "git",
"url": "https://github.com/jedwards1211/typescript-library-skeleton.git"
"url": "https://github.com/codemodsquad/print-highlighted-ast.git"
},
"keywords": [
"typescript"
"ast",
"babel",
"debugging"
],
"author": "Andy Edwards",
"license": "MIT",
"bugs": {
"url": "https://github.com/jedwards1211/typescript-library-skeleton/issues"
"url": "https://github.com/codemodsquad/print-highlighted-ast/issues"
},
"homepage": "https://github.com/jedwards1211/typescript-library-skeleton#readme",
"homepage": "https://github.com/codemodsquad/print-highlighted-ast#readme",
"devDependencies": {
"@babel/cli": "^7.1.5",
"@babel/core": "^7.1.6",
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-export-default-from": "^7.0.0",
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
Expand All @@ -94,11 +96,17 @@
"@babel/preset-env": "^7.1.6",
"@babel/preset-typescript": "^7.7.2",
"@babel/register": "^7.0.0",
"@babel/traverse": "^7.9.5",
"@babel/types": "^7.9.5",
"@commitlint/cli": "^6.0.2",
"@commitlint/config-conventional": "^6.0.2",
"@jedwards1211/commitlint-config": "^1.0.0",
"@jedwards1211/eslint-config-typescript": "^1.0.0",
"@types/babel__generator": "^7.6.1",
"@types/babel__traverse": "^7.0.10",
"@types/chai": "^4.2.0",
"@types/chalk": "^2.2.0",
"@types/lodash": "^4.14.149",
"@types/mocha": "^5.2.7",
"@types/node": "^12.12.6",
"babel-eslint": "^10.0.1",
Expand All @@ -122,7 +130,11 @@
"typescript": "^3.7.2"
},
"dependencies": {
"@babel/runtime": "^7.1.5"
"@babel/generator": "^7.9.5",
"@babel/parser": "^7.9.4",
"@babel/runtime": "^7.1.5",
"chalk": "^4.0.0",
"lodash": "^4.17.15"
},
"renovate": {
"extends": [
Expand Down
45 changes: 42 additions & 3 deletions src/index.spec.ts
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};`)
})
})
65 changes: 65 additions & 0 deletions src/index.ts
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
}
Loading

0 comments on commit b5e090c

Please sign in to comment.