Added full JSX support to build pipeline

This commit is contained in:
squidfunk
2016-10-10 12:35:25 +02:00
parent 3e47149fe4
commit 7b585f191a
27 changed files with 229 additions and 107 deletions

View File

@@ -1,6 +1,9 @@
{ {
"presets": ["es2015"], "presets": ["es2015"],
"plugins": [ "plugins": [
"add-module-exports" "add-module-exports",
["transform-react-jsx", {
"pragma": "JSX.createElement"
}]
] ]
} }

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
/build
/material
/site

View File

@@ -20,6 +20,7 @@
files: files:
ignore: ignore:
- node_modules/**
- src/assets/stylesheets/_shame.scss - src/assets/stylesheets/_shame.scss
- src/assets/stylesheets/extensions/_codehilite.scss # Temporary disabled - src/assets/stylesheets/extensions/_codehilite.scss # Temporary disabled

View File

@@ -35,6 +35,7 @@ const config = {
src: "src/assets", /* Source directory for assets */ src: "src/assets", /* Source directory for assets */
build: "material/assets" /* Target directory for assets */ build: "material/assets" /* Target directory for assets */
}, },
lib: "lib", /* Libraries */
views: { views: {
src: "src", /* Source directory for views */ src: "src", /* Source directory for views */
build: "material" /* Target directory for views */ build: "material" /* Target directory for views */
@@ -43,11 +44,11 @@ const config = {
const args = yargs const args = yargs
.default("clean", false) /* Clean before build */ .default("clean", false) /* Clean before build */
.default("karma", false) /* Karma watchdog */ .default("karma", true) /* Karma watchdog */
.default("lint", true) /* Lint sources */ .default("lint", true) /* Lint sources */
.default("mkdocs", false) /* MkDocs watchdog */ .default("mkdocs", true) /* MkDocs watchdog */
.default("optimize", true) /* Optimize sources */ .default("optimize", false) /* Optimize sources */
.default("revision", true) /* Revision assets */ .default("revision", false) /* Revision assets */
.default("sourcemaps", false) /* Create sourcemaps */ .default("sourcemaps", false) /* Create sourcemaps */
.argv .argv
@@ -91,7 +92,7 @@ gulp.src = (...glob) => {
* Helper function to load a task * Helper function to load a task
*/ */
const load = 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" "assets:stylesheets:clean"
]) ])
/*
* Lint sources
*/
gulp.task("assets:lint", [
"assets:javascripts:lint",
"assets:stylesheets:lint"
])
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
* Views * Views
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
@@ -317,6 +310,7 @@ gulp.task("watch", [
"assets:build", "assets:build",
"views:build" "views:build"
], () => { ], () => {
process.env.WATCH = true
/* Start MkDocs server */ /* Start MkDocs server */
if (args.mkdocs) if (args.mkdocs)

View File

@@ -24,50 +24,46 @@
* Definition * Definition
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
/** export default /* JSX */ {
* 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)
/* Set all properties */ /**
for (const attr of Object.keys(properties)) * Create a native DOM node
el.setAttribute(attr, properties[attr]) *
* @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 */ /* Set all properties */
const iterateChildNodes = nodes => { if (properties)
for (const node of nodes) { for (const attr of Object.keys(properties))
el.setAttribute(attr, properties[attr])
/* Directly append content */ /* Iterate child nodes */
if (typeof node === "string" || const iterateChildNodes = nodes => {
typeof node === "number") { for (const node of nodes) {
el.textContent += node
/* Recurse, if we got an array */ /* Directly append text content */
} else if (Array.isArray(node)) { if (typeof node === "string" ||
iterateChildNodes(node) typeof node === "number") {
el.textContent += node
/* Append regular nodes */ /* Recurse, if we got an array */
} else { } else if (Array.isArray(node)) {
el.appendChild(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

5
lib/tasks/.eslintrc Normal file
View File

@@ -0,0 +1,5 @@
{
"rules": {
"no-invalid-this": 0
}
}

View File

@@ -35,7 +35,7 @@ export default (gulp, config, args) => {
return () => { return () => {
return gulp.src(`${config.assets.src}/javascripts/**/*.js`) return gulp.src(`${config.assets.src}/javascripts/**/*.js`)
/* Transpile with webpack */ /* Build with webpack */
.pipe( .pipe(
stream({ stream({
entry: "application.js", entry: "application.js",
@@ -44,17 +44,27 @@ export default (gulp, config, args) => {
library: "Application" library: "Application"
}, },
module: { module: {
/* Transpile ES6 to ES5 with Babel */
loaders: [ loaders: [
{ {
loader: "babel-loader", loader: "babel-loader",
test: path.join(process.cwd(), test: /\.jsx?$/
`${config.assets.src}/javascripts`)
} }
] ]
}, },
plugins: [ 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( ].concat(
/* Minify sources */
args.optimize ? [ args.optimize ? [
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
compress: { compress: {
@@ -62,20 +72,30 @@ export default (gulp, config, args) => {
} }
}) })
] : []), ] : []),
stats: {
colors: true /* Module resolver */
},
resolve: { resolve: {
modulesDirectories: [ modulesDirectories: [
"src/assets/javascripts", "src/assets/javascripts",
"node_modules" "node_modules"
], ],
extensions: [ extensions: [
"", ".js" "",
".js",
".jsx"
] ]
}, },
/* Webpack commandline output */
stats: {
colors: true
},
/* Sourcemap support */
devtool: args.sourcemaps ? "source-map" : "" devtool: args.sourcemaps ? "source-map" : ""
})) }))
/* Revisioning */
.pipe(gulpif(args.revision, rev())) .pipe(gulpif(args.revision, rev()))
.pipe(gulpif(args.revision, .pipe(gulpif(args.revision,
version({ manifest: gulp.src("manifest.json") }))) version({ manifest: gulp.src("manifest.json") })))

View File

@@ -54,7 +54,7 @@ export default (gulp, config) => {
file.eslint.results[0].filePath = file.eslint.results[0].filePath =
path.relative(process.cwd(), file.path) path.relative(process.cwd(), file.path)
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
})) }))
@@ -67,7 +67,7 @@ export default (gulp, config) => {
console.log(format(file.eslint.results)) console.log(format(file.eslint.results))
} }
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
})) }))
@@ -85,7 +85,7 @@ export default (gulp, config) => {
if (results.errorCount || results.warningCount) if (results.errorCount || results.warningCount)
errors.push(file) errors.push(file)
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
@@ -96,7 +96,7 @@ export default (gulp, config) => {
return file.relative return file.relative
}).join(", ") }).join(", ")
// eslint-disable-next-line no-invalid-this /* Emit error */
this.emit("error", new util.PluginError("eslint", this.emit("error", new util.PluginError("eslint",
`Terminated with errors in files: ${message}`)) `Terminated with errors in files: ${message}`))
} }

