@immense/vue-pom-generator 1.0.56 → 1.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/RELEASE_NOTES.md CHANGED
@@ -1,27 +1,30 @@
1
1
  ● ## Highlights
2
2
 
3
- - Added support for awaited handler wrappers in POM generation
4
- - Enhanced async/await pattern detection in Vue event handlers
5
- - Improved test coverage with 39 new test lines across transform and utils
3
+ - Fixed router introspection cleanup to properly restore DOM globals after execution
4
+ - Improved test coverage for router introspection with 83 new test lines
5
+ - Refactored router-introspection implementation for better DOM shim management
6
6
 
7
7
  ## Changes
8
8
 
9
- - **Handler Detection:** `utils.ts` now correctly identifies and processes event handlers
10
- wrapped in `await` expressions
11
- - **Test Coverage:** Added comprehensive tests for awaited handler scenarios in
12
- `transform.test.ts` and `utils-coverage.test.ts`
9
+ ### Bug Fixes
10
+ - **Router introspection**: Restored router DOM globals after introspection to prevent state
11
+ pollution between test runs and avoid interfering with downstream code that depends on these
12
+ globals
13
+
14
+ ### Testing
15
+ - Added comprehensive test coverage for router DOM global restoration behavior
13
16
 
14
17
  ## Breaking Changes
15
18
 
16
- None.
19
+ None
17
20
 
18
21
  ## Pull Requests Included
19
22
 
20
- - #17 fix: support awaited handler wrappers
21
- (https://github.com/immense/vue-pom-generator/pull/17) by @dkattan
23
+ - #18 fix: restore router DOM globals after introspection
24
+ (https://github.com/immense/vue-pom-generator/pull/18)
22
25
 
23
26
  ## Testing
24
27
 
25
- Added 39 lines of new tests covering awaited handler wrapper scenarios and edge cases. All tests
26
- passing.
28
+ Added 83 lines of test coverage for router introspection DOM global handling. All existing tests
29
+ continue to pass.
27
30
 
package/dist/index.cjs CHANGED
@@ -3157,6 +3157,56 @@ function createRouterIntrospectionVueStubPlugin(options) {
3157
3157
  }
3158
3158
  };
3159
3159
  }
3160
+ function snapshotGlobalValue(name) {
3161
+ const g = globalThis;
3162
+ return {
3163
+ descriptor: Object.getOwnPropertyDescriptor(globalThis, name),
3164
+ value: g[name]
3165
+ };
3166
+ }
3167
+ function setTemporaryGlobal(name, value, snapshots) {
3168
+ if (!snapshots.has(name))
3169
+ snapshots.set(name, snapshotGlobalValue(name));
3170
+ const snapshot = snapshots.get(name);
3171
+ if (!snapshot)
3172
+ return false;
3173
+ if (!snapshot.descriptor || snapshot.descriptor.configurable) {
3174
+ Object.defineProperty(globalThis, name, {
3175
+ configurable: true,
3176
+ enumerable: snapshot.descriptor?.enumerable ?? true,
3177
+ writable: true,
3178
+ value
3179
+ });
3180
+ return true;
3181
+ }
3182
+ if ("writable" in snapshot.descriptor && snapshot.descriptor.writable) {
3183
+ Reflect.set(globalThis, name, value);
3184
+ return true;
3185
+ }
3186
+ if (snapshot.descriptor.set) {
3187
+ snapshot.descriptor.set.call(globalThis, value);
3188
+ return true;
3189
+ }
3190
+ return false;
3191
+ }
3192
+ function restoreTemporaryGlobals(snapshots) {
3193
+ for (const [name, snapshot] of Array.from(snapshots.entries()).reverse()) {
3194
+ const { descriptor, value } = snapshot;
3195
+ if (!descriptor) {
3196
+ Reflect.deleteProperty(globalThis, name);
3197
+ continue;
3198
+ }
3199
+ if (descriptor.configurable) {
3200
+ Object.defineProperty(globalThis, name, descriptor);
3201
+ continue;
3202
+ }
3203
+ if ("writable" in descriptor && descriptor.writable) {
3204
+ Reflect.set(globalThis, name, value);
3205
+ continue;
3206
+ }
3207
+ descriptor.set?.call(globalThis, value);
3208
+ }
3209
+ }
3160
3210
  const PARAM_TOKEN_PREFIX = "__VUE_TESTID_PARAM__";
3161
3211
  function getParamToken(name) {
3162
3212
  return `${PARAM_TOKEN_PREFIX}${name}__`;
@@ -3505,12 +3555,18 @@ function createMinimalLocation() {
3505
3555
  ancestorOrigins: { length: 0, contains: () => false, item: () => null, [Symbol.iterator]: [][Symbol.iterator] }
3506
3556
  };
3507
3557
  }
3508
- async function ensureDomShim() {
3558
+ function ensureDomShim() {
3509
3559
  const g = globalThis;
3510
3560
  if (typeof document !== "undefined" && typeof window !== "undefined")
3511
- return;
3561
+ return () => {
3562
+ };
3512
3563
  const minimalDoc = createMinimalDocument();
3513
3564
  const minimalLocation = createMinimalLocation();
3565
+ const snapshots = /* @__PURE__ */ new Map();
3566
+ const setGlobal = (name, value) => {
3567
+ if (!setTemporaryGlobal(name, value, snapshots))
3568
+ debugLog(`could not temporarily install global ${name}`);
3569
+ };
3514
3570
  const win = {
3515
3571
  document: minimalDoc,
3516
3572
  location: minimalLocation,
@@ -3555,17 +3611,17 @@ async function ensureDomShim() {
3555
3611
  queueMicrotask,
3556
3612
  performance: globalThis.performance
3557
3613
  };
3558
- g.window = win;
3559
- g.document = minimalDoc;
3560
- g.location = minimalLocation;
3614
+ setGlobal("window", win);
3615
+ setGlobal("document", minimalDoc);
3616
+ setGlobal("location", minimalLocation);
3561
3617
  if (!g.self)
3562
- g.self = win;
3618
+ setGlobal("self", win);
3563
3619
  if (!g.navigator)
3564
- g.navigator = win.navigator;
3620
+ setGlobal("navigator", win.navigator);
3565
3621
  if (!g.history)
3566
- g.history = win.history;
3622
+ setGlobal("history", win.history);
3567
3623
  if (!g.MutationObserver) {
3568
- g.MutationObserver = class {
3624
+ setGlobal("MutationObserver", class {
3569
3625
  disconnect() {
3570
3626
  }
3571
3627
  observe() {
@@ -3573,20 +3629,20 @@ async function ensureDomShim() {
3573
3629
  takeRecords() {
3574
3630
  return [];
3575
3631
  }
3576
- };
3632
+ });
3577
3633
  }
3578
3634
  if (!g.ResizeObserver) {
3579
- g.ResizeObserver = class {
3635
+ setGlobal("ResizeObserver", class {
3580
3636
  disconnect() {
3581
3637
  }
3582
3638
  observe() {
3583
3639
  }
3584
3640
  unobserve() {
3585
3641
  }
3586
- };
3642
+ });
3587
3643
  }
3588
3644
  if (!g.IntersectionObserver) {
3589
- g.IntersectionObserver = class {
3645
+ setGlobal("IntersectionObserver", class {
3590
3646
  disconnect() {
3591
3647
  }
3592
3648
  observe() {
@@ -3596,10 +3652,10 @@ async function ensureDomShim() {
3596
3652
  takeRecords() {
3597
3653
  return [];
3598
3654
  }
3599
- };
3655
+ });
3600
3656
  }
3601
3657
  if (!g.requestIdleCallback) {
3602
- g.requestIdleCallback = (cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1);
3658
+ setGlobal("requestIdleCallback", (cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1));
3603
3659
  }
3604
3660
  if (!g.localStorage || !g.sessionStorage) {
3605
3661
  const storageFactory = () => {
@@ -3622,12 +3678,15 @@ async function ensureDomShim() {
3622
3678
  };
3623
3679
  };
3624
3680
  if (!g.localStorage)
3625
- g.localStorage = storageFactory();
3681
+ setGlobal("localStorage", storageFactory());
3626
3682
  if (!g.sessionStorage)
3627
- g.sessionStorage = storageFactory();
3683
+ setGlobal("sessionStorage", storageFactory());
3628
3684
  }
3629
3685
  if (!g.requestAnimationFrame)
3630
- g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 16);
3686
+ setGlobal("requestAnimationFrame", (cb) => setTimeout(() => cb(Date.now()), 16));
3687
+ return () => {
3688
+ restoreTemporaryGlobals(snapshots);
3689
+ };
3631
3690
  }
3632
3691
  function unwrapNuxtPageSegment(segment, prefix, suffix) {
3633
3692
  if (!segment.startsWith(prefix) || !segment.endsWith(suffix))
@@ -3745,85 +3804,89 @@ async function parseRouterFileFromCwd(routerEntryPath, options = {}) {
3745
3804
  }
3746
3805
  const cwd = path.dirname(routerEntry);
3747
3806
  const moduleShims = normalizeRouterIntrospectionModuleShims(options.moduleShims);
3748
- await ensureDomShim();
3749
- debugLog(`parseRouterFileFromCwd cwd=${cwd}`);
3750
- const vite = await import("vite");
3751
- const server = await vite.createServer({
3752
- root: cwd,
3753
- configFile: false,
3754
- logLevel: "error",
3755
- // This server is created only to SSR-load the router module. Disable HMR/WebSocket
3756
- // to avoid port conflicts in dev/test environments.
3757
- server: { middlewareMode: true, hmr: false, ws: false },
3758
- appType: "custom",
3759
- // IMPORTANT:
3760
- // This internal, short-lived Vite server exists only to `ssrLoadModule()` the router entry.
3761
- // We close it immediately after reading routes.
3762
- //
3763
- // Vite's dependency optimizer (vite:dep-scan / optimizeDeps) runs asynchronously and can
3764
- // still have pending resolve requests when we call `server.close()`, which surfaces as:
3765
- // "The server is being restarted or closed. Request is outdated [plugin vite:dep-scan]"
3766
- //
3767
- // Disable optimizeDeps entirely for this internal server to avoid that race.
3768
- optimizeDeps: {
3769
- disabled: true
3770
- },
3771
- resolve: {
3772
- alias: {
3773
- "@": cwd
3774
- }
3775
- },
3776
- // Important: Do NOT include @vitejs/plugin-vue here.
3777
- // We stub all `.vue` imports ourselves, and including the Vue plugin would attempt to parse
3778
- // those stubbed modules as real SFCs (and fail).
3779
- plugins: [createRouterIntrospectionVueStubPlugin({ routerEntryAbs: routerEntry, moduleShims })]
3780
- });
3807
+ const restoreDomShim = ensureDomShim();
3781
3808
  try {
3782
- const moduleId = node_url.pathToFileURL(routerEntry).href;
3783
- debugLog(`ssrLoadModule(${moduleId}) start`);
3784
- const mod = await server.ssrLoadModule(moduleId);
3785
- debugLog(`ssrLoadModule(${moduleId}) done; hasDefault=${typeof mod?.default === "function"}`);
3786
- const makeRouter = mod?.default;
3787
- if (typeof makeRouter !== "function") {
3788
- throw new TypeError(`[vue-pom-generator] ${routerEntry} must export a default router factory function (export default makeRouter).`);
3789
- }
3790
- let router;
3809
+ debugLog(`parseRouterFileFromCwd cwd=${cwd}`);
3810
+ const vite = await import("vite");
3811
+ const server = await vite.createServer({
3812
+ root: cwd,
3813
+ configFile: false,
3814
+ logLevel: "error",
3815
+ // This server is created only to SSR-load the router module. Disable HMR/WebSocket
3816
+ // to avoid port conflicts in dev/test environments.
3817
+ server: { middlewareMode: true, hmr: false, ws: false },
3818
+ appType: "custom",
3819
+ // IMPORTANT:
3820
+ // This internal, short-lived Vite server exists only to `ssrLoadModule()` the router entry.
3821
+ // We close it immediately after reading routes.
3822
+ //
3823
+ // Vite's dependency optimizer (vite:dep-scan / optimizeDeps) runs asynchronously and can
3824
+ // still have pending resolve requests when we call `server.close()`, which surfaces as:
3825
+ // "The server is being restarted or closed. Request is outdated [plugin vite:dep-scan]"
3826
+ //
3827
+ // Disable optimizeDeps entirely for this internal server to avoid that race.
3828
+ optimizeDeps: {
3829
+ disabled: true
3830
+ },
3831
+ resolve: {
3832
+ alias: {
3833
+ "@": cwd
3834
+ }
3835
+ },
3836
+ // Important: Do NOT include @vitejs/plugin-vue here.
3837
+ // We stub all `.vue` imports ourselves, and including the Vue plugin would attempt to parse
3838
+ // those stubbed modules as real SFCs (and fail).
3839
+ plugins: [createRouterIntrospectionVueStubPlugin({ routerEntryAbs: routerEntry, moduleShims })]
3840
+ });
3791
3841
  try {
3792
- router = makeRouter();
3793
- } catch (err) {
3794
- throw new Error(`[vue-pom-generator] makeRouter() invocation failed: ${String(err)}`);
3795
- }
3796
- const routeNameMap = /* @__PURE__ */ new Map();
3797
- const routePathMap = /* @__PURE__ */ new Map();
3798
- const routeMetaEntries = [];
3799
- for (const r of router.getRoutes()) {
3800
- const componentInfo = await getComponentInfoFromRouteRecord(r, { rootDir: cwd });
3801
- const componentName = resolveIntrospectedComponentName(componentInfo, options.componentNaming);
3802
- if (!componentName)
3803
- continue;
3804
- if (typeof r.path === "string" && r.path.length) {
3805
- routePathMap.set(r.path, componentName);
3842
+ const moduleId = node_url.pathToFileURL(routerEntry).href;
3843
+ debugLog(`ssrLoadModule(${moduleId}) start`);
3844
+ const mod = await server.ssrLoadModule(moduleId);
3845
+ debugLog(`ssrLoadModule(${moduleId}) done; hasDefault=${typeof mod?.default === "function"}`);
3846
+ const makeRouter = mod?.default;
3847
+ if (typeof makeRouter !== "function") {
3848
+ throw new TypeError(`[vue-pom-generator] ${routerEntry} must export a default router factory function (export default makeRouter).`);
3806
3849
  }
3807
- if (typeof r.name === "string" && r.name.length) {
3808
- const key = toPascalCase(r.name);
3809
- routeNameMap.set(key, componentName);
3850
+ let router;
3851
+ try {
3852
+ router = makeRouter();
3853
+ } catch (err) {
3854
+ throw new Error(`[vue-pom-generator] makeRouter() invocation failed: ${String(err)}`);
3810
3855
  }
3811
- const { paramKeys, queryKeys } = getRoutePropsKeys(r);
3812
- const paramsMeta = getRouteParamMeta(router, r, paramKeys);
3813
- const pathTemplate = buildRouteTemplate(router, r, paramsMeta.map((p) => p.name));
3814
- if (typeof pathTemplate === "string" && pathTemplate.length) {
3815
- routeMetaEntries.push({
3816
- componentName,
3817
- pathTemplate,
3818
- params: paramsMeta,
3819
- query: queryKeys
3820
- });
3856
+ const routeNameMap = /* @__PURE__ */ new Map();
3857
+ const routePathMap = /* @__PURE__ */ new Map();
3858
+ const routeMetaEntries = [];
3859
+ for (const r of router.getRoutes()) {
3860
+ const componentInfo = await getComponentInfoFromRouteRecord(r, { rootDir: cwd });
3861
+ const componentName = resolveIntrospectedComponentName(componentInfo, options.componentNaming);
3862
+ if (!componentName)
3863
+ continue;
3864
+ if (typeof r.path === "string" && r.path.length) {
3865
+ routePathMap.set(r.path, componentName);
3866
+ }
3867
+ if (typeof r.name === "string" && r.name.length) {
3868
+ const key = toPascalCase(r.name);
3869
+ routeNameMap.set(key, componentName);
3870
+ }
3871
+ const { paramKeys, queryKeys } = getRoutePropsKeys(r);
3872
+ const paramsMeta = getRouteParamMeta(router, r, paramKeys);
3873
+ const pathTemplate = buildRouteTemplate(router, r, paramsMeta.map((p) => p.name));
3874
+ if (typeof pathTemplate === "string" && pathTemplate.length) {
3875
+ routeMetaEntries.push({
3876
+ componentName,
3877
+ pathTemplate,
3878
+ params: paramsMeta,
3879
+ query: queryKeys
3880
+ });
3881
+ }
3821
3882
  }
3883
+ return { routeNameMap, routePathMap, routeMetaEntries };
3884
+ } finally {
3885
+ debugLog("closing internal vite server");
3886
+ await server.close();
3822
3887
  }
3823
- return { routeNameMap, routePathMap, routeMetaEntries };
3824
3888
  } finally {
3825
- debugLog("closing internal vite server");
3826
- await server.close();
3889
+ restoreDomShim();
3827
3890
  }
3828
3891
  });
3829
3892
  }