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

[feat] decrease bundle size #399

Closed
loynoir opened this issue Apr 20, 2023 · 3 comments
Closed

[feat] decrease bundle size #399

loynoir opened this issue Apr 20, 2023 · 3 comments
Labels
enhancement future Candidate future functionality

Comments

@loynoir
Copy link

loynoir commented Apr 20, 2023

Feat

Have a glance at the output, seems a lot of unused logic is not tree-shake-able, and thus not tree-shake away.

Reproduce: Care about detail

import { Type } from '@sinclair/typebox'
import { TypeCompiler } from '@sinclair/typebox/compiler'

const spec = Type.Object(
  {
    foo: Type.String(),
    bar: Type.Number()
  },
  { additionalProperties: false }
)

const C = TypeCompiler.Compile(spec)
const Check = C.Check.bind(C)

const value = {}

if (Check(value)) {
  console.log('OK')
} else {
  console.warn([...C.Errors(value)])
}
$ du -h reproduce.mjs 
4.0K    reproduce.mjs
$ npm exec -- esbuild --format=esm --bundle --outfile=reproduce.output.mjs reproduce.mjs

  reproduce.output.mjs  148.7kb

Reproduce: Not care about detail, with compile

#396 (comment)

const Check = (() => {
  return function check(value) {
    return (
      typeof value === 'object' &&
      value !== null &&
      !Array.isArray(value) &&
      typeof value.foo === 'string' &&
      typeof value.bar === 'number' &&
      Number.isFinite(value.bar) &&
      Object.getOwnPropertyNames(value).length === 2
    )
  }
})()

const value = {}

if (Check(value)) {
  console.log('OK')
} else {
  // console.warn([...C.Errors(value)])
}
$ du -h reproduce.output.mjs 
4.0K    reproduce.output.mjs
@sinclairzx81
Copy link
Owner

@loynoir Heya, The compiler only imports the modules it needs to operate. These aren't tree shake-able (afaik) (as all functionality may be invoked at runtime depending on the schema). These are the imports taken by the compiler.

https://github.com/sinclairzx81/typebox/blob/master/src/compiler/compiler.ts#L29-L32

Bundle Sizes

The following are the current compiled / minified sizes when pulling individual submodules (under the esbuild profile used by this tool) (just to give an indication of what size cost there is to importing a sub module).

┌──────────────────────┬────────────┬────────────┬─────────────┐
│       (index)        │  Compiled  │  Minified  │ Compression │
├──────────────────────┼────────────┼────────────┼─────────────┤
│ typebox/compiler     │ '127.2 kb' │ ' 56.9 kb' │  '2.23 x'   │
│ typebox/errors       │ '110.9 kb' │ ' 49.2 kb' │  '2.25 x'   │
│ typebox/system       │ ' 76.4 kb' │ ' 31.5 kb' │  '2.42 x'   │
│ typebox/value        │ '176.9 kb' │ ' 76.8 kb' │  '2.30 x'   │
│ typebox              │ ' 75.3 kb' │ ' 31.1 kb' │  '2.42 x'   │
└──────────────────────┴────────────┴────────────┴─────────────┘

This list is maintained with each NPM publish here

Considerations on Value submodule

Currently there is some consideration going towards splitting the typebox/value (for example supporting typebox/value/check imports via package.json). This may be useful for browser users who only need checking, but not diffing, patching, and other functionality.

Outside of this, there isn't too much that can be done to reduce bundle sizes at this stage. However, reducing code overhead is a long term goal for the project though, but carrying out refactoring's to reduce code size also has a maintainability cost. For now, TypeBox is favoring maintainability over reduce code.

I am open to suggestions to reduce bundled output, there may be places where cross module imports can be reduced or split.

@loynoir
Copy link
Author

loynoir commented Apr 21, 2023

These aren't tree shake-able (afaik) (as all functionality may be invoked at runtime depending on the schema).

Before

$ npm exec -- esbuild --format=esm --bundle --outfile=reproduce.output.mjs reproduce.mjs
  reproduce.output.mjs  148.7kb

After

  "type": "module",
    "target": "ES2022",
    "module": "ES2022",
$ npm exec -- esbuild --format=esm --bundle --outfile=reproduce.output.mjs reproduce.mjs
  reproduce.output.mjs  123.4kb

@sinclairzx81
Copy link
Owner

Closing and deferring this work until 0.28.x has stabilized and type compositing and value infrastructure has been refactored accordingly.

Marking labels for future enhancement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement future Candidate future functionality
Projects
None yet
Development

No branches or pull requests

2 participants