Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Virtual EmberConf 2022: Platform State of the Union

Virtual EmberConf 2022: Platform State of the Union

22bb3e56828870ee9a0dd93aeadbe04a?s=128

Godfrey Chan

April 19, 2022
Tweet

More Decks by Godfrey Chan

Other Decks in Programming

Transcript

  1. PLATFORM STATE OF THE UNION VERY VIRTUAL SUPER ONLINE

  2. POLARIS ALIGNING WITH MODERN PACKAGES

  3. PACKAGES HOW DO THEY WORK? package.json { "name": "my-app", /*

    ... */ "devDependencies": { "moment": "^2.29.2" /* ... */ } } import moment from 'moment';
  4. PACKAGES HOW DO THEY WORK? import moment from 'moment'; node_modules

    moment moment.js
  5. HOW IT ALL BEGAN THE “SCRIPT” IN JAVASCRIPT

  6. HOW IT ALL BEGAN THE “SCRIPT” IN JAVASCRIPT <!DOCTYPE html>

    <html> <head> <title>My App</title> </head> <body> <script src="underscore.js"></script> <script src="jquery.js"></script> <script src="backbone.js"></script> </body> </html>
  7. HOW IT ALL BEGAN THE “SCRIPT” IN JAVASCRIPT app/app.js window.App

    MySelectComponent = Ember.Component.extend({ /* ... */ }); app/router.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/nav-bar.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/select-menu.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); dist/my-app.js // app/app.js window.App = Ember.Application.create({ /* ... */ }); // app/router.js App.Router.map({ /* ... */ }); // app/components/nav-bar.js App.NavBarComponent = Ember.Component.extend({ /* ... */ }); // app/components/select-menu.js App.SelectMenuComponent = Ember.Component.extend({ /* ... */ }); // app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ /* ... */ });
  8. app/app.js window.App MySelectComponent = Ember.Component.extend({ /* ... */ }); app/router.js

    App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/nav-bar.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/select-menu.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); dist/my-app.js // app/app.js window.App = Ember.Application.create({ /* ... */ }); // app/router.js App.Router.map({ /* ... */ }); // app/components/nav-bar.js App.NavBarComponent = Ember.Component.extend({ /* ... */ }); // app/components/select-menu.js App.SelectMenuComponent = Ember.Component.extend({ /* ... */ }); // app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ /* ... */ }); “bundling” “bundle” TANGENT
  9. JAVASCRIPT MODULES THE NATIVE MODULES SYSTEM app/components/my-select.js import Ember from

    'ember'; export default Ember.Component.extend({ classNames: ['my-select'], init() { this._super(); this.set('isActive', false); }, click(event) { event.preventDefault(); this.toggleProperty('isActive'); }, /* ... */ }); app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ classNames: ['my-select'], init() { this._super(); this.set('isActive', false); }, click(event) { event.preventDefault(); this.toggleProperty('isActive'); }, /* ... */ }); !
  10. JAVASCRIPT MODULES THE NATIVE MODULES SYSTEM app/components/my-select.js import Ember from

    'ember'; export default Ember.Component.extend({ classNames: ['my-select'], init() { this._super(); this.set('isActive', false); }, click(event) { event.preventDefault(); this.toggleProperty('isActive'); }, /* ... */ }); app/components/my-select.js App.MySelectComponent = Ember.Component.extend({ classNames: ['my-select'], init() { this._super(); this.set('isActive', false); }, click(event) { event.preventDefault(); this.toggleProperty('isActive'); }, /* ... */ }); !
  11. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES my-app

    > _ ember build Bundles
  12. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  13. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  14. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  15. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  16. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  17. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  18. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  19. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  20. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  21. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES app/components/loading-spinner.js

    app/components/date-picker.js import Component from '@ember/component'; import task from 'ember-concurrency'; import moment from 'moment'; import ENV from 'my-app/config/environment'; export default class DatePicker extends Component { /* ... */ } dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */
  22. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency ember-moment ember-source
  23. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency ember-moment ember-source
  24. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency ember-moment ember-source
  25. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency ember-moment ember-source
  26. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency ember-moment ember-source
  27. EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES dist/my-app.js

    /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; }); dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ ember-concurrency moment ember-source
  28. PACKAGES HOW DO THEY WORK? import moment from 'moment'; window.require('moment');

    dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ });
  29. PACKAGES HOW DO THEY WORK? import moment from 'moment'; node_modules

    moment moment.js
  30. NPM Registry package.json JavaScript Modules (ESM) Modern JavaScript Loose Modules

    Node Resolution Exports Maps TypeScript Types Dynamic import() Top-level Await import.meta Import Assertions <script type="module"> Import Maps import.meta.resolve JSON Modules CSS Modules Constructable Stylesheets Web Bundles pnpm yarn npm webpack Rollup Parcel Vite Snowpack JSPM unpkg.com skypack.dev Workspaces Editors TypeScript GitHub Storybook Universal Package
  31. NPM Registry package.json JavaScript Modules (ESM) Modern JavaScript Loose Modules

    Node Resolution Exports Maps TypeScript Types Dynamic import() Top-level Await import.meta Import Assertions <script type="module"> Import Maps import.meta.resolve JSON Modules CSS Modules Constructable Stylesheets Web Bundles pnpm yarn npm webpack Rollup Parcel Vite Snowpack JSPM unpkg.com skypack.dev Workspaces Editors TypeScript GitHub Storybook Universal Package
  32. POLARIS ALIGNING WITH MODERN PACKAGES

  33. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  34. ADDONS TODAY PUBLISHED AS DYNAMIC BUILD INSTRUCTIONS > _ ember

    build ember-concurrency ember-moment ember-source dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function /* ... */ }); define('@ember/component', [], function ( /* ... */ }); define('@ember/object', [], function () { /* ... */ });
  35. ADDONS TODAY PUBLISHED AS DYNAMIC BUILD INSTRUCTIONS > _ ember

    build ember-concurrency ember-moment ember-source dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function /* ... */ }); define('@ember/component', [], function ( /* ... */ }); define('@ember/object', [], function () { /* ... */ });
  36. ADDONS TODAY PUBLISHED AS DYNAMIC BUILD INSTRUCTIONS > _ ember

    build ember-concurrency ember-moment ember-source dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function /* ... */ }); define('@ember/component', [], function ( /* ... */ }); define('@ember/object', [], function () { /* ... */ });
  37. ADDONS TODAY PUBLISHED AS DYNAMIC BUILD INSTRUCTIONS > _ ember

    build ember-concurrency ember-moment ember-source dist/vendor.js define('ember-concurrency', [ '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function /* ... */ }); define('@ember/component', [], function ( /* ... */ }); define('@ember/object', [], function () { /* ... */ });
  38. ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES https://emberjs.github.io/rfcs/0507-embroider-v2-package-format.html Universal Packages

    NPM Registry package.json JavaScript Modules (ESM) “Modern” JavaScript Features Node Resolution Exports Maps TypeScript Types
  39. ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES > _ npm

    publish ember-concurrency .npmignore package.json tsconfig.json index.ts
  40. ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES > _ npm

    publish tsc ember-concurrency .npmignore package.json tsconfig.json index.ts
  41. ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES ember-concurrency package.json index.js

    index.d.ts ember-concurrency .npmignore package.json tsconfig.json index.ts > _ npm publish
  42. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  43. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq';

  44. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; package.json

    { "name": "my-app", /* ... */ "devDependencies": { "lodash": "^4.0.0" /* ... */ } }
  45. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; node_modules

    lodash uniq.js
  46. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; import

    task from 'ember-concurrency'; package.json { "name": "my-app", /* ... */ "devDependencies": { "ember-concurrency": "^2.2.1" /* ... */ } }
  47. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; import

    task from 'ember-concurrency'; node_modules ember-concurrency index.js
  48. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; import

    task from 'ember-concurrency'; import { timeout } from 'ember-concurrency/utils'; // ERROR! import nope from 'ember-concurrency/private'; package.json { "name": "ember-concurrency", /* ... */ "exports": { ".": "./src/index.js", "./utils": "./src/concurrency-utils.js" } } Using exports Map https://nodejs.org/api/packages.html#subpath-exports
  49. None
  50. EMBROIDER A NEW BUILD PIPELINE? Powered By > _ ember

    build my-app Bundles
  51. EMBROIDER A NEW BUILD ARCHITECTURE Powered By > _ ember

    build ember-concurrency ember-moment ember-source my-app Bundles
  52. EMBROIDER A NEW BUILD ARCHITECTURE Powered By > _ ember

    build ember-concurrency ember-moment ember-source my-app Bundles
  53. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js
  54. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js
  55. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js import task from 'ember-concurrency';
  56. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js import task from 'ember-concurrency'; import uniq from 'lodash/uniq';
  57. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js import task from 'ember-concurrency'; import uniq from 'lodash/uniq'; import Tracker from './utils/tracker';
  58. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js import task from 'ember-concurrency'; import uniq from 'lodash/uniq'; import Tracker from './utils/tracker'; utils/tracker.js
  59. TREE SHAKING LAZY DEPENDENCY INCLUSION node_modules ember-concurrency index.js lodash unescape.js

    union.js unionBy.js unionWith.js uniq.js unionBy.js unionWith.js app.js import task from 'ember-concurrency'; import uniq from 'lodash/uniq'; import Tracker from './utils/tracker'; utils/tracker.js import union from 'lodash/union';
  60. EMBROIDER A NEW BUILD ARCHITECTURE Outsourced To Powered By >

    _ ember build my-app Bundles
  61. EMBROIDER A PLUGGABLE BUILD ARCHITECTURE Outsourced To Powered By >

    _ ember build my-app Bundles
  62. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  63. TREE SHAKING TODAY COULD IT BE DONE? dist/vendor.js define('ember-concurrency', [

    '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ }); dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  64. TREE SHAKING TODAY COULD IT BE DONE? dist/vendor.js define('ember-concurrency', [

    '@ember/application', '@ember/object', '@ember/object/computed', /* ... */ ], function () { /* ... */ }); define('moment', [], function () { /* ... */ }); define('@ember/application', [], function () { /* ... */ }); define('@ember/component', [], function () { /* ... */ }); define('@ember/object', [], function () { /* ... */ }); dist/my-app.js /* ... */ define('my-app/components/loading-spinner', [], function() { /* ... */ }); define('my-app/components/date-picker', [ '@ember/component', 'ember-concurrency', 'moment', 'my-app/config/environment' ],function(Component, task, moment, ENV) { class DatePicker extends Component { /* ... */ } return DatePicker; }); define('my-app/config/environment', [], function () { return { environment: 'development', modulePrefix: 'my-app', /* ... */ }; });
  65. TREE SHAKING TODAY COULD IT BE DONE? app/router.js import EmberRouter

    from '@ember/routing/router'; export default class Router extends EmberRouter {} Router.map(function () { this.route('post'); });
  66. TREE SHAKING TODAY COULD IT BE DONE? app/router.js import EmberRouter

    from '@ember/routing/router'; export default class Router extends EmberRouter {} Router.map(function () { this.route('post'); }); app/controllers/post.js import Controller from '@ember/controller'; export default class PostController extends Controller { /* ... */ } app/routes/post.js import Route from '@ember/routing/route'; import { service } from '@ember/service'; export default class PostRoute extends Route { @service store; async model(params) { this.store.find('post', params.post_id); } }
  67. TREE SHAKING TODAY COULD IT BE DONE? app/router.js import EmberRouter

    from '@ember/routing/router'; export default class Router extends EmberRouter {} Router.map(function () { this.route('post'); }); app/controllers/post.js import Controller from '@ember/controller'; export default class PostController extends Controller { /* ... */ } app/routes/post.js import Route from '@ember/routing/route'; import { service } from '@ember/service'; export default class PostRoute extends Route { @service store; async model(params) { this.store.find('post', params.post_id); } }
  68. EMBROIDER ENCODING EMBER CONVENTIONS Outsourced To Powered By my-app Bundles

    my-app
  69. BAD IDEA

  70. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hello there, welcome

    back! </Hello>
  71. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" RFC #311 <AngleBracket>
  72. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component RFC #311 <AngleBracket>
  73. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); RFC #311 <AngleBracket>
  74. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); RFC #311 <AngleBracket> ember-resolver
  75. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) RFC #311 <AngleBracket> ember-resolver loader.js
  76. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) Step 2. Setup the component Manager = getComponentManager(Hello); Template = getComponentTemplate(Hello); C = Manager.createComponent(Hello, Args); This = Manager.getContext(C); Step 3. Render render(Template, This, Args); document.createElement(!!"), etc RFC #311 <AngleBracket> ember-resolver loader.js RFC #213 Component Manager RFC #481 Co-location glimmer-vm
  77. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) Step 2. Setup the component Manager = getComponentManager(Hello); Template = getComponentTemplate(Hello); C = Manager.createComponent(Hello, Args); This = Manager.getContext(C); Step 3. Render render(Template, This, Args); document.createElement(!!"), etc RFC #311 <AngleBracket> ember-resolver loader.js RFC #213 Component Manager RFC #481 Co-location glimmer-vm
  78. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) Step 2. Setup the component Manager = getComponentManager(Hello); Template = getComponentTemplate(Hello); C = Manager.createComponent(Hello, Args); This = Manager.getContext(C); Step 3. Render render(Template, This, Args); document.createElement(!!"), etc RFC #311 <AngleBracket> ember-resolver loader.js RFC #213 Component Manager RFC #481 Co-location glimmer-vm
  79. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> {{log Hello}} ~~~~~ Error. Hello is not a value! Rendering a component named "hello" Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) Step 2. Setup the component Manager = getComponentManager(Hello); Template = getComponentTemplate(Hello); C = Manager.createComponent(Hello, Args); This = Manager.getContext(C); Step 3. Render render(Template, This, Args); document.createElement(!!"), etc RFC #311 <AngleBracket> ember-resolver loader.js RFC #213 Component Manager RFC #481 Co-location glimmer-vm
  80. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <HelloProvider as |Hello|> <Hello>

    Hi there, welcome back! </Hello> {{!-- Works. Hello is a value! --}} {{log Hello}} </HelloProvider> Rendering the value Hello as a component Step 1. Find the component owner.factoryFor("component:hello"); require("my-app/components/hello"); (load the AMD module from app bundle) Step 2. Setup the component Manager = getComponentManager(Hello); Template = getComponentTemplate(Hello); C = Manager.createComponent(Hello, Args); This = Manager.getContext(C); Step 3. Render render(Template, This, Args); document.createElement(!!"), etc RFC #64 Contextual Components ember-resolver loader.js RFC #213 Component Manager RFC #481 Co-location glimmer-vm Skipped!
  81. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    <template> <Hello> Hi there, welcome back! </Hello> {{!-- Works. Hello is a value! --}} {{log Hello}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  82. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    <template> <Hello> Hi there, welcome back! </Hello> {{!-- Works. Hello is a value! --}} {{log Hello}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  83. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    // Same value inside and outside the template console.log(Hello); <template> <Hello> Hi there, welcome back! </Hello> {{!-- Works. Hello is a value! --}} {{log Hello}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  84. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    const GREETING = 'Hi there'; <template> <Hello> {{GREETING}}, welcome back! </Hello> </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  85. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    /** * Randomly chosen a value for the given choices. */ function sample(...choices) { let i = Math.floor(Math.random() * choices.length); return choices[i]; } <template> <Hello> Hi there, welcome back! </Hello> {{sample 'You won a prize!' 'Better luck next time!'}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  86. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    import sample from 'my-app/helpers/sample'; <template> <Hello> Hi there, welcome back! </Hello> {{sample 'You won a prize!' 'Better luck next time!'}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  87. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Hello from 'my-app/components/hello';

    import sample from 'lodash/sample'; import { array } from '@ember/helper'; <template> <Hello> Hi there, welcome back! </Hello> {{sample (array 'You won a prize!' 'Better luck next time!' )}} </template> RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  88. TEMPLATES IN POLARIS VALUES EVERYWHERE app/components/welcome.gjs import Component from '@glimmer/component';

    import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import Hello from 'my-app/components/hello'; import sample from 'lodash/sample'; export default class Welcome extends Component { @tracked result = this.lottery(); @action retry() { this.result = this.lottery(); } lottery() { return sample(['You won a prize!', 'Better luck next time!']); } <template> <Hello>Hi there, welcome back!</Hello> {{this.result}} </template> } RFC #373 Modifier Manager RFC #432 Contextual Helpers/Modifiers RFC #496 Strict Mode Templates Refactor Handlebars parser and glimmer-vm RFC #625 Helper Manager RFC #756 Default Helper Manager RFC #779 <template>
  89. TANGENT Is this!!" JSX?!

  90. TANGENT

  91. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  92. POLARIS HOW DO WE GET THERE?

  93. None
  94. None
  95. None
  96. None
  97. Blueprints POLARIS Deprecations Migration Guides Codemods Guides Tutorial Blog Posts

    Outreach Best Practices Bug Reports Examples Primitives High-level Features Lint Rules Paradigms Patterns Cookbooks Discussions Talks Experience Reports Abstractions Tools Libraries Integrations Ember Inspector RFCs