@immense/vue-pom-generator 1.0.51 → 1.0.53

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 (43) hide show
  1. package/README.md +61 -22
  2. package/RELEASE_NOTES.md +47 -14
  3. package/class-generation/base-page.ts +0 -1
  4. package/class-generation/index.ts +23 -60
  5. package/dist/class-generation/base-page.d.ts.map +1 -1
  6. package/dist/class-generation/index.d.ts +3 -2
  7. package/dist/class-generation/index.d.ts.map +1 -1
  8. package/dist/eslint.config.d.ts.map +1 -1
  9. package/dist/index.cjs +531 -167
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.mjs +531 -167
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/plugin/create-vue-pom-generator-plugins.d.ts +2 -2
  16. package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
  17. package/dist/plugin/nuxt-discovery.d.ts +47 -0
  18. package/dist/plugin/nuxt-discovery.d.ts.map +1 -0
  19. package/dist/plugin/path-utils.d.ts +4 -5
  20. package/dist/plugin/path-utils.d.ts.map +1 -1
  21. package/dist/plugin/support/build-plugin.d.ts +6 -3
  22. package/dist/plugin/support/build-plugin.d.ts.map +1 -1
  23. package/dist/plugin/support/dev-plugin.d.ts +6 -3
  24. package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
  25. package/dist/plugin/support-plugins.d.ts +5 -2
  26. package/dist/plugin/support-plugins.d.ts.map +1 -1
  27. package/dist/plugin/types.d.ts +33 -17
  28. package/dist/plugin/types.d.ts.map +1 -1
  29. package/dist/plugin/vue-plugin.d.ts +1 -1
  30. package/dist/plugin/vue-plugin.d.ts.map +1 -1
  31. package/dist/router-introspection.d.ts +4 -2
  32. package/dist/router-introspection.d.ts.map +1 -1
  33. package/dist/tests/nuxt-discovery.test.d.ts +2 -0
  34. package/dist/tests/nuxt-discovery.test.d.ts.map +1 -0
  35. package/dist/transform.d.ts +11 -0
  36. package/dist/transform.d.ts.map +1 -1
  37. package/dist/utils.d.ts +15 -0
  38. package/dist/utils.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/plugin/support/generation-metrics.d.ts +0 -10
  41. package/dist/plugin/support/generation-metrics.d.ts.map +0 -1
  42. package/dist/tests/generation-metrics.test.d.ts +0 -2
  43. package/dist/tests/generation-metrics.test.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -22,10 +22,11 @@ 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");
@@ -104,6 +105,133 @@ function createLogger(options) {
104
105
  }
105
106
  };
106
107
  }
