stylex-webpack
Version:
The another Webpack Plugin for Facebook's StyleX
84 lines (78 loc) • 3.87 kB
JavaScript
;
var core = require('@babel/core');
var stylexBabelPlugin = require('@stylexjs/babel-plugin');
var loaderUtils = require('loader-utils');
function stringifyRequest(loaderContext, request) {
return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
}
const VIRTUAL_CSS_PATH = require.resolve('./stylex.virtual.css');
const isSupplementedLoaderContext = (context)=>{
// eslint-disable-next-line prefer-object-has-own -- target older
return Object.prototype.hasOwnProperty.call(context, 'StyleXWebpackContextKey');
};
const PLUGIN_NAME = 'stylex';
async function stylexLoader(inputCode, inputSourceMap) {
const callback = this.async();
const { stylexImports, stylexOption, nextjsMode } = this.getOptions();
// bail out early if the input doesn't contain stylex imports
if (!stylexImports.some((importName)=>inputCode.includes(importName))) {
return callback(null, inputCode, inputSourceMap);
}
if (!isSupplementedLoaderContext(this)) {
return callback(new Error('stylex-loader: loader context is not SupplementedLoaderContext!'));
}
try {
const { code, map, metadata } = await core.transformAsync(inputCode, {
babelrc: false,
inputSourceMap,
sourceFileName: this.resourcePath,
filename: this.resourcePath,
parserOpts: {
plugins: /\.tsx?$/.test(this.resourcePath) ? [
'typescript',
'jsx'
] : [
'jsx'
]
},
plugins: [
stylexBabelPlugin.withOptions(stylexOption)
]
});
const logger = this._compiler?.getInfrastructureLogger(PLUGIN_NAME);
// If metadata.stylex doesn't exist at all, we only need to return the transformed code
if (!metadata || !('stylex' in metadata) || metadata.stylex == null) {
logger?.debug(`No stylex styles generated from ${this.resourcePath}`);
return callback(null, code ?? undefined, map ?? undefined);
}
// this.stylexRules[filename] = metadata.stylex;
logger?.debug(`Read stylex styles from ${this.resourcePath}:`, metadata.stylex);
// TODO: rspack doesn't support custom loader context
// Find a better way to register stylex rules to the compiler instance
this.StyleXWebpackContextKey.registerStyleXRules(this.resourcePath, metadata.stylex);
// color: #fff is not url safe
const serializedStyleXRules = JSON.stringify(metadata.stylex);
const urlParams = new URLSearchParams({
from: this.resourcePath,
stylex: serializedStyleXRules
});
if (!nextjsMode) {
// Normal webpack mode
// We generate a virtual css file that looks like it is relative to the source
const virtualFileName = loaderUtils.interpolateName(this, '[path][name].[hash:base64:8].stylex.virtual.css', {
content: serializedStyleXRules
});
const virtualCssRequest = stringifyRequest(this, `${virtualFileName}!=!${VIRTUAL_CSS_PATH}?${urlParams.toString()}`);
const postfix = `\nimport ${virtualCssRequest};`;
return callback(null, code + postfix, map ?? undefined);
}
// Next.js App Router doesn't support inline matchResource and inline loaders
// So we adapt Next.js' "external" css import approach instead
const virtualCssRequest = stringifyRequest(this, `${VIRTUAL_CSS_PATH}?${urlParams.toString()}`);
const postfix = `\nimport ${virtualCssRequest};`;
return callback(null, code + postfix, map ?? undefined);
} catch (error) {
return callback(error);
}
}
module.exports = stylexLoader;