Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

One time imports #139

Open
chriseppstein opened this Issue · 76 comments
@chriseppstein

We need a way to import library files just a single time. The @import statement is basically like ruby's load directive, but I think we need an import with ruby's require semantics (does nothing if it's been imported already).

What do you think about @depend?

@MarioRicalde

This would be for performance, right?

I can think of this being useful on big packages that have one file that does lots of includes.

@chriseppstein

Yes. performance is a big part, it allows each module to be used in isolation without a performance penalty. It also makes it easier to override mixins because they won't be re-parsed later if you define the override after the first time it's loaded.

@MarioRicalde

I like that last part.. making overriding easier would be great.

@anthonyshort

Definitely. This is something I've been seeing a need for lately as well. It would be super handy.

@chriseppstein

Marked #156 as a duplicate of this issue.

@seanofw

Copying my proposal from the other thread: I propose adding an @import-once directive that can be dropped into the imported file, as the first statement in that file. It takes no parameters:

@import-once;

This is a safer directive than the @import-vs-@require solution, since it ensures that the imported file can't be accidentally imported wrong; if it was designed to be imported only once, it can assert that itself, and a caller need not know its behavior to be able to use it correctly.

@seanofw

Oh, the other thing in the other thread: If anybody needs it, I have a cute workaround involving adding a @function and an @if statement in the right places; it produces results that are very much like @import-once would produce. It's not perfect, but it does work. You can find my solution here: #156

@chriseppstein

@seanofw Can you please provide the use case of a file that is "designed to be imported only once"?

@seanofw

@chriseppstein: The best example I can give is a diamond-style dependency pattern, which occurs in larger software.

So let's say you have these four files in a big system that's been well-factored into small units:

  • _DropDownMenu.scss
  • _CarModelPicker.scss: @import "DropDownMenu";
  • _ColorPicker.scss: @import "DropDownMenu";
  • FindACarPage.scss: @import "CarModelPicker"; @import "ColorPicker";

So "DropDownMenu" has the stylings for general, shared dropdown menus that appear when somebody clicks a dropdown button. "CarModelPicker" is the layout for a frequently-reused blob of markup that, say, lets a user choose a car by its year and make and model, and that markup contains dropdown menus. "ColorPicker" is the layout for a frequently-reused blob of markup that lets a user choose a color, and that markup also contains dropdown menus. Finally, the "FindACar" page displays both a model-picker and a color-picker to let a user find a car.

See the problem? On the "FindACar" page, if you don't design "DropDownMenu" to be imported only once, you'll get the same dropdown-menu CSS generated twice --- two different places in the same resulting FindACar.css file. You don't need to use @mixins or anything fancier than @import to demonstrate the problem.

In a large system where the CSS has been heavily sliced-and-diced like this --- like our flagship application with 1.2 million lines of code in it --- the @imports of a single page can end up both broad and deep. It's possible for the same page to unknowingly import, say, "_Links.scss" twenty times, because from the page's perspective, it's listing out its dependencies, and each of those dependencies are listing theirs, and so on until you get to a few base styles that nearly everybody needs.

For a small application, this isn't an issue anyone would notice; but for a big application like ours, avoiding redundant imports is absolutely critical.

@chriseppstein

@seanofw I totally understand this pattern and complex structure. My own site is quite complex. but I don't understand why _DropDownMenu.scss should be the one to declare it's importing behavior instead of the caller.

@seanofw

@chriseppstein: The answer is because of the potential for misuse. I work in a large software shop, and believe me, even among really smart, capable coders, if somebody can do it wrong, they will do it wrong.

By making _DropDownMenu.scss the one to declare "You can only import me once," you don't give anybody the option of screwing it up and importing it six times; you can't do it wrong even if you want to. But if _CarModelPicker.scss and _ColorPicker.scss are the ones that are responsible for saying, "Only import DropDownMenu once," that provides two points for failure instead of one: It's two different places where somebody can --- and will --- screw it up. And in a large system, two points for failure quickly become twenty, and those twenty quickly become two hundred.

Putting it inside _DropDownMenu.scss is a form of encapsulation, to use an OO term; _DropDownMenu.scss is then guarding itself against misuse.

Another thing to consider: Is there ever a scenario when it makes sense for a single .scss file to be able to both be imported many times by some callers and only once by others? Is there any scenario where that's sane, desirable behavior? If the answer is no (and I think it's no), then the system should guard against it, because that then must be an accident --- undesirable behavior --- if it happens.

@jacobrask

Is depend used in any other language? Using require or import-once would make it familiar to developers from other languages, the latter being the most obvious and very unlikely for anyone to use by mistake.

@verekia

This feature seems fundamental for having clean re-usable code in an architecture with inheritance. I wish I had it even for this very simple use case.

Plus, I agree with @jacobrask, and think require and import-once are particularly clear and intuitive.

@MoOx

What about @import "stuff" !once; ?

@ianstormtaylor

+1 for require, this would make working with modules much easier
-1 for forcing the callee to declare its import preference

@arbales

Being unable to do this in a large modular codebase is a bummer. We ship different combinations of "modules" to our primary web client that we do to secondary clients (widgets, gadgets, etc).

When _buttons.css.sass begins to depend on _gradients.css.sass, but _gradients.css.sass is included on a per-package level (application.sass, gadget.sass,…) to avoid multiple imports, every change in dependencies requires tabulation by the developer or building every package.

I also think allowing the callee to specify this behavior would be interesting, if not expected.

@ianstormtaylor

The more I try to develop modular components in SASS the more this feature becomes necessary to not have to deal with the order of dependencies by hand in my main.sass which is easy to screw up and no one wants to maintain comments for.

Letting files require whatever dependencies they need makes things much more obvious than having everything imported once into main.sass. It's the equivalent of storing all of your components as globals in Javascript instead of using something modular like AMD—which makes over-reaching/tight-coupling easy to do by accident, and in general just leads to more tangled code.

So, +1 this a lot.

@zakness

Having to include @import-once; in the importee would theoretically reduce the points of possible failure, but it also has the (in my opinion, huge) downside of making the behavior of @import inconsistent. The developer importing files now has to know whether or not the files they're importing expect to be imported once or N times.

I like @depend because it conveys a dependency, which I believe is the main use case for this feature. When I see @require I think of PHP's require function, which does not convey onceness (but that’s probably just because I’m not a Ruby guy :P).

I’m curious, what are some example use cases for importing files multiple times? I’ve worked on large and small projects and this was never something I or my team wanted. Would you consider changing the @import directive itself to always import once?

@thejase

+1000 for this.

@svallory

I'm with @zakness, importing a file more than once is the odd scenario. Makes more sense to me going with changing @import to only import once and make @include "filename" include the file no matter what.

@neekey

+1 for this feature like @import-once, because when you import a extend-only class multiple times( by mistake... ) , than it will be compiled multiple times... which is exactly not what we want.

@Snugug

@svallory except that @include is already a keyword for mixins in the Scss format and absolutely should not be repurposed for @import-once or some such.

@thejase

+1 to @MoOx's suggestion: @import 'file' !once;

@svallory

@Snugug That's kinda the motive behind my suggestion. You see, mixins can be included multiple times. It's already known how @include 'mixing' works. So, by analogy, @include 'file' would work the same way. You may think conflicting files and mixin names would be a problem, but since the context is mutually exclusive, i.e. you always use a @include mixin inside a rule and @include file outside one, it shouldn't be a problem at all.

@Snugug

@svallory That's a dangerous, and in fact incorrect, assumption to make. Mixins absolutely do not need to be included inside a rule, and there are many many "setup" mixins that you in fact specifically do not include inside a rule. Take, for example, Compass' establish-baseline mixin which writes the selectors it needs.

Moreover, I firmly believe that the mental model behind a mixin and a file are different enough that context alone isn't a good enough indicator alone of what you mean to do, especially in the case of files that include mixins of the same name. Take a CSS Normalize file as a for instance. The logical name of the file would probably _normalize.s*ss, and that file contains @mixin normalize and @mixin normalize-legacy. When I call @include normalize, what should I expect to happen? Should I expect the file to be imported again? Should it be the mixin? Both should really only be imported/used once, and both should be written at doc root (outside of a selector).

I personally like @MoOx's !once suggestion as it's a common enough pattern throughout Sass that I think it's a good fit (we've got !default for variables and !optional for extends).

@arbales
@svallory

@Snugug thank you for your comment. I missed that point. I have nothing more to say than I totally agree with you.

@jhilden jhilden referenced this issue in rails/sass-rails
Closed

Shouldn't @import be aware of dupes? #134

@kottenator

