Ponysearch/searx/static/themes/simple/gruntfile.js
Markus Heiser b07884c958 [fix] Optimize SVG for WEB usage / CSP 'style-src self'
- Replace grunt-contrib-htmlmin by grunt-image [1].

- Activate svgo's [2] convertStyleToAttrs to make the HTML inline SVGs
  compoliant to the CSP policy [3]::

    Content-Security-Policy: style-src self;

[1] https://www.npmjs.com/package/grunt-image
[2] https://github.com/svg/svgo
[3] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src

Closes: https://github.com/searxng/searxng/issues/502

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2021-11-15 14:51:55 +01:00

245 lines
7.2 KiB
JavaScript

/*jshint esversion: 6 */
module.exports = function(grunt) {
const path = require('path');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
scripts: {
files: ['gruntfile.js', 'src/**'],
tasks: ['eslint', 'copy', 'concat', 'svg2jinja', 'uglify', 'image', 'less:development', 'less:production']
}
},
eslint: {
options: {
configFile: '.eslintrc.json',
failOnError: false
},
target: [
'svg4web.svgo.js',
'src/js/main/*.js',
'src/js/head/*.js',
'../__common__/js/*.js'
],
},
stylelint: {
options: {
formatter: 'unix',
},
src: [
'src/less/**/*.less',
]
},
copy: {
js: {
expand: true,
cwd: './node_modules',
dest: './js/',
flatten: true,
filter: 'isFile',
timestamp: true,
src: [
'./leaflet/dist/leaflet.js',
]
},
css: {
expand: true,
cwd: './node_modules',
dest: './css/',
flatten: true,
filter: 'isFile',
timestamp: true,
src: [
'./leaflet/dist/leaflet.css',
]
},
leaflet_images: {
expand: true,
cwd: './node_modules',
dest: './css/images/',
flatten: true,
filter: 'isFile',
timestamp: true,
src: [
'./leaflet/dist/images/*.png',
]
},
},
concat: {
head_and_body: {
options: {
separator: ';'
},
files: {
'js/searxng.head.js': ['src/js/head/*.js'],
'js/searxng.js': ['src/js/main/*.js', '../__common__/js/*.js', './node_modules/autocomplete-js/dist/autocomplete.js']
}
}
},
uglify: {
options: {
output: {
comments: 'some'
},
ie8: false,
warnings: true,
compress: false,
mangle: true,
sourceMap: true
},
dist: {
files: {
'js/searxng.head.min.js': ['js/searxng.head.js'],
'js/searxng.min.js': ['js/searxng.js']
}
}
},
image: {
svg4web: {
options: {
svgo: ['--config', 'svg4web.svgo.js']
},
files: {
'../../../templates/__common__/searxng-wordmark.min.svg': 'src/svg/searxng-wordmark.svg'
}
}
},
less: {
development: {
options: {
paths: ["less"],
},
files: {
"css/searxng.css": "src/less/style.less",
"css/searxng-rtl.css": "src/less/style-rtl.less"
}
},
production: {
options: {
paths: ["less"],
plugins: [
new (require('less-plugin-clean-css'))()
],
sourceMap: true,
sourceMapURL: (name) => { const s = name.split('/'); return s[s.length - 1] + '.map';},
outputSourceFiles: false,
sourceMapRootpath: '../',
},
files: {
"css/searxng.min.css": "src/less/style.less",
"css/searxng-rtl.min.css": "src/less/style-rtl.less"
}
},
},
svg2jinja: {
all: {
src: {
'warning': 'node_modules/ionicons/dist/svg/alert-outline.svg',
'close': 'node_modules/ionicons/dist/svg/close-outline.svg',
'chevron-up-outline': 'node_modules/ionicons/dist/svg/chevron-up-outline.svg',
'chevron-right': 'node_modules/ionicons/dist/svg/chevron-forward-outline.svg',
'chevron-left': 'node_modules/ionicons/dist/svg/chevron-back-outline.svg',
'menu-outline': 'node_modules/ionicons/dist/svg/menu-outline.svg',
'ellipsis-vertical-outline': 'node_modules/ionicons/dist/svg/ellipsis-vertical-outline.svg',
'magnet-outline': 'node_modules/ionicons/dist/svg/magnet-outline.svg',
'globe-outline': 'node_modules/ionicons/dist/svg/globe-outline.svg',
'search-outline': 'node_modules/ionicons/dist/svg/search-outline.svg',
'image-outline': 'node_modules/ionicons/dist/svg/image-outline.svg',
'play-outline': 'node_modules/ionicons/dist/svg/play-outline.svg',
'newspaper-outline': 'node_modules/ionicons/dist/svg/newspaper-outline.svg',
'location-outline': 'node_modules/ionicons/dist/svg/location-outline.svg',
'musical-notes-outline': 'node_modules/ionicons/dist/svg/musical-notes-outline.svg',
'layers-outline': 'node_modules/ionicons/dist/svg/layers-outline.svg',
'school-outline': 'node_modules/ionicons/dist/svg/school-outline.svg',
'file-tray-full-outline': 'node_modules/ionicons/dist/svg/file-tray-full-outline.svg',
'people-outline': 'node_modules/ionicons/dist/svg/people-outline.svg',
},
dest: '../../../templates/simple/icons.html',
},
},
});
grunt.registerMultiTask('svg2jinja', 'Create Jinja2 macro', function() {
const ejs = require('ejs'), svgo = require('svgo');
const icons = {}
for(const iconName in this.data.src) {
const svgFileName = this.data.src[iconName];
try {
const svgContent = grunt.file.read(svgFileName, { encoding: 'utf8' })
const svgoResult = svgo.optimize(svgContent, {
path: svgFileName,
multipass: true,
plugins: [
{
name: "removeTitle",
},
{
name: "removeXMLNS",
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [
{ "aria-hidden": "true" }
]
}
}
]
});
icons[iconName] = svgoResult.data.replace("'", "\\'");
} catch (err) {
grunt.log.error(err);
}
}
const template = `{# this file was generated by searx/static/themes/simple/gruntfile.js #}
{%- set icons = {
<% for (const iconName in icons) { %> '<%- iconName %>':'<%- icons[iconName] %>',
<% } %>
}
-%}
{% macro icon(action, alt) -%}
{{ icons[action] | replace("ionicon", "ion-icon") | safe }}
{%- endmacro %}
{% macro icon_small(action) -%}
{{ icons[action] | replace("ionicon", "ion-icon-small") | safe }}
{%- endmacro %}
{% macro icon_big(action, alt) -%}
{{ icons[action] | replace("ionicon", "ion-icon-big") | safe }}
{%- endmacro %}
`;
const result = ejs.render(template, { icons });
grunt.file.write(this.data.dest, result, { encoding: 'utf8' });
grunt.log.ok(this.data.dest + " created");
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-image');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-stylelint');
grunt.loadNpmTasks('grunt-eslint');
grunt.registerTask('test', ['jshint']);
grunt.registerTask('default', [
'eslint',
'stylelint',
'copy',
'concat',
'svg2jinja',
'uglify',
'image',
'less:development',
'less:production'
]);
};