diff --git a/lib/convert-to-amd-filter.js b/lib/convert-to-amd-filter.js
index da31aaf..54b2328 100644
--- a/lib/convert-to-amd-filter.js
+++ b/lib/convert-to-amd-filter.js
@@ -10,20 +10,22 @@
// limitations under the License.
/* eslint-env node */
-'use strict';
+"use strict";
-const fs = require('fs');
-const path = require('path');
-const Filter = require('broccoli-filter');
-const cheerio = require('cheerio');
-const beautify_js = require('js-beautify');
-const beautify_html = require('js-beautify').html;
-const _ = require('lodash');
+const fs = require("fs");
+const path = require("path");
+const Filter = require("broccoli-filter");
+const cheerio = require("cheerio");
+const beautify_js = require("js-beautify");
+const beautify_html = require("js-beautify").html;
+const _ = require("lodash");
-const replaceRequireAndDefine = require('./replace-require-and-define');
+const replaceRequireAndDefine = require("./replace-require-and-define");
-const amdLoadingTemplate = _.template(fs.readFileSync(path.join(__dirname, 'amd-loading.txt'), 'utf8'));
-const indexFiles = ['index.html', 'tests/index.html'];
+const amdLoadingTemplate = _.template(
+ fs.readFileSync(path.join(__dirname, "amd-loading.txt"), "utf8")
+);
+const indexFiles = ["index.html", "tests/index.html"];
// Class for replacing, in the generated code, the AMD protected keyword 'require' and 'define'.
// We are replacing these keywords by non conflicting words.
@@ -32,14 +34,16 @@ module.exports = class ConvertToAMD extends Filter {
constructor(inputTree, options = {}) {
super(inputTree, {});
- this.extensions = ['js', 'html'];
+ this.extensions = ["js", "html"];
// Options for the process
this.loader = options.amdOptions.loader;
this.amdPackages = options.amdOptions.packages || [];
this.excludePaths = options.amdOptions.excludePaths;
- this.loadingFilePath = (options.amdOptions.loadingFilePath || 'assets').replace(/\/$/, "");
- this.rootURL = options.rootURL || '';
+ this.loadingFilePath = (
+ options.amdOptions.loadingFilePath || "assets"
+ ).replace(/\/$/, "");
+ this.rootURL = options.rootURL || "";
this.inline = !!options.amdOptions.inline;
// Because the filter is call for partial rebuild during 'ember serve', we need to
@@ -52,16 +56,17 @@ module.exports = class ConvertToAMD extends Filter {
// - tests/index.html
// We need to keep things separated as they don't load the same script set.
this.indexHtmlCaches = {
- 'index.html': {
+ "index.html": {
scriptsToLoad: [],
- loadingScript: this.loadingFilePath + '/amd-loading.js',
- afterLoadingScript: this.loadingFilePath + '/after-amd-loading.js'
+ loadingScript: this.loadingFilePath + "/amd-loading.js",
+ afterLoadingScript: this.loadingFilePath + "/after-amd-loading.js",
},
- 'tests/index.html': {
+ "tests/index.html": {
scriptsToLoad: [],
- loadingScript: this.loadingFilePath + '/amd-loading-tests.js',
- afterLoadingScript: this.loadingFilePath + '/after-amd-loading-tests.js'
- }
+ loadingScript: this.loadingFilePath + "/amd-loading-tests.js",
+ afterLoadingScript:
+ this.loadingFilePath + "/after-amd-loading-tests.js",
+ },
};
}
@@ -71,7 +76,7 @@ module.exports = class ConvertToAMD extends Filter {
return null;
}
- if (relativePath.indexOf('index.html') >= 0) {
+ if (relativePath.indexOf("index.html") >= 0) {
return relativePath;
}
@@ -81,7 +86,7 @@ module.exports = class ConvertToAMD extends Filter {
}
}
- if (relativePath.indexOf('.js') >= 0) {
+ if (relativePath.indexOf(".js") >= 0) {
return relativePath;
}
@@ -89,7 +94,7 @@ module.exports = class ConvertToAMD extends Filter {
}
processString(code, relativePath) {
- if (relativePath.indexOf('.js') >= 0) {
+ if (relativePath.indexOf(".js") >= 0) {
return this._processJsFile(code, relativePath);
}
@@ -97,18 +102,17 @@ module.exports = class ConvertToAMD extends Filter {
}
_processIndexFile(code, relativePath) {
-
const cheerioQuery = cheerio.load(code);
// Get the collection of scripts
// Scripts that have a 'src' will be loaded by AMD
// Scripts that have a body will be assembled into a post loading file and loaded at the end of the AMD loading process
- const scriptElements = cheerioQuery('body > script');
+ const scriptElements = cheerioQuery("body > script");
const scriptsToLoad = [];
const inlineScripts = [];
scriptElements.each(function () {
- if (cheerioQuery(this).attr('src')) {
- scriptsToLoad.push(`"${cheerioQuery(this).attr('src')}"`);
+ if (cheerioQuery(this).attr("src")) {
+ scriptsToLoad.push(`"${cheerioQuery(this).attr("src")}"`);
} else {
inlineScripts.push(cheerioQuery(this).html());
}
@@ -119,11 +123,16 @@ module.exports = class ConvertToAMD extends Filter {
// If we have inline scripts, we will save them into a script file and load it as part of the amd loading
this.indexHtmlCaches[relativePath].afterLoadingCode = undefined;
if (inlineScripts.length > 0) {
- this.indexHtmlCaches[relativePath].afterLoadingCode = beautify_js(replaceRequireAndDefine(inlineScripts.join('\n\n')), {
- indent_size: 2,
- max_preserve_newlines: 1
- });
- scriptsToLoad.push(`"${this.rootURL}${this.indexHtmlCaches[relativePath].afterLoadingScript}"`);
+ this.indexHtmlCaches[relativePath].afterLoadingCode = beautify_js(
+ replaceRequireAndDefine(inlineScripts.join("\n\n")),
+ {
+ indent_size: 2,
+ max_preserve_newlines: 1,
+ }
+ );
+ scriptsToLoad.push(
+ `"${this.rootURL}${this.indexHtmlCaches[relativePath].afterLoadingScript}"`
+ );
}
// Replace the original ember scripts by the amd ones
@@ -132,14 +141,18 @@ module.exports = class ConvertToAMD extends Filter {
// Beautify the index.html
return beautify_html(cheerioQuery.html(), {
indent_size: 2,
- max_preserve_newlines: 0
+ max_preserve_newlines: 0,
});
}
_processJsFile(code, relativePath) {
-
const externalAmdModulesForFile = new Set();
- const modifiedSource = replaceRequireAndDefine(code, this.amdPackages, externalAmdModulesForFile);
+ const modifiedSource = replaceRequireAndDefine(
+ code,
+ this.amdPackages,
+ externalAmdModulesForFile,
+ relativePath.includes("vendor.js")
+ );
// Bookkeeping of what has changed for this file compared to previous builds
if (externalAmdModulesForFile.size === 0) {
@@ -154,7 +167,6 @@ module.exports = class ConvertToAMD extends Filter {
}
_buildModuleInfos() {
-
// Build different arrays representing the modules for the injection in the start script
const objs = [];
const names = [];
@@ -168,14 +180,13 @@ module.exports = class ConvertToAMD extends Filter {
});
return {
- names: names.join(','),
- objects: objs.join(','),
- adoptables: adoptables.join(',')
+ names: names.join(","),
+ objects: objs.join(","),
+ adoptables: adoptables.join(","),
};
}
async build() {
-
// Clear before each build since the filter is kept by ember-cli during 'ember serve'
// and being reused without going thru postProcessTree. If we don't clean we may get
// previous modules.
@@ -184,16 +195,15 @@ module.exports = class ConvertToAMD extends Filter {
const result = await super.build();
// Re-assemble the external AMD modules set with the updated cache
- this.externalAmdModulesCache.forEach(externalAmdModules => {
- externalAmdModules.forEach(externalAmdModule => {
+ this.externalAmdModulesCache.forEach((externalAmdModules) => {
+ externalAmdModules.forEach((externalAmdModule) => {
this.externalAmdModules.add(externalAmdModule);
});
});
// Write the various script files we need
const moduleInfos = this._buildModuleInfos();
- indexFiles.forEach(indexFile => {
-
+ indexFiles.forEach((indexFile) => {
const indexPath = path.join(this.outputPath, indexFile);
if (!fs.existsSync(indexPath)) {
// When building for production, tests/index.html will not exist, so we can skip its loading scripts
@@ -202,39 +212,58 @@ module.exports = class ConvertToAMD extends Filter {
// We add scripts to each index.html file to kick off the loading of amd modules.
const cheerioQuery = cheerio.load(fs.readFileSync(indexPath));
- const amdScripts = [ `` ];
- const scripts = this.indexHtmlCaches[indexFile].scriptsToLoad.join(',');
- const loadingScript = beautify_js(amdLoadingTemplate(_.assign(moduleInfos, { scripts })), {
- indent_size: 2,
- max_preserve_newlines: 1
- });
+ const amdScripts = [
+ ``,
+ ];
+ const scripts = this.indexHtmlCaches[indexFile].scriptsToLoad.join(",");
+ const loadingScript = beautify_js(
+ amdLoadingTemplate(_.assign(moduleInfos, { scripts })),
+ {
+ indent_size: 2,
+ max_preserve_newlines: 1,
+ }
+ );
if (this.inline) {
// Inline the amd-loading script directly in index.html
amdScripts.push(``);
} else {
// Add a script tag to index.html to load the amd-loading script, and write the script to the output directory
- amdScripts.push(``);
- fs.writeFileSync(path.join(this.outputPath, this.indexHtmlCaches[indexFile].loadingScript), loadingScript);
+ amdScripts.push(
+ ``
+ );
+ fs.writeFileSync(
+ path.join(
+ this.outputPath,
+ this.indexHtmlCaches[indexFile].loadingScript
+ ),
+ loadingScript
+ );
}
// After loading script
if (this.indexHtmlCaches[indexFile].afterLoadingCode) {
- fs.writeFileSync(path.join(this.outputPath, this.indexHtmlCaches[indexFile].afterLoadingScript), this.indexHtmlCaches[indexFile].afterLoadingCode);
+ fs.writeFileSync(
+ path.join(
+ this.outputPath,
+ this.indexHtmlCaches[indexFile].afterLoadingScript
+ ),
+ this.indexHtmlCaches[indexFile].afterLoadingCode
+ );
}
// Remove from body previous script tags (case for ember serve).
- cheerioQuery('body > script').remove();
-
+ cheerioQuery("body > script").remove();
+
// Write the new script tags
- cheerioQuery('body').prepend(amdScripts.join('\n'));
+ cheerioQuery("body").prepend(amdScripts.join("\n"));
const html = beautify_html(cheerioQuery.html(), {
indentSize: 2,
- max_preserve_newlines: 0
+ max_preserve_newlines: 0,
});
fs.writeFileSync(indexPath, html);
});
return result;
}
-}
+};
diff --git a/lib/replace-require-and-define.js b/lib/replace-require-and-define.js
index 03fd46e..a089a82 100644
--- a/lib/replace-require-and-define.js
+++ b/lib/replace-require-and-define.js
@@ -10,30 +10,31 @@
// limitations under the License.
/* eslint-env node */
-'use strict';
+"use strict";
-const {
- parse
-} = require('@babel/parser');
-const traverse = require('@babel/traverse').default;
-const t = require('@babel/types');
-const generator = require('@babel/generator').default;
+const { parse } = require("@babel/parser");
+const traverse = require("@babel/traverse").default;
+const t = require("@babel/types");
+const generator = require("@babel/generator").default;
// Replace indentifier
const IdentifierMap = {
- 'require': 'eriuqer',
- 'define': 'enifed'
+ require: "eriuqer",
+ define: "enifed",
};
const Identifiers = Object.keys(IdentifierMap);
-
// Use babel to parse/traverse/generate code.
// - replace define and require with enifed and eriuqer
// - if required, collect the external AMD modules referenced
-module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmdModules) {
-
+module.exports = function replaceRequireAndDefine(
+ code,
+ amdPackages,
+ externalAmdModules,
+ handleGlobalObj = false
+) {
const ast = parse(code);
traverse(ast, {
@@ -42,36 +43,39 @@ module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmd
// This will take care of the loader.js code where define and require are define globally
// The cool thing is that babel will rename all references as well
// Rename at the end so we don't overlap with the CallExpression visitor
- Identifiers.forEach(identifier => path.scope.rename(identifier, IdentifierMap[identifier]));
- }
+ Identifiers.forEach((identifier) =>
+ path.scope.rename(identifier, IdentifierMap[identifier])
+ );
+ },
},
CallExpression(path) {
-
// Looking for:
// - call for the global define function. If it matches then rename it and collect the external AMD references
// - call for the global require function. It it matches then rename it
// - eval calls: We need to process the eval code itself (used by auto-import)
- const {
- node
- } = path;
+ const { node } = path;
// Collect external AMD references
// Looking for define('foo', ['a', 'b'], function(a, b) {})
- if (amdPackages &&
+ if (
+ amdPackages &&
externalAmdModules &&
t.isIdentifier(node.callee, {
- name: 'define'
+ name: "define",
}) &&
node.arguments.length >= 2 &&
- t.isArrayExpression(node.arguments[1])) {
-
+ t.isArrayExpression(node.arguments[1])
+ ) {
node.arguments[1].elements.forEach(function (element) {
if (!t.isStringLiteral(element)) {
return;
}
const isExternalAmd = amdPackages.some(function (amdPackage) {
- return element.value.indexOf(amdPackage + '/') === 0 || element.value === amdPackage;
+ return (
+ element.value.indexOf(amdPackage + "/") === 0 ||
+ element.value === amdPackage
+ );
});
if (!isExternalAmd) {
@@ -79,41 +83,85 @@ module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmd
}
externalAmdModules.add(element.value);
-
});
}
// auto-import injects eval expression. We need to process them as individual code
- if (t.isIdentifier(node.callee, {
- name: 'eval'
- }) && t.isStringLiteral(node.arguments[0])) {
- node.arguments[0].value = replaceRequireAndDefine(node.arguments[0].value, amdPackages, externalAmdModules);
+ if (
+ t.isIdentifier(node.callee, {
+ name: "eval",
+ }) &&
+ t.isStringLiteral(node.arguments[0])
+ ) {
+ node.arguments[0].value = replaceRequireAndDefine(
+ node.arguments[0].value,
+ amdPackages,
+ externalAmdModules
+ );
}
},
+ MemberExpression(path) {
+ // Deal with ember loader changes
+ // Specifically look for globalObj.define or globalObj.require
+ // This is fragile but ember is such a mess by re-defining define and require!
+ if (!handleGlobalObj) {
+ return;
+ }
+
+ if (
+ !t.isIdentifier(path.node.object) ||
+ path.node.object.name !== "globalObj"
+ ) {
+ return;
+ }
+
+ if (
+ !t.isIdentifier(path.node.property) ||
+ !Identifiers.includes(path.node.property.name)
+ ) {
+ return;
+ }
+
+ // Rename
+ path.node.property.name = IdentifierMap[path.node.property.name];
+ },
Identifier(path) {
// Only interested by our identifiers that have no bindings in the path scope
- if (!Identifiers.includes(path.node.name) || path.scope.hasBinding(path.node.name)) {
+ if (
+ !Identifiers.includes(path.node.name) ||
+ path.scope.hasBinding(path.node.name)
+ ) {
return;
}
// Avoid: foo.define/require
- if (t.isMemberExpression(path.container) && path.container.property === path.node) {
+ if (
+ t.isMemberExpression(path.container) &&
+ path.container.property === path.node
+ ) {
return;
}
// Avoid class properties/methods
- if ((t.isClassMethod(path.container) || t.isClassProperty(path.container)) && path.container.key === path.node) {
+ if (
+ (t.isClassMethod(path.container) ||
+ t.isClassProperty(path.container)) &&
+ path.container.key === path.node
+ ) {
return;
}
// Rename
path.node.name = IdentifierMap[path.node.name];
- }
-
+ },
});
- return generator(ast, {
- retainLines: true,
- retainFunctionParens: true
- }, code).code;
-}
+ return generator(
+ ast,
+ {
+ retainLines: true,
+ retainFunctionParens: true,
+ },
+ code
+ ).code;
+};