View File

@@ -29,7 +29,7 @@ import util from "gulp-util"
* Task: lint SASS sources * Task: lint SASS sources
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
export default (gulp, config, args) => { export default (gulp, config) => {
return () => { return () => {
return gulp.src(`${config.assets.src}/stylesheets/**/*.scss`) return gulp.src(`${config.assets.src}/stylesheets/**/*.scss`)
@@ -46,7 +46,7 @@ export default (gulp, config, args) => {
filename: path.relative(process.cwd(), file.path) filename: path.relative(process.cwd(), file.path)
}) })
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
})) }))
@@ -56,7 +56,7 @@ export default (gulp, config, args) => {
through.obj(function(file, enc, cb) { through.obj(function(file, enc, cb) {
sasslint.outputResults([file.sasslint]) sasslint.outputResults([file.sasslint])
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
})) }))
@@ -74,7 +74,7 @@ export default (gulp, config, args) => {
if (results.errorCount || results.warningCount) if (results.errorCount || results.warningCount)
errors.push(file) errors.push(file)
// eslint-disable-next-line no-invalid-this /* Push file to next stage */
this.push(file) this.push(file)
cb() cb()
@@ -85,7 +85,7 @@ export default (gulp, config, args) => {
return file.relative return file.relative
}).join(", ") }).join(", ")
// eslint-disable-next-line no-invalid-this /* Emit error */
this.emit("error", new util.PluginError("eslint", this.emit("error", new util.PluginError("eslint",
`Terminated with errors in files: ${message}`)) `Terminated with errors in files: ${message}`))
} }

36
lib/tasks/tests/unit.js Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2016 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 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()
}
}

View File

@@ -29,7 +29,6 @@ import { Server } from "karma"
export default () => { export default () => {
return cb => { return cb => {
process.env.GULP = true
new Server({ new Server({
configFile: path.join(process.cwd(), "tests/karma.conf.js") configFile: path.join(process.cwd(), "tests/karma.conf.js")
}, cb).start() }, cb).start()

View File

@@ -9,8 +9,8 @@
], ],
"homepage": "http://squidfunk.github.io/mkdocs-material/", "homepage": "http://squidfunk.github.io/mkdocs-material/",
"bugs": { "bugs": {
"url" : "https://github.com/squidfunk/mkdocs-material/issues", "url": "https://github.com/squidfunk/mkdocs-material/issues",
"email" : "martin.donath@squidfunk.com" "email": "martin.donath@squidfunk.com"
}, },
"license": "MIT", "license": "MIT",
"author": { "author": {
@@ -25,7 +25,7 @@
"scripts": { "scripts": {
"build": "./node_modules/.bin/gulp build --clean --optimize --revision", "build": "./node_modules/.bin/gulp build --clean --optimize --revision",
"clean": "./node_modules/.bin/gulp clean", "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", "start": "./node_modules/.bin/gulp watch --no-lint",
"test": "./node_modules/.bin/gulp test" "test": "./node_modules/.bin/gulp test"
}, },

View File

@@ -1,9 +0,0 @@
{
"presets": ["es2015"],
"plugins": [
"add-module-exports",
["transform-react-jsx", {
"pragma": "createElement"
}]
]
}

View File

@@ -20,6 +20,7 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
const path = require("path")
const webpack = require("webpack") const webpack = require("webpack")
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
@@ -34,7 +35,7 @@ module.exports = karma => {
/* Include babel polyfill to support older browsers */ /* Include babel polyfill to support older browsers */
files: [ files: [
"node_modules/babel-polyfill/dist/polyfill.js", "node_modules/babel-polyfill/dist/polyfill.js",
"tests/unit/*.spec.jsx" "tests/unit/*.spec.+(js|jsx)"
], ],
/* Test reporters */ /* Test reporters */
@@ -47,27 +48,47 @@ module.exports = karma => {
/* Preprocess test files */ /* Preprocess test files */
preprocessors: { preprocessors: {
"tests/unit/*.spec.jsx": ["webpack", "sourcemap"] "tests/unit/*.spec.+(js|jsx)": ["webpack", "sourcemap"]
}, },
/* Configuration for webpack */ /* Configuration for webpack */
webpack: { webpack: {
devtool: "inline-source-map",
plugins: [
/* Inject DOM node creation helper for JSX support */
new webpack.ProvidePlugin({
createElement: "./_lib/create-element.js"
})
],
module: { module: {
/* Transpile ES6 to ES5 with Babel */
loaders: [ 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 */ /* Suppress messages by webpack */
@@ -85,12 +106,13 @@ module.exports = karma => {
} }
/* Setup for continuous integration */ /* Setup for continuous integration */
if (process.env.CONTINUOUS_INTEGRATION) { if (process.env.CI) {
// TODO TBD // TODO TBD
/* Setup for local development environment */ /* Setup for local development environment */
} else if (process.env.GULP) { } else if (process.env.WATCH) {
delete config.reporters delete config.reporters
delete config.client.captureConsole
/* Setup for local testing */ /* Setup for local testing */
} else { } else {

View File

@@ -1,24 +1,76 @@
/*
* Copyright (c) 2016 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 chai from "chai" import chai from "chai"
import Marker from
"../../src/assets/javascripts/components/Material/Sidebar/Marker"
describe("Karma test runner", function() { describe("Karma test runner", function() {
chai.should() chai.should()
let sandbox = null // TODO: sandbox shit! should be okay once?
beforeEach(function() { beforeEach(function() {
sandbox = (
<ul class="list"> for (let i = 0; i < 25; i++)
{[...Array(10)].map((x, i) => { document.body.appendChild(
return <li class={`foo-${i + 1}`}>Element {i + 1}</li> <p id={`anchor_${i + 1}`}>{i + 1}</p>
)
document.body.appendChild(
<ul>
{[...Array(25)].map((_, i) => {
return (
<li>
<a href={`#anchor_${i + 1}`}>{i + 1}</a>
</li>
)
})} })}
</ul> </ul>
) )
}) })
it("should compile JSX correctly", function() { it("should compile JSX correctly",
document.body.appendChild(sandbox) shouldCompileJSXCorrectly)
return true
})
}) })
// 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
}