Skip to content

Commit 156903c

Browse files
jonrohanjoshblack
andauthored
feat(styled-react): Add an automatic generation of a components.json file to be used in the primer-react/use-styled-react-import eslint plugin (#6848)
Co-authored-by: Josh Black <joshblack@users.noreply.github.com>
1 parent 697d6e0 commit 156903c

File tree

5 files changed

+93
-1
lines changed

5 files changed

+93
-1
lines changed

.changeset/silent-wasps-warn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/styled-react": minor
3+
---
4+
5+
feat(styled-react): Add an automatic generation of a components.json file to be used in the `primer-react/use-styled-react-import` eslint plugin

packages/styled-react/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ to an alternative styling solution, such as CSS Modules.
2828

2929
The documentation for `@primer/react` lives at [primer.style](https://primer.style). There, you'll find detailed documentation on getting started, all of the components, our theme, our principles, and more.
3030

31+
## Components List
32+
33+
A machine-readable list of all exported components, utilities, and types is available at build time:
34+
35+
```javascript
36+
import componentsData from '@primer/styled-react/components.json' with {type: 'json'}
37+
38+
console.log(componentsData.components) // Array of component names
39+
console.log(componentsData.utilities) // Array of utility names
40+
console.log(componentsData.types) // Array of type names
41+
```
42+
43+
This JSON file is automatically generated during the build process and includes:
44+
45+
- `components`: All React components exported by the package
46+
- `utilities`: Theme utilities and helper functions
47+
- `types`: TypeScript type definitions
48+
3149
## 🙌 Contributing
3250

3351
We love collaborating with folks inside and outside of GitHub and welcome contributions! If you're interested, check out our [contributing docs](contributor-docs/CONTRIBUTING.md) for more info on how to get started.

packages/styled-react/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"./experimental": {
1515
"types": "./dist/experimental.d.ts",
1616
"default": "./dist/experimental.js"
17-
}
17+
},
18+
"./components.json": "./dist/components.json"
1819
},
1920
"files": [
2021
"README.md",

packages/styled-react/script/build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
#!/bin/bash
22

3+
# Build the package
34
npx rollup -c
5+
6+
# Generate components.json
7+
./script/generate-components-json
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env node
2+
3+
import fs from 'node:fs/promises'
4+
import {fileURLToPath} from 'node:url'
5+
import path from 'node:path'
6+
import babel from '@babel/core'
7+
8+
const __filename = fileURLToPath(import.meta.url)
9+
const __dirname = path.dirname(__filename)
10+
11+
const PACKAGE_ROOT = path.resolve(__dirname, '..')
12+
async function generateComponentsJson() {
13+
const components = new Set()
14+
const types = new Set()
15+
const utilities = new Set()
16+
17+
const entrypoints = ['src/index.tsx', 'src/deprecated.tsx', 'src/experimental.tsx']
18+
19+
for (const entrypoint of entrypoints) {
20+
const filename = path.join(PACKAGE_ROOT, entrypoint)
21+
const contents = await fs.readFile(filename, 'utf8')
22+
const ast = babel.parse(contents, {
23+
sourceFileName: filename,
24+
sourceType: 'module',
25+
parserOpts: {
26+
plugins: ['typescript', 'jsx'],
27+
},
28+
})
29+
30+
babel.traverse(ast, {
31+
// export { XYZ }
32+
// export type { XYZ }
33+
ExportNamedDeclaration(path) {
34+
for (const specifier of path.node.specifiers) {
35+
const exported = specifier.exported.name
36+
const exportKind = path.node.exportKind === 'type' ? 'type' : specifier.exportKind
37+
38+
if (exportKind === 'type') {
39+
types.add(exported)
40+
} else if (exported[0] === exported[0].toLowerCase()) {
41+
utilities.add(exported)
42+
} else {
43+
components.add(exported)
44+
}
45+
}
46+
},
47+
})
48+
}
49+
50+
await fs.writeFile(
51+
path.join(PACKAGE_ROOT, 'dist', 'components.json'),
52+
JSON.stringify(
53+
{
54+
components: Array.from(components).sort((a, b) => a.localeCompare(b)),
55+
types: Array.from(types).sort((a, b) => a.localeCompare(b)),
56+
utilities: Array.from(utilities).sort((a, b) => a.localeCompare(b)),
57+
},
58+
null,
59+
2,
60+
),
61+
)
62+
}
63+
64+
generateComponentsJson()

0 commit comments

Comments
 (0)