diff --git a/.circleci/config.yml b/.circleci/config.yml index d2decec56c..be2d7750c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,7 +22,7 @@ parameters: # 3. Commit this change to the PR branch where the changes exist. current_golden_images_hash: type: string - default: fe5761e6acafee627d7782f8f8856ca05a69260c + default: 89cb380330cef18d34c0d4cf893790d9c7167b08 wireit_cache_name: type: string default: wireit diff --git a/.nvmrc b/.nvmrc index 297d47ba07..829e9737e4 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.13.1 \ No newline at end of file +20.19.0 \ No newline at end of file diff --git a/1st-gen/package.json b/1st-gen/package.json index feeb3b17cf..48029d8b63 100644 --- a/1st-gen/package.json +++ b/1st-gen/package.json @@ -18,8 +18,7 @@ "scripts": { "analyze": "lit-analyzer \"{packages,tools}/*/src/**/!(*.css).ts\"", "build": "wireit", - "prebuild": "wireit", - "build:clear-cache": "rimraf packages/*/tsconfig.tsbuildinfo && rimraf tools/*/tsconfig.tsbuildinfo", + "build:clear-cache": "rimraf .wireit && rimraf packages/*/tsconfig.tsbuildinfo && rimraf tools/*/tsconfig.tsbuildinfo", "build:confirm": "node ./scripts/confirm-build.js", "build:css": "wireit", "build:css:watch": "wireit", @@ -30,11 +29,10 @@ "build:types": "wireit", "build:watch": "wireit", "changelog:global": "node ./scripts/update-global-changelog.js", - "changeset-publish": "yarn prepublishOnly && yarn changeset version && yarn constraints --fix && yarn install --refresh-lockfile && yarn version:update && yarn changeset publish --no-git-tag && yarn push-to-remote && yarn create-git-tag && yarn postpublish", - "changeset-snapshot-publish": "yarn prepublishOnly && yarn changeset version --snapshot snapshot && yarn constraints --fix && yarn install --refresh-lockfile && yarn version:update && yarn changeset publish --no-git-tag --tag snapshot", "chromatic": "chromatic --build-script-name storybook:build # note that --project-token must be set in your env variables", "create-git-tag": "node --no-warnings ./scripts/create-git-tag.js", "custom-element-json": "node ./scripts/custom-element-json.js", + "dev:core": "yarn workspace @spectrum-web-components/core dev", "docs:analyze": "cem analyze --globs \"packages/**/*.ts\" --exclude \"**/*.d.ts\" --exclude \"**/stories/**\" --exclude \"**/icons/**\" --exclude \"**/elements/**\" --outdir projects/documentation --litelement", "docs:build": "yarn workspace documentation build", "docs:ci": "yarn docs:analyze && run-p docs:production storybook:build && cp projects/documentation/custom-elements.json projects/documentation/dist/storybook", @@ -50,20 +48,14 @@ "new-package": "yarn workspace swc-templates plop", "postinstall": "husky || true", "postpack": "pinst --enable", - "postpublish": "yarn prepublish:react && yarn publish:react && yarn postpublish:react", - "postpublish:react": "git reset --hard HEAD^ && git prune && rimraf react", + "prebuild": "wireit", "preeleventy": "yarn docs:analyze", "prepack": "pinst --disable", - "prepublish:react": "yarn build:react && sed -i \"\" \"s/react/# react/g\" .gitignore && git commit -am \"Commit React Wrappers\" --no-verify", - "prepublishOnly": "rimraf react && yarn build && yarn custom-element-json && yarn build:confirm && yarn changelog:global", "prestorybook": "wireit", "prestorybook:build": "cem analyze --outdir storybook/", "pretest:bench": "yarn build:tests && test -f test/benchmark/cli.js ||:", "pretest:visual": "yarn build && yarn build", "process-icons": "wireit", - "publish:react": "yarn changeset publish --no-git-tag --tag latest --no-push", - "push-to-remote": "git add . && git commit -m \"chore: release new versions #publish\" && git push", - "dev:core": "yarn workspace @spectrum-web-components/core dev", "start": "run-p dev:core storybook", "storybook": "wireit", "storybook:build": "NODE_ENV=production storybook build -o projects/documentation/dist/storybook -c storybook", @@ -85,7 +77,6 @@ "test:watch": "yarn test:watch:focus unit", "test:watch:flags:focus": "yarn build && run-p build:watch \"test:start --watch --group {1} --config web-test-runner.config.ci-chromium-flags.js\" --", "test:watch:focus": "yarn dev:core & yarn build && run-p build:watch \"test:start --watch --group {1}\" --", - "version:update": "genversion --verbose --semi --esm ./tools/base/src/version.js", "vrt:preview": "yarn wds --config test/visual/wds-vrt.config.js" }, "workspaces": [ @@ -99,7 +90,7 @@ "@changesets/changelog-github": "0.5.1", "@changesets/cli": "2.29.7", "@commitlint/cli": "19.8.1", - "@commitlint/config-conventional": "^19.8.1", + "@commitlint/config-conventional": "19.8.1", "@custom-elements-manifest/analyzer": "0.10.6", "@geometricpanda/storybook-addon-badges": "2.0.5", "@lit/react": "1.0.8", @@ -176,7 +167,7 @@ "jsonc-eslint-parser": "2.4.1", "latest-version": "9.0.0", "lightningcss": "1.30.1", - "lint-staged": "^16.1.2", + "lint-staged": "16.2.6", "lit": "^2.5.0 || ^3.1.3", "lit-analyzer": "2.0.3", "lit-html": "^2.4.0 || ^3.1.3", @@ -190,7 +181,7 @@ "prettier-plugin-package": "1.4.0", "pretty-bytes": "7.1.0", "re-template-tag": "2.0.1", - "replace-in-file": "^8.3.0", + "replace-in-file": "8.3.0", "rimraf": "6.0.1", "rollup": "4.52.2", "sinon": "17.0.2", @@ -216,10 +207,6 @@ "lit-element", "lit-html" ], - "engines": { - "node": ">=20", - "yarn": ">=4.6.0" - }, "wireit": { "build": { "dependencies": [ diff --git a/1st-gen/packages/picker/stories/picker.stories.ts b/1st-gen/packages/picker/stories/picker.stories.ts index 61a5a0f763..ad76843a40 100644 --- a/1st-gen/packages/picker/stories/picker.stories.ts +++ b/1st-gen/packages/picker/stories/picker.stories.ts @@ -774,6 +774,9 @@ custom.args = { open: true, }; custom.decorators = [isOverlayOpen]; +custom.swc_vrt = { + skip: true, +}; export const BackgroundClickTest = (): TemplateResult => { return html` diff --git a/1st-gen/projects/css-custom-vars-viewer/package.json b/1st-gen/projects/css-custom-vars-viewer/package.json index f53670e168..a60c41e2b4 100644 --- a/1st-gen/projects/css-custom-vars-viewer/package.json +++ b/1st-gen/projects/css-custom-vars-viewer/package.json @@ -13,10 +13,6 @@ "./sp-css-table.js": "./dist/src/sp-css-table.js", "./custom-vars-viewer.js": "./dist/src/custom-vars-viewer.js" }, - "engines": { - "node": ">=16.14.2", - "yarn": ">=1.16.0" - }, "scripts": { "analyze": "cem analyze --litelement", "build": "yarn parse-json && tsc && npm run analyze -- --exclude dist", diff --git a/1st-gen/projects/documentation/rollup.config.js b/1st-gen/projects/documentation/rollup.config.js index cc3737d671..a44b2e6bfa 100644 --- a/1st-gen/projects/documentation/rollup.config.js +++ b/1st-gen/projects/documentation/rollup.config.js @@ -240,7 +240,7 @@ export default async () => { replacement: '../../packages/', }, { - find: /^@swc\/core\/(.*)$/, + find: /^@spectrum-web-components\/core\/(.*)$/, replacement: path.resolve( '../../../2nd-gen/packages/core/dist/$1' ), diff --git a/1st-gen/scripts/cem-tools.js b/1st-gen/scripts/cem-tools.js index 37bf0f9518..e33fd14243 100644 --- a/1st-gen/scripts/cem-tools.js +++ b/1st-gen/scripts/cem-tools.js @@ -75,11 +75,16 @@ export function getWorkspacePackages( .filter( (pkg) => !ignoredPackages.includes(pkg.name) && - pkg.name !== '@spectrum-web-components/1st-gen' + pkg.name !== '@spectrum-web-components/1st-gen' && + pkg.name !== '@spectrum-web-components/2nd-gen' && + // Only include packages in 1st-gen packages/ and tools/ directories + (pkg.location.startsWith('1st-gen/packages/') || + pkg.location.startsWith('1st-gen/tools/')) ) .map((pkg) => ({ name: pkg.name, - path: pkg.location, + // Remove '1st-gen/' prefix since rootDir is already set to 1st-gen + path: pkg.location.replace(/^1st-gen\//, ''), })); } @@ -112,14 +117,15 @@ export async function customElementJson( }) .then(async () => { const outdir = options.outdir ?? pkg.path; + const packageName = pkg.name || pkg.path || 'unknown'; // Check if the custom-elements.json file exists if (fs.existsSync(path.join(outdir, 'custom-elements.json'))) { console.log( - `${'✓'.green} ${pkg.name.cyan} has a custom-elements.json file` + `${'✓'.green} ${packageName.cyan} has a custom-elements.json file` ); } else { console.log( - `${'❌'.red} ${pkg.name.cyan} does not have a custom-elements.json file` + `${'❌'.red} ${packageName.cyan} does not have a custom-elements.json file` ); } }) diff --git a/1st-gen/scripts/confirm-build.js b/1st-gen/scripts/confirm-build.js index 1116eb2eb0..8ac0a3195a 100644 --- a/1st-gen/scripts/confirm-build.js +++ b/1st-gen/scripts/confirm-build.js @@ -14,9 +14,15 @@ import fs from 'fs'; import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; import glob from 'fast-glob'; import 'colors'; +import { version } from '@spectrum-web-components/base/src/version.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const rootDir = path.join(__dirname, '..'); async function verifyCustomElementsJson() { // Components that don't need their own custom-elements.json manifest @@ -27,13 +33,20 @@ async function verifyCustomElementsJson() { 'packages/close-button', ]); - const packages = glob.sync('packages/*/', { onlyDirectories: true }); + const packages = glob.sync('packages/*/', { + onlyDirectories: true, + cwd: rootDir, + }); const checks = packages.map(async (pkg) => { const pkgPath = pkg.replace(/\/$/, ''); if (customElementsIgnoreList.has(pkgPath)) { return; } - const customElementsPath = path.join(pkg, 'custom-elements.json'); + const customElementsPath = path.join( + rootDir, + pkg, + 'custom-elements.json' + ); if (!fs.existsSync(customElementsPath)) { throw new Error(`Missing custom-elements.json in ${pkg}`); } @@ -42,38 +55,31 @@ async function verifyCustomElementsJson() { return Promise.all(checks); } -function verifyVersionJs() { +function verifyVersionSync() { let basePackageJson; try { basePackageJson = JSON.parse( - fs.readFileSync('tools/base/package.json', 'utf8') + fs.readFileSync( + path.join(rootDir, 'tools/base/package.json'), + 'utf8' + ) ); } catch (error) { throw new Error('Failed to read tools/base/package.json'); } - const versionJsPath = 'tools/base/src/version.js'; - - if (!fs.existsSync(versionJsPath)) { - throw new Error('version.js file is missing'); - } - const versionContent = fs.readFileSync(versionJsPath, 'utf8'); - const versionMatch = versionContent.match(/version = ['"]([^'"]+)['"]/); - - if (!versionMatch) { - throw new Error('Could not find version in version.js'); - } - - const versionJs = versionMatch[1]; - if (versionJs !== basePackageJson.version) { + if (version !== basePackageJson.version) { throw new Error( - `Version mismatch: version.js (${versionJs}) does not match tools/base/package.json (${basePackageJson.version})` + `Version mismatch: version.js (${version}) does not match tools/base/package.json (${basePackageJson.version})` ); } } async function verifyBuildArtifacts() { - const packages = glob.sync('packages/*/', { onlyDirectories: true }); + const packages = glob.sync('packages/*/', { + onlyDirectories: true, + cwd: rootDir, + }); const requiredFilesIgnoreList = new Set([ 'packages/clear-button', // extends button 'packages/close-button', // extends button @@ -97,7 +103,7 @@ async function verifyBuildArtifacts() { const checks = packages.map(async (pkg) => { const pkgPath = pkg.replace(/\/$/, ''); - const srcPath = path.join(pkg, 'src'); + const srcPath = path.join(rootDir, pkg, 'src'); if (!fs.existsSync(srcPath)) { throw new Error(`Missing src directory in ${pkg}`); } @@ -114,7 +120,7 @@ async function verifyBuildArtifacts() { // Verify all required files exist for this package for (const [filePattern, description] of requiredFiles) { - const pattern = path.join(pkg, filePattern); + const pattern = path.join(rootDir, pkg, filePattern); const files = glob.sync(pattern); if (files.length === 0) { throw new Error( @@ -131,8 +137,8 @@ async function main() { console.log('Verifying custom-elements.json files...'.cyan); await verifyCustomElementsJson(); - console.log('Verifying version.js...'.cyan); - verifyVersionJs(); + console.log('Verifying version synchronization...'.cyan); + verifyVersionSync(); console.log('Verifying build artifacts...'.cyan); await verifyBuildArtifacts(); diff --git a/1st-gen/scripts/update-global-changelog.js b/1st-gen/scripts/update-global-changelog.js index 3e8583ef46..1631d8c8e8 100644 --- a/1st-gen/scripts/update-global-changelog.js +++ b/1st-gen/scripts/update-global-changelog.js @@ -143,7 +143,7 @@ async function processChangesets() { const coreChanges = extractChanges( frontmatter, cleanDescription, - /['"]@swc\/core['"]:\s*(major|minor|patch)/g + /['"]@spectrum-web-components\/core['"]:\s*(major|minor|patch)/g ); // Merge results into categorized buckets @@ -299,8 +299,8 @@ function updateChangelogFile( * Reads changeset files, categorizes changes by type (major/minor/patch), * and updates both the 1st-gen and @spectrum-web-components/core changelogs accordingly. * - * Should be run during the release process after prepublishOnly but before - * changeset version. Automatically called by `yarn changeset-publish`. + * Should be run during the release process before changeset version. + * Automatically called by the unified publish script for regular releases. * * @returns {Promise} * @throws {Error} If there's an issue with git tags or file operations diff --git a/1st-gen/test/visual/rollup.config.js b/1st-gen/test/visual/rollup.config.js index 0a3d991b82..7c6c730cdf 100644 --- a/1st-gen/test/visual/rollup.config.js +++ b/1st-gen/test/visual/rollup.config.js @@ -27,7 +27,7 @@ export default { alias({ entries: [ { - find: /^@swc\/core\/(.*)$/, + find: /^@spectrum-web-components\/core\/(.*)$/, replacement: path.resolve( process.cwd(), '../2nd-gen/packages/core/dist/$1' diff --git a/1st-gen/tools/base/src/version.js b/1st-gen/tools/base/src/version.js old mode 100755 new mode 100644 index 64e807e036..7e996598a6 --- a/1st-gen/tools/base/src/version.js +++ b/1st-gen/tools/base/src/version.js @@ -1,2 +1,12 @@ -// Generated by genversion. -export const version = '1.9.0'; +/** + * Copyright 2025 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. + */ +export * from '@spectrum-web-components/core/shared/base/version.js'; diff --git a/1st-gen/tools/theme/package.json b/1st-gen/tools/theme/package.json index 3b7a4fb8d4..deb45513fc 100755 --- a/1st-gen/tools/theme/package.json +++ b/1st-gen/tools/theme/package.json @@ -290,6 +290,7 @@ ], "dependencies": { "@spectrum-web-components/base": "1.9.0", + "@spectrum-web-components/core": "0.0.1", "@spectrum-web-components/styles": "1.9.0" }, "types": "./src/index.d.ts", diff --git a/1st-gen/web-test-runner.config.js b/1st-gen/web-test-runner.config.js index d7687f0cd1..7493f97048 100644 --- a/1st-gen/web-test-runner.config.js +++ b/1st-gen/web-test-runner.config.js @@ -58,7 +58,7 @@ export default { alias({ entries: [ { - find: /^@swc\/core\/(.*)$/, + find: /^@spectrum-web-components\/core\/(.*)$/, replacement: path.resolve( __dirname, '../2nd-gen/packages/core/dist/$1' diff --git a/2nd-gen/README.md b/2nd-gen/README.md index 1d1443b934..a906116de4 100644 --- a/2nd-gen/README.md +++ b/2nd-gen/README.md @@ -1,159 +1,25 @@ # Spectrum Web Components - Second Generation -This folder contains the second generation of Spectrum Web Components. +This workspace contains the second generation of Spectrum Web Components, built with modern tooling and architecture. -## Architecture Overview +## Packages -The 2nd generation follows a dual-package architecture: - -- **`packages/core/`** - Abstract base classes providing common functionality -- **`packages/swc/`** - Concrete component implementations with styling and stories - -## Components - -The following components are available in the barebones milestone: - -- ... - -## Tooling Stack - -### Build System - -- **Vite** -- **TypeScript** - -### Development & Testing - -- **Storybook v9** - Component development with Web Components + Vite framework -- **Vitest** - Fast testing with browser mode and Playwright integration -- **Playwright** - End-to-end testing and accessibility validation - -### Code Quality - -- **ESLint** - Code linting with TypeScript support -- **Prettier** - Code formatting -- **Accessibility** - Built-in a11y testing with axe-core +- **[@spectrum-web-components/core](./packages/core)** - Abstract base classes providing shared functionality +- **[@adobe/swc](./packages/swc)** - Concrete component implementations with styling ## Getting Started -### Installation - ```bash -# Install dependencies +# Install dependencies from repository root yarn install -# Build packages +# Build 2nd-gen packages yarn build -``` - -### Development -```bash -# Start Storybook for component development +# Start Storybook yarn storybook - -# Run tests -yarn test - -# Build all packages -yarn build ``` -### Project Structure - -``` -2nd-gen/ -├── packages/ -│ ├── base/ # Abstract base classes -│ │ └── components/ -│ │ ├── alert/ -│ │ ├── badge/ -│ │ ├── button/ -│ │ ├── divider/ -│ │ ├── progress-bar/ -│ │ └── slider/ -│ └── swc/ # Concrete implementations -│ ├── .storybook/ # Storybook configuration -│ └── components/ -│ ├── alert/ -│ │ ├── Alert.ts -│ │ ├── styles.css.js -│ │ ├── stories/ -│ │ ├── test/ -│ │ └── README.md -│ └── ... (other components) -├── vitest.config.js # Test configuration -├── playwright.config.js # E2E test configuration -└── tsconfig.json # TypeScript configuration -``` - -## Component Development - -### Creating a New Component - -1. **Base Class**: Create base class in `packages/core/components/` -2. **Implementation**: Create concrete rendering implementation in `packages/swc/components/` -3. **Styles**: TBD (WIP) -4. **Stories**: Create Storybook stories (CSF) for development and documentation -5. **Tests**: Add Vitest tests for functionality and accessibility - -### Component Structure - -Each component follows this structure: - -``` -component-name/ -├── index.ts # Main export -├── ComponentName.ts # Implementation -├── styles.css.js # Styling -├── stories/ -│ └── ComponentName.stories.ts -├── test/ -│ └── ComponentName.test.ts -└── README.md # Component documentation -``` - -## Testing Strategy - -WIP - -## Build Outputs - -WIP - -Each package produces: - -- **ESM modules** targeting ES2022 -- **TypeScript declarations** (.d.ts files) -- **Source maps** for debugging -- **Tree-shakable exports** for optimal bundle sizes - -## Design Principles - -## Migration from 1st Generation - -The 2nd generation is designed to coexist with the 1st generation during the transition period. Components can be migrated incrementally as needed. - -## Contributing - -WIP - -### Code Standards - -WIP, example ideas below: - -- Follow existing component patterns -- Include comprehensive tests -- Document public APIs -- Use semantic commit messages - -## Future Roadmap - -The barebones milestone establishes the foundation. Future iterations will add: +## Documentation -- Additional components -- Advanced accessibility features -- Enhanced developer tooling -- Visual regression testing -- React wrapper generation -- ... +For comprehensive documentation on architecture, development workflows, and migration guides, see the [CONTRIBUTOR-DOCS](../CONTRIBUTOR-DOCS) directory in the repository root. diff --git a/2nd-gen/package.json b/2nd-gen/package.json index d375d8e6e1..025cc0d5e3 100644 --- a/2nd-gen/package.json +++ b/2nd-gen/package.json @@ -18,8 +18,11 @@ "scripts": { "build": "yarn workspaces foreach --from '{@spectrum-web-components/core,@adobe/swc}' --recursive run build", "clean": "yarn workspaces foreach --from '{@spectrum-web-components/core,@adobe/swc}' --recursive run clean", + "dev:analyze": "yarn workspace @adobe/swc analyze:watch", + "dev:core": "yarn workspace @spectrum-web-components/core dev", "lint": "eslint . --ext .ts,.js,.json", - "start": "yarn workspace @spectrum-web-components/core dev & yarn workspace @adobe/swc analyze:watch & yarn workspace @adobe/swc storybook", + "start": "run-p dev:core dev:analyze storybook", + "storybook": "yarn workspace @adobe/swc storybook", "storybook:build": "yarn workspace @adobe/swc storybook:build", "test": "yarn workspace @adobe/swc test" }, @@ -28,7 +31,8 @@ ], "devDependencies": { "eslint": "8.57.1", - "eslint-plugin-simple-import-sort": "12.1.1" + "eslint-plugin-simple-import-sort": "12.1.1", + "npm-run-all2": "8.0.4" }, "keywords": [ "design-system", @@ -41,9 +45,5 @@ "lit-html", "2nd-gen" ], - "engines": { - "node": ">=20", - "yarn": ">=4.6.0" - }, "packageManager": "yarn@4.9.2" } diff --git a/2nd-gen/packages/core/README.md b/2nd-gen/packages/core/README.md new file mode 100644 index 0000000000..77c3333f02 --- /dev/null +++ b/2nd-gen/packages/core/README.md @@ -0,0 +1,7 @@ +# @spectrum-web-components/core + +Abstract base classes for Spectrum Web Components. + +This package provides shared functionality between multiple generations of implementations and is **not intended for direct external use**. + +For more information, visit the [Spectrum Web Components repository](https://github.com/adobe/spectrum-web-components). diff --git a/2nd-gen/packages/core/package.json b/2nd-gen/packages/core/package.json index f94bab8e54..189944d996 100644 --- a/2nd-gen/packages/core/package.json +++ b/2nd-gen/packages/core/package.json @@ -61,11 +61,16 @@ "design-system", "spectrum", "adobe", + "adobe-spectrum", "web components", - "base-classes", - "abstract" + "web-components", + "lit-element", + "lit-html", + "component", + "css" ], - "engines": { - "node": ">=20" - } + "publishConfig": { + "access": "public" + }, + "module": "./dist/index.js" } diff --git a/2nd-gen/packages/core/shared/base/version.ts b/2nd-gen/packages/core/shared/base/version.ts index 018b37860c..64e807e036 100644 --- a/2nd-gen/packages/core/shared/base/version.ts +++ b/2nd-gen/packages/core/shared/base/version.ts @@ -1,13 +1,2 @@ -/** - * Copyright 2025 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. - */ // Generated by genversion. export const version = '1.9.0'; diff --git a/2nd-gen/packages/swc/package.json b/2nd-gen/packages/swc/package.json index c46edf4a4d..9422321bd0 100644 --- a/2nd-gen/packages/swc/package.json +++ b/2nd-gen/packages/swc/package.json @@ -30,6 +30,7 @@ } }, "main": "./dist/index.js", + "module": "./dist/index.js", "files": [ "dist/" ], @@ -39,12 +40,13 @@ "build": "vite build", "clean": "rimraf dist", "dev": "vite build --watch", + "dev:core": "yarn workspace @spectrum-web-components/core dev", "storybook": "yarn analyze && storybook dev -p 6006", "storybook:build": "yarn analyze && storybook build", "test": "vitest --run", "test:coverage": "vitest --coverage", "test:ui": "vitest --ui", - "test:watch": "yarn workspace @spectrum-web-components/core dev & vitest" + "test:watch": "run-p dev:core vitest" }, "types": "./dist/index.d.ts", "dependencies": { @@ -55,7 +57,7 @@ "@axe-core/playwright": "4.10.2", "@custom-elements-manifest/analyzer": "0.10.8", "@storybook/addon-a11y": "10.0.1", - "@storybook/addon-designs": "10.0.2", + "@storybook/addon-designs": "11.0.1", "@storybook/addon-docs": "10.0.1", "@storybook/addon-vitest": "10.0.1", "@storybook/test-runner": "0.23.0", @@ -85,12 +87,16 @@ "design-system", "spectrum", "adobe", + "adobe-spectrum", "web components", - "lit", - "components" + "web-components", + "lit-element", + "lit-html", + "component", + "css" ], - "engines": { - "node": ">=20" - }, - "customElements": ".storybook/custom-elements.json" + "customElements": ".storybook/custom-elements.json", + "publishConfig": { + "access": "public" + } } diff --git a/CONTRIBUTOR-DOCS/01_contributor-guides/06_releasing-swc.md b/CONTRIBUTOR-DOCS/01_contributor-guides/06_releasing-swc.md index e0e0ef5a2c..fea3d0403c 100644 --- a/CONTRIBUTOR-DOCS/01_contributor-guides/06_releasing-swc.md +++ b/CONTRIBUTOR-DOCS/01_contributor-guides/06_releasing-swc.md @@ -98,29 +98,82 @@ If not logged in, run `npm login` to sign in to your account. ## Releasing to NPM — the good stuff -1. Run `git checkout main && git fetch && git pull && git clean -dfX` to ensure you are working with the latest code -2. Run `yarn install && yarn build` to install all dependencies and build the pre-processed assets for publication. - 1. Confirm no files were updated or modified -3. Scan the version summary for any unexpected changes - 1. In your IDE search `': major` , `': minor`, `': patch` , based on the results in the order of this search list, the highest level takes precedence - 1. exclude files: `.changeset/README.md` -4. Open your authenticator app to have it ready -5. Run`yarn changeset-publish` -6. Enter the one-time password from your authenticator for NPM. - 1. Wait for a fresh password; a stale timer might cause issues. -7. After the SWC packages are released, the React Wrapper packages will be generated. - 1. This multi-phase approach ensures that the wrapped packages share the same version as the standard packages. -8. Enter a new one-time password from your authenticator for NPM. -9. The `yarn publish:changeset` command will automatically commit the changes to main with a commit message of `chore: release new versions #publish` - 1. The docs site will publish automatically if the `#publish` string is included in the commit message and the check suite runs successfully. -10. Confirm the build on `main` passes +The publishing workflow is handled by a single unified script (`scripts/publish.js`) that automates the entire process: cleaning, building, versioning, publishing, and git operations. + +1. **Prepare your workspace:** + - Run `git checkout main && git fetch && git pull` to ensure you have the latest code + - Confirm the working directory is clean with `git status` +2. **Review changesets:** + - Scan the `.changeset` directory for pending changes + - In your IDE search `': major`, `': minor`, `': patch` to verify the release impact + - Exclude files: `.changeset/README.md` + - The highest level takes precedence (major > minor > patch) + - Confirm the changes match your expectations +3. **Prepare for publishing:** + - Open your authenticator app to have it ready + - You'll need to enter a one-time password twice during the process: + 1. Once for the main SWC packages + 2. Once for the React wrapper packages +4. **Run the publish command:** + - **Regular release:** `yarn publish` + - Creates git tags + - Publishes to npm with `latest` tag + - Commits changes to `main` + - **Snapshot release:** `yarn publish:snapshot` + - No git tags + - Publishes to npm with `snapshot` tag + - No git commits + - **Custom tag release:** `node ./scripts/publish.js --tag beta` + - No git tags + - Publishes to npm with custom tag (e.g., `beta`, `alpha`, `rc`, `nightly`) + - No git commits +5. **What happens during publishing:** + 1. The script cleans all build artifacts and reinstalls dependencies + 2. Builds all packages (1st-gen and 2nd-gen) + 3. Generates custom elements manifests + 4. Versions packages with changesets + 5. Publishes to npm (you'll enter your first OTP here) + 6. Builds React wrapper packages + 7. Publishes React wrappers to npm (you'll enter your second OTP here) + 8. For regular releases: commits changes and creates git tags +6. **Verify the release:** + - For regular releases, confirm the build on `main` passes + - Check the [tags page](https://github.com/adobe/spectrum-web-components/tags) to verify new tags were created + - The docs site will publish automatically if the commit message includes `#publish` and checks pass ### Troubleshooting If publishing fails with an error: - Check the [list of tags](https://github.com/adobe/spectrum-web-components/tags) to see if new tags have been released for your publishing attempt. -- If they were, run `yarn publish:changeset` again. +- If they were, run `yarn publish` again (or the appropriate command for your release type). + +### Custom npm tags + +For special releases like beta, alpha, nightly, or release candidates, you can use custom npm tags with the `--tag` flag: + +```bash +# Beta release +node ./scripts/publish.js --tag beta + +# Alpha release +node ./scripts/publish.js --tag alpha + +# Nightly release +node ./scripts/publish.js --tag nightly + +# Release candidate +node ./scripts/publish.js --tag rc +``` + +Users can then install these versions with: + +```bash +yarn add @spectrum-web-components/button@beta +yarn add @spectrum-web-components/button@alpha +yarn add @spectrum-web-components/button@nightly +yarn add @spectrum-web-components/button@rc +``` --- diff --git a/lint-staged.config.js b/lint-staged.config.js index 109465a58e..f6122a8b26 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -23,10 +23,10 @@ export default { 'yarn workspace @spectrum-web-components/1st-gen lit-analyzer', ], 'package.json': () => [ - 'cd 1st-gen && genversion --es6 --semi tools/base/src/version.js', + 'genversion --source ./1st-gen/tools/base/package.json --es6 --semi --force ./2nd-gen/packages/core/shared/base/version.ts', 'yarn constraints --fix', 'yarn install --refresh-lockfile', - 'git add 1st-gen/tools/base/src/version.js yarn.lock', + 'git add 2nd-gen/packages/core/shared/base/version.ts yarn.lock', ], '.changeset/*.md': ['node 1st-gen/scripts/escape-changelog-tags.js'], '!(*.css|*.ts)': [ diff --git a/package.json b/package.json index 2f6ebb56b9..bd84fcb68a 100644 --- a/package.json +++ b/package.json @@ -14,17 +14,19 @@ }, "type": "module", "scripts": { - "build": "yarn build:2nd-gen && yarn build:1st-gen ", + "build": "run-s build:2nd-gen build:1st-gen", "build:1st-gen": "yarn workspace @spectrum-web-components/1st-gen build", "build:2nd-gen": "yarn workspace @spectrum-web-components/2nd-gen build", - "lint": "yarn lint:1st-gen && yarn lint:2nd-gen", + "lint": "run-s lint:2nd-gen lint:1st-gen", "lint:1st-gen": "yarn workspace @spectrum-web-components/1st-gen lint", "lint:2nd-gen": "yarn workspace @spectrum-web-components/2nd-gen lint", - "postinstall": "husky || true && patch-package", - "start": "yarn start:2nd-gen & yarn start:1st-gen", + "postinstall": "husky || true", + "publish": "node ./scripts/publish.js", + "publish:snapshot": "node ./scripts/publish.js --tag snapshot", + "start": "run-p start:2nd-gen start:1st-gen", "start:1st-gen": "yarn workspace @spectrum-web-components/1st-gen start", "start:2nd-gen": "yarn workspace @spectrum-web-components/2nd-gen start", - "test": "yarn test:1st-gen & yarn test:2nd-gen", + "test": "run-p test:2nd-gen test:1st-gen", "test:1st-gen": "yarn workspace @spectrum-web-components/1st-gen test", "test:2nd-gen": "yarn workspace @spectrum-web-components/2nd-gen test" }, @@ -42,11 +44,13 @@ "devDependencies": { "@changesets/cli": "2.29.7", "@commitlint/cli": "19.8.1", - "@commitlint/config-conventional": "^19.8.1", + "@commitlint/config-conventional": "19.8.1", + "genversion": "3.2.0", "husky": "9.1.7", - "lint-staged": "^16.1.2", - "patch-package": "^8.0.0", - "replace-in-file": "^8.3.0" + "lint-staged": "16.2.6", + "npm-run-all2": "8.0.4", + "replace-in-file": "8.3.0", + "yargs": "17.7.2" }, "keywords": [ "design-system", @@ -59,7 +63,7 @@ "lit-html" ], "engines": { - "node": ">=20.10.0", + "node": ">=20.19.0", "yarn": ">=4.6.0" }, "packageManager": "yarn@4.9.2" diff --git a/scripts/publish.js b/scripts/publish.js new file mode 100755 index 0000000000..1d7a3fc39c --- /dev/null +++ b/scripts/publish.js @@ -0,0 +1,145 @@ +#!/usr/bin/env node + +/** + * Copyright 2025 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. + */ + +/* eslint-disable no-console */ + +/** + * Unified publishing script for both 1st-gen and 2nd-gen packages. + * Handles the complete release workflow including build, versioning, and publishing. + * + * @example + * # Regular release with git tags (uses "latest" tag) + * yarn publish + * + * # Snapshot release with custom tag + * node ./scripts/publish.js --tag snapshot + * + * # Nightly release + * node ./scripts/publish.js --tag nightly + * + * # Beta release + * node ./scripts/publish.js --tag beta + */ + +import { execSync } from 'child_process'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +const args = yargs(hideBin(process.argv)) + .option('tag', { + type: 'string', + description: + 'NPM dist-tag to publish under. Use "latest" for regular releases, or any other tag (e.g., "nightly", "snapshot", "beta") for snapshot releases', + default: 'latest', + }) + .help() + .parse(); + +// Infer snapshot mode from tag +const isSnapshot = args.tag !== 'latest'; + +function run(command, description) { + if (description) { + console.log(`\n📦 ${description}...`); + } + try { + execSync(command, { stdio: 'inherit' }); + } catch (error) { + console.error(`❌ Failed: ${description || command}`); + process.exit(1); + } +} + +async function publish() { + console.log('\n🚀 Starting publish workflow...\n'); + + // Step 0: Clean slate - remove all git-ignored files and reinstall dependencies + run('git clean -dfX', 'Cleaning all git-ignored files'); + run('yarn install', 'Installing fresh dependencies'); + + // Step 1: Prepublish - Build everything and generate manifests + run('yarn build', 'Building all packages'); + run( + 'yarn workspace @spectrum-web-components/1st-gen custom-element-json', + 'Generating custom elements manifests' + ); + run( + 'yarn workspace @spectrum-web-components/1st-gen build:confirm', + 'Confirming build artifacts' + ); + + // Step 2: Version bump with changesets + if (isSnapshot) { + run( + `yarn changeset version --snapshot ${args.tag}`, + `Versioning packages (snapshot: ${args.tag})` + ); + } else { + // Update changelog before versioning and only for regular releases + run( + 'yarn workspace @spectrum-web-components/1st-gen changelog:global', + 'Updating global changelog' + ); + run('yarn changeset version', 'Versioning packages'); + } + // Step 3: Update version file for 2nd-gen + run( + 'genversion --source ./1st-gen/tools/base/package.json --semi --es6 --force ./2nd-gen/packages/core/shared/base/version.ts', + 'Updating 2nd-gen version.ts from 1st-gen' + ); + + // Step 4: Publish to npm + run( + `yarn changeset publish --no-git-tag --tag ${args.tag}`, + `Publishing to npm (tag: ${args.tag})` + ); + + // Step 5: Git operations (skip for snapshots) + if (!isSnapshot) { + run( + 'git add . && git commit -m "chore: release new versions #publish"', + 'Committing release' + ); + run('git push', 'Pushing to remote'); + run( + 'node --no-warnings ./1st-gen/scripts/create-git-tag.js', + 'Creating and pushing git tag' + ); + } + + // Step 6: Postpublish - React wrappers (build and publish directly with npm) + run( + 'yarn workspace @spectrum-web-components/1st-gen build:react', + 'Building React wrappers' + ); + + // Publish each React package directly using npm + const publishCmd = isSnapshot + ? `npm publish --tag ${args.tag} --access public` + : `npm publish --access public`; + + run( + `cd 1st-gen/react && for dir in */; do (cd "$dir" && ${publishCmd}) || exit 1; done`, + `Publishing React wrappers (tag: ${args.tag})` + ); + + run('rm -rf 1st-gen/react', 'Removing React wrappers'); + + console.log('\n✅ Publish workflow completed successfully!\n'); +} + +publish().catch((error) => { + console.error('❌ Publish workflow failed:', error); + process.exit(1); +}); diff --git a/yarn.config.cjs b/yarn.config.cjs index 411d64faf0..4ebf7f2606 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -27,10 +27,13 @@ module.exports = defineConfig({ * Fetch a list of all the component workspaces using a glob pattern * @type {string[]} components */ - const components = fg.sync('{packages,tools}/*', { - cwd: __dirname, - onlyDirectories: true, - }); + const components = fg.sync( + '{1st-gen/{packages,tools},2nd-gen/packages}/*', + { + cwd: __dirname, + onlyDirectories: true, + } + ); /** * This function checks the workspace for any local package references @@ -101,9 +104,14 @@ module.exports = defineConfig({ * to simplify into a readable set of operations * @param {Workspace} workspace * @param {string} folderName + * @param {boolean} is2ndGen * @returns {void} */ - function validateComponentPackageJson(workspace, folderName) { + function validateComponentPackageJson( + workspace, + folderName, + is2ndGen = false + ) { // Only update the homepage if it does not already exist if (!workspace.manifest.homepage) { workspace.set( @@ -112,24 +120,32 @@ module.exports = defineConfig({ ); } - workspace.set('publishConfig.access', 'public'); workspace.set('type', 'module'); + workspace.set('publishConfig.access', 'public'); workspace.set('keywords', keywords(['component', 'css'])); - // A subset of components have a different entry point than the default - if ( - [ - 'clear-button', - 'close-button', - 'modal', - 'opacity-checkerboard', - ].includes(folderName) - ) { - workspace.set('main', `./src/${folderName}.css.js`); - workspace.set('module', `./src/${folderName}.css.js`); + // 2nd-gen packages use different entry points + if (is2ndGen) { + // 2nd-gen uses dist folder for builds + workspace.set('main', './dist/index.js'); + workspace.set('module', './dist/index.js'); } else { - workspace.set('main', './src/index.js'); - workspace.set('module', './src/index.js'); + // 1st-gen packages use src folder + // A subset of components have a different entry point than the default + if ( + [ + 'clear-button', + 'close-button', + 'modal', + 'opacity-checkerboard', + ].includes(folderName) + ) { + workspace.set('main', `./src/${folderName}.css.js`); + workspace.set('module', `./src/${folderName}.css.js`); + } else { + workspace.set('main', './src/index.js'); + workspace.set('module', './src/index.js'); + } } } @@ -245,8 +261,10 @@ module.exports = defineConfig({ * Process the components workspaces with component-specific configuration */ if (isComponent) { - const folderName = workspace.cwd?.split('/')?.[1]; - validateComponentPackageJson(workspace, folderName); + // Get the last part of the path (e.g., 'button' from '1st-gen/packages/button') + const folderName = workspace.cwd?.split('/').pop(); + const is2ndGen = workspace.cwd.startsWith('2nd-gen/'); + validateComponentPackageJson(workspace, folderName, is2ndGen); validateLocalPackages(workspace); } else { /** diff --git a/yarn.lock b/yarn.lock index 2da92dce7d..b32df2f1d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -178,7 +178,7 @@ __metadata: "@custom-elements-manifest/analyzer": "npm:0.10.8" "@spectrum-web-components/core": "npm:0.0.1" "@storybook/addon-a11y": "npm:10.0.1" - "@storybook/addon-designs": "npm:10.0.2" + "@storybook/addon-designs": "npm:11.0.1" "@storybook/addon-docs": "npm:10.0.1" "@storybook/addon-vitest": "npm:10.0.1" "@storybook/test-runner": "npm:0.23.0" @@ -1971,7 +1971,7 @@ __metadata: languageName: node linkType: hard -"@commitlint/config-conventional@npm:^19.8.1": +"@commitlint/config-conventional@npm:19.8.1": version: 19.8.1 resolution: "@commitlint/config-conventional@npm:19.8.1" dependencies: @@ -5306,7 +5306,7 @@ __metadata: "@changesets/changelog-github": "npm:0.5.1" "@changesets/cli": "npm:2.29.7" "@commitlint/cli": "npm:19.8.1" - "@commitlint/config-conventional": "npm:^19.8.1" + "@commitlint/config-conventional": "npm:19.8.1" "@custom-elements-manifest/analyzer": "npm:0.10.6" "@geometricpanda/storybook-addon-badges": "npm:2.0.5" "@lit/react": "npm:1.0.8" @@ -5383,7 +5383,7 @@ __metadata: jsonc-eslint-parser: "npm:2.4.1" latest-version: "npm:9.0.0" lightningcss: "npm:1.30.1" - lint-staged: "npm:^16.1.2" + lint-staged: "npm:16.2.6" lit: "npm:^2.5.0 || ^3.1.3" lit-analyzer: "npm:2.0.3" lit-html: "npm:^2.4.0 || ^3.1.3" @@ -5397,7 +5397,7 @@ __metadata: prettier-plugin-package: "npm:1.4.0" pretty-bytes: "npm:7.1.0" re-template-tag: "npm:2.0.1" - replace-in-file: "npm:^8.3.0" + replace-in-file: "npm:8.3.0" rimraf: "npm:6.0.1" rollup: "npm:4.52.2" sinon: "npm:17.0.2" @@ -5421,6 +5421,7 @@ __metadata: dependencies: eslint: "npm:8.57.1" eslint-plugin-simple-import-sort: "npm:12.1.1" + npm-run-all2: "npm:8.0.4" languageName: unknown linkType: soft @@ -6374,6 +6375,7 @@ __metadata: resolution: "@spectrum-web-components/theme@workspace:1st-gen/tools/theme" dependencies: "@spectrum-web-components/base": "npm:1.9.0" + "@spectrum-web-components/core": "npm:0.0.1" "@spectrum-web-components/styles": "npm:1.9.0" languageName: unknown linkType: soft @@ -6535,16 +6537,16 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-designs@npm:10.0.2": - version: 10.0.2 - resolution: "@storybook/addon-designs@npm:10.0.2" +"@storybook/addon-designs@npm:11.0.1": + version: 11.0.1 + resolution: "@storybook/addon-designs@npm:11.0.1" dependencies: "@figspec/react": "npm:^1.0.0" peerDependencies: - "@storybook/addon-docs": ^0.0.0-0 || ^9.0.0 || ^9.1.0-0 + "@storybook/addon-docs": ^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^0.0.0-0 || ^9.0.0 || ^9.1.0-0 + storybook: ^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 peerDependenciesMeta: "@storybook/addon-docs": optional: true @@ -6552,7 +6554,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10c0/8d02487ad337570eabc7962131ee933bf3995fe4700211dc734958d3a59c31f981214c0bc651b574b76f6e97abf259603bd277b30693b45eea2abf64899aab74 + checksum: 10c0/69aca21c9be81345c91c782a72d7b2d90633581ef28be582836af1d9a7ddcc7515816391020b0ae947058bc59fd56c439a4b957a46ece6439cfbf8bfd1e0e2e7 languageName: node linkType: hard @@ -20188,7 +20190,7 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:^16.1.2": +"lint-staged@npm:16.2.6": version: 16.2.6 resolution: "lint-staged@npm:16.2.6" dependencies: @@ -26565,7 +26567,7 @@ __metadata: languageName: node linkType: hard -"replace-in-file@npm:^8.3.0": +"replace-in-file@npm:8.3.0": version: 8.3.0 resolution: "replace-in-file@npm:8.3.0" dependencies: @@ -27154,11 +27156,13 @@ __metadata: dependencies: "@changesets/cli": "npm:2.29.7" "@commitlint/cli": "npm:19.8.1" - "@commitlint/config-conventional": "npm:^19.8.1" + "@commitlint/config-conventional": "npm:19.8.1" + genversion: "npm:3.2.0" husky: "npm:9.1.7" - lint-staged: "npm:^16.1.2" - patch-package: "npm:^8.0.0" - replace-in-file: "npm:^8.3.0" + lint-staged: "npm:16.2.6" + npm-run-all2: "npm:8.0.4" + replace-in-file: "npm:8.3.0" + yargs: "npm:17.7.2" languageName: unknown linkType: soft