Jelajahi Sumber

fix(dashboard): Update imports in hooks to fix typings

Michael Bromley 6 bulan lalu
induk
melakukan
cd450edbc2

+ 7 - 14
.lintstagedrc.json

@@ -1,16 +1,9 @@
 {
-  "admin-ui/**/*.ts": [
-    "npm run lint",
-    "npm run format",
-    "git add"
-  ],
-  "admin-ui/**/*.html": [
-    "npm run format",
-    "git add"
-  ],
-  "packages/!(dev-server)/**/*.ts": [
-    "npm run lint",
-    "npm run format",
-    "git add"
-  ]
+    "admin-ui/**/*.ts": ["npm run lint", "npm run format", "git add"],
+    "admin-ui/**/*.html": ["npm run format", "git add"],
+    "packages/!(dev-server)/**/*.ts": ["npm run lint", "npm run format", "git add"],
+    "packages/dashboard/src/lib/**/use-*.{ts,tsx}": [
+        "cd packages/dashboard && node scripts/check-lib-imports.js",
+        "git add"
+    ]
 }

+ 129 - 129
packages/dashboard/package.json

@@ -1,133 +1,133 @@
 {
-    "name": "@vendure/dashboard",
-    "private": false,
-    "version": "3.3.5",
-    "type": "module",
-    "repository": {
-        "type": "git",
-        "url": "https://github.com/vendure-ecommerce/vendure"
+  "name": "@vendure/dashboard",
+  "private": false,
+  "version": "3.3.5",
+  "type": "module",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/vendure-ecommerce/vendure"
+  },
+  "homepage": "https://www.vendure.io",
+  "funding": "https://github.com/sponsors/michaelbromley",
+  "publishConfig": {
+    "access": "public"
+  },
+  "scripts": {
+    "dev": "vite",
+    "build:standalone": "vite build",
+    "build": "tsc --project tsconfig.plugin.json",
+    "watch": "tsc --project tsconfig.plugin.json --watch",
+    "test": "vitest run",
+    "lint": "eslint .",
+    "preview": "vite preview",
+    "generate-index": "node scripts/generate-index.js"
+  },
+  "module": "./src/lib/index.ts",
+  "main": "./src/lib/index.ts",
+  "types": "./src/lib/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./src/lib/index.d.ts",
+      "import": "./src/lib/index.ts",
+      "require": "./src/lib/index.ts"
     },
-    "homepage": "https://www.vendure.io",
-    "funding": "https://github.com/sponsors/michaelbromley",
-    "publishConfig": {
-        "access": "public"
-    },
-    "scripts": {
-        "dev": "vite",
-        "build:standalone": "vite build",
-        "build": "tsc --project tsconfig.plugin.json",
-        "watch": "tsc --project tsconfig.plugin.json --watch",
-        "test": "vitest run",
-        "lint": "eslint .",
-        "preview": "vite preview",
-        "generate-index": "node ./generate-index.js"
-    },
-    "module": "./src/lib/index.ts",
-    "main": "./src/lib/index.ts",
-    "types": "./src/lib/index.d.ts",
-    "exports": {
-        ".": {
-            "types": "./src/lib/index.d.ts",
-            "import": "./src/lib/index.ts",
-            "require": "./src/lib/index.ts"
-        },
-        "./plugin": {
-            "types": "./dist/plugin/index.d.ts",
-            "import": "./dist/plugin/index.js",
-            "require": "./dist/plugin/index.js"
-        }
-    },
-    "files": [
-        "dist",
-        "src",
-        "vite",
-        "lingui.config.js",
-        "index.html"
-    ],
-    "dependencies": {
-        "@dnd-kit/core": "^6.3.1",
-        "@dnd-kit/modifiers": "^9.0.0",
-        "@dnd-kit/sortable": "^10.0.0",
-        "@hookform/resolvers": "^4.1.3",
-        "@lingui/babel-plugin-lingui-macro": "^5.2.0",
-        "@lingui/cli": "^5.2.0",
-        "@lingui/core": "^5.2.0",
-        "@lingui/react": "^5.2.0",
-        "@lingui/vite-plugin": "^5.2.0",
-        "@radix-ui/react-accordion": "^1.2.3",
-        "@radix-ui/react-alert-dialog": "^1.1.6",
-        "@radix-ui/react-avatar": "^1.1.3",
-        "@radix-ui/react-checkbox": "^1.1.4",
-        "@radix-ui/react-collapsible": "^1.1.3",
-        "@radix-ui/react-dialog": "^1.1.6",
-        "@radix-ui/react-dropdown-menu": "^2.1.6",
-        "@radix-ui/react-hover-card": "^1.1.6",
-        "@radix-ui/react-label": "^2.1.2",
-        "@radix-ui/react-popover": "^1.1.6",
-        "@radix-ui/react-scroll-area": "^1.2.3",
-        "@radix-ui/react-select": "^2.1.6",
-        "@radix-ui/react-separator": "^1.1.2",
-        "@radix-ui/react-slot": "^1.1.2",
-        "@radix-ui/react-switch": "^1.1.3",
-        "@radix-ui/react-tabs": "^1.1.3",
-        "@radix-ui/react-tooltip": "^1.1.8",
-        "@tailwindcss/vite": "^4.1.5",
-        "@tanstack/eslint-plugin-query": "^5.66.1",
-        "@tanstack/react-query": "^5.66.7",
-        "@tanstack/react-query-devtools": "^5.68.0",
-        "@tanstack/react-router": "^1.105.0",
-        "@tanstack/react-table": "^8.21.2",
-        "@tanstack/router-devtools": "^1.105.0",
-        "@tanstack/router-plugin": "^1.105.0",
-        "@tiptap/pm": "^2.11.5",
-        "@tiptap/react": "^2.11.5",
-        "@tiptap/starter-kit": "^2.11.5",
-        "@types/react": "^19.0.10",
-        "@types/react-dom": "^19.0.4",
-        "@types/react-grid-layout": "^1.3.5",
-        "@uidotdev/usehooks": "^2.4.1",
-        "@vendure/common": "3.3.5",
-        "@vendure/core": "3.3.5",
-        "@vitejs/plugin-react": "^4.3.4",
-        "awesome-graphql-client": "^2.1.0",
-        "class-variance-authority": "^0.7.1",
-        "clsx": "^2.1.1",
-        "cmdk": "^1.0.0",
-        "date-fns": "^3.6.0",
-        "gql.tada": "^1.8.10",
-        "graphql": "^16.10.0",
-        "json-edit-react": "^1.23.1",
-        "lucide-react": "^0.475.0",
-        "motion": "^12.6.2",
-        "next-themes": "^0.4.6",
-        "react": "^19.0.0",
-        "react-day-picker": "^9.6.7",
-        "react-dom": "^19.0.0",
-        "react-dropzone": "^14.3.8",
-        "react-grid-layout": "^1.5.1",
-        "react-hook-form": "^7.54.2",
-        "recharts": "^2.15.1",
-        "sonner": "^2.0.1",
-        "tailwind-merge": "^3.2.0",
-        "tailwindcss": "^4.1.5",
-        "tailwindcss-animate": "^1.0.7",
-        "tsconfig-paths": "^4.2.0",
-        "tw-animate-css": "^1.2.9",
-        "vite": "^6.3.5",
-        "zod": "^3.24.2"
-    },
-    "devDependencies": {
-        "@eslint/js": "^9.19.0",
-        "@types/node": "^22.13.4",
-        "eslint": "^9.19.0",
-        "eslint-plugin-react": "^7.37.4",
-        "eslint-plugin-react-hooks": "^5.0.0",
-        "eslint-plugin-react-refresh": "^0.4.18",
-        "globals": "^15.14.0",
-        "vite-plugin-dts": "^4.5.3"
-    },
-    "optionalDependencies": {
-        "lightningcss-linux-arm64-musl": "^1.29.3",
-        "lightningcss-linux-x64-musl": "^1.29.1"
+    "./plugin": {
+      "types": "./dist/plugin/index.d.ts",
+      "import": "./dist/plugin/index.js",
+      "require": "./dist/plugin/index.js"
     }
+  },
+  "files": [
+    "dist",
+    "src",
+    "vite",
+    "lingui.config.js",
+    "index.html"
+  ],
+  "dependencies": {
+    "@dnd-kit/core": "^6.3.1",
+    "@dnd-kit/modifiers": "^9.0.0",
+    "@dnd-kit/sortable": "^10.0.0",
+    "@hookform/resolvers": "^4.1.3",
+    "@lingui/babel-plugin-lingui-macro": "^5.2.0",
+    "@lingui/cli": "^5.2.0",
+    "@lingui/core": "^5.2.0",
+    "@lingui/react": "^5.2.0",
+    "@lingui/vite-plugin": "^5.2.0",
+    "@radix-ui/react-accordion": "^1.2.3",
+    "@radix-ui/react-alert-dialog": "^1.1.6",
+    "@radix-ui/react-avatar": "^1.1.3",
+    "@radix-ui/react-checkbox": "^1.1.4",
+    "@radix-ui/react-collapsible": "^1.1.3",
+    "@radix-ui/react-dialog": "^1.1.6",
+    "@radix-ui/react-dropdown-menu": "^2.1.6",
+    "@radix-ui/react-hover-card": "^1.1.6",
+    "@radix-ui/react-label": "^2.1.2",
+    "@radix-ui/react-popover": "^1.1.6",
+    "@radix-ui/react-scroll-area": "^1.2.3",
+    "@radix-ui/react-select": "^2.1.6",
+    "@radix-ui/react-separator": "^1.1.2",
+    "@radix-ui/react-slot": "^1.1.2",
+    "@radix-ui/react-switch": "^1.1.3",
+    "@radix-ui/react-tabs": "^1.1.3",
+    "@radix-ui/react-tooltip": "^1.1.8",
+    "@tailwindcss/vite": "^4.1.5",
+    "@tanstack/eslint-plugin-query": "^5.66.1",
+    "@tanstack/react-query": "^5.66.7",
+    "@tanstack/react-query-devtools": "^5.68.0",
+    "@tanstack/react-router": "^1.105.0",
+    "@tanstack/react-table": "^8.21.2",
+    "@tanstack/router-devtools": "^1.105.0",
+    "@tanstack/router-plugin": "^1.105.0",
+    "@tiptap/pm": "^2.11.5",
+    "@tiptap/react": "^2.11.5",
+    "@tiptap/starter-kit": "^2.11.5",
+    "@types/react": "^19.0.10",
+    "@types/react-dom": "^19.0.4",
+    "@types/react-grid-layout": "^1.3.5",
+    "@uidotdev/usehooks": "^2.4.1",
+    "@vendure/common": "3.3.5",
+    "@vendure/core": "3.3.5",
+    "@vitejs/plugin-react": "^4.3.4",
+    "awesome-graphql-client": "^2.1.0",
+    "class-variance-authority": "^0.7.1",
+    "clsx": "^2.1.1",
+    "cmdk": "^1.0.0",
+    "date-fns": "^3.6.0",
+    "gql.tada": "^1.8.10",
+    "graphql": "^16.10.0",
+    "json-edit-react": "^1.23.1",
+    "lucide-react": "^0.475.0",
+    "motion": "^12.6.2",
+    "next-themes": "^0.4.6",
+    "react": "^19.0.0",
+    "react-day-picker": "^9.6.7",
+    "react-dom": "^19.0.0",
+    "react-dropzone": "^14.3.8",
+    "react-grid-layout": "^1.5.1",
+    "react-hook-form": "^7.54.2",
+    "recharts": "^2.15.1",
+    "sonner": "^2.0.1",
+    "tailwind-merge": "^3.2.0",
+    "tailwindcss": "^4.1.5",
+    "tailwindcss-animate": "^1.0.7",
+    "tsconfig-paths": "^4.2.0",
+    "tw-animate-css": "^1.2.9",
+    "vite": "^6.3.5",
+    "zod": "^3.24.2"
+  },
+  "devDependencies": {
+    "@eslint/js": "^9.19.0",
+    "@types/node": "^22.13.4",
+    "eslint": "^9.19.0",
+    "eslint-plugin-react": "^7.37.4",
+    "eslint-plugin-react-hooks": "^5.0.0",
+    "eslint-plugin-react-refresh": "^0.4.18",
+    "globals": "^15.14.0",
+    "vite-plugin-dts": "^4.5.3"
+  },
+  "optionalDependencies": {
+    "lightningcss-linux-arm64-musl": "^1.29.3",
+    "lightningcss-linux-x64-musl": "^1.29.1"
+  }
 }

+ 114 - 0
packages/dashboard/scripts/check-lib-imports.js

@@ -0,0 +1,114 @@
+#!/usr/bin/env node
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const LIB_DIR = path.join(__dirname, '../src/lib');
+
+// Allowlist of @/ imports that are safe to keep
+const ALLOWED_IMPORTS = ['@/lib/trans.js', '@/lib/utils.js', '@/components/ui'];
+
+function findHookFiles(dir) {
+    const files = [];
+
+    function traverse(currentDir) {
+        const items = fs.readdirSync(currentDir);
+
+        for (const item of items) {
+            const fullPath = path.join(currentDir, item);
+            const stat = fs.statSync(fullPath);
+
+            if (stat.isDirectory()) {
+                traverse(fullPath);
+            } else if (item.startsWith('use-') && (item.endsWith('.ts') || item.endsWith('.tsx'))) {
+                files.push(fullPath);
+            }
+        }
+    }
+
+    traverse(dir);
+    return files;
+}
+
+function checkFileForBadImports(filePath) {
+    const content = fs.readFileSync(filePath, 'utf8');
+    const lines = content.split('\n');
+    const badImports = [];
+
+    for (let i = 0; i < lines.length; i++) {
+        const line = lines[i];
+        const trimmedLine = line.trim();
+
+        // Check for import statements that start with @/
+        if (trimmedLine.startsWith('import') && trimmedLine.includes('@/')) {
+            // Check if this import is in the allowlist
+            const isAllowed = ALLOWED_IMPORTS.some(allowed => {
+                if (allowed.endsWith('/')) {
+                    // For directory patterns like '@/components/ui'
+                    return trimmedLine.includes(allowed);
+                } else {
+                    // For exact file matches
+                    return trimmedLine.includes(allowed);
+                }
+            });
+
+            if (!isAllowed) {
+                badImports.push({
+                    line: i + 1,
+                    content: trimmedLine,
+                });
+            }
+        }
+    }
+
+    return badImports;
+}
+
+function main() {
+    console.log('🔍 Checking for @/ imports in hook files (use-*.ts/tsx) in src/lib directory...\n');
+    console.log('✅ Allowed imports:');
+    ALLOWED_IMPORTS.forEach(allowed => {
+        console.log(`   - ${allowed}`);
+    });
+    console.log('');
+
+    if (!fs.existsSync(LIB_DIR)) {
+        console.error('❌ src/lib directory not found!');
+        process.exit(1);
+    }
+
+    const files = findHookFiles(LIB_DIR);
+    let hasBadImports = false;
+    let totalBadImports = 0;
+
+    for (const file of files) {
+        const relativePath = path.relative(process.cwd(), file);
+        const badImports = checkFileForBadImports(file);
+
+        if (badImports.length > 0) {
+            hasBadImports = true;
+            totalBadImports += badImports.length;
+
+            console.log(`❌ ${relativePath}:`);
+            for (const badImport of badImports) {
+                console.log(`   Line ${badImport.line}: ${badImport.content}`);
+            }
+            console.log('');
+        }
+    }
+
+    if (hasBadImports) {
+        console.log(`❌ Found ${totalBadImports} bad import(s) in ${files.length} hook file(s)`);
+        console.log('💡 All imports in hook files should use relative paths, except for allowed imports');
+        process.exit(1);
+    } else {
+        console.log(`✅ No bad imports found in ${files.length} hook file(s)`);
+        console.log('🎉 All imports in hook files are using relative paths or allowed @/ paths');
+    }
+}
+
+main();

+ 0 - 0
packages/dashboard/generate-index.js → packages/dashboard/scripts/generate-index.js


+ 2 - 1
packages/dashboard/src/lib/framework/extension-api/use-dashboard-extensions.ts

@@ -1,7 +1,8 @@
-import { onExtensionSourceChange } from '@/framework/extension-api/define-dashboard-extension.js';
 import { useEffect, useState } from 'react';
 import { runDashboardExtensions } from 'virtual:dashboard-extensions';
 
+import { onExtensionSourceChange } from './define-dashboard-extension.js';
+
 /**
  * @description
  * This hook is used to load dashboard extensions via the `virtual:dashboard-extensions` module,

+ 5 - 8
packages/dashboard/src/lib/framework/form-engine/use-generated-form.tsx

@@ -1,16 +1,13 @@
-import { getOperationVariablesFields } from '@/framework/document-introspection/get-document-structure.js';
-import {
-    createFormSchemaFromFields,
-    getDefaultValuesFromFields,
-} from '@/framework/form-engine/form-schema-tools.js';
-import { transformRelationFields } from '@/framework/form-engine/utils.js';
-import { useChannel } from '@/hooks/use-channel.js';
-import { useServerConfig } from '@/hooks/use-server-config.js';
 import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
 import { zodResolver } from '@hookform/resolvers/zod';
 import { VariablesOf } from 'gql.tada';
 import { FormEvent } from 'react';
 import { useForm } from 'react-hook-form';
+import { useChannel } from '../../hooks/use-channel.js';
+import { useServerConfig } from '../../hooks/use-server-config.js';
+import { getOperationVariablesFields } from '../document-introspection/get-document-structure.js';
+import { createFormSchemaFromFields, getDefaultValuesFromFields } from './form-schema-tools.js';
+import { transformRelationFields } from './utils.js';
 
 export interface GeneratedFormOptions<
     T extends TypedDocumentNode<any, any>,

+ 4 - 4
packages/dashboard/src/lib/framework/page/use-detail-page.ts

@@ -1,7 +1,3 @@
-import { NEW_ENTITY_PATH } from '@/constants.js';
-import { api, Variables } from '@/graphql/api.js';
-import { useCustomFieldConfig } from '@/hooks/use-custom-field-config.js';
-import { useExtendedDetailQuery } from '@/hooks/use-extended-detail-query.js';
 import { removeReadonlyCustomFields } from '@/lib/utils.js';
 import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
 import {
@@ -16,6 +12,10 @@ import { DocumentNode } from 'graphql';
 import { FormEvent } from 'react';
 import { UseFormReturn } from 'react-hook-form';
 
+import { NEW_ENTITY_PATH } from '../../constants.js';
+import { api, Variables } from '../../graphql/api.js';
+import { useCustomFieldConfig } from '../../hooks/use-custom-field-config.js';
+import { useExtendedDetailQuery } from '../../hooks/use-extended-detail-query.js';
 import { addCustomFields } from '../document-introspection/add-custom-fields.js';
 import {
     getEntityName,

+ 4 - 5
packages/dashboard/src/lib/framework/page/use-extended-router.tsx

@@ -1,10 +1,9 @@
-import { ErrorPage } from '@/components/shared/error-page.js';
-import { useDashboardExtensions } from '@/framework/extension-api/use-dashboard-extensions.js';
-import { ListPage } from '@/framework/page/list-page.js';
-import { extensionRoutes } from '@/framework/page/page-api.js';
-import { AUTHENTICATED_ROUTE_PREFIX } from '@/constants.js';
 import { AnyRoute, createRoute, Router } from '@tanstack/react-router';
 import { useMemo } from 'react';
+import { ErrorPage } from '../../components/shared/error-page.js';
+import { AUTHENTICATED_ROUTE_PREFIX } from '../../constants.js';
+import { useDashboardExtensions } from '../extension-api/use-dashboard-extensions.js';
+import { extensionRoutes } from './page-api.js';
 
 /**
  * Extends the TanStack Router with additional routes for each dashboard

+ 2 - 1
packages/dashboard/src/lib/hooks/use-channel.ts

@@ -1,6 +1,7 @@
-import { ChannelContext } from '@/providers/channel-provider.js';
 import * as React from 'react';
 
+import { ChannelContext } from '../providers/channel-provider.js';
+
 // Hook to use the channel context
 
 /**

+ 2 - 1
packages/dashboard/src/lib/hooks/use-extended-detail-query.ts

@@ -1,9 +1,10 @@
-import { extendDetailFormQuery } from '@/framework/document-extension/extend-detail-form-query.js';
 import { useLingui } from '@/lib/trans.js';
 import { DocumentNode } from 'graphql';
 import { useEffect, useMemo, useRef } from 'react';
 import { toast } from 'sonner';
 
+import { extendDetailFormQuery } from '../framework/document-extension/extend-detail-form-query.js';
+
 /**
  * @description
  * Extends a detail page query document with any registered extensions provided by

+ 3 - 2
packages/dashboard/src/lib/hooks/use-extended-list-query.ts

@@ -1,10 +1,11 @@
-import { getListQueryDocuments } from '@/framework/data-table/data-table-extensions.js';
-import { extendDocument } from '@/framework/document-extension/extend-document.js';
 import { useLingui } from '@/lib/trans.js';
 import { DocumentNode } from 'graphql';
 import { useEffect, useMemo, useRef } from 'react';
 import { toast } from 'sonner';
 
+import { getListQueryDocuments } from '../framework/data-table/data-table-extensions.js';
+import { extendDocument } from '../framework/document-extension/extend-document.js';
+
 import { usePageBlock } from './use-page-block.js';
 import { usePage } from './use-page.js';
 

+ 4 - 2
packages/dashboard/src/lib/hooks/use-grouped-permissions.ts

@@ -1,7 +1,9 @@
-import { useServerConfig } from '@/hooks/use-server-config.js';
-import { ServerConfig } from '@/providers/server-config.js';
 import { useMemo } from 'react';
 
+import { ServerConfig } from '../providers/server-config.js';
+
+import { useServerConfig } from './use-server-config.js';
+
 export function useGroupedPermissions() {
     const serverConfig = useServerConfig();
     const permissionDefinitions = serverConfig?.permissions ?? [];

+ 1 - 1
packages/dashboard/src/lib/hooks/use-page-block.tsx

@@ -1,5 +1,5 @@
-import { PageBlockContext } from '@/framework/layout-engine/page-block-provider.js';
 import { useContext } from 'react';
+import { PageBlockContext } from '../framework/layout-engine/page-block-provider.js';
 
 export function usePageBlock() {
     const pageBlock = useContext(PageBlockContext);

+ 2 - 2
packages/dashboard/src/lib/hooks/use-page.tsx

@@ -1,5 +1,5 @@
-import { useContext } from "react";
-import { PageContext } from '@/framework/layout-engine/page-provider.js';
+import { useContext } from 'react';
+import { PageContext } from '../framework/layout-engine/page-provider.js';
 
 export function usePage() {
     const page = useContext(PageContext);

+ 3 - 2
packages/dashboard/src/lib/hooks/use-permissions.ts

@@ -1,7 +1,8 @@
-import { useAuth } from '@/hooks/use-auth.js';
-import { useChannel } from '@/hooks/use-channel.js';
 import { Permission } from '@vendure/common/lib/generated-types';
 
+import { useAuth } from './use-auth.js';
+import { useChannel } from './use-channel.js';
+
 /**
  * @description
  * **Status: Developer Preview**

+ 2 - 1
packages/dashboard/src/lib/hooks/use-server-config.ts

@@ -1,4 +1,5 @@
-import { ServerConfigContext } from '@/providers/server-config.js';
 import React from 'react';
 
+import { ServerConfigContext } from '../providers/server-config.js';
+
 export const useServerConfig = () => React.useContext(ServerConfigContext);

+ 2 - 1
packages/dashboard/src/lib/hooks/use-theme.ts

@@ -1,6 +1,7 @@
-import { ThemeProviderContext } from '@/providers/theme-provider.js';
 import { useContext } from 'react';
 
+import { ThemeProviderContext } from '../providers/theme-provider.js';
+
 export const useTheme = () => {
     const context = useContext(ThemeProviderContext);