mirror of
https://github.com/Microsoft/vscode
synced 2024-10-06 03:17:00 +00:00
Implements hot reloading of js for the monaco playground server. (#185555)
This commit is contained in:
parent
b975405819
commit
b8a3395fed
|
@ -23,10 +23,9 @@ function main() {
|
|||
const editorMainBundle = new CachedBundle('vs/editor/editor.main', moduleIdMapper);
|
||||
fileServer.overrideFileContent(editorMainBundle.entryModulePath, () => editorMainBundle.bundle());
|
||||
|
||||
const hotReloadJsCode = getHotReloadCode(new URL('/file-changes', server.url));
|
||||
const loaderPath = path.join(rootDir, 'out/vs/loader.js');
|
||||
fileServer.overrideFileContent(loaderPath, async () =>
|
||||
Buffer.from(new TextEncoder().encode(`${await fsPromise.readFile(loaderPath, 'utf8')}\n${hotReloadJsCode}`))
|
||||
Buffer.from(new TextEncoder().encode(makeLoaderJsHotReloadable(await fsPromise.readFile(loaderPath, 'utf8'), new URL('/file-changes', server.url))))
|
||||
);
|
||||
|
||||
const watcher = DirWatcher.watchRecursively(moduleIdMapper.rootDir);
|
||||
|
@ -35,7 +34,7 @@ function main() {
|
|||
editorMainBundle.bundle();
|
||||
console.log(`${new Date().toLocaleTimeString()}, file change: ${path}`);
|
||||
});
|
||||
server.use('/file-changes', handleGetFileChangesRequest(watcher, fileServer));
|
||||
server.use('/file-changes', handleGetFileChangesRequest(watcher, fileServer, moduleIdMapper));
|
||||
|
||||
console.log(`Server listening on ${server.url}`);
|
||||
}
|
||||
|
@ -169,6 +168,12 @@ function getContentType(filePath: string): string {
|
|||
return 'image/png';
|
||||
case '.jpg':
|
||||
return 'image/jpg';
|
||||
case '.svg':
|
||||
return 'image/svg+xml';
|
||||
case '.html':
|
||||
return 'text/html';
|
||||
case '.wasm':
|
||||
return 'application/wasm';
|
||||
default:
|
||||
return 'text/plain';
|
||||
}
|
||||
|
@ -215,56 +220,107 @@ class DirWatcher {
|
|||
}
|
||||
}
|
||||
|
||||
function handleGetFileChangesRequest(watcher: DirWatcher, fileServer: FileServer): ChainableRequestHandler {
|
||||
function handleGetFileChangesRequest(watcher: DirWatcher, fileServer: FileServer, moduleIdMapper: SimpleModuleIdPathMapper): ChainableRequestHandler {
|
||||
return async (req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
const d = watcher.onDidChange(fsPath => {
|
||||
const path = fileServer.filePathToUrlPath(fsPath);
|
||||
if (path) {
|
||||
res.write(JSON.stringify({ changedPath: path }) + '\n');
|
||||
res.write(JSON.stringify({ changedPath: path, moduleId: moduleIdMapper.getModuleId(fsPath) }) + '\n');
|
||||
}
|
||||
});
|
||||
res.on('close', () => d.dispose());
|
||||
};
|
||||
}
|
||||
function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): string {
|
||||
loaderJsCode = loaderJsCode.replace(
|
||||
/constructor\(env, scriptLoader, defineFunc, requireFunc, loaderAvailableTimestamp = 0\) {/,
|
||||
'$&globalThis.$$globalModuleManager = this;'
|
||||
);
|
||||
|
||||
function getHotReloadCode(fileChangesUrl: URL): string {
|
||||
const additionalJsCode = `
|
||||
function $watchChanges() {
|
||||
console.log("Connecting to server to watch for changes...");
|
||||
fetch(${JSON.stringify(fileChangesUrl)})
|
||||
.then(async request => {
|
||||
const reader = request.body.getReader();
|
||||
let buffer = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) { break; }
|
||||
buffer += new TextDecoder().decode(value);
|
||||
const lines = buffer.split('\\n');
|
||||
buffer = lines.pop();
|
||||
for (const line of lines) {
|
||||
const data = JSON.parse(line);
|
||||
if (data.changedPath.endsWith('.css')) {
|
||||
console.log('css changed', data.changedPath);
|
||||
const styleSheet = [...document.querySelectorAll("link[rel='stylesheet']")].find(l => new URL(l.href, document.location.href).pathname.endsWith(data.changedPath));
|
||||
if (styleSheet) {
|
||||
styleSheet.href = styleSheet.href.replace(/\\?.*/, '') + '?' + Date.now();
|
||||
const $$globalModuleManager: any = undefined;
|
||||
|
||||
// This code will be appended to loader.js
|
||||
function $watchChanges(fileChangesUrl: string) {
|
||||
let reloadFn;
|
||||
if (globalThis.$sendMessageToParent) {
|
||||
reloadFn = () => globalThis.$sendMessageToParent({ kind: 'reload' });
|
||||
} else if (typeof window !== 'undefined') {
|
||||
reloadFn = () => window.location.reload();
|
||||
} else {
|
||||
reloadFn = () => { };
|
||||
}
|
||||
|
||||
console.log('Connecting to server to watch for changes...');
|
||||
(fetch as any)(fileChangesUrl)
|
||||
.then(async request => {
|
||||
const reader = request.body.getReader();
|
||||
let buffer = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) { break; }
|
||||
buffer += new TextDecoder().decode(value);
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop()!;
|
||||
for (const line of lines) {
|
||||
const data = JSON.parse(line);
|
||||
let handled = false;
|
||||
if (data.changedPath.endsWith('.css')) {
|
||||
console.log('css changed', data.changedPath);
|
||||
const styleSheet = [...document.querySelectorAll(`link[rel='stylesheet']`)].find((l: any) => new URL(l.href, document.location.href).pathname.endsWith(data.changedPath)) as any;
|
||||
if (styleSheet) {
|
||||
styleSheet.href = styleSheet.href.replace(/\?.*/, '') + '?' + Date.now();
|
||||
}
|
||||
handled = true;
|
||||
} else if (data.changedPath.endsWith('.js') && data.moduleId) {
|
||||
console.log('js changed', data.changedPath);
|
||||
const moduleId = $$globalModuleManager._moduleIdProvider.getModuleId(data.moduleId);
|
||||
if ($$globalModuleManager._modules2[moduleId]) {
|
||||
const srcUrl = $$globalModuleManager._config.moduleIdToPaths(data.moduleId);
|
||||
const newSrc = await (await fetch(srcUrl)).text();
|
||||
(new Function('define', newSrc))(function (deps, callback) {
|
||||
const oldModule = $$globalModuleManager._modules2[moduleId];
|
||||
delete $$globalModuleManager._modules2[moduleId];
|
||||
|
||||
$$globalModuleManager.defineModule(data.moduleId, deps, callback);
|
||||
const newModule = $$globalModuleManager._modules2[moduleId];
|
||||
const oldExports = { ...oldModule.exports };
|
||||
|
||||
Object.assign(oldModule.exports, newModule.exports);
|
||||
newModule.exports = oldModule.exports;
|
||||
|
||||
handled = true;
|
||||
|
||||
for (const cb of [...globalThis.$hotReload_deprecateExports]) {
|
||||
cb(oldExports, newModule.exports);
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
console.log('hot reloaded', data.moduleId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$sendMessageToParent({ kind: "reload" });
|
||||
|
||||
if (!handled) { reloadFn(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
setTimeout($watchChanges, 1000);
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
setTimeout(() => $watchChanges(fileChangesUrl), 1000);
|
||||
});
|
||||
|
||||
}
|
||||
$watchChanges();
|
||||
}
|
||||
|
||||
const additionalJsCode = `
|
||||
(${(function () {
|
||||
globalThis.$hotReload_deprecateExports = new Set<(oldExports: any, newExports: any) => void>();
|
||||
}).toString()})();
|
||||
${$watchChanges.toString()}
|
||||
$watchChanges(${JSON.stringify(fileChangesUrl)});
|
||||
`;
|
||||
return additionalJsCode;
|
||||
|
||||
return `${loaderJsCode}\n${additionalJsCode}`;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
|
Loading…
Reference in a new issue