khmyznikov / pwa-install

Installation dialog for Progressive Web Application. Provides a more convenient user experience and fixes the lack of native dialogs in some browsers.
https://www.khmyznikov.com/pwa-install
MIT License
358 stars 59 forks source link

[Question] Typescript Support for React Proxy #46

Closed spencerdezartsmith closed 3 months ago

spencerdezartsmith commented 7 months ago

Do you have support for typescript that is available for the React Proxy you have provided?

Thanks!

lnhrdt commented 6 months ago

I found this issue looking for the same thing.

The docs suggest importing the wrapper using import PWAInstall from '@khmyznikov/pwa-install/dist/pwa-install.react.js' and there's no type definitions.

I see the react wrapper is declared in src/fallback/react.ts but it doesn't seem to be exported properly in index.ts. I also see the TypeScript source files are surprisingly included in the bundle but when I tried to import them directly using import PWAInstall from '@khmyznikov/pwa-install/src/fallback/react' the result is of type any.

@spencerdezartsmith I see you marked this as completed, did you find a solution?

khmyznikov commented 5 months ago

@lnhrdt for react wrapper this types was generated https://github.com/khmyznikov/pwa-install/blob/main/dist/types/fallback/react.d.ts

anilanar commented 4 months ago

@khmyznikov I suggest you try using this package on codesandbox with react+typescript template and see why it doesn't work. You either need to adapt package.json and define multiple exports like https://www.kravchyk.com/typescript-npm-package-json-exports/ or you need to move files in dist in such a way that react.js and react.d.ts are in the same directory so that something like import PWAInstall from '@khmyznikov/pwa-install/dist/react' works.

khmyznikov commented 4 months ago

@anilanar thanks for hints. I have changed how the react wrapper is built, can you try to use latest commit like this in package.json: "@khmyznikov/pwa-install": "github:khmyznikov/pwa-install" and check how it works for you? @lnhrdt @spencerdezartsmith also calling you to help verify the fix.

anilanar commented 4 months ago

@khmyznikov The disadvantage of your fix (using exports) is that it requires the relatively new "Node16" (or "NodeNext") moduleResolution in tsconfig. Many projects don't use that new module resolution yet, instead they use "node". It's great that you have exports in package.json, but I think you need to put react.js and react.d.ts next to each other for other users.

khmyznikov commented 4 months ago

@anilanar should be doable. But, even "new" node16 security support ended 8 months ago. It's actually old 😅

anilanar commented 4 months ago

@khmyznikov You are totally right but... Listen to me for a moment 😄

Most FE projects are single-page apps and use just a bundler like webpack or vite with ES modules. They don't use node.js runtime. Node.js implemented ESMs with their own "flavor" that contradicted all existing code that used ES modules. All imports must contain file extensions like import foo from './foo.js'. Typescript, years later, added support for that with a big BUT: you need to import ts files with a js extension. So if you have foo.ts, it must be imported like import foo from './foo.js'. With TS 5, they tried to fix that by introducing moduleResolution: bundler. Now you can import either without extensions or with a ts extension, again a big BUT, you cannot use async imports! It doesn't end there and there are even more problems, but this should be sufficient for convincing 😃

Therefore, most FE codebases still use moduleResolution: node because there's no reason to use something else, at least for now. In future, if libraries start replacing main, module, types fields with exports, then yes, everybody will have to play along.

TL;DR node.js fucked up by adding ESMs too late, many years after people adopted ESM. And when they added ESM support, they changed the rules. So nice of them! TS kind of fucked up by adding node+ESM support many years after node added ESM support. They went along with node's rules AND with their insistence on the extension topic: https://github.com/microsoft/TypeScript/issues/49083#issuecomment-1435399267 Libraries and actual users got hurt and lost their respect for ESM.

anilanar commented 4 months ago

BTW it seems the following will be fixed with TS 5.5 soonish:

they tried to fix that by introducing moduleResolution: bundler ... you cannot use async imports!

khmyznikov commented 4 months ago

@anilanar can you check again for moduleResolution: node ? Works ok on my test project.

khmyznikov commented 3 months ago

I think the react types finally fixed

anilanar commented 3 months ago

I'll work on this area again this week, I'll give it a try 👍

khmyznikov commented 3 months ago

@anilanar wrapper code shouldn't be needed.

import React, { useEffect, useRef, useState } from 'react';

import PWAInstall from '@khmyznikov/pwa-install/react-legacy';
import { PWAInstallElement } from '@khmyznikov/pwa-install';

function App() {
  const appName = 'My PWA';

  const [promptEvent, setPromptEvent] = useState(null);
  const pwaInstallRef = useRef<PWAInstallElement>(null);

  useEffect(() => {
      // @ts-ignore
    let lastPromptEvent = window.promptEvent;

    const intervalId = setInterval(() => {
        // @ts-ignore
      if (window.promptEvent !== lastPromptEvent) {
          // @ts-ignore
        lastPromptEvent = window.promptEvent;
          // @ts-ignore
        setPromptEvent(window.promptEvent); // Update state with new value
      }
    }, 100); // Check every 100ms

    // Cleanup interval on component unmount
    return () => {
      clearInterval(intervalId);
    };
  }, []);

  return (
    <div className="App">
      <header className="App-header">

        <button onClick={() => pwaInstallRef.current?.install()}>Install</button>
        <button onClick={() => pwaInstallRef.current?.hideDialog()}>Hide</button>
        <button onClick={() => pwaInstallRef.current?.showDialog(true)}>Show</button>

        <PWAInstall ref={pwaInstallRef} externalPromptEvent={promptEvent} name={appName} onPwaInstallAvailableEvent={(event) => console.log(event)}></PWAInstall>
      </header>
    </div>
  );
}

export default App;

Sample with externalPromptEvent in async mode.

anilanar commented 2 months ago

@khmyznikov Sadly it doesn't work.

Here's a repro I initialized with tsc --init and npm init -y and just changed module to esnext and moduleResolution to node.

https://github.com/anilanar/pwa-install-ts-repro

khmyznikov commented 2 months ago

@anilanar not sure how to fix old module resolution. Here's a fully working sample: https://stackblitz.com/edit/vite-react-ts-2eeiak?file=src%2FApp.tsx