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"],
"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:
ignore:
- node_modules/**
- src/assets/stylesheets/_shame.scss
- src/assets/stylesheets/extensions/_codehilite.scss # Temporary disabled

View File

@ -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)

View File

@ -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

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 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") })))

View File

@ -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}`))
}

View File

@ -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}`))
}

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 () => {
return cb => {
process.env.GULP = true
new Server({
configFile: path.join(process.cwd(), "tests/karma.conf.js")
}, cb).start()

View File

@ -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"
},

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.
*/
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 {

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 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 = (
<ul class="list">
{[...Array(10)].map((x, i) => {
return <li class={`foo-${i + 1}`}>Element {i + 1}</li>
for (let i = 0; i < 25; i++)
document.body.appendChild(
<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>
)
})
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
}