fix: replace invalid useRouter import from @directus/extensions-sdk with vue-router
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 26s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 26s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
The Directus 11.x SDK does not export useRouter. Importing it caused a SyntaxError that crashed the entire extensions bundle, preventing ALL modules from appearing in the Data Studio sidebar. Changes: - Replace useRouter import from @directus/extensions-sdk → vue-router - Add scripts/validate-sdk-imports.sh to catch invalid SDK imports - Integrate SDK import validation into pre-push hook - Add EXTENSIONS_AUTO_RELOAD to docker-compose.yml - Remove debug NODE_ENV=development
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
# Validate Directus SDK imports before push
|
||||||
|
# This prevents runtime crashes caused by importing non-existent exports
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
if [ -f "$SCRIPT_DIR/scripts/validate-sdk-imports.sh" ]; then
|
||||||
|
"$SCRIPT_DIR/scripts/validate-sdk-imports.sh" || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if we are pushing a tag
|
# Check if we are pushing a tag
|
||||||
while read local_ref local_sha remote_ref remote_sha
|
while read local_ref local_sha remote_ref remote_sha
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
infra-cms:
|
infra-cms:
|
||||||
image: directus/directus:11
|
image: directus/directus:11.15.2
|
||||||
ports:
|
ports:
|
||||||
- "8059:8055"
|
- "8059:8055"
|
||||||
networks:
|
networks:
|
||||||
@@ -14,6 +14,7 @@ services:
|
|||||||
DB_CLIENT: "sqlite3"
|
DB_CLIENT: "sqlite3"
|
||||||
DB_FILENAME: "/directus/database/data.db"
|
DB_FILENAME: "/directus/database/data.db"
|
||||||
WEBSOCKETS_ENABLED: "true"
|
WEBSOCKETS_ENABLED: "true"
|
||||||
|
PUBLIC_URL: "http://cms.localhost"
|
||||||
EMAIL_TRANSPORT: "smtp"
|
EMAIL_TRANSPORT: "smtp"
|
||||||
EMAIL_SMTP_HOST: "smtp.eu.mailgun.org"
|
EMAIL_SMTP_HOST: "smtp.eu.mailgun.org"
|
||||||
EMAIL_SMTP_PORT: "587"
|
EMAIL_SMTP_PORT: "587"
|
||||||
@@ -21,19 +22,29 @@ services:
|
|||||||
EMAIL_SMTP_PASSWORD: "4592fcb94599ee1a45b4ac2386fd0a64-102c75d8-ca2870e6"
|
EMAIL_SMTP_PASSWORD: "4592fcb94599ee1a45b4ac2386fd0a64-102c75d8-ca2870e6"
|
||||||
EMAIL_SMTP_SECURE: "false"
|
EMAIL_SMTP_SECURE: "false"
|
||||||
EMAIL_FROM: "postmaster@mg.mintel.me"
|
EMAIL_FROM: "postmaster@mg.mintel.me"
|
||||||
|
LOG_LEVEL: "debug"
|
||||||
|
SERVE_APP: "true"
|
||||||
|
EXTENSIONS_AUTO_RELOAD: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- ./database:/directus/database
|
- ./database:/directus/database
|
||||||
- ./uploads:/directus/uploads
|
- ./uploads:/directus/uploads
|
||||||
- ./schema:/directus/schema
|
- ./schema:/directus/schema
|
||||||
- ./extensions:/directus/extensions
|
- ./extensions:/directus/extensions
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8055/server/health" ]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.infra-cms.rule=Host(`cms.localhost`)"
|
- "traefik.http.routers.at-mintel-infra-cms.rule=Host(`cms.localhost`)"
|
||||||
- "traefik.http.services.infra-cms.loadbalancer.server.port=8055"
|
- "traefik.http.services.at-mintel-infra-cms.loadbalancer.server.port=8055"
|
||||||
|
- "traefik.http.services.at-mintel-infra-cms.loadbalancer.healthcheck.path=/server/health"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
name: mintel-infra-cms-internal
|
name: at-mintel-cms-network
|
||||||
infra:
|
infra:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
141
packages/cms-infra/extensions/unified-dashboard/src/module.vue
Normal file
141
packages/cms-infra/extensions/unified-dashboard/src/module.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<private-view title="Overview">
|
||||||
|
<div class="dashboard">
|
||||||
|
<header class="dashboard-header">
|
||||||
|
<h1 class="title">Infrastructure Stack</h1>
|
||||||
|
<p class="subtitle">Zentrale Schnittstelle für Firmen, Personen und Leads.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card" @click="navigateTo('/company-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="business" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Firmen</span>
|
||||||
|
<span class="stat-value">{{ stats.companies }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" @click="navigateTo('/people-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="person" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Personen</span>
|
||||||
|
<span class="stat-value">{{ stats.people }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" @click="navigateTo('/acquisition-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="auto_awesome" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Leads</span>
|
||||||
|
<span class="stat-value">{{ stats.leads }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="recent-activity">
|
||||||
|
<h2 class="section-title">Schnellzugriff</h2>
|
||||||
|
<div class="action-grid">
|
||||||
|
<v-button secondary block @click="navigateTo('/people-manager?create=true')">
|
||||||
|
<v-icon name="person_add" left />
|
||||||
|
Neue Person anlegen
|
||||||
|
</v-button>
|
||||||
|
<v-button secondary block @click="navigateTo('/acquisition-manager?create=true')">
|
||||||
|
<v-icon name="add_link" left />
|
||||||
|
Neuen Lead registrieren
|
||||||
|
</v-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</private-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useApi } from '@directus/extensions-sdk';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const api = useApi();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const stats = ref({
|
||||||
|
companies: 0,
|
||||||
|
people: 0,
|
||||||
|
leads: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchStats() {
|
||||||
|
try {
|
||||||
|
const [comp, peop, lead] = await Promise.all([
|
||||||
|
api.get('/items/companies?aggregate[count]=*'),
|
||||||
|
api.get('/items/people?aggregate[count]=*'),
|
||||||
|
api.get('/items/leads?aggregate[count]=*')
|
||||||
|
]);
|
||||||
|
stats.value = {
|
||||||
|
companies: comp.data.data[0].count,
|
||||||
|
people: peop.data.data[0].count,
|
||||||
|
leads: lead.data.data[0].count
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch stats:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateTo(path: string) {
|
||||||
|
router.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetchStats);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dashboard { padding: 40px; }
|
||||||
|
.dashboard-header { margin-bottom: 48px; }
|
||||||
|
.title { font-size: 32px; font-weight: 800; letter-spacing: -0.5px; margin-bottom: 8px; }
|
||||||
|
.subtitle { color: var(--theme--foreground-subdued); font-size: 16px; }
|
||||||
|
|
||||||
|
.stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 48px; }
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: var(--theme--background-normal);
|
||||||
|
border: 1px solid var(--theme--border);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
border-color: var(--theme--primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: var(--theme--background-subdued);
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--theme--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content { display: flex; flex-direction: column; }
|
||||||
|
.stat-label { font-size: 12px; font-weight: 700; text-transform: uppercase; color: var(--theme--foreground-subdued); letter-spacing: 0.5px; }
|
||||||
|
.stat-value { font-size: 28px; font-weight: 800; color: var(--theme--foreground); }
|
||||||
|
|
||||||
|
.arrow { position: absolute; right: 24px; opacity: 0.2; }
|
||||||
|
.stat-card:hover .arrow { opacity: 1; color: var(--theme--primary); }
|
||||||
|
|
||||||
|
.recent-activity { max-width: 600px; }
|
||||||
|
.section-title { font-size: 18px; font-weight: 700; margin-bottom: 24px; }
|
||||||
|
.action-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||||
|
</style>
|
||||||
141
packages/unified-dashboard/src/module.vue
Normal file
141
packages/unified-dashboard/src/module.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<private-view title="Overview">
|
||||||
|
<div class="dashboard">
|
||||||
|
<header class="dashboard-header">
|
||||||
|
<h1 class="title">Infrastructure Stack</h1>
|
||||||
|
<p class="subtitle">Zentrale Schnittstelle für Firmen, Personen und Leads.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card" @click="navigateTo('/company-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="business" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Firmen</span>
|
||||||
|
<span class="stat-value">{{ stats.companies }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" @click="navigateTo('/people-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="person" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Personen</span>
|
||||||
|
<span class="stat-value">{{ stats.people }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" @click="navigateTo('/acquisition-manager')">
|
||||||
|
<div class="stat-icon"><v-icon name="auto_awesome" large /></div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<span class="stat-label">Leads</span>
|
||||||
|
<span class="stat-value">{{ stats.leads }}</span>
|
||||||
|
</div>
|
||||||
|
<v-icon name="chevron_right" class="arrow" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="recent-activity">
|
||||||
|
<h2 class="section-title">Schnellzugriff</h2>
|
||||||
|
<div class="action-grid">
|
||||||
|
<v-button secondary block @click="navigateTo('/people-manager?create=true')">
|
||||||
|
<v-icon name="person_add" left />
|
||||||
|
Neue Person anlegen
|
||||||
|
</v-button>
|
||||||
|
<v-button secondary block @click="navigateTo('/acquisition-manager?create=true')">
|
||||||
|
<v-icon name="add_link" left />
|
||||||
|
Neuen Lead registrieren
|
||||||
|
</v-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</private-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useApi } from '@directus/extensions-sdk';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const api = useApi();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const stats = ref({
|
||||||
|
companies: 0,
|
||||||
|
people: 0,
|
||||||
|
leads: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchStats() {
|
||||||
|
try {
|
||||||
|
const [comp, peop, lead] = await Promise.all([
|
||||||
|
api.get('/items/companies?aggregate[count]=*'),
|
||||||
|
api.get('/items/people?aggregate[count]=*'),
|
||||||
|
api.get('/items/leads?aggregate[count]=*')
|
||||||
|
]);
|
||||||
|
stats.value = {
|
||||||
|
companies: comp.data.data[0].count,
|
||||||
|
people: peop.data.data[0].count,
|
||||||
|
leads: lead.data.data[0].count
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch stats:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateTo(path: string) {
|
||||||
|
router.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetchStats);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dashboard { padding: 40px; }
|
||||||
|
.dashboard-header { margin-bottom: 48px; }
|
||||||
|
.title { font-size: 32px; font-weight: 800; letter-spacing: -0.5px; margin-bottom: 8px; }
|
||||||
|
.subtitle { color: var(--theme--foreground-subdued); font-size: 16px; }
|
||||||
|
|
||||||
|
.stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 48px; }
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: var(--theme--background-normal);
|
||||||
|
border: 1px solid var(--theme--border);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
border-color: var(--theme--primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: var(--theme--background-subdued);
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--theme--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content { display: flex; flex-direction: column; }
|
||||||
|
.stat-label { font-size: 12px; font-weight: 700; text-transform: uppercase; color: var(--theme--foreground-subdued); letter-spacing: 0.5px; }
|
||||||
|
.stat-value { font-size: 28px; font-weight: 800; color: var(--theme--foreground); }
|
||||||
|
|
||||||
|
.arrow { position: absolute; right: 24px; opacity: 0.2; }
|
||||||
|
.stat-card:hover .arrow { opacity: 1; color: var(--theme--primary); }
|
||||||
|
|
||||||
|
.recent-activity { max-width: 600px; }
|
||||||
|
.section-title { font-size: 18px; font-weight: 700; margin-bottom: 24px; }
|
||||||
|
.action-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||||
|
</style>
|
||||||
100
scripts/validate-sdk-imports.sh
Executable file
100
scripts/validate-sdk-imports.sh
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# validate-sdk-imports.sh
|
||||||
|
# Validates that Directus extensions only use exports that exist in @directus/extensions-sdk.
|
||||||
|
# Prevents the "SyntaxError: doesn't provide an export named" runtime crash
|
||||||
|
# that silently breaks ALL extensions in the browser.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# Valid exports from @directus/extensions-sdk in Directus 11.x
|
||||||
|
# If Directus is upgraded, update this list by running:
|
||||||
|
# curl -s http://cms.localhost/admin/assets/@directus_extensions-sdk.*.entry.js | grep -oE 'export\{[^}]+\}'
|
||||||
|
VALID_EXPORTS=(
|
||||||
|
"defineDisplay"
|
||||||
|
"defineEndpoint"
|
||||||
|
"defineHook"
|
||||||
|
"defineInterface"
|
||||||
|
"defineLayout"
|
||||||
|
"defineModule"
|
||||||
|
"defineOperationApi"
|
||||||
|
"defineOperationApp"
|
||||||
|
"definePanel"
|
||||||
|
"defineTheme"
|
||||||
|
"getFieldsFromTemplate"
|
||||||
|
"getRelationType"
|
||||||
|
"useApi"
|
||||||
|
"useCollection"
|
||||||
|
"useExtensions"
|
||||||
|
"useFilterFields"
|
||||||
|
"useItems"
|
||||||
|
"useLayout"
|
||||||
|
"useSdk"
|
||||||
|
"useStores"
|
||||||
|
"useSync"
|
||||||
|
)
|
||||||
|
|
||||||
|
ERRORS=0
|
||||||
|
|
||||||
|
echo "🔍 Validating @directus/extensions-sdk imports..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Search all .ts and .vue files in extension directories
|
||||||
|
SEARCH_DIRS=(
|
||||||
|
"$REPO_ROOT/packages/cms-infra/extensions"
|
||||||
|
"$REPO_ROOT/packages/unified-dashboard"
|
||||||
|
"$REPO_ROOT/packages/customer-manager"
|
||||||
|
"$REPO_ROOT/packages/company-manager"
|
||||||
|
"$REPO_ROOT/packages/people-manager"
|
||||||
|
"$REPO_ROOT/packages/acquisition-manager"
|
||||||
|
"$REPO_ROOT/packages/feedback-commander"
|
||||||
|
)
|
||||||
|
|
||||||
|
for DIR in "${SEARCH_DIRS[@]}"; do
|
||||||
|
[ -d "$DIR" ] || continue
|
||||||
|
|
||||||
|
# Find all imports from @directus/extensions-sdk
|
||||||
|
while IFS= read -r line; do
|
||||||
|
FILE=$(echo "$line" | cut -d: -f1)
|
||||||
|
LINENUM=$(echo "$line" | cut -d: -f2)
|
||||||
|
CONTENT=$(echo "$line" | cut -d: -f3-)
|
||||||
|
|
||||||
|
# Extract imported names from the import statement
|
||||||
|
IMPORTS=$(echo "$CONTENT" | grep -oE '\{[^}]+\}' | tr -d '{}' | tr ',' '\n' | sed 's/^ *//;s/ *$//' | sed 's/ as .*//')
|
||||||
|
|
||||||
|
for IMPORT in $IMPORTS; do
|
||||||
|
[ -z "$IMPORT" ] && continue
|
||||||
|
FOUND=false
|
||||||
|
for VALID in "${VALID_EXPORTS[@]}"; do
|
||||||
|
if [ "$IMPORT" = "$VALID" ]; then
|
||||||
|
FOUND=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$FOUND" = false ]; then
|
||||||
|
echo "❌ INVALID IMPORT: '$IMPORT' in $FILE:$LINENUM"
|
||||||
|
echo " '$IMPORT' is NOT exported by @directus/extensions-sdk in Directus 11.x"
|
||||||
|
echo " This WILL crash ALL extensions at runtime!"
|
||||||
|
echo ""
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done < <(grep -rn "from ['\"]@directus/extensions-sdk['\"]" "$DIR" --include="*.ts" --include="*.vue" 2>/dev/null || true)
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$ERRORS" -gt 0 ]; then
|
||||||
|
echo "💥 Found $ERRORS invalid import(s)!"
|
||||||
|
echo ""
|
||||||
|
echo "Valid exports from @directus/extensions-sdk:"
|
||||||
|
printf " %s\n" "${VALID_EXPORTS[@]}"
|
||||||
|
echo ""
|
||||||
|
echo "Common fixes:"
|
||||||
|
echo " useRouter → import { useRouter } from 'vue-router'"
|
||||||
|
echo " useRoute → import { useRoute } from 'vue-router'"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅ All @directus/extensions-sdk imports are valid."
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user