Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

.js in import of ESM #35

Closed
WordlessEcho opened this issue Mar 29, 2022 · 12 comments
Closed

.js in import of ESM #35

WordlessEcho opened this issue Mar 29, 2022 · 12 comments
Labels
bug Something isn't working library: typescript

Comments

@WordlessEcho
Copy link

I am using Yarn to download this package:

yarn add @material/material-color-utilities

After fixing #29 I got these error:

$ C:\files\src\hct\node_modules\.bin\ts-node-esm .\index.ts
C:\files\src\hct\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:383
    throw new ERR_MODULE_NOT_FOUND(
          ^
CustomError: Cannot find module 'C:\files\src\hct\node_modules\@material\material-color-utilities\dist\blend\blend' imported from C:\files\src\hct\node_modules\@material\material-color-utilities\dist\index.js
    at finalizeResolution (C:\files\src\hct\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:383:11)
    at moduleResolve (C:\files\src\hct\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:818:10)
    at Object.defaultResolve (C:\files\src\hct\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:929:11)
    at C:\files\src\hct\node_modules\ts-node\src\esm.ts:228:33
    at entrypointFallback (C:\files\src\hct\node_modules\ts-node\src\esm.ts:179:34)
    at resolve (C:\files\src\hct\node_modules\ts-node\src\esm.ts:227:12)
    at resolve (C:\files\src\hct\node_modules\ts-node\src\child\child-loader.ts:19:39)
    at ESMLoader.resolve (node:internal/modules/esm/loader:580:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:294:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

When I checking dist\index.js:

export * from './blend/blend';

I made some changes to this:

export * from './blend/blend.js';

After I adding .js to all imports. It works perfect to me.

More info: export - JavaScript | MDN

@wszgrcy
Copy link

wszgrcy commented Apr 25, 2022

same bug typescript is not support esm but build package with esm.....
we can use

export async function loadEsmModule<T>(modulePath: string|URL): Promise<T> {
  const namespaceObject =
      (await new Function('modulePath', `return import(modulePath);`)(modulePath));

  // If it is not ESM then the values needed will be stored in the `default` property.
  // TODO_ESM: This can be removed once `@angular/*` packages are ESM only.
  if (namespaceObject.default) {
    return namespaceObject.default;
  } else {
    return namespaceObject;
  }
}

like this but is not perfect

@victorandree
Copy link

FWIW, Node.js requires file extensions and it's likely required in a browser environment, too. Skipping them is really only a feature of various packagers. From "import specifiers – Mandatory file extensions" in Node.js API docs for ECMAScript modules:

A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. './startup/index.js') must also be fully specified.

This behavior matches how import behaves in browser environments, assuming a typically configured server.

Some more information on this can be read in the TypeScript 4.7 Beta announcement:

This code works in CommonJS modules, but will fail in ES modules because relative import paths need to use extensions. As a result, it will have to be rewritten to use the extension of the output of foo.ts – so bar.ts will instead have to import from ./foo.js.

Basically, the suggested fix is to add .js to all imports, even in TypeScript code:

This might feel a bit cumbersome at first, but TypeScript tooling like auto-imports and path completion will typically just do this for you.

That would fix it for Node.js. I'm not sure if it would break stuff in e.g. Webpack.

For now, I'm using a loader when using this package with Node.js. Use it like this: node --experimental-loader=./module-loader.mjs <script>

/**
 * This module contains loader hooks to force loading `@material/material-color-utilities` as ECMAScript modules, even though its imports lack file endings.
 */

import { createRequire } from 'module';

const PACKAGE_NAME = '@material/material-color-utilities';

/**
 * `resolve` loader hook to force loading `@material/material-color-utilities` as ECMAScript modules.
 *
 * The package's imports lack file endings (e.g. ".js"), so we need to resolve them ourselves. This requires a loader hook.
 *
 * @param specifier {string}
 * @param context {{ conditions: string[], parentURL?: string }}
 * @param defaultResolve {any}
 * @returns {{ format: string, url: string }}
 */
export async function resolve(specifier, context, defaultResolve) {
  const specifierInsidePackage =
    context.parentURL && context.parentURL.includes(PACKAGE_NAME);

  if (specifierInsidePackage) {
    const require = createRequire(context.parentURL);
    const url = new URL(require.resolve(specifier), 'file://').toString();

    return {
      format: 'module',
      url,
    };
  }

  return defaultResolve(specifier, context, defaultResolve);
}

@pschiffmann
Copy link

AFAICT, TypeScript emits imports in the same style as they appear in the source file. That means to fix this issue, you just need to append .js to all import and export declarations in typescript/**/*.ts files. E.g. file index.ts should look like this:

export * from './blend/blend.js';
export * from './hct/cam16.js';
export * from './hct/hct.js';
...

@pschiffmann
Copy link

When you get this typescript error:

./node_modules/@material/material-color-utilities/dist/index.d.ts:17:15 - error TS2835: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Did you mean './blend/blend.js'?

17 export * from './blend/blend';

Then as a workaround, pin the package to version 0.1.1 in your package.json, like this:

{
  "dependencies": {
    "@material/material-color-utilities": "0.1.1"
  }
}

This workaround will be unnecessary when the fix from my previous comment is implemented and published on npm.

@hungtcs
Copy link

hungtcs commented Apr 28, 2022

If webpack is used, configuring fullyspecificed can solve

{
  test: /\.m?js$/,
  resolve: {
    fullySpecified: false,
  },
},

@immjs
Copy link

immjs commented May 22, 2022

Hello, I wish to know if a pull request for this issue would be accepted anyway, given its chore-esque nature

@WordlessEcho
Copy link
Author

@immjs Hi. I don't know about that. You can check pr history for details.

@Nevro
Copy link

Nevro commented Jun 26, 2022

This command will append .js to all import and export declarations in all js files

find './node_modules/@material/material-color-utilities' -type f -name '*.js' -exec \
sed -Ei '/(import|export)/s/(\x22|\x27)(\.{1,2}\/(([[:alnum:]_-]+|\.{2})\/)*[[:alnum:]_-]+)(\.js)?\1/\1\2.js\1/g' {} +

Execute in project directory!

if you want it to be done automatically after npm upgrade or npm rebuild, add postinstall script to your project package.json

{
  "type": "module",
  "dependencies": {
    "@material/material-color-utilities": "^0.1.2"
  },
  "scripts": {
    "postinstall": "find './node_modules/@material/material-color-utilities/' -type f -name '*.js' -exec sed -Ei '\/(import|export)\/s\/(\\x22|\\x27)(\\.{1,2}\\\/(([[:alnum:]_-]+|\\.{2})\\\/)*[[:alnum:]_-]+)(\\.js)?\\1\/\\1\\2.js\\1\/g' {} +"
  }
}

EDIT:
Improved "parent" path validation!
https://regex101.com/r/OUTBH4/2

@omkar-nath
Copy link

omkar-nath commented Aug 8, 2022

I am facing the same issue. I am using rollup for bundling. Can someone guide, how can I resolve this?

Did you mean 'blend.js'?
BREAKING CHANGE: The request './blend/blend' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.```

@kwaa
Copy link

kwaa commented Aug 13, 2022

Hi all, since @material/material-color-utilties not accepting external contributions, I created a fork @importantimport/material-color-utilties.

It replaces tsc with unbuild to fix this issue and support CJS, and it works fine for now. (only partially tested)

feel free to use it!

@WillsterJohnson
Copy link

Is there any word from the Material Foundation team regarding this?
Seems like it would be at least on their radar, considering a design choice has resulted in the package being unusable in most development environments (see: everyone's using a framework).

@guidezpl
Copy link
Collaborator

Thanks for your patience. This will be fixed in the next release, closing

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working library: typescript
Projects
None yet
Development

Successfully merging a pull request may close this issue.