Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ lint-staged.config.js
jest.config.js
jest.config.react18.js
babel.config.js
codemods/__testfixtures__/
plopfile.mjs
release.config.js
coverage/
Expand Down
62 changes: 62 additions & 0 deletions codemods/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Reactist Text codemod

Migrates the breaking Text, Heading, and Display APIs to the consolidated Text component and its
named variants.

## Run

```bash
npx jscodeshift@17.3.0 \
--transform ./node_modules/@doist/reactist/codemods/text-variants.ts \
--extensions js,jsx,ts,tsx \
--parser tsx \
src
```

Run Prettier and the application's type-check after the transform.

## Mappings

| Legacy Text size | Regular or omitted | Semibold | Bold |
| ---------------- | ------------------ | ----------- | ----------- |
| subtitle | subheader-2 | subheader-1 | subheader-1 |
| body or omitted | body-3 | body-2 | body-1 |
| copy | callout-2 | callout-1 | callout-1 |
| caption | caption-3 | caption-2 | caption-1 |

Bold subtitle and copy text use the nearest named variant and change from 700 to 600 weight. Other
Text mappings preserve size and weight.

| Legacy Heading metrics | Text variant |
| ---------------------- | ------------ |
| 32px/700 | header-1 |
| 24px/700 | header-2 |
| 20px/700 | header-3 |
| 16px/700 or 16px/600 | subheader-1 |
| 16px/400 | subheader-2 |
| 14px/700 | body-1 |
| 14px/600 | body-2 |
| 14px/400 | body-3 |
| 12px/700 | caption-1 |
| 12px/600 | caption-2 |
| 12px/400 | caption-3 |

The 24px/700 mapping changes to 26px/700. The 16px/700 mapping changes to 16px/600. Other Heading
mappings preserve size and weight. The transform preserves the original semantic heading level
with `render={<hN />}` when the variant does not render that element by default.

Existing named Heading variants map directly from `heading-1` through `heading-4` to `header-1`
through `header-4`. Display variants keep their `display-1` through `display-5` names. Heading and
Display imports are rebound to Text while their local names stay unchanged.

## Manual migrations

The transform changes only the documented static mappings. Unsupported size/weight combinations,
dynamic expressions, duplicate props, and prop spreads remain unchanged. Each unresolved use
receives a TODO(reactist-codemod) comment, and the command prints its file and line. Direct Heading
JSX uses still migrate when the same import has indirect references. The indirect statements
receive TODOs for manual migration. Namespace references, removed Heading and Display types, and
custom component values passed to `as` also receive TODOs.

Resolve every TODO before upgrading to the new Reactist major version. Do not mechanically choose
an undocumented nearest variant: confirm the intended visual hierarchy with design.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react'

import {
/* display import */ Display,
Display as Hero,
/* heading import */ Heading,
Heading as Title,
Text,
} from '@doist/reactist'

