Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from esm-dev:main #59

Merged
merged 2 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
deploy:
name: Deploy esm.sh to production
name: Deploy server to production
runs-on: ubuntu-latest
environment: esm.sh

Expand Down Expand Up @@ -134,7 +134,7 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

publish_esmsh_to_npm:
name: Publish "esm.sh" to NPM
name: Publish package "esm.sh" to NPM
runs-on: ubuntu-latest
environment: release
needs: [build_esmsh_cli]
Expand Down Expand Up @@ -183,7 +183,7 @@ jobs:
- name: Extract Release Note
run: echo "console.log(require('fs').readFileSync('CHANGELOG.md','utf8').split('## ')[1].slice('${{ github.ref_name }}'.length).trim())" | node > release-note.txt

- name: Create Release
- name: Release
uses: softprops/action-gh-release@v2
with:
body_path: release-note.txt
Expand Down
72 changes: 35 additions & 37 deletions server/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,15 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
return esbuild.OnResolveResult{Path: path}, nil
}

// ban file urls
// ban file: imports
if strings.HasPrefix(args.Path, "file:") {
return esbuild.OnResolveResult{
Path: fmt.Sprintf("/error.js?type=unsupported-file-dependency&name=%s&importer=%s", strings.TrimPrefix(args.Path, "file:"), ctx.esm.Specifier()),
External: true,
}, nil
}

// skip dataurl/http modules
// skip data: and http: imports
if strings.HasPrefix(args.Path, "data:") || strings.HasPrefix(args.Path, "https:") || strings.HasPrefix(args.Path, "http:") {
return esbuild.OnResolveResult{
Path: args.Path,
Expand All @@ -441,6 +441,7 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}, nil
}

specifier := normalizeImportSpecifier(args.Path)
withTypeJSON := len(args.With) > 0 && args.With["type"] == "json"

// it's implicit external
Expand All @@ -455,9 +456,6 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}, nil
}

// normalize specifier
specifier := normalizeImportSpecifier(args.Path)

// check `?alias` option
if len(ctx.args.alias) > 0 && !isRelPathSpecifier(specifier) {
pkgName, _, subpath, _ := splitEsmPath(specifier)
Expand All @@ -469,7 +467,7 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}
}

