@hkdigital/lib-core 0.5.63 → 0.5.66
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 +29 -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
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
4
4
|
import { join, relative, resolve, dirname } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
5
|
|
|
7
|
-
const
|
|
8
|
-
const PROJECT_ROOT = join(__dirname, '..');
|
|
6
|
+
const PROJECT_ROOT = process.cwd();
|
|
9
7
|
const SRC_DIR = join(PROJECT_ROOT, 'src');
|
|
10
8
|
|
|
11
9
|
/**
|
|
@@ -96,6 +94,8 @@ async function validateFile(filePath) {
|
|
|
96
94
|
const relativePath = relative(PROJECT_ROOT, filePath);
|
|
97
95
|
const isTestFile = filePath.endsWith('.test.js') ||
|
|
98
96
|
filePath.endsWith('.spec.js');
|
|
97
|
+
const isInLib = filePath.includes('/src/lib/');
|
|
98
|
+
const isInRoutes = filePath.includes('/src/routes/');
|
|
99
99
|
|
|
100
100
|
// Check each line for import statements
|
|
101
101
|
const lines = content.split('\n');
|
|
@@ -127,17 +127,20 @@ async function validateFile(filePath) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// Check 1: Cross-domain relative imports (3+ levels up)
|
|
130
|
-
|
|
130
|
+
// Only enforce for lib files
|
|
131
|
+
if (isInLib && importPath.match(/^\.\.\/\.\.\/\.\.\//)) {
|
|
131
132
|
errors.push(
|
|
132
|
-
`${relativePath}:${lineNum}
|
|
133
|
-
`
|
|
133
|
+
`${relativePath}:${lineNum}\n` +
|
|
134
|
+
` from '${importPath}'\n` +
|
|
135
|
+
` => Use $lib/ for cross-domain imports`
|
|
134
136
|
);
|
|
135
137
|
continue;
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
// Check 2: Local index.js imports (skip for test files)
|
|
140
|
+
// Check 2: Local index.js imports (skip for test files and routes)
|
|
139
141
|
// Allow ./index.js and ./subfolder/index.js but flag parent navigation
|
|
140
|
-
|
|
142
|
+
// Only enforce for lib files
|
|
143
|
+
if (isInLib && !isTestFile && importPath.match(/\/index\.js$/)) {
|
|
141
144
|
// Allow same-directory and child directory imports
|
|
142
145
|
// Examples: ./index.js, ./subfolder/index.js, ./(meta)/index.js
|
|
143
146
|
// Flag parent navigation: ../index.js, ../../index.js
|
|
@@ -145,8 +148,9 @@ async function validateFile(filePath) {
|
|
|
145
148
|
|
|
146
149
|
if (isParentNavigation) {
|
|
147
150
|
errors.push(
|
|
148
|
-
`${relativePath}:${lineNum}
|
|
149
|
-
`
|
|
151
|
+
`${relativePath}:${lineNum}\n` +
|
|
152
|
+
` from '${importPath}'\n` +
|
|
153
|
+
` => Use $lib/ or import specific file instead`
|
|
150
154
|
);
|
|
151
155
|
continue;
|
|
152
156
|
}
|
|
@@ -256,8 +260,9 @@ async function validateFile(filePath) {
|
|
|
256
260
|
if (stats.isFile()) {
|
|
257
261
|
const correctImport = baseImportPath + ext;
|
|
258
262
|
errors.push(
|
|
259
|
-
`${relativePath}:${lineNum}
|
|
260
|
-
`
|
|
263
|
+
`${relativePath}:${lineNum}\n` +
|
|
264
|
+
` from '${importPath}'\n` +
|
|
265
|
+
` => from '${correctImport}'`
|
|
261
266
|
);
|
|
262
267
|
foundNonStandard = true;
|
|
263
268
|
break;
|
|
@@ -288,9 +293,10 @@ async function validateFile(filePath) {
|
|
|
288
293
|
|
|
289
294
|
if (wouldBeParentNavigation) {
|
|
290
295
|
errors.push(
|
|
291
|
-
`${relativePath}:${lineNum}
|
|
292
|
-
`
|
|
293
|
-
`
|
|
296
|
+
`${relativePath}:${lineNum}\n` +
|
|
297
|
+
` from '${importPath}'\n` +
|
|
298
|
+
` => Create export file like '${importPath}.js' or ` +
|
|
299
|
+
`import specific file`
|
|
294
300
|
);
|
|
295
301
|
continue;
|
|
296
302
|
}
|
|
@@ -298,8 +304,9 @@ async function validateFile(filePath) {
|
|
|
298
304
|
const suggestion = importPath.endsWith('/') ?
|
|
299
305
|
`${importPath}index.js` : `${importPath}/index.js`;
|
|
300
306
|
errors.push(
|
|
301
|
-
`${relativePath}:${lineNum}
|
|
302
|
-
`
|
|
307
|
+
`${relativePath}:${lineNum}\n` +
|
|
308
|
+
` from '${importPath}'\n` +
|
|
309
|
+
` => from '${suggestion}'`
|
|
303
310
|
);
|
|
304
311
|
continue;
|
|
305
312
|
}
|
|
@@ -338,8 +345,9 @@ async function validateFile(filePath) {
|
|
|
338
345
|
|
|
339
346
|
if (!fileExists) {
|
|
340
347
|
errors.push(
|
|
341
|
-
`${relativePath}:${lineNum}
|
|
342
|
-
`'${importPath}'`
|
|
348
|
+
`${relativePath}:${lineNum}\n` +
|
|
349
|
+
` from '${importPath}'\n` +
|
|
350
|
+
` => Import path does not exist`
|
|
343
351
|
);
|
|
344
352
|
}
|
|
345
353
|
}
|
|
@@ -363,8 +371,8 @@ async function main() {
|
|
|
363
371
|
|
|
364
372
|
if (allErrors.length > 0) {
|
|
365
373
|
console.error('❌ Found import path violations:\n');
|
|
366
|
-
allErrors.forEach(error => console.error(
|
|
367
|
-
console.error(
|
|
374
|
+
allErrors.forEach(error => console.error(`${error}\n`));
|
|
375
|
+
console.error(`${allErrors.length} error(s) found.`);
|
|
368
376
|
process.exit(1);
|
|
369
377
|
}
|
|
370
378
|
|