最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Typescript auto generated JS file: "Uncaught TypeError: Failed to resolve module specifier" - Sta

programmeradmin3浏览0评论

Currently working on a Blazor project where I want advanced mapping functionality, using the Leaflet.js library with typescript bindings.

I have added leaflet and @types/leaflet as node modules for allowing typescript support.

With everything ready and running, the browser console shows the following error:

Uncaught TypeError: Failed to resolve module specifier "leaflet". Relative references must start with either "/", "./", or "../".

There is one line generated at the top of the JS file:

import * as L from 'leaflet';

If I remove this line, everything works, but I can't remove it manually because it's generated automatically from my TS file, where it's needed.

I suspect my mistake is in tsconfig, or in the typescript file itself.

tsconfig:

{
  "pileOnSave": true,
  "pilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "ES6",
    "strict": true,
    "rootDir": "typescript",
    "outDir": "wwwroot/scripts",
    "esModuleInterop": true,
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ],
}

Typescript file:

import * as L from 'leaflet'

let map: L.Map;
let apiKey: string = "";
let mapStyle: string = 'saitken88/cl2vcjae400aw14qoiawg02c8'
let centreLatLong: L.LatLngExpression = [51.509865, -0.118092];

let mapOptions: L.MapOptions = {
    minZoom: 6,
    maxZoom: 9,
    center: centreLatLong,
    zoom: 6,
    attributionControl: false,
};

function initMap(mapId: string) {
    console.log('init map');
    map = L.map(mapId, mapOptions).setView(centreLatLong, 13);

    L.tileLayer(`/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}`, {
        maxZoom: 18,
        id: mapStyle,
        tileSize: 512,
        zoomOffset: -1,
        accessToken: apiKey,
    }).addTo(map);
}

Compiled JS file:

import * as L from 'leaflet';
let map;
let apiKey = "";
let mapStyle = 'saitken88/cl2vcjae400aw14qoiawg02c8';
let centreLatLong = [51.509865, -0.118092];
let mapOptions = {
    minZoom: 6,
    maxZoom: 9,
    center: centreLatLong,
    zoom: 6,
    attributionControl: false,
};
function initMap(mapId) {
    console.log('init map');
    map = L.map(mapId, mapOptions).setView(centreLatLong, 13);
    L.tileLayer(`/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}`, {
        attribution: 'Map data &copy; <a href=";>OpenStreetMap</a> contributors, Imagery © <a href="/">Mapbox</a>',
        maxZoom: 18,
        id: mapStyle,
        tileSize: 512,
        zoomOffset: -1,
        accessToken: apiKey,
    }).addTo(map);
}
//# sourceMappingURL=property-map.js.map

index.html scripts:

    <script src="scripts/maps/leaflet.js" type="module"></script>
    <script src="scripts/maps/property-map.js" type="module"></script>

Not sure if this is needed but this is my packake.json

{
  "version": "1.0.0",
  "name": "asp",
  "private": true,
  "type": "module",
  "devDependencies": {
    "@types/leaflet": "^1.7.9"
  },
  "dependencies": {
    "leaflet": "^1.8.0"
  }
}

I understand webpack might be a solution here, and have tried it, but frankly it seemed overly cumbersome for just getting one typescript file to pile nicely.

Currently working on a Blazor project where I want advanced mapping functionality, using the Leaflet.js library with typescript bindings.

I have added leaflet and @types/leaflet as node modules for allowing typescript support.

With everything ready and running, the browser console shows the following error:

Uncaught TypeError: Failed to resolve module specifier "leaflet". Relative references must start with either "/", "./", or "../".

There is one line generated at the top of the JS file:

import * as L from 'leaflet';

If I remove this line, everything works, but I can't remove it manually because it's generated automatically from my TS file, where it's needed.

I suspect my mistake is in tsconfig, or in the typescript file itself.

tsconfig:

{
  "pileOnSave": true,
  "pilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "ES6",
    "strict": true,
    "rootDir": "typescript",
    "outDir": "wwwroot/scripts",
    "esModuleInterop": true,
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ],
}

Typescript file:

import * as L from 'leaflet'

let map: L.Map;
let apiKey: string = "";
let mapStyle: string = 'saitken88/cl2vcjae400aw14qoiawg02c8'
let centreLatLong: L.LatLngExpression = [51.509865, -0.118092];

let mapOptions: L.MapOptions = {
    minZoom: 6,
    maxZoom: 9,
    center: centreLatLong,
    zoom: 6,
    attributionControl: false,
};

function initMap(mapId: string) {
    console.log('init map');
    map = L.map(mapId, mapOptions).setView(centreLatLong, 13);

    L.tileLayer(`https://api.mapbox./styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}`, {
        maxZoom: 18,
        id: mapStyle,
        tileSize: 512,
        zoomOffset: -1,
        accessToken: apiKey,
    }).addTo(map);
}

Compiled JS file:

