Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getting conditionNames required warning, but my config has conditionNames property in webpack config #226

Closed
elirov opened this issue Feb 17, 2023 · 8 comments

Comments

@elirov
Copy link

elirov commented Feb 17, 2023

$ yarn watch
yarn run v1.23.0-20220130.1630

WARNING: You should add "svelte" to the "resolve.conditionNames" array in your webpack config. See https://github.com/sveltejs/svelte-loader#resolveconditionnames for more information

webpack config file:

  config.resolve = {
    fallback: { assert: false },
    extensions: ['.cjs', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.css', '.scss', '.svelte'],
    modules: ['src', 'lib', 'node_modules', 'tmpls'],
    alias: {
      vendor: __dirname + '/lib/vendor',
      jquery: __dirname + '/lib/vendor-adapters/jquery-global-adapter',
      jqueryNonGlobal: __dirname + '/node_modules/jquery',
      moment: __dirname + '/lib/vendor-adapters/moment-global-adapter',
      momentNonGlobal: __dirname + '/node_modules/moment',
      xdoc: __dirname + '/src/xdoc',
      client: __dirname + '/src/client',
      node_modules: 'node_modules',
      test: __dirname + '/src/test',
      svelte: __dirname + '/node_modules/svelte',
      src: __dirname + '/src',
      tmpls: __dirname + '/tmpls',
    },
    conditionNames: ['svelte'],
  };

Any places I should check? Is the warning looking at the value of the property? Or is it trying to parse the config file itself?

@dummdidumm
Copy link
Member

Could you post the complete config file (including where/how the export of it happens; you can redact any sensitive strings)? Check happens here and should find your settings, so that's strange

@elirov
Copy link
Author

elirov commented Feb 18, 2023

const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const fs = require('fs');
const path = require('path');
const { merge } = require('webpack-merge');
const crypto = require('crypto');
const uuidv4 = require('uuid/v4');
const FORM_KEY = 'f0';
const XDOC_CONTAINER_ID = 'XDocContainer-' + FORM_KEY;
const argv = require('yargs')(process.argv.slice(2)).argv;
const { processNestedHtmlFactory, addTemplateAliases } = require('./webpack-utils');
const buildStaticFields = require('./build-utils/xdoc-static-field-processor');
const buildDynamicFields = require('./build-utils/xdoc-static-to-dynamic-field-converter');
const { pipe } = require('lodash/fp');
const sveltePreprocess = require('svelte-preprocess');

const ROOT = path.resolve(__dirname);
const rootPath = path.join.bind(path, ROOT);

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();

const getTmplConfig = async (env, tmplSettings) => {
  const tmplName = env.tmpl;
  let tmplDefinedSettings;
  try {
    //tmplDefinedSettings = require(rootPath(`/tmpls/${tmplName}/xdoc-platform-settings.mjs`)).default;
    tmplDefinedSettings = await import(rootPath(`/tmpls/${tmplName}/xdoc-platform-settings.mjs`));
  } catch (err) {
    console.log(`${tmplName} needs to provide tmpls/${tmplName}/xdoc-platform-settings.mjs`, err);
    throw err;
  }
  let fieldMapPath;
  if (fs.existsSync(`${__dirname}/tmpls/${tmplName}/fieldmap/fieldmap.js`)) {
    fieldMapPath = `tmpls/${tmplName}/fieldmap/fieldmap.js`;
  } else {
    fieldMapPath = `tmpls/${tmplName}/fieldMap.json`;
  }

  let fieldConverter;
  if (tmplDefinedSettings.build?.convertFieldsToDynamic) {
    console.log(`converting tmpls/${tmplName}/fieldMap.json static fields to dynamic fields`);
    fieldConverter = buildDynamicFields;
  } else {
    console.log(`NOT converting static fields defined in tmpls/${tmplName}/fieldMap.json`);
    fieldConverter = buildStaticFields;
  }
  const config = {
    infrastructureLogging: {
      level: 'warn',
    },
    entry: {},
    target: 'web',
    module: {
      rules: [
        {
          test: /\.html$/,
          use: [
            {
              loader: 'html-loader',
              options: {
                esModule: false,
                preprocessor: (content, loaderContext) => {
                  content = content.replace(/__IMPORT_TEMPLATE_PATH__/g, rootPath(`/tmpls/${tmplName}`));
                  const combineHtmlPreprocessor = processNestedHtmlFactory(rootPath, rootPath(`/tmpls/${tmplName}`));
                  const combined = combineHtmlPreprocessor(content, loaderContext);
                  return fieldConverter(combined, {
                    context: loaderContext,
                    fieldMapPath: rootPath(fieldMapPath),
                    formKey: FORM_KEY,
                  });
                },
              },
            },
          ],
        },
      ],
    },
    plugins: [
      new webpack.NormalModuleReplacementPlugin(/__IMPORT_TEMPLATE_PATH__/, (res) => {
        res.request = res.request.replace(/__IMPORT_TEMPLATE_PATH__/g, `${__dirname}/tmpls/${tmplName}`);
      }),
      new webpack.NormalModuleReplacementPlugin(/__IMPORT_FIELDMAP_PATH__/, (res) => {
        res.request = res.request.replace(/__IMPORT_FIELDMAP_PATH__/g, fieldMapPath);
      }),
      new webpack.NormalModuleReplacementPlugin(/__IMPORT_TEMPLATE_INIT_SCRIPT__/, (res) => {
        res.request = res.request.replace(
          /__IMPORT_TEMPLATE_INIT_SCRIPT__/g,
          `${__dirname}/${tmplSettings.initScript}`
        );
      }),
      new webpack.NormalModuleReplacementPlugin(/__FORM_KEY__/, (res) => {
        res.request = res.request.replace(/__FORM_KEY__/g, FORM_KEY);
      }),
      new HtmlWebpackPlugin({
        inject: true,
        chunks: [tmplName],
        template: `src/client/dev/index.ejs`,
        filename: `${tmplName}.html`,
      }),
    ],
  };
  config.entry[tmplName] = `${__dirname}/src/client/dev/main.js`;
  return config;
};

const getMainConfig = (env, tmplSettings) => {
  var config = {};
  config.devtool = 'eval-cheap-module-source-map';
  config.mode = 'development';
  config.optimization = {
    usedExports: true,
  };
  config.resolve = {
    fallback: { assert: false },
    extensions: ['.cjs', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.css', '.scss', '.svelte'],
    modules: ['src', 'lib', 'node_modules', 'tmpls'],
    alias: {
      vendor: __dirname + '/lib/vendor',
      jquery: __dirname + '/lib/vendor-adapters/jquery-global-adapter',
      jqueryNonGlobal: __dirname + '/node_modules/jquery',
      moment: __dirname + '/lib/vendor-adapters/moment-global-adapter',
      momentNonGlobal: __dirname + '/node_modules/moment',
      xdoc: __dirname + '/src/xdoc',
      client: __dirname + '/src/client',
      node_modules: 'node_modules',
      test: __dirname + '/src/test',
      svelte: __dirname + '/node_modules/svelte',
      src: __dirname + '/src',
      tmpls: __dirname + '/tmpls',
    },
  };
  pipe(addTemplateAliases)(config.resolve.alias);
  // console.log('aliases = ', config.resolve.alias);
  config.resolveLoader = {
    modules: ['node_modules', path.resolve(__dirname, 'build-utils', 'loaders')],
  };
  config.output = {
    path: __dirname + '/dist/dev',
    filename: '[name].js',
  };
  config.module = {
    rules: [
      {
        test: /\.(svelte)$/,
        use: {
          loader: 'svelte-loader',
          options: {
            compilerOptions: {
              dev: true,
            },
            preprocess: sveltePreprocess({}),
            // ignore a11y errors
            onwarn: (warning, handler) => {
              if (warning.code.toLowerCase().startsWith('a11y-')) {
                return;
              }
              handler(warning);
            },
          },
        },
      },
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'esbuild-loader',
            options: {
              loader: 'ts',
              target: 'es2020',
              //onlyCompileBundledFiles: true,
              //transpileOnly: true,
            },
          },
        ],
      },
      {
        test: /\.c?m?jsx?$/,
        resolve: {
          fullySpecified: false,
        },
        use: { loader: 'esbuild-loader' },
        exclude: {
          and: [/node_modules/],
        },
      },
      {
        test: require.resolve('jquery'),
        use: [
          {
            loader: 'expose-loader',
            options: {
              exposes: ['jQuery', '$'],
            },
          },
        ],
      },
      {
        test: /\.dot$/,
        use: [
          {
            loader: 'raw-loader',
          },
        ],
      },
      {
        test: /\.scss$/,
        include: path.resolve(__dirname, 'src/client/dev'),
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { url: false, sourceMap: true },
          },
          {
            loader: 'sass-loader',
            options: {
              additionalData: `$templateId: '${tmplSettings.templateId}';`,
            },
          },
          {
            loader: 'xdoc-sass-loader',
            options: {
              tmplPath: `${__dirname}/tmpls/${env.tmpl}`,
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        exclude: path.resolve(__dirname, 'src/client/dev'),
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { url: false, sourceMap: true },
          },
          {
            loader: 'sass-loader',
            options: {
              additionalData: `$templateId: '${tmplSettings.templateId}';`,
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { url: false, sourceMap: true },
          },
          {
            loader: 'less-loader',
            options: { sourceMap: true },
          },
        ],
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { sourceMap: true },
          },
        ],
      },
      {
        test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: 'url-loader?limit=10000',
      },
      {
        test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/,
        use: 'file-loader',
      },
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        use: ['file-loader?name=images/[name].[ext]'],
      },
    ],
  };

  // optionally disables pdf printing if narrative styling is not used within the passed in template
  if (!fs.existsSync(`./tmpls/${env.tmpl}/narrative.scss`)) {
    config.module.rules.push({
      test: /pdf-print.js/,
      loader: 'null-loader',
    });
    if (env.tmpl && process.env.BABEL_ENV !== 'test') {
      console.log(`${env.tmpl} does not use narrative.scss. dev tool PDF printing disabled.`);
    }
  }

  config.plugins = [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: __dirname + '/server',
          to: __dirname + '/dist/dev/server',
        },
        {
          from: __dirname + '/node_modules/material-design-icons-iconfont/dist/fonts',
          to: __dirname + '/dist/dev/server/global-resources/fonts',
        },
        {
          from: __dirname + '/node_modules/tinymce',
          to: __dirname + '/dist/dev/servlet/gwt/assets/tinymce',
        },
      ],
    }),
    new webpack.ProgressPlugin(),
    new webpack.ProvidePlugin({
      autosize: 'autosize',
      _: 'lodash',
      doT: 'dot',
    }),
    new webpack.DefinePlugin({
      __FORM_KEY: JSON.stringify(FORM_KEY),
      __XDOC_CONTAINER_ID: JSON.stringify(XDOC_CONTAINER_ID),
      __TEMPLATE_ID: JSON.stringify(tmplSettings.templateId),
      __TEMPLATE_HEADER_ID: JSON.stringify(tmplSettings.templateHeaderId),
      __TEMPLATE_BODY_ID: JSON.stringify(tmplSettings.templateBodyId),
      __TEMPLATE_RDE_NAME__: JSON.stringify(env.tmpl),
      __APP_ENV: "'DEV'",
      __API_BASE_URL: "'" + (argv.apiUrl ?? '/servlet') + "'",
    }),
  ];
  return config;
};

