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: add esbuild plugin #113

Merged
merged 40 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7d0268a
Add eslint-plugin package
nedjulius Dec 8, 2023
94b7407
Add initial test suite
nedjulius Dec 8, 2023
14637f0
Improve CSS handling and tests
nedjulius Dec 9, 2023
6fe9c5f
Address some of the comments
nedjulius Dec 9, 2023
d3a0f5f
Add babel scripts for jsx, flow and ts
nedjulius Dec 9, 2023
cd62e9f
Add package diff
nedjulius Dec 9, 2023
4a287db
Add snapshots and fix nits
nedjulius Dec 10, 2023
7811896
Fix conflicts
nedjulius Dec 10, 2023
17011de
Fix rename bug
nedjulius Dec 10, 2023
eb94b3c
Add esbuild example app
nedjulius Dec 10, 2023
90b06c3
Fix incorrect loaders
nedjulius Dec 10, 2023
083de80
Update package-lock.json
nedjulius Dec 10, 2023
891c561
Remove unnecessary file
nedjulius Dec 10, 2023
bad0b30
Fix some nits in example
nedjulius Dec 10, 2023
9c1ee21
Add additional style to text fixture
nedjulius Dec 11, 2023
0aa6583
Remove ext from import in example
nedjulius Dec 11, 2023
96324ac
Address some of the comments
nedjulius Dec 11, 2023
52caaf3
Update package-lock.json
nedjulius Dec 11, 2023
35f164d
Add missing function call and snapshot
nedjulius Dec 11, 2023
27dcb73
Remove comment
nedjulius Dec 11, 2023
0fcd813
Pluralize function name
nedjulius Dec 11, 2023
922f158
Merge branch 'main' of github.com:nedjulius/stylex into nedjulius/add…
nedjulius Dec 13, 2023
7a788d0
Add types
nedjulius Dec 13, 2023
99da76c
Fix build script
nedjulius Dec 13, 2023
f4ff4da
Add docs
nedjulius Dec 13, 2023
8b9fca8
Fix tabulation
nedjulius Dec 13, 2023
e48090a
Retake snapshots
nedjulius Dec 13, 2023
a12edd2
Format prettier
nedjulius Dec 13, 2023
3e1692f
Add more missing types
nedjulius Dec 13, 2023
2608220
Run formatter
nedjulius Dec 13, 2023
6b25103
Fix formatting
nedjulius Dec 14, 2023
736c1f2
Lock dependency and fix import
nedjulius Dec 14, 2023
9338cb8
Disable unused key
nedjulius Dec 14, 2023
4ff83a5
Add docs
nedjulius Dec 14, 2023
ac9caa0
Merge conflict
nedjulius Dec 17, 2023
a07f5bb
Update package-lock.json
nedjulius Dec 18, 2023
91f6eca
Update package-lock.json
nedjulius Dec 18, 2023
cd1f8b9
Add snapshots
nedjulius Dec 18, 2023
9615a5b
Address comments
nedjulius Dec 18, 2023
6fa1540
Merge branch 'main' into nedjulius/add-esbuild-plugin
nedjulius Dec 24, 2023
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
1,481 changes: 1,481 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@types/jest": "^29.5.5",
"babel-plugin-syntax-hermes-parser": "^0.18.0",
"benchmark": "^2.1.4",
"esbuild": "^0.19.8",
"eslint": "8.47.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-ft-flow": "^3.0.0",
Expand Down
3 changes: 3 additions & 0 deletions packages/esbuild-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @stylexjs/esbuild-plugin

...
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 12 additions & 0 deletions packages/esbuild-plugin/__tests__/__fixtures__/.babelrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": 11
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
}
}
]
]
}
20 changes: 20 additions & 0 deletions packages/esbuild-plugin/__tests__/__fixtures__/bazStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/

'use strict';

import stylex from 'stylex';

export default stylex.create({
baz: {
display: 'block',
height: 900,
width: '50%',
},
});
19 changes: 19 additions & 0 deletions packages/esbuild-plugin/__tests__/__fixtures__/fooStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/

'use strict';

import stylex from 'stylex';

export default stylex.create({
foo: {
display: 'inline',
width: '100%',
},
});
39 changes: 39 additions & 0 deletions packages/esbuild-plugin/__tests__/__fixtures__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/

'use strict';

import stylex from 'stylex';
import fooStyles from './fooStyles';

const fadeInAnimation = stylex.keyframes({
'0%': {
opacity: 0,
},
'100%': {
opacity: 1,
},
});

const styles = stylex.create({
bar: {
animationName: fadeInAnimation,
display: 'flex',
marginLeft: 10,
height: 700,
backgroundColor: 'red',
':hover': {
backgroundColor: 'pink',
},
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
},
});

export default function App() {
return stylex(fooStyles.foo, styles.bar);
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
}
43 changes: 43 additions & 0 deletions packages/esbuild-plugin/__tests__/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/

'use strict';

const path = require('path');
const esbuild = require('esbuild');
const stylexPlugin = require('../src/index');

async function build(options = {}) {
const { outputFiles } = await esbuild.build({
entryPoints: [path.resolve(__dirname, '__fixtures__/index.js')],
external: ['stylex'],
minify: false,
bundle: true,
write: false,
plugins: [stylexPlugin({ ...options })],
});

return { js: outputFiles[0], css: outputFiles[1] };
}

describe('esbuild-plugin-stylex', () => {
it('prod', async () => {
const { js, css } = await build();

expect(js).toBeDefined();
expect(css).toBeDefined();
});

it('dev', async () => {
const { js, css } = await build({ dev: true });

expect(js).toBeDefined();
expect(css).toBeUndefined();
});
});
26 changes: 26 additions & 0 deletions packages/esbuild-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@stylexjs/esbuild-plugin",
"version": "0.0.0",
"description": "StyleX esbuild plugin",
"main": "src/index.js",
"repository": "https://github.com/facebook/stylex.git",
"license": "MIT",
"scripts": {
"test": "jest"
},
"jest": {
"testPathIgnorePatterns": [
"/node_modules/",
"/__fixtures__/"
],
"testEnvironment": "node"
},
"dependencies": {
"@babel/core": "^7.23.5",
"@stylexjs/babel-plugin": "^0.3.0"
},
"devDependencies": {
"eslint": "^8.55.0",
"@stylexjs/stylex": "0.3.0"
}
}
105 changes: 105 additions & 0 deletions packages/esbuild-plugin/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/

const babel = require('@babel/core');
const stylexBabelPlugin = require('@stylexjs/babel-plugin');
const path = require('path');

Check failure on line 12 in packages/esbuild-plugin/src/index.js

View workflow job for this annotation

GitHub Actions / lint

'path' is assigned a value but never used. Allowed unused vars must match /^_/u
const fs = require('fs/promises');

const PACKAGE_NAME = 'esbuild-plugin-stylex';

const IS_DEV_ENV =
process.env.NODE_ENV === 'development' ||
process.env.BABEL_ENV === 'development';

function stylexPlugin({
dev = IS_DEV_ENV,
unstable_moduleResolution = { type: 'commonJS', rootDir: process.cwd() },
stylexImports = ['stylex', '@stylexjs/stylex'],
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
// path
outPath = 'stylex.css',

Check failure on line 26 in packages/esbuild-plugin/src/index.js

View workflow job for this annotation

GitHub Actions / lint

'outPath' is assigned a value but never used. Allowed unused vars must match /^_/u
babelConfig: { plugins = [], presets = [] } = {},
...options
} = {}) {
return {
name: PACKAGE_NAME,
async setup({ onLoad, onEnd, initialOptions }) {
const stylexRules = {};

onEnd(({ outputFiles }) => {
const rules = Object.values(stylexRules).flat();

if (rules.length === 0) {
return;
}

const collectedCSS = stylexBabelPlugin.processStylexRules(rules, true);
const shouldWriteToDisk =
initialOptions.write === undefined || initialOptions.write;

if (!shouldWriteToDisk) {
// write to disk
// fs.writeFile(path.resolve(__dirname, outPath))
return;
}

outputFiles.push({
path: '<stdout>',
contents: new TextEncoder().encode(collectedCSS),
get text() {
return collectedCSS;
},
});
nmn marked this conversation as resolved.
Show resolved Hide resolved
});

onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
const currFileName = args.path;
const inputCode = await fs.readFile(currFileName, 'utf8');

if (
!stylexImports.some((importName) => inputCode.includes(importName))
nedjulius marked this conversation as resolved.
Show resolved Hide resolved
) {
// avoid transform if file doesn't have stylex imports
// esbuild proceeds to the next callback
return;
}

// sourcemap?
const { code, metadata } = await babel.transformAsync(inputCode, {
babelrc: false,
filename: currFileName,
presets,
plugins: [
...plugins,
[
stylexBabelPlugin,
// handle Flow or TS plugin here
{
dev,
unstable_moduleResolution,
...options,
},
],
],
});

if (!dev && metadata.stylex !== null && metadata.stylex.length > 0) {
stylexRules[args.path] = metadata.stylex;
}

return {
contents: code,
loader: currFileName.endsWith('.js') ? 'js' : 'tsx',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

tsx loader can handle ts, tsx and jsx files

Copy link
Contributor Author

@nedjulius nedjulius Dec 10, 2023

Choose a reason for hiding this comment

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

update: turns out this wasn't entirely correct. encountered some import resolution problems when using tsx loader for JSX files, even though the esbuild documentation said that either loader is fine. for the sake of simplicity, I just match extension to loader. we can rely on extensions because of the filter guarantees.

I could also create a dictionary and map extension to loader in case esbuild changes the loader names or we want to use different loader for a given extension. lmk what you think.

};
});
},
};
}

module.exports = stylexPlugin;
Loading