parcel-bundler / parcel

The zero configuration build tool for the web. 📦🚀
https://parceljs.org
MIT License
43.48k stars 2.27k forks source link

Bundle is missing exports #7622

Open halvardssm opened 2 years ago

halvardssm commented 2 years ago

🐛 bug report

Hi! I am trying to refactor an npm package from js/jquery to ts, and in the update process, I wanted to use Parcel as bundler due to its ease of use and zero config. When using parcel build, the code gets generated, but functions and enums are not accessible in the bundle nor exported.

🎛 Configuration (.babelrc, package.json, cli command)

package.json

{
  "packageManager": "yarn@3.1.1",
  "name": "clippy.js",
  "version": "2.0.0",
  "source": "lib/index.ts",
  "main": "dist/main.js",
  "module": "dist/module.js",
  "types": "dist/types.d.ts",
  "files": [
    "dist/*"
  ],
}

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ES3",
    "strict": true,
    "allowJs": true,
    "checkJs": true,
    "sourceMap": true,
    "declaration": true,
    "outDir": "dist",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "lib": ["esnext","DOM"]
  },
  "include": ["lib"]
}

🤔 Expected Behavior

Enums and functions should be exported from and be accessible in the bundle

😯 Current Behavior

Uncaught (in promise) TypeError: $dF6ai.getAgent is not a function
    $38cce2cce4d8c489$export$74cd1f32039e2d7f clippy.ts:13
    App index.9978ec32.js:1018
    React 5
    unstable_runWithPriority scheduler.development.js:468
    React 3
    workLoop scheduler.development.js:417
    flushWork scheduler.development.js:390
    performWorkUntilDeadline scheduler.development.js:157
    L7NvB scheduler.development.js:180
    L7NvB scheduler.development.js:645
    newRequire index.9978ec32.js:71
    localRequire index.9978ec32.js:84
    k1C1m index.js:6
    newRequire index.9978ec32.js:71
    localRequire index.9978ec32.js:84
    React 2
    newRequire index.9978ec32.js:71
    localRequire index.9978ec32.js:84
    ["1mpsW"]< React
    newRequire index.9978ec32.js:71
    localRequire index.9978ec32.js:84
    ipvmp index.tsx:1
    newRequire index.9978ec32.js:71
    <anonymous> index.9978ec32.js:122
    <anonymous> index.9978ec32.js:145

💁 Possible Solution

🔦 Context

💻 Code Sample

https://github.com/halvardssm/clippy.js

To reproduce:

  1. Clone and checkout branch parcel-repro
  2. Run yarn install
  3. Run yarn pack
  4. Add "clippy": "file:./clippy.js-v2.0.0.tgz", to deps
  5. Run yarn install
  6. Run yarn start and visit the page in the browser
  7. Inspect the console to see errors.

To verify that the issues are only happening when bundling with parcel, checkout branch refactor and start the app.

🌍 Your Environment

Software Version(s)
Parcel 2.2.0
Node 16.13.2
npm/Yarn Yarn 3.1.1 PNP
Operating System MacOS & Windows 11
CalamityAdam commented 2 years ago

I am having the same issue. parts of a dependency are being "stripped away" during build and do not exist in the bundle. they do, however, exist in the source maps. so local development works fine. but when using just the bundle (via the build output), pieces are completely missing.

seems to be a possible duplicate of #7580 and #7555

🐛 bug report

using redux-toolkit query in a react 17 app, one of the functions available as an export from @reduxjs/toolkit/query/react, fetchBaseQuery, can not be found in the build, with the following error being thrown in the browser:

Uncaught TypeError: Gh.fetchBaseQuery is not a function
strangely, when looking through the build's bundles, the definition for that function can be found in the source maps. however, in the bundled javascript file, the only reference to fetchBaseQuery can be found in the line causing the above error to be thrown. I can see when Gh is defined and built out, but fetchBaseQuery is not a part of it. furthermore, (referencing the following screenshots) it appears as if fetchBaseQuery should be found on qh instead of Gh, based on the bundle. description screenshot
bundled source code line causing the error to be thrown source code line throwing error
bundled source code showing where Gh is defined source code showing where `Gh` is defined
source mapped output showing the original code (where error is being thrown) source mapped code showing original code throwing error

🎛 Configuration (.babelrc, package.json, cli command)

babel.config.json (only used for jest tests)

{
  "env": {
    "test": {
      "presets": [
        "@parcel/babel-preset-env",
        "@babel/preset-typescript",
        "@babel/preset-react"
      ]
    }
  }
}

.parcelrc.json

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
      "@parcel/transformer-babel",
      "@parcel/transformer-js",
      "@parcel/transformer-react-refresh-wrap"
    ]
  }
}

build script:

"build": "parcel build src/index.html --public-url /app --no-source-maps",

package.json (everything is up to date):

{
  ...
  "dependencies": {
    "@auth0/auth0-react": "^1.9.0",
    "@optimizely/react-sdk": "^2.8.0",
    "@ramsey-design-system/button": "^2.0.1",
    "@ramsey-design-system/icon": "^2.0.0",
    "@ramsey-design-system/icons": "^2.3.1",
    "@ramsey-design-system/link": "^1.3.1",
    "@reduxjs/toolkit": "^1.7.1",
    "apophenia-javascript-logger": "^2.0.3",
    "gazelle-design-system": "^1.16.3",
    "postcss-modules": "^4.3.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.6",
    "react-router-dom": "^6.2.0",
    "redux": "^4.1.2",
    "styled-components": "^5.3.3",
    "typescript": "^4.5.5"
  },
  "scripts": {
    "clean": "rm dist/* .parcel-cache/* || true",
    "build": "parcel build src/index.html --public-url /app --no-source-maps",
    "start": "parcel src/index.html --open --port 3000",
    "start-fresh": "yarn clean && yarn build && yarn start",
    "test": "jest",
    "test:watch": "jest --watch"
  },
  "devDependencies": {
    "@babel/core": "^7.16.12",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@googlemaps/jest-mocks": "^2.3.0",
    "@parcel/babel-preset-env": "^2.2.1",
    "@parcel/transformer-sass": "^2.2.1",
    "@ramseyinhouse/eslint-config": "^1.0.4",
    "@ramseyinhouse/stylelint-config": "^1.1.0",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "@types/google.maps": "^3.47.3",
    "@types/jest": "^27.4.0",
    "@types/react": "^17.0.38",
    "@types/react-dom": "^17.0.11",
    "@types/react-router-dom": "^5.3.3",
    "@types/styled-components": "^5.1.21",
    "@typescript-eslint/eslint-plugin": "^5.10.2",
    "@typescript-eslint/parser": "^5.10.2",
    "autoprefixer": "^10.4.2",
    "axios": "^0.25.0",
    "babel-jest": "^27.4.6",
    "babel-plugin-styled-components": "^2.0.2",
    "eslint": "^8.8.0",
    "eslint-plugin-react-hooks": "^4.3.0",
    "jest": "^27.4.7",
    "jest-styled-components": "^7.0.8",
    "modules": "^0.4.0",
    "msw": "^0.36.8",
    "parcel": "^2.2.1",
    "plugins": "^0.4.2",
    "postcss": "^8.0.0",
    "postcss-custom-properties": "^12.1.4",
    "postcss-import": "^14.0.2",
    "postcss-url": "^10.1.3",
    "prettier": "^2.5.0",
    "redux-devtools": "^3.7.0",
    "stylelint": "^14.3.0",
    "stylelint-config-prettier": "^9.0.3",
    "stylelint-config-sass-guidelines": "^9.0.1",
    "stylelint-config-standard-scss": "^3.0.0",
    "stylelint-config-styled-components": "^0.1.1",
    "stylelint-order": "^5.0.0",
    "stylelint-processor-styled-components": "^1.10.0",
    "typescript-plugin-styled-components": "^2.0.0"
  },
  ...
}

🤔 Expected Behavior

fetchBaseQuery definition should be found and no error should be thrown. the definition of fetchBaseQuery should be included in the bundle

😯 Current Behavior

fetchBaseQuery definition can not be found. note in the source maps, fetchBaseQuery can be found, thus it works fine in local development. however, in the bundled js file, the definition for fetchBaseQuery can not be found.

💁 Possible Solution

n/a. sorry 😕

🔦 Context

this one is frustratingly misleading. the old "works on my machine"! the 'missing' function is defined properly in the source maps, thus local development sees no issue. however when deployed to a production environment via parcel build ... --no-source-maps the whole application tanks because of that uncaught error

💻 Code Sample

this is probably not helpful, but here is how the missing function is being used:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const API_BASE_URL = 'https://somewebsite.com/api';

const searchApi = createApi({
  reducerPath: 'searchApi',
  baseQuery: fetchBaseQuery({ baseUrl: API_BASE_URL }),
  endpoints: (builder) => ({
    getProsByProgramAndZip: builder.query({
      query: ({ program, zip }) =>
        `/pro/search?programKey=${program}&postalCodes=${zip}`,
    }),
  }),
});

const { useGetProsByProgramAndZipQuery } = searchApi;

export { API_BASE_URL, searchApi, useGetProsByProgramAndZipQuery };

🌍 Your Environment

Software Version(s)
Parcel 2.2.1
Node 14.18.1
Yarn 1.22.17
Operating System macOS Monterey 12.1
CalamityAdam commented 2 years ago

did some more digging on this. looks like in my case above the issue is that parcel (or, parcel-bundler?) is not recognizing aliased files. @reduxjs/toolkit/query/react is just doing export * from '@reduxjs/toolkit/query', so for some reason parcel is not recognizing those exports.

When I change this line:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

to this:

import { createApi } from '@reduxjs/toolkit/query/react';
import { fetchBaseQuery } from '@reduxjs/toolkit/query';

which is nothing more than explicit imports rather than using aliased (is that the right term?) imports, the bundler correctly grabs the definition of fetchBaseQuery.

the super strange thing is that the correct definition of fetchBaseQuery can be found in the source maps, but not in the minified bundle. so, in local development it works fine (using source maps) but deployed it does not (using only the bundle).

I don't know where to go from this, but I feel like this is some pretty good context to diagnose exactly what's failing in the bundler!

mischnic commented 2 years ago

Reproduction for that RTK problem:

// index.js
import { createApi, fetchBaseQuery } from "./lib";
console.log(createApi, fetchBaseQuery);

// lib/package.json
{"sideEffects": false}

// lib/index.js
console.log(this);
export * from "./lib1.js";
export var createApi = 123;

// lib/lib1.js
export function fetchBaseQuery(){}
starpit commented 2 years ago

I'm trying to figure out whether our issue is the same as this. I think so?

mynpm/index.ts:

import * as GraphApi from "./graph"
export { GraphApi }

publish this as mynpm

apiconsumer.ts

import { GraphApi } from 'mynpm'

Module '"mynpm"' has no exported member 'GraphApi'.

Workarounds?

I'm actually not sure how to work around this issue. One can hack it for non-types (such as re-exporting functions), but typescript does not allow exporting types as part of a const. And, when I try

export * as GraphApi from './graph'

I get, from parcel

@parcel/transformer-typescript-types: node.exportClause.elements is not iterable

i.e. https://github.com/parcel-bundler/parcel/issues/5911

autra commented 1 year ago

We reproduce it with https://gitlab.com/giro3d/giro3d-sample-application. Our workaround is to use --no-optimize

drewcorlin1 commented 1 year ago

I'm running into a similar issue on Parcel 2.9.3, where I have a module in my application which exports a single object (and depends on no other modules).

export const routes = {
  home: () => '/',
  search: () => '/search',
  account: () => '/account',
  login: () => '/login',
  forgotPassword: () => '/forgot-password',
  notFound: () => '/not-found',
};

If I run parcel with optimization enabled this module gets "optimized out". Other places that reference this module then end up with this routes just being an empty object, so they fail trying to call routes.home() since routes.home is undefined.

Interestingly if I do something else to reference this object, like console.log(routes) within this module, then it behaves properly.

Similarly if I run with --no-optimize the problem goes away, but that is not ideal. Please let me know if I can provide any more information!

Edit: Trying to add more context if it's helpful. Things that do "fix" this problem

Things that do not

This was not always the case. I set this project up a couple of months ago with no issue. I recently cleared out my node_modules and package-lock, and reinstalled all dependencies. I now see this issue. I'm still trying to understand what dependency changed to uncover this, as I have a huge diff in my package-lock. Will update this comment as I find anything