Search code examples
javascriptnode.jswebpackassets

Webpack not bundling assets with output bundle.js file


I am using Webpack to pack static javascript files with folders and assets into a single output bundle.js file. The bundle.js file is then taken out from the original codebase and statically hosted in the backend server. The problem is that the assets are not bundled within the bundle.js file. When I try to display the publicly hosted index.js url in the frontent, I get the error message saying

GET http://127.0.0.1:5503/public/Stage/sounds/pop.wav 404 (Not Found)

Right now the backend is running on localhost:5000, and the frontend is running on localhost:5503. The webpack version is 5.77.0.

I know webpack will automatically bundle assets if they are explicitly imported and referenced in files, but The problem is that the static javascript files are actually javascripts of Scratch codes that are converted using a packet called sb-edit, and I cannot change the way the code is written.

This is how the assets are referenced in the files:

new Costume("button4-a", "./Button4/costumes/button4-a.svg")

This is the content of the webpack.config, I am using asset-modules so there is no need for file-loader or any other loaders:

const path = require('path')


module.exports = {
    mode: 'development',
    entry: './index.js', 
    output: {
        filename: 'bundle.js', 
        path: path.resolve(__dirname, 'dist'),
        assetModuleFilename: '[path][name].[ext]',
    },
    experiments: { outputModule: true },
    externals: {
        'https://unpkg.com/leopard@^1/dist/index.esm.js': 'https://unpkg.com/leopard@^1/dist/index.esm.js',
    },
    externalsType: 'module',
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif|mp3|wav)$/i,
                type: 'asset',
              },
          ],
    },
    optimization: {
        usedExports: true,
        sideEffects: false,
    },
    resolve: {
        fallback: {
          "path": require.resolve("path-browserify"),
          "url": require.resolve("url"),
          "events": require.resolve('events'),
        }
      },
};

This is one example (not the whole structure) of the static javascript files structure:

.
├── Sprite2/
│   ├── costumes/
│   │   └── costumes1.svg
│   ├── sounds/
│   │   └── pop.wav
│   └── Sprite2.js
├── Sprite3/
│   ├── costumes/
│   │   └── costumes1.svg
│   ├── sounds/
│   │   └── pop.wav
│   └── Sprite3.js
└── index.js

The file is referenced in the frontend like this:

<script type="module" src="http://localhost:5000/api/v1/scratch/bundle.js"></script>

I have tried changing the asset module types to asset/resource, asset/inline and asset but none of these options work. Is there any way to achieve this without making changes to the original code? Thank you so much in advance!


Solution

  • I managed to find a way to work around this, and I hope this will help others in some way.

    First of all, I used the webpack's CopyWebpackPlugin to copy all the assets to the destination folder while maintaining their original file hierarchy. I ignored all the files that were not assets:

    plugins: [
      new CopyWebpackPlugin({
        patterns: [
          {
            from: './**/*',
            to: './',
            context: './',
            globOptions: {
              ignore: ['**/*.js', '**/node_modules/**', '**/*.html', '**/*.json']
            }
          }
        ]
      }),
    ],
    

    Then, I put these assets folder to the statically held folder in the backend along with the generated bundle.js file.

    Originally I was only using the statically held js file in the script tag like this:

    <script type="module" src="http://localhost:5000/api/v1/scratch/bundle.js"></script>
    

    Now I am first using axios to get the bundle js file first, then I parse the js file and manually prepend the backend's URL in front of the relative assets paths inside the bundle:

    axios.get('http://localhost:5000/url-to-statically-held-folder/bundle.js')
      .then(response => {
        let jsCode = response.data;
    
        jsCode = jsCode.replace(/\.[\/\w\s-]+\.(png|svg|jpg|jpeg|gif|mp3|wav)/g, (match) => {
          let urlStr = `http://localhost:5000/url-to-statically-held-folder/${match}`
          return urlStr;
        });
    
        const script = document.createElement('script');
        script.type = 'module';
        script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(jsCode);
        document.head.appendChild(script);
      })
      .catch(error => {
        console.error(error);
      });
    

    Now it works.