Get a path to a file from Node application - javascript

I have an application, where structure looks like this:
.
├── index.js
├── unix
│   ├── index.js
└── win32
├── foo.exe
└── index.js
win32/index.js was accessing foo.exe like this:
let command = path.join(__dirname, "foo.exe") + ' -some-arguments';
exec(command);
But now using Webpack I compile my application into one bundle.js and put that foo.exe next to it:
.
├── foo.exe
└── bundle.js
And now path.join(__dirname, "foo.exe") doesn't work anymore. It doesn't find the foo.exe. I've changed it to
let command = path.resolve(
"node_modules/my-library/dist",
"foo.exe"
);
And it works fine but looks like there is a better way to do it.
UPD:
my Webpack config file:
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "production",
entry: "./src",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
libraryTarget: "umd"
},
plugins: [new CopyPlugin([{ from: "./src/win32/foo.exe" }])],
target: "node"
};

To have __dirname behave the usual way, and not being it changed (mocked) by Webpack, you have to add a property in your Webpack config :
module.exports = {
//...
node: {
__dirname: false,
}
};
Documentation

You need to add these node config in your webpack. So you can use __dirname after you built your code with webpack
node: {
__dirname: false,
},

Related

Typescript compiles with warnings

I am restructuring the app and switching to monorepo using CRA and refactoring existing CRA configs.
The folder structure now looks like this.
.
├── api
│ ├── business
│ └── consumer
└── tsconfig.json
├── apps
│ ├── business
└── tsconfig.json
│ │ └── src
│ │ ├── components
│ │ ├── constants
│ │ ├── pages
│ │ └── reducers
│ └── consumer
└── tsconfig.json
│ └── src
│ ├── components
│ ├── constants
│ ├── pages
│ ├── reducers
│ └── selectors
├── config
│ ├── jest
│ └── utils
├── core
│ ├── components
│ ├── helpers
│ ├── services
│ ├── static
│ │ ├── images
│ │ └── styles
│ └── utils
├── public
├── scripts
├── tsconfig.json
├── shared
│ ├── components
│ ├── constants
│ ├── hooks
│ ├── redux
│ ├── selectors
│ └── types
I have added root paths in the root tsconfig.json and extended it for every in-module tsconfig.json.The root tsconfig.json looks like this
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"core/*": [
"core/*"
],
"api/*": [
"api/*"
],
"apps/*": [
"apps/*"
],
"shared/*": [
"shared/*"
],
"types/*": [
"shared/types/*"
],
"styles/*": [
"core/static/styles/*"
],
},
"jsx": "preserve",
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"allowJs": true,
"sourceMap": true,
"skipLibCheck": true,
"noImplicitAny": false,
"esModuleInterop": true,
"resolveJsonModule": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"tsBuildInfoFile": "./buildcache/front-end",
},
"exclude": ["node_modules", "**/*.js"]
}
And here is the tsconfig.json for the business app.
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"components/*": [
"src/components/*"
],
"constants/*": [
"src/constants/*"
],
"helpers/*": [
"src/helpers/*"
],
"pages/*": [
"src/pages/*"
],
"reducers/*": [
"src/reducers/*"
],
"selectors/*": [
"src/selectors/*"
],
"types/*": [
"src/types/*"
],
"core/*": [
"../../core/*"
],
"apps/*": [
"../../apps/*"
],
"shared/*": [
"../../shared/*"
],
"api/*": [
"../../api/*"
]
}
},
"extends": "../../tsconfig.json"
}
I have also refactored paths.js to match my current folder structure ( P.S. - CRA doesn't allow to import files outside of the src so I changed paths ).
appName can be either business or consumer.I am running the app like npm start business which exposes the app name to the node process.
const grpConfig = require('./grp-config');
const parseArgv = require('./utils/parse-argv');
const args = parseArgv(process.argv);
const devMode = process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development';
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const publicUrlOrPath = devMode ? '/' : `${grpConfig.CDN_URL}/${args.appName}/`;
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx',
];
// Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension =>
fs.existsSync(resolveFn(`${filePath}.${extension}`)),
);
if (extension) {
return resolveFn(`${filePath}.${extension}`);
}
return resolveFn(`${filePath}.js`);
};
// config after eject: we're in ./config/
module.exports = {
publicUrlOrPath,
appRoot: resolveApp('.'),
appSrc: resolveApp(`${args.appName}/src`),
appPath: resolveApp(`apps/${args.appName}`),
dotenv: resolveApp('.env'),
appPublic: resolveApp('public'),
yarnLockFile: resolveApp('yarn.lock'),
appHtml: resolveApp(`public/index.html`),
appTsConfig: resolveApp(`apps/${args.appName}/tsconfig.json`),
appJsConfig: resolveApp('jsconfig.json'),
appPackageJson: resolveApp('package.json'),
appNodeModules: resolveApp('node_modules'),
appBuild: resolveApp(`build/${args.appName}`),
appWebpackCache: resolveApp('node_modules/.cache'),
swSrc: resolveModule(resolveApp, 'src/service-worker'),
testsSetup: resolveModule(resolveApp, 'tests/setupTests'),
appIndexJs: resolveModule(resolveApp, `apps/${args.appName}/index`),
appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'),
};
module.exports.moduleFileExtensions = moduleFileExtensions;
And finally the webpack config.
{
// Webpack noise constrained to errors and warnings
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: paths.appIndexJs,
output: {
// The build folder.
path: paths.appBuild,
pathinfo: isEnvDevelopment,
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[name].[hash][ext]',
publicPath: paths.publicUrlOrPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
},
resolve: {
alias: {
// api: path.resolve(__dirname, 'api'),
// apps: path.resolve(__dirname, 'apps'),
// core: path.resolve(__dirname, 'core'),
// shared: path.resolve(__dirname, 'shared'),
components: path.resolve(__dirname, 'apps', 'business', 'src', 'components'),
reducers: path.resolve(__dirname, 'apps', 'business', 'src', 'components'),
// reducers: path.resolve(__dirname, paths.appPath, 'src', 'reducers'),
// pages: path.resolve(__dirname, 'apps', 'business', 'src', 'pages'),
// constants: path.resolve(__dirname, 'apps', 'business', 'src', 'constants'),
// helpers: path.resolve(__dirname, 'apps', 'business', 'src', 'helpers'),
// selectors: path.resolve(__dirname, 'apps', 'business', 'src', 'selectors'),
// types: path.resolve(__dirname, 'apps', 'business', 'src', 'types'),
},
},
module: {
strictExportPresence: true,
},
plugins: [
new ModuleNotFoundPlugin(paths.appPath),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
async: isEnvDevelopment,
typescript: {
typescriptPath: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
configFile: paths.appTsConfig,
context: paths.appPath,
diagnosticOptions: {
syntactic: true,
},
mode: 'write-references',
// profile: true,
},
logger: {
infrastructure: 'silent',
},
}),
].filter(Boolean),
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
}
I have removed nonrelated webpack configs so as not to create a mess for your convenience.
I am not sure what I have set up wrong, but when I am running npm start business, it throws me a lot of warnings like this.
Cannot find module 'reducers/root-reducer' or its corresponding type
declarations.
import { rootReducer } from 'reducers/root-reducer';

Webpack output all the files within a directory with the same name into a different folder

I have the following directory:
- src/
- js/
- profile1/
- script1.js
- script2.js
- profile2/
- script3.js
- script4.js
- script5.js
- dist/
- js/
package.js
webpack.config.js
I am trying to webpack all JS files from /src/js into dist/js folder conserving the same naming and folder structure.
I could write a different config for each file and webpack each JS file, but I am wondering if there is a set of config that I can write to do all the operations in one shot.
I have tried:
const path = require('path');
module.exports = {
entry: './src/js/*.js',
output: {
filename: '*.js',
path: path.resolve(__dirname, 'dist'),
}
}
failed miserably.
You can match the entry file first like using glob (install it with npm i glob). Then you can change the output dynamically by appending [name] to output.filename. Here's the complete code:
const path = require('path');
const glob = require('glob');
module.exports = {
entry: Object.fromEntries(glob.sync(path.resolve(__dirname, 'src/js/**/*.js')).map((v) => [
v.split('src/js/')[1], v,
])),
output: {
filename: '[name]',
path: path.resolve(__dirname, 'dist/js'),
},
};

Webpack: include html files and their js scripts alongside standalone js scripts

I current have a project that follows this structure:
src/
├── browserAction/
│ ├── assets/
│ ├── index.html
│ ├── script.js
│ └── style.css
├── options/
│ ├── assets/
│ ├── index.html
│ ├── script.js
│ └── style.css
├── manifest.json
├── background_script.js
└── content_script.js
I currently have webpack setup to transpile the background and content script with babel and copy the manifest as a standalone file but I can't figure out how to bundle the two index.html files (containing the contents of script.js and style.js) and keep the file structure of being in two separate folders. My current webpack config is:
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
module.exports = {
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
context: path.resolve(__dirname, 'src'),
entry: { background_script: './background_script.js', content_script: './content_script.js'},
module: {
rules: [
{
test: /\.html$/i,
use: [
'file-loader',
'extract-loader',
{
loader: 'html-loader',
options: {
minimize: IS_PRODUCTION,
},
},
],
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
},
},
],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
},
],
},
plugins: [new CopyWebpackPlugin({ patterns: [{ from: 'manifest.json', to: '.' }] }), new CleanWebpackPlugin()],
};
And my intent is the output into dist matches:
dist/
├── browserAction/
│ └── index.html
├── options/
│ └── index.html
├── manifest.json
├── background_script.js
└── content_script.js
What loaders do I need to use to achieve this? I've been experimenting with various ones but I can't get the results I need.
You're looking for html-webpack-plugin. An example configuration:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: {
browser_action: './browserAction/script.js',
options: './options/script.js',
background_script: './background_script.js',
content_script: './content_script.js'
},
module: {
// your loaders
// ...
},
plugins: [
new HtmlWebpackPlugin({
filename: 'browserAction/index.html',
template: 'browserAction/index.html',
chunks: ['browser_action']
}),
new HtmlWebpackPlugin({
filename: 'options/index.html',
template: 'options/index.html',
chunks: ['options']
})
]
}
By the way, it looks like you're trying to write a browser extension; I'd recommend a boilerplate like this which has webpack already configured.

React & Webpack: Loading failed for the <script> with source http://localhost:8080/app/bundle.js

I am new in reactjs. I tried to configure react with basic index page including index.js(containing a console.log()) but when i tried to run server index.html showing properly but bundle.js is not loading. I search it a lot but not getting proper answer can any one help me please.
my webpack.config.js is
// Webpack config js.
var webpack = require("webpack");
var path = require("path");
var DIST_VAR = path.resolve(__dirname, "dist");
var SRC_VAR = path.resolve(__dirname, "src");
var config = {
entry : SRC_VAR + "\\app\\index.js",
output: {
path: DIST_VAR + "\\app\\",
filename: "bundle.js",
publicPath : "\\app\\",
},
module: {
rules: [
{
test: /\.js?/,
include: SRC_VAR,
loader: "babel-loader",
query: {
presets: [ "react" , "es2015" , "stage-2"]
}
}
]
}
};
module.exports = config;
Error is showing in console: Loading failed for the with source “http://localhost:8080/app/bundle.js”.
Edit:
Folder Listing added..
Folder PATH listing
Volume serial number is BE9C-4E51
C:.
| package-lock.json
| package.json
| webpack.config.js
|
+---dist
| | index.html
| |
| \---app
| bundle.js
|
+---node_modules
| <Here the node_modules>
\---src
| index.html
|
\---App
index.js
I'll make some assumptions without seeing your project folder structure.
Looks like it could be your publicPath. Unless that's what you intended, the /app folder shouldn't be visible and since your console is showing "localhost:8080/app/bundle.js" that means it's looking for "project-root/src/app/app/bundle.js" instead of "project-root/src/app/bundle.js"
In the webpack docs they're telling you to default to root '/' and looking at my own webpack file thats what mine is currently set to as well.
Reference:
https://webpack.js.org/guides/public-path/
Edit: Here's an example using Webpack 3. Version 4 just came out and this will not work, so I'd be careful where you're getting your config examples from if you are using Webpack 4.
const webpack = require('webpack');
const path = require('path');
module.exports = {
plugins: [
// new webpack.NamedModulesPlugin(),
// new webpack.HotModuleReplacementPlugin()
],
context: path.join(__dirname, 'src'),
entry: [
// 'webpack/hot/dev-server',
// 'webpack-hot-middleware/client',
// 'babel-polyfill',
// 'history',
'./index.js'
],
output: {
path: path.join(__dirname, 'www'),
filename: 'bundle.js',
publicPath: '/'
},
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel-loader']
}],
resolve: {
modules: [
path.join(__dirname, 'node_modules'),
],
},
};
after installing
npm init -y
and
npm install --save-dev webpack webpack-dev-server webpack-cli
and your structure files
src/
build/
webpack.config.js
package.json
go to package.json, and add build command:
"scripts": {
"start":"webpack serve --mode development",
"build":"webpack"
},
in webpack.config.js
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, './src/index.js'),
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
devServer: {
contentBase: path.resolve(__dirname, './build'),
},
};
so,in your build/index.html
<script type="text/javascript" src="./bundle.js"></script>

Webpack bundle client specific components

I have a product made with React.js (sth like CRM) which I would like to distribute to many of my customers but with client-specific components (override the common ones).
Source folder structure of main codebase looks like this:
...
/src
├── components
│   ├── Sidebar
│   ├── Navbar
│   ├── Buttons
│   ├── Chart
│   └── __tests__
├── containers
│   ├── About
│   ├── App
│   ├── Chat
│   ├── ...
├── helpers
├── redux
│   ...
├── theme
| ...
└── vendor
...
/custom-src
├── clientA
│   ├── components
│   ├── containers
│   └── theme
└── clientB
└── components
...
But, each customer wants custom designed components for their CRM like custom Navbar, custom Sidebar, etc.. + custom UI theme (CSSs).
I don't want to have multiple codebases but also I don't want to distribute each client the same bundled code which will also include custom components of other clients.
Let's say I have clientA. He has some custom made components (which overrides the common ones), custom made containers and specific theme. Til now I was using bash script to merge /src folder with /custom-src/<client> folder but this approach does not seem right to me and also I have to make temp folder outside the working directory which is not very practical.
Does somebody know how can do this using webpack, which I already use for code bundling?
My current webpack configuration looks like this:
{
devtool: 'inline-source-map',
context: path.resolve(__dirname, '..'),
entry: {
'main': [
'webpack-hot-middleware/client?path=http://' + host + ':' + port + '/__webpack_hmr',
'./src/theme/loader!./webpack/styles.js',
'font-awesome-webpack!./src/theme/font-awesome.config.js',
'./src/client.js'
]
},
output: {
path: assetsPath,
filename: '[name]-[hash].js',
chunkFilename: '[name]-[chunkhash].js',
publicPath: 'http://' + host + ':' + port + '/dist/'
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['babel?' + JSON.stringify(babelLoaderQuery), 'eslint-loader']
},
{test: /\.json$/, loader: 'json-loader'},
{
test: /\.less$/,
loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap'
},
{
test: /\.scss$/,
loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap'
},
{test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
{test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream"},
{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file"},
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml"},
{
test: webpackIsomorphicToolsPlugin.regular_expression('images'),
loader: 'url-loader?limit=10240&name=[hash:6].[ext]'
}
]
},
progress: true,
resolve: {
modulesDirectories: [
'src',
'node_modules'
],
extensions: ['', '.json', '.js', '.jsx']
},
plugins: [
// hot reload
new webpack.HotModuleReplacementPlugin(),
new webpack.IgnorePlugin(/webpack-stats\.json$/),
new webpack.DefinePlugin({
__CLIENT__: true,
__SERVER__: false,
__DEVELOPMENT__: true
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),
webpackIsomorphicToolsPlugin.development()
]
}
So I solved this thing using webpack's resolve.alias functionality with dynamically added filenames into it. Here's the code:
// webpack.config.js
let variation = require('./variation')("clientA");
...
let alias = Object.assign(variation, {
"components" : path.resolve(__dirname, '../src/components'),
"config" : path.resolve(__dirname, '../src/config'),
"containers" : path.resolve(__dirname, '../src/containers'),
"helpers" : path.resolve(__dirname, '../src/helpers'),
"theme" : path.resolve(__dirname, '../src/theme'),
"utils" : path.resolve(__dirname, '../src/utils'),
"vendor" : path.resolve(__dirname, '../src/vendor')
});
...
module.exports = {
...
resolve: {
...
alias: alias,
...
},
...
}
.
// variation.js
const fs = require('fs');
const path = require('path');
const walkSync = function (dir, filelist, base) {
const files = fs.readdirSync(dir);
const extension = /\.(js|jsx)$/i;
filelist = filelist || {};
files.forEach(function (file) {
const dirname = dir.replace(base, '').substr(1);
const fullname = dir + '/' + file;
const filename = file.replace(/(.*)\.[^.]+$/, '$1');
if (fs.statSync(dir + '/' + file).isDirectory()) {
filelist = walkSync(dir + '/' + file, filelist, base);
} else {
if (extension.test(file)) {
filelist[dirname + '/' + filename] = fullname;
} else {
filelist[dirname + '/' + file] = fullname;
}
}
});
return filelist;
};
module.exports = function(variation){
const dirname = path.resolve(__dirname, '../custom/' + variation);
const aliasComponents = walkSync(dirname + "/components", {}, dirname);
const aliasContainers = walkSync(dirname + "/containers", {}, dirname);
return Object.assign({}, aliasComponents, aliasContainers);
};
I hope someone will find it helpful.

Categories