@@ -16,13 +16,13 @@ import {
1616} from '../../../server/utils/fileUtils' ;
1717import { getAllScriptOffsets } from '../../utils/consoleUtils' ;
1818import { registerFrame } from '../../utils/dispatcher' ;
19- import { createBlobUrl } from './filesReducer' ;
19+ import { createBlobUrl , File } from './filesReducer' ;
2020import resolvePathsForElementsWithAttribute from '../../../server/utils/resolveUtils' ;
2121
22- let objectUrls = { } ;
23- let objectPaths = { } ;
22+ let objectUrls : Record < string , string > = { } ;
23+ let objectPaths : Record < string , string > = { } ;
2424
25- const Frame = styled . iframe `
25+ const Frame = styled . iframe < { fullView ?: boolean } > `
2626 min-height: 100%;
2727 min-width: 100%;
2828 position: absolute;
@@ -34,29 +34,32 @@ const Frame = styled.iframe`
3434 ` }
3535` ;
3636
37- function resolveCSSLinksInString ( content , files ) {
37+ function resolveCSSLinksInString ( content : string , files : File [ ] ) : string {
3838 let newContent = content ;
39- let cssFileStrings = content . match ( STRING_REGEX ) ;
40- cssFileStrings = cssFileStrings || [ ] ;
41- cssFileStrings . forEach ( ( cssFileString ) => {
42- if ( cssFileString . match ( MEDIA_FILE_QUOTED_REGEX ) ) {
43- const filePath = cssFileString . substr ( 1 , cssFileString . length - 2 ) ;
44- const quoteCharacter = cssFileString . substr ( 0 , 1 ) ;
45- const resolvedFile = resolvePathToFile ( filePath , files ) ;
46- if ( resolvedFile ) {
47- if ( resolvedFile . url ) {
48- newContent = newContent . replace (
49- cssFileString ,
50- quoteCharacter + resolvedFile . url + quoteCharacter
51- ) ;
39+ const cssFileStrings = content . match ( STRING_REGEX ) ;
40+ if ( cssFileStrings ) {
41+ cssFileStrings . forEach ( ( cssFileString ) => {
42+ if ( cssFileString . match ( MEDIA_FILE_QUOTED_REGEX ) ) {
43+ const filePath = cssFileString . substr ( 1 , cssFileString . length - 2 ) ;
44+ const quoteCharacter = cssFileString . substr ( 0 , 1 ) ;
45+ const resolvedFile = resolvePathToFile ( filePath , files ) as
46+ | File
47+ | undefined ;
48+ if ( resolvedFile && typeof resolvedFile === 'object' ) {
49+ if ( resolvedFile . url ) {
50+ newContent = newContent . replace (
51+ cssFileString ,
52+ quoteCharacter + resolvedFile . url + quoteCharacter
53+ ) ;
54+ }
5255 }
5356 }
54- }
55- } ) ;
57+ } ) ;
58+ }
5659 return newContent ;
5760}
5861
59- function jsPreprocess ( jsText ) {
62+ function jsPreprocess ( jsText : string ) : string {
6063 let newContent = jsText ;
6164 // check the code for js errors before sending it to strip comments
6265 // or loops.
@@ -72,53 +75,59 @@ function jsPreprocess(jsText) {
7275 return newContent ;
7376}
7477
75- function resolveJSLinksInString ( content , files ) {
78+ function resolveJSLinksInString ( content : string , files : File [ ] ) : string {
7679 let newContent = content ;
77- let jsFileStrings = content . match ( STRING_REGEX ) ;
78- jsFileStrings = jsFileStrings || [ ] ;
79- jsFileStrings . forEach ( ( jsFileString ) => {
80- if ( jsFileString . match ( MEDIA_FILE_QUOTED_REGEX ) ) {
81- const filePath = jsFileString . substr ( 1 , jsFileString . length - 2 ) ;
82- const quoteCharacter = jsFileString . substr ( 0 , 1 ) ;
83- const resolvedFile = resolvePathToFile ( filePath , files ) ;
80+ const jsFileStrings = content . match ( STRING_REGEX ) ;
81+ if ( jsFileStrings ) {
82+ jsFileStrings . forEach ( ( jsFileString ) => {
83+ if ( jsFileString . match ( MEDIA_FILE_QUOTED_REGEX ) ) {
84+ const filePath = jsFileString . substr ( 1 , jsFileString . length - 2 ) ;
85+ const quoteCharacter = jsFileString . substr ( 0 , 1 ) ;
86+ const resolvedFile = resolvePathToFile ( filePath , files ) as
87+ | File
88+ | undefined ;
8489
85- if ( resolvedFile ) {
86- if ( resolvedFile . url ) {
87- newContent = newContent . replace (
88- jsFileString ,
89- quoteCharacter + resolvedFile . url + quoteCharacter
90- ) ;
91- } else if ( resolvedFile . name . match ( PLAINTEXT_FILE_REGEX ) ) {
92- newContent = newContent . replace (
93- jsFileString ,
94- quoteCharacter + resolvedFile . blobUrl + quoteCharacter
95- ) ;
90+ if ( resolvedFile && typeof resolvedFile === 'object' ) {
91+ if ( resolvedFile . url ) {
92+ newContent = newContent . replace (
93+ jsFileString ,
94+ quoteCharacter + resolvedFile . url + quoteCharacter
95+ ) ;
96+ } else if ( resolvedFile . name . match ( PLAINTEXT_FILE_REGEX ) ) {
97+ newContent = newContent . replace (
98+ jsFileString ,
99+ quoteCharacter + resolvedFile . blobUrl + quoteCharacter
100+ ) ;
101+ }
96102 }
97103 }
98- }
99- } ) ;
104+ } ) ;
105+ }
100106
101107 return jsPreprocess ( newContent ) ;
102108}
103109
104- function resolveScripts ( sketchDoc , files ) {
110+ function resolveScripts ( sketchDoc : Document , files : File [ ] ) : void {
105111 const scriptsInHTML = sketchDoc . getElementsByTagName ( 'script' ) ;
106112 const scriptsInHTMLArray = Array . prototype . slice . call ( scriptsInHTML ) ;
107113 scriptsInHTMLArray . forEach ( ( script ) => {
108114 if (
109115 script . getAttribute ( 'src' ) &&
110116 script . getAttribute ( 'src' ) . match ( NOT_EXTERNAL_LINK_REGEX ) !== null
111117 ) {
112- const resolvedFile = resolvePathToFile ( script . getAttribute ( 'src' ) , files ) ;
113- if ( resolvedFile ) {
118+ const resolvedFile = resolvePathToFile (
119+ script . getAttribute ( 'src' ) ,
120+ files
121+ ) as File | undefined ;
122+ if ( resolvedFile && typeof resolvedFile === 'object' ) {
114123 if ( resolvedFile . url ) {
115124 script . setAttribute ( 'src' , resolvedFile . url ) ;
116125 } else {
117126 // in the future, when using y.js, could remake the blob for only the file(s)
118127 // that changed
119128 const blobUrl = createBlobUrl ( resolvedFile ) ;
120129 script . setAttribute ( 'src' , blobUrl ) ;
121- const blobPath = blobUrl . split ( '/' ) . pop ( ) ;
130+ const blobPath = blobUrl . split ( '/' ) . pop ( ) || '' ;
122131 // objectUrls[blobUrl] = `${resolvedFile.filePath}${
123132 // resolvedFile.filePath.length > 0 ? '/' : ''
124133 // }${resolvedFile.name}`;
@@ -141,7 +150,7 @@ function resolveScripts(sketchDoc, files) {
141150 } ) ;
142151}
143152
144- function resolveStyles ( sketchDoc , files ) {
153+ function resolveStyles ( sketchDoc : Document , files : File [ ] ) : void {
145154 const inlineCSSInHTML = sketchDoc . getElementsByTagName ( 'style' ) ;
146155 const inlineCSSInHTMLArray = Array . prototype . slice . call ( inlineCSSInHTML ) ;
147156 inlineCSSInHTMLArray . forEach ( ( style ) => {
@@ -155,8 +164,11 @@ function resolveStyles(sketchDoc, files) {
155164 css . getAttribute ( 'href' ) &&
156165 css . getAttribute ( 'href' ) . match ( NOT_EXTERNAL_LINK_REGEX ) !== null
157166 ) {
158- const resolvedFile = resolvePathToFile ( css . getAttribute ( 'href' ) , files ) ;
159- if ( resolvedFile ) {
167+ const resolvedFile = resolvePathToFile (
168+ css . getAttribute ( 'href' ) ,
169+ files
170+ ) as File | undefined ;
171+ if ( resolvedFile && typeof resolvedFile === 'object' ) {
160172 if ( resolvedFile . url ) {
161173 css . href = resolvedFile . url ; // eslint-disable-line
162174 } else {
@@ -170,8 +182,8 @@ function resolveStyles(sketchDoc, files) {
170182 } ) ;
171183}
172184
173- function resolveJSAndCSSLinks ( files ) {
174- const newFiles = [ ] ;
185+ function resolveJSAndCSSLinks ( files : File [ ] ) : File [ ] {
186+ const newFiles : File [ ] = [ ] ;
175187 files . forEach ( ( file ) => {
176188 const newFile = { ...file } ;
177189 if ( file . name . match ( / .* \. j s $ / i) ) {
@@ -184,15 +196,25 @@ function resolveJSAndCSSLinks(files) {
184196 return newFiles ;
185197}
186198
187- function addLoopProtect ( sketchDoc ) {
199+ function addLoopProtect ( sketchDoc : Document ) : void {
188200 const scriptsInHTML = sketchDoc . getElementsByTagName ( 'script' ) ;
189201 const scriptsInHTMLArray = Array . prototype . slice . call ( scriptsInHTML ) ;
190202 scriptsInHTMLArray . forEach ( ( script ) => {
191203 script . innerHTML = jsPreprocess ( script . innerHTML ) ; // eslint-disable-line
192204 } ) ;
193205}
194206
195- function injectLocalFiles ( files , htmlFile , options ) {
207+ interface InjectOptions {
208+ basePath : string ;
209+ gridOutput : boolean ;
210+ textOutput : boolean ;
211+ }
212+
213+ function injectLocalFiles (
214+ files : File [ ] ,
215+ htmlFile : File ,
216+ options : InjectOptions
217+ ) : string {
196218 const { basePath, gridOutput, textOutput } = options ;
197219 let scriptOffs = [ ] ;
198220 objectUrls = { } ;
@@ -253,53 +275,83 @@ p5.prototype.registerMethod('afterSetup', p5.prototype.ensureAccessibleCanvas);`
253275 return `<!DOCTYPE HTML>\n${ sketchDoc . documentElement . outerHTML } ` ;
254276}
255277
256- function getHtmlFile ( files ) {
257- return files . filter ( ( file ) => file . name . match ( / .* \. h t m l $ / i) ) [ 0 ] ;
278+ function getHtmlFile ( files : File [ ] ) : File {
279+ return files . filter ( ( file : File ) => file . name . match ( / .* \. h t m l $ / i) ) [ 0 ] ;
280+ }
281+
282+ interface EmbedFrameProps {
283+ files : File [ ] ;
284+ isPlaying : boolean ;
285+ basePath : string ;
286+ gridOutput : boolean ;
287+ textOutput : boolean ;
258288}
259289
260- function EmbedFrame ( { files, isPlaying, basePath, gridOutput, textOutput } ) {
261- const iframe = useRef ( ) ;
290+ function EmbedFrame ( {
291+ files,
292+ isPlaying,
293+ basePath,
294+ gridOutput,
295+ textOutput
296+ } : EmbedFrameProps ) {
297+ const iframe = useRef < HTMLIFrameElement > ( null ) ;
262298 const htmlFile = useMemo ( ( ) => getHtmlFile ( files ) , [ files ] ) ;
263- const srcRef = useRef ( ) ;
299+ const srcRef = useRef < string | undefined > ( ) ;
264300
265301 useEffect ( ( ) => {
302+ if ( ! iframe . current ?. contentWindow ) {
303+ return ;
304+ }
305+
266306 const unsubscribe = registerFrame (
267307 iframe . current . contentWindow ,
268308 window . origin
269309 ) ;
310+ // eslint-disable-next-line consistent-return
270311 return ( ) => {
271312 unsubscribe ( ) ;
272313 } ;
273314 } ) ;
274315
275316 function renderSketch ( ) {
276317 const doc = iframe . current ;
277- if ( isPlaying ) {
318+ if ( isPlaying && doc ) {
278319 const htmlDoc = injectLocalFiles ( files , htmlFile , {
279320 basePath,
280321 gridOutput,
281322 textOutput
282323 } ) ;
283- const generatedHtmlFile = {
324+ const generatedHtmlFile : File = {
325+ id : '' ,
284326 name : 'index.html' ,
285- content : htmlDoc
327+ content : htmlDoc ,
328+ children : [ ]
286329 } ;
287330 const htmlUrl = createBlobUrl ( generatedHtmlFile ) ;
288331 const toRevoke = srcRef . current ;
289332 srcRef . current = htmlUrl ;
290333 // BRO FOR SOME REASON YOU HAVE TO DO THIS TO GET IT TO WORK ON SAFARI
291334 setTimeout ( ( ) => {
292- doc . src = htmlUrl ;
335+ if ( doc ) {
336+ doc . src = htmlUrl ;
337+ }
293338 if ( toRevoke ) {
294339 blobUtil . revokeObjectURL ( toRevoke ) ;
295340 }
296341 } , 0 ) ;
297- } else {
342+ } else if ( doc ) {
298343 doc . src = '' ;
299344 }
300345 }
301346
302- useEffect ( renderSketch , [ files , isPlaying ] ) ;
347+ useEffect ( renderSketch , [
348+ files ,
349+ isPlaying ,
350+ htmlFile ,
351+ basePath ,
352+ gridOutput ,
353+ textOutput
354+ ] ) ;
303355 return (
304356 < Frame
305357 aria-label = "Sketch Preview"
0 commit comments