Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/silent-wasps-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/styled-react": minor
---

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
18 changes: 18 additions & 0 deletions packages/styled-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ to an alternative styling solution, such as CSS Modules.

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.

## Components List

A machine-readable list of all exported components, utilities, and types is available at build time:

```javascript
import componentsData from '@primer/styled-react/components.json' with {type: 'json'}

console.log(componentsData.components) // Array of component names
console.log(componentsData.utilities) // Array of utility names
console.log(componentsData.types) // Array of type names
```

This JSON file is automatically generated during the build process and includes:

- `components`: All React components exported by the package
- `utilities`: Theme utilities and helper functions
- `types`: TypeScript type definitions

## 🙌 Contributing

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.
3 changes: 2 additions & 1 deletion packages/styled-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"./experimental": {
"types": "./dist/experimental.d.ts",
"default": "./dist/experimental.js"
}
},
"./components.json": "./dist/components.json"
},
"files": [
"README.md",
Expand Down
4 changes: 4 additions & 0 deletions packages/styled-react/script/build
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#!/bin/bash

# Build the package
npx rollup -c

# Generate components.json
./script/generate-components-json
64 changes: 64 additions & 0 deletions packages/styled-react/script/generate-components-json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env node

import fs from 'node:fs/promises'
import {fileURLToPath} from 'node:url'
import path from 'node:path'
import babel from '@babel/core'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const PACKAGE_ROOT = path.resolve(__dirname, '..')
async function generateComponentsJson() {
const components = new Set()
const types = new Set()
const utilities = new Set()

const entrypoints = ['src/index.tsx', 'src/deprecated.tsx', 'src/experimental.tsx']

for (const entrypoint of entrypoints) {
const filename = path.join(PACKAGE_ROOT, entrypoint)
const contents = await fs.readFile(filename, 'utf8')
const ast = babel.parse(contents, {
sourceFileName: filename,
sourceType: 'module',
parserOpts: {
plugins: ['typescript', 'jsx'],
},
})

babel.traverse(ast, {
// export { XYZ }
// export type { XYZ }
ExportNamedDeclaration(path) {
for (const specifier of path.node.specifiers) {
const exported = specifier.exported.name
const exportKind = path.node.exportKind === 'type' ? 'type' : specifier.exportKind

if (exportKind === 'type') {
types.add(exported)
} else if (exported[0] === exported[0].toLowerCase()) {
utilities.add(exported)
} else {
components.add(exported)
}
}
},
})
}

await fs.writeFile(
path.join(PACKAGE_ROOT, 'dist', 'components.json'),
JSON.stringify(
{
components: Array.from(components).sort((a, b) => a.localeCompare(b)),
types: Array.from(types).sort((a, b) => a.localeCompare(b)),
utilities: Array.from(utilities).sort((a, b) => a.localeCompare(b)),
},
null,
2,
),
)
}

generateComponentsJson()
Loading