From 7b585f191ac8a168eb4bcabe7c8cd74fa805e33d Mon Sep 17 00:00:00 2001 From: squidfunk Date: Mon, 10 Oct 2016 12:35:25 +0200 Subject: [PATCH] Added full JSX support to build pipeline --- .babelrc | 5 +- .eslintignore | 3 + .sass-lint.yml | 1 + Gulpfile.babel.js | 20 ++---- .../create-element.js => lib/providers/jsx.js | 72 +++++++++---------- lib/tasks/.eslintrc | 5 ++ .../tasks}/assets/images/build/ico.js | 0 .../tasks}/assets/images/build/svg.js | 0 {tasks => lib/tasks}/assets/images/clean.js | 0 .../assets/javascripts/build/application.js | 36 +++++++--- .../assets/javascripts/build/modernizr.js | 0 .../tasks}/assets/javascripts/clean.js | 0 .../tasks}/assets/javascripts/lint.js | 8 +-- .../tasks}/assets/stylesheets/build.js | 0 .../tasks}/assets/stylesheets/clean.js | 0 .../tasks}/assets/stylesheets/lint.js | 10 +-- {tasks => lib/tasks}/mkdocs/build.js | 0 {tasks => lib/tasks}/mkdocs/clean.js | 0 {tasks => lib/tasks}/mkdocs/serve.js | 0 lib/tasks/tests/unit.js | 36 ++++++++++ {tasks => lib/tasks}/tests/unit/watch.js | 1 - {tasks => lib/tasks}/views/build.js | 0 {tasks => lib/tasks}/views/clean.js | 0 package.json | 6 +- tests/.babelrc | 9 --- tests/karma.conf.js | 52 ++++++++++---- tests/unit/Marker.spec.jsx | 72 ++++++++++++++++--- 27 files changed, 229 insertions(+), 107 deletions(-) create mode 100644 .eslintignore rename tests/unit/_lib/create-element.js => lib/providers/jsx.js (53%) create mode 100644 lib/tasks/.eslintrc rename {tasks => lib/tasks}/assets/images/build/ico.js (100%) rename {tasks => lib/tasks}/assets/images/build/svg.js (100%) rename {tasks => lib/tasks}/assets/images/clean.js (100%) rename {tasks => lib/tasks}/assets/javascripts/build/application.js (82%) rename {tasks => lib/tasks}/assets/javascripts/build/modernizr.js (100%) rename {tasks => lib/tasks}/assets/javascripts/clean.js (100%) rename {tasks => lib/tasks}/assets/javascripts/lint.js (93%) rename {tasks => lib/tasks}/assets/stylesheets/build.js (100%) rename {tasks => lib/tasks}/assets/stylesheets/clean.js (100%) rename {tasks => lib/tasks}/assets/stylesheets/lint.js (92%) rename {tasks => lib/tasks}/mkdocs/build.js (100%) rename {tasks => lib/tasks}/mkdocs/clean.js (100%) rename {tasks => lib/tasks}/mkdocs/serve.js (100%) create mode 100644 lib/tasks/tests/unit.js rename {tasks => lib/tasks}/tests/unit/watch.js (98%) rename {tasks => lib/tasks}/views/build.js (100%) rename {tasks => lib/tasks}/views/clean.js (100%) delete mode 100644 tests/.babelrc diff --git a/.babelrc b/.babelrc index e51860877..9691c8965 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,9 @@ { "presets": ["es2015"], "plugins": [ - "add-module-exports" + "add-module-exports", + ["transform-react-jsx", { + "pragma": "JSX.createElement" + }] ] } \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..115d06489 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +/build +/material +/site \ No newline at end of file diff --git a/.sass-lint.yml b/.sass-lint.yml index e324f7de4..eabb7cb3f 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -20,6 +20,7 @@ files: ignore: + - node_modules/** - src/assets/stylesheets/_shame.scss - src/assets/stylesheets/extensions/_codehilite.scss # Temporary disabled diff --git a/Gulpfile.babel.js b/Gulpfile.babel.js index 6c0e837f6..3df904861 100755 --- a/Gulpfile.babel.js +++ b/Gulpfile.babel.js @@ -35,6 +35,7 @@ const config = { src: "src/assets", /* Source directory for assets */ build: "material/assets" /* Target directory for assets */ }, + lib: "lib", /* Libraries */ views: { src: "src", /* Source directory for views */ build: "material" /* Target directory for views */ @@ -43,11 +44,11 @@ const config = { const args = yargs .default("clean", false) /* Clean before build */ - .default("karma", false) /* Karma watchdog */ + .default("karma", true) /* Karma watchdog */ .default("lint", true) /* Lint sources */ - .default("mkdocs", false) /* MkDocs watchdog */ - .default("optimize", true) /* Optimize sources */ - .default("revision", true) /* Revision assets */ + .default("mkdocs", true) /* MkDocs watchdog */ + .default("optimize", false) /* Optimize sources */ + .default("revision", false) /* Revision assets */ .default("sourcemaps", false) /* Create sourcemaps */ .argv @@ -91,7 +92,7 @@ gulp.src = (...glob) => { * Helper function to load a task */ const load = task => { - return require(`./tasks/${task}`)(gulp, config, args) + return require(`./${config.lib}/tasks/${task}`)(gulp, config, args) } /* ---------------------------------------------------------------------------- @@ -219,14 +220,6 @@ gulp.task("assets:clean", [ "assets:stylesheets:clean" ]) -/* - * Lint sources - */ -gulp.task("assets:lint", [ - "assets:javascripts:lint", - "assets:stylesheets:lint" -]) - /* ---------------------------------------------------------------------------- * Views * ------------------------------------------------------------------------- */ @@ -317,6 +310,7 @@ gulp.task("watch", [ "assets:build", "views:build" ], () => { + process.env.WATCH = true /* Start MkDocs server */ if (args.mkdocs) diff --git a/tests/unit/_lib/create-element.js b/lib/providers/jsx.js similarity index 53% rename from tests/unit/_lib/create-element.js rename to lib/providers/jsx.js index a8d8e157e..efcda77b0 100644 --- a/tests/unit/_lib/create-element.js +++ b/lib/providers/jsx.js @@ -24,50 +24,46 @@ * Definition * ------------------------------------------------------------------------- */ -/** - * Create a native DOM node - * - * @param {string} tag - Tag name - * @param {object} properties - Properties - * @param {string|number|Array} children - Child nodes - * @return {HTMLElement} Native DOM node - */ -const createElement = (tag, properties, ...children) => { - const el = document.createElement(tag) +export default /* JSX */ { - /* Set all properties */ - for (const attr of Object.keys(properties)) - el.setAttribute(attr, properties[attr]) + /** + * Create a native DOM node + * + * @param {string} tag - Tag name + * @param {object} properties - Properties + * @param {string|number|Array} children - Child nodes + * @return {HTMLElement} Native DOM node + */ + createElement(tag, properties, ...children) { + const el = document.createElement(tag) - /* Iterate child nodes */ - const iterateChildNodes = nodes => { - for (const node of nodes) { + /* Set all properties */ + if (properties) + for (const attr of Object.keys(properties)) + el.setAttribute(attr, properties[attr]) - /* Directly append content */ - if (typeof node === "string" || - typeof node === "number") { - el.textContent += node + /* Iterate child nodes */ + const iterateChildNodes = nodes => { + for (const node of nodes) { - /* Recurse, if we got an array */ - } else if (Array.isArray(node)) { - iterateChildNodes(node) + /* Directly append text content */ + if (typeof node === "string" || + typeof node === "number") { + el.textContent += node - /* Append regular nodes */ - } else { - el.appendChild(node) + /* Recurse, if we got an array */ + } else if (Array.isArray(node)) { + iterateChildNodes(node) + + /* Append regular nodes */ + } else { + el.appendChild(node) + } } } + + /* Iterate child nodes and return element */ + iterateChildNodes(children) + return el } - - /* Iterate child nodes */ - iterateChildNodes(children) - - /* Return element */ - return el } - -/* ---------------------------------------------------------------------------- - * Exports - * ------------------------------------------------------------------------- */ - -export default createElement diff --git a/lib/tasks/.eslintrc b/lib/tasks/.eslintrc new file mode 100644 index 000000000..d4cadde28 --- /dev/null +++ b/lib/tasks/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "no-invalid-this": 0 + } +} \ No newline at end of file diff --git a/tasks/assets/images/build/ico.js b/lib/tasks/assets/images/build/ico.js similarity index 100% rename from tasks/assets/images/build/ico.js rename to lib/tasks/assets/images/build/ico.js diff --git a/tasks/assets/images/build/svg.js b/lib/tasks/assets/images/build/svg.js similarity index 100% rename from tasks/assets/images/build/svg.js rename to lib/tasks/assets/images/build/svg.js diff --git a/tasks/assets/images/clean.js b/lib/tasks/assets/images/clean.js similarity index 100% rename from tasks/assets/images/clean.js rename to lib/tasks/assets/images/clean.js diff --git a/tasks/assets/javascripts/build/application.js b/lib/tasks/assets/javascripts/build/application.js similarity index 82% rename from tasks/assets/javascripts/build/application.js rename to lib/tasks/assets/javascripts/build/application.js index 0307b1366..b0da1e32d 100644 --- a/tasks/assets/javascripts/build/application.js +++ b/lib/tasks/assets/javascripts/build/application.js @@ -35,7 +35,7 @@ export default (gulp, config, args) => { return () => { return gulp.src(`${config.assets.src}/javascripts/**/*.js`) - /* Transpile with webpack */ + /* Build with webpack */ .pipe( stream({ entry: "application.js", @@ -44,17 +44,27 @@ export default (gulp, config, args) => { library: "Application" }, module: { + + /* Transpile ES6 to ES5 with Babel */ loaders: [ { loader: "babel-loader", - test: path.join(process.cwd(), - `${config.assets.src}/javascripts`) + test: /\.jsx?$/ } ] }, plugins: [ - new webpack.NoErrorsPlugin() + + /* Don't emit assets that include errors */ + new webpack.NoErrorsPlugin(), + + /* Provide JSX helper */ + new webpack.ProvidePlugin({ + JSX: path.join(process.cwd(), `${config.lib}/providers/jsx.js`) + }) ].concat( + + /* Minify sources */ args.optimize ? [ new webpack.optimize.UglifyJsPlugin({ compress: { @@ -62,20 +72,30 @@ export default (gulp, config, args) => { } }) ] : []), - stats: { - colors: true - }, + + /* Module resolver */ resolve: { modulesDirectories: [ "src/assets/javascripts", "node_modules" ], extensions: [ - "", ".js" + "", + ".js", + ".jsx" ] }, + + /* Webpack commandline output */ + stats: { + colors: true + }, + + /* Sourcemap support */ devtool: args.sourcemaps ? "source-map" : "" })) + + /* Revisioning */ .pipe(gulpif(args.revision, rev())) .pipe(gulpif(args.revision, version({ manifest: gulp.src("manifest.json") }))) diff --git a/tasks/assets/javascripts/build/modernizr.js b/lib/tasks/assets/javascripts/build/modernizr.js similarity index 100% rename from tasks/assets/javascripts/build/modernizr.js rename to lib/tasks/assets/javascripts/build/modernizr.js diff --git a/tasks/assets/javascripts/clean.js b/lib/tasks/assets/javascripts/clean.js similarity index 100% rename from tasks/assets/javascripts/clean.js rename to lib/tasks/assets/javascripts/clean.js diff --git a/tasks/assets/javascripts/lint.js b/lib/tasks/assets/javascripts/lint.js similarity index 93% rename from tasks/assets/javascripts/lint.js rename to lib/tasks/assets/javascripts/lint.js index c8264b5a1..b5e17e5b4 100644 --- a/tasks/assets/javascripts/lint.js +++ b/lib/tasks/assets/javascripts/lint.js @@ -54,7 +54,7 @@ export default (gulp, config) => { file.eslint.results[0].filePath = path.relative(process.cwd(), file.path) - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() })) @@ -67,7 +67,7 @@ export default (gulp, config) => { console.log(format(file.eslint.results)) } - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() })) @@ -85,7 +85,7 @@ export default (gulp, config) => { if (results.errorCount || results.warningCount) errors.push(file) - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() @@ -96,7 +96,7 @@ export default (gulp, config) => { return file.relative }).join(", ") - // eslint-disable-next-line no-invalid-this + /* Emit error */ this.emit("error", new util.PluginError("eslint", `Terminated with errors in files: ${message}`)) } diff --git a/tasks/assets/stylesheets/build.js b/lib/tasks/assets/stylesheets/build.js similarity index 100% rename from tasks/assets/stylesheets/build.js rename to lib/tasks/assets/stylesheets/build.js diff --git a/tasks/assets/stylesheets/clean.js b/lib/tasks/assets/stylesheets/clean.js similarity index 100% rename from tasks/assets/stylesheets/clean.js rename to lib/tasks/assets/stylesheets/clean.js diff --git a/tasks/assets/stylesheets/lint.js b/lib/tasks/assets/stylesheets/lint.js similarity index 92% rename from tasks/assets/stylesheets/lint.js rename to lib/tasks/assets/stylesheets/lint.js index 1ce179e7f..d61b40fe6 100644 --- a/tasks/assets/stylesheets/lint.js +++ b/lib/tasks/assets/stylesheets/lint.js @@ -29,7 +29,7 @@ import util from "gulp-util" * Task: lint SASS sources * ------------------------------------------------------------------------- */ -export default (gulp, config, args) => { +export default (gulp, config) => { return () => { return gulp.src(`${config.assets.src}/stylesheets/**/*.scss`) @@ -46,7 +46,7 @@ export default (gulp, config, args) => { filename: path.relative(process.cwd(), file.path) }) - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() })) @@ -56,7 +56,7 @@ export default (gulp, config, args) => { through.obj(function(file, enc, cb) { sasslint.outputResults([file.sasslint]) - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() })) @@ -74,7 +74,7 @@ export default (gulp, config, args) => { if (results.errorCount || results.warningCount) errors.push(file) - // eslint-disable-next-line no-invalid-this + /* Push file to next stage */ this.push(file) cb() @@ -85,7 +85,7 @@ export default (gulp, config, args) => { return file.relative }).join(", ") - // eslint-disable-next-line no-invalid-this + /* Emit error */ this.emit("error", new util.PluginError("eslint", `Terminated with errors in files: ${message}`)) } diff --git a/tasks/mkdocs/build.js b/lib/tasks/mkdocs/build.js similarity index 100% rename from tasks/mkdocs/build.js rename to lib/tasks/mkdocs/build.js diff --git a/tasks/mkdocs/clean.js b/lib/tasks/mkdocs/clean.js similarity index 100% rename from tasks/mkdocs/clean.js rename to lib/tasks/mkdocs/clean.js diff --git a/tasks/mkdocs/serve.js b/lib/tasks/mkdocs/serve.js similarity index 100% rename from tasks/mkdocs/serve.js rename to lib/tasks/mkdocs/serve.js diff --git a/lib/tasks/tests/unit.js b/lib/tasks/tests/unit.js new file mode 100644 index 000000000..d237ae9c3 --- /dev/null +++ b/lib/tasks/tests/unit.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Martin Donath + * + * 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 path from "path" +import { Server } from "karma" + +/* ---------------------------------------------------------------------------- + * Task: start karma test runner + * ------------------------------------------------------------------------- */ + +export default (gulp, config) => { + return cb => { + new Server({ + configFile: path.join(process.cwd(), config.tests.unit) + }, cb).start() + } +} diff --git a/tasks/tests/unit/watch.js b/lib/tasks/tests/unit/watch.js similarity index 98% rename from tasks/tests/unit/watch.js rename to lib/tasks/tests/unit/watch.js index c92197523..f0d728c21 100644 --- a/tasks/tests/unit/watch.js +++ b/lib/tasks/tests/unit/watch.js @@ -29,7 +29,6 @@ import { Server } from "karma" export default () => { return cb => { - process.env.GULP = true new Server({ configFile: path.join(process.cwd(), "tests/karma.conf.js") }, cb).start() diff --git a/tasks/views/build.js b/lib/tasks/views/build.js similarity index 100% rename from tasks/views/build.js rename to lib/tasks/views/build.js diff --git a/tasks/views/clean.js b/lib/tasks/views/clean.js similarity index 100% rename from tasks/views/clean.js rename to lib/tasks/views/clean.js diff --git a/package.json b/package.json index b92df663d..2b2318356 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ ], "homepage": "http://squidfunk.github.io/mkdocs-material/", "bugs": { - "url" : "https://github.com/squidfunk/mkdocs-material/issues", - "email" : "martin.donath@squidfunk.com" + "url": "https://github.com/squidfunk/mkdocs-material/issues", + "email": "martin.donath@squidfunk.com" }, "license": "MIT", "author": { @@ -25,7 +25,7 @@ "scripts": { "build": "./node_modules/.bin/gulp build --clean --optimize --revision", "clean": "./node_modules/.bin/gulp clean", - "pre-commit": "./node_modules/.bin/gulp assets:lint", + "pre-commit": "./node_modules/.bin/eslint . && ./node_modules/.bin/sass-lint -c .sass-lint.yml -vq", "start": "./node_modules/.bin/gulp watch --no-lint", "test": "./node_modules/.bin/gulp test" }, diff --git a/tests/.babelrc b/tests/.babelrc deleted file mode 100644 index 61a632adb..000000000 --- a/tests/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": ["es2015"], - "plugins": [ - "add-module-exports", - ["transform-react-jsx", { - "pragma": "createElement" - }] - ] -} \ No newline at end of file diff --git a/tests/karma.conf.js b/tests/karma.conf.js index 017a5132b..7cfde9669 100644 --- a/tests/karma.conf.js +++ b/tests/karma.conf.js @@ -20,6 +20,7 @@ * IN THE SOFTWARE. */ +const path = require("path") const webpack = require("webpack") /* ---------------------------------------------------------------------------- @@ -34,7 +35,7 @@ module.exports = karma => { /* Include babel polyfill to support older browsers */ files: [ "node_modules/babel-polyfill/dist/polyfill.js", - "tests/unit/*.spec.jsx" + "tests/unit/*.spec.+(js|jsx)" ], /* Test reporters */ @@ -47,27 +48,47 @@ module.exports = karma => { /* Preprocess test files */ preprocessors: { - "tests/unit/*.spec.jsx": ["webpack", "sourcemap"] + "tests/unit/*.spec.+(js|jsx)": ["webpack", "sourcemap"] }, /* Configuration for webpack */ webpack: { - devtool: "inline-source-map", - plugins: [ - - /* Inject DOM node creation helper for JSX support */ - new webpack.ProvidePlugin({ - createElement: "./_lib/create-element.js" - }) - ], module: { + + /* Transpile ES6 to ES5 with Babel */ loaders: [ { - test: /\.jsx?$/, - loader: "babel-loader" + loader: "babel-loader", + test: /\.jsx?$/ } ] - } + }, + plugins: [ + + /* Don't emit assets that include errors */ + new webpack.NoErrorsPlugin(), + + /* Provide JSX helper */ + new webpack.ProvidePlugin({ + JSX: path.join(process.cwd(), "lib/providers/jsx.js") + }) + ], + + /* Module resolver */ + resolve: { + modulesDirectories: [ + "src/assets/javascripts", + "node_modules" + ], + extensions: [ + "", + ".js", + ".jsx" + ] + }, + + /* Sourcemap support */ + devtool: "inline-source-map" }, /* Suppress messages by webpack */ @@ -85,12 +106,13 @@ module.exports = karma => { } /* Setup for continuous integration */ - if (process.env.CONTINUOUS_INTEGRATION) { + if (process.env.CI) { // TODO TBD /* Setup for local development environment */ - } else if (process.env.GULP) { + } else if (process.env.WATCH) { delete config.reporters + delete config.client.captureConsole /* Setup for local testing */ } else { diff --git a/tests/unit/Marker.spec.jsx b/tests/unit/Marker.spec.jsx index df335d9b3..b137d5ab1 100644 --- a/tests/unit/Marker.spec.jsx +++ b/tests/unit/Marker.spec.jsx @@ -1,24 +1,76 @@ +/* + * Copyright (c) 2016 Martin Donath + * + * 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 chai from "chai" +import Marker from + "../../src/assets/javascripts/components/Material/Sidebar/Marker" + describe("Karma test runner", function() { chai.should() - let sandbox = null - + // TODO: sandbox shit! should be okay once? beforeEach(function() { - sandbox = ( -
    - {[...Array(10)].map((x, i) => { - return
  • Element {i + 1}
  • + + for (let i = 0; i < 25; i++) + document.body.appendChild( +

    {i + 1}

    + ) + + document.body.appendChild( +
      + {[...Array(25)].map((_, i) => { + return ( +
    • + {i + 1} +
    • + ) })}
    ) }) - it("should compile JSX correctly", function() { - document.body.appendChild(sandbox) - return true - }) + it("should compile JSX correctly", + shouldCompileJSXCorrectly) }) +// iframe? +function shouldCompileJSXCorrectly(cb) { + const marker = new Marker("a") + marker.listen() + + // window.location.hash = "#_5" + // window.addEventListener("hashchange", function(e) { + // console.log(document.querySelectorAll("[data-md-marked]")) + // cb() + // }) + window.scrollTo(200, 200) + setTimeout(() => { + console.log(document.querySelectorAll("[data-md-marked]")) + cb() + }, 100) + + // console.log(marker) + // return true + +} +