There are a number of different strategies we have been discussing for setting up the index in the sidebar of ec-addon-docs, and this issue seeks to enumerate them so we can get some consensus on the best path moving forward.
Modules vs Types
Fundamentally, there are two main modes of thinking about the index - we can either show a listing based on the module structure of the addon, or we can show a listing based on the types of the things being documented. For example:
Modules
/components
{{foo-component}}
/components/utils
[c] BaseComponentClass
[f] componentHelperFunc()
[f] otherFunc()
[v] STATIC_VALUE
/models
[c] FooModel
/utils
[f] helperFunc()
[v] CONSTANT_VALUE
vs
Components
{{foo-component}}
Classes
BaseComponentClass
FooModel
Functions
componentHelperFunc()
helperFunc()
otherFunc()
Variables
CONSTANT_VALUE
STATIC_VALUE
Modules are the new paradigm in JS - most old doc tools like YUIDoc and JSDoc didn't have support for them for the longest time, and still don't have full support. These tools were much more class oriented, and employed the Types strategy.
Both of these strategies are suboptimal - Modules focuses too much on the layout in the file system and doesn't easily allow developers to see the bigger picture, a bad file system layout can make the docs very hard to reason about. However, Modules do allow for related functionality to be grouped together, something which can get very confusing in the Types system (note in the above example, functions from /components/utils
end up split apart due to alphabetical ordering in the Types layout).
Strategies
For the following strategies we will use a subset of the addon structure of Liquid Fire. This subset contains some files which would likely be considered private and not part of LF's exteranl API, but they are included for two main reasons
- They are representative of a large addons potential API space, and diversity will let us see the complex edge/corner cases here to make a decision that isn't a massive footgun
- Developers actually working on LF would likely benefit from having access to the private docs - this is true for most large addons. While not a primary priority, it would be ideal to have a system that is "toggle-able" and allows developers to quickly see documentation for private APIs if they want to contribute to the addon/library.
The structure has also been modified slightly to bring out certain edge cases (like pod modules, for instance)
/components
/liquid-bind
/component.js
LiquidBind
/liquid-if
/component.js
LiquidIf
/liquid-outlet.js
/component.js
LiquidOutlet
/liquid-unless.js
/component.js
LiquidUnless
/helpers
/lf-lock-model.js
lfLockModel()
/lf-or.js
lfOr()
/mixins
/pausable.js
Pausable
/services
/liquid-fire-transitions.js
TransitionMap
/transitions
/fade.js
fade()
/default.js
default()
/move-over.js
moveOver()
/explode.js
explode()
/animate.js
animate()
stop()
isAnimating()
timeSpent()
timeRemaining()
finish()
/promise.js
Promise
/mutation-observer.js
MutatationObserver
Modules With Hosting
As we noted before, modules can be suboptimal because it depends heavily on the file structure being decent. However, Ember has an advantage here because the file system is generally decent - it's dominated by very well known conventions throughout the Ember ecosystem. The Modules With Hoisting approach would use this to our advantage, organizing mostly around the module system, but "hoisting" based on a few very simple heuristics:
- If a file is a component or helper pod, show it as if it were the parent module
- If an export is a default export, show it as if it belongs to the parent module
These rules are additive, so a typical pod component would be hoisted by two levels for instance. This is the output for this strategy:
/
Promise
MutationObserver
/components
LiquidBind
LiquidIf
LiquidOutlet
LiquidUnless
/helpers
lfLockModel()
lfOr()
/mixins
Pausable
/services
TransitionMap
/transitions
fade()
default()
moveOver()
explode()
/animate
animate()
stop()
isAnimating()
timeSpent()
timeRemaining()
finish()
Resolved Types w/ Modules
For this strategy, we would leverage the fact that Ember has a notion of "globals" via the resolver. We can be sure, based on the structure of an addon, that certain constructs like Components, Services, etc will be essentially in a global namespace and will not have naming collisions. This strategy would follow the resolver directly - anything that isn't a resolved type would be considered one of the primitive types (Class, Function, Variable/Constant). For consistency, these would also be divided by type, but each type would also show module paths (potentially with hoisting as described above):
Components
LiquidBind
LiquidIf
LiquidOutlet
LiquidUnless
Helpers
lfLockModel()
lfOr()
Services
TransitionMap
Classes
/
Promise
MutationObserver
/mixins
Pausable
Functions
/transitions
fade()
default()
moveOver()
explode()
/animate
animate()
stop()
isAnimating()
timeSpent()
timeRemaining()
finish()
Resolved Types+ w/ Modules
This strategy is the same as resolved types, but with additional heuristics for unresolved types that are still generally considered "primitive" types in Ember. The only real example of this use cases would be Mixins, and the heuristic would be to check against class name, route name, and possibly a manual tag users could add.
Components
LiquidBind
LiquidIf
LiquidOutlet
LiquidUnless
Helpers
lfLockModel()
lfOr()
Mixins
Pausable
Services
TransitionMap
Classes
/
Promise
MutationObserver
Functions
/transitions
fade()
default()
moveOver()
explode()
/animate
animate()
stop()
isAnimating()
timeSpent()
timeRemaining()
finish()