我正在尝试在导入emscripten创建的webassembly库的Typescript项目上使用webpack。这一切都在 Power BI 自定义视觉对象中运行,这使一切变得更加有趣。我认为由于Power BI上下文,Typescript和WebAssembly,这特别令人毛骨悚然,但目前似乎它可能只是一个webpack问题。
我有一个我认为是路径问题的问题,但我是 webpack 的新手,有点迷茫。编译有效,但视觉对象为块 1 抛出"ChunkLoadError"。(有两个块。可能相关也可能不相关的内容:
- 我可以将浏览器定向到"https://localhost:8080/assets/1.js"(或...0.js(很好。
- 我发现(我认为(因为 Power BI 将我的代码放在 iframe 中,最初 webpack 尝试的请求 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",
}),
],
};
它现在可以工作了。我认为,事实证明,这是错误的组合,由于在Power BI中调试的困难而变得更加成问题。大多数见解来自此示例和此模板。我不确定,现在感恩节假期之后,哪一件事解决了它,但这里有一些关键的变化:
导入 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",
},
],
},
我还需要将 babel-polyfill 和 regenerator-runtime 添加到我的 package.json 中。
编辑 当转移到生产环境时,我意识到输出依赖于 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]",
},