Configure custom "root" directory with "babel-plugin-module-resolver"? - javascript

Here is my project structure:
cloudRun
distApp // TRANSPILED APP FILES FROM ./src
distService // TRANSPILED BACKEND FILES FROM ./cloudRun/src
src // SOURCE FILES FOR THE BACKEND CODE
index.js // INDEX.JS FOR THE BACKEND CODE
babel.config.js // CONFIG FOR THE BABEL TRANSPILE SCRIPT
src // SOURCE FILES FOR THE APP
index.js // INDEX.JS FOR THE APP CODE
package.json // THIS IS THE MAIN PROJECT package.json
I'll try to be very succinct and clear.
In both of the index.js (app and backend code) I use path aliases in the source code.
For example:
./src/some-folder/some-file.js
import xxx from "#src/hooks/someHoot";
// IN THE TRANSPILED VERSION #src MUST BE CONVERTED TO ./cloudRun/distApp
And also, for example:
./cloudRun/src/some-folder/some-file.js
import xxx from "#src/hooks/someHoot";
// IN THE TRANSPILED VERSION #src MUST BE CONVERTED TO ./cloudRun/distApp
But somehow I'm having trouble when configuring module-resolver on babel.config.js. Either I get it to work correctly with the path aliases present on ./src (and path aliases on ./cloudRun/src are all wrong by 1 level) or vice-versa.
For example:
.cloudRun/babel.config.js
plugins = [
["module-resolver", {
"alias": {
"#src" : "./distApp",
"#hooks" : "./distApp/hooks",
}
}]
];
This works for the ./src files. But files from ./cloudRun/src are all wrong by 1 level up.
And if I change to this:
.cloudRun/babel.config.js
plugins = [
["module-resolver", {
"alias": {
"#src" : "./cloudRun/distApp",
"#hooks" : "./cloudRun/distApp/hooks",
}
}]
];
Then it works fine for the ./cloudRun/src files. But all files from ./src will be wrong by 1 level down.
I was thinking that I might fix this with the "root" option in the module-resolver config. But I couldn't make it work yet.
Maybe something like this:
.cloudRun/babel.config.js
plugins = [
["module-resolver", {
"root": ["./cloudRun"], // SET A NEW ROOT HERE
"alias": {
"#src" : "./distApp",
"#hooks" : "./distApp/hooks",
}
}]
];
I've tried many things inside the "root" config. But so far it doesn't seem to make any difference.
Here is how I run babel:
// SCRIPTS FROM ./package.json
babel src --out-dir cloudRun/distApp --config-file ./cloudRun/babel.config.js
babel cloudRun/src --out-dir cloudRun/distService --config-file ./cloudRun/babel.config.js

Related

webpack babel how to copy transpiled file if i don't use import/require

I have a file in my project called test.js
I don't import/require it anywhere which means my webpack won't call babel-loader for that js file.
Question: what I want is to move test.js into /dist folder, but as a compiled/transpiled. What's the best practice for it?
What I tried: I tried to use a copy-webpack-plugin and use its transform parameters before copying the file, but I can't seem to find the good babel package.
{
from: 'test.js',
to: '/dist/test.js',
transform(content, path) {
//what do I write here?
},
}
The simplest approach I could think about is to use several entry points like this:
{
entry: {
a: "./your-main-stuff",
b: "./your-single-file",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js"
}
}
which will create your a.js main bundle and b.js file in __dirname/dist folder both transpiled provided you used corresponding loader(s).
And from copy-webpack-plugin docs section:
webpack-copy-plugin is not designed to copy files generated from the
build process; rather, it is to copy files that already exist in the
source tree, as part of the build process.
so it seems to be difficult (if possible) making it move transpiled files.
Update. If you want to output files into different folders w/o changing your src folder, additonal tools needed. For your case (just 1 file) I would write a simple script and add it into package.json script section combined with webpack call like:
"scripts": {
"dev": "webpack && babel path-to-script.js --out-file path-to-script-compiled.js"
}
Just like in the previous answer, initially I went with the "scripts" entry in package.json that runs babel. But for a number of reasons I wanted to use webpack 5 to do the job. So after failing with webpack-copy-plugin and a good amount of digging around I came to this solution:
let config = {
entry: [
glob.sync(srcDir + '/**/*.js') // get all .js files from the source dir
],
output : {
filename : '[name].rem.js', // webpack wants to bundle - it can bundle here ;)
path: outDir
},
resolve: {
alias: {
'app': appDir
}
},
plugins: [
new RemoveEmptyScriptsPlugin({extensions: ['js'], scriptExtensions: /\.rem\.js/}) // for all .js source files that get bundled remove the bundle .rem.js file
],
module: {
rules:[{
test: /\.jsx?$/,
type: 'asset/resource', // get webpack to take it out instead of bundling
generator: {
filename: ({filename}) => filename // return full file name so directory structure is preserved
},
use: {
loader: 'babel-loader',
options: {
targets: { node: 16 },
presets: [
['#babel/preset-env', { modules: 'commonjs' /* transpile import/export */}],
]
}
}
}]
}
};
// Since the code does not go through the full pipeline and imports are not getting resolved, aliases will remain in the code.
// To resolve them it takes to hack the aliases object into the babel config
config.module.rules[0].use.options.plugins.push(['babel-plugin-webpack-alias-7', {config: {resolve: {alias: config.resolve.alias}}}];
And it does a good job but beware that it takes to use the patched versions of the two plugins (unless the patches have been merged already)!

How to create multiple pages with different languages from one template?

I want to generate multiple pages which will have content on different languages from one common template. How can I do it with webpack?
I tried to use different webpack plugins like webpack-static-i18n-html, i18n-webpack-plugin but nothing works for me. The best thing I found is a webpack-static-i18n-html, but it has bad support and this plugin can't watch changes in JSON files with translated text. Below is what I have for now.
This is my code from webpack.common.js.
const Path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const StaticI18nHtmlPlugin = require("webpack-static-i18n-html");
//...
module.exports = {
//...
plugins: [
//...
new StaticI18nHtmlPlugin({
locale: 'en',
locales: ['en', 'ua', 'ru'],
baseDir: Path.posix.join(__dirname, ".."),
outputDir: 'src/localized-pages',
outputDefault: '__lng__/__file__',
localesPath: 'src/locales',
files: 'src/templates/index.html'
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: Path.resolve(__dirname, '../src/templates/index.html')
}),
new HtmlWebpackPlugin({
filename: 'ua/index.html',
template: Path.resolve(__dirname, '../src/localized-pages/ua/src/templates/index.html')
}),
new HtmlWebpackPlugin({
filename: 'ru/index.html',
template: Path.resolve(__dirname, '../src/localized-pages/ru/src/templates/index.html')
}),
//...
],
//...
};
I also have webpack.dev.js and webpack.prod.js which I merge with webpack.common.js via webpack-merge plugin. As you can see, after generating pages, I have to use HtmlWebpackPlugin to serve them. It's awkward to use.
locales folder:
locales
|-en.json
|-ua.json
|-ru.json
en.json file:
{
"key": {
"innerKey" : "value"
}
}
Then plugin generates from:
<p data-t>key.innerKay</p>
this
<p>value</p>
But as I said, If I change en.json nothing will regenerate. I will not use this way to generate multiple pages for different languages.
So, I would like to generate several pages from one template. Is there any way to do this with webpack?
I was working on a multi language admin dashboard with Webpack and was wondering how could I tackle this problem until I found a way to make everything automatic with a multiple language web template.
First of all, webpack-static-i18n-html isn't a good solution because most of its packages are deprecated. But actually the mentioned package is based on a good npm package called node-static-i18n. So, the first thing you need to do is installing this npm package using this command
npm install -g static-i18n
Next, you need to make your translation file as *.json files and in json format and put them in a folder which I named "locales" and I put it in my "src" folder of my project. I need two languages for my website. One of them is English and another is Farsi or Persian. Therefore I made two file namely fa.json and en.json. So, I have folder and file structure like the picture below:
My file and folder structure in my Webpack project
This is part of my en.json file as an example:
{
"menu": {
"items": {
"dashboard": "Dashboard",
"posts": "Posts",
"media": "Media"
},
"sub": {
"items": {
"all-posts": "All Posts",
"add-new-post": "Add New",
"categories": "Categories"
}
}
}
}
This is part of my fa.json file as an example:
{
"menu": {
"items": {
"dashboard": "پیشخوان",
"posts": "نوشته ها",
"media": "رسانه"
},
"sub": {
"items": {
"all-posts": "نوشته ها",
"add-new-post": "افزودن نوشته",
"categories": "دسته ها"
}
}
}
}
and you can use them in your html tags like this:
<span class="className" data-t>menu.items.dashboard</span>
Please notice that for using translation you should use the attribute data-t in your tags like span then you can use key and values saved in your related json file to use translations between your tags. for more information about data-t and its usage please go to the plugin's Github page that I mentioned it earlier in this text on the plugin name.
Next, you should write needed command in the script section of your package.json file to run node-static-i18n to translate your template based on your html template file and save them in i18n folder in root of your project as below:
"scripts": {
"i18n": "static-i18n -l en -i fa -i en src --localesPath src/locales/",
}
in the above command:
-l: The default locale.
-i: the list of locales to be generated.
--localesPath: The directory of the translations, where each file should be named LOCALE_NAME.json
Now if you run npm run i18n this command should make a folder in root path of your project called i18n containing html files in two languages in this case. it will be like the picture below:
i18n folder and translated html files in it
Next you should config your Html Webpack Plugin in your Webpack config file to show these pages in your browser like this:
plugins: [
.
.
.
new HtmlWebpackPlugin({
//inject: false,
chunks: ['main'],
template: 'i18n/index.html',
filename: 'index.html'
}),
new HtmlWebpackPlugin({
//inject: false,
chunks: ['main-rtl'],
template: 'i18n/fa/index.html',
filename: 'fa/index.html'
})
]
because you need to see changes on your browser automatically you need another package called npm-watch to install through this command:
npm install -D npm-watch
Then, you should change script section of your package.json like this:
"scripts": {
"i18n-watch": "watch 'npm run i18n' src",
"i18n": "static-i18n -l en -i fa -i en src --localesPath src/locales/",
}
By using the command npm run i18n-watch whenever you make any changes in your locale files or your original html template in src folder it's gonna re-translate your html file based on new information and if you're running your webpack dev server you can see the result right after you save changes.
After that, to run i18n-watch command and your Webpack dev server at the same time it would be great installing another npm package for this purpose called npm-run-all by using the command below:
npm i -D npm-run-all
Finally, you can change the script section of your package.json like this to run i18n-watch and your Webpack dev server at the same time and after that if you make any changes you can see the result in the browser right after saving changes.
"scripts": {
"i18n-watch": "watch 'npm run i18n' src",
"i18n": "static-i18n -l en -i fa -i en src --localesPath src/locales/",
"webpack-dev": "webpack-dev-server --open --config=config/webpack.dev.js",
"start": "npm-run-all --parallel webpack-dev i18n-watch"
}
Now, if you use npm start in your terminal you will see your Webpack dev server and i18n-watch are running at the same time watching for any changes.
Hopefully this makes sense.

How to add a folder as entry in npm package?

I am trying to publish a npm module. Which has a following folder structure.
In my package.json it has "main": "./dist/" I understand this resolve for index.js. But in the dist folder I have individual files named as string.js, class.js, dom.js I am planning to import them as
import { isValidZipCode } from '#scope/utils/string'; but right now I have to import them as import { isValidZipCode } from '#scope/utils/dist/string';
Is there a way I can resolve a folder when I import a module from node_modules?
EDIT: Main idea is to import the files as import { isValidZipCode } from '#scope/utils/string' when I keep individual files for individual exports.
The other answers are correct for the most part, but I think there's one thing that's missing (either from your OG post or from their answers), which is:
Your folder structure is definitely not standard, which likely led to your current problems as well as non-helpful results in the Google searches when you tried to find an answer.
You didn't show your package.json nor your webpack.config.js file contents, which are the key to answering your question even if you did have such a weird file structure.
Some suggestions:
Change your folder structure to be something along the lines of
/
|--src
|--utils
|--string.js
|--[... other js files]
|--index.js
|--dist (will be generated automatically)
|--[config files, like package.json, webpack.config.js, etc]
Make your webpack.config.js have something along the lines of:
output: {
path: path.resolve(__dirname, 'dist'),
//...
}
plugins: [
new CopyWebpackPlugin({
patterns: [
'ReadMe.md', // optional
'package.json',
'LICENSE.md' // optional
]
})
],
In order to fix/normalize the output (e.g. output would be /dist/utils/[string.js, ...], /dist/package.json).
Then, make your package.json main something like
"main": "utils/string.js"
After doing that, your output should look something like
/
|--src
|--utils
|--string.js
|--[... other js files]
|--index.js
|--dist
|--utils
|--string.js
|--[... other js files]
|--index.js // optional: only if you want to support stuff like
// `import { isValidZip } from '#scope/utils';`
|--package.json
|--[config files, like package.json, webpack.config.js, etc]
Finally, you need to cd dist and run npm publish from inside there. (That's why you need the package.json inside that directory.)
I can't really go into details about the #scope portion since I haven't done that myself, but I did the above for one of my own projects and it worked as expected.
All you need to do is to make a index file in root folder then just export all files with the following:
In your dist/string export each method/function on it, and for the index do it follows:
export * from "./dist";
as it helps maintain code and looks cleaner to eye
Regards :)
Create a index file in root folder then just export all files like this
export { default as Str } from "./dist/string";
export { default as Cls } from "./dist/class";
export { default as Dom } from "./dist/dom";
and also update package.json change main from./dis/ to ./
Hope this will help you. Happy coding.

Webpack && npm, how can I create a script that can both be imported by another webpack project, or included as a script tag?

Let's say I'm using webpack 4 and have a project that looks like this:
/src/index.js
const Important = "Important Text"
export default Important
global.important = Important
I compile it using the following webpack confing:
output: {
libraryTarget: 'commonjs2',
},
module: {
rules: [
{ ...babelConfig }
]
}
And the package.json has:
{
...packageJsonContents
"name": "important-project",
"main": "./dist/main.js",
}
This will create the minified file /dist/main.js
I save that plugin to npm as testproject
Now I create another project, configure Webpack and npm install important-project.
In the /src/index.js:
import Important from 'important-project'
console.log(Important) //prints "Important Text"
This works, however my goal is for this to be a general use plugin and I want to also be able to include it as a script tag in other projects, however when I include it as a script tag, it complains that module is not defined.
<script src="node_modules/important-project/dist/main.js"></script>
What's the conventional approach here? Should I create two builds? One for in html, and another for in webpack?

Typescript doesn't resolve modules correctly after compiling to the js

I set the property paths in the tsconfig.json file, like:
"paths": {
"*": [
"*",
"src/*",
"node_modules/*"
],
"src/*": [ "./src/*" ]
},
And it gets me to be able to take some module more easy:
e.g.
- src
|- moduleA
|- utils
|- moduleB
// moduleA
import { something } from 'utils/moduleB'
but after compile I get next path in the moduleB.js:
something = require('utils/moduleB')
instead of relative path:
something = require('./utils/moduleB')
It doesn't work under Node because Node module resolution system knows nothing about utils folder.
So, how can I force tsc to use relative paths here?
Upd:
Here is the real example the one of my resulting js file:
data & utils they are the inner and not externals modules. My question is why tsc doesn't resolve them according to the baseUrl in the compiled files

Categories