If you're using webpack with Ember, you may want to configure the devtool option to have higher fidelity sourcemaps. Note though that on bigger projects (and I mean more than a few dozens of MB of JS), enabling the source-map option can increase build time by up to 40% (from at least what I've seen).

If you're using: ember-auto-import:

// ember-cli-build.js
let app = new EmberApp(defaults, {
  autoImport: {
    webpack: {
      devtool: 'source-map',
    }
  }
  /* ... */
});

embroider:

// ember-cli-build.js
return require('@embroider/compat').compatBuild(app, Webpack, {
  /* ... */
  packagerOptions: {
    webpackConfig: {
      devtool: 'source-map',
      /* ... */
    },
  },
});

A simple component

with the defaults

default devtool option

The code from the screenshot

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   Header: () => (/* binding */ Header)
/* harmony export */ });
/* harmony import */ var _header_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./header.css */ "./components/header.css");
/* harmony import */ var ember_primitives__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ember-primitives */ "../../.pnpm/[email protected]_@[email protected]_@[email protected]_@[email protected]_2udfmchmj4htznuxtzf4ynvf4y/node_modules/ember-primitives/dist/components/external-link.js");
/* harmony import */ var _icons_fa_external_link__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons/fa/external-link */ "./components/icons/fa/external-link.ts");
/* harmony import */ var _universal_ember_preem__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @universal-ember/preem */ "../../.pnpm/@[email protected]_@[email protected]_@[email protected]_@glimmer+component@_orczibosv232nhi52ufgg3bjky/node_modules/@universal-ember/preem/dist/index.js");
/* harmony import */ var _ember_template_factory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @ember/template-factory */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/template-factory/index.js");
/* harmony import */ var _ember_component__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @ember/component */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/component/index.js");
/* harmony import */ var _ember_component_template_only__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @ember/component/template-only */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/component/template-only.js");







const Header = (0,_ember_component__WEBPACK_IMPORTED_MODULE_4__.setComponentTemplate)((0,_ember_template_factory__WEBPACK_IMPORTED_MODULE_3__.createTemplateFactory)(
/*

  <header>
    <div>
      <ExternalLink class="github" href="https://github.com/NullVoxPopuli/package-majors">
        <img alt="" src="/images/github-logo.png" />
        GitHub
        <Arrow />
      </ExternalLink>
    </div>

    <div>
      <ThemeToggle />
    </div>
  </header>

*/
{
  "id": "YMACeO+O",
  "block": "[[[1,\"\\n  \"],[10,\"header\"],[12],[1,\"\\n    \"],[10,0],[12],[1,\"\\n      \"],[8,[32,0],[[24,0,\"github\"],[24,6,\"https://github.com/NullVoxPopuli/package-majors\"]],null,[[\"default\"],[[[[1,\"\\n        \"],[10,\"img\"],[14,\"alt\",\"\"],[14,\"src\",\"/images/github-logo.png\"],[12],[13],[1,\"\\n        GitHub\\n        \"],[8,[32,1],null,null,null],[1,\"\\n      \"]],[]]]]],[1,\"\\n    \"],[13],[1,\"\\n\\n    \"],[10,0],[12],[1,\"\\n      \"],[8,[32,2],null,null,null],[1,\"\\n    \"],[13],[1,\"\\n  \"],[13],[1,\"\\n\"]],[],false,[]]",
  "moduleName": "/home/nvp/Development/NullVoxPopuli/package-majors/node_modules/.embroider/rewritten-app/components/header.ts",
  "scope": () => [ember_primitives__WEBPACK_IMPORTED_MODULE_6__.ExternalLink, _icons_fa_external_link__WEBPACK_IMPORTED_MODULE_1__.Arrow, _universal_ember_preem__WEBPACK_IMPORTED_MODULE_2__.ThemeToggle],
  "isStrictMode": true
}), (0,_ember_component_template_only__WEBPACK_IMPORTED_MODULE_5__["default"])());

//# sourceURL=webpack://package-majors/./components/header.ts?

with devtool: 'source-map'

setting devtool to 'source-map' shows nearly original source

The code from the screenshot

import { template } from "@ember/template-compiler";
import './header.css';
import { ThemeToggle } from '@universal-ember/preem';
import { ExternalLink } from 'ember-primitives';
import { Arrow } from './icons/fa/external-link';
export const Header = template(`
  <header>
    <div>
      <ExternalLink class="github" href="https://github.com/NullVoxPopuli/package-majors">
        <img alt="" src="/images/github-logo.png" />
        GitHub
        <Arrow />
      </ExternalLink>
    </div>

    <div>
      <ThemeToggle />
    </div>
  </header>
`, {
    eval () {
        return eval(arguments[0]);
    }
});

Debugging

Using devtool: 'source-map' also enables in-browser breakpoints that more closely match the code in the source. With gjs/gts it's not original source, as browsers don't know what gjs/gts is, but it's "pretty close".

with the defaults

With the default, a bunch of paths end up emitted in the source.

example with devtool set to the default paused on line 66, showing a bunch of paths exposed in the code

The code from the screenshot

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   Search: () => (/* binding */ Search)
/* harmony export */ });
/* harmony import */ var _glimmer_component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @glimmer/component */ "../rewritten-packages/@glimmer/component.dac0f4a2/node_modules/@glimmer/component/index.js");
/* harmony import */ var _ember_service__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @ember/service */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/service/index.js");
/* harmony import */ var ember_primitives__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ember-primitives */ "../../.pnpm/[email protected]_@[email protected]_@[email protected]_@[email protected]_2udfmchmj4htznuxtzf4ynvf4y/node_modules/ember-primitives/dist/components/form.js");
/* harmony import */ var _ember_helper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @ember/helper */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/helper/index.js");
/* harmony import */ var _name_input__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./name-input */ "./components/name-input.ts");
/* harmony import */ var _show_minors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./show-minors */ "./components/show-minors.ts");
/* harmony import */ var _show_old__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./show-old */ "./components/show-old.ts");
/* harmony import */ var _ember_template_factory__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @ember/template-factory */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/template-factory/index.js");
/* harmony import */ var _ember_component__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @ember/component */ "../rewritten-packages/ember-source.848c2a54/node_modules/ember-source/@ember/component/index.js");
/* harmony import */ var _home_nvp_Development_NullVoxPopuli_package_majors_node_modules_pnpm_decorator_transforms_1_0_1_babel_core_7_23_3_node_modules_decorator_transforms_dist_runtime_cjs__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../.pnpm/[email protected]_@[email protected]/node_modules/decorator-transforms/dist/runtime.cjs */ "../../.pnpm/[email protected]_@[email protected]/node_modules/decorator-transforms/dist/runtime.cjs");
/* harmony import */ var _home_nvp_Development_NullVoxPopuli_package_majors_node_modules_pnpm_decorator_transforms_1_0_1_babel_core_7_23_3_node_modules_decorator_transforms_dist_runtime_cjs__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(_home_nvp_Development_NullVoxPopuli_package_majors_node_modules_pnpm_decorator_transforms_1_0_1_babel_core_7_23_3_node_modules_decorator_transforms_dist_runtime_cjs__WEBPACK_IMPORTED_MODULE_8__);










/**
 * This is triggered on every value change,
 * which we don't need for this app.
 * The early return makes it a normal submit,
 * so the <Form> abstraction is basically just doing the
 * `FormData` conversion for us.
 */
function handleSubmit(onChange1, data1, eventType1) {
  if (eventType1 !== 'submit') return;
  onChange1(data1);
}
class Search extends _glimmer_component__WEBPACK_IMPORTED_MODULE_0__["default"] {
  static {
    (0,_ember_component__WEBPACK_IMPORTED_MODULE_7__.setComponentTemplate)((0,_ember_template_factory__WEBPACK_IMPORTED_MODULE_6__.createTemplateFactory)(
    /*

        <Form @onChange={{fn handleSubmit this.updateSearch}}>
          <NameInput @value={{this.last.packages}} />

          <ShowMinors checked={{this.last.minors}} />
          <ShowOld checked={{this.last.old}} />
        </Form>

    */
    {
      "id": "q4ddJ3hO",
      "block": "[[[1,\"\\n    \"],[8,[32,0],null,[[\"@onChange\"],[[28,[32,1],[[32,2],[30,0,[\"updateSearch\"]]],null]]],[[\"default\"],[[[[1,\"\\n      \"],[8,[32,3],null,[[\"@value\"],[[30,0,[\"last\",\"packages\"]]]],null],[1,\"\\n\\n      \"],[8,[32,4],[[16,\"checked\",[30,0,[\"last\",\"minors\"]]]],null,null],[1,\"\\n      \"],[8,[32,5],[[16,\"checked\",[30,0,[\"last\",\"old\"]]]],null,null],[1,\"\\n    \"]],[]]]]],[1,\"\\n  \"]],[],false,[]]",
      "moduleName": "/home/nvp/Development/NullVoxPopuli/package-majors/node_modules/.embroider/rewritten-app/components/search.ts",
      "scope": () => [ember_primitives__WEBPACK_IMPORTED_MODULE_9__.Form, _ember_helper__WEBPACK_IMPORTED_MODULE_2__.fn, handleSubmit, _name_input__WEBPACK_IMPORTED_MODULE_3__.NameInput, _show_minors__WEBPACK_IMPORTED_MODULE_4__.ShowMinors, _show_old__WEBPACK_IMPORTED_MODULE_5__.ShowOld],
      "isStrictMode": true
    }), this);
  }
  static {
    (0,_home_nvp_Development_NullVoxPopuli_package_majors_node_modules_pnpm_decorator_transforms_1_0_1_babel_core_7_23_3_node_modules_decorator_transforms_dist_runtime_cjs__WEBPACK_IMPORTED_MODULE_8__.f)(this, "router", [_ember_service__WEBPACK_IMPORTED_MODULE_1__.service]);
  }
  #router = ((0,_home_nvp_Development_NullVoxPopuli_package_majors_node_modules_pnpm_decorator_transforms_1_0_1_babel_core_7_23_3_node_modules_decorator_transforms_dist_runtime_cjs__WEBPACK_IMPORTED_MODULE_8__.i)(this, "router"), void 0);
  get last() {
    let qps1 = this.router.currentRoute?.queryParams;
    let minors1 = qps1?.['minors'];
    let packages1 = qps1?.['packages'];
    let old1 = qps1?.['old'];
    return {
      packages: packages1 ? `${packages1}` : '',
      minors: minors1 ? `${minors1}` : undefined,
      old: old1 ? `${old1}` : undefined
    };
  }
  updateSearch = data1 => {
    let {
      packageName: packages1,
      showMinors: minors1,
      showOld: old1
    } = data1;
    this.router.transitionTo('query', {
      queryParams: {
        packages: packages1,
        minors: minors1,
        old: old1
      }
    });
  };
}

//# sourceURL=webpack://package-majors/./components/search.ts?

with devtool: 'source-map'

All the TypeScript that was authored is very close to original source, with the main differences being minor line breaks and spacing changes.

example with devtool set to 'source-map' paused on line 52, showing original source for the getter

The code from the screenshot

import { template } from "@ember/template-compiler";
import Component from '@glimmer/component';
import { fn } from '@ember/helper';
import { service } from '@ember/service';
import { Form } from 'ember-primitives';
import { NameInput } from './name-input';
import { ShowMinors } from './show-minors';
import { ShowOld } from './show-old';
import type RouterService from '@ember/routing/router-service';
import type { DownloadsResponse } from 'package-majors/types';
/**
 * This is triggered on every value change,
 * which we don't need for this app.
 * The early return makes it a normal submit,
 * so the <Form> abstraction is basically just doing the
 * `FormData` conversion for us.
 */ function handleSubmit(onChange1: (data: SearchFormData) => void, data1: unknown, eventType1: string) {
    if (eventType1 !== 'submit') return;
    onChange1(data1 as SearchFormData);
}
interface SearchFormData {
    packageName: string;
    showMinors?: 'on';
    showOld?: 'on';
}
export class Search extends Component<{
    Blocks: {
        default: [data: DownloadsResponse];
    };
}> {
    static{
        template(`
    <Form @onChange={{fn handleSubmit this.updateSearch}}>
      <NameInput @value={{this.last.packages}} />

      <ShowMinors checked={{this.last.minors}} />
      <ShowOld checked={{this.last.old}} />
    </Form>
  `, {
            component: this,
            eval () {
                return eval(arguments[0]);
            }
        });
    }
    @service
    router: RouterService;
    get last() {
        let qps1 = this.router.currentRoute?.queryParams;
        let minors1 = qps1?.['minors'];
        let packages1 = qps1?.['packages'];
        let old1 = qps1?.['old'];
        return {
            packages: packages1 ? `${packages1}` : '',
            minors: minors1 ? `${minors1}` : undefined,
            old: old1 ? `${old1}` : undefined
        };
    }
    updateSearch = (data1: SearchFormData)=>{
        let { packageName: packages1, showMinors: minors1, showOld: old1 } = data1;
        this.router.transitionTo('query', {
            queryParams: {
                packages: packages1,
                minors: minors1,
                old: old1
            }
        });
    };
}