Support es2015 module exports (Babel, TypeScript)

Detect when a script file is an es2015 module created by
a transpiler like Babel or TypeScript and use the `default`
export instead of `module.exports` in such case.

The following artefacts support the new syntax now:

  - boot scripts
  - components
  - middleware
  - model scripts
This commit is contained in:
John McLaughlin 2017-06-18 19:49:16 +07:00 committed by Miroslav Bajtoš
parent e5224368d4
commit 2b596c8c8b
No known key found for this signature in database
GPG Key ID: 6F2304BA9361C7E3
14 changed files with 105 additions and 13 deletions

View File

@ -14,6 +14,7 @@ var debug = require('debug')('loopback:boot:compiler');
var Module = require('module');
var _ = require('lodash');
var g = require('strong-globalize')();
var requireNodeOrEsModule = require('./require');
var FILE_EXTENSION_JSON = '.json';
@ -589,7 +590,7 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
// Try to require the module and check if <module>.<fragment> is a valid
// function
var m = require(sourceFile);
var m = requireNodeOrEsModule(sourceFile);
if (typeof m[fragment] === 'function') {
resolved.sourceFile = sourceFile;
return resolved;
@ -778,7 +779,7 @@ function loadMixins(sourceFiles, normalization) {
meta.name = name;
if (utils.fileExistsSync(metafile)) {
// May overwrite name, not sourceFile
_.extend(meta, require(metafile));
_.extend(meta, requireNodeOrEsModule(metafile));
}
meta.sourceFile = filepath;
mixinInstructions[meta.name] = meta;

View File

@ -10,6 +10,7 @@ var async = require('async');
var path = require('path');
var format = require('util').format;
var g = require('strong-globalize')();
var requireNodeOrEsModule = require('./require');
/**
* Execute bootstrap instructions gathered by `boot.compile`.
@ -211,7 +212,7 @@ function defineMixins(app, instructions) {
if (!modelBuilder.mixins || !mixins.length) return;
mixins.forEach(function(obj) {
var mixin = require(obj.sourceFile);
var mixin = requireNodeOrEsModule(obj.sourceFile);
if (typeof mixin === 'function' || mixin.prototype instanceof BaseClass) {
debug('Defining mixin %s', obj.name);
@ -244,7 +245,7 @@ function defineModels(app, instructions) {
model = registry.createModel(data.definition);
if (data.sourceFile) {
debug('Loading customization script %s', data.sourceFile);
var code = require(data.sourceFile);
var code = requireNodeOrEsModule(data.sourceFile);
if (typeof code === 'function') {
debug('Customizing model %s', name);
code(model);
@ -288,12 +289,12 @@ function runScripts(app, list, callback) {
list.forEach(function(filepath) {
debug('Requiring script %s', filepath);
try {
var exports = require(filepath);
if (typeof exports === 'function') {
var bootFn = requireNodeOrEsModule(filepath);
if (typeof bootFn === 'function') {
debug('Exported function detected %s', filepath);
functions.push({
path: filepath,
func: exports,
func: bootFn,
});
}
} catch (err) {
@ -340,7 +341,7 @@ function setupMiddleware(app, instructions) {
middleware.forEach(function(data) {
debug('Configuring middleware %j%s', data.sourceFile,
data.fragment ? ('#' + data.fragment) : '');
var factory = require(data.sourceFile);
var factory = requireNodeOrEsModule(data.sourceFile);
if (data.fragment) {
factory = factory[data.fragment].bind(factory);
}
@ -429,7 +430,7 @@ function getUpdatedConfigObject(app, config, opts) {
function setupComponents(app, instructions) {
instructions.components.forEach(function(data) {
debug('Configuring component %j', data.sourceFile);
var configFn = require(data.sourceFile);
var configFn = requireNodeOrEsModule(data.sourceFile);
var opts = {
useEnvVars: true,
};

9
lib/require.js Normal file
View File

@ -0,0 +1,9 @@
// Copyright IBM Corp. 2015,2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function requireNodeOrEsModule(sourceFile) {
var exports = require(sourceFile);
return exports && exports.__esModule ? exports.default : exports;
};

View File

@ -57,10 +57,15 @@ describe('browser support', function() {
expect(Object.keys(app.models)).to.include('Customer');
expect(app.models.Customer.settings)
.to.have.property('_customized', 'Customer');
expect(Object.keys(app.models)).to.include('ProductUmd');
expect(app.models.ProductUmd.settings)
.to.have.property('_customized', 'UMD');
// configured in fixtures/browser-app/component-config.json
// and fixtures/browser-app/components/dummy-component.js
// configured in fixtures/browser-app/component-config.json,
// fixtures/browser-app/components/dummy-component.js and
// fixtures/browser-app/components/dummy-component-umd.js
expect(app.dummyComponentOptions).to.eql({ option: 'value' });
expect(app.dummyComponentUmdOptions).to.eql({ option: 'valueUmd' });
done();
});
@ -79,8 +84,9 @@ describe('browser support', function() {
var modelBuilder = app.registry.modelBuilder;
var registry = modelBuilder.mixins.mixins;
expect(Object.keys(registry)).to.eql(['TimeStamps']);
expect(Object.keys(registry)).to.eql(['AuditedUmd', 'TimeStamps']);
expect(app.models.Customer.timeStampsMixin).to.eql(true);
expect(app.models.ProductUmd.auditedMixin).to.eql(true);
done();
});

View File

@ -278,6 +278,7 @@ describe('executor', function() {
'barStarted',
'barFinished',
'barSyncExecuted',
'umdLoaded',
]);
done();
}, 10);
@ -294,6 +295,7 @@ describe('executor', function() {
'barStarted',
'barFinished',
'barSyncExecuted',
'umdLoaded',
]);
done();
});
@ -838,6 +840,7 @@ describe('executor', function() {
.end(function(err, res) {
if (err) return done(err);
expect(res.headers.names).to.equal('custom-middleware');
expect(res.headers.umd).to.equal('success');
done();
});
});

View File

@ -1,5 +1,8 @@
{
"./components/dummy-component": {
"option": "value"
},
"./components/dummy-component-umd": {
"option": "valueUmd"
}
}

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2015,2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = {
default: function(app, options) {
app.dummyComponentUmdOptions = options;
},
};
Object.defineProperty(module.exports, '__esModule', { value: true });

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2014,2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = {
default: function(Model, options) {
Model.auditedMixin = true;
},
};
Object.defineProperty(module.exports, '__esModule', { value: true });

View File

@ -7,5 +7,8 @@
},
"Customer": {
"dataSource": "db"
},
"ProductUmd": {
"dataSource": "db"
}
}

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2014,2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = {
default: function(ProductUmd) {
ProductUmd.settings._customized = 'UMD';
},
};
Object.defineProperty(module.exports, '__esModule', { value: true });

View File

@ -0,0 +1,5 @@
{
"name": "ProductUmd",
"base": "User",
"mixins": {"AuditedUmd": {} }
}

11
test/fixtures/simple-app/boot/umd.js vendored Normal file
View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2014,2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = {
default: function(app) {
process.bootFlags.push('umdLoaded');
},
};
Object.defineProperty(module.exports, '__esModule', { value: true });

View File

@ -2,6 +2,9 @@
"initial": {
"../../helpers/push-name-middleware": {
"params": "custom-middleware"
}
},
"../../helpers/set-umd-middleware": {
"params": "success"
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = {
default: function(value) {
return function(req, res, next) {
res.setHeader('umd', value);
next();
};
},
};
Object.defineProperty(module.exports, '__esModule', { value: true });