@@ -7,6 +7,9 @@ const hashForDep = require('hash-for-dep');
77const debugGenerator = require ( 'heimdalljs-logger' ) ;
88const logger = debugGenerator ( 'ember-cli-htmlbars:utils' ) ;
99const addDependencyTracker = require ( './addDependencyTracker' ) ;
10+ const vm = require ( 'vm' ) ;
11+
12+ const TemplateCompilerCache = new Map ( ) ;
1013
1114const INLINE_PRECOMPILE_MODULES = Object . freeze ( {
1215 'ember-cli-htmlbars' : 'hbs' ,
@@ -92,18 +95,10 @@ function buildParalleizedBabelPlugin(
9295function buildOptions ( projectConfig , templateCompilerPath , pluginInfo ) {
9396 let EmberENV = projectConfig . EmberENV || { } ;
9497
95- purgeModule ( templateCompilerPath ) ;
96-
97- // do a full clone of the EmberENV (it is guaranteed to be structured
98- // cloneable) to prevent ember-template-compiler.js from mutating
99- // the shared global config
100- let clonedEmberENV = JSON . parse ( JSON . stringify ( EmberENV ) ) ;
101- global . EmberENV = clonedEmberENV ; // Needed for eval time feature flag checks
102-
10398 let htmlbarsOptions = {
10499 isHTMLBars : true ,
105100 EmberENV : EmberENV ,
106- templateCompiler : require ( templateCompilerPath ) ,
101+ templateCompiler : getTemplateCompiler ( templateCompilerPath , EmberENV ) ,
107102 templateCompilerPath : templateCompilerPath ,
108103
109104 pluginNames : pluginInfo . pluginNames ,
@@ -116,37 +111,47 @@ function buildOptions(projectConfig, templateCompilerPath, pluginInfo) {
116111 pluginCacheKey : pluginInfo . cacheKeys ,
117112 } ;
118113
119- purgeModule ( templateCompilerPath ) ;
120-
121- delete global . Ember ;
122- delete global . EmberENV ;
123-
124114 return htmlbarsOptions ;
125115}
126116
127- function purgeModule ( templateCompilerPath ) {
128- // ensure we get a fresh templateCompilerModuleInstance per ember-addon
129- // instance NOTE: this is a quick hack, and will only work as long as
130- // templateCompilerPath is a single file bundle
131- //
132- // (╯°□°)╯︵ ɹǝqɯǝ
133- //
134- // we will also fix this in ember for future releases
135-
136- // Module will be cached in .parent.children as well. So deleting from require.cache alone is not sufficient.
137- let mod = require . cache [ templateCompilerPath ] ;
138- if ( mod && mod . parent ) {
139- let index = mod . parent . children . indexOf ( mod ) ;
140- if ( index >= 0 ) {
141- mod . parent . children . splice ( index , 1 ) ;
142- } else {
143- throw new TypeError (
144- `ember-cli-htmlbars attempted to purge ' ${ templateCompilerPath } ' but something went wrong.`
145- ) ;
146- }
117+ function getTemplateCompiler ( templateCompilerPath , EmberENV = { } ) {
118+ let templateCompilerFullPath = require . resolve ( templateCompilerPath ) ;
119+ let cacheData = TemplateCompilerCache . get ( templateCompilerFullPath ) ;
120+
121+ if ( cacheData === undefined ) {
122+ let templateCompilerContents = fs . readFileSync ( templateCompilerFullPath , { encoding : 'utf-8' } ) ;
123+ let templateCompilerCacheKey = crypto
124+ . createHash ( 'md5' )
125+ . update ( templateCompilerContents )
126+ . digest ( 'hex' ) ;
127+
128+ cacheData = {
129+ script : new vm . Script ( templateCompilerContents , {
130+ filename : templateCompilerPath ,
131+ } ) ,
132+
133+ templateCompilerCacheKey ,
134+ } ;
135+
136+ TemplateCompilerCache . set ( templateCompilerFullPath , cacheData ) ;
147137 }
148138
149- delete require . cache [ templateCompilerPath ] ;
139+ let { script } = cacheData ;
140+
141+ // do a full clone of the EmberENV (it is guaranteed to be structured
142+ // cloneable) to prevent ember-template-compiler.js from mutating
143+ // the shared global config
144+ let clonedEmberENV = JSON . parse ( JSON . stringify ( EmberENV ) ) ;
145+
146+ let context = vm . createContext ( {
147+ EmberENV : clonedEmberENV ,
148+ module : { require, exports : { } } ,
149+ require,
150+ } ) ;
151+
152+ script . runInContext ( context ) ;
153+
154+ return context . module . exports ;
150155}
151156
152157function registerPlugins ( templateCompiler , plugins ) {
@@ -259,11 +264,10 @@ function setup(pluginInfo, options) {
259264
260265function makeCacheKey ( templateCompilerPath , pluginInfo , extra ) {
261266 let templateCompilerFullPath = require . resolve ( templateCompilerPath ) ;
262- let templateCompilerCacheKey = crypto
263- . createHash ( 'md5' )
264- . update ( fs . readFileSync ( templateCompilerFullPath , { encoding : 'utf-8' } ) )
265- . digest ( 'hex' ) ;
267+ let { templateCompilerCacheKey } = TemplateCompilerCache . get ( templateCompilerFullPath ) ;
268+
266269 let cacheItems = [ templateCompilerCacheKey , extra ] . concat ( pluginInfo . cacheKeys . sort ( ) ) ;
270+
267271 // extra may be undefined
268272 return cacheItems . filter ( Boolean ) . join ( '|' ) ;
269273}
@@ -332,7 +336,6 @@ function setupPlugins(wrappers) {
332336
333337module . exports = {
334338 buildOptions,
335- purgeModule,
336339 registerPlugins,
337340 unregisterPlugins,
338341 initializeEmberENV,
@@ -343,4 +346,5 @@ module.exports = {
343346 isColocatedBabelPluginRegistered,
344347 isInlinePrecompileBabelPluginRegistered,
345348 buildParalleizedBabelPlugin,
349+ getTemplateCompiler,
346350} ;
0 commit comments