paint-brush
Criando um plugin personalizado para Vite: o guia mais fácilpor@gmakarov
1,012 leituras
1,012 leituras

Criando um plugin personalizado para Vite: o guia mais fácil

por German Makarov9m2024/01/21
Read on Terminal Reader

Muito longo; Para ler

Para o servidor de desenvolvimento, ele usa esbuild com módulos ES nativos, que são suportados por navegadores modernos, e não precisamos agrupar o código em um único arquivo, e isso nos fornece HRM (Hot Module Replacement) rápido. Para o pacote, ele usa rollup.js porque é flexível e possui um grande ecossistema; permite a criação de pacotes de produção altamente otimizados com diferentes formatos de saída. A interface do plugin do Vite é baseada no Rollup, mas com opções e ganchos adicionais para trabalhar com o servidor de desenvolvimento.
featured image - Criando um plugin personalizado para Vite: o guia mais fácil
German Makarov HackerNoon profile picture
0-item

O Vite está se tornando cada vez mais popular entre os desenvolvedores, mas como a comunidade não é tão grande (como no Webpack ), pode ser necessário criar seu próprio plugin personalizado para resolver seus problemas. Neste artigo, discutiremos como criar um plugin para Vite e detalharei meu próprio plugin.

Como funcionam os plug-ins no Vite

Para criar um plugin, é importante saber que o Vite utiliza diferentes sistemas de build para o servidor de desenvolvimento (comando vite ) e o bundle (comando vite build ).


Para o servidor de desenvolvimento, ele usa esbuild com módulos ES nativos, que são suportados por navegadores modernos, e não precisamos agrupar o código em um único arquivo, e isso nos fornece HRM (Hot Module Replacement) rápido.


Para o pacote, ele usa rollup.js porque é flexível e possui um grande ecossistema; permite a criação de pacotes de produção altamente otimizados com diferentes formatos de saída.


A interface do plugin do Vite é baseada no Rollup, mas com opções e ganchos adicionais para trabalhar com o servidor de desenvolvimento.

Criando um plugin básico

Ao criar um plug-in, você pode incorporá-lo em seu vite.config.js . Não há necessidade de criar um novo pacote para isso. Depois de perceber que um plugin foi útil em seus projetos, considere compartilhá-lo com a comunidade e contribuir com o ecossistema Vite.


Além disso, como rollup.js tem uma comunidade e um ecossistema maiores, você pode considerar a criação de um plugin para rollup.js, e ele funcionará perfeitamente no Vite. Portanto, se a funcionalidade do seu plugin funcionar apenas para o pacote, você pode usar o plugin rollup.js em vez do Vite, e os usuários podem usar o plugin rollup em seus projetos Vite sem problemas.


Se você criar um plug-in para rollup, abrangerá mais usuários que usam apenas rollup.js. Se o seu plugin afetar o servidor de desenvolvimento, você deve usar o plugin Vite.


Vamos começar a criar o plugin diretamente em vite.config.ts :


 // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });


Neste exemplo, criei um plugin chamado myPlugin que imprime a configuração do Vite assim que for resolvido no console em ambos os estágios: dev server e bundle. Se eu quiser imprimir a configuração apenas no modo de servidor de desenvolvimento, devo adicionar apply: 'serve' e apply: 'build' para pacote.


 // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', apply: 'serve', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });


Além disso, posso retornar uma série de plugins; é útil para separar funcionalidades do servidor de desenvolvimento e do pacote:


 // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin[] { return [ { name: 'my-plugin:serve', apply: 'serve', configResolved(config) { console.log('dev server:', config); }, }, { name: 'my-plugin:build', apply: 'build', configResolved(config) { console.log('bundle:', config); }, }, ]; } export default defineConfig({ plugins: [ myPlugin(), ], });


E é basicamente isso; você pode facilmente adicionar pequenos plug-ins à configuração do Vite. E se um plugin ficar muito grande, prefiro movê-lo para outro arquivo ou até mesmo criar um pacote.


Se você precisar de algo mais complexo, há muitos ganchos úteis que você pode explorar na documentação do Vite. Mas, como exemplo, vamos detalhar meu próprio plugin abaixo.

Quebrando um plugin real

Então, eu tenho um plugin para criar sprites SVG baseados em arquivos de ícones - vite-plugin-svg-spritemap .

O objetivo é pegar todos os ícones .svg na pasta src/icons e coletar seu conteúdo em um único arquivo .svg chamado SVG sprite. Vamos começar pelo estágio de pacote:


 import { Plugin, ResolvedConfig } from 'vite'; import path from 'path'; import fs from 'fs-extra'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: 'my-plugin:build', apply: 'build', async configResolved(_config) { config = _config; }, writeBundle() { const sprite = getSpriteContent({ pattern: 'src/icons/*.svg' }); const filePath = path.resolve(config.root, config.build.outDir, 'sprite.svg'); fs.ensureFileSync(filePath); fs.writeFileSync(filePath, sprite); }, }; }


  1. Hook configResolved nos permite obter a configuração quando for resolvido para usá-la nos próximos ganchos;


  2. O gancho writeBundle é chamado após a conclusão do processo de empacotamento e, aqui, criarei o arquivo sprite.svg ;


  3. A função getSpriteContent retorna uma string de sprites SVG preparados com base no padrão src/icons/*.svg . Não vou me aprofundar nisso; você pode conferir meu outro artigo explicando todo o processo de geração de sprites SVG ;


  4. Em seguida, eu crio o caminho absoluto para sprite.svg para colocar o conteúdo do sprite SVG com path.resolve() , certifico-me de que o arquivo existe (ou crio um) com fs.ensureFileSync ., e gravo o conteúdo do sprite SVG nele .


Agora, a parte mais interessante – o estágio do servidor de desenvolvimento. Não posso usar writeBundle aqui e não posso hospedar arquivos quando o servidor de desenvolvimento está em execução, então precisamos usar o middleware do servidor para capturar a solicitação para sprite.svg .


 import { Plugin, ResolvedConfig } from 'vite'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { // (1) return () => { server.middlewares.use(async (req, res, next) => { // (2) if (req.url !== '/sprite.svg') { return next(); // (3) } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { // (4) 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }


  1. configureServer é um gancho para configurar o servidor de desenvolvimento. Ele é acionado antes que o middleware interno do Vite seja instalado; no meu caso, preciso adicionar middleware customizado após o middleware interno, então retorno uma função;


  2. Para adicionar middleware personalizado para capturar todas as solicitações ao servidor de desenvolvimento, eu uso server.middlewares.use() . Preciso dele para detectar solicitações com a URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg) , para poder emular o comportamento do arquivo;


  3. Se a URL da solicitação não for /sprite.svg - pule para o próximo middleware (ou seja, passe o controle para o próximo manipulador na cadeia);


  4. Para preparar o conteúdo do arquivo, coloco o resultado de getSpriteContent na variável sprite e envio como resposta com cabeçalhos configurados (tipo de conteúdo e status HTTP 200).


Como resultado, simulei o comportamento do arquivo.


Mas se os arquivos em src/icons forem alterados, excluídos ou adicionados, devemos reiniciar o servidor para gerar novo conteúdo de sprite via getSpriteContent ; para isso, usarei a biblioteca de observação de arquivos - chokidar . Vamos adicionar manipuladores chokidar ao código:


 import { Plugin, ResolvedConfig } from 'vite'; import chokidar from 'chokidar'; function myPlugin(): Plugin { let config: ResolvedConfig; let watcher: chokidar.FSWatcher; // Defined variable for chokidar instance. return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { function reloadPage() { // Function that sends a signal to reload the server. server.ws.send({ type: 'full-reload', path: '*' }); } watcher = chokidar .watch('src/icons/*.svg', { // Watch src/icons/*.svg cwd: config.root, // Define project root path ignoreInitial: true, // Don't trigger chokidar on instantiation. }) .on('add', reloadPage) // Add listeners to add, modify, delete. .on('change', reloadPage) .on('unlink', reloadPage); return () => { server.middlewares.use(async (req, res, next) => { if (req.url !== '/sprite.svg') { return next(); } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }


Como você pode ver, a API de criação de plugins não é muito complicada. Você só precisa encontrar ganchos do Vite ou Rollup que se adaptem às suas tarefas. No meu exemplo, estou usando writeBundle do Rollup.js (como eu disse, ele é usado para gerar o pacote) e configureServer do Vite porque Rollup.js não tem suporte nativo ao servidor de desenvolvimento.


No caso do writeBundle foi muito simples, pegamos o conteúdo do sprite SVG e colocamos em um arquivo. No caso do servidor de desenvolvimento, fiquei confuso porque não pude fazer o mesmo; Eu olhei para plug-ins de outros autores e todos eles fazem a mesma coisa.


Então, eu uso configureServer e, por meio do argumento server , adiciono middleware que aciona todas as solicitações ao servidor de desenvolvimento, interceptando a solicitação sprite.svg .

Usando ganchos Vite

Como mencionei antes, para criar plugins mais úteis, você precisa explorar os ganchos. Eles são explicados em detalhes na documentação:

https://vitejs.dev/guide/api-plugin#universal-hooks

https://vitejs.dev/guide/api-plugin#vite-specific-hooks

Como nomear um plug-in

Em termos de nomenclatura, o Vite possui algumas convenções para plugins, então é melhor você conferir antes de terminar. Aqui estão alguns pontos-chave:


  • Os plug-ins Vite devem ter um nome exclusivo com um prefixo vite-plugin- ;


  • Inclua a palavra-chave vite-plugin em package.json;


  • Inclua uma seção na documentação do plugin explicando por que ele é um plugin somente do Vite (por exemplo, ele usa ganchos de plugin específicos do Vite);


  • Se o seu plugin funcionar apenas para uma estrutura específica, inclua seu nome como parte do prefixo ( vite-plugin-vue- , vite-plugin-react- , vite-plugin-svelte- ).

Como publicar e compartilhar seu plugin

Se você decidir publicar seu plugin no NPM, recomendo porque compartilhar conhecimento e expertise é um princípio fundamental da comunidade de TI, fomentando o crescimento coletivo. Para saber como publicar e manter seu pacote, confira meu guia → A maneira mais fácil de criar um pacote NPM .


Eu também recomendo enviar seu plugin para a lista da comunidade do vite - awesome-vite . Muitas pessoas procuram os plugins mais adequados por lá, e seria uma ótima oportunidade de contribuir com o ecossistema Vite! O processo de envio do seu plugin é simples - apenas certifique-se de cumprir os termos e criar uma solicitação pull. Você pode encontrar a lista de termos aqui .


No geral, ele precisa ser específico para Vite (não rollup), código aberto e ter boa documentação. Boa sorte com seus plug-ins!