Búsqueda de sitios web

¿Por qué Webpack no puede encontrar un módulo cuando el compilador de TypeScript sí puede?


Estoy intentando usar Fuse en mis aplicaciones de TypeScript. Estoy importando los tipos del módulo con import * as fuselib from 'fuse.js';. Esto se compila bien con tsc. El problema con el que me encuentro es cuando construyo el proyecto usando webpack --config config/webpack.prod.js --progress --profile --bail.

Recibo el error No se puede encontrar el módulo 'fuse.js'. Los tipos de Fuse se pueden encontrar aquí. Al mirar mi JS compilado, no puedo encontrar la palabra fuse.js, así que supongo que Webpack está alterando el nombre. Intenté ignorar la palabra clave fuse.js en el UglifyJsPlugin, pero eso no ayudó.

Mi configuración de Webpack es bastante estándar.

webpack.prod.js
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');

const ENV = process.env.NODE_ENV = process.env.ENV = 'production';

module.exports = webpackMerge(commonConfig, {
    devtool: 'source-map',

    output: {
        path: helpers.root('dist'),
        publicPath: '/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[hash].chunk.js'
    },

    htmlLoader: {
        minimize: false // workaround for ng2
    },

    plugins: [
        new webpack.NoErrorsPlugin(),
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
            mangle: {
                keep_fnames: true,
                except: ['fuse.js']
            }
        }),
            new ExtractTextPlugin('[name].[hash].css'),
            new webpack.DefinePlugin({
                'process.env': {
                    'ENV': JSON.stringify(ENV)
                }
            })
    ]
});
webpack.common.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');

module.exports = {
    entry: {
        'polyfills': './src/polyfills.ts',
        'vendor': './src/vendor.ts',
        'app': './src/main.ts'
    },

    resolve: {
        extensions: ['', '.js', '.ts', '.tsx'],
        modulesDirectories: ['src', 'node_modules']
    },

    module: {
        loaders: [
        {
            test: /\.ts$/,
            loaders: ['awesome-typescript-loader', 'angular2-template-loader', 'angular2-router-loader']
        },
        {
            test: /\.html$/,
            loader: 'html'
        },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                loader: 'file?name=assets/[name].[hash].[ext]'
            },
            {
                test: /\.css$/,
                exclude: helpers.root('src', 'app'),
                loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
            },
                {
                    test: /\.css$/,
                    include: helpers.root('src', 'app'),
                    loader: 'raw'
                }
        ]
    },

    plugins: [
        // for materialize-css
        new webpack.ProvidePlugin({
            "window.jQuery": "jquery",
            "window._": "lodash",
            _: "lodash"
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: ['app', 'vendor', 'polyfills']
        }),
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        })
    ]
};

¿Qué me falta para que Webpack vea el módulo fuse.js?

Respuesta #1

Actualizar

Escribí nuevas declaraciones para la pregunta del bibliotecario basándome en esta respuesta. Todo debería funcionar bien con una versión reciente ya que vienen incluidas con la biblioteca.

Respuesta

Está bien, esto es lo que está pasando y por qué.

En primer lugar, Fuze/index.d.ts intenta declararse a sí mismo tanto como un módulo global como un módulo externo ambiental, pero realiza ambos de manera incorrecta. Esto hace que el uso indebido, como el que provocó tu error, sea casi inevitable.

Contiene una declaración de módulo que contiene una declaración de clase, presumiblemente con la intención de describir la forma del módulo, pero la clase no se exporta.

declare module 'fuse.js' {
  class Fuze // missing one of: export, export =, export default
}

Esto significa que no puedo importar el módulo correctamente y de hecho hay un error de tipo al intentar importar un valor y/o tipo desde él.

Más abajo en Fuse/index.d.ts declara su global

declare const Fuse;

Presumiblemente, basándose en las convenciones y leyendo los comentarios en el JavaScript real, esto está destinado a tener la misma forma que lo que se exporta desde el módulo. Desafortunadamente, tiene el tipo any, que no es ni el mismo tipo que el módulo intentado, porque no es válido, ni el tipo de la clase Fuse, que está atrapada dentro de dicho módulo pero no se exporta...

Entonces, ¿por qué el error? Probablemente tengas uno de los siguientes en algún lugar de tu programa:

import 'fuse.js';

import Fuse from 'fuse.js';

import * as Fuse from 'fuse.js';

seguido por algún uso de Fuse como

const myFuse = new Fuse();

Esto provocará que TypeScript genere una importación para la representación en tiempo de ejecución de Fuse fuse, de modo que puedas usar el valor importado del módulo.

Para solucionar el problema, puedes usar el const Fuse global y no importarlo en ningún lugar. Desafortunadamente, eso no es lo que se pretende. Casi con certeza, el autor quiso tener el siguiente contenido en Fuze/index.d.ts:

export = Fuse;

export as namespace Fuse;

declare class Fuse {
    constructor(list: any[], options?: Fuse.FuseOptions)
    search<T>(pattern: string): T[];
    search(pattern: string): any[];
}

declare namespace Fuse {
    export interface FuseOptions {
        id?: string;
        caseSensitive?: boolean;
        include?: string[];
        shouldSort?: boolean;
        searchFn?: any;
        sortFn?: (a: { score: number }, b: { score: number }) => number;
        getFn?: (obj: any, path: string) => any;
        keys?: string[] | { name: string; weight: number }[];
        verbose?: boolean;
        tokenize?: boolean;
        tokenSeparator?: RegExp;
        matchAllTokens?: boolean;
        location?: number;
        distance?: number;
        threshold?: number;
        maxPatternLength?: number;
        minMatchCharLength?: number;
        findAllMatches?: boolean;
    }
}

Que declara una clase que está disponible globalmente, para aquellos que no usan módulos, o mediante una importación para aquellos que sí lo hacen. Puedes usar la declaración de estilo UMD anterior para obtener la experiencia de tipado que el autor tenía en mente. La que viene incluida con la biblioteca no proporciona información de tipos y en realidad genera errores cuando se utiliza.

Considera enviar una solicitud de extracción al mantenedor con la corrección.

Uso:

Puede utilizar esta declaración de las siguientes maneras:

Estilo CommonJS, AMD o UMD

import Fuse = require('fuse.js');

const myFuseOptions: Fuse.FuseOptions = {
  caseSensitive: false
};
const myFuse = new Fuse([], myFuseOptions);

ES con estilo de interoperabilidad CommonJS

(cuando se usa "module": "system" o "allowSyntheticDefaultImports") con SystemJS, Webpacks recientes, o si se canaliza a través de Babel. A partir de TypeScript 2.7 también se puede usar la nueva opción --esModuleInterop sin ninguna herramienta de módulos adicional o transpiladores.

import Fuse from 'fuse.js';

const myFuseOptions: Fuse.FuseOptions = {
    caseSensitive: false
};
const myFuse = new Fuse([], myFuseOptions);

A partir de TypeScript 2.7, la interoperabilidad con módulos ES ahora está disponible directamente en el lenguaje. Eso significa que no necesitas usar Babel, SystemJS o webpack para escribir importaciones correctas.

Respuesta #2

El truco es proporcionar acceso a la variable global Fuse. Esto se hace con webpack usando el ProvidePlugin

agrega el siguiente plugin al array de plugins de webpack:

    ...
    new webpack.ProvidePlugin({
        "Fuse": "fuse.js"
    })
    ...

Artículos relacionados