-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GetIntrinsic: remove from PlainDate, add monkeypatching test #105
base: main
Are you sure you want to change the base?
Conversation
describe('Monkeypatch', () => { | ||
describe('PlainDate', () => { | ||
it("Monkeypatching Duration doesn't affect PlainDate", () => { | ||
Temporal.Duration.prototype.constructor = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably we also want to check Temporal.Duration = null
and delete Temporal.Duration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the tests are .mjs
, imports will be read-only, so JS won't let us do those two things (although monkeypatching the prototype seems to be valid).
HI @jens-ox - I spent some time digging into what would be involved in getting all of Temporal to compile with The good news is that once those two files are made TS-strict-friendly, the rest of the codebase requires only a handful of changes to compile with The bad news is that those two files are much larger and harder to understand than the rest of Temporal, which will make it really hard for someone new coming to the project. So I'd suggest that you start with this intrinsics PR as a good first step. In parallel, I can help out by making needed changes in ecmascript.ts and calendar.ts. The latter also needs to be fully upgraded to TS types (it still uses a lot of I should have a PR done for those changes on Monday. This should make it much easier for you to layer the GetIntrinsic work on top. Sound like an OK plan? re: monkeypatching, here's some quick use cases that should continue to work: (I believe that they work in today's polyfill, but it's probably good to test these in a real app to make sure they actually work on top of v0.3.0 of the polyfill) import { Temporal } from '@js-temporal/polyfill';
// now monkeypatch a Temporal method
Temporal.PlainDate.prototype.equals = () => { return 'not equal!'; }
d = Temporal.PlainDate.from('2020-01-01')
// calling that method should run the patched code
d.equals('2020-01-01')
// => 'not equal!'
// If another Temporal type creates an instance of that method, it should also run the patched code
Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().equals('2020-01-01')
// => 'not equal!'
// patching again will change behavior of *existing* instances
Temporal.PlainDate.prototype.equals = () => { return 'actually, equal!'; }
d.equals('2020-01-01')
// => 'actually, equal!'
// also works for static methods
Temporal.PlainDate.compare = (d1, d2) => { return 42; }
Temporal.PlainDate.compare('2020-01-01', '2020-01-01');
// => 42
// can also replace entire types
Temporal.PlainDate = class { constructor() { this.year = 1999 }}
new Temporal.PlainDate().year
// => 1999
// however, those replaced types won't be used by Temporal internal code
// when creating new instances. It will use intrinsic types.
Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().year
// => 2020
// but those intrinsic types still retain the patches to its prototype
Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().equals('2020-01-01')
// => 'actually, equal!' @ptomato (or @gibson42 who seems to know more about monkeypatching than anyone)
|
@gibson042 (as I think Justin typo'd above) |
I believe so. While checking whether
The main thing I was concerned about when switching from GetIntrinsic to internal imports was your second-to-last example (that instances created internally in Temporal might be instances of the "public" replaced type, which would be bad.) |
Hi all, I fully removed I'll try to add the monkeypatches from above, but as the tests are |
There was also some funky stuff going on with calendar initialization in |
configurable: true | ||
}); | ||
if (DEBUG) { | ||
Object.defineProperty(Class.prototype, Symbol.for('nodejs.util.inspect.custom'), { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this functionality (which AFAIK provides a way for Node to show friendlier output in the debugger) moved somewhere else? If not, then it probably should be, unless for some reason there's a better way to handle this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is mainly useful when debugging or testing things out interactively. I care mostly that it is present in proposal-temporal, I think it's probably useful here as well, but I'd leave it up to the people who maintain this polyfill more actively than me.
Cool! The resulting code is much more readable. Very nice!
The one failure on main was an "unexpected pass" because its PR was merged but we didn't update expected-failures.txt. If you rebase on top of #110 then this failure will go away. There should be no other failures on main. If you're seeing more than one failure on main, could you The other failure does not occur on main, and it's a concerning one:
What's odd is that when I checked out your PR branch and ran this test in the debugger, it didn't fail. Not sure why. Perhaps because I'm not correctly picking up any changes to Test262 tests that you made (did you make any?) Could you try rebasing your PR on top of #110 (which updates Test262 to latest) and see what, if any, failures remain? |
Previously, GetIntrinsic returned a type that could be `undefined`. That's fixed in this commit. Note that intrinsics.ts may be removed completely in js-temporal#105; if that PR lands first then we'll remove this commit.
Previously, GetIntrinsic returned a type that could be `undefined`. That's fixed in this commit. Note that intrinsics.ts may be removed completely in js-temporal#105; if that PR lands first then we'll remove this commit.
Branch is rebased 👍 |
I did some testing today, monkeypatching things will still work in CommonJS. Using the module import, this will not work, as the imports are read-only. |
@jens-ox - Do all the cases in #105 (comment) work when the polyfill is imported in an ESM app (other than modifying properties on exported objects, as noted above) or @ptomato - I noticed a subtle issue in monkeypatch behavior: should static methods on Temporal types be patchable? If my understanding of
But I don't see anything in the spec that describes whether static methods like |
FYI @jens-ox - most of the other maintainers of this polyfill are out for the holidays, so it's unlikely that you'll get more feedback on this PR until January. Just wanted to let you know we weren't ignoring you! 😄 |
That's fine @justingrant 😊 I'm currently digging through the code - if it's ok I'm going to create a discussion post with some questions regarding the codebase. |
Sounds good. I'll be around over the holidays so can answer questions.
BTW, this work is here: #109. It turned out that after converting those two files to be |
To be clear, this PR #105 is still good (assuming we can figure out the monkeypatching stuff discussed above) but we can close #103 because it's covered by #109 along with a few recent already-merged PRs (#104, #106, #108, #110, #111, #112) that came out of the investigation of #109. |
Sounds great! Regarding monkeypatching - this seems to work on my end, and the monkeypatching capabilities testable in a |
Here is the content of the /* eslint-disable no-console */
const { Temporal } = require('./dist/index.cjs');
Temporal.PlainDate.prototype.equals = () => {
return 'not equal!';
};
const d = Temporal.PlainDate.from('2020-01-01');
// calling that method should run the patched code
console.log(d.equals('2020-01-01'));
// => 'not equal!'
// If another Temporal type creates an instance of that method, it should also run the patched code
Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().equals('2020-01-01');
// => 'not equal!'
// patching again will change behavior of *existing* instances
Temporal.PlainDate.prototype.equals = () => {
return 'actually, equal!';
};
console.log(d.equals('2020-01-01'));
// also works for static methods
Temporal.PlainDate.compare = (d1, d2) => {
return 42;
};
console.log(Temporal.PlainDate.compare('2020-01-01', '2020-01-01'));
// => 42
// can also replace entire types
Temporal.PlainDate = class {
constructor() {
this.year = 1999;
}
};
console.log(new Temporal.PlainDate().year);
// => 1999
// however, those replaced types won't be used by Temporal internal code
// when creating new instances. It will use intrinsic types.
console.log(Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().year);
// => 2020
// but those intrinsic types still retain the patches to its prototype
console.log(Temporal.ZonedDateTime.from('2020-01-01[America/Los_Angeles]').toPlainDate().equals('2020-01-01'));
// => 'actually, equal!' |
I took a look at this, not in detail because most of it is searching and replacing GetIntrinsic and I trust that the TypeScript compiler and the tests are ensuring that that was done correctly. Is there any part of this PR in particular you'd like me to pay attention to? |
Previously, GetIntrinsic returned a type that could be `undefined`. That's fixed in this commit. Note that intrinsics.ts may be removed completely in #105; if that PR lands first then we'll remove this commit.
Hi @jens-ox - We're all back from the holiday break and ready to review this. There's been quite a few PRs merged recently so now this PR has a lot of merge conflicts. Could you rebase it? Thanks! |
@jens-ox, are you still interested in completing this PR? Let us know if someone else should pick it up. Thanks! |
Hi @justingrant, I'll pick this up this weekend 👍 |
Hi @jens-ox - any luck getting back to this? Would be good to wrap this up before more merge conflicts are introduced. |
Hi @justingrant! Sorry, I got a new job and completely forgot about my open source things. I updated the PR - but feel free to close this if this is not relevant anymore. |
Hi all! As discussed in #103, it would be nice to remove
GetIntrinsic
and just import things.This PR removes
GetIntrinsic
(as of writing this only fromplainDate.ts
in order to make sure that it makes sense before removing it elsewhere).I also added a test that monkey-patches
Duration
(used byPlainDate
) to make sure that it doesn't affect the behaviour ofPlainDate
-- not sure if I did it correctly, as I never used monkey-patching before.As importing something from a file doesn't import from
globalThis
anyway (at least that's my understanding), monkey-patches done in userland should not the library. If someone with more knowledge of monkey-patching could verify this, that would probably be great.Patching stuff in userland also seems the main usecase for ES6 Proxies, so maybe this isn't something to worry about anyway?