indxel-cli 0.1.1 → 0.2.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 +287 -13
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +288 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command6 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import ora from "ora";
|
|
10
|
-
import {
|
|
10
|
+
import { existsSync as existsSync2 } from "fs";
|
|
11
|
+
import { writeFile, mkdir, readFile as readFile2 } from "fs/promises";
|
|
11
12
|
import { join as join2 } from "path";
|
|
12
13
|
|
|
13
14
|
// src/detect.ts
|
|
@@ -175,7 +176,18 @@ export default function robots() {
|
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
// src/commands/init.ts
|
|
178
|
-
var
|
|
179
|
+
var PRE_PUSH_HOOK = `#!/bin/sh
|
|
180
|
+
# indxel SEO guard \u2014 blocks push if critical SEO errors are found
|
|
181
|
+
echo "\\033[36m[indxel]\\033[0m Running SEO check before push..."
|
|
182
|
+
npx indxel-cli check --ci
|
|
183
|
+
if [ $? -ne 0 ]; then
|
|
184
|
+
echo ""
|
|
185
|
+
echo "\\033[31m[indxel] Push blocked \u2014 fix SEO errors first.\\033[0m"
|
|
186
|
+
echo "\\033[2m Run 'npx indxel-cli check' for details.\\033[0m"
|
|
187
|
+
exit 1
|
|
188
|
+
fi
|
|
189
|
+
`;
|
|
190
|
+
var initCommand = new Command("init").description("Initialize indxel in your Next.js project").option("--cwd <path>", "Project directory", process.cwd()).option("--force", "Overwrite existing files", false).option("--hook", "Install git pre-push hook to block pushes on SEO errors", false).action(async (opts) => {
|
|
179
191
|
const cwd = opts.cwd;
|
|
180
192
|
const spinner = ora("Detecting project...").start();
|
|
181
193
|
const project = await detectProject(cwd);
|
|
@@ -218,6 +230,31 @@ var initCommand = new Command("init").description("Initialize indxel in your Nex
|
|
|
218
230
|
} else {
|
|
219
231
|
console.log(chalk.dim(` - robots already exists (skip)`));
|
|
220
232
|
}
|
|
233
|
+
const gitDir = join2(cwd, ".git");
|
|
234
|
+
const hasGit = existsSync2(gitDir);
|
|
235
|
+
if (opts.hook || opts.force) {
|
|
236
|
+
if (!hasGit) {
|
|
237
|
+
console.log(chalk.yellow(" \u26A0") + " No .git directory found \u2014 skip hook install");
|
|
238
|
+
} else {
|
|
239
|
+
const hooksDir = join2(gitDir, "hooks");
|
|
240
|
+
const hookPath = join2(hooksDir, "pre-push");
|
|
241
|
+
if (existsSync2(hookPath) && !opts.force) {
|
|
242
|
+
const existing = await readFile2(hookPath, "utf-8");
|
|
243
|
+
if (existing.includes("indxel")) {
|
|
244
|
+
console.log(chalk.dim(" - pre-push hook already installed (skip)"));
|
|
245
|
+
} else {
|
|
246
|
+
console.log(chalk.yellow(" \u26A0") + " pre-push hook already exists (use --force to overwrite)");
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
await mkdir(hooksDir, { recursive: true });
|
|
250
|
+
await writeFile(hookPath, PRE_PUSH_HOOK, { mode: 493 });
|
|
251
|
+
filesCreated.push(".git/hooks/pre-push");
|
|
252
|
+
console.log(chalk.green(" \u2713") + " Installed git pre-push hook");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} else if (hasGit) {
|
|
256
|
+
console.log(chalk.dim(" - Use --hook to install git pre-push guard"));
|
|
257
|
+
}
|
|
221
258
|
console.log("");
|
|
222
259
|
if (filesCreated.length > 0) {
|
|
223
260
|
console.log(
|
|
@@ -231,6 +268,9 @@ var initCommand = new Command("init").description("Initialize indxel in your Nex
|
|
|
231
268
|
console.log(chalk.dim(" Next steps:"));
|
|
232
269
|
console.log(chalk.dim(` 1. Edit seo.config.${ext} with your site details`));
|
|
233
270
|
console.log(chalk.dim(" 2. Run ") + chalk.bold("npx indxel check") + chalk.dim(" to audit your pages"));
|
|
271
|
+
if (!opts.hook && hasGit) {
|
|
272
|
+
console.log(chalk.dim(" 3. Run ") + chalk.bold("npx indxel init --hook") + chalk.dim(" to guard git pushes"));
|
|
273
|
+
}
|
|
234
274
|
console.log("");
|
|
235
275
|
});
|
|
236
276
|
|
|
@@ -241,7 +281,7 @@ import ora2 from "ora";
|
|
|
241
281
|
import { validateMetadata } from "indxel";
|
|
242
282
|
|
|
243
283
|
// src/scanner.ts
|
|
244
|
-
import { readFile as
|
|
284
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
245
285
|
import { join as join3, dirname, sep } from "path";
|
|
246
286
|
import { glob } from "glob";
|
|
247
287
|
async function scanPages(projectRoot, appDir) {
|
|
@@ -253,7 +293,7 @@ async function scanPages(projectRoot, appDir) {
|
|
|
253
293
|
const pages = [];
|
|
254
294
|
for (const file of pageFiles) {
|
|
255
295
|
const fullPath = join3(appDirFull, file);
|
|
256
|
-
const content = await
|
|
296
|
+
const content = await readFile3(fullPath, "utf-8");
|
|
257
297
|
const route = filePathToRoute(file);
|
|
258
298
|
const page = {
|
|
259
299
|
filePath: join3(appDir, file),
|
|
@@ -273,7 +313,7 @@ async function scanPages(projectRoot, appDir) {
|
|
|
273
313
|
});
|
|
274
314
|
for (const file of layoutFiles) {
|
|
275
315
|
const fullPath = join3(appDirFull, file);
|
|
276
|
-
const content = await
|
|
316
|
+
const content = await readFile3(fullPath, "utf-8");
|
|
277
317
|
const route = filePathToRoute(file).replace(/\/layout$/, "") || "/";
|
|
278
318
|
const hasMetadataExport = hasExport(content, "metadata") || hasExport(content, "generateMetadata");
|
|
279
319
|
if (hasMetadataExport) {
|
|
@@ -551,15 +591,15 @@ function computeSummary(results) {
|
|
|
551
591
|
}
|
|
552
592
|
|
|
553
593
|
// src/store.ts
|
|
554
|
-
import { existsSync as
|
|
555
|
-
import { readFile as
|
|
594
|
+
import { existsSync as existsSync3 } from "fs";
|
|
595
|
+
import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
556
596
|
import { join as join4 } from "path";
|
|
557
597
|
var STORE_DIR = ".indxel";
|
|
558
598
|
var LAST_CHECK_FILE = "last-check.json";
|
|
559
599
|
async function saveCheckResult(cwd, summary) {
|
|
560
600
|
const storeDir = join4(cwd, STORE_DIR);
|
|
561
|
-
if (!
|
|
562
|
-
await
|
|
601
|
+
if (!existsSync3(storeDir)) {
|
|
602
|
+
await mkdir2(storeDir, { recursive: true });
|
|
563
603
|
}
|
|
564
604
|
const stored = {
|
|
565
605
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -586,11 +626,11 @@ async function saveCheckResult(cwd, summary) {
|
|
|
586
626
|
}
|
|
587
627
|
async function loadPreviousCheck(cwd) {
|
|
588
628
|
const filePath = join4(cwd, STORE_DIR, LAST_CHECK_FILE);
|
|
589
|
-
if (!
|
|
629
|
+
if (!existsSync3(filePath)) {
|
|
590
630
|
return null;
|
|
591
631
|
}
|
|
592
632
|
try {
|
|
593
|
-
const data = await
|
|
633
|
+
const data = await readFile4(filePath, "utf-8");
|
|
594
634
|
return JSON.parse(data);
|
|
595
635
|
} catch {
|
|
596
636
|
return null;
|
|
@@ -1118,14 +1158,248 @@ var keywordsCommand = new Command4("keywords").description("Research keyword opp
|
|
|
1118
1158
|
}
|
|
1119
1159
|
});
|
|
1120
1160
|
|
|
1161
|
+
// src/commands/index.ts
|
|
1162
|
+
import { Command as Command5 } from "commander";
|
|
1163
|
+
import chalk6 from "chalk";
|
|
1164
|
+
import ora5 from "ora";
|
|
1165
|
+
import { fetchSitemap as fetchSitemap2, fetchRobots as fetchRobots2 } from "indxel";
|
|
1166
|
+
async function checkPlan(apiKey) {
|
|
1167
|
+
try {
|
|
1168
|
+
const apiUrl = process.env.INDXEL_API_URL || "https://www.indxel.com";
|
|
1169
|
+
const res = await fetch(`${apiUrl}/api/cli/plan`, {
|
|
1170
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
1171
|
+
signal: AbortSignal.timeout(1e4)
|
|
1172
|
+
});
|
|
1173
|
+
if (!res.ok) return null;
|
|
1174
|
+
const data = await res.json();
|
|
1175
|
+
return data.plan ?? null;
|
|
1176
|
+
} catch {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
var indexCommand = new Command5("index").description("Submit your pages to search engines and check indexation status").argument("<url>", "Site URL (e.g., https://yoursite.com)").option("--check", "Check which pages appear indexed (Pro+)", false).option("--indexnow-key <key>", "IndexNow API key for Bing/Yandex/DuckDuckGo submission (Pro+)").option("--api-key <key>", "Indxel API key (required for --check and --indexnow-key)").option("--json", "Output results as JSON", false).action(async (url, opts) => {
|
|
1181
|
+
const jsonOutput = opts.json;
|
|
1182
|
+
const needsPaid = opts.check || opts.indexnowKey;
|
|
1183
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
1184
|
+
url = `https://${url}`;
|
|
1185
|
+
}
|
|
1186
|
+
let baseUrl;
|
|
1187
|
+
try {
|
|
1188
|
+
baseUrl = new URL(url);
|
|
1189
|
+
} catch {
|
|
1190
|
+
console.error(chalk6.red(" Invalid URL."));
|
|
1191
|
+
process.exit(1);
|
|
1192
|
+
}
|
|
1193
|
+
const origin = baseUrl.origin;
|
|
1194
|
+
const host = baseUrl.hostname;
|
|
1195
|
+
if (!jsonOutput) {
|
|
1196
|
+
console.log("");
|
|
1197
|
+
console.log(chalk6.bold(" indxel index") + chalk6.dim(` \u2014 ${origin}`));
|
|
1198
|
+
console.log("");
|
|
1199
|
+
}
|
|
1200
|
+
if (needsPaid) {
|
|
1201
|
+
const apiKey = opts.apiKey || process.env.INDXEL_API_KEY;
|
|
1202
|
+
if (!apiKey) {
|
|
1203
|
+
if (!jsonOutput) {
|
|
1204
|
+
console.log(chalk6.red(" \u2717 --check and --indexnow-key require an API key (Pro plan)."));
|
|
1205
|
+
console.log(chalk6.dim(" Use --api-key or set INDXEL_API_KEY."));
|
|
1206
|
+
console.log(chalk6.dim(" Get your key at https://indxel.com/dashboard/settings"));
|
|
1207
|
+
console.log("");
|
|
1208
|
+
}
|
|
1209
|
+
process.exit(1);
|
|
1210
|
+
}
|
|
1211
|
+
const plan = await checkPlan(apiKey);
|
|
1212
|
+
if (!plan) {
|
|
1213
|
+
if (!jsonOutput) {
|
|
1214
|
+
console.log(chalk6.red(" \u2717 Invalid API key."));
|
|
1215
|
+
console.log("");
|
|
1216
|
+
}
|
|
1217
|
+
process.exit(1);
|
|
1218
|
+
}
|
|
1219
|
+
if (plan === "FREE") {
|
|
1220
|
+
if (!jsonOutput) {
|
|
1221
|
+
console.log(chalk6.red(" \u2717 IndexNow submission and indexation check require a Pro plan."));
|
|
1222
|
+
console.log(chalk6.dim(" Upgrade at https://indxel.com/pricing"));
|
|
1223
|
+
console.log("");
|
|
1224
|
+
}
|
|
1225
|
+
process.exit(1);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
const sitemapSpinner = jsonOutput ? null : ora5("Fetching sitemap...").start();
|
|
1229
|
+
const sitemapResult = await fetchSitemap2(origin);
|
|
1230
|
+
const sitemapUrl = `${origin}/sitemap.xml`;
|
|
1231
|
+
if (!sitemapResult.found || sitemapResult.urls.length === 0) {
|
|
1232
|
+
if (sitemapSpinner) sitemapSpinner.fail("No sitemap found");
|
|
1233
|
+
if (!jsonOutput) {
|
|
1234
|
+
console.log(chalk6.dim(" Create a sitemap first: ") + chalk6.bold("npx indxel init"));
|
|
1235
|
+
console.log("");
|
|
1236
|
+
}
|
|
1237
|
+
if (jsonOutput) {
|
|
1238
|
+
console.log(JSON.stringify({ error: "No sitemap found" }, null, 2));
|
|
1239
|
+
}
|
|
1240
|
+
process.exit(1);
|
|
1241
|
+
}
|
|
1242
|
+
if (sitemapSpinner) sitemapSpinner.succeed(`Found sitemap \u2014 ${sitemapResult.urls.length} URLs`);
|
|
1243
|
+
const robotsSpinner = jsonOutput ? null : ora5("Checking robots.txt...").start();
|
|
1244
|
+
const robotsResult = await fetchRobots2(origin);
|
|
1245
|
+
let sitemapInRobots = false;
|
|
1246
|
+
if (robotsResult.found) {
|
|
1247
|
+
sitemapInRobots = robotsResult.sitemapUrls.some(
|
|
1248
|
+
(s) => s.toLowerCase().includes("sitemap")
|
|
1249
|
+
);
|
|
1250
|
+
if (sitemapInRobots) {
|
|
1251
|
+
if (robotsSpinner) robotsSpinner.succeed("robots.txt references sitemap");
|
|
1252
|
+
} else {
|
|
1253
|
+
if (robotsSpinner) robotsSpinner.warn("robots.txt found but doesn't reference sitemap");
|
|
1254
|
+
if (!jsonOutput) {
|
|
1255
|
+
console.log(chalk6.dim(` Add this to your robots.txt:`));
|
|
1256
|
+
console.log(chalk6.dim(` Sitemap: ${sitemapUrl}`));
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
} else {
|
|
1260
|
+
if (robotsSpinner) robotsSpinner.warn("No robots.txt found");
|
|
1261
|
+
if (!jsonOutput) {
|
|
1262
|
+
console.log(chalk6.dim(" Create one with: ") + chalk6.bold("npx indxel init"));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
if (!jsonOutput) console.log("");
|
|
1266
|
+
const indexNowKey = opts.indexnowKey || process.env.INDEXNOW_KEY;
|
|
1267
|
+
let indexNowResult = [];
|
|
1268
|
+
if (indexNowKey) {
|
|
1269
|
+
const urls = sitemapResult.urls.map((u) => u.loc);
|
|
1270
|
+
const indexNowSpinner = jsonOutput ? null : ora5("Submitting via IndexNow...").start();
|
|
1271
|
+
const indexNowEngines = [
|
|
1272
|
+
{ name: "Bing/Yandex", endpoint: "https://api.indexnow.org/indexnow" }
|
|
1273
|
+
];
|
|
1274
|
+
for (const engine of indexNowEngines) {
|
|
1275
|
+
try {
|
|
1276
|
+
const res = await fetch(engine.endpoint, {
|
|
1277
|
+
method: "POST",
|
|
1278
|
+
headers: { "Content-Type": "application/json" },
|
|
1279
|
+
body: JSON.stringify({
|
|
1280
|
+
host,
|
|
1281
|
+
key: indexNowKey,
|
|
1282
|
+
keyLocation: `${origin}/${indexNowKey}.txt`,
|
|
1283
|
+
urlList: urls.slice(0, 1e4)
|
|
1284
|
+
// IndexNow limit
|
|
1285
|
+
}),
|
|
1286
|
+
signal: AbortSignal.timeout(15e3)
|
|
1287
|
+
});
|
|
1288
|
+
if (res.ok || res.status === 200 || res.status === 202) {
|
|
1289
|
+
indexNowResult.push({ submitted: true, engine: engine.name, status: res.status });
|
|
1290
|
+
if (indexNowSpinner) indexNowSpinner.succeed(`IndexNow \u2014 ${urls.length} URLs submitted to ${engine.name}`);
|
|
1291
|
+
} else {
|
|
1292
|
+
indexNowResult.push({ submitted: false, engine: engine.name, status: res.status });
|
|
1293
|
+
if (indexNowSpinner) indexNowSpinner.warn(`IndexNow \u2014 ${engine.name} returned HTTP ${res.status}`);
|
|
1294
|
+
}
|
|
1295
|
+
} catch (err) {
|
|
1296
|
+
indexNowResult.push({ submitted: false, engine: engine.name });
|
|
1297
|
+
if (indexNowSpinner) indexNowSpinner.fail(`IndexNow \u2014 ${err instanceof Error ? err.message : "failed"}`);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
} else {
|
|
1301
|
+
if (!jsonOutput) {
|
|
1302
|
+
console.log(chalk6.bold(" IndexNow") + chalk6.dim(" (Bing, Yandex, DuckDuckGo)"));
|
|
1303
|
+
console.log(chalk6.dim(" Get instant indexing by setting up IndexNow:"));
|
|
1304
|
+
console.log(chalk6.dim(" 1. Generate a key at ") + chalk6.underline("https://www.bing.com/indexnow"));
|
|
1305
|
+
console.log(chalk6.dim(` 2. Host the key file at ${origin}/{key}.txt`));
|
|
1306
|
+
console.log(chalk6.dim(" 3. Run: ") + chalk6.bold(`npx indxel index ${host} --indexnow-key YOUR_KEY`));
|
|
1307
|
+
console.log("");
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (!jsonOutput) {
|
|
1311
|
+
console.log(chalk6.bold(" Google Search Console"));
|
|
1312
|
+
console.log(chalk6.dim(" Google requires manual setup via Search Console:"));
|
|
1313
|
+
console.log(chalk6.dim(" 1. Go to ") + chalk6.underline("https://search.google.com/search-console"));
|
|
1314
|
+
console.log(chalk6.dim(` 2. Add & verify ${host}`));
|
|
1315
|
+
console.log(chalk6.dim(" 3. Submit your sitemap: Sitemaps > Add > sitemap.xml"));
|
|
1316
|
+
console.log("");
|
|
1317
|
+
}
|
|
1318
|
+
let indexationResults = null;
|
|
1319
|
+
if (opts.check) {
|
|
1320
|
+
const checkSpinner = jsonOutput ? null : ora5("Checking indexation status...").start();
|
|
1321
|
+
indexationResults = [];
|
|
1322
|
+
const urls = sitemapResult.urls.map((u) => u.loc);
|
|
1323
|
+
let indexedCount = 0;
|
|
1324
|
+
for (let i = 0; i < urls.length; i++) {
|
|
1325
|
+
const pageUrl = urls[i];
|
|
1326
|
+
if (checkSpinner) {
|
|
1327
|
+
checkSpinner.text = `Checking indexation... ${i + 1}/${urls.length}`;
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
const cacheUrl = `https://webcache.googleusercontent.com/search?q=cache:${encodeURIComponent(pageUrl)}`;
|
|
1331
|
+
const res = await fetch(cacheUrl, {
|
|
1332
|
+
method: "HEAD",
|
|
1333
|
+
signal: AbortSignal.timeout(5e3),
|
|
1334
|
+
redirect: "manual",
|
|
1335
|
+
headers: {
|
|
1336
|
+
"User-Agent": "Mozilla/5.0 (compatible; Indxel/0.1; +https://indxel.com)"
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
const indexed = res.status === 200 || res.status === 301 || res.status === 302;
|
|
1340
|
+
indexationResults.push({ url: pageUrl, indexed });
|
|
1341
|
+
if (indexed) indexedCount++;
|
|
1342
|
+
} catch {
|
|
1343
|
+
indexationResults.push({ url: pageUrl, indexed: false });
|
|
1344
|
+
}
|
|
1345
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1346
|
+
}
|
|
1347
|
+
if (checkSpinner) {
|
|
1348
|
+
checkSpinner.succeed(`Indexation: ${indexedCount}/${urls.length} pages found in Google cache`);
|
|
1349
|
+
}
|
|
1350
|
+
if (!jsonOutput) {
|
|
1351
|
+
console.log("");
|
|
1352
|
+
const notIndexed = indexationResults.filter((r) => !r.indexed);
|
|
1353
|
+
if (notIndexed.length > 0) {
|
|
1354
|
+
console.log(chalk6.bold(` Not indexed (${notIndexed.length})`));
|
|
1355
|
+
for (const r of notIndexed.slice(0, 20)) {
|
|
1356
|
+
console.log(chalk6.red(" \u2717 ") + r.url);
|
|
1357
|
+
}
|
|
1358
|
+
if (notIndexed.length > 20) console.log(chalk6.dim(` ... and ${notIndexed.length - 20} more`));
|
|
1359
|
+
console.log("");
|
|
1360
|
+
}
|
|
1361
|
+
if (notIndexed.length === 0) {
|
|
1362
|
+
console.log(chalk6.green(" \u2713 All pages appear indexed"));
|
|
1363
|
+
console.log("");
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (jsonOutput) {
|
|
1368
|
+
console.log(JSON.stringify({
|
|
1369
|
+
sitemap: { url: sitemapUrl, urls: sitemapResult.urls.length },
|
|
1370
|
+
robotsTxt: { found: robotsResult.found, referencesSitemap: sitemapInRobots },
|
|
1371
|
+
indexNow: indexNowResult.length > 0 ? indexNowResult : null,
|
|
1372
|
+
indexation: indexationResults
|
|
1373
|
+
}, null, 2));
|
|
1374
|
+
} else {
|
|
1375
|
+
console.log(chalk6.bold(" \u2500\u2500\u2500 Summary \u2500\u2500\u2500"));
|
|
1376
|
+
console.log("");
|
|
1377
|
+
console.log(` Sitemap: ${sitemapResult.urls.length} URLs`);
|
|
1378
|
+
console.log(` robots.txt: ${robotsResult.found ? sitemapInRobots ? chalk6.green("\u2713 references sitemap") : chalk6.yellow("\u26A0 missing sitemap ref") : chalk6.red("\u2717 not found")}`);
|
|
1379
|
+
if (indexNowResult.length > 0) {
|
|
1380
|
+
for (const r of indexNowResult) {
|
|
1381
|
+
console.log(` IndexNow: ${r.submitted ? chalk6.green("\u2713 submitted") : chalk6.red("\u2717 failed")} (${r.engine})`);
|
|
1382
|
+
}
|
|
1383
|
+
} else {
|
|
1384
|
+
console.log(` IndexNow: ${chalk6.dim("not configured (use --indexnow-key)")}`);
|
|
1385
|
+
}
|
|
1386
|
+
if (indexationResults) {
|
|
1387
|
+
const indexedCount = indexationResults.filter((r) => r.indexed).length;
|
|
1388
|
+
console.log(` Google cache: ${indexedCount}/${indexationResults.length} indexed`);
|
|
1389
|
+
}
|
|
1390
|
+
console.log("");
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1121
1394
|
// src/index.ts
|
|
1122
1395
|
function createProgram() {
|
|
1123
|
-
const program2 = new
|
|
1396
|
+
const program2 = new Command6();
|
|
1124
1397
|
program2.name("indxel").description("Infrastructure SEO developer-first. ESLint pour le SEO.").version("0.1.0");
|
|
1125
1398
|
program2.addCommand(initCommand);
|
|
1126
1399
|
program2.addCommand(checkCommand);
|
|
1127
1400
|
program2.addCommand(crawlCommand);
|
|
1128
1401
|
program2.addCommand(keywordsCommand);
|
|
1402
|
+
program2.addCommand(indexCommand);
|
|
1129
1403
|
return program2;
|
|
1130
1404
|
}
|
|
1131
1405
|
|