Перейти к содержимому
GitHubXDiscord

extractTrigger

Стабильный · с 0.1.0

Извлекает первый useEffect(() => { ... }, []) в компоненте в выделенный файл *.trigger.ts. Исходный компонент переписывается так, чтобы зажигать новое событие вместо инлайн-исполнения эффекта. Поставляется как программный API (extractTrigger({ ... })) и как CLI (triggery-codemod extract-trigger).

Это прагматичный V1 — он работает с типовой формой одного useEffect с понятным телом побочного эффекта. Сложные эффекты (cleanup-функции, несколько эффектов в одном файле, динамические deps) требуют ручной доводки; codemod останавливается на первом матче и отчитывается о сделанном.

import { extractTrigger } from '@triggery/codemod';
function extractTrigger(options: ExtractTriggerOptions): ExtractTriggerResult;

interface ExtractTriggerOptions {
  readonly file: string;
  readonly name: string;
  readonly outDir?: string;
  readonly dryRun?: boolean;
  readonly project?: import('ts-morph').Project;
}

interface ExtractTriggerResult {
  readonly sourceUpdated: boolean;
  readonly triggerFilePath: string;
  readonly triggerFileContent: string;
  readonly originalEffectBody: string;
}
ПараметрТипОбязательныйОписание
filestringдаПуть к исходнику .tsx (или .ts).
namestringдаId триггера в kebab-case. Используется для вывода имени символа (newMessageTrigger) и имени файла (new-message.trigger.ts).
outDirstringнетДиректория для сгенерированного файла триггера. По умолчанию — директория исходника.
dryRunbooleanнетСпланировать изменения и вернуть их без записи на диск.
projectts-morph.ProjectнетПредсуществующий project. Позволяет CLI переиспользовать один tsconfig между батчами.
ПолеТипОписание
sourceUpdatedbooleanБыл ли переписан исходный файл.
triggerFilePathstringАбсолютный путь сгенерированного файла *.trigger.ts.
triggerFileContentstringСодержимое нового файла триггера.
originalEffectBodystringИзвлечённое тело без отступов.
triggery-codemod extract-trigger --name <kebab-case> [--out-dir <path>] [--dry-run] <file>
ФлагОбязательныйОписание
--nameдаId триггера в kebab-case.
--out-dirнетПереопределить дефолтную выходную директорию.
--dry-runнетРаспечатать запланированные изменения без записи.

CLI печатает сгенерированный путь и однострочную подсказку с import, который нужно добавить в файл компонента (codemod намеренно не правит импорты — держит AST-изменения минимальными и предсказуемыми).

import { extractTrigger } from '@triggery/codemod';

const result = extractTrigger({
  file: 'src/Chat.tsx',
  name: 'new-message',
  dryRun: true,
});

console.log(result.triggerFilePath);   // .../src/new-message.trigger.ts
console.log(result.triggerFileContent); // generated module text

Программно — запись в выделенную папку триггеров

Заголовок раздела «Программно — запись в выделенную папку триггеров»
import { extractTrigger } from '@triggery/codemod';

extractTrigger({
  file:   'src/screens/Chat.tsx',
  name:   'new-message',
  outDir: 'src/triggers',
});
// → src/triggers/new-message.trigger.ts created
// → src/screens/Chat.tsx rewritten: useEffect block replaced with useEvent(...)
triggery-codemod extract-trigger --name new-message src/Chat.tsx

Вывод:

Generated src/new-message.trigger.ts
Add this import to the component file:
  import { newMessageTrigger } from './new-message.trigger';

Сгенерированный файл — это стартер: codemod не может вывести surface “events / conditions / actions” рантайма без твоего ввода. Он выдаёт void-payload событие и Record<string, never> для conditions/actions; схему ты заполняешь руками.

import { createTrigger } from '@triggery/core';

/**
 * Extracted automatically by @triggery/codemod from a useEffect block.
 * Review the generated handler — the codemod does its best but cannot infer
 * the runtime "events / conditions / actions" surface without your input.
 * …
 */
export const newMessageTrigger = createTrigger<{
  events: { 'new-message': void };
  conditions: Record<string, never>;
  actions: Record<string, never>;
}>({
  id: 'new-message',
  events: ['new-message'],
  required: [],
  handler({ event, conditions, actions, check }) {
    // TODO: migrated from useEffect — refactor side effects into actions.
    /* … original effect body … */
  },
});