Commit 94c3699d authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt

switch to fully webpack-powered frontend tooling

parent d07485cb
Pipeline #955 failed with stage
in 3 minutes and 44 seconds
{
"presets": [
["es2015", { "modules": false }],
"stage-2"
],
"plugins": [
"transform-runtime",
"lodash",
["babel-root-slash-import", { "rootPathSuffix": "res/js" }]
],
"comments": false,
"env": {
"test": {
"plugins": [ "istanbul" ]
}
}
}
......@@ -8,7 +8,6 @@ node_modules
static/
whoosh_index
res/img/backdrops
ASSET_VERSION
stadt/settings/local.py
*.swp
*.pyc
......@@ -25,3 +24,4 @@ debian/*.substvars
debian/files
stadtgestalten.egg-info/
/root/
core/templates/core/_assets.html
{
"minify": true,
"options": [
"setClasses"
],
"feature-detects": [
"test/inputtypes"
]
}
const { isDebug } = require('./webpack/env')
module.exports = ({ file, options }) => {
return {
plugins: {
'autoprefixer': isDebug ? false : options.autoprefixer || {},
'cssnano': isDebug ? false : options.cssnano || {}
}
}
}
......@@ -8,6 +8,7 @@ default-target: build
# include project makefiles
include make.d/virtualenv.mk
include make.d/app.mk
include make.d/nodejs.mk
include make.d/assets.mk
include make.d/test.mk
include make.d/dist.mk
......@@ -56,7 +56,6 @@ def _clean_source(src):
else:
url = _urlparse_to_dict(urlparse(static(src)))
qs = parse_qsl(url['query'])
qs.append(('v', settings.ASSET_VERSION))
url['query'] = urlencode(qs)
return _build_url(url)
......@@ -288,15 +287,4 @@ class CSPMiddleware(object):
# add javascript assets
add_javascript_reference('stadt/js/app.js')
add_javascript_inline('document.documentElement.setAttribute("class", "js")', stage='early')
# add stylesheet assets
add_style_reference('stadt/css/app.css')
# add manifest and application icons
add_link('stadt/config/manifest.json', rel='manifest')
for size in (16, 32, 48, 62, 144, 192):
add_link(
'stadt/img/logos/logo_%d.png' % size, 'icon', sizes=('%dx%d' % (size, size)),
type='image/png')
......@@ -21,6 +21,8 @@
{% include_assets 'early' %}
<script type="application/json" id="app-configuration">{% app_config %}</script>
{% comment %}core/_assets.html is generated by make assets{% endcomment %}
{% include "core/_assets.html" %}
</head>
<body data-component="browser-warning" class="month-{% now "m" %}{% block body_class %}{% endblock %}">
......
......@@ -3,7 +3,7 @@ Section: python
Priority: optional
Maintainer: Stadtgestalten Maintainers <wir@stadtgestalten.org>
Build-Depends: debhelper (>= 9), dh-exec, dh-python, dh-virtualenv,
python3-flake8, python3-all, python3-setuptools, yarn, nodejs, nodejs-legacy
python3-flake8, python3-all, python3-setuptools, nodejs, rsync, wget
Standards-Version: 3.9.6
Package: stadtgestalten
......
DIR_STATIC = static
DIR_STATIC = $(DIR_BUILD)/static
DIR_RES = res
DIR_LESS = $(DIR_RES)/less
DIR_JS = $(DIR_RES)/js
DIR_IMG = $(DIR_RES)/img
DIR_FONTS = $(DIR_RES)/fonts
DIR_NODE = node_modules
DIR_NODE_BIN ?= $(DIR_NODE)/.bin
BIN_YARN ?= yarn
BIN_NODE ?= node
BIN_STANDARD = $(DIR_NODE_BIN)/standard
BIN_POSTCSS = $(DIR_NODE_BIN)/postcss
BIN_LESSC = $(DIR_NODE_BIN)/lessc
BIN_WEBPACK = $(DIR_NODE_BIN)/webpack
BIN_SVGO = $(DIR_NODE_BIN)/svgo
BIN_FONTDUMP = $(DIR_NODE_BIN)/fontdump
BIN_MINIFY_JS = node -e "var fs = require('fs'); console.log(JSON.stringify(JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8'))))"
DEPS_LESS = $(shell find "$(DIR_LESS)" -type f -name "*.less")
DEPS_JS = $(shell find "$(DIR_JS)" -type f -name "*.js")
DEPS_IMG = $(shell find "$(DIR_IMG)" -type f -not -name "*.svg")
DEPS_SVG = $(shell find "$(DIR_IMG)" -type f -name "*.svg")
DEPS_MANIFEST = $(DIR_RES)/config/manifest.json
DEPS_ASSETS = $(shell find "$(DIR_RES)" -type f)
DEPS_FONTS = $(shell find "$(DIR_FONTS)" -type f)
# intermediate build files
BUILD_FONT_GOOGLE = $(DIR_BUILD)/fonts/google
BUILD_CSS_APP = $(DIR_BUILD)/css/app_unprefixed.css
# static output files
STATIC_CSS_APP = $(DIR_BUILD)/static/css/app.css
STATIC_JS_APP = $(DIR_BUILD)/static/js/app.js
STATIC_FONT = $(DIR_BUILD)/static/fonts
STATIC_FONT_GOOGLE = $(STATIC_FONT)/google
STATIC_FONT_FONTAWESOME = $(STATIC_FONT)/font-awesome
STATIC_IMG = $(DIR_BUILD)/static/img
STATIC_CONFIG_MANIFEST = $(DIR_BUILD)/static/config/manifest.json
URL_FONT_GOOGLE = https://fonts.googleapis.com/css?family=Roboto+Slab:300,400,700|Roboto:300,400,400i,500,700
$(DIR_NODE): package.json
$(DIR_NODE): $(BIN_NODE) package.json
@# in dh_auto_install yarn tries to create a cache folder in /usr/local/share/.cache
@# this has something to do with the environment dh_auto_install creates
@# so we call it with an empty environment to be on the safe side
env -i PATH="$$PATH" yarn install --no-progress
env -i PATH="$$PATH" "$(BIN_NODE)" "$(BIN_NODE_PKG)" install --no-progress
touch -c "$(DIR_NODE)"
$(STATIC_FONT_GOOGLE): $(DIR_NODE)
mkdir -p "$(BUILD_FONT_GOOGLE)"
$(BIN_FONTDUMP) --target-directory "$(BUILD_FONT_GOOGLE)" --web-directory "../fonts/google" "$(URL_FONT_GOOGLE)"
"$(BIN_NODE)" "$(BIN_FONTDUMP)" --target-directory "$(BUILD_FONT_GOOGLE)" --web-directory "." "$(URL_FONT_GOOGLE)"
mkdir -p "$(STATIC_FONT_GOOGLE)"
rsync -a --exclude "*.css" "$(BUILD_FONT_GOOGLE)/" "$(STATIC_FONT_GOOGLE)"
touch "$(STATIC_FONT_GOOGLE)"
$(STATIC_FONT_FONTAWESOME): $(DIR_NODE)
mkdir -p "$(STATIC_FONT_FONTAWESOME)"
rsync -a "$(DIR_NODE)/font-awesome/fonts/" "$(STATIC_FONT_FONTAWESOME)"
touch "$(STATIC_FONT_FONTAWESOME)"
$(STATIC_FONT): $(DEPS_FONT)
mkdir -p "$(STATIC_FONT)"
rsync -a "$(DIR_FONTS)/" "$(STATIC_FONT)"
touch "$(STATIC_FONT)"
$(STATIC_IMG): $(DIR_NODE) $(DEPS_IMG) $(DEPS_SVG)
mkdir -p "$(STATIC_IMG)"
rsync -a --exclude "*.svg" "$(DIR_IMG)/" "$(STATIC_IMG)"
for svg in $(DEPS_SVG); do \
echo "compressing svg '$$svg'"; \
"$(BIN_SVGO)" --multipass --quiet \
"$$svg" "$(STATIC_IMG)/$$(realpath --relative-to "$(DIR_IMG)" "$$svg")"; \
done
touch "$(STATIC_IMG)"
$(STATIC_CONFIG_MANIFEST): $(DEPS_MANIFEST)
mkdir -p "$$(dirname "$(STATIC_CONFIG_MANIFEST)")"
cat "$(DEPS_MANIFEST)" | $(BIN_MINIFY_JS) > "$(STATIC_CONFIG_MANIFEST)"
$(STATIC_CSS_APP): $(DIR_NODE) $(DEPS_LESS) $(BUILD_FONT_GOOGLE)
NODE_ENV=production $(BIN_LESSC) --strict-math=on --strict-units=on "$(DIR_LESS)/app.less" "$(BUILD_CSS_APP)"
NODE_ENV=production $(BIN_POSTCSS) --config postcss.config.js --output "$(STATIC_CSS_APP)" "$(BUILD_CSS_APP)"
$(STATIC_JS_APP): $(DIR_NODE) $(DEPS_JS)
NODE_ENV=production $(BIN_WEBPACK) --bail
$(DIR_STATIC): $(STATIC_FONT_GOOGLE) $(DEPS_ASSETS)
NODE_ENV=production "$(BIN_NODE)" $(BIN_WEBPACK)" --bail
touch "$(DIR_STATIC)"
.PHONY: assets_fonts
assets_fonts: $(STATIC_FONT) $(STATIC_FONT_GOOGLE) $(STATIC_FONT_FONTAWESOME)
.PHONY: assets_img
assets_img: $(STATIC_IMG)
.PHONY: assets_config
assets_config: $(STATIC_CONFIG_MANIFEST)
.PHONY: assets_css
assets_css: $(STATIC_CSS_APP)
assets_fonts: $(STATIC_FONT_GOOGLE)
.PHONY: assets_js
assets_js: $(STATIC_JS_APP)
.PHONY: assets_webpack
assets_webpack: $(DIR_STATIC)
.PHONY: assets
assets: assets_fonts assets_img assets_config assets_css assets_js
assets: assets_fonts assets_webpack
DIR_INSTALL_ROOT ?= usr/share/stadtgestalten
FILE_VERSION_ASSET = ASSET_VERSION
PYTHON_INSTALL_ARGS = --root "$(DESTDIR)" \
--install-lib="/$(DIR_INSTALL_ROOT)" \
......@@ -17,4 +16,3 @@ build: assets app_collect_static
.PHONY: install
install: build
git log --oneline res | head -n 1 | cut -f 1 -d " " > "$(DESTDIR)/$(DIR_INSTALL_ROOT)/$(FILE_VERSION_ASSET)"
BIN_NODE_SYSTEM = $(shell hash nodejs 2>/dev/null && echo nodejs || hash node 2>/dev/null && echo node)
BIN_NODE ?= $(DIR_BUILD)/node/bin/node
BIN_NODE_PKG ?= $(DIR_BUILD)/node/bin/npm
NODE_VERSION = $(shell hash "$(BIN_NODE_SYSTEM)" && "$(BIN_NODE_SYSTEM)" -v || echo "v0.0.0")
NODE_VERSION_MIN = v8.7.0
NODE_URL_X86 = https://nodejs.org/dist/$(NODE_VERSION_MIN)/node-$(NODE_VERSION_MIN)-linux-x86.tar.xz
NODE_URL_X64 = https://nodejs.org/dist/$(NODE_VERSION_MIN)/node-$(NODE_VERSION_MIN)-linux-x64.tar.xz
NODE_URL = $(shell [ "$$(uname -m)" = "x86_64" ] && echo "$(NODE_URL_X64)" || echo "$(NODE_URL_X64)")
NODE_TMP = $(DIR_BUILD)/node_tmp
NODE_DEST = $(DIR_BUILD)/node
assets_node_download:
rm -rf "$(NODE_DEST)"
mkdir -p "$(NODE_TMP)" "$(NODE_DEST)"
wget -O - "$(NODE_URL)" | tar xJ -C "$(NODE_TMP)" -f -
rsync -a "$(NODE_TMP)/$$(basename -s ".tar.xz" "$(NODE_URL)")/" "$(NODE_DEST)"
rm -rf "$(NODE_TMP)"
assets_node_system:
rm -rf "$(NODE_DEST)"
mkdir -p "$(NODE_DEST)/bin"
ln -s "$$(which $(BIN_NODE_SYSTEM))" "$(NODE_DEST)/bin/node"
ln -s "$$(which npm)" "$(NODE_DEST)/bin/npm"
$(BIN_NODE):
@if [ "$(NODE_VERSION)" = $$(echo -e "$(NODE_VERSION)\n$(NODE_VERSION_MIN)" | sort -V | head -n1) ] || ! hash npm 2>/dev/null; then \
echo "nodejs version is too old. downloading from server..."; \
$(MAKE) assets_node_download; \
else \
echo "system nodejs version is sufficient. linking system files..."; \
$(MAKE) assets_node_system; \
fi
......@@ -8,7 +8,7 @@ lint: lint_js lint-python lint_packages
.PHONY: lint_js
lint_js: $(DIR_NODE)
$(BIN_YARN) run lint
$(BIN_NODE_PKG) run lint
.PHONY: lint_packages
lint_packages:
......@@ -42,4 +42,4 @@ test_py: virtualenv_check
.PHONY: test_js
test_js: $(DIR_NODE) lint_js
$(BIN_YARN) run test
$(BIN_NODE_PKG) run test
......@@ -70,6 +70,6 @@
<canvas class="snake"></canvas>
</main>
<script src="/stadt/static/stadt/js/snake.js"></script>
<script src="/stadt/static/stadt/snake.js"></script>
</body>
</html>
This diff is collapsed.
{
"name": "stadtgestalten",
"title": "Stadtgestalten",
"description": "Stadtgestalten ist eine Plattform um lokale Vernetzung zu fördern",
"version": "2.0.1",
"license": "AGPL-3.0",
"engines": {
"node": ">=4.6.1"
"node": ">=6.4.0"
},
"scripts": {
"test": "true",
"lint": "standard --plugin html 'res/**/*.{js,vue}'"
"lint": "standard 'res/**/*.{js,vue}' 'webpack/**/*.js'"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.25.0",
"babel-eslint": "^8.0.1",
"babel-loader": "^7.1.1",
"babel-plugin-lodash": "^3.2.11",
"babel-plugin-transform-remove-debugger": "^6.8.4",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-plugin-transform-remove-console": "^6.8.5",
"babel-plugin-transform-remove-debugger": "^6.8.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-xregexp": "0.0.6",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.22.0",
"babel-root-slash-import": "^1.1.0",
"css-loader": "^0.28.4",
"csswring": "v4.2.3",
"eslint-plugin-html": "^3.1.1",
"extract-text-webpack-plugin": "^3.0.1",
"favicons-webpack-plugin": "0.0.7",
"file-loader": "^1.1.5",
"html-loader": "^0.5.1",
"html-webpack-plugin": "^2.30.1",
"json-loader": "^0.5.7",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"lodash-webpack-plugin": "^0.11.4",
"modernizr-loader": "^1.0.1",
"postcss": "^6.0.6",
"postcss-banner": ">=1",
"postcss-loader": "^2.0.8",
"standard": "^10.0.2",
"svgo": "^0.7.2",
"vue-loader": "^12.2.2",
"svgo-loader": "^1.2.1",
"vue-loader": "^13.3.0",
"vue-template-compiler": "^2.4.1",
"webpack": "^3.3.0",
"webpack-bundle-size-analyzer": "^2.7.0"
"webpack-bundle-analyzer": "^2.9.0",
"webpack-bundle-size-analyzer": "^2.7.0",
"webpack-pwa-manifest": "^3.3.2"
},
"dependencies": {
"animejs": "^2.0.2",
......@@ -55,6 +78,7 @@
"luett": "^2.3.1",
"masonry-layout": "^4.2.0",
"matches-selector-polyfill": "^1.0.0",
"modernizr": "^3.5.0",
"moment": "^2.13.0",
"node-fontdump": "^1.2.1",
"object-assign-shim": "^1.0.0",
......@@ -72,5 +96,52 @@
"vue": "^2.4.1",
"vue-on-click-outside": "^1.0.3",
"vue-tabs-component": "https://github.com/stadtgestalten/vue-tabs-component.git#dist"
}
},
"babel": {
"plugins": [
"lodash",
"syntax-dynamic-import",
"transform-async-to-generator",
"transform-es2015-unicode-regex",
"transform-regenerator",
"transform-runtime",
"transform-xregexp"
],
"presets": [
"stage-2",
[
"env",
{
"loose": true,
"useBuiltIns": true,
"modules": false,
"targets": {
"browsers": [
"last 2 versions",
"> 4%",
"Firefox ESR",
"IE 11"
]
}
}
]
]
},
"standard": {
"parser": "babel-eslint",
"envs": [
"browser",
"node",
"mocha"
],
"plugins": [
"html"
]
},
"browserslist": [
"last 2 versions",
"> 4%",
"Firefox ESR",
"IE 11"
]
}
'use strict'
const fs = require('fs')
const banner = fs.readFileSync('res/templates/banner.txt', 'utf-8')
module.exports = {
plugins: [
require('autoprefixer')({
browsers: ['last 2 versions', 'ie 11', '> 5%']
}),
require('postcss-banner')({
banner: banner
}),
require('csswring')
]
}
{
"lang": "de",
"background_color": "#ffffff",
"theme_color": "#2a62ac",
"name": "Stadtgestalten",
"short_name": "Stadtgestalten",
"description": "Stadtgestalten ist eine neue Internet-Plattform und ermöglicht aktiven, engagierten Menschen und Gruppen in der Stadt, sich unkompliziert zu informieren, zu präsentieren und miteinander zu vernetzen.",
"display": "standalone",
"icons": [
{
"src": "/static/stadt/img/logos/logo_16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_62.png",
"sizes": "62x62",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/stadt/img/logos/logo_512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
......@@ -33,7 +33,7 @@
&:before {
width: 15%;
height: 15%;
.stripes(@color-text-default, transparent, "../img/stripe.png");
.stripes(@color-text-default, transparent, "../../img/stripe.png");
}
&:after {
......
......@@ -49,7 +49,7 @@
position: absolute;
width: 100%;
height: 100%;
background: url("../img/xmas_hat.png") no-repeat 0 0;
background: url("../../img/xmas_hat.png") no-repeat 0 0;
background-size: contain;
top: -40%;
right: -23%;
......
......@@ -345,7 +345,7 @@
justify-content: space-around;
border: none;
color: white;
background: url(../img/button/triangle.svg) repeat 0 -96px transparent;
background: url(../../img/button/triangle.svg) repeat 0 -96px transparent;
right: -.5rem;
&:hover {
......
......@@ -10,7 +10,7 @@
}
.decoration-striped {
.stripes(@color-text-default, white, "../img/stripe.png");
.stripes(@color-text-default, white, "../../img/stripe.png");
}
.decoration-icon {
......
......@@ -45,7 +45,7 @@ label {
height: 6rem;
width: 6rem;
transform: translateX(-50%);
background: url("../img/logos/logo_large.svg");
background: url("../../img/logos/logo_large.svg");
background-size: contain;
border-radius: 2px;
}
......
......@@ -22,7 +22,7 @@
}
.page-footer-content {
background: url("../img/logos/logo_white.svg") no-repeat 2rem 2rem rgba(255, 255, 255, .8);
background: url("../../img/logos/logo_white.svg") no-repeat 2rem 2rem rgba(255, 255, 255, .8);
background-size: 5rem auto;
padding-left: 9rem;
min-height: 7rem;
......
......@@ -104,12 +104,12 @@
}
&:before {
.stripes(@color-text-default, transparent, "../img/stripe.png");
.stripes(@color-text-default, transparent, "../../img/stripe.png");
top: 0;
}
&:after {
.stripes(white, transparent, "../img/stripe_white.png");
.stripes(white, transparent, "../../img/stripe_white.png");
background-position: -2px 0;
top: 10px;
clear: both;
......
// import fontawesome completely
@import "../../../node_modules/font-awesome/less/font-awesome";
@fa-font-path: "../fonts/font-awesome";
......@@ -2,10 +2,10 @@
font-family: "Roboto";
font-weight: normal;
font-style: normal;
src: url("../fonts/noto_color_emoji/NotoColorEmoji.ttf") format("truetype"),
url("../fonts/noto_emoji/NotoEmoji-Regular.ttf") format("truetype"),
url("../fonts/noto_emoji/notoemoji-regular-webfont.woff") format("woff"),
url("../fonts/noto_emoji/notoemoji-regular-webfont.woff2") format("woff2");
src: url("../../fonts/noto_color_emoji/NotoColorEmoji.ttf") format("truetype"),
url("../../fonts/noto_emoji/NotoEmoji-Regular.ttf") format("truetype"),
url("../../fonts/noto_emoji/notoemoji-regular-webfont.woff") format("woff"),