简体   繁体   中英

Webpack 3 + babel only parsing when entry is NOT higher than ./webpack.config.js

I'm building a scaffolding engine to spin up Django-backend + React-frontend sites. I'm also taking a multi-page app approach, instead of an SPA, so things are a bit... unorthodox.

I'm showing you the end result of that scaffolding because the problem seems to be with my webpack or babel configuration, and the location of the input component I give them.

So first, this file structure DOES work, the react component renders beautifully on Django's server.

node/
    package.json
    .babelrc
    webpack.config.js
    /src
        /Home.js (<- the react component)
    ...
www/
    manage.py
    www/
         templates/
             home.html
         dist/
             home.js (<- webpack's output)
...

But I would prefer it like this, where the component is closer to the django template.html file:

node/
    package.json
    .babelrc
    webpack.config.js
    ...
www/
    manage.py
    www/
         templates/
             home.html
             react/
                  components/
                  containers/
                      Home.js (<- the react component)
         dist/
             home.js (<- webpack's output)

However, I now get a build error where it complains when it sees the JSX - as if babel didn't polyfill it at all.

The only thing I changed is that the component is no longer under the node/ directory (and obviously I updated the path in webpack.config.js to reflect that)

I don't think it's a matter of webpack finding the right path. Because it IS reading the component in the templates dir, and it's still putting the bundle correctly in the dist dir. But it's not polyfilling, and the resulting output file is broken. I don't just mean it's uglified, I mean it's completely broken (see below)

Is it even possible to do what I'm trying to do?

Details

The error when I run 'npm run build' to run webpack:

ERROR in ../www/www/templates/react/containers/HomeContainer.js
Module build failed: SyntaxError: Unexpected token (6:2)

  4 | const Home = props => {
  5 |   return (
> 6 |       <div>

babelrc

{
    "presets": ["env", "react"]
}

webpack.config.js

const webpack = require('webpack'); //to access built-in plugins
const path = require('path');

// This one works
// let ent = "./HomeContainer.js"

// This one does NOT work
let ent = "../www/www/templates/react/containers/HomeContainer.js"

let out = "../www/www/static/dist/js/Home"

module.exports = {
    entry: {
        [out]: ent
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, './'),
    },
    module: {
        rules: [
            { test: /\.css$/,  exclude: /node_modules/, use: 'css-loader' },
            { test: /\.scss$/, exclude: /node_modules/, use: 'sass-loader' },
            { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: "babel-loader" }
        ],
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(),
    ],
}

package.json

{
    "name": "frontend",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo 'Error: no test specified' && exit 1",
        "build": "webpack"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "babel-cli": "^6.24.1",
        "babel-core": "^6.25.0",
        "babel-loader": "^7.1.1",
        "babel-preset-env": "^1.6.0",
        "babel-preset-react": "^6.24.1",
        "css-loader": "^0.28.4",
        "node-sass": "^4.5.3",
        "sass-loader": "^6.0.6",
        "webpack": "^3.1.0"
    },
    "dependencies": {
        "react": "^15.6.1",
        "react-dom": "^15.6.1"
    }
}

The broken dist/home.js

!function(m){function t(e){if(n[e])return n[e].exports;var r=n[e]={i:e,l:!1,exports:{}};return m[e].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=m,t.c=n,t.d=function(m,n,e){t.o(m,n)||Object.defineProperty(m,n,{configurable:!1,enumerable:!0,get:e})},t.n=function(m){var n=m&&m.__esModule?function(){return m.default}:function(){return m};return t.d(n,"a",n),n},t.o=function(m,t){return Object.prototype.hasOwnProperty.call(m,t)},t.p="",t(t.s=0)}([function(m,t){throw new Error("Module build failed: SyntaxError: Unexpected token (6:2)\n\n[0m [90m 4 | [39m[36mconst[39m [33mHome[39m [33m=[39m props [33m=>[39m {\n [90m 5 | [39m\t[36mreturn[39m (\n[31m[1m>[22m[39m[90m 6 | [39m\t\t[33m<[39m[33mdiv[39m[33m>[39m\n [90m   | [39m\t\t[31m[1m^[22m[39m\n [90m 7 | [39m\t\t\t[33m<[39m[33mh1[39m[33m>[39m[33mHome[39m page [36mfor[39m { window[33m.[39mprops[33m.[39mproject_title }[33m<[39m[33m/[39m[33mh1[39m[33m>[39m\n [90m 8 | [39m\t\t\t[33m<[39m[33mp[39m[33m>[39m[33mTo[39m edit [36mthis[39m page[33m,[39m you[32m'll find the react component at /frontend/src/root/Home.js</p>[39m\n [90m 9 | [39m\t\t[33m<[39m[33m/[39m[33mdiv[39m[33m>[39m[0m\n")}]);

The dist/home.js that does work

!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=81)}([function(e,t){function n(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function r(e){if(l===setTimeout)return setTimeout(e,0);if((l===n||!l)&&setTimeout)return l=setTimeout,setTimeout(e,0);try{return l(e,0)}catch(t){try{return l.call(null,e,0)}catch(t){return l.call(this,e,0)}}}function i(e){if(p===clearTimeout)return clearTimeout(e);if((p===o||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(e);try{return p(e)}catch(t){try{return p.call(null,e)}catch(t){return p.call(this,e)}}}function a(){m&&f&&(m=!1,f.length?h=f.concat(h):v=-1,h.length&&s())}function s(){if(!m){var e=r(a);m=!0;for(var t=h.length;t;){for(f=h,h=[];++v<t;)f&&f[v].run();v=-1,t=h.length}f=null,m=!1,i(e)}}function u(e,t){this.fun=e,this.array=t}function c(){}var l,p,d=e.exports={};!function(){try{l="function"==typeof setTimeout?setTimeout:n}catch(e){l=n}try{p="function"==typeof clearTimeout?clearTimeout:o}catch(e){p=o}}();var f,h=[],m=!1,v=-1;d.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];h.push(new u(e,t)),1!==h.length||m||r(s)},u.prototype.run=function(){this.fun.apply(null,this.array)},d.title="browser",d.browser=!0,d.env={},d.argv=[],d.version="",d.versions={},d.on=c,d.addListener=c,d.once=c,d.off=c,d.removeListener=c,d.removeAllListeners=c,d.emit=c,d.prependListener=c,d.prependOnceListener=c,d.listeners=function(e){return[]},d.binding=function(e){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(e){throw new Error("process.chdir is not supported")},d.umask=function(){return 0}},function(e,t,n){"use strict";(function(t){function n(e,t,n,r,i,a,s,u){if(o(t),!e){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,i,a,s,u],p=0;c=new Error(t.replace(/%s/g,function(){return l[p++]})),c.name="Invariant Violation"}throw c.framesToPop=1,c}}var o=function(e){};"production"!==t.env.NODE_ENV&&(o=function(e){if(void 0===e)throw new Error("invariant requires an error message argument")}),e.exports=n}).call(t,n(0))},function(e,t,n){"use strict";(function(t){var o=n(9),r=o;if("production"!==t.env.NODE_ENV){var i=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),o=1;o<t;o++)n[o-1]=arguments[o];var r=0,i="Warning: "+e.replace(/%s/g,function(){return n[r++]});"undefined"!=typeof console&&console.error(i);try{throw new Error(i)}catch(e){}};r=function(e,t){if(void 0===t)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(0!==t.indexOf("Failed Composite propType: ")&&!e){for(var n=arguments.length,o=Array(n>2?n-2:0),r=2;r<n;r++)o[r-2]=arguments[r];i.apply(void 0,[t].concat(o))}}}e.exports=r}).call(t,n(0))},function(e,t,n){"use strict";function o(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,o=0;o<t;o++)n+="&args[]="+encodeURIComponent(arguments[o+1]);n+=" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.";var r=new Error(n);throw r.name="Invariant Violation",r.framesToPop=1,r}e.exports=o},function(e,t,n){"use strict";function o(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}/*

The issue is that Babel looks for a .babelrc closest to the file that is being transpiled . That means it looks in the directory of the file and starts climbing up the directory tree until it finds one or reaches the root of the file system. But as your node directory is a sibling of www , where your sources are, it is impossible to find your .babelrc and therefore your presets won't be applied.

You can configure options on babel-loader , which would be used regardless of the directory. Unfortunately you'll run into an issue with the module resolution, which by default is the same as the Node module resolution . When a module is imported it will be searched for in the node_modules directory and if it wasn't found there, it will look in the parent directory (ie ../node_modules ) and so on. This is the same behaviour that Babel uses, and that's very common for tools in Node.

The presets can be imported in the config, so they aren't being resolved from the file that is being transpiled.

{
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  options: {
    presets: [
      // Need to require them, so they are resolved from this config,
      // otherwise they are resolved from the directory of the file that
      // is being transpiled.
      require('babel-preset-env'),
      require('babel-preset-react')
    ]
  }
}

In addition to that you'll face the problem of resolving any module you installed with npm, because they are again in a sibling directory and won't be found. Webpack allows you to work around that with resolve.module .

resolve: {
  modules: [
    // Also look for modules next to the webpack config, because dependencies
    // like `react` are installed here, not where the components are.
    path.resolve(__dirname, 'node_modules'),
    'node_modules'
  ],
}

Even though that works, I strongly advise against this structure. After all it's a workaround within webpack and if you want to add any other tool you will very likely run into issues.

The most important part is that the node modules should be in the root of your project and anything that want to use them will be in subdirectories. Something like this:

.
├─ .babelrc
├─ manage.py
├─ package.json
├─ webpack.config.js
└─ www
   ├─ dist
   │  └─ bundle.js
   └─ templates
      ├─ home.html
      └─ react
         ├─ components
         └─ containers
            └─ Home.js

With that you don't need to work around anything and you can be sure that any module installed from npm will work. Additionally, you can run everything from the root of your project, whether it's installing an npm package, running webpack or running your python build.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM