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 Module = require('module');
var _ = require('lodash'); var _ = require('lodash');
var g = require('strong-globalize')(); var g = require('strong-globalize')();
var requireNodeOrEsModule = require('./require');
var FILE_EXTENSION_JSON = '.json'; 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 // Try to require the module and check if <module>.<fragment> is a valid
// function // function
var m = require(sourceFile); var m = requireNodeOrEsModule(sourceFile);
if (typeof m[fragment] === 'function') { if (typeof m[fragment] === 'function') {
resolved.sourceFile = sourceFile; resolved.sourceFile = sourceFile;
return resolved; return resolved;
@ -778,7 +779,7 @@ function loadMixins(sourceFiles, normalization) {
meta.name = name; meta.name = name;
if (utils.fileExistsSync(metafile)) { if (utils.fileExistsSync(metafile)) {
// May overwrite name, not sourceFile // May overwrite name, not sourceFile
_.extend(meta, require(metafile)); _.extend(meta, requireNodeOrEsModule(metafile));
} }
meta.sourceFile = filepath; meta.sourceFile = filepath;
mixinInstructions[meta.name] = meta; mixinInstructions[meta.name] = meta;

View File

@ -10,6 +10,7 @@ var async = require('async');
var path = require('path'); var path = require('path');
var format = require('util').format; var format = require('util').format;
var g = require('strong-globalize')(); var g = require('strong-globalize')();
var requireNodeOrEsModule = require('./require');
/** /**
* Execute bootstrap instructions gathered by `boot.compile`. * Execute bootstrap instructions gathered by `boot.compile`.
@ -211,7 +212,7 @@ function defineMixins(app, instructions) {
if (!modelBuilder.mixins || !mixins.length) return; if (!modelBuilder.mixins || !mixins.length) return;
mixins.forEach(function(obj) { mixins.forEach(function(obj) {
var mixin = require(obj.sourceFile); var mixin = requireNodeOrEsModule(obj.sourceFile);
if (typeof mixin === 'function' || mixin.prototype instanceof BaseClass) { if (typeof mixin === 'function' || mixin.prototype instanceof BaseClass) {
debug('Defining mixin %s', obj.name); debug('Defining mixin %s', obj.name);
@ -244,7 +245,7 @@ function defineModels(app, instructions) {
model = registry.createModel(data.definition); model = registry.createModel(data.definition);
if (data.sourceFile) { if (data.sourceFile) {
debug('Loading customization script %s', data.sourceFile); debug('Loading customization script %s', data.sourceFile);
var code = require(data.sourceFile); var code = requireNodeOrEsModule(data.sourceFile);
if (typeof code === 'function') { if (typeof code === 'function') {
debug('Customizing model %s', name); debug('Customizing model %s', name);
code(model); code(model);
@ -288,12 +289,12 @@ function runScripts(app, list, callback) {
list.forEach(function(filepath) { list.forEach(function(filepath) {
debug('Requiring script %s', filepath); debug('Requiring script %s', filepath);
try { try {
var exports = require(filepath); var bootFn = requireNodeOrEsModule(filepath);
if (typeof exports === 'function') { if (typeof bootFn === 'function') {
debug('Exported function detected %s', filepath); debug('Exported function detected %s', filepath);
functions.push({ functions.push({
path: filepath, path: filepath,
func: exports, func: bootFn,
}); });
} }
} catch (err) { } catch (err) {
@ -340,7 +341,7 @@ function setupMiddleware(app, instructions) {
middleware.forEach(function(data) { middleware.forEach(function(data) {
debug('Configuring middleware %j%s', data.sourceFile, debug('Configuring middleware %j%s', data.sourceFile,
data.fragment ? ('#' + data.fragment) : ''); data.fragment ? ('#' + data.fragment) : '');
var factory = require(data.sourceFile); var factory = requireNodeOrEsModule(data.sourceFile);
if (data.fragment) { if (data.fragment) {
factory = factory[data.fragment].bind(factory); factory = factory[data.fragment].bind(factory);
} }
@ -429,7 +430,7 @@ function getUpdatedConfigObject(app, config, opts) {
function setupComponents(app, instructions) { function setupComponents(app, instructions) {
instructions.components.forEach(function(data) { instructions.components.forEach(function(data) {
debug('Configuring component %j', data.sourceFile); debug('Configuring component %j', data.sourceFile);
var configFn = require(data.sourceFile); var configFn = requireNodeOrEsModule(data.sourceFile);
var opts = { var opts = {
useEnvVars: true, 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(Object.keys(app.models)).to.include('Customer');
expect(app.models.Customer.settings) expect(app.models.Customer.settings)
.to.have.property('_customized', 'Customer'); .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 // configured in fixtures/browser-app/component-config.json,
// and fixtures/browser-app/components/dummy-component.js // 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.dummyComponentOptions).to.eql({ option: 'value' });
expect(app.dummyComponentUmdOptions).to.eql({ option: 'valueUmd' });
done(); done();
}); });
@ -79,8 +84,9 @@ describe('browser support', function() {
var modelBuilder = app.registry.modelBuilder; var modelBuilder = app.registry.modelBuilder;
var registry = modelBuilder.mixins.mixins; 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.Customer.timeStampsMixin).to.eql(true);
expect(app.models.ProductUmd.auditedMixin).to.eql(true);
done(); done();
}); });

View File

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

View File

@ -1,5 +1,8 @@
{ {
"./components/dummy-component": { "./components/dummy-component": {
"option": "value" "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": { "Customer": {
"dataSource": "db" "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": { "initial": {
"../../helpers/push-name-middleware": { "../../helpers/push-name-middleware": {
"params": "custom-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 });