Skip to content

Break out reexports by path #60

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

Merged
merged 5 commits into from
Mar 12, 2021
Merged

Break out reexports by path #60

merged 5 commits into from
Mar 12, 2021

Conversation

dfreeman
Copy link
Member

While having a single entrypoint with all the reexports for an environment was convenient to implement, as I sat down to migrate my realworld-glimmerx-example-app repo over to the new glint API, it just didn't feel very good.

The proposed import scheme here aligns "normal" import paths 1:1 with glint import paths, allowing us to use exactly the same specifiers for each and making the transition in both directions smoother.

It also avoids triggering a type error if, for instance, you don't have ember-modifier installed in your app, since that import wouldn't trigger until you actually tried to import from @glint/environment-ember-loose/ember-modifier.

GlimmerX

Normal Path Glint Path
@glimmerx/component @glint/environment-glimmerx/component
@glimmerx/modifier @glint/environment-glimmerx/modifier
@glimmerx/helper @glint/environment-glimmerx/helper

Ember

Normal Path Glint Path
@ember/component @glint/environment-ember-loose/ember-component
@ember/component/helper @glint/environment-ember-loose/ember-component/helper
@glimmer/component @glint/environment-ember-loose/glimmer-component
ember-modifier @glint/environment-ember-loose/ember-modifier

@dfreeman dfreeman added the breaking A breaking change label Mar 12, 2021
@chriskrycho
Copy link
Member

chriskrycho commented Mar 12, 2021

This approach makes a lot of sense to me, esp. because it paves a path to eliminating these over time as (a) the root types align better with our needs and (b) we move as an ecosystem into strict mode.

A question I had, here, though—based in part on my experimentation with Glint a couple weeks ago: what is the DX for go-to-definition, hover-docs, etc. on these? I think we end up showing the custom definition (and I don't actually see how we could do otherwise)? If so, have you given any thought to how we can make that experience better?

There's a related question we'll want to solve around docs—you pointed out to me and @jamescdavis in Discord that copy-and-pasting something like the each-in docs to get feedback on that is basically a non-starter for maintenance reasons.

@dfreeman
Copy link
Member Author

I think we end up showing the custom definition (and I don't actually see how we could do otherwise)?

That's right, though it's possible to continue to trace the lineage from there to the actual source (the same way you have to with e.g. @glimmer/component today if you go-to-definition that module and get export { default } from '...').

TypeScript in theory supports the @inheritDoc directive, but I couldn't seem to get that to work for classes themselves no matter what I tried. For class members, hover-docs show up the way you'd expect:

image

Longer-term I'm still hopeful we can get back to a place where these reexports are unnecessary. There are essentially three things preventing us from just using declaration merging today:

  1. the inability to represent Yields and Element on components
  2. Helper's base compute method being type-opaque
  3. fn and on being exported values in GlimmerX

Issue 1 will be hopefully be solved for Glimmer components by a forthcoming RFC. Issue 2 we can in principle solve whenever by updating DT, though I suspect it makes sense to wait for the component types RFC first to see how that shakes out.

Issue 3 is slightly thornier, but there are well established patterns (used in the built-in DOM types, for instance) for making exported values type-augmentable. GlimmerX's on, for example, could be refactored slightly from:

export declare function on(
  element: Element,
  eventName: string,
  callBack: EventListenerOrEventListenerObject,
  options?: AddEventListenerOptions
): void;

To:

interface OnModifier {
  (
    element: Element,
    eventName: string,
    callBack: EventListenerOrEventListenerObject,
    options?: AddEventListenerOptions
  ): void;
}

export declare const on: OnModifier;

That would allow us to add an [InvokeDirect] signature without GlimmerX itself needing to know anything about glint at all.

copy-and-pasting something like the each-in docs to get feedback on that is basically a non-starter for maintenance reasons.

I actually started poking at the git history for those doc comments yesterday, and it turns out they change very infrequently. My growing suspicion is that it's not likely to be a meaningfully higher maintenance burden to keep the docs up to date than it is to keep the type definitions themselves in sync.

@dfreeman dfreeman merged commit 143a4da into master Mar 12, 2021
@dfreeman dfreeman deleted the reexport-paths branch March 12, 2021 16:47
@dfreeman
Copy link
Member Author

Going ahead and merging this since it doesn't make any of the issues you raised worse than the current status quo.

@jamescdavis
Copy link
Member

This makes a lot of sense to me as well. It reminds me of the ember-decorators thing we all went through.
(e.g. import { computed } from '@ember-decorators/object'; -> import { computed } from '@ember/object';)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking A breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants