wix / stylable

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

Allow stylesheets to explicitly define their public exports #1489

Open tomrav opened 4 years ago

tomrav commented 4 years ago

Stylable allows multiple types of symbols to be imported across stylesheets:

All of these parts are automatically exposed as a public interface for every stylesheet.

This means that a user that wishes to limit the scope of their exposed style API cannot do so.

Furthermore, once we implement the feature suggested in issue #1484 it becomes even easier for anyone consuming a project to access any part of any stylesheet "legitimately".

Suggested feature - add @st-exports to explicitly manage exports

We can introduce a new feature to Stylable that would allow a user to specify which parts of every stylesheet they wish to expose for external use. Any symbol not mentioned will count as a private member and will not be exposed for import across stylesheets.

If no @st-export definition exists in the stylesheet, the current behavior of everything being public is maintained.

Note: the chosen syntax (@) is in line with our suggested @st-import shorthand suggested in PR #936

Example

A component stylesheet exposing some of its internals

/* button.st.css */
:vars {
    c1: green;
    c2: gold;
}

.icon { --c3: purple; }
.text {}
.private {} /* not included in the export below, hence private */

@keyframes blink {}

@st-export 
    c1 as c11, /* rename exports */
    c2, 
    --c3, 
    icon, 
    text, 
    keyframes(blink);

An index file exposing the desired parts of its API. This in conjunction with the feature in #1484 cover both ends of the import/export relationship.

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

@st-export Button; 

A user consuming the 3rd-party exports either through a default import or a `named one.

/* user-code.st.css */
:import {
    -st-from: "3rd-party/index.st.css";
    -st-default: Index(
        Button(
            c11, 
            c2
        )
    );
    -st-named: Button as BtnX(
        c11, 
        c2, 
        --c3, 
        icon, 
        text, 
        keyframes(blink)
    );
}

Possible variation

We can consider using the same sort of destructuring syntax introduced in #1484 in the @st-exports directive as well to narrow down the export to specific parts of a stylesheet root.

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

@st-export Button(c1 as c11, c2); 

This example exposes three items, Button the component root, and two variables c1 (aliased to c11) and c2. Any imports passing through this export see only those items. This allows managing the entire project exports through a single outward-facing file.

In concept, this is similar to our CLI generated index file, but within the project source code (instead of dist), and more specific about which parts it exposes.

tomrav commented 4 years ago

Random thought. Everything written above talks about managing exports as a way of limiting the public interface.

Does this relate to the issue raised in #294? Should @st-export also limit use through pseudo-element syntax?

/* button.st.css */
.root {}
.icon {}
.text {}
@st-export icon;
/* user-code.st.css */
:import {
  -st-from: './button.st.css';
  -st-default: Button;
  -st-named: icon  /* exists */, 
             text; /* doesn't exist */
}

Button::icon {} /* exists */
Button::text {} /* doesn't exist? */
idoros commented 4 years ago

@st-export also limit use through pseudo-element syntax?

If this would change the way pseudo elements are exposed then it be in conflict with @custom-selector.

I would prefer a directive to control access to pseudo-elements. Suggested it a while ago: #294