Skip to content

The code published to npm is minified #4065

Closed

Description

@Raynos

Since the code in the npm tarball is minified by default ( https://socket.dev/npm/package/preact/files/10.15.1/dist/preact.mjs )

It's nearly impossible to debug or profile the preact framework when using it to build my app.

Please do not publish minified code and let me run my own minifier.

Activity

  1. JoviDeCroock commented on Jul 9, 2023

    @JoviDeCroock
    Member

    The main goal of us doing so has been to

    • ensure the lowest size, we know what properties are safe to mangle
    • consistent performance, no minifier/transpiler would insert a snippet that performs poorly

    I do see what you are saying about traces becoming unreadable while investigating bugs/performance. It's been one of my main motivators to fight for "readable" dist bundles as well.

    However doing so would most likely warrant a major as we do ship properties that we need mangled for our plugin ecosystem, the reconciler is pluggable so devtools/signals/fast-refresh rely on that being consistent.

    If you are facing a bug and don't know how to reproduce due to the bundle being unreadable I am open to figure this out together on a GitHub discussion/...

  2. Raynos commented on Jul 9, 2023

    @Raynos
    Author

    For my personal use case I am likely to copy paste the source code of preact into my application and use it directly so that I can have the unminified version.

    I ran into the same issue with react, and was fighting with nextjs in that it does not allow me to set NODE_ENV

    It would help me if you committed a version of preact.single-file.js into the git repo so I could copy a single file instead of the entire source code.

    The way i used to copy jquery.js into my vendor/jquery.js codebase and also copy vendor/jquery.min.js and control what I load when and where for development / staging / production.

  3. Raynos commented on Jul 9, 2023

    @Raynos
    Author

    I guess having dist/preact.raw.js next to dist/preact.min.js would also help me, in that I can quickly and easily copy the raw single file source code or import it with require('preact/dist/preact.raw.js')

    This would be a non breaking change, just an extra file into the npm publish tarball.

  4. rschristian commented on Jul 9, 2023

    @rschristian
    Member

    For my personal use case I am likely to copy paste the source code of preact into my application and use it directly so that I can have the unminified version.

    As Jovi mentioned, this could be an issue as the plugin system relies upon mangled properties for hooking into the reconciler. If you're only using Preact core, you shouldn't run into any issues, but you are locking yourself off from devtools/prefresh/etc.

    Unfortunately I'm not sure a mangled, but unminified, distribution file would end up being much more readable... _children still needs to become __k, etc.

  5. Raynos commented on Jul 10, 2023

    @Raynos
    Author

    I appreciate the technical explanation of why this wouldn't work with the ecosystem and the devtools.

    I am however completely blown away that this technical limitation exists. it boggles my mind.

    A mangled & unminified build would not really be helpful. I guess all the plugin ecosystem tools would have to support both mangled & unmangled copy of preact with some kind of config setting.

  6. rschristian commented on Jul 11, 2023

    @rschristian
    Member

    Well, I'm not quite sure what other alternatives exist. The problem is that properties need to remain consistent across versions and across every distribution method.

    Mangling ahead of time certainly isn't ideal, but I don't know of any better options that doesn't offload the problem onto end users.

  7. Raynos commented on Jul 11, 2023

    @Raynos
    Author

    The consistent mangling for ecosystem I can understand.

    Would be nice if the ecosystem respects both the unmangled and mangled property names, so I could use either the min or raw version.

  8. MicahZoltu commented on Dec 13, 2023

    @MicahZoltu

    I'm also frustrated by this right now as I have some code that is getting called too frequently and what is happening inside preact is quite opaque. The included TypeScript definition files help a lot with reading the code, but unfortunately Firefox (at least) doesn't unmangle variables in the debugger so this is what I see:
    image

    My position on this is that minification/mangling isn't worth the costs, especially given that basically every file served over the internet is gzipped. A given word/token (e.g., _children) is essentially one lookup table entry in a gzipped file plus maybe a byte or two of data per reference, thus mangling gives very minimal benefits. Meanwhile, the cost is that it takes significantly longer to debug applications than if the code was un-mangled.

    I would encourage comparing just gzipping vs gzipping+mangling to make sure you are actually benefiting in a significant way at least. My guess is that mangling isn't actually saving much. Even if it is, there should still be a way to run without mangling for people who don't want/need mangling.

  9. dliebner commented on Dec 29, 2023

    @dliebner

    Is there a way to import the source version?

    I'm currently using:
    import * as preactSignals from '@preact/signals-core';

    I tried this:
    import * as preactSignals from '@preact/signals-core/src/index.ts';
    ... but rollup complained about it not being JavaScript even though I have the typescript plugin loaded, and other typescript imports in my bundle are working fine.

  10. rschristian commented on Dec 30, 2023

    @rschristian
    Member

    Is there a way to import the source version?

    As mentioned, no.

    rollup complained about it not being JavaScript even though I have the typescript plugin loaded, and other typescript imports in my bundle are working fine.

    You'd need to configure your TS plugin to compile node_modules, that's not standard behavior.

  11. matthewmueller commented on Feb 4, 2024

    @matthewmueller

    I was able to get a PoC working with esbuild by:

    Tweaking preact/package.json:

    1. Add preact/src as a valid export
    "./src": {
      "types": "./src/index.d.ts",
      "browser": "./src/index.js",
      "import": "./src/index.js"
    },
    1. Tweak preact/jsx-dev-runtime to point to src:
    "./jsx-dev-runtime": {
      "types": "./jsx-runtime/src/index.d.ts",
      "browser": "./jsx-runtime/src/index.js",
      "import": "./jsx-runtime/src/index.js"
    },

    Currently jsx-dev-runtime points to the same thing as jsx-runtime, so unless that's for historical reasons, might make sense to tweak for dev. Otherwise an ./jsx-runtime/src export could be added.

    Then you adjust the alias settings in esbuild for development:

    Alias: map[string]string{
    	"preact":             "preact/src",
    	"preact/jsx-runtime": "preact/jsx-dev-runtime",
    },

    I'd be open to submitting a PR if this is something the Preact team would consider!

  12. johrstrom commented on Jul 9, 2024

    @johrstrom
    1. Add preact/src as a valid export

    I'd be open to submitting a PR if this is something the Preact team would consider!

    I would +1 this as somewhere in between 10.20.1 and 10.22.1 an exported function started being compiled as $2 that's conflicts with jQuery/Ruby on Rails javascript.

    I'm currently exploring something like this in esbuild to force it to resolve to the src instead of the dist directories.

    const preactSrcResolvePlugin = {
      name: 'preactSrcResolve',
      setup(build) {
        build.onResolve({ filter: /preact/ }, args => {
          const basePath = `${__dirname}/node_modules/preact`;
          if (args.path == 'preact') {
            return { path: `${basePath}/src/index.js` }
          } else if(args.path == 'preact/hooks') {
            return { path: `${basePath}/hooks/src/index.js` }
          }
        })
      },
    }
  13. lilnasy commented on Nov 26, 2024

    @lilnasy
    Contributor

    Seeing how unminified code is published on npm as well, I ended up with the following patch, and another one in prefresh to unmangle the fields. It added 253 bytes to the brotli'd javascript size.

    preact.patch
    diff --git a/package.json b/package.json
    index 166b927ce89d1ff7ecc259912cfc6ff26fbe5f13..27baf4ea22b3e84464ff4fb8749f1530ef3d5594 100644
    --- a/package.json
    +++ b/package.json
    @@ -20,56 +20,56 @@
             "types": "./src/index-5.d.ts"
           },
           "types": "./src/index.d.ts",
    -      "browser": "./dist/preact.module.js",
    +      "browser": "./src/index.js",
           "umd": "./dist/preact.umd.js",
           "import": "./dist/preact.mjs",
           "require": "./dist/preact.js"
         },
         "./compat": {
           "types": "./compat/src/index.d.ts",
    -      "browser": "./compat/dist/compat.module.js",
    +      "browser": "./compat/src/index.js",
           "umd": "./compat/dist/compat.umd.js",
           "import": "./compat/dist/compat.mjs",
           "require": "./compat/dist/compat.js"
         },
         "./debug": {
           "types": "./debug/src/index.d.ts",
    -      "browser": "./debug/dist/debug.module.js",
    +      "browser": "./debug/src/index.js",
           "umd": "./debug/dist/debug.umd.js",
           "import": "./debug/dist/debug.mjs",
           "require": "./debug/dist/debug.js"
         },
         "./devtools": {
           "types": "./devtools/src/index.d.ts",
    -      "browser": "./devtools/dist/devtools.module.js",
    +      "browser": "./devtools/src/index.js",
           "umd": "./devtools/dist/devtools.umd.js",
           "import": "./devtools/dist/devtools.mjs",
           "require": "./devtools/dist/devtools.js"
         },
         "./hooks": {
           "types": "./hooks/src/index.d.ts",
    -      "browser": "./hooks/dist/hooks.module.js",
    +      "browser": "./hooks/src/index.js",
           "umd": "./hooks/dist/hooks.umd.js",
           "import": "./hooks/dist/hooks.mjs",
           "require": "./hooks/dist/hooks.js"
         },
         "./test-utils": {
           "types": "./test-utils/src/index.d.ts",
    -      "browser": "./test-utils/dist/testUtils.module.js",
    +      "browser": "./test-utils/src/index.js",
           "umd": "./test-utils/dist/testUtils.umd.js",
           "import": "./test-utils/dist/testUtils.mjs",
           "require": "./test-utils/dist/testUtils.js"
         },
         "./jsx-runtime": {
           "types": "./jsx-runtime/src/index.d.ts",
    -      "browser": "./jsx-runtime/dist/jsxRuntime.module.js",
    +      "browser": "./jsx-runtime/src/index.js",
           "umd": "./jsx-runtime/dist/jsxRuntime.umd.js",
           "import": "./jsx-runtime/dist/jsxRuntime.mjs",
           "require": "./jsx-runtime/dist/jsxRuntime.js"
         },
         "./jsx-dev-runtime": {
           "types": "./jsx-runtime/src/index.d.ts",
    -      "browser": "./jsx-runtime/dist/jsxRuntime.module.js",
    +      "browser": "./jsx-runtime/src/index.js",
           "umd": "./jsx-runtime/dist/jsxRuntime.umd.js",
           "import": "./jsx-runtime/dist/jsxRuntime.mjs",
           "require": "./jsx-runtime/dist/jsxRuntime.js"
    @prefresh/core.patch
    diff --git a/src/constants.js b/src/constants.js
    index b39d40371ec6e156678fb9ca2a2719aa94bb1b1b..5260b84312057693b76002dd7b79d036d70e7e85 100644
    --- a/src/constants.js
    +++ b/src/constants.js
    @@ -1,13 +1,13 @@
    -export const VNODE_COMPONENT = '__c';
    +export const VNODE_COMPONENT = '_component';
     export const NAMESPACE = '__PREFRESH__';
    -export const COMPONENT_HOOKS = '__H';
    -export const HOOKS_LIST = '__';
    -export const EFFECTS_LIST = '__h';
    -export const RERENDER_COUNT = '__r';
    -export const CATCH_ERROR_OPTION = '__e';
    -export const COMPONENT_DIRTY = '__d';
    -export const VNODE_DOM = '__e';
    -export const VNODE_CHILDREN = '__k';
    -export const HOOK_VALUE = '__';
    -export const HOOK_ARGS = '__H';
    -export const HOOK_CLEANUP = '__c';
    +export const COMPONENT_HOOKS = '__hooks';
    +export const HOOKS_LIST = '_list';
    +export const EFFECTS_LIST = '_pendingEffects';
    +export const RERENDER_COUNT = '_rerenderCount';
    +export const CATCH_ERROR_OPTION = '_catchError';
    +export const COMPONENT_DIRTY = '_dirty';
    +export const VNODE_DOM = '_dom';
    +export const VNODE_CHILDREN = '_children';
    +export const HOOK_VALUE = '_value';
    +export const HOOK_ARGS = '_args';
    +export const HOOK_CLEANUP = '_cleanup';
    diff --git a/src/index.js b/src/index.js
    index 6c80ef4ed9ca406299c2ed0271c87eb19e6011d5..0ca44d43722191e9adfd31e6bb331daf9b9c6341 100644
    --- a/src/index.js
    +++ b/src/index.js
    @@ -54,7 +54,7 @@ function replaceComponent(OldType, NewType, resetHookState) {
       pendingUpdates = pendingUpdates.filter(p => p[0] !== OldType);
     
       vnodes.forEach(vnode => {
    -    if (!vnode.__c || !vnode.__c.__P) return;
    +    if (!vnode._component || !vnode._component._parentDom) return;
         // update the type in-place to reference the new component
         vnode.type = NewType;
  14. lilnasy commented on Dec 14, 2024

    @lilnasy
    Contributor

    Just ran into a pesky hydration bug. Preact kept remounting dom elements, rerunning the @starting-style transitions, and causing a flash. The html elements preact was hydrating were identical to what it rendered; logically I had done everything right. I had no idea what it wanted from me!

    It turned out there was some whitespace around the elements and preact was diffing against blank TextNodes. There's no way I would have found that out if I wasn't stepping through the unminified preact code in the debugger.

  15. 8 remaining items

  16. JoviDeCroock commented on Dec 14, 2024

    @JoviDeCroock
    Member

    Hmm, then we probably diff the divs correctly but maybe the unmount of the empty text node causes the state of the HTMLElement to be reset as it moves from position 1 to 0. Maybe it's worth to not unmount empty text nodes for this reason, they should not lead to erroneous child diffing.

  17. lilnasy commented on Dec 14, 2024

    @lilnasy
    Contributor

    In the meantime maybe this helps:

    The message did not make sense to me originally, but I dismissed it thinking engineers are not communicators. Only when I was looking preact/debug's source code did I realise it was supposed to log the diffed element tag names.

    Again, because I have the unminified code running, I was able to add this line to node_modules to immediately determine why it wasn't logging the available elements. That's twice in one day!

    Here's the repro for the flashing issue: https://stackblitz.com/edit/github-vmfzstte?file=src%2Fpages%2Findex.astro,h1.ts&on=stackblitz

  18. rschristian commented on Dec 15, 2024

    @rschristian
    Member

    but I dismissed it thinking engineers are not communicators.

    Snide comments like this aren't helpful, please try to keep things a bit more positive for people volunteering time.

    Again, because I have the unminified code running, I was able to add this line to node_modules to immediately determine why it wasn't logging the available elements. That's twice in one day!

    Alternatively, we do offer source maps... you can simply open your debugger and add a breakpoint. I get the value of console.log debugging, it's absolutely my go-to, but our source maps, along with any proper JS debugger, handles this situation (and more) very, very well.

  19. lilnasy commented on Dec 15, 2024

    @lilnasy
    Contributor

    Sorry. It's hard to communicate tone over text, but I didn't mean to be snide.

  20. amb26 commented on Feb 17, 2025

    @amb26

    This would still be highly desirable for me - I promisingly saw that this thread was referenced from the v11 tracking branch but couldn't track down exactly from which comment. It would be marvellous if v11 could include a build without name mangling.

  21. rschristian commented on Feb 17, 2025

    @rschristian
    Member

    @amb26 As mentioned, some name mangling would be required, but the problem with separate builds (which I ran into yesterday whilst testing out an idea) is that it's really, really easy to load the unminified build and standard build which breaks certain things as Preact must be a singleton. You have to be very careful with your build tooling and your aliases to get something usable.

    A separate build output seems more likely to harm than help, I'm not sure that's a viable option for us. Source maps will probably continue to be the recommended path.

  22. ericmorand commented on Oct 20, 2025

    @ericmorand

    Just my two cents: npm is a package registry, targetting the end-users. Code needs to be minified, and source maps should not be included. If one needs the non minified code or the source map, it means that they want to debug preact itself. Not their code; they want to debug something else.

    We never need to debug anything else than our own code. If something else needs to be debugged, it must be done from the source code of the said thing, not from the published artefact. npm is already a massive waste of bandwidth with source maps, not required dependencies, CDN files, all those things that are never even used by the applications that import the packages. Let's not add non-minified files just because some people want to debug something that they are not working on.

    If you need to debug preact, clone the preact repository and debug from there. If you have a runtime error on your application, this is because there is a bug in your application. There is no need to debug preact.

  23. rschristian commented on Oct 20, 2025

    @rschristian
    Member

    We never need to debug anything else than our own code. If something else needs to be debugged, it must be done from the source code of the said thing, not from the published artefact.

    Sorry, but this is far from true, quite often (5+ times a week) I need to debug external code, usually starting from the context of my existing app. Expecting users to jump from "found bug in app" straight to "extracted minimal repro in the source lib's tests" is just not very realistic.

    Source maps are meant to be shipped alongside built assets, that's pretty much the whole point. All in, Preact is 1.5mb w/ no deps. We don't need to worry ourselves with NPM's bandwidth.

    If you have a runtime error on your application, this is because there is a bug in your application.

    Us maintainers aren't perfect, we absolutely do make mistakes. Runtime errors aren't necessarily bugs in the application.

  24. ericmorand commented on Oct 20, 2025

    @ericmorand

    Sorry, but this is far from true, quite often (5+ times a week) I need to debug external code, usually starting from the context of my existing app

    You understand that if the rest of the world was doing the same thing, our industry would have ceased to exist decades ago? Imagine the Linux ecosystem if, whenever you install a package from a repository, you would get the compiled artefact plus the source maps and the embedded source code? This would represent gazillions of peta-octets of bandwidth and disk usage wasted every single year. The source code of the Linux kernel itself is 50 millions of line or so. Add the weight of the source map mappings and, well, you get the deal. And then add the size of every single package source code and source maps.

    Imagine the same with, I don't know, Unreal libraries and assets, Windows DLLs, MacOS binaries, PS5, Switch, Xbox, Android, iOS...basically every single programmable system ever, and you get an idea about why nobody would ever want to do that: HDD space would have been depleted decades ago, and internet bandwidth would be non existent, wasted by gazillions of useless data.

    Have you met a single developer outside of the JavaScript ecosystem that once said to its community "please provide the source code and the source-maps alongside the compiled package because I need them to understand why my application, that I'm currently developing, doesn't work as expected" and was granted satisfaction?

    Us maintainers aren't perfect, we absolutely do make mistakes. Runtime errors aren't necessarily bugs in the application.

    What you are describing are bugs. If I use your product and it does not work as described in the documentation and the specification, then this is a bug. I then open an issue. And maybe propose a fix.

    What this issue is about is not what you describe. In this issue, some people request to be able to debug your code during the development of their product. Which means one thing: they are not sure if they are doing something wrong, and thus need to dig into your code to understand why something is not working as expected.

    My opinion: they are doing so because they don't want to actually read the documentation and specification. Would they do so, they would be able to tell if they are doing something wrong, or if they found a bug in preact, without a doubt.

  25. rschristian commented on Oct 20, 2025

    @rschristian
    Member

    Have you met a single developer outside of the JavaScript ecosystem that once said to its community "please provide the source code and the source-maps alongside the compiled package because I need them to understand why my application, that I'm currently developing, doesn't work as expected" and was granted satisfaction?

    Er, yes, this is what debug symbols are for. Some bigger platforms have dedicated symbol servers to supply this information.

    My opinion: they are doing so because they don't want to actually read the documentation and specification. Would they do so, they would be able to tell if they are doing something wrong, or if they found a bug in preact, without a doubt.

    We don't have a comprehensive specification of what Preact does in certain situations, and certain errors absolutely have little documentation. That's our bad, we'd love to do better, but this is something we do in our free time, by and large. Users can and will come across some undocumented dragons and source maps help in those situations.


    Anyways, I think this issue has little to offer at this point in time. The maintainers are very aware of difficulties here and will try to take action where/when we think is appropriate, though today that has difficulties and tradeoffs which blocks any action for the foreseeable future.

    We appreciate the feedback from everyone!

  26. ericmorand commented on Oct 20, 2025

    @ericmorand
  27. matthewmueller commented on Oct 20, 2025

    @matthewmueller

    For future readers, mind summarizing why this has difficulties?

    From my quick look at the problem last year (comment), it seems it could be solved with a few extra exports, but probably missing something.

  28. rschristian commented on Oct 20, 2025

    @rschristian
    Member

    For future readers, mind summarizing why this has difficulties?

    As stated above, our ecosystem (first party especially) relies upon variable mangling to function correctly. Unless you're copy/pasting our mangle.json into your build system (which we don't publish, so you'd have to copy from GitHub), you're not building the library correctly and there will be runtime errors.

    preact-iso's <Router>, for example, makes use of this.__v.__k here. This corresponds with this._vnode._children pre-mangle, an example of which can be seen here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions