Phoenix, webpack with lodash issues. Cannot load lodash

Hi, am a noob at webpack so please bear with me. I am doing the best I can.

What i am trying to do is to cherrypick a subset of lodash, specifically debounce to load in my application via app.js

The debounce function is available via _.debounce as _ is a global. e.g. _.debounce()

But the debounce function is not available on its own e.g. debounce()

Also any kind feedback on my webpack.config.js will be very much appreciated.

Thank you.

app.js

import _ from "../node_modules/lodash";
import debounce from "../node_modules/lodash/debounce";

webpack.config.js

const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { SourceMapDevToolPlugin } = require("webpack");
const PurgecssPlugin = require('purgecss-webpack-plugin');
const globAll = require('glob-all');

// Custom PurgeCSS extractor for Tailwind that allows special characters in
// class names.
// Regex explanation: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex
const TailwindExtractor = content => {
  return content.match(/[\w-/:]+(?<!:)/g) || [];
};

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new TerserPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({}),
      new PurgecssPlugin({
        paths: globAll.sync([
          '../lib/**/templates/**/*.html.eex',
          '../lib/**/views/**/*.ex',
          '../assets/js/**/*.js',
        ]),
        extractors: [
          {
            extractor: TailwindExtractor,
            extensions: ['html', 'js', 'eex', 'ex'],
          },
        ],
      }),
    ]
  },
  entry: {
    './app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js']),
    //'./froala_tui_integration.js': './js/froala_tui_integration.js',
    // './test.js': './js/test.js',
  },
  output: {
    filename: '[name]',
    path: path.resolve(__dirname, '../priv/static/js'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.js$/,
        enforce: 'pre',
        use: ['source-map-loader'],
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
      },
      {
        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: '../fonts'
          }
        }]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
    new SourceMapDevToolPlugin({ filename: "[file].map" }),
  ]
});

I am not sure about lodash, but for npm packages in general you import the package name, not the file name. eg:

import _ from "lodash";
import debounce from "lodash.debounce";

Hi thanks! Oh in this case, im trying to coerce npm to look into the assets\node_modules path as it keeps looking at my APPDATA folder.

And i am very very positive i have installed the packages in the local folder (WITHOUT the -g switch) which is why I put the path instead of the package-name.

But nevertheless, import _ from "../node_modules/lodash"; works perfectly so it is probably not the issue.

If you need to do that, then something is amiss. npm has a notion of local root directory, in the case of a phoenix setup it is the assets/ dir. It expects to find node_modules/, package.json, there. so everything just work. If it does not, then I suggest you fix that thing first instead of limping along.

If you bypass all that, and you want to import a file, you need to have the full path to the file, like

import bluh from './bluh.js';

And you need to make sure bluh.js is a proper ES6 module with a default export actually called bluh.

1 Like

The problem is in VSCode when i put the following

import _ from "lodash";
import debounce from "lodash/debounce";

Intellisense reveals that module is in
“C:/Users/chemist/AppData/Local/Microsoft/TypeScript/4.2/node_modules/@types/lodash/index”

and

module “C:/Users/chemist/AppData/Local/Microsoft/TypeScript/4.2/node_modules/@types/lodash/debounce”

Do you think its normal?

Yes i key in the desired module into package.json and run npm install to download the modules into the assets/node_modules folder.

I have no idea about VScode or intellisense, and I think if you stick to running npm at the command line you need not to care.

By the way, I looked up in npm and “lodash” and “lodash.debounce” are two different packages. They are most likely built from the same source, nevertheless you should install both and not try to import something buried deep.

I just found out I had to disable the typescript.disableAutomaticTypeAcquisition setting in VSCode so Intellisense now reports the correct location.

import debounce from "lodash/debounce";

However I am still unable to access this function. Do you think it has something to do with an erroneous webpack.config.js where the imports are not set as global variables?

at this point the question has nothing to do with elixir or phoenix. From my reading of the npm page, the package lodash.debounce was last updated 5 years ago, which is ancient in javascript world. I am not even sure it supports the much recent ‘import’ syntax.

It looks like the lodash people really discourage users from using individual functions and want you to use the whole thing. Better not to go against the wish of the author.