@hkdigital/lib-core 0.5.70 → 0.5.71

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.
@@ -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("../../components.js").Slide | null, import("../../components.js").Layer]>;
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";
@@ -3,7 +3,6 @@ export { default as ImageSlide } from './ImageSlide.svelte';
3
3
 
4
4
  export { PresenterState } from './Presenter.state.svelte.js';
5
5
 
6
- export * from './typedef.js';
7
6
  export * from './constants.js';
8
7
 
9
8
  // @ts-ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.70",
3
+ "version": "0.5.71",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"
@@ -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
  *
@@ -352,6 +500,36 @@ async function validateFile(filePath) {
352
500
  // Strip query parameters (Vite asset imports like ?preset=render)
353
501
  const importPath = importPathRaw.split('?')[0];
354
502
 
503
+ // Check if import uses a project alias
504
+ const isAliasImport = Object.keys(PROJECT_ALIASES).some(
505
+ alias => importPath === alias || importPath.startsWith(alias + '/')
506
+ );
507
+
508
+ if (isAliasImport) {
509
+ // Extract imported names from the import statement
510
+ const importedNames = extractImportNames(line);
511
+
512
+ // Check each imported name for barrel exports
513
+ for (const importedName of importedNames) {
514
+ const barrelPath = await findAliasBarrelExport(
515
+ importPath,
516
+ importedName
517
+ );
518
+
519
+ if (barrelPath) {
520
+ errors.push(
521
+ `${relativePath}:${lineNum}\n` +
522
+ ` from '${importPath}'\n` +
523
+ ` => from '${barrelPath}' (use barrel export)`
524
+ );
525
+ break; // Only report once per line
526
+ }
527
+ }
528
+
529
+ // Skip further validation for alias imports
530
+ continue;
531
+ }
532
+
355
533
  // Check external packages from configured scopes
356
534
  const isExternalPackage = !importPath.startsWith('./') &&
357
535
  !importPath.startsWith('../') &&
@@ -698,6 +876,17 @@ async function validateFile(filePath) {
698
876
  async function main() {
699
877
  console.log('Validating import paths...\n');
700
878
 
879
+ // Load project aliases from svelte.config.js
880
+ PROJECT_ALIASES = await loadAliases();
881
+
882
+ if (Object.keys(PROJECT_ALIASES).length > 0) {
883
+ console.log('Found project aliases:');
884
+ for (const [alias, target] of Object.entries(PROJECT_ALIASES)) {
885
+ console.log(` ${alias} → ${target}`);
886
+ }
887
+ console.log();
888
+ }
889
+
701
890
  const files = await findFiles(SRC_DIR);
702
891
  const allErrors = [];
703
892