styled-components 6.4.0-prerelease.4 → 6.4.0-prerelease.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  'data-styled';
12
12
  const SC_ATTR_ACTIVE = 'active';
13
13
  const SC_ATTR_VERSION = 'data-styled-version';
14
- const SC_VERSION = "6.4.0-prerelease.4";
14
+ const SC_VERSION = "6.4.0-prerelease.7";
15
15
  const SPLITTER = '/*!sc*/\n';
16
16
  const IS_BROWSER = typeof window !== 'undefined' && typeof document !== 'undefined';
17
17
  function readSpeedyFlag(name) {
@@ -273,11 +273,11 @@
273
273
  continue;
274
274
  const selector = SC_ATTR + '.g' + group + '[id="' + id + '"]';
275
275
  let content = '';
276
- names.forEach(name => {
276
+ for (const name of names) {
277
277
  if (name.length > 0) {
278
278
  content += name + ',';
279
279
  }
280
- });
280
+ }
281
281
  // NOTE: It's easier to collect rules and have the marker
282
282
  // after the actual rules to simplify the rehydration
283
283
  css += rules + selector + '{content:"' + content + '"}' + SPLITTER;
@@ -658,7 +658,7 @@
658
658
  return '';
659
659
  }
660
660
  if (typeof value === 'number' && value !== 0 && !(name in unitless) && !name.startsWith('--')) {
661
- return `${value}px`; // Presumes implicit 'px' suffix for unitless numbers except for CSS variables
661
+ return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers except for CSS variables
662
662
  }
663
663
  return String(value).trim();
664
664
  }
@@ -739,13 +739,13 @@
739
739
  continue;
740
740
  // @ts-expect-error Property 'isCss' does not exist on type 'any[]'
741
741
  if ((Array.isArray(val) && val.isCss) || isFunction(val)) {
742
- rules.push(`${hyphenateStyleName(key)}:`, val, ';');
742
+ rules.push(hyphenateStyleName(key) + ':', val, ';');
743
743
  }
744
744
  else if (isPlainObject(val)) {
745
- rules.push(`${key} {`, ...objToCssArray(val), '}');
745
+ rules.push(key + ' {', ...objToCssArray(val), '}');
746
746
  }
747
747
  else {
748
- rules.push(`${hyphenateStyleName(key)}: ${addUnitIfNeeded(key, val)};`);
748
+ rules.push(hyphenateStyleName(key) + ': ' + addUnitIfNeeded(key, val) + ';');
749
749
  }
750
750
  }
751
751
  return rules;
@@ -825,7 +825,7 @@
825
825
  * Convenience function for joining strings to form className chains
826
826
  */
827
827
  function joinStrings(a, b) {
828
- return a && b ? `${a} ${b}` : a || b || '';
828
+ return a && b ? a + ' ' + b : a || b || '';
829
829
  }
830
830
  function joinStringArray(arr, sep) {
831
831
  return arr.join(sep || '');
@@ -898,9 +898,9 @@
898
898
  rebuildGroup(styleSheet) {
899
899
  const id = this.componentId;
900
900
  styleSheet.clearRules(id);
901
- this.instanceRules.forEach(entry => {
901
+ for (const entry of this.instanceRules.values()) {
902
902
  styleSheet.insertRules(id, entry.name, entry.rules);
903
- });
903
+ }
904
904
  }
905
905
  }
906
906
 
@@ -1167,22 +1167,26 @@
1167
1167
  * Takes an element and recurses through it's rules added the namespace to the start of each selector.
1168
1168
  * Takes into account media queries by recursing through child rules if they are present.
1169
1169
  */
