@awarebydefault/display-case 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/display-case.prompt.md +64 -0
- package/docs/ai-agents.md +126 -0
- package/docs/cli.md +99 -0
- package/docs/configuration.md +410 -0
- package/docs/documentation-panel.md +50 -0
- package/docs/examples/README.md +14 -0
- package/docs/examples/multi-variant.case.tsx +30 -0
- package/docs/examples/plain.case.tsx +22 -0
- package/docs/examples/tweak-control.placard.md +80 -0
- package/docs/examples/tweaks.case.tsx +39 -0
- package/docs/hierarchy.md +59 -0
- package/docs/quick-start.md +78 -0
- package/docs/style-engines.md +180 -0
- package/docs/testing.md +245 -0
- package/docs/theming.md +97 -0
- package/docs/tweaks.md +75 -0
- package/docs/writing-cases.md +144 -0
- package/docs/writing-placard-docs.md +194 -0
- package/package.json +113 -0
- package/skills/display-case-author-case/README.md +20 -0
- package/skills/display-case-author-case/SKILL.md +40 -0
- package/skills/display-case-author-placard-doc/README.md +24 -0
- package/skills/display-case-author-placard-doc/SKILL.md +65 -0
- package/skills/display-case-review/README.md +19 -0
- package/skills/display-case-review/SKILL.md +30 -0
- package/skills/display-case-snapshot/README.md +20 -0
- package/skills/display-case-snapshot/SKILL.md +29 -0
- package/src/checks/a11y-scanner.test.ts +240 -0
- package/src/checks/a11y-scanner.ts +410 -0
- package/src/checks/check-text.test.ts +53 -0
- package/src/checks/check-text.ts +78 -0
- package/src/checks/check.test.ts +194 -0
- package/src/checks/check.ts +473 -0
- package/src/checks/providers/pixelmatch-diff.test.ts +79 -0
- package/src/checks/providers/pixelmatch-diff.ts +30 -0
- package/src/checks/providers/playwright-driver.ts +104 -0
- package/src/checks/ssr-check.test.ts +73 -0
- package/src/checks/ssr-check.ts +96 -0
- package/src/checks/structure-check.cross-package.test.ts +165 -0
- package/src/checks/structure-check.test.ts +651 -0
- package/src/checks/structure-check.ts +988 -0
- package/src/checks/tokens-check.test.ts +159 -0
- package/src/checks/tokens-check.ts +162 -0
- package/src/cli.ts +218 -0
- package/src/commands/agents.test.ts +24 -0
- package/src/commands/agents.ts +28 -0
- package/src/commands/init-run.test.ts +123 -0
- package/src/commands/init.test.ts +63 -0
- package/src/commands/init.ts +412 -0
- package/src/commands/publish.test.ts +210 -0
- package/src/commands/publish.ts +292 -0
- package/src/core/affected.test.ts +99 -0
- package/src/core/affected.ts +144 -0
- package/src/core/catalog.test.ts +152 -0
- package/src/core/catalog.ts +92 -0
- package/src/core/discovery.test.ts +184 -0
- package/src/core/discovery.ts +250 -0
- package/src/core/manifest.ts +41 -0
- package/src/core/mdx-lite/__fixtures__/box-stub.tsx +7 -0
- package/src/core/mdx-lite/index.ts +393 -0
- package/src/core/mdx-lite/mdx-lite.test.ts +345 -0
- package/src/core/mdx-plugin.test.ts +60 -0
- package/src/core/mdx-plugin.ts +30 -0
- package/src/flow-step.test-d.ts +39 -0
- package/src/index.test.ts +100 -0
- package/src/index.ts +564 -0
- package/src/render/collect-styles.emotion.test.tsx +114 -0
- package/src/render/collect-styles.test.tsx +72 -0
- package/src/render/collect-styles.ts +33 -0
- package/src/render/documents.test.ts +184 -0
- package/src/render/documents.ts +88 -0
- package/src/render/render-node.test.tsx +160 -0
- package/src/render/render-node.tsx +133 -0
- package/src/render/ssr-primer.test.tsx +25 -0
- package/src/render/ssr-primer.tsx +54 -0
- package/src/render/ssr-render.test.tsx +142 -0
- package/src/render/ssr-render.tsx +63 -0
- package/src/render/ssr-shell.test.tsx +57 -0
- package/src/render/ssr-shell.tsx +54 -0
- package/src/server/prod-server.ts +237 -0
- package/src/server/server.test.ts +117 -0
- package/src/server/server.ts +1039 -0
- package/src/style-engine.test-d.ts +37 -0
- package/src/testing/test-helpers.ts +27 -0
- package/src/types/pixelmatch.d.ts +12 -0
- package/src/ui/browser-entry.tsx +51 -0
- package/src/ui/chrome.css +485 -0
- package/src/ui/design-system/README.md +88 -0
- package/src/ui/design-system/components/controls/Button.case.tsx +52 -0
- package/src/ui/design-system/components/controls/Button.css +89 -0
- package/src/ui/design-system/components/controls/Button.placard.md +14 -0
- package/src/ui/design-system/components/controls/Button.test.tsx +45 -0
- package/src/ui/design-system/components/controls/Button.tsx +41 -0
- package/src/ui/design-system/components/controls/IconButton.case.tsx +52 -0
- package/src/ui/design-system/components/controls/IconButton.css +67 -0
- package/src/ui/design-system/components/controls/IconButton.placard.md +13 -0
- package/src/ui/design-system/components/controls/IconButton.test.tsx +39 -0
- package/src/ui/design-system/components/controls/IconButton.tsx +47 -0
- package/src/ui/design-system/components/controls/Input.case.tsx +50 -0
- package/src/ui/design-system/components/controls/Input.css +52 -0
- package/src/ui/design-system/components/controls/Input.placard.md +12 -0
- package/src/ui/design-system/components/controls/Input.test.tsx +43 -0
- package/src/ui/design-system/components/controls/Input.tsx +45 -0
- package/src/ui/design-system/components/controls/Select.case.tsx +48 -0
- package/src/ui/design-system/components/controls/Select.css +44 -0
- package/src/ui/design-system/components/controls/Select.placard.md +15 -0
- package/src/ui/design-system/components/controls/Select.test.tsx +57 -0
- package/src/ui/design-system/components/controls/Select.tsx +58 -0
- package/src/ui/design-system/components/controls/SelectMenu.case.tsx +100 -0
- package/src/ui/design-system/components/controls/SelectMenu.css +72 -0
- package/src/ui/design-system/components/controls/SelectMenu.placard.md +18 -0
- package/src/ui/design-system/components/controls/SelectMenu.test.tsx +66 -0
- package/src/ui/design-system/components/controls/SelectMenu.tsx +377 -0
- package/src/ui/design-system/components/index.ts +66 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.case.tsx +44 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.tsx +51 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.case.tsx +38 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.tsx +41 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.case.tsx +24 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.placard.md +12 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.tsx +41 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.case.tsx +27 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.placard.md +13 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.tsx +34 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.case.tsx +36 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.placard.md +7 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.tsx +36 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.case.tsx +20 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.placard.md +12 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.tsx +33 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.case.tsx +56 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.placard.md +17 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.tsx +45 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.case.tsx +17 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.placard.md +16 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.tsx +39 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.case.tsx +26 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.tsx +42 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.case.tsx +23 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.placard.md +14 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.tsx +34 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.case.tsx +28 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.tsx +46 -0
- package/src/ui/design-system/components/primer-specimen/index.ts +31 -0
- package/src/ui/design-system/components/primer-specimen/styles.css +476 -0
- package/src/ui/design-system/components/shell/A11yPage.case.tsx +237 -0
- package/src/ui/design-system/components/shell/A11yPage.placard.md +15 -0
- package/src/ui/design-system/components/shell/CaseTemplate.case.tsx +32 -0
- package/src/ui/design-system/components/shell/CaseTemplate.placard.md +5 -0
- package/src/ui/design-system/components/shell/CasesPage.case.tsx +141 -0
- package/src/ui/design-system/components/shell/CasesPage.placard.md +12 -0
- package/src/ui/design-system/components/shell/PrimerPage.case.tsx +22 -0
- package/src/ui/design-system/components/shell/PrimerPage.placard.md +3 -0
- package/src/ui/design-system/components/shell/PrimerTemplate.case.tsx +22 -0
- package/src/ui/design-system/components/shell/PrimerTemplate.placard.md +5 -0
- package/src/ui/design-system/components/shell/ShellView.case.tsx +57 -0
- package/src/ui/design-system/components/shell/ShellView.placard.md +5 -0
- package/src/ui/design-system/components/shell/ShellView.tsx +678 -0
- package/src/ui/design-system/components/shell/shell-fixtures.tsx +727 -0
- package/src/ui/design-system/components/showcase/A11yBadge.case.tsx +46 -0
- package/src/ui/design-system/components/showcase/A11yBadge.css +27 -0
- package/src/ui/design-system/components/showcase/A11yBadge.placard.md +11 -0
- package/src/ui/design-system/components/showcase/A11yBadge.test.tsx +31 -0
- package/src/ui/design-system/components/showcase/A11yBadge.tsx +41 -0
- package/src/ui/design-system/components/showcase/A11yPanel.case.tsx +121 -0
- package/src/ui/design-system/components/showcase/A11yPanel.css +198 -0
- package/src/ui/design-system/components/showcase/A11yPanel.placard.md +19 -0
- package/src/ui/design-system/components/showcase/A11yPanel.test.tsx +81 -0
- package/src/ui/design-system/components/showcase/A11yPanel.tsx +144 -0
- package/src/ui/design-system/components/showcase/Chip.case.tsx +48 -0
- package/src/ui/design-system/components/showcase/Chip.css +51 -0
- package/src/ui/design-system/components/showcase/Chip.placard.md +13 -0
- package/src/ui/design-system/components/showcase/Chip.test.tsx +46 -0
- package/src/ui/design-system/components/showcase/Chip.tsx +54 -0
- package/src/ui/design-system/components/showcase/Eyebrow.case.tsx +30 -0
- package/src/ui/design-system/components/showcase/Eyebrow.css +16 -0
- package/src/ui/design-system/components/showcase/Eyebrow.placard.md +10 -0
- package/src/ui/design-system/components/showcase/Eyebrow.test.tsx +38 -0
- package/src/ui/design-system/components/showcase/Eyebrow.tsx +29 -0
- package/src/ui/design-system/components/showcase/FlowNav.case.tsx +35 -0
- package/src/ui/design-system/components/showcase/FlowNav.css +29 -0
- package/src/ui/design-system/components/showcase/FlowNav.placard.md +13 -0
- package/src/ui/design-system/components/showcase/FlowNav.test.tsx +48 -0
- package/src/ui/design-system/components/showcase/FlowNav.tsx +58 -0
- package/src/ui/design-system/components/showcase/ImpactTag.case.tsx +19 -0
- package/src/ui/design-system/components/showcase/ImpactTag.css +36 -0
- package/src/ui/design-system/components/showcase/ImpactTag.placard.md +14 -0
- package/src/ui/design-system/components/showcase/ImpactTag.test.tsx +40 -0
- package/src/ui/design-system/components/showcase/ImpactTag.tsx +35 -0
- package/src/ui/design-system/components/showcase/NavItem.case.tsx +86 -0
- package/src/ui/design-system/components/showcase/NavItem.css +111 -0
- package/src/ui/design-system/components/showcase/NavItem.placard.md +13 -0
- package/src/ui/design-system/components/showcase/NavItem.test.tsx +65 -0
- package/src/ui/design-system/components/showcase/NavItem.tsx +95 -0
- package/src/ui/design-system/components/showcase/RenderAddress.case.tsx +21 -0
- package/src/ui/design-system/components/showcase/RenderAddress.css +35 -0
- package/src/ui/design-system/components/showcase/RenderAddress.placard.md +7 -0
- package/src/ui/design-system/components/showcase/RenderAddress.test.tsx +26 -0
- package/src/ui/design-system/components/showcase/RenderAddress.tsx +43 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.case.tsx +84 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.css +61 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.placard.md +21 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.test.tsx +81 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.tsx +75 -0
- package/src/ui/design-system/components/showcase/Sidebar.case.tsx +67 -0
- package/src/ui/design-system/components/showcase/Sidebar.css +6 -0
- package/src/ui/design-system/components/showcase/Sidebar.placard.md +14 -0
- package/src/ui/design-system/components/showcase/Sidebar.test.tsx +32 -0
- package/src/ui/design-system/components/showcase/Sidebar.tsx +30 -0
- package/src/ui/design-system/components/showcase/Stage.case.tsx +51 -0
- package/src/ui/design-system/components/showcase/Stage.css +91 -0
- package/src/ui/design-system/components/showcase/Stage.placard.md +15 -0
- package/src/ui/design-system/components/showcase/Stage.test.tsx +84 -0
- package/src/ui/design-system/components/showcase/Stage.tsx +97 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.case.tsx +81 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.css +169 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.placard.md +20 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.tsx +230 -0
- package/src/ui/design-system/components/showcase/Wordmark.case.tsx +42 -0
- package/src/ui/design-system/components/showcase/Wordmark.css +31 -0
- package/src/ui/design-system/components/showcase/Wordmark.placard.md +10 -0
- package/src/ui/design-system/components/showcase/Wordmark.test.tsx +22 -0
- package/src/ui/design-system/components/showcase/Wordmark.tsx +22 -0
- package/src/ui/design-system/primer-specimens/brand.tsx +26 -0
- package/src/ui/design-system/primer-specimens/colors.tsx +83 -0
- package/src/ui/design-system/primer-specimens/components.tsx +308 -0
- package/src/ui/design-system/primer-specimens/foundations.tsx +71 -0
- package/src/ui/design-system/primer-specimens/index.ts +25 -0
- package/src/ui/design-system/primer-specimens/showcase.tsx +68 -0
- package/src/ui/design-system/primer-specimens/spacing.tsx +101 -0
- package/src/ui/design-system/primer-specimens/type.tsx +75 -0
- package/src/ui/design-system/primer.mdx +236 -0
- package/src/ui/design-system/styles.css +14 -0
- package/src/ui/design-system/tokens/colors.css +172 -0
- package/src/ui/design-system/tokens/fonts.css +18 -0
- package/src/ui/design-system/tokens/spacing.css +48 -0
- package/src/ui/design-system/tokens/typography.css +49 -0
- package/src/ui/markdown.test.tsx +54 -0
- package/src/ui/markdown.tsx +19 -0
- package/src/ui/primer-mount.tsx +76 -0
- package/src/ui/primer.css +175 -0
- package/src/ui/primer.tsx +277 -0
- package/src/ui/render-mount.tsx +284 -0
- package/src/ui/shell-core.test.ts +340 -0
- package/src/ui/shell-core.ts +295 -0
- package/src/ui/shell.tsx +60 -0
- package/src/ui/test-ids.ts +53 -0
- package/src/ui/use-shell.ts +1230 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { rm } from 'node:fs/promises'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import type { ComponentType } from 'react'
|
|
5
|
+
import {
|
|
6
|
+
extractBoundNames,
|
|
7
|
+
mdxToTsx,
|
|
8
|
+
scanBraces,
|
|
9
|
+
scanElement,
|
|
10
|
+
scanString,
|
|
11
|
+
segmentMdx,
|
|
12
|
+
} from './index'
|
|
13
|
+
|
|
14
|
+
const FIXTURES = join(import.meta.dir, '__fixtures__')
|
|
15
|
+
|
|
16
|
+
/** Assert the emitted TSX is syntactically valid by transpiling it with Bun's
|
|
17
|
+
* built-in transpiler (no extra deps). Throws on a syntax error. */
|
|
18
|
+
function assertValidTsx(code: string): void {
|
|
19
|
+
const t = new Bun.Transpiler({ loader: 'tsx' })
|
|
20
|
+
t.transformSync(code)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Low-level scanners
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
describe('scanString', () => {
|
|
28
|
+
test('plain strings and escapes', () => {
|
|
29
|
+
expect(scanString(`"ab\\"c"x`, 0)).toBe(7)
|
|
30
|
+
expect(scanString(`'a'`, 0)).toBe(3)
|
|
31
|
+
})
|
|
32
|
+
test('template with nested interpolation containing braces', () => {
|
|
33
|
+
// Build the literal `a${ {x:1} }b` without a raw ${ token in source.
|
|
34
|
+
const s = `\`a${'$'}{ {x:1} }b\``
|
|
35
|
+
expect(scanString(s, 0)).toBe(s.length)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('scanBraces', () => {
|
|
40
|
+
test('nested object expression', () => {
|
|
41
|
+
const s = '{{ a: 1, b: { c: 2 } }}rest'
|
|
42
|
+
expect(s.slice(0, scanBraces(s, 0))).toBe('{{ a: 1, b: { c: 2 } }}')
|
|
43
|
+
})
|
|
44
|
+
test('braces inside strings are ignored', () => {
|
|
45
|
+
const s = `{ "a}b" + '}' }tail`
|
|
46
|
+
expect(s.slice(0, scanBraces(s, 0))).toBe(`{ "a}b" + '}' }`)
|
|
47
|
+
})
|
|
48
|
+
test('block and line comments containing braces', () => {
|
|
49
|
+
const s = '{ /* } */ x } '
|
|
50
|
+
expect(s.slice(0, scanBraces(s, 0))).toBe('{ /* } */ x }')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('scanElement', () => {
|
|
55
|
+
const end = (s: string) => {
|
|
56
|
+
const tags: string[] = []
|
|
57
|
+
return { i: scanElement(s, 0, tags), tags }
|
|
58
|
+
}
|
|
59
|
+
test('self-closing element', () => {
|
|
60
|
+
const r = end('<Foo />after')
|
|
61
|
+
expect('<Foo />after'.slice(0, r.i)).toBe('<Foo />')
|
|
62
|
+
expect(r.tags).toEqual(['Foo'])
|
|
63
|
+
})
|
|
64
|
+
test('attributes with > and < inside strings', () => {
|
|
65
|
+
const s = '<Foo title="a > b" data-x="<3">hi</Foo>!'
|
|
66
|
+
expect(s.slice(0, end(s).i)).toBe('<Foo title="a > b" data-x="<3">hi</Foo>')
|
|
67
|
+
})
|
|
68
|
+
test('object expression prop with nested braces', () => {
|
|
69
|
+
const s = '<W style={{ fontSize: "1rem", x: { y: 2 } }}>t</W>z'
|
|
70
|
+
expect(s.slice(0, end(s).i)).toBe(
|
|
71
|
+
'<W style={{ fontSize: "1rem", x: { y: 2 } }}>t</W>',
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
test('nested same-name elements track depth', () => {
|
|
75
|
+
const s = '<Box><Box><Box/></Box></Box>tail'
|
|
76
|
+
expect(s.slice(0, end(s).i)).toBe('<Box><Box><Box/></Box></Box>')
|
|
77
|
+
})
|
|
78
|
+
test('fragment shorthand', () => {
|
|
79
|
+
const s = '<><A/><B/></>x'
|
|
80
|
+
const r = end(s)
|
|
81
|
+
expect(s.slice(0, r.i)).toBe('<><A/><B/></>')
|
|
82
|
+
expect(r.tags).toEqual(['A', 'B'])
|
|
83
|
+
})
|
|
84
|
+
test('JSX expression comment containing delimiters', () => {
|
|
85
|
+
const s = '<A>{/* < } > */}done</A>!'
|
|
86
|
+
expect(s.slice(0, end(s).i)).toBe('<A>{/* < } > */}done</A>')
|
|
87
|
+
})
|
|
88
|
+
test('spread attribute', () => {
|
|
89
|
+
const s = '<A {...props} b="1"/>z'
|
|
90
|
+
expect(s.slice(0, end(s).i)).toBe('<A {...props} b="1"/>')
|
|
91
|
+
})
|
|
92
|
+
test('multi-line element with blank lines in the body', () => {
|
|
93
|
+
const s = '<Box>\n\n <A/>\n\n <B/>\n\n</Box>\ntrailing'
|
|
94
|
+
const r = end(s)
|
|
95
|
+
expect(s.slice(0, r.i)).toBe('<Box>\n\n <A/>\n\n <B/>\n\n</Box>')
|
|
96
|
+
expect(r.tags).toEqual(['Box', 'A', 'B'])
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('extractBoundNames', () => {
|
|
101
|
+
test('default, named, aliased, namespace, and export bindings', () => {
|
|
102
|
+
const code = [
|
|
103
|
+
"import Def from './a'",
|
|
104
|
+
"import { x, y as z } from './b'",
|
|
105
|
+
"import Mix, { q } from './c'",
|
|
106
|
+
"import * as NS from './d'",
|
|
107
|
+
'export const Local = 1',
|
|
108
|
+
].join('\n')
|
|
109
|
+
const names = extractBoundNames(code)
|
|
110
|
+
expect([...names].sort()).toEqual(
|
|
111
|
+
['Def', 'Local', 'Mix', 'NS', 'q', 'x', 'z'].sort(),
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Segmentation
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
describe('segmentMdx', () => {
|
|
121
|
+
test('classifies imports, prose, and block JSX', () => {
|
|
122
|
+
const blocks = segmentMdx(
|
|
123
|
+
[
|
|
124
|
+
"import { Button } from './c'",
|
|
125
|
+
'',
|
|
126
|
+
'# Title',
|
|
127
|
+
'',
|
|
128
|
+
'Some **bold** prose.',
|
|
129
|
+
'',
|
|
130
|
+
'<Display title="x"><Button/></Display>',
|
|
131
|
+
'',
|
|
132
|
+
'After.',
|
|
133
|
+
].join('\n'),
|
|
134
|
+
)
|
|
135
|
+
expect(blocks.map((b) => b.kind)).toEqual([
|
|
136
|
+
'imports',
|
|
137
|
+
'markdown',
|
|
138
|
+
'jsx',
|
|
139
|
+
'markdown',
|
|
140
|
+
])
|
|
141
|
+
const jsx = blocks[2] as Extract<(typeof blocks)[number], { kind: 'jsx' }>
|
|
142
|
+
expect(jsx.tags).toEqual(['Display', 'Button'])
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('fenced code containing <Tag> stays markdown, not JSX', () => {
|
|
146
|
+
const blocks = segmentMdx(
|
|
147
|
+
['```mdx', '<Display title="x">', ' <Foo/>', '</Display>', '```'].join(
|
|
148
|
+
'\n',
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
expect(blocks).toHaveLength(1)
|
|
152
|
+
expect(blocks[0]?.kind).toBe('markdown')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('multi-line named import is one imports block', () => {
|
|
156
|
+
const blocks = segmentMdx(
|
|
157
|
+
['import {', ' A,', ' B,', '} from "./x"', '', 'prose'].join('\n'),
|
|
158
|
+
)
|
|
159
|
+
expect(blocks[0]?.kind).toBe('imports')
|
|
160
|
+
expect((blocks[0] as { code: string }).code).toContain('B,')
|
|
161
|
+
expect(blocks[1]?.kind).toBe('markdown')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('multi-line JSX block with blank lines inside is one jsx block', () => {
|
|
165
|
+
const blocks = segmentMdx(
|
|
166
|
+
[
|
|
167
|
+
'<Display title="t">',
|
|
168
|
+
'',
|
|
169
|
+
' <A/>',
|
|
170
|
+
'',
|
|
171
|
+
' <B/>',
|
|
172
|
+
'',
|
|
173
|
+
'</Display>',
|
|
174
|
+
].join('\n'),
|
|
175
|
+
)
|
|
176
|
+
expect(blocks).toHaveLength(1)
|
|
177
|
+
expect(blocks[0]?.kind).toBe('jsx')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('prose line starting with lowercase < or autolink is not JSX', () => {
|
|
181
|
+
const blocks = segmentMdx('see <https://example.com> and a < b here')
|
|
182
|
+
expect(blocks).toHaveLength(1)
|
|
183
|
+
expect(blocks[0]?.kind).toBe('markdown')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('degenerate inputs', () => {
|
|
187
|
+
expect(segmentMdx('')).toEqual([])
|
|
188
|
+
expect(segmentMdx(' \n\n ')).toEqual([])
|
|
189
|
+
expect(segmentMdx("import x from './x'").map((b) => b.kind)).toEqual([
|
|
190
|
+
'imports',
|
|
191
|
+
])
|
|
192
|
+
expect(segmentMdx('<A/>').map((b) => b.kind)).toEqual(['jsx'])
|
|
193
|
+
expect(segmentMdx('just prose').map((b) => b.kind)).toEqual(['markdown'])
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test('CRLF newlines are normalized', () => {
|
|
197
|
+
const blocks = segmentMdx('# T\r\n\r\nbody')
|
|
198
|
+
expect(blocks).toHaveLength(1)
|
|
199
|
+
expect((blocks[0] as { text: string }).text).toBe('# T\n\nbody')
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Compilation to TSX
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
describe('mdxToTsx', () => {
|
|
208
|
+
test('emits a valid default-export component', () => {
|
|
209
|
+
const code = mdxToTsx(
|
|
210
|
+
[
|
|
211
|
+
"import { Button } from './c'",
|
|
212
|
+
'',
|
|
213
|
+
'# Hi',
|
|
214
|
+
'',
|
|
215
|
+
'<Display><Button/></Display>',
|
|
216
|
+
].join('\n'),
|
|
217
|
+
)
|
|
218
|
+
assertValidTsx(code)
|
|
219
|
+
expect(code).toContain('export default function MDXContent')
|
|
220
|
+
expect(code).toContain("import { Button } from './c'")
|
|
221
|
+
// Display is not imported → resolved from the components prop.
|
|
222
|
+
expect(code).toContain('const { Display } = __components')
|
|
223
|
+
// Button is imported → NOT destructured from components.
|
|
224
|
+
expect(code).not.toContain('Button } = __components')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('markdown payload is embedded as a JSON-safe string', () => {
|
|
228
|
+
// Prose containing backticks and a dollar-brace must not corrupt the TSX.
|
|
229
|
+
const code = mdxToTsx(
|
|
230
|
+
`a \`code\` and ${'$'}{not interpolated} and a "quote"`,
|
|
231
|
+
)
|
|
232
|
+
assertValidTsx(code)
|
|
233
|
+
expect(code).toContain('<__Md options={__mdOpts}>')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
test('no external components → no empty destructure', () => {
|
|
237
|
+
const code = mdxToTsx("import { A } from './a'\n\n<A/>")
|
|
238
|
+
assertValidTsx(code)
|
|
239
|
+
expect(code).not.toContain('= __components\n const { h1')
|
|
240
|
+
expect(code).not.toContain('{ } = __components')
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// End-to-end SSR round-trips
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
describe('SSR round-trip (synthetic)', () => {
|
|
249
|
+
test('imports resolve, prose renders, JSX specimens render', async () => {
|
|
250
|
+
const src = [
|
|
251
|
+
"import { Box } from './box-stub'",
|
|
252
|
+
'',
|
|
253
|
+
'# Heading',
|
|
254
|
+
'',
|
|
255
|
+
'Some **prose** here.',
|
|
256
|
+
'',
|
|
257
|
+
'<Display title="Demo"><Box>inner</Box></Display>',
|
|
258
|
+
].join('\n')
|
|
259
|
+
const code = mdxToTsx(src, { markdownSpecifier: 'markdown-to-jsx' })
|
|
260
|
+
const tmp = join(FIXTURES, `__rt_${Date.now()}.tsx`)
|
|
261
|
+
await Bun.write(tmp, code)
|
|
262
|
+
try {
|
|
263
|
+
const { renderToStaticMarkup } = await import('react-dom/server')
|
|
264
|
+
const { createElement } = await import('react')
|
|
265
|
+
const mod = (await import(tmp)) as {
|
|
266
|
+
default: ComponentType<{ components?: unknown }>
|
|
267
|
+
}
|
|
268
|
+
const Display = ({
|
|
269
|
+
title,
|
|
270
|
+
children,
|
|
271
|
+
}: {
|
|
272
|
+
title?: string
|
|
273
|
+
children?: unknown
|
|
274
|
+
}) =>
|
|
275
|
+
createElement(
|
|
276
|
+
'section',
|
|
277
|
+
{ 'data-display': '', title },
|
|
278
|
+
children as never,
|
|
279
|
+
)
|
|
280
|
+
const html = renderToStaticMarkup(
|
|
281
|
+
createElement(mod.default, { components: { Display } }) as never,
|
|
282
|
+
)
|
|
283
|
+
expect(html).toContain('<strong>prose</strong>') // prose via markdown-to-jsx
|
|
284
|
+
expect(html).toContain('data-display') // Display resolved from components
|
|
285
|
+
expect(html).toContain('data-box') // imported Box rendered
|
|
286
|
+
expect(html).toContain('inner')
|
|
287
|
+
} finally {
|
|
288
|
+
await rm(tmp, { force: true })
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
describe('SSR round-trip (real primer.mdx)', () => {
|
|
294
|
+
const Primer = join(
|
|
295
|
+
import.meta.dir,
|
|
296
|
+
'..',
|
|
297
|
+
'..',
|
|
298
|
+
'ui',
|
|
299
|
+
'design-system',
|
|
300
|
+
'primer.mdx',
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
test('the real primer segments, counts Display specimens, and emits valid TSX', async () => {
|
|
304
|
+
const source = await Bun.file(Primer).text()
|
|
305
|
+
const blocks = segmentMdx(source)
|
|
306
|
+
const displays = blocks
|
|
307
|
+
.filter((b) => b.kind === 'jsx')
|
|
308
|
+
.filter((b) => (b as { tags: string[] }).tags[0] === 'Display').length
|
|
309
|
+
expect(displays).toBeGreaterThanOrEqual(15)
|
|
310
|
+
// prose exists
|
|
311
|
+
expect(blocks.some((b) => b.kind === 'markdown')).toBe(true)
|
|
312
|
+
assertValidTsx(mdxToTsx(source))
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
test('the real primer renders to HTML under SSR with a stubbed Display', async () => {
|
|
316
|
+
const source = await Bun.file(Primer).text()
|
|
317
|
+
const code = mdxToTsx(source, { markdownSpecifier: 'markdown-to-jsx' })
|
|
318
|
+
// Write next to primer.mdx so its relative imports (./components, etc.) resolve.
|
|
319
|
+
const tmp = join(
|
|
320
|
+
import.meta.dir,
|
|
321
|
+
'..',
|
|
322
|
+
'..',
|
|
323
|
+
'ui',
|
|
324
|
+
'design-system',
|
|
325
|
+
`__rt_primer_${Date.now()}.tsx`,
|
|
326
|
+
)
|
|
327
|
+
await Bun.write(tmp, code)
|
|
328
|
+
try {
|
|
329
|
+
const { renderToStaticMarkup } = await import('react-dom/server')
|
|
330
|
+
const { createElement } = await import('react')
|
|
331
|
+
const mod = (await import(tmp)) as {
|
|
332
|
+
default: ComponentType<{ components?: unknown }>
|
|
333
|
+
}
|
|
334
|
+
const Display = ({ children }: { children?: unknown }) =>
|
|
335
|
+
createElement('section', { 'data-display': '' }, children as never)
|
|
336
|
+
const html = renderToStaticMarkup(
|
|
337
|
+
createElement(mod.default, { components: { Display } }) as never,
|
|
338
|
+
)
|
|
339
|
+
expect(html).toContain('data-display')
|
|
340
|
+
expect(html).toContain('Design System') // a heading from the prose
|
|
341
|
+
} finally {
|
|
342
|
+
await rm(tmp, { force: true })
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { rm } from 'node:fs/promises'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { makeTempDir } from '../testing/test-helpers'
|
|
5
|
+
import { mdxPlugin } from './mdx-plugin'
|
|
6
|
+
|
|
7
|
+
type OnLoadArgs = { path: string }
|
|
8
|
+
type OnLoadResult = { contents: string; loader: string }
|
|
9
|
+
type OnLoadCb = (args: OnLoadArgs) => Promise<OnLoadResult>
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Drive the plugin without a real Bun build: capture the `onLoad` handler it
|
|
13
|
+
* registers, then invoke it against a temp `.mdx` file and inspect the compiled
|
|
14
|
+
* output.
|
|
15
|
+
*/
|
|
16
|
+
function captureOnLoad(): {
|
|
17
|
+
filter: RegExp
|
|
18
|
+
run: OnLoadCb
|
|
19
|
+
} {
|
|
20
|
+
const calls: Array<{ filter: RegExp; cb: OnLoadCb }> = []
|
|
21
|
+
const build = {
|
|
22
|
+
onLoad: (opts: { filter: RegExp }, cb: OnLoadCb) =>
|
|
23
|
+
calls.push({ filter: opts.filter, cb }),
|
|
24
|
+
}
|
|
25
|
+
const plugin = mdxPlugin()
|
|
26
|
+
plugin.setup(build as unknown as Parameters<typeof plugin.setup>[0])
|
|
27
|
+
if (!calls[0]) throw new Error('plugin registered no onLoad handler')
|
|
28
|
+
return { filter: calls[0].filter, run: calls[0].cb }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('mdxPlugin', () => {
|
|
32
|
+
test('registers an onLoad handler that matches .mdx files only', () => {
|
|
33
|
+
const { filter } = captureOnLoad()
|
|
34
|
+
expect(filter.test('doc.mdx')).toBe(true)
|
|
35
|
+
expect(filter.test('doc.tsx')).toBe(false)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('compiles an .mdx document to a TSX module on load', async () => {
|
|
39
|
+
const { run } = captureOnLoad()
|
|
40
|
+
const dir = await makeTempDir()
|
|
41
|
+
try {
|
|
42
|
+
const file = join(dir, 'doc.mdx')
|
|
43
|
+
await Bun.write(
|
|
44
|
+
file,
|
|
45
|
+
"import { Button } from './c'\n\n# Title\n\nSome **bold** text.\n\n<Display><Button/></Display>\n",
|
|
46
|
+
)
|
|
47
|
+
const result = await run({ path: file })
|
|
48
|
+
// mdx-lite emits TSX so Bun handles imports + JSX expression props.
|
|
49
|
+
expect(result.loader).toBe('tsx')
|
|
50
|
+
expect(result.contents).toContain('export default function MDXContent')
|
|
51
|
+
// Author imports pass through; <Display> resolves from the components prop.
|
|
52
|
+
expect(result.contents).toContain("import { Button } from './c'")
|
|
53
|
+
expect(result.contents).toContain('const { Display } = __components')
|
|
54
|
+
// Emitted TSX is syntactically valid.
|
|
55
|
+
new Bun.Transpiler({ loader: 'tsx' }).transformSync(result.contents)
|
|
56
|
+
} finally {
|
|
57
|
+
await rm(dir, { recursive: true, force: true })
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BunPlugin } from 'bun'
|
|
2
|
+
import { mdxToTsx } from './mdx-lite'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bun bundler plugin that compiles `.mdx` to TSX on load, so the Primer entry can
|
|
6
|
+
* `import` an authored `.mdx` document and the components it pulls in.
|
|
7
|
+
*
|
|
8
|
+
* Compilation is done by the in-repo `mdx-lite` compiler (no `@mdx-js/mdx`): it
|
|
9
|
+
* segments the document into imports / prose / block-level JSX and emits a `.tsx`
|
|
10
|
+
* module. The author's `import`s and JSX expression props pass through verbatim
|
|
11
|
+
* and are handled by Bun's own TSX toolchain; prose renders via `markdown-to-jsx`
|
|
12
|
+
* (the same renderer the `.placard.md` DocPanel uses, so the two reading surfaces
|
|
13
|
+
* stay consistent). The compiled default export is `MDXContent({ components })`:
|
|
14
|
+
* capitalized JSX a doc doesn't import (notably `<Display>`) resolves from the
|
|
15
|
+
* `components` prop passed at render time (see `primer-mount`).
|
|
16
|
+
*
|
|
17
|
+
* The `tsx` loader hands the emitted source back to Bun, which strips types and
|
|
18
|
+
* compiles JSX with the project's automatic React runtime.
|
|
19
|
+
*/
|
|
20
|
+
export function mdxPlugin(): BunPlugin {
|
|
21
|
+
return {
|
|
22
|
+
name: 'display-case-mdx',
|
|
23
|
+
setup(build) {
|
|
24
|
+
build.onLoad({ filter: /\.mdx$/ }, async (args) => {
|
|
25
|
+
const source = await Bun.file(args.path).text()
|
|
26
|
+
return { contents: mdxToTsx(source), loader: 'tsx' }
|
|
27
|
+
})
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-level test for `flowStep` inference. Compiled by `tsc` (the `lint:types`
|
|
3
|
+
* check) but never executed — each `@ts-expect-error` asserts a compile error,
|
|
4
|
+
* and a `const x: T =` asserts the inferred type. A regression of the inference
|
|
5
|
+
* fails the typecheck.
|
|
6
|
+
*/
|
|
7
|
+
import { flowStep, tweak } from './index'
|
|
8
|
+
|
|
9
|
+
// A tweaked step: `values` is typed from its own tweaks.
|
|
10
|
+
flowStep({
|
|
11
|
+
tweaks: { error: tweak.boolean(false), code: tweak.text('') },
|
|
12
|
+
render: ({ values }) => {
|
|
13
|
+
const _error: boolean = values.error
|
|
14
|
+
const _code: string = values.code
|
|
15
|
+
void _error
|
|
16
|
+
void _code
|
|
17
|
+
// @ts-expect-error — `nope` is not a declared tweak of this step
|
|
18
|
+
void values.nope
|
|
19
|
+
return null
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// A step with no tweaks: `values` is empty; render need not read it.
|
|
24
|
+
flowStep({
|
|
25
|
+
transitions: ['Done'],
|
|
26
|
+
render: ({ goto }) => {
|
|
27
|
+
goto('Done')
|
|
28
|
+
return null
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// A no-tweaks step that wrongly reads a tweak value.
|
|
33
|
+
flowStep({
|
|
34
|
+
render: ({ values }) => {
|
|
35
|
+
// @ts-expect-error — this step declares no tweaks
|
|
36
|
+
void values.error
|
|
37
|
+
return null
|
|
38
|
+
},
|
|
39
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
defineCases,
|
|
4
|
+
defineConfig,
|
|
5
|
+
defineFlow,
|
|
6
|
+
flowStep,
|
|
7
|
+
HIERARCHY_LEVELS,
|
|
8
|
+
tweak,
|
|
9
|
+
} from './index'
|
|
10
|
+
|
|
11
|
+
describe('tweak builders', () => {
|
|
12
|
+
test('text defaults to an empty string and echoes a provided default', () => {
|
|
13
|
+
expect(tweak.text()).toEqual({ kind: 'text', default: '' })
|
|
14
|
+
expect(tweak.text('hi')).toEqual({ kind: 'text', default: 'hi' })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('boolean defaults to false', () => {
|
|
18
|
+
expect(tweak.boolean()).toEqual({ kind: 'boolean', default: false })
|
|
19
|
+
expect(tweak.boolean(true)).toEqual({ kind: 'boolean', default: true })
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('number defaults to zero', () => {
|
|
23
|
+
expect(tweak.number()).toEqual({ kind: 'number', default: 0 })
|
|
24
|
+
expect(tweak.number(42)).toEqual({ kind: 'number', default: 42 })
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('choice records its options and default', () => {
|
|
28
|
+
expect(tweak.choice(['sm', 'md', 'lg'], 'md')).toEqual({
|
|
29
|
+
kind: 'choice',
|
|
30
|
+
options: ['sm', 'md', 'lg'],
|
|
31
|
+
default: 'md',
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('defineCases', () => {
|
|
37
|
+
test('builds a non-flow module from a name and cases', () => {
|
|
38
|
+
const mod = defineCases('Button', { Default: () => null })
|
|
39
|
+
expect(mod.component).toBe('Button')
|
|
40
|
+
expect(mod.isFlow).toBe(false)
|
|
41
|
+
expect(mod.level).toBeUndefined()
|
|
42
|
+
expect(mod.area).toBeUndefined()
|
|
43
|
+
expect(Object.keys(mod.cases)).toEqual(['Default'])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('carries level and area from meta', () => {
|
|
47
|
+
const mod = defineCases(
|
|
48
|
+
'Button',
|
|
49
|
+
{ Default: () => null },
|
|
50
|
+
{
|
|
51
|
+
level: 'atom',
|
|
52
|
+
area: 'marketing',
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
expect(mod.level).toBe('atom')
|
|
56
|
+
expect(mod.area).toBe('marketing')
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('defineFlow', () => {
|
|
61
|
+
test('builds a flow module pinned at the flow level', () => {
|
|
62
|
+
const mod = defineFlow('Sign In', {
|
|
63
|
+
steps: { Start: flowStep({ render: () => null }) },
|
|
64
|
+
})
|
|
65
|
+
expect(mod.component).toBe('Sign In')
|
|
66
|
+
expect(mod.isFlow).toBe(true)
|
|
67
|
+
expect(mod.level).toBe('flow')
|
|
68
|
+
expect(Object.keys(mod.cases)).toEqual(['Start'])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('carries an area tag from config', () => {
|
|
72
|
+
const mod = defineFlow('Sign In', { steps: {}, area: 'auth' })
|
|
73
|
+
expect(mod.area).toBe('auth')
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('identity helpers', () => {
|
|
78
|
+
test('flowStep returns its argument unchanged', () => {
|
|
79
|
+
const step = { render: () => null }
|
|
80
|
+
expect(flowStep(step)).toBe(step)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('defineConfig returns its argument unchanged', () => {
|
|
84
|
+
const config = { title: 'X', roots: ['**/*.case.tsx'] }
|
|
85
|
+
expect(defineConfig(config)).toBe(config)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('HIERARCHY_LEVELS', () => {
|
|
90
|
+
test('orders the atomic-design levels from atom to flow', () => {
|
|
91
|
+
expect(HIERARCHY_LEVELS).toEqual([
|
|
92
|
+
'atom',
|
|
93
|
+
'molecule',
|
|
94
|
+
'organism',
|
|
95
|
+
'template',
|
|
96
|
+
'page',
|
|
97
|
+
'flow',
|
|
98
|
+
])
|
|
99
|
+
})
|
|
100
|
+
})
|