웹 확장 프로그램
Visual Studio Code는 브라우저에서 편집기로 실행될 수 있습니다. 한 가지 예는 GitHub의 저장소 또는 Pull Request를 탐색할 때 . (마침표 키)를 눌러 접근할 수 있는 github.dev 사용자 인터페이스입니다. VS Code가 웹에서 사용될 때 설치된 확장 프로그램은 브라우저에서 '웹 확장 호스트'라고 불리는 확장 호스트에서 실행됩니다. 웹 확장 호스트에서 실행될 수 있는 확장은 '웹 확장'이라고 합니다.
웹 확장은 일반 확장과 동일한 구조를 공유하지만, 다른 런타임으로 인해 Node.js 런타임용으로 작성된 확장과 동일한 코드로 실행되지 않습니다. 웹 확장은 여전히 전체 VS Code API에 액세스할 수 있지만, 더 이상 Node.js API 및 모듈 로드에는 액세스할 수 없습니다. 대신 웹 확장은 브라우저 샌드박스에 의해 제한되므로 일반 확장에 비해 제한 사항이 있습니다.
웹 확장 런타임은 VS Code 데스크톱에서도 지원됩니다. 확장을 웹 확장으로 만들기로 결정하면 웹용 VS Code (vscode.dev 및 github.dev 포함), 데스크톱 및 GitHub Codespaces와 같은 서비스에서 지원됩니다.
웹 확장 구조
웹 확장은 일반 확장과 같은 구조입니다. 확장 매니페스트(package.json)는 확장 소스 코드의 진입점을 정의하고 확장 기여를 선언합니다.
웹 확장의 경우, 주요 진입점 파일은 일반 확장에서 main 속성이 아닌 browser 속성에 의해 정의됩니다.
contributes 속성은 웹 확장과 일반 확장 모두에 대해 동일하게 작동합니다.
아래 예시는 웹 확장 호스트에서만 실행되는 간단한 hello world 확장의 package.json을 보여줍니다 (browser 진입점만 포함).
{
"name": "helloworld-web-sample",
"displayName": "helloworld-web-sample",
"description": "HelloWorld example for VS Code in the browser",
"version": "0.0.1",
"publisher": "vscode-samples",
"repository": "https://github.com/microsoft/vscode-extension-samples/helloworld-web-sample",
"engines": {
"vscode": "^1.74.0"
},
"categories": ["Other"],
"activationEvents": [],
"browser": "./dist/web/extension.js",
"contributes": {
"commands": [
{
"command": "helloworld-web-sample.helloWorld",
"title": "Hello World"
}
]
},
"scripts": {
"vscode:prepublish": "npm run package-web",
"compile-web": "webpack",
"watch-web": "webpack --watch",
"package-web": "webpack --mode production --devtool hidden-source-map"
},
"devDependencies": {
"@types/vscode": "^1.59.0",
"ts-loader": "^9.2.2",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.0",
"@types/webpack-env": "^1.16.0",
"process": "^0.11.10"
}
}
참고: 확장이 1.74 이전의 VS Code 버전을 대상으로 하는 경우,
activationEvents에onCommand:helloworld-web-sample.helloWorld를 명시적으로 나열해야 합니다.
main 진입점만 있고 browser가 없는 확장은 웹 확장이 아닙니다. 웹 확장 호스트에서 무시되며 확장 보기에서 다운로드할 수 없습니다.

