neutrinojs / webpack-chain Goto Github PK
View Code? Open in Web Editor NEWA chaining API to generate and simplify the modification of Webpack configurations.
License: Mozilla Public License 2.0
A chaining API to generate and simplify the modification of Webpack configurations.
License: Mozilla Public License 2.0
Something like this might be helpful:
neutrino.config.module
.rule(options.ruleId)
.test(options.test)
.each(['style-loader', 'css-loader'], (val, rule) => {
rule.loader(require.resolve(val));
});
It would be great if examples in the documentation would include minimal webpack's configs or config snippets that correspond to the examples. Perhaps, that would help to better understand the API.
It would be great to be able to create modules like:
// Require the webpack-chain module. This module exports a single
// constructor function for creating a configuration API.
const config = require('webpack-chain/immutable');
// No need to instantiate, because config is already a root empty value.
// const config = new Config();
// Make configuration changes using the chain API.
// Every API call tracks a change to the stored configuration.
// Interact with entry points
const init = config
.entry('index')
.add('src/index.js')
.end()
// Modify output settings
.output
.path('dist')
.filename('[name].bundle.js');
// Create named rules which can be modified later
const eslint = init.module
.rule('lint')
.test(/\.js$/)
.pre()
.include('src')
// Even create named loaders for later modification
.loader('eslint', 'eslint-loader', {
rules: {
semi: 'off'
}
});
const babel = eslint.module
.rule('compile')
.test(/\.js$/)
.include('src', 'test')
.loader('babel', 'babel-loader', {
presets: [
[require.resolve('babel-preset-es2015'), { modules: false }]
]
});
// Create named plugins, too!
const plugins = babel
.plugin('clean')
.use(CleanPlugin, [BUILD], { root: CWD });
// Export the completed configuration object to be consumed by webpack
module.exports = babel.getConfig();
I'm considering using Neutrino (which uses this package) and I'm trying to add ExtractTextPlugin (https://github.com/webpack-contrib/extract-text-webpack-plugin) to my Neutrino project.
The extract-text-webpack-plugin docs use this example:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
So, I assumed this would be the equivalent for webpack-chain (this is a custom preset being called in my package.json):
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = ({ config }) => {
config.module
.rule("css")
.use(ExtractTextPlugin.extract, {
fallback: "style-loader",
use: "css-loader"
});
config.plugin("extract-css").use(ExtractTextPlugin, "styles.css");
};
But I get the following error:
config.module.rule(...).loader(...).use is not a function
Am I doing anything wrong here?
Hello! I'm working with a vue project. It has a default set of loaders for various file types.
For scss it uses oneOf
ruleset with resourceQuery
's, the last of them is what I believe a default rule with no resourceQuery
(correct me if I'm wrong here).
My task required to extract raw css as a string and the issue here is that the final default loader in vue is vue-style-loader
which does not allow this. I decided to use css-loader
for this.
So the question is: am I able to put my own oneOf
rule before the default one without having to rewrite defaults?
I'm aware I can override loaders in import
statement, but I may require to do this in other parts of the project so I thought it would be the best solution.
To make it easier to find the relevant pages.
Loaders on rules are order dependent, yet, looking at the source code for webpack-chain, it's being treated as a map (rule.uses). How would you inject a loader at the head of the loader chain? Right now, i have to clear
the uses but that feels like it defeats what webpack-chain is trying for.
//base.js
config.module
.rule('Javascript')
.test(/\.jsx?$/)
.enforce('pre')
.include
.add(path.resolve(__dirname, 'src'))
.end()
.use('babel')
.loader('babel-loader');
//development.js
config.module
.rule('Javascript').uses.clear().end()
.use('hot')
.loader('react-hot-loader/webpack').end()
.use('babel')
.loader('babel-loader');
Webpack supports a noParse key in the config. Is there a reason it's missing from the API?
How can i transform an existing webpack configuration object to webpack-chain format?
There is some tools that can do it ?
Interacting with the include
and exclude
APIs for Rule are not friendly, as they only allow adding paths to the Set. Any other operations require using the backing store at _include
and _exclude
. We should make these work similar to a ChainedSet
, but this will mean another breaking change.
This is a Feature Request.
I wonder if you're interested in implementing the eject webpack config feature (similar to what create-react-app
does):
config.entry('client').add('src/index.js')
config.plugin('define')
.use(webpack.DefinePlugin, [{foo: 'foo'}])
.outputName('webpack.DefinePlugin')
config.toString() // or maybe config.toConfig({ string: true })
//=> '{"entry":{"client":["src/index.js"]},"plugins":[new webpack.DefinePlugin({\"foo\": \"foo\"})]}'
test()
method defines expressions tested over files . In webpack-chain
this method can be only overridden but can't be extended if its value is an array.
What if we add an alternative method that will be dedicated exactly to extensions and will support ChainedSet interface? Example
config.module
.rule('scripts')
.extensions
.add('js')
.add('jsx')
.merge([...])
.end()
.use('babel')
.loader('babel-loader');
This collection can be converted to RegExp just before it is formatted for Webpack compilation.
Pros:
I would even suggest to completely replace test()
method as it will simplify the implementation.
In my vue.config.js
file, I have the following:
..
chainWebpack: config => {
config
.plugin('clean')
.use('clean-webpack-plugin', [
'css',
'fonts',
'images',
'img',
'js',
], { beforeEmit: true, root: path.join(__dirname, 'public') });
}
..
However, the additional arguments after the array of paths is not being picked up by webpackChain, you can see that from this inspect:
09:51 $ vue inspect --plugin clean
/* config.plugin('clean') */
new (require('clean-webpack-plugin'))(
'css',
'fonts',
'images',
'img',
'js'
)
Is there something I'm missing so that the additional args get picked up?
Plugin args
in config.plugin('plugin', args)
must always be an array, otherwise cryptic error
cannot call undefined as a function
is produced in init
method here https://github.com/neutrinojs/webpack-chain/blob/master/src/Plugin.js#L14
Consider add argument validation isArray
for plugins args
.
webpack-chain
is awesome.
However, it's hard to manage the order of ChainedMap
, and order is matter in webpack
, e.g., entry/plugins/module.rules.
Is it possible to provide .before()
.after()
API for every ChainedMap
, same as config.plugins
.
I suggest to provide the following API:
// same as config.plugin(name).after(otherName)
.before()
.after()
// or maybe
.append()
.prepend()
// sometime will be better then .get(key)
.first()
.last()
.key()
// then we can do this:
const key = config.entryPoints.first().key();
config.entry('abc').before(key).add('abc.js')
webpack-serve is a webpack-dev-server
alternative that targets modern browsers.
The configuration is slightly different, as it delegates most options to webpack-dev-middleware and webpack-hot-client
through the dev
and hot
options respectively.
It expects options to be provided through the serve
key in the Webpack config, but right now webpack-chain
does not let that option go through, which would be convenient even if you decide not to create shorthands for the different parts of it.
..and possibility other new conditions on module rules. See https://webpack.js.org/configuration/module/#rule-resourcequery
The documentation says there is config.getConfig()
method to convert the config object to webpack specific configuration, but there is no such method. It should to be changed to .toConfig()
ERROR in ./src/index.ts 33:20
Module parse failed: Unexpected token (33:20)
You may need an appropriate loader to handle this file type.
| apiKey: config.EngineApiKey,
| },
> formatError: (error: any) => {
| console.log(error);
| return error;
@ multi ./src/index.ts index[0]
myconfig
const Config = require('webpack-chain');
const path = require('path');
const config = new Config();
config
.entry('index')
.add(path.resolve(__dirname, '../src/index.ts'))
.end()
.output.path(path.resolve(__dirname, '../dist'))
.filename('[name].bundle.js');
config.mode('development');
// tslint-loader
config.module
.rule('lint')
.test(/\.ts$/)
.pre()
.include.add('../src')
.end()
.use('tslint')
.loader('tslint-loader')
.options({
rules: {
fix: true,
configFile: '../tslint.json',
},
});
config.module
.rule('ts')
.test(/\.ts$/)
.include.add('src')
.end()
.use('ts-loader')
.loader('ts-loader');
config.resolve.extensions
.add('.ts')
.add('.js')
.clear();
module.exports = config.toConfig();
I don't know what's wrong with me. It seems that he doesn't carry out ts-loader.
my package.json
"devDependencies": {
"@2fd/graphdoc": "^2.4.0",
"@types/bcryptjs": "^2.4.1",
"@types/bunyan": "^1.8.4",
"@types/graphql": "^14.0.0",
"@types/ioredis": "^4.0.1",
"@types/jsonwebtoken": "^7.2.8",
"@types/koa": "^2.0.46",
"@types/koa-router": "^7.0.31",
"@types/lodash": "^4.14.116",
"@types/node": "^10.10.1",
"@types/nodemailer": "^4.6.5",
"@types/nodemailer-smtp-transport": "^2.7.4",
"@types/pg": "^7.4.10",
"@types/uuid": "^3.4.4",
"@types/validator": "^9.4.2",
"cross-env": "^5.2.0",
"nodemon": "^1.18.4",
"pre-commit": "^1.2.2",
"prettier": "^1.14.2",
"tslint": "^5.11.0",
"tslint-loader": "^3.6.0",
"typescript": "^3.0.3",
"webpack": "^4.19.1",
"webpack-chain": "^4.11.0",
"webpack-cli": "^3.1.0"
},
"dependencies": {
"apollo-server-koa": "^2.0.7",
"bcryptjs": "^2.4.3",
"bunyan": "^1.8.12",
"dataloader": "^1.4.0",
"graphql": "^14.0.2",
"graphql-import": "^0.7.1",
"ioredis": "^4.0.0",
"jsonwebtoken": "^8.3.0",
"koa": "^2.5.3",
"koa-bunyan-logger": "^2.0.0",
"koa-router": "^7.4.0",
"lodash": "^4.17.11",
"moment": "^2.22.2",
"nodemailer": "^4.6.8",
"nodemailer-smtp-transport": "^2.7.4",
"on-finished": "^2.3.0",
"pg": "^7.4.3",
"ts-loader": "^5.1.1",
"tslib": "^1.9.3",
"uuid": "^3.3.2",
"validator": "^10.7.1"
}
Hi,
I try to integrate webpack-chain in my project but it seems I can't do something.
Let's say I have a TypeScript loader :
chain.module.rule('ts')
.test(/\.tsx?$/)
.exclude.add(/(node_modules|bower_components)/).end()
.use("ts").loader("ts-loader").options(typeScriptConfigurator())
The thing is, now I wish to use React Hot Loader which requires me to add a loader before the "ts-loader" (Because it has to be executed after ts-loader)
Is there some way to do something like ?
chain.module.rule('ts').prependUse("hot").loader("react-hot-loader/webpack")
Thanks in advance.
config .entry('index') .add({ pageOne: './src/pageOne/index.js', pageTwo: './src/pageTwo/index.js', pageThree: './src/pageThree/index.js' }) .end()
This is error
I must be missing something, but how do you remove a rule from existing config?
First off, loving webpack-chain. Declaratively setting up a webpack config is fantastic.
I'm struggling to switch over ExtractTextPlugin. The pattern I'm looking to convert is well described in the Webpack Migration Guide, specifically the module part:
https://webpack.js.org/guides/migrating/#extracttextplugin-extract
Would you happen to have any suggestions? I'm posting this here in hopes that other people might find this useful.
According to the webpack resolve docs, you can use alias under resolveLoader
and other features familiar from resolve.
I'm using it through a new Vue CLI 3 project, and trying to do the following:
config.resolveLoader.alias.set('text', 'raw-loader')
, however config.resolveLoader.alias
doesn't exist and isn't in the webpack-chain
docs.
With webpack-chain
is this impossible?
I tried out the api and tried to create a minimal configuration with only entrypoint ,outputPath and outputFilename.
I get output something like
{"output":{"path":"dist","filename":"bundle.js"},"resolve":{"modules":[],"extensions":[]},"resolveLoader":{"modules":[]},"plugins":[],"entry":{"index":['index.js']}}
Wouldn't it be better to prune out the empty configs of resolve
, resolveLoader
and plugins
?
Option devServer.color
is CLI only and produces webpack-dev-server cryptic error
options should NOT have additional properties
see: https://webpack.js.org/configuration/dev-server/#devserver-color-cli-only
Why keep invalid CLI-only options in options anyway?
example:
config.optimization.minimizer.plugin(name).use(WebpackPlugin, args)
i wanted to add webpack-manifest-plugin and was only able to do so via trial and error by adding an empty array as the second arg.
What does this arg do? How can I help to improve the docs?
neutrino.config.plugin('manifest')
.use(ManifestPlugin, [], { filename: 'manifest.json' });
I placed this repo here because it seemed generic enough, but maybe it should be a part of the Neutrino org just for clarity.
See:
#95 (comment)
Merge of plugins under a config
map has special logic to merge each plugin entry. The same logic should be provided when merging a resolve
map, as these can also have plugins.
I am trying to merge the following plugin in a fork of create-react-app
/ react-scripts
that uses neutrino:
resolve: {
plugin: {
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
"ModuleScope": {
plugin: ModuleScopePlugin,
args: [paths.appSrc, [paths.appPackageJson]]
}
},
I attach pull request #82 to resolve this issue
Instead of manually creating all of the methods for webpack-chain, it would be more maintainable to take the Webpack schema and generate the methods from it.
https://github.com/webpack/webpack/blob/master/schemas/webpackOptionsSchema.json
In neutrinojs/neutrino#1150 there was confusion about the API of rule.exclude.
We should probably add clearer documentation about it to the README here :-)
(Edited by @edmorley to make it clearer that this is a documentation issue not a bug)
When creating a loader through the Loader API, the signature is:
.loader(name, loader, options)
When modifying a loader it is:
// props: Object: { loader, options }
.loader(name, props => props)
Plugins are different. To instantiate:
.plugin(name).use(plugin, ...args)
To modify requires instantiating the plugin manually through a handler:
.plugin(name).inject((Plugin, args) => new Plugin(...args))
Plugin should hypothetically operate like Loader:
// create
.plugin(name, plugin, ...args)
// modify
.plugin(name, props => props)
Concretely, this could be:
config.plugin('env', EnvironmentPlugin, ['NODE_ENV'])
// ...
config.plugin('env', props => merge(props, { args: [...props.args, 'OTHER_ENV_VAR'] }))
It would be nice to see documentation that provides example .babelrc
, .eslintrc
, and webpack.config.js
configurations and illustrates how they would be replicated using webpack-chain
.
Note that I'm using webpack-chain
via neutrino
, so I'd also like to make sure I don't blow away settings that were already provided via neutrino presets.
"module": {
"rule": {
"css": {
"loader": {
"css": {
"options": {
"modules": true
}
}
}
}
}
}
}
This should extend the rule.css.loader.css
if one exists, but currently it overwrites.
This plugin has two interconnected config entries. A standard plugin, and a special module loader:
{
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
}),
},
],
},
}
Behind the scenes, this extract
function returns an array of loaders, specially formatted like so:
use: [
{ loader: 'extract-text-webpack-plugin/loader', options: { omit: 1, remove: true } },
{ loader: 'style-loader' },
{ loader: 'css-loader' },
]
Not sure why they don't document this api for loader syntax consistency. In any case, ExtractTextPlugin.extract
doesn't work well with how webpack-chain
currently abstracts loaders in Rule
and ChainedMap
.
I'm not convinced that webpack-chain
's design should accommodate this pattern. Instead I'd suggest testing and documenting how to make it work with .merge()
like so:
config.module.rule('style')
.test(/\.css$/)
.merge({
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader',
}),
})
I haven't had time to verify this but I imagine this should work, even though the uses get their names from array indices: '0', '1', '2'.
Does this make sense. Where could this be documented?
See:
#28 (comment)
config.plugin('then')
.use({
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
console.log('worked!')
})
}
})
the error
TypeError: Cannot read property '__expression' of undefined
but webpack4 support to create anonymous plugin,and in webpack plugins config array,it works fine,how to run in chain?
STR:
yarn add [email protected] @neutrinojs/[email protected]
mkdir src
touch contribute.json src/index.js
.neutrinorc.js
containing:module.exports = {
use: [
'@neutrinojs/web',
(neutrino) => {
neutrino.config
.plugin('copy')
.tap(args => {
args[0] = [
'contribute.json'
];
return args;
});
}
]
}
yarn start
Expected:
Error message that makes the mistake clear. eg:
Error: Plugin 'copy' is not defined
(along with a stack trace that points to the correct line in .neutrinorc.js
)
Actual:
webpack-chain gives an error message that is hard to track back to a root cause (unclear error message and does not reference a specific line in .neutrinorc.js
):
C:\Users\Ed\src\neutrino-test\node_modules\webpack-chain\src\Plugin.js:9
this.init((Plugin, args = []) => new Plugin(...args));
^
TypeError: Plugin is not a constructor
at init (C:\Users\Ed\src\neutrino-test\node_modules\webpack-chain\src\Plugin.js:9:38)
at Object.toConfig (C:\Users\Ed\src\neutrino-test\node_modules\webpack-chain\src\Plugin.js:38:12)
at clean.Object.assign.plugins.plugins.values.map.plugin (C:\Users\Ed\src\neutrino-test\node_modules\webpack-chain\src\Config.js:69:59)
at Array.map (<anonymous>)
at module.exports.toConfig (C:\Users\Ed\src\neutrino-test\node_modules\webpack-chain\src\Config.js:69:38)
at ChainAction.Future.chain.chain.chain [as mapper] (C:\Users\Ed\src\neutrino-test\node_modules\neutrino\src\api.js:281:61)
at ChainAction$resolved [as resolved] (C:\Users\Ed\src\neutrino-test\node_modules\fluture\index.js:805:27)
at resolved (C:\Users\Ed\src\neutrino-test\node_modules\fluture\index.js:220:19)
at Immediate.EncaseP2$res [as _onImmediate] (C:\Users\Ed\src\neutrino-test\node_modules\fluture\index.js:1847:7)
at runCallback (timers.js:791:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
The root cause here is that the copy
plugin is only defined when command === 'build'
, and so there is no such plugin to .tap()
during yarn start.
I'm using this with Visual Studio Code and it would be great to explore the available API operations via the autocomplete of the editor. If there's a .d.ts file in the npm package, most editors should be able to enable this.
Hello everyone!
I really love how easy it is to manipulate Webpack configs with webpack-chain
. This is possible thanks to ChainedMap and ChainedSet. I wanted to apply the same primitives to other configs such as a PostCSS preset and Jest. Though, it felt kind of strange to use webpack-chain/lib/ChainedMap
and webpack-chain/lib/ChainedSet
under the hood.
At MOXY, we have created chained-config which is just that:
chained-config offers those constructors in a separate package so that we can apply the same concepts to other configurations other than Webpack.
I'm writing this because I'm obliged by the license to inform you of any changes that I make in your software. But that was not the primary goal. Down the road, we plan to create jest-chain
, babel-chain
and friends with chained-config
. It would be great if webpack-chain
could use the same base package. The reason is that chained-config
has a some differences that are noted on each API section.
I'm writing this so that the creators of the package may evaluate chained-config
and possibly decide if it may be used as the foundation for webpack-chain
. I know that this will translate into breaking changes, therefore it must be done at the right time.
Feel free to close this issue if this is something that you do not want to do.
Thanks for this awesome package!
There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.
Error type: Preset name not found within published preset config (monorepo:angularmaterial). Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.
I try to test webpack-chain with Webpack 4 one error is not valid because of Webpack 4 is no need config anymore. So not passing any config Webpack is still work. Can we remove this test?
test('validate empty', t => {
const config = new Config();
const errors = validateSchema(config.toConfig());
t.is(errors.length, 1);
});
Is i have to learn webpack before use this ? I don't know what i should start with chain.
When to add, when to test, when to use, when to loader ?
Document should provide simple tutorial like webpack to make beginer approch easier
This example doesn't work.
This part of code
chainWebpack: config => {
config.module
.rule('vue')
.use("vue-loader")
.tap(options => merge(options, { transformAssetUrls:{ 'parallax': 'src',} }))
}
fail with ReferenceError: merge is not defined
error. Should i import it from somewhere?
Here https://github.com/mozilla-neutrino/webpack-chain#config-module-rules-uses-loaders-modifying-options the example says:
config.module
.rule('compile')
.use('babel')
.tap(options => merge(options, { plugins: ['babel-plugin-syntax-object-rest-spread'] }));
However it's not mentioned anywhere where merge
is supposed to come from. I believe that should be documented at least once.
if I want to create or copy or delete some file to output dir, or do anything after dev-server started or webpack builded,how i can do?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.