@immense/vue-pom-generator 1.0.55 → 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,28 +1,30 @@
1
1
  ● ## Highlights
2
2
 
3
- - Fixed manual release workflow guardrails to prevent accidental triggers
4
- - Cleaned up stale regression test
5
- - Single workflow file change with improved safety checks
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
- **CI/CD Improvements**
10
- - Added guardrails to prevent unintended manual release triggers in GitHub Actions workflow
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
11
13
 
12
- **Testing**
13
- - Removed stale regression test case
14
+ ### Testing
15
+ - Added comprehensive test coverage for router DOM global restoration behavior
14
16
 
15
17
  ## Breaking Changes
16
18
 
17
- None.
19
+ None
18
20
 
19
21
  ## Pull Requests Included
20
22
 
21
- - #16 fix: guard manual releases (https://github.com/immense/vue-pom-generator/pull/16)
22
- (@dkattan)
23
+ - #18 fix: restore router DOM globals after introspection
24
+ (https://github.com/immense/vue-pom-generator/pull/18)
23
25
 
24
26
  ## Testing
25
27
 
26
- Includes test cleanup (removal of stale regression test). Workflow changes validated through
27
- GitHub Actions.
28
+ Added 83 lines of test coverage for router introspection DOM global handling. All existing tests
29
+ continue to pass.
28
30
 
package/dist/index.cjs CHANGED
@@ -1265,6 +1265,12 @@ function nodeHandlerAttributeInfo(node) {
1265
1265
  const n = node2;
1266
1266
  return typeof n.callee === "object" && n.callee !== null && Array.isArray(n.arguments);
1267
1267
  };
1268
+ const isAwaitExpressionNode = (node2) => {
1269
+ if (!isNodeType(node2, "AwaitExpression"))
1270
+ return false;
1271
+ const n = node2;
1272
+ return typeof n.argument === "object" && n.argument !== null;
1273
+ };
1268
1274
  const isAssignmentExpressionNode = (node2) => {
1269
1275
  if (!isNodeType(node2, "AssignmentExpression"))
1270
1276
  return false;
@@ -1471,14 +1477,15 @@ function nodeHandlerAttributeInfo(node) {
1471
1477
  if (isArrowFunctionExpressionNode(expr)) {
1472
1478
  const body = expr.body;
1473
1479
  const tryFromCallExpression = (call) => {
1474
- if (!isCallExpressionNode(call)) {
1480
+ const resolvedCall = isAwaitExpressionNode(call) ? call.argument : call;
1481
+ if (!isCallExpressionNode(resolvedCall)) {
1475
1482
  return null;
1476
1483
  }
1477
- const name = getLastIdentifierFromMemberChain(call.callee);
1484
+ const name = getLastIdentifierFromMemberChain(resolvedCall.callee);
1478
1485
  if (!name) {
1479
1486
  return null;
1480
1487
  }
1481
- const suffix = getStableSuffixFromCall(call);
1488
+ const suffix = getStableSuffixFromCall(resolvedCall);
1482
1489
  const semanticNameHint = suffix ? `${toPascalCase(name)}${suffix}` : toPascalCase(name);
1483
1490
  return semanticNameHint;
1484
1491
  };
@@ -3150,6 +3157,56 @@ function createRouterIntrospectionVueStubPlugin(options) {
3150
3157
  }
3151
3158
  };
3152
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
+ }
3153
3210
  const PARAM_TOKEN_PREFIX = "__VUE_TESTID_PARAM__";
3154
3211
  function getParamToken(name) {
3155
3212
  return `${PARAM_TOKEN_PREFIX}${name}__`;
@@ -3498,12 +3555,18 @@ function createMinimalLocation() {
3498
3555
  ancestorOrigins: { length: 0, contains: () => false, item: () => null, [Symbol.iterator]: [][Symbol.iterator] }
3499
3556
  };
3500
3557
  }
3501
- async function ensureDomShim() {
3558
+ function ensureDomShim() {
3502
3559
  const g = globalThis;
3503
3560
  if (typeof document !== "undefined" && typeof window !== "undefined")
3504
- return;
3561
+ return () => {
3562
+ };
3505
3563
  const minimalDoc = createMinimalDocument();
3506
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
+ };
3507
3570
  const win = {
3508
3571
  document: minimalDoc,
3509
3572
  location: minimalLocation,
@@ -3548,17 +3611,17 @@ async function ensureDomShim() {
3548
3611
  queueMicrotask,
3549
3612
  performance: globalThis.performance
3550
3613
  };
3551
- g.window = win;
3552
- g.document = minimalDoc;
3553
- g.location = minimalLocation;
3614
+ setGlobal("window", win);
3615
+ setGlobal("document", minimalDoc);
3616
+ setGlobal("location", minimalLocation);
3554
3617
  if (!g.self)
3555
- g.self = win;
3618
+ setGlobal("self", win);
3556
3619
  if (!g.navigator)
3557
- g.navigator = win.navigator;
3620
+ setGlobal("navigator", win.navigator);
3558
3621
  if (!g.history)
3559
- g.history = win.history;
3622
+ setGlobal("history", win.history);
3560
3623
  if (!g.MutationObserver) {
3561
- g.MutationObserver = class {
3624
+ setGlobal("MutationObserver", class {
3562
3625
  disconnect() {
3563
3626
  }
3564
3627
  observe() {
@@ -3566,20 +3629,20 @@ async function ensureDomShim() {
3566
3629
  takeRecords() {
3567
3630
  return [];
3568
3631
  }
3569
- };
3632
+ });
3570
3633
  }
3571
3634
  if (!g.ResizeObserver) {
3572
- g.ResizeObserver = class {
3635
+ setGlobal("ResizeObserver", class {
3573
3636
  disconnect() {
3574
3637
  }
3575
3638
  observe() {
3576
3639
  }
3577
3640
  unobserve() {
3578
3641
  }
3579
- };
3642
+ });
3580
3643
  }
3581
3644
  if (!g.IntersectionObserver) {
3582
- g.IntersectionObserver = class {
3645
+ setGlobal("IntersectionObserver", class {
3583
3646
  disconnect() {
3584
3647
  }
3585
3648
  observe() {
@@ -3589,10 +3652,10 @@ async function ensureDomShim() {
3589
3652
  takeRecords() {
3590
3653
  return [];
3591
3654
  }
3592
- };
3655
+ });
3593
3656
  }
3594
3657
  if (!g.requestIdleCallback) {
3595
- g.requestIdleCallback = (cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1);
3658
+ setGlobal("requestIdleCallback", (cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1));
3596
3659
  }
3597
3660
  if (!g.localStorage || !g.sessionStorage) {
3598
3661
  const storageFactory = () => {
@@ -3615,12 +3678,15 @@ async function ensureDomShim() {
3615
3678
  };
3616
3679
  };
3617
3680
  if (!g.localStorage)
3618
- g.localStorage = storageFactory();
3681
+ setGlobal("localStorage", storageFactory());
3619
3682
  if (!g.sessionStorage)
3620
- g.sessionStorage = storageFactory();
3683
+ setGlobal("sessionStorage", storageFactory());
3621
3684
  }
3622
3685
  if (!g.requestAnimationFrame)
3623
- g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 16);
3686
+ setGlobal("requestAnimationFrame", (cb) => setTimeout(() => cb(Date.now()), 16));
3687
+ return () => {
3688
+ restoreTemporaryGlobals(snapshots);
3689
+ };
3624
3690
  }
3625
3691
  function unwrapNuxtPageSegment(segment, prefix, suffix) {
3626
3692
  if (!segment.startsWith(prefix) || !segment.endsWith(suffix))
@@ -3738,85 +3804,89 @@ async function parseRouterFileFromCwd(routerEntryPath, options = {}) {
3738
3804
  }
3739
3805
  const cwd = path.dirname(routerEntry);
3740
3806
  const moduleShims = normalizeRouterIntrospectionModuleShims(options.moduleShims);
3741
- await ensureDomShim();
3742
- debugLog(`parseRouterFileFromCwd cwd=${cwd}`);
3743
- const vite = await import("vite");
3744
- const server = await vite.createServer({
3745
- root: cwd,
3746
- configFile: false,
3747
- logLevel: "error",
3748
- // This server is created only to SSR-load the router module. Disable HMR/WebSocket
3749
- // to avoid port conflicts in dev/test environments.
3750
- server: { middlewareMode: true, hmr: false, ws: false },
3751
- appType: "custom",
3752
- // IMPORTANT:
3753
- // This internal, short-lived Vite server exists only to `ssrLoadModule()` the router entry.
3754
- // We close it immediately after reading routes.
3755
- //
3756
- // Vite's dependency optimizer (vite:dep-scan / optimizeDeps) runs asynchronously and can
3757
- // still have pending resolve requests when we call `server.close()`, which surfaces as:
3758
- // "The server is being restarted or closed. Request is outdated [plugin vite:dep-scan]"
3759
- //
3760
- // Disable optimizeDeps entirely for this internal server to avoid that race.
3761
- optimizeDeps: {
3762
- disabled: true
3763
- },
3764
- resolve: {
3765
- alias: {
3766
- "@": cwd
3767
- }
3768
- },
3769
- // Important: Do NOT include @vitejs/plugin-vue here.
3770
- // We stub all `.vue` imports ourselves, and including the Vue plugin would attempt to parse
3771
- // those stubbed modules as real SFCs (and fail).
3772
- plugins: [createRouterIntrospectionVueStubPlugin({ routerEntryAbs: routerEntry, moduleShims })]
3773
- });
3807
+ const restoreDomShim = ensureDomShim();
3774
3808
  try {
3775
- const moduleId = node_url.pathToFileURL(routerEntry).href;
3776
- debugLog(`ssrLoadModule(${moduleId}) start`);
3777
- const mod = await server.ssrLoadModule(moduleId);
3778
- debugLog(`ssrLoadModule(${moduleId}) done; hasDefault=${typeof mod?.default === "function"}`);
3779
- const makeRouter = mod?.default;
3780
- if (typeof makeRouter !== "function") {
3781
- throw new TypeError(`[vue-pom-generator] ${routerEntry} must export a default router factory function (export default makeRouter).`);
3782
- }
3783
- 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
+ });
3784
3841
  try {
3785
- router = makeRouter();
3786
- } catch (err) {
3787
- throw new Error(`[vue-pom-generator] makeRouter() invocation failed: ${String(err)}`);
3788
- }
3789
- const routeNameMap = /* @__PURE__ */ new Map();
3790
- const routePathMap = /* @__PURE__ */ new Map();
3791
- const routeMetaEntries = [];
3792
- for (const r of router.getRoutes()) {
3793
- const componentInfo = await getComponentInfoFromRouteRecord(r, { rootDir: cwd });
3794
- const componentName = resolveIntrospectedComponentName(componentInfo, options.componentNaming);
3795
- if (!componentName)
3796
- continue;
3797
- if (typeof r.path === "string" && r.path.length) {
3798
- 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).`);
3799
3849
  }
3800
- if (typeof r.name === "string" && r.name.length) {
3801
- const key = toPascalCase(r.name);
3802
- 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)}`);
3803
3855
  }
3804
- const { paramKeys, queryKeys } = getRoutePropsKeys(r);
3805
- const paramsMeta = getRouteParamMeta(router, r, paramKeys);
3806
- const pathTemplate = buildRouteTemplate(router, r, paramsMeta.map((p) => p.name));
3807
- if (typeof pathTemplate === "string" && pathTemplate.length) {
3808
- routeMetaEntries.push({
3809
- componentName,
3810
- pathTemplate,
3811
- params: paramsMeta,
3812
- query: queryKeys
3813
- });
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
+ }
3814
3882
  }
3883
+ return { routeNameMap, routePathMap, routeMetaEntries };
3884
+ } finally {
3885
+ debugLog("closing internal vite server");
3886
+ await server.close();
3815
3887
  }
3816
- return { routeNameMap, routePathMap, routeMetaEntries };
3817
3888
  } finally {
3818
- debugLog("closing internal vite server");
3819
- await server.close();
3889
+ restoreDomShim();
3820
3890
  }
3821
3891
  });
3822
3892
  }