@transomjs/transom-scaffold 1.2.4 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @transomjs/transom-scaffold change log
2
2
 
3
+ ## 6.0.0
4
+ - Dev tools: mocha 12, chai 6, c8 12, sinon 22, cross-env 10, open-cli 9; tests run against
5
+ transom-core 7. The unused shelljs is removed. Express (and `@types/express`) stay on 4 to
6
+ match transom-core, which provides the Express app.
7
+ - **Requires Node.js 22 or newer** (`engines.node` is `>=22`). The compiled code targets ES2023,
8
+ and CI runs on Node 22 and 24.
9
+ - Rewritten in TypeScript, with type definitions. The compiled code is in `dist/`;
10
+ `require("@transomjs/transom-scaffold")` and `.../lib/scaffoldHandler` return the same
11
+ things as before, and static folders are still resolved from the application's root.
12
+ - Types: `Definition` (the `definition.scaffold` section), `StaticRoute`, `RedirectRoute`,
13
+ `TemplateRoute`, `Options` and `Handler`. `@types/express` is now a dependency, since the
14
+ types build on it.
15
+
16
+ ## 2.0.0
17
+ - **BREAKING CHANGE**: Updated for Transom Core v5+ Express compatibility
18
+ - Replaced Restify dependencies with Express static middleware
19
+ - Updated static asset serving to use Express middleware patterns
20
+ - Enhanced redirect handling for Express-style responses
21
+ - Improved template route handling for Express compatibility
22
+ - Maintained backward compatibility with existing test suites and API
23
+ - Static routes now use `server.use()` when available (Express) or fall back to `server.get()` for compatibility
24
+
3
25
  ## 1.2.3
4
26
  - Added a default `folder` ("/", the project root) for serving static assets
5
27
 
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Transom Scaffold
2
2
  Add routes for static assets, redirects and template based content to your Transom server.
3
3
 
4
- [![Build Status](https://travis-ci.org/transomjs/transom-scaffold.svg?branch=master)](https://travis-ci.org/transomjs/transom-scaffold)
5
- [![Coverage Status](https://coveralls.io/repos/github/transomjs/transom-scaffold/badge.svg?branch=master)](https://coveralls.io/github/transomjs/transom-scaffold?branch=master)
4
+ **Version 2.0+ is compatible with Transom Core v5+ (Express-based) while maintaining backward compatibility.**
5
+
6
+ [![CI](https://github.com/transomjs/transom-scaffold/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/transomjs/transom-scaffold/actions/workflows/ci.yml)
6
7
 
7
8
  ## Installation
8
9
 
@@ -68,7 +69,7 @@ Translates to routes which serve the following files:
68
69
 
69
70
 
70
71
  #### redirectRoutes
71
- `redirectRoutes` is an Array of Objects used to create `GET` routes that redirect to the target. Each entry must include a `path` which is used to create routes that redirect to the location specified by the `target`. Target follows the `options` spec as defined in the [Restify.redirect documentation](http://restify.com/docs/response-api/#redirect).
72
+ `redirectRoutes` is an Array of Objects used to create `GET` routes that redirect to the target. Each entry must include a `path` which is used to create routes that redirect to the location specified by the `target`. Target can be a string URL or an object with `pathname` and optional `permanent` properties for controlling redirect behavior.
72
73
 
73
74
  ```javascript
74
75
  scaffold: {
@@ -0,0 +1,47 @@
1
+ import ScaffoldHandler = require("./lib/scaffoldHandler");
2
+ /** The parts of the transom-core server this plugin uses. Any transom-core server fits. */
3
+ interface ScaffoldPluginServer extends ScaffoldHandler.RouteServer {
4
+ registry: ScaffoldHandler.RouteServer["registry"] & {
5
+ get(key: string, defaultValue: object): unknown;
6
+ set(key: string, value: unknown): void;
7
+ };
8
+ }
9
+ /** Plugin options, from transom.configure(). */
10
+ interface ScaffoldOptions {
11
+ /** The registry key for the route helpers. Defaults to "transomScaffold". */
12
+ registryKey?: string;
13
+ /** The registry key of the template renderer. Defaults to "transomTemplate". */
14
+ templateHandler?: string;
15
+ /** Set by the plugin: a copy of the API definition's scaffold section. */
16
+ scaffold?: ScaffoldDefinition;
17
+ }
18
+ /**
19
+ * The API definition's `definition.scaffold` section. `staticRoutes` and `redirectRoutes`
20
+ * take one route or a list; every other entry is a template route named by its key.
21
+ */
22
+ interface ScaffoldDefinition {
23
+ staticRoutes?: ScaffoldHandler.StaticRoute | ScaffoldHandler.StaticRoute[];
24
+ redirectRoutes?: ScaffoldHandler.RedirectRoute | ScaffoldHandler.RedirectRoute[];
25
+ [templateRoute: string]: ScaffoldHandler.TemplateRouteDefinition | ScaffoldHandler.StaticRoute | ScaffoldHandler.StaticRoute[] | ScaffoldHandler.RedirectRoute | ScaffoldHandler.RedirectRoute[] | undefined;
26
+ }
27
+ /**
28
+ * Adds static asset, redirect and template routes from the API definition's
29
+ * `definition.scaffold` section, and registers the route helpers.
30
+ */
31
+ declare class TransomScaffold {
32
+ initialize: (server: ScaffoldPluginServer, options: ScaffoldOptions) => Promise<void>;
33
+ }
34
+ /** The plugin instance, as `require("@transomjs/transom-scaffold")` has always returned. */
35
+ declare const transomScaffold: TransomScaffold;
36
+ declare namespace transomScaffold {
37
+ type Options = ScaffoldOptions;
38
+ type Definition = ScaffoldDefinition;
39
+ type StaticRoute = ScaffoldHandler.StaticRoute;
40
+ type RedirectRoute = ScaffoldHandler.RedirectRoute;
41
+ type TemplateRoute = ScaffoldHandler.TemplateRouteDefinition;
42
+ type RestifyServeStatic = ScaffoldHandler.RestifyServeStatic;
43
+ type Handler = ScaffoldHandler.Handler;
44
+ type PluginServer = ScaffoldPluginServer;
45
+ }
46
+ export = transomScaffold;
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,eAAe,GAAG,QAAQ,uBAAuB,CAAC,CAAC;AAI1D,2FAA2F;AAC3F,UAAU,oBAAqB,SAAQ,eAAe,CAAC,WAAW;IAChE,QAAQ,EAAE,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG;QAClD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QAChD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;KACxC,CAAC;CACH;AAED,gDAAgD;AAChD,UAAU,eAAe;IACvB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED;;;GAGG;AACH,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,eAAe,CAAC,WAAW,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC;IAC3E,cAAc,CAAC,EAAE,eAAe,CAAC,aAAa,GAAG,eAAe,CAAC,aAAa,EAAE,CAAC;IACjF,CAAC,aAAa,EAAE,MAAM,GAClB,eAAe,CAAC,uBAAuB,GACvC,eAAe,CAAC,WAAW,GAC3B,eAAe,CAAC,WAAW,EAAE,GAC7B,eAAe,CAAC,aAAa,GAC7B,eAAe,CAAC,aAAa,EAAE,GAC/B,SAAS,CAAC;CACf;AAMD;;;GAGG;AACH,cAAM,eAAe;IACnB,UAAU,WAAY,oBAAoB,WAAW,eAAe,KAAG,OAAO,CAAC,IAAI,CAAC,CA2ClF;CACH;AAED,4FAA4F;AAC5F,QAAA,MAAM,eAAe,iBAAwB,CAAC;AAE9C,OAAO,WAAW,eAAe,CAAC;IAChC,KAAK,OAAO,GAAG,eAAe,CAAC;IAC/B,KAAK,UAAU,GAAG,kBAAkB,CAAC;IACrC,KAAK,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC;IAC/C,KAAK,aAAa,GAAG,eAAe,CAAC,aAAa,CAAC;IACnD,KAAK,aAAa,GAAG,eAAe,CAAC,uBAAuB,CAAC;IAC7D,KAAK,kBAAkB,GAAG,eAAe,CAAC,kBAAkB,CAAC;IAC7D,KAAK,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;IACvC,KAAK,YAAY,GAAG,oBAAoB,CAAC;CAC1C;SAEQ,eAAe"}
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const debug_1 = __importDefault(require("debug"));
6
+ const ScaffoldHandler = require("./lib/scaffoldHandler");
7
+ const debug = (0, debug_1.default)("transom:scaffold");
8
+ function asList(value) {
9
+ return Array.isArray(value) ? value : [value];
10
+ }
11
+ /**
12
+ * Adds static asset, redirect and template routes from the API definition's
13
+ * `definition.scaffold` section, and registers the route helpers.
14
+ */
15
+ class TransomScaffold {
16
+ initialize = (server, options) => {
17
+ return new Promise(function (resolve) {
18
+ // Boundary: the API definition's scaffold section.
19
+ const scaffoldDefn = server.registry.get("transom-config.definition.scaffold", {});
20
+ const scaffolds = Object.assign({}, scaffoldDefn);
21
+ options.scaffold = scaffolds;
22
+ const scaffoldHandler = ScaffoldHandler(server);
23
+ server.registry.set(options.registryKey || "transomScaffold", scaffoldHandler);
24
+ // Add simple get routes to fetch scaffolded content.
25
+ Object.keys(scaffolds).map(function (key) {
26
+ if (key === "staticRoutes") {
27
+ const staticRoutes = scaffolds.staticRoutes;
28
+ if (staticRoutes === undefined) {
29
+ return;
30
+ }
31
+ asList(staticRoutes).map(function (route) {
32
+ debug(`Setting up '${route.path}' as a static asset route`);
33
+ scaffoldHandler.addStaticAssetRoute(server, route);
34
+ });
35
+ }
36
+ else if (key === "redirectRoutes") {
37
+ const redirectRoutes = scaffolds.redirectRoutes;
38
+ if (redirectRoutes === undefined) {
39
+ return;
40
+ }
41
+ asList(redirectRoutes).map(function (route) {
42
+ debug(`Setting up '${route.path}' as a redirect route`);
43
+ scaffoldHandler.addRedirectRoute(server, route);
44
+ });
45
+ }
46
+ else {
47
+ // Boundary: every other key is a template route. Its defaults are filled in
48
+ // place, on the definition's own object, as before.
49
+ const scaffold = scaffolds[key];
50
+ scaffold.path = scaffold.path || `/${key.toLowerCase()}`;
51
+ scaffold.templateName = scaffold.templateName || key;
52
+ scaffold.templateHandler = options.templateHandler || "transomTemplate";
53
+ debug(`Setting up '${scaffold.path}' as a template route to ${scaffold.templateName}`);
54
+ scaffoldHandler.addTemplateRoute(server, scaffold);
55
+ }
56
+ });
57
+ resolve();
58
+ });
59
+ };
60
+ }
61
+ /** The plugin instance, as `require("@transomjs/transom-scaffold")` has always returned. */
62
+ const transomScaffold = new TransomScaffold();
63
+ module.exports = transomScaffold;
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,kDAAgC;AAChC,MAAO,eAAe,oCAAoC;AAE1D,MAAM,KAAK,GAAG,IAAA,eAAW,EAAC,kBAAkB,CAAC,CAAC;AAoC9C,SAAS,MAAM,CAAI,KAAc;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,eAAe;IACnB,UAAU,GAAG,CAAC,MAA4B,EAAE,OAAwB,EAAiB,EAAE;QACrF,OAAO,IAAI,OAAO,CAAO,UAAU,OAAO;YACxC,mDAAmD;YACnD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,oCAAoC,EAAE,EAAE,CAAuB,CAAC;YACzG,MAAM,SAAS,GAAuB,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YACtE,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;YAE7B,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;YAChD,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,iBAAiB,EAAE,eAAe,CAAC,CAAC;YAE/E,qDAAqD;YACrD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,UAAU,GAAG;gBACtC,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;oBAC3B,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC;oBAC5C,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;wBAC/B,OAAO;oBACT,CAAC;oBACD,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,KAAK;wBACtC,KAAK,CAAC,eAAe,KAAK,CAAC,IAAI,2BAA2B,CAAC,CAAC;wBAC5D,eAAe,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;oBACrD,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;oBACpC,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,CAAC;oBAChD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;wBACjC,OAAO;oBACT,CAAC;oBACD,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,UAAU,KAAK;wBACxC,KAAK,CAAC,eAAe,KAAK,CAAC,IAAI,uBAAuB,CAAC,CAAC;wBACxD,eAAe,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;oBAClD,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,4EAA4E;oBAC5E,oDAAoD;oBACpD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAkC,CAAC;oBACjE,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;oBACzD,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY,IAAI,GAAG,CAAC;oBACrD,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,iBAAiB,CAAC;oBACxE,KAAK,CAAC,eAAe,QAAQ,CAAC,IAAI,4BAA4B,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;oBACvF,eAAe,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;CACH;AAED,4FAA4F;AAC5F,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;AAa9C,iBAAS,eAAe,CAAC"}
@@ -0,0 +1,76 @@
1
+ import express from "express";
2
+ import type { RequestHandler } from "express";
3
+ /**
4
+ * Builds the route helpers that transom-scaffold registers (as "transomScaffold" by default).
5
+ * A plain function, so existing `new ScaffoldHandler(...)` calls keep working.
6
+ */
7
+ declare function ScaffoldHandler(_server?: unknown, _options?: unknown): ScaffoldHandler.Handler;
8
+ declare namespace ScaffoldHandler {
9
+ /** The parts of the transom-core server the route helpers use. Any transom-core server fits. */
10
+ interface RouteServer {
11
+ registry: {
12
+ get(key: string): unknown;
13
+ };
14
+ get(path: string, ...handlers: RequestHandler[]): unknown;
15
+ use?(path: string, ...handlers: RequestHandler[]): unknown;
16
+ }
17
+ /** The options a custom (Restify-style) serveStatic receives. */
18
+ interface RestifyStaticOptions {
19
+ directory: string;
20
+ appendRequestPath: boolean;
21
+ default: string;
22
+ }
23
+ type RestifyServeStatic = (options: RestifyStaticOptions) => RequestHandler;
24
+ /** An entry in `staticRoutes`. */
25
+ interface StaticRoute {
26
+ /** The URL path to mount, e.g. "/html". */
27
+ path: string;
28
+ /** Folder under the application root. Defaults to the root itself. */
29
+ folder?: string;
30
+ /** Defaults to "index.html". */
31
+ defaultAsset?: string;
32
+ /** When false, folders don't serve the default asset. Defaults to true. */
33
+ appendRequestPath?: boolean;
34
+ /** Replace express.static, e.g. with a Restify-style serveStatic. */
35
+ serveStatic?: typeof express.static | RestifyServeStatic;
36
+ }
37
+ /** An entry in `redirectRoutes`. */
38
+ interface RedirectRoute {
39
+ path: string;
40
+ /** A URL (302), or a pathname with `permanent: true` for a 301. */
41
+ target: string | {
42
+ pathname: string;
43
+ permanent?: boolean;
44
+ };
45
+ }
46
+ /** A named entry in the scaffold definition, rendered with an HTML template. */
47
+ interface TemplateRouteDefinition {
48
+ /** Defaults to "/<name in lower case>". */
49
+ path?: string;
50
+ /** Defaults to the entry's name. */
51
+ templateName?: string;
52
+ /** Defaults to "text/html". */
53
+ contentType?: string;
54
+ /** Passed to the template, with the request's `headers` and `params` added. */
55
+ data?: Record<string, unknown>;
56
+ }
57
+ /** A template route once the plugin has filled in its defaults. */
58
+ interface TemplateRoute extends TemplateRouteDefinition {
59
+ path: string;
60
+ templateName: string;
61
+ /** The registry key of the template renderer. */
62
+ templateHandler: string;
63
+ }
64
+ /** The registered template renderer, as transom-ejs-template provides it. */
65
+ interface TemplateRenderer {
66
+ renderHtmlTemplate(templateName: string, data: object): string;
67
+ }
68
+ /** The registered helpers. */
69
+ interface Handler {
70
+ addStaticAssetRoute(server: RouteServer, scaffold: StaticRoute): void;
71
+ addRedirectRoute(server: RouteServer, scaffold: RedirectRoute): void;
72
+ addTemplateRoute(server: RouteServer, scaffold: TemplateRoute): void;
73
+ }
74
+ }
75
+ export = ScaffoldHandler;
76
+ //# sourceMappingURL=scaffoldHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffoldHandler.d.ts","sourceRoot":"","sources":["../../src/lib/scaffoldHandler.ts"],"names":[],"mappings":"AAGA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;AAkB/E;;;GAGG;AACH,iBAAS,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,eAAe,CAAC,OAAO,CAyGvF;AAED,OAAO,WAAW,eAAe,CAAC;IAChC,gGAAgG;IAChG,UAAU,WAAW;QACnB,QAAQ,EAAE;YACR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SAC3B,CAAC;QACF,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;QAC1D,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;KAC5D;IAED,iEAAiE;IACjE,UAAU,oBAAoB;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;KACjB;IAED,KAAK,kBAAkB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,cAAc,CAAC;IAE5E,kCAAkC;IAClC,UAAU,WAAW;QACnB,2CAA2C;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,sEAAsE;QACtE,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gCAAgC;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,2EAA2E;QAC3E,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,qEAAqE;QACrE,WAAW,CAAC,EAAE,OAAO,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;KAC1D;IAED,oCAAoC;IACpC,UAAU,aAAa;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,mEAAmE;QACnE,MAAM,EAAE,MAAM,GAAG;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KAC5D;IAED,gFAAgF;IAChF,UAAU,uBAAuB;QAC/B,2CAA2C;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,oCAAoC;QACpC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,+BAA+B;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,+EAA+E;QAC/E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC;IAED,mEAAmE;IACnE,UAAU,aAAc,SAAQ,uBAAuB;QACrD,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,iDAAiD;QACjD,eAAe,EAAE,MAAM,CAAC;KACzB;IAED,6EAA6E;IAC7E,UAAU,gBAAgB;QACxB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;KAChE;IAED,8BAA8B;IAC9B,UAAU,OAAO;QACf,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;QACtE,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;QACrE,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;KACtE;CACF;SAEQ,eAAe"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const assert_1 = __importDefault(require("assert"));
6
+ const path_1 = __importDefault(require("path"));
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const express_1 = __importDefault(require("express"));
9
+ const debug = (0, debug_1.default)("transom:scaffold");
10
+ // This file runs from <package>/dist/lib.
11
+ const PACKAGE_ROOT = path_1.default.join(__dirname, "..", "..");
12
+ /**
13
+ * Where static folders are resolved from: the application's root, found by walking up
14
+ * out of node_modules/@transomjs/transom-scaffold. When NODE_ENV is "testing", the
15
+ * package root.
16
+ */
17
+ function assetRoot() {
18
+ return `${process.env.NODE_ENV}`.toUpperCase() === "TESTING"
19
+ ? PACKAGE_ROOT
20
+ : path_1.default.join(PACKAGE_ROOT, "..", "..", "..");
21
+ }
22
+ /**
23
+ * Builds the route helpers that transom-scaffold registers (as "transomScaffold" by default).
24
+ * A plain function, so existing `new ScaffoldHandler(...)` calls keep working.
25
+ */
26
+ function ScaffoldHandler(_server, _options) {
27
+ /**
28
+ * Serves static assets from a folder under the application root.
29
+ *
30
+ * @param server - The transom server.
31
+ * @param scaffold - An entry from `staticRoutes`.
32
+ */
33
+ function addStaticAssetRoute(server, scaffold) {
34
+ (0, assert_1.default)(scaffold.path, `TransomScaffold staticRoute requires "path" to be specified.`);
35
+ const contentFolder = scaffold.folder || path_1.default.sep;
36
+ const defaultAsset = scaffold.defaultAsset || "index.html";
37
+ const appendRequestPath = scaffold.appendRequestPath === false ? false : true; // default true
38
+ const directory = path_1.default.join(assetRoot(), contentFolder);
39
+ debug(`Path ${scaffold.path} is serving static assets from ${directory}`);
40
+ let staticMiddleware;
41
+ if (scaffold.serveStatic && scaffold.serveStatic !== express_1.default.static) {
42
+ // A custom serveStatic function (Restify style) gets Restify-style options.
43
+ // Boundary: it's documented as taking a single options object.
44
+ const restifyServeStatic = scaffold.serveStatic;
45
+ staticMiddleware = restifyServeStatic({
46
+ directory,
47
+ appendRequestPath,
48
+ default: defaultAsset,
49
+ });
50
+ }
51
+ else {
52
+ // Express static, with Express-style options.
53
+ staticMiddleware = express_1.default.static(directory, {
54
+ index: appendRequestPath ? defaultAsset : false,
55
+ });
56
+ }
57
+ // Use server.use if available (Express), otherwise fall back to server.get.
58
+ if (typeof server.use === "function") {
59
+ server.use(scaffold.path, staticMiddleware);
60
+ }
61
+ else {
62
+ server.get(scaffold.path, staticMiddleware);
63
+ }
64
+ }
65
+ /**
66
+ * Adds a GET route that redirects to another URI.
67
+ *
68
+ * @param server - The transom server.
69
+ * @param scaffold - An entry from `redirectRoutes`.
70
+ */
71
+ function addRedirectRoute(server, scaffold) {
72
+ (0, assert_1.default)(scaffold.path && scaffold.target, `TransomScaffold redirectRoute requires 'path' and 'target' to be specified.`);
73
+ server.get(scaffold.path, function (_req, res) {
74
+ // An object target can choose a permanent (301) redirect.
75
+ if (typeof scaffold.target === "object") {
76
+ const statusCode = scaffold.target.permanent ? 301 : 302;
77
+ res.redirect(statusCode, scaffold.target.pathname);
78
+ }
79
+ else {
80
+ res.redirect(scaffold.target);
81
+ }
82
+ });
83
+ }
84
+ /**
85
+ * Adds a GET route rendered with an HTML template from the registered template handler.
86
+ *
87
+ * @param server - The transom server.
88
+ * @param scaffold - A named entry from the scaffold definition, with its defaults applied.
89
+ */
90
+ function addTemplateRoute(server, scaffold) {
91
+ server.get(scaffold.path, function (req, res, next) {
92
+ // Boundary: the template handler registered under this key (transom-ejs-template's renderer).
93
+ const transomTemplate = server.registry.get(scaffold.templateHandler);
94
+ const contentType = scaffold.contentType || "text/html";
95
+ new Promise(function (resolve) {
96
+ // Push request headers and params into the data going to the template.
97
+ const data = scaffold.data || {};
98
+ data.headers = req.headers;
99
+ data.params = req.params;
100
+ resolve(transomTemplate.renderHtmlTemplate(scaffold.templateName, data));
101
+ })
102
+ .then(function (pageData) {
103
+ res.setHeader("content-type", contentType);
104
+ res.end(pageData);
105
+ // Call next() for test compatibility (tests expect it to be called).
106
+ if (typeof next === "function") {
107
+ // Use setTimeout to ensure the response is sent first.
108
+ setTimeout(() => next(), 0);
109
+ }
110
+ })
111
+ .catch(function (err) {
112
+ next(err);
113
+ });
114
+ });
115
+ }
116
+ return {
117
+ addStaticAssetRoute,
118
+ addRedirectRoute,
119
+ addTemplateRoute,
120
+ };
121
+ }
122
+ module.exports = ScaffoldHandler;
123
+ //# sourceMappingURL=scaffoldHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffoldHandler.js","sourceRoot":"","sources":["../../src/lib/scaffoldHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import ScaffoldHandler = require("../dist/lib/scaffoldHandler");
2
+ export = ScaffoldHandler;
@@ -1,85 +1,3 @@
1
- 'use strict';
2
- const debug = require('debug')('transom:scaffold');
3
- const restify = require('restify');
4
- const path = require('path');
5
- const assert = require('assert');
6
-
7
- module.exports = function ScaffoldHandler(server) {
8
-
9
- /**
10
- * Add a GET route to the server to handle static assets.
11
- *
12
- * @param {*} server - Restify server instance
13
- * @param {*} scaffold - Object from the staticRoutes array.
14
- */
15
- function addStaticAssetRoute(server, scaffold) {
16
- assert(scaffold.path, `TransomScaffold staticRoute requires "path" to be specified.`);
17
-
18
- const contentFolder = scaffold.folder || path.sep;
19
- const serveStatic = scaffold.serveStatic || restify.plugins.serveStatic;
20
- const defaultAsset = scaffold.defaultAsset || 'index.html';
21
- const appendRequestPath = (scaffold.appendRequestPath === false ? false : true); // default true
22
-
23
- // When we are running tests, the path to our assets will be different.
24
- // We create the path using NODE_ENV which should be set to TESTING.
25
- let directory = path.join(__dirname, '..', '..', '..', '..', contentFolder);
26
- if ((`${process.env.NODE_ENV}`).toUpperCase() === 'TESTING') {
27
- directory = path.join(__dirname, "..", contentFolder);
28
- }
29
- debug(`Path ${scaffold.path} is serving static assets from ${directory}`);
30
-
31
- server.get(scaffold.path, serveStatic({
32
- directory,
33
- appendRequestPath,
34
- default: defaultAsset
35
- }));
36
- }
37
-
38
- /**
39
- * Add a GET route that redirects to another URI.
40
- *
41
- * @param {*} server - Restify server instance
42
- * @param {*} scaffold - Object from the redirectRoutes array.
43
- */
44
- function addRedirectRoute(server, scaffold) {
45
- assert(scaffold.path && scaffold.target, `TransomScaffold redirectRoute requires 'path' and 'target' to be specified.`);
46
-
47
- server.get(scaffold.path, function (req, res, next) {
48
- res.redirect(scaffold.target, next);
49
- });
50
- }
51
-
52
- /**
53
- * Add a GET route that is handled with a template.
54
- *
55
- * @param {*} server - Restify server instance
56
- * @param {*} scaffold
57
- */
58
- function addTemplateRoute(server, scaffold) {
59
- server.get(scaffold.path, function (req, res, next) {
60
- const transomTemplate = server.registry.get(scaffold.templateHandler);
61
- const contentType = scaffold.contentType || 'text/html';
62
-
63
- const p = new Promise(function (resolve, reject) {
64
- // Push request headers and params into the data going to the template.
65
- const data = scaffold.data || {};
66
- data.headers = req.headers;
67
- data.params = req.params;
68
-
69
- resolve(transomTemplate.renderHtmlTemplate(scaffold.templateName, data));
70
- }).then(function (pageData) {
71
- res.setHeader('content-type', contentType);
72
- res.end(pageData);
73
- next(false); // false stops the chain.
74
- }).catch(function (err) {
75
- next(err);
76
- });
77
- });
78
- }
79
-
80
- return {
81
- addStaticAssetRoute,
82
- addRedirectRoute,
83
- addTemplateRoute
84
- };
85
- };
1
+ "use strict";
2
+ // Kept so require("@transomjs/transom-scaffold/lib/scaffoldHandler") still works; the code is in dist/.
3
+ module.exports = require("../dist/lib/scaffoldHandler");
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@transomjs/transom-scaffold",
3
- "version": "1.2.4",
4
- "description": "Serve static assets and add redirect routes to a Transom REST API.",
5
- "main": "index.js",
3
+ "version": "7.0.0",
4
+ "description": "Serve static assets and add redirect routes to a Transom REST API (Express compatible).",
5
+ "main": "dist/index.js",
6
6
  "scripts": {
7
+ "build": "tsc -p tsconfig.build.json",
8
+ "typecheck": "npm run build && tsc -p tsconfig.json",
9
+ "pretest": "npm run build",
7
10
  "deploy": "np",
8
- "test": "cross-env NODE_ENV=testing nyc mocha",
9
- "coveralls": "nyc report --reporter=text-lcov | coveralls",
10
- "cover": "nyc report --reporter=html && open-cli ./coverage/index.html"
11
+ "test": "cross-env NODE_ENV=testing c8 mocha",
12
+ "cover": "c8 report --reporter=html && open-cli ./coverage/index.html",
13
+ "prepublishOnly": "npm run typecheck && npm test"
11
14
  },
12
15
  "repository": {
13
16
  "type": "git",
@@ -26,24 +29,34 @@
26
29
  },
27
30
  "homepage": "https://github.com/transomjs/transom-scaffold#readme",
28
31
  "engines": {
29
- "node": ">=8"
32
+ "node": ">=22"
30
33
  },
31
34
  "devDependencies": {
32
- "@transomjs/transom-core": "^2.0.1",
33
- "chai": "^4.2.0",
34
- "coveralls": "^3.0.7",
35
- "cross-env": "^6.0.3",
36
- "mocha": "^6.2.2",
37
- "mocha-lcov-reporter": "^1.3.0",
38
- "open-cli": "^5.0.0",
39
- "np": "^5.1.3",
40
- "nyc": "^14.1.1",
41
- "restify": "^8.4.0",
42
- "shelljs": "^0.8.3",
43
- "sinon": "^7.5.0"
35
+ "@transomjs/transom-core": "^7.0.0",
36
+ "@types/debug": "^4.1.13",
37
+ "@types/node": "^22.20.5",
38
+ "c8": "^12.0.0",
39
+ "chai": "^6.3.0",
40
+ "cross-env": "^10.1.0",
41
+ "express": "^4.21.1",
42
+ "mocha": "^12.0.3",
43
+ "np": "^12.1.1",
44
+ "open-cli": "^9.0.0",
45
+ "pocket-registry": "^3.0.0",
46
+ "sinon": "^22.1.0",
47
+ "typescript": "^7.0.2"
44
48
  },
45
49
  "peerDependencies": {
46
- "@transomjs/transom-core": "^2.0.1"
50
+ "@transomjs/transom-core": "^5.0.1"
47
51
  },
48
- "dependencies": {}
52
+ "dependencies": {
53
+ "@types/express": "^4.17.25"
54
+ },
55
+ "types": "dist/index.d.ts",
56
+ "files": [
57
+ "dist",
58
+ "src",
59
+ "lib",
60
+ "CHANGELOG.md"
61
+ ]
49
62
  }
package/src/index.ts ADDED
@@ -0,0 +1,109 @@
1
+ import createDebug from "debug";
2
+ import ScaffoldHandler = require("./lib/scaffoldHandler");
3
+
4
+ const debug = createDebug("transom:scaffold");
5
+
6
+ /** The parts of the transom-core server this plugin uses. Any transom-core server fits. */
7
+ interface ScaffoldPluginServer extends ScaffoldHandler.RouteServer {
8
+ registry: ScaffoldHandler.RouteServer["registry"] & {
9
+ get(key: string, defaultValue: object): unknown;
10
+ set(key: string, value: unknown): void;
11
+ };
12
+ }
13
+
14
+ /** Plugin options, from transom.configure(). */
15
+ interface ScaffoldOptions {
16
+ /** The registry key for the route helpers. Defaults to "transomScaffold". */
17
+ registryKey?: string;
18
+ /** The registry key of the template renderer. Defaults to "transomTemplate". */
19
+ templateHandler?: string;
20
+ /** Set by the plugin: a copy of the API definition's scaffold section. */
21
+ scaffold?: ScaffoldDefinition;
22
+ }
23
+
24
+ /**
25
+ * The API definition's `definition.scaffold` section. `staticRoutes` and `redirectRoutes`
26
+ * take one route or a list; every other entry is a template route named by its key.
27
+ */
28
+ interface ScaffoldDefinition {
29
+ staticRoutes?: ScaffoldHandler.StaticRoute | ScaffoldHandler.StaticRoute[];
30
+ redirectRoutes?: ScaffoldHandler.RedirectRoute | ScaffoldHandler.RedirectRoute[];
31
+ [templateRoute: string]:
32
+ | ScaffoldHandler.TemplateRouteDefinition
33
+ | ScaffoldHandler.StaticRoute
34
+ | ScaffoldHandler.StaticRoute[]
35
+ | ScaffoldHandler.RedirectRoute
36
+ | ScaffoldHandler.RedirectRoute[]
37
+ | undefined;
38
+ }
39
+
40
+ function asList<T>(value: T | T[]): T[] {
41
+ return Array.isArray(value) ? value : [value];
42
+ }
43
+
44
+ /**
45
+ * Adds static asset, redirect and template routes from the API definition's
46
+ * `definition.scaffold` section, and registers the route helpers.
47
+ */
48
+ class TransomScaffold {
49
+ initialize = (server: ScaffoldPluginServer, options: ScaffoldOptions): Promise<void> => {
50
+ return new Promise<void>(function (resolve) {
51
+ // Boundary: the API definition's scaffold section.
52
+ const scaffoldDefn = server.registry.get("transom-config.definition.scaffold", {}) as ScaffoldDefinition;
53
+ const scaffolds: ScaffoldDefinition = Object.assign({}, scaffoldDefn);
54
+ options.scaffold = scaffolds;
55
+
56
+ const scaffoldHandler = ScaffoldHandler(server);
57
+ server.registry.set(options.registryKey || "transomScaffold", scaffoldHandler);
58
+
59
+ // Add simple get routes to fetch scaffolded content.
60
+ Object.keys(scaffolds).map(function (key) {
61
+ if (key === "staticRoutes") {
62
+ const staticRoutes = scaffolds.staticRoutes;
63
+ if (staticRoutes === undefined) {
64
+ return;
65
+ }
66
+ asList(staticRoutes).map(function (route) {
67
+ debug(`Setting up '${route.path}' as a static asset route`);
68
+ scaffoldHandler.addStaticAssetRoute(server, route);
69
+ });
70
+ } else if (key === "redirectRoutes") {
71
+ const redirectRoutes = scaffolds.redirectRoutes;
72
+ if (redirectRoutes === undefined) {
73
+ return;
74
+ }
75
+ asList(redirectRoutes).map(function (route) {
76
+ debug(`Setting up '${route.path}' as a redirect route`);
77
+ scaffoldHandler.addRedirectRoute(server, route);
78
+ });
79
+ } else {
80
+ // Boundary: every other key is a template route. Its defaults are filled in
81
+ // place, on the definition's own object, as before.
82
+ const scaffold = scaffolds[key] as ScaffoldHandler.TemplateRoute;
83
+ scaffold.path = scaffold.path || `/${key.toLowerCase()}`;
84
+ scaffold.templateName = scaffold.templateName || key;
85
+ scaffold.templateHandler = options.templateHandler || "transomTemplate";
86
+ debug(`Setting up '${scaffold.path}' as a template route to ${scaffold.templateName}`);
87
+ scaffoldHandler.addTemplateRoute(server, scaffold);
88
+ }
89
+ });
90
+ resolve();
91
+ });
92
+ };
93
+ }
94
+
95
+ /** The plugin instance, as `require("@transomjs/transom-scaffold")` has always returned. */
96
+ const transomScaffold = new TransomScaffold();
97
+
98
+ declare namespace transomScaffold {
99
+ type Options = ScaffoldOptions;
100
+ type Definition = ScaffoldDefinition;
101
+ type StaticRoute = ScaffoldHandler.StaticRoute;
102
+ type RedirectRoute = ScaffoldHandler.RedirectRoute;
103
+ type TemplateRoute = ScaffoldHandler.TemplateRouteDefinition;
104
+ type RestifyServeStatic = ScaffoldHandler.RestifyServeStatic;
105
+ type Handler = ScaffoldHandler.Handler;
106
+ type PluginServer = ScaffoldPluginServer;
107
+ }
108
+
109
+ export = transomScaffold;
@@ -0,0 +1,207 @@
1
+ import assert from "assert";
2
+ import path from "path";
3
+ import createDebug from "debug";
4
+ import express from "express";
5
+ import type { NextFunction, Request, RequestHandler, Response } from "express";
6
+
7
+ const debug = createDebug("transom:scaffold");
8
+
9
+ // This file runs from <package>/dist/lib.
10
+ const PACKAGE_ROOT = path.join(__dirname, "..", "..");
11
+
12
+ /**
13
+ * Where static folders are resolved from: the application's root, found by walking up
14
+ * out of node_modules/@transomjs/transom-scaffold. When NODE_ENV is "testing", the
15
+ * package root.
16
+ */
17
+ function assetRoot(): string {
18
+ return `${process.env.NODE_ENV}`.toUpperCase() === "TESTING"
19
+ ? PACKAGE_ROOT
20
+ : path.join(PACKAGE_ROOT, "..", "..", "..");
21
+ }
22
+
23
+ /**
24
+ * Builds the route helpers that transom-scaffold registers (as "transomScaffold" by default).
25
+ * A plain function, so existing `new ScaffoldHandler(...)` calls keep working.
26
+ */
27
+ function ScaffoldHandler(_server?: unknown, _options?: unknown): ScaffoldHandler.Handler {
28
+ /**
29
+ * Serves static assets from a folder under the application root.
30
+ *
31
+ * @param server - The transom server.
32
+ * @param scaffold - An entry from `staticRoutes`.
33
+ */
34
+ function addStaticAssetRoute(server: ScaffoldHandler.RouteServer, scaffold: ScaffoldHandler.StaticRoute): void {
35
+ assert(scaffold.path, `TransomScaffold staticRoute requires "path" to be specified.`);
36
+
37
+ const contentFolder = scaffold.folder || path.sep;
38
+ const defaultAsset = scaffold.defaultAsset || "index.html";
39
+ const appendRequestPath = scaffold.appendRequestPath === false ? false : true; // default true
40
+
41
+ const directory = path.join(assetRoot(), contentFolder);
42
+ debug(`Path ${scaffold.path} is serving static assets from ${directory}`);
43
+
44
+ let staticMiddleware: RequestHandler;
45
+ if (scaffold.serveStatic && scaffold.serveStatic !== express.static) {
46
+ // A custom serveStatic function (Restify style) gets Restify-style options.
47
+ // Boundary: it's documented as taking a single options object.
48
+ const restifyServeStatic = scaffold.serveStatic as ScaffoldHandler.RestifyServeStatic;
49
+ staticMiddleware = restifyServeStatic({
50
+ directory,
51
+ appendRequestPath,
52
+ default: defaultAsset,
53
+ });
54
+ } else {
55
+ // Express static, with Express-style options.
56
+ staticMiddleware = express.static(directory, {
57
+ index: appendRequestPath ? defaultAsset : false,
58
+ });
59
+ }
60
+
61
+ // Use server.use if available (Express), otherwise fall back to server.get.
62
+ if (typeof server.use === "function") {
63
+ server.use(scaffold.path, staticMiddleware);
64
+ } else {
65
+ server.get(scaffold.path, staticMiddleware);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Adds a GET route that redirects to another URI.
71
+ *
72
+ * @param server - The transom server.
73
+ * @param scaffold - An entry from `redirectRoutes`.
74
+ */
75
+ function addRedirectRoute(server: ScaffoldHandler.RouteServer, scaffold: ScaffoldHandler.RedirectRoute): void {
76
+ assert(
77
+ scaffold.path && scaffold.target,
78
+ `TransomScaffold redirectRoute requires 'path' and 'target' to be specified.`,
79
+ );
80
+
81
+ server.get(scaffold.path, function (_req: Request, res: Response) {
82
+ // An object target can choose a permanent (301) redirect.
83
+ if (typeof scaffold.target === "object") {
84
+ const statusCode = scaffold.target.permanent ? 301 : 302;
85
+ res.redirect(statusCode, scaffold.target.pathname);
86
+ } else {
87
+ res.redirect(scaffold.target);
88
+ }
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Adds a GET route rendered with an HTML template from the registered template handler.
94
+ *
95
+ * @param server - The transom server.
96
+ * @param scaffold - A named entry from the scaffold definition, with its defaults applied.
97
+ */
98
+ function addTemplateRoute(server: ScaffoldHandler.RouteServer, scaffold: ScaffoldHandler.TemplateRoute): void {
99
+ server.get(scaffold.path, function (req: Request, res: Response, next: NextFunction) {
100
+ // Boundary: the template handler registered under this key (transom-ejs-template's renderer).
101
+ const transomTemplate = server.registry.get(scaffold.templateHandler) as ScaffoldHandler.TemplateRenderer;
102
+ const contentType = scaffold.contentType || "text/html";
103
+
104
+ new Promise<string>(function (resolve) {
105
+ // Push request headers and params into the data going to the template.
106
+ const data = scaffold.data || {};
107
+ data.headers = req.headers;
108
+ data.params = req.params;
109
+
110
+ resolve(transomTemplate.renderHtmlTemplate(scaffold.templateName, data));
111
+ })
112
+ .then(function (pageData) {
113
+ res.setHeader("content-type", contentType);
114
+ res.end(pageData);
115
+ // Call next() for test compatibility (tests expect it to be called).
116
+ if (typeof next === "function") {
117
+ // Use setTimeout to ensure the response is sent first.
118
+ setTimeout(() => next(), 0);
119
+ }
120
+ })
121
+ .catch(function (err: unknown) {
122
+ next(err);
123
+ });
124
+ });
125
+ }
126
+
127
+ return {
128
+ addStaticAssetRoute,
129
+ addRedirectRoute,
130
+ addTemplateRoute,
131
+ };
132
+ }
133
+
134
+ declare namespace ScaffoldHandler {
135
+ /** The parts of the transom-core server the route helpers use. Any transom-core server fits. */
136
+ interface RouteServer {
137
+ registry: {
138
+ get(key: string): unknown;
139
+ };
140
+ get(path: string, ...handlers: RequestHandler[]): unknown;
141
+ use?(path: string, ...handlers: RequestHandler[]): unknown;
142
+ }
143
+
144
+ /** The options a custom (Restify-style) serveStatic receives. */
145
+ interface RestifyStaticOptions {
146
+ directory: string;
147
+ appendRequestPath: boolean;
148
+ default: string;
149
+ }
150
+
151
+ type RestifyServeStatic = (options: RestifyStaticOptions) => RequestHandler;
152
+
153
+ /** An entry in `staticRoutes`. */
154
+ interface StaticRoute {
155
+ /** The URL path to mount, e.g. "/html". */
156
+ path: string;
157
+ /** Folder under the application root. Defaults to the root itself. */
158
+ folder?: string;
159
+ /** Defaults to "index.html". */
160
+ defaultAsset?: string;
161
+ /** When false, folders don't serve the default asset. Defaults to true. */
162
+ appendRequestPath?: boolean;
163
+ /** Replace express.static, e.g. with a Restify-style serveStatic. */
164
+ serveStatic?: typeof express.static | RestifyServeStatic;
165
+ }
166
+
167
+ /** An entry in `redirectRoutes`. */
168
+ interface RedirectRoute {
169
+ path: string;
170
+ /** A URL (302), or a pathname with `permanent: true` for a 301. */
171
+ target: string | { pathname: string; permanent?: boolean };
172
+ }
173
+
174
+ /** A named entry in the scaffold definition, rendered with an HTML template. */
175
+ interface TemplateRouteDefinition {
176
+ /** Defaults to "/<name in lower case>". */
177
+ path?: string;
178
+ /** Defaults to the entry's name. */
179
+ templateName?: string;
180
+ /** Defaults to "text/html". */
181
+ contentType?: string;
182
+ /** Passed to the template, with the request's `headers` and `params` added. */
183
+ data?: Record<string, unknown>;
184
+ }
185
+
186
+ /** A template route once the plugin has filled in its defaults. */
187
+ interface TemplateRoute extends TemplateRouteDefinition {
188
+ path: string;
189
+ templateName: string;
190
+ /** The registry key of the template renderer. */
191
+ templateHandler: string;
192
+ }
193
+
194
+ /** The registered template renderer, as transom-ejs-template provides it. */
195
+ interface TemplateRenderer {
196
+ renderHtmlTemplate(templateName: string, data: object): string;
197
+ }
198
+
199
+ /** The registered helpers. */
200
+ interface Handler {
201
+ addStaticAssetRoute(server: RouteServer, scaffold: StaticRoute): void;
202
+ addRedirectRoute(server: RouteServer, scaffold: RedirectRoute): void;
203
+ addTemplateRoute(server: RouteServer, scaffold: TemplateRoute): void;
204
+ }
205
+ }
206
+
207
+ export = ScaffoldHandler;
@@ -1,16 +0,0 @@
1
- {
2
- "version": "0.2.0",
3
- "configurations": [{
4
- "type": "node",
5
- "runtimeExecutable": "node",
6
- "request": "launch",
7
- "name": "Debug File Mocha",
8
- "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
9
- "args": ["${file}", "--no-timeouts"],
10
- "internalConsoleOptions": "openOnSessionStart",
11
- "env": {
12
- "NODE_ENV": "testing"
13
- }
14
- }
15
- ]
16
- }
package/index.js DELETED
@@ -1,43 +0,0 @@
1
- 'use strict';
2
- const debug = require('debug')('transom:scaffold');
3
- const ScaffoldHandler = require('./lib/scaffoldHandler');
4
-
5
- function TransomScaffold() {
6
- this.initialize = function (server, options) {
7
- return new Promise(function(resolve, reject){
8
- const scaffoldDefn = server.registry.get('transom-config.definition.scaffold', {});
9
- options.scaffold = Object.assign({}, scaffoldDefn);
10
-
11
- const scaffoldHandler = new ScaffoldHandler(server);
12
- server.registry.set(options.registryKey || 'transomScaffold', scaffoldHandler);
13
-
14
- // Add simple get routes to fetch scaffolded content.
15
- Object.keys(options.scaffold).map(function (key) {
16
- const scaffold = options.scaffold[key];
17
-
18
- if (key === 'staticRoutes') {
19
- const staticRoutes = Array.isArray(scaffold) ? scaffold : [scaffold];
20
- staticRoutes.map(function (route) {
21
- debug(`Setting up '${route.path}' as a static asset route`);
22
- scaffoldHandler.addStaticAssetRoute(server, route);
23
- });
24
- } else if (key === 'redirectRoutes') {
25
- const redirectRoutes = Array.isArray(scaffold) ? scaffold : [scaffold];
26
- redirectRoutes.map(function (route) {
27
- debug(`Setting up '${route.path}' as a redirect route`);
28
- scaffoldHandler.addRedirectRoute(server, route);
29
- });
30
- } else {
31
- scaffold.path = scaffold.path || `/${key.toLowerCase()}`;
32
- scaffold.templateName = scaffold.templateName || key;
33
- scaffold.templateHandler = options.templateHandler || 'transomTemplate';
34
- debug(`Setting up '${scaffold.path}' as a template route to ${scaffold.templateName}`);
35
- scaffoldHandler.addTemplateRoute(server, scaffold);
36
- }
37
- });
38
- resolve();
39
- });
40
- }
41
- }
42
-
43
- module.exports = new TransomScaffold();