Skip to content

Commit

Permalink
Webpack extract to CSS (#624)
Browse files Browse the repository at this point in the history
* feat: adds css loader

* feat: use compiled strip plugin

* chore: decorates funcs

* chore: clarify in comments

* chore: fixes tests

* chore: adds basic types for the webpack loaders

* chore: adds extract to scripts

* chore: adds async component

* chore: adds jsdoc

* feat: adds sorting plugin

* chore: adds predicate

* fix: fixes extract with cjs source

* chore: rename to extract plugin

* chore: reword comments

* chore: fix lint erors

* chore: stub events

* chore: flush all compiled CSS through one style sheet

* feat: adds sort stub func to css

* chore: move predicate into utils

* chore: add style loader when not extracting

* chore: move ot common util

* feat: adds first pass at sort plugin

* fix: use double quotes to fix parsing error

* chore: adds stub plugin

* feat: fills in discard duplicate plugin

* chore: adds css min to webpack

* chore: adds tests for webpack

* chore: use local webpack

* chore: adds tests for babel output extraction

* chore: renames to merge duplicate at rules

* chore: resolves code review comments

* chore: bikesheding

* chore: fix build script

* chore: move to private

* docs(changeset): The `onFoundStyleSheet` option has been replaced by `onFoundStyleSheet`. This callback will be called once with all found styles at the end of the pass.

* docs(changeset): Added new `sort` function to sort atomic style sheets.

* docs(changeset): Added new `createError` and `toBoolean` functions.

* chore: unlink react pkg

* docs(changeset): Added new option `extract` with pairing webpack plugin `CompiledExtractPlugin`.
Configuring them will strip all the runtime from your app and extract all styles to an atomic style sheet.

For help getting started with this feature read the [extracting css guide](https://compiledcssinjs.com/docs/extracting-css).

* feat: turns on extracting from node modules

* chore: adds test fixtures

* chore: refactor

* feat: adds include/exclude for extract plugin

* feat: adds test support

* chore: fix version

* chore: remove ts ignore
  • Loading branch information
Madou authored Mar 20, 2021
1 parent 8666e30 commit 0bb1c11
Show file tree
Hide file tree
Showing 57 changed files with 1,596 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://unpkg.com/@changesets/config@1.0.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": true,
"linked": [["@compiled/babel-plugin", "@compiled/babel-plugin-strip-runtime", "@compiled/react"]],
"linked": [["@compiled/babel-plugin", "@compiled/babel-plugin-strip-runtime"]],
"access": "public",
"baseBranch": "master"
}
5 changes: 5 additions & 0 deletions .changeset/grumpy-teachers-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@compiled/css': patch
---

Added new `sort` function to sort atomic style sheets.
5 changes: 5 additions & 0 deletions .changeset/many-mugs-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@compiled/utils': patch
---

Added new `createError` and `toBoolean` functions.
8 changes: 8 additions & 0 deletions .changeset/rare-monkeys-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@compiled/webpack-loader': patch
---

Added new option `extract` with pairing webpack plugin `CompiledExtractPlugin`.
Configuring them will strip all the runtime from your app and extract all styles to an atomic style sheet.

For help getting started with this feature read the [extracting css guide](https://compiledcssinjs.com/docs/extracting-css).
5 changes: 5 additions & 0 deletions .changeset/rude-impalas-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@compiled/babel-plugin-strip-runtime': patch
---

The `onFoundStyleSheet` option has been replaced by `onFoundStyleSheet`. This callback will be called once with all found styles at the end of the pass.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@ jobs:
- name: Build webpack example
run: yarn build:webpack

- name: Build webpack extracted example
run: yarn build:webpack:extract

- name: Build parcel example
run: yarn build:parcel
7 changes: 7 additions & 0 deletions examples/packages/babel-component/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"presets": [
["@babel/preset-env", { "targets": { "browsers": "last 1 version" } }],
["@babel/preset-react", { "runtime": "automatic" }]
],
"plugins": [["@compiled/babel-plugin", { "importReact": false }]]
}
22 changes: 22 additions & 0 deletions examples/packages/babel-component/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@private/babel-component",
"private": true,
"version": "0.0.1",
"main": "./dist/index.js",
"scripts": {
"build": "babel ./src --out-dir=./dist"
},
"dependencies": {
"@compiled/react": "*"
},
"devDependencies": {
"@compiled/babel-plugin": "*",
"@babel/core": "^7.12.16",
"@babel/cli": "^7.12.16",
"@babel/preset-env": "^7.12.16",
"@babel/preset-react": "^7.12.13"
},
"peerDependencies": {
"react": "^17.0.1"
}
}
16 changes: 16 additions & 0 deletions examples/packages/babel-component/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { styled } from '@compiled/react';

const Button = styled.button`
color: blue;
font-size: 30px;
border: 2px solid blue;
padding: 8px;
`;

export default function BabelComponent({ children }) {
return (
<div css={{ marginTop: 30 }}>
<Button>{children}</Button>
</div>
);
}
4 changes: 2 additions & 2 deletions examples/packages/parcel/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { hydrate } from 'react-dom';
import { render } from 'react-dom';
import App from './app';

