Skip to content

Commit 9934985

Browse files
committed
fix: ts errors in Preview module
1 parent c19aef2 commit 9934985

File tree

5 files changed

+27720
-31856
lines changed

5 files changed

+27720
-31856
lines changed

client/custom.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,8 @@ interface NodeModule {
1919
accept(path?: string, callback?: () => void): void;
2020
};
2121
}
22+
23+
// Type declarations for modules without TypeScript definitions
24+
declare module 'loop-protect';
25+
declare module 'jshint';
26+
declare module 'decomment';

client/modules/Preview/EmbedFrame.tsx

Lines changed: 116 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import {
1616
} from '../../../server/utils/fileUtils';
1717
import { getAllScriptOffsets } from '../../utils/consoleUtils';
1818
import { registerFrame } from '../../utils/dispatcher';
19-
import { createBlobUrl } from './filesReducer';
19+
import { createBlobUrl, File } from './filesReducer';
2020
import 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(/.*\.js$/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(/.*\.html$/i))[0];
278+
function getHtmlFile(files: File[]): File {
279+
return files.filter((file: File) => file.name.match(/.*\.html$/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"

client/modules/Preview/filesReducer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import mime from 'mime';
44
import { PLAINTEXT_FILE_REGEX } from '../../../server/utils/fileUtils';
55

66
// Define types for File, State, and Action
7-
interface File {
7+
export interface File {
88
id: string;
99
name: string;
1010
content: string;
1111
blobUrl?: string;
12+
url?: string;
1213
children: string[];
14+
filePath?: string;
1315
}
1416

1517
interface State extends Array<File> {}

0 commit comments

Comments
 (0)