Webpack require.context does not work with Path.resolve - javascript

webpack.config.js
"use strict";
const Path = require("path");
const resolvePath = (...paths) => Path.resolve(__dirname, ...paths);
module.exports = {
entry: {
data: "./src/data/index.ts" // resolvePath("src", "data", "index.ts") does not work
}
/** more **/
}
index.ts
const req = require.context("./yaml", true, /\.ya?ml$/i);
req.keys().forEach((key: any) => req(key));
Using "./src/data/index.ts" or resolvePath("src", "data", "index.ts") compile the code. But only "./src/data/index.ts" includes the YAML files. YAML files are located at ./src/data/yaml.
How does Path.resolve affect require.context? If I want to use Path.resolve, how should I write the correct require.context?

Related

Webpack configuration problem with module.exports entry

I'm trying to configurate webpack but I stuck because of this error. I think the problem is with entry. But when i try to add it without path: like in tutorial i get "(property) path: path.PlatformPath" ',' expected
const webpack = require('webpack')
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const IS_DEVELOPMENT = process.env.NODE_ENV === 'dev'
const dirApp = path.join(__dirname, 'app')
const dirAssets = path.join(__dirname, 'assets')
const dirStyles = path.join(__dirname, 'styles')
const dirNode = 'node_modules'
module.exports = {
entry: {
path: path.join(dirApp, 'index.js'),
path: path.join(dirStyles, 'index.scss'),
},
resolve: {
modules: {
dirApp,
dirAssets,
dirStyles,
dirNode
}
}
}
I get this error
[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.resolve.modules should be an array:
object { alias?, aliasFields?, byDependency?, cache?, cachePredicate?, cacheWithContext?, conditionNames?, descriptionFiles?, enforceExtension?, exportsFields?, extensionAlias?, extensions?,
fallback?, fileSystem?, fullySpecified?, importsFields?, mainFields?, mainFiles?, modules?, plugins?, preferAbsolute?, preferRelative?, resolver?, restrictions?, roots?, symlinks?, unsafeCache?, useSyncFileSystemCalls? }
-> Folder names or directory paths where to find modules.
Your resolve.modules is Object, but should be Array. Check here for docs.

How to import a namespaced css from node_modules in the entries of a rewired create-react-app? Is react-app-rewire-multiple-entry the way to go?

I have a create-react-app not ejected and rewired with react-app-rewired and customized with customize-cra.
This is the scenario and I currently can't change it.
Here it is the configuration of config-overrides.js:
const path = require('path')
const fs = require('fs')
const {
override,
overrideDevServer,
watchAll,
removeModuleScopePlugin,
addWebpackModuleRule
} = require('customize-cra')
const theme = process.env.REACT_APP_THEME
const currentDirectory = fs.realpathSync(process.cwd())
const resolveApp = relativePath => path.resolve(currentDirectory, relativePath)
module.exports = {
webpack: override(
removeModuleScopePlugin(),
addWebpackModuleRule({
test: /\.svg$/,
loader: 'raw-loader',
include: [
resolveApp('../source/' + theme + '/icons/dist')
]
})
),
devServer: overrideDevServer(
watchAll()
),
paths: paths => {
paths.appBuild = path.join(paths.appBuild, theme)
return paths
}
}
A new need now is to import in the app some css from a local package, setup in package.json with a local namespace
"dependencies": {
"#namespace/helpers": "*",
I thought to use react-app-rewire-multiple-entry that seems the perfect lib to import multiple entries for a rewired create-react-app
Here is the new update:
const path = require('path')
const fs = require('fs')
const {
override,
overrideDevServer,
watchAll,
removeModuleScopePlugin,
addWebpackModuleRule
} = require('customize-cra')
const theme = process.env.REACT_APP_THEME
const currentDirectory = fs.realpathSync(process.cwd())
const resolveApp = relativePath => path.resolve(currentDirectory, relativePath)
// new css entries configuration
const cssEntries = require('react-app-rewire-multiple-entry')([
{
entry: './src/index.tsx',
helpers_1: '../node_modules/#namespace/helpers/dist/index.css',
helpers_2: '#namespace/helpers/dist/index.css'
}
])
module.exports = {
webpack: override(
cssEntries.addMultiEntry, // new css entries
removeModuleScopePlugin(),
addWebpackModuleRule({
test: /\.svg$/,
loader: 'raw-loader',
include: [
resolveApp('../source/' + theme + '/icons/dist')
]
})
),
devServer: overrideDevServer(
watchAll()
),
paths: paths => {
paths.appBuild = path.join(paths.appBuild, theme)
return paths
}
}
But both approaches currently implemented (first helpers_1: '../node_modules/#namespace/helpers/dist/index.css', and then helpers_2: '#namespace/helpers/dist/index.css') are not loading the css in the create-react-app.
Any ideas to fix it?
Or something wrong that you see?
Thanks in advance

Solidity: How to compile multiple smart contracts in compile.js file?

I would like to compile multiple contracts in one compile.js file but I'm not sure how to do it.
My compile.js file with a single contract looks like this:
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const lotteryPath = path.resolve(__dirname, 'contracts', 'Lottery.sol');
const source = fs.readFileSync(lotteryPath, 'utf8');
module.exports = solc.compile(source, 1);
How can I add more contracts to the compile.js file? I understand that the 1 must be changed to the number of contracts, but not sure what else is required?
Here is an example I did. You can find it in my public repo. Briefly, I have a "build" folder where I write the output of each compiled contract to Json files.
const path = require("path"); //nodejs ’path’ module
const solc = require("solc"); //solidity compiler module
const fs = require("fs-extra"); //file system module
// Feth path of build
const buildPath = path.resolve(__dirname, "build");
const contractspath = path.resolve(__dirname, "contracts");
// Removes folder build and every file in it
fs.removeSync(buildPath);
// Fetch all Contract files in Contracts folder
const fileNames = fs.readdirSync(contractspath);
// Gets ABI of all contracts into variable input
const input = fileNames.reduce(
(input, fileName) => {
const filePath = path.resolve(__dirname, "contracts", fileName);
const source = fs.readFileSync(filePath, "utf8");
return { sources: { ...input.sources, [fileName]: source } };
},
{ sources: {} }
);
// Compile all contracts
const output = solc.compile(input, 1).contracts;
// Re-Create build folder for output files from each contract
fs.ensureDirSync(buildPath);
// Output contains all objects from all contracts
// Write the contents of each to different files
for (let contract in output) {
fs.outputJsonSync(
path.resolve(buildPath, contract.split(":")[1] + ".json"),
output[contract]
);
}
Basically, if you do not change your path struct to mine, you have to change you this part of the above code:
// Feth path of build
const buildPath = path.resolve(__dirname, "build");
const contractspath = path.resolve(__dirname, "contracts");
The approved solution does not work for solidity >0.6.0 and <=0.8.1.
For the mentioned versions, I solved it as follows:
const path = require("path");
const fs = require("fs-extra");
const solc = require("solc");
const buildPath = path.resolve(__dirname, "build");
fs.removeSync(buildPath);
const contractPath = path.resolve(__dirname, "contracts");
const fileNames = fs.readdirSync(contractPath);
const compilerInput = {
language: "Solidity",
sources: fileNames.reduce((input, fileName) => {
const filePath = path.resolve(contractPath, fileName);
const source = fs.readFileSync(filePath, "utf8");
return { ...input, [fileName]: { content: source } };
}, {}),
settings: {
outputSelection: {
"*": {
"*": ["abi", "evm.bytecode.object"],
},
},
},
};
// Compile All contracts
const compiled = JSON.parse(solc.compile(JSON.stringify(compilerInput)));
fs.ensureDirSync(buildPath);
fileNames.map((fileName) => {
const contracts = Object.keys(compiled.contracts[fileName]);
contracts.map((contract) => {
fs.outputJsonSync(
path.resolve(buildPath, contract + ".json"),
compiled.contracts[fileName][contract]
);
});
});
be sure to check that your pragma solidity x.x.x matches with the version specified in your package.json. For example, if I'm using solidity 0.6.12 my solidity compiles would be:
"dependencies": {
...
"solc": "^0.6.12",
...
}
compile.js:
const path= require('path');
const solc = require('solc');
const fs = require('fs-extra');
const builtPath = path.resolve(__dirname, 'build');
//remove file in build module
fs.removeSync(builtPath);
const healthPath = path.resolve(__dirname, 'contract','health.sol');
//read content present in file
console.log(healthPath);
const source = fs.readFileSync(healthPath,'utf8');
//compile contract
const output = solc.compile(source,1).contracts;
//create build folder
fs.ensureDirSync(builtPath);
console.log(output);
for(let contract in output)
{
fs.outputJsonSync(
path.resolve(buildPath, contract.replace(':','')+ '.json'),
output[contract]
);
}

Copy and modify html index file when building with webpack

My current structure of files goes like this
root/
prod/
index.html
prod.js
webpack.config.js
index.html
bundle.js
...
bundle.js and prod.js generated from different webpack builds (separate commands)
root/prod/index.html file
<script src="./prod.js">
rest of the html tags
root/index.html file
<script src="./bundle.js">
rest of the html tags
what i need to do is everytime i run the prod build (builds files under the prod folder) is to copy the root/index.html to root/prod/index.html but modify
this line <script src="./bundle.js">
to this <script src="./prod.js">
i currently using the copy-webpack-plug in but this does not have an option to modify a file when coping
There is any way to achieve this kind of behavior instead of changing root/prod/index.html manually every time (i mean replacing the script tag src attribute)?
my current webpack build looks like this
const path = require('path');
const fs = require('fs');
const gracefulFs = require('graceful-fs');
gracefulFs.gracefulify(fs);
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const entryPath = './entry-webpack.js';
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = (env) => {
console.log('NODE_ENV: ', env.NODE_ENV);
const ENV = env.NODE_ENV;
let plugins = [];
let enviroment;
let bundleName;
let exportPath;
if (ENV === 'prod') {
enviroment = 'production';
plugins = [
new UglifyJsPlugin({include: /\.min\.js$/}),
new CopyPlugin([
{ from: 'index.html', to: './prod/index.html' }
])
];
bundleName = 'prod';
exportPath = './prod/[name].js';
} else {
enviroment = 'development';
bundleName = 'bundle';
exportPath = './[name].js';
}
return {
context: path.join(__dirname, ''),
mode: enviroment,
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',
}
]
},
plugins,
entry: {
[bundleName]: entryPath,
},
output: {
path: __dirname,
filename: exportPath,
}
};
};
Not a solution but a workaround using webpack-shell-plugin
adding this to webpack plugins (executes anything pre and after build)
new WebpackShellPlugin(
{
onBuildStart:['echo "---Webpack Start---"'],
onBuildEnd:['node replace-script']
})
plus replace-script.js script
const fs = require('fs')
fs.readFile('./docs/index.html', 'utf8', (err, data) =>
err ? console.log("ERROR" + err)
: fs.writeFile(
'./docs/index.html',
data.replace(`<script src="bundle.js">`, `<script src="prod.js"></script>`),
'utf8',
(err) =>
err ? console.log("ERROR" + err)
: console.log("SUCCESS")
)
);
a better solution without the need to add a new script would be much appreciated

How to import javascript export file generated from the webpack.config?

Problem description
I need to set the DEV or Production environment so I can test API's either locally or get them ready for production.
ie: Either use http://localhost/api/app/login or /api/app/login
Now I was able to accomplish this by attaching NODE_ENV variables to my npm scripts in the package.json and a couple of lines of code in my webpack.config as so:
"scripts": {
"dev": "NODE_ENV=development webpack-dev-server --history-api-fallback",
"prod": "NODE_ENV=production webpack-dev-server -p",
webpack.config
const environment = process.env.NODE_ENV;
....
new CopyWebpackPlugin([{ from: "static" }])
^ that will create an env I can use in my services/api.js file. Now my Problem is that my Jest tests will fail every time because env is undefined.
Attempted solution - need help
So now instead what I'm trying to do is use node to actually generate a Javascript file that I can actually import directly into my services/api.js that way I avoid the undefined env error in testing.
I'm able to create a Javascript file with the following updates to my webpack.config
const fs = require('fs');
const webpack = require('webpack')
const environment = process.env.NODE_ENV;
// fs.writeFileSync('src/consts/env.txt', environment);
const stream = fs.createWriteStream("src/consts/endpoints.js");
stream.once('open', function(fd) {
stream.write('export const environment = () => "'+environment+'"');
stream.end();
});
The file it created (src/consts/endpoints.js):
export const environment = () => "development"
I've also tried this way:
export const environment = "development"
Now my updated services/api.js
import axios from 'axios'
import environment from '../consts/endpoints'
console.log('api.js environment:', environment);
However environment is undefined when I check out localhost.
How can I fix this problem? Is it a race condition? Is there another way to generate the file I need to import?
I tried to generate a .txt file however I can't import that file locally, can only do it in the cloud.
Full webpack.config file
const fs = require('fs');
const webpack = require('webpack')
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
const dist = path.resolve(__dirname, "dist");
const src = path.resolve(__dirname, "src");
const environment = process.env.NODE_ENV;
// fs.writeFileSync('src/consts/env.txt', environment);
const stream = fs.createWriteStream("src/services/environment.js");
stream.once('open', function(fd) {
stream.write('export const environment = "'+environment+'"');
stream.end();
});
module.exports = {
context: src,
entry: [
"./index.js"
],
output: {
path: dist,
filename: "manage2.bundle.js",
publicPath: '/static/',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: ["css-loader", "sass-loader"],
publicPath: dist
})
}
]
},
devServer: {
hot: false,
quiet: true,
publicPath: "",
contentBase: path.join(__dirname, "dist"),
compress: true,
stats: "errors-only",
open: true
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
new ExtractTextPlugin({
filename: "manage2.css",
disable: false,
allChunks: true
}),
new CopyWebpackPlugin([{ from: "static" }])
]
};
// new webpack.DefinePlugin({ env: JSON.stringify(environment) })
Figured it out!
I was importing the file incorrectly.
So from webpack.config
const environment = process.env.NODE_ENV;
const stream = fs.createWriteStream("src/services/environment.js");
stream.once('open', function(fd) {
stream.write('const env = "'+environment+'"\n');
stream.write('export default env');
stream.end();
});
^ That generates this file (src/services/environment.js) which contains:
const env = "development"
export default env
Finally in my services/api.js file I can use the import statement the way I wanted, I was missing the export default from above.
import axios from 'axios'
import endpoints from './endpoints'
import env from './environment'
const api = endpoints(env);
console.log('api', api);
const log = (method, err) => {
console.error(`%c${method}`, 'background: #393939; color: #F25A43', err);
return err;
};
export const userLogin = (username, password) => {
const post_data = { username, password };
return axios.post(api.login, post_data)
.then(res => res)
.catch((err) => log('api.userLogin', err));
};
And in my package.json I can change the ENV var to spit out "development" or "production".
"scripts": {
"dev": "NODE_ENV=development webpack-dev-server --history-api-fallback",
"prod": "NODE_ENV=production webpack-dev-server -p",

Categories