const buildTmplSettings = (env) => {
  const settings = {
    templateId: 'NA',
    templateHeaderId: undefined,
    templateBodyId: undefined,
    initScript: `tmpls/${env.tmpl}/init.js`,
  };

  // yarn test
  // yarn watch supernote
  // yarn watch without a template name - error

  if (!env.ideAnalyzer) {
    const hash = crypto.createHash('sha1');
    hash.update(fs.readFileSync(settings.initScript) + fs.readFileSync(`tmpls/${env.tmpl}/template.html`));
    settings.templateId = hash.digest('hex');
    settings.templateHeaderId = uuidv4().replace(/-/g, '');
    settings.templateBodyId = uuidv4().replace(/-/g, '');
  }

  return settings;
};

const knownEnvVars = ['WEBPACK_BUNDLE', 'WEBPACK_BUILD', 'WEBPACK_WATCH', 't', 'tmpl', 'template', '--template'];

function findTemplateName(env) {
  const envKeys = Object.keys(env);
  const unknownArguments = envKeys.filter((v) => !knownEnvVars.includes(v));
  if (unknownArguments.length === 1) {
    // if only one unknown arg, then it's the template name
    return unknownArguments[0];
  }
  return undefined;
}

module.exports = async (env) => {
  if (process.env.BABEL_ENV !== 'test') {
    env.tmpl = env.tmpl ?? env.template ?? env.t ?? findTemplateName(env);
  }
  if (!env.tmpl) {
    throw Error(`No template name given. Args given: ${Object.keys(env)}. use yarn watch TEMPLATE`);
  }
  if (env.tmpl === 'development') {
    env.ideAnalyzer = true;
  }
  const tmplSettings = buildTmplSettings(env);
  const webpackConfig = await getTmplConfig(env, tmplSettings);
  return merge(
    getMainConfig(env, tmplSettings),
    process.env.WATCH_STATS === 'on' ? smp.wrap(webpackConfig) : webpackConfig
  );
};

@elirov
Copy link
Author

elirov commented Feb 18, 2023

I guess the interesting part of this config is that we're returning an async function instead of a complete object. But that's because we need to do some async actions before we have our complete config. As far as I know this type of setup is supported by webpack.

@elirov
Copy link
Author

elirov commented Feb 18, 2023

Also, I see that we're depending on env, and it looks like the only way for us to get the "env" parameter is to have module.exports be assigned to a function as opposed to an object.
see: https://webpack.js.org/guides/environment-variables/

@dehmer
Copy link

dehmer commented Feb 27, 2023

@elirov Your final config seems to be lacking conditionNames: ['svelte']. I was struggling with the same issue. But neither module.exports = env => {...} nor module.exports = env => [...] seem to be a problem. I verified this by playing with https://github.com/sveltejs/template-webpack template.

For me it was definitively the missing conditionNames: ['svelte']

🤞

@dehmer
Copy link

dehmer commented Feb 27, 2023

@elirov Sorry for the false information above! (env) => ... IS the problem. The template had an older version of svelte-loader. 3.1.5 barks if config returns a function. Maybe you can get rid of env by using "build": "cross-env NODE_ENV=production webpack" and const mode = process.env.NODE_ENV || 'development' as demonstrated in the template.

@dehmer
Copy link

dehmer commented Feb 27, 2023

Returning multiple configurations won't work either. I would need this for an Electron Build (main and renderer configs.)

At least, I would expect module.exports = [config] from working without warnings.

@dehmer
Copy link

dehmer commented Feb 27, 2023

@dummdidumm

All working flawlessly in 3.1.7:
mode.export = config
mode.export = [config]
mode.export = env => config
mode.export = env => [config]

Thank! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants