Add js web stuff to landing page + documentation
This commit is contained in:
87
go/jsbundler/import_check.go
Normal file
87
go/jsbundler/import_check.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package jsbundler
|
||||
|
||||
// esbuild silently resolves a `.js` import to a sibling `.ts`/`.tsx`/`.jsx`,
|
||||
// which lets misnamed specifiers slip through. This check catches them up
|
||||
// front so every import path names the file that actually exists on disk.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type importViolation struct {
|
||||
file string
|
||||
line int
|
||||
spec string // the written specifier, e.g. "./Icons.js"
|
||||
actual string // the corrected specifier, e.g. "./Icons.ts"
|
||||
}
|
||||
|
||||
// reImportSpec matches a quoted relative module specifier ending in ".js".
|
||||
var reImportSpec = regexp.MustCompile(`(["'])((?:\.\.?/)[^"']*?\.js)["']`)
|
||||
|
||||
// tsExtensions are the non-.js source extensions a .js specifier may
|
||||
// actually resolve to, in esbuild's resolution order.
|
||||
var tsExtensions = []string{".ts", ".tsx", ".jsx"}
|
||||
|
||||
// checkImportExtensions scans every JS/TS source under frontend/src for
|
||||
// relative import specifiers written with a ".js" extension whose literal
|
||||
// target does not exist but a sibling ".ts"/".tsx"/".jsx" does. esbuild
|
||||
// resolves these transparently, so they bundle fine — but the import path
|
||||
// lies about the file it points to. Specifiers that resolve to a real ".js"
|
||||
// file, and dangling specifiers with no sibling at all, are left for esbuild.
|
||||
func checkImportExtensions() []importViolation {
|
||||
var violations []importViolation
|
||||
srcRoot := filepath.Join(frontendDir, "src")
|
||||
|
||||
filepath.WalkDir(srcRoot, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() {
|
||||
return err
|
||||
}
|
||||
switch filepath.Ext(path) {
|
||||
case ".js", ".ts", ".tsx", ".jsx":
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
for i, line := range strings.Split(string(raw), "\n") {
|
||||
for _, m := range reImportSpec.FindAllStringSubmatch(line, -1) {
|
||||
spec := m[2]
|
||||
if _, statErr := os.Stat(filepath.Join(dir, spec)); statErr == nil {
|
||||
continue // resolves to a real .js file — fine
|
||||
}
|
||||
base := strings.TrimSuffix(spec, ".js")
|
||||
for _, ext := range tsExtensions {
|
||||
if _, statErr := os.Stat(filepath.Join(dir, base+ext)); statErr == nil {
|
||||
violations = append(violations, importViolation{
|
||||
file: path,
|
||||
line: i + 1,
|
||||
spec: spec,
|
||||
actual: base + ext,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return violations
|
||||
}
|
||||
|
||||
func reportImportViolations(violations []importViolation) {
|
||||
fmt.Fprintf(os.Stderr, "\nImport extension check failed: %d import(s) use a .js extension for a non-.js file.\n", len(violations))
|
||||
for _, v := range violations {
|
||||
fmt.Fprintf(os.Stderr, " %s:%d: %q should be %q\n", v.file, v.line, v.spec, v.actual)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Rename each specifier to match the file's real extension.")
|
||||
}
|
||||
Reference in New Issue
Block a user