From d76736c00eb7ff39c749dc389e1ebe36d5ec0406 Mon Sep 17 00:00:00 2001 From: Bryan Naegele Date: Sun, 5 Jun 2016 22:58:23 -0500 Subject: [PATCH] Initial Flow support --- .babelrc | 10 +++++-- .eslintignore | 1 + .eslintrc | 34 ++++++++++++++++++++++-- .flowconfig | 24 +++++++++++++++++ CONTRIBUTING.md | 4 +++ interfaces/front-matter.js | 3 +++ interfaces/html-frontmatter.js | 3 +++ interfaces/invariant.js | 3 +++ interfaces/object-assign.js | 3 +++ interfaces/parse-filepath.js | 24 +++++++++++++++++ interfaces/slash.js | 3 +++ lib/isomorphic/create-routes.js | 2 ++ lib/isomorphic/gatsby-helpers.js | 1 + lib/isomorphic/html.js | 2 ++ lib/isomorphic/pages/_template.js | 1 + lib/isomorphic/wrappers/html.js | 1 + lib/isomorphic/wrappers/md.js | 1 + lib/loaders/config-loader/index.js | 1 + lib/loaders/html-loader/index.js | 1 + lib/loaders/markdown-loader/index.js | 1 + lib/utils/babel-config.js | 2 ++ lib/utils/build-css.js | 1 + lib/utils/build-html.js | 1 + lib/utils/build-javascript.js | 1 + lib/utils/build-page/index.js | 5 ++-- lib/utils/build-page/load-frontmatter.js | 10 ++++--- lib/utils/build-page/path-resolver.js | 5 +++- lib/utils/build-page/url-resolver.js | 2 ++ lib/utils/build.js | 2 ++ lib/utils/develop.js | 1 + lib/utils/glob-pages.js | 1 + lib/utils/init-starter.js | 1 + lib/utils/load-context.js | 1 + lib/utils/new.js | 1 + lib/utils/post-build.js | 1 + lib/utils/serve-build.js | 1 + lib/utils/static-entry.js | 1 + lib/utils/web-entry.js | 1 + package.json | 10 ++++++- scripts/flow-check.js | 10 +++++++ 40 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 .eslintignore create mode 100644 .flowconfig create mode 100644 interfaces/front-matter.js create mode 100644 interfaces/html-frontmatter.js create mode 100644 interfaces/invariant.js create mode 100644 interfaces/object-assign.js create mode 100644 interfaces/parse-filepath.js create mode 100644 interfaces/slash.js create mode 100644 scripts/flow-check.js diff --git a/.babelrc b/.babelrc index 007ca4aaa4ce1..368f66d80c896 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,10 @@ { - "presets": ['react', 'es2015', 'stage-0'], - "plugins": [] + "presets": [ + 'react', + 'es2015', + 'stage-0' + ], + "plugins": [ + 'transform-flow-strip-types' + ] } diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000..3a4f3989dbcdf --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +interfaces \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index 6d75ca0ffe10d..97960247233c0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,9 @@ "plugin:ava/recommended" ], "plugins": [ - "ava" + "ava", + "flowtype", + "flow-vars" ], "rules": { "indent": [2, 2, {"SwitchCase": 1}], @@ -20,9 +22,37 @@ "react/prefer-stateless-function": [0], "no-underscore-dangle": ["error", { "allow": ["_config"] }], "import/no-unresolved": [0], - "global-require": [0] + "global-require": [0], + "no-duplicate-imports": [0], + "flowtype/require-parameter-type": 1, + "flowtype/require-return-type": [ + 1, + "always", + { + "annotateUndefined": "never" + } + ], + "flowtype/space-after-type-colon": [ + 1, + "always" + ], + "flowtype/space-before-type-colon": [ + 1, + "never" + ], + "flowtype/type-id-match": [ + 0, + "^([A-Z][a-z0-9]+)+Type$" + ], + "flow-vars/define-flow-type": 1, + "flow-vars/use-flow-type": 1, }, "globals": { "__PREFIX_LINKS__": true, }, + "settings": { + "flowtype": { + "onlyFilesWithFlowAnnotation": true + } + } } diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000000000..3f2c8ddeace7f --- /dev/null +++ b/.flowconfig @@ -0,0 +1,24 @@ +[version] +0.27.0 + +[ignore] +.*/node_modules/.* +.*/test/.* + +[include] +node_modules + +[libs] +./interfaces/ +node_modules/iflow-debug/index.js.flow +node_modules/iflow-lodash/index.js.flow +node_modules/iflow-react-router/index.js.flow + +[options] +esproposal.class_instance_fields=enable +esproposal.class_static_fields=enable + +strip_root=true + +suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe +suppress_comment= \\(.\\|\n\\)*\\$FlowIssue \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3f5c5ce88093..b228cc2dc60f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,10 @@ simple steps: Test suite can be run via `npm test`. +This project uses [FlowType](https://flowtype.org/) for static type checking. + +**Note for Windows Users** - Flow is not currently supported on Windows. Please do not let this prevent you from contributing. If you can use a VM for this part, great! Otherwise, feel free to open a PR and ask for assistance. + The usual contributing steps are: * Fork the [official repository](https://github.com/gatsbyjs/gatsby). diff --git a/interfaces/front-matter.js b/interfaces/front-matter.js new file mode 100644 index 0000000000000..5a23b24da9175 --- /dev/null +++ b/interfaces/front-matter.js @@ -0,0 +1,3 @@ +declare module 'front-matter' { + declare var exports: (input: string) => { attributes: {}, body: string }; +} diff --git a/interfaces/html-frontmatter.js b/interfaces/html-frontmatter.js new file mode 100644 index 0000000000000..f6949fd790a24 --- /dev/null +++ b/interfaces/html-frontmatter.js @@ -0,0 +1,3 @@ +declare module 'html-frontmatter' { + declare var exports: (input: string) => {}; +} diff --git a/interfaces/invariant.js b/interfaces/invariant.js new file mode 100644 index 0000000000000..1a58e100cba5b --- /dev/null +++ b/interfaces/invariant.js @@ -0,0 +1,3 @@ +declare module 'invariant' { + declare var exports: (condition: any, message: string) => ?Error +} diff --git a/interfaces/object-assign.js b/interfaces/object-assign.js new file mode 100644 index 0000000000000..4a8f8dc130b41 --- /dev/null +++ b/interfaces/object-assign.js @@ -0,0 +1,3 @@ +declare module 'object-assign' { + declare var exports: (target: Object, ...sources?: Array) => {}; +} diff --git a/interfaces/parse-filepath.js b/interfaces/parse-filepath.js new file mode 100644 index 0000000000000..0047ba50992a0 --- /dev/null +++ b/interfaces/parse-filepath.js @@ -0,0 +1,24 @@ +declare module 'parse-filepath' { + declare type FileData = { + root: string, + dir: string, + base: string, + ext: string, + name: string, + + // aliases + extname: string, + basename: string, + dirname: string, + stem: string, + + // original path + path: string, + + // getters + absolute: () => string, + isAbsolute: () => Boolean + } + + declare var exports: (input: string) => FileData +} diff --git a/interfaces/slash.js b/interfaces/slash.js new file mode 100644 index 0000000000000..516a4e3eedf47 --- /dev/null +++ b/interfaces/slash.js @@ -0,0 +1,3 @@ +declare module 'slash' { + declare var exports: (input: string) => string; +} diff --git a/lib/isomorphic/create-routes.js b/lib/isomorphic/create-routes.js index 2703a76a81485..d74d41da2d374 100644 --- a/lib/isomorphic/create-routes.js +++ b/lib/isomorphic/create-routes.js @@ -1,3 +1,4 @@ +/* @flow weak */ import filter from 'lodash/filter' import sortBy from 'lodash/sortBy' import last from 'lodash/last' @@ -96,6 +97,7 @@ module.exports = (files, pagesReq) => { const wrappers = {} staticFileTypes.forEach((type) => { try { + // $FlowIssue - https://github.com/facebook/flow/issues/1975 wrappers[type] = require(`wrappers/${type}`) } catch (e) { // Ignore error. diff --git a/lib/isomorphic/gatsby-helpers.js b/lib/isomorphic/gatsby-helpers.js index fd1d188b117f3..49ef26df0e759 100644 --- a/lib/isomorphic/gatsby-helpers.js +++ b/lib/isomorphic/gatsby-helpers.js @@ -1,3 +1,4 @@ +/* @flow weak */ import { config } from 'config' import invariant from 'invariant' import isString from 'lodash/isString' diff --git a/lib/isomorphic/html.js b/lib/isomorphic/html.js index b1e6a09008151..51294c49cb1d6 100644 --- a/lib/isomorphic/html.js +++ b/lib/isomorphic/html.js @@ -1,3 +1,5 @@ +/* @flow weak */ + import React, { PropTypes } from 'react' import { prefixLink } from 'gatsby-helpers' diff --git a/lib/isomorphic/pages/_template.js b/lib/isomorphic/pages/_template.js index 23634162c28ea..224932651c76f 100644 --- a/lib/isomorphic/pages/_template.js +++ b/lib/isomorphic/pages/_template.js @@ -1,3 +1,4 @@ +/* @flow weak */ import React, { PropTypes } from 'react' const defaultMessage = ` diff --git a/lib/isomorphic/wrappers/html.js b/lib/isomorphic/wrappers/html.js index fb902e1f4d5ee..3a02642833f51 100644 --- a/lib/isomorphic/wrappers/html.js +++ b/lib/isomorphic/wrappers/html.js @@ -1,3 +1,4 @@ +/* @flow weak */ import React, { PropTypes } from 'react' module.exports = React.createClass({ diff --git a/lib/isomorphic/wrappers/md.js b/lib/isomorphic/wrappers/md.js index 31ec9ec02cff8..804dec3207c31 100644 --- a/lib/isomorphic/wrappers/md.js +++ b/lib/isomorphic/wrappers/md.js @@ -1,3 +1,4 @@ +/* @flow weak */ import React, { PropTypes } from 'react' module.exports = React.createClass({ diff --git a/lib/loaders/config-loader/index.js b/lib/loaders/config-loader/index.js index 45513bc0114f1..6c4ce7ce28b9b 100644 --- a/lib/loaders/config-loader/index.js +++ b/lib/loaders/config-loader/index.js @@ -1,3 +1,4 @@ +/* @flow weak */ const toml = require('toml') const loaderUtils = require('loader-utils') const path = require('path') diff --git a/lib/loaders/html-loader/index.js b/lib/loaders/html-loader/index.js index b49758e395e46..b3049d00dc2e5 100644 --- a/lib/loaders/html-loader/index.js +++ b/lib/loaders/html-loader/index.js @@ -1,3 +1,4 @@ +/* @flow weak */ import htmlFrontMatter from 'html-frontmatter' import objectAssign from 'object-assign' diff --git a/lib/loaders/markdown-loader/index.js b/lib/loaders/markdown-loader/index.js index d9ee72ffe6107..d64ea6de5ea64 100644 --- a/lib/loaders/markdown-loader/index.js +++ b/lib/loaders/markdown-loader/index.js @@ -1,3 +1,4 @@ +/* @flow weak */ import frontMatter from 'front-matter' import markdownIt from 'markdown-it' import hljs from 'highlight.js' diff --git a/lib/utils/babel-config.js b/lib/utils/babel-config.js index 384786bd8b69d..11290a0ab47d5 100644 --- a/lib/utils/babel-config.js +++ b/lib/utils/babel-config.js @@ -1,3 +1,4 @@ +/* @flow weak */ import resolve from 'babel-core/lib/helpers/resolve' import fs from 'fs' import path from 'path' @@ -104,6 +105,7 @@ function findBabelrc (directory) { */ function findBabelPackage (directory) { try { + // $FlowIssue - https://github.com/facebook/flow/issues/1975 const packageJson = require(path.join(directory, 'package.json')) return packageJson.babel } catch (error) { diff --git a/lib/utils/build-css.js b/lib/utils/build-css.js index 08d445c1e7138..aa04c75d7f862 100644 --- a/lib/utils/build-css.js +++ b/lib/utils/build-css.js @@ -1,3 +1,4 @@ +/* @flow weak */ import webpack from 'webpack' import webpackConfig from './webpack.config' import fs from 'fs' diff --git a/lib/utils/build-html.js b/lib/utils/build-html.js index 7b1354a86ef58..b6b56b718d8fa 100644 --- a/lib/utils/build-html.js +++ b/lib/utils/build-html.js @@ -1,3 +1,4 @@ +/* @flow weak */ require('node-cjsx').transform() import webpack from 'webpack' import globPages from './glob-pages' diff --git a/lib/utils/build-javascript.js b/lib/utils/build-javascript.js index 66a889f7b6bec..f3b6fb7464a65 100644 --- a/lib/utils/build-javascript.js +++ b/lib/utils/build-javascript.js @@ -1,3 +1,4 @@ +/* @flow weak */ import webpack from 'webpack' import webpackConfig from './webpack.config' diff --git a/lib/utils/build-page/index.js b/lib/utils/build-page/index.js index d1c16486d82d7..039371ea82466 100644 --- a/lib/utils/build-page/index.js +++ b/lib/utils/build-page/index.js @@ -1,12 +1,13 @@ +/* @flow */ import path from 'path' import objectAssign from 'object-assign' import pathResolver from './path-resolver' import loadFrontmatter from './load-frontmatter' -export default function buildPage (directory, page) { +export default function buildPage (directory: string, page: string) { const pageData = loadFrontmatter(page) - const relativePath = path.relative(path.join(directory, 'pages'), page) + const relativePath: string = path.relative(path.join(directory, 'pages'), page) const pathData = pathResolver(relativePath, pageData) return objectAssign({}, pathData, { data: pageData }) diff --git a/lib/utils/build-page/load-frontmatter.js b/lib/utils/build-page/load-frontmatter.js index d1148873b080b..398d597acd549 100644 --- a/lib/utils/build-page/load-frontmatter.js +++ b/lib/utils/build-page/load-frontmatter.js @@ -1,11 +1,12 @@ +/* @flow */ import fs from 'fs' import path from 'path' import frontMatter from 'front-matter' import objectAssign from 'object-assign' import htmlFrontMatter from 'html-frontmatter' -export default function loadFrontmatter (pagePath) { - const ext = path.extname(pagePath).slice(1) +export default function loadFrontmatter (pagePath: string): {} { + const ext: string = path.extname(pagePath).slice(1) // Load data for each file type. // TODO use webpack-require to ensure data loaded @@ -14,10 +15,11 @@ export default function loadFrontmatter (pagePath) { let data if (ext === 'md') { - const rawData = frontMatter(fs.readFileSync(pagePath, 'utf-8')) + const rawData = frontMatter(fs.readFileSync(pagePath, { encoding: 'utf-8' })) data = objectAssign({}, rawData.attributes) } else if (ext === 'html') { - const html = fs.readFileSync(pagePath, 'utf-8') + const html = fs.readFileSync(pagePath, { encoding: 'utf-8' }) + // $FlowIssue - https://github.com/facebook/flow/issues/1870 data = objectAssign({}, htmlFrontMatter(html), { body: html }) } else { data = {} diff --git a/lib/utils/build-page/path-resolver.js b/lib/utils/build-page/path-resolver.js index 500ec79bfd5ed..b84acc3c6f5c3 100644 --- a/lib/utils/build-page/path-resolver.js +++ b/lib/utils/build-page/path-resolver.js @@ -1,8 +1,10 @@ +/* @flow */ import slash from 'slash' import parsePath from 'parse-filepath' import urlResolver from './url-resolver' -export default function pathResolver (relativePath, pageData={}) { + +export default function pathResolver (relativePath: string, pageData: {} = {}) { const data = {} data.file = parsePath(relativePath) @@ -26,3 +28,4 @@ export default function pathResolver (relativePath, pageData={}) { return data } + diff --git a/lib/utils/build-page/url-resolver.js b/lib/utils/build-page/url-resolver.js index 511c64606bf37..51c7df8c3e635 100644 --- a/lib/utils/build-page/url-resolver.js +++ b/lib/utils/build-page/url-resolver.js @@ -1,3 +1,4 @@ +/* @flow weak */ import { posix as path } from 'path' import _, { startsWith } from 'lodash' import invariant from 'invariant' @@ -5,6 +6,7 @@ import invariant from 'invariant' let rewritePath try { const gatsbyNodeConfig = path.resolve(process.cwd(), './gatsby-node') + // $FlowIssue - https://github.com/facebook/flow/issues/1975 const nodeConfig = require(gatsbyNodeConfig) rewritePath = nodeConfig.rewritePath } catch (e) { diff --git a/lib/utils/build.js b/lib/utils/build.js index e5d3b74d747f1..1d1939bfcffc3 100644 --- a/lib/utils/build.js +++ b/lib/utils/build.js @@ -1,3 +1,4 @@ +/* @flow weak */ import toml from 'toml' import fs from 'fs' import _ from 'lodash' @@ -12,6 +13,7 @@ function customPost (program, callback) { const directory = program.directory let customPostBuild try { + // $FlowIssue - https://github.com/facebook/flow/issues/1975 const gatsbyNodeConfig = require(`${directory}/gatsby-node`) customPostBuild = gatsbyNodeConfig.postBuild } catch (e) { diff --git a/lib/utils/develop.js b/lib/utils/develop.js index 93e9e2681e970..d61f829c1853d 100644 --- a/lib/utils/develop.js +++ b/lib/utils/develop.js @@ -1,3 +1,4 @@ +/* @flow weak */ require('node-cjsx').transform() import Hapi from 'hapi' import Boom from 'boom' diff --git a/lib/utils/glob-pages.js b/lib/utils/glob-pages.js index 0af6a32466cca..bc9c80f22ee8a 100644 --- a/lib/utils/glob-pages.js +++ b/lib/utils/glob-pages.js @@ -1,3 +1,4 @@ +/* @flow weak */ import glob from 'glob' import buildPage from './build-page' const debug = require('debug')('gatsby:glob') diff --git a/lib/utils/init-starter.js b/lib/utils/init-starter.js index 940fb626d6ab9..c07b954fee8ef 100644 --- a/lib/utils/init-starter.js +++ b/lib/utils/init-starter.js @@ -1,3 +1,4 @@ +/* @flow weak */ import { exec } from 'child_process' import fs from 'fs-extra' import sysPath from 'path' diff --git a/lib/utils/load-context.js b/lib/utils/load-context.js index 920ab8a67ef7e..137385574ed70 100644 --- a/lib/utils/load-context.js +++ b/lib/utils/load-context.js @@ -1,3 +1,4 @@ +/* @flow weak */ // This file is auto-written and used by Gatsby to require // files from your pages directory. module.exports = function (callback) { diff --git a/lib/utils/new.js b/lib/utils/new.js index aaa0794b6ef07..244dee94cf579 100644 --- a/lib/utils/new.js +++ b/lib/utils/new.js @@ -1,3 +1,4 @@ +/* @flow weak */ const logger = require('tracer').colorConsole() const initStarter = require('./init-starter') diff --git a/lib/utils/post-build.js b/lib/utils/post-build.js index 338a8dc4e3dc2..a97ad7a87418a 100644 --- a/lib/utils/post-build.js +++ b/lib/utils/post-build.js @@ -1,3 +1,4 @@ +/* @flow weak */ import path from 'path' import glob from 'glob' import fs from 'fs-extra' diff --git a/lib/utils/serve-build.js b/lib/utils/serve-build.js index 8f83c0f1a74d7..4a8e0b1d3f2d9 100644 --- a/lib/utils/serve-build.js +++ b/lib/utils/serve-build.js @@ -1,3 +1,4 @@ +/* @flow weak */ import Hapi from 'hapi' import opn from 'opn' diff --git a/lib/utils/static-entry.js b/lib/utils/static-entry.js index f9972f6f25ec3..b341adce830e4 100644 --- a/lib/utils/static-entry.js +++ b/lib/utils/static-entry.js @@ -1,3 +1,4 @@ +/* @flow weak */ import React from 'react' import { renderToString, renderToStaticMarkup } from 'react-dom/server' import { match, RouterContext } from 'react-router' diff --git a/lib/utils/web-entry.js b/lib/utils/web-entry.js index 292539c57402f..b2d0e2e6aa0d1 100644 --- a/lib/utils/web-entry.js +++ b/lib/utils/web-entry.js @@ -1,3 +1,4 @@ +/* @flow weak */ import React from 'react' import ReactDOM from 'react-dom' import { applyRouterMiddleware, browserHistory, Router } from 'react-router' diff --git a/package.json b/package.json index 93aa913363157..6c70a018da16e 100644 --- a/package.json +++ b/package.json @@ -86,15 +86,22 @@ "ava-http": "^0.2.1", "babel-cli": "^6.9.0", "babel-eslint": "^6.0.2", + "babel-plugin-transform-flow-strip-types": "^6.8.0", "babel-register": "^6.9.0", "bluebird": "^3.4.0", "cheerio": "^0.20.0", "eslint": "^2.11.1", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-ava": "^2.5.0", + "eslint-plugin-flow-vars": "^0.4.0", + "eslint-plugin-flowtype": "^2.2.7", "eslint-plugin-import": "^1.8.1", "eslint-plugin-jsx-a11y": "^1.5.3", "eslint-plugin-react": "^5.1.1", + "flow-bin": "^0.27.0", + "iflow-debug": "^1.0.15", + "iflow-lodash": "^1.1.15", + "iflow-react-router": "^1.1.15", "nyc": "^6.6.1" }, "engines": { @@ -119,7 +126,8 @@ "scripts": { "build": "babel lib --out-dir dist/", "lint": "eslint --ext .js,.jsx --ignore-path .gitignore .", - "test": "npm run lint && npm run test-node && npm run test-integration", + "lint:flow": "babel-node scripts/flow-check.js", + "test": "npm run lint && npm run test-node && npm run test-integration && npm run lint:flow", "test-coverage": "node_modules/.bin/nyc --reporter=lcov --reporter=text npm test", "test-integration": "node_modules/.bin/ava test/integration", "test-node": "node_modules/.bin/ava test/utils", diff --git a/scripts/flow-check.js b/scripts/flow-check.js new file mode 100644 index 0000000000000..f8f9e19e2d287 --- /dev/null +++ b/scripts/flow-check.js @@ -0,0 +1,10 @@ +import { execFileSync } from 'child_process' +import flowBin from 'flow-bin' + +try { + if (process.platform !== 'win32') { + execFileSync(flowBin, ['check'], { stdio: 'inherit' }) + } +} catch (e) { + process.exit(1) +}