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

Quoted and unquoted colors in maps produces 'Duplicate key "transparent" in map' #1169

Closed
AlbertoElias opened this issue May 7, 2015 · 25 comments · Fixed by #1175, #1254 or #1736
Closed

Comments

@AlbertoElias
Copy link

In Ruby Sass, CSS colors in map keys are considered as colours as you can see here instead of as strings.

It seems to be the same in Libsass, but if I do something similar as the first example, link, when they're set as map keys, they're both considered strings, but if you map-get, they are considered as colors if they're not quoted.

I also tried using the recently released node-sass 3.0.0 that uses the stable Libsass 3.2.0 and it still happens.

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

Sorry, I'm confused. The example you've given fails on Ruby Sass 3.4.12.

$colors: (
    "transparent": #000000,
);

.c {
  color: map-get($colors, transparent);
  foo: type-of(map-get($colors, transparent));
}

Ruby Sass

.c {
  foo: null; }

LibSass

.c {
  foo: null; }

What behaviour are you expecting?

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

$colors: (
    transparent: #000000,
    "transparent"": #000000,
);

Ruby

Invalid CSS after " "transparent"": expected ":", was "": #000000,"

LibSass

error reading values after "transparent" on line 3 at column 2

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

$colors: (
    transparent: #000000,
);

.c {
  color: map-get($colors, transparent);
  foo: type-of(map-get($colors, transparent));
}

Ruby

.c {
  color: #000000;
  foo: color; }

LibSass

.c {
  color: #000000;
  foo: color; }\

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

As far as I can tell the behaviour is the same as Ruby Sass.

@KittyGiraudel
Copy link

$map: (
  'red': 'foo',
   red: 'bar',
);

.foo {
  content: inspect($map);
}

Ruby Sass:

.foo {
  content: ("red": "foo", red: "bar");
}

LibSass:

Duplicate key "red" in map ("red": "bar"). on line 3 at column 14

Discussion: https://twitter.com/HugoGiraudel/status/596267826933014528

@kaelig
Copy link
Contributor

kaelig commented May 7, 2015

Maybe this will help: http://sassmeister.com/gist/3e30a4ee6cab083648c9

$colors: (
    "transparent": transparent,
    transparent: transparent
);

Throws "Duplicate key "transparent" in map ("transparent": transparent). on line 3 at column 27" using LibSass but not in Sass.

@KittyGiraudel
Copy link

Also:

$map: (
   'red': 'bar',
);

.foo {
  content: type-of(nth(map-keys($map), 1));
}

Ruby Sass:

.foo {
  content: string;
}

LibSass:

.foo {
  content: color;
}

LibSass is clearly doing something wrong here. Or the behaviour is not correctly described in the spec, and basically nobody quite knows what should happen, but I'd find it sad I think. :D

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

Thanks for the clarification.

The issue appears to be specific to setting map keys. AFAIK we, correctly, treat colour stings as colours. Marked this for the next small release.

@KittyGiraudel
Copy link

Cool, thanks for the quick feedback @xzyfer. :)

@AlbertoElias
Copy link
Author

Yes, in the second link I posted, you can see that colour strings are treated like colours. But in map keys, they're not, and that's why an error is thrown in my third example if you uncomment the second key in the map (I forgot to mention that).

Thanks everyone for the very quick response!

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

This is also an interesting exception

$map: (
  'red': 'foo',
);

$map: map-set(red, 'bar');

.foo {
  content: inspect($map);
}

Ruby and LibSass

.foo {
  content: map-set(red, "bar"); }

Makes perfect sense considering the string 'red' will be cast to a colour object.

This may actually be a bug in Ruby Sass? /cc @chriseppstein

@KittyGiraudel
Copy link

What is map-set? :x

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

Ah my bad, too many langauges.

$map: (
  'red': 'foo',
);

$map: map-merge($map, (red: 'bar'));

.foo {
  content: inspect($map);
}

Ruby and LibSass

.foo {
  content: ("red": "foo", red: "bar"); }

The inconsistency here does kinda bug me, I wonder what the rationale is here.

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

I've looked into this further and it appears I'm mistake. As far as I can tell Ruby Sass will always treat a quoted colour as a String. This is the crux of our issue.

.foo {
  a: type-of("red");
  b: type-of(red);
}

Ruby Sass

.foo {
  a: string;
  b: color; }

LibSass

.foo {
  a: color;
  b: color; }

@KittyGiraudel
Copy link

As far as I can tell Ruby Sass will always treat a quoted colour as a String.

