diff --git a/.parcelrc-build b/.parcelrc-build index 26c0e82bb59..dfe9f72b2b9 100644 --- a/.parcelrc-build +++ b/.parcelrc-build @@ -10,7 +10,7 @@ "packages/@react-spectrum/s2/{s2wf-icons,spectrum-illustrations}/**/*.svg": ["@react-spectrum/parcel-transformer-s2-icon"], // Disable PostCSS from running over style macro output "packages/@react-spectrum/s2/**/*.css": ["@parcel/transformer-css"], - "*.css": ["...", "parcel-transformer-css-env"], + "*.css": ["parcel-transformer-css-env", "..."], "*.svg": ["@parcel/transformer-svg-react"], "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [ "@parcel/transformer-js", diff --git a/package.json b/package.json index d7cf8f162e5..97f8b6f5396 100644 --- a/package.json +++ b/package.json @@ -241,7 +241,7 @@ "micromark-extension-mdxjs": "patch:micromark-extension-mdxjs@npm%3A1.0.0#~/.yarn/patches/micromark-extension-mdxjs-npm-1.0.0-d2b6b69e4a.patch", "remark-mdx": "patch:remark-mdx@npm%3A2.0.0-rc.2#~/.yarn/patches/remark-mdx-npm-2.0.0-rc.2-7a71234e1f.patch", "remark-parse": "patch:remark-parse@npm%3A10.0.1#~/.yarn/patches/remark-parse-npm-10.0.1-e654d7df78.patch", - "lightningcss": "1.30.1", + "lightningcss": "1.32.0", "react-server-dom-parcel": "canary", "react-test-renderer": "19.1.0", "@parcel/packager-react-static": "^2.16.3", diff --git a/packages/@adobe/react-spectrum/exports/Toast.ts b/packages/@adobe/react-spectrum/exports/Toast.ts index 272e4b679c3..49492f1fab3 100644 --- a/packages/@adobe/react-spectrum/exports/Toast.ts +++ b/packages/@adobe/react-spectrum/exports/Toast.ts @@ -14,4 +14,4 @@ export {ToastContainer, ToastQueue} from '../src/toast/ToastContainer'; -export type {SpectrumToastOptions, SpectrumToastContainerProps} from '../src/toast/ToastContainer'; +export type {SpectrumToastOptions, SpectrumToastContainerProps, CloseFunction} from '../src/toast/ToastContainer'; diff --git a/packages/@adobe/react-spectrum/src/toast/ToastContainer.tsx b/packages/@adobe/react-spectrum/src/toast/ToastContainer.tsx index e4f8d6086ce..e003fc9be5b 100644 --- a/packages/@adobe/react-spectrum/src/toast/ToastContainer.tsx +++ b/packages/@adobe/react-spectrum/src/toast/ToastContainer.tsx @@ -37,7 +37,7 @@ export interface SpectrumToastOptions extends ToastOptions, DOMProps { shouldCloseOnAction?: boolean } -type CloseFunction = () => void; +export type CloseFunction = () => void; function wrapInViewTransition(fn: () => void): void { if ('startViewTransition' in document) { diff --git a/packages/@adobe/react-spectrum/test/table/TreeGridTable.test.tsx b/packages/@adobe/react-spectrum/test/table/TreeGridTable.test.tsx index c45c35178a4..74228628142 100644 --- a/packages/@adobe/react-spectrum/test/table/TreeGridTable.test.tsx +++ b/packages/@adobe/react-spectrum/test/table/TreeGridTable.test.tsx @@ -692,42 +692,78 @@ describe('TableView with expandable rows', function () { expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Foo')); }); - it('should properly wrap focus with ArrowRight (RTL)', function () { + it('should move focus to parent if already collapsed (RTL)', function () { let treegrid = render(, undefined, 'ar-AE'); - let row = treegrid.getAllByRole('row')[2]; + let row1 = treegrid.getAllByRole('row')[1]; + let row2 = treegrid.getAllByRole('row')[2]; focusCell(treegrid, 'Row 1, Lvl 2, Foo'); moveFocus('ArrowRight'); + expect(document.activeElement).toBe(row2); + expect(row2).toContainElement(getCell(treegrid, 'Row 1, Lvl 2, Foo')); + // Focus doesn't move because Arrow Right on the row will collapse it here + moveFocus('ArrowRight'); + expect(document.activeElement).toBe(row2); + moveFocus('ArrowRight'); + expect(document.activeElement).toBe(row1); + moveFocus('ArrowRight'); + expect(document.activeElement).toBe(row1); + moveFocus('ArrowRight'); + }); + + it('should properly wrap focus with ArrowRight when top level row is collapsed (RTL)', function () { + let treegrid = render(, undefined, 'ar-AE'); + let row = treegrid.getAllByRole('row')[4]; + focusCell(treegrid, 'Row 2, Lvl 1, Foo'); + moveFocus('ArrowRight'); expect(document.activeElement).toBe(row); - expect(row).toContainElement(getCell(treegrid, 'Row 1, Lvl 2, Foo')); - // Focus doesn't move because Arrow Right on the row will collapse it + expect(row).toContainElement(getCell(treegrid, 'Row 2, Lvl 1, Foo')); + // Focus doesn't move because Arrow Right on the row will collapse it here moveFocus('ArrowRight'); expect(document.activeElement).toBe(row); moveFocus('ArrowRight'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Baz')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Baz')); moveFocus('ArrowRight'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Bar')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Bar')); moveFocus('ArrowRight'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Foo')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Foo')); }); }); describe('ArrowLeft', function () { - it('should properly wrap focus with ArrowLeft', function () { + it('should move focus to parent if already collapsed', function () { let treegrid = render(); - let row = treegrid.getAllByRole('row')[2]; + let row1 = treegrid.getAllByRole('row')[1]; + let row2 = treegrid.getAllByRole('row')[2]; focusCell(treegrid, 'Row 1, Lvl 2, Foo'); moveFocus('ArrowLeft'); + expect(document.activeElement).toBe(row2); + expect(row2).toContainElement(getCell(treegrid, 'Row 1, Lvl 2, Foo')); + // Focus doesn't move because Arrow Left on the row will collapse it here + moveFocus('ArrowLeft'); + expect(document.activeElement).toBe(row2); + moveFocus('ArrowLeft'); + expect(document.activeElement).toBe(row1); + moveFocus('ArrowLeft'); + expect(document.activeElement).toBe(row1); + moveFocus('ArrowLeft'); + }); + + it('should properly wrap focus with ArrowLeft when top level row is collapsed', function () { + let treegrid = render(); + let row = treegrid.getAllByRole('row')[4]; + focusCell(treegrid, 'Row 2, Lvl 1, Foo'); + moveFocus('ArrowLeft'); expect(document.activeElement).toBe(row); - expect(row).toContainElement(getCell(treegrid, 'Row 1, Lvl 2, Foo')); + expect(row).toContainElement(getCell(treegrid, 'Row 2, Lvl 1, Foo')); // Focus doesn't move because Arrow Left on the row will collapse it here moveFocus('ArrowLeft'); expect(document.activeElement).toBe(row); moveFocus('ArrowLeft'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Baz')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Baz')); moveFocus('ArrowLeft'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Bar')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Bar')); moveFocus('ArrowLeft'); - expect(document.activeElement).toBe(getCell(treegrid, 'Row 1, Lvl 2, Foo')); + expect(document.activeElement).toBe(getCell(treegrid, 'Row 2, Lvl 1, Foo')); }); it('should properly wrap focus with ArrowLeft (RTL)', function () { diff --git a/packages/@adobe/spectrum-css-temp/components/button/index.css b/packages/@adobe/spectrum-css-temp/components/button/index.css index f37fcb3db20..c7468d13762 100644 --- a/packages/@adobe/spectrum-css-temp/components/button/index.css +++ b/packages/@adobe/spectrum-css-temp/components/button/index.css @@ -25,6 +25,8 @@ governing permissions and limitations under the License. } .spectrum-BaseButton { + composes: i18nFontFamily; + /* Contain halo */ position: relative; @@ -69,8 +71,6 @@ governing permissions and limitations under the License. text-decoration: none; - composes: i18nFontFamily; - line-height: var(--spectrum-button-line-height); user-select: none; diff --git a/packages/@internationalized/date/src/CalendarDate.ts b/packages/@internationalized/date/src/CalendarDate.ts index 6eff9f68ee1..0e5679a892b 100644 --- a/packages/@internationalized/date/src/CalendarDate.ts +++ b/packages/@internationalized/date/src/CalendarDate.ts @@ -17,6 +17,8 @@ import {dateTimeToString, dateToString, timeToString, zonedDateTimeToString} fro import {GregorianCalendar} from './calendars/GregorianCalendar'; import {toCalendarDateTime, toDate, toZoned, zonedToDate} from './conversion'; +export type DateValue = CalendarDate | CalendarDateTime | ZonedDateTime; + function shiftArgs(args: any[]) { let calendar: Calendar = typeof args[0] === 'object' ? args.shift() diff --git a/packages/@internationalized/date/src/index.ts b/packages/@internationalized/date/src/index.ts index 5d373c842f1..39b6072f180 100644 --- a/packages/@internationalized/date/src/index.ts +++ b/packages/@internationalized/date/src/index.ts @@ -27,6 +27,7 @@ export type { CycleOptions, CycleTimeOptions } from './types'; +export type {DateValue} from './CalendarDate'; export {CalendarDate, CalendarDateTime, Time, ZonedDateTime} from './CalendarDate'; export {GregorianCalendar} from './calendars/GregorianCalendar'; diff --git a/packages/@internationalized/date/src/queries.ts b/packages/@internationalized/date/src/queries.ts index e27d50c377b..3862dc0b4eb 100644 --- a/packages/@internationalized/date/src/queries.ts +++ b/packages/@internationalized/date/src/queries.ts @@ -11,12 +11,10 @@ */ import {AnyCalendarDate, AnyTime, Calendar} from './types'; -import {CalendarDate, CalendarDateTime, ZonedDateTime} from './CalendarDate'; +import {CalendarDate, CalendarDateTime, DateValue, ZonedDateTime} from './CalendarDate'; import {fromAbsolute, toAbsolute, toCalendar, toCalendarDate} from './conversion'; import {weekStartData} from './weekStartData'; -type DateValue = CalendarDate | CalendarDateTime | ZonedDateTime; - /** Returns whether the given dates occur on the same day, regardless of the time or calendar system. */ export function isSameDay(a: DateValue, b: DateValue): boolean { b = toCalendar(b, a.calendar); diff --git a/packages/@react-spectrum/s2/src/page.macro.ts b/packages/@react-spectrum/s2/src/page.macro.ts index c3017838b11..8b62934501d 100644 --- a/packages/@react-spectrum/s2/src/page.macro.ts +++ b/packages/@react-spectrum/s2/src/page.macro.ts @@ -26,7 +26,8 @@ export function generatePageStyles(this: MacroContext | void): void { this.addAsset({ type: 'css', content: `:where(:root, :host) { - color-scheme: light dark; + --s2-color-scheme: light dark; + color-scheme: var(--s2-color-scheme); --s2-container-bg: ${colorToken(tokens['background-base-color'])}; background: var(--s2-container-bg); --s2-scale: 1; @@ -38,11 +39,11 @@ export function generatePageStyles(this: MacroContext | void): void { } &[data-color-scheme=light] { - color-scheme: light; + --s2-color-scheme: light; } &[data-color-scheme=dark] { - color-scheme: dark; + --s2-color-scheme: dark; } &[data-background=layer-1] { @@ -57,8 +58,8 @@ export function generatePageStyles(this: MacroContext | void): void { } } -// This generates a low specificity rule to define default values for -// --lightningcss-light and --lightningcss-dark. This is used when rendering +// This generates a low specificity rule to define a default value for +// --s2-color-scheme. This is used when rendering // a without setting a colorScheme prop, and when page.css is not present. // It is equivalent to setting `color-scheme: light dark`, but without overriding // the browser default for content outside the provider. @@ -69,16 +70,10 @@ export function generateDefaultColorSchemeStyles(this: MacroContext | void): voi type: 'css', content: `@layer _.a { :where(:root, :host) { - --lightningcss-light: initial; - --lightningcss-dark: ; + --s2-color-scheme: light dark; --s2-scale: 1; --s2-font-size-base: 14; - @media (prefers-color-scheme: dark) { - --lightningcss-light: ; - --lightningcss-dark: initial; - } - @media not ((hover: hover) and (pointer: fine)) { --s2-scale: 1.25; --s2-font-size-base: 17; diff --git a/packages/@react-spectrum/s2/src/style-utils.ts b/packages/@react-spectrum/s2/src/style-utils.ts index 463f2b6520b..a9c8f3812ea 100644 --- a/packages/@react-spectrum/s2/src/style-utils.ts +++ b/packages/@react-spectrum/s2/src/style-utils.ts @@ -152,15 +152,17 @@ export const fieldInput = () => ({ * ``` */ export const setColorScheme = () => ({ - colorScheme: { - // Default to page color scheme if none is defined. - default: '[var(--lightningcss-light, light) var(--lightningcss-dark, dark)]', - colorScheme: { - 'light dark': 'light dark', - light: 'light', - dark: 'dark' + '--s2-color-scheme': { + type: 'colorScheme', + value: { + colorScheme: { + 'light dark': 'light dark', + light: 'light', + dark: 'dark' + } } - } + }, + colorScheme: '--s2-color-scheme' } as const); export function staticColor(): Record { diff --git a/packages/@react-spectrum/s2/style/index.ts b/packages/@react-spectrum/s2/style/index.ts index 76f145d762b..f21fc0ea971 100644 --- a/packages/@react-spectrum/s2/style/index.ts +++ b/packages/@react-spectrum/s2/style/index.ts @@ -86,7 +86,7 @@ export const focusRing = () => ({ outlineOffset: 2 } as const); -interface IconStyle { +export interface IconStyle { size?: 'XS' | 'S' | 'M' | 'L' |'XL', color?: 'white' | 'black' | 'accent' | 'neutral' | 'negative' | 'informative' | 'positive' | 'notice' | 'gray' | 'red' | 'orange' | 'yellow' | 'chartreuse' | 'celery' | 'green' | 'seafoam' | 'cyan' | 'blue' | 'indigo' | 'purple' | 'fuchsia' | 'magenta' | 'pink' | 'turquoise' | 'cinnamon' | 'brown' | 'silver', margin?: Spacing, diff --git a/packages/@react-spectrum/s2/style/tokens.ts b/packages/@react-spectrum/s2/style/tokens.ts index 690a50f592b..8a2f0ce83ab 100644 --- a/packages/@react-spectrum/s2/style/tokens.ts +++ b/packages/@react-spectrum/s2/style/tokens.ts @@ -13,9 +13,17 @@ // package.json in this directory is not the real package.json. Lint rule not smart enough. import assert from 'assert'; // eslint-disable-next-line rulesdir/imports -import * as tokens from '@adobe/spectrum-tokens/dist/json/variables.json'; +import * as originalTokens from '@adobe/spectrum-tokens/dist/json/variables.json'; -export function getToken(name: keyof typeof tokens): string { +// This forces TSC to inline the token keys instead of leaving a dependency on it. +function keys>(v: T): Record { + return v; +} + +const tokens = keys(originalTokens); +type TokenName = keyof typeof tokens; + +export function getToken(name: TokenName): string { return (tokens[name] as any).value; } @@ -26,7 +34,7 @@ export interface ColorToken { forcedColors?: string } -export function colorToken(name: keyof typeof tokens): ColorToken | ColorRef { +export function colorToken(name: TokenName): ColorToken | ColorRef { let token = tokens[name] as typeof tokens['gray-25'] | typeof tokens['neutral-content-color-default']; if ('ref' in token) { return { @@ -43,7 +51,7 @@ export function colorToken(name: keyof typeof tokens): ColorToken | ColorRef { }; } -export function rawColorToken(name: keyof typeof tokens): string { +export function rawColorToken(name: TokenName): string { let token = tokens[name] as typeof tokens['gray-25']; return `light-dark(${token.sets.light.value}, ${token.sets.dark.value})`; } @@ -55,7 +63,7 @@ export interface ColorRef { forcedColors?: string } -export function weirdColorToken(name: keyof typeof tokens): ColorRef { +export function weirdColorToken(name: TokenName): ColorRef { let token = tokens[name] as typeof tokens['accent-background-color-default']; return { type: 'ref', @@ -66,18 +74,18 @@ export function weirdColorToken(name: keyof typeof tokens): ColorRef { type ReplaceColor = S extends `${infer S}-color-${infer N}` ? `${S}-${N}` : S; -export function colorScale(scale: S): Record>, ReturnType> { +export function colorScale(scale: S): Record>, ReturnType> { let res: any = {}; let re = new RegExp(`^${scale}-\\d+$`); for (let token in tokens) { if (re.test(token)) { - res[token.replace('-color', '')] = colorToken(token as keyof typeof tokens); + res[token.replace('-color', '')] = colorToken(token as TokenName); } } return res; } -export function simpleColorScale(scale: S): Record, string> { +export function simpleColorScale(scale: S): Record, string> { let res: any = {}; let re = new RegExp(`^${scale}-\\d+$`); for (let token in tokens) { @@ -156,10 +164,10 @@ const indexes = { }; /** Returns the index of a font token relative to font-size-100 (which is index 0). */ -export function fontSizeToken(name: keyof typeof tokens): number { +export function fontSizeToken(name: TokenName): number { let token = tokens[name] as typeof tokens['font-size-100'] | typeof tokens['heading-size-m']; if ('ref' in token) { - name = token.ref.slice(1, -1) as keyof typeof tokens; + name = token.ref.slice(1, -1) as TokenName; } let index = indexes[name]; diff --git a/packages/@react-spectrum/toast/src/index.ts b/packages/@react-spectrum/toast/src/index.ts index 272b646609b..2fd8e8dc143 100644 --- a/packages/@react-spectrum/toast/src/index.ts +++ b/packages/@react-spectrum/toast/src/index.ts @@ -14,4 +14,4 @@ export {ToastContainer, ToastQueue} from '@adobe/react-spectrum/Toast'; -export type {SpectrumToastOptions, SpectrumToastContainerProps} from '@adobe/react-spectrum/Toast'; +export type {SpectrumToastOptions, SpectrumToastContainerProps, CloseFunction} from '@adobe/react-spectrum/Toast'; diff --git a/packages/@react-stately/data/src/index.ts b/packages/@react-stately/data/src/index.ts index cc53c3e0edf..68cac432e0f 100644 --- a/packages/@react-stately/data/src/index.ts +++ b/packages/@react-stately/data/src/index.ts @@ -15,5 +15,5 @@ export {useAsyncList} from 'react-stately/useAsyncList'; export {useTreeData} from 'react-stately/useTreeData'; export {useListData} from 'react-stately/useListData'; export type {ListOptions, ListData} from 'react-stately/useListData'; -export type {AsyncListOptions, AsyncListData} from 'react-stately/useAsyncList'; +export type {AsyncListOptions, AsyncListData, AsyncListLoadFunction, AsyncListLoadOptions, AsyncListStateUpdate} from 'react-stately/useAsyncList'; export type {TreeOptions, TreeData} from 'react-stately/useTreeData'; diff --git a/packages/dev/codemods/src/index.ts b/packages/dev/codemods/src/index.ts index e6859e47d7d..b461e1bd3a4 100644 --- a/packages/dev/codemods/src/index.ts +++ b/packages/dev/codemods/src/index.ts @@ -2,6 +2,7 @@ const {parseArgs} = require('node:util'); import {s1_to_s2} from './s1-to-s2/src'; import {use_monopackages} from './use-monopackages/src'; +import {use_subpaths} from './use-subpaths/src'; interface JSCodeshiftOptions { /** @@ -52,9 +53,12 @@ export interface UseMonopackagesCodemodOptions extends JSCodeshiftOptions { packages?: string } -const codemods: Record void> = { +export interface UseSubpathsCodemodOptions extends JSCodeshiftOptions {} + +const codemods: Record void> = { 's1-to-s2': s1_to_s2, - 'use-monopackages': use_monopackages + 'use-monopackages': use_monopackages, + 'use-subpaths': use_subpaths }; // https://github.com/facebook/jscodeshift?tab=readme-ov-file#usage-cli @@ -103,6 +107,7 @@ async function main() { parser: 'tsx', ignorePattern: '**/node_modules/**', path: '.', + extensions: 'js,jsx,mjs,cjs,ts,tsx', ...values })); } diff --git a/packages/dev/codemods/src/use-monopackages/src/codemod.ts b/packages/dev/codemods/src/use-monopackages/src/codemod.ts index bc50395f2e0..f610765a76f 100644 --- a/packages/dev/codemods/src/use-monopackages/src/codemod.ts +++ b/packages/dev/codemods/src/use-monopackages/src/codemod.ts @@ -1,6 +1,8 @@ import {API, FileInfo, ImportSpecifier, Options} from 'jscodeshift'; const fs = require('fs'); const path = require('path'); +const Module = require('module'); +const url = require('url'); function areSpecifiersAlphabetized(specifiers: ImportSpecifier[]) { const specifierNames = specifiers.map( @@ -54,10 +56,16 @@ const transformer: Transformer = function transformer(file: FileInfo, api: API, const monopackageExports: Record = {}; selectedPackages.forEach((pkg) => { - const indexPath = path.join( - process.cwd(), - `node_modules/${packages[pkg].monopackage}/dist/types.d.ts` - ); + let indexPath: string; + try { + let pkgPath = path.dirname(Module.findPackageJSON(packages[pkg].monopackage, url.pathToFileURL(file.path || `${process.cwd()}/index`))!); + indexPath = `${pkgPath}/dist/types/exports/index.d.ts`; + if (!fs.existsSync(indexPath)) { + indexPath = `${pkgPath}/exports/index.ts`; + } + } catch { + return; + } if (fs.existsSync(indexPath)) { anyIndexFound = true; diff --git a/packages/dev/codemods/src/use-subpaths/README.md b/packages/dev/codemods/src/use-subpaths/README.md new file mode 100644 index 00000000000..98c42ea903a --- /dev/null +++ b/packages/dev/codemods/src/use-subpaths/README.md @@ -0,0 +1,19 @@ +# Codemod to use subpath exports + +Replaces monopackage imports with subpaths. + +```diff +- import {Button} from 'react-aria-components'; ++ import {Button} from 'react-aria-components/Button'; +``` + +## Usage + +Run `npx @react-spectrum/codemods use-subpaths` from the directory you want to update imports in. + +### Options + +- `--parser` - The [parser](https://github.com/facebook/jscodeshift?tab=readme-ov-file#parser) to use for parsing the source files. Defaults to `tsx`. +- `--ignore-pattern` - A [glob pattern](https://github.com/facebook/jscodeshift?tab=readme-ov-file#ignoring-files-and-directories) of files to ignore. Defaults to `**/node_modules/**`. +- `--dry` - Run the codemod without making changes to the files. +- `--path` - The path to the directory to run the codemod in. Defaults to the current directory. diff --git a/packages/dev/codemods/src/use-subpaths/src/codemod.test.ts b/packages/dev/codemods/src/use-subpaths/src/codemod.test.ts new file mode 100644 index 00000000000..6bc5c8f3aee --- /dev/null +++ b/packages/dev/codemods/src/use-subpaths/src/codemod.test.ts @@ -0,0 +1,128 @@ +// @ts-ignore +import {defineInlineTest} from 'jscodeshift/dist/testUtils'; +import transform from './codemod'; + +function test(name: string, input: string, output: string) { + defineInlineTest(transform, {}, input, output, name); +} + +test( + 'rewrites a uniquely mapped specifier', + ` +import {Accordion} from '@adobe/react-spectrum'; +`, + ` +import { Accordion } from '@adobe/react-spectrum/Accordion'; +` +); + +test( + 'splits mixed uniquely mapped specifiers across subpaths', + ` +import {Accordion, Button} from '@adobe/react-spectrum'; +`, + ` +import { Accordion } from '@adobe/react-spectrum/Accordion'; +import { Button } from '@adobe/react-spectrum/Button'; +` +); + +test( + 'groups multi-mapped specifiers with a uniquely mapped import from the same declaration', + ` +import {ListView, Item} from '@adobe/react-spectrum'; +`, + ` +import { ListView, Item } from '@adobe/react-spectrum/ListView'; +` +); + +test( + 'handles when Item is available in multiple existing imports', + ` +import {ListView, ActionGroup, Item} from '@adobe/react-spectrum'; +`, + ` +import { ListView, Item } from '@adobe/react-spectrum/ListView'; +import { ActionGroup } from '@adobe/react-spectrum/ActionGroup'; +` +); + +test( + 'handles multiple monopackage imports', + ` +import {ListView} from '@adobe/react-spectrum'; +import {ActionGroup, Item} from '@adobe/react-spectrum'; +`, + ` +import { ListView, Item } from '@adobe/react-spectrum/ListView'; +import { ActionGroup } from '@adobe/react-spectrum/ActionGroup'; +` +); + +test( + 'reuses an existing matching subpath import elsewhere in the file', + ` +import {Item} from '@adobe/react-spectrum'; +import {ListView} from '@adobe/react-spectrum/ListView'; +`, + ` +import { ListView, Item } from '@adobe/react-spectrum/ListView'; +` +); + +test( + 'preserves aliases when moving specifiers', + ` +import {ListView as RSListView, Item as RSItem} from '@adobe/react-spectrum'; +`, + ` +import { ListView as RSListView, Item as RSItem } from '@adobe/react-spectrum/ListView'; +` +); + +test( + 'keeps unsupported, default, and namespace imports untouched', + ` +import ReactSpectrum, {Accordion, fakeThing} from '@adobe/react-spectrum'; +import * as RAC from 'react-aria-components'; +`, + ` +import ReactSpectrum, { fakeThing } from '@adobe/react-spectrum'; +import { Accordion } from '@adobe/react-spectrum/Accordion'; +import * as RAC from 'react-aria-components'; +` +); + +test( + 'merges into an existing destination import', + ` +import {Disclosure} from '@adobe/react-spectrum'; +import {Accordion} from '@adobe/react-spectrum/Accordion'; +`, + ` +import { Accordion, Disclosure } from '@adobe/react-spectrum/Accordion'; +` +); + +test( + 'leaves already subpathed imports unchanged', + ` +import {Accordion} from '@adobe/react-spectrum/Accordion'; +`, + ` +import {Accordion} from '@adobe/react-spectrum/Accordion'; +` +); + +test( + 'groups by import kind', + ` +import {Button, ButtonContext} from 'react-aria-components'; +import type {ButtonProps} from 'react-aria-components'; +`, + ` +import { Button, ButtonContext } from 'react-aria-components/Button'; +import type { ButtonProps } from 'react-aria-components/Button'; +` +); diff --git a/packages/dev/codemods/src/use-subpaths/src/codemod.ts b/packages/dev/codemods/src/use-subpaths/src/codemod.ts new file mode 100644 index 00000000000..dcb60540049 --- /dev/null +++ b/packages/dev/codemods/src/use-subpaths/src/codemod.ts @@ -0,0 +1,204 @@ +/* eslint-disable max-depth */ +import {API, FileInfo} from 'jscodeshift'; +import {getSpecifiersByPackage} from './specifiers'; +import {parse} from '@babel/parser'; +import {parse as recastParse} from 'recast'; +import * as t from '@babel/types'; + +function getImportedName(specifier: t.ImportSpecifier): string { + return specifier.imported.type === 'Identifier' ? specifier.imported.name : specifier.imported.value; +} + +function getImportKey(specifier: t.ImportSpecifier): string { + const importedName = getImportedName(specifier); + const localName = specifier.local?.name ?? importedName; + const importKind = specifier.importKind ?? 'value'; + return `${importKind}:${importedName}:${localName}`; +} + +function getImportDeclarationKey(node: t.ImportDeclaration): string { + return (node.importKind ?? 'value') + ':' + node.source.value; +} + +function getNamedSpecifierKeys(importDeclaration: t.ImportDeclaration): Set { + let keys = new Set(); + for (let specifier of importDeclaration.specifiers ?? []) { + if (specifier.type === 'ImportSpecifier') { + keys.add(getImportKey(specifier)); + } + } + + return keys; +} + +function createImportDeclaration( + source: string, + importKind: t.ImportSpecifier['importKind'], + specifiers: t.ImportSpecifier[] +): t.ImportDeclaration { + let declaration = t.importDeclaration(specifiers, t.stringLiteral(source)); + declaration.importKind = importKind ?? 'value'; + return declaration; +} + +function resolveTargetSource( + candidates: string[], + uniqueSources: Set, + existingImports: Map +): string { + // Group with the first unique source that contained a candidate. + // For example if you imported ListBox, ActionGroup, and Item, Item would be grouped with ListBox. + for (let source of uniqueSources) { + if (candidates.includes(source)) { + return source; + } + } + + // Group with an already existing import. + for (let source of existingImports.keys()) { + if (candidates.includes(source)) { + return source; + } + } + + return candidates[0]; +} + +export default function transformer(file: FileInfo, api: API): string { + let specifiersByPackage = getSpecifiersByPackage(file.path); + let j = api.jscodeshift.withParser({ + parse(source: string) { + return recastParse(source, { + parser: { + parse(innerSource: string) { + return parse(innerSource, { + sourceType: 'module', + plugins: [ + 'jsx', + 'typescript', + 'importAssertions', + 'dynamicImport', + 'decorators-legacy', + 'classProperties', + 'classPrivateProperties', + 'classPrivateMethods', + 'exportDefaultFrom', + 'exportNamespaceFrom', + 'objectRestSpread', + 'optionalChaining', + 'nullishCoalescingOperator', + 'topLevelAwait' + ], + tokens: true, + errorRecovery: true + }); + } + } + }); + } + }); + + let root = j(file.source); + let program = root.get().node.program as t.Program; + let uniqueSources = new Set(); + let existingImports = new Map(); + + for (let node of program.body) { + if (node.type === 'ImportDeclaration') { + let source = node.source.value; + if (typeof source !== 'string') { + continue; + } + + existingImports.set(getImportDeclarationKey(node), node); + + if (source in specifiersByPackage) { + let sourceMap = specifiersByPackage[source]; + for (let specifier of node.specifiers ?? []) { + if (specifier.type !== 'ImportSpecifier') { + continue; + } + + let importedName = getImportedName(specifier); + let candidates = sourceMap[importedName]; + if (candidates && candidates.length === 1) { + let importKind = node.importKind || 'value'; + uniqueSources.add(importKind + ':' + candidates[0]); + } + } + } + } + } + + let didChange = false; + + program.body = program.body.flatMap(node => { + if (node.type !== 'ImportDeclaration') { + return [node]; + } + + let source = node.source.value; + if (typeof source !== 'string' || !(source in specifiersByPackage)) { + return [node]; + } + + let importDeclaration = node; + let sourceMap = specifiersByPackage[node.source.value]; + let movedSpecifiersBySource = new Map(); + + importDeclaration.specifiers = importDeclaration.specifiers.filter(specifier => { + if (specifier.type !== 'ImportSpecifier') { + return true; + } + + let importedName = getImportedName(specifier); + let candidates = sourceMap[importedName]; + if (!candidates || candidates.length === 0) { + return true; + } + + let importKind = node.importKind || 'value'; + let targetSource = candidates.length === 1 + ? importKind + ':' + candidates[0] + : resolveTargetSource(candidates.map(c => importKind + ':' + c), uniqueSources, existingImports); + + let movedSpecifiers = movedSpecifiersBySource.get(targetSource) ?? []; + movedSpecifiers.push(t.cloneNode(specifier, true)); + movedSpecifiersBySource.set(targetSource, movedSpecifiers); + didChange = true; + return false; + }); + + if (movedSpecifiersBySource.size === 0) { + return [node]; + } + + let newDeclarations: t.Statement[] = []; + if (importDeclaration.specifiers.length > 0) { + newDeclarations.push(node); + } + + for (let [targetSource, movedSpecifiers] of movedSpecifiersBySource) { + let destinationImport = existingImports.get(targetSource); + + if (!destinationImport) { + destinationImport = createImportDeclaration(targetSource.slice(targetSource.indexOf(':') + 1), importDeclaration.importKind, []); + newDeclarations.push(destinationImport); + existingImports.set(targetSource, destinationImport); + } + + let existingSpecifierKeys = getNamedSpecifierKeys(destinationImport); + for (let movedSpecifier of movedSpecifiers) { + let key = getImportKey(movedSpecifier); + if (!existingSpecifierKeys.has(key)) { + destinationImport.specifiers.push(movedSpecifier); + existingSpecifierKeys.add(key); + } + } + } + + return newDeclarations; + }); + + return didChange ? root.toSource({quote: 'single'}) : file.source; +}; diff --git a/packages/dev/codemods/src/use-subpaths/src/index.ts b/packages/dev/codemods/src/use-subpaths/src/index.ts new file mode 100644 index 00000000000..db00547bb63 --- /dev/null +++ b/packages/dev/codemods/src/use-subpaths/src/index.ts @@ -0,0 +1,14 @@ +import {run as jscodeshift} from 'jscodeshift/src/Runner.js'; +import path from 'path'; +import {UseSubpathsCodemodOptions} from '../..'; + +const transformPath = path.join(__dirname, 'codemod.js'); + +export async function use_subpaths(options: UseSubpathsCodemodOptions): Promise> { + let { + path: filePath = '.', + ...rest + } = options; + + return await jscodeshift(transformPath, [filePath], rest); +} diff --git a/packages/dev/codemods/src/use-subpaths/src/specifiers.ts b/packages/dev/codemods/src/use-subpaths/src/specifiers.ts new file mode 100644 index 00000000000..05098bf1d77 --- /dev/null +++ b/packages/dev/codemods/src/use-subpaths/src/specifiers.ts @@ -0,0 +1,72 @@ +/* eslint-disable max-depth */ +import fs from 'fs'; +import Module from 'module'; +import {parse} from '@babel/parser'; +import path from 'path'; +import url from 'url'; + +const PACKAGES = [ + '@adobe/react-spectrum', + '@react-spectrum/s2', + 'react-aria-components', + 'react-aria', + 'react-stately' +]; + +const specifiersByPackage: Record> = {}; + +/** Builds a mapping of monopackage -> export -> subpaths that contain the export. */ +export function getSpecifiersByPackage(from: string) { + for (let pkg of PACKAGES) { + if (specifiersByPackage[pkg]) { + continue; + } + + let dir: string; + try { + let pkgPath = path.dirname(Module.findPackageJSON(pkg, url.pathToFileURL(from || `${process.cwd()}/index`))!); + dir = `${pkgPath}/dist/types/exports`; + if (!fs.existsSync(dir)) { + dir = `${pkgPath}/exports`; + } + + if (!fs.existsSync(dir)) { + continue; + } + } catch { + continue; + } + + let exports: Record = {}; + for (let entry of fs.readdirSync(dir, {withFileTypes: true})) { + if (entry.name === 'index.ts' || entry.name === 'index.d.ts' || !entry.isFile()) { + continue; + } + + let contents = fs.readFileSync(`${dir}/${entry.name}`, 'utf8'); + let ast = parse(contents, { + sourceType: 'module', + plugins: ['typescript'] + }); + + let importSpecifier = `${pkg}/${entry.name.replace(/(\.d)?\.ts$/, '')}`; + for (let node of ast.program.body) { + if (node.type === 'ExportNamedDeclaration') { + for (let specifier of node.specifiers) { + if (specifier.type !== 'ExportSpecifier') { + continue; + } + + let exported = specifier.exported.type === 'Identifier' ? specifier.exported.name : specifier.exported.value; + exports[exported] ??= []; + exports[exported].push(importSpecifier); + } + } + } + + specifiersByPackage[pkg] = exports; + } + } + + return specifiersByPackage; +} diff --git a/packages/dev/parcel-transformer-css-env/CSSEnvTransformer.js b/packages/dev/parcel-transformer-css-env/CSSEnvTransformer.js index 400b9d3f2d9..bf6888f3b15 100644 --- a/packages/dev/parcel-transformer-css-env/CSSEnvTransformer.js +++ b/packages/dev/parcel-transformer-css-env/CSSEnvTransformer.js @@ -23,7 +23,7 @@ module.exports = new Transformer({ engines: { browsers: 'baseline widely available' }, - shouldOptimize: options.mode === 'production' + shouldOptimize: asset.env.shouldOptimize }); return [asset]; diff --git a/packages/dev/parcel-transformer-s2-icon/IconTransformer.js b/packages/dev/parcel-transformer-s2-icon/IconTransformer.js index 5082edffd5a..d9b1b3f3456 100644 --- a/packages/dev/parcel-transformer-s2-icon/IconTransformer.js +++ b/packages/dev/parcel-transformer-s2-icon/IconTransformer.js @@ -59,10 +59,10 @@ module.exports = new Transformer({ ] }, replaceAttrValues: { - 'var(--iconPrimary, #222)': `var(--iconPrimary, var(--lightningcss-light, ${tokens['gray-800'].sets.light.value}) var(--lightningcss-dark, ${tokens['gray-800'].sets.dark.value}))`, - 'var(--iconPrimary, #222222)': `var(--iconPrimary, var(--lightningcss-light, ${tokens['gray-800'].sets.light.value}) var(--lightningcss-dark, ${tokens['gray-800'].sets.dark.value}))`, - 'var(--iconPrimary, #292929)': `var(--iconPrimary, var(--lightningcss-light, ${tokens['gray-800'].sets.light.value}) var(--lightningcss-dark, ${tokens['gray-800'].sets.dark.value}))`, - 'var(--spectrum-global-color-gray-800, #292929)': `var(--iconPrimary, var(--lightningcss-light, ${tokens['gray-800'].sets.light.value}) var(--lightningcss-dark, ${tokens['gray-800'].sets.dark.value}))` + 'var(--iconPrimary, #222)': `var(--iconPrimary, light-dark(${tokens['gray-800'].sets.light.value}, ${tokens['gray-800'].sets.dark.value}))`, + 'var(--iconPrimary, #222222)': `var(--iconPrimary, light-dark(${tokens['gray-800'].sets.light.value}, ${tokens['gray-800'].sets.dark.value}))`, + 'var(--iconPrimary, #292929)': `var(--iconPrimary, light-dark(${tokens['gray-800'].sets.light.value}, ${tokens['gray-800'].sets.dark.value}))`, + 'var(--spectrum-global-color-gray-800, #292929)': `var(--iconPrimary, light-dark(${tokens['gray-800'].sets.light.value}, ${tokens['gray-800'].sets.dark.value}))` }, typescript: true, ref: true, diff --git a/packages/dev/s2-docs/pages/react-aria/blog/accessible-color-descriptions.mdx b/packages/dev/s2-docs/pages/react-aria/blog/accessible-color-descriptions.mdx index 73cdd921263..4de4560c70c 100644 --- a/packages/dev/s2-docs/pages/react-aria/blog/accessible-color-descriptions.mdx +++ b/packages/dev/s2-docs/pages/react-aria/blog/accessible-color-descriptions.mdx @@ -60,9 +60,9 @@ In addition to reducing the number of strings we need, these descriptions are al Our algorithm for generating color descriptions works in the [OKLCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) color space, recently standardized by CSS. This color space offers the advantage of uniform lightness across all hues, unlike HSL, where hues like blue appear significantly darker than hues like green or yellow at the same lightness value. The difference between HSL and OKLCH is shown below.
-
HSL
+
HSL
-
OKLCH
+
OKLCH
In HSL, certain hues also appear to shift as the lightness changes — for example, blue tends to shift toward purple as it gets lighter. This would lead to perceptually inaccurate descriptions, where colors that appear purple might be described as blue. OKLCH resolves this issue by maintaining a consistent hue across all lightness levels. diff --git a/packages/react-aria/src/table/useTableRow.ts b/packages/react-aria/src/table/useTableRow.ts index 1e3cb26b7a5..95df8d68fae 100644 --- a/packages/react-aria/src/table/useTableRow.ts +++ b/packages/react-aria/src/table/useTableRow.ts @@ -84,9 +84,20 @@ export function useTableRow(props: GridRowProps, state: TableState | Tr if ((e.key === EXPANSION_KEYS['expand'][direction]) && state.selectionManager.focusedKey === treeNode.key && hasChildRows && state.expandedKeys !== 'all' && !state.expandedKeys.has(treeNode.key)) { state.toggleKey(treeNode.key); e.stopPropagation(); - } else if ((e.key === EXPANSION_KEYS['collapse'][direction]) && state.selectionManager.focusedKey === treeNode.key && hasChildRows && (state.expandedKeys === 'all' || state.expandedKeys.has(treeNode.key))) { - state.toggleKey(treeNode.key); - e.stopPropagation(); + } else if ((e.key === EXPANSION_KEYS['collapse'][direction]) && state.selectionManager.focusedKey === treeNode.key) { + if (state.expandedKeys !== 'all') { + if (hasChildRows && state.expandedKeys.has(treeNode.key)) { + state.toggleKey(treeNode.key); + e.stopPropagation(); + } else if (!state.expandedKeys.has(treeNode.key) && treeNode.parentKey && treeNode.level > 0) { + // Item is a leaf or already collapsed, move focus to parent + state.selectionManager.setFocusedKey(treeNode.parentKey); + e.stopPropagation(); + } + } else if (state.expandedKeys === 'all') { + state.toggleKey(treeNode.key); + e.stopPropagation(); + } } }, 'aria-expanded': hasChildRows ? state.expandedKeys === 'all' || state.expandedKeys.has(node.key) : undefined, diff --git a/packages/react-stately/exports/useAsyncList.ts b/packages/react-stately/exports/useAsyncList.ts index 44f2b577478..177f132a2be 100644 --- a/packages/react-stately/exports/useAsyncList.ts +++ b/packages/react-stately/exports/useAsyncList.ts @@ -11,4 +11,4 @@ */ export {useAsyncList} from '../src/data/useAsyncList'; -export type {AsyncListOptions, AsyncListData} from '../src/data/useAsyncList'; +export type {AsyncListOptions, AsyncListData, AsyncListLoadFunction, AsyncListLoadOptions, AsyncListStateUpdate} from '../src/data/useAsyncList'; diff --git a/packages/react-stately/src/data/useAsyncList.ts b/packages/react-stately/src/data/useAsyncList.ts index c63ff8713b4..2d81599a1e2 100644 --- a/packages/react-stately/src/data/useAsyncList.ts +++ b/packages/react-stately/src/data/useAsyncList.ts @@ -32,9 +32,9 @@ export interface AsyncListOptions { sort?: AsyncListLoadFunction & {sortDescriptor: SortDescriptor}> } -type AsyncListLoadFunction = AsyncListLoadOptions> = (state: S) => AsyncListStateUpdate | Promise>; +export type AsyncListLoadFunction = AsyncListLoadOptions> = (state: S) => AsyncListStateUpdate | Promise>; -interface AsyncListLoadOptions { +export interface AsyncListLoadOptions { /** The items currently in the list. */ items: T[], /** The keys of the currently selected items in the list. */ @@ -51,7 +51,7 @@ interface AsyncListLoadOptions { loadingState?: LoadingState } -interface AsyncListStateUpdate { +export interface AsyncListStateUpdate { /** The new items to append to the list. */ items: Iterable, /** The keys to add to the selection. */ diff --git a/yarn.lock b/yarn.lock index 64e69118b68..fd94d936c89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20615,92 +20615,102 @@ __metadata: languageName: node linkType: hard -"lightningcss-darwin-arm64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-darwin-arm64@npm:1.30.1" +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"lightningcss-darwin-x64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-darwin-x64@npm:1.30.1" +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"lightningcss-freebsd-x64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-freebsd-x64@npm:1.30.1" +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"lightningcss-linux-arm-gnueabihf@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.1" +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"lightningcss-linux-arm64-gnu@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm64-gnu@npm:1.30.1" +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-arm64-musl@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm64-musl@npm:1.30.1" +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"lightningcss-linux-x64-gnu@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-x64-gnu@npm:1.30.1" +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-x64-musl@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-x64-musl@npm:1.30.1" +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"lightningcss-win32-arm64-msvc@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-win32-arm64-msvc@npm:1.30.1" +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"lightningcss-win32-x64-msvc@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-win32-x64-msvc@npm:1.30.1" +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"lightningcss@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss@npm:1.30.1" +"lightningcss@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" dependencies: detect-libc: "npm:^2.0.3" - lightningcss-darwin-arm64: "npm:1.30.1" - lightningcss-darwin-x64: "npm:1.30.1" - lightningcss-freebsd-x64: "npm:1.30.1" - lightningcss-linux-arm-gnueabihf: "npm:1.30.1" - lightningcss-linux-arm64-gnu: "npm:1.30.1" - lightningcss-linux-arm64-musl: "npm:1.30.1" - lightningcss-linux-x64-gnu: "npm:1.30.1" - lightningcss-linux-x64-musl: "npm:1.30.1" - lightningcss-win32-arm64-msvc: "npm:1.30.1" - lightningcss-win32-x64-msvc: "npm:1.30.1" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" dependenciesMeta: + lightningcss-android-arm64: + optional: true lightningcss-darwin-arm64: optional: true lightningcss-darwin-x64: @@ -20721,7 +20731,7 @@ __metadata: optional: true lightningcss-win32-x64-msvc: optional: true - checksum: 10c0/1e1ad908f3c68bf39d964a6735435a8dd5474fb2765076732d64a7b6aa2af1f084da65a9462443a9adfebf7dcfb02fb532fce1d78697f2a9de29c8f40f09aee3 + checksum: 10c0/70945bd55097af46fc9fab7f5ed09cd5869d85940a2acab7ee06d0117004a1d68155708a2d462531cea2fc3c67aefc9333a7068c80b0b78dd404c16838809e03 languageName: node linkType: hard