1170
- function recursivelySetNamepace(compiled, namespace) {
1171
- return compiled.map(rule => {
1170
+ function recursivelySetNamespace(compiled, namespace) {
1171
+ for (let i = 0; i < compiled.length; i++) {
1172
+ const rule = compiled[i];
1172
1173
  if (rule.type === 'rule') {
1173
1174
  // add the namespace to the start
1174
- rule.value = `${namespace} ${rule.value}`;
1175
+ rule.value = namespace + ' ' + rule.value;
1175
1176
  // add the namespace after each comma for subsequent selectors.
1176
- rule.value = rule.value.replaceAll(',', `,${namespace} `);
1177
- rule.props = rule.props.map(prop => {
1178
- return `${namespace} ${prop}`;
1179
- });
1177
+ rule.value = rule.value.replaceAll(',', ',' + namespace + ' ');
1178
+ const props = rule.props;
1179
+ const newProps = [];
1180
+ for (let j = 0; j < props.length; j++) {
1181
+ newProps[j] = namespace + ' ' + props[j];
1182
+ }
1183
+ rule.props = newProps;
1180
1184
  }
1181
1185
  if (Array.isArray(rule.children) && rule.type !== '@keyframes') {
1182
- rule.children = recursivelySetNamepace(rule.children, namespace);
1186
+ rule.children = recursivelySetNamespace(rule.children, namespace);
1183
1187
  }
1184
- return rule;
1185
- });
1188
+ }
1189
+ return compiled;
1186
1190
  }
1187
1191
  function createStylisInstance({ options = EMPTY_OBJECT, plugins = EMPTY_ARRAY, } = EMPTY_OBJECT) {
1188
1192
  let _componentId;
@@ -1251,9 +1255,9 @@
1251
1255
  _selector = selector;
1252
1256
  _selectorRegexp = undefined; // Reset for lazy creation per call
1253
1257
  const flatCSS = sanitizeCSS(stripLineComments(css));
1254
- let compiled = ue(prefix || selector ? `${prefix} ${selector} { ${flatCSS} }` : flatCSS);
1258
+ let compiled = ue(prefix || selector ? prefix + ' ' + selector + ' { ' + flatCSS + ' }' : flatCSS);
1255
1259
  if (options.namespace) {
1256
- compiled = recursivelySetNamepace(compiled, options.namespace);
1260
+ compiled = recursivelySetNamespace(compiled, options.namespace);
1257
1261
  }
1258
1262
  _stack = [];
1259
1263
  ve(compiled, _middleware);
@@ -1286,8 +1290,7 @@
1286
1290
  const StylisContext = React.createContext(undefined)
1287
1291
  ;
1288
1292
  function useStyleSheetContext() {
1289
- // Skip useContext if we're in an RSC environment without context support
1290
- return React.useContext(StyleSheetContext) ;
1293
+ return React.useContext(StyleSheetContext);
1291
1294
  }
1292
1295
  function StyleSheetManager(props) {
1293
1296
  // In RSC environments without context support, StyleSheetManager becomes a no-op
@@ -1630,7 +1633,7 @@
1630
1633
  return css;
1631
1634
  }
1632
1635
  /**
1633
- * Create a theme contract that bridges `ThemeProvider` and CSS custom properties.
1636
+ * Create a theme backed by CSS custom properties, bridging `ThemeProvider` and CSS variables.
1634
1637
  *
1635
1638
  * Returns an object with the same shape as the input theme, but every leaf value
1636
1639
  * is a `var(--prefix-*, fallback)` CSS string. Use these in styled component
@@ -1990,10 +1993,6 @@
1990
1993
  // Control characters and non-letter first symbols are not supported
1991
1994
  const escapeRegex = /[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~-]+/g;
1992
1995
  const dashesAtEnds = /(^-|-$)/g;
1993
- /**
1994
- * TODO: Explore using CSS.escape when it becomes more available
1995
- * in evergreen browsers.
1996
- */
1997
1996
  function escape(str) {
1998
1997
  return str // Replace all possible CSS selectors
1999
1998
  .replace(escapeRegex, '-') // Remove extraneous hyphens at the start and end
@@ -2047,9 +2046,6 @@
2047
2046
  class ComponentStyle {
2048
2047
  constructor(rules, componentId, baseStyle) {
2049
2048
  this.rules = rules;
2050
- this.staticRulesId = '';
2051
- this.isStatic =
2052
- "development" === 'production' ;
2053
2049
  this.componentId = componentId;
2054
2050
  this.baseHash = phash(SEED, componentId);
2055
2051
  this.baseStyle = baseStyle;
@@ -2061,42 +2057,48 @@
2061
2057
  let names = this.baseStyle
2062
2058
  ? this.baseStyle.generateAndInjectStyles(executionContext, styleSheet, stylis)
2063
2059
  : '';
2064
- // force dynamic classnames if user-supplied stylis plugins are in use
2065
- if (this.isStatic && !stylis.hash) {
2066
- if (this.staticRulesId && styleSheet.hasNameForId(this.componentId, this.staticRulesId)) {
2067
- names = joinStrings(names, this.staticRulesId);
2068
- }
2069
- else {
2070
- const cssStatic = joinStringArray(flatten(this.rules, executionContext, styleSheet, stylis));
2071
- const name = generateAlphabeticName(phash(this.baseHash, cssStatic) >>> 0);
2072
- if (!styleSheet.hasNameForId(this.componentId, name)) {
2073
- const cssStaticFormatted = stylis(cssStatic, '.' + name, undefined, this.componentId);
2074
- styleSheet.insertRules(this.componentId, name, cssStaticFormatted);
2075
- }
2076
- names = joinStrings(names, name);
2077
- this.staticRulesId = name;
2078
- }
2079
- }
2080
- else {
2081
- let dynamicHash = phash(this.baseHash, stylis.hash);
2060
+ {
2082
2061
  let css = '';
2083
2062
  for (let i = 0; i < this.rules.length; i++) {
2084
2063
  const partRule = this.rules[i];
2085
2064
  if (typeof partRule === 'string') {
2086
2065
  css += partRule;
2087
- dynamicHash = phash(dynamicHash, partRule);
2088
2066
  }
2089
2067
  else if (partRule) {
2090
- const partString = joinStringArray(flatten(partRule, executionContext, styleSheet, stylis));
2091
- // The same value can switch positions in the array, so we include "i" in the hash.
2092
- // Split into two phash calls to avoid temp string allocation (partString + i).
2093
- // phash processes right-to-left, so phash(h, a+b) === phash(phash(h, b), a).
2094
- dynamicHash = phash(phash(dynamicHash, String(i)), partString);
2095
- css += partString;
2068
+ // Fast path: inline function call for the common case (interpolation
2069
+ // returning a string). Avoids flatten's type dispatch and array alloc.
2070
+ if (isStatelessFunction(partRule)) {
2071
+ const fnResult = partRule(executionContext);
2072
+ if (typeof fnResult === 'string') {
2073
+ css += fnResult;
2074
+ }
2075
+ else if (fnResult !== undefined && fnResult !== null && fnResult !== false) {
2076
+ if (typeof fnResult === 'object' &&
2077
+ !Array.isArray(fnResult) &&
2078
+ !isKeyframes(fnResult) &&
2079
+ !isPlainObject(fnResult)) {
2080
+ console.error(`${getComponentName(partRule)} is not a styled component and cannot be referred to via component selector. See https://www.styled-components.com/docs/advanced#referring-to-other-components for more details.`);
2081
+ }
2082
+ css += joinStringArray(flatten(fnResult, executionContext, styleSheet, stylis));
2083
+ }
2084
+ }
2085
+ else {
2086
+ css += joinStringArray(flatten(partRule, executionContext, styleSheet, stylis));
2087
+ }
2096
2088
  }
2097
2089
  }
2098
2090
  if (css) {
2099
- const name = generateAlphabeticName(dynamicHash >>> 0);
2091
+ // Cache css->name to skip phash+generateName for repeat CSS strings.
2092
+ // The CSS string fully determines the class name for a given component,
2093
+ // so a Map lookup replaces O(cssLen) hashing on cache hit.
2094
+ if (!this.dynamicNameCache)
2095
+ this.dynamicNameCache = new Map();
2096
+ const cacheKey = stylis.hash ? stylis.hash + css : css;
2097
+ let name = this.dynamicNameCache.get(cacheKey);
2098
+ if (!name) {
2099
+ name = generateAlphabeticName(phash(phash(this.baseHash, stylis.hash), css) >>> 0);
2100
+ this.dynamicNameCache.set(cacheKey, name);
2101
+ }
2100
2102
  if (!styleSheet.hasNameForId(this.componentId, name)) {
2101
2103
  const cssFormatted = stylis(css, '.' + name, undefined, this.componentId);
2102
2104
  styleSheet.insertRules(this.componentId, name, cssFormatted);
@@ -2108,17 +2110,37 @@
2108
2110
  }
2109
2111
  }
2110
2112
 
2113
+ const hasOwn = Object.prototype.hasOwnProperty;
2111
2114
  const identifiers = {};
2112
2115
  /* We depend on components having unique IDs */
2113
2116
  function generateId(displayName, parentComponentId) {
2114
2117
  const name = typeof displayName !== 'string' ? 'sc' : escape(displayName);
2115
2118
  // Ensure that no displayName can lead to duplicate componentIds
2116
2119
  identifiers[name] = (identifiers[name] || 0) + 1;
2117
- const componentId = `${name}-${generateComponentId(
2118
- // SC_VERSION gives us isolation between multiple runtimes on the page at once
2119
- // this is improved further with use of the babel plugin "namespace" feature
2120
- SC_VERSION + name + identifiers[name])}`;
2121
- return parentComponentId ? `${parentComponentId}-${componentId}` : componentId;
2120
+ const componentId = name +
2121
+ '-' +
2122
+ generateComponentId(
2123
+ // SC_VERSION gives us isolation between multiple runtimes on the page at once
2124
+ // this is improved further with use of the babel plugin "namespace" feature
2125
+ SC_VERSION + name + identifiers[name]);
2126
+ return parentComponentId ? parentComponentId + '-' + componentId : componentId;
2127
+ }
2128
+ /**
2129
+ * Shallow-compare two context objects using a stored key count to avoid
2130
+ * a second iteration pass. Returns true if all own-property values match.
2131
+ */
2132
+ function shallowEqualContext(prev, next, prevKeyCount) {
2133
+ const a = prev;
2134
+ const b = next;
2135
+ let nextKeyCount = 0;
2136
+ for (const key in b) {
2137
+ if (hasOwn.call(b, key)) {
2138
+ nextKeyCount++;
2139
+ if (a[key] !== b[key])
2140
+ return false;
2141
+ }
2142
+ }
2143
+ return nextKeyCount === prevKeyCount;
2122
2144
  }
2123
2145
  function useInjectedStyle(componentStyle, resolvedAttrs, styleSheet, stylis) {
2124
2146
  const className = componentStyle.generateAndInjectStyles(resolvedAttrs, styleSheet, stylis);
@@ -2131,9 +2153,8 @@
2131
2153
  const context = Object.assign(Object.assign({}, props), {
2132
2154
  // unset, add `props.className` back at the end so props always "wins"
2133
2155
  className: undefined, theme });
2134
- let attrDef;
2135
- for (let i = 0; i < attrs.length; i += 1) {
2136
- attrDef = attrs[i];
2156
+ for (let i = 0; i < attrs.length; i++) {
2157
+ const attrDef = attrs[i];
2137
2158
  // Pass a shallow copy to function attrs so the callback's captured
2138
2159
  // reference isn't mutated by subsequent attrs processing (#3336).
2139
2160
  const resolvedAttrDef = isFunction(attrDef) ? attrDef(Object.assign({}, context)) : attrDef;
@@ -2158,46 +2179,81 @@
2158
2179
  return context;
2159
2180
  }
2160
2181
  let seenUnknownProps = new Set();
2161
- function useStyledComponentImpl(forwardedComponent, props, forwardedRef) {
2162
- const { attrs: componentAttrs, componentStyle, defaultProps, foldedComponentIds, styledComponentId, target, } = forwardedComponent;
2163
- const contextTheme = React.useContext(ThemeContext) ;
2164
- const ssc = useStyleSheetContext();
2165
- const shouldForwardProp = forwardedComponent.shouldForwardProp || ssc.shouldForwardProp;
2166
- if (React.useDebugValue) {
2167
- React.useDebugValue(styledComponentId);
2168
- }
2169
- // NOTE: the non-hooks version only subscribes to this when !componentStyle.isStatic,
2170
- // but that'd be against the rules-of-hooks. We could be naughty and do it anyway as it
2171
- // should be an immutable value, but behave for now.
2172
- const theme = determineTheme(props, contextTheme, defaultProps) || (EMPTY_OBJECT);
2173
- const context = resolveContext(componentAttrs, props, theme);
2174
- const elementToBeCreated = context.as || target;
2182
+ function buildPropsForElement(context, elementToBeCreated, theme, shouldForwardProp) {
2175
2183
  const propsForElement = {};
2176
2184
  for (const key in context) {
2177
- // @ts-expect-error context may have arbitrary properties from attrs
2178
2185
  if (context[key] === undefined) ;
2179
2186
  else if (key[0] === '$' || key === 'as' || (key === 'theme' && context.theme === theme)) ;
2180
2187
  else if (key === 'forwardedAs') {
2181
2188
  propsForElement.as = context.forwardedAs;
2182
2189
  }
2183
2190
  else if (!shouldForwardProp || shouldForwardProp(key, elementToBeCreated)) {
2184
- // @ts-expect-error context may have arbitrary properties from attrs
2185
2191
  propsForElement[key] = context[key];
2186
2192
  if (!shouldForwardProp &&
2187
2193
  "development" === 'development' &&
2188
2194
  !isPropValid(key) &&
2189
2195
  !seenUnknownProps.has(key) &&
2190
- // Only warn on DOM Element.
2191
2196
  domElements.has(elementToBeCreated)) {
2192
2197
  seenUnknownProps.add(key);
2193
2198
  console.warn(`styled-components: it looks like an unknown prop "${key}" is being sent through to the DOM, which will likely trigger a React console error. If you would like automatic filtering of unknown props, you can opt-into that behavior via \`<StyleSheetManager shouldForwardProp={...}>\` (connect an API like \`@emotion/is-prop-valid\`) or consider using transient props (\`$\` prefix for automatic filtering.)`);
2194
2199
  }
2195
2200
  }
2196
2201
  }
2197
- const generatedClassName = useInjectedStyle(componentStyle, context, ssc.styleSheet, ssc.stylis);
2202
+ return propsForElement;
2203
+ }
2204
+ function useStyledComponentImpl(forwardedComponent, props, forwardedRef) {
2205
+ const { attrs: componentAttrs, componentStyle, defaultProps, foldedComponentIds, styledComponentId, target, } = forwardedComponent;
2206
+ const contextTheme = React.useContext(ThemeContext) ;
2207
+ const ssc = useStyleSheetContext();
2208
+ const shouldForwardProp = forwardedComponent.shouldForwardProp || ssc.shouldForwardProp;
2209
+ if (React.useDebugValue) {
2210
+ React.useDebugValue(styledComponentId);
2211
+ }
2212
+ // NOTE: the non-hooks version only subscribes to this when !componentStyle.isStatic,
2213
+ // but that'd be against the rules-of-hooks. We could be naughty and do it anyway as it
2214
+ // should be an immutable value, but behave for now.
2215
+ const theme = determineTheme(props, contextTheme, defaultProps) || (EMPTY_OBJECT);
2216
+ let context;
2217
+ let generatedClassName;
2218
+ // Client-only render cache: skip resolveContext and generateAndInjectStyles
2219
+ // when props+theme haven't changed. propsForElement is always rebuilt since
2220
+ // it's mutated with className/ref after construction.
2221
+ // false and IS_RSC are build/module-level constants for dead-code elimination.
2222
+ {
2223
+ const renderCacheRef = React.useRef(null);
2224
+ const prev = renderCacheRef.current;
2225
+ if (prev !== null &&
2226
+ prev[1] === theme &&
2227
+ prev[2] === ssc.styleSheet &&
2228
+ prev[3] === ssc.stylis &&
2229
+ shallowEqualContext(prev[0], props, prev[4])) {
2230
+ context = prev[5];
2231
+ generatedClassName = prev[6];
2232
+ }
2233
+ else {
2234
+ context = resolveContext(componentAttrs, props, theme);
2235
+ generatedClassName = useInjectedStyle(componentStyle, context, ssc.styleSheet, ssc.stylis);
2236
+ let propsKeyCount = 0;
2237
+ for (const key in props) {
2238
+ if (hasOwn.call(props, key))
2239
+ propsKeyCount++;
2240
+ }
2241
+ renderCacheRef.current = [
2242
+ props,
2243
+ theme,
2244
+ ssc.styleSheet,
2245
+ ssc.stylis,
2246
+ propsKeyCount,
2247
+ context,
2248
+ generatedClassName,
2249
+ ];
2250
+ }
2251
+ }
2198
2252
  if (forwardedComponent.warnTooManyClasses) {
2199
2253
  forwardedComponent.warnTooManyClasses(generatedClassName);
2200
2254
  }
2255
+ const elementToBeCreated = context.as || target;
2256
+ const propsForElement = buildPropsForElement(context, elementToBeCreated, theme, shouldForwardProp);
2201
2257
  let classString = joinStrings(foldedComponentIds, styledComponentId);
2202
2258
  if (generatedClassName) {
2203
2259
  classString += ' ' + generatedClassName;
@@ -2205,15 +2261,10 @@
2205
2261
  if (context.className) {
2206
2262
  classString += ' ' + context.className;
2207
2263
  }
2208
- propsForElement[
2209
- // handle custom elements which React doesn't properly alias
2210
- isTag(elementToBeCreated) &&
2264
+ propsForElement[isTag(elementToBeCreated) &&
2211
2265
  !domElements.has(elementToBeCreated)
2212
2266
  ? 'class'
2213
2267
  : 'className'] = classString;
2214
- // forwardedRef is coming from React.forwardRef.
2215
- // But it might not exist. Since React 19 handles `ref` like a prop, it only define it if there is a value.
2216
- // We don't want to inject an empty ref.
2217
2268
  if (forwardedRef) {
2218
2269
  propsForElement.ref = forwardedRef;
2219
2270
  }
@@ -2226,7 +2277,7 @@
2226
2277
  const isCompositeComponent = !isTag(target);
2227
2278
  const { attrs = EMPTY_ARRAY, componentId = generateId(options.displayName, options.parentComponentId), displayName = generateDisplayName(target), } = options;
2228
2279
  const styledComponentId = options.displayName && options.componentId
2229
- ? `${escape(options.displayName)}-${options.componentId}`
2280
+ ? escape(options.displayName) + '-' + options.componentId
2230
2281
  : options.componentId || componentId;
2231
2282
  // fold the underlying StyledComponent attrs up (implicit extend)
2232
2283
  const finalAttrs = isTargetStyledComp && styledComponentTarget.attrs