Skip to content

Commit 4987a04

Browse files
committed
fix(server): resolve esbuild from app dir, not server dir
When `@webjskit/cli` is installed globally (the typical case for end-users), `import('esbuild')` from `packages/server/src/dev.js` walks up from the global install tree — never reaching the user app's `node_modules`. The dev server then served `.ts` files as `/* esbuild missing */` even after `npm i` succeeded in the scaffold. Adds a `loadEsbuild(appDir)` helper that: 1. Resolves `esbuild` from the user's `appDir/package.json` (where the scaffold ships it as a devDep), then 2. Falls back to bare `import('esbuild')` for workspace-linked installs. Bumps `@webjskit/server` 0.1.0 → 0.1.1 and `@webjskit/cli` 0.1.3 → 0.1.4 (cli pin updated to server@0.1.1).
1 parent 5a158e3 commit 4987a04

4 files changed

Lines changed: 74 additions & 14 deletions

File tree

package-lock.json

Lines changed: 39 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@webjskit/cli",
3-
"version": "0.1.3",
3+
"version": "0.1.4",
44
"type": "module",
55
"description": "webjs CLI — dev, start, create, db",
66
"bin": {
@@ -13,7 +13,7 @@
1313
"README.md"
1414
],
1515
"dependencies": {
16-
"@webjskit/server": "0.1.0"
16+
"@webjskit/server": "0.1.1"
1717
},
1818
"publishConfig": {
1919
"access": "public"

packages/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@webjskit/server",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"type": "module",
55
"description": "webjs dev/prod server: SSR, router, API, server actions, live reload",
66
"main": "index.js",

packages/server/src/dev.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ async function handleCore(req, ctx) {
449449
}
450450
// TypeScript source: esbuild-strip types, cache by mtime.
451451
if (/\.m?ts$/.test(abs)) {
452-
return tsResponse(abs, dev);
452+
return tsResponse(abs, dev, appDir);
453453
}
454454
return fileResponse(abs, { dev, immutable: false });
455455
}
@@ -795,14 +795,14 @@ let _superjsonBundle = null;
795795
*/
796796
async function serveBundledSuperjson(appDir, dev) {
797797
if (!_superjsonBundle) {
798-
let build;
799-
try { ({ build } = await import('esbuild')); }
800-
catch {
798+
const esbuildMod = await loadEsbuild(appDir);
799+
if (!esbuildMod) {
801800
return new Response('/* esbuild missing */', {
802801
status: 500,
803802
headers: { 'content-type': 'application/javascript; charset=utf-8' },
804803
});
805804
}
805+
const { build } = esbuildMod;
806806
const entryPoint = locatePackageDir(appDir, 'superjson');
807807
if (!entryPoint) return new Response('superjson not found', { status: 404 });
808808
const result = await build({
@@ -824,15 +824,15 @@ async function serveBundledSuperjson(appDir, dev) {
824824
});
825825
}
826826

827-
async function tsResponse(abs, dev) {
828-
let esbuild;
829-
try { ({ transform: esbuild } = await import('esbuild')); }
830-
catch {
827+
async function tsResponse(abs, dev, appDir) {
828+
const esbuildMod = await loadEsbuild(appDir);
829+
if (!esbuildMod) {
831830
return new Response(
832831
'/* esbuild missing — run `npm i -D esbuild` to enable TypeScript sources */',
833832
{ status: 500, headers: { 'content-type': 'application/javascript; charset=utf-8' } }
834833
);
835834
}
835+
const esbuild = esbuildMod.transform;
836836
const st = await stat(abs);
837837
const cached = TS_CACHE.get(abs);
838838
if (cached && cached.mtimeMs === st.mtimeMs) {
@@ -911,6 +911,30 @@ function locatePackageDir(appDir, pkgName) {
911911
return null;
912912
}
913913

914+
/**
915+
* Load esbuild — preferring the user app's `node_modules` (where the scaffold
916+
* lists it as a devDep). Falls back to the server module's resolution path
917+
* (works for workspace-linked installs). Returns `null` if not found.
918+
*
919+
* Without this two-step lookup, a globally-installed `@webjskit/cli` can't see
920+
* esbuild from the user's app and serves `.ts` files as an "esbuild missing"
921+
* stub even though `npm i` succeeded.
922+
*
923+
* @param {string} appDir
924+
* @returns {Promise<typeof import('esbuild') | null>}
925+
*/
926+
let _esbuild = null;
927+
async function loadEsbuild(appDir) {
928+
if (_esbuild) return _esbuild;
929+
try {
930+
const req = createRequire(join(appDir, 'package.json'));
931+
_esbuild = await import(pathToFileURL(req.resolve('esbuild')).href);
932+
return _esbuild;
933+
} catch {}
934+
try { _esbuild = await import('esbuild'); return _esbuild; } catch {}
935+
return null;
936+
}
937+
914938
const RELOAD_CLIENT_JS = `// webjs dev reload client
915939
const es = new EventSource('/__webjs/events');
916940
es.addEventListener('reload', () => location.reload());

0 commit comments

Comments
 (0)