Skip to content

Commit

Permalink
feat: add esbuild integration (#765)
Browse files Browse the repository at this point in the history
* feat: add esbuild integration

* fix: ignore node_modules in file transform
  • Loading branch information
Mokshit06 authored Jun 6, 2021
1 parent b6df746 commit 511a717
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 0 deletions.
31 changes: 31 additions & 0 deletions docs/BUNDLERS_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,37 @@ You can pass options to the loader like so:
}
```

### esbuild

To use Linaria with Rollup, you don't need to install any external package since esbuild handles CSS by itself:

```sh
yarn add --dev @linaria/esbuild
```

Then add it to your esbuild config:

```js
import linaria from '@linaria/esbuild';
import esbuild from 'esbuild';

const prod = process.env.NODE_ENV === 'production';

esbuild
.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
bundle: true,
minify: prod,
plugins: [
linaria({
sourceMap: prod,
}),
],
})
.catch(() => process.exit(1));
```

### Rollup

To use Linaria with Rollup, you need to use it together with a plugin which handles CSS files, such as `rollup-plugin-css-only`:
Expand Down
35 changes: 35 additions & 0 deletions packages/esbuild/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<p align="center">
<img alt="Linaria" src="https://raw.githubusercontent.com/callstack/linaria/HEAD/website/assets/linaria-logo@2x.png" width="496">
</p>

<p align="center">
Zero-runtime CSS in JS library.
</p>

---

### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation.

## Features

- Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build
- Familiar **CSS syntax** with Sass like nesting
- Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes
- Easily find where the style was defined with **CSS sourcemaps**
- **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint)
- Use **JavaScript for logic**, no CSS preprocessor needed
- Optionally use any **CSS preprocessor** such as Sass or PostCSS

**[Why use Linaria](../../docs/BENEFITS.md)**

## Installation

```sh
npm install @linaria/core @linaria/react @linaria/babel-preset @linaria/shaker
```

or

```sh
yarn add @linaria/core @linaria/react @linaria/babel-preset @linaria/shaker
```
3 changes: 3 additions & 0 deletions packages/esbuild/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = require('../../babel.config');

module.exports = config;
48 changes: 48 additions & 0 deletions packages/esbuild/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@linaria/esbuild",
"version": "3.0.0-beta.5",
"publishConfig": {
"access": "public"
},
"description": "Blazing fast zero-runtime CSS in JS library",
"main": "lib/index.js",
"module": "esm/index.js",
"types": "types",
"files": [
"types/",
"lib/",
"esm/"
],
"license": "MIT",
"repository": "git@github.com:callstack/linaria.git",
"bugs": {
"url": "https://github.com/callstack/linaria/issues"
},
"homepage": "https://github.com/callstack/linaria#readme",
"keywords": [
"react",
"linaria",
"css",
"css-in-js",
"styled-components",
"babel-plugin",
"babel",
"esbuild"
],
"scripts": {
"build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start",
"build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start",
"build": "yarn build:lib && yarn build:esm",
"build:declarations": "tsc --emitDeclarationOnly --outDir types",
"prepare": "yarn build && yarn build:declarations",
"typecheck": "tsc --noEmit --composite false",
"watch": "yarn build --watch"
},
"dependencies": {
"esbuild": "^0.12.5",
"@linaria/babel-preset": "^3.0.0-beta.5"
},
"peerDependencies": {
"@babel/core": ">=7"
}
}
95 changes: 95 additions & 0 deletions packages/esbuild/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* This file contains an esbuild loader for Linaria.
* It uses the transform.ts function to generate class names from source code,
* returns transformed code without template literals and attaches generated source maps
*/

import path from 'path';
import fs from 'fs';
import type { PluginOptions, Preprocessor } from '@linaria/babel-preset';
import { slugify, transform } from '@linaria/babel-preset';
import esbuild, { Plugin, TransformOptions, Loader } from 'esbuild';

type EsbuildPluginOptions = {
sourceMap?: boolean;
preprocessor?: Preprocessor;
esbuildOptions?: TransformOptions;
} & Partial<PluginOptions>;

const nodeModulesRegex = /^(?:.*[\\/])?node_modules(?:[\\/].*)?$/;

export default function linaria({
sourceMap,
preprocessor,
esbuildOptions,
...rest
}: EsbuildPluginOptions = {}): Plugin {
return {
name: 'linaria',
setup(build) {
const cssLookup = new Map<string, string>();

build.onResolve({ filter: /\.linaria\.css$/ }, (args) => {
return {
namespace: 'linaria',
path: args.path,
};
});

build.onLoad({ filter: /.*/, namespace: 'linaria' }, (args) => {
return {
contents: cssLookup.get(args.path),
loader: 'css',
};
});

build.onLoad({ filter: /\.(js|jsx|ts|tsx)$/ }, (args) => {
const rawCode = fs.readFileSync(args.path, 'utf8');
const { ext, name: filename } = path.parse(args.path);
const loader = ext.replace(/^\./, '') as Loader;

if (nodeModulesRegex.test(args.path)) {
return {
loader,
contents: rawCode,
};
}

const { code } = esbuild.transformSync(rawCode, {
...esbuildOptions,
loader,
});
const result = transform(code, {
filename: args.path,
preprocessor,
pluginOptions: rest,
});

if (!result.cssText) {
return {
contents: code,
};
}

let { cssText } = result;

const slug = slugify(cssText);
const cssFilename = `${filename}_${slug}.linaria.css`;

if (sourceMap && result.cssSourceMapText) {
const map = Buffer.from(result.cssSourceMapText).toString('base64');
cssText += `/*# sourceMappingURL=data:application/json;base64,${map}*/`;
}

cssLookup.set(cssFilename, cssText);

return {
contents: `
import ${JSON.stringify(cssFilename)};
${result.code}
`,
};
});
},
};
}
12 changes: 12 additions & 0 deletions packages/esbuild/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {},
"rootDir": "src/"
},
"references": [
{
"path": "../babel"
}
]
}
6 changes: 6 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5896,6 +5896,11 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"

esbuild@^0.12.5:
version "0.12.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.5.tgz#36076a6bc1966ba2741981d30512e95e8aaff495"
integrity sha512-vcuP53pA5XiwUU4FnlXM+2PnVjTfHGthM7uP1gtp+9yfheGvFFbq/KyuESThmtoHPUrfZH5JpxGVJIFDVD1Egw==

escalade@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
Expand Down Expand Up @@ -9676,6 +9681,7 @@ minipass-fetch@^1.1.2:
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.2.tgz#573766fb1ae86e30df916a6b060bc2e801bf8f37"
integrity sha512-/i4fX1ss+Dtwyk++OsAI6SEV+eE1dvI6W+0hORdjfruQ7VD5uYTetJIHcEMjWiEiszWjn2aAtP1CB/Q4KfeoYA==
dependencies:
encoding "^0.1.12"
minipass "^3.1.0"
minipass-sized "^1.0.3"
minizlib "^2.0.0"
Expand Down

0 comments on commit 511a717

Please sign in to comment.