Skip to content
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

Provide a watching build mode that writes to the disk in development #1070

Open
andreypopp opened this issue Nov 20, 2016 · 103 comments
Open

Provide a watching build mode that writes to the disk in development #1070

andreypopp opened this issue Nov 20, 2016 · 103 comments
Milestone

Comments

@andreypopp
Copy link
Contributor

@andreypopp andreypopp commented Nov 20, 2016

See #1018 (comment) for a discussion which leads to the creation of this issue.

I propose adding a new command (working name build-dev) which watches for changes and rebuild the bundle (effectively, given the current CRA implementation, it should execute webpack --watch), then writes it onto filesystem.

The main motivation is an easier integration path with non-Node.js dev servers: such servers could just serve continuously updating bundle from the filesystem.

The only desired configuration options is an --output, which could probably be specified via a command line argument:

% npm run build-dev -- --output ../static/www

Alternative command name suggestions:

  • watch
  • build-watch
@gaearon
Copy link
Member

@gaearon gaearon commented Nov 20, 2016

My concern: the concept of a dev server is already confusing to many people and I've seen countless issues from people trying to find dev server output in the filesystem, adding scripts with hrefs to local files, when they didn't need it.

So I'm worried introducing this feature is useful in some advanced cases but might make everything more confusing in simple cases for people who mistakingly start with build-dev instead because they haven't seen a dev server before when dev server is exactly what they need.

@leftdevel
Copy link

@leftdevel leftdevel commented Nov 21, 2016

(As the OP of #1018) :
For me it all boils down to:

  • Write the dev bundle that results from running npm start, to the file system.
  • 'watch' for changes (in dev mode).

@gaearon makes a great point saying that CRA biggest win is saving the developer from configuring the dev server which is the most difficult feature. I'd be happy if at least we could access the dev bundle.js file from the filesystem, I wouldn't care if the dev server is started, I would just ignore it.

My argument is that:

CRA is quite appealing to non Node.js developers. There are many experienced developers in python, php, .net out there, but not all of us/them are that experienced in frontend set ups.

CRA makes it a no-brainer to use react. And to be honest, these days it's way more difficult to set up a React project than an ember or angularjs 1.x project. React API is pretty simple and powerful, but when you introduce React to your organization projects, it's not just React, but all its friends which are hard to master (webpack, babel, presets, linters, etc).

Not sure if you guys have read this blog post yet https://goo.gl/HviDRn, but it's about a company that has a backend in Flask and were evaluating a frontend framework. They talk about how difficult is to keep up with the shifting ecosystem around React, and I'd totally agree on their pain points. At the end they chose Emberjs because of their CLI tools that makes it super easy to integrate.

I think CRA was created with the mindset of helping people that are just starting with React, and playing around with a few simple tutorials, but it has the potential of becoming a profesional tool for people that don't have that much time to spend learning webpack, babel, if to use fetch or superagent, what Promise polyfill to use, etc.

We need something opinionated that just works. Note that I'm totally fine with the already existing features in CRA.

Sorry for the long post.
** flies away **

@gaearon
Copy link
Member

@gaearon gaearon commented Dec 6, 2016

Tagging with a milestone so I remember to get back to it.

@viankakrisna
Copy link
Contributor

@viankakrisna viankakrisna commented Jan 12, 2017

I think this is the same as #1316 should i just close that?

@gaearon
Copy link
Member

@gaearon gaearon commented Jan 12, 2017

Yea, this is the same.

@gaearon gaearon changed the title Provide a new command which watches for changes and rebuilds bundle Provide a watching build mode that writes to the disk Jan 12, 2017
@gaearon gaearon changed the title Provide a watching build mode that writes to the disk Provide a watching build mode that writes to the disk in development Jan 12, 2017
@viankakrisna
Copy link
Contributor

@viankakrisna viankakrisna commented Feb 22, 2017

So I'm worried introducing this feature is useful in some advanced cases but might make everything more confusing in simple cases for people who mistakingly start with build-dev instead because they haven't seen a dev server before when dev server is exactly what they need.

Isn't this can be solved with proper documentation and gentle reminder when they use this feature? Would be happy to working on a react-scripts watch PR. I have 'ejected' several project because i need this feature.

@gaearon
Copy link
Member

@gaearon gaearon commented Feb 22, 2017

I'm not opposed to adding it. Happy to take a look at a PR.

@aeneasr
Copy link

@aeneasr aeneasr commented Apr 17, 2017

I'm currently using cross-env NODE_ENV=development webpack . --config node_modules/react-scripts/config/webpack.config.dev.js -w to achieve that. However, it requires adding webpack-cli to the node_modules.

The only problem is that appending the hash to the bundle's name bundle.[hash].js makes the entrypoint unpredictable which is why I'm still ejecting the scripts. Any way to resolve that?

@dkaplan-pivotal
Copy link

@dkaplan-pivotal dkaplan-pivotal commented Apr 21, 2017

I want to add an additional story to add context to why I'd like this feature: We built our app with server side rendering and waited until we had a feature that would require enough js/ajax to justify a single page app. We got to that point and wanted to replace that one page with a react frontend. The plan would be that react would gradually expand to the rest of the app (if needed, maybe it'd never be needed).

