-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shopify Liquid VS Code extension for Web #529
base: main
Are you sure you want to change the base?
Conversation
}; | ||
|
||
const reader = new BrowserMessageReader(worker); | ||
const writer = new BrowserMessageWriter(worker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I learned by looking at examples that there was an easier way :)
@@ -40,6 +40,7 @@ | |||
"dev:build": "webpack --mode development", | |||
"dev:syntax": "yarn --cwd ./syntaxes dev", | |||
"dev:watch": "webpack --mode development --watch", | |||
"dev:web": "yarn build && vscode-test-web --permission=clipboard-read --permission=clipboard-write --browserType=chromium --extensionDevelopmentPath=.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vscode-test-web is the one that lets you run chrome and have vscode run in it with your extension loaded. It was a real breeze ngl!
@@ -83,7 +85,8 @@ | |||
"activationEvents": [ | |||
"workspaceContains:**/.theme-check.yml" | |||
], | |||
"main": "./dist/extension.js", | |||
"main": "./dist/node/extension.js", | |||
"browser": "./dist/browser/extension.js", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Major difference is here: We have a different build for web that uses theme-language-server-browser
instead of theme-language-server-node
"messages", | ||
"verbose" | ||
] | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added that stuff in there because I was tired of not having code completion in settings.json to enable the LSP logging
function createWorkerLanguageClient( | ||
context: ExtensionContext, | ||
clientOptions: LanguageClientOptions, | ||
) { | ||
// Create a worker. The worker main file implements the language server. | ||
const serverMain = Uri.joinPath(context.extensionUri, 'dist', 'browser', 'server.js'); | ||
const worker = new Worker(serverMain.toString(true)); | ||
|
||
// create the language server client to communicate with the server running in the worker | ||
return new LanguageClient( | ||
'shopifyLiquid', | ||
'Theme Check Language Server', | ||
clientOptions, | ||
worker, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Major difference is that here we start the language server in a Web Worker instead of settingup STDIO and a subprocess.
7cb9d21
to
ccf72aa
Compare
71a2c6b
to
2ffbef6
Compare
2ffbef6
to
4d7cefe
Compare
c22727f
to
e1aed1e
Compare
- Change `Config` interface to refer to `rootUri` instead of `root` as an absolutePath - Get rid of `fileExists` DI in theme-check Dependencies - Get rid of `fileExists` DI in theme-language-server Dependencies - Add `fs` DI in theme-check Dependencies - Add `fs` DI in theme-language-server Dependencies - Add `MockFileSystem` implementation (for tests) - Add `NodeFileSystem` implementation - Add optional `fs` argument to `theme-language-server-node#startServer()` The `fs` DI has the following interface (so far): ```ts interface FileSystem { stat(uri: string): Promise<{ fileType, size }>; readFile(uri: string): Promise<string>; readDirectory(uri: string): Promise<[uri: string, fileType: FileType][]>; } ``` theme-language-server-node now also accepts an optional `fs` implementation in startServer.
c9b4e00
to
7bd1486
Compare
Instead of having that logic be injected in, I added an abstract implementation in theme-check-common that depends on the virtual file system. We will no longer need to inject this behaviour :)
FileSystem is an ambient import which made the DX suffer when refactoring code. It's also more obvious now that the FileSystem interface is an interface that you need to implement, not something concrete that you can use directly.
4f24bdc
to
ae0ac49
Compare
Replace it with URIs. That way we won't need to worry about windows paths being weird. The only place where we still have them is for places where we deal with `glob` and `loadConfig` in `theme-{check,language-server}-node`. - SourceCode.absolutePath -> SourceCode.uri - etc.
07b17c9
to
dd53f1a
Compare
We still have an optiomal dependency that is otherwise implemented by a naive implementation (for CLI use cases), but in the language server we prefer the buffer over the file in the file system... Which makes me think we could get rid of that by actually making that logic part of the getTranslationsFactory directly. Prefer theme files over fs.
39b4dbe
to
28c2318
Compare
All that logic didn't need to be in the language server. This makes the dep injection easier too because we no longer have this upstream concern.
Replace with AbstractFileSystem-based implementation
Replace with abstract-file-system-based implementation TODO make that shit faster. Seems highlighy unoptimized.
28c2318
to
6ec9236
Compare
Replace with abstract-file-system-based implementation in common code.
- Adds support for VS Code for Web - Adds support for remote files
7c5b372
to
71d5086
Compare
You don't need to query VS Code _every time_ the user types something for the same imformation. That's rather slow! We'll invalidate the relevant caches when files are saved/deleted/created/renamed.
/** | ||
* The AbstractFileSystem interface is used to abstract file system operations. | ||
* | ||
* This way, the Theme Check library can be used in different environments, | ||
* such as the browser, node.js or VS Code (which works with local files, remote | ||
* files and on the web) | ||
*/ | ||
export interface AbstractFileSystem { | ||
stat(uri: string): Promise<FileStat>; | ||
readFile(uri: string): Promise<string>; | ||
readDirectory(uri: string): Promise<FileTuple[]>; | ||
} | ||
|
||
export enum FileType { | ||
Unknown = 0, | ||
File = 1, | ||
Directory = 2, | ||
SymbolicLink = 64, | ||
} | ||
|
||
export interface FileStat { | ||
type: FileType; | ||
size: number; | ||
} | ||
|
||
/** A vscode-uri string */ | ||
export type UriString = string; | ||
|
||
export type FileTuple = [uri: UriString, fileType: FileType]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the most important file in this large diff.
fileExists, | ||
fileSize, | ||
filesForURI, | ||
findRootURI: findConfigurationRootURI, | ||
getDefaultLocaleFactory, | ||
getDefaultTranslationsFactory, | ||
getDefaultSchemaLocaleFactory, | ||
getDefaultSchemaTranslationsFactory, | ||
getThemeSettingsSchemaForRootURI, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)
fileExists, | ||
fileSize, | ||
findRootURI: findConfigurationRootURI, | ||
getDefaultLocaleFactory, | ||
getDefaultTranslationsFactory, | ||
getDefaultSchemaLocaleFactory, | ||
getDefaultSchemaTranslationsFactory, | ||
fs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)
@@ -48,7 +51,7 @@ export interface RequiredDependencies { | |||
* @param uri - a file path | |||
* @returns {Promise<Config>} | |||
*/ | |||
loadConfig(uri: URI): Promise<Config>; | |||
loadConfig(uri: URI, fileExists: (uri: string) => Promise<boolean>): Promise<Config>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the fileExists here is a performance improvement. Wanted loadConfig
to use findRoot
and that thing depends on fileExists
which should be the CachedFileSystem, not the raw one.
{ scheme: 'file', language: 'liquid' }, | ||
{ scheme: 'file', language: 'plaintext' }, | ||
{ scheme: 'file', language: 'html' }, | ||
{ scheme: 'file', language: 'javascript' }, | ||
{ scheme: 'file', language: 'css' }, | ||
{ scheme: 'file', language: 'scss' }, | ||
{ scheme: 'file', language: 'json' }, | ||
{ scheme: 'file', language: 'jsonc' }, | ||
{ language: 'liquid' }, | ||
{ language: 'plaintext' }, | ||
{ language: 'html' }, | ||
{ language: 'javascript' }, | ||
{ language: 'css' }, | ||
{ language: 'scss' }, | ||
{ language: 'json' }, | ||
{ language: 'jsonc' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This thing took me a while to figure out since I wrote this 3 years ago. We need to remove scheme
in order for completion requests from vscode-vfs://
URIs to trigger.
libraryTarget: 'var', | ||
library: 'serverExportVar', | ||
devtoolModuleFilenameTemplate: '../../[resource-path]', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason I need 2x a config that should look like one is because of this difference here. The worker has to be "import-less", but the extension needs a require('vscode')
call in the output that gets shimmed.
Made to demo what it's like to use a VirtualFileSystem in a non-vscode environment.
3919133
to
8a05d45
Compare
8a05d45
to
f3d1197
Compare
95da0b8
to
3a7ea94
Compare
What are you adding in this PR?
vscode-extension
:browser
entry in ourpackage.json
src
folder has been split intonode
,common
andbrowser
foldersbrowser
module starts the language server in a Web Workernode
module starts the language server in a subprocesstheme-check-*
, andtheme-language-server-*
:AbsolutePath
concerns withUri
s.Dependencies
with a singlefs: AbstractFileSystem
dependencyfs
NodeFileSystem
is available (and used by default)NodeFileSystem
is available (and used by default)VsCodeFileSystem
is availablefs/readFile
-> a TextDecodedvscode.workspace.fs.readFile(uri)
fs/readDirectory
->vscode.workspace.fs.readDirectory(uri)
fs/stat
->vscode.workspace.fs.stat(uri)
node
, the same happens but short circuits to theNodeFileSystem
forfile:///
URIs.codemirror-language-client
's playground implements a mock "MainThreadFileSystem" that we could use as inspiration for online-store-webDemo
vscode-for-web-vfs.mp4
Top-hatting
Debugging in Chromium VS Code for Web
The "true" E2E way. Demo worthy version of "showing it works in browser"
vscode-for-web-vfs-2.mp4
Debugging in the Desktop app
A faster, debuggable way that runs the browser extension in the desktop app. This disables all the Node APIs but it lets you debug the code.
Debugging the CodeMirror Language client
(Not new, but you should know that this flow is also updated to virtual file systems).
TODO
What's next? Any followup issues?
loadConfig
work in-browserWhat did you learn?
BrowserMessageReader(worker)
andBrowserMessageWriter(worker)
.vscode.workspace.fs
is pretty cool. It does a pretty clever the URIScheme -> FileSystemProvider mapping that makes it so we could eventually have a "ShopifyBackendFileSystem" that would write directly to the storefront.Before you deploy
changeset