@stylexjs/postcss-plugin 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,3 +135,14 @@ useCSSLayers: boolean; // Default: false
135
135
 
136
136
  Enabling this option switches Stylex from using `:not(#\#)` to using `@layers`
137
137
  for handling CSS specificity.
138
+
139
+ ---
140
+
141
+ ### importSources
142
+
143
+ ```js
144
+ importSources: Array<string | { from: string, as: string }>; // Default: ['@stylexjs/stylex', 'stylex']
145
+ ```
146
+
147
+ Possible strings where you can import stylex from. Files that do not match the
148
+ import sources may be skipped from being processed to speed up compilation.
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: [
3
+ ['@stylexjs/babel-plugin', { dev: false, runtimeInjection: false }],
4
+ ],
5
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import { css } from 'react-strict-dom';
9
+
10
+ export const styles = css.create({
11
+ object: {
12
+ backgroundColor: 'yellow',
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import * as custom from 'custom';
9
+
10
+ export const styles = custom.create({
11
+ string: {
12
+ backgroundColor: 'blue',
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import * as stylex from '@stylexjs/stylex';
9
+
10
+ export const styles = stylex.create({
11
+ second: {
12
+ backgroundColor: 'green',
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import * as stylex from '@stylexjs/stylex';
9
+
10
+ export const styles = stylex.create({
11
+ container: {
12
+ backgroundColor: 'red',
13
+ },
14
+ });
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const path = require('path');
11
+ const postcss = require('postcss');
12
+ const createPlugin = require('../src/plugin');
13
+
14
+ describe('@stylexjs/postcss-plugin', () => {
15
+ const fixturesDir = path.resolve(__dirname, '__fixtures__');
16
+
17
+ async function runStylexPostcss(options = {}, inputCSS = '@stylex;') {
18
+ // Create a new instance for each test as the plugin is stateful
19
+ const stylexPostcssPlugin = createPlugin();
20
+
21
+ const plugin = stylexPostcssPlugin({
22
+ cwd: fixturesDir,
23
+ include: ['**/*.js'],
24
+ babelConfig: {
25
+ configFile: path.join(fixturesDir, '.babelrc.js'),
26
+ },
27
+ ...options,
28
+ });
29
+
30
+ const processor = postcss([plugin]);
31
+ const result = await processor.process(inputCSS, {
32
+ from: path.join(fixturesDir, 'input.css'),
33
+ });
34
+
35
+ return result;
36
+ }
37
+
38
+ test('extracts CSS from StyleX files', async () => {
39
+ const result = await runStylexPostcss();
40
+
41
+ expect(result.css).toMatchInlineSnapshot(`
42
+ ".x1u857p9{background-color:green}
43
+ .xrkmrrc{background-color:red}"
44
+ `);
45
+
46
+ // Check that messages contain dependency information
47
+ expect(result.messages.length).toBeGreaterThan(0);
48
+ expect(result.messages.some((m) => m.type === 'dir-dependency')).toBe(true);
49
+ });
50
+
51
+ test('handles empty CSS input without @stylex rule', async () => {
52
+ const result = await runStylexPostcss({}, '/* No stylex rule here */');
53
+
54
+ expect(result.css).toMatchInlineSnapshot('"/* No stylex rule here */"');
55
+ expect(result.messages.length).toBe(0);
56
+ });
57
+
58
+ test('supports CSS layers', async () => {
59
+ const result = await runStylexPostcss({ useCSSLayers: true });
60
+
61
+ expect(result.css).toContain('@layer');
62
+ expect(result.css).toMatchInlineSnapshot(`
63
+ "
64
+ @layer priority1;
65
+ @layer priority1{
66
+ .x1u857p9{background-color:green}
67
+ .xrkmrrc{background-color:red}
68
+ }"
69
+ `);
70
+ });
71
+
72
+ test('handles exclude patterns', async () => {
73
+ const result = await runStylexPostcss({
74
+ exclude: ['**/styles-second.js'],
75
+ });
76
+
77
+ // Should not contain styles-second.js styles
78
+ expect(result.css).not.toContain('green');
79
+
80
+ expect(result.css).toMatchInlineSnapshot(
81
+ '".xrkmrrc{background-color:red}"',
82
+ );
83
+ });
84
+
85
+ test('respects string syntax for importSources', async () => {
86
+ // Default importSources should not process any files
87
+ const defaultResult = await runStylexPostcss({
88
+ include: ['**/import-sources-*.js'],
89
+ });
90
+
91
+ expect(defaultResult.css).toBe('');
92
+
93
+ // Custom importSources should process only import-sources-string.js
94
+ const customResult = await runStylexPostcss({
95
+ include: ['**/import-sources-*.js'],
96
+ importSources: ['custom'],
97
+ babelConfig: {
98
+ babelrc: false,
99
+ plugins: [
100
+ [
101
+ '@stylexjs/babel-plugin',
102
+ {
103
+ dev: false,
104
+ runtimeInjection: false,
105
+ importSources: ['custom'],
106
+ },
107
+ ],
108
+ ],
109
+ },
110
+ });
111
+
112
+ expect(customResult.css).toMatchInlineSnapshot(
113
+ '".x1t391ir{background-color:blue}"',
114
+ );
115
+ });
116
+
117
+ test('supports object syntax for importSources', async () => {
118
+ const result = await runStylexPostcss({
119
+ include: ['**/import-sources-object.js'],
120
+ importSources: [{ as: 'css', from: 'react-strict-dom' }],
121
+ babelConfig: {
122
+ babelrc: false,
123
+ plugins: [
124
+ [
125
+ '@stylexjs/babel-plugin',
126
+ {
127
+ dev: false,
128
+ runtimeInjection: false,
129
+ importSources: [{ as: 'css', from: 'react-strict-dom' }],
130
+ },
131
+ ],
132
+ ],
133
+ },
134
+ });
135
+
136
+ expect(result.css).toMatchInlineSnapshot(
137
+ '".x1cu41gw{background-color:yellow}"',
138
+ );
139
+ });
140
+
141
+ test('skips files that do not match include/exclude patterns', async () => {
142
+ const result = await runStylexPostcss({
143
+ include: ['**/styles-second.js'],
144
+ });
145
+
146
+ // Should contain styles-second.js styles but not styles.js
147
+ expect(result.css).not.toContain('red');
148
+
149
+ expect(result.css).toMatchInlineSnapshot(
150
+ '".x1u857p9{background-color:green}"',
151
+ );
152
+ });
153
+ });
package/jest.config.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ module.exports = {
11
+ testPathIgnorePatterns: ['/__fixtures__/'],
12
+ testEnvironment: 'node',
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stylexjs/postcss-plugin",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "PostCSS plugin for StyleX",
5
5
  "main": "src/index.js",
6
6
  "repository": {
@@ -8,9 +8,12 @@
8
8
  "url": "git+https://github.com/facebook/stylex.git"
9
9
  },
10
10
  "license": "MIT",
11
+ "scripts": {
12
+ "test": "jest"
13
+ },
11
14
  "dependencies": {
12
15
  "@babel/core": "^7.26.8",
13
- "@stylexjs/babel-plugin": "0.12.0",
16
+ "@stylexjs/babel-plugin": "0.13.1",
14
17
  "postcss": "^8.4.49",
15
18
  "fast-glob": "^3.3.2",
16
19
  "glob-parent": "^6.0.2",
package/src/builder.js CHANGED
@@ -104,7 +104,8 @@ function createBuilder() {
104
104
 
105
105
  // Transforms the included files, bundles the CSS, and returns the result.
106
106
  async function build({ shouldSkipTransformError }) {
107
- const { cwd, babelConfig, useCSSLayers, isDev } = getConfig();
107
+ const { cwd, babelConfig, useCSSLayers, importSources, isDev } =
108
+ getConfig();
108
109
 
109
110
  const files = getFiles();
110
111
  const filesToTransform = [];
@@ -140,7 +141,7 @@ function createBuilder() {
140
141
  filesToTransform.map((file) => {
141
142
  const filePath = path.resolve(cwd, file);
142
143
  const contents = fs.readFileSync(filePath, 'utf-8');
143
- if (!bundler.shouldTransform(contents)) {
144
+ if (!bundler.shouldTransform(contents, { importSources })) {
144
145
  return;
145
146
  }
146
147
  return bundler.transform(filePath, contents, babelConfig, {
package/src/bundler.js CHANGED
@@ -13,8 +13,15 @@ module.exports = function createBundler() {
13
13
  const styleXRulesMap = new Map();
14
14
 
15
15
  // Determines if the source code should be transformed based on the presence of StyleX imports.
16
- function shouldTransform(sourceCode) {
17
- return sourceCode.includes('stylex');
16
+ function shouldTransform(sourceCode, options) {
17
+ const { importSources } = options;
18
+
19
+ return importSources.some((importSource) => {
20
+ if (typeof importSource === 'string') {
21
+ return sourceCode.includes(importSource);
22
+ }
23
+ return sourceCode.includes(importSource.from);
24
+ });
18
25
  }
19
26
 
20
27
  // Transforms the source code using Babel, extracting StyleX rules and storing them.
@@ -25,6 +32,7 @@ module.exports = function createBundler() {
25
32
  filename: id,
26
33
  caller: {
27
34
  name: '@stylexjs/postcss-plugin',
35
+ platform: 'web',
28
36
  isDev,
29
37
  },
30
38
  ...babelConfig,
package/src/index.js CHANGED
@@ -5,95 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- const postcss = require('postcss');
9
- const createBuilder = require('./builder');
8
+ const createPlugin = require('./plugin');
10
9
 
11
- const PLUGIN_NAME = '@stylexjs/postcss-plugin';
12
-
13
- const builder = createBuilder();
14
-
15
- const isDev = process.env.NODE_ENV === 'development';
16
-
17
- const plugin = ({
18
- cwd = process.cwd(),
19
- // By default reuses the Babel configuration from the project root.
20
- // Use `babelrc: false` to disable this behavior.
21
- babelConfig = {},
22
- include,
23
- exclude,
24
- useCSSLayers = false,
25
- }) => {
26
- exclude = [
27
- // Exclude type declaration files by default because it never contains any CSS rules.
28
- '**/*.d.ts',
29
- '**/*.flow',
30
- ...(exclude ?? []),
31
- ];
32
-
33
- // Whether to skip the error when transforming StyleX rules.
34
- // Useful in watch mode where Fast Refresh can recover from errors.
35
- // Initial transform will still throw errors in watch mode to surface issues early.
36
- let shouldSkipTransformError = false;
37
-
38
- return {
39
- postcssPlugin: PLUGIN_NAME,
40
- plugins: [
41
- // Processes the PostCSS root node to find and transform StyleX at-rules.
42
- async function (root, result) {
43
- const fileName = result.opts.from;
44
-
45
- // Configure the builder with the provided options
46
- await builder.configure({
47
- include,
48
- exclude,
49
- cwd,
50
- babelConfig,
51
- useCSSLayers,
52
- isDev,
53
- });
54
-
55
- // Find the "@stylex" at-rule
56
- const styleXAtRule = builder.findStyleXAtRule(root);
57
- if (styleXAtRule == null) {
58
- return;
59
- }
60
-
61
- // Get dependencies to be watched for changes
62
- const dependencies = builder.getDependencies();
63
-
64
- // Add each dependency to the PostCSS result messages.
65
- // This watches the entire "./src" directory for "./src/**/*.{ts,tsx}"
66
- // to handle new files and deletions reliably in watch mode.
67
- for (const dependency of dependencies) {
68
- result.messages.push({
69
- plugin: PLUGIN_NAME,
70
- parent: fileName,
71
- ...dependency,
72
- });
73
- }
74
-
75
- // Build and parse the CSS from collected StyleX rules
76
- const css = await builder.build({
77
- shouldSkipTransformError,
78
- });
79
- const parsed = await postcss.parse(css, {
80
- from: fileName,
81
- });
82
-
83
- // Replace the "@stylex" rule with the generated CSS
84
- styleXAtRule.replaceWith(parsed);
85
-
86
- result.root = root;
87
-
88
- if (!shouldSkipTransformError) {
89
- // Build was successful, subsequent builds are for watch mode
90
- shouldSkipTransformError = true;
91
- }
92
- },
93
- ],
94
- };
95
- };
96
-
97
- plugin.postcss = true;
98
-
99
- module.exports = plugin;
10
+ module.exports = createPlugin();
package/src/plugin.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ const postcss = require('postcss');
8
+ const createBuilder = require('./builder');
9
+
10
+ module.exports = function createPlugin() {
11
+ const PLUGIN_NAME = '@stylexjs/postcss-plugin';
12
+
13
+ const builder = createBuilder();
14
+
15
+ const isDev = process.env.NODE_ENV === 'development';
16
+
17
+ const plugin = ({
18
+ cwd = process.cwd(),
19
+ // By default reuses the Babel configuration from the project root.
20
+ // Use `babelrc: false` to disable this behavior.
21
+ babelConfig = {},
22
+ include,
23
+ exclude,
24
+ useCSSLayers = false,
25
+ importSources = ['@stylexjs/stylex', 'stylex'],
26
+ }) => {
27
+ exclude = [
28
+ // Exclude type declaration files by default because it never contains any CSS rules.
29
+ '**/*.d.ts',
30
+ '**/*.flow',
31
+ ...(exclude ?? []),
32
+ ];
33
+
34
+ // Whether to skip the error when transforming StyleX rules.
35
+ // Useful in watch mode where Fast Refresh can recover from errors.
36
+ // Initial transform will still throw errors in watch mode to surface issues early.
37
+ let shouldSkipTransformError = false;
38
+
39
+ return {
40
+ postcssPlugin: PLUGIN_NAME,
41
+ plugins: [
42
+ // Processes the PostCSS root node to find and transform StyleX at-rules.
43
+ async function (root, result) {
44
+ const fileName = result.opts.from;
45
+
46
+ // Configure the builder with the provided options
47
+ await builder.configure({
48
+ include,
49
+ exclude,
50
+ cwd,
51
+ babelConfig,
52
+ useCSSLayers,
53
+ importSources,
54
+ isDev,
55
+ });
56
+
57
+ // Find the "@stylex" at-rule
58
+ const styleXAtRule = builder.findStyleXAtRule(root);
59
+ if (styleXAtRule == null) {
60
+ return;
61
+ }
62
+
63
+ // Get dependencies to be watched for changes
64
+ const dependencies = builder.getDependencies();
65
+
66
+ // Add each dependency to the PostCSS result messages.
67
+ // This watches the entire "./src" directory for "./src/**/*.{ts,tsx}"
68
+ // to handle new files and deletions reliably in watch mode.
69
+ for (const dependency of dependencies) {
70
+ result.messages.push({
71
+ plugin: PLUGIN_NAME,
72
+ parent: fileName,
73
+ ...dependency,
74
+ });
75
+ }
76
+
77
+ // Build and parse the CSS from collected StyleX rules
78
+ const css = await builder.build({
79
+ shouldSkipTransformError,
80
+ });
81
+ const parsed = await postcss.parse(css, {
82
+ from: fileName,
83
+ });
84
+
85
+ // Replace the "@stylex" rule with the generated CSS
86
+ styleXAtRule.replaceWith(parsed);
87
+
88
+ result.root = root;
89
+
90
+ if (!shouldSkipTransformError) {
91
+ // Build was successful, subsequent builds are for watch mode
92
+ shouldSkipTransformError = true;
93
+ }
94
+ },
95
+ ],
96
+ };
97
+ };
98
+
99
+ plugin.postcss = true;
100
+
101
+ return plugin;
102
+ };