@ripast/vue 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +111 -2
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
3
|
-
import { extractTemplateExpressions, posToLineCol, rewriteTemplateReferences } from "@ripast/core/adapter";
|
|
2
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
3
|
+
import { extractTemplateExpressions, hyphenateVueName, isGeneratedNuxtPath, isInsideNuxtAutoImportScope, loadNuxtPathAliases, posToLineCol, resolveBestImportSpecifier, rewriteTemplateReferences, rgFiles } from "@ripast/core/adapter";
|
|
4
4
|
import { URI } from "vscode-uri";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { FileType, createLanguage, createLanguageService, createUriMap } from "@volar/language-service";
|
|
@@ -273,6 +273,112 @@ async function getDiags(vue, fileName) {
|
|
|
273
273
|
function diagKey(d) {
|
|
274
274
|
return `${d.range.start.line}:${d.range.start.character}:${d.code ?? ""}:${d.message}`;
|
|
275
275
|
}
|
|
276
|
+
async function finalizeVueFileRename(cwd, oldAbs, newAbs, existingChanges, autoImportScopes) {
|
|
277
|
+
const changes = [];
|
|
278
|
+
const warnings = [];
|
|
279
|
+
if (!oldAbs.endsWith(".vue") || !newAbs.endsWith(".vue")) return {
|
|
280
|
+
changes,
|
|
281
|
+
warnings
|
|
282
|
+
};
|
|
283
|
+
const oldName = basename(oldAbs, ".vue");
|
|
284
|
+
const newName = basename(newAbs, ".vue");
|
|
285
|
+
mergeIntoChanges(changes, rewriteResolveComponentSites(cwd, oldName, newName, existingChanges, oldAbs, newAbs, warnings));
|
|
286
|
+
if (autoImportScopes.size && isInsideNuxtAutoImportScope(oldAbs, autoImportScopes) && !isInsideNuxtAutoImportScope(newAbs, autoImportScopes)) mergeIntoChanges(changes, addExplicitComponentImports(cwd, oldName, newName, newAbs, mergeView(existingChanges, changes), oldAbs));
|
|
287
|
+
return {
|
|
288
|
+
changes,
|
|
289
|
+
warnings
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function mergeView(a, b) {
|
|
293
|
+
const map = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const c of a) map.set(c.path, c);
|
|
295
|
+
for (const c of b) map.set(c.path, c);
|
|
296
|
+
return [...map.values()];
|
|
297
|
+
}
|
|
298
|
+
function mergeIntoChanges(target, incoming) {
|
|
299
|
+
for (const change of incoming) {
|
|
300
|
+
const existing = target.find((c) => c.path === change.path);
|
|
301
|
+
if (existing) existing.after = change.after;
|
|
302
|
+
else target.push(change);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function rewriteResolveComponentSites(cwd, oldName, newName, changes, oldAbs, newAbs, warnings) {
|
|
306
|
+
const candidates = new Set(rgFiles("resolveComponent", { cwd }));
|
|
307
|
+
if (!candidates.size) return [];
|
|
308
|
+
const byPath = new Map(changes.map((change) => [change.path, change]));
|
|
309
|
+
const escaped = escapeRe(oldName);
|
|
310
|
+
const literalRe = new RegExp(`resolveComponent\\s*\\(\\s*(['"\`])${escaped}\\1`, "g");
|
|
311
|
+
const out = [];
|
|
312
|
+
const warnFiles = [];
|
|
313
|
+
for (const path of candidates) {
|
|
314
|
+
if (path === oldAbs || path === newAbs || isGeneratedNuxtPath(cwd, path)) continue;
|
|
315
|
+
const before = byPath.get(path)?.before ?? readFileSync(path, "utf8");
|
|
316
|
+
const current = byPath.get(path)?.after ?? before;
|
|
317
|
+
literalRe.lastIndex = 0;
|
|
318
|
+
if (!literalRe.test(current)) continue;
|
|
319
|
+
warnFiles.push(relative(cwd, path));
|
|
320
|
+
if (oldName === newName) continue;
|
|
321
|
+
literalRe.lastIndex = 0;
|
|
322
|
+
const after = current.replace(literalRe, (match, quote) => match.replace(`${quote}${oldName}${quote}`, `${quote}${newName}${quote}`));
|
|
323
|
+
if (after === current) continue;
|
|
324
|
+
out.push({
|
|
325
|
+
path,
|
|
326
|
+
rel: relative(cwd, path),
|
|
327
|
+
before,
|
|
328
|
+
after
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (warnFiles.length) warnings.push(`rename-file: "${oldName}" is referenced via resolveComponent() in ${warnFiles.length} file(s) [${warnFiles.join(", ")}]. The component must stay globally registered for these sites to resolve (plugin app.component(...) or Nuxt components dir).`);
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
334
|
+
function addExplicitComponentImports(cwd, oldName, newName, newAbs, changes, oldAbs) {
|
|
335
|
+
const tokens = new Set([
|
|
336
|
+
oldName,
|
|
337
|
+
newName,
|
|
338
|
+
hyphenateVueName(oldName),
|
|
339
|
+
hyphenateVueName(newName)
|
|
340
|
+
]);
|
|
341
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
342
|
+
for (const token of tokens) for (const path of rgFiles(token, {
|
|
343
|
+
cwd,
|
|
344
|
+
glob: "*.vue"
|
|
345
|
+
})) candidates.add(path);
|
|
346
|
+
if (!candidates.size) return [];
|
|
347
|
+
const aliases = loadNuxtPathAliases(cwd);
|
|
348
|
+
const byPath = new Map(changes.map((change) => [change.path, change]));
|
|
349
|
+
const tagRe = new RegExp(`<\\/?(?:${escapeRe(newName)}|${escapeRe(hyphenateVueName(newName))})(?=[\\s/>])`);
|
|
350
|
+
const out = [];
|
|
351
|
+
for (const path of candidates) {
|
|
352
|
+
if (path === oldAbs || path === newAbs || isGeneratedNuxtPath(cwd, path)) continue;
|
|
353
|
+
const before = byPath.get(path)?.before ?? readFileSync(path, "utf8");
|
|
354
|
+
const current = byPath.get(path)?.after ?? before;
|
|
355
|
+
if (!tagRe.test(current)) continue;
|
|
356
|
+
if (hasDefaultImportFromVue(current, newName)) continue;
|
|
357
|
+
const after = insertVueComponentImport(current, newName, resolveBestImportSpecifier(path, newAbs, aliases, "./placeholder.vue"));
|
|
358
|
+
if (after === current) continue;
|
|
359
|
+
out.push({
|
|
360
|
+
path,
|
|
361
|
+
rel: relative(cwd, path),
|
|
362
|
+
before,
|
|
363
|
+
after
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
function escapeRe(s) {
|
|
369
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
370
|
+
}
|
|
371
|
+
function hasDefaultImportFromVue(source, name) {
|
|
372
|
+
return new RegExp(`\\bimport\\s+${escapeRe(name)}\\s+from\\s+['"\`][^'"\`]+\\.vue['"\`]`).test(source);
|
|
373
|
+
}
|
|
374
|
+
function insertVueComponentImport(source, name, specifier) {
|
|
375
|
+
const match = source.match(/<script(?:\s[^>]*)?>/);
|
|
376
|
+
if (!match || match.index === void 0) throw new Error(`ripast rename-file: "${name}" is auto-imported in Nuxt; the new path falls outside auto-import scope but a consumer has no <script> block to receive an explicit import. Add a <script setup> block first.`);
|
|
377
|
+
const insertAt = match.index + match[0].length;
|
|
378
|
+
const importLine = `\nimport ${name} from '${specifier}'`;
|
|
379
|
+
if (source.slice(insertAt, insertAt + 1) === "\n") return `${source.slice(0, insertAt)}${importLine}${source.slice(insertAt)}`;
|
|
380
|
+
return `${source.slice(0, insertAt)}${importLine}\n${source.slice(insertAt)}`;
|
|
381
|
+
}
|
|
276
382
|
const adapter = {
|
|
277
383
|
name: "vue",
|
|
278
384
|
hasFilesContaining: hasVueFilesContaining,
|
|
@@ -292,6 +398,9 @@ const adapter = {
|
|
|
292
398
|
autoImportScopes(cwd) {
|
|
293
399
|
if (!isNuxtProject(cwd)) return /* @__PURE__ */ new Set();
|
|
294
400
|
return nuxtAutoImportScopes(cwd);
|
|
401
|
+
},
|
|
402
|
+
async finalizeFileRename(cwd, oldAbs, newAbs, existingChanges) {
|
|
403
|
+
return finalizeVueFileRename(cwd, oldAbs, newAbs, existingChanges, isNuxtProject(cwd) ? nuxtAutoImportScopes(cwd) : /* @__PURE__ */ new Set());
|
|
295
404
|
}
|
|
296
405
|
};
|
|
297
406
|
const DEFAULT_NUXT_AUTO_IMPORT_DIRS = [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripast/vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "Vue (and Nuxt) framework adapter for ripast. Provides Volar/Vue Language Service-backed rename, move, and template rewriting for .vue SFCs.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"typescript": ">=5.4",
|
|
45
|
-
"@ripast/core": "0.0
|
|
45
|
+
"@ripast/core": "0.1.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@volar/language-service": "^2.4.28",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vscode-uri": "^3.1.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@ripast/core": "0.0
|
|
58
|
+
"@ripast/core": "0.1.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "obuild",
|