Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ondemand support for serve mode #1556

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ try {
"quiet",
"version",
"watch",
"ondemand",
"dryrun",
"help",
"serve",
Expand Down Expand Up @@ -80,6 +81,7 @@ try {
} else if (argv.help) {
console.log(elev.getHelp());
} else if (argv.serve) {
elev.setOnDemand(argv.ondemand);
elev.watch().then(function () {
elev.serve(argv.port);
});
Expand Down
70 changes: 62 additions & 8 deletions src/Eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,14 @@ class Eleventy {
*/
this.formatsOverride = null;

/** @member {Boolean} */
this.isOnDemand = false;

/** @member {Object<string, Function>} */
this._onDemandBuild = {};

/** @member {Object} - tbd. */
this.eleventyServe = new EleventyServe();
this.eleventyServe = new EleventyServe(this._requestBuild.bind(this));

/** @member {String} - Holds the path to the input directory. */
this.rawInput = input;
Expand All @@ -97,6 +103,30 @@ class Eleventy {
this.watchTargets.watchJavaScriptDependencies = this.config.watchJavaScriptDependencies;
}

async _requestBuild(p) {
if (!this.isOnDemand) {
return;
}

p = TemplatePath.join(this.outputDir, p);

let requests = [];
if (p.endsWith('/')) {
requests.push(p + 'index.html');
} else if (!p.endsWith('.html')) {
requests.push(p, p + '/index.html');
} else {
requests.push(p);
}

for (let request of requests) {
let run = this._onDemandBuild[request];
if (run) {
return run();
}
}
}

getNewTimestamp() {
if (performance) {
return performance.now();
Expand Down Expand Up @@ -244,6 +274,7 @@ class Eleventy {
let writeCount = this.writer.getWriteCount();
let skippedCount = this.writer.getSkippedCount();
let copyCount = this.writer.getCopyCount();
let onDemandCount = this.writer.getOnDemandCount();

let slashRet = [];

Expand All @@ -253,11 +284,19 @@ class Eleventy {
);
}

slashRet.push(
`Wrote ${writeCount} ${simplePlural(writeCount, "file", "files")}${
skippedCount ? ` (skipped ${skippedCount})` : ""
}`
);
if (onDemandCount) {
slashRet.push(
`Prepared ${onDemandCount} ${simplePlural(onDemandCount, "file", "files")}`
);
}

if (writeCount || skippedCount || onDemandCount === 0) {
slashRet.push(
`Wrote ${writeCount} ${simplePlural(writeCount, "file", "files")}${
skippedCount ? ` (skipped ${skippedCount})` : ""
}`
);
}

if (slashRet.length) {
ret.push(slashRet.join(" / "));
Expand Down Expand Up @@ -380,6 +419,16 @@ Verbose Output: ${this.verboseMode}`);
}
}

/**
* Updates the verbose mode of Eleventy.
*
* @method
* @param {Boolean} isOnDemand - Shall Eleventy run in on demand mode when serving?
*/
setOnDemand(isOnDemand) {
this.isOnDemand = isOnDemand;
}

/**
* Reads the version of Eleventy.
*
Expand Down Expand Up @@ -759,9 +808,14 @@ Arguments:
await this.config.events.emit("beforeBuild");

try {
let promise = this.writer.write();
if (this.isOnDemand) {
this._onDemandBuild = await this.writer.prepareOnDemand();
debug(`Generated ${Object.keys(this._onDemandBuild).length} on-demand candidates…`);
} else {
let promise = this.writer.write();
ret = await promise;
Comment on lines +815 to +816
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let promise = this.writer.write();
ret = await promise;
ret = await this.writer.write();

}

ret = await promise;
await this.config.events.emit("afterBuild");
} catch (e) {
EleventyErrorHandler.initialMessage(
Expand Down
16 changes: 15 additions & 1 deletion src/EleventyServe.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const config = require("./Config");
const debug = require("debug")("EleventyServe");

class EleventyServe {
constructor() {}
constructor(requestBuild) {
this._requestBuild = requestBuild;
}

get config() {
return this.configOverride || config.getConfig();
Expand Down Expand Up @@ -152,6 +154,18 @@ class EleventyServe {
this.cleanupRedirect(this.savedPathPrefix);

let options = this.getOptions(port);

// if we're on ondemand mode, this will force a build of the file: otherwise, it's a noop
options.middleware = [
(req, res, next) => {
let p = req.url.slice(1);
this._requestBuild(p).catch((err) => {
// TODO: do something with this error
debug(`Could not requestBuild for request: ${req.url}`);
}).then(() => next());
}
];

this.server.init(options);

// this needs to happen after `.getOptions`
Expand Down
10 changes: 3 additions & 7 deletions src/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -653,13 +653,9 @@ class Template extends TemplateContent {
return content;
}

async writeMapEntry(mapEntry) {
await Promise.all(
mapEntry._pages.map(async (page) => {
let content = await this.renderPageEntry(mapEntry, page);
return this._write(page.outputPath, content);
})
);
async writePageEntry(mapEntry, page) {
let content = await this.renderPageEntry(mapEntry, page);
return this._write(page.outputPath, content);
}

// TODO this but better
Expand Down
63 changes: 59 additions & 4 deletions src/TemplateWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class TemplateWriter {
this.isDryRun = false;
this.writeCount = 0;
this.skippedCount = 0;
this.onDemandCount = 0;

// TODO can we get rid of this? It’s only used for tests in `get eleventyFiles``
this.passthroughAll = isPassthroughAll;
Expand Down Expand Up @@ -61,6 +62,7 @@ class TemplateWriter {
restart() {
this.writeCount = 0;
this.skippedCount = 0;
this.onDemandCount = 0;
debugDev("Resetting counts to 0");
}

Expand Down Expand Up @@ -184,10 +186,12 @@ class TemplateWriter {
async _writeTemplate(mapEntry) {
let tmpl = mapEntry.template;

return tmpl.writeMapEntry(mapEntry).then(() => {
this.skippedCount += tmpl.getSkippedCount();
this.writeCount += tmpl.getWriteCount();
});
await Promise.all(
mapEntry._pages.map((page) => tmpl.writePageEntry(mapEntry, page))
);

this.skippedCount += tmpl.getSkippedCount();
this.writeCount += tmpl.getWriteCount();
}

async writePassthroughCopy(paths) {
Expand Down Expand Up @@ -249,6 +253,53 @@ class TemplateWriter {
return promises;
}

async prepareOnDemand() {
let onDemandBuild = {};

// TODO: this models write()

let paths = await this._getAllPaths();
await this.writePassthroughCopy(paths);

await this._createTemplateMap(paths);
debug("Template map created.");

if (
this.incrementalFile &&
this.eleventyFiles
.getPassthroughManager()
.isPassthroughCopyFile(paths, this.incrementalFile)
) {
return onDemandBuild;
}

for (let mapEntry of this.templateMap.getMap()) {
let {template: tmpl, _pages: pages} = mapEntry;

for (let page of pages) {
// in ondemand, building with no permalink isn't supported
const {outputPath} = page;
if (outputPath === false) {
continue;
}
this.onDemandCount++;

// return the same promise if requested multiple times
let p;
onDemandBuild[outputPath] = () => {
if (p === undefined) {
p = tmpl.writePageEntry(mapEntry, page);
}
return p.then(() => {
console.log(`Built ${request}…`);
});
};
}
}

return onDemandBuild;
}

async write() {
let paths = await this._getAllPaths();
let promises = [];
Expand Down Expand Up @@ -302,6 +353,10 @@ class TemplateWriter {
getSkippedCount() {
return this.skippedCount;
}

getOnDemandCount() {
return this.onDemandCount;
}
}

module.exports = TemplateWriter;