oddbird / accoutrement

Combined Sass Accoutrement tools, with option for individual module imports

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

get-token returns key string when $handle-missing-keys = 'null'

johncrim opened this issue · comments

Given the following 2 tests, the first test fails, and the second test passes:

 $map: (
    'foo': (
      'baz': 2
    )
  )

  @include it('Returns null when root key missing if null mode is enabled') {
    $handle-missing-keys: 'null' !global;

    @include assert-equal(
      get-token($map, 'missing'),
      null
    );

    $handle-missing-keys: 'silent' !global;
  }

  @include it('Returns null when sub-key missing if null mode is enabled') {
    $handle-missing-keys: 'null' !global;

    @include assert-equal(
      get-token($map, 'foo->missing'),
      null
    );

    $handle-missing-keys: 'silent' !global;
  }

It appears that the code path is different for root keys vs nested keys. Since I have a test, I'll attempt to provide a fix.

I dug into this in hopes of providing a fix, and to be frank it looks like a proper fix requires a significant refactor. Right now all the $handle-missing-keys logic is in _a_replace, and that code path isn't touched when the key is missing altogether.

The code is pretty difficult to follow and reason about due to conflated responsibilities - eg _a_replace does a lot more than string replacement, and _a_parse does a lot more than parsing a key string or list or map. Both functions return resolved values, eg what one would expect from _a_get. If I were to refactor this, my goal would be to focus _a_parse so it purely parses the key into a data structure that can be resolved in a straightforward manner (like a list of individual keys/references) without referencing the map, and _a_get would resolve that data structure to values in the map, and handle missing values in one place. That's probably more than you want to fix this bug.

On the positive side, the extensive unit tests are great for protecting against breaks in existing behavior. I really appreciate this project and how it enables front-end developers to work, so please take that criticism as intended to be helpful.

It might be possible to detect this case without the refactor, but it's more challenging than one would think, because recursive resolution is central to the expected behavior (eg other internal code calls a.get-token() and expects the key to be returned when there is no match). It's also interesting that it only fails on the missing root key case, if there are 2 keys (eg missing->foo) null is returned.

To workaround this issue for now, I will detect both null returned and key returned as meaning "value not found".

This probably isn't worth fixing until the migration to use sass's nested maps functions. At that point I would expect a refactor that separates parsing from resolution, so it would be easy to handle missing values in a single location: within get-token() (and not within the parsing functions).

@johncrim Thanks for tracking that down. Are you interested in helping with that sass modules refactor? I opened a branch a while back, but never had the time to get very far.

Hi @mirisuzanne - yes, I'm interested in helping. I'm working nights and weekend on my startup, so time may be an issue, but I do think I can provide significant help here.

I think the primary reason this happens is that we currently allow the $key argument to accept either a token key, or a token to parse for internal aliases & adjustments. Since we allow both, it's impossible to know if the key is "missing" or if it wasn't intended as a key in the first place.

I think, moving forward (if we keep the parser at all), those should be distinct functions.

  • token.get() accepts a map and key, and respects missing-key config
  • token.compile() accepts a map and arbitrary token to be compiled or returned as-is

fixed (and possibly to-be-removed) in the modules upgrade.