이 출시되었습니다! 11월의 새로운 기능 및 수정 사항을 읽어보세요.

웹 확장 프로그램

Visual Studio Code는 브라우저에서 편집기로 실행될 수 있습니다. 한 가지 예는 GitHub의 저장소 또는 Pull Request를 탐색할 때 . (마침표 키)를 눌러 접근할 수 있는 github.dev 사용자 인터페이스입니다. VS Code가 웹에서 사용될 때 설치된 확장 프로그램은 브라우저에서 '웹 확장 호스트'라고 불리는 확장 호스트에서 실행됩니다. 웹 확장 호스트에서 실행될 수 있는 확장은 '웹 확장'이라고 합니다.

웹 확장은 일반 확장과 동일한 구조를 공유하지만, 다른 런타임으로 인해 Node.js 런타임용으로 작성된 확장과 동일한 코드로 실행되지 않습니다. 웹 확장은 여전히 전체 VS Code API에 액세스할 수 있지만, 더 이상 Node.js API 및 모듈 로드에는 액세스할 수 없습니다. 대신 웹 확장은 브라우저 샌드박스에 의해 제한되므로 일반 확장에 비해 제한 사항이 있습니다.

웹 확장 런타임은 VS Code 데스크톱에서도 지원됩니다. 확장을 웹 확장으로 만들기로 결정하면 웹용 VS Code (vscode.devgithub.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 버전을 대상으로 하는 경우, activationEventsonCommand:helloworld-web-sample.helloWorld를 명시적으로 나열해야 합니다.

main 진입점만 있고 browser가 없는 확장은 웹 확장이 아닙니다. 웹 확장 호스트에서 무시되며 확장 보기에서 다운로드할 수 없습니다.

Extensions view

선언적 기여만 있는 확장(contributes만 있고 main 또는 browser가 없음)은 웹 확장일 수 있습니다. 확장 작성자가 수정하지 않고도 웹용 VS Code에 설치하여 실행할 수 있습니다. 선언적 기여가 있는 확장의 예로는 테마, 문법, 스니펫이 있습니다.

확장은 브라우저 및 Node.js 런타임 모두에서 실행되도록 browsermain 진입점을 모두 가질 수 있습니다. 기존 확장을 웹 확장으로 업데이트 섹션에서는 확장을 두 런타임 모두에서 작동하도록 마이그레이션하는 방법을 보여줍니다.

웹 확장 활성화 섹션에서는 웹 확장 호스트에서 확장을 로드할 수 있는지 여부를 결정하는 데 사용되는 규칙을 나열합니다.

웹 확장 메인 파일

웹 확장의 메인 파일은 browser 속성에 의해 정의됩니다. 이 스크립트는 웹 확장 호스트의 브라우저 WebWorker 환경에서 실행됩니다. 브라우저 워커 샌드박스에 의해 제한되며 Node.js 런타임에서 실행되는 일반 확장과 비교할 때 제한 사항이 있습니다.

  • 다른 모듈을 가져오거나 요구하는 것은 지원되지 않습니다. importScripts도 사용할 수 없습니다. 결과적으로 코드는 단일 파일로 패키징되어야 합니다.
  • VS Code APIrequire('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.jsonwebworker 런타임에 맞는 컴파일 옵션을 정의합니다.

helloworld-web-sample의 소스 코드는 생성기가 만드는 것과 유사합니다.

Webpack 구성

webpack 구성 파일은 yo code에 의해 자동으로 생성됩니다. 확장 소스 코드를 단일 JavaScript 파일로 번들링하여 웹 확장 호스트에서 로드합니다.

나중에 esbuild를 번들러로 사용하는 방법을 설명하겠지만, 지금은 webpack으로 시작하겠습니다.

webpack.config.js

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.jsonmain에 현재 사용 중인 파일로 이 경로를 가리키는 것으로 시작할 수 있습니다.
    • 테스트를 패키징하지 않으려면 테스트 스위트 필드를 생략할 수 있습니다.
  • output 필드는 컴파일된 파일이 위치할 위치를 나타냅니다.
    • [name]entry에서 사용된 키로 대체됩니다. 따라서 생성된 구성 파일에서는 dist/web/extension.jsdist/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 플러그인을 사용하여 process Node.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.pemlocalhost-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에서 확장 테스트를 실행할 수 있습니다.

이 유틸리티는 다음 단계를 수행합니다.

  1. 로컬 웹 서버에서 Web용 VS Code를 시작합니다.
  2. 지정된 브라우저를 엽니다.
  3. 제공된 테스트 실행기 스크립트를 실행합니다.

연속 빌드에서 테스트를 실행하여 확장이 모든 브라우저에서 작동하는지 확인할 수 있습니다.

테스트 실행기 스크립트는 웹 확장 메인 파일과 동일한 제한 사항을 가진 웹 확장 호스트에서 실행됩니다.

  • 모든 파일은 단일 파일로 번들링됩니다. 여기에는 테스트 실행기(예: 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.jsonbrowser 속성을 설정해야 합니다.

이 단계를 사용하여 브라우저 환경을 위해 확장을 다시 컴파일하세요.

  • webpack 구성 섹션에 표시된 대로 webpack 구성 파일을 추가하세요. Node.js 확장 코드에 대한 webpack 파일이 이미 있는 경우 웹용 새 섹션을 추가할 수 있습니다. 예시로 vscode-css-formatter를 확인하세요.
  • launch.jsontasks.json 파일을 웹 확장 테스트 섹션에 표시된 대로 추가하세요.
  • webpack 구성 파일에서 입력 파일을 기존 Node.js 메인 파일로 설정하거나 웹 확장을 위한 새 메인 파일을 만드세요.
  • package.json웹 확장 구조 섹션에 표시된 대로 browserscripts 속성을 추가하세요.
  • npm run compile-web을 실행하여 webpack을 호출하고 확장이 웹에서 실행되도록 만드는 데 필요한 부분을 확인하세요.

가능한 한 많은 소스 코드를 재사용할 수 있도록 다음은 몇 가지 기법입니다.

  • path와 같은 Node.js 코어 모듈을 폴리필하려면 resolve.fallback에 항목을 추가하세요.
  • process와 같은 Node.js 전역 변수를 제공하려면 DefinePlugin 플러그인을 사용하세요.
  • 브라우저와 노드 런타임 모두에서 작동하는 노드 모듈을 사용하세요. 노드 모듈은 browsermain 진입점을 모두 정의하여 이를 수행할 수 있습니다. 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의 사용을 주의하세요. vscode workspace.fs를 사용하여 대체하세요.

확장이 웹에서 실행될 때 기능이 적은 것은 괜찮습니다. When 절 컨텍스트를 사용하여 웹에서 가상 워크스페이스에서 실행될 때 사용 가능한 또는 숨겨진 명령, 보기 및 작업을 제어하세요.

  • virtualWorkspace 컨텍스트 변수를 사용하여 현재 작업 공간이 파일 시스템이 아닌 작업 공간인지 확인하세요.
  • resourceScheme을 사용하여 현재 리소스가 file 리소스인지 확인하세요.
  • 플랫폼 셸이 있는 경우 shellExecutionSupported를 사용하세요.
  • 명령이 적용되지 않는 이유를 설명하는 대화 상자를 표시하는 대체 명령 처리기를 구현하세요.

WebWorker는 프로세스 포크의 대안으로 사용할 수 있습니다. JSON, CSS 및 HTML 언어 서버와 같은 빌트인 JSON, CSSHTML 언어 서버를 포함하여 여러 언어 서버를 웹 확장으로 실행하도록 업데이트했습니다. 아래 언어 서버 프로토콜 섹션에서 자세한 내용을 확인할 수 있습니다.

브라우저 런타임 환경은 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' 모듈을 번들에서 제외합니다.
    • processbuffer에 대한 폴리필을 생성합니다.
    • esbuildProblemMatcherPlugin 플러그인을 사용하여 번들러가 완료되지 못하게 한 오류를 보고합니다. 이 플러그인은 확장으로도 설치해야 하는 esbuild 문제 해결사가 감지하는 형식으로 오류를 내보냅니다.
    • testBundlePlugin을 사용하여 모든 테스트 파일과 mocha 테스트 실행기 mochaTestRunner.js를 참조하는 테스트 메인 파일(extensionTests.js)을 구현합니다.
  • --watch 플래그가 전달된 경우 소스 파일의 변경 사항을 감시하고 변경 사항이 감지될 때마다 번들을 다시 빌드합니다.

esbuild는 TypeScript 파일과 직접 작업할 수 있습니다. 그러나 esbuild는 타입 검사를 수행하지 않고 모든 타입 선언을 제거하기만 합니다. 구문 오류만 보고되며 esbuild가 실패하는 원인이 될 수 있습니다.

따라서 TypeScript 컴파일러(tsc)를 별도로 실행하지만 코드를 내보내지는 않습니다 (--noEmit 플래그).

package.jsonscripts 섹션은 이제 다음과 같이 보입니다.

  "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:esbuildwatch-web:tsc 스크립트를 실행합니다. package.jsondevDependencies 섹션에 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);
    }
  });
}

샘플

© . This site is unofficial and not affiliated with Microsoft.