diff --git a/package.json b/package.json
index 5c97c9bb..77841577 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"prepack": "npx svelte-package",
"release-next": "npm version prerelease --preid next && npm publish && git push && git push --tags && sleep 1 && npm dist-tag add svelteplot@$(npm view . version) next",
"docs": "npm run build && cd build && rsync --recursive . vis4.net:svelteplot/alpha0/",
- "screenshots": "node screenshot-examples.js"
+ "screenshots": "node screenshot-examples.js",
+ "check-js-extensions": "node scripts/check-js-extensions.js src"
},
"exports": {
".": {
diff --git a/scripts/check-js-extensions.js b/scripts/check-js-extensions.js
new file mode 100755
index 00000000..4ea9db11
--- /dev/null
+++ b/scripts/check-js-extensions.js
@@ -0,0 +1,138 @@
+#!/usr/bin/env node
+/* eslint-disable no-console */
+
+/**
+ * This script checks for missing .js extensions in import statements.
+ * It helps identify issues with ESM imports where TypeScript requires .js extensions.
+ */
+
+import { readFile, readdir, stat } from 'fs/promises';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+// Convert file:// URLs to paths
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+// Regular expressions to match import statements without .js extensions
+const regexImportFrom =
+ /import\s+(?:type\s+)?(?:{[^}]*}|\*\s+as\s+[^;]*|[^;{]*)\s+from\s+['"]([^'"]*)['"]/g;
+const regexExportFrom =
+ /export\s+(?:type\s+)?(?:{[^}]*}|\*\s+as\s+[^;]*)\s+from\s+['"]([^'"]*)['"]/g;
+
+// Skip node_modules and build directories
+const excludedDirs = ['node_modules', 'build', '.svelte-kit', 'dist', '.git', 'examples', 'tests'];
+
+// Only check certain file types
+const includedExtensions = ['.ts', '.js', '.svelte'];
+
+// Paths that should have .js extensions (relative paths and alias paths)
+const shouldHaveJsExtension = (importPath) => {
+ // Skip Svelte imports
+ if (importPath.endsWith('.svelte')) return false;
+
+ // Skip npm package imports (those that don't start with . or /)
+ if (
+ !importPath.startsWith('.') &&
+ !importPath.startsWith('/') &&
+ !importPath.startsWith('$lib')
+ )
+ return false;
+
+ // Skip imports with extensions already
+ if (path.extname(importPath)) return false;
+
+ return true;
+};
+
+async function* walkDirectory(dir) {
+ const entries = await readdir(dir, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+
+ if (entry.isDirectory()) {
+ if (!excludedDirs.includes(entry.name)) {
+ yield* walkDirectory(fullPath);
+ }
+ } else if (includedExtensions.includes(path.extname(entry.name))) {
+ yield fullPath;
+ }
+ }
+}
+
+async function checkFile(filePath) {
+ const content = await readFile(filePath, 'utf8');
+ const issues = [];
+
+ // Find all import statements
+ let match;
+
+ // Check import statements
+ regexImportFrom.lastIndex = 0;
+ while ((match = regexImportFrom.exec(content)) !== null) {
+ const importPath = match[1];
+ if (shouldHaveJsExtension(importPath)) {
+ issues.push({
+ line: content.substring(0, match.index).split('\n').length,
+ importPath,
+ statement: match[0]
+ });
+ }
+ }
+
+ // Check export from statements
+ regexExportFrom.lastIndex = 0;
+ while ((match = regexExportFrom.exec(content)) !== null) {
+ const importPath = match[1];
+ if (shouldHaveJsExtension(importPath)) {
+ issues.push({
+ line: content.substring(0, match.index).split('\n').length,
+ importPath,
+ statement: match[0]
+ });
+ }
+ }
+
+ return { filePath, issues };
+}
+
+async function main() {
+ const rootDir = process.argv[2] || process.cwd();
+ console.log(`Checking for missing .js extensions in ${rootDir}...\n`);
+
+ let totalIssues = 0;
+ let filesWithIssues = 0;
+
+ for await (const filePath of walkDirectory(rootDir)) {
+ const { issues } = await checkFile(filePath);
+
+ if (issues.length > 0) {
+ console.log(`\x1b[33m${filePath}\x1b[0m`);
+ filesWithIssues++;
+
+ for (const issue of issues) {
+ totalIssues++;
+ console.log(
+ ` Line ${issue.line}: Missing .js extension in import: ${issue.importPath}`
+ );
+ console.log(` ${issue.statement}`);
+ }
+ console.log('');
+ }
+ }
+
+ if (totalIssues === 0) {
+ console.log('\x1b[32mNo missing .js extensions found!\x1b[0m');
+ } else {
+ console.log(
+ `\x1b[31mFound ${totalIssues} missing .js extensions in ${filesWithIssues} files.\x1b[0m`
+ );
+ process.exit(1);
+ }
+}
+
+main().catch((err) => {
+ console.error('Error:', err);
+ process.exit(1);
+});
diff --git a/src/lib/Plot.svelte b/src/lib/Plot.svelte
index 840120b0..2db722c5 100644
--- a/src/lib/Plot.svelte
+++ b/src/lib/Plot.svelte
@@ -12,7 +12,7 @@
+
+
+
+
+
+ d < 0 ? 'negative' : 'positive'} />
+
+
+
diff --git a/src/routes/examples/axis/ticks-inside.svelte b/src/routes/examples/axis/ticks-inside.svelte
new file mode 100644
index 00000000..43faecac
--- /dev/null
+++ b/src/routes/examples/axis/ticks-inside.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/src/routes/features/markers/+page.md b/src/routes/features/markers/+page.md
index 87f690a3..a4c724fa 100644
--- a/src/routes/features/markers/+page.md
+++ b/src/routes/features/markers/+page.md
@@ -88,7 +88,7 @@ Note that for the interpolation methods `basis`, `bundle`, and `step`, the marke
import { Plot, LineY, Dot } from 'svelteplot';
import Slider from '$lib/ui/Slider.svelte';
import Select from '$lib/ui/Select.svelte';
- import type { CurveName } from '$lib/types.js';
+ import type { CurveName } from '$lib/types/index.js';
// curve demo
const numbers = [
@@ -134,7 +134,7 @@ You can also specify a custom marker icon using the `marker` snippet:
```svelte live
diff --git a/src/routes/marks/line/CO2Decades.svelte b/src/routes/marks/line/CO2Decades.svelte
index dd364b87..e16f6904 100644
--- a/src/routes/marks/line/CO2Decades.svelte
+++ b/src/routes/marks/line/CO2Decades.svelte
@@ -1,5 +1,5 @@
diff --git a/src/routes/transforms/interval/+page.md b/src/routes/transforms/interval/+page.md
index 9914781f..1c205b83 100644
--- a/src/routes/transforms/interval/+page.md
+++ b/src/routes/transforms/interval/+page.md
@@ -33,7 +33,7 @@ In contrast, a [rectY](/marks/rect) mark with the interval option and the day in
```svelte live
diff --git a/src/tests/dot.test.svelte b/src/tests/dot.test.svelte
index 07762711..a892a88d 100644
--- a/src/tests/dot.test.svelte
+++ b/src/tests/dot.test.svelte
@@ -1,5 +1,5 @@