Provide options.scriptExtensions for 2.x#242
Conversation
|
Can one of the admins verify this patch? To accept patch and trigger a build add comment ".ok\W+to\W+test." |
|
Can one of the admins verify this patch? |
2 similar comments
|
Can one of the admins verify this patch? |
|
Can one of the admins verify this patch? |
|
@supasate thank you for the pull request! Does @clarkorz's #240 (comment) apply here too?
|
|
@slnode ok to test |
bajtos
left a comment
There was a problem hiding this comment.
The patch looks good in general, I'd like you to clean up the code a bit - please take a look at my comments below.
|
|
||
| var appRootDir = options.appRootDir = options.appRootDir || process.cwd(); | ||
| var env = options.env || process.env.NODE_ENV || 'development'; | ||
| var scriptExtensions = options.scriptExtensions = options.scriptExtensions || |
There was a problem hiding this comment.
Modifying input arguments (setting options.scriptExtensions) is a bad practice, we have been bitten by this in the past. Please treat options are readonly/frozen.
I see that you are accessing options.scriptExtensions at https://github.com/strongloop/loopback-boot/pull/242/files#diff-ee97c5091b89979aace94674818996baR722.
Here are two of many possible solutions how to keep options frozen and still have scriptExtensions set:
// use prototypal inheritance
options = Object.create(options);
// shallow-copy properties
options = util._extend({}, options);(A line like the above would go to L38-39 above, before we start processing the options object.)
| modelDefinitions, scriptExtensions) { | ||
| var registry = verifyModelDefinitions(rootDir, modelDefinitions, | ||
| scriptExtensions) || | ||
| findModelDefinitions(rootDir, sources, scriptExtensions); |
There was a problem hiding this comment.
The indentation makes it difficult to distinguish function parameters from the second function call. I am proposing the following style:
var registry = verifyModelDefinitions(rootDir, modelDefinitions,
scriptExtensions);
if (!registry) {
registry = findModelDefinitions(rootDir, sources, scriptExtensions);
}| var appRootDir = options.appRootDir = options.appRootDir || process.cwd(); | ||
| var env = options.env || process.env.NODE_ENV || 'development'; | ||
| var scriptExtensions = options.scriptExtensions = options.scriptExtensions || | ||
| _.keys(require.extensions); |
There was a problem hiding this comment.
Considering that you are converting this array back into an object down in isPreferredExtension, I think it's better to make the conversion right here.
var scriptExtensions = options.scriptExtensions ?
arrayToObject(scriptExtensions) :
require.extensions;|
|
||
| function isPreferredExtension(filename, extensions) { | ||
| var includeExtensions = extensions ? | ||
| arrayToObject(extensions) : require.extensions; |
There was a problem hiding this comment.
Since isPreferredExtension is a private method, there is no need to keep support for the old one-arg API.
function isPreferredExtension(filename, includeExtensions) {
assert(!!includeExtensions, '"includeExtensions" argument is required');
// the code below remains unchanged
var ext = path.extname(filename);
return (ext in includeExtensions) && !(ext in getExcludedExtensions());}| process.nextTick(function() { | ||
| process.bootFlags.push('customFinished'); | ||
| callback(); | ||
| }); |
There was a problem hiding this comment.
This is unnecessarily complex. The thing to verify in your test is whether loopback-boot recognises a custom extension. There is no need to verify how boot script are loaded/executed again.
'use strict';
module.exports = function(app, callback) {
process.bootFlags.push('customjs');
callback();
};Same comment applies to custom.customjs2.
| }); | ||
| }); | ||
|
|
||
| it('should load scripts specified in options.scriptExtensions', |
There was a problem hiding this comment.
it('searches boot file extensions specified in options.scriptExtensions')See also http://loopback.io/doc/en/contrib/style-guide.html#test-naming
|
@supasate also please take a look at #240 (comment) - please add a test reproducing the issue (I think the trick will be to use a model file with a custom extension that's not registered in |
|
Also please rebase your patch on top of the latest |
|
Hi @bajtos, I've rebased and fixed issues based on your comments. I made a sample repo at https://github.com/supasate/loopback-boot-with-jest. I'll squash later if it's alright. I also fixed the 3.x branch. |
|
I also found another issue about file name resolving (strongloop/loopback#2201) (not related to this PR but it will make |
| } | ||
|
|
||
| function resolveAppScriptPath(rootDir, relativePath, resolveOptions) { | ||
| function resolveAppScriptPath(rootDir, relativePath, resolveOptions, |
There was a problem hiding this comment.
Please keep resolveOptions as the last function arg. I would consider converting scriptExtensions into a property in resolveOptions, to keep the number of function arguments reasonably short.
There was a problem hiding this comment.
After refactoring, there is no need to use scriptExtensions in this function anymore.
| scriptExtensions, normalization, | ||
| modelInstructions) { | ||
| // load mixins from `options.mixins` | ||
| var sourceFiles = options.mixins || []; |
There was a problem hiding this comment.
What is the rationale for this change? I mean why to pass options properties as individual arguments? buildAllMixinInstructions is already accepting way too many arguments, we should look into ways how to make that list shorter, not longer...
There was a problem hiding this comment.
I tried to make code look consistent with how mixinDirs and mixinSources are declared and passed to the function..
Do you want to refactor those arguments by passing only options and set their default values in buildAllMixinInstructions instead?
There was a problem hiding this comment.
I refactored by passing only options.
| (typeof require.extensions[otherFileExtension]) === 'function') { | ||
| results.push(otherFile); | ||
| } | ||
| // if (!onlyScriptsExportingFunction) |
There was a problem hiding this comment.
We don't keep commented-out code around, please remove.
If I understand this code correctly, the intention was:
- return a file with any extension when
onlyScriptsExportingFunctionis not set - return a file with an extension that can be loaded by Node.js runtime (an extension that is registered in
require.extensions) whenonlyScriptsExportingFunctionis set
We should preserve these two modes.
I am proposing to replace onlyScriptsExportingFunction arg with scriptExtensions and modify all function callers accordingly.
// implementation here
if (!scriptExtensions || otherFileExtension in scriptExtensions)
results.push(otherFile);
// usage - flag not set
fixFileExtension(filepath, files)
// usage -flag set
fixFileExtension(filepath, files, scriptExtensions)There was a problem hiding this comment.
Oops! That's my mistake. I tested something and forgot to uncomment. I'll fix as you suggested.
|
@supasate thank you for the pull request, please take a look at the comment I left above. Also please rebase your patch on top of the latest |
|
About rebasing on top of the latest |
|
Landed, thank you for the contribution! |
|
Released in |
|
Thanks @bajtos ! I think you can review the 3.x branch also. The code is almost the same except that it is splitted into several files. |
Description
Provide scriptExtensions option for custom loading.
Due to require.extensions is not used by some framework (e.g. jest). So, this feature allows specifying file extensions supported by custom loaders.
Related issues
strongloop/loopback#3204
Checklist
guide