@ckeditor/ckeditor5-dev-manual-server 57.0.0-alpha.0 → 57.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,14 +1,6 @@
1
- import { createReadStream, existsSync, globSync, readFileSync, realpathSync, statSync } from "node:fs";
1
+ import { createReadStream, globSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
- import path, { extname, isAbsolute, posix, relative, resolve } from "node:path";
4
- import { parse, serialize } from "parse5";
5
- import { appendChild, createElement, getAttribute, getTextContent, hasAttribute, isElementNode, query, removeAttribute, removeNode, setAttribute, setTextContent } from "@parse5/tools";
6
- import { unified } from "unified";
7
- import remarkGfm from "remark-gfm";
8
- import remarkParse from "remark-parse";
9
- import remarkRehype from "remark-rehype";
10
- import { toHtml } from "hast-util-to-html";
11
- import { readFile } from "node:fs/promises";
3
+ import { basename, extname, isAbsolute, posix, relative, resolve } from "node:path";
12
4
  //#region src/utils.ts
13
5
  /**
14
6
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
@@ -35,6 +27,20 @@ function cacheValue(compute) {
35
27
  function stringifyValues(obj) {
36
28
  return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, JSON.stringify(value)]));
37
29
  }
30
+ function normalizePackageName(packageName) {
31
+ return packageName.replace(/^ckeditor5-/, "");
32
+ }
33
+ /**
34
+ * Builds a predicate that decides whether a package is part of the `include` selection. When the
35
+ * selection is empty every package is included. Package names are matched by their normalized form,
36
+ * so both short (`case-change`) and full (`ckeditor5-case-change`) names work. A `null` package name
37
+ * (an asset without a package segment) is only kept when the selection is empty.
38
+ */
39
+ function createPackageNameFilter(includePackageNames) {
40
+ if (includePackageNames.length == 0) return () => true;
41
+ const normalizedIncludePackageNames = new Set(includePackageNames.map(normalizePackageName));
42
+ return (packageName) => packageName != null && normalizedIncludePackageNames.has(normalizePackageName(packageName));
43
+ }
38
44
  function toPublicFilePath(filePath, workspaceRoot) {
39
45
  const relativeFilePath = relative(workspaceRoot, filePath);
40
46
  if (!relativeFilePath.startsWith("..") && !isAbsolute(relativeFilePath)) return toPublicSpecifier(relativeFilePath);
@@ -55,63 +61,246 @@ function stripLeadingSlash(value) {
55
61
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
56
62
  * For licensing, see LICENSE.md.
57
63
  */
64
+ const MANUAL_TESTS_DIRECTORY = "tests/manual/";
65
+ const MANUAL_TEST_SUFFIX$1 = ".manual.html";
66
+ /**
67
+ * Discovers manual test pages. A manual test is a single full HTML document named
68
+ * `<slug>.manual.html` located under a package's `tests/manual/` directory. Plain `.html`
69
+ * files are treated as static fixtures and are never collected here.
70
+ *
71
+ * The `patterns` are package root globs, for example `packages/*`.
72
+ */
58
73
  function collectManualPages(patterns, workspaceRoot) {
59
- const groupedFiles = /* @__PURE__ */ new Map();
60
- const manualPages = [];
61
- for (const relativeFilePath of matchFiles(patterns, workspaceRoot)) {
62
- if (relativeFilePath.includes("/_utils/")) continue;
63
- const parsedPath = parseManualTestAssetPath(relativeFilePath);
64
- if (!parsedPath) continue;
65
- const entryKey = `${parsedPath.packageRootPath}/${parsedPath.packageName}/${parsedPath.slug}`;
66
- const matchedFiles = groupedFiles.get(entryKey) || {};
67
- matchedFiles[parsedPath.extension] = relativeFilePath;
68
- groupedFiles.set(entryKey, matchedFiles);
69
- }
70
- for (const matchedFiles of groupedFiles.values()) {
71
- if (!matchedFiles.html) continue;
72
- const scriptFilePath = matchedFiles.ts || matchedFiles.js;
73
- if (!scriptFilePath) continue;
74
- const parsedPath = parseManualTestAssetPath(scriptFilePath);
75
- manualPages.push({
76
- displayName: humanizeSlug(parsedPath.slug),
77
- htmlFilePath: toPublicSpecifier(matchedFiles.html),
78
- instructionsFilePath: matchedFiles.md ? toPublicSpecifier(matchedFiles.md) : void 0,
79
- packageName: parsedPath.packageName,
80
- scriptFilePath: toPublicSpecifier(scriptFilePath),
81
- slug: parsedPath.slug
82
- });
83
- }
84
- manualPages.sort((a, b) => a.packageName.localeCompare(b.packageName) || a.slug.localeCompare(b.slug));
85
- return new Map(manualPages.map((entry) => [entry.htmlFilePath, entry]));
74
+ const manualPages = matchManualPageFiles(patterns, workspaceRoot).map((relativeFilePath) => toManualPageEntry(relativeFilePath, workspaceRoot)).toSorted((a, b) => a.packageName.localeCompare(b.packageName) || a.slug.localeCompare(b.slug)).map((entry) => [entry.htmlFilePath, entry]);
75
+ return new Map(manualPages);
76
+ }
77
+ function matchManualPageFiles(patterns, workspaceRoot) {
78
+ return patterns.map((pattern) => `${pattern.replace(/\/+$/, "")}/${MANUAL_TESTS_DIRECTORY}**/*${MANUAL_TEST_SUFFIX$1}`).flatMap((pattern) => globSync(pattern, { cwd: workspaceRoot }).map((match) => toPosixPath(match)));
79
+ }
80
+ function toManualPageEntry(relativeFilePath, workspaceRoot) {
81
+ const directoryStartIndex = relativeFilePath.startsWith(MANUAL_TESTS_DIRECTORY) ? 0 : relativeFilePath.indexOf(`/${MANUAL_TESTS_DIRECTORY}`) + 1;
82
+ const packagePath = relativeFilePath.slice(0, Math.max(directoryStartIndex - 1, 0));
83
+ const slugPath = relativeFilePath.slice(directoryStartIndex + 13);
84
+ return {
85
+ htmlFilePath: toPublicSpecifier(relativeFilePath),
86
+ packageName: packagePath ? packagePath.slice(packagePath.lastIndexOf("/") + 1) : basename(workspaceRoot),
87
+ slug: slugPath.slice(0, -12)
88
+ };
89
+ }
90
+ //#endregion
91
+ //#region src/manual-test-plugin/plugin.ts
92
+ /**
93
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
94
+ * For licensing, see LICENSE.md.
95
+ */
96
+ const MANUAL_HEADER_ELEMENT = "ck-manual-header";
97
+ const MANUAL_ENTRIES_VIRTUAL_ID = "virtual:ckeditor5-manual-entries";
98
+ const MANUAL_THEME_ROOT = realpathSync(fileURLToPath(import.meta.resolve("@ckeditor/ckeditor5-dev-manual-server/theme")));
99
+ const MANUAL_CATALOG_FILE_PATH = resolve(MANUAL_THEME_ROOT, "catalog.html");
100
+ const MANUAL_CATALOG_SCRIPT_FILE_PATH = resolve(MANUAL_THEME_ROOT, "catalog.ts");
101
+ const MANUAL_BOOTSTRAP_SCRIPT_FILE_PATH = resolve(MANUAL_THEME_ROOT, "manual-bootstrap.ts");
102
+ const MANUAL_HEADER_SCRIPT_FILE_PATH = resolve(MANUAL_THEME_ROOT, "manual-header.ts");
103
+ function manualTestsPlugin(options) {
104
+ let workspaceRoot = process.cwd();
105
+ let base = "/";
106
+ const includePackageNames = (options.include || []).filter(Boolean);
107
+ const manualPagesCache = cacheValue(() => filterManualPages(collectManualPages(options.paths, workspaceRoot), includePackageNames));
108
+ const getManualPages = manualPagesCache.get;
109
+ const getManualPageEntryForFile = (filePath) => {
110
+ return getManualPages().get(toPublicSpecifier(relative(workspaceRoot, filePath)));
111
+ };
112
+ const getManualCatalogBuildInputFilePath = () => resolve(workspaceRoot, "index.html");
113
+ const getManualCatalogPublicPath = () => toPublicFilePath(getManualCatalogBuildInputFilePath(), workspaceRoot);
114
+ const getManualCatalogScriptPublicPath = () => toPublicFilePath(MANUAL_CATALOG_SCRIPT_FILE_PATH, workspaceRoot);
115
+ const getManualBootstrapScriptPublicPath = () => toPublicFilePath(MANUAL_BOOTSTRAP_SCRIPT_FILE_PATH, workspaceRoot);
116
+ const getManualHeaderScriptPublicPath = () => toPublicFilePath(MANUAL_HEADER_SCRIPT_FILE_PATH, workspaceRoot);
117
+ const getManualBuildInputs = () => [getManualCatalogBuildInputFilePath(), ...[...getManualPages().values()].map((entry) => resolve(workspaceRoot, stripLeadingSlash(entry.htmlFilePath)))];
118
+ const resolvedVirtualModuleId = `\0${MANUAL_ENTRIES_VIRTUAL_ID}`;
119
+ const getClientEntries = () => [...getManualPages().values()].map((entry) => ({
120
+ href: toBasePublicPath(entry.htmlFilePath, base),
121
+ packageName: entry.packageName,
122
+ slug: entry.slug
123
+ }));
124
+ const getManualEntriesJson = () => JSON.stringify(getClientEntries(), null, 2);
125
+ return {
126
+ name: "ckeditor5-manual-tests",
127
+ config() {
128
+ return { build: { rolldownOptions: { input: getManualBuildInputs() } } };
129
+ },
130
+ configResolved(config) {
131
+ workspaceRoot = config.root;
132
+ base = config.base || "/";
133
+ manualPagesCache.invalidate();
134
+ config.build.rolldownOptions.input = getManualBuildInputs();
135
+ },
136
+ configureServer(server) {
137
+ server.middlewares.use((request, _response, next) => {
138
+ rewriteCatalogRequest(request, getManualCatalogPublicPath());
139
+ next();
140
+ });
141
+ const clientEnvironment = server.environments.client;
142
+ keepManualHtmlSourceFresh(clientEnvironment.bundledDev?.memoryFiles, getManualPages, workspaceRoot);
143
+ },
144
+ resolveId(source) {
145
+ if (source == MANUAL_ENTRIES_VIRTUAL_ID) return resolvedVirtualModuleId;
146
+ if (isManualCatalogBuildInputSpecifier(source, getManualCatalogBuildInputFilePath(), workspaceRoot)) return getManualCatalogBuildInputFilePath();
147
+ return null;
148
+ },
149
+ load(id) {
150
+ if (id == resolvedVirtualModuleId) return `export const manualTestEntries = ${getManualEntriesJson()};`;
151
+ if (toPosixPath(id) == toPosixPath(getManualCatalogBuildInputFilePath())) return readFileSync(MANUAL_CATALOG_FILE_PATH, "utf8");
152
+ return null;
153
+ },
154
+ transformIndexHtml: {
155
+ order: "pre",
156
+ handler(html, context) {
157
+ if (isManualCatalogHtmlFile(context.filename, getManualCatalogBuildInputFilePath())) return html.replace("./catalog.ts", getManualCatalogScriptPublicPath());
158
+ const entry = getManualPageEntryForFile(context.filename);
159
+ if (!entry) return;
160
+ const tags = [createModuleScriptTag(getManualBootstrapScriptPublicPath(), "head-prepend")];
161
+ if (html.includes(`<${MANUAL_HEADER_ELEMENT}`)) tags.push(...createManualHeaderTags(entry, getManualHeaderScriptPublicPath(), toBaseCatalogPath(base, entry.htmlFilePath)));
162
+ return {
163
+ html,
164
+ tags
165
+ };
166
+ }
167
+ }
168
+ };
169
+ }
170
+ /**
171
+ * Keeps the served manual test HTML in sync with the source file while the dev server runs.
172
+ *
173
+ * Under `experimental.bundledDev`, Vite serves each manual test's HTML from an in-memory bundle
174
+ * produced by the rolldown dev engine. That engine emits the HTML output only during the initial
175
+ * build and never regenerates it when the source `.html` changes: its bundle state reports no
176
+ * stale output for HTML entries, `devEngine.invalidate()` throws on a non-JS module, and even a
177
+ * forced full build leaves the HTML memory file untouched (verified against Vite 8.1.0). A `.html`
178
+ * edit still triggers a full page reload, so without this the browser reloads into the same stale
179
+ * HTML until the server is restarted.
180
+ *
181
+ * The built-in-memory HTML rewrites the entry script, injects the asset tags and manual chrome,
182
+ * and copies the source `<head>` — all inside `<head>`. The markup after `</head>` is a verbatim
183
+ * copy of the source. So on every request we keep the freshly built `<head>` and splice in the
184
+ * current post-`</head>` markup read from disk. Splicing unconditionally (rather than only after a
185
+ * detected change) also covers sources that changed between the initial build and the first request
186
+ * for the page, and it is idempotent for unchanged sources. This reflects edits to the test body
187
+ * without re-running Vite's HTML transform or parsing the document. Any edit that would change
188
+ * what belongs in `<head>` is not picked up this way and still needs a server restart, because
189
+ * reproducing it would require re-running the build pipeline the dev engine refuses to run for HTML
190
+ * entries. That covers edits confined to the `<head>` (styles, meta, extra scripts) as well as
191
+ * adding or removing `<ck-manual-header>` in the body: toggling it changes the injected header
192
+ * chrome (`<meta>` and the component script) in `<head>`, so the chrome only updates after a
193
+ * restart even though the body splice already reflects the element itself.
194
+ *
195
+ * When `bundledDev` is not enabled the store is absent and this is a no-op: Vite's normal dev
196
+ * pipeline already serves fresh HTML on every request.
197
+ */
198
+ function keepManualHtmlSourceFresh(memoryFiles, getManualPages, workspaceRoot) {
199
+ if (!memoryFiles) return;
200
+ const getBundledFile = memoryFiles.get.bind(memoryFiles);
201
+ memoryFiles.get = (filePath) => {
202
+ const file = getBundledFile(filePath);
203
+ if (!file || !getManualPages().has(toPublicSpecifier(filePath))) return file;
204
+ try {
205
+ const sourceFilePath = resolve(workspaceRoot, stripLeadingSlash(filePath));
206
+ const freshHtml = composeFreshManualHtml(file.source, readFileSync(sourceFilePath, "utf8"));
207
+ return freshHtml == null ? file : { source: freshHtml };
208
+ } catch {
209
+ return file;
210
+ }
211
+ };
212
+ }
213
+ /**
214
+ * Combines the freshly built `<head>` (asset tags and injected manual chrome) with the current
215
+ * post-`</head>` markup from the source file. Returns `null` when either document is missing a
216
+ * `</head>`, so the caller can fall back to the built output.
217
+ */
218
+ function composeFreshManualHtml(builtHtml, sourceHtml) {
219
+ const builtHeadEnd = findHeadCloseTagIndex(builtHtml);
220
+ const sourceHeadEnd = findHeadCloseTagIndex(sourceHtml);
221
+ if (builtHeadEnd == -1 || sourceHeadEnd == -1) return null;
222
+ return builtHtml.slice(0, builtHeadEnd + 7) + sourceHtml.slice(sourceHeadEnd + 7);
223
+ }
224
+ /**
225
+ * Finds the index of the `</head>` closing tag, or `-1` when the document has none. A `</head>`
226
+ * literal may also appear earlier inside a `<head>` comment or an inline script string, so of all
227
+ * the occurrences preceding the `<body>` tag the last one is taken. Without a `<body>` tag (or
228
+ * when every occurrence follows it) the first occurrence wins, matching the pre-heuristic
229
+ * behavior for such malformed documents.
230
+ */
231
+ function findHeadCloseTagIndex(html) {
232
+ const firstHeadCloseIndex = /<\/head>/i.exec(html)?.index ?? -1;
233
+ const bodyMatch = /<body[\s>]/i.exec(html);
234
+ if (!bodyMatch) return firstHeadCloseIndex;
235
+ const headClosePattern = /<\/head>/gi;
236
+ let lastHeadCloseIndex = -1;
237
+ let match;
238
+ while ((match = headClosePattern.exec(html)) && match.index < bodyMatch.index) lastHeadCloseIndex = match.index;
239
+ return lastHeadCloseIndex == -1 ? firstHeadCloseIndex : lastHeadCloseIndex;
240
+ }
241
+ /**
242
+ * Injection contract for the `<ck-manual-header>` component, added to `<head>` only when the
243
+ * page source contains the element:
244
+ * - a `<meta>` carrying the package name and the base-aware catalog href the component reads;
245
+ * - the module `<script>` that defines the custom element (folded into the page bundle under
246
+ * `bundledDev`, which is fine — it still executes).
247
+ */
248
+ function createManualHeaderTags(entry, headerScriptPublicPath, catalogHref) {
249
+ return [{
250
+ tag: "meta",
251
+ attrs: {
252
+ "name": MANUAL_HEADER_ELEMENT,
253
+ "data-package-name": entry.packageName,
254
+ "data-catalog-href": catalogHref
255
+ },
256
+ injectTo: "head"
257
+ }, createModuleScriptTag(headerScriptPublicPath)];
86
258
  }
87
- function parseManualTestAssetPath(filePath) {
88
- const match = stripLeadingSlash(toPosixPath(filePath)).match(/^(?:(.*?)\/)?([^/]+)\/tests\/manual\/(.+)\.(html|js|md|ts)$/);
89
- if (!match) return null;
259
+ function createModuleScriptTag(src, injectTo = "head") {
90
260
  return {
91
- extension: match[4],
92
- packageName: match[2],
93
- packageRootPath: match[1] || "",
94
- slug: match[3]
261
+ tag: "script",
262
+ attrs: {
263
+ type: "module",
264
+ src
265
+ },
266
+ injectTo
95
267
  };
96
268
  }
97
- function humanizeSlug(slug) {
98
- return slug.split("/").map((pathPart) => pathPart.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ")).join(" / ");
269
+ function filterManualPages(manualPages, includePackageNames) {
270
+ const isIncluded = createPackageNameFilter(includePackageNames);
271
+ return new Map([...manualPages].filter(([, entry]) => isIncluded(entry.packageName)));
272
+ }
273
+ function isManualCatalogHtmlFile(filePath, catalogBuildInputFilePath) {
274
+ const normalizedFilePath = toPosixPath(filePath);
275
+ return normalizedFilePath == toPosixPath(MANUAL_CATALOG_FILE_PATH) || normalizedFilePath == toPosixPath(catalogBuildInputFilePath);
276
+ }
277
+ function isManualCatalogBuildInputSpecifier(source, catalogBuildInputFilePath, workspaceRoot) {
278
+ return toPosixPath(resolve(workspaceRoot, source)) == toPosixPath(catalogBuildInputFilePath);
279
+ }
280
+ function toBasePublicPath(publicPath, base) {
281
+ if (base == "" || base == "./") return `.${publicPath}`;
282
+ return `${base.replace(/\/$/, "")}${publicPath}`;
283
+ }
284
+ function toBaseCatalogPath(base, entryHtmlFilePath) {
285
+ if (base == "" || base == "./") return posix.relative(posix.dirname(stripLeadingSlash(entryHtmlFilePath)), "index.html");
286
+ return base;
287
+ }
288
+ function rewriteCatalogRequest(request, manualCatalogPublicPath) {
289
+ const url = parseRequestUrl(request.url);
290
+ if (!url) return;
291
+ if (url.pathname == "/" || url.pathname == "/index.html") request.url = `${manualCatalogPublicPath}${url.search}`;
99
292
  }
100
- function matchFiles(patterns, workspaceRoot) {
101
- return patterns.flatMap((pattern) => globSync(pattern, { cwd: workspaceRoot }).map((match) => toPosixPath(match)));
293
+ function parseRequestUrl(requestUrl) {
294
+ return URL.parse(requestUrl || "", "http://localhost");
102
295
  }
103
296
  //#endregion
104
- //#region src/manual-test-plugin/static-assets.ts
297
+ //#region src/static-assets-plugin/static-assets.ts
105
298
  /**
106
299
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
107
300
  * For licensing, see LICENSE.md.
108
301
  */
109
- const PROCESSED_MANUAL_TEST_EXTENSIONS = /* @__PURE__ */ new Set([
110
- ".html",
111
- ".js",
112
- ".md",
113
- ".ts"
114
- ]);
302
+ const PROCESSED_MANUAL_TEST_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts"]);
303
+ const MANUAL_TEST_SUFFIX = ".manual.html";
115
304
  const VITE_MODULE_QUERY_PARAMETERS = /* @__PURE__ */ new Set([
116
305
  "import",
117
306
  "raw",
@@ -120,7 +309,7 @@ const VITE_MODULE_QUERY_PARAMETERS = /* @__PURE__ */ new Set([
120
309
  "inline"
121
310
  ]);
122
311
  function collectManualStaticAssets(patterns, workspaceRoot) {
123
- return new Map(patterns.flatMap((pattern) => globSync(pattern, { cwd: workspaceRoot })).sort().filter((relativeFilePath) => statSync(resolve(workspaceRoot, relativeFilePath), { throwIfNoEntry: false })?.isFile()).filter(isManualStaticAssetPath).map((relativeFilePath) => [toPublicSpecifier(relativeFilePath), resolve(workspaceRoot, relativeFilePath)]));
312
+ return new Map(patterns.map((pattern) => `${pattern.replace(/\/+$/, "")}/tests/manual/**/*`).flatMap((pattern) => globSync(pattern, { cwd: workspaceRoot })).sort().filter((relativeFilePath) => statSync(resolve(workspaceRoot, relativeFilePath), { throwIfNoEntry: false })?.isFile()).filter(isManualStaticAssetPath).map((relativeFilePath) => [toPublicSpecifier(relativeFilePath), resolve(workspaceRoot, relativeFilePath)]));
124
313
  }
125
314
  function createManualStaticAssetsMiddleware(collectStaticAssets) {
126
315
  return (request, response, next) => {
@@ -163,6 +352,7 @@ function decodePathname(pathname) {
163
352
  }
164
353
  }
165
354
  function isManualStaticAssetPath(filePath) {
355
+ if (filePath.endsWith(MANUAL_TEST_SUFFIX)) return false;
166
356
  return extname(filePath) != "" && !PROCESSED_MANUAL_TEST_EXTENSIONS.has(extname(filePath));
167
357
  }
168
358
  function getContentType(extension) {
@@ -170,6 +360,8 @@ function getContentType(extension) {
170
360
  case ".avif": return "image/avif";
171
361
  case ".css": return "text/css; charset=utf-8";
172
362
  case ".gif": return "image/gif";
363
+ case ".htm":
364
+ case ".html": return "text/html; charset=utf-8";
173
365
  case ".ico": return "image/x-icon";
174
366
  case ".jpg":
175
367
  case ".jpeg": return "image/jpeg";
@@ -187,187 +379,22 @@ function getContentType(extension) {
187
379
  }
188
380
  }
189
381
  //#endregion
190
- //#region src/manual-test-plugin/parse-markdown.ts
191
- /**
192
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
193
- * For licensing, see LICENSE.md.
194
- */
195
- const markdownProcessor = unified().use(remarkParse).use(remarkGfm).use(remarkRehype, { allowDangerousHtml: true });
196
- function loadRenderedInstructions(entry, workspaceRoot) {
197
- if (!entry.instructionsFilePath) return "";
198
- const markdown = readFileSync(resolve(workspaceRoot, entry.instructionsFilePath.slice(1)), "utf8").trim();
199
- if (!markdown) return "";
200
- return toHtml(markdownProcessor.runSync(markdownProcessor.parse(markdown)), { allowDangerousHtml: true });
201
- }
202
- //#endregion
203
- //#region src/manual-test-plugin/shell-html.ts
204
- /**
205
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
206
- * For licensing, see LICENSE.md.
207
- */
208
- function createManualShellHtml({ catalogPublicPath = "/", entry, html, shellScriptPublicPath, shellTemplateFilePath, workspaceRoot }) {
209
- const shellDocument = parse(readFileSync(shellTemplateFilePath, "utf8").replace("href=\"/\" title=\"Back to test index\"", `href="${catalogPublicPath}" title="Back to test index"`));
210
- const shellHead = getRequiredElementByTagName$1(shellDocument, "head");
211
- const shellBody = getRequiredElementByTagName$1(shellDocument, "body");
212
- const shellTitle = getRequiredElementByTagName$1(shellHead, "title");
213
- const shellDataElement = query(shellBody, (candidate) => {
214
- return isElementNode(candidate) && getAttribute(candidate, "id") == "manual-shell-data";
215
- });
216
- const shellScriptElement = getRequiredElementByAttribute(shellHead, "data-manual-shell-script");
217
- const manualDocument = parse(html);
218
- const manualHead = getRequiredElementByTagName$1(manualDocument, "head");
219
- const manualBody = getRequiredElementByTagName$1(manualDocument, "body");
220
- const manualTitle = findChildElement(manualHead, "title");
221
- const testScriptElement = getRequiredElementByAttribute(shellHead, "data-manual-test-script");
222
- const testScriptFileName = posix.basename(entry.scriptFilePath);
223
- removeManualTestScriptNodes(manualDocument, testScriptFileName);
224
- setTextContent(shellTitle, (manualTitle ? getTextContent(manualTitle) : "").trim() || entry.displayName);
225
- setAttribute(shellScriptElement, "src", shellScriptPublicPath);
226
- removeAttribute(shellScriptElement, "data-manual-shell-script");
227
- setAttribute(testScriptElement, "src", `./${testScriptFileName}`);
228
- removeAttribute(testScriptElement, "data-manual-test-script");
229
- setTextContent(shellDataElement, stringifyHtmlScriptData(createManualData(entry, workspaceRoot)));
230
- copyManualBodyAttributes(manualBody, shellBody);
231
- appendChildren(shellHead, manualHead.childNodes.filter(shouldMoveManualHeadNode));
232
- appendChild(shellBody, createManualTestContainer(manualBody));
233
- return serialize(shellDocument);
234
- }
235
- function createManualData(entry, workspaceRoot) {
236
- return {
237
- displayName: entry.displayName,
238
- instructionsHtml: loadRenderedInstructions(entry, workspaceRoot),
239
- packageName: entry.packageName
240
- };
241
- }
242
- function stringifyHtmlScriptData(value) {
243
- return JSON.stringify(value).split("<").join("\\u003c");
244
- }
245
- function copyManualBodyAttributes(manualBody, shellBody) {
246
- for (const { name, value } of manualBody.attrs) {
247
- if (name == "class") {
248
- setAttribute(shellBody, "class", [getAttribute(shellBody, "class"), value].filter(Boolean).join(" "));
249
- continue;
250
- }
251
- if (!hasAttribute(shellBody, name)) setAttribute(shellBody, name, value);
252
- }
253
- }
254
- function shouldMoveManualHeadNode(node) {
255
- if (!isElementNode(node)) return true;
256
- if (node.tagName == "title") return false;
257
- if (node.tagName != "meta") return true;
258
- return !getAttribute(node, "charset") && getAttribute(node, "name") != "viewport";
259
- }
260
- function appendChildren(parent, childNodes) {
261
- for (const node of childNodes) {
262
- removeNode(node);
263
- appendChild(parent, node);
264
- }
265
- }
266
- function createManualTestContainer(manualBody) {
267
- const container = createElement("div", { class: "manual-test-container" });
268
- appendChildren(container, [...manualBody.childNodes]);
269
- return container;
270
- }
271
- function removeManualTestScriptNodes(parent, testScriptFileName) {
272
- for (const node of [...parent.childNodes]) {
273
- if (!isElementNode(node)) continue;
274
- if (isManualTestScriptNode(node, testScriptFileName)) {
275
- removeNode(node);
276
- continue;
277
- }
278
- removeManualTestScriptNodes(node, testScriptFileName);
279
- }
280
- }
281
- function isManualTestScriptNode(node, testScriptFileName) {
282
- if (node.tagName != "script") return false;
283
- const source = getAttribute(node, "src");
284
- if (!source) return false;
285
- if (isExternalScriptSource$1(source)) return false;
286
- return getScriptModuleName(posix.basename(source.split("?")[0].split("#")[0])) == getScriptModuleName(testScriptFileName);
287
- }
288
- function isExternalScriptSource$1(source) {
289
- return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("//");
290
- }
291
- function getScriptModuleName(fileName) {
292
- return fileName.replace(/\.[jt]s$/, "");
293
- }
294
- function getRequiredElementByTagName$1(root, tagName) {
295
- return query(root, (candidate) => isElementNode(candidate) && candidate.tagName == tagName);
296
- }
297
- function getRequiredElementByAttribute(root, attributeName) {
298
- return query(root, (candidate) => isElementNode(candidate) && hasAttribute(candidate, attributeName));
299
- }
300
- function findChildElement(root, tagName) {
301
- return root.childNodes.find((node) => isElementNode(node) && node.tagName == tagName);
302
- }
303
- //#endregion
304
- //#region src/manual-test-plugin/plugin.ts
382
+ //#region src/static-assets-plugin/plugin.ts
305
383
  /**
306
384
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
307
385
  * For licensing, see LICENSE.md.
308
386
  */
309
- const MANUAL_ENTRIES_VIRTUAL_ID = "virtual:ckeditor5-manual-entries";
310
- const MANUAL_THEME_ROOT = realpathSync(fileURLToPath(import.meta.resolve("@ckeditor/ckeditor5-dev-manual-server/theme")));
311
- const MANUAL_CATALOG_FILE_PATH = resolve(MANUAL_THEME_ROOT, "catalog.html");
312
- const MANUAL_CATALOG_SCRIPT_FILE_PATH = resolve(MANUAL_THEME_ROOT, "catalog.ts");
313
- const MANUAL_SHELL_TEMPLATE_FILE_PATH = resolve(MANUAL_THEME_ROOT, "shell.html");
314
- const MANUAL_SHELL_SCRIPT_FILE_PATH = resolve(MANUAL_THEME_ROOT, "shell.ts");
315
- function manualTestsPlugin(options) {
387
+ function manualStaticAssetsPlugin(options) {
316
388
  let workspaceRoot = process.cwd();
317
- let base = "/";
318
- const manualTestPatterns = options.paths;
319
- const manualPagePatterns = manualTestPatterns.map(toManualPagePattern);
320
389
  const includePackageNames = (options.include || []).filter(Boolean);
321
- const manualPagesCache = cacheValue(() => filterManualPages(collectManualPages(manualPagePatterns, workspaceRoot), includePackageNames));
322
- const getManualPages = manualPagesCache.get;
323
- const manualStaticAssetsCache = cacheValue(() => collectManualStaticAssets(getManualStaticAssetPatterns(getManualPages(), manualTestPatterns, includePackageNames), workspaceRoot));
324
- const getManualStaticAssets = manualStaticAssetsCache.get;
325
- const invalidateManualFileCaches = () => {
326
- manualPagesCache.invalidate();
327
- manualStaticAssetsCache.invalidate();
328
- };
329
- const getManualPageEntryForFile = (filePath) => {
330
- return getManualPages().get(toPublicSpecifier(relative(workspaceRoot, filePath)));
331
- };
332
- const getManualCatalogBuildInputFilePath = () => resolve(workspaceRoot, "index.html");
333
- const getManualCatalogPublicPath = () => toPublicFilePath(getManualCatalogBuildInputFilePath(), workspaceRoot);
334
- const getManualCatalogScriptPublicPath = () => toPublicFilePath(MANUAL_CATALOG_SCRIPT_FILE_PATH, workspaceRoot);
335
- const getManualShellScriptPublicPath = () => toPublicFilePath(MANUAL_SHELL_SCRIPT_FILE_PATH, workspaceRoot);
336
- const getManualCatalogClientPath = (entry) => toBaseCatalogPath(base, entry.htmlFilePath);
337
- const getManualBuildInputs = () => [getManualCatalogBuildInputFilePath(), ...[...getManualPages().values()].map((entry) => resolve(workspaceRoot, entry.htmlFilePath.slice(1)))];
338
- const resolvedVirtualModuleId = `\0${MANUAL_ENTRIES_VIRTUAL_ID}`;
339
- const getClientEntries = () => [...getManualPages().values()].map((entry) => ({
340
- displayName: entry.displayName,
341
- href: toBasePublicPath(entry.htmlFilePath, base),
342
- packageName: entry.packageName,
343
- slug: entry.slug
344
- }));
345
- const getManualEntriesJson = () => JSON.stringify(getClientEntries(), null, 2);
390
+ const getManualStaticAssets = cacheValue(() => filterManualStaticAssets(collectManualStaticAssets(options.paths, workspaceRoot), includePackageNames, workspaceRoot)).get;
346
391
  return {
347
- name: "ckeditor5-manual-tests",
348
- config() {
349
- return { build: { rolldownOptions: { input: getManualBuildInputs() } } };
350
- },
392
+ name: "ckeditor5-manual-static-assets",
351
393
  configResolved(config) {
352
394
  workspaceRoot = config.root;
353
- base = config.base || "/";
354
- invalidateManualFileCaches();
355
- config.build.rolldownOptions.input = getManualBuildInputs();
356
395
  },
357
396
  configureServer(server) {
358
- const { memoryFiles } = server.environments.client;
359
- useManualTestMiddlewares(server, getManualCatalogPublicPath, getManualStaticAssets);
360
- useBundledDevManualHtmlSource(memoryFiles, getManualPages, getManualShellScriptPublicPath, getManualCatalogClientPath, () => workspaceRoot);
361
- },
362
- resolveId(source) {
363
- if (source == MANUAL_ENTRIES_VIRTUAL_ID) return resolvedVirtualModuleId;
364
- if (isManualCatalogBuildInputSpecifier(source, getManualCatalogBuildInputFilePath(), workspaceRoot)) return getManualCatalogBuildInputFilePath();
365
- return null;
366
- },
367
- load(id) {
368
- if (id == resolvedVirtualModuleId) return `export const manualTestEntries = ${getManualEntriesJson()};`;
369
- if (toPosixPath(id) == toPosixPath(getManualCatalogBuildInputFilePath())) return readFileSync(MANUAL_CATALOG_FILE_PATH, "utf8");
370
- return null;
397
+ server.middlewares.use(createManualStaticAssetsMiddleware(getManualStaticAssets));
371
398
  },
372
399
  generateBundle() {
373
400
  for (const [publicPath, filePath] of getManualStaticAssets()) this.emitFile({
@@ -375,143 +402,16 @@ function manualTestsPlugin(options) {
375
402
  fileName: stripLeadingSlash(publicPath),
376
403
  source: readFileSync(filePath)
377
404
  });
378
- },
379
- transformIndexHtml: {
380
- order: "pre",
381
- handler(html, context) {
382
- if (isManualCatalogHtmlFile(context.filename, getManualCatalogBuildInputFilePath())) return html.replace("./catalog.ts", getManualCatalogScriptPublicPath());
383
- const entry = getManualPageEntryForFile(context.filename);
384
- if (!entry) return;
385
- return createManualShellHtml({
386
- catalogPublicPath: getManualCatalogClientPath(entry),
387
- entry,
388
- html,
389
- shellScriptPublicPath: getManualShellScriptPublicPath(),
390
- shellTemplateFilePath: MANUAL_SHELL_TEMPLATE_FILE_PATH,
391
- workspaceRoot
392
- });
393
- }
394
405
  }
395
406
  };
396
407
  }
397
- function useManualTestMiddlewares(server, getManualCatalogPublicPath, getManualStaticAssets) {
398
- server.middlewares.use(createManualStaticAssetsMiddleware(getManualStaticAssets));
399
- useManualCatalogMiddleware(server, getManualCatalogPublicPath);
408
+ function filterManualStaticAssets(staticAssets, includePackageNames, workspaceRoot) {
409
+ const isIncluded = createPackageNameFilter(includePackageNames);
410
+ return new Map([...staticAssets].filter(([publicPath]) => isIncluded(getManualTestPackageName(publicPath, workspaceRoot))));
400
411
  }
401
- function useManualCatalogMiddleware(server, getManualCatalogPublicPath) {
402
- server.middlewares.use((request, _response, next) => {
403
- rewriteCatalogRequest(request, getManualCatalogPublicPath());
404
- next();
405
- });
406
- }
407
- function useBundledDevManualHtmlSource(memoryFiles, getManualPages, getManualShellScriptPublicPath, getManualCatalogClientPath, getWorkspaceRoot) {
408
- if (!memoryFiles) return;
409
- const getBundledFile = memoryFiles.get.bind(memoryFiles);
410
- memoryFiles.get = (filePath) => {
411
- const file = getBundledFile(filePath);
412
- const entry = getManualPages().get(toPublicSpecifier(filePath));
413
- if (!file || !entry) return file;
414
- const workspaceRoot = getWorkspaceRoot();
415
- const shellScriptPublicPath = getManualShellScriptPublicPath();
416
- const bundledHtml = getFileSource(file);
417
- return { source: mergeBundledAssetTags(createManualShellHtml({
418
- catalogPublicPath: getManualCatalogClientPath(entry),
419
- entry,
420
- html: readFileSync(resolve(workspaceRoot, stripLeadingSlash(entry.htmlFilePath)), "utf8"),
421
- shellScriptPublicPath,
422
- shellTemplateFilePath: MANUAL_SHELL_TEMPLATE_FILE_PATH,
423
- workspaceRoot
424
- }), bundledHtml, entry, shellScriptPublicPath) };
425
- };
426
- }
427
- function getFileSource(file) {
428
- return typeof file.source == "string" ? file.source : Buffer.from(file.source).toString("utf8");
429
- }
430
- function toManualPagePattern(pattern) {
431
- if (/\.(?:html|js|md|ts|\{[^}]*\})$/.test(pattern)) return pattern;
432
- return `${pattern}.{html,js,md,ts}`;
433
- }
434
- function filterManualPages(manualPages, includePackageNames) {
435
- if (includePackageNames.length == 0) return manualPages;
436
- const normalizedIncludePackageNames = new Set(includePackageNames.map(normalizePackageName));
437
- return new Map([...manualPages].filter(([, entry]) => normalizedIncludePackageNames.has(normalizePackageName(entry.packageName))));
438
- }
439
- function normalizePackageName(packageName) {
440
- return packageName.replace(/^ckeditor5-/, "");
441
- }
442
- function getManualStaticAssetPatterns(manualPages, manualTestPatterns, includePackageNames) {
443
- if (includePackageNames.length == 0) return manualTestPatterns;
444
- return [...new Set([...manualPages.values()].map(getManualTestRootDirectory))].map((manualTestRootDirectory) => `${manualTestRootDirectory}/**/*`);
445
- }
446
- function getManualTestRootDirectory(entry) {
447
- const htmlFilePath = stripLeadingSlash(entry.htmlFilePath);
448
- const manualDirectoryIndex = htmlFilePath.indexOf("/tests/manual/");
449
- return htmlFilePath.slice(0, manualDirectoryIndex + 14 - 1);
450
- }
451
- function isManualCatalogHtmlFile(filePath, catalogBuildInputFilePath) {
452
- const normalizedFilePath = toPosixPath(filePath);
453
- return normalizedFilePath == toPosixPath(MANUAL_CATALOG_FILE_PATH) || normalizedFilePath == toPosixPath(catalogBuildInputFilePath);
454
- }
455
- function isManualCatalogBuildInputSpecifier(source, catalogBuildInputFilePath, workspaceRoot) {
456
- return toPosixPath(resolve(workspaceRoot, source)) == toPosixPath(catalogBuildInputFilePath);
457
- }
458
- function toBasePublicPath(publicPath, base) {
459
- if (base == "" || base == "./") return `.${publicPath}`;
460
- return `${base.replace(/\/$/, "")}${publicPath}`;
461
- }
462
- function toBaseCatalogPath(base, entryHtmlFilePath) {
463
- if (base == "" || base == "./") return posix.relative(posix.dirname(stripLeadingSlash(entryHtmlFilePath)), "index.html");
464
- return base;
465
- }
466
- function mergeBundledAssetTags(sourceHtml, bundledHtml, entry, shellScriptPublicPath) {
467
- const sourceDocument = parse(sourceHtml);
468
- const bundledDocument = parse(bundledHtml);
469
- const sourceHead = getRequiredElementByTagName(sourceDocument, "head");
470
- const bundledHead = getRequiredElementByTagName(bundledDocument, "head");
471
- const testScriptFileName = posix.basename(entry.scriptFilePath);
472
- const hasBundledShellAsset = bundledHead.childNodes.some((node) => isBundledModuleAsset(node, "shell"));
473
- for (const node of [...sourceHead.childNodes]) if (isSourceTestScript(node, testScriptFileName) || hasBundledShellAsset && isSourceShellScript(node, shellScriptPublicPath)) removeNode(node);
474
- for (const node of bundledHead.childNodes.filter(isBundledAssetTag)) {
475
- removeNode(node);
476
- appendChild(sourceHead, node);
477
- }
478
- return serialize(sourceDocument);
479
- }
480
- function isSourceTestScript(node, testScriptFileName) {
481
- if (!isElementNode(node) || node.tagName != "script") return false;
482
- const source = getAttribute(node, "src");
483
- if (!source) return false;
484
- if (isExternalScriptSource(source)) return false;
485
- return posix.basename(source) == testScriptFileName;
486
- }
487
- function isExternalScriptSource(source) {
488
- return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("//");
489
- }
490
- function isSourceShellScript(node, shellScriptPublicPath) {
491
- return isElementNode(node) && node.tagName == "script" && getAttribute(node, "src") == shellScriptPublicPath;
492
- }
493
- function isBundledModuleAsset(node, moduleName) {
494
- if (!isElementNode(node)) return false;
495
- if (node.tagName == "script") return isBundledModulePath(getAttribute(node, "src"), moduleName);
496
- return node.tagName == "link" && getAttribute(node, "rel") == "modulepreload" && isBundledModulePath(getAttribute(node, "href"), moduleName);
497
- }
498
- function isBundledModulePath(path, moduleName) {
499
- return Boolean(path?.startsWith(`/assets/${moduleName}-`));
500
- }
501
- function isBundledAssetTag(node) {
502
- if (!isElementNode(node)) return false;
503
- return Boolean(node.tagName == "script" && getAttribute(node, "src")?.startsWith("/assets/") || node.tagName == "link" && getAttribute(node, "href")?.startsWith("/assets/"));
504
- }
505
- function rewriteCatalogRequest(request, manualCatalogPublicPath) {
506
- const url = parseRequestUrl(request.url);
507
- if (!url) return;
508
- if (url.pathname == "/" || url.pathname == "/index.html") request.url = `${manualCatalogPublicPath}${url.search}`;
509
- }
510
- function parseRequestUrl(requestUrl) {
511
- return URL.parse(requestUrl || "", "http://localhost");
512
- }
513
- function getRequiredElementByTagName(root, tagName) {
514
- return query(root, (candidate) => isElementNode(candidate) && candidate.tagName == tagName);
412
+ function getManualTestPackageName(publicPath, workspaceRoot) {
413
+ if (publicPath.startsWith("/tests/manual/")) return basename(workspaceRoot);
414
+ return publicPath.match(/^\/(?:.*\/)?([^/]+)\/tests\/manual\//)?.[1] || null;
515
415
  }
516
416
  //#endregion
517
417
  //#region src/debug-plugin/plugin.ts
@@ -561,14 +461,15 @@ function refreshPlugin() {
561
461
  name: "ckeditor5-manual-refresh",
562
462
  apply: "serve",
563
463
  configureServer(server) {
564
- const clientEnvironment = server.environments.client;
565
- wrapBundledDevClientSend(clientEnvironment.clients);
566
- wrapBundledDevFullReloads(clientEnvironment);
464
+ const bundledDev = server.environments.client.bundledDev;
465
+ if (!bundledDev) return;
466
+ wrapBundledDevClientSend(bundledDev.clients);
467
+ wrapBundledDevFullReloads(bundledDev);
567
468
  }
568
469
  };
569
470
  }
570
471
  function wrapBundledDevClientSend(clients) {
571
- if (!clients) return;
472
+ if (!clients || typeof clients.setupIfNeeded != "function") return;
572
473
  const setupIfNeeded = clients.setupIfNeeded.bind(clients);
573
474
  clients.setupIfNeeded = (client, clientId) => {
574
475
  if (!client[isManualRefreshWrappedClient]) {
@@ -581,12 +482,12 @@ function wrapBundledDevClientSend(clients) {
581
482
  return setupIfNeeded(client, clientId);
582
483
  };
583
484
  }
584
- function wrapBundledDevFullReloads(clientEnvironment) {
585
- if (!clientEnvironment.handleHmrOutput) return;
586
- const handleHmrOutput = clientEnvironment.handleHmrOutput.bind(clientEnvironment);
587
- clientEnvironment.handleHmrOutput = (client, files, hmrOutput, invalidateInformation) => {
485
+ function wrapBundledDevFullReloads(bundledDev) {
486
+ if (typeof bundledDev.handleHmrOutput != "function") return;
487
+ const handleHmrOutput = bundledDev.handleHmrOutput.bind(bundledDev);
488
+ bundledDev.handleHmrOutput = (client, files, hmrOutput, invalidateInformation) => {
588
489
  if (hmrOutput.type == "FullReload" && shouldShowManualRefreshPromptForFiles(files)) {
589
- clientEnvironment.devEngine?.ensureLatestBuildOutput().catch(() => {});
490
+ ensureLatestBuildOutput(bundledDev);
590
491
  client.send({
591
492
  type: "custom",
592
493
  event: MANUAL_REFRESH_EVENT_NAME
@@ -596,6 +497,11 @@ function wrapBundledDevFullReloads(clientEnvironment) {
596
497
  return handleHmrOutput(client, files, hmrOutput, invalidateInformation);
597
498
  };
598
499
  }
500
+ function ensureLatestBuildOutput(bundledDev) {
501
+ try {
502
+ bundledDev.devEngine?.ensureLatestBuildOutput().catch(() => {});
503
+ } catch {}
504
+ }
599
505
  function sendManualRefreshPayload(payload, send) {
600
506
  if (shouldShowManualRefreshPrompt(payload)) {
601
507
  send({
@@ -619,42 +525,27 @@ function shouldShowManualRefreshPromptForFiles(files) {
619
525
  return files.some((file) => !file.endsWith(".css") && !file.endsWith(".html"));
620
526
  }
621
527
  //#endregion
622
- //#region src/raw-plugin/plugin.ts
528
+ //#region src/svg-plugin/plugin.ts
623
529
  /**
624
530
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
625
531
  * For licensing, see LICENSE.md.
626
532
  */
627
- const RAW_QUERY = "?ckeditor5-raw";
628
- function rawHtmlPlugin() {
533
+ function rawSvgPlugin() {
629
534
  return {
630
- name: "ckeditor5-raw",
535
+ name: "ckeditor5-raw-svg",
631
536
  enforce: "pre",
632
- resolveId(source, importer) {
633
- if (!importer || source.includes("?")) return null;
634
- if (path.extname(source) != ".html") return null;
635
- const filePath = resolveHtmlFilePath(source, importer);
636
- if (filePath) return `${filePath}${RAW_QUERY}`;
637
- return null;
638
- },
639
- async load(id) {
640
- if (!id.endsWith(RAW_QUERY)) return null;
641
- const source = await readFile(id.slice(0, -14), "utf8");
642
- return {
643
- code: `export default ${JSON.stringify(source)};`,
644
- map: null
645
- };
537
+ load: {
538
+ filter: { id: { include: /\.svg$/ } },
539
+ handler(id) {
540
+ const content = readFileSync(id, "utf-8");
541
+ return {
542
+ code: `export default ${JSON.stringify(content)};`,
543
+ map: null
544
+ };
545
+ }
646
546
  }
647
547
  };
648
548
  }
649
- function resolveHtmlFilePath(source, importer) {
650
- if (!source.startsWith(".")) return null;
651
- const filePath = path.resolve(path.dirname(getFilePathFromId(importer)), source);
652
- return existsSync(filePath) ? toPosixPath(filePath) : null;
653
- }
654
- function getFilePathFromId(id) {
655
- const queryIndex = id.indexOf("?");
656
- return queryIndex >= 0 ? id.slice(0, queryIndex) : id;
657
- }
658
549
  //#endregion
659
550
  //#region src/index.ts
660
551
  /**
@@ -662,4 +553,4 @@ function getFilePathFromId(id) {
662
553
  * For licensing, see LICENSE.md.
663
554
  */
664
555
  //#endregion
665
- export { ckDebugPlugin, manualTestsPlugin, rawHtmlPlugin, refreshPlugin, stringifyValues };
556
+ export { ckDebugPlugin, manualStaticAssetsPlugin, manualTestsPlugin, rawSvgPlugin, refreshPlugin, stringifyValues };