Pods are a type of file organization structure in ember-cli. They allow you to gather related modules and assets together in your file structure. Currently pod structured modules can live side by side with your basic modules because the resolver that ships with ember-cli will look up your path in pod structure, and then fall back to basic structure if no paths are resolved.
Soon there will be a point where some blueprints will only generate in pod structure, and others will only generate in basic structure. This will allow us to collect a list of types that support each lookup structure and only need to lookup modules once. This will also cause less ambiguity around what a project structure looks like.
My opinion on pods is that they should not live in the same namespace as type based modules. We should re-appropriate 'components',' models', and 'routes' (or 'resources') as pod prefixes to house the concerns of global ui, data, and route specific ui, respectively. Everything else stays the same, so type based modules like 'helpers', 'services', or 'utils' would stay as is. Blueprints should only generate as either a pod based module or a type based module, but not both, as it complicates the scheme and adds ambiguity to what is where in a project.
The need for multiple pod prefixes to separate the concerns is a pattern I've seen coming out of all the pods discussions and requests. To me, that sounds like a cowpath being made, and we should address those concerns somehow.
When engines come into play, they would live in a sibling folder to app, much like lib with in-repo-addons, but would use the same scheme as the app folder.
A type based structure, where files are organized by type. Basic files are located in a root folder with the pluralized name of the type of files it contains, and named by the concern they are related to. For instance, a user
template file would be contained in app/templates/user.hbs
, and a user
route would be in app/routes/user.js
.
A concern based structure, where files are organized by what they are related to. Pod files are located in a folder named with the name of the concern it relates to, and named by the type of file they are. For instance, all files relating to a user
are contained in app/user
folder. So a user
template would be app/user/template.hbs
and route would be app/user/route.js
.
There is still need for modules in basic structure. Certain file types lose discoverability when in pod structure. Helpers, initializers, mixins, models, services, and utils all make more sense in basic structure. When these file types in pod structure, they are indistinguishable from pods that represent routes and their associated assets.
The term "pod" almost describes a naming convention moreso than the structure a module is held in. The difference with a pod is that it has a prefix to define where it should belong. The prefix is necessary to prevent naming collisions with pods using ember types as their name (app/services
would be a common case). If you were to break down a structure by its parts, you would see a path is made up of four parts:
- root: The root folder, generally
app
can beaddon
as well - prefix: The grouping namespace for a pod file, like
pods
- name: The name of the file including nesting paths, like
taco
orusers/edit/edit-profile
- type: The type of the file, like
component
orroute
To understand this breakdown, let's look at a few files in the current basic and pod structures.
basic
app/routes/taco.js
root | prefix | type | name |
---|---|---|---|
app | - | route | taco |
pod
app/taco/route.js
root | prefix | type | name |
---|---|---|---|
app | - | route | taco |
Note how the type and the filename just swap positions between structures (with different plurarization). Let's look at the same pod generated in an environment with a podModulePrefix
defined.
pod
app/pods/taco/route.js
root | prefix | type | name |
---|---|---|---|
app | pods | route | taco |
The prefix is the second part of the podModulePrefix
value in the environment config (sans the app-name). All examples after this should be assumed to have a podModulePrefix
defined.
Components are a special case because they have a secondary prefix in pod structure. It could be argued that the components
part of the prefix is actually part of the namespace, but I feel that it's more like a prefix due to the way they are looked up.
basic
app/components/taco-bar.js
root | prefix | type | name |
---|---|---|---|
app | - | component | foo-bar |
pod
app/pods/components/taco-bar/component.js
root | prefix | type | name |
---|---|---|---|
app | pods/components | component | taco-bar |
Nested components are essentially the same, with additonal entities in the namespace. The first element of the namespace
basic
app/components/taco/bar/taco-bar.js
root | prefix | type | name |
---|---|---|---|
app | - | component | taco/bar/taco-bar |
pod
app/pods/components/taco/bar/taco-bar/component.js
root | prefix | type | name |
---|---|---|---|
app | pods/components | component | taco/bar/taco-bar |
The root comes into play when developing in an addon.
addon/components/taco-bar.js
root | prefix | type | name |
---|---|---|---|
addon | - | component | taco-bar |
app/components/taco-bar.js
root | prefix | type | name |
---|---|---|---|
app | - | component | taco-bar |
Currently most blueprints support both pod and basic structure. For pods to become the default, we will need to remove that flexibility and force blueprint types to only generate in the one structure they support. Helpers, initializers, mixins, models, services, and utils all will still generate in basic structure.
Blueprints contain a files
folder, which contains a template of the files to be generated by the blueprint. The file paths contain tokens (indicated by __
surrounding the name of the token) which are replaced when the blueprint is generated.
A blueprint's files
folder structure can be used to implicitly indicate whether a blueprint supports a structure or not (via a test to check for the existence of necessary tokens to support the structure). For example:
// current: can be pod or basic structure
files/app/__path__/__name__.js
// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/route.js
// current: basic structure only
files/app/services/__name__.js
// proposed: basic structure only
files/__root__/services/__name__.js
// current: can be pod or basic structure
files/__root__/__path__/__name__.js
// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/component.js
// current: can be pod or basic structure
files/app/__path__/__name__.js
// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/model.js
Regarding the blueprints for shared vs route namespaces, the default blueprint will be route namespaced. The shared blueprint will simply re-use the default blueprint and override the __prefix__
token value to be in the shared
namespace.
I think that a single podModulePrefix
is too constrictive for ember-cli projects. I propose that we should provide more than one namespace for files in pod structure, as the structure can apply to more than one concern.
I believe that pods make sense for the following concerns:
- shared ui (components + templates)
- route specific ui (components + templates)
- shared models
- route specific models
All of these concerns can be separated and still support pod structure. We just have to provide namespaces for them that are standardized and make the resolver aware of them.
This issue may be solved by using engines as namespaces, but not enough is known about how engines will work yet to be sure.
Structures are key to providing a way to describe multiple pod prefixes/namespaces, as well as the ability to move files between them. A structure is a simple construct that:
- describes file path tokens it is aware of (
__root__
,__path__
,__name__
) - provides a prefix (if any)
- provides a way to test if a blueprint is supported by the structure
- provides the resolver pattern it conforms to (type or pod based lookup)
Structures will identify how to generate modules in a project, they will specify what resolver lookup pattern they conform to, but the resolver will not need to be aware of the structures.