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

Godfrey Chan

April 19, 2022
Tweet

More Decks by Godfrey Chan

Other Decks in Programming

Transcript

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

    ... */ "devDependencies": { "moment": "^2.29.2" /* ... */ } } import moment from 'moment';
  2. 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>
  3. 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({ /* ... */ });
  4. 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
  5. 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'); }, /* ... */ }); !
  6. 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'); }, /* ... */ }); !
  7. 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', /* ... */ }; });
  8. 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', /* ... */ }; });
  9. 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', /* ... */ }; });
  10. 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', /* ... */ }; });
  11. 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', /* ... */ }; });
  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', /* ... */ }; }); 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 () { /* ... */
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  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 moment ember-source
  23. 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 () { /* ... */ });
  24. 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
  25. 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
  26. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  27. 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 () { /* ... */ });
  28. 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 () { /* ... */ });
  29. 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 () { /* ... */ });
  30. 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 () { /* ... */ });
  31. 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
  32. ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES > _ npm

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

    publish tsc ember-concurrency .npmignore package.json tsconfig.json index.ts
  34. 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
  35. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

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

    { "name": "my-app", /* ... */ "devDependencies": { "lodash": "^4.0.0" /* ... */ } }
  37. 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" /* ... */ } }
  38. STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; import

    task from 'ember-concurrency'; node_modules ember-concurrency index.js
  39. 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
  40. EMBROIDER A NEW BUILD ARCHITECTURE Powered By > _ ember

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

    build ember-concurrency ember-moment ember-source my-app Bundles
  42. 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
  43. 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
  44. 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';
  45. 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';
  46. 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';
  47. 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
  48. 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';
  49. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  50. 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', /* ... */ }; });
  51. 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', /* ... */ }; });
  52. 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'); });
  53. 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); } }
  54. 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); } }
  55. TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome

    back! </Hello> Rendering a component named "hello" RFC #311 <AngleBracket>
  56. 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>
  57. 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>
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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!
  65. 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>
  66. 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>
  67. 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>
  68. 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>
  69. 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>
  70. 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>
  71. 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>
  72. 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>
  73. GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal

    packages 2. Imports are standard imports 3. Express dependencies as imports
  74. 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