svelte-vitals 0.27.0 → 0.28.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/bin.js CHANGED
@@ -2,13 +2,14 @@
2
2
  import {
3
3
  CONFIG_FILENAMES,
4
4
  buildRulesConfig,
5
+ discoverApps,
5
6
  findUnknownRuleIds,
6
7
  isReporterName,
7
8
  knownRuleIds,
8
9
  readCoreVersion,
9
10
  readPackageVersion,
10
11
  run
11
- } from "./chunk-NRQKNTAB.js";
12
+ } from "./chunk-O5VMXV2Q.js";
12
13
 
13
14
  // src/bin.ts
14
15
  import mri3 from "mri";
@@ -960,13 +961,17 @@ var LOCKFILE_TO_PM = {
960
961
  "pnpm-lock.yaml": "pnpm",
961
962
  "yarn.lock": "yarn",
962
963
  "bun.lock": "bun",
963
- "bun.lockb": "bun"
964
+ "bun.lockb": "bun",
965
+ "package-lock.json": "npm"
964
966
  };
965
- function detectPackageManager(io) {
967
+ function detectPackageManagerFromLockfile(io) {
966
968
  for (const [file, pm] of Object.entries(LOCKFILE_TO_PM)) {
967
969
  if (io.readFile(join3(io.cwd, file)) !== void 0) return pm;
968
970
  }
969
- return "npm";
971
+ return void 0;
972
+ }
973
+ function detectPackageManager(io) {
974
+ return detectPackageManagerFromLockfile(io) ?? "npm";
970
975
  }
971
976
  function hasVitePackage(io) {
972
977
  const raw = io.readFile(join3(io.cwd, "package.json"));
@@ -993,31 +998,34 @@ function readInstalledViteVersion(io) {
993
998
  }
994
999
 
995
1000
  // src/ci/action-pin.generated.ts
996
- var ACTION_SHA = "3cf64af159479578663195757c5740629e32ac94";
997
- var ACTION_VERSION = "0.3.2";
1001
+ var ACTION_SHA = "90b7538b5bed65ac123f46d77511dd4b48fa69bb";
1002
+ var ACTION_VERSION = "0.3.3";
998
1003
 
999
1004
  // src/install/index.ts
1005
+ function detectPackageManagerNear(io, appDir) {
1006
+ return detectPackageManagerFromLockfile({ ...io, cwd: appDir }) ?? detectPackageManager(io);
1007
+ }
1000
1008
  function planForClient(client, scope, io, force) {
1001
1009
  const path = client.resolvePath(scope, io.cwd, io.home);
1002
1010
  const existing = io.readFile(path);
1003
1011
  const merged = client.format === "toml" ? mergeToml(existing, MCP_ENTRY, force) : mergeJson(existing, MCP_ENTRY, force);
1004
1012
  return { id: client.id, label: client.label, scope, path, status: merged.status, content: merged.content };
1005
1013
  }
1006
- function resolveCandidate(io, candidates) {
1014
+ function resolveCandidate(io, baseDir, candidates) {
1007
1015
  for (const rel of candidates) {
1008
- const path = join4(io.cwd, rel);
1016
+ const path = join4(baseDir, rel);
1009
1017
  const content = io.readFile(path);
1010
1018
  if (content !== void 0) return { path, content };
1011
1019
  }
1012
- return { path: join4(io.cwd, candidates[0]), content: void 0 };
1020
+ return { path: join4(baseDir, candidates[0]), content: void 0 };
1013
1021
  }
1014
- function planForVitePlugin(io) {
1015
- const { path, content } = resolveCandidate(io, ["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
1022
+ function planForVitePlugin(io, appDir) {
1023
+ const { path, content } = resolveCandidate(io, appDir, ["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
1016
1024
  const result = codemodViteConfig(content);
1017
1025
  return { id: "vite-plugin", label: viteTargetById("vite-plugin").label, path, ...result };
1018
1026
  }
1019
- function planForViteHooks(io) {
1020
- const { path, content } = resolveCandidate(io, ["src/hooks.server.ts", "src/hooks.server.js"]);
1027
+ function planForViteHooks(io, appDir) {
1028
+ const { path, content } = resolveCandidate(io, appDir, ["src/hooks.server.ts", "src/hooks.server.js"]);
1021
1029
  const result = codemodHooksServer(content);
1022
1030
  return { id: "vite-hooks", label: viteTargetById("vite-hooks").label, path, ...result };
1023
1031
  }
@@ -1044,23 +1052,23 @@ function planForAgentTarget(target, io, force, version) {
1044
1052
  return { id: target.id, label: target.label, path, status, content };
1045
1053
  });
1046
1054
  }
1047
- function planForConfigTarget(target, io, force) {
1048
- const existingRel = findExistingConfigFile(io.readFile, io.cwd);
1055
+ function planForConfigTarget(target, io, force, appDir) {
1056
+ const existingRel = findExistingConfigFile(io.readFile, appDir);
1049
1057
  if (existingRel !== void 0) {
1050
- const path2 = join4(io.cwd, existingRel);
1058
+ const path2 = join4(appDir, existingRel);
1051
1059
  const status = force ? "updated" : "exists";
1052
1060
  const content2 = force ? buildConfigFileTemplate({
1053
- useDefineConfig: existingRel.endsWith(".ts") && hasSvelteVitalsDependency(io.readFile, io.cwd),
1054
- useCommonJs: existingRel.endsWith(".js") && !isEsmProject(io.readFile, io.cwd)
1061
+ useDefineConfig: existingRel.endsWith(".ts") && hasSvelteVitalsDependency(io.readFile, appDir),
1062
+ useCommonJs: existingRel.endsWith(".js") && !isEsmProject(io.readFile, appDir)
1055
1063
  }) : void 0;
1056
1064
  return { id: target.id, label: target.label, path: path2, status, content: content2 };
1057
1065
  }
1058
1066
  const ext = detectBestConfigExtension({
1059
1067
  readFile: io.readFile,
1060
- cwd: io.cwd,
1068
+ cwd: appDir,
1061
1069
  nodeVersion: io.nodeVersion ?? process.version
1062
1070
  });
1063
- const path = join4(io.cwd, `svelte-vitals.config.${ext}`);
1071
+ const path = join4(appDir, `svelte-vitals.config.${ext}`);
1064
1072
  const content = buildConfigFileTemplate({ useDefineConfig: ext === "ts" });
1065
1073
  return { id: target.id, label: target.label, path, status: "created", content };
1066
1074
  }
@@ -1187,6 +1195,44 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
1187
1195
  io.errorLog("svelte-vitals: no valid clients or targets selected.");
1188
1196
  return 2;
1189
1197
  }
1198
+ const hasSvelteConfig = (dir) => {
1199
+ try {
1200
+ return io.readFile(join4(dir, "svelte.config.js")) !== void 0 || io.readFile(join4(dir, "svelte.config.ts")) !== void 0;
1201
+ } catch {
1202
+ return false;
1203
+ }
1204
+ };
1205
+ const needsApp = viteIds.length > 0 || configIds.length > 0;
1206
+ let appDir = io.cwd;
1207
+ if (needsApp) {
1208
+ if (flags.app) {
1209
+ const candidate = join4(io.cwd, flags.app);
1210
+ if (!hasSvelteConfig(candidate)) {
1211
+ io.errorLog(`svelte-vitals: --app '${flags.app}' is not a SvelteKit app (no svelte.config.{js,ts} there).`);
1212
+ return 2;
1213
+ }
1214
+ appDir = candidate;
1215
+ } else if (!hasSvelteConfig(io.cwd)) {
1216
+ const apps = await (io.discoverApps ?? discoverApps)(io.cwd);
1217
+ if (apps.length === 1) {
1218
+ io.errorLog(`svelte-vitals: detected SvelteKit app at ${apps[0]}; targeting it for the Vite/config targets.`);
1219
+ appDir = join4(io.cwd, apps[0]);
1220
+ } else if (apps.length > 1) {
1221
+ if (io.isTTY) {
1222
+ const picked = await prompts.selectApp(apps);
1223
+ if (picked === null) {
1224
+ io.log("Cancelled.");
1225
+ return 0;
1226
+ }
1227
+ appDir = join4(io.cwd, picked);
1228
+ } else {
1229
+ io.errorLog(`svelte-vitals: multiple SvelteKit apps found: ${apps.join(", ")}.`);
1230
+ io.errorLog(`svelte-vitals: pass one with --app, e.g. \`svelte-vitals install --app ${apps[0]}\`.`);
1231
+ return 2;
1232
+ }
1233
+ }
1234
+ }
1235
+ }
1190
1236
  const rows = [];
1191
1237
  for (const client of clients) {
1192
1238
  let scope;
@@ -1215,7 +1261,7 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
1215
1261
  }
1216
1262
  }
1217
1263
  for (const viteId of viteIds) {
1218
- rows.push(viteId === "vite-plugin" ? planForVitePlugin(io) : planForViteHooks(io));
1264
+ rows.push(viteId === "vite-plugin" ? planForVitePlugin(io, appDir) : planForViteHooks(io, appDir));
1219
1265
  }
1220
1266
  for (const agentId of agentIds) {
1221
1267
  const target = agentTargetById(agentId);
@@ -1231,7 +1277,7 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
1231
1277
  for (const configId of configIds) {
1232
1278
  const target = configTargetById(configId);
1233
1279
  try {
1234
- rows.push(planForConfigTarget(target, io, flags.force ?? false));
1280
+ rows.push(planForConfigTarget(target, io, flags.force ?? false, appDir));
1235
1281
  } catch (err) {
1236
1282
  io.errorLog(
1237
1283
  `svelte-vitals: could not check existing config file: ${err instanceof Error ? err.message : String(err)}`
@@ -1291,17 +1337,18 @@ ${indent(r.snippet ?? "")}`);
1291
1337
  io.errorLog(`svelte-vitals: failed to write ${r.path}: ${err instanceof Error ? err.message : String(err)}`);
1292
1338
  }
1293
1339
  }
1294
- if (viteWasWritten && io.runCommand && !hasVitePackage(io)) {
1295
- const pm = detectPackageManager(io);
1340
+ const appIo = { ...io, cwd: appDir };
1341
+ if (viteWasWritten && io.runCommand && !hasVitePackage(appIo)) {
1342
+ const pm = appDir === io.cwd ? detectPackageManager(io) : detectPackageManagerNear(io, appDir);
1296
1343
  const { command, args } = installCommand(pm);
1297
1344
  io.log(`Installing @svelte-vitals/vite via ${pm}...`);
1298
- const code = io.runCommand(command, args, io.cwd);
1345
+ const code = io.runCommand(command, args, appDir);
1299
1346
  if (code !== 0) {
1300
1347
  io.errorLog(
1301
1348
  `svelte-vitals: failed to install @svelte-vitals/vite (${command} ${args.join(" ")} exited ${code}). Install it manually.`
1302
1349
  );
1303
1350
  } else {
1304
- const installedVersion = readInstalledViteVersion(io);
1351
+ const installedVersion = readInstalledViteVersion(appIo) ?? readInstalledViteVersion(io);
1305
1352
  io.log(
1306
1353
  installedVersion ? `svelte-vitals: installed @svelte-vitals/vite@${installedVersion} \u2014 compare against \`svelte-vitals --version\`'s core number if findings ever seem out of sync.` : "svelte-vitals: installed @svelte-vitals/vite (could not read the installed version from node_modules)."
1307
1354
  );
@@ -1345,18 +1392,20 @@ function resolveInstallArgs(argv) {
1345
1392
  if (rawScope === "project" || rawScope === "global") scope = rawScope;
1346
1393
  else errors.push(`svelte-vitals: unknown --scope '${rawScope}'; expected project|global.`);
1347
1394
  }
1395
+ const app = typeof argv.app === "string" && argv.app.trim() !== "" ? argv.app.trim() : void 0;
1348
1396
  const refresh = Boolean(argv.refresh);
1349
1397
  if (refresh && rawClients.length > 0) {
1350
1398
  errors.push("svelte-vitals: --refresh regenerates existing files and cannot be combined with --client.");
1351
1399
  }
1352
1400
  if (errors.length > 0) return { flags: null, warnings, errors };
1353
- if (refresh && (scope !== void 0 || Boolean(argv.yes) || Boolean(argv.force))) {
1354
- warnings.push("svelte-vitals: --scope, --yes, and --force are ignored with --refresh.");
1401
+ if (refresh && (scope !== void 0 || Boolean(argv.yes) || Boolean(argv.force) || app !== void 0)) {
1402
+ warnings.push("svelte-vitals: --scope, --yes, --force, and --app are ignored with --refresh.");
1355
1403
  }
1356
1404
  return {
1357
1405
  flags: {
1358
1406
  ...client.length > 0 ? { client } : {},
1359
1407
  ...scope ? { scope } : {},
1408
+ ...app !== void 0 && !refresh ? { app } : {},
1360
1409
  yes: Boolean(argv.yes),
1361
1410
  dryRun: Boolean(argv["dry-run"]),
1362
1411
  force: Boolean(argv.force),
@@ -1400,6 +1449,13 @@ Options:
1400
1449
  the same pass as everything else; supports --force to regenerate. \`svelte-vitals
1401
1450
  ci upgrade\` remains the way to bump an existing workflow's pinned action version.
1402
1451
  --scope <scope> project | global (applies to all selected clients; codex is always global)
1452
+ --app <dir> Monorepo: the SvelteKit app directory the vite-plugin/vite-hooks/config-file
1453
+ targets write into (e.g. --app apps/web). Without it, when the current
1454
+ directory isn't itself a SvelteKit app, one detected app is used
1455
+ automatically (with a notice), several prompt a picker on a TTY, and
1456
+ non-interactive runs exit 2 asking for --app. All other targets (MCP
1457
+ configs, skills, ci-workflow) always write at the current directory \u2014
1458
+ the repo root is their correct home.
1403
1459
  --yes, -y Skip the confirmation prompt
1404
1460
  --dry-run Print the planned changes and exit without writing
1405
1461
  --force Overwrite an existing svelte-vitals entry
@@ -1470,6 +1526,14 @@ function clackPrompts() {
1470
1526
  });
1471
1527
  return p.isCancel(res) ? null : res;
1472
1528
  },
1529
+ selectApp: async (apps) => {
1530
+ const res = await p.select({
1531
+ message: "Multiple SvelteKit apps found \u2014 which one should the Vite/config targets go into?",
1532
+ options: apps.map((a) => ({ value: a, label: a })),
1533
+ initialValue: apps[0]
1534
+ });
1535
+ return p.isCancel(res) ? null : res;
1536
+ },
1473
1537
  confirm: async (planText) => {
1474
1538
  const res = await p.confirm({ message: `Apply this plan?
1475
1539
  ${planText}` });
@@ -1480,7 +1544,7 @@ ${planText}` });
1480
1544
  async function runInstallCli(args) {
1481
1545
  const argv = mri(args, {
1482
1546
  boolean: ["yes", "dry-run", "force", "refresh", "help"],
1483
- string: ["client", "scope"],
1547
+ string: ["client", "scope", "app"],
1484
1548
  alias: { y: "yes", h: "help" }
1485
1549
  });
1486
1550
  if (argv.help) {
@@ -1546,6 +1546,7 @@ async function run(opts = {}) {
1546
1546
 
1547
1547
  export {
1548
1548
  ProjectError,
1549
+ discoverApps,
1549
1550
  readPackageVersion,
1550
1551
  readCoreVersion,
1551
1552
  isReporterName,
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  routeMatcher,
11
11
  run,
12
12
  spinnerEnabled
13
- } from "./chunk-NRQKNTAB.js";
13
+ } from "./chunk-O5VMXV2Q.js";
14
14
  export {
15
15
  ProjectError,
16
16
  analyzeProject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-vitals",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "A SvelteKit SEO checker — not a runtime Web Vitals reporter. Static analysis of your routes' head metadata.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "smol-toml": "^1.7.0",
47
47
  "svelte": "^5.56.4",
48
48
  "tinyglobby": "^0.2.17",
49
- "@svelte-vitals/core": "0.24.0"
49
+ "@svelte-vitals/core": "0.25.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^24.13.3"