My setup consists of a lot of .js files being used in my base.html template and the same list being repeated in my Gruntfile.js when concatenating all the files for production.
How can I have the list of files in a single JSON (or whatever other format) and be able to pass it to both Django and Grunt?
Here's a simplified example:
base.html:
<html lang="en">
<head>
<title></title>
</head>
<body>
<script src="static/some.js"></script>
<!-- a lot more js files go here -->
</body>
</html>
Gruntfile.js:
uglify: {
options:{},
build: {
options:{
report: 'min'
},
files: {
'static/some.min.js': [
'static/some.js',
// lots of other js files go here
]
}
}
I know that Grunt can load JSON files through grunt.file.readJSON('files.json') or require('files.json') but how can I do the same in Django templates?
In case anybody is interested, here's what I went for:
sample.json:
{
"files": [
"file1.js",
"file2.js"
]
}
Grunt task:
module.exports = function(grunt) {
var generalJS = prepare(grunt.file.readJSON('sample.json'));
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options:{},
build: {
options:{},
files: {
'sample.min.js': generalJS.files
}
}
}
});
grunt.registerTask('build', ['uglify']);
}
Django template tag:
#register.simple_tag
def render_js_files(file_location):
result = ""
static_base_path = settings.STATIC_DIR
json_js_files_path = os.path.join(static_base_path, file_location)
json_data = open(json_js_files_path)
data = json.load(json_data)
files_dirs = data['files']
json_data.close()
src_template = "<script src='%s'></script>\n"
for js_file_path in files_dirs:
result += src_template % urljoin(PrefixNode.handle_simple("STATIC_URL"), js_file_path)
return result
Usage in base.html:
{% render_js_files 'sample.json' %}
Related
I have a some.json file like this:
{
"disneyland-paris": {
"lang": "de"
},
"hanoi": {
"lang": "de"
}
}
… that I want to get into a nunjucks template with:
pipe(data(function() {
return JSON.parse(fs.readFileSync(".../some.json"))
}))
.pipe(nunjucksRender())
How would I access this data within nunjucks?
This does not work:
{{ some }}
or
{{ some.json }}
One of many approaches you could take is use gulp nunjucks render for your gulp project. If you decide to go this route, you can use the following steps as a proof-of-concept to achieve your goal. And if not, you can borrow ideas from the following steps anyway!
Step 1 - Within your project, "you could" structure your JSON data like so in a file called Languages.js :
const languages = [
{
"group": [{
"location":"disenyland-paris",
"lang": "de"
},
{
"location":"hanoi",
"lang": "de"
},
]
}];
module.exports = languages;
Step 2 - From your gulpfile.js, assuming you are running a gulp project, call your JSON data, then reference it within your Nunjucks logic as an environmental global...
...
const nunjucksRender = require('gulp-nunjucks-render');
const Languages = require('./src/models/Languages');
...
const manageEnvironment = function (environment) {
environment.addGlobal('mLangauges',Languages);
}
...
function genNunJucks(cb) {
return src(['src/views/*.html'])
.pipe(nunjucksRender({
path: ['src/views/', 'src/views/parts'], // String or Array
ext: '.html',
inheritExtension: false,
envOptions: {
watch: true,
},
manageEnv: manageEnvironment,
loaders: null
}))
.pipe(dest('pub')); //the final destination of your public pages
cb();
}
//then do more stuff to get genNunJucks() ready for gulp commands...
Step 3 - From within your Nunjucks template file, call the data like so...
{% for sections in mLangauges %}
{% for mgroup in sections.group %}
<p>{{mgroup.location}}</p>
<p>{{mgroup.lang}}</p>
{% endfor %}
{% endfor %}
All that is left to do is run your gulp project :)
Tip - If you change your JSON data while working, you may need to re-build your gulp project to see the udpated JSON data on your web page. Re-building can be as simple as running 'npm run build' if you set it up right in your package.json file.
I am using both vue single-file components and separating of markup and logic to .pug and .ts files respectively. If you interesting why I don't unify is please see the comments section.
Problem
import template from "#UI_Framework/Components/Controls/InputFields/InputField.vue.pug";
import { Component, Vue } from "vue-property-decorator";
console.log(template);
#Component({
template,
components: {
CompoundControlBase
}
})
export default class InputField extends Vue {
// ...
}
In development building mode exported template is correct (I beautified it for readability):
<CompoundControlBase
:required="required"
:displayInputIsRequiredBadge="displayInputIsRequiredBadge"
<TextareaAutosize
v-if="multiline"
:value="value"
/><TextareaAutosize>
</CompoundControlBase>
In production mode, my markup has been lowercased. So, the console.log(template) outputs:
<compoundcontrolbase
:required=required
:displayinputisrequiredbadge=displayInputIsRequiredBadge
<textareaautosize
v-if=multiline
:value=value
></textareaautosize>
</compoundcontrolbase>
Off course, I got broken view.
Webpack config
const WebpackConfig = {
// ...
optimization: {
noEmitOnErrors: !isDevelopmentBuildingMode,
minimize: !isDevelopmentBuildingMode
},
module: {
rules: [
{
test: /\.vue$/u,
loader: "vue-loader"
},
{
test: /\.pug$/u,
oneOf: [
// for ".vue" files
{
resourceQuery: /^\?vue/u,
use: [ "pug-plain-loader" ]
},
// for ".pug" files
{
use: [ "html-loader", "pug-html-loader" ]
}
]
},
// ...
]
}
}
Comments
To be honest, I don't know why we need ? in resourceQuery: /^\?vue/u, (explanations are welcome).
However, in development building mode above config works property for both xxxx.vue and xxxx.vue.pug files.
I am using below files naming convention:
xxx.pug: pug file which will not be used as vue component template.
xxx.vue.pug: pug file which will be used as vue component template.
xxx.vue: single-file vue component.
xxx.vue.ts: the logic of vue component. Required exported template from xxx.vue.pug as in InputField case.
Why I need xxx.vue.ts? Because of this:
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
Neither public methods/fields nor non-default methods are visible for TypeScrpt xxx.vue files. For the common (non-applied) components, I can't accept it.
Repro
🌎 GitHub
Step 1: Install dependencies
npm i
Step 2: Let's check the development building first
npm run DevelopmentBuild
In line 156 of DevelopmentBuild\EntryPoint.js, you can check that below pug template:
Alpha
Bravo OK
has been compiled properly:
Step 3: Problem on production build
npm run ProuductionBuild
You can find the lowercased tags in the column 13:
You can also open index.html in your browser and check the console.log() output with compiled TestComponent.
The problem is the "html-loader". It has the option minimize set to true in production mode (html-loader/#minimize).
I had a similar problem in angular and had to unset some options like (see for reference html-minifier-terser#options-quick-reference).
// webpack.config.js
{
test: /\.pug$/u,
oneOf: [
// for ".vue" files
{
resourceQuery: /^\?vue/u,
use: [ "pug-plain-loader" ]
},
// for ".pug" files
{
use: [ "html-loader", "pug-html-loader" ]
}
],
options: {
minimize: { // <----
caseSensitive: false // <----
} // <----
}
},
I want to serve a JSON file with gin server. And set some customize values in the HTML file. Use JavaScript in it to call the JSON file.
My application structure:
.
├── main.go
└── templates
├── index.html
└── web.json
I put these basic source into main.go file:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
var router *gin.Engine
func main() {
router = gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/web", func(c *gin.Context) {
c.HTML(
http.StatusOK,
"index.html",
gin.H{
"title": "Web",
"url": "./web.json",
},
)
})
router.Run()
}
Some code in templates/index.html file:
<!doctype html>
<html>
<head>
<title>{{ .title }}</title>
// ...
</head>
<body>
<div id="swagger-ui"></div>
// ...
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "{{ .url }}",
dom_id: '#swagger-ui',
// ...
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>
When running the application, I got a fetch error:
Not Found ./web.json
So how should I serve the web.json file to be accessed in the Gin internal server?
Quoting the original gin docs: https://github.com/gin-gonic/gin#serving-static-files
func main() {
router := gin.Default()
router.Static("/assets", "./assets")
router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
So basically you should define a route specific to your JSON file next to other routes you've defined. And then use that.
Which loader/plugin should I use to move multiple html files from one folder (src) to another (dist) which only imports files that obey certain rules in this example I need to import multiple html files, so the regex would be:
/.html$/
I know that I can move html using html-webpack-plugin but I don't want to create object instance for every page I want to move. I also don't want to make multiple imports in my app.js (entry point for webpack).
So i tried with copy-webpack-plugin but this one moves everything from src to dist. Is it possible to filter with my regex pattern or do you know other way that works to do this?
new CopyWebpackPlugin([{
from: './src',
to: path.resolve(__dirname, "dist")
}],
I gues that I could use system
mkdir dist && cp ./src/*.html ./dist
And simply run this in my package.json in the script before running webpack -d
but this is not the "polite" way of doing this. I'm sure that webpack can do that easily..
Ps. Also, it would be nice to minimize those files, if possible.
Moving all files in batch
So for now it works like this. To not have to include every single file from the src folder in to my app.js (which is my entry point for webpack) I required all files using require.context()
Because my app.js is also in my src folder I use relative path to require all other file:
// requires all files in current directory and sub directories
require.context("./", true, /^\.\/.*\..*/);
Or if you want to import files with certain extension sue this instead:
// requires all html files in current directory and sub directories
require.context("./", true, /^\.\/.*\.html/);
So now everything is imported and I don't have to require files manually. The only thing I have to do is set entry to app.js using path.resolve.
I now can use test property to get what I want from within in webpack.config.js module rules.
...
module: {
rules: [
{
test: /\.(html)$/,
use:
[
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
context: './src',
outputPath: '/',
publicPath: '/'
}
},
...
This works fine my files are moved (but not minified). Perhaps someone can take this a little bit further and the answer will be completed.
Below i place full file for reference only
webpack.config.js
// imports node plugin which allows us to save data to a file for example css external files
var ExtractTextPlugin = require("extract-text-webpack-plugin");
// cleans defined folders before webpack will build new files now we can remove package.json commands "rm -rf ./dist && ..."
var CleanWebpackPlugin = require("clean-webpack-plugin");
// copies to memory html from template file and injects css and javascript as well as img src to newly generated html file.
var HtmlWebpackPlugin = require("html-webpack-plugin");
// to include jquery we need to import 'jquery' in app.js but also we need to make connection between bundle.js jquery script
var Webpack = require('webpack');
// includes node path resover that is need for webpack-dev-server to run properly
var path = require('path');
// webpack configuration
module.exports = {
entry: [
path.resolve(__dirname, "src/app.js"),
],
output: {
path: path.resolve(__dirname, 'dist'), // defins the main utput directory
filename: 'js/bundle.js',
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: 8080,
},
module: {
rules: [
{
test: /\.(html)$/,
use:
[
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
context: './src',
outputPath: '/',
publicPath: '/'
}
},
]
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'img/',
publicPath: '/' // this path relates to reference path from the index.html file that imports out bundle.js file
}
}
},
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
]
},
{
test: /\.sass$/,
include: [
path.resolve(__dirname, "src/sass")
],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
//resolve-url-loader may be chained before sass-loader if necessary
use: [
{
loader: 'css-loader',
options: {
minimize: false,
sourceMap: true,
}
},
'sass-loader'
]
})
},
]
},
plugins: [
// mapping jQuery variable to our node module dependency (remember to import 'jquery' in app.js)
// below we make jquery available as both the $ and jQuery variable
new Webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
// use extract plugin to build an external file loded by sass-loader > complied to css > movig to bundle.css
new ExtractTextPlugin({
filename: 'css/bundle.css'
}),
// remove all files from this folder before generating new files
// new CleanWebpackPlugin(['dist']),
],
}
You should be able to use copy-webpack-plugin's support for globs to achieve what you want.
new CopyWebpackPlugin([
{
from: './src/*.html',
to: path.resolve(__dirname, 'dist')
}
])
The globs accept minimatch options too.
EDIT:
In addition, if you're copying just HTML (and want to minify them), you might want to take a look at html-webpack-plugin. The minify option allows you to minify those HTML files.
I'm working on an HTML 5 app that I'm building with Grunt. I am trying to use the grunt-preprocess task on my HTML. In attempt to just see if it works, I have the preprocess task setup as follows:
module.exports = function(config) {
return {
dev: {
cwd: 'build/temp',
src: [
'**/*.css',
'**/*.js',
'index.html',
'app/**/*.html'
],
options: {
inline : true,
context: {
ENV: 'dev'
}
}
}
};
};
I have a basic index.html file that looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>Test</title>
</head>
<body>
<!-- #exclude -->
<header>You're on dev!</header>
<!-- #endexclude -->
Welcome
</body>
</html>
I've added the exclude directive just to see if its running through the preprocessor. I know that grunt is running the preprocess task. I'm basing this on the fact that when I run grunt --verbose, I see the following at the end:
Running "preprocess:dev" (preprocess) task
Verifying property preprocess.dev exists in config...OK
Files: css/animations.css, css/app.css, css/application.css,...
Verifying property preprocess exists in config...OK
Options: inline, context={"ENV":"dev"}
Reading css/animations.css...ERROR
Warning: Unable to read "css/animations.css" file (Error code: ENOENT). Use --force to continue.
I can't make sense of this error. The error says css/animations.css can't be found. Yet, it shows it in the Files list previously. I've also confirmed its on the file system.
Can somebody please tell me what I'm doing wrong?
You need to set expand: true when using cwd.
See http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically for more info
Your task would become:
dev: {
expand: true,
cwd: 'build/temp',
src: [
'**/*.css',
'**/*.js',
'index.html',
'app/**/*.html'
],
options: {
inline : true,
context: {
ENV: 'dev'
}
}
}