我创建了一个使用 Webview 的 VS Code 扩展。在 Webview 中,我有一个指向
node_modules
文件夹中文件的链接,我按照各种来源的建议将其添加到 html 中(例如 vscode-codicons-sample):
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 Code here 中的说明进行操作。
在
.vscodeignore
文件中,我按照指示排除 node_modules
文件夹。 (这就是捆绑的主要原因)
当我现在打包项目时,当然,.vsix 文件中不存在
node_modules
,因此 Uri 无效。
你会如何解决这个问题? (我使用这种方法除了代码图标之外还插入了更多链接)
提前致谢!
我决定采纳@MikeLischke的建议,将所需的文件复制到打包的
dist
文件夹中。但我不想手动执行此操作,所以我执行了以下操作。
首先,我创建了一个类,用于在
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
。
这就是奇迹发生的地方(别担心,没那么复杂)。
您可以使用 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 并在 webview 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'),
],
});
我希望这对其他人有用。