fix(build): postinstall script patches flowbite-svelte TypeScript optional params

flowbite-svelte ships TypeScript optional-param syntax (x?: string, event?: MouseEvent,
date?: Date) in its dist .svelte files. Vite's esbuild dep pre-bundler processes these
files (via dist/*/index.js re-exports) and fails with "Unexpected '?'" parse errors
whenever the lockfile changes and the dep cache is rebuilt.

- Add scripts/postinstall.mjs: patches 4 specific TypeScript signatures in
  CommandPalette.svelte, ScrollSpy.svelte, and Datepicker.svelte after every npm install.
  Patches are idempotent and targeted — only the ?-in-param-position syntax is removed;
  optional-chaining (?.) in function bodies is untouched.
- Add "postinstall" script to package.json to run it automatically.
- Upgrade flowbite-svelte 1.31.0 → 1.33.1 (both have the same issue; 1.33.1 is current).
- npm update now works without breaking the dev server.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-22 18:33:53 -04:00
parent 99c4934045
commit 9c30d4618a
3 changed files with 436 additions and 315 deletions

72
scripts/postinstall.mjs Normal file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env node
/**
* Postinstall: strip TypeScript optional-param syntax from flowbite-svelte dist files.
*
* flowbite-svelte ships raw .svelte files with `<script lang="ts">` in its dist.
* esbuild (used by Vite's dep pre-bundler) cannot parse TypeScript optional params
* like `(x?: string)` or `event?` in function signatures — it fails with
* "Unexpected '?'" or "Expected ')' but found '?'" errors.
*
* The patches below remove only the TypeScript syntax that causes esbuild to fail.
* The `?.` optional-chaining calls in function bodies are left untouched (those are
* valid JavaScript and handle undefined correctly at runtime).
*
* This runs automatically via `"postinstall": "node scripts/postinstall.mjs"` and
* is safe to re-run — the replacements are idempotent.
*/
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
const root = fileURLToPath(new URL('..', import.meta.url));
const dist = resolve(root, 'node_modules/flowbite-svelte/dist');
if (!existsSync(dist)) {
console.log('postinstall: flowbite-svelte dist not found, skipping.');
process.exit(0);
}
let patched = 0;
function patch(relPath, from, to) {
const file = resolve(dist, relPath);
if (!existsSync(file)) return;
const original = readFileSync(file, 'utf8');
const updated = original.replace(from, to);
if (updated !== original) {
writeFileSync(file, updated, 'utf8');
console.log(` patched: ${relPath}`);
patched++;
}
}
// CommandPalette.svelte: (x?: string) => x?.toLowerCase()...
// Remove optional type annotation from the `check` helper param.
patch(
'command-palette/CommandPalette.svelte',
/\(x\?:\s*string\)/g,
'(x)'
);
// ScrollSpy.svelte: function scrollToSection(itemId: string, event?: MouseEvent)
// Remove type annotations from both params.
patch(
'scroll-spy/ScrollSpy.svelte',
/scrollToSection\(itemId:\s*string,\s*event\?:\s*MouseEvent\)/g,
'scrollToSection(itemId, event)'
);
// Datepicker.svelte: two functions with optional typed params + return-type annotations.
patch(
'datepicker/Datepicker.svelte',
/\(date\?:\s*Date\):\s*string/g,
'(date)'
);
patch(
'datepicker/Datepicker.svelte',
/\(date1\?:\s*Date,\s*date2\?:\s*Date\):\s*boolean/g,
'(date1, date2)'
);
console.log(`postinstall: flowbite-svelte — ${patched} file(s) patched.`);