Skip to content

Commit 8fbd35d

Browse files
Merge pull request #1442 from opencomponents/add-routes-conf
add routes option in oc.json
2 parents 8bb3517 + c555e6d commit 8fbd35d

File tree

10 files changed

+110
-37
lines changed

10 files changed

+110
-37
lines changed

src/cli/domain/ocConfig.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ export interface OpenComponentsConfig {
99
registries?: string[];
1010
/** Development-specific configuration settings */
1111
development?: {
12+
/** Additional Express routes to mount on the registry application */
13+
routes?: Array<{
14+
route: string;
15+
method: string;
16+
handler: string;
17+
}>;
1218
/** JavaScript code to be included in the preview HTML's <head> section.
1319
* Can be either a filepath to a JS script or inline JavaScript code.
1420
*/
@@ -45,6 +51,11 @@ type ParsedConfig = {
4551
sourcePath?: string;
4652
registries: string[];
4753
development: {
54+
routes?: Array<{
55+
route: string;
56+
method: string;
57+
handler: string;
58+
}>;
4859
preload?: string;
4960
plugins: {
5061
dynamic?: Record<string, string>;
@@ -91,7 +102,8 @@ function parseConfig(config: OpenComponentsConfig): ParsedConfig {
91102
development: {
92103
preload: config.development?.preload,
93104
plugins,
94-
fallback: config.development?.fallback
105+
fallback: config.development?.fallback,
106+
routes: config.development?.routes
95107
}
96108
};
97109

src/cli/facade/dev.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -190,26 +190,33 @@ const dev = ({ local, logger }: { logger: Logger; local: Local }) =>
190190
liveReload = { refresher, port: otherPort };
191191
}
192192

193-
const registry = oc.Registry({
194-
baseUrl,
195-
prefix: opts.prefix || '',
196-
dependencies: dependencies.modules,
197-
compileClient: true,
198-
discovery: true,
199-
env: { name: 'local' },
200-
fallbackRegistryUrl,
201-
fallbackClient,
202-
hotReloading,
203-
liveReloadPort: liveReload.port,
204-
local: true,
205-
postRequestPayloadSize,
206-
components: opts.components,
207-
path: path.resolve(componentsDir),
208-
port,
209-
templates: dependencies.templates,
210-
verbosity: 1,
211-
preload: localConfig.development?.preload
212-
});
193+
let registry: ReturnType<typeof oc.Registry>;
194+
try {
195+
registry = oc.Registry({
196+
baseUrl,
197+
prefix: opts.prefix || '',
198+
dependencies: dependencies.modules,
199+
compileClient: true,
200+
discovery: true,
201+
env: { name: 'local' },
202+
fallbackRegistryUrl,
203+
fallbackClient,
204+
hotReloading,
205+
liveReloadPort: liveReload.port,
206+
local: true,
207+
postRequestPayloadSize,
208+
components: opts.components,
209+
path: path.resolve(componentsDir),
210+
port,
211+
templates: dependencies.templates,
212+
verbosity: 1,
213+
preload: localConfig.development?.preload,
214+
routes: localConfig.development?.routes
215+
});
216+
} catch (err: any) {
217+
logger.err(String(err));
218+
throw err;
219+
}
213220

214221
registerPlugins(registry);
215222

src/registry/domain/options-sanitiser.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,5 +220,36 @@ export default function optionsSanitiser(input: RegistryOptions): Config {
220220
options.env = {};
221221
}
222222

223+
// Handle routes with string handlers - load the handler files
224+
if (options.routes) {
225+
options.routes = options.routes.map((route) => {
226+
if (typeof route.handler === 'string') {
227+
try {
228+
const fs = require('node:fs');
229+
const path = require('node:path');
230+
const handlerPath = path.isAbsolute(route.handler)
231+
? route.handler
232+
: path.resolve(process.cwd(), route.handler);
233+
234+
if (fs.existsSync(handlerPath)) {
235+
// Clear require cache to ensure fresh module loading
236+
delete require.cache[require.resolve(handlerPath)];
237+
const handlerModule = require(handlerPath);
238+
route.handler = handlerModule.default || handlerModule;
239+
} else {
240+
throw new Error(`Handler file not found: ${handlerPath}`);
241+
}
242+
} catch (error) {
243+
console.warn(
244+
`Warning: Could not load route handler "${route.handler}":`,
245+
(error as Error).message
246+
);
247+
// Keep the original string handler to avoid breaking the application
248+
}
249+
}
250+
return route;
251+
});
252+
}
253+
223254
return options as Config;
224255
}

src/registry/domain/validators/registry-configuration.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,13 @@ export default function registryConfiguration(
6565
);
6666
}
6767

68-
if (typeof route.handler !== 'function') {
68+
if (
69+
typeof route.handler !== 'function' &&
70+
typeof route.handler !== 'string'
71+
) {
6972
return returnError(
70-
strings.errors.registry.CONFIGURATION_ROUTES_HANDLER_MUST_BE_FUNCTION
73+
strings.errors.registry
74+
.CONFIGURATION_ROUTES_HANDLER_MUST_BE_FUNCTION_OR_FILE_PATH
7175
);
7276
}
7377

src/registry/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ export default function registry<T = any>(inputOptions: RegistryOptions<T>) {
6868
eventsHandler.fire('start', {});
6969

7070
if (options.verbosity) {
71-
ok(`Registry started at port http://localhost:${app.get('port')}`);
71+
ok(
72+
`Registry started at port http://localhost:${app.get('port')}${options.prefix}`
73+
);
7274

7375
if (componentsInfo) {
7476
const componentsNumber = Object.keys(

src/registry/router.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ export function create(app: Express, conf: Config, repository: Repository) {
3131

3232
const prefix = conf.prefix;
3333

34-
if (prefix !== '/') {
34+
const definedBaseRoute = conf.routes?.find((route) => route.route === '/');
35+
36+
if (prefix !== '/' && !definedBaseRoute) {
3537
app.get('/', (_req, res) => res.redirect(prefix));
3638
app.get(prefix.substring(0, prefix.length - 1), routes.index);
3739
}
@@ -92,9 +94,21 @@ export function create(app: Express, conf: Config, repository: Repository) {
9294

9395
if (conf.routes) {
9496
for (const route of conf.routes) {
95-
app[
96-
route.method.toLowerCase() as 'get' | 'post' | 'put' | 'patch' | 'head'
97-
](route.route, route.handler);
97+
// Ensure handler is a function (should be converted by options-sanitiser)
98+
if (typeof route.handler === 'function') {
99+
app[
100+
route.method.toLowerCase() as
101+
| 'get'
102+
| 'post'
103+
| 'put'
104+
| 'patch'
105+
| 'head'
106+
](route.route, route.handler);
107+
} else {
108+
console.warn(
109+
`Warning: Route handler for "${route.route}" is not a function. Skipping route.`
110+
);
111+
}
98112
}
99113
}
100114
}

src/registry/views/preview.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,14 @@ export default function preview(vm: {
280280
});
281281
}
282282
283-
if (window.oc && window.oc.events && window.oc.events.on) {
284-
window.oc.events.on('oc:error', function(ev, error) {
283+
window.oc = window.oc || {};
284+
window.oc.cmd = window.oc.cmd || [];
285+
window.oc.cmd.push(function(oc) {
286+
oc.events.on('oc:error', function(ev, error) {
285287
ensureOverlay(error);
286288
});
287-
}
289+
});
290+
288291
})();
289292
</script>
290293

src/resources/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ export default {
117117
'Registry configuration is not valid: prefix should end with "/"',
118118
CONFIGURATION_PREFIX_DOES_NOT_START_WITH_SLASH:
119119
'Registry configuration is not valid: prefix should start with "/"',
120-
CONFIGURATION_ROUTES_HANDLER_MUST_BE_FUNCTION:
121-
'Registry configuration is not valid: handler should be a function',
120+
CONFIGURATION_ROUTES_HANDLER_MUST_BE_FUNCTION_OR_FILE_PATH:
121+
'Registry configuration is not valid: handler should be a function or a file path',
122122
CONFIGURATION_ROUTES_NOT_VALID:
123123
'Registry configuration is not valid: each route should contain route, method and handler',
124124
CONFIGURATION_ROUTES_MUST_BE_ARRAY:

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ export interface Config<T = any> {
352352
routes?: Array<{
353353
route: string;
354354
method: string;
355-
handler: (req: Request, res: Response) => void;
355+
handler: string | ((req: Request, res: Response) => void);
356356
}>;
357357
/**
358358
* Convenience S3 configuration – if present the registry will create

test/unit/registry-domain-validator.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,16 +368,16 @@ describe('registry : domain : validator', () => {
368368
});
369369
});
370370

371-
describe('when route contains handler that is not a function', () => {
371+
describe('when route contains handler that is not a function or a string', () => {
372372
const conf = {
373-
routes: [{ route: '/hello', method: 'get', handler: 'hello' }],
373+
routes: [{ route: '/hello', method: 'get', handler: 3 }],
374374
s3: baseS3Conf
375375
};
376376

377377
it('should not be valid', () => {
378378
expect(validate(conf).isValid).to.be.false;
379379
expect(validate(conf).message).to.be.eql(
380-
'Registry configuration is not valid: handler should be a function'
380+
'Registry configuration is not valid: handler should be a function or a file path'
381381
);
382382
});
383383
});

0 commit comments

Comments
 (0)