@hkdigital/lib-core 0.5.70 → 0.5.72
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
CHANGED
|
@@ -303,17 +303,27 @@ pnpm run lint:imports
|
|
|
303
303
|
node node_modules/@hkdigital/lib-core/scripts/validate-imports.mjs
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
+
**Alias support:**
|
|
307
|
+
|
|
308
|
+
The validator automatically reads path aliases from your
|
|
309
|
+
`svelte.config.js` and applies the same barrel export validation rules
|
|
310
|
+
to alias imports. This ensures consistent import patterns across:
|
|
311
|
+
- Internal `$lib/` imports
|
|
312
|
+
- Project aliases like `$hklib-core`, `$hklib-pro`, etc.
|
|
313
|
+
- External `@hkdigital/*` package imports
|
|
314
|
+
|
|
306
315
|
**Validation rules (enforced for `src/lib/` files only):**
|
|
307
316
|
|
|
308
|
-
1. **
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
317
|
+
1. **Alias optimization** - Use `$lib` instead of `$src/lib` (built-in
|
|
318
|
+
SvelteKit alias)
|
|
319
|
+
2. **Cross-domain imports** - Use `$lib/` instead of `../../../`
|
|
320
|
+
3. **Prefer barrel exports** - Use higher-level export files when
|
|
321
|
+
available (applies to `$lib/`, aliases, and `@hkdigital/*` packages)
|
|
322
|
+
4. **Parent index.js imports** - Use `$lib/` or import specific files
|
|
323
|
+
5. **Non-standard extensions** - Include full extension (e.g.,
|
|
312
324
|
`.svelte.js`)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
7. **External package optimization** - Suggests barrel exports for
|
|
316
|
-
`@hkdigital/*` packages
|
|
325
|
+
6. **Directory imports** - Write explicitly or create barrel export file
|
|
326
|
+
7. **File existence** - All import paths must resolve to existing files
|
|
317
327
|
|
|
318
328
|
**Barrel export preference:**
|
|
319
329
|
|
|
@@ -322,6 +332,10 @@ exports your target. This encourages shorter imports that can be
|
|
|
322
332
|
combined:
|
|
323
333
|
|
|
324
334
|
```js
|
|
335
|
+
// Optimize built-in aliases:
|
|
336
|
+
import { MyComponent } from '$src/lib/ui/components.js';
|
|
337
|
+
// → Use: import { MyComponent } from '$lib/ui/components.js';
|
|
338
|
+
|
|
325
339
|
// Internal imports - instead of deep imports:
|
|
326
340
|
import ProfileBlocks from '$lib/ui/components/profile-blocks/ProfileBlocks.svelte';
|
|
327
341
|
import Button from '$lib/ui/primitives/buttons/Button.svelte';
|
|
@@ -330,6 +344,14 @@ import Button from '$lib/ui/primitives/buttons/Button.svelte';
|
|
|
330
344
|
import { ProfileBlocks } from '$lib/ui/components.js';
|
|
331
345
|
import { Button } from '$lib/ui/primitives.js';
|
|
332
346
|
|
|
347
|
+
// Project aliases - instead of deep imports:
|
|
348
|
+
import { Logger } from '$hklib-core/logging/logger/Logger.js';
|
|
349
|
+
import { HttpClient } from '$hklib-core/network/http/HttpClient.js';
|
|
350
|
+
|
|
351
|
+
// Use barrel exports:
|
|
352
|
+
import { Logger } from '$hklib-core/logging.js';
|
|
353
|
+
import { HttpClient } from '$hklib-core/network/http.js';
|
|
354
|
+
|
|
333
355
|
// External imports - instead of deep imports:
|
|
334
356
|
import { TextButton } from '@hkdigital/lib-core/ui/primitives/buttons/index.js';
|
|
335
357
|
import { TextInput } from '@hkdigital/lib-core/ui/primitives/inputs/index.js';
|
|
@@ -341,7 +363,7 @@ import { TextButton, TextInput } from '@hkdigital/lib-core/ui/primitives.js';
|
|
|
341
363
|
The validator checks from highest to lowest level (`$lib/ui.js` →
|
|
342
364
|
`$lib/ui/components.js` → `$lib/ui/components/profile-blocks.js`) and
|
|
343
365
|
suggests the highest-level file that exports your target. The same
|
|
344
|
-
logic applies to external `@hkdigital/*` packages.
|
|
366
|
+
logic applies to project aliases and external `@hkdigital/*` packages.
|
|
345
367
|
|
|
346
368
|
**Routes are exempt from strict rules:**
|
|
347
369
|
|
|
@@ -353,6 +375,17 @@ files.
|
|
|
353
375
|
**Example output:**
|
|
354
376
|
|
|
355
377
|
```
|
|
378
|
+
Validating import paths...
|
|
379
|
+
|
|
380
|
+
Found project aliases:
|
|
381
|
+
$src → src
|
|
382
|
+
$examples → src/routes/examples
|
|
383
|
+
$hklib-core → src/lib
|
|
384
|
+
|
|
385
|
+
src/lib/ui/panels/Panel.svelte:3
|
|
386
|
+
from '$src/lib/ui/components.js'
|
|
387
|
+
=> from '$lib/ui/components.js' (use built-in $lib alias)
|
|
388
|
+
|
|
356
389
|
src/lib/ui/panels/Panel.svelte:6
|
|
357
390
|
from '../../../components/profile-blocks/ProfileBlocks.svelte'
|
|
358
391
|
=> from '$lib/ui/components.js' (use barrel export)
|
|
@@ -362,6 +395,10 @@ src/lib/ui/pages/Profile.svelte:8
|
|
|
362
395
|
=> from '$lib/ui/components.js' (use barrel export for shorter imports)
|
|
363
396
|
|
|
364
397
|
src/lib/forms/LoginForm.svelte:4
|
|
398
|
+
from '$hklib-core/logging/logger/Logger.js'
|
|
399
|
+
=> from '$hklib-core/logging.js' (use barrel export)
|
|
400
|
+
|
|
401
|
+
src/lib/forms/LoginForm.svelte:6
|
|
365
402
|
from '@hkdigital/lib-core/ui/primitives/buttons/index.js'
|
|
366
403
|
=> from '@hkdigital/lib-core/ui/primitives.js' (use barrel export)
|
|
367
404
|
|
|
@@ -370,9 +407,10 @@ src/routes/explorer/[...path]/+page.svelte:4
|
|
|
370
407
|
✅ Allowed in routes
|
|
371
408
|
```
|
|
372
409
|
|
|
373
|
-
**What gets checked for
|
|
410
|
+
**What gets checked for barrel export suggestions:**
|
|
374
411
|
|
|
375
|
-
The validator only suggests barrel exports for
|
|
412
|
+
The validator only suggests barrel exports (for `$lib/`, project aliases,
|
|
413
|
+
and external `@hkdigital/*` packages) for:
|
|
376
414
|
- Explicit `index.js` imports
|
|
377
415
|
- Component files (`.svelte`)
|
|
378
416
|
- Class files (capitalized `.js` files)
|
|
@@ -380,6 +418,20 @@ The validator only suggests barrel exports for:
|
|
|
380
418
|
Intentional imports like `helpers.js`, `config.js`, or other lowercase
|
|
381
419
|
utility files are assumed to be the public API and won't be flagged.
|
|
382
420
|
|
|
421
|
+
**Alias configuration:**
|
|
422
|
+
|
|
423
|
+
The validator automatically detects aliases in your `svelte.config.js`.
|
|
424
|
+
For example, if your config has:
|
|
425
|
+
```js
|
|
426
|
+
alias: {
|
|
427
|
+
$src: 'src',
|
|
428
|
+
$hklib-core: 'node_modules/@hkdigital/lib-core/dist'
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The validator will apply the same barrel export rules to these aliases
|
|
433
|
+
as it does to `$lib/` and `@hkdigital/*` imports.
|
|
434
|
+
|
|
383
435
|
### Import Patterns and Export Structure
|
|
384
436
|
|
|
385
437
|
**Public exports use domain-specific files matching folder names:**
|
|
@@ -13,6 +13,6 @@ declare const Presenter: import("svelte").Component<{
|
|
|
13
13
|
classes?: string;
|
|
14
14
|
slides?: import("./typedef.js").Slide[];
|
|
15
15
|
presenterRef?: import("./Presenter.state.svelte.js").PresenterRef;
|
|
16
|
-
layoutSnippet: import("svelte").Snippet<[import("
|
|
16
|
+
layoutSnippet: import("svelte").Snippet<[import("./typedef.js").Slide | null, import("./typedef.js").Layer]>;
|
|
17
17
|
loadingSnippet?: import("svelte").Snippet;
|
|
18
18
|
}, {}, "presenterRef">;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { default as Presenter } from "./Presenter.svelte";
|
|
2
2
|
export { default as ImageSlide } from "./ImageSlide.svelte";
|
|
3
3
|
export { PresenterState } from "./Presenter.state.svelte.js";
|
|
4
|
-
export * from "./typedef.js";
|
|
5
4
|
export * from "./constants.js";
|
|
6
5
|
export * from "./util.js";
|
package/package.json
CHANGED
|
@@ -12,6 +12,55 @@ const SRC_DIR = join(PROJECT_ROOT, 'src');
|
|
|
12
12
|
*/
|
|
13
13
|
const EXTERNAL_SCOPES_TO_VALIDATE = ['@hkdigital'];
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Project aliases from svelte.config.js
|
|
17
|
+
* Loaded dynamically at startup
|
|
18
|
+
*
|
|
19
|
+
* @type {Record<string, string>}
|
|
20
|
+
*/
|
|
21
|
+
let PROJECT_ALIASES = {};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load aliases from svelte.config.js
|
|
25
|
+
*
|
|
26
|
+
* @returns {Promise<Record<string, string>>} Alias mappings
|
|
27
|
+
*/
|
|
28
|
+
async function loadAliases() {
|
|
29
|
+
try {
|
|
30
|
+
const configPath = join(PROJECT_ROOT, 'svelte.config.js');
|
|
31
|
+
|
|
32
|
+
// Use dynamic import to load ES module
|
|
33
|
+
const config = await import(`file://${configPath}`);
|
|
34
|
+
const svelteConfig = config.default;
|
|
35
|
+
|
|
36
|
+
if (svelteConfig?.kit?.alias) {
|
|
37
|
+
return svelteConfig.kit.alias;
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Config file doesn't exist or can't be loaded
|
|
41
|
+
// This is OK - not all projects will have aliases
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve an alias path to its filesystem location
|
|
49
|
+
*
|
|
50
|
+
* @param {string} aliasPath - Import path using alias (e.g., $hklib-core/...)
|
|
51
|
+
*
|
|
52
|
+
* @returns {string|null} Resolved filesystem path or null
|
|
53
|
+
*/
|
|
54
|
+
function resolveAliasPath(aliasPath) {
|
|
55
|
+
for (const [alias, target] of Object.entries(PROJECT_ALIASES)) {
|
|
56
|
+
if (aliasPath === alias || aliasPath.startsWith(alias + '/')) {
|
|
57
|
+
const pathAfterAlias = aliasPath.slice(alias.length);
|
|
58
|
+
return join(PROJECT_ROOT, target, pathAfterAlias);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
15
64
|
/**
|
|
16
65
|
* Find all JS and Svelte files recursively
|
|
17
66
|
*
|
|
@@ -314,6 +363,105 @@ async function findExternalBarrelExport(importPath, targetName) {
|
|
|
314
363
|
return null;
|
|
315
364
|
}
|
|
316
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Find highest-level barrel export in alias path
|
|
368
|
+
*
|
|
369
|
+
* For $hklib-core/ui/primitives/buttons/index.js:
|
|
370
|
+
* - Check $hklib-core/ui/primitives.js
|
|
371
|
+
* - Check $hklib-core/ui.js
|
|
372
|
+
*
|
|
373
|
+
* @param {string} importPath - Alias import path
|
|
374
|
+
* @param {string} targetName - Name of export to find
|
|
375
|
+
*
|
|
376
|
+
* @returns {Promise<string|null>} Suggested barrel path or null
|
|
377
|
+
*/
|
|
378
|
+
async function findAliasBarrelExport(importPath, targetName) {
|
|
379
|
+
// Find the matching alias
|
|
380
|
+
let matchedAlias = null;
|
|
381
|
+
let pathAfterAlias = null;
|
|
382
|
+
|
|
383
|
+
for (const alias of Object.keys(PROJECT_ALIASES)) {
|
|
384
|
+
if (importPath === alias || importPath.startsWith(alias + '/')) {
|
|
385
|
+
matchedAlias = alias;
|
|
386
|
+
pathAfterAlias = importPath.slice(alias.length);
|
|
387
|
+
if (pathAfterAlias.startsWith('/')) {
|
|
388
|
+
pathAfterAlias = pathAfterAlias.slice(1);
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (!matchedAlias || !pathAfterAlias) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const pathInAlias = pathAfterAlias.split('/');
|
|
399
|
+
|
|
400
|
+
// If no path in alias, nothing to suggest
|
|
401
|
+
if (pathInAlias.length === 0 || pathInAlias[0] === '') {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const aliasRootPath = resolveAliasPath(matchedAlias);
|
|
406
|
+
|
|
407
|
+
// Extract target to find (last part without extension)
|
|
408
|
+
const lastPart = pathInAlias[pathInAlias.length - 1];
|
|
409
|
+
const targetBase = lastPart.replace(/\.(js|svelte)$/, '');
|
|
410
|
+
|
|
411
|
+
// Only check for specific import types (matches internal logic)
|
|
412
|
+
// 1. Explicit index.js imports
|
|
413
|
+
// 2. Component files (.svelte)
|
|
414
|
+
// 3. Class files (capitalized .js)
|
|
415
|
+
let shouldCheck = false;
|
|
416
|
+
|
|
417
|
+
if (lastPart === 'index.js') {
|
|
418
|
+
shouldCheck = true;
|
|
419
|
+
} else if (lastPart.endsWith('.svelte')) {
|
|
420
|
+
shouldCheck = true;
|
|
421
|
+
} else if (lastPart.match(/^[A-Z][^/]*\.js$/)) {
|
|
422
|
+
shouldCheck = true;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (!shouldCheck) return null;
|
|
426
|
+
|
|
427
|
+
// Try progressively higher-level barrel files
|
|
428
|
+
for (let i = 1; i < pathInAlias.length; i++) {
|
|
429
|
+
const barrelPath = pathInAlias.slice(0, i).join('/') + '.js';
|
|
430
|
+
const fsBarrelPath = join(aliasRootPath, barrelPath);
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
const stats = await stat(fsBarrelPath);
|
|
434
|
+
if (stats.isFile()) {
|
|
435
|
+
const content = await readFile(fsBarrelPath, 'utf-8');
|
|
436
|
+
|
|
437
|
+
// Check if this barrel exports our target
|
|
438
|
+
// Patterns to match:
|
|
439
|
+
// export { TextButton } from './path';
|
|
440
|
+
// export * from './path';
|
|
441
|
+
const exportPatterns = [
|
|
442
|
+
// Named export with exact name
|
|
443
|
+
new RegExp(
|
|
444
|
+
`export\\s+\\{[^}]*\\b${targetName}\\b[^}]*\\}`,
|
|
445
|
+
'm'
|
|
446
|
+
),
|
|
447
|
+
// Re-export all
|
|
448
|
+
/export\s+\*\s+from/,
|
|
449
|
+
// Default export
|
|
450
|
+
new RegExp(`export\\s+default\\s+${targetName}\\b`, 'm')
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
if (exportPatterns.some(pattern => pattern.test(content))) {
|
|
454
|
+
return `${matchedAlias}/${barrelPath}`;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
// File doesn't exist, continue
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
317
465
|
/**
|
|
318
466
|
* Validate import paths in a file
|
|
319
467
|
*
|
|
@@ -350,7 +498,52 @@ async function validateFile(filePath) {
|
|
|
350
498
|
const importPathRaw = importMatch[1];
|
|
351
499
|
|
|
352
500
|
// Strip query parameters (Vite asset imports like ?preset=render)
|
|
353
|
-
|
|
501
|
+
let importPath = importPathRaw.split('?')[0];
|
|
502
|
+
|
|
503
|
+
// Check if using $src/lib when $lib is available (built-in SvelteKit)
|
|
504
|
+
// Report the issue and normalize the path for further validation
|
|
505
|
+
let hasSrcLibIssue = false;
|
|
506
|
+
if (importPath.startsWith('$src/lib/') || importPath === '$src/lib') {
|
|
507
|
+
hasSrcLibIssue = true;
|
|
508
|
+
const optimizedPath = importPath.replace('$src/lib', '$lib');
|
|
509
|
+
errors.push(
|
|
510
|
+
`${relativePath}:${lineNum}\n` +
|
|
511
|
+
` from '${importPath}'\n` +
|
|
512
|
+
` => from '${optimizedPath}' (use built-in $lib alias)`
|
|
513
|
+
);
|
|
514
|
+
// Normalize for further validation
|
|
515
|
+
importPath = optimizedPath;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Check if import uses a project alias
|
|
519
|
+
const isAliasImport = Object.keys(PROJECT_ALIASES).some(
|
|
520
|
+
alias => importPath === alias || importPath.startsWith(alias + '/')
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
if (isAliasImport) {
|
|
524
|
+
// Extract imported names from the import statement
|
|
525
|
+
const importedNames = extractImportNames(line);
|
|
526
|
+
|
|
527
|
+
// Check each imported name for barrel exports
|
|
528
|
+
for (const importedName of importedNames) {
|
|
529
|
+
const barrelPath = await findAliasBarrelExport(
|
|
530
|
+
importPath,
|
|
531
|
+
importedName
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
if (barrelPath) {
|
|
535
|
+
errors.push(
|
|
536
|
+
`${relativePath}:${lineNum}\n` +
|
|
537
|
+
` from '${importPath}'\n` +
|
|
538
|
+
` => from '${barrelPath}' (use barrel export)`
|
|
539
|
+
);
|
|
540
|
+
break; // Only report once per line
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Skip further validation for alias imports
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
354
547
|
|
|
355
548
|
// Check external packages from configured scopes
|
|
356
549
|
const isExternalPackage = !importPath.startsWith('./') &&
|
|
@@ -698,6 +891,17 @@ async function validateFile(filePath) {
|
|
|
698
891
|
async function main() {
|
|
699
892
|
console.log('Validating import paths...\n');
|
|
700
893
|
|
|
894
|
+
// Load project aliases from svelte.config.js
|
|
895
|
+
PROJECT_ALIASES = await loadAliases();
|
|
896
|
+
|
|
897
|
+
if (Object.keys(PROJECT_ALIASES).length > 0) {
|
|
898
|
+
console.log('Found project aliases:');
|
|
899
|
+
for (const [alias, target] of Object.entries(PROJECT_ALIASES)) {
|
|
900
|
+
console.log(` ${alias} → ${target}`);
|
|
901
|
+
}
|
|
902
|
+
console.log();
|
|
903
|
+
}
|
|
904
|
+
|
|
701
905
|
const files = await findFiles(SRC_DIR);
|
|
702
906
|
const allErrors = [];
|
|
703
907
|
|