بحث هيكلي قوي للغاية واستبدال 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
يدويًا سيكون أمرًا سيئًا. يمكنك محاولة استخدام استبدال regex، لكنه غير مناسب ولن يتحمل المسافات البيضاء وفواصل الأسطر جيدًا إلا إذا كنت تعمل بجد في regex. يمكنك حتى استخدام 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
أكثر تعقيدًا وقوة من ذلك، وبالنسبة لحالات الاستخدام المتقدمة حقًا، فهي تحتوي على واجهة برمجة تطبيقات بديهية يمكنك استخدامها:
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 بعد الكثير من العمل الشاق؟ أعمل الآن على ملحق VSCode. بعد ذلك أريد إنشاء موقع ويب للتوثيق يوضح بشكل أفضل كيفية استخدام astx
.
ملحق VSCode موجود حاليًا في مرحلة تجريبية، لكن جربه!
بينما كنت أفكر في القيام بذلك، اكتشفت أداة فهم، وهي أداة مشابهة ألهمت بناء جملة الالتقاط $
. هناك عدة أسباب جعلتني أقرر إنشاء astx
على أي حال:
grasp -e 'setValue($k, $v, true)' -R 'setValueSilently({{k}}, {{v}})' file.js
jscodeshift
يمكنني استخدامها في JS لحالات الاستخدام المتقدمة التي ربما تكون غير ملائمة/مستحيلة في Grasp لذا فإن فلسفة astx
هي:
الصق الكود الخاص بك في AST Explorer إذا كنت تريد التعرف على بنية AST.
أنماط البحث عن 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 }
إلخ. سيتم التقاط الخصائص الإضافية في match.arrayCaptures
/ match.arrayPathCaptures
، ويمكن نشرها في تعبيرات الاستبدال. على سبيل المثال، 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; }
سوف يتطابق مع function foo()
التي تحتوي على throw new Error('test')
، وسيتم التقاط العبارات قبل وبعد بيان الرمي في $$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 | الذي يستخدم مع البيانات ... |
السلسلة التي هي مجرد عنصر نائب مثل '$foo'
ستطابق أي سلسلة وتلتقط محتوياتها في match.stringCaptures.$foo
. تنطبق نفس قواعد الهروب على المعرفات. يعمل هذا أيضًا مع القيم الحرفية للنماذج مثل `$foo`
والقيم الحرفية للنماذج ذات العلامات مثل doSomething`$foo`
.
يمكن أن يكون هذا مفيدًا للعمل مع بيانات الاستيراد. على سبيل المثال، راجع تحويل بيانات الطلب إلى الواردات.
التعليق الفارغ ( /**/
) في النمط سوف "يستخرج" عقدة للمطابقة. على سبيل المثال، النمط const x = { /**/ $key: $value }
سيطابق فقط عقد ObjectProperty
مقابل $key: $value
.
لن يتمكن المحلل اللغوي من تحليل $key: $value
بمفرده أو معرفة أنك تقصد ObjectProperty
، بدلاً من شيء مختلف مثل x: number
في const x: number = 1
، لذا فإن استخدام /**/
يمكّنك للعمل حول هذا. يمكنك استخدام هذا لمطابقة أي نوع عقدة لا يمثل تعبيرًا أو بيانًا صالحًا في حد ذاته. على سبيل المثال 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)
سيطابق تصريحات let
حيث يتطابق المُهيئ مع $a + $b
، ويلتقط المُهيئ كـ $init
.
$Maybe<pattern>
يطابق إما التعليق التوضيحي للنوع المحدد أو لا توجد عقدة في مكانه. على سبيل المثال let $a: $Maybe<number>
سوف يتطابق مع let foo: number
و let foo
(بدون تعليق توضيحي للنوع)، لكن لن let foo: string``let foo: string
.
$Or<...>
يطابق العقد التي تطابق واحدًا على الأقل من التعليقات التوضيحية للنوع المحدد. على سبيل المثال let $x: $Or<number[], string[]>
سيطابق إعلانات let
من النوع number[]
أو string[]
.
$And<...>
يطابق العقد التي تطابق جميع التعليقات التوضيحية للنوع المحدد. يعد هذا مفيدًا في الغالب لتضييق نطاق أنواع العقد التي يمكن التقاطها في عنصر نائب معين. على سبيل المثال، let $a: $And<$type, $elem[]>
سوف يطابق تصريحات let
حيث يتطابق التعليق التوضيحي للنوع مع $elem[]
، ويلتقط التعليق التوضيحي للنوع كـ $type
.
$Ordered
يفرض النمط على مطابقة العقد الشقيقة بنفس الترتيب.
$Unordered
يفرض النمط على مطابقة العقد الشقيقة بأي ترتيب.
import { NodePath } from 'astx'
هذه هي نفس واجهة NodePath
مثل ast-types
، مع بعض التحسينات على تعريفات نوع الطريقة. يستخدم astx
ast-types
لاجتياز التعليمات البرمجية، على أمل دعم المحللين اللغويين المختلفين في المستقبل.
import { Astx } from 'astx'
constructor(backend: Backend, paths: NodePath<any>[] | Match[], options?: { withCaptures?: Match[] })
backend
هي تطبيق المحلل اللغوي/المولد المستخدم.
تحدد paths
NodePath
s أو Match
es التي تريد أن تقوم طرق Astx
بالبحث/التشغيل عليها.
.find(...)
( Astx
) يبحث عن التطابقات للنمط المحدد ضمن مسارات البداية لهذا المثيل ويعيد مثيل Astx
الذي يحتوي على التطابقات.
إذا قمت باستدعاء astx.find('foo($$args)')
على المثيل الأولي الذي تم تمريره إلى وظيفة التحويل الخاصة بك، فسوف يعثر على جميع الاستدعاءات إلى foo
داخل الملف، ويعيد تلك المطابقات في مثيل Astx
جديد.
ستعمل الأساليب الموجودة على المثيل الذي تم إرجاعه فقط على المسارات المتطابقة.
على سبيل المثال، إذا قمت بإجراء astx.find('foo($$args)').find('$a + $b')
، فإن استدعاء find
الثاني سيبحث فقط عن $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[]
(يمكنك استخدام دالة سلسلة القالب ذات علامات 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'`
لن يتطابق مع أي من هاتين الحالتين.
يجب أن يحتوي النمط على بيانات الاستيراد فقط.
.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
) إزالة التطابقات من .find()
أو الالتقاطات المركزة في مثيل Astx
هذا.
.matched
( this | null
) تُرجع نسخة Astx
هذه إذا كانت تحتوي على تطابق واحد على الأقل، وإلا فإنها تُرجع null
.
نظرًا لأن .find()
و .closest()
و .destruct()
تُرجع دائمًا مثيل Astx
، حتى لو لم تكن هناك تطابقات، يمكنك استخدام .find(...).matched
إذا كنت تريد فقط قيمة محددة عندما تكون هناك كانت مباراة واحدة على الأقل.
.size()
( number
) إرجاع عدد التطابقات من استدعاء .find()
أو .closest()
الذي أعاد هذا المثيل.
[name: `$${string}` | `$$${string}` | `$$$${string}`]
( Astx
) الحصول على مثيل Astx
يركز على الالتقاط (الالتقاطات) name
المحدد.
على سبيل المثال، يمكنك القيام بما يلي:
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
الصدق لمطابقة واحدة على الأقل.
iteratee
هي دالة سيتم استدعاؤها باستخدام match: Astx, index: number, parent: Astx
وإرجاع true
أو false
.
.every(predicate)
( boolean
) إرجاع predicate
true
unelss خطأ لمطابقة واحدة على الأقل.
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
المتطابقة s.
.captures
تم التقاط Node
من العناصر النائبة في نمط المطابقة. على سبيل المثال، إذا كان النمط foo($bar)
، فسيكون .captures.$bar
هو Node
الوسيطة الأولى.
.pathCaptures
تم التقاط NodePath
من العناصر النائبة في نمط المطابقة. على سبيل المثال، إذا كان النمط foo($bar)
، فسيكون .pathCaptures.$bar
هو NodePath
للوسيطة الأولى.
.arrayCaptures
تم التقاط Node[]
من العناصر النائبة للمصفوفة في نمط المطابقة. على سبيل المثال، إذا كان النمط foo({ ...$bar })
، فإن .arrayCaptures.$bar
سيكون Node[]
s لخصائص الكائن.
.arrayPathCaptures
تم التقاط NodePath[]
من العناصر النائبة للصفيف في نمط المطابقة. على سبيل المثال، إذا كان النمط foo({ ...$bar })
فإن .pathArrayCaptures.$bar
سيكون NodePath[]
s لخصائص الكائن.
.stringCaptures
قيم السلسلة التي تم التقاطها من العناصر النائبة للسلسلة في نمط المطابقة. على سبيل المثال، إذا كان النمط عبارة عن import foo from '$foo'
، فسيكون stringCaptures.$foo
هو مسار الاستيراد.
مثل jscodeshift
، يمكنك وضع تعليمات برمجية لإجراء تحويل في ملف .ts
أو .js
(الإعداد الافتراضي هو astx.ts
أو astx.js
في دليل العمل، ما لم تحدد ملفًا مختلفًا باستخدام خيار -t
CLI).
تختلف واجهة برمجة تطبيقات ملف التحويل قليلاً عن jscodeshift
. يمكنك الحصول على الصادرات التالية:
exports.find
(اختياري)سلسلة تعليمات برمجية أو عقدة AST للنمط الذي يمكن العثور عليه في الملفات التي يتم تحويلها.
exports.where
(اختياري) حيث شروط التقاط العناصر النائبة في exports.find
. راجع FindOptions.where
( { [captureName: string]: (path: NodePath<any>) => boolean }
) لمزيد من المعلومات.
exports.replace
(اختياري) سلسلة تعليمات برمجية، أو عقدة AST، أو وظيفة استبدال لاستبدال مطابقات exports.find
بها.
وسيطات الدالة هي نفسها الموضحة في .find().replace()
.
exports.astx
(اختياري) دالة لإجراء تحويل عشوائي باستخدام Astx
API. يتم استدعاؤه باستخدام كائن له الخصائص التالية:
file
( string
) - المسار إلى الملف الذي يتم تحويلهsource
( string
) - الكود المصدري للملف الجاري تحويلهastx
( Astx
) - مثيل Astx
APIt
( AstTypes
) - تعريفات ast-types
للمحلل اللغوي المختارexpression
- قالب حرفي مُعلَّم لتحليل التعليمات البرمجية كتعبيرstatement
- قالب حرفي مُعلَّم لتحليل التعليمات البرمجية كبيانstatements
- قالب حرفي مُعلَّم لتحليل التعليمات البرمجية كمجموعة من البياناتreport
( (message: unknown) => void
)mark
(...matches: Astx[]) => void
) - تحدد التطابقات المحددة التي سيتم عرضها في قائمة التطابقات لـ vscode-astx، وما إلى ذلك على عكس jscodeshift
، يمكن أن تكون وظيفة التحويل الخاصة بك غير متزامنة، ولا يلزمها إرجاع التعليمات البرمجية المحولة، ولكن يمكنك إرجاع string
. يمكنك أيضًا إرجاع null
لتخطي الملف.
exports.onReport
(اختياري) إذا كان report(x)
من دالة exports.astx
، فسيتم استدعاؤه باستخدام onReport({ file, report: x })
.
إذا كنت تستخدم عدة سلاسل عمليات، فسيتم استدعاء onReport
في العملية الأصلية، لذلك يجب أن تكون رسالة التقرير قيمة قابلة للتسلسل. يسمح هذا للتحويل بجمع التقارير من جميع العاملين (ومن ثم يحتمل أن يفعلوا شيئًا معهم في finish
).
إذا أعاد onReport
Promise
فسيتم انتظاره.
exports.finish
(اختياري)سيتم استدعاء هذا بعد تشغيل التحويل على كافة ملفات الإدخال.
إذا كنت تستخدم مؤشرات ترابط عاملة متعددة، فسيتم استدعاء finish
في العملية الأصلية. يمكنك استخدام onReport
finish
معًا لجمع المعلومات من كل ملف إدخال وإنتاج نوع من المخرجات المدمجة في النهاية.
إذا أعادت finish
Promise
فسيتم انتظاره.
يدعم astx
التكوين في الأماكن التالية (عبر cosmiconfig
):
astx
في package.json.astxrc
بتنسيق JSON أو YAML.astxrc.json
أو .astxrc.yaml
أو .astxrc.yml
أو .astxrc.js
أو .astxrc.cjs
astx.config.js
أو astx.config.cjs
CommonJS تقوم بتصدير كائنإذا تم تنسيق قاعدة التعليمات البرمجية الخاصة بك مع أجمل، أوصي بتجربة هذا أولاً:
{
"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 الخاص بمشروعك، إن وجد. يمكنك تمرير --parser recast/babel
إذا كنت تريد استخدام recast
لمحاولة الحفاظ على التنسيق في الإخراج، لكنني أحيانًا أرى أخطاء في بناء الجملة في الإخراج.
على عكس 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]