import { isAbsolute, join } from "../../deno_ral/path.ts";
import { RenderResult, RenderResultFile } from "../../command/render/types.ts";
import { md5HashSync } from "../../core/hash.ts";
import { HttpDevServerRenderMonitor } from "../../core/http-devserver.ts";
import { isJupyterNotebook } from "../../core/jupyter/jupyter.ts";
import { logError } from "../../core/log.ts";
import { isHtmlContent } from "../../core/mime.ts";
import { PromiseQueue } from "../../core/promise.ts";
import { extensionFilesFromDirs } from "../../extension/extension.ts";
import { projectFormatOutputDir } from "../project-shared.ts";
import { kProjectType, ProjectContext } from "../types.ts";
import { projectType } from "../types/project-types.ts";
export class ServeRenderManager {
public fileRequiresReRender(
file: string,
inputFile: string,
extensionDirs: string[],
resourceFiles: string[],
project: ProjectContext,
) {
const lastRenderHash = this.fileRenders_.get(file);
if (lastRenderHash) {
return lastRenderHash !==
this.fileRenderHash(
file,
inputFile,
extensionDirs,
resourceFiles,
project,
);
} else {
return true;
}
}
public submitRender(render: () => Promise<RenderResult>) {
HttpDevServerRenderMonitor.onRenderStart();
return this.renderQueue_.enqueue(render);
}
public isRendering() {
return this.renderQueue_.isRunning();
}
public onRenderError(error: Error) {
HttpDevServerRenderMonitor.onRenderStop(false);
if (error.message) {
logError(error);
}
}
public onRenderResult(
result: RenderResult,
extensionDirs: string[],
resourceFiles: string[],
project: ProjectContext,
) {
HttpDevServerRenderMonitor.onRenderStop(true);
const fileOutputDir = (file: RenderResultFile) => {
const format = file.format;
return projectFormatOutputDir(
format,
project,
projectType(project.config?.project?.[kProjectType]),
);
};
result.files.forEach((resultFile) => {
const file = isAbsolute(resultFile.file)
? resultFile.file
: join(fileOutputDir(resultFile), resultFile.file);
const inputFile = isAbsolute(resultFile.input)
? resultFile.input
: join(project.dir, resultFile.input);
this.fileRenders_.set(
file,
this.fileRenderHash(
file,
inputFile,
extensionDirs,
resourceFiles,
project,
),
);
});
}
private fileRenderHash(
file: string,
inputFile: string,
extensionDirs: string[],
resourceFiles: string[],
project: ProjectContext,
) {
const resourceHash = [
file,
inputFile,
...resourceFiles,
...extensionFilesFromDirs(extensionDirs),
...(project.files.config || []),
...(project.files.configResources || []),
...(project.files.resources || []),
].reduce((hash, file) => {
try {
return hash + Deno.statSync(file).mtime?.toUTCString();
} catch {
return hash;
}
}, "");
if (isJupyterNotebook(inputFile) || !isHtmlContent(file)) {
return String(Deno.statSync(file).mtime) +
String(Deno.statSync(inputFile).mtime) +
resourceHash;
} else {
return md5HashSync(Deno.readTextFileSync(file)) +
md5HashSync(Deno.readTextFileSync(inputFile)) +
resourceHash;
}
}
private fileRenders_ = new Map<string, string>();
private renderQueue_ = new PromiseQueue<RenderResult>();
}