Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@stylable/core

Package Overview
Dependencies
Maintainers
6
Versions
218
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stylable/core - npm Package Compare versions

Comparing version 5.8.0 to 5.9.0

dist/helpers/eql.d.ts

2

dist/create-stylable-processor.js

@@ -9,3 +9,3 @@ "use strict";

return (0, cached_process_file_1.cachedProcessFile)((from, content) => {
return new stylable_processor_1.StylableProcessor(createDiagnostics === null || createDiagnostics === void 0 ? void 0 : createDiagnostics(from), resolveNamespace).process(cssParser(content, { from }));
return new stylable_processor_1.StylableProcessor(createDiagnostics?.(from), resolveNamespace).process(cssParser(content, { from }));
}, (resolvedPath) => {

@@ -12,0 +12,0 @@ return fileSystem.readFileSync(resolvedPath, 'utf8');

@@ -34,5 +34,5 @@ "use strict";

else if (typeof boxed === 'object' && boxed) {
const customValue = customValues === null || customValues === void 0 ? void 0 : customValues[boxed.type];
const customValue = customValues?.[boxed.type];
let value = boxed.value;
if ((customValue === null || customValue === void 0 ? void 0 : customValue.flattenValue) && node) {
if (customValue?.flattenValue && node) {
value = customValue.getValue([], boxed, node, customValues);

@@ -194,5 +194,5 @@ }

function isCustomValue(symbol) {
return (symbol === null || symbol === void 0 ? void 0 : symbol._kind) === 'CustomValue';
return symbol?._kind === 'CustomValue';
}
exports.isCustomValue = isCustomValue;
//# sourceMappingURL=custom-values.js.map

@@ -9,6 +9,5 @@ "use strict";

report(diagnostic, context) {
var _a;
const node = context.node;
this.reports.push({
filePath: (_a = node.source) === null || _a === void 0 ? void 0 : _a.input.from,
filePath: node.source?.input.from,
...diagnostic,

@@ -15,0 +14,0 @@ ...context,

@@ -104,3 +104,3 @@ import { FeatureContext, FeatureTransformContext } from './feature';

export declare function namespaceClass(meta: StylableMeta, symbol: StylableSymbol, node: SelectorNode, // ToDo: check this is the correct type, should this be inline selector?
originMeta: StylableMeta): void;
wrapInGlobal?: boolean): void;
export declare function addDevRules({ getResolvedSymbols, meta }: FeatureTransformContext): void;

@@ -107,0 +107,0 @@ export declare function createWarningRule(extendedNode: string, scopedExtendedNode: string, extendedFile: string, extendingNode: string, scopedExtendingNode: string, extendingFile: string): postcss.Rule;

@@ -33,3 +33,2 @@ "use strict";

const STSymbol = __importStar(require("./st-symbol"));
const STGlobal = __importStar(require("./st-global"));
const STCustomState = __importStar(require("./st-custom-state"));

@@ -141,11 +140,10 @@ const resolve_1 = require("../helpers/resolve");

];
selectorContext.setCurrentAnchor({ name: node.value, type: 'class', resolved });
selectorContext.setNodeResolve(node, resolved);
selectorContext.setNextSelectorScope(resolved, node, node.value);
const { symbol, meta } = (0, resolve_1.getOriginDefinition)(resolved);
if (selectorContext.originMeta === meta && symbol[`-st-states`]) {
// ToDo: refactor out to transformer validation phase
(0, custom_state_1.validateRuleStateDefinition)(selectorContext.rule, context.meta, resolver, context.diagnostics);
(0, custom_state_1.validateRuleStateDefinition)(selectorContext.selectorStr, selectorContext.ruleOrAtRule, context.meta, resolver, context.diagnostics);
}
if (selectorContext.transform) {
namespaceClass(meta, symbol, node, originMeta);
namespaceClass(meta, symbol, node);
}

@@ -163,8 +161,7 @@ },

transformIntoSelector(meta, name) {
var _a;
const localSymbol = STSymbol.get(meta, name);
const resolved = (localSymbol === null || localSymbol === void 0 ? void 0 : localSymbol._kind) === 'import'
const resolved = localSymbol?._kind === 'import'
? this.stylable.resolver.deepResolve(localSymbol)
: { _kind: 'css', meta, symbol: localSymbol };
if ((resolved === null || resolved === void 0 ? void 0 : resolved._kind) !== 'css' || ((_a = resolved.symbol) === null || _a === void 0 ? void 0 : _a._kind) !== 'class') {
if (resolved?._kind !== 'css' || resolved.symbol?._kind !== 'class') {
return undefined;

@@ -179,3 +176,3 @@ }

};
namespaceClass(resolved.meta, resolved.symbol, node, meta);
namespaceClass(resolved.meta, resolved.symbol, node, false);
return (0, css_selector_parser_1.stringifySelectorAst)(node);

@@ -227,10 +224,23 @@ }

function namespaceClass(meta, symbol, node, // ToDo: check this is the correct type, should this be inline selector?
originMeta) {
wrapInGlobal = true) {
if (`-st-global` in symbol && symbol[`-st-global`]) {
// change node to `-st-global` value
const flatNode = (0, selector_1.convertToSelector)(node);
const globalMappedNodes = symbol[`-st-global`];
flatNode.nodes = globalMappedNodes;
// ToDo: check if this is causes an issue with globals from an imported alias
STGlobal.addGlobals(originMeta, globalMappedNodes);
if (wrapInGlobal) {
const globalMappedNodes = symbol[`-st-global`];
(0, selector_1.convertToPseudoClass)(node, 'global', [
{
type: 'selector',
nodes: globalMappedNodes,
after: '',
before: '',
end: 0,
start: 0,
},
]);
}
else {
const flatNode = (0, selector_1.convertToSelector)(node);
const globalMappedNodes = symbol[`-st-global`];
flatNode.nodes = globalMappedNodes;
}
}

@@ -341,3 +351,3 @@ else {

const rule = decl.parent;
if ((rule === null || rule === void 0 ? void 0 : rule.type) !== 'rule') {
if (rule?.type !== 'rule') {
return;

@@ -344,0 +354,0 @@ }

@@ -237,3 +237,2 @@ "use strict";

const checkNextName = (node) => {
var _a;
const { type, value } = node;

@@ -257,3 +256,3 @@ if (type === 'comment' || type === 'space') {

containers.push({ name, global });
(_a = namedNodeRefs[name]) !== null && _a !== void 0 ? _a : (namedNodeRefs[name] = []);
namedNodeRefs[name] ?? (namedNodeRefs[name] = []);
namedNodeRefs[name].push(node);

@@ -260,0 +259,0 @@ }

@@ -78,3 +78,2 @@ "use strict";

analyzeAtRule({ context, atRule }) {
var _a;
const isStylable = context.meta.type === 'stylable';

@@ -105,3 +104,3 @@ if (atRule.name === `property`) {

if (atRule.nodes) {
(_a = typedDefinitions[name]) !== null && _a !== void 0 ? _a : (typedDefinitions[name] = []);
typedDefinitions[name] ?? (typedDefinitions[name] = []);
typedDefinitions[name].push(atRule);

@@ -152,7 +151,6 @@ }

transformAtRuleNode({ context, atRule, resolved }) {
var _a;
if (atRule.name !== `property`) {
return;
}
if ((_a = atRule.nodes) === null || _a === void 0 ? void 0 : _a.length) {
if (atRule.nodes?.length) {
const propName = (0, global_1.globalValue)(atRule.params) || atRule.params;

@@ -172,5 +170,4 @@ if (resolved[propName]) {

transformValue({ node, data: { cssVarsMapping } }) {
var _a;
const { value } = node;
const varWithPrefix = ((_a = node.nodes[0]) === null || _a === void 0 ? void 0 : _a.value) || ``;
const varWithPrefix = node.nodes[0]?.value || ``;
if ((0, css_custom_property_1.validateCustomPropertyName)(varWithPrefix)) {

@@ -226,3 +223,2 @@ if (cssVarsMapping && cssVarsMapping[varWithPrefix]) {

parsed.walk((node) => {
var _a;
if (node.type === 'function' && node.value === 'var' && node.nodes) {

@@ -245,3 +241,3 @@ const varName = node.nodes[0];

context,
name: ((_a = postcss_value_parser_1.default.stringify(varName)) === null || _a === void 0 ? void 0 : _a.trim()) || ``,
name: postcss_value_parser_1.default.stringify(varName)?.trim() || ``,
node: decl,

@@ -315,5 +311,5 @@ global: context.meta.type === 'css',

const cssVar = STSymbol.get(meta, symbolName, `cssVar`);
return (cssVar === null || cssVar === void 0 ? void 0 : cssVar.global) ? symbolName : (0, css_custom_property_1.generateScopedCSSVar)(meta.namespace, symbolName.slice(2));
return cssVar?.global ? symbolName : (0, css_custom_property_1.generateScopedCSSVar)(meta.namespace, symbolName.slice(2));
}
exports.scopeCSSVar = scopeCSSVar;
//# sourceMappingURL=css-custom-property.js.map

@@ -154,3 +154,3 @@ "use strict";

const globalName = context.meta.type === 'stylable' ? (0, global_1.globalValue)(atRule.params) : undefined;
const name = globalName !== null && globalName !== void 0 ? globalName : atRule.params;
const name = globalName ?? atRule.params;
if (!name) {

@@ -163,3 +163,3 @@ return;

? getTransformedName(resolve)
: globalName !== null && globalName !== void 0 ? globalName : (0, namespace_1.namespace)(name, context.meta.namespace);
: globalName ?? (0, namespace_1.namespace)(name, context.meta.namespace);
},

@@ -166,0 +166,0 @@ transformDeclaration({ decl, resolved }) {

@@ -153,3 +153,2 @@ "use strict";

function parseLayerParams(params, report, atRule, isStylable) {
var _a, _b;
const names = [];

@@ -174,3 +173,3 @@ const globals = {};

layers.push(splittedLayer);
(_a = namedNodeRefs[name]) !== null && _a !== void 0 ? _a : (namedNodeRefs[name] = []);
namedNodeRefs[name] ?? (namedNodeRefs[name] = []);
namedNodeRefs[name].push(splittedLayer);

@@ -186,3 +185,3 @@ names.push(name);

if (globalName) {
(_b = namedNodeRefs[globalName]) !== null && _b !== void 0 ? _b : (namedNodeRefs[globalName] = []);
namedNodeRefs[globalName] ?? (namedNodeRefs[globalName] = []);
namedNodeRefs[globalName].push(node);

@@ -189,0 +188,0 @@ names.push(globalName);

@@ -33,5 +33,3 @@ "use strict";

const STCustomState = __importStar(require("./st-custom-state"));
const STCustomSelector = __importStar(require("./st-custom-selector"));
const diagnostics_1 = require("../diagnostics");
const selector_1 = require("../helpers/selector");
const is_vendor_prefixed_1 = __importDefault(require("is-vendor-prefixed"));

@@ -44,3 +42,3 @@ exports.diagnostics = {

transformSelectorNode({ context, selectorContext }) {
const { currentAnchor, node, rule, scopeSelectorAst } = selectorContext;
const { inferredSelector, node, ruleOrAtRule, scopeSelectorAst } = selectorContext;
if (node.type !== 'pseudo_class') {

@@ -50,34 +48,9 @@ return;

// find matching custom state
let foundCustomState = false;
for (const { symbol, meta } of currentAnchor.resolved) {
// Handle node resolve mapping for custom-selector.
// Currently custom selectors cannot get to this point in the process,
// due to them being replaced at the beginning of the transform process.
// However by using an internal process to analyze the context of selectors for
// the language service, a source selector can reach this point without the initial
// transform. This code keeps the custom selector untouched, but registers the AST it resolves to.
// ToDo: in the future we want to move the custom selector transformation inline, or remove it all together.
const customSelector = node.value.startsWith('--') &&
symbol['-st-root'] &&
STCustomSelector.getCustomSelectorExpended(meta, node.value.slice(2));
if (customSelector) {
const mappedSelectorAst = (0, selector_1.parseSelectorWithCache)(customSelector, { clone: true });
const mappedContext = selectorContext.createNestedContext(mappedSelectorAst);
// ToDo: wrap in :is() to get intersection of selectors
scopeSelectorAst(mappedContext);
if (mappedContext.currentAnchor) {
selectorContext.setNodeResolve(node, mappedContext.currentAnchor.resolved);
}
return; // this is not a state
const name = node.value;
const inferredState = inferredSelector.getPseudoClasses({ name })[name];
const foundCustomState = !!inferredState;
if (inferredState) {
if (selectorContext.transform) {
STCustomState.transformPseudoClassToCustomState(inferredState.state, inferredState.meta, node.value, node, inferredState.meta.namespace, context.resolver, context.diagnostics, ruleOrAtRule);
}
//
const states = symbol[`-st-states`];
if (states && Object.hasOwnProperty.call(states, node.value)) {
foundCustomState = true;
// transform custom state
if (selectorContext.transform) {
STCustomState.transformPseudoClassToCustomState(states, meta, node.value, node, meta.namespace, context.resolver, context.diagnostics, rule);
}
break;
}
}

@@ -88,16 +61,31 @@ // handle nested pseudo classes

// ignore `:st-global` since it is handled after the mixin transformation
if (selectorContext.experimentalSelectorInference) {
selectorContext.setNextSelectorScope([
{
_kind: 'css',
meta: context.meta,
symbol: { _kind: 'element', name: '*' },
},
], node);
}
return;
}
else {
const hasSubSelectors = node.value.match(/not|any|-\w+?-any|matches|is|where|has|local|nth-child|nth-last-child/);
// pickup all nested selectors except nth initial selector
const innerSelectors = (node.nodes[0] && node.nodes[0].type === `nth` ? node.nodes.slice(1) : node.nodes);
const nestedContext = selectorContext.createNestedContext(innerSelectors);
const nestedContext = selectorContext.createNestedContext(innerSelectors, selectorContext.inferredSelector);
scopeSelectorAst(nestedContext);
/**
* ToDo: remove once elements is deprecated!
* support deprecated elements.
* used to flatten nested elements for some native pseudo classes.
*/
if (node.value.match(/not|any|-\w+?-any|matches|is|where|has|local/)) {
// delegate elements of first selector
// change selector inference
if (hasSubSelectors && innerSelectors.length) {
if (selectorContext.experimentalSelectorInference &&
!node.value.match(/not|has/)) {
// set inferred to subject of nested selectors + prev compound
const prevNode = selectorContext.lastInferredSelectorNode;
if (prevNode && prevNode.type !== 'combinator') {
nestedContext.inferredMultipleSelectors.add(selectorContext.inferredSelector);
}
selectorContext.setNextSelectorScope(nestedContext.inferredMultipleSelectors, node);
}
// legacy: delegate elements of first selector
selectorContext.elements[selectorContext.selectorIndex].push(...nestedContext.elements[0]);

@@ -113,3 +101,3 @@ }

context.diagnostics.report(exports.diagnostics.UNKNOWN_STATE_USAGE(node.value), {
node: rule,
node: ruleOrAtRule,
word: node.value,

@@ -116,0 +104,0 @@ });

@@ -55,3 +55,3 @@ "use strict";

const resolvedClass = resolvedSymbols.class[node.value];
if ((resolvedClass === null || resolvedClass === void 0 ? void 0 : resolvedClass.length) > 1 && resolvedClass[0].symbol.alias) {
if (resolvedClass?.length > 1 && resolvedClass[0].symbol.alias) {
// fallback to imported class alias for case that no actual

@@ -76,4 +76,3 @@ // type selector was found in the source rules, but transform is

}
selectorContext.setCurrentAnchor({ name: node.value, type: 'element', resolved });
selectorContext.setNodeResolve(node, resolved);
selectorContext.setNextSelectorScope(resolved, node, node.value);
// native node does not resolve e.g. div

@@ -83,3 +82,3 @@ if (selectorContext.transform && resolved && resolved.length > 1) {

if (symbol._kind === 'class') {
CSSClass.namespaceClass(meta, symbol, node, selectorContext.originMeta);
CSSClass.namespaceClass(meta, symbol, node);
}

@@ -86,0 +85,0 @@ else {

@@ -74,2 +74,3 @@ import type { StylableMeta } from '../stylable-meta';

resolved: T['RESOLVED'];
transformer: StylableTransformer;
}) => void;

@@ -80,3 +81,3 @@ transformSelectorNode: (options: {

selectorContext: Required<ScopeContext>;
}) => void;
}) => boolean | void;
transformDeclaration: (options: {

@@ -83,0 +84,0 @@ context: FeatureTransformContext;

@@ -87,2 +87,4 @@ "use strict";

prepareAST({ context, node, toRemove }) {
// called without experimentalSelectorInference
// split selectors & remove definitions
if (node.type === 'rule' && node.selector.match(exports.CUSTOM_SELECTOR_RE)) {

@@ -98,20 +100,39 @@ node.selector = transformCustomSelectorInline(context.meta, node.selector, {

},
transformSelectorNode({ context, selectorContext, node }) {
const customSelector = node.value.startsWith('--') &&
getCustomSelectorExpended(context.meta, node.value.slice(2));
if (customSelector) {
const mappedSelectorAst = (0, selector_1.parseSelectorWithCache)(customSelector, { clone: true });
const mappedContext = selectorContext.createNestedContext(mappedSelectorAst);
selectorContext.scopeSelectorAst(mappedContext);
const inferredSelector = selectorContext.experimentalSelectorInference
? mappedContext.inferredMultipleSelectors
: mappedContext.inferredSelector;
selectorContext.setNextSelectorScope(inferredSelector, node); // doesn't add to the resolved elements
if (selectorContext.transform) {
selectorContext.transformIntoMultiSelector(node, mappedSelectorAst);
}
}
return !!customSelector;
},
transformAtRuleNode({ atRule }) {
if (atRule.name === 'custom-selector') {
atRule.remove();
}
},
});
// API
function isScoped(meta, name) {
var _a;
const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
return (_a = analyzed[name]) === null || _a === void 0 ? void 0 : _a.isScoped;
return analyzed[name]?.isScoped;
}
exports.isScoped = isScoped;
function getCustomSelector(meta, name) {
var _a;
const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
return (_a = analyzed[name]) === null || _a === void 0 ? void 0 : _a.ast;
return analyzed[name]?.ast;
}
exports.getCustomSelector = getCustomSelector;
function getCustomSelectorExpended(meta, name) {
var _a;
const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
return (_a = analyzed[name]) === null || _a === void 0 ? void 0 : _a.selector;
return analyzed[name]?.selector;
}

@@ -127,3 +148,3 @@ exports.getCustomSelectorExpended = getCustomSelectorExpended;

const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
const inlined = (0, custom_selector_1.transformCustomSelectors)(ast, (name) => { var _a; return (_a = analyzed[name]) === null || _a === void 0 ? void 0 : _a.ast; }, (report) => {
const inlined = (0, custom_selector_1.transformCustomSelectors)(ast, (name) => analyzed[name]?.ast, (report) => {
if (options.diagnostics && options.node) {

@@ -130,0 +151,0 @@ const unknownSelector = `:--${report.unknown}`;

import type { StylableMeta } from '../stylable-meta';
import type { SelectorNode, ImmutableSelectorNode, SelectorList, PseudoClass } from '@tokey/css-selector-parser';
import type { ImmutableSelectorNode, SelectorList } from '@tokey/css-selector-parser';
import type * as postcss from 'postcss';

@@ -15,4 +15,3 @@ export declare const diagnostics: {

export declare function getGlobalRules(meta: StylableMeta): postcss.Rule[];
export declare function unwrapPseudoGlobals(selectorAst: SelectorList): PseudoClass[];
export declare function addGlobals(meta: StylableMeta, selectorAst: SelectorNode[]): void;
export declare function unwrapPseudoGlobals(selectorAst: SelectorList): SelectorList;
//# sourceMappingURL=st-global.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addGlobals = exports.unwrapPseudoGlobals = exports.getGlobalRules = exports.hooks = exports.diagnostics = void 0;
exports.unwrapPseudoGlobals = exports.getGlobalRules = exports.hooks = exports.diagnostics = void 0;
const feature_1 = require("./feature");

@@ -18,4 +18,3 @@ const plugable_record_1 = require("../helpers/plugable-record");

analyzeSelectorNode({ context, node, topSelectorIndex, rule, originalNode }) {
var _a, _b, _c;
var _d, _e;
var _a, _b;
const { rules } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);

@@ -35,4 +34,4 @@ if (node.type === 'selector' || node.type === 'combinator' || node.type === 'comment') {

// mark selector as global only if it isn't set
(_a = (_d = ruleData.topLevelSelectorsFlags)[topSelectorIndex]) !== null && _a !== void 0 ? _a : (_d[topSelectorIndex] = true);
if (node.nodes && ((_b = node.nodes) === null || _b === void 0 ? void 0 : _b.length) > 1) {
(_a = ruleData.topLevelSelectorsFlags)[topSelectorIndex] ?? (_a[topSelectorIndex] = true);
if (node.nodes && node.nodes?.length > 1) {
context.diagnostics.report(exports.diagnostics.UNSUPPORTED_MULTI_SELECTOR_IN_GLOBAL(), {

@@ -47,3 +46,3 @@ node: rule,

// mark selector as global only if it isn't set
(_c = (_e = ruleData.topLevelSelectorsFlags)[topSelectorIndex]) !== null && _c !== void 0 ? _c : (_e[topSelectorIndex] = true);
(_b = ruleData.topLevelSelectorsFlags)[topSelectorIndex] ?? (_b[topSelectorIndex] = true);
}

@@ -89,4 +88,7 @@ else {

const selectorAst = (0, selector_1.parseSelectorWithCache)(r.selector, { clone: true });
const globals = unwrapPseudoGlobals(selectorAst);
addGlobals(meta, globals);
(0, selector_1.walkSelector)(unwrapPseudoGlobals(selectorAst), (inner) => {
if (inner.type === 'class') {
meta.globals[inner.value] = true;
}
});
r.selector = (0, selector_1.stringifySelector)(selectorAst);

@@ -111,7 +113,5 @@ });

(0, selector_1.walkSelector)(selectorAst, (node) => {
var _a;
if (node.type === 'pseudo_class' && node.value === 'global') {
collectedGlobals.push(node);
if (((_a = node.nodes) === null || _a === void 0 ? void 0 : _a.length) === 1) {
(0, selector_1.flattenFunctionalSelector)(node);
if (node.nodes?.length === 1) {
collectedGlobals.push((0, selector_1.flattenFunctionalSelector)(node));
}

@@ -125,13 +125,2 @@ return selector_1.walkSelector.skipNested;

exports.unwrapPseudoGlobals = unwrapPseudoGlobals;
function addGlobals(meta, selectorAst) {
for (const ast of selectorAst) {
(0, selector_1.walkSelector)(ast, (inner) => {
if (inner.type === 'class') {
// ToDo: consider if to move to css-class feature.
meta.globals[inner.value] = true;
}
});
}
}
exports.addGlobals = addGlobals;
//# sourceMappingURL=st-global.js.map

@@ -87,4 +87,3 @@ "use strict";

analyzeAtRule({ context, atRule }) {
var _a;
if (atRule.name === `st-import` && ((_a = atRule.parent) === null || _a === void 0 ? void 0 : _a.type) !== `root`) {
if (atRule.name === `st-import` && atRule.parent?.type !== `root`) {
context.diagnostics.report(exports.diagnostics.NO_ST_IMPORT_IN_NESTED_SCOPE(), {

@@ -101,3 +100,2 @@ node: atRule,

analyzeSelectorNode({ context, rule, node }) {
var _a;
if (node.value !== `import`) {

@@ -110,3 +108,3 @@ return;

}
if (((_a = rule.parent) === null || _a === void 0 ? void 0 : _a.type) !== `root`) {
if (rule.parent?.type !== `root`) {
context.diagnostics.report(exports.diagnostics.NO_PSEUDO_IMPORT_IN_NESTED_SCOPE(), {

@@ -113,0 +111,0 @@ node: rule,

@@ -25,2 +25,5 @@ "use strict";

};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -38,3 +41,3 @@ exports.StylablePublicApi = exports.hooks = exports.diagnostics = exports.MixinType = void 0;

const postcss = __importStar(require("postcss"));
const postcss_value_parser_1 = require("postcss-value-parser");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
const stylable_assets_1 = require("../stylable-assets");

@@ -74,3 +77,2 @@ const stylable_utils_1 = require("../stylable-utils");

resolveExpr(meta, expr, { diagnostics = new diagnostics_1.Diagnostics(), resolveOptionalArgs = false, } = {}) {
var _a;
const resolvedSymbols = this.stylable.resolver.resolveSymbols(meta, diagnostics);

@@ -122,3 +124,5 @@ const { mainNamespace } = resolvedSymbols;

kind: 'invalid',
args: ((_a = data.valueNode) === null || _a === void 0 ? void 0 : _a.type) === 'function' ? (0, postcss_value_parser_1.stringify)(data.valueNode.nodes) : '',
args: data.valueNode?.type === 'function'
? postcss_value_parser_1.default.stringify(data.valueNode.nodes)
: '',
});

@@ -313,3 +317,3 @@ }

const optionalArgs = new Set();
const roots = getCSSMixinRoots(context.meta, resolveChain, ({ mixinRoot, resolvedClass, isRootMixin }) => {
const roots = getCSSMixinRoots(context.meta, resolveChain, ({ mixinRoot, resolved, isRootMixin }) => {
const stVarOverride = context.evaluator.stVarOverride || {};

@@ -323,8 +327,11 @@ const mixDef = config.mixDef;

const resolvedArgs = (0, functions_1.resolveArgumentsValue)(namedArgs, config.transformer, context.meta, context.diagnostics, mixDef.data.originDecl, stVarOverride, config.path, config.cssPropertyMapping);
collectOptionalArgs({ meta: resolvedClass.meta, resolver: context.resolver }, mixinRoot, optionalArgs);
collectOptionalArgs({ meta: resolved.meta, resolver: context.resolver }, mixinRoot, optionalArgs);
// transform mixin
const mixinMeta = resolvedClass.meta;
const symbolName = isRootMixin && resolvedClass.meta !== context.meta ? 'default' : mixDef.data.type;
config.transformer.transformAst(mixinRoot, mixinMeta, undefined, resolvedArgs, config.path.concat(symbolName + ' from ' + context.meta.source), true, resolvedClass.symbol.name);
(0, stylable_assets_1.fixRelativeUrls)(mixinRoot, resolvedClass.meta.source, context.meta.source);
const mixinMeta = resolved.meta;
const symbolName = isRootMixin && resolved.meta !== context.meta ? 'default' : mixDef.data.type;
config.transformer.transformAst(mixinRoot, mixinMeta, undefined, resolvedArgs, config.path.concat(symbolName + ' from ' + context.meta.source), true, config.transformer.createInferredSelector(mixinMeta, {
name: resolved.symbol.name,
type: resolved.symbol._kind,
}));
(0, stylable_assets_1.fixRelativeUrls)(mixinRoot, resolved.meta.source, context.meta.source);
});

@@ -363,3 +370,3 @@ for (const overrideArg of overrideKeys) {

const mixinRoot = (0, rule_1.createSubsetAst)(resolved.meta.sourceAst, (resolved.symbol._kind === 'class' ? '.' : '') + resolved.symbol.name, undefined, isRootMixin, (name) => STCustomSelector.getCustomSelector(contextMeta, name));
processMixinRoot({ mixinRoot, resolvedClass: resolved, isRootMixin });
processMixinRoot({ mixinRoot, resolved, isRootMixin });
roots.push(mixinRoot);

@@ -387,7 +394,6 @@ if (resolved.symbol[`-st-extends`]) {

mixinRoot.walkDecls((decl) => {
var _a;
if (!decl.value.match(regexp)) {
const parent = decl.parent;
decl.remove();
if (((_a = parent === null || parent === void 0 ? void 0 : parent.nodes) === null || _a === void 0 ? void 0 : _a.length) === 0) {
if (parent?.nodes?.length === 0) {
parent.remove();

@@ -394,0 +400,0 @@ }

@@ -73,3 +73,3 @@ "use strict";

// empty params (not even empty quotes)
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), { node });
diag?.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), { node });
return;

@@ -90,3 +90,3 @@ }

// extra definition - mark as invalid and clear namespace
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.EXTRA_DEFINITION(), {
diag?.report(exports.diagnostics.EXTRA_DEFINITION(), {
node,

@@ -106,3 +106,3 @@ word: postcss_value_parser_1.default.stringify(valueNode),

// invalid definition - mark as invalid and clear namespace
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.EXTRA_DEFINITION(), {
diag?.report(exports.diagnostics.EXTRA_DEFINITION(), {
node,

@@ -118,3 +118,3 @@ word: postcss_value_parser_1.default.stringify(valueNode),

// no namespace found
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.INVALID_NAMESPACE_DEF(), {
diag?.report(exports.diagnostics.INVALID_NAMESPACE_DEF(), {
node,

@@ -126,3 +126,3 @@ });

// empty namespace found
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), {
diag?.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), {
node,

@@ -136,3 +136,3 @@ });

// empty namespace found
diag === null || diag === void 0 ? void 0 : diag.report(exports.diagnostics.INVALID_NAMESPACE_VALUE(), {
diag?.report(exports.diagnostics.INVALID_NAMESPACE_VALUE(), {
node,

@@ -139,0 +139,0 @@ word: namespace,

@@ -53,2 +53,4 @@ "use strict";

prepareAST({ node, toRemove }) {
// called without experimentalSelectorInference
// flatten @st-scope before transformation
if (isStScopeStatement(node)) {

@@ -59,2 +61,23 @@ flattenScope(node);

},
transformAtRuleNode({ context: { meta }, atRule, transformer }) {
if (isStScopeStatement(atRule)) {
const { selector, inferredSelector } = transformer.scopeSelector(meta, atRule.params, atRule);
// transform selector in params
atRule.params = selector;
// track selector context for nested selector nodes
transformer.containerInferredSelectorMap.set(atRule, inferredSelector);
}
},
transformLastPass({ ast }) {
// called with experimentalSelectorInference=true
// flatten @st-scope after transformation
const toRemove = [];
for (const node of ast.nodes) {
if (isStScopeStatement(node)) {
flattenScope(node);
toRemove.push(() => node.replaceWith(node.nodes || []));
}
}
toRemove.forEach((remove) => remove());
},
});

@@ -84,7 +107,6 @@ // API

function getStScope(rule) {
var _a;
let current = rule;
while (current === null || current === void 0 ? void 0 : current.parent) {
while (current?.parent) {
current = current.parent;
if (isStScopeStatement(current) && ((_a = current.parent) === null || _a === void 0 ? void 0 : _a.type) === 'root') {
if (isStScopeStatement(current) && current.parent?.type === 'root') {
return current;

@@ -91,0 +113,0 @@ }

@@ -145,4 +145,4 @@ "use strict";

const computedStVar = {
value: runtimeValue !== null && runtimeValue !== void 0 ? runtimeValue : outputValue,
input: topLevelType !== null && topLevelType !== void 0 ? topLevelType : (0, custom_values_1.unbox)(outputValue, false),
value: runtimeValue ?? outputValue,
input: topLevelType ?? (0, custom_values_1.unbox)(outputValue, false),
diagnostics,

@@ -265,4 +265,4 @@ };

// override with value
if (stVarOverride === null || stVarOverride === void 0 ? void 0 : stVarOverride[varName]) {
parsedNode.resolvedValue = stVarOverride === null || stVarOverride === void 0 ? void 0 : stVarOverride[varName];
if (stVarOverride?.[varName]) {
parsedNode.resolvedValue = stVarOverride?.[varName];
return;

@@ -280,3 +280,3 @@ }

const resolvedVar = resolvedSymbols.var[varName];
const resolvedVarSymbol = resolvedVar === null || resolvedVar === void 0 ? void 0 : resolvedVar.symbol;
const resolvedVarSymbol = resolvedVar?.symbol;
const possibleNonSTVarSymbol = STSymbol.get(context.meta, varName);

@@ -372,3 +372,3 @@ if (resolvedVarSymbol) {

const symbol = STSymbol.get(meta, name);
switch (symbol === null || symbol === void 0 ? void 0 : symbol._kind) {
switch (symbol?._kind) {
case 'var':

@@ -382,3 +382,3 @@ parseVarsFromExpr(symbol.text).forEach((refName) => varsToCheck.push({

const resolved = context.resolver.deepResolve(symbol);
if ((resolved === null || resolved === void 0 ? void 0 : resolved._kind) === 'css' && resolved.symbol._kind === 'var') {
if (resolved?._kind === 'css' && resolved.symbol._kind === 'var') {
varsToCheck.push({ meta: resolved.meta, name: resolved.symbol.name });

@@ -385,0 +385,0 @@ }

@@ -106,3 +106,3 @@ "use strict";

if (diagnostics && node) {
diagnostics.report(exports.functionDiagnostics.FAIL_TO_EXECUTE_FORMATTER(parsedNode.resolvedValue, error === null || error === void 0 ? void 0 : error.message), {
diagnostics.report(exports.functionDiagnostics.FAIL_TO_EXECUTE_FORMATTER(parsedNode.resolvedValue, error?.message), {
node,

@@ -109,0 +109,0 @@ word: node.value,

@@ -15,6 +15,5 @@ "use strict";

function validateAtProperty(atRule, diagnostics) {
var _a;
const name = atRule.params;
const atPropertyValues = new Map();
if (!((_a = atRule.nodes) === null || _a === void 0 ? void 0 : _a.length)) {
if (!atRule.nodes?.length) {
return {

@@ -21,0 +20,0 @@ valid: true,

@@ -146,5 +146,5 @@ import type * as postcss from 'postcss';

export declare const systemValidators: Record<string, StateParamType>;
export declare function validateRuleStateDefinition(rule: postcss.Rule, meta: StylableMeta, resolver: StylableResolver, diagnostics: Diagnostics): void;
export declare function validateStateArgument(stateAst: StateParsedValue, meta: StylableMeta, value: string, resolver: StylableResolver, diagnostics: Diagnostics, rule?: postcss.Rule, validateDefinition?: boolean, validateValue?: boolean): StateResult;
export declare function transformPseudoClassToCustomState(states: MappedStates, meta: StylableMeta, name: string, node: PseudoClass, namespace: string, resolver: StylableResolver, diagnostics: Diagnostics, rule?: postcss.Rule): void;
export declare function validateRuleStateDefinition(selector: string, selectorNode: postcss.Rule | postcss.AtRule, meta: StylableMeta, resolver: StylableResolver, diagnostics: Diagnostics): void;
export declare function validateStateArgument(stateAst: StateParsedValue, meta: StylableMeta, value: string, resolver: StylableResolver, diagnostics: Diagnostics, selectorNode?: postcss.Node, validateDefinition?: boolean, validateValue?: boolean): StateResult;
export declare function transformPseudoClassToCustomState(stateDef: MappedStates[string], meta: StylableMeta, name: string, stateNode: PseudoClass, namespace: string, resolver: StylableResolver, diagnostics: Diagnostics, selectorNode?: postcss.Node): void;
export declare function isTemplateState(state: MappedStates[string]): state is TemplateStateParsedValue;

@@ -151,0 +151,0 @@ export declare function createBooleanStateClassName(stateName: string, namespace: string): string;

@@ -100,3 +100,3 @@ "use strict";

}
if ((paramType === null || paramType === void 0 ? void 0 : paramType.type) === 'string') {
if (paramType?.type === 'string') {
defineTemplateState(stateName, paramType, argsFirstNode, argsFullValue, mappedStates, diagnostics, decl);

@@ -411,5 +411,4 @@ }

};
function validateRuleStateDefinition(rule, meta, resolver, diagnostics) {
const parentRule = rule;
const selectorAst = (0, selector_1.parseSelectorWithCache)(parentRule.selector);
function validateRuleStateDefinition(selector, selectorNode, meta, resolver, diagnostics) {
const selectorAst = (0, selector_1.parseSelectorWithCache)(selector);
if (selectorAst.length && selectorAst.length === 1) {

@@ -421,3 +420,3 @@ const singleSelectorAst = selectorAst[0];

const classMeta = features_1.CSSClass.get(meta, className);
const states = classMeta === null || classMeta === void 0 ? void 0 : classMeta[`-st-states`];
const states = classMeta?.[`-st-states`];
if (states && classMeta._kind === 'class') {

@@ -429,5 +428,5 @@ for (const stateName in states) {

const stateParam = isTemplateState(state) ? state.params[0] : state;
const { errors } = validateStateArgument(stateParam, meta, stateParam.defaultValue || '', resolver, diagnostics, parentRule, true, !!stateParam.defaultValue);
const { errors } = validateStateArgument(stateParam, meta, stateParam.defaultValue || '', resolver, diagnostics, selectorNode, true, !!stateParam.defaultValue);
if (errors) {
rule.walkDecls((decl) => {
selectorNode.walkDecls((decl) => {
if (decl.prop === `-st-states`) {

@@ -453,5 +452,5 @@ diagnostics.report(exports.stateDiagnostics.DEFAULT_PARAM_FAILS_VALIDATION(stateName, stateParam.defaultValue || '', errors), {

exports.validateRuleStateDefinition = validateRuleStateDefinition;
function validateStateArgument(stateAst, meta, value, resolver, diagnostics, rule, validateDefinition, validateValue = true) {
function validateStateArgument(stateAst, meta, value, resolver, diagnostics, selectorNode, validateDefinition, validateValue = true) {
const resolvedValidations = {
res: resolveParam(meta, resolver, diagnostics, rule, value || stateAst.defaultValue),
res: resolveParam(meta, resolver, diagnostics, selectorNode, value || stateAst.defaultValue),
errors: null,

@@ -463,3 +462,3 @@ };

if (resolvedValidations.res || validateDefinition) {
const { errors } = validator.validate(resolvedValidations.res, stateAst.arguments, resolveParam.bind(null, meta, resolver, diagnostics, rule), !!validateDefinition, validateValue);
const { errors } = validator.validate(resolvedValidations.res, stateAst.arguments, resolveParam.bind(null, meta, resolver, diagnostics, selectorNode), !!validateDefinition, validateValue);
resolvedValidations.errors = errors;

@@ -475,19 +474,18 @@ }

// transform
function transformPseudoClassToCustomState(states, meta, name, node, namespace, resolver, diagnostics, rule) {
const stateDef = states[name];
function transformPseudoClassToCustomState(stateDef, meta, name, stateNode, namespace, resolver, diagnostics, selectorNode) {
if (stateDef === null) {
(0, selector_1.convertToClass)(node).value = createBooleanStateClassName(name, namespace);
delete node.nodes;
(0, selector_1.convertToClass)(stateNode).value = createBooleanStateClassName(name, namespace);
delete stateNode.nodes;
}
else if (typeof stateDef === 'string') {
// simply concat global mapped selector - ToDo: maybe change to 'selector'
(0, selector_1.convertToInvalid)(node).value = stateDef;
delete node.nodes;
(0, selector_1.convertToInvalid)(stateNode).value = stateDef;
delete stateNode.nodes;
}
else if (typeof stateDef === 'object') {
if (isTemplateState(stateDef)) {
convertTemplateState(meta, resolver, diagnostics, rule, node, stateDef, name);
convertTemplateState(meta, resolver, diagnostics, selectorNode, stateNode, stateDef, name);
}
else {
resolveStateValue(meta, resolver, diagnostics, rule, node, stateDef, name, namespace);
resolveStateValue(meta, resolver, diagnostics, selectorNode, stateNode, stateDef, name, namespace);
}

@@ -517,6 +515,6 @@ }

exports.resolveStateParam = resolveStateParam;
function convertTemplateState(meta, resolver, diagnostics, rule, node, stateParamDef, name) {
function convertTemplateState(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name) {
const paramStateDef = stateParamDef.params[0];
const resolvedParam = getParamInput(meta, resolver, diagnostics, rule, node, paramStateDef, name);
validateParam(meta, resolver, diagnostics, rule, paramStateDef, resolvedParam, name);
const resolvedParam = getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, paramStateDef, name);
validateParam(meta, resolver, diagnostics, selectorNode, paramStateDef, resolvedParam, name);
const strippedParam = (0, string_1.stripQuotation)(resolvedParam);

@@ -527,13 +525,13 @@ transformMappedStateWithParam({

param: strippedParam,
node,
rule,
node: stateNode,
selectorNode: selectorNode,
diagnostics,
});
}
function getParamInput(meta, resolver, diagnostics, rule, node, stateParamDef, name) {
const inputValue = node.nodes && node.nodes.length ? (0, selector_1.stringifySelector)(node.nodes) : ``;
const resolvedParam = resolveParam(meta, resolver, diagnostics, rule, inputValue ? inputValue : stateParamDef.defaultValue);
if (rule && !inputValue && !stateParamDef.defaultValue) {
function getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name) {
const inputValue = stateNode.nodes && stateNode.nodes.length ? (0, selector_1.stringifySelector)(stateNode.nodes) : ``;
const resolvedParam = resolveParam(meta, resolver, diagnostics, selectorNode, inputValue ? inputValue : stateParamDef.defaultValue);
if (selectorNode && !inputValue && !stateParamDef.defaultValue) {
diagnostics.report(exports.stateDiagnostics.NO_STATE_ARGUMENT_GIVEN(name, stateParamDef.type), {
node: rule,
node: selectorNode,
word: name,

@@ -544,7 +542,7 @@ });

}
function validateParam(meta, resolver, diagnostics, rule, stateParamDef, resolvedParam, name) {
function validateParam(meta, resolver, diagnostics, selectorNode, stateParamDef, resolvedParam, name) {
const validator = exports.systemValidators[stateParamDef.type];
let stateParamOutput;
try {
stateParamOutput = validator.validate(resolvedParam, stateParamDef.arguments, resolveParam.bind(null, meta, resolver, diagnostics, rule), false, true);
stateParamOutput = validator.validate(resolvedParam, stateParamDef.arguments, resolveParam.bind(null, meta, resolver, diagnostics, selectorNode), false, true);
}

@@ -558,5 +556,5 @@ catch (e) {

}
if (rule && stateParamOutput.errors) {
if (selectorNode && stateParamOutput.errors) {
diagnostics.report(exports.stateDiagnostics.FAILED_STATE_VALIDATION(name, resolvedParam, stateParamOutput.errors), {
node: rule,
node: selectorNode,
word: resolvedParam,

@@ -567,16 +565,16 @@ });

}
function resolveStateValue(meta, resolver, diagnostics, rule, node, stateParamDef, name, namespace) {
const resolvedParam = getParamInput(meta, resolver, diagnostics, rule, node, stateParamDef, name);
validateParam(meta, resolver, diagnostics, rule, stateParamDef, resolvedParam, name);
function resolveStateValue(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name, namespace) {
const resolvedParam = getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name);
validateParam(meta, resolver, diagnostics, selectorNode, stateParamDef, resolvedParam, name);
const strippedParam = (0, string_1.stripQuotation)(resolvedParam);
(0, selector_1.convertToClass)(node).value = createStateWithParamClassName(name, namespace, strippedParam);
delete node.nodes;
(0, selector_1.convertToClass)(stateNode).value = createStateWithParamClassName(name, namespace, strippedParam);
delete stateNode.nodes;
}
function transformMappedStateWithParam({ stateName, template, param, node, rule, diagnostics, }) {
function transformMappedStateWithParam({ stateName, template, param, node, selectorNode, diagnostics, }) {
const targetSelectorStr = template.replace(/\$0/g, param);
const selectorAst = (0, selector_1.parseSelectorWithCache)(targetSelectorStr, { clone: true });
if (selectorAst.length > 1) {
if (rule) {
if (selectorNode) {
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_MULTI_SELECTOR(stateName, targetSelectorStr), {
node: rule,
node: selectorNode,
});

@@ -588,6 +586,6 @@ }

const firstSelector = selectorAst[0].nodes.find(({ type }) => type !== 'comment');
if ((firstSelector === null || firstSelector === void 0 ? void 0 : firstSelector.type) === 'type' || (firstSelector === null || firstSelector === void 0 ? void 0 : firstSelector.type) === 'universal') {
if (rule) {
if (firstSelector?.type === 'type' || firstSelector?.type === 'universal') {
if (selectorNode) {
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_INITIAL_SELECTOR(stateName, targetSelectorStr), {
node: rule,
node: selectorNode,
});

@@ -605,7 +603,7 @@ }

if (unexpectedSelector) {
if (rule) {
if (selectorNode) {
switch (unexpectedSelector.type) {
case 'combinator':
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_COMPLEX_SELECTOR(stateName, targetSelectorStr), {
node: rule,
node: selectorNode,
});

@@ -615,3 +613,3 @@ break;

diagnostics.report(exports.stateDiagnostics.INVALID_SELECTOR(stateName, targetSelectorStr), {
node: rule,
node: selectorNode,
});

@@ -626,7 +624,7 @@ break;

}
function resolveParam(meta, resolver, diagnostics, rule, nodeContent) {
function resolveParam(meta, resolver, diagnostics, node, nodeContent) {
const defaultStringValue = '';
const param = nodeContent || defaultStringValue;
return (0, functions_1.evalDeclarationValue)(resolver, param, meta, rule, undefined, undefined, diagnostics);
return (0, functions_1.evalDeclarationValue)(resolver, param, meta, node, undefined, undefined, diagnostics);
}
//# sourceMappingURL=custom-state.js.map

@@ -9,3 +9,3 @@ "use strict";

const match = str.match(globalValueRegExp);
return match === null || match === void 0 ? void 0 : match[1];
return match?.[1];
}

@@ -12,0 +12,0 @@ exports.globalValue = globalValue;

@@ -149,4 +149,3 @@ "use strict";

function parseStImport(atRule, context, diagnostics) {
var _a, _b;
var _c;
var _a;
const keyframes = {};

@@ -186,3 +185,3 @@ const importObj = {

for (const [impName, impAsName] of namedTyped) {
(_a = (_c = importObj.typed)[kind]) !== null && _a !== void 0 ? _a : (_c[kind] = {});
(_a = importObj.typed)[kind] ?? (_a[kind] = {});
importObj.typed[kind][impAsName] = impName;

@@ -202,3 +201,3 @@ }

}
else if (!((_b = imports.from) === null || _b === void 0 ? void 0 : _b.trim())) {
else if (!imports.from?.trim()) {
diagnostics.report(exports.parseImportMessages.ST_IMPORT_EMPTY_FROM(), { node: atRule });

@@ -400,3 +399,2 @@ }

var _a;
var _b;
const { nodes } = tokens;

@@ -411,3 +409,3 @@ for (let i = 0; i < nodes.length; i++) {

if (isImportAs(space, as)) {
if ((spaceAfter === null || spaceAfter === void 0 ? void 0 : spaceAfter.type) === 'space' && (asName === null || asName === void 0 ? void 0 : asName.type) === 'word') {
if (spaceAfter?.type === 'space' && asName?.type === 'word') {
mainBucket[asName.value] = token.value;

@@ -433,3 +431,3 @@ i += 4; //ignore next 4 tokens

else {
(_a = typedBuckets[_b = token.value]) !== null && _a !== void 0 ? _a : (typedBuckets[_b] = {});
typedBuckets[_a = token.value] ?? (typedBuckets[_a] = {});
handleNamedTokens(token, typedBuckets[token.value], null, node, diagnostics);

@@ -441,3 +439,3 @@ }

function isImportAs(space, as) {
return (space === null || space === void 0 ? void 0 : space.type) === 'space' && (as === null || as === void 0 ? void 0 : as.type) === 'word' && (as === null || as === void 0 ? void 0 : as.value) === 'as';
return space?.type === 'space' && as?.type === 'word' && as?.value === 'as';
}

@@ -448,3 +446,3 @@ function tryCollectImportsDeep(resolver, meta, imports = new Set(), onImport = undefined, depth = 0) {

const resolved = resolver.resolvePath(context, request);
onImport === null || onImport === void 0 ? void 0 : onImport({ context, request, resolved, depth });
onImport?.({ context, request, resolved, depth });
if (!imports.has(resolved)) {

@@ -451,0 +449,0 @@ imports.add(resolved);

@@ -19,3 +19,3 @@ "use strict";

if (emitStrategyDiagnostics) {
diagnostics === null || diagnostics === void 0 ? void 0 : diagnostics.report(diagnostic, { ...options, node: mixinNode });
diagnostics?.report(diagnostic, { ...options, node: mixinNode });
}

@@ -41,3 +41,3 @@ }

else if (node.type === 'string') {
diagnostics === null || diagnostics === void 0 ? void 0 : diagnostics.report(exports.mixinHelperDiagnostics.VALUE_CANNOT_BE_STRING(), {
diagnostics?.report(exports.mixinHelperDiagnostics.VALUE_CANNOT_BE_STRING(), {
node: mixinNode,

@@ -44,0 +44,0 @@ word: mixinNode.value,

@@ -32,3 +32,2 @@ "use strict";

return (namespace, stylesheetOriginPath, stylesheetPath = stylesheetOriginPath) => {
var _a;
const packageInfo = getPackageInfo(stylesheetPath);

@@ -49,3 +48,3 @@ const buildNamespaceParams = {

};
const { namespace: resultNs, hashPart } = (_a = buildNamespace(buildNamespaceParams)) !== null && _a !== void 0 ? _a : defaultNamespaceBuilder(buildNamespaceParams);
const { namespace: resultNs, hashPart } = buildNamespace(buildNamespaceParams) ?? defaultNamespaceBuilder(buildNamespaceParams);
const hashStr = hashFn(hashPart).toString();

@@ -52,0 +51,0 @@ let i = typeof hashFragment === 'number'

@@ -1,2 +0,2 @@

import { parseCssSelector, stringifySelectorAst, walk, SelectorNode, Selector, SelectorList, FunctionalSelector, Class, Attribute, Invalid, ImmutableSelectorList, ImmutableSelectorNode } from '@tokey/css-selector-parser';
import { parseCssSelector, stringifySelectorAst, walk, SelectorNode, PseudoClass, Selector, SelectorList, FunctionalSelector, Class, Attribute, Invalid, ImmutableSelectorList, ImmutableSelectorNode, Combinator } from '@tokey/css-selector-parser';
export declare const parseSelector: typeof parseCssSelector;

@@ -31,2 +31,4 @@ export declare const stringifySelector: typeof stringifySelectorAst;

export declare function convertToSelector(node: SelectorNode): Selector;
export declare function convertToPseudoClass(node: SelectorNode, name: string, nestedSelectors?: SelectorList): PseudoClass;
export declare function createCombinatorSelector(partial: Partial<Combinator>): Combinator;
export declare function isInPseudoClassContext(parents: ReadonlyArray<ImmutableSelectorNode>): boolean;

@@ -33,0 +35,0 @@ export declare function matchTypeAndValue(a: Partial<ImmutableSelectorNode>, b: Partial<ImmutableSelectorNode>): boolean;

@@ -6,3 +6,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.scopeNestedSelector = exports.isCompRoot = exports.matchTypeAndValue = exports.isInPseudoClassContext = exports.convertToSelector = exports.convertToInvalid = exports.convertToAttribute = exports.convertToClass = exports.flattenFunctionalSelector = exports.isSimpleSelector = exports.parseSelectorWithCache = exports.walkSelector = exports.stringifySelector = exports.parseSelector = void 0;
exports.scopeNestedSelector = exports.isCompRoot = exports.matchTypeAndValue = exports.isInPseudoClassContext = exports.createCombinatorSelector = exports.convertToPseudoClass = exports.convertToSelector = exports.convertToInvalid = exports.convertToAttribute = exports.convertToClass = exports.flattenFunctionalSelector = exports.isSimpleSelector = exports.parseSelectorWithCache = exports.walkSelector = exports.stringifySelector = exports.parseSelector = void 0;
const css_selector_parser_1 = require("@tokey/css-selector-parser");

@@ -95,2 +95,30 @@ const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));

exports.convertToSelector = convertToSelector;
function convertToPseudoClass(node, name, nestedSelectors) {
const castedNode = node;
castedNode.type = 'pseudo_class';
castedNode.value = name;
castedNode.colonComments = [];
if (nestedSelectors) {
castedNode.nodes = nestedSelectors;
}
else {
delete castedNode.nodes;
}
return castedNode;
}
exports.convertToPseudoClass = convertToPseudoClass;
function createCombinatorSelector(partial) {
const type = partial.combinator || 'space';
return {
type: `combinator`,
combinator: type,
value: partial.value ?? (type === 'space' ? ` ` : type),
before: partial.before ?? ``,
after: partial.after ?? ``,
start: partial.start ?? 0,
end: partial.end ?? 0,
invalid: partial.invalid ?? false,
};
}
exports.createCombinatorSelector = createCombinatorSelector;
function isInPseudoClassContext(parents) {

@@ -97,0 +125,0 @@ for (const parent of parents) {

export { safeParse } from './parser';
export { processorDiagnostics, StylableProcessor } from './stylable-processor';
export { StylableTransformer, postProcessor, replaceValueHook, StylableExports, transformerDiagnostics, ResolvedElement, } from './stylable-transformer';
export { StylableTransformer, postProcessor, replaceValueHook, StylableExports, transformerDiagnostics, ResolvedElement, InferredSelector, } from './stylable-transformer';
export { validateDefaultConfig } from './stylable';

@@ -5,0 +5,0 @@ export { STSymbol, STImport, STGlobal, STNamespace, STCustomSelector, STCustomState, CSSClass, CSSType, CSSKeyframes, CSSLayer, CSSContains, CSSCustomProperty, } from './features';

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugableRecord = exports.processDeclarationFunctions = exports.createAtImportProps = exports.parsePseudoImport = exports.tryCollectImportsDeep = exports.createCustomValue = exports.packageNamespaceFactory = exports.createStylableFileProcessor = exports.cachedProcessFile = exports.StylableResolver = exports.reportDiagnostic = exports.emitDiagnostics = exports.parseSelectorWithCache = exports.namespaceDelimiter = exports.namespace = exports.fixRelativeUrls = exports.isRelativeNativeCss = exports.makeAbsolute = exports.isAsset = exports.knownPseudoClassesWithNestedSelectors = exports.nativePseudoElements = exports.nativePseudoClasses = exports.cssParse = exports.murmurhash3_32_gc = exports.CSSCustomProperty = exports.CSSContains = exports.CSSLayer = exports.CSSKeyframes = exports.CSSType = exports.CSSClass = exports.STCustomState = exports.STCustomSelector = exports.STNamespace = exports.STGlobal = exports.STImport = exports.STSymbol = exports.validateDefaultConfig = exports.transformerDiagnostics = exports.StylableTransformer = exports.StylableProcessor = exports.processorDiagnostics = exports.safeParse = void 0;
exports.plugableRecord = exports.processDeclarationFunctions = exports.createAtImportProps = exports.parsePseudoImport = exports.tryCollectImportsDeep = exports.createCustomValue = exports.packageNamespaceFactory = exports.createStylableFileProcessor = exports.cachedProcessFile = exports.StylableResolver = exports.reportDiagnostic = exports.emitDiagnostics = exports.parseSelectorWithCache = exports.namespaceDelimiter = exports.namespace = exports.fixRelativeUrls = exports.isRelativeNativeCss = exports.makeAbsolute = exports.isAsset = exports.knownPseudoClassesWithNestedSelectors = exports.nativePseudoElements = exports.nativePseudoClasses = exports.cssParse = exports.murmurhash3_32_gc = exports.CSSCustomProperty = exports.CSSContains = exports.CSSLayer = exports.CSSKeyframes = exports.CSSType = exports.CSSClass = exports.STCustomState = exports.STCustomSelector = exports.STNamespace = exports.STGlobal = exports.STImport = exports.STSymbol = exports.validateDefaultConfig = exports.InferredSelector = exports.transformerDiagnostics = exports.StylableTransformer = exports.StylableProcessor = exports.processorDiagnostics = exports.safeParse = void 0;
var parser_1 = require("./parser");

@@ -12,2 +12,3 @@ Object.defineProperty(exports, "safeParse", { enumerable: true, get: function () { return parser_1.safeParse; } });

Object.defineProperty(exports, "transformerDiagnostics", { enumerable: true, get: function () { return stylable_transformer_1.transformerDiagnostics; } });
Object.defineProperty(exports, "InferredSelector", { enumerable: true, get: function () { return stylable_transformer_1.InferredSelector; } });
var stylable_1 = require("./stylable");

@@ -14,0 +15,0 @@ Object.defineProperty(exports, "validateDefaultConfig", { enumerable: true, get: function () { return stylable_1.validateDefaultConfig; } });

@@ -9,3 +9,3 @@ "use strict";

// this allows @stylable/core to be bundled for browser usage without special custom configuration
const ResolverFactory_1 = __importDefault(require("enhanced-resolve/lib/ResolverFactory"));
const ResolverFactory_js_1 = __importDefault(require("enhanced-resolve/lib/ResolverFactory.js"));
function bundleSafeRequireExtensions() {

@@ -27,3 +27,3 @@ let extensions;

: bundleSafeRequireExtensions();
const eResolver = ResolverFactory_1.default.createResolver({
const eResolver = ResolverFactory_js_1.default.createResolver({
...resolveOptions,

@@ -30,0 +30,0 @@ extensions,

@@ -31,5 +31,4 @@ "use strict";

function emitDiagnostics(ctx, meta, diagnosticsMode, filePath) {
var _a, _b;
(_a = meta.diagnostics) === null || _a === void 0 ? void 0 : _a.reports.forEach(handleReport);
(_b = meta.transformDiagnostics) === null || _b === void 0 ? void 0 : _b.reports.forEach(handleReport);
meta.diagnostics?.reports.forEach(handleReport);
meta.transformDiagnostics?.reports.forEach(handleReport);
function handleReport(diagnostic) {

@@ -36,0 +35,0 @@ reportDiagnostic(ctx, diagnosticsMode, diagnostic, filePath);

@@ -34,3 +34,3 @@ "use strict";

const parent = rule.parent;
if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'rule') {
if (parent?.type === 'rule') {
this.diagnostics.report(exports.processorDiagnostics.INVALID_NESTING(rule.selector, parent.selector), { node: rule });

@@ -37,0 +37,0 @@ }

@@ -22,7 +22,6 @@ "use strict";

getModule({ context, request }) {
var _a, _b, _c;
let entity;
let resolvedPath;
const key = cacheKey(context, request);
if ((_a = this.cache) === null || _a === void 0 ? void 0 : _a.has(key)) {
if (this.cache?.has(key)) {
const entity = this.cache.get(key);

@@ -48,3 +47,3 @@ if (entity.kind === 'resolve') {

};
(_b = this.cache) === null || _b === void 0 ? void 0 : _b.set(key, entity);
this.cache?.set(key, entity);
return entity;

@@ -70,3 +69,3 @@ }

}
(_c = this.cache) === null || _c === void 0 ? void 0 : _c.set(key, entity);
this.cache?.set(key, entity);
return entity;

@@ -78,5 +77,4 @@ }

resolvePath(directoryPath, request) {
var _a, _b, _c;
const key = cacheKey(directoryPath, request);
let resolvedPath = (_b = (_a = this.cache) === null || _a === void 0 ? void 0 : _a.get(key)) === null || _b === void 0 ? void 0 : _b.resolvedPath;
let resolvedPath = this.cache?.get(key)?.resolvedPath;
if (resolvedPath !== undefined) {

@@ -86,3 +84,3 @@ return resolvedPath;

resolvedPath = this.moduleResolver(directoryPath, request);
(_c = this.cache) === null || _c === void 0 ? void 0 : _c.set(key, {
this.cache?.set(key, {
resolvedPath,

@@ -238,3 +236,3 @@ value: null,

}
else if ((deepResolved === null || deepResolved === void 0 ? void 0 : deepResolved._kind) === `js`) {
else if (deepResolved?._kind === `js`) {
resolvedSymbols.js[name] = deepResolved;

@@ -348,3 +346,3 @@ resolvedSymbols.mainNamespace[name] = `js`;

const extendPath = [];
while (current === null || current === void 0 ? void 0 : current.symbol) {
while (current?.symbol) {
if (isInPath(extendPath, current)) {

@@ -416,3 +414,3 @@ break;

else {
if (deepResolved === null || deepResolved === void 0 ? void 0 : deepResolved.symbol.alias) {
if (deepResolved?.symbol.alias) {
meta.sourceAst.walkRules(new RegExp('\\.' + name), (rule) => {

@@ -442,7 +440,6 @@ diagnostics.report(features_1.CSSClass.diagnostics.UNKNOWN_IMPORT_ALIAS(name), {

function resolveByNamespace(meta, symbol, resolver, type) {
var _a, _b;
const current = { meta, symbol };
while ('import' in current.symbol && current.symbol.import) {
const res = resolver.resolveImported(current.symbol.import, current.symbol.name, type);
if ((res === null || res === void 0 ? void 0 : res._kind) === 'css' && ((_a = res.symbol) === null || _a === void 0 ? void 0 : _a._kind) === type) {
if (res?._kind === 'css' && res.symbol?._kind === type) {
({ meta: current.meta, symbol: current.symbol } = res);

@@ -454,3 +451,3 @@ }

}
if (((_b = current.symbol) === null || _b === void 0 ? void 0 : _b._kind) === type) {
if (current.symbol?._kind === type) {
return current;

@@ -457,0 +454,0 @@ }

@@ -7,5 +7,5 @@ import * as postcss from 'postcss';

import type { StylableMeta } from './stylable-meta';
import { STSymbol } from './features';
import { CSSResolve, StylableResolverCache, StylableResolver } from './stylable-resolver';
import { CSSResolve, StylableResolverCache, StylableResolver, createSymbolResolverWithCache } from './stylable-resolver';
import type { ModuleResolver } from './types';
import type { MappedStates } from './helpers/custom-state';
export interface ResolvedElement {

@@ -52,2 +52,3 @@ name: string;

stVarOverride?: Record<string, string>;
experimentalSelectorInference?: boolean;
}

@@ -61,2 +62,3 @@ export declare const transformerDiagnostics: {

};
type PostcssContainer = postcss.Container<postcss.ChildNode> | postcss.Document;
export declare class StylableTransformer {

@@ -72,24 +74,63 @@ fileProcessor: FileProcessor<StylableMeta>;

private evaluator;
private getResolvedSymbols;
getResolvedSymbols: ReturnType<typeof createSymbolResolverWithCache>;
private directiveNodes;
experimentalSelectorInference: boolean;
containerInferredSelectorMap: Map<PostcssContainer, InferredSelector>;
constructor(options: TransformerOptions);
transform(meta: StylableMeta): StylableResults;
transformAst(ast: postcss.Root, meta: StylableMeta, metaExports?: StylableExports, stVarOverride?: Record<string, string>, path?: string[], mixinTransform?: boolean, topNestClassName?: string): void;
transformAst(ast: postcss.Root, meta: StylableMeta, metaExports?: StylableExports, stVarOverride?: Record<string, string>, path?: string[], mixinTransform?: boolean, inferredNestSelector?: InferredSelector): void;
resolveSelectorElements(meta: StylableMeta, selector: string): ResolvedElement[][];
scopeRule(meta: StylableMeta, rule: postcss.Rule, topNestClassName?: string, unwrapGlobals?: boolean): string;
scopeSelector(originMeta: StylableMeta, selector: string, rule?: postcss.Rule, topNestClassName?: string, unwrapGlobals?: boolean): {
scopeSelector(originMeta: StylableMeta, selector: string, selectorNode?: postcss.Rule | postcss.AtRule, inferredNestSelector?: InferredSelector, unwrapGlobals?: boolean): {
selector: string;
elements: ResolvedElement[][];
targetSelectorAst: SelectorList;
inferredSelector: InferredSelector;
};
createSelectorContext(meta: StylableMeta, selectorAst: SelectorList, rule: postcss.Rule, topNestClassName?: string): ScopeContext;
createSelectorContext(meta: StylableMeta, selectorAst: SelectorList, selectorNode: postcss.Rule | postcss.AtRule, selectorStr?: string, selectorNest?: InferredSelector): ScopeContext;
createInferredSelector(meta: StylableMeta, { name, type }: {
name: string;
type: 'class' | 'element';
}): InferredSelector;
scopeSelectorAst(context: ScopeContext): SelectorList;
private handleCompoundNode;
private handleCustomSelector;
}
interface ScopeAnchor {
type: 'class' | 'element' | 'pseudo-element';
name: string;
resolved: Array<CSSResolve<ClassSymbol | ElementSymbol>>;
type SelectorSymbol = ClassSymbol | ElementSymbol;
type InferredResolve = CSSResolve<SelectorSymbol>;
type InferredPseudoElement = {
inferred: InferredSelector;
selectors: SelectorList;
};
type InferredPseudoClass = {
meta: StylableMeta;
state: MappedStates[string];
};
export declare class InferredSelector {
private api;
protected resolveSet: Set<InferredResolve[]>;
constructor(api: Pick<StylableTransformer, 'getResolvedSymbols' | 'createSelectorContext' | 'scopeSelectorAst'>, resolve?: InferredResolve[] | InferredSelector);
isEmpty(): boolean;
set(resolve: InferredResolve[] | InferredSelector): void;
clone(): InferredSelector;
/**
* Adds to the set of inferred resolved CSS
* Assumes passes CSSResolved from the same meta/symbol are
* the same from the same cached transform process to dedupe them.
*/
add(resolve: InferredResolve[] | InferredSelector): void;
getPseudoClasses({ name: searchedName }?: {
name?: string;
}): Record<string, InferredPseudoClass>;
getPseudoElements({ isFirstInSelector, experimentalSelectorInference, name, }: {
isFirstInSelector: boolean;
experimentalSelectorInference: boolean;
name?: string;
}): Record<string, InferredPseudoElement>;
private matchedElement;
getSingleResolve(): InferredResolve[];
}
declare class SelectorMultiplier {
private dupIndicesPerSelector;
addSplitPoint(selectorIndex: number, nodeIndex: number, selectors: SelectorList): void;
duplicateSelectors(targetSelectors: SelectorList): void;
}
export declare class ScopeContext {

@@ -99,21 +140,27 @@ originMeta: StylableMeta;

selectorAst: SelectorList;
rule: postcss.Rule;
ruleOrAtRule: postcss.Rule | postcss.AtRule;
scopeSelectorAst: StylableTransformer['scopeSelectorAst'];
topNestClassName: string;
private transformer;
inferredSelectorNest: InferredSelector;
transform: boolean;
additionalSelectors: Array<() => Selector>;
selectorStr: string;
selectorIndex: number;
elements: any[];
selectorAstResolveMap: Map<ImmutableSelectorNode, CSSResolve<STSymbol.StylableSymbol>[]>;
selectorAstResolveMap: Map<ImmutableSelectorNode, InferredSelector>;
selector?: Selector;
compoundSelector?: CompoundSelector;
node?: CompoundSelector['nodes'][number];
currentAnchor?: ScopeAnchor;
nestingSelectorAnchor?: ScopeAnchor;
constructor(originMeta: StylableMeta, resolver: StylableResolver, selectorAst: SelectorList, rule: postcss.Rule, scopeSelectorAst: StylableTransformer['scopeSelectorAst'], topNestClassName?: string);
initRootAnchor(anchor: ScopeAnchor): void;
setNodeResolve(node: SelectorNode, resolve: CSSResolve[]): void;
setCurrentAnchor(anchor: ScopeAnchor): void;
insertDescendantCombinatorBeforePseudoElement(): void;
createNestedContext(selectorAst: SelectorList): ScopeContext;
splitSelectors: SelectorMultiplier;
lastInferredSelectorNode: SelectorNode | undefined;
isStandaloneSelector: boolean;
inferredSelectorContext: InferredSelector;
inferredSelector: InferredSelector;
inferredMultipleSelectors: InferredSelector;
constructor(originMeta: StylableMeta, resolver: StylableResolver, selectorAst: SelectorList, ruleOrAtRule: postcss.Rule | postcss.AtRule, scopeSelectorAst: StylableTransformer['scopeSelectorAst'], transformer: StylableTransformer, inferredSelectorNest: InferredSelector, selectorContext: InferredSelector, selectorStr?: string);
get experimentalSelectorInference(): boolean;
static legacyElementsTypesMapping: Record<string, string>;
setNextSelectorScope(resolved: InferredResolve[] | InferredSelector, node: SelectorNode, name?: string): void;
isFirstInSelector(node: SelectorNode): boolean;
createNestedContext(selectorAst: SelectorList, selectorContext?: InferredSelector): ScopeContext;
transformIntoMultiSelector(node: SelectorNode, selectors: SelectorList): void;
isDuplicateStScopeDiagnostic(): boolean;

@@ -120,0 +167,0 @@ }

@@ -29,5 +29,4 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.ScopeContext = exports.StylableTransformer = exports.transformerDiagnostics = void 0;
exports.ScopeContext = exports.InferredSelector = exports.StylableTransformer = exports.transformerDiagnostics = void 0;
const is_vendor_prefixed_1 = __importDefault(require("is-vendor-prefixed"));
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
const postcss = __importStar(require("postcss"));

@@ -38,2 +37,3 @@ const diagnostics_1 = require("./diagnostics");

const selector_1 = require("./helpers/selector");
const eql_1 = require("./helpers/eql");
const css_selector_parser_1 = require("@tokey/css-selector-parser");

@@ -53,2 +53,3 @@ const rule_1 = require("./helpers/rule");

this.directiveNodes = [];
this.containerInferredSelectorMap = new Map();
this.diagnostics = options.diagnostics;

@@ -59,2 +60,3 @@ this.keepValues = options.keepValues || false;

this.postProcessor = options.postProcessor;
this.experimentalSelectorInference = options.experimentalSelectorInference === true;
this.resolver = new stylable_resolver_1.StylableResolver(options.fileProcessor, options.requireModule, options.moduleResolver, options.resolverCache || new Map());

@@ -89,3 +91,5 @@ this.mode = options.mode || 'production';

features_2.STGlobal.hooks.transformInit({ context });
meta.transformedScopes = validateScopes(this, meta);
if (!this.experimentalSelectorInference) {
meta.transformedScopes = validateScopes(this, meta);
}
this.transformAst(meta.targetAst, meta, metaExports);

@@ -96,3 +100,3 @@ meta.transformDiagnostics = this.diagnostics;

}
transformAst(ast, meta, metaExports, stVarOverride = this.defaultStVarOverride, path = [], mixinTransform = false, topNestClassName = ``) {
transformAst(ast, meta, metaExports, stVarOverride = this.defaultStVarOverride, path = [], mixinTransform = false, inferredNestSelector) {
if (meta.type !== 'stylable') {

@@ -115,3 +119,3 @@ return;

};
prepareAST(transformContext, ast);
prepareAST(transformContext, ast, this.experimentalSelectorInference);
const cssClassResolve = features_2.CSSClass.hooks.transformResolve(transformResolveOptions);

@@ -130,2 +134,3 @@ const stVarResolve = features_2.STVar.hooks.transformResolve(transformResolveOptions);

resolved: {},
transformer: this,
});

@@ -138,2 +143,3 @@ }

resolved: cssVarsMapping,
transformer: this,
});

@@ -146,2 +152,3 @@ }

resolved: keyframesResolve,
transformer: this,
});

@@ -154,2 +161,3 @@ }

resolved: layerResolve,
transformer: this,
});

@@ -162,2 +170,3 @@ }

resolved: layerResolve,
transformer: this,
});

@@ -170,4 +179,21 @@ }

resolved: containsResolve,
transformer: this,
});
}
else if (name === 'st-scope') {
features_2.STScope.hooks.transformAtRuleNode({
context: transformContext,
atRule,
resolved: containsResolve,
transformer: this,
});
}
else if (name === 'custom-selector') {
features_2.STCustomSelector.hooks.transformAtRuleNode({
context: transformContext,
atRule,
resolved: containsResolve,
transformer: this,
});
}
};

@@ -220,3 +246,13 @@ const handleDeclaration = (decl) => {

}
node.selector = this.scopeRule(meta, node, topNestClassName);
// get context inferred selector
let currentParent = node.parent;
while (currentParent && !this.containerInferredSelectorMap.has(currentParent)) {
currentParent = currentParent.parent;
}
// transform selector
const { selector, inferredSelector } = this.scopeSelector(meta, node.selector, node, (currentParent && this.containerInferredSelectorMap.get(currentParent)) ||
inferredNestSelector);
// save results
this.containerInferredSelectorMap.set(node, inferredSelector);
node.selector = selector;
}

@@ -240,2 +276,5 @@ else if (node.type === 'atrule') {

};
if (this.experimentalSelectorInference) {
features_2.STScope.hooks.transformLastPass(lastPassParams);
}
features_2.STMixin.hooks.transformLastPass(lastPassParams);

@@ -280,8 +319,4 @@ if (!mixinTransform) {

}
scopeRule(meta, rule, topNestClassName, unwrapGlobals) {
return this.scopeSelector(meta, rule.selector, rule, topNestClassName, unwrapGlobals)
.selector;
}
scopeSelector(originMeta, selector, rule, topNestClassName, unwrapGlobals = false) {
const context = this.createSelectorContext(originMeta, (0, selector_1.parseSelectorWithCache)(selector, { clone: true }), rule || postcss.rule({ selector }), topNestClassName);
scopeSelector(originMeta, selector, selectorNode, inferredNestSelector, unwrapGlobals = false) {
const context = this.createSelectorContext(originMeta, (0, selector_1.parseSelectorWithCache)(selector, { clone: true }), selectorNode || postcss.rule({ selector }), selector, inferredNestSelector);
const targetSelectorAst = this.scopeSelectorAst(context);

@@ -295,22 +330,20 @@ if (unwrapGlobals) {

elements: context.elements,
inferredSelector: context.inferredMultipleSelectors,
};
}
createSelectorContext(meta, selectorAst, rule, topNestClassName) {
return new ScopeContext(meta, this.resolver, selectorAst, rule, this.scopeSelectorAst.bind(this), topNestClassName);
createSelectorContext(meta, selectorAst, selectorNode, selectorStr, selectorNest) {
const inferredContext = this.createInferredSelector(meta, {
name: meta.root,
type: 'class',
});
return new ScopeContext(meta, this.resolver, selectorAst, selectorNode, this.scopeSelectorAst.bind(this), this, selectorNest || inferredContext.clone(), inferredContext, selectorStr);
}
createInferredSelector(meta, { name, type }) {
const resolvedSymbols = this.getResolvedSymbols(meta);
const resolved = resolvedSymbols[type][name];
return new InferredSelector(this, resolved);
}
scopeSelectorAst(context) {
const { originMeta, selectorAst } = context;
// group compound selectors: .a.b .c:hover, a .c:hover -> [[[.a.b], [.c:hover]], [[.a], [.c:hover]]]
const selectorList = (0, css_selector_parser_1.groupCompoundSelectors)(selectorAst);
// resolve meta classes and elements
const resolvedSymbols = this.getResolvedSymbols(originMeta);
// set stylesheet root as the global anchor
if (!context.currentAnchor) {
context.initRootAnchor({
name: originMeta.root,
type: 'class',
resolved: resolvedSymbols.class[originMeta.root],
});
}
const startedAnchor = context.currentAnchor;
const selectorList = (0, css_selector_parser_1.groupCompoundSelectors)(context.selectorAst);
// loop over selectors

@@ -324,2 +357,7 @@ for (const selector of selectorList) {

if (node.type !== `compound_selector`) {
if (node.type === 'combinator') {
if (this.experimentalSelectorInference) {
context.setNextSelectorScope(context.inferredSelectorContext, node);
}
}
continue;

@@ -330,2 +368,11 @@ }

for (const compoundNode of node.nodes) {
if (compoundNode.type === 'universal' && this.experimentalSelectorInference) {
context.setNextSelectorScope([
{
_kind: 'css',
meta: context.originMeta,
symbol: { _kind: 'element', name: '*' },
},
], node);
}
context.node = compoundNode;

@@ -336,5 +383,7 @@ // transform node

}
// add inferred selector end to multiple selector
context.inferredMultipleSelectors.add(context.inferredSelector);
if (selectorList.length - 1 > context.selectorIndex) {
// reset current anchor
context.initRootAnchor(startedAnchor);
// reset current anchor for all except last selector
context.inferredSelector = new InferredSelector(this, context.inferredSelectorContext);
}

@@ -347,5 +396,5 @@ }

const targetAst = (0, css_selector_parser_1.splitCompoundSelectors)(selectorList);
context.additionalSelectors.forEach((addSelector) => targetAst.push(addSelector()));
context.splitSelectors.duplicateSelectors(targetAst);
for (let i = 0; i < targetAst.length; i++) {
selectorAst[i] = targetAst[i];
context.selectorAst[i] = targetAst[i];
}

@@ -355,4 +404,3 @@ return targetAst;

handleCompoundNode(context) {
const { currentAnchor, node, originMeta, topNestClassName } = context;
const resolvedSymbols = this.getResolvedSymbols(originMeta);
const { inferredSelector, node, originMeta } = context;
const transformerContext = {

@@ -386,47 +434,16 @@ meta: originMeta,

}
const len = currentAnchor.resolved.length;
const lookupStartingPoint = len === 1 /* no extends */ ? 0 : 1;
let resolved;
for (let i = lookupStartingPoint; i < len; i++) {
const { symbol, meta } = currentAnchor.resolved[i];
if (!symbol[`-st-root`]) {
continue;
}
const isFirstInSelector = context.selectorAst[context.selectorIndex].nodes[0] === node;
const customSelector = features_2.STCustomSelector.getCustomSelectorExpended(meta, node.value);
if (customSelector) {
this.handleCustomSelector(customSelector, meta, context, node.value, node, isFirstInSelector);
return;
}
const requestedPart = features_2.CSSClass.get(meta, node.value);
if (symbol.alias || !requestedPart) {
// skip alias since they cannot add parts
continue;
}
resolved = this.getResolvedSymbols(meta).class[node.value];
// first definition of a part in the extends/alias chain
context.setCurrentAnchor({
name: node.value,
type: 'pseudo-element',
resolved,
});
context.setNodeResolve(node, resolved);
const resolvedPart = (0, resolve_1.getOriginDefinition)(resolved);
const inferredElement = inferredSelector.getPseudoElements({
isFirstInSelector: context.isFirstInSelector(node),
name: node.value,
experimentalSelectorInference: this.experimentalSelectorInference,
})[node.value];
if (inferredElement) {
context.setNextSelectorScope(inferredElement.inferred, node, node.value);
if (context.transform) {
if (!resolvedPart.symbol[`-st-root`] && !isFirstInSelector) {
// insert nested combinator before internal custom element
context.insertDescendantCombinatorBeforePseudoElement();
}
features_2.CSSClass.namespaceClass(resolvedPart.meta, resolvedPart.symbol, node, originMeta);
context.transformIntoMultiSelector(node, inferredElement.selectors);
}
break;
}
if (!resolved) {
else {
// first definition of a part in the extends/alias chain
context.setCurrentAnchor({
name: node.value,
type: 'pseudo-element',
resolved: [],
});
context.setNodeResolve(node, []);
context.setNextSelectorScope([], node, node.value);
if (!native_reserved_lists_1.nativePseudoElements.includes(node.value) &&

@@ -436,3 +453,3 @@ !(0, is_vendor_prefixed_1.default)(node.value) &&

this.diagnostics.report(exports.transformerDiagnostics.UNKNOWN_PSEUDO_ELEMENT(node.value), {
node: context.rule,
node: context.ruleOrAtRule,
word: node.value,

@@ -444,3 +461,3 @@ });

else if (node.type === 'pseudo_class') {
features_2.CSSPseudoClass.hooks.transformSelectorNode({
const isCustomSelector = features_2.STCustomSelector.hooks.transformSelectorNode({
context: transformerContext,

@@ -450,58 +467,13 @@ selectorContext: context,

});
}
else if (node.type === `nesting`) {
if (context.nestingSelectorAnchor) {
context.setCurrentAnchor(context.nestingSelectorAnchor);
context.setNodeResolve(node, context.nestingSelectorAnchor.resolved);
}
else {
/**
* although it is always assumed to be class symbol, the get is done from
* the general `st-symbol` feature because the actual symbol can
* be a type-element symbol that is actually an imported root in a mixin
*/
const origin = features_2.STSymbol.get(originMeta, topNestClassName || originMeta.root); // ToDo: handle other cases
context.setCurrentAnchor({
name: origin.name,
type: origin._kind,
resolved: resolvedSymbols[origin._kind][origin.name],
if (!isCustomSelector) {
features_2.CSSPseudoClass.hooks.transformSelectorNode({
context: transformerContext,
selectorContext: context,
node,
});
context.setNodeResolve(node, resolvedSymbols[origin._kind][origin.name]);
}
}
}
handleCustomSelector(customSelector, meta, context, name, node, isFirstInSelector) {
const selectorList = (0, selector_1.parseSelectorWithCache)(customSelector, { clone: true });
const hasSingleSelector = selectorList.length === 1;
const internalContext = new ScopeContext(meta, this.resolver, removeFirstRootInFirstCompound(selectorList, meta), context.rule, this.scopeSelectorAst.bind(this));
const customAstSelectors = this.scopeSelectorAst(internalContext);
if (!isFirstInSelector) {
customAstSelectors.forEach(setSingleSpaceOnSelectorLeft);
else if (node.type === `nesting`) {
context.setNextSelectorScope(context.inferredSelectorNest, node, node.value);
}
if (hasSingleSelector && internalContext.currentAnchor) {
context.setCurrentAnchor({
name,
type: 'pseudo-element',
resolved: internalContext.currentAnchor.resolved,
});
context.setNodeResolve(node, internalContext.currentAnchor.resolved);
}
else {
// unknown context due to multiple selectors
context.setCurrentAnchor({
name,
type: 'pseudo-element',
resolved: anyElementAnchor(meta).resolved,
});
context.setNodeResolve(node, anyElementAnchor(meta).resolved);
}
if (context.transform) {
Object.assign(node, customAstSelectors[0]);
}
// first one handled inline above
for (let i = 1; i < customAstSelectors.length; i++) {
const selectorNode = context.selectorAst[context.selectorIndex];
const nodeIndex = selectorNode.nodes.indexOf(node);
context.additionalSelectors.push(lazyCreateSelector(customAstSelectors[i], selectorNode, nodeIndex));
}
}

@@ -515,3 +487,3 @@ }

const rule = postcss.rule({ selector: scope.params });
const context = new ScopeContext(meta, transformer.resolver, (0, selector_1.parseSelectorWithCache)(rule.selector, { clone: true }), rule, transformer.scopeSelectorAst.bind(transformer));
const context = transformer.createSelectorContext(meta, (0, selector_1.parseSelectorWithCache)(rule.selector, { clone: true }), rule, rule.selector);
transformedScopes[rule.selector] = (0, css_selector_parser_1.groupCompoundSelectors)(transformer.scopeSelectorAst(context));

@@ -532,111 +504,350 @@ const ruleReports = transformer.diagnostics.reports.splice(len);

}
function removeFirstRootInFirstCompound(selectorList, meta) {
const compounded = (0, css_selector_parser_1.groupCompoundSelectors)(selectorList);
for (const selector of compounded) {
const first = selector.nodes.find(({ type }) => type === `compound_selector`);
if (first && first.type === `compound_selector`) {
first.nodes = first.nodes.filter((node) => {
return !(node.type === 'class' && node.value === meta.root);
});
}
function removeFirstRootInFirstCompound(selector, meta) {
let hadRoot = false;
const compoundedSelector = (0, css_selector_parser_1.groupCompoundSelectors)(selector);
const first = compoundedSelector.nodes.find(({ type }) => type === `compound_selector`);
if (first) {
first.nodes = first.nodes.filter((node) => {
if (node.type === 'class' && node.value === meta.root) {
hadRoot = true;
return false;
}
return true;
});
}
return (0, css_selector_parser_1.splitCompoundSelectors)(compounded);
return { selector: (0, css_selector_parser_1.splitCompoundSelectors)(compoundedSelector), hadRoot };
}
function setSingleSpaceOnSelectorLeft(n) {
n.before = ` `;
let parent = n;
let nextLeft = n.nodes[0];
while (nextLeft) {
if (`before` in nextLeft) {
nextLeft.before = ``;
class InferredSelector {
constructor(api, resolve) {
this.api = api;
this.resolveSet = new Set();
if (resolve) {
this.add(resolve);
}
if (nextLeft.type === `selector`) {
nextLeft = nextLeft.nodes[0];
parent = nextLeft;
}
isEmpty() {
return this.resolveSet.size === 0;
}
set(resolve) {
if (resolve === this) {
return;
}
else if (nextLeft.type === `combinator` && nextLeft.combinator === `space`) {
parent.nodes.shift();
nextLeft = parent.nodes[0];
this.resolveSet.clear();
this.add(resolve);
}
clone() {
return new InferredSelector(this.api, this);
}
/**
* Adds to the set of inferred resolved CSS
* Assumes passes CSSResolved from the same meta/symbol are
* the same from the same cached transform process to dedupe them.
*/
add(resolve) {
if (resolve instanceof InferredSelector) {
resolve.resolveSet.forEach((resolve) => this.add(resolve));
}
else {
return;
this.resolveSet.add(resolve);
}
}
getPseudoClasses({ name: searchedName } = {}) {
const collectedStates = {};
const resolvedCount = {};
const expectedIntersectionCount = this.resolveSet.size; // ToDo: dec for any types
const addInferredState = (name, meta, state) => {
const existing = collectedStates[name];
if (!existing) {
collectedStates[name] = { meta, state };
resolvedCount[name] = 1;
}
else {
const isStatesEql = (0, eql_1.isEqual)(existing.state, state);
if (isStatesEql &&
// states from same meta
(existing.meta === meta ||
// global states
typeof state === 'string' ||
state?.type === 'template')) {
resolvedCount[name]++;
}
}
};
// infer states from multiple resolved selectors
for (const resolvedContext of this.resolveSet.values()) {
const resolvedFoundNames = new Set();
resolved: for (const { symbol, meta } of resolvedContext) {
const states = symbol[`-st-states`];
if (!states) {
continue;
}
if (searchedName) {
if (Object.hasOwnProperty.call(states, searchedName)) {
// track state
addInferredState(searchedName, meta, states[searchedName]);
break resolved;
}
}
else {
// get all states
for (const [name, state] of Object.entries(states)) {
if (!resolvedFoundNames.has(name)) {
// track state
resolvedFoundNames.add(name);
addInferredState(name, meta, state);
}
}
}
}
}
// strict: remove states that do not exist on ALL resolved selectors
return expectedIntersectionCount > 1
? Object.entries(collectedStates).reduce((resultStates, [name, InferredState]) => {
if (resolvedCount[name] >= expectedIntersectionCount) {
resultStates[name] = InferredState;
}
return resultStates;
}, {})
: collectedStates;
}
getPseudoElements({ isFirstInSelector, experimentalSelectorInference, name, }) {
const collectedElements = {};
const resolvedCount = {};
const checked = {};
const expectedIntersectionCount = this.resolveSet.size; // ToDo: dec for any types
const addInferredElement = (name, inferred, selectors) => {
const item = (collectedElements[name] || (collectedElements[name] = {
inferred: new InferredSelector(this.api),
selectors: [],
}));
// check inferred matching
if (!item.inferred.matchedElement(inferred)) {
// ToDo: bailout fast
return;
}
// add match
resolvedCount[name]++;
item.inferred.add(inferred);
item.selectors.push(...selectors);
};
// infer elements from multiple resolved selectors
for (const resolvedContext of this.resolveSet.values()) {
/**
* search for elements in each resolved selector.
* start at 1 for extended symbols to prefer inherited elements over local
*/
const startIndex = resolvedContext.length === 1 ? 0 : 1;
resolved: for (let i = startIndex; i < resolvedContext.length; i++) {
const { symbol, meta } = resolvedContext[i];
if (!symbol['-st-root'] || symbol.alias) {
// non-root & alias classes don't have parts: bailout
continue;
}
if (name) {
resolvedCount[name] ?? (resolvedCount[name] = 0);
checked[name] || (checked[name] = new Set());
const uniqueId = meta.source + '::' + name;
if (checked[name].has(uniqueId)) {
resolvedCount[name]++;
continue;
}
checked[name].add(uniqueId);
// prefer custom selector
const customSelector = features_2.STCustomSelector.getCustomSelectorExpended(meta, name);
if (customSelector) {
const selectorList = (0, selector_1.parseSelectorWithCache)(customSelector, {
clone: true,
});
selectorList.forEach((selector) => {
const r = removeFirstRootInFirstCompound(selector, meta);
selector.nodes = r.selector.nodes;
selector.before = '';
if (!r.hadRoot && !isFirstInSelector) {
selector.nodes.unshift((0, selector_1.createCombinatorSelector)({ combinator: 'space' }));
}
});
const internalContext = this.api.createSelectorContext(meta, selectorList, postcss.rule({ selector: customSelector }), customSelector);
internalContext.isStandaloneSelector = isFirstInSelector;
const customAstSelectors = this.api.scopeSelectorAst(internalContext);
const inferred = customAstSelectors.length === 1 || experimentalSelectorInference
? internalContext.inferredMultipleSelectors
: new InferredSelector(this.api, [
{
_kind: 'css',
meta,
symbol: { _kind: 'element', name: '*' },
},
]);
addInferredElement(name, inferred, customAstSelectors);
break resolved;
}
// matching class part
const classSymbol = features_2.CSSClass.get(meta, name);
if (classSymbol) {
const resolvedPart = this.api.getResolvedSymbols(meta).class[name];
const resolvedBaseSymbol = (0, resolve_1.getOriginDefinition)(resolvedPart);
const nodes = [];
// insert descendant combinator before internal custom element
if (!resolvedBaseSymbol.symbol[`-st-root`] && !isFirstInSelector) {
nodes.push((0, selector_1.createCombinatorSelector)({ combinator: 'space' }));
}
// create part class
const classNode = {};
features_2.CSSClass.namespaceClass(resolvedBaseSymbol.meta, resolvedBaseSymbol.symbol, classNode);
nodes.push(classNode);
addInferredElement(name, new InferredSelector(this.api, resolvedPart), [
{ type: 'selector', after: '', before: '', end: 0, start: 0, nodes },
]);
break resolved;
}
}
else {
// ToDo: implement get all elements
}
}
}
// strict: remove elements that do not exist on ALL resolved selectors
return expectedIntersectionCount > 1
? Object.entries(collectedElements).reduce((resultElements, [name, InferredElement]) => {
if (resolvedCount[name] >= expectedIntersectionCount) {
resultElements[name] = InferredElement;
}
return resultElements;
}, {})
: collectedElements;
}
matchedElement(inferred) {
for (const target of this.resolveSet) {
const targetBaseElementSymbol = (0, resolve_1.getOriginDefinition)(target);
for (const tested of inferred.resolveSet) {
const testedBaseElementSymbol = (0, resolve_1.getOriginDefinition)(tested);
if (targetBaseElementSymbol !== testedBaseElementSymbol) {
return false;
}
}
}
return true;
}
// function to temporarily handle single resolved selector type while refactoring
// ToDo: remove temporarily single resolve
getSingleResolve() {
if (this.resolveSet.size !== 1) {
return [];
}
return this.resolveSet.values().next().value;
}
}
function anyElementAnchor(meta) {
return {
type: 'element',
name: '*',
resolved: [{ _kind: 'css', meta, symbol: { _kind: 'element', name: '*' } }],
};
}
function lazyCreateSelector(customElementChunk, selectorNode, nodeIndex) {
if (nodeIndex === -1) {
throw new Error('not supported inside nested classes');
exports.InferredSelector = InferredSelector;
class SelectorMultiplier {
constructor() {
this.dupIndicesPerSelector = [];
}
return () => {
const clone = (0, lodash_clonedeep_1.default)(selectorNode);
clone.nodes[nodeIndex].nodes = customElementChunk.nodes;
return clone;
};
addSplitPoint(selectorIndex, nodeIndex, selectors) {
var _a;
if (selectors.length) {
(_a = this.dupIndicesPerSelector)[selectorIndex] || (_a[selectorIndex] = []);
this.dupIndicesPerSelector[selectorIndex].push([nodeIndex, selectors]);
}
}
duplicateSelectors(targetSelectors) {
// iterate top level selector
for (const [selectorIndex, insertionPoints] of Object.entries(this.dupIndicesPerSelector)) {
const duplicationList = [targetSelectors[Number(selectorIndex)]];
// iterate insertion points
for (const [nodeIndex, selectors] of insertionPoints) {
// collect the duplicate selectors to be multiplied by following insertion points
const added = [];
// iterate selectors for insertion point
for (const replaceSelector of selectors) {
// duplicate selectors and replace selector at insertion point
for (const originSelector of duplicationList) {
const dupSelector = { ...originSelector, nodes: [...originSelector.nodes] };
dupSelector.nodes[nodeIndex] = replaceSelector;
added.push(dupSelector);
}
}
// add the duplicated selectors from insertion point to
// the list of selector to be duplicated for following insertion
// points and to the target selector list
for (const addedSelector of added) {
duplicationList.push(addedSelector);
targetSelectors.push(addedSelector);
}
}
}
}
}
class ScopeContext {
constructor(originMeta, resolver, selectorAst, rule, scopeSelectorAst, topNestClassName = ``) {
constructor(originMeta, resolver, selectorAst, ruleOrAtRule, scopeSelectorAst, transformer, inferredSelectorNest, selectorContext, selectorStr) {
this.originMeta = originMeta;
this.resolver = resolver;
this.selectorAst = selectorAst;
this.rule = rule;
this.ruleOrAtRule = ruleOrAtRule;
this.scopeSelectorAst = scopeSelectorAst;
this.topNestClassName = topNestClassName;
this.transformer = transformer;
this.inferredSelectorNest = inferredSelectorNest;
this.transform = true;
this.additionalSelectors = [];
// source multi-selector input
this.selectorStr = '';
this.selectorIndex = -1;
this.elements = [];
this.selectorAstResolveMap = new Map();
// store selector duplication points
this.splitSelectors = new SelectorMultiplier();
// selector is not a continuation of another selector
this.isStandaloneSelector = true;
// combined type of the multiple selectors
this.inferredMultipleSelectors = new InferredSelector(this.transformer);
this.selectorStr = selectorStr || (0, selector_1.stringifySelector)(selectorAst);
this.inferredSelectorContext = new InferredSelector(this.transformer, selectorContext);
this.inferredSelector = new InferredSelector(this.transformer, this.inferredSelectorContext);
}
initRootAnchor(anchor) {
this.currentAnchor = anchor;
get experimentalSelectorInference() {
return this.transformer.experimentalSelectorInference;
}
setNodeResolve(node, resolve) {
this.selectorAstResolveMap.set(node, resolve);
}
setCurrentAnchor(anchor) {
if (this.selectorIndex !== undefined && this.selectorIndex !== -1) {
this.elements[this.selectorIndex].push(anchor);
setNextSelectorScope(resolved, node, name) {
if (name && this.selectorIndex !== undefined && this.selectorIndex !== -1) {
this.elements[this.selectorIndex].push({
type: ScopeContext.legacyElementsTypesMapping[node.type] || 'unknown',
name,
resolved: Array.isArray(resolved) ? resolved : resolved.getSingleResolve(),
});
}
this.currentAnchor = anchor;
this.inferredSelector.set(resolved);
this.selectorAstResolveMap.set(node, this.inferredSelector.clone());
this.lastInferredSelectorNode = node;
}
insertDescendantCombinatorBeforePseudoElement() {
if (this.selector &&
this.compoundSelector &&
this.node &&
this.node.type === `pseudo_element`) {
if (this.compoundSelector.nodes[0] === this.node) {
const compoundIndex = this.selector.nodes.indexOf(this.compoundSelector);
this.selector.nodes.splice(compoundIndex, 0, {
type: `combinator`,
combinator: `space`,
value: ` `,
before: ``,
after: ``,
start: this.node.start,
end: this.node.start,
invalid: false,
});
}
isFirstInSelector(node) {
const isFirstNode = this.selectorAst[this.selectorIndex].nodes[0] === node;
if (isFirstNode && this.selectorIndex === 0 && !this.isStandaloneSelector) {
// force false incase a this context is a splitted part from another selector
return false;
}
return isFirstNode;
}
createNestedContext(selectorAst) {
const ctx = new ScopeContext(this.originMeta, this.resolver, selectorAst, this.rule, this.scopeSelectorAst, this.topNestClassName);
Object.assign(ctx, this);
ctx.selectorAst = selectorAst;
ctx.selectorIndex = -1;
ctx.elements = [];
ctx.additionalSelectors = [];
createNestedContext(selectorAst, selectorContext) {
const ctx = new ScopeContext(this.originMeta, this.resolver, selectorAst, this.ruleOrAtRule, this.scopeSelectorAst, this.transformer, this.inferredSelectorNest, selectorContext || this.inferredSelectorContext);
ctx.transform = this.transform;
ctx.selectorAstResolveMap = this.selectorAstResolveMap;
return ctx;
}
transformIntoMultiSelector(node, selectors) {
// transform into the first selector
Object.assign(node, selectors[0]);
// keep track of additional selectors for
// duplication at the end of the selector transform
selectors.shift();
const selectorNode = this.selectorAst[this.selectorIndex];
const nodeIndex = selectorNode.nodes.indexOf(node);
this.splitSelectors.addSplitPoint(this.selectorIndex, nodeIndex, selectors);
}
isDuplicateStScopeDiagnostic() {
var _a;
if (this.experimentalSelectorInference || this.ruleOrAtRule.type !== 'rule') {
// this check is not required when experimentalSelectorInference is on
// as @st-scope is not flatten at the beginning of the transformation
// and diagnostics on it's selector is only checked once.
return false;
}
// ToDo: should be removed once st-scope transformation moves to the end of the transform process
const transformedScope = (_a = this.originMeta.transformedScopes) === null || _a === void 0 ? void 0 : _a[(0, postcss_ast_extension_1.getRuleScopeSelector)(this.rule) || ``];
const transformedScope = this.originMeta.transformedScopes?.[(0, postcss_ast_extension_1.getRuleScopeSelector)(this.ruleOrAtRule) || ``];
if (transformedScope && this.selector && this.compoundSelector) {

@@ -661,2 +872,7 @@ const currentCompoundSelector = (0, selector_1.stringifySelector)(this.compoundSelector);

}
ScopeContext.legacyElementsTypesMapping = {
pseudo_element: 'pseudo-element',
class: 'class',
type: 'element',
};
exports.ScopeContext = ScopeContext;

@@ -668,3 +884,3 @@ /**

*/
function prepareAST(context, ast) {
function prepareAST(context, ast, experimentalSelectorInference) {
// ToDo: inline transformations

@@ -676,5 +892,9 @@ const toRemove = [];

features_2.STImport.hooks.prepareAST(input);
features_2.STScope.hooks.prepareAST(input);
if (!experimentalSelectorInference) {
features_2.STScope.hooks.prepareAST(input);
}
features_2.STVar.hooks.prepareAST(input);
features_2.STCustomSelector.hooks.prepareAST(input);
if (!experimentalSelectorInference) {
features_2.STCustomSelector.hooks.prepareAST(input);
}
features_2.CSSCustomProperty.hooks.prepareAST(input);

@@ -681,0 +901,0 @@ });

@@ -62,3 +62,3 @@ "use strict";

else {
report === null || report === void 0 ? void 0 : report.report(exports.utilDiagnostics.INVALID_MERGE_OF(node.toString()), {
report?.report(exports.utilDiagnostics.INVALID_MERGE_OF(node.toString()), {
node: rule,

@@ -65,0 +65,0 @@ });

@@ -29,2 +29,3 @@ import type { CacheItem, FileProcessor, MinimalFS } from './cached-process-file';

fileProcessorCache?: Record<string, CacheItem<StylableMeta>>;
experimentalSelectorInference?: boolean;
}

@@ -58,2 +59,3 @@ export declare function validateDefaultConfig(defaultConfigObj: any): void;

protected fileProcessorCache?: Record<string, CacheItem<StylableMeta>>;
private experimentalSelectorInference;
constructor(config: StylableConfig);

@@ -60,0 +62,0 @@ getDependencies(meta: StylableMeta): Dependency[];

@@ -38,3 +38,7 @@ "use strict";

// This defines and validates known configs for the defaultConfig in 'stylable.config.js
const globalDefaultSupportedConfigs = new Set(['resolveModule', 'resolveNamespace']);
const globalDefaultSupportedConfigs = new Set([
'resolveModule',
'resolveNamespace',
'experimentalSelectorInference',
]);
function validateDefaultConfig(defaultConfigObj) {

@@ -58,2 +62,3 @@ if (typeof defaultConfigObj === 'object') {

this.diagnostics = new diagnostics_1.Diagnostics();
this.experimentalSelectorInference = !!config.experimentalSelectorInference;
this.projectRoot = config.projectRoot;

@@ -123,2 +128,3 @@ this.fileSystem = config.fileSystem;

mode: this.mode,
experimentalSelectorInference: this.experimentalSelectorInference,
...options,

@@ -125,0 +131,0 @@ });

@@ -17,3 +17,3 @@ "use strict";

const res = resolver.resolveImported(imported, '');
if ((res === null || res === void 0 ? void 0 : res._kind) === 'css' && !visited.has(res.meta.source)) {
if (res?._kind === 'css' && !visited.has(res.meta.source)) {
visited.add(res.meta.source);

@@ -30,3 +30,3 @@ const dependency = {

}
else if ((res === null || res === void 0 ? void 0 : res._kind) === 'js') {
else if (res?._kind === 'js') {
const resolvedPath = resolver.resolvePath(imported.context, imported.request);

@@ -33,0 +33,0 @@ if (!visited.has(resolvedPath)) {

{
"name": "@stylable/core",
"version": "5.8.0",
"version": "5.9.0",
"description": "CSS for Components",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -7,3 +7,2 @@ import { createFeature, FeatureContext, FeatureTransformContext } from './feature';

import type { ElementSymbol } from './css-type';
import * as STGlobal from './st-global';
import * as STCustomState from './st-custom-state';

@@ -15,3 +14,2 @@ import { getOriginDefinition } from '../helpers/resolve';

import {
convertToSelector,
convertToClass,

@@ -21,2 +19,4 @@ stringifySelector,

parseSelectorWithCache,
convertToPseudoClass,
convertToSelector,
} from '../helpers/selector';

@@ -208,4 +208,3 @@ import { getAlias } from '../stylable-utils';

];
selectorContext.setCurrentAnchor({ name: node.value, type: 'class', resolved });
selectorContext.setNodeResolve(node, resolved);
selectorContext.setNextSelectorScope(resolved, node, node.value);
const { symbol, meta } = getOriginDefinition(resolved);

@@ -215,3 +214,4 @@ if (selectorContext.originMeta === meta && symbol[`-st-states`]) {

validateRuleStateDefinition(
selectorContext.rule,
selectorContext.selectorStr,
selectorContext.ruleOrAtRule,
context.meta,

@@ -223,3 +223,3 @@ resolver,

if (selectorContext.transform) {
namespaceClass(meta, symbol, node, originMeta);
namespaceClass(meta, symbol, node);
}

@@ -254,3 +254,3 @@ },

};
namespaceClass(resolved.meta, resolved.symbol, node, meta);
namespaceClass(resolved.meta, resolved.symbol, node, false);
return stringifySelectorAst(node);

@@ -305,11 +305,23 @@ }

node: SelectorNode, // ToDo: check this is the correct type, should this be inline selector?
originMeta: StylableMeta
wrapInGlobal = true
) {
if (`-st-global` in symbol && symbol[`-st-global`]) {
// change node to `-st-global` value
const flatNode = convertToSelector(node);
const globalMappedNodes = symbol[`-st-global`]!;
flatNode.nodes = globalMappedNodes;
// ToDo: check if this is causes an issue with globals from an imported alias
STGlobal.addGlobals(originMeta, globalMappedNodes);
if (wrapInGlobal) {
const globalMappedNodes = symbol[`-st-global`]!;
convertToPseudoClass(node, 'global', [
{
type: 'selector',
nodes: globalMappedNodes,
after: '',
before: '',
end: 0,
start: 0,
},
]);
} else {
const flatNode = convertToSelector(node);
const globalMappedNodes = symbol[`-st-global`]!;
flatNode.nodes = globalMappedNodes;
}
} else {

@@ -316,0 +328,0 @@ node = convertToClass(node);

import { createFeature } from './feature';
import { nativePseudoClasses } from '../native-reserved-lists';
import * as STCustomState from './st-custom-state';
import * as STCustomSelector from './st-custom-selector';
import { createDiagnosticReporter } from '../diagnostics';
import { parseSelectorWithCache } from '../helpers/selector';
import type { Selector } from '@tokey/css-selector-parser';

@@ -22,3 +20,3 @@ import isVendorPrefixed from 'is-vendor-prefixed';

transformSelectorNode({ context, selectorContext }) {
const { currentAnchor, node, rule, scopeSelectorAst } = selectorContext;
const { inferredSelector, node, ruleOrAtRule, scopeSelectorAst } = selectorContext;
if (node.type !== 'pseudo_class') {

@@ -28,45 +26,20 @@ return;

// find matching custom state
let foundCustomState = false;
for (const { symbol, meta } of currentAnchor.resolved) {
// Handle node resolve mapping for custom-selector.
// Currently custom selectors cannot get to this point in the process,
// due to them being replaced at the beginning of the transform process.
// However by using an internal process to analyze the context of selectors for
// the language service, a source selector can reach this point without the initial
// transform. This code keeps the custom selector untouched, but registers the AST it resolves to.
// ToDo: in the future we want to move the custom selector transformation inline, or remove it all together.
const customSelector =
node.value.startsWith('--') &&
symbol['-st-root'] &&
STCustomSelector.getCustomSelectorExpended(meta, node.value.slice(2));
if (customSelector) {
const mappedSelectorAst = parseSelectorWithCache(customSelector, { clone: true });
const mappedContext = selectorContext.createNestedContext(mappedSelectorAst);
// ToDo: wrap in :is() to get intersection of selectors
scopeSelectorAst(mappedContext);
if (mappedContext.currentAnchor) {
selectorContext.setNodeResolve(node, mappedContext.currentAnchor.resolved);
}
return; // this is not a state
const name = node.value;
const inferredState = inferredSelector.getPseudoClasses({ name })[name];
const foundCustomState = !!inferredState;
if (inferredState) {
if (selectorContext.transform) {
STCustomState.transformPseudoClassToCustomState(
inferredState.state,
inferredState.meta,
node.value,
node,
inferredState.meta.namespace,
context.resolver,
context.diagnostics,
ruleOrAtRule
);
}
//
const states = symbol[`-st-states`];
if (states && Object.hasOwnProperty.call(states, node.value)) {
foundCustomState = true;
// transform custom state
if (selectorContext.transform) {
STCustomState.transformPseudoClassToCustomState(
states,
meta,
node.value,
node,
meta.namespace,
context.resolver,
context.diagnostics,
rule
);
}
break;
}
}
// handle nested pseudo classes

@@ -76,4 +49,19 @@ if (node.nodes && !foundCustomState) {

// ignore `:st-global` since it is handled after the mixin transformation
if (selectorContext.experimentalSelectorInference) {
selectorContext.setNextSelectorScope(
[
{
_kind: 'css',
meta: context.meta,
symbol: { _kind: 'element', name: '*' },
},
],
node
);
}
return;
} else {
const hasSubSelectors = node.value.match(
/not|any|-\w+?-any|matches|is|where|has|local|nth-child|nth-last-child/
);
// pickup all nested selectors except nth initial selector

@@ -83,11 +71,26 @@ const innerSelectors = (

) as Selector[];
const nestedContext = selectorContext.createNestedContext(innerSelectors);
const nestedContext = selectorContext.createNestedContext(
innerSelectors,
selectorContext.inferredSelector
);
scopeSelectorAst(nestedContext);
/**
* ToDo: remove once elements is deprecated!
* support deprecated elements.
* used to flatten nested elements for some native pseudo classes.
*/
if (node.value.match(/not|any|-\w+?-any|matches|is|where|has|local/)) {
// delegate elements of first selector
// change selector inference
if (hasSubSelectors && innerSelectors.length) {
if (
selectorContext.experimentalSelectorInference &&
!node.value.match(/not|has/)
) {
// set inferred to subject of nested selectors + prev compound
const prevNode = selectorContext.lastInferredSelectorNode;
if (prevNode && prevNode.type !== 'combinator') {
nestedContext.inferredMultipleSelectors.add(
selectorContext.inferredSelector
);
}
selectorContext.setNextSelectorScope(
nestedContext.inferredMultipleSelectors,
node
);
}
// legacy: delegate elements of first selector
selectorContext.elements[selectorContext.selectorIndex].push(

@@ -107,3 +110,3 @@ ...nestedContext.elements[0]

context.diagnostics.report(diagnostics.UNKNOWN_STATE_USAGE(node.value), {
node: rule,
node: ruleOrAtRule,
word: node.value,

@@ -110,0 +113,0 @@ });

@@ -73,4 +73,3 @@ import { createFeature, FeatureContext } from './feature';

}
selectorContext.setCurrentAnchor({ name: node.value, type: 'element', resolved });
selectorContext.setNodeResolve(node, resolved);
selectorContext.setNextSelectorScope(resolved, node, node.value);
// native node does not resolve e.g. div

@@ -80,3 +79,3 @@ if (selectorContext.transform && resolved && resolved.length > 1) {

if (symbol._kind === 'class') {
CSSClass.namespaceClass(meta, symbol, node, selectorContext.originMeta);
CSSClass.namespaceClass(meta, symbol, node);
} else {

@@ -83,0 +82,0 @@ node.value = symbol.name;

@@ -72,2 +72,4 @@ import type { StylableMeta } from '../stylable-meta';

resolved: T['RESOLVED'];
// ToDo: move to FeatureTransformContext
transformer: StylableTransformer;
}) => void;

@@ -78,3 +80,3 @@ transformSelectorNode: (options: {

selectorContext: Required<ScopeContext>;
}) => void;
}) => boolean | void;
transformDeclaration: (options: {

@@ -81,0 +83,0 @@ context: FeatureTransformContext;

@@ -79,2 +79,4 @@ import { plugableRecord } from '../helpers/plugable-record';

prepareAST({ context, node, toRemove }) {
// called without experimentalSelectorInference
// split selectors & remove definitions
if (node.type === 'rule' && node.selector.match(CUSTOM_SELECTOR_RE)) {

@@ -89,2 +91,25 @@ node.selector = transformCustomSelectorInline(context.meta, node.selector, {

},
transformSelectorNode({ context, selectorContext, node }) {
const customSelector =
node.value.startsWith('--') &&
getCustomSelectorExpended(context.meta, node.value.slice(2));
if (customSelector) {
const mappedSelectorAst = parseSelectorWithCache(customSelector, { clone: true });
const mappedContext = selectorContext.createNestedContext(mappedSelectorAst);
selectorContext.scopeSelectorAst(mappedContext);
const inferredSelector = selectorContext.experimentalSelectorInference
? mappedContext.inferredMultipleSelectors
: mappedContext.inferredSelector;
selectorContext.setNextSelectorScope(inferredSelector, node); // doesn't add to the resolved elements
if (selectorContext.transform) {
selectorContext.transformIntoMultiSelector(node, mappedSelectorAst);
}
}
return !!customSelector;
},
transformAtRuleNode({ atRule }) {
if (atRule.name === 'custom-selector') {
atRule.remove();
}
},
});

@@ -91,0 +116,0 @@

@@ -11,8 +11,3 @@ import { createFeature } from './feature';

import type { StylableMeta } from '../stylable-meta';
import type {
SelectorNode,
ImmutableSelectorNode,
SelectorList,
PseudoClass,
} from '@tokey/css-selector-parser';
import type { ImmutableSelectorNode, SelectorList } from '@tokey/css-selector-parser';
import { createDiagnosticReporter } from '../diagnostics';

@@ -112,4 +107,7 @@ import type * as postcss from 'postcss';

const selectorAst = parseSelectorWithCache(r.selector, { clone: true });
const globals = unwrapPseudoGlobals(selectorAst);
addGlobals(meta, globals);
walkSelector(unwrapPseudoGlobals(selectorAst), (inner) => {
if (inner.type === 'class') {
meta.globals[inner.value] = true;
}
});
r.selector = stringifySelector(selectorAst);

@@ -134,8 +132,7 @@ });

export function unwrapPseudoGlobals(selectorAst: SelectorList) {
const collectedGlobals: PseudoClass[] = [];
const collectedGlobals: SelectorList = [];
walkSelector(selectorAst, (node) => {
if (node.type === 'pseudo_class' && node.value === 'global') {
collectedGlobals.push(node);
if (node.nodes?.length === 1) {
flattenFunctionalSelector(node);
collectedGlobals.push(flattenFunctionalSelector(node));
}

@@ -148,12 +145,1 @@ return walkSelector.skipNested;

}
export function addGlobals(meta: StylableMeta, selectorAst: SelectorNode[]) {
for (const ast of selectorAst) {
walkSelector(ast, (inner) => {
if (inner.type === 'class') {
// ToDo: consider if to move to css-class feature.
meta.globals[inner.value] = true;
}
});
}
}

@@ -14,3 +14,3 @@ import { createFeature, FeatureTransformContext } from './feature';

import * as postcss from 'postcss';
import { FunctionNode, WordNode, stringify } from 'postcss-value-parser';
import postcssValueParser, { FunctionNode, WordNode } from 'postcss-value-parser';
import { fixRelativeUrls } from '../stylable-assets';

@@ -186,3 +186,5 @@ import { isValidDeclaration, mergeRules, utilDiagnostics } from '../stylable-utils';

args:
data.valueNode?.type === 'function' ? stringify(data.valueNode.nodes) : '',
data.valueNode?.type === 'function'
? postcssValueParser.stringify(data.valueNode.nodes)
: '',
});

@@ -446,3 +448,3 @@ }

resolveChain,
({ mixinRoot, resolvedClass, isRootMixin }) => {
({ mixinRoot, resolved, isRootMixin }) => {
const stVarOverride = context.evaluator.stVarOverride || {};

@@ -468,3 +470,3 @@ const mixDef = config.mixDef;

collectOptionalArgs(
{ meta: resolvedClass.meta, resolver: context.resolver },
{ meta: resolved.meta, resolver: context.resolver },
mixinRoot,

@@ -474,5 +476,5 @@ optionalArgs

// transform mixin
const mixinMeta: StylableMeta = resolvedClass.meta;
const mixinMeta: StylableMeta = resolved.meta;
const symbolName =
isRootMixin && resolvedClass.meta !== context.meta ? 'default' : mixDef.data.type;
isRootMixin && resolved.meta !== context.meta ? 'default' : mixDef.data.type;
config.transformer.transformAst(

@@ -485,5 +487,8 @@ mixinRoot,

true,
resolvedClass.symbol.name
config.transformer.createInferredSelector(mixinMeta, {
name: resolved.symbol.name,
type: resolved.symbol._kind,
})
);
fixRelativeUrls(mixinRoot, resolvedClass.meta.source, context.meta.source);
fixRelativeUrls(mixinRoot, resolved.meta.source, context.meta.source);
}

@@ -530,3 +535,3 @@ );

mixinRoot: postcss.Root;
resolvedClass: CSSResolve;
resolved: CSSResolve<ClassSymbol | ElementSymbol>;
isRootMixin: boolean;

@@ -545,3 +550,3 @@ }) => void

);
processMixinRoot({ mixinRoot, resolvedClass: resolved, isRootMixin });
processMixinRoot({ mixinRoot, resolved, isRootMixin });
roots.push(mixinRoot);

@@ -548,0 +553,0 @@ if (resolved.symbol[`-st-extends`]) {

@@ -40,2 +40,4 @@ import { createFeature } from './feature';

prepareAST({ node, toRemove }) {
// called without experimentalSelectorInference
// flatten @st-scope before transformation
if (isStScopeStatement(node)) {

@@ -46,2 +48,27 @@ flattenScope(node);

},
transformAtRuleNode({ context: { meta }, atRule, transformer }) {
if (isStScopeStatement(atRule)) {
const { selector, inferredSelector } = transformer.scopeSelector(
meta,
atRule.params,
atRule
);
// transform selector in params
atRule.params = selector;
// track selector context for nested selector nodes
transformer.containerInferredSelectorMap.set(atRule, inferredSelector);
}
},
transformLastPass({ ast }) {
// called with experimentalSelectorInference=true
// flatten @st-scope after transformation
const toRemove = [];
for (const node of ast.nodes) {
if (isStScopeStatement(node)) {
flattenScope(node);
toRemove.push(() => node.replaceWith(node.nodes || []));
}
}
toRemove.forEach((remove) => remove());
},
});

@@ -48,0 +75,0 @@

@@ -695,3 +695,4 @@ import type * as postcss from 'postcss';

export function validateRuleStateDefinition(
rule: postcss.Rule,
selector: string,
selectorNode: postcss.Rule | postcss.AtRule,
meta: StylableMeta,

@@ -701,4 +702,3 @@ resolver: StylableResolver,

) {
const parentRule = rule;
const selectorAst = parseSelectorWithCache(parentRule.selector);
const selectorAst = parseSelectorWithCache(selector);
if (selectorAst.length && selectorAst.length === 1) {

@@ -724,3 +724,3 @@ const singleSelectorAst = selectorAst[0];

diagnostics,
parentRule,
selectorNode,
true,

@@ -730,3 +730,3 @@ !!stateParam.defaultValue

if (errors) {
rule.walkDecls((decl) => {
selectorNode.walkDecls((decl) => {
if (decl.prop === `-st-states`) {

@@ -764,3 +764,3 @@ diagnostics.report(

diagnostics: Diagnostics,
rule?: postcss.Rule,
selectorNode?: postcss.Node,
validateDefinition?: boolean,

@@ -770,3 +770,9 @@ validateValue = true

const resolvedValidations: StateResult = {
res: resolveParam(meta, resolver, diagnostics, rule, value || stateAst.defaultValue),
res: resolveParam(
meta,
resolver,
diagnostics,
selectorNode,
value || stateAst.defaultValue
),
errors: null,

@@ -783,3 +789,3 @@ };

stateAst.arguments,
resolveParam.bind(null, meta, resolver, diagnostics, rule),
resolveParam.bind(null, meta, resolver, diagnostics, selectorNode),
!!validateDefinition,

@@ -800,25 +806,40 @@ validateValue

export function transformPseudoClassToCustomState(
states: MappedStates,
stateDef: MappedStates[string],
meta: StylableMeta,
name: string,
node: PseudoClass,
stateNode: PseudoClass,
namespace: string,
resolver: StylableResolver,
diagnostics: Diagnostics,
rule?: postcss.Rule
selectorNode?: postcss.Node
) {
const stateDef = states[name];
if (stateDef === null) {
convertToClass(node).value = createBooleanStateClassName(name, namespace);
delete node.nodes;
convertToClass(stateNode).value = createBooleanStateClassName(name, namespace);
delete stateNode.nodes;
} else if (typeof stateDef === 'string') {
// simply concat global mapped selector - ToDo: maybe change to 'selector'
convertToInvalid(node).value = stateDef;
delete node.nodes;
convertToInvalid(stateNode).value = stateDef;
delete stateNode.nodes;
} else if (typeof stateDef === 'object') {
if (isTemplateState(stateDef)) {
convertTemplateState(meta, resolver, diagnostics, rule, node, stateDef, name);
convertTemplateState(
meta,
resolver,
diagnostics,
selectorNode,
stateNode,
stateDef,
name
);
} else {
resolveStateValue(meta, resolver, diagnostics, rule, node, stateDef, name, namespace);
resolveStateValue(
meta,
resolver,
diagnostics,
selectorNode,
stateNode,
stateDef,
name,
namespace
);
}

@@ -856,4 +877,4 @@ }

diagnostics: Diagnostics,
rule: postcss.Rule | undefined,
node: PseudoClass,
selectorNode: postcss.Node | undefined,
stateNode: PseudoClass,
stateParamDef: TemplateStateParsedValue,

@@ -867,4 +888,4 @@ name: string

diagnostics,
rule,
node,
selectorNode,
stateNode,
paramStateDef,

@@ -874,3 +895,3 @@ name

validateParam(meta, resolver, diagnostics, rule, paramStateDef, resolvedParam, name);
validateParam(meta, resolver, diagnostics, selectorNode, paramStateDef, resolvedParam, name);

@@ -882,4 +903,4 @@ const strippedParam = stripQuotation(resolvedParam);

param: strippedParam,
node,
rule,
node: stateNode,
selectorNode: selectorNode,
diagnostics,

@@ -892,8 +913,9 @@ });

diagnostics: Diagnostics,
rule: postcss.Rule | undefined,
node: PseudoClass,
selectorNode: postcss.Node | undefined,
stateNode: PseudoClass,
stateParamDef: StateParsedValue,
name: string
) {
const inputValue = node.nodes && node.nodes.length ? stringifySelector(node.nodes) : ``;
const inputValue =
stateNode.nodes && stateNode.nodes.length ? stringifySelector(stateNode.nodes) : ``;
const resolvedParam = resolveParam(

@@ -903,9 +925,9 @@ meta,

diagnostics,
rule,
selectorNode,
inputValue ? inputValue : stateParamDef.defaultValue
);
if (rule && !inputValue && !stateParamDef.defaultValue) {
if (selectorNode && !inputValue && !stateParamDef.defaultValue) {
diagnostics.report(stateDiagnostics.NO_STATE_ARGUMENT_GIVEN(name, stateParamDef.type), {
node: rule,
node: selectorNode,
word: name,

@@ -920,3 +942,3 @@ });

diagnostics: Diagnostics,
rule: postcss.Rule | undefined,
selectorNode: postcss.Node | undefined,
stateParamDef: StateParsedValue,

@@ -933,3 +955,3 @@ resolvedParam: string,

stateParamDef.arguments,
resolveParam.bind(null, meta, resolver, diagnostics, rule),
resolveParam.bind(null, meta, resolver, diagnostics, selectorNode),
false,

@@ -947,3 +969,3 @@ true

if (rule && stateParamOutput.errors) {
if (selectorNode && stateParamOutput.errors) {
diagnostics.report(

@@ -956,3 +978,3 @@ stateDiagnostics.FAILED_STATE_VALIDATION(

{
node: rule,
node: selectorNode,
word: resolvedParam,

@@ -968,4 +990,4 @@ }

diagnostics: Diagnostics,
rule: postcss.Rule | undefined,
node: PseudoClass,
selectorNode: postcss.Node | undefined,
stateNode: PseudoClass,
stateParamDef: StateParsedValue,

@@ -979,4 +1001,4 @@ name: string,

diagnostics,
rule,
node,
selectorNode,
stateNode,
stateParamDef,

@@ -986,7 +1008,7 @@ name

validateParam(meta, resolver, diagnostics, rule, stateParamDef, resolvedParam, name);
validateParam(meta, resolver, diagnostics, selectorNode, stateParamDef, resolvedParam, name);
const strippedParam = stripQuotation(resolvedParam);
convertToClass(node).value = createStateWithParamClassName(name, namespace, strippedParam);
delete node.nodes;
convertToClass(stateNode).value = createStateWithParamClassName(name, namespace, strippedParam);
delete stateNode.nodes;
}

@@ -999,3 +1021,3 @@

node,
rule,
selectorNode,
diagnostics,

@@ -1007,3 +1029,3 @@ }: {

node: PseudoClass;
rule?: postcss.Rule;
selectorNode?: postcss.Node;
diagnostics: Diagnostics;

@@ -1014,7 +1036,7 @@ }) {

if (selectorAst.length > 1) {
if (rule) {
if (selectorNode) {
diagnostics.report(
stateDiagnostics.UNSUPPORTED_MULTI_SELECTOR(stateName, targetSelectorStr),
{
node: rule,
node: selectorNode,
}

@@ -1027,7 +1049,7 @@ );

if (firstSelector?.type === 'type' || firstSelector?.type === 'universal') {
if (rule) {
if (selectorNode) {
diagnostics.report(
stateDiagnostics.UNSUPPORTED_INITIAL_SELECTOR(stateName, targetSelectorStr),
{
node: rule,
node: selectorNode,
}

@@ -1046,3 +1068,3 @@ );

if (unexpectedSelector) {
if (rule) {
if (selectorNode) {
switch (unexpectedSelector.type) {

@@ -1056,3 +1078,3 @@ case 'combinator':

{
node: rule,
node: selectorNode,
}

@@ -1065,3 +1087,3 @@ );

{
node: rule,
node: selectorNode,
}

@@ -1082,3 +1104,3 @@ );

diagnostics: Diagnostics,
rule?: postcss.Rule,
node?: postcss.Node,
nodeContent?: string

@@ -1088,3 +1110,3 @@ ) {

const param = nodeContent || defaultStringValue;
return evalDeclarationValue(resolver, param, meta, rule, undefined, undefined, diagnostics);
return evalDeclarationValue(resolver, param, meta, node, undefined, undefined, diagnostics);
}

@@ -6,2 +6,3 @@ import {

SelectorNode,
PseudoClass,
Selector,

@@ -15,2 +16,3 @@ SelectorList,

ImmutableSelectorNode,
Combinator,
} from '@tokey/css-selector-parser';

@@ -115,3 +117,33 @@ import cloneDeep from 'lodash.clonedeep';

}
export function convertToPseudoClass(
node: SelectorNode,
name: string,
nestedSelectors?: SelectorList
): PseudoClass {
const castedNode = node as PseudoClass;
castedNode.type = 'pseudo_class';
castedNode.value = name;
castedNode.colonComments = [];
if (nestedSelectors) {
castedNode.nodes = nestedSelectors;
} else {
delete castedNode.nodes;
}
return castedNode;
}
export function createCombinatorSelector(partial: Partial<Combinator>): Combinator {
const type = partial.combinator || 'space';
return {
type: `combinator`,
combinator: type,
value: partial.value ?? (type === 'space' ? ` ` : type),
before: partial.before ?? ``,
after: partial.after ?? ``,
start: partial.start ?? 0,
end: partial.end ?? 0,
invalid: partial.invalid ?? false,
};
}
export function isInPseudoClassContext(parents: ReadonlyArray<ImmutableSelectorNode>) {

@@ -118,0 +150,0 @@ for (const parent of parents) {

@@ -10,2 +10,3 @@ export { safeParse } from './parser';

ResolvedElement,
InferredSelector,
} from './stylable-transformer';

@@ -12,0 +13,0 @@ export { validateDefaultConfig } from './stylable';

// importing the factory directly, as we feed it our own fs, and don't want graceful-fs to be implicitly imported
// this allows @stylable/core to be bundled for browser usage without special custom configuration
import ResolverFactory from 'enhanced-resolve/lib/ResolverFactory';
import ResolverFactory from 'enhanced-resolve/lib/ResolverFactory.js';

@@ -5,0 +5,0 @@ import type { ModuleResolver } from './types';

import isVendorPrefixed from 'is-vendor-prefixed';
import cloneDeep from 'lodash.clonedeep';
import * as postcss from 'postcss';

@@ -8,4 +7,9 @@ import type { FileProcessor } from './cached-process-file';

import { nativePseudoElements } from './native-reserved-lists';
import { parseSelectorWithCache, stringifySelector } from './helpers/selector';
import {
createCombinatorSelector,
parseSelectorWithCache,
stringifySelector,
} from './helpers/selector';
import { isEqual } from './helpers/eql';
import {
SelectorNode,

@@ -31,3 +35,2 @@ Selector,

import {
STSymbol,
STImport,

@@ -55,2 +58,3 @@ STGlobal,

import { getRuleScopeSelector } from './deprecated/postcss-ast-extension';
import type { MappedStates } from './helpers/custom-state';

@@ -109,2 +113,3 @@ export interface ResolvedElement {

stVarOverride?: Record<string, string>;
experimentalSelectorInference?: boolean;
}

@@ -120,2 +125,4 @@

type PostcssContainer = postcss.Container<postcss.ChildNode> | postcss.Document;
export class StylableTransformer {

@@ -131,4 +138,6 @@ public fileProcessor: FileProcessor<StylableMeta>;

private evaluator: StylableEvaluator;
private getResolvedSymbols: ReturnType<typeof createSymbolResolverWithCache>;
public getResolvedSymbols: ReturnType<typeof createSymbolResolverWithCache>;
private directiveNodes: postcss.Declaration[] = [];
public experimentalSelectorInference: boolean;
public containerInferredSelectorMap = new Map<PostcssContainer, InferredSelector>();
constructor(options: TransformerOptions) {

@@ -140,2 +149,3 @@ this.diagnostics = options.diagnostics;

this.postProcessor = options.postProcessor;
this.experimentalSelectorInference = options.experimentalSelectorInference === true;
this.resolver = new StylableResolver(

@@ -175,3 +185,5 @@ options.fileProcessor,

STGlobal.hooks.transformInit({ context });
meta.transformedScopes = validateScopes(this, meta);
if (!this.experimentalSelectorInference) {
meta.transformedScopes = validateScopes(this, meta);
}
this.transformAst(meta.targetAst, meta, metaExports);

@@ -190,3 +202,3 @@ meta.transformDiagnostics = this.diagnostics;

mixinTransform = false,
topNestClassName = ``
inferredNestSelector?: InferredSelector
) {

@@ -212,3 +224,3 @@ if (meta.type !== 'stylable') {

};
prepareAST(transformContext, ast);
prepareAST(transformContext, ast, this.experimentalSelectorInference);

@@ -229,2 +241,3 @@ const cssClassResolve = CSSClass.hooks.transformResolve(transformResolveOptions);

resolved: {},
transformer: this,
});

@@ -236,2 +249,3 @@ } else if (name === 'property') {

resolved: cssVarsMapping,
transformer: this,
});

@@ -243,2 +257,3 @@ } else if (name === 'keyframes') {

resolved: keyframesResolve,
transformer: this,
});

@@ -250,2 +265,3 @@ } else if (name === 'layer') {

resolved: layerResolve,
transformer: this,
});

@@ -257,2 +273,3 @@ } else if (name === 'import') {

resolved: layerResolve,
transformer: this,
});

@@ -264,3 +281,18 @@ } else if (name === 'container') {

resolved: containsResolve,
transformer: this,
});
} else if (name === 'st-scope') {
STScope.hooks.transformAtRuleNode({
context: transformContext,
atRule,
resolved: containsResolve,
transformer: this,
});
} else if (name === 'custom-selector') {
STCustomSelector.hooks.transformAtRuleNode({
context: transformContext,
atRule,
resolved: containsResolve,
transformer: this,
});
}

@@ -315,3 +347,18 @@ };

}
node.selector = this.scopeRule(meta, node, topNestClassName);
// get context inferred selector
let currentParent: PostcssContainer | undefined = node.parent;
while (currentParent && !this.containerInferredSelectorMap.has(currentParent)) {
currentParent = currentParent.parent;
}
// transform selector
const { selector, inferredSelector } = this.scopeSelector(
meta,
node.selector,
node,
(currentParent && this.containerInferredSelectorMap.get(currentParent)) ||
inferredNestSelector
);
// save results
this.containerInferredSelectorMap.set(node, inferredSelector);
node.selector = selector;
} else if (node.type === 'atrule') {

@@ -335,2 +382,5 @@ handleAtRule(node);

};
if (this.experimentalSelectorInference) {
STScope.hooks.transformLastPass(lastPassParams);
}
STMixin.hooks.transformLastPass(lastPassParams);

@@ -377,23 +427,20 @@ if (!mixinTransform) {

}
public scopeRule(
meta: StylableMeta,
rule: postcss.Rule,
topNestClassName?: string,
unwrapGlobals?: boolean
): string {
return this.scopeSelector(meta, rule.selector, rule, topNestClassName, unwrapGlobals)
.selector;
}
public scopeSelector(
originMeta: StylableMeta,
selector: string,
rule?: postcss.Rule,
topNestClassName?: string,
selectorNode?: postcss.Rule | postcss.AtRule,
inferredNestSelector?: InferredSelector,
unwrapGlobals = false
): { selector: string; elements: ResolvedElement[][]; targetSelectorAst: SelectorList } {
): {
selector: string;
elements: ResolvedElement[][];
targetSelectorAst: SelectorList;
inferredSelector: InferredSelector;
} {
const context = this.createSelectorContext(
originMeta,
parseSelectorWithCache(selector, { clone: true }),
rule || postcss.rule({ selector }),
topNestClassName
selectorNode || postcss.rule({ selector }),
selector,
inferredNestSelector
);

@@ -408,2 +455,3 @@ const targetSelectorAst = this.scopeSelectorAst(context);

elements: context.elements,
inferredSelector: context.inferredMultipleSelectors,
};

@@ -414,5 +462,10 @@ }

selectorAst: SelectorList,
rule: postcss.Rule,
topNestClassName?: string
selectorNode: postcss.Rule | postcss.AtRule,
selectorStr?: string,
selectorNest?: InferredSelector
) {
const inferredContext = this.createInferredSelector(meta, {
name: meta.root,
type: 'class',
});
return new ScopeContext(

@@ -422,23 +475,21 @@ meta,

selectorAst,
rule,
selectorNode,
this.scopeSelectorAst.bind(this),
topNestClassName
this,
selectorNest || inferredContext.clone(),
inferredContext,
selectorStr
);
}
public createInferredSelector(
meta: StylableMeta,
{ name, type }: { name: string; type: 'class' | 'element' }
) {
const resolvedSymbols = this.getResolvedSymbols(meta);
const resolved = resolvedSymbols[type][name];
return new InferredSelector(this, resolved);
}
public scopeSelectorAst(context: ScopeContext): SelectorList {
const { originMeta, selectorAst } = context;
// group compound selectors: .a.b .c:hover, a .c:hover -> [[[.a.b], [.c:hover]], [[.a], [.c:hover]]]
const selectorList = groupCompoundSelectors(selectorAst);
// resolve meta classes and elements
const resolvedSymbols = this.getResolvedSymbols(originMeta);
// set stylesheet root as the global anchor
if (!context.currentAnchor) {
context.initRootAnchor({
name: originMeta.root,
type: 'class',
resolved: resolvedSymbols.class[originMeta.root],
});
}
const startedAnchor = context.currentAnchor!;
const selectorList = groupCompoundSelectors(context.selectorAst);
// loop over selectors

@@ -452,2 +503,7 @@ for (const selector of selectorList) {

if (node.type !== `compound_selector`) {
if (node.type === 'combinator') {
if (this.experimentalSelectorInference) {
context.setNextSelectorScope(context.inferredSelectorContext, node);
}
}
continue;

@@ -458,2 +514,14 @@ }

for (const compoundNode of node.nodes) {
if (compoundNode.type === 'universal' && this.experimentalSelectorInference) {
context.setNextSelectorScope(
[
{
_kind: 'css',
meta: context.originMeta,
symbol: { _kind: 'element', name: '*' },
},
],
node
);
}
context.node = compoundNode;

@@ -464,5 +532,10 @@ // transform node

}
// add inferred selector end to multiple selector
context.inferredMultipleSelectors.add(context.inferredSelector);
if (selectorList.length - 1 > context.selectorIndex) {
// reset current anchor
context.initRootAnchor(startedAnchor);
// reset current anchor for all except last selector
context.inferredSelector = new InferredSelector(
this,
context.inferredSelectorContext
);
}

@@ -475,5 +548,5 @@ }

const targetAst = splitCompoundSelectors(selectorList);
context.additionalSelectors.forEach((addSelector) => targetAst.push(addSelector()));
context.splitSelectors.duplicateSelectors(targetAst);
for (let i = 0; i < targetAst.length; i++) {
selectorAst[i] = targetAst[i];
context.selectorAst[i] = targetAst[i];
}

@@ -483,4 +556,3 @@ return targetAst;

private handleCompoundNode(context: Required<ScopeContext>) {
const { currentAnchor, node, originMeta, topNestClassName } = context;
const resolvedSymbols = this.getResolvedSymbols(originMeta);
const { inferredSelector, node, originMeta } = context;
const transformerContext = {

@@ -512,68 +584,15 @@ meta: originMeta,

}
const len = currentAnchor.resolved.length;
const lookupStartingPoint = len === 1 /* no extends */ ? 0 : 1;
let resolved: Array<CSSResolve<ClassSymbol | ElementSymbol>> | undefined;
for (let i = lookupStartingPoint; i < len; i++) {
const { symbol, meta } = currentAnchor.resolved[i];
if (!symbol[`-st-root`]) {
continue;
}
const isFirstInSelector =
context.selectorAst[context.selectorIndex].nodes[0] === node;
const customSelector = STCustomSelector.getCustomSelectorExpended(meta, node.value);
if (customSelector) {
this.handleCustomSelector(
customSelector,
meta,
context,
node.value,
node,
isFirstInSelector
);
return;
}
const requestedPart = CSSClass.get(meta, node.value);
if (symbol.alias || !requestedPart) {
// skip alias since they cannot add parts
continue;
}
resolved = this.getResolvedSymbols(meta).class[node.value];
// first definition of a part in the extends/alias chain
context.setCurrentAnchor({
name: node.value,
type: 'pseudo-element',
resolved,
});
context.setNodeResolve(node, resolved);
const resolvedPart = getOriginDefinition(resolved);
const inferredElement = inferredSelector.getPseudoElements({
isFirstInSelector: context.isFirstInSelector(node),
name: node.value,
experimentalSelectorInference: this.experimentalSelectorInference,
})[node.value];
if (inferredElement) {
context.setNextSelectorScope(inferredElement.inferred, node, node.value);
if (context.transform) {
if (!resolvedPart.symbol[`-st-root`] && !isFirstInSelector) {
// insert nested combinator before internal custom element
context.insertDescendantCombinatorBeforePseudoElement();
}
CSSClass.namespaceClass(
resolvedPart.meta,
resolvedPart.symbol,
node,
originMeta
);
context.transformIntoMultiSelector(node, inferredElement.selectors);
}
break;
}
if (!resolved) {
} else {
// first definition of a part in the extends/alias chain
context.setCurrentAnchor({
name: node.value,
type: 'pseudo-element',
resolved: [],
});
context.setNodeResolve(node, []);
context.setNextSelectorScope([], node, node.value);

@@ -588,3 +607,3 @@ if (

{
node: context.rule,
node: context.ruleOrAtRule,
word: node.value,

@@ -596,3 +615,3 @@ }

} else if (node.type === 'pseudo_class') {
CSSPseudoClass.hooks.transformSelectorNode({
const isCustomSelector = STCustomSelector.hooks.transformSelectorNode({
context: transformerContext,

@@ -602,73 +621,13 @@ selectorContext: context,

});
} else if (node.type === `nesting`) {
if (context.nestingSelectorAnchor) {
context.setCurrentAnchor(context.nestingSelectorAnchor);
context.setNodeResolve(node, context.nestingSelectorAnchor.resolved);
} else {
/**
* although it is always assumed to be class symbol, the get is done from
* the general `st-symbol` feature because the actual symbol can
* be a type-element symbol that is actually an imported root in a mixin
*/
const origin = STSymbol.get(originMeta, topNestClassName || originMeta.root) as
| ClassSymbol
| ElementSymbol; // ToDo: handle other cases
context.setCurrentAnchor({
name: origin.name,
type: origin._kind,
resolved: resolvedSymbols[origin._kind][origin.name],
if (!isCustomSelector) {
CSSPseudoClass.hooks.transformSelectorNode({
context: transformerContext,
selectorContext: context,
node,
});
context.setNodeResolve(node, resolvedSymbols[origin._kind][origin.name]);
}
} else if (node.type === `nesting`) {
context.setNextSelectorScope(context.inferredSelectorNest, node, node.value);
}
}
private handleCustomSelector(
customSelector: string,
meta: StylableMeta,
context: ScopeContext,
name: string,
node: SelectorNode,
isFirstInSelector: boolean
) {
const selectorList = parseSelectorWithCache(customSelector, { clone: true });
const hasSingleSelector = selectorList.length === 1;
const internalContext = new ScopeContext(
meta,
this.resolver,
removeFirstRootInFirstCompound(selectorList, meta),
context.rule,
this.scopeSelectorAst.bind(this)
);
const customAstSelectors = this.scopeSelectorAst(internalContext);
if (!isFirstInSelector) {
customAstSelectors.forEach(setSingleSpaceOnSelectorLeft);
}
if (hasSingleSelector && internalContext.currentAnchor) {
context.setCurrentAnchor({
name,
type: 'pseudo-element',
resolved: internalContext.currentAnchor.resolved,
});
context.setNodeResolve(node, internalContext.currentAnchor.resolved);
} else {
// unknown context due to multiple selectors
context.setCurrentAnchor({
name,
type: 'pseudo-element',
resolved: anyElementAnchor(meta).resolved,
});
context.setNodeResolve(node, anyElementAnchor(meta).resolved);
}
if (context.transform) {
Object.assign(node, customAstSelectors[0]);
}
// first one handled inline above
for (let i = 1; i < customAstSelectors.length; i++) {
const selectorNode = context.selectorAst[context.selectorIndex];
const nodeIndex = selectorNode.nodes.indexOf(node);
context.additionalSelectors.push(
lazyCreateSelector(customAstSelectors[i], selectorNode, nodeIndex)
);
}
}
}

@@ -682,8 +641,7 @@

const context = new ScopeContext(
const context = transformer.createSelectorContext(
meta,
transformer.resolver,
parseSelectorWithCache(rule.selector, { clone: true }),
rule,
transformer.scopeSelectorAst.bind(transformer)
rule.selector
);

@@ -713,66 +671,322 @@ transformedScopes[rule.selector] = groupCompoundSelectors(

function removeFirstRootInFirstCompound(selectorList: SelectorList, meta: StylableMeta) {
const compounded = groupCompoundSelectors(selectorList);
for (const selector of compounded) {
const first = selector.nodes.find(({ type }) => type === `compound_selector`);
if (first && first.type === `compound_selector`) {
first.nodes = first.nodes.filter((node) => {
return !(node.type === 'class' && node.value === meta.root);
});
}
function removeFirstRootInFirstCompound(selector: Selector, meta: StylableMeta) {
let hadRoot = false;
const compoundedSelector = groupCompoundSelectors(selector);
const first = compoundedSelector.nodes.find(
({ type }) => type === `compound_selector`
) as CompoundSelector;
if (first) {
first.nodes = first.nodes.filter((node) => {
if (node.type === 'class' && node.value === meta.root) {
hadRoot = true;
return false;
}
return true;
});
}
return splitCompoundSelectors(compounded);
return { selector: splitCompoundSelectors(compoundedSelector), hadRoot };
}
function setSingleSpaceOnSelectorLeft(n: Selector) {
n.before = ` `;
let parent: Selector = n;
let nextLeft: SelectorNode | undefined = n.nodes[0];
while (nextLeft) {
if (`before` in nextLeft) {
nextLeft.before = ``;
type SelectorSymbol = ClassSymbol | ElementSymbol;
type InferredResolve = CSSResolve<SelectorSymbol>;
type InferredPseudoElement = {
inferred: InferredSelector;
selectors: SelectorList;
};
type InferredPseudoClass = {
meta: StylableMeta;
state: MappedStates[string];
};
export class InferredSelector {
protected resolveSet = new Set<InferredResolve[]>();
constructor(
private api: Pick<
StylableTransformer,
'getResolvedSymbols' | 'createSelectorContext' | 'scopeSelectorAst'
>,
resolve?: InferredResolve[] | InferredSelector
) {
if (resolve) {
this.add(resolve);
}
if (nextLeft.type === `selector`) {
nextLeft = nextLeft.nodes[0];
parent = nextLeft as Selector;
} else if (nextLeft.type === `combinator` && nextLeft.combinator === `space`) {
parent.nodes.shift();
nextLeft = parent.nodes[0];
} else {
}
public isEmpty() {
return this.resolveSet.size === 0;
}
public set(resolve: InferredResolve[] | InferredSelector) {
if (resolve === this) {
return;
}
this.resolveSet.clear();
this.add(resolve);
}
}
public clone() {
return new InferredSelector(this.api, this);
}
/**
* Adds to the set of inferred resolved CSS
* Assumes passes CSSResolved from the same meta/symbol are
* the same from the same cached transform process to dedupe them.
*/
public add(resolve: InferredResolve[] | InferredSelector) {
if (resolve instanceof InferredSelector) {
resolve.resolveSet.forEach((resolve) => this.add(resolve));
} else {
this.resolveSet.add(resolve);
}
}
public getPseudoClasses({ name: searchedName }: { name?: string } = {}) {
const collectedStates: Record<string, InferredPseudoClass> = {};
const resolvedCount: Record<string, number> = {};
const expectedIntersectionCount = this.resolveSet.size; // ToDo: dec for any types
const addInferredState = (
name: string,
meta: StylableMeta,
state: MappedStates[string]
) => {
const existing = collectedStates[name];
if (!existing) {
collectedStates[name] = { meta, state };
resolvedCount[name] = 1;
} else {
const isStatesEql = isEqual(existing.state, state);
if (
isStatesEql &&
// states from same meta
(existing.meta === meta ||
// global states
typeof state === 'string' ||
state?.type === 'template')
) {
resolvedCount[name]++;
}
}
};
// infer states from multiple resolved selectors
for (const resolvedContext of this.resolveSet.values()) {
const resolvedFoundNames = new Set<string>();
resolved: for (const { symbol, meta } of resolvedContext) {
const states = symbol[`-st-states`];
if (!states) {
continue;
}
if (searchedName) {
if (Object.hasOwnProperty.call(states, searchedName)) {
// track state
addInferredState(searchedName, meta, states[searchedName]);
break resolved;
}
} else {
// get all states
for (const [name, state] of Object.entries(states)) {
if (!resolvedFoundNames.has(name)) {
// track state
resolvedFoundNames.add(name);
addInferredState(name, meta, state);
}
}
}
}
}
// strict: remove states that do not exist on ALL resolved selectors
return expectedIntersectionCount > 1
? Object.entries(collectedStates).reduce((resultStates, [name, InferredState]) => {
if (resolvedCount[name] >= expectedIntersectionCount) {
resultStates[name] = InferredState;
}
return resultStates;
}, {} as typeof collectedStates)
: collectedStates;
}
public getPseudoElements({
isFirstInSelector,
experimentalSelectorInference,
name,
}: {
isFirstInSelector: boolean;
experimentalSelectorInference: boolean;
name?: string;
}) {
const collectedElements: Record<string, InferredPseudoElement> = {};
const resolvedCount: Record<string, number> = {};
const checked: Record<string, Set<string>> = {};
const expectedIntersectionCount = this.resolveSet.size; // ToDo: dec for any types
const addInferredElement = (
name: string,
inferred: InferredSelector,
selectors: SelectorList
) => {
const item = (collectedElements[name] ||= {
inferred: new InferredSelector(this.api),
selectors: [],
});
// check inferred matching
if (!item.inferred.matchedElement(inferred)) {
// ToDo: bailout fast
return;
}
// add match
resolvedCount[name]++;
item.inferred.add(inferred);
item.selectors.push(...selectors);
};
// infer elements from multiple resolved selectors
for (const resolvedContext of this.resolveSet.values()) {
/**
* search for elements in each resolved selector.
* start at 1 for extended symbols to prefer inherited elements over local
*/
const startIndex = resolvedContext.length === 1 ? 0 : 1;
resolved: for (let i = startIndex; i < resolvedContext.length; i++) {
const { symbol, meta } = resolvedContext[i];
if (!symbol['-st-root'] || symbol.alias) {
// non-root & alias classes don't have parts: bailout
continue;
}
if (name) {
resolvedCount[name] ??= 0;
checked[name] ||= new Set();
const uniqueId = meta.source + '::' + name;
if (checked[name].has(uniqueId)) {
resolvedCount[name]++;
continue;
}
checked[name].add(uniqueId);
// prefer custom selector
const customSelector = STCustomSelector.getCustomSelectorExpended(meta, name);
if (customSelector) {
const selectorList = parseSelectorWithCache(customSelector, {
clone: true,
});
selectorList.forEach((selector) => {
const r = removeFirstRootInFirstCompound(selector, meta);
selector.nodes = r.selector.nodes;
selector.before = '';
if (!r.hadRoot && !isFirstInSelector) {
selector.nodes.unshift(
createCombinatorSelector({ combinator: 'space' })
);
}
});
const internalContext = this.api.createSelectorContext(
meta,
selectorList,
postcss.rule({ selector: customSelector }),
customSelector
);
internalContext.isStandaloneSelector = isFirstInSelector;
const customAstSelectors = this.api.scopeSelectorAst(internalContext);
const inferred =
customAstSelectors.length === 1 || experimentalSelectorInference
? internalContext.inferredMultipleSelectors
: new InferredSelector(this.api, [
{
_kind: 'css',
meta,
symbol: { _kind: 'element', name: '*' },
},
]);
function anyElementAnchor(meta: StylableMeta): {
type: 'class' | 'element';
name: string;
resolved: Array<CSSResolve<ElementSymbol>>;
} {
return {
type: 'element',
name: '*',
resolved: [{ _kind: 'css', meta, symbol: { _kind: 'element', name: '*' } }],
};
}
addInferredElement(name, inferred, customAstSelectors);
break resolved;
}
// matching class part
const classSymbol = CSSClass.get(meta, name);
if (classSymbol) {
const resolvedPart = this.api.getResolvedSymbols(meta).class[name];
const resolvedBaseSymbol = getOriginDefinition(resolvedPart);
const nodes: SelectorNode[] = [];
// insert descendant combinator before internal custom element
if (!resolvedBaseSymbol.symbol[`-st-root`] && !isFirstInSelector) {
nodes.push(createCombinatorSelector({ combinator: 'space' }));
}
// create part class
const classNode = {} as SelectorNode;
CSSClass.namespaceClass(
resolvedBaseSymbol.meta,
resolvedBaseSymbol.symbol,
classNode
);
nodes.push(classNode);
function lazyCreateSelector(
customElementChunk: Selector,
selectorNode: Selector,
nodeIndex: number
): () => Selector {
if (nodeIndex === -1) {
throw new Error('not supported inside nested classes');
addInferredElement(name, new InferredSelector(this.api, resolvedPart), [
{ type: 'selector', after: '', before: '', end: 0, start: 0, nodes },
]);
break resolved;
}
} else {
// ToDo: implement get all elements
}
}
}
// strict: remove elements that do not exist on ALL resolved selectors
return expectedIntersectionCount > 1
? Object.entries(collectedElements).reduce(
(resultElements, [name, InferredElement]) => {
if (resolvedCount[name] >= expectedIntersectionCount) {
resultElements[name] = InferredElement;
}
return resultElements;
},
{} as typeof collectedElements
)
: collectedElements;
}
return (): Selector => {
const clone = cloneDeep(selectorNode);
(clone.nodes[nodeIndex] as any).nodes = customElementChunk.nodes;
return clone;
};
private matchedElement(inferred: InferredSelector): boolean {
for (const target of this.resolveSet) {
const targetBaseElementSymbol = getOriginDefinition(target);
for (const tested of inferred.resolveSet) {
const testedBaseElementSymbol = getOriginDefinition(tested);
if (targetBaseElementSymbol !== testedBaseElementSymbol) {
return false;
}
}
}
return true;
}
// function to temporarily handle single resolved selector type while refactoring
// ToDo: remove temporarily single resolve
getSingleResolve(): InferredResolve[] {
if (this.resolveSet.size !== 1) {
return [];
}
return this.resolveSet.values().next().value;
}
}
interface ScopeAnchor {
type: 'class' | 'element' | 'pseudo-element';
name: string;
resolved: Array<CSSResolve<ClassSymbol | ElementSymbol>>;
class SelectorMultiplier {
private dupIndicesPerSelector: [nodeIndex: number, selectors: SelectorList][][] = [];
public addSplitPoint(selectorIndex: number, nodeIndex: number, selectors: SelectorList) {
if (selectors.length) {
this.dupIndicesPerSelector[selectorIndex] ||= [];
this.dupIndicesPerSelector[selectorIndex].push([nodeIndex, selectors]);
}
}
public duplicateSelectors(targetSelectors: SelectorList) {
// iterate top level selector
for (const [selectorIndex, insertionPoints] of Object.entries(this.dupIndicesPerSelector)) {
const duplicationList = [targetSelectors[Number(selectorIndex)]];
// iterate insertion points
for (const [nodeIndex, selectors] of insertionPoints) {
// collect the duplicate selectors to be multiplied by following insertion points
const added: SelectorList = [];
// iterate selectors for insertion point
for (const replaceSelector of selectors) {
// duplicate selectors and replace selector at insertion point
for (const originSelector of duplicationList) {
const dupSelector = { ...originSelector, nodes: [...originSelector.nodes] };
dupSelector.nodes[nodeIndex] = replaceSelector;
added.push(dupSelector);
}
}
// add the duplicated selectors from insertion point to
// the list of selector to be duplicated for following insertion
// points and to the target selector list
for (const addedSelector of added) {
duplicationList.push(addedSelector);
targetSelectors.push(addedSelector);
}
}
}
}
}

@@ -782,11 +996,21 @@

public transform = true;
public additionalSelectors: Array<() => Selector> = [];
// source multi-selector input
public selectorStr = '';
public selectorIndex = -1;
public elements: any[] = [];
public selectorAstResolveMap = new Map<ImmutableSelectorNode, CSSResolve[]>();
public selectorAstResolveMap = new Map<ImmutableSelectorNode, InferredSelector>();
public selector?: Selector;
public compoundSelector?: CompoundSelector;
public node?: CompoundSelector['nodes'][number];
public currentAnchor?: ScopeAnchor;
public nestingSelectorAnchor?: ScopeAnchor;
// store selector duplication points
public splitSelectors = new SelectorMultiplier();
public lastInferredSelectorNode: SelectorNode | undefined;
// selector is not a continuation of another selector
public isStandaloneSelector = true;
// used for nesting or after combinators
public inferredSelectorContext: InferredSelector;
// current type while traversing a selector
public inferredSelector: InferredSelector;
// combined type of the multiple selectors
public inferredMultipleSelectors: InferredSelector = new InferredSelector(this.transformer);
constructor(

@@ -796,41 +1020,49 @@ public originMeta: StylableMeta,

public selectorAst: SelectorList,
public rule: postcss.Rule,
public ruleOrAtRule: postcss.Rule | postcss.AtRule,
public scopeSelectorAst: StylableTransformer['scopeSelectorAst'],
public topNestClassName: string = ``
) {}
public initRootAnchor(anchor: ScopeAnchor) {
this.currentAnchor = anchor;
private transformer: StylableTransformer,
public inferredSelectorNest: InferredSelector,
selectorContext: InferredSelector,
selectorStr?: string
) {
this.selectorStr = selectorStr || stringifySelector(selectorAst);
this.inferredSelectorContext = new InferredSelector(this.transformer, selectorContext);
this.inferredSelector = new InferredSelector(
this.transformer,
this.inferredSelectorContext
);
}
public setNodeResolve(node: SelectorNode, resolve: CSSResolve[]) {
this.selectorAstResolveMap.set(node, resolve);
get experimentalSelectorInference() {
return this.transformer.experimentalSelectorInference;
}
public setCurrentAnchor(anchor: ScopeAnchor) {
if (this.selectorIndex !== undefined && this.selectorIndex !== -1) {
this.elements[this.selectorIndex].push(anchor);
static legacyElementsTypesMapping: Record<string, string> = {
pseudo_element: 'pseudo-element',
class: 'class',
type: 'element',
};
public setNextSelectorScope(
resolved: InferredResolve[] | InferredSelector,
node: SelectorNode,
name?: string
) {
if (name && this.selectorIndex !== undefined && this.selectorIndex !== -1) {
this.elements[this.selectorIndex].push({
type: ScopeContext.legacyElementsTypesMapping[node.type] || 'unknown',
name,
resolved: Array.isArray(resolved) ? resolved : resolved.getSingleResolve(),
});
}
this.currentAnchor = anchor;
this.inferredSelector.set(resolved);
this.selectorAstResolveMap.set(node, this.inferredSelector.clone());
this.lastInferredSelectorNode = node;
}
public insertDescendantCombinatorBeforePseudoElement() {
if (
this.selector &&
this.compoundSelector &&
this.node &&
this.node.type === `pseudo_element`
) {
if (this.compoundSelector.nodes[0] === this.node) {
const compoundIndex = this.selector.nodes.indexOf(this.compoundSelector);
this.selector.nodes.splice(compoundIndex, 0, {
type: `combinator`,
combinator: `space`,
value: ` `,
before: ``,
after: ``,
start: this.node.start,
end: this.node.start,
invalid: false,
});
}
public isFirstInSelector(node: SelectorNode) {
const isFirstNode = this.selectorAst[this.selectorIndex].nodes[0] === node;
if (isFirstNode && this.selectorIndex === 0 && !this.isStandaloneSelector) {
// force false incase a this context is a splitted part from another selector
return false;
}
return isFirstNode;
}
public createNestedContext(selectorAst: SelectorList) {
public createNestedContext(selectorAst: SelectorList, selectorContext?: InferredSelector) {
const ctx = new ScopeContext(

@@ -840,19 +1072,33 @@ this.originMeta,

selectorAst,
this.rule,
this.ruleOrAtRule,
this.scopeSelectorAst,
this.topNestClassName
this.transformer,
this.inferredSelectorNest,
selectorContext || this.inferredSelectorContext
);
Object.assign(ctx, this);
ctx.selectorAst = selectorAst;
ctx.transform = this.transform;
ctx.selectorAstResolveMap = this.selectorAstResolveMap;
ctx.selectorIndex = -1;
ctx.elements = [];
ctx.additionalSelectors = [];
return ctx;
}
public transformIntoMultiSelector(node: SelectorNode, selectors: SelectorList) {
// transform into the first selector
Object.assign(node, selectors[0]);
// keep track of additional selectors for
// duplication at the end of the selector transform
selectors.shift();
const selectorNode = this.selectorAst[this.selectorIndex];
const nodeIndex = selectorNode.nodes.indexOf(node);
this.splitSelectors.addSplitPoint(this.selectorIndex, nodeIndex, selectors);
}
public isDuplicateStScopeDiagnostic() {
if (this.experimentalSelectorInference || this.ruleOrAtRule.type !== 'rule') {
// this check is not required when experimentalSelectorInference is on
// as @st-scope is not flatten at the beginning of the transformation
// and diagnostics on it's selector is only checked once.
return false;
}
// ToDo: should be removed once st-scope transformation moves to the end of the transform process
const transformedScope =
this.originMeta.transformedScopes?.[getRuleScopeSelector(this.rule) || ``];
this.originMeta.transformedScopes?.[getRuleScopeSelector(this.ruleOrAtRule) || ``];
if (transformedScope && this.selector && this.compoundSelector) {

@@ -883,3 +1129,7 @@ const currentCompoundSelector = stringifySelector(this.compoundSelector);

*/
function prepareAST(context: FeatureTransformContext, ast: postcss.Root) {
function prepareAST(
context: FeatureTransformContext,
ast: postcss.Root,
experimentalSelectorInference: boolean
) {
// ToDo: inline transformations

@@ -891,5 +1141,9 @@ const toRemove: Array<postcss.Node | (() => void)> = [];

STImport.hooks.prepareAST(input);
STScope.hooks.prepareAST(input);
if (!experimentalSelectorInference) {
STScope.hooks.prepareAST(input);
}
STVar.hooks.prepareAST(input);
STCustomSelector.hooks.prepareAST(input);
if (!experimentalSelectorInference) {
STCustomSelector.hooks.prepareAST(input);
}
CSSCustomProperty.hooks.prepareAST(input);

@@ -896,0 +1150,0 @@ });

@@ -39,6 +39,11 @@ import type { CacheItem, FileProcessor, MinimalFS } from './cached-process-file';

fileProcessorCache?: Record<string, CacheItem<StylableMeta>>;
experimentalSelectorInference?: boolean;
}
// This defines and validates known configs for the defaultConfig in 'stylable.config.js
const globalDefaultSupportedConfigs = new Set(['resolveModule', 'resolveNamespace']);
const globalDefaultSupportedConfigs = new Set([
'resolveModule',
'resolveNamespace',
'experimentalSelectorInference',
]);
export function validateDefaultConfig(defaultConfigObj: any) {

@@ -87,3 +92,5 @@ if (typeof defaultConfigObj === 'object') {

protected fileProcessorCache?: Record<string, CacheItem<StylableMeta>>;
private experimentalSelectorInference: boolean;
constructor(config: StylableConfig) {
this.experimentalSelectorInference = !!config.experimentalSelectorInference;
this.projectRoot = config.projectRoot;

@@ -164,2 +171,3 @@ this.fileSystem = config.fileSystem;

mode: this.mode,
experimentalSelectorInference: this.experimentalSelectorInference,
...options,

@@ -166,0 +174,0 @@ });

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc