safe point before auto-import cleanup
This commit is contained in:
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.');
|
||||
}
|
||||
Reference in New Issue
Block a user