diff --git a/.gitignore b/.gitignore index 1d59fb6..f9ad5b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ __pycache__/ db.sqlite3 + +webpack_bundles/ diff --git a/Dockerfile b/Dockerfile index 1227f21..69a3c12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,10 +4,11 @@ RUN mkdir -p /_build/ WORKDIR /_build/ ADD . /_build/ -RUN apt-get update +RUN apt update -y -RUN apt-get install -y \ +RUN apt install -y \ sudo \ + curl \ build-essential \ python3-dev \ python3-pip \ @@ -15,6 +16,8 @@ RUN apt-get install -y \ RUN make create_user +RUN make setup_node + RUN mkdir /var/run/sshd RUN rm -Rf /_build/ diff --git a/Makefile b/Makefile index 7b80b39..e04882b 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,18 @@ SHELL := /bin/bash create_user: source scripts/build/create_user.sh + +setup_node: + curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - + apt install -y nodejs + npm install -g npm + +vue_dev: + rm -rf pythonph/assets/webpack_bundles/*; + cd frontend; npm run build_dev; cd -; + make assets + +vue: + rm -rf pythonph/assets/webpack_bundles/*; + cd frontend; npm run build; cd -; + make assets diff --git a/README.md b/README.md index cfbb99b..166c117 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ -#PythonPH +# PythonPH -## Prerequisites + +### Prerequisites * Docker * Docker Compose * Git +* VueJS + -## Spinning a docker container +### Spinning a docker container ```bash $ docker-compose -f compose/development.yml run -d --rm --name --service-ports app ``` -###### Notes: +#### Notes: * Change `name` into what you want to call your container. * Update `development.yml` if there's conflicting with the ports. + + +### Building vue files +Run the make command +* vue - Create production bundle of vue files +* vue_dev - Create development bundle of vue files + +example: `make vue` or `make vue_dev` diff --git a/config/settings/base.py b/config/settings/base.py index 2717c1c..163a33c 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -14,6 +14,9 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +APP_DIR = os.path.join(os.path.dirname(BASE_DIR), 'pythonph') +PROJECT_DIR = os.path.dirname(BASE_DIR) +FRONTEND_DIR = os.path.join(PROJECT_DIR, 'frontend') # Quick-start development settings - unsuitable for production @@ -39,7 +42,9 @@ 'django.contrib.staticfiles', ] -THIRD_PARTY_APPS = [] +THIRD_PARTY_APPS = [ + 'django_js_reverse', +] LOCAL_APPS = [] @@ -124,3 +129,19 @@ # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(APP_DIR, 'static') +STATICFILES_DIRS = [ + os.path.join(APP_DIR, 'assets'), +] + +# WEBPACK CONFIGURATION +WEBPACK_LOADER = { + 'DEFAULT': { + 'CACHE': False, + 'BUNDLE_DIR_NAME': 'webpack_bundles/', # must end with slash + 'STATS_FILE': os.path.join(FRONTEND_DIR, 'webpack-stats.json'), + 'POLL_INTERVAL': 0.1, + 'TIMEOUT': None, + 'IGNORE': ['.+\.hot-update.js', '.+\.map'] + } +} diff --git a/config/urls.py b/config/urls.py index a7f4dac..8178c62 100644 --- a/config/urls.py +++ b/config/urls.py @@ -5,17 +5,19 @@ Examples: Function views 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views + 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django_js_reverse.views import urls_js + from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), + path('jsreverse/', urls_js, name='js_reverse'), ] diff --git a/frontend/.babelrc b/frontend/.babelrc new file mode 100644 index 0000000..e812394 --- /dev/null +++ b/frontend/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + ["env", { "modules": false }], + "stage-3" + ] +} diff --git a/frontend/.eslintrc b/frontend/.eslintrc new file mode 100644 index 0000000..dededb9 --- /dev/null +++ b/frontend/.eslintrc @@ -0,0 +1,46 @@ +{ + "root": true, + "extends": "standard", + "globals": { + "window": true, + "document": true, + "fetch": true, + "Headers": true, + "Request": true, + "FormData": true, + "FileReader": true, + "localStorage": true + }, + "env": { + "node": true, + "es6": true, + "amd": true, + "browser": true, + "jquery": false + }, + "parserOptions": { + "ecmaFeatures": { + "globalReturn": true, + "generators": false, + "objectLiteralDuplicateProperties": false, + "experimentalObjectRestSpread": true + }, + "ecmaVersion": 2017, + "sourceType": "module" + }, + "plugins": [ + "import", + "html", + ], + "settings": { + "import/core-modules": [], + "import/ignore": [ + "node_modules", + "\\.(coffee|scss|css|less|hbs|svg|json)$" + ] + }, + "rules": { + "no-console": 0, + "comma-dangle": 0 + } +} diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..666f698 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,16 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log +yarn-error.log + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln + +package-lock.json +yarn.lock +webpack-stats.json diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..90dc8b7 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,41 @@ +{ + "name": "frontend", + "description": "A Vue.js project", + "version": "1.0.0", + "author": "", + "private": true, + "scripts": { + "watch": "cross-env NODE_ENV=development webpack --d --watch", + "build_dev": "cross-env NODE_ENV=development webpack --progress --hide-modules", + "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" + }, + "dependencies": { + "axios": "^0.17.0", + "style-loader": "^0.19.0", + "vue": "^2.4.4", + "vuex": "^3.0.1" + }, + "devDependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.2", + "babel-preset-env": "^1.6.0", + "babel-preset-stage-3": "^6.24.1", + "cross-env": "^5.0.5", + "css-loader": "^0.28.7", + "eslint": "^4.18.2", + "eslint-config-standard": "^11.0.0-beta.0", + "eslint-loader": "^1.9.0", + "eslint-plugin-html": "^3.2.2", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-node": "^5.2.1", + "eslint-plugin-promise": "^3.6.0", + "eslint-plugin-standard": "^3.0.1", + "eslint-plugin-vue": "^4.3.0", + "file-loader": "^1.1.4", + "vue-loader": "^13.0.5", + "vue-template-compiler": "^2.4.4", + "webpack": "^3.6.0", + "webpack-bundle-tracker": "^0.2.0", + "webpack-dev-server": "^2.9.1" + } +} diff --git a/frontend/src/sample/Sample.vue b/frontend/src/sample/Sample.vue new file mode 100644 index 0000000..1f49de3 --- /dev/null +++ b/frontend/src/sample/Sample.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/sample/index.js b/frontend/src/sample/index.js new file mode 100644 index 0000000..535d376 --- /dev/null +++ b/frontend/src/sample/index.js @@ -0,0 +1,8 @@ +import Vue from 'vue' + +import Sample from './Sample.vue' + +new Vue({ + el: '#app-sample', + render: h => h(Sample) +}) diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js new file mode 100644 index 0000000..1e14f1f --- /dev/null +++ b/frontend/webpack.config.js @@ -0,0 +1,99 @@ +var path = require('path') +var webpack = require('webpack') +var BundleTracker = require('webpack-bundle-tracker') + +module.exports = { + context: __dirname, + entry: { + sample: './src/sample/', + }, + output: { + path: path.resolve('../pythonph/assets/webpack_bundles/'), + publicPath: '/assets/webpack_bundles/', + filename: '[name].[chunkhash].js' + }, + plugins: [ + new BundleTracker({filename: './webpack-stats.json'}) + ], + module: { + rules: [ + { + enforce: 'pre', + test: /\.vue$/, + exclude: /node_modules/, + loader: 'eslint-loader', + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + loaders: { + } + // other vue-loader options go here + } + }, + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules(?![\\/]vue-awesome[\\/])/ + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: '[name].[ext]?[hash]' + } + }, + // For bootstrap-vue + { + test: /\.css$/, + loaders: [ + 'style-loader', + 'css-loader' + ] + }, + // https://gist.github.com/mosinve/00d1c2715dd573c6db38e9ac7139c215#file-webpack-config-js-L49 + {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?mimetype=image/svg+xml'}, + {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?mimetype=application/font-woff'}, + {test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?mimetype=application/font-woff'}, + {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?mimetype=application/octet-stream'}, + {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'}, + ] + }, + resolve: { + alias: { + 'vue$': 'vue/dist/vue.esm.js', + 'app': path.resolve('./src') + } + }, + devServer: { + historyApiFallback: true, + noInfo: true, + overlay: true + }, + performance: { + hints: false + }, + devtool: '#eval-source-map' +} + +if (process.env.NODE_ENV === 'production') { + module.exports.devtool = '#source-map' + // http://vue-loader.vuejs.org/en/workflow/production.html + module.exports.plugins = (module.exports.plugins || []).concat([ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"' + } + }), + new webpack.optimize.UglifyJsPlugin({ + sourceMap: true, + compress: { + warnings: false + } + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ]) +} diff --git a/pythonph/templates/base.html b/pythonph/templates/base.html new file mode 100644 index 0000000..c2a87db --- /dev/null +++ b/pythonph/templates/base.html @@ -0,0 +1,58 @@ +{% load static %} + + + + + {# Meta Section #} + {% include 'common/metas.html' %} + {% block head_meta %} + {% endblock head_meta %} + + + {% block title %} + Python PH + {% endblock title %} + + + {# Fonts Section #} + {% include 'common/fonts.html' %} + {% block head_fonts %} + {% endblock head_fonts %} + + {# Styles Section #} + {% block head_styles %} + {% include 'common/styles.html' %} + {% endblock head_styles %} + + + + {# Django JS Reverse #} + {% comment %} + Put this here so that it will be accessible already for the Vue components + {% endcomment %} + + + + + +{% block body %} + + {% block header %} + {% include 'common/header.html' %} + {% endblock header %} + + {% block content %} + {% endblock content %} + + {% block footer %} + {% include 'common/footer.html' %} + {% endblock footer %} +{% endblock body %} + +{# JS Section #} +{% include 'common/scripts.html' %} +{% block scripts %} +{% endblock scripts %} + + + diff --git a/pythonph/templates/sample/index.html b/pythonph/templates/sample/index.html new file mode 100644 index 0000000..f7aa440 --- /dev/null +++ b/pythonph/templates/sample/index.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load static %} +{% load render_bundle from webpack_loader %} + +{% block title %} + Sample +{% endblock title %} + +{% block content %} +
+
+{% endblock content %} + +{% block scripts %} +{% render_bundle 'sample' %} +{% endblock scripts %} diff --git a/requirements/base.pip b/requirements/base.pip index e72874d..f8ba430 100644 --- a/requirements/base.pip +++ b/requirements/base.pip @@ -1 +1,4 @@ Django==2.0 + +django-js-reverse +django-webpack-loader