Giter Site home page Giter Site logo

prefresh's Introduction

Prefresh

Fast-refresh for Preact!

Integrations

Writing your own integration

Best practices

Recognition

We need to be able to recognise your components, this means that components should start with a capital letter and hook should start with use followed by a capital letter. This allows the Babel plugin to effectively recognise these.

Do note that a component as seen below is not named.

export default () => {
  return <p>Want to refresh</p>;
};

Instead do:

const Refresh = () => {
  return <p>Want to refresh</p>;
};

export default Refresh;

When you are working with HOC's be sure to lift up the displayName so we can recognise it as a component.

Usage in IE11

If you want to use @prefresh/webpack with IE11, you'll need to transpile the @prefresh/core and @prefresh/utils packages.

prefresh's People

Contributors

basixkor avatar cyyynthia avatar dbr031 avatar dependabot[bot] avatar developit avatar dreierf avatar fredkschott avatar github-actions[bot] avatar itsmapleleaf avatar izook avatar jaybeeuu avatar jovidecroock avatar jvdsande avatar lemonmade avatar lfre avatar lukeed avatar marvinhagemeister avatar natemoo-re avatar nickrttn avatar notwoods avatar pepsryuu avatar phuctm97 avatar piotr-cz avatar rschristian avatar sidujjain avatar siilwyn avatar sventschui avatar tobiasmelen avatar valin4tor avatar yyx990803 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

prefresh's Issues

Next.js 9.5 Webpack v5 support incompatible with @prefresh/next plugin

Just wanted to leave a friendly heads up, attempted to upgrade today for better build times and got this error:

❯ yarn dev
yarn run v1.22.4
warning package.json: No license field
$ next
ready - started server on http://localhost:3000
(node:23923) [DEP_WEBPACK_CONFIGURATION_OPTIMIZATION_NO_EMIT_ON_ERRORS] DeprecationWarning: optimization.noEmitOnErrors is deprecated in favor of optimization.emitOnErrors
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:23923) [DEP_WEBPACK_MAIN_TEMPLATE_REQUIRE] DeprecationWarning: MainTemplate.hooks.require is deprecated (use JavascriptModulesPlugin.getCompilationHooks().renderRequire instead)
(node:23923) [DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS] DeprecationWarning: The externals-function should be defined like ({context, request}, cb) => { ... }
/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/NormalModuleFactory.js:249
                                                        throw new Error(deprecationChangedHookMessage("afterResolve"));
                                                        ^

Error: NormalModuleFactory.afterResolve is no longer a waterfall hook, but a bailing hook instead. Do not return the passed object, but modify it instead. Returning false will ignore the request and results in no module created.

Running with node --trace-deprecation ./node_modules/next/dist/bin/next gives better error, and points to the exact bit of code:

❯ node --trace-deprecation ./node_modules/next/dist/bin/next
ready - started server on http://localhost:3000
(node:27700) [DEP_WEBPACK_CONFIGURATION_OPTIMIZATION_NO_EMIT_ON_ERRORS] DeprecationWarning: optimization.noEmitOnErrors is deprecated in favor of optimization.emitOnErrors
    at /home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/config/normalization.js:201:9
    at nestedConfig (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/config/normalization.js:42:52)
    at getNormalizedWebpackOptions (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/config/normalization.js:180:17)
    at createCompiler (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/webpack.js:58:18)
    at /home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/webpack.js:40:48
    at Array.map (<anonymous>)
    at createMultiCompiler (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/webpack.js:40:33)
    at webpack (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/webpack.js:108:14)
    at f (/home/user/Projects/hasura-admin-next-preact/node_modules/webpack/lib/index.js:31:15)
    at HotReloader.start (/home/user/Projects/hasura-admin-next-preact/node_modules/next/dist/server/hot-reloader.js:16:415)
(node:27700) [DEP_WEBPACK_MAIN_TEMPLATE_REQUIRE] DeprecationWarning: MainTemplate.hooks.require is deprecated (use JavascriptModulesPlugin.getCompilationHooks().renderRequire instead)
    at /home/user/Projects/hasura-admin-next-preact/node_modules/@prefresh/next/src/plugin.js:46:43

Seems like this is the bit it's complaining about being deprecated (@prefresh/next/src/plugin.js:46:43):

compiler.hooks.compilation.tap(NAME, compilation => {
	compilation.mainTemplate.hooks.require.tap(NAME, (source, chunk, hash) =>
		createRefreshTemplate(source, chunk, hash, compilation.mainTemplate)
	);
});

DeprecationWarning: MainTemplate.hooks.require is deprecated (use JavascriptModulesPlugin.getCompilationHooks().renderRequire instead)
at /home/user/Projects/hasura-admin-next-preact/node_modules/@prefresh/next/src/plugin.js:46:43

Support for strict package managers

Strict package managers like pnpm and yarn 2 don't flatten the dependency tree meaning that if we inject @prefresh/core or @prefresh/utils into the client-codebase this won't resolve correctly due to it being a dependency installed on the integration level rather than the base.

To circumvent this we'll need a few PR's like #80 to fix this issue. This puts these two dependencies at the root so it can be used from the installed module.

  • next
  • nollup
  • snowpack #80
  • vite #76
  • webpack

Only wrap runtime around components

We only want to wrap Components with the refresh-runtime for this we'll have to check the exports provided (be that default or named) with an assumption.

  • Does the exported member have a prototype of Component
  • Is the exported member a function starting with a capital letter

Bug after upgrading from 0.8.2 to 0.9.0

Webpack doesn't bundle after upgrading:

  • @prefresh/webpack from 0.8.2 to 0.9.0

CLI error:

building ./node_modules/html-webpack-plugin/lib/child-compiler.js:175
    return childCompilation.assets[helperFileName].source();
                                                   ^

TypeError: Cannot read property 'source' of undefined
    at ./node_modules/html-webpack-plugin/lib/child-compiler.js:175:52
    at Array.map (<anonymous>)
    at extractHelperFilesFromCompilation (./node_modules/html-webpack-plugin/lib/child-compiler.js:174:43)
    at ./node_modules/html-webpack-plugin/lib/child-compiler.js:113:13
    at ./node_modules/webpack/lib/Compiler.js:343:11
    at ./node_modules/webpack/lib/Compiler.js:681:15
    at AsyncSeriesHook.eval [as callAsync] (eval at create (./node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:24:1)
    at AsyncSeriesHook.lazyCompileHook (./node_modules/tapable/lib/Hook.js:154:20)
    at ./node_modules/webpack/lib/Compiler.js:678:31
    at AsyncSeriesHook.eval [as callAsync] (eval at create (./node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook (./node_modules/tapable/lib/Hook.js:154:20)
    at ./node_modules/webpack/lib/Compilation.js:1423:35
    at AsyncSeriesHook.eval [as callAsync] (eval at create (./node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook (./node_modules/tapable/lib/Hook.js:154:20)
    at ./node_modules/webpack/lib/Compilation.js:1414:32
    at AsyncSeriesHook.eval [as callAsync] (eval at create (./node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)

Yarn's lock changes (no other deps were updated):
Screen Shot 2020-08-09 at 9 37 46 PM

Add examples

Add a small hello world example (counter app) for every integration.

  • next
  • nollup
  • snowpack
  • vite
  • webpack

Introduce ESM and CJS exports

We'll need a build process so we can export both ESM and CJS, we should also use this oppurtunity to add export maps.

We could keep using cjyes or we could try out microbundle.

CC @developit any opinions?

Class component methods

The refresh on class components currently happens correctly apart from one scenario

class Landing extends Component {

  constructor(props) {
    super(props);
    this.state = { i: 0 }; 
    this.increment =  this.increment.bind(this); // this will make it fail!
  }

  increment() {
    this.setState({ i: (this as any).state.i + 1 });
  }
}

Error on a fresh preact-cli installation

Hi,

I created a new application with preact-cli with the template typescript (I also tried default). When I use preact watch --refresh I have this error:

Uncaught TypeError: Cannot read property 'sign' of undefined at bundle.js:1611 at setup (common.js?f66b:7) at Object.eval (browser.js?52b2:167) at Object.55../common (browser.js?52b2:179) at o (_prelude.js?917a:1) at eval (_prelude.js?917a:1) at Object.eval (url.js?2b44:7) at Object.52.debug (url.js?2b44:10) at o (_prelude.js?917a:1) at eval (_prelude.js?917a:1)

Tryout React-Refresh

At this point our hooks support is very basic, however we'd like to accurately handle:

  • switching hooks positions
  • changing effect function signatures
  • custom hooks

Hook issues using @prefresh/nollup

I came across an issue where the state is not being persisted when using components defined in the same file. It compiles and runs, but once prefresh and nollup injects new code for it, the hook state is reset to 0. Preact example provided here

Another related issue is when using hooks like useMemo inside components like here.
It exhibits Uncaught TypeError: undefined is not a function or Uncaught (in promise) TypeError: $RefreshSig$ is not a function when HMR injects this code.

I did verify that this works properly with the react-refresh example, so I guess these are related to the @prefresh/nollup plugin. Maybe this is @PepsRyuu's domain? :)

Fully rely on flushing

We can remove a lot of the module.hot.accept code now that we have a flushing mechanic which simplifies the bundler integrations a lot.

Would this work with preact/compat too?

Hey team, first off, wonderful job on what you all have done so far 💯

Just wanted to understand if this will support reloading in config with preact/compat aliases as well? (particularly in the webpack 4 case).

If yes, how would one set this up to work in that environment.
If no, is it a case of "it's out of scope" or "support will be added later".

Thanks in advance :)

Snowpack integration

Snowpack 2 will be fleshing out their HMR support soon, let's participate in the RFC and make this work well for both react-refresh and prefresh.

Pending PR

Hooks break components when using ts-loader + babel-loader + @prefresh/webpack

There seems to be some issue using hooks in components when using ts-loader in combination with babel-loader + @babel/preset-env and @prefresh/webpack. For some reason, components with hooks become undefined.

I have created a reproduction here. If you run the start npm script, you will see the issue I'm talking about in your browser.

If you comment out these lines (remove @babel/preset-env), the problem goes away.

If you return those lines and comment out these lines (remove ts-loader), then everything works. But they can't seem to coexist.

Perhaps I have it misconfigured in some way. Is there something in @babel/preset-env that would cause this? I would very much prefer to use ts-loader together with babel-loader, because there are also transformations that I want Typescript to make. However, in development I am using @prefresh/webpack, so it refuses to work correctly.

One thing that is different between my main project repo and this reproduction: in my project repo, the element becomes <undefined>, and doesn't throw the error in the browser. There is a lot more going on there, though, so there's apparently something that is allowing it to pass through as undefined. I just didn't bother taking the time to build it up to that point. I just wanted to showcase the failure.

Verify expected behaviour for hook-dependencies

Now when you would add a dependency the effect would rerun, when you'd remove one it won't. This does feel pretty correct to me, the problem being that if you update the implementation of the hook that it won't rerun, this is the part I'm unsure about.

What do people expect?

Debounce updates

Debounce the flushing of updates through saving for webpack and next since these builds can take a while.

Hold on to hookState

We should be able to hold on to hookState by having a set of heuristics to determine how we handle inserted/removed states, ...

// Reset hooks state
// TODO:
//  allow resubbing context: https://github.com/preactjs/preact/pull/2501
//  find reliable way of diffing depedency arrays
//  find reliable way to assert inserted state hooks
// if (hooks && hooks.__) {
//   const count = countStatefulHooks(hooks.__);
//   Component.prototype.forceUpdate.call(c);
//   const newCount = countStatefulHooks(hooks.__);
//   if (count !== newCount) {
//     // We'll have to reset hookState and call forceUpdate again.
//     // There's another case where we add a dependency to a hook but
//     // I'm not sure if we need to cover this.
//   }
// } else {
//   Component.prototype.forceUpdate.call(c);
// }

Make `@prefresh/vite` compatible with strict package managers

Currently @prefresh/vite injects code that directly imports from @prefresh/core and @prefresh/utils, which are not direct dependencies of the project, and cause the resolution to fail when using strict package managers like pnpm or yarn 2.

To make it compatible, we should use paths only under @prefresh/vite.

Exclude example folder when npm publish @prefresh/webpack

Hi @JoviDeCroock

Why submit this issue

When preact-cli create new project, it will install @prefresh/webpack/example's node_modules. example folder will occupy 38MB.

I think example folder is unnecessary when we use @prefresh/webpack, right ?

so I think it will reduce amount of file size if exclude example folder when npm publish
(for now, new preact project's node_modules is 270MB 😟)

How to reproduce

npx preact-cli@latest create default preact-project

and check @prefresh/webpack file size

How to fix (suggestion)

My suggestion is use npmignore and gitignore, but I don't know what the purpose of package.json's exports field for and this package's publish flow.

So I think it's still need rely on you to consider again how to improve it.

screenshots

file size

image
image

npm publish --dryr-run

image

Thanks all of your hard work and great job!!

Handle asynchronous component errors

When we for instance have a scenario where we load a component asynchronously

const MyLayComponent = lazy(() => import('comp'))

const App = () => {
  return (
    <Suspense>
      <MyLazyComponent />
   </Suspense>
  )
}

and that component throws an error the vnode will exist but we have to navigate up the tree to start the refresh higher up since the dom still has to get connected from the parent.

Climbing the tree won't work since due to the nature of hot reloading that parent should not have the new Child function reference meaning we'd have to resort to reload()

Vite 1.0

I just released Vite 1.0.0-beta.1 which includes a number of breaking changes. Specifically, the transform API has changed and now also supports returning source map via return { code, map }.

Let me know when you have a compatible release and I will update the template in create-vite-app. Thanks!

Error: No dependency factory available for this dependency type: MultiEntryDependency

> csgo@1.0.0 dev C:\localhost\csgo
> cross-env NODE_OPTIONS='--inspect' next

Debugger listening on ws://127.0.0.1:9229/09daed7d-7fde-432b-b57f-f9776fcecd38
For help, see: https://nodejs.org/en/docs/inspector
[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
[ ready ] compiled successfully - ready on http://localhost:3000
[ event ] build page: /next/dist/pages/_error
[ wait ]  compiling ...
Error: No dependency factory available for this dependency type: MultiEntryDependency
    at Compilation._addModuleChain (C:\localhost\csgo\node_modules\webpack\lib\Compilation.js:1057:10)
    at Compilation.addEntry (C:\localhost\csgo\node_modules\webpack\lib\Compilation.js:1164:8)
    at C:\localhost\csgo\node_modules\next\dist\server\on-demand-entry-handler.js:2:168
    at new Promise (<anonymous>)
    at addEntry (C:\localhost\csgo\node_modules\next\dist\server\on-demand-entry-handler.js:2:58)
    at C:\localhost\csgo\node_modules\next\dist\server\on-demand-entry-handler.js:3:129
    at async Promise.all (index 0)
(node:3676) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'addChunk' of null
    at Object.connectChunkAndModule (C:\localhost\csgo\node_modules\webpack\lib\GraphHelpers.js:35:13)
    at Compilation.seal (C:\localhost\csgo\node_modules\webpack\lib\Compilation.js:1308:17)
    at C:\localhost\csgo\node_modules\webpack\lib\Compiler.js:675:18
    at C:\localhost\csgo\node_modules\webpack\lib\Compilation.js:1261:4
    at AsyncSeriesHook.eval [as callAsync] (eval at create (C:\localhost\csgo\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonym
ous>:13:1)
    at AsyncSeriesHook.lazyCompileHook (C:\localhost\csgo\node_modules\tapable\lib\Hook.js:154:20)
    at Compilation.finish (C:\localhost\csgo\node_modules\webpack\lib\Compilation.js:1253:28)
    at C:\localhost\csgo\node_modules\webpack\lib\Compiler.js:672:17
    at eval (eval at create (C:\localhost\csgo\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:11:1)
(node:3676) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async fu
nction without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled p
romise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (
rejection id: 2)
(node:3676) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not han
dled will terminate the Node.js process with a non-zero exit code.
[ event ] build page: /support
[ event ] build page: /support

Next.js plugin

It should be possible to create a Next.js plugin that does the following:

  • mutate defaultLoaders.babel include react-refresh/babel
  • set hasReactRefresh = false
  • inject @prefresh/webpack into the babel plugins

Custom hook refresh leaves stale ref

When we update a custom hook we leave a stale reference in the Functional Components using this hook. We need to find a way to ensure we're calling forceUpdate() with the updated hook on these vnodes.

Let's say we update useCustomState.js to return a different callback, this is implemented in our App.js file. module.hot will trigger for useCustomState and we'll know this is connected to the App type and it's vnode, however if we'd just force the update of this vnode we'd be executing a function with the stale version of useCustomState. We'd have to force webpack to reevaluate this constructor (or need a way to get the new version of this constructor).

Concrete example:

export const useCounter = () => {
  const [count, setCount] = useState(0):
  return [count, () => { setCount(count + 1) }];
}
const Counter = () => {
  const [count, increment] = useCounter();
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

Changing the second argument to increment by 2 will successfully get accepted but the event-handler will still only increment by 1.

Next-integration

We need to figure out how to integrate with Next.js and PR it to this repository it would be a huge plus if we could do this in a plugin.

`@prefresh/[email protected]`: require is not defined

Hi there,

After upgrading from @prefresh/[email protected] to @prefresh/[email protected], the following lines are injected into the browser code, causing a reference error:

require('@prefresh/vite/runtime');
const { compareSignatures } = require('@prefresh/vite/utils');

This can be reproduced by running the following:

$ yarn create vite-app test --template preact && cd test
$ yarn && yarn upgrade --latest && yarn dev

Everything works fine when pinning the version to 0.8.1. Thanks! 👍

Kind regards,
Hampus Kraft.

Error recovery

We should be able to recover from runtime errors, this seems to be correctly handled in prefresh but the rerenderCount in Preact isn't correctly reset making renders not occur.

@prefresh/vite not working for non-root component

In the default vite template for preact, created with
npx create-vite-app preact-hmr --template preact, editing the default app.jsx works as expected and is reflected on the page instantly. Any new component created and used within app.jsx doesn't cause anything to change in the page when that new component is changed itself.

Posted in Vite. But thought maybe it might be directly related to this instead.
vitejs/vite#388 (comment)

Limit variable assignment

As rightfully pointed out in #18 we're assigning some var in the global scope, this makes it so that we could create intersections with things the user is defining. For that reason we should limit the amount of variable assignments to a minimum and give the ones we have left a prefixed name.

Vite integration

Vite is now partially supported, this mainly works well for class components and hooks will have some quirks due to the missing babel plugin.

Releasing under @prefresh/vite

UPDATE: will make a PR one of these days where we run a small babel step.

Window is not defined

Hi there,

Running into this error using the webpack integration with the react-refresh/babel plugin

packages/webpack/src/utils/createTemplate.js

// Execute the module function
const prevRefreshReg = window.$RefreshReg$; // ERROR: Window is undefined
const prevRefreshSig = window.$RefreshSig$;

I have tried modifying the globalObject in the webpack output to use this, but unfortunately still doesn't work and seems to break other things.

webpack.config below:

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OUTPUT_DIR = 'dist';
const PreactRefreshPlugin = require('@prefresh/webpack');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');

const mainEntries = [path.resolve(__dirname, './src/engine/client/index.tsx')];

// Exports
module.exports = {
  entry: {
    editor: mainEntries,
    av: path.resolve(__dirname, './src/engine/client/AV/AVIndex.tsx'),
    login: path.resolve(__dirname, './src/engine/client/Login/index.tsx'),
  },
  devServer: {
    disableHostCheck: true,
    port: 9000,
    hot: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
      'Access-Control-Allow-Headers':
        'X-Requested-With, content-type, Authorization',
    },
    proxy: {
      '/api': 'http://localhost:1234/dimsum/api',
    },
  },
  mode: 'development',
  stats: 'errors-only',
  target: 'web',
  devtool: 'eval-source-map',
  node: {
    fs: 'empty',
  },
  output: {
    path: path.join(__dirname, OUTPUT_DIR),
    pathinfo: false,
    // publicPath: '/',
    publicPath: 'http://localhost:9000/',
    filename: 'dimsum/static/[name].pkgd.js',
    chunkFilename: 'dimsum/static/[name].pkgd.js',
    jsonpFunction: 'dimsumCoreJsonpFunction',
  },
  resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.css'],
    modules: [path.resolve(__dirname, 'node_modules')],
    alias: {
      // 'react-dom': '@hot-loader/react-dom',
      react: 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
      'react-dom': 'preact/compat',
    },
  },

  optimization: {
    splitChunks: {
      // chunks: 'all',
      cacheGroups: {
        vendor: {
          chunks: 'initial',
          name: 'vendor',
          enforce: true,
          test: /node_modules/,
        },
      },
    },
  },

  // optimization: {
  //   // removeAvailableModules: false,
  //   // removeEmptyChunks: false,
  //   splitChunks: false,
  // },

  module: {
    rules: [
      {
        test: /\.worker\.ts$/,
        use: [
          {
            loader: 'worker-loader',
            options: { inline: true },
          },
          {
            loader: 'babel-loader',
            options: {
              babelrc: false,
              cacheDirectory: true,
              presets: [
                [
                  '@babel/env',
                  {
                    modules: false,
                    targets: {
                      browsers: ['last 2 versions'],
                    },
                  },
                ],
                '@babel/typescript',
                '@babel/react',
              ],
              plugins: [
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-proposal-optional-chaining',
                'react-refresh/babel',
              ],
            },
          },
        ],
      },
      {
        test: /\.(ts|tsx)$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              babelrc: false,
              cacheDirectory: true,
              presets: [
                [
                  '@babel/env',
                  {
                    modules: false,

                    targets: {
                      browsers: ['last 2 versions'],
                    },
                  },
                ],
                '@babel/typescript',
                '@babel/react',
              ],
              plugins: [
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-proposal-optional-chaining',
                'transform-mui-imports',
                'babel-plugin-styled-components',
                'react-refresh/babel',
              ],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        include: /node_modules/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(ttf|otf|woff|woff2)$/,
        loader: 'file-loader',
      },
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new PreactRefreshPlugin(),
    new MonacoWebpackPlugin({
      languages: ['json', 'html', 'scss'],
    }),
    new MiniCssExtractPlugin({
      filename: 'vendor.css',
      chunkFilename: '[id].css',
    }),
  ],
};

Add contributing docs

Mention the measures we're taking to support strict-package managers as well as a high-level introduction how all of this works.

Integration tests

We need puppeteer tests for

  • webpack
  • next.js
  • vite
  • snowpack
  • nollup

We need tests for refreshing:

  • custom hook
  • JSX

Improve detection

Currently we rely on detection because a Component is exported, this doesn't feel quite right to me. A better alternative would be to use the register function better.

This would allow us to detect non-exported Components within the same file, .... What we would have to do is only use module.hot to schedule a reload and then detect which components have changed with the register function.

A register means we will tie a function (Component) to an identifier (moduleId), this allows us to do lookups. So if we change ComponentX we can't look this up by type anymore but the identifier will have remained the same. When we see a register for a function with an id that already exists we can safely assume this is a hot-reloaded component for which we should schedule a hot-reload.

Ideally this should be done by depth but that doesn't feel 100% reliable at the moment.

Warn for infractions against best practices

When we see a file that exports a component as well as a custom hook (both can be identified with the @prefresh/utils exports) we could warn our user that this can lead to indeterministic behaviour.

These warnings will most likely drastically change with #130

Automated publishing

It's a lot of effort to publish at this moment, publish, add Release to GH, ... I'd love to make this a little easier, I don't mind manually publishing to enable 2FA security but some easing up would be awesome.

Vite beta-12, TypeError: Cannot read property '__e' of undefined

Error

catchError.js:8 Uncaught TypeError: Cannot read property '__e' of undefined
    at catchError.js:8

Happens in catchError.js

import { options } from '/@modules/preact/dist/preact.module.js';
import {
	CATCH_ERROR_OPTION,
	COMPONENT_DIRTY,
	VNODE_COMPONENT
} from '/@modules/@prefresh/core/src/constants.js';

const oldCatchError = options[CATCH_ERROR_OPTION]; // options is undefined here
options[CATCH_ERROR_OPTION] = (error, vnode) => {
	if (vnode[VNODE_COMPONENT] && vnode[VNODE_COMPONENT][COMPONENT_DIRTY]) {
		vnode[VNODE_COMPONENT][COMPONENT_DIRTY] = false;
	}

	if (oldCatchError) oldCatchError(error, vnode);
};

Somehow it seems that Preact is not imported correctly. 🤷 I have tried to find the reason, but so far without success. Vite has introduced a breaking change in beta-2 that the vite client path has changed from /vite/hmr to /vite/client. Maybe it is related.

Hooks (useState) invalidation

Replacing the declaration w with the comment in the next line doesn't reset the state.

export function App() {
  const [v] = useState(Math.random());

  const [w] = useState(Math.random());
  // const [w] = useState(Math.random() + 1);

  return (
    <div>
      App <br />
      {v} <br /> {w}
    </div>
  );
}

This might seem fine but if the datatype of the state changes and the value is used accordingly, you might get an invalid UI state.
(React Fast Refresh invalidates App's state in this case and also when renaming the variable name w to something else for the same reason.)

Investigate next constantly resetting hookState

Currently next seems to always reset hookState this could be due to missing react-refresh/babel, example app: developit/nextjs-preact-demo#16

It seems like Next.js handles modules differently and is using a different type of webpack injection. That's why our template is never injected doing the registration. We only have the hot handlers which will work fine but think that functional components always need full resets rather than statefull/stateless resets.

Next injection vs prefresh webpack injection

It seems like when in Next.js every filename is a function that returns an array of functions rather than an actual filename, the check for them whether or not to bind is not the filename extension but whether or not the module call is present.

The lines don't start with a module call but include it.

We'll probably need to write a custom plugin to properly support Next.

After fixing this I noticed there's another issue present when combined with preact-devtools the hookState is kept around. These seem to be reset here

Lifting up `displayName` example?

Hi there,

having problems getting this to work with preact/compat version using React.memo HOC.

If i have a component like:

export const Component = React.memo(function Foo(){
  return <h1>Yo</h1>;
});

I receive an error like cannot read displayName of undefined.

This also happens with React.forwardRef

Setting Componet.displayName = 'Foo'; does not solve it.

Any ideas?

Many thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.