import * as L from 'leaflet';
let map;
let apiKey = "";
let mapStyle = 'saitken88/cl2vcjae400aw14qoiawg02c8';
let centreLatLong = [51.509865, -0.118092];
let mapOptions = {
    minZoom: 6,
    maxZoom: 9,
    center: centreLatLong,
    zoom: 6,
    attributionControl: false,
};
function initMap(mapId) {
    console.log('init map');
    map = L.map(mapId, mapOptions).setView(centreLatLong, 13);
    L.tileLayer(`https://api.mapbox./styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}`, {
        attribution: 'Map data &copy; <a href="https://www.openstreetmap/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox./">Mapbox</a>',
        maxZoom: 18,
        id: mapStyle,
        tileSize: 512,
        zoomOffset: -1,
        accessToken: apiKey,
    }).addTo(map);
}
//# sourceMappingURL=property-map.js.map

index.html scripts:

    <script src="scripts/maps/leaflet.js" type="module"></script>
    <script src="scripts/maps/property-map.js" type="module"></script>

Not sure if this is needed but this is my packake.json

{
  "version": "1.0.0",
  "name": "asp",
  "private": true,
  "type": "module",
  "devDependencies": {
    "@types/leaflet": "^1.7.9"
  },
  "dependencies": {
    "leaflet": "^1.8.0"
  }
}

I understand webpack might be a solution here, and have tried it, but frankly it seemed overly cumbersome for just getting one typescript file to pile nicely.

Share Improve this question asked May 7, 2022 at 8:14 Stuart AitkenStuart Aitken 1,0121 gold badge17 silver badges32 bronze badges 6
  • Is this a node module or local file? This Error normaly only occurres when you want to import a local js file – Dominik Lovetinsky Commented May 9, 2022 at 12:01
  • I guess you're missing "module": "es2020", or "module": "es6", in pilerOptions. See Specify what module code is generated.. – insertusernamehere Commented May 9, 2022 at 12:08
  • @DominikLovetinsky it's a local JS file which is generated automatically from a local typescript file. – Stuart Aitken Commented May 9, 2022 at 12:15
  • @insertusernamehere Didn't work, unfortunately – Stuart Aitken Commented May 9, 2022 at 12:16
  • Try to add ./ to as prefix, because local files need to accessed via relative path – Dominik Lovetinsky Commented May 9, 2022 at 14:30
 |  Show 1 more ment

1 Answer 1

Reset to default 9 +100

The TypeScript piler (tsc), just by itself, does not perform any bundling (on the contrary of webpack typically). It piles each *.ts file individually, and outputs corresponding *.js files representing each of them, without touching the import paths.

Hence your import * as L from 'leaflet'; line is copied as-is into the generated JS file.

When you load that generated JS file with <script src="scripts/maps/property-map.js" type="module"></script>, the browser understands the import syntax (thanks to the module type), but there the module specifier needs to be different from your TypeScript project:

Note: In some module systems, you can omit the file extension and the leading /, ./, or ../ (e.g. 'modules/square'). This doesn't work in native JavaScript modules.

Hence your error message.

You can try these 2 possible solutions:

  1. Get rid of the import
  2. Use ES module absolute import path as an URL

1. Get rid of the import

Assuming your scripts/maps/leaflet.js file is the actual Leaflet library script in UMD form (so that it provides the global L object) (either the source or minified version), then you actually do not need to import it explicitly:

  • In your TypeScript project, you can remove the import line; TS will still know what L is, because it eagerly loads definitions from your node_modules/@types folder:

By default all visible@types” packages are included in your pilation. Packages in node_modules/@types of any enclosing folder are considered visible.

  • In the browser loading the generated JS file without the import, it knows what L is, because it is previously assigned by Leaflet library as UMD

Make sure to add "allowUmdGlobalAccess": true to your pilerOptions in tsconfig.json

2. Use ES module absolute import path as an URL

If you prefer sticking to explicit import, then you could still load Leaflet with an absolute path, but to be patible with the browser native JS modules, absolute paths are actually URL's:

import * as L from "https://cdn.jsdelivr/npm/[email protected]/dist/leaflet-src.esm.js";

Again, the TS piler will copy that path untouched, and the browser will now be happy (and you can even get rid of the <script src="scripts/maps/leaflet.js" type="module"></script> tag in your index.html file; the latter will automatically load Leaflet from the specified URL, as intended from JS modules!)

However, your TS project, on the contrary, will no longer understand that path, and therefore no longer know what L is.

To make it happy again, we can tell TypeScript what that import path is in terms of types, using the paths alias in tsconfig.json:

A series of entries which re-map imports to lookup locations relative to the baseUrl.

{
  "pilerOptions": {
    // ...
    "baseUrl": ".", // Required for paths
    "paths": {
      "https://cdn.jsdelivr/npm/[email protected]/dist/leaflet-src.esm.js": [
        "node_modules/@types/leaflet/index" // Re-map to the type
      ]
    }
  },
  // ...
}

With this, we get the "best of both worlds": type safety, and native browser ES modules (hence no need for a bundler like webpack)

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论