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

Proposal: Provide tree-shakable v shorthand for valibot functions instead of namespace imports #1018

Open
k257588376 opened this issue Jan 17, 2025 · 8 comments
Assignees
Labels
question Further information is requested

Comments

@k257588376
Copy link

k257588376 commented Jan 17, 2025

The current approach is where you have to manually import everything using import * as v from 'valibot'.
While this method works, it introduces a few inconveniences. First, you lose the convenience of having v as a shorthand globally available with autocompete. using import * as v lacks autocomplete support and requires you to manually type import * as v from 'valibot'.

Modern JavaScript bundlers like Webpack, Rollup, and ESBuild fully support tree shaking with the export * as ... from '...' syntax in ECMAScript Modules. When using export * as ... from '...', a bundler can analyze the module and prune out unused exports even if they are imported under a namespace. For instance, lets rename index.ts to valibot.ts:

// File: src/valibot.ts
export * from './actions/index.ts';
export * from './methods/index.ts';
export * from './regex.ts';
export * from './schemas/index.ts';
export * from './storages/index.ts';
export * from './types/index.ts';
export * from './utils/index.ts';

// File: src/index.ts
export * from "./valibot.ts"
export * as v from "./valibot.ts"
// now the user of the library can use `v` with autocompletion and fully tree-shakable!
import { v } from "valibot"
const EmailSchema = v.pipe(v.string(), v.email());

Please, check the example of tree-shaking here: https://rollupjs.org/repl/?version=4.30.1&shareable=JTd...
It also works for Webpack, I tested tree-shaking on https://webpack-repl.vercel.app/ but it doesn't provide a shareable link for you.

@fabian-hiller fabian-hiller self-assigned this Jan 17, 2025
@fabian-hiller fabian-hiller added the question Further information is requested label Jan 17, 2025
@fabian-hiller
Copy link
Owner

Thanks for creating this issue and helping to make Valibot better! Do you think this approach will also work when putting everything into a single file? Our current bundling process produces a single JS file and I guess it will export a simple object in this case that cannot be tree-shaken.

@k257588376
Copy link
Author

Yes, you're right! Unfortunately, current bundling process that produces a single file cannot have this kind of tree-shakable namespace export. The output of tsup build:

var __defProp = Object.defineProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// src/valibot.ts
var valibot_exports = {};
__export(valibot_exports, {
  BASE64_REGEX: () => BASE64_REGEX,
  BIC_REGEX: () => BIC_REGEX,
  CUID2_REGEX: () => CUID2_REGEX,
  DECIMAL_REGEX: () => DECIMAL_REGEX,
  DIGITS_REGEX: () => DIGITS_REGEX,
  EMAIL_REGEX: () => EMAIL_REGEX,
  EMOJI_REGEX: () => EMOJI_REGEX,
  HEXADECIMAL_REGEX: () => HEXADECIMAL_REGEX,
  HEX_COLOR_REGEX: () => HEX_COLOR_REGEX,
  IMEI_REGEX: () => IMEI_REGEX,
  IPV4_REGEX: () => IPV4_REGEX,
  IPV6_REGEX: () => IPV6_REGEX,
  IP_REGEX: () => IP_REGEX,
...

And valibot_exports is an object that couldn't be tree shaken

@k257588376
Copy link
Author

k257588376 commented Jan 23, 2025

@fabian-hiller, is there some disadvantages of having multi-file bundle? Are you open for PR that will migrate to rollup & esbuild bundling process that will preserve module structure?

@fabian-hiller
Copy link
Owner

The only thing I don't like is that we're creating two ways to do exactly the same thing, and I'm sure that will confuse some users. Another idea would be to create a VS Code extension with an import shortcut when typing v and maybe prefiltering when typing v. so that only things that actually make sense depending on the context show up.

@Thanaen
Copy link

Thanaen commented Jan 24, 2025

I don't know if my remark is off-topic, but one advantage, in my eyes, of issuing several files is that it would allow environments that don't support tree-shaking (such as react-native / expo) to import only the necessary code 😁

PS: This is what I do with Remeda, for example: I use import { isPlainObject } from "remeda/dist/isPlainObject" in react-native to import only this function, instead of the recommended import * as R from "remeda"

@fabian-hiller
Copy link
Owner

Good to know. I will wait for more feedback on this regards.

@belgattitude
Copy link

@k257588376 @fabian-hiller

is there some disadvantages of having multi-file bundle? Are you open for PR that will migrate to rollup & esbuild bundling process that will preserve module structure?

About preserving module structure, I think it's achievable with tsup.

I did it in the past by using this configuration

import browserslistToEsbuild from 'browserslist-to-esbuild';
import { esbuildPluginFilePathExtensions } from 'esbuild-plugin-file-path-extensions';
import { defineConfig } from 'tsup';

export default defineConfig((options) => {
  return {
    // 👇 Let splitting by default
    // splitting: true,
   // 👇 This makes the trick
    esbuildPlugins: [esbuildPluginFilePathExtensions({ esmExtension: 'mjs' })],
   // 👇 This makes the trick
    treeshake: true,
    cjsInterop: false,
    clean: true,
    dts: true,
    // bundle: false,
    entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.types.ts'],
    format: ['cjs', 'esm'],
    minify: !options.watch,
    minifyIdentifiers: true,
    minifySyntax: true,
    minifyWhitespace: true,
    outExtension({ format }) {
      return {
        js: `.${format === 'cjs' ? 'cjs' : 'mjs'}`,
      };
    },
    platform: 'browser',
    sourcemap: !options.watch,
    target: ['es2022', ...browserslistToEsbuild()],

    tsconfig: './tsconfig.build.json',
  };
});

See https://github.com/belgattitude/httpx/blob/main/packages/assert/tsup.config.mjs

Image

If interested I'm open to try in a P/R something similar

@fabian-hiller
Copy link
Owner

I answered in the PR you created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants