@hkdigital/lib-core 0.5.65 → 0.5.67
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/README.md +37 -0
- package/package.json +1 -1
- package/scripts/validate-imports.mjs +42 -21
package/README.md
CHANGED
|
@@ -277,6 +277,43 @@ pnpm run upgrade:all # Update all packages
|
|
|
277
277
|
pnpm run publish:npm # Version bump and publish to npm
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
### Import Validation
|
|
281
|
+
|
|
282
|
+
The library includes a validation script to enforce consistent import
|
|
283
|
+
patterns. Run it in your project:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
node node_modules/@hkdigital/lib-core/scripts/validate-imports.mjs
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Validation rules (enforced for `src/lib/` files only):**
|
|
290
|
+
|
|
291
|
+
1. **Cross-domain imports** - Use `$lib/` instead of `../../../`
|
|
292
|
+
2. **Parent index.js imports** - Use `$lib/` or import specific files
|
|
293
|
+
3. **Non-standard extensions** - Include full extension (e.g.,
|
|
294
|
+
`.svelte.js`)
|
|
295
|
+
4. **Directory imports** - Write explicitly (e.g., `./path/index.js`)
|
|
296
|
+
5. **File existence** - All import paths must resolve to existing files
|
|
297
|
+
|
|
298
|
+
**Routes are exempt from strict rules:**
|
|
299
|
+
|
|
300
|
+
Files in `src/routes/` can use relative imports freely, including parent
|
|
301
|
+
navigation and index.js imports. Since SvelteKit doesn't provide a
|
|
302
|
+
`$routes` alias, relative imports are the standard pattern for route
|
|
303
|
+
files.
|
|
304
|
+
|
|
305
|
+
**Example output:**
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
src/lib/ui/components/Button.svelte:7
|
|
309
|
+
from '$lib/ui/primitives/buttons'
|
|
310
|
+
=> from '$lib/ui/primitives/buttons/index.js'
|
|
311
|
+
|
|
312
|
+
src/routes/explorer/[...path]/+page.svelte:4
|
|
313
|
+
from '../components/index.js'
|
|
314
|
+
✅ Allowed in routes
|
|
315
|
+
```
|
|
316
|
+
|
|
280
317
|
### Import Patterns and Export Structure
|
|
281
318
|
|
|
282
319
|
**Public exports use domain-specific files matching folder names:**
|
package/package.json
CHANGED
|
@@ -94,6 +94,8 @@ async function validateFile(filePath) {
|
|
|
94
94
|
const relativePath = relative(PROJECT_ROOT, filePath);
|
|
95
95
|
const isTestFile = filePath.endsWith('.test.js') ||
|
|
96
96
|
filePath.endsWith('.spec.js');
|
|
97
|
+
const isInLib = filePath.includes('/src/lib/');
|
|
98
|
+
const isInRoutes = filePath.includes('/src/routes/');
|
|
97
99
|
|
|
98
100
|
// Check each line for import statements
|
|
99
101
|
const lines = content.split('\n');
|
|
@@ -125,17 +127,20 @@ async function validateFile(filePath) {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
// Check 1: Cross-domain relative imports (3+ levels up)
|
|
128
|
-
|
|
130
|
+
// Only enforce for lib files
|
|
131
|
+
if (isInLib && importPath.match(/^\.\.\/\.\.\/\.\.\//)) {
|
|
129
132
|
errors.push(
|
|
130
|
-
`${relativePath}:${lineNum}
|
|
131
|
-
`
|
|
133
|
+
`${relativePath}:${lineNum}\n` +
|
|
134
|
+
` from '${importPath}'\n` +
|
|
135
|
+
` => Use $lib/ for cross-domain imports`
|
|
132
136
|
);
|
|
133
137
|
continue;
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
// Check 2: Local index.js imports (skip for test files)
|
|
140
|
+
// Check 2: Local index.js imports (skip for test files and routes)
|
|
137
141
|
// Allow ./index.js and ./subfolder/index.js but flag parent navigation
|
|
138
|
-
|
|
142
|
+
// Only enforce for lib files
|
|
143
|
+
if (isInLib && !isTestFile && importPath.match(/\/index\.js$/)) {
|
|
139
144
|
// Allow same-directory and child directory imports
|
|
140
145
|
// Examples: ./index.js, ./subfolder/index.js, ./(meta)/index.js
|
|
141
146
|
// Flag parent navigation: ../index.js, ../../index.js
|
|
@@ -143,8 +148,9 @@ async function validateFile(filePath) {
|
|
|
143
148
|
|
|
144
149
|
if (isParentNavigation) {
|
|
145
150
|
errors.push(
|
|
146
|
-
`${relativePath}:${lineNum}
|
|
147
|
-
`
|
|
151
|
+
`${relativePath}:${lineNum}\n` +
|
|
152
|
+
` from '${importPath}'\n` +
|
|
153
|
+
` => Use $lib/ or import specific file instead`
|
|
148
154
|
);
|
|
149
155
|
continue;
|
|
150
156
|
}
|
|
@@ -254,8 +260,9 @@ async function validateFile(filePath) {
|
|
|
254
260
|
if (stats.isFile()) {
|
|
255
261
|
const correctImport = baseImportPath + ext;
|
|
256
262
|
errors.push(
|
|
257
|
-
`${relativePath}:${lineNum}
|
|
258
|
-
`
|
|
263
|
+
`${relativePath}:${lineNum}\n` +
|
|
264
|
+
` from '${importPath}'\n` +
|
|
265
|
+
` => from '${correctImport}'`
|
|
259
266
|
);
|
|
260
267
|
foundNonStandard = true;
|
|
261
268
|
break;
|
|
@@ -280,24 +287,37 @@ async function validateFile(filePath) {
|
|
|
280
287
|
if (!hasAnyExtension) {
|
|
281
288
|
// Check if it's a directory with index.js
|
|
282
289
|
if (await isDirectoryWithIndex(fsPath)) {
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
|
|
290
|
+
// For $lib/ imports, suggest creating a barrel export file
|
|
291
|
+
// following the library pattern: folder → matching .js file
|
|
292
|
+
if (importPath.startsWith('$lib/')) {
|
|
293
|
+
errors.push(
|
|
294
|
+
`${relativePath}:${lineNum}\n` +
|
|
295
|
+
` from '${importPath}'\n` +
|
|
296
|
+
` => Create export file: ${importPath}.js (remove index.js)`
|
|
297
|
+
);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
286
300
|
|
|
301
|
+
// For relative imports with parent navigation,
|
|
302
|
+
// suggest creating export file
|
|
303
|
+
const wouldBeParentNavigation = importPath.match(/^\.\.\//);
|
|
287
304
|
if (wouldBeParentNavigation) {
|
|
288
305
|
errors.push(
|
|
289
|
-
`${relativePath}:${lineNum}
|
|
290
|
-
`
|
|
291
|
-
`
|
|
306
|
+
`${relativePath}:${lineNum}\n` +
|
|
307
|
+
` from '${importPath}'\n` +
|
|
308
|
+
` => Create export file: ${importPath}.js or ` +
|
|
309
|
+
`import specific file`
|
|
292
310
|
);
|
|
293
311
|
continue;
|
|
294
312
|
}
|
|
295
313
|
|
|
314
|
+
// For local relative imports (./path), suggest index.js
|
|
296
315
|
const suggestion = importPath.endsWith('/') ?
|
|
297
316
|
`${importPath}index.js` : `${importPath}/index.js`;
|
|
298
317
|
errors.push(
|
|
299
|
-
`${relativePath}:${lineNum}
|
|
300
|
-
`
|
|
318
|
+
`${relativePath}:${lineNum}\n` +
|
|
319
|
+
` from '${importPath}'\n` +
|
|
320
|
+
` => from '${suggestion}'`
|
|
301
321
|
);
|
|
302
322
|
continue;
|
|
303
323
|
}
|
|
@@ -336,8 +356,9 @@ async function validateFile(filePath) {
|
|
|
336
356
|
|
|
337
357
|
if (!fileExists) {
|
|
338
358
|
errors.push(
|
|
339
|
-
`${relativePath}:${lineNum}
|
|
340
|
-
`'${importPath}'`
|
|
359
|
+
`${relativePath}:${lineNum}\n` +
|
|
360
|
+
` from '${importPath}'\n` +
|
|
361
|
+
` => Import path does not exist`
|
|
341
362
|
);
|
|
342
363
|
}
|
|
343
364
|
}
|
|
@@ -361,8 +382,8 @@ async function main() {
|
|
|
361
382
|
|
|
362
383
|
if (allErrors.length > 0) {
|
|
363
384
|
console.error('❌ Found import path violations:\n');
|
|
364
|
-
allErrors.forEach(error => console.error(
|
|
365
|
-
console.error(
|
|
385
|
+
allErrors.forEach(error => console.error(`${error}\n`));
|
|
386
|
+
console.error(`${allErrors.length} error(s) found.`);
|
|
366
387
|
process.exit(1);
|
|
367
388
|
}
|
|
368
389
|
|