Comparing version 0.0.0 to 0.1.0
@@ -0,141 +1,219 @@ | ||
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; | ||
var _toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; | ||
(function (global, factory) { | ||
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(require("lodash"), require("acorn/util/walk"), require("walkall")) : typeof define === "function" && define.amd ? define(["lodash", "acorn/util/walk", "walkall"], factory) : global.acornUmd = factory(global.lodash, global.walk, global.walkall); | ||
})(this, function (lodash, walk, walkall) { | ||
"use strict"; | ||
var _lodash = require("lodash"); | ||
var isRequireCallee = lodash.matches({ | ||
name: "require", | ||
type: "Identifier" | ||
}); | ||
var assign = _lodash.assign; | ||
var find = _lodash.find; | ||
var filter = _lodash.filter; | ||
var isMatch = _lodash.isMatch; | ||
var matches = _lodash.matches; | ||
var pluck = _lodash.pluck; | ||
var reject = _lodash.reject; | ||
var take = _lodash.take; | ||
var zip = _lodash.zip; | ||
// Set up an AST Node similar to an ES6 import node | ||
function constructImportNode(node, type) { | ||
var start = node.start; | ||
var end = node.end; | ||
var walk = _interopRequire(require("acorn/util/walk")); | ||
return { | ||
type: type, | ||
reference: node, | ||
specifiers: [], | ||
start: start, end: end | ||
}; | ||
var walkall = _interopRequire(require("walkall")); | ||
var isRequireCallee = matches({ | ||
name: "require", | ||
type: "Identifier" | ||
}); | ||
var isDefineCallee = matches({ | ||
type: "CallExpression" }); | ||
var isArrayExpr = matches({ | ||
type: "ArrayExpression" | ||
}); | ||
var isFuncExpr = matches({ | ||
type: "FunctionExpression" | ||
}); | ||
// Set up an AST Node similar to an ES6 import node | ||
function constructImportNode(node, type) { | ||
var start = node.start; | ||
var end = node.end; | ||
return { | ||
type: type, | ||
reference: node, | ||
specifiers: [], | ||
start: start, end: end | ||
}; | ||
} | ||
function createImportSpecifier(source, isDef) { | ||
// Add the specifier | ||
var name = source.name; | ||
var type = source.type; | ||
var start = source.start; | ||
var end = source.end; | ||
return { | ||
start: start, end: end, | ||
type: "ImportSpecifier", | ||
id: { | ||
type: type, start: start, end: end, name: name | ||
}, | ||
"default": typeof isDef === "boolean" ? isDef : true | ||
}; | ||
} | ||
function createSourceNode(node, source) { | ||
var value = source.value; | ||
var raw = source.raw; | ||
var start = source.start; | ||
var end = source.end; | ||
return { | ||
type: "Literal", | ||
reference: node, | ||
value: value, raw: raw, start: start, end: end | ||
}; | ||
} | ||
function constructCJSImportNode(node) { | ||
var result = constructImportNode(node, "CJSImport"); | ||
var importExpr = undefined, | ||
isVariable = false; | ||
switch (node.type) { | ||
case "CallExpression": | ||
importExpr = node; | ||
break; | ||
case "VariableDeclaration": | ||
isVariable = true; | ||
/* falls through */ | ||
case "Property": | ||
{ | ||
var declaration = isVariable ? node.declarations[0] : node; | ||
// init for var, value for property | ||
var value = declaration.init || declaration.value; | ||
if (isMatch(value, { type: "CallExpression" })) { | ||
importExpr = value; | ||
} | ||
var source = isVariable ? declaration.id : declaration.key; | ||
result.specifiers.push(createImportSpecifier(source, isVariable)); | ||
} | ||
} | ||
function constructCJSImportNode(node) { | ||
var result = constructImportNode(node, "CJSImport"); | ||
var importExpr = undefined, | ||
isVariable = false; | ||
result.source = createSourceNode(node, importExpr.arguments[0]); | ||
return result; | ||
} | ||
function findCJS(ast) { | ||
// Recursively walk ast searching for requires | ||
var requires = []; | ||
walk.simple(ast, walkall.makeVisitors(function (node) { | ||
var expr = undefined; | ||
switch (node.type) { | ||
case "CallExpression": | ||
importExpr = node; | ||
expr = node; | ||
break; | ||
case "Property": | ||
case "VariableDeclaration": | ||
isVariable = true; | ||
/* falls through */ | ||
case "Property": | ||
{ | ||
var declaration = isVariable ? node.declarations[0] : node; | ||
// init for var, value for property | ||
var _value = declaration.init || declaration.value; | ||
if (lodash.isMatch(_value, { type: "CallExpression" })) { | ||
importExpr = _value; | ||
} | ||
var source = isVariable ? declaration.id : declaration.key; | ||
// Add the specifier | ||
var _name = source.name; | ||
var type = source.type; | ||
var _start = source.start; | ||
var _end = source.end; | ||
result.specifiers.push({ | ||
start: _start, end: _end, | ||
type: "ImportSpecifier", | ||
id: { | ||
type: type, start: _start, end: _end, name: _name | ||
}, | ||
"default": isVariable | ||
}); | ||
var declaration = node.declarations ? node.declarations[0] : node; | ||
// init for var, value for property | ||
var value = declaration.init || declaration.value; | ||
if (isMatch(value, { type: "CallExpression" })) { | ||
expr = value; | ||
} | ||
} | ||
if (expr && isRequireCallee(expr.callee)) { | ||
requires.push(node); | ||
} | ||
}), walkall.traversers); | ||
var _importExpr$arguments$0 = importExpr.arguments[0]; | ||
var value = _importExpr$arguments$0.value; | ||
var raw = _importExpr$arguments$0.raw; | ||
var start = _importExpr$arguments$0.start; | ||
var end = _importExpr$arguments$0.end; | ||
// Filter the overlapping requires (e.g. if var x = require('./x') it'll show up twice). | ||
// Do this by just checking line #'s | ||
return reject(requires, function (node) { | ||
return requires.some(function (parent) { | ||
return [node.start, node.stop].some(function (pos) { | ||
return pos > parent.start && pos < parent.end; | ||
}); | ||
}); | ||
}).map(constructCJSImportNode); | ||
} | ||
result.source = { | ||
type: "Literal", | ||
reference: node, | ||
value: value, raw: raw, start: start, end: end | ||
}; | ||
// Note there can be more than one define per file with global registeration. | ||
function findAMD(ast) { | ||
return pluck(filter(ast.body, { | ||
type: "ExpressionStatement" | ||
}), "expression").filter(isDefineCallee) | ||
// Til https://github.com/lodash/lodash/commit/f20d8f5cc05f98775969c504b081ccc1fddb54c5 | ||
.filter(function (node) { | ||
return isMatch(node.callee, { | ||
name: "define", | ||
type: "Identifier" | ||
}); | ||
}) | ||
// Ensure the define takes params and has a function | ||
.filter(function (node) { | ||
return node.arguments.length <= 3; | ||
}).filter(function (node) { | ||
return filter(node.arguments, isFuncExpr).length === 1; | ||
}).filter(function (node) { | ||
return filter(node.arguments, isArrayExpr).length <= 1; | ||
}) | ||
// Now just zip the array arguments and the provided function params | ||
.map(function (node) { | ||
var outnode = constructImportNode(node, "AMDImport"); | ||
return result; | ||
} | ||
var func = find(node.arguments, isFuncExpr); | ||
var imports = find(node.arguments, isArrayExpr) || { elements: [] }; | ||
function findCJS(ast) { | ||
// Recursively walk ast searching for requires | ||
var requires = []; | ||
walk.simple(ast, walkall.makeVisitors(function (node) { | ||
var expr = undefined; | ||
switch (node.type) { | ||
case "CallExpression": | ||
expr = node; | ||
break; | ||
case "Property": | ||
case "VariableDeclaration": | ||
var declaration = node.declarations ? node.declarations[0] : node; | ||
// init for var, value for property | ||
var value = declaration.init || declaration.value; | ||
if (lodash.isMatch(value, { type: "CallExpression" })) { | ||
expr = value; | ||
} | ||
} | ||
if (expr && isRequireCallee(expr.callee)) { | ||
requires.push(node); | ||
} | ||
}), walkall.traversers); | ||
var params = take(func.params, imports.elements.length); | ||
outnode.specifiers = params; | ||
return lodash(requires) | ||
// Filter the overlapping requires (e.g. if var x = require('./x') it'll show up twice). | ||
// Do this by just checking line #'s | ||
.reject(function (node) { | ||
return lodash.any(requires, function (parent) { | ||
return [node.start, node.stop].some(function (pos) { | ||
return lodash.inRange(pos, parent.start + 0.1, parent.end); | ||
}); | ||
if (imports) { | ||
// Use an array even though its not spec as there isn't a better way to | ||
// represent this structure | ||
outnode.sources = imports.elements.map(function (imp) { | ||
return createSourceNode(node, imp); | ||
}); | ||
}).map(constructCJSImportNode).value(); | ||
} | ||
// Make nicer repr: [[importSrc, paramName]] | ||
outnode.imports = zip(imports.elements, params); | ||
} | ||
return outnode; | ||
}); | ||
// Now just format them up | ||
// .map(node => console.log(node)); | ||
} | ||
var acorn_umd = function acorn_umd(ast, options) { | ||
options = lodash.extend({ | ||
cjs: true, | ||
// TODO | ||
amd: false, | ||
es6: true | ||
}, options); | ||
module.exports = function (ast, options) { | ||
options = assign({ | ||
cjs: true, | ||
// TODO | ||
amd: false, | ||
es6: true | ||
}, options); | ||
var result = []; | ||
var result = []; | ||
if (options.cjs) { | ||
result.push.apply(result, _toConsumableArray(findCJS(ast))); | ||
} | ||
if (options.cjs) { | ||
result.push.apply(result, _toConsumableArray(findCJS(ast))); | ||
} | ||
if (options.es6) { | ||
result.push.apply(result, _toConsumableArray(lodash.filter(ast.body, { | ||
type: "ImportDeclaration" | ||
}))); | ||
} | ||
if (options.es6) { | ||
result.push.apply(result, _toConsumableArray(filter(ast.body, { | ||
type: "ImportDeclaration" | ||
}))); | ||
} | ||
return result; | ||
}; | ||
if (options.amd) { | ||
result.push.apply(result, _toConsumableArray(findAMD(ast))); | ||
} | ||
return acorn_umd; | ||
}); | ||
//# sourceMappingURL=./acorn-umd.js.map | ||
return result; | ||
}; | ||
// calleee: { | ||
// name: 'define', | ||
// type: 'Identifier' | ||
// } |
141
gulpfile.js
@@ -1,35 +0,32 @@ | ||
var gulp = require('gulp'); | ||
var $ = require('gulp-load-plugins')({ | ||
replaceString: /^gulp(-|\.)([0-9]+)?/ | ||
}); | ||
const fs = require('fs'); | ||
const gulp = require('gulp'); | ||
const $ = require('gulp-load-plugins')(); | ||
const del = require('del'); | ||
const path = require('path'); | ||
const mkdirp = require('mkdirp'); | ||
const isparta = require('isparta'); | ||
const esperanto = require('esperanto'); | ||
const browserify = require('browserify'); | ||
const runSequence = require('run-sequence'); | ||
const source = require('vinyl-source-stream'); | ||
// Adjust this file to configure the build | ||
const config = require('./config'); | ||
const manifest = require('./package.json'); | ||
const config = manifest.nodeBoilerplateOptions; | ||
const mainFile = manifest.main; | ||
const destinationFolder = path.dirname(mainFile); | ||
// Remove the built files | ||
gulp.task('clean', function(cb) { | ||
del([config.destinationFolder], cb); | ||
del([destinationFolder], cb); | ||
}); | ||
// Remove our temporary files | ||
gulp.task('clean:tmp', function(cb) { | ||
del(['tmp'], cb); | ||
}); | ||
// Send a notification when JSHint fails, | ||
// so that you know your changes didn't build | ||
function ding(file) { | ||
function jshintNotify(file) { | ||
if (!file.jshint) { return; } | ||
return file.jshint.success ? false : 'JSHint failed'; | ||
} | ||
function jscsNotify(file) { | ||
if (!file.jscs) { return; } | ||
return file.jscs.success ? false : 'JSCS failed'; | ||
} | ||
// Lint our source code | ||
gulp.task('lint:src', function() { | ||
gulp.task('lint-src', function() { | ||
return gulp.src(['src/**/*.js']) | ||
@@ -39,3 +36,5 @@ .pipe($.plumber()) | ||
.pipe($.jshint.reporter('jshint-stylish')) | ||
.pipe($.notify(ding)) | ||
.pipe($.notify(jshintNotify)) | ||
.pipe($.jscs()) | ||
.pipe($.notify(jscsNotify)) | ||
.pipe($.jshint.reporter('fail')); | ||
@@ -45,8 +44,10 @@ }); | ||
// Lint our test code | ||
gulp.task('lint:test', function() { | ||
return gulp.src(['test/unit/**/*.js']) | ||
gulp.task('lint-test', function() { | ||
return gulp.src(['test/**/*.js']) | ||
.pipe($.plumber()) | ||
.pipe($.jshint()) | ||
.pipe($.jshint.reporter('jshint-stylish')) | ||
.pipe($.notify(ding)) | ||
.pipe($.notify(jshintNotify)) | ||
.pipe($.jscs()) | ||
.pipe($.notify(jscsNotify)) | ||
.pipe($.jshint.reporter('fail')); | ||
@@ -56,64 +57,23 @@ }); | ||
// Build two versions of the library | ||
gulp.task('build', ['lint:src', 'clean'], function(done) { | ||
esperanto.bundle({ | ||
base: 'src', | ||
entry: config.entryFileName, | ||
}).then(function(bundle) { | ||
res = bundle.toUmd({ | ||
sourceMap: true, | ||
sourceMapSource: config.entryFileName + '.js', | ||
sourceMapFile: config.exportFileName + '.js', | ||
name: config.exportVarName | ||
}); | ||
gulp.task('build', ['lint-src', 'clean'], function() { | ||
// Write the generated sourcemap | ||
fs.mkdirSync(config.destinationFolder); | ||
fs.writeFileSync(path.join(config.destinationFolder, config.exportFileName + '.js'), res.map.toString()); | ||
$.file(config.exportFileName + '.js', res.code, { src: true }) | ||
.pipe($.plumber()) | ||
.pipe($.sourcemaps.init({ loadMaps: true })) | ||
.pipe($.babel({ blacklist: ['useStrict'] })) | ||
.pipe($.sourcemaps.write('./', {addComment: false})) | ||
.pipe(gulp.dest(config.destinationFolder)) | ||
.pipe($.filter(['*', '!**/*.js.map'])) | ||
.pipe($.rename(config.exportFileName + '.min.js')) | ||
.pipe($.uglifyjs({ | ||
outSourceMap: true, | ||
inSourceMap: config.destinationFolder + '/' + config.exportFileName + '.js.map', | ||
})) | ||
.pipe(gulp.dest(config.destinationFolder)) | ||
.on('end', done); | ||
}) | ||
.catch(done); | ||
}); | ||
// Use babel to build the library to CommonJS modules. This | ||
// is fed to Browserify, which builds the version of the lib | ||
// for our browser spec runner. | ||
gulp.task('compile_browser_script', function() { | ||
return gulp.src(['src/**/*.js']) | ||
// Create our output directory | ||
mkdirp.sync(destinationFolder); | ||
return gulp.src('src/**/*.js') | ||
.pipe($.plumber()) | ||
.pipe($.babel({modules: 'common'})) | ||
.pipe(gulp.dest('tmp')) | ||
.pipe($.filter([config.entryFileName + '.js'])) | ||
.pipe($.rename('__entry.js')) | ||
.pipe(gulp.dest('tmp')); | ||
.pipe($.babel({ blacklist: ['useStrict'] })) | ||
.pipe(gulp.dest(destinationFolder)); | ||
}); | ||
// Bundle our app for our unit tests | ||
gulp.task('browserify', ['compile_browser_script'], function() { | ||
var bundleStream = browserify(['./test/setup/browserify.js']).bundle(); | ||
return bundleStream | ||
.on('error', function(err){ | ||
console.log(err.message); | ||
this.emit('end'); | ||
}) | ||
function test() { | ||
return gulp.src(['test/setup/node.js', 'test/unit/**/*.js'], {read: false}) | ||
.pipe($.plumber()) | ||
.pipe(source('./tmp/__spec-build.js')) | ||
.pipe(gulp.dest('')) | ||
.pipe($.livereload()); | ||
}); | ||
.pipe($.mocha({reporter: 'dot', globals: config.mochaGlobals})); | ||
} | ||
// Make babel preprocess the scripts the user tries to import from here on. | ||
require('babel/register'); | ||
gulp.task('coverage', function(done) { | ||
require('babel/register'); | ||
gulp.src(['src/*.js']) | ||
@@ -130,27 +90,12 @@ .pipe($.plumber()) | ||
function test() { | ||
return gulp.src(['test/setup/node.js', 'test/unit/**/*.js'], {read: false}) | ||
.pipe($.plumber()) | ||
.pipe($.mocha({reporter: 'dot', globals: config.mochaGlobals})); | ||
} | ||
// Lint and run our tests | ||
gulp.task('test', ['lint:src', 'lint:test'], function() { | ||
require('babel/register')({ modules: 'common' }); | ||
return test(); | ||
}); | ||
gulp.task('test', ['lint-src', 'lint-test'], test); | ||
// Ensure that linting occurs before browserify runs. This prevents | ||
// the build from breaking due to poorly formatted code. | ||
gulp.task('build_in_sequence', function(callback) { | ||
runSequence(['lint:src', 'lint:test'], 'browserify', callback); | ||
// Run the headless unit tests as you make changes. | ||
gulp.task('watch', ['test'], function() { | ||
gulp.watch(['src/**/*', 'test/**/*', '.jshintrc', 'test/.jshintrc'], ['test']); | ||
}); | ||
// Set up a livereload environment for our spec runner | ||
gulp.task('test:browser', ['build_in_sequence'], function() { | ||
$.livereload.listen({port: 35729, host: 'localhost', start: true}); | ||
return gulp.watch(['src/**/*.js', 'test/**/*', '.jshintrc', 'test/.jshintrc', 'config/index.json'], ['build_in_sequence']); | ||
}); | ||
// An alias of test | ||
gulp.task('default', ['test']); | ||
gulp.task('default', ['test']); |
{ | ||
"name": "acorn-umd", | ||
"version": "0.0.0", | ||
"version": "0.1.0", | ||
"description": "Parse acorn ast for AMD, CommonJS, and ES6 definitions", | ||
"main": "dist/acorn-umd.js", | ||
"scripts": { | ||
"test": "gulp" | ||
"test": "gulp", | ||
"build": "gulp build", | ||
"coverage": "gulp coverage", | ||
"prepublish": "gulp build" | ||
}, | ||
@@ -14,10 +17,8 @@ "repository": { | ||
"keywords": [ | ||
"boilerplate", | ||
"es6", | ||
"node", | ||
"starter", | ||
"kit", | ||
"transpile", | ||
"6to5", | ||
"babel" | ||
"acorn", | ||
"modules", | ||
"ast", | ||
"umd", | ||
"commonjs", | ||
"amd" | ||
], | ||
@@ -31,14 +32,10 @@ "author": "Graeme Yeates <megawac@gmail.com>", | ||
"devDependencies": { | ||
"babel": "^4.7.3", | ||
"browserify": "^8.1.1", | ||
"chai": "^1.10.0", | ||
"babel": "^4.3.0", | ||
"chai": "^2.0.0", | ||
"del": "^1.1.1", | ||
"esperanto": "^0.6.7", | ||
"gulp": "^3.8.10", | ||
"gulp-babel": "^4.0.0", | ||
"gulp-file": "^0.2.0", | ||
"gulp-filter": "^2.0.0", | ||
"gulp-istanbul": "^0.6.0", | ||
"gulp-jscs": "^1.4.0", | ||
"gulp-jshint": "^1.9.0", | ||
"gulp-livereload": "^3.4.0", | ||
"gulp-load-plugins": "^0.8.0", | ||
@@ -48,18 +45,21 @@ "gulp-mocha": "^2.0.0", | ||
"gulp-plumber": "^0.6.6", | ||
"gulp-rename": "^1.2.0", | ||
"gulp-sourcemaps": "^1.3.0", | ||
"gulp-uglifyjs": "^0.5.0", | ||
"isparta": "^1.0.1", | ||
"isparta": "^2.0.0", | ||
"jshint-stylish": "^1.0.0", | ||
"mkdirp": "^0.5.0", | ||
"mocha": "^2.1.0", | ||
"run-sequence": "^1.0.2", | ||
"sinon": "^1.12.2", | ||
"sinon-chai": "^2.6.0", | ||
"vinyl-source-stream": "^1.0.0" | ||
"sinon-chai": "^2.7.0" | ||
}, | ||
"nodeBoilerplateOptions": { | ||
"mochaGlobals": [ | ||
"stub", | ||
"spy", | ||
"expect" | ||
] | ||
}, | ||
"dependencies": { | ||
"acorn": "^0.12.0", | ||
"lodash": "^3.5.0", | ||
"walkall": "deathcap/acorn-walkall#es6" | ||
"walkall": "0.0.5" | ||
} | ||
} |
@@ -6,4 +6,4 @@ # acorn-umd | ||
[![Travis build status](http://img.shields.io/travis/megawac/acorn-umd.svg?style=flat)](https://travis-ci.org/megawac/acorn-umd) | ||
[![Test Coverage](https://codeclimate.com/github/megawac/acorn-umd/badges/coverage.svg)](https://codeclimate.com/github/megawac/acorn-umd) | ||
[![Code Climate](https://codeclimate.com/github/megawac/acorn-umd/badges/gpa.svg)](https://codeclimate.com/github/megawac/acorn-umd) | ||
[![Test Coverage](https://codeclimate.com/github/megawac/acorn-umd/badges/coverage.svg)](https://codeclimate.com/github/megawac/acorn-umd) | ||
[![Dependency Status](https://david-dm.org/megawac/acorn-umd.svg)](https://david-dm.org/megawac/acorn-umd) | ||
@@ -79,4 +79,45 @@ [![devDependency Status](https://david-dm.org/megawac/acorn-umd/dev-status.svg)](https://david-dm.org//acorn-umd#info=devDependencies) | ||
# TODO | ||
# AMD Imports | ||
- AMD Support | ||
```js | ||
let code = ` | ||
foo(); | ||
define(['foo', 'unused-import'], function($) { | ||
return $(); | ||
}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
console.log(parsed); | ||
[ | ||
{ | ||
type: 'AMDImport', | ||
reference: { DEFINE_NODE }, | ||
start: 12, | ||
end: 81, | ||
specifiers: [ { type: 'Identifier', start: 54, end: 55, name: '$' } ], | ||
sources: | ||
[ { type: 'Literal', | ||
reference: [Object], | ||
value: 'foo', | ||
raw: '\'foo\'', | ||
start: 20, | ||
end: 25 }, | ||
{ type: 'Literal', | ||
reference: [Object], | ||
value: 'unused-import', | ||
raw: '\'unused-import\'', | ||
start: 27, | ||
end: 42 } ], | ||
// Grouped [source, variable] name array | ||
imports: [ [ {SOURCE_NODE}, {VARIABLE_NODE} ], [ {SOURCE_NODE}, undefined ] | ||
} | ||
] | ||
``` |
@@ -1,6 +0,6 @@ | ||
import lodash from 'lodash'; | ||
import {assign, find, filter, isMatch, matches, pluck, reject, take, zip} from 'lodash'; | ||
import walk from 'acorn/util/walk'; | ||
import walkall from 'walkall'; | ||
const isRequireCallee = lodash.matches({ | ||
const isRequireCallee = matches({ | ||
name: 'require', | ||
@@ -10,2 +10,18 @@ type: 'Identifier' | ||
const isDefineCallee = matches({ | ||
type: 'CallExpression', | ||
// calleee: { | ||
// name: 'define', | ||
// type: 'Identifier' | ||
// } | ||
}); | ||
const isArrayExpr = matches({ | ||
type: 'ArrayExpression' | ||
}); | ||
const isFuncExpr = matches({ | ||
type: 'FunctionExpression' | ||
}); | ||
// Set up an AST Node similar to an ES6 import node | ||
@@ -22,2 +38,24 @@ function constructImportNode(node, type) { | ||
function createImportSpecifier(source, isDef) { | ||
// Add the specifier | ||
let {name, type, start, end} = source; | ||
return { | ||
start, end, | ||
type: 'ImportSpecifier', | ||
id: { | ||
type, start, end, name | ||
}, | ||
default: typeof isDef === 'boolean' ? isDef : true | ||
}; | ||
} | ||
function createSourceNode(node, source) { | ||
let {value, raw, start, end} = source; | ||
return { | ||
type: 'Literal', | ||
reference: node, | ||
value, raw, start, end | ||
}; | ||
} | ||
function constructCJSImportNode(node) { | ||
@@ -38,3 +76,3 @@ let result = constructImportNode(node, 'CJSImport'); | ||
let value = declaration.init || declaration.value; | ||
if (lodash.isMatch(value, { type: 'CallExpression' })) { | ||
if (isMatch(value, { type: 'CallExpression' })) { | ||
importExpr = value; | ||
@@ -44,23 +82,7 @@ } | ||
let source = isVariable ? declaration.id : declaration.key; | ||
// Add the specifier | ||
let {name, type, start, end} = source; | ||
result.specifiers.push({ | ||
start, end, | ||
type: 'ImportSpecifier', | ||
id: { | ||
type, start, end, name | ||
}, | ||
default: isVariable | ||
}); | ||
result.specifiers.push(createImportSpecifier(source, isVariable)); | ||
} | ||
} | ||
let {value, raw, start, end} = importExpr.arguments[0]; | ||
result.source = { | ||
type: 'Literal', | ||
reference: node, | ||
value, raw, start, end | ||
}; | ||
result.source = createSourceNode(node, importExpr.arguments[0]); | ||
return result; | ||
@@ -83,3 +105,3 @@ } | ||
let value = declaration.init || declaration.value; | ||
if (lodash.isMatch(value, { type: 'CallExpression' })) { | ||
if (isMatch(value, { type: 'CallExpression' })) { | ||
expr = value; | ||
@@ -93,15 +115,54 @@ } | ||
return lodash(requires) | ||
// Filter the overlapping requires (e.g. if var x = require('./x') it'll show up twice). | ||
// Do this by just checking line #'s | ||
.reject(node => { | ||
return lodash.any(requires, parent => | ||
[node.start, node.stop].some(pos => lodash.inRange(pos, parent.start + 0.1, parent.end))); | ||
// Filter the overlapping requires (e.g. if var x = require('./x') it'll show up twice). | ||
// Do this by just checking line #'s | ||
return reject(requires, node => { | ||
return requires.some(parent => | ||
[node.start, node.stop].some(pos => pos > parent.start && pos < parent.end)); | ||
}) | ||
.map(constructCJSImportNode) | ||
.value(); | ||
.map(constructCJSImportNode); | ||
} | ||
// Note there can be more than one define per file with global registeration. | ||
function findAMD(ast) { | ||
return pluck(filter(ast.body, { | ||
type: 'ExpressionStatement' | ||
}), 'expression') | ||
.filter(isDefineCallee) | ||
// Til https://github.com/lodash/lodash/commit/f20d8f5cc05f98775969c504b081ccc1fddb54c5 | ||
.filter(node => { | ||
return isMatch(node.callee, { | ||
name: 'define', | ||
type: 'Identifier' | ||
}); | ||
}) | ||
// Ensure the define takes params and has a function | ||
.filter(node => node.arguments.length <= 3) | ||
.filter(node => filter(node.arguments, isFuncExpr).length === 1) | ||
.filter(node => filter(node.arguments, isArrayExpr).length <= 1) | ||
// Now just zip the array arguments and the provided function params | ||
.map(node => { | ||
let outnode = constructImportNode(node, 'AMDImport'); | ||
let func = find(node.arguments, isFuncExpr); | ||
let imports = find(node.arguments, isArrayExpr) || {elements: []}; | ||
let params = take(func.params, imports.elements.length); | ||
outnode.specifiers = params; | ||
if (imports) { | ||
// Use an array even though its not spec as there isn't a better way to | ||
// represent this structure | ||
outnode.sources = imports.elements.map(imp => createSourceNode(node, imp)); | ||
// Make nicer repr: [[importSrc, paramName]] | ||
outnode.imports = zip(imports.elements, params); | ||
} | ||
return outnode; | ||
}); | ||
// Now just format them up | ||
// .map(node => console.log(node)); | ||
} | ||
export default function(ast, options) { | ||
options = lodash.extend({ | ||
options = assign({ | ||
cjs: true, | ||
@@ -121,3 +182,3 @@ // TODO | ||
if (options.es6) { | ||
result.push(...lodash.filter(ast.body, { | ||
result.push(...filter(ast.body, { | ||
type: 'ImportDeclaration' | ||
@@ -127,3 +188,7 @@ })); | ||
if (options.amd) { | ||
result.push(...findAMD(ast)); | ||
} | ||
return result; | ||
} |
@@ -1,8 +0,6 @@ | ||
var setup = require('./setup'); | ||
var config = require('../../config'); | ||
global[config.exportVarName] = require('../../src/' + config.entryFileName); | ||
global.chai = require('chai'); | ||
global.sinon = require('sinon'); | ||
global.chai.use(require('sinon-chai')); | ||
setup(); | ||
require('babel/register'); | ||
require('./setup')(); |
@@ -15,2 +15,2 @@ module.exports = function() { | ||
}); | ||
} | ||
}; |
@@ -6,3 +6,3 @@ import acorn from 'acorn'; | ||
describe('Parsing AST for CommonJS imports', function() { | ||
describe('var|let|const cases', function() { | ||
@@ -182,5 +182,3 @@ let code = ` | ||
function a() { | ||
} | ||
export default function a() {} | ||
`; | ||
@@ -203,7 +201,145 @@ | ||
describe('acorn-umd', function() { | ||
it('should exist', function() { | ||
expect(acornUmd).to.exist(); | ||
}); | ||
describe('Parsing AMD define import nodes', function() { | ||
describe('common case', function() { | ||
let code = ` | ||
foo(); | ||
define(['foo', 'bar', 'twat', 'unused-import'], function(foo, bar, $) { | ||
return foo(); | ||
}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
it('AMD identifies multiple variables', function() { | ||
expect(parsed).to.have.length(1); | ||
}); | ||
it('has the correct specifiers,imports&sources', function() { | ||
let {specifiers, imports, sources} = parsed[0]; | ||
expect(imports).to.have.length(4); | ||
expect(sources).to.have.length(4); | ||
expect(specifiers).to.have.length(3); | ||
}); | ||
it('sources are zipped correctly', function() { | ||
[['foo', 'foo'], ['bar', 'bar'], ['twat', '$'], ['unused-import']].forEach((pair, i) => { | ||
let cpair = parsed[0].imports[i]; | ||
expect(cpair[0]).to.have.property('value', pair[0]); | ||
expect(cpair[0]).to.have.property('raw', `'${pair[0]}'`); | ||
if (pair.length > 1) { | ||
expect(cpair[1]).to.have.property('name', pair[1]); | ||
} else { | ||
expect(cpair[1]).to.be.undefined; | ||
} | ||
}); | ||
}); | ||
}); | ||
describe('AMD works with global declaration with imports', function() { | ||
let code = ` | ||
define(['smt'], 'global', function(smt) {return null;}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
it('has the correct length', function() { | ||
expect(parsed).to.have.length(1); | ||
}); | ||
it('has the correct specifiers,imports&sources', function() { | ||
let {specifiers, imports, sources} = parsed[0]; | ||
expect(imports).to.have.length(1); | ||
expect(sources).to.have.length(1); | ||
expect(specifiers).to.have.length(1); | ||
}); | ||
}); | ||
describe('AMD identifies no variables', function() { | ||
let code = ` | ||
define(function() {return null;}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
it('has the correct length', function() { | ||
expect(parsed).to.be.length(1); | ||
}); | ||
it('has the correct specifiers,imports&sources', function() { | ||
let {specifiers, imports, sources} = parsed[0]; | ||
expect(imports).to.be.empty; | ||
expect(sources).to.be.empty; | ||
expect(specifiers).to.be.empty; | ||
}); | ||
}); | ||
describe('AMD identifies with gllobal declaration & no variables', function() { | ||
let code = ` | ||
define('global', function() {return null;}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
it('has the correct length', function() { | ||
expect(parsed).to.be.length(1); | ||
}); | ||
it('has the correct specifiers,imports&sources', function() { | ||
let {specifiers, imports, sources} = parsed[0]; | ||
expect(imports).to.be.empty; | ||
expect(sources).to.be.empty; | ||
expect(specifiers).to.be.empty; | ||
}); | ||
}); | ||
describe('with multiple declarations in a file', function() { | ||
let code = ` | ||
define('foo', function() {return 5}); | ||
define(['foo', 'x'], 'bar', function(foo, x) { | ||
return x + foo; | ||
}); | ||
define(['bar', 'unused-import'], function(bar) { | ||
return Math.pow(bar, 2); | ||
}); | ||
`; | ||
let ast = acorn.parse(code, {ecmaVersion: 6}); | ||
let parsed = umd(ast, { | ||
es6: false, amd: true, cjs: false | ||
}); | ||
it('finds all defines with imports', function() { | ||
expect(parsed).to.have.length(3); | ||
}); | ||
it('Global no vars parsed correctly', function() { | ||
let {specifiers, imports, sources} = parsed[0]; | ||
expect(imports).to.be.empty; | ||
expect(sources).to.be.empty; | ||
expect(specifiers).to.be.empty; | ||
}); | ||
it('Global with imports parsed correctly', function() { | ||
let {specifiers, imports, sources} = parsed[1]; | ||
expect(imports).to.have.length(2); | ||
expect(sources).to.have.length(2); | ||
expect(specifiers).to.have.length(2); | ||
}); | ||
it('Anon with imports parsed correctly', function() { | ||
let {specifiers, imports, sources} = parsed[2]; | ||
expect(imports).to.have.length(2); | ||
expect(sources).to.have.length(2); | ||
expect(specifiers).to.have.length(1); | ||
}); | ||
}); | ||
}); | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
18
736
2
121
0
0
0
36275
15
+ Addedacorn@0.4.2(transitive)
+ Addedwalkall@0.0.5(transitive)
Updatedwalkall@0.0.5