@hkdigital/lib-core 0.5.77 → 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 +45 -0
- package/package.json +1 -1
- package/scripts/README.md +65 -0
- package/scripts/validate-imports.mjs +117 -0
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
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
|
*
|
|
@@ -538,6 +603,42 @@ async function validateFile(filePath) {
|
|
|
538
603
|
);
|
|
539
604
|
|
|
540
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
|
+
|
|
541
642
|
// Extract imported names from the import statement
|
|
542
643
|
const importedNames = extractImportNames(line);
|
|
543
644
|
|
|
@@ -981,6 +1082,22 @@ async function main() {
|
|
|
981
1082
|
console.log();
|
|
982
1083
|
}
|
|
983
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
|
+
|
|
984
1101
|
const files = await findFiles(SRC_DIR);
|
|
985
1102
|
const allErrors = [];
|
|
986
1103
|
|