New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Loader can change the filename of a required file #9
Comments
can you please give little more detail on this? |
I've written a loader that turns every function myLoader() {
var filename = this.request.split("!")[1],
clientFilename = filename.replace(/\.server\.js$/i, ".client.js"),
amphibiousFilename;
if (fs.existsSync(clientFilename)) {
return fs.readFileSync(clientFilename, "utf8");
}
amphibiousFilename = filename.replace(/\.server\.js$/i, ".js");
if (fs.existsSync(amphibiousFilename)) {
return fs.readFileSync(amphibiousFilename, "utf8");
}
throw new Error("You're trying to include the server-only module '" +
filename + "' within '" + this.context + "'");
}
myLoader.loader = __filename;
myLoader.test = /\.server\.js$/i; But now the this.filename = clientFilename; which updates the Ok, another solution would be to generate a module that requires the client module but this seems like too much overhead since in this case "thousands" of forwarding ghost-server-modules would appear in the browser. |
Use the new resolve postprocessor feature: resolve: {
postprocess: {
normal: [
function(filename, cb) {
var file = filename.split("!").pop();
var clientFile = file.replace(/\.server\.js$/, ".client.js");
fs.exists(clientFile, function(client) {
if(client) return callback(null, filename.replace(/\.server\.js/, ".client.js");
var amphibiousFile = file.replace(/\.server\.js$/, ".js");
fs.exists(amphibiousFile, function(amphibious) {
if(amphibious) return callback(null, filename.replace(/\.server\.js/, ".js");
return callback(new Error("You're trying to include the server-only module '" + filename + "'"))
});
});
}
]
}
} This do a lot of things better than a loader.
|
Why are they called post processor? I'm just curious, but shouldn't they be called preprocessor because they're applied before the actual module is included? |
Anyhow, I like the new feature! :) |
It's named post processor because it's executed after the resolving. I added pre and post loaders, which are executed before and after the normal loaders. |
I'm a bit confused. Could you explain the purpose of pre- and postloaders? Can't loaders be piped? And why are pre and postloaders not part of the Additionally to that there are normal and context post-processors which are executed before the loaders. I'm concerned that the API will be way too complicated. I've expected to configure different loaders more like middlewares. My current stack for the framework for instance looks now something like: preLoaders: [
// compile .class files
{ test: /\.class\.js/i, loader: "compile" }
],
resolve: {
loaders: [
// load all html files with the raw loader by default
// (yes, someday we will minify the html here)
{ test: /\.html$/i, loader: "raw" },
// require all modules within the page folder as lazy bundle
{ test: /([\/\\])pages\1(\w+\1)*(\w+)\1\3Page\.class\.js$/i, loader: "bundle/lazy" }
],
postprocess: {
normal: [
// swap server modules to client modules if present
{ test: /\.server(\.class)?\.js$/i, loader: "lookForClientModule" }
]
}
} And I'd expect to do it like this: loaders: [
{ test: /\.server(\.class)?\.js$/i, loader: "lookForClientModule" },
{ test: /\.class\.js/i, loader: "compile" },
{ test: /\.html$/i, loader: "raw" },
{ test: /([\/\\])pages\1(\w+\1)*(\w+)\1\3Page\.class\.js$/i, loader: "bundle/lazy" }
] Notice how some loaders would be called accordingly their position in the loaders-array? For the file I really appreciate your willingness to help me to adjust webpack to my needs, but I'm afraid the API is getting too complicated. |
Maybe we should split it into PreProcessors |
Maybe I should add a flow chart... This are the steps for adding a
There are more stuff running for the chunking and some specific features, but this is not included here. |
That new API are totally optional and are intended for extensions to the compile system. They acts as middleware at 3 mayor points of the process. Loaders operate on the "content" of the module.
I. e. if you want to add the functionality of exclusion of files to webpack. // config:
preLoaders: [{test: /\.excluded\.js$/, loader: "excluded"}]
// loader:
module.exports = function(source) {
this.cacheable(); // flag for caching
this.clearDependencies(); // result do not depend on "source"
return "module.exports = undefined";
} I. e. if you want to inject a testing framework into the compiled code, which replaces the require method. // config:
postLoaders: [{test: /./, loader: "testingframework"}]
// loader:
module.exports = function(source) {
this.cacheable(); // flag for caching
return "require = require('testingframework')(require.valueOf());\n" + source;
} |
What do you mean by different view on files? |
The preLoaders can alter the file before the normal stuff sees it. So it offers a different view on the file. Examples for preLoaders:
preLoaders (and postLoaders) are some kind of extensions. |
Ok, I guessed something like that. But it seems quite strange that pre- and postLoaders are not part of the resolve options. I also don't get it why I can't configure loaders like middlewares. In your flowchart preLoaders are executed right before the loaders and postLoaders right after the loaders. Why don't remove pre- and postLoaders and say: "Ok, we have this stack of loaders that is executed in the order they are defined according as the regexp matches or not"? |
I think that configuring middlewares is very familiar to most node developers. And since webpack loaders can be piped they are actual middlewares. On the other hand configure objects that are too complex can be very confusing. |
pre and postLoaders are not part of the resolve options, because they are not part of the resolve process (see flowchart). The normal developer should not even touch any of the loader options in configuration. He should use loaders through the I've a bigger project using wbepack and this is my config: var config = {
input: path.join(__dirname, "lib", "client.js"),
output: path.join(__dirname, "public_compiled", prefix + "asserts", "[hash].js"),
publicPrefix: prefix + "asserts/",
includeFilenames: !live,
watch: !live,
watchDelay: 100,
debug: !live,
minimize: live,
filenames: !live,
resolve: {
alias: {
"nls": "xxxxxx/nls"
},
paths: [path.join(__dirname, "node_modules")]
},
// options for specific loaders
css: {
requireUrl: "url/auto!"
},
url: {
dataUrlLimit: 100000
},
i18n: {
locales: ["de"]
}
}; No need to put any loader into the config. I use some custom loaders and use code splitting often (maybe too often^^). It's still in work and I expect it to grow 3-4 times. Included libaries: bootstrap, jquery, three.js, CodeMirror, some jquery-plugins, and a few smaller libaries.
Takes 15s to compile produtive version. In watch mode 1,6s after each change. |
But in a context of a client-server-framework - that is build on webpack - I want to hide the webpack loaders for developers that are not familiar with bundling techniques. I want to provide a good default, that uses different loaders based on their file ending and their folder. For instance, every Page-Class is loaded via But if requested the developer may customize the loading behavior via the That's my goal. Trying to provide a good default that works for 95% of all cases but with the possibility to customize it. That's why I'm always fiddling around with the config instead of using the exclamation mark syntax. Because than I have to explain it to the developer that wants to use my framework. |
ok that are good reasons... should be possible with the existing api :) |
Yes, actually it is working. But I'm not 100% sure why ... that's why I'm kind of nervous :). I'll explain shortly my current setup: Basically I have a class compiler which evaluates the module code and adds some lines to the end of the module that makes it possible to write private, protected, public, static or even abstract properties. This compiler is applied if the file ends on So I would've expected the class compiler to be a postLoader since it enhances the existing JavaScript. But in this case the output results in one big chunk. So what I actually have to do is to add it as a preLoader, which works perfectly fine. But maybe that's already too implementation specific. |
It should work with both loader types. You loader may corrupt the module generated by the bundle loader (format). As nodeclass is valid javascript, enhanced by the compiler, I would think it fit postLoaders best. You should not filter the postLoader by regexp, but enhance only valid source. So it would be possible to generate nodeclass source by loader. |
I. e. If we follow the sheme that file format conversion should happen in (normal) loaders: preLoader: fileFormatA -> fileFormatA fileFormatA may be coffeescript, javascript, text, png, css, less, etc. |
So, how can I change the imported filename in 2019 not using an alias? 😅 |
This would be a nice feature for the debug view. :)
The text was updated successfully, but these errors were encountered: