adminjs icon indicating copy to clipboard operation
adminjs copied to clipboard

[Bug]: Component "XXXX" has not been bundled

Open serolgames opened this issue 10 months ago • 18 comments

Contact Details

No response

What happened?

Hello, Suddenly the error Component "XXXX" has not been bundled appeared for all of my components.

In dev mode it's working properly. But when it's in prod I have the following error at startup : "Expression expected in /app/dist/admin/components/dashboard.js"

This is my config :

components.ts : `import { ComponentLoader } from 'adminjs'

const componentLoader = new ComponentLoader()

const Components = { Dashboard: componentLoader.add('Dashboard', './components/dashboard'), ArticlePicture: componentLoader.add('ArticlePicture', './components/articlePicture'), UserPicture: componentLoader.add('UserPicture', './components/userPicture'), }

export { componentLoader, Components }`

adminjs.ts : ` admin = new AdminJS({ assets: { styles:['/main.css'], }, componentLoader,
branding:{ //my branding }, locale: { //code for locales }, resources: [ ///my resources ], dashboard: { component: Components.Dashboard, }, })

admin.watch() `

tsconfig.json : { "compilerOptions": { "jsx": "preserve", "target": "ESNext", "module": "NodeNext", "moduleResolution": "NodeNext", "resolveJsonModule": true, "removeComments": true, "allowJs": true, "outDir": "./dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, }, "include": [ "src/**/*", "src/**/*.json" ], "exclude": [ "node_modules", "src/migrations" ] }

Bug prevalence

Every time

AdminJS dependencies version

"@adminjs/express": "^6.1.1",
"@adminjs/sequelize": "^4.0.0",
"adminjs": "^7.8.15",

What browsers do you see the problem on?

Firefox

Relevant log output

Expression expected in /app/dist/admin/components/dashboard.js
Component "XXXX" has not been bundled

Relevant code that's giving you issues

components.ts : 
import { ComponentLoader } from 'adminjs'

const componentLoader = new ComponentLoader()

const Components = {
    Dashboard: componentLoader.add('Dashboard', './components/dashboard'),
    ArticlePicture: componentLoader.add('ArticlePicture', './components/articlePicture'),
    UserPicture: componentLoader.add('UserPicture', './components/userPicture'),
}

export { componentLoader, Components }

adminjs.ts : 
admin = new AdminJS({ 
		assets:
		{
			styles:['/main.css'],
		},
		componentLoader,   
		branding:{ 
                 //my branding 
		},
		locale: { 
		   //code for locales
		},
		resources: [
                   ///my resources
		], 
		dashboard: {
			component: Components.Dashboard,
		},
	})

	admin.watch() 

tsconfig.json : 
{
  "compilerOptions": {
    "jsx": "preserve",
    "target": "ESNext",     
    "module": "NodeNext",     
    "moduleResolution": "NodeNext",
    "resolveJsonModule": true,  
    "removeComments": true,
    "allowJs": true,   
    "outDir": "./dist",  
    "esModuleInterop": true, 
    "forceConsistentCasingInFileNames": true,    
    "strict": true,                                 
    "skipLibCheck": true,
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,                      
  },
  "include": [
    "src/**/*", 
    "src/**/*.json"
  ],
  "exclude": [
    "node_modules",
    "src/migrations"
  ]
}
dashboard: 

import React, { useState, useEffect } from 'react'

const Dashboard = () => 
{
    const [usersCount, setuserCount] = useState(0);

    useEffect(() => 
    {
        fetch('/admin/users-count')
        .then((response) => response.json())
        .then((data) => setuserCount(data.usersCount))
        .catch((error) => console.error('Err : ', error));
    }, []);

    return(
        <div className='admin-dashboard-box-container'>
            <p><span style={{fontSize: 25, fontWeight: 'bold'}}>{usersCount}</span></p>
            <p>test</p>
        </div>
    )
}

export default Dashboard

serolgames avatar Feb 21 '25 18:02 serolgames

Where do you host your app?

dziraf avatar Feb 21 '25 18:02 dziraf

Where do you host your app?

On a VPS in a docker container

serolgames avatar Feb 21 '25 18:02 serolgames

Can you check if:

  1. .adminjs folder is created in your docker image when the server starts? Unless you use @adminjs/bundler, the custom components bundle is generated when the server starts.
  2. Changing jsx to react in tsconfig.json solves the issue?
  3. Using absolute paths via path.join solves the issue?

Are your components using jsx, tsx or js extensions?

dziraf avatar Feb 21 '25 18:02 dziraf

Can you check if:

1. `.adminjs` folder is created in your docker image when the server starts? Unless you use `@adminjs/bundler`, the custom components bundle is generated when the server starts.

2. Changing `jsx` to `react` in `tsconfig.json` solves the issue?

3. Using absolute paths via `path.join` solves the issue?

Are your components using jsx, tsx or js extensions?

  1. .adminjs folder is ship in the container from the dev environment.
  2. No change
  3. No change

My components are jsx.

In dev it's working. Not in prod. Note that it used to work in prod, but not anymore for a reason I can't explain. I didn't remember changing something in specific

serolgames avatar Feb 21 '25 18:02 serolgames

Re: 1. Ideally, .adminjs folder should be generated in the environment where the AJS will be running. It means you shouldn't commit that folder and it should be recreated in your Docker container when you start the application. Or, if you use @adminjs/bundler you can pregenerate all bundle files and upload them to some kind of storage. Not sure if this is related, if you run your local/dev app and a prod app in a Docker container, it should probably work regardless.

You could have a look at .adminjs folder contents:

  • .adminjs/.entry.js should contain AdminJS.UserComponents mappings. Check if all of your components are there and if their import paths are correct. If the paths are relative, they should be relative to the file inside of .adminjs folder and corresponding files should exist.
  • .adminjs/bundle.js should contain the actual bundle generated from files in .entry.js. This file might actually not exist if you're running the admin panel in dev/watch mode since then the bundle is served dynamically.

Other thing to consider is:

You use admin.watch(). This method starts the bundler in watch mode where whenever any file changes occur, it will recreate the custom components.bundle.js. If your NODE_ENV is set to production, this command does nothing. Instead, you should use initialize:

if (process.env.NODE_ENV === 'production') await admin.initialize();
else admin.watch();

Also make sure you use production for remote environments since otherwise the bundles served won't be minified.

Extra questions:

  1. When production environment starts, do the logs say that AdminJS is bundling files and afterwards how many files had been bundled?
  2. Could you share a screenshot of your browser's Console tab with the exact error?

dziraf avatar Feb 22 '25 07:02 dziraf

Re: 1. Ideally, .adminjs folder should be generated in the environment where the AJS will be running. It means you shouldn't commit that folder and it should be recreated in your Docker container when you start the application. Or, if you use @adminjs/bundler you can pregenerate all bundle files and upload them to some kind of storage. Not sure if this is related, if you run your local/dev app and a prod app in a Docker container, it should probably work regardless.

You could have a look at .adminjs folder contents:

* `.adminjs/.entry.js` should contain `AdminJS.UserComponents` mappings. Check if all of your components are there and if their import paths are correct. If the paths are relative, they should be relative to the file inside of `.adminjs` folder and corresponding files should exist.

* `.adminjs/bundle.js` should contain the actual bundle generated from files in `.entry.js`. This file might actually not exist if you're running the admin panel in dev/watch mode since then the bundle is served dynamically.

Other thing to consider is:

You use admin.watch(). This method starts the bundler in watch mode where whenever any file changes occur, it will recreate the custom components.bundle.js. If your NODE_ENV is set to production, this command does nothing. Instead, you should use initialize:

if (process.env.NODE_ENV === 'production') await admin.initialize(); else admin.watch();

Also make sure you use production for remote environments since otherwise the bundles served won't be minified.

Extra questions:

1. When production environment starts, do the logs say that AdminJS is bundling files and afterwards how many files had been bundled?

2. Could you share a screenshot of your browser's `Console` tab with the exact error?

Hello, Thank you for trying to help me, really appreciate.

Following your advice I added .adminjs in my gitignore and dockerignore.

I also added this code : if(process.env.NODE_ENV === 'production'){ admin.initialize() }else{ admin.watch() }

Now when I start my app in prod I'm getting the following logs : AdminJS: bundling user components... AdminJS: bundling user components... App listening at port 3000

And when I'm going to my admin page my components are still not showing up and I have this log error (on the server) :

api-app-1 | NotFoundError: Not Found api-app-1 | at createHttpError (/app/node_modules/send/index.js:864:12) api-app-1 | at SendStream.error (/app/node_modules/send/index.js:169:31) api-app-1 | at SendStream.pipe (/app/node_modules/send/index.js:469:14) api-app-1 | at sendfile (/app/node_modules/express/lib/response.js:988:8) api-app-1 | at ServerResponse.sendFile (/app/node_modules/express/lib/response.js:397:3) api-app-1 | at file:///app/node_modules/@adminjs/express/lib/buildRouter.js:61:17 api-app-1 | at Layer.handleRequest (/app/node_modules/router/lib/layer.js:145:17) api-app-1 | at next (/app/node_modules/router/lib/route.js:160:13) api-app-1 | at Route.dispatch (/app/node_modules/router/lib/route.js:120:3) api-app-1 | at handle (/app/node_modules/router/index.js:427:11) api-app-1 | at Layer.handleRequest (/app/node_modules/router/lib/layer.js:145:17) api-app-1 | at /app/node_modules/router/index.js:292:15 api-app-1 | at processParams (/app/node_modules/router/index.js:574:12) api-app-1 | at next (/app/node_modules/router/index.js:288:5) api-app-1 | at /app/node_modules/express-formidable/lib/middleware.js:36:7 api-app-1 | at IncomingForm.<anonymous> (/app/node_modules/formidable/lib/incoming_form.js:107:9) api-app-1 | at IncomingForm.emit (node:events:518:28) api-app-1 | at IncomingForm._maybeEnd (/app/node_modules/formidable/lib/incoming_form.js:563:8) api-app-1 | at Object.end (/app/node_modules/formidable/lib/incoming_form.js:251:12) api-app-1 | at IncomingMessage.<anonymous> (/app/node_modules/formidable/lib/incoming_form.js:132:30) api-app-1 | at IncomingMessage.emit (node:events:518:28) api-app-1 | at endReadableNT (node:internal/streams/readable:1698:12) api-app-1 | at process.processTicksAndRejections (node:internal/process/task_queues:90:21) api-app-1 | NotFoundError: Not Found api-app-1 | at createHttpError (/app/node_modules/send/index.js:864:12) api-app-1 | at SendStream.error (/app/node_modules/send/index.js:169:31) api-app-1 | at SendStream.pipe (/app/node_modules/send/index.js:469:14) api-app-1 | at sendfile (/app/node_modules/express/lib/response.js:988:8) api-app-1 | at ServerResponse.sendFile (/app/node_modules/express/lib/response.js:397:3) api-app-1 | at file:///app/node_modules/@adminjs/express/lib/buildRouter.js:61:17 api-app-1 | at Layer.handleRequest (/app/node_modules/router/lib/layer.js:145:17) api-app-1 | at next (/app/node_modules/router/lib/route.js:160:13) api-app-1 | at Route.dispatch (/app/node_modules/router/lib/route.js:120:3) api-app-1 | at handle (/app/node_modules/router/index.js:427:11) api-app-1 | at Layer.handleRequest (/app/node_modules/router/lib/layer.js:145:17) api-app-1 | at /app/node_modules/router/index.js:292:15 api-app-1 | at processParams (/app/node_modules/router/index.js:574:12) api-app-1 | at next (/app/node_modules/router/index.js:288:5) api-app-1 | at /app/node_modules/express-formidable/lib/middleware.js:36:7 api-app-1 | at IncomingForm.<anonymous> (/app/node_modules/formidable/lib/incoming_form.js:107:9) api-app-1 | at IncomingForm.emit (node:events:518:28) api-app-1 | at IncomingForm._maybeEnd (/app/node_modules/formidable/lib/incoming_form.js:563:8) api-app-1 | at Object.end (/app/node_modules/formidable/lib/incoming_form.js:251:12) api-app-1 | at IncomingMessage.<anonymous> (/app/node_modules/formidable/lib/incoming_form.js:132:30) api-app-1 | at IncomingMessage.emit (node:events:518:28) api-app-1 | at endReadableNT (node:internal/streams/readable:1698:12) api-app-1 | at process.processTicksAndRejections (node:internal/process/task_queues:90:21)

And on my browser I have this error :

The resource at “https://myprodurl/admin/frontend/assets/components.bundle.js” was blocked due to an incorrect MIME type (“text/html”) (X-Content-Type-Options: nosniff).

I went into the container to see the files, I have no admin/frontend/assets folder

$ ls README.md dist jest.config.ts nodemon.json package.json src __tests__ docs node_modules package-lock.json public tsconfig.json

my build is in dist folder. There's no admin folder in public folder

/app/dist/admin# ls adminjsconfig.js components components.js.map adminjsconfig.js.map components.js

Have a nice day

serolgames avatar Feb 22 '25 11:02 serolgames

Hello, any news about it?

serolgames avatar Mar 01 '25 14:03 serolgames

Hello, I really need help because it's for professional use. Do you need more information about my repo ?

serolgames avatar Mar 13 '25 10:03 serolgames

Do you use a CDN? https://expressjs.com/en/guide/behind-proxies.html

dziraf avatar Mar 13 '25 12:03 dziraf

Do you use a CDN? https://expressjs.com/en/guide/behind-proxies.html

I don't have a CDN but in my index I have app.set('trust proxy', 1);

serolgames avatar Mar 13 '25 12:03 serolgames

if you get Content-Type: text/html error, it usually means you get error page instead of the file

see what you get from the response:

curl -I https://myprodurl/admin/frontend/assets/components.bundle.js

are you using some reverse proxy? Maybe it's misconfigured?

aszymanskiit avatar Mar 13 '25 12:03 aszymanskiit

if you get Content-Type: text/html error, it usually means you get error page instead of the file

see what you get from the response:

curl -I https://myprodurl/admin/frontend/assets/components.bundle.js

are you using some reverse proxy? Maybe it's misconfigured?

I'm using a reverse proxy with nginx. This is the config :

`server {

server_name xxxxx;
server_tokens off;
# Block wget user agent
if ($http_user_agent ~* (wget|curl) ) {
      return 403;
}

location /images/ {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    client_max_body_size 10M;
}

location / {
 limit_req zone=xxxxxxx burst=12 nodelay;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header Host $host;
     proxy_pass http://localhost:3000;
     proxy_http_version 1.1;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";
 client_max_body_size 10M;
}

location ~* \.(jpg|jpeg|png|gif|ico)$ {
    expires 20d;
    add_header Cache-Control "public, no-transform";
}

gzip on;
gzip_disable "msie6";
gzip_vary on; 
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_static on;

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xxxxx/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xxxxxx/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

} `

serolgames avatar Mar 13 '25 13:03 serolgames

Any idea?

serolgames avatar Mar 17 '25 10:03 serolgames

Hi @serolgames,
I hope I'm not too late to help.

I ran into the same issue and found your question while troubleshooting. I spent around 5–6 hours reading and debugging the source code of both adminjs and @adminjs/express, and I finally found a workaround — though I haven't yet identified the root cause, and I'm honestly too tired and sleepy to keep digging for now.

The issue seems to be with the default .adminjs folder used for bundling. Express (res.send) appears to have trouble serving files from directories that start with a dot (like .adminjs).

The workaround is to use a custom directory that doesn’t start with a dot, like adminjs-temp or any other non-hidden folder.

You can fix it by setting the ADMIN_JS_TMP_DIR environment variable. For example:

ADMIN_JS_TMP_DIR=./node_modules/tmp/adminjs

This resolved the issue on my end. Hope it helps anyone else facing the same problem!

hameda169 avatar Jun 06 '25 23:06 hameda169

Solution from @hameda169 works (thank you!). I had that error when running the app on render.com and locally (with NODE_ENV=production). Changing the folder location fixed it.

programystic-dev avatar Jul 28 '25 16:07 programystic-dev

Hi @serolgames, I hope I'm not too late to help.

I ran into the same issue and found your question while troubleshooting. I spent around 5–6 hours reading and debugging the source code of both adminjs and @adminjs/express, and I finally found a workaround — though I haven't yet identified the root cause, and I'm honestly too tired and sleepy to keep digging for now.

The issue seems to be with the default .adminjs folder used for bundling. Express (res.send) appears to have trouble serving files from directories that start with a dot (like .adminjs).

The workaround is to use a custom directory that doesn’t start with a dot, like adminjs-temp or any other non-hidden folder.

You can fix it by setting the ADMIN_JS_TMP_DIR environment variable. For example:

ADMIN_JS_TMP_DIR=./node_modules/tmp/adminjs

This resolved the issue on my end. Hope it helps anyone else facing the same problem!

Not working for me, tried ./node_modules/tmp/adminjs, /tmp/adminjs and so on, still for some reason only behind reverse proxy custom components are inaccessible

terminaate avatar Aug 19 '25 08:08 terminaate

Hi @serolgames, I hope I'm not too late to help. I ran into the same issue and found your question while troubleshooting. I spent around 5–6 hours reading and debugging the source code of both adminjs and @adminjs/express, and I finally found a workaround — though I haven't yet identified the root cause, and I'm honestly too tired and sleepy to keep digging for now. The issue seems to be with the default .adminjs folder used for bundling. Express (res.send) appears to have trouble serving files from directories that start with a dot (like .adminjs). The workaround is to use a custom directory that doesn’t start with a dot, like adminjs-temp or any other non-hidden folder. You can fix it by setting the ADMIN_JS_TMP_DIR environment variable. For example: ADMIN_JS_TMP_DIR=./node_modules/tmp/adminjs This resolved the issue on my end. Hope it helps anyone else facing the same problem!

Not working for me, tried ./node_modules/tmp/adminjs, /tmp/adminjs and so on, still for some reason only behind reverse proxy custom components are inaccessible

/admin/frontend/assets/components.bundle.js just redirecting to /admin/login

terminaate avatar Aug 19 '25 08:08 terminaate

@terminaate Could you please explain more what did you actually do to apply my solution?

hameda169 avatar Sep 20 '25 09:09 hameda169