bimba-cli 0.7.20 → 0.7.21
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/plugin.js +1 -1
- package/serve.js +174 -37
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -48,7 +48,7 @@ function compileErrorSnippet(error) {
|
|
|
48
48
|
|
|
49
49
|
function compileErrorSignature(errors) {
|
|
50
50
|
return errors
|
|
51
|
-
.map(error => [compileErrorMessage(error), compileErrorLine(error)
|
|
51
|
+
.map(error => [compileErrorMessage(error), compileErrorLine(error)].join('\n'))
|
|
52
52
|
.join('\n---\n');
|
|
53
53
|
}
|
|
54
54
|
|
package/serve.js
CHANGED
|
@@ -354,16 +354,39 @@ function rewriteBareImports(js) {
|
|
|
354
354
|
return js
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
function isMissingFileError(error) {
|
|
358
|
+
return error?.code === 'ENOENT' || String(error?.message || error).includes('ENOENT: no such file or directory')
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function missingCompileResult(filepath) {
|
|
362
|
+
dropFileState(filepath)
|
|
363
|
+
return { js: '', errors: [], slots: null, changeType: 'missing', missing: true }
|
|
364
|
+
}
|
|
365
|
+
|
|
357
366
|
async function compileFile(filepath) {
|
|
358
367
|
const abs = path.resolve(filepath)
|
|
368
|
+
if (!existsSync(abs)) return missingCompileResult(abs)
|
|
369
|
+
|
|
359
370
|
const file = Bun.file(filepath)
|
|
360
|
-
|
|
371
|
+
let stat
|
|
372
|
+
try {
|
|
373
|
+
stat = await file.stat()
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (isMissingFileError(error)) return missingCompileResult(abs)
|
|
376
|
+
throw error
|
|
377
|
+
}
|
|
361
378
|
const mtime = stat.mtime.getTime()
|
|
362
379
|
|
|
363
380
|
const cached = _compileCache.get(abs)
|
|
364
381
|
if (cached && cached.mtime === mtime) return _normalizeResult(cached.result, { changeType: 'cached' })
|
|
365
382
|
|
|
366
|
-
|
|
383
|
+
let code
|
|
384
|
+
try {
|
|
385
|
+
code = await file.text()
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (isMissingFileError(error)) return missingCompileResult(abs)
|
|
388
|
+
throw error
|
|
389
|
+
}
|
|
367
390
|
const result = compiler.compile(code, {
|
|
368
391
|
sourcePath: filepath,
|
|
369
392
|
platform: 'browser',
|
|
@@ -655,7 +678,7 @@ export function serve(entrypoint, flags) {
|
|
|
655
678
|
|
|
656
679
|
function errorSignature(errors) {
|
|
657
680
|
return serializeErrors(errors)
|
|
658
|
-
.map(error => [error.message, error.line || ''
|
|
681
|
+
.map(error => [error.message, error.line || ''].join('\n'))
|
|
659
682
|
.join('\n---\n')
|
|
660
683
|
}
|
|
661
684
|
|
|
@@ -685,6 +708,16 @@ export function serve(entrypoint, flags) {
|
|
|
685
708
|
showTrackedError(key, item)
|
|
686
709
|
}
|
|
687
710
|
|
|
711
|
+
function errorText(errors) {
|
|
712
|
+
const list = Array.isArray(errors) ? errors : [errors]
|
|
713
|
+
return list.map(errorMessage).join('\n')
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function errorResponse(file, errors, status = 500) {
|
|
717
|
+
reportError(file, errors)
|
|
718
|
+
return new Response(errorText(errors), { status })
|
|
719
|
+
}
|
|
720
|
+
|
|
688
721
|
function clearError(file) {
|
|
689
722
|
const key = file ? normalizeFile(file) : null
|
|
690
723
|
const wasStatusFile = key && _statusFile === key
|
|
@@ -742,6 +775,10 @@ export function serve(entrypoint, flags) {
|
|
|
742
775
|
const out = await compileFile(filepath)
|
|
743
776
|
|
|
744
777
|
if (!isCurrentChange(filename, version)) return
|
|
778
|
+
if (out.missing) {
|
|
779
|
+
clearError(rel)
|
|
780
|
+
return
|
|
781
|
+
}
|
|
745
782
|
|
|
746
783
|
if (out.errors?.length) {
|
|
747
784
|
reportError(rel, out.errors)
|
|
@@ -757,6 +794,11 @@ export function serve(entrypoint, flags) {
|
|
|
757
794
|
broadcast({ type: 'update', file: rel, slots: out.slots || 'shifted' })
|
|
758
795
|
} catch(e) {
|
|
759
796
|
if (!isCurrentChange(filename, version)) return
|
|
797
|
+
if (isMissingFileError(e)) {
|
|
798
|
+
dropFileState(filepath)
|
|
799
|
+
clearError(rel)
|
|
800
|
+
return
|
|
801
|
+
}
|
|
760
802
|
reportError(rel, [{ message: e.message, snippet: e.stack || e.message }])
|
|
761
803
|
}
|
|
762
804
|
}
|
|
@@ -774,6 +816,7 @@ export function serve(entrypoint, flags) {
|
|
|
774
816
|
fetch: async (req, server) => {
|
|
775
817
|
const url = new URL(req.url)
|
|
776
818
|
const pathname = url.pathname
|
|
819
|
+
try {
|
|
777
820
|
|
|
778
821
|
// WebSocket upgrade for HMR
|
|
779
822
|
if (pathname === '/__hmr__') {
|
|
@@ -782,21 +825,33 @@ export function serve(entrypoint, flags) {
|
|
|
782
825
|
|
|
783
826
|
if (pathname.startsWith('/__bimba_vendor__/')) {
|
|
784
827
|
const specifier = vendorSpecifierFromPath(pathname)
|
|
828
|
+
const file = 'vendor:' + (specifier || pathname)
|
|
785
829
|
const bundled = specifier ? await bundleVendor(specifier) : null
|
|
786
830
|
if (bundled?.code) {
|
|
831
|
+
clearError(file)
|
|
787
832
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
788
833
|
}
|
|
789
834
|
|
|
790
|
-
return
|
|
835
|
+
return errorResponse(file, bundled?.errors || [`Could not bundle vendor module: ${specifier}`])
|
|
791
836
|
}
|
|
792
837
|
|
|
793
838
|
// HTML: index or any .html file
|
|
794
839
|
if (pathname === '/' || pathname.endsWith('.html')) {
|
|
795
840
|
const htmlFile = pathname === '/' ? htmlPath : '.' + pathname
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
841
|
+
const file = 'html:' + normalizeFile(htmlFile)
|
|
842
|
+
try {
|
|
843
|
+
let html = await Bun.file(htmlFile).text()
|
|
844
|
+
clearError(file)
|
|
845
|
+
return new Response(transformHtml(html, entrypoint), {
|
|
846
|
+
headers: { 'Content-Type': 'text/html' },
|
|
847
|
+
})
|
|
848
|
+
} catch (error) {
|
|
849
|
+
if (isMissingFileError(error)) {
|
|
850
|
+
clearError(file)
|
|
851
|
+
return new Response('Not Found', { status: 404 })
|
|
852
|
+
}
|
|
853
|
+
return errorResponse(file, [error])
|
|
854
|
+
}
|
|
800
855
|
}
|
|
801
856
|
|
|
802
857
|
// Imba files: compile on demand and serve as JS
|
|
@@ -805,15 +860,22 @@ export function serve(entrypoint, flags) {
|
|
|
805
860
|
const file = normalizeFile(pathname)
|
|
806
861
|
try {
|
|
807
862
|
const out = await compileFile(filepath)
|
|
863
|
+
if (out.missing) {
|
|
864
|
+
clearError(file)
|
|
865
|
+
return new Response('Not Found', { status: 404 })
|
|
866
|
+
}
|
|
808
867
|
if (out.errors?.length) {
|
|
809
|
-
|
|
810
|
-
return new Response(out.errors.map(e => e.message).join('\n'), { status: 500 })
|
|
868
|
+
return errorResponse(file, out.errors)
|
|
811
869
|
}
|
|
812
870
|
clearError(file)
|
|
813
871
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
814
872
|
} catch(e) {
|
|
815
|
-
|
|
816
|
-
|
|
873
|
+
if (isMissingFileError(e)) {
|
|
874
|
+
dropFileState(filepath)
|
|
875
|
+
clearError(file)
|
|
876
|
+
return new Response('Not Found', { status: 404 })
|
|
877
|
+
}
|
|
878
|
+
return errorResponse(file, [{ message: e.message, snippet: e.stack || e.message }])
|
|
817
879
|
}
|
|
818
880
|
}
|
|
819
881
|
|
|
@@ -823,26 +885,50 @@ export function serve(entrypoint, flags) {
|
|
|
823
885
|
if (pathname.endsWith('.css')) {
|
|
824
886
|
const cssPath = resolveFileCandidate(path.join(htmlDir, pathname)) || resolveFileCandidate('.' + pathname)
|
|
825
887
|
const cssFile = cssPath ? Bun.file(cssPath) : null
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
888
|
+
const file = 'css:' + normalizeFile(cssPath || pathname)
|
|
889
|
+
try {
|
|
890
|
+
if (cssFile && await cssFile.exists()) {
|
|
891
|
+
if (req.headers.get('sec-fetch-dest') === 'style') {
|
|
892
|
+
clearError(file)
|
|
893
|
+
return new Response(cssFile, { headers: { 'Content-Type': 'text/css' } })
|
|
894
|
+
}
|
|
830
895
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
896
|
+
const css = await cssFile.text()
|
|
897
|
+
const id = JSON.stringify(pathname)
|
|
898
|
+
const js = [
|
|
899
|
+
`const id = ${id};`,
|
|
900
|
+
`let el = document.querySelector('style[data-bimba-css=' + JSON.stringify(id) + ']');`,
|
|
901
|
+
`if (!el) { el = document.createElement('style'); el.setAttribute('data-bimba-css', id); document.head.appendChild(el); }`,
|
|
902
|
+
`el.textContent = ${JSON.stringify(css)};`,
|
|
903
|
+
].join('\n')
|
|
904
|
+
clearError(file)
|
|
905
|
+
return new Response(js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
906
|
+
}
|
|
907
|
+
} catch (error) {
|
|
908
|
+
if (isMissingFileError(error)) {
|
|
909
|
+
clearError(file)
|
|
910
|
+
return new Response('Not Found', { status: 404 })
|
|
911
|
+
}
|
|
912
|
+
return errorResponse(file, [error])
|
|
840
913
|
}
|
|
841
914
|
}
|
|
842
915
|
|
|
843
916
|
if (!pathname.startsWith('/node_modules/') && (pathname.endsWith('.js') || pathname.endsWith('.mjs'))) {
|
|
844
917
|
const jsFile = resolveFileCandidate(path.join(htmlDir, pathname)) || resolveFileCandidate('.' + pathname)
|
|
845
|
-
if (jsFile)
|
|
918
|
+
if (jsFile) {
|
|
919
|
+
const file = 'js:' + normalizeFile(jsFile)
|
|
920
|
+
try {
|
|
921
|
+
const response = await serveJavaScriptFile(jsFile)
|
|
922
|
+
clearError(file)
|
|
923
|
+
return response
|
|
924
|
+
} catch (error) {
|
|
925
|
+
if (isMissingFileError(error)) {
|
|
926
|
+
clearError(file)
|
|
927
|
+
return new Response('Not Found', { status: 404 })
|
|
928
|
+
}
|
|
929
|
+
return errorResponse(file, [error])
|
|
930
|
+
}
|
|
931
|
+
}
|
|
846
932
|
}
|
|
847
933
|
|
|
848
934
|
// Direct node_modules URLs (from user import maps or explicit imports)
|
|
@@ -853,28 +939,45 @@ export function serve(entrypoint, flags) {
|
|
|
853
939
|
if (resolved?.endsWith('.imba')) {
|
|
854
940
|
const out = await compileFile(resolved)
|
|
855
941
|
const file = normalizeFile(resolved)
|
|
942
|
+
if (out.missing) {
|
|
943
|
+
clearError(file)
|
|
944
|
+
return new Response('Not Found', { status: 404 })
|
|
945
|
+
}
|
|
856
946
|
if (out.errors?.length) {
|
|
857
|
-
|
|
858
|
-
return new Response(out.errors.map(e => e.message).join('\n'), { status: 500 })
|
|
947
|
+
return errorResponse(file, out.errors)
|
|
859
948
|
}
|
|
860
949
|
clearError(file)
|
|
861
950
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
862
951
|
}
|
|
863
952
|
|
|
864
953
|
if (resolved) {
|
|
954
|
+
const file = 'vendor:' + normalizeFile(pathname)
|
|
865
955
|
const bundled = await bundleVendor(path.resolve(resolved))
|
|
866
956
|
if (bundled?.code) {
|
|
957
|
+
clearError(file)
|
|
867
958
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
868
959
|
}
|
|
869
|
-
return
|
|
960
|
+
return errorResponse(file, bundled?.errors || [`Could not bundle ${pathname}`])
|
|
870
961
|
}
|
|
871
962
|
}
|
|
872
963
|
|
|
873
964
|
// Static files: check htmlDir first (for assets relative to HTML), then root
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
965
|
+
try {
|
|
966
|
+
const inHtmlDirPath = path.join(htmlDir, pathname)
|
|
967
|
+
const inHtmlDir = Bun.file(inHtmlDirPath)
|
|
968
|
+
if (await inHtmlDir.exists()) {
|
|
969
|
+
clearError('static:' + normalizeFile(inHtmlDirPath))
|
|
970
|
+
return new Response(inHtmlDir)
|
|
971
|
+
}
|
|
972
|
+
const inRootPath = '.' + pathname
|
|
973
|
+
const inRoot = Bun.file(inRootPath)
|
|
974
|
+
if (await inRoot.exists()) {
|
|
975
|
+
clearError('static:' + normalizeFile(inRootPath))
|
|
976
|
+
return new Response(inRoot)
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
if (!isMissingFileError(error)) return errorResponse('static:' + normalizeFile(pathname), [error])
|
|
980
|
+
}
|
|
878
981
|
|
|
879
982
|
// Try extensions for extensionless paths (e.g. node_modules imports)
|
|
880
983
|
const lastSegment = pathname.split('/').pop()
|
|
@@ -884,28 +987,62 @@ export function serve(entrypoint, flags) {
|
|
|
884
987
|
if (existsSync(imbaPath)) {
|
|
885
988
|
const out = await compileFile(imbaPath)
|
|
886
989
|
const file = normalizeFile(imbaPath)
|
|
990
|
+
if (out.missing) {
|
|
991
|
+
clearError(file)
|
|
992
|
+
return new Response('Not Found', { status: 404 })
|
|
993
|
+
}
|
|
887
994
|
if (out.errors?.length) {
|
|
888
|
-
|
|
889
|
-
return new Response(out.errors.map(e => e.message).join('\n'), { status: 500 })
|
|
995
|
+
return errorResponse(file, out.errors)
|
|
890
996
|
}
|
|
891
997
|
clearError(file)
|
|
892
998
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
893
999
|
}
|
|
894
1000
|
for (const ext of ['.js', '.mjs']) {
|
|
895
1001
|
const withExt = '.' + pathname + ext
|
|
896
|
-
if (existsSync(withExt))
|
|
1002
|
+
if (existsSync(withExt)) {
|
|
1003
|
+
const file = 'js:' + normalizeFile(withExt)
|
|
1004
|
+
try {
|
|
1005
|
+
const response = await serveJavaScriptFile(withExt)
|
|
1006
|
+
clearError(file)
|
|
1007
|
+
return response
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
if (isMissingFileError(error)) {
|
|
1010
|
+
clearError(file)
|
|
1011
|
+
return new Response('Not Found', { status: 404 })
|
|
1012
|
+
}
|
|
1013
|
+
return errorResponse(file, [error])
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
897
1016
|
}
|
|
898
1017
|
}
|
|
899
1018
|
|
|
900
1019
|
// SPA fallback for extension-less paths
|
|
901
|
-
|
|
1020
|
+
if (!lastSegment.includes('.')) {
|
|
1021
|
+
const file = 'html:' + normalizeFile(htmlPath)
|
|
1022
|
+
try {
|
|
902
1023
|
let html = await Bun.file(htmlPath).text()
|
|
1024
|
+
clearError(file)
|
|
903
1025
|
return new Response(transformHtml(html, entrypoint), {
|
|
904
1026
|
headers: { 'Content-Type': 'text/html' },
|
|
905
1027
|
})
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
if (isMissingFileError(error)) {
|
|
1030
|
+
clearError(file)
|
|
1031
|
+
return new Response('Not Found', { status: 404 })
|
|
1032
|
+
}
|
|
1033
|
+
return errorResponse(file, [error])
|
|
906
1034
|
}
|
|
1035
|
+
}
|
|
907
1036
|
|
|
908
1037
|
return new Response('Not Found', { status: 404 })
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
const file = 'server:' + normalizeFile(pathname || req.url)
|
|
1040
|
+
if (isMissingFileError(error)) {
|
|
1041
|
+
clearError(file)
|
|
1042
|
+
return new Response('Not Found', { status: 404 })
|
|
1043
|
+
}
|
|
1044
|
+
return errorResponse(file, [error])
|
|
1045
|
+
}
|
|
909
1046
|
},
|
|
910
1047
|
|
|
911
1048
|
websocket: {
|