xregexp: Import does not work anymore since version 4.4.0

We use xregexp in a shared library that is used in our client (Angular 10) as well as on our server (NodeJS / Webpack 5). Until version 4.3.0 we were able to import xregexp like this and everything worked as expected:

const XRegExp = require('xregexp');

Now we wanted to upgrade to version 4.4.0, but get the following error in the client application (Angular 10):

TypeError: XRegExp.matchRecursive is not a function

The server still works as expected.

Changing the import statement to const XRegExp = require('xregexp').default; solves the problem in the client but leads to this error on the server: TypeError: Cannot read property 'matchRecursive' of undefined.

We also tried import * as XRegExp from 'xregexp'; but this does not work either. Any ideas why this suddenly does not work anymore?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 20 (8 by maintainers)

Commits related to this issue

Most upvoted comments

Alright, I just published v4.4.1 with the fix from https://github.com/slevithan/xregexp/pull/308, can y’all confirm it works for you?

Actually, after reading https://webpack.js.org/configuration/resolve/#resolvemainfields more closely, it sounds like adding a browser field to package.json, that points to lib/index.js (same as main) should fix things.

resolve.mainFields [string]

When importing from an npm package, e.g. import * as D3 from ‘d3’, this option will determine which fields in its package.json are checked. The default values will vary based upon the target specified in your webpack configuration.

When the target property is set to webworker, web, or left unspecified:

webpack.config.js

module.exports = { //… resolve: { mainFields: [‘browser’, ‘module’, ‘main’] } }; For any other target (including node):

webpack.config.js

module.exports = { //… resolve: { mainFields: [‘module’, ‘main’] } }; For example, consider an arbitrary library called upstream with a package.json that contains the following fields:

{ “browser”: “build/upstream.js”, “module”: “index” } When we import * as Upstream from ‘upstream’ this will actually resolve to the file in the browser property. The browser property takes precedence because it’s the first item in mainFields. Meanwhile, a Node.js application bundled by webpack will first try to resolve using the file in the module field.

I’ll open a PR for that.

Hey all, maintainer who merged https://github.com/slevithan/xregexp/pull/281 here. I’m still wrapping my head around this issue, but I tend to agree with the following comments in that this is a webpack bug and/or configuration issue:

https://github.com/slevithan/xregexp/issues/305#issuecomment-729712632

If you’re using webpack and not using esm, you should probably configure webpack to ignore module main fields since you are going against the grain.

webpack.js.org/configuration/resolve/#resolvemainfields

https://github.com/slevithan/xregexp/issues/305#issuecomment-729763947

Another option to try in the meantime, is to add xregexp to the externals so that webpack doesn’t bundle it and lets node process the require().


Here’s why: I installed Node 14 (to get module support), and ran both import and require versions of the code, getting the same result from each (see the Details block below). If webpack is supposed to make it so that you can use the module in the browser, the same way that you can from the server, then it doesn’t seem to be working properly here, and needs to be configured differently, as @jsg2021 suggested in their comments above.

Let me know if I’ve overlooked anything. I do realize that even if webpack is technically wrong here, it might be easier to just work around it, rather than requiring XRegExp users to change their code, so I’m certainly still open to that.

$ node --version
v14.13.1
$ cat test.mjs
───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: test.mjs
───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ import XRegExp from 'xregexp';
   2   │ console.log(XRegExp)
   3   │ console.log(XRegExp())
───────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
$ node test.mjs
[Function: XRegExp] {
  version: '4.4.0',
  _clipDuplicates: [Function: clipDuplicates],
  _hasNativeFlag: [Function: hasNativeFlag],
  _dec: [Function: dec],
  _hex: [Function: hex],
  _pad4: [Function: pad4],
  addToken: [Function (anonymous)],
  cache: [Function (anonymous)] { flush: [Function (anonymous)] },
  escape: [Function (anonymous)],
  exec: [Function (anonymous)],
  forEach: [Function (anonymous)],
  globalize: [Function (anonymous)],
  install: [Function (anonymous)],
  isInstalled: [Function (anonymous)],
  isRegExp: [Function (anonymous)],
  match: [Function (anonymous)],
  matchChain: [Function (anonymous)],
  replace: [Function (anonymous)],
  replaceEach: [Function (anonymous)],
  split: [Function (anonymous)],
  test: [Function (anonymous)],
  uninstall: [Function (anonymous)],
  union: [Function (anonymous)],
  tag: [Function (anonymous)],
  build: [Function (anonymous)],
  matchRecursive: [Function (anonymous)],
  addUnicodeData: [Function (anonymous)],
  _getUnicodeProperty: [Function (anonymous)]
}
/(?:)/ { xregexp: { captureNames: null, source: '', flags: '' } }
$ cat test.js
───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: test.js
───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ const XRegExp = require('xregexp');
   2   │ console.log(XRegExp)
   3   │ console.log(XRegExp())
───────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
$ node !$
node test.js
[Function: XRegExp] {
  version: '4.4.0',
  _clipDuplicates: [Function: clipDuplicates],
  _hasNativeFlag: [Function: hasNativeFlag],
  _dec: [Function: dec],
  _hex: [Function: hex],
  _pad4: [Function: pad4],
  addToken: [Function (anonymous)],
  cache: [Function (anonymous)] { flush: [Function (anonymous)] },
  escape: [Function (anonymous)],
  exec: [Function (anonymous)],
  forEach: [Function (anonymous)],
  globalize: [Function (anonymous)],
  install: [Function (anonymous)],
  isInstalled: [Function (anonymous)],
  isRegExp: [Function (anonymous)],
  match: [Function (anonymous)],
  matchChain: [Function (anonymous)],
  replace: [Function (anonymous)],
  replaceEach: [Function (anonymous)],
  split: [Function (anonymous)],
  test: [Function (anonymous)],
  uninstall: [Function (anonymous)],
  union: [Function (anonymous)],
  tag: [Function (anonymous)],
  build: [Function (anonymous)],
  matchRecursive: [Function (anonymous)],
  addUnicodeData: [Function (anonymous)],
  _getUnicodeProperty: [Function (anonymous)]
}
/(?:)/ { xregexp: { captureNames: null, source: '', flags: '' } }
$

For the OP, use:

import XRegExp from 'xregexp';

If you’re using webpack and not using esm, you should probably configure webpack to ignore module main fields since you are going against the grain.

https://webpack.js.org/configuration/resolve/#resolvemainfields

This bug was introduced by the pull request https://github.com/slevithan/xregexp/pull/281 with this commit https://github.com/slevithan/xregexp/commit/99ee554835ac7a54bf2d73697601fe5520e97b06 by adding "module": "./src/index.js"

So lets see why this is causing issues.
Bundlers like webpack which support CommonJs and ESModules will start using ESModules from xregexp 4.4.0 instead of the previous CommonJs integration from 4.3.0.

require('xregexp') // --> function (commonJs)

After the change we get an object instead which includes the function as a default property:

require('xregexp') // -> { esModule: true, default: function } (esModules)

@slevithan can we just revert #281 ?