超強大的結構搜尋和取代 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,
})
`
經過大量的努力,我終於在 2022 年 12 月發布了版本 2?現在我正在開發 VSCode 擴充功能。之後我想製作一個文件網站來更好地說明如何使用astx
。
VSCode 擴充功能目前處於測試階段,請嘗試!
當我考慮這樣做時,我發現了一種類似的工具,它啟發了$
capture 語法。無論如何,我決定製作astx
有幾個原因:
grasp -e 'setValue($k, $v, true)' -R 'setValueSilently({{k}}, {{v}})' file.js
jscodeshift
的 API,可以在 JS 中用於高級用例,而這些用例在 Grasp 中可能很尷尬/不可能所以astx
的哲學是:
如果您需要了解 AST 的結構,請將程式碼貼到 AST Explorer 中。
Astx 查找模式只是 JavaScript 或 TypeScript 程式碼,可能包含佔位符通配符或其他特殊結構,例如$Or(A, B)
。一般來說,模式中非通配符或特殊結構的部分必須完全匹配。
例如,查找模式foo($a)
與使用單一參數對函數foo
任何呼叫相符。此參數可以是任何內容,並被捕獲為$a
。
替換模式與查找模式幾乎相同,只是佔位符被替換為查找模式捕獲到佔位符名稱中的任何內容,並且像$Or(A, B)
這樣的特殊查找結構在替換模式中沒有特殊含義。 (將來,可能會有特殊的替換結構對捕獲的節點執行某種轉換。)
例如,找出模式foo($a)
符合foo(1 + 2)
,則取代模式foo({ value: $a })
將產生程式碼foo({ value: 1 + 2 })
。
一般來說,以$
開頭的標識符是一個佔位符,其功能類似於通配符。佔位符分為三種類型:
$<name>
符合任何單一節點(「節點佔位符」)$$<name>
符合連續的節點清單(「陣列佔位符」)$$$<name>
:符合所有其他同級(「其餘佔位符」) <name>
(如果給出)必須以字母或數字開頭;否則標識符將不會被視為佔位符。
其餘佔位符 ( $$$
) 不能是有序列表佔位符 ( $$
) 的同級。
除非佔位符是匿名的,否則它將「捕獲」匹配的節點,這意味著您可以在替換模式中使用相同的佔位符將匹配的節點插入到生成的替換中。在 Node API 中,您也可以透過佔位符名稱存取擷取的 AST 路徑/節點。
$<name>
)這些佔位符會匹配單一節點。例如,模式[$a, $b]
與具有兩個元素的陣列表達式匹配,並且這些元素被捕獲為$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 }
.
您可以使用...$$captureName
來匹配其他屬性,例如{ foo: 1, ...$$rest }
將匹配{ foo: 1 }
, { foo: 1, bar: 2 }
, { foo: 1, bar: 2, ...props }
bar: 2, match.arrayCaptures
match.arrayPathCaptures
{ foo: 1, bar: 2, ...props }
等。例如, astx.find`{ foo: 1, ...$$rest }`.replace`{ bar: 1, ...$$rest }`
將轉換{ 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
佔位符。
其餘佔位符 ( $$$
) 不能是有序列表佔位符 ( $$
) 的同級。
一些標記為 TODO 的項目可能確實有效,但未經測試。
類型 | 支援列表匹配嗎? | 預設無序? | 筆記 |
---|---|---|---|
ArrayExpression.elements | ✅ | ||
ArrayPattern.elements | ✅ | ||
BlockStatement.body | ✅ | ||
CallExpression.arguments | ✅ | ||
Class(Declaration/Expression).implements | ✅ | ✅ | |
ClassBody.body | ✅ | ✅ | |
ComprehensionExpression.blocks | 待辦事項 | ||
DeclareClass.body | 待辦事項 | ✅ | |
DeclareClass.implements | 待辦事項 | ✅ | |
DeclareExportDeclaration.specifiers | 待辦事項 | ✅ | |
DeclareInterface.body | 待辦事項 | ||
DeclareInterface.extends | 待辦事項 | ||
DoExpression.body | 待辦事項 | ||
ExportNamedDeclaration.specifiers | ✅ | ✅ | |
Function.decorators | 待辦事項 | ||
Function.params | ✅ | ||
FunctionTypeAnnotation/TSFunctionType.params | ✅ | ||
GeneratorExpression.blocks | 待辦事項 | ||
ImportDeclaration.specifiers | ✅ | ✅ | |
(TS)InterfaceDeclaration.body | 待辦事項 | ✅ | |
(TS)InterfaceDeclaration.extends | 待辦事項 | ✅ | |
IntersectionTypeAnnotation/TSIntersectionType.types | ✅ | ✅ | |
JSX(Element/Fragment).children | ✅ | ||
JSX(Opening)Element.attributes | ✅ | ✅ | |
MethodDefinition.decorators | 待辦事項 | ||
NewExpression.arguments | ✅ | ||
ObjectExpression.properties | ✅ | ✅ | |
ObjectPattern.decorators | 待辦事項 | ||
ObjectPattern.properties | ✅ | ✅ | |
(ObjectTypeAnnotation/TSTypeLiteral).properties | ✅ | ✅ | 使用$a: $ 符合一個屬性,使用$$a: $ 或$$$a: $ 來符合多個屬性 |
Program.body | ✅ | ||
Property.decorators | 待辦事項 | ||
SequenceExpression | ✅ | ||
SwitchCase.consequent | ✅ | ||
SwitchStatement.cases | 待辦事項 | ||
TemplateLiteral.quasis/expressions | ❓ 不確定我能不能想出文法 | ||
TryStatement.guardedHandlers | 待辦事項 | ||
TryStatement.handlers | 待辦事項 | ||
TSFunctionType.parameters | ✅ | ||
TSCallSignatureDeclaration.parameters | 待辦事項 | ||
TSConstructorType.parameters | 待辦事項 | ||
TSConstructSignatureDeclaration.parameters | 待辦事項 | ||
TSDeclareFunction.params | 待辦事項 | ||
TSDeclareMethod.params | 待辦事項 | ||
EnumDeclaration.body/TSEnumDeclaration.members | 待辦事項 | ✅ | |
TSIndexSignature.parameters | 待辦事項 | ||
TSMethodSignature.parameters | 待辦事項 | ||
TSModuleBlock.body | 待辦事項 | ||
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 語句轉換為匯入。
模式中的空註解 ( /**/
) 將「提取」節點進行比對。例如,模式const x = { /**/ $key: $value }
將只將ObjectProperty
節點與$key: $value
進行比對。
解析器無法自行解析$key: $value
或知道您的意思是ObjectProperty
,而不是像const x: number = 1
中的x: number
之類的不同內容,因此使用/**/
可以讓您解決這個問題。您可以使用它來匹配本身不是有效表達式或語句的任何節點類型。例如, type T = /**/ Array<number>
將符合Array<number>
類型註解。
/**/
也適用於替換模式。
$Maybe(pattern)
匹配給定表達式或不匹配其位置的節點。例如, let $a = $Maybe(2)
將匹配let foo = 2
和let foo
(沒有初始化程序),但不會匹配let foo = 3
。
$Or(...)
匹配至少匹配一種給定模式的節點。例如$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<...>
匹配至少與一個給定類型註釋匹配的節點。例如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')
,第二個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 }
)節點捕獲的 where 條件。例如,如果您的查找模式是$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[]
(您可以使用作為第二個參數提供的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()
相同格式的導入語句,但刪除所有給定的說明符。
.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
)如果該Astx
實例至少有一個符合項,則傳回該實例,否則傳回null
。
由於.find()
、 .closest()
和.destruct()
總是傳回一個Astx
實例,即使沒有符合項,如果您只想在存在時定義值,則可以使用.find(...).matched
至少一場比賽。
.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
)傳回false
除非predicate
對於至少一項符合傳回 true。
iteratee
是使用match: Astx, index: number, parent: Astx
呼叫的函數,並傳回true
或false
。
.every(predicate)
( boolean
)傳回true
除非predicate
對於至少一個符合項傳回 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
)傳回一個Astx
實例,除了此實例中存在的擷取之外,還包含來自給定...captures
。
您可以傳遞以下類型的參數:
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
。
.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
檔案中放置執行轉換的程式碼(預設為工作目錄中的astx.ts
或astx.js
,除非您使用-t
CLI 選項指定不同的檔案)。
不過,轉換檔 API 與jscodeshift
有點不同。您可以進行以下匯出:
exports.find
(可選)要在正在轉換的檔案中尋找的模式的程式碼字串或 AST 節點。
exports.where
(可選)在exports.find
中捕獲佔位符的Where 條件。有關詳細信息,請參閱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]