@stylexswc/rs-compiler
Version:
NAPI-RS compiler for transform StyleX code
178 lines (177 loc) • 7.11 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertyValidationMode = exports.SourceMaps = void 0;
exports.normalizeRsOptions = normalizeRsOptions;
exports.shouldTransformFile = shouldTransformFile;
exports.transform = transform;
const picomatch_1 = __importDefault(require("picomatch"));
const path = __importStar(require("path"));
const transform_1 = __importDefault(require("../dist/transform"));
// const enums are erased by TypeScript — provide runtime values
// so ESM consumers can import them.
exports.SourceMaps = Object.freeze({
True: 'True',
False: 'False',
Inline: 'Inline',
});
exports.PropertyValidationMode = Object.freeze({
Throw: 'throw',
Warn: 'warn',
Silent: 'silent',
});
/**
* Default values for StyleX options.
* Every field that has a sensible default is listed here.
*/
const defaultOptions = {
dev: false,
test: false,
debug: false,
enableFontSizePxToRem: false,
runtimeInjection: false,
treeshakeCompensation: false,
enableInlinedConditionalMerge: true,
enableLogicalStylesPolyfill: false,
enableMinifiedKeys: true,
enableLegacyValueFlipping: false,
enableLTRRTLComments: false,
legacyDisableLayers: false,
useRealFileForSource: true,
enableMediaQueryOrder: true,
enableDebugClassNames: false,
propertyValidationMode: 'silent',
styleResolution: 'property-specificity',
importSources: ['stylex', '@stylexjs/stylex'],
};
// ── normalizeRsOptions ──────────────────────────────────────────────
/** Strip keys whose value is `undefined` so they don't clobber defaults. */
function definedEntries(obj) {
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
}
/**
* Normalize StyleX compiler options by applying defaults and merging
* user-provided values. Uses a spread/defaults pattern: defaults are
* applied first, then user-provided values overlay them
* (undefined keys skipped).
*/
function normalizeRsOptions(options) {
if (options == null) {
throw new TypeError('Options must be an object, received null/undefined');
}
// Non-object input (string, number, etc.) — treat as empty options
const inputOptions = typeof options === 'object' ? options : {};
const definedOptions = definedEntries(inputOptions);
// Spread defaults then user values (undefined keys already stripped)
const result = {
...defaultOptions,
...definedOptions,
include: definedOptions.include ?? [],
exclude: definedOptions.exclude ?? [],
swcPlugins: definedOptions.swcPlugins ?? [],
};
return result;
}
// ── shouldTransformFile ─────────────────────────────────────────────
/**
* Determine whether a file should be transformed based on include/exclude
* patterns (glob strings or RegExp).
*/
function shouldTransformFile(filePath, include, exclude) {
const relativePath = path.relative(process.cwd(), filePath).split(path.sep).join('/');
if (include && include.length > 0) {
if (!include.some(p => matchPattern(relativePath, p))) {
return false;
}
}
if (exclude && exclude.length > 0) {
if (exclude.some(p => matchPattern(relativePath, p))) {
return false;
}
}
return true;
}
/** Match a file path against a single pattern (glob string or RegExp). */
function matchPattern(filePath, pattern) {
if (pattern instanceof RegExp) {
// Reset lastIndex to avoid nondeterministic results with /g or /y flags
pattern.lastIndex = 0;
return pattern.test(filePath);
}
if (typeof pattern !== 'string' || pattern === '') {
return false;
}
return picomatch_1.default.isMatch(filePath, pattern, { dot: true });
}
// ── transform ───────────────────────────────────────────────────────
/**
* Transform source code with StyleX. When `options.swcPlugins` is set,
* SWC plugins are applied first, then the native StyleX transform runs.
*/
function transform(filename, code, options) {
// Apply include/exclude filter before transforming
if (!shouldTransformFile(filename, options.include, options.exclude)) {
return {
code,
metadata: { stylex: [] },
map: undefined,
};
}
let transformedCode = code;
if (options.swcPlugins?.length) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const swc = require('@swc/core');
const result = swc.transformSync(transformedCode, {
filename,
sourceMaps: options.sourceMap === 'Inline'
? 'inline'
: options.sourceMap === 'False'
? false
: options.sourceMap !== undefined,
jsc: {
parser: { syntax: 'typescript', tsx: true },
target: 'es2022',
experimental: { plugins: options.swcPlugins },
},
});
transformedCode = result.code;
}
// Strip TS-only fields before passing to native transform
const { swcPlugins: _swcPlugins, include: _include, exclude: _exclude, ...nativeOptions } = options;
return transform_1.default.transform(filename, transformedCode, nativeOptions);
}