grunt-contrib-cssmin: css source map appears empty in dev tools - json

I am using grunt with cssmin to minify and concatenate css files.
Css files are well concatenated and minified but when I try to watch the non minified files in chrome dev tools under 'Sources' tab, files appears empty.
Here is my cssmin task:
cssmin: {
options: {
report: 'gzip',
keepSpecialComments: 0,
sourceMap: true,
outputSourceFiles: true
},
target: {
files: {
'web/assets/dist/css/vendors.min.css': [
'bower_components/bootstrap/dist/css/bootstrap.min.css',
'bower_components/chosen/chosen.min.css',
'bower_components/slabText/css/slabtext.css',
'bower_components/video.js/dist/video-js.css',
'bower_components/video.js/dist/video-js.css'
],
'web/assets/dist/css/app.min.css': [
'app/Resources/assets/css/jumbotron-narrow.css',
'app/Resources/assets/css/custom.css',
],
}
}
},
Source map setting is enabled under chrome.
web/assets/dist/css/app.min.css.map looks like this:
{
"version":3,
"sources":["app/Resources/assets/css/jumbotron-narrow.css","app/Resources/assets/css/custom.css"],
"names":[],
"mappings":"AAeA,QAdA,KAeI,eAAgB,KADpB,QA6BA,WAEI,cAAe,IAAI,MAAM,QA7C7B,KACI,YAAa,KCUb,YAAa,eAAkB,WDHnC,QAFA,QACA,WAEI,cAAe,KACf,aAAc,KASlB,WACI,WAAY,EACZ,cAAe,EACf,YAAa,KAIjB,QACI,YAAa,KACb,MAAO,KACP,WAAY,IAAI,MAAM,QAI1B,yBACI,WACI,UAAW,OAGnB,qBACI,OAAQ,KAAK,EAIjB,WACI,WAAY,OAGhB,gBACI,QAAS,KAAK,KACd,UAAW,KAIf,WACI,OAAQ,KAAK,EAEjB,gBACI,WAAY,KAIhB,oCAII,QAFA,QACA,WAEI,cAAe,EACf,aAAc,EAGlB,QACI,cAAe,KAGnB,WACI,cAAe,GC3EvB,WACI,YAAa,eACb,IAAK,iCAAkC,mBAG3C,WACI,YAAa,YACb,IAAK,8BAA+B,mBAOxC,GACI,WAAY,KACZ,eAAgB,UAChB,YAAa,EACb,YAAa,YAAe,WAC5B,YAAa,EAAI,EAAI,IAAI,KAG7B,4BACI,QAAS,MACT,YAAa,KACb,aAAc,KACd,cAAe,IAGnB,UACI,UAAW,MACX,WAAY,KAIhB,oBACI,MAAO,IAGX,sBACI,gBAAiB,KACjB,YAAa,EAGjB,cACI,aAAc,QAIlB,iCACI,QAAS"
}
Version from my package.json:
"grunt-contrib-cssmin": "^0.12.0",
What am I doing wrong?

Edit: Firstly, try specifying a root in your options of the Gruntfile.js as follows:
// ...
cssmin: {
options: {
// ...
root: 'web/assets/dist/css/' // <-- Add this too.
},
target: {
// ...
}
/...
The Issue
The sourceMap file that grunt-contrib-cssmin is generating is incorrectly specifying the paths in the sources Array. The resultant sourceMap should be as follows:
app.min.css.map
{
"version":3,
"sources":[
"../../../../app/Resources/assets/css/jumbotron-narrow.css",
"../../../../app/Resources/assets/css/custom.css"
],
"names":[],
"mappings":"AAeA,QAdA,KAeI,eAAgB,KADpB,QA6BA,WAEI,cAAe,IAAI,MAAM,QA7C7B,KACI,YAAa,KCUb,YAAa,eAAkB,WDHnC,QAFA,QACA,WAEI,cAAe,KACf,aAAc,KASlB,WACI,WAAY,EACZ,cAAe,EACf,YAAa,KAIjB,QACI,YAAa,KACb,MAAO,KACP,WAAY,IAAI,MAAM,QAI1B,yBACI,WACI,UAAW,OAGnB,qBACI,OAAQ,KAAK,EAIjB,WACI,WAAY,OAGhB,gBACI,QAAS,KAAK,KACd,UAAW,KAIf,WACI,OAAQ,KAAK,EAEjB,gBACI,WAAY,KAIhB,oCAII,QAFA,QACA,WAEI,cAAe,EACf,aAAc,EAGlB,QACI,cAAe,KAGnB,WACI,cAAe,GC3EvB,WACI,YAAa,eACb,IAAK,iCAAkC,mBAG3C,WACI,YAAa,YACb,IAAK,8BAA+B,mBAOxC,GACI,WAAY,KACZ,eAAgB,UAChB,YAAa,EACb,YAAa,YAAe,WAC5B,YAAa,EAAI,EAAI,IAAI,KAG7B,4BACI,QAAS,MACT,YAAa,KACb,aAAc,KACd,cAAe,IAGnB,UACI,UAAW,MACX,WAAY,KAIhB,oBACI,MAAO,IAGX,sBACI,gBAAiB,KACjB,YAAa,EAGjB,cACI,aAAc,QAIlB,iCACI,QAAS"
}
Note the sourceRoot prefixes ../../../../ added to each path in the sources Array.
In the SourceMap Specification a section reads:
Resolving Sources
If the sources are not absolute URLs after prepending of the “sourceRoot”, the sources are resolved relative to the SourceMap (like resolving script src in a html document).
If specifying a root path, (as initially mentioned), does not resolve the issue then it indicates that this part of the specification is not being honoured by grunt-contrib-cssmin.
Alternate Solution
If the initial suggested fix does not work then consider adding a custom Task to invoke a Function which fixes each sources path after the sourceMap(s) have been created by grunt-contrib-cssmin.
You could do something like this:
Gruntfile.js
module.exports = function (grunt) {
grunt.initConfig( {
cssmin: {
options: {
report: 'gzip',
keepSpecialComments: 0,
sourceMap: true,
outputSourceFiles: true
},
target: {
files: {
'web/assets/dist/css/vendors.min.css': [
'bower_components/bootstrap/dist/css/bootstrap.min.css',
'bower_components/chosen/chosen.min.css',
'bower_components/slabText/css/slabtext.css',
'bower_components/video.js/dist/video-js.css'
],
'web/assets/dist/css/app.min.css': [
'app/Resources/assets/css/jumbotron-narrow.css',
'app/Resources/assets/css/custom.css'
]
}
}
}
});
/**
* Helper function prefixes sources paths in sourceMap files with a sourceRoot.
*
* `grunt-contrib-cssmin` does not apply the correct sourceRoot for each
* path in the `sources` Array. See github issue #248 for further info:
* (https://github.com/gruntjs/grunt-contrib-cssmin/issues/248)
*
* #param {String} filePath - The path to the sourceMap to fix.
* #param {String} sourceRootPrefix - The sourceRoot prefix e.g. ../../
*/
function prefixSourceMap(filePath, sourceRootPrefix) {
var json = grunt.file.readJSON(filePath);
json.sources = json.sources.map(function (_path) {
return sourceRootPrefix + _path;
});
grunt.file.write(filePath, JSON.stringify(json));
}
// Register Task to invoke function for fixing paths in each sourceMap file.
grunt.registerTask('fixSourceMaps', 'Fix paths in each sourceMap', function () {
prefixSourceMap('web/assets/dist/css/vendors.min.css.map', '../../../../');
prefixSourceMap('web/assets/dist/css/app.min.css.map', '../../../../');
});
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask("default", [
'cssmin',
'fixSourceMaps' // <-- Must be after cssmin tasks.
]);
};
Notes
The the custom grunt.registertask named fixSourceMaps invokes the prefixSourceMap function for each sourceMap file to fix.
Two arguments are passed to the prefixSourceMap function, namely:
filePath - The path to the sourceMap to fix.
sourceRootPrefix - The sourceRoot prefix e.g. ../../
For fixing both sourceMap files (vendors.min.css.map and app.min.css.map) the sourceRootPrefix argument is specified as ../../../../ - This is specified as four levels due to the relationship between the resultant .min.css file and it's location to the original unminified source .css file within your directory structure.
If you were to add another set of .css files to minify to your cssmin.target.files Task, in which the relationship between the resultant .min.css file and the unminified source .css file was not four levels, you will need to specify a different sourceRootPrefix value.
For example, lets say in cssmin.target.files the following css is to be minified:
// ...
target: {
files: {
//...
'web/assets/quux.min.css': [ // <-- Two levels deep
'path/to/file/foo.css',
'path/to/file/baz.css'
],
// ...
}
}
// ...
As the resultant quux.min.css is saved in the directory two levels deep then the sourceRootPrefix argument is passed to the prefixSoureMap functions as '../../'. For example:
//...
grunt.registerTask('fixSourceMaps', 'Fix paths in each sourceMap', function () {
prefixSourceMap('web/assets/quux.min.css.map', '../../');
//...
});
//...

as explained in comment above, there was 2 issues in my Gruntfile :
missing root directive as explained by #RobC
original assets were not accessible by the webserver, so I moved them in a location under the web root of my virtualhost. In my case web/assets. Here is my cssmin task that is now working:
cssmin: {
options: {
root: 'web/assets/dist/css/',
report: 'gzip',
keepSpecialComments: 0,
sourceMap: true,
outputSourceFiles: true
},
target: {
files: {
'web/assets/dist/css/app.min.css': [
'web/assets/css/jumbotron-narrow.css',
'web/assets/css/custom.css',
],
}
}
},

Related

Use .styl file as dependency to compile another

I have two files:
1 - common.styl (this file has imports that will be used on all pages of my project.)
#import 'utils/variables.styl'
#import 'utils/fonts.styl'
#import 'utils/mixin.styl'
2 - home.styl (this file is only for home section of my project and depends on common.styl)
body
font-family CoolDown
.box
background $commonBg
In my gulpfile I've created two tasks, one to compile the common.styl and another to compile all the pages of my project.
Common task:
gulp.task('commonCSS', function () {
return gulp.src('src/styles/common.styl')
.pipe($.plumber())
.pipe($.stylus({
'include css': true,
use: [jeet(), nib(), rupture()],
import: ['jeet', 'nib', 'rupture']
}))
.pipe($.autoprefixer(stylBrowsers))
.pipe(gulp.dest(paths.build + 'css'))
.pipe(reload({ stream: true }));
});
My problem is the pages task, this only works if I put the common.styl file in the src path and concat them. But doing this way I need to put a name in the file. I would like to use the name of the current .styl being compiled.
gulp.task('pagesCSS', ['commonCSS'], function () {
return gulp.src(['src/styles/common.styl', 'src/styles/pages/**/*.styl'])
.pipe($.plumber())
.pipe($.concat('**page.css**')
.pipe($.stylus({
'include css': true,
use: [ jeet(), nib(), rupture() ],
import: ['jeet', 'nib', 'rupture']
}))
.pipe($.autoprefixer(stylBrowsers))
.pipe($.minifyCss())
.pipe(gulp.dest(paths.build + 'css'))
.pipe(reload({ stream: true }));
});
The question is: There is a way to include the common.styl to be used by pagesCSS task?
Maybe I'm missing something or using the wrong solution here.
Can't you just #import or #require common.styl in those files? #import "../../common" and Stylus will include them for every page, no need for concat.
Or...
You can use import option you're already have in your config object. Stylus will include common.styl at the beginning of each file by itself.
First add paths: ['node_modules', 'src/styles']. This way Stylus will know how to resolve paths for imports. You can probably skip this step if you provide full path in next one.
Now you can add your common.styl to import: ['jeet', 'nib', 'rupture', 'common'].
I'm using this configuration for my variables so I don't have to include them in each file.
Full example should look somewhat like that:
gulp.task('pagesCSS', ['commonCSS'], function () {
return gulp.src('src/styles/pages/**/*.styl') // <-- only pages styles are piped
.pipe($.plumber())
// .pipe($.concat('**page.css**') // <-- remove this line
.pipe($.stylus({
'include css': true,
use: [ jeet(), nib(), rupture() ],
paths: ['node_modules', 'src/styles'] // <-- resolve your styles path
import: ['jeet', 'nib', 'rupture', 'common'] // <-- add your shared file
}))
.pipe($.autoprefixer(stylBrowsers))
.pipe($.minifyCss())
.pipe(gulp.dest(paths.build + 'css'))
.pipe(reload({ stream: true }));
});

Sourcemaps are detected in chrome but original source is not loaded, using webpack-2

When running an application that is built using webpack 2, sourcemaps are detected in chrome but original source is not loaded.
I'm using webpack beta21.
These files used to be detected automatically, ie when a breakpoint was put in the the output from webpack js file, the source view would jump to the original source input to webpack. But now I am stuck with this screen:
config:
var path = require("path");
var webpack = require("webpack");
var WebpackBuildNotifierPlugin = require('webpack-build-notifier');
const PATHS = {
app: path.join(__dirname, '../client'),
build: path.join(__dirname, '../public')
};
module.exports = {
entry: {
app: PATHS.app + '/app.js'
},
output: {
path: PATHS.build,
filename: '[name].js'
},
devtool: "source-map",
module: {
loaders: [
{
test: /\.js?$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'client'),
],
exclude: /node_modules/
},
{
test: /\.css/,
loader: "style!css"
}
]
},
resolve: {
// you can now require('file') instead of require('file.js')
extensions: ['', '.js', '.json']
} ,
plugins: [
new WebpackBuildNotifierPlugin()
]
};
Generated files with source maps won't automatically redirect to their original files, because there's potentially a 1-to-many relationship.
If you see the message Source Map Detected, the original file should already appear on the side file tree or the file explorer via Crl + P. If you don't know the original file name, you can open the source map file itself.
The source map path can be identified by a //# sourceMappingURL= comment or the X-SourceMap header:
Open up the source map via url and look for the sources property for the original file name:
The original file should be visible in the sources panel:
If you don't see the message Source Map Detected
You can manually add an external source map by right clicking and selecting Add Source Map:
Additional Resources
If that still doesn't work, you can try a Source Map Validator
For webpack specifically, you can configure the devtool property
If you're mapping to a workspace, that means you already have the source code. Including the source code in your source map is creating an unnecessary redundancy.
Use nosources-source-map instead.
The issue with external source maps was fixed in Chrome 52 but it looks like you've got your devtool set differently from mine, I use:
devtool: '#source-maps'
How are you building your source? If you're running with -d it will switch to inline source maps

RequireJS text plugin: cannot load HTML from other domain

I'd like to fetch some html from another domain with require.js. I know that CORS policies doesn't allow this easily. Note: I have configured the web server (with Access-Control-Allow-Origin "*" and other directives) and require.js so far that all JS and CSS files (css with require-css plugin) gets loaded from the other domain as expected - just fetching html makes problems. But in the browser network protocol I can see that the html content even gets loaded. However, this content does not get passed to the require function! The browser gets the content, but require.js doesn't provide it as an parameter...
My configuration:
requirejs.config({
baseUrl: "http://some.other.domain/",
paths: {
jquery: 'ext/jquery/jquery.min',
htmlTemplate: 'test.html?',
siteCss: '../css/site'
},
shim: {
htmlTemplate: [
'css!siteCss'
]
},
config: {
text: {
useXhr: function (url, protocol, hostname, port) {
return true;
}
}
},
map: {
'*': {
text: 'ext/require/text',
css: 'ext/require/css.min'
}
}
});
require(['text!htmlTemplate'], function (htmlTemplate) {
console.log(htmlTemplate); // prints 'undefined' into the console
});
Two notes: The useXhr configuration is taken from require.js text plugin adds “.js” to the file name but it makes no difference if it is there or not. I appended a ? to htmlTemplate path. With this the .js does not get appended to the URL and the browser loads the html content - as said before, unfortunately, without that require.js is passing it to parameter htmlTemplate.
What can I do? I've read that if I use the require.js optimizer the generated file wouldn't have this problem anymore (however that works...). But I need to develop my JS without optimization on every edit.
Update: Found one solution but I'd be happy if anyone can provide the 'right' solution.
I've found the actual problem! This part:
config: {
text: {
useXhr: function (url, protocol, hostname, port) {
return true;
}
}
},
should really do it. However, I found out that it wasn't called at all. Instead, the default implementation was called. And this returned false.
To make it work it is necessary to have the right keys in the config section since the mapping doesn't seem to be evaluated for it.
So this is the right configuration that fetches HTML from the other domain:
requirejs.config({
baseUrl: "http://some.other.domain/",
paths: {
jquery: 'ext/jquery/jquery.min',
htmlTemplate: 'test.html', // // ---> removed the '?'
siteCss: '../css/site'
},
shim: {
htmlTemplate: [
'css!siteCss'
]
},
config: {
'ext/require/text': { // ---> full path is required!!!
useXhr: function (url, protocol, hostname, port) {
return true;
}
}
},
map: {
'*': {
text: 'ext/require/text',
css: 'ext/require/css.min'
}
}
});
require(['text!htmlTemplate'], function (htmlTemplate) {
console.log(htmlTemplate); // now prints HTML into the console!!!
});
Hallelujah!
Found the right hint here. Another option might be to set the path for text. At least the configuration must be set somehow so that the function gets called...
I think I've found a solution. Doc of requirejs/text:
So if the text plugin determines that the request for the resource is on another domain, it will try to access a ".js" version of the resource by using a script tag. Script tag GET requests are allowed across domains. The .js version of the resource should just be a script with a define() call in it that returns a string for the module value.
Because of that I changed the configuration to this, so text is not used anymore:
requirejs.config({
baseUrl: "http://some.other.domain/",
paths: {
jquery: 'ext/jquery/jquery.min',
htmlTemplate: 'test.html', // removed the '?'
siteCss: '../css/site'
},
shim: {
htmlTemplate: [
'css!siteCss'
]
},
map: {
'*': {
// removed the text plugin
css: 'ext/require/css.min'
}
}
// removed the useXhr configuration for the text plugin
});
require(['htmlTemplate'], function (htmlTemplate) {
console.log(htmlTemplate); // prints '<div>Here I am!</div>' into the console
});
Now http://some.other.domain/test.html.js gets loaded. The content of test.html is:
define(function () {
return '<div>Here I am!</div>';
});
So I surrounded the HTML with a little bit of JS - no problem to me. And now htmlTemplate is set to the expected string. It's not pure HTML anymore, but since it is a fixed template (i.e. not generated) it may be acceptable.
I am getting No 'Access-Control-Allow-Origin' header is present on the requested resource. after adding the code
config: {
'ext/require/text': { // required!!!
useXhr: function (url, protocol, hostname, port) {
return true;
}
}
},

how to make webpack sourcemap to original files

I'm trying to debug an application written in Angular 2 build from webpack with VScode. I'm able to use the debugger for chrome extension in VSCode to attach my application. And it did hit the break point of the compiled js file. But it cannot find the sourcemap files.
It seems that webpack will have a webpack:// to host the files which the *.js file pointed to, like in the image:
And I can set the breakpoint inside the ts files inside webpack folder. However vscode is not able to find the ts files. So I change the configuration of webpack to
output: {
path:root('__build');
devtoolModuleFilenameTemplate: function(info){
return "file:///"+info.absoluteResourcePath;
}
},
And then all files seemed to map to the absolute paths of the original ts files. And in chrome developer tool it looks like this:
But both chrome and vscode said the files inside this file:// is different from the original ts files. So I'm wondering whether there's a way that in webpack's configuration could make *.js file sourcemap to original ts files. And here's all my configurations:
typescript configuration:
{
"compilerOptions": {
"outDir": "dist",
"target": "ES5",
"module": "commonjs",
"sourceMap": true
}
}
webpack config:
{
entry: "./src/app/bootstrap",
output: {
path: root('__build__'),
filename: env({
'development': '[name].js',
'all': '[name].[hash].min.js'
}),
devtoolModuleFilenameTemplate: function(info){
return "file:///"+info.absoluteResourcePath;
}
},
devtool:'source-map',
devServer: {
contentBase: "public/"
}
}
Another thing is that if in chrome developer tools, if I add the original files into the workspace and map the files from file:// to this folder, I can actually set breakpoints inside these files. So I'm wondering there's a way to map to local resources in vscode.
I changed this:
output: {
// ...snip...
devtoolModuleFilenameTemplate: function(info){
return "file:///"+info.absoluteResourcePath;
}
},
to this:
output: {
// ...snip...
devtoolModuleFilenameTemplate: function(info){
return "file:///"+encodeURI(info.absoluteResourcePath);
}
},
and now it encodes the spaces properly, and the sourcemap file works as expected.
Thanks to Rob Lourens, this problem is caused by spaces and other special characters in the file path that may break sourcemaps.

Gruntfile to minify all HTML files of a folder?

I’m using https://github.com/jney/grunt-htmlcompressor to compress HTML files. But it’s requiring me to manually type-in all the HTML files which I want minified:
grunt.initConfig({
htmlcompressor: {
compile: {
files: {
'dest/index.html': 'src/index.html'
},
options: {
type: 'html',
preserveServerScript: true
}
}
}
});
Is there a way to specify that I want to minify all HTML files of the entire folder (and subfolders)?
Or, in case this html-compressor is limited, are you aware of a different npm package that does this HTML mification?
The glob pattern should be allowed for any grunt task by default. Then simply use dynamic files to build each one to your dest
Instead of:
files: {
'dest/index.html': 'src/index.html'
},
Go with:
files: [
{
expand: true, // Enable dynamic expansion.
cwd: 'src/', // Src matches are relative to this path.
src: ['**/*.html'], // Actual pattern(s) to match.
dest: 'dest/', // Destination path prefix.
},
],
As far as another plugin I would recommend grunts contrib version since it's more common and maintained by the grunt team.