Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Virtual EmberConf 2022: Platform State of the Union
Godfrey Chan
April 19, 2022
Programming
0
75
Virtual EmberConf 2022: Platform State of the Union
Godfrey Chan
April 19, 2022
Tweet
Share
More Decks by Godfrey Chan
See All by Godfrey Chan
Virtual EmberConf 2021: Platform State of the Union
chancancode
0
150
Virtual EmberConf 2020: Platform State of the Union
chancancode
1
290
Thinking in TypeScript
chancancode
0
190
The Lifecycle of a Rails Request
chancancode
3
9.9k
Dropping Down To The Metal™ (2018)
chancancode
0
72
Using Skylight to Solve Real-World Performance Problems
chancancode
1
170
Prying Open The Black Box (EmberConf 2018)
chancancode
0
210
Bending The Curve: Putting Rust in Ruby with Helix
chancancode
2
2.3k
Helix: Ruby Native Extensions Without Fear
chancancode
0
110
Other Decks in Programming
See All in Programming
競プロのすすめ
uya116
0
650
はじめてのプルリク - BLEA 編
watany
0
140
Seleniumでイキってたらサーバを絞め落としかけてた話
kenfujita
0
350
GoogleI/O2022 LT報告会資料
shinsukefujita1126
0
220
Java初心者が知っておくべきプログラミングのこと - JJUG CCC 2022 Spring
kishida
4
510
Node.jsデザインパターンを読んで
mmmommm
0
820
Vite でお手軽 Vue.js の環境構築
azuki
2
170
Maintaining Software Correctness
dlew
PRO
3
230
The strategies behind ddd – AdeoDevSummit 2022
lilobase
PRO
4
220
Oracle REST Data Service: APEX Office Hours
thatjeffsmith
0
660
Android Tools & Performance
takahirom
1
410
JSのウェブフレームワークで高速なルーターを実装する方法
usualoma
0
1.1k
Featured
See All Featured
Debugging Ruby Performance
tmm1
65
10k
Support Driven Design
roundedbygravity
86
8.5k
Writing Fast Ruby
sferik
612
57k
Documentation Writing (for coders)
carmenhchung
48
2.5k
Art Directing for the Web. Five minutes with CSS Template Areas
malarkey
196
9.4k
Adopting Sorbet at Scale
ufuk
63
7.6k
Streamline your AJAX requests with AmplifyJS and jQuery
dougneiner
126
8.5k
Bootstrapping a Software Product
garrettdimon
296
110k
Learning to Love Humans: Emotional Interface Design
aarron
261
37k
Bash Introduction
62gerente
597
210k
The Art of Programming - Codeland 2020
erikaheidi
32
9.4k
Designing the Hi-DPI Web
ddemaree
272
32k
Transcript
PLATFORM STATE OF THE UNION VERY VIRTUAL SUPER ONLINE
POLARIS ALIGNING WITH MODERN PACKAGES
PACKAGES HOW DO THEY WORK? package.json { "name": "my-app", /*
... */ "devDependencies": { "moment": "^2.29.2" /* ... */ } } import moment from 'moment';
PACKAGES HOW DO THEY WORK? import moment from 'moment'; node_modules
moment moment.js
HOW IT ALL BEGAN THE “SCRIPT” IN JAVASCRIPT
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>
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({ /* ... */ });
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
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'); }, /* ... */ }); !
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'); }, /* ... */ }); !
EMBER BUILD PIPELINE OUR ANSWER TO THE MISSING PIECES my-app
> _ ember build Bundles
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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', /* ... */ }; });
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 () { /* ... */
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
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
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
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
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
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
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 () { /* ... */ });
PACKAGES HOW DO THEY WORK? import moment from 'moment'; node_modules
moment moment.js
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
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
POLARIS ALIGNING WITH MODERN PACKAGES
GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal
packages 2. Imports are standard imports 3. Express dependencies as imports
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 () { /* ... */ });
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 () { /* ... */ });
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 () { /* ... */ });
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 () { /* ... */ });
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
ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES > _ npm
publish ember-concurrency .npmignore package.json tsconfig.json index.ts
ADDONS IN POLARIS PUBLISHED AS UNIVERSAL PACKAGES > _ npm
publish tsc ember-concurrency .npmignore package.json tsconfig.json index.ts
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
GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal
packages 2. Imports are standard imports 3. Express dependencies as imports
STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq';
STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; package.json
{ "name": "my-app", /* ... */ "devDependencies": { "lodash": "^4.0.0" /* ... */ } }
STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; node_modules
lodash uniq.js
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" /* ... */ } }
STANDARD IMPORTS NODE PACKAGE RESOLUTION import uniq from 'lodash/uniq'; import
task from 'ember-concurrency'; node_modules ember-concurrency index.js
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
None
EMBROIDER A NEW BUILD PIPELINE? Powered By > _ ember
build my-app Bundles
EMBROIDER A NEW BUILD ARCHITECTURE Powered By > _ ember
build ember-concurrency ember-moment ember-source my-app Bundles
EMBROIDER A NEW BUILD ARCHITECTURE Powered By > _ ember
build ember-concurrency ember-moment ember-source my-app Bundles
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
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
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';
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';
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';
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
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';
EMBROIDER A NEW BUILD ARCHITECTURE Outsourced To Powered By >
_ ember build my-app Bundles
EMBROIDER A PLUGGABLE BUILD ARCHITECTURE Outsourced To Powered By >
_ ember build my-app Bundles
GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal
packages 2. Imports are standard imports 3. Express dependencies as imports
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', /* ... */ }; });
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', /* ... */ }; });
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'); });
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); } }
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); } }
EMBROIDER ENCODING EMBER CONVENTIONS Outsourced To Powered By my-app Bundles
my-app
BAD IDEA
TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hello there, welcome
back! </Hello>
TEMPLATES TODAY NAME BASED RESOLUTIONS app/components/welcome.hbs <Hello> Hi there, welcome
back! </Hello> Rendering a component named "hello" RFC #311 <AngleBracket>
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>
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>
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
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
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
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
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
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
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!
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>
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>
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>
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>
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>
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>
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>
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>
TANGENT Is this!!" JSX?!
TANGENT
GUIDING PRINCIPLES ALIGNING WITH MODERN PACKAGES 1. Addons are universal
packages 2. Imports are standard imports 3. Express dependencies as imports
POLARIS HOW DO WE GET THERE?
None
None
None
None
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