But CRA appears to assume that in development mode, your frontend is a different app from your backend. That's not necessarily true if you want to bring react to an application that already exists. (Even if you go with the dual-app idea, you have to deal with CORS. I think you could argue that CORS complexity outweighs the output file complexity.)

So our workaround is to always build to production and copy the files into our server so they can be rendered. But the minification and source map generation adds an extra 10-15 seconds when we do that every time and we can't use a watch. When you want frequent, fast feedback loops, those 10-15 seconds add up to considerable lost time.

Let me know if I'm doing something wrong here. Maybe there's an obvious workaround here.

@viankakrisna
Copy link
Contributor

@viankakrisna viankakrisna commented Apr 21, 2017

@dkaplan-pivotal I have the exact use case! There is a proof of concept here #1616 but i think #1994 & #1588 is a better solution (minimized risk of deploying development script + reload on file change). Good to see that #1994 & #1588 is tagged for 0.9.6.

@dkaplan-pivotal
Copy link

@dkaplan-pivotal dkaplan-pivotal commented Apr 21, 2017

Thanks for your reply. @viankakrisna I don't see how #1994 & #1588 add up to a development mode where you can have a single page of your app use react.

@viankakrisna
Copy link
Contributor

@viankakrisna viankakrisna commented Apr 21, 2017

#1994 and #1588 will allow you to include the development bundle to any of your local sites with live reloading. I've tested it by referencing the bundle on my local wordpress site and it reloads on file change. (haven't tested css hot reload because i'm using styled-components)

@ltoshea
Copy link

@ltoshea ltoshea commented Apr 25, 2017

I'd just like to echo all the above, when loading resources from an external file it's a huge pain for the names to change every time you make a small change and rebuild.

@koyadovic
Copy link

@koyadovic koyadovic commented Apr 12, 2020

A workaround, if using Bash, is to execute something like this:

yarn build && inotifywait -m -r -e modify -e attrib -e move -e create -e delete public src |
while read events; do
  pkill -9 sleep
  pkill -9 yarn
  sleep 2 && yarn build &
done

This waits for changes into public or src directories. If so, kills a possible previous execution of yarn and launches yarn build in the background again.

@Friss
Copy link
Contributor

@Friss Friss commented May 11, 2020

Now that you can specify the port and host for the dev tools via ENV vars I was able to get a pretty nice setup for developing a CRA app and Express backend.

I have this route handler that basically checks if a requested file can be served from the dev server. It uses fetch from node-fetch.

const fetch = require('node-fetch');
if (process.env.NODE_ENV !== 'production') {
  app.use(async (req, res, next) => {
    try {
      console.log('attempting', `http://localhost:3000${req.path}`);

      const response = await fetch(`http://localhost:3000${req.path}`);
      const headers = response.headers;
      const body = await response.arrayBuffer();

     // If no headers that means we didn't get a response worth sending back move on to next route.
      if (!headers) {
        return next();
      }

      if (headers.get('content-type')) {
        res.setHeader('content-type', headers.get('content-type'));
      }

      if (headers.get('content-length')) {
        res.setHeader('content-length', headers.get('content-length'));
      }

      res.send(new Buffer.from(body));
    } catch (e) {
      console.error(e);
      next();
    }
  });
}

And a basic template engine

app.engine('template', async (filePath, options, callback) => {
  let template = app.locals.appTemplate;

  if (isTest) {
    return callback(null, '');
  }

  if (!isProd) {
    try {
      template = await fetch('http://localhost:3000').then((res) => res.text());
    } catch (e) {}
  }

 // Handle how you want to serve the build HTML in production. I personally serve it from a DB. 
if (!template) {
 return BUILT_HTML_OUTPUT;
}

  return callback(null, template);
});
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'template');

Then you can run your express app on a different port say 3001 and start CRA with:

"start": "WDS_SOCKET_HOST=localhost WDS_SOCKET_PORT=3000 react-scripts start",

@josephr5000
Copy link

@josephr5000 josephr5000 commented May 12, 2020

@Nargonath -
I forked this project and updated the react-scripts/config/webpackDevServer.config.js file.

    quiet: true,
    // WebpackDevServer has an option to write every build to disk. This can be
    // useful for certain use cases such as developing browser extensions where one
    // would need to otherwise do full builds.
    writeToDisk: true,

