diff --git a/.chromatic-fc/.parcelrc-v3 b/.chromatic-fc/.parcelrc-v3 new file mode 100644 index 00000000000..654bb815133 --- /dev/null +++ b/.chromatic-fc/.parcelrc-v3 @@ -0,0 +1,41 @@ +{ + "extends": ["../parcel-v3/.parcelrc-icons", "@parcel/config-default"], + "resolvers": ["@parcel/resolver-glob", "..."], + "transformers": { + "docs:*": ["@parcel/transformer-ts-doc"], + "*.stories.{ts,tsx,js,mjs,jsm,jsx,es6,cjs}": [ + "@parcel/transformer-storybook-v3", + "..." + ], + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "packages/@adobe/**/*.css": [ + "../parcel-v3/PostCSSTransformer.mjs", + { + "plugin": "@parcel/transformer-css", + "config": { + "cssModules": true, + "pseudoClasses": { + "focusVisible": "focus-ring", + "hover": "is-hovered" + } + } + } + ], + "raw:*": ["@parcel/transformer-raw"] + }, + "bundler": { + "plugin": "@parcel/bundler-default", + "config": { + "manualSharedBundles": [ + { + "assets": [ + "packages/@react-spectrum/s2/**", + "packages/dev/s2-docs/src/**", + "packages/dev/s2-docs/pages/s2/**/*.{mdx,tsx,ts,js}" + ], + "types": ["css"] + } + ] + } + } +} diff --git a/.chromatic-fc/main.mjs b/.chromatic-fc/main.mjs index 8abcc5fbab3..7a6bfc038f1 100644 --- a/.chromatic-fc/main.mjs +++ b/.chromatic-fc/main.mjs @@ -5,7 +5,7 @@ export default { }, stories: [ '../packages/**/chromatic-fc/**/*.stories.{js,jsx,ts,tsx}', - '../packages/@react-spectrum/s2/chromatic/*.stories.@(js|jsx|mjs|ts|tsx)' + '../packages/@react-spectrum/s2/chromatic/*.stories.{js,jsx,mjs,ts,tsx}' ], addons: process.env.NODE_ENV === 'production' ? [] : ['storybook/actions', '@storybook/addon-a11y'], diff --git a/.chromatic/.parcelrc-v3 b/.chromatic/.parcelrc-v3 new file mode 100644 index 00000000000..654bb815133 --- /dev/null +++ b/.chromatic/.parcelrc-v3 @@ -0,0 +1,41 @@ +{ + "extends": ["../parcel-v3/.parcelrc-icons", "@parcel/config-default"], + "resolvers": ["@parcel/resolver-glob", "..."], + "transformers": { + "docs:*": ["@parcel/transformer-ts-doc"], + "*.stories.{ts,tsx,js,mjs,jsm,jsx,es6,cjs}": [ + "@parcel/transformer-storybook-v3", + "..." + ], + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "packages/@adobe/**/*.css": [ + "../parcel-v3/PostCSSTransformer.mjs", + { + "plugin": "@parcel/transformer-css", + "config": { + "cssModules": true, + "pseudoClasses": { + "focusVisible": "focus-ring", + "hover": "is-hovered" + } + } + } + ], + "raw:*": ["@parcel/transformer-raw"] + }, + "bundler": { + "plugin": "@parcel/bundler-default", + "config": { + "manualSharedBundles": [ + { + "assets": [ + "packages/@react-spectrum/s2/**", + "packages/dev/s2-docs/src/**", + "packages/dev/s2-docs/pages/s2/**/*.{mdx,tsx,ts,js}" + ], + "types": ["css"] + } + ] + } + } +} diff --git a/.chromatic/main.mjs b/.chromatic/main.mjs index 1eecedc39ad..e68b1855be0 100644 --- a/.chromatic/main.mjs +++ b/.chromatic/main.mjs @@ -4,8 +4,8 @@ export default { options: {} }, stories: [ - '../packages/**/chromatic/**/*.stories.@(js|jsx|ts|tsx)', - '../packages/@react-spectrum/s2/chromatic/*.stories.@(js|jsx|mjs|ts|tsx)' + '../packages/**/chromatic/**/*.stories.{js,jsx,ts,tsx}', + '../packages/@react-spectrum/s2/chromatic/*.stories.{js,jsx,mjs,ts,tsx}' ], addons: process.env.NODE_ENV === 'production' ? [] : ['storybook/actions', '@storybook/addon-a11y'], diff --git a/.circleci/config.yml b/.circleci/config.yml index 4143cad465b..5611bbbbde5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,32 +22,30 @@ executors: docker: - image: cimg/node:24.14.1 resource_class: medium.gen2 - environment: + environment: &global-environment CACHE_VERSION: v1 + # PARCEL_V3: '1' working_directory: ~/react-spectrum rsp-large: docker: - image: cimg/node:24.14.1 resource_class: large.gen2 - environment: - CACHE_VERSION: v1 + environment: *global-environment working_directory: ~/react-spectrum rsp-xlarge: docker: - image: cimg/node:24.14.1 resource_class: xlarge.gen2 - environment: - CACHE_VERSION: v1 + environment: *global-environment working_directory: ~/react-spectrum rsp-2xlarge: docker: - image: cimg/node:24.14.1 resource_class: 2xlarge.gen2 - environment: - CACHE_VERSION: v1 + environment: *global-environment working_directory: ~/react-spectrum commands: @@ -563,7 +561,7 @@ jobs: - run: name: build s2 storybook - command: yarn build:storybook-s2 + command: make storybook-s2 - persist_to_workspace: root: dist diff --git a/.storybook-s2/.parcelrc b/.storybook-s2/.parcelrc index 51b7698f539..fcb30d4a6ac 100644 --- a/.storybook-s2/.parcelrc +++ b/.storybook-s2/.parcelrc @@ -8,7 +8,6 @@ // Disable PostCSS from running over style macro output "*.css": ["@parcel/transformer-css"], "*.svg": ["@parcel/transformer-svg-react"], - "*.{mdx,md}": ["parcel-transformer-mdx-storybook"], "raw:*": ["@parcel/transformer-raw"] } } diff --git a/.storybook-s2/.parcelrc-v3 b/.storybook-s2/.parcelrc-v3 new file mode 100644 index 00000000000..4467104b911 --- /dev/null +++ b/.storybook-s2/.parcelrc-v3 @@ -0,0 +1,29 @@ +{ + "extends": ["../parcel-v3/.parcelrc-icons", "@parcel/config-default"], + "resolvers": ["@parcel/resolver-glob", "..."], + "transformers": { + "docs:*": ["@parcel/transformer-ts-doc"], + "*.stories.{ts,tsx,js,mjs,jsm,jsx,es6,cjs}": [ + "@parcel/transformer-storybook-v3", + "..." + ], + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "*.css": ["@parcel/transformer-css"], + "raw:*": ["@parcel/transformer-raw"] + }, + "bundler": { + "plugin": "@parcel/bundler-default", + "config": { + "manualSharedBundles": [ + { + "assets": [ + "packages/@react-spectrum/s2/**", + "packages/dev/s2-docs/src/**", + "packages/dev/s2-docs/pages/s2/**/*.{mdx,tsx,ts,js}" + ], + "types": ["css"] + } + ] + } + } +} diff --git a/.storybook-s2/docs/Icons.jsx b/.storybook-s2/docs/Icons.jsx index 8d6ec48b061..12d0afe5486 100644 --- a/.storybook-s2/docs/Icons.jsx +++ b/.storybook-s2/docs/Icons.jsx @@ -20,7 +20,7 @@ export function Icons() {

See below for a full list of available icons. Click to copy import statement.

{Object.keys(icons).map(icon => { - let Icon = icons[icon].default; + let Icon = icons[icon].default || icons[icon]; let name = icon.replace(/^S2_Icon_(.*?)(Size\d+)?_2.*/, '$1'); let importName = name.replace(/^(\d)/, '_$1'); return ( diff --git a/.storybook-s2/docs/Illustrations.jsx b/.storybook-s2/docs/Illustrations.jsx index 87189ec8464..2c1cd134e86 100644 --- a/.storybook-s2/docs/Illustrations.jsx +++ b/.storybook-s2/docs/Illustrations.jsx @@ -1,5 +1,5 @@ -import linearIllustrations from '@react-spectrum/s2/spectrum-illustrations/linear/*.tsx'; -import gradientIllustrations from '@react-spectrum/s2/spectrum-illustrations/gradient/*/*.tsx'; +import linearIllustrations from '../../packages/@react-spectrum/s2/spectrum-illustrations/linear/*.tsx'; +import gradientIllustrations from '../../packages/@react-spectrum/s2/spectrum-illustrations/gradient/*/*.tsx'; import Paste from '@react-spectrum/s2/icons/Paste'; import {style} from '../../packages/@react-spectrum/s2/style/spectrum-theme' with {type: 'macro'}; import {ActionButton, Radio, RadioGroup} from '@react-spectrum/s2'; @@ -48,7 +48,8 @@ export function Illustrations() { marginTop: 32 })}> {Object.keys(gradientIllustrations[gradientStyle]).map(icon => { - let Illustration = gradientIllustrations[gradientStyle][icon].default; + let Illustration = gradientIllustrations[gradientStyle][icon]; + Illustration = Illustration.default || Illustration; return (
{Object.keys(linearIllustrations).map(icon => { - let Illustration = linearIllustrations[icon].default; + let Illustration = linearIllustrations[icon]; + Illustration = Illustration.default || Illustration; return (

{children}

, @@ -23,7 +23,7 @@ export function MDXLayout({children}) { return (
- {children} + {cloneElement(children, {components: mdxComponents})}
); diff --git a/.storybook-s2/docs/Release Notes.mdx b/.storybook-s2/docs/Release Notes.mdx index 08b58379a3f..83e799356dd 100644 --- a/.storybook-s2/docs/Release Notes.mdx +++ b/.storybook-s2/docs/Release Notes.mdx @@ -239,7 +239,7 @@ added to help you generate a properly optimized output when using the bundler of ### Updates - [ProgressBar](?path=/docs/progressbar--docs): Support side label, update edges to be rounded, and support custom widths -- [ProgressCircle](<(?path=/docs/progresscircle--docs)>): Update edges to be rounded +- [ProgressCircle](?path=/docs/progresscircle--docs): Update edges to be rounded - [Badge](?path=/docs/badge--docs): Add subtle and outline fill variants - [Breadcrumbs](?path=/docs/breadcrumbs--docs): Add collapse behavior - [Button](?path=/docs/button--docs): Add support for pending state diff --git a/.storybook-s2/docs/highlight.js b/.storybook-s2/docs/highlight.js index 9ffaf9435f8..22b10ab2a42 100644 --- a/.storybook-s2/docs/highlight.js +++ b/.storybook-s2/docs/highlight.js @@ -1,5 +1,5 @@ -const treeSitter = require('tree-sitter-highlight'); -const tokens = require('@adobe/spectrum-tokens/dist/json/variables.json'); +// const treeSitter = require('tree-sitter-highlight'); +// const tokens = require('@adobe/spectrum-tokens/dist/json/variables.json'); function colorToken(name) { let token = tokens[name]; @@ -7,32 +7,33 @@ function colorToken(name) { } exports.highlight = function (code, lang = 'JSX') { - this.addAsset({ - type: 'css', - content: ` -.keyword { - color: ${colorToken('magenta-1000')} -} -.string { - color: ${colorToken('green-1000')} -} -.number { - color: ${colorToken('pink-1000')} -} -.property, .attribute { - color: ${colorToken('indigo-1000')} -} -.function, .tag, .constructor { - color: ${colorToken('red-1000')} -} -.comment { - color: ${colorToken('gray-700')} -} -.variable { - color: ${colorToken('fuchsia-1000')} -} -` - }); + return code; + // this.addAsset({ + // type: 'css', + // content: ` + // .keyword { + // color: ${colorToken('magenta-1000')} + // } + // .string { + // color: ${colorToken('green-1000')} + // } + // .number { + // color: ${colorToken('pink-1000')} + // } + // .property, .attribute { + // color: ${colorToken('indigo-1000')} + // } + // .function, .tag, .constructor { + // color: ${colorToken('red-1000')} + // } + // .comment { + // color: ${colorToken('gray-700')} + // } + // .variable { + // color: ${colorToken('fuchsia-1000')} + // } + // ` + // }); - return treeSitter.highlight(code, treeSitter.Language[lang]); + // return treeSitter.highlight(code, treeSitter.Language[lang]); }; diff --git a/.storybook-s2/main.ts b/.storybook-s2/main.ts index 7d9ac62341c..5b87a8dc038 100644 --- a/.storybook-s2/main.ts +++ b/.storybook-s2/main.ts @@ -20,8 +20,8 @@ const localAddon = (rel: string) => fileURLToPath(import.meta.resolve(rel)); const config: StorybookConfig = { stories: [ './docs/*.mdx', - '../packages/@react-spectrum/s2/stories/*.stories.@(js|jsx|mjs|ts|tsx)', - '../packages/@react-spectrum/ai/stories/*.stories.@(js|jsx|mjs|ts|tsx)' + '../packages/@react-spectrum/s2/stories/*.stories.{js,jsx,mjs,ts,tsx}', + '../packages/@react-spectrum/ai/stories/*.stories.{js,jsx,mjs,ts,tsx}' ], addons: [ localAddon('./custom-addons/provider/preset.ts'), diff --git a/.storybook-s2/preview.tsx b/.storybook-s2/preview.tsx index ce6dd315a43..130f2ee436d 100644 --- a/.storybook-s2/preview.tsx +++ b/.storybook-s2/preview.tsx @@ -35,12 +35,70 @@ channel.on(DARK_MODE_EVENT_NAME, (isDark: boolean) => { document.documentElement.dataset.colorScheme = isDark ? 'dark' : 'light'; }); +const exclude = [ + 'key', + 'ref', + 'id', + 'slot', + 'onCopy', + 'onCut', + 'onPaste', + 'onCompositionStart', + 'onCompositionEnd', + 'onCompositionUpdate', + 'onSelect', + 'onBeforeInput', + 'onInput', + 'onKeyDown', + 'onKeyUp', + 'onHoverStart', + 'onHoverEnd', + 'onHoverChange', + 'onFocus', + 'onBlur', + 'onFocusChange', + 'onClick', + 'onAuxClick', + 'onContextMenu', + 'onDoubleClick', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onTouchCancel', + 'onTouchEnd', + 'onTouchMove', + 'onTouchStart', + 'onPointerDown', + 'onPointerMove', + 'onPointerUp', + 'onPointerCancel', + 'onPointerEnter', + 'onPointerLeave', + 'onPointerOver', + 'onPointerOut', + 'onGotPointerCapture', + 'onLostPointerCapture', + 'onScroll', + 'onWheel', + 'onAnimationStart', + 'onAnimationEnd', + 'onAnimationIteration', + 'onTransitionCancel', + 'onTransitionEnd', + 'onTransitionRun', + 'onTransitionStart' +]; + /** @type {import('@storybook/react').Preview} */ const preview = { parameters: { controls: { matchers: {}, - exclude: ['key', 'ref'] + exclude: new RegExp(`^(((${exclude.join('|')})(Capture)?)|aria-.*)$`) }, docs: { container: props => { diff --git a/.storybook/.parcelrc-v3 b/.storybook/.parcelrc-v3 new file mode 100644 index 00000000000..d26b17c9b8e --- /dev/null +++ b/.storybook/.parcelrc-v3 @@ -0,0 +1,26 @@ +{ + "extends": "@parcel/config-default", + "resolvers": ["@parcel/resolver-glob", "..."], + "transformers": { + "docs:*": ["@parcel/transformer-ts-doc"], + "*.stories.{ts,tsx,js,mjs,jsm,jsx,es6,cjs}": [ + "@parcel/transformer-storybook-v3", + "..." + ], + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "packages/@adobe/**/*.css": [ + "../parcel-v3/PostCSSTransformer.mjs", + { + "plugin": "@parcel/transformer-css", + "config": { + "cssModules": true, + "pseudoClasses": { + "focusVisible": "focus-ring", + "hover": "is-hovered" + } + } + } + ], + "raw:*": ["@parcel/transformer-raw"] + } +} diff --git a/AGENTS.md b/AGENTS.md index 2649d884eaa..c8a140f56fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,6 +24,23 @@ This repo does **not** use the conventional JS toolchain — use these, don't sw - **Match the surrounding code** — follow the naming, structure, and patterns of neighboring files. - **Commit format** — use conventional-commit prefixes (`fix:`, `feat:`, `chore:`, `docs:`) as seen in the git history. +## Filing GitHub issues + +This repository uses GitHub issue **forms** defined in [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/) (`Bug_Report.yml`, `Feature_Request.yml`, `Documentation.yml`, `Feedback.yml`). Blank issues are disabled, and `CONTRIBUTING.md` asks contributors to follow the templates. Please do the same when filing on a user's behalf: + +- **Search first.** Check existing open and closed issues to avoid duplicates. +- **Prefer the web flow** so the correct form is applied: +- **If filing via the `gh` CLI, replicate the template's fields in the body.** `gh issue create --template` does *not* render these YAML issue forms, so passing `--template` alone will not produce a valid issue. Instead, fill in the chosen template's fields manually. For a bug report (`Bug_Report.yml`), include: + - a general summary + - Expected Behavior + - Current Behavior + - Possible Solution (optional) + - Context (how it affects you / what you're trying to accomplish) + - Steps to Reproduce, a minimal repro, ideally a StackBlitz + - Version, Browsers, Operating System +- **Questions and open-ended discussion** belong in [Discussions](https://github.com/adobe/react-spectrum/discussions), not the issue tracker. +- **Security issues** must not be filed on the public tracker. Follow the process in `CONTRIBUTING.md`. + ## Task-specific workflows Read the relevant file before starting that kind of work (other agents: read the file directly; Claude will surface it): diff --git a/Makefile b/Makefile index 2e96af6568b..775eac93450 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ PATH := ./node_modules/.bin:$(PATH) BRANCH := $(or $(CIRCLE_BRANCH),$(shell git rev-parse --abbrev-ref HEAD)) BRANCH_TYPE := $(if $(filter $(BRANCH),main),main,pr) HASH := $(shell git rev-parse HEAD) +BUILD_ENTRIES := packages/@react-{spectrum,aria,stately}/*/ packages/@internationalized/{message,string,date,number}/ packages/{react-aria,react-stately,react-aria-components,@adobe/react-spectrum} all: node_modules @@ -38,7 +39,7 @@ clean_dist: rm -rf packages/{react-aria,react-aria-components,react-stately}/i18n rm -rf packages/@adobe/react-spectrum/i18n rm -rf packages/@react-aria/i18n/server - rm -rf packages/@react-spectrum/s2/style/dist packages/@react-spectrum/s2/page.css packages/@react-spectrum/s2/icons packages/@react-spectrum/s2/illustrations + rm -rf packages/@react-spectrum/s2/style/dist packages/@react-spectrum/s2/icons packages/@react-spectrum/s2/illustrations git clean -Xdf packages/{@adobe/react-spectrum,@react-spectrum/s2,react-aria,react-stately,react-aria-components} clean_parcel: @@ -83,7 +84,18 @@ icons: packages/@spectrum-icons/workflow/src packages/@spectrum-icons/color/src @$(MAKE) illustrations storybook: - NODE_ENV=production yarn build:storybook + if [ -n "$$PARCEL_V3" ]; then \ + yarn build:storybook-parcel3; \ + else \ + NODE_ENV=production yarn build:storybook; \ + fi + +storybook-s2: + if [ -n "$$PARCEL_V3" ]; then \ + yarn build:storybook-s2-parcel3; \ + else \ + yarn build:storybook-s2; \ + fi # for now doesn't have deploy since v3 doesn't have a place for docs and stuff yet ci: @@ -101,7 +113,12 @@ publish-nightly: build build: mkdir -p dist yarn tsgo --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist/types --rootDir packages - parcel build packages/@react-{spectrum,aria,stately}/*/ packages/@internationalized/{message,string,date,number}/ packages/{react-aria,react-stately,react-aria-components,@adobe/react-spectrum} --no-optimize --config .parcelrc-build + if [ -n "$$PARCEL_V3" ]; then \ + node scripts/generateS2IconIndex.js; \ + parcel3 build $(BUILD_ENTRIES) --no-optimize --config parcel-v3/.parcelrc-build; \ + else \ + parcel build $(BUILD_ENTRIES) --no-optimize --config .parcelrc-build; \ + fi yarn workspaces foreach --all -pt run prepublishOnly node scripts/buildEsm.js node scripts/buildI18n.js @@ -193,7 +210,7 @@ build-s2-docs: check-starter-css yarn workspace @react-spectrum/s2-docs generate:md yarn workspace @react-spectrum/s2-docs generate:og LIBRARY=react-aria node scripts/buildRegistry.mjs - yarn build:s2-docs + if [ -n "$$PARCEL_V3" ]; then yarn build:s2-docs-parcel3; else yarn build:s2-docs; fi LIBRARY=react-aria node scripts/createFeedS2.mjs mkdir -p dist/s2-docs/react-aria/$(PUBLIC_URL) mkdir -p dist/s2-docs/s2/$(PUBLIC_URL) diff --git a/package.json b/package.json index c95f33fe76f..1833ad309fb 100644 --- a/package.json +++ b/package.json @@ -32,18 +32,26 @@ "install-18": "node scripts/react-18-install-prep.mjs && yarn add react@^18 react-dom@^18 react-test-renderer@^18.3.1 && node scripts/oldReactSupport.mjs", "install-canary": "node scripts/react-canary-install-prep.mjs && yarn add react@canary react-dom@canary", "start": "cross-env NODE_ENV=storybook storybook dev -p 9003 --ci -c '.storybook'", + "start-parcel3": "cross-env NODE_ENV=storybook PARCEL_V3=1 storybook dev -p 9003 --ci -c '.storybook'", "build:storybook": "storybook build -c .storybook -o dist/$(git rev-parse HEAD)/storybook", + "build:storybook-parcel3": "PARCEL_V3=1 storybook build -c .storybook -o dist/$(git rev-parse HEAD)/storybook", "start:chromatic": "CHROMATIC=1 NODE_ENV=storybook storybook dev -p 9004 --ci -c '.chromatic'", + "start:chromatic-parcel3": "PARCEL_V3=1 CHROMATIC=1 NODE_ENV=storybook storybook dev -p 9004 --ci -c '.chromatic'", "build:chromatic": "CHROMATIC=1 storybook build -c .chromatic -o dist/$(git rev-parse HEAD)/chromatic --stats-json", "start:chromatic-fc": "CHROMATIC=1 NODE_ENV=storybook storybook dev -p 9005 --ci -c '.chromatic-fc'", + "start:chromatic-fc-parcel3": "PARCEL_V3=1 CHROMATIC=1 NODE_ENV=storybook storybook dev -p 9005 --ci -c '.chromatic-fc'", "build:chromatic-fc": "CHROMATIC=1 storybook build -c .chromatic-fc -o dist/$(git rev-parse HEAD)/chromatic-fc --stats-json", "start:s2": "NODE_ENV=storybook storybook dev -p 6006 --ci -c '.storybook-s2'", + "start:s2-parcel3": "NODE_ENV=storybook PARCEL_V3=1 storybook dev -p 6006 --ci -c '.storybook-s2'", "build:storybook-s2": "NODE_ENV=storybook storybook build -c .storybook-s2 -o dist/$(git rev-parse HEAD)/storybook-s2", + "build:storybook-s2-parcel3": "NODE_ENV=storybook PARCEL_V3=1 storybook build -c .storybook-s2 -o dist/$(git rev-parse HEAD)/storybook-s2", "build:s2-storybook-docs": "NODE_ENV=storybook storybook build -c .storybook-s2 --docs", "start:docs": "DOCS_ENV=dev parcel 'packages/@adobe/react-spectrum/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", "build:docs": "DOCS_ENV=staging parcel build 'packages/@adobe/react-spectrum/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", "start:s2-docs": "yarn workspace @react-spectrum/s2-docs start", + "start:s2-docs-parcel3": "yarn workspace @react-spectrum/s2-docs start:parcel3", "build:s2-docs": "yarn workspace @react-spectrum/s2-docs build", + "build:s2-docs-parcel3": "yarn workspace @react-spectrum/s2-docs build:parcel3", "check:s2-docs-build": "node packages/dev/s2-docs/scripts/validateS2DocsBuild.mjs", "build:mcp": "yarn workspace @react-spectrum/mcp build && yarn workspace @react-aria/mcp build", "generate:skills": "node packages/dev/s2-docs/scripts/generateAgentSkills.mjs", @@ -106,6 +114,7 @@ "@parcel/optimizer-terser": "^2.16.3", "@parcel/packager-react-static": "^2.16.3", "@parcel/packager-ts": "^2.16.3", + "@parcel/parcel3": "^0.0.6", "@parcel/reporter-bundle-analyzer": "^2.16.3", "@parcel/reporter-cli": "^2.16.3", "@parcel/resolver-glob": "^2.16.3", @@ -113,7 +122,9 @@ "@parcel/transformer-inline": "^2.16.3", "@parcel/transformer-inline-string": "^2.16.3", "@parcel/transformer-react-static": "^2.16.3", + "@parcel/transformer-storybook-v3": "^0.0.2", "@parcel/transformer-svg-react": "^2.16.3", + "@parcel/transformer-ts-doc": "^0.0.2", "@parcel/transformer-typescript-types": "^2.16.3", "@react-spectrum/parcel-namer-s2": "workspace:^", "@react-spectrum/s2-icon-builder": "workspace:^", @@ -244,6 +255,7 @@ "recast": "0.23.6", "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", + "rsc-html-stream": "0.0.8", "svgo": "^3" }, "packageManager": "yarn@4.13.0", diff --git a/packages/@adobe/react-spectrum/package.json b/packages/@adobe/react-spectrum/package.json index ab6170de3d7..26411e885ad 100644 --- a/packages/@adobe/react-spectrum/package.json +++ b/packages/@adobe/react-spectrum/package.json @@ -26,6 +26,7 @@ "./*": { "source": "./exports/*.ts", "types": "./dist/types/exports/*.d.ts", + "legacy-module": "./dist/exports/*.js", "import": "./dist/exports/*.mjs", "require": "./dist/exports/*.cjs" } diff --git a/packages/@adobe/react-spectrum/src/provider/Provider.tsx b/packages/@adobe/react-spectrum/src/provider/Provider.tsx index fe7e6ac0535..9cdf57cbc99 100644 --- a/packages/@adobe/react-spectrum/src/provider/Provider.tsx +++ b/packages/@adobe/react-spectrum/src/provider/Provider.tsx @@ -16,6 +16,7 @@ import clsx from 'clsx'; import {Context} from './context'; import {DOMRef} from '@react-types/shared'; import {filterDOMProps} from 'react-aria/filterDOMProps'; +import {getVersion} from './version-macro' with {type: 'macro'}; import {I18nProvider, useLocale} from 'react-aria/I18nProvider'; import {ModalProvider, useModalProvider} from 'react-aria/private/overlays/useModal'; import {ProviderContext, ProviderProps} from './types'; @@ -27,8 +28,9 @@ import typographyStyles from '@adobe/spectrum-css-temp/components/typography/ind import {useColorScheme, useScale} from './mediaQueries'; import {useDOMRef} from '../utils/useDOMRef'; import {useStyleProps} from '../utils/styleProps'; -// @ts-ignore -import {version} from '../../package.json'; + +// A macro ensures that we don't import the entire package.json. +const version = getVersion(); const DEFAULT_BREAKPOINTS = {S: 640, M: 768, L: 1024, XL: 1280, XXL: 1536}; diff --git a/packages/@adobe/react-spectrum/src/provider/version-macro.ts b/packages/@adobe/react-spectrum/src/provider/version-macro.ts new file mode 100644 index 00000000000..ced1395de41 --- /dev/null +++ b/packages/@adobe/react-spectrum/src/provider/version-macro.ts @@ -0,0 +1,5 @@ +import {version} from '../../package.json'; + +export function getVersion() { + return version; +} diff --git a/packages/@react-spectrum/ai/chromatic/Prose.stories.tsx b/packages/@react-spectrum/ai/chromatic/Prose.stories.tsx index 67392a5fa3f..492ef269f96 100644 --- a/packages/@react-spectrum/ai/chromatic/Prose.stories.tsx +++ b/packages/@react-spectrum/ai/chromatic/Prose.stories.tsx @@ -12,7 +12,7 @@ import {Button} from '@react-spectrum/s2/Button'; import type {Meta, StoryObj} from '@storybook/react'; -import {prose} from '@react-spectrum/ai/style' with {type: 'macro'}; +import {prose} from '../src/style/prose' with {type: 'macro'}; import ProseExample from '../stories/prose.mdx'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; diff --git a/packages/@react-spectrum/ai/stories/prose.stories.tsx b/packages/@react-spectrum/ai/stories/prose.stories.tsx index 4f008187549..0cf681e8830 100644 --- a/packages/@react-spectrum/ai/stories/prose.stories.tsx +++ b/packages/@react-spectrum/ai/stories/prose.stories.tsx @@ -12,7 +12,7 @@ import {createPortal} from 'react-dom'; import type {Meta} from '@storybook/react'; -import {prose} from '@react-spectrum/ai/style' with {type: 'macro'}; +import {prose} from '../src/style/prose' with {type: 'macro'}; import ProseExample from './prose.mdx'; // @ts-ignore import React, {ReactNode, useRef, useState} from 'react'; diff --git a/packages/@react-spectrum/s2/.gitignore b/packages/@react-spectrum/s2/.gitignore index 54847193419..6ba951bac06 100644 --- a/packages/@react-spectrum/s2/.gitignore +++ b/packages/@react-spectrum/s2/.gitignore @@ -1,4 +1,3 @@ icons illustrations -page.css -page.css.map +Icon diff --git a/packages/@react-spectrum/s2/package.json b/packages/@react-spectrum/s2/package.json index 45fdcc3d6ee..ccf596f80e8 100644 --- a/packages/@react-spectrum/s2/package.json +++ b/packages/@react-spectrum/s2/package.json @@ -12,9 +12,9 @@ "dist", "style", "page.css", - "page.css.map", "icons", "illustrations", + "Icon", "src", "exports", "*.js" @@ -34,16 +34,17 @@ "require": "./dist/exports/index.cjs" }, "./page.css": { - "source": "./src/page.ts", "types": "./dist/types/src/page.d.ts", "default": "./page.css" }, "./style": { "source": "./style/index.ts", "types": "./dist/types/style/index.d.ts", - "module": "./style/dist/module.mjs", - "import": "./style/dist/module.mjs", - "require": "./style/dist/main.cjs" + "node": { + "module": "./style/dist/module.mjs", + "import": "./style/dist/module.mjs", + "require": "./style/dist/main.cjs" + } }, "./icons/*": { "source": "./s2wf-icons/S2_Icon_*_20_N.svg", @@ -80,7 +81,7 @@ "access": "public" }, "scripts": { - "prepublishOnly": "rm -rf dist/page.cjs* && mv dist/private/page*.css page.css && mv dist/private/page.css.map page.css.map || true", + "build:page-css": "node scripts/buildPageCss.mjs", "prepack": "npm pkg delete scripts devDependencies resolutions alias targets", "postpack": "git checkout -- package.json" }, @@ -108,17 +109,12 @@ "react-dom": "^19.0.0-rc.1" }, "browserslist": "last 2 Chrome versions, last 2 Safari versions, last 2 Firefox versions, last 2 Edge versions", - "page": "dist/page.cjs", "style-main": "style/dist/main.cjs", "style-module": "style/dist/module.mjs", "targets": { "module": false, "main": false, "types": false, - "page": { - "source": "src/page.ts", - "isLibrary": true - }, "style-module": { "source": "style/index.ts", "isLibrary": true, diff --git a/packages/@react-spectrum/s2/page.css b/packages/@react-spectrum/s2/page.css new file mode 100644 index 00000000000..68f8c77b0dc --- /dev/null +++ b/packages/@react-spectrum/s2/page.css @@ -0,0 +1,44 @@ +:where(:root, :host) { + --s2-color-scheme: light dark; + color-scheme: var(--s2-color-scheme); + --s2-container-bg: light-dark(rgb(255, 255, 255), rgb(17, 17, 17)); + background: var(--s2-container-bg); + --s2-scale: 1; + --s2-font-size-base: 14; + + /* For backward compatibility in two cases: + * 1. When a component compiled with an earlier version of S2 is embedded in a newer provider. + * 2. When S2 CSS is compiled with lightningcss, setting color-scheme via a variable does not work. */ + --lightningcss-light: initial; + --lightningcss-dark: ; + + @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; + } + + &[data-color-scheme='light'] { + --s2-color-scheme: light; + --lightningcss-light: initial; + --lightningcss-dark: ; + } + + &[data-color-scheme='dark'] { + --s2-color-scheme: dark; + --lightningcss-light: ; + --lightningcss-dark: initial; + } + + &[data-background='layer-1'] { + --s2-container-bg: light-dark(rgb(248, 248, 248), rgb(27, 27, 27)); + } + + &[data-background='layer-2'] { + --s2-container-bg: light-dark(rgb(255, 255, 255), rgb(34, 34, 34)); + } +} diff --git a/packages/@react-spectrum/s2/scripts/buildPageCss.mjs b/packages/@react-spectrum/s2/scripts/buildPageCss.mjs new file mode 100644 index 00000000000..b0054b2ba3e --- /dev/null +++ b/packages/@react-spectrum/s2/scripts/buildPageCss.mjs @@ -0,0 +1,59 @@ +import fs from 'fs'; +import tokens from '@adobe/spectrum-tokens/dist/json/variables.json' with {type: 'json'}; + +function colorToken(token) { + return `light-dark(${token.sets.light.value}, ${token.sets.dark.value})`; +} + +function weirdColorToken(token) { + return `light-dark(${token.sets.light.sets.light.value}, ${token.sets.dark.sets.dark.value})`; +} + +fs.writeFileSync( + import.meta.dirname + '/../page.css', + `:where(:root, :host) { + --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; + --s2-font-size-base: 14; + + /* For backward compatibility in two cases: + * 1. When a component compiled with an earlier version of S2 is embedded in a newer provider. + * 2. When S2 CSS is compiled with lightningcss, setting color-scheme via a variable does not work. */ + --lightningcss-light: initial; + --lightningcss-dark: ; + + @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; + } + + &[data-color-scheme=light] { + --s2-color-scheme: light; + --lightningcss-light: initial; + --lightningcss-dark: ; + } + + &[data-color-scheme=dark] { + --s2-color-scheme: dark; + --lightningcss-light: ; + --lightningcss-dark: initial; + } + + &[data-background=layer-1] { + --s2-container-bg: ${colorToken(tokens['background-layer-1-color'])}; + } + + &[data-background=layer-2] { + --s2-container-bg: ${weirdColorToken(tokens['background-layer-2-color'])}; + } +} +` +); diff --git a/packages/@react-spectrum/s2/src/page.macro.ts b/packages/@react-spectrum/s2/src/page.macro.ts index 2739ad4f0d9..8691e973d56 100644 --- a/packages/@react-spectrum/s2/src/page.macro.ts +++ b/packages/@react-spectrum/s2/src/page.macro.ts @@ -11,68 +11,6 @@ */ import {MacroContext} from '@parcel/macros'; -import tokens from '@adobe/spectrum-tokens/dist/json/variables.json'; - -function colorToken(token: (typeof tokens)['gray-25']) { - return `light-dark(${token.sets.light.value}, ${token.sets.dark.value})`; -} - -function weirdColorToken(token: (typeof tokens)['background-layer-2-color']) { - return `light-dark(${token.sets.light.sets.light.value}, ${token.sets.dark.sets.dark.value})`; -} - -export function generatePageStyles(this: MacroContext | void): void { - if (this && typeof this.addAsset === 'function') { - this.addAsset({ - type: 'css', - content: `:where(:root, :host) { - --s2-color-scheme: light; - color-scheme: var(--s2-color-scheme); - --s2-container-bg: ${colorToken(tokens['background-base-color'])}; - background: var(--s2-container-bg); - --s2-scale: 1; - --s2-font-size-base: 14; - - /* For backward compatibility in two cases: - * 1. When a component compiled with an earlier version of S2 is embedded in a newer provider. - * 2. When S2 CSS is compiled with lightningcss, setting color-scheme via a variable does not work. */ - --lightningcss-light: initial; - --lightningcss-dark: ; - - @media (prefers-color-scheme: dark) { - --s2-color-scheme: dark; - --lightningcss-light: ; - --lightningcss-dark: initial; - } - - @media not ((hover: hover) and (pointer: fine)) { - --s2-scale: 1.25; - --s2-font-size-base: 17; - } - - &[data-color-scheme=light] { - --s2-color-scheme: light; - --lightningcss-light: initial; - --lightningcss-dark: ; - } - - &[data-color-scheme=dark] { - --s2-color-scheme: dark; - --lightningcss-light: ; - --lightningcss-dark: initial; - } - - &[data-background=layer-1] { - --s2-container-bg: ${colorToken(tokens['background-layer-1-color'])}; - } - - &[data-background=layer-2] { - --s2-container-bg: ${weirdColorToken(tokens['background-layer-2-color'])}; - } - }` - }); - } -} // This generates a low specificity rule to define a default value for // --s2-color-scheme. This is used when rendering diff --git a/packages/@react-spectrum/s2/src/page.ts b/packages/@react-spectrum/s2/src/page.ts deleted file mode 100644 index 7e8cf9dd8e2..00000000000 --- a/packages/@react-spectrum/s2/src/page.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2024 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -import {generatePageStyles} from './page.macro' with {type: 'macro'}; - -generatePageStyles(); diff --git a/packages/@react-spectrum/s2/stories/TableView.stories.tsx b/packages/@react-spectrum/s2/stories/TableView.stories.tsx index 3f4e1da7a65..faae58266f9 100644 --- a/packages/@react-spectrum/s2/stories/TableView.stories.tsx +++ b/packages/@react-spectrum/s2/stories/TableView.stories.tsx @@ -60,8 +60,7 @@ const events = ['onResizeStart', 'onResize', 'onResizeEnd', 'onSelectionChange', const meta: Meta = { component: TableView, parameters: { - layout: 'centered', - controls: {exclude: ['onResize']} + layout: 'centered' }, tags: ['autodocs'], args: {...getActionArgs(events)}, diff --git a/packages/@react-spectrum/s2/test/utils/render.tsx b/packages/@react-spectrum/s2/test/utils/render.tsx index c2221fdc284..6c618e3b230 100644 --- a/packages/@react-spectrum/s2/test/utils/render.tsx +++ b/packages/@react-spectrum/s2/test/utils/render.tsx @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import '../../src/page'; +import '../../page.css'; import {Provider, type ProviderProps} from '../../src/Provider'; import React, {ReactElement} from 'react'; import {render as vitestBrowserRender} from 'vitest-browser-react'; diff --git a/packages/dev/parcel-transformer-intl/IntlTransformer.js b/packages/dev/parcel-transformer-intl/IntlTransformer.js index 8dfb066b37e..7c4b91e83ad 100644 --- a/packages/dev/parcel-transformer-intl/IntlTransformer.js +++ b/packages/dev/parcel-transformer-intl/IntlTransformer.js @@ -17,7 +17,8 @@ module.exports = new Transformer({ async transform({asset}) { let code = await asset.getCode(); let json = JSON.parse(code); - let res = compileStrings(json); + let format = !asset.target || asset.target.outputFormat === 'commonjs' ? 'cjs' : 'esm'; + let res = compileStrings(json, {format}); asset.type = 'js'; asset.setCode(res); return [asset]; diff --git a/packages/dev/s2-docs/.parcelrc-s2-docs-v3 b/packages/dev/s2-docs/.parcelrc-s2-docs-v3 new file mode 100644 index 00000000000..275e3ca3cf4 --- /dev/null +++ b/packages/dev/s2-docs/.parcelrc-s2-docs-v3 @@ -0,0 +1,28 @@ +{ + "extends": ["../../../parcel-v3/.parcelrc-icons", "@parcel/config-default"], + "resolvers": ["@parcel/resolver-glob", "..."], + "transformers": { + "docs:*": ["@parcel/transformer-ts-doc"], + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "url:*.svg": ["@parcel/transformer-svg", "@parcel/transformer-raw"], + "data-url:*.svg": ["@parcel/transformer-svg", "@parcel/transformer-inline"], + "starters/tailwind/**/*.css": ["@parcel/transformer-tailwind", "..."], + "packages/dev/s2-docs/tailwind/*.css": ["@parcel/transformer-tailwind", "..."], + "*.mdx": ["./MDXTransformer.mjs", "..."] + }, + "bundler": { + "plugin": "@parcel/bundler-default", + "config": { + "manualSharedBundles": [ + { + "assets": [ + "packages/@react-spectrum/s2/**", + "packages/dev/s2-docs/src/**", + "packages/dev/s2-docs/pages/s2/**/*.{mdx,tsx,ts,js}" + ], + "types": ["css"] + } + ] + } + } +} diff --git a/packages/dev/s2-docs/package.json b/packages/dev/s2-docs/package.json index 608ce5bd1df..86f684fce37 100644 --- a/packages/dev/s2-docs/package.json +++ b/packages/dev/s2-docs/package.json @@ -6,9 +6,13 @@ "start": "DOCS_ENV=dev parcel 'pages/**/*.mdx' --config .parcelrc-s2-docs", "start:s2": "DOCS_ENV=dev LIBRARY=s2 parcel 'pages/s2/**/*.mdx' -p 4321 --config .parcelrc-s2-docs --dist-dir dist/s2 --cache-dir ../../../.parcel-cache/s2", "start:react-aria": "DOCS_ENV=dev LIBRARY=react-aria parcel 'pages/react-aria/**/*.mdx' -p 1234 --config .parcelrc-s2-docs --dist-dir dist/react-aria --cache-dir ../../../.parcel-cache/react-aria", + "start:parcel3": "PARCEL_V3=1 DOCS_ENV=dev parcel3 run server.tsx --config .parcelrc-s2-docs-v3", "build": "yarn build:s2 --public-url $PUBLIC_URL && yarn build:react-aria --public-url $PUBLIC_URL && node scripts/generateAgentSkills.mjs", "build:s2": "LIBRARY=s2 parcel build 'pages/s2/**/*.mdx' --config .parcelrc-s2-docs --dist-dir dist/s2 --cache-dir ../../../.parcel-cache/s2", "build:react-aria": "LIBRARY=react-aria parcel build 'pages/react-aria/**/*.mdx' --config .parcelrc-s2-docs --dist-dir dist/react-aria --cache-dir ../../../.parcel-cache/react-aria", + "build:parcel3": "yarn build:s2-parcel3 && yarn build:react-aria-parcel3", + "build:s2-parcel3": "LIBRARY=s2 parcel3 build ssg-s2.tsx --config .parcelrc-s2-docs-v3 --dist-dir dist/s2 --public-url $PUBLIC_URL && node dist/s2/server/ssg-s2.js && rm -rf dist/s2/server", + "build:react-aria-parcel3": "LIBRARY=react-aria parcel3 build ssg-react-aria.tsx --config .parcelrc-s2-docs-v3 --dist-dir dist/react-aria --public-url $PUBLIC_URL && node dist/react-aria/server/ssg-react-aria.js && rm -rf dist/react-aria/server", "generate:og": "node scripts/generateOGImages.mjs", "generate:md": "node scripts/generateMarkdownDocs.mjs", "generate:mcpb": "node scripts/generateMcpb.mjs" @@ -16,7 +20,7 @@ "dependencies": { "@babel/parser": "^7.26.3", "@parcel/plugin": "^2.16.3", - "@parcel/rsc": "^2.16.3", + "@parcel/rsc": "next", "@react-aria/focus": "^3.20.4", "@react-aria/i18n": "^3.12.10", "@react-aria/interactions": "^3.25.1", @@ -38,10 +42,12 @@ "react-aria": "^3.40.0", "react-aria-components": "^1.7.1", "react-dom": "^19.2.0", + "react-server-dom-parcel": "^19.2.8", "react-stately": "^3.38.0", "remark-mdx": "^3.1.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", + "rsc-html-stream": "^0.0.8", "satori": "^0.16.1", "sharp": "^0.33.5", "textarea-caret": "^3.1.0", diff --git a/packages/dev/s2-docs/pages/react-aria/Button.mdx b/packages/dev/s2-docs/pages/react-aria/Button.mdx index 33478c4307c..d57633e4946 100644 --- a/packages/dev/s2-docs/pages/react-aria/Button.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Button.mdx @@ -36,13 +36,6 @@ export const description = 'Allows a user to perform an action, with mouse, touc initialProps={{children: 'Press me'}} type="tailwind" files={["starters/tailwind/src/Button.tsx"]} /> - {/* */} ## Events diff --git a/packages/dev/s2-docs/pages/react-aria/home/home.css b/packages/dev/s2-docs/pages/react-aria/home/home.css index 5391f7a80d5..0d137dda66e 100644 --- a/packages/dev/s2-docs/pages/react-aria/home/home.css +++ b/packages/dev/s2-docs/pages/react-aria/home/home.css @@ -291,12 +291,12 @@ } .home .iphone-frame { - background-image: url(../../../assets/iphone-frame.webp); + background-image: url(/packages/dev/s2-docs/assets/iphone-frame.webp); background-size: contain; } .home .iphone-mask { - mask-image: url(../../../assets/iphone-mask.webp), linear-gradient(#fff 0 0); + mask-image: url(/packages/dev/s2-docs/assets/iphone-mask.webp), linear-gradient(#fff 0 0); mask-size: contain; mask-composite: exclude; } diff --git a/packages/dev/s2-docs/pages/s2/ColorWheel.mdx b/packages/dev/s2-docs/pages/s2/ColorWheel.mdx index e28b35b3537..64d55ea79ac 100644 --- a/packages/dev/s2-docs/pages/s2/ColorWheel.mdx +++ b/packages/dev/s2-docs/pages/s2/ColorWheel.mdx @@ -53,41 +53,6 @@ function Example() { } ``` -{/* ## Integration - -ColorWheel works well alongside other color components to create complete color picking experiences. - -```tsx render hideImports -"use client"; -import {ColorWheel, ColorSlider, ColorSwatch} from '@react-spectrum/s2'; -import {useState} from 'react'; - -function Example() { - let [color, setColor] = useState('hsl(30, 80%, 60%)'); - - return ( -
- -
- - - -
-
- ); -} -``` */} - ## API diff --git a/packages/dev/s2-docs/server.tsx b/packages/dev/s2-docs/server.tsx new file mode 100644 index 00000000000..6310e82f36f --- /dev/null +++ b/packages/dev/s2-docs/server.tsx @@ -0,0 +1,60 @@ +import {createReadStream, promises} from 'fs'; +import {createServer} from 'http'; +import path from 'path'; +import {renderRequest} from '@parcel/rsc/node'; +// @ts-ignore +import routes from './pages/**/*.mdx?async=true&flat=true'; + +const MIME_TYPES = { + default: 'application/octet-stream', + html: 'text/html; charset=UTF-8', + js: 'text/javascript', + css: 'text/css', + png: 'image/png', + jpg: 'image/jpeg', + gif: 'image/gif', + ico: 'image/x-icon', + svg: 'image/svg+xml' +}; + +createServer(async (req, res) => { + let url = req.url!; + if (url.endsWith('/')) { + url += 'index'; + } + + let page = routes['./pages' + url + '.mdx']; + if (page) { + let mod = await page(); + let {default: Page, ...exports} = mod; + renderRequest( + req, + res, + , + {component: Page} + ); + } else { + let u = new URL(url, 'http://localhost'); + let p = path.join(__dirname, '../../../dist', u.pathname.slice(1)); + let exists = await promises.access(p).then( + () => true, + () => false + ); + if (exists) { + let mimeType = MIME_TYPES[path.extname(p).slice(1)] || MIME_TYPES.default; + res.writeHead(200, {'Content-Type': mimeType}); + createReadStream(p).pipe(res); + } else { + res.statusCode = 404; + res.end(); + } + } +}).listen(1234); +console.log('Server listening on port 1234'); diff --git a/packages/dev/s2-docs/src/CodeBlock.tsx b/packages/dev/s2-docs/src/CodeBlock.tsx index b5c25dd75e4..8e17dcece40 100644 --- a/packages/dev/s2-docs/src/CodeBlock.tsx +++ b/packages/dev/s2-docs/src/CodeBlock.tsx @@ -14,6 +14,8 @@ import path from 'path'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; import {VisualExample, VisualExampleProps} from './VisualExample'; +const baseDir = process.env.PARCEL_V3 ? __dirname + '/../../../../' : '../../../'; + const example = style({ backgroundColor: 'layer-1', borderRadius: 'xl', @@ -106,7 +108,8 @@ export function CodeBlock({ } let resolveFrom = path.resolve( - 'pages', + baseDir, + 'packages/dev/s2-docs/pages', dir || (props.type === 's2' ? 's2' : 'react-aria'), 'index.tsx' ); @@ -275,9 +278,7 @@ export function File({ maxLines?: number; type?: 'vanilla' | 'tailwind' | 's2'; }) { - let contents = readFile( - path.isAbsolute(filename) ? filename : path.resolve('../../../', filename) - ).replace(STARTER_ALIAS_RE, './'); + let contents = readFile(path.resolve(baseDir, filename)).replace(STARTER_ALIAS_RE, './'); return ( @@ -306,7 +307,7 @@ export function getFiles(files: string[], type: string | undefined, npmDeps = {} if (type === 'tailwind' && !fileContents['index.css']) { fileContents['index.css'] = readFileReplace( - path.resolve('../../../starters/tailwind/src/index.css') + path.resolve(baseDir, 'starters/tailwind/src/index.css') ); } @@ -314,7 +315,7 @@ export function getFiles(files: string[], type: string | undefined, npmDeps = {} } function findAllFiles(files: string[], npmDeps = {}) { - files = files.map(file => (path.isAbsolute(file) ? file : path.resolve('../../../', file))); + files = files.map(file => (path.isAbsolute(file) ? file : path.resolve(baseDir, file))); let queue: string[] = [...files]; let allFiles = new Set(); @@ -358,7 +359,7 @@ function parseFile(file: string, contents: string, npmDeps = {}, urls = {}) { let resolved = specifier.startsWith('.') ? path.resolve(path.dirname(file), specifier) - : path.resolve('../../../' + specifier); + : path.resolve(baseDir, specifier); if (path.extname(resolved) === '') { if (fs.existsSync(resolved + '.tsx')) { resolved += '.tsx'; diff --git a/packages/dev/s2-docs/src/getPages.ts b/packages/dev/s2-docs/src/getPages.ts index c924ad2ad08..ea8a837fd9d 100644 --- a/packages/dev/s2-docs/src/getPages.ts +++ b/packages/dev/s2-docs/src/getPages.ts @@ -2,18 +2,22 @@ import {cache} from 'react'; import {getBaseUrl} from './pageUtils'; import {glob, readFile} from 'fs/promises'; import type {Page} from '@parcel/rsc'; +import path from 'path'; // eslint-disable-next-line import {transformAsync} from '@parcel/rust/lib/index.js'; +const cwd = process.env.PARCEL_V3 ? __dirname : './src'; + export const getPages = cache(async () => { let pages: string[] = []; - for await (let page of glob('pages/*/**/*.mdx')) { + for await (let page of glob('../pages/*/**/*.mdx', {cwd})) { pages.push(page); } return Promise.all( pages.map(async page => { - let code = await readFile(page); + let file = path.join(cwd, page); + let code = await readFile(file); let res: any = await transformAsync({ filename: page, code, @@ -40,7 +44,7 @@ export const getPages = cache(async () => { inline_constants: false }); - let name = page.slice(6, -4); + let name = page.slice('../pages/'.length, -4); return { name, url: getUrl(name), diff --git a/packages/dev/s2-docs/src/pageUtils.ts b/packages/dev/s2-docs/src/pageUtils.ts index d46a4a54d2f..2b946ae7b8c 100644 --- a/packages/dev/s2-docs/src/pageUtils.ts +++ b/packages/dev/s2-docs/src/pageUtils.ts @@ -27,10 +27,12 @@ export function getBaseUrl(library: 'react-aria' | 's2') { export function getRSCUrl(pathname: string) { let url = new URL(pathname, location.href); url.pathname = url.pathname.replace('.html', ''); - if (url.pathname.endsWith('/')) { - url.pathname += 'index.rsc'; - } else { - url.pathname += '.rsc'; + if (!process.env.PARCEL_V3 || process.env.NODE_ENV === 'production') { + if (url.pathname.endsWith('/')) { + url.pathname += 'index.rsc'; + } else { + url.pathname += '.rsc'; + } } url.search = ''; diff --git a/packages/dev/s2-docs/src/prefetch.ts b/packages/dev/s2-docs/src/prefetch.ts index 09ffcae1631..6a969d0d6ce 100644 --- a/packages/dev/s2-docs/src/prefetch.ts +++ b/packages/dev/s2-docs/src/prefetch.ts @@ -8,6 +8,10 @@ import {type ReactElement} from 'react'; const prefetchPromises = new Map>(); export function prefetchRoute(pathname: string, priority: RequestPriority = 'low') { + if (process.env.NODE_ENV !== 'production') { + return; + } + let url = getRSCUrl(pathname); // Skip if already prefetched diff --git a/packages/dev/s2-docs/ssg-react-aria.tsx b/packages/dev/s2-docs/ssg-react-aria.tsx new file mode 100644 index 00000000000..31785ae41e9 --- /dev/null +++ b/packages/dev/s2-docs/ssg-react-aria.tsx @@ -0,0 +1,5 @@ +import {render} from './ssg'; +// @ts-ignore +import routes from './pages/react-aria/**/*.mdx?async=true&flat=true'; + +render(routes); diff --git a/packages/dev/s2-docs/ssg-s2.tsx b/packages/dev/s2-docs/ssg-s2.tsx new file mode 100644 index 00000000000..59d3191fbb2 --- /dev/null +++ b/packages/dev/s2-docs/ssg-s2.tsx @@ -0,0 +1,5 @@ +import {render} from './ssg'; +// @ts-ignore +import routes from './pages/s2/**/*.mdx?async=true&flat=true'; + +render(routes); diff --git a/packages/dev/s2-docs/ssg.tsx b/packages/dev/s2-docs/ssg.tsx new file mode 100644 index 00000000000..ba99a5e4469 --- /dev/null +++ b/packages/dev/s2-docs/ssg.tsx @@ -0,0 +1,126 @@ +import {availableParallelism} from 'node:os'; +import {ComponentType, ReactNode} from 'react' with {env: 'react-client'}; +import {createFromReadableStream} from 'react-server-dom-parcel/client.edge' with { + env: 'react-client' +}; +import {createWriteStream, mkdirSync} from 'fs'; +import {dirname} from 'path'; +import {finished} from 'node:stream/promises'; +import {injectRSCPayload} from 'rsc-html-stream/server'; +import {isMainThread, Worker, workerData} from 'node:worker_threads'; +import {prerender} from 'react-dom/static.edge' with {env: 'react-client'}; +import {Readable} from 'stream'; +import {renderToReadableStream} from 'react-server-dom-parcel/server.edge'; + +let base = './pages/'; +let baseDir = base; +if (process.env.LIBRARY) { + base += process.env.LIBRARY + '/'; +} + +export async function render(routes) { + let pages = Object.keys(routes); + if (isMainThread) { + let workerCount = Math.min(availableParallelism(), pages.length); + if (workerCount > 1) { + let routesPerWorker = Math.floor(pages.length / workerCount); + let remainder = pages.length % workerCount; + let offset = 0; + let workers: Promise[] = []; + + for (let i = 0; i < workerCount; i++) { + let routeCount = routesPerWorker + (i < remainder ? 1 : 0); + let workerRoutes = pages.slice(offset, offset + routeCount); + offset += routeCount; + + workers.push( + new Promise((resolve, reject) => { + let worker = new Worker(process.argv[1], {workerData: workerRoutes}); + worker.once('error', reject); + worker.once('exit', code => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Rendering worker exited with code ${code}`)); + } + }); + }) + ); + } + + await Promise.all(workers); + return; + } + } else { + pages = workerData; + } + + for (let page of pages) { + console.log('rendering ' + page); + try { + let mod = await routes[page](); + let Page = mod.default; + let url = page.slice(base.length, -4); + let {html, rsc} = await prerenderHTML( + , + {component: Page} + ); + + let distPath = page.slice(baseDir.length, -4); + mkdirSync('dist/' + dirname(distPath), {recursive: true}); + + let htmlStream = createWriteStream('dist/' + distPath + '.html'); + html.pipe(htmlStream); + + let rscStream = createWriteStream('dist/' + distPath + '.rsc'); + rsc.pipe(rscStream); + + await Promise.all([finished(htmlStream), finished(rscStream)]); + } catch (err) { + console.error(err); + } + } +} + +interface RSCToHTMLOptions { + component?: ComponentType; + identifierPrefix?: string; + namespaceURI?: string; + nonce?: string; + progressiveChunkSize?: number; + signal?: AbortSignal; + temporaryReferences?: any; + onError?: (error: unknown, errorInfo?: any) => string | void; +} + +async function prerenderHTML( + root: any, + options?: RSCToHTMLOptions +): Promise<{html: Readable; rsc: Readable}> { + let stream = renderToReadableStream(root, options); + + // Use client react to render the RSC payload to HTML. + let [s1, renderStream] = stream.tee(); + let [injectStream, rscStream] = s1.tee(); + let data: Promise; + function Content() { + data ??= createFromReadableStream(renderStream); + return data; + } + + let {prelude} = await prerender(, { + ...options, + bootstrapScriptContent: (options?.component as any)?.bootstrapScript + }); + + return { + html: Readable.fromWeb(prelude.pipeThrough(injectRSCPayload(injectStream)) as any), + rsc: Readable.fromWeb(rscStream) + }; +} diff --git a/packages/dev/storybook-builder-parcel/gen-preview-modern.mjs b/packages/dev/storybook-builder-parcel/gen-preview-modern.mjs index c885fa3ede9..6aa338d0372 100644 --- a/packages/dev/storybook-builder-parcel/gen-preview-modern.mjs +++ b/packages/dev/storybook-builder-parcel/gen-preview-modern.mjs @@ -3,6 +3,8 @@ import {Buffer} from 'node:buffer'; import {createRequire} from 'node:module'; import {loadPreviewOrConfigFile, normalizeStories} from 'storybook/internal/common'; +const PARCEL_V3 = Boolean(process.env.PARCEL_V3); + // @parcel/utils is CJS-only; use createRequire to load it from ESM const _require = createRequire(import.meta.url); const {relativePath} = _require('@parcel/utils'); @@ -173,17 +175,44 @@ function processPreviewAnnotation(annotationPath) { * @param stories An array of absolute story paths. */ async function toImportFn(stories, generatedEntries) { - const entries = stories.map(glob => { - return `...import(${JSON.stringify('story:' + btoa(relativePath(generatedEntries, glob)))})`; - }); + let imports = []; + let entries = []; + for (let glob of stories) { + if (PARCEL_V3) { + let name = `entry_${imports.length}`; + imports.push( + `import ${name} from ${JSON.stringify(relativePath(generatedEntries, glob) + '?async=true&flat=true')};` + ); + entries.push(`...${name}`); + } else { + entries.push( + `...import(${JSON.stringify('story:' + btoa(relativePath(generatedEntries, glob)))})` + ); + } + } + + let relative = relativePath(process.cwd(), generatedEntries); return ` + ${imports.join('\n')} + + function relative(from, to) { + let fromParts = from.split('/').filter(Boolean); + let toParts = to.split('/').filter(Boolean); + let i = 0; + while (i < fromParts.length && fromParts[i] === toParts[i]) { + i++; + } + return '../'.repeat(fromParts.length - i) + toParts.slice(i).join('/'); + } + const importers = { ${entries.join(',\n')} }; - async function importFn(path) { - return importers[path](); + async function importFn(p) { + p = ${PARCEL_V3 ? `relative(${JSON.stringify(relative)}, p)` : 'p'}; + return importers[p](); } `; } diff --git a/packages/dev/storybook-builder-parcel/preset.mjs b/packages/dev/storybook-builder-parcel/preset.mjs index d12296d4b1a..5fb9095afa0 100644 --- a/packages/dev/storybook-builder-parcel/preset.mjs +++ b/packages/dev/storybook-builder-parcel/preset.mjs @@ -14,10 +14,13 @@ import path from 'node:path'; import {fileURLToPath} from 'node:url'; import {createProxyMiddleware} from 'http-proxy-middleware'; import fs from 'node:fs'; +import {spawn} from 'node:child_process'; import {generateIframeModern} from './gen-iframe-modern.mjs'; import {generatePreviewModern, generateSetupAddons} from './gen-preview-modern.mjs'; +const PARCEL_V3 = Boolean(process.env.PARCEL_V3); + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const generatedEntries = path.join(__dirname, 'generated-entries'); @@ -66,15 +69,19 @@ export async function start({options, router}) { req.connection = connection; }); - watcherSubscription = await parcel.watch(); - process.on('SIGINT', async () => { - await watcherSubscription?.unsubscribe(); - process.exit(); - }); + if (!PARCEL_V3) { + watcherSubscription = await parcel.watch(); + process.on('SIGINT', async () => { + await watcherSubscription?.unsubscribe(); + process.exit(); + }); + } return { async bail() { - await watcherSubscription?.unsubscribe(); + if (!PARCEL_V3) { + await watcherSubscription?.unsubscribe(); + } }, stats: {}, totalTime: 0 @@ -83,7 +90,9 @@ export async function start({options, router}) { export async function build({options}) { const parcel = await createParcel(options); - await parcel.run(); + if (!PARCEL_V3) { + await parcel.run(); + } } // No core presets to register; previewAnnotations come transitively from @@ -117,36 +126,64 @@ async function createParcel(options, isDev = false) { await generatePreviewModern(options, generatedEntries) ); - return new Parcel({ - entries: path.join(generatedEntries, 'iframe.html'), - config: path.resolve(options.configDir, '.parcelrc'), - mode: isDev ? 'development' : 'production', - serveOptions: isDev ? {port: 3000} : null, - hmrOptions: isDev ? {port: 3001} : null, - additionalReporters: [ - {packageName: '@parcel/reporter-cli', resolveFrom: __filename}, - ...(options.statsJson - ? [ - { - packageName: 'parcel-reporter-turbosnap-stats', - resolveFrom: __filename - } - ] - : []) - ], - targets: { - storybook: { - distDir: options.outputDir, - publicUrl: './', - engines: { - browsers: [ - 'last 2 Chrome version', - 'last 2 Safari versions', - 'last 2 Edge version', - 'last 2 Firefox versions' - ] + if (PARCEL_V3) { + let parcel = spawn( + 'parcel3', + [ + isDev ? 'serve' : 'build', + '--config', + path.resolve(options.configDir, '.parcelrc-v3'), + '-p', + '3000', + '--dist-dir', + options.outputDir, + path.join(generatedEntries, 'iframe.html') + ], + {stdio: 'inherit'} + ); + if (!isDev) { + await new Promise((resolve, reject) => { + parcel.on('close', code => { + if (code === 0) { + resolve(); + } else { + reject(); + } + }); + }); + } + } else { + return new Parcel({ + entries: path.join(generatedEntries, 'iframe.html'), + config: path.resolve(options.configDir, '.parcelrc'), + mode: isDev ? 'development' : 'production', + serveOptions: isDev ? {port: 3000} : null, + hmrOptions: isDev ? {port: 3001} : null, + additionalReporters: [ + {packageName: '@parcel/reporter-cli', resolveFrom: __filename}, + ...(options.statsJson + ? [ + { + packageName: 'parcel-reporter-turbosnap-stats', + resolveFrom: __filename + } + ] + : []) + ], + targets: { + storybook: { + distDir: options.outputDir, + publicUrl: './', + engines: { + browsers: [ + 'last 2 Chrome version', + 'last 2 Safari versions', + 'last 2 Edge version', + 'last 2 Firefox versions' + ] + } } } - } - }); + }); + } } diff --git a/packages/react-aria-components/package.json b/packages/react-aria-components/package.json index 34aff3e05c0..8a2bc5a444a 100644 --- a/packages/react-aria-components/package.json +++ b/packages/react-aria-components/package.json @@ -45,6 +45,7 @@ "./*": { "source": "./exports/*.ts", "types": "./dist/types/exports/*.d.ts", + "legacy-module": "./dist/exports/*.js", "import": "./dist/exports/*.mjs", "require": "./dist/exports/*.cjs" }, diff --git a/packages/react-aria/package.json b/packages/react-aria/package.json index 5e83ad3ab21..4e2af853be2 100644 --- a/packages/react-aria/package.json +++ b/packages/react-aria/package.json @@ -26,6 +26,7 @@ "./*": { "source": "./exports/*.ts", "types": "./dist/types/exports/*.d.ts", + "legacy-module": "./dist/exports/*.js", "import": "./dist/exports/*.mjs", "require": "./dist/exports/*.cjs" } diff --git a/packages/react-stately/package.json b/packages/react-stately/package.json index 1b6f9488022..303c36a86dd 100644 --- a/packages/react-stately/package.json +++ b/packages/react-stately/package.json @@ -16,6 +16,7 @@ "./*": { "source": "./exports/*.ts", "types": "./dist/types/exports/*.d.ts", + "legacy-module": "./dist/exports/*.js", "import": "./dist/exports/*.mjs", "require": "./dist/exports/*.cjs" } diff --git a/parcel-v3/.parcelrc-build b/parcel-v3/.parcelrc-build new file mode 100644 index 00000000000..99d17a9a322 --- /dev/null +++ b/parcel-v3/.parcelrc-build @@ -0,0 +1,22 @@ +{ + "extends": [".parcelrc-icons", "@parcel/config-default"], + "resolvers": ["@parcel/resolver-glob", "..."], + "bundler": "@parcel/bundler-library", + "transformers": { + "packages/**/intl/**/*.json": ["parcel-transformer-intl"], + "bundle-text:*.svg": ["@parcel/transformer-svg", "@parcel/transformer-inline"], + "packages/@adobe/**/*.css": [ + "./PostCSSTransformer.mjs", + { + "plugin": "@parcel/transformer-css", + "config": { + "cssModules": true, + "pseudoClasses": { + "focusVisible": "focus-ring", + "hover": "is-hovered" + } + } + } + ] + } +} diff --git a/parcel-v3/.parcelrc-icons b/parcel-v3/.parcelrc-icons new file mode 100644 index 00000000000..a79baacff1f --- /dev/null +++ b/parcel-v3/.parcelrc-icons @@ -0,0 +1,44 @@ +{ + "transformers": { + "illustration:*.svg": [ + { + "plugin": "@parcel/transformer-svg-jsx", + "config": { + "ref": true, + "svgoConfig": { + "default": true, + "prefixIds": true + }, + "replaceAttrValues": { + "var(--iconPrimary,#222)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--iconPrimary,#222222)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--iconPrimary,#292929)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--spectrum-global-color-gray-800,#292929)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))" + }, + "template": "'use client';\nimport {createIllustration} from '@react-spectrum/s2/Icon';\nexport default /*#__PURE__*/ createIllustration($ICON);" + } + } + ], + "packages/@react-spectrum/s2/s2wf-icons/**/*.svg": [ + { + "plugin": "@parcel/transformer-svg-jsx", + "config": { + "ref": true, + "svgoConfig": { + "default": true, + "prefixIds": true + }, + "replaceAttrValues": { + "var(--iconPrimary,#222)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--iconPrimary,#222222)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--iconPrimary,#292929)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))", + "var(--spectrum-global-color-gray-800,#292929)": "var(--iconPrimary, light-dark(rgb(41, 41, 41), rgb(219, 219, 219)))" + }, + "template": "'use client';\nimport {createIcon} from '@react-spectrum/s2/Icon';\nexport default /*#__PURE__*/ createIcon($ICON);" + } + } + ], + "*.svg": ["@parcel/transformer-svg-jsx"] + }, + "bundler": "@parcel/bundler-default" +} diff --git a/parcel-v3/PostCSSTransformer.mjs b/parcel-v3/PostCSSTransformer.mjs new file mode 100644 index 00000000000..1af16d280ad --- /dev/null +++ b/parcel-v3/PostCSSTransformer.mjs @@ -0,0 +1,16 @@ +import {Transformer} from '@parcel/plugin'; +import postcss from 'postcss'; + +let processors = require('@spectrum-css/component-builder/css/processors').processors; + +export default new Transformer({ + async transform({asset}) { + let code = asset.getCode(); + let res = await postcss(processors).process(code, { + map: false, + from: new URL(asset.url).pathname + }); + asset.setCode(res.css); + asset.type = 'css'; + } +}); diff --git a/scripts/buildEsm.js b/scripts/buildEsm.js index 97337b039f6..b99cc692feb 100644 --- a/scripts/buildEsm.js +++ b/scripts/buildEsm.js @@ -34,32 +34,45 @@ for (let pkg of fs.globSync(['packages/@react-{spectrum,aria,stately}/*/'])) { // Add extra shims for bundlers that don't support package.json exports, specifically webpack 4 and Parcel (without config). for (let pkg of ['@adobe/react-spectrum', 'react-aria', 'react-stately', 'react-aria-components']) { for (let file of fs.globSync(`packages/${pkg}/dist/exports/**/*.js`)) { - if (file.endsWith('index.js')) { - continue; + if (!file.endsWith('index.js')) { + writePackageExportShim(file); } + } +} - // webpack 4 does not support importing non-ESM modules from .mjs files, so rename to .js - // This should be sufficient because Parcel prioritizes .js over .cjs. - // We do not support any tools that only support CommonJS. - let shim = file.replace('/dist/exports/', '/'); - let specifier = path.relative(shim, file); - - let dir = shim.replace('.js', ''); - fs.mkdirSync(dir, {recursive: true}); - fs.writeFileSync( - dir + '/package.json', - JSON.stringify( - { - main: specifier.replace('.js', '.cjs'), - module: specifier, - types: path.relative( - shim, - file.replace('/dist/exports/', '/dist/types/exports/').replace('.js', '.d.ts') - ) - }, - null, - 2 - ) + '\n' - ); +if (process.env.PARCEL_V3) { + // S2 icons use a package self-reference so that Icon is shared across the separate icon and + // component build targets. Only add this shim because other S2 subpaths do not need legacy support. + let s2Icon = 'packages/@react-spectrum/s2/dist/exports/Icon.mjs'; + if (fs.existsSync(s2Icon)) { + writePackageExportShim(s2Icon); } } + +function writePackageExportShim(file) { + let extension = path.extname(file); + + // webpack 4 does not support importing non-ESM modules from .mjs files, so most packages use + // the generated .js variant. S2 is built directly to .mjs and only needs the Icon shim. + let shim = file.replace('/dist/exports/', '/'); + let specifier = path.relative(shim, file); + + let dir = shim.slice(0, -extension.length); + fs.mkdirSync(dir, {recursive: true}); + fs.writeFileSync( + dir + '/package.json', + JSON.stringify( + { + main: specifier.slice(0, -extension.length) + '.cjs', + module: specifier, + types: path.relative( + shim, + file.replace('/dist/exports/', '/dist/types/exports/').slice(0, -extension.length) + + '.d.ts' + ) + }, + null, + 2 + ) + '\n' + ); +} diff --git a/scripts/generateS2IconIndex.js b/scripts/generateS2IconIndex.js index 094867a5594..da676669f0f 100644 --- a/scripts/generateS2IconIndex.js +++ b/scripts/generateS2IconIndex.js @@ -68,7 +68,9 @@ function generate(dir) { } } - let relative = path.relative(dir, 'packages/@react-spectrum/s2/src/Icon'); + let relative = process.env.PARCEL_V3 + ? '@react-spectrum/s2/Icon' + : path.relative(dir, 'packages/@react-spectrum/s2/src/Icon'); let typeImport = dir.includes('ui-icons') ? "import {ReactNode, SVGProps} from 'react';" : `import {ReactNode} from 'react'; @@ -105,7 +107,7 @@ ${dir.includes('spectrum-illustrations') ? "'use client';" : ''} ); } if (ctx) { - imports.push("import {useContextProps} from 'react-aria-components';"); + imports.push("import {useContextProps} from 'react-aria-components/slots';"); } if (!isIllustration) { imports.push("import {style} from '../style' with {type: 'macro'};"); diff --git a/scripts/testDocs.js b/scripts/testDocs.js index c215c5d11ec..d9981322918 100644 --- a/scripts/testDocs.js +++ b/scripts/testDocs.js @@ -2,7 +2,7 @@ const {chromium, firefox, webkit} = require('playwright'); const {exec} = require('child_process'); const http = require('http'); const path = require('path'); -const glob = require('glob'); +const {glob} = require('fs/promises'); function parseArgs() { const args = process.argv.slice(2); @@ -19,14 +19,14 @@ function parseArgs() { async function startServer() { return new Promise((resolve, reject) => { console.log('Starting documentation server...'); - const child = exec('yarn start:docs', { + const child = exec('yarn start:s2-docs-parcel3', { env: {...process.env, DOCS_ENV: 'dev'} }); child.stdout.on('data', data => { console.log(`Server output: ${data}`); - if (data.includes('Server running at')) { + if (data.includes('Server listening on')) { console.log('Documentation server is running'); - resolve({process: child, baseUrl: data.split(' ')[3].trim()}); + resolve({process: child, baseUrl: 'http://localhost:1234'}); } }); child.stderr.on('data', data => { @@ -63,31 +63,15 @@ function waitForServer(url, timeout = 30000, interval = 1000) { } async function getPageLinks() { - const packagePaths = [ - 'packages/@react-{spectrum,aria,stately}/*/docs/*.mdx', - 'packages/react-aria-components/docs/**/*.mdx', - 'packages/@internationalized/*/docs/*.mdx' - ]; - - const rootPages = 'packages/dev/docs/pages/**/*.mdx'; + const rootPages = 'packages/dev/s2-docs/pages/react-aria/*.mdx'; let links = []; - for (const pattern of packagePaths) { - const files = await glob(pattern); - for (const file of files) { - const parts = file.split(path.sep); - const packageName = parts[1].replace('@', ''); - const componentName = path.basename(file, '.mdx'); - links.push(`/${packageName}/${componentName}.html`); - } - } - const rootFiles = await glob(rootPages); - for (const file of rootFiles) { - const relativePath = path.relative('packages/dev/docs/pages', file); + for await (const file of rootFiles) { + const relativePath = path.relative('packages/dev/s2-docs/pages', file); const urlPath = path.join('/', path.dirname(relativePath), path.basename(relativePath, '.mdx')); - links.push(`${urlPath}.html`); + links.push(urlPath); } return links; @@ -130,6 +114,13 @@ async function testDocs() { msgUrl.startsWith(server.baseUrl) && (msg.type() === 'error' || msg.type() === 'warning') ) { + if ( + /Connection to the HMR server|outdated JSX|PressResponder|Error during WebSocket handshake/.test( + msg.text() + ) + ) { + return; + } console.log(`${msg.type().toUpperCase()} on ${currentPage}: ${msg.text()}`); messages.push({type: msg.type(), path: currentPage, text: msg.text()}); } diff --git a/yarn.lock b/yarn.lock index be52a4d5341..ca28f2b2137 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5625,6 +5625,97 @@ __metadata: languageName: node linkType: hard +"@parcel/parcel3-darwin-arm64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-darwin-arm64@npm:0.0.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/parcel3-darwin-x64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-darwin-x64@npm:0.0.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/parcel3-linux-arm64-musl@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-linux-arm64-musl@npm:0.0.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/parcel3-linux-arm64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-linux-arm64@npm:0.0.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/parcel3-linux-x64-musl@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-linux-x64-musl@npm:0.0.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/parcel3-linux-x64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-linux-x64@npm:0.0.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/parcel3-win32-arm64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-win32-arm64@npm:0.0.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/parcel3-win32-x64@npm:0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3-win32-x64@npm:0.0.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@parcel/parcel3@npm:^0.0.6": + version: 0.0.6 + resolution: "@parcel/parcel3@npm:0.0.6" + dependencies: + "@parcel/parcel3-darwin-arm64": "npm:0.0.6" + "@parcel/parcel3-darwin-x64": "npm:0.0.6" + "@parcel/parcel3-linux-arm64": "npm:0.0.6" + "@parcel/parcel3-linux-arm64-musl": "npm:0.0.6" + "@parcel/parcel3-linux-x64": "npm:0.0.6" + "@parcel/parcel3-linux-x64-musl": "npm:0.0.6" + "@parcel/parcel3-win32-arm64": "npm:0.0.6" + "@parcel/parcel3-win32-x64": "npm:0.0.6" + dependenciesMeta: + "@parcel/parcel3-darwin-arm64": + optional: true + "@parcel/parcel3-darwin-x64": + optional: true + "@parcel/parcel3-linux-arm64": + optional: true + "@parcel/parcel3-linux-arm64-musl": + optional: true + "@parcel/parcel3-linux-x64": + optional: true + "@parcel/parcel3-linux-x64-musl": + optional: true + "@parcel/parcel3-win32-arm64": + optional: true + "@parcel/parcel3-win32-x64": + optional: true + bin: + parcel3: bin/parcel3 + checksum: 10c0/af2b9d4943a4d34e459cb68485c149aabe000066d65c45c2f7958f98358d38ac580afff98affb521617daa1ce0bb75fa16b8b3aa1a7d9200ff30395df038cd24 + languageName: node + linkType: hard + "@parcel/plugin@npm:2.16.3, @parcel/plugin@npm:^2.0.0, @parcel/plugin@npm:^2.16.3": version: 2.16.3 resolution: "@parcel/plugin@npm:2.16.3" @@ -5749,16 +5840,16 @@ __metadata: languageName: unknown linkType: soft -"@parcel/rsc@npm:^2.16.3": - version: 2.16.3 - resolution: "@parcel/rsc@npm:2.16.3" +"@parcel/rsc@npm:next": + version: 3.0.0-alpha.0 + resolution: "@parcel/rsc@npm:3.0.0-alpha.0" dependencies: react-server-dom-parcel: "npm:^19.1.1" rsc-html-stream: "npm:^0.0.6" peerDependencies: react: ^19.1.0 react-dom: ^19.1.0 - checksum: 10c0/84c14f8c3064c990c2b42ee42cb4d1fa18f7f6e1c69e14f1a182c1db610fc790e461fa9d1af360ae01895685cd5f453da773a288a164cb86f762223403e8d492 + checksum: 10c0/2e86fcec8fc01f8483fe80cca17ed67bf9a88b2f8d0cabd020680a91f7f991ecdda649ba304ed563a75e8063ecdeb80d8619dd25d35ee056d76248a427876a74 languageName: node linkType: hard @@ -6185,6 +6276,85 @@ __metadata: languageName: node linkType: hard +"@parcel/transformer-storybook-darwin-arm64@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-darwin-arm64@npm:0.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/transformer-storybook-darwin-x64@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-darwin-x64@npm:0.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/transformer-storybook-linux-arm64-gnu@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-linux-arm64-gnu@npm:0.0.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/transformer-storybook-linux-arm64-musl@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-linux-arm64-musl@npm:0.0.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/transformer-storybook-linux-x64-gnu@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-linux-x64-gnu@npm:0.0.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/transformer-storybook-linux-x64-musl@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-linux-x64-musl@npm:0.0.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/transformer-storybook-v3@npm:^0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-v3@npm:0.0.2" + dependencies: + "@parcel/transformer-storybook-darwin-arm64": "npm:0.0.2" + "@parcel/transformer-storybook-darwin-x64": "npm:0.0.2" + "@parcel/transformer-storybook-linux-arm64-gnu": "npm:0.0.2" + "@parcel/transformer-storybook-linux-arm64-musl": "npm:0.0.2" + "@parcel/transformer-storybook-linux-x64-gnu": "npm:0.0.2" + "@parcel/transformer-storybook-linux-x64-musl": "npm:0.0.2" + "@parcel/transformer-storybook-win32-x64-msvc": "npm:0.0.2" + dependenciesMeta: + "@parcel/transformer-storybook-darwin-arm64": + optional: true + "@parcel/transformer-storybook-darwin-x64": + optional: true + "@parcel/transformer-storybook-linux-arm64-gnu": + optional: true + "@parcel/transformer-storybook-linux-arm64-musl": + optional: true + "@parcel/transformer-storybook-linux-x64-gnu": + optional: true + "@parcel/transformer-storybook-linux-x64-musl": + optional: true + "@parcel/transformer-storybook-win32-x64-msvc": + optional: true + checksum: 10c0/3ef62a1855956e579951c27bc701f3ee6964449d7c4458caa3063778b64a79abd9fd7fda1468cb159453aabe1af0a1c72c76ba55f71c0e1ec097f446fce86819 + languageName: node + linkType: hard + +"@parcel/transformer-storybook-win32-x64-msvc@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-storybook-win32-x64-msvc@npm:0.0.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@parcel/transformer-storybook@npm:^0.0.2, @parcel/transformer-storybook@workspace:packages/dev/parcel-transformer-storybook": version: 0.0.0-use.local resolution: "@parcel/transformer-storybook@workspace:packages/dev/parcel-transformer-storybook" @@ -6223,6 +6393,85 @@ __metadata: languageName: node linkType: hard +"@parcel/transformer-ts-doc-darwin-arm64@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-darwin-arm64@npm:0.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-darwin-x64@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-darwin-x64@npm:0.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-linux-arm64-gnu@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-linux-arm64-gnu@npm:0.0.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-linux-arm64-musl@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-linux-arm64-musl@npm:0.0.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-linux-x64-gnu@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-linux-x64-gnu@npm:0.0.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-linux-x64-musl@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-linux-x64-musl@npm:0.0.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc-win32-x64-msvc@npm:0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc-win32-x64-msvc@npm:0.0.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@parcel/transformer-ts-doc@npm:^0.0.2": + version: 0.0.2 + resolution: "@parcel/transformer-ts-doc@npm:0.0.2" + dependencies: + "@parcel/transformer-ts-doc-darwin-arm64": "npm:0.0.2" + "@parcel/transformer-ts-doc-darwin-x64": "npm:0.0.2" + "@parcel/transformer-ts-doc-linux-arm64-gnu": "npm:0.0.2" + "@parcel/transformer-ts-doc-linux-arm64-musl": "npm:0.0.2" + "@parcel/transformer-ts-doc-linux-x64-gnu": "npm:0.0.2" + "@parcel/transformer-ts-doc-linux-x64-musl": "npm:0.0.2" + "@parcel/transformer-ts-doc-win32-x64-msvc": "npm:0.0.2" + dependenciesMeta: + "@parcel/transformer-ts-doc-darwin-arm64": + optional: true + "@parcel/transformer-ts-doc-darwin-x64": + optional: true + "@parcel/transformer-ts-doc-linux-arm64-gnu": + optional: true + "@parcel/transformer-ts-doc-linux-arm64-musl": + optional: true + "@parcel/transformer-ts-doc-linux-x64-gnu": + optional: true + "@parcel/transformer-ts-doc-linux-x64-musl": + optional: true + "@parcel/transformer-ts-doc-win32-x64-msvc": + optional: true + checksum: 10c0/4e1bfdcdac1b938412c296b1a219e83d6faca72cd71bccd6d3c9221385dd36f7e358920facfbeae22233b27f64a3b824ce96256c21a0ffefbc14c5834615cac1 + languageName: node + linkType: hard + "@parcel/transformer-typescript-types@npm:^2.16.3": version: 2.16.3 resolution: "@parcel/transformer-typescript-types@npm:2.16.3" @@ -8036,7 +8285,7 @@ __metadata: dependencies: "@babel/parser": "npm:^7.26.3" "@parcel/plugin": "npm:^2.16.3" - "@parcel/rsc": "npm:^2.16.3" + "@parcel/rsc": "npm:next" "@react-aria/focus": "npm:^3.20.4" "@react-aria/i18n": "npm:^3.12.10" "@react-aria/interactions": "npm:^3.25.1" @@ -8060,10 +8309,12 @@ __metadata: react-aria: "npm:^3.40.0" react-aria-components: "npm:^1.7.1" react-dom: "npm:^19.2.0" + react-server-dom-parcel: "npm:^19.2.8" react-stately: "npm:^3.38.0" remark-mdx: "npm:^3.1.0" remark-parse: "npm:^11.0.0" remark-stringify: "npm:^11.0.0" + rsc-html-stream: "npm:^0.0.8" satori: "npm:^0.16.1" sharp: "npm:^0.33.5" textarea-caret: "npm:^3.1.0" @@ -26566,6 +26817,7 @@ __metadata: "@parcel/optimizer-terser": "npm:^2.16.3" "@parcel/packager-react-static": "npm:^2.16.3" "@parcel/packager-ts": "npm:^2.16.3" + "@parcel/parcel3": "npm:^0.0.6" "@parcel/reporter-bundle-analyzer": "npm:^2.16.3" "@parcel/reporter-cli": "npm:^2.16.3" "@parcel/resolver-glob": "npm:^2.16.3" @@ -26573,7 +26825,9 @@ __metadata: "@parcel/transformer-inline": "npm:^2.16.3" "@parcel/transformer-inline-string": "npm:^2.16.3" "@parcel/transformer-react-static": "npm:^2.16.3" + "@parcel/transformer-storybook-v3": "npm:^0.0.2" "@parcel/transformer-svg-react": "npm:^2.16.3" + "@parcel/transformer-ts-doc": "npm:^0.0.2" "@parcel/transformer-typescript-types": "npm:^2.16.3" "@react-spectrum/parcel-namer-s2": "workspace:^" "@react-spectrum/s2-icon-builder": "workspace:^" @@ -27716,10 +27970,10 @@ __metadata: languageName: node linkType: hard -"rsc-html-stream@npm:^0.0.6": - version: 0.0.6 - resolution: "rsc-html-stream@npm:0.0.6" - checksum: 10c0/b405a90b2d0e017b0fdbe6209f925efc1690dd1074a4ac1171d7ee20297a31bcde375e275bd4a992f104b2de70744d7605b4f6c76addb4a50e2cad6521524742 +"rsc-html-stream@npm:0.0.8": + version: 0.0.8 + resolution: "rsc-html-stream@npm:0.0.8" + checksum: 10c0/c65aa0b59dfebcc89dcc71510f3c9255be5d994b28e9cb69286453ef898159320f84984afb3d76751cdb636dd41655e33808a4864f825f59bb680e1c62d7ac20 languageName: node linkType: hard