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

The documented CJS import doesn't work with typescript #133

Open
henrify opened this issue Feb 8, 2022 · 21 comments
Open

The documented CJS import doesn't work with typescript #133

henrify opened this issue Feb 8, 2022 · 21 comments

Comments

@henrify
Copy link

henrify commented Feb 8, 2022

Hi, the docs start with saying that CommonJS version is rooted at "./cjs/nats.js", however, that cannot be imported directly from TypeScript, as there are no typings.

For CommonJS + TypeScript, the only import I got to work was:
import {connect, NatsConnection} from "nats.ws/lib/src/mod.js";

Is that the correct way?

Plus obviously it would be good to fix the basics -- use a more standard package layout that is easy and obvious to import, and document clearly how to import for modern stuff for those who still fail at the easy and obvious (currently the "importing the module" section of the docs only talks about using a <script> tag, which admittedly was a great and popular way in the 1990s and even early 2000s, but in 2022 not so much anymore).

@aricart
Copy link
Member

aricart commented Feb 9, 2022

you should be able to just import {connect} from "nats.ws", you'll need to configure your IDE to look at the types - which are on "types": "lib/src/mod.d.ts" - what IDE are you using?

@aricart
Copy link
Member

aricart commented Feb 9, 2022

If you have suggestions for other import conditions, that would be awesome - please submit a PR showing how you consume it. On more complex stacks the imports are fairly automatic and match the simple import cases shown. The bundlers in those stacks are able to decipher what to do.

@henrify
Copy link
Author

henrify commented Feb 9, 2022

Thanks for your fast reply @aricart but the CommonJS + TypeScript story doesn't go like that...

  1. Your example import {connect} from "nats.ws" will try to import the ESM module and fails.

  2. It is not possible to "configure IDE to look at the types". All IDEs rely on TypeScript compiler, and without type information TypeScript compiler fails to compile. And even if "configuring IDE" would somehow work, then building/bundling would anyway fail, so no point in configuring IDE.

  3. We use create-react-app v4 based build/bundling stack, and I can confirm that it does NOT somehow magically "decipher what to do" or "match import cases", but fails to compile (unless importing the completely non-obvious and non-documented "nats.ws/lib/src/mod.js" - which was the complaint in my original issue).

  4. I don't understand what you mean by other import "conditions"?

Anyway, is there a strong reason why you can't setup this library in a more standard way?

I think import {connect} from "nats.ws/cjs would be already pretty decent and doable in 60 seconds?

@aricart
Copy link
Member

aricart commented Feb 9, 2022

@henrify take a look at #116

if you can help make the types show up better (mind you they do for me in my environment), that would be great.

@henrify
Copy link
Author

henrify commented Feb 9, 2022

Sure, I'll have a look.

What is your environemnt then?

@aricart
Copy link
Member

aricart commented Feb 9, 2022

webstorm - I'll do a simple script to create an environment in react.

@henrify
Copy link
Author

henrify commented Feb 9, 2022

Okay I had a look at the linked issue and the library structure.

  • The linked issue doesn't seem to talk much about typescript, which is the main point of this issue.

  • Getting plain "nats.ws" import to work from CommonJS/Babel/WebPack seems challenging. I think what is happening when importing is that the ESM's "module" property in package.json takes precedence over the CommonJS "main" property -- even when WebPack is not configured to handle ESM, causing the compilation to fail.

  • Getting "nats.ws/cjs" import to work was as simple as I thought: renaming cjs/nats.js to cjs/index.js and then copying mods.d.ts to cjs/index.d.ts worked on the first try.

@aricart
Copy link
Member

aricart commented Feb 9, 2022

so the typings are processed by the IDE normally - otherwise, the completions wouldn't be able to figure out the structure - and open the .ts.d files, here's a simple repo - yes it doesn't install the typescript machinery, but it does create a react project, and when started everything works. So at least for top of the world react - it seems to do the right thing. Perhaps you can help me expand on this and verify on your side.

See https://github.com/aricart/npmscripts

In my case clicking on the connect function correctly opens ~/<stuffhere>/npmscripts/natsws-react/node_modules/nats.ws/lib/src/connect.d.ts just as expected

@aricart
Copy link
Member

aricart commented Feb 9, 2022

@henrify - just really want to get to the bottom of this - because previous bundles were problematic - these run as is - and indeed I suspect that somewhere there's some issue with WebPack and typescript compilers - check out this note as well to make sure we are in sync - https://github.com/nats-io/nats.ws/blob/main/developer_notes.md#older-typescript-compiler

@aricart
Copy link
Member

aricart commented Feb 9, 2022

image

@henrify
Copy link
Author

henrify commented Feb 9, 2022

@aricart that is the expected behavior.

When importing from "nats.ws" the d.ts typings are found just fine, but building / bundling for CommonJS fails, because the "nats.ws" is resolves to ESM module, and ESM modules cannot be used from CommonJS.

@aricart
Copy link
Member

aricart commented Feb 10, 2022

are you running in the server?

@aricart
Copy link
Member

aricart commented Feb 10, 2022

Not trying to antagonize you - but built project works for me in react.
image

image

@aricart
Copy link
Member

aricart commented Feb 10, 2022

Indeed the react project is javascript - let me create a version that uses typescript, but I would expect the bundler to do exactly what it did.

@henrify
Copy link
Author

henrify commented Feb 10, 2022

Regarding the environment: If you mean WebStorm as in the JetBrains IDE, then that is not really your "environment "in the meaning and context of bundling/building -- WebStorm delegates to something else under the hood to do the build (tsc, webpack, etc.), and what that something else is determines whether import .. "nats.ws" format works or not.

Regarding your new test project that imports successfully: create-react-app has just (couple of weeks ago?) had a major release (v5) and it is possible that they have changed the build / bundling stack so that v5 can import ESM modules (but 99.9% of existing projects still use v4 which does not work with ESM modules). Your test project should probably use the older and dominant v4 to make it relevant for existing projects (but of course testing also with v5 would be good).

@aricart
Copy link
Member

aricart commented Feb 10, 2022

@henrify - I did some more digging with version 4 - never mind that you cannot run create-react-app version 4.0.3 - the createReactApp.js effectively will refuse to run if the 5 is available - regardless of whether the global is installed or not - I bypassed this issue by editing the script.

checkForLatestVersion()
    .catch(() => {
      try {
        return execSync('npm view create-react-app version').toString().trim();
      } catch (e) {
        return null;
      }
    })
    .then(latest => {
      if (latest && semver.lt(packageJson.version, latest)) {
        console.log();
        console.error(
          chalk.yellow(
            `You are running \`create-react-app\` ${packageJson.version}, which is behind the latest release (${latest}).\n\n` +
              'We no longer support global installation of Create React App.'
          )
        );
        console.log();
        console.log(
          'Please remove any global installs with one of the following commands:\n' +
            '- npm uninstall -g create-react-app\n' +
            '- yarn global remove create-react-app'
        );
        console.log();
        console.log(
          'The latest instructions for creating a new app can be found here:\n' +
            'https://create-react-app.dev/docs/getting-started/'
        );
        console.log();
        process.exit(1);
      } else {
        createApp(
          projectName,
          program.verbose,
          program.scriptsVersion,
          program.template,
          program.useNpm,
          program.usePnp
        );
      }
    });

image

image

image

I was able to run it - note it is not doing typescript - I didn't go there because that further adds another layer - but at the react level, the current bundling for nats.ws is accessible to react 4 and 5

@henrify
Copy link
Author

henrify commented Feb 10, 2022

Hmm, what was the exact import statement you used?

@aricart
Copy link
Member

aricart commented Feb 10, 2022

import React, { useEffect, useState } from "react";
import { connect } from "nats.ws";
...

@henrify
Copy link
Author

henrify commented Feb 10, 2022

Best guess...

With Javascript, the 'import' statement is kept as is, which means that you are using browser/node native ESM support, and therefore loading the ESM module works.

With TypeScript, the 'import' statement is transpiled to CommonJS 'require()' call, which tries to load the same ESM module file, and therefore fails.

Try with typescript? (it's just npx create-react-app my-app --template typescript)

@aricart
Copy link
Member

aricart commented Feb 10, 2022

will do - but realize again the old create app is not supported anymore, and yes tsc and bundlers have received updates. Going the other way is not correct - I believe that if you do the "expanded" import where you point it at the file, it will do the right thing for you.

@christiaangoossens
Copy link

I believe something like this can be written in the package.json to add typing automatically:


  "exports": {
    ".": {
      "import": {
        "types": "./lib/src/index.d.ts",
        "default": "./esm/nats.js"
      },
      "require": {
        "types": "./lib/src/index.d.ts",
        "default": "./cjs/nats.js"
      }
    }
  },

Instead of the current:

"exports": {
  ".": {
    "require": "./cjs/nats.js",
    "import": "./esm/nats.js"
  }
},

This would require creating an index.d.ts file, but tsc can do this automatically.

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

No branches or pull requests

3 participants