JavaScript と TypeScript の非常に強力な構造検索と置換により、リファクタリングを自動化します
$<name>
)$$<name>
)$$$<name>
)$Maybe(pattern)
$Or(...)
$And(...)
$Maybe<pattern>
$Or<...>
$And<...>
$Ordered
$Unordered
constructor(backend: Backend, paths: NodePath<any>[] | Match[], options?: { withCaptures?: Match[] })
.find(...)
( Astx
).closest(...)
( Astx
).destruct(...)
( Astx
)FindOptions
FindOptions.where
( { [captureName: string]: (path: Astx) => boolean }
).find(...).replace(...)
( void
).findImports(...)
( Astx
).addImports(...)
( Astx
).removeImports(...)
( boolean
).replaceImport(...).with(...)
( boolean
).remove()
( void
).matched
( this | null
).size()
( number
)[name: `$${string}` | `$$${string}` | `$$$${string}`]
( Astx
).placeholder
( string | undefined
).node
( Node
).path
( NodePath
).code
( string
).stringValue
( string
)[Symbol.iterator]
( Iterator<Astx>
).matches
( Match[]
).match
( Match
).paths
( NodePath[]
).nodes
( Node[]
).some(predicate)
( boolean
).every(predicate)
( boolean
).filter(iteratee)
( Astx
).map<T>(iteratee)
( T[]
).at(index)
( Astx
).withCaptures(...captures)
( Astx
).type
.path
.node
.paths
.nodes
.captures
.pathCaptures
.arrayCaptures
.arrayPathCaptures
.stringCaptures
exports.find
(オプション)exports.where
(オプション)exports.replace
(オプション)exports.astx
(オプション)exports.onReport
(オプション)exports.finish
(オプション)parser
parserOptions
prettier
単純なリファクタリングは退屈で反復的な作業になる可能性があります。たとえば、コードベース全体で次の変更を加えたいとします。
// before:
rmdir ( 'old/stuff' )
rmdir ( 'new/stuff' , true )
// after:
rmdir ( 'old/stuff' )
rmdir ( 'new/stuff' , { force : true } )
rmdir
への多数の呼び出しを手動で変更するのは最悪です。正規表現置換を使用してみることもできますが、それは面倒で、正規表現を熱心に使用しない限り、空白文字や改行を十分に許容できません。 jscodeshift
使用することもできますが、このような単純な場合には時間がかかりすぎて、必要以上に難しく感じ始めます...
今では、より良いオプションがあります... astx
使用すると、自信を持ってリファクタリングできます。
astx
--find ' rmdir($path, $force) '
--replace ' rmdir($path, { force: $force }) '
これはastx
パターンの基本的な例です。これは、プレースホルダーやその他の特殊な一致構造を含めることができる単なる JS または TS コードです。 astx
、 $path
と$force
代わりに任意の式を受け入れて、パターンに一致するコードを検索します。次に、 astx
各一致を置換パターンで置き換え、キャプチャした式を$path
( 'new/stuff'
) と$force
( true
) に置き換えます。
しかし、これはほんの始まりにすぎません。 astx
パターンはこれよりもはるかに複雑かつ強力である可能性があり、非常に高度なユースケースのために使用できる直感的な API があります。
for ( const match of astx . find `rmdir($path, $force)` ) {
const { $path , $force } = match
// do stuff with $path.node, $path.code, etc...
}
Do not access Object.prototype method 'hasOwnProperty' from target object
エラーが大量に発生しましたか?
// astx.js
exports . find = `$a.hasOwnProperty($b)`
exports . replace = `Object.hasOwn($a, $b)`
最近、仕事のために次の変更を加えたいと思いました。
// before
const pkg = OrgPackage ( {
subPackage : [
'services' ,
async ? 'async' : 'blocking' ,
... namespace ,
Names . ServiceType ( { resource , async } ) ,
] ,
} )
// after
const pkg = [
... OrgPackage ( ) ,
'services' ,
async ? 'async' : 'blocking' ,
... namespace ,
Names . ServiceType ( { resource , async } ) ,
]
これは、リスト マッチングを使用して簡単に実行できます。
// astx.js
exports . find = `OrgPackage({subPackage: [$$p]})`
exports . replace = `[...OrgPackage(), $$p]`
// astx.js
exports . find = `const $id = require('$source')`
exports . replace = `import $id from '$source'`
// astx.js
export const find = `if ($a) { if ($b) $body }`
export const replace = `if ($a && $b) $body`
jscodeshift-add-imports
には、次のパターンに従ったテスト ケースが多数ありました。
it ( `leaves existing default imports untouched` , function ( ) {
const code = `import Baz from 'baz'`
const root = j ( code )
const result = addImports ( root , statement `import Foo from 'baz'` )
expect ( result ) . to . deep . equal ( { Foo : 'Baz' } )
expect ( root . toSource ( ) ) . to . equal ( code )
} )
次のように、もっとドライにしたいと思いました。
it ( `leaves existing default imports untouched` , function ( ) {
testCase ( {
code : `import Baz from 'baz'` ,
add : `import Foo from 'baz'` ,
expectedCode : `import Baz from 'baz'` ,
expectedReturn : { Foo : 'Baz' } ,
} )
} )
上記の変換は次のとおりです。 (もちろん、予想されるコードが異なる場合などに備えて、これのバリエーションをいくつか実行する必要がありました)
exports . find = `
const code = $code
const root = j(code)
const result = addImports(root, statement`$add`)
expect(result).to.deep.equal($expectedReturn)
expect(root.toSource()).to.equal(code)
`
exports . replace = `
testCase({
code: $code,
add: `$add`,
expectedCode: $code,
expectedReturn: $expectedReturn,
})
`
多大な労力を費やした結果、ようやくバージョン 2 が 2022 年 12 月にリリースされました。現在、VSCode 拡張機能に取り組んでいます。その後、 astx
使用方法をよりわかりやすく説明するドキュメント Web サイトを作成したいと考えています。
VSCode 拡張機能は現在ベータ版ですが、ぜひ試してみてください。
これを作ろうと考えていたときに、 $
Capture 構文のインスピレーションとなった同様のツールである Graps を発見しました。とにかくastx
作成することに決めた理由はいくつかあります。
grasp -e 'setValue($k, $v, true)' -R 'setValueSilently({{k}}, {{v}})' file.js
jscodeshift
のような API が欲しかったastx
の哲学は次のとおりです。
AST の構造について学ぶ必要がある場合は、コードを AST Explorer に貼り付けます。
Astx 検索パターンは、プレースホルダーワイルドカードや$Or(A, B)
などの他の特別な構造を含む可能性がある単なる JavaScript または TypeScript コードです。一般に、ワイルドカードや特別な構成要素ではないパターンの部分は正確に一致する必要があります。
たとえば、検索パターンfoo($a)
、単一の引数を持つ関数foo
の呼び出しに一致します。引数には何でも指定でき、 $a
としてキャプチャされます。
置換パターンは、検索パターンとほぼ同じですが、プレースホルダーは検索パターンによってプレースホルダー名に取り込まれたものに置き換えられ、 $Or(A, B)
のような特別な検索構造は置換パターンでは特別な意味を持ちません。 (将来的には、キャプチャされたノードに対して何らかの変換を実行する特別な置換構造が登場する可能性があります。)
たとえば、検索パターンfoo($a)
foo(1 + 2)
に一致し、置換パターンfoo({ value: $a })
コードfoo({ value: 1 + 2 })
を生成します。
一般に、 $
で始まる識別子は、ワイルドカードのように機能するプレースホルダーです。プレースホルダーには 3 つのタイプがあります。
$<name>
任意の単一ノード (「ノード プレースホルダー」) に一致します$$<name>
連続するノードのリスト (「配列プレースホルダー」) に一致します。$$$<name>
: 他のすべての兄弟と一致します (「残りのプレースホルダー」) <name>
(指定する場合) は文字または数字で始まる必要があります。そうしないと、識別子はプレースホルダーとして扱われません。
残りのプレースホルダー ( $$$
) は、順序付きリストのプレースホルダー ( $$
) の兄弟であってはなりません。
プレースホルダーが匿名でない限り、一致したノードを「キャプチャ」します。つまり、置換パターンで同じプレースホルダーを使用して、一致したノードを生成された置換に補間できます。 Node API では、プレースホルダー名を介してキャプチャされた AST パス/ノードにアクセスすることもできます。
$<name>
)これらのプレースホルダーは単一のノードと一致します。たとえば、パターン[$a, $b]
は 2 つの要素を持つ配列式と一致し、それらの要素は$a
および$b
としてキャプチャされます。
$$<name>
)これらのプレースホルダーは、連続するノードのリストと一致します。たとえば、パターン[1, $$a, 2, $$b]
は、最初の要素が1
、後続の要素が2
である配列式と一致します。 1
から最初の2
までの要素は$$a
としてキャプチャされ、最初の2
以降の要素は$$b
としてキャプチャされます。
$$$<name>
)これらのプレースホルダーは、他のもので一致しなかった残りの兄弟と一致します。たとえば、パターン[1, $$$a, 2]
は、任意のインデックスに要素1
と2
持つ配列式に一致します。他の要素 ( 1
と2
の追加の出現を含む) は、 $$$a
としてキャプチャされます。
名前のないプレースホルダーを使用して、ノードをキャプチャせずに一致させることができます。 $
任意の単一ノードに一致し、 $$
ノードの連続リストに一致し、 $$$
他のすべての兄弟に一致します。
同じキャプチャ プレースホルダーを複数回使用する場合、後続の位置は、プレースホルダーの最初の出現時にキャプチャされたものと一致する必要があります。
たとえば、パターンfoo($a, $a, $b, $b)
、次のfoo(1, 1, {foo: 1}, {foo: 1})
のみに一致します。
foo ( 1 , 1 , { foo : 1 } , { foo : 1 } ) // match
foo ( 1 , 2 , { foo : 1 } , { foo : 1 } ) // no match
foo ( 1 , 1 , { foo : 1 } , { bar : 1 } ) // no match
注: 配列キャプチャ プレースホルダ ( $$a
) と残りキャプチャ プレースホルダ ( $$$a
) は現在、後方参照をサポートしていません。
ObjectExpression
(別名オブジェクト リテラル) パターンは、同じプロパティを持つコード内の任意のObjectExpression
と任意の順序で一致します。プロパティが欠落しているか追加されている場合は一致しません。たとえば、 { foo: 1, bar: $bar }
は、 { foo: 1, bar: 2 }
または{ bar: 'hello', foo: 1 }
には一致しますが、 { foo: 1 }
または{ foo: 1, bar: 2, baz: 3 }
は一致しません{ foo: 1, bar: 2, baz: 3 }
。
...$$captureName
使用して追加のプロパティと一致させることができます。たとえば、 { foo: 1, ...$$rest }
は、 { foo: 1 }
、 { foo: 1, bar: 2 }
、 { foo: 1, bar: 2, ...props }
と一致します。 { foo: 1, bar: 2, ...props }
など。追加のプロパティはmatch.arrayCaptures
/ match.arrayPathCaptures
でキャプチャされ、置換式に分散できます。たとえば、 astx.find`{ foo: 1, ...$$rest }`.replace`{ bar: 1, ...$$rest }`
{ foo: 1, qux: {}, ...props }
を変換します。 { foo: 1, qux: {}, ...props }
を{ bar: 1, qux: {}, ...props }
に変換します。
/^$$[a-z0-9]+$/i
の形式ではないスプレッド プロパティはキャプチャ プレースホルダーではありません。たとえば、 { ...foo }
{ ...foo }
のみに一致します。 { ...$_$foo }
は{ ...$$foo }
のみに一致します (先頭の$_
$
のエスケープです)。
現在、プロパティを特定の順序で照合する方法はありませんが、将来的には追加される可能性があります。
AST にノードのリストがある場合、多くの場合、 $$
で始まるプレースホルダーを使用して複数の要素を照合できます。たとえば、 [$$before, 3, $$after]
は、要素3
を含む任意の配列式に一致します。最初の3
つより前の要素は$$before
にキャプチャされ、最初の3
以降の要素は$$after
にキャプチャされます。
これはブロックステートメントでも機能します。たとえば、 function foo() { $$before; throw new Error('test'); $$after; }
throw new Error('test')
を含むfunction foo()
と一致し、その throw ステートメントの前後のステートメントはそれぞれ$$before
と$$after
でキャプチャされます。
場合によっては、リストの照合はデフォルトで順序付けされますが、場合によっては順序付けされません。たとえば、次の表に示すように、 ObjectExpression
プロパティの一致はデフォルトでは順序付けされていません。 $$
プレースホルダーまたは特別な$Ordered
プレースホルダーを使用すると、順序付けされた一致が強制されます。 $$$
プレースホルダーまたは特別な$Unordered
ホルダーを使用すると、順序なしの一致が強制されます。
$$$
で始まるプレースホルダーを使用すると、それは「残り」キャプチャとして扱われ、一致式の他のすべての要素は順序どおりに一致しません。たとえば、 import {a, b, $$$rest} from 'foo'
import {c, b, d, e, a} from 'foo'
と一致し、指定子c
、 d
、およびe
を$$$rest
に置きます。 $$$rest
プレースホルダー。
残りのプレースホルダー ( $$$
) は、順序付きリストのプレースホルダー ( $$
) の兄弟であってはなりません。
TODO とマークされた一部の項目は実際に動作する可能性がありますが、テストされていません。
タイプ | リストマッチングをサポートしますか? | デフォルトでは順序付けされていませんか? | 注意事項 |
---|---|---|---|
ArrayExpression.elements | ✅ | ||
ArrayPattern.elements | ✅ | ||
BlockStatement.body | ✅ | ||
CallExpression.arguments | ✅ | ||
Class(Declaration/Expression).implements | ✅ | ✅ | |
ClassBody.body | ✅ | ✅ | |
ComprehensionExpression.blocks | TODO | ||
DeclareClass.body | TODO | ✅ | |
DeclareClass.implements | TODO | ✅ | |
DeclareExportDeclaration.specifiers | TODO | ✅ | |
DeclareInterface.body | TODO | ||
DeclareInterface.extends | TODO | ||
DoExpression.body | TODO | ||
ExportNamedDeclaration.specifiers | ✅ | ✅ | |
Function.decorators | TODO | ||
Function.params | ✅ | ||
FunctionTypeAnnotation/TSFunctionType.params | ✅ | ||
GeneratorExpression.blocks | TODO | ||
ImportDeclaration.specifiers | ✅ | ✅ | |
(TS)InterfaceDeclaration.body | TODO | ✅ | |
(TS)InterfaceDeclaration.extends | TODO | ✅ | |
IntersectionTypeAnnotation/TSIntersectionType.types | ✅ | ✅ | |
JSX(Element/Fragment).children | ✅ | ||
JSX(Opening)Element.attributes | ✅ | ✅ | |
MethodDefinition.decorators | TODO | ||
NewExpression.arguments | ✅ | ||
ObjectExpression.properties | ✅ | ✅ | |
ObjectPattern.decorators | TODO | ||
ObjectPattern.properties | ✅ | ✅ | |
(ObjectTypeAnnotation/TSTypeLiteral).properties | ✅ | ✅ | 1 つのプロパティに一致するには$a: $ を使用し、複数のプロパティに一致するには$$a: $ または$$$a: $ を使用します |
Program.body | ✅ | ||
Property.decorators | TODO | ||
SequenceExpression | ✅ | ||
SwitchCase.consequent | ✅ | ||
SwitchStatement.cases | TODO | ||
TemplateLiteral.quasis/expressions | ❓ 構文を思いつくかどうかわからない | ||
TryStatement.guardedHandlers | TODO | ||
TryStatement.handlers | TODO | ||
TSFunctionType.parameters | ✅ | ||
TSCallSignatureDeclaration.parameters | TODO | ||
TSConstructorType.parameters | TODO | ||
TSConstructSignatureDeclaration.parameters | TODO | ||
TSDeclareFunction.params | TODO | ||
TSDeclareMethod.params | TODO | ||
EnumDeclaration.body/TSEnumDeclaration.members | TODO | ✅ | |
TSIndexSignature.parameters | TODO | ||
TSMethodSignature.parameters | TODO | ||
TSModuleBlock.body | TODO | ||
TSTypeLiteral.members | ✅ | ✅ | |
TupleTypeAnnotation/TSTupleType.types | ✅ | ||
(TS)TypeParameterDeclaration.params | ✅ | ||
(TS)TypeParameterInstantiation.params | ✅ | ||
UnionTypeAnnotation/TSUnionType.types | ✅ | ✅ | |
VariableDeclaration.declarations | ✅ | ||
WithStatement.body | with ステートメントを使用する人は... |
'$foo'
のような単なるプレースホルダーである文字列は、任意の文字列と一致し、その内容をmatch.stringCaptures.$foo
にキャプチャします。識別子の場合と同じエスケープ規則が適用されます。これは、 `$foo`
のようなテンプレート リテラルや、 doSomething`$foo`
のようなタグ付きテンプレート リテラルにも機能します。
これは、インポート ステートメントを操作する場合に役立ちます。たとえば、「require ステートメントを import に変換する」を参照してください。
パターン内の空のコメント ( /**/
) は、一致するノードを「抽出」します。たとえば、パターンconst x = { /**/ $key: $value }
は、単にObjectProperty
ノードを$key: $value
と照合します。
パーサーは、 $key: $value
それ自体で解析することはできず、 const x: number = 1
のx: number
のような別のものではなく、 ObjectProperty
意味していることを認識できないため、 /**/
使用することで次のことが可能になります。これを回避するには。これを使用すると、それ自体では有効な式またはステートメントではないノード タイプと一致させることができます。たとえば、 type T = /**/ Array<number>
Array<number>
タイプの注釈と一致します。
/**/
置換パターンでも機能します。
$Maybe(pattern)
指定された式に一致するか、その場所にノードが存在しないかのいずれかに一致します。たとえば、 let $a = $Maybe(2)
let foo = 2
およびlet foo
(初期化子なし) には一致しますが、 let foo = 3
には一致しません。
$Or(...)
指定されたパターンの少なくとも 1 つと一致するノードを照合します。たとえば$Or(foo($$args), {a: $value})
foo
への呼び出しと、 a
プロパティのみを持つオブジェクト リテラルに一致します。
$And(...)
指定されたパターンのすべてに一致するノードと一致します。これは、特定のプレースホルダーにキャプチャできるノードの種類を絞り込む場合に主に役立ちます。たとえば、 let $a = $And($init, $a + $b)
初期化子が$a + $b
と一致するlet
宣言に一致し、初期化子を$init
としてキャプチャします。
$Maybe<pattern>
指定されたタイプの注釈に一致するか、その場所にノードがないかのいずれかに一致します。たとえば、 let $a: $Maybe<number>
let foo: number
とlet foo
(型注釈なし) には一致しますが、 let foo: string``let foo: string
は一致しません。
$Or<...>
指定された型アノテーションの少なくとも 1 つと一致するノードと一致します。たとえば、 let $x: $Or<number[], string[]>
タイプnumber[]
またはstring[]
のlet
宣言に一致します。
$And<...>
指定されたタイプの注釈のすべてに一致するノードと一致します。これは、特定のプレースホルダーにキャプチャできるノードの種類を絞り込むのに主に役立ちます。たとえば、 let $a: $And<$type, $elem[]>
型注釈が$elem[]
と一致するlet
宣言に一致し、型注釈を$type
としてキャプチャします。
$Ordered
パターンが同じ順序で兄弟ノードと一致するように強制します。
$Unordered
パターンが任意の順序で兄弟ノードと一致するように強制します。
import { NodePath } from 'astx'
これは、 ast-types
と同じNodePath
インターフェースですが、メソッドのタイプ定義にいくつかの改良が加えられています。 astx
、将来的にさまざまなパーサーをサポートすることを期待して、コードを走査するためにast-types
使用します。
import { Astx } from 'astx'
constructor(backend: Backend, paths: NodePath<any>[] | Match[], options?: { withCaptures?: Match[] })
backend
使用されているパーサー/ジェネレーターの実装です。
paths
Astx
メソッドで検索/操作するNodePath
またはMatch
を指定します。
.find(...)
( Astx
)このインスタンスの開始パス内で指定されたパターンに一致するものを検索し、一致したものを含むAstx
インスタンスを返します。
変換関数に渡された最初のインスタンスでastx.find('foo($$args)')
を呼び出すと、ファイル内のfoo
へのすべての呼び出しが検索され、それらの一致が新しいAstx
インスタンスで返されます。
返されたインスタンスのメソッドは、一致したパス上でのみ動作します。
たとえば、 astx.find('foo($$args)').find('$a + $b')
を実行すると、2 番目のfind
呼び出しでは、 foo($$args)
に一致する範囲内で$a + $b
のみが検索されます) foo($$args)
、ファイル内の任意の場所ではなく。
.find
メソッドまたはタグ付きテンプレート リテラルとして呼び出すことができます。
.find`pattern`
.find(pattern: string | string[] | Node | Node[] | NodePath | NodePath[] | ((wrapper: Astx) => boolean), options?: FindOptions)
パターンを文字列として指定する場合、それは有効な式またはステートメントである必要があります。それ以外の場合は、すでに解析または構築した有効な AST ノードである必要があります。タグ付きテンプレート リテラル内の文字列、AST ノード、AST ノードの配列、およびAstx
インスタンスを補間できます。
たとえば、 astx.find`${t.identifier('foo')} + 3`
を実行できます。
または、次のようにして複数のステートメントを一致させることもできます
astx . find `
const $a = $b;
$$c;
const $d = $a + $e;
`
これは、(たとえば) ステートメントconst foo = 1; const bar = foo + 5;
、間に任意の数のステートメントを含めます。
.closest(...)
( Astx
) .find()
と似ていますが、子孫に至るのではなく、AST の祖先を上に検索します。指定されたパターンに一致する各入力パスの最も近い包含ノードを検索します。
.destruct(...)
( Astx
) .find()
と似ていますが、入力パスの子孫をパターンに対してテストしません。パターンに一致する入力パスのみが結果に含まれます。
FindOptions
次のオプションのプロパティを持つオブジェクト:
FindOptions.where
( { [captureName: string]: (path: Astx) => boolean }
)ノードキャプチャの条件。たとえば、検索パターンが$a()
の場合、 { where: { $a: astx => /foo|bar/.test(astx.node.name) } }
とすることができ、これは引数なしの呼び出しのみに一致します。 foo
またはbar
に。
.find(...).replace(...)
( void
) root
内の指定されたパターンに一致するものを検索して置換します。
.replace
呼び出す方法はいくつかあります。 .find
上記のどの方法でも呼び出すことができます。
.find(...).replace`replacement`
.find(...).replace(replacement: string | string | Node | Node[])
.find(...).replace(replacement: (match: Astx, parse: ParsePattern) => string)
.find(...).replace(replacement: (match: Astx, parse: ParsePattern) => Node | Node[])
置換を文字列として指定する場合、それは有効な式またはステートメントである必要があります。すでに解析または構築した AST ノードとして置き換えを行うことができます。または、置換関数を指定することもできます。この関数は一致するたびに呼び出され、文字列またはNode | Node[]
を返す必要があります。 Node | Node[]
(2 番目の引数として提供されるタグ付きテンプレート文字列parse
関数を使用して、コードを文字列に解析できます。たとえば、すべての引数ゼロの関数呼び出しで関数名を大文字にすることができます ( foo(); bar()
はFOO(); BAR()
) は次のようになります。
astx
.find`$fn()`
.replace(({ captures: { $fn } }) => `${$fn.name.toUpperCase()}()`)
.findImports(...)
( Astx
)追加の指定子を許容し、型インポートが要求された場合に同じ名前の値インポートと一致するインポートを検索するための.find()
の便利なバージョン。
たとえば、 .findImports`import $a from 'a'`
import A, { b, c } from 'a'
またはimport { default as a } from 'a'
に一致し、 $a
をキャプチャしますが、 .find`import $a from 'a'`
これらのどちらの場合にも一致しません。
パターンには import ステートメントのみを含める必要があります。
.addImports(...)
( Astx
) .findImports()
と似ていますが、見つからなかったインポートを追加します。たとえば、ソースコードが与えられたとします。
import { foo , type bar as qux } from 'foo'
import 'g'
そして手術は
const { $bar } = astx . addImports `
import type { bar as $bar } from 'foo'
import FooDefault from 'foo'
import * as g from 'g'
`
出力は次のようになります
import FooDefault , { foo , type bar as qux } from 'foo'
import * as g from 'g'
$bar
で識別子qux
キャプチャします。
.removeImports(...)
( boolean
) .findImports()
と同じ形式の import ステートメントを受け取りますが、指定されたすべての指定子を削除します。
.replaceImport(...).with(...)
( boolean
)単一のインポート指定子を別のインポート指定子に置き換えます。たとえば、入力が与えられたとすると、
import { Match , Route , Location } from 'react-router-dom'
import type { History } from 'history'
そして操作
astx . replaceImport `
import { Location } from 'react-router-dom'
` . with `
import type { Location } from 'history'
`
出力は次のようになります
import { Match , Route } from 'react-router-dom'
import type { History , Location } from 'history'
検索パターンと置換パターンの両方に、単一の指定子を持つ単一のインポート ステートメントが含まれている必要があります。
.remove()
( void
)このAstx
インスタンスの.find()
またはフォーカスされたキャプチャから一致を削除します。
.matched
( this | null
)一致するものが少なくとも 1 つある場合はこのAstx
インスタンスを返し、それ以外の場合はnull
を返します。
.find()
、 .closest()
、および.destruct()
は常にAstx
インスタンスを返すため、一致するものがなかった場合でも、定義された値のみが必要な場合は.find(...).matched
使用できます。少なくとも1試合はあった。
.size()
( number
)このインスタンスを返した.find()
または.closest()
呼び出しからの一致の数を返します。
[name: `$${string}` | `$$${string}` | `$$$${string}`]
( Astx
)指定されたname
のキャプチャに焦点を当てたAstx
インスタンスを取得します。
たとえば、次のようにすることができます。
for ( const { $v } of astx . find `process.env.$v` ) {
report ( $v . code )
}
.placeholder
( string | undefined
)このインスタンスが表すプレースホルダーの名前。例えば:
const match = astx . find `function $fn($$params) { $$body }`
console . log ( match . placeholder ) // undefined
const { $fn , $$params } = match
console . log ( $fn . placeholder ) // $fn
console . log ( $$params . placeholder ) // $$params
.node
( Node
)最初に一致した最初のノードを返します。一致するものがない場合はエラーをスローします。
.path
( NodePath
)最初に一致した最初のパスを返します。一致するものがない場合はエラーをスローします。
.code
( string
)最初に一致した最初のノードからコードを生成します。一致するものがない場合はエラーをスローします。
.stringValue
( string
)フォーカスされたキャプチャが文字列キャプチャの場合、最初のノードの文字列値を返します。一致するものがない場合はエラーをスローします。
[Symbol.iterator]
( Iterator<Astx>
)各一致を反復処理し、各一致のAstx
インスタンスを返します。
.matches
( Match[]
)このインスタンスを返した.find()
または.closest()
呼び出しから一致を取得します。
.match
( Match
)このインスタンスを返した.find()
または.closest()
呼び出しから最初に一致したものを取得します。
一致するものがなかった場合はエラーをスローします。
.paths
( NodePath[]
) .find()
および.closest()
検索するパスを返します。このインスタンスが.find()
または.closest()
によって返された場合、これらは検索パターンに一致したノードのパスになります。
.nodes
( Node[]
) .find()
および.closest()
検索するノードを返します。このインスタンスが.find()
または.closest()
によって返された場合、これらは検索パターンに一致したノードです。
.some(predicate)
( boolean
) predicate
少なくとも 1 つの一致に対して true を返さない限り、 false
を返します。
iteratee
match: Astx, index: number, parent: Astx
で呼び出され、 true
またはfalse
返す関数です。
.every(predicate)
( boolean
) true
を返します。unelss predicate
少なくとも 1 つの一致に対して false を返します。
iteratee
match: Astx, index: number, parent: Astx
で呼び出され、 true
またはfalse
返す関数です。
.filter(iteratee)
( Astx
)一致するものをフィルタリングします。
iteratee
match: Astx, index: number, parent: Astx
で呼び出され、 true
またはfalse
返す関数です。 iteratee
true
返す一致のみが結果に含まれます。
.map<T>(iteratee)
( T[]
)一致をマッピングします。
iteratee
match: Astx, index: number, parent: Astx
で呼び出され、結果の配列に含める値を返す関数です。
.at(index)
( Astx
)指定されたindex
で一致するものを選択します。
.withCaptures(...captures)
( Astx
)このインスタンスに存在するキャプチャに加えて、指定された...captures
キャプチャからのキャプチャを含むAstx
インスタンスを返します。
次の種類の引数を渡すことができます。
Astx
インスタンス - インスタンスからのすべてのキャプチャが含まれます。Astx[placeholder]
インスタンス - 指定されたプレースplaceholder
のキャプチャが含まれます。{ $name: Astx[placeholder] }
- 指定されたプレースplaceholder
のキャプチャ。名前が$name
に変更されます。Match
import { type Match } from 'astx'
.type
一致のタイプ: 'node'
または'nodes'
。
.path
一致したノードのNodePath
。 type
が'nodes'
の場合、これはpaths[0]
になります。
.node
一致したNode
。 type
が'nodes'
の場合、これはnodes[0]
になります。
.paths
一致したノードのNodePaths
。
.nodes
一致したNode
s。
.captures
Node
は一致パターンのプレースホルダーから取得されます。たとえば、パターンがfoo($bar)
の場合、 .captures.$bar
最初の引数のNode
になります。
.pathCaptures
NodePath
は、一致パターン内のプレースホルダーから取得されます。たとえば、パターンがfoo($bar)
の場合、 .pathCaptures.$bar
最初の引数のNodePath
になります。
.arrayCaptures
Node[]
は、一致パターンの配列プレースホルダーから取得されます。たとえば、パターンがfoo({ ...$bar })
の場合、 .arrayCaptures.$bar
オブジェクト プロパティのNode[]
になります。
.arrayPathCaptures
NodePath[]
、一致パターンの配列プレースホルダーからキャプチャされます。たとえば、パターンがfoo({ ...$bar })
の場合、 .pathArrayCaptures.$bar
オブジェクト プロパティのNodePath[]
になります。
.stringCaptures
一致パターン内の文字列プレースホルダーから取得された文字列値。たとえば、パターンがimport foo from '$foo'
の場合、 stringCaptures.$foo
インポート パスになります。
jscodeshift
と同様に、変換を実行するコードを.ts
または.js
ファイルに配置できます ( -t
CLI オプションで別のファイルを指定しない限り、デフォルトは作業ディレクトリ内のastx.ts
またはastx.js
です)。
ただし、変換ファイル API はjscodeshift
とは少し異なります。次のエクスポートを行うことができます。
exports.find
(オプション)変換されるファイル内で検索するパターンのコード文字列または AST ノード。
exports.where
(オプション)ここで、 exports.find
のキャプチャ プレースホルダーの条件。詳細については、 FindOptions.where
( { [captureName: string]: (path: NodePath<any>) => boolean }
)」を参照してください。
exports.replace
(オプション) exports.find
の一致を置換するコード文字列、AST ノード、または置換関数。
関数の引数は.find().replace()
で説明したものと同じです。
exports.astx
(オプション) Astx
APIを使用して任意の変換を実行する関数。次のプロパティを持つオブジェクトを使用して呼び出されます。
file
( string
) - 変換されるファイルへのパスsource
( string
) - 変換されるファイルのソース コードastx
( Astx
) - Astx
API インスタンスt
( AstTypes
) - 選択したパーサーのast-types
定義expression
- コードを式として解析するためのタグ付きテンプレート リテラルstatement
- コードをステートメントとして解析するためのタグ付きテンプレート リテラルstatements
- コードをステートメントの配列として解析するためのタグ付きテンプレート リテラルreport
( (message: unknown) => void
)mark
( (...matches: Astx[]) => void
) - vscode-astx などの一致リストに表示される指定された一致をマークします。 jscodeshift
とは異なり、変換関数は非同期にすることができ、変換されたコードを返す必要はありませんが、 string
を返すことができます。 null
返してファイルをスキップすることもできます。
exports.onReport
(オプション) exports.astx
関数からreport(x)
を呼び出す場合、これはonReport({ file, report: x })
で呼び出されます。
複数のワーカー スレッドを使用している場合、 onReport
親プロセスで呼び出されるため、レポート メッセージはシリアル化可能な値である必要があります。これにより、変換ですべてのワーカーからレポートを収集できるようになります (その後、 finish
でそれらのワーカーを使用して何かを実行できる可能性があります)。
onReport
Promise
返した場合、それは待機されます。
exports.finish
(オプション)これは、すべての入力ファイルに対して変換が実行された後に呼び出されます。
複数のワーカー スレッドを使用している場合、 finish
親プロセスで呼び出されます。 onReport
とfinish
一緒に使用して、各入力ファイルから情報を収集し、最後に何らかの組み合わせた出力を生成できます。
finish
Promise
返した場合は待機されます。
astx
次の場所での構成をサポートします ( cosmiconfig
経由)。
astx
プロパティ.astxrc
ファイル.astxrc.json
、 .astxrc.yaml
、 .astxrc.yml
、 .astxrc.js
、または.astxrc.cjs
ファイルastx.config.js
またはastx.config.cjs
CommonJS モジュールコードベースが prettier でフォーマットされている場合は、最初にこれを試してみることをお勧めします。
{
"parser" : " babel/auto " ,
"parserOptions" : {
"preserveFormat" : " generatorHack "
}
}
(または CLI オプションとして)
--parser babel/auto --parserOptions '{"preserveFormat": "generatorHack"}'
これが失敗した場合はparser: 'recast/babel/auto'
または非/auto
パーサーを試してください。
走行距離はrecast
によって異なる場合があります。 JS と TS の新しい構文機能をすぐに最新の状態に保つことができず、無効な構文が出力されるのを何度も見てきました。
これからは、 @babel/generator
またはprettier
使用して変更された AST を出力し、未変更のノードに対して元のソースをそのまま使用するためのフックを使用して、信頼性の高いソリューションに取り組むつもりです。
parser
使用するパーサー。オプション:
babel/auto
(デフォルト)babel
( babel/auto
より高速ですが、代わりにデフォルトの解析オプションを使用します。 parserOptions
の構成が必要な場合があります)recast/babel
recast/babel/auto
babel/auto
babel 設定が存在する場合は、そこから解析オプションを自動的に決定します。 babel
代わりに固定解析オプションを使用するため、 babel/auto
より高速ですが、 parserOptions
設定が必要な場合があります。 recast/babel(/auto)
オプションは、フォーマットを保持するためにrecast
を使用します。いくつかのファイルでrecast
出力の無効な構文が確認されたため、注意して使用してください。
parserOptions
パーサーに渡すオプション。現時点では、これは@babel/parser
オプションと次の追加オプションのみです。
preserveFormat
(適用対象: babel
、 babel/auto
) preserveFormat: 'generatorHack'
、内部@babel/generator
API をハイジャックすることによって、変更されていないすべてのノードの形式を保存する実験的なハックを使用します。prettier
false
の場合、変換されたソース コードを再フォーマットするためにprettier
を使用しないでください。デフォルトはtrue
です。
Astx には、変換を実行するための CLI が含まれています。 CLI は指定されたファイルを処理し、変更内容の差分を出力し、変更を書き込むかどうかを確認するプロンプトを表示します。
デフォルトでは、プロジェクトにインストールされているバージョンとプロジェクトの babel 構成 (存在する場合) を使用して babel で解析します。 recast
使用して出力の書式を保持したい場合は、 --parser recast/babel
渡すことができますが、その出力に構文エラーが表示されることがあります。
jscodeshift
とは異なり、 prettier
がプロジェクトにインストールされている場合、変換されたコードはprettier
でフォーマットされます。
Usage:
astx -f <code> [<files...>] [<directories...>]
Searches for the -f pattern in the given files and directories
and prints out the matches in context
astx -f <code> -r <code> [<files...>] [<directories...>]
Quick search and replace in the given files and directories
(make sure to quote code)
Example:
astx -f 'rmdir($path, $force)' -r 'rmdir($path, { force: $force })' src
astx -t <transformFile> [<files ...>] [<directories ...>]
Applies a transform file to the given files and directories
astx [<files ...>] [<directories ...>]
Applies the default transform file (astx.ts or astx.js in working directory)
to the given files and directories
Options:
--help Show help [boolean]
--version Show version number [boolean]
-t, --transform path to the transform file. Can be either a local path or
url. Defaults to ./astx.ts or ./astx.js if --find isn't
given
--parser parser to use (options: babel, babel/auto, recast/babel,
recast/babel/auto) [string]
--parserOptions options for parser [string]
-f, --find search pattern [string]
-r, --replace replace pattern [string]
-y, --yes don't ask for confirmation before writing changes
[boolean]
--gitignore ignore gitignored files [boolean] [default: true]
--workers number of worker threads to use [number]