diff --git a/.eslintrc.js b/.eslintrc.js index 2d229af2f02..7cc2aa88664 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,7 +16,7 @@ module.exports = { globals: { ASTNode: true, NodePath: true, - Recast: true, + $Exact: true, }, overrides: [ { @@ -28,7 +28,7 @@ module.exports = { }, }, { - files: 'src/**/__tests__/*-test.js', + files: '@(src|bin)/**/__tests__/*-test.js', env: { jest: true }, }, ], diff --git a/README.md b/README.md index 30bf287c867..4a5b93c82f4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ `react-docgen` is a CLI and toolbox to help extracting information from [React][] components, and generate documentation from it. -It uses [recast][] and [@babel/parser][] to parse the source into an AST and provides methods to process this AST to extract the desired information. The output / return value is a JSON blob / JavaScript object. +It uses [ast-types][] and [@babel/parser][] to parse the source into an AST and provides methods to process this AST to extract the desired information. The output / return value is a JSON blob / JavaScript object. It provides a default implementation for React components defined via `React.createClass`, [ES2015 class definitions][classes] or functions @@ -82,8 +82,8 @@ As with the CLI, this will look for the exported component created through `Reac | Parameter | Type | Description | | -------------- | ------ | --------------- | | source | string | The source text | -| resolver | function | A function of the form `(ast: ASTNode, recast: Object) => (NodePath|Array)`. Given an AST and a reference to recast, it returns an (array of) NodePath which represents the component definition. | -| handlers | Array\ | An array of functions of the form `(documentation: Documentation, definition: NodePath) => void`. Each function is called with a `Documentation` object and a reference to the component definition as returned by `resolver`. Handlers extract relevant information from the definition and augment `documentation`. | +| resolver | function | A function of the form `(ast: ASTNode, parser: Parser) => (NodePath|Array)`. Given an AST and a reference to the parser, it returns an (array of) NodePath which represents the component definition. | +| handlers | Array\ | An array of functions of the form `(documentation: Documentation, definition: NodePath, parser: Parser) => void`. Each function is called with a `Documentation` object and a reference to the component definition as returned by `resolver`. Handlers extract relevant information from the definition and augment `documentation`. | | opt #### options @@ -412,6 +412,6 @@ The structure of the JSON blob / JavaScript object is as follows: [react]: http://facebook.github.io/react/ [flow]: http://flowtype.org/ [typescript]: http://typescriptlang.org/ -[recast]: https://github.com/benjamn/recast +[ast-types]: https://github.com/benjamn/ast-types [@babel/parser]: https://github.com/babel/babel/tree/master/packages/babel-parser [classes]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes diff --git a/benchmark/fixtures/CircularProgress.js b/benchmark/fixtures/CircularProgress.js new file mode 100644 index 00000000000..e2c42bdab43 --- /dev/null +++ b/benchmark/fixtures/CircularProgress.js @@ -0,0 +1,249 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { chainPropTypes } from '@material-ui/utils'; +import withStyles from '../styles/withStyles'; +import { capitalize } from '../utils/helpers'; + +const SIZE = 44; + +function getRelativeValue(value, min, max) { + const clampedValue = Math.min(Math.max(min, value), max); + return (clampedValue - min) / (max - min); +} + +function easeOut(t) { + t = getRelativeValue(t, 0, 1); + // https://gist.github.com/gre/1650294 + t = (t -= 1) * t * t + 1; + return t; +} + +function easeIn(t) { + return t * t; +} + +export const styles = theme => ({ + /* Styles applied to the root element. */ + root: { + display: 'inline-block', + lineHeight: 1, // Keep the progress centered + }, + /* Styles applied to the root element if `variant="static"`. */ + static: { + transition: theme.transitions.create('transform'), + }, + /* Styles applied to the root element if `variant="indeterminate"`. */ + indeterminate: { + animation: 'mui-progress-circular-rotate 1.4s linear infinite', + // Backward compatible logic between JSS v9 and v10. + // To remove with the release of Material-UI v4 + animationName: '$mui-progress-circular-rotate', + }, + /* Styles applied to the root element if `color="primary"`. */ + colorPrimary: { + color: theme.palette.primary.main, + }, + /* Styles applied to the root element if `color="secondary"`. */ + colorSecondary: { + color: theme.palette.secondary.main, + }, + /* Styles applied to the `svg` element. */ + svg: {}, + /* Styles applied to the `circle` svg path. */ + circle: { + stroke: 'currentColor', + // Use butt to follow the specification, by chance, it's already the default CSS value. + // strokeLinecap: 'butt', + }, + /* Styles applied to the `circle` svg path if `variant="static"`. */ + circleStatic: { + transition: theme.transitions.create('stroke-dashoffset'), + }, + /* Styles applied to the `circle` svg path if `variant="indeterminate"`. */ + circleIndeterminate: { + animation: 'mui-progress-circular-dash 1.4s ease-in-out infinite', + // Backward compatible logic between JSS v9 and v10. + // To remove with the release of Material-UI v4 + animationName: '$mui-progress-circular-dash', + // Some default value that looks fine waiting for the animation to kicks in. + strokeDasharray: '80px, 200px', + strokeDashoffset: '0px', // Add the unit to fix a Edge 16 and below bug. + }, + '@keyframes mui-progress-circular-rotate': { + '100%': { + transform: 'rotate(360deg)', + }, + }, + '@keyframes mui-progress-circular-dash': { + '0%': { + strokeDasharray: '1px, 200px', + strokeDashoffset: '0px', + }, + '50%': { + strokeDasharray: '100px, 200px', + strokeDashoffset: '-15px', + }, + '100%': { + strokeDasharray: '100px, 200px', + strokeDashoffset: '-125px', + }, + }, + /* Styles applied to the `circle` svg path if `disableShrink={true}`. */ + circleDisableShrink: { + animation: 'none', + }, +}); + +/** + * ## ARIA + * + * If the progress bar is describing the loading progress of a particular region of a page, + * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` + * attribute to `true` on that region until it has finished loading. + */ +const CircularProgress = React.forwardRef(function CircularProgress( + props, + ref, +) { + const { + classes, + className, + color, + disableShrink, + size, + style, + thickness, + value, + variant, + ...other + } = props; + + const circleStyle = {}; + const rootStyle = {}; + const rootProps = {}; + + if (variant === 'determinate' || variant === 'static') { + const circumference = 2 * Math.PI * ((SIZE - thickness) / 2); + circleStyle.strokeDasharray = circumference.toFixed(3); + rootProps['aria-valuenow'] = Math.round(value); + + if (variant === 'static') { + circleStyle.strokeDashoffset = `${( + ((100 - value) / 100) * + circumference + ).toFixed(3)}px`; + rootStyle.transform = 'rotate(-90deg)'; + } else { + circleStyle.strokeDashoffset = `${( + easeIn((100 - value) / 100) * circumference + ).toFixed(3)}px`; + rootStyle.transform = `rotate(${(easeOut(value / 70) * 270).toFixed( + 3, + )}deg)`; + } + } + + return ( +
+ + + +
+ ); +}); + +CircularProgress.propTypes = { + /** + * Override or extend the styles applied to the component. + * See [CSS API](#css) below for more details. + */ + classes: PropTypes.object.isRequired, + /** + * @ignore + */ + className: PropTypes.string, + /** + * The color of the component. It supports those theme colors that make sense for this component. + */ + color: PropTypes.oneOf(['primary', 'secondary', 'inherit']), + /** + * If `true`, the shrink animation is disabled. + * This only works if variant is `indeterminate`. + */ + disableShrink: chainPropTypes(PropTypes.bool, props => { + if (props.disableShrink && props.variant !== 'indeterminate') { + return new Error( + 'Material-UI: you have provided the `disableShrink` property ' + + 'with a variant other than `indeterminate`. This will have no effect.', + ); + } + + return null; + }), + /** + * The size of the circle. + */ + size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * @ignore + */ + style: PropTypes.object, + /** + * The thickness of the circle. + */ + thickness: PropTypes.number, + /** + * The value of the progress indicator for the determinate and static variants. + * Value between 0 and 100. + */ + value: PropTypes.number, + /** + * The variant to use. + * Use indeterminate when there is no progress value. + */ + variant: PropTypes.oneOf(['determinate', 'indeterminate', 'static']), +}; + +CircularProgress.defaultProps = { + color: 'primary', + disableShrink: false, + size: 40, + thickness: 3.6, + value: 0, + variant: 'indeterminate', +}; + +export default withStyles(styles, { name: 'MuiCircularProgress', flip: false })( + CircularProgress, +); diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100644 index 00000000000..ea0f53d4160 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,61 @@ +/* eslint-disable */ +const fs = require('fs'); +const path = require('path'); +const Table = require('cli-table'); +const Benchmark = require('benchmark'); +const { parse } = require('..'); + +console.log(`Node: ${process.version}`); + +const head = ['fixture', 'timing']; + +const files = ['./fixtures/CircularProgress.js']; + +const table = new Table({ + head, + style: { + head: ['bold'], + }, +}); + +if (!global.gc) { + console.error( + 'Garbage collection unavailable. Pass --expose-gc ' + + 'when launching node to enable forced garbage collection.', + ); + process.exit(); +} + +files.forEach(file => { + const code = fs.readFileSync(path.join(__dirname, file), 'utf-8'); + const suite = new Benchmark.Suite(file.replace(/\.\/fixtures\//, '')); + const options = { filename: file, babelrc: false, configFile: false }; + + // warmup + parse(code, null, null, options); + global.gc(); + suite.add(0, () => { + parse(code, null, null, options); + }); + const result = [suite.name]; + suite.on('cycle', function(event) { + { + // separate scope so we can cleanup all this afterwards + const bench = event.target; + const factor = bench.hz < 100 ? 100 : 1; + const msg = `${Math.round(bench.hz * factor) / + factor} ops/sec ±${Math.round(bench.stats.rme * 100) / + 100}% (${Math.round(bench.stats.mean * 1000)}ms)`; + result.push(msg); + } + global.gc(); + }); + + console.log(`Running benchmark for ${suite.name} ...`); + global.gc(); + suite.run({ async: false }); + global.gc(); // gc is disabled so ensure we run it + table.push(result); +}); +global.gc(); // gc is disabled so ensure we run it +console.log(table.toString()); diff --git a/bin/__tests__/example/customResolver.js b/bin/__tests__/example/customResolver.js index f60feec4dfa..54362d6de40 100644 --- a/bin/__tests__/example/customResolver.js +++ b/bin/__tests__/example/customResolver.js @@ -16,8 +16,10 @@ const code = ` }) `; -module.exports = function(ast, recast) { - return new recast.types.NodePath(recast.parse(code)).get( +const { NodePath } = require('ast-types'); + +module.exports = function(ast, parser) { + return new NodePath(parser.parse(code)).get( 'program', 'body', 0, diff --git a/bin/__tests__/react-docgen-test.js b/bin/__tests__/react-docgen-test.js index e50c44ff46f..e7fc35fd432 100644 --- a/bin/__tests__/react-docgen-test.js +++ b/bin/__tests__/react-docgen-test.js @@ -6,15 +6,11 @@ * */ -/*global jasmine, describe, it, expect, afterEach*/ - // NOTE: This test spawns a subprocesses that load the files from dist/, not // src/. Before running this test run `npm run build` or `npm run watch`. const TEST_TIMEOUT = 120000; -jasmine.DEFAULT_TIMEOUT_INTERVAL = TEST_TIMEOUT; - const fs = require('fs'); const path = require('path'); const rimraf = require('rimraf'); @@ -90,7 +86,7 @@ describe('react-docgen CLI', () => { tempDir = null; tempComponents = []; tempNoComponents = []; - }); + }, TEST_TIMEOUT); it( 'reads from stdin', diff --git a/flow-typed/recast.js b/flow-typed/recast.js index 294875fb4ec..1b60e55c191 100644 --- a/flow-typed/recast.js +++ b/flow-typed/recast.js @@ -9,7 +9,7 @@ /*eslint no-unused-vars: 0*/ /** - * A minimal set of declarations to make flow work with the recast API. + * A minimal set of declarations to make flow work with the ast-types API. */ type ASTNode = Object; @@ -35,8 +35,3 @@ declare class NodePath { filter(f: (p: NodePath) => boolean): Array; push(node: ASTNode): void; } - -type Recast = { - parse: (src: string) => ASTNode, - print: (path: NodePath) => { code: string }, -}; diff --git a/package.json b/package.json index 65b22b5c663..65dcca20da3 100644 --- a/package.json +++ b/package.json @@ -39,11 +39,12 @@ "dependencies": { "@babel/core": "^7.4.4", "@babel/runtime": "^7.0.0", + "ast-types": "^0.12.4", "async": "^2.1.4", "commander": "^2.19.0", "doctrine": "^3.0.0", "node-dir": "^0.1.10", - "recast": "^0.17.6" + "strip-indent": "^2.0.0" }, "devDependencies": { "@babel/cli": "^7.0.0", @@ -53,6 +54,8 @@ "@babel/preset-flow": "^7.0.0", "babel-eslint": "^10.0.1", "babel-jest": "^24.1.0", + "benchmark": "^2.1.4", + "cli-table": "^0.3.1", "cross-spawn": "^6.0.4", "eslint": "^5.7.0", "eslint-config-prettier": "^4.0.0", @@ -66,6 +69,9 @@ "temp": "^0.9.0" }, "jest": { + "snapshotSerializers": [ + "./tests/NodePathSerializer.js" + ], "setupFilesAfterEnv": [ "/tests/setupTestFramework.js" ], diff --git a/src/__tests__/main-test.js b/src/__tests__/main-test.js index 77b34754549..aa94fc4c3da 100644 --- a/src/__tests__/main-test.js +++ b/src/__tests__/main-test.js @@ -6,18 +6,15 @@ * */ -/*global describe, it, expect*/ - import fs from 'fs'; import path from 'path'; - -import * as docgen from '../main'; +import { parse, handlers } from '../main'; import { ERROR_MISSING_DEFINITION } from '../parse'; describe('main', () => { function test(source) { it('parses with default resolver/handlers', () => { - const docs = docgen.parse(source); + const docs = parse(source); expect(docs).toEqual({ displayName: 'ABC', description: 'Example component description', @@ -39,9 +36,7 @@ describe('main', () => { }); it('parses with custom handlers', () => { - const docs = docgen.parse(source, null, [ - docgen.handlers.componentDocblockHandler, - ]); + const docs = parse(source, null, [handlers.componentDocblockHandler]); expect(docs).toEqual({ description: 'Example component description', }); @@ -217,7 +212,7 @@ describe('main', () => { export default NotAComponent; `; - expect(() => docgen.parse(source)).toThrowError(ERROR_MISSING_DEFINITION); + expect(() => parse(source)).toThrowError(ERROR_MISSING_DEFINITION); }); }); @@ -226,12 +221,12 @@ describe('main', () => { const fileNames = fs.readdirSync(fixturePath); for (let i = 0; i < fileNames.length; i++) { const filePath = path.join(fixturePath, fileNames[i]); - const fileContent = fs.readFileSync(filePath); + const fileContent = fs.readFileSync(filePath, 'utf8'); it(`processes component "${fileNames[i]}" without errors`, () => { let result; expect(() => { - result = docgen.parse(fileContent, null, null, { + result = parse(fileContent, null, null, { filename: filePath, babelrc: false, }); diff --git a/src/__tests__/parse-test.js b/src/__tests__/parse-test.js index 6fd44c39922..20ccc41527c 100644 --- a/src/__tests__/parse-test.js +++ b/src/__tests__/parse-test.js @@ -6,29 +6,14 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -const fs = require('fs'); -const temp = require('temp'); - -jest.disableAutomock(); +import fs from 'fs'; +import temp from 'temp'; +import { expression } from '../../tests/utils'; +import parse, { ERROR_MISSING_DEFINITION } from '../parse'; describe('parse', () => { - let utils; - let parse, ERROR_MISSING_DEFINITION; - - beforeEach(() => { - utils = require('../../tests/utils'); - // ugly but necessary because ../parse has default and named exports - ({ default: parse, ERROR_MISSING_DEFINITION } = require('../parse')); - }); - - function pathFromSource(source) { - return utils.parse(source).get('body', 0, 'expression'); - } - it('allows custom component definition resolvers', () => { - const path = pathFromSource('({foo: "bar"})'); + const path = expression('{foo: "bar"}'); const resolver = jest.fn(() => path); const handler = jest.fn(); parse('//empty', resolver, [handler]); diff --git a/src/babelParser.js b/src/babelParser.js index 7ef995e445a..a8e386c6150 100644 --- a/src/babelParser.js +++ b/src/babelParser.js @@ -45,6 +45,10 @@ function getDefaultPlugins(options: BabelOptions) { ]; } +export type Parser = { + parse: (src: string) => ASTNode, +}; + type ParserOptions = { plugins?: Array, tokens?: boolean, @@ -86,9 +90,6 @@ function buildOptions( parserOpts.plugins = getDefaultPlugins(babelOptions); } - // Recast needs tokens to be in the tree - // $FlowIssue tokens is clearly in the Options - parserOpts.tokens = true; // Ensure we always have estree plugin enabled, if we add it a second time // here it does not matter parserOpts.plugins.push('estree'); @@ -96,12 +97,12 @@ function buildOptions( return parserOpts; } -export default function buildParse(options?: Options = {}) { +export default function buildParse(options?: Options = {}): Parser { const { parserOptions, ...babelOptions } = options; const parserOpts = buildOptions(parserOptions, babelOptions); return { - parse(src: string) { + parse(src: string): ASTNode { return babel.parseSync(src, { parserOpts, ...babelOptions, diff --git a/src/handlers/__tests__/componentDocblockHandler-test.js b/src/handlers/__tests__/componentDocblockHandler-test.js index 9c5c9dc06df..e1fff5057c7 100644 --- a/src/handlers/__tests__/componentDocblockHandler-test.js +++ b/src/handlers/__tests__/componentDocblockHandler-test.js @@ -6,13 +6,11 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { parse } from '../../../tests/utils'; + describe('componentDocblockHandler', () => { - let parse; let documentation; let componentDocblockHandler; @@ -22,7 +20,6 @@ describe('componentDocblockHandler', () => { } beforeEach(() => { - ({ parse } = require('../../../tests/utils')); documentation = new (require('../../Documentation'))(); componentDocblockHandler = require('../componentDocblockHandler').default; }); diff --git a/src/handlers/__tests__/componentMethodsHandler-test.js b/src/handlers/__tests__/componentMethodsHandler-test.js index 4612d9af7dc..9ff40ff7a6c 100644 --- a/src/handlers/__tests__/componentMethodsHandler-test.js +++ b/src/handlers/__tests__/componentMethodsHandler-test.js @@ -6,18 +6,15 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { parse } from '../../../tests/utils'; + describe('componentMethodsHandler', () => { let documentation; let componentMethodsHandler; - let parse; beforeEach(() => { - ({ parse } = require('../../../tests/utils')); documentation = new (require('../../Documentation'))(); componentMethodsHandler = require('../componentMethodsHandler').default; }); diff --git a/src/handlers/__tests__/componentMethodsJsDocHandler-test.js b/src/handlers/__tests__/componentMethodsJsDocHandler-test.js index c45afc338c6..68abde32181 100644 --- a/src/handlers/__tests__/componentMethodsJsDocHandler-test.js +++ b/src/handlers/__tests__/componentMethodsJsDocHandler-test.js @@ -6,8 +6,6 @@ * */ -/*global describe, beforeEach, it, expect*/ - import Documentation from '../../Documentation'; import componentMethodsJsDocHandler from '../componentMethodsJsDocHandler'; diff --git a/src/handlers/__tests__/defaultPropsHandler-test.js b/src/handlers/__tests__/defaultPropsHandler-test.js index e84c78bc60b..088d5b911a4 100644 --- a/src/handlers/__tests__/defaultPropsHandler-test.js +++ b/src/handlers/__tests__/defaultPropsHandler-test.js @@ -8,13 +8,13 @@ jest.mock('../../Documentation'); +import { parse } from '../../../tests/utils'; + describe('defaultPropsHandler', () => { let documentation; let defaultPropsHandler; - let parse; beforeEach(() => { - ({ parse } = require('../../../tests/utils')); documentation = new (require('../../Documentation'))(); defaultPropsHandler = require('../defaultPropsHandler').default; }); diff --git a/src/handlers/__tests__/displayNameHandler-test.js b/src/handlers/__tests__/displayNameHandler-test.js index 9505095bf50..c967eb169a5 100644 --- a/src/handlers/__tests__/displayNameHandler-test.js +++ b/src/handlers/__tests__/displayNameHandler-test.js @@ -6,18 +6,15 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { expression, statement } from '../../../tests/utils'; + describe('defaultPropsHandler', () => { let documentation; let displayNameHandler; - let expression, statement; beforeEach(() => { - ({ expression, statement } = require('../../../tests/utils')); documentation = new (require('../../Documentation'))(); displayNameHandler = require('../displayNameHandler').default; }); diff --git a/src/handlers/__tests__/flowTypeHandler-test.js b/src/handlers/__tests__/flowTypeHandler-test.js index 8b12f47c175..c63f9b30cad 100644 --- a/src/handlers/__tests__/flowTypeHandler-test.js +++ b/src/handlers/__tests__/flowTypeHandler-test.js @@ -6,19 +6,16 @@ * */ -/*global jest, describe, it, expect, beforeEach*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { expression, statement } from '../../../tests/utils'; + describe('flowTypeHandler', () => { - let statement, expression; let getFlowTypeMock; let documentation; let flowTypeHandler; beforeEach(() => { - ({ statement, expression } = require('../../../tests/utils')); getFlowTypeMock = jest.fn(() => ({})); jest.setMock('../../utils/getFlowType', getFlowTypeMock); jest.mock('../../utils/getFlowType'); diff --git a/src/handlers/__tests__/propDocblockHandler-test.js b/src/handlers/__tests__/propDocblockHandler-test.js index 39392b672f6..47e48995313 100644 --- a/src/handlers/__tests__/propDocblockHandler-test.js +++ b/src/handlers/__tests__/propDocblockHandler-test.js @@ -6,21 +6,15 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -const os = require('os'); -const EOL = os.EOL; - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { expression, statement } from '../../../tests/utils'; + describe('propDocBlockHandler', () => { - let expression, statement; let documentation; let propDocBlockHandler; beforeEach(() => { - ({ expression, statement } = require('../../../tests/utils')); documentation = new (require('../../Documentation'))(); propDocBlockHandler = require('../propDocBlockHandler').default; }); @@ -72,11 +66,7 @@ describe('propDocBlockHandler', () => { expect(documentation.descriptors).toEqual({ foo: { description: - 'Foo comment with' + - EOL + - 'many lines!' + - EOL + - '\neven with empty lines in between', + 'Foo comment with\nmany lines!\n\neven with empty lines in between', }, }); }); diff --git a/src/handlers/__tests__/propTypeCompositionHandler-test.js b/src/handlers/__tests__/propTypeCompositionHandler-test.js index 02d688d5979..b77ea521d7c 100644 --- a/src/handlers/__tests__/propTypeCompositionHandler-test.js +++ b/src/handlers/__tests__/propTypeCompositionHandler-test.js @@ -6,19 +6,16 @@ * */ -/*global jest, describe, it, expect, beforeEach*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { expression, statement } from '../../../tests/utils'; + describe('propTypeCompositionHandler', () => { - let statement, expression; let getPropTypeMock; let documentation; let propTypeCompositionHandler; beforeEach(() => { - ({ statement, expression } = require('../../../tests/utils')); getPropTypeMock = jest.fn(() => ({})); jest.setMock('../../utils/getPropType', getPropTypeMock); jest.mock('../../utils/getPropType'); diff --git a/src/handlers/componentDocblockHandler.js b/src/handlers/componentDocblockHandler.js index e4a4b34e6af..dcacea4cd41 100644 --- a/src/handlers/componentDocblockHandler.js +++ b/src/handlers/componentDocblockHandler.js @@ -7,20 +7,15 @@ * @flow */ +import types from 'ast-types'; import type Documentation from '../Documentation'; - -import recast from 'recast'; import { getDocblock } from '../utils/docblock'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function isClassDefinition(nodePath) { const node = nodePath.node; - return ( - types.ClassDeclaration.check(node) || types.ClassExpression.check(node) - ); + return t.ClassDeclaration.check(node) || t.ClassExpression.check(node); } /** @@ -45,14 +40,14 @@ export default function componentDocblockHandler( if (description == null) { // Find parent statement (e.g. var Component = React.createClass();) let searchPath = path; - while (searchPath && !types.Statement.check(searchPath.node)) { + while (searchPath && !t.Statement.check(searchPath.node)) { searchPath = searchPath.parent; } if (searchPath) { // If the parent is an export statement, we have to traverse one more up if ( - types.ExportNamedDeclaration.check(searchPath.parentPath.node) || - types.ExportDefaultDeclaration.check(searchPath.parentPath.node) + t.ExportNamedDeclaration.check(searchPath.parentPath.node) || + t.ExportDefaultDeclaration.check(searchPath.parentPath.node) ) { searchPath = searchPath.parentPath; } diff --git a/src/handlers/componentMethodsHandler.js b/src/handlers/componentMethodsHandler.js index c7d1ad392a0..e515d2e4be2 100644 --- a/src/handlers/componentMethodsHandler.js +++ b/src/handlers/componentMethodsHandler.js @@ -7,18 +7,14 @@ * @flow */ -import recast from 'recast'; - +import types from 'ast-types'; import getMemberValuePath from '../utils/getMemberValuePath'; import getMethodDocumentation from '../utils/getMethodDocumentation'; import isReactComponentClass from '../utils/isReactComponentClass'; import isReactComponentMethod from '../utils/isReactComponentMethod'; - import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * The following values/constructs are considered methods: @@ -30,12 +26,9 @@ const { */ function isMethod(path) { const isProbablyMethod = - (types.MethodDefinition.check(path.node) && - path.node.kind !== 'constructor') || - (types.ClassProperty.check(path.node) && - types.Function.check(path.get('value').node)) || - (types.Property.check(path.node) && - types.Function.check(path.get('value').node)); + (t.MethodDefinition.check(path.node) && path.node.kind !== 'constructor') || + ((t.ClassProperty.check(path.node) || t.Property.check(path.node)) && + t.Function.check(path.get('value').node)); return isProbablyMethod && !isReactComponentMethod(path); } @@ -52,7 +45,7 @@ export default function componentMethodsHandler( let methodPaths = []; if (isReactComponentClass(path)) { methodPaths = path.get('body', 'body').filter(isMethod); - } else if (types.ObjectExpression.check(path.node)) { + } else if (t.ObjectExpression.check(path.node)) { methodPaths = path.get('properties').filter(isMethod); // Add the statics object properties. diff --git a/src/handlers/defaultPropsHandler.js b/src/handlers/defaultPropsHandler.js index a517ccb71f4..a98bc92f357 100644 --- a/src/handlers/defaultPropsHandler.js +++ b/src/handlers/defaultPropsHandler.js @@ -7,33 +7,30 @@ * @flow */ -import type Documentation from '../Documentation'; - +import types from 'ast-types'; import getPropertyName from '../utils/getPropertyName'; import getMemberValuePath from '../utils/getMemberValuePath'; import printValue from '../utils/printValue'; -import recast from 'recast'; import resolveToValue from '../utils/resolveToValue'; import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue'; import isReactComponentClass from '../utils/isReactComponentClass'; import isReactForwardRefCall from '../utils/isReactForwardRefCall'; +import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function getDefaultValue(path: NodePath) { let node = path.node; let defaultValue; - if (types.Literal.check(node)) { + if (t.Literal.check(node)) { defaultValue = node.raw; } else { - if (types.AssignmentPattern.check(path.node)) { + if (t.AssignmentPattern.check(path.node)) { path = resolveToValue(path.get('right')); } else { path = resolveToValue(path); } - if (types.ImportDeclaration.check(path.node)) { + if (t.ImportDeclaration.check(path.node)) { defaultValue = node.name; } else { node = path.node; @@ -44,9 +41,9 @@ function getDefaultValue(path: NodePath) { return { value: defaultValue, computed: - types.CallExpression.check(node) || - types.MemberExpression.check(node) || - types.Identifier.check(node), + t.CallExpression.check(node) || + t.MemberExpression.check(node) || + t.Identifier.check(node), }; } @@ -76,13 +73,13 @@ function getDefaultPropsPath(componentDefinition: NodePath): ?NodePath { return null; } - if (types.FunctionExpression.check(defaultPropsPath.node)) { + if (t.FunctionExpression.check(defaultPropsPath.node)) { // Find the value that is returned from the function and process it if it is // an object literal. const returnValue = resolveFunctionDefinitionToReturnValue( defaultPropsPath, ); - if (returnValue && types.ObjectExpression.check(returnValue.node)) { + if (returnValue && t.ObjectExpression.check(returnValue.node)) { defaultPropsPath = returnValue; } } @@ -95,12 +92,12 @@ function getDefaultValuesFromProps( isStateless: boolean, ) { properties - .filter(propertyPath => types.Property.check(propertyPath.node)) + .filter(propertyPath => t.Property.check(propertyPath.node)) // Don't evaluate property if component is functional and the node is not an AssignmentPattern .filter( propertyPath => !isStateless || - types.AssignmentPattern.check(propertyPath.get('value').node), + t.AssignmentPattern.check(propertyPath.get('value').node), ) .forEach(propertyPath => { const propName = getPropertyName(propertyPath); @@ -132,14 +129,14 @@ export default function defaultPropsHandler( } // Do both statelessProps and defaultProps if both are available so defaultProps can override - if (statelessProps && types.ObjectPattern.check(statelessProps.node)) { + if (statelessProps && t.ObjectPattern.check(statelessProps.node)) { getDefaultValuesFromProps( statelessProps.get('properties'), documentation, true, ); } - if (defaultPropsPath && types.ObjectExpression.check(defaultPropsPath.node)) { + if (defaultPropsPath && t.ObjectExpression.check(defaultPropsPath.node)) { getDefaultValuesFromProps( defaultPropsPath.get('properties'), documentation, diff --git a/src/handlers/displayNameHandler.js b/src/handlers/displayNameHandler.js index 867c133ee54..a13d23c8833 100644 --- a/src/handlers/displayNameHandler.js +++ b/src/handlers/displayNameHandler.js @@ -7,17 +7,14 @@ * @flow */ -import type Documentation from '../Documentation'; - +import types from 'ast-types'; import getMemberValuePath from '../utils/getMemberValuePath'; import getNameOrValue from '../utils/getNameOrValue'; -import recast from 'recast'; import resolveToValue from '../utils/resolveToValue'; import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue'; +import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; export default function displayNameHandler( documentation: Documentation, @@ -28,27 +25,27 @@ export default function displayNameHandler( // Function and class declarations need special treatment. The name of the // function / class is the displayName if ( - types.ClassDeclaration.check(path.node) || - types.FunctionDeclaration.check(path.node) + t.ClassDeclaration.check(path.node) || + t.FunctionDeclaration.check(path.node) ) { documentation.set('displayName', getNameOrValue(path.get('id'))); } else if ( - types.ArrowFunctionExpression.check(path.node) || - types.FunctionExpression.check(path.node) + t.ArrowFunctionExpression.check(path.node) || + t.FunctionExpression.check(path.node) ) { let currentPath = path; while (currentPath.parent) { - if (types.VariableDeclarator.check(currentPath.parent.node)) { + if (t.VariableDeclarator.check(currentPath.parent.node)) { documentation.set( 'displayName', getNameOrValue(currentPath.parent.get('id')), ); return; - } else if (types.AssignmentExpression.check(currentPath.parent.node)) { + } else if (t.AssignmentExpression.check(currentPath.parent.node)) { const leftPath = currentPath.parent.get('left'); if ( - types.Identifier.check(leftPath.node) || - types.Literal.check(leftPath.node) + t.Identifier.check(leftPath.node) || + t.Literal.check(leftPath.node) ) { documentation.set('displayName', getNameOrValue(leftPath)); return; @@ -64,10 +61,10 @@ export default function displayNameHandler( // If display name is defined as a getter we get a function expression as // value. In that case we try to determine the value from the return // statement. - if (types.FunctionExpression.check(displayNamePath.node)) { + if (t.FunctionExpression.check(displayNamePath.node)) { displayNamePath = resolveFunctionDefinitionToReturnValue(displayNamePath); } - if (!displayNamePath || !types.Literal.check(displayNamePath.node)) { + if (!displayNamePath || !t.Literal.check(displayNamePath.node)) { return; } documentation.set('displayName', displayNamePath.node.value); diff --git a/src/handlers/flowTypeHandler.js b/src/handlers/flowTypeHandler.js index 2512dd805ae..34a9ccf4aca 100644 --- a/src/handlers/flowTypeHandler.js +++ b/src/handlers/flowTypeHandler.js @@ -6,10 +6,8 @@ * * @flow */ -import recast from 'recast'; - -import type Documentation from '../Documentation'; +import types from 'ast-types'; import getFlowType from '../utils/getFlowType'; import getTSType from '../utils/getTSType'; import getPropertyName from '../utils/getPropertyName'; @@ -20,19 +18,19 @@ import resolveToValue from '../utils/resolveToValue'; import setPropDescription from '../utils/setPropDescription'; import { unwrapUtilityType } from '../utils/flowUtilityTypes'; import { type TypeParameters } from '../utils/getTypeParameters'; +import type Documentation from '../Documentation'; + +const { namedTypes: t } = types; -const { - types: { namedTypes: types }, -} = recast; function setPropDescriptor( documentation: Documentation, path: NodePath, typeParams: ?TypeParameters, ): void { - if (types.ObjectTypeSpreadProperty.check(path.node)) { + if (t.ObjectTypeSpreadProperty.check(path.node)) { const argument = unwrapUtilityType(path.get('argument')); - if (types.ObjectTypeAnnotation.check(argument.node)) { + if (t.ObjectTypeAnnotation.check(argument.node)) { applyToFlowTypeProperties( documentation, argument, @@ -47,7 +45,7 @@ function setPropDescriptor( const name = argument.get('id').get('name'); const resolvedPath = resolveToValue(name); - if (resolvedPath && types.TypeAlias.check(resolvedPath.node)) { + if (resolvedPath && t.TypeAlias.check(resolvedPath.node)) { const right = resolvedPath.get('right'); applyToFlowTypeProperties( documentation, @@ -60,7 +58,7 @@ function setPropDescriptor( } else { documentation.addComposes(name.node.name); } - } else if (types.ObjectTypeProperty.check(path.node)) { + } else if (t.ObjectTypeProperty.check(path.node)) { const type = getFlowType(path.get('value'), typeParams); const propName = getPropertyName(path); if (!propName) return; @@ -73,7 +71,7 @@ function setPropDescriptor( // to not need to duplicate the logic for checking for // imported types that are spread in to props. setPropDescription(documentation, path); - } else if (types.TSPropertySignature.check(path.node)) { + } else if (t.TSPropertySignature.check(path.node)) { const type = getTSType(path.get('typeAnnotation'), typeParams); const propName = getPropertyName(path); diff --git a/src/handlers/propDocBlockHandler.js b/src/handlers/propDocBlockHandler.js index 289e9ed5112..a10dd20d9b2 100644 --- a/src/handlers/propDocBlockHandler.js +++ b/src/handlers/propDocBlockHandler.js @@ -7,29 +7,26 @@ * @flow */ -import type Documentation from '../Documentation'; - +import types from 'ast-types'; import getMemberValuePath from '../utils/getMemberValuePath'; -import recast from 'recast'; import resolveToValue from '../utils/resolveToValue'; import setPropDescription from '../utils/setPropDescription'; +import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function resolveDocumentation( documentation: Documentation, path: NodePath, ): void { - if (!types.ObjectExpression.check(path.node)) { + if (!t.ObjectExpression.check(path.node)) { return; } path.get('properties').each(propertyPath => { - if (types.Property.check(propertyPath.node)) { + if (t.Property.check(propertyPath.node)) { setPropDescription(documentation, propertyPath); - } else if (types.SpreadElement.check(propertyPath.node)) { + } else if (t.SpreadElement.check(propertyPath.node)) { const resolvedValuePath = resolveToValue(propertyPath.get('argument')); resolveDocumentation(documentation, resolvedValuePath); } diff --git a/src/handlers/propTypeCompositionHandler.js b/src/handlers/propTypeCompositionHandler.js index 1649c388b9f..c6f13c10a90 100644 --- a/src/handlers/propTypeCompositionHandler.js +++ b/src/handlers/propTypeCompositionHandler.js @@ -7,16 +7,13 @@ * @flow */ -import type Documentation from '../Documentation'; - +import types from 'ast-types'; import getMemberValuePath from '../utils/getMemberValuePath'; -import recast from 'recast'; import resolveToModule from '../utils/resolveToModule'; import resolveToValue from '../utils/resolveToValue'; +import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * It resolves the path to its module name and adds it to the "composes" entry @@ -32,7 +29,7 @@ function amendComposes(documentation, path) { function processObjectExpression(documentation, path) { path.get('properties').each(function(propertyPath) { switch (propertyPath.node.type) { - case types.SpreadElement.name: + case t.SpreadElement.name: amendComposes( documentation, resolveToValue(propertyPath.get('argument')), @@ -56,7 +53,7 @@ export default function propTypeCompositionHandler( } switch (propTypesPath.node.type) { - case types.ObjectExpression.name: + case t.ObjectExpression.name: processObjectExpression(documentation, propTypesPath); break; default: diff --git a/src/handlers/propTypeHandler.js b/src/handlers/propTypeHandler.js index 9356efff71b..61dfac8703e 100644 --- a/src/handlers/propTypeHandler.js +++ b/src/handlers/propTypeHandler.js @@ -7,21 +7,18 @@ * @flow */ -import type Documentation from '../Documentation'; - +import types from 'ast-types'; import getPropType from '../utils/getPropType'; import getPropertyName from '../utils/getPropertyName'; import getMemberValuePath from '../utils/getMemberValuePath'; import isReactModuleName from '../utils/isReactModuleName'; import isRequiredPropType from '../utils/isRequiredPropType'; import printValue from '../utils/printValue'; -import recast from 'recast'; import resolveToModule from '../utils/resolveToModule'; import resolveToValue from '../utils/resolveToValue'; +import type Documentation from '../Documentation'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function isPropTypesExpression(path) { const moduleName = resolveToModule(path); @@ -32,13 +29,13 @@ function isPropTypesExpression(path) { } function amendPropTypes(getDescriptor, path) { - if (!types.ObjectExpression.check(path.node)) { + if (!t.ObjectExpression.check(path.node)) { return; } path.get('properties').each(propertyPath => { switch (propertyPath.node.type) { - case types.Property.name: { + case t.Property.name: { const propName = getPropertyName(propertyPath); if (!propName) return; @@ -55,10 +52,10 @@ function amendPropTypes(getDescriptor, path) { } break; } - case types.SpreadElement.name: { + case t.SpreadElement.name: { const resolvedValuePath = resolveToValue(propertyPath.get('argument')); switch (resolvedValuePath.node.type) { - case types.ObjectExpression.name: // normal object literal + case t.ObjectExpression.name: // normal object literal amendPropTypes(getDescriptor, resolvedValuePath); break; } diff --git a/src/main.js b/src/main.js index d2786d1273b..8db42557353 100644 --- a/src/main.js +++ b/src/main.js @@ -43,7 +43,7 @@ const defaultHandlers = [ * documentation (from a docblock). */ function defaultParse( - src: string, + src: string | Buffer, resolver?: ?Resolver, handlers?: ?Array, options?: Options = {}, @@ -55,7 +55,7 @@ function defaultParse( handlers = defaultHandlers; } - return parse(src, resolver, handlers, options); + return parse(String(src), resolver, handlers, options); } export { diff --git a/src/parse.js b/src/parse.js index 0e964e26289..d4a87fec5fc 100644 --- a/src/parse.js +++ b/src/parse.js @@ -9,9 +9,7 @@ import Documentation, { type DocumentationObject } from './Documentation'; import postProcessDocumentation from './utils/postProcessDocumentation'; - -import buildParser, { type Options } from './babelParser'; -import recast from 'recast'; +import buildParser, { type Options, type Parser } from './babelParser'; import type { Handler, Resolver } from './types'; const ERROR_MISSING_DEFINITION = 'No suitable component definition found.'; @@ -19,11 +17,14 @@ const ERROR_MISSING_DEFINITION = 'No suitable component definition found.'; function executeHandlers( handlers: Array, componentDefinitions: Array, + parser: Parser, ): Array { return componentDefinitions.map( - (componentDefinition): DocumentationObject => { + (componentDefinition: NodePath): DocumentationObject => { const documentation = new Documentation(); - handlers.forEach(handler => handler(documentation, componentDefinition)); + handlers.forEach(handler => + handler(documentation, componentDefinition, parser), + ); return postProcessDocumentation(documentation.toObject()); }, ); @@ -36,14 +37,15 @@ function executeHandlers( * `resolver` is a strategy to find the AST node(s) of the component * definition(s) inside `src`. * It is a function that gets passed the program AST node of - * the source as first argument, and a reference to recast as second argument. + * the source as first argument, and a reference to the parser as second argument. * * This allows you define your own strategy for finding component definitions. * * `handlers` is an array of functions which are passed a reference to the * component definitions (extracted by `resolver`) so that they can extract * information from it. They get also passed a reference to a `Documentation` - * object to attach the information to. + * object to attach the information to. A reference to the parser is parsed as the + * last argument. * * If `resolver` returns an array of component definitions, `parse` will return * an array of documentation objects. If `resolver` returns a single node @@ -55,16 +57,18 @@ export default function parse( handlers: Array, options: Options, ): Array | DocumentationObject { - const ast = recast.parse(src, { parser: buildParser(options) }); - const componentDefinitions = resolver(ast.program, recast); + const parser = buildParser(options); + const ast = parser.parse(src); + ast.__src = src; + const componentDefinitions = resolver(ast, parser); if (Array.isArray(componentDefinitions)) { if (componentDefinitions.length === 0) { throw new Error(ERROR_MISSING_DEFINITION); } - return executeHandlers(handlers, componentDefinitions); + return executeHandlers(handlers, componentDefinitions, parser); } else if (componentDefinitions) { - return executeHandlers(handlers, [componentDefinitions])[0]; + return executeHandlers(handlers, [componentDefinitions], parser)[0]; } throw new Error(ERROR_MISSING_DEFINITION); diff --git a/src/resolver/__tests__/findAllComponentDefinitions-test.js b/src/resolver/__tests__/findAllComponentDefinitions-test.js index 37fd267a150..c67a0e53b37 100644 --- a/src/resolver/__tests__/findAllComponentDefinitions-test.js +++ b/src/resolver/__tests__/findAllComponentDefinitions-test.js @@ -6,15 +6,15 @@ * */ -/*global describe, it, expect*/ - -import recast from 'recast'; +import types from 'ast-types'; import * as utils from '../../../tests/utils'; import findAllComponentDefinitions from '../findAllComponentDefinitions'; +const { NodePath } = types; + describe('findAllComponentDefinitions', () => { function parse(source) { - return findAllComponentDefinitions(utils.parse(source, recast), recast); + return findAllComponentDefinitions(utils.parse(source), utils.getParser()); } describe('React.createClass', () => { @@ -28,7 +28,7 @@ describe('findAllComponentDefinitions', () => { const result = parse(source); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(1); - expect(result[0] instanceof recast.types.NodePath).toBe(true); + expect(result[0] instanceof NodePath).toBe(true); expect(result[0].node.type).toBe('ObjectExpression'); }); diff --git a/src/resolver/__tests__/findAllExportedComponentDefinitions-test.js b/src/resolver/__tests__/findAllExportedComponentDefinitions-test.js index cb40555ea0a..d8985095000 100644 --- a/src/resolver/__tests__/findAllExportedComponentDefinitions-test.js +++ b/src/resolver/__tests__/findAllExportedComponentDefinitions-test.js @@ -6,8 +6,6 @@ * */ -/*global describe, it, expect*/ -import recast from 'recast'; import * as utils from '../../../tests/utils'; import findAllExportedComponentDefinitions from '../findAllExportedComponentDefinitions'; @@ -18,7 +16,7 @@ describe('findAllExportedComponentDefinitions', () => { } function findComponents(path) { - return findAllExportedComponentDefinitions(path, recast); + return findAllExportedComponentDefinitions(path, utils.getParser()); } describe('CommonJS module exports', () => { diff --git a/src/resolver/__tests__/findExportedComponentDefinition-test.js b/src/resolver/__tests__/findExportedComponentDefinition-test.js index 849ca43fe0a..4875deb649a 100644 --- a/src/resolver/__tests__/findExportedComponentDefinition-test.js +++ b/src/resolver/__tests__/findExportedComponentDefinition-test.js @@ -6,15 +6,15 @@ * */ -/*global describe, it, expect*/ - -import recast from 'recast'; import * as utils from '../../../tests/utils'; import findExportedComponentDefinition from '../findExportedComponentDefinition'; describe('findExportedComponentDefinition', () => { function parse(source) { - return findExportedComponentDefinition(utils.parse(source), recast); + return findExportedComponentDefinition( + utils.parse(source), + utils.getParser(), + ); } describe('CommonJS module exports', () => { diff --git a/src/resolver/findAllComponentDefinitions.js b/src/resolver/findAllComponentDefinitions.js index 47121531dcc..13dbf633796 100644 --- a/src/resolver/findAllComponentDefinitions.js +++ b/src/resolver/findAllComponentDefinitions.js @@ -7,6 +7,7 @@ * @flow */ +import types from 'ast-types'; import isReactComponentClass from '../utils/isReactComponentClass'; import isReactCreateClassCall from '../utils/isReactCreateClassCall'; import isReactForwardRefCall from '../utils/isReactForwardRefCall'; @@ -14,15 +15,15 @@ import isStatelessComponent from '../utils/isStatelessComponent'; import normalizeClassDefinition from '../utils/normalizeClassDefinition'; import resolveToValue from '../utils/resolveToValue'; +const { visit, namedTypes: t } = types; + /** * Given an AST, this function tries to find all object expressions that are * passed to `React.createClass` calls, by resolving all references properly. */ export default function findAllReactCreateClassCalls( ast: ASTNode, - recast: Object, ): Array { - const types = recast.types.namedTypes; const definitions = new Set(); function classVisitor(path) { @@ -40,7 +41,7 @@ export default function findAllReactCreateClassCalls( return false; } - recast.visit(ast, { + visit(ast, { visitFunctionDeclaration: statelessVisitor, visitFunctionExpression: statelessVisitor, visitArrowFunctionExpression: statelessVisitor, @@ -55,7 +56,7 @@ export default function findAllReactCreateClassCalls( definitions.add(path); } else if (isReactCreateClassCall(path)) { const resolvedPath = resolveToValue(path.get('arguments', 0)); - if (types.ObjectExpression.check(resolvedPath.node)) { + if (t.ObjectExpression.check(resolvedPath.node)) { definitions.add(resolvedPath); } } diff --git a/src/resolver/findAllExportedComponentDefinitions.js b/src/resolver/findAllExportedComponentDefinitions.js index e032b567653..ecf4f95afbd 100644 --- a/src/resolver/findAllExportedComponentDefinitions.js +++ b/src/resolver/findAllExportedComponentDefinitions.js @@ -7,6 +7,7 @@ * @flow */ +import types from 'ast-types'; import isExportsOrModuleAssignment from '../utils/isExportsOrModuleAssignment'; import isReactComponentClass from '../utils/isReactComponentClass'; import isReactCreateClassCall from '../utils/isReactCreateClassCall'; @@ -17,11 +18,13 @@ import resolveExportDeclaration from '../utils/resolveExportDeclaration'; import resolveToValue from '../utils/resolveToValue'; import resolveHOC from '../utils/resolveHOC'; -function ignore() { +const { visit, namedTypes: t } = types; + +function ignore(): false { return false; } -function isComponentDefinition(path) { +function isComponentDefinition(path: NodePath): boolean { return ( isReactCreateClassCall(path) || isReactComponentClass(path) || @@ -30,11 +33,11 @@ function isComponentDefinition(path) { ); } -function resolveDefinition(definition, types): ?NodePath { +function resolveDefinition(definition: NodePath): ?NodePath { if (isReactCreateClassCall(definition)) { // return argument const resolvedPath = resolveToValue(definition.get('arguments', 0)); - if (types.ObjectExpression.check(resolvedPath.node)) { + if (t.ObjectExpression.check(resolvedPath.node)) { return resolvedPath; } } else if (isReactComponentClass(definition)) { @@ -66,13 +69,11 @@ function resolveDefinition(definition, types): ?NodePath { */ export default function findExportedComponentDefinitions( ast: ASTNode, - recast: Object, ): Array { - const types = recast.types.namedTypes; const components: Array = []; - function exportDeclaration(path) { - const definitions: Array = resolveExportDeclaration(path, types) + function exportDeclaration(path: NodePath): ?boolean { + const definitions: Array = resolveExportDeclaration(path) .reduce((acc, definition) => { if (isComponentDefinition(definition)) { acc.push(definition); @@ -84,7 +85,7 @@ export default function findExportedComponentDefinitions( } return acc; }, []) - .map(definition => resolveDefinition(definition, types)); + .map(definition => resolveDefinition(definition)); if (definitions.length === 0) { return false; @@ -97,7 +98,7 @@ export default function findExportedComponentDefinitions( return false; } - recast.visit(ast, { + visit(ast, { visitFunctionDeclaration: ignore, visitFunctionExpression: ignore, visitClassDeclaration: ignore, @@ -115,7 +116,7 @@ export default function findExportedComponentDefinitions( visitExportNamedDeclaration: exportDeclaration, visitExportDefaultDeclaration: exportDeclaration, - visitAssignmentExpression: function(path) { + visitAssignmentExpression: function(path: NodePath): ?boolean { // Ignore anything that is not `exports.X = ...;` or // `module.exports = ...;` if (!isExportsOrModuleAssignment(path)) { @@ -130,7 +131,7 @@ export default function findExportedComponentDefinitions( return false; } } - const definition = resolveDefinition(path, types); + const definition = resolveDefinition(path); if (definition && components.indexOf(definition) === -1) { components.push(definition); } diff --git a/src/resolver/findExportedComponentDefinition.js b/src/resolver/findExportedComponentDefinition.js index eaa48931c86..4a873c8c53c 100644 --- a/src/resolver/findExportedComponentDefinition.js +++ b/src/resolver/findExportedComponentDefinition.js @@ -7,6 +7,7 @@ * @flow */ +import types from 'ast-types'; import isExportsOrModuleAssignment from '../utils/isExportsOrModuleAssignment'; import isReactComponentClass from '../utils/isReactComponentClass'; import isReactCreateClassCall from '../utils/isReactCreateClassCall'; @@ -17,6 +18,8 @@ import resolveExportDeclaration from '../utils/resolveExportDeclaration'; import resolveToValue from '../utils/resolveToValue'; import resolveHOC from '../utils/resolveHOC'; +const { namedTypes: t, visit } = types; + const ERROR_MULTIPLE_DEFINITIONS = 'Multiple exported component definitions found.'; @@ -33,11 +36,11 @@ function isComponentDefinition(path) { ); } -function resolveDefinition(definition, types) { +function resolveDefinition(definition) { if (isReactCreateClassCall(definition)) { // return argument const resolvedPath = resolveToValue(definition.get('arguments', 0)); - if (types.ObjectExpression.check(resolvedPath.node)) { + if (t.ObjectExpression.check(resolvedPath.node)) { return resolvedPath; } } else if (isReactComponentClass(definition)) { @@ -69,13 +72,11 @@ function resolveDefinition(definition, types) { */ export default function findExportedComponentDefinition( ast: ASTNode, - recast: Object, ): ?NodePath { - const types = recast.types.namedTypes; let foundDefinition; function exportDeclaration(path) { - const definitions = resolveExportDeclaration(path, types).reduce( + const definitions = resolveExportDeclaration(path).reduce( (acc, definition) => { if (isComponentDefinition(definition)) { acc.push(definition); @@ -97,11 +98,11 @@ export default function findExportedComponentDefinition( // If a file exports multiple components, ... complain! throw new Error(ERROR_MULTIPLE_DEFINITIONS); } - foundDefinition = resolveDefinition(definitions[0], types); + foundDefinition = resolveDefinition(definitions[0]); return false; } - recast.visit(ast, { + visit(ast, { visitFunctionDeclaration: ignore, visitFunctionExpression: ignore, visitClassDeclaration: ignore, @@ -109,13 +110,13 @@ export default function findExportedComponentDefinition( visitIfStatement: ignore, visitWithStatement: ignore, visitSwitchStatement: ignore, - visitCatchCause: ignore, visitWhileStatement: ignore, visitDoWhileStatement: ignore, visitForStatement: ignore, visitForInStatement: ignore, + visitForOfStatement: ignore, + visitImportDeclaration: ignore, - visitExportDeclaration: exportDeclaration, visitExportNamedDeclaration: exportDeclaration, visitExportDefaultDeclaration: exportDeclaration, @@ -138,7 +139,7 @@ export default function findExportedComponentDefinition( // If a file exports multiple components, ... complain! throw new Error(ERROR_MULTIPLE_DEFINITIONS); } - foundDefinition = resolveDefinition(path, types); + foundDefinition = resolveDefinition(path); return false; }, }); diff --git a/src/types.js b/src/types.js index ff504122091..ac82bff9067 100644 --- a/src/types.js +++ b/src/types.js @@ -4,9 +4,11 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow */ import type Documentation from './Documentation'; +import { type Parser } from './babelParser'; export type PropTypeDescriptor = { name: @@ -43,10 +45,12 @@ export type FlowBaseType = { alias?: string, }; -export type FlowSimpleType = FlowBaseType & {| - name: string, - raw?: string, -|}; +export type FlowSimpleType = $Exact< + FlowBaseType & { + name: string, + raw?: string, + }, +>; export type FlowLiteralType = FlowBaseType & { name: 'literal', @@ -61,7 +65,7 @@ export type FlowElementsType = FlowBaseType & { export type FlowFunctionArgumentType = { name: string, - type: FlowTypeDescriptor, + type?: FlowTypeDescriptor, rest?: boolean, }; @@ -75,6 +79,17 @@ export type FlowFunctionSignatureType = FlowBaseType & { }, }; +export type TSFunctionSignatureType = FlowBaseType & { + name: 'signature', + type: 'function', + raw: string, + signature: { + arguments: Array, + return: FlowTypeDescriptor, + this?: FlowTypeDescriptor, + }, +}; + export type FlowObjectSignatureType = FlowBaseType & { name: 'signature', type: 'object', @@ -98,13 +113,18 @@ export type FlowTypeDescriptor = export type PropDescriptor = { type?: PropTypeDescriptor, flowType?: FlowTypeDescriptor, + tsType?: FlowTypeDescriptor, required?: boolean, defaultValue?: any, description?: string, }; -export type Handler = (documentation: Documentation, path: NodePath) => void; +export type Handler = ( + documentation: Documentation, + path: NodePath, + parser: Parser, +) => void; export type Resolver = ( node: ASTNode, - recast: Recast, + parser: Parser, ) => ?NodePath | ?Array; diff --git a/src/utils/__tests__/__snapshots__/getMembers-test.js.snap b/src/utils/__tests__/__snapshots__/getMembers-test.js.snap new file mode 100644 index 00000000000..685744318fb --- /dev/null +++ b/src/utils/__tests__/__snapshots__/getMembers-test.js.snap @@ -0,0 +1,152 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getMembers finds all "members" "inside" a MemberExpression 1`] = ` +Array [ + Object { + "argumentsPath": Node { + "arguments": Array [ + Node { + "end": 12, + "loc": SourceLocation { + "end": Position { + "column": 12, + "line": 1, + }, + "start": Position { + "column": 9, + "line": 1, + }, + }, + "raw": "123", + "start": 9, + "type": "Literal", + "value": 123, + }, + ], + "callee": Node { + "computed": false, + "end": 8, + "loc": SourceLocation { + "end": Position { + "column": 8, + "line": 1, + }, + "start": Position { + "column": 1, + "line": 1, + }, + }, + "object": Node { + "end": 4, + "loc": SourceLocation { + "end": Position { + "column": 4, + "line": 1, + }, + "identifierName": "foo", + "start": Position { + "column": 1, + "line": 1, + }, + }, + "name": "foo", + "start": 1, + "type": "Identifier", + }, + "property": Node { + "end": 8, + "loc": SourceLocation { + "end": Position { + "column": 8, + "line": 1, + }, + "identifierName": "bar", + "start": Position { + "column": 5, + "line": 1, + }, + }, + "name": "bar", + "start": 5, + "type": "Identifier", + }, + "start": 1, + "type": "MemberExpression", + }, + "end": 13, + "loc": SourceLocation { + "end": Position { + "column": 13, + "line": 1, + }, + "start": Position { + "column": 1, + "line": 1, + }, + }, + "start": 1, + "type": "CallExpression", + }, + "computed": false, + "path": Node { + "end": 8, + "loc": SourceLocation { + "end": Position { + "column": 8, + "line": 1, + }, + "identifierName": "bar", + "start": Position { + "column": 5, + "line": 1, + }, + }, + "name": "bar", + "start": 5, + "type": "Identifier", + }, + }, + Object { + "argumentsPath": null, + "computed": true, + "path": Node { + "end": 22, + "loc": SourceLocation { + "end": Position { + "column": 22, + "line": 1, + }, + "identifierName": "baz", + "start": Position { + "column": 19, + "line": 1, + }, + }, + "name": "baz", + "start": 19, + "type": "Identifier", + }, + }, + Object { + "argumentsPath": null, + "computed": true, + "path": Node { + "end": 26, + "loc": SourceLocation { + "end": Position { + "column": 26, + "line": 1, + }, + "start": Position { + "column": 24, + "line": 1, + }, + }, + "raw": "42", + "start": 24, + "type": "Literal", + "value": 42, + }, + }, +] +`; diff --git a/src/utils/__tests__/__snapshots__/getPropType-test.js.snap b/src/utils/__tests__/__snapshots__/getPropType-test.js.snap index f41957ab084..a277a74a0c0 100644 --- a/src/utils/__tests__/__snapshots__/getPropType-test.js.snap +++ b/src/utils/__tests__/__snapshots__/getPropType-test.js.snap @@ -10,7 +10,7 @@ Object { exports[`getPropType detects custom validation functions for function 1`] = ` Object { "name": "custom", - "raw": "(function() {})", + "raw": "function() {}", } `; diff --git a/src/utils/__tests__/docblock-test.js b/src/utils/__tests__/docblock-test.js index 954973c344b..bcc7708f7da 100644 --- a/src/utils/__tests__/docblock-test.js +++ b/src/utils/__tests__/docblock-test.js @@ -6,14 +6,9 @@ * */ -/*global describe, it, expect*/ -import os from 'os'; import { statement } from '../../../tests/utils'; - import { getDoclets, getDocblock } from '../docblock'; -const EOL = os.EOL; - describe('docblock', () => { describe('getDoclets', () => { it('extracts single line doclets', () => { @@ -50,21 +45,21 @@ describe('docblock', () => { ]; it('gets the closest docblock of the given node', () => { - const node = statement(source.join(EOL)); - expect(getDocblock(node)).toEqual(comment.join(EOL)); + const node = statement(source.join('\n')); + expect(getDocblock(node)).toEqual(comment.join('\n')); }); const terminators = [ - '\u000A', - '\u000D', + '\u000A', // \n + '\u000D', // \r '\u2028', '\u2029', - '\u000D\u000A', + '\u000D\u000A', // \r\n ]; terminators.forEach(t => { it('can handle ' + escape(t) + ' as line terminator', () => { const node = statement(source.join(t)); - expect(getDocblock(node)).toEqual(comment.join(EOL)); + expect(getDocblock(node)).toEqual(comment.join(t)); }); }); diff --git a/src/utils/__tests__/flowUtilityTypes-test.js b/src/utils/__tests__/flowUtilityTypes-test.js index 8075c21cb63..36f90be2970 100644 --- a/src/utils/__tests__/flowUtilityTypes-test.js +++ b/src/utils/__tests__/flowUtilityTypes-test.js @@ -6,8 +6,6 @@ * */ -/*global describe, it, expect*/ - import { unwrapUtilityType, isSupportedUtilityType } from '../flowUtilityTypes'; import { statement } from '../../../tests/utils'; diff --git a/src/utils/__tests__/getClassMemberValuePath-test.js b/src/utils/__tests__/getClassMemberValuePath-test.js index 3baa9c80e32..ec37362fb1f 100644 --- a/src/utils/__tests__/getClassMemberValuePath-test.js +++ b/src/utils/__tests__/getClassMemberValuePath-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import getClassMemberValuePath from '../getClassMemberValuePath'; +import { statement } from '../../../tests/utils'; describe('getClassMemberValuePath', () => { - let getClassMemberValuePath; - let statement; - - beforeEach(() => { - getClassMemberValuePath = require('../getClassMemberValuePath').default; - ({ statement } = require('../../../tests/utils')); - }); - describe('MethodDefinitions', () => { it('finds "normal" method definitions', () => { const def = statement(` diff --git a/src/utils/__tests__/getFlowType-test.js b/src/utils/__tests__/getFlowType-test.js index 5a7e11318bd..971a99a838a 100644 --- a/src/utils/__tests__/getFlowType-test.js +++ b/src/utils/__tests__/getFlowType-test.js @@ -6,19 +6,10 @@ * */ -/* global jest, describe, beforeEach, it, expect */ - -jest.disableAutomock(); +import getFlowType from '../getFlowType'; +import { expression, statement } from '../../../tests/utils'; describe('getFlowType', () => { - let expression, statement; - let getFlowType; - - beforeEach(() => { - getFlowType = require('../getFlowType').default; - ({ expression, statement } = require('../../../tests/utils')); - }); - it('detects simple types', () => { const simplePropTypes = [ 'string', diff --git a/src/utils/__tests__/getMemberExpressionRoot-test.js b/src/utils/__tests__/getMemberExpressionRoot-test.js index 02358ba44bf..3b12ebaba93 100644 --- a/src/utils/__tests__/getMemberExpressionRoot-test.js +++ b/src/utils/__tests__/getMemberExpressionRoot-test.js @@ -6,10 +6,7 @@ * */ -/*global describe, it, expect*/ - import { expression } from '../../../tests/utils'; - import getMemberExpressionRoot from '../getMemberExpressionRoot'; describe('getMemberExpressionRoot', () => { diff --git a/src/utils/__tests__/getMemberExpressionValuePath-test.js b/src/utils/__tests__/getMemberExpressionValuePath-test.js index 088b3def609..fa8ba03c10d 100644 --- a/src/utils/__tests__/getMemberExpressionValuePath-test.js +++ b/src/utils/__tests__/getMemberExpressionValuePath-test.js @@ -6,20 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { statement } from '../../../tests/utils'; +import getMemberExpressionValuePath from '../getMemberExpressionValuePath'; describe('getMemberExpressionValuePath', () => { - let getMemberExpressionValuePath; - let statement; - - beforeEach(() => { - getMemberExpressionValuePath = require('../getMemberExpressionValuePath') - .default; - ({ statement } = require('../../../tests/utils')); - }); - describe('MethodExpression', () => { it('finds "normal" property definitions', () => { const def = statement(` diff --git a/src/utils/__tests__/getMemberValuePath-test.js b/src/utils/__tests__/getMemberValuePath-test.js index c17fbf08003..6588c568264 100644 --- a/src/utils/__tests__/getMemberValuePath-test.js +++ b/src/utils/__tests__/getMemberValuePath-test.js @@ -6,8 +6,6 @@ * */ -/*global jest, describe, it, expect*/ - jest.mock('../getPropertyValuePath'); jest.mock('../getClassMemberValuePath'); jest.mock('../getMemberExpressionValuePath'); @@ -20,7 +18,7 @@ import getMemberValuePath from '../getMemberValuePath'; import getMemberExpressionValuePath from '../getMemberExpressionValuePath'; describe('getMemberValuePath', () => { - it('handles ObjectExpresisons', () => { + it('handles ObjectExpressions', () => { const path = expression('{}'); getMemberValuePath(path, 'foo'); diff --git a/src/utils/__tests__/getMembers-test.js b/src/utils/__tests__/getMembers-test.js index fb4375782f0..a0f0c81b6c6 100644 --- a/src/utils/__tests__/getMembers-test.js +++ b/src/utils/__tests__/getMembers-test.js @@ -6,35 +6,13 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression } from '../../../tests/utils'; +import getMembers from '../getMembers'; describe('getMembers', () => { - let expression; - let getMembers; - let memberExpressionPath; - - beforeEach(() => { - getMembers = require('../getMembers').default; - ({ expression } = require('../../../tests/utils')); - memberExpressionPath = expression('foo.bar(123)(456)[baz][42]'); - }); - it('finds all "members" "inside" a MemberExpression', () => { - const members = getMembers(memberExpressionPath); + const members = getMembers(expression('foo.bar(123)(456)[baz][42]')); - //bar(123) - expect(members[0].path.node.name).toEqual('bar'); - expect(members[0].computed).toBe(false); - expect(members[0].argumentsPath.get(0).node.value).toEqual(123); - //[baz] - expect(members[1].path.node.name).toEqual('baz'); - expect(members[1].computed).toBe(true); - expect(members[1].argumentsPath).toBe(null); - //[42] - expect(members[2].path.node.value).toEqual(42); - expect(members[2].computed).toBe(true); - expect(members[2].argumentsPath).toBe(null); + expect(members).toMatchSnapshot(); }); }); diff --git a/src/utils/__tests__/getMethodDocumentation-test.js b/src/utils/__tests__/getMethodDocumentation-test.js index 4f77432d010..d0991ed07d4 100644 --- a/src/utils/__tests__/getMethodDocumentation-test.js +++ b/src/utils/__tests__/getMethodDocumentation-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { statement } from '../../../tests/utils'; +import getMethodDocumentation from '../getMethodDocumentation'; describe('getMethodDocumentation', () => { - let getMethodDocumentation; - let statement; - - beforeEach(() => { - getMethodDocumentation = require('../getMethodDocumentation').default; - ({ statement } = require('../../../tests/utils')); - }); - describe('name', () => { it('extracts the method name', () => { const def = statement(` diff --git a/src/utils/__tests__/getParameterName-test.js b/src/utils/__tests__/getParameterName-test.js index 60f1e571fef..97caa1a38e1 100644 --- a/src/utils/__tests__/getParameterName-test.js +++ b/src/utils/__tests__/getParameterName-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression } from '../../../tests/utils'; +import getParameterName from '../getParameterName'; describe('getParameterName', () => { - let getParameterName; - let expression; - - beforeEach(() => { - getParameterName = require('../getParameterName').default; - ({ expression } = require('../../../tests/utils')); - }); - it('returns the name for a normal parameter', () => { const def = expression('function(a) {}'); const param = def.get('params', 0); diff --git a/src/utils/__tests__/getPropType-test.js b/src/utils/__tests__/getPropType-test.js index 3aaf8400944..217c1ebadeb 100644 --- a/src/utils/__tests__/getPropType-test.js +++ b/src/utils/__tests__/getPropType-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression, statement } from '../../../tests/utils'; +import getPropType from '../getPropType'; describe('getPropType', () => { - let expression, statement; - let getPropType; - - beforeEach(() => { - getPropType = require('../getPropType').default; - ({ expression, statement } = require('../../../tests/utils')); - }); - it('detects simple prop types', () => { const simplePropTypes = [ 'array', diff --git a/src/utils/__tests__/getPropertyName-test.js b/src/utils/__tests__/getPropertyName-test.js index 0404addb8f0..1b4523cb7a2 100644 --- a/src/utils/__tests__/getPropertyName-test.js +++ b/src/utils/__tests__/getPropertyName-test.js @@ -9,19 +9,14 @@ */ import { parse, expression } from '../../../tests/utils'; +import getPropertyName from '../getPropertyName'; describe('getPropertyName', () => { - let getPropertyName; - function parsePath(src) { const root = parse(src.trim()); return root.get('body', root.node.body.length - 1, 'expression'); } - beforeEach(() => { - getPropertyName = require('../getPropertyName').default; - }); - it('returns the name for a normal property', () => { const def = expression('{ foo: 1 }'); const param = def.get('properties', 0); diff --git a/src/utils/__tests__/getPropertyValuePath-test.js b/src/utils/__tests__/getPropertyValuePath-test.js index 19532354b13..b98afea40a6 100644 --- a/src/utils/__tests__/getPropertyValuePath-test.js +++ b/src/utils/__tests__/getPropertyValuePath-test.js @@ -6,25 +6,12 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { statement } from '../../../tests/utils'; +import getPropertyValuePath from '../getPropertyValuePath'; describe('getPropertyValuePath', () => { - let recast; - let getPropertyValuePath; - - function parse(src) { - return new recast.types.NodePath(recast.parse(src).program.body[0]); - } - - beforeEach(() => { - getPropertyValuePath = require('../getPropertyValuePath').default; - recast = require('recast'); - }); - it('returns the value path if the property exists', () => { - const objectExpressionPath = parse('({foo: 21, bar: 42})').get( + const objectExpressionPath = statement('({foo: 21, bar: 42})').get( 'expression', ); expect(getPropertyValuePath(objectExpressionPath, 'bar')).toBe( @@ -33,7 +20,7 @@ describe('getPropertyValuePath', () => { }); it('returns undefined if the property does not exist', () => { - const objectExpressionPath = parse('({foo: 21, bar: 42})').get( + const objectExpressionPath = statement('({foo: 21, bar: 42})').get( 'expression', ); expect(getPropertyValuePath(objectExpressionPath, 'baz')).toBeUndefined(); diff --git a/src/utils/__tests__/getTSType-test.js b/src/utils/__tests__/getTSType-test.js index 8ff2f35e276..e636a7d4ffe 100644 --- a/src/utils/__tests__/getTSType-test.js +++ b/src/utils/__tests__/getTSType-test.js @@ -6,32 +6,23 @@ * */ -/* global jest, describe, beforeEach, it, expect */ +import { expression as expr, statement as stmt } from '../../../tests/utils'; +import getTSType from '../getTSType'; -jest.disableAutomock(); - -describe('getTSType', () => { - let expression, statement; - let getTSType; - - beforeEach(() => { - getTSType = require('../getTSType').default; - const { - expression: expr, - statement: stmt, - } = require('../../../tests/utils'); - expression = code => - expr(code, undefined, { - filename: 'test.ts', - babelrc: false, - }); - statement = code => - stmt(code, undefined, { - filename: 'test.ts', - babelrc: false, - }); +function expression(code) { + return expr(code, { + filename: 'test.ts', + babelrc: false, + }); +} +function statement(code) { + return stmt(code, { + filename: 'test.ts', + babelrc: false, }); +} +describe('getTSType', () => { it('detects simple types', () => { const simplePropTypes = [ 'string', diff --git a/src/utils/__tests__/getTypeAnnotation-test.js b/src/utils/__tests__/getTypeAnnotation-test.js index f7d7106b380..6dba800b853 100644 --- a/src/utils/__tests__/getTypeAnnotation-test.js +++ b/src/utils/__tests__/getTypeAnnotation-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression } from '../../../tests/utils'; +import getTypeAnnotation from '../getTypeAnnotation'; describe('getTypeAnnotation', () => { - let expression; - let getTypeAnnotation; - - beforeEach(() => { - getTypeAnnotation = require('../getTypeAnnotation').default; - ({ expression } = require('../../../tests/utils')); - }); - it('detects simple type', () => { const path = expression('x: xyz'); diff --git a/src/utils/__tests__/isExportsOrModuleAssignment-test.js b/src/utils/__tests__/isExportsOrModuleAssignment-test.js index ae4062be3f2..bea36371742 100644 --- a/src/utils/__tests__/isExportsOrModuleAssignment-test.js +++ b/src/utils/__tests__/isExportsOrModuleAssignment-test.js @@ -6,36 +6,26 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { statement } from '../../../tests/utils'; +import isExportsOrModuleAssignment from '../isExportsOrModuleAssignment'; describe('isExportsOrModuleAssignment', () => { - let recast; - let isExportsOrModuleAssignment; - - function parse(src) { - return new recast.types.NodePath(recast.parse(src).program.body[0]); - } - - beforeEach(() => { - isExportsOrModuleAssignment = require('../isExportsOrModuleAssignment') - .default; - recast = require('recast'); - }); - it('detects "module.exports = ...;"', () => { - expect(isExportsOrModuleAssignment(parse('module.exports = foo;'))).toBe( - true, - ); + expect( + isExportsOrModuleAssignment(statement('module.exports = foo;')), + ).toBe(true); }); it('detects "exports.foo = ..."', () => { - expect(isExportsOrModuleAssignment(parse('exports.foo = foo;'))).toBe(true); + expect(isExportsOrModuleAssignment(statement('exports.foo = foo;'))).toBe( + true, + ); }); it('does not accept "exports = foo;"', () => { // That doesn't actually export anything - expect(isExportsOrModuleAssignment(parse('exports = foo;'))).toBe(false); + expect(isExportsOrModuleAssignment(statement('exports = foo;'))).toBe( + false, + ); }); }); diff --git a/src/utils/__tests__/isReactComponentClass-test.js b/src/utils/__tests__/isReactComponentClass-test.js index de9132f7f9e..46b2581f12b 100644 --- a/src/utils/__tests__/isReactComponentClass-test.js +++ b/src/utils/__tests__/isReactComponentClass-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression, statement, parse } from '../../../tests/utils'; +import isReactComponentClass from '../isReactComponentClass'; describe('isReactComponentClass', () => { - let isReactComponentClass; - let expression, statement, parse; - - beforeEach(() => { - isReactComponentClass = require('../isReactComponentClass').default; - ({ expression, statement, parse } = require('../../../tests/utils')); - }); - describe('render method', () => { it('accepts class declarations with a render method', () => { const def = statement('class Foo { render() {}}'); diff --git a/src/utils/__tests__/isReactComponentMethod-test.js b/src/utils/__tests__/isReactComponentMethod-test.js index 9c619df6e6c..c0b4920517b 100644 --- a/src/utils/__tests__/isReactComponentMethod-test.js +++ b/src/utils/__tests__/isReactComponentMethod-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression, statement } from '../../../tests/utils'; +import isReactComponentMethod from '../isReactComponentMethod'; describe('isReactComponentMethod', () => { - let isReactComponentMethod; - let expression, statement; - - beforeEach(() => { - isReactComponentMethod = require('../isReactComponentMethod').default; - ({ expression, statement } = require('../../../tests/utils')); - }); - it('returns true if the method is a component class method', () => { const def = statement('class Foo { render() {}}'); const method = def.get('body', 'body', 0); diff --git a/src/utils/__tests__/isReactCreateClassCall-test.js b/src/utils/__tests__/isReactCreateClassCall-test.js index 5bc481ac538..edf30b40235 100644 --- a/src/utils/__tests__/isReactCreateClassCall-test.js +++ b/src/utils/__tests__/isReactCreateClassCall-test.js @@ -6,27 +6,18 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { parse } from '../../../tests/utils'; +import isReactCreateClassCall from '../isReactCreateClassCall'; describe('isReactCreateClassCall', () => { - let isReactCreateClassCall; - let utils; - - function parse(src) { - const root = utils.parse(src); + function parsePath(src) { + const root = parse(src); return root.get('body', root.node.body.length - 1, 'expression'); } - beforeEach(() => { - isReactCreateClassCall = require('../isReactCreateClassCall').default; - utils = require('../../../tests/utils'); - }); - describe('built in React.createClass', () => { it('accepts createClass called on React', () => { - const def = parse(` + const def = parsePath(` var React = require("React"); React.createClass({ render() {} @@ -36,7 +27,7 @@ describe('isReactCreateClassCall', () => { }); it('accepts createClass called on aliased React', () => { - const def = parse(` + const def = parsePath(` var other = require("React"); other.createClass({ render() {} @@ -46,7 +37,7 @@ describe('isReactCreateClassCall', () => { }); it('ignores other React calls', () => { - const def = parse(` + const def = parsePath(` var React = require("React"); React.isValidElement({}); `); @@ -54,7 +45,7 @@ describe('isReactCreateClassCall', () => { }); it('ignores non React calls to createClass', () => { - const def = parse(` + const def = parsePath(` var React = require("bob"); React.createClass({ render() {} @@ -66,7 +57,7 @@ describe('isReactCreateClassCall', () => { describe('modular in create-react-class', () => { it('accepts create-react-class', () => { - const def = parse(` + const def = parsePath(` var createReactClass = require("create-react-class"); createReactClass({ render() {} @@ -76,7 +67,7 @@ describe('isReactCreateClassCall', () => { }); it('accepts create-react-class calls on another name', () => { - const def = parse(` + const def = parsePath(` var makeClass = require("create-react-class"); makeClass({ render() {} @@ -86,7 +77,7 @@ describe('isReactCreateClassCall', () => { }); it('ignores non create-react-class calls to createReactClass', () => { - const def = parse(` + const def = parsePath(` var createReactClass = require("bob"); createReactClass({ render() {} diff --git a/src/utils/__tests__/isRequiredPropType-test.js b/src/utils/__tests__/isRequiredPropType-test.js index 483fb8c6133..deef0518d11 100644 --- a/src/utils/__tests__/isRequiredPropType-test.js +++ b/src/utils/__tests__/isRequiredPropType-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { expression } from '../../../tests/utils'; +import isRequiredPropType from '../isRequiredPropType'; describe('isRequiredPropType', () => { - let expression; - let isRequiredPropType; - - beforeEach(() => { - isRequiredPropType = require('../isRequiredPropType').default; - ({ expression } = require('../../../tests/utils')); - }); - it('considers isRequired', () => { expect(isRequiredPropType(expression('foo.bar.isRequired'))).toEqual(true); expect(isRequiredPropType(expression('foo.isRequired.bar'))).toEqual(true); diff --git a/src/utils/__tests__/isStatelessComponent-test.js b/src/utils/__tests__/isStatelessComponent-test.js index f81ccfa8151..abfcc5ece6d 100644 --- a/src/utils/__tests__/isStatelessComponent-test.js +++ b/src/utils/__tests__/isStatelessComponent-test.js @@ -6,19 +6,10 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { statement, parse } from '../../../tests/utils'; +import isStatelessComponent from '../isStatelessComponent'; describe('isStatelessComponent', () => { - let isStatelessComponent; - let statement, parse; - - beforeEach(() => { - isStatelessComponent = require('../isStatelessComponent').default; - ({ statement, parse } = require('../../../tests/utils')); - }); - const componentIdentifiers = { JSX: '
', JSXFragment: '<>', diff --git a/src/utils/__tests__/match-test.js b/src/utils/__tests__/match-test.js index 1711f49c08a..1270e95f6e8 100644 --- a/src/utils/__tests__/match-test.js +++ b/src/utils/__tests__/match-test.js @@ -6,17 +6,9 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import match from '../match'; describe('match', () => { - let match; - - beforeEach(() => { - match = require('../match').default; - }); - it('matches with exact properties', () => { expect(match({ foo: { bar: 42 } }, { foo: { bar: 42 } })).toBe(true); }); diff --git a/src/utils/__tests__/normalizeClassDefinition-test.js b/src/utils/__tests__/normalizeClassDefinition-test.js index 746bb887f4d..df9bf8ac77d 100644 --- a/src/utils/__tests__/normalizeClassDefinition-test.js +++ b/src/utils/__tests__/normalizeClassDefinition-test.js @@ -6,22 +6,15 @@ * */ -/*global describe, beforeEach, it, expect*/ +import { statement, parse } from '../../../tests/utils'; +import normalizeClassDefinition from '../normalizeClassDefinition'; describe('normalizeClassDefinition', () => { - let parse; - let normalizeClassDefinition; - - beforeEach(() => { - ({ parse } = require('../../../tests/utils')); - normalizeClassDefinition = require('../normalizeClassDefinition').default; - }); - it('finds assignments to class declarations', () => { - const classDefinition = parse(` + const classDefinition = statement(` class Foo {} Foo.propTypes = 42; - `).get('body', 0); + `); normalizeClassDefinition(classDefinition); const { @@ -38,11 +31,11 @@ describe('normalizeClassDefinition', () => { }); it('should not fail on classes without ids', () => { - const classDefinition = parse(` + const classDefinition = statement(` export default class extends React.Component { static propTypes = 42; } - `).get('body', 0, 'declaration'); + `).get('declaration'); normalizeClassDefinition(classDefinition); const { @@ -59,10 +52,10 @@ describe('normalizeClassDefinition', () => { }); it('finds assignments to class expressions', () => { - let classDefinition = parse(` + let classDefinition = statement(` var Foo = class {}; Foo.propTypes = 42; - `).get('body', 0, 'declarations', 0, 'init'); + `).get('declarations', 0, 'init'); normalizeClassDefinition(classDefinition); let { @@ -95,22 +88,12 @@ describe('normalizeClassDefinition', () => { }); it('ignores assignments further up the tree', () => { - const classDefinition = parse(` + const classDefinition = statement(` var Foo = function() { (class {}); }; Foo.bar = 42; - `).get( - 'body', - 0, - 'declarations', - 0, - 'init', - 'body', - 'body', - '0', - 'expression', - ); + `).get('declarations', 0, 'init', 'body', 'body', '0', 'expression'); normalizeClassDefinition(classDefinition); const { diff --git a/src/utils/__tests__/parseJsDoc-test.js b/src/utils/__tests__/parseJsDoc-test.js index 5746ad55240..c6ff7f33a23 100644 --- a/src/utils/__tests__/parseJsDoc-test.js +++ b/src/utils/__tests__/parseJsDoc-test.js @@ -6,17 +6,9 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import parseJsDoc from '../parseJsDoc'; describe('parseJsDoc', () => { - let parseJsDoc; - - beforeEach(() => { - parseJsDoc = require('../parseJsDoc').default; - }); - describe('description', () => { it('extracts the method description in jsdoc', () => { const docblock = ` diff --git a/src/utils/__tests__/printValue-test.js b/src/utils/__tests__/printValue-test.js index cc933e681e4..b5856c71770 100644 --- a/src/utils/__tests__/printValue-test.js +++ b/src/utils/__tests__/printValue-test.js @@ -6,21 +6,12 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { parse } from '../../../tests/utils'; +import printValue from '../printValue'; describe('printValue', () => { - let printValue; - let utils; - - beforeEach(() => { - printValue = require('../printValue').default; - utils = require('../../../tests/utils'); - }); - function pathFromSource(source) { - return utils.parse(source).get('body', 0, 'expression'); + return parse(source).get('body', 0, 'expression'); } it('does not print leading comments', () => { diff --git a/src/utils/__tests__/resolveExportDeclaration-test.js b/src/utils/__tests__/resolveExportDeclaration-test.js index 4a010eb74a4..7bbd8832821 100644 --- a/src/utils/__tests__/resolveExportDeclaration-test.js +++ b/src/utils/__tests__/resolveExportDeclaration-test.js @@ -6,12 +6,9 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - jest.mock('../resolveToValue'); import { statement } from '../../../tests/utils'; - import resolveToValue from '../resolveToValue'; import resolveExportDeclaration from '../resolveExportDeclaration'; diff --git a/src/utils/__tests__/resolveHOC-test.js b/src/utils/__tests__/resolveHOC-test.js index b42859e9541..27df4fd912b 100644 --- a/src/utils/__tests__/resolveHOC-test.js +++ b/src/utils/__tests__/resolveHOC-test.js @@ -6,27 +6,18 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ +import types from 'ast-types'; +import resolveHOC from '../resolveHOC'; +import * as utils from '../../../tests/utils'; -jest.disableAutomock(); +const { builders } = types; describe('resolveHOC', () => { - let builders; - let utils; - let resolveHOC; - function parse(src) { const root = utils.parse(src); return root.get('body', root.node.body.length - 1, 'expression'); } - beforeEach(() => { - const recast = require('recast'); - builders = recast.types.builders; - resolveHOC = require('../resolveHOC').default; - utils = require('../../../tests/utils'); - }); - it('resolves simple hoc', () => { const path = parse(['hoc(42);'].join('\n')); expect(resolveHOC(path)).toEqualASTNode(builders.literal(42)); diff --git a/src/utils/__tests__/resolveObjectKeysToArray-test.js b/src/utils/__tests__/resolveObjectKeysToArray-test.js index 04fa899724a..2948627cb16 100644 --- a/src/utils/__tests__/resolveObjectKeysToArray-test.js +++ b/src/utils/__tests__/resolveObjectKeysToArray-test.js @@ -6,14 +6,12 @@ * */ -/* eslint-env jest */ - -import recast from 'recast'; - -const builders = recast.types.builders; +import types from 'ast-types'; import resolveObjectKeysToArray from '../resolveObjectKeysToArray'; import * as utils from '../../../tests/utils'; +const { builders } = types; + describe('resolveObjectKeysToArray', () => { function parse(src) { const root = utils.parse(src); diff --git a/src/utils/__tests__/resolveObjectValuesToArray-test.js b/src/utils/__tests__/resolveObjectValuesToArray-test.js index 117024c9947..b03d5b2bdcf 100644 --- a/src/utils/__tests__/resolveObjectValuesToArray-test.js +++ b/src/utils/__tests__/resolveObjectValuesToArray-test.js @@ -6,14 +6,12 @@ * */ -/* eslint-env jest */ - -import recast from 'recast'; - -const builders = recast.types.builders; +import types from 'ast-types'; import resolveObjectValuesToArray from '../resolveObjectValuesToArray'; import * as utils from '../../../tests/utils'; +const { builders } = types; + describe('resolveObjectValuesToArray', () => { function parse(src) { const root = utils.parse(src); @@ -68,6 +66,14 @@ describe('resolveObjectValuesToArray', () => { ); }); + it('does not resolve Object.values with complex computed key', () => { + const path = parse( + ['var foo = { [()=>{}]: 1, [5]: 2};', 'Object.values(foo);'].join('\n'), + ); + + expect(resolveObjectValuesToArray(path)).toBeNull(); + }); + it('resolves Object.values when using resolvable spread', () => { const path = parse( [ diff --git a/src/utils/__tests__/resolveToModule-test.js b/src/utils/__tests__/resolveToModule-test.js index 73a54c417ae..4d2c65b02d7 100644 --- a/src/utils/__tests__/resolveToModule-test.js +++ b/src/utils/__tests__/resolveToModule-test.js @@ -6,26 +6,17 @@ * */ -/*global jest, describe, beforeEach, it, expect*/ - -jest.disableAutomock(); +import { parse } from '../../../tests/utils'; +import resolveToModule from '../resolveToModule'; describe('resolveToModule', () => { - let utils; - let resolveToModule; - - function parse(src) { - const root = utils.parse(src); + function parsePath(src) { + const root = parse(src); return root.get('body', root.node.body.length - 1, 'expression'); } - beforeEach(() => { - resolveToModule = require('../resolveToModule').default; - utils = require('../../../tests/utils'); - }); - it('resolves identifiers', () => { - const path = parse(` + const path = parsePath(` var foo = require("Foo"); foo; `); @@ -33,7 +24,7 @@ describe('resolveToModule', () => { }); it('resolves function calls', () => { - const path = parse(` + const path = parsePath(` var foo = require("Foo"); foo(); `); @@ -41,7 +32,7 @@ describe('resolveToModule', () => { }); it('resolves member expressions', () => { - const path = parse(` + const path = parsePath(` var foo = require("Foo"); foo.bar().baz; `); @@ -49,7 +40,7 @@ describe('resolveToModule', () => { }); it('understands destructuring', () => { - const path = parse(` + const path = parsePath(` var {foo} = require("Foo"); foo; `); @@ -58,13 +49,13 @@ describe('resolveToModule', () => { describe('ES6 import declarations', () => { it('resolves ImportDefaultSpecifier', () => { - let path = parse(` + let path = parsePath(` import foo from "Foo"; foo; `); expect(resolveToModule(path)).toBe('Foo'); - path = parse(` + path = parsePath(` import foo, {createElement} from "Foo"; foo; `); @@ -72,7 +63,7 @@ describe('resolveToModule', () => { }); it('resolves ImportSpecifier', () => { - const path = parse(` + const path = parsePath(` import {foo, bar} from "Foo"; bar; `); @@ -80,7 +71,7 @@ describe('resolveToModule', () => { }); it('resolves aliased ImportSpecifier', () => { - const path = parse(` + const path = parsePath(` import {foo, bar as baz} from "Foo"; baz; `); @@ -88,7 +79,7 @@ describe('resolveToModule', () => { }); it('resolves ImportNamespaceSpecifier', () => { - const path = parse(` + const path = parsePath(` import * as foo from "Foo"; foo; `); diff --git a/src/utils/__tests__/resolveToValue-test.js b/src/utils/__tests__/resolveToValue-test.js index 6964a992e54..fc4ec17c1f3 100644 --- a/src/utils/__tests__/resolveToValue-test.js +++ b/src/utils/__tests__/resolveToValue-test.js @@ -6,27 +6,25 @@ * */ -/*global describe, it, expect*/ - -import recast from 'recast'; - -const builders = recast.types.builders; +import types from 'ast-types'; import resolveToValue from '../resolveToValue'; -import * as utils from '../../../tests/utils'; +import { parse } from '../../../tests/utils'; + +const { builders } = types; describe('resolveToValue', () => { - function parse(src) { - const root = utils.parse(src.trim()); + function parsePath(src) { + const root = parse(src.trim()); return root.get('body', root.node.body.length - 1, 'expression'); } it('resolves simple variable declarations', () => { - const path = parse(['var foo = 42;', 'foo;'].join('\n')); + const path = parsePath(['var foo = 42;', 'foo;'].join('\n')); expect(resolveToValue(path)).toEqualASTNode(builders.literal(42)); }); it('resolves object destructuring', () => { - const path = parse(['var {foo: {bar: baz}} = bar;', 'baz;'].join('\n')); + const path = parsePath(['var {foo: {bar: baz}} = bar;', 'baz;'].join('\n')); // Node should be equal to bar.foo.bar expect(resolveToValue(path)).toEqualASTNode( @@ -41,25 +39,27 @@ describe('resolveToValue', () => { }); it('handles SpreadElements properly', () => { - const path = parse(['var {foo: {bar}, ...baz} = bar;', 'baz;'].join('\n')); + const path = parsePath( + ['var {foo: {bar}, ...baz} = bar;', 'baz;'].join('\n'), + ); expect(resolveToValue(path)).toEqualASTNode(path); }); it('returns the original path if it cannot be resolved', () => { - const path = parse(['function foo() {}', 'foo()'].join('\n')); + const path = parsePath(['function foo() {}', 'foo()'].join('\n')); expect(resolveToValue(path)).toEqualASTNode(path); }); it('resolves variable declarators to their init value', () => { - const path = utils.parse('var foo = 42;').get('body', 0, 'declarations', 0); + const path = parse('var foo = 42;').get('body', 0, 'declarations', 0); expect(resolveToValue(path)).toEqualASTNode(builders.literal(42)); }); it('resolves to class declarations', () => { - const path = parse(` + const path = parsePath(` class Foo {} Foo; `); @@ -67,7 +67,7 @@ describe('resolveToValue', () => { }); it('resolves to class function declaration', () => { - const path = parse(` + const path = parsePath(` function foo() {} foo; `); @@ -75,7 +75,7 @@ describe('resolveToValue', () => { }); it('resolves type cast expressions', () => { - const path = parse(` + const path = parsePath(` function foo() {} (foo: any); `); @@ -84,7 +84,7 @@ describe('resolveToValue', () => { describe('assignments', () => { it('resolves to assigned values', () => { - const path = parse(['var foo;', 'foo = 42;', 'foo;'].join('\n')); + const path = parsePath(['var foo;', 'foo = 42;', 'foo;'].join('\n')); expect(resolveToValue(path)).toEqualASTNode(builders.literal(42)); }); @@ -92,7 +92,7 @@ describe('resolveToValue', () => { describe('ImportDeclaration', () => { it('resolves default import references to the import declaration', () => { - const path = parse(['import foo from "Foo"', 'foo;'].join('\n')); + const path = parsePath(['import foo from "Foo"', 'foo;'].join('\n')); const value = resolveToValue(path); expect(Array.isArray(value.value)).toBe(false); @@ -100,7 +100,7 @@ describe('resolveToValue', () => { }); it('resolves named import references to the import declaration', () => { - const path = parse(['import {foo} from "Foo"', 'foo;'].join('\n')); + const path = parsePath(['import {foo} from "Foo"', 'foo;'].join('\n')); const value = resolveToValue(path); expect(Array.isArray(value.value)).toBe(false); @@ -108,7 +108,9 @@ describe('resolveToValue', () => { }); it('resolves aliased import references to the import declaration', () => { - const path = parse(['import {foo as bar} from "Foo"', 'bar;'].join('\n')); + const path = parsePath( + ['import {foo as bar} from "Foo"', 'bar;'].join('\n'), + ); const value = resolveToValue(path); expect(Array.isArray(value.value)).toBe(false); @@ -116,7 +118,7 @@ describe('resolveToValue', () => { }); it('resolves namespace import references to the import declaration', () => { - const path = parse(['import * as bar from "Foo"', 'bar;'].join('\n')); + const path = parsePath(['import * as bar from "Foo"', 'bar;'].join('\n')); const value = resolveToValue(path); expect(Array.isArray(value.value)).toBe(false); @@ -126,13 +128,13 @@ describe('resolveToValue', () => { describe('MemberExpression', () => { it("resolves a MemberExpression to it's init value", () => { - const path = parse(['var foo = { bar: 1 };', 'foo.bar;'].join('\n')); + const path = parsePath(['var foo = { bar: 1 };', 'foo.bar;'].join('\n')); expect(resolveToValue(path)).toEqualASTNode(builders.literal(1)); }); it('resolves a MemberExpression in the scope chain', () => { - const path = parse( + const path = parsePath( ['var foo = 1;', 'var bar = { baz: foo };', 'bar.baz;'].join('\n'), ); @@ -140,7 +142,7 @@ describe('resolveToValue', () => { }); it('resolves a nested MemberExpression in the scope chain', () => { - const path = parse( + const path = parsePath( [ 'var foo = { bar: 1 };', 'var bar = { baz: foo.bar };', @@ -152,7 +154,7 @@ describe('resolveToValue', () => { }); it('returns the last resolvable MemberExpression', () => { - const path = parse( + const path = parsePath( [ 'import foo from "bar";', 'var bar = { baz: foo.bar };', @@ -169,7 +171,7 @@ describe('resolveToValue', () => { }); it('returns the path itself if it can not resolve it any further', () => { - const path = parse( + const path = parsePath( ['var foo = {};', 'foo.bar = 1;', 'foo.bar;'].join('\n'), ); diff --git a/src/utils/__tests__/setPropDescription-test.js b/src/utils/__tests__/setPropDescription-test.js index aa64c573048..e2012414a1d 100644 --- a/src/utils/__tests__/setPropDescription-test.js +++ b/src/utils/__tests__/setPropDescription-test.js @@ -6,19 +6,15 @@ * */ -/*global jest, describe, it, expect, beforeEach*/ - -jest.disableAutomock(); jest.mock('../../Documentation'); +import { expression } from '../../../tests/utils'; + describe('setPropDescription', () => { - let expression; let defaultDocumentation; let setPropDescription; beforeEach(() => { - ({ expression } = require('../../../tests/utils')); - defaultDocumentation = new (require('../../Documentation'))(); setPropDescription = require('../setPropDescription').default; }); diff --git a/src/utils/docblock.js b/src/utils/docblock.js index e8b4452d608..563414ea1d9 100644 --- a/src/utils/docblock.js +++ b/src/utils/docblock.js @@ -14,11 +14,9 @@ const DOCLET_PATTERN = /^@(\w+)(?:$|\s((?:[^](?!^@\w))*))/gim; function parseDocblock(str) { - const lines = str.split('\n'); - for (let i = 0, l = lines.length; i < l; i++) { - lines[i] = lines[i].replace(/^\s*\*\s?/, ''); - } - return lines.join('\n').trim(); + // Does not use \s in the regex as this would match also \n and conflicts + // with windows line endings. + return str.replace(/^[ \t]*\*[ \t]?/gm, '').trim(); } const DOCBLOCK_HEADER = /^\*\s/; diff --git a/src/utils/expressionTo.js b/src/utils/expressionTo.js index 9c2a3b98c08..487a96e3c7f 100644 --- a/src/utils/expressionTo.js +++ b/src/utils/expressionTo.js @@ -9,12 +9,10 @@ /*eslint no-loop-func: 0, no-use-before-define: 0*/ +import types from 'ast-types'; import resolveToValue from './resolveToValue'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Splits a MemberExpression or CallExpression into parts. @@ -27,10 +25,10 @@ function toArray(path: NodePath): Array { while (parts.length > 0) { path = parts.shift(); const node = path.node; - if (types.CallExpression.check(node)) { + if (t.CallExpression.check(node)) { parts.push(path.get('callee')); continue; - } else if (types.MemberExpression.check(node)) { + } else if (t.MemberExpression.check(node)) { parts.push(path.get('object')); if (node.computed) { const resolvedPath = resolveToValue(path.get('property')); @@ -43,16 +41,16 @@ function toArray(path: NodePath): Array { result.push(node.property.name); } continue; - } else if (types.Identifier.check(node)) { + } else if (t.Identifier.check(node)) { result.push(node.name); continue; - } else if (types.Literal.check(node)) { + } else if (t.Literal.check(node)) { result.push(node.raw); continue; - } else if (types.ThisExpression.check(node)) { + } else if (t.ThisExpression.check(node)) { result.push('this'); continue; - } else if (types.ObjectExpression.check(node)) { + } else if (t.ObjectExpression.check(node)) { const properties = path.get('properties').map(function(property) { return ( toString(property.get('key')) + ': ' + toString(property.get('value')) @@ -60,7 +58,7 @@ function toArray(path: NodePath): Array { }); result.push('{' + properties.join(', ') + '}'); continue; - } else if (types.ArrayExpression.check(node)) { + } else if (t.ArrayExpression.check(node)) { result.push( '[' + path diff --git a/src/utils/flowUtilityTypes.js b/src/utils/flowUtilityTypes.js index 8934463586c..167ec34881b 100644 --- a/src/utils/flowUtilityTypes.js +++ b/src/utils/flowUtilityTypes.js @@ -6,11 +6,9 @@ * * @flow */ -import recast from 'recast'; +import types from 'ast-types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; const supportedUtilityTypes = new Set(['$Exact', '$ReadOnly']); @@ -19,7 +17,7 @@ const supportedUtilityTypes = new Set(['$Exact', '$ReadOnly']); * https://flow.org/en/docs/types/utilities/ for which types are available. */ export function isSupportedUtilityType(path: NodePath): boolean { - if (types.GenericTypeAnnotation.check(path.node)) { + if (t.GenericTypeAnnotation.check(path.node)) { const idPath = path.get('id'); return !!idPath && supportedUtilityTypes.has(idPath.node.name); } diff --git a/src/utils/getClassMemberValuePath.js b/src/utils/getClassMemberValuePath.js index 6e8275acac8..3623090dcc3 100644 --- a/src/utils/getClassMemberValuePath.js +++ b/src/utils/getClassMemberValuePath.js @@ -7,12 +7,10 @@ * @flow */ +import types from 'ast-types'; import getNameOrValue from './getNameOrValue'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; export default function getClassMemberValuePath( classDefinition: NodePath, @@ -25,9 +23,8 @@ export default function getClassMemberValuePath( .get('body', 'body') .filter( memberPath => - (!memberPath.node.computed || - types.Literal.check(memberPath.node.key)) && - !types.PrivateName.check(memberPath.node.key) && + (!memberPath.node.computed || t.Literal.check(memberPath.node.key)) && + !t.PrivateName.check(memberPath.node.key) && getNameOrValue(memberPath.get('key')) === memberName && memberPath.node.kind !== 'set', ) diff --git a/src/utils/getFlowType.js b/src/utils/getFlowType.js index 1aca5dc6d65..572058e245d 100644 --- a/src/utils/getFlowType.js +++ b/src/utils/getFlowType.js @@ -7,11 +7,9 @@ * @flow */ -/* eslint no-use-before-define: 0 */ - +import types from 'ast-types'; import getPropertyName from './getPropertyName'; import printValue from './printValue'; -import recast from 'recast'; import getTypeAnnotation from '../utils/getTypeAnnotation'; import resolveToValue from '../utils/resolveToValue'; import { resolveObjectToNameArray } from '../utils/resolveObjectKeysToArray'; @@ -19,15 +17,14 @@ import getTypeParameters, { type TypeParameters, } from '../utils/getTypeParameters'; import type { - FlowTypeDescriptor, FlowElementsType, FlowFunctionSignatureType, FlowObjectSignatureType, + FlowSimpleType, + FlowTypeDescriptor, } from '../types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; const flowTypes = { AnyTypeAnnotation: 'any', @@ -71,16 +68,16 @@ function getFlowTypeWithRequirements( function handleKeysHelper(path: NodePath): ?FlowElementsType { let value = path.get('typeParameters', 'params', 0); - if (types.TypeofTypeAnnotation.check(value.node)) { + if (t.TypeofTypeAnnotation.check(value.node)) { value = value.get('argument', 'id'); - } else if (!types.ObjectTypeAnnotation.check(value.node)) { + } else if (!t.ObjectTypeAnnotation.check(value.node)) { value = value.get('id'); } const resolvedPath = resolveToValue(value); if ( resolvedPath && - (types.ObjectExpression.check(resolvedPath.node) || - types.ObjectTypeAnnotation.check(resolvedPath.node)) + (t.ObjectExpression.check(resolvedPath.node) || + t.ObjectTypeAnnotation.check(resolvedPath.node)) ) { const keys = resolveObjectToNameArray(resolvedPath, true); @@ -118,7 +115,7 @@ function handleGenericTypeAnnotation( } let type: FlowTypeDescriptor; - if (types.QualifiedTypeIdentifier.check(path.node.id)) { + if (t.QualifiedTypeIdentifier.check(path.node.id)) { const id = path.get('id'); if (id.node.qualification.name === 'React') { @@ -191,9 +188,10 @@ function handleObjectTypeAnnotation( }); path.get('properties').each(param => { - if (types.ObjectTypeProperty.check(param.node)) { + if (t.ObjectTypeProperty.check(param.node)) { type.signature.properties.push({ - key: getPropertyName(param), + // For ObjectTypeProperties `getPropertyName` always returns string + key: ((getPropertyName(param): any): string), value: getFlowTypeWithRequirements(param.get('value'), typeParams), }); } @@ -202,7 +200,7 @@ function handleObjectTypeAnnotation( return type; } -function handleInterfaceDeclaration(path: NodePath): FlowElementsType { +function handleInterfaceDeclaration(path: NodePath): FlowSimpleType { // Interfaces are handled like references which would be documented separately, // rather than inlined like type aliases. return { @@ -271,7 +269,7 @@ function handleFunctionTypeAnnotation( name: param.node.name ? param.node.name.name : '', type: typeAnnotation ? getFlowTypeWithResolvedTypes(typeAnnotation, typeParams) - : null, + : undefined, }); }); @@ -283,7 +281,7 @@ function handleFunctionTypeAnnotation( name: rest.node.name ? rest.node.name.name : '', type: typeAnnotation ? getFlowTypeWithResolvedTypes(typeAnnotation, typeParams) - : null, + : undefined, rest: true, }); } @@ -324,7 +322,7 @@ function getFlowTypeWithResolvedTypes( const node = path.node; let type: ?FlowTypeDescriptor; - const isTypeAlias = types.TypeAlias.check(path.parentPath.node); + const isTypeAlias = t.TypeAlias.check(path.parentPath.node); // When we see a typealias mark it as visited so that the next // call of this function does not run into an endless loop if (isTypeAlias) { diff --git a/src/utils/getMemberExpressionRoot.js b/src/utils/getMemberExpressionRoot.js index 2eb71445697..c87785bbc9a 100644 --- a/src/utils/getMemberExpressionRoot.js +++ b/src/utils/getMemberExpressionRoot.js @@ -7,10 +7,9 @@ * @flow */ -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +import types from 'ast-types'; + +const { namedTypes: t } = types; /** * Returns the path to the first part of the MemberExpression. I.e. given a @@ -25,6 +24,6 @@ export default function getMemberExpressionRoot( ): NodePath { do { memberExpressionPath = memberExpressionPath.get('object'); - } while (types.MemberExpression.check(memberExpressionPath.node)); + } while (t.MemberExpression.check(memberExpressionPath.node)); return memberExpressionPath; } diff --git a/src/utils/getMemberExpressionValuePath.js b/src/utils/getMemberExpressionValuePath.js index ab071b0afc4..f9a5aa5f909 100644 --- a/src/utils/getMemberExpressionValuePath.js +++ b/src/utils/getMemberExpressionValuePath.js @@ -7,17 +7,15 @@ * @flow */ +import types from 'ast-types'; import getNameOrValue from './getNameOrValue'; import { String as toString } from './expressionTo'; import isReactForwardRefCall from './isReactForwardRefCall'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { visit, namedTypes: t } = types; function resolveName(path) { - if (types.VariableDeclaration.check(path.node)) { + if (t.VariableDeclaration.check(path.node)) { const declarations = path.get('declarations'); if (declarations.value.length && declarations.value.length !== 1) { throw new TypeError( @@ -31,20 +29,20 @@ function resolveName(path) { return value; } - if (types.FunctionDeclaration.check(path.node)) { + if (t.FunctionDeclaration.check(path.node)) { return path.get('id', 'name').value; } if ( - types.FunctionExpression.check(path.node) || - types.ArrowFunctionExpression.check(path.node) || - types.TaggedTemplateExpression.check(path.node) || - types.CallExpression.check(path.node) || + t.FunctionExpression.check(path.node) || + t.ArrowFunctionExpression.check(path.node) || + t.TaggedTemplateExpression.check(path.node) || + t.CallExpression.check(path.node) || isReactForwardRefCall(path) ) { let currentPath = path; while (currentPath.parent) { - if (types.VariableDeclarator.check(currentPath.parent.node)) { + if (t.VariableDeclarator.check(currentPath.parent.node)) { return currentPath.parent.get('id', 'name').value; } @@ -84,16 +82,16 @@ export default function getMemberExpressionValuePath( } let result; - recast.visit(program, { + visit(program, { visitAssignmentExpression(path) { const memberPath = path.get('left'); - if (!types.MemberExpression.check(memberPath.node)) { + if (!t.MemberExpression.check(memberPath.node)) { return this.traverse(path); } if ( (!memberPath.node.computed || - types.Literal.check(memberPath.node.property)) && + t.Literal.check(memberPath.node.property)) && getNameOrValue(memberPath.get('property')) === memberName && toString(memberPath.get('object')) === localName ) { diff --git a/src/utils/getMemberValuePath.js b/src/utils/getMemberValuePath.js index 9f810d7d9b8..0583834fd51 100644 --- a/src/utils/getMemberValuePath.js +++ b/src/utils/getMemberValuePath.js @@ -6,16 +6,13 @@ * * @flow */ - +import types from 'ast-types'; import getClassMemberValuePath from './getClassMemberValuePath'; import getMemberExpressionValuePath from './getMemberExpressionValuePath'; import getPropertyValuePath from './getPropertyValuePath'; import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; const SYNONYMS = { getDefaultProps: 'defaultProps', @@ -24,27 +21,27 @@ const SYNONYMS = { const POSTPROCESS_MEMBERS = { propTypes: path => - types.Function.check(path.node) + t.Function.check(path.node) ? resolveFunctionDefinitionToReturnValue(path) : path, }; const LOOKUP_METHOD = { - [types.ArrowFunctionExpression.name]: getMemberExpressionValuePath, - [types.CallExpression.name]: getMemberExpressionValuePath, - [types.FunctionExpression.name]: getMemberExpressionValuePath, - [types.FunctionDeclaration.name]: getMemberExpressionValuePath, - [types.VariableDeclaration.name]: getMemberExpressionValuePath, - [types.ObjectExpression.name]: getPropertyValuePath, - [types.ClassDeclaration.name]: getClassMemberValuePath, - [types.ClassExpression.name]: getClassMemberValuePath, + [t.ArrowFunctionExpression.name]: getMemberExpressionValuePath, + [t.CallExpression.name]: getMemberExpressionValuePath, + [t.FunctionExpression.name]: getMemberExpressionValuePath, + [t.FunctionDeclaration.name]: getMemberExpressionValuePath, + [t.VariableDeclaration.name]: getMemberExpressionValuePath, + [t.ObjectExpression.name]: getPropertyValuePath, + [t.ClassDeclaration.name]: getClassMemberValuePath, + [t.ClassExpression.name]: getClassMemberValuePath, }; function isSupportedDefinitionType({ node }) { return ( - types.ObjectExpression.check(node) || - types.ClassDeclaration.check(node) || - types.ClassExpression.check(node) || + t.ObjectExpression.check(node) || + t.ClassDeclaration.check(node) || + t.ClassExpression.check(node) || /** * Adds support for libraries such as * [styled components]{@link https://github.com/styled-components} that use @@ -55,12 +52,12 @@ function isSupportedDefinitionType({ node }) { * https://github.com/Jmeyering/react-docgen-annotation-resolver) could be * used to add these definitions. */ - types.TaggedTemplateExpression.check(node) || + t.TaggedTemplateExpression.check(node) || // potential stateless function component - types.VariableDeclaration.check(node) || - types.ArrowFunctionExpression.check(node) || - types.FunctionDeclaration.check(node) || - types.FunctionExpression.check(node) || + t.VariableDeclaration.check(node) || + t.ArrowFunctionExpression.check(node) || + t.FunctionDeclaration.check(node) || + t.FunctionExpression.check(node) || /** * Adds support for libraries such as * [system-components]{@link https://jxnblk.com/styled-system/system-components} that use @@ -71,7 +68,7 @@ function isSupportedDefinitionType({ node }) { * https://github.com/Jmeyering/react-docgen-annotation-resolver) could be * used to add these definitions. */ - types.CallExpression.check(node) + t.CallExpression.check(node) ); } diff --git a/src/utils/getMembers.js b/src/utils/getMembers.js index ed5e813489a..0b02083bd39 100644 --- a/src/utils/getMembers.js +++ b/src/utils/getMembers.js @@ -7,11 +7,9 @@ * @flow */ -/* eslint no-labels: 0 */ +import types from 'ast-types'; -/** - * Helper methods for dealing with MemberExpressions (and CallExpressions). - */ +const { namedTypes: t } = types; type MemberDescriptor = { path: NodePath, @@ -19,12 +17,6 @@ type MemberDescriptor = { argumentsPath?: ?NodePath, }; -import recast from 'recast'; - -const { - types: { namedTypes: types }, -} = recast; - /** * Given a "nested" Member/CallExpression, e.g. * @@ -46,7 +38,7 @@ export default function getMembers( // eslint-disable-next-line no-constant-condition loop: while (true) { switch (true) { - case types.MemberExpression.check(path.node): + case t.MemberExpression.check(path.node): result.push({ path: path.get('property'), computed: path.node.computed, @@ -55,7 +47,7 @@ export default function getMembers( argumentsPath = null; path = path.get('object'); break; - case types.CallExpression.check(path.node): + case t.CallExpression.check(path.node): argumentsPath = path.get('arguments'); path = path.get('callee'); break; diff --git a/src/utils/getMethodDocumentation.js b/src/utils/getMethodDocumentation.js index 163a74b73a1..1b7b5d9572a 100644 --- a/src/utils/getMethodDocumentation.js +++ b/src/utils/getMethodDocumentation.js @@ -7,18 +7,16 @@ * @flow */ +import types from 'ast-types'; import { getDocblock } from './docblock'; import getFlowType from './getFlowType'; import getTSType from './getTSType'; import getParameterName from './getParameterName'; import getPropertyName from './getPropertyName'; import getTypeAnnotation from './getTypeAnnotation'; -import recast from 'recast'; import type { FlowTypeDescriptor } from '../types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; type MethodParameter = { name: string, @@ -46,14 +44,14 @@ function getMethodParamsDoc(methodPath) { functionExpression.get('params').each(paramPath => { let type = null; const typePath = getTypeAnnotation(paramPath); - if (typePath && types.Flow.check(typePath.node)) { + if (typePath && t.Flow.check(typePath.node)) { type = getFlowType(typePath); - if (types.GenericTypeAnnotation.check(typePath.node)) { + if (t.GenericTypeAnnotation.check(typePath.node)) { type.alias = typePath.node.id.name; } } else if (typePath) { type = getTSType(typePath); - if (types.TSTypeReference.check(typePath.node)) { + if (t.TSTypeReference.check(typePath.node)) { type.alias = typePath.node.typeName.name; } } @@ -76,7 +74,7 @@ function getMethodReturnDoc(methodPath) { if (functionExpression.node.returnType) { const returnType = getTypeAnnotation(functionExpression.get('returnType')); - if (returnType && types.Flow.check(returnType.node)) { + if (returnType && t.Flow.check(returnType.node)) { return { type: getFlowType(returnType) }; } else if (returnType) { return { type: getTSType(returnType) }; diff --git a/src/utils/getNameOrValue.js b/src/utils/getNameOrValue.js index f70e7d61a04..088ee8827a1 100644 --- a/src/utils/getNameOrValue.js +++ b/src/utils/getNameOrValue.js @@ -7,11 +7,9 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * If node is an Identifier, it returns its name. If it is a literal, it returns @@ -20,9 +18,9 @@ const { export default function getNameOrValue(path: NodePath, raw?: boolean): string { const node = path.node; switch (node.type) { - case types.Identifier.name: + case t.Identifier.name: return node.name; - case types.Literal.name: + case t.Literal.name: return raw ? node.raw : node.value; default: throw new TypeError('Argument must be an Identifier or a Literal'); diff --git a/src/utils/getParameterName.js b/src/utils/getParameterName.js index 1244142a4ae..9e171eb105c 100644 --- a/src/utils/getParameterName.js +++ b/src/utils/getParameterName.js @@ -7,24 +7,21 @@ * @flow */ -import recast from 'recast'; - +import types from 'ast-types'; import printValue from './printValue'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; export default function getParameterName(parameterPath: NodePath): string { switch (parameterPath.node.type) { - case types.Identifier.name: + case t.Identifier.name: return parameterPath.node.name; - case types.AssignmentPattern.name: + case t.AssignmentPattern.name: return getParameterName(parameterPath.get('left')); - case types.ObjectPattern.name: - case types.ArrayPattern.name: + case t.ObjectPattern.name: + case t.ArrayPattern.name: return printValue(parameterPath); - case types.RestElement.name: + case t.RestElement.name: return '...' + getParameterName(parameterPath.get('argument')); default: throw new TypeError( diff --git a/src/utils/getPropType.js b/src/utils/getPropType.js index 2eb4dc97f6f..6e8d0a03cce 100644 --- a/src/utils/getPropType.js +++ b/src/utils/getPropType.js @@ -9,36 +9,34 @@ /*eslint no-use-before-define: 0*/ +import types from 'ast-types'; import { getDocblock } from '../utils/docblock'; import getMembers from './getMembers'; import getPropertyName from './getPropertyName'; import isRequiredPropType from '../utils/isRequiredPropType'; import printValue from './printValue'; -import recast from 'recast'; import resolveToValue from './resolveToValue'; import resolveObjectKeysToArray from './resolveObjectKeysToArray'; import resolveObjectValuesToArray from './resolveObjectValuesToArray'; import type { PropTypeDescriptor, PropDescriptor } from '../types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function getEnumValues(path) { const values = []; path.get('elements').each(function(elementPath) { - if (types.SpreadElement.check(elementPath.node)) { + if (t.SpreadElement.check(elementPath.node)) { const value = resolveToValue(elementPath.get('argument')); - if (types.ArrayExpression.check(value.node)) { + if (t.ArrayExpression.check(value.node)) { // if the SpreadElement resolved to an Array, add all their elements too return values.push(...getEnumValues(value)); } else { // otherwise we'll just print the SpreadElement itself return values.push({ value: printValue(elementPath), - computed: !types.Literal.check(elementPath.node), + computed: !t.Literal.check(elementPath.node), }); } } @@ -47,7 +45,7 @@ function getEnumValues(path) { const value = resolveToValue(elementPath); return values.push({ value: printValue(value), - computed: !types.Literal.check(value.node), + computed: !t.Literal.check(value.node), }); }); @@ -57,7 +55,7 @@ function getEnumValues(path) { function getPropTypeOneOf(argumentPath) { const type: PropTypeDescriptor = { name: 'enum' }; let value = resolveToValue(argumentPath); - if (!types.ArrayExpression.check(value.node)) { + if (!t.ArrayExpression.check(value.node)) { value = resolveObjectKeysToArray(value) || resolveObjectValuesToArray(value); if (value) { @@ -75,7 +73,7 @@ function getPropTypeOneOf(argumentPath) { function getPropTypeOneOfType(argumentPath) { const type: PropTypeDescriptor = { name: 'union' }; - if (!types.ArrayExpression.check(argumentPath.node)) { + if (!t.ArrayExpression.check(argumentPath.node)) { type.computed = true; type.value = printValue(argumentPath); } else { @@ -134,14 +132,14 @@ function getPropTypeObjectOf(argumentPath) { */ function getPropTypeShapish(name, argumentPath) { const type: PropTypeDescriptor = { name }; - if (!types.ObjectExpression.check(argumentPath.node)) { + if (!t.ObjectExpression.check(argumentPath.node)) { argumentPath = resolveToValue(argumentPath); } - if (types.ObjectExpression.check(argumentPath.node)) { + if (t.ObjectExpression.check(argumentPath.node)) { const value = {}; argumentPath.get('properties').each(function(propertyPath) { - if (propertyPath.get('type').value === types.SpreadElement.name) { + if (propertyPath.get('type').value === t.SpreadElement.name) { // It is impossible to resolve a name for a spread element return; } @@ -213,9 +211,9 @@ export default function getPropType(path: NodePath): PropTypeDescriptor { getMembers(path, true).some(member => { const node = member.path.node; let name; - if (types.Literal.check(node)) { + if (t.Literal.check(node)) { name = node.value; - } else if (types.Identifier.check(node) && !member.computed) { + } else if (t.Identifier.check(node) && !member.computed) { name = node.name; } if (name) { @@ -230,11 +228,11 @@ export default function getPropType(path: NodePath): PropTypeDescriptor { }); if (!descriptor) { const node = path.node; - if (types.Identifier.check(node) && simplePropTypes.includes(node.name)) { + if (t.Identifier.check(node) && simplePropTypes.includes(node.name)) { descriptor = { name: node.name }; } else if ( - types.CallExpression.check(node) && - types.Identifier.check(node.callee) && + t.CallExpression.check(node) && + t.Identifier.check(node.callee) && propTypes.hasOwnProperty(node.callee.name) ) { descriptor = propTypes[node.callee.name](path.get('arguments', 0)); diff --git a/src/utils/getPropertyName.js b/src/utils/getPropertyName.js index 595591a848d..79f305efe88 100644 --- a/src/utils/getPropertyName.js +++ b/src/utils/getPropertyName.js @@ -7,13 +7,11 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import getNameOrValue from './getNameOrValue'; import resolveToValue from './resolveToValue'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; export const COMPUTED_PREFIX = '@computed#'; @@ -23,20 +21,17 @@ export const COMPUTED_PREFIX = '@computed#'; * returns the value of the literal or name of the identifier. */ export default function getPropertyName(propertyPath: NodePath): ?string { - if (types.ObjectTypeSpreadProperty.check(propertyPath.node)) { + if (t.ObjectTypeSpreadProperty.check(propertyPath.node)) { return getNameOrValue(propertyPath.get('argument').get('id'), false); } else if (propertyPath.node.computed) { const key = propertyPath.get('key'); // Try to resolve variables and member expressions - if ( - types.Identifier.check(key.node) || - types.MemberExpression.check(key.node) - ) { + if (t.Identifier.check(key.node) || t.MemberExpression.check(key.node)) { const value = resolveToValue(key).node; if ( - types.Literal.check(value) && + t.Literal.check(value) && (typeof value.value === 'string' || typeof value.value === 'number') ) { return `${value.value}`; @@ -44,12 +39,12 @@ export default function getPropertyName(propertyPath: NodePath): ?string { } // generate name for identifier - if (types.Identifier.check(key.node)) { + if (t.Identifier.check(key.node)) { return `${COMPUTED_PREFIX}${key.node.name}`; } if ( - types.Literal.check(key.node) && + t.Literal.check(key.node) && (typeof key.node.value === 'string' || typeof key.node.value === 'number') ) { return `${key.node.value}`; diff --git a/src/utils/getPropertyValuePath.js b/src/utils/getPropertyValuePath.js index 1b6852e9241..f8440d55060 100644 --- a/src/utils/getPropertyValuePath.js +++ b/src/utils/getPropertyValuePath.js @@ -7,12 +7,10 @@ * @flow */ +import types from 'ast-types'; import getPropertyName from './getPropertyName'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Given an ObjectExpression, this function returns the path of the value of @@ -22,7 +20,7 @@ export default function getPropertyValuePath( path: NodePath, propertyName: string, ): ?NodePath { - types.ObjectExpression.assert(path.node); + t.ObjectExpression.assert(path.node); return path .get('properties') diff --git a/src/utils/getTSType.js b/src/utils/getTSType.js index 9c41b4f1e8a..5c3f95bb652 100644 --- a/src/utils/getTSType.js +++ b/src/utils/getTSType.js @@ -6,9 +6,10 @@ * * @flow */ + +import types from 'ast-types'; import getPropertyName from './getPropertyName'; import printValue from './printValue'; -import recast from 'recast'; import getTypeAnnotation from '../utils/getTypeAnnotation'; import resolveToValue from '../utils/resolveToValue'; import { resolveObjectToNameArray } from '../utils/resolveObjectKeysToArray'; @@ -16,16 +17,15 @@ import getTypeParameters, { type TypeParameters, } from '../utils/getTypeParameters'; import type { - FlowTypeDescriptor, FlowElementsType, - FlowFunctionSignatureType, FlowFunctionArgumentType, FlowObjectSignatureType, + FlowSimpleType, + FlowTypeDescriptor, + TSFunctionSignatureType, } from '../types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; const tsTypes = { TSAnyKeyword: 'any', @@ -71,7 +71,7 @@ function handleTSTypeReference( typeParams: ?TypeParameters, ): ?FlowTypeDescriptor { let type: FlowTypeDescriptor; - if (types.TSQualifiedName.check(path.node.typeName)) { + if (t.TSQualifiedName.check(path.node.typeName)) { const typeName = path.get('typeName'); if (typeName.node.left.name === 'React') { @@ -144,19 +144,23 @@ function handleTSTypeLiteral( path.get('members').each(param => { if ( - types.TSPropertySignature.check(param.node) || - types.TSMethodSignature.check(param.node) + t.TSPropertySignature.check(param.node) || + t.TSMethodSignature.check(param.node) ) { + const propName = getPropertyName(param); + if (!propName) { + return; + } type.signature.properties.push({ - key: getPropertyName(param), + key: propName, value: getTSTypeWithRequirements( param.get('typeAnnotation'), typeParams, ), }); - } else if (types.TSCallSignatureDeclaration.check(param.node)) { + } else if (t.TSCallSignatureDeclaration.check(param.node)) { type.signature.constructor = handleTSFunctionType(param, typeParams); - } else if (types.TSIndexSignature.check(param.node)) { + } else if (t.TSIndexSignature.check(param.node)) { type.signature.properties.push({ key: getTSTypeWithResolvedTypes( param @@ -176,7 +180,7 @@ function handleTSTypeLiteral( return type; } -function handleTSInterfaceDeclaration(path: NodePath): FlowElementsType { +function handleTSInterfaceDeclaration(path: NodePath): FlowSimpleType { // Interfaces are handled like references which would be documented separately, // rather than inlined like type aliases. return { @@ -213,8 +217,8 @@ function handleTSIntersectionType( function handleTSFunctionType( path: NodePath, typeParams: ?TypeParameters, -): FlowFunctionSignatureType { - const type: FlowFunctionSignatureType = { +): TSFunctionSignatureType { + const type: TSFunctionSignatureType = { name: 'signature', type: 'function', raw: printValue(path), @@ -233,7 +237,7 @@ function handleTSFunctionType( name: param.node.name || '', type: typeAnnotation ? getTSTypeWithResolvedTypes(typeAnnotation, typeParams) - : null, + : undefined, }; if (param.node.name === 'this') { @@ -284,13 +288,13 @@ function handleTSTypeQuery( return { name: path.node.exprName.name }; } -function handleTSTypeOperator(path: NodePath): FlowTypeDescriptor { +function handleTSTypeOperator(path: NodePath): ?FlowTypeDescriptor { if (path.node.operator !== 'keyof') { return null; } let value = path.get('typeAnnotation'); - if (types.TSTypeQuery.check(value.node)) { + if (t.TSTypeQuery.check(value.node)) { value = value.get('exprName'); } else if (value.node.id) { value = value.get('id'); @@ -299,8 +303,8 @@ function handleTSTypeOperator(path: NodePath): FlowTypeDescriptor { const resolvedPath = resolveToValue(value); if ( resolvedPath && - (types.ObjectExpression.check(resolvedPath.node) || - types.TSTypeLiteral.check(resolvedPath.node)) + (t.ObjectExpression.check(resolvedPath.node) || + t.TSTypeLiteral.check(resolvedPath.node)) ) { const keys = resolveObjectToNameArray(resolvedPath, true); @@ -320,13 +324,13 @@ function getTSTypeWithResolvedTypes( path: NodePath, typeParams: ?TypeParameters, ): FlowTypeDescriptor { - if (types.TSTypeAnnotation.check(path.node)) { + if (t.TSTypeAnnotation.check(path.node)) { path = path.get('typeAnnotation'); } const node = path.node; let type: ?FlowTypeDescriptor; - const isTypeAlias = types.TSTypeAliasDeclaration.check(path.parentPath.node); + const isTypeAlias = t.TSTypeAliasDeclaration.check(path.parentPath.node); // When we see a typealias mark it as visited so that the next // call of this function does not run into an endless loop @@ -345,7 +349,7 @@ function getTSTypeWithResolvedTypes( if (node.type in tsTypes) { type = { name: tsTypes[node.type] }; - } else if (types.TSLiteralType.check(node)) { + } else if (t.TSLiteralType.check(node)) { type = { name: 'literal', value: node.literal.raw || `${node.literal.value}`, diff --git a/src/utils/getTypeAnnotation.js b/src/utils/getTypeAnnotation.js index 3baa4d610c5..cb2fbf7875b 100644 --- a/src/utils/getTypeAnnotation.js +++ b/src/utils/getTypeAnnotation.js @@ -7,10 +7,9 @@ * @flow */ -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +import types from 'ast-types'; + +const { namedTypes: t } = types; function hasTypeAnnotation(path: NodePath): boolean { return !!path.node.typeAnnotation; @@ -28,8 +27,8 @@ export default function getTypeAnnotation(path: NodePath): ?NodePath { resultPath = resultPath.get('typeAnnotation'); } while ( hasTypeAnnotation(resultPath) && - !types.FlowType.check(resultPath.node) && - !types.TSType.check(resultPath.node) + !t.FlowType.check(resultPath.node) && + !t.TSType.check(resultPath.node) ); return resultPath; diff --git a/src/utils/isExportsOrModuleAssignment.js b/src/utils/isExportsOrModuleAssignment.js index 71ab871c90f..34e086ee653 100644 --- a/src/utils/isExportsOrModuleAssignment.js +++ b/src/utils/isExportsOrModuleAssignment.js @@ -7,24 +7,22 @@ * @flow */ +import types from 'ast-types'; import * as expressionTo from './expressionTo'; -import recast from 'recast'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is of form `exports.foo = ...;` or * `modules.exports = ...;`. */ export default function isExportsOrModuleAssignment(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } if ( - !types.AssignmentExpression.check(path.node) || - !types.MemberExpression.check(path.node.left) + !t.AssignmentExpression.check(path.node) || + !t.MemberExpression.check(path.node.left) ) { return false; } diff --git a/src/utils/isReactChildrenElementCall.js b/src/utils/isReactChildrenElementCall.js index 9186acb700f..9b00481e860 100644 --- a/src/utils/isReactChildrenElementCall.js +++ b/src/utils/isReactChildrenElementCall.js @@ -7,21 +7,19 @@ * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is a function call of the form * `React.Children.only(...)`. */ export default function isReactChildrenElementCall(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } diff --git a/src/utils/isReactCloneElementCall.js b/src/utils/isReactCloneElementCall.js index 4bf5fe0c509..3b2094c89ae 100644 --- a/src/utils/isReactCloneElementCall.js +++ b/src/utils/isReactCloneElementCall.js @@ -7,21 +7,19 @@ * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is a function call of the form * `React.createElement(...)`. */ export default function isReactCloneElementCall(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } diff --git a/src/utils/isReactComponentClass.js b/src/utils/isReactComponentClass.js index 1df88a5a7c5..201524ac0d0 100644 --- a/src/utils/isReactComponentClass.js +++ b/src/utils/isReactComponentClass.js @@ -7,20 +7,18 @@ * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; import resolveToValue from './resolveToValue'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function isRenderMethod(node) { const isProperty = node.type === 'ClassProperty'; return ( - (types.MethodDefinition.check(node) || isProperty) && + (t.MethodDefinition.check(node) || isProperty) && !node.computed && !node.static && (node.kind === '' || node.kind === 'method' || isProperty) && @@ -34,10 +32,7 @@ function isRenderMethod(node) { */ export default function isReactComponentClass(path: NodePath): boolean { const node = path.node; - if ( - !types.ClassDeclaration.check(node) && - !types.ClassExpression.check(node) - ) { + if (!t.ClassDeclaration.check(node) && !t.ClassExpression.check(node)) { return false; } diff --git a/src/utils/isReactComponentMethod.js b/src/utils/isReactComponentMethod.js index c4b2a4fcc78..43b6f078a9a 100644 --- a/src/utils/isReactComponentMethod.js +++ b/src/utils/isReactComponentMethod.js @@ -7,13 +7,10 @@ * @flow */ -import recast from 'recast'; - +import types from 'ast-types'; import getPropertyName from './getPropertyName'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; const componentMethods = [ 'componentDidMount', @@ -42,8 +39,8 @@ const componentMethods = [ */ export default function(methodPath: NodePath): boolean { if ( - !types.MethodDefinition.check(methodPath.node) && - !types.Property.check(methodPath.node) + !t.MethodDefinition.check(methodPath.node) && + !t.Property.check(methodPath.node) ) { return false; } diff --git a/src/utils/isReactCreateClassCall.js b/src/utils/isReactCreateClassCall.js index 443dc91ec28..484af2f30a8 100644 --- a/src/utils/isReactCreateClassCall.js +++ b/src/utils/isReactCreateClassCall.js @@ -7,21 +7,19 @@ * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is a function call of the form * `React.createClass(...)`. */ function isReactCreateClassCallBuiltIn(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } @@ -40,7 +38,7 @@ function isReactCreateClassCallBuiltIn(path: NodePath): boolean { * ``` */ function isReactCreateClassCallModular(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } diff --git a/src/utils/isReactCreateElementCall.js b/src/utils/isReactCreateElementCall.js index 2937da1c743..0bb81cb5dcb 100644 --- a/src/utils/isReactCreateElementCall.js +++ b/src/utils/isReactCreateElementCall.js @@ -7,21 +7,19 @@ * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is a function call of the form * `React.createElement(...)`. */ export default function isReactCreateElementCall(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } diff --git a/src/utils/isReactForwardRefCall.js b/src/utils/isReactForwardRefCall.js index f6b38221da9..56d1d936e10 100644 --- a/src/utils/isReactForwardRefCall.js +++ b/src/utils/isReactForwardRefCall.js @@ -6,21 +6,19 @@ * * @flow */ +import types from 'ast-types'; import isReactModuleName from './isReactModuleName'; import match from './match'; -import recast from 'recast'; import resolveToModule from './resolveToModule'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true if the expression is a function call of the form * `React.forwardRef(...)`. */ export default function isReactForwardRefCall(path: NodePath): boolean { - if (types.ExpressionStatement.check(path.node)) { + if (t.ExpressionStatement.check(path.node)) { path = path.get('expression'); } diff --git a/src/utils/isStatelessComponent.js b/src/utils/isStatelessComponent.js index de9a9c14b2e..3e35ee7e1d7 100644 --- a/src/utils/isStatelessComponent.js +++ b/src/utils/isStatelessComponent.js @@ -7,18 +7,16 @@ * @flow */ +import types from 'ast-types'; import getPropertyValuePath from './getPropertyValuePath'; import isReactComponentClass from './isReactComponentClass'; import isReactCreateClassCall from './isReactCreateClassCall'; import isReactCreateElementCall from './isReactCreateElementCall'; import isReactCloneElementCall from './isReactCloneElementCall'; import isReactChildrenElementCall from './isReactChildrenElementCall'; -import recast from 'recast'; import resolveToValue from './resolveToValue'; -const { - types: { namedTypes: types }, -} = recast; +const { visit, namedTypes: t } = types; const validPossibleStatelessComponentTypes = [ 'Property', @@ -84,17 +82,17 @@ function resolvesToJSXElementOrReactCall(path) { if (calleeValue.node.type === 'MemberExpression') { if (calleeValue.get('object').node.type === 'Identifier') { resolvedValue = resolveToValue(calleeValue.get('object')); - } else if (types.MemberExpression.check(calleeValue.node)) { + } else if (t.MemberExpression.check(calleeValue.node)) { do { calleeValue = calleeValue.get('object'); namesToResolve.unshift(calleeValue.get('property')); - } while (types.MemberExpression.check(calleeValue.node)); + } while (t.MemberExpression.check(calleeValue.node)); resolvedValue = resolveToValue(calleeValue.get('object')); } } - if (resolvedValue && types.ObjectExpression.check(resolvedValue.node)) { + if (resolvedValue && t.ObjectExpression.check(resolvedValue.node)) { const resolvedMemberExpression = namesToResolve.reduce( (result, nodePath) => { if (!nodePath) { @@ -103,7 +101,7 @@ function resolvesToJSXElementOrReactCall(path) { if (result) { result = getPropertyValuePath(result, nodePath.node.name); - if (result && types.Identifier.check(result.node)) { + if (result && t.Identifier.check(result.node)) { return resolveToValue(result); } } @@ -142,7 +140,7 @@ function returnsJSXElementOrReactCall(path) { scope = path.get('value').scope; } - recast.visit(path, { + visit(path, { visitReturnStatement(returnPath) { // Only check return statements which are part of the checked function scope if (returnPath.scope !== scope) return false; diff --git a/src/utils/isUnreachableFlowType.js b/src/utils/isUnreachableFlowType.js index 68350fdc6ca..c15e9fab5ce 100644 --- a/src/utils/isUnreachableFlowType.js +++ b/src/utils/isUnreachableFlowType.js @@ -7,11 +7,9 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Returns true of the path is an unreachable TypePath @@ -19,8 +17,8 @@ const { export default (path: NodePath): boolean => { return ( !path || - types.Identifier.check(path.node) || - types.ImportDeclaration.check(path.node) || - types.CallExpression.check(path.node) + t.Identifier.check(path.node) || + t.ImportDeclaration.check(path.node) || + t.CallExpression.check(path.node) ); }; diff --git a/src/utils/normalizeClassDefinition.js b/src/utils/normalizeClassDefinition.js index 53b7a31fe65..53e11bfa18d 100644 --- a/src/utils/normalizeClassDefinition.js +++ b/src/utils/normalizeClassDefinition.js @@ -7,13 +7,12 @@ * @flow */ +import types from 'ast-types'; import getMemberExpressionRoot from '../utils/getMemberExpressionRoot'; import getMembers from '../utils/getMembers'; -import recast from 'recast'; -const { - types: { namedTypes: types, builders }, -} = recast; +const { builders, visit, namedTypes: t } = types; + const ignore = () => false; /** @@ -39,26 +38,26 @@ export default function normalizeClassDefinition( classDefinition: NodePath, ): void { let variableName; - if (types.ClassDeclaration.check(classDefinition.node)) { + if (t.ClassDeclaration.check(classDefinition.node)) { // Class declarations don't have an id, e.g.: `export default class extends React.Component {}` if (classDefinition.node.id) { variableName = classDefinition.node.id.name; } - } else if (types.ClassExpression.check(classDefinition.node)) { + } else if (t.ClassExpression.check(classDefinition.node)) { let { parentPath } = classDefinition; while ( parentPath.node !== classDefinition.scope.node && - !types.BlockStatement.check(parentPath.node) + !t.BlockStatement.check(parentPath.node) ) { if ( - types.VariableDeclarator.check(parentPath.node) && - types.Identifier.check(parentPath.node.id) + t.VariableDeclarator.check(parentPath.node) && + t.Identifier.check(parentPath.node.id) ) { variableName = parentPath.node.id.name; break; } else if ( - types.AssignmentExpression.check(parentPath.node) && - types.Identifier.check(parentPath.node.left) + t.AssignmentExpression.check(parentPath.node) && + t.Identifier.check(parentPath.node.left) ) { variableName = parentPath.node.left.name; break; @@ -72,17 +71,17 @@ export default function normalizeClassDefinition( } const scopeRoot = classDefinition.scope; - recast.visit(scopeRoot.node, { + visit(scopeRoot.node, { visitFunction: ignore, visitClassDeclaration: ignore, visitClassExpression: ignore, visitForInStatement: ignore, visitForStatement: ignore, visitAssignmentExpression: function(path) { - if (types.MemberExpression.check(path.node.left)) { + if (t.MemberExpression.check(path.node.left)) { const first = getMemberExpressionRoot(path.get('left')); if ( - types.Identifier.check(first.node) && + t.Identifier.check(first.node) && first.node.name === variableName ) { const [member] = getMembers(path.get('left')); diff --git a/src/utils/printValue.js b/src/utils/printValue.js index 5523db4348b..6ec664e68ed 100644 --- a/src/utils/printValue.js +++ b/src/utils/printValue.js @@ -7,15 +7,47 @@ * @flow */ -import recast from 'recast'; +import strip from 'strip-indent'; + +function deindent(code: string): string { + const firstNewLine = code.indexOf('\n'); + + return ( + code.slice(0, firstNewLine + 1) + + // remove indentation from all lines except first. + strip(code.slice(firstNewLine + 1)) + ); +} + +function getSrcFromAst(path: NodePath): string { + do { + if (path.node.type === 'File') { + return path.node.__src; + } + path = path.parentPath; + } while (path != null); + + throw new Error('Could not find source attached to File node'); +} /** * Prints the given path without leading or trailing comments. */ export default function printValue(path: NodePath): string { - if (path.node.comments) { - path.node.comments.length = 0; + if (path.node.start == null) { + // This only happens when we use AST builders to create nodes that do not actually + // exist in the source (e.g. when resolving Object.keys()). We might need to enhance + // this if we start using builders from `ast-types` more. + if (path.node.type === 'Literal') { + return `"${path.node.value}"`; + } + throw new Error( + `Cannot print raw value for type '${ + path.node.type + }'. Please report this with an example at https://github.com/reactjs/react-docgen/issues`, + ); } + const src = getSrcFromAst(path); - return recast.print(path).code; + return deindent(src.slice(path.node.start, path.node.end)); } diff --git a/src/utils/resolveExportDeclaration.js b/src/utils/resolveExportDeclaration.js index ae7c535d075..705027ed9ff 100644 --- a/src/utils/resolveExportDeclaration.js +++ b/src/utils/resolveExportDeclaration.js @@ -7,22 +7,19 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import resolveToValue from './resolveToValue'; -const { - types: { namedTypes: _types }, -} = recast; +const { namedTypes: t } = types; export default function resolveExportDeclaration( path: NodePath, - types: Object = _types, ): Array { const definitions = []; if (path.node.default) { definitions.push(path.get('declaration')); } else if (path.node.declaration) { - if (types.VariableDeclaration.check(path.node.declaration)) { + if (t.VariableDeclaration.check(path.node.declaration)) { path .get('declaration', 'declarations') .each(declarator => definitions.push(declarator)); diff --git a/src/utils/resolveGenericTypeAnnotation.js b/src/utils/resolveGenericTypeAnnotation.js index 9d7934c79fc..f1e7775b9e8 100644 --- a/src/utils/resolveGenericTypeAnnotation.js +++ b/src/utils/resolveGenericTypeAnnotation.js @@ -7,14 +7,12 @@ * @flow */ +import types from 'ast-types'; import isUnreachableFlowType from '../utils/isUnreachableFlowType'; -import recast from 'recast'; import resolveToValue from '../utils/resolveToValue'; import { unwrapUtilityType } from './flowUtilityTypes'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; function tryResolveGenericTypeAnnotation(path: NodePath): ?NodePath { let typePath = unwrapUtilityType(path); @@ -22,9 +20,9 @@ function tryResolveGenericTypeAnnotation(path: NodePath): ?NodePath { if (typePath.node.id) { idPath = typePath.get('id'); - } else if (types.TSTypeReference.check(typePath.node)) { + } else if (t.TSTypeReference.check(typePath.node)) { idPath = typePath.get('typeName'); - } else if (types.TSExpressionWithTypeArguments.check(typePath.node)) { + } else if (t.TSExpressionWithTypeArguments.check(typePath.node)) { idPath = typePath.get('expression'); } @@ -34,9 +32,9 @@ function tryResolveGenericTypeAnnotation(path: NodePath): ?NodePath { return; } - if (types.TypeAlias.check(typePath.node)) { + if (t.TypeAlias.check(typePath.node)) { return tryResolveGenericTypeAnnotation(typePath.get('right')); - } else if (types.TSTypeAliasDeclaration.check(typePath.node)) { + } else if (t.TSTypeAliasDeclaration.check(typePath.node)) { return tryResolveGenericTypeAnnotation(typePath.get('typeAnnotation')); } diff --git a/src/utils/resolveHOC.js b/src/utils/resolveHOC.js index 5fce189d4ad..31e0628c82b 100644 --- a/src/utils/resolveHOC.js +++ b/src/utils/resolveHOC.js @@ -7,13 +7,11 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import isReactCreateClassCall from './isReactCreateClassCall'; import isReactForwardRefCall from './isReactForwardRefCall'; -const { - types: { NodePath, namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * If the path is a call expression, it recursively resolves to the @@ -24,7 +22,7 @@ const { export default function resolveHOC(path: NodePath): NodePath { const node = path.node; if ( - types.CallExpression.check(node) && + t.CallExpression.check(node) && !isReactCreateClassCall(path) && !isReactForwardRefCall(path) ) { diff --git a/src/utils/resolveObjectKeysToArray.js b/src/utils/resolveObjectKeysToArray.js index a4a348f9384..c4036a67767 100644 --- a/src/utils/resolveObjectKeysToArray.js +++ b/src/utils/resolveObjectKeysToArray.js @@ -7,39 +7,37 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import resolveToValue from './resolveToValue'; -const { - types: { ASTNode, NodePath, builders, namedTypes: types }, -} = recast; +const { ASTNode, NodePath, builders, namedTypes: t } = types; function isObjectKeysCall(node: ASTNode): boolean { return ( - types.CallExpression.check(node) && + t.CallExpression.check(node) && node.arguments.length === 1 && - types.MemberExpression.check(node.callee) && - types.Identifier.check(node.callee.object) && + t.MemberExpression.check(node.callee) && + t.Identifier.check(node.callee.object) && node.callee.object.name === 'Object' && - types.Identifier.check(node.callee.property) && + t.Identifier.check(node.callee.property) && node.callee.property.name === 'keys' ); } function isWhitelistedObjectProperty(prop) { return ( - (types.Property.check(prop) && - ((types.Identifier.check(prop.key) && !prop.computed) || - types.Literal.check(prop.key))) || - types.SpreadElement.check(prop) + (t.Property.check(prop) && + ((t.Identifier.check(prop.key) && !prop.computed) || + t.Literal.check(prop.key))) || + t.SpreadElement.check(prop) ); } function isWhiteListedObjectTypeProperty(prop) { return ( - types.ObjectTypeProperty.check(prop) || - types.ObjectTypeSpreadProperty.check(prop) || - types.TSPropertySignature.check(prop) + t.ObjectTypeProperty.check(prop) || + t.ObjectTypeSpreadProperty.check(prop) || + t.TSPropertySignature.check(prop) ); } @@ -49,16 +47,16 @@ export function resolveObjectToNameArray( raw: boolean = false, ): ?Array { if ( - (types.ObjectExpression.check(object.value) && + (t.ObjectExpression.check(object.value) && object.value.properties.every(isWhitelistedObjectProperty)) || - (types.ObjectTypeAnnotation.check(object.value) && + (t.ObjectTypeAnnotation.check(object.value) && object.value.properties.every(isWhiteListedObjectTypeProperty)) || - (types.TSTypeLiteral.check(object.value) && + (t.TSTypeLiteral.check(object.value) && object.value.members.every(isWhiteListedObjectTypeProperty)) ) { let values = []; let error = false; - const properties = types.TSTypeLiteral.check(object.value) + const properties = t.TSTypeLiteral.check(object.value) ? object.get('members') : object.get('properties'); properties.each(propPath => { @@ -66,22 +64,22 @@ export function resolveObjectToNameArray( const prop = propPath.value; if ( - types.Property.check(prop) || - types.ObjectTypeProperty.check(prop) || - types.TSPropertySignature.check(prop) + t.Property.check(prop) || + t.ObjectTypeProperty.check(prop) || + t.TSPropertySignature.check(prop) ) { // Key is either Identifier or Literal const name = prop.key.name || (raw ? prop.key.raw : prop.key.value); values.push(name); } else if ( - types.SpreadElement.check(prop) || - types.ObjectTypeSpreadProperty.check(prop) + t.SpreadElement.check(prop) || + t.ObjectTypeSpreadProperty.check(prop) ) { let spreadObject = resolveToValue(propPath.get('argument')); - if (types.GenericTypeAnnotation.check(spreadObject.value)) { + if (t.GenericTypeAnnotation.check(spreadObject.value)) { const typeAlias = resolveToValue(spreadObject.get('id')); - if (types.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { + if (t.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { spreadObject = resolveToValue(typeAlias.get('right')); } } diff --git a/src/utils/resolveObjectValuesToArray.js b/src/utils/resolveObjectValuesToArray.js index 865e2bf7734..eb224ab51d1 100644 --- a/src/utils/resolveObjectValuesToArray.js +++ b/src/utils/resolveObjectValuesToArray.js @@ -7,44 +7,42 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import resolveToValue from './resolveToValue'; +const { ASTNode, NodePath, builders, namedTypes: t } = types; + type ObjectPropMap = { properties: Array, values: Object, }; -const { - types: { ASTNode, NodePath, builders, namedTypes: types }, -} = recast; - function isObjectValuesCall(node: ASTNode): boolean { return ( - types.CallExpression.check(node) && + t.CallExpression.check(node) && node.arguments.length === 1 && - types.MemberExpression.check(node.callee) && - types.Identifier.check(node.callee.object) && + t.MemberExpression.check(node.callee) && + t.Identifier.check(node.callee.object) && node.callee.object.name === 'Object' && - types.Identifier.check(node.callee.property) && + t.Identifier.check(node.callee.property) && node.callee.property.name === 'values' ); } function isWhitelistedObjectProperty(prop) { return ( - (types.Property.check(prop) && - ((types.Identifier.check(prop.key) && !prop.computed) || - types.Literal.check(prop.key))) || - types.SpreadElement.check(prop) + (t.Property.check(prop) && + ((t.Identifier.check(prop.key) && !prop.computed) || + t.Literal.check(prop.key))) || + t.SpreadElement.check(prop) ); } function isWhiteListedObjectTypeProperty(prop) { return ( - types.ObjectTypeProperty.check(prop) || - types.ObjectTypeSpreadProperty.check(prop) || - types.TSPropertySignature.check(prop) + t.ObjectTypeProperty.check(prop) || + t.ObjectTypeSpreadProperty.check(prop) || + t.TSPropertySignature.check(prop) ); } @@ -54,17 +52,17 @@ export function resolveObjectToPropMap( raw: boolean = false, ): ?ObjectPropMap { if ( - (types.ObjectExpression.check(object.value) && + (t.ObjectExpression.check(object.value) && object.value.properties.every(isWhitelistedObjectProperty)) || - (types.ObjectTypeAnnotation.check(object.value) && + (t.ObjectTypeAnnotation.check(object.value) && object.value.properties.every(isWhiteListedObjectTypeProperty)) || - (types.TSTypeLiteral.check(object.value) && + (t.TSTypeLiteral.check(object.value) && object.value.members.every(isWhiteListedObjectTypeProperty)) ) { const properties = []; let values = {}; let error = false; - const members = types.TSTypeLiteral.check(object.value) + const members = t.TSTypeLiteral.check(object.value) ? object.get('members') : object.get('properties'); members.each(propPath => { @@ -74,9 +72,9 @@ export function resolveObjectToPropMap( if (prop.kind === 'get' || prop.kind === 'set') return; if ( - types.Property.check(prop) || - types.ObjectTypeProperty.check(prop) || - types.TSPropertySignature.check(prop) + t.Property.check(prop) || + t.ObjectTypeProperty.check(prop) || + t.TSPropertySignature.check(prop) ) { // Key is either Identifier or Literal const name = prop.key.name || (raw ? prop.key.raw : prop.key.value); @@ -90,13 +88,13 @@ export function resolveObjectToPropMap( } values[name] = value; } else if ( - types.SpreadElement.check(prop) || - types.ObjectTypeSpreadProperty.check(prop) + t.SpreadElement.check(prop) || + t.ObjectTypeSpreadProperty.check(prop) ) { let spreadObject = resolveToValue(propPath.get('argument')); - if (types.GenericTypeAnnotation.check(spreadObject.value)) { + if (t.GenericTypeAnnotation.check(spreadObject.value)) { const typeAlias = resolveToValue(spreadObject.get('id')); - if (types.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { + if (t.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { spreadObject = resolveToValue(typeAlias.get('right')); } } @@ -124,7 +122,7 @@ export function resolveObjectToPropMap( } /** - * Returns an ArrayExpression which contains all the keys resolved from an object + * Returns an ArrayExpression which contains all the values resolved from an object * * Ignores setters in objects * diff --git a/src/utils/resolveToModule.js b/src/utils/resolveToModule.js index 6b3aa94a74f..9d2ea3855f7 100644 --- a/src/utils/resolveToModule.js +++ b/src/utils/resolveToModule.js @@ -7,13 +7,11 @@ * @flow */ +import types from 'ast-types'; import match from './match'; -import recast from 'recast'; import resolveToValue from './resolveToValue'; -const { - types: { namedTypes: types }, -} = recast; +const { namedTypes: t } = types; /** * Given a path (e.g. call expression, member expression or identifier), @@ -23,30 +21,28 @@ const { export default function resolveToModule(path: NodePath): ?string { const node = path.node; switch (node.type) { - case types.VariableDeclarator.name: + case t.VariableDeclarator.name: if (node.init) { return resolveToModule(path.get('init')); } break; - case types.CallExpression.name: - if ( - match(node.callee, { type: types.Identifier.name, name: 'require' }) - ) { + case t.CallExpression.name: + if (match(node.callee, { type: t.Identifier.name, name: 'require' })) { return node.arguments[0].value; } return resolveToModule(path.get('callee')); - case types.Identifier.name: - case types.JSXIdentifier.name: { + case t.Identifier.name: + case t.JSXIdentifier.name: { const valuePath = resolveToValue(path); if (valuePath !== path) { return resolveToModule(valuePath); } break; } - case types.ImportDeclaration.name: + case t.ImportDeclaration.name: return node.source.value; - case types.MemberExpression.name: - while (path && types.MemberExpression.check(path.node)) { + case t.MemberExpression.name: + while (path && t.MemberExpression.check(path.node)) { path = path.get('object'); } if (path) { diff --git a/src/utils/resolveToValue.js b/src/utils/resolveToValue.js index 4dc3f290b93..73b7e436745 100644 --- a/src/utils/resolveToValue.js +++ b/src/utils/resolveToValue.js @@ -7,33 +7,31 @@ * @flow */ -import recast from 'recast'; +import types from 'ast-types'; import getMemberExpressionRoot from './getMemberExpressionRoot'; import getPropertyValuePath from './getPropertyValuePath'; import { Array as toArray } from './expressionTo'; import { traverseShallow } from './traverse'; -const { - types: { NodePath, builders, namedTypes: types }, -} = recast; +const { namedTypes: t, NodePath, builders } = types; function buildMemberExpressionFromPattern(path: NodePath): ?NodePath { const node = path.node; - if (types.Property.check(node)) { + if (t.Property.check(node)) { const objPath = buildMemberExpressionFromPattern(path.parent); if (objPath) { return new NodePath( builders.memberExpression( objPath.node, node.key, - types.Literal.check(node.key), + t.Literal.check(node.key), ), objPath, ); } - } else if (types.ObjectPattern.check(node)) { + } else if (t.ObjectPattern.check(node)) { return buildMemberExpressionFromPattern(path.parent); - } else if (types.VariableDeclarator.check(node)) { + } else if (t.VariableDeclarator.check(node)) { return path.get('init'); } return null; @@ -47,17 +45,17 @@ function findScopePath(paths: Array, path: NodePath): ?NodePath { const parentPath = resultPath.parent; if ( - types.ImportDefaultSpecifier.check(parentPath.node) || - types.ImportSpecifier.check(parentPath.node) || - types.ImportNamespaceSpecifier.check(parentPath.node) || - types.VariableDeclarator.check(parentPath.node) || - types.TypeAlias.check(parentPath.node) || - types.InterfaceDeclaration.check(parentPath.node) || - types.TSTypeAliasDeclaration.check(parentPath.node) || - types.TSInterfaceDeclaration.check(parentPath.node) + t.ImportDefaultSpecifier.check(parentPath.node) || + t.ImportSpecifier.check(parentPath.node) || + t.ImportNamespaceSpecifier.check(parentPath.node) || + t.VariableDeclarator.check(parentPath.node) || + t.TypeAlias.check(parentPath.node) || + t.InterfaceDeclaration.check(parentPath.node) || + t.TSTypeAliasDeclaration.check(parentPath.node) || + t.TSInterfaceDeclaration.check(parentPath.node) ) { resultPath = parentPath; - } else if (types.Property.check(parentPath.node)) { + } else if (t.Property.check(parentPath.node)) { // must be inside a pattern const memberExpressionPath = buildMemberExpressionFromPattern(parentPath); if (memberExpressionPath) { @@ -85,7 +83,7 @@ function findLastAssignedValue(scope, name) { // Skip anything that is not an assignment to a variable with the // passed name. if ( - !types.Identifier.check(node.left) || + !t.Identifier.check(node.left) || node.left.name !== name || node.operator !== '=' ) { @@ -111,16 +109,16 @@ function findLastAssignedValue(scope, name) { */ export default function resolveToValue(path: NodePath): NodePath { const node = path.node; - if (types.VariableDeclarator.check(node)) { + if (t.VariableDeclarator.check(node)) { if (node.init) { return resolveToValue(path.get('init')); } - } else if (types.MemberExpression.check(node)) { + } else if (t.MemberExpression.check(node)) { const resolved = resolveToValue(getMemberExpressionRoot(path)); - if (types.ObjectExpression.check(resolved.node)) { + if (t.ObjectExpression.check(resolved.node)) { let propertyPath = resolved; for (const propertyName of toArray(path).slice(1)) { - if (propertyPath && types.ObjectExpression.check(propertyPath.node)) { + if (propertyPath && t.ObjectExpression.check(propertyPath.node)) { propertyPath = getPropertyValuePath(propertyPath, propertyName); } if (!propertyPath) { @@ -131,23 +129,23 @@ export default function resolveToValue(path: NodePath): NodePath { return propertyPath; } } else if ( - types.ImportDefaultSpecifier.check(node) || - types.ImportNamespaceSpecifier.check(node) || - types.ImportSpecifier.check(node) + t.ImportDefaultSpecifier.check(node) || + t.ImportNamespaceSpecifier.check(node) || + t.ImportSpecifier.check(node) ) { // go up two levels as first level is only the array of specifiers return path.parentPath.parentPath; - } else if (types.AssignmentExpression.check(node)) { + } else if (t.AssignmentExpression.check(node)) { if (node.operator === '=') { return resolveToValue(path.get('right')); } - } else if (types.TypeCastExpression.check(node)) { + } else if (t.TypeCastExpression.check(node)) { return resolveToValue(path.get('expression')); - } else if (types.Identifier.check(node)) { + } else if (t.Identifier.check(node)) { if ( - (types.ClassDeclaration.check(path.parentPath.node) || - types.ClassExpression.check(path.parentPath.node) || - types.Function.check(path.parentPath.node)) && + (t.ClassDeclaration.check(path.parentPath.node) || + t.ClassExpression.check(path.parentPath.node) || + t.Function.check(path.parentPath.node)) && path.parentPath.get('id') === path ) { return path.parentPath; diff --git a/src/utils/traverse.js b/src/utils/traverse.js index 8e5dd0b87ab..f6d143ef15e 100644 --- a/src/utils/traverse.js +++ b/src/utils/traverse.js @@ -7,9 +7,9 @@ * @flow */ -type Visitor = (path: NodePath) => any; +import types from 'ast-types'; -import recast from 'recast'; +type Visitor = (path: NodePath) => any; /** * A helper function that doesn't traverse into nested blocks / statements by @@ -19,7 +19,7 @@ export function traverseShallow( ast: ASTNode, visitors: { [key: string]: Visitor }, ): void { - recast.visit(ast, { ...defaultVisitors, ...visitors }); + types.visit(ast, { ...defaultVisitors, ...visitors }); } const ignore = () => false; @@ -36,7 +36,6 @@ const defaultVisitors = { visitForStatement: ignore, visitForInStatement: ignore, visitForOfStatement: ignore, - visitExportDeclaration: ignore, visitExportNamedDeclaration: ignore, visitExportDefaultDeclaration: ignore, visitConditionalExpression: ignore, diff --git a/tests/NodePathSerializer.js b/tests/NodePathSerializer.js new file mode 100644 index 00000000000..648d6b83ea6 --- /dev/null +++ b/tests/NodePathSerializer.js @@ -0,0 +1,11 @@ +const { NodePath } = require('ast-types'); + +module.exports = { + print(val, serialize) { + return serialize(val.node); + }, + + test(val) { + return val && val instanceof NodePath; + }, +}; diff --git a/tests/setupTestFramework.js b/tests/setupTestFramework.js index 0aad6e09f69..be09e4733e0 100644 --- a/tests/setupTestFramework.js +++ b/tests/setupTestFramework.js @@ -1,6 +1,6 @@ /* eslint-env jest */ -const recast = require('recast'); +const types = require('ast-types'); const diff = require('jest-diff'); const utils = require('jest-matcher-utils'); @@ -21,12 +21,12 @@ const matchers = { // the next Node it finds even if value is an array const receivedNode = received.value; let expectedNode = expected; - if (expected instanceof recast.types.NodePath) { + if (expected instanceof types.NodePath) { expectedNode = expected.value; } return { - pass: recast.types.astNodesAreEquivalent(receivedNode, expectedNode), + pass: types.astNodesAreEquivalent(receivedNode, expectedNode), message: () => { const diffString = diff(expectedNode, receivedNode); diff --git a/tests/utils.js b/tests/utils.js index 216261c146f..6ac8e2a59eb 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -2,9 +2,11 @@ * Helper methods for tests. */ -import _recast from 'recast'; +import types from 'ast-types'; import buildParser from '../src/babelParser'; +const { NodePath } = types; + function stringify(value) { if (Array.isArray(value)) { return value.join('\n'); @@ -12,21 +14,25 @@ function stringify(value) { return value; } +export function getParser(options = {}) { + return buildParser(options); +} /** * Returns a NodePath to the program node of the passed node */ -export function parse(src, recast = _recast, options = {}) { - return new recast.types.NodePath( - recast.parse(stringify(src), { parser: buildParser(options) }).program, - ); +export function parse(src, options = {}) { + const ast = getParser(options).parse(stringify(src)); + ast.__src = src; + + return new NodePath(ast).get('program'); } -export function statement(src, recast = _recast, options) { - return parse(src, recast, options).get('body', 0); +export function statement(src, options) { + return parse(src, options).get('body', 0); } -export function expression(src, recast = _recast, options) { - return statement('(' + src + ')', recast, options).get('expression'); +export function expression(src, options) { + return statement('(' + src + ')', options).get('expression'); } /** diff --git a/yarn.lock b/yarn.lock index 043c346c98b..aea2ba01ca1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -891,7 +891,7 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -ast-types@0.12.4: +ast-types@^0.12.4: version "0.12.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.12.4.tgz#71ce6383800f24efc9a1a3308f3a6e420a0974d1" integrity sha512-ky/YVYCbtVAS8TdMIaTiPFHwEpRB5z1hctepJplTr3UW5q8TDrpIMCILyk8pmLxGtn2KCtC/lSn7zOsaI7nzDw== @@ -1007,6 +1007,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik= + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + binary-extensions@^1.0.0: version "1.13.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.0.tgz#9523e001306a32444b907423f1de2164222f6ab1" @@ -1186,6 +1194,13 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" +cli-table@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" + integrity sha1-9TsFJmqLGguTSz0IIebi3FkUriM= + dependencies: + colors "1.0.3" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1230,6 +1245,11 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2: version "1.3.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" @@ -1610,7 +1630,7 @@ esprima@^3.1.3: resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2946,7 +2966,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -3525,6 +3545,11 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +platform@^1.3.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" + integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -3560,7 +3585,7 @@ pretty-format@^24.0.0: ansi-regex "^4.0.0" ansi-styles "^3.2.0" -private@^0.1.6, private@^0.1.8: +private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== @@ -3667,16 +3692,6 @@ realpath-native@^1.0.0, realpath-native@^1.0.2: dependencies: util.promisify "^1.0.0" -recast@^0.17.6: - version "0.17.6" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.17.6.tgz#64ae98d0d2dfb10ff92ff5fb9ffb7371823b69fa" - integrity sha512-yoQRMRrK1lszNtbkGyM4kN45AwylV5hMiuEveUBlxytUViWevjvX6w+tzJt1LH4cfUhWt4NZvy3ThIhu6+m5wQ== - dependencies: - ast-types "0.12.4" - esprima "~4.0.0" - private "^0.1.8" - source-map "~0.6.1" - regenerate-unicode-properties@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" @@ -4204,6 +4219,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"