UNPKG

@stylable/core

Version:

CSS for Components

369 lines 16 kB
"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