-
-
Notifications
You must be signed in to change notification settings - Fork 622
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(config): ability to declare global resources by classes #858
Conversation
Couldn't we just have |
You meant If so I would say we alias it to |
src/framework-configuration.js
Outdated
import { metadata } from 'aurelia-metadata'; | ||
|
||
|
||
/** @typedef ConfigInfo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't seen this notation before. How is this different from declaring an interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think interface won't work in the mixed environment so I used jsdoc instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the jsdoc notation is similar to interface, except that it could work without any issues in a JS environment. Once we switch to TS, it can simply be changed to interface
No, I just mean have So, why should the proposed API require a developer write redundant code that tells the framework that the custom attribute is a custom attribute (as an example)? Another thought would be to just overload the |
Yes, that's what I wanted to do, but it needs some discussion. I would prefer to have it right at About relying on the class name, I was a bit in @jods4 side while doing it, as it doesn't go well with minifier or bundler that mangle named export. But there is no reason to not support that. |
Class names being mangled by a minifier isn't much of a problem. Just create Babel/TypeScript plugins that automatically add the metadata at compile time 😀 Then the developer had the ear of using a naming convention, with the size savings that come with a minifier. |
Maybe we can treat it the way we currently treat it, find explicit configuration that was added via decorators, then convention all the way and fallback to custom elements. Then it's devs' job to ensure that a value converter isn't used as a custom element 😄 |
I'm fine with that. I'm also fine with keeping the function names you have currently. I just want to have |
I have updated the PR, |
I like this a lot! |
f2dd6da
to
205b027
Compare
205b027
to
f6a434f
Compare
It looks to me @bigopon has written text that could pass partly as documentation. So I think if documentation should be amended also and if so, which pieces should be amended? |
Some thoughts. One nice thing about this API is that it moves away from module names embedded in strings, which is not friendly with modern JS tools. Adopting this provides a path to get rid of As far as registering multiple components, what about One limitation of global resources is that they can't be lazy-loaded. Removing modules as string has been discussed a few times previously and the most important problem that has not been adressed here is how the "origin" concept and relative sub-resources are loaded by |
I was thinking about global resources lazy loading, but couldn't find a good reason why a global resources needs to be both declared global, and lazy loaded, as by the time application starts, all global resources need to be ready. Otherwise, they could have been in local view resources hierarchy.
|
It's a nice API, |
@bigopon Great work. Just let me know when you feel it's ready. |
(Emphasis mine) This is how it works today. If we add a lazily loaded API such as suggested Today this is achieved with local resources, but if you use a large component on a handful of pages, it becomes a bit annoying and registering it globally is simpler.
Beware, here be dragons. I would really like if we moved away from the That said, Basically what I'm saying is: yes let's drop origin when working we new apis but be prepared for some lib fixing.
|
@jods4 This API benefits plugin distribution more than app development. Normally, in an application, I wouldn't want to mess with file path (read Origin), while for plugin, I can't care less about it, and all I want is to packed things into one file to distribute easily. Probably it's @EisenbergEffect 's call |
I'm really happy for this pr as it directs aurelia to be more bundler/loader agnostic 🕺 |
Ok, I want to do a final review of this, but I'd like to get this in soon. Assuming we have the warning that this registration method doesn't work with conventions, are there any objections to us merging this and pushing it out in a release (perhaps next week)? |
quick question, will this pr allow us to declare local dependencies by passing the @inlineView('<template>cool content</template>')
@customElement('custom-element')
class CustomElement {}
const view = `
<template>
cool content <custom-element></custom-element>
</template>
`;
@inlineView(view, [CustomElement])
class App {} |
@obedm503 theoretically yes. It could be phase 2 of this feature, where we can do things you just suggested, and also something like following (though I'm not sure, need some experiments) // custom-element.js
@inlineView('<template>cool content</template>')
@customElement('custom-element')
class CustomElement {}
// app.js
const view = `
<template>
cool content <custom-element></custom-element>
</template>
`;
@inlineView(view, () => import('custom-element.js'))
class App {} |
@obedm503 Update to the above, the loading resources locally has very little to do with this PR, but it's what I'm thinking of. Sorry for the misinformation. |
guys, when do you plan to merge this? |
@stalniy We may not get this exact PR in, but we will get this feature in. After reviewing this PR, we started to go back and take these ideas and push them down into the core of the framework. @bigopon has put together a number of PRs to different parts of the framework to make this even more official, and not just something that is patched up on the top layer. We've merged a number of those PRs already and I believe he's got one or two more coming that will finish off the new feature set. |
@bigopon @EisenbergEffect |
@JSeligsohn the documentation of this feature isn't in yet ... With the latest version of the core modules, instead of the following: aurelia.use.globalResources([
'my-element',
'my-attribute'
]) you can do: import { MyCustomElement } from './my-element'
import { MyCustomAttribute } from './my-attribute'
aurelia.use.globalResources(
MyCustomElement,
MyCustomAttribute,
) For custom elements that are registered via their constructors, their views need to be on the static property class MyCustomElement {
static $view = '<template>${message}</template>'
} If external resources need to be imported into a view, use a property class MyCustomElement {
static $view = {
template: '<template>${message}</template>',
dependencies: [MyAttribute, () => import('my-module').then(myModule => myModule.MyClass)]
}
} Another feature is the ability to configure resources without having to import anything from Aurelia core modules, this is to make it simpler to develop a plugin/share code. To do this, use a static property Example for a custom element: class MyCustomElement {
static $resource = {
name: 'my-el', // equivalent of @customElement('my-el')
type: 'element', // can be omitted if element
processContent: () => {...}, // equivalent of @processContent(...)
}
} or custom attribute: class MyCustomAttribute {
static $resource = {
type: 'attribute',
name: 'my-attr',
// etc...
}
}
|
@bigopon Thank you! This is very helpful. |
@JSeligsohn you can see from this example: class MyCustomElement {
static $view = {
template: '<template>${message}</template>',
dependencies: [
MyAttribute
]
}
} It can also be like this class MyCustomElement {
static $view = {
template: '<template>${message}</template>',
dependencies: [
MyAttribute,
MyAnotherCustomElement
]
}
} |
@bigopon Are you saying the |
@JSeligsohn I was answering this Q:
Basically there' 2 types of resources: global, and local. Both types can be registered using either constructor or string for module path. For the above example, I was giving an example of how to register a custom element, with constructor reference |
Oh, okay, I see now what you mean. Thanks for the clarification! |
Tangentially related, @bigopon, are you aware of a way to actually create a custom element "on the fly"? Let's say I dynamically built an html "view" as well as a "viewModel" (class), could I then put those two together and make it into a custom element at runtime? The additional challenge here seems to me to be that aurelia can only reference "modules" that were already built at compile time, but no way to pass in a runtime created class. I welcome any suggestions. |
At the moment, Aurelia only absorbs global resource declarations via modules loading, which also enables all sorts of lazy loading: component level, router level, but there is no convenient way to load resources directly (or synchronously) via implementation (classes). This PR enables that scenario, which should also help bundler bundle Aurelia application more effectively.
4 new APIs added to
FrameworkConfiguration
:customElement(implementation: Function): void
customAttribute(implementation: Function): void
bindingBehavior(implementation: Function): void
valueConverter(implementation: Function): void
Examples:
Current way
new way
How this helps: we see that classes / implementations are used directly and thus, should be more friendly with bundlers. An example would be tree shaking: if we are to use module name via string, there would be no way to tree shake an unused resources, but if we are to use classes, it would be possible. Following example is how
aurelia-templating-resources
would look like if this was supported:Current way
New way
So it can be tree shake via environment variables, with the help of minifiers like the following examples:
New way + environment variables + tree shaking
And then in bundlers, we can easily shake default resources via defining the environment variables. For example, say we know our application won't use compose, just need to do like following in webpack configuration:
With this enabled, we can offer more out of the box binding behaviors/ value converters with fool proof tree shaking configuration and bundle size will never go off the roof.
Caveats:
Summary:
pros
cons
API
The api could be improved, so it doens't have to be too tedious:
From:
To:
Update 1:
I have overloaded the method
globalResources
. Now it can take either string as module name, class (function) as implementation or one object for explicit configuration. Example:It needs to be mentioned explicitly somewhere in the doc that loading resources via module name string will always come after the loading resources via implementation, as one is loaded during POST task, and the other is loaded during PLUGIN(MAIN) task
Update 2: Some details for discussion
ViewEngine.importViewResources
:How resources are loaded currently with the new API: synchronous resources will be loaded first, if it is a custom element, its view will be loaded after all resources that is loaded via module name have been loaded..
MyBindingBehavior
class will not be registered asmy
binding behavior when building for production using the new API.