@baybreezy/docd 0.0.1 → 0.0.3
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/modules/css.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { addVitePlugin, createResolver, defineNuxtModule } from "@nuxt/kit";
|
|
2
|
+
import { joinURL } from "ufo";
|
|
3
|
+
|
|
4
|
+
export default defineNuxtModule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "docd:css",
|
|
7
|
+
},
|
|
8
|
+
setup(_options, nuxt) {
|
|
9
|
+
const resolver = createResolver(import.meta.url);
|
|
10
|
+
|
|
11
|
+
// Absolute path to the layer's tailwind entry — this is the file that owns
|
|
12
|
+
// `@import "tailwindcss"` and therefore controls Tailwind's scanner.
|
|
13
|
+
const tailwindCssPath = resolver.resolve("../app/assets/css/tailwind.css");
|
|
14
|
+
|
|
15
|
+
// The consuming app's content directory. We convert to forward-slashes so
|
|
16
|
+
// the injected @source glob works on Windows too.
|
|
17
|
+
const contentDir = joinURL(nuxt.options.rootDir, "content").replace(/\\/g, "/");
|
|
18
|
+
|
|
19
|
+
// Inject `@source <contentDir>/**/*` into tailwind.css
|
|
20
|
+
addVitePlugin({
|
|
21
|
+
name: "docd:inject-tailwind-sources",
|
|
22
|
+
enforce: "pre",
|
|
23
|
+
transform(code: string, id: string) {
|
|
24
|
+
if (id.split("?")[0] !== tailwindCssPath) return null;
|
|
25
|
+
return {
|
|
26
|
+
code: `${code}\n@source ${JSON.stringify(`${contentDir}/**/*`)};`,
|
|
27
|
+
map: null,
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Suppress noisy Vite warnings produced during Tailwind's build pass.
|
|
33
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
34
|
+
const logger = config.customLogger;
|
|
35
|
+
if (!logger) return;
|
|
36
|
+
const ignore = ["@tailwindcss/vite:generate:build", "nuxt:module-preload-polyfill"];
|
|
37
|
+
const originalWarn = logger.warn.bind(logger);
|
|
38
|
+
logger.warn = (msg, options) => {
|
|
39
|
+
if (ignore.some((p) => msg.includes(p))) return;
|
|
40
|
+
originalWarn(msg, options);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
});
|
package/nuxt.config.ts
CHANGED
|
@@ -50,6 +50,7 @@ export default defineNuxtConfig({
|
|
|
50
50
|
"@morev/vue-transitions/nuxt",
|
|
51
51
|
"vue-sonner/nuxt",
|
|
52
52
|
resolver.resolve("./modules/routing"),
|
|
53
|
+
resolver.resolve("./modules/css"),
|
|
53
54
|
resolver.resolve("./modules/config"),
|
|
54
55
|
resolver.resolve("./modules/custom-icons"),
|
|
55
56
|
resolver.resolve("./modules/prose-component-meta"),
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { workerData, parentPort } from "node:worker_threads";
|
|
2
|
+
|
|
3
|
+
import { getComponentMeta } from "nuxt-component-meta/parser";
|
|
4
|
+
|
|
5
|
+
const { batch, rootDir, cacheDir } = workerData;
|
|
6
|
+
|
|
7
|
+
const results = [];
|
|
8
|
+
|
|
9
|
+
for (const { manifestPath, absolutePath } of batch) {
|
|
10
|
+
try {
|
|
11
|
+
const raw = getComponentMeta(absolutePath, {
|
|
12
|
+
rootDir,
|
|
13
|
+
cache: true,
|
|
14
|
+
cacheDir,
|
|
15
|
+
});
|
|
16
|
+
results.push({ manifestPath, raw, ok: true });
|
|
17
|
+
} catch (err) {
|
|
18
|
+
results.push({ manifestPath, raw: null, ok: false, error: String(err) });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
parentPort.postMessage(results);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { cpus } from "node:os";
|
|
3
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
-
|
|
5
|
-
import {
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { Worker } from "node:worker_threads";
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
componentNameFromPath,
|
|
@@ -170,6 +171,67 @@ function normalizeExposed(exposed: RawExposedMeta): ProseComponentMetaExposed {
|
|
|
170
171
|
};
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
interface WorkerResult {
|
|
175
|
+
manifestPath: string;
|
|
176
|
+
raw: Record<string, unknown[]> | null;
|
|
177
|
+
ok: boolean;
|
|
178
|
+
error?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const WORKER_PATH = fileURLToPath(new URL("./component-meta-worker.mjs", import.meta.url));
|
|
182
|
+
|
|
183
|
+
async function runInWorkers(
|
|
184
|
+
components: ProseComponentMetaSource[],
|
|
185
|
+
opts: { rootDir: string; cache: boolean; cacheDir: string }
|
|
186
|
+
): Promise<WorkerResult[]> {
|
|
187
|
+
const numWorkers = Math.min(cpus().length, components.length, 8);
|
|
188
|
+
const chunkSize = Math.ceil(components.length / numWorkers);
|
|
189
|
+
|
|
190
|
+
const chunks: ProseComponentMetaSource[][] = [];
|
|
191
|
+
for (let i = 0; i < components.length; i += chunkSize) {
|
|
192
|
+
chunks.push(components.slice(i, i + chunkSize));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const spawnWorker = (batch: ProseComponentMetaSource[]) =>
|
|
196
|
+
new Promise<WorkerResult[]>((resolve, reject) => {
|
|
197
|
+
const worker = new Worker(WORKER_PATH, {
|
|
198
|
+
workerData: {
|
|
199
|
+
batch: batch.map((c) => ({
|
|
200
|
+
manifestPath: c.manifestPath,
|
|
201
|
+
absolutePath: c.absolutePath,
|
|
202
|
+
})),
|
|
203
|
+
rootDir: opts.rootDir,
|
|
204
|
+
cacheDir: opts.cacheDir,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
worker.on("message", resolve);
|
|
208
|
+
worker.on("error", reject);
|
|
209
|
+
worker.on("exit", (code) => {
|
|
210
|
+
if (code !== 0) reject(new Error(`Component meta worker exited with code ${code}`));
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const results = await Promise.all(chunks.map(spawnWorker));
|
|
216
|
+
return results.flat();
|
|
217
|
+
} catch {
|
|
218
|
+
// Fall back to sequential processing if workers fail.
|
|
219
|
+
const { getComponentMeta } = await import("nuxt-component-meta/parser");
|
|
220
|
+
return components.map(({ manifestPath, absolutePath }) => {
|
|
221
|
+
try {
|
|
222
|
+
const raw = getComponentMeta(absolutePath, {
|
|
223
|
+
rootDir: opts.rootDir,
|
|
224
|
+
cache: opts.cache,
|
|
225
|
+
cacheDir: opts.cacheDir,
|
|
226
|
+
}) as Record<string, unknown[]>;
|
|
227
|
+
return { manifestPath, raw, ok: true };
|
|
228
|
+
} catch (err) {
|
|
229
|
+
return { manifestPath, raw: null, ok: false, error: String(err) };
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
173
235
|
async function listVueFiles(dir: string): Promise<string[]> {
|
|
174
236
|
const files: string[] = [];
|
|
175
237
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -233,10 +295,12 @@ export async function generateProseComponentMeta(
|
|
|
233
295
|
);
|
|
234
296
|
let hasChanges = sources.length !== Object.keys(previousManifest.components || {}).length;
|
|
235
297
|
|
|
298
|
+
// First pass: resolve hashes and split into cache hits vs misses.
|
|
299
|
+
const cacheMisses: ProseComponentMetaSource[] = [];
|
|
300
|
+
|
|
236
301
|
for (const component of sources) {
|
|
237
302
|
const stats = await stat(component.absolutePath);
|
|
238
303
|
const sourceHash = `${stats.mtimeMs}:${stats.size}`;
|
|
239
|
-
|
|
240
304
|
sourceHashes[component.manifestPath] = sourceHash;
|
|
241
305
|
|
|
242
306
|
const cachedEntry = previousManifest.components?.[component.manifestPath];
|
|
@@ -244,27 +308,33 @@ export async function generateProseComponentMeta(
|
|
|
244
308
|
|
|
245
309
|
if (cachedEntry && cachedHash === sourceHash) {
|
|
246
310
|
components[component.manifestPath] = cachedEntry;
|
|
247
|
-
|
|
311
|
+
} else {
|
|
312
|
+
hasChanges = true;
|
|
313
|
+
cacheMisses.push(component);
|
|
248
314
|
}
|
|
315
|
+
}
|
|
249
316
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
317
|
+
// Second pass: process cache misses in parallel worker threads.
|
|
318
|
+
if (cacheMisses.length > 0) {
|
|
319
|
+
const rawResults = await runInWorkers(cacheMisses, {
|
|
253
320
|
rootDir: options.rootDir,
|
|
254
321
|
cache: options.cache ?? true,
|
|
255
|
-
cacheDir: options.cacheDir,
|
|
322
|
+
cacheDir: options.cacheDir ?? "",
|
|
256
323
|
});
|
|
257
324
|
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
name
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
325
|
+
for (const result of rawResults) {
|
|
326
|
+
if (!result.ok || !result.raw) continue;
|
|
327
|
+
const name = componentNameFromPath(result.manifestPath);
|
|
328
|
+
components[result.manifestPath] = {
|
|
329
|
+
name,
|
|
330
|
+
tag: componentTagFromName(name),
|
|
331
|
+
path: result.manifestPath,
|
|
332
|
+
props: (result.raw.props ?? []).map(normalizeProp),
|
|
333
|
+
slots: (result.raw.slots ?? []).map(normalizeSlot),
|
|
334
|
+
events: (result.raw.events ?? []).map(normalizeEvent),
|
|
335
|
+
exposed: (result.raw.exposed ?? []).map(normalizeExposed),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
268
338
|
}
|
|
269
339
|
|
|
270
340
|
if (!hasChanges) {
|