@stylable/core
Version:
CSS for Components
153 lines (138 loc) • 4.28 kB
text/typescript
import type { StylableExports } from './stylable-transformer';
interface InjectCSSOptions {
/**
* omitting the id will fallback to module.id/import.meta.url
*/
id?: string | undefined;
/**
* css string to inject
*/
css: string;
/**
* code to generate the css string to inject
*/
cssCode?: string;
/**
* calculated style depth
*/
depth: number | string;
/**
* use code to get the depth
*/
depthCode?: string;
/**
* reconciliation will happen only for style with the same runtimeId
*/
runtimeId: string;
}
interface ModuleOptions {
/**
* module namespace
*/
namespace: string;
/**
* static imports for the module
*/
imports?: Array<{ from: string; defaultImport?: string }>;
/**
* Stylable transforms exports
*/
jsExports: StylableExports | { [K in keyof StylableExports]: string };
/**
* the request of the module runtime api e.g @stylable/runtime
*/
runtimeRequest?: string;
/**
* target module format
*/
moduleType: 'esm' | 'cjs';
/**
* es3 compat mode
*/
varType?: 'const' | 'var';
/**
* inject code immediately after imports
*/
header?: string;
/**
* inject code after the entire module code
*/
footer?: string;
}
export function generateStylableJSModuleSource(
moduleOptions: ModuleOptions,
injectOptions?: InjectCSSOptions
) {
const {
namespace,
imports = [],
jsExports,
moduleType,
runtimeRequest,
varType = 'const',
header = '',
footer = '',
} = moduleOptions;
const { classes, keyframes, layers, containers, stVars, vars } = jsExports;
const exportKind = moduleType === 'esm' ? `export ${varType} ` : 'module.exports.';
return `
${imports.map(moduleRequest(moduleType)).join('\n')}
${runtimeImport(moduleType, runtimeRequest, injectOptions)}
${header}
${varType} _namespace_ = ${JSON.stringify(namespace)};
${varType} _style_ = (...args) => classesRuntime(_namespace_, ...args);
${exportKind}cssStates = (stateMapping) => statesRuntime(_namespace_, stateMapping);
${exportKind}style = _style_;
${exportKind}st = _style_;
${exportKind}namespace = _namespace_;
${exportKind}classes = ${JSON.stringify(classes)};
${exportKind}keyframes = ${JSON.stringify(keyframes)};
${exportKind}layers = ${JSON.stringify(layers)};
${exportKind}containers = ${JSON.stringify(containers)};
${exportKind}stVars = ${JSON.stringify(stVars)};
${exportKind}vars = ${JSON.stringify(vars)};
${runtimeExecuteInject(moduleType, injectOptions)}
${footer}
`;
}
function moduleRequest(moduleType: 'esm' | 'cjs') {
return ({ from, defaultImport }: { from: string; defaultImport?: string }) => {
const request = JSON.stringify(from);
if (defaultImport) {
return moduleType === 'esm'
? `import ${defaultImport} from ${request};`
: `const ${defaultImport} = require(${request});`;
}
return moduleType === 'esm' ? `import ${request};` : `require(${request});`;
};
}
function runtimeImport(
moduleType: 'esm' | 'cjs',
runtimeRequest: string | undefined,
injectOptions: InjectCSSOptions | undefined
) {
const importInjectCSS = injectOptions?.css ? `, injectCSS` : '';
const request = JSON.stringify(runtimeRequest ?? '@stylable/runtime');
return moduleType === 'esm'
? `import { classesRuntime, statesRuntime${importInjectCSS} } from ${request};`
: `const { classesRuntime, statesRuntime${importInjectCSS} } = require(${request});`;
}
function runtimeExecuteInject(
moduleType: 'esm' | 'cjs',
injectOptions: InjectCSSOptions | undefined
) {
if (!injectOptions?.css) {
return '';
}
const { id, css, cssCode, depthCode, depth, runtimeId } = injectOptions;
let out = 'injectCSS(';
out += id ? JSON.stringify(id) : moduleType === 'esm' ? 'import.meta.url' : 'module.id';
out += ', ';
out += cssCode || JSON.stringify(css);
out += ', ';
out += depthCode || JSON.stringify(depth) || '-1';
out += ', ';
out += JSON.stringify(runtimeId);
out += ');';
return out;
}