Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created December 8, 2019 09:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mizchi/9dbd9dbbc97378c48ced47964dc97055 to your computer and use it in GitHub Desktop.
Save mizchi/9dbd9dbbc97378c48ced47964dc97055 to your computer and use it in GitHub Desktop.

Entrypoint を探す

yarn web で起動する scripts/code-web.js を読む。 node の http-server になって、パスを動的に書き換えている。

(サーバーが本当に必要なのか?静的アセットにならないんだろうか?という目線で読む)

試しに変数をダンプしてみた。

{
  APP_ROOT: '/Users/mizchi/github/vscode',
  EXTENSIONS_ROOT: '/Users/mizchi/github/vscode/extensions',
  WEB_MAIN: '/Users/mizchi/github/vscode/src/vs/code/browser/workbench/workbench-dev.html'
}

WEB_MAIN が差す workbench-dev.html というファイルがそれっぽい。

JS 初期化してそうな部分を探す。

<script>
	// NOTE: Changes to inline scripts require update of content security policy
	self.require = {
		baseUrl: `${window.location.origin}/static/out`,
		paths: {
			'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`,
			'onigasm-umd': `${window.location.origin}/static/remote/web/node_modules/onigasm-umd/release/main`,
			xterm: `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
			'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
			'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
			'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
			'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`
		}
	};
</script>
<script src="./static/out/vs/loader.js"></script>
<script>
	// NOTE: Changes to inline scripts require update of content security policy
	require(['vs/code/browser/workbench/workbench'], function() {});
</script>

サーバーの実装を見ると、 static/ の部分をプロジェクトルートに指し直して実行してるっぽい。

/**
 * @param {import('http').IncomingMessage} req
 * @param {import('http').ServerResponse} res
 * @param {import('url').UrlWithParsedQuery} parsedUrl
 */
function handleStatic(req, res, parsedUrl) {
	// Strip `/static/` from the path
	const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length)));
	return serveFile(req, res, path.join(APP_ROOT, relativeFilePath));
}

out/ 以下のコードは src/ と相対パスで対応しているっぽいので、適宜読み替える。

src/vs/loader.js はコミット済みのファイルで、その実体はコメント読む限り、 https://github.com/Microsoft/vscode-loader

ざっと見た感じ、要は AMD loader なので、 vs/code/browser/workbench/workbench が起動してそう。

そのエントリポイントっぽい部分

// Find config by checking for DOM
const configElement = document.getElementById('vscode-workbench-web-configuration');
const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined;
if (!configElement || !configElementAttribute) {
	throw new Error('Missing web configuration element');
}

const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents } = JSON.parse(
	configElementAttribute
);

workbench-dev.html のそれっぽい部分

		<!-- Workbench Configuration -->
		<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}" />

これは scripts/code-web.js のサーバーから注入されてて、

const data = (await util.promisify(fs.readFile)(WEB_MAIN))
	.toString()
	.replace(
		'{{WORKBENCH_WEB_CONFIGURATION}}',
		escapeAttribute(
			JSON.stringify({
				staticExtensions,
				folderUri: { scheme: 'memfs', path: `/sample-folder` }
			})
		)
	)
	.replace('{{WEBVIEW_ENDPOINT}}', '')
	.replace('{{REMOTE_USER_DATA_URI}}', '');

だから、ここで起動する staticExtensions や 内部的な folder を memfs で起動する、みたいなことが行われている。

// Finally create workbench
create(document.body, {
	...config,
	workspaceProvider: new WorkspaceProvider(workspace, payload),
	urlCallbackProvider: new PollingURLCallbackProvider(),
	credentialsProvider: new LocalStorageCredentialsProvider()
});

この create の実装 workbench.web.api.ts にあって

/**
 * Creates the workbench with the provided options in the provided container.
 *
 * @param domElement the container to create the workbench in
 * @param options for setting up the workbench
 */
function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise<void> {
	return main(domElement, options);
}

つまりここまで main 関数に渡す諸々を初期化していたっぽい。

この main は web.main.ts

export function main(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise<void> {
	const renderer = new BrowserMain(domElement, options);
	return renderer.open();
}

BrowserMain の constructor をみると空だったので、 open をみる

	async open(): Promise<void> {
		const services = await this.initServices();

		await domContentLoaded();
		mark('willStartWorkbench');

		// Base Theme
		this.restoreBaseTheme();

		// Create Workbench
		const workbench = new Workbench(this.domElement, services.serviceCollection, services.logService);

		// Listeners
		this.registerListeners(workbench, services.storageService);

		// Driver
		if (this.configuration.driver) {
			(async () => this._register(await registerWindowDriver()))();
		}

		// Startup
		workbench.startup();
	}

this.initServices() をざっと読んだ感じ

  • LogService: ログ情報
  • BrowserWorkbenchEnvironmentService: 実行環境情報っぽい
  • ProductService: 中を見るとバージョンの定数などが埋まっていただけ
  • RemoteAuthorityResolverService: vscode リモートの接続の認証周り?
  • SignService: 認証周りかな?
  • RemoteAgentService: リモートに接続しに行くサービス?
  • FileService: ファイルの読み書きを行ってそう。

ここまでで、 FileService が memfs で起動してるのだろう、という直感が得られた。永続化したいので、ここを読みにいく。

FileService

FileService は実装ではなく、これに fileService.registerProvider で各種環境ごとのアダプタを実装する形っぽい。

ここにログを仕込むと、以下のものが流れてきた。

  • vscode-userdata: UserDataProvider
  • http: FetchFileSystemProvider
  • https: FetchFileSystemProvider
  • trustedDomains: TrustedDomainsFileSystemProvider
  • (ここで workbench が起動した)
  • vscode-log: IndexedDBLogProvider
  • memfs: RemoteFileSystemProvider

つまり memfs とは RemoteFileSystemProvider の特殊実装なのでは。

Service

WorkspaceServiceStorageService の初期化が Web 用に特殊化されてる。

// Long running services (workspace, config, storage)
const services = await Promise.all([
	this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(
		service => {
			// Workspace
			serviceCollection.set(IWorkspaceContextService, service);

			// Configuration
			serviceCollection.set(IConfigurationService, service);

			return service;
		}
	),

	this.createStorageService(payload, environmentService, fileService, logService).then(service => {
		// Storage
		serviceCollection.set(IStorageService, service);

		return service;
	})
]);
	private async createStorageService(
		payload: IWorkspaceInitializationPayload,
		environmentService: IWorkbenchEnvironmentService,
		fileService: IFileService,
		logService: ILogService
	): Promise<BrowserStorageService> {
		const storageService = new BrowserStorageService(environmentService, fileService);

		try {
			await storageService.initialize(payload);
			return storageService;
		} catch (error) {
			onUnexpectedError(error);
			logService.error(error);

			return storageService;
		}
	}

BrowserStorageService をみると良いっぽい。


Workbench

Layout を継承してる

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment