I have a Stencil ponent and, as part of its business logic, I need to use an external Javascript file mylib.js
. The Javascript file contains some business logic the Stencil ponent should use.
Here's my ponent:
import { Component, Element, Prop, h, Host } from '@stencil/core';
import moment from 'moment';
import mylib from 'src/js/mylib.js';
@Component({
tag: 'dashboard-widget',
styleUrl: 'dashboard-widget.css',
shadow: false
})
export class DashboardWidget {
@Element() el: HTMLElement;
@Prop() canvas: HTMLElement;
@Prop() channelName: string = "";
@Prop() channelValue: string = "";
@Prop() isBlinking: boolean = true;
ponentDidLoad() {
console.log(mylib.test());
}
render() {
return (
<Host>
<div class="card-content card-content-padding">
<b>{this.channelName}</b>
<h1 class="dashboard-card-value">{this.channelValue}</h1>
<canvas class="dashboard-card-canvas"></canvas>
</div>
</Host>
);
}
}
I keep getting the error
TypeScript Cannot find module 'src/js/mylib.js'.
I tried:
import mylib from 'src/js/mylib.js';
import 'src/js/mylib.js';
import 'mylib' from 'src/js/mylib.js';
to no avail.
The mylib.js
file is inside the js
folder.
The online documentation doesn't mention at all how to import external libraries. I've been successful importing moment.js but only because I installed it first by doing:
npm install moment
and then importing it inside the ponent by doing:
import moment from 'moment';
I also tried to "import" the external JS library by referencing it inside the index.html
<script src="assets/js/mylib.js"></script>
The library is available outside the ponent but not inside it
I have a Stencil ponent and, as part of its business logic, I need to use an external Javascript file mylib.js
. The Javascript file contains some business logic the Stencil ponent should use.
Here's my ponent:
import { Component, Element, Prop, h, Host } from '@stencil/core';
import moment from 'moment';
import mylib from 'src/js/mylib.js';
@Component({
tag: 'dashboard-widget',
styleUrl: 'dashboard-widget.css',
shadow: false
})
export class DashboardWidget {
@Element() el: HTMLElement;
@Prop() canvas: HTMLElement;
@Prop() channelName: string = "";
@Prop() channelValue: string = "";
@Prop() isBlinking: boolean = true;
ponentDidLoad() {
console.log(mylib.test());
}
render() {
return (
<Host>
<div class="card-content card-content-padding">
<b>{this.channelName}</b>
<h1 class="dashboard-card-value">{this.channelValue}</h1>
<canvas class="dashboard-card-canvas"></canvas>
</div>
</Host>
);
}
}
I keep getting the error
TypeScript Cannot find module 'src/js/mylib.js'.
I tried:
import mylib from 'src/js/mylib.js';
import 'src/js/mylib.js';
import 'mylib' from 'src/js/mylib.js';
to no avail.
The mylib.js
file is inside the js
folder.
The online documentation doesn't mention at all how to import external libraries. I've been successful importing moment.js but only because I installed it first by doing:
npm install moment
and then importing it inside the ponent by doing:
import moment from 'moment';
I also tried to "import" the external JS library by referencing it inside the index.html
<script src="assets/js/mylib.js"></script>
The library is available outside the ponent but not inside it
Share Improve this question edited Mar 16, 2020 at 11:08 Gianluca Ghettini asked Mar 11, 2020 at 10:09 Gianluca GhettiniGianluca Ghettini 11.6k24 gold badges99 silver badges167 bronze badges2 Answers
Reset to default 14Since you're mentioning Moment.js, I'll explain how to load that first. It's possible to do it the way you did by importing it inside your ponent's module, however that will result in a large bundle size because the npm package of moment is not targeted for browsers but for use in Node.js projects where bundle size doesn't matter. Moment.js distributes browser bundles inside the package though. So what you can do is add a copy task to your Stencil output target to copy node_modules/moment/min/moment.min.js
into your build directory:
// stencil.config.ts
import { Config } from '@stencil/core';
export const config: Config = {
namespace: 'my-app',
outputTargets: [
{
type: 'www',
copy: [
{
src: '../node_modules/moment/min/moment.min.js',
dest: 'lib/moment.min.js'
}
]
}
]
};
Then, as you already tried with your lib, you can load that script in src/index.html
:
<script src="/lib/moment.min.js"></script>
However, your Typescript project won't know yet that moment
is available in the global scope. That's easy to solve though, you can just add a declaration file somewhere in your project, e. g. src/global.d.ts
with the content
import _moment from 'moment';
declare global {
const moment: typeof _moment;
}
For your test files which run in the Node.js context, not a browser context, you can either import moment or also add moment
to the global scope, by creating a file (e. g. jest-setup-file.js
) with the content
global.moment = require('moment');
and then in stencil.config.ts
you just add the setupFilesAfterEnv
field to testing
:
{
testing: {
setupFilesAfterEnv: ['<rootDir>/jest-setup-file.js']
}
}
If you don't need the script in your whole application but only when a specific ponent is loaded, it makes more sense to only load the script from within that ponent. The easiest way to do that is to create a script
element and add it to the DOM:
declare const MyLib: any; // assuming that `mylib.js` adds `MyLib` to the global scope
export class MyComp {
ponentWillLoad() {
const src = "assets/js/mylib.js";
if (document.querySelector(`script[src="${src}"]`)) {
return; // already exists
}
const script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
}
}
Your script/library will most likely also contribute a variable to the global scope, so you'll have to let Typescript know again, which you can do using the declare
keyword to declare that a variable exists in the global context (see https://www.typescriptlang/docs/handbook/declaration-files/by-example.html#global-variables).
As another example, here's a helper I wrote to load the google maps api:
export const importMapsApi = async () =>
new Promise<typeof google.maps>((resolve, reject) => {
if ('google' in window) {
return resolve(google.maps);
}
const script = document.createElement('script');
script.onload = () => resolve(google.maps);
script.onerror = reject;
script.src = `https://maps.googleapis./maps/api/js?key=${googleApiKey}&libraries=places`;
document.body.appendChild(script);
});
(The google
type es from the @types/googlemaps
package)
I can then use this like
const maps = await importMapsApi();
const service = new maps.DistanceMatrixService();
To import files that aren't installed by NPM you can use relative paths prefixed with ./
or ../
:
import mylib from '../../js/mylib.js';
You can import everything that is exported from that JS file and even use named imports:
mylib.js
export function someFunction() {
// some logic
}
dashboard-widget.tsx
import { someFunction } from '../../js/mylib.js`;
const result = someFunction();