Skip to content

Commit

Permalink
#71 Handle recent ember loader changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ffaubry committed Dec 23, 2021
1 parent 79f564d commit eac7d94
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 97 deletions.
147 changes: 88 additions & 59 deletions lib/convert-to-amd-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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",
},
};
}

Expand All @@ -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;
}

Expand All @@ -81,34 +86,33 @@ module.exports = class ConvertToAMD extends Filter {
}
}

if (relativePath.indexOf('.js') >= 0) {
if (relativePath.indexOf(".js") >= 0) {
return relativePath;
}

return null;
}

processString(code, relativePath) {
if (relativePath.indexOf('.js') >= 0) {
if (relativePath.indexOf(".js") >= 0) {
return this._processJsFile(code, relativePath);
}

return this._processIndexFile(code, relativePath);
}

_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());
}
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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 = [];
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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 = [ `<script src="${this.loader}" data-amd=true></script>` ];
const scripts = this.indexHtmlCaches[indexFile].scriptsToLoad.join(',');
const loadingScript = beautify_js(amdLoadingTemplate(_.assign(moduleInfos, { scripts })), {
indent_size: 2,
max_preserve_newlines: 1
});
const amdScripts = [
`<script src="${this.loader}" data-amd=true></script>`,
];
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(`<script>${loadingScript}</script>`);
} else {
// Add a script tag to index.html to load the amd-loading script, and write the script to the output directory
amdScripts.push(`<script src="${this.rootURL}${this.indexHtmlCaches[indexFile].loadingScript}" data-amd-loading=true></script>`);
fs.writeFileSync(path.join(this.outputPath, this.indexHtmlCaches[indexFile].loadingScript), loadingScript);
amdScripts.push(
`<script src="${this.rootURL}${this.indexHtmlCaches[indexFile].loadingScript}" data-amd-loading=true></script>`
);
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;
}
}
};
Loading

0 comments on commit eac7d94

Please sign in to comment.