raw-loader errors out when attempting to require any .md file.
Adding raw loader to import markdown files:
test: /\.md$/i,
use: [{
loader: 'raw-loader',
options: {
esModule: false
}
}],
In .js file, require the markdown file..
return require(postPath)
// postPath is '../posts/awards.md'
Error: Cannot find module '../posts/awards.md'
at webpackEmptyContext (eval at <path to file>)
....
the path to markdown file is the relative path: /posts/awards.md
If I change awards.md to awards.json it works. So maybe it is an issue with raw-loader looking for a export in awards.md and not find one, thus erroring out? Isn't the point of esModule: false to instruct Webpack to NOT treat it as module?
Seems like you're having the same problem as this person.
Quoting an answer:
Webpack performs a static analyse at build time.
It doesn't try to infer variables which that import(test) could be anything, hence the failure.
It is also the case for import(path+"a.js").
If you need truly dynamic imports, you have to restrict it to a known path:
import("./locales/" + locale + ".js")
I re-created your problem and sure enough:
function test(mod) {
return require(mod)
}
console.log(test("./test.md"))
Doesn't work. HOWEVER this works:
function test(mod) {
return require("./" + mod)
}
console.log(test("test.md"))
So it should suffice to change your code to this:
return require("../" + postPath + ".md")
And change postPath to:
// postPath is 'posts/awards'
Related
I'm trying to convert out codebase which is comprised of multiple repositories to use modules instead of namespaces.
Sorry ahead of time if my understanding is fundamentally wrong, my main expirience in javascript is in this company so this is all I've ever known.
The codebase has heiharchies where certain repositories inherit from the base one so for instance if we have A,B,C,D,E then B,C,D,E all know A.
C might know B, but D only knows A, while E knows D and C. (Attached image to be clear)
Right now we have every repository compile into 1 js file using tsconfig outFile, and we load all of it in our html file one by one in order in script tags.
I've started converting the "upper" repos as modules can use namespaces, (so in my example E and D). I changed all of the code inside the repositories themselves to be modular and not use namespaces.
It now compiles properly and works (individually - the issue I'm having is with making them import from one another).
Since I'm trying to preserve the behavior it seems like I need to use a bundler.
I'm trying to use webpack but I'm having some problems with it.
I created a package.json (we didn't have one because we just directly made one js file and put it in the html until now yes that means we couldn't import things properly it's a nightmare it's why I'm trying to change it).
Installed webpack ts-loader and yarg.
I've then made the webpack.config.js I linked below and tried compiling.
Some of my repos don't have good entry files because they are initialized by other repos so I have to make a massive list and check that all files are at least written in the JS (there has to be a solution for this i'm missing right?)
The other issue is I don't understand how to make the other repos import from the repo I just bundled.
I put the bundled.js in the node_modules but when I attempt to import a class from it I get the following error:
"TS2305: Module '"../../node_modules/#types/hybridPanel.js"' has no exported member 'test'.
(I am trying to load D in E in this instance both have been converted to modules and are not using namespaces so this should work).
Do I need to publish the repo somewhere and npm install it in repo2 (I thought that's the same thing as moving the js over).
So the questions I have are:
How do I make this work?
What do I do about the entry files issue?
Am I even going about this the right way (will I even get types if I just import the js)?
Will we need to change our tags in the html to be type=module instead of javascript?
If I were to add a module (using npm or similar) will I then need to add it to the externals tag in the webpack? I'm not confident in my understanding just yet.
Is this the correct way of converting to modules from namespaces? Our plan is if I can get this to work to convert the rest of the higher ones next and then do the lower ones all at once (the base ones multiple people use) and fix the imports in the higher ones once we get there.
webpack config link :
https://pastebin.com/iNqV1dEV
const webpack = require("webpack");
const path = require("path");
const yargs = require("yargs");
const env = yargs.argv.env; // use --env with webpack 2
const pkg = require("./package.json");
const shouldExportToAMD = yargs.argv.amd;
let libraryName = pkg.name;
let outputFile, mode;
if (shouldExportToAMD) {
libraryName += ".amd";
}
if (env === "build") {
mode = "production";
outputFile = libraryName + ".min.js";
} else {
mode = "development";
outputFile = libraryName + ".js";
}
const config = {
mode: mode,
entry: [__dirname + "/src/panel/MoPanelManager.ts", __dirname + "/src/panel/chat/MoSingleChat.ts", __dirname + "/src/panel/booth/MoBoothDisplays.ts", __dirname + "/src/panel/chat/MoGifs.ts", __dirname + "/src/settings/MoSettings.ts"],
devtool: "source-map",
output: {
path: __dirname + "/www/module",
filename: outputFile,
library: libraryName,
libraryTarget: "umd",
libraryExport: "default",
umdNamedDefine: true,
globalObject: "typeof self !== 'undefined' ? self : this",
},
module: {
rules: [
{
test: /\.ts?$/,
use: {
loader: 'ts-loader',
},
exclude: /(node_modules|bower_components)/,
},
],
},
resolve: {
modules: [path.resolve("./node_modules"), path.resolve("./src")],
extensions: [".ts", ".js"]
},
};
module.exports = config;
Image of the repo example
I'd like to build html/index.pug into dist/index.html using Webpack 5.
With Webpack 4 I used to use file-loader for this, but it seems deprecated in Webpack 5: no mentions of it in the Loaders page. The Webpack 5 solutions seems to be using Asset Modules: that page makes it clear that file-loader was the old solution, for Webpack 4.
So far I failed to get it to work though. These are a couple of configurations I tried in my webpack.config.js's module.rules:
1. Using the pug-loader only
{
test:path.resolve('./html/index.pug'),
type:'asset/resource',
loader:'pug-loader',
generator:{filename:'index.html'}}
}
This creates a dist/index.html file which contains the following:
var pug = require("!../node_modules/pug-runtime/index.js");
function template(locals) {var pug_html = "", pug_mixins = {}, pug_interp;pug_html = pug_html + "\u003C!DOCTYPE html\u003E\u003Chtml lang=\"en\"\u003E\u003Chead\u003E\u003Ctitle\u003E" + (pug.escape(null == (pug_interp = "Hello World") ? "" : pug_interp)) + "\u003C\u002Ftitle\u003E\u003C\u002Fhead\u003E\u003Cbody\u003E\u003Cp\u003EHello World\u003C\u002Fp\u003E\u003C\u002Fbody\u003E\u003C\u002Fhtml\u003E";;return pug_html;};
module.exports = template;
It looks like pug-loader converts the pug file into a JavaScript module which generates the html code when it's called. What I want is the resulting HTML code, instead of a JS function that generates it.
2. Using val-loader to execute the JavaScript module generated above
{
test:path.resolve('./html/index.pug'),
type:'asset/resource',
use:['val-loader','pug-loader'],
generator:{filename:'index.html'}}
}
This doesn't work either: Webpack throws an error when it tries to build dist/index.pug:
ERROR in ./html/index.pug
Module build failed (from ./node_modules/val-loader/dist/cjs.js):
Error: Unable to execute "/home/bluenebula/work/webpack5-test/html/index.pug": Error: Cannot find module '!../node_modules/pug-runtime/index.js'
Note that /home/bluenebula/work/webpack5-test/node_modules/pug-runtime/index.js does exist.
Question
Are Asset Modules the right tool to generate an HTML file from a Pug one using Webpack 5?
What else am I supposed to do?
How do I get it to work?
(Pug 3.0.2)
I replaced the loader 'pug-loader' with '#webdiscus/pug-loader',
and it works.
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.pug'),
filename: 'index.html',
}),
],
module: {
rules: [
{
test: /\.pug$/,
loader: '#webdiscus/pug-loader',
},
],
},
The reference I followed :
https://github.com/webdiscus/pug-loader#usage-in-javascript
Although my dev server is running on localhost:3000, I have set up my host file to point www.mysite.com to localhost. In my JavaScript, I have code like:
import myImage from '../assets/my-image.jpg'
const MyCmp => <img src={myImage} />
Using Webpack's file-loader, it transforms that import into a URL to the hosted image. However, it uses the localhost path to that image, but I'd like it to use the www.mysite.com domain. I looked at both the publicPath and postTransformPublicPath options for file-loader, but those only appear to allow you to modify the part of the path that comes after the domain.
I personally don't like the notion of defining host-information statically in the build output. This is something that should be determined in runtime based on where you actually put your files.
If you are like me then there are two options here.
Both involve you calling a global method that you have defined on i.e. window / global scope.
The purpose of the global method is to resolve the root path (the domain, etc) in runtime.
Define a global method
So lets say you define a method on the global scope somewhere in your startup code like so:
(<any>window).getWebpackBundleRootPath = function (webpackLibraryId) {
if (webpackLibraryId == null) return throw "OOOPS DO SOMETHING HERE!";
// Preferably these variables should be loaded from a config-file of sorts.
if(webpackLibraryId == "yourwebpacklibrary1") return "https://www.yoursite.com/";
// If you have other libraries that are hosted somewhere else, put them here...
return "...some default path for all other libraries...";
};
The next step is to configure webpack to call this global method when it tries to resolve the path.
As I mentioned there are two ways, one that manipulates the output of the webpack and one that is more integrated in webpacks configuration (although only for file-loader but I think it should suffice).
It's worth mentioning that you don't need a global method if you only have one bundle or if you host all your bundles in one place. Then it would be enough to use a global variable instead. It should be quite easy to modify the example below to accommodate this.
First option: configure webpack file-loader to call your method when resolving path
This solution will not require something to be done post build. If this fits your need and covers all scenarios I would go for this option.
Edit your webpack config file
var path = require('path');
let config = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
module: {
rules: [{
// Please note that this only defines the resolve behavior for ttf. If you want to resolve other files you need to configure the postTransformPublicPath for those too. This is a big caveat in my opinion and might be a reason for using second option.
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
outputPath: 'assets/fonts', // The folder where you store your fonts.
name: '[name].[ext]',
// This is where the magic happens. This lets you override the output of webpack file resolves.
postTransformPublicPath: function (p) {
// Because of the way webpack file-loader works the input from to this method will look something like this: __webpack_public_path__ + "/assets/fonts/yourfont.ttf"
// But we are not interested in using the __webpack_public_path__ variable so lets remove that.
p = p.replace('__webpack_public_path__ + ', '');
// Return a stringified call to our global method and append the relative path to the root path returned.
return `getWebpackBundleRootPath("${config.output.library}") + ${p}`;
}
}
}]
},
},
...
};
module.exports = config;
As you might have noticed in the comments in the webpack config file you need to specify the resolve behavior for each file-loader that you add (if someone knows a better way, please let me know). This is why I still use the second option.
Second option: manipulate the output of the webpack in a postbuild step
Example webpack.config.js file
For completeness sake here is an example of a webpack.config.js file that contains the variables used in the postbuild script.
var path = require('path');
module.exports = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
...
}
Create a postbuild.js file
Create a file postbuild.js next to your package.json with the following content:
const fs = require('fs');
// We take the path to the webpack config file as input so that we can read settings from it.
const webpackConfigFile = process.argv[2];
// Read the webpack config file into memory.
const config = require(webpackConfigFile);
// The file to manipulate is the output javascript bundle that webpack produces.
const inputFile = config.output.path + config.output.filename;
// Load the file into memory.
let fileContent = fs.readFileSync(inputFile, 'utf8');
// Replace the default public path with the call to the method. Please note that if you specify a publicPath as '/' or something very common you might end up with a problem so make sure it is unique in the file to avoid other unrelated stuff being replaced as well.
fileContent = fileContent.replace('"' + config.output.publicPath + '"', 'getWebpackBundleRootPath("' + config.output.library + '")');
// Save the manipulated file back to disk.
fs.writeFileSync(inputFile, fileContent, 'utf8');
Call the postbuild.js automatically on build
Next step is to actually call the postbuild.js script after each build.
This can be done in a postscript in package.json like so (in the script section in your package.json):
{
"scripts": {
"build": "webpack",
"postbuild": "node postbuild.js ./webpack.config.js"
}
}
From now on whenever you run the build script it will also run the postbuild script (from npm or yarn, etc).
You can of course also manually run the postbuild.js script manually after each build instead.
but those only appear to allow you to modify the part of the path that comes after the domain.
Not really, you can give it an URL that includes the domain.
In your case, assuming your images are under the assets directory, you will have something like this in your webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpe?g|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
publicPath: 'https://www.example.com/assets',
outputPath: 'assets'
}
}
},
...
]
}
...
Webpack's Rule option presents two things (in a full, not shortcut syntax): resource and issuer.
In a Rule the properties test, include, exclude and resource are matched with the resource and the property issuer is matched with the issuer.
So, it is somewhat clear what properties are related to a resource:
{
resource: {
test: ...,
include: ...,
exclude: ...,
},
issuer: { ...boom?? }
}
But what properties are matched with the issuer? There is just nothing for issuer in their docs:
A Condition matched with the issuer. See details in Rule conditions.
And details do not explain issuer.
Why? They have created an option, but haven't decided on its properties yet?
They have created an option, but haven't decided on its properties yet?
The value of issuer is a Condition. The most common Condition is an object with test, include and/or exclude properties, which you have used for the resource. Everything you can use for resource, you can also use for issuer.
In fact, Rule.resource expects itself a Condition, excerpt from the docs:
Rule.resource
A Condition matched with the resource. You can either supply a Rule.resource option or use the shortcut options Rule.test, Rule.exclude, and Rule.include.
The only difference to the issuer is that there are shortcuts (Rule.test, Rule.exclude and Rule.include), because that's the majority of the use-cases. It roughly translates to:
resource: {
test: Rule.test,
exclude: Rule.exclude,
include: Rule.include,
}
And details do not explain issuer.
Clicking on See details in Rule conditions leads to a description, which even contains an example. Excerpt:
Rule Conditions
There are two input values for the conditions:
The resource: An absolute path to the file requested. It's already resolved according to the resolve rules.
The issuer: An absolute path to the file of the module which requested the resource. It's the location of the import.
Example: When we import "./style.css" from app.js, the resource is /path/to/style.css and the issuer is /path/to/app.js.
That is definitely an explanation, but maybe it isn't good enough, so I'll explain it in more details.
To illustrate the purpose of issuer I will use a contrived example, which could potentially be a use-case for it. Let's assume that you have some JavaScript code, which you would like to show to the user (the actual code) and at the same time you want to run that code in another part of the application. The code in question will be a simple greeting function.
greeter.js
export default function greet(name) {
console.log(`Hello ${name}!`);
}
If we want to show the source of greeter.js, we could read it from the file system, but because we'd like to run it in the browser, this is not an option. As we are using webpack, we can use the raw-loader to import the greeter.js file as a string instead of JavaScript. Assuming we have configured it, we can create a module that prints the source code.
printer.js
import greetSource from "./greeter";
export default function printSource() {
console.log(greetSource);
}
In our entry point we want to use the greeter and the printer at the same time.
index.js
import greet from "./greeter";
import printSource from "./printer";
greet("World");
printSource();
Now we have a problem, because we've configured raw-loader for greeter.js, therefore greet will be a string, not a function and that will cause a runtime error. What we want is to import greeter.js in index.js as a regular JavaScript file, but we want to import it as a string in printer.js. We could use an inline loader definition, but that would be rather tedious.
This is where issuer comes in. Whichever JavaScript file is imported from printer.js should be passed through the raw-loader, and from any other file we'd like to use babel-loader.
We will define two webpack rules. Both rules target only JavaScript files, so we test for the .js file ending, for every file that is imported, this it the resource. We would like to know which file imported it (where the import statement was), this is the issuer.
printer.js
// Resource: greeter.js, Issuer: printer.js
import greetSource from "./greeter";
index.js
// Resource: greeter.js, Issuer: index.js
import greet from "./greeter";
For the rules, it means that we want to exclude printer.js as an issuer from the babel-loader rule, and include only printer.js for the raw-loader rule.
module: {
rules: [
{
loader: "babel-loader",
resource: {
test: /\.js$/
},
issuer: {
exclude: /printer\.js$/
}
},
{
loader: "raw-loader",
resource: {
test: /\.js$/
},
issuer: {
include: /printer\.js$/
}
}
]
}
Note: It's not necessary to include the resource option for the raw-loader rule and if you leave it out, it would apply the raw-loader to everything that is being imported in printer.js, which may or may not be what you want (think of including CSS to style the output)
Bundling and running the above code will produce the following output:
Hello World!
export default function greet(name) {
console.log(`Hello ${name}!`);
}
I am trying to set up my project to use rollup, as part of an angular2 move to AOT compilation, however, I am getting the following issue.
Error: 'Subject' is not exported by node_modules\rxjs\Subject.js
This is my rollup.js file:
import rollup from 'rollup';
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify'
export default {
entry: 'client/main.js',
dest: 'public/assets/js/build.js',
sourceMap: false,
format: 'iife',
plugins: [
nodeResolve({jsnext: true, module: true}),
commonjs({
include: 'node_modules/rxjs/**',
include: 'node_modules/angular2-jwt/**',
}),
uglify()
]
}
Why is this happening, I have followed the angular2 cookbook guide?
You'll need to use the namedExports option with rollup-plugin-commonjs: https://github.com/rollup/rollup-plugin-commonjs#custom-named-exports.
Also, you may find it useful to include: 'node_modules/**' rather than individual packages, as otherwise any dependencies of your dependencies will bypass the plugin (in the config above, you have duplicate include properties – perhaps that's just a typo? If you need to pass multiple values, use an array).
commonjs({
include: 'node_modules/**',
namedExports: {
'node_modules/rxjs/Subject.js': [ 'Subject' ]
}
})
I finally figured this out on my system.
The named export solution is wrong since rollup-plugin-commonjs will handle the exports in Subject.js just fine.
The problem for me was the "includes" option in rollup-plugin-commonjs.
There are two issues.
Number one: when specifying the includes in the options as "node_modules/rxjs/**" you have to be sure the full path resolves to where you expect it to.
Example:
I run my build command from "c:/foo/build" but my files are in "c:/my-source" then the include patterns might resolve to "c:/build/node_modules" which means when the commonjs plugin is checking if it should handle "node_modules/rxjs/" it will see that "c:/my-source/node_modules/rxjs/" does not match "c:/build/node_modules/rxjs/**" thus it will not convert the exports to ES6.
The second issue is case sensitivity. The include patterns are case sensitive. This tripped me up too.
Both of these issues can be confirmed by opening the "node_modules\rollup-plugin-commonjs\dist\rollup-plugin-commonjs.cjs.js" file and debugging the "transform" function.
If you modify the code (at the very beginning of the transform function) to be something like this
if (id.includes('rxjs\\Subject.js')) {
debugger;
}
and then run the code in a debugger, you can step through the code until it gets to the filter function. There you will see the filter function is skipping the "rxjs/Subject.js" file.
I almost guarantee this is the problem when the error occurs.
I have found that this can happen in combination with symlinks.