선언적 기여만 있는 확장(contributes만 있고 main 또는 browser가 없음)은 웹 확장일 수 있습니다. 확장 작성자가 수정하지 않고도 웹용 VS Code에 설치하여 실행할 수 있습니다. 선언적 기여가 있는 확장의 예로는 테마, 문법, 스니펫이 있습니다.
확장은 브라우저 및 Node.js 런타임 모두에서 실행되도록 browser 및 main 진입점을 모두 가질 수 있습니다. 기존 확장을 웹 확장으로 업데이트 섹션에서는 확장을 두 런타임 모두에서 작동하도록 마이그레이션하는 방법을 보여줍니다.
웹 확장 활성화 섹션에서는 웹 확장 호스트에서 확장을 로드할 수 있는지 여부를 결정하는 데 사용되는 규칙을 나열합니다.
웹 확장 메인 파일
웹 확장의 메인 파일은 browser 속성에 의해 정의됩니다. 이 스크립트는 웹 확장 호스트의 브라우저 WebWorker 환경에서 실행됩니다. 브라우저 워커 샌드박스에 의해 제한되며 Node.js 런타임에서 실행되는 일반 확장과 비교할 때 제한 사항이 있습니다.
- 다른 모듈을 가져오거나 요구하는 것은 지원되지 않습니다.
importScripts도 사용할 수 없습니다. 결과적으로 코드는 단일 파일로 패키징되어야 합니다. - VS Code API는
require('vscode')패턴을 통해 로드할 수 있습니다.require에 대한 shim이 있으므로 작동하지만, 이 shim은 추가 확장 파일이나 추가 노드 모듈을 로드하는 데 사용할 수 없습니다.require('vscode')만 작동합니다. process,os,setImmediate,path,util,url과 같은 Node.js 전역 변수 및 라이브러리는 런타임에 사용할 수 없습니다. 그러나 webpack과 같은 도구를 사용하여 추가할 수 있습니다. webpack 구성 섹션에서는 이 방법을 설명합니다.- 열린 작업 영역 또는 폴더는 가상 파일 시스템에 있습니다. 작업 영역 파일에 대한 액세스는
vscode.workspace.fs에서 액세스할 수 있는 VS Code 파일 시스템 API를 통해 이루어져야 합니다. - 확장 컨텍스트 위치(
ExtensionContext.extensionUri) 및 저장 위치(ExtensionContext.storageUri,globalStorageUri)도 가상 파일 시스템에 있으며vscode.workspace.fs를 통해 액세스해야 합니다. - 웹 리소스에 액세스하려면 Fetch API를 사용해야 합니다. 액세스하는 리소스는 CORS(Cross-Origin Resource Sharing)를 지원해야 합니다.
- 자식 프로세스를 생성하거나 실행 파일을 실행하는 것은 불가능합니다. 그러나 Worker API를 통해 웹 워커를 생성할 수 있습니다. 이는 웹 확장에서의 언어 서버 프로토콜 섹션에서 설명하는 것처럼 언어 서버를 실행하는 데 사용됩니다.
- 일반 확장과 마찬가지로, 확장의
activate/deactivate함수는exports.activate = ...패턴을 통해 내보내야 합니다.
웹 확장 개발
다행히 TypeScript 및 webpack과 같은 도구는 브라우저 런타임 제약 사항의 상당 부분을 숨길 수 있어 일반 확장과 동일한 방식으로 웹 확장을 작성할 수 있습니다. 웹 확장과 일반 확장은 종종 동일한 소스 코드에서 생성될 수 있습니다.
예를 들어, yo code 생성기로 생성된 Hello Web Extension은 빌드 스크립트만 다릅니다. 디버그: 디버깅 선택 및 시작 명령을 사용하여 액세스할 수 있는 제공된 실행 구성을 사용하여 생성된 확장을 기존 Node.js 확장과 동일하게 실행하고 디버그할 수 있습니다.
웹 확장 만들기
새 웹 확장을 스캐폴딩하려면 yo code를 사용하고 New Web Extension을 선택하세요. 최신 버전의 generator-code (>= generator-code@1.6)이 설치되어 있는지 확인하세요. 생성기와 yo를 업데이트하려면 npm i -g yo generator-code를 실행하세요.
생성된 확장은 확장 소스 코드(hello world 알림을 표시하는 명령), package.json 매니페스트 파일, webpack 또는 esbuild 구성 파일로 구성됩니다.
간단하게 유지하기 위해 webpack을 번들러로 사용한다고 가정합니다. 이 문서의 끝에서 esbuild를 선택했을 때 달라지는 점을 설명합니다.
src/web/extension.ts는 확장의 진입점 소스 코드 파일입니다. 일반 hello 확장과 동일합니다.package.json은 확장 매니페스트입니다.browser속성을 사용하여 진입점을 가리킵니다.- 컴파일, 감시, 패키징을 위한
compile-web,watch-web,package-web스크립트를 제공합니다.
webpack.config.js는 확장 소스를 단일 파일로 컴파일하고 번들링하는 webpack 구성 파일입니다..vscode/launch.json에는 웹 확장 및 테스트를 웹 확장 호스트에서 VS Code 데스크톱에서 실행하는 실행 구성이 포함되어 있습니다 (extensions.webWorker설정은 더 이상 필요하지 않습니다)..vscode/task.json에는 실행 구성에서 사용하는 빌드 작업이 포함되어 있습니다. webpack 특정ts-webpack-watch문제 해결사를 사용하는npm run watch-web을 호출합니다..vscode/extensions.json에는 문제 해결사를 제공하는 확장이 포함되어 있습니다. 실행 구성이 작동하려면 이러한 확장을 설치해야 합니다.tsconfig.json은webworker런타임에 맞는 컴파일 옵션을 정의합니다.
helloworld-web-sample의 소스 코드는 생성기가 만드는 것과 유사합니다.
Webpack 구성
webpack 구성 파일은 yo code에 의해 자동으로 생성됩니다. 확장 소스 코드를 단일 JavaScript 파일로 번들링하여 웹 확장 호스트에서 로드합니다.
나중에 esbuild를 번들러로 사용하는 방법을 설명하겠지만, 지금은 webpack으로 시작하겠습니다.
const path = require('path');
const webpack = require('webpack');
/** @typedef {import('webpack').Configuration} WebpackConfig **/
/** @type WebpackConfig */
const webExtensionConfig = {
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
target: 'webworker', // extensions run in a webworker context
entry: {
extension: './src/web/extension.ts', // source of the web extension main file
'test/suite/index': './src/web/test/suite/index.ts' // source of the web extension test runner
},
output: {
filename: '[name].js',
path: path.join(__dirname, './dist/web'),
libraryTarget: 'commonjs',
devtoolModuleFilenameTemplate: '../../[resource-path]'
},
resolve: {
mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules
extensions: ['.ts', '.js'], // support ts-files and js-files
alias: {
// provides alternate implementation for node module and source files
},
fallback: {
// Webpack 5 no longer polyfills Node.js core modules automatically.
// see https://webpack.js.org/configuration/resolve/#resolvefallback
// for the list of Node.js core module polyfills.
assert: require.resolve('assert')
}
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser' // provide a shim for the global `process` variable
})
],
externals: {
vscode: 'commonjs vscode' // ignored because it doesn't exist
},
performance: {
hints: false
},
devtool: 'nosources-source-map' // create a source map that points to the original source file
};
module.exports = [webExtensionConfig];
webpack.config.js의 중요한 필드는 다음과 같습니다.
entry필드에는 확장 및 테스트 스위트의 주요 진입점이 포함됩니다.- 이 경로를 조정하여 확장의 진입점을 올바르게 가리켜야 할 수 있습니다.
- 기존 확장의 경우,
package.json의main에 현재 사용 중인 파일로 이 경로를 가리키는 것으로 시작할 수 있습니다. - 테스트를 패키징하지 않으려면 테스트 스위트 필드를 생략할 수 있습니다.
output필드는 컴파일된 파일이 위치할 위치를 나타냅니다.[name]은entry에서 사용된 키로 대체됩니다. 따라서 생성된 구성 파일에서는dist/web/extension.js및dist/web/test/suite/index.js가 생성됩니다.
target필드는 컴파일된 JavaScript 파일이 실행될 환경 유형을 나타냅니다. 웹 확장인 경우webworker로 설정해야 합니다.resolve필드에는 브라우저에서 작동하지 않는 노드 라이브러리에 대한 별칭 및 폴백을 추가하는 기능이 포함됩니다.path와 같은 라이브러리를 사용하는 경우 웹 컴파일 컨텍스트에서path를 확인하는 방법을 지정할 수 있습니다. 예를 들어,path: path.resolve(__dirname, 'src/my-path-implementation-for-web.js')와 같이path를 정의하는 프로젝트의 파일로 가리킬 수 있습니다. 또는path-browserify라고 하는 Browserify 노드 패키지 버전을 사용하여path: require.resolve('path-browserify')로 지정할 수 있습니다.- Node.js 코어 모듈 폴리필 목록은 webpack resolve.fallback을 참조하세요.
plugins섹션은 DefinePlugin 플러그인을 사용하여processNode.js 전역 변수와 같은 전역 변수를 폴리필합니다.
웹 확장 테스트
마켓플레이스에 게시하기 전에 웹 확장을 테스트하는 현재 세 가지 방법이 있습니다.
--extensionDevelopmentKind=web옵션으로 실행되는 데스크톱용 VS Code를 사용하여 VS Code에서 실행되는 웹 확장 호스트에서 웹 확장을 실행하세요.@vscode/test-web노드 모듈을 사용하여 로컬 서버에서 제공되는 VS Code for the Web 및 확장이 포함된 브라우저를 여세요.- sideload를 통해 확장을 vscode.dev에 설치하여 실제 환경에서 확장을 확인하세요.
데스크톱용 VS Code에서 웹 확장 테스트
기존 VS Code 확장 개발 환경을 사용하기 위해 데스크톱용 VS Code는 일반 Node.js 확장 호스트와 함께 웹 확장 호스트를 실행하는 것을 지원합니다.
새 웹 확장 생성기에서 제공하는 pwa-extensionhost 실행 구성을 사용하세요.
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Web Extension in VS Code",
"type": "pwa-extensionHost",
"debugWebWorkerHost": true,
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentKind=web"
],
"outFiles": ["${workspaceFolder}/dist/web/**/*.js"],
"preLaunchTask": "npm: watch-web"
}
]
}
tasks.json에 예상되는 작업 npm: watch-web을 호출하여 확장을 컴파일하는 npm: watch-web 작업을 사용합니다.
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch-web",
"group": "build",
"isBackground": true,
"problemMatcher": ["$ts-webpack-watch"]
}
]
}
$ts-webpack-watch는 webpack 도구의 출력을 구문 분석할 수 있는 문제 해결사입니다. TypeScript + Webpack Problem Matchers 확장에서 제공합니다.
실행되는 **확장 개발 호스트** 인스턴스에서 웹 확장은 웹 확장 호스트에서 사용할 수 있으며 실행됩니다. Hello World 명령을 실행하여 확장을 활성화하세요.
실행 중인 확장 보기(명령: 개발자: 실행 중인 확장 보기)를 열어 웹 확장 호스트에서 실행 중인 확장을 확인하세요.
브라우저에서 @vscode/test-web을 사용하여 웹 확장 테스트
@vscode/test-web 노드 모듈은 브라우저에서 웹 확장을 테스트하기 위한 CLI 및 API를 제공합니다.
이 노드 모듈은 vscode-test-web이라는 npm 바이너리를 제공하여 명령줄에서 VS Code for the Web을 열 수 있습니다.
.vscode-test-web에 VS Code의 웹 부분을 다운로드합니다.localhost:3000에서 로컬 서버를 시작합니다.- 브라우저(Chromium, Firefox 또는 Webkit)를 엽니다.
명령줄에서 실행할 수 있습니다.
npx @vscode/test-web --extensionDevelopmentPath=$extensionFolderPath $testDataPath
또는 @vscode/test-web을 확장의 개발 종속성으로 추가하고 스크립트에서 호출하는 것이 좋습니다.
"devDependencies": {
"@vscode/test-web": "*"
},
"scripts": {
"open-in-browser": "vscode-test-web --extensionDevelopmentPath=. ."
}
@vscode/test-web README에서 더 많은 CLI 옵션을 확인할 수 있습니다.
| 옵션 | 인수 설명 |
|---|---|
| --browserType | 시작할 브라우저: chromium (기본값), firefox 또는 webkit |
| --extensionDevelopmentPath | 개발 중인 확장을 포함하도록 가리키는 경로. |
| --extensionTestsPath | 실행할 테스트 모듈의 경로. |
| --permission | 열린 브라우저에 부여된 권한: 예: clipboard-read, clipboard-write.전체 옵션 목록은 여기를 참조하세요. 인수는 여러 번 제공될 수 있습니다. |
| --folder-uri | VS Code를 열 워크스페이스의 URI. folderPath가 제공된 경우 무시됩니다. |
| --extensionPath | 추가 확장을 포함하는 폴더를 가리키는 경로. 인수는 여러 번 제공될 수 있습니다. |
| folderPath | VS Code를 열 로컬 폴더. 폴더 내용은 가상 파일 시스템으로 사용 가능하며 작업 공간으로 열립니다. |
VS Code의 웹 부분은 .vscode-test-web 폴더에 다운로드됩니다. 이 폴더를 .gitignore 파일에 추가해야 합니다.
vscode.dev에서 웹 확장 테스트
확장을 마켓플레이스에 게시하여 모든 사람이 웹용 VS Code에서 사용할 수 있도록 하기 전에, 실제 vscode.dev 환경에서 확장의 동작을 확인할 수 있습니다.
vscode.dev에서 확장을 보려면 먼저 vscode.dev가 다운로드하여 실행할 수 있도록 로컬 컴퓨터에서 호스팅해야 합니다.
먼저 mkcert를 설치해야 합니다.
그런 다음, localhost.pem 및 localhost-key.pem 파일을 잃어버리지 않을 위치(예: $HOME/certs)에 생성합니다.
$ mkdir -p $HOME/certs
$ cd $HOME/certs
$ mkcert -install
$ mkcert localhost
그런 다음, 확장 경로에서 npx serve를 실행하여 HTTP 서버를 시작합니다.
$ npx serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem
npx: installed 78 in 2.196s
┌────────────────────────────────────────────────────┐
│ │
│ Serving! │
│ │
│ - Local: https://:5000 │
│ - On Your Network: https://172.19.255.26:5000 │
│ │
│ Copied local address to clipboard! │
│ │
└────────────────────────────────────────────────────┘
마지막으로 vscode.dev를 열고 명령 팔레트(⇧⌘P (Windows, Linux Ctrl+Shift+P))에서 개발자: 위치에서 확장 설치...을 실행하고, 위의 URL(예: https://:5000)을 붙여넣은 다음 설치를 선택합니다.
로그 확인
브라우저의 개발자 도구 콘솔에서 로그를 확인하여 확장 프로그램의 오류, 상태 및 로그를 볼 수 있습니다.
vscode.dev 자체의 다른 로그를 볼 수도 있습니다. 또한, 중단점을 쉽게 설정하거나 확장의 소스 코드를 볼 수 없습니다. 이러한 제한 사항으로 인해 vscode.dev에서의 디버깅 경험은 그리 좋지 않으므로, vscode.dev에 sideload하기 전에 테스트를 위해 앞의 두 가지 옵션을 사용하는 것이 좋습니다. sideload는 확장을 게시하기 전 최종 확인 단계로 좋습니다.
웹 확장 테스트
웹 확장 테스트가 지원되며 일반 확장 테스트와 유사하게 구현할 수 있습니다. 확장 테스트의 기본 구조를 배우려면 확장 테스트 문서를 참조하세요.
@vscode/test-web 노드 모듈은 @vscode/test-electron (이전 이름 vscode-test)에 해당합니다. 이를 통해 명령줄에서 Chromium, Firefox 및 Safari에서 확장 테스트를 실행할 수 있습니다.
이 유틸리티는 다음 단계를 수행합니다.
- 로컬 웹 서버에서 Web용 VS Code를 시작합니다.
- 지정된 브라우저를 엽니다.
- 제공된 테스트 실행기 스크립트를 실행합니다.
연속 빌드에서 테스트를 실행하여 확장이 모든 브라우저에서 작동하는지 확인할 수 있습니다.
테스트 실행기 스크립트는 웹 확장 메인 파일과 동일한 제한 사항을 가진 웹 확장 호스트에서 실행됩니다.
- 모든 파일은 단일 파일로 번들링됩니다. 여기에는 테스트 실행기(예: Mocha)와 모든 테스트(일반적으로
*.test.ts)가 포함되어야 합니다. require('vscode')만 지원됩니다.
yo code 웹 확장 생성기에서 생성된 webpack 구성은 테스트 섹션을 포함합니다. ./src/web/test/suite/index.ts에서 테스트 실행기 스크립트를 예상합니다. 제공된 테스트 실행기 스크립트는 Mocha의 웹 버전을 사용하며 모든 테스트 파일을 가져오는 webpack 특정 구문을 포함합니다.
require('mocha/mocha'); // import the mocha web build
export function run(): Promise<void> {
return new Promise((c, e) => {
mocha.setup({
ui: 'tdd',
reporter: undefined
});
// bundles all files in the current directory matching `*.test`
const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r);
importAll(require.context('.', true, /\.test$/));
try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
}
명령줄에서 웹 테스트를 실행하려면 package.json에 다음을 추가하고 npm test로 실행하세요.
"devDependencies": {
"@vscode/test-web": "*"
},
"scripts": {
"test": "vscode-test-web --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/index.js"
}
테스트 데이터가 있는 폴더를 열려면 마지막 매개변수로 로컬 폴더 경로(folderPath)를 전달하세요.
VS Code(Insiders) 데스크톱에서 확장 테스트를 실행(및 디버그)하려면 Extension Tests in VS Code 실행 구성을 사용하세요.
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension Tests in VS Code",
"type": "extensionHost",
"debugWebWorkerHost": true,
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentKind=web",
"--extensionTestsPath=${workspaceFolder}/dist/web/test/suite/index"
],
"outFiles": ["${workspaceFolder}/dist/web/**/*.js"],
"preLaunchTask": "npm: watch-web"
}
]
}
웹 확장 게시
웹 확장은 다른 확장과 함께 Marketplace에 호스팅됩니다.
확장을 게시할 때 최신 버전의 vsce를 사용하는지 확인하세요. vsce는 웹 확장인 모든 확장을 태그를 지정합니다. 이를 위해 vsce는 웹 확장 활성화 섹션에 나열된 규칙을 사용합니다.
기존 확장을 웹 확장으로 업데이트
코드 없는 확장
코드가 없고 기여 포인트만 있는 확장(예: 테마, 스니펫, 기본 언어 확장)은 수정이 필요하지 않습니다. 웹 확장 호스트에서 실행될 수 있으며 확장 보기에서 설치할 수 있습니다.
재게시할 필요는 없지만, 확장의 새 버전을 게시할 때는 최신 버전의 vsce를 사용해야 합니다.
코드 있는 확장 마이그레이션
소스 코드가 있는 확장(main 속성으로 정의됨)은 웹 확장 메인 파일을 제공하고 package.json에 browser 속성을 설정해야 합니다.
이 단계를 사용하여 브라우저 환경을 위해 확장을 다시 컴파일하세요.
- webpack 구성 섹션에 표시된 대로 webpack 구성 파일을 추가하세요. Node.js 확장 코드에 대한 webpack 파일이 이미 있는 경우 웹용 새 섹션을 추가할 수 있습니다. 예시로 vscode-css-formatter를 확인하세요.
launch.json및tasks.json파일을 웹 확장 테스트 섹션에 표시된 대로 추가하세요.- webpack 구성 파일에서 입력 파일을 기존 Node.js 메인 파일로 설정하거나 웹 확장을 위한 새 메인 파일을 만드세요.
package.json에 웹 확장 구조 섹션에 표시된 대로browser및scripts속성을 추가하세요.npm run compile-web을 실행하여 webpack을 호출하고 확장이 웹에서 실행되도록 만드는 데 필요한 부분을 확인하세요.
가능한 한 많은 소스 코드를 재사용할 수 있도록 다음은 몇 가지 기법입니다.
path와 같은 Node.js 코어 모듈을 폴리필하려면 resolve.fallback에 항목을 추가하세요.process와 같은 Node.js 전역 변수를 제공하려면 DefinePlugin 플러그인을 사용하세요.- 브라우저와 노드 런타임 모두에서 작동하는 노드 모듈을 사용하세요. 노드 모듈은
browser및main진입점을 모두 정의하여 이를 수행할 수 있습니다. webpack은 자동으로 대상에 맞는 것을 사용합니다. 이렇게 하는 노드 모듈의 예로는 request-light 및 @vscode/l10n이 있습니다. - 노드 모듈 또는 소스 파일에 대한 대체 구현을 제공하려면 resolve.alias를 사용하세요.
- 코드를 브라우저 부분, Node.js 부분, 공통 부분으로 분리하세요. 공통 부분에서는 브라우저와 Node.js 런타임 모두에서 작동하는 코드만 사용하세요. Node.js와 브라우저에서 구현이 다른 기능에 대한 추상화를 만드세요.
path,URI.file,context.extensionPath,rootPath,uri.fsPath의 사용을 주의하세요. 이들은 웹용 VS Code에서 사용되는 가상 워크스페이스(파일 시스템이 아닌)에서는 작동하지 않습니다. 대신URI.parse,context.extensionUri를 사용하여 URI를 사용하세요.vscode-uri노드 모듈은joinPath,dirName,baseName,extName,resolvePath를 제공합니다.fs의 사용을 주의하세요. vscodeworkspace.fs를 사용하여 대체하세요.
확장이 웹에서 실행될 때 기능이 적은 것은 괜찮습니다. When 절 컨텍스트를 사용하여 웹에서 가상 워크스페이스에서 실행될 때 사용 가능한 또는 숨겨진 명령, 보기 및 작업을 제어하세요.
virtualWorkspace컨텍스트 변수를 사용하여 현재 작업 공간이 파일 시스템이 아닌 작업 공간인지 확인하세요.resourceScheme을 사용하여 현재 리소스가file리소스인지 확인하세요.- 플랫폼 셸이 있는 경우
shellExecutionSupported를 사용하세요. - 명령이 적용되지 않는 이유를 설명하는 대화 상자를 표시하는 대체 명령 처리기를 구현하세요.
WebWorker는 프로세스 포크의 대안으로 사용할 수 있습니다. JSON, CSS 및 HTML 언어 서버와 같은 빌트인 JSON, CSS 및 HTML 언어 서버를 포함하여 여러 언어 서버를 웹 확장으로 실행하도록 업데이트했습니다. 아래 언어 서버 프로토콜 섹션에서 자세한 내용을 확인할 수 있습니다.
브라우저 런타임 환경은 JavaScript 및 WebAssembly의 실행만 지원합니다. 다른 프로그래밍 언어로 작성된 라이브러리는 크로스 컴파일되어야 합니다. 예를 들어 C/C++ 및 Rust를 WebAssembly로 컴파일하는 도구가 있습니다. 예를 들어, vscode-anycode 확장은 WebAssembly로 컴파일된 C/C++ 코드인 tree-sitter를 사용합니다.
웹 확장에서의 언어 서버 프로토콜
vscode-languageserver-node는 언어 서버 프로토콜 (LSP)의 구현으로, JSON, CSS, HTML과 같은 언어 서버 구현의 기반으로 사용됩니다.
3.16.0부터 클라이언트와 서버는 브라우저 구현도 제공합니다. 서버는 웹 워커에서 실행될 수 있으며, 연결은 웹 워커의 postMessage 프로토콜을 기반으로 합니다.
브라우저용 클라이언트는 'vscode-languageclient/browser'에서 찾을 수 있습니다.
import { LanguageClient } from `vscode-languageclient/browser`;
서버는 vscode-languageserver/browser에 있습니다.
lsp-web-extension-sample은 이 작동 방식을 보여줍니다.
웹 확장 활성화
VS Code는 다음과 같은 경우 확장을 웹 확장으로 자동 처리합니다.
- 확장 매니페스트(
package.json)에browser진입점이 있는 경우. - 확장 매니페스트에
main진입점이 없고 다음 기여 포인트가 없는 경우:localizations,debuggers,terminal,typescriptServerPlugins.
확장이 웹 확장 호스트에서도 작동하는 디버거 또는 터미널을 제공하려면 browser 진입점을 정의해야 합니다.
ESBuild 사용
webpack 대신 esbuild를 사용하려면 다음을 수행하세요.
esbuild.js 빌드 스크립트를 추가하세요.
const esbuild = require('esbuild');
const glob = require('glob');
const path = require('path');
const polyfill = require('@esbuild-plugins/node-globals-polyfill');
const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');
async function main() {
const ctx = await esbuild.context({
entryPoints: ['src/web/extension.ts', 'src/web/test/suite/extensionTests.ts'],
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: 'browser',
outdir: 'dist/web',
external: ['vscode'],
logLevel: 'warning',
// Node.js global to browser globalThis
define: {
global: 'globalThis'
},
plugins: [
polyfill.NodeGlobalsPolyfillPlugin({
process: true,
buffer: true
}),
testBundlePlugin,
esbuildProblemMatcherPlugin /* add to the end of plugins array */
]
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
}
/**
* For web extension, all tests, including the test runner, need to be bundled into
* a single module that has a exported `run` function .
* This plugin bundles implements a virtual file extensionTests.ts that bundles all these together.
* @type {import('esbuild').Plugin}
*/
const testBundlePlugin = {
name: 'testBundlePlugin',
setup(build) {
build.onResolve({ filter: /[\/\\]extensionTests\.ts$/ }, args => {
if (args.kind === 'entry-point') {
return { path: path.resolve(args.path) };
}
});
build.onLoad({ filter: /[\/\\]extensionTests\.ts$/ }, async args => {
const testsRoot = path.join(__dirname, 'src/web/test/suite');
const files = await glob.glob('*.test.{ts,tsx}', { cwd: testsRoot, posix: true });
return {
contents:
`export { run } from './mochaTestRunner.ts';` +
files.map(f => `import('./${f}');`).join(''),
watchDirs: files.map(f => path.dirname(path.resolve(testsRoot, f))),
watchFiles: files.map(f => path.resolve(testsRoot, f))
};
});
}
};
/**
* This plugin hooks into the build process to print errors in a format that the problem matcher in
* Visual Studio Code can understand.
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
name: 'esbuild-problem-matcher',
setup(build) {
build.onStart(() => {
console.log('[watch] build started');
});
build.onEnd(result => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
if (location == null) return;
console.error(` ${location.file}:${location.line}:${location.column}:`);
});
console.log('[watch] build finished');
});
}
};
main().catch(e => {
console.error(e);
process.exit(1);
});
빌드 스크립트는 다음을 수행합니다.
- esbuild로 빌드 컨텍스트를 생성합니다. 컨텍스트는 다음과 같이 구성됩니다.
src/web/extension.ts의 코드를 단일 파일dist/web/extension.js로 번들링합니다.- 모든 테스트(테스트 실행기(mocha) 포함)를 단일 파일
dist/web/test/suite/extensionTests.js로 번들링합니다. --production플래그가 전달된 경우 코드를 최소화합니다.--production플래그가 전달되지 않은 경우 소스 맵을 생성합니다.- VS Code 런타임에서 제공되므로 'vscode' 모듈을 번들에서 제외합니다.
process및buffer에 대한 폴리필을 생성합니다.- esbuildProblemMatcherPlugin 플러그인을 사용하여 번들러가 완료되지 못하게 한 오류를 보고합니다. 이 플러그인은 확장으로도 설치해야 하는
esbuild문제 해결사가 감지하는 형식으로 오류를 내보냅니다. - testBundlePlugin을 사용하여 모든 테스트 파일과 mocha 테스트 실행기
mochaTestRunner.js를 참조하는 테스트 메인 파일(extensionTests.js)을 구현합니다.
--watch플래그가 전달된 경우 소스 파일의 변경 사항을 감시하고 변경 사항이 감지될 때마다 번들을 다시 빌드합니다.
esbuild는 TypeScript 파일과 직접 작업할 수 있습니다. 그러나 esbuild는 타입 검사를 수행하지 않고 모든 타입 선언을 제거하기만 합니다. 구문 오류만 보고되며 esbuild가 실패하는 원인이 될 수 있습니다.
따라서 TypeScript 컴파일러(tsc)를 별도로 실행하지만 코드를 내보내지는 않습니다 (--noEmit 플래그).
package.json의 scripts 섹션은 이제 다음과 같이 보입니다.
"scripts": {
"vscode:prepublish": "npm run package-web",
"compile-web": "npm run check-types && node esbuild.js",
"watch-web": "npm-run-all -p watch-web:*",
"watch-web:esbuild": "node esbuild.js --watch",
"watch-web:tsc": "tsc --noEmit --watch --project tsconfig.json",
"package-web": "npm run check-types && node esbuild.js --production",
"check-types": "tsc --noEmit",
"pretest": "npm run compile-web",
"test": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/extensionTests.js",
"run-in-browser": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. ."
}
npm-run-all은 지정된 접두사와 일치하는 스크립트 이름을 병렬로 실행하는 노드 모듈입니다. 우리에게는 watch-web:esbuild 및 watch-web:tsc 스크립트를 실행합니다. package.json의 devDependencies 섹션에 npm-run-all을 추가해야 합니다.
다음 tasks.json 파일은 각 watch 작업에 대한 별도의 터미널을 제공합니다.
{
"version": "2.0.0",
"tasks": [
{
"label": "watch-web",
"dependsOn": ["npm: watch-web:tsc", "npm: watch-web:esbuild"],
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
},
"runOptions": {
"runOn": "folderOpen"
}
},
{
"type": "npm",
"script": "watch-web:esbuild",
"group": "build",
"problemMatcher": "$esbuild-watch",
"isBackground": true,
"label": "npm: watch-web:esbuild",
"presentation": {
"group": "watch",
"reveal": "never"
}
},
{
"type": "npm",
"script": "watch-web:tsc",
"group": "build",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"label": "npm: watch-web:tsc",
"presentation": {
"group": "watch",
"reveal": "never"
}
},
{
"label": "compile",
"type": "npm",
"script": "compile-web",
"problemMatcher": ["$tsc", "$esbuild"]
}
]
}
이것은 esbuild 빌드 스크립트에서 참조되는 mochaTestRunner.js입니다.
// Imports mocha for the browser, defining the `mocha` global.
import 'mocha/mocha';
mocha.setup({
ui: 'tdd',
reporter: undefined
});
export function run(): Promise<void> {
return new Promise((c, e) => {
try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
}