preactjs / preact

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
https://preactjs.com
MIT License
36.89k stars 1.95k forks source link

The code published to npm is minified #4065

Open Raynos opened 1 year ago

Raynos commented 1 year ago

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.

JoviDeCroock commented 1 year ago

The main goal of us doing so has been to

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/...

Raynos commented 1 year ago

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.

Raynos commented 1 year ago

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.

rschristian commented 1 year ago

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.

Raynos commented 1 year ago

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.

rschristian commented 1 year ago

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. A few CDNs mangle themselves (whether they should is another issue) and bundlers may need some config to ensure both Preact & the add-on have consistently mangled properties.

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.

Raynos commented 1 year ago

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.

MicahZoltu commented 11 months ago

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.

dliebner commented 11 months ago

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.

rschristian commented 11 months ago

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.

matthewmueller commented 10 months ago

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!

johrstrom commented 4 months ago
  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` }
      }
    })
  },
}
lilnasy commented 5 days ago

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 ```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 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; ```