hydrate(<App />, document.getElementById('root'));
render(<App />, document.getElementById('root'));
11 changes: 8 additions & 3 deletions examples/packages/webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@
"dependencies": {
"@compiled/react": "*",
"@compiled/webpack-loader": "*",
"@private/babel-component": "*",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "5.3.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"css-loader": "^5.1.2",
"css-minimizer-webpack-plugin": "^1.2.0",
"html-webpack-plugin": "^5.3.1",
"mini-css-extract-plugin": "^1.3.9",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"style-loader": "^2.0.0",
"webpack": "^5.27.0",
"webpack-cli": "^4.5.0"
}
Expand Down
8 changes: 8 additions & 0 deletions examples/packages/webpack/src/app.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Suspense, lazy } from 'react';
import '@compiled/react';
import BabelComponent from '@private/babel-component';
import { primary } from './module';
import HelloWorld from './component';

const AsyncComponent = lazy(() => import('./async'));

export default function Home() {
return (
<>
<div css={{ fontSize: 50, color: primary }}>hello from webpack</div>
<HelloWorld>TypeScript component</HelloWorld>
<BabelComponent>Component from NPM</BabelComponent>
<Suspense fallback="Loading...">
<AsyncComponent>I was loaded async</AsyncComponent>
</Suspense>
</>
);
}
22 changes: 22 additions & 0 deletions examples/packages/webpack/src/async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { styled } from '@compiled/react';

const LoadedAsync = styled.button`
font-weight: 700;
color: purple;
border: 2px solid pink;
background-color: transparent;
:focus {
color: blue;
}
:hover {
color: red;
}
@media (min-width: 500px) {
border: 2px solid red;
}
`;

export default LoadedAsync;
42 changes: 41 additions & 1 deletion examples/packages/webpack/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,48 @@ import { primary } from './module';
const HelloWorld = styled.div`
color: ${primary};
font-weight: 500;
:hover {
color: red;
}
:focus {
color: blue;
}
`;

const MediaComponent = styled.div`
border: 10px dotted purple;
:before {
content: 'small screen';
}
@media (min-width: 500px) {
border: 2px solid red;
:before {
content: 'large screen';
}
}
`;

export default function TSComponent(props: { children: string }): JSX.Element {
return <HelloWorld>{props.children}</HelloWorld>;
return (
<HelloWorld tabIndex={0}>
{props.children}
<MediaComponent />

<div
css={{
span: {
color: '#ccc',
':hover': {
color: 'red',
},
},
}}>
<span>nested span</span>
</div>
</HelloWorld>
);
}
4 changes: 4 additions & 0 deletions examples/packages/webpack/src/extra.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
body::before {
display: block;
content: 'CSS FROM A FILE OUTSIDE OF COMPILED';
}
5 changes: 3 additions & 2 deletions examples/packages/webpack/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { hydrate } from 'react-dom';
import { render } from 'react-dom';
import './extra.css';
import App from './app';

function createRoot() {
Expand All @@ -9,7 +10,7 @@ function createRoot() {
}

const element = document.getElementById('root') || createRoot();
hydrate(<App />, element);
render(<App />, element);

if (module.hot) {
module.hot.accept();
Expand Down
22 changes: 20 additions & 2 deletions examples/packages/webpack/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CompiledExtractPlugin } = require('@compiled/webpack-loader');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

const extractCSS = process.env.EXTRACT_TO_CSS === 'true';

module.exports = {
mode: 'development',
Expand All @@ -24,11 +29,24 @@ module.exports = {
loader: '@compiled/webpack-loader',
options: {
importReact: false,
extract: extractCSS,
},
},
],
},
],
{
test: /\.css$/i,
use: [extractCSS ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'],
},
].filter(Boolean),
},
plugins: [
extractCSS && new MiniCssExtractPlugin({ filename: '[name].css' }),
extractCSS && new CompiledExtractPlugin(),
new HtmlWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),
].filter(Boolean),
optimization: {
minimizer: ['...', new CssMinimizerPlugin()],
},
plugins: [new HtmlWebpackPlugin(), new webpack.HotModuleReplacementPlugin()],
};
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,28 @@
"start:cli": "cd packages/cli && yarn start",
"start:inspect": "npx nodemon --exec \"node --inspect-brk node_modules/.bin/start-storybook -p 6006 --ci\" --watch packages/babel-plugin/ -e tsx",
"start:webpack": "yarn build:browser && yarn build:esm && cd examples/packages/webpack && yarn start",
"start:webpack:extract": "yarn build:browser && yarn build:esm && cd examples/packages/webpack && EXTRACT_TO_CSS=true yarn start",
"test": "yarn build:cjs && yarn build:esm && jest --no-cache",
"test:watch": "yarn build:esm && jest --no-cache --watch",
"test:imports": "node test/test-imports",
"test:cover": "yarn test --collectCoverage",
"lint": "eslint --config .eslintrc.js --ext tsx,ts ./packages/**/src ./examples",
"lint:fix": "yarn lint -- --fix",
"build": "yarn build:esm && yarn build:cjs && yarn build:browser",
"build:esm": "ttsc --build packages/tsconfig.json",
"build:esm": "ttsc --build packages/tsconfig.json && yarn build:examples-babel",
"build:cjs": "ttsc --build packages/tsconfig.cjs.json",
"build:examples-babel": "cd examples/packages/babel-component && yarn build",
"build:browser": "IS_NODE_EXPRESSION='false' ttsc --build packages/tsconfig.browser.json",
"build:dead-code-elimination": "cd test/dead-code-elimination && ttsc",
"build:inspect": "node --inspect-brk node_modules/typescript/lib/tsc.js --build packages",
"build:webpack": "yarn build:browser && yarn build:esm && cd examples/packages/webpack && yarn build",
"build:webpack:extract": "yarn build:browser && yarn build:esm && cd examples/packages/webpack && EXTRACT_TO_CSS=true yarn build",
"build:parcel": "yarn build:browser && yarn build:esm && cd examples/packages/parcel && yarn build",
"build-storybook": "build-storybook",
"build-ssr": "CI=false && yarn build && cd examples/packages/ssr && yarn build",
"bundlesize": "yarn build && yarn build:dead-code-elimination && size-limit",
"postinstall": "npx yarn-deduplicate && yarn --ignore-scripts",
"release": "yarn clean && yarn build && yarn changeset publish"
"release": "yarn clean && yarn build && yarn changeset publish",
"postinstall": "npx yarn-deduplicate && yarn --ignore-scripts"
},
"devDependencies": {
"@babel/plugin-transform-react-jsx-self": "^7.12.13",
Expand Down Expand Up @@ -78,9 +81,9 @@
"workspaces-run": "^1.0.1"
},
"resolutions": {
"typescript": "^4.2.3",
"@babel/core": "^7.13.10",
"jest": "^26.6.3",
"@babel/core": "^7.13.10"
"typescript": "^4.2.3"
},
"husky": {
"hooks": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import stripRuntimeBabelPlugin from '../index';

const transform = (
opts: {
callback?: (style: string) => void;
callback?: (style: string[]) => void;
runtime: 'automatic' | 'classic';
run: 'bake' | 'extract' | 'both';
} = {
Expand All @@ -18,7 +18,7 @@ const transform = (

const plugins: any = [
runBake && [compiledBabelPlugin, { importReact: opts.runtime === 'classic' }],
runExtract && [stripRuntimeBabelPlugin, { onFoundStyleSheet: opts.callback }],
runExtract && [stripRuntimeBabelPlugin, { onFoundStyleRules: opts.callback }],
].filter(Boolean);

const result = transformSync(typeof code === 'string' ? code : code[0], {
Expand Down Expand Up @@ -86,8 +86,10 @@ describe('first party strip runtime', () => {
const Component = () => <div css={{ fontSize: 12, color: 'blue' }}>hello world</div>
`;

expect(callback).toHaveBeenCalledWith('._1wyb1fwx{font-size:12px}');
expect(callback).toHaveBeenCalledWith('._syaz13q2{color:blue}');
expect(callback).toHaveBeenCalledWith([
'._1wyb1fwx{font-size:12px}',
'._syaz13q2{color:blue}',
]);
});

it('should callback on every found style automatic', () => {
Expand All @@ -99,8 +101,10 @@ describe('first party strip runtime', () => {
const Component = () => <div css={{ fontSize: 12, color: 'blue' }}>hello world</div>
`;

expect(callback).toHaveBeenCalledWith('._1wyb1fwx{font-size:12px}');
expect(callback).toHaveBeenCalledWith('._syaz13q2{color:blue}');
expect(callback).toHaveBeenCalledWith([
'._1wyb1fwx{font-size:12px}',
'._syaz13q2{color:blue}',
]);
});
});

Expand Down Expand Up @@ -158,8 +162,10 @@ describe('first party strip runtime', () => {

transform({ runtime: 'classic', run: 'both', callback })(baked);

expect(callback).toHaveBeenCalledWith('._1wyb1fwx{font-size:12px}');
expect(callback).toHaveBeenCalledWith('._syaz13q2{color:blue}');
expect(callback).toHaveBeenCalledWith([
'._1wyb1fwx{font-size:12px}',
'._syaz13q2{color:blue}',
]);
});

it('should callback on every found style automatic', () => {
Expand All @@ -172,8 +178,10 @@ describe('first party strip runtime', () => {

transform({ runtime: 'automatic', run: 'both', callback })(baked);

expect(callback).toHaveBeenCalledWith('._1wyb1fwx{font-size:12px}');
expect(callback).toHaveBeenCalledWith('._syaz13q2{color:blue}');
expect(callback).toHaveBeenCalledWith([
'._1wyb1fwx{font-size:12px}',
'._syaz13q2{color:blue}',
]);
});
});
});
Loading

0 comments on commit 0bb1c11

Please sign in to comment.