@stylable/core
Version:
CSS for Components
369 lines • 16 kB
JavaScript
"use strict";
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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__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.scopeCSSVar = exports.getTransformedName = exports.getRuntimeTypedDefinitionNames = exports.StylablePublicApi = exports.addCSSProperty = exports.get = exports.transformPropertyIdent = exports.hooks = exports.diagnostics = void 0;
const feature_1 = require("./feature");
const STSymbol = __importStar(require("./st-symbol"));
const css_custom_property_1 = require("../helpers/css-custom-property");
const value_1 = require("../helpers/value");
const global_1 = require("../helpers/global");
const plugable_record_1 = require("../helpers/plugable-record");
const diagnostics_1 = require("../diagnostics");
const stylable_resolver_1 = require("../stylable-resolver");
// ToDo: refactor out - parse once and pass to hooks
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
exports.diagnostics = {
...css_custom_property_1.atPropertyValidationWarnings,
ILLEGAL_CSS_VAR_USE: (0, diagnostics_1.createDiagnosticReporter)('01005', 'error', (name) => `a custom css property must begin with "--" (double-dash), but received "${name}"`),
ILLEGAL_CSS_VAR_ARGS: (0, diagnostics_1.createDiagnosticReporter)('01006', 'error', (name) => `custom property "${name}" usage (var()) must receive comma separated values`),
DEPRECATED_ST_GLOBAL_CUSTOM_PROPERTY: (0, diagnostics_1.createDiagnosticReporter)('01007', 'info', () => `"st-global-custom-property" is deprecated and will be removed in the next version. Use "@property" with ${global_1.GLOBAL_FUNC}`),
GLOBAL_CSS_VAR_MISSING_COMMA: (0, diagnostics_1.createDiagnosticReporter)('01008', 'error', (name) => `"@st-global-custom-property" received the value "${name}", but its values must be comma separated`),
ILLEGAL_GLOBAL_CSS_VAR: (0, diagnostics_1.createDiagnosticReporter)('01009', 'error', (name) => `"@st-global-custom-property" received the value "${name}", but it must begin with "--" (double-dash)`),
MISSING_PROP_NAME: (0, diagnostics_1.createDiagnosticReporter)('01010', 'error', () => `missing custom property name for "var(--[PROP NAME])"`),
UNDEFINED_CSS_CUSTOM_PROP: (0, diagnostics_1.createDiagnosticReporter)('01011', 'error', (name) => `Undefined "${name}" custom property. Please define the property using '@property' or import it with '@st-import' when 'strictCustomProperty' is enabled.`),
};
const dataKey = plugable_record_1.plugableRecord.key('custom-property');
exports.hooks = (0, feature_1.createFeature)({
metaInit({ meta }) {
plugable_record_1.plugableRecord.set(meta.data, dataKey, {
stCustomGlobalProperty: {},
typedDefinitions: {},
});
},
analyzeInit(context) {
// ToDo: move to `STImport.ImportTypeHook`
for (const [symbolName, symbol] of Object.entries(STSymbol.getAllByType(context.meta, `import`))) {
if ((0, css_custom_property_1.validateCustomPropertyName)(symbolName)) {
const importSymbol = STSymbol.get(context.meta, symbolName, `import`);
if (!importSymbol) {
console.warn(`imported symbol "${symbolName}" not found on "${context.meta.source}"`);
continue;
}
addCSSProperty({
context,
node: symbol.import.rule,
name: symbolName,
global: false,
final: true,
alias: importSymbol,
});
}
}
},
analyzeAtRule({ context, atRule }) {
const isStylable = context.meta.type === 'stylable';
if (atRule.name === `property`) {
let name = atRule.params;
let global = !isStylable;
// check global
const globalVarName = isStylable ? (0, global_1.globalValue)(name) : undefined;
if (globalVarName !== undefined) {
name = globalVarName.trim();
global = true;
}
const { stCustomGlobalProperty, typedDefinitions } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
// handle conflict with deprecated `@st-global-custom-property`
if (stCustomGlobalProperty[name]) {
global = true;
}
addCSSProperty({
context,
node: atRule,
name,
global,
final: true,
});
(0, css_custom_property_1.validateAtProperty)(atRule, context.diagnostics);
// save reference to runtime definitions
if (atRule.nodes) {
typedDefinitions[name] ?? (typedDefinitions[name] = []);
typedDefinitions[name].push(atRule);
}
}
else if (atRule.name === `st-global-custom-property` && isStylable) {
analyzeDeprecatedStGlobalCustomProperty(context, atRule);
}
},
analyzeDeclaration({ context, decl }) {
// register prop
if ((0, css_custom_property_1.validateCustomPropertyName)(decl.prop)) {
addCSSProperty({
context,
node: decl,
name: decl.prop,
global: context.meta.type === 'css',
final: false,
});
}
// register value
if (decl.value.includes('var(')) {
analyzeDeclValueVarCalls(context, decl);
}
},
prepareAST({ context, node, toRemove }) {
if (node.type === `atrule` &&
node.name === 'st-global-custom-property' &&
context.meta.type === 'stylable') {
toRemove.push(node);
}
},
transformResolve({ context: { meta, getResolvedSymbols } }) {
const customPropsMapping = {
localToGlobal: {},
locals: new Set(),
};
const resolvedSymbols = getResolvedSymbols(meta);
for (const [localVarName, localSymbol] of Object.entries(STSymbol.getAllByType(meta, `cssVar`))) {
const resolve = resolveFinalSymbol(meta, localSymbol, resolvedSymbols);
customPropsMapping.localToGlobal[localVarName] = getTransformedName(resolve);
if (resolve.meta === meta) {
customPropsMapping.locals.add(localVarName);
}
}
return customPropsMapping;
},
transformAtRuleNode({ context, atRule, resolved }) {
if (atRule.name !== `property`) {
return;
}
if (atRule.nodes?.length) {
const propName = (0, global_1.globalValue)(atRule.params) || atRule.params;
if (resolved.localToGlobal[propName]) {
atRule.params = resolved.localToGlobal[propName] || atRule.params;
}
}
else if (context.meta.type === 'stylable') {
// remove `@property` with no body
atRule.remove();
}
},
transformDeclaration({ decl, resolved }) {
decl.prop = resolved.localToGlobal[decl.prop] || decl.prop;
},
transformValue({ node, data: { meta }, context: { getResolvedSymbols } }) {
const { value } = node;
const varWithPrefix = node.nodes[0]?.value || ``;
if ((0, css_custom_property_1.validateCustomPropertyName)(varWithPrefix)) {
transformPropertyIdent(meta, node.nodes[0], getResolvedSymbols);
}
// handle default values - ToDo: check if required
if (node.nodes.length > 2) {
node.resolvedValue = (0, value_1.stringifyFunction)(value, node);
}
},
transformJSExports({ exports, resolved }) {
for (const varName of resolved.locals) {
exports.vars[varName.slice(2)] = resolved.localToGlobal[varName];
}
},
});
// API
function transformPropertyIdent(meta, node, getResolvedSymbols) {
const varWithPrefix = node.value || '';
const resolvedSymbols = getResolvedSymbols(meta);
const localSymbol = STSymbol.get(meta, varWithPrefix, `cssVar`);
if (localSymbol) {
node.value = getTransformedName(resolveFinalSymbol(meta, localSymbol, resolvedSymbols));
}
}
exports.transformPropertyIdent = transformPropertyIdent;
function get(meta, name) {
return STSymbol.get(meta, name, `cssVar`);
}
exports.get = get;
function resolveFinalSymbol(meta, localSymbol, resolvedSymbols) {
return (resolvedSymbols.cssVar[localSymbol.name] || {
// fallback to local namespace
_kind: `css`,
symbol: localSymbol,
meta,
});
}
function addCSSProperty({ context, node, name, global, final, alias, }) {
// validate indent
if (!(0, css_custom_property_1.validateCustomPropertyName)(name)) {
context.diagnostics.report(exports.diagnostics.ILLEGAL_CSS_VAR_USE(name), {
node,
word: name,
});
return;
}
// usages bailout: addition of weak definition reference `--x: var(--x)`
if (!final) {
const existing = STSymbol.get(context.meta, name, `cssVar`);
if (existing) {
// already defined
return;
// eslint-disable-next-line no-constant-condition
}
else if (context.meta.type === 'stylable' && context.meta.flags.strictCustomProperty) {
// strict mode
context.diagnostics.report(exports.diagnostics.UNDEFINED_CSS_CUSTOM_PROP(name), {
node,
word: name,
});
}
}
// define symbol
STSymbol.addSymbol({
context,
symbol: {
_kind: 'cssVar',
name: name,
global,
alias,
},
safeRedeclare: !final || !!alias,
node,
});
}
exports.addCSSProperty = addCSSProperty;
const UNKNOWN_LOCATION = Object.freeze({
offset: -1,
line: -1,
column: -1,
});
class StylablePublicApi {
constructor(stylable) {
this.stylable = stylable;
}
getProperties(meta) {
const results = {};
const topLevelDiagnostics = new diagnostics_1.Diagnostics();
const getResolvedSymbols = (0, stylable_resolver_1.createSymbolResolverWithCache)(this.stylable.resolver, topLevelDiagnostics);
const { cssVar } = getResolvedSymbols(meta);
for (const [name, symbol] of Object.entries(cssVar)) {
const defAst = STSymbol.getSymbolAstNode(symbol.meta, symbol.symbol);
results[name] = {
meta: symbol.meta,
localName: symbol.symbol.name,
targetName: getTransformedName(symbol),
source: {
meta: symbol.meta,
start: defAst?.source?.start || UNKNOWN_LOCATION,
end: defAst?.source?.end || UNKNOWN_LOCATION,
},
};
}
return results;
}
}
exports.StylablePublicApi = StylablePublicApi;
function analyzeDeclValueVarCalls(context, decl) {
const parsed = (0, postcss_value_parser_1.default)(decl.value);
parsed.walk((node) => {
if (node.type === 'function' && node.value === 'var' && node.nodes) {
const varName = node.nodes[0];
if (!varName) {
context.diagnostics.report(exports.diagnostics.MISSING_PROP_NAME(), {
node: decl,
});
return;
}
if (!(0, value_1.validateAllowedNodesUntil)(node, 1)) {
const args = postcss_value_parser_1.default.stringify(node.nodes);
context.diagnostics.report(exports.diagnostics.ILLEGAL_CSS_VAR_ARGS(args), {
node: decl,
word: args,
});
}
addCSSProperty({
context,
name: postcss_value_parser_1.default.stringify(varName)?.trim() || ``,
node: decl,
global: context.meta.type === 'css',
final: false,
});
}
});
}
function analyzeDeprecatedStGlobalCustomProperty(context, atRule) {
// report deprecation
context.diagnostics.report(exports.diagnostics.DEPRECATED_ST_GLOBAL_CUSTOM_PROPERTY(), {
node: atRule,
});
//
const cssVarsByComma = atRule.params.split(',');
const cssVarsBySpacing = atRule.params
.trim()
.split(/\s+/g)
.filter((s) => s !== ',');
if (cssVarsBySpacing.length > cssVarsByComma.length) {
context.diagnostics.report(exports.diagnostics.GLOBAL_CSS_VAR_MISSING_COMMA(atRule.params), {
node: atRule,
word: atRule.params,
});
return;
}
for (const entry of cssVarsByComma) {
const name = entry.trim();
if ((0, css_custom_property_1.validateCustomPropertyName)(name)) {
// ToDo: change to modify global instead of override
addCSSProperty({
context,
node: atRule,
name,
global: true,
final: true,
});
// keep track of defined props through `@st-custom-global-property` in order
// to not override the default with following `@property` definitions
const { stCustomGlobalProperty } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
stCustomGlobalProperty[name] = STSymbol.get(context.meta, name, `cssVar`);
}
else {
context.diagnostics.report(exports.diagnostics.ILLEGAL_GLOBAL_CSS_VAR(name), {
node: atRule,
word: name,
});
}
}
}
function getRuntimeTypedDefinitionNames(meta) {
const { typedDefinitions } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
return Object.keys(typedDefinitions);
}
exports.getRuntimeTypedDefinitionNames = getRuntimeTypedDefinitionNames;
function getTransformedName({ symbol, meta }) {
return symbol.global ? symbol.name : (0, css_custom_property_1.generateScopedCSSVar)(meta.namespace, symbol.name.slice(2));
}
exports.getTransformedName = getTransformedName;
function scopeCSSVar(resolver, meta, symbolName) {
const importedVar = resolver.deepResolve(STSymbol.get(meta, symbolName));
if (importedVar &&
importedVar._kind === 'css' &&
importedVar.symbol &&
importedVar.symbol._kind === 'cssVar') {
importedVar;
return getTransformedName(importedVar);
}
const cssVar = STSymbol.get(meta, symbolName, `cssVar`);
return cssVar?.global ? symbolName : (0, css_custom_property_1.generateScopedCSSVar)(meta.namespace, symbolName.slice(2));
}
exports.scopeCSSVar = scopeCSSVar;
//# sourceMappingURL=css-custom-property.js.map