207 lines
7.7 KiB
JavaScript
207 lines
7.7 KiB
JavaScript
/**
|
|
* 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.');
|
|
}
|