-
-
Notifications
You must be signed in to change notification settings - Fork 67
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 #673 from ember-cli/backport-sandbox-changes
- Loading branch information
Showing
5 changed files
with
174 additions
and
197 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
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,15 @@ | ||
'use strict'; | ||
|
||
const crypto = require('crypto'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const hashForDep = require('hash-for-dep'); | ||
const debugGenerator = require('heimdalljs-logger'); | ||
const logger = debugGenerator('ember-cli-htmlbars:utils'); | ||
const addDependencyTracker = require('./addDependencyTracker'); | ||
const vm = require('vm'); | ||
|
||
const TemplateCompilerCache = new Map(); | ||
|
||
const INLINE_PRECOMPILE_MODULES = Object.freeze({ | ||
'ember-cli-htmlbars': 'hbs', | ||
|
@@ -79,18 +83,10 @@ function buildParalleizedBabelPlugin(pluginInfo, templateCompilerPath, isProduct | |
function buildOptions(projectConfig, templateCompilerPath, pluginInfo) { | ||
let EmberENV = projectConfig.EmberENV || {}; | ||
|
||
purgeModule(templateCompilerPath); | ||
|
||
// do a full clone of the EmberENV (it is guaranteed to be structured | ||
// cloneable) to prevent ember-template-compiler.js from mutating | ||
// the shared global config | ||
let clonedEmberENV = JSON.parse(JSON.stringify(EmberENV)); | ||
global.EmberENV = clonedEmberENV; // Needed for eval time feature flag checks | ||
|
||
let htmlbarsOptions = { | ||
isHTMLBars: true, | ||
EmberENV: EmberENV, | ||
templateCompiler: require(templateCompilerPath), | ||
templateCompiler: getTemplateCompiler(templateCompilerPath, EmberENV), | ||
templateCompilerPath: templateCompilerPath, | ||
|
||
plugins: { | ||
|
@@ -102,57 +98,64 @@ function buildOptions(projectConfig, templateCompilerPath, pluginInfo) { | |
pluginCacheKey: pluginInfo.cacheKeys, | ||
}; | ||
|
||
purgeModule(templateCompilerPath); | ||
|
||
delete global.Ember; | ||
delete global.EmberENV; | ||
|
||
return htmlbarsOptions; | ||
} | ||
|
||
function purgeModule(templateCompilerPath) { | ||
// ensure we get a fresh templateCompilerModuleInstance per ember-addon | ||
// instance NOTE: this is a quick hack, and will only work as long as | ||
// templateCompilerPath is a single file bundle | ||
// | ||
// (╯°□°)╯︵ ɹǝqɯǝ | ||
// | ||
// we will also fix this in ember for future releases | ||
|
||
// Module will be cached in .parent.children as well. So deleting from require.cache alone is not sufficient. | ||
let mod = require.cache[templateCompilerPath]; | ||
if (mod && mod.parent) { | ||
let index = mod.parent.children.indexOf(mod); | ||
if (index >= 0) { | ||
mod.parent.children.splice(index, 1); | ||
} else { | ||
throw new TypeError( | ||
`ember-cli-htmlbars attempted to purge '${templateCompilerPath}' but something went wrong.` | ||
); | ||
} | ||
} | ||
function getTemplateCompiler(templateCompilerPath, EmberENV = {}) { | ||
let templateCompilerFullPath = require.resolve(templateCompilerPath); | ||
let cacheData = TemplateCompilerCache.get(templateCompilerFullPath); | ||
|
||
delete require.cache[templateCompilerPath]; | ||
} | ||
if (cacheData === undefined) { | ||
let templateCompilerContents = fs.readFileSync(templateCompilerFullPath, { encoding: 'utf-8' }); | ||
let templateCompilerCacheKey = crypto | ||
.createHash('md5') | ||
.update(templateCompilerContents) | ||
.digest('hex'); | ||
|
||
function registerPlugins(templateCompiler, plugins) { | ||
if (plugins) { | ||
for (let type in plugins) { | ||
for (let i = 0, l = plugins[type].length; i < l; i++) { | ||
templateCompiler.registerPlugin(type, plugins[type][i]); | ||
} | ||
} | ||
cacheData = { | ||
script: new vm.Script(templateCompilerContents, { | ||
filename: templateCompilerPath, | ||
}), | ||
|
||
templateCompilerCacheKey, | ||
}; | ||
|
||
TemplateCompilerCache.set(templateCompilerFullPath, cacheData); | ||
} | ||
} | ||
|
||
function unregisterPlugins(templateCompiler, plugins) { | ||
if (plugins) { | ||
for (let type in plugins) { | ||
for (let i = 0, l = plugins[type].length; i < l; i++) { | ||
templateCompiler.unregisterPlugin(type, plugins[type][i]); | ||
} | ||
} | ||
let { script } = cacheData; | ||
|
||
// do a full clone of the EmberENV (it is guaranteed to be structured | ||
// cloneable) to prevent ember-template-compiler.js from mutating | ||
// the shared global config | ||
let clonedEmberENV = JSON.parse(JSON.stringify(EmberENV)); | ||
|
||
let sandbox = { | ||
EmberENV: clonedEmberENV, | ||
|
||
// Older versions of ember-template-compiler (up until [email protected]) | ||
// eagerly access `setTimeout` without checking via `typeof` first | ||
setTimeout, | ||
clearTimeout, | ||
|
||
// fake the module into thinking we are running inside a Node context | ||
module: { require, exports: {} }, | ||
require, | ||
}; | ||
|
||
// if we are running on a Node version _without_ a globalThis | ||
// we must provide a `global` | ||
// | ||
// this is due to https://git.io/Jtb7s (Ember 3.27+) | ||
if (typeof globalThis === 'undefined') { | ||
sandbox.global = sandbox; | ||
} | ||
|
||
let context = vm.createContext(sandbox); | ||
|
||
script.runInContext(context); | ||
|
||
return context.module.exports; | ||
} | ||
|
||
function initializeEmberENV(templateCompiler, EmberENV) { | ||
|
@@ -196,11 +199,22 @@ function setup(pluginInfo, options) { | |
let htmlbarsOptions = buildOptions(projectConfig, templateCompilerPath, pluginInfo); | ||
let { templateCompiler } = htmlbarsOptions; | ||
|
||
registerPlugins(templateCompiler, { | ||
ast: pluginInfo.plugins, | ||
}); | ||
let templatePrecompile = templateCompiler.precompile; | ||
|
||
let precompile = (template, options) => { | ||
let plugins = pluginInfo.plugins || []; | ||
// concat so we ensure we don't mutate the original plugins | ||
// reverse to ensure that original AST plugin ordering is preserved | ||
let astPlugins = [].concat(plugins).reverse(); | ||
|
||
options = options || {}; | ||
options.plugins = { | ||
ast: astPlugins, | ||
}; | ||
|
||
return templatePrecompile(template, options); | ||
}; | ||
|
||
let { precompile } = templateCompiler; | ||
precompile.baseDir = () => path.resolve(__dirname, '..'); | ||
|
||
let cacheKey; | ||
|
@@ -220,10 +234,22 @@ function setup(pluginInfo, options) { | |
return plugin; | ||
} | ||
|
||
function makeCacheKey(templateCompilerPath, pluginInfo, extra) { | ||
function getTemplateCompilerCacheKey(templateCompilerPath) { | ||
let templateCompilerFullPath = require.resolve(templateCompilerPath); | ||
let templateCompilerCacheKey = fs.readFileSync(templateCompilerFullPath, { encoding: 'utf-8' }); | ||
let cacheData = TemplateCompilerCache.get(templateCompilerFullPath); | ||
|
||
if (cacheData === undefined) { | ||
getTemplateCompiler(templateCompilerFullPath); | ||
cacheData = TemplateCompilerCache.get(templateCompilerFullPath); | ||
} | ||
|
||
return cacheData.templateCompilerCacheKey; | ||
} | ||
|
||
function makeCacheKey(templateCompilerPath, pluginInfo, extra) { | ||
let templateCompilerCacheKey = getTemplateCompilerCacheKey(templateCompilerPath); | ||
let cacheItems = [templateCompilerCacheKey, extra].concat(pluginInfo.cacheKeys.sort()); | ||
|
||
// extra may be undefined | ||
return cacheItems.filter(Boolean).join('|'); | ||
} | ||
|
@@ -288,9 +314,6 @@ function setupPlugins(wrappers) { | |
|
||
module.exports = { | ||
buildOptions, | ||
purgeModule, | ||
registerPlugins, | ||
unregisterPlugins, | ||
initializeEmberENV, | ||
template, | ||
setup, | ||
|
@@ -299,4 +322,6 @@ module.exports = { | |
isColocatedBabelPluginRegistered, | ||
isInlinePrecompileBabelPluginRegistered, | ||
buildParalleizedBabelPlugin, | ||
getTemplateCompiler, | ||
getTemplateCompilerCacheKey, | ||
}; |
Oops, something went wrong.