@deot/docs-dever 1.1.1 → 1.1.2
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/README.md +9 -0
- package/dist/index.cjs +232 -0
- package/dist/index.js +232 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,6 +54,15 @@ development 和 preview 会保持服务运行;build 在 Vite 构建结束后
|
|
|
54
54
|
- 为客户端路由提供 HTML history fallback;静态托管若只能配置自定义 404 页,请改用 Client 的 [`@deot/docs:redirect`](../client/README.md) 约定。
|
|
55
55
|
- 在 build 模式复制 Markdown、JSON(含 `.page.json`)、SFC 等静态内容资源;
|
|
56
56
|
- 当当前仓库源码存在时,将 locale、renderer、markdown、playground、theme 包映射到本地入口。
|
|
57
|
+
- 在 development 模式提供 `GET /__docs/module/<包名>`,把工作区源码编成一份浏览器 ESM。preview 与 build 对该地址返回 404。
|
|
58
|
+
|
|
59
|
+
## 本地模块
|
|
60
|
+
|
|
61
|
+
`doc dev` 扫描项目 `packages/*/package.json`。存在 `src/index.ts` 或 `index.ts`,且真实路径仍在项目内时,可以用 `/__docs/module/<包名>` 取到编译结果。例如 `@deot/docs-locale` 对应 `/__docs/module/@deot/docs-locale`。
|
|
62
|
+
|
|
63
|
+
当前包内的相对 `.vue`、`.ts`、`.tsx` 和样式会打进这一份 ESM,样式写入 iframe 的 `document.head`。`vue`、其它工作区包,以及其余裸导入保持原样,交给 Playground import map。`vue/jsx-runtime` 会打进包。
|
|
64
|
+
|
|
65
|
+
站点自己决定是否使用这些地址。本仓库的 `index.html` 在 `window.__DOCS_RUNTIME__?.mode !== 'development'` 时把 `modules` 留空,development 时改指本地地址。直接打开 HTML 没有 runtime,因此仍走内置 CDN。管理页里对同名包的覆盖会压过 `index.html`。改完源码后刷新文档页;已打开的 Playground 不会热更新这份外部模块。
|
|
57
66
|
|
|
58
67
|
### `getDeverMode(options)`
|
|
59
68
|
|
package/dist/index.cjs
CHANGED
|
@@ -99,6 +99,230 @@ var resolveDocsWorkspace = (cwd = process.cwd(), workspace) => {
|
|
|
99
99
|
node_path.join(projectRoot, "index.html")
|
|
100
100
|
].join(" "));
|
|
101
101
|
};
|
|
102
|
+
var BUNDLED_SPECIFIERS = /* @__PURE__ */ new Set(["vue/jsx-runtime", "vue/jsx-dev-runtime"]);
|
|
103
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
104
|
+
"coverage",
|
|
105
|
+
"dist",
|
|
106
|
+
"node_modules"
|
|
107
|
+
]);
|
|
108
|
+
var cache = /* @__PURE__ */ new Map();
|
|
109
|
+
var pending = /* @__PURE__ */ new Map();
|
|
110
|
+
var buildCount = 0;
|
|
111
|
+
var readPackageName = (manifestPath) => {
|
|
112
|
+
try {
|
|
113
|
+
const manifest = JSON.parse(node_fs.readFileSync(manifestPath, "utf8"));
|
|
114
|
+
return typeof manifest.name === "string" && manifest.name ? manifest.name : "";
|
|
115
|
+
} catch {
|
|
116
|
+
return "";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var resolveSourceEntry = (directory) => {
|
|
120
|
+
for (const relative of ["src/index.ts", "index.ts"]) {
|
|
121
|
+
const candidate = node_path.resolve(directory, relative);
|
|
122
|
+
if (!node_fs.existsSync(candidate) || !node_fs.statSync(candidate).isFile()) continue;
|
|
123
|
+
const realEntry = node_fs.realpathSync(candidate);
|
|
124
|
+
if (!isInside(directory, realEntry)) continue;
|
|
125
|
+
return realEntry;
|
|
126
|
+
}
|
|
127
|
+
return "";
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* 收集项目 `packages/*` 里能编成浏览器模块的包。只接受留在项目内的真实路径。
|
|
131
|
+
* @param projectRoot 文档项目根目录。
|
|
132
|
+
* @returns 包名到源码入口的映射。同名包保留先扫描到的一项。
|
|
133
|
+
*/
|
|
134
|
+
var resolveWorkspaceModuleEntries = (projectRoot) => {
|
|
135
|
+
const realRoot = node_fs.existsSync(projectRoot) ? node_fs.realpathSync(projectRoot) : node_path.resolve(projectRoot);
|
|
136
|
+
const packagesDir = node_path.resolve(realRoot, "packages");
|
|
137
|
+
if (!node_fs.existsSync(packagesDir) || !node_fs.statSync(packagesDir).isDirectory()) return [];
|
|
138
|
+
const realPackages = node_fs.realpathSync(packagesDir);
|
|
139
|
+
if (!isInside(realRoot, realPackages)) return [];
|
|
140
|
+
const seen = /* @__PURE__ */ new Set();
|
|
141
|
+
const entries = [];
|
|
142
|
+
for (const item of node_fs.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
143
|
+
if (!item.isDirectory() && !item.isSymbolicLink()) continue;
|
|
144
|
+
const directory = node_path.resolve(packagesDir, item.name);
|
|
145
|
+
if (!node_fs.existsSync(directory) || !node_fs.statSync(directory).isDirectory()) continue;
|
|
146
|
+
const realDirectory = node_fs.realpathSync(directory);
|
|
147
|
+
if (!isInside(realPackages, realDirectory)) continue;
|
|
148
|
+
const name = readPackageName(node_path.join(realDirectory, "package.json"));
|
|
149
|
+
const entry = name ? resolveSourceEntry(realDirectory) : "";
|
|
150
|
+
if (!name || !entry || seen.has(name)) continue;
|
|
151
|
+
seen.add(name);
|
|
152
|
+
entries.push({
|
|
153
|
+
name,
|
|
154
|
+
entry,
|
|
155
|
+
directory: realDirectory
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return entries;
|
|
159
|
+
};
|
|
160
|
+
var directorySignature = (directory) => {
|
|
161
|
+
const files = [];
|
|
162
|
+
const walk = (current) => {
|
|
163
|
+
const realCurrent = node_fs.realpathSync(current);
|
|
164
|
+
if (!isInside(directory, realCurrent)) return;
|
|
165
|
+
for (const item of node_fs.readdirSync(realCurrent, { withFileTypes: true })) {
|
|
166
|
+
if (item.name === ".git" || IGNORED_DIRECTORIES.has(item.name)) continue;
|
|
167
|
+
const filename = node_path.join(realCurrent, item.name);
|
|
168
|
+
if (item.isSymbolicLink()) {
|
|
169
|
+
if (!node_fs.existsSync(filename)) continue;
|
|
170
|
+
const realName = node_fs.realpathSync(filename);
|
|
171
|
+
if (!isInside(directory, realName)) continue;
|
|
172
|
+
const stat = node_fs.statSync(realName);
|
|
173
|
+
if (stat.isDirectory()) walk(realName);
|
|
174
|
+
else if (stat.isFile()) files.push(`${realName}:${stat.mtimeMs}`);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (item.isDirectory()) {
|
|
178
|
+
walk(filename);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (item.isFile()) files.push(`${filename}:${node_fs.statSync(filename).mtimeMs}`);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
walk(directory);
|
|
185
|
+
files.sort();
|
|
186
|
+
return files.join("\n");
|
|
187
|
+
};
|
|
188
|
+
var isWorkspaceModuleExternal = (id) => {
|
|
189
|
+
if (!id || id.startsWith("\0") || id.startsWith(".") || node_path.isAbsolute(id)) return false;
|
|
190
|
+
if (BUNDLED_SPECIFIERS.has(id)) return false;
|
|
191
|
+
return true;
|
|
192
|
+
};
|
|
193
|
+
var readAssetSource = (source) => typeof source === "string" ? source : Buffer.from(source).toString("utf8");
|
|
194
|
+
var toBundleResult = (result) => {
|
|
195
|
+
const output = Array.isArray(result) ? result[0] : result;
|
|
196
|
+
if (!output || !("output" in output) || !Array.isArray(output.output)) throw new Error("Module build did not produce a bundle");
|
|
197
|
+
return output;
|
|
198
|
+
};
|
|
199
|
+
var injectStyles = (name, code, css) => {
|
|
200
|
+
if (!css) return code;
|
|
201
|
+
return [
|
|
202
|
+
"if (typeof document !== \"undefined\") {",
|
|
203
|
+
" const style = document.createElement(\"style\");",
|
|
204
|
+
`\tstyle.setAttribute("data-docs-module", ${JSON.stringify(name)});`,
|
|
205
|
+
`\tstyle.textContent = ${JSON.stringify(css)};`,
|
|
206
|
+
" document.head.appendChild(style);",
|
|
207
|
+
"}",
|
|
208
|
+
code
|
|
209
|
+
].join("\n");
|
|
210
|
+
};
|
|
211
|
+
var buildWorkspaceModule = async (projectRoot, entry) => {
|
|
212
|
+
buildCount += 1;
|
|
213
|
+
const bundle = toBundleResult(await (0, vite.build)({
|
|
214
|
+
configFile: false,
|
|
215
|
+
envFile: false,
|
|
216
|
+
root: projectRoot,
|
|
217
|
+
publicDir: false,
|
|
218
|
+
logLevel: "silent",
|
|
219
|
+
mode: "development",
|
|
220
|
+
appType: "custom",
|
|
221
|
+
define: { "process.env.NODE_ENV": JSON.stringify("development") },
|
|
222
|
+
plugins: [(0, _vitejs_plugin_vue.default)(), (0, _vitejs_plugin_vue_jsx.default)()],
|
|
223
|
+
build: {
|
|
224
|
+
write: false,
|
|
225
|
+
minify: false,
|
|
226
|
+
cssMinify: false,
|
|
227
|
+
cssCodeSplit: false,
|
|
228
|
+
sourcemap: false,
|
|
229
|
+
emptyOutDir: false,
|
|
230
|
+
copyPublicDir: false,
|
|
231
|
+
modulePreload: false,
|
|
232
|
+
reportCompressedSize: false,
|
|
233
|
+
assetsInlineLimit: 1e8,
|
|
234
|
+
chunkSizeWarningLimit: 1e5,
|
|
235
|
+
rolldownOptions: {
|
|
236
|
+
input: entry.entry,
|
|
237
|
+
preserveEntrySignatures: "strict",
|
|
238
|
+
treeshake: false,
|
|
239
|
+
external: isWorkspaceModuleExternal,
|
|
240
|
+
output: {
|
|
241
|
+
format: "es",
|
|
242
|
+
codeSplitting: false,
|
|
243
|
+
entryFileNames: "module.js"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
const chunks = bundle.output.filter((item) => item.type === "chunk");
|
|
249
|
+
if (chunks.length !== 1) throw new Error(`Expected a single module chunk, got ${chunks.length}`);
|
|
250
|
+
const css = bundle.output.filter((item) => item.type === "asset" && item.fileName.endsWith(".css")).map((item) => readAssetSource(item.source)).join("\n");
|
|
251
|
+
return injectStyles(entry.name, chunks[0].code, css);
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* 把工作区包编成一份浏览器 ESM。`vue` 和其它工作区包保持裸导入。
|
|
255
|
+
* @param projectRoot 用于解析 node_modules 的项目根。
|
|
256
|
+
* @param entry 已通过边界检查的包入口。
|
|
257
|
+
* @returns 可直接交给浏览器执行的模块源码。
|
|
258
|
+
*/
|
|
259
|
+
var compileWorkspaceModule = (projectRoot, entry) => {
|
|
260
|
+
const signature = directorySignature(entry.directory);
|
|
261
|
+
const cached = cache.get(entry.name);
|
|
262
|
+
if (cached?.signature === signature) return Promise.resolve(cached.code);
|
|
263
|
+
const inflight = pending.get(entry.name);
|
|
264
|
+
if (inflight) return inflight;
|
|
265
|
+
const task = buildWorkspaceModule(projectRoot, entry).then((code) => {
|
|
266
|
+
cache.set(entry.name, {
|
|
267
|
+
signature: directorySignature(entry.directory),
|
|
268
|
+
code
|
|
269
|
+
});
|
|
270
|
+
return code;
|
|
271
|
+
}).finally(() => {
|
|
272
|
+
if (pending.get(entry.name) === task) pending.delete(entry.name);
|
|
273
|
+
});
|
|
274
|
+
pending.set(entry.name, task);
|
|
275
|
+
return task;
|
|
276
|
+
};
|
|
277
|
+
var moduleFailure = (message) => `throw new Error(${JSON.stringify(message)});`;
|
|
278
|
+
/**
|
|
279
|
+
* 响应 `/__docs/module/<包名>`。未知包返回 404;编译失败返回会抛错的 ESM。
|
|
280
|
+
* @param req 开发服务请求。
|
|
281
|
+
* @param res 开发服务响应。
|
|
282
|
+
* @param projectRoot 文档项目根目录。
|
|
283
|
+
* @returns 是否已处理该请求。路径不匹配时为 false。
|
|
284
|
+
*/
|
|
285
|
+
var respondWorkspaceModule = async (req, res, projectRoot) => {
|
|
286
|
+
const pathname = (req.url || "/").split(/[?#]/u, 1)[0] || "/";
|
|
287
|
+
if (!pathname.startsWith("/__docs/module/")) return false;
|
|
288
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
289
|
+
res.statusCode = 405;
|
|
290
|
+
res.setHeader("Allow", "GET, HEAD");
|
|
291
|
+
res.end("Method Not Allowed");
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
let name = "";
|
|
295
|
+
try {
|
|
296
|
+
name = decodeURIComponent(pathname.slice(15));
|
|
297
|
+
} catch {
|
|
298
|
+
res.statusCode = 400;
|
|
299
|
+
res.end("Bad Request");
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
if (!name || name.split("/").includes("..") || name.includes("\\") || name.includes("\0")) {
|
|
303
|
+
res.statusCode = 404;
|
|
304
|
+
res.end("Not Found");
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
const entry = resolveWorkspaceModuleEntries(projectRoot).find((item) => item.name === name);
|
|
308
|
+
if (!entry) {
|
|
309
|
+
res.statusCode = 404;
|
|
310
|
+
res.end("Not Found");
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
let code;
|
|
314
|
+
try {
|
|
315
|
+
code = await compileWorkspaceModule(projectRoot, entry);
|
|
316
|
+
} catch (reason) {
|
|
317
|
+
code = moduleFailure(reason instanceof Error ? reason.message : "Module compile failed");
|
|
318
|
+
}
|
|
319
|
+
res.statusCode = 200;
|
|
320
|
+
res.setHeader("Content-Type", "text/javascript; charset=utf-8");
|
|
321
|
+
res.setHeader("Cache-Control", "no-store");
|
|
322
|
+
if (req.method === "HEAD") res.end();
|
|
323
|
+
else res.end(code);
|
|
324
|
+
return true;
|
|
325
|
+
};
|
|
102
326
|
//#endregion
|
|
103
327
|
//#region packages/dever/src/plugins/index.ts
|
|
104
328
|
var innerPathRegex = /^\/(@|__)/;
|
|
@@ -298,6 +522,14 @@ var configureWorkspaceServer = (options, resolvedWorkspace) => (server) => {
|
|
|
298
522
|
let realResourceRoot = realWorkspace;
|
|
299
523
|
try {
|
|
300
524
|
const rawPathname = getRawPathname(req.url || "/");
|
|
525
|
+
if (!options.preview && rawPathname.startsWith("/__docs/module/")) {
|
|
526
|
+
respondWorkspaceModule(req, res, resolved.projectRoot).catch((reason) => {
|
|
527
|
+
if (res.writableEnded) return;
|
|
528
|
+
res.statusCode = 500;
|
|
529
|
+
res.end(reason instanceof Error ? reason.message : "Module compile failed");
|
|
530
|
+
});
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
301
533
|
if (rawPathname.startsWith("/__docs/") && rawPathname !== "/__docs/events") {
|
|
302
534
|
res.statusCode = 404;
|
|
303
535
|
res.end("Not Found");
|
package/dist/index.js
CHANGED
|
@@ -70,6 +70,230 @@ var resolveDocsWorkspace = (cwd = process.cwd(), workspace) => {
|
|
|
70
70
|
path.join(projectRoot, "index.html")
|
|
71
71
|
].join(" "));
|
|
72
72
|
};
|
|
73
|
+
var BUNDLED_SPECIFIERS = /* @__PURE__ */ new Set(["vue/jsx-runtime", "vue/jsx-dev-runtime"]);
|
|
74
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
75
|
+
"coverage",
|
|
76
|
+
"dist",
|
|
77
|
+
"node_modules"
|
|
78
|
+
]);
|
|
79
|
+
var cache = /* @__PURE__ */ new Map();
|
|
80
|
+
var pending = /* @__PURE__ */ new Map();
|
|
81
|
+
var buildCount = 0;
|
|
82
|
+
var readPackageName = (manifestPath) => {
|
|
83
|
+
try {
|
|
84
|
+
const manifest = JSON.parse(fs$1.readFileSync(manifestPath, "utf8"));
|
|
85
|
+
return typeof manifest.name === "string" && manifest.name ? manifest.name : "";
|
|
86
|
+
} catch {
|
|
87
|
+
return "";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var resolveSourceEntry = (directory) => {
|
|
91
|
+
for (const relative of ["src/index.ts", "index.ts"]) {
|
|
92
|
+
const candidate = path.resolve(directory, relative);
|
|
93
|
+
if (!fs$1.existsSync(candidate) || !fs$1.statSync(candidate).isFile()) continue;
|
|
94
|
+
const realEntry = fs$1.realpathSync(candidate);
|
|
95
|
+
if (!isInside(directory, realEntry)) continue;
|
|
96
|
+
return realEntry;
|
|
97
|
+
}
|
|
98
|
+
return "";
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* 收集项目 `packages/*` 里能编成浏览器模块的包。只接受留在项目内的真实路径。
|
|
102
|
+
* @param projectRoot 文档项目根目录。
|
|
103
|
+
* @returns 包名到源码入口的映射。同名包保留先扫描到的一项。
|
|
104
|
+
*/
|
|
105
|
+
var resolveWorkspaceModuleEntries = (projectRoot) => {
|
|
106
|
+
const realRoot = fs$1.existsSync(projectRoot) ? fs$1.realpathSync(projectRoot) : path.resolve(projectRoot);
|
|
107
|
+
const packagesDir = path.resolve(realRoot, "packages");
|
|
108
|
+
if (!fs$1.existsSync(packagesDir) || !fs$1.statSync(packagesDir).isDirectory()) return [];
|
|
109
|
+
const realPackages = fs$1.realpathSync(packagesDir);
|
|
110
|
+
if (!isInside(realRoot, realPackages)) return [];
|
|
111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
112
|
+
const entries = [];
|
|
113
|
+
for (const item of fs$1.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
114
|
+
if (!item.isDirectory() && !item.isSymbolicLink()) continue;
|
|
115
|
+
const directory = path.resolve(packagesDir, item.name);
|
|
116
|
+
if (!fs$1.existsSync(directory) || !fs$1.statSync(directory).isDirectory()) continue;
|
|
117
|
+
const realDirectory = fs$1.realpathSync(directory);
|
|
118
|
+
if (!isInside(realPackages, realDirectory)) continue;
|
|
119
|
+
const name = readPackageName(path.join(realDirectory, "package.json"));
|
|
120
|
+
const entry = name ? resolveSourceEntry(realDirectory) : "";
|
|
121
|
+
if (!name || !entry || seen.has(name)) continue;
|
|
122
|
+
seen.add(name);
|
|
123
|
+
entries.push({
|
|
124
|
+
name,
|
|
125
|
+
entry,
|
|
126
|
+
directory: realDirectory
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return entries;
|
|
130
|
+
};
|
|
131
|
+
var directorySignature = (directory) => {
|
|
132
|
+
const files = [];
|
|
133
|
+
const walk = (current) => {
|
|
134
|
+
const realCurrent = fs$1.realpathSync(current);
|
|
135
|
+
if (!isInside(directory, realCurrent)) return;
|
|
136
|
+
for (const item of fs$1.readdirSync(realCurrent, { withFileTypes: true })) {
|
|
137
|
+
if (item.name === ".git" || IGNORED_DIRECTORIES.has(item.name)) continue;
|
|
138
|
+
const filename = path.join(realCurrent, item.name);
|
|
139
|
+
if (item.isSymbolicLink()) {
|
|
140
|
+
if (!fs$1.existsSync(filename)) continue;
|
|
141
|
+
const realName = fs$1.realpathSync(filename);
|
|
142
|
+
if (!isInside(directory, realName)) continue;
|
|
143
|
+
const stat = fs$1.statSync(realName);
|
|
144
|
+
if (stat.isDirectory()) walk(realName);
|
|
145
|
+
else if (stat.isFile()) files.push(`${realName}:${stat.mtimeMs}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (item.isDirectory()) {
|
|
149
|
+
walk(filename);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (item.isFile()) files.push(`${filename}:${fs$1.statSync(filename).mtimeMs}`);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
walk(directory);
|
|
156
|
+
files.sort();
|
|
157
|
+
return files.join("\n");
|
|
158
|
+
};
|
|
159
|
+
var isWorkspaceModuleExternal = (id) => {
|
|
160
|
+
if (!id || id.startsWith("\0") || id.startsWith(".") || path.isAbsolute(id)) return false;
|
|
161
|
+
if (BUNDLED_SPECIFIERS.has(id)) return false;
|
|
162
|
+
return true;
|
|
163
|
+
};
|
|
164
|
+
var readAssetSource = (source) => typeof source === "string" ? source : Buffer.from(source).toString("utf8");
|
|
165
|
+
var toBundleResult = (result) => {
|
|
166
|
+
const output = Array.isArray(result) ? result[0] : result;
|
|
167
|
+
if (!output || !("output" in output) || !Array.isArray(output.output)) throw new Error("Module build did not produce a bundle");
|
|
168
|
+
return output;
|
|
169
|
+
};
|
|
170
|
+
var injectStyles = (name, code, css) => {
|
|
171
|
+
if (!css) return code;
|
|
172
|
+
return [
|
|
173
|
+
"if (typeof document !== \"undefined\") {",
|
|
174
|
+
" const style = document.createElement(\"style\");",
|
|
175
|
+
`\tstyle.setAttribute("data-docs-module", ${JSON.stringify(name)});`,
|
|
176
|
+
`\tstyle.textContent = ${JSON.stringify(css)};`,
|
|
177
|
+
" document.head.appendChild(style);",
|
|
178
|
+
"}",
|
|
179
|
+
code
|
|
180
|
+
].join("\n");
|
|
181
|
+
};
|
|
182
|
+
var buildWorkspaceModule = async (projectRoot, entry) => {
|
|
183
|
+
buildCount += 1;
|
|
184
|
+
const bundle = toBundleResult(await build({
|
|
185
|
+
configFile: false,
|
|
186
|
+
envFile: false,
|
|
187
|
+
root: projectRoot,
|
|
188
|
+
publicDir: false,
|
|
189
|
+
logLevel: "silent",
|
|
190
|
+
mode: "development",
|
|
191
|
+
appType: "custom",
|
|
192
|
+
define: { "process.env.NODE_ENV": JSON.stringify("development") },
|
|
193
|
+
plugins: [vue(), vueJsx()],
|
|
194
|
+
build: {
|
|
195
|
+
write: false,
|
|
196
|
+
minify: false,
|
|
197
|
+
cssMinify: false,
|
|
198
|
+
cssCodeSplit: false,
|
|
199
|
+
sourcemap: false,
|
|
200
|
+
emptyOutDir: false,
|
|
201
|
+
copyPublicDir: false,
|
|
202
|
+
modulePreload: false,
|
|
203
|
+
reportCompressedSize: false,
|
|
204
|
+
assetsInlineLimit: 1e8,
|
|
205
|
+
chunkSizeWarningLimit: 1e5,
|
|
206
|
+
rolldownOptions: {
|
|
207
|
+
input: entry.entry,
|
|
208
|
+
preserveEntrySignatures: "strict",
|
|
209
|
+
treeshake: false,
|
|
210
|
+
external: isWorkspaceModuleExternal,
|
|
211
|
+
output: {
|
|
212
|
+
format: "es",
|
|
213
|
+
codeSplitting: false,
|
|
214
|
+
entryFileNames: "module.js"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}));
|
|
219
|
+
const chunks = bundle.output.filter((item) => item.type === "chunk");
|
|
220
|
+
if (chunks.length !== 1) throw new Error(`Expected a single module chunk, got ${chunks.length}`);
|
|
221
|
+
const css = bundle.output.filter((item) => item.type === "asset" && item.fileName.endsWith(".css")).map((item) => readAssetSource(item.source)).join("\n");
|
|
222
|
+
return injectStyles(entry.name, chunks[0].code, css);
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* 把工作区包编成一份浏览器 ESM。`vue` 和其它工作区包保持裸导入。
|
|
226
|
+
* @param projectRoot 用于解析 node_modules 的项目根。
|
|
227
|
+
* @param entry 已通过边界检查的包入口。
|
|
228
|
+
* @returns 可直接交给浏览器执行的模块源码。
|
|
229
|
+
*/
|
|
230
|
+
var compileWorkspaceModule = (projectRoot, entry) => {
|
|
231
|
+
const signature = directorySignature(entry.directory);
|
|
232
|
+
const cached = cache.get(entry.name);
|
|
233
|
+
if (cached?.signature === signature) return Promise.resolve(cached.code);
|
|
234
|
+
const inflight = pending.get(entry.name);
|
|
235
|
+
if (inflight) return inflight;
|
|
236
|
+
const task = buildWorkspaceModule(projectRoot, entry).then((code) => {
|
|
237
|
+
cache.set(entry.name, {
|
|
238
|
+
signature: directorySignature(entry.directory),
|
|
239
|
+
code
|
|
240
|
+
});
|
|
241
|
+
return code;
|
|
242
|
+
}).finally(() => {
|
|
243
|
+
if (pending.get(entry.name) === task) pending.delete(entry.name);
|
|
244
|
+
});
|
|
245
|
+
pending.set(entry.name, task);
|
|
246
|
+
return task;
|
|
247
|
+
};
|
|
248
|
+
var moduleFailure = (message) => `throw new Error(${JSON.stringify(message)});`;
|
|
249
|
+
/**
|
|
250
|
+
* 响应 `/__docs/module/<包名>`。未知包返回 404;编译失败返回会抛错的 ESM。
|
|
251
|
+
* @param req 开发服务请求。
|
|
252
|
+
* @param res 开发服务响应。
|
|
253
|
+
* @param projectRoot 文档项目根目录。
|
|
254
|
+
* @returns 是否已处理该请求。路径不匹配时为 false。
|
|
255
|
+
*/
|
|
256
|
+
var respondWorkspaceModule = async (req, res, projectRoot) => {
|
|
257
|
+
const pathname = (req.url || "/").split(/[?#]/u, 1)[0] || "/";
|
|
258
|
+
if (!pathname.startsWith("/__docs/module/")) return false;
|
|
259
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
260
|
+
res.statusCode = 405;
|
|
261
|
+
res.setHeader("Allow", "GET, HEAD");
|
|
262
|
+
res.end("Method Not Allowed");
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
let name = "";
|
|
266
|
+
try {
|
|
267
|
+
name = decodeURIComponent(pathname.slice(15));
|
|
268
|
+
} catch {
|
|
269
|
+
res.statusCode = 400;
|
|
270
|
+
res.end("Bad Request");
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
if (!name || name.split("/").includes("..") || name.includes("\\") || name.includes("\0")) {
|
|
274
|
+
res.statusCode = 404;
|
|
275
|
+
res.end("Not Found");
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
const entry = resolveWorkspaceModuleEntries(projectRoot).find((item) => item.name === name);
|
|
279
|
+
if (!entry) {
|
|
280
|
+
res.statusCode = 404;
|
|
281
|
+
res.end("Not Found");
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
let code;
|
|
285
|
+
try {
|
|
286
|
+
code = await compileWorkspaceModule(projectRoot, entry);
|
|
287
|
+
} catch (reason) {
|
|
288
|
+
code = moduleFailure(reason instanceof Error ? reason.message : "Module compile failed");
|
|
289
|
+
}
|
|
290
|
+
res.statusCode = 200;
|
|
291
|
+
res.setHeader("Content-Type", "text/javascript; charset=utf-8");
|
|
292
|
+
res.setHeader("Cache-Control", "no-store");
|
|
293
|
+
if (req.method === "HEAD") res.end();
|
|
294
|
+
else res.end(code);
|
|
295
|
+
return true;
|
|
296
|
+
};
|
|
73
297
|
//#endregion
|
|
74
298
|
//#region packages/dever/src/plugins/index.ts
|
|
75
299
|
var innerPathRegex = /^\/(@|__)/;
|
|
@@ -269,6 +493,14 @@ var configureWorkspaceServer = (options, resolvedWorkspace) => (server) => {
|
|
|
269
493
|
let realResourceRoot = realWorkspace;
|
|
270
494
|
try {
|
|
271
495
|
const rawPathname = getRawPathname(req.url || "/");
|
|
496
|
+
if (!options.preview && rawPathname.startsWith("/__docs/module/")) {
|
|
497
|
+
respondWorkspaceModule(req, res, resolved.projectRoot).catch((reason) => {
|
|
498
|
+
if (res.writableEnded) return;
|
|
499
|
+
res.statusCode = 500;
|
|
500
|
+
res.end(reason instanceof Error ? reason.message : "Module compile failed");
|
|
501
|
+
});
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
272
504
|
if (rawPathname.startsWith("/__docs/") && rawPathname !== "/__docs/events") {
|
|
273
505
|
res.statusCode = 404;
|
|
274
506
|
res.end("Not Found");
|