Руководство по поддержке флага --dry-run для генераторов Nx
Узнайте, как сделать ваш кастомный генератор Nx поддерживающим флаг --dry-run при вызове других генераторов. Исправьте предупреждение и реализуйте корректную симуляцию dry-run с помощью этого полного руководства.
Как сделать так, чтобы кастомный генератор Nx поддерживал флаг --dry-run при вызове другого генератора?
Я разрабатываю кастомный генератор Nx, который создает библиотеку и изменяет рабочее пространство (например, обновляет правила ESLint). Генератор работает корректно, но когда я запускаю его с флагом --dry-run, Nx отображает следующее предупреждение:
“ПРИМЕЧАНИЕ: Этот генератор не поддерживает --dry-run. Если вы запускаете это в Nx Console, он должен выполниться корректно после нажатия кнопки ‘Generate’.”
Предупреждение исчезает, если я удаляю вызов другого встроенного генератора (например, libraryGenerator из @nx/angular/generators), и dry-run работает как ожидается. Похоже, что вызов другого генератора внутри моего кастомного отключает симуляцию dry-run.
Вот упрощенный пример моего генератора:
import { Tree, formatFiles } from '@nx/devkit';
import { libraryGenerator } from '@nx/angular/generators';
export default async function myGenerator(tree: Tree, options: any) {
await libraryGenerator(tree, {
...options,
name: 'demo-lib',
});
// еще редактирования, все с использованием tree.read/write...
await formatFiles(tree);
}
Есть ли способ сделать так, чтобы составной генератор все еще поддерживал флаг --dry-run?
Поддержка флага --dry-run в кастомных генераторах Nx
Чтобы ваш кастомный генератор Nx поддерживал флаг --dry-run при вызове другого генератора, необходимо правильно распространять контекст dry-run через параметры генератора. Проблема возникает потому, что вызываемый генератор не осведомлен о режиме dry-run, поэтому вам нужно явно передать флаг dry-run и правильно его обработать.
Содержание
- Понимание флага --dry-run в Nx
- Основная проблема с составными генераторами
- Решение: Распространение флага --dry-run
- Альтернативный подход: Использование вспомогательных функций генераторов
- Лучшие практики для поддержки dry-run
Понимание флага --dry-run в Nx
Nx использует архитектуру на основе Tree (дерева), где все операции с файловой системой проходят через виртуальную файловую систему. Флаг --dry-run позволяет пользователям симулировать, какие изменения были бы сделаны без фактического изменения их рабочего пространства. Это особенно полезно для:
- Предварительного просмотра изменений перед их применением
- Обучающих целей
- Сценариев скриптов, где вы хотите сначала проверить изменения
Когда включен режим --dry-run, Nx должен отображать все файловые операции, которые были бы выполнены, но пропустить фактическую запись на диск.
Основная проблема с составными генераторами
Проблема, с которой вы сталкиваетесь, возникает потому, что при вызове другого генератора внутри вашего кастомного генератора вы не передаете контекст dry-run. Вызываемый генератор (например, libraryGenerator) не знает, что он работает в режиме dry-run, поэтому он ведет себя так, как будто вносит реальные изменения.
Рассмотрим ваш код:
export default async function myGenerator(tree: Tree, options: any) {
await libraryGenerator(tree, {
...options,
name: 'demo-lib',
});
// ...
}
Объект options не содержит информацию о режиме dry-run, поэтому libraryGenerator продолжает работать в обычном режиме.
Решение: Распространение флага --dry-run
Решение заключается в проверке, работает ли ваш генератор в режиме dry-run, и передаче этой информации вызываемому генератору. Вот как это реализовать:
import { Tree, formatFiles } from '@nx/devkit';
import { libraryGenerator } from '@nx/angular/generators';
import { stripIndents } from '@nx/devkit/src/utils/strip-indents';
export default async function myGenerator(tree: Tree, options: any) {
// Проверяем, работаем ли мы в режиме dry-run
const isDryRun = options.dryRun || false;
// Передаем флаг dry-run вызываемому генератору
await libraryGenerator(tree, {
...options,
name: 'demo-lib',
dryRun: isDryRun,
});
// Ваша кастомная логика - убедитесь, что она также учитывает dry-run
if (!isDryRun) {
// Выполняем фактические изменения только при работе не в режиме dry-run
// Пример: обновление конфигурации ESLint
const eslintConfig = tree.read('eslint.config.js');
if (eslintConfig) {
const updatedConfig = modifyEslintConfig(eslintConfig.toString());
tree.write('eslint.config.js', updatedConfig);
}
}
// formatFiles также должен учитывать dry-run
if (!isDryRun) {
await formatFiles(tree);
}
}
// Вспомогательная функция для модификации конфигурации ESLint
function modifyEslintConfig(config: string): string {
return stripIndents`
${config}
// Кастомные правила ESLint, добавленные my-generator
rules: {
'my-custom-rule': 'error'
}
`;
}
Ключевые моменты решения:
- Обнаружение режима dry-run: Проверьте
options.dryRun, чтобы определить, работает ли генератор в режиме симуляции - Распространение флага: Передайте
dryRun: isDryRunвызываемому генератору - Условная логика: Выполняйте фактические файловые операции только при работе не в режиме dry-run
- Вспомогательные функции: Создавайте функции, которые можно легко тестировать и которые работают последовательно в обоих режимах
Альтернативный подход: Использование вспомогательных функций генераторов
Для более сложных сценариев вы можете создать вспомогательные функции, которые последовательно обрабатывают логику dry-run:
import { Tree, formatFiles } from '@nx/devkit';
import { libraryGenerator } from '@nx/angular/generators';
import { stripIndents } from '@nx/devkit/src/utils/strip-indents';
// Вспомогательная функция, учитывающая dry-run
export async function safeUpdateFile(tree: Tree, path: string, content: string, isDryRun: boolean) {
if (!isDryRun) {
tree.write(path, content);
console.log(`✏️ ${path}`);
} else {
console.log(`📝 Запись в: ${path}`);
console.log(`Предпросмотр содержимого:\n${content.substring(0, 100)}...`);
}
}
export default async function myGenerator(tree: Tree, options: any) {
const isDryRun = options.dryRun || false;
// Вызов генератора библиотеки с поддержкой dry-run
await libraryGenerator(tree, {
...options,
name: 'demo-lib',
dryRun: isDryRun,
});
// Использование вспомогательных функций для последовательного поведения dry-run
const eslintConfig = tree.read('eslint.config.js');
if (eslintConfig) {
const updatedConfig = modifyEslintConfig(eslintConfig.toString());
await safeUpdateFile(tree, 'eslint.config.js', updatedConfig, isDryRun);
}
// Форматирование файлов только при работе не в режиме dry-run
if (!isDryRun) {
await formatFiles(tree);
}
}
Этот подход обеспечивает более последовательное ведение журнала и поведение в вашем генераторе.
Лучшие практики для поддержки dry-run
1. Всегда проверяйте флаг dry-run
Перед выполнением любых файловых операций проверяйте, работаете ли вы в режиме dry-run:
const isDryRun = options.dryRun || false;
if (!isDryRun) {
// Фактические файловые операции здесь
}
2. Предоставляйте четкий вывод в режиме dry-run
В режиме dry-run четко указывайте, что произошло бы:
if (isDryRun) {
console.log(`📋 Режим dry-run - обновление: ${filePath}`);
console.log(`📋 Предпросмотр содержимого:\n${content.substring(0, 200)}...`);
}
3. Обрабатывайте все вызовы генераторов
Убедитесь, что все вызовы генераторов внутри вашего кастомного генератора также получают флаг dry-run:
await someOtherGenerator(tree, {
...options,
dryRun: isDryRun,
// другие параметры...
});
4. Используйте встроенные утилиты Nx
Используйте существующие утилиты Nx, которые поддерживают dry-run:
import { addDependenciesToPackageJson } from '@nx/devkit';
await addDependenciesToPackageJson(
tree,
{ 'my-package': '^1.0.0' },
{},
isDryRun
);
5. Тестируйте вашу реализацию dry-run
Создавайте тесты для проверки поведения dry-run:
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { myGenerator } from './generator';
describe('my-generator', () => {
it('должен поддерживать режим dry-run', async () => {
const tree = createTreeWithEmptyWorkspace();
await myGenerator(tree, {
name: 'test-lib',
dryRun: true,
});
// Проверяем, что фактические изменения не были внесены
expect(tree.exists('libs/test-lib')).toBeFalsy();
});
});
Заключение
Чтобы ваш кастомный генератор Nx поддерживал флаг --dry-run при вызове другого генератора, вам необходимо:
- Обнаруживать режим dry-run, проверяя
options.dryRun - Распространять флаг на все вызываемые генераторы
- Реализовывать условную логику, которая выполняет фактические файловые операции только при работе не в режиме dry-run
- Предоставлять четкий вывод в режиме dry-run, чтобы помочь пользователям понять, что произошло бы
- Тестировать вашу реализацию, чтобы убедиться, что она правильно работает в обоих режимах
Ключевое понимание заключается в том, что поддержка dry-run должна обрабатываться на каждом уровне вашего генератора - не только в основной логике, но и в любых вызовах генераторов или файловых операциях, которые вы выполняете. Следуя этим шаблонам, вы создадите генераторы, которые обеспечивают последовательный и полезный пользовательский опыт независимо от того, работают ли они в режиме симуляции или фактического выполнения.
Помните, что предупреждение, которое вы видели (“Этот генератор не поддерживает --dry-run”), исчезнет после правильной реализации поддержки dry-run, поскольку Nx распознает, что ваш генератор может правильно обрабатывать режим симуляции.