babel-plugin-preval icon indicating copy to clipboard operation
babel-plugin-preval copied to clipboard

How to force recompile?

Open silvenon opened this issue 8 years ago • 38 comments

  • babel-plugin-preval version: 1.4.1
  • node version: 8.1.3
  • npm (or yarn) version: npm 5.2.0

Relevant code or config

const result = preval`
  const fs = require('fs');
  const path = require('path');
  const content = fs.readFileSync(path.join(__dirname, 'file.md'));
  module.exports = content.toString();
`;

console.log(result);

What you did: I ran the code above, to export contents of file.md.

What happened: when I modify file.md the result of preval didn't change when I tried to compile it again. Maybe I'm missing something?

Reproduction repository: demo

Problem description: preval probably doesn't see the reason to recompile because its tagged template literal didn't change.

Suggested solution: I don't know. 😅

silvenon avatar Jul 16 '17 15:07 silvenon

The problem isn't with preval, but with the babel cache. You could disable that and it should work (though I don't think that it would recompile in watch mode unless you change the importing file anyway). Perhaps @hzoo has some ideas for us 😀

kentcdodds avatar Jul 16 '17 16:07 kentcdodds

Another way to get around this problem would be to use the import syntax or the // @preval comment. That should resolve most (all?) problems related to this. If that works for you, do you mind adding something as a FAQ?

kentcdodds avatar Jul 16 '17 16:07 kentcdodds

Thanks for your suggestion! I updated my demo with @preval (I also tried import) but it still didn't update when I tried to recompile. I will gladly add a FAQ entry when we figure this out.

silvenon avatar Jul 17 '17 07:07 silvenon

Thanks for giving that a try. Surprised that didn't work! I don't have any time to dedicate to working on this particular issue right now, so anyone's welcome to help with this! Thanks!

kentcdodds avatar Jul 17 '17 12:07 kentcdodds

Hey I've just taken a look. It's definitely that babel-node is caching and the js code "doesn't" change. You could use nodemon to watch for changes and disable the babel cache.

Something like this should work: nodemon.json:

{
  "verbose": false,
  "ignore": ["node_modules"],
  "env": {
    "NODE_ENV": "development",
    "BABEL_DISABLE_CACHE": 1
  },
  "execMap": {
    "js": "babel-node"
  },
  "ext": ".js,.md",
  "watch": "./src/"
}

Then your start script will become "start": "nodemon index.js".

mattphillips avatar Jul 17 '17 14:07 mattphillips

Just ran into this with my docs stuff 😅 I have to manually go in and save the file to recompile. I'm using the import syntax as well with no luck. Going to try and see if I can figure anything out. Some of this stuff is over my head, but I'll report any findings.

souporserious avatar Jul 17 '17 15:07 souporserious

Thanks @souporserious! I experience this with Next.js and it's pretty annoying, I wind up just adding a // 12345 etc... comment to the file using/importing preval and that works alright, but it's not a super great experience. I'd love something that would help avoid this issue for Next/Webpack/Babel/etc.

kentcdodds avatar Jul 17 '17 15:07 kentcdodds

Babel's caching is definitely lacking. There's no core functionality for it, so everyone implements it outside core, which means there aren't any core primitives to express when caches should be invalidated. I'm still hoping I can get something like that into core for 7.x, but I can't promise it'll happen.

loganfsmyth avatar Jul 17 '17 21:07 loganfsmyth

How can we help you @loganfsmyth?

kentcdodds avatar Jul 17 '17 22:07 kentcdodds

Good question. Basically trying to figure out how I can actually get paid to do something interesting at the moment, because working on Babel full-time for free for a few months has kind of eaten through my motivation :(

loganfsmyth avatar Jul 17 '17 22:07 loganfsmyth

Ah! You're still looking for a job? Ping me on twitter DM or something and we'll see if we can help you find something! :smile:

kentcdodds avatar Jul 17 '17 22:07 kentcdodds

Another workaround is clearing out the babel cache folder (default at ./node_modules/.cache/babel-loader before build, my scripts example

  "scripts": {
    "start": "yarn clear:babel-cache && next",
    "build": "yarn clear:babel-cache next build && next export",
    "clear:babel-cache": "rimraf -rf ./node_modules/.cache/babel-loader/*"
  }

kenvunz avatar Jul 18 '17 21:07 kenvunz

Is it bad to set up a watch task and have that run every time a file is changed @kenvunz? Been sort of a rough workflow having to start and stop my dev server to see any changes would love to have it work on file change.

souporserious avatar Jul 19 '17 02:07 souporserious

Bad? No. Performant? No also. Is it noticable? Depends, but probably not.

kentcdodds avatar Jul 19 '17 02:07 kentcdodds

Has anyone gotten this to work with webpack-dev-server? I tried disabling babel-cache by setting an environment variable with no luck :/ I still have to start and stop the server to get any changes.

souporserious avatar Jul 19 '17 05:07 souporserious

@souporserious babel-loader's cache is enabled based on the presence of the cacheDirectory option to the loader. Do you have that flag set?

loganfsmyth avatar Jul 19 '17 05:07 loganfsmyth

Hmm interesting 🤔 I don't have that set at all. Is it possible babel cache doesn't have anything to do with not getting the changes? Could it be something else?

Here's my config:

  const { resolve } = require('path')
  const webpack = require('webpack')

  const config = {
    entry: [
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      resolve(__dirname, 'example/index.js'),
    ],

    output: {
      path: resolve(__dirname, 'example'),
      filename: 'bundle.js',
    },

    devtool: 'inline-source-map',

    devServer: {
      host: '0.0.0.0',
      contentBase: resolve(__dirname, 'example'),
      hot: true,
      inline: true,
      historyApiFallback: true,
      disableHostCheck: true,
    },

    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
              options: {
                plugins: ['preval', 'import-glob'],
                presets: [['es2015', { modules: false }], 'stage-0', 'react'],
              },
            },
          ],
        },
      ],
    },

    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NamedModulesPlugin(),
    ],
  }

  module.exports = config

souporserious avatar Jul 19 '17 17:07 souporserious

You know I don't think I actually thought through my comment last night properly, sorry.

With your setup, it's Webpack itself that is doing the in-memory caching. If you change a referenced file in preval, Webpack has no way of knowing that the JS file needs to be reprocessed. The Babel build process would actually have to return a list of referenced files so Webpack could be told what to watch. I'm not actually 100% sure Webpack exposes an API for that, though I know it does expose an .addDependency , not sure if it's what we'd want.

loganfsmyth avatar Jul 19 '17 17:07 loganfsmyth

Ahh I see 😕 thanks for the explanation! A little over my head, but I'll see if I can get anywhere. This stuff is so powerful, I can deal with the nuances for now 😸

souporserious avatar Jul 19 '17 17:07 souporserious

I don't think there's anything we can do in this package to make this work. Would someone like to document this issue in the README (in the FAQ)? Then we can close this.

kentcdodds avatar Oct 27 '17 22:10 kentcdodds

Workaround:

module.rules: [
  {
    // add this before the babel-loader
    test: path.resolve(__dirname, "file.js"),
    use: {
      loader: path.resolve(__dirname, "add-dependency-loader.js"),
      options: {
        file: path.resolve(__dirname, "file.md")
      }
    }
  }
]
// add-dependency-loader.js
module.exports = function(source, map) {
  this.addDependency(this.query.file);
  this.callback(null, source, map);
}

sokra avatar Nov 26 '17 10:11 sokra

I am not sure this is the right place to follow up the babel cache problem.

As you know I am working on graphql.macro last weekend. Everything works great for one-time production build, but If we do some IO side effects in the babel compile time it will break the recompile mechanism. [graphql.macro#6] 😥

Maybe we should add a note in the babel-plugin-macros repository?

evenchange4 avatar Jan 22 '18 01:01 evenchange4

Yes, we should probably add a "caveats" section for things that are currently problems that we're still working on. Would you like to do that @evenchange4?

kentcdodds avatar Jan 22 '18 02:01 kentcdodds

Sure, I will send a PR later.

evenchange4 avatar Jan 22 '18 02:01 evenchange4

Another workaround is clearing out the babel cache folder (default at ./node_modules/.cache/babel-loader) before build

For me this directory did not exist; my default cache was located at ~/.babel.json (file contents are cached in the JSON). Hopefully this comment saves someone the time it took for me to track that down.

kumar303 avatar Feb 01 '18 00:02 kumar303

I had only one file called files.js that's using preval to read my entire file hierarchy, so any solution including webpack/babel is an overkill. I don't change the file regularly, but I'm changing the files that it reads all the time.

I made a simple node script called watch-files.js that does this:

const fs = require('fs');
const path = require('path');

let filesPath = path.join(__dirname, 'src', 'config', 'files.js');

setInterval(() => {
  const file = fs.readFileSync(filesPath, 'utf8');
  fs.writeFileSync(filesPath, file + ' ');
  setTimeout(() => {
    fs.writeFileSync(filesPath, file);
  }, 100);
}, 1000);

It works perfectly.

kitze avatar Nov 01 '18 04:11 kitze

@kitze that's a fantastic hack 💯 :shipit:

kentcdodds avatar Nov 06 '18 21:11 kentcdodds

Whoops, didn't mean to close! That hack is not considered an "acceptable solution" 😉

kentcdodds avatar Nov 06 '18 21:11 kentcdodds

I just published a lib for this purpose, it works by adding annotation to your js file:

/*!@compileDependencies([
  './my/js-dependencie1.js',
  '../dependencie2.js',
  '../a-directory/',
])*/

when a specified dependency change, the file containing annotation is automatically recompiled, directories are watched recursively and must end with a trailing slash

babel-watch-extra is coupled to nodemon

here is the link: https://github.com/di-ninja/babel-watch-extra

usage: babel-watch-extra --src src --dist dist entry-point1.js entry-point2.js

It has also others advantages against official @babel/cli watcher:

  • take into account deletion and others events, keeping everything synced
  • fix a bug of watching that was caused by awaitWriteFinish option of chokidar used in official @babel/cli watcher (with my favorite editor, geany, when I save a file, it write a temp file first, and then move it to dest, and sometimes the recompile watching was just lost and never fired again until I restart babel watch)

see also: https://stackoverflow.com/a/53510227/5338073

devthejo avatar Nov 28 '18 00:11 devthejo

@takion that's great but I still have no idea how to use your lib in combination with babel-plugin-preval?

kitze avatar Nov 28 '18 11:11 kitze