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

Can't use typescript commands with ESM modules #421

Closed
davidthor opened this issue May 20, 2022 · 5 comments · Fixed by #422
Closed

Can't use typescript commands with ESM modules #421

davidthor opened this issue May 20, 2022 · 5 comments · Fixed by #422

Comments

@davidthor
Copy link

Hi there,

I'm trying to create an oclif CLI as an ESM package that uses typescript, but am having trouble with using commands with the .ts extension. The CLI works fine if my command files are .js files, but the minute I rename them to .ts I get the following error:

$ ./bin/dev.js 
(node:73944) [ERR_REQUIRE_ESM] Error Plugin: my-cli: Must use import to load ES Module: /cli/src/commands/create/index.ts
require() of ES modules is not supported.
require() of /cli/src/commands/create/index.ts from /cli/node_modules/@oclif/core/lib/module-loader.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.
Instead change the requiring code to use import(), or remove "type": "module" from /cli/package.json.

module: @oclif/core@1.8.2
task: toCached
plugin: my-cli
root: /cli
See more details with DEBUG=*
(Use `node --trace-warnings ...` to show where the warning was created)
The CLI

VERSION
  my-cli/2.0.0 darwin-x64 node-v16.15.0

USAGE
  $ cli [COMMAND]

TOPICS
  plugins  List installed plugins.

COMMANDS
  help     Display help for cli.
  plugins  List installed plugins.

I tried digging around in the module-loader doing things like adding the .ts extension to s_EXTENSIONS and the isPathModule() method, but only seem to be digging a deeper hole. Does anyone have an example of how this is expected to work?

Source

./bin/dev.js

#!/usr/bin/env node

import oclif from '@oclif/core';
import path from 'node:path';
import url from 'node:url';
import { register } from 'ts-node';

const project = path.join(path.dirname(url.fileURLToPath(import.meta.url)), '..', 'tsconfig.json');

// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development';

register({ project });

// In dev mode, always show stack traces
oclif.settings.debug = true;

// Start the CLI
oclif
  .run(void 0, import.meta.url)
  .then(oclif.flush)
  .catch(oclif.Errors.handle);

package.json

{
  "name": "my-cli",
  "version": "2.0.0",
  "description": "The CLI",
  "type": "module",
  "exports": {
    ".": "./dist/index.js"
  },
  "files": [
    "/bin",
    "/dist",
    "/npm-shrinkwrap.json",
    "/oclif.manifest.json"
  ],
  "bin": {
    "cli": "./bin/run"
  },
  "oclif": {
    "bin": "cli",
    "dirname": "cli",
    "commands": "./dist/commands",
    "plugins": [
      "@oclif/plugin-help",
      "@oclif/plugin-plugins"
    ],
    "topicSeparator": " "
  },
  "dependencies": {
    "@oclif/core": "^1.8.2",
    "@oclif/plugin-help": "^5.1.12",
    "@oclif/plugin-plugins": "^2.1.0",
    "ajv": "^8.11.0",
    "axios": "^0.27.2",
    "cli-table": "^0.3.11",
    "execa": "^6.1.0",
    "inquirer": "^8.2.4"
  },
  "devDependencies": {
    "@oclif/test": "^2.1.0",
    "@types/cli-table": "^0.3.0",
    "@types/inquirer": "^8.2.1",
    "prettier": "^2.6.2",
    "shx": "^0.3.4",
    "ts-node": "^10.7.0",
    "typescript": "^4.6.4"
  },
  "scripts": {
    "build": "shx rm -rf dist && tsc -b",
    "lint": "eslint . --ext .ts --config .eslintrc",
    "postpack": "shx rm -f oclif.manifest.json",
    "posttest": "npm run lint",
    "prepack": "npm run build && oclif manifest && oclif readme",
    "version": "oclif readme && git add README.md"
  }
}

./src/commands/create/index.ts

import { Command } from '@oclif/core';

export default class CreateCommand extends Command {
  static description = 'Create a new resource';

  async run() {}
}
@mdonnalley
Copy link
Contributor

@davidthor thanks for posting the issue - I'm looking into it now

@typhonrt
Copy link
Contributor

typhonrt commented May 20, 2022

Check this response... Looks like you need to use ts-node in the shebang of the run script. I haven't tried this myself, so no idea on how it will work w/ a mixed CLI project offhand. Worth a shot though.

#!/usr/bin/env node to #!/usr/bin/env ts-node

#372 (comment)

@mdonnalley
Copy link
Contributor

@typhonrt Thanks for the comment - that was the last piece of the puzzle I needed to solve this. Using the ts-node shebang works but we still need to support directory imports for typescript projects, which I made a PR for: #422

@davidthor Once that PR is merged you'll need to ensure that you have the following in your project:

  • the ts-node shebang in dev.js described above
  • Add the following to your tsconfig.json under compilerOptions:
    • "module": "ES2020",
    • "moduleResolution": "node",
  • Add a ts-node section to your tsconfig.json with the following:
    • { "esm": true }

@davidthor
Copy link
Author

davidthor commented May 20, 2022

Add a ts-node section to your tsconfig.json with the following:

  • { "esm": true }

Funnily enough I had tried everything done in the PR and your comment EXCEPT this addition to the tsconfig. That did the trick! Thanks!

---- Edit ----

In case anyone finds this later, I was having trouble with relative module imports even after adding this. What worked for me is changing the ./bin/dev.js file shebang line to the following:

#!/usr/bin/env node --loader ts-node/esm --experimental-specifier-resolution=node

The --experimental-specifier-resolution=node made my imports work again, and I had to use the ts-node/esm loader in node instead of the ts-node cli directly because the experimental flag isn't available in the ts-node cli. Hope that helps!

@tonyt-adept
Copy link

@davidthor Thanks a million for coming back with that edit.

Been struggling with Relative Imports all day getting this error:

(node:14697) [MODULE_NOT_FOUND] ModuleLoadError Plugin: <MY_CLI>: [MODULE_NOT_FOUND] import() failed to load <Failed relate import>

Adding your shebang modifiers got everything working!

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

Successfully merging a pull request may close this issue.

4 participants