I'm trying to build project that contains some components with dynamic imports, like:
import {Directive, Input, ElementRef} from '@angular/core';
@Directive({
selector: '[saKnob]'
})
export class KnobDirective {
@Input() saKnob: any;
constructor(private el: ElementRef) {
import('jquery-knob').then(()=>{
this.render()
})
}
render(){
$(this.el.nativeElement).knob(this.saKnob || {})
}
}
The dynamic import on the constructor seems to be the problem. I'm getting the following error:
ERROR in ./src/app/shared/forms/input/knob.directive.ts 15:8
Module parse failed: 'import' and 'export' may only appear at the top level
(15:8)
You may need an appropriate loader to handle this file type.
| var _this = this;
| this.el = el;
> import('jquery-knob').then(function () {
| _this.render();
| });
As far as I researched, this kind of import is supported since Angular 4, and I'm using Angular 7.
Does anyone have an idea on what could be the problem?
* UPDATE *
As pointed by some answers, I was already using esnext on my tsconfig.app.file
file:
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "esnext",
"types": [],
"paths": {
"@app/*": ["app/*"],
"@env/*": ["environments/*"]
}
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
And here's the contents of the tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "es2015",
"target": "es5",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"],
"paths": {
"@app/*": ["src/app/*"],
"@env/*": ["src/environments/*"]
}
}
}
My typescript version is ~3.1.6
.
TypeScript 2.4 added support for dynamic import() expressions, which allow us to asynchronously load and execute ECMAScript modules on demand. This means that we can conditionally and lazily import other modules and libraries.
To load dynamically a module call import(path) as a function with an argument indicating the specifier (aka path) to a module. const module = await import(path) returns a promise that resolves to an object containing the components of the imported module.
An import is what you put in the imports property of the @NgModule decorator. It enables an Angular module to use functionality that was defined in another Angular module. An export what you put is the exports property of the @NgModule decorator.
In short:
It's caused by npm issue appeared with @angular-devkit/build-angular
update from 0.12.x
to 0.13.x
and related webpack update under the hood from 4.28.x
to 4.29.x
.
Possible solutions (workarounds):
@angular-devkit/build-angular
0.12.x acorn
dependency (e. g. npm i --save acorn
will add 6.1.1
at the time of writing). Another popular workaround is to run npm update acorn --depth 20 && npm dedupe
.Details:
Have recently stumbled upon similar issue after project update from Angular 7.2 to Angular 7.3. Before update build was fine, and esnext
as target
was already specified in tsconfig.json
.
After some tests I've revealed that it relates to @angular-devkit/build-angular
and found issues (13767, 13793) in angular-cli that surprisingly were closed.
But alan-agius4 comment in issue 13793 shed some light to real origin: invalid hoisting for peer dependencies; explained in details by sokra in this comment.
There were already accepted pull request #147 for issue #4794, but then it was reverted in pull request #152 and issue #4794 remains opened at the time of writing.
dynamic-import
You can't just call import
like that. You need to import SystemJS
first. then call its import
method.
import('jquery-knob').then(()=>{
this.render()
})
stackblitz: https://stackblitz.com/edit/angular-bkbqkj
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With