Новости об искусственном интеллекте
Модели ИИ
Сервисы
Исследования
The Time AI

Новости искусственного интеллекта

Инструкции/ Разработка

Как проверить локальные ссылки в Markdown после правки ИИ в Windows

Составляем явный список относительных путей и проверяем наличие файлов. С учебным примером, скриптом Node.js и границами проверки.

ИнструкцияПроверка ·
Подробности проверки
  • Windows 10.0.26300
  • PowerShell 7.6.5
  • Node.js v24.22.1
Условная сцена проверки локальной документации: ноутбук, блокнот и USB-накопитель на рабочем столе
Фото: TheTimeAI

После правки ИИ ссылка может вести к переименованному файлу или отсутствующей картинке. Ниже — проверка существования файлов из явного списка путей. Вы вручную переносите адреса из одного Markdown-файла, а скрипт проверяет их внутри выбранной папки. Он не разбирает Markdown и не подтверждает исправность всего документа или проекта.

Что понадобится

Нужны Windows, PowerShell и Node.js 24.x. Проверьте версию командой ниже. Если node не найден, получите Windows-версию ветки 24.x с официальной страницы https://nodejs.org/en/download, следуйте официальным инструкциям установки и откройте новый PowerShell. Покупки, npm-пакеты и платный API не нужны. Установка здесь не автоматизируется.

powershell
node --version

Ожидается v24.x.x. Используйте обычную локальную папку, не UNC-путь, сетевую папку, symlink или junction. Не меняйте файлы во время проверки. Скрипт отклоняет обнаруженные символические ссылки и junction в пути, включая предков корня. Это не защита от одновременной подмены файлов другим процессом.

1. Подготовьте учебный пример

Схема: Markdown-файл, ручная сверка всех нужных ссылок, JSON-список и Node.js, проверяющий наличие файлов только из списка.
Фото: TheTimeAI
Открыть крупнее
  1. Создайте новую пустую папку md-link-lab командами ниже. Если имя занято, выберите другое и замените его во всех командах; не выполняйте пример поверх своих документов.
  2. Создайте учебные файлы. picture.png намеренно содержит текст: проверяется наличие файла, а не корректность изображения.
  3. Сохраните весь следующий JavaScript-код как check-files.mjs рядом с md-link-lab, в UTF-8.
powershell
New-Item -ItemType Directory -Path .\md-link-lab -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path .\md-link-lab\docs | Out-Null
Set-Content -Encoding utf8 .\md-link-lab\docs\intro.md 'Учебный текст'
Set-Content -Encoding utf8 '.\md-link-lab\docs\with space.md' 'Учебный текст'
Set-Content -Encoding utf8 .\md-link-lab\picture.png 'fixture, not a real image'
Set-Content -Encoding utf8 .\md-link-lab\README.md @('[Введение](docs/intro.md)', '[Пробел](docs/with%20space.md)', '![Картинка](picture.png)', '[Нет файла](docs/missing.md)')
Set-Content -Encoding utf8 .\md-link-lab\links.json '["docs/intro.md", "docs/with%20space.md", "picture.png", "docs/missing.md"]'
javascript
import { lstatSync, readFileSync } from 'node:fs';
import { resolve, parse, join, relative, isAbsolute, sep } from 'node:path';

