Skip to content

Commit

Permalink
Merge pull request #182 from 201-created/module-unification
Browse files Browse the repository at this point in the history
Initial implementation of the module unification feature flag
  • Loading branch information
rwjblue committed Mar 28, 2017
2 parents 9efec75 + ff12633 commit a24d4ae
Show file tree
Hide file tree
Showing 13 changed files with 972 additions and 1,072 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ env:
- EMBER_TRY_SCENARIO=ember-release
- EMBER_TRY_SCENARIO=ember-beta
- EMBER_TRY_SCENARIO=ember-canary
- EMBER_TRY_SCENARIO=module-unification

matrix:
fast_finish: true
Expand Down
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,32 @@ In the `ember-resolver` codebase, you can import these flags:
import { EMBER_RESOLVER_MODULE_UNIFICATION } from 'ember-resolver/features';
```
#### Current feature flags
### Current feature flags
* None at this time.
#### `EMBER_RESOLVER_MODULE_UNIFICATION`
Ember [RFC #154](https://github.com/emberjs/rfcs/blob/master/text/0143-module-unification.md)
describes an improved resolution strategy and filename-on-disk
layout for Ember applications. To experiment with this feature
it must be enabled as described above, then use the `src/`
directory on disk. You can generate a new app that uses
this layout by using the following commands:
```
# Install Ember-CLI canary globally:
npm install -g ember-cli/ember-cli
# Create a new app with the module unification blueprint
ember new my-app -b ember-module-unification-blueprint
```
This will create an app running a module unification layout from
the
[ember-module-unification-blueprint](https://github.com/emberjs/ember-module-unification-blueprint)
package. By default, this app will be correctly configured.
* It uses the `glimmer-wrapper` resolver.
* It builds an glimmer resolver config and passes it to the resolver.
* It starts with a `src/` based layout on disk.
## Upgrading
Expand All @@ -58,7 +81,7 @@ version use `yarn` or `npm`. For example:
yarn upgrade ember-resolver
```
#### Migrating from bower
### Migrating from bower
Before v1.0.1 `ember-resolver` was primarially consumed bia bower. To migrate
install the addon version via `yarn` or `npm`. If you're currently using
Expand Down
4 changes: 4 additions & 0 deletions config/ember-try.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/*jshint node:true*/
module.exports = {
scenarios: [
{
name: 'module-unification',
command: 'EMBER_RESOLVER_MODULE_UNIFICATION=true ember test'
},
{
name: 'default',
bower: {
Expand Down
18 changes: 18 additions & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
'use strict';
/*jshint node:true*/
/* global require, module */
var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
var MergeTrees = require('broccoli-merge-trees');
var Funnel = require('broccoli-funnel');

module.exports = function(defaults) {
let testTrees = [new Funnel('tests', {
exclude: [/^dummy/],
})];

let config = defaults.project.config();
let resolverConfig = config['ember-resolver'] || {};

if (resolverConfig.features.EMBER_RESOLVER_MODULE_UNIFICATION) {
testTrees.push('mu-trees/tests');
}

var app = new EmberAddon(defaults, {
trees: {
tests: new MergeTrees(testTrees)
},

// Add options here
vendorFiles: {
'ember-resolver.js': null
Expand Down
57 changes: 51 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
'use strict';

var VersionChecker = require('ember-cli-version-checker');
var path = require('path');

module.exports = {
name: 'ember-resolver',

init: function() {
this._super.init.apply(this, arguments);
this.options = this.options || {};

emberResolverFeatureFlags() {
var config = this.project.config();
var resolverConfig = config['ember-resolver'] || {};

var resolverFeatureFlags = Object.assign({
return Object.assign({
/* Add default feature flags here */
EMBER_RESOLVER_MODULE_UNIFICATION: false
}, resolverConfig.features);
},

init: function() {
this._super.init.apply(this, arguments);
this.options = this.options || {};

this._emberResolverFeatureFlags = this.emberResolverFeatureFlags();

this.options.babel = {
loose: true,
Expand All @@ -31,13 +37,52 @@ module.exports = {
features: {
name: 'ember-resolver',
source: 'ember-resolver/features',
flags: resolverFeatureFlags
flags: this._emberResolverFeatureFlags
}
}]
]
};
},

treeForAddon: function() {
var MergeTrees = require('broccoli-merge-trees');
let addonTrees = [].concat(
this._super.treeForAddon.apply(this, arguments),
this._emberResolverFeatureFlags.EMBER_RESOLVER_MODULE_UNIFICATION && this._moduleUnificationTrees()
).filter(Boolean);

return new MergeTrees(addonTrees);
},

_moduleUnificationTrees() {
var resolve = require('resolve');
var Funnel = require('broccoli-funnel');

let featureTreePath = path.join(this.root, 'mu-trees/addon');
var featureTree = new Funnel(featureTreePath, {
destDir: 'ember-resolver'
});

var glimmerResolverSrc = require.resolve('@glimmer/resolver/package');
var glimmerResolverPath = path.dirname(glimmerResolverSrc);
var glimmerResolverTree = new Funnel(glimmerResolverPath, {
srcDir: 'dist/modules/es2017',
destDir: '@glimmer/resolver'
});

var glimmerDISrc = resolve.sync('@glimmer/di', { basedir: glimmerResolverPath });
var glimmerDITree = new Funnel(path.join(glimmerDISrc, '../../../..'), {
srcDir: 'dist/modules/es2017',
destDir: '@glimmer/di'
});

return [
this.preprocessJs(featureTree, { registry: this.registry }),
this.preprocessJs(glimmerResolverTree, { registry: this.registry }),
this.preprocessJs(glimmerDITree, { registry: this.registry }),
];
},

included: function() {
this._super.included.apply(this, arguments);

Expand Down
88 changes: 88 additions & 0 deletions mu-trees/addon/ember-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* This config describes canonical Ember, as described in the
* module unification spec:
*
* https://github.com/dgeb/rfcs/blob/module-unification/text/0000-module-unification.md
*
*/
export default function generateConfig(name) {
return {
app: {
name,
rootName: name
},
types: {
adapter: { definitiveCollection: 'models' },
application: { definitiveCollection: 'main' },
controller: { definitiveCollection: 'routes' },
component: { definitiveCollection: 'components' },
'component-lookup': { definitiveCollection: 'main' },
event_dispatcher: { definitiveCollection: 'main' },
helper: { definitiveCollection: 'components' },
initializer: { definitiveCollection: 'initializers' },
'instance-initializers': { definitiveCollection: 'instance-initializer' },
location: { definitiveCollection: 'main' },
model: { definitiveCollection: 'models' },
partial: { definitiveCollection: 'partials' },
renderer: { definitiveCollection: 'main' },
route: { definitiveCollection: 'routes' },
router: { definitiveCollection: 'main' },
serializer: { definitiveCollection: 'models' },
service: { definitiveCollection: 'services' },
template: {
definitiveCollection: 'routes',
fallbackCollectionPrefixes: {
'components': 'components'
}
},
transform: { definitiveCollection: 'transforms' },
util: { definitiveCollection: 'utils' },
view: { definitiveCollection: 'views' },
'-view-registry': { definitiveCollection: 'main' },
'-bucket-cache': { definitiveCollection: 'main' }
},
collections: {
'main': {
types: ['router', '-bucket-cache', 'component-lookup', '-view-registry', 'event_dispatcher', 'application', 'location', 'renderer']
},
components: {
group: 'ui',
types: ['component', 'helper', 'template']
},
initializers: {
group: 'init',
types: ['initializer']
},
'instance-initializers': {
group: 'init',
types: ['instance-initializers']
},
models: {
group: 'data',
types: ['model', 'adapter', 'serializer']
},
partials: {
group: 'ui',
types: ['partial']
},
routes: {
group: 'ui',
privateCollections: ['components'],
types: ['route', 'controller', 'template']
},
services: {
types: ['service']
},
utils: {
unresolvable: true
},
views: {
types: ['view']
},
transforms: {
group: 'data',
types: ['transform']
}
}
};
}
72 changes: 72 additions & 0 deletions mu-trees/addon/module-registries/requirejs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* global require, requirejs */
import {
deserializeSpecifier
} from '@glimmer/di';

export default class RequireJSRegistry {

constructor(config, modulePrefix) {
this._config = config;
this._modulePrefix = modulePrefix;
}

normalize(specifier) {
let s = deserializeSpecifier(specifier);

// This is hacky solution to get around the fact that Ember
// doesn't know it is requesting a partial. It requests something like
// 'template:/my-app/routes/-author'
// Would be better to request 'template:my-app/partials/author'
let isPartial = s.type === 'template' && s.name[0] === '-';
if (isPartial) {
s.name = s.name.slice(1);
s.collection = 'partials';
}

let collectionDefinition = this._config.collections[s.collection];
let group = collectionDefinition && collectionDefinition.group;
let segments = [ s.rootName, this._modulePrefix ];

if (group) {
segments.push(group);
}

// Special case to handle definiteCollection for templates
// eventually want to find a better way to address.
// Dgeb wants to find a better way to handle these
// in config without needing definiteCollections.
let ignoreCollection = s.type === 'template' &&
s.collection === 'routes' &&
s.namespace === 'components';

if (s.collection !== 'main' && !ignoreCollection) {
segments.push(s.collection);
}

if (s.namespace) {
segments.push(s.namespace);
}

if (s.name !== 'main') {
segments.push(s.name);
}

if (!isPartial) {
segments.push(s.type);
}

let path = segments.join('/');

return path;
}

has(specifier) {
let path = this.normalize(specifier);
return path in requirejs.entries;
}

get(specifier) {
let path = this.normalize(specifier);
return require(path).default;
}
}
34 changes: 34 additions & 0 deletions mu-trees/addon/resolvers/glimmer-wrapper/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Ember from 'ember';
import GlimmerResolver from '@glimmer/resolver/resolver';
import RequireJSRegistry from '../../module-registries/requirejs';

const { DefaultResolver } = Ember;

/*
* Wrap the @glimmer/resolver in Ember's resolver API. Although
* this code extends from the DefaultResolver, it should never
* call `_super` or call into that code.
*/
const Resolver = DefaultResolver.extend({
init() {
this._super(...arguments);

if (!this.glimmerModuleRegistry) {
this.glimmerModuleRegistry = new RequireJSRegistry(this.config, 'src');
}

this._glimmerResolver = new GlimmerResolver(this.config, this.glimmerModuleRegistry);
},

normalize: null,

resolve(lookupString) {
return this._resolve(lookupString);
},

_resolve(lookupString) {
return this._glimmerResolver.resolve(lookupString);
}
});

export default Resolver;
Loading

0 comments on commit a24d4ae

Please sign in to comment.