From a95a4fedc7a06dc077cd986d426e87205f351e08 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Thu, 30 Jun 2022 01:01:39 -0400 Subject: [PATCH] Drive all template compilation from babel Classically, standalone templates got a dedicated preprocessor that had nothing to do with Javascript transpilation. Later came inline templates that appear inside Javascript. Those were handled as a totally separate code path from the standalone templates. To this day, there are two different code paths for handling these two cases. But at this point, templates-inside-javascript are the foundational primitive that is aligned with [where Ember is heading](https://github.com/emberjs/rfcs/pull/779), because they have access to Javascript scope and this solves a lot of problems. We can treat standalone HBS as just a degenerate kind of JS that is easily up-compiled via a pure function, allowing them to go down the Javascript code path and allowing us to drop all the old code path. This also makes it easier to support new features like [AST transforms that can manipulate Javascript scope](https://github.com/emberjs/babel-plugin-ember-template-compilation/pull/5). Embroider already [implemented this same pattern](https://github.com/embroider-build/embroider/pull/1010). --- lib/template-compiler-plugin.js | 145 ++--------- lib/utils.js | 32 --- node-tests/addon-test.js | 7 +- node-tests/ast_plugins_test.js | 247 ------------------ node-tests/template_compiler_test.js | 359 ++------------------------- node-tests/utils_test.js | 30 --- package.json | 3 +- yarn.lock | 2 +- 8 files changed, 44 insertions(+), 781 deletions(-) delete mode 100644 node-tests/ast_plugins_test.js diff --git a/lib/template-compiler-plugin.js b/lib/template-compiler-plugin.js index 4f9a58c5..dec159e1 100644 --- a/lib/template-compiler-plugin.js +++ b/lib/template-compiler-plugin.js @@ -1,48 +1,14 @@ 'use strict'; -const path = require('path'); -const utils = require('./utils'); const Filter = require('broccoli-persistent-filter'); -const crypto = require('crypto'); -const stringify = require('json-stable-stringify'); -const stripBom = require('strip-bom'); - -function rethrowBuildError(error) { - if (!error) { - throw new Error('Unknown Error'); - } - - if (typeof error === 'string') { - throw new Error('[string exception]: ' + error); - } else { - // augment with location and type information and re-throw. - error.type = 'Template Compiler Error'; - error.location = error.location && error.location.start; - - throw error; - } -} +const jsStringEscape = require('js-string-escape'); class TemplateCompiler extends Filter { - constructor(inputTree, _options, requiresModuleApiPolyfill = true) { - let options = _options || {}; - + constructor(inputTree, options = {}) { if (!('persist' in options)) { options.persist = true; } - super(inputTree, options); - - this.options = options; - this.inputTree = inputTree; - this.requiresModuleApiPolyfill = requiresModuleApiPolyfill; - - // TODO: do we need this? - this.precompile = this.options.templateCompiler.precompile; - - let { templateCompiler, EmberENV } = options; - - utils.initializeEmberENV(templateCompiler, EmberENV); } baseDir() { @@ -50,106 +16,23 @@ class TemplateCompiler extends Filter { } processString(string, relativePath) { - let srcDir = this.inputPaths[0]; - let srcName = path.join(srcDir, relativePath); - - try { - // we have to reverse these for reasons that are a bit bonkers. the initial - // version of this system used `registeredPlugin` from - // `ember-template-compiler.js` to set up these plugins (because Ember ~ 1.13 - // only had `registerPlugin`, and there was no way to pass plugins directly - // to the call to `compile`/`precompile`). calling `registerPlugin` - // unfortunately **inverted** the order of plugins (it essentially did - // `PLUGINS = [plugin, ...PLUGINS]`). - // - // sooooooo...... we are forced to maintain that **absolutely bonkers** ordering - let astPlugins = this.options.plugins ? [...this.options.plugins.ast].reverse() : []; - - let precompiled = this.options.templateCompiler.precompile(stripBom(string), { - contents: string, - isProduction: this.options.isProduction, - moduleName: relativePath, - parseOptions: { - srcName: srcName, - }, - - // intentionally not using `plugins: this.options.plugins` here - // because if we do, Ember will mutate the shared plugins object (adding - // all of the built in AST transforms into plugins.ast, which breaks - // persistent caching) - plugins: { - ast: astPlugins, - }, - }); - - if (this.options.dependencyInvalidation) { - let plugins = pluginsWithDependencies(this.options.plugins.ast); - let dependencies = []; - for (let i = 0; i < plugins.length; i++) { - let pluginDeps = plugins[i].getDependencies(relativePath); - dependencies = dependencies.concat(pluginDeps); - } - this.dependencies.setDependencies(relativePath, dependencies); - } - - if (this.requiresModuleApiPolyfill) { - return `export default Ember.HTMLBars.template(${precompiled});`; - } else { - return `import { createTemplateFactory } from '@ember/template-factory';\n\nexport default createTemplateFactory(${precompiled});`; - } - } catch (error) { - rethrowBuildError(error); + return [ + `import { hbs } from 'ember-cli-htmlbars';`, + `export default hbs('${jsStringEscape(string)}', { moduleName: '${jsStringEscape( + relativePath + )}' });`, + '', + ].join('\n'); + } + + getDestFilePath(relativePath) { + if (relativePath.endsWith('.hbs')) { + return relativePath.replace(/\.hbs$/, '.js'); } } - - _buildOptionsForHash() { - let strippedOptions = {}; - - for (let key in this.options) { - if (key !== 'templateCompiler') { - strippedOptions[key] = this.options[key]; - } - } - - strippedOptions._requiresModuleApiPolyfill = this.requiresModuleApiPolyfill; - - return strippedOptions; - } - - optionsHash() { - if (!this._optionsHash) { - let templateCompilerCacheKey = utils.getTemplateCompilerCacheKey( - this.options.templateCompilerPath - ); - - this._optionsHash = crypto - .createHash('md5') - .update(stringify(this._buildOptionsForHash()), 'utf8') - .update(templateCompilerCacheKey, 'utf8') - .digest('hex'); - } - - return this._optionsHash; - } - - cacheKeyProcessString(string, relativePath) { - return ( - this.optionsHash() + Filter.prototype.cacheKeyProcessString.call(this, string, relativePath) - ); - } } TemplateCompiler.prototype.extensions = ['hbs', 'handlebars']; TemplateCompiler.prototype.targetExtension = 'js'; -function pluginsWithDependencies(registeredPlugins) { - let found = []; - for (let i = 0; i < registeredPlugins.length; i++) { - if (registeredPlugins[i].getDependencies) { - found.push(registeredPlugins[i]); - } - } - return found; -} - module.exports = TemplateCompiler; diff --git a/lib/utils.js b/lib/utils.js index 66af4d12..aaec2526 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -190,34 +190,6 @@ function getTemplateCompiler(templateCompilerPath, EmberENV = {}) { return context.module.exports; } -function initializeEmberENV(templateCompiler, EmberENV) { - if (!templateCompiler || !EmberENV) { - return; - } - - let props; - - if (EmberENV.FEATURES) { - props = Object.keys(EmberENV.FEATURES); - - props.forEach((prop) => { - templateCompiler._Ember.FEATURES[prop] = EmberENV.FEATURES[prop]; - }); - } - - if (EmberENV) { - props = Object.keys(EmberENV); - - props.forEach((prop) => { - if (prop === 'FEATURES') { - return; - } - - templateCompiler._Ember.ENV[prop] = EmberENV[prop]; - }); - } -} - function setup(pluginInfo, options) { let projectConfig = options.projectConfig || {}; let templateCompilerPath = options.templateCompilerPath; @@ -374,13 +346,9 @@ function setupPlugins(wrappers) { module.exports = { buildOptions, - initializeEmberENV, setup, - makeCacheKey, setupPlugins, isColocatedBabelPluginRegistered, isInlinePrecompileBabelPluginRegistered, buildParalleizedBabelPlugin, - getTemplateCompiler, - getTemplateCompilerCacheKey, }; diff --git a/node-tests/addon-test.js b/node-tests/addon-test.js index de4b9a4e..5f31f0a1 100644 --- a/node-tests/addon-test.js +++ b/node-tests/addon-test.js @@ -87,8 +87,11 @@ describe('ember-cli-htmlbars addon', function () { yield output.build(); expect(output.read()).to.deep.equal({ - 'hello.js': - 'export default Ember.HTMLBars.template({"id":"pb4oG9l/","block":"[[[10,0],[12],[1,\\"Hello, World!\\"],[13]],[],false,[]]","moduleName":"hello.hbs","isStrictMode":false});', + 'hello.js': [ + `import { hbs } from 'ember-cli-htmlbars';`, + `export default hbs('
Hello, World!
', { moduleName: 'hello.hbs' });`, + '', + ].join('\n'), }); }) ); diff --git a/node-tests/ast_plugins_test.js b/node-tests/ast_plugins_test.js deleted file mode 100644 index 4dd61468..00000000 --- a/node-tests/ast_plugins_test.js +++ /dev/null @@ -1,247 +0,0 @@ -/// @ts-check -'use strict'; - -const path = require('path'); -const assert = require('assert'); -const fs = require('fs'); -const TemplateCompiler = require('../lib/template-compiler-plugin'); -const co = require('co'); -const { createTempDir, createBuilder } = require('broccoli-test-helper'); -const fixturify = require('fixturify'); -const addDependencyTracker = require('../lib/addDependencyTracker'); -const templateCompiler = require('ember-source/dist/ember-template-compiler.js'); - -describe('AST plugins', function () { - const they = it; - this.timeout(10000); - - let input, output, builder, tree, htmlbarsOptions; - - async function clearTreeCache(tree) { - if (tree && tree.processor.processor._cache) { - await tree.processor.processor._cache.clear(); - } - } - - beforeEach( - co.wrap(function* () { - rewriterCallCount = 0; - input = yield createTempDir(); - input.write(fixturify.readSync(`${__dirname}/fixtures`)); - htmlbarsOptions = { - isHTMLBars: true, - templateCompiler: templateCompiler, - templateCompilerPath: require.resolve('ember-source/dist/ember-template-compiler.js'), - }; - }) - ); - - afterEach( - co.wrap(function* () { - yield clearTreeCache(tree); - - if (builder) { - builder.cleanup(); - } - - yield input.dispose(); - - if (output) { - yield output.dispose(); - } - }) - ); - - let rewriterCallCount; - function DivRewriterImpl(env) { - let programStackDepth = 0; - let rewriter = { - name: 'test-div-rewriter', - tagNameFile: undefined, - tagName: 'span', - resetDependencies() { - rewriter.tagNameFile = undefined; - rewriter.tagName = 'span'; - }, - dependencies() { - return rewriter.tagNameFile ? [rewriter.tagNameFile] : []; - }, - visitor: { - Program: { - enter() { - programStackDepth++; - if (programStackDepth === 1) { - let sourceFile = env.meta.moduleName; - let pathInfo = sourceFile && path.parse(sourceFile); - if (pathInfo) { - if (pathInfo.base === 'template.hbs') { - rewriterCallCount++; - } - let tagNameFile = input.path(`${pathInfo.name}.tagname`); - if (fs.existsSync(tagNameFile)) { - let tagName = fs.readFileSync(tagNameFile, 'utf-8').trim(); - rewriter.tagName = tagName; - rewriter.tagNameFile = tagNameFile; - } - } - } - }, - exit() { - programStackDepth--; - }, - }, - ElementNode(node) { - if (node.tag === 'div') { - node.tag = rewriter.tagName; - } - }, - }, - }; - return rewriter; - } - const DivRewriter = addDependencyTracker(DivRewriterImpl, true); - - they( - 'are accepted and used.', - co.wrap(function* () { - htmlbarsOptions.plugins = { - ast: [DivRewriter], - }; - - tree = new TemplateCompiler(input.path(), htmlbarsOptions); - - output = createBuilder(tree); - yield output.build(); - - let templateOutput = output.readText('template.js'); - assert.ok(!templateOutput.match(/div/)); - assert.ok(templateOutput.match(/my-custom-element/)); - assert.strictEqual(rewriterCallCount, 1); - }) - ); - - they( - 'will bust the hot cache if the dependency changes.', - co.wrap(function* () { - Object.assign(htmlbarsOptions, { - plugins: { - ast: [DivRewriter], - }, - dependencyInvalidation: true, - }); - - tree = new TemplateCompiler(input.path(), htmlbarsOptions); - - output = createBuilder(tree); - yield output.build(); - - let templateOutput = output.readText('template.js'); - assert.ok(!templateOutput.match(/div/)); - assert.ok(templateOutput.match(/my-custom-element/)); - assert.strictEqual(rewriterCallCount, 1); - - // The state didn't change. the output should be cached - // and the rewriter shouldn't be invoked. - yield output.build(); - assert.deepStrictEqual(output.changes(), {}); - templateOutput = output.readText('template.js'); - assert.ok(!templateOutput.match(/div/)); - assert.ok(templateOutput.match(/my-custom-element/)); - assert.strictEqual(rewriterCallCount, 1); - - // The state changes. the cache key updates and the template - // should be recompiled. - input.write({ - 'template.tagname': 'MyChangedElement', - }); - yield output.build(); - assert.deepStrictEqual(output.changes(), { - 'template.js': 'change', - 'template.tagname': 'change', - }); - templateOutput = output.readText('template.js'); - assert.strictEqual(rewriterCallCount, 2); - assert.ok(templateOutput.match(/my-changed-element/)); - }) - ); - - describe('with persistent caching enabled', function () { - let forcePersistenceValue; - before(function () { - forcePersistenceValue = process.env.FORCE_PERSISTENCE_IN_CI; - process.env.FORCE_PERSISTENCE_IN_CI = 'true'; - }); - - after(function () { - process.env.FORCE_PERSISTENCE_IN_CI = forcePersistenceValue; - }); - - they( - 'will bust the persistent cache if the template cache key changes.', - co.wrap(function* () { - Object.assign(htmlbarsOptions, { - plugins: { - ast: [DivRewriter], - }, - dependencyInvalidation: true, - }); - - let firstTree, secondTree, thirdTree; - - try { - firstTree = new TemplateCompiler(input.path(), htmlbarsOptions); - - try { - output = createBuilder(firstTree); - yield output.build(); - - let templateOutput = output.readText('template.js'); - assert.ok(!templateOutput.match(/div/)); - assert.ok(templateOutput.match(/my-custom-element/)); - assert.strictEqual(rewriterCallCount, 1); - } finally { - yield output.dispose(); - } - - // The state didn't change. the output should be cached - // and the rewriter shouldn't be invoked. - secondTree = new TemplateCompiler(input.path(), htmlbarsOptions); - try { - let output = createBuilder(secondTree); - yield output.build(); - assert.deepStrictEqual(output.changes()['template.js'], 'create'); - // the "new" file is read from cache. - let templateOutput = output.readText('template.js'); - assert.ok(!templateOutput.match(/div/)); - assert.ok(templateOutput.match(/my-custom-element/)); - assert.strictEqual(rewriterCallCount, 1); - } finally { - yield output.dispose(); - } - - // The state changes. the cache key updates and the template - // should be recompiled. - input.write({ - 'template.tagname': 'MyChangedElement', - }); - - thirdTree = new TemplateCompiler(input.path(), htmlbarsOptions); - try { - let output = createBuilder(thirdTree); - yield output.build(); - let templateOutput = output.readText('template.js'); - assert.strictEqual(rewriterCallCount, 2); - assert.ok(templateOutput.match(/my-changed-element/)); - assert.strictEqual(rewriterCallCount, 2); - } finally { - yield output.dispose(); - } - } finally { - clearTreeCache(firstTree); - clearTreeCache(secondTree); - clearTreeCache(thirdTree); - } - }) - ); - }); -}); diff --git a/node-tests/template_compiler_test.js b/node-tests/template_compiler_test.js index 76bd4601..a853bd8f 100644 --- a/node-tests/template_compiler_test.js +++ b/node-tests/template_compiler_test.js @@ -2,355 +2,42 @@ const assert = require('assert'); const TemplateCompiler = require('../lib/template-compiler-plugin'); -const co = require('co'); const { createTempDir, createBuilder } = require('broccoli-test-helper'); const fixturify = require('fixturify'); -const { stripIndent } = require('common-tags'); +const jsStringEscape = require('js-string-escape'); describe('TemplateCompiler', function () { this.timeout(10000); let input, output, builder; - beforeEach( - co.wrap(function* () { - input = yield createTempDir(); - input.write(fixturify.readSync(`${__dirname}/fixtures`)); - }) - ); - - afterEach( - co.wrap(function* () { - if (builder) { - builder.cleanup(); - } - - yield input.dispose(); - - if (output) { - yield output.dispose(); - } - }) - ); - - let htmlbarsOptions, htmlbarsPrecompile; - - beforeEach(function () { - htmlbarsOptions = { - isHTMLBars: true, - plugins: { - ast: [], - }, - templateCompiler: require('ember-source/dist/ember-template-compiler.js'), - templateCompilerPath: require.resolve('ember-source/dist/ember-template-compiler.js'), - }; - - htmlbarsPrecompile = htmlbarsOptions.templateCompiler.precompile; + beforeEach(async function () { + input = await createTempDir(); + input.write(fixturify.readSync(`${__dirname}/fixtures`)); }); - describe('requiresModuleApiPolyfill: true', function () { - it( - 'precompiles templates into htmlbars', - co.wrap(function* () { - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('template.hbs'); - let expected = `export default Ember.HTMLBars.template(${htmlbarsPrecompile(source, { - moduleName: 'template.hbs', - })});`; - assert.strictEqual(output.readText('template.js'), expected); - }) - ); - - it('invokes AST plugins', async function () { - let source = '{{foo-bar}}'; - input.write({ - 'template.hbs': source, - }); - let plugin = (env) => { - return { - name: 'fake-ast-plugin', - - visitor: { - MustacheStatement() { - return env.syntax.builders.text('Huzzah!'); - }, - }, - }; - }; - - htmlbarsOptions.plugins.ast.push(plugin); - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - await output.build(); - - let expected = `export default Ember.HTMLBars.template(${htmlbarsPrecompile(source, { - moduleName: 'template.hbs', - plugins: { - ast: [plugin], - }, - })});`; - - let outputString = output.readText('template.js'); - assert.strictEqual(outputString, expected); - assert.ok(outputString.includes('Huzzah!')); - }); - - it('AST Plugins have access to `isProduction` status', async function () { - let source = '{{foo-bar}}'; - input.write({ - 'template.hbs': source, - }); - - let wasProduction = false; - let plugin = (env) => { - wasProduction = env.isProduction; - - return { - name: 'fake-ast-plugin', - - visitor: {}, - }; - }; - - htmlbarsOptions.isProduction = true; - htmlbarsOptions.plugins.ast.push(plugin); - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - await output.build(); - - assert.ok(wasProduction); - }); - - it( - 'ignores utf-8 byte order marks', - co.wrap(function* () { - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('template.hbs'); - let expected = `export default Ember.HTMLBars.template(${htmlbarsPrecompile(source, { - moduleName: 'template-with-bom.hbs', - })});`; - - assert.strictEqual(output.readText('template-with-bom.js'), expected); - }) - ); - - it( - 'passes FEATURES to compiler when provided as `FEATURES` [DEPRECATED]', - co.wrap(function* () { - htmlbarsOptions.FEATURES = { - 'ember-htmlbars-component-generation': true, - }; - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('web-component-template.hbs'); - let expected = `export default Ember.HTMLBars.template(${htmlbarsPrecompile(source, { - moduleName: 'web-component-template.hbs', - })});`; - - assert.strictEqual(output.readText('web-component-template.js'), expected); - }) - ); - - it( - 'passes FEATURES to compiler when provided as `EmberENV.FEATURES`', - co.wrap(function* () { - htmlbarsOptions.EmberENV = { - FEATURES: { - 'ember-htmlbars-component-generation': true, - }, - }; - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, true); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('web-component-template.hbs'); - let expected = `export default Ember.HTMLBars.template(${htmlbarsPrecompile(source, { - moduleName: 'web-component-template.hbs', - })});`; - - assert.strictEqual(output.readText('web-component-template.js'), expected); - }) - ); + afterEach(async function () { + if (builder) { + builder.cleanup(); + } + await input.dispose(); + if (output) { + await output.dispose(); + } }); - describe('requiresModuleApiPolyfill: false', function () { - it( - 'precompiles templates into htmlbars', - co.wrap(function* () { - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('template.hbs'); - let expected = stripIndent` - import { createTemplateFactory } from '@ember/template-factory'; - - export default createTemplateFactory(${htmlbarsPrecompile(source, { - moduleName: 'template.hbs', - })}); - `; - assert.strictEqual(output.readText('template.js'), expected); - }) - ); - - it('invokes AST plugins', async function () { - let source = '{{foo-bar}}'; - input.write({ - 'template.hbs': source, - }); - let plugin = (env) => { - return { - name: 'fake-ast-plugin', - - visitor: { - MustacheStatement() { - return env.syntax.builders.text('Huzzah!'); - }, - }, - }; - }; - - htmlbarsOptions.plugins.ast.push(plugin); - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - await output.build(); - - let expected = stripIndent` - import { createTemplateFactory } from '@ember/template-factory'; - - export default createTemplateFactory(${htmlbarsPrecompile(source, { - moduleName: 'template.hbs', - plugins: { - ast: [plugin], - }, - })}); - `; - - let outputString = output.readText('template.js'); - assert.strictEqual(outputString, expected); - assert.ok(outputString.includes('Huzzah!')); - }); - - it('AST Plugins have access to `isProduction` status', async function () { - let source = '{{foo-bar}}'; - input.write({ - 'template.hbs': source, - }); - - let wasProduction = false; - let plugin = (env) => { - wasProduction = env.isProduction; - - return { - name: 'fake-ast-plugin', - - visitor: {}, - }; - }; - - htmlbarsOptions.isProduction = true; - htmlbarsOptions.plugins.ast.push(plugin); - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - await output.build(); - - assert.ok(wasProduction); - }); - - it( - 'ignores utf-8 byte order marks', - co.wrap(function* () { - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('template.hbs'); - let expected = stripIndent` - import { createTemplateFactory } from '@ember/template-factory'; - - export default createTemplateFactory(${htmlbarsPrecompile(source, { - moduleName: 'template-with-bom.hbs', - })}); - `; - - assert.strictEqual(output.readText('template-with-bom.js'), expected); - }) - ); - - it( - 'passes FEATURES to compiler when provided as `FEATURES` [DEPRECATED]', - co.wrap(function* () { - htmlbarsOptions.FEATURES = { - 'ember-htmlbars-component-generation': true, - }; - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('web-component-template.hbs'); - let expected = stripIndent` - import { createTemplateFactory } from '@ember/template-factory'; - - export default createTemplateFactory(${htmlbarsPrecompile(source, { - moduleName: 'web-component-template.hbs', - })}); - `; - - assert.strictEqual(output.readText('web-component-template.js'), expected); - }) - ); - - it( - 'passes FEATURES to compiler when provided as `EmberENV.FEATURES`', - co.wrap(function* () { - htmlbarsOptions.EmberENV = { - FEATURES: { - 'ember-htmlbars-component-generation': true, - }, - }; - - let tree = new TemplateCompiler(input.path(), htmlbarsOptions, false); - - output = createBuilder(tree); - yield output.build(); - - let source = input.readText('web-component-template.hbs'); - let expected = stripIndent` - import { createTemplateFactory } from '@ember/template-factory'; + it('converts hbs into JS', async function () { + let tree = new TemplateCompiler(input.path()); - export default createTemplateFactory(${htmlbarsPrecompile(source, { - moduleName: 'web-component-template.hbs', - })}); - `; + output = createBuilder(tree); + await output.build(); - assert.strictEqual(output.readText('web-component-template.js'), expected); - }) - ); + let source = input.readText('template.hbs'); + let expected = [ + `import { hbs } from 'ember-cli-htmlbars';`, + `export default hbs('${jsStringEscape(source)}', { moduleName: 'template.hbs' });`, + '', + ].join('\n'); + assert.strictEqual(output.readText('template.js'), expected); }); }); diff --git a/node-tests/utils_test.js b/node-tests/utils_test.js index b4022454..96c3dd82 100644 --- a/node-tests/utils_test.js +++ b/node-tests/utils_test.js @@ -4,36 +4,6 @@ const utils = require('../lib/utils'); const assert = require('assert'); describe('utils', function () { - let templateCompiler; - - describe('initializeEmberENV', function () { - beforeEach(function () { - templateCompiler = require('ember-source/dist/ember-template-compiler'); - }); - - it('passes other ENV variables to compiler when provided', function () { - let EmberENV = { - FOO_BAR: true, - }; - - utils.initializeEmberENV(templateCompiler, EmberENV); - - assert.strictEqual(templateCompiler._Ember.ENV.FOO_BAR, true); - }); - - it('passes features through when provided', function () { - let EmberENV = { - FEATURES: { - BLAH: true, - }, - }; - - utils.initializeEmberENV(templateCompiler, EmberENV); - - assert.strictEqual(templateCompiler._Ember.FEATURES.BLAH, true); - }); - }); - describe('setupPlugins', function () { it('for 0 plugins', function () { let pluginWrappers = []; diff --git a/package.json b/package.json index 78a81822..4814ddf6 100644 --- a/package.json +++ b/package.json @@ -42,10 +42,9 @@ "fs-tree-diff": "^2.0.1", "hash-for-dep": "^1.5.1", "heimdalljs-logger": "^0.1.10", - "json-stable-stringify": "^1.0.1", + "js-string-escape": "^1.0.1", "semver": "^7.3.4", "silent-error": "^1.1.1", - "strip-bom": "^4.0.0", "walk-sync": "^2.2.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index d0c7f080..fa3093b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8241,7 +8241,7 @@ jquery@^3.5.1: js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" - integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0"