108
+ 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);
109
+ function toUniqueResolvedPaths(paths) {
110
+ return Array.from(new Set(paths.map((value) => path.resolve(value))));
111
+ }
112
+ function resolveNuxtAlias(value, context) {
113
+ const aliases = [
114
+ ["~~", context.rootDir],
115
+ ["@@", context.rootDir],
116
+ ["~/", `${context.srcDir}${path.sep}`],
117
+ ["@/", `${context.srcDir}${path.sep}`],
118
+ ["~", context.srcDir],
119
+ ["@", context.srcDir]
120
+ ];
121
+ for (const [alias, replacement] of Object.entries(context.alias ?? {})) {
122
+ aliases.push([alias, replacement]);
123
+ }
124
+ aliases.sort((a, b) => b[0].length - a[0].length);
125
+ for (const [alias, replacement] of aliases) {
126
+ if (value === alias)
127
+ return replacement;
128
+ if (value.startsWith(`${alias}/`) || value.startsWith(`${alias}${path.sep}`)) {
129
+ const suffix = value.slice(alias.length + 1);
130
+ return path.resolve(replacement, suffix);
131
+ }
132
+ }
133
+ return value;
134
+ }
135
+ function resolveNuxtPath(value, baseDir, context) {
136
+ const resolvedAlias = resolveNuxtAlias(value, context);
137
+ return path.isAbsolute(resolvedAlias) ? path.resolve(resolvedAlias) : path.resolve(baseDir, resolvedAlias);
138
+ }
139
+ function normalizeNuxtComponentDirs(value, baseDir, context) {
140
+ if (Array.isArray(value)) {
141
+ return value.flatMap((entry) => normalizeNuxtComponentDirs(entry, baseDir, context));
142
+ }
143
+ if (value === false) {
144
+ return [];
145
+ }
146
+ if (value === true || value === void 0) {
147
+ return [
148
+ path.resolve(baseDir, "components/islands"),
149
+ path.resolve(baseDir, "components/global"),
150
+ path.resolve(baseDir, "components")
151
+ ];
152
+ }
153
+ if (typeof value === "string") {
154
+ return [resolveNuxtPath(value, baseDir, context)];
155
+ }
156
+ if (!value || typeof value !== "object") {
157
+ return [];
158
+ }
159
+ if (typeof value === "object" && value && "path" in value && typeof value.path === "string") {
160
+ return [resolveNuxtPath(value.path, baseDir, context)];
161
+ }
162
+ if (typeof value !== "object" || !value || !("dirs" in value)) {
163
+ return [];
164
+ }
165
+ return normalizeNuxtComponentDirs(value.dirs, baseDir, context);
166
+ }
167
+ function resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd = process.cwd()) {
168
+ const rootDir = path.resolve(nuxtOptions.rootDir ?? cwd);
169
+ const srcDir = path.resolve(nuxtOptions.srcDir ?? rootDir);
170
+ const fallbackLayer = {
171
+ cwd: rootDir,
172
+ config: {
173
+ rootDir,
174
+ srcDir,
175
+ dir: nuxtOptions.dir,
176
+ components: nuxtOptions.components,
177
+ alias: nuxtOptions.alias
178
+ }
179
+ };
180
+ const layers = nuxtOptions._layers?.length ? nuxtOptions._layers : [fallbackLayer];
181
+ const normalizedNuxtOptions = {
182
+ ...nuxtOptions,
183
+ _layers: layers
184
+ };
185
+ const layerDirectories = getLayerDirectories({ options: normalizedNuxtOptions });
186
+ const pageDirs = layerDirectories.map((layer) => layer.appPages);
187
+ const layoutDirs = layerDirectories.map((layer) => layer.appLayouts);
188
+ const componentDirs = [];
189
+ for (const [index, layer] of layers.entries()) {
190
+ const layerDirectory = layerDirectories[index];
191
+ const layerRootDir = path.resolve(layerDirectory?.root ?? layer.config?.rootDir ?? layer.cwd ?? rootDir);
192
+ const layerSrcDir = path.resolve(layerDirectory?.app ?? layer.config?.srcDir ?? layer.cwd ?? srcDir);
193
+ const context = {
194
+ rootDir: layerRootDir,
195
+ srcDir: layerSrcDir,
196
+ alias: {
197
+ ...nuxtOptions.alias ?? {},
198
+ ...layer.config?.alias ?? {}
199
+ }
200
+ };
201
+ componentDirs.push(...normalizeNuxtComponentDirs(layer.config?.components, layerSrcDir, context));
202
+ }
203
+ const uniquePageDirs = toUniqueResolvedPaths(pageDirs);
204
+ const uniqueLayoutDirs = toUniqueResolvedPaths(layoutDirs);
205
+ const uniqueComponentDirs = toUniqueResolvedPaths(componentDirs);
206
+ return {
207
+ rootDir,
208
+ srcDir,
209
+ pageDirs: uniquePageDirs,
210
+ layoutDirs: uniqueLayoutDirs,
211
+ componentDirs: uniqueComponentDirs,
212
+ wrapperSearchRoots: []
213
+ };
214
+ }
215
+ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
216
+ let loadNuxtConfig;
217
+ let getLayerDirectories;
218
+ try {
219
+ const nuxtKitEntry = requireFromModule.resolve("@nuxt/kit");
220
+ ({ loadNuxtConfig, getLayerDirectories } = await import(node_url.pathToFileURL(nuxtKitEntry).href));
221
+ } catch (error) {
222
+ throw new TypeError(
223
+ `[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)}`
224
+ );
225
+ }
226
+ if (typeof loadNuxtConfig !== "function") {
227
+ throw new TypeError("[vue-pom-generator] Nuxt mode requires @nuxt/kit.loadNuxtConfig().");
228
+ }
229
+ if (typeof getLayerDirectories !== "function") {
230
+ throw new TypeError("[vue-pom-generator] Nuxt mode requires @nuxt/kit.getLayerDirectories().");
231
+ }
232
+ const nuxtOptions = await loadNuxtConfig({ cwd });
233
+ return resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd);
234
+ }
107
235
  function createTypeScriptProject() {
108
236
  return new tsMorph.Project({
109
237
  useInMemoryFileSystem: true,
@@ -769,7 +897,14 @@ function getTemplateSlotScope(node) {
769
897
  return null;
770
898
  }
771
899
  function isSimpleScopeIdentifier(value) {
772
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
900
+ if (!value) {
901
+ return false;
902
+ }
903
+ try {
904
+ return types.isIdentifier(parser.parseExpression(value, { plugins: ["typescript"] }));
905
+ } catch {
906
+ return false;
907
+ }
773
908
  }
774
909
  function buildSlotScopeFallbackKeyExpression(identifier) {
775
910
  return `${identifier}.key ?? ${identifier}.data?.id ?? ${identifier}.id ?? ${identifier}.value ?? ${identifier}`;
@@ -789,6 +924,29 @@ function tryGetBindingIdentifierName(node) {
789
924
  }
790
925
  return null;
791
926
  }
927
+ function splitNullishCoalescingExpression(expr) {
928
+ const ast = parser.parseExpression(expr, { plugins: ["typescript"] });
929
+ const toSourceText = (node) => {
930
+ if (node.start == null || node.end == null) {
931
+ throw new Error("[vue-pom-generator] Unable to recover source for nullish-coalescing expression.");
932
+ }
933
+ return expr.slice(node.start, node.end).trim();
934
+ };
935
+ const parts = [];
936
+ const visit = (node) => {
937
+ if (types.isLogicalExpression(node) && node.operator === "??") {
938
+ visit(node.left);
939
+ visit(node.right);
940
+ return;
941
+ }
942
+ const candidate = toSourceText(node);
943
+ if (candidate) {
944
+ parts.push(candidate);
945
+ }
946
+ };
947
+ visit(ast);
948
+ return parts;
949
+ }
792
950
  function getSlotScopeObjectPropertyKeyName(node) {
793
951
  if (types.isIdentifier(node)) {
794
952
  return node.name;
@@ -2045,7 +2203,7 @@ function applyResolvedDataTestId(args) {
2045
2203
  if (!expr) {
2046
2204
  return [];
2047
2205
  }
2048
- return expr.split("??").map((part) => part.trim()).filter(Boolean);
2206
+ return splitNullishCoalescingExpression(expr);
2049
2207
  };
2050
2208
  let dataTestId = args.preferredGeneratedValue;
2051
2209
  let fromExisting = false;
@@ -2766,27 +2924,24 @@ function safeRealpath(value) {
2766
2924
  const resolvedParent = safeRealpath(parent);
2767
2925
  return resolvedParent === parent ? value : path.join(resolvedParent, path.basename(value));
2768
2926
  }
2927
+ function isPathWithinDir(filePathAbs, dirPathAbs) {
2928
+ const fileAbs = path.resolve(filePathAbs);
2929
+ const dirAbs = path.resolve(dirPathAbs);
2930
+ const rel = path.relative(dirAbs, fileAbs);
2931
+ if (!rel)
2932
+ return true;
2933
+ return !rel.startsWith("..") && !path.isAbsolute(rel);
2934
+ }
2769
2935
  function resolveComponentNameFromPath(options) {
2770
- const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
2936
+ const { projectRoot, viewsDirAbs, sourceDirs, extraRoots = [] } = options;
2771
2937
  const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
2772
2938
  const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
2773
2939
  const normalizedAbsFilename = path.normalize(safeRealpath(absFilename));
2774
2940
  const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
2775
- const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
2776
- for (const base of rootBases) {
2777
- for (const dir of scanDirs) {
2778
- const absDir = path.resolve(base, dir);
2779
- try {
2780
- const pagesDir = path.join(absDir, "pages");
2781
- if (fs.existsSync(pagesDir))
2782
- roots.push(pagesDir);
2783
- const componentsDir = path.join(absDir, "components");
2784
- if (fs.existsSync(componentsDir))
2785
- roots.push(componentsDir);
2786
- } catch {
2787
- }
2788
- }
2789
- }
2941
+ const roots = [
2942
+ viewsDirAbs,
2943
+ ...sourceDirs.flatMap((dir) => path.isAbsolute(dir) ? [dir] : rootBases.map((base) => path.resolve(base, dir)))
2944
+ ];
2790
2945
  const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(safeRealpath(r))))).sort((a, b) => b.length - a.length);
2791
2946
  for (const root of potentialRoots) {
2792
2947
  if (normalizedAbsFilename.startsWith(root + path.sep) || normalizedAbsFilename === root) {
@@ -3150,7 +3305,7 @@ function resolveIntrospectedComponentName(componentInfo, componentNaming) {
3150
3305
  filename: componentInfo.filePath,
3151
3306
  projectRoot: componentNaming.projectRoot,
3152
3307
  viewsDirAbs: componentNaming.viewsDirAbs,
3153
- scanDirs: componentNaming.scanDirs,
3308
+ sourceDirs: componentNaming.sourceDirs,
3154
3309
  extraRoots: componentNaming.extraRoots
3155
3310
  });
3156
3311
  }
@@ -3262,69 +3417,97 @@ async function ensureDomShim() {
3262
3417
  if (!g.requestAnimationFrame)
3263
3418
  g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 16);
3264
3419
  }
3265
- async function introspectNuxtPages(projectRoot) {
3266
- const possiblePagesDirs = ["app/pages", "pages"];
3267
- let pagesDir = "";
3268
- for (const dir of possiblePagesDirs) {
3269
- const abs = path.resolve(projectRoot, dir);
3270
- if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
3271
- pagesDir = abs;
3420
+ function unwrapNuxtPageSegment(segment, prefix, suffix) {
3421
+ if (!segment.startsWith(prefix) || !segment.endsWith(suffix))
3422
+ return null;
3423
+ const value = segment.slice(prefix.length, segment.length - suffix.length);
3424
+ return value.length > 0 ? value : null;
3425
+ }
3426
+ function resolveNuxtPageSegment(segment) {
3427
+ if (segment === "index") {
3428
+ return { pathPart: "", params: [] };
3429
+ }
3430
+ const optionalParamName = unwrapNuxtPageSegment(segment, "[[", "]]");
3431
+ if (optionalParamName) {
3432
+ return {
3433
+ pathPart: `:${optionalParamName}?`,
3434
+ params: [{ name: optionalParamName, optional: true }]
3435
+ };
3436
+ }
3437
+ const catchAllParamName = unwrapNuxtPageSegment(segment, "[...", "]");
3438
+ if (catchAllParamName) {
3439
+ return {
3440
+ pathPart: `:${catchAllParamName}(.*)*`,
3441
+ params: [{ name: catchAllParamName, optional: false }]
3442
+ };
3443
+ }
3444
+ const requiredParamName = unwrapNuxtPageSegment(segment, "[", "]");
3445
+ if (requiredParamName) {
3446
+ return {
3447
+ pathPart: `:${requiredParamName}`,
3448
+ params: [{ name: requiredParamName, optional: false }]
3449
+ };
3450
+ }
3451
+ return { pathPart: segment, params: [] };
3452
+ }
3453
+ function toPathSegments(value) {
3454
+ const segments = [];
3455
+ let current = path.normalize(value);
3456
+ while (current && current !== "." && current !== path.sep) {
3457
+ const parsed = path.parse(current);
3458
+ if (!parsed.base || parsed.base === ".")
3272
3459
  break;
3273
- }
3460
+ segments.unshift(parsed.base);
3461
+ if (!parsed.dir || parsed.dir === "." || parsed.dir === current)
3462
+ break;
3463
+ current = parsed.dir;
3274
3464
  }
3275
- if (!pagesDir) {
3465
+ return segments;
3466
+ }
3467
+ async function introspectNuxtPages(projectRoot, options = {}) {
3468
+ const possiblePageDirs = options.pageDirs?.length ? options.pageDirs : ["app/pages", "pages"].map((dir) => path.resolve(projectRoot, dir));
3469
+ const pageDirs = possiblePageDirs.map((dir) => path.resolve(projectRoot, dir)).filter((dir) => {
3470
+ try {
3471
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
3472
+ } catch {
3473
+ return false;
3474
+ }
3475
+ });
3476
+ if (!pageDirs.length) {
3276
3477
  debugLog(`[router-introspection][nuxt] Could not find pages directory in ${projectRoot}`);
3277
3478
  return { routeNameMap: /* @__PURE__ */ new Map(), routePathMap: /* @__PURE__ */ new Map(), routeMetaEntries: [] };
3278
3479
  }
3480
+ const routePathMap = /* @__PURE__ */ new Map();
3279
3481
  const routeMetaEntries = [];
3280
- const walk = (dir, baseRoute) => {
3482
+ const walk = (pagesDir, dir) => {
3281
3483
  const files = fs.readdirSync(dir);
3282
3484
  for (const file of files) {
3283
3485
  const fullPath = path.join(dir, file);
3284
3486
  const stat = fs.statSync(fullPath);
3285
3487
  if (stat.isDirectory()) {
3286
- walk(fullPath, `${baseRoute}/${file}`);
3488
+ walk(pagesDir, fullPath);
3287
3489
  continue;
3288
3490
  }
3289
3491
  if (!file.endsWith(".vue"))
3290
3492
  continue;
3291
- const componentName = file.slice(0, -4);
3292
- if (componentName === "index" && baseRoute === "") {
3293
- routeMetaEntries.push({
3294
- componentName: "index",
3295
- pathTemplate: "/",
3296
- params: [],
3297
- query: []
3298
- });
3299
- continue;
3300
- }
3301
- let routePath = componentName === "index" ? baseRoute : `${baseRoute}/${componentName}`;
3302
- if (!routePath.startsWith("/"))
3303
- routePath = `/${routePath}`;
3493
+ const componentName = resolveComponentNameFromPath({
3494
+ filename: fullPath,
3495
+ projectRoot,
3496
+ viewsDirAbs: pagesDir,
3497
+ sourceDirs: [pagesDir],
3498
+ extraRoots: [process.cwd()]
3499
+ });
3500
+ const relativePath = path.relative(pagesDir, fullPath);
3501
+ const parsed = path.parse(relativePath);
3502
+ const routeSegments = toPathSegments(path.join(parsed.dir, parsed.name));
3304
3503
  const params = [];
3305
- let pathTemplate = "";
3306
- for (let i = 0; i < routePath.length; i++) {
3307
- const ch = routePath[i];
3308
- if (ch !== "[") {
3309
- pathTemplate += ch;
3310
- continue;
3311
- }
3312
- let name = "";
3313
- i++;
3314
- while (i < routePath.length) {
3315
- const c = routePath[i];
3316
- if (c === "]")
3317
- break;
3318
- name += c;
3319
- i++;
3320
- }
3321
- if (name) {
3322
- params.push({ name, optional: false });
3323
- pathTemplate += `:${name}`;
3324
- } else {
3325
- pathTemplate += "[]";
3326
- }
3327
- }
3504
+ const pathParts = routeSegments.flatMap((segment) => {
3505
+ const resolution = resolveNuxtPageSegment(segment);
3506
+ params.push(...resolution.params);
3507
+ return resolution.pathPart ? [resolution.pathPart] : [];
3508
+ });
3509
+ const pathTemplate = pathParts.length ? `/${pathParts.join("/")}` : "/";
3510
+ routePathMap.set(pathTemplate, componentName);
3328
3511
  routeMetaEntries.push({
3329
3512
  componentName,
3330
3513
  pathTemplate,
@@ -3333,10 +3516,12 @@ async function introspectNuxtPages(projectRoot) {
3333
3516
  });
3334
3517
  }
3335
3518
  };
3336
- walk(pagesDir, "");
3519
+ for (const pageDir of pageDirs) {
3520
+ walk(pageDir, pageDir);
3521
+ }
3337
3522
  return {
3338
3523
  routeNameMap: /* @__PURE__ */ new Map(),
3339
- routePathMap: /* @__PURE__ */ new Map(),
3524
+ routePathMap,
3340
3525
  routeMetaEntries
3341
3526
  };
3342
3527
  }
@@ -3632,15 +3817,20 @@ function resolveVueSourcePath(targetClassName, vueFilesPathMap, projectRoot) {
3632
3817
  }
3633
3818
  async function getRouteMetaByComponent(projectRoot, routerEntry, routerType, options = {}) {
3634
3819
  const root = projectRoot ?? process.cwd();
3635
- const viewsDir = options.viewsDir ?? "src/views";
3636
- const viewsDirAbs = path.isAbsolute(viewsDir) ? viewsDir : path.resolve(root, viewsDir);
3637
- const scanDirs = options.scanDirs?.length ? options.scanDirs : ["src"];
3820
+ const pageDirs = options.pageDirs?.length ? options.pageDirs : ["src/views"];
3821
+ const pageDirsAbs = pageDirs.map((dir) => path.isAbsolute(dir) ? dir : path.resolve(root, dir));
3822
+ const primaryPageDirAbs = pageDirsAbs[0] ?? path.resolve(root, "src/views");
3823
+ const sourceDirs = [
3824
+ ...pageDirs,
3825
+ ...options.componentDirs?.length ? options.componentDirs : ["src/components"],
3826
+ ...options.layoutDirs?.length ? options.layoutDirs : ["src/layouts"]
3827
+ ];
3638
3828
  const extraRoots = process.cwd() !== root ? [process.cwd()] : [];
3639
- const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
3829
+ const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root, { pageDirs: pageDirsAbs }) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
3640
3830
  componentNaming: {
3641
3831
  projectRoot: root,
3642
- viewsDirAbs,
3643
- scanDirs,
3832
+ viewsDirAbs: primaryPageDirAbs,
3833
+ sourceDirs,
3644
3834
  extraRoots
3645
3835
  }
3646
3836
  });
@@ -3840,15 +4030,17 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3840
4030
  vueRouterFluentChaining,
3841
4031
  routerEntry,
3842
4032
  routerType,
3843
- viewsDir,
3844
- scanDirs,
4033
+ pageDirs,
4034
+ componentDirs,
4035
+ layoutDirs,
3845
4036
  routeMetaByComponent: routeMetaByComponentOverride
3846
4037
  } = options;
3847
4038
  const emitLanguages = emitLanguagesOverride?.length ? emitLanguagesOverride : ["ts"];
3848
4039
  const outDir = outDirOverride ?? "./pom";
3849
4040
  const routeMetaByComponent = routeMetaByComponentOverride ?? (vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType, {
3850
- viewsDir,
3851
- scanDirs
4041
+ pageDirs,
4042
+ componentDirs,
4043
+ layoutDirs
3852
4044
  }) : void 0);
3853
4045
  const generatedFilePaths = [];
3854
4046
  const writeGeneratedFile = (file) => {
@@ -6637,8 +6829,11 @@ function createBuildProcessorPlugin(options) {
6637
6829
  const {
6638
6830
  componentHierarchyMap,
6639
6831
  vueFilesPathMap,
6640
- viewsDir,
6641
- scanDirs,
6832
+ getPageDirs,
6833
+ getComponentDirs,
6834
+ getLayoutDirs,
6835
+ getViewsDir,
6836
+ getSourceDirs,
6642
6837
  basePageClassPath,
6643
6838
  normalizedBasePagePath,
6644
6839
  outDir,
@@ -6659,7 +6854,7 @@ function createBuildProcessorPlugin(options) {
6659
6854
  excludedComponents,
6660
6855
  getWrapperSearchRoots,
6661
6856
  routerAwarePoms,
6662
- resolvedRouterEntry,
6857
+ getResolvedRouterEntry,
6663
6858
  routerType,
6664
6859
  routerModuleShims,
6665
6860
  loggerRef
@@ -6669,7 +6864,8 @@ function createBuildProcessorPlugin(options) {
6669
6864
  interactiveComponentCount: 0,
6670
6865
  dataTestIdCount: 0
6671
6866
  };
6672
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
6867
+ const getViewsDirAbs = () => path.isAbsolute(getViewsDir()) ? getViewsDir() : path.resolve(projectRootRef.current, getViewsDir());
6868
+ const getPageDirsAbs = () => getPageDirs().map((dir) => path.isAbsolute(dir) ? dir : path.resolve(projectRootRef.current, dir));
6673
6869
  const getScriptInfo = (source, filename) => {
6674
6870
  try {
6675
6871
  const { descriptor } = compilerSfc.parse(source, { filename });
@@ -6709,7 +6905,7 @@ function createBuildProcessorPlugin(options) {
6709
6905
  return out;
6710
6906
  };
6711
6907
  let supplemented = 0;
6712
- for (const dir of scanDirs) {
6908
+ for (const dir of getSourceDirs()) {
6713
6909
  const absDir = path.resolve(projectRootRef.current, dir);
6714
6910
  if (!fs.existsSync(absDir))
6715
6911
  continue;
@@ -6719,7 +6915,7 @@ function createBuildProcessorPlugin(options) {
6719
6915
  filename: absolutePath,
6720
6916
  projectRoot: projectRootRef.current,
6721
6917
  viewsDirAbs: getViewsDirAbs(),
6722
- scanDirs,
6918
+ sourceDirs: getSourceDirs(),
6723
6919
  extraRoots: [process.cwd()]
6724
6920
  });
6725
6921
  if (componentHierarchyMap.has(componentName))
@@ -6794,16 +6990,17 @@ function createBuildProcessorPlugin(options) {
6794
6990
  }
6795
6991
  let result;
6796
6992
  if (routerType === "nuxt") {
6797
- result = await introspectNuxtPages(projectRootRef.current);
6993
+ result = await introspectNuxtPages(projectRootRef.current, { pageDirs: getPageDirsAbs() });
6798
6994
  } else {
6995
+ const resolvedRouterEntry = getResolvedRouterEntry();
6799
6996
  if (!resolvedRouterEntry)
6800
6997
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
6801
6998
  result = await parseRouterFileFromCwd(resolvedRouterEntry, {
6802
6999
  moduleShims: routerModuleShims,
6803
7000
  componentNaming: {
6804
7001
  projectRoot: projectRootRef.current,
6805
- viewsDirAbs: path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir),
6806
- scanDirs
7002
+ viewsDirAbs: getViewsDirAbs(),
7003
+ sourceDirs: getSourceDirs()
6807
7004
  }
6808
7005
  });
6809
7006
  }
@@ -6863,10 +7060,11 @@ function createBuildProcessorPlugin(options) {
6863
7060
  customPomImportNameCollisionBehavior,
6864
7061
  testIdAttribute,
6865
7062
  vueRouterFluentChaining: routerAwarePoms,
6866
- routerEntry: resolvedRouterEntry,
7063
+ routerEntry: getResolvedRouterEntry(),
6867
7064
  routerType,
6868
- viewsDir,
6869
- scanDirs
7065
+ pageDirs: getPageDirs(),
7066
+ componentDirs: getComponentDirs(),
7067
+ layoutDirs: getLayoutDirs()
6870
7068
  });
6871
7069
  lastGeneratedMetrics = metrics;
6872
7070
  loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
@@ -6880,8 +7078,11 @@ function createDevProcessorPlugin(options) {
6880
7078
  const {
6881
7079
  nativeWrappers,
6882
7080
  excludedComponents,
6883
- viewsDir,
6884
- scanDirs,
7081
+ getPageDirs,
7082
+ getComponentDirs,
7083
+ getLayoutDirs,
7084
+ getViewsDir,
7085
+ getSourceDirs,
6885
7086
  getWrapperSearchRoots,
6886
7087
  projectRootRef,
6887
7088
  normalizedBasePagePath,
@@ -6900,12 +7101,29 @@ function createDevProcessorPlugin(options) {
6900
7101
  existingIdBehavior,
6901
7102
  testIdAttribute,
6902
7103
  routerAwarePoms,
6903
- resolvedRouterEntry,
7104
+ getResolvedRouterEntry,
6904
7105
  routerType,
6905
7106
  routerModuleShims,
6906
7107
  loggerRef
6907
7108
  } = options;
6908
7109
  let scheduleVueFileRegen = null;
7110
+ const getProjectRootCandidates = () => Array.from(/* @__PURE__ */ new Set([
7111
+ path.resolve(projectRootRef.current),
7112
+ path.resolve(process.cwd())
7113
+ ]));
7114
+ const resolveProjectPath = (maybePath) => {
7115
+ if (path.isAbsolute(maybePath))
7116
+ return maybePath;
7117
+ const candidates = getProjectRootCandidates().map((root) => path.resolve(root, maybePath));
7118
+ return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
7119
+ };
7120
+ const getSourceDirRoots = () => Array.from(new Set(
7121
+ getProjectRootCandidates().flatMap((root) => getSourceDirs().map((dir) => path.resolve(root, dir)))
7122
+ ));
7123
+ const isContainedInScanDirs = (filePath) => {
7124
+ const absolutePath = path.resolve(filePath);
7125
+ return getSourceDirRoots().some((scanDirAbs) => isPathWithinDir(absolutePath, scanDirAbs));
7126
+ };
6909
7127
  return {
6910
7128
  name: "vue-pom-generator-dev",
6911
7129
  apply: "serve",
@@ -6916,16 +7134,13 @@ function createDevProcessorPlugin(options) {
6916
7134
  return;
6917
7135
  if (!ctx.file.endsWith(".vue"))
6918
7136
  return;
6919
- const isContainedInScanDirs = scanDirs.some((dir) => {
6920
- const absDir = path.resolve(projectRootRef.current, dir);
6921
- return ctx.file.startsWith(absDir + path.sep);
6922
- });
6923
- if (!isContainedInScanDirs)
7137
+ if (!isContainedInScanDirs(ctx.file))
6924
7138
  return;
6925
7139
  scheduleVueFileRegen(ctx.file, "hmr");
6926
7140
  },
6927
7141
  async configureServer(server) {
6928
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
7142
+ const getViewsDirAbs = () => resolveProjectPath(getViewsDir());
7143
+ const getPageDirsAbs = () => getPageDirs().map((dir) => resolveProjectPath(dir));
6929
7144
  const routerInitPromise = (async () => {
6930
7145
  if (!routerAwarePoms) {
6931
7146
  setRouteNameToComponentNameMap(/* @__PURE__ */ new Map());
@@ -6934,8 +7149,9 @@ function createDevProcessorPlugin(options) {
6934
7149
  }
6935
7150
  let result;
6936
7151
  if (routerType === "nuxt") {
6937
- result = await introspectNuxtPages(projectRootRef.current);
7152
+ result = await introspectNuxtPages(projectRootRef.current, { pageDirs: getPageDirsAbs() });
6938
7153
  } else {
7154
+ const resolvedRouterEntry = getResolvedRouterEntry();
6939
7155
  if (!resolvedRouterEntry)
6940
7156
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
6941
7157
  result = await parseRouterFileFromCwd(resolvedRouterEntry, {
@@ -6943,7 +7159,7 @@ function createDevProcessorPlugin(options) {
6943
7159
  componentNaming: {
6944
7160
  projectRoot: projectRootRef.current,
6945
7161
  viewsDirAbs: getViewsDirAbs(),
6946
- scanDirs,
7162
+ sourceDirs: getSourceDirs(),
6947
7163
  extraRoots: [process.cwd()]
6948
7164
  }
6949
7165
  });
@@ -7015,6 +7231,19 @@ function createDevProcessorPlugin(options) {
7015
7231
  let snapshotHierarchy = /* @__PURE__ */ new Map();
7016
7232
  let snapshotVuePathMap = /* @__PURE__ */ new Map();
7017
7233
  const filePathToComponentName = /* @__PURE__ */ new Map();
7234
+ const createEmptyComponentDependencies = (absolutePath) => {
7235
+ const viewsDirAbs = path.resolve(getViewsDirAbs());
7236
+ const relToViewsDir = path.relative(viewsDirAbs, absolutePath);
7237
+ const isView = !relToViewsDir.startsWith("..") && !path.isAbsolute(relToViewsDir);
7238
+ return {
7239
+ filePath: absolutePath,
7240
+ childrenComponentSet: /* @__PURE__ */ new Set(),
7241
+ usedComponentSet: /* @__PURE__ */ new Set(),
7242
+ dataTestIdSet: /* @__PURE__ */ new Set(),
7243
+ isView,
7244
+ methodsContent: ""
7245
+ };
7246
+ };
7018
7247
  const getComponentNameForFile = (filePath) => {
7019
7248
  const normalized = path.resolve(filePath);
7020
7249
  const existing = filePathToComponentName.get(normalized);
@@ -7024,7 +7253,7 @@ function createDevProcessorPlugin(options) {
7024
7253
  filename: normalized,
7025
7254
  projectRoot: projectRootRef.current,
7026
7255
  viewsDirAbs: getViewsDirAbs(),
7027
- scanDirs,
7256
+ sourceDirs: getSourceDirs(),
7028
7257
  extraRoots: [process.cwd()]
7029
7258
  });
7030
7259
  filePathToComponentName.set(normalized, name);
@@ -7034,8 +7263,6 @@ function createDevProcessorPlugin(options) {
7034
7263
  const started = node_perf_hooks.performance.now();
7035
7264
  const absolutePath = path.resolve(filePath);
7036
7265
  const componentName = getComponentNameForFile(absolutePath);
7037
- targetVuePathMap.set(componentName, absolutePath);
7038
- targetHierarchy.delete(componentName);
7039
7266
  let sfc = "";
7040
7267
  try {
7041
7268
  sfc = fs.readFileSync(absolutePath, "utf8");
@@ -7043,9 +7270,15 @@ function createDevProcessorPlugin(options) {
7043
7270
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: false };
7044
7271
  }
7045
7272
  const template = extractTemplateFromSfc(sfc, absolutePath);
7046
- if (!template.trim())
7273
+ if (!template.trim()) {
7274
+ targetVuePathMap.set(componentName, absolutePath);
7275
+ targetHierarchy.set(componentName, createEmptyComponentDependencies(absolutePath));
7047
7276
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: true };
7277
+ }
7048
7278
  const { bindings: bindingMetadata, isScriptSetup } = getScriptInfo(sfc, absolutePath);
7279
+ const provisionalHierarchy = /* @__PURE__ */ new Map();
7280
+ const provisionalVuePathMap = new Map(targetVuePathMap);
7281
+ provisionalVuePathMap.set(componentName, absolutePath);
7049
7282
  compilerDom__namespace.compile(template, {
7050
7283
  filename: absolutePath,
7051
7284
  prefixIdentifiers: true,
@@ -7054,7 +7287,7 @@ function createDevProcessorPlugin(options) {
7054
7287
  nodeTransforms: [
7055
7288
  createTestIdTransform(
7056
7289
  componentName,
7057
- targetHierarchy,
7290
+ provisionalHierarchy,
7058
7291
  nativeWrappers,
7059
7292
  excludedComponents,
7060
7293
  getViewsDirAbs(),
@@ -7064,23 +7297,27 @@ function createDevProcessorPlugin(options) {
7064
7297
  missingSemanticNameBehavior,
7065
7298
  testIdAttribute,
7066
7299
  warn: (message) => loggerRef.current.warn(message),
7067
- vueFilesPathMap: targetVuePathMap,
7300
+ vueFilesPathMap: provisionalVuePathMap,
7068
7301
  wrapperSearchRoots: getWrapperSearchRoots()
7069
7302
  }
7070
7303
  )
7071
7304
  ]
7072
7305
  });
7306
+ targetVuePathMap.set(componentName, absolutePath);
7307
+ targetHierarchy.set(
7308
+ componentName,
7309
+ provisionalHierarchy.get(componentName) ?? createEmptyComponentDependencies(absolutePath)
7310
+ );
7073
7311
  return { componentName, ms: node_perf_hooks.performance.now() - started, compiled: true };
7074
7312
  };
7075
- const fullRebuildSnapshotFromFilesystem = () => {
7313
+ const fullRebuildSnapshotFromFilesystem = (logLabel) => {
7076
7314
  const t0 = node_perf_hooks.performance.now();
7077
7315
  const nextHierarchy = /* @__PURE__ */ new Map();
7078
7316
  const nextVuePathMap = /* @__PURE__ */ new Map();
7079
7317
  filePathToComponentName.clear();
7080
7318
  let totalVueFiles = 0;
7081
7319
  let compiledCount = 0;
7082
- for (const dir of scanDirs) {
7083
- const absDir = path.resolve(projectRootRef.current, dir);
7320
+ for (const absDir of getSourceDirRoots()) {
7084
7321
  if (!fs.existsSync(absDir))
7085
7322
  continue;
7086
7323
  const vueFiles = walkFilesRecursive(absDir);
@@ -7094,12 +7331,12 @@ function createDevProcessorPlugin(options) {
7094
7331
  snapshotHierarchy = nextHierarchy;
7095
7332
  snapshotVuePathMap = nextVuePathMap;
7096
7333
  const t1 = node_perf_hooks.performance.now();
7097
- logInfo(`initial scan: found ${totalVueFiles} .vue files in ${scanDirs.join(", ")}`);
7098
- logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
7334
+ logInfo(`scan(${logLabel}): found ${totalVueFiles} .vue files in ${getSourceDirs().join(", ")}`);
7335
+ logInfo(`compile(${logLabel}): ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
7099
7336
  };
7100
- const generateAggregatedFromSnapshot = (reason) => {
7337
+ const generateAggregatedFromSnapshot = async (logLabel) => {
7101
7338
  const t0 = node_perf_hooks.performance.now();
7102
- generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
7339
+ await generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
7103
7340
  outDir,
7104
7341
  emitLanguages,
7105
7342
  typescriptOutputStructure,
@@ -7110,25 +7347,27 @@ function createDevProcessorPlugin(options) {
7110
7347
  customPomDir,
7111
7348
  customPomImportAliases,
7112
7349
  customPomImportNameCollisionBehavior,
7113
- viewsDir,
7114
- scanDirs,
7350
+ pageDirs: getPageDirs(),
7351
+ componentDirs: getComponentDirs(),
7352
+ layoutDirs: getLayoutDirs(),
7115
7353
  testIdAttribute,
7116
7354
  vueRouterFluentChaining: routerAwarePoms,
7117
- routerEntry: resolvedRouterEntry,
7355
+ routerEntry: getResolvedRouterEntry(),
7118
7356
  routerType
7119
7357
  });
7120
7358
  const t1 = node_perf_hooks.performance.now();
7121
- logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
7359
+ logInfo(`generate(${logLabel}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
7122
7360
  };
7123
7361
  let timer = null;
7124
7362
  let maxWaitTimer = null;
7125
7363
  const pendingChangedVueFiles = /* @__PURE__ */ new Set();
7126
7364
  const pendingDeletedComponents = /* @__PURE__ */ new Set();
7365
+ let regenerationSequence = Promise.resolve();
7127
7366
  const initialBuildPromise = (async () => {
7128
7367
  const t0 = node_perf_hooks.performance.now();
7129
7368
  await routerInitPromise;
7130
- fullRebuildSnapshotFromFilesystem();
7131
- generateAggregatedFromSnapshot("startup");
7369
+ fullRebuildSnapshotFromFilesystem("startup");
7370
+ await generateAggregatedFromSnapshot("startup");
7132
7371
  const t1 = node_perf_hooks.performance.now();
7133
7372
  logInfo(`startup total: ${formatMs(t1 - t0)}`);
7134
7373
  })();
@@ -7156,7 +7395,7 @@ function createDevProcessorPlugin(options) {
7156
7395
  snapshotHierarchy = nextHierarchy;
7157
7396
  snapshotVuePathMap = nextVuePathMap;
7158
7397
  const t1 = node_perf_hooks.performance.now();
7159
- generateAggregatedFromSnapshot(reason);
7398
+ await generateAggregatedFromSnapshot(reason);
7160
7399
  const t2 = node_perf_hooks.performance.now();
7161
7400
  return {
7162
7401
  files,
@@ -7167,7 +7406,12 @@ function createDevProcessorPlugin(options) {
7167
7406
  totalMs: t2 - t0
7168
7407
  };
7169
7408
  };
7170
- const watchedVueGlobs = scanDirs.map((dir) => path.resolve(projectRootRef.current, dir, "**", "*.vue"));
7409
+ const enqueueRegeneration = (reason) => {
7410
+ const currentRun = regenerationSequence.catch(() => void 0).then(() => regenerateFromPending(reason));
7411
+ regenerationSequence = currentRun.then(() => void 0, () => void 0);
7412
+ return currentRun;
7413
+ };
7414
+ const watchedVueGlobs = getSourceDirRoots().map((scanDirAbs) => path.resolve(scanDirAbs, "**", "*.vue"));
7171
7415
  const watchedPluginGlob = path.resolve(projectRootRef.current, "vite-plugins", "vue-pom-generator", "**", "*.ts");
7172
7416
  const runtimeDir = path.dirname(basePageClassPath);
7173
7417
  server.watcher.add([
@@ -7193,7 +7437,7 @@ function createDevProcessorPlugin(options) {
7193
7437
  timer = null;
7194
7438
  }
7195
7439
  maxWaitTimer = null;
7196
- void regenerateFromPending("max-wait").then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7440
+ void enqueueRegeneration("max-wait").then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7197
7441
  logInfo(
7198
7442
  `max-wait: files=${files.length} deleted=${deletedCount} compile=${formatMs(compileMs)} wall=${formatMs(preGenerateMs)} gen=${formatMs(generateMs)} total=${formatMs(totalMs)}`
7199
7443
  );
@@ -7217,7 +7461,7 @@ function createDevProcessorPlugin(options) {
7217
7461
  maxWaitTimer = null;
7218
7462
  }
7219
7463
  const reason = pendingChangedVueFiles.size || pendingDeletedComponents.size ? "batched" : "noop";
7220
- void regenerateFromPending(reason).then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7464
+ void enqueueRegeneration(reason).then(({ files, deletedCount, compileMs, preGenerateMs, generateMs, totalMs }) => {
7221
7465
  if (files.length || deletedCount) {
7222
7466
  logInfo(
7223
7467
  `batched: files=${files.length} deleted=${deletedCount} compile=${formatMs(compileMs)} wall=${formatMs(preGenerateMs)} gen=${formatMs(generateMs)} total=${formatMs(totalMs)}`
@@ -7244,11 +7488,7 @@ function createDevProcessorPlugin(options) {
7244
7488
  return;
7245
7489
  if (!p.endsWith(".vue"))
7246
7490
  return;
7247
- const isContainedInScanDirs = scanDirs.some((dir) => {
7248
- const absDir = path.resolve(projectRootRef.current, dir);
7249
- return p.startsWith(absDir + path.sep);
7250
- });
7251
- if (!isContainedInScanDirs)
7491
+ if (!isContainedInScanDirs(p))
7252
7492
  return;
7253
7493
  void (async () => {
7254
7494
  await initialBuildPromise;
@@ -7261,11 +7501,7 @@ function createDevProcessorPlugin(options) {
7261
7501
  return;
7262
7502
  if (!p.endsWith(".vue"))
7263
7503
  return;
7264
- const isContainedInScanDirs = scanDirs.some((dir) => {
7265
- const absDir = path.resolve(projectRootRef.current, dir);
7266
- return p.startsWith(absDir + path.sep);
7267
- });
7268
- if (!isContainedInScanDirs)
7504
+ if (!isContainedInScanDirs(p))
7269
7505
  return;
7270
7506
  void (async () => {
7271
7507
  await initialBuildPromise;
@@ -7329,8 +7565,11 @@ function createSupportPlugins(options) {
7329
7565
  vueFilesPathMap,
7330
7566
  nativeWrappers,
7331
7567
  excludedComponents,
7332
- viewsDir,
7333
- scanDirs,
7568
+ getPageDirs,
7569
+ getComponentDirs,
7570
+ getLayoutDirs,
7571
+ getViewsDir,
7572
+ getSourceDirs,
7334
7573
  getWrapperSearchRoots,
7335
7574
  nameCollisionBehavior = "suffix",
7336
7575
  missingSemanticNameBehavior,
@@ -7362,7 +7601,6 @@ function createSupportPlugins(options) {
7362
7601
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
7363
7602
  return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(projectRootRef.current, routerEntry);
7364
7603
  };
7365
- const resolvedRouterEntry = resolveRouterEntry2();
7366
7604
  const getDefaultBasePageClassPath = () => {
7367
7605
  try {
7368
7606
  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));
@@ -7375,8 +7613,11 @@ function createSupportPlugins(options) {
7375
7613
  const tsProcessor = createBuildProcessorPlugin({
7376
7614
  componentHierarchyMap,
7377
7615
  vueFilesPathMap,
7378
- viewsDir,
7379
- scanDirs,
7616
+ getPageDirs,
7617
+ getComponentDirs,
7618
+ getLayoutDirs,
7619
+ getViewsDir,
7620
+ getSourceDirs,
7380
7621
  basePageClassPath,
7381
7622
  normalizedBasePagePath,
7382
7623
  outDir,
@@ -7398,15 +7639,18 @@ function createSupportPlugins(options) {
7398
7639
  getWrapperSearchRoots,
7399
7640
  routerAwarePoms,
7400
7641
  routerType,
7401
- resolvedRouterEntry,
7642
+ getResolvedRouterEntry: resolveRouterEntry2,
7402
7643
  routerModuleShims,
7403
7644
  loggerRef
7404
7645
  });
7405
7646
  const devProcessor = createDevProcessorPlugin({
7406
7647
  nativeWrappers,
7407
7648
  excludedComponents,
7408
- viewsDir,
7409
- scanDirs,
7649
+ getPageDirs,
7650
+ getComponentDirs,
7651
+ getLayoutDirs,
7652
+ getViewsDir,
7653
+ getSourceDirs,
7410
7654
  getWrapperSearchRoots,
7411
7655
  projectRootRef,
7412
7656
  normalizedBasePagePath,
@@ -7426,7 +7670,7 @@ function createSupportPlugins(options) {
7426
7670
  testIdAttribute,
7427
7671
  routerAwarePoms,
7428
7672
  routerType,
7429
- resolvedRouterEntry,
7673
+ getResolvedRouterEntry: resolveRouterEntry2,
7430
7674
  routerModuleShims,
7431
7675
  loggerRef
7432
7676
  });
@@ -7584,7 +7828,7 @@ function createVuePluginWithTestIds(options) {
7584
7828
  getViewsDirAbs,
7585
7829
  testIdAttribute,
7586
7830
  loggerRef,
7587
- scanDirs = ["src"],
7831
+ getSourceDirs,
7588
7832
  getWrapperSearchRoots,
7589
7833
  getProjectRoot
7590
7834
  } = options;
@@ -7593,7 +7837,7 @@ function createVuePluginWithTestIds(options) {
7593
7837
  filename,
7594
7838
  projectRoot: getProjectRoot(),
7595
7839
  viewsDirAbs: getViewsDirAbs(),
7596
- scanDirs,
7840
+ sourceDirs: getSourceDirs(),
7597
7841
  extraRoots: [process.cwd()]
7598
7842
  });
7599
7843
  };
@@ -7609,7 +7853,7 @@ function createVuePluginWithTestIds(options) {
7609
7853
  if (absFilename.startsWith(viewsDirAbs + path.sep) || absFilename === viewsDirAbs)
7610
7854
  return true;
7611
7855
  const rootsToTry = [projectRoot, process.cwd()];
7612
- const matched = scanDirs.some((dir) => {
7856
+ const matched = getSourceDirs().some((dir) => {
7613
7857
  return rootsToTry.some((root) => {
7614
7858
  const absDir = path.resolve(root, dir);
7615
7859
  if (absFilename.startsWith(absDir + path.sep) || absFilename === absDir)
@@ -7784,6 +8028,25 @@ function createVuePluginWithTestIds(options) {
7784
8028
  };
7785
8029
  return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
7786
8030
  }
8031
+ const nuxtConfigMarker$1 = /* @__PURE__ */ Symbol.for("@immense/vue-pom-generator.nuxt");
8032
+ const nuxtConfigFileNames = [
8033
+ "nuxt.config.ts",
8034
+ "nuxt.config.js",
8035
+ "nuxt.config.mjs",
8036
+ "nuxt.config.cjs",
8037
+ "nuxt.config.mts",
8038
+ "nuxt.config.cts",
8039
+ ".nuxtrc"
8040
+ ];
8041
+ const nuxtSourceMarkers = [
8042
+ "app.vue",
8043
+ "app",
8044
+ "pages",
8045
+ "layouts",
8046
+ "components",
8047
+ "layers",
8048
+ ".nuxt"
8049
+ ];
7787
8050
  function assertNonEmptyString(value, name) {
7788
8051
  if (!value || !value.trim()) {
7789
8052
  throw new Error(`${name} must be a non-empty string.`);
@@ -7831,6 +8094,57 @@ function resolveMissingSemanticNameBehavior(value) {
7831
8094
  }
7832
8095
  return value.missingSemanticNameBehavior ?? "ignore";
7833
8096
  }
8097
+ function readPackageJson(projectRoot) {
8098
+ const packageJsonPath = path.join(projectRoot, "package.json");
8099
+ if (!fs.existsSync(packageJsonPath)) {
8100
+ return null;
8101
+ }
8102
+ try {
8103
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
8104
+ } catch {
8105
+ return null;
8106
+ }
8107
+ }
8108
+ function recordHasOwnStringKey(value, key) {
8109
+ return value !== null && value !== void 0 && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, key) && typeof value[key] === "string";
8110
+ }
8111
+ function projectPackageLooksNuxt(projectRoot) {
8112
+ const packageJson = readPackageJson(projectRoot);
8113
+ if (!packageJson) {
8114
+ return false;
8115
+ }
8116
+ const dependencyGroups = [
8117
+ packageJson.dependencies,
8118
+ packageJson.devDependencies,
8119
+ packageJson.peerDependencies,
8120
+ packageJson.optionalDependencies
8121
+ ];
8122
+ if (dependencyGroups.some((group) => {
8123
+ const dependencyGroup = typeof group === "object" && group !== null ? group : void 0;
8124
+ return recordHasOwnStringKey(dependencyGroup, "nuxt") || recordHasOwnStringKey(dependencyGroup, "nuxt-nightly");
8125
+ })) {
8126
+ return true;
8127
+ }
8128
+ if (typeof packageJson.scripts !== "object" || packageJson.scripts === null || Array.isArray(packageJson.scripts)) {
8129
+ return false;
8130
+ }
8131
+ return Object.values(packageJson.scripts).some((script) => {
8132
+ if (typeof script !== "string") {
8133
+ return false;
8134
+ }
8135
+ const normalizedScript = script.trim();
8136
+ return normalizedScript === "nuxt" || normalizedScript.startsWith("nuxt ") || normalizedScript === "nuxi" || normalizedScript.startsWith("nuxi ");
8137
+ });
8138
+ }
8139
+ function detectNuxtProject(options, projectRoot) {
8140
+ if (options[nuxtConfigMarker$1] === true) {
8141
+ return true;
8142
+ }
8143
+ if (nuxtConfigFileNames.some((fileName) => fs.existsSync(path.join(projectRoot, fileName)))) {
8144
+ return true;
8145
+ }
8146
+ return projectPackageLooksNuxt(projectRoot) && nuxtSourceMarkers.some((entry) => fs.existsSync(path.join(projectRoot, entry)));
8147
+ }
7834
8148
  function assertRouterModuleShims(value, name) {
7835
8149
  if (!value)
7836
8150
  return;
@@ -7938,14 +8252,18 @@ function assertNotVitePluginInstance(options) {
7938
8252
  function createVuePomGeneratorPlugins(options = {}) {
7939
8253
  assertNotVitePluginInstance(options);
7940
8254
  const injection = options.injection ?? {};
8255
+ const isNuxt = detectNuxtProject(options, process.cwd());
7941
8256
  const generationSetting = options.generation;
7942
8257
  const generationOptions = generationSetting === false ? null : generationSetting ?? {};
7943
8258
  const generationEnabled = generationOptions !== null;
8259
+ const vueGenerationOptions = generationOptions;
7944
8260
  const verbosity = options.logging?.verbosity ?? "warn";
7945
8261
  const vueOptions = options.vueOptions;
7946
- const viewsDir = injection.viewsDir ?? "src/views";
7947
- const scanDirs = injection.scanDirs ?? ["src"];
7948
- const wrapperSearchRoots = injection.wrapperSearchRoots ?? [];
8262
+ const legacyVueOptions = options;
8263
+ const pageDirsRef = { current: !isNuxt ? [legacyVueOptions.injection?.viewsDir ?? "src/views"] : ["app/pages"] };
8264
+ const componentDirsRef = { current: !isNuxt ? legacyVueOptions.injection?.componentDirs ?? ["src/components"] : ["app/components"] };
8265
+ const layoutDirsRef = { current: !isNuxt ? legacyVueOptions.injection?.layoutDirs ?? ["src/layouts"] : ["app/layouts"] };
8266
+ const wrapperSearchRootsRef = { current: !isNuxt ? legacyVueOptions.injection?.wrapperSearchRoots ?? [] : [] };
7949
8267
  const nativeWrappers = injection.nativeWrappers ?? {};
7950
8268
  const excludedComponents = injection.excludeComponents ?? [];
7951
8269
  const testIdAttribute = (injection.attribute ?? "data-testid").trim() || "data-testid";
@@ -7953,10 +8271,9 @@ function createVuePomGeneratorPlugins(options = {}) {
7953
8271
  const outDir = (generationOptions?.outDir ?? "tests/playwright/__generated__").trim();
7954
8272
  const emitLanguages = generationOptions?.emit && generationOptions.emit.length ? generationOptions.emit : ["ts"];
7955
8273
  const nameCollisionBehavior = generationOptions?.nameCollisionBehavior ?? "suffix";
7956
- const routerEntry = generationOptions?.router?.entry;
7957
- const routerType = generationOptions?.router?.type ?? "vue-router";
7958
- const routerModuleShims = generationOptions?.router?.moduleShims;
7959
- const isNuxt = routerType === "nuxt";
8274
+ const routerEntry = !isNuxt ? vueGenerationOptions?.router?.entry : void 0;
8275
+ const routerType = isNuxt ? "nuxt" : vueGenerationOptions?.router?.type ?? "vue-router";
8276
+ const routerModuleShims = !isNuxt ? vueGenerationOptions?.router?.moduleShims : void 0;
7960
8277
  if (isNuxt && options.vuePluginOwnership === "internal") {
7961
8278
  throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
7962
8279
  }
@@ -7973,17 +8290,30 @@ function createVuePomGeneratorPlugins(options = {}) {
7973
8290
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
7974
8291
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
7975
8292
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
8293
+ const getPageDirs = () => pageDirsRef.current;
8294
+ const getViewsDir = () => getPageDirs()[0] ?? "src/views";
8295
+ const getComponentDirs = () => componentDirsRef.current;
8296
+ const getLayoutDirs = () => layoutDirsRef.current;
8297
+ const getSourceDirs = () => Array.from(/* @__PURE__ */ new Set([
8298
+ ...getPageDirs(),
8299
+ ...getComponentDirs(),
8300
+ ...getLayoutDirs()
8301
+ ]));
8302
+ const getWrapperSearchRoots = () => wrapperSearchRootsRef.current;
7976
8303
  const sharedStateKey = JSON.stringify({
7977
8304
  cwd: process.cwd(),
7978
- viewsDir,
7979
- scanDirs,
7980
- wrapperSearchRoots,
8305
+ mode: isNuxt ? "nuxt" : "vue",
8306
+ pageDirs: isNuxt ? null : getPageDirs(),
8307
+ componentDirs: isNuxt ? null : getComponentDirs(),
8308
+ layoutDirs: isNuxt ? null : getLayoutDirs(),
8309
+ wrapperSearchRoots: isNuxt ? null : getWrapperSearchRoots(),
7981
8310
  outDir,
7982
8311
  testIdAttribute,
7983
8312
  routerType,
7984
8313
  vuePluginOwnership
7985
8314
  });
7986
8315
  const sharedState = getSharedGeneratorState(sharedStateKey);
8316
+ let templateCompilerOptionsForResolvedPlugin;
7987
8317
  const projectRootRef = { current: process.cwd() };
7988
8318
  const loggerRef = {
7989
8319
  current: createLogger({ verbosity })
@@ -7991,30 +8321,50 @@ function createVuePomGeneratorPlugins(options = {}) {
7991
8321
  const configPlugin = {
7992
8322
  name: "vue-pom-generator-config",
7993
8323
  enforce: "pre",
7994
- configResolved(config) {
8324
+ async configResolved(config) {
7995
8325
  projectRootRef.current = config.root;
7996
8326
  loggerRef.current = createLogger({ verbosity, viteLogger: config.logger });
8327
+ if (vueGenerationOptions?.router?.type === "nuxt") {
8328
+ throw new Error('[vue-pom-generator] Remove generation.router.type="nuxt". Nuxt projects are auto-detected.');
8329
+ }
8330
+ if (isNuxt) {
8331
+ const nuxtDiscovery = await loadNuxtProjectDiscovery(process.cwd());
8332
+ projectRootRef.current = nuxtDiscovery.rootDir;
8333
+ pageDirsRef.current = nuxtDiscovery.pageDirs.length ? nuxtDiscovery.pageDirs : [path.resolve(nuxtDiscovery.srcDir, "pages")];
8334
+ componentDirsRef.current = nuxtDiscovery.componentDirs;
8335
+ layoutDirsRef.current = nuxtDiscovery.layoutDirs;
8336
+ wrapperSearchRootsRef.current = nuxtDiscovery.wrapperSearchRoots;
8337
+ }
7997
8338
  assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
7998
- assertNonEmptyString(viewsDir, "[vue-pom-generator] injection.viewsDir");
7999
- assertNonEmptyStringArray(wrapperSearchRoots, "[vue-pom-generator] injection.wrapperSearchRoots");
8339
+ assertNonEmptyString(getViewsDir(), "[vue-pom-generator] injection.viewsDir");
8340
+ assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
8341
+ assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
8342
+ assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
8000
8343
  assertErrorBehavior(errorBehavior, "[vue-pom-generator] errorBehavior");
8001
8344
  if (generationEnabled) {
8002
8345
  assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
8003
8346
  assertOneOf(typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
8004
8347
  assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
8005
- if (generationOptions?.router && routerType === "vue-router") {
8348
+ if (!isNuxt && vueGenerationOptions?.router && routerType === "vue-router") {
8006
8349
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
8007
8350
  }
8008
8351
  }
8009
8352
  if (usesExternalVuePlugin) {
8010
- applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
8353
+ applyTemplateCompilerOptionsToResolvedVuePlugin(
8354
+ config,
8355
+ templateCompilerOptionsForResolvedPlugin,
8356
+ isNuxt ? "nuxt" : vuePluginOwnership
8357
+ );
8011
8358
  }
8012
8359
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
8360
+ loggerRef.current.info(`viewsDir=${getViewsDir()}`);
8361
+ loggerRef.current.info(`componentDirs=${getComponentDirs().join(", ")}`);
8362
+ loggerRef.current.info(`layoutDirs=${getLayoutDirs().join(", ")}`);
8013
8363
  loggerRef.current.info(`Active plugins: ${(config.plugins ?? []).map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
8014
8364
  }
8015
8365
  };
8016
- const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
8017
- const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8366
+ const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, getViewsDir());
8367
+ const getWrapperSearchRootsAbs = () => getWrapperSearchRoots().map((root) => resolveFromProjectRoot(projectRootRef.current, root));
8018
8368
  const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
8019
8369
  const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
8020
8370
  vueOptions,
@@ -8029,10 +8379,11 @@ function createVuePomGeneratorPlugins(options = {}) {
8029
8379
  getViewsDirAbs,
8030
8380
  testIdAttribute,
8031
8381
  loggerRef,
8032
- scanDirs,
8382
+ getSourceDirs,
8033
8383
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
8034
8384
  getProjectRoot: () => projectRootRef.current
8035
8385
  });
8386
+ templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
8036
8387
  const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
8037
8388
  const supportPlugins = createSupportPlugins({
8038
8389
  componentTestIds,
@@ -8040,8 +8391,11 @@ function createVuePomGeneratorPlugins(options = {}) {
8040
8391
  vueFilesPathMap,
8041
8392
  nativeWrappers,
8042
8393
  excludedComponents,
8043
- viewsDir,
8044
- scanDirs,
8394
+ getPageDirs,
8395
+ getComponentDirs,
8396
+ getLayoutDirs,
8397
+ getViewsDir,
8398
+ getSourceDirs,
8045
8399
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
8046
8400
  nameCollisionBehavior,
8047
8401
  missingSemanticNameBehavior,
@@ -8086,11 +8440,21 @@ function createVuePomGeneratorPlugins(options = {}) {
8086
8440
  }
8087
8441
  return resultPlugins;
8088
8442
  }
8443
+ const nuxtConfigMarker = /* @__PURE__ */ Symbol.for("@immense/vue-pom-generator.nuxt");
8089
8444
  function defineVuePomGeneratorConfig(options) {
8090
8445
  return options;
8091
8446
  }
8447
+ function defineNuxtPomGeneratorConfig(options) {
8448
+ const markedOptions = { ...options };
8449
+ Object.defineProperty(markedOptions, nuxtConfigMarker, {
8450
+ value: true,
8451
+ enumerable: false
8452
+ });
8453
+ return markedOptions;
8454
+ }
8092
8455
  exports.createVuePomGeneratorPlugins = createVuePomGeneratorPlugins;
8093
8456
  exports.default = createVuePomGeneratorPlugins;
8457
+ exports.defineNuxtPomGeneratorConfig = defineNuxtPomGeneratorConfig;
8094
8458
  exports.defineVuePomGeneratorConfig = defineVuePomGeneratorConfig;
8095
8459
  exports.vuePomGenerator = createVuePomGeneratorPlugins;
8096
8460
  //# sourceMappingURL=index.cjs.map