Skip to content

Commit 42ed775

Browse files
committed
docs
1 parent ef1ed5d commit 42ed775

File tree

1 file changed

+91
-22
lines changed

1 file changed

+91
-22
lines changed

README.md

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Parse in reverse. AST goes in, code comes out.
66

77
```js
88
import { print } from 'esrap';
9+
import ts from 'esrap/languages/ts';
910

10-
const { code, map } = print({
11+
const ast = {
1112
type: 'Program',
1213
body: [
1314
{
@@ -26,19 +27,105 @@ const { code, map } = print({
2627
}
2728
}
2829
]
29-
});
30+
};
31+
32+
const { code, map } = print(ast, ts());
3033

3134
console.log(code); // alert('hello world!');
3235
```
3336

3437
If the nodes of the input AST have `loc` properties (e.g. the AST was generated with [`acorn`](https://github.com/acornjs/acorn/tree/master/acorn/#interface) with the `locations` option set), sourcemap mappings will be created.
3538

39+
## Built-in languages
40+
41+
`esrap` ships with two built-in languages — `ts()` and `tsx()` — which can print ASTs conforming to [`@typescript-eslint/types`](https://www.npmjs.com/package/@typescript-eslint/types) (which extends [ESTree](https://github.com/estree/estree)):
42+
43+
```js
44+
import ts from 'esrap/languages/ts';
45+
import tsx from 'esrap/languages/tsx';
46+
```
47+
48+
Both languages accept an options object:
49+
50+
```js
51+
const { code, map } = print(
52+
ast,
53+
ts({
54+
// how string literals should be quoted — `single` (the default) or `double`
55+
quotes: 'single',
56+
57+
// an array of `{ type: 'Line' | 'Block', value: string, loc: { start, end } }` objects
58+
comments: []
59+
})
60+
);
61+
```
62+
63+
You can generate the `comments` array by, for example, using [Acorn's](https://github.com/acornjs/acorn/tree/master/acorn/#interface) `onComment` option.
64+
65+
## Custom languages
66+
67+
You can also create your own languages:
68+
69+
```ts
70+
import { print, type Visitors } from 'esrap';
71+
72+
const language: Visitors<MyNodeType> = {
73+
_(node, context, visit) {
74+
// the `_` visitor handles any node type
75+
context.write('[');
76+
visit(node);
77+
context.write(']');
78+
},
79+
List(node, context) {
80+
// node.type === 'List'
81+
for (const child of node.children) {
82+
context.visit(child);
83+
}
84+
},
85+
Foo(node, context) {
86+
// node.type === 'Foo'
87+
context.write('foo');
88+
},
89+
Bar(node, context) {
90+
// node.type === 'Bar'
91+
context.write('bar');
92+
}
93+
};
94+
95+
const ast: MyNodeType = {
96+
type: 'List',
97+
children: [{ type: 'Foo' }, { type: 'Bar' }]
98+
};
99+
100+
const { code, map } = print(ast, language);
101+
102+
code; // `[[foo][bar]]`
103+
```
104+
105+
The `context` API has several methods:
106+
107+
- `context.write(data: string, node?: BaseNode)` — add a string. If `node` is provided and has a standard `loc` property (with `start` and `end` properties each with a `line` and `column`), a sourcemap mapping will be created
108+
- `context.indent()` — increase the indentation level, typically before adding a newline
109+
- `context.newline()` — self-explanatory
110+
- `context.margin()` — causes the next newline to be repeated (consecutive newlines are otherwise merged into one)
111+
- `context.dedent()` — decrease the indentation level (again, typically before adding a newline)
112+
- `context.visit(node: BaseNode)` — calls the visitor corresponding to `node.type`
113+
- `context.location(line: number, column: number)` — insert a sourcemap mapping _without_ calling `context.write(...)`
114+
- `context.measure()` — returns the number of characters contained in `context`
115+
- `context.empty()` — returns true if the context has no content
116+
- `context.new()` — creates a child context
117+
- `context.append(child)` — appends a child context
118+
119+
In addition, `context.multiline` is `true` if the context has multiline content. (This is useful for knowing, for example, when to insert newlines between nodes.)
120+
121+
To understand how to wield these methods effectively, read the source code for the built-in languages.
122+
36123
## Options
37124

38125
You can pass the following options:
39126

40127
```js
41-
const { code, map } = print(ast, {
128+
const { code, map } = print(ast, ts(), {
42129
// Populate the `sources` field of the resulting sourcemap
43130
// (note that the AST is assumed to come from a single file)
44131
sourceMapSource: 'input.js',
@@ -51,28 +138,10 @@ const { code, map } = print(ast, {
51138
sourceMapEncodeMappings: false,
52139

53140
// String to use for indentation — defaults to '\t'
54-
indent: ' ',
55-
56-
// Whether to wrap strings in single or double quotes — defaults to 'single'.
57-
// This only applies to string literals with no `raw` value, which generally
58-
// means the AST node was generated programmatically, rather than parsed
59-
// from an original source
60-
quotes: 'single',
61-
62-
// Overrwrite the inbuilt printers
63-
handlers: {
64-
...ts, // the default, imported from 'esrap/languages/ts'
65-
CustomNode(node, state) {
66-
state.commands.push('this is custom') // see the source code in the inbuilt languages for examples on how to make your own
67-
}
68-
}
141+
indent: ' '
69142
});
70143
```
71144

72-
## TypeScript
73-
74-
`esrap` can also print TypeScript nodes, assuming they match the ESTree-like [`@typescript-eslint/types`](https://www.npmjs.com/package/@typescript-eslint/types).
75-
76145
## Why not just use Prettier?
77146

78147
Because it's ginormous.

0 commit comments

Comments
 (0)