Disable background:url(...) encoding as base64 in Minify package - css

I use minify to minimize my js and css files. But after minimizing the files, the ones which contain background:url(...) become larger, because the url is encoded to base64.
I want to turn off this css-base64-images function. But according to an issue raised in 2016, this is not possible.
package.json:
"devDependencies": {
"minify": "^9.1.0",
"postcss-cli": "^10.0.0"
}
My code:
import { minify } from 'minify';
const myFunction = () => {
/* some code... */
minify(filepath).then((file) => {
/* some code... */
});
}
According to this article, in most cases, it is not neccessary to optimize images with base64. In my case some css files have grown to10,000 KB from 40-50 KB, therefore I want to turn of base64.

I figured out a solution to "turn off" base64.
The minify function accepts 2 parameters, name and userOptions.
The userOptions is passed down to node_modules\css-b64-images\lib\css-b64-images.js. There is the function called replaceUrlByB64, which does not encode to base64, if maxSize is smaller then the file size.
if (stat.size > options.maxSize){
return cb(new Error('Skip ' + imageUrl + ' Exceed max size'), css);
}
My code:
import { minify } from 'minify';
/* Set image maxSize to 0 to skip url replacement by base64 in minify.
node_modules/css-b64-images/lib/css-b64-images.js function: replaceUrlByB64 */
const skipReplacingUrlByB64 = {
img : {
maxSize: 0,
},
}
const myFunction = () => {
/* some code... */
minify(filepath, skipReplacingUrlByB64).then((file) => {
/* some code... */
});
}
The structure of skipReplacingUrlByB64 object is based on how userOptions is retrieved in node_modules\minify\lib\img.js
const options = {
...defaultOptions,
...userOptions?.img || {},
};
package.json is the same as in the question
"devDependencies": {
"minify": "^9.1.0",
"postcss-cli": "^10.0.0"
}

Related

how to convert every sass file to css automatically with grunt

I want to convert all my scss files(located in sass/ folder) to css files(located in css/ folder) automatically on save. And I want it to work even with the new ones. For example, I had index-style.scss and header-style.scss, already converted to css, but if I create another scss file, like footer-style.scss, I want it to convert to css AUTOMATICALLY, without editing Gruntfile or something.
scss and css files will always match
I tried:
files: {
'src/main/webapp/WEB-INF/css/' : 'src/main/webapp/WEB-INF/sass/'
}
there were no compiling errors, but it didn't convert scss file. Also tried :
files: {
'src/main/webapp/WEB-INF/css/*.css' : 'src/main/webapp/WEB-INF/sass/*.scss'
}
Is this even possible?
P.S.
I also tried adding this code to the Gruntfile.js:
let cssDir = "src/main/webapp/WEB-INF/css/";
let sassDir = "src/main/webapp/WEB-INF/sass/";
let filesObj = {}
// filesObj[cssDir+"login-page.css"] = sassDir+"login-page.scss"
let fs = require('fs')
fs.readdir(sassDir, function (err, files) {
if (err) {
console.log("could not list the dir" + err);
process.exit(1);
}
files.forEach(function (file, index) {
console.log("forEach is working!")
let cssPath = cssDir+file;
fs.access(cssPath,(err) => {
console.log("fs.access is working")
if (err) {
fs.appendFile(cssPath, '', function (err) {
console.log("appendFile is working")
if (err) {
console.log("Grunt file 17 line error");
process.exit(1);
}
})
}
})
console.log("\n\n==========================================\n\n"+cssPath+"\n\n"+sassDir+'file\n')
filesObj[cssDir+'file'] = sassDir+'file'
})
})
...
sass: {
dist: {
options: {
sourcemap: false,
compress: false,
yuicompress: false,
style: 'expanded',
},
files: filesObj
},
}
...
but, it didn't help

How to set path for dynamic import in webpack 4

I have a NET Core application with the following webpack config optimization and output sections located in ClientApp folder which is placed in the root of the project
optimization: {
splitChunks: {
cacheGroups: {
translations: {
test: /[\\/]node_modules[\\/]webapps-translations/,
name(module, chunks, cacheGroupKey) {
const moduleFileName = module.identifier()
.split('\\').pop().toLowerCase().replace('.json', '');
return `${moduleFileName}`;
}
}
}
}
},
output: {
path: path.resolve(path.join('..', 'wwwroot', 'js')),
publicPath: path.join('..', 'js'),
filename: `[name]${jsMin}.js`
}
I have the following dynamic import statement
import(
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`webapps-translations/assets/${lang}.json`).then(m => {
let translations = m.default;
// TODO: use translations
});
Webpack generates the files in the desire location but the dynamic import request path is wrong. My page URL is http://localhost:5001/api/mypage but the generated import path is http://localhost:5001/api/jsde.js. The generated webpack URL should be http://localhost:5001/js/de.js
How can I configure the right path for dynamic import?

How do I parse CSS to prefix CSS selectors

I have the following my-file.css:
.a [data-smth] { ... }
.b .c.d { ... }
.e { ... }
#f { ... }
and I want to do something like the following (pseudocode) in Node.js:
let css = readFile('my-file.css')
let prefixedCss = prefixClasses(css, 'prfx-')
writeFile(prefixedCss, 'my-prefixed-file.css')
to end up with my-prefixed-file.css:
.prfx-a [data-smth] { ... }
.prfx-b .prfx-c.prfx-d { ... }
.prfx-e { ... }
#f { ... }
I have found these npm modules:
https://github.com/vic/prefix-css (hasn't been updated in years and has issues)
https://pegjs.org/ (requires lots of low-level AST configuration)
But I was wondering whether there are better/safer solutions that have already been tested/become standard practice ?
NOTE: The above file contents was just an example. I'm looking for a way to achieve this for files whose content is completely unknown to me. So I'm looking for a "universal" solution.
Any help would be most welcome & thank you in advance! :-)
You might want to check https://github.com/marceloucker/postcss-prefixer#postcss-prefixer.
postcss-prefixer
PostCSS plugin to add a prefix to all css selectors classes and ids.
Usage
With PostCSS cli:
Install postcss-cli and postcss-prefixer on your project directory:
npm install postcss-cli postcss-prefixer --save-dev
and at your package.json
"scripts": {
"postcss": "postcss input.css -u postcss-prefixer -o output.css"
}
Others
postcss([ require('postcss-prefixer')({ /* options */ }) ])
Options
prefix
Type: `string`<br>
Default: none
String to be used as prefix
ignore
Type: `array`<br>
Default: `[]`
Array of selectors to be ignored by the plugin, accepts string and regex.
Example
Example of usage with results generated by the plugin.
Code
const postcss = require('postcss');
const prefixer = require('postcss-prefixer');
const input = fs.readFileSync('path/to/file.css', 'utf-8');
const output = postcss([
prefixer({
prefix: 'prefix-'
ignore: [ /selector-/, '.ignore', '#ignore' ]
})
]).process(input);
Input:
#selector-one .example {
/* content */
}
.selector-two .example2 {
/* content */
}
#ignore .ignore {
/* content */
}
#ignore .other {
/* content */
}
Output:
#selector-one .prefix-example {
/* content */
}
.selector-two .prefix-example2 {
/* content */
}
#ignore .ignore {
/* content */
}
#ignore .prefix-other {
/* content */
}
Credits
Plugin based on postcss-class-prefix create by thompsongl.

Aurelia: Stylesheet loaded but not removed

In an aurelia project I have several components that import additional stylesheets, e.g. from semantic-ui. After leaving the components page, the stylesheet is still active and not removed. Is it possible to 'unload' the stylesheets?
Update (2018-03-27):
I submitted a PR to enable this as an opt-in, you can keep track of it here: https://github.com/aurelia/templating-resources/pull/344
Original answer:
A word of warning, this is untested and aurelia-internals-hacky.
What you could do is override the default CSSViewEngineHooks and CSSResource classes to keep track of the style elements it injects, and then add an beforeUnbind hook to remove the styles again.. before unbind (right after detached)
Unfortunately the CSSResource class is not exported from aurelia-templating-resources so we need to go one layer deeper and overwrite the existing style loader plugins that returns instances of CSSResource.
Here's how:
First we grab the code from aurelia-templating-resources/src/css-resource.js, put it in our own src/css-resource.js/ts and make a few tweaks to it (don't think too much of the size, it's just a copy-paste with a few small tweaks, annotated with comments):
import {ViewResources, resource, ViewCompileInstruction} from 'aurelia-templating';
import {Loader} from 'aurelia-loader';
import {Container} from 'aurelia-dependency-injection';
import {relativeToFile} from 'aurelia-path';
import {DOM, FEATURE} from 'aurelia-pal';
let cssUrlMatcher = /url\((?!['"]data)([^)]+)\)/gi;
function fixupCSSUrls(address, css) {
if (typeof css !== 'string') {
throw new Error(`Failed loading required CSS file: ${address}`);
}
return css.replace(cssUrlMatcher, (match, p1) => {
let quote = p1.charAt(0);
if (quote === '\'' || quote === '"') {
p1 = p1.substr(1, p1.length - 2);
}
return 'url(\'' + relativeToFile(p1, address) + '\')';
});
}
class CSSResource {
constructor(address: string) {
this.address = address;
this._scoped = null;
this._global = false;
this._alreadyGloballyInjected = false;
}
initialize(container: Container, target: Function): void {
this._scoped = new target(this);
}
register(registry: ViewResources, name?: string): void {
if (name === 'scoped') {
registry.registerViewEngineHooks(this._scoped);
} else {
this._global = true;
}
}
load(container: Container): Promise<CSSResource> {
return container.get(Loader)
.loadText(this.address)
.catch(err => null)
.then(text => {
text = fixupCSSUrls(this.address, text);
this._scoped.css = text;
if (this._global) {
this._alreadyGloballyInjected = true;
// DOM.injectStyles(text); <- replace this
// this is one of the two possible moments where the style is injected
// _scoped is the CSSViewEngineHooks instance, and we handle the removal there
this._scoped.styleNode = DOM.injectStyles(text);
}
});
}
}
class CSSViewEngineHooks {
constructor(owner: CSSResource) {
this.owner = owner;
this.css = null;
}
beforeCompile(content: DocumentFragment, resources: ViewResources, instruction: ViewCompileInstruction): void {
if (instruction.targetShadowDOM) {
DOM.injectStyles(this.css, content, true);
} else if (FEATURE.scopedCSS) {
let styleNode = DOM.injectStyles(this.css, content, true);
styleNode.setAttribute('scoped', 'scoped');
} else if (this._global && !this.owner._alreadyGloballyInjected) {
// DOM.injectStyles(this.css); <- replace this
// save a reference to the node so we can easily remove it later
this.styleNode = DOM.injectStyles(this.css);
this.owner._alreadyGloballyInjected = true;
}
}
// this is the hook we add, here we remove the node again
beforeUnbind(): void {
if (this._global && this.owner._alreadyGloballyInjected) {
DOM.removeNode(this.styleNode);
this.owner._alreadyGloballyInjected = false;
}
}
}
export function _createCSSResource(address: string): Function {
#resource(new CSSResource(address))
class ViewCSS extends CSSViewEngineHooks {}
return ViewCSS;
}
Then, in our main.ts/js we do the same thing aurelia-templating-resources.js does, but with our own version.
So we do this after the call to aurelia.use.standardConfiguration() etc, to override the existing one
let viewEngine = config.container.get(ViewEngine);
let styleResourcePlugin = {
fetch(address) {
return { [address]: _createCSSResource(address) };
}
};
['.css', '.less', '.sass', '.scss', '.styl'].forEach(ext => viewEngine.addResourcePlugin(ext, styleResourcePlugin));
And that should pretty much do the trick.. :)
I have found a plugin to resolve the issue:
https://github.com/jbockle/aurelia-useable-style-loader
But for the latest Webpack webpack.config.js should be a little bit different than in a plugin readme.
You should load .css files this way:
use: [
{ loader: 'style-loader', options: { injectType: 'lazyStyleTag' } },
'css-loader'
]
Instead of this:
use: ['style-loader/useable', 'css-loader']

Creative sdk: How to get Base64 image data after save

How I can get the base64 format while saving the image?
onSave: function(imageID, newURL) {
originalImage.src = newURL;
featherEditor.close();
}
Here is some code I used in an Angular 1 / Cordova (Phonegap) app, obviously you might want to strip out the promises, and its also not in any way refactored (just two copy/paste jobs) but does the the treat. You also need the cordova file plugin installed.
function _launchEditor(imageUrl, options) {
/* 2.a) Prep work for calling `.edit()` */
var deferred = $q.defer();
function success(newUrl) {
/**
* This function will handle the conversion from a file to base64 format
*
* #path string
* #callback function receives as first parameter the content of the image
*/
function getFileContentAsBase64(path, callback) {
window.resolveLocalFileSystemURL(path, gotFile, error);
function gotFile(fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function (e) {
var content = this.result;
callback(content);
};
// The most important point, use the readAsDatURL Method from the file plugin
reader.readAsDataURL(file);
});
}
}
getFileContentAsBase64(newUrl, function (base64Image) {
//window.open(base64Image);
// Then you'll be able to handle the myimage.png file as base64
deferred.resolve({
data: base64Image,
url: newUrl
})
});
}
function error(error) {
deferred.reject(error);
}
if (!options) {
options = {
outputType: CSDKImageEditor.OutputType.JPEG,
tools: [
CSDKImageEditor.ToolType.CROP,
CSDKImageEditor.ToolType.ORIENTATION,
CSDKImageEditor.ToolType.TEXT,
CSDKImageEditor.ToolType.DRAW
],
quality: 50
}
}
/* 2.b) Launch the Image Editor */
CSDKImageEditor.edit(success, error, imageUrl, options);
return deferred.promise;
}
Then just call with
_launchEditor(<ImageUrl>)
.then(function(data){
//data.url = creativesdk saved image url
//data.data = base64 image data
})

Resources