function noLinks(absolute) {
  let current = parse(absolute).root;
  const parts = absolute.slice(current.length).split(sep).filter(Boolean);
  if (lstatSync(current).isSymbolicLink()) throw Error('symlink-root');
  for (const part of parts) {
    current = join(current, part);
    if (lstatSync(current).isSymbolicLink()) throw Error('symlink-or-junction');
  }
  return lstatSync(absolute);
}
function localFile(root, raw) {
  if (typeof raw !== 'string' || !raw || raw.length > 1000) throw Error('needs-manual: expected path');
  // Deliberately small path language; this is not a Markdown parser.
  const value = raw.replaceAll('%20', ' ');
  if (/[\\:%?#<>|"*\x00-\x1f\x7f]/u.test(value) || value.startsWith('/'))
    throw Error('needs-manual: unsupported path');
  const parts = value.split('/');
  if (parts.some(p => !p || p === '.' || p === '..' || /[. ]$/u.test(p)
    || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(p)))
    throw Error('needs-manual: unsupported segment');
  const target = resolve(root, ...parts);
  const rel = relative(root, target);
  if (!rel || rel === '..' || rel.startsWith('..' + sep) || isAbsolute(rel))
    throw Error('needs-manual: outside-root');
  if (!noLinks(target).isFile()) throw Error('needs-manual: not a regular file');
  return target;
}
try {
  if (process.argv.length !== 4) throw Error('usage: node check-files.mjs ROOT LIST-RELATIVE.json');
  const root = resolve(process.argv[2]);
  if (process.platform !== 'win32' || !/^[a-z]:\\/iu.test(root)) throw Error('local Windows drive required; no UNC');
  if (!noLinks(root).isDirectory()) throw Error('root must be directory');
  const listFile = localFile(root, process.argv[3]);
  const list = JSON.parse(readFileSync(listFile, 'utf8').replace(/^\uFEFF/u, ''));
  if (!Array.isArray(list) || list.length < 1 || list.length > 500)
    throw Error('needs-manual: list must have 1..500 paths');
  let failed = 0;
  for (const item of list) {
    try {
      localFile(root, item);
      console.log('EXISTS', JSON.stringify(item));
    } catch (error) {
      failed++;
      console.log(error.code === 'ENOENT' ? 'MISSING' : 'NEEDS-MANUAL', JSON.stringify(item), error.code || error.message);
    }
  }
  console.log(failed ? 'LIST-HAS-ISSUES' : 'FILES-EXIST-IN-LIST');
  console.log('NEEDS-MANUAL: Markdown syntax, list completeness, anchors and remote URLs were not checked.');
  process.exitCode = failed ? 1 : 0;
} catch (error) {
  console.error('NEEDS-MANUAL', error.code || error.message);
  process.exitCode = 2;
}

2. Запустите проверку и сравните хеши

  1. Перед запуском снимите SHA-256 учебных файлов. Команды ниже хранят снимок в переменной PowerShell; файл отчёта в проверяемую папку не записывается.
  2. Запустите node с корнем папки и именем списка относительно корня. Сразу сохраните код возврата в переменную.
  3. Повторите хеширование. Пустой вывод Compare-Object означает, что пути и хеши файлов совпали между двумя снимками. Это результат конкретного сравнения, а не гарантия будущей неизменности.
powershell
$before = @(Get-ChildItem -LiteralPath .\md-link-lab -Recurse -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { "$($_.Path)|$($_.Hash)" } | Sort-Object)
node .\check-files.mjs .\md-link-lab links.json
$checkExit = $LASTEXITCODE
$after = @(Get-ChildItem -LiteralPath .\md-link-lab -Recurse -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { "$($_.Path)|$($_.Hash)" } | Sort-Object)
Compare-Object -ReferenceObject $before -DifferenceObject $after
$checkExit

Для примера ожидаются три EXISTS, один MISSING для docs/missing.md, итог LIST-HAS-ISSUES и код 1. В конце всегда появляется NEEDS-MANUAL: сам Markdown и полнота списка не проверялись. Это ожидаемый исход с намеренно отсутствующим файлом, а не ошибка установки.

Для второго исхода измените в учебном README.md адрес docs/missing.md на docs/intro.md и так же исправьте последний элемент links.json. Затем заново выполните весь блок со снимками хешей. Ожидается FILES-EXIST-IN-LIST и код 0. Правки внесены до нового снимка; сам скрипт их не делает.

3. Перенесите метод на копию Markdown

  1. Возьмите отдельную локальную копию папки. Корнем выберите папку, где лежит проверяемый Markdown-файл. Для Markdown в подпапке используйте отдельный корень и список.
  2. Прочитайте Markdown как текст. Из [Инструкция](docs/intro.md) перенесите в JSON только docs/intro.md; аналогично выпишите пути изображений. ИИ может предложить список, но человек должен сверить каждую запись и убедиться, что нужные ссылки не пропущены.
  3. Пишите пути через /, JSON-строки — в двойных кавычках. Допускаются пробелы и %20. Начальное ./ уберите при переносе в список. Исходный Markdown автоматически не меняется.
  4. Запустите тот же check-files.mjs с новым корнем и списком. Разберите все MISSING и NEEDS-MANUAL. Исправляйте адрес в копии Markdown и согласованно обновляйте список; затем повторяйте запуск.

Как читать отчёт

Схема результатов: EXISTS означает наличие файла, MISSING — отсутствие, неподдерживаемый путь и выход за корень требуют ручной проверки. Якоря, URL и полнота списка не проверяются.
Фото: TheTimeAI
Открыть крупнее
Результаты относятся только к перечисленным путям
РезультатЗначение
EXISTSСейчас обнаружен обычный файл. Содержимое, смысл ссылки и корректность изображения не проверены.
MISSINGОдин из компонентов пути отсутствует. Проверьте опечатку, переименование и корень.
NEEDS-MANUALСтрока требует ручной проверки. Не считайте её успешной.
FILES-EXIST-IN-LIST; код 0Все элементы списка ведут к существующим обычным файлам. Это не PASS Markdown.
LIST-HAS-ISSUES; код 1Есть отсутствующие файлы или неподдерживаемые элементы.
Код 2Проверка не началась: проблема запуска, корня, JSON или файла списка.

Что остаётся проверить вручную

Ссылки [текст][id], определения ссылок, сокращённые ссылки, вложенные скобки, необязательные заголовки, HTML и экранирование требуют ручного разбора. Не включайте примеры из блоков кода и обратных кавычек как действующие ссылки. Скрипт Markdown вообще не читает: эти случаи всегда остаются NEEDS-MANUAL, даже при коде 0. Он не проверяет полноту списка, внешние URL, якоря заголовков, регистр для будущего Linux-сервера или результат публикации сайта.

URL, якоря #section, query-параметры, абсолютные пути, обратные слеши, переходы .., каталоги и пути через symlink/junction в списке отклоняются, а не молча пропускаются. Кроме %20, percent-кодирование не поддерживается. Скрипт не обходит подпапки и не ищет ссылки за вас. Файл может существовать, но оказаться не тем документом: важные адресаты откройте и проверьте отдельно.

Отмена и завершение

Проверяющий читает JSON-список и метаданные путей, не исправляет Markdown и не записывает проверяемые файлы. Автоматически откатывать ему нечего. Собственные правки отменяйте по исходной копии. Учебную папку и check-files.mjs можно удалить, убедившись, что это именно созданный вами пример. Перед передачей документа ещё раз сверьте Markdown со списком: успешная проверка неполного списка остаётся неполной проверкой.

Поделиться

ВКонтактеTelegramWhatsApp

К другим инструкциям