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

feat: support from global for composes #669

Merged
merged 4 commits into from Sep 15, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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 {
Expand Down
45 changes: 29 additions & 16 deletions packages/processor/test/composition.test.js
Expand Up @@ -8,7 +8,7 @@ const Processor = require("../processor.js");
describe("/processor.js", () => {
describe("composition", () => {
let processor;

beforeEach(() => {
processor = new Processor({
namer,
Expand All @@ -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.`);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I wish it was easier in pegjs to customize the error messages. Nothing you need to change here, just annoyed and sharing.

}
});

it("should fail if a composition references a non-existant class", async () => {
try {
await processor.string(
Expand All @@ -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(
Expand Down Expand Up @@ -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",
Expand All @@ -131,9 +131,9 @@ describe("/processor.js", () => {
}
`)
);

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

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

Expand All @@ -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();
});

Expand All @@ -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();
});
});
Expand Down
53 changes: 43 additions & 10 deletions packages/www/src/guide/feature-composing-styles.md
@@ -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
Expand All @@ -10,7 +10,7 @@ Selector limitations mean that it's difficult to use complicated selectors, so t

.local {
composes: composable;

color: red;
}

Expand All @@ -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"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this to match the output of the example file above

}
*/
```

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
Expand All @@ -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;
}
```
11 changes: 11 additions & 0 deletions parsers/composes.pegjs
@@ -1,5 +1,6 @@
start
= composition
/ global
/ namespaced
/ simple

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

// fooga from global
// fooga, wooga from global
global
jquense marked this conversation as resolved.
Show resolved Hide resolved
= refs:references _ "from" _ global_keyword {
return {
type : "simple",
refs: refs.map(({ name }) => ({ name, global : true }))
};
}

// fooga.booga
namespaced
= ns:ident "." ref:ident {
Expand Down
9 changes: 6 additions & 3 deletions parsers/shared.pegjs
Expand Up @@ -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"
Expand Down