Migrate build pipeline to Webpack 2

This commit is contained in:
squidfunk 2017-02-11 16:00:45 +01:00 committed by Martin Donath
parent 7c36b5c4ef
commit 80fe7e010d
8 changed files with 219 additions and 281 deletions

View File

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

View File

@ -50,12 +50,13 @@ export default (gulp, config, args) => {
], ],
output: { output: {
filename: "application.js", filename: "application.js",
library: "Application" library: "app",
libraryTarget: "window"
}, },
module: { module: {
/* Transpile ES6 to ES5 with Babel */ /* Transpile ES6 to ES5 with Babel */
loaders: [ rules: [
{ {
loader: "babel-loader", loader: "babel-loader",
test: /\.jsx?$/ test: /\.jsx?$/
@ -65,7 +66,7 @@ export default (gulp, config, args) => {
plugins: [ plugins: [
/* Don't emit assets that include errors */ /* Don't emit assets that include errors */
new webpack.NoErrorsPlugin(), new webpack.NoEmitOnErrorsPlugin(),
/* Provide JSX helper */ /* Provide JSX helper */
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
@ -77,19 +78,30 @@ export default (gulp, config, args) => {
args.optimize ? [ args.optimize ? [
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
compress: { compress: {
warnings: false warnings: false,
screw_ie8: true, // eslint-disable-line camelcase
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true, // eslint-disable-line camelcase
evaluate: true,
if_return: true, // eslint-disable-line camelcase
join_vars: true // eslint-disable-line camelcase
},
output: {
comments: false
} }
}) })
] : []), ] : []),
/* Module resolver */ /* Module resolver */
resolve: { resolve: {
modulesDirectories: [ modules: [
"src/assets/javascripts", "src/assets/javascripts",
"node_modules" "node_modules"
], ],
extensions: [ extensions: [
"",
".js", ".js",
".jsx" ".jsx"
] ]
@ -102,7 +114,7 @@ export default (gulp, config, args) => {
/* Sourcemap support */ /* Sourcemap support */
devtool: args.sourcemaps ? "inline-source-map" : "" devtool: args.sourcemaps ? "inline-source-map" : ""
})) }, webpack))
/* Revisioning */ /* Revisioning */
.pipe(gulpif(args.revision, rev())) .pipe(gulpif(args.revision, rev()))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -124,8 +124,8 @@
{% endblock %} {% endblock %}
</div> </div>
{% block scripts %} {% block scripts %}
<script src="{{ base_url }}/assets/javascripts/application-7f35cda7dd.js"></script> <script src="{{ base_url }}/assets/javascripts/application-39a173f6f5.js"></script>
<script>var config={url:{base:"{{ base_url }}"}},app=new Application(config);app.initialize()</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>
{% endfor %} {% endfor %}

10
src/.babelrc Normal file
View File

@ -0,0 +1,10 @@
{
"presets": [
["es2015", { "modules": false }]
],
"plugins": [
["transform-react-jsx", {
"pragma": "JSX.createElement"
}]
]
}

View File

@ -27,215 +27,199 @@ import Material from "./components/Material"
* Application * Application
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
export default class Application { export const initialize = config => {
/** /* Initialize Modernizr and FastClick */
* Create the application new Material.Event.Listener(document, "DOMContentLoaded", () => {
*
* @constructor
* @param {object} config Configuration object
*/
constructor(config) {
this.config_ = config
}
/** /* Test for iOS */
* Initialize all components and listeners Modernizr.addTest("ios", () => {
*/ return !!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)
initialize() { })
/* Initialize Modernizr and FastClick */ /* Test for web application context */
new Material.Event.Listener(document, "DOMContentLoaded", () => { Modernizr.addTest("standalone", () => {
return !!navigator.standalone
})
/* Test for iOS */ /* Attach FastClick to mitigate 300ms delay on touch devices */
Modernizr.addTest("ios", () => { FastClick.attach(document.body)
return !!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)
})
/* Test for web application context */ /* Wrap all data tables for better overflow scrolling */
Modernizr.addTest("standalone", () => { const tables = document.querySelectorAll("table:not([class])")
return !!navigator.standalone Array.prototype.forEach.call(tables, table => {
}) const wrap = document.createElement("div")
wrap.classList.add("md-typeset__table")
if (table.nextSibling) {
table.parentNode.insertBefore(wrap, table.nextSibling)
} else {
table.parentNode.appendChild(wrap)
}
wrap.appendChild(table)
})
/* Attack FastClick to mitigate 300ms delay on touch devices */ /* Force 1px scroll offset to trigger overflow scrolling */
FastClick.attach(document.body) if (Modernizr.ios) {
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
Array.prototype.forEach.call(scrollable, item => {
item.addEventListener("touchstart", () => {
const top = item.scrollTop
/* Wrap all data tables for better overflow scrolling */ /* We're at the top of the container */
const tables = document.querySelectorAll("table:not([class])") if (top === 0) {
Array.prototype.forEach.call(tables, table => { item.scrollTop = 1
const wrap = document.createElement("div")
wrap.classList.add("md-typeset__table")
if (table.nextSibling) {
table.parentNode.insertBefore(wrap, table.nextSibling)
} else {
table.parentNode.appendChild(wrap)
}
wrap.appendChild(table)
})
/* Force 1px scroll offset to trigger overflow scrolling */
if (Modernizr.ios) {
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
Array.prototype.forEach.call(scrollable, item => {
item.addEventListener("touchstart", () => {
const top = item.scrollTop
/* We're at the top of the container */
if (top === 0) {
item.scrollTop = 1
/* We're at the bottom of the container */ /* We're at the bottom of the container */
} else if (top + item.offsetHeight === item.scrollHeight) { } else if (top + item.offsetHeight === item.scrollHeight) {
item.scrollTop = top - 1 item.scrollTop = top - 1
}
})
})
}
}).listen()
/* Component: sidebar container */
if (!Modernizr.csscalc)
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, [
"resize", "orientationchange"
], new Material.Sidebar.Container("[data-md-component=container]")))
/* Component: sidebar with navigation */
new Material.Event.MatchMedia("(min-width: 1220px)",
new Material.Event.Listener(window, [
"scroll", "resize", "orientationchange"
], new Material.Sidebar.Position("[data-md-component=navigation]")))
/* Component: sidebar with table of contents */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, [
"scroll", "resize", "orientationchange"
], new Material.Sidebar.Position("[data-md-component=toc]")))
/* Component: link blurring for table of contents */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, "scroll",
new Material.Nav.Blur("[data-md-component=toc] .md-nav__link")))
/* Component: collapsible elements for navigation */
const collapsibles =
document.querySelectorAll("[data-md-component=collapsible]")
Array.prototype.forEach.call(collapsibles, collapse => {
new Material.Event.MatchMedia("(min-width: 1220px)",
new Material.Event.Listener(collapse.previousElementSibling, "click",
new Material.Nav.Collapse(collapse)))
})
/* Component: active pane monitor for iOS scrolling fixes */
new Material.Event.MatchMedia("(max-width: 1219px)",
new Material.Event.Listener(
"[data-md-component=navigation] [data-md-toggle]", "change",
new Material.Nav.Scrolling("[data-md-component=navigation] nav")))
/* Component: search body lock for mobile */
new Material.Event.MatchMedia("(max-width: 959px)",
new Material.Event.Listener("[data-md-toggle=search]", "change",
new Material.Search.Lock("[data-md-toggle=search]")))
/* Component: search results */
new Material.Event.Listener(document.forms.search.query, [
"focus", "keyup"
], new Material.Search.Result("[data-md-component=result]", () => {
return fetch(`${this.config_.url.base}/mkdocs/search_index.json`, {
credentials: "same-origin"
}).then(response => response.json())
.then(data => {
return data.docs.map(doc => {
doc.location = this.config_.url.base + doc.location
return doc
})
})
})).listen()
/* Listener: prevent touches on overlay if navigation is active */
new Material.Event.MatchMedia("(max-width: 1219px)",
new Material.Event.Listener("[data-md-component=overlay]", "touchstart",
ev => ev.preventDefault()))
/* Listener: close drawer when anchor links are clicked */
new Material.Event.MatchMedia("(max-width: 959px)",
new Material.Event.Listener("[data-md-component=navigation] [href^='#']",
"click", () => {
const toggle = document.querySelector("[data-md-toggle=drawer]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
} }
})) })
/* Listener: focus input after opening search */
new Material.Event.Listener("[data-md-toggle=search]", "change", ev => {
setTimeout(toggle => {
const query = document.forms.search.query
if (toggle.checked)
query.focus()
}, 400, ev.target)
}).listen()
/* Listener: open search on focus */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(document.forms.search.query, "focus", () => {
const toggle = document.querySelector("[data-md-toggle=search]")
if (!toggle.checked) {
toggle.checked = true
toggle.dispatchEvent(new CustomEvent("change"))
}
}))
/* Listener: close search when clicking outside */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(document.body, "click", () => {
const toggle = document.querySelector("[data-md-toggle=search]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
}
}))
/* Listener: disable search when ESC key is pressed */
new Material.Event.Listener(window, "keyup", ev => {
const code = ev.keyCode || ev.which
if (code === 27) {
const toggle = document.querySelector("[data-md-toggle=search]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
document.forms.search.query.blur()
}
}
}).listen()
/* Listener: fix unclickable toggle due to blur handler */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener("[data-md-toggle=search]", "click",
ev => ev.stopPropagation()))
/* Listener: prevent search from closing when clicking */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener("[data-md-component=search]", "click",
ev => ev.stopPropagation()))
/* Retrieve facts for the given repository type */
;(() => {
const el = document.querySelector("[data-md-source]")
if (!el) return Promise.resolve([])
switch (el.dataset.mdSource) {
case "github": return new Material.Source.Adapter.GitHub(el).fetch()
default: return Promise.resolve([])
}
/* Render repository source information */
})().then(facts => {
const sources = document.querySelectorAll("[data-md-source]")
Array.prototype.forEach.call(sources, source => {
new Material.Source.Repository(source)
.initialize(facts)
}) })
}
}).listen()
/* Component: sidebar container */
if (!Modernizr.csscalc)
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, [
"resize", "orientationchange"
], new Material.Sidebar.Container("[data-md-component=container]")))
/* Component: sidebar with navigation */
new Material.Event.MatchMedia("(min-width: 1220px)",
new Material.Event.Listener(window, [
"scroll", "resize", "orientationchange"
], new Material.Sidebar.Position("[data-md-component=navigation]")))
/* Component: sidebar with table of contents */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, [
"scroll", "resize", "orientationchange"
], new Material.Sidebar.Position("[data-md-component=toc]")))
/* Component: link blurring for table of contents */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(window, "scroll",
new Material.Nav.Blur("[data-md-component=toc] .md-nav__link")))
/* Component: collapsible elements for navigation */
const collapsibles =
document.querySelectorAll("[data-md-component=collapsible]")
Array.prototype.forEach.call(collapsibles, collapse => {
new Material.Event.MatchMedia("(min-width: 1220px)",
new Material.Event.Listener(collapse.previousElementSibling, "click",
new Material.Nav.Collapse(collapse)))
})
/* Component: active pane monitor for iOS scrolling fixes */
new Material.Event.MatchMedia("(max-width: 1219px)",
new Material.Event.Listener(
"[data-md-component=navigation] [data-md-toggle]", "change",
new Material.Nav.Scrolling("[data-md-component=navigation] nav")))
/* Component: search body lock for mobile */
new Material.Event.MatchMedia("(max-width: 959px)",
new Material.Event.Listener("[data-md-toggle=search]", "change",
new Material.Search.Lock("[data-md-toggle=search]")))
/* Component: search results */
new Material.Event.Listener(document.forms.search.query, [
"focus", "keyup"
], new Material.Search.Result("[data-md-component=result]", () => {
return fetch(`${config.url.base}/mkdocs/search_index.json`, {
credentials: "same-origin"
}).then(response => response.json())
.then(data => {
return data.docs.map(doc => {
doc.location = config.url.base + doc.location
return doc
})
})
})).listen()
/* Listener: prevent touches on overlay if navigation is active */
new Material.Event.MatchMedia("(max-width: 1219px)",
new Material.Event.Listener("[data-md-component=overlay]", "touchstart",
ev => ev.preventDefault()))
/* Listener: close drawer when anchor links are clicked */
new Material.Event.MatchMedia("(max-width: 959px)",
new Material.Event.Listener("[data-md-component=navigation] [href^='#']",
"click", () => {
const toggle = document.querySelector("[data-md-toggle=drawer]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
}
}))
/* Listener: focus input after opening search */
new Material.Event.Listener("[data-md-toggle=search]", "change", ev => {
setTimeout(toggle => {
const query = document.forms.search.query
if (toggle.checked)
query.focus()
}, 400, ev.target)
}).listen()
/* Listener: open search on focus */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(document.forms.search.query, "focus", () => {
const toggle = document.querySelector("[data-md-toggle=search]")
if (!toggle.checked) {
toggle.checked = true
toggle.dispatchEvent(new CustomEvent("change"))
}
}))
/* Listener: close search when clicking outside */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener(document.body, "click", () => {
const toggle = document.querySelector("[data-md-toggle=search]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
}
}))
/* Listener: disable search when ESC key is pressed */
new Material.Event.Listener(window, "keyup", ev => {
const code = ev.keyCode || ev.which
if (code === 27) {
const toggle = document.querySelector("[data-md-toggle=search]")
if (toggle.checked) {
toggle.checked = false
toggle.dispatchEvent(new CustomEvent("change"))
document.forms.search.query.blur()
}
}
}).listen()
/* Listener: fix unclickable toggle due to blur handler */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener("[data-md-toggle=search]", "click",
ev => ev.stopPropagation()))
/* Listener: prevent search from closing when clicking */
new Material.Event.MatchMedia("(min-width: 960px)",
new Material.Event.Listener("[data-md-component=search]", "click",
ev => ev.stopPropagation()))
/* Retrieve facts for the given repository type */
;(() => {
const el = document.querySelector("[data-md-source]")
if (!el) return Promise.resolve([])
switch (el.dataset.mdSource) {
case "github": return new Material.Source.Adapter.GitHub(el).fetch()
default: return Promise.resolve([])
}
/* Render repository source information */
})().then(facts => {
const sources = document.querySelectorAll("[data-md-source]")
Array.prototype.forEach.call(sources, source => {
new Material.Source.Repository(source)
.initialize(facts)
}) })
} })
} }

View File

@ -245,17 +245,7 @@
{% block scripts %} {% block scripts %}
<script src="{{ base_url }}/assets/javascripts/application.js"></script> <script src="{{ base_url }}/assets/javascripts/application.js"></script>
<script> <script>
app.initialize({ url: { base: "{{ base_url }}", } });
/* Configuration for application */
var config = {
url: {
base: "{{ base_url }}",
}
};
/* Initialize application */
var app = new Application(config);
app.initialize();
</script> </script>
{% for path in extra_javascript %} {% for path in extra_javascript %}
<script src="{{ path }}"></script> <script src="{{ path }}"></script>