// resolve specifier using the package `imports` field
// resolve specifier using the `imports` field of package.json
if len(ctx.pkgJson.Imports) > 0 {
if v, ok := ctx.pkgJson.Imports[specifier]; ok {
if s, ok := v.(string); ok {
Expand All @@ -493,7 +491,7 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}
}

// resolve specifier using the `browser` field
// resolve specifier using the `browser` field of package.json
if !isRelPathSpecifier(specifier) && len(ctx.pkgJson.Browser) > 0 && ctx.isBrowserTarget() {
if name, ok := ctx.pkgJson.Browser[specifier]; ok {
if name == "" {
Expand All @@ -506,26 +504,14 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}
}

// use `npm:` specifier for `denonext` target if the specifier is in the `forceNpmSpecifiers` list
if forceNpmSpecifiers[specifier] && ctx.target == "denonext" {
version := ""
pkgName, _, subPath, _ := splitEsmPath(specifier)
if pkgName == ctx.esm.PkgName {
version = ctx.esm.PkgVersion
} else if v, ok := ctx.pkgJson.Dependencies[pkgName]; ok && isExactVersion(v) {
version = v
} else if v, ok := ctx.pkgJson.PeerDependencies[pkgName]; ok && isExactVersion(v) {
version = v
}
p := pkgName
if version != "" {
p += "@" + version
}
if subPath != "" {
p += "/" + subPath
// nodejs builtin module
if isNodeBuiltInModule(specifier) {
externalPath, err := ctx.resolveExternalModule(specifier, args.Kind, withTypeJSON, analyzeMode)
if err != nil {
return esbuild.OnResolveResult{}, err
}
return esbuild.OnResolveResult{
Path: fmt.Sprintf("npm:%s", p),
Path: externalPath,
External: true,
}, nil
}
Expand All @@ -539,18 +525,6 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
fullFilepath = path.Join(ctx.wd, "node_modules", specifier)
}

// nodejs builtin module
if isNodeBuiltInModule(specifier) {
externalPath, err := ctx.resolveExternalModule(specifier, args.Kind, withTypeJSON, analyzeMode)
if err != nil {
return esbuild.OnResolveResult{}, err
}
return esbuild.OnResolveResult{
Path: externalPath,
External: true,
}, nil
}

// node native modules do not work via http import
if strings.HasSuffix(fullFilepath, ".node") && existsFile(fullFilepath) {
return esbuild.OnResolveResult{
Expand Down Expand Up @@ -815,6 +789,30 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
}
}

// use `npm:` specifier for `denonext` target if the specifier is in the `forceNpmSpecifiers` list
if forceNpmSpecifiers[specifier] && ctx.target == "denonext" {
version := ""
pkgName, _, subPath, _ := splitEsmPath(specifier)
if pkgName == ctx.esm.PkgName {
version = ctx.esm.PkgVersion
} else if v, ok := ctx.pkgJson.Dependencies[pkgName]; ok && isExactVersion(v) {
version = v
} else if v, ok := ctx.pkgJson.PeerDependencies[pkgName]; ok && isExactVersion(v) {
version = v
}
p := pkgName
if version != "" {
p += "@" + version
}
if subPath != "" {
p += "/" + subPath
}
return esbuild.OnResolveResult{
Path: fmt.Sprintf("npm:%s", p),
External: true,
}, nil
}

// replace some npm modules with browser native APIs
var replacement npm_replacements.NpmReplacement
var ok bool
Expand Down
4 changes: 4 additions & 0 deletions server/build_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,10 @@ func (ctx *BuildContext) resolveExternalModule(specifier string, kind api.Resolv
return specifier, nil
}

if ctx.externalAll {
return specifier, nil
}

defer func() {
if err == nil && !withTypeJSON {
resolvedPathFull := resolvedPath
Expand Down
3 changes: 1 addition & 2 deletions server/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ func bundleHttpModule(npmrc *NpmRC, entry string, importMap common.ImportMap, co
if u.Scheme == entryUrl.Scheme && u.Host == entryUrl.Host {
if (endsWith(u.Path, moduleExts...) || endsWith(u.Path, ".css", ".json", ".vue", ".svelte", ".md")) && !u.Query().Has("url") {
return esbuild.OnResolveResult{Path: path, Namespace: "http"}, nil
} else {
return esbuild.OnResolveResult{Path: path, Namespace: "url"}, nil
}
return esbuild.OnResolveResult{Path: path, Namespace: "url"}, nil
}
}
}
Expand Down
54 changes: 32 additions & 22 deletions test/build-args/external.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
import { assertEquals, assertStringIncludes } from "jsr:@std/assert";

Deno.test("`?external` query", async () => {
const res1 = await fetch("http://localhost:8080/[email protected]?external=react");
const code1 = await res1.text();
assertStringIncludes(code1, '"/[email protected]/X-ZXJlYWN0/denonext/react-dom.mjs"');

const res2 = await fetch("http://localhost:8080/*[email protected]/jsx-runtime");
const code2 = await res2.text();
assertStringIncludes(code2, '"/*[email protected]/denonext/jsx-runtime.mjs"');

const res3 = await fetch("http://localhost:8080/[email protected]/hooks?external=preact");
const code3 = await res3.text();
assertStringIncludes(code3, '"/[email protected]/X-ZXByZWFjdA/denonext/hooks.mjs"');
{
const res = await fetch("http://localhost:8080/[email protected]?external=react");
assertStringIncludes(await res.text(), '"/[email protected]/X-ZXJlYWN0/denonext/react-dom.mjs"');
}
{
const res = await fetch("http://localhost:8080/[email protected]/hooks?external=preact");
assertStringIncludes(await res.text(), '"/[email protected]/X-ZXByZWFjdA/denonext/hooks.mjs"');
}
{
const res = await fetch("http://localhost:8080/*[email protected]/jsx-runtime");
assertStringIncludes(await res.text(), '"/*[email protected]/denonext/jsx-runtime.mjs"');
}
{
const res = await fetch("http://localhost:8080/*[email protected]/denonext/jsx-runtime.mjs");
assertStringIncludes(await res.text(), 'from"preact"');
}
{
const res = await fetch("http://localhost:8080/*[email protected]/denonext/hooks.mjs");
assertStringIncludes(await res.text(), 'from"preact"');
}
});

Deno.test("drop invalid `?external`", async () => {
const res1 = await fetch("http://localhost:8080/[email protected]?target=es2022&external=foo,bar,react");
const code1 = await res1.text();
assertStringIncludes(code1, '"/[email protected]/X-ZXJlYWN0/es2022/react-dom.mjs"');

const res2 = await fetch("http://localhost:8080/[email protected]?target=es2022&external=foo,bar,preact");
const code2 = await res2.text();
assertStringIncludes(code2, '"/[email protected]/es2022/react-dom.mjs"');

const res3 = await fetch("http://localhost:8080/[email protected]?external=react-dom");
const code3 = await res3.text();
assertStringIncludes(code3, '"/[email protected]/denonext/react-dom.mjs"');
{
const res = await fetch("http://localhost:8080/[email protected]?target=es2022&external=foo,bar,react");
assertStringIncludes(await res.text(), '"/[email protected]/X-ZXJlYWN0/es2022/react-dom.mjs"');
}
{
const res = await fetch("http://localhost:8080/[email protected]?target=es2022&external=foo,bar,preact");
assertStringIncludes(await res.text(), '"/[email protected]/es2022/react-dom.mjs"');
}
{
const res = await fetch("http://localhost:8080/[email protected]?external=react-dom");
assertStringIncludes(await res.text(), '"/[email protected]/denonext/react-dom.mjs"');
}
});

Deno.test("types with `?external`", async () => {
Expand Down
Loading