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

EvalError: Unexpected token 'export' in #1022

Closed
ntucker opened this issue Jul 25, 2022 · 19 comments
Closed

EvalError: Unexpected token 'export' in #1022

ntucker opened this issue Jul 25, 2022 · 19 comments
Labels
bug report 🦗 Issue is probably a bug, but it needs to be checked bundler: webpack 📦 Issue is related to webpack bundler needs: complete repro 🖥️ Issue need to have complete repro provided

Comments

@ntucker
Copy link

ntucker commented Jul 25, 2022

Environment

  • Linaria version: 4.1.0
  • Bundler (+ version): webpack 5.78
  • Node.js version: 16
  • OS: ubuntu

Description

➤ YN0000: /home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:384
➤ YN0000:         throw new EvalError(`${e.message} in${callstack.join('\n| ')}\n`);
➤ YN0000:         ^
➤ YN0000: 
➤ YN0000: EvalError: Unexpected token 'export' in
➤ YN0000: | /home/circleci/project/node_modules/antd/es/index.js
➤ YN0000: | src/app/App.tsx
➤ YN0000: 
➤ YN0000:     at /home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:384:15
➤ YN0000:     at Array.forEach (<anonymous>)
➤ YN0000:     at Module.evaluate (/home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:361:10)
➤ YN0000:     at require.Object.assign.ensure (/home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:319:11)
➤ YN0000:     at src/app/App.tsx:1:46
➤ YN0000:     at src/app/App.tsx:2:3
➤ YN0000:     at Script.runInContext (node:vm:139:12)
➤ YN0000:     at /home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:368:16
➤ YN0000:     at Array.forEach (<anonymous>)
➤ YN0000:     at Module.evaluate (/home/circleci/project/node_modules/@linaria/babel-preset/lib/module.js:361:10)

Seems related to babel doing a transform it shouldn't?

Reproducible Demo

ntucker/anansi#1591

  1. git clone https://github.com/ntucker/anansi
  2. git checkout renovate/major-linaria
  3. yarn install
  4. yarn build:pkg
  5. cd examples/concurrent
  6. yarn build
@ntucker ntucker added bug report 🦗 Issue is probably a bug, but it needs to be checked needs: complete repro 🖥️ Issue need to have complete repro provided needs: triage 🏷 Issue needs to be checked and prioritized labels Jul 25, 2022
@github-actions github-actions bot added bundler: webpack 📦 Issue is related to webpack bundler and removed needs: triage 🏷 Issue needs to be checked and prioritized labels Jul 25, 2022
@Anber
Copy link
Collaborator

Anber commented Jul 25, 2022

Did it work in 4.0?

@ntucker
Copy link
Author

ntucker commented Jul 25, 2022

@Anber Didn't work in 4.0 either. Worked in last 3 beta tho.

@Anber
Copy link
Collaborator

Anber commented Jul 25, 2022

The key problem here is that since 4.0, Linaria started to use built-in file resolvers. In your case, Webpack resolves ES-version of Antd instead of CJS that causes Unexpected token 'export' when the resolved file is evaluated. As a quick workaround, you can specify a rule in Linaria config, which will force transformation for es-files:

    rules: [
        {
            action: require.resolve('@linaria/shaker'),
        },
        {
            test: /\/node_modules\//,
            action: 'ignore',
        },
        {
            // this one
            test: (filename, code) => {
                if (!/\/node_modules\//.test(filename)) {
                    return false;
                }

                return /(?:^|\n|;)\s*(?:export|import)\s+/.test(code);
            },
            action: require.resolve('@linaria/shaker'),
        },
    ],

I'll try to figure out how to solve this problem without forcing users to change configs.

@ntucker
Copy link
Author

ntucker commented Jul 25, 2022

Don't we want webpack to resolve ES versions? Why does it expect CJS?

@ntucker
Copy link
Author

ntucker commented Jul 25, 2022

@Anber doing your suggestion fixes that error and results in:

/home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:384
        throw new EvalError(`${e.message} in${callstack.join('\n| ')}\n`);
        ^

EvalError: (0 , _util.pad2) is not a function in
| /home/ntucker/src/anansi/node_modules/@ant-design/colors/dist/index.esm.js
| /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/utils.js
| /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/components/IconBase.js
| /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/components/AntdIcon.js
| /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/icons/CheckCircleFilled.js
| /home/ntucker/src/anansi/node_modules/antd/es/message/index.js
| /home/ntucker/src/anansi/node_modules/antd/es/config-provider/index.js
| /home/ntucker/src/anansi/node_modules/antd/es/layout/layout.js
| /home/ntucker/src/anansi/node_modules/antd/es/layout/index.js
| /home/ntucker/src/anansi/node_modules/antd/es/index.js
| src/app/App.tsx

    at /home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:384:15
    at Array.forEach (<anonymous>)
    at Module.evaluate (/home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:361:10)
    at require.Object.assign.ensure (/home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:319:11)
    at /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/utils.js:1:697
    at /home/ntucker/src/anansi/node_modules/@ant-design/icons/es/utils.js:3:3
    at Script.runInContext (node:vm:139:12)
    at /home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:368:16
    at Array.forEach (<anonymous>)
    at Module.evaluate (/home/ntucker/src/anansi/node_modules/@linaria/babel-preset/lib/module.js:361:10)

@Anber
Copy link
Collaborator

Anber commented Jul 25, 2022

Ok, I'll take a look in a couple of hours.

@Evalon
Copy link

Evalon commented Jul 26, 2022

Is it right that we need to traverse to another file because we should understand what type of the current symbol?
I think this problem will arise frequently in monorepos and shared components libraries. Maybe we can use some magic comment or do this check at runtime?

As a obvious workaround:

import { Button, ButtonProps } from 'ui-kit'

const getColor = (props: ButtonProps) => {
  return props.theme === 'info' ? 'blue' : 'green'
}

const ForwardButton = forwardRef<HTMLButtonElement, ButtonProps>(
  (props, ref) => <Button ref={ref} {...props} />,
)

export const StyledButton = styled(ForwardButton)`
  color: ${getColor};

But it's not really convenient. Maybe you have better solution in mind?

Anyway, great library! Thanks for your work :)

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

Don't we want webpack to resolve ES versions? Why does it expect CJS?

It's… hard. ESM currently isn't supported by browsers and nodejs. Especially in the form which is now widely used. However, it's straightforward to parse and process ESM instead of CJS (e.g. ⅔ of collectExportsAndImports.ts is for processing CJS). That's why bundlers prefer ESM and resolve ESM whenever it's possible. For me, it's also preferable to parse ESM instead of CJS (statical analysis of exports/imports in CJS is tricky and expensive) but to improve performance and stability, I tried to avoid excess work inside node_modules. It worked for Linaria <4 because it used nodejs resolver, which almost always returns already complied versions of a required library, but since 4.0, Linaria uses resolvers from bundlers that can resolve ESM/CJS/whatever-they-want. It looks like the best solution here would be to start to process all files that look like ESM, even if those files are in node_modules.

@ntucker
Copy link
Author

ntucker commented Jul 26, 2022

@Anber I guess I'm wondering if everyone supports and loves ESM - why does it need to be CJS? Maybe the problem is just that something is treating it like CJS when it should be treated like ESM.

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

@ntucker it must be CJM because it is impossible to evaluate ESM. You can't just take ESM code and tell node "hey, run this code and give me the result". Well, you can, but you will get the error from your first message :) So that, it have to be transpired to CJS.

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

Anyway, it's not a big deal to process files inside node_modules. For some bundlers it is the only way (Vite resolves everything in ESM). I'm just a bit stuck with #1021. When I resolve it, I'll release a fix for this bug as well.

@ntucker
Copy link
Author

ntucker commented Jul 26, 2022

Node 12+ supports ESM...how does that work?

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

It supports if it is an mjs file with fully specified imports. But if you check node_modules/antd/es/index.js in your project, you will see that the file itself is not mjs, and all imports inside don't have extensions.

@ntucker
Copy link
Author

ntucker commented Jul 26, 2022

I wonder if it's possible to avoid evaluation in case these are not used for any linaria computations.... for instance this my example imports from antd are only needed for the component itself.

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

That is what @linaria/shaker does. Unfortunately, sometimes heuristics don't work well, and some redundant code is kept alive.

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

I'll check why it tries to import antd. Thank you for pointing out that it is unnecessary here.

@Anber
Copy link
Collaborator

Anber commented Jul 26, 2022

@Evalon if we do it in runtime, we cannot be zero-runtime :)

@Anber
Copy link
Collaborator

Anber commented Jul 27, 2022

in case these are not used for any linaria computations

In that specific case antd is used, because styled have to know the type of component it wraps and that's why Linaria tries to evaluate it. However, inners of antd can be treeshaked a bit more effectively.

@WJCHumble
Copy link
Contributor

WJCHumble commented Mar 28, 2023

This problem has always existed on Windows, because the Regex configured by the default rules in linaria.config.js did not adapt to the Windows operating system.

Then, recently, I saw that someone fixed this problem #1225 a few days ago, but the latest version 4.4.3 of @linaria/babel-preset does not contain this modification yet. So, if someone encounters this problem on Windows now, it can be solved by configuring the following rules in linaria.config.js:

const nodeModulesRegExp = /[\\/]node_modules[\\/]/;

module.exports = {
    rules: [
        {
            action: require.resolve('@linaria/shaker'),
        },
        {
            test: nodeModulesRegExp,
            action: 'ignore',
        },
        {
            test: (filename, code) => {
                if (!nodeModulesRegExp.test(filename)) {
                    return false;
                }

                return /(?:^|\n|;)\s*(?:export|import)\s+/.test(code);
            },
            action: require.resolve('@linaria/shaker'),
        }
    ]
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug report 🦗 Issue is probably a bug, but it needs to be checked bundler: webpack 📦 Issue is related to webpack bundler needs: complete repro 🖥️ Issue need to have complete repro provided
Projects
None yet
Development

No branches or pull requests

4 participants