Besides, LESS has both @import and @import-once, but from v.1.4 they want to make default @import logic as @import-once (and to remove the second one).

Maybe it's good to do the same in SASS / SCSS?

@MoOx
@zakdances

Sass definitely needs an "@require" or something equivalent. It shouldn't force the "_name.scss" convention just to avoid duplicates. It's been 2 years...

@octplane

In my case, the main application CSS is 8000 lines long and contains more or less 1092 selectors, of which 400 are repeated twice or more once sass has run. This is partly due to the way @import works by reimporting all its dependencies everytime we call it on a scss.

I'd have expected some kind of way to prevent that. My concerns are:

  • generated size of the computed css
  • ability to debug the generated css: right now, because of the inclusion, most of my element matchs the same selector twice in the final css, the second one overriding every attribute of the first one, leading to poor visibility and high WAT-attitude among developpers.
@aaronjensen

:+1: aside from being able to perform a nested import:

.foo
  @import bar

I don't see many reasons that @import should behave the way it does. I'd be fine switching to @require or @import-once but I would imagine that I and most people would just replace all of them. In other words, I think we should swap to "import once" behavior by default.

@cimmanon

@aaronjensen What would be the benefit to breaking cases where importing a file multiple times is exactly what is expected (because that's how it worked before) by making "import once" the default behavior? In a perfect world, a user shouldn't upgrade Sass and find out that it broke all of their code.

@aaronjensen

@cimmanon It's obviously up to the breaking change policy of the project. Maybe that's a major version thing, maybe it doesn't make sense to break ever. I doubt there are too many people that rely on that behavior knowingly but I'm sure given the nature of css there are plenty that rely on it unknowingly and it would be a hard to figure out thing in that case. If the desire is to go for the non-breaking change that's no skin off my back, but I'd recommend updating docs and examples to use that as the default.

@marquis

+1. The redundant CSS not only affects performance and debugging, but for us pushed our total selector count past IE's max, breaking our site for those users.

@alecperkins

+1. The @extend and %placeholder capabilities beg for an import-once feature. It's unintuitive for the rewritten selectors to be duplicated (though makes sense after thinking about how import and placeholders work). Also, it's sloppy to need a different file describe the dependencies of a module to avoid duplication. @require is a good name, keeping @import consistent with how it is now (both in Sass and CSS).

@alecperkins alecperkins referenced this issue in stylus/stylus
Closed

Placeholder selectors in Stylus #617

@kaelig

I will say exactly the same thing @verekia said: "This feature seems fundamental for having clean re-usable code in an architecture with inheritance."

@chriseppstein

@kaelig it's something we intend to handle in Sass 4.0 when we create a proper concept of sass modules and namespacing and revamp how @import works.

@curaloucura

Hey guys, I see that this thread is quite old already. What's the status of having a way to import a file only once? I am having trouble with the CSS outputs with dupes and I believe that with the use cases here it's already clear the need for such, right?

@svallory

A while ago I created a custom importer which makes @import only import the same file once. I don't know if it would work with the current version, but if anyone is interested I could try to find it and share.

@aaronjensen

@svallory that'd be great.

@svallory

Ok, here it goes: https://gist.github.com/svallory/5850711 But I really can't tell you how well it will work now. Also, I don't remember how to replace the sass default importer by this one, but it shouldn't be too hard. It's fundamental that this is the only importer in use.

@svallory

Btw, liftr is a half-way project I had to stop working on. Don't bother by the references to the name in the code. I left it there because it shows how you can customize path resolution inside the importer.

@thejase
@svallory

I just pushed a working command line tool to https://github.com/theblacksmith/smartsass tested with Sass 3.2.9. In other setups, like compass or rails, you'll need another way to pass the :filesystem_importer option.

I'm sure the tool is pretty rough around the edges and it would be great if someone could review the code. I'm not a ruby developer, I learned it just to create this importer so forgive me for any sins I may have committed in the code.

Cheers!

@fedotxxl

+1. Pls implement this feature

@xzyfer

I can't express how critical this feature is for developing large, modular frameworks without duplicate output.

The current @import semantics aren't intuitive and, I may be wrong here, I believe web browsers dedupe @import's in css.

@benschwarz

Aiming at being objective here:

  • Changing @import is a seriously breaking change, it will not happen.
  • +1ing doesn't add anything to the conversation.

Do we have a clear proposal / suggestion that @chriseppstein or @nex3 have agreed with? Is there an actionable development path?

If you've got an opinion, I suggest that you submit a proposal… if its solid, and simple, it'll get agreed upon.

2 year old issues don't resolve themselves without buy-in, folks.

@xzyfer

As it stands the current work arounds aren't all the great as they stand if your intending to distribute you project in a similar fashion to the bootstrap sass port.

People looking for a solution now may want to look at https://gist.github.com/courtsimas/1167053.
Someone kindly built an implementation of the above solution https://github.com/wilsonpage/sass-import-once/blob/master/sass-import-once.scss. Hopefully these will ease the pain of waiting for 4.x.

What could make these approaches cleaner is existences checks (#821) :+1:

@thejase

@xzyfer That is an incredible find! I would say the first solution is better (except the lack of a !default suffix), because you can use %placeholders inside it. They aren't allowed inside mixins. That was my main argument for "import-once." Wooooohoooooo!

Totally now using this on JSMACSS!!!!

@xzyfer

I'd suggest having a quick look at this before going to crazy with place holders https://gist.github.com/xzyfer/6394659. If you take the final sass example and try to use media queries you'll run into a deprecation warning. I personally think deprecating @extending placeholders in media queries is a backwards move, although it makes sense for @extending normal selectors.

There's an example of a workaround here (#905) which from memory works as expected, inserting the placeholder content within the media query context, in 2.3.10 even thought it throws a deprecation warning.

@thejase

Eh, I generally keep most of my placeholders on a mobile-first context, meaning they apply to all. I try not to complicate that part too much.

@thejase

Give it 10 years and we'll see native placeholders for CSS... guaranteed.

@xzyfer

Can you please elaborate further?
AFAIK you cannot extend a placeholder inside a media query unless it was defined within that media query. Although sass currently allows this, and does the right thing, it throws deprecation warnings stating it'll no longer function in 3.3

@brauliobo

hello all, this issue is quite old. I've just reported a duplicated of this #928 .
Is there a fix for this in the near future??

@thejase

xzyfer: I just mean that I try not to create placeholders that would need to be overwritten in MQs.

brauliobo: read through for an import-once function.

@brauliobo

@thejase: @import-once is integrated into the official code?

@brauliobo

pasting https://gist.github.com/courtsimas/1167053 before including anything isn't nice. this or another solution should be on the sass core code

@thejase
@brauliobo

it is not nice because it adds more than 10 lines of code in each sass file I need it.

@aaronjensen

How would it be "safer"? The gist is clever, but it's a total hack in my opinion. For one, it requires you to wrap each file you want to be able to protect in an if, so you couldn't use it w/ any files you don't own, for another it requires you to litter your top level files w/ that function code. I'm holding out for a native version.

@brauliobo

@aaronjensen +1 for a native version. it is very needed. the sass developers are considering this??

@thejase
@svallory

@brauliobo it's not a built in solution, but you can use SmartSass. It will use the SSS compiler you have installed but replace the importer by one which makes include import files only once

@brauliobo

@svallory why shouldn't it be built in?

@thejase

@brauliobo He's not saying it shouldn't. He's saying this is a possible solution in the meantime.

@svallory

@brauliobo It's not "built in" because it's not part of the sass compiler. But it's totally transparent for the apps using it. And I sure hope it will be made part of sass someday.

@StefanoRausch

@brauliobo : I do have a slight variation of the solution mentioned above — see https://gist.github.com/StefanoRausch/6744610 .

I do import it only once at the very beginning —think framework here, no duplication in every Sass file — and from there on I only have to either wrap the code in discussion as content for the mixin or I can use the function, depending on the constraints set by the mixin.

@import file -> @include once( "" ) { ... } OR @if( include-once( "" ) { ... }

Still a workaround, but nice to have ;)

@StefanoRausch

See #156 too.

@Frikki

Three years... and counting!

@herringtown

Tick, tock.....thought this would have been addressed by now, having read through the entire thread.

Are @StefanoRausch & @courtsimas workarounds still the only viable solution to not have duplicated css when extending a selector from another (imported) file?

@tmccombs

Why is this still not fixed? Almost every language has some sort of import-once mechanism, but not sass.

@chriseppstein

Because it's a 4.0 feature.

@chriseppstein chriseppstein locked and limited conversation to collaborators
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Something went wrong with that request. Please try again.