@hkdigital/lib-core 0.5.61 → 0.5.63

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.
Files changed (39) hide show
  1. package/dist/browser/navigation/navigation.svelte.js +1 -1
  2. package/dist/generic/data/classes/IterableTree.js +1 -1
  3. package/dist/generic/promises/classes/HkPromise.js +1 -1
  4. package/dist/logging/client.d.ts +1 -1
  5. package/dist/logging/client.js +1 -1
  6. package/dist/logging/common.d.ts +1 -1
  7. package/dist/logging/common.js +1 -1
  8. package/dist/logging/internal/factories/client.d.ts +1 -1
  9. package/dist/logging/internal/factories/client.js +1 -1
  10. package/dist/logging/internal/factories/server.d.ts +1 -1
  11. package/dist/logging/internal/factories/server.js +1 -1
  12. package/dist/logging/internal/logger.d.ts +1 -0
  13. package/dist/logging/internal/logger.js +1 -0
  14. package/dist/logging/server.d.ts +1 -1
  15. package/dist/logging/server.js +1 -1
  16. package/dist/ui/components/presenter/ImageSlide.svelte +1 -1
  17. package/dist/ui/components/presenter/Presenter.state.svelte.js +2 -0
  18. package/dist/ui/components/rows/panel-row-2/PanelRow2.svelte +1 -1
  19. package/dist/ui/components/tab-bar/HkTabBar.svelte +1 -1
  20. package/dist/ui/primitives/area.d.ts +2 -0
  21. package/dist/ui/primitives/area.js +2 -0
  22. package/dist/ui/primitives/icons/HkTabIcon.svelte +1 -1
  23. package/dist/ui/primitives/icons/index.d.ts +0 -1
  24. package/dist/ui/primitives/icons/index.js +0 -2
  25. package/dist/util/array/index.js +2 -2
  26. package/dist/util/compare/index.js +1 -1
  27. package/dist/util/expect/compounds.js +1 -1
  28. package/dist/util/expect/objects.js +1 -1
  29. package/dist/util/iterate/index.js +1 -1
  30. package/dist/util/object/index.js +4 -4
  31. package/dist/util/string/interpolate.js +1 -1
  32. package/package.json +5 -1
  33. package/scripts/README.md +279 -0
  34. package/scripts/import-fixes.txt +60 -0
  35. package/scripts/validate-imports.mjs +377 -0
  36. package/dist/logging/internal/logger/index.d.ts +0 -1
  37. package/dist/logging/internal/logger/index.js +0 -1
  38. package/dist/ui/components/index.d.ts +0 -11
  39. package/dist/ui/components/index.js +0 -23
@@ -1,6 +1,6 @@
1
1
  import { onMount } from 'svelte';
2
2
 
3
- import { lazySingleton } from '../../util/singleton/index.js';
3
+ import { lazySingleton } from '../../util/singleton.js';
4
4
 
5
5
  import { beforeNavigate, onNavigate, afterNavigate } from '$app/navigation';
6
6
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as expect from '../../../util/expect.js';
4
4
 
5
- import { PATH_SEPARATOR } from '../../../util/object/index.js';
5
+ import { PATH_SEPARATOR } from '../../../util/object.js';
6
6
 
7
7
  /* ------------------------------------------------------------------ Typedef */
8
8
 
@@ -36,7 +36,7 @@
36
36
 
37
37
  import * as expect from '../../../util/expect.js';
38
38
 
39
- import { noop } from '../../../util/function/index.js';
39
+ import { noop } from '../../../util/function.js';
40
40
 
41
41
  import { PromiseError } from '../../errors.js';
42
42
 
@@ -1,3 +1,3 @@
1
1
  export { createClientLogger } from "./internal/factories/client.js";
2
- export { Logger } from "./internal/logger/index.js";
2
+ export { default as Logger } from "./internal/logger/Logger.js";
3
3
  export * from "./levels.js";
@@ -2,7 +2,7 @@
2
2
  export { createClientLogger } from './internal/factories/client.js';
3
3
 
4
4
  // Logger (for advanced usage)
5
- export { Logger } from './internal/logger/index.js';
5
+ export { default as Logger } from './internal/logger/Logger.js';
6
6
 
7
7
  export * from './levels.js';
8
8
 
@@ -1,2 +1,2 @@
1
1
  export * from "./levels.js";
2
- export { Logger } from "./internal/logger/index.js";
2
+ export { default as Logger } from "./internal/logger/Logger.js";
@@ -2,4 +2,4 @@
2
2
  export * from './levels.js';
3
3
 
4
4
  // Logger (for advanced usage)
5
- export { Logger } from './internal/logger/index.js';
5
+ export { default as Logger } from './internal/logger/Logger.js';
@@ -11,4 +11,4 @@
11
11
  * @returns {Logger} Configured logger instance
12
12
  */
13
13
  export function createClientLogger(name: string, level: import("../../typedef.js").LogLevel, consoleOptions?: Object): Logger;
14
- import { Logger } from '../logger/index.js';
14
+ import { Logger } from '../logger.js';
@@ -1,4 +1,4 @@
1
- import { Logger } from '../logger/index.js';
1
+ import { Logger } from '../logger.js';
2
2
  import { ConsoleAdapter } from '../adapters/console.js';
3
3
  import { DEBUG, LOG, LEVELS } from '../../levels.js';
4
4
 
@@ -11,4 +11,4 @@
11
11
  * @returns {Logger} Configured logger instance
12
12
  */
13
13
  export function createServerLogger(name: string, level: import("../../typedef.js").LogLevel, pinoOptions?: Object): Logger;
14
- import { Logger } from '../logger/index.js';
14
+ import { Logger } from '../logger.js';
@@ -1,4 +1,4 @@
1
- import { Logger } from '../logger/index.js';
1
+ import { Logger } from '../logger.js';
2
2
  import { PinoAdapter } from '../adapters/pino.js';
3
3
  import { LOG, LEVELS } from '../../levels.js';
4
4
 
@@ -0,0 +1 @@
1
+ export { default as Logger } from "./logger/Logger.js";
@@ -0,0 +1 @@
1
+ export { default as Logger } from './logger/Logger.js';
@@ -1,3 +1,3 @@
1
1
  export { createServerLogger } from "./internal/factories/server.js";
2
- export { Logger } from "./internal/logger/index.js";
2
+ export { default as Logger } from "./internal/logger/Logger.js";
3
3
  export * from "./levels.js";
@@ -2,6 +2,6 @@
2
2
  export { createServerLogger } from './internal/factories/server.js';
3
3
 
4
4
  // Logger (for advanced usage)
5
- export { Logger } from './internal/logger/index.js';
5
+ export { default as Logger } from './internal/logger/Logger.js';
6
6
 
7
7
  export * from './levels.js';
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { ImageBox } from '../index.js';
2
+ import { ImageBox } from '../../components.js';
3
3
 
4
4
  /**
5
5
  * @type {{
@@ -291,6 +291,8 @@ export class PresenterState {
291
291
  // Can't transition if we're already transitioning to this slide
292
292
  if (slide.name === this.nextSlideName) {
293
293
  console.debug(`gotoSlide already transitioning to slide [${slide.name}]`);
294
+ // Clear any pending slide since we're already going where the user wants
295
+ this.pendingSlideName = null;
294
296
  return;
295
297
  }
296
298
 
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { PanelGridRow } from '../index.js';
2
+ import PanelGridRow from '../panel-grid-row/PanelGridRow.svelte';
3
3
 
4
4
  /**
5
5
  * @example
@@ -6,7 +6,7 @@
6
6
  * @see https://next.skeleton.dev/docs/resources/contribute/components
7
7
  */
8
8
 
9
- import { createOrGetState } from './HkTabBar.state.svelte';
9
+ import { createOrGetState } from './HkTabBar.state.svelte.js';
10
10
 
11
11
  import { HkGridArea } from '../../primitives.js';
12
12
 
@@ -0,0 +1,2 @@
1
+ export { default as HkArea } from "./area//HkArea.svelte";
2
+ export { default as HkGridArea } from "./area/HkGridArea.svelte";
@@ -0,0 +1,2 @@
1
+ export { default as HkArea } from './area//HkArea.svelte';
2
+ export { default as HkGridArea } from './area/HkGridArea.svelte';
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { onMount } from 'svelte';
12
12
 
13
- import { HkGridArea } from '../area';
13
+ import { HkGridArea } from '../area.js';
14
14
 
15
15
  import HkIcon from './HkIcon.svelte';
16
16
 
@@ -1,4 +1,3 @@
1
1
  export { default as HkIcon } from "./HkIcon.svelte";
2
2
  export { default as HkTabIcon } from "./HkTabIcon.svelte";
3
3
  export { default as SteezeIcon } from "./SteezeIcon.svelte";
4
- export * from "./typedef.js";
@@ -2,5 +2,3 @@ export { default as HkIcon } from './HkIcon.svelte';
2
2
  export { default as HkTabIcon } from './HkTabIcon.svelte';
3
3
 
4
4
  export { default as SteezeIcon } from './SteezeIcon.svelte';
5
-
6
- export * from './typedef.js';
@@ -2,9 +2,9 @@
2
2
 
3
3
  import * as expect from '../expect.js';
4
4
 
5
- import { smallestFirst, largestFirst } from '../compare/index.js';
5
+ import { smallestFirst, largestFirst } from '../compare.js';
6
6
 
7
- import { objectGet, PATH_SEPARATOR } from '../object/index.js';
7
+ import { objectGet, PATH_SEPARATOR } from '../object.js';
8
8
 
9
9
  import { Selector } from '../../generic/data.js';
10
10
 
@@ -1,6 +1,6 @@
1
1
  /* ------------------------------------------------------------------ Imports */
2
2
 
3
- import { objectGet } from '../object/index.js';
3
+ import { objectGet } from '../object.js';
4
4
 
5
5
  /* ------------------------------------------------------------------ Exports */
6
6
 
@@ -1,5 +1,5 @@
1
1
  import * as v from 'valibot';
2
- import * as is from '../is/index.js';
2
+ import * as is from '../is.js';
3
3
 
4
4
  /** Compound validators - combinations and unions of basic validators */
5
5
 
@@ -1,5 +1,5 @@
1
1
  import * as v from 'valibot';
2
- import * as is from '../is/index.js';
2
+ import * as is from '../is.js';
3
3
 
4
4
  /** Internals */
5
5
 
@@ -1,7 +1,7 @@
1
1
  /* ------------------------------------------------------------------ Imports */
2
2
 
3
3
  import * as expect from '../expect.js';
4
- import { smallestFirst, largestFirst } from '../compare/index.js';
4
+ import { smallestFirst, largestFirst } from '../compare.js';
5
5
 
6
6
  import { IterableTree } from '../../generic/data.js';
7
7
 
@@ -2,15 +2,15 @@
2
2
 
3
3
  import * as expect from '../expect.js';
4
4
 
5
- import { equals } from '../compare/index.js';
5
+ import { equals } from '../compare.js';
6
6
 
7
- import { toArrayPath } from '../array/index.js';
7
+ import { toArrayPath } from '../array.js';
8
8
 
9
9
  import { toStringPath } from '../string.js';
10
10
 
11
- import * as is from '../is/index.js';
11
+ import * as is from '../is.js';
12
12
 
13
- import { iterateObjectPaths, iterateObjectEntries } from '../iterate/index.js';
13
+ import { iterateObjectPaths, iterateObjectEntries } from '../iterate.js';
14
14
 
15
15
  // ------------------------------------------------------------------- Internals
16
16
 
@@ -2,7 +2,7 @@ import * as expect from '../expect.js';
2
2
 
3
3
  import { toArrayPath } from '../array/index.js';
4
4
 
5
- import { objectGet, PATH_SEPARATOR } from '../object/index.js';
5
+ import { objectGet, PATH_SEPARATOR } from '../object.js';
6
6
 
7
7
  export const RE_JS_EXPRESSION = /\$\{([^${}]*)\}/g;
8
8
  export const RE_MUSTACHE = /\{\{([^{}]*)\}\}/g;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.61",
3
+ "version": "0.5.63",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"
@@ -41,11 +41,13 @@
41
41
  "cache:clear": "rm -rf node_modules/.cache/imagetools",
42
42
  "lint:prettier": "prettier --check .",
43
43
  "lint:eslint": "eslint .",
44
+ "lint:imports": "node scripts/validate-imports.mjs",
44
45
  "test:unit": "vitest",
45
46
  "test:unit-run": "pnpm run test:unit -- --run",
46
47
  "prepack:sync": "svelte-kit sync",
47
48
  "prepack:build": "svelte-package",
48
49
  "prepack:lint": "publint",
50
+ "prepack:imports": "node scripts/validate-imports.mjs",
49
51
  "publish:npm:version": "npm version patch",
50
52
  "publish:npm:publish": "npm publish --access public",
51
53
  "upgrade:hk:update": "ncu --dep dev,optional,peer,prod '@hkdigital/*' -u",
@@ -54,6 +56,7 @@
54
56
  },
55
57
  "files": [
56
58
  "dist",
59
+ "scripts",
57
60
  "CLAUDE.md",
58
61
  "!dist/**/*.test.*",
59
62
  "!dist/**/*.spec.*",
@@ -102,6 +105,7 @@
102
105
  "@skeletonlabs/skeleton-svelte": "1.3.1",
103
106
  "@steeze-ui/heroicons": "^2.4.2",
104
107
  "@sveltejs/adapter-auto": "^6.1.1",
108
+ "@sveltejs/adapter-node": "^5.4.0",
105
109
  "@sveltejs/kit": "^2.47.0",
106
110
  "@sveltejs/package": "^2.5.4",
107
111
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
@@ -0,0 +1,279 @@
1
+ # Import Path Validation
2
+
3
+ This directory contains validation scripts for enforcing consistent
4
+ import path conventions across the project.
5
+
6
+ ## Overview
7
+
8
+ The `validate-imports.mjs` script checks all JavaScript and Svelte
9
+ files to ensure imports follow project conventions. These rules align
10
+ with how Node.js and Vite resolve modules, ensuring code works both
11
+ during development and when published as a package.
12
+
13
+ ## Running the Linter
14
+
15
+ ```bash
16
+ # Run validation
17
+ node scripts/validate-imports.mjs
18
+
19
+ # Add to package.json scripts
20
+ {
21
+ "scripts": {
22
+ "lint:imports": "node scripts/validate-imports.mjs"
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Import Rules Summary
28
+
29
+ 1. **No cross-domain relative imports** - Use `$lib/` for 3+ levels up
30
+ 2. **Write index.js explicitly** - Don't rely on implicit directory
31
+ resolution (except in test files)
32
+ 3. **Standard extensions optional** - `.js` and `.svelte` can be
33
+ omitted
34
+ 4. **Non-standard extensions required** - `.svelte.js`, `.test.js`,
35
+ etc. must be explicit
36
+ 5. **Import paths must exist** - All imports must resolve to actual
37
+ files
38
+
39
+ ## Import Rules
40
+
41
+ ### Rule 1: No cross-domain relative imports
42
+
43
+ **Don't navigate up 3+ levels with relative paths**
44
+
45
+ When importing from a different domain (different main folder under
46
+ `src/lib/`), use `$lib/` imports instead of long relative paths.
47
+
48
+ ❌ Bad:
49
+ ```javascript
50
+ // In src/lib/ui/components/MyComponent.svelte
51
+ import { http } from '../../../network/http.js';
52
+ ```
53
+
54
+ ✅ Good:
55
+ ```javascript
56
+ // In src/lib/ui/components/MyComponent.svelte
57
+ import { http } from '$lib/network/http.js';
58
+ ```
59
+
60
+ **Why:** Makes imports clearer, easier to refactor, and independent of
61
+ file location.
62
+
63
+ ### Rule 2: Write index.js explicitly (except same-directory)
64
+
65
+ **If importing from a directory, write the index.js explicitly**
66
+
67
+ When a directory contains an `index.js` file, don't rely on implicit
68
+ resolution - write it explicitly.
69
+
70
+ ❌ Bad:
71
+ ```javascript
72
+ // When src/lib/ui/primitives/ has index.js
73
+ import { Button } from '$lib/ui/primitives';
74
+ import { Card } from '../components/cards';
75
+ ```
76
+
77
+ ✅ Good:
78
+ ```javascript
79
+ import { Button } from '$lib/ui/primitives/index.js';
80
+ import { Card } from '../components/cards/index.js';
81
+ ```
82
+
83
+ ✅ Exception - same-directory imports allowed:
84
+ ```javascript
85
+ // In drag-drop/DropZoneArea.svelte
86
+ import { DropZone } from './index.js'; // OK - sibling file
87
+ ```
88
+
89
+ ✅ Exception - test files can import from index.js:
90
+ ```javascript
91
+ // In *.test.js or *.spec.js files
92
+ import { helper } from './index.js'; // OK in tests
93
+ ```
94
+
95
+ **Why:**
96
+ - Makes it explicit that you're importing from a re-export file
97
+ - Same-directory imports are allowed for module cohesion (files
98
+ within the same module can use their local index.js)
99
+ - Parent navigation (`../index.js`) should use cross-domain imports
100
+ or specific files instead
101
+
102
+ ### Rule 3: Standard extensions are optional
103
+
104
+ **Standard extensions (.js, .svelte) can be omitted**
105
+
106
+ Both `$lib/` and relative imports can omit `.js` and `.svelte`
107
+ extensions. Vite and Node.js will resolve them correctly.
108
+
109
+ ✅ All valid:
110
+ ```javascript
111
+ // File exists at src/lib/util/array.js
112
+ import { arraySlice } from '$lib/util/array';
113
+ import { arraySlice } from '$lib/util/array.js';
114
+
115
+ // File exists at ./components/Button.svelte
116
+ import Button from './components/Button';
117
+ import Button from './components/Button.svelte';
118
+ ```
119
+
120
+ **Why:** Vite and Node.js automatically resolve extensions, so
121
+ requiring them is unnecessary for internal code.
122
+
123
+ ### Rule 4: Non-standard extensions must be explicit
124
+
125
+ **Files with non-standard extensions require the full extension**
126
+
127
+ Non-standard extensions like `.svelte.js`, `.test.js`, or `.spec.js`
128
+ are not automatically resolved by module loaders, so they must be
129
+ written explicitly in imports.
130
+
131
+ ❌ Bad:
132
+ ```javascript
133
+ // File is MyComponent.state.svelte.js
134
+ import { state } from './MyComponent.state.svelte';
135
+ import { state } from './MyComponent.state';
136
+ ```
137
+
138
+ ✅ Good:
139
+ ```javascript
140
+ // File is MyComponent.state.svelte.js
141
+ import { state } from './MyComponent.state.svelte.js';
142
+ ```
143
+
144
+ **Why:** Module resolution only tries standard extensions (.js,
145
+ .svelte). Non-standard compound extensions won't be found
146
+ automatically and will cause import errors.
147
+
148
+ ### Rule 5: Import paths must exist
149
+
150
+ **All import paths must resolve to actual files**
151
+
152
+ The linter verifies that every internal import (`$lib/`, `./`, `../`)
153
+ points to a file that exists on the filesystem. This catches typos,
154
+ moved/deleted files, and incorrect paths early.
155
+
156
+ ❌ Bad:
157
+ ```javascript
158
+ // File doesn't exist
159
+ import { helper } from './utlis.js'; // Typo: should be "utils"
160
+ import Button from '$lib/ui/Buttton.svelte'; // Typo in filename
161
+ import { old } from './deleted-file.js'; // File was removed
162
+ ```
163
+
164
+ ✅ Good:
165
+ ```javascript
166
+ // All files exist
167
+ import { helper } from './utils.js';
168
+ import Button from '$lib/ui/Button.svelte';
169
+ import { current } from './existing-file.js';
170
+ ```
171
+
172
+ **Special handling:**
173
+ - **Query parameters**: Vite asset imports with query strings (e.g.,
174
+ `'./image.jpg?preset=render'`) are supported - the query string is
175
+ stripped before checking file existence
176
+ - **Resolution order**: Follows Node.js/Vite resolution (tries `.js`,
177
+ `.svelte`, `.svelte.js`, `/index.js` for extensionless imports)
178
+
179
+ **Why:** Catches broken imports immediately instead of discovering
180
+ them at build time or runtime. Helps prevent errors when refactoring
181
+ or moving files.
182
+
183
+ ## How Module Resolution Works
184
+
185
+ Understanding how Node.js and Vite resolve imports helps explain
186
+ these rules:
187
+
188
+ ### Resolution order for `import { x } from './foo'`
189
+
190
+ 1. Try `./foo` (exact match)
191
+ 2. Try `./foo.js`
192
+ 3. Try `./foo.svelte`
193
+ 4. Try `./foo/index.js`
194
+ 5. Try `./foo/index.svelte`
195
+
196
+ ### Examples
197
+
198
+ ```javascript
199
+ // Import: from './components/things'
200
+ // Resolution:
201
+ // 1. ./components/things (not found)
202
+ // 2. ./components/things.js (✓ found!) → uses this file
203
+ // 3. Never checks ./components/things/index.js
204
+
205
+ // Import: from './components/cards'
206
+ // Resolution:
207
+ // 1. ./components/cards (not found)
208
+ // 2. ./components/cards.js (not found)
209
+ // 3. ./components/cards.svelte (not found)
210
+ // 4. ./components/cards/index.js (✓ found!) → uses this file
211
+ // → Should write './components/cards/index.js' explicitly
212
+ ```
213
+
214
+ ## Directory imports vs file imports
215
+
216
+ The linter follows Node.js/Vite resolution order and only flags
217
+ directory imports when they actually resolve to a directory's
218
+ index.js:
219
+
220
+ ✅ No warning - imports a file:
221
+ ```javascript
222
+ // src/lib/util/array.js exists (file)
223
+ import { arraySlice } from '$lib/util/array';
224
+ ```
225
+
226
+ ⚠️ Warning - imports a directory's index.js:
227
+ ```javascript
228
+ // src/lib/ui/primitives/ directory with index.js
229
+ import { Button } from '$lib/ui/primitives';
230
+ // Should be: from '$lib/ui/primitives/index.js'
231
+ ```
232
+
233
+ ### Edge case: file and directory with same name
234
+
235
+ When both a file and directory exist with the same base name, the
236
+ file is always resolved first:
237
+
238
+ ```javascript
239
+ // src/lib/util/env.js (file) exists
240
+ // src/lib/util/env/index.js (directory) also exists
241
+ import { isTestEnv } from '$lib/util/env';
242
+ // ✅ Resolves to env.js, no warning
243
+ ```
244
+
245
+ The linter correctly follows this resolution order.
246
+
247
+ ## Test file exceptions
248
+
249
+ Test files (*.test.js, *.spec.js) are exempt from Rule 2 because
250
+ they test the public API. It's natural for tests to import from
251
+ `./index.js` alongside the code being tested.
252
+
253
+ ```javascript
254
+ // In Button.test.js
255
+ import { Button } from './index.js'; // ✅ OK in test files
256
+ ```
257
+
258
+ ## External package imports
259
+
260
+ The linter only checks internal imports (`$lib/`, `./`, `../`).
261
+ External package imports are not validated:
262
+
263
+ ```javascript
264
+ import { derived } from 'svelte/store'; // Not checked
265
+ import { v } from 'valibot'; // Not checked
266
+ ```
267
+
268
+ ## Implementation details
269
+
270
+ The validator:
271
+ 1. Scans all `.js` and `.svelte` files in `src/`
272
+ 2. Parses import statements using regex
273
+ 3. Resolves paths to filesystem locations
274
+ 4. Checks if directories contain `index.js` files
275
+ 5. Reports violations with file:line references
276
+
277
+ Exit codes:
278
+ - `0` - All imports valid
279
+ - `1` - Violations found or script error
@@ -0,0 +1,60 @@
1
+ src/lib/ui/components/rows/panel-row-2/PanelRow2.svelte:2 - Parent index.js import (use $lib/ or import specific file)
2
+ import { PanelGridRow } from '../index.js';
3
+ import { PanelGridRow } from '../index.js'; // TODO: Import specific file or refactor
4
+
5
+ src/lib/ui/components/tab-bar/HkTabBar.svelte:9 - Missing non-standard extension (use './HkTabBar.state.svelte.js')
6
+ import { createOrGetState } from './HkTabBar.state.svelte';
7
+ import { createOrGetState } from './HkTabBar.state.svelte.js';
8
+
9
+ src/lib/ui/primitives/icons/HkTabIcon.svelte:13 - Directory import (write explicitly: '../area/index.js')
10
+ import { HkGridArea } from '../area';
11
+ import { HkGridArea } from '../area/index.js';
12
+
13
+ src/lib/util/array/index.js:5 - Parent index.js import (use $lib/ or import specific file)
14
+ import { smallestFirst, largestFirst } from '../compare/index.js';
15
+ import { smallestFirst, largestFirst } from '../compare.js';
16
+
17
+ src/lib/util/array/index.js:7 - Parent index.js import (use $lib/ or import specific file)
18
+ import { objectGet, PATH_SEPARATOR } from '../object/index.js';
19
+ import { objectGet, PATH_SEPARATOR } from '../object.js';
20
+
21
+ src/lib/util/compare/index.js:3 - Parent index.js import (use $lib/ or import specific file)
22
+ import { objectGet } from '../object/index.js';
23
+ import { objectGet } from '../object.js';
24
+
25
+ src/lib/util/expect/compounds.js:2 - Parent index.js import (use $lib/ or import specific file)
26
+ import * as is from '../is/index.js';
27
+ import * as is from '../is.js';
28
+
29
+ src/lib/util/expect/objects.js:2 - Parent index.js import (use $lib/ or import specific file)
30
+ import * as is from '../is/index.js';
31
+ import * as is from '../is.js';
32
+
33
+ src/lib/util/iterate/index.js:4 - Parent index.js import (use $lib/ or import specific file)
34
+ import { smallestFirst, largestFirst } from '../compare/index.js';
35
+ import { smallestFirst, largestFirst } from '../compare.js';
36
+
37
+ src/lib/util/object/index.js:5 - Parent index.js import (use $lib/ or import specific file)
38
+ import { equals } from '../compare/index.js';
39
+ import { equals } from '../compare.js';
40
+
41
+ src/lib/util/object/index.js:7 - Parent index.js import (use $lib/ or import specific file)
42
+ import { toArrayPath } from '../array/index.js';
43
+ import { toArrayPath } from '../array.js';
44
+
45
+ src/lib/util/object/index.js:11 - Parent index.js import (use $lib/ or import specific file)
46
+ import * as is from '../is/index.js';
47
+ import * as is from '../is.js';
48
+
49
+ src/lib/util/object/index.js:13 - Parent index.js import (use $lib/ or import specific file)
50
+ import { iterateObjectPaths, iterateObjectEntries } from '../iterate/index.js';
51
+ import { iterateObjectPaths, iterateObjectEntries } from '../iterate.js';
52
+
53
+ src/lib/util/string/interpolate.js:5 - Parent index.js import (use $lib/ or import specific file)
54
+ import { objectGet, PATH_SEPARATOR } from '../object/index.js';
55
+ import { objectGet, PATH_SEPARATOR } from '../object.js';
56
+
57
+ src/routes/explorer/[...path]/+page.svelte:4 - Parent index.js import (use $lib/ or import specific file)
58
+ import { TopBar, Explorer } from '../components/index.js';
59
+ import { TopBar, Explorer } from '../components.js';
60
+
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readdir, readFile, stat } from 'node:fs/promises';
4
+ import { join, relative, resolve, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
+ const PROJECT_ROOT = join(__dirname, '..');
9
+ const SRC_DIR = join(PROJECT_ROOT, 'src');
10
+
11
+ /**
12
+ * Find all JS and Svelte files recursively
13
+ *
14
+ * @param {string} dir - Directory to search
15
+ *
16
+ * @returns {Promise<string[]>} Array of file paths
17
+ */
18
+ async function findFiles(dir) {
19
+ const files = [];
20
+ const entries = await readdir(dir, { withFileTypes: true });
21
+
22
+ for (const entry of entries) {
23
+ const fullPath = join(dir, entry.name);
24
+ if (entry.isDirectory()) {
25
+ if (entry.name !== 'node_modules' && entry.name !== '.svelte-kit') {
26
+ files.push(...await findFiles(fullPath));
27
+ }
28
+ } else if (entry.name.endsWith('.js') || entry.name.endsWith('.svelte')) {
29
+ files.push(fullPath);
30
+ }
31
+ }
32
+
33
+ return files;
34
+ }
35
+
36
+ /**
37
+ * Check if import path resolves to directory with index.js
38
+ *
39
+ * Follows Node.js/Vite resolution order:
40
+ * 1. Check for file with .js extension
41
+ * 2. Check for file with .svelte extension
42
+ * 3. Check for directory with index.js
43
+ *
44
+ * @param {string} fsPath - Filesystem path to check (without extension)
45
+ *
46
+ * @returns {Promise<boolean>} True if resolves to directory with index.js
47
+ */
48
+ async function isDirectoryWithIndex(fsPath) {
49
+ // First check if a file exists (files resolve before directories)
50
+ try {
51
+ const jsStats = await stat(fsPath + '.js');
52
+ if (jsStats.isFile()) {
53
+ return false; // Resolves to file, not directory
54
+ }
55
+ } catch {
56
+ // .js file doesn't exist, continue checking
57
+ }
58
+
59
+ try {
60
+ const svelteStats = await stat(fsPath + '.svelte');
61
+ if (svelteStats.isFile()) {
62
+ return false; // Resolves to file, not directory
63
+ }
64
+ } catch {
65
+ // .svelte file doesn't exist, continue checking
66
+ }
67
+
68
+ // Now check if it's a directory with index.js
69
+ try {
70
+ const dirStats = await stat(fsPath);
71
+ if (dirStats.isDirectory()) {
72
+ try {
73
+ await stat(join(fsPath, 'index.js'));
74
+ return true; // Directory with index.js
75
+ } catch {
76
+ return false; // Directory but no index.js
77
+ }
78
+ }
79
+ } catch {
80
+ // Path doesn't exist at all
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * Validate import paths in a file
88
+ *
89
+ * @param {string} filePath - Path to the file
90
+ *
91
+ * @returns {Promise<string[]>} Array of error messages
92
+ */
93
+ async function validateFile(filePath) {
94
+ const content = await readFile(filePath, 'utf-8');
95
+ const errors = [];
96
+ const relativePath = relative(PROJECT_ROOT, filePath);
97
+ const isTestFile = filePath.endsWith('.test.js') ||
98
+ filePath.endsWith('.spec.js');
99
+
100
+ // Check each line for import statements
101
+ const lines = content.split('\n');
102
+ for (let index = 0; index < lines.length; index++) {
103
+ const line = lines[index];
104
+ const lineNum = index + 1;
105
+
106
+ // Skip if not an import line
107
+ if (!line.trim().startsWith('import ')) {
108
+ continue;
109
+ }
110
+
111
+ // Extract import path from line
112
+ const importMatch = line.match(/from ['"]([^'"]+)['"]/);
113
+ if (!importMatch) {
114
+ continue;
115
+ }
116
+
117
+ const importPathRaw = importMatch[1];
118
+
119
+ // Strip query parameters (Vite asset imports like ?preset=render)
120
+ const importPath = importPathRaw.split('?')[0];
121
+
122
+ // Skip external packages (no ./ or $lib prefix)
123
+ if (!importPath.startsWith('./') &&
124
+ !importPath.startsWith('../') &&
125
+ !importPath.startsWith('$lib/')) {
126
+ continue;
127
+ }
128
+
129
+ // Check 1: Cross-domain relative imports (3+ levels up)
130
+ if (importPath.match(/^\.\.\/\.\.\/\.\.\//)) {
131
+ errors.push(
132
+ `${relativePath}:${lineNum} - Cross-domain relative import ` +
133
+ `(use $lib/ instead)`
134
+ );
135
+ continue;
136
+ }
137
+
138
+ // Check 2: Local index.js imports (skip for test files)
139
+ // Allow ./index.js and ./subfolder/index.js but flag parent navigation
140
+ if (!isTestFile && importPath.match(/\/index\.js$/)) {
141
+ // Allow same-directory and child directory imports
142
+ // Examples: ./index.js, ./subfolder/index.js, ./(meta)/index.js
143
+ // Flag parent navigation: ../index.js, ../../index.js
144
+ const isParentNavigation = importPath.match(/^\.\..*\/index\.js$/);
145
+
146
+ if (isParentNavigation) {
147
+ errors.push(
148
+ `${relativePath}:${lineNum} - Parent index.js import ` +
149
+ `(use $lib/ or import specific file)`
150
+ );
151
+ continue;
152
+ }
153
+ }
154
+
155
+ // Resolve to filesystem path
156
+ let fsPath;
157
+ if (importPath.startsWith('$lib/')) {
158
+ fsPath = join(PROJECT_ROOT, importPath.replace('$lib/', 'src/lib/'));
159
+ } else {
160
+ fsPath = resolve(dirname(filePath), importPath);
161
+ }
162
+
163
+ // Check 3: Non-standard extensions (must be explicit)
164
+ // Files like .svelte.js need full extension in import
165
+ // Examples:
166
+ // import './Button.svelte' when Button.svelte.js exists → error
167
+ // import './Button' when Button.svelte.js exists → error
168
+ // import './Button.svelte.js' → correct!
169
+
170
+ const hasFullNonStandardExt = importPath.match(/\.(svelte\.js|test\.js|spec\.js)$/);
171
+
172
+ if (!hasFullNonStandardExt) {
173
+ // First check if file resolves with standard extension
174
+ let fileExistsWithStandardExt = false;
175
+
176
+ // Check .js
177
+ if (importPath.endsWith('.js')) {
178
+ try {
179
+ const stats = await stat(fsPath);
180
+ if (stats.isFile()) {
181
+ fileExistsWithStandardExt = true;
182
+ }
183
+ } catch {
184
+ // Doesn't exist
185
+ }
186
+ }
187
+
188
+ // Check .svelte
189
+ if (!fileExistsWithStandardExt && importPath.endsWith('.svelte')) {
190
+ try {
191
+ const stats = await stat(fsPath);
192
+ if (stats.isFile()) {
193
+ fileExistsWithStandardExt = true;
194
+ }
195
+ } catch {
196
+ // Doesn't exist
197
+ }
198
+ }
199
+
200
+ // Check implicit .js (no extension in import)
201
+ if (!fileExistsWithStandardExt &&
202
+ !importPath.endsWith('.js') &&
203
+ !importPath.endsWith('.svelte')) {
204
+ try {
205
+ const stats = await stat(fsPath + '.js');
206
+ if (stats.isFile()) {
207
+ fileExistsWithStandardExt = true;
208
+ }
209
+ } catch {
210
+ // Doesn't exist
211
+ }
212
+ }
213
+
214
+ // Check implicit .svelte (no extension in import)
215
+ if (!fileExistsWithStandardExt &&
216
+ !importPath.endsWith('.js') &&
217
+ !importPath.endsWith('.svelte')) {
218
+ try {
219
+ const stats = await stat(fsPath + '.svelte');
220
+ if (stats.isFile()) {
221
+ fileExistsWithStandardExt = true;
222
+ }
223
+ } catch {
224
+ // Doesn't exist
225
+ }
226
+ }
227
+
228
+ // Only check for non-standard extensions if standard doesn't exist
229
+ if (!fileExistsWithStandardExt) {
230
+ // Remove any standard extension from import path
231
+ const baseImportPath = importPath
232
+ .replace(/\.js$/, '')
233
+ .replace(/\.svelte$/, '');
234
+
235
+ let baseFsPath;
236
+ if (baseImportPath.startsWith('$lib/')) {
237
+ baseFsPath = join(
238
+ PROJECT_ROOT,
239
+ baseImportPath.replace('$lib/', 'src/lib/')
240
+ );
241
+ } else if (baseImportPath.startsWith('./') ||
242
+ baseImportPath.startsWith('../')) {
243
+ baseFsPath = resolve(dirname(filePath), baseImportPath);
244
+ } else {
245
+ baseFsPath = fsPath;
246
+ }
247
+
248
+ // Check for files with non-standard extensions
249
+ const nonStandardExts = ['.svelte.js', '.test.js', '.spec.js'];
250
+ let foundNonStandard = false;
251
+
252
+ for (const ext of nonStandardExts) {
253
+ try {
254
+ const testPath = baseFsPath + ext;
255
+ const stats = await stat(testPath);
256
+ if (stats.isFile()) {
257
+ const correctImport = baseImportPath + ext;
258
+ errors.push(
259
+ `${relativePath}:${lineNum} - Missing non-standard ` +
260
+ `extension (use '${correctImport}')`
261
+ );
262
+ foundNonStandard = true;
263
+ break;
264
+ }
265
+ } catch {
266
+ // File doesn't exist with this extension, continue checking
267
+ }
268
+ }
269
+
270
+ // Skip remaining checks if we found a non-standard extension issue
271
+ if (foundNonStandard) {
272
+ continue;
273
+ }
274
+ }
275
+ }
276
+
277
+ // Check 4: Directory imports (when directory has index.js)
278
+ // Only check if import doesn't have extension (extensionless imports)
279
+ const hasAnyExtension = importPath.match(/\.(js|svelte)$/) ||
280
+ importPath.match(/\.(svelte\.js|test\.js|spec\.js)$/);
281
+
282
+ if (!hasAnyExtension) {
283
+ // Check if it's a directory with index.js
284
+ if (await isDirectoryWithIndex(fsPath)) {
285
+ // Don't suggest ../path/index.js (parent navigation)
286
+ // Suggest creating export file instead
287
+ const wouldBeParentNavigation = importPath.match(/^\.\.\//);
288
+
289
+ if (wouldBeParentNavigation) {
290
+ errors.push(
291
+ `${relativePath}:${lineNum} - Directory import requires ` +
292
+ `parent navigation (create export file like '${importPath}.js' ` +
293
+ `or import specific file)`
294
+ );
295
+ continue;
296
+ }
297
+
298
+ const suggestion = importPath.endsWith('/') ?
299
+ `${importPath}index.js` : `${importPath}/index.js`;
300
+ errors.push(
301
+ `${relativePath}:${lineNum} - Directory import ` +
302
+ `(write explicitly: '${suggestion}')`
303
+ );
304
+ continue;
305
+ }
306
+ }
307
+
308
+ // Check 5: File existence (after all other checks)
309
+ // Verify that the import path resolves to an existing file
310
+ // Follow Node.js/Vite resolution order
311
+ let fileExists = false;
312
+ const possiblePaths = [];
313
+
314
+ // If import has extension, check exact path
315
+ if (importPath.match(/\.(js|svelte|svelte\.js|test\.js|spec\.js)$/)) {
316
+ possiblePaths.push(fsPath);
317
+ } else {
318
+ // No extension, try in resolution order
319
+ possiblePaths.push(fsPath); // Exact match
320
+ possiblePaths.push(fsPath + '.js');
321
+ possiblePaths.push(fsPath + '.svelte');
322
+ possiblePaths.push(fsPath + '.svelte.js');
323
+ possiblePaths.push(fsPath + '/index.js');
324
+ possiblePaths.push(fsPath + '/index.svelte');
325
+ }
326
+
327
+ for (const testPath of possiblePaths) {
328
+ try {
329
+ const stats = await stat(testPath);
330
+ if (stats.isFile()) {
331
+ fileExists = true;
332
+ break;
333
+ }
334
+ } catch {
335
+ // File doesn't exist, continue checking
336
+ }
337
+ }
338
+
339
+ if (!fileExists) {
340
+ errors.push(
341
+ `${relativePath}:${lineNum} - Import path does not exist: ` +
342
+ `'${importPath}'`
343
+ );
344
+ }
345
+ }
346
+
347
+ return errors;
348
+ }
349
+
350
+ /**
351
+ * Main validation function
352
+ */
353
+ async function main() {
354
+ console.log('Validating import paths...\n');
355
+
356
+ const files = await findFiles(SRC_DIR);
357
+ const allErrors = [];
358
+
359
+ for (const file of files) {
360
+ const errors = await validateFile(file);
361
+ allErrors.push(...errors);
362
+ }
363
+
364
+ if (allErrors.length > 0) {
365
+ console.error('❌ Found import path violations:\n');
366
+ allErrors.forEach(error => console.error(` ${error}`));
367
+ console.error(`\n${allErrors.length} error(s) found.`);
368
+ process.exit(1);
369
+ }
370
+
371
+ console.log('✅ All import paths are valid!');
372
+ }
373
+
374
+ main().catch(error => {
375
+ console.error('Validation script error:', error);
376
+ process.exit(1);
377
+ });
@@ -1 +0,0 @@
1
- export { default as Logger } from "./Logger.js";
@@ -1 +0,0 @@
1
- export { default as Logger } from './Logger.js';
@@ -1,11 +0,0 @@
1
- export { default as ButtonGroup } from "./button-group/ButtonGroup.svelte";
2
- export { default as CompareLeftRight } from "./compare-left-right/CompareLeftRight.svelte";
3
- export { default as GameBox } from "./game-box/GameBox.svelte";
4
- export { default as HkAppLayout } from "./hk-app-layout/HkAppLayout.svelte";
5
- export { default as ImageBox } from "./image-box/ImageBox.svelte";
6
- export { default as Presenter } from "./presenter/Presenter.svelte";
7
- export { default as ImageSlide } from "./presenter/ImageSlide.svelte";
8
- export { default as VirtualViewport } from "./virtual-viewport/VirtualViewport.svelte";
9
- export * from "./button-group/typedef.js";
10
- export * from "./presenter/typedef.js";
11
- export { createOrGetState as createOrGetAppLayoutState, createState as createAppLayoutState, getState as getAppLayoutState } from "./hk-app-layout/HkAppLayout.state.svelte.js";
@@ -1,23 +0,0 @@
1
- export { default as ButtonGroup } from './button-group/ButtonGroup.svelte';
2
- export { default as CompareLeftRight } from './compare-left-right/CompareLeftRight.svelte';
3
- export { default as GameBox } from './game-box/GameBox.svelte';
4
-
5
- export {
6
- createOrGetState as createOrGetAppLayoutState,
7
- createState as createAppLayoutState,
8
- getState as getAppLayoutState
9
- } from './hk-app-layout/HkAppLayout.state.svelte.js';
10
-
11
- export { default as HkAppLayout } from './hk-app-layout/HkAppLayout.svelte';
12
-
13
- export { default as ImageBox } from './image-box/ImageBox.svelte';
14
-
15
- export { default as Presenter } from './presenter/Presenter.svelte';
16
- export { default as ImageSlide } from './presenter/ImageSlide.svelte';
17
-
18
- export { default as VirtualViewport } from './virtual-viewport/VirtualViewport.svelte';
19
-
20
- // > Types
21
-
22
- export * from './button-group/typedef.js';
23
- export * from './presenter/typedef.js';