diff --git a/packages/core/src/features/css.d.ts b/packages/core/src/features/css.d.ts index 0c9370e5..9357c31c 100644 --- a/packages/core/src/features/css.d.ts +++ b/packages/core/src/features/css.d.ts @@ -24,6 +24,14 @@ export type InitComposer = { } } & Styling +/** Composer as it has been processed. */ +export type Internals = { + /** Component type. */ + type: any + /** Component composers. */ + composers: Set +} + /** Composer as it has been processed. */ export type Composer = [ /** Composer base class name. */ diff --git a/packages/core/src/features/css.js b/packages/core/src/features/css.js index 5a6cb0be..93f1edec 100644 --- a/packages/core/src/features/css.js +++ b/packages/core/src/features/css.js @@ -1,4 +1,4 @@ -import { $$composers } from '../utility/composers.js' +import { internals } from '../utility/internals.js' import { createMemo } from '../utility/createMemo.js' import { define } from '../utility/define.js' import { hasNames } from '../utility/hasNames.js' @@ -8,6 +8,7 @@ import { toCssRules } from '../convert/toCssRules.js' import { toHash } from '../convert/toHash.js' import { toTailDashed } from '../convert/toTailDashed.js' +/** @typedef {import('./css').Internals} Internals */ /** @typedef {import('./css').Composer} Composer */ /** @typedef {import('./css').Config} Config */ /** @typedef {import('./css').InitComposer} InitComposer */ @@ -26,53 +27,41 @@ const createComponentFunctionMap = createMemo() /** Returns a function that applies component styles. */ export const createComponentFunction = (/** @type {Config} */ config, /** @type {SheetGroup} */ sheet) => createComponentFunctionMap(config, () => (...args) => { - /** @type {string | Function} Component type, which may be a function or a string. */ - let componentType = null - - /** @type {Set} Composers. */ - const composers = new Set() + /** @type {Internals} */ + let internal = { + type: null, + composers: new Set(), + } for (const arg of args) { // skip any void argument if (arg == null) continue - switch (typeof arg) { - case 'function': - // allow a composer-less function to be the component type - if (componentType == null && !arg[$$composers]) { - componentType = arg - - break - } - - case 'object': - // allow a type property to be this component type - if (componentType == null && arg.type != null) componentType = arg.type - - // copy all composers into this component - if ($$composers in arg) - for (const composer of arg[$$composers]) { - composers.add(composer) - } - // otherwise, add a new composer to this component - else if (!('$$typeof' in arg)) { - const composer = createComposer(arg, config) + // conditionally extend the component + if (arg[internals]) { + internal.type = arg[internals].type - composers.add(composer) - } + for (const composer of arg[internals].composers) { + internal.composers.add(composer) + } + } - break + // otherwise, conditionally define the component type + else if (arg.constructor !== Object || arg.$$typeof) { + internal.type = arg + } - case 'string': - componentType = arg + // otherwise, add a new composer to this component + else { + internal.composers.add(createComposer(arg, config)) } } // set the component type if none was set - if (componentType == null) componentType = 'span' - if (!composers.size) composers.add(['PJLV', {}, [], [], {}, []]) + if (internal.type == null) internal.type = 'span' + if (!internal.composers.size) internal.composers.add(['PJLV', {}, [], [], {}, []]) - return createRenderer(config, componentType, composers, sheet) + return createRenderer(config, internal, sheet) }) /** Creates a composer from a configuration object. */ @@ -142,8 +131,7 @@ const createComposer = (/** @type {InitComposer} */ { variants: initSingularVari const createRenderer = ( /** @type {Config} */ config, - /** @type {string | Function} */ type, - /** @type {Set} */ composers, + /** @type {Internals} */ internal, /** @type {import('../sheet').SheetGroup} */ sheet ) => { const [ @@ -151,7 +139,7 @@ const createRenderer = ( baseClassNames, prefilledVariants, undefinedVariants - ] = getPreparedDataFromComposers(composers) + ] = getPreparedDataFromComposers(internal.composers) const selector = `.${baseClassName}${baseClassNames.length > 1 ? `:where(.${baseClassNames.slice(1).join('.')})` : ``}` @@ -202,7 +190,7 @@ const createRenderer = ( // 2.2.1. orders regular variants before responsive variants // 2.3. iterate their compound variants, add their compound variant classes - for (const [composerBaseClass, composerBaseStyle, singularVariants, compoundVariants] of composers) { + for (const [composerBaseClass, composerBaseStyle, singularVariants, compoundVariants] of internal.composers) { if (!sheet.rules.styled.cache.has(composerBaseClass)) { sheet.rules.styled.cache.add(composerBaseClass) @@ -276,7 +264,7 @@ const createRenderer = ( const renderedToString = () => renderedClassName return { - type, + type: internal.type, className: renderedClassName, selector, props: forwardProps, @@ -286,14 +274,14 @@ const createRenderer = ( const toString = () => { if (!sheet.rules.styled.cache.has(baseClassName)) render() + return baseClassName } return define(render, { - type, className: baseClassName, selector, - [$$composers]: composers, + [internals]: internal, toString, }) } // prettier-ignore diff --git a/packages/core/src/utility/composers.js b/packages/core/src/utility/composers.js deleted file mode 100644 index 904e41ad..00000000 --- a/packages/core/src/utility/composers.js +++ /dev/null @@ -1 +0,0 @@ -export const $$composers = Symbol.for('sxs.composers') diff --git a/packages/core/src/utility/internals.js b/packages/core/src/utility/internals.js new file mode 100644 index 00000000..da568f29 --- /dev/null +++ b/packages/core/src/utility/internals.js @@ -0,0 +1 @@ +export const internals = Symbol.for('stitches.internal') diff --git a/packages/react/src/features/styled.js b/packages/react/src/features/styled.js index 85866028..916ef901 100644 --- a/packages/react/src/features/styled.js +++ b/packages/react/src/features/styled.js @@ -1,8 +1,7 @@ import React from 'react' -import { $$composers } from '../../../core/src/utility/composers.js' +import { internals } from '../../../core/src/utility/internals.js' import { createMemo } from '../../../core/src/utility/createMemo.js' -import { getComponentType } from '../utility/getComponentType.js' import { createComponentFunction } from '../../../core/src/features/css.js' @@ -17,9 +16,8 @@ export const createStyledFunction = ({ /** @type {Config} */ config, /** @type { const css = createComponentFunction(config, sheet) const styled = (...args) => { - const DefaultType = getComponentType(args[0]) - const cssComponent = css(...args) + const DefaultType = cssComponent[internals].type const styledComponent = React.forwardRef((props, ref) => { const Type = props && props.as || DefaultType @@ -38,9 +36,8 @@ export const createStyledFunction = ({ /** @type {Config} */ config, /** @type { styledComponent.className = cssComponent.className styledComponent.displayName = `Styled.${DefaultType.displayName || DefaultType.name || DefaultType}` styledComponent.selector = cssComponent.selector - styledComponent.type = DefaultType styledComponent.toString = toString - styledComponent[$$composers] = cssComponent[$$composers] + styledComponent[internals] = cssComponent[internals] return styledComponent } diff --git a/packages/react/src/utility/getComponentType.js b/packages/react/src/utility/getComponentType.js deleted file mode 100644 index a4638f6d..00000000 --- a/packages/react/src/utility/getComponentType.js +++ /dev/null @@ -1,14 +0,0 @@ -import { $$composers } from '../../../core/src/utility/composers.js' -import { hasOwn } from '../../../core/src/utility/hasOwn.js' - -/** Return the best-matching component type from an unknown type. */ -export const getComponentType = (type) => ( - // if type is void, return a span - type == null ? 'span' : - // if the type is a non react object, return a span - typeof type === 'object' && !type.$$typeof ? 'span' : - // if the type is a styled component, return its inner type - hasOwn(type, $$composers) ? getComponentType(type.type) : - // otherwise, return its type - type -) // prettier-ignore diff --git a/packages/react/tests/component.js b/packages/react/tests/component.js index 41dc06db..1876914a 100644 --- a/packages/react/tests/component.js +++ b/packages/react/tests/component.js @@ -1,33 +1,35 @@ import { createStitches } from '../src/index.js' +const internals = Symbol.for('stitches.internal') + describe('Components', () => { - // test('The `styled` function returns an implicit span component', () => { - // const { styled } = createStitches() - // const component = styled() + test('The `styled` function returns an implicit span component', () => { + const { styled } = createStitches() + const component = styled() - // expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) - // expect(component.type).toBe('span') - // }) + expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) + expect(component[internals].type).toBe('span') + }) - // test('The `styled` function can return an explicit div component', () => { - // const { styled } = createStitches() - // const component = styled('div') + test('The `styled` function can return an explicit div component', () => { + const { styled } = createStitches() + const component = styled('div') - // expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) - // expect(component.type).toBe('div') - // }) + expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) + expect(component[internals].type).toBe('div') + }) - // test('The `styled` function can return an explicit React component', () => { - // function TextComponent() { - // return 'text' - // } + test('The `styled` function can return an explicit React component', () => { + function TextComponent() { + return 'text' + } - // const { styled } = createStitches() - // const component = styled(TextComponent) + const { styled } = createStitches() + const component = styled(TextComponent) - // expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) - // expect(component.type).toBe(TextComponent) - // }) + expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) + expect(component[internals].type).toBe(TextComponent) + }) test('The `styled` function can return an explicit forwarded React component', () => { const ForwardedComponent = { @@ -39,6 +41,6 @@ describe('Components', () => { const component = styled(ForwardedComponent) expect(component.$$typeof).toBe(Symbol.for('react.forward_ref')) - expect(component.type).toBe(ForwardedComponent) + expect(component[internals].type).toBe(ForwardedComponent) }) })