Skip to content

Release v137#1312

Merged
ije merged 44 commits intomainfrom
release-v137
Feb 17, 2026
Merged

Release v137#1312
ije merged 44 commits intomainfrom
release-v137

Conversation

@ije
Copy link
Member

@ije ije commented Feb 9, 2026

No description provided.

@ije ije marked this pull request as ready for review February 15, 2026 04:09
Copilot AI review requested due to automatic review settings February 15, 2026 04:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This is a major release (v137) that includes significant new features, dependency updates, refactoring, and bug fixes. The PR introduces date-based versioning for npm packages, a new ?meta API endpoint, and the esm.sh/x feature for running TypeScript/JSX in browsers. Additionally, it includes the initial release of the CLI tool (v0.1.0) as a separate component with its own versioning scheme.

Changes:

  • Introduced date-based package versioning (yyyy-mm-dd format)
  • Added ?meta API endpoint for retrieving module build metadata
  • Refactored EsmPath.ID() to EsmPath.PackageId() throughout the codebase
  • Released CLI tool as v0.1.0 with separate versioning from the server
  • Updated dependencies (Go modules, Deno, esbuild-internal, gox, rex, etc.)
  • Enhanced JSX import source detection to support more frameworks (solid-js, mono-jsx, vue)
  • Improved development mode imports handling for React, React-DOM, and Vue
  • Updated documentation to use "no-build" terminology consistently
  • Removed deprecated validateModule function and associated imports
  • Simplified web handler by removing base64-encoded import map parameters

Reviewed changes

Copilot reviewed 36 out of 38 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
server/release.go Updated server version to v137
server/router.go Refactored to use PackageId() method and added module field to meta API response
server/path.go Renamed ID() method to PackageId() for clarity
server/transform.go Updated JSX import source detection to check for more frameworks
server/npmrc.go Updated getPackageInfoByDate to accept time.Time parameter directly
server/build.go Updated all references from ID() to PackageId()
server/build_resolver.go Updated all references from ID() to PackageId()
server/dts_transform.go Updated all references from ID() to PackageId()
server/cjs_module_lexer.go Updated reference from ID() to PackageId()
server/embed/tsx.ts Updated tsx package version to 1.5.3
internal/npm/npm.go Refactored IsDateVersion to return parsed time.Time and removed ConvertDateVersionToTime
internal/importmap/meta.go Changed SubPath field to not be serialized in JSON and added Module field
internal/importmap/importmap.go Added Has() method to Imports type
internal/deno/deno.go Updated Deno version to 2.6.9
web/release.go Changed version to v0.1.0 (separate versioning for web package)
web/utils.go Removed validateModule function and unused imports
web/handler.go Simplified by removing base64-encoded import map parameter handling
web/internal/loader.js Enhanced JSX detection, improved dev imports handling, added isHttpUrl helper
web/README.md Updated documentation with TailwindCSS examples and "no-build" terminology
cli/version.go Set CLI version to v0.1.0
cli/cli.go Updated help message and command routing
cli/command_serve.go Refactored to separate serve() function
cli/command_dev.go Added new Dev() command
cli/command_init.go Added new Init() command for project scaffolding
cli/command_add.go Fixed terminal line clearing issue
cli/command_tidy.go Updated help message
cli/README.md Updated documentation to reflect new features and installation methods
cli/npm/README.md Updated documentation with improved installation instructions
cli/npm/package.json Fixed "nobuild" to "no-build" typo
test/transform/transform.test.ts Updated test to use proper import map keys
test/meta/meta.test.ts Removed assertion for deprecated subpath field
go.mod Updated Go version to 1.26.0 and updated dependencies
go.sum Updated checksums for new dependency versions
Dockerfile Updated to Go 1.26 and Deno 2.6.9, fixed typo in comment
CHANGELOG-SERVER.md Added v137 release notes
CHANGELOG-CLI.md Added initial CLI changelog for v0.1.0
HOSTING.md Updated Docker image version reference
README.md Removed deprecated esm.sh/tsx documentation section
Comments suppressed due to low confidence (1)

CHANGELOG-SERVER.md:15

  • The date example uses "2026-01-02", which is in the future relative to the current date (February 15, 2026). This appears to be a typo - the example should use a past date to demonstrate the feature properly, such as "2025-01-02" or another historical date.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (firstSegment === pkgName || firstSegment.startsWith(pkgName + "@")) {
const version = firstSegment.split("@")[1];
if (version) {
// replace extension `.mjs` with `.development.mjs`
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a double space in the comment: "replace extension .mjs with" should have only one space after .mjs".

Copilot uses AI. Check for mistakes.
devImports[specifier] = moduleUrl.toString();
} else if (specifier.endsWith("/")) {
// match esm.sh specified route: "https://esm.sh/react@19.2.0&dev/" pattern
devImports[specifier] = url.replace(version, version + "&dev" )
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the line. For consistency with the rest of the file, this line should end with a semicolon.

Copilot uses AI. Check for mistakes.
async function tailwind(_id, content, config) {
// check if the url is a http url
function isHttpUrl(url) {
return typeof url === "string" && url.startsWith("https://") || url.startsWith("http://");
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect operator precedence in the return statement. The expression evaluates as typeof url === "string" && url.startsWith("https://") OR url.startsWith("http://"), which could cause a runtime error if url is not a string. Should be wrapped in parentheses: typeof url === "string" && (url.startsWith("https://") || url.startsWith("http://")).

Copilot uses AI. Check for mistakes.
web/handler.go Outdated
return
}
imfi, err := os.Lstat(filepath.Join(s.config.AppDir, string(im)))
indeHtmlStat, err := os.Lstat(filepath.Join(s.config.AppDir, "index.html"))
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name has a typo: "indeHtmlStat" should be "indexHtmlStat" for clarity and correctness.

Copilot uses AI. Check for mistakes.
web/handler.go Outdated
size = fi.Size()
}
etag := fmt.Sprintf("w/\"%x-%x-%x-%x%s\"", modTime, size, imfi.ModTime().UnixMilli(), imfi.Size(), s.etagSuffix)
etag := fmt.Sprintf("w/\"%x-%x-%x-%x%s\"", modTime, size, indeHtmlStat.ModTime().UnixMilli(), indeHtmlStat.Size(), s.etagSuffix)
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name has a typo: "indeHtmlStat" should be "indexHtmlStat" for clarity and correctness.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 42 out of 154 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

cli/command_init.go:101

  • The directory path references "templates/" but the embed directive on line 17 of this file uses "template" (without the 's'). This mismatch will cause the init command to fail because the embedded filesystem won't contain the expected template files. The embed directive should be updated to match: //go:embed templates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 42 out of 154 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

test/meta/meta.test.ts:47

  • The new module field is added to the meta response (see server/router.go:1703) but there are no tests verifying it. Consider adding an assertion to check that meta.module is present and has the expected value.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +35 to 52
// encodeUrl converts a url.URL to a string without escaping the path.
func encodeUrl(u *url.URL) string {
var buf strings.Builder
n := len(u.Scheme) + 3 + len(u.Host) + len(u.Path) + len(u.RawQuery)
if u.RawQuery != "" {
n++ // '?'
}
namedExports = make([]string, len(ast.NamedExports))
i := 0
for name := range ast.NamedExports {
namedExports[i] = name
i++
buf.Grow(n)
buf.WriteString(u.Scheme)
buf.Write([]byte{':', '/', '/'})
buf.WriteString(u.Host)
buf.WriteString(u.Path)
if u.RawQuery != "" {
buf.WriteByte('?')
buf.WriteString(u.RawQuery)
}
return
return buf.String()
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The encodeUrl function doesn't handle URL fragments (hash). If a URL contains a fragment (e.g., #section), it will be silently dropped. Consider adding support for u.Fragment if fragments are expected in the URLs being encoded.

Copilot uses AI. Check for mistakes.
Comment on lines 568 to 572
im, err := importmap.ParseEsmPath(url)
if err != nil {
http.Error(w, "importmap:failed to parse vue import url: "+url, 500)
}
options["vueVersion"] = im.Version
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After calling http.Error for a failed parse, the function continues to execute instead of returning. This will cause options["vueVersion"] to be set to an empty/invalid version. Add a return statement after the error handling on line 570.

Copilot uses AI. Check for mistakes.
Comment on lines 583 to 587
im, err := importmap.ParseEsmPath(url)
if err != nil {
http.Error(w, "importmap:failed to parse svelte import url: "+url, 500)
}
options["svelteVersion"] = im.Version
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After calling http.Error for a failed parse, the function continues to execute instead of returning. This will cause options["svelteVersion"] to be set to an empty/invalid version. Add a return statement after the error handling on line 585.

Copilot uses AI. Check for mistakes.
web/handler.go Outdated
Comment on lines 576 to 578
if importMap.Imports.Has("vue/jsx-runtime") || importMap.Imports.Has("vue/") {
options["jsxImportSource"] = "vue"
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code will panic if importMap is nil. The null check on line 565 doesn't protect line 576. Add a null check before accessing importMap.Imports here.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 156 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 45 to 50
async function tsx(filename, sourceCode, options) {
const { isDev, react, preact, jsxImportSource } = options ?? {};
let lang = filename.endsWith(".md?jsx") ? "jsx" : undefined;
let code = sourceCode ?? await Deno.readTextFile("." + filename);
let map = undefined;
let map = options.map;
if (filename.endsWith(".svelte") || filename.endsWith(".md?svelte")) {
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsx() reads options.map directly. If options is ever null/undefined for a worker call, this will throw at runtime; use optional access (or destructure map from options ?? {}) everywhere options.map is referenced.

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 93
const { transform } = await import("npm:@esm.sh/vue-compiler@1.0.1");
const ret = await transform(filename, sourceCode, {
imports: { "@vue/compiler-sfc": import("npm:@vue/compiler-sfc@" + getPackageVersion(importMap, "vue", "3")) },
const ret = await transform(filename, code, {
imports: { "@vue/compiler-sfc": import("npm:@vue/compiler-sfc@" + vueVersion) },
isDev,
devRuntime: isDev ? "/@vdr" : undefined,
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transformVue() imports @vue/compiler-sfc@${vueVersion} but vueVersion can be missing/empty, which produces an invalid npm specifier (e.g. @vue/compiler-sfc@undefined). Provide a default (supported major) or throw a clear error when it isn’t provided.

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +331
q := u.Query()
q.Set("dev", "TRUE")
u.RawQuery = strings.Replace(q.Encode(), "dev=TRUE", "dev", 1)
}
devImports = append(devImports, [2]string{specifier, encodeUrl(u)})
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dev query is added by setting dev=TRUE and then string-replacing it to dev. This is brittle (depends on encoder output and could affect other params); build the query string explicitly so the final URL is always correct.

Copilot uses AI. Check for mistakes.
web/handler.go Outdated
Comment on lines 349 to 353
if jsxRuntimeUrl != "" && !im.Imports.Has(jsxRuntime+"/jsx-dev-runtime") {
u, err := url.Parse(jsxRuntimeUrl)
if err == nil {
if strings.HasSuffix(u.Path, "/jsx-runtime.mjs") {
u.Path = u.Path[0:len(u.Path)-16] + "/jsx-dev-runtime.development.mjs"
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsxRuntimeUrl can resolve to a relative/local URL from the import map (e.g. /vendor/jsx-runtime.js). Parsing and then serializing it as scheme://host... will yield an invalid :///... URL and overwrite the import map entry. Only apply this dev-runtime rewrite when the resolved URL is an absolute http(s) URL (or handle relative URLs separately).

Copilot uses AI. Check for mistakes.
Comment on lines 52 to 56
importMap: {
imports: {
"@jsxRuntime": "https://preact@10.13.2",
"preact/jsx-runtime": "https://esm.sh/preact@10.13.2/jsx-runtime",
"preact-render-to-string": "https://esm.sh/preact-render-to-string6.0.2",
},
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import map URL for preact-render-to-string is missing an @ before the version (.../preact-render-to-string6.0.2). This will likely 404 during transform/bundling; update it to a valid esm.sh package URL.

Copilot uses AI. Check for mistakes.
Comment on lines +579 to +583
im, err := importmap.ParseEsmPath(url)
if err != nil {
http.Error(w, "importmap: failed to parse vue import url: "+url, 500)
return
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Vue modules, im.Version can be empty (or ParseEsmPath can fail for non-CDN/local imports). If vueVersion is missing, the loader will later import an invalid @vue/compiler-sfc@.... Consider defaulting to a supported major version and/or only parsing when the import URL is http(s).

Copilot uses AI. Check for mistakes.
Comment on lines 101 to 105
const { isDev, svelteVersion } = options ?? {};
const { compile, VERSION } = await import("npm:svelte@" + svelteVersion + "/compiler");
const majorVersion = parseInt(VERSION.split(".", 1)[0]);
if (majorVersion < 5) {
throw new Error("Unsupported Svelte version: " + VERSION + ". Please use svelte@5 or higher.");
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transformSvelte() imports svelte@${svelteVersion}/compiler but svelteVersion can be missing/empty, which will break the loader. Provide a default supported version (e.g. 5) or throw a clear error when it can’t be inferred.

Copilot uses AI. Check for mistakes.
Comment on lines +595 to +599
im, err := importmap.ParseEsmPath(url)
if err != nil {
http.Error(w, "importmap: failed to parse svelte import url: "+url, 500)
return
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue for Svelte: if im.Version is empty (or the import isn’t a parseable CDN URL), svelteVersion becomes missing and the loader will import svelte@undefined. Consider defaulting to a supported major version and/or only parsing when the import URL is http(s).

Copilot uses AI. Check for mistakes.
Comment on lines 79 to 83
if jsxImportSource == "" && (loader == esbuild.LoaderJSX || loader == esbuild.LoaderTSX) && options.importMap != nil {
var ok bool
for _, key := range []string{"@jsxRuntime", "@jsxImportSource"} {
path, resolved := options.importMap.Resolve(key, nil)
if resolved {
jsxImportSource = strings.TrimSuffix(path, "/jsx-runtime")
ok = true
for _, key := range options.importMap.Imports.Keys() {
if strings.HasSuffix(key, "/jsx-runtime") {
jsxImportSource = strings.TrimSuffix(key, "/jsx-runtime")
break
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imports.Keys() returns map keys in an arbitrary order. If multiple */jsx-runtime entries exist, this can pick a different jsxImportSource nondeterministically between runs. Consider checking a deterministic priority list (or sorting) before selecting the runtime.

Copilot uses AI. Check for mistakes.
@ije ije merged commit 3b725a3 into main Feb 17, 2026
2 checks passed
@ije ije deleted the release-v137 branch February 17, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant