@stylexjs/unplugin 0.17.4 → 0.18.0
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 +49 -15
- package/lib/bun.d.ts +20 -0
- package/lib/bun.js +81 -0
- package/lib/core.d.ts +27 -0
- package/lib/core.js +420 -0
- package/lib/es/bun.mjs +73 -0
- package/lib/es/core.mjs +410 -0
- package/lib/es/esbuild.mjs +53 -0
- package/lib/es/farm.mjs +3 -0
- package/lib/es/index.mjs +26 -596
- package/lib/es/rolldown.mjs +4 -0
- package/lib/es/rollup.mjs +64 -0
- package/lib/es/rspack.mjs +4 -0
- package/lib/es/unloader.mjs +3 -0
- package/lib/es/vite.mjs +174 -0
- package/lib/es/webpack.mjs +43 -0
- package/lib/esbuild.d.ts +12 -0
- package/lib/esbuild.js +60 -0
- package/lib/farm.d.ts +12 -0
- package/lib/farm.js +9 -0
- package/lib/index.d.ts +25 -13
- package/lib/index.js +82 -585
- package/lib/rolldown.d.ts +12 -0
- package/lib/rolldown.js +10 -0
- package/lib/rollup.d.ts +12 -0
- package/lib/rollup.js +72 -0
- package/lib/rspack.d.ts +12 -0
- package/lib/rspack.js +10 -0
- package/lib/unloader.d.ts +12 -0
- package/lib/unloader.js +9 -0
- package/lib/vite.d.ts +12 -0
- package/lib/vite.js +181 -0
- package/lib/webpack.d.ts +12 -0
- package/lib/webpack.js +50 -0
- package/package.json +52 -5
package/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# @stylexjs/unplugin
|
|
2
2
|
|
|
3
|
-
Universal bundler plugin for StyleX built on top of `unplugin`. It compiles
|
|
3
|
+
Universal bundler plugin for StyleX built on top of `unplugin`. It compiles
|
|
4
|
+
StyleX at build time, aggregates CSS from all transformed modules, and appends
|
|
5
|
+
the result into an existing CSS asset produced by your bundler (or emits a
|
|
6
|
+
stable fallback when none exists).
|
|
4
7
|
|
|
5
8
|
- Adapters for Vite/Rollup, Webpack/Rspack, and esbuild.
|
|
6
9
|
- Designed to keep StyleX output consolidated and deterministic.
|
|
7
|
-
- Dev helpers expose virtual modules for hot CSS reloads:
|
|
10
|
+
- Dev helpers expose virtual modules for hot CSS reloads:
|
|
11
|
+
`virtual:stylex:runtime` (JS) and `/virtual:stylex.css` (CSS) or
|
|
12
|
+
`virtual:stylex:css-only` (JS shim).
|
|
8
13
|
|
|
9
14
|
## Install
|
|
10
15
|
|
|
@@ -14,6 +19,10 @@ npm i -D @stylexjs/unplugin
|
|
|
14
19
|
|
|
15
20
|
## Usage by bundler
|
|
16
21
|
|
|
22
|
+
Bundler-specific entrypoints are available (for example,
|
|
23
|
+
`@stylexjs/unplugin/vite`, `@stylexjs/unplugin/webpack`,
|
|
24
|
+
`@stylexjs/unplugin/esbuild`, and `@stylexjs/unplugin/bun`).
|
|
25
|
+
|
|
17
26
|
### Vite
|
|
18
27
|
|
|
19
28
|
```ts
|
|
@@ -33,10 +42,20 @@ export default defineConfig({
|
|
|
33
42
|
```
|
|
34
43
|
|
|
35
44
|
Notes:
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
|
|
46
|
+
- The plugin auto-discovers installed packages that depend on `@stylexjs/stylex`
|
|
47
|
+
(or any configured `importSources`) and excludes them from
|
|
48
|
+
`optimizeDeps`/`ssr.optimizeDeps` so their StyleX code is transformed. Use
|
|
49
|
+
`externalPackages` to force-deopt additional deps.
|
|
50
|
+
- `devMode: 'full'` injects a lightweight runtime that refetches the dev CSS
|
|
51
|
+
endpoint on HMR. `css-only` serves just the CSS endpoint. `off` disables dev
|
|
52
|
+
middleware/virtual modules.
|
|
53
|
+
- In dev, inject the virtual CSS + runtime from your HTML shell. If a
|
|
54
|
+
`<script src="/@id/virtual:stylex:runtime">` tag is blocked by CORS (some
|
|
55
|
+
frameworks proxy assets differently), call `import('virtual:stylex:runtime')`
|
|
56
|
+
or `import('virtual:stylex:css-only')` from a tiny client shim instead.
|
|
57
|
+
- Ensure your app produces a CSS asset (default Vite behavior). If none exists,
|
|
58
|
+
the plugin writes `stylex.css` in the output.
|
|
40
59
|
|
|
41
60
|
Dev HTML injection (baseline):
|
|
42
61
|
|
|
@@ -49,7 +68,8 @@ Dev HTML injection (baseline):
|
|
|
49
68
|
```
|
|
50
69
|
|
|
51
70
|
If your environment can safely load the runtime via a virtual module ID, replace
|
|
52
|
-
the inline script with
|
|
71
|
+
the inline script with
|
|
72
|
+
`<script type="module" src="/@id/virtual:stylex:runtime">`.
|
|
53
73
|
|
|
54
74
|
### Rollup
|
|
55
75
|
|
|
@@ -116,19 +136,33 @@ esbuild.build({
|
|
|
116
136
|
## Options (shared)
|
|
117
137
|
|
|
118
138
|
- `dev`: boolean, defaults based on `NODE_ENV`/`BABEL_ENV`.
|
|
119
|
-
- `importSources`: array of import sources to scan, default
|
|
139
|
+
- `importSources`: array of import sources to scan, default
|
|
140
|
+
`['stylex', '@stylexjs/stylex']`.
|
|
120
141
|
- `useCSSLayers`: boolean, emit CSS layers.
|
|
121
142
|
- `babelConfig`: `{ plugins, presets }` to merge into the internal Babel call.
|
|
122
143
|
- `unstable_moduleResolution`: forwarded to the StyleX Babel plugin.
|
|
123
144
|
- `lightningcssOptions`: pass-through options for `lightningcss`.
|
|
124
|
-
- `cssInjectionTarget`: `(fileName: string) => boolean` to pick a CSS asset to
|
|
125
|
-
|
|
145
|
+
- `cssInjectionTarget`: `(fileName: string) => boolean` to pick a CSS asset to
|
|
146
|
+
append to. Defaults to `index.css`, `style.css`, or the first `.css` asset.
|
|
147
|
+
- `externalPackages`: package names inside `node_modules` that should be treated
|
|
148
|
+
like app code (useful if they ship StyleX). They are excluded from Vite
|
|
149
|
+
dependency optimization.
|
|
126
150
|
- `devMode`: `'full' | 'css-only' | 'off'` (Vite only).
|
|
127
|
-
- `devPersistToDisk`: persist collected rules to
|
|
151
|
+
- `devPersistToDisk`: persist collected rules to
|
|
152
|
+
`node_modules/.stylex/rules.json` in dev so multiple Vite environments can
|
|
153
|
+
share CSS.
|
|
128
154
|
|
|
129
155
|
## Notes
|
|
130
156
|
|
|
131
|
-
- With multiple outputs (e.g. client/SSR), each output gets its own aggregated
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
157
|
+
- With multiple outputs (e.g. client/SSR), each output gets its own aggregated
|
|
158
|
+
StyleX CSS.
|
|
159
|
+
- If the bundler produces no CSS assets, the plugin emits a fallback
|
|
160
|
+
`stylex.css` (often in `assets/` for Rollup/Vite or alongside esbuild
|
|
161
|
+
outputs).
|
|
162
|
+
- When using extraction plugins (Webpack/Rspack), ensure they run so there is a
|
|
163
|
+
CSS asset to append to; otherwise the fallback file is created.
|
|
164
|
+
- Dev HMR CSS hookup: add `<link rel="stylesheet" href="/virtual:stylex.css" />`
|
|
165
|
+
to your shell in dev. For the JS runtime, prefer
|
|
166
|
+
`import('virtual:stylex:runtime')` (or `virtual:stylex:css-only` if you only
|
|
167
|
+
need CSS) from a local client shim when direct
|
|
168
|
+
`<script src="/@id/virtual:stylex:runtime">` fails due to CORS/proxying.
|
package/lib/bun.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 type { UserOptions } from './core';
|
|
9
|
+
|
|
10
|
+
type BunOptions = UserOptions & {
|
|
11
|
+
bunDevCssOutput?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare const plugin: any;
|
|
15
|
+
|
|
16
|
+
declare const createStylexBunPlugin: (options?: Partial<BunOptions>) => any;
|
|
17
|
+
|
|
18
|
+
export { createStylexBunPlugin };
|
|
19
|
+
|
|
20
|
+
export default plugin;
|
package/lib/bun.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.createStylexBunPlugin = void 0;
|
|
7
|
+
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
8
|
+
var _promises = _interopRequireDefault(require("node:fs/promises"));
|
|
9
|
+
var _core = require("./core");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
const loaders = {
|
|
12
|
+
'.js': 'js',
|
|
13
|
+
'.jsx': 'jsx',
|
|
14
|
+
'.ts': 'ts',
|
|
15
|
+
'.tsx': 'tsx'
|
|
16
|
+
};
|
|
17
|
+
const createStylexBunPlugin = (userOptions = {}) => {
|
|
18
|
+
const options = {
|
|
19
|
+
...userOptions
|
|
20
|
+
};
|
|
21
|
+
if (options.dev == null) options.dev = true;
|
|
22
|
+
if (options.runtimeInjection == null) options.runtimeInjection = false;
|
|
23
|
+
if (options.useCSSLayers == null) options.useCSSLayers = true;
|
|
24
|
+
const plugin = (0, _core.unpluginFactory)(options, {
|
|
25
|
+
framework: 'bun'
|
|
26
|
+
});
|
|
27
|
+
const cssOutput = options.bunDevCssOutput || _nodePath.default.resolve(process.cwd(), 'dist', 'stylex.dev.css');
|
|
28
|
+
let lastCss = null;
|
|
29
|
+
const writeCss = async () => {
|
|
30
|
+
const css = plugin.__stylexCollectCss?.() || '';
|
|
31
|
+
const next = css ? `:root { --stylex-injection: 0; }\n${css}` : ':root { --stylex-injection: 0; }';
|
|
32
|
+
if (next === lastCss) return;
|
|
33
|
+
lastCss = next;
|
|
34
|
+
try {
|
|
35
|
+
await _promises.default.mkdir(_nodePath.default.dirname(cssOutput), {
|
|
36
|
+
recursive: true
|
|
37
|
+
});
|
|
38
|
+
await _promises.default.writeFile(cssOutput, next, 'utf8');
|
|
39
|
+
} catch {}
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
name: '@stylexjs/unplugin-bun',
|
|
43
|
+
async setup(build) {
|
|
44
|
+
if (plugin.buildStart) {
|
|
45
|
+
build.onStart(async () => {
|
|
46
|
+
await plugin.buildStart();
|
|
47
|
+
await writeCss();
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
build.onStart(async () => {
|
|
51
|
+
await writeCss();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (plugin.buildEnd) {
|
|
55
|
+
build.onEnd(async () => {
|
|
56
|
+
await plugin.buildEnd();
|
|
57
|
+
await writeCss();
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
build.onEnd(async () => {
|
|
61
|
+
await writeCss();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
build.onLoad({
|
|
65
|
+
filter: /\.[cm]?[jt]sx?$/
|
|
66
|
+
}, async args => {
|
|
67
|
+
const code = await Bun.file(args.path).text();
|
|
68
|
+
const result = plugin.transform ? await plugin.transform(code, args.path) : null;
|
|
69
|
+
const nextCode = result?.code ?? code;
|
|
70
|
+
await writeCss();
|
|
71
|
+
return {
|
|
72
|
+
contents: nextCode,
|
|
73
|
+
loader: loaders[_nodePath.default.extname(args.path)] || 'js'
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
exports.createStylexBunPlugin = createStylexBunPlugin;
|
|
80
|
+
const defaultBunPlugin = createStylexBunPlugin({});
|
|
81
|
+
var _default = exports.default = defaultBunPlugin;
|
package/lib/core.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
import { type UnpluginFactory, type UnpluginInstance } from 'unplugin';
|
|
8
|
+
import type { Options as StyleXOptions } from '@stylexjs/babel-plugin';
|
|
9
|
+
import type { TransformOptions } from 'lightningcss';
|
|
10
|
+
|
|
11
|
+
type LightningcssOptions = TransformOptions<any>;
|
|
12
|
+
|
|
13
|
+
export type UserOptions = StyleXOptions & {
|
|
14
|
+
useCSSLayers?: boolean;
|
|
15
|
+
enableLTRRTLComments?: boolean;
|
|
16
|
+
legacyDisableLayers?: boolean;
|
|
17
|
+
lightningcssOptions?: LightningcssOptions;
|
|
18
|
+
cssInjectionTarget?: (filepath: string) => boolean;
|
|
19
|
+
devPersistToDisk?: boolean;
|
|
20
|
+
devMode?: 'full' | 'css-only' | 'off';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const unpluginFactory: UnpluginFactory<Partial<UserOptions>, false>;
|
|
24
|
+
|
|
25
|
+
declare const unplugin: UnpluginInstance<Partial<UserOptions>, false>;
|
|
26
|
+
|
|
27
|
+
export default unplugin;
|
package/lib/core.js
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
exports.pickCssAssetFromRollupBundle = pickCssAssetFromRollupBundle;
|
|
8
|
+
exports.replaceCssAssetWithHashedCopy = replaceCssAssetWithHashedCopy;
|
|
9
|
+
exports.unpluginFactory = exports.unplugin = void 0;
|
|
10
|
+
var _unplugin = require("unplugin");
|
|
11
|
+
var _core = require("@babel/core");
|
|
12
|
+
var _babelPlugin = _interopRequireDefault(require("@stylexjs/babel-plugin"));
|
|
13
|
+
var _pluginSyntaxFlow = _interopRequireDefault(require("@babel/plugin-syntax-flow"));
|
|
14
|
+
var _pluginSyntaxJsx = _interopRequireDefault(require("@babel/plugin-syntax-jsx"));
|
|
15
|
+
var _pluginSyntaxTypescript = _interopRequireDefault(require("@babel/plugin-syntax-typescript"));
|
|
16
|
+
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
17
|
+
var _nodeFs = _interopRequireDefault(require("node:fs"));
|
|
18
|
+
var _promises = _interopRequireDefault(require("node:fs/promises"));
|
|
19
|
+
var _nodeModule = require("node:module");
|
|
20
|
+
var _lightningcss = require("lightningcss");
|
|
21
|
+
var _browserslist = _interopRequireDefault(require("browserslist"));
|
|
22
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
|
+
function pickCssAssetFromRollupBundle(bundle, choose) {
|
|
24
|
+
const assets = Object.values(bundle).filter(a => a && a.type === 'asset' && typeof a.fileName === 'string' && a.fileName.endsWith('.css'));
|
|
25
|
+
if (assets.length === 0) return null;
|
|
26
|
+
if (typeof choose === 'function') {
|
|
27
|
+
const chosen = assets.find(a => choose(a.fileName));
|
|
28
|
+
if (chosen) return chosen;
|
|
29
|
+
}
|
|
30
|
+
const best = assets.find(a => /(^|\/)index\.css$/.test(a.fileName)) || assets.find(a => /(^|\/)style\.css$/.test(a.fileName));
|
|
31
|
+
return best || assets[0];
|
|
32
|
+
}
|
|
33
|
+
function processCollectedRulesToCSS(rules, options) {
|
|
34
|
+
if (!rules || rules.length === 0) return '';
|
|
35
|
+
const collectedCSS = _babelPlugin.default.processStylexRules(rules, {
|
|
36
|
+
useLayers: !!options.useCSSLayers,
|
|
37
|
+
enableLTRRTLComments: options?.enableLTRRTLComments
|
|
38
|
+
});
|
|
39
|
+
const {
|
|
40
|
+
code
|
|
41
|
+
} = (0, _lightningcss.transform)({
|
|
42
|
+
targets: (0, _lightningcss.browserslistToTargets)((0, _browserslist.default)('>= 1%')),
|
|
43
|
+
...options.lightningcssOptions,
|
|
44
|
+
filename: 'stylex.css',
|
|
45
|
+
code: Buffer.from(collectedCSS)
|
|
46
|
+
});
|
|
47
|
+
return code.toString();
|
|
48
|
+
}
|
|
49
|
+
function getAssetBaseName(asset) {
|
|
50
|
+
if (asset?.name && typeof asset.name === 'string') return asset.name;
|
|
51
|
+
const fallback = asset?.fileName ? _nodePath.default.basename(asset.fileName) : 'stylex.css';
|
|
52
|
+
const match = /^(.*?)(-[a-z0-9]{8,})?\.css$/i.exec(fallback);
|
|
53
|
+
if (match) return `${match[1]}.css`;
|
|
54
|
+
return fallback || 'stylex.css';
|
|
55
|
+
}
|
|
56
|
+
function replaceBundleReferences(bundle, oldFileName, newFileName) {
|
|
57
|
+
for (const item of Object.values(bundle)) {
|
|
58
|
+
if (!item) continue;
|
|
59
|
+
if (item.type === 'chunk') {
|
|
60
|
+
if (typeof item.code === 'string' && item.code.includes(oldFileName)) {
|
|
61
|
+
item.code = item.code.split(oldFileName).join(newFileName);
|
|
62
|
+
}
|
|
63
|
+
const importedCss = item.viteMetadata?.importedCss;
|
|
64
|
+
if (importedCss instanceof Set && importedCss.has(oldFileName)) {
|
|
65
|
+
importedCss.delete(oldFileName);
|
|
66
|
+
importedCss.add(newFileName);
|
|
67
|
+
} else if (Array.isArray(importedCss)) {
|
|
68
|
+
const next = importedCss.map(name => name === oldFileName ? newFileName : name);
|
|
69
|
+
item.viteMetadata.importedCss = next;
|
|
70
|
+
}
|
|
71
|
+
} else if (item.type === 'asset' && typeof item.source === 'string') {
|
|
72
|
+
if (item.source.includes(oldFileName)) {
|
|
73
|
+
item.source = item.source.split(oldFileName).join(newFileName);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function replaceCssAssetWithHashedCopy(ctx, bundle, asset, nextSource) {
|
|
79
|
+
const baseName = getAssetBaseName(asset);
|
|
80
|
+
const referenceId = ctx.emitFile({
|
|
81
|
+
type: 'asset',
|
|
82
|
+
name: baseName,
|
|
83
|
+
source: nextSource
|
|
84
|
+
});
|
|
85
|
+
const nextFileName = ctx.getFileName(referenceId);
|
|
86
|
+
const oldFileName = asset.fileName;
|
|
87
|
+
if (!nextFileName || !oldFileName || nextFileName === oldFileName) {
|
|
88
|
+
asset.source = nextSource;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
replaceBundleReferences(bundle, oldFileName, nextFileName);
|
|
92
|
+
const emitted = bundle[nextFileName];
|
|
93
|
+
if (emitted && emitted !== asset) {
|
|
94
|
+
delete bundle[nextFileName];
|
|
95
|
+
}
|
|
96
|
+
asset.fileName = nextFileName;
|
|
97
|
+
asset.source = nextSource;
|
|
98
|
+
if (bundle[oldFileName] === asset) {
|
|
99
|
+
delete bundle[oldFileName];
|
|
100
|
+
}
|
|
101
|
+
bundle[nextFileName] = asset;
|
|
102
|
+
}
|
|
103
|
+
function readJSON(file) {
|
|
104
|
+
try {
|
|
105
|
+
const content = _nodeFs.default.readFileSync(file, 'utf8');
|
|
106
|
+
return JSON.parse(content);
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function findNearestPackageJson(startDir) {
|
|
112
|
+
let dir = startDir;
|
|
113
|
+
for (;;) {
|
|
114
|
+
const candidate = _nodePath.default.join(dir, 'package.json');
|
|
115
|
+
if (_nodeFs.default.existsSync(candidate)) return candidate;
|
|
116
|
+
const parent = _nodePath.default.dirname(dir);
|
|
117
|
+
if (parent === dir) break;
|
|
118
|
+
dir = parent;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function toPackageName(importSource) {
|
|
123
|
+
const source = typeof importSource === 'string' ? importSource : importSource?.from;
|
|
124
|
+
if (!source || source.startsWith('.') || source.startsWith('/')) return null;
|
|
125
|
+
if (source.startsWith('@')) {
|
|
126
|
+
const [scope, name] = source.split('/');
|
|
127
|
+
if (scope && name) return `${scope}/${name}`;
|
|
128
|
+
}
|
|
129
|
+
const [pkg] = source.split('/');
|
|
130
|
+
return pkg || null;
|
|
131
|
+
}
|
|
132
|
+
function hasStylexDependency(manifest, targetPackages) {
|
|
133
|
+
if (!manifest || typeof manifest !== 'object') return false;
|
|
134
|
+
const depFields = ['dependencies', 'peerDependencies', 'optionalDependencies'];
|
|
135
|
+
for (const field of depFields) {
|
|
136
|
+
const deps = manifest[field];
|
|
137
|
+
if (!deps || typeof deps !== 'object') continue;
|
|
138
|
+
for (const name of Object.keys(deps)) {
|
|
139
|
+
if (targetPackages.has(name)) return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
function discoverStylexPackages({
|
|
145
|
+
importSources,
|
|
146
|
+
explicitPackages,
|
|
147
|
+
rootDir,
|
|
148
|
+
resolver
|
|
149
|
+
}) {
|
|
150
|
+
const targetPackages = new Set(importSources.map(toPackageName).filter(Boolean).concat(['@stylexjs/stylex']));
|
|
151
|
+
const found = new Set(explicitPackages || []);
|
|
152
|
+
const pkgJsonPath = findNearestPackageJson(rootDir);
|
|
153
|
+
if (!pkgJsonPath) return Array.from(found);
|
|
154
|
+
const pkgDir = _nodePath.default.dirname(pkgJsonPath);
|
|
155
|
+
const pkgJson = readJSON(pkgJsonPath);
|
|
156
|
+
if (!pkgJson) return Array.from(found);
|
|
157
|
+
const depFields = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
158
|
+
const deps = new Set();
|
|
159
|
+
for (const field of depFields) {
|
|
160
|
+
const entries = pkgJson[field];
|
|
161
|
+
if (!entries || typeof entries !== 'object') continue;
|
|
162
|
+
for (const name of Object.keys(entries)) deps.add(name);
|
|
163
|
+
}
|
|
164
|
+
for (const dep of deps) {
|
|
165
|
+
let manifestPath = null;
|
|
166
|
+
try {
|
|
167
|
+
manifestPath = resolver.resolve(`${dep}/package.json`, {
|
|
168
|
+
paths: [pkgDir]
|
|
169
|
+
});
|
|
170
|
+
} catch {}
|
|
171
|
+
if (!manifestPath) continue;
|
|
172
|
+
const manifest = readJSON(manifestPath);
|
|
173
|
+
if (hasStylexDependency(manifest, targetPackages)) {
|
|
174
|
+
found.add(dep);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return Array.from(found);
|
|
178
|
+
}
|
|
179
|
+
const unpluginFactory = (userOptions = {}, metaOptions) => {
|
|
180
|
+
const framework = metaOptions?.framework;
|
|
181
|
+
const {
|
|
182
|
+
dev = process.env.NODE_ENV === 'development' || process.env.BABEL_ENV === 'development',
|
|
183
|
+
unstable_moduleResolution = {
|
|
184
|
+
type: 'commonJS',
|
|
185
|
+
rootDir: process.cwd()
|
|
186
|
+
},
|
|
187
|
+
babelConfig: {
|
|
188
|
+
plugins = [],
|
|
189
|
+
presets = []
|
|
190
|
+
} = {},
|
|
191
|
+
importSources = ['stylex', '@stylexjs/stylex'],
|
|
192
|
+
useCSSLayers = false,
|
|
193
|
+
lightningcssOptions,
|
|
194
|
+
cssInjectionTarget,
|
|
195
|
+
externalPackages = [],
|
|
196
|
+
devPersistToDisk = false,
|
|
197
|
+
devMode = 'full',
|
|
198
|
+
treeshakeCompensation = ['vite', 'rollup', 'rolldown'].includes(framework),
|
|
199
|
+
...stylexOptions
|
|
200
|
+
} = userOptions;
|
|
201
|
+
const stylexRulesById = new Map();
|
|
202
|
+
function getSharedStore() {
|
|
203
|
+
try {
|
|
204
|
+
const g = globalThis;
|
|
205
|
+
if (!g.__stylex_unplugin_store) {
|
|
206
|
+
g.__stylex_unplugin_store = {
|
|
207
|
+
rulesById: new Map(),
|
|
208
|
+
version: 0
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return g.__stylex_unplugin_store;
|
|
212
|
+
} catch {
|
|
213
|
+
return {
|
|
214
|
+
rulesById: stylexRulesById,
|
|
215
|
+
version: 0
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const nearestPkgJson = findNearestPackageJson(process.cwd());
|
|
220
|
+
const requireFromCwd = nearestPkgJson ? (0, _nodeModule.createRequire)(nearestPkgJson) : (0, _nodeModule.createRequire)(_nodePath.default.join(process.cwd(), 'package.json'));
|
|
221
|
+
const stylexPackages = discoverStylexPackages({
|
|
222
|
+
importSources,
|
|
223
|
+
explicitPackages: externalPackages,
|
|
224
|
+
rootDir: nearestPkgJson ? _nodePath.default.dirname(nearestPkgJson) : process.cwd(),
|
|
225
|
+
resolver: requireFromCwd
|
|
226
|
+
});
|
|
227
|
+
function findNearestNodeModules(startDir) {
|
|
228
|
+
let dir = startDir;
|
|
229
|
+
for (;;) {
|
|
230
|
+
const candidate = _nodePath.default.join(dir, 'node_modules');
|
|
231
|
+
if (_nodeFs.default.existsSync(candidate)) {
|
|
232
|
+
const stat = _nodeFs.default.statSync(candidate);
|
|
233
|
+
if (stat.isDirectory()) return candidate;
|
|
234
|
+
}
|
|
235
|
+
const parent = _nodePath.default.dirname(dir);
|
|
236
|
+
if (parent === dir) break;
|
|
237
|
+
dir = parent;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const NEAREST_NODE_MODULES = findNearestNodeModules(process.cwd());
|
|
242
|
+
const DISK_RULES_DIR = NEAREST_NODE_MODULES ? _nodePath.default.join(NEAREST_NODE_MODULES, '.stylex') : _nodePath.default.join(process.cwd(), 'node_modules', '.stylex');
|
|
243
|
+
const DISK_RULES_PATH = _nodePath.default.join(DISK_RULES_DIR, 'rules.json');
|
|
244
|
+
async function runBabelTransform(inputCode, filename, callerName) {
|
|
245
|
+
const result = await (0, _core.transformAsync)(inputCode, {
|
|
246
|
+
babelrc: false,
|
|
247
|
+
filename,
|
|
248
|
+
presets,
|
|
249
|
+
plugins: [...plugins, /\.jsx?/.test(_nodePath.default.extname(filename)) ? _pluginSyntaxFlow.default : [_pluginSyntaxTypescript.default, {
|
|
250
|
+
isTSX: true
|
|
251
|
+
}], _pluginSyntaxJsx.default, _babelPlugin.default.withOptions({
|
|
252
|
+
...stylexOptions,
|
|
253
|
+
importSources,
|
|
254
|
+
treeshakeCompensation,
|
|
255
|
+
dev,
|
|
256
|
+
unstable_moduleResolution
|
|
257
|
+
})],
|
|
258
|
+
caller: {
|
|
259
|
+
name: callerName,
|
|
260
|
+
supportsStaticESM: true,
|
|
261
|
+
supportsDynamicImport: true,
|
|
262
|
+
supportsTopLevelAwait: !inputCode.includes('require('),
|
|
263
|
+
supportsExportNamespaceFrom: true
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
if (!result || result.code == null) {
|
|
267
|
+
return {
|
|
268
|
+
code: inputCode,
|
|
269
|
+
map: null,
|
|
270
|
+
metadata: {}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
function escapeReg(src) {
|
|
276
|
+
return src.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
277
|
+
}
|
|
278
|
+
function containsStylexImport(code, source) {
|
|
279
|
+
const s = escapeReg(typeof source === 'string' ? source : source.from);
|
|
280
|
+
const re = new RegExp(`(?:from\\s*['"]${s}['"]|import\\s*\\(\\s*['"]${s}['"]\\s*\\)|require\\s*\\(\\s*['"]${s}['"]\\s*\\)|^\\s*import\\s*['"]${s}['"])`, 'm');
|
|
281
|
+
return re.test(code);
|
|
282
|
+
}
|
|
283
|
+
function shouldHandle(code) {
|
|
284
|
+
if (!code) return false;
|
|
285
|
+
return importSources.some(src => containsStylexImport(code, src));
|
|
286
|
+
}
|
|
287
|
+
function resetState() {
|
|
288
|
+
stylexRulesById.clear();
|
|
289
|
+
if (devPersistToDisk) {
|
|
290
|
+
try {
|
|
291
|
+
_nodeFs.default.rmSync(DISK_RULES_PATH, {
|
|
292
|
+
force: true
|
|
293
|
+
});
|
|
294
|
+
} catch {}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function collectCss() {
|
|
298
|
+
const merged = new Map();
|
|
299
|
+
if (devPersistToDisk) {
|
|
300
|
+
try {
|
|
301
|
+
if (_nodeFs.default.existsSync(DISK_RULES_PATH)) {
|
|
302
|
+
const json = JSON.parse(_nodeFs.default.readFileSync(DISK_RULES_PATH, 'utf8'));
|
|
303
|
+
for (const [k, v] of Object.entries(json)) merged.set(k, v);
|
|
304
|
+
}
|
|
305
|
+
} catch {}
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const shared = getSharedStore().rulesById;
|
|
309
|
+
for (const [k, v] of shared.entries()) merged.set(k, v);
|
|
310
|
+
} catch {}
|
|
311
|
+
for (const [k, v] of stylexRulesById.entries()) merged.set(k, v);
|
|
312
|
+
const allRules = Array.from(merged.values()).flat();
|
|
313
|
+
return processCollectedRulesToCSS(allRules, {
|
|
314
|
+
useCSSLayers,
|
|
315
|
+
lightningcssOptions,
|
|
316
|
+
enableLTRRTLComments: stylexOptions?.enableLTRRTLComments
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async function persistRulesToDisk(id, rules) {
|
|
320
|
+
if (!devPersistToDisk) return;
|
|
321
|
+
try {
|
|
322
|
+
let current = {};
|
|
323
|
+
try {
|
|
324
|
+
const txt = await _promises.default.readFile(DISK_RULES_PATH, 'utf8');
|
|
325
|
+
current = JSON.parse(txt);
|
|
326
|
+
} catch {}
|
|
327
|
+
if (rules && Array.isArray(rules) && rules.length > 0) {
|
|
328
|
+
current[id] = rules;
|
|
329
|
+
} else if (current[id]) {
|
|
330
|
+
delete current[id];
|
|
331
|
+
}
|
|
332
|
+
await _promises.default.writeFile(DISK_RULES_PATH, JSON.stringify(current), 'utf8');
|
|
333
|
+
} catch {}
|
|
334
|
+
}
|
|
335
|
+
const plugin = {
|
|
336
|
+
name: '@stylexjs/unplugin',
|
|
337
|
+
apply: (config, env) => {
|
|
338
|
+
try {
|
|
339
|
+
const command = env?.command || (typeof config === 'string' ? undefined : undefined);
|
|
340
|
+
if (devMode === 'off' && command === 'serve') return false;
|
|
341
|
+
} catch {}
|
|
342
|
+
return true;
|
|
343
|
+
},
|
|
344
|
+
enforce: 'pre',
|
|
345
|
+
buildStart() {
|
|
346
|
+
resetState();
|
|
347
|
+
},
|
|
348
|
+
buildEnd() {},
|
|
349
|
+
async transform(code, id) {
|
|
350
|
+
const JS_LIKE_RE = /\.[cm]?[jt]sx?(\?|$)/;
|
|
351
|
+
if (!JS_LIKE_RE.test(id)) return null;
|
|
352
|
+
if (!shouldHandle(code)) return null;
|
|
353
|
+
const dir = _nodePath.default.dirname(id);
|
|
354
|
+
const basename = _nodePath.default.basename(id);
|
|
355
|
+
const file = _nodePath.default.join(dir, basename.split('?')[0] || basename);
|
|
356
|
+
const result = await runBabelTransform(code, file, '@stylexjs/unplugin');
|
|
357
|
+
const {
|
|
358
|
+
metadata
|
|
359
|
+
} = result;
|
|
360
|
+
if (!stylexOptions.runtimeInjection) {
|
|
361
|
+
const hasRules = metadata && Array.isArray(metadata.stylex) && metadata.stylex.length > 0;
|
|
362
|
+
const shared = getSharedStore();
|
|
363
|
+
if (hasRules) {
|
|
364
|
+
stylexRulesById.set(id, metadata.stylex);
|
|
365
|
+
shared.rulesById.set(id, metadata.stylex);
|
|
366
|
+
shared.version++;
|
|
367
|
+
await persistRulesToDisk(id, metadata.stylex);
|
|
368
|
+
} else {
|
|
369
|
+
stylexRulesById.delete(id);
|
|
370
|
+
if (shared.rulesById.has(id)) {
|
|
371
|
+
shared.rulesById.delete(id);
|
|
372
|
+
shared.version++;
|
|
373
|
+
}
|
|
374
|
+
await persistRulesToDisk(id, []);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const ctx = this;
|
|
378
|
+
if (ctx && ctx.meta && ctx.meta.watchMode && typeof ctx.parse === 'function') {
|
|
379
|
+
try {
|
|
380
|
+
const ast = ctx.parse(result.code);
|
|
381
|
+
for (const stmt of ast.body) {
|
|
382
|
+
if (stmt.type === 'ImportDeclaration') {
|
|
383
|
+
const resolved = await ctx.resolve(stmt.source.value, id);
|
|
384
|
+
if (resolved && !resolved.external) {
|
|
385
|
+
const loaded = await ctx.load(resolved);
|
|
386
|
+
if (loaded && loaded.meta && 'stylex' in loaded.meta) {
|
|
387
|
+
stylexRulesById.set(resolved.id, loaded.meta.stylex);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
} catch {}
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
code: result.code,
|
|
396
|
+
map: result.map
|
|
397
|
+
};
|
|
398
|
+
},
|
|
399
|
+
shouldTransformCachedModule({
|
|
400
|
+
id,
|
|
401
|
+
meta
|
|
402
|
+
}) {
|
|
403
|
+
if (meta && 'stylex' in meta) {
|
|
404
|
+
stylexRulesById.set(id, meta.stylex);
|
|
405
|
+
}
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
plugin.__stylexCollectCss = collectCss;
|
|
410
|
+
plugin.__stylexResetState = resetState;
|
|
411
|
+
plugin.__stylexGetSharedStore = getSharedStore;
|
|
412
|
+
plugin.__stylexDevMode = devMode;
|
|
413
|
+
plugin.__stylexCssInjectionTarget = cssInjectionTarget;
|
|
414
|
+
plugin.__stylexPackages = stylexPackages;
|
|
415
|
+
return plugin;
|
|
416
|
+
};
|
|
417
|
+
exports.unpluginFactory = unpluginFactory;
|
|
418
|
+
const unpluginInstance = (0, _unplugin.createUnplugin)(unpluginFactory);
|
|
419
|
+
var _default = exports.default = unpluginInstance;
|
|
420
|
+
const unplugin = exports.unplugin = unpluginInstance;
|