Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject middleware generated CSS files instead of reloading #411

Closed
zanona opened this issue Jan 29, 2015 · 24 comments
Closed

Inject middleware generated CSS files instead of reloading #411

zanona opened this issue Jan 29, 2015 · 24 comments

Comments

@zanona
Copy link

zanona commented Jan 29, 2015

I have the following middleware that accepts requests to .less files and compiles those, serving these file as CSS. However, when the response is sent, the browser reloads instead of injecting the changes.

Since this would be a pure CSS file only with a different extension (since it has already been compiled to CSS) how do I make Browser-sync inject the new styles as per with .css files instead of reloading the page?

<link rel=stylesheet href=main.less>
...
files: '*.less',
function (req, res, next) {
    if (req.url.match(/\.less$/)) {
        require('child_process').exec('lessc ' + req.url, function (e, o) {
            res.setHeader('Content-Type', 'text/css');
            return res.end(o); //Reloads the browser instead of injecting changes
        });
    } else {
        next();
    }
}
@shakyShane
Copy link
Contributor

Ahh nice - could you set-up a small example repo where I can test/implement this - I think it would be a cool feature.

@zanona
Copy link
Author

zanona commented Jan 30, 2015

@shakyShane
Copy link
Contributor

@shakyShane
Copy link
Contributor

Got this working locally and wow, what an awesome workflow...

@shakyShane
Copy link
Contributor

it'll end up being

/*global module, require*/

var url = require("url");

function less(src) {
    var f = require('fs').readFileSync('src/' + src).toString();
    return require('less').render(f);
}

module.exports = {
    files: ['src/*.less'],
    injectFileTypes: ["less"],
    tagNames: {
        "less": "link"
    },
    server: {
        baseDir: 'src',
        middleware: function (req, res, next) {
            var parsed = require("url").parse(req.url);
            if (parsed.pathname.match(/\.less$/)) {
                less(parsed.pathname).then(function (o) {
                    res.setHeader('Content-Type', 'text/css');
                    res.end(o.css);
                });
            } else { next(); }
        }
    }
};

@shakyShane
Copy link
Contributor

^ you can't just do a match on the url because of timestamps etc

shakyShane pushed a commit to BrowserSync/browser-sync-client that referenced this issue Feb 18, 2015
@zanona
Copy link
Author

zanona commented Feb 18, 2015

wow indeed! just tested on 2.1.6 and it works brilliantly!
Mind the possibilities, no need to flood dev environment by building production files anymore.
I saw you also added support for scss as well which is great.
It might also be worth it to add .styl as well for the folks using stylus.
Thanks a lot @shakyShane

@shakyShane
Copy link
Contributor

Haha! wow - no problem :)

We're creating a 'recipes` section soon and this workflow will be first on the list :)

@shakyShane
Copy link
Contributor

^ this will even work with the proxy too

@shakyShane
Copy link
Contributor

It might also be worth it to add .styl as well for the folks using stylus.

yeah - maybe, although any other can be added with

tagNames: {
    "styl": "link"
}

@zanona
Copy link
Author

zanona commented Feb 18, 2015

Sounds good enough to me 👍

@shakyShane
Copy link
Contributor

@zanona
Copy link
Author

zanona commented Feb 20, 2015

👍 very nice man! already using like crazy this new feature. And oh boy, it's so much better than less.js in the browser. Thanks again.

@shakyShane
Copy link
Contributor

Thanks to you for bringing it to my attention - feel free to contribute any workflows you have to that repo :)

@zanona
Copy link
Author

zanona commented Apr 17, 2015

hey @shakyShane, just revisiting this functionality after having stumbled upon an issue:

main.less

@import 'secondary.less';
body { background: red; }

secondary.less

body { font-size: 1.2em; color: green; }

Everything work as expected when saving main.less file. However, unfortunately, when saving secondary.less, which is an imported file, Browser Sync won't auto refresh the stylesheet.

I can see this happening due the fact that the request is not yet mapped for secondary.less but only for main.less, so saving it wouldn't trigger anything?

Is there any way you can think of to allow such imported stylesheets to be updated accordingly when saved?

Thanks in advance.

@zanona
Copy link
Author

zanona commented Apr 26, 2015

hey @shakyShane, perhaps it would be best if I open a new issue on this aspect? Please let me know

@zanona
Copy link
Author

zanona commented May 7, 2015

just in case someone is looking for this, I sort of, found a solution.

from my bs-config.js file

function reloadLess() {
    exec('browser-sync reload --files="*.less"');
}

module.exports = {
    files: [
        'src/*.html',
        'src/scripts/*.js',
        {
            match: ['src/styles/*.less'],
            fn: function (event, file) {
                if (event !== 'change') { return; }
                reloadLess();
                // it would be nice to reference the bs instance here so I could simply do
                // bs.reload('*.less');
            }
        }
    ],
    injectFileTypes: ['less'],
    ...
};

The only thing that would be nice here, since I use the global installation of browser-sync, is that from bs-config.js I haven't find any way to reference to the running browser-sync instance anywhere so that from the reloadLess method I could simply call bs.reload();

It would be nice that we could have a way to retrieve this reference somewhere.
I know that from within the fn method from the files property, the instance of the fileWatcher library is returned when using this.
So perhaps that could be a nice place to also provide browser-sync instance so we don't need to use the command line interface to reload the files.

I hope this can be helpful to someone.

@shakyShane
Copy link
Contributor

@zanona - agreed, you should be able to call methods on your instance from within the watcher callback... will certainly add that.

@zanona
Copy link
Author

zanona commented May 21, 2015

Thanks buddy

@shakyShane
Copy link
Contributor

@zanona browser-sync@2.7.3 will give you what you want, as seen here #631 (comment)

@zanona
Copy link
Author

zanona commented May 21, 2015

Beautiful! Updating…now 👍 Thanks Shane

@lunelson
Copy link

I'm trying to accomplish something similar to this, proxying Harp.js;

but Harp does all the pre-processing transparently, so I want BS to watch ['**/*.scss', '**/*.jade'] files, while injecting the changed .css and .html. Can this be done?

My attempt is based on the code here, and in the BS API and Options pages, and this thread. This code deals first only with .scss files.

var bs = require('browser-sync').create();

bs.init({
    proxy: "localhost:9000",
    files: [
        './**/*.jade',
        './**/*.js',
        {
            options: { ignoreInitial: true },
            match: './**/*.scss',
            fn: function (event, file) {
                if (event === 'change') {
                    bs.stream();
                }
            }
        }
    ]
});

is this at all on the right track... ?

@zanona
Copy link
Author

zanona commented Sep 24, 2015

hey @lunelson, have you checked the recipe for middleware CSS injection already?

https://github.com/Browsersync/recipes/tree/master/recipes/middleware.css.injection

Perhaps that can help you further. — I can see the injectFileTypes attribute is missing from your code.

All the best

@lunelson
Copy link

Thanks for that tip @zanona. I seem to have figured it out. injectFileTypes doesn't seem to be necessary—or maybe wouldn't have worked anyway—(I think) because from the browser's point of view it's accessing css files not scss. In other words the whole middleware thing of pre-processing and modifying the request is being handled by Harp. It seems that assuming Harp is running, it just comes down to telling BS what and when to reload/inject.

The following is working for me, to inject html changes from jade files, and inject css changes from scss files. js changes cause a full reload of course.

on the command line, in the project dir

# in one tab
harp server
# in a second tab
node bs.js

...and this is the content of bs.js. NB this relies on knowing the specific filenames of the css files your pages are referencing. You can also just pass a glob *.css but the injection is then slower

var bs = require('browser-sync').create();
var bs_html = require('bs-html-injector');

bs.use(bs_html, { files: './**/*.jade'});
bs.init({
  proxy: "localhost:9000",
  files: [
    './**/*.js',
    {
      match: './**/*.scss',
      fn: function (event) {
        if (event === 'change') {
          bs.reload(['style.css', '/global.css']);
        }
      }
    }
  ]
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants