带有 webpack 和 link 到 node_modules 的 VS 代码扩展

VS Code extension with webpack and link to node_modules

我创建了一个使用 Webview. In the Webview I have a link to a file in the node_modules folder which I added to the html as recommended by various sources (e.g. vscode-codicons-sample 的 VS Code 扩展:

const codiconsUri = webview.asWebviewUri(
  Uri.joinPath(this.context.extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')
);

并在 html 中:

<link href="${codiconsUri}" rel="stylesheet" />

现在想用webpack来打包。我按照 Visual Studio 代码 here.

中的说明进行操作

.vscodeignore 文件中,我按照说明排除了 node_modules 文件夹。 (这是捆绑的主要原因)

当我现在打包项目时,当然,.vsix 文件中不存在 node_modules,因此 Uri 无效。

你会如何解决这个问题? (除了codicons,我用这个方法插入了更多的链接)

提前致谢!

我决定采纳@MikeLischke 的建议,将需要的文件复制到打包好的dist 文件夹中。但我不想手动做,所以我做了以下。

NodeModulesAccessor

首先,我创建了一个 class,它在 node_modules 文件夹中的文件和打包的 dist 文件夹中的目标之间进行映射。

import { NodeModulesKeys } from './nodeModulesKeys';
import { NodeModulesValue } from './nodeModulesValue';

export class NodeModulesAccessor {
  static readonly outputPath = 'dist';

  private static readonly pathMapping = new Map<NodeModulesKeys, NodeModulesValue>([
    [
      NodeModulesKeys.ffmpegMinJs,
      {
        sourcePath: ['node_modules', '@ffmpeg', 'ffmpeg', 'dist'],
        destinationPath: ['libs', '@ffmpeg', 'ffmpeg', 'dist'],
        fileName: 'ffmpeg.min.js',
      },
    ],
    [
      NodeModulesKeys.ffmpegCoreJs,
      {
        sourcePath: ['node_modules', '@ffmpeg', 'core', 'dist'],
        destinationPath: ['libs', '@ffmpeg', 'core', 'dist'],
        fileName: 'ffmpeg-core.js',
        includeFolder: true,
      },
    ],
    [
      NodeModulesKeys.codiconCss,
      {
        sourcePath: ['node_modules', '@vscode', 'codicons', 'dist'],
        destinationPath: ['libs', '@vscode', 'codicons', 'dist'],
        fileName: 'codicon.css',
        includeFolder: true,
      },
    ],
  ]);

  static getPathToOutputFile(key: NodeModulesKeys): string[] {
    const path = this.getMappedValue(key);
    return [this.outputPath, ...path.destinationPath, path.fileName];
  }

  static getPathToNodeModulesFile(key: NodeModulesKeys): NodeModulesValue {
    return this.getMappedValue(key);
  }

  private static getMappedValue(key: NodeModulesKeys): NodeModulesValue {
    const value = this.pathMapping.get(key);
    if (!value) {
      throw Error(`Path to "${key}" is not mapped.`);
    }
    return value;
  }
}

NodeModulesKeys 是我要使用的所有文件的简单枚举:

export enum NodeModulesKeys {
  ffmpegMinJs,
  ffmpegCoreJs,
  codiconCss,
}

NodeModulesValue是一个接口:

export interface NodeModulesValue {
  sourcePath: string[];
  destinationPath: string[];
  fileName: string;
  includeFolder?: boolean;
}

一些库(例如 codicons)需要文件夹中有多个文件。这就是为什么 NodeModulesValue 有一个可选字段 includeFolder.

webpack.config.ts

神奇的地方就在这里(别担心,没那么复杂)。

您可以在捆绑时使用CopyWebpackPlugin复制文件:

import * as path from 'path';
import { NodeModulesAccessor } from './src/node-modules-accessor/nodeModulesAccessor';
import { NodeModulesKeys } from './src/node-modules-accessor/nodeModulesKeys';
import { Configuration } from 'webpack';
import CopyPlugin = require('copy-webpack-plugin');

const config: Configuration = {
  // omitted, nothing special here
  plugins: [copyNodeModulesFiles()],
};

function copyNodeModulesFiles(): CopyPlugin {
  const files: NodeModulesKeys[] = Object.keys(NodeModulesKeys)
    .filter((key) => !isNaN(Number(key)))
    .map((key) => Number(key));
  const copies: CopyPlugin.ObjectPattern[] = files.map((file) => {
    const value = NodeModulesAccessor.getPathToNodeModulesFile(file);
    let sourcePath;
    let destinationPath;
    if (value.includeFolder) {
      sourcePath = path.join(...value.sourcePath);
      destinationPath = path.join(...value.destinationPath);
    } else {
      sourcePath = path.join(...value.sourcePath, value.fileName);
      destinationPath = path.join(...value.destinationPath, value.fileName);
    }
    return {
      from: sourcePath,
      to: destinationPath,
    };
  });
  return new CopyPlugin({
    patterns: copies,
  });
}

module.exports = config;

这里我们遍历 NodeModulesKeys 枚举的所有值。 () 并为每个添加复制指令。

如果需要,我们也可以复制整个文件夹。

网络视图集成

要获取 Uris 并在网络视图中使用它们 html,我们再次使用 NodeModulesAccessor

const codiconsUri = webview.asWebviewUri(
  Uri.joinPath(this.context.extensionUri, ...NodeModulesAccessor.getPathToOutputFile(NodeModulesKeys.codiconCss))
);

重要

为了让 webview 能够访问 dist/libs 目录,您在创建 webview 时将目录定义为 localResourceRoot:

this.viewPanel = window.createWebviewPanel('sampleId', 'Sample Webview', ViewColumn.Beside, {
  enableScripts: true,
  retainContextWhenHidden: true,
  localResourceRoots: [
    Uri.joinPath(this.context.extensionUri, NodeModulesAccessor.outputPath, 'libs'), // <--- Important
    Uri.joinPath(this.context.extensionUri, 'media'),
  ],
});

我希望这对其他人有用。