Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions lib/imports-for.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
var acorn = require('acorn');

module.exports = importsFor;

function importsFor(src, fullPath) {
var result = parse(src);
// In host applications the source is already ES5 code.
// In addons the source is ES6 code.

// First, try to parse as es5 code. Es6 code will return an error.
var result = tryCatch(parseEs5, src);

// If a syntax error is thrown, we assume this is because src is es6 code.
if (result instanceof Error) {
result = tryCatch(parseEs6, src);
}

// If result is still an error, there must have been a parse error.
if (result instanceof Error) {
throw new Error('Error parsing code while looking for "npm:" imports: ' + result.stack || result + ' in file: ' + fullPath);
}
Expand All @@ -28,7 +39,7 @@ function head(array) {
return array[0];
}

function parse(src) {
function parseEs5(src) {
var imports = {};

var ast = acorn.parse(src, { locations: true });
Expand All @@ -44,5 +55,39 @@ function parse(src) {
});
}
});

return imports;
}

function parseEs6(src) {
var imports = {};

var ast = acorn.parse(src, {
ecmaVersion: 6,
sourceType: 'module',
locations: true
});

forEachNode(ast, function(entry) {
if (entry.type === 'ImportDeclaration') {
var source = entry.source.value;
if (source.slice(0,4) === 'npm:') {
if (entry.kind === 'named') {
throw new Error("ember-browserify doesn't support named imports (you tried to import " + entry.specifiers[0].id.name + " from " + source);
}
imports[source.slice(4)] = entry.source.loc;
}
}
});

return imports;
}

function tryCatch(func, arg) {
try {
return func.call(null, arg);
}
catch(e) {
return e;
}
}
145 changes: 91 additions & 54 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

