Skip to content

Commit 7e9a6a2

Browse files
committed
Use @babel/parser to parse example code in loaders
1 parent 754cdbb commit 7e9a6a2

File tree

12 files changed

+56
-63
lines changed

12 files changed

+56
-63
lines changed

package-lock.json

Lines changed: 5 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,12 @@
2727
"node": ">=10"
2828
},
2929
"dependencies": {
30+
"@babel/parser": "^7.13.11",
3031
"@mdx-js/mdx": "^1.6.22",
3132
"@mdx-js/react": "^1.6.22",
3233
"@tippyjs/react": "4.1.0",
3334
"@vxna/mini-html-webpack-template": "^2.0.0",
3435
"acorn": "^8.0.5",
35-
"acorn-jsx": "^5.3.1",
36-
"acorn-loose": "^8.0.2",
3736
"assert": "1.5.0",
3837
"ast-types": "~0.14.2",
3938
"clean-webpack-plugin": "^3.0.0",

src/loaders/rehype/__tests__/exportStories.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ export const basic = () => <Container>{nums.map(x => x)}</Container>
527527
"
528528
529529
export const __namedExamples = {
530-
'basic': 'const nums = [\\\\'eins\\\\', \\\\'zwei\\\\', \\\\'polizei\\\\'];\\\\n\\\\n<Container>{nums.map(x => x)}</Container>'
530+
'basic': 'const nums = [\\\\'eins\\\\', \\\\'zwei\\\\', \\\\'polizei\\\\'] as const;\\\\n\\\\n<Container>{nums.map(x => x)}</Container>'
531531
};
532532
export const __storiesScope = {};
533533

src/loaders/rehype/exportStories.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
ImportDefaultSpecifier,
1414
ImportNamespaceSpecifier,
1515
} from 'estree';
16-
import acornJsx from 'acorn-jsx';
1716
import { Parent, Node as MdxNode } from 'unist';
1817
import { walk } from 'estree-walker';
1918
import { generate } from 'escodegen';
@@ -296,7 +295,7 @@ export default ({
296295
}
297296

298297
const storiesCode = fs.readFileSync(storiesFile, 'utf8');
299-
const storiesAst = getAst(storiesCode, [acornJsx()]);
298+
const storiesAst = getAst(storiesCode);
300299
if (!storiesAst) {
301300
return tree;
302301
}
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1-
import acornJsx from 'acorn-jsx';
21
import getAst from '../getAst';
32

43
describe('getAst', () => {
54
test('return AST', () => {
65
const result = getAst(`42`);
7-
expect(result).toHaveProperty('type', 'Program');
6+
expect(result).toHaveProperty('type', 'File');
87
});
98

10-
test('accept Acorn plugins', () => {
11-
const result = getAst(`<X />`, [acornJsx()]);
12-
expect(result).toHaveProperty('type', 'Program');
9+
test('understands JSX', () => {
10+
const result = getAst(`<X />`);
11+
expect(result).toHaveProperty('type', 'File');
12+
});
13+
14+
test('understands multiple JSX elements without a root element', () => {
15+
const result = getAst(`<X /><Y />`);
16+
expect(result).toHaveProperty('type', 'File');
1317
});
1418

1519
test('understands new ECMAScript syntax', () => {
1620
const result = getAst(`foo?.bar?.baz()`);
17-
expect(result).toHaveProperty('type', 'Program');
21+
expect(result).toHaveProperty('type', 'File');
1822
});
1923

2024
test('understands TypeScript', () => {
2125
const result = getAst(`const pizza = [1, 2, 3] as const`);
22-
expect(result).toHaveProperty('type', 'Program');
26+
expect(result).toHaveProperty('type', 'File');
2327
});
2428
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import getImportsMap from '../getImportsMap';
2+
3+
test('find import statements in code', () => {
4+
expect(getImportsMap(`import A from 'pizza'; import { A as X, B } from 'lunch';`)).toEqual({
5+
A: 'pizza',
6+
B: 'lunch',
7+
X: 'lunch',
8+
});
9+
});

src/loaders/utils/getAst.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
1-
import { Parser, Options } from 'acorn';
2-
import { parse as looseParse } from 'acorn-loose';
1+
import { parse, ParserOptions } from '@babel/parser';
32
import { Program } from 'estree';
43
import Logger from 'glogg';
54

65
const logger = Logger('rsg');
76

8-
export const ACORN_OPTIONS: Options = {
9-
ecmaVersion: 'latest',
7+
const startsWithJsx = (code: string): boolean => !!code.trim().match(/^</);
8+
9+
const wrapCodeInFragment = (code: string): string => `<React.Fragment>${code}</React.Fragment>;`;
10+
11+
export const BABEL_OPTIONS: ParserOptions = {
12+
allowReturnOutsideFunction: true,
13+
allowAwaitOutsideFunction: true,
14+
errorRecovery: true,
1015
sourceType: 'module',
16+
plugins: [
17+
'jsx',
18+
// TODO: How to support flow? We can't use `flow` and `typescript` plugins together
19+
'typescript',
20+
// Generate AST compatible with ESTree
21+
// TODO: We may want to rewrite the code to use Babel AST instead
22+
'estree',
23+
],
1124
};
1225

1326
/**
14-
* 1. Try to parse source code using Acorn, return AST.
15-
* 2. If failed: try to parse source using with Acorn Loose, return AST.
16-
* 3. If failed: return undefined.
27+
* Try to parse source code using Babel parser, return AST.
28+
* If failed, return undefined.
1729
*/
18-
export default function getAst(
19-
code: string,
20-
plugins: ((BaseParser: typeof Parser) => typeof Parser)[] = []
21-
): Program | undefined {
22-
const parser = Parser.extend(...plugins);
23-
30+
export default function getAst(code: string): Program | undefined {
31+
const wrappedCode = startsWithJsx(code) ? wrapCodeInFragment(code) : code;
2432
try {
25-
return (parser.parse(code, ACORN_OPTIONS) as unknown) as Program;
33+
return (parse(wrappedCode, BABEL_OPTIONS) as unknown) as Program; // TODO: @babel/types.File
2634
} catch (err) {
27-
try {
28-
return (looseParse(code, ACORN_OPTIONS) as unknown) as Program;
29-
} catch (innerErr) {
30-
logger.debug(`Acorn cannot parse code: ${innerErr.message}\n\nCode:\n${code}`);
31-
return undefined;
32-
}
35+
// TODO: Remove console.log
36+
console.log(`Babel cannot parse code: ${err.message}\n\nCode:\n${code}`);
37+
logger.debug(`Babel cannot parse code: ${err.message}\n\nCode:\n${code}`);
38+
return undefined;
3339
}
3440
}

src/loaders/utils/getImports.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import acornJsx from 'acorn-jsx';
21
import { walk } from 'estree-walker';
32
import { Node } from 'estree';
43
import getAst from './getAst';
@@ -12,7 +11,7 @@ export default function getImports(code: string): string[] {
1211
// imports/requires are not allowed in this case, and we'll wrap the code
1312
// in React.Fragment on the frontend
1413
// 2. All other errors - we'll deal with them on the frontend
15-
const ast = getAst(code, [acornJsx()]);
14+
const ast = getAst(code);
1615
if (!ast) {
1716
return [];
1817
}

src/loaders/utils/getImportsMap.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import acornJsx from 'acorn-jsx';
21
import { walk } from 'estree-walker';
32
import { Node } from 'estree';
43
import getAst from './getAst';
@@ -9,7 +8,7 @@ import getAst from './getAst';
98
export default function getImportsMap(code: string): Record<string, string> {
109
// Parse example source code, but ignore errors:
1110
// we'll deal with them on the frontend
12-
const ast = getAst(code, [acornJsx()]);
11+
const ast = getAst(code);
1312
if (!ast) {
1413
return {};
1514
}

src/typings/dependencies/acorn-jsx.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)