Почему webpack не загружает куски?

Я пытаюсь использовать веб-пакет в проекте Typescript, который импортирует библиотеку веб-сборки, созданную emscripten. Все это выполняется внутри настраиваемого визуального элемента Power BI, что делает все еще более увлекательным. Я думаю, что это слишком сложно из-за контекста Power BI, TypeScript и WebAssembly, но на данный момент кажется, что это может быть просто проблема с веб-пакетом.

У меня есть проблема с путями, но я новичок в webpack и немного заблудился. Компиляция работает, но визуальный элемент выдает ошибку ChunkLoadError для фрагмента 1. (Есть два фрагмента.) Вещи, которые могут иметь значение, а могут и не иметь:

  • Я могу указать в браузере "https://localhost:8080/assets/1.js "(или ... 0.js) нормально.
  • Я обнаружил, что (я думаю), поскольку Power BI помещает мой код в iframe, изначально URL-адрес запроса, который пытался выполнить веб-пакет, был "https://app.powerbi.com/13.0.11428.218/assets/0.js". Это вызвало свойство baseUri в сгенерированном им теге скрипта. Изменение output[publicPath] на «https://localhost:8080/assets/» позволило браузеру загрузить фрагменты ( Я вижу, что они успешно входят в инструменты разработчика браузера), но webpack все еще жалуется.
  • Пробовал сдуть node_modules и переустановить. Никаких кубиков.

После вышесказанного вот как выглядит ошибка:

VM1292:119 Uncaught (in promise) ChunkLoadError: Loading chunk 1 failed.
(missing: https://localhost:8080/assets/1.js)
    at Function.requireEnsure [as e] (<anonymous>:119:26)
    at new Visual (<anonymous>:23155:79)
    at r.create [as creator] (<anonymous>:237:14)
    at r.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:6200)
    at https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:14488
    at t.executeSafely (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:18033)
    at t.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:14439)
    at i.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:35819)
    at i.executeMessage (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:42074)
    at i.onMessageReceived (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:41772)

Если я остановлюсь на исключении, я окажусь здесь:

/******/                var error = new Error();
/******/                onScriptComplete = function (event) {
/******/                    // avoid mem leaks in IE.
/******/                    script.onerror = script.onload = null;
/******/                    clearTimeout(timeout);
/******/                    var chunk = installedChunks[chunkId];
/******/                    if(chunk !== 0) {
/******/                        if(chunk) {
/******/                            var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/                            var realSrc = event && event.target && event.target.src;
/******/                            error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/                            error.name = 'ChunkLoadError';
/******/                            error.type = errorType;
/******/                            error.request = realSrc;
/******/            ---->           chunk[1](error);
/******/                        }
/******/                        installedChunks[chunkId] = undefined;
/******/                    }
/******/                };

«Приостановлено из-за отклонения обещания» Но, увы, я действительно не знаю, на что я смотрю или для чего.

Вот мой webpack.config.js:

const path = require("path");
const fs = require("fs");

const webpack = require("webpack");

console.log(require.resolve("powerbi-visuals-webpack-plugin"));
const PowerBICustomVisualsWebpackPlugin = require("powerbi-visuals-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const Visualizer = require("webpack-visualizer-plugin");
const ExtraWatchWebpackPlugin = require("extra-watch-webpack-plugin");

// api configuration
const powerbiApi = require("powerbi-visuals-api");

// visual configuration json path
const pbivizPath = "./pbiviz.json";
const pbivizFile = require(path.join(__dirname, pbivizPath));

// the visual capabilities content
const capabilitiesPath = "./capabilities.json";
const capabilitiesFile = require(path.join(__dirname, capabilitiesPath));

const pluginLocation = "./.tmp/precompile/visualPlugin.ts"; // path to visual plugin file, the file generates by the plugin

// string resources
// const resourcesFolder = path.join(".", "stringResources");
// const localizationFolders = fs.readdirSync(resourcesFolder);
const localizationFolders = [];
const resourcesFolder = ".";

// babel options to support IE11
const babelOptions = {
    presets: [
        [
            require.resolve("@babel/preset-env"),
            {
                targets: {
                    ie: "11",
                },
                useBuiltIns: "entry",
                modules: false,
                corejs: "3",
            },
        ],
    ],
    sourceType: "unambiguous", // Tell babel that the project can contain different module types, not only es2015 modules.
    cacheDirectory: path.join(".tmp", "babelCache"),
};

module.exports = {
    entry: {
        "visual.js": pluginLocation,
    },
    target: "web",
    node: {
        fs: "empty",
    },
    optimization: {
        concatenateModules: false,
        // minimize: true, // enable minimization for create *.pbiviz file less than 2 Mb, can be disabled for dev mode
    },
    devtool: "source-map",
    mode: "development",
    module: {
        rules: [
            {
                parser: {
                    amd: false,
                },
            },
            {
                test: /(\.ts)x|\.ts$/,
                include: /powerbi-visuals-|src|precompile\\visualPlugin.ts/,
                use: [
                    {
                        loader: require.resolve("babel-loader"),
                        options: babelOptions,
                    },
                    {
                        loader: require.resolve("ts-loader"),
                        options: {
                            transpileOnly: false,
                            experimentalWatchApi: false,
                        },
                    },
                ],
            },
            {
                test: /(\.js)x|\.js$/,
                use: [
                    {
                        loader: require.resolve("babel-loader"),
                        options: babelOptions,
                    },
                ],
            },
            {
                test: /\.json$/,
                loader: require.resolve("json-loader"),
                type: "javascript/auto",
            },
            {
                test: /\.less$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: require.resolve("css-loader"),
                    },
                    {
                        loader: require.resolve("less-loader"),
                        options: {
                            paths: [path.resolve(__dirname, "..", "node_modules")],
                        },
                    },
                ],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: require.resolve("css-loader"),
                    },
                ],
            },
            {
                test: /\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp)$/i,
                use: [
                    {
                        loader: "base64-inline-loader",
                    },
                ],
            },
        ],
    },
    resolve: {
        extensions: [".wasm", ".tsx", ".ts", ".jsx", ".js", ".css"],
    },
    output: {
        path: path.join(__dirname, "/.tmp", "drop"),
        publicPath: "https://localhost:8080/assets/",
        chunkFilename: "[name].js",
        filename: "[name]",
    },
    devServer: {
        disableHostCheck: true,
        contentBase: path.join(__dirname, ".tmp", "drop"), // path with assets for dev server, they are generated by webpack plugin
        compress: true,
        port: 8080, // dev server port
        publicPath: "https://localhost:8080/assets/",
        hot: false,
        inline: false,
        // cert files for dev server
        https: {
            pfx: fs.readFileSync(path.join(__dirname, "node_modules/powerbi-visuals-tools/certs/PowerBICustomVisualTest_public.pfx")), // for windows
            passphrase: "##########",
        },
        headers: {
            "access-control-allow-origin": "*",
            "cache-control": "public, max-age=0",
        },
    },
    externals: {
        "powerbi-visuals-api": "null",
        fakeDefine: "false",
        corePowerbiObject: "Function('return this.powerbi')()",
        realWindow: "Function('return this')()",
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "visual.css",
            chunkFilename: "[id].css",
        }),
        new Visualizer({
            filename: "webpack.statistics.dev.html",
        }),
        // visual plugin regenerates with the visual source, but it does not require relaunching dev server
        new webpack.WatchIgnorePlugin([
            path.join(__dirname, pluginLocation),
            "./.tmp/**/*.*",
        ]),
        // custom visuals plugin instance with options
        new PowerBICustomVisualsWebpackPlugin({
            ...pbivizFile,
            capabilities: capabilitiesFile,
            stringResources: localizationFolders.map((localization) => path.join(
                resourcesFolder,
                localization,
                "resources.resjson",
            )),
            apiVersion: powerbiApi.version,
            capabilitiesSchema: powerbiApi.schemas.capabilities,
            pbivizSchema: powerbiApi.schemas.pbiviz,
            stringResourcesSchema: powerbiApi.schemas.stringResources,
            dependenciesSchema: powerbiApi.schemas.dependencies,
            devMode: true,
            generatePbiviz: false,
            generateResources: true,
            modules: true,
            visualSourceLocation: "../../src/visual",
            pluginLocation,
            packageOutPath: path.join(__dirname, "dist"),
        }),
        new ExtraWatchWebpackPlugin({
            files: [
                pbivizPath,
                capabilitiesPath,
            ],
            dirs: [
                "assets",
            ],
        }),
        new webpack.ProvidePlugin({
            window: "realWindow",
            define: "fakeDefine",
            powerbi: "corePowerbiObject",
        }),
    ],
};

person Trygve    schedule 21.11.2019    source источник


Ответы (1)


Теперь работает. Я думаю, что это была комбинация ошибок, усложненных из-за сложности отладки внутри Power BI. Большая часть информации была получена из этого примера и этого template. Я не уверен, что сейчас, после перерыва на День Благодарения, что из этого решило, но вот несколько ключевых изменений:

Импорт WebAssembly: в visual.ts я ссылался на библиотеку emscripten следующим образом:

import module from "emscriptenModule";
const moduleWasm = require("../node_modules/.../module.wasm");
import "regenerator-runtime/runtime";
...
module({
    locateFile(filename: string) {
        if (filename.endsWith(".wasm")) {
            return moduleWasm;
        }
        return filename;
    },
}).then((module) => {
    this.module = module;
    this.Init();
});

webpack.config.js изменения:

module: {
    rules: [
        {
            test: /module\.js$/,
            loader: "exports-loader",
        },
        // wasm files should not be processed but just be emitted and we want
        // to have their public URL.
        {
            test: /module\.wasm$/,
            type: "javascript/auto",
            loader: "file-loader",
        },
    ],
},

Мне также нужно было добавить в свой package.json babel-polyfill и регенератор-runtime.

РЕДАКТИРОВАТЬ При переходе к производству я понял, что выходные данные зависят от сервера разработки по адресу localhost: 8080. Теперь он работает со следующими дополнительными изменениями:

webpack.config.js

        {
            test: /module\.wasm$/,
            type: "javascript/auto",
            loader: "base-64-loader", // <--
        },
...

  output: {
    path: path.join(__dirname, ".tmp", "drop"),
    publicPath: "assets", // <--
    chunkFilename: "[name].js",
    filename: "[name]",
  },
person Trygve    schedule 05.12.2019