export function ConsolidatedComponents() {
return (
<>
<Heading level={1} variant="heading-1">
Header 1
</Heading>
<Heading level={2} variant="heading-2">
Header 2
</Heading>
<Heading level="3" variant="heading-3">
Header 3
</Heading>
<Heading level={6} variant="heading-4">
Header 4
</Heading>
<Title variant="heading-2" render={<button type="button" />}>
Custom header
</Title>
<Display variant="display-1">Display 1</Display>
<Display variant="display-2">Display 2</Display>
<Display variant="display-3">Display 3</Display>
<Display variant="display-4">Display 4</Display>
<Hero variant="display-5" render={<h1 />}>
Display 5
</Hero>
<Text variant="body-1">Body</Text>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react'

import {
/* display import */ Text as Display,
Text as Hero,
/* heading import */ Text as Heading,
Text as Title,
Text,
} from '@doist/reactist'

export function ConsolidatedComponents() {
return (
<>
<Heading variant="header-1">Header 1</Heading>
<Heading variant="header-2">Header 2</Heading>
<Heading variant="header-3">Header 3</Heading>
<Heading variant="header-4" render={<h6 />}>
Header 4
</Heading>
<Title variant="header-2" render={<button type="button" />}>
Custom header
</Title>
<Display variant="display-1">Display 1</Display>
<Display variant="display-2">Display 2</Display>
<Display variant="display-3">Display 3</Display>
<Display variant="display-4">Display 4</Display>
<Hero variant="display-5" render={<h1 />}>
Display 5
</Hero>
<Text variant="body-1">Body</Text>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Heading, Heading as Title } from '@doist/reactist'

type Labels = {
Heading: string
Title: string
}

const labels: Labels = {
Heading: 'Heading',
Title: 'Title',
}

const memberLabels = [labels.Heading, labels.Title]
const { Heading: headingLabel, Title: titleLabel } = labels
const UI = { Heading: 'div', Title: 'span' }

export function HeadingNameKeys() {
return (
<>
<Heading level={1} size="largest">
{headingLabel}
</Heading>
<Title variant="heading-2">{titleLabel}</Title>
<UI.Heading />
<UI.Title />
<div Heading="Heading" Title="Title" />
{memberLabels.join(', ')}
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Text as Heading, Text as Title } from '@doist/reactist'

type Labels = {
Heading: string
Title: string
}

const labels: Labels = {
Heading: 'Heading',
Title: 'Title',
}

const memberLabels = [labels.Heading, labels.Title]
const { Heading: headingLabel, Title: titleLabel } = labels
const UI = { Heading: 'div', Title: 'span' }

export function HeadingNameKeys() {
return (
<>
<Heading variant="header-1">{headingLabel}</Heading>
<Title variant="header-2" render={<h1 />}>
{titleLabel}
</Title>
<UI.Heading />
<UI.Title />
<div Heading="Heading" Title="Title" />
{memberLabels.join(', ')}
</>
)
}
53 changes: 53 additions & 0 deletions codemods/__testfixtures__/text-variants-heading.input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as React from 'react'

import { Heading, Heading as Title } from '@doist/reactist'

export function ExactHeadings() {
return (
<>
<Heading level={1} size="largest">
Large
</Heading>
<Heading level={1}>Current default</Heading>
<Title level={2} size="larger">
Visual 20
</Title>
<Heading level={4} size="largest" weight="regular">
Visual 20
</Heading>
<Heading level={1} size="larger">
Header 2 rendered as h1
</Heading>
<Heading level={2} size="largest">
Header 2 rendered as h2
</Heading>
<Heading level={2}>Subheader 1 from bold</Heading>
<Heading level={3} size="larger" weight="medium">
Subheader 1 from medium
</Heading>
<Heading level={2} weight="light">
Subheader 2
</Heading>
<Heading level={3}>Body 1</Heading>
<Heading level={4} weight="medium">
Body 2
</Heading>
<Heading level={5}>Body 1 rendered as h5</Heading>
<Heading level={6} size="larger">
Subheader 1 rendered as h6
</Heading>
<Heading level={2} size="smaller" weight="light">
Body 3
</Heading>
<Heading level={3} size="smaller">
Caption 1
</Heading>
<Heading level={3} size="smaller" weight="medium">
Caption 2
</Heading>
<Heading level={3} size="smaller" weight="light">
Caption 3
</Heading>
</>
)
}
57 changes: 57 additions & 0 deletions codemods/__testfixtures__/text-variants-heading.output.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as React from 'react'

import { Text as Heading, Text as Title } from '@doist/reactist'

export function ExactHeadings() {
return (
<>
<Heading variant="header-1">Large</Heading>
<Heading variant="header-3" render={<h1 />}>
Current default
</Heading>
<Title variant="header-3" render={<h2 />}>
Visual 20
</Title>
<Heading variant="header-3" render={<h4 />}>
Visual 20
</Heading>
<Heading variant="header-2" render={<h1 />}>
Header 2 rendered as h1
</Heading>
<Heading variant="header-2">Header 2 rendered as h2</Heading>
<Heading variant="subheader-1" render={<h2 />}>
Subheader 1 from bold
</Heading>
<Heading variant="subheader-1" render={<h3 />}>
Subheader 1 from medium
</Heading>
<Heading variant="subheader-2" render={<h2 />}>
Subheader 2
</Heading>
<Heading variant="body-1" render={<h3 />}>
Body 1
</Heading>
<Heading variant="body-2" render={<h4 />}>
Body 2
</Heading>
<Heading variant="body-1" render={<h5 />}>
Body 1 rendered as h5
</Heading>
<Heading variant="subheader-1" render={<h6 />}>
Subheader 1 rendered as h6
</Heading>
<Heading variant="body-3" render={<h2 />}>
Body 3
</Heading>
<Heading variant="caption-1" render={<h3 />}>
Caption 1
</Heading>
<Heading variant="caption-2" render={<h3 />}>
Caption 2
</Heading>
<Heading variant="caption-3" render={<h3 />}>
Caption 3
</Heading>
</>
)
}
14 changes: 14 additions & 0 deletions codemods/__testfixtures__/text-variants-idempotence.input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Text as Heading } from '@doist/reactist'

export function HeadingVariants() {
return (
<>
<Heading variant="header-1" render={<h1 />}>
Page title
</Heading>
<Heading variant="header-3" render={<h4 />}>
Prominent subsection
</Heading>
</>
)
}
14 changes: 14 additions & 0 deletions codemods/__testfixtures__/text-variants-idempotence.output.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Text as Heading } from '@doist/reactist'

export function HeadingVariants() {
return (
<>
<Heading variant="header-1" render={<h1 />}>
Page title
</Heading>
<Heading variant="header-3" render={<h4 />}>
Prominent subsection
</Heading>
</>
)
}
21 changes: 21 additions & 0 deletions codemods/__testfixtures__/text-variants-indirect-heading.input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react'

import { Heading, Text } from '@doist/reactist'

const Title = Heading
const title = React.createElement(Heading, { level: 1, size: 'largest' }, 'Created title')

export function IndirectHeading() {
return (
<>
<Heading level={1} size="largest">
Direct title
</Heading>
<Title level={1} size="largest">
Aliased title
</Title>
{title}
<Text>Body</Text>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react'

import { Text as Heading, Text } from '@doist/reactist'

/* TODO(reactist-codemod): indirect Heading reference */
const Title = Heading
/* TODO(reactist-codemod): indirect Heading reference */
const title = React.createElement(Heading, { level: 1, size: 'largest' }, 'Created title')

export function IndirectHeading() {
return (
<>
<Heading variant="header-1">Direct title</Heading>
<Title level={1} size="largest">
Aliased title
</Title>
{title}
<Text>Body</Text>
</>
)
}
Loading
Loading