lasso
Version:
Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application
203 lines (171 loc) • 7.13 kB
JavaScript
const ok = require('assert').ok;
const nodePath = require('path');
const streamToString = require('./util/streamToString');
const transport = require('lasso-modules-client/transport');
const StringTransformer = require('./util/StringTransformer');
function shouldPreresolvePath(path) {
return true;
// if (path.indexOf('$') !== -1) {
// // If the require path has a special "$" character then
// // we must replace with the preresolved path to avoid
// // problems.
// return true;
// }
//
// if (path.indexOf('node_modules') !== -1) {
// // If the require path includes a "node_modules" directory in the path then
// // preresolve since we normalize "node_modules" to "$"
// return true;
// }
//
//
// if (path.charAt(0) === '.') {
// return false;
// }
//
// return true;
}
function preresolvePath(require, stringTransformer) {
if (!require.argRange) {
// Can't preresolve the require if we don't know where it is in the string...
return;
}
const resolved = require.resolved;
if (shouldPreresolvePath(require.path)) {
stringTransformer.comment(require.argRange);
stringTransformer.insert(require.argRange[0], '\'' + resolved.clientPath + '\'');
}
}
function transformRequires(code, inspected, asyncBlocks, lassoContext) {
// We have two goals with this function:
// 1) Comment out all non-JavaScript module requires
// require('./test.css'); --> /*require('./test.css');*/
// 2) Update the first argument for all require('lasso-loader').async(...) calls
//
// In addition, we want to *maintain line numbers* for the transformed code to be nice!
const stringTransformer = new StringTransformer();
function transformRequire(require) {
ok(require.resolved, '"require.resolved" expected');
if (require.resolved.voidRemap) {
if (require.range) {
stringTransformer.comment(require.range);
stringTransformer.insert(require.range[0], '({})');
}
return;
}
const resolved = require.resolved;
if (!resolved.isDir && (resolved.type || !lassoContext.dependencyRegistry.getRequireHandler(resolved.path, lassoContext))) {
if (require.range) {
stringTransformer.comment(require.range);
stringTransformer.insert(require.range[0], 'void 0');
}
} else {
preresolvePath(require, stringTransformer);
}
}
function transformAsyncCall(asyncBlock) {
const name = asyncBlock.name;
const firstArgRange = asyncBlock.firstArgRange;
if (asyncBlock.packageIdProvided) {
// If `name` is not provided then it means that there was no
// function body so there is no auto-generated async meta name.
if (name) {
const packageIdExpression = code.substring(asyncBlock.firstArgRange[0], asyncBlock.firstArgRange[1]);
// This path is taken when when async is called no arguments:
// For example:
// require('lasso-loader').async(blah, function() {...});
stringTransformer.comment(firstArgRange);
stringTransformer.insert(firstArgRange[0],
'[' + JSON.stringify(name) + ',' + packageIdExpression + ']');
}
} else if (asyncBlock.hasInlineDependencies) {
ok(name, '"asyncBlock.name" required');
// This path is taken when when async is called with
// array of dependencies.
// For example:
// require('lasso-loader').async(['./a.js', './b.js'], function() {...});
stringTransformer.comment(firstArgRange);
stringTransformer.insert(firstArgRange[0], JSON.stringify(name));
} else {
ok(name, '"asyncBlock.name" required');
// This path is taken when when async is called no arguments:
// For example:
// require('lasso-loader').async(function() {...});
stringTransformer.insert(firstArgRange[0], JSON.stringify(name) + ', ');
}
}
if (asyncBlocks && asyncBlocks.length) {
asyncBlocks.forEach(transformAsyncCall);
}
inspected.allRequires.forEach(transformRequire);
return stringTransformer.transform(code);
}
exports.create = function(config, lasso) {
return {
properties: {
path: 'string',
file: 'file',
globals: 'string',
wait: 'boolean',
object: 'boolean',
inspected: 'object',
requireCreateReadStream: 'function',
requireLastModified: 'function',
asyncBlocks: 'array'
},
getDir: function() {
return nodePath.dirname(this.file);
},
getSourceFile: function() {
return this.file;
},
read: function(lassoContext) {
const requireCreateReadStream = this.requireCreateReadStream;
const requireInspected = this.inspected;
const asyncBlocks = this.asyncBlocks;
const isObject = this.object;
const globals = this.globals;
const path = this.path;
const additionalVars = this._additionalVars;
ok(requireCreateReadStream, '"requireCreateReadStream" is required');
ok(requireInspected, '"requireInspected" is required');
ok(path, '"path" is required');
const stream = requireCreateReadStream();
return streamToString(stream)
.then((code) => {
if (isObject) {
return transport.codeGenerators.define(path, code, {
object: true,
modulesRuntimeGlobal: config.modulesRuntimeGlobal
});
} else {
const transformedCode = transformRequires(code, requireInspected, asyncBlocks, lassoContext);
const defCode = transport.codeGenerators.define(
path,
transformedCode,
{
additionalVars,
globals,
modulesRuntimeGlobal: config.modulesRuntimeGlobal
});
return defCode;
}
});
},
async getLastModified (lassoContext) {
return this.requireLastModified;
},
getUnbundledTargetPrefix: function(lassoContext) {
return config.unbundledTargetPrefix;
},
getUnbundledTarget(lassoContext) {
return this.path;
},
calculateKey () {
return 'modules-define:' + this.path;
},
toString() {
return `[commonjs-def: path="${this.path}"]`;
}
};
};