@immense/vue-pom-generator 1.0.52 → 1.0.54

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.
Files changed (51) hide show
  1. package/README.md +71 -27
  2. package/RELEASE_NOTES.md +38 -27
  3. package/class-generation/base-page.ts +18 -6
  4. package/class-generation/callout.ts +229 -679
  5. package/class-generation/floating-ui-callout.ts +857 -0
  6. package/class-generation/index.ts +24 -15
  7. package/class-generation/pointer.ts +152 -109
  8. package/dist/class-generation/base-page.d.ts +11 -5
  9. package/dist/class-generation/base-page.d.ts.map +1 -1
  10. package/dist/class-generation/callout.d.ts +44 -1
  11. package/dist/class-generation/callout.d.ts.map +1 -1
  12. package/dist/class-generation/floating-ui-callout.d.ts +4 -0
  13. package/dist/class-generation/floating-ui-callout.d.ts.map +1 -0
  14. package/dist/class-generation/index.d.ts +3 -2
  15. package/dist/class-generation/index.d.ts.map +1 -1
  16. package/dist/class-generation/pointer.d.ts +24 -5
  17. package/dist/class-generation/pointer.d.ts.map +1 -1
  18. package/dist/index.cjs +783 -231
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.mjs +783 -231
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/plugin/create-vue-pom-generator-plugins.d.ts +2 -2
  25. package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
  26. package/dist/plugin/nuxt-discovery.d.ts +47 -0
  27. package/dist/plugin/nuxt-discovery.d.ts.map +1 -0
  28. package/dist/plugin/path-utils.d.ts +4 -5
  29. package/dist/plugin/path-utils.d.ts.map +1 -1
  30. package/dist/plugin/support/build-plugin.d.ts +6 -3
  31. package/dist/plugin/support/build-plugin.d.ts.map +1 -1
  32. package/dist/plugin/support/dev-plugin.d.ts +6 -3
  33. package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
  34. package/dist/plugin/support/virtual-modules.d.ts +2 -2
  35. package/dist/plugin/support/virtual-modules.d.ts.map +1 -1
  36. package/dist/plugin/support-plugins.d.ts +5 -2
  37. package/dist/plugin/support-plugins.d.ts.map +1 -1
  38. package/dist/plugin/types.d.ts +35 -19
  39. package/dist/plugin/types.d.ts.map +1 -1
  40. package/dist/plugin/vue-plugin.d.ts +1 -1
  41. package/dist/plugin/vue-plugin.d.ts.map +1 -1
  42. package/dist/router-introspection.d.ts +4 -2
  43. package/dist/router-introspection.d.ts.map +1 -1
  44. package/dist/tests/nuxt-discovery.test.d.ts +2 -0
  45. package/dist/tests/nuxt-discovery.test.d.ts.map +1 -0
  46. package/dist/vite.config.d.ts.map +1 -1
  47. package/package.json +9 -13
  48. package/dist/plugin/support/generation-metrics.d.ts +0 -10
  49. package/dist/plugin/support/generation-metrics.d.ts.map +0 -1
  50. package/dist/tests/generation-metrics.test.d.ts +0 -2
  51. package/dist/tests/generation-metrics.test.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -22,19 +22,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
25
+ const fs = require("node:fs");
25
26
  const path = require("node:path");
26
27
  const process = require("node:process");
28
+ const node_module = require("node:module");
27
29
  const node_url = require("node:url");
28
- const fs = require("node:fs");
29
30
  const compilerDom = require("@vue/compiler-dom");
30
31
  const compilerSfc = require("@vue/compiler-sfc");
31
32
  const parser = require("@babel/parser");
32
33
  const compilerCore = require("@vue/compiler-core");
33
34
  const tsMorph = require("ts-morph");
34
- const jsdom = require("jsdom");
35
35
  const types = require("@babel/types");
36
36
  const node_perf_hooks = require("node:perf_hooks");
37
- const virtualImport = require("vite-plugin-virtual");
38
37
  const vue = require("@vitejs/plugin-vue");
39
38
  var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
40
39
  function _interopNamespaceDefault(e) {
@@ -104,6 +103,133 @@ function createLogger(options) {
104
103
  }
105
104
  };
106
105
  }
106
+ const requireFromModule = node_module.createRequire(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
107
+ function toUniqueResolvedPaths(paths) {
108
+ return Array.from(new Set(paths.map((value) => path.resolve(value))));
109
+ }
110
+ function resolveNuxtAlias(value, context) {
111
+ const aliases = [
112
+ ["~~", context.rootDir],
113
+ ["@@", context.rootDir],
114
+ ["~/", `${context.srcDir}${path.sep}`],
115
+ ["@/", `${context.srcDir}${path.sep}`],
116
+ ["~", context.srcDir],
117
+ ["@", context.srcDir]
118
+ ];
119
+ for (const [alias, replacement] of Object.entries(context.alias ?? {})) {
120
+ aliases.push([alias, replacement]);
121
+ }
122
+ aliases.sort((a, b) => b[0].length - a[0].length);
123
+ for (const [alias, replacement] of aliases) {
124
+ if (value === alias)
125
+ return replacement;
126
+ if (value.startsWith(`${alias}/`) || value.startsWith(`${alias}${path.sep}`)) {
127
+ const suffix = value.slice(alias.length + 1);
128
+ return path.resolve(replacement, suffix);
129
+ }
130
+ }
131
+ return value;
132
+ }
133
+ function resolveNuxtPath(value, baseDir, context) {
134
+ const resolvedAlias = resolveNuxtAlias(value, context);
135
+ return path.isAbsolute(resolvedAlias) ? path.resolve(resolvedAlias) : path.resolve(baseDir, resolvedAlias);
136
+ }
137
+ function normalizeNuxtComponentDirs(value, baseDir, context) {
138
+ if (Array.isArray(value)) {
139
+ return value.flatMap((entry) => normalizeNuxtComponentDirs(entry, baseDir, context));
140
+ }
141
+ if (value === false) {
142
+ return [];
143
+ }
144
+ if (value === true || value === void 0) {
145
+ return [
146
+ path.resolve(baseDir, "components/islands"),
147
+ path.resolve(baseDir, "components/global"),
148
+ path.resolve(baseDir, "components")
149
+ ];
150
+ }
151
+ if (typeof value === "string") {
152
+ return [resolveNuxtPath(value, baseDir, context)];
153
+ }
154
+ if (!value || typeof value !== "object") {
155
+ return [];
156
+ }
157
+ if (typeof value === "object" && value && "path" in value && typeof value.path === "string") {
158
+ return [resolveNuxtPath(value.path, baseDir, context)];
159
+ }
160
+ if (typeof value !== "object" || !value || !("dirs" in value)) {
161
+ return [];
162
+ }
163
+ return normalizeNuxtComponentDirs(value.dirs, baseDir, context);
164
+ }
165
+ function resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd = process.cwd()) {
166
+ const rootDir = path.resolve(nuxtOptions.rootDir ?? cwd);
167
+ const srcDir = path.resolve(nuxtOptions.srcDir ?? rootDir);
168
+ const fallbackLayer = {
169
+ cwd: rootDir,
170
+ config: {
171
+ rootDir,
172
+ srcDir,
173
+ dir: nuxtOptions.dir,
174
+ components: nuxtOptions.components,
175
+ alias: nuxtOptions.alias
176
+ }
177
+ };
178
+ const layers = nuxtOptions._layers?.length ? nuxtOptions._layers : [fallbackLayer];
179
+ const normalizedNuxtOptions = {
180
+ ...nuxtOptions,
181
+ _layers: layers
182
+ };
183
+ const layerDirectories = getLayerDirectories({ options: normalizedNuxtOptions });
184
+ const pageDirs = layerDirectories.map((layer) => layer.appPages);
185
+ const layoutDirs = layerDirectories.map((layer) => layer.appLayouts);
186
+ const componentDirs = [];
187
+ for (const [index, layer] of layers.entries()) {
188
+ const layerDirectory = layerDirectories[index];
189
+ const layerRootDir = path.resolve(layerDirectory?.root ?? layer.config?.rootDir ?? layer.cwd ?? rootDir);
190
+ const layerSrcDir = path.resolve(layerDirectory?.app ?? layer.config?.srcDir ?? layer.cwd ?? srcDir);
191
+ const context = {
192
+ rootDir: layerRootDir,
193
+ srcDir: layerSrcDir,
194
+ alias: {
195
+ ...nuxtOptions.alias ?? {},
196
+ ...layer.config?.alias ?? {}
197
+ }
198
+ };
199
+ componentDirs.push(...normalizeNuxtComponentDirs(layer.config?.components, layerSrcDir, context));
200
+ }
201
+ const uniquePageDirs = toUniqueResolvedPaths(pageDirs);
202
+ const uniqueLayoutDirs = toUniqueResolvedPaths(layoutDirs);
203
+ const uniqueComponentDirs = toUniqueResolvedPaths(componentDirs);
204
+ return {
205
+ rootDir,
206
+ srcDir,
207
+ pageDirs: uniquePageDirs,
208
+ layoutDirs: uniqueLayoutDirs,
209
+ componentDirs: uniqueComponentDirs,
210
+ wrapperSearchRoots: []
211
+ };
212
+ }
213
+ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
214
+ let loadNuxtConfig;
215
+ let getLayerDirectories;
216
+ try {
217
+ const nuxtKitEntry = requireFromModule.resolve("@nuxt/kit");
218
+ ({ loadNuxtConfig, getLayerDirectories } = await import(node_url.pathToFileURL(nuxtKitEntry).href));
219
+ } catch (error) {
220
+ throw new TypeError(
221
+ `[vue-pom-generator] Nuxt mode requires @nuxt/kit to be available so Nuxt directories can be resolved from nuxt.config. ${error instanceof Error ? error.message : String(error)}`
222
+ );
223
+ }
224
+ if (typeof loadNuxtConfig !== "function") {
225
+ throw new TypeError("[vue-pom-generator] Nuxt mode requires @nuxt/kit.loadNuxtConfig().");
226
+ }
227
+ if (typeof getLayerDirectories !== "function") {
228
+ throw new TypeError("[vue-pom-generator] Nuxt mode requires @nuxt/kit.getLayerDirectories().");
229
+ }
230
+ const nuxtOptions = await loadNuxtConfig({ cwd });
231
+ return resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd);
232
+ }
107
233
  function createTypeScriptProject() {
108
234
  return new tsMorph.Project({
109
235
  useInMemoryFileSystem: true,
@@ -2796,27 +2922,24 @@ function safeRealpath(value) {
2796
2922
  const resolvedParent = safeRealpath(parent);
2797
2923
  return resolvedParent === parent ? value : path.join(resolvedParent, path.basename(value));
2798
2924
  }
2925
+ function isPathWithinDir(filePathAbs, dirPathAbs) {
2926
+ const fileAbs = path.resolve(filePathAbs);
2927
+ const dirAbs = path.resolve(dirPathAbs);
2928
+ const rel = path.relative(dirAbs, fileAbs);
2929
+ if (!rel)
2930
+ return true;
2931
+ return !rel.startsWith("..") && !path.isAbsolute(rel);
2932
+ }
2799
2933
  function resolveComponentNameFromPath(options) {
2800
- const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
2934
+ const { projectRoot, viewsDirAbs, sourceDirs, extraRoots = [] } = options;
2801
2935
  const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
2802
2936
  const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
2803
2937
  const normalizedAbsFilename = path.normalize(safeRealpath(absFilename));
2804
2938
  const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
2805
- const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
2806
- for (const base of rootBases) {
2807
- for (const dir of scanDirs) {
2808
- const absDir = path.resolve(base, dir);
2809
- try {
2810
- const pagesDir = path.join(absDir, "pages");
2811
- if (fs.existsSync(pagesDir))
2812
- roots.push(pagesDir);
2813
- const componentsDir = path.join(absDir, "components");
2814
- if (fs.existsSync(componentsDir))
2815
- roots.push(componentsDir);
2816
- } catch {
2817
- }
2818
- }
2819
- }
2939
+ const roots = [
2940
+ viewsDirAbs,
2941
+ ...sourceDirs.flatMap((dir) => path.isAbsolute(dir) ? [dir] : rootBases.map((base) => path.resolve(base, dir)))
2942
+ ];
2820
2943
  const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(safeRealpath(r))))).sort((a, b) => b.length - a.length);
2821
2944
  for (const root of potentialRoots) {
2822
2945
  if (normalizedAbsFilename.startsWith(root + path.sep) || normalizedAbsFilename === root) {
@@ -3180,30 +3303,260 @@ function resolveIntrospectedComponentName(componentInfo, componentNaming) {
3180
3303
  filename: componentInfo.filePath,
3181
3304
  projectRoot: componentNaming.projectRoot,
3182
3305
  viewsDirAbs: componentNaming.viewsDirAbs,
3183
- scanDirs: componentNaming.scanDirs,
3306
+ sourceDirs: componentNaming.sourceDirs,
3184
3307
  extraRoots: componentNaming.extraRoots
3185
3308
  });
3186
3309
  }
3187
3310
  return componentInfo.componentName;
3188
3311
  }
3312
+ function hasChildren(parent) {
3313
+ return "children" in parent;
3314
+ }
3315
+ function appendNode(parent, child) {
3316
+ parent.childNodes.push(child);
3317
+ if (hasChildren(parent) && child.nodeType === 1)
3318
+ parent.children.push(child);
3319
+ return child;
3320
+ }
3321
+ function removeNode(parent, child) {
3322
+ const idx = parent.childNodes.indexOf(child);
3323
+ if (idx >= 0)
3324
+ parent.childNodes.splice(idx, 1);
3325
+ if (hasChildren(parent) && child.nodeType === 1) {
3326
+ const childIdx = parent.children.indexOf(child);
3327
+ if (childIdx >= 0)
3328
+ parent.children.splice(childIdx, 1);
3329
+ }
3330
+ return child;
3331
+ }
3332
+ function createMinimalDocument() {
3333
+ const doc = {
3334
+ nodeType: 9,
3335
+ // DOCUMENT_NODE
3336
+ documentElement: null,
3337
+ head: null,
3338
+ body: null,
3339
+ createElement(tag) {
3340
+ const element = {
3341
+ nodeType: 1,
3342
+ tagName: tag.toUpperCase(),
3343
+ childNodes: [],
3344
+ children: [],
3345
+ style: {},
3346
+ dataset: {},
3347
+ setAttribute() {
3348
+ },
3349
+ getAttribute() {
3350
+ return null;
3351
+ },
3352
+ removeAttribute() {
3353
+ },
3354
+ addEventListener() {
3355
+ },
3356
+ removeEventListener() {
3357
+ },
3358
+ appendChild(child) {
3359
+ return appendNode(this, child);
3360
+ },
3361
+ removeChild(child) {
3362
+ return removeNode(this, child);
3363
+ },
3364
+ insertBefore(child) {
3365
+ return appendNode(this, child);
3366
+ },
3367
+ querySelector() {
3368
+ return null;
3369
+ },
3370
+ querySelectorAll() {
3371
+ return [];
3372
+ },
3373
+ contains() {
3374
+ return false;
3375
+ },
3376
+ matches() {
3377
+ return false;
3378
+ },
3379
+ closest() {
3380
+ return null;
3381
+ },
3382
+ getBoundingClientRect() {
3383
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0, toJSON() {
3384
+ return {};
3385
+ } };
3386
+ },
3387
+ cloneNode() {
3388
+ return this;
3389
+ }
3390
+ };
3391
+ return element;
3392
+ },
3393
+ createTextNode(text) {
3394
+ return { nodeType: 3, textContent: text };
3395
+ },
3396
+ createComment(text) {
3397
+ return { nodeType: 8, textContent: text };
3398
+ },
3399
+ createDocumentFragment() {
3400
+ return {
3401
+ nodeType: 11,
3402
+ childNodes: [],
3403
+ appendChild(child) {
3404
+ return appendNode(this, child);
3405
+ }
3406
+ };
3407
+ },
3408
+ getElementById() {
3409
+ return null;
3410
+ },
3411
+ querySelector() {
3412
+ return null;
3413
+ },
3414
+ querySelectorAll() {
3415
+ return [];
3416
+ },
3417
+ addEventListener() {
3418
+ },
3419
+ removeEventListener() {
3420
+ },
3421
+ createEvent() {
3422
+ return {
3423
+ initEvent() {
3424
+ }
3425
+ };
3426
+ },
3427
+ queryCommandSupported() {
3428
+ return false;
3429
+ }
3430
+ };
3431
+ const html = doc.createElement("html");
3432
+ const head = doc.createElement("head");
3433
+ const body = doc.createElement("body");
3434
+ const app = doc.createElement("div");
3435
+ app.id = "app";
3436
+ html.appendChild(head);
3437
+ html.appendChild(body);
3438
+ body.appendChild(app);
3439
+ doc.documentElement = html;
3440
+ doc.head = head;
3441
+ doc.body = body;
3442
+ return Object.assign({}, doc);
3443
+ }
3444
+ function createMinimalLocation() {
3445
+ const url = new URL("https://example.test/");
3446
+ return {
3447
+ get href() {
3448
+ return url.href;
3449
+ },
3450
+ set href(v) {
3451
+ try {
3452
+ Object.assign(url, new URL(v));
3453
+ } catch {
3454
+ }
3455
+ },
3456
+ get origin() {
3457
+ return url.origin;
3458
+ },
3459
+ get protocol() {
3460
+ return url.protocol;
3461
+ },
3462
+ get host() {
3463
+ return url.host;
3464
+ },
3465
+ get hostname() {
3466
+ return url.hostname;
3467
+ },
3468
+ get port() {
3469
+ return url.port;
3470
+ },
3471
+ get pathname() {
3472
+ return url.pathname;
3473
+ },
3474
+ set pathname(v) {
3475
+ url.pathname = v;
3476
+ },
3477
+ get search() {
3478
+ return url.search;
3479
+ },
3480
+ set search(v) {
3481
+ url.search = v;
3482
+ },
3483
+ get hash() {
3484
+ return url.hash;
3485
+ },
3486
+ set hash(v) {
3487
+ url.hash = v;
3488
+ },
3489
+ assign() {
3490
+ },
3491
+ reload() {
3492
+ },
3493
+ replace() {
3494
+ },
3495
+ toString() {
3496
+ return url.href;
3497
+ },
3498
+ ancestorOrigins: { length: 0, contains: () => false, item: () => null, [Symbol.iterator]: [][Symbol.iterator] }
3499
+ };
3500
+ }
3189
3501
  async function ensureDomShim() {
3190
- const domShimHtml = "<!doctype html><html><head></head><body><div id='app'></div></body></html>";
3191
- const domShimUrl = "https://example.test/";
3192
3502
  const g = globalThis;
3193
3503
  if (typeof document !== "undefined" && typeof window !== "undefined")
3194
3504
  return;
3195
- const dom = new jsdom.JSDOM(domShimHtml, { url: domShimUrl });
3196
- g.window = dom.window;
3197
- g.document = dom.window.document;
3198
- g.location = dom.window.location;
3505
+ const minimalDoc = createMinimalDocument();
3506
+ const minimalLocation = createMinimalLocation();
3507
+ const win = {
3508
+ document: minimalDoc,
3509
+ location: minimalLocation,
3510
+ navigator: { userAgent: "node" },
3511
+ history: { pushState() {
3512
+ }, replaceState() {
3513
+ }, go() {
3514
+ }, back() {
3515
+ }, forward() {
3516
+ }, state: null, length: 0, scrollRestoration: "auto" },
3517
+ addEventListener() {
3518
+ },
3519
+ removeEventListener() {
3520
+ },
3521
+ dispatchEvent() {
3522
+ return true;
3523
+ },
3524
+ getComputedStyle() {
3525
+ return {};
3526
+ },
3527
+ scrollTo() {
3528
+ },
3529
+ scroll() {
3530
+ },
3531
+ scrollBy() {
3532
+ },
3533
+ matchMedia() {
3534
+ return { matches: false, media: "", onchange: null, addListener() {
3535
+ }, removeListener() {
3536
+ }, addEventListener() {
3537
+ }, removeEventListener() {
3538
+ }, dispatchEvent() {
3539
+ return true;
3540
+ } };
3541
+ },
3542
+ requestAnimationFrame: (cb) => setTimeout(() => cb(Date.now()), 16),
3543
+ cancelAnimationFrame: (id) => clearTimeout(id),
3544
+ setTimeout,
3545
+ clearTimeout,
3546
+ setInterval,
3547
+ clearInterval,
3548
+ queueMicrotask,
3549
+ performance: globalThis.performance
3550
+ };
3551
+ g.window = win;
3552
+ g.document = minimalDoc;
3553
+ g.location = minimalLocation;
3199
3554
  if (!g.self)
3200
- g.self = dom.window;
3555
+ g.self = win;
3201
3556
  if (!g.navigator)
3202
- g.navigator = dom.window.navigator;
3557
+ g.navigator = win.navigator;
3203
3558
  if (!g.history)
3204
- g.history = { pushState() {
3205
- }, replaceState() {
3206
- } };
3559
+ g.history = win.history;
3207
3560
  if (!g.MutationObserver) {
3208
3561
  g.MutationObserver = class {
3209
3562
  disconnect() {
@@ -3241,10 +3594,6 @@ async function ensureDomShim() {
3241
3594
  if (!g.requestIdleCallback) {
3242
3595
  g.requestIdleCallback = (cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1);
3243
3596
  }
3244
- const doc = g.document;
3245
- if (doc && typeof doc.queryCommandSupported !== "function") {
3246
- doc.queryCommandSupported = () => false;
3247
- }
3248
3597
  if (!g.localStorage || !g.sessionStorage) {
3249
3598
  const storageFactory = () => {
3250
3599
  const store = /* @__PURE__ */ new Map();
@@ -3270,91 +3619,100 @@ async function ensureDomShim() {
3270
3619
  if (!g.sessionStorage)
3271
3620
  g.sessionStorage = storageFactory();
3272
3621
  }
3273
- const names = Object.getOwnPropertyNames(dom.window);
3274
- const shouldCopyGlobal = (name) => {
3275
- if (name === "Node" || name === "Element" || name === "Document" || name === "Event" || name === "EventTarget")
3276
- return true;
3277
- if (name.endsWith("Event"))
3278
- return true;
3279
- if (name.startsWith("HTML") && name.endsWith("Element"))
3280
- return true;
3281
- if (name.startsWith("SVG") && name.endsWith("Element"))
3282
- return true;
3283
- return false;
3284
- };
3285
- for (const name of names) {
3286
- if (!shouldCopyGlobal(name) || g[name])
3287
- continue;
3288
- const value = Reflect.get(dom.window, name);
3289
- if (value)
3290
- g[name] = value;
3291
- }
3292
3622
  if (!g.requestAnimationFrame)
3293
3623
  g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 16);
3294
3624
  }
3295
- async function introspectNuxtPages(projectRoot) {
3296
- const possiblePagesDirs = ["app/pages", "pages"];
3297
- let pagesDir = "";
3298
- for (const dir of possiblePagesDirs) {
3299
- const abs = path.resolve(projectRoot, dir);
3300
- if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
3301
- pagesDir = abs;
3625
+ function unwrapNuxtPageSegment(segment, prefix, suffix) {
3626
+ if (!segment.startsWith(prefix) || !segment.endsWith(suffix))
3627
+ return null;
3628
+ const value = segment.slice(prefix.length, segment.length - suffix.length);
3629
+ return value.length > 0 ? value : null;
3630
+ }
3631
+ function resolveNuxtPageSegment(segment) {
3632
+ if (segment === "index") {
3633
+ return { pathPart: "", params: [] };
3634
+ }
3635
+ const optionalParamName = unwrapNuxtPageSegment(segment, "[[", "]]");
3636
+ if (optionalParamName) {
3637
+ return {
3638
+ pathPart: `:${optionalParamName}?`,
3639
+ params: [{ name: optionalParamName, optional: true }]
3640
+ };
3641
+ }
3642
+ const catchAllParamName = unwrapNuxtPageSegment(segment, "[...", "]");
3643
+ if (catchAllParamName) {
3644
+ return {
3645
+ pathPart: `:${catchAllParamName}(.*)*`,
3646
+ params: [{ name: catchAllParamName, optional: false }]
3647
+ };
3648
+ }
3649
+ const requiredParamName = unwrapNuxtPageSegment(segment, "[", "]");
3650
+ if (requiredParamName) {
3651
+ return {
3652
+ pathPart: `:${requiredParamName}`,
3653
+ params: [{ name: requiredParamName, optional: false }]
3654
+ };
3655
+ }
3656
+ return { pathPart: segment, params: [] };
3657
+ }
3658
+ function toPathSegments(value) {
3659
+ const segments = [];
3660
+ let current = path.normalize(value);
3661
+ while (current && current !== "." && current !== path.sep) {
3662
+ const parsed = path.parse(current);
3663
+ if (!parsed.base || parsed.base === ".")
3302
3664
  break;
3303
- }
3665
+ segments.unshift(parsed.base);
3666
+ if (!parsed.dir || parsed.dir === "." || parsed.dir === current)
3667
+ break;
3668
+ current = parsed.dir;
3304
3669
  }
3305
- if (!pagesDir) {
3670
+ return segments;
3671
+ }
3672
+ async function introspectNuxtPages(projectRoot, options = {}) {
3673
+ const possiblePageDirs = options.pageDirs?.length ? options.pageDirs : ["app/pages", "pages"].map((dir) => path.resolve(projectRoot, dir));
3674
+ const pageDirs = possiblePageDirs.map((dir) => path.resolve(projectRoot, dir)).filter((dir) => {
3675
+ try {
3676
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
3677
+ } catch {
3678
+ return false;
3679
+ }
3680
+ });
3681
+ if (!pageDirs.length) {
3306
3682
  debugLog(`[router-introspection][nuxt] Could not find pages directory in ${projectRoot}`);
3307
3683
  return { routeNameMap: /* @__PURE__ */ new Map(), routePathMap: /* @__PURE__ */ new Map(), routeMetaEntries: [] };
3308
3684
  }
3685
+ const routePathMap = /* @__PURE__ */ new Map();
3309
3686
  const routeMetaEntries = [];
3310
- const walk = (dir, baseRoute) => {
3687
+ const walk = (pagesDir, dir) => {
3311
3688
  const files = fs.readdirSync(dir);
3312
3689
  for (const file of files) {
3313
3690
  const fullPath = path.join(dir, file);
3314
3691
  const stat = fs.statSync(fullPath);
3315
3692
  if (stat.isDirectory()) {
3316
- walk(fullPath, `${baseRoute}/${file}`);
3693
+ walk(pagesDir, fullPath);
3317
3694
  continue;
3318
3695
  }
3319
3696
  if (!file.endsWith(".vue"))
3320
3697
  continue;
3321
- const componentName = file.slice(0, -4);
3322
- if (componentName === "index" && baseRoute === "") {
3323
- routeMetaEntries.push({
3324
- componentName: "index",
3325
- pathTemplate: "/",
3326
- params: [],
3327
- query: []
3328
- });
3329
- continue;
3330
- }
3331
- let routePath = componentName === "index" ? baseRoute : `${baseRoute}/${componentName}`;
3332
- if (!routePath.startsWith("/"))
3333
- routePath = `/${routePath}`;
3698
+ const componentName = resolveComponentNameFromPath({
3699
+ filename: fullPath,
3700
+ projectRoot,
3701
+ viewsDirAbs: pagesDir,
3702
+ sourceDirs: [pagesDir],
3703
+ extraRoots: [process.cwd()]
3704
+ });
3705
+ const relativePath = path.relative(pagesDir, fullPath);
3706
+ const parsed = path.parse(relativePath);
3707
+ const routeSegments = toPathSegments(path.join(parsed.dir, parsed.name));
3334
3708
  const params = [];
3335
- let pathTemplate = "";
3336
- for (let i = 0; i < routePath.length; i++) {
3337
- const ch = routePath[i];
3338
- if (ch !== "[") {
3339
- pathTemplate += ch;
3340
- continue;
3341
- }
3342
- let name = "";
3343
- i++;
3344
- while (i < routePath.length) {
3345
- const c = routePath[i];
3346
- if (c === "]")
3347
- break;
3348
- name += c;
3349
- i++;
3350
- }
3351
- if (name) {
3352
- params.push({ name, optional: false });
3353
- pathTemplate += `:${name}`;
3354
- } else {
3355
- pathTemplate += "[]";
3356
- }
3357
- }
3709
+ const pathParts = routeSegments.flatMap((segment) => {
3710
+ const resolution = resolveNuxtPageSegment(segment);
3711
+ params.push(...resolution.params);
3712
+ return resolution.pathPart ? [resolution.pathPart] : [];
3713
+ });
3714
+ const pathTemplate = pathParts.length ? `/${pathParts.join("/")}` : "/";
3715
+ routePathMap.set(pathTemplate, componentName);
3358
3716
  routeMetaEntries.push({
3359
3717
  componentName,
3360
3718
  pathTemplate,
@@ -3363,10 +3721,12 @@ async function introspectNuxtPages(projectRoot) {
3363
3721
  });
3364
3722
  }
3365
3723
  };
3366
- walk(pagesDir, "");
3724
+ for (const pageDir of pageDirs) {
3725
+ walk(pageDir, pageDir);
3726
+ }
3367
3727
  return {
3368
3728
  routeNameMap: /* @__PURE__ */ new Map(),
3369
- routePathMap: /* @__PURE__ */ new Map(),
3729
+ routePathMap,
3370
3730
  routeMetaEntries
3371
3731
  };
3372
3732
  }
@@ -3662,15 +4022,20 @@ function resolveVueSourcePath(targetClassName, vueFilesPathMap, projectRoot) {
3662
4022
  }
3663
4023
  async function getRouteMetaByComponent(projectRoot, routerEntry, routerType, options = {}) {
3664
4024
  const root = projectRoot ?? process.cwd();
3665
- const viewsDir = options.viewsDir ?? "src/views";
3666
- const viewsDirAbs = path.isAbsolute(viewsDir) ? viewsDir : path.resolve(root, viewsDir);
3667
- const scanDirs = options.scanDirs?.length ? options.scanDirs : ["src"];
4025
+ const pageDirs = options.pageDirs?.length ? options.pageDirs : ["src/views"];
4026
+ const pageDirsAbs = pageDirs.map((dir) => path.isAbsolute(dir) ? dir : path.resolve(root, dir));
4027
+ const primaryPageDirAbs = pageDirsAbs[0] ?? path.resolve(root, "src/views");
4028
+ const sourceDirs = [
4029
+ ...pageDirs,
4030
+ ...options.componentDirs?.length ? options.componentDirs : ["src/components"],
4031
+ ...options.layoutDirs?.length ? options.layoutDirs : ["src/layouts"]
4032
+ ];
3668
4033
  const extraRoots = process.cwd() !== root ? [process.cwd()] : [];
3669
- const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
4034
+ const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root, { pageDirs: pageDirsAbs }) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
3670
4035
  componentNaming: {
3671
4036
  projectRoot: root,
3672
- viewsDirAbs,
3673
- scanDirs,
4037
+ viewsDirAbs: primaryPageDirAbs,
4038
+ sourceDirs,
3674
4039
  extraRoots
3675
4040
  }
3676
4041
  });
@@ -3870,15 +4235,17 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3870
4235
  vueRouterFluentChaining,
3871
4236
  routerEntry,
3872
4237
  routerType,
3873
- viewsDir,
3874
- scanDirs,
4238
+ pageDirs,
4239
+ componentDirs,
4240
+ layoutDirs,
3875
4241
  routeMetaByComponent: routeMetaByComponentOverride
3876
4242
  } = options;
3877
4243
  const emitLanguages = emitLanguagesOverride?.length ? emitLanguagesOverride : ["ts"];
3878
4244
  const outDir = outDirOverride ?? "./pom";
3879
4245
  const routeMetaByComponent = routeMetaByComponentOverride ?? (vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType, {
3880
- viewsDir,
3881
- scanDirs
4246
+ pageDirs,
4247
+ componentDirs,
4248
+ layoutDirs
3882
4249
  }) : void 0);
3883
4250
  const generatedFilePaths = [];
3884
4251
  const writeGeneratedFile = (file) => {
@@ -4223,7 +4590,7 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
4223
4590
  " }",
4224
4591
  "",
4225
4592
  " // Minimal vue-select helper mirroring the TS BasePage.selectVSelectByTestId behavior.",
4226
- " // Note: annotationText is currently a no-op in C# output (we don't render a cursor overlay).",
4593
+ " // Note: annotationText is currently a no-op in C# output (we don't render a pointer overlay).",
4227
4594
  " protected async Task SelectVSelectByTestIdAsync(string testId, string value, int timeOut = 500)",
4228
4595
  " {",
4229
4596
  " var root = LocatorByTestId(testId);",
@@ -6183,7 +6550,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
6183
6550
  const existingIdBehavior = options.existingIdBehavior ?? "preserve";
6184
6551
  const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
6185
6552
  const nameCollisionBehavior = options.nameCollisionBehavior ?? "suffix";
6186
- const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "ignore";
6553
+ const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "error";
6187
6554
  const warn = options.warn;
6188
6555
  const vueFilesPathMap = options.vueFilesPathMap;
6189
6556
  const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
@@ -6667,8 +7034,11 @@ function createBuildProcessorPlugin(options) {
6667
7034
  const {
6668
7035
  componentHierarchyMap,
6669
7036
  vueFilesPathMap,
6670
- viewsDir,
6671
- scanDirs,
7037
+ getPageDirs,
7038
+ getComponentDirs,
7039
+ getLayoutDirs,
7040
+ getViewsDir,
7041
+ getSourceDirs,
6672
7042
  basePageClassPath,
6673
7043
  normalizedBasePagePath,
6674
7044
  outDir,
@@ -6683,13 +7053,13 @@ function createBuildProcessorPlugin(options) {
6683
7053
  customPomImportNameCollisionBehavior,
6684
7054
  testIdAttribute,
6685
7055
  nameCollisionBehavior,
6686
- missingSemanticNameBehavior,
7056
+ missingSemanticNameBehavior = "error",
6687
7057
  existingIdBehavior,
6688
7058
  nativeWrappers,
6689
7059
  excludedComponents,
6690
7060
  getWrapperSearchRoots,
6691
7061
  routerAwarePoms,
6692
- resolvedRouterEntry,
7062
+ getResolvedRouterEntry,
6693
7063
  routerType,
6694
7064
  routerModuleShims,
6695
7065
  loggerRef
@@ -6699,7 +7069,8 @@ function createBuildProcessorPlugin(options) {
6699
7069
  interactiveComponentCount: 0,
6700
7070
  dataTestIdCount: 0
6701
7071
  };
6702
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
7072
+ const getViewsDirAbs = () => path.isAbsolute(getViewsDir()) ? getViewsDir() : path.resolve(projectRootRef.current, getViewsDir());
7073
+ const getPageDirsAbs = () => getPageDirs().map((dir) => path.isAbsolute(dir) ? dir : path.resolve(projectRootRef.current, dir));
6703
7074
  const getScriptInfo = (source, filename) => {
6704
7075
  try {
6705
7076
  const { descriptor } = compilerSfc.parse(source, { filename });
@@ -6739,7 +7110,7 @@ function createBuildProcessorPlugin(options) {
6739
7110
  return out;
6740
7111
  };
6741
7112
  let supplemented = 0;
6742
- for (const dir of scanDirs) {
7113
+ for (const dir of getSourceDirs()) {
6743
7114
  const absDir = path.resolve(projectRootRef.current, dir);
6744
7115
  if (!fs.existsSync(absDir))
6745
7116
  continue;
@@ -6749,7 +7120,7 @@ function createBuildProcessorPlugin(options) {
6749
7120
  filename: absolutePath,
6750
7121
  projectRoot: projectRootRef.current,
6751
7122
  viewsDirAbs: getViewsDirAbs(),
6752
- scanDirs,
7123
+ sourceDirs: getSourceDirs(),
6753
7124
  extraRoots: [process.cwd()]
6754
7125
  });
6755
7126
  if (componentHierarchyMap.has(componentName))
@@ -6824,16 +7195,17 @@ function createBuildProcessorPlugin(options) {
6824
7195
  }
6825
7196
  let result;
6826
7197
  if (routerType === "nuxt") {
6827
- result = await introspectNuxtPages(projectRootRef.current);
7198
+ result = await introspectNuxtPages(projectRootRef.current, { pageDirs: getPageDirsAbs() });
6828
7199
  } else {
7200
+ const resolvedRouterEntry = getResolvedRouterEntry();
6829
7201
  if (!resolvedRouterEntry)
6830
7202
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
6831
7203
  result = await parseRouterFileFromCwd(resolvedRouterEntry, {
6832
7204
  moduleShims: routerModuleShims,
6833
7205
  componentNaming: {
6834
7206
  projectRoot: projectRootRef.current,
6835
- viewsDirAbs: path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir),
6836
- scanDirs
7207
+ viewsDirAbs: getViewsDirAbs(),
7208
+ sourceDirs: getSourceDirs()
6837
7209
  }
6838
7210
  });
6839
7211
  }
@@ -6867,6 +7239,10 @@ function createBuildProcessorPlugin(options) {
6867
7239
  this.error(`callout.ts not found at ${calloutPath}. Ensure it is included in the build.`);
6868
7240
  }
6869
7241
  this.addWatchFile(calloutPath);
7242
+ const floatingUiCalloutPath = path.resolve(path.dirname(basePageClassPath), "floating-ui-callout.ts");
7243
+ if (fs.existsSync(floatingUiCalloutPath)) {
7244
+ this.addWatchFile(floatingUiCalloutPath);
7245
+ }
6870
7246
  },
6871
7247
  async buildEnd(error) {
6872
7248
  if (error) {
@@ -6893,10 +7269,11 @@ function createBuildProcessorPlugin(options) {
6893
7269
  customPomImportNameCollisionBehavior,
6894
7270
  testIdAttribute,
6895
7271
  vueRouterFluentChaining: routerAwarePoms,
6896
- routerEntry: resolvedRouterEntry,
7272
+ routerEntry: getResolvedRouterEntry(),
6897
7273
  routerType,
6898
- viewsDir,
6899
- scanDirs
7274
+ pageDirs: getPageDirs(),
7275
+ componentDirs: getComponentDirs(),
7276
+ layoutDirs: getLayoutDirs()
6900
7277
  });
6901
7278
  lastGeneratedMetrics = metrics;
6902
7279
  loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
@@ -6910,8 +7287,11 @@ function createDevProcessorPlugin(options) {
6910
7287
  const {
6911
7288
  nativeWrappers,
6912
7289
  excludedComponents,
6913
- viewsDir,
6914
- scanDirs,
7290
+ getPageDirs,
7291
+ getComponentDirs,
7292
+ getLayoutDirs,
7293
+ getViewsDir,
7294
+ getSourceDirs,
6915
7295
  getWrapperSearchRoots,
6916
7296
  projectRootRef,
6917
7297
  normalizedBasePagePath,
@@ -6926,16 +7306,33 @@ function createDevProcessorPlugin(options) {
6926
7306
  customPomImportAliases,
6927
7307
  customPomImportNameCollisionBehavior,
6928
7308
  nameCollisionBehavior = "suffix",
6929
- missingSemanticNameBehavior,
7309
+ missingSemanticNameBehavior = "error",
6930
7310
  existingIdBehavior,
6931
7311
  testIdAttribute,
6932
7312
  routerAwarePoms,
6933
- resolvedRouterEntry,
7313
+ getResolvedRouterEntry,
6934
7314
  routerType,
6935
7315
  routerModuleShims,
6936
7316
  loggerRef
6937
7317
  } = options;
6938
7318
  let scheduleVueFileRegen = null;
7319
+ const getProjectRootCandidates = () => Array.from(/* @__PURE__ */ new Set([
7320
+ path.resolve(projectRootRef.current),
7321
+ path.resolve(process.cwd())
7322
+ ]));
7323
+ const resolveProjectPath = (maybePath) => {
7324
+ if (path.isAbsolute(maybePath))
7325
+ return maybePath;
7326
+ const candidates = getProjectRootCandidates().map((root) => path.resolve(root, maybePath));
7327
+ return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
7328
+ };
7329
+ const getSourceDirRoots = () => Array.from(new Set(
7330
+ getProjectRootCandidates().flatMap((root) => getSourceDirs().map((dir) => path.resolve(root, dir)))
7331
+ ));
7332
+ const isContainedInScanDirs = (filePath) => {
7333
+ const absolutePath = path.resolve(filePath);
7334
+ return getSourceDirRoots().some((scanDirAbs) => isPathWithinDir(absolutePath, scanDirAbs));
7335
+ };
6939
7336
  return {
6940
7337
  name: "vue-pom-generator-dev",
6941
7338
  apply: "serve",
@@ -6946,16 +7343,13 @@ function createDevProcessorPlugin(options) {
6946
7343
  return;
6947
7344
  if (!ctx.file.endsWith(".vue"))
6948
7345
  return;
6949
- const isContainedInScanDirs = scanDirs.some((dir) => {
6950
- const absDir = path.resolve(projectRootRef.current, dir);
6951
- return ctx.file.startsWith(absDir + path.sep);
6952
- });
6953
- if (!isContainedInScanDirs)
7346
+ if (!isContainedInScanDirs(ctx.file))
6954
7347
  return;
6955
7348
  scheduleVueFileRegen(ctx.file, "hmr");
6956
7349
  },
6957
7350
  async configureServer(server) {
6958
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
7351
+ const getViewsDirAbs = () => resolveProjectPath(getViewsDir());
7352
+ const getPageDirsAbs = () => getPageDirs().map((dir) => resolveProjectPath(dir));
6959
7353
  const routerInitPromise = (async () => {
6960
7354
  if (!routerAwarePoms) {
6961
7355
  setRouteNameToComponentNameMap(/* @__PURE__ */ new Map());
@@ -6964,8 +7358,9 @@ function createDevProcessorPlugin(options) {
6964
7358
  }
6965
7359
  let result;
6966
7360
  if (routerType === "nuxt") {
6967
- result = await introspectNuxtPages(projectRootRef.current);
7361
+ result = await introspectNuxtPages(projectRootRef.current, { pageDirs: getPageDirsAbs() });
6968
7362
  } else {
7363
+ const resolvedRouterEntry = getResolvedRouterEntry();
6969
7364
  if (!resolvedRouterEntry)
6970
7365
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
6971
7366
  result = await parseRouterFileFromCwd(resolvedRouterEntry, {
@@ -6973,7 +7368,7 @@ function createDevProcessorPlugin(options) {
6973
7368
  componentNaming: {
6974
7369
  projectRoot: projectRootRef.current,
6975
7370
  viewsDirAbs: getViewsDirAbs(),
6976
- scanDirs,
7371
+ sourceDirs: getSourceDirs(),
6977
7372
  extraRoots: [process.cwd()]
6978
7373
  }
6979
7374
  });
@@ -7045,6 +7440,19 @@ function createDevProcessorPlugin(options) {
7045
7440
  let snapshotHierarchy = /* @__PURE__ */ new Map();
7046
7441
  let snapshotVuePathMap = /* @__PURE__ */ new Map();
7047
7442
  const filePathToComponentName = /* @__PURE__ */ new Map();
7443
+ const createEmptyComponentDependencies = (absolutePath) => {
7444
+ const viewsDirAbs = path.resolve(getViewsDirAbs());
7445
+ const relToViewsDir = path.relative(viewsDirAbs, absolutePath);
7446
+ const isView = !relToViewsDir.startsWith("..") && !path.isAbsolute(relToViewsDir);
7447
+ return {
7448
+ filePath: absolutePath,
7449
+ childrenComponentSet: /* @__PURE__ */ new Set(),
7450
+ usedComponentSet: /* @__PURE__ */ new Set(),
7451
+ dataTestIdSet: /* @__PURE__ */ new Set(),
7452
+ isView,
7453
+ methodsContent: ""
7454
+ };
7455
+ };
7048
7456
  const getComponentNameForFile = (filePath) => {
7049
7457
  const normalized = path.resolve(filePath);
7050
7458
  const existing = filePathToComponentName.get(normalized);
@@ -7054,7 +7462,7 @@ function createDevProcessorPlugin(options) {
7054
7462
  filename: normalized,
7055
7463
  projectRoot: projectRootRef.current,
7056
7464
  viewsDirAbs: getViewsDirAbs(),
7057
- scanDirs,
7465
+ sourceDirs: getSourceDirs(),
7058
7466
  extraRoots: [process.cwd()]
7059
7467
  });
7060
7468
  filePathToComponentName.set(normalized, name);
@@ -7064,8 +7472,6 @@ function createDevProcessorPlugin(options) {
7064
7472
  const started = node_perf_hooks.performance.now();
7065
7473
  const absolutePath = path.resolve(filePath);
7066
7474
  const componentName = getComponentNameForFile(absolutePath);
7067
- targetVuePathMap.set(componentName, absolutePath);
7068
- targetHierarchy.delete(componentName);
7069
7475
  let sfc = "";
7070
7476
  try {
7071
7477
  sfc = fs.readFileSync(absolutePath, "utf8");
@@ -7073,9 +7479,15 @@ function createDevProcessorPlugin(options) {
7073
7479
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: false };
7074
7480
  }
7075
7481
  const template = extractTemplateFromSfc(sfc, absolutePath);
7076
- if (!template.trim())
7482
+ if (!template.trim()) {
7483
+ targetVuePathMap.set(componentName, absolutePath);
7484
+ targetHierarchy.set(componentName, createEmptyComponentDependencies(absolutePath));
7077
7485
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: true };
7486
+ }
7078
7487
  const { bindings: bindingMetadata, isScriptSetup } = getScriptInfo(sfc, absolutePath);
7488
+ const provisionalHierarchy = /* @__PURE__ */ new Map();
7489
+ const provisionalVuePathMap = new Map(targetVuePathMap);
7490
+ provisionalVuePathMap.set(componentName, absolutePath);
7079
7491
  compilerDom__namespace.compile(template, {
7080
7492
  filename: absolutePath,
7081
7493
  prefixIdentifiers: true,
@@ -7084,7 +7496,7 @@ function createDevProcessorPlugin(options) {
7084
7496
  nodeTransforms: [
7085
7497
  createTestIdTransform(
7086
7498
  componentName,
7087
- targetHierarchy,
7499
+ provisionalHierarchy,
7088
7500
  nativeWrappers,
7089
7501
  excludedComponents,
7090
7502
  getViewsDirAbs(),
@@ -7094,23 +7506,27 @@ function createDevProcessorPlugin(options) {
7094
7506
  missingSemanticNameBehavior,
7095
7507
  testIdAttribute,
7096
7508
  warn: (message) => loggerRef.current.warn(message),
7097
- vueFilesPathMap: targetVuePathMap,
7509
+ vueFilesPathMap: provisionalVuePathMap,
7098
7510
  wrapperSearchRoots: getWrapperSearchRoots()
7099
7511
  }
7100
7512
  )
7101
7513
  ]
7102
7514
  });
7515
+ targetVuePathMap.set(componentName, absolutePath);
7516
+ targetHierarchy.set(
7517
+ componentName,
7518
+ provisionalHierarchy.get(componentName) ?? createEmptyComponentDependencies(absolutePath)
7519
+ );
7103
7520
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: true };
7104
7521
  };
7105
- const fullRebuildSnapshotFromFilesystem = () => {
7522
+ const fullRebuildSnapshotFromFilesystem = (logLabel) => {
7106
7523
  const t0 = node_perf_hooks.performance.now();
7107
7524
  const nextHierarchy = /* @__PURE__ */ new Map();
7108
7525
  const nextVuePathMap = /* @__PURE__ */ new Map();
7109
7526
  filePathToComponentName.clear();
7110
7527
  let totalVueFiles = 0;
7111
7528
  let compiledCount = 0;
7112
- for (const dir of scanDirs) {
7113
- const absDir = path.resolve(projectRootRef.current, dir);
7529
+ for (const absDir of getSourceDirRoots()) {
7114
7530
  if (!fs.existsSync(absDir))
7115
7531
  continue;
7116
7532
  const vueFiles = walkFilesRecursive(absDir);
@@ -7124,12 +7540,12 @@ function createDevProcessorPlugin(options) {
7124
7540
  snapshotHierarchy = nextHierarchy;
7125
7541
  snapshotVuePathMap = nextVuePathMap;
7126
7542
  const t1 = node_perf_hooks.performance.now();
7127
- logInfo(`initial scan: found ${totalVueFiles} .vue files in ${scanDirs.join(", ")}`);
7128
- logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
7543
+ logInfo(`scan(${logLabel}): found ${totalVueFiles} .vue files in ${getSourceDirs().join(", ")}`);
7544
+ logInfo(`compile(${logLabel}): ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
7129
7545
  };
7130
- const generateAggregatedFromSnapshot = (reason) => {
7546
+ const generateAggregatedFromSnapshot = async (logLabel) => {
7131
7547
  const t0 = node_perf_hooks.performance.now();
7132
- generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
7548
+ await generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
7133
7549
  outDir,
7134
7550
  emitLanguages,
7135
7551
  typescriptOutputStructure,
@@ -7140,25 +7556,27 @@ function createDevProcessorPlugin(options) {
7140
7556
  customPomDir,
7141
7557
  customPomImportAliases,
7142
7558
  customPomImportNameCollisionBehavior,
7143
- viewsDir,
7144
- scanDirs,
7559
+ pageDirs: getPageDirs(),
7560
+ componentDirs: getComponentDirs(),
7561
+ layoutDirs: getLayoutDirs(),
7145
7562
  testIdAttribute,
7146
7563
  vueRouterFluentChaining: routerAwarePoms,
7147
- routerEntry: resolvedRouterEntry,
7564
+ routerEntry: getResolvedRouterEntry(),
7148
7565
  routerType
7149
7566
  });
7150
7567
  const t1 = node_perf_hooks.performance.now();
7151
- logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
7568
+ logInfo(`generate(${logLabel}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
7152
7569
  };
7153
7570
  let timer = null;
7154
7571
  let maxWaitTimer = null;
7155
7572
  const pendingChangedVueFiles = /* @__PURE__ */ new Set();
7156
7573
  const pendingDeletedComponents = /* @__PURE__ */ new Set();
7574
+ let regenerationSequence = Promise.resolve();
7157
7575
  const initialBuildPromise = (async () => {
7158
7576
  const t0 = node_perf_hooks.performance.now();
7159
7577
  await routerInitPromise;
7160
- fullRebuildSnapshotFromFilesystem();
7161
- generateAggregatedFromSnapshot("startup");
7578
+ fullRebuildSnapshotFromFilesystem("startup");
7579
+ await generateAggregatedFromSnapshot("startup");
7162
7580
  const t1 = node_perf_hooks.performance.now();
7163
7581
  logInfo(`startup total: ${formatMs(t1 - t0)}`);
7164
7582
  })();
@@ -7186,7 +7604,7 @@ function createDevProcessorPlugin(options) {
7186
7604
  snapshotHierarchy = nextHierarchy;
7187
7605
  snapshotVuePathMap = nextVuePathMap;
7188
7606
  const t1 = node_perf_hooks.performance.now();
7189
- generateAggregatedFromSnapshot(reason);
7607
+ await generateAggregatedFromSnapshot(reason);
7190
7608
  const t2 = node_perf_hooks.performance.now();
7191
7609
  return {
7192
7610
  files,
@@ -7197,7 +7615,12 @@ function createDevProcessorPlugin(options) {
7197
7615
  totalMs: t2 - t0
7198
7616
  };
7199
7617
  };
7200
- const watchedVueGlobs = scanDirs.map((dir) => path.resolve(projectRootRef.current, dir, "**", "*.vue"));
7618
+ const enqueueRegeneration = (reason) => {
7619
+ const currentRun = regenerationSequence.catch(() => void 0).then(() => regenerateFromPending(reason));
7620
+ regenerationSequence = currentRun.then(() => void 0, () => void 0);
7621
+ return currentRun;
7622
+ };
7623
+ const watchedVueGlobs = getSourceDirRoots().map((scanDirAbs) => path.resolve(scanDirAbs, "**", "*.vue"));
7201
7624
  const watchedPluginGlob = path.resolve(projectRootRef.current, "vite-plugins", "vue-pom-generator", "**", "*.ts");
7202
7625
  const runtimeDir = path.dirname(basePageClassPath);
7203
7626
  server.watcher.add([
@@ -7205,7 +7628,8 @@ function createDevProcessorPlugin(options) {
7205
7628
  watchedPluginGlob,
7206
7629
  basePageClassPath,
7207
7630
  path.resolve(runtimeDir, "pointer.ts"),
7208
- path.resolve(runtimeDir, "callout.ts")
7631
+ path.resolve(runtimeDir, "callout.ts"),
7632
+ path.resolve(runtimeDir, "floating-ui-callout.ts")
7209
7633
  ]);
7210
7634
  scheduleVueFileRegenLocal = (filePath, source) => {
7211
7635
  pendingChangedVueFiles.add(filePath);
@@ -7223,7 +7647,7 @@ function createDevProcessorPlugin(options) {
7223
7647
  timer = null;
7224
7648
  }
7225
7649
  maxWaitTimer = null;
7226
- void regenerateFromPending("max-wait").then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7650
+ void enqueueRegeneration("max-wait").then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7227
7651
  logInfo(
7228
7652
  `max-wait: files=${files.length} deleted=${deletedCount} compile=${formatMs(compileMs)} wall=${formatMs(preGenerateMs)} gen=${formatMs(generateMs)} total=${formatMs(totalMs)}`
7229
7653
  );
@@ -7247,7 +7671,7 @@ function createDevProcessorPlugin(options) {
7247
7671
  maxWaitTimer = null;
7248
7672
  }
7249
7673
  const reason = pendingChangedVueFiles.size || pendingDeletedComponents.size ? "batched" : "noop";
7250
- void regenerateFromPending(reason).then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7674
+ void enqueueRegeneration(reason).then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7251
7675
  if (files.length || deletedCount) {
7252
7676
  logInfo(
7253
7677
  `batched: files=${files.length} deleted=${deletedCount} compile=${formatMs(compileMs)} wall=${formatMs(preGenerateMs)} gen=${formatMs(generateMs)} total=${formatMs(totalMs)}`
@@ -7274,11 +7698,7 @@ function createDevProcessorPlugin(options) {
7274
7698
  return;
7275
7699
  if (!p.endsWith(".vue"))
7276
7700
  return;
7277
- const isContainedInScanDirs = scanDirs.some((dir) => {
7278
- const absDir = path.resolve(projectRootRef.current, dir);
7279
- return p.startsWith(absDir + path.sep);
7280
- });
7281
- if (!isContainedInScanDirs)
7701
+ if (!isContainedInScanDirs(p))
7282
7702
  return;
7283
7703
  void (async () => {
7284
7704
  await initialBuildPromise;
@@ -7291,11 +7711,7 @@ function createDevProcessorPlugin(options) {
7291
7711
  return;
7292
7712
  if (!p.endsWith(".vue"))
7293
7713
  return;
7294
- const isContainedInScanDirs = scanDirs.some((dir) => {
7295
- const absDir = path.resolve(projectRootRef.current, dir);
7296
- return p.startsWith(absDir + path.sep);
7297
- });
7298
- if (!isContainedInScanDirs)
7714
+ if (!isContainedInScanDirs(p))
7299
7715
  return;
7300
7716
  void (async () => {
7301
7717
  await initialBuildPromise;
@@ -7345,12 +7761,20 @@ function generateTestIdsModule(componentTestIds) {
7345
7761
  });
7346
7762
  });
7347
7763
  }
7764
+ const VIRTUAL_ID = "virtual:testids";
7765
+ const RESOLVED_ID = `\0${VIRTUAL_ID}`;
7348
7766
  function createTestIdsVirtualModulesPlugin(componentTestIds) {
7349
- const maybeModule = virtualImport;
7350
- const virtual = maybeModule.default ?? virtualImport;
7351
- return virtual({
7352
- "virtual:testids": () => generateTestIdsModule(componentTestIds)
7353
- });
7767
+ return {
7768
+ name: "vue-pom-generator:virtual-testids",
7769
+ resolveId(id) {
7770
+ if (id === VIRTUAL_ID)
7771
+ return RESOLVED_ID;
7772
+ },
7773
+ load(id) {
7774
+ if (id === RESOLVED_ID)
7775
+ return generateTestIdsModule(componentTestIds);
7776
+ }
7777
+ };
7354
7778
  }
7355
7779
  function createSupportPlugins(options) {
7356
7780
  const {
@@ -7359,11 +7783,14 @@ function createSupportPlugins(options) {
7359
7783
  vueFilesPathMap,
7360
7784
  nativeWrappers,
7361
7785
  excludedComponents,
7362
- viewsDir,
7363
- scanDirs,
7786
+ getPageDirs,
7787
+ getComponentDirs,
7788
+ getLayoutDirs,
7789
+ getViewsDir,
7790
+ getSourceDirs,
7364
7791
  getWrapperSearchRoots,
7365
7792
  nameCollisionBehavior = "suffix",
7366
- missingSemanticNameBehavior,
7793
+ missingSemanticNameBehavior = "error",
7367
7794
  existingIdBehavior,
7368
7795
  outDir,
7369
7796
  emitLanguages,
@@ -7392,7 +7819,6 @@ function createSupportPlugins(options) {
7392
7819
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
7393
7820
  return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(projectRootRef.current, routerEntry);
7394
7821
  };
7395
- const resolvedRouterEntry = resolveRouterEntry2();
7396
7822
  const getDefaultBasePageClassPath = () => {
7397
7823
  try {
7398
7824
  return node_url.fileURLToPath(new URL("../class-generation/base-page.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
@@ -7405,8 +7831,11 @@ function createSupportPlugins(options) {
7405
7831
  const tsProcessor = createBuildProcessorPlugin({
7406
7832
  componentHierarchyMap,
7407
7833
  vueFilesPathMap,
7408
- viewsDir,
7409
- scanDirs,
7834
+ getPageDirs,
7835
+ getComponentDirs,
7836
+ getLayoutDirs,
7837
+ getViewsDir,
7838
+ getSourceDirs,
7410
7839
  basePageClassPath,
7411
7840
  normalizedBasePagePath,
7412
7841
  outDir,
@@ -7428,15 +7857,18 @@ function createSupportPlugins(options) {
7428
7857
  getWrapperSearchRoots,
7429
7858
  routerAwarePoms,
7430
7859
  routerType,
7431
- resolvedRouterEntry,
7860
+ getResolvedRouterEntry: resolveRouterEntry2,
7432
7861
  routerModuleShims,
7433
7862
  loggerRef
7434
7863
  });
7435
7864
  const devProcessor = createDevProcessorPlugin({
7436
7865
  nativeWrappers,
7437
7866
  excludedComponents,
7438
- viewsDir,
7439
- scanDirs,
7867
+ getPageDirs,
7868
+ getComponentDirs,
7869
+ getLayoutDirs,
7870
+ getViewsDir,
7871
+ getSourceDirs,
7440
7872
  getWrapperSearchRoots,
7441
7873
  projectRootRef,
7442
7874
  normalizedBasePagePath,
@@ -7456,7 +7888,7 @@ function createSupportPlugins(options) {
7456
7888
  testIdAttribute,
7457
7889
  routerAwarePoms,
7458
7890
  routerType,
7459
- resolvedRouterEntry,
7891
+ getResolvedRouterEntry: resolveRouterEntry2,
7460
7892
  routerModuleShims,
7461
7893
  loggerRef
7462
7894
  });
@@ -7614,7 +8046,7 @@ function createVuePluginWithTestIds(options) {
7614
8046
  getViewsDirAbs,
7615
8047
  testIdAttribute,
7616
8048
  loggerRef,
7617
- scanDirs = ["src"],
8049
+ getSourceDirs,
7618
8050
  getWrapperSearchRoots,
7619
8051
  getProjectRoot
7620
8052
  } = options;
@@ -7623,7 +8055,7 @@ function createVuePluginWithTestIds(options) {
7623
8055
  filename,
7624
8056
  projectRoot: getProjectRoot(),
7625
8057
  viewsDirAbs: getViewsDirAbs(),
7626
- scanDirs,
8058
+ sourceDirs: getSourceDirs(),
7627
8059
  extraRoots: [process.cwd()]
7628
8060
  });
7629
8061
  };
@@ -7639,7 +8071,7 @@ function createVuePluginWithTestIds(options) {
7639
8071
  if (absFilename.startsWith(viewsDirAbs + path.sep) || absFilename === viewsDirAbs)
7640
8072
  return true;
7641
8073
  const rootsToTry = [projectRoot, process.cwd()];
7642
- const matched = scanDirs.some((dir) => {
8074
+ const matched = getSourceDirs().some((dir) => {
7643
8075
  return rootsToTry.some((root) => {
7644
8076
  const absDir = path.resolve(root, dir);
7645
8077
  if (absFilename.startsWith(absDir + path.sep) || absFilename === absDir)
@@ -7814,6 +8246,25 @@ function createVuePluginWithTestIds(options) {
7814
8246
  };
7815
8247
  return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
7816
8248
  }
8249
+ const nuxtConfigMarker$1 = /* @__PURE__ */ Symbol.for("@immense/vue-pom-generator.nuxt");
8250
+ const nuxtConfigFileNames = [
8251
+ "nuxt.config.ts",
8252
+ "nuxt.config.js",
8253
+ "nuxt.config.mjs",
8254
+ "nuxt.config.cjs",
8255
+ "nuxt.config.mts",
8256
+ "nuxt.config.cts",
8257
+ ".nuxtrc"
8258
+ ];
8259
+ const nuxtSourceMarkers = [
8260
+ "app.vue",
8261
+ "app",
8262
+ "pages",
8263
+ "layouts",
8264
+ "components",
8265
+ "layers",
8266
+ ".nuxt"
8267
+ ];
7817
8268
  function assertNonEmptyString(value, name) {
7818
8269
  if (!value || !value.trim()) {
7819
8270
  throw new Error(`${name} must be a non-empty string.`);
@@ -7854,12 +8305,63 @@ function assertErrorBehavior(value, name) {
7854
8305
  }
7855
8306
  function resolveMissingSemanticNameBehavior(value) {
7856
8307
  if (!value) {
7857
- return "ignore";
8308
+ return "error";
7858
8309
  }
7859
8310
  if (value === "ignore" || value === "error") {
7860
8311
  return value;
7861
8312
  }
7862
- return value.missingSemanticNameBehavior ?? "ignore";
8313
+ return value.missingSemanticNameBehavior ?? "error";
8314
+ }
8315
+ function readPackageJson(projectRoot) {
8316
+ const packageJsonPath = path.join(projectRoot, "package.json");
8317
+ if (!fs.existsSync(packageJsonPath)) {
8318
+ return null;
8319
+ }
8320
+ try {
8321
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
8322
+ } catch {
8323
+ return null;
8324
+ }
8325
+ }
8326
+ function recordHasOwnStringKey(value, key) {
8327
+ return value !== null && value !== void 0 && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, key) && typeof value[key] === "string";
8328
+ }
8329
+ function projectPackageLooksNuxt(projectRoot) {
8330
+ const packageJson = readPackageJson(projectRoot);
8331
+ if (!packageJson) {
8332
+ return false;
8333
+ }
8334
+ const dependencyGroups = [
8335
+ packageJson.dependencies,
8336
+ packageJson.devDependencies,
8337
+ packageJson.peerDependencies,
8338
+ packageJson.optionalDependencies
8339
+ ];
8340
+ if (dependencyGroups.some((group) => {
8341
+ const dependencyGroup = typeof group === "object" && group !== null ? group : void 0;
8342
+ return recordHasOwnStringKey(dependencyGroup, "nuxt") || recordHasOwnStringKey(dependencyGroup, "nuxt-nightly");
8343
+ })) {
8344
+ return true;
8345
+ }
8346
+ if (typeof packageJson.scripts !== "object" || packageJson.scripts === null || Array.isArray(packageJson.scripts)) {
8347
+ return false;
8348
+ }
8349
+ return Object.values(packageJson.scripts).some((script) => {
8350
+ if (typeof script !== "string") {
8351
+ return false;
8352
+ }
8353
+ const normalizedScript = script.trim();
8354
+ return normalizedScript === "nuxt" || normalizedScript.startsWith("nuxt ") || normalizedScript === "nuxi" || normalizedScript.startsWith("nuxi ");
8355
+ });
8356
+ }
8357
+ function detectNuxtProject(options, projectRoot) {
8358
+ if (options[nuxtConfigMarker$1] === true) {
8359
+ return true;
8360
+ }
8361
+ if (nuxtConfigFileNames.some((fileName) => fs.existsSync(path.join(projectRoot, fileName)))) {
8362
+ return true;
8363
+ }
8364
+ return projectPackageLooksNuxt(projectRoot) && nuxtSourceMarkers.some((entry) => fs.existsSync(path.join(projectRoot, entry)));
7863
8365
  }
7864
8366
  function assertRouterModuleShims(value, name) {
7865
8367
  if (!value)
@@ -7968,14 +8470,18 @@ function assertNotVitePluginInstance(options) {
7968
8470
  function createVuePomGeneratorPlugins(options = {}) {
7969
8471
  assertNotVitePluginInstance(options);
7970
8472
  const injection = options.injection ?? {};
8473
+ const isNuxt = detectNuxtProject(options, process.cwd());
7971
8474
  const generationSetting = options.generation;
7972
8475
  const generationOptions = generationSetting === false ? null : generationSetting ?? {};
7973
8476
  const generationEnabled = generationOptions !== null;
8477
+ const vueGenerationOptions = generationOptions;
7974
8478
  const verbosity = options.logging?.verbosity ?? "warn";
7975
8479
  const vueOptions = options.vueOptions;
7976
- const viewsDir = injection.viewsDir ?? "src/views";
7977
- const scanDirs = injection.scanDirs ?? ["src"];
7978
- const wrapperSearchRoots = injection.wrapperSearchRoots ?? [];
8480
+ const legacyVueOptions = options;
8481
+ const pageDirsRef = { current: !isNuxt ? [legacyVueOptions.injection?.viewsDir ?? "src/views"] : ["app/pages"] };
8482
+ const componentDirsRef = { current: !isNuxt ? legacyVueOptions.injection?.componentDirs ?? ["src/components"] : ["app/components"] };
8483
+ const layoutDirsRef = { current: !isNuxt ? legacyVueOptions.injection?.layoutDirs ?? ["src/layouts"] : ["app/layouts"] };
8484
+ const wrapperSearchRootsRef = { current: !isNuxt ? legacyVueOptions.injection?.wrapperSearchRoots ?? [] : [] };
7979
8485
  const nativeWrappers = injection.nativeWrappers ?? {};
7980
8486
  const excludedComponents = injection.excludeComponents ?? [];
7981
8487
  const testIdAttribute = (injection.attribute ?? "data-testid").trim() || "data-testid";
@@ -7983,10 +8489,9 @@ function createVuePomGeneratorPlugins(options = {}) {
7983
8489
  const outDir = (generationOptions?.outDir ?? "tests/playwright/__generated__").trim();
7984
8490
  const emitLanguages = generationOptions?.emit && generationOptions.emit.length ? generationOptions.emit : ["ts"];
7985
8491
  const nameCollisionBehavior = generationOptions?.nameCollisionBehavior ?? "suffix";
7986
- const routerEntry = generationOptions?.router?.entry;
7987
- const routerType = generationOptions?.router?.type ?? "vue-router";
7988
- const routerModuleShims = generationOptions?.router?.moduleShims;
7989
- const isNuxt = routerType === "nuxt";
8492
+ const routerEntry = !isNuxt ? vueGenerationOptions?.router?.entry : void 0;
8493
+ const routerType = isNuxt ? "nuxt" : vueGenerationOptions?.router?.type ?? "vue-router";
8494
+ const routerModuleShims = !isNuxt ? vueGenerationOptions?.router?.moduleShims : void 0;
7990
8495
  if (isNuxt && options.vuePluginOwnership === "internal") {
7991
8496
  throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
7992
8497
  }
@@ -8003,66 +8508,100 @@ function createVuePomGeneratorPlugins(options = {}) {
8003
8508
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
8004
8509
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
8005
8510
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
8511
+ const getPageDirs = () => pageDirsRef.current;
8512
+ const getViewsDir = () => getPageDirs()[0] ?? "src/views";
8513
+ const getComponentDirs = () => componentDirsRef.current;
8514
+ const getLayoutDirs = () => layoutDirsRef.current;
8515
+ const getSourceDirs = () => Array.from(/* @__PURE__ */ new Set([
8516
+ ...getPageDirs(),
8517
+ ...getComponentDirs(),
8518
+ ...getLayoutDirs()
8519
+ ]));
8520
+ const getWrapperSearchRoots = () => wrapperSearchRootsRef.current;
8006
8521
  const sharedStateKey = JSON.stringify({
8007
8522
  cwd: process.cwd(),
8008
- viewsDir,
8009
- scanDirs,
8010
- wrapperSearchRoots,
8523
+ mode: isNuxt ? "nuxt" : "vue",
8524
+ pageDirs: isNuxt ? null : getPageDirs(),
8525
+ componentDirs: isNuxt ? null : getComponentDirs(),
8526
+ layoutDirs: isNuxt ? null : getLayoutDirs(),
8527
+ wrapperSearchRoots: isNuxt ? null : getWrapperSearchRoots(),
8011
8528
  outDir,
8012
8529
  testIdAttribute,
8013
8530
  routerType,
8014
8531
  vuePluginOwnership
8015
8532
  });
8016
8533
  const sharedState = getSharedGeneratorState(sharedStateKey);
8534
+ let templateCompilerOptionsForResolvedPlugin;
8017
8535
  const projectRootRef = { current: process.cwd() };
8018
8536
  const loggerRef = {
8019
8537
  current: createLogger({ verbosity })
8020
8538
  };
8021
- const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
8022
- const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8023
- const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8024
- const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8025
- vueOptions,
8026
- existingIdBehavior,
8027
- nameCollisionBehavior,
8028
- nativeWrappers,
8029
- elementMetadata,
8030
- semanticNameMap,
8031
- componentHierarchyMap,
8032
- vueFilesPathMap,
8033
- excludedComponents,
8034
- getViewsDirAbs,
8035
- testIdAttribute,
8036
- loggerRef,
8037
- scanDirs,
8038
- getWrapperSearchRoots: getWrapperSearchRootsAbs,
8039
- getProjectRoot: () => projectRootRef.current
8040
- });
8041
8539
  const configPlugin = {
8042
8540
  name: "vue-pom-generator-config",
8043
8541
  enforce: "pre",
8044
- configResolved(config) {
8542
+ async configResolved(config) {
8045
8543
  projectRootRef.current = config.root;
8046
8544
  loggerRef.current = createLogger({ verbosity, viteLogger: config.logger });
8545
+ if (vueGenerationOptions?.router?.type === "nuxt") {
8546
+ throw new Error('[vue-pom-generator] Remove generation.router.type="nuxt". Nuxt projects are auto-detected.');
8547
+ }
8548
+ if (isNuxt) {
8549
+ const nuxtDiscovery = await loadNuxtProjectDiscovery(process.cwd());
8550
+ projectRootRef.current = nuxtDiscovery.rootDir;
8551
+ pageDirsRef.current = nuxtDiscovery.pageDirs.length ? nuxtDiscovery.pageDirs : [path.resolve(nuxtDiscovery.srcDir, "pages")];
8552
+ componentDirsRef.current = nuxtDiscovery.componentDirs;
8553
+ layoutDirsRef.current = nuxtDiscovery.layoutDirs;
8554
+ wrapperSearchRootsRef.current = nuxtDiscovery.wrapperSearchRoots;
8555
+ }
8047
8556
  assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
8048
- assertNonEmptyString(viewsDir, "[vue-pom-generator] injection.viewsDir");
8049
- assertNonEmptyStringArray(wrapperSearchRoots, "[vue-pom-generator] injection.wrapperSearchRoots");
8557
+ assertNonEmptyString(getViewsDir(), "[vue-pom-generator] injection.viewsDir");
8558
+ assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
8559
+ assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
8560
+ assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
8050
8561
  assertErrorBehavior(errorBehavior, "[vue-pom-generator] errorBehavior");
8051
8562
  if (generationEnabled) {
8052
8563
  assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
8053
8564
  assertOneOf(typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
8054
8565
  assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
8055
- if (generationOptions?.router && routerType === "vue-router") {
8566
+ if (!isNuxt && vueGenerationOptions?.router && routerType === "vue-router") {
8056
8567
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
8057
8568
  }
8058
8569
  }
8059
8570
  if (usesExternalVuePlugin) {
8060
- applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
8571
+ applyTemplateCompilerOptionsToResolvedVuePlugin(
8572
+ config,
8573
+ templateCompilerOptionsForResolvedPlugin,
8574
+ isNuxt ? "nuxt" : vuePluginOwnership
8575
+ );
8061
8576
  }
8062
8577
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
8578
+ loggerRef.current.info(`viewsDir=${getViewsDir()}`);
8579
+ loggerRef.current.info(`componentDirs=${getComponentDirs().join(", ")}`);
8580
+ loggerRef.current.info(`layoutDirs=${getLayoutDirs().join(", ")}`);
8063
8581
  loggerRef.current.info(`Active plugins: ${(config.plugins ?? []).map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
8064
8582
  }
8065
8583
  };
8584
+ const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8585
+ const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8586
+ const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8587
+ const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8588
+ vueOptions,
8589
+ existingIdBehavior,
8590
+ nameCollisionBehavior,
8591
+ nativeWrappers,
8592
+ elementMetadata,
8593
+ semanticNameMap,
8594
+ componentHierarchyMap,
8595
+ vueFilesPathMap,
8596
+ excludedComponents,
8597
+ getViewsDirAbs,
8598
+ testIdAttribute,
8599
+ loggerRef,
8600
+ getSourceDirs,
8601
+ getWrapperSearchRoots: getWrapperSearchRootsAbs,
8602
+ getProjectRoot: () => projectRootRef.current
8603
+ });
8604
+ templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8066
8605
  const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
8067
8606
  const supportPlugins = createSupportPlugins({
8068
8607
  componentTestIds,
@@ -8070,8 +8609,11 @@ function createVuePomGeneratorPlugins(options = {}) {
8070
8609
  vueFilesPathMap,
8071
8610
  nativeWrappers,
8072
8611
  excludedComponents,
8073
- viewsDir,
8074
- scanDirs,
8612
+ getPageDirs,
8613
+ getComponentDirs,
8614
+ getLayoutDirs,
8615
+ getViewsDir,
8616
+ getSourceDirs,
8075
8617
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
8076
8618
  nameCollisionBehavior,
8077
8619
  missingSemanticNameBehavior,
@@ -8116,11 +8658,21 @@ function createVuePomGeneratorPlugins(options = {}) {
8116
8658
  }
8117
8659
  return resultPlugins;
8118
8660
  }
8661
+ const nuxtConfigMarker = /* @__PURE__ */ Symbol.for("@immense/vue-pom-generator.nuxt");
8119
8662
  function defineVuePomGeneratorConfig(options) {
8120
8663
  return options;
8121
8664
  }
8665
+ function defineNuxtPomGeneratorConfig(options) {
8666
+ const markedOptions = { ...options };
8667
+ Object.defineProperty(markedOptions, nuxtConfigMarker, {
8668
+ value: true,
8669
+ enumerable: false
8670
+ });
8671
+ return markedOptions;
8672
+ }
8122
8673
  exports.createVuePomGeneratorPlugins = createVuePomGeneratorPlugins;
8123
8674
  exports.default = createVuePomGeneratorPlugins;
8675
+ exports.defineNuxtPomGeneratorConfig = defineNuxtPomGeneratorConfig;
8124
8676
  exports.defineVuePomGeneratorConfig = defineVuePomGeneratorConfig;
8125
8677
  exports.vuePomGenerator = createVuePomGeneratorPlugins;
8126
8678
  //# sourceMappingURL=index.cjs.map