185 lines
4.9 KiB
JavaScript
185 lines
4.9 KiB
JavaScript
var getModuleType = require('module-definition');
|
|
var debug = require('debug')('precinct');
|
|
var Walker = require('node-source-walk');
|
|
|
|
var detectiveCjs = require('detective-cjs');
|
|
var detectiveAmd = require('detective-amd');
|
|
var detectiveEs6 = require('detective-es6');
|
|
var detectiveLess = require('detective-less');
|
|
var detectivePostcss = require('detective-postcss');
|
|
var detectiveSass = require('detective-sass');
|
|
var detectiveScss = require('detective-scss');
|
|
var detectiveStylus = require('detective-stylus');
|
|
var detectiveTypeScript = require('detective-typescript');
|
|
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
var natives = process.binding('natives');
|
|
|
|
/**
|
|
* Finds the list of dependencies for the given file
|
|
*
|
|
* @param {String|Object} content - File's content or AST
|
|
* @param {Object} [options]
|
|
* @param {String} [options.type] - The type of content being passed in. Useful if you want to use a non-js detective
|
|
* @return {String[]}
|
|
*/
|
|
function precinct(content, options) {
|
|
options = options || {};
|
|
var dependencies = [];
|
|
var ast;
|
|
var type = options.type;
|
|
|
|
// Legacy form backCompat where type was the second parameter
|
|
if (typeof options === 'string') {
|
|
type = options;
|
|
options = {};
|
|
}
|
|
|
|
debug('options given: ', options);
|
|
|
|
// We assume we're dealing with a JS file
|
|
if (!type && typeof content !== 'object') {
|
|
debug('we assume this is JS');
|
|
var walker = new Walker();
|
|
|
|
try {
|
|
// Parse once and distribute the AST to all detectives
|
|
ast = walker.parse(content);
|
|
debug('parsed the file content into an ast');
|
|
precinct.ast = ast;
|
|
} catch (e) {
|
|
// In case a previous call had it populated
|
|
precinct.ast = null;
|
|
debug('could not parse content: %s', e.message);
|
|
return dependencies;
|
|
}
|
|
// SASS files shouldn't be parsed by Acorn
|
|
} else {
|
|
ast = content;
|
|
|
|
if (typeof content === 'object') {
|
|
precinct.ast = content;
|
|
}
|
|
}
|
|
|
|
type = type || getModuleType.fromSource(ast);
|
|
debug('module type: ', type);
|
|
|
|
var theDetective;
|
|
var mixedMode = options.es6 && options.es6.mixedImports;
|
|
|
|
switch (type) {
|
|
case 'commonjs':
|
|
theDetective = mixedMode ? detectiveEs6Cjs : detectiveCjs;
|
|
break;
|
|
case 'css':
|
|
theDetective = detectivePostcss;
|
|
break;
|
|
case 'amd':
|
|
theDetective = detectiveAmd;
|
|
break;
|
|
case 'es6':
|
|
theDetective = mixedMode ? detectiveEs6Cjs : detectiveEs6;
|
|
break;
|
|
case 'sass':
|
|
theDetective = detectiveSass;
|
|
break;
|
|
case 'less':
|
|
theDetective = detectiveLess;
|
|
break;
|
|
case 'scss':
|
|
theDetective = detectiveScss;
|
|
break;
|
|
case 'stylus':
|
|
theDetective = detectiveStylus;
|
|
break;
|
|
case 'ts':
|
|
theDetective = detectiveTypeScript;
|
|
break;
|
|
case 'tsx':
|
|
theDetective = detectiveTypeScript.tsx;
|
|
break;
|
|
}
|
|
|
|
if (theDetective) {
|
|
dependencies = theDetective(ast, options[type]);
|
|
} else {
|
|
debug('no detective found for: ' + type);
|
|
}
|
|
|
|
// For non-JS files that we don't parse
|
|
if (theDetective && theDetective.ast) {
|
|
precinct.ast = theDetective.ast;
|
|
}
|
|
|
|
return dependencies;
|
|
}
|
|
|
|
function detectiveEs6Cjs(ast, detectiveOptions) {
|
|
return detectiveEs6(ast, detectiveOptions).concat(detectiveCjs(ast, detectiveOptions));
|
|
}
|
|
|
|
function assign(o1, o2) {
|
|
for (var key in o2) {
|
|
if (o2.hasOwnProperty(key)) {
|
|
o1[key] = o2[key];
|
|
}
|
|
}
|
|
|
|
return o1;
|
|
}
|
|
|
|
/**
|
|
* Returns the dependencies for the given file path
|
|
*
|
|
* @param {String} filename
|
|
* @param {Object} [options]
|
|
* @param {Boolean} [options.includeCore=true] - Whether or not to include core modules in the dependency list
|
|
* @param {Object} [options.fileSystem=undefined] - An alternative fs implementation to use for reading the file path.
|
|
* @return {String[]}
|
|
*/
|
|
precinct.paperwork = function(filename, options) {
|
|
options = assign({
|
|
includeCore: true
|
|
}, options || {});
|
|
|
|
// Note: released with options.fs but intended options.fileSystem for consistency in the community
|
|
// TODO: Remove options.fs in the next major version update
|
|
var fileSystem = options.fileSystem || options.fs || fs;
|
|
var content = fileSystem.readFileSync(filename, 'utf8');
|
|
var ext = path.extname(filename);
|
|
var type;
|
|
|
|
if (ext === '.styl') {
|
|
debug('paperwork: converting .styl into the stylus type');
|
|
type = 'stylus';
|
|
}
|
|
// We need to sniff the JS module to find its type, not by extension
|
|
// Other possible types pass through normally
|
|
else if (ext !== '.js' && ext !== '.jsx') {
|
|
debug('paperwork: stripping the dot from the extension to serve as the type');
|
|
type = ext.replace('.', '');
|
|
}
|
|
|
|
if (type) {
|
|
debug('paperwork: setting the module type');
|
|
options.type = type;
|
|
}
|
|
|
|
debug('paperwork: invoking precinct');
|
|
var deps = precinct(content, options);
|
|
|
|
if (!options.includeCore) {
|
|
return deps.filter(function(d) {
|
|
return !natives[d];
|
|
});
|
|
}
|
|
|
|
debug('paperwork: got these results\n', deps);
|
|
return deps;
|
|
};
|
|
|
|
module.exports = precinct;
|