Comments (7)
First, this library is awesome.
Thanks! I'm definitely open to suggestions so thanks for filing this issue. Is the thing you're using this for on Github? I'm curious to see how people are using it.
I'm guessing this is to support partial matches, but sometimes I want
matcher.match
to fail if one of its captures fails.
This should be the way it is now. The trouble is that the match
has a side effect which I can't really encode into the type system easily, so the result of get current()
needs to potentially be undefined
since you can call it before a match happens. I worked around this issue with matchPath
by only calling the callback with unwrapped optionals:
codemod/packages/matchers/src/utils/matchPath.ts
Lines 33 to 35 in ac879f8
But this won't work in your example since you're working with Node
values and not NodePath
. One possibility would be to change the behavior of get current()
to throw if there isn't a captured value yet. This would be a breaking change, but I think it might be worth the ergonomic improvement. Maybe also add hasCaptured(): boolean
to better support things like the or
matcher where one or more of the branches might not have any captures.
What do you think?
from codemod.
Is the thing you're using this for on Github? I'm curious to see how people are using it.
Just me playing around so far. Trying to define a query made up of one or more conditions for Babel's AST that I can name, reuse, combine, compose. If not all conditions are met, then the thing I'm looking for doesn't exist and I expect the query returns nothing. If all conditions are met I expect something truthy and all captures bound with the patterns they matched.
One possibility would be to change the behavior of get current() to throw if there isn't a captured value yet. This would be a breaking change, but I think it might be worth the ergonomic improvement.
throw
ing is an interesting proposal. Recently read this: https://overreacted.io/algebraic-effects-for-the-rest-of-us/. Could allow for some kind of continuation pattern or flexibility and communication not provided by normal functions.
I don't think this warrants a breaking change because the current behavior is really powerful and useful and working perfectly. I think if all-or-nothing matches are supported at all they should be added as a new thing. Based on what you're telling me, I'm worried that might require copying and pasting a large part of the codebase with minor alterations. I'll start looking into it.
from codemod.
Something like this could be a userland solution.
type CaptureMap<C> = { [k: string]: m.CapturedMatcher<any, C> }
type BoundCaptures<C> = { [k: string]: C }
function query<T, C>(
value: unknown,
condition: m.Matcher<T>,
bindings: CaptureMap<C>,
): BoundCaptures<C> | void {
if (!condition.match(value)) return
let boundCaptures = {} as BoundCaptures<C>
for (let k in bindings) {
let v = bindings[k].current
if (!v) return
boundCaptures[k] = v
}
return boundCaptures
}
from codemod.
throw
ing is an interesting proposal. Recently read this: https://overreacted.io/algebraic-effects-for-the-rest-of-us/. Could allow for some kind of continuation pattern or flexibility and communication not provided by normal functions.
That's funny. I just read that recently too.
I don't think this warrants a breaking change because the current behavior is really powerful and useful and working perfectly. I think if all-or-nothing matches are supported at all they should be added as a new thing. Based on what you're telling me, I'm worried that might require copying and pasting a large part of the codebase with minor alterations. I'll start looking into it.
I don't think it would actually be that big a deal. It would technically be a breaking change, but the difference would be fairly minimal. Instead of checking matcher.current
you'd check matcher.hasCaptured()
or something like that. Your example above would become this:
return matcher.match(node) ? [node, $methodName.current] : null
You'd know it's safe to access $methodName.current
because you'd know that match
couldn't have returned true
if the $methodName
matcher didn't capture a value. This could be wrapped in something to make it a bit safer if you wanted, similar to matchPath
:
return m.matchNode(
matcher, node,
{ $methodName },
({ $methodName }) => [node, $methodName]
);
Might be overkill.
from codemod.
Actually, I totally forgot that this is what the match
helper does. You can use it like so:
export const matchRamdaExpr = (node:unknown, ramdaIdent = "R"): [t.MemberExpression, t.Identifier] | null => {
let $methodName = m.capture(m.identifier())
const matcher =
m.memberExpression(
m.identifier(ramdaIdent),
$methodName,
)
let result: ReturnType<typeof matchRamdaExpr> = null;
m.match(
matcher,
{ $methodName },
node,
({ $methodName }) => result = [node as t.MemberExpression, $methodName]
);
return result;
}
If m.match
returned the value of callback
when matching, you could simplify to this:
export const matchRamdaExpr = (node:unknown, ramdaIdent = "R"): [t.MemberExpression, t.Identifier] | null => {
let $methodName = m.capture(m.identifier())
const matcher =
m.memberExpression(
m.identifier(ramdaIdent),
$methodName,
)
return match(
matcher,
{ $methodName },
node,
({ $methodName }) => [node as t.MemberExpression, $methodName]
) || null;
}
What do you think?
from codemod.
I took a stab at it in #240. Let me know what you think.
from codemod.
Thanks. I really admire your attentiveness. I think we'd all prefer no breaking changes unless absolutely necessary and plain returns instead of callbacks, but I haven't had time to look into the issue any deeper. I felt like the query sketch above was at the right level to implement something like this, above the API as it is now. In conjunction with it, your examples reminded me of Datomic's query syntax: https://docs.datomic.com/cloud/query/query-data-reference.html
;; query
[:find ?e
:where [?e :artist/name "The Beatles"]]
;; args
db
;; Example query using the clojure API
(d/q '[:find ?e
:where [?e :artist/name "The Beatles"]]
db)
;; The previous example also works with datomic.client.api used for Datomic Cloud
;; Example query using the datomic.client.api arity-1
(d/q {:query '[:find ?e
:where [?e :artist/name "The Beatles"]]
:args [db]})
;; Result. Your exact value may differ.
[[26757714973567138]]
Seems like we're bordering on a similar signature though maybe not quite. (query find <bound-vars-I-want-back> <patterns-with-variable-bindings> <the-thing-to-match-against>)
.
Preserving partial matches would be important to me personally cause I think there's a fair amount of cool things and optimization that could be done with the information that some things matched but not others.
from codemod.
Related Issues (20)
- `and` matches HOT 1
- Difference between codemod-js and jscodeshift HOT 1
- Error: Requires Babel "^7.0.0-beta.41", but was loaded with "7.9.0" HOT 4
- Options are not passed into the plugin HOT 6
- Is there any way to ignore certain paths? HOT 5
- Bug? (babelrc options not work) + Use case (need to pass fn to plugin options) HOT 2
- Suggestion: replace custom file path resolver with globby HOT 5
- Suggestion: use Piscina for faster codemods HOT 1
- ASTExplorer latest revision link does not work with `--remote-plugin` HOT 1
- Debug mode? HOT 1
- [Question] How to create npx compatible codemod runners
- [Question] How to list changed files? HOT 1
- 3.1.0 Breaking Changes? HOT 6
- Adding an onComplete function HOT 1
- .match issues with TypeScript and fromCapture HOT 3
- Decorators cannot be used to decorate parameters. HOT 1
- Run with a custom babel.config.js HOT 1
- combine spacers and matchers HOT 3
- function expression m.anything doesn't match null id HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from codemod.