/** * 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.'); }