带有 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'),
],
});
我希望这对其他人有用。
我创建了一个使用 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'),
],
});
我希望这对其他人有用。