Strange problem with AU1, inheritance, DialogService and dumber

Greetings.

My setup is Aurelia 1 + TypeScript + dumber.

I am encountering a strange problem with this (simplified) setup which involves class inheritance and DialogService.

In short:

  • base component which opens a dialog using its class as the viewModel
  • two derived components
  • page which requires both components

At runtime, the page breaks with the following error:

Edge:

TypeError: Class extends value undefined is not a constructor or null

Firefox:

TypeError: class heritage inspectionAnswerListBase_1.InspectionAnswerListBase is not an object or null

What happens is that the first component works correctly, but in the second one the base class is undefined. Note that this is not dependent on the actual components but only in their order (the first works, the second breaks).
However, specifying the viewModel for the dialog as a module path (string) instead of an import DOES work.

Out tsconfig.json is:

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": "src/",
    "sourceMap": true,
    "target": "ES2017",
    "module": "AMD",
    "declaration": false,
    "removeComments": true,
    "importHelpers": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "noEmitOnError": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "alwaysStrict": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitAny": false,
    "noImplicitOverride": true
  },
  "exclude": [
    "node_modules",
    "obj",
    "bin"
  ]
}

I do not seem to be able to pinpoint the exact source of the problem, I suspect some odd edge case.
I am not even sure whether I should expect a solution, but maybe something here rings a bell for someone.

1 Like

you probably got a circular dependency somewhere somehow, causing the Base to be evaluated too early before the module itself is ready. Hence it’s returning undefined because of the way Webpack bundles the modules. Check your dependency graph or maybe simplify the base.ts file.

Thank you from your attention. Also tagging @huochunpeng in case this has something to do with dumber.
I had thought about circular dependencies but I could not find any (I plan to check this again more thouroughly later).

I have added to each file a simple console.log after the various imports and they seem to be loaded in the wrong order:

required: Derived2
required: SomeComponent
required: Base
required: Derived1

I would expect Derived2 to be loaded after SomeComponent and Base, which is what happens if I remove the reference to SomeComponent.

required: Base
required: Derived1
required: Derived2

There seems to be something odd with the dialog viewmodel but I have yet to discover what.

The first thing you can debug is to use webpack with the same code, to see if you can duplicate same issue. If webpack has no issue, then it’s probably a bug in dumber that I can help to look into.

“Using Webpack with the same code” does not really seem straightforward; I will have to look more into it.
For now more testing showed the problem might be related to SomeComponent and Base being in different bundles; there might be some hidden circular dependency somewhere.

Circular deps can only be supported within one bundle. You can update your dumber config, just comment out code splitting, to see if it works inside one bundle.

I don’t remember whether dumber log a warning when circular deps are detected, you can have a look of your console log when bundling.

I can confirm that by not splitting code the problem goes away. I will have to check for circular dependencies more thoroughly.
If indeed it is due to circular dependencies, do you have any idea on why the problem happens ONLY if the component is required more than one time?

My guess is your SomeComponent has a dep (or deep dep through another import) on Base.

Another suggestion, instead of using abstract base class, you can use DI + service pattern to share common functionality.

I think current trend is to avoid using inheritance in OOP.

class CommonService {
  openDialog() {...}
}

class Comp1 {
  constructor(commonService: CommonService) {}
  hitButton() {
    this.commonService.openDialog().then(...)
  }
}
2 Likes