From 7a52ddeba2a68388b544f529d2d92104420f77b0 Mon Sep 17 00:00:00 2001 From: Shipwreckt Date: Fri, 31 Oct 2025 20:02:14 +0000 Subject: Changed from static to 11ty! --- node_modules/@11ty/eleventy/src/EleventyServe.js | 321 +++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 node_modules/@11ty/eleventy/src/EleventyServe.js (limited to 'node_modules/@11ty/eleventy/src/EleventyServe.js') diff --git a/node_modules/@11ty/eleventy/src/EleventyServe.js b/node_modules/@11ty/eleventy/src/EleventyServe.js new file mode 100644 index 0000000..65525ef --- /dev/null +++ b/node_modules/@11ty/eleventy/src/EleventyServe.js @@ -0,0 +1,321 @@ +import assert from "node:assert"; + +import debugUtil from "debug"; +import { Merge, DeepCopy, TemplatePath } from "@11ty/eleventy-utils"; + +import EleventyBaseError from "./Errors/EleventyBaseError.js"; +import ConsoleLogger from "./Util/ConsoleLogger.js"; +import PathPrefixer from "./Util/PathPrefixer.js"; +import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js"; +import { getModulePackageJson } from "./Util/ImportJsonSync.js"; +import { EleventyImport } from "./Util/Require.js"; +import { isGlobMatch } from "./Util/GlobMatcher.js"; + +const debug = debugUtil("Eleventy:EleventyServe"); + +class EleventyServeConfigError extends EleventyBaseError {} + +const DEFAULT_SERVER_OPTIONS = { + module: "@11ty/eleventy-dev-server", + port: 8080, + // pathPrefix: "/", + // setup: function() {}, + // ready: function(server) {}, + // logger: { info: function() {}, error: function() {} } +}; + +class EleventyServe { + #eleventyConfig; + + constructor() { + this.logger = new ConsoleLogger(); + this._initOptionsFetched = false; + this._aliases = undefined; + this._watchedFiles = new Set(); + } + + get config() { + if (!this.eleventyConfig) { + throw new EleventyServeConfigError( + "You need to set the eleventyConfig property on EleventyServe.", + ); + } + + return this.eleventyConfig.getConfig(); + } + + set config(config) { + throw new Error("It’s not allowed to set config on EleventyServe. Set eleventyConfig instead."); + } + + setAliases(aliases) { + this._aliases = aliases; + + if (this._server && "setAliases" in this._server) { + this._server.setAliases(aliases); + } + } + + get eleventyConfig() { + if (!this.#eleventyConfig) { + throw new EleventyServeConfigError( + "You need to set the eleventyConfig property on EleventyServe.", + ); + } + + return this.#eleventyConfig; + } + + set eleventyConfig(config) { + this.#eleventyConfig = config; + + if (checkPassthroughCopyBehavior(this.#eleventyConfig.userConfig, "serve")) { + this.#eleventyConfig.userConfig.events.on("eleventy.passthrough", ({ map }) => { + // for-free passthrough copy + this.setAliases(map); + }); + } + } + + // TODO directorynorm + setOutputDir(outputDir) { + // TODO check if this is different and if so, restart server (if already running) + // This applies if you change the output directory in your config file during watch/serve + this.outputDir = outputDir; + } + + async getServerModule(name) { + try { + if (!name || name === DEFAULT_SERVER_OPTIONS.module) { + return import("@11ty/eleventy-dev-server").then((i) => i.default); + } + + // Look for peer dep in local project + let projectNodeModulesPath = TemplatePath.absolutePath("./node_modules/"); + let serverPath = TemplatePath.absolutePath(projectNodeModulesPath, name); + // No references outside of the project node_modules are allowed + if (!serverPath.startsWith(projectNodeModulesPath)) { + throw new Error("Invalid node_modules name for Eleventy server instance, received:" + name); + } + + let serverPackageJson = getModulePackageJson(serverPath); + // Normalize with `main` entry from + if (TemplatePath.isDirectorySync(serverPath)) { + if (serverPackageJson.main) { + serverPath = TemplatePath.absolutePath( + projectNodeModulesPath, + name, + serverPackageJson.main, + ); + } else { + throw new Error( + `Eleventy server ${name} is missing a \`main\` entry in its package.json file. Traversed up from ${serverPath}.`, + ); + } + } + + let module = await EleventyImport(serverPath); + + if (!("getServer" in module)) { + throw new Error( + `Eleventy server module requires a \`getServer\` static method. Could not find one on module: \`${name}\``, + ); + } + + if (serverPackageJson["11ty"]?.compatibility) { + try { + this.eleventyConfig.userConfig.versionCheck(serverPackageJson["11ty"].compatibility); + } catch (e) { + this.logger.warn(`Warning: \`${name}\` Plugin Compatibility: ${e.message}`); + } + } + + return module; + } catch (e) { + this.logger.error( + "There was an error with your custom Eleventy server. We’re using the default server instead.\n" + + e.message, + ); + debug("Eleventy server error %o", e); + return import("@11ty/eleventy-dev-server").then((i) => i.default); + } + } + + get options() { + if (this._options) { + return this._options; + } + + this._options = Object.assign( + { + pathPrefix: PathPrefixer.normalizePathPrefix(this.config.pathPrefix), + logger: this.logger, + }, + DEFAULT_SERVER_OPTIONS, + this.config.serverOptions, + ); + + this._savedConfigOptions = DeepCopy({}, this.config.serverOptions); + + if (!this._initOptionsFetched && this.getSetupCallback()) { + throw new Error( + "Init options have not yet been fetched in the setup callback. This probably means that `init()` has not yet been called.", + ); + } + + return this._options; + } + + get server() { + if (!this._server) { + throw new Error("Missing server instance. Did you call .initServerInstance?"); + } + + return this._server; + } + + async initServerInstance() { + if (this._server) { + return; + } + + let serverModule = await this.getServerModule(this.options.module); + + // Static method `getServer` was already checked in `getServerModule` + this._server = serverModule.getServer("eleventy-server", this.outputDir, this.options); + + this.setAliases(this._aliases); + + if (this._globsNeedWatching) { + this._server.watchFiles(this._watchedFiles); + this._globsNeedWatching = false; + } + } + + getSetupCallback() { + let setupCallback = this.config.serverOptions.setup; + if (setupCallback && typeof setupCallback === "function") { + return setupCallback; + } + } + + async #init() { + let setupCallback = this.getSetupCallback(); + if (setupCallback) { + let opts = await setupCallback(); + this._initOptionsFetched = true; + + if (opts) { + Merge(this.options, opts); + } + } + } + + async init() { + if (!this._initPromise) { + this._initPromise = this.#init(); + } + + return this._initPromise; + } + + // Port comes in here from --port on the command line + async serve(port) { + this._commandLinePort = port; + + await this.init(); + await this.initServerInstance(); + + this.server.serve(port || this.options.port); + + if (typeof this.config.serverOptions?.ready === "function") { + if (typeof this.server.ready === "function") { + // Dev Server 2.0.7+ + // wait for ready promise to resolve before triggering ready callback + await this.server.ready(); + await this.config.serverOptions?.ready(this.server); + } else { + throw new Error( + "The `ready` option in Eleventy’s `setServerOptions` method requires a `ready` function on the Dev Server instance. If you’re using Eleventy Dev Server, you will need Dev Server 2.0.7+ or newer to use this feature.", + ); + } + } + } + + async close() { + if (this._server) { + await this._server.close(); + + this._server = undefined; + } + } + + async sendError({ error }) { + if (this._server) { + await this.server.sendError({ + error, + }); + } + } + + // Restart the server entirely + // We don’t want to use a native `restart` method (e.g. restart() in Vite) so that + // we can correctly handle a `module` property change (changing the server type) + async restart() { + // Blow away cached options + delete this._options; + + await this.close(); + + // saved --port in `serve()` + await this.serve(this._commandLinePort); + + // rewatch the saved watched files (passthrough copy) + if ("watchFiles" in this.server) { + this.server.watchFiles(this._watchedFiles); + } + } + + // checkPassthroughCopyBehavior check is called upstream in Eleventy.js + // TODO globs are not removed from watcher + watchPassthroughCopy(globs) { + this._watchedFiles = globs; + + if (this._server && "watchFiles" in this.server) { + this.server.watchFiles(globs); + this._globsNeedWatching = false; + } else { + this._globsNeedWatching = true; + } + } + + isEmulatedPassthroughCopyMatch(filepath) { + return isGlobMatch(filepath, this._watchedFiles); + } + + hasOptionsChanged() { + try { + assert.deepStrictEqual(this.config.serverOptions, this._savedConfigOptions); + return false; + } catch (e) { + return true; + } + } + + // Live reload the server + async reload(reloadEvent = {}) { + if (!this._server) { + return; + } + + // Restart the server if the options have changed + if (this.hasOptionsChanged()) { + debug("Server options changed, we’re restarting the server"); + await this.restart(); + } else { + await this.server.reload(reloadEvent); + } + } +} + +export default EleventyServe; -- cgit v1.2.3