// Support old versions of Ember CLI.
// Nearest thing which provides `.import``
function findHost(current) {
var app;

Expand All @@ -15,12 +16,92 @@ function findHost(current) {
return app;
}

// The root host.
function findRoot(current) {
var app;

// Keep iterating upward until we don't have a grandparent.
// Has to do this grandparent check because at some point we hit the project.
// Stop at lazy engine boundaries.
do {
app = current.app || app;
} while (current.parent && current.parent.parent && (current = current.parent));

return app;
}

// The thing which browserify should run on.
function findTarget(current) {
// If we are the project, or the project's child.
if (!current.parent || !current.parent.parent) {
return current.app;
} else {
return current.parent;
}
}

var Funnel = require('broccoli-funnel');

function getPreprocessor(instance) {
return {
name: 'ember-browserify',
ext: 'js',
toTree: function(tree) {
var type = 'js';
var root = findRoot(instance);
var target = findTarget(instance);

var outputFile, options;

// TODO: Sort out tests.
if (type === 'js'){
outputFile = 'browserify/browserify.js';
} else if (type === 'test'){
outputFile = 'browserify-tests/browserify.js';
}

if (outputFile) {
var StubGenerator = require('./stub-generator');
var CachingBrowserify = require('./caching-browserify');
var MergeTrees = require('broccoli-merge-trees');

options = Object.create(instance.options);
options.outputFile = outputFile;
options.basedir = target.root;

// produces:
// - browserify_stubs.js (for CachingBrowserify, to build a bundle);
// - any inputFiles that had npm imports
var stubs = new StubGenerator(tree, options);

tree = new MergeTrees([
// original files
tree,

// copies rewritten inputFiles over (overwriting original files)
new Funnel(stubs, { exclude: ['browserify_stubs.js'] }),

// produces browserify bundle, named options.outputFile (defaulting to browserify/browserify.js)
new CachingBrowserify(stubs, options)
], { overwrite: true });
}

return tree;
}
};
}

module.exports = {
name: 'ember-browserify',

included: function(app) {
included: function() {
var host = findHost(this);
var root = findRoot(this);
var target = findTarget(this);
var project = root.project;

target.registry.add('js', getPreprocessor(this), ['js']);

var VersionChecker = require('ember-cli-version-checker');

var checker = new VersionChecker(this);
Expand All @@ -29,71 +110,27 @@ module.exports = {
if (emberCliVersion.satisfies('< 2.0.0')) {
throw new TypeError('ember-browserify@^2.0.0 no longer supports ember-cli versions less then 2.0.0.');
}
this.that = app;

app = findHost(this);

var enableSourcemaps = app.options.sourcemaps && app.options.sourcemaps.enabled && app.options.sourcemaps.extensions.indexOf('js') > -1;

this.app = app;
var enableSourcemaps = root.options.sourcemaps && root.options.sourcemaps.enabled && root.options.sourcemaps.extensions.indexOf('js') > -1;

this.options = {
root: this.app.project.root,
browserifyOptions: app.project.config(app.env).browserify || {},
root: project.root,
browserifyOptions: project.config(root.env).browserify || {},
enableSourcemap: enableSourcemaps,
fullPaths: app.env !== 'production'
fullPaths: root.env !== 'production'
};

app.import('browserify/browserify.js');
if (app.tests && (process.env.BROWSERIFY_TESTS || this.options.browserifyOptions.tests)) {
app.import('browserify-tests/browserify.js', {
host.import('browserify/browserify.js');
if (host.tests && (process.env.BROWSERIFY_TESTS || this.options.browserifyOptions.tests)) {
host.import('browserify-tests/browserify.js', {
type: 'test'
});
}

if (app.importWhitelistFilters) {
app.importWhitelistFilters.push(function(moduleName) {
if (host.importWhitelistFilters) {
host.importWhitelistFilters.push(function(moduleName) {
return moduleName.slice(0,4) === 'npm:';
});
}
},

postprocessTree: function(type, tree) {
console.log('OMG', type, this.name, this.app.name, this.that.name);
var outputFile, options;

if (type === 'js'){
outputFile = 'browserify/browserify.js';
} else if (type === 'test'){
outputFile = 'browserify-tests/browserify.js';
}

if (outputFile) {
var StubGenerator = require('./stub-generator');
var CachingBrowserify = require('./caching-browserify');
var MergeTrees = require('broccoli-merge-trees');

options = Object.create(this.options);
options.outputFile = outputFile;
options.basedir = this.app.root;

// produces:
// - browserify_stubs.js (for CachingBrowserify, to build a bundle);
// - any inputFiles that had npm imports
var stubs = new StubGenerator(tree, options);

tree = new MergeTrees([
// original files
tree,

// copies rewritten inputFiles over (overwriting original files)
new Funnel(stubs, { exclude: ['browserify_stubs.js'] }),

// produces browserify bundle, named options.outputFile (defaulting to browserify/browserify.js)
new CachingBrowserify(stubs, options)
], { overwrite: true });
}

return tree;
}
};
2 changes: 1 addition & 1 deletion lib/stub-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ StubGenerator.prototype.build = function() {
if (!/\.js$/.test(path)) { break; }

var content = fs.readFileSync(fullInputPath, 'UTF8');
var imports = importsFor(content);
var imports = importsFor(content, fullInputPath);

Object.keys(imports).forEach(function(name) {
imports[name].version = versionForModuleName(name, this.basedir);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"symlink-or-copy": "^1.1.6"
},
"dependencies": {
"acorn": "^2.6.4",
"acorn": "^4.0.0",
"broccoli-caching-writer": "^3.0.3",
"broccoli-funnel": "^1.0.6",
"broccoli-kitchen-sink-helpers": "^0.3.1",
Expand Down
5 changes: 2 additions & 3 deletions tests/dummy/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import Ember from 'ember';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
import acorn from 'npm:acorn';

let App;

Ember.MODEL_FACTORY_INJECTIONS = true;

App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver,
acorn // just add this here, so hinting says happy
Resolver
});

loadInitializers(App, config.modulePrefix);
Expand Down