From e3b0ba7b1e0b60f0d62421306dac90e3a41308c4 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 15 Nov 2023 14:29:52 -0800 Subject: [PATCH] Use magic-string to generate correct sourcemaps for colocated templates --- lib/colocated-broccoli-plugin.js | 22 ++- node-tests/colocated-broccoli-plugin-test.js | 139 ++++++++++++------- package.json | 1 + yarn.lock | 12 ++ 4 files changed, 124 insertions(+), 50 deletions(-) diff --git a/lib/colocated-broccoli-plugin.js b/lib/colocated-broccoli-plugin.js index 2d740cf6..042214a4 100644 --- a/lib/colocated-broccoli-plugin.js +++ b/lib/colocated-broccoli-plugin.js @@ -6,6 +6,7 @@ const walkSync = require('walk-sync'); const Plugin = require('broccoli-plugin'); const logger = require('heimdalljs-logger')('ember-cli-htmlbars:colocated-broccoli-plugin'); const FSTree = require('fs-tree-diff'); +const MagicString = require('magic-string'); module.exports = class ColocatedTemplateProcessor extends Plugin { constructor(tree) { @@ -165,10 +166,27 @@ module.exports = class ColocatedTemplateProcessor extends Plugin { jsContents = `import templateOnly from '@ember/component/template-only';\n\nexport default templateOnly();\n`; } - jsContents = prefix + jsContents; - let jsOutputPath = path.join(this.outputPath, backingClassPath); + if (prefix.length > 0) { + let jsContentsMagic = new MagicString(jsContents); + + jsContentsMagic.prepend(prefix); + + jsContents = jsContentsMagic.toString(); + + // It's unclear how to format the comment correctly for coffeescript. + if (!backingClassPath.endsWith('.coffee')) { + let jsContentsMap = jsContentsMagic.generateMap({ + source: backingClassPath, + includeContent: true, + hires: true, + }); + + jsContents += `\n//# sourceMappingURL=${jsContentsMap.toUrl()}`; + } + } + switch (method) { case 'unlink': { if (filePathParts.ext === '.hbs' && hasBackingClass) { diff --git a/node-tests/colocated-broccoli-plugin-test.js b/node-tests/colocated-broccoli-plugin-test.js index afbb826e..8c3129c2 100644 --- a/node-tests/colocated-broccoli-plugin-test.js +++ b/node-tests/colocated-broccoli-plugin-test.js @@ -5,6 +5,31 @@ const ColocatedTemplateCompiler = require('../lib/colocated-broccoli-plugin'); const { createTempDir, createBuilder } = require('broccoli-test-helper'); const { stripIndent } = require('common-tags'); +function stripSourceMaps(input) { + let output = {}; + for (let key in input) { + if (key === 'components') { + output[key] = {}; + for (let component in input[key]) { + if (typeof input[key][component] === 'string') { + output[key][component] = input[key][component].replace( + /(\/\/# sourceMappingURL=data:application\/json;charset=utf-8;base64,).+(\n|$)/, + '$1{{SOURCEMAP}}' + ); + } else { + output[key][component] = input[key][component]; + } + } + } else if (typeof input[key] === 'object') { + output[key] = stripSourceMaps(input[key]); + } else { + output[key] = input[key]; + } + } + + return output; +} + describe('ColocatedTemplateCompiler', function () { this.timeout(10000); @@ -40,17 +65,18 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', @@ -100,7 +126,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { @@ -110,6 +136,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -155,7 +182,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { @@ -260,7 +287,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { components: { 'foo.ts': stripIndent` @@ -269,6 +296,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -303,7 +331,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { components: { 'foo.coffee': stripIndent` @@ -343,17 +371,18 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { '@scope-name': { 'addon-name-here': { components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"@scope-name/addon-name-here/components/foo.hbs","parseOptions":{"srcName":"@scope-name/addon-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', @@ -391,7 +420,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { '@scope-name': { 'addon-name-here': { components: { @@ -401,6 +430,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -439,7 +469,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), input.read()); + assert.deepStrictEqual(stripSourceMaps(output.read()), input.read()); await output.build(); @@ -462,7 +492,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), input.read()); + assert.deepStrictEqual(stripSourceMaps(output.read()), input.read()); await output.build(); @@ -477,7 +507,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), {}); + assert.deepStrictEqual(stripSourceMaps(output.read()), {}); await output.build(); @@ -508,7 +538,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { components: { 'foo.js': stripIndent` @@ -548,7 +578,7 @@ describe('ColocatedTemplateCompiler', function () { output = createBuilder(tree); await output.build(); - assert.deepStrictEqual(output.read(), { + assert.deepStrictEqual(stripSourceMaps(output.read()), { 'app-name-here': { components: { 'foo.js': stripIndent` @@ -585,18 +615,19 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', @@ -631,7 +662,7 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -642,6 +673,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -676,7 +708,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -716,7 +748,7 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -727,6 +759,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -761,7 +794,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -801,7 +834,7 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -834,18 +867,19 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', @@ -895,18 +929,20 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} + `, }, templates: { 'application.hbs': '{{outlet}}', @@ -937,18 +973,19 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("whoops!", {"contents":"whoops!","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', @@ -983,7 +1020,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -994,6 +1031,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -1025,7 +1063,7 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -1036,6 +1074,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -1071,7 +1110,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -1082,6 +1121,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -1111,7 +1151,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -1122,6 +1162,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooBarComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -1163,7 +1204,7 @@ describe('ColocatedTemplateCompiler', function () { await output.build(); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', @@ -1174,6 +1215,7 @@ describe('ColocatedTemplateCompiler', function () { import Component from '@glimmer/component'; export default class FooComponent extends Component {} + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}} `, }, templates: { @@ -1205,18 +1247,19 @@ describe('ColocatedTemplateCompiler', function () { ); assert.deepStrictEqual( - output.read(), + stripSourceMaps(output.read()), { 'app-name-here': { 'router.js': '// stuff here', components: { - 'foo.js': - stripIndent` + 'foo.js': stripIndent` import { hbs } from 'ember-cli-htmlbars'; const __COLOCATED_TEMPLATE__ = hbs("{{yield}}", {"contents":"{{yield}}","moduleName":"app-name-here/components/foo.hbs","parseOptions":{"srcName":"app-name-here/components/foo.hbs"}}); import templateOnly from '@ember/component/template-only'; - export default templateOnly();` + '\n', + export default templateOnly(); + + //# sourceMappingURL=data:application/json;charset=utf-8;base64,{{SOURCEMAP}}`, }, templates: { 'application.hbs': '{{outlet}}', diff --git a/package.json b/package.json index 7ef8da40..9a2592b5 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "hash-for-dep": "^1.5.1", "heimdalljs-logger": "^0.1.10", "js-string-escape": "^1.0.1", + "magic-string": "^0.30.5", "semver": "^7.3.4", "silent-error": "^1.1.1", "walk-sync": "^2.2.0" diff --git a/yarn.lock b/yarn.lock index 1ad16393..fe5356f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2341,6 +2341,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@^0.3.9": version "0.3.15" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" @@ -10698,6 +10703,13 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +magic-string@^0.30.5: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"