I can confirm that. Actually there is no such thing as quoted colour in Ruby Sass. A quoted value is a string, no matter what it contains. So 'red' will never be evaluated as a color, no matter where, no matter how.

@kaelig
Copy link
Contributor

kaelig commented May 7, 2015

I wouldn't recommend to do this but there is a workaround:

$map: (
  'transparent': foo,
  rgba(0, 0, 0, 0): foo
);

// Both work in Ruby Sass and LibSass
$foo: map-get($map, transparent);
$foo: map-get($map, 'transparent');

@AlbertoElias
Copy link
Author

Yes, I used @kaelig´s workaround for my second example and it works fine

@xzyfer
Copy link
Contributor

xzyfer commented May 7, 2015

Thanks everyone. This is a tricky one, the colour ones always are. We'll aim to get this in the next small release. A sass-spec will really speed up the process hint hint 😄

@chriseppstein
Copy link
Contributor

CSS cheats by using property specific parsing rules so they can disambiguate when a color is a color etc. SassScript has to work the same everywhere in a document so we basically have to choose to treat color identifiers as colors everywhere.

@mgreter
Copy link
Contributor

mgreter commented May 7, 2015

Here come my findings. See this scss example:

$map1: ( red: 'literal' );
$map2: ( 'red': 'quoted' );
$map3: ( #{re}#{d}: 'interpolated' );

foo {
  content: inspect($map1);
  content: inspect($map2);
  content: inspect($map3);
}

$merge1: map-merge($map1, $map2);
$merge2: map-merge($map1, $map3);
$merge3: map-merge($map2, $map3);

bar {
  content: inspect($merge1);
  content: inspect($merge2);
  content: inspect($merge3);
}

Results in:

foo {
  content: (red: "literal");
  content: ("red": "quoted");
  content: (red: "interpolated"); }

bar {
  content: (red: "literal", "red": "quoted");
  content: (red: "literal", red: "interpolated");
  content: ("red": "interpolated"); }

As you can see, the interpolated value has the same key as the quoted string, although they have different output. This makes sense, since both are internally strings. Colors seems to be color key, no matter what. The following example show a bit more what I mean:

baz {
  content: inspect(( yellow: 'bar' ));
  content: inspect(( 'yellow': 'bar' ));
}

output nested:

baz {
  content: (yellow: "bar");
  content: ("yellow": "bar"); }

output compressed (reformatted):

baz {
  content:(#ff0: "bar");
  content:("yellow": "bar") }

You can see that keys get the same output representation like all other Sass Values.

So to me it looks like OP is saying that transparent is not recognised as a color, but as a string literal? Will no investigate on that issue, since I can reproduce that.

@mgreter mgreter self-assigned this May 7, 2015
mgreter added a commit to mgreter/libsass that referenced this issue May 7, 2015
Keys need to be immutable for the main structure, so we
need to evaluate them while the map is being parsed. We
may defer the map initialization into the eval step too.

Fixes sass#1169
@xzyfer
Copy link
Contributor

xzyfer commented May 13, 2015

This fix was a false positive due to sass/sass-spec#380. The spec still fails.

@xzyfer xzyfer reopened this May 13, 2015
@kaelig
Copy link
Contributor

kaelig commented May 13, 2015

Interesting. We managed to compile our codebase successfully since that partial fix, so for us it's all good.

@xzyfer
Copy link
Contributor

xzyfer commented May 13, 2015

Looks like it was partially working on 3.2.3, but is now broken in 3.2.4 from 2eef52a.

@xzyfer xzyfer modified the milestones: 3.2.5, 3.2.3 May 13, 2015
@mgreter mgreter removed their assignment May 13, 2015
xzyfer added a commit to xzyfer/sass-spec that referenced this issue May 31, 2015
@mgreter
Copy link
Contributor

mgreter commented Nov 14, 2015

This still does not seem to be fixed properly!?

$map: (
   red: 'bar',
  'red': 'foo',
);

.foo {
  content: inspect($map);
}

Another case that does not work:

$map: (
  random(): 'bar',
  random(): 'foo',
);

.foo {
  content: inspect($map);
}

I think we cannot use a hash map in the parser directly and have to delay the map creation to the eval stage, since we cannot eval the keys in the parser stage. With the second example it should be clear that we definitely need to eval the keys before we can add them to a map.

@AlbertoElias
Copy link
Author

Yes, this does seem to have broken again in Libsass 3.3. We're getting duplicate map keys errors again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment