diff --git a/ivette/.gitignore b/ivette/.gitignore
index e776663c6d3cf6a1573ce6fd0d6ad83e4cfa63e0..6f5c2ec2a1203c5f0e3546ea0130c5bc333d0be3 100644
--- a/ivette/.gitignore
+++ b/ivette/.gitignore
@@ -11,5 +11,6 @@ yarn-error.log
 /bin
 /dist
 /doc/html
+/src/renderer/loader.ts
 
 # --------------------------------------------------------------------------
diff --git a/ivette/Makefile b/ivette/Makefile
index 74e740a6d8a97fd4ee429d2e3c6835fc8e41871b..527aab28db662f073e52007c402a404cdf79b15e 100644
--- a/ivette/Makefile
+++ b/ivette/Makefile
@@ -9,9 +9,9 @@ DOME_CUSTOM_ENTRIES= yes
 COPYRIGHT=CEA LIST / LSL
 # --------------------------------------------------------------------------
 
-.PHONY: all app dev doc serve dist lint fixlint
+.PHONY: all app dev pkg doc serve dist lint fixlint
 
-all: lint app
+all: pkg lint app
 
 app: dome-app
 dev: dome-dev
@@ -27,6 +27,23 @@ tsc: dome-pkg dome-templ
 	yarn run typecheck
 	yarn run lint --fix --cache --cache-location .eslint-cache
 
+# --------------------------------------------------------------------------
+# --- Ivette Package Loader
+# --------------------------------------------------------------------------
+
+LOADER=src/renderer/loader.ts
+PACKAGES=$(shell find src -name "pkg.json")
+
+lint: pkg
+app: pkg
+dev: pkg
+pkg: $(LOADER)
+$(LOADER): $(PACKAGES) ./configure.js ./Makefile
+	@rm -f $(LOADER)
+	@echo "[Ivette] configure packages"
+	@node ./configure.js $(LOADER) $(PACKAGES)
+	@chmod -f a-w $(LOADER)
+
 # --------------------------------------------------------------------------
 # --- Frama-C API
 # --------------------------------------------------------------------------
diff --git a/ivette/configure.js b/ivette/configure.js
new file mode 100644
index 0000000000000000000000000000000000000000..ebbf384515fd5e8b27b8d00dec5fbaa7336087a9
--- /dev/null
+++ b/ivette/configure.js
@@ -0,0 +1,47 @@
+// --------------------------------------------------------------------------
+// --- Configure Packages
+// --- Called by [make pkg]
+// --------------------------------------------------------------------------
+
+const path = require('path');
+const fs = require('fs');
+
+const loader = process.argv[2];
+const inputFiles = process.argv.slice(3);
+const packages = new Map();
+let buffer = '// Ivette Package Loader (generated)\n';
+
+inputFiles.forEach((file) => {
+  try {
+    const pkgId = path.relative('./src',path.dirname(file));
+    const pkgSrc = fs.readFileSync(file, { encoding: 'UTF-8' });
+    const pkgJson = JSON.parse(pkgSrc);
+    packages.set(pkgId,pkgJson);
+  } catch(err) {
+    console.error(`[Dome] Error ${file}: ${err}`);
+    process.exit(1);
+  }
+});
+
+function depend(id) {
+  const pkg = packages.get(id);
+  if (pkg) configure(pkg,id);
+}
+
+function configure(pkg, id) {
+  if (!pkg.done) {
+    pkg.done = true;
+    for(let parent = id;;) {
+      parent = path.dirname(parent);
+      if (!parent || parent === '.') break;
+      depend(parent);
+    }
+    const { depends=[], main='.' } = pkg;
+    depends.forEach(depend);
+    console.log(`[Ivette] package ${id}`);
+    buffer += `import '${path.join(id,main)}';\n`;
+  }
+}
+
+packages.forEach(configure);
+fs.writeFileSync(loader, buffer);
diff --git a/ivette/src/frama-c/pkg.json b/ivette/src/frama-c/pkg.json
new file mode 100644
index 0000000000000000000000000000000000000000..8a0024216f05e9c67a485bff3d0156d27edb2b66
--- /dev/null
+++ b/ivette/src/frama-c/pkg.json
@@ -0,0 +1,3 @@
+{
+  "name": "Frama-C"
+}
diff --git a/ivette/src/frama-c/plugins/dive/pkg.json b/ivette/src/frama-c/plugins/dive/pkg.json
new file mode 100644
index 0000000000000000000000000000000000000000..a832e6f2b4d05f1d0366e01e683ea298ce6a6a48
--- /dev/null
+++ b/ivette/src/frama-c/plugins/dive/pkg.json
@@ -0,0 +1,3 @@
+{
+  "name": "Frama-C/Dive"
+}
diff --git a/ivette/src/frama-c/plugins/eva/pkg.json b/ivette/src/frama-c/plugins/eva/pkg.json
new file mode 100644
index 0000000000000000000000000000000000000000..753dacec0049c0968417a1b55308748c7b3f70cb
--- /dev/null
+++ b/ivette/src/frama-c/plugins/eva/pkg.json
@@ -0,0 +1,3 @@
+{
+  "name": "Frama-C/Eva"
+}
diff --git a/ivette/src/renderer/Application.tsx b/ivette/src/renderer/Application.tsx
index abd682bc507fe60805e005460478aab7f0dbe6d1..db7ff6f6d260407bc5887c27de03b6bfd32f0dab 100644
--- a/ivette/src/renderer/Application.tsx
+++ b/ivette/src/renderer/Application.tsx
@@ -13,7 +13,7 @@ import * as Sidebar from 'dome/frame/sidebars';
 import * as Controller from './Controller';
 import * as Extensions from './Extensions';
 import * as Laboratory from './Laboratory';
-import './PackageLoader';
+import './loader';
 
 // --------------------------------------------------------------------------
 // --- Main View
diff --git a/ivette/src/renderer/PackageLoader.tsx b/ivette/src/renderer/PackageLoader.tsx
deleted file mode 100644
index df0c5edbe93256e63a089f0a01506f3b216c500a..0000000000000000000000000000000000000000
--- a/ivette/src/renderer/PackageLoader.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-/* --------------------------------------------------------------------------*/
-/* --- This file is intended to be generated                              ---*/
-/* --------------------------------------------------------------------------*/
-
-import 'frama-c';
-import 'frama-c/plugins/eva';
-import 'frama-c/plugins/dive';
-
-/* --------------------------------------------------------------------------*/