Skip to content

Commit 2678f5a

Browse files
committed
replace jsdom
Signed-off-by: shmck <shawn.j.mckay@gmail.com>
1 parent d74bcb1 commit 2678f5a

File tree

5 files changed

+75
-195
lines changed

5 files changed

+75
-195
lines changed

.vscodeignore

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ vsc-extension-quickstart.md
99
## Modules
1010
node_modules/**
1111
!node_modules/fsevents/**
12-
!node_modules/jsdom/**
1312

1413
## TypeScript
1514
**/tsconfig.json

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"package": "./scripts/package.sh",
3232
"storybook": "yarn --cwd web-app storybook",
3333
"test": "jest",
34-
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=build/extension.js --external:vscode --external:fsevents --external:jsdom --format=cjs --platform=node",
34+
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=build/extension.js --external:vscode --external:fsevents --format=cjs --platform=node",
3535
"esbuild": "npm run esbuild-base -- --sourcemap",
3636
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
3737
"test-compile": "tsc -watch -p ./"
@@ -42,7 +42,6 @@
4242
"eslint": "7.32.0",
4343
"git-url-parse": "11.6.0",
4444
"jest": "27.3.1",
45-
"jsdom": "18.1.1",
4645
"node-fetch": "2.6.6",
4746
"semver": "7.3.5",
4847
"ts-jest": "27.0.7",

src/services/webview/render.ts

+69-45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import{JSDOM}from'jsdom'
21
import*aspathfrom'path'
32
import*asvscodefrom'vscode'
43
import{asyncReadFile}from'../node'
@@ -14,39 +13,64 @@ const getNonce = (): string => {
1413
returntext
1514
}
1615

16+
/**
17+
* render
18+
* configures the index.html into a webview panel
19+
*
20+
* React + webpack generate a number of files that are injected on build time
21+
* and must be accommodated for the webview using a vscode:// path.
22+
*
23+
* Modified paths include
24+
* - /static/js/x.chunk.js
25+
* - /static/js/main.chunk.js
26+
* - runtime-main.js
27+
* - /static/css/x.chunk.css
28+
* - /static/css/main.chunk.css
29+
*
30+
* This function also:
31+
* - modifies the base href of the index.html to be the root of the workspace
32+
* - manages the CSP policy for all the loaded files
33+
*/
1734
asyncfunctionrender(panel: vscode.WebviewPanel,rootPath: string): Promise<void>{
35+
// generate vscode-resource build path uri
36+
constcreateUri=(_filePath: string): any=>{
37+
constfilePath=(_filePath.startsWith('vscode') ? _filePath.substr(16) : _filePath).replace('///','\\')
38+
39+
// @ts-ignore
40+
returnpanel.webview.asWebviewUri(vscode.Uri.file(path.join(rootPath,filePath)))
41+
}
42+
1843
try{
1944
// load copied index.html from web app build
20-
constdom=awaitJSDOM.fromFile(path.join(rootPath,'index.html'))
21-
const{ document }=dom.window
45+
lethtml=awaitasyncReadFile(path.join(rootPath,'index.html'),'utf8')
2246

23-
// set base href
24-
constbase: HTMLBaseElement=document.createElement('base')
25-
base.href=`${vscode.Uri.file(path.join(rootPath,'build')).with({scheme: 'vscode-resource'})}`
26-
27-
document.head.appendChild(base)
47+
// set base href at top of <head>
48+
constbaseHref=`${vscode.Uri.file(path.join(rootPath,'build')).with({scheme: 'vscode-resource'})}`
49+
html=html.replace('<head>',`<head><base href="${baseHref}" />`)
2850

2951
// used for CSP
3052
constnonces: string[]=[]
3153
consthashes: string[]=[]
3254

33-
// generate vscode-resource build path uri
34-
constcreateUri=(_filePath: string): any=>{
35-
constfilePath=(_filePath.startsWith('vscode') ? _filePath.substr(16) : _filePath).replace('///','\\')
36-
37-
// @ts-ignore
38-
returnpanel.webview.asWebviewUri(vscode.Uri.file(path.join(rootPath,filePath)))
55+
// fix paths for react static scripts to use vscode-resource paths
56+
varjsBundleChunkRegex=/\/static\/js\/[\d].[^"]*\.js/g
57+
varjsBundleChunk: RegExpExecArray|null=jsBundleChunkRegex.exec(html)
58+
if(jsBundleChunk){
59+
constnonce: string=getNonce()
60+
nonces.push(nonce)
61+
constsrc=createUri(jsBundleChunk[0])
62+
// replace script src, add nonce
63+
html=html.replace(jsBundleChunk[0],`${src}" nonce="${nonce}`)
3964
}
4065

41-
// fix paths for scripts
42-
constscripts: HTMLScriptElement[]=Array.from(document.getElementsByTagName('script'))
43-
for(constscriptofscripts){
44-
if(script.src){
45-
constnonce: string=getNonce()
46-
nonces.push(nonce)
47-
script.nonce=nonce
48-
script.src=createUri(script.src)
49-
}
66+
varmainBundleChunkRegex=/\/static\/js\/main.[^"]*\.js/g
67+
varmainBundleChunk: RegExpExecArray|null=mainBundleChunkRegex.exec(html)
68+
if(mainBundleChunk){
69+
constnonce: string=getNonce()
70+
nonces.push(nonce)
71+
constsrc=createUri(mainBundleChunk[0])
72+
// replace script src, add nonce
73+
html=html.replace(mainBundleChunk[0],`${src}" nonce="${nonce}`)
5074
}
5175

5276
// support additional CSP exemptions when CodeRoad is embedded
@@ -61,43 +85,45 @@ async function render(panel: vscode.WebviewPanel, rootPath: string): Promise<voi
6185
}
6286
}
6387

64-
// add run-time script from webpack
65-
construnTimeScript=document.createElement('script')
66-
runTimeScript.nonce=getNonce()
67-
nonces.push(runTimeScript.nonce)
68-
6988
// note: file cannot be imported or results in esbuild error. Easier to read it.
7089
letmanifest
7190
try{
7291
constmanifestPath=path.join(rootPath,'asset-manifest.json')
73-
console.log(manifestPath)
7492
constmanifestFile=awaitasyncReadFile(manifestPath,'utf8')
7593
manifest=JSON.parse(manifestFile)
7694
}catch(e){
7795
thrownewError('Failed to read manifest file')
7896
}
7997

80-
runTimeScript.src=createUri(manifest.files['runtime-main.js'])
81-
document.body.appendChild(runTimeScript)
98+
// add run-time script from webpack at top of <body>
99+
construntimeNonce=getNonce()
100+
nonces.push(runtimeNonce)
101+
construntimeSrc=createUri(manifest.files['runtime-main.js'])
102+
html=html.replace('<body>',`<body><script src="${runtimeSrc}" nonce="${runtimeNonce}"></script>`)
103+
104+
varcssBundleChunkRegex=/\/static\/css\/[\d].[^"]*\.css/g
105+
varcssBundleChunk: RegExpExecArray|null=cssBundleChunkRegex.exec(html)
106+
if(cssBundleChunk){
107+
consthref=createUri(cssBundleChunk[0])
108+
// replace script src, add nonce
109+
html=html.replace(cssBundleChunk[0],href)
110+
}
82111

83-
// fix paths for links
84-
conststyles: HTMLLinkElement[]=Array.from(document.getElementsByTagName('link'))
85-
for(conststyleofstyles){
86-
if(style.href){
87-
style.href=createUri(style.href)
88-
}
112+
varmainCssBundleChunkRegex=/\/static\/css\/main.[^"]*\.css/g
113+
varmainCssBundleChunk: RegExpExecArray|null=mainCssBundleChunkRegex.exec(html)
114+
if(mainCssBundleChunk){
115+
consthref=createUri(mainCssBundleChunk[0])
116+
// replace script src, add nonce
117+
html=html.replace(mainCssBundleChunk[0],href)
89118
}
90119

91120
// set CSP (content security policy) to grant permission to local files
92121
// while blocking unexpected malicious network requests
93-
constcspMeta: HTMLMetaElement=document.createElement('meta')
94-
cspMeta.httpEquiv='Content-Security-Policy'
95-
96122
constwrapInQuotes=(str: string)=>`'${str}'`
97123
constnonceString=nonces.map((nonce: string)=>wrapInQuotes(`nonce-${nonce}`)).join(' ')
98124
consthashString=hashes.map(wrapInQuotes).join(' ')
99125

100-
cspMeta.content=
126+
constcspMetaString=
101127
[
102128
`default-src 'self'`,
103129
`manifest-src ${hashString} 'self'`,
@@ -110,10 +136,8 @@ async function render(panel: vscode.WebviewPanel, rootPath: string): Promise<voi
110136
// @ts-ignore
111137
`style-src ${panel.webview.cspSource} https: 'self' 'unsafe-inline'`,
112138
].join('; ')+';'
113-
document.head.appendChild(cspMeta)
114-
115-
// stringify dom
116-
consthtml=dom.serialize()
139+
// add CSP to end of <head>
140+
html=html.replace('</head>',`<meta http-equiv="Content-Security-Policy" content="${cspMetaString}" /></head>`)
117141

118142
// set view
119143
panel.webview.html=html

web-app/public/index.html

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<!DOCTYPE html>
22
<htmllang="en">
33
<head>
4+
<!-- INJECT_BASE -->
45
<metacharset="utf-8" />
56
<metaname="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
67
<metaname="theme-color" content="#000000" />
@@ -47,6 +48,7 @@
4748
}
4849
}
4950
</script>
51+
<!-- INJECT_CSP -->
5052
</head>
5153

5254
<body>
@@ -68,5 +70,6 @@ <h3 id="coderoad-message">
6870
To begin the development, run `npm start` or `yarn start`.
6971
To create a production bundle, use `npm run build` or `yarn build`.
7072
-->
73+
<!-- INJECT_SCRIPT -->
7174
</body>
7275
</html>

0 commit comments

Comments
 (0)
close