Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion core/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,23 @@ export const createCapacitor = (win: WindowCapacitor): CapacitorInstance => {
const platform = getPlatform();
const pluginHeader = getPluginHeader(pluginName);
let jsImplementation: any;
let jsImplementationPromise: Promise<any> | undefined;

const loadPluginImplementation = (): Promise<any> => {
// Share one load between calls made before it settles, so a lazy
// implementation such as `web: () => import('./web').then(...)` is
// only created once. A failed load is retried on the next call.
if (!jsImplementationPromise) {
jsImplementationPromise = resolvePluginImplementation().catch((err) => {
jsImplementationPromise = undefined;
throw err;
});
}

return jsImplementationPromise;
};

const loadPluginImplementation = async (): Promise<any> => {
const resolvePluginImplementation = async (): Promise<any> => {
if (!jsImplementation && platform in jsImplementations) {
jsImplementation =
typeof jsImplementations[platform] === 'function'
Expand Down
46 changes: 46 additions & 0 deletions core/src/tests/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,52 @@ describe('plugin', () => {
expect(rtn).toBeUndefined();
});

it('calls made before a lazy web implementation loads share one instance', async () => {
let created = 0;
const AwesomeWeb = class extends WebPlugin {
constructor() {
super();
created++;
}
async fire() {
this.notifyListeners('change', { value: 88 });
}
};
cap = initCapacitorGlobal(win);
const Awesome = cap.registerPlugin<any>('Awesome', {
web: async () => new AwesomeWeb(),
});

const first = jest.fn();
const second = jest.fn();
// neither listener is awaited before the next call
const handles = [Awesome.addListener('change', first), Awesome.addListener('change', second)];
await Promise.all(handles);
await Awesome.fire();

expect(created).toBe(1);
expect(first).toHaveBeenCalledWith({ value: 88 });
expect(second).toHaveBeenCalledWith({ value: 88 });
});

it('retries a lazy implementation that failed to load', async () => {
let attempts = 0;
cap = initCapacitorGlobal(win);
const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {
web: async () => {
attempts++;
if (attempts === 1) {
throw 'unable to load module';
}
return { mph: async () => 88 };
},
});

await expect(Awesome.mph()).rejects.toBe('unable to load module');
expect(await Awesome.mph()).toBe(88);
expect(attempts).toBe(2);
});

const mockAndroidBridge = () => {
win.androidBridge = {
postMessage: () => {
Expand Down