bimba-cli 0.7.1 → 0.7.3
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.
- package/package.json +1 -1
- package/serve.js +51 -30
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -524,7 +524,9 @@ function resolveEntry(depPkg) {
|
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
// Build an ES import map from package.json dependencies.
|
|
527
|
-
//
|
|
527
|
+
// The import map is intentionally simple — it just maps bare specifiers
|
|
528
|
+
// to /node_modules/ URLs. All the smart resolution (conditional exports,
|
|
529
|
+
// CJS→ESM, entry points, extensions) happens on the server side.
|
|
528
530
|
async function buildImportMap() {
|
|
529
531
|
const imports = {
|
|
530
532
|
'imba/runtime': 'https://esm.sh/imba/runtime',
|
|
@@ -534,26 +536,8 @@ async function buildImportMap() {
|
|
|
534
536
|
const pkg = JSON.parse(await Bun.file('./package.json').text());
|
|
535
537
|
for (const [name] of Object.entries(pkg.dependencies || {})) {
|
|
536
538
|
if (name === 'imba') continue;
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const entry = resolveEntry(depPkg);
|
|
540
|
-
if (entry && !entry.startsWith('http')) {
|
|
541
|
-
// Local package — serve from node_modules
|
|
542
|
-
imports[name] = `/node_modules/${name}/${entry}`;
|
|
543
|
-
// Always add trailing-slash mapping for deep/subpath imports
|
|
544
|
-
// (e.g. highlight.js/lib/languages/javascript). The browser
|
|
545
|
-
// doesn't enforce Node's `exports` restrictions, it just needs
|
|
546
|
-
// a URL prefix to resolve bare specifiers like "pkg/sub/path".
|
|
547
|
-
imports[name + '/'] = `/node_modules/${name}/`;
|
|
548
|
-
} else {
|
|
549
|
-
// No resolvable entry — use esm.sh to auto-wrap
|
|
550
|
-
imports[name] = `https://esm.sh/${name}`;
|
|
551
|
-
imports[name + '/'] = `https://esm.sh/${name}/`;
|
|
552
|
-
}
|
|
553
|
-
} catch(_) {
|
|
554
|
-
imports[name] = `https://esm.sh/${name}`;
|
|
555
|
-
imports[name + '/'] = `https://esm.sh/${name}/`;
|
|
556
|
-
}
|
|
539
|
+
imports[name] = `/node_modules/${name}/`;
|
|
540
|
+
imports[name + '/'] = `/node_modules/${name}/`;
|
|
557
541
|
}
|
|
558
542
|
} catch(_) { /* no package.json */ }
|
|
559
543
|
|
|
@@ -766,16 +750,49 @@ export function serve(entrypoint, flags) {
|
|
|
766
750
|
}
|
|
767
751
|
}
|
|
768
752
|
|
|
769
|
-
// node_modules:
|
|
770
|
-
//
|
|
753
|
+
// node_modules: all smart resolution happens here.
|
|
754
|
+
// The import map just maps bare specifiers to /node_modules/pkg/ URLs.
|
|
755
|
+
// This block handles: entry point resolution, conditional exports
|
|
756
|
+
// (CJS→ESM), subpath resolution, .imba compilation.
|
|
771
757
|
if (pathname.startsWith('/node_modules/')) {
|
|
772
758
|
const filepath = '.' + pathname
|
|
759
|
+
|
|
760
|
+
// Serve a resolved file — compile .imba on the fly, pass JS through
|
|
761
|
+
const serveResolved = async (filePath) => {
|
|
762
|
+
if (filePath.endsWith('.imba')) {
|
|
763
|
+
const out = await compileFile(filePath)
|
|
764
|
+
if (out.errors?.length) return new Response(out.errors.map(e => e.message).join('\n'), { status: 500 })
|
|
765
|
+
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
766
|
+
}
|
|
767
|
+
const f = Bun.file(filePath)
|
|
768
|
+
if (await f.exists()) return new Response(f, { headers: { 'Content-Type': 'application/javascript' } })
|
|
769
|
+
return null
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Resolve entry point for root package requests (/node_modules/pkg/ or /node_modules/pkg)
|
|
773
|
+
const parts = pathname.slice(1).split('/') // ['node_modules', 'pkg', ...]
|
|
774
|
+
const isScoped = parts[1]?.startsWith('@')
|
|
775
|
+
const pkgParts = isScoped ? 3 : 2 // node_modules/@scope/pkg or node_modules/pkg
|
|
776
|
+
const subParts = parts.slice(pkgParts)
|
|
777
|
+
const isRootRequest = subParts.length === 0 || (subParts.length === 1 && subParts[0] === '')
|
|
778
|
+
|
|
779
|
+
if (isRootRequest) {
|
|
780
|
+
const pkgDir = './' + parts.slice(0, pkgParts).join('/')
|
|
781
|
+
const depPkg = await readPkgJson(pkgDir)
|
|
782
|
+
if (depPkg) {
|
|
783
|
+
const entry = resolveEntry(depPkg)
|
|
784
|
+
if (entry) {
|
|
785
|
+
const resp = await serveResolved(path.join(pkgDir, entry))
|
|
786
|
+
if (resp) return resp
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Subpath: resolve via conditional exports (CJS→ESM)
|
|
773
792
|
const esmPath = await resolveNodeModuleESM(filepath)
|
|
774
793
|
if (esmPath) {
|
|
775
|
-
const
|
|
776
|
-
if (
|
|
777
|
-
headers: { 'Content-Type': 'application/javascript' },
|
|
778
|
-
})
|
|
794
|
+
const resp = await serveResolved(esmPath)
|
|
795
|
+
if (resp) return resp
|
|
779
796
|
}
|
|
780
797
|
}
|
|
781
798
|
|
|
@@ -785,11 +802,15 @@ export function serve(entrypoint, flags) {
|
|
|
785
802
|
const inRoot = Bun.file('.' + pathname)
|
|
786
803
|
if (await inRoot.exists()) return new Response(inRoot)
|
|
787
804
|
|
|
788
|
-
// Try
|
|
805
|
+
// Try extensions for extensionless paths (e.g. node_modules imports)
|
|
789
806
|
const lastSegment = pathname.split('/').pop()
|
|
790
807
|
if (!lastSegment.includes('.')) {
|
|
791
|
-
//
|
|
792
|
-
|
|
808
|
+
// Try .imba first (compile on the fly), then .js/.mjs
|
|
809
|
+
const imbaPath = '.' + pathname + '.imba'
|
|
810
|
+
if (existsSync(imbaPath)) {
|
|
811
|
+
const out = await compileFile(imbaPath)
|
|
812
|
+
if (!out.errors?.length) return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
813
|
+
}
|
|
793
814
|
for (const ext of ['.js', '.mjs']) {
|
|
794
815
|
const withExt = Bun.file('.' + pathname + ext)
|
|
795
816
|
if (await withExt.exists()) return new Response(withExt, {
|