x)- [x] bug report -> please search issues before submitting
- [ ] feature request
Related issues might be #5275 and / or #4431
@angular/cli: 1.0.0 (e)
node: 7.8.0
os: linux x64
@angular/common: 4.1.3
@angular/compiler: 4.1.3
@angular/core: 4.1.3
@angular/forms: 4.1.3
@angular/http: 4.1.3
@angular/platform-browser: 4.1.3
@angular/platform-browser-dynamic: 4.1.3
@angular/router: 4.1.3
@angular/cli: 1.0.0
@angular/compiler-cli: 4.1.3
Also reproducible using @ngtools/webpack version 1.4.1, 1.4.0, 1.2.7. I did not check any version before 1.2.7.
Further, the bug was also reproduced on OSX.
After running ng new, I modified the following files like this:
<h1>
{{title}}
</h1>
{{foobar}}
import { Component } from '@angular/core';
declare function require(string):string;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
foobar:string;
constructor() {
let filename = 'foobar';
this.foobar = require('./' + filename + '.html');
}
}
This should be dynamically required
Then, ng serve. I also tested this with ng eject and npm start (same behavior).
ERROR Error: Cannot find module "."
Stack trace:
webpackMissingModule@http://localhost:4200/main.bundle.js:58:67
AppComponent@http://localhost:4200/main.bundle.js:58:34
createClass@http://localhost:4200/vendor.bundle.js:11809:26
createDirectiveInstance@http://localhost:4200/vendor.bundle.js:11643:37
createViewNodes@http://localhost:4200/vendor.bundle.js:13006:49
createRootView@http://localhost:4200/vendor.bundle.js:12911:5
callWithDebugContext@http://localhost:4200/vendor.bundle.js:14126:39
debugCreateRootView@http://localhost:4200/vendor.bundle.js:13586:12
ComponentFactory_.prototype.create@http://localhost:4200/vendor.bundle.js:10832:37
ComponentFactoryBoundToModule.prototype.create@http://localhost:4200/vendor.bundle.js:4368:16
ApplicationRef_.prototype.bootstrap@http://localhost:4200/vendor.bundle.js:5952:40
PlatformRef_.prototype._moduleDoBootstrap/<@http://localhost:4200/vendor.bundle.js:5741:72
PlatformRef_.prototype._moduleDoBootstrap@http://localhost:4200/vendor.bundle.js:5741:13
PlatformRef_.prototype._bootstrapModuleFactoryWithZone/</</<@http://localhost:4200/vendor.bundle.js:5703:21
ZoneDelegate.prototype.invoke@http://localhost:4200/polyfills.bundle.js:2835:17
onInvoke@http://localhost:4200/vendor.bundle.js:5069:28
ZoneDelegate.prototype.invoke@http://localhost:4200/polyfills.bundle.js:2834:17
Zone.prototype.run@http://localhost:4200/polyfills.bundle.js:2585:24
scheduleResolveOrReject/<@http://localhost:4200/polyfills.bundle.js:3262:52
ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.bundle.js:2868:17
onInvokeTask@http://localhost:4200/vendor.bundle.js:5060:28
ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.bundle.js:2867:17
Zone.prototype.runTask@http://localhost:4200/polyfills.bundle.js:2635:28
drainMicroTaskQueue@http://localhost:4200/polyfills.bundle.js:3028:25
ERROR CONTEXT Object { view: Object, nodeIndex: 1, nodeDef: Object, elDef: Object, elView: Object }
localhost:4200 should show:
app works!
This should be dynamically required
The really weird thing about this bug is that it only occurs on the first build of the file. While ng serve or npm start is running, I can open src/app/app.component/ts and save it (without any changes). The app compiles and works just fine.
I tried using System.import instead of require, but the same problem still exists. Doesn't work on first build, works after saving the file while ng is watching.
Explicitly disabling aot doesn't help either (using ng serve --no-aot, ng serve --aot false, or ng serve --aot=false
I experimented a bit more with System.import. My component looks like this:
import { Component } from '@angular/core';
declare var System:any;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
foobar:string;
constructor() {
let filename = 'foobar';
System.import('./' + filename + '.html').then((file) => {
this.foobar = file;
});
}
}
On first compilation, the output is:
** NG Live Development Server is running on http://localhost:4200 **
Hash: 9b75b2c6426cd3555cd8
Time: 12211ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 160 kB {4} [initial] [rendered]
chunk {1} main.bundle.js, main.bundle.js.map (main) 4.32 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.4 MB [initial] [rendered]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
webpack: Compiled successfully.
Then, if I open the file and save it, the output of webpack is:
webpack: Compiling...
Hash: d72262bdb116e279e8db
Time: 455ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 160 kB {4} [initial]
chunk {1} main.bundle.js, main.bundle.js.map (main) 4.32 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.4 MB [initial]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry]
chunk {5} 5.chunk.js, 5.chunk.js.map 58 bytes {1} [rendered]
webpack: Compiled successfully.
As you can see, the incremental build includes a chunk file (and everything works as expected).
ng build and serving it using a standalone server doesn't solve the problem.
Since System.import is deprecated in webpack, I tried using require.ensure, however I get the exact behavior as previously with System.import
I tried using typescript 2.4.0, since it supports native import statements. I had to change the module compiler option in src/tsconfig.app.json for it to compile, but didn't have much luck - it appears to be broken in a different way than this bug and I couldn't get it to work at all.
Same behavior using require.context.
require('./' + filename + '.html') isn't analyzable because it can't determine the value of filename at build time.
There may be something I'm missing here, but it doesn't appear to be logical.
I can see how the dynamic require can be a bit confusing, but as per #6164 it seems like angular cli accounts for System.import.
It would probably help if there was a more hinting context also.
For example your context is specified as "./", instead you you should try for something more like
require(`./templates/${fileName}.html`)
Additionally @Brocco is correct, this isn't really the recommended way to use ContextModules with webpack. Instead you should be writing something like this.
const getLazyFile = (fileName) => System.import(`./templates/${fileName}.html`);
//.....
constructor() {
getLazyFile("seanThomasLarkin").then((module) => {
doSomethingWithThis(module);
});
}
Also, System.import isn't officially dropped from webpack 3 yet. So I would recommend using System.import while you can, and then migrate to import('').
It would be an easy enough to write a friendly codemod or script to convert them all when the time comes. Even the cli team could write one for when TS 2.4 becomes the default.
A different context doesn't help, I used ./ above to have small, simple example to replicate the behavior.
Further, I tried System.import and similar flavors of already, and all suffer from the same problem. Even with your implementation:
import { Component } from '@angular/core';
declare var System:any;
const getLazyFile = (filename) => System.import(`./${filename}.html`);
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
foobar:string;
constructor() {
getLazyFile("foobar").then((file) => {
this.foobar = file;
});
}
}
ng serveโฑ ng serve
** NG Live Development Server is running on http://localhost:4200 **
Hash: 63b02d33648a134691b8
Time: 13663ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 160 kB {4} [initial] [rendered]
chunk {1} main.bundle.js, main.bundle.js.map (main) 4.24 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.4 MB [initial] [rendered]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
webpack: Compiled successfully.
ng serve is still watching and save it again, thus triggering an incremental build:webpack: Compiling...
Hash: da590111e6d74ee0e301
Time: 1078ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 160 kB {4} [initial]
chunk {1} main.bundle.js, main.bundle.js.map (main) 4.24 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.4 MB [initial]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry]
chunk {5} 5.chunk.js, 5.chunk.js.map 58 bytes {1} [rendered]
webpack: Compiled successfully.
This time an additional chunk for lazy loading was correctly created and the app works just fine.
So why was the issue closed? It still exists using System.import?
@Brocco and/or @TheLarkInn any chance this issue is getting re-evaluated? If no, could you please give me a clear answer on why this is not being considered an issue? I don't want to appear as rude but if I don't hear back in this thread, I will file the issue again, as it's blocking our team from migrating to angular-cli.
@pierrebeaucamp We can confirm a similiar issue (https://github.com/angular/angular-cli/issues/6629#issue-234925419). When trying to lazy load .json files, AoT compiler finds only a limited number of files. However, any changes made after that which the watcher is aware of and the changed lazy loaded files are included in the re-compilation.
We think we've (@markuskeunecke) identified the problem. If you want to load dynamic files with AoT such as .json - the file which is to be loaded dynamically is not allowed to have any other .ts file in the same directory or any of it's child directories. Our assumption is that tree-shaking occurs first on all directories where .ts files are found, then the dynamic imports are evaluated by which point any previously iterated directories are ignored. This is why after a hot reload the file is found because no metadata for that current directory has been created and the AoT doesn't tree-shake for the new file/directory.
Thanks for reporting a possible solution!
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
We think we've (@markuskeunecke) identified the problem. If you want to load dynamic files with AoT such as
.json- the file which is to be loaded dynamically is not allowed to have any other.tsfile in the same directory or any of it's child directories. Our assumption is thattree-shakingoccurs first on all directories where.tsfiles are found, then the dynamic imports are evaluated by which point any previously iterated directories are ignored. This is why after ahot reloadthe file is found because nometadatafor that current directory has been created and the AoT doesn't tree-shake for the new file/directory.