The above setting with a few other changes like

  "homepage": ".",

got my local dev environment working in CRA2.x

After merging in CRA 3.x, this stopped working. I don't see the /dist folder anymore.

wondering if anyone else is facing this issue?

@Nargonath I'm one of the many who need this feature, and in scanning I found the writeToDisk: true solution and your comment saying it didn't work in CRA 3.x.

I'm using CRA 3.4.1 and just tried this today and found it works perfectly! I hope that provides you some encouragement to try it again.

For routing to work I also needed to set homepage in package.json, but didn't need to make any other changes. yarn start did a dev build and left the results in the dist directory. Very helpful!

@Nargonath
Copy link

@Nargonath Nargonath commented Jun 2, 2020

@josephr5000 Alright, thanks for letting me know. I should try it out again. This would simplify much of my cra-build-watch.

@josephr5000
Copy link

@josephr5000 josephr5000 commented Jun 2, 2020

@josephr5000 Alright, thanks for letting me know. I should try it out again. This would simplify much of my cra-build-watch.

@Nargonath FYI I have noticed this setting is fragile. I think yarn add seems to feel free to "refresh" my node_modules, and finds this modification a nice thing to "refresh". I do occasionally have to re-apply this, so if yours stops working check out the config file to make sure your setting is still there. If I weren't so lazy I'd look for a more permanent fix, but this solves my immediate problem.

@Nargonath
Copy link

@Nargonath Nargonath commented Jun 3, 2020

I developed a lib which monkey patches the node_modules for that exact purpose so I don't think it would have the same problem as you have but thanks for letting know. 😉

@ibraheemdev
Copy link

@ibraheemdev ibraheemdev commented Aug 2, 2020

Is there any update on this? Is there any other way to achieve a development hot reloading output to a folder? This is useful when you serve static files (/build) from a server and need hot reloading without rebuilding every time.

@joaovbibiano
Copy link

@joaovbibiano joaovbibiano commented Aug 7, 2020

I need this, because the same reason above... we have a serve that serves our CRA... in development we need the debug bundle, not just in memory.

@ibraheemdev
Copy link

@ibraheemdev ibraheemdev commented Aug 19, 2020

For anyone else who has this issue, I ended up going with a combination of this gist and the cra-build-watch package. The package is probably the best and most configurable solution, but I just needed a simple script:

process.env.NODE_ENV = "development";

const fs = require("fs-extra");
const paths = require("react-scripts/config/paths");
const webpack = require("webpack");
const config = require("react-scripts/config/webpack.config")("development");

// the output directory of the development files
outputPath = paths.appPath + "/dist";
config.output.path = outputPath;

// update the webpack dev config in order to remove the use of webpack hotreload tools
config.entry = config.entry.filter((f) => !f.match(/webpackHotDevClient/));
config.plugins = config.plugins.filter((p) => !(p instanceof webpack.HotModuleReplacementPlugin));

(async () => {
  await fs.emptyDir(outputPath)
  webpack(config).watch({}, (err) => {
    if (err) {
      console.error(err);
    } else {
      // copy the remaining thing from the public folder to the output folder
      fs.copySync(paths.appPublic, outputPath, {
        dereference: true,
        filter: file => file !== paths.appHtml
      });
    }
  });
})();

@bjankord
Copy link

@bjankord bjankord commented Sep 18, 2020

@iansu @ianschmitz @mrmckeb @Timer @gaearon Is there any interest with adding the writeToDisk flag into create-react-app proper as @pupudu described here?
#1070 (comment)

It looks like there was a PR to add this capability but it was closed purely out of the stale PR bot coming to close it. Would be nice to get that reopened and reviewed if maintainers think this would be a valuable addition to add into create-react-app.

From a consumer perspective, it would be helpful for being able to pipe the built files through express for faster developer feedback when working on an express/create-react-app.

The ability to write files to disk with a watch mode is also a feature that vue-cli has which I've found to be quite useful.

@mrmckeb
Copy link
Collaborator

@mrmckeb mrmckeb commented Sep 22, 2020

We actually utilise something similar in my company, I agree there's value in this feature... but I'm not sure we can get it out for 4.0 though. I'll flag it for discussion.

@mrmckeb mrmckeb removed this from the 100.0 milestone Sep 22, 2020
@mrmckeb mrmckeb added this to the 4.0 milestone Sep 22, 2020
@mrmckeb mrmckeb removed this from the 4.0 milestone Sep 22, 2020
@mrmckeb mrmckeb added this to the 4.1 milestone Sep 22, 2020
@pupudu
Copy link

@pupudu pupudu commented Sep 22, 2020

Great to see this being discussed.
I am happy to update the PR and reopen when we are good to go 👍

mildmojo pushed a commit to xometry/create-react-app that referenced this issue Oct 15, 2020
Applies the patch from this abandoned PR:

facebook#7812

Based on this 4-year-old issue about emitting dev builds to disk:

facebook#1070

To use, call `react-scripts start --writeToDisk`. This will emit
dev environment built files to the normally-production output
path.
@lewisl9029
Copy link

@lewisl9029 lewisl9029 commented Nov 4, 2020

@pupudu, was looking at your PR just now, looks like it changes the publicUrl as well as other unrelated behavior based on the flag (whether or not to open the browser). Just wanted to raise that there are many use cases for writing the dev bundle to disk outside of serving dev assets with a different server, that might require the publicUrl to remain unchanged:

For instance, building an unoptimized build for faster CI (#1960, #1070 (comment)), developing browser extensions (#1070 (comment)) or electron apps (#1070 (comment)) using the CRA dev server for faster feedback loop with fast refresh and faster builds (no need to optimize), and for interacting with other developer tools that might make use of the unoptimized bundle (I'm actually working on something right now that processes dev bundles, and currently it requires patching CRA).

I think @ioloie's PR (#6144) that only adds a WRITE_TO_DISK env var is the more flexible option, since everything else your PR changes can already be controlled through existing env vars independently from WRITE_TO_DISK behavior (see BROWSER, PUBLIC_URL): https://create-react-app.dev/docs/advanced-configuration/

@bjankord
Copy link

@bjankord bjankord commented Nov 4, 2020

I've been using https://www.npmjs.com/package/cra-build-watch as a work-around for this issue. I like the idea of a WRITE_TO_DISK flag. I think it would be awesome if when that flag is set, it applies the webpack config changes that are set in the cra-build-watch package.

@efranca
Copy link

@efranca efranca commented Nov 24, 2020

@bjankord Thanks for the hint! It serves perfectly for my need as well. It would be very nice to have this feature out of the box. Anyway, https://www.npmjs.com/package/cra-build-watch works just great!!!

@cseas
Copy link

@cseas cseas commented Dec 6, 2020

This feature would be extremely useful for anyone who's trying to inject some server data or trying to do some server-side templating instead of just serving the build as it is with a static server.

This blog explains the use case very well. Also #1703

The main problem with trying to use Express with create-react-app is that react-scripts start doesn't output anything so there's nothing to serve with Express in development mode with hot reload. This usually is not a problem if the server isn't doing anything in index.html (proxy works fine). But if we want to use any sort of templating using the server then it becomes a challenge for development because while developing we can't serve our react app with Express.

We already have cra-build-watch that does this. Isn't there a way to include that feature as part of create-react-app? It doesn't have to be the default behaviour but something the user can enable with a flag when they know what they want.

@Luk-z
Copy link

@Luk-z Luk-z commented Feb 12, 2021

I'm working on CRA + SSR, this feature would be appreciated.
Currently I accomplished it using nodemon and on every file changes -> yarn build && yarn ssr. But this is not performant and probably I will switch soon to webpack.
Without this feature and working with SSR, eject + webpack is inevitable...

@poelzi
Copy link

@poelzi poelzi commented Jun 9, 2021

I seriously can't understand this bug is open for 5 years already. If you want to give a prototype to testers, you want the version deoployed, but with full debug informations for recording the tracebacks etc. I don't understand why there is no debug build, seriously, I have not seen any compiler that forces you to build optimized builds only....

@sancelot
Copy link

@sancelot sancelot commented Jul 8, 2021

Agreeing with @poelzi

@AntonOfTheWoods
Copy link

@AntonOfTheWoods AntonOfTheWoods commented Jul 9, 2021

Can someone confirm that this makes CRA absolutely useless for use cases where you want to do things like run your data access (to indexeddb, for example) only via the service worker? It appears that the service worker file is only generated for production builds, meaning the sws can only be used for caching, and nothing "serious". Is that the current situation or have I missed some completely obvious way of doing this?

@burakgavaskargo
Copy link

@burakgavaskargo burakgavaskargo commented Aug 4, 2021

I got it worked with Craco.

module.exports = {
  devServer: {
    writeToDisk: true,
  },
};
  • craco start

This will output the files under dist folder

@rib
Copy link

@rib rib commented Aug 8, 2021

I have an existing template-based/jekyll site that is adding pages built with React and introduces a header that is a React/bootstrap component to existing pages. Running a dev server is fine for testing the newer content, but right now I want to debug some details that relate to the integration with the pre-existing site whereby I want to test a full build, as it will run in production, except disable things like javascript minification. It seems surprising that you can't just do something like yarn build --debug to build a site for deployment except keep all javascript debuggable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet