ai(instructions)

Replaces code found with ast-grep pattern using instructions provided to LLM.

    import { files } from '@codemod.com/workflow'

    await files()
      .jsFam()
      .astGrep`$SCHEMA($$$REST, [$$$ACTIONS])`
      .ai`
        SCHEMA could be one of 'any', 'array', 'bigint', 'blob', 'boolean', 'custom', 'date', 'enum_', 'instance', 'intersect',
        'lazy', 'literal', 'looseObject', 'looseTuple', 'map', 'nan', 'never', 'nonNullable', 'nonNullish', 'nonOptional',
        'null_', 'nullable', 'nullish', 'number', 'object', 'objectWithRest', 'optional', 'picklist', 'record', 'set',
        'strictObject', 'strictTuple', 'string', 'symbol', 'tuple', 'tupleWithRest', 'undefined_', 'union', 'unknown', 'variant',
        'void_'
        ACTIONS is array of calls to one of 'bic', 'brand', 'bytes', 'check', 'creditCard', 'cuid2', 'decimal', 'email',
        'emoji', 'empty', 'endsWith', 'every', 'excludes', 'finite', 'hash', 'hexadecimal', 'hexColor', 'imei', 'includes',
        'integer', 'ip', 'ipv4', 'ipv6', 'isoDate', 'isoDateTime', 'isoTime', 'isoTimeSecond', 'isoTimestamp', 'isoWeek',
        'length', 'mac', 'mac48', 'mac64', 'maxBytes', 'maxLength', 'maxSize', 'maxValue', 'mimeType', 'minBytes',
        'minLength', 'minSize', 'minValue', 'multipleOf', 'nonEmpty', 'notBytes', 'notLength', 'notSize', 'notValue', 'octal',
        'readonly', 'regex', 'safeInteger', 'size', 'some', 'startsWith', 'toLowerCase', 'toMaxValue', 'toMinValue', 'toUpperCase',
        'transform', 'trim', 'trimEnd', 'trimStart', 'ulid', 'url', 'uuid', 'value','forward'
        
        If $SCHEMA is not one of SCHEMA, skip
        If $$$ACTIONS is not one of ACTIONS, skip

        If skipping - return same string

        Take into account recursive replacements
        
        Example
        Before: v.string('some string', [v.email()])
        After: v.pipe(v.string('some string'), v.email())
        `
v.string([v.email(), v.endsWith('@gmail.com')])
ai(instructions) requires --OPENAI_API_KEY=somekey to be passed to workflow.

Parameters

instructions
string | readonly string[]
required

Instructions to LLM in natural language with before and after examples.

map(callback)

Works similar to array.map - maps found code with astGrep.

import { astGrep } from '@codemod.com/workflow'

const allUsagesOfConsoleLog = await astGrep('console.log($$$A)')
    .map(({ getNode }) => getNode().text())
    
console.log(allUsagesOfConsoleLog)
// [
//   "console.log('sample console.log')",
//   "console.log('debugging flow')",
// ]
import { astGrep, contexts } from '@codemod.com/workflow'

const allUsagesOfConsoleLog = await astGrep('console.log($$$A)')
    .map(({ getNode }) => ({
        code: getNode().text(),
        file: contexts.getFileContext().file,
    }))
    
console.log(allUsagesOfConsoleLog)
// [
//   {
//     code: "console.log('sample console.log')",
//     file: "src/app.js",
//   },
//   {
//     code: "console.log('debugging flow')",
//     file: "src/page.js",
//},
// ]

Parameters

callback
CallbackFunction
required
import type { SgNode } from '@ast-grep/napi'

interface CallbackFunctionHelpers {
  getNode: () => SgNode,
  getMatch: (m: string) => SgNode | null,
  getMultipleMatches: (m: string) => SgNode[],
}

type CallbackFunction =
  (helpers: ReplacementFunctionHelpers) =>
  Promise<string | undefined> | string | undefined

It will be called for every found node. It can return anything. You can use file context inside for example.

Helper functions:

replace(replacement)

Replaces code found with astGrep. Receives context with every found node.

import { astGrep } from '@codemod.com/workflow'

await astGrep('console.log($$$A)')
  .replace('console.error($$$A)')
import { astGrep } from '@codemod.com/workflow'

await astGrep('console.log($$$A)')
  .replace(({ getMultipleMatches }) => {
    const args = getMultipleMatches('A')
      .filter(node => node.kind !== 'arrow_function' && node.kind !== ',')
      
    return `console.error(${args.join(', '))})`
  })

Parameters

replacement
string | ReplacementFunction
required
import type { SgNode } from '@ast-grep/napi'

interface ReplacementFunctionHelpers {
  getNode: () => SgNode,
  getMatch: (m: string) => SgNode | null,
  getMultipleMatches: (m: string) => SgNode[],
}

type ReplacementFunction =
  (helpers: ReplacementFunctionHelpers) =>
  Promise<string | undefined> | string | undefined

If string is provided, it will be used as a replacement for every found node. If function is provided, it will be called for every found node. It should return a string which will be used as a replacement.

Replacement string can contain matches from search pattern which would be inserted in place. For example, if search pattern is console.log($$$A) and replacement is console.error($$$A), then console.log('debugging flow') will be replaced with console.error('debugging flow').

If callback is provided, first argument of callback has few helper functions:

  • getNode() - returns current found node
  • getMatch(m: string) - return single match
  • getMultipleMatches(m: string) - return multiple matches You can iterate over matches and return new string.