diff --git a/e2e/typescript/src/assets/fixture-reset.ts b/e2e/typescript/src/assets/fixture-reset.ts index 1a031326af..1ccddde547 100644 --- a/e2e/typescript/src/assets/fixture-reset.ts +++ b/e2e/typescript/src/assets/fixture-reset.ts @@ -132,41 +132,41 @@ assertType({ }); assertType({ - '.bar': { color: 'beige' }, - '.foo': { padding: '5px' }, - '.qux': { paddingLeft: '5px' }, + '&.bar': { color: 'beige' }, + '&.foo': { padding: '5px' }, + '&.qux': { paddingLeft: '5px' }, }); assertType({ - '.bar': { '--color': 'red' }, + '&.bar': { '--color': 'red' }, }); assertType({ - '.bar': { animationName: { from: {}, to: {} } }, + '&.bar': { animationName: { from: {}, to: {} } }, }); // Nested custom selectors // assertType({ - '.foo': { - '.bar': { flexShrink: 0 }, - '.baz': { flexShrink: 'initial' }, - '.qux': { opacity: 0 }, - '.fred': { zIndex: 0 }, - '.thud': { zIndex: 1 }, + '&.foo': { + '&.bar': { flexShrink: 0 }, + '&.baz': { flexShrink: 'initial' }, + '&.qux': { opacity: 0 }, + '&.fred': { zIndex: 0 }, + '&.thud': { zIndex: 1 }, }, - '.bar': { - '.baz': { color: 'beige' }, - '.qux': { paddingLeft: '5px' }, + '&.bar': { + '&.baz': { color: 'beige' }, + '&.qux': { paddingLeft: '5px' }, }, - '.baz': { - '.qux': { + '&.baz': { + '&.qux': { '--color': 'red', }, }, - '.qux': { - '.bar': { flexShrink: 'var(--bar)' }, - '.baz': { opacity: 'var(--baz)' }, + '&.qux': { + '&.bar': { flexShrink: 'var(--bar)' }, + '&.baz': { opacity: 'var(--baz)' }, }, }); @@ -326,8 +326,8 @@ assertType({ // assertType({ - '.foo': { - '.baz': { + '&.foo': { + '&.baz': { // @ts-expect-error "1" is invalid value for "overflowX" overflowX: '1', padding: 0, @@ -337,23 +337,23 @@ assertType({ }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', - '.bar': { + '&.bar': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object }, }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', zIndex: 1, - '.bar': { + '&.bar': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', zIndex: 1, @@ -361,13 +361,13 @@ assertType({ }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error Object is not assignable to CSS property zIndex: { color: 'red' }, // @ts-expect-error Object is not assignable to CSS property opacity: { color: 'red' }, - '.bar': { + '&.bar': { // @ts-expect-error Object is not assignable to CSS property zIndex: { color: 'red' }, // @ts-expect-error Object is not assignable to CSS property diff --git a/e2e/typescript/src/assets/fixture.ts b/e2e/typescript/src/assets/fixture.ts index df4b356ad7..74caa1116d 100644 --- a/e2e/typescript/src/assets/fixture.ts +++ b/e2e/typescript/src/assets/fixture.ts @@ -31,6 +31,15 @@ assertType({ }, }); +assertType({ + ':hover': { animationName: { from: {}, to: {} } }, +}); +assertType({ + '&.bar': { + animationName: { from: {}, to: {} }, + }, +}); + assertType({ // @ts-expect-error "200" is not a valid CSS value for "height" animationName: { @@ -43,6 +52,16 @@ assertType({ from: { '--opacity': 0 }, }, }); +assertType({ + animationName: { + from: { + // @ts-expect-error Only strings can be used as values for CSS variables + '&.foo': { + color: 'red', + }, + }, + }, +}); // Basic styles // @@ -55,6 +74,13 @@ assertType({ zIndex: 1 }); assertType({ paddingLeft: '5px' }); assertType({ color: 'beige' }); +assertType({ WebkitBackgroundSize: 'initial' }); + +// @ts-expect-error Properties should be camelCase +assertType({ 'padding-left': 'beige' }); +// @ts-expect-error Properties should be camelCase +assertType({ '-webkit-scrollbar': 'beige' }); + // CSS variables // @@ -102,16 +128,17 @@ assertType({ assertType({ ':hover': { content: "'foo'" }, }); -assertType({ - ':hover': { animationName: { from: {}, to: {} } }, -}); assertType({ // @ts-expect-error Values of selectors can be only objects ':hover': 'red', }); +assertType({ + // @ts-expect-error ":foo" is not valid pseudo selector + ':foo': { flexShrink: 0 }, +}); -// Custom selectors +// Complex pseudo selectors // assertType({ @@ -119,6 +146,7 @@ assertType({ ':hover:active': { flexShrink: 'initial' }, ':hover:visited': { zIndex: 0 }, ':hover:focus-visible': { zIndex: 1 }, + ':hover &.foo': { zIndex: 1 }, }); assertType({ @@ -129,40 +157,62 @@ assertType({ }); assertType({ - '.bar': { color: 'beige' }, - '.foo': { paddingLeft: '5px' }, + // @ts-expect-error Values of selectors can be only objects + ':hover:focus': 'red', +}); +assertType({ + // @ts-expect-error ":foo" is not valid pseudo selector + ':foo:hover': { flexShrink: 0 }, +}); +assertType({ + // @ts-expect-error ":foo" is not valid pseudo selector + ':foo &.bar': { flexShrink: 0 }, +}); + +// Custom selectors +// + +assertType({ + '&.bar': { color: 'beige' }, + '&.foo': { paddingLeft: '5px' }, }); assertType({ - '.bar': { '--color': 'red' }, + '&.bar': { '--color': 'red' }, }); + assertType({ - '.bar': { animationName: { from: {}, to: {} } }, + // @ts-expect-error Values of selectors can be only objects + '&.bar': 'red', +}); +assertType({ + // @ts-expect-error Custom selector should start with "&" + '.bar': { color: 'beige' }, }); // Nested custom selectors // assertType({ - '.foo': { - '.bar': { flexShrink: 0 }, - '.baz': { flexShrink: 'initial' }, - '.qux': { opacity: 0 }, - '.fred': { zIndex: 0 }, - '.thud': { zIndex: 1 }, + '&.foo': { + '&.bar': { flexShrink: 0 }, + '&.baz': { flexShrink: 'initial' }, + '&.qux': { opacity: 0 }, + '&.fred': { zIndex: 0 }, + '&.thud': { zIndex: 1 }, }, - '.bar': { - '.baz': { color: 'beige' }, - '.qux': { paddingLeft: '5px' }, + '&.bar': { + '&.baz': { color: 'beige' }, + '&.qux': { paddingLeft: '5px' }, }, - '.baz': { - '.qux': { + '&.baz': { + '&.qux': { '--color': 'red', }, }, - '.qux': { - '.bar': { flexShrink: 'var(--bar)' }, - '.baz': { opacity: 'var(--baz)' }, + '&.qux': { + '&.bar': { flexShrink: 'var(--bar)' }, + '&.baz': { opacity: 'var(--baz)' }, }, }); @@ -315,8 +365,8 @@ assertType({ // assertType({ - '.foo': { - '.baz': { + '&.foo': { + '&.baz': { // @ts-expect-error outline-box is an invalid value for box-sizing overflowX: '1', // @ts-expect-error "5" is an invalid value for paddingLeft @@ -325,23 +375,23 @@ assertType({ }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', - '.bar': { + '&.bar': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', }, }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', zIndex: 1, - '.bar': { + '&.bar': { // @ts-expect-error outline-box is an invalid value for box-sizing boxSizing: 'outline-box', zIndex: 1, @@ -349,13 +399,13 @@ assertType({ }, }); assertType({ - '.foo': { + '&.foo': { // @ts-expect-error Object is not assignable to CSS property zIndex: { color: 'red' }, // @ts-expect-error Object is not assignable to CSS property opacity: { color: 'red' }, - '.bar': { + '&.bar': { // @ts-expect-error Object is not assignable to CSS property zIndex: { color: 'red' }, // @ts-expect-error Object is not assignable to CSS property diff --git a/packages/babel-preset/__fixtures__/object-sequence-expr/code.ts b/packages/babel-preset/__fixtures__/object-sequence-expr/code.ts index 009b9ebce9..358015facb 100644 --- a/packages/babel-preset/__fixtures__/object-sequence-expr/code.ts +++ b/packages/babel-preset/__fixtures__/object-sequence-expr/code.ts @@ -5,12 +5,11 @@ const switchClassName = 'fui-Switch'; let _a: Record; export const useStyles = makeStyles({ - root: - ((_a = {}), - (_a[':hover .' + switchClassName] = { - ':before': { - backgroundColor: 'red', - }, - }), - _a), + root: ((_a = {}), + (_a[':hover .' + switchClassName] = { + ':before': { + backgroundColor: 'red', + } as GriffelStyle, + }), + _a) as any, }); diff --git a/packages/babel-preset/src/assets/normalizeStyleRules.ts b/packages/babel-preset/src/assets/normalizeStyleRules.ts index 65d11cde5b..626734ad0e 100644 --- a/packages/babel-preset/src/assets/normalizeStyleRules.ts +++ b/packages/babel-preset/src/assets/normalizeStyleRules.ts @@ -1,4 +1,4 @@ -import type { GriffelAnimation, GriffelStyle } from '@griffel/core'; +import type { GriffelAnimation, GriffelResetStyle, GriffelResetAnimation, GriffelStyle } from '@griffel/core'; import { tokenize } from 'stylis'; import { isAssetUrl } from './isAssetUrl'; @@ -47,14 +47,11 @@ export function normalizeStyleRule( }, ''); } -export function normalizeStyleRules( - path: typeof import('path'), - projectRoot: string, - filename: string, - stylesBySlots: Record | GriffelStyle, -): Record { +export function normalizeStyleRules< + Styles extends GriffelStyle | GriffelAnimation | GriffelResetStyle | GriffelResetAnimation, +>(path: typeof import('path'), projectRoot: string, filename: string, styles: Styles): Styles { return Object.fromEntries( - Object.entries(stylesBySlots).map(([key, value]) => { + Object.entries(styles).map(([key, value]) => { if (typeof value === 'undefined' || value === null) { return [key, value]; } @@ -65,7 +62,7 @@ export function normalizeStyleRules( key, value.map(rule => { if (typeof rule === 'object') { - return normalizeStyleRules(path, projectRoot, filename, rule as GriffelAnimation); + return normalizeStyleRules(path, projectRoot, filename, rule); } return normalizeStyleRule(path, projectRoot, filename, rule); diff --git a/packages/babel-preset/src/transformPlugin.ts b/packages/babel-preset/src/transformPlugin.ts index 95b8d64469..10f5230e12 100644 --- a/packages/babel-preset/src/transformPlugin.ts +++ b/packages/babel-preset/src/transformPlugin.ts @@ -3,7 +3,7 @@ import { types as t } from '@babel/core'; import { declare } from '@babel/helper-plugin-utils'; import { Module } from '@linaria/babel-preset'; import shakerEvaluator from '@linaria/shaker'; -import type { GriffelStyle, CSSRulesByBucket, CSSClassesMapBySlot } from '@griffel/core'; +import type { GriffelStyle, CSSRulesByBucket, CSSClassesMapBySlot, GriffelResetStyle } from '@griffel/core'; import { resolveStyleRulesForSlots, resolveResetStyleRules, normalizeCSSBucketEntry } from '@griffel/core'; import * as path from 'path'; @@ -318,7 +318,7 @@ export const transformPlugin = declare, PluginObj; +/** + * @internal + */ export type DebugSequence = { sequenceHash: SequenceHash; direction: 'ltr' | 'rtl'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index bf4e99e9aa..c4a8dc3668 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -91,10 +91,11 @@ export type { GriffelStyle, // Reset styles GriffelResetStyle, - // Internal types + GriffelResetAnimation, } from '@griffel/style-types'; export type { + // Internal types CSSClasses, CSSClassesMapBySlot, CSSBucketEntry, diff --git a/packages/core/src/runtime/compileKeyframeCSS.ts b/packages/core/src/runtime/compileKeyframeCSS.ts index d2fd5211b6..2f511e3b73 100644 --- a/packages/core/src/runtime/compileKeyframeCSS.ts +++ b/packages/core/src/runtime/compileKeyframeCSS.ts @@ -1,10 +1,12 @@ -import type { GriffelAnimation } from '@griffel/style-types'; +import type { GriffelAnimation, GriffelResetAnimation } from '@griffel/style-types'; import { compile, middleware, serialize, rulesheet, stringify } from 'stylis'; import { prefixerPlugin } from './stylis/prefixerPlugin'; import { cssifyObject } from './utils/cssifyObject'; -export function compileKeyframeRule(keyframeObject: GriffelAnimation): string { +export function compileKeyframeRule