Skip to content
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

feat: adds the strict resolver as an option #837

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 97 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,115 @@ export default class App extends Application {
// ...snip...
```

## Strict

> Originally from <https://github.com/stefanpenner/ember-strict-resolver>
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

in app/resolver.js

```js
export { default } from "ember-strict-resolver";
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved
```

_For additional improvements when fully using the ember-strict-resolver monkey patching the registry to no longer cache and simply returning the values passed like the following can be produce extra performance._
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

```js
// disable the normalization cache as we no longer normalize, the cache has become a bottle neck.
Ember.Registry.prototype.normalize = function (i) {
return i;
};
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I have a broader comment, which is: if we think this is worth doing, we should build a public API for it. It's one thing to do this as an experiment in our app (a successful one, I might add!) but we should not recommend others do it until there is a public API, or else we're setting people up for pain.


## Migration

Migrating away from use the _ember-resolver/classic_ can be done in piecemeal by supporting a sub-set of the old resolution formats.
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

> normalize is needed, because without it you will get errors related to failing to be able to inject services that were never normalized in the registry.
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

```js
// app/resolver.js

import Resolver from "ember-strict-resolver";
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

export default class extends Resolver {
legacyMappings = {
"service:camelCaseNotSupported": "service:camel-case-not-supported",
};

resolve(_fullName) {
return super.resolve(this.legacyMappings[_fullName] || _fullName);
}

normalize(_fullName) {
return this.legacyMappings[_fullName] || _fullName;
}
}
```

This will allow you file PRs with libraries that currently do not support the strict resolver in its entirety.

In the event that you have a component that is failing to resolve correctly with the error `Attempted to lookup "helper:nameOfVariable". Use "helper:name-of-variable" instead.` please convert your template to use explicit-this. The template lint can be enabled by turning on [no-implicit-this](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-implicit-this.md).
gabrielcsapo marked this conversation as resolved.
Show resolved Hide resolved

An example of what this looks like is the following

```hbs
// addon/components/templates/foo.hbs

<div>
{{fullName}}
</div>
```

This will result in the error, `Attempted to lookup "helper:fullName". Use "helper:full-name" instead.`. The fix for this would be to decide if this is a argument being passed into foo or if this is a local property.

_fullName_ is coming from an invocation of _Foo_ like the following:

```
<Foo
@fullName="The Teamster"
/>
```

Then the fix for your template would be:

```hbs
// addon/components/templates/foo.hbs

<div>
{{@fullName}}
</div>
```

If _fullName_ is a property on your component the fix would be:

```hbs
// addon/components/templates/foo.hbs

<div>
{{this.fullName}}
</div>
```

## Addon Development

### Installation

* `git clone` this repository
* `npm install`
* `bower install`
- `git clone` this repository
- `npm install`
- `bower install`

### Running

* `ember server`
* Visit your app at http://localhost:4200.
- `ember server`
- Visit your app at http://localhost:4200.

### Running Tests

* `ember test`
* `ember test --server`
- `ember test`
- `ember test --server`

### Building

* `ember build`
- `ember build`

For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/).
130 changes: 130 additions & 0 deletions addon/addon/resolvers/strict/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/* globals requirejs */

import { warn } from '@ember/debug';
import { dasherize } from '@ember/string';
import { DEBUG } from '@glimmer/env';

import require from 'require';

export default class Resolver {
constructor(attrs) {
if (attrs) {
this.namespace = attrs.namespace;
}
// secret handshake with router to ensure substates are enabled
// see https://github.com/emberjs/ember.js/blob/a429dc327ee6ef97d948a83e727886c75c6fe043/packages/%40ember/-internals/routing/lib/system/router.ts#L344
this.moduleBasedResolver = true;
}

static create(args) {
return new this(args);
}

has(moduleName) {
return moduleName in (requirejs.entries || requirejs._eak_seen);
}

parseFullName(fullName) {
let prefix, type, name;

let fullNameParts = fullName.split('@');

if (fullNameParts.length === 3) {
if (fullNameParts[0].length === 0) {
// leading scoped namespace: `@scope/pkg@type:name`
prefix = `@${fullNameParts[1]}`;
let prefixParts = fullNameParts[2].split(':');
type = prefixParts[0];
name = prefixParts[1];
} else {
// interweaved scoped namespace: `type:@scope/pkg@name`
prefix = `@${fullNameParts[1]}`;
type = fullNameParts[0].slice(0, -1);
name = fullNameParts[2];
}

if (type === 'template:components') {
name = `components/${name}`;
type = 'template';
}
} else if (fullNameParts.length === 2) {
let prefixParts = fullNameParts[0].split(':');

if (prefixParts.length === 2) {
if (prefixParts[1].length === 0) {
type = prefixParts[0];
name = `@${fullNameParts[1]}`;
} else {
prefix = prefixParts[1];
type = prefixParts[0];
name = fullNameParts[1];
}
} else {
let nameParts = fullNameParts[1].split(':');

prefix = fullNameParts[0];
type = nameParts[0];
name = nameParts[1];
}

if (type === 'template' && prefix.lastIndexOf('components/', 0) === 0) {
name = `components/${name}`;
prefix = prefix.slice(11);
}
} else {
fullNameParts = fullName.split(':');

prefix = this.namespace.modulePrefix;
type = fullNameParts[0];
name = fullNameParts[1];
}

return {
prefix,
type,
name
}
}

moduleNameForFullName(fullName) {
let moduleName;

const { prefix, type, name } = this.parseFullName(fullName);

if (name === 'main') {
moduleName = `${prefix}/${type}`;
} else if (type === 'engine') {
moduleName = `${name}/engine`;
} else if (type === 'route-map') {
moduleName = `${name}/routes`;
} else if (type === 'config') {
moduleName = `${prefix}/${type}/${name.replace(/\./g, '/')}`;
} else {
moduleName = `${prefix}/${type}s/${name.replace(/\./g, '/')}`;
}

return moduleName;
}

resolve(fullName) {
const moduleName = this.moduleNameForFullName(fullName);

if (this.has(moduleName)) {
// hit
return require(moduleName)['default'];
}
// miss
}

normalize(fullName) {
if(DEBUG) {
const { type } = this.parseFullName(fullName);

if(['service'].includes(type)) {
warn(`Attempted to lookup "${fullName}". Use "${dasherize(fullName)}" instead.`, !fullName.match(/[a-z]+[A-Z]+/), { id: 'ember-strict-resolver.camelcase-names' });
}
}

return fullName;
}
}
10 changes: 6 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading