Closed elirov closed 1 year ago
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
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
);
};
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.
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/
@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']
🤞
@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.
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.
@dummdidumm
All working flawlessly in 3.1.7:
mode.export = config
mode.export = [config]
mode.export = env => config
mode.export = env => [config]
Thank! 👍
$ 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:
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?