dialog: viewModel is not found by webpack

I’m submitting a bug report

  • Library Version: 1.0.0-rc.1.0.3

Please tell us about your environment:

  • Operating System: Windows 10

  • Node Version: 6.10.3

  • NPM Version: 5.0.0

  • JSPM OR Webpack AND Version webpack 2.5.1

  • Browser: All

  • Language: TypeScript 2.3

Current behavior: DialogService is not finding my viewModel. It does not report the problem and instead puts up an entirely empty and invisible dialog.

I invoke .open like this:

        return this.dialogService.open(settings) ...

where settings.viewModel is set like this:

import {Prompt} from "../resources/commonDialogs/prompt";
.
.
.
settings.viewModel = Prompt;

(Note: Prompt is my own, not from the Aurelia-dialog example.)

DialogService sees the Prompt viewModel function and tries to do something with it here:

 DialogService.prototype.ensureViewModel = function (compositionContext) {
        if (typeof compositionContext.viewModel === 'function') {
            compositionContext.viewModel = __WEBPACK_IMPORTED_MODULE_1_aurelia_metadata__["Origin"].get(compositionContext.viewModel).moduleId;
        }
        if (typeof compositionContext.viewModel === 'string') {
            return this.compositionEngine.ensureViewModel(compositionContext);
        }
        return Promise.resolve(compositionContext);
    };

But WEBPACK_IMPORTED_MODULE_1_aurelia_metadata__["Origin"].get(compositionContext.viewModel).moduleId returns undefined.

All of the relevant code is contained in a vendor.js file generated by webpack.

Expected/desired behavior:

  • What is the expected behavior? Should find my viewmodel or else report an error. If there are additional steps that I’m not following, then they should be documented.

  • What is the motivation / use case for changing the behavior? Supplying a viewModel in the settings is supposed to work. I assume webpack is part of the use case as well.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 66 (20 by maintainers)

Most upvoted comments

Not anytime, but very likely. If the module that contains your class is added to a concatenated module, then it won’t work.

@dkent600 I meant ES import a viewmodel that is then feeded as a ctor to aurelia-dialog.

With Webpack it creates unsolvable origin problems when using DLL and now when using ModuleConcatenationPlugin.

Instead you should pass the module name, wrapped with moduleName.

Using ES import in your code is fine. In fact it’s the module system I recommend when using Webpack to bundle your app (it supports advanced optimizations such as ModuleConcatenationPlugin, tree shaking or exports renaming).

@dkent600 I’ll try to fix it.

OK, now we’re talking. It’s @niieani’s magic, but thankfully I understand that part well enough that I can explain.

Aurelia has a lot of reliance on the origin (module name) of resources (viewmodels & co.). As long as modules are loaded through aurelia-loader, they are tagged so that this works (it’s done inside aurelia-loader-webpack).

The issue arises when something that was not loaded through aurelia-loader is suddenly asked for origin by Aurelia. This happens very rarely, and the pattern above (imported VM for aurelia-dialog) is the main occurrence of this.

Before I even got started on the Webpack 2.0 plugin, @niieani had identified this issue and came up with the following solution: he would look at every loaded module for an export that matches the object whose origin is asked for. This is done here: https://github.com/aurelia/metadata/blob/master/src/origin.js#L37-L56

As you can see it relies on PLATFORM.eachModule. How do you iterate all modules? Well, it’s not part of the official API (it grabs directly at internals), but aurelia-loader-webpack provides this implementation: https://github.com/aurelia/loader-webpack/blob/master/src/aurelia-loader-webpack.ts#L90-L100

And this is where the limitation lies. At the time when @niieani did this we did not have DLL support, which I added later. If you have some familiarity with Webpack internals you understand that the code I linked above is only looking at modules inside the entry bundle. Any other bundle (DLL… and maybe code splits, I would need to double-check that) is not examined.

This is the problem you’ve identified.

The root cause is in aurelia-loader-webpack, the solution would be to dig deeper into Webpack’s internals and look at modules into other (e.g. DLL) bundles that have been loaded. As this is all undocumented stuff, it would need a bit of research.

@jods4

  1. Using your 01-No_splits demo I opened the prompt from ferngully-aurelia-tools successfully - the modification I did in the ferngully-aurelia-tools plugin was:
    • in services/dialog-service
      import {Prompt} from '../resources/commonDialogs/prompt';
      // ...
      settings.viewModel = Prompt;
      
    • removed all PLATFORM.moduleName related to the prompt - in the plugin
    • and it just works, Origin.get(compositionContext.viewModel).moduleId is resolved OK.
  2. In the provided repro with the same modification to the plugin Origin.get(compositionContext.viewModel).moduleId resolves to undefined. The only difference I see is in the build setup - one bundle vs multiple bundles. The vendors bundle, in which is ferngully-aurelia-tools, is build with the DLL plugin. How can this affect your magic for Origin.get(...).moduleId you will say.

OK, so had to launch it from VS to get the dist rebuild. I’ll post more tomorrow.

This should work for finding it:

import { PLATFORM } from 'aurelia-pal';
// ...
settings.viewModel = PLATFORM.moduleName('../resources/commonDialogs/prompt');

Also do check the aurelia-webpack wiki since here I see 2 issues:

  • no error is propagated - dialog related(will work on this)
  • can not resolve moduleId - webpack related