In this article, we'll explore how to automatically import React components in your codebase, without the need for manual imports in each file. This can save a lot of time and reduce the risk of human error, especially in large projects. We'll be using the Auto Import plugin, which can automatically find and import components based on file patterns
Step 1: Installing Dependencies
First, we need to install the auto-import and fast-glob packages. These packages will allow us to automatically import components into our project.
npm install --save-dev unplugin-auto-import fast-glob
Step 2: Configuring the Auto Import Plugin
To begin with, we need to define the directories where our components are located. In this particular case, the components and layouts directories are used. We also use the omit
key to exclude certain strings in our component names.
For instance, consider a component located at src/components/base/button.jsx
. We want to name this component BaseButton
and not SrcComponentsBaseButton
. To achieve this, we use the omit
key to remove the ./src/components
prefix from the component name.
const directories = [
{
pattern: './src/components/**/*.{tsx,jsx}',
omit: './src/components'
},
{
pattern: './src/layouts/*.{tsx,jsx}',
omit: './src/'
}
]
Then we will use fast-glob
package to search for files that match the patterns specified in the directories array. The objectMode
option is set to true, so that fast-glob
will return an array of objects with information about the matched files.
const entries = fg.sync(
directories.map(x => x.pattern),
{
dot: true,
objectMode: true
}
)
Then we will iterate over the matched files returned by the fast-glob
. For each file, it generates an object with the component name and the import path. The component name is generated by removing the omit string from the file path, and converting the remaining path segments to PascalCase. The import path is generated by replacing the ./src string with @ to indicate that the file is part of the project's source code. Finally, the object is returned as part of an array.
const imports = entries.map(entry => {
const dirOptions = directories.find(dir => minimatch(entry.path, dir.pattern))
const componentName = entry.path
.replace(new RegExp(dirOptions.omit, 'gi'), '')
.split('/')
.filter(Boolean)
.map(pascalCaseWithCapitals)
.join('')
const fromPath = entry.path
.replace(/\.\/src/gi, '@')
return {
[fromPath]: [
[removeExtension(entry.name), removeExtension(componentName)]
]
}
})
Finally, our imports function would look like this:
function getComponentImports() {
// Define an array of directory objects to search for components
const directories = [
{
pattern: './src/components/**/*.{tsx,jsx}',
omit: './src/components'
},
{
pattern: './src/layouts/*.{tsx,jsx}',
omit: './src/'
}
];
// Search for component files in directories and return their file paths as an array
const entries = fg.sync(
directories.map(x => x.pattern),
{
dot: true,
objectMode: true
}
);
// For each component file, extract the component name and fromPath
const imports = entries.map(entry => {
// Find the directory object that matches the current file path
const dirOptions = directories.find(dir => minimatch(entry.path, dir.pattern));
// Extract the component name from the file path using the directory's omit string
const componentName = entry.path
.replace(new RegExp(dirOptions.omit, 'gi'), '')
.split('/')
.filter(Boolean)
.map(pascalCaseWithCapitals)
.join('');
// Replace './src' with '@' in the fromPath
const fromPath = entry.path
.replace(/\.\/src/gi, '@');
// Return an object with the fromPath and an array containing the component name and its variable name
return {
[fromPath]: [
[removeExtension(entry.name), removeExtension(componentName)]
]
}
});
return imports;
}
Step 3: Using unplugin-auto-import
We need to add the plugin to the plugins array in the defineConfig object in vite.config.js. We also need to specify the options for the plugin.
AutoImport({
// Filepath to generate corresponding .d.ts file.
// Defaults to './auto-imports.d.ts' when `typescript` is installed locally.
// Set `false` to disable.
dts: './auto-imports.d.ts',
// Generate corresponding .eslintrc-auto-import.json file.
// eslint globals Docs - https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals
eslintrc: {
enabled: true,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
},
// targets to transform
include: [
/\.[tj]sx?$/ // .ts, .tsx, .js, .jsx
],
// global imports to register
imports: [
// Get component imports
...getComponentImports(),
// Import React
'react',
// Import React Router
'react-router'
]
})
Step 4: We add it to our vite.config.js
export default defineConfig({
plugins: [
AutoImport({
dts: './auto-imports.d.ts',
defaultExportByFilename: false,
eslintrc: {
enabled: true,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
},
include: [
/\.[tj]sx?$/ // .ts, .tsx, .js, .jsx
],
dirs: [
'./src/hooks'
],
imports: [
// Get component imports
...getComponentImports(),
// Import React
'react',
// Import React Router
'react-router'
]
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
});
Hopefully it makes sense and help you guys. Here's the full code
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'url'
import react from '@vitejs/plugin-react'
import AutoImport from 'unplugin-auto-import/vite'
import fg from 'fast-glob'
import { minimatch } from 'minimatch'
import path from 'path'
function pascalCaseWithCapitals (str) {
return str
.split('/')
.map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))
.join('')
}
function removeExtension (str: string) {
return path.basename(str, path.extname(str))
}
function getComponentImports () {
const directories = [
{
pattern: './src/components/**/*.{tsx,jsx}',
omit: './src/components'
},
{
pattern: './src/layouts/*.{tsx,jsx}',
omit: './src/'
}
]
const entries = fg.sync(
directories.map(x => x.pattern),
{
dot: true,
objectMode: true
}
)
return entries.map(entry => {
const dirOptions = directories.find(dir => minimatch(entry.path, dir.pattern))
const componentName = entry.path
.replace(new RegExp(dirOptions.omit, 'gi'), '')
.split('/')
.filter(Boolean)
.map(pascalCaseWithCapitals)
.join('')
const fromPath = entry.path
.replace(/\.\/src/gi, '@')
return {
[fromPath]: [
[removeExtension(entry.name), removeExtension(componentName)]
]
}
})
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
AutoImport({
dts: './auto-imports.d.ts',
defaultExportByFilename: false,
eslintrc: {
enabled: true,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
},
include: [
/\.[tj]sx?$/ // .ts, .tsx, .js, .jsx
],
dirs: [
'./src/hooks'
],
imports: [
// @ts-ignore
...getComponentImports(),
// @ts-ignore
'react',
// @ts-ignore
'react-router'
]
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Top comments (0)