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

Stylelint preprocessor #140

Merged
merged 25 commits into from
Nov 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ jobs:
- store_artifacts:
path: website/__integration-tests__/__image_snapshots__/__diff_output__
destination: website-snapshot-diffs
build-website:
build-and-lint-website:
<<: *defaults
steps:
- attach_workspace:
at: ~/linaria
- run: |
cd website && yarn run build:all && cd ..
- run: cd website && yarn run build:all && cd ..
- run: cd website && yarn run lint-css && cd ..
Copy link
Member

@satya164 satya164 Oct 27, 2017

Choose a reason for hiding this comment

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

maybe we should do

- run : |
    cd website
    yarn run build:all
    yarn run lint-css
    cd ..

instead?

workflows:
version: 2
build-and-test:
Expand All @@ -81,6 +81,6 @@ workflows:
- integration-tests:
requires:
- install-dependencies
- build-website:
- build-and-lint-website:
requires:
- install-dependencies
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ coverage/
flow-typed/
node_modules/
vendor/
website/static/server
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[ignore]
<PROJECT_ROOT>/build/.*
<PROJECT_ROOT>/website/node_modules/preact/.*
<PROJECT_ROOT>/website/node_modules/stylelint/.*

[include]
flow-typed/
Expand Down
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,22 @@ Add the `linaria/babel` preset to your Babel configuration:
}
```

See [Configuring Babel](/docs/BABEL_PRESET.md) for more options and [Bundlers integration](/docs/BUNDLERS_INTEGRATION.md) for integrating with Webpack.
## Documentation

* [API and usage](/docs/API.md)
* [Client APIs](/docs/API.md#client-apis)
* [Server APIs](/docs/API.md#server-apis)
* [Configuring Babel](/docs/BABEL_PRESET.md)
* [Preset documentation](/docs/BABEL_PRESET.md#linariababel-preset)
* [Create React App](/docs/BABEL_PRESET.md#create-react-app-ejected)
* [Next.js](/docs/BABEL_PRESET.md#nextjs)
* [Dynamic Styles](/docs/DYNAMIC_STYLES.md)
* [Theming](/docs/THEMING.md)
* [Server Rendering](/docs/SERVER_RENDERING.md)
* [Bundlers integration](/docs/BUNDLERS_INTEGRATION.md)
* [Webpack](/docs/BUNDLERS_INTEGRATION.md#webpack)
* [Linting](/docs/LINTING.md)
* [Example](/website)

## How it works

Expand Down Expand Up @@ -174,16 +189,6 @@ export function App() {
```
Here, there should be no side-effects in the `colors.js` file, or any file it imports. We recommend to move helpers and shared configuration to files without any side-effects.

## Documentation

* [API and usage](/docs/API.md)
* [Configuring Babel](/docs/BABEL_PRESET.md)
* [Dynamic Styles](/docs/DYNAMIC_STYLES.md)
* [Theming](/docs/THEMING.md)
* [Server Rendering](/docs/SERVER_RENDERING.md)
* [Bundlers integration](/docs/BUNDLERS_INTEGRATION.md)
* [Example](/website)

## Editor Plugins

### CSS Autocompletion
Expand Down
10 changes: 8 additions & 2 deletions babel.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
/* eslint-disable global-require */

module.exports = function linariaBabelPreset(context, opts = {}) {
const options = Object.assign(
opts,
// Escape hatch for overwriting linaria preset's options.
JSON.parse(process.env.LINARIA_BABEL_PRESET_OVERRIDES || '{}')
);

return {
plugins: [
[require('./build/babel/preval-extract').default, opts],
[require('./build/babel/rewire-imports').default, opts],
[require('./build/babel/preval-extract').default, options],
[require('./build/babel/rewire-imports').default, options],
],
};
};
41 changes: 41 additions & 0 deletions docs/LINTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Linting

## stylelint

For linting styles with [stylelint](https://stylelint.io/), we provide our custom config tailored for linaria - `linaria/stylelint-config` based on [`stylelint-config-recommended`](https://github.com/stylelint/stylelint-config-recommended).

### Installation

Both `stylelint` and `stylelint-config-recommended` are `peerDependencies` so you need to install them manually:

```bash
yarn add stylelint stylelint-config-recommended --dev
```

### Configuring stylelint

All you need to do is to set your config to extend from `linaria/stylelint-config`.

Here's the example `.stylelintrc` configuration file:

```json
{
"extends": [
"linaria/stylelint-config"
]
}
```

Please refer to the [official stylelint documentation](https://stylelint.io/user-guide/configuration/) for more info about configuration.

### Running the linter

Add the following to your `package.json` scripts:

```json
"lint:css": "stylelint src/**/*.js"
```

Now, you can run `yarn lint:css` to run the linter.

For more information refer to [stylelint documentation](https://stylelint.io/user-guide/cli/).
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"/babel.js",
"/server.js"
],
"peerDependencies": {
"stylelint": "8.x",
"stylelint-config-recommended": "1.x"
},
"dependencies": {
"babel-code-frame": "^6.26.0",
"babel-core": "^6.26.0",
Expand Down
25 changes: 25 additions & 0 deletions src/__tests__/sheet.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,29 @@ describe('sheet module', () => {

expect(sheet.dump()).toBe('.lol{color:pink;}');
});

it('should collect raw styles', () => {
// $FlowFixMe
process.env.LINARIA_COLLECT_RAW_STYLES = true;

const header = 'header_123abc';
sheet.insertRaw({
filename: 'test.js',
template: ['color: blue;'],
expressions: [],
classname: header,
});

process.env.LINARIA_COLLECT_RAW_STYLES = undefined;

expect(sheet.rawStyles()).toEqual({
'test.js': [
{
template: ['color: blue;'],
expressions: [],
classname: header,
},
],
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ SyntaxError: test.js: Linaria css evaluation error:
5 | \`;
at File.buildCodeFrameError (<<CWD>>/node_modules/babel-core/lib/transformation/file/index.js:427:15)
at NodePath.buildCodeFrameError (<<CWD>>/node_modules/babel-traverse/lib/path/index.js:140:26)
at resolveSource (<<CWD>>/build/babel/preval-extract/resolveSource.js:31:16)
at resolveSource (<<CWD>>/build/babel/preval-extract/resolveSource.js:35:16)
at Object.Identifier (<<CWD>>/build/babel/preval-extract/index.js:46:48)
at NodePath._call (<<CWD>>/node_modules/babel-traverse/lib/path/context.js:76:18)
at NodePath.call (<<CWD>>/node_modules/babel-traverse/lib/path/context.js:48:17)
Expand Down
32 changes: 24 additions & 8 deletions src/babel/preval-extract/__tests__/prevalStyles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jest.mock('../../lib/moduleSystem', () => ({
instantiateModule: jest.fn(() => ({ exports: 'header__abc123' })),
}));

function runAssertions(expectedReplacement) {
function runAssertions(expectedReplacement, source = 'css`color: #ffffff`') {
const path = {
node: {
loc: {
Expand All @@ -30,7 +30,7 @@ function runAssertions(expectedReplacement) {
},
},
getSource() {
return 'css`color: #ffffff`';
return source;
},
findParent() {
return this.parentPath;
Expand All @@ -50,18 +50,34 @@ function runAssertions(expectedReplacement) {
expect(instantiateModule).toHaveBeenCalled();
}

function clearMocks() {
process.env.NODE_ENV = '';
getReplacement.mockClear();
instantiateModule.mockClear();
clearLocalModulesFromCache.mockClear();
}

describe('preval-extract/prevalStyles', () => {
beforeEach(() => {
process.env.NODE_ENV = '';
getReplacement.mockClear();
instantiateModule.mockClear();
clearLocalModulesFromCache.mockClear();
});
beforeEach(clearMocks);

it('should eval styles and replace css with class name from content', () => {
runAssertions("css.named('header', 'test.js')`color: #ffffff`");
});

it('should eval styles and append filename if used with css.named', () => {
runAssertions(
"css.named('header', 'test.js')`color: #ffffff`",
"css.named('header')`color: #ffffff`"
);

clearMocks();

runAssertions(
"css.named('header', 'filename.js')`color: #ffffff`",
"css.named('header', 'filename.js')`color: #ffffff`"
);
});

it('should eval styles and replace css with class name from filename', () => {
process.env.NODE_ENV = 'production';
runAssertions("css.named('header')`color: #ffffff`");
Expand Down
8 changes: 8 additions & 0 deletions src/babel/preval-extract/__tests__/resolveSource.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,12 @@ describe('preval-extract/resolveSource', () => {
null
);
});

it("should return null if it's a global identifier", () => {
expect(resolveSource({}, { node: { name: 'module' } })).toBeNull();
expect(resolveSource({}, { node: { name: '__filename' } })).toBeNull();
expect(resolveSource({}, { node: { name: '__dirname' } })).toBeNull();
expect(resolveSource({}, { node: { name: 'global' } })).toBeNull();
expect(resolveSource({}, { node: { name: 'exports' } })).toBeNull();
});
});
7 changes: 7 additions & 0 deletions src/babel/preval-extract/prevalStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export default function(
env === 'production'
? `css.named('${name}')`
: `css.named('${name}', '${state.filename}')`
)
.replace(
/css\.named\(([^,]+)\)/,
(input, customName) =>
env === 'production'
? input
: `css.named(${customName}, '${state.filename}')`
)}`,
loc: path.node.loc.start,
},
Expand Down
8 changes: 8 additions & 0 deletions src/babel/preval-extract/resolveSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export default function resolveSource(
types: BabelTypes,
path: NodePath<*>
): ?RequirementSource {
if (
['module', 'global', '__dirname', '__filename', 'exports'].includes(
path.node.name
)
) {
return null;
}

const binding = getSelfBinding(path);

if (!binding) {
Expand Down
1 change: 1 addition & 0 deletions src/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const named = (name?: string = 'css', filename: ?string = null) => (
const slug = slugify(filename || styles);
const classname = `${name}__${slug}`;

sheet.insertRaw({ filename, template, expressions, classname });
sheet.insert(`.${classname}`, styles);

return classname;
Expand Down
21 changes: 21 additions & 0 deletions src/sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

import stylis from 'stylis';

type RawStyles = {
template: string[],
expressions: string[],
classname: string,
};

function sheet() {
let cache: { [selector: string]: string } = {};
const rawCache: { [selector: string]: RawStyles[] } = {};

const isBrowser =
typeof window === 'object' && window != null && window.document != null;
Expand All @@ -25,6 +32,17 @@ function sheet() {
}

return {
insertRaw({
filename,
template,
expressions,
classname,
}: RawStyles & { filename: ?string }) {
if (filename && process.env.LINARIA_COLLECT_RAW_STYLES) {
rawCache[filename] = (rawCache[filename] || [])
.concat({ template, expressions, classname });
}
},
insert(selector: string, css: string) {
if (selector in cache) {
return;
Expand All @@ -34,6 +52,9 @@ function sheet() {
cache[selector] = css;
node.appendData(`\n${text}`);
},
rawStyles() {
return rawCache;
},
styles() {
return cache;
},
Expand Down
Loading