Closed geelen closed 7 years ago
Hi @geelen
First thanks for your interest in this project - I've been seeking some better use cases and babel-plugin-styled-components-named would be another good one.
I do think what your saying will be possible. To date I've been waiting on CRA to stabilize to see if this idea was feasible.
Last week I updated CRA from 0.2.1 to 0.7.0 no issues... so unless something major changes I think we're good on that.
I will invest some time next week to see about setting this up properly via an npm install to make things as automatic as possible.
The current process:
No it's not another boilerplate, it's just not very well explained and very manual at this point.
Given a new app created via CRA.
The scripts section from the CRA app package.json
need to repoint to the scripts folder you copied in.
"scripts": {
"default": "node node_modules/react-scripts/scripts/build.js",
"start": "node scripts/start",
"build": "node scripts/build"
},
You also need to install 2 existing npm packages
You'll need 2 existing npm packages
npm install rewire --save-dev npm install rimraf --save-dev (I think I'll be able to remove this dep)
config-overrides.js
file. Update this file as need be.I'll see what I can do for a more automatic setup and we'll go from there.
Hmm if that's the case I think this should be really straightforward. I might have some time later this week to have a crack at it, but if you get there first then 👍
Btw I think what you want is basically the same as any "command line" NPM project. I've never released one, but I found an article on the NPM blog that makes it sound pretty easy :)
This would also be relevant to CSS Modules, too, since there are a lot of people bugging CRA to include support for that. All it requires is adding ?modules=true
to the css-loader
but CRA doesn't really expose any way for that to be done.
Cool - I've got actually got a good start on this today. The way I'm setting it up should be very flexible i.e. we can add loaders etc..
I'll keep rolling and let you know once I have something to work with that you can install via NPM.
@geelen
I'm doing some testing.
I haven't worked with CSS modules yet ...
Given the existing loader lines from the Dev and Pro Webpack configs how would they need to end up like
CRA Dev
{
test: /\.css$/,
loader: 'style!css?importLoaders=1!postcss'
},
CRA Prod
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&-autoprefixer!postcss')
},
Like this?
loader: 'style!css?importLoaders=1&modules=true!postcss'
loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&modules=true&-autoprefixer!postcss')
I believe so yep! On Sun., 13 Nov. 2016 at 11:58 am Tim Arney notifications@github.com wrote:
@geelen https://github.com/geelen
I'm doing some testing.
I haven't worked with CSS modules yet ...
Given the existing loader lines from the Dev and Pro Webpack configs how would they need to end up like
CRA Dev
{ test: /.css$/, loader: 'style!css?importLoaders=1!postcss' },
CRA Prod
{ test: /.css$/, loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&-autoprefixer!postcss') },
Like this?
loader: 'style!css?importLoaders=1&modules=true!postcss'
loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&modules=true&-autoprefixer!postcss')
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/timarney/react-app-rewired/issues/1#issuecomment-260159258, or mute the thread https://github.com/notifications/unsubscribe-auth/AABa4JPkABMg9VQ8SWJm3WNyXvAgBk_Xks5q9mCrgaJpZM4KwTWV .
@geelen If you want to toy around with this at some point you can now run
create-react-app your-app-name --scripts-version rewire-react-scripts
That will create a fresh CRA project with a config-overrides.js
file in it. Like the existing repo but via npm install.
With that you can mess with the configs from CRA.
That's a start.
The babel-plugin-styled-components-named plugin do you know if that can loaded via webpack or only via a .babelrc file?
FYI - just pushed 0.0.6 the sample config-overrides.js file works for CSS Modules
create-react-app your-app-name --scripts-version rewire-react-scripts
Modifiy App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import styles from './App.css';
class App extends Component {
render() {
return (
<div className={styles.App}>
<div className={styles['App-header']}>
<img src={logo} className={styles['App-logo']} alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className={styles['App-intro']}>
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
@geelen can you confirm with that plugin installed -> babel-plugin-styled-components-named
The result in Dev Tools would be:
If yes than this thing works.
config-overrides.js
module.exports = function override (config,env) {
config.module.loaders[0].query.plugins = ['styled-components-named'];
return config;
}
Yep! If you wrote const Title = styled.h1
then that should be the output! 😄
So if you've got an existing CRA project what are the instructions to switch it to this one?
It's just a matter of updating the devDependencies
CRA
"devDependencies": {
"react-scripts": "0.7.0"
},
Rewired
"devDependencies": {
"rewire-react-scripts": "0.0.6"
},
Outside of that for existing apps at this point you would need to create a config-overrides.js.
I should be able to setup a little script to take care of that part.
Hmm, personally I think it'd be better if it ran alongside react-scripts
rather than replaced it. That feels like a less invasive change, and would mean that rewire-react-scripts
could be much smaller, containing just the scripts needed rather than a fork of CRA itself.
Would that work?
Ya it's not ideal especially given all I'm doing is wrapping webpack kickoff scripts with a function (init) so I can get a hook - https://github.com/timarney/create-react-app/commit/adf82c34aff39c3c9227fe0dfbe2cb964708c194#diff-8d717038a105d8c7993616152faa1a38
My original 'hack' didn't use a fork but given there's really no hooks the kickoff wasn't clean... The original used rewire to gain access to the configs etc... but given the setup of those scripts i.e. for the build script 'recursive' ran when it was included... than I would just run it again with the rewired version.
Forking the entire repo to make those alterations seems to be best practice at least so far https://github.com/facebookincubator/create-react-app/issues/682 CRA is setup to look for a fork as a replacement so it's fairly seamless / built in create-react-app my-app --scripts-version my-react-scripts-fork
I'm looking to avoid duplicating / maintaining those entire files (build and start) into a new package to keep the maintenance down.
Given the minimal amount of changes needed to get those hooks working (a few lines really) a fork seemed like the best option to me. The part that gets published to npm is just the react scripts package - I don't plan on altering anything else in CRA but I do want to keep it in sync with upstream to ensure the webpack / build scripts remain up to date.
Do you see any paths without duplicating / maintaining those entire files?
Hahah ok I see the problem. This just took me a while, and I really didn't think it was going to work, but it does!
/* start.js */
var fs = require('fs');
var path = require('path');
var config = require('react-scripts/config/webpack.config.dev')
var override = require(path.resolve(fs.realpathSync(process.cwd()) + '/config-overrides'));
require.cache[require.resolve('react-scripts/config/webpack.config.dev')].exports =
override(config, process.env.NODE_ENV || 'development');
require('react-scripts/scripts/start')
You load the config, then run your overrides on it, then patch the require.cache
so that when you start react-scripts
and it loads the config it gets your patched config.
Neat, hey! I had a read through proxyquire which I've used in testing before and it seems like this is a legit butchery of require.cache
. But maybe there's something I'm missing...
For the record, I also did a version with two files in case patching require.cache[...].export
is bad for some reason:
/* start.js */
var config = require('./dev');
require.cache[require.resolve('react-scripts/config/webpack.config.dev')] =
require.cache[require.resolve('./dev')];
require('react-scripts/scripts/start')
/* dev.js */
var fs = require('fs');
var path = require('path');
var config = require('react-scripts/config/webpack.config.dev')
var override = require(path.resolve(fs.realpathSync(process.cwd()) + '/config-overrides'));
module.exports = override(config, process.env.NODE_ENV);
This also works, but I prefer the single-file approach.
Oh also your config-overrides
template example is invalid, I had to change it to this:
/* config-overrides.js */
module.exports = function override(config, env) {
var loader = '';
if (env === 'development') {
loader = 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss';
} else {
loader = 'style!css?modules&-autoprefixer&importLoaders=1!postcss';
}
// Find the right loader then patch its 'loader' property
config.module.loaders.forEach(l => {
if (String(l.test) == String(/\.css$/)) l.loader = loader
})
// default override
return config;
}
What do you think?
Nice that looks ideal - if you like I can add you as a collaborator on this repo get a new branch kicked off with that code.
I'll checkout that proxyquire repo.
Brilliant just gave it a go - seems to work flawless. Super clean.
Here's an init script I had started on to write entries to the package.json file + copy over the config-ovverides.js file
var fs = require('fs-extra');
var path = require('path');
var pathExists = require('path-exists');
var appPath = fs.realpathSync(process.cwd())
var appPackage = require(path.join(appPath, 'package.json'));
// Setup the script rules
if(!appPackage.scripts) return;
appPackage.scripts['rewire-start'] = 'react-app-rewired/start';
appPackage.scripts['rewire-build'] = 'react-app-rewired/build';
fs.writeFileSync(
path.join(appPath, 'package.json'),
JSON.stringify(appPackage, null, 2)
);
var overrideExists = pathExists.sync(path.join(appPath, 'config-overrides.js'));
if (!overrideExists) {
// Copy the files for the user
fs.copySync(path.resolve(__dirname, 'config-overrides.js'), 'config-overrides.js');
}
Think that will work or better idea?
Also
How's this looks for the build script?
/* build.js */
process.env.NODE_ENV = 'production';
var fs = require('fs');
var path = require('path');
var config = require('react-scripts/config/webpack.config.prod')
var override = require(path.resolve(fs.realpathSync(process.cwd()) + '/config-overrides'));
require.cache[require.resolve('react-scripts/config/webpack.config.prod')].exports =
override(config, process.env.NODE_ENV);
require('react-scripts/scripts/build')
I don't think you need to do it, personally. I think you just have manual instructions to npm install react-app-rewired
, edit package.json
and create config-overrides.js
in your local directory. If the example file is quite small and readable it's useful for people to see it and get a sense of what it can do. Something like this maybe?
export default (config, env, { changeLoader, addBabelTransform }) => {
let cssLoader
if (env === 'development') {
addBabelTransform('babel-plugin-styled-components-named')
cssLoader = 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss'
} else {
cssLoader = 'style!css?modules&-autoprefixer&importLoaders=1!postcss'
}
changeLoader({
test: /^.css$/
loader: cssLoader
})
}
Or how about:
export const dev = ({ changeLoader, addBabelTransform }) => {
addBabelTransform('babel-plugin-styled-components-named')
changeLoader({
test: /^.css$/
loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss'
})
}
export const production = ({ changeLoader }) => {
changeLoader({
test: /^.css$/
loader: 'style!css?modules&-autoprefixer&importLoaders=1!postcss'
})
}
Feels like an ok way to pass in helpers...
Probably worth raising another issue for this though, it might be best to not reinvent the wheel and build off webpack-config instead.
Okay pushed up a new branch if you can take a peak (just the start and build files for now) - https://github.com/timarney/react-app-rewired/tree/usecache
Ya I like the idea of building off webpack-config can flesh that out further in another issue.
Sounds good on the setup stuff it's pretty basic now. Given some good sample config-overrides I think people will get it (CSS Modules maybe Sass and a few others).
👍 looking good. I'll start raising issues for stuff as I think of it, I'll have some time later this week to dive in a bit deeper than the issue tracker :)
Cool sounds good - really happy with this solution. It's ultimately the vision I had for this repo. No eject and no fork. Clean and simple.
Feel free to mod things as you see fit. I'll do what I can next week as well to push things forward.
@geelen closing this issue as I think I have all discussed back into master ... as you mentioned we can open a new issue for looking at webpack-config.
I really like the idea behind this project, but it still kinda feels like a boilerplate — you have to decide to use
react-app-rewired
instead ofcreate-react-app
at the beginning, right?Would it be possible to wrap this up in a different way, so that you could take a perfectly normal CRA app and then, instead of ejecting, do something like this:
Then suddenly your magic
config-overrides.js
file gets picked up and you're all good. It feels like this API should be totally possible with the work you've already done, but let me know if I'm missing something.I really want something like this to exist for people to be able to use babel-plugin-styled-components-named without ejecting. Reckon that's possible?