wix / stylable

Stylable - CSS for components
https://stylable.io
MIT License
1.27k stars 62 forks source link

Allow destructuring imports to access a stylesheet's inner parts #1484

Open tomrav opened 4 years ago

tomrav commented 4 years ago

The Stylable CLI can generate either CJS or ESM output code for their appropriate environments.

One of the features that contributes to this is the ability to automatically generate an index file that contains references to every stylesheet root in the project.

Example:

/* button.st.css */
:vars {
    bgColor: green;
}

.root {}
.icon {
    --textColor: green;
}
.label {}

@keyframes blink {}

Generated index file

/* index.st.css */
:import {
    -st-from: './button.st.css';
    -st-default: Button;
}

Button {}

For component root type consumption this is enough. However, if you're interested in parts (classes, CSS vars, st vars, and keyframes) defined within a specific project stylesheet then you're forced to target the component stylesheet by path explicitly, instead of using the generated index stylesheet.

/* external consumer */
:import {
    -st-from: 'other-project/index.st.css';
    -st-named: Button; 
    /* the Button's classes ".icon" and ".label",
        the variables "bgColor" and "--textColor" and 
        the keyframe "blink" are not exposed through this index file 
     */
}

.myButton {
    -st-extends: Button;
}

This means that a consuming project would need to be familiar with the target output of a 3rd party project to consume its inner parts. Targeting other-project/cjs/button.st.css or other-project/esm/button.st.css would require knowing about both the part to the stylesheet (cjs or esm in this case) and the stylesheet name (button.st.css) explicitly.

Suggested solution - allow imports to be destructured

A possible solution would be to extend the current capabilities of import statements to support a behavior similar to destructuring.

We still need to consider which type of parenthesis would be the best syntax here, "()" or "[]", for now, all examples below use the "()" option

:import {
    -st-from: 'other-project/index.st.css';
    -st-default: SomeName(
        Button as MyButton( /* alias functionality maintained */
            icon as myIcon, /* alias for a destructured part */
            label, 
            bgColor, 
            --textColor, 
            keyframes(blink),
            Label( /* Exposed component root */
                text
            )
        ), 
        Keyframes(keyframes(blink)), 
        /* an element named "Keyframes" showing the difference to our keyframes function above */
    );
    -st-named: Button(
        icon as myIcon, 
        label, 
        bgColor, 
        --textColor, 
        keyframes(blink)
    ); 
}
.myIcon { /* imported icon and variables usage */
    background: value(bgColor);
    color: var(--textColor);
} 
/* ... */

This solution means we negate the requirement of structural knowledge of 3rd-party projects. Any inner part required can be accessed through the component root exposed in the index stylesheet through destructuring.

Furthermore, by allowing such imports, we can avoid having to use a Member variable type syntax, which can get bloated/repetitive in CSS-like syntax.

A possible downside

Now that one can jump across stylesheets within a single import leads to the fact that any part of any stylesheet effectively becomes a public interface (when using the CLI to auto-generate the index).

To help combat this behavior another new feature is suggested in separate issue (#1489) to allow defining a stylesheet's export explicitly.

idoros commented 4 years ago

Have you given thoughts for an equivalence ability in the future shorthand import syntax? and Is there a any chance of adding language service support here? 🙏

tomrav commented 3 years ago

We have a PR (#936) open for a while now with a suggestion for an import shorthand. I think the feature and syntax offered here fit that shorthand just as well.

@st-import [
    Button(
        icon as myIcon, 
        label, 
        bgColor, 
        --textColor, 
        keyframes(blink)
    )
], Default from 'blah.st.css';

We aim at offering LSP support for all features, to what extent exactly that will resolve for this feature, I am not yet sure.

argshook commented 3 years ago

I vote for the shorthand syntax. It's a bit closer to what exists in ecmascript and thus would be more familiar for developers.

It would also be consistent with what's suggested in https://github.com/wix/stylable/issues/1489

i would even go with the {} brackets, those are ingrained in muscle memory. For example

@st-import Button, { c11, text } from 'style.st.css';
@st-export Button { c11 as c1, text };
shlomitc commented 3 years ago

Hey guys. This issue is open for a while now. As I'm reading this again, it seems like the proposal here is to suggest some kind of new syntax, but this would probably not be backward compatible for whoever will use older versions

What about simply adding a new feature to the CLI tool to re-expose all internal parts with some prefix? It looks straightforward for the inner classes, not sure about the variables.

barak007 commented 3 years ago

@shlomitc we actually opened a PR to address this #1626 when finished you will be able to expand the index exports.