Set up comprehensive frontend testing infrastructure
- Install Jest for unit testing with React Testing Library - Install Playwright for end-to-end testing - Configure Jest with proper TypeScript support and module mapping - Create test setup files and utilities for both unit and e2e tests Components: * Jest configuration with coverage thresholds * Playwright configuration with browser automation * Unit tests for LoginForm, AuthContext, and useSocketIO hook * E2E tests for authentication, dashboard, and agents workflows * GitHub Actions workflow for automated testing * Mock data and API utilities for consistent testing * Test documentation with best practices Testing features: - Unit tests with 70% coverage threshold - E2E tests with API mocking and user journey testing - CI/CD integration for automated test runs - Cross-browser testing support with Playwright - Authentication system testing end-to-end 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										198
									
								
								frontend/node_modules/playwright/lib/plugins/gitCommitInfoPlugin.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								frontend/node_modules/playwright/lib/plugins/gitCommitInfoPlugin.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| "use strict"; | ||||
| var __create = Object.create; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __getProtoOf = Object.getPrototypeOf; | ||||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||||
| var __export = (target, all) => { | ||||
|   for (var name in all) | ||||
|     __defProp(target, name, { get: all[name], enumerable: true }); | ||||
| }; | ||||
| var __copyProps = (to, from, except, desc) => { | ||||
|   if (from && typeof from === "object" || typeof from === "function") { | ||||
|     for (let key of __getOwnPropNames(from)) | ||||
|       if (!__hasOwnProp.call(to, key) && key !== except) | ||||
|         __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||||
|   } | ||||
|   return to; | ||||
| }; | ||||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||||
|   // If the importer is in node compatibility mode or this is not an ESM | ||||
|   // file that has been converted to a CommonJS file using a Babel- | ||||
|   // compatible transform (i.e. "__esModule" has not been set), then set | ||||
|   // "default" to the CommonJS "module.exports" for node compatibility. | ||||
|   isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||||
|   mod | ||||
| )); | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var gitCommitInfoPlugin_exports = {}; | ||||
| __export(gitCommitInfoPlugin_exports, { | ||||
|   addGitCommitInfoPlugin: () => addGitCommitInfoPlugin | ||||
| }); | ||||
| module.exports = __toCommonJS(gitCommitInfoPlugin_exports); | ||||
| var fs = __toESM(require("fs")); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| const GIT_OPERATIONS_TIMEOUT_MS = 3e3; | ||||
| const addGitCommitInfoPlugin = (fullConfig) => { | ||||
|   fullConfig.plugins.push({ factory: gitCommitInfoPlugin.bind(null, fullConfig) }); | ||||
| }; | ||||
| function print(s, ...args) { | ||||
|   console.log("GitCommitInfo: " + s, ...args); | ||||
| } | ||||
| function debug(s, ...args) { | ||||
|   if (!process.env.DEBUG_GIT_COMMIT_INFO) | ||||
|     return; | ||||
|   print(s, ...args); | ||||
| } | ||||
| const gitCommitInfoPlugin = (fullConfig) => { | ||||
|   return { | ||||
|     name: "playwright:git-commit-info", | ||||
|     setup: async (config, configDir) => { | ||||
|       const metadata = config.metadata; | ||||
|       const ci = await ciInfo(); | ||||
|       if (!metadata.ci && ci) { | ||||
|         debug("ci info", ci); | ||||
|         metadata.ci = ci; | ||||
|       } | ||||
|       if (fullConfig.captureGitInfo?.commit || fullConfig.captureGitInfo?.commit === void 0 && ci) { | ||||
|         const git = await gitCommitInfo(configDir).catch((e) => print("failed to get git commit info", e)); | ||||
|         if (git) { | ||||
|           debug("commit info", git); | ||||
|           metadata.gitCommit = git; | ||||
|         } | ||||
|       } | ||||
|       if (fullConfig.captureGitInfo?.diff || fullConfig.captureGitInfo?.diff === void 0 && ci) { | ||||
|         const diffResult = await gitDiff(configDir, ci).catch((e) => print("failed to get git diff", e)); | ||||
|         if (diffResult) { | ||||
|           debug(`diff length ${diffResult.length}`); | ||||
|           metadata.gitDiff = diffResult; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| }; | ||||
| async function ciInfo() { | ||||
|   if (process.env.GITHUB_ACTIONS) { | ||||
|     let pr; | ||||
|     try { | ||||
|       const json = JSON.parse(await fs.promises.readFile(process.env.GITHUB_EVENT_PATH, "utf8")); | ||||
|       pr = { title: json.pull_request.title, number: json.pull_request.number, baseHash: json.pull_request.base.sha }; | ||||
|     } catch { | ||||
|     } | ||||
|     return { | ||||
|       commitHref: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`, | ||||
|       commitHash: process.env.GITHUB_SHA, | ||||
|       prHref: pr ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${pr.number}` : void 0, | ||||
|       prTitle: pr?.title, | ||||
|       prBaseHash: pr?.baseHash, | ||||
|       buildHref: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}` | ||||
|     }; | ||||
|   } | ||||
|   if (process.env.GITLAB_CI) { | ||||
|     return { | ||||
|       commitHref: `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`, | ||||
|       commitHash: process.env.CI_COMMIT_SHA, | ||||
|       buildHref: process.env.CI_JOB_URL, | ||||
|       branch: process.env.CI_COMMIT_REF_NAME | ||||
|     }; | ||||
|   } | ||||
|   if (process.env.JENKINS_URL && process.env.BUILD_URL) { | ||||
|     return { | ||||
|       commitHref: process.env.BUILD_URL, | ||||
|       commitHash: process.env.GIT_COMMIT, | ||||
|       branch: process.env.GIT_BRANCH | ||||
|     }; | ||||
|   } | ||||
| } | ||||
| async function gitCommitInfo(gitDir) { | ||||
|   const separator = `---786eec917292---`; | ||||
|   const tokens = [ | ||||
|     "%H", | ||||
|     // commit hash | ||||
|     "%h", | ||||
|     // abbreviated commit hash | ||||
|     "%s", | ||||
|     // subject | ||||
|     "%B", | ||||
|     // raw body (unwrapped subject and body) | ||||
|     "%an", | ||||
|     // author name | ||||
|     "%ae", | ||||
|     // author email | ||||
|     "%at", | ||||
|     // author date, UNIX timestamp | ||||
|     "%cn", | ||||
|     // committer name | ||||
|     "%ce", | ||||
|     // committer email | ||||
|     "%ct", | ||||
|     // committer date, UNIX timestamp | ||||
|     "" | ||||
|     // branch | ||||
|   ]; | ||||
|   const output = await runGit(`git log -1 --pretty=format:"${tokens.join(separator)}" && git rev-parse --abbrev-ref HEAD`, gitDir); | ||||
|   if (!output) | ||||
|     return void 0; | ||||
|   const [hash, shortHash, subject, body, authorName, authorEmail, authorTime, committerName, committerEmail, committerTime, branch] = output.split(separator); | ||||
|   return { | ||||
|     shortHash, | ||||
|     hash, | ||||
|     subject, | ||||
|     body, | ||||
|     author: { | ||||
|       name: authorName, | ||||
|       email: authorEmail, | ||||
|       time: +authorTime * 1e3 | ||||
|     }, | ||||
|     committer: { | ||||
|       name: committerName, | ||||
|       email: committerEmail, | ||||
|       time: +committerTime * 1e3 | ||||
|     }, | ||||
|     branch: branch.trim() | ||||
|   }; | ||||
| } | ||||
| async function gitDiff(gitDir, ci) { | ||||
|   const diffLimit = 1e5; | ||||
|   if (ci?.prBaseHash) { | ||||
|     await runGit(`git fetch origin ${ci.prBaseHash} --depth=1 --no-auto-maintenance --no-auto-gc --no-tags --no-recurse-submodules`, gitDir); | ||||
|     const diff2 = await runGit(`git diff ${ci.prBaseHash} HEAD`, gitDir); | ||||
|     if (diff2) | ||||
|       return diff2.substring(0, diffLimit); | ||||
|   } | ||||
|   if (ci) | ||||
|     return; | ||||
|   const uncommitted = await runGit("git diff", gitDir); | ||||
|   if (uncommitted === void 0) { | ||||
|     return; | ||||
|   } | ||||
|   if (uncommitted) | ||||
|     return uncommitted.substring(0, diffLimit); | ||||
|   const diff = await runGit("git diff HEAD~1", gitDir); | ||||
|   return diff?.substring(0, diffLimit); | ||||
| } | ||||
| async function runGit(command, cwd) { | ||||
|   debug(`running "${command}"`); | ||||
|   const start = (0, import_utils.monotonicTime)(); | ||||
|   const result = await (0, import_utils.spawnAsync)( | ||||
|     command, | ||||
|     [], | ||||
|     { stdio: "pipe", cwd, timeout: GIT_OPERATIONS_TIMEOUT_MS, shell: true } | ||||
|   ); | ||||
|   if ((0, import_utils.monotonicTime)() - start > GIT_OPERATIONS_TIMEOUT_MS) { | ||||
|     print(`timeout of ${GIT_OPERATIONS_TIMEOUT_MS}ms exceeded while running "${command}"`); | ||||
|     return; | ||||
|   } | ||||
|   if (result.code) | ||||
|     debug(`failure, code=${result.code} | ||||
|  | ||||
| ${result.stderr}`); | ||||
|   else | ||||
|     debug(`success`); | ||||
|   return result.code ? void 0 : result.stdout.trim(); | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   addGitCommitInfoPlugin | ||||
| }); | ||||
							
								
								
									
										28
									
								
								frontend/node_modules/playwright/lib/plugins/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								frontend/node_modules/playwright/lib/plugins/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| "use strict"; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||||
| var __export = (target, all) => { | ||||
|   for (var name in all) | ||||
|     __defProp(target, name, { get: all[name], enumerable: true }); | ||||
| }; | ||||
| var __copyProps = (to, from, except, desc) => { | ||||
|   if (from && typeof from === "object" || typeof from === "function") { | ||||
|     for (let key of __getOwnPropNames(from)) | ||||
|       if (!__hasOwnProp.call(to, key) && key !== except) | ||||
|         __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||||
|   } | ||||
|   return to; | ||||
| }; | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var plugins_exports = {}; | ||||
| __export(plugins_exports, { | ||||
|   webServer: () => import_webServerPlugin.webServer | ||||
| }); | ||||
| module.exports = __toCommonJS(plugins_exports); | ||||
| var import_webServerPlugin = require("./webServerPlugin"); | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   webServer | ||||
| }); | ||||
							
								
								
									
										207
									
								
								frontend/node_modules/playwright/lib/plugins/webServerPlugin.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								frontend/node_modules/playwright/lib/plugins/webServerPlugin.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| "use strict"; | ||||
| var __create = Object.create; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __getProtoOf = Object.getPrototypeOf; | ||||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||||
| var __export = (target, all) => { | ||||
|   for (var name in all) | ||||
|     __defProp(target, name, { get: all[name], enumerable: true }); | ||||
| }; | ||||
| var __copyProps = (to, from, except, desc) => { | ||||
|   if (from && typeof from === "object" || typeof from === "function") { | ||||
|     for (let key of __getOwnPropNames(from)) | ||||
|       if (!__hasOwnProp.call(to, key) && key !== except) | ||||
|         __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||||
|   } | ||||
|   return to; | ||||
| }; | ||||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||||
|   // If the importer is in node compatibility mode or this is not an ESM | ||||
|   // file that has been converted to a CommonJS file using a Babel- | ||||
|   // compatible transform (i.e. "__esModule" has not been set), then set | ||||
|   // "default" to the CommonJS "module.exports" for node compatibility. | ||||
|   isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||||
|   mod | ||||
| )); | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var webServerPlugin_exports = {}; | ||||
| __export(webServerPlugin_exports, { | ||||
|   WebServerPlugin: () => WebServerPlugin, | ||||
|   webServer: () => webServer, | ||||
|   webServerPluginsForConfig: () => webServerPluginsForConfig | ||||
| }); | ||||
| module.exports = __toCommonJS(webServerPlugin_exports); | ||||
| var import_net = __toESM(require("net")); | ||||
| var import_path = __toESM(require("path")); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_utils2 = require("playwright-core/lib/utils"); | ||||
| var import_utilsBundle = require("playwright-core/lib/utilsBundle"); | ||||
| const DEFAULT_ENVIRONMENT_VARIABLES = { | ||||
|   "BROWSER": "none", | ||||
|   // Disable that create-react-app will open the page in the browser | ||||
|   "FORCE_COLOR": "1", | ||||
|   "DEBUG_COLORS": "1" | ||||
| }; | ||||
| const debugWebServer = (0, import_utilsBundle.debug)("pw:webserver"); | ||||
| class WebServerPlugin { | ||||
|   constructor(options, checkPortOnly) { | ||||
|     this.name = "playwright:webserver"; | ||||
|     this._options = options; | ||||
|     this._checkPortOnly = checkPortOnly; | ||||
|   } | ||||
|   async setup(config, configDir, reporter) { | ||||
|     this._reporter = reporter; | ||||
|     this._isAvailableCallback = this._options.url ? getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter)) : void 0; | ||||
|     this._options.cwd = this._options.cwd ? import_path.default.resolve(configDir, this._options.cwd) : configDir; | ||||
|     try { | ||||
|       await this._startProcess(); | ||||
|       await this._waitForProcess(); | ||||
|     } catch (error) { | ||||
|       await this.teardown(); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|   async teardown() { | ||||
|     debugWebServer(`Terminating the WebServer`); | ||||
|     await this._killProcess?.(); | ||||
|     debugWebServer(`Terminated the WebServer`); | ||||
|   } | ||||
|   async _startProcess() { | ||||
|     let processExitedReject = (error) => { | ||||
|     }; | ||||
|     this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject); | ||||
|     const isAlreadyAvailable = await this._isAvailableCallback?.(); | ||||
|     if (isAlreadyAvailable) { | ||||
|       debugWebServer(`WebServer is already available`); | ||||
|       if (this._options.reuseExistingServer) | ||||
|         return; | ||||
|       const port = new URL(this._options.url).port; | ||||
|       throw new Error(`${this._options.url ?? `http://localhost${port ? ":" + port : ""}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`); | ||||
|     } | ||||
|     debugWebServer(`Starting WebServer process ${this._options.command}...`); | ||||
|     const { launchedProcess, gracefullyClose } = await (0, import_utils.launchProcess)({ | ||||
|       command: this._options.command, | ||||
|       env: { | ||||
|         ...DEFAULT_ENVIRONMENT_VARIABLES, | ||||
|         ...process.env, | ||||
|         ...this._options.env | ||||
|       }, | ||||
|       cwd: this._options.cwd, | ||||
|       stdio: "stdin", | ||||
|       shell: true, | ||||
|       attemptToGracefullyClose: async () => { | ||||
|         if (process.platform === "win32") | ||||
|           throw new Error("Graceful shutdown is not supported on Windows"); | ||||
|         if (!this._options.gracefulShutdown) | ||||
|           throw new Error("skip graceful shutdown"); | ||||
|         const { signal, timeout = 0 } = this._options.gracefulShutdown; | ||||
|         process.kill(-launchedProcess.pid, signal); | ||||
|         return new Promise((resolve, reject) => { | ||||
|           const timer = timeout !== 0 ? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout`)), timeout) : void 0; | ||||
|           launchedProcess.once("close", (...args) => { | ||||
|             clearTimeout(timer); | ||||
|             resolve(); | ||||
|           }); | ||||
|         }); | ||||
|       }, | ||||
|       log: () => { | ||||
|       }, | ||||
|       onExit: (code) => processExitedReject(new Error(code ? `Process from config.webServer was not able to start. Exit code: ${code}` : "Process from config.webServer exited early.")), | ||||
|       tempDirectories: [] | ||||
|     }); | ||||
|     this._killProcess = gracefullyClose; | ||||
|     debugWebServer(`Process started`); | ||||
|     launchedProcess.stderr.on("data", (data) => { | ||||
|       if (debugWebServer.enabled || (this._options.stderr === "pipe" || !this._options.stderr)) | ||||
|         this._reporter.onStdErr?.(prefixOutputLines(data.toString(), this._options.name)); | ||||
|     }); | ||||
|     launchedProcess.stdout.on("data", (data) => { | ||||
|       if (debugWebServer.enabled || this._options.stdout === "pipe") | ||||
|         this._reporter.onStdOut?.(prefixOutputLines(data.toString(), this._options.name)); | ||||
|     }); | ||||
|   } | ||||
|   async _waitForProcess() { | ||||
|     if (!this._isAvailableCallback) { | ||||
|       this._processExitedPromise.catch(() => { | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|     debugWebServer(`Waiting for availability...`); | ||||
|     const launchTimeout = this._options.timeout || 60 * 1e3; | ||||
|     const cancellationToken = { canceled: false }; | ||||
|     const { timedOut } = await Promise.race([ | ||||
|       (0, import_utils.raceAgainstDeadline)(() => waitFor(this._isAvailableCallback, cancellationToken), (0, import_utils.monotonicTime)() + launchTimeout), | ||||
|       this._processExitedPromise | ||||
|     ]); | ||||
|     cancellationToken.canceled = true; | ||||
|     if (timedOut) | ||||
|       throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`); | ||||
|     debugWebServer(`WebServer available`); | ||||
|   } | ||||
| } | ||||
| async function isPortUsed(port) { | ||||
|   const innerIsPortUsed = (host) => new Promise((resolve) => { | ||||
|     const conn = import_net.default.connect(port, host).on("error", () => { | ||||
|       resolve(false); | ||||
|     }).on("connect", () => { | ||||
|       conn.end(); | ||||
|       resolve(true); | ||||
|     }); | ||||
|   }); | ||||
|   return await innerIsPortUsed("127.0.0.1") || await innerIsPortUsed("::1"); | ||||
| } | ||||
| async function waitFor(waitFn, cancellationToken) { | ||||
|   const logScale = [100, 250, 500]; | ||||
|   while (!cancellationToken.canceled) { | ||||
|     const connected = await waitFn(); | ||||
|     if (connected) | ||||
|       return; | ||||
|     const delay = logScale.shift() || 1e3; | ||||
|     debugWebServer(`Waiting ${delay}ms`); | ||||
|     await new Promise((x) => setTimeout(x, delay)); | ||||
|   } | ||||
| } | ||||
| function getIsAvailableFunction(url, checkPortOnly, ignoreHTTPSErrors, onStdErr) { | ||||
|   const urlObject = new URL(url); | ||||
|   if (!checkPortOnly) | ||||
|     return () => (0, import_utils.isURLAvailable)(urlObject, ignoreHTTPSErrors, debugWebServer, onStdErr); | ||||
|   const port = urlObject.port; | ||||
|   return () => isPortUsed(+port); | ||||
| } | ||||
| const webServer = (options) => { | ||||
|   return new WebServerPlugin(options, false); | ||||
| }; | ||||
| const webServerPluginsForConfig = (config) => { | ||||
|   const shouldSetBaseUrl = !!config.config.webServer; | ||||
|   const webServerPlugins = []; | ||||
|   for (const webServerConfig of config.webServers) { | ||||
|     if (webServerConfig.port && webServerConfig.url) | ||||
|       throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`); | ||||
|     let url; | ||||
|     if (webServerConfig.port || webServerConfig.url) { | ||||
|       url = webServerConfig.url || `http://localhost:${webServerConfig.port}`; | ||||
|       if (shouldSetBaseUrl && !webServerConfig.url) | ||||
|         process.env.PLAYWRIGHT_TEST_BASE_URL = url; | ||||
|     } | ||||
|     webServerPlugins.push(new WebServerPlugin({ ...webServerConfig, url }, webServerConfig.port !== void 0)); | ||||
|   } | ||||
|   return webServerPlugins; | ||||
| }; | ||||
| function prefixOutputLines(output, prefixName = "WebServer") { | ||||
|   const lastIsNewLine = output[output.length - 1] === "\n"; | ||||
|   let lines = output.split("\n"); | ||||
|   if (lastIsNewLine) | ||||
|     lines.pop(); | ||||
|   lines = lines.map((line) => import_utils2.colors.dim(`[${prefixName}] `) + line); | ||||
|   if (lastIsNewLine) | ||||
|     lines.push(""); | ||||
|   return lines.join("\n"); | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   WebServerPlugin, | ||||
|   webServer, | ||||
|   webServerPluginsForConfig | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins