Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
dry-run: false
language: go
- name: Upload Crash
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
run: |
cd bindings/js
npm test
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
with:
name: ${{ matrix.os }}.node
path: bindings/js/prebuilds/*/*.node
Expand All @@ -74,7 +74,7 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- name: Install node-gyp and prebuildify
run: npm install --location=global node-gyp node-gyp-build node-api-headers prebuildify
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: bindings/js/prebuilds
- name: Move artifacts
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
CIBW_MANYLINUX_AARCH64_IMAGE: quay.io/pypa/manylinux_2_28_aarch64
CIBW_BEFORE_ALL: .ci/ensure-go.sh; make -C bindings/py compile
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: wheels-linux-${{ matrix.go_target }}
path: ./wheelhouse/*.whl
Expand Down Expand Up @@ -74,7 +74,7 @@ jobs:
env:
CIBW_ARCHS: ${{ matrix.cibw_target }}
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: wheels-windows-${{ matrix.go_target }}
path: ./wheelhouse/*.whl
Expand Down Expand Up @@ -109,7 +109,7 @@ jobs:
env:
CIBW_ARCHS: ${{ matrix.cibw_target }}
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: wheels-macos-${{ matrix.go_target }}
path: ./wheelhouse/*.whl
Expand All @@ -132,7 +132,7 @@ jobs:
- name: Build package
run: make -C bindings/py build
- name: Upload sdist
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: wheels
path: bindings/py/dist/*
Expand All @@ -143,7 +143,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
path: bindings/py/artifacts
- name: Bundle release artifacts
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ Minify is a minifier package written in [Go][1]. It provides HTML5, CSS3, JS, JS
The core functionality associates mimetypes with minification functions, allowing embedded resources (like CSS or JS within HTML files) to be minified as well. Users can add new implementations that are triggered based on a mimetype (or pattern), or redirect to an external command (like ClosureCompiler, UglifyCSS, ...).

### Sponsors
I'm actively looking for support in the form of donations or sponsorships to keep developing this library and highly appreciate any gesture. Please see the Sponsors button in GitHub for ways to contribute, or contact me directly.

#### SiteGround
[![SiteGround](https://www.siteground.com/img/downloads/siteground-logo-black-transparent-vector.svg)](https://www.siteground.com/)

Thank you SiteGround for having sponsored this project for many years! Their contribution is invaluable for code maintenance and improvements. If you are in need of professional web hosting, I can highly recommend their products.

#### Requesting sponsors
I'm actively looking for support in the form of donations or sponsorships to keep developing this library and highly appreciate any gesture. Please see the Sponsors button in GitHub for ways to contribute, or contact me directly.

#### Table of Contents

- [Minify](#minify)
Expand Down
18 changes: 18 additions & 0 deletions js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,24 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
}
}
}
} else if expr.Op == js.AndToken {
// TODO: use truthy instead of true?
if (isTrue(expr.X) || isFalse(expr.Y)) && !hasSideEffects(expr.X) {
m.minifyExpr(expr.Y, prec)
break
} else if (isTrue(expr.Y) || isFalse(expr.X)) && !hasSideEffects(expr.Y) {
m.minifyExpr(expr.X, prec)
break
}
} else if expr.Op == js.OrToken {
// TODO: use truthy instead of true?
if (isTrue(expr.X) || isFalse(expr.Y)) && !hasSideEffects(expr.Y) {
m.minifyExpr(expr.X, prec)
break
} else if (isTrue(expr.Y) || isFalse(expr.X)) && !hasSideEffects(expr.X) {
m.minifyExpr(expr.Y, prec)
break
}
} else if expr.Op == js.EqToken {
if left, ok := expr.X.(*js.Var); ok {
if right, ok := expr.Y.(*js.BinaryExpr); ok {
Expand Down
9 changes: 5 additions & 4 deletions js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,8 @@ func TestJS(t *testing.T) {
//{`()=>a()`, ``},
{`if(true){let b}`, ``},
{`if(true){let b=5}`, ``},
{`if(true){const b=c}`, `!0&&c`},
{`if(true){const b=c.d}`, `!0&&c.d`},
{`if(true){const b=c}`, `c`},
{`if(true){const b=c.d}`, `c.d`},

// arrow functions
{`() => {}`, `()=>{}`},
Expand Down Expand Up @@ -860,6 +860,7 @@ func TestJS(t *testing.T) {
{"var a = /*! comment */ b;", "/*! comment */var a=b"}, // #664
{`var c="";for(let i=0;;);var d="";for(let i=0;;);`, `var d,c="";for(let i=0;;);d="";for(let i=0;;);`}, // #687
{"String.raw`\\b`", "String.raw`\\b`"}, // #701
{"if(true){const a=setInterval(()=>{clearInterval(a)},100)}", "{const a=setInterval(()=>{clearInterval(a)},100)}"}, // #867
}

m := minify.New()
Expand Down Expand Up @@ -896,8 +897,8 @@ func TestJSVarRenaming(t *testing.T) {
{`function a(){var b;b}`, `function a(){var a;a}`},
{`!function(){x=function(){return fun()};var fun=function(){return 0}}`, `!function(){x=function(){return a()};var a=function(){return 0}}`},
{`!function(){var x=function(){return y};const y=5;x,y}`, `!function(){var b=function(){return a};const a=5;b,a}`},
{`!function(){if(1){const x=5;x;5}var y=function(){return x};y}`, `!function(){if(1){const a=5;a,5}var a=function(){return x};a}`},
{`!function(){var x=function(){return y};x;if(1){const y=5;y;5}}`, `!function(){var a=function(){return y};if(a,1){const a=5;a,5}}`},
{`!function(){if(1){const x=5;x;5}var y=function(){return x};y}`, `!function(){{const a=5;a,5}var a=function(){return x};a}`},
{`!function(){var x=function(){return y};x;if(1){const y=5;y;5}}`, `!function(){var a=function(){return y};a;{const a=5;a,5}}`},
{`!function(){var x=function(){return y};x;if(z)var y=5}`, `!function(){var a,b=function(){return a};b,z&&(a=5)}`},
{`!function(){var x=function(){return y};x;if(z){var y=5;5}}`, `!function(){var a,b=function(){return a};b,z&&(a=5,5)}`},
{`!function(){var x,y,z=(x,y)=>x+y;x,y,z}`, `!function(){var a,b,c=(a,b)=>a+b;a,b,c}`},
Expand Down
33 changes: 29 additions & 4 deletions js/stmtlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,32 @@ import (
func optimizeStmt(i js.IStmt) js.IStmt {
// convert if/else into expression statement, and optimize blocks
if ifStmt, ok := i.(*js.IfStmt); ok {
if ifStmt.Body != nil {
ifStmt.Body = optimizeStmt(ifStmt.Body)
if truthy, ok := isTruthy(ifStmt.Cond); ok && truthy {
if hasSideEffects(ifStmt.Cond) {
ifStmt.Else = nil
return i // TODO: remove if and return StmtList(Cond, Body)
}
return optimizeStmt(ifStmt.Body)
} else if ok {
// falsy
if isEmptyStmt(ifStmt.Else) {
if hasSideEffects(ifStmt.Cond) {
return &js.ExprStmt{Value: ifStmt.Cond}
}
return &js.EmptyStmt{}
} else if hasSideEffects(ifStmt.Cond) {
if unaryExpr, ok := ifStmt.Cond.(*js.UnaryExpr); ok && unaryExpr.Op == js.NotToken {
ifStmt.Cond = unaryExpr.X
} else {
ifStmt.Cond = &js.UnaryExpr{js.NotToken, ifStmt.Cond}
}
ifStmt.Body, ifStmt.Else = ifStmt.Else, nil
return i // TODO: remove if and return StmtList(Cond, Body)
}
return optimizeStmt(ifStmt.Else)
}

ifStmt.Body = optimizeStmt(ifStmt.Body)
if ifStmt.Else != nil {
ifStmt.Else = optimizeStmt(ifStmt.Else)
}
Expand Down Expand Up @@ -122,7 +145,9 @@ func optimizeStmt(i js.IStmt) js.IStmt {
// remove let or const declaration in otherwise empty scope, but keep assignments
exprs := []js.IExpr{}
for _, item := range varDecl.List {
if item.Default != nil && hasSideEffects(item.Default) {
if bindingUsed(item.Binding) {
return blockStmt
} else if item.Default != nil && hasSideEffects(item.Default) {
exprs = append(exprs, item.Default)
}
}
Expand Down Expand Up @@ -150,7 +175,7 @@ func optimizeStmtList(list []js.IStmt, blockType blockType) []js.IStmt {
j := 0 // write index
for i := 0; i < len(list); i++ { // read index
if ifStmt, ok := list[i].(*js.IfStmt); ok && !isEmptyStmt(ifStmt.Else) {
// if(!a)b;else c => if(a)c; else b
// if(a)return b;else c => if(a)b; c
if unary, ok := ifStmt.Cond.(*js.UnaryExpr); ok && unary.Op == js.NotToken && isFlowStmt(lastStmt(ifStmt.Else)) {
ifStmt.Cond = unary.X
ifStmt.Body, ifStmt.Else = ifStmt.Else, ifStmt.Body
Expand Down
28 changes: 28 additions & 0 deletions js/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,34 @@ func hasDefines(v *js.VarDecl) bool {
return false
}

func bindingUsed(ibinding js.IBinding) bool {
switch binding := ibinding.(type) {
case *js.Var:
if 1 < binding.Uses {
return true
}
case *js.BindingArray:
for _, item := range binding.List {
if item.Binding != nil && bindingUsed(item.Binding) {
return true
}
}
if binding.Rest != nil && bindingUsed(binding.Rest) {
return true
}
case *js.BindingObject:
for _, item := range binding.List {
if item.Value.Binding != nil && bindingUsed(item.Value.Binding) {
return true
}
}
if binding.Rest != nil && bindingUsed(binding.Rest) {
return true
}
}
return false
}

func bindingVars(ibinding js.IBinding) (vs []*js.Var) {
switch binding := ibinding.(type) {
case *js.Var:
Expand Down
Loading