Skip to content
Permalink
Browse files

feat: support from global for composes (#669)

This is nice feature in css-modules, for doing quick composition of global classes. `global` acts as a keyword instead of a file here.

Co-authored-by: Pat Cavit <github@patcavit.com>
  • Loading branch information...
jquense and tivac committed Sep 15, 2019
1 parent a57cddf commit 0a7996e948927705ab9454f331f2c44d4e48b256
@@ -28,6 +28,14 @@ Object {
}
`;

exports[`/processor.js composition should compose from global keyword 1`] = `
Object {
"global-compose.css": Object {
"a": "b c d a",
},
}
`;

exports[`/processor.js composition should compose from globals 1`] = `
Object {
"global-compose.css": Object {
@@ -8,7 +8,7 @@ const Processor = require("../processor.js");
describe("/processor.js", () => {
describe("composition", () => {
let processor;

beforeEach(() => {
processor = new Processor({
namer,
@@ -22,10 +22,10 @@ describe("/processor.js", () => {
".a { composes: b from nowhere.css; }"
);
} catch({ message }) {
expect(message).toMatch(`SyntaxError: Expected source but "n" found.`);
expect(message).toMatch(`SyntaxError: Expected global or source but "n" found.`);
}
});

it("should fail if a composition references a non-existant class", async () => {
try {
await processor.string(
@@ -36,7 +36,7 @@ describe("/processor.js", () => {
expect(message).toMatch(`Invalid composes reference`);
}
});

it("should fail if a composition references a non-existant file", async () => {
try {
await processor.string(
@@ -108,12 +108,12 @@ describe("/processor.js", () => {
}
`)
);

const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});

it("should allow composes anywhere", async () => {
await processor.string(
"./multiple-composes.css",
@@ -131,9 +131,9 @@ describe("/processor.js", () => {
}
`)
);

const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});

@@ -144,9 +144,22 @@ describe("/processor.js", () => {
.a { composes: global(b); }
`)
);


const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});

it("should compose from global keyword", async () => {
await processor.string(
"./global-compose.css",
dedent(`
.a { composes: b, c, d from global; }
`)
);

const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});

@@ -162,17 +175,17 @@ describe("/processor.js", () => {
}
`)
);

const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});

it("should compose from other files", async () => {
await processor.file(require.resolve("./specimens/composes.css"));

const { compositions } = await processor.output();

expect(compositions).toMatchSnapshot();
});
});
@@ -1,6 +1,6 @@
### Style Composition

Selector limitations mean that it's difficult to use complicated selectors, so to enable building anything of complexity you can compose selectors. These compositions can be within a file or even pull in classes defined in other files.
Selector limitations mean that it's difficult to use complicated selectors, so to enable building anything of complexity you can compose selectors. These compositions can be within a file, reference global CSS class, or even pull in classes defined in other files.

::: repl
```css
@@ -10,7 +10,7 @@ Selector limitations mean that it's difficult to use complicated selectors, so t
.local {
composes: composable;
color: red;
}
@@ -25,20 +25,53 @@ Selector limitations mean that it's difficult to use complicated selectors, so t
When this file is required the JS object will contain the expected keys, but the arrays will now contain more values.

```js
var css = require("./styles.css");
var css = require("./style.css");
// css is:
/*
{
single : "dafdfcc_other aeacf0c_single",
// Since .multiple is only a singular composes: declaration there's no need
// for it to be rewritten, it's left out of the output
multiple : "dafdfcc_more f5507abd_than aeacf0c_one",
local : "dafdfcc_other aeacf0c_single"
composable : "dafdfcc_composable",
local : "dafdfcc_composable aeacf0c_local",
removed : "dafdfcc_composable aeacf0c_local aeacf0c_removed"
}
*/
```

Composition also works between files, by providing the source file.

::: repl
```css
/* === style-guide.css === */
.body {
margin: 10px;
height: 100%;
}
/* === home-page.css === */
.body {
composes: body from "/style-guide.css";
padding: 10px;
}
```
::::

Styles can also be composed directly from the global scope to help with interoperability
with CSS frameworks or other non-module styles.

::: repl
```css
.box {
composes: d-flex, px-4, py-3 from global;
color: blue;
}
```
::::


If you're going to be doing a lot of composition with another file you can store the filename into a value for ease of referencing.

::: repl
@@ -58,13 +91,13 @@ If you're going to be doing a lot of composition with another file you can store
.head {
composes: heading from guide;
font-size: 120%;
}
.body {
composes: body from guide;
padding: 10px;
}
```
@@ -1,5 +1,6 @@
start
= composition
/ global
/ namespaced
/ simple

@@ -14,6 +15,16 @@ composition
};
}

// fooga from global
// fooga, wooga from global
global
= refs:references _ "from" _ global_keyword {
return {
type : "simple",
refs: refs.map(({ name }) => ({ name, global : true }))
};
}

// fooga.booga
namespaced
= ns:ident "." ref:ident {
@@ -5,19 +5,22 @@ _ "whitespace"
s "string"
= "\"" chars:[^\"\r\n]* "\"" { return chars.join(""); }
/ "\'" chars:[^\'\r\n]* "\'" { return chars.join(""); }

// Partials

// wooga
ident "identifier"
= chars:[a-z0-9-_]i+ { return chars.join(""); }

global_keyword "global"
= "global"

// wooga
// global(wooga)
reference "reference"
= "global(" _ ref:ident _ ")" { return { name : ref, global : true }; }
= global_keyword "(" _ ref:ident _ ")" { return { name : ref, global : true }; }
/ ref:ident { return { name : ref }; }

// wooga
// wooga, tooga
references "references"

0 comments on commit 0a7996e

Please sign in to comment.
You can’t perform that action at this time.