@hkdigital/lib-core 0.5.76 → 0.5.78

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/claude.md CHANGED
@@ -130,6 +130,51 @@ This is a modern SvelteKit library built with Svelte 5 and Skeleton.dev v3 compo
130
130
 
131
131
  **Import validation:** Run `node scripts/validate-imports.mjs` to validate import patterns. The validator checks for barrel export files at each level and suggests the highest-level file that exports your target. These rules are enforced for `src/lib/` files only. Files in `src/routes/` can use relative imports freely. See README.md "Import Validation" section for usage in other projects.
132
132
 
133
+ ### Critical: Aliases in Libraries vs Apps
134
+
135
+ **IMPORTANT**: Due to Vite/SvelteKit limitations, aliases in library
136
+ code (`src/lib/**`) must resolve to paths **inside the project folder**.
137
+ Aliases to external packages or paths outside the project break when
138
+ building libraries.
139
+
140
+ **The problem:**
141
+ ```javascript
142
+ // ❌ These aliases don't work in src/lib/** files:
143
+ // svelte.config.js
144
+ alias: {
145
+ '$ext': 'node_modules/@hkdigital/lib-core/dist', // Outside project
146
+ '$pkg': '@hkdigital/lib-core' // Package name (doesn't resolve)
147
+ }
148
+
149
+ // In src/lib/** - BREAKS during build!
150
+ import { Button } from '$ext/ui/primitives.js';
151
+ ```
152
+
153
+ **The solution:**
154
+ - **In library code (`src/lib/**`)**: Use direct package imports
155
+ - **In app code (`src/routes/**`)**: Aliases work fine
156
+
157
+ ```javascript
158
+ // ✅ Library code - use direct imports
159
+ import { Button } from '@hkdigital/lib-core/ui/primitives.js';
160
+
161
+ // ✅ App code - aliases OK (not built/published)
162
+ import { Button } from '$ext/ui/primitives.js';
163
+ ```
164
+
165
+ **Local aliases work everywhere:**
166
+ ```javascript
167
+ // svelte.config.js
168
+ alias: {
169
+ '$lib': 'src/lib', // ✅ Inside project - works everywhere
170
+ '$examples': 'src/routes/examples' // ✅ Inside project
171
+ }
172
+ ```
173
+
174
+ **Why**: Vite/SvelteKit aliases are designed for internal project
175
+ structure. Build tools cannot properly handle aliases pointing outside
176
+ the project or to package names.
177
+
133
178
  ## Class Export Conventions
134
179
  - **All classes should be default exports**: `export default class ClassName`
135
180
  - **Import classes without destructuring**: `import ClassName from './ClassName.js'`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.76",
3
+ "version": "0.5.78",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"
package/scripts/README.md CHANGED
@@ -35,6 +35,8 @@ node scripts/validate-imports.mjs
35
35
  etc. must be explicit
36
36
  5. **Import paths must exist** - All imports must resolve to actual
37
37
  files
38
+ 6. **Aliases must resolve inside project** - Library code (`src/lib/`)
39
+ can only use aliases that resolve to paths inside the project folder
38
40
 
39
41
  ## Import Rules
40
42
 
@@ -180,6 +182,69 @@ import { current } from './existing-file.js';
180
182
  them at build time or runtime. Helps prevent errors when refactoring
181
183
  or moving files.
182
184
 
185
+ ### Rule 6: Aliases must resolve inside the project
186
+
187
+ **Library code must only use aliases that resolve to paths inside the
188
+ project folder**
189
+
190
+ Aliases are designed for internal project structure, not external
191
+ packages. Due to Vite/SvelteKit limitations, aliases that point
192
+ outside the project folder or to package names cannot work correctly in
193
+ library code.
194
+
195
+ ❌ Bad (aliases that don't work in `src/lib/`):
196
+ ```javascript
197
+ // svelte.config.js
198
+ alias: {
199
+ // Points to node_modules (outside project)
200
+ '$ext-lib': 'node_modules/@some/library/dist',
201
+
202
+ // Package name (Vite treats as relative path, doesn't resolve)
203
+ '$pkg': '@some/library',
204
+
205
+ // Absolute path outside project
206
+ '$external': '/usr/local/lib/something'
207
+ }
208
+
209
+ // In src/lib/ui/MyComponent.svelte
210
+ import { Button } from '$ext-lib/ui/primitives.js';
211
+ // ^ Breaks when building library!
212
+ ```
213
+
214
+ ✅ Good (use direct package imports):
215
+ ```javascript
216
+ // In src/lib/ui/MyComponent.svelte
217
+ import { Button } from '@some/library/ui/primitives.js';
218
+ ```
219
+
220
+ ✅ Good (local aliases work everywhere):
221
+ ```javascript
222
+ // svelte.config.js
223
+ alias: {
224
+ '$lib': 'src/lib', // ✅ Inside project
225
+ '$examples': 'src/routes/examples' // ✅ Inside project
226
+ }
227
+
228
+ // In src/lib/ui/MyComponent.svelte
229
+ import { helper } from '$lib/util/helpers.js'; // ✅ Works!
230
+ ```
231
+
232
+ ✅ Also good (aliases work fine in app code):
233
+ ```javascript
234
+ // In src/routes/+page.svelte (not built/published)
235
+ import { Button } from '$ext-lib/ui/primitives.js';
236
+ // ✅ OK - app code isn't published
237
+ ```
238
+
239
+ **Scope:**
240
+ - **Enforced in**: `src/lib/**` (library code that gets built)
241
+ - **Allowed in**: `src/routes/**` (app code, not published)
242
+
243
+ **Why:** Vite/SvelteKit aliases are designed for internal project
244
+ structure. Build tools (`@sveltejs/package`, Vite) cannot properly
245
+ handle aliases pointing outside the project folder or to package names.
246
+ For external dependencies, use direct imports.
247
+
183
248
  ## How Module Resolution Works
184
249
 
185
250
  Understanding how Node.js and Vite resolve imports helps explain
@@ -23,6 +23,15 @@ const EXTERNAL_SCOPES_TO_VALIDATE = ['@hkdigital'];
23
23
  */
24
24
  let PROJECT_ALIASES = {};
25
25
 
26
+ /**
27
+ * Unsafe aliases that don't resolve inside the project folder
28
+ * These break when building libraries with @sveltejs/package
29
+ *
30
+ * Maps alias name to suggested fix (package name or explanation)
31
+ * @type {Map<string, string>}
32
+ */
33
+ const UNSAFE_ALIASES = new Map();
34
+
26
35
  /**
27
36
  * Load aliases from svelte.config.js
28
37
  *
@@ -47,6 +56,62 @@ async function loadAliases() {
47
56
  return {};
48
57
  }
49
58
 
59
+ /**
60
+ * Extract package name from node_modules path
61
+ *
62
+ * @param {string} path - Path containing node_modules
63
+ *
64
+ * @returns {string|null} Package name or null
65
+ */
66
+ function extractPackageNameFromPath(path) {
67
+ // Find node_modules in the path
68
+ const match = path.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);
69
+ if (match) {
70
+ return match[1]; // Returns @scope/package or package
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Detect unsafe aliases that don't resolve inside the project
77
+ * Populates the UNSAFE_ALIASES map
78
+ */
79
+ function detectUnsafeAliases() {
80
+ UNSAFE_ALIASES.clear();
81
+
82
+ for (const [alias, target] of Object.entries(PROJECT_ALIASES)) {
83
+ let suggestion = null;
84
+
85
+ // Resolve to absolute path
86
+ const resolvedPath = isAbsolute(target) ?
87
+ target : join(PROJECT_ROOT, target);
88
+
89
+ // Normalize both paths for comparison (resolve symlinks, etc.)
90
+ const normalizedResolved = resolve(resolvedPath);
91
+ const normalizedRoot = resolve(PROJECT_ROOT);
92
+
93
+ // Check if resolved path is inside the project folder
94
+ const isInsideProject = normalizedResolved.startsWith(normalizedRoot);
95
+
96
+ if (!isInsideProject) {
97
+ // Path is outside project - check if it's in node_modules
98
+ if (resolvedPath.includes('/node_modules/')) {
99
+ const packageName = extractPackageNameFromPath(resolvedPath);
100
+ if (packageName) {
101
+ suggestion = packageName;
102
+ }
103
+ } else {
104
+ // Outside project but not node_modules
105
+ suggestion = '(path outside project - use direct imports)';
106
+ }
107
+
108
+ if (suggestion) {
109
+ UNSAFE_ALIASES.set(alias, suggestion);
110
+ }
111
+ }
112
+ }
113
+ }
114
+
50
115
  /**
51
116
  * Resolve an alias path to its filesystem location
52
117
  *
@@ -286,6 +351,7 @@ async function findExternalBarrelExport(importPath, targetName) {
286
351
  // 1. Explicit index.js imports
287
352
  // 2. Component files (.svelte)
288
353
  // 3. Class files (capitalized .js)
354
+ // 4. Imports without extension (missing extension or directory)
289
355
  let shouldCheck = false;
290
356
 
291
357
  if (lastPart === 'index.js') {
@@ -294,6 +360,9 @@ async function findExternalBarrelExport(importPath, targetName) {
294
360
  shouldCheck = true;
295
361
  } else if (lastPart.match(/^[A-Z][^/]*\.js$/)) {
296
362
  shouldCheck = true;
363
+ } else if (!lastPart.includes('.')) {
364
+ // No extension - could be missing or directory import
365
+ shouldCheck = true;
297
366
  }
298
367
 
299
368
  if (!shouldCheck) return null;
@@ -421,6 +490,7 @@ async function findAliasBarrelExport(importPath, targetName) {
421
490
  // 1. Explicit index.js imports
422
491
  // 2. Component files (.svelte)
423
492
  // 3. Class files (capitalized .js)
493
+ // 4. Imports without extension (missing extension or directory)
424
494
  let shouldCheck = false;
425
495
 
426
496
  if (lastPart === 'index.js') {
@@ -429,6 +499,9 @@ async function findAliasBarrelExport(importPath, targetName) {
429
499
  shouldCheck = true;
430
500
  } else if (lastPart.match(/^[A-Z][^/]*\.js$/)) {
431
501
  shouldCheck = true;
502
+ } else if (!lastPart.includes('.')) {
503
+ // No extension - could be missing or directory import
504
+ shouldCheck = true;
432
505
  }
433
506
 
434
507
  if (!shouldCheck) return null;
@@ -530,6 +603,42 @@ async function validateFile(filePath) {
530
603
  );
531
604
 
532
605
  if (isAliasImport) {
606
+ // Find which alias is being used
607
+ let matchedAlias = null;
608
+ for (const alias of Object.keys(PROJECT_ALIASES)) {
609
+ if (importPath === alias || importPath.startsWith(alias + '/')) {
610
+ matchedAlias = alias;
611
+ break;
612
+ }
613
+ }
614
+
615
+ // Check for unsafe alias usage in library code
616
+ if (isInLib && matchedAlias && UNSAFE_ALIASES.has(matchedAlias)) {
617
+ const suggestion = UNSAFE_ALIASES.get(matchedAlias);
618
+ const pathAfterAlias = importPath.slice(matchedAlias.length);
619
+
620
+ // Construct suggested import if it's a package name
621
+ let errorMsg;
622
+ if (suggestion.startsWith('(')) {
623
+ // Generic message (not a package name)
624
+ errorMsg = `${relativePath}:${lineNum}\n` +
625
+ ` from '${importPath}'\n` +
626
+ ` => ${suggestion}`;
627
+ } else {
628
+ // Package name - construct full import path
629
+ const suggestedImport = suggestion + pathAfterAlias;
630
+ errorMsg = `${relativePath}:${lineNum}\n` +
631
+ ` from '${importPath}'\n` +
632
+ ` => from '${suggestedImport}' ` +
633
+ `(alias resolves outside project)`;
634
+ }
635
+
636
+ errors.push(errorMsg);
637
+
638
+ // Skip further validation for this import
639
+ continue;
640
+ }
641
+
533
642
  // Extract imported names from the import statement
534
643
  const importedNames = extractImportNames(line);
535
644
 
@@ -973,6 +1082,22 @@ async function main() {
973
1082
  console.log();
974
1083
  }
975
1084
 
1085
+ // Detect unsafe aliases (outside project folder)
1086
+ detectUnsafeAliases();
1087
+
1088
+ if (UNSAFE_ALIASES.size > 0) {
1089
+ console.log(
1090
+ '⚠️ Unsafe aliases detected (resolve outside project folder):'
1091
+ );
1092
+ for (const [alias, suggestion] of UNSAFE_ALIASES.entries()) {
1093
+ console.log(
1094
+ ` ${alias} → ${suggestion} ` +
1095
+ `(breaks in src/lib/, OK in src/routes/)`
1096
+ );
1097
+ }
1098
+ console.log();
1099
+ }
1100
+
976
1101
  const files = await findFiles(SRC_DIR);
977
1102
  const allErrors = [];
978
1103