Comments (40)
@import should have nothing to do with sprockets or tilt. Only places where sprockets gets involved is in the directive processors at the top of each file. After that, each template is rendered from right to left with each extension. In the case with less, @imports are supported to other files ending with .less extension.
If this is not the case can you show me in the code where with some examples of how this works in something like Sass/Compass?
from less-rails.
If this is not the case can you show me in the code where with some examples of how this works in something like Sass/Compass?
Be glad to once I get to my laptop.
The biggest issue is that Sprockets uses its internal representation of the import hierarchy to determine staleness. This means that updated Less partials won't take effect until you make a change to the topmost stylesheet.
I suspect that this will also prevent Sprockets from updating the asset tag hash unless the topmost stylesheet has been changed as well.
from less-rails.
From the Compass issue on Rails 3.1 support:
What we'll do is make Sass @import directives find things via sprockets. Sass users shouldn't have to use hacky comments to accomplish this behavior. Sass has already implemented this feature, we just need to glue it together.
from less-rails.
Another issue it that Sprockets will serve you partials individually in development for ease of debugging. When @import
is processed independently in Less, Sprockets can't do this. So, without integration with Sprockets,
- staleness of compiled stylesheets isn't detected correctly,
- bundling of stylesheets can't be enabled/disabled depending on environment
from less-rails.
I've seen what you have been talking about and I'll read over this in detail when I finish up the asset helpers this weekend.
from less-rails.
Great. Thanks!
from less-rails.
I'm not sure I want to do this one because it would require hacking how less.rb does it's @import. Cant we just leave this as is and not monkey patch LESS's @import?
from less-rails.
Without Sprockets hooks, Less in Rails is flat-out broken.
Test this yourself. Run rake assets:precompile
with your current stylesheets. Then change an @import
ed stylesheet and run it again. The cache-busting digest stamp will not have changed. Any browsers that already have the asset downloaded won't re-fetch it until it expires, which could potentially be years in the future.
The Sass project added hooks to handle this. Ask Less to do it too.
from less-rails.
@stouset As I was saying in the other ticket. Can please submit a patch with your ideas?
from less-rails.
I would have already if I had the time. :)
from less-rails.
OK, then your likely to see this in a holding pattern for a week or more. So you have plenty of time to chip in and help out. I think I have us in a good spot with the current state and the testing system. FWIW, you may be able to get by using the depend_on directive.
from less-rails.
That'll (barely) work for now (I have a lot of partials), but the core issue needs to be resolved before less-rails
is ready for 3.1 apps in production.
For the time being, it might be worth filing a bug against less
to add hooks for this. You know the internals better than I do, so you'd probably have a better notion of how best they could add support for it.
from less-rails.
I just wanted to +1 the need for the hooks. There are workaround, like depends_on
, but ultimately the hooks are needed for that It Just Works™ experience.
from less-rails.
If you guys can point me to the sprockets hooks, I can have a crack at it. We are using less-rails in production, and are having this issue.
from less-rails.
@cowboyd I am not sure what @stouset was referring too when he said you had to just hook into sprockets. I can not find nothing in either sprockets or tilt that have to do with a standard way to do @import for CSS. In my opinion it is just out of the scope of the each project, but willing to learn where I might be wrong.
That said, sass-rails has behavior for @import
which is heavily tied to Sass's @import
and making it work with the pipeline. In my mind this get's complex because you would be in the area of over riding or hooking into LESS's @import
and method chaining or injecting new behavior t make less-rails act like sass-rails, or more to the point, LESS work like SASS.
So maybe the place to look is the README on the sass-rails for the section called "Glob Imports" https://github.com/rails/sass-rails and the importer class https://github.com/rails/sass-rails/blob/master/lib/sass/rails/importer.rb
So I have no real direction at a quick glance, just places to go digging. Here is another, all the current issues on the less.js project with @import
marked in them :) https://github.com/cloudhead/less.js/issues/search?q=%40import
from less-rails.
I should say above, I "can not find" any hooks.
from less-rails.
Ok, I've had a look, and I think that it should actually be a pretty simple fix, but it will require monkey patching less.js at runtime.
If I understand correctly in this code, scope
is an instance of Sprockets::Context
which is unique to the asset being compiled. We can add any dependencies there, and sprockets will respect them.
To accomplish this, we will override tree.Import.prototype.toCSS() to add the path as a depdendency to the scope
A potential problem with this is that we'll need to inject the sprockets context as global variable into the environment, which could wreak havoc in multi-threaded environments. There might be a way to access the options passed into the parser from the the invidual AST nodes, which would be a godsend. I'll investigate that. Otherwise, we'll have to modify less.rb to allow for the creation of a unique JavaScript context for each asset.
from less-rails.
Yea, I thought about that potential problem in multi-threaded envs or apps with mountable engines by putting scope there. I use it in the template handlers. https://github.com/metaskills/less-rails/blob/master/lib/less/rails/helpers.rb#L94
Any thoughts in this would be welcome. I am not sure if my concern was valid or not, especially since or do not know how engines and that scope could be shared or not. Either way, me putting it in one place in the less code is very brittle.
from less-rails.
Did I say simple fix? :) That was perhaps a bit too soon. The good news is that less does pass all options to the root toCSS()
invocation to all child nodes.
To be clear, the only thing I'm proposing is to add the path passed to @import as a sprockets dependency. I would not change anything about the behavior of @import
from less-rails.
Ok, I think we can rig both cases (helpers, and @import
hooking) to options passed in at tree render time. That should cleanly avoid threading issues. e.g.
engine = parser.parse(data)
engine.to_css :sprockets => scope
I'll have a closer look at this tomorrow, but probably won't get anything working until next week.
from less-rails.
About ready to get back into this. Anything worth sharing before I do?
from less-rails.
Turns out not to be such a simple thing. Turns out we need to patch in some non-trivial changes to less.js to get it to pass down options to all subnodes in the parse tree.
I still have that code lying around somewhere. I'll dig it out and have a look.
from less-rails.
I can't contribute to the solution, but here is another reason why the @import
probably needs to be integrated with Sprockets: Let's say you want to use mixins in your article.css.less
file:
@import "twitter/bootstrap";
.article {
.border-radius(15px);
}
But in comment.css.less
you also need mixins:
@import "twitter/bootstrap";
.comment {
.border-radius(15px);
}
Now if you want to compile everything into a single application.css (through require_tree .
), the whole bootstrap library will be included twice, because Sprockets doesn't know about the duplicate @import
.
from less-rails.
(Using //= require "twitter/bootstrap"
instead of @import
will not work, of course, because then LESS doesn't know about the mixins.)
from less-rails.
tl;dr: I was wrong; please ignore my last two comments.
It seems that I spoke too soon. Sass appears to have the same problem:
@import
causes duplicate imports.- https://github.com/rails/sass-rails recommends against
//= require
, but I assume it's what most people are doing because of Rails's default application.css.
So I guess the preferred way would be to use //= require "twitter/bootstrap"
in application.css.less, and @import "twitter/bootstrap/mixins";
wherever you need the mixins, so as to not re-include the entire framework. (Including the mixins is a noop until you actually use one of the mixins.)
Anyway, sorry for spamming. :-)
from less-rails.
OK, I am back into this one. I would love for some feedback on setting up a concrete understanding of what needs to be "fixed"
Let's assume I have some killer less framework nested away deep in my own vendor/assets or perhaps a gem and that this is hooked up correctly via less-rails load paths. For now the only thing this framework has is a single .mycolor
function which is set to output color:green;
. Here is the structure I have
File: app/assets/stylesheets/application.css
/*
*= require application/base
*= require_self
*/
File: app/assets/stylesheets/application/base.css.less
@import 'less_framework/base';
#container {
.mycolor();
}
So assuming that I have config.assets.digest = true
and I run the rake assets:precompile
. I end up with the following two files with identical content of #container{color:green;}
- application-ec2842901042e01dc30f351daefbc5cf.css
- application.css
So now I go into my killer less framework and change the .mycolor()
function to return red. This will mimic a simulated behind the scenes update of some framework gem. I destroy my public/assets
directory and re-run the rake assets:precompile
task. Both of these files below are generated with new identical content of #container{color:red;}
- application-ec1e498de79479c0b96ff4d08c83909b.css
- application.css
At this point I can easily see that the digest has changed. So initially I am not seeing how things are totally broken?
from less-rails.
I replied by email, but it didn't seem to go through.
I destroy my
public/assets
directory...
This is broken. In no other asset engine is this required. Worse, if you forget this step, broken assets are pushed to your asset host with no prior warning.
In addition, asset-url
and friends are broken. These statements go directly into the precompiled assets, and should return the relative path to assets, including digests. Without this, any background images or other assets can only point to the non-digested version, meaning client-side asset caching cannot be used.
from less-rails.
I destroy my public/assets directory...
This is broken. In no other asset engine is this required. Worse, if you forget this step, broken assets are pushed to your asset host with no prior warning.
That is suboptimal, but I think calling it totally broken isn't completely fair. For the record the asset-* helpers do return relative paths with shas for us (we've been using this in production for several weeks); and, since we package each release and deploy it into a separate directory, there is never a chance of serving corrupted assets.
While it is a clear annoyance in development mode, it has performed flawlessly for us in production, which I think sets the bar for what is broken and what is not.
At the moment, my priority is refactor the less gem so that it can be used on jruby and rubinious, but I do have a personal desire to see this issue quashed. That said, the reason that this fix is non-trivial is that the less.js source needs to be modified to pass down context to each node as it is rendering the parse tree into CSS. It is inconsistent about which nodes receive context and which ones do not.
Unfortunately, @import
nodes do not receive the full context. So, IIRC, the first question is, what is the changeset that is needed, and second, how to we get it into the JS source. Do we monkey patch it at runtime? Try to get it incorporated into the mainline source? maintain a fork?
from less-rails.
@stouset I'm having a hard time following you. Are you telling me that if I did not destroy my public/assets (i.e. run rake assets:clean
) then I would not have gotten a new digest? If so that is incorrect. No matter which way I cut it, I always get a new digest. So without removing or running the approved blessed assets:clean task, I get these files. Note, the .gz versions where there in my last comment but just showing you them now. Seriously what exactly is broken?
- application-ec1e498de79479c0b96ff4d08c83909b.css
- application-ec1e498de79479c0b96ff4d08c83909b.css.gz
- application-ec2842901042e01dc30f351daefbc5cf.css
- application-ec2842901042e01dc30f351daefbc5cf.css.gz
- application.css
Re, your comment about asset-url, etc. Can you explain more too, are the tests failing for you? Or they totally written wrong? If I put this in my framework file.
.myimage() {
background-image: image-url("less_framework/less.png");
}
Then I place that image in my frameworks assets/images directory. Then I change my own base.css.less like so.
#container {
.mycolor();
.myimage();
}
And recompile I get these in my assets directory along with this content #container{color:red;background-image:url(/assets/less_framework/less.png);}
in each of the application.css files. Please details what is wrong.
- less_framework/less-ef9b99c1df7d13853cb626bdf3f1f269.png
- less_framework/less.png
from less-rails.
Thanks @cowboyd I recently saw that less.rb was not jRuby happy by a comment from @drnic on another gem that uses less-rails. jRuby is not a priority of mine, nor any interest, nor should we muddy the waters on this ticket. To that end, this ticket was about (1) wanting to @import
.css.less extensions and (2) saying that @import
is totally borked if files it imported were changed. Thru this discussion, it was mentioned about digests in asset url helpers. I have opened up ticket #9 for that so it is easy to track and not have a single ticket be a throw crap at the wall scenario.
So, back on my understanding of the two points of this ticket. I think (1) is either a misunderstanding of how to use Less with the asset pipeline and where we should use sprockets or less's @import
. More to the point how to make Less more like Sass. I see no value to using @import
for a foo.css.less file that would need to go thru the whole sprockets rendering context again. If so, can someone point out why we need to worry about it because @stouset's scenario for the (2) concern has thus far not been true.
from less-rails.
So in short, can someone show me some code that demonstrates what your expectation or problem is?
from less-rails.
That is suboptimal, but I think calling it totally broken isn't completely fair. For the record the asset-* helpers do return relative paths with shas for us (we've been using this in production for several weeks); and, since we package each release and deploy it into a separate directory, there is never a chance of serving corrupted assets.
While it is a clear annoyance in development mode, it has performed flawlessly for us in production, which I think sets the bar for what is broken and what is not.
The fact that you've accidentally worked around the problem doesn't mean it's not broken. :)
Here are the bits of less-rails
that are not working correctly, and a comparison with the Sass plugin. First, the players:
// $ cat application.css
//= require 'application-scss'
//= require 'application-less'
// $ cat application-scss.css.scss
@import 'foo';
#container { @include foo; }
// $ cat _foo.css.scss
@mixin foo { color: white; }
// $ cat application-less.css.less
@import 'bar';
#container { .bar(); }
// $ cat _bar.css.less
.bar { color: white; }
@import doesn't work out of the box with sprockets' file naming conventions
$ curl localhost:3000/assets/application.css
Less::ParseError: file 'bar.less' wasn't found.
Let's explicitly ask for a partial.
// $ cat application-less.css.less
@import '_bar';
#container { .bar(); }
$ curl localhost:3000/assets/application.css
Less::ParseError: file '_bar.less' wasn't found.
No joy. But if we import it explicitly?
// $ cat application-less.css.less
@import '_bar.css.less';
#container { .bar(); }
$ curl localhost:3000/assets/application.css
/* line 2, /Users/stouset/less-test/app/assets/stylesheets/application-scss.css.scss */
#container {
color: white;
}
.bar {
color: white;
}
#container {
color: white;
}
Success!
@import doesn't work with Sprockets' asset freshness checking
Let's go ahead and make some changes to our assets.
// $ cat _foo.css.scss
@mixin foo { color: black; }
// $ cat _bar.css.less
.bar { color: black; }
$ curl localhost:3000/assets/application.css
/* line 2, /Users/stouset/less-test/app/assets/stylesheets/application-scss.css.scss */
#container {
color: black;
}
.bar {
color: white;
}
#container {
color: white;
}
Wait, what? Why are my less styles unchanged? Even restarting the Rails server doesn't fix anything. You have to run rake tmp:cache:clear
or add blank padding lines to the outermost Less to trigger a refresh.
Note: this doesn't seem to be broken in production (rake assets:precompile
will always use the latest) as of Rails 3.1, however, rake assets:precompile
is broken for existing Rails 3.0 apps. I don't remember the exact details, but I believe it replaces the contents of existing stamped-assets without changing the stamp (which is a huge showstopper for anyone serving cached assets [which damn well ought to be everyone]).
from less-rails.
To preempt the reply about using require
instead of @import
:
$ cat application-less.css.less
//= require '_bar'
#container { .bar(); }
$ curl localhost:3000/assets/application.css
Less::ParseError: .bar is undefined
Basically, Sprockets evaluates each require
'd file in isolation before attaching them all together.
from less-rails.
@stouset This ticket is not for digests, please go to issue #9.
from less-rails.
I was listing the entire set of problems caused by not integrating with Sprockets (which was the original focus of this issue). The first two problems have nothing to do with asset digests.
from less-rails.
Let me recap for @stouset. You think we should monkey patch Less' @import
for the following reasons.
Run rake assets:precompile with your current stylesheets.
Then change an@imported
stylesheet and run it again.
The cache-busting digest stamp will not have changed.
This is incorrect, if the imported stylesheet affects the final outcome, it will change the digest as I have shown. Your statement you've accidentally worked around the problem doesn't mean it's not broken
thought technically true, you could take a step back and say something like, oh sorry, I meant in development things were not working and I assumed that precompile would not work, etc.
@import
doesn't work out of the box with sprockets' file naming conventions
And maybe it should not. If that is the case, this will become a documentation challenge to educate people why we have chosen not to do this along with examples of how to use Less.js in the asset pipeline. So in your example code, just rename _bar.css.less
to bar.less
.
Let me restate, I am not going to do anything simply for the sake of Less to Sass parity. There are some things that I think are logical to do, like generators, asset helpers, etc. But just by saying that Sass (sass-rails) does something is not going to accomplish anything. You will have to be make a clear case with a lot less hyperbole than your previous comments.
@import
doesn't work with Sprockets' asset freshness checking
Now that could be annoying. It is also the first time you have articulated the problem too! I would hate myself to be working on my killer new Less framework along side my awesome app, then change the way a function does something and have to clear the cache all the time. So far this is the only worthwhile reason I have seen to monkey patch @import
. I will look into this specifically and followup.
from less-rails.
This is incorrect, if the imported stylesheet affects the final outcome, it will change the digest as I have shown.
From my above post:
Note: this doesn't seem to be broken in production (rake assets:precompile will always use the latest) as of Rails 3.1, however, rake assets:precompile is broken for existing Rails 3.0 apps. I don't remember the exact details, but I believe it replaces the contents of existing stamped-assets without changing the stamp (which is a huge showstopper for anyone serving cached assets [which damn well ought to be everyone]).
However, I fucked up the version numbers. Rails 3.1.1 does not exhibit this problem, Rails 3.1.0 does.
Now that could be annoying. It is also the first time you have articulated the problem too!
From my very first comment:
The biggest issue is that Sprockets uses its internal representation of the import hierarchy to determine staleness. This means that updated Less partials won't take effect until you make a change to the topmost stylesheet.
from less-rails.
I've updated my previous comment to remove the asset fingerprinting complaint. Per issue #9, I am an idiot and was missing the fact that only fingerprinted assets use fingerprints for the assets they include.
from less-rails.
On a lighter side, I wonder how many github notifications this poor guy has gotten throughout this discussion :)
https://github.com/import
from less-rails.
Offline gist: Failed ImportProcessor
https://gist.github.com/1368396
from less-rails.
Related Issues (20)
- release 2.8.0 to rubygems HOT 8
- Tests for Ruby 2.2+ and suppport for NOT using therubyracer HOT 4
- @import remote file fails with (less) option HOT 1
- Is therubyracer needed to be installed with less-rails? HOT 1
- N/A
- less-rails doesn't load with Ruby 2.4.0 HOT 6
- Doesn't work with sprockets 4 beta HOT 3
- Could we use other javascript runtime(e.g. mini_racer)? HOT 2
- Sprockets 3 compatibility HOT 1
- Silence warnings HOT 1
- less-rails 3.0.0 in Rails 4 project causing Asset logical path has no extension error on asset compilation HOT 4
- Uplifting from 2.8 to 3.0 causes issues with precompiling. HOT 2
- error evaluating function `data-uri`: Optional dependency "mime" is required for .svg HOT 1
- Version 2.8.0 is showing deprecation warning from register_engine HOT 2
- rails s command issue HOT 2
- less-rails initializer causes load_config_initializers to run too early, before other gem initilizers HOT 1
- less-rails not working with Rails 6 - undefined method `pathname' HOT 13
- less-rails 5.0 require sprocket 3 HOT 1
- Any way to remove/avoid therubyracer? HOT 2
- heroku deploy failing with therubyracer dependency HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from less-rails.