Webpack: установите путь к моим активам в соответствии с именем записи - PullRequest
0 голосов
/ 25 мая 2020

Используя webpack, у меня есть многостраничное приложение, которое работает с несколькими записями. У меня есть такое дерево проектов:

-- webpack.config.js
-- src/
    -- first/
        -- main.ts
        -- helper.ts
        -- main.css
        -- index.html
    -- second/
        -- main.ts
        -- main.css
        -- index.html
    -- assets/
        -- myAsset1.png
        -- myAsset2.png
        -- myAsset3.png

Я хотел что-нибудь аккуратное и упорядоченное по папкам. Для этого я каждый раз добавляю [name]/... для name или filename, например:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
  entry: {
    first: './src/first/main.ts',
    second: './src/second/main.ts',
  },
  output: {
    path: path.resolve(__dirname, 'dist'), // output directory
    filename: '[name]/script.js', // name of the generated bundle
    publicPath: '/',
  },
  module: {
    rules: [
      // TypeScript
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        options: {
          onlyCompileBundledFiles: true,
        },
      },
      // Style
      {
        test: /\.s?[ac]ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'resolve-url-loader',
          },
          {
            loader: 'sass-loader',
          },
        ],
      },
      // Fonts & Images
      {
        test: /\.(woff(2)?|ttf|eot|jpg|png|svg|md)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              limit: 8192,
              name: '[name].[ext]',
            },
          },
        ],
      },
    ],
  },
  resolve: {
    modules: ['node_modules', path.resolve(process.cwd(), 'src')],
    extensions: ['.ts', '.js'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(process.cwd(), 'src/first/index.html'),
      inject: true,
      chunks: ['first'],
      filename: 'first/index.html',
    }),
    new HtmlWebpackPlugin({
      template: './src/second/index.html',
      inject: true,
      chunks: ['second'],
      filename: 'second/index.html',
    }),
    new MiniCssExtractPlugin({
      filename: '[name]/style.css',
      chunkFilename: '[name]/style.css',
    }),
  ],
};

Результат будет:

-- dist/
    -- first/
        -- script.js
        -- style.css
        -- index.html
    -- second/
        -- script.js
        -- style.css
        -- index.html
    -- myAsset1.png
    -- myAsset2.png
    -- myAsset3.png

Однако я бы хотел, чтобы активы в имени папки соответствовали записи, из которой они были импортированы. Действительно, myAssets1.png и myAssets2.png импортируются из main.ts из first папки, а myAssets3.png импортируется из main.ts из second папки

-- dist/
    -- first/
        -- script.js
        -- style.css
        -- index.html
        -- myAsset1.png
        -- myAsset2.png
    -- second/
        -- script.js
        -- style.css
        -- index.html
        -- myAsset3.png

Как я мог установить путь для моих активов согласно имени записи?

1 Ответ

1 голос
/ 25 мая 2020

Чтобы указать, что ресурсы, которые обрабатывает файловый загрузчик , будут сохранены в требуемом месте, должно произойти несколько вещей:

  1. вам необходимо определить, какая точка входа является эмитентом этого файла.
  2. введите его как resourceQuery
  3. используйте введенное resourceQuery в функции name из file-loader, чтобы указать путь.

Для достижения 1,2 вы можете создать собственный загрузчик, который извлечет эмитент и присоединит запрос.

// entry-dir-injector-loader.js
const path = require('path');

function recursiveIssuer(m) {
  if (m.issuer && m.issuer.context) {
    return recursiveIssuer(m.issuer);
  } else if (m.context) {
    return m.context;
  } else {
    return false;
  }
}

module.exports = function (content) {
  const entry = recursiveIssuer(this._module);
  const entryRelativePath = path.relative(this.rootContext, entry).replace('src/', '');
  this.resourceQuery = `?entryDir=${entryRelativePath}`;
  return content;
};

Затем укажите name как функция , извлечь запрос.

module.exports = {
  module: {
    rules: [
      {
        test: /\.(woff(2)?|ttf|eot|jpg|png|svg|md)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              limit: 8192,
              name(resourcePath, resourceQuery) {
                const entryDir = resourceQuery
                  .substring(1)
                  .split('&')
                  .filter((key) => key.startsWith('entryDir='))
                  .map((key) => key.split('=').pop())
                  .join('');

                return path.join(entryDir, `[name].[ext]`);
              },
            },
          },
          {
            loader: './entry-dir-injector-loader.js',
          },
        ],
      },
    ],
  },
};
...