tildeio / libkit

17 stars 4 forks source link

Using a libkit library in an Ember app #2

Closed chriskrycho closed 6 years ago

chriskrycho commented 7 years ago

(This may well be moving- and family-health-problems-induced 🤦‍♂️, but it's not obvious so I figured I'd write it up as a question.)

I've converted a small library to using libkit, and while the build tooling is quite nice, what I've found is that when I try to consume it from within our Ember application, I'm not getting the benefits of the modules – TypeScript simply doesn't resolve anything but index.d.ts via the import syntax.

Example:

// works; these are the Maybe and Result *modules*; they're re-exported
// from the index file
import { Maybe, Result } from 'true-myth';

// does not work; TS seems not to identify this module's existence
import { just, nothing } from 'true-myth/maybe';

It seems related to this issue, and specifically the solution posited by this comment seems like it might work; but I'm not 100% positive, and if so that seems like a limitation of the current implementation?

Again, quite possibly I'm missing something! Or perhaps it's necessary to do something like the post-install steps or custom "paths" value suggested in that thread?

Edit: I can confirm that manually adding the paths works, but this seems very much less than optimal in general.

chancancode commented 6 years ago

@chriskrycho our philosophy is that you should re-export all public things from index.ts. This allows you to treat all internal paths as private and refactor/move around things as needed. Especially when we are talking about small libraries, this should work quite well. Do you have a specific reason to want to not follow that pattern?

chriskrycho commented 6 years ago

@chancancode that philosophy certainly fits well with the normal Node way of doing things! It's also quite sensible in terms of structure and organization that are meant to be private. But in this case, the modules themselves are part of the public API! They're an intentional and carefully-considered part of the structure of the library, for the sake of discoverability and learnability.

Often, in both TS and other languages, I like to structure my modules around specific types and the functions or methods associated with them. So, for the example I linked (a fairly ergonomic and very lightweight implementation of Maybe and Result for TypeScript), I did ultimately expose each of those types from the root. So this works:

import { Maybe } from 'true-myth';
// or `const { Maybe } = require('true-myth');`
const { just, nothing } = Maybe;

However, it's much cleaner and clearer, and frankly much more idiomatic in every context I work in that isn't Node module land, to structure and import from modules which have a one-to-one relationship between the relevant type and its various functions:

import Maybe, { just, nothing } from 'true-myth/maybe';

That's true of my experience in Python, Rust, C♯, F♯, Elm, etc. (modulo variations on specific details around name imports being explicit vs. namespace-opening behavior). It's also (now!) true of Ember itself: we no longer just expose everything as import Ember from 'ember'; we import Service from '@ember/service', and that's good for all the reasons outlined in RFC #176. That holds even for a small library like this one, in my view.

chriskrycho commented 6 years ago

For anyone who stumbles across this: I ended up dropping libkit and writing my own small build scripts to accomplish what I needed; I wrote that up in a blog post. Unless or until Microsoft supports generating single-file type definitions with module support, I suggest that libkit should do what I’m doing in my own libraries and what ember-cli-typescript is doing!bundling the type defs at the root during prepublishOnly.