diff --git a/change/@fluentui-eslint-plugin-react-components-a42b3f04-d734-4f80-a860-34b0ccbebba0.json b/change/@fluentui-eslint-plugin-react-components-a42b3f04-d734-4f80-a860-34b0ccbebba0.json new file mode 100644 index 00000000000000..a10e945f8b71ff --- /dev/null +++ b/change/@fluentui-eslint-plugin-react-components-a42b3f04-d734-4f80-a860-34b0ccbebba0.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/eslint-plugin-react-components", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-global-context-265c61ea-e1cf-47bc-9769-5db14b86d5f9.json b/change/@fluentui-global-context-265c61ea-e1cf-47bc-9769-5db14b86d5f9.json new file mode 100644 index 00000000000000..a65fc7befc6a28 --- /dev/null +++ b/change/@fluentui-global-context-265c61ea-e1cf-47bc-9769-5db14b86d5f9.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/global-context", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-charts-3bff1def-f8cd-4d1d-9fe7-1f2e2af1be73.json b/change/@fluentui-react-charts-3bff1def-f8cd-4d1d-9fe7-1f2e2af1be73.json new file mode 100644 index 00000000000000..145a23efe5d88f --- /dev/null +++ b/change/@fluentui-react-charts-3bff1def-f8cd-4d1d-9fe7-1f2e2af1be73.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-charts", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-dialog-2c645ca9-9895-4fcd-8b60-fa3dcc0603ba.json b/change/@fluentui-react-dialog-2c645ca9-9895-4fcd-8b60-fa3dcc0603ba.json new file mode 100644 index 00000000000000..e6c4d0cdcc9dcf --- /dev/null +++ b/change/@fluentui-react-dialog-2c645ca9-9895-4fcd-8b60-fa3dcc0603ba.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-dialog", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-icons-compat-18f87f14-3d27-4f69-a8f2-b07ab4c0e970.json b/change/@fluentui-react-icons-compat-18f87f14-3d27-4f69-a8f2-b07ab4c0e970.json new file mode 100644 index 00000000000000..bed10968050be9 --- /dev/null +++ b/change/@fluentui-react-icons-compat-18f87f14-3d27-4f69-a8f2-b07ab4c0e970.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to getWindow utility files", + "packageName": "@fluentui/react-icons-compat", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-migration-v0-v9-7b3fd831-aff7-4c4b-b6f5-69c0f2f0289b.json b/change/@fluentui-react-migration-v0-v9-7b3fd831-aff7-4c4b-b6f5-69c0f2f0289b.json new file mode 100644 index 00000000000000..cbf6b8f5a51667 --- /dev/null +++ b/change/@fluentui-react-migration-v0-v9-7b3fd831-aff7-4c4b-b6f5-69c0f2f0289b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-migration-v0-v9", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-migration-v8-v9-322dd77a-8973-4830-99c2-9f817dad99fb.json b/change/@fluentui-react-migration-v8-v9-322dd77a-8973-4830-99c2-9f817dad99fb.json new file mode 100644 index 00000000000000..2ed256b3495ce1 --- /dev/null +++ b/change/@fluentui-react-migration-v8-v9-322dd77a-8973-4830-99c2-9f817dad99fb.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-migration-v8-v9", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-nav-21d41252-e3c6-48f0-89a0-4b1ef7eb9436.json b/change/@fluentui-react-nav-21d41252-e3c6-48f0-89a0-4b1ef7eb9436.json new file mode 100644 index 00000000000000..121111f937fc3e --- /dev/null +++ b/change/@fluentui-react-nav-21d41252-e3c6-48f0-89a0-4b1ef7eb9436.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-nav", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-overflow-5c5a44b8-b8e6-4144-80db-ef13594286ff.json b/change/@fluentui-react-overflow-5c5a44b8-b8e6-4144-80db-ef13594286ff.json new file mode 100644 index 00000000000000..269a4bb371720c --- /dev/null +++ b/change/@fluentui-react-overflow-5c5a44b8-b8e6-4144-80db-ef13594286ff.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-overflow", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-portal-f67f8efe-1b1c-49ca-805d-718342d2402d.json b/change/@fluentui-react-portal-f67f8efe-1b1c-49ca-805d-718342d2402d.json new file mode 100644 index 00000000000000..6d17bf432cee9c --- /dev/null +++ b/change/@fluentui-react-portal-f67f8efe-1b1c-49ca-805d-718342d2402d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-portal", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-positioning-b7699d36-6e35-4f60-811c-4e799450b15e.json b/change/@fluentui-react-positioning-b7699d36-6e35-4f60-811c-4e799450b15e.json new file mode 100644 index 00000000000000..c0a121f4a3e438 --- /dev/null +++ b/change/@fluentui-react-positioning-b7699d36-6e35-4f60-811c-4e799450b15e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-positioning", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-provider-cdafb819-6857-4978-8d87-c28d58616650.json b/change/@fluentui-react-provider-cdafb819-6857-4978-8d87-c28d58616650.json new file mode 100644 index 00000000000000..f3e58abb844414 --- /dev/null +++ b/change/@fluentui-react-provider-cdafb819-6857-4978-8d87-c28d58616650.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-provider", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-shared-contexts-7440097b-04fc-4e98-933e-9a13641937a8.json b/change/@fluentui-react-shared-contexts-7440097b-04fc-4e98-933e-9a13641937a8.json new file mode 100644 index 00000000000000..a3833a450634cb --- /dev/null +++ b/change/@fluentui-react-shared-contexts-7440097b-04fc-4e98-933e-9a13641937a8.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-shared-contexts", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tag-picker-585ccfde-a4fb-4ddb-bb0e-6e3e0bcaba74.json b/change/@fluentui-react-tag-picker-585ccfde-a4fb-4ddb-bb0e-6e3e0bcaba74.json new file mode 100644 index 00000000000000..88b256bc5370ac --- /dev/null +++ b/change/@fluentui-react-tag-picker-585ccfde-a4fb-4ddb-bb0e-6e3e0bcaba74.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-tag-picker", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-text-6f6c2fc2-3bba-4ce8-ab7c-5e62e2e5c271.json b/change/@fluentui-react-text-6f6c2fc2-3bba-4ce8-ab7c-5e62e2e5c271.json new file mode 100644 index 00000000000000..479561e471709c --- /dev/null +++ b/change/@fluentui-react-text-6f6c2fc2-3bba-4ce8-ab7c-5e62e2e5c271.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-text", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-toast-4efa9a56-f4e2-4466-8a6d-3018db34b7dc.json b/change/@fluentui-react-toast-4efa9a56-f4e2-4466-8a6d-3018db34b7dc.json new file mode 100644 index 00000000000000..ca9ccdaac531f7 --- /dev/null +++ b/change/@fluentui-react-toast-4efa9a56-f4e2-4466-8a6d-3018db34b7dc.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-toast", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-utilities-9eeaab33-3389-450e-9eca-9a9245b4faf1.json b/change/@fluentui-react-utilities-9eeaab33-3389-450e-9eca-9a9245b4faf1.json new file mode 100644 index 00000000000000..f8563ad2678f6d --- /dev/null +++ b/change/@fluentui-react-utilities-9eeaab33-3389-450e-9eca-9a9245b4faf1.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing \"use client\" directive to client components and styles", + "packageName": "@fluentui/react-utilities", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charts/library/src/components/AnnotationOnlyChart/useAnnotationOnlyChartStyles.styles.ts b/packages/charts/react-charts/library/src/components/AnnotationOnlyChart/useAnnotationOnlyChartStyles.styles.ts index 6ec54bca1ab80e..8a18a9702fea95 100644 --- a/packages/charts/react-charts/library/src/components/AnnotationOnlyChart/useAnnotationOnlyChartStyles.styles.ts +++ b/packages/charts/react-charts/library/src/components/AnnotationOnlyChart/useAnnotationOnlyChartStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { tokens, typographyStyles } from '@fluentui/react-theme'; diff --git a/packages/charts/react-charts/library/src/utilities/getWindow.ts b/packages/charts/react-charts/library/src/utilities/getWindow.ts index 19ff343ee9c652..2dfed1b5cb06ac 100644 --- a/packages/charts/react-charts/library/src/utilities/getWindow.ts +++ b/packages/charts/react-charts/library/src/utilities/getWindow.ts @@ -1,3 +1,5 @@ +'use client'; + import { canUseDOM } from '@fluentui/react-utilities'; let _window: Window | undefined = undefined; diff --git a/packages/eslint-plugin/src/configs/react/index.js b/packages/eslint-plugin/src/configs/react/index.js index 36952ef4015d5f..27dcaacef18e88 100644 --- a/packages/eslint-plugin/src/configs/react/index.js +++ b/packages/eslint-plugin/src/configs/react/index.js @@ -6,6 +6,7 @@ const reactCompilerPlugin = require('eslint-plugin-react-compiler'); const { __internal } = require('../../internal'); const { createReactCrossVersionRules } = require('../../shared/react-cross-version-rules'); const { defineConfig } = require('eslint/config'); +const { testFiles } = require('../../utils/configHelpers'); /** @type {import("eslint").Linter.RulesRecord} */ const typeAwareRules = { @@ -105,7 +106,7 @@ module.exports = defineConfig( }, { - files: ['**/*.test.{ts,tsx}'], + files: [...testFiles], rules: { 'react-compiler/react-compiler': 'off', '@fluentui/react-components/enforce-use-client': 'off', diff --git a/packages/react-components/eslint-plugin-react-components/README.md b/packages/react-components/eslint-plugin-react-components/README.md index 9600e6ca8909c5..cd309e62a756ac 100644 --- a/packages/react-components/eslint-plugin-react-components/README.md +++ b/packages/react-components/eslint-plugin-react-components/README.md @@ -119,6 +119,7 @@ The rule looks for any of the following client-only features: - Custom hooks (functions whose name starts with `use` and are not in the safe set: `use`, `useId`) - JSX event handler props (properties starting with `on` followed by a capital letter, like `onClick`) - Direct references to browser globals (`window`, `document`, `navigator`, `localStorage`, `sessionStorage`, `history`, `location`) +- SSR-unsafe functions that internally use browser APIs (e.g. `canUseDOM()`, `makeStyles()`, `makeResetStyles()`, `makeStaticStyles()`) If at least one feature is present, the directive must be the very first statement in the file. If no features are found, any existing `'use client'` directive will be reported as unnecessary and auto-fixed. @@ -164,6 +165,47 @@ export function add(a: number, b: number) { } ``` +#### ❌ Don't (SSR-unsafe function at module scope) + +```ts +import * as React from 'react'; +import { canUseDOM } from '../ssr/index'; + +// canUseDOM() accesses browser APIs internally +export const useIsomorphicLayoutEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect; +``` + +#### ✅ Do (directive added for SSR-unsafe function) + +```ts +'use client'; +import * as React from 'react'; +import { canUseDOM } from '../ssr/index'; + +export const useIsomorphicLayoutEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect; +``` + +#### ❌ Don't (Griffel styling function at module scope) + +```ts +import { makeStyles } from '@griffel/react'; + +export const useStyles = makeStyles({ + root: { backgroundColor: 'red' }, +}); +``` + +#### ✅ Do (directive added for Griffel function) + +```ts +'use client'; +import { makeStyles } from '@griffel/react'; + +export const useStyles = makeStyles({ + root: { backgroundColor: 'red' }, +}); +``` + No options – enable to enforce consistent usage of the directive. ## License diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.spec.ts b/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.spec.ts index 63759fc0b1f564..397f2be469462d 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.spec.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.spec.ts @@ -161,6 +161,23 @@ export const testCases = { };`, }, + // ============================================================================= + // SSR UNSAFE FUNCTIONS - Functions that internally use browser APIs at module scope + // ============================================================================= + ssrUnsafeFunctions: { + canUseDOMAtModuleScope: `import * as React from 'react'; + import { canUseDOM } from '../ssr/index'; + + export const useIsomorphicLayoutEffect: typeof React.useEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect; + `, + canUseDOMInVariable: `import { canUseDOM } from '../ssr/canUseDOM'; + + const IS_BROWSER = canUseDOM(); + + export const getBrowserInfo = () => IS_BROWSER ? 'browser' : 'server'; + `, + }, + // ============================================================================= // EVENT HANDLERS - JSX event attributes that require client-side execution // ============================================================================= @@ -232,6 +249,22 @@ export const testCases = { return state; }; `, + importedHookReference: `import * as React from 'react'; + import { createPreset } from '../createPreset'; + import { useBody1Styles } from './useBody1Styles.styles'; + + export const Body1 = createPreset({ + useStyles: useBody1Styles, + displayName: 'Body1', + }); + `, + importedSSRUnsafeReference: `import { createPreset } from '../createPreset'; + import { canUseDOM } from '../ssr/canUseDOM'; + + export const config = { + checkDOM: canUseDOM, + }; + `, }, // ============================================================================= @@ -331,6 +364,16 @@ export const testCases = { root: { backgroundColor: 'red' }, }); `, + resetStyles: `import { makeResetStyles } from '@griffel/react'; + export const useResetStyles = makeResetStyles({ + color: 'blue', + }); + `, + staticStyles: `import { makeStaticStyles } from '@griffel/react'; + export const useStaticStyles = makeStaticStyles({ + body: { margin: 0 }, + }); + `, barrelExports: `export { Counter } from './Counter';`, }, @@ -391,10 +434,6 @@ ruleTester.run(RULE_NAME, rule, { name: 'Pure utility functions - no directive needed', code: testCases.utilities.pureFunctions, }, - { - name: 'Styling utilities (makeStyles) - no directive needed', - code: testCases.utilities.stylingUtilities, - }, { name: 'Barrel re-exports - no directive needed', code: testCases.utilities.barrelExports, @@ -539,6 +578,34 @@ ruleTester.run(RULE_NAME, rule, { name: 'Custom hook with context with "use client" directive', code: `"use client";\n${testCases.customHooks.customHookWithContext}`, }, + { + name: 'Imported custom hook reference with "use client" directive', + code: `"use client";\n${testCases.customHooks.importedHookReference}`, + }, + { + name: 'Imported RSC-unsface function reference with "use client" directive', + code: `"use client";\n${testCases.customHooks.importedSSRUnsafeReference}`, + }, + + // ============================================================================= + // SSR UNSAFE FUNCTIONS - With directive (correct usage) + // ============================================================================= + { + name: 'Styling utilities (makeStyles) with "use client" directive', + code: `"use client";\n${testCases.utilities.stylingUtilities}`, + }, + { + name: 'Reset styles (makeResetStyles) with "use client" directive', + code: `"use client";\n${testCases.utilities.resetStyles}`, + }, + { + name: 'Static styles (makeStaticStyles) with "use client" directive', + code: `"use client";\n${testCases.utilities.staticStyles}`, + }, + { + name: 'canUseDOM() with "use client" directive', + code: `"use client";\n${testCases.ssrUnsafeFunctions.canUseDOMAtModuleScope}`, + }, // ============================================================================= // IMPORT VARIATIONS - With directive (correct usage) @@ -692,6 +759,40 @@ ruleTester.run(RULE_NAME, rule, { output: `"use client";\n${testCases.browserApis.nestedAccess}`, }, + // ============================================================================= + // SSR UNSAFE FUNCTIONS - Missing directive (should error) + // ============================================================================= + { + name: 'makeStyles() without "use client" directive', + code: testCases.utilities.stylingUtilities, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.utilities.stylingUtilities}`, + }, + { + name: 'makeResetStyles() without "use client" directive', + code: testCases.utilities.resetStyles, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.utilities.resetStyles}`, + }, + { + name: 'makeStaticStyles() without "use client" directive', + code: testCases.utilities.staticStyles, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.utilities.staticStyles}`, + }, + { + name: 'canUseDOM() called at module scope without "use client" directive', + code: testCases.ssrUnsafeFunctions.canUseDOMAtModuleScope, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.ssrUnsafeFunctions.canUseDOMAtModuleScope}`, + }, + { + name: 'canUseDOM() in variable declaration without "use client" directive', + code: testCases.ssrUnsafeFunctions.canUseDOMInVariable, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.ssrUnsafeFunctions.canUseDOMInVariable}`, + }, + // ============================================================================= // EVENT HANDLERS - Missing directive (should error) // ============================================================================= @@ -751,6 +852,18 @@ ruleTester.run(RULE_NAME, rule, { errors: [{ messageId: 'missingUseClient' }], output: `"use client";\n${testCases.customHooks.customHookWithContext}`, }, + { + name: 'Imported custom hook reference without "use client" directive', + code: testCases.customHooks.importedHookReference, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.customHooks.importedHookReference}`, + }, + { + name: 'Imported RSC-unsface function reference without "use client" directive', + code: testCases.customHooks.importedSSRUnsafeReference, + errors: [{ messageId: 'missingUseClient' }], + output: `"use client";\n${testCases.customHooks.importedSSRUnsafeReference}`, + }, // ============================================================================= // IMPORT VARIATIONS - Missing directive (should error) @@ -823,12 +936,6 @@ ruleTester.run(RULE_NAME, rule, { errors: [{ messageId: 'unnecessaryUseClient' }], output: testCases.utilities.pureFunctions, }, - { - name: 'Styling utilities with unnecessary "use client" directive', - code: `"use client";\n${testCases.utilities.stylingUtilities}`, - errors: [{ messageId: 'unnecessaryUseClient' }], - output: testCases.utilities.stylingUtilities, - }, { name: 'Barrel exports with unnecessary "use client" directive', code: `"use client";\n${testCases.utilities.barrelExports}`, diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.ts b/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.ts index f25b4b1cf4c1c6..bdef62000a9d0d 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/enforce-use-client.ts @@ -10,7 +10,7 @@ type MessageIds = 'missingUseClient' | 'unnecessaryUseClient'; /** * Represents the different types of client-side features that require the 'use client' directive */ -type FeatureKind = 'react_api' | 'custom_hook' | 'event_handler' | 'browser_api'; +type FeatureKind = 'react_api' | 'custom_hook' | 'event_handler' | 'browser_api' | 'rsc_unsafe_function'; /** * Combined client feature detection result @@ -35,6 +35,10 @@ interface RuleState { misplacedDirective: TSESTree.ExpressionStatement | null; /** First detected client-side feature with its node (for early exit and error reporting) */ firstClientFeature: ClientFeatureDetection | null; + /** Set of imported custom hook names (e.g., useCustomHook from imports) */ + importedCustomHooks: Set; + /** Set of imported RSC-unsface function names */ + importedRSCUnsafeFunctions: Set; } /** @@ -87,6 +91,19 @@ const BROWSER_GLOBALS = new Set([ 'location', ]); +/** + * Functions that internally use browser APIs and should only be called in 'use client' modules + * These functions may have typeof checks internally, but when called at module scope they still + * require client-side execution. + */ +const RSC_UNSAFE_FUNCTIONS = new Set([ + 'canUseDOM', + // Griffel styling functions that require client-side execution + 'makeStyles', + 'makeResetStyles', + 'makeStaticStyles', +]); + /** * Determines if a property name represents an event handler * @param name - The property name to check @@ -130,6 +147,8 @@ export const rule = createRule<[], MessageIds>({ topDirectivePresent: false, misplacedDirective: null, firstClientFeature: null, + importedCustomHooks: new Set(), + importedRSCUnsafeFunctions: new Set(), }; /** @@ -190,6 +209,88 @@ export const rule = createRule<[], MessageIds>({ } }, + /** + * Track imported custom hooks and RSC-unsface functions + */ + ImportDeclaration(node: TSESTree.ImportDeclaration) { + if (shouldSkipAnalysis()) { + return; + } + + for (const specifier of node.specifiers) { + if (specifier.type === AST_NODE_TYPES.ImportSpecifier) { + const importedName = + specifier.imported.type === AST_NODE_TYPES.Identifier + ? specifier.imported.name + : specifier.imported.value; + + // Track custom hooks + if (isPotentialCustomHook(importedName)) { + ruleState.importedCustomHooks.add(specifier.local.name); + } + + // Track RSC-unsface functions + if (RSC_UNSAFE_FUNCTIONS.has(importedName)) { + ruleState.importedRSCUnsafeFunctions.add(specifier.local.name); + } + } + } + }, + + /** + * Detect when imported custom hooks or RSC-unsface functions are referenced + */ + Identifier(node: TSESTree.Identifier) { + if (shouldSkipAnalysis()) { + return; + } + + // Skip if this identifier is in a type-only position + const parent = node.parent; + if (!parent) { + return; + } + + // Skip type annotations, type parameters, and import/export declarations + if ( + parent.type === AST_NODE_TYPES.TSTypeReference || + parent.type === AST_NODE_TYPES.TSTypeQuery || + parent.type === AST_NODE_TYPES.TSTypeAnnotation || + parent.type === AST_NODE_TYPES.TSTypeParameter || + parent.type === AST_NODE_TYPES.ImportSpecifier || + parent.type === AST_NODE_TYPES.ImportDefaultSpecifier || + parent.type === AST_NODE_TYPES.ImportNamespaceSpecifier || + parent.type === AST_NODE_TYPES.ExportSpecifier + ) { + return; + } + + // Skip if it's a property key in an object (unless it's a shorthand property) + if (parent.type === AST_NODE_TYPES.Property) { + if (parent.key === node && !parent.shorthand && !parent.computed) { + return; + } + } + + // Skip if it's the left side of an assignment or variable declarator + if ( + (parent.type === AST_NODE_TYPES.AssignmentExpression && parent.left === node) || + (parent.type === AST_NODE_TYPES.VariableDeclarator && parent.id === node) + ) { + return; + } + + // Check if this is a reference to an imported custom hook + if (ruleState.importedCustomHooks.has(node.name)) { + recordFirstClientFeature('custom_hook', node.name, node); + } + + // Check if this is a reference to an imported RSC-unsface function + if (ruleState.importedRSCUnsafeFunctions.has(node.name)) { + recordFirstClientFeature('rsc_unsafe_function', node.name, node); + } + }, + /** * Check function calls for React APIs and custom hooks */ @@ -204,6 +305,8 @@ export const rule = createRule<[], MessageIds>({ recordFirstClientFeature('react_api', name, node); } else if (isPotentialCustomHook(name)) { recordFirstClientFeature('custom_hook', name, node); + } else if (RSC_UNSAFE_FUNCTIONS.has(name)) { + recordFirstClientFeature('rsc_unsafe_function', name, node); } } else if (node.callee.type === AST_NODE_TYPES.MemberExpression) { const memberExpr = node.callee; diff --git a/packages/react-components/global-context/src/global-context-selector.ts b/packages/react-components/global-context/src/global-context-selector.ts index 3ea2a2a7f72f92..25800a9341676e 100644 --- a/packages/react-components/global-context/src/global-context-selector.ts +++ b/packages/react-components/global-context/src/global-context-selector.ts @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createContext as baseCreateContext } from '@fluentui/react-context-selector'; import { canUseDOM } from '@fluentui/react-utilities'; diff --git a/packages/react-components/react-dialog/library/src/utils/useDisableBodyScroll.styles.ts b/packages/react-components/react-dialog/library/src/utils/useDisableBodyScroll.styles.ts index 48166c00523a1f..63170da230dd6b 100644 --- a/packages/react-components/react-dialog/library/src/utils/useDisableBodyScroll.styles.ts +++ b/packages/react-components/react-dialog/library/src/utils/useDisableBodyScroll.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeResetStyles } from '@griffel/react'; // this style must be applied to the html element to disable scrolling diff --git a/packages/react-components/react-icons-compat/library/src/getWindow.ts b/packages/react-components/react-icons-compat/library/src/getWindow.ts index 8071315f188a6d..10001ab75cc25a 100644 --- a/packages/react-components/react-icons-compat/library/src/getWindow.ts +++ b/packages/react-components/react-icons-compat/library/src/getWindow.ts @@ -1,3 +1,5 @@ +'use client'; + import { canUseDOM } from '@fluentui/react-utilities'; let _window: Window | undefined = undefined; diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/Attachment.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/Attachment.styles.ts index 4116ac3a4c3b7a..b072e71933bb75 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/Attachment.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/Attachment.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { createCustomFocusIndicatorStyle, makeResetStyles, makeStyles, tokens } from '@fluentui/react-components'; import { attachmentActionClassName } from './AttachmentAction'; import { attachmentIconClassName } from './AttachmentIcon'; diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentAction.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentAction.styles.ts index 0fef7690dc4c26..b4dbd9128caabc 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentAction.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentAction.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useAttachmentActionStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentBody.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentBody.styles.ts index 31968db23e5568..66a4c8e3b959fd 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentBody.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentBody.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useAttachmentBodyStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentDescription.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentDescription.styles.ts index 68f40591438112..b2055dc0ec7d4b 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentDescription.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentDescription.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles, tokens } from '@fluentui/react-components'; export const useAttachmentDescriptionStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentHeader.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentHeader.styles.ts index 1acff5712b78a7..4377b6f6619a4f 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentHeader.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentHeader.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles, tokens } from '@fluentui/react-components'; export const useAttachmentHeaderStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentIcon.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentIcon.styles.ts index 9acb93783aada5..5e57fea619ea5a 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentIcon.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Attachment/AttachmentIcon.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useAttachmentIconStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Flex/Flex.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Flex/Flex.styles.ts index 9c065cfee3ba7c..02de01da852ac0 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Flex/Flex.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Flex/Flex.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; const gapValues = { diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Grid/Grid.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Grid/Grid.styles.ts index d1fdcabef3cf64..6bce6fe0c6bd38 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Grid/Grid.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Grid/Grid.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useGridStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/ItemLayout/ItemLayout.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/ItemLayout/ItemLayout.styles.ts index bcaae68266b915..6007373b07638f 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/ItemLayout/ItemLayout.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/ItemLayout/ItemLayout.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useItemLayoutStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Segment/Segment.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Segment/Segment.styles.ts index 6bbcff6dd8376a..6f0facd6574e25 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Segment/Segment.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Segment/Segment.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles, shorthands, tokens } from '@fluentui/react-components'; export const useSegmentStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/StyledText/StyledText.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/StyledText/StyledText.styles.ts index d059975021c313..b7479621753017 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/StyledText/StyledText.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/StyledText/StyledText.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles, tokens } from '@fluentui/react-components'; export const useSizeStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v0-v9/library/src/components/Video/Video.styles.ts b/packages/react-components/react-migration-v0-v9/library/src/components/Video/Video.styles.ts index eef59d5fe51fc8..443a96c0d655f6 100644 --- a/packages/react-components/react-migration-v0-v9/library/src/components/Video/Video.styles.ts +++ b/packages/react-components/react-migration-v0-v9/library/src/components/Video/Video.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useVideoStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v8-v9/library/src/components/Checkbox/Checkbox.styles.ts b/packages/react-components/react-migration-v8-v9/library/src/components/Checkbox/Checkbox.styles.ts index 65241a6f9e0d86..5a63dc0126af1d 100644 --- a/packages/react-components/react-migration-v8-v9/library/src/components/Checkbox/Checkbox.styles.ts +++ b/packages/react-components/react-migration-v8-v9/library/src/components/Checkbox/Checkbox.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@fluentui/react-components'; export const useCheckboxStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackItemShim.styles.ts b/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackItemShim.styles.ts index d5a3279224ef1e..f4f3be075c64c0 100644 --- a/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackItemShim.styles.ts +++ b/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackItemShim.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; export const useStackItemShimStyles = makeStyles({ diff --git a/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackShim.styles.ts b/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackShim.styles.ts index 6c088e8cc5bf53..8c21e08c92ef44 100644 --- a/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackShim.styles.ts +++ b/packages/react-components/react-migration-v8-v9/library/src/components/Stack/StackShim.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; export const useStackStyles = makeStyles({ diff --git a/packages/react-components/react-motion/stories/src/Tokens/Cards.styles.ts b/packages/react-components/react-motion/stories/src/Tokens/Cards.styles.ts index 6f0f8a40e59fe7..7bec650c2ffb26 100644 --- a/packages/react-components/react-motion/stories/src/Tokens/Cards.styles.ts +++ b/packages/react-components/react-motion/stories/src/Tokens/Cards.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles, tokens } from '@fluentui/react-components'; export const useClasses = makeStyles({ diff --git a/packages/react-components/react-nav/library/src/components/sharedNavStyles.styles.ts b/packages/react-components/react-nav/library/src/components/sharedNavStyles.styles.ts index a35db02fa3bac7..557473c2d08ec4 100644 --- a/packages/react-components/react-nav/library/src/components/sharedNavStyles.styles.ts +++ b/packages/react-components/react-nav/library/src/components/sharedNavStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { iconFilledClassName, iconRegularClassName } from '@fluentui/react-icons'; import { tokens, typographyStyles } from '@fluentui/react-theme'; import { makeResetStyles, makeStyles } from '@griffel/react'; diff --git a/packages/react-components/react-overflow/library/src/components/useOverflowStyles.styles.ts b/packages/react-components/react-overflow/library/src/components/useOverflowStyles.styles.ts index 804444fb6c464e..9352930813d4a5 100644 --- a/packages/react-components/react-overflow/library/src/components/useOverflowStyles.styles.ts +++ b/packages/react-components/react-overflow/library/src/components/useOverflowStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { DATA_OVERFLOWING, DATA_OVERFLOW_MENU } from '../constants'; diff --git a/packages/react-components/react-popover/library/src/testing/mockUsePopoverContext.ts b/packages/react-components/react-popover/library/src/testing/mockUsePopoverContext.ts index 65a13cc70cb6cf..7dede39b3570d1 100644 --- a/packages/react-components/react-popover/library/src/testing/mockUsePopoverContext.ts +++ b/packages/react-components/react-popover/library/src/testing/mockUsePopoverContext.ts @@ -1,3 +1,5 @@ +'use client'; + import { usePopoverContext_unstable } from '../popoverContext'; import type { PopoverContextValue } from '../popoverContext'; diff --git a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts index a3882f6d1b9332..ed71de80786792 100644 --- a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts +++ b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; export const usePortalMountNodeStylesStyles = makeStyles({ diff --git a/packages/react-components/react-positioning/library/src/hooks/useSafeZoneArea/SafeZoneArea.styles.ts b/packages/react-components/react-positioning/library/src/hooks/useSafeZoneArea/SafeZoneArea.styles.ts index 2ab286bbd1b4e5..29a83e54bcd180 100644 --- a/packages/react-components/react-positioning/library/src/hooks/useSafeZoneArea/SafeZoneArea.styles.ts +++ b/packages/react-components/react-positioning/library/src/hooks/useSafeZoneArea/SafeZoneArea.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; diff --git a/packages/react-components/react-provider/library/src/components/FluentProvider/renderFluentProvider.tsx b/packages/react-components/react-provider/library/src/components/FluentProvider/renderFluentProvider.tsx index 429d40bff8526d..978ef6547734d3 100644 --- a/packages/react-components/react-provider/library/src/components/FluentProvider/renderFluentProvider.tsx +++ b/packages/react-components/react-provider/library/src/components/FluentProvider/renderFluentProvider.tsx @@ -1,5 +1,8 @@ /** @jsxRuntime automatic */ /** @jsxImportSource @fluentui/react-jsx-runtime */ + +'use client'; + import { canUseDOM, assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { TextDirectionProvider } from '@griffel/react'; diff --git a/packages/react-components/react-shared-contexts/library/src/CustomStyleHooksContext/CustomStyleHooksContext.ts b/packages/react-components/react-shared-contexts/library/src/CustomStyleHooksContext/CustomStyleHooksContext.ts index f8601db9c77baf..cef1c6503ff6f3 100644 --- a/packages/react-components/react-shared-contexts/library/src/CustomStyleHooksContext/CustomStyleHooksContext.ts +++ b/packages/react-components/react-shared-contexts/library/src/CustomStyleHooksContext/CustomStyleHooksContext.ts @@ -1,9 +1,10 @@ -/* eslint-disable */ +'use client'; import * as React from 'react'; type CustomStyleHook = (state: unknown) => void; +/* eslint-disable @typescript-eslint/naming-convention */ // The list of hooks is built from the exports from react-components/src/index export type CustomStyleHooksContextValue = Partial<{ useAccordionHeaderStyles_unstable: CustomStyleHook; @@ -207,12 +208,14 @@ export type CustomStyleHooksContextValue = Partial<{ useVirtualizerScrollViewStyles_unstable: CustomStyleHook; useVirtualizerStyles_unstable: CustomStyleHook; }>; +/* eslint-enable */ /** * @internal */ export const CustomStyleHooksContext = React.createContext(undefined); +// eslint-disable-next-line @typescript-eslint/no-empty-function const noop = () => {}; /** diff --git a/packages/react-components/react-tag-picker/library/src/components/TagPickerOptionGroup/useTagPickerOptionGroup.ts b/packages/react-components/react-tag-picker/library/src/components/TagPickerOptionGroup/useTagPickerOptionGroup.ts index b667493cdf4189..3d86dba8e68200 100644 --- a/packages/react-components/react-tag-picker/library/src/components/TagPickerOptionGroup/useTagPickerOptionGroup.ts +++ b/packages/react-components/react-tag-picker/library/src/components/TagPickerOptionGroup/useTagPickerOptionGroup.ts @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import type { TagPickerOptionGroupProps, TagPickerOptionGroupState } from './TagPickerOptionGroup.types'; import { useOptionGroup_unstable } from '@fluentui/react-combobox'; diff --git a/packages/react-components/react-teaching-popover/library/src/testing/mockTeachingPopoverCarouselContext.ts b/packages/react-components/react-teaching-popover/library/src/testing/mockTeachingPopoverCarouselContext.ts index 591dab4ea2928c..97b423cef21d2b 100644 --- a/packages/react-components/react-teaching-popover/library/src/testing/mockTeachingPopoverCarouselContext.ts +++ b/packages/react-components/react-teaching-popover/library/src/testing/mockTeachingPopoverCarouselContext.ts @@ -1,3 +1,5 @@ +'use client'; + import { CarouselContextValue, carouselContextDefaultValue, diff --git a/packages/react-components/react-text/library/src/components/presets/Body1/Body1.tsx b/packages/react-components/react-text/library/src/components/presets/Body1/Body1.tsx index 35ff31e82c7bf3..f189bcf8a35536 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1/Body1.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Body1/Body1.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body1/useBody1Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Body1/useBody1Styles.styles.ts index 0df28b21d38ebf..277990ca0fc50f 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1/useBody1Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Body1/useBody1Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body1Strong/Body1Strong.tsx b/packages/react-components/react-text/library/src/components/presets/Body1Strong/Body1Strong.tsx index a141e8a5c5db22..8e1f68e8272fd9 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1Strong/Body1Strong.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Body1Strong/Body1Strong.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body1Strong/useBody1StrongStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Body1Strong/useBody1StrongStyles.styles.ts index a29f3e9c1702f5..a8810e9a85add6 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1Strong/useBody1StrongStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Body1Strong/useBody1StrongStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body1Stronger/Body1Stronger.tsx b/packages/react-components/react-text/library/src/components/presets/Body1Stronger/Body1Stronger.tsx index 7888663a18764c..a47e91626b53f3 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1Stronger/Body1Stronger.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Body1Stronger/Body1Stronger.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body1Stronger/useBody1StrongerStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Body1Stronger/useBody1StrongerStyles.styles.ts index cb883ab7e08466..247e2242639a8a 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body1Stronger/useBody1StrongerStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Body1Stronger/useBody1StrongerStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body2/Body2.tsx b/packages/react-components/react-text/library/src/components/presets/Body2/Body2.tsx index e8890915931a13..9f08b8928fb8db 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body2/Body2.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Body2/Body2.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Body2/useBody2Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Body2/useBody2Styles.styles.ts index 116208d5d38342..1c4b97e59dab21 100644 --- a/packages/react-components/react-text/library/src/components/presets/Body2/useBody2Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Body2/useBody2Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1/Caption1.tsx b/packages/react-components/react-text/library/src/components/presets/Caption1/Caption1.tsx index fb218b9c125b0f..6c9418ccf5f73d 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1/Caption1.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Caption1/Caption1.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1/useCaption1Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Caption1/useCaption1Styles.styles.ts index cff14d3f686382..a74ed8c0e2da49 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1/useCaption1Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Caption1/useCaption1Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1Strong/Caption1Strong.tsx b/packages/react-components/react-text/library/src/components/presets/Caption1Strong/Caption1Strong.tsx index e7bcf1d0441034..8160e7f627350d 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1Strong/Caption1Strong.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Caption1Strong/Caption1Strong.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1Strong/useCaption1StrongStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Caption1Strong/useCaption1StrongStyles.styles.ts index 0e9512d9753ba5..a0386ab769e515 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1Strong/useCaption1StrongStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Caption1Strong/useCaption1StrongStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/Caption1Stronger.tsx b/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/Caption1Stronger.tsx index a75e250a85d046..11619929e41963 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/Caption1Stronger.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/Caption1Stronger.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/useCaption1Stronger.styles.ts b/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/useCaption1Stronger.styles.ts index 57094df4c67676..226582248385e1 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/useCaption1Stronger.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Caption1Stronger/useCaption1Stronger.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption2/Caption2.tsx b/packages/react-components/react-text/library/src/components/presets/Caption2/Caption2.tsx index 2e04cbb0fb58c9..7007bf238e2350 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption2/Caption2.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Caption2/Caption2.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption2/useCaption2Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Caption2/useCaption2Styles.styles.ts index 3b91b20030bae5..aec9348a203c32 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption2/useCaption2Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Caption2/useCaption2Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption2Strong/Caption2Strong.tsx b/packages/react-components/react-text/library/src/components/presets/Caption2Strong/Caption2Strong.tsx index dec57fd5b13b57..435eb255c3d667 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption2Strong/Caption2Strong.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Caption2Strong/Caption2Strong.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; diff --git a/packages/react-components/react-text/library/src/components/presets/Caption2Strong/useCaption2StrongStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Caption2Strong/useCaption2StrongStyles.styles.ts index 7f8770d4255673..3aee3e8e3af5ca 100644 --- a/packages/react-components/react-text/library/src/components/presets/Caption2Strong/useCaption2StrongStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Caption2Strong/useCaption2StrongStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Display/Display.tsx b/packages/react-components/react-text/library/src/components/presets/Display/Display.tsx index 2a6de3425b97ac..528145be68c6ee 100644 --- a/packages/react-components/react-text/library/src/components/presets/Display/Display.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Display/Display.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Display/useDisplayStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Display/useDisplayStyles.styles.ts index 354c41720f2139..2089657570565a 100644 --- a/packages/react-components/react-text/library/src/components/presets/Display/useDisplayStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Display/useDisplayStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/LargeTitle/LargeTitle.tsx b/packages/react-components/react-text/library/src/components/presets/LargeTitle/LargeTitle.tsx index 1bfaba1dbd9dc5..2b7585cf43adb2 100644 --- a/packages/react-components/react-text/library/src/components/presets/LargeTitle/LargeTitle.tsx +++ b/packages/react-components/react-text/library/src/components/presets/LargeTitle/LargeTitle.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/LargeTitle/useLargeTitleStyles.styles.ts b/packages/react-components/react-text/library/src/components/presets/LargeTitle/useLargeTitleStyles.styles.ts index 864703643dcc30..8a8e00ebd4338b 100644 --- a/packages/react-components/react-text/library/src/components/presets/LargeTitle/useLargeTitleStyles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/LargeTitle/useLargeTitleStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle1/Subtitle1.tsx b/packages/react-components/react-text/library/src/components/presets/Subtitle1/Subtitle1.tsx index 26e0cc3a65ad79..b4d785dfe7853a 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle1/Subtitle1.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle1/Subtitle1.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle1/useSubtitle1Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Subtitle1/useSubtitle1Styles.styles.ts index 06f48757cca049..5f0be2615bd4e0 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle1/useSubtitle1Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle1/useSubtitle1Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle2/Subtitle2.tsx b/packages/react-components/react-text/library/src/components/presets/Subtitle2/Subtitle2.tsx index 90d59875956a58..a1ac96038c0220 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle2/Subtitle2.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle2/Subtitle2.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle2/useSubtitle2Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Subtitle2/useSubtitle2Styles.styles.ts index 9ce5de3a44f6ca..790b1c3f95b816 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle2/useSubtitle2Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle2/useSubtitle2Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/Subtitle2Stronger.tsx b/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/Subtitle2Stronger.tsx index 7958fedf137964..aea8dc7db02d34 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/Subtitle2Stronger.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/Subtitle2Stronger.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/useSubtitle2Stronger.styles.ts b/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/useSubtitle2Stronger.styles.ts index a0fbdcfb1b7083..1ead2f1b7b258a 100644 --- a/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/useSubtitle2Stronger.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Subtitle2Stronger/useSubtitle2Stronger.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title1/Title1.tsx b/packages/react-components/react-text/library/src/components/presets/Title1/Title1.tsx index 8597aff94acc3c..b4d7acc9e064ae 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title1/Title1.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Title1/Title1.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title1/useTitle1Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Title1/useTitle1Styles.styles.ts index 126f5eda5accfa..312451d6ab92b6 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title1/useTitle1Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Title1/useTitle1Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title2/Title2.tsx b/packages/react-components/react-text/library/src/components/presets/Title2/Title2.tsx index 6d1c2da80a1ddf..f327a6488ec6df 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title2/Title2.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Title2/Title2.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title2/useTitle2Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Title2/useTitle2Styles.styles.ts index 0d6a78592c958a..1f079a61269be1 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title2/useTitle2Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Title2/useTitle2Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title3/Title3.tsx b/packages/react-components/react-text/library/src/components/presets/Title3/Title3.tsx index 353b01fc5fefac..243acfba81b685 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title3/Title3.tsx +++ b/packages/react-components/react-text/library/src/components/presets/Title3/Title3.tsx @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { createPreset } from '../createPreset'; import type { TextPresetProps } from '../../Text/Text.types'; diff --git a/packages/react-components/react-text/library/src/components/presets/Title3/useTitle3Styles.styles.ts b/packages/react-components/react-text/library/src/components/presets/Title3/useTitle3Styles.styles.ts index 26996a6eb02090..850ab97471517a 100644 --- a/packages/react-components/react-text/library/src/components/presets/Title3/useTitle3Styles.styles.ts +++ b/packages/react-components/react-text/library/src/components/presets/Title3/useTitle3Styles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeStyles } from '@griffel/react'; import { SlotClassNames } from '@fluentui/react-utilities'; import type { TextSlots } from '../../Text/Text.types'; diff --git a/packages/react-components/react-toast/library/src/components/Timer/useTimerStyles.styles.ts b/packages/react-components/react-toast/library/src/components/Timer/useTimerStyles.styles.ts index b3d875547e9d68..7b3b98049f38ce 100644 --- a/packages/react-components/react-toast/library/src/components/Timer/useTimerStyles.styles.ts +++ b/packages/react-components/react-toast/library/src/components/Timer/useTimerStyles.styles.ts @@ -1,3 +1,5 @@ +'use client'; + import { makeResetStyles } from '@griffel/react'; export const useBaseAnimationStyles = makeResetStyles({ diff --git a/packages/react-components/react-utilities/src/hooks/useIsomorphicLayoutEffect.ts b/packages/react-components/react-utilities/src/hooks/useIsomorphicLayoutEffect.ts index 7c8a42b8b3196f..ccc761b0c847e8 100644 --- a/packages/react-components/react-utilities/src/hooks/useIsomorphicLayoutEffect.ts +++ b/packages/react-components/react-utilities/src/hooks/useIsomorphicLayoutEffect.ts @@ -1,3 +1,5 @@ +'use client'; + import * as React from 'react'; import { canUseDOM } from '../ssr/index'; diff --git a/packages/react-components/react-utilities/src/ssr/canUseDOM.ts b/packages/react-components/react-utilities/src/ssr/canUseDOM.ts index 7bc01153240a17..f556bb4c22367d 100644 --- a/packages/react-components/react-utilities/src/ssr/canUseDOM.ts +++ b/packages/react-components/react-utilities/src/ssr/canUseDOM.ts @@ -1,9 +1,8 @@ +'use client'; + /** * Verifies if an application can use DOM. */ - -'use client'; - export function canUseDOM(): boolean { return ( /* eslint-disable @nx/workspace-no-restricted-globals -- expected ignore ( SSR friendly acquisition of globals )*/