-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from AppGeo/better-addon-support
adds robust addon support
- Loading branch information
Showing
17 changed files
with
651 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ npm-debug.log | |
*~ | ||
coverage/ | ||
/vendor/ | ||
/emberate-addons/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
language: node_js | ||
node_js: | ||
- "5.1" | ||
- "4.0" | ||
- "0.11" | ||
- "0.10" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
'use strict'; | ||
|
||
var fs = require('fs'); | ||
var path = require('path'); | ||
var readJson = require('read-json-sync'); | ||
var CombinedStream = require('combined-stream'); | ||
var spider = require('spider-stream'); | ||
|
||
module.exports = function(options) { | ||
var stream = CombinedStream.create(); | ||
var moduleList = fs.readdirSync('node_modules'); | ||
moduleList.forEach(function (moduleName) { | ||
var modulePath = path.resolve(path.join('node_modules', moduleName)); | ||
try { | ||
var data = readJson(path.join(modulePath, 'package.json')); | ||
} catch (err) { | ||
return; | ||
} | ||
if (isAddon(data)) { | ||
options.addonList.push(moduleName); | ||
var addonPath = path.resolve(path.join(modulePath, 'addon')); | ||
crawlPath(stream, addonPath); | ||
var addonAppPath = path.resolve(path.join(modulePath, 'app')); | ||
crawlPath(stream, addonAppPath); | ||
} | ||
}); | ||
return stream; | ||
} | ||
|
||
function isAddon(data) { | ||
try { | ||
return data.keywords.indexOf('ember-addon') !== -1; | ||
} catch (err) { | ||
return false; | ||
} | ||
} | ||
|
||
function crawlPath(returnStream, path) { | ||
try { | ||
fs.accessSync(path, fs.F_OK); | ||
returnStream.append(spider(path)); | ||
} catch (e) { /* noop */ } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
'use strict'; | ||
/** | ||
* Ember addons are often written in a way that is incompatible | ||
* with node's require specification. In ember-cli, the modules | ||
* are transpiled on a per file basis and wrapped in an AMD module | ||
* loader. The contents of the 'addon' directory are registered in | ||
* the module loader as `<%addon name%>/<%file type%>/<%file name%>` | ||
* and the contents of the 'app' directory are registered in the module | ||
* loader as `<%app name%>/<%file type%>/<%file name%>`. | ||
* | ||
* The addon's `app` files usually require the addon's `addon` files by | ||
* the name that they will be registered under, and not the 1 to 1 file | ||
* to module mapping that node uses for module requires. | ||
* | ||
* The require statements are expecting to be using the require defined | ||
* by Ember's module loader, so at application runtime, these paths are | ||
* correct. But for browserify and node, these paths are broken because | ||
* the files are actually located in | ||
* `<%addon name%>/<%(app|addon)%>/<%file type%>/<%file name%>` | ||
* | ||
* This, coupled with the way that browserify handles applying transforms | ||
* to files located in `node_modules`, makes the addons incompatable with | ||
* browserify and node. This function acts as a translator by copying the | ||
* addon files that are going to be included into an application directory | ||
* and transpiling the broken require statements to work with the new file | ||
* location and the 1 to 1 mapping that node expects. | ||
*/ | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var mkdirp = require('mkdirp'); | ||
|
||
var espree = require('espree'); | ||
var astring = require('astring'); | ||
|
||
var nodeFolder = path.resolve('node_modules'); | ||
module.exports = function copyAddonFile(file, options, cb) { | ||
function isAddonImport(importString) { | ||
return options.addonList.indexOf(nameSpaceFrom(importString)) !== -1; | ||
} | ||
var newFile = file.replace(nodeFolder, path.resolve(options.addonPath)); | ||
fs.readFile(file, function(err, data) { | ||
var text = data.toString(); | ||
// make sure the file is not a template | ||
if (/\.js$/.test(file)) { | ||
// parse the file for static analysis | ||
var AST = parseData(text); | ||
for (var node in AST.body) { | ||
// For now, assume all ember addons are written in es6, this will only look for 'import' statements, | ||
// and match on import statements that are trying to import addon modules | ||
if (AST.body[node].type === "ImportDeclaration" && isAddonImport(AST.body[node].source.value)) { | ||
// transpile the import statement to a node compatable format | ||
var namespace = nameSpaceFrom(AST.body[node].source.value); | ||
// For now only expect the addons to reference <%namespace%>/addon modules | ||
var namespacedImportDir = path.join(path.resolve(options.addonPath), namespace, 'addon'); | ||
// resolve the new location of the file | ||
var relativeNamespacedDir = path.relative(path.dirname(newFile), namespacedImportDir); | ||
var newImportValue = AST.body[node].source.value.replace(namespace, relativeNamespacedDir); | ||
AST.body[node].source.value = newImportValue; | ||
AST.body[node].source.raw = "'"+newImportValue+"'"; | ||
} | ||
} | ||
// recompile the file | ||
text = astring(AST, { | ||
indent: ' ' | ||
}); | ||
} | ||
mkdirp(path.dirname(newFile), function (err) { | ||
/* istanbul ignore next */ | ||
if (err) { | ||
console.log('error creating directory for file: '+newFile); | ||
console.log(err.stack); | ||
return cb(newFile); | ||
} | ||
fs.writeFile(newFile, text, function(err) { | ||
/* istanbul ignore next */ | ||
if (err) { | ||
console.log('error writing file: '+newFile); | ||
console.log(err.stack); | ||
} | ||
cb(newFile); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
function nameSpaceFrom(file) { | ||
file = file.replace(nodeFolder+'/', ''); | ||
return file.split('/')[0]; | ||
} | ||
|
||
function parseData(text) { | ||
return espree.parse(text, { | ||
ecmaVersion: 6, | ||
sourceType: "module" | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = function isAddon(path) { | ||
return /^ember-addon\:/i.test(path); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,30 @@ | ||
var path = require('path'); | ||
var rmExt = require('./rmExt'); | ||
var isAddon = require('./isAddon'); | ||
|
||
module.exports = function(name) { | ||
module.exports = function(name, options) { | ||
name = rmExt(name); | ||
var namespace = options.modulePrefix; | ||
if (isAddon(name)) { | ||
// Preserve the addon's namespace in the name if the file | ||
// is located in the 'addon' directory | ||
if (/^ember\-addon\:.+\/addon\//i.test(name)) { | ||
|
||
var basePath = 'ember-addon:'+path.resolve(options.addonPath)+'/'; | ||
name = name.replace(basePath, ''); | ||
var parts = name.split('/'); | ||
namespace = parts.shift(); | ||
// remove the 'addon' segment of the path | ||
parts.shift(); | ||
name = parts.join('/'); | ||
} else { | ||
name = name.replace(/^ember\-addon\:.+\/app\//i, ''); | ||
} | ||
} | ||
if (/^components\/.+\/template$/i.test(name)) { | ||
name = 'templates/'+name.replace(/\/template$/i, ''); | ||
} else if (/^components\/.+\/component$/i.test(name)) { | ||
name = name.replace(/\/component$/i, ''); | ||
} | ||
return name; | ||
return namespace+'/'+name; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,14 @@ | ||
var rmExt = require('./rmExt'); | ||
var fixPathSep = require('./fixPathSep'); | ||
var path = require('path'); | ||
var isAddon = require('./isAddon'); | ||
|
||
module.exports = function(pathName) { | ||
module.exports = function(pathName, options) { | ||
pathName = /\.hbs$/i.test(pathName) ? fixPathSep(pathName) : rmExt(pathName); | ||
return './' + pathName; | ||
if (isAddon(pathName)) { | ||
pathName = pathName.replace('ember-addon:', ''); | ||
return path.relative(path.resolve(options.rootPath), pathName); | ||
} else { | ||
return './' + pathName; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.