sindresorhus / gulp-autoprefixer

Prefix CSS
MIT License
694 stars 50 forks source link

Stripping out inline source maps #1

Closed andrewjmead closed 9 years ago

andrewjmead commented 10 years ago

I though it was an issue with gulp-sass, but it turns out that gulp-autoprefixer was the culprit.

Problematic Task

gulp.task('css:dev', function () {
    return gulp.src('public/css/core.scss')
        .pipe(sass({sourceComments: 'map', errLogToConsole: true}))
        .pipe(autoprefix())
        .pipe(gulp.dest('public/css/'));
});

Issue

After the scss is compiled into css, it has inline source maps that look like the following

/*# sourceMappingURL=data:application/json;base64,[base64 data]*/

Once that file is run through autoprefixer, the source maps don't exist. I wasn't able to find any documenation

battaglr commented 10 years ago

Can you please confirm that the version you are using is the v1?

andrewjmead commented 10 years ago

Yes, I'm on 1.0.0. I found the problematic line:

res = autoprefixer(fileOpts).process(file.contents.toString(), {
    map: file.sourceMap ? {annotation: false} : false, // ***** causing the issue
    from: file.relative,
    to: file.relative
});

postcss/autoprefixer readme.md - Autoprefixer supports inline source maps too.

It seems like everything should work, but it's removing the css source map comment entirely.

andrewjmead commented 10 years ago

I just discovered an inline option that postcss/autoprefixer-core has:

res = autoprefixer(fileOpts).process(file.contents.toString(), {
    map: {inline: true}, // let users of gulp-autoprefixer set some options
    from: file.relative,
    to: file.relative
 });

This adds support for inline maps. Is this configuration exposed to users of gulp-autoprefixer?

sindresorhus commented 10 years ago

As mentioned in the readme gulp-sourcemaps is the blessed way to do source maps in gulp. You shouldn't have to mess around with individual plugins source map settings.

andrewjmead commented 10 years ago

Hmmm, I'll give that another shot. I tried to setup source maps with gulp-sourcemaps but was not able to successfully do so.

andrewjmead commented 10 years ago

https://github.com/floridoo/gulp-sourcemaps does not support sass/scss at the moment. I opened an issue with them, but I still with the source maps configs were exposed via this plugin.

andrewjmead commented 10 years ago

My Solution

gulp-sass seems to only support inline source maps. When I switch to gulp-ruby-sass, everything started to work well.

For the record, here is the final working task:

gulp.task('css:dev', function () {
    return gulp.src('public/css/core.scss')
        .pipe(sass({sourcemap: true, sourcemapPath: '../scss'}))
        .on('error', function (error) {
            console.error(error);
            this.emit('end');
        })
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(autoprefix({
            browsers: ['last 2 versions']
        }))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('public/build/css/'))
        .pipe(livereload({auto: false}));
});
sindresorhus commented 10 years ago

@andrewjmead yup https://github.com/dlmanning/gulp-sass/pull/51

koistya commented 10 years ago

After upgrading to gulp-autoprefixer v1.0.0:

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();

gulp.task('styles', function () {
    return gulp.src('src/styles/bootstrap.less')
        .pipe($.sourcemaps.init())
        .pipe($.less())
        .pipe($.autoprefixer())
        .pipe($.sourcemaps.write())
        .pipe(gulp.dest('./build/css'));
});
$ gulp styles
[09:58:04] Using gulpfile .\gulpfile.js
[09:58:04] Starting 'styles'...

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map's "file" property. Both were omitted.
    at SourceMapGenerator_applySourceMap [as applySourceMap] (.\node_modules\gulp-autoprefixer\node_modules\vinyl-sourcemaps-apply\node_modules\source-map\lib\source-map\source-map-generator.js:171:17)
    at applySourceMap (.\node_modules\gulp-autoprefixer\node_modules\vinyl-sourcemaps-apply\index.js:11:15)
    at DestroyableTransform._transform (.\node_modules\gulp-autoprefixer\index.js:35:5)
    at DestroyableTransform.Transform._read (.\node_modules\gulp-autoprefixer\node_modules\through2\node_modules\readable-stream\lib\_stream_transform.js:184:10)
    at DestroyableTransform.Transform._write (.\node_modules\gulp-autoprefixer\node_modules\through2\node_modules\readable-stream\lib\_stream_transform.js:172:12)
    at doWrite (.\node_modules\gulp-autoprefixer\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:237:10)
    at writeOrBuffer (.\node_modules\gulp-autoprefixer\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:227:5)
    at DestroyableTransform.Writable.write (.\node_modules\gulp-autoprefixer\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:194:11)
    at write (.\node_modules\gulp-less\node_modules\through2\node_modules\readable-stream\lib\_stream_readable.js:605:24)
    at flow (.\node_modules\gulp-less\node_modules\through2\node_modules\readable-stream\lib\_stream_readable.js:614:7)

Any ideas on how to fix it?

anthonyhastings commented 10 years ago

I'm having the same problems as @andrewjmead. When I run my SCSS files through gulp-ruby-sass and then through the autoprefixer, a map file is indeed being generated, but not being referenced in the compiled CSS file. It still has an inline sourcemap. My task is as follows:

return gulp.src('./css/src/*.scss')
    .pipe(plumber())
    .pipe(sass({
        noCache: false,
        sourcemap: true,
        style: 'compact'
        sourcemapPath: './src'
    }))
    .pipe(sourcemaps.init({
        loadMaps: true
    }))
    .pipe(autoprefixer({
        browsers: ["last 2 versions", "> 1%", "ie 8", "ie 7"],
        cascade: false
    }))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./css/'));
andrewjmead commented 10 years ago

@antwan1986 Are you sure that sourcemapPath is correct?

anthonyhastings commented 10 years ago

If I've read the sourcemapPath documentation correctly:

A relative path from the output CSS directory to the Sass source directory as seen by your web server.

My folder structure is as follows:

/gulpfile.js
/css/build.css
/css/src/build.scss

My sourcemapPath in this instance would be correct, no? from /css to /css/src/?

andrewjmead commented 10 years ago

@antwan1986 Your sourcemapPath would be correct then. Can you try copying the rest of my task and seeing if that helps? Our look identical (and mine works) except for a few plugin options.

fixmysync commented 10 years ago

I'm having the same problem as @antwan1986 - I would like to use the external sourcemap but autoprefixer keeps overwriting the /# sourceMappingURL=css.map / if I follow the docs for gulp-autoprefixer, gulp-ruby-sass and gulp-sourcemaps.

@andrewjmead I tried your set up, but that gave me inline sourcemaps.

This is the closest I've got to getting it right, but a second map called styles.css.map.map is being generated.

This is my gulpfile:

 // Load Plugins
var gulp = require('gulp');
var Combine = require('stream-combiner');
var plugins = require('gulp-load-plugins')({ camelize: true });
var lr = require('tiny-lr');
var server = lr();
var sourcemaps = require('gulp-sourcemaps');
var sass = require('gulp-ruby-sass');

// Styles Task
gulp.task('styles', function() {
    return gulp.src('library/scss/source/*.scss')
        .pipe(sass({style: 'compact', compass: true}))
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(plugins.autoprefixer('last 2 versions', 'ie 9', 'ios 6', 'android 4'))
        .pipe(sourcemaps.write('../scss/source/', {addComment: true}))
        .pipe(plugins.livereload(server))
        .pipe(gulp.dest('library/css'))
        .pipe(plugins.notify({ message: 'The styles tasks are done!' }));
}); 

// more tasks here

gulp.task('default', ['styles', 'plugins', 'scripts', 'watch']);

And this is what node has to say when I run gulp styles

Using gulpfile ~/path-to-my-file/gulpfile.js
[00:53:22] Starting 'styles'...
[00:53:22] gulp-ruby-sass: directory
[00:53:33] gulp-ruby-sass: write style.css
  write style.css.map
[00:53:34] style.css.map was reloaded.
[00:53:34] style.css was reloaded.
[00:53:34] style.css.map.map was reloaded.
[00:53:34] style.css.map was reloaded.
[00:53:34] Live reload server listening on: 35729
[00:53:34] gulp-notify: [Gulp notification] The styles tasks are done!
[00:53:35] gulp-notify: [Gulp notification] The styles tasks are done!
[00:53:35] gulp-notify: [Gulp notification] The styles tasks are done!
[00:53:35] gulp-notify: [Gulp notification] The styles tasks are done!
[00:53:35] Finished 'styles' after 14 s

I've tried moving .pipe(sourcemaps.init({loadMaps: true})) to after autoprefixer and I've also tried putting .pipe(sourcemaps.init()) there instead - none of these options work.

andrewjmead commented 10 years ago

@MandyMadeThis I was unable to find a working combination aside from the one I posted :frowning:

anthonyhastings commented 10 years ago

Just retried your suggestions myself @andrewjmead, the sourcemap stays inline for me :(

andrewjmead commented 10 years ago

Yeah, you might have to use inline maps (I have to). Are they not working or is there some other reason why you need external source maps?

anthonyhastings commented 10 years ago

I need them to be external for my production environments. I find them useful to have on production servers for easier debugging, however, the source map size can be considerable and there's no point in putting that extra weight onto the client / customer if they'll never use it themselves.

andrewjmead commented 10 years ago

As mentioned in the readme gulp-sourcemaps is the blessed way to do source maps in gulp. You shouldn't have to mess around with individual plugins source map settings.

@sindresorhus I agree. The issue is that the two main SASS gulp tasks, gulp-sass, and gulp-ruby-sass have their own sourcemap settings. There is not good way to get all the plugins to work together.

andrewjmead commented 10 years ago

@antwan1986 Here is a good solution for external sourcemaps.

Gulp-sass only generates inline sourcemaps, but you can load those in with gulp-sourcemaps, then save them as external sourcemaps with no extra files or strange things. Most of the problems I had came form open issues with gulp-ruby-sass.

gulp.task('sass:main', function () {
    return gulp.src('theme/scss/styles.scss')
        .pipe(sass({sourceComments: 'map'}))
        .on('error', function (error) {
            console.error(error);
            this.emit('end');
        })
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(autoprefix({
            browsers: ['last 2 versions']
        }))
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('./theme/css'));
});
fixmysync commented 10 years ago

@andrewjmead - will this solution strip the inline sourcemap from the CSS file after the task runs? Or will you have both an external .map file and an inline soucemap?

andrewjmead commented 10 years ago

@MandyMadeThis I should have clarified :smile:. This solution starts with an inline sourcemap (all gulp-sass supports). Then the sourcemaps are loaded in and saved as external sourcemaps. The internal sourcemap is removed, and is replaced by a reference to the external sourcemap.

In the end, you have a css file with a path reference to an external sourcemap :smile:!

jaidetree commented 10 years ago

@andrewjmead I am trying your solution but once autoprefix() is run, the sourcemap gets removed from the gulp-sass buffer and sourcemaps.write() doesn't load anything.

Resulting in a gulp-sourcemap-write: source file not found:<file path>. However if I remove autoprefix, it generates the correct sourcemap but then I'm lacking any of the prefixed css.

gulp.task('sass', function () {
  gulp.src(dir('static/sass/**/*.scss'))
    .pipe(sass({
      sourceComments: 'map',
      includePaths: [dir('static/sass')]
    }))
    .pipe(sourcemaps.init({ loadMaps: true }))
    .pipe(autoprefix({
      browsers: ['last 2 versions']
    }))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest(dir('static/css')))
    .pipe(reload({ stream: true }));
});
aaronjensen commented 10 years ago

@jayzawrotny this is more complicated (as it includes prepending some vendor css) and working for me (pardon the coffee)

gulp = require('gulp')
autoprefixer = require('gulp-autoprefixer')
sass = require('gulp-sass')
reload = require('browser-sync').reload
sourcemaps = require('gulp-sourcemaps')
filter = require('gulp-filter')
merge = require('merge-stream')
concat = require('gulp-concat')

gulp.task('css', ->
  vendor = gulp.src([
    'node_modules/normalize.css/normalize.css'
  ])

  main = gulp.src(['app/assets/css/main.sass'])
    .pipe(sourcemaps.init())
    .pipe(sass())
    .pipe(sourcemaps.write())

    .pipe(sourcemaps.init(loadMaps: true))
    .pipe(autoprefixer())
    .pipe(sourcemaps.write())

  merge(vendor, main)
    .pipe(sourcemaps.init(loadMaps: true))
    .pipe(concat("main.css"))
    .pipe(sourcemaps.write('./'))

    .pipe(gulp.dest('public/css'))

    .pipe(filter('**/*.css'))
    .pipe(reload(stream: true))
)
valdelama commented 10 years ago

I am having the same issue as @jayzawrotny, this works fine:

var startSassTask = lazypipe()
  .pipe(sourcemaps.init)
  .pipe(sass)
  //.pipe(prefix)
  .pipe(sourcemaps.write);

and as soon as I uncomment the .pipe(prefix) (which is using gulp-autoprefixer) then I get the error message:
gulp-sourcemap-write: source file not found: [path trying to find a css file but pointing at my sass dir]

@aaronjensen I think your solution only works if your src is a single file, not a glob

andrewjmead commented 10 years ago

@valdelama I did a quick write up of my solution (it's also posted above): http://www.devworkflows.com/posts/getting-scss-auto-prefixer-and-source-map-to-play-nice/

valdelama commented 10 years ago

@andrewjmead Thanks, I tried your solution and it did create a .map file but it wasn't recognised in chrome dev tools, or at least I couldn't see the .scss source files. If I use my code from above (with autoprefixer commented out) then I have inline sourcemaps that are picked up by chrome automatically. Also from what I can tell from the documentation it is now recommended to not use the gulp-sass built in sourcemaps and instead use gulp-sourcemaps for all plugins.

andrewjmead commented 10 years ago

@valdelama I'm with you. The issue is that the gulp plugin (in this case gulp-sass) needs to explicitly support gulp-sourcemaps. If plugin developers do support gulp-sourcemaps, awesome, but it's not supported automatically.

If the .map file is generated, but it's not showing, there is a chance that it's invalid or that you don't have the chrome dev tools properly setup.

valdelama commented 10 years ago

@andrewjmead gulp-sass does support it now, that's why if you check my code above you will see that this works fine (using gulp-sourcemaps):

var startSassTask = lazypipe()
  .pipe(sourcemaps.init)
  .pipe(sass)
  //.pipe(prefix)
  .pipe(sourcemaps.write);

but as soon as I uncomment the line calling gulp-autoprefixer then I get the gulp-sourcemap-write: source file not found error which is what lead me to believe this is a problem with gulp-autoprefixer and not gulp-sass

aaronjensen commented 10 years ago

@valdelama that does not work. This does: https://github.com/sindresorhus/gulp-autoprefixer/issues/1#issuecomment-57258788

aaronjensen commented 10 years ago

I believe the problem is w/ autoprefixer, they either dont' support gulp-sourcemaps properly or something else, but what I have in the comment works as a workaround

aaronjensen commented 10 years ago

@andrewjmead you may consider updating your post now that gulp-sass properly supports gulp-sourcemaps:

gulp.task('sass:custom', function () {
    return gulp.src('theme/scss/styles.scss')
        .pipe(sourcemaps.init())

        // Convert sass into css
        .pipe(sass())

        // Catch any SCSS errors and prevent them from crashing gulp
        .on('error', function (error) {
            console.error(error);
            this.emit('end');
        })

        .pipe(sourcemaps.write())

        // Load existing internal sourcemap
        .pipe(sourcemaps.init({loadMaps: true}))

        // Autoprefix properties
        .pipe(autoprefixer({
            browsers: ['last 2 versions']
        }))

        // Write final .map file
        .pipe(sourcemaps.write('./'))

        // Save the CSS
        .pipe(gulp.dest('./theme/css'));
});
andrewjmead commented 10 years ago

@aaronjensen I will :smile:

valdelama commented 10 years ago

@aaronjensen @andrewjmead I did try that method as well and I get a different kind of error, like this: "/vendor/assets/stylesheets/prism.scss" is not in the SourceMap. basically it errors on the first src file in the main .scss file (which is not an issue when I leave autoprefixer out of it)

andrewjmead commented 10 years ago

Cool, maybe my method broke with the latest gulp-sass update. I'll check it out.

aaronjensen commented 10 years ago

@valdelama you've got latest gulp-autoprefixer and gulp-sass? could you paste the entire task?

valdelama commented 10 years ago

@aaronjensen Package versions:

"gulp-autoprefixer": "^1.0.1",
"gulp-sass": "^1.0",
"gulp-sourcemaps": "^1.2.2"

Pasting the complete tasks is kind of difficult because it's broken up into multiple small reusable parts via lazy pipes but here's the relevant code:

var startSassTask = lazypipe()
  .pipe(sourcemaps.init)
    .pipe(sass, {
      errLogToConsole: true,
      includePaths: [vendorAssets.styles, bowerAssets, wegoAssets.styles, gemAssets],
      imagePath: '/images'
    })
    .pipe(sourcemaps.write)
    .pipe(sourcemaps.init, {loadMaps: true})
    .pipe(prefix)
  .pipe(sourcemaps.write, './');

var sassDevTask = startSassTask
  .pipe(gulp.dest, publicAssets)
  .pipe(browserSync.reload, {stream:true});

gulp.task('publicCss', function() {
  return gulp.src('./' + appAssets.styles + '/public_pages.scss',
                  './' + appAssets.styles + '/public_pages_ie8.scss')
    .pipe(sassDevTask());
});

It's part of a pretty large gulp file to replace the rails asset pipeline.

Am I right in thinking that the way this should work in an ideal world is like this (simplified demo):

gulp.src('./scss/*.scss')
  .pipe(sourcemaps.init())
    .pipe(sass())
    .pipe(autoprefixer())
  .pipe(sourcemaps.write())
  .pipe('./css');

?

aaronjensen commented 10 years ago

@valdelama looks like it's the same as mine, more or less. The only difference is I'm only passing one file through, perhaps that makes a difference in this house of cards :/

I believe what you pasted is how it should work, but it sounds like the problem is not w/ gulp-autoprefixer (according to #8).

fixmysync commented 10 years ago

I needed to use gulp-ruby-sass because my project is also using compass and gulp-sass does not support compass. So I came up with a solution and it's working beautifully. I've got a development styles task that gives me external sourcemaps and a production styles task that autoprefixes everything for me - I don't need both in development, so this solution works great for me.

Here is what it looks like:

 var gulp = require('gulp');
 var plugins = require('gulp-load-plugins')({ camelize: true });
 var livereload = require('gulp-livereload');

 // errors function
 function errorLog (error) {
     console.error.bind(error);
     this.emit('end');
 }

 // Development Styles Task
 gulp.task('devStyles', function() {
      return gulp.src('library/scss/main.scss')
          .pipe(plugins.rubySass({ style: 'compact', compass: true }))
          .on('error',errorLog)
          .pipe(gulp.dest('library/css'))
          .pipe(livereload())
          .pipe(plugins.notify({ message: 'The dev styles are done!' }));
 })

 // Production Styles Task
 gulp.task('prodStyles', function() {
      return gulp.src('library/scss/main.scss')
         .pipe(plugins.rubySass({ style: 'compressed', compass: true }))
         .on('error',errorLog)
         .pipe(plugins.autoprefixer( '> 1%', 'last 2 versions', 'ie 9', 'ios 6', 'android 4'))
         .pipe(gulp.dest('library/css'))
         .pipe(plugins.notify({ message: 'The production styles are done' }));
   });

  // A bunch more tasks here

  // Multiple Gulp Tasks
 // run: gulp for default tasks
 gulp.task('default', ['devStyles', 'vendor', 'scripts', 'watch']);

 // run: 'gulp build' for production tasks
 gulp.task('build', ['prodStyles', 'vendor', 'scripts']);
andrewjmead commented 10 years ago

@aaronjensen I'm unable to get gulp-sass to work with gulp-sourcemaps. I'm getting bad map files and duplicate map files: http://cl.ly/image/312o373X432e

The core.css.map file also does not work.

aaronjensen commented 10 years ago

@andrewjmead i'm not sure how the collabs feel about this becoming the sass/autoprefixer sourcemaps support thread, but maybe you could put your gulp task in here, it sounds like maybe you're writing to a file twice write('./') rather than writing inline write()

andrewjmead commented 10 years ago

@aaronjensen Sorry, I just copied your example :smile:. My old way still works, so I'm just going to keep using that.

valdelama commented 10 years ago

@aaronjensen @andrewjmead Just FYI, based on this comment I got it kind of working. Working as in I have sourcemaps and they lead me to the right file via dev tools however line numbers are wrong (as the original commentor mentions) and I still get the source file not found warning.
I also updated gulp-sass to 1.1.0 and gulp-sourcemaps to 1.2.4 which might have helped.
This is the code I'm using (with lazypipes):

var startSassTask = lazypipe()
  .pipe(sourcemaps.init)
    .pipe(sass, {
      errLogToConsole: true,
      includePaths: [vendorAssets.styles, bowerAssets.styles, wegoAssets.styles, gemAssets.styles],
      imagePath: (isDev ? '/images' : '/assets')
    })
  .pipe(sourcemaps.write, {includeContent: false})
  .pipe(sourcemaps.init, {loadMaps: true})
    .pipe(prefix, {cascade: false})
  .pipe(sourcemaps.write);
polarathene commented 9 years ago

Pretty sure the issue is related to libsass outputting an incorrect sourcemap, autoprefixer runs fine when provided a proper sourcemap. Check that without autoprefixer that your sourcemap in devtools maps to the correct line in .scss, in my case it was either null or the last line of the file. It'll be fixed with libsass 3.2, more info here

polarathene commented 9 years ago

@andrewjmead @aaronjensen @antwan1986 @valdelama There was an update to gulp-sass today, it supports the v2 beta of node-sass and in turn libsass 3.1-beta. The fixed mappings and bugfixes should solve this issue.

I've provided code examples that worked for me for inline and external sourcemaps with embedded or external sources. You can see them here.