safe point before auto-import cleanup
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,7 +6,8 @@ coverage
|
||||
.cache
|
||||
.output
|
||||
# .env
|
||||
dist
|
||||
dist/
|
||||
dist-*/
|
||||
.DS_Store
|
||||
.idea
|
||||
.eslintcache
|
||||
@@ -14,4 +15,4 @@ api-generator/typedoc.json
|
||||
**/.DS_Store
|
||||
Dev-documentacao/
|
||||
supabase/
|
||||
evolution-api/
|
||||
evolution-api/
|
||||
|
||||
136
docs/clean-auto-imports.md
Normal file
136
docs/clean-auto-imports.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# clean-auto-imports.js
|
||||
|
||||
Script para remover imports manuais redundantes do projeto, considerando que o projeto usa `unplugin-auto-import` e `unplugin-vue-components` com `PrimeVueResolver`.
|
||||
|
||||
---
|
||||
|
||||
## Por que esse script existe
|
||||
|
||||
O projeto tem `vite.config.mjs` configurado com:
|
||||
|
||||
- **`unplugin-auto-import`** → auto-importa todas as APIs do Vue (`ref`, `computed`, `watch`, `onMounted`, etc.) em qualquer arquivo `.vue` ou `.js`
|
||||
- **`unplugin-vue-components` + `PrimeVueResolver`** → auto-importa componentes do PrimeVue usados nos templates `.vue`
|
||||
|
||||
Com isso, manter os imports manuais desses símbolos é redundante. O script limpa tudo de uma vez.
|
||||
|
||||
---
|
||||
|
||||
## Como usar
|
||||
|
||||
### Dry-run (só lista o que seria alterado, sem escrever nada)
|
||||
|
||||
```bash
|
||||
node scripts/clean-auto-imports.js --dry-run
|
||||
```
|
||||
|
||||
### Aplicar as mudanças
|
||||
|
||||
```bash
|
||||
node scripts/clean-auto-imports.js
|
||||
```
|
||||
|
||||
### Após rodar, verificar se está tudo ok
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## O que o script remove
|
||||
|
||||
### Em arquivos `.vue` e `.js`
|
||||
|
||||
Qualquer símbolo Vue dentro de `import { ... } from 'vue'` que já é auto-importado:
|
||||
|
||||
```js
|
||||
// Antes
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
|
||||
// Depois (linha removida completamente)
|
||||
```
|
||||
|
||||
Lista completa dos símbolos removidos:
|
||||
|
||||
`computed`, `createApp`, `customRef`, `defineAsyncComponent`, `defineComponent`, `effectScope`, `getCurrentInstance`, `getCurrentScope`, `h`, `inject`, `isProxy`, `isReactive`, `isReadonly`, `isRef`, `isShallow`, `markRaw`, `nextTick`, `onActivated`, `onBeforeMount`, `onBeforeUnmount`, `onBeforeUpdate`, `onDeactivated`, `onErrorCaptured`, `onMounted`, `onUnmounted`, `onUpdated`, `provide`, `reactive`, `readonly`, `ref`, `resolveComponent`, `shallowReactive`, `shallowReadonly`, `shallowRef`, `toRaw`, `toRef`, `toRefs`, `toValue`, `triggerRef`, `unref`, `useAttrs`, `useCssModule`, `useCssVars`, `useId`, `useModel`, `useSlots`, `useTemplateRef`, `watch`, `watchEffect`, `watchPostEffect`, `watchSyncEffect`
|
||||
|
||||
Tipos também removidos de `import type { ... } from 'vue'`:
|
||||
|
||||
`Component`, `ComputedRef`, `ComponentPublicInstance`, `DirectiveBinding`, `InjectionKey`, `PropType`, `Ref`, `ShallowRef`, `MaybeRef`, `MaybeRefOrGetter`, `VNode`, `WritableComputedRef`, e outros.
|
||||
|
||||
### Somente em arquivos `.vue`
|
||||
|
||||
Default imports de componentes PrimeVue:
|
||||
|
||||
```js
|
||||
// Antes
|
||||
import Button from 'primevue/button'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Select from 'primevue/select'
|
||||
|
||||
// Depois (linhas removidas)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## O que o script NÃO remove
|
||||
|
||||
### `main.js` é ignorado completamente
|
||||
|
||||
O arquivo de bootstrap do app nunca é tocado.
|
||||
|
||||
### Serviços e plugins do PrimeVue são mantidos
|
||||
|
||||
```js
|
||||
import PrimeVue from 'primevue/config' // mantido
|
||||
import ConfirmationService from 'primevue/confirmationservice' // mantido
|
||||
import ToastService from 'primevue/toastservice' // mantido
|
||||
```
|
||||
|
||||
### Composables do PrimeVue são mantidos
|
||||
|
||||
```js
|
||||
import { useToast } from 'primevue/usetoast' // mantido
|
||||
import { useConfirm } from 'primevue/useconfirm' // mantido
|
||||
```
|
||||
|
||||
### Aliases Vue são mantidos se o original não está na lista
|
||||
|
||||
```js
|
||||
import { ref as vRef } from 'vue' // "ref" está na lista → removido
|
||||
import { defineProps as dp } from 'vue' // "defineProps" não está → mantido
|
||||
```
|
||||
|
||||
### Imports de outros pacotes não são tocados
|
||||
|
||||
```js
|
||||
import { useRouter } from 'vue-router' // mantido (não é do 'vue')
|
||||
import { useAuthStore } from '@/stores' // mantido (local)
|
||||
import { useFeriados } from '@/composables/useFeriados' // mantido
|
||||
```
|
||||
|
||||
### Imports de componentes PrimeVue em arquivos `.js` são mantidos
|
||||
|
||||
O `PrimeVueResolver` só funciona em templates `.vue`. Se um `.js` importa um componente PrimeVue, provavelmente está sendo usado programaticamente — o script não toca esses casos.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Erro "X is not defined" após rodar o script**
|
||||
|
||||
Isso indica que o símbolo estava sendo usado fora do escopo que o auto-import alcança. Basta re-adicionar o import manualmente no arquivo específico.
|
||||
|
||||
**Quero revertir tudo**
|
||||
|
||||
Use o git:
|
||||
|
||||
```bash
|
||||
git checkout src/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quando rodar novamente
|
||||
|
||||
O script é seguro para rodar múltiplas vezes (idempotente). Se novos arquivos forem criados com imports manuais, basta rodar novamente.
|
||||
1096
package-lock.json
generated
1096
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,8 +47,10 @@
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.2.5",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"sass": "^1.55.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-vue-components": "^0.27.3",
|
||||
"vite": "^5.3.1",
|
||||
"vitest": "^4.0.18"
|
||||
|
||||
206
scripts/clean-auto-imports.js
Normal file
206
scripts/clean-auto-imports.js
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* clean-auto-imports.js
|
||||
*
|
||||
* Remove imports manuais redundantes de arquivos .vue e .js,
|
||||
* considerando que o projeto usa:
|
||||
* - unplugin-auto-import → auto-importa APIs do Vue
|
||||
* - unplugin-vue-components + PrimeVueResolver → auto-importa componentes PrimeVue nos templates
|
||||
*
|
||||
* Uso: node scripts/clean-auto-imports.js
|
||||
* Dry-run (sem escrever): node scripts/clean-auto-imports.js --dry-run
|
||||
*/
|
||||
|
||||
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { join, extname, relative, basename } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const isDryRun = process.argv.includes('--dry-run');
|
||||
|
||||
// ─── Lista exata do auto-imports.d.ts gerado pelo unplugin-auto-import ────────
|
||||
const VUE_AUTO_IMPORTS = new Set([
|
||||
'EffectScope', 'computed', 'createApp', 'customRef', 'defineAsyncComponent',
|
||||
'defineComponent', 'effectScope', 'getCurrentInstance', 'getCurrentScope',
|
||||
'getCurrentWatcher', 'h', 'inject', 'isProxy', 'isReactive', 'isReadonly',
|
||||
'isRef', 'isShallow', 'markRaw', 'nextTick', 'onActivated', 'onBeforeMount',
|
||||
'onBeforeUnmount', 'onBeforeUpdate', 'onDeactivated', 'onErrorCaptured',
|
||||
'onMounted', 'onRenderTracked', 'onRenderTriggered', 'onScopeDispose',
|
||||
'onServerPrefetch', 'onUnmounted', 'onUpdated', 'onWatcherCleanup', 'provide',
|
||||
'reactive', 'readonly', 'ref', 'resolveComponent', 'shallowReactive',
|
||||
'shallowReadonly', 'shallowRef', 'toRaw', 'toRef', 'toRefs', 'toValue',
|
||||
'triggerRef', 'unref', 'useAttrs', 'useCssModule', 'useCssVars', 'useId',
|
||||
'useModel', 'useSlots', 'useTemplateRef', 'watch', 'watchEffect',
|
||||
'watchPostEffect', 'watchSyncEffect',
|
||||
// Tipos re-exportados (import type { ... } from 'vue')
|
||||
'Component', 'Slot', 'Slots', 'ComponentPublicInstance', 'ComputedRef',
|
||||
'DirectiveBinding', 'ExtractDefaultPropTypes', 'ExtractPropTypes',
|
||||
'ExtractPublicPropTypes', 'InjectionKey', 'PropType', 'Ref', 'ShallowRef',
|
||||
'MaybeRef', 'MaybeRefOrGetter', 'VNode', 'WritableComputedRef',
|
||||
]);
|
||||
|
||||
// ─── Paths do PrimeVue que NÃO são componentes (serviços, plugins, diretivas) ─
|
||||
const PRIMEVUE_KEEP_PATHS = new Set([
|
||||
'primevue/config',
|
||||
'primevue/confirmationservice',
|
||||
'primevue/toastservice',
|
||||
'primevue/dialogservice',
|
||||
'primevue/useconfirm',
|
||||
'primevue/usetoast',
|
||||
'primevue/usedynamicdialog',
|
||||
'primevue/ripple',
|
||||
'primevue/tooltip',
|
||||
'primevue/focustrap',
|
||||
'primevue/styleclass',
|
||||
'primevue/badgedirective',
|
||||
'primevue/animateonscroll',
|
||||
'primevue/passthrough',
|
||||
'primevue/useStyle',
|
||||
]);
|
||||
|
||||
// ─── Arquivos a ignorar ────────────────────────────────────────────────────────
|
||||
const SKIP_FILES = new Set(['main.js', 'main.ts']);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function getAllFiles(dir) {
|
||||
const results = [];
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (!['node_modules', '.git', 'dist', '.nuxt', 'public'].includes(entry.name)) {
|
||||
results.push(...await getAllFiles(fullPath));
|
||||
}
|
||||
} else {
|
||||
const ext = extname(entry.name);
|
||||
if (['.vue', '.js', '.mjs'].includes(ext) && !SKIP_FILES.has(entry.name)) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove do `import { ... } from 'vue'` (single e multi-line)
|
||||
* os símbolos que já são auto-importados.
|
||||
*/
|
||||
function cleanVueImports(content) {
|
||||
// Captura tanto single-line quanto multi-line: import [type] { ... } from 'vue'
|
||||
const importRegex = /^import\s+(type\s+)?\{([^}]+)\}\s+from\s+['"]vue['"]\s*;?/gm;
|
||||
|
||||
let changed = false;
|
||||
const modified = content.replace(importRegex, (match, typeKeyword, namedImports) => {
|
||||
const symbols = namedImports
|
||||
.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
// Para cada símbolo, verifica se está na lista de auto-imports
|
||||
// Suporta aliases: "ref as vRef" → extrai "ref"
|
||||
const keep = symbols.filter(sym => {
|
||||
const name = sym.split(/\s+as\s+/)[0].trim();
|
||||
return !VUE_AUTO_IMPORTS.has(name);
|
||||
});
|
||||
|
||||
if (keep.length === symbols.length) return match; // nada a remover
|
||||
|
||||
changed = true;
|
||||
if (keep.length === 0) return ''; // remove a linha toda
|
||||
|
||||
const isType = typeKeyword ? 'type ' : '';
|
||||
return `import ${isType}{ ${keep.join(', ')} } from 'vue'`;
|
||||
});
|
||||
|
||||
return { content: modified, changed };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove `import ComponentName from 'primevue/...'` em arquivos .vue.
|
||||
* Mantém serviços/plugins listados em PRIMEVUE_KEEP_PATHS.
|
||||
* Mantém named imports ({ useToast }) pois esses não são resolvidos pelo resolver de componentes.
|
||||
*/
|
||||
function cleanPrimeVueImports(content) {
|
||||
// Apenas default imports de componentes (PascalCase)
|
||||
const importRegex = /^import\s+([A-Z]\w*)\s+from\s+['"]primevue\/([^'"]+)['"]\s*;?\n?/gm;
|
||||
|
||||
let changed = false;
|
||||
const modified = content.replace(importRegex, (match, componentName, path) => {
|
||||
const fullPath = `primevue/${path}`;
|
||||
if (PRIMEVUE_KEEP_PATHS.has(fullPath)) return match;
|
||||
|
||||
changed = true;
|
||||
return '';
|
||||
});
|
||||
|
||||
return { content: modified, changed };
|
||||
}
|
||||
|
||||
/** Remove linhas em branco consecutivas excessivas (máx 2 \n seguidos) */
|
||||
function collapseBlankLines(content) {
|
||||
return content.replace(/\n{3,}/g, '\n\n');
|
||||
}
|
||||
|
||||
async function processFile(filePath) {
|
||||
const ext = extname(filePath);
|
||||
const original = await readFile(filePath, 'utf-8');
|
||||
|
||||
let modified = original;
|
||||
let changed = false;
|
||||
|
||||
// 1. Vue auto-imports → .vue e .js
|
||||
const vueResult = cleanVueImports(modified);
|
||||
if (vueResult.changed) {
|
||||
modified = vueResult.content;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 2. PrimeVue component imports → apenas .vue
|
||||
if (ext === '.vue') {
|
||||
const pvResult = cleanPrimeVueImports(modified);
|
||||
if (pvResult.changed) {
|
||||
modified = pvResult.content;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
modified = collapseBlankLines(modified);
|
||||
if (!isDryRun) {
|
||||
await writeFile(filePath, modified, 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
// ─── Main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
const srcDir = join(__dirname, '..', 'src');
|
||||
|
||||
console.log(isDryRun ? '🔍 Dry-run ativo — nenhum arquivo será alterado\n' : '');
|
||||
console.log(`Varrendo: ${srcDir}\n`);
|
||||
|
||||
const files = await getAllFiles(srcDir);
|
||||
console.log(`${files.length} arquivos encontrados...\n`);
|
||||
|
||||
let modifiedCount = 0;
|
||||
let skippedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const wasModified = await processFile(file);
|
||||
if (wasModified) {
|
||||
console.log('✓', relative(srcDir, file));
|
||||
modifiedCount++;
|
||||
} else {
|
||||
skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n─────────────────────────────────────────`);
|
||||
console.log(`Modificados : ${modifiedCount} arquivos`);
|
||||
console.log(`Sem mudança : ${skippedCount} arquivos`);
|
||||
if (isDryRun) {
|
||||
console.log('\n⚠ Dry-run: rode sem --dry-run para aplicar as mudanças.');
|
||||
}
|
||||
73
src/auto-imports.d.ts
vendored
Normal file
73
src/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue').EffectScope
|
||||
const computed: typeof import('vue').computed
|
||||
const createApp: typeof import('vue').createApp
|
||||
const customRef: typeof import('vue').customRef
|
||||
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
|
||||
const defineComponent: typeof import('vue').defineComponent
|
||||
const effectScope: typeof import('vue').effectScope
|
||||
const getCurrentInstance: typeof import('vue').getCurrentInstance
|
||||
const getCurrentScope: typeof import('vue').getCurrentScope
|
||||
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
|
||||
const h: typeof import('vue').h
|
||||
const inject: typeof import('vue').inject
|
||||
const isProxy: typeof import('vue').isProxy
|
||||
const isReactive: typeof import('vue').isReactive
|
||||
const isReadonly: typeof import('vue').isReadonly
|
||||
const isRef: typeof import('vue').isRef
|
||||
const isShallow: typeof import('vue').isShallow
|
||||
const markRaw: typeof import('vue').markRaw
|
||||
const nextTick: typeof import('vue').nextTick
|
||||
const onActivated: typeof import('vue').onActivated
|
||||
const onBeforeMount: typeof import('vue').onBeforeMount
|
||||
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
|
||||
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
|
||||
const onDeactivated: typeof import('vue').onDeactivated
|
||||
const onErrorCaptured: typeof import('vue').onErrorCaptured
|
||||
const onMounted: typeof import('vue').onMounted
|
||||
const onRenderTracked: typeof import('vue').onRenderTracked
|
||||
const onRenderTriggered: typeof import('vue').onRenderTriggered
|
||||
const onScopeDispose: typeof import('vue').onScopeDispose
|
||||
const onServerPrefetch: typeof import('vue').onServerPrefetch
|
||||
const onUnmounted: typeof import('vue').onUnmounted
|
||||
const onUpdated: typeof import('vue').onUpdated
|
||||
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
|
||||
const provide: typeof import('vue').provide
|
||||
const reactive: typeof import('vue').reactive
|
||||
const readonly: typeof import('vue').readonly
|
||||
const ref: typeof import('vue').ref
|
||||
const resolveComponent: typeof import('vue').resolveComponent
|
||||
const shallowReactive: typeof import('vue').shallowReactive
|
||||
const shallowReadonly: typeof import('vue').shallowReadonly
|
||||
const shallowRef: typeof import('vue').shallowRef
|
||||
const toRaw: typeof import('vue').toRaw
|
||||
const toRef: typeof import('vue').toRef
|
||||
const toRefs: typeof import('vue').toRefs
|
||||
const toValue: typeof import('vue').toValue
|
||||
const triggerRef: typeof import('vue').triggerRef
|
||||
const unref: typeof import('vue').unref
|
||||
const useAttrs: typeof import('vue').useAttrs
|
||||
const useCssModule: typeof import('vue').useCssModule
|
||||
const useCssVars: typeof import('vue').useCssVars
|
||||
const useId: typeof import('vue').useId
|
||||
const useModel: typeof import('vue').useModel
|
||||
const useSlots: typeof import('vue').useSlots
|
||||
const useTemplateRef: typeof import('vue').useTemplateRef
|
||||
const watch: typeof import('vue').watch
|
||||
const watchEffect: typeof import('vue').watchEffect
|
||||
const watchPostEffect: typeof import('vue').watchPostEffect
|
||||
const watchSyncEffect: typeof import('vue').watchSyncEffect
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import { fileURLToPath, URL } from 'node:url';
|
||||
import { PrimeVueResolver } from '@primevue/auto-import-resolver';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { visualizer } from 'rollup-plugin-visualizer'; // bundle analyzer
|
||||
import AutoImport from 'unplugin-auto-import/vite'; // auto import Vue composables
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
@@ -15,21 +17,38 @@ export default defineConfig({
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
//optimizeDeps: {
|
||||
// noDiscovery: true
|
||||
//},
|
||||
|
||||
plugins: [
|
||||
vue(),
|
||||
|
||||
tailwindcss(),
|
||||
|
||||
// Auto import de APIs do Vue (ref, computed, onMounted, etc)
|
||||
AutoImport({
|
||||
imports: ['vue'],
|
||||
dts: 'src/auto-imports.d.ts' // gera tipagem automática
|
||||
}),
|
||||
|
||||
// Auto import de componentes do PrimeVue
|
||||
Components({
|
||||
resolvers: [PrimeVueResolver()]
|
||||
}),
|
||||
|
||||
// Visualizador de bundle (gera stats.html no build)
|
||||
visualizer({
|
||||
filename: 'dist/stats-before.html', // depois você muda pra stats-after.html
|
||||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true
|
||||
})
|
||||
],
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
|
||||
Reference in New Issue
Block a user