Skip to content

Commit 5ac48cd

Browse files
feat: ScriptManager hook system (#1078)
* feat: initial version of ScriptManager tapable hooks * refactor: move hooks from static to instance property - Move hooks from static property to instance property - Initialize hooks in init() method - Update test references to use ScriptManager.shared.hooks - Keep the same functionality but improve code organization * fix: loading scripts in TesterApp * fix: call resolve hook if presented instead of default logic * feat: add load script hooks * fix: add noop resolve and load script * feat: override params in `beforeResolve()` * feat: add an ability to replace load and resolve * fix: use `isUsed` property * feat: pass more params to `beforeResolve` * feat: pass more params to rest functions * chore: move test to separate file * chore: lint * chore: revert unnecessary changes in `ScriptManager.test.ts` * fix: simplify public API * fix: type issue * feat: add tests for waterfall pattern in `before*` hooks * chore: lint * feat: make `afterResolve` and `afterLoad` waterfall hooks * feat: add tests for waterfall pattern in `afterResolve` and `afterLoad` * chore: add changeset * feat: remove callbacks, use async handlers * chore: simplify usage * Revert "fix: type issue" This reverts commit 55598a8. * chore: update changeset * chore: cleanup * chore: cleanup * chore: rename params to args & options * feat: universal async/sync hooks * chore: cleanup tester app index.js * test: rework ScriptManager hook tests * feat: rework error hooks * feat: allow empty return from error hooks * chore: rework setup in tester-app to support typing * feat: add more error tests * chore: cleanup * test: add beforeLoad test * test: parametrize first two tests * fix: typecheck * fix: skip validate plugins in tests * feat: allow sync callbacks in hooks --------- Co-authored-by: Jakub Romanczyk <jakub.romanczyk@callstack.com>
1 parent 332f384 commit 5ac48cd

10 files changed

Lines changed: 847 additions & 106 deletions

File tree

.changeset/curly-buttons-cough.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@callstack/repack": minor
3+
---
4+
5+
Add hook system to `ScriptManager` for runtime manipulation of script loading process.

apps/tester-app/index.js

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,8 @@
1-
import { Script, ScriptManager } from '@callstack/repack/client';
21
import { AppRegistry } from 'react-native';
32
import { name as appName } from './app.json';
43
import App from './src/App';
54

6-
if (!__DEV__) {
7-
ScriptManager.shared.setStorage(AsyncStorage);
8-
}
9-
10-
ScriptManager.shared.addResolver((scriptId, _caller) => {
11-
if (__DEV__) {
12-
return {
13-
url: Script.getDevServerURL(scriptId),
14-
cache: false,
15-
};
16-
}
17-
18-
if (scriptId.includes('local')) {
19-
return {
20-
url: Script.getFileSystemURL(scriptId),
21-
cache: false,
22-
};
23-
}
24-
25-
return {
26-
url: Script.getRemoteURL(`http://localhost:9999/${scriptId}`),
27-
};
28-
});
29-
30-
// ScriptManager.shared.on('resolving', (...args) => {
31-
// console.log('DEBUG/resolving', ...args);
32-
// });
33-
34-
// ScriptManager.shared.on('resolved', (...args) => {
35-
// console.log('DEBUG/resolved', ...args);
36-
// });
37-
38-
// ScriptManager.shared.on('prefetching', (...args) => {
39-
// console.log('DEBUG/prefetching', ...args);
40-
// });
41-
42-
// ScriptManager.shared.on('loading', (...args) => {
43-
// console.log('DEBUG/loading', ...args);
44-
// });
45-
46-
// ScriptManager.shared.on('loaded', (...args) => {
47-
// console.log('DEBUG/loaded', ...args);
48-
// });
49-
50-
// ScriptManager.shared.on('error', (...args) => {
51-
// console.log('DEBUG/error', ...args);
52-
// });
5+
// ScriptManager setup
6+
import './src/setup';
537

548
AppRegistry.registerComponent(appName, () => App);

apps/tester-app/src/setup.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Script, ScriptManager } from '@callstack/repack/client';
2+
import AsyncStorage from '@react-native-async-storage/async-storage';
3+
4+
if (!__DEV__) {
5+
ScriptManager.shared.setStorage(AsyncStorage);
6+
}
7+
8+
ScriptManager.shared.addResolver(async (scriptId, _caller) => {
9+
if (__DEV__) {
10+
return {
11+
url: Script.getDevServerURL(scriptId),
12+
cache: false,
13+
};
14+
}
15+
16+
if (scriptId.includes('local')) {
17+
return {
18+
url: Script.getFileSystemURL(scriptId),
19+
cache: false,
20+
};
21+
}
22+
23+
return {
24+
url: Script.getRemoteURL(`http://localhost:9999/${scriptId}`),
25+
};
26+
});
27+
28+
// ScriptManager Event Listeners
29+
// Run `react-native start --verbose` to see these logs
30+
ScriptManager.shared.on('resolving', (...args) => {
31+
console.debug('ScriptManagerEvent:resolving', ...args);
32+
});
33+
34+
ScriptManager.shared.on('resolved', (...args) => {
35+
console.debug('ScriptManagerEvent:resolved', ...args);
36+
});
37+
38+
ScriptManager.shared.on('prefetching', (...args) => {
39+
console.debug('ScriptManagerEvent:prefetching', ...args);
40+
});
41+
42+
ScriptManager.shared.on('loading', (...args) => {
43+
console.debug('ScriptManagerEvent:loading', ...args);
44+
});
45+
46+
ScriptManager.shared.on('loaded', (...args) => {
47+
console.debug('ScriptManagerEvent:loaded', ...args);
48+
});
49+
50+
ScriptManager.shared.on('error', (...args) => {
51+
console.debug('ScriptManagerEvent:error', ...args);
52+
});
53+
54+
// ScriptManager Hooks
55+
// Run `react-native start --verbose` to see these logs
56+
ScriptManager.shared.hooks.beforeResolve(async (args) => {
57+
console.debug('ScriptManager.shared.hooks.beforeResolve', args);
58+
return args;
59+
});
60+
61+
ScriptManager.shared.hooks.resolve(async (args) => {
62+
console.debug('ScriptManager.shared.hooks.resolve', args);
63+
const { scriptId, caller, referenceUrl } = args.options;
64+
for (const [, , resolve] of args.resolvers) {
65+
const locator = await resolve(scriptId, caller, referenceUrl);
66+
if (locator) return locator;
67+
}
68+
});
69+
70+
ScriptManager.shared.hooks.afterResolve(async (args) => {
71+
console.debug('ScriptManager.shared.hooks.afterResolve', args);
72+
return args;
73+
});
74+
75+
ScriptManager.shared.hooks.beforeLoad(async (args) => {
76+
console.debug('ScriptManager.shared.hooks.beforeLoad', args);
77+
return args;
78+
});
79+
80+
ScriptManager.shared.hooks.load(async (args) => {
81+
console.debug('ScriptManager.shared.hooks.load', args);
82+
await args.loadScript();
83+
return true;
84+
});
85+
86+
ScriptManager.shared.hooks.afterLoad(async (args) => {
87+
console.debug('ScriptManager.shared.hooks.afterLoad', args);
88+
return args;
89+
});

biome.jsonc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"suspicious": {
4949
"noControlCharactersInRegex": "off",
5050
"noExplicitAny": "off",
51-
"noFallthroughSwitchClause": "off"
51+
"noFallthroughSwitchClause": "off",
52+
"noConfusingVoidType": "off"
5253
}
5354
},
5455
"ignore": ["templates/*", "tests/metro-compat/**/__tests__/**"]

packages/repack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"react-refresh": "^0.14.0",
9595
"schema-utils": "^4.2.0",
9696
"shallowequal": "^1.1.0",
97+
"tapable": "^2.2.1",
9798
"throttleit": "^2.1.0",
9899
"webpack-merge": "^6.0.1"
99100
},

packages/repack/src/commands/common/config/__tests__/makeCompilerConfig.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { makeCompilerConfig } from '../makeCompilerConfig.js';
66

77
jest.mock('../getConfigFilePath.js');
88
jest.mock('../loadProjectConfig.js');
9+
jest.mock('../validatePlugins.js');
910

1011
const setupMocks = () => {
1112
const mocks = {

0 commit comments

Comments
 (0)