简体   繁体   中英

Can I load a file from a bundled javascript application?

I have a node application that when built is bundled all into one file. I want to split out of this bundle the app configuration parameters (it's just a simple object).

The ./build directory becomes populated with only three files: index.js , config.js and a map file.

When I cd into the directory and launch the app with node index.js , I get the following error:

TypeError: Cannot read property 'logPath' of undefined
    at Module.<anonymous> (/home/*/repo/build/index.js:1:2367)
    at t (/home/*/repo/build/index.js:1:172)
    at /home/*/repo/build/index.js:1:964
    at Object.<anonymous> (/home/*/repo/build/index.js:1:973)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

The top of the built config.js file looks like

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = {
    logPath: 'logs',
    ....
};

The webpack configuration I am using looks as such

const path = require('path');
const nodeExternals = require('webpack-node-externals');

function excludeConfig(context, request, callback) {
    /config/.test(request)
        ? callback(null, 'require("./config.js")', + request)
        : callback();
}

module.exports = {
    entry: {
        index: path.resolve(__dirname, './src/server/index.js')
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: 'awesome-typescript-loader',
                exclude: ['node_modules']
            }, {
                test: /\.js$/,
                loader: 'source-map-loader',
                enforce: 'pre'
            }
        ]
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
        modules: [path.resolve(__dirname, 'node_modules')]
    },
    devtool: 'source-map',
    output: {
        filename: 'index.js',
        path: path.join(__dirname, '/build')
    },
    mode: 'production',
    target: 'node',
    externals: [
        nodeExternals(),
        excludeConfig
    ],
};

My config file is being built by gulp with the following strategy

const ts = require('gulp-typescript');
const tsProject = ts.createProject('tsconfig.json');
gulp.task('add-config', function () {
    return gulp
        .src('src/*config.ts')
        .pipe(tsProject())
        .pipe(gulp.dest('build'));
});

The tsconfig.json file looks as follows:

{
    "compilerOptions": {
        "outDir": "./build",
        "allowJs": true,
        "checkJs": false,
        "module": "commonjs",
        "target": "es5",
        "moduleResolution": "node",
        "lib": ["es2015", "dom"]
    }
}

My hunch is that after the build the configuration file is not providing what the bundle is expecting.

The bundle does contain the following line:

function (e, r) { e.exports = require("./config.js") }

Any ideas on how I can make the bundle load the config.js file?

It seems that after much static analysis, and lack of a comprehensive understanding of the different ways to import modules, for some reason the bundle defines my config file to be a harmony import and attempts to retrieve a default from the file (which in my case I hadn't any).

There are two fixes available (both worked fine):

  • change the import style away from using defaults by using brackets around the import (I don't know why I prefer this)
import { config } from './../../config';
  • change the export style and adopt the default route
export default <Config>{
    logPath: 'logs',
    ...
};

To be honest, I don't know why this worked before when this project had still not started the migration to TypeScript. I know that everything babel related was removed before I started so I can't get my head around it.

Webpack Internals

The module loader looks like this:

(function(module, exports) {
    module.exports = require("./config.js");
})

It is then passed on to another module and loaded as follows:

/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./../../config */ "./../config");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_config__WEBPACK_IMPORTED_MODULE_1__);
const PATH = _config__WEBPACK_IMPORTED_MODULE_1___default.a.logPath.replace(/\/*$/, '');

Notice how _config__WEBPACK_IMPORTED_MODULE_1__ already has the contents of my configuration object. However, webpack sends it into a function n which encapsulates the import into a getter function on member a . This encapsulation resolves to using default if the module was marked as an esModule

__webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
        function getDefault() { return module['default']; } :
        function getModuleExports() { return module; };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
};

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