From 2dc7185a433f4ebd540860a49cd8829760fbd49a Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Thu, 28 Nov 2024 23:42:41 +0100 Subject: [PATCH] Making workspaces (#536) I noticed types collision between sources and integration test in #535 . That could not be fixed by deduplication. On contrary, I'm going to approach it by separating development and integration testing environments and make isolated workspaces. --- .github/workflows/ci.yml | 9 +- .github/workflows/minor.yml | 2 +- .github/workflows/npm-publish.yml | 5 +- .github/workflows/patch.yml | 2 +- README.md | 92 +----------------- bun.lockb | Bin 92738 -> 94712 bytes bunfig.toml | 1 + package.json | 74 +++----------- .../integration-test}/.gitignore | 0 .../integration-test}/integration.spec.ts | 0 .../integration-test}/tsconfig.json | 2 +- .../merge-sx/CHANGELOG.md | 7 ++ packages/merge-sx/LICENSE | 21 ++++ packages/merge-sx/README.md | 91 +++++++++++++++++ packages/merge-sx/package.json | 64 ++++++++++++ {src => packages/merge-sx/src}/index.spec.ts | 0 {src => packages/merge-sx/src}/index.ts | 0 packages/merge-sx/tsconfig.json | 4 + .../merge-sx/tsdown.config.ts | 0 tools/benchmark.bench.ts | 2 +- tools/version.ts | 53 ++++++++++ tsconfig.json | 3 +- 22 files changed, 266 insertions(+), 166 deletions(-) mode change 100644 => 120000 README.md rename {integration-test => packages/integration-test}/.gitignore (100%) rename {integration-test => packages/integration-test}/integration.spec.ts (100%) rename {integration-test => packages/integration-test}/tsconfig.json (66%) rename CHANGELOG.md => packages/merge-sx/CHANGELOG.md (95%) create mode 100644 packages/merge-sx/LICENSE create mode 100644 packages/merge-sx/README.md create mode 100644 packages/merge-sx/package.json rename {src => packages/merge-sx/src}/index.spec.ts (100%) rename {src => packages/merge-sx/src}/index.ts (100%) create mode 100644 packages/merge-sx/tsconfig.json rename tsdown.config.ts => packages/merge-sx/tsdown.config.ts (100%) create mode 100644 tools/version.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a27b2a6..a3292669 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,20 +120,19 @@ jobs: env: MUI_VERSION: ${{ matrix.mui-version }} run: | - bun link - cat >integration-test/package.json <packages/integration-test/package.json <MUI 5 has introduced a new way of styling React components using a Theme-aware -`sx` property. It can be necessary to create your own styled components while still allowing for additional styling -by the consumer. In this case your component will have its own `sx` property, most likely optional. This makes it -necessary somehow to combine your own styles with the styles coming from the consumer of your component. One approach -might be using a styling wrapper, an alternative way to style your component with the -[`styled()` utility](https://mui.com/system/styled/). Thus, you could apply the consumer's `sx` to the pre-styled -component. However, this approach can be inconvenient for several reasons. - -I came to conclusion that merging several `sx` properties is better. However, the `SxProps` has rather complex data -type. It can be an object with styling properties, can be function, can be `null`. It can be a challenge to perform -a merge under strict typing of Typescript. - -## How it works - -Luckily, starting version [5.1.0](https://github.com/mui/material-ui/releases/tag/v5.1.0) of MUI `SxProps` -[can](https://github.com/mui/material-ui/blob/v5.1.0/packages/mui-system/src/styleFunctionSx/styleFunctionSx.d.ts#L60) -also be `array`. However, nested arrays are not allowed, so this utility does exactly the flat merge, also bringing -support for conditional and optional inclusions, providing a very simple and semantically clean interface. - -## Performance - -The utility has been tested to support up to 65535 arguments. - -![Performance chart](https://raw.githubusercontent.com/RobinTail/merge-sx/refs/heads/master/performance.svg) - -## Examples - -### Conditional merging - -The utility supports `false`: - -```tsx - -``` - -### Optional merging - -The utility supports `undefined`: - -```tsx -interface MyButtonProps { - sx?: SxProps; // optional prop for consumer -} - -const MyButton = ({ sx: consumerSx }: MyButtonProps) => ( - -); -``` - -### Inline Theme supplying - -The utility is generic and accepts the type argument. - -```ts -// theme is Theme -mergeSx(sx1, (theme) => ({ mb: theme.spacing(1) })); -``` diff --git a/README.md b/README.md new file mode 120000 index 00000000..8b0ec445 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +packages/merge-sx/README.md \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 908e80c96ce916fe491c4598599cb591278fc5d5..6502b45f163dee0714a63df46e68a67257b777ad 100755 GIT binary patch delta 23276 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 V4eqBdP)UCHc5)VgNg{{zkdV%h)z diff --git a/bunfig.toml b/bunfig.toml index a16a2973..fe89be02 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,5 +1,6 @@ [install] peer = false +registry = { url = "https://registry.npmjs.org", token = "$NPM_KEY" } [test] coverage = true # always enable coverage coverageReporter = ["text", "lcov"] diff --git a/package.json b/package.json index 1ec9cfed..9a77cb90 100644 --- a/package.json +++ b/package.json @@ -1,77 +1,27 @@ { - "name": "merge-sx", - "version": "3.2.4", - "description": "Combines multiple SxProps for Material UI components.", - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "type": "module", - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.cts", - "default": "./dist/index.cjs" - } - } - }, - "files": [ - "dist", - "*.md" - ], - "sideEffects": false, - "repository": { - "type": "git", - "url": "git+https://github.com/RobinTail/merge-sx.git" - }, - "homepage": "https://github.com/RobinTail/merge-sx", + "private": true, + "workspaces": ["packages/*"], "author": { "name": "Anna Bocharova", "url": "https://robintail.cz" }, - "bugs": "https://github.com/RobinTail/merge-sx/issues", - "funding": "https://github.com/sponsors/RobinTail", "license": "MIT", "scripts": { - "build": "tsdown && attw --pack", + "build": "bun run --cwd packages/merge-sx build", "lint": "bun run biome check", - "test": "tsc --noEmit && bun test src", - "intTest": "tsc -p integration-test/tsconfig.json && bun test integration-test", - "mdfix": "bunx --bun prettier *.md --write", - "postversion": "git push && git push --tags", - "prepublishOnly": "bun run lint && bun run test && bun run build" - }, - "peerDependencies": { - "@mui/material": "^5.1.0 || ^6.0.0" + "test": "bun test --filter merge-sx", + "intTest": "tsc -p packages/integration-test && bun test --filter integration-test", + "mdfix": "bunx --bun prettier packages/merge-sx/*.md --write", + "version": "bun run tools/version.ts" }, "devDependencies": { "@arethetypeswrong/cli": "^0.17.0", "@biomejs/biome": "1.9.4", - "@emotion/styled": "^11.13.0", - "@mui/material": "^6.1.2", "@tsconfig/bun": "^1.0.7", "@types/bun": "latest", - "react": "^18.3.1", - "tsdown": "^0.3.0", - "typescript": "^5.6.2" - }, - "keywords": [ - "react", - "typescript", - "styled-components", - "reactjs", - "material-ui", - "styling", - "css-in-js", - "jss", - "strict", - "mui", - "merge", - "combine", - "merging", - "muiv5" - ] + "@types/semver": "^7.5.8", + "semver": "^7.6.3", + "tsdown": "^0.3.1", + "typescript": "^5.7.2" + } } diff --git a/integration-test/.gitignore b/packages/integration-test/.gitignore similarity index 100% rename from integration-test/.gitignore rename to packages/integration-test/.gitignore diff --git a/integration-test/integration.spec.ts b/packages/integration-test/integration.spec.ts similarity index 100% rename from integration-test/integration.spec.ts rename to packages/integration-test/integration.spec.ts diff --git a/integration-test/tsconfig.json b/packages/integration-test/tsconfig.json similarity index 66% rename from integration-test/tsconfig.json rename to packages/integration-test/tsconfig.json index a71ff2be..cbb904ee 100644 --- a/integration-test/tsconfig.json +++ b/packages/integration-test/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.json", + "extends": "../../tsconfig.json", "include": ["*.ts"], "compilerOptions": { "noEmit": true diff --git a/CHANGELOG.md b/packages/merge-sx/CHANGELOG.md similarity index 95% rename from CHANGELOG.md rename to packages/merge-sx/CHANGELOG.md index 307b823d..900f4e2a 100644 --- a/CHANGELOG.md +++ b/packages/merge-sx/CHANGELOG.md @@ -2,6 +2,13 @@ ## Version 3 +### v3.3.0 + +- Technical update: + - Upgraded all dependencies and tested on latest MUI 6; + - Implemented workspaces to avoid duplicated dependencies for the integration test; + - Releasing using `bun publish`. + ### v3.2.3 - Dependencies update: diff --git a/packages/merge-sx/LICENSE b/packages/merge-sx/LICENSE new file mode 100644 index 00000000..c9112185 --- /dev/null +++ b/packages/merge-sx/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Anna Bocharova + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/merge-sx/README.md b/packages/merge-sx/README.md new file mode 100644 index 00000000..357b33db --- /dev/null +++ b/packages/merge-sx/README.md @@ -0,0 +1,91 @@ +# merge-sx + +[![Coverage Status](https://coveralls.io/repos/github/RobinTail/merge-sx/badge.svg?branch=master)](https://coveralls.io/github/RobinTail/merge-sx?branch=master) +![NPM Downloads](https://img.shields.io/npm/dw/merge-sx) +![NPM License](https://img.shields.io/npm/l/merge-sx) + +Combines multiple [`SxProps`](https://mui.com/system/getting-started/the-sx-prop) +for [Material UI](https://mui.com/) components. + +## Installation + +```shell +npm install merge-sx +# or +yarn add merge-sx +``` + +## Usage + +The utility provides a very simple and semantically clean interface, that supports conditional and optional inclusions. + +```ts +import { mergeSx } from "merge-sx"; + +// Merge your SxProps +mergeSx(sx1, sx2 /*, ... */); +// Merge optionally +mergeSx(internalSx, props?.sx); // supports undefined +// Merge conditionally +mergeSx(commonSx, isMobile && mobileSx); // supports false +``` + +## Why might you need it + +MUI 5 has introduced a new way of styling React components using a Theme-aware +`sx` property. It can be necessary to create your own styled components while still allowing for additional styling +by the consumer. In this case your component will have its own `sx` property, most likely optional. This makes it +necessary somehow to combine your own styles with the styles coming from the consumer of your component. One approach +might be using a styling wrapper, an alternative way to style your component with the +[`styled()` utility](https://mui.com/system/styled/). Thus, you could apply the consumer's `sx` to the pre-styled +component. However, this approach can be inconvenient for several reasons. + +I came to conclusion that merging several `sx` properties is better. However, the `SxProps` has rather complex data +type. It can be an object with styling properties, can be function, can be `null`. It can be a challenge to perform +a merge under strict typing of Typescript. + +## How it works + +Luckily, starting version [5.1.0](https://github.com/mui/material-ui/releases/tag/v5.1.0) of MUI `SxProps` +[can](https://github.com/mui/material-ui/blob/v5.1.0/packages/mui-system/src/styleFunctionSx/styleFunctionSx.d.ts#L60) +also be `array`. However, nested arrays are not allowed, so this utility does exactly the flat merge, also bringing +support for conditional and optional inclusions, providing a very simple and semantically clean interface. + +## Performance + +The utility has been tested to support up to 65535 arguments. + +![Performance chart](https://raw.githubusercontent.com/RobinTail/merge-sx/refs/heads/master/performance.svg) + +## Examples + +### Conditional merging + +The utility supports `false`: + +```tsx + +``` + +### Optional merging + +The utility supports `undefined`: + +```tsx +interface MyButtonProps { + sx?: SxProps; // optional prop for consumer +} + +const MyButton = ({ sx: consumerSx }: MyButtonProps) => ( + +); +``` + +### Inline Theme supplying + +The utility is generic and accepts the type argument. + +```ts +// theme is Theme +mergeSx(sx1, (theme) => ({ mb: theme.spacing(1) })); +``` diff --git a/packages/merge-sx/package.json b/packages/merge-sx/package.json new file mode 100644 index 00000000..b05e13c2 --- /dev/null +++ b/packages/merge-sx/package.json @@ -0,0 +1,64 @@ +{ + "name": "merge-sx", + "version": "3.3.0-beta.8", + "description": "Combines multiple SxProps for Material UI components.", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "dist", + "*.md" + ], + "sideEffects": false, + "repository": { + "type": "git", + "url": "git+https://github.com/RobinTail/merge-sx.git" + }, + "homepage": "https://github.com/RobinTail/merge-sx", + "author": { + "name": "Anna Bocharova", + "url": "https://robintail.cz" + }, + "scripts": { + "build": "tsdown && attw --pack", + "test": "tsc --noEmit && bun test", + "prepublishOnly": "bun biome check && bun run test && bun run build" + }, + "peerDependencies": { + "@mui/material": "^5.1.0 || ^6.0.0" + }, + "devDependencies": { + "@emotion/styled": "^11.13.5", + "@mui/material": "^6.1.9", + "react": "^18.3.1" + }, + "keywords": [ + "react", + "typescript", + "styled-components", + "reactjs", + "material-ui", + "styling", + "css-in-js", + "jss", + "strict", + "mui", + "merge", + "combine", + "merging", + "muiv5" + ] +} diff --git a/src/index.spec.ts b/packages/merge-sx/src/index.spec.ts similarity index 100% rename from src/index.spec.ts rename to packages/merge-sx/src/index.spec.ts diff --git a/src/index.ts b/packages/merge-sx/src/index.ts similarity index 100% rename from src/index.ts rename to packages/merge-sx/src/index.ts diff --git a/packages/merge-sx/tsconfig.json b/packages/merge-sx/tsconfig.json new file mode 100644 index 00000000..596e2cf7 --- /dev/null +++ b/packages/merge-sx/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"] +} diff --git a/tsdown.config.ts b/packages/merge-sx/tsdown.config.ts similarity index 100% rename from tsdown.config.ts rename to packages/merge-sx/tsdown.config.ts diff --git a/tools/benchmark.bench.ts b/tools/benchmark.bench.ts index ed45f263..befd5c56 100644 --- a/tools/benchmark.bench.ts +++ b/tools/benchmark.bench.ts @@ -1,6 +1,6 @@ import type { SxProps, Theme } from "@mui/system"; import { bench, describe } from "vitest"; -import { mergeSx } from "../src"; +import { mergeSx } from "../packages/merge-sx/src"; describe("Performance", () => { for (const count of [10, 100, 1000, 10000]) { diff --git a/tools/version.ts b/tools/version.ts new file mode 100644 index 00000000..3a0f6476 --- /dev/null +++ b/tools/version.ts @@ -0,0 +1,53 @@ +import { fail } from "node:assert"; +import { $ } from "bun"; +import { type ReleaseType, inc, valid as isValidVersion } from "semver"; + +const path = "./packages/merge-sx/package.json"; +const variants: ReleaseType[] = [ + "major", + "premajor", + "minor", + "preminor", + "patch", + "prepatch", + "prerelease", +]; + +const isValidTarget = (subject: string): subject is ReleaseType => + (variants as string[]).includes(subject); + +const isDirty = async () => (await $`git status --porcelain`.quiet()).text(); + +const target = Bun.argv.pop(); +const json = await Bun.file(path).json(); +const { version: current } = json; + +if (!isValidVersion(current)) + throw new Error(`Invalid current version ${current}`); + +if (await isDirty()) + throw new Error( + "There are uncommitted changes. Commit them before releasing.", + ); + +const desired = isValidVersion(target) + ? target + : target && isValidTarget(target) + ? inc(current, target, "beta", "1") + : fail("invalid target version"); + +if (!desired) throw new Error("Failed to bump"); +console.debug(current, "—>", desired); + +const output = JSON.stringify( + Object.assign(json, { version: desired }), + null, + 2, +); +await Bun.write(path, `${output}\n`); + +await $`git add ${path}`; +await $`git commit -m v${desired}`; +await $`git tag v${desired}`; +await $`git push`; +await $`git push --tags`; diff --git a/tsconfig.json b/tsconfig.json index 1910f7a6..eba46e70 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,4 @@ { "extends": "@tsconfig/bun/tsconfig.json", - "compilerOptions": {}, - "include": ["src"] + "compilerOptions": {} }