Skip to content
Snippets Groups Projects
Commit 968516a9 authored by Martin Donath's avatar Martin Donath Committed by GitHub
Browse files

Merge pull request #186 from squidfunk/chore/flow-type-checking

Integrate static type-checking with JSDoc and Flow
parents 8f48f0bc aaa3c8d6
Branches
No related tags found
No related merge requests found
Showing
with 309 additions and 31 deletions
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
/material /material
/site /site
# Files used and generated by flow
/lib/declarations
/tmp
# Files generated by visual tests # Files generated by visual tests
/gemini-report /gemini-report
/tests/visual/data /tests/visual/data
...@@ -169,7 +169,7 @@ ...@@ -169,7 +169,7 @@
"space-unary-ops": 2, "space-unary-ops": 2,
"spaced-comment": [2, "always", { "spaced-comment": [2, "always", {
"line": { "line": {
"markers": ["/"], "markers": ["/", ":"],
"exceptions": ["-", "+"] "exceptions": ["-", "+"]
}, },
"block": { "block": {
......
[ignore]
.*/node_modules/.*
[libs]
lib/declarations/
[options]
strip_root=true
...@@ -45,17 +45,25 @@ git checkout -- . ...@@ -45,17 +45,25 @@ git checkout -- .
FILES=$(git diff --cached --name-only --diff-filter=ACMR | \ FILES=$(git diff --cached --name-only --diff-filter=ACMR | \
grep "\.\(js\|jsx\|scss\)$") grep "\.\(js\|jsx\|scss\)$")
# Run the check and print indicator # Run check and print indicator
if [ "$FILES" ]; then if [ "$FILES" ]; then
npm run lint --silent
# If we're on master, abort commit # If linter terminated with errors, abort commit
if [ $? -gt 0 ]; then if [ $? -gt 0 ]; then
echo -e "\x1B[31m✗\x1B[0m Linter - \x1B[31m$MESSAGE\x1B[0m" echo -e "\x1B[31m✗\x1B[0m Linter - \x1B[31m$MESSAGE\x1B[0m"
exit 1 exit 1
else else
echo -e "\x1B[32m✓\x1B[0m Linter" echo -e "\x1B[32m✓\x1B[0m Linter"
fi fi
# If flow terminated with errors, abort commit
npm run flow --silent > /dev/null
if [ $? -gt 0 ]; then
echo -e "\x1B[31m✗\x1B[0m Flow - \x1B[31m$MESSAGE\x1B[0m"
exit 1
else
echo -e "\x1B[32m✓\x1B[0m Flow"
fi
fi fi
# We're good # We're good
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
# NPM-related # NPM-related
/node_modules /node_modules
/npm-debug.log* /npm-debug.log*
/yarn-error.log
# Files generated by build # Files generated by build
/build /build
...@@ -31,6 +32,9 @@ ...@@ -31,6 +32,9 @@
/MANIFEST /MANIFEST
/site /site
# Files generated by flow typechecker
/tmp
# Files generated by visual tests # Files generated by visual tests
/gemini-report /gemini-report
/tests/visual/baseline/local /tests/visual/baseline/local
......
...@@ -55,9 +55,11 @@ let args = yargs ...@@ -55,9 +55,11 @@ let args = yargs
.default("sourcemaps", false) /* Create sourcemaps */ .default("sourcemaps", false) /* Create sourcemaps */
.argv .argv
/* Only use the last value seen, so overrides are possible */ /* Only use the last seen value if boolean, so overrides are possible */
args = Object.keys(args).reduce((result, arg) => { args = Object.keys(args).reduce((result, arg) => {
result[arg] = [].concat(args[arg]).pop() result[arg] = Array.isArray(args[arg]) && typeof args[arg][0] === "boolean"
? [].concat(args[arg]).pop()
: args[arg]
return result return result
}, {}) }, {})
...@@ -147,19 +149,28 @@ gulp.task("assets:images:clean", ...@@ -147,19 +149,28 @@ gulp.task("assets:images:clean",
/* /*
* Build application logic * Build application logic
*
* When revisioning, the build must be serialized due to race conditions
* happening when two tasks try to write manifest.json simultaneously
*/ */
gulp.task("assets:javascripts:build:application", gulp.task("assets:javascripts:build:application", args.revision ? [
load("assets/javascripts/build/application")) "assets:stylesheets:build"
] : [], load("assets/javascripts/build/application"))
/* /*
* Build custom modernizr * Build custom modernizr
*
* When revisioning, the build must be serialized due to race conditions
* happening when two tasks try to write manifest.json simultaneously
*/ */
gulp.task("assets:javascripts:build:modernizr", [ gulp.task("assets:javascripts:build:modernizr", [
"assets:stylesheets:build" "assets:stylesheets:build"
], load("assets/javascripts/build/modernizr")) ].concat(args.revision ? [
"assets:javascripts:build:application"
] : []), load("assets/javascripts/build/modernizr"))
/* /*
* Build application logic and modernizr * Build application logic and Modernizr
*/ */
gulp.task("assets:javascripts:build", (args.clean ? [ gulp.task("assets:javascripts:build", (args.clean ? [
"assets:javascripts:clean" "assets:javascripts:clean"
...@@ -178,6 +189,12 @@ gulp.task("assets:javascripts:build", (args.clean ? [ ...@@ -178,6 +189,12 @@ gulp.task("assets:javascripts:build", (args.clean ? [
gulp.task("assets:javascripts:clean", gulp.task("assets:javascripts:clean",
load("assets/javascripts/clean")) load("assets/javascripts/clean"))
/*
* Annotate JavaScript
*/
gulp.task("assets:javascripts:annotate",
load("assets/javascripts/annotate"))
/* /*
* Lint JavaScript * Lint JavaScript
*/ */
......
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Declarations
* ------------------------------------------------------------------------- */
declare module "fastclick" {
/* Type: FastClick */
declare type FastClick = {
attach(name: HTMLElement): void
}
/* Exports */
declare export default FastClick
}
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Declarations
* ------------------------------------------------------------------------- */
declare module "js-cookie" {
/* Type: Options for setting cookie values */
declare type Options = {
path?: string,
expires?: number | string
}
/* Type: Cookie */
declare type Cookie = {
getJSON(json: string): Object,
set(key: string, value: string, options?: Options): string
}
/* Exports */
declare export default Cookie
}
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Declarations
* ------------------------------------------------------------------------- */
declare class Jsx {
static createElement(tag: string, properties?: Object,
...children?: Array<string | number | Array<HTMLElement>>
): HTMLElement
}
/* Exports */
declare export default Jsx
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Declarations
* ------------------------------------------------------------------------- */
/*
* Currently, it's not possible to export a function that returns a class type,
* as the imports just don't correctly work with flow. As a workaround we
* export an object until this error is fixed.
*/
declare module "lunr" {
declare function exports(name: () => void): Object
}
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Declarations
* ------------------------------------------------------------------------- */
declare class Modernizr {
static addTest(name: string, test: () => boolean): void
}
/* Exports */
declare export default Modernizr
...@@ -24,13 +24,13 @@ ...@@ -24,13 +24,13 @@
* Module * Module
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
export default /* JSX */ { export default /* Jsx */ {
/** /**
* Create a native DOM node from JSX's intermediate representation * Create a native DOM node from JSX's intermediate representation
* *
* @param {string} tag - Tag name * @param {string} tag - Tag name
* @param {object} properties - Properties * @param {?Object} properties - Properties
* @param {...(string|number|Array)} children - Child nodes * @param {...(string|number|Array)} children - Child nodes
* @return {HTMLElement} Native DOM node * @return {HTMLElement} Native DOM node
*/ */
......
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { transform } from "babel-core"
import jsdoc2flow from "flow-jsdoc"
import through from "through2"
/* ----------------------------------------------------------------------------
* Task: annotate JavaScript
* ------------------------------------------------------------------------- */
export default (gulp, config) => {
return () => {
return gulp.src(`${config.assets.src}/javascripts/**/*.{js,jsx}`)
/* Linting */
.pipe(
through.obj(function(file, enc, done) {
if (file.isNull() || file.isStream())
return done()
/* Perform Babel transformation to resolve JSX calls */
const transformed = transform(file.contents.toString(), {
plugins: [
["transform-react-jsx", {
"pragma": "Jsx.createElement"
}]
]
})
/* Annotate contents */
file.contents = new Buffer(jsdoc2flow(
`/* @flow */\n\n${transformed.code}`
).toString())
/* Push file to next stage */
this.push(file)
done()
}))
/* Print errors */
.pipe(gulp.dest("tmp/assets/javascripts"))
}
}
...@@ -70,7 +70,7 @@ export default (gulp, config, args) => { ...@@ -70,7 +70,7 @@ export default (gulp, config, args) => {
/* Provide JSX helper */ /* Provide JSX helper */
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
JSX: path.join(process.cwd(), `${config.lib}/providers/jsx.js`) Jsx: path.join(process.cwd(), `${config.lib}/providers/jsx.js`)
}) })
].concat( ].concat(
......
...@@ -39,7 +39,7 @@ const format = eslint.getFormatter() ...@@ -39,7 +39,7 @@ const format = eslint.getFormatter()
export default (gulp, config) => { export default (gulp, config) => {
return () => { return () => {
return gulp.src(`${config.assets.src}/javascripts/**/*.js`) return gulp.src(`${config.assets.src}/javascripts/**/*.{js,jsx}`)
/* Linting */ /* Linting */
.pipe( .pipe(
......
This diff is collapsed.
This diff is collapsed.
...@@ -141,7 +141,7 @@ ...@@ -141,7 +141,7 @@
{% endblock %} {% endblock %}
</div> </div>
{% block scripts %} {% block scripts %}
<script src="{{ base_url }}/assets/javascripts/application-0dae3d4464.js"></script> <script src="{{ base_url }}/assets/javascripts/application-8dc3dfc020.js"></script>
<script>app.initialize({url:{base:"{{ base_url }}"}})</script> <script>app.initialize({url:{base:"{{ base_url }}"}})</script>
{% for path in extra_javascript %} {% for path in extra_javascript %}
<script src="{{ path }}"></script> <script src="{{ path }}"></script>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="md-search__overlay"></div> <div class="md-search__overlay"></div>
<div class="md-search__inner"> <div class="md-search__inner">
<form class="md-search__form" name="search"> <form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" placeholder="{{ lang.t('search.placeholder') }}" accesskey="s" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false"> <input type="text" class="md-search__input" name="query" placeholder="{{ lang.t('search.placeholder') }}" accesskey="s" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query">
<label class="md-icon md-search__icon" for="search"></label> <label class="md-icon md-search__icon" for="search"></label>
</form> </form>
<div class="md-search__output"> <div class="md-search__output">
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
"scripts": { "scripts": {
"build": "scripts/build", "build": "scripts/build",
"clean": "scripts/clean", "clean": "scripts/clean",
"flow": "scripts/flow",
"lint": "scripts/lint", "lint": "scripts/lint",
"start": "scripts/start", "start": "scripts/start",
"test:visual:run": "scripts/test/visual/run", "test:visual:run": "scripts/test/visual/run",
...@@ -34,7 +35,7 @@ ...@@ -34,7 +35,7 @@
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.7.3", "autoprefixer": "^6.7.3",
"babel-core": "^6.0.0", "babel-core": "^6.23.0",
"babel-eslint": "^7.1.1", "babel-eslint": "^7.1.1",
"babel-loader": "^6.3.1", "babel-loader": "^6.3.1",
"babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-add-module-exports": "^0.2.1",
...@@ -48,8 +49,10 @@ ...@@ -48,8 +49,10 @@
"custom-event-polyfill": "^0.3.0", "custom-event-polyfill": "^0.3.0",
"del": "^2.2.2", "del": "^2.2.2",
"ecstatic": "^2.1.0", "ecstatic": "^2.1.0",
"eslint": "^3.14.0", "eslint": "^3.16.0",
"fastclick": "^1.0.6", "fastclick": "^1.0.6",
"flow-bin": "^0.39.0",
"flow-jsdoc": "^0.2.2",
"git-hooks": "^1.1.7", "git-hooks": "^1.1.7",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-changed": "^2.0.0", "gulp-changed": "^2.0.0",
...@@ -93,14 +96,6 @@ ...@@ -93,14 +96,6 @@
"chai": "^3.5.0", "chai": "^3.5.0",
"eslint-plugin-mocha": "^4.8.0", "eslint-plugin-mocha": "^4.8.0",
"gemini": "^4.14.3", "gemini": "^4.14.3",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-coverage": "^1.1.1",
"karma-mocha": "^1.3.0",
"karma-notify-reporter": "^1.0.1",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.26",
"karma-webpack": "^2.0.1",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"moniker": "^0.1.2", "moniker": "^0.1.2",
"saucelabs": "^1.4.0", "saucelabs": "^1.4.0",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment