@hkdigital/lib-core 0.5.69 → 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.69",
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
  *
@@ -240,10 +289,145 @@ async function findExternalBarrelExport(importPath, targetName) {
240
289
 
241
290
  if (!shouldCheck) return null;
242
291
 
292
+ // Read package.json to check for exports mapping
293
+ let exportsMapping = null;
294
+ try {
295
+ const pkgJsonPath = join(nodeModulesPath, 'package.json');
296
+ const pkgJsonContent = await readFile(pkgJsonPath, 'utf-8');
297
+ const pkgJson = JSON.parse(pkgJsonContent);
298
+
299
+ // Check if there's a "./*" export mapping
300
+ if (pkgJson.exports && pkgJson.exports['./*']) {
301
+ const mapping = pkgJson.exports['./*'];
302
+ const mappingStr = typeof mapping === 'string' ?
303
+ mapping : mapping.default;
304
+
305
+ // Extract prefix from mapping like "./dist/*" -> "dist/"
306
+ if (mappingStr && mappingStr.includes('*')) {
307
+ exportsMapping = mappingStr.replace(/\/?\*$/, '');
308
+ if (exportsMapping.startsWith('./')) {
309
+ exportsMapping = exportsMapping.slice(2);
310
+ }
311
+ if (exportsMapping && !exportsMapping.endsWith('/')) {
312
+ exportsMapping += '/';
313
+ }
314
+ }
315
+ }
316
+ } catch {
317
+ // Could not read package.json, continue without mapping
318
+ }
319
+
243
320
  // Try progressively higher-level barrel files
244
321
  for (let i = 1; i < pathInPackage.length; i++) {
245
322
  const barrelPath = pathInPackage.slice(0, i).join('/') + '.js';
246
- const fsBarrelPath = join(nodeModulesPath, barrelPath);
323
+
324
+ // Try both with and without exports mapping
325
+ const pathsToTry = [
326
+ join(nodeModulesPath, barrelPath),
327
+ exportsMapping ?
328
+ join(nodeModulesPath, exportsMapping + barrelPath) : null
329
+ ].filter(Boolean);
330
+
331
+ for (const fsBarrelPath of pathsToTry) {
332
+ try {
333
+ const stats = await stat(fsBarrelPath);
334
+ if (stats.isFile()) {
335
+ const content = await readFile(fsBarrelPath, 'utf-8');
336
+
337
+ // Check if this barrel exports our target
338
+ // Patterns to match:
339
+ // export { TextButton } from './path';
340
+ // export * from './path';
341
+ const exportPatterns = [
342
+ // Named export with exact name
343
+ new RegExp(
344
+ `export\\s+\\{[^}]*\\b${targetName}\\b[^}]*\\}`,
345
+ 'm'
346
+ ),
347
+ // Re-export all
348
+ /export\s+\*\s+from/,
349
+ // Default export
350
+ new RegExp(`export\\s+default\\s+${targetName}\\b`, 'm')
351
+ ];
352
+
353
+ if (exportPatterns.some(pattern => pattern.test(content))) {
354
+ return `${pkgName}/${barrelPath}`;
355
+ }
356
+ }
357
+ } catch {
358
+ // File doesn't exist, continue
359
+ }
360
+ }
361
+ }
362
+
363
+ return null;
364
+ }
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);
247
431
 
248
432
  try {
249
433
  const stats = await stat(fsBarrelPath);
@@ -267,7 +451,7 @@ async function findExternalBarrelExport(importPath, targetName) {
267
451
  ];
268
452
 
269
453
  if (exportPatterns.some(pattern => pattern.test(content))) {
270
- return `${pkgName}/${barrelPath}`;
454
+ return `${matchedAlias}/${barrelPath}`;
271
455
  }
272
456
  }
273
457
  } catch {
@@ -316,6 +500,36 @@ async function validateFile(filePath) {
316
500
  // Strip query parameters (Vite asset imports like ?preset=render)
317
501
  const importPath = importPathRaw.split('?')[0];
318
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
+
319
533
  // Check external packages from configured scopes
320
534
  const isExternalPackage = !importPath.startsWith('./') &&
321
535
  !importPath.startsWith('../') &&
@@ -662,6 +876,17 @@ async function validateFile(filePath) {
662
876
  async function main() {
663
877
  console.log('Validating import paths...\n');
664
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
+
665
890
  const files = await findFiles(SRC_DIR);
666
891
  const allErrors = [];
667
892