Josh Close
Josh Close

Reputation: 23383

How can I output multiple folders with webpack?

I have a folder structure for my JS modules. I want one module per page. This is not a single page app.

How can I output files in a folder structure?

From what I can see, the only possibility is to output [name].js. This could work if I make the names very unique, or I could make the names have a - for a folder separator. That would mean a/b/c.js would translate to name a-b-c. I really don't like this. I would like to be able to require("a/b/c").

From what I can tell, I can't use a single bundled file either because require is not available outside of the module. If it was, I could just build a single bundle and require("a/b/c") on every page.

If there is a good way to do this that I'm not finding on the internet, please let me know.

It looks like I'm able to easily do this with require.js using r.js, but I don't want to use require.js and would like CommonJS modules.

Upvotes: 11

Views: 9217

Answers (3)

tmsss
tmsss

Reputation: 2119

With a little wrangling with node you can create an entry object to pass in the config. In my case I used the name of the folders of the second level to create different bundles, but you can easily adapt to your needs.

const path = require('path');
var glob = require('glob')

const exportPath = path.resolve(__dirname,`./../public/javascripts/plugins`);

// create a glob of files
const entryArray = glob.sync('./plugins/**/{svg,src}/**/*.*');

/**
 * Create a dictionary of entries in format: folder: ['file', 'file2']
 * https://webpack.js.org/configuration/entry-context/#entry
 */

var folders = []
var entries = {};

// list unique folders
entryArray.map((item) => {
  const folderName = item.split('/')[2];
  if (!folders.includes(folderName)) {
    folders.push(folderName);
  }
});

// assign files to each folder
folders.map((folder) => {
  var imports = [];
  entryArray.map((item) => {
    const folderName = item.split('/')[2];
    
    if (folder == folderName) {
      imports.push(item);
    }

  });

  entries[folder] = imports
});

module.exports = {
    entry: entries,
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
              query: {
                presets: [ '@babel/preset-env' ],
              },
            }
          ],
        },
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader',
            {
              loader: 'postcss-loader',
              options: {
                plugins: [
                  require('postcss-nested-ancestors'),
                  require('postcss-nested')
                ]
              }
            }
          ]
        },
        {
          test: /\.svg$/,
          loader: 'svg-inline-loader?removeSVGTagAttrs=false'
        }
    ],
  },
  output: {
    path: exportPath,
    filename: '[name]/dist/bundle.js',
    libraryTarget: 'umd',
    libraryExport: 'default'
  }
};

Upvotes: 0

Anatoliy Arkhipov
Anatoliy Arkhipov

Reputation: 691

You can define an entry point using slash, like this:

entry: {
    "main-plugin/js/background":"./src/main-plugin/background"
}

And output like this:

output: {
    path: path.join(__dirname, 'public'),
    filename: '[name].js'
},

This setup will create a public/main-plugin/js folder and will place the background.js into it. It works at least on Win7x64.

Upvotes: 14

Matheus Carmo
Matheus Carmo

Reputation: 59

You can use [name] to make new folders too. Like this:

output: {
    path: __dirname,
    filename: '[name]/[name].js',
    chunkFilename: '[name].js',
    publicPath: '/assets/'
},

Upvotes: 1

Related Questions