Skip to content

Commit

Permalink
Convert to TypeScript and add glint support
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeAstapov committed Jul 9, 2024
1 parent c01f406 commit 207884b
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 37 deletions.
4 changes: 3 additions & 1 deletion ember-lazy-mount/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"@typescript-eslint/parser": "^7.7.1",
"babel-plugin-ember-template-compilation": "^2.2.5",
"concurrently": "^8.2.2",
"ember-source": "~4.12.4",
"ember-template-lint": "^6.0.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -84,7 +85,8 @@
"prettier": "^3.3.2",
"prettier-plugin-ember-template-tag": "^2.0.2",
"rollup": "^4.18.1",
"typescript": "^5.5.3"
"typescript": "^5.5.3",
"webpack": "^5.92.1"
},
"peerDependencies": {
"ember-source": "^4.12.0 || >= 5"
Expand Down
2 changes: 1 addition & 1 deletion ember-lazy-mount/src/components/lazy-mount.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
{{yield (hash isLoading=true error=null)}}
{{else if this.error}}
{{yield (hash isLoading=false error=this.error)}}
{{else}}
{{else if this.loadedName}}
{{mount this.loadedName model=@model}}
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,27 @@ import { action, setProperties } from '@ember/object';
import { inject as service } from '@ember/service';
import { buildWaiter } from '@ember/test-waiters';

import type EngineLoaderService from '../services/engine-loader.ts';

const waiter = buildWaiter('ember-lazy-mount:lazy-mount');

interface LazyMountSignature {
Args: {
name: string;
model: unknown;
};
Blocks: {
default: [
| { isLoading: true; error: null }
| { isLoading: false; error: Error }
| undefined,
];
};
}

/**
* The `{{lazy-mount}}` component works just like the
* [`{{mount}}` helper](https://emberjs.com/api/ember/3.5/classes/Ember.Templates.helpers/methods/mount?anchor=mount).
* [`{{mount}}` helper](https://api.emberjs.com/ember/5.9/classes/Ember.Templates.helpers/methods/mount?anchor=mount).
*
* It accepts the name of the engine as a positional parameter and also an
* optional `model` parameter.
Expand Down Expand Up @@ -43,7 +59,7 @@ const waiter = buildWaiter('ember-lazy-mount:lazy-mount');
* the engine.
*
* ```hbs
* {{#lazy-mount engineName model=optionalDataForTheEngine as |engine|}}
* {{#lazy-mount this.engineName model=this.optionalDataForTheEngine as |engine|}}
* {{#if engine.isLoading}}
* 🕑 The engine is loading...
* {{else if engine.error}}
Expand All @@ -59,10 +75,10 @@ const waiter = buildWaiter('ember-lazy-mount:lazy-mount');
* the model of the engine.
* @public
*/
export default class LazyMount extends Component {
export default class LazyMount extends Component<LazyMountSignature> {
tagName = '';

@service engineLoader;
@service declare engineLoader: EngineLoaderService;

/**
* The name of the engine to load and subsequently mount.
Expand All @@ -71,7 +87,7 @@ export default class LazyMount extends Component {
* @type {string}
* @public
*/
name = null;
name: string | null = null;

/**
* Optional model that will be passed through to the engine.
Expand All @@ -82,7 +98,7 @@ export default class LazyMount extends Component {
* @type {any?}
* @public
*/
model = null;
model: unknown = null;

/**
* Optional callback called when the engine starts loading.
Expand All @@ -91,7 +107,7 @@ export default class LazyMount extends Component {
* @type {(() => void)?}
* @public
*/
onLoad = null;
onLoad: (() => void) | null = null;

/**
* Optional callback called when the engine finished loading.
Expand All @@ -100,7 +116,7 @@ export default class LazyMount extends Component {
* @type {(() => void)?}
* @public
*/
didLoad = null;
didLoad: (() => void) | null = null;

/**
* Optional callback called when the engine filed to load.
Expand All @@ -109,7 +125,7 @@ export default class LazyMount extends Component {
* @type {((error: Error) => void)?}
* @public
*/
onError = null;
onError: ((error: Error) => void) | null = null;

/**
* When the engine was loaded successfully, this will then be the name of the
Expand All @@ -122,7 +138,7 @@ export default class LazyMount extends Component {
* @type {string?}
* @private
*/
loadedName = null;
loadedName: string | null = null;

/**
* If an error occurred while loading the engine, it will be set here.
Expand All @@ -131,7 +147,7 @@ export default class LazyMount extends Component {
* @type {Error?}
* @private
*/
error = null;
error: Error | null = null;

/**
* While the bundle is being loaded, this property is `true`.
Expand All @@ -140,9 +156,9 @@ export default class LazyMount extends Component {
* @type {boolean}
* @private
*/
isLoading = false;
isLoading: boolean = false;

@action initLoadEngine(name) {
@action initLoadEngine(name: string): void {
assert(`lazy-mount: Argument 'name' is missing.`, name);

if (name !== this.loadedName) {
Expand All @@ -166,21 +182,21 @@ export default class LazyMount extends Component {
* @async
* @private
*/
async loadEngine(name = this.name) {
async loadEngine(name: string): Promise<void> {
const shouldCancel = this._thread();
const engineLoader = this.engineLoader;

this.setLoading();

if (!engineLoader.isLoaded(name)) {
let token = waiter.beginAsync();
const token = waiter.beginAsync();

try {
await engineLoader.load(name);
if (shouldCancel()) return;
} catch (error) {
if (shouldCancel()) return;
this.setError(error);
this.setError(error as Error);
return;
} finally {
waiter.endAsync(token);
Expand All @@ -191,25 +207,25 @@ export default class LazyMount extends Component {
}

setLoading() {
this.onLoad && this.onLoad();
this.onLoad?.();
setProperties(this, { loadedName: null, error: null, isLoading: true });
}

setLoaded(loadedName) {
this.didLoad && this.didLoad();
setLoaded(loadedName: string) {
this.didLoad?.();
setProperties(this, { loadedName, error: null, isLoading: false });
}

setError(error) {
this.onError && this.onError(error);
setError(error: Error) {
this.onError?.(error);
setProperties(this, { loadedName: null, error, isLoading: false });
}

/**
* The following is a really low-fidelity implementation of something that
* would be handled by ember-concurrency or ember-lifeline.
*/
_threadId = null;
_threadId: Record<string, never> | null = null;

_thread() {
const threadId = (this._threadId = {});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { getOwner } from '@ember/owner';
import Service from '@ember/service';
import { getOwner } from '@ember/owner';

import type Owner from '@ember/owner';

type ExtendedOwner = Owner & {
hasRegistration(name: string): boolean;
};

export default class EngineLoaderService extends Service {
/**
Expand All @@ -11,9 +17,9 @@ export default class EngineLoaderService extends Service {
* @param {String} name
* @return {Boolean}
*/
isLoaded(name) {
const owner = getOwner(this);
return owner.hasRegistration(`engine:${name}`);
isLoaded(name: string) {
const owner = <ExtendedOwner>getOwner(this);
return Boolean(owner?.hasRegistration(`engine:${name}`));
}

/**
Expand All @@ -23,12 +29,14 @@ export default class EngineLoaderService extends Service {
*
* @param {String} name
*/
register(name) {
register(name: string) {
if (this.isLoaded(name)) return;

const owner = getOwner(this);
owner.register(
owner?.register(
`engine:${name}`,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
globalThis.require(`${name}/engine`).default,
);
}
Expand All @@ -39,11 +47,11 @@ export default class EngineLoaderService extends Service {
* @param {String} name
* @async
*/
async load(name) {
async load(name: string) {
if (this.isLoaded(name)) return;

const assetLoader = getOwner(this).lookup('service:asset-loader');
await assetLoader.loadBundle(name);
const assetLoader = getOwner(this)?.lookup('service:asset-loader');
await assetLoader?.loadBundle(name);
this.register(name);
}
}
6 changes: 2 additions & 4 deletions ember-lazy-mount/src/template-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// Add all your components, helpers and modifiers to the template registry here, so apps don't have to do this.
// See https://typed-ember.gitbook.io/glint/environments/ember/authoring-addons

// import type MyComponent from './components/my-component';
import type LazyMount from './components/lazy-mount.ts';

// Remove this once entries have been added! 👇
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export default interface Registry {
// MyComponent: typeof MyComponent
LazyMount: typeof LazyMount;
}
14 changes: 14 additions & 0 deletions ember-lazy-mount/unpublished-development-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,17 @@ declare module '@glint/environment-ember-loose/registry' {
// See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons
}
}

class AssetLoaderService {
loadBundle(name: string): Promise<void>;
}

declare module '@ember/service' {
interface Registry {
'asset-loader': AssetLoaderService;
}
}

declare interface globalThis {
require(string): unknown;
}
Loading

0 comments on commit 207884b

Please sign in to comment.