How to access DOM from webpack loader - javascript

I'm writing a webpack loader for a specific file extension, and I would like to append a custom import to the DOM
module.exports = function(source) {
...
document.appendChild(myImport)
return `export default 'hello'`;
}
but the DOM is not accessible
ReferenceError: document is not defined
Is there a way to access the DOM from my loader?
My webpack configuration is:
const path = require('path')
module.exports = {
...
module: {
rules: [{
test: /\.myextension$/,
use: {
loader: 'my-loader'
}
}]
}
};

No, the code from loader is going to be ran on a node.js env, which has no power over the dom (browser env). To be able to manipulate the dom, you would have to output a code though loader which would be inserted on that type of file and then when executed on the browser it would do the modifications.
Something like:
module.exports = function(source) {
return `export default function(){
document.appendChild(myImport)
}`;
}

Related

Load a CJS module config inside another file

I'm trying to load a config inside a node module but I'm not sure which is a good practice to do it, I have this scenario:
A config my.config.js:
module.exports = {
content: [
'./src/**/*.{tsx,ts}',
],
}
Then I have a module cli.mjs is supposed to load it:
import arg from 'arg'
import { readFile } from 'fs/promises'
import path from 'path'
let configPath = './my.config.js'
const args = arg({
'--config': String,
'-c': '--config',
})
if (args['--config']) {
configPath = args['--config']
}
console.log(readFile(path.resolve(configPath), { encoding: 'utf8' }))
This will just return a simple string with my config inside, not a javascript object:
`
module.exports = {
content: [
'./src/**/*.{tsx,ts}',
],
}
`
How should I load my config in the right way?
I've saw basically everyone use configs like that in TypeScript or CJS projects, but it's hard to me see how and where they parse these configs, probably I'm missing some basic information about this?

Importing custom CommonJS module fails

I created a CommonJS module in project A in the following way:
const { WebElement } = require('selenium-webdriver');
const { By } = require('selenium-webdriver');
class VlElement extends WebElement {
constructor(driver, selector) {
...
}
async getClassList() {
...
}
}
module.exports = VlElement;
In project B I use the following code:
const VlElement = require('projectA');
class VlButton extends VlElement {
constructor(driver, selector) {
super(driver, selector);
}
...
}
module.exports = VlButton;
When running the code, VLElemlent cannot be found.
It is in my package.json and I can see VLElement under projectB > node_modules > projectA.
What am I doing wrong with my exports?
Thanks in advance.
Regards
Make sure you have a projectB/mode_modules/package.json with a main which points to the file that defines/exports VlElement, like this:
"main": "path/to/file/with/VlElement.js",
When you call require('projectA'); this has to be resolved to a file inside projectA so that it can be evaluated to (and return) the exports from that file. The main entry in the package.json allows this (but defaults to index.js, so if you are using that you don't need package.json, probably, but you should have it anyway).
You can have multiple files with various exports, but remember require('projectA'); can still only return one thing, so the way to do that is usually to have an index.js which looks something like:
module.exports = {
'something': require('./something.js'),
'otherthing': require('./otherthing.js'),
'etc': require('./etc.js'),
};

How to properly export an ES6 module function as a library for use in a node app?

Let's say that I have a node.js application, which does NOT go through my webpack bundling:
Node App
const Html = require('./build/ssr-bundle.js');
let result = Html.ssrbundle.render();
console.log(result);
Here is my ES6/JSX file, which is getting processed by webpack and I want to be able to access that render function in my node app (you guessed right, I am trying to SSR react stuff ;) )
src/Html.js -(webpack)-> build/ssr-bundle.js
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import CustomComponent from './custom-component.js';
module.exports = {
render : function () {
return ReactDOMServer.renderToString(<CustomComponent />);
} };
And here is my Webpack config
webpack.config.js
var path = require('path');
module.exports = {
entry: {
ssr: './src/Html.js',
//frontend: './src/frontend-Html.js'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: 'ssr-bundle.js',
library: 'ssrbundle'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['env','react'],
plugins: ["transform-es2015-destructuring", "transform-object-rest-spread"]
}
},
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
},
stats: {
colors: true
},
devtool: 'source-map'
};
Whatever I do, I cannot figure out how to properly use that exported variable "ssrbundle" and subsequently the render function. If I had my node app included in the bundle, everything would be all right, but this is not what I want to do.
As apokryfos suggested, I played around with the libraryTarget Webpack setting. You can find more info on using Webpack to author a library (what I was really trying to achieve) here:
https://webpack.js.org/guides/author-libraries/
and here are code examples:
https://github.com/kalcifer/webpack-library-example/blob/master/webpack.config.babel.js.
What did the trick for me, was to set the libraryTarget to "umd" , which is different than the "var" setting which is set by default and is suitable i.e. for including the script in an HTML file

How can I use a static helper function(javascript) in SCSS to set the base url for images for different environments?

In my SCSS file I need to use different base urls for different app environments which will be prepended to the image name.
Example:
For production environment
background: url(/prod/image.png);
For development environment
background: url(/dev/image.png);
The helper function which I'm using in the rest of my app returns the base path of the static assets and it looks like this:
static imagePath() {
let imagesPath;
if (this.isProduction()) {
basePath = '/prod';
} else {
basePath = '/dev';
}
return basePath
}
How to achieve this?
Edit:*
I'm using extract-text-webpack-plugin which won't let me output multiple css files.
For example you can have 2 main files (dev.scss and prod.scss) that will look like:
// prod.scss
$basePath: '/prod';
#import "style.scss";
and same for dev.scss.
Otherwise you can use some placeholder for path prefix and substitute it with actual prefix on post-processing step. For example you can use this plugin for PostCSS.
UPDATE:
Following discussion in comments here is (untested) example of how webpack configuration may look like:
module.exports = {
// ....
module: {
rules: [
// ....
{
test: /\.scss$/,
use: {
loader: StringReplacePlugin.replace({
replacements: [
{
pattern: /{urlPrefix}/ig,
replacement: () => process.env.NODE_ENV !== 'production' ? '/dev' : '/prod',
}
]
}, 'sass-loader'),
}
},
// ....
],
},
plugins: [
new StringReplacePlugin(),
// ....
],
// ....
};

Using a script that exposes a global var in a Webpack build

In my (first) Webpack build I'm having trouble comprehending how I should be loading a script that simply exposes a global var.
The script I'm trying to load is basically something like this:
//File: MyLibrary.js
var MyLibrary = (function(window) {
function MyLibrary() {
// Do librarious stuff
}
return MyLibrary;
})(typeof window !== 'undefined' ? window : null);
I figured I should use the exports-loader since according to the docs it should be just the thing for this case:
The file sets a variable in the global context with var XModule = ....
var XModule = require("exports?XModule!./file.js")
So I put this in my config:
module: {
loaders: [
{
test: /MyLibrary\.js$/,
loader: "exports?MyLibrary!./MyLibrary.js"
}
]
}
But this results in an error:
ERROR in Loader MyLibrary.js didn't return a function
which confuses me, since it's not supposed to return a function, that's the whole point why I'm using this particular loader...
So how should I load the script?
you don't specify the path to the library in loader property, simply:
module: {
loaders: [
{
test: /MyLibrary\.js$/,
loader: "exports?MyLibrary"
}
]
}

Categories