Skip to content

Commit

Permalink
Merge pull request #149 from supercharge/view-share-data
Browse files Browse the repository at this point in the history
Views: share data
  • Loading branch information
marcuspoehls authored Nov 3, 2023
2 parents bc9ab50 + d8a6b7d commit b7f62ca
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 12 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
- add `md5` method: create a Node.js MD5 hash
- add `sha256` method: create a Node.js SHA256 hash
- add `sha512` method: create a Node.js SHA512 hash
- `@supercharge/view`
- add `share` method: share data across view templates. This is useful for data like your app name
- add `sharedData` method: returns the shared data

### Updated
- bump dependencies
Expand Down Expand Up @@ -39,6 +42,8 @@
- `@supercharge/hashing`
- removed `bcrypt` package from being installed automatically, users must install it explicitely when the hashing driver should use bcrypt
- hashing options require a factory function to return the hash driver constructor
- `@supercharge/view`
- export for the view response changed from `View` to `ViewResponse`
- `@supercharge/http`
- the `RequestHeaderBag` extends the `InputBag` which changes the behavior of the `has(key)` method: it returns `false` if the stored value is `undefined` and returns `true` otherwise

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ export { Dict } from './utils/dict.js'

export { ViewConfig } from './view/config.js'
export { ViewConfigBuilder } from './view/config-builder.js'
export { ViewEngine } from './view/engine.js'
export { ViewEngine, ViewSharedData } from './view/engine.js'
export { ViewBuilderCallback } from './view/response.js'
export { ViewResponseConfig } from './view/response-config.js'
23 changes: 23 additions & 0 deletions packages/contracts/src/view/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
import { HelperDelegate } from 'handlebars'
import { ViewResponseConfig } from './response-config.js'

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ViewSharedData {
//
}

export interface ViewEngine {
/**
* Boot the view engine. This may contain loading partial views
Expand Down Expand Up @@ -40,4 +45,22 @@ export interface ViewEngine {
*/
hasHelper (name: string): boolean

/**
* Share a given state of data to all views, across HTTP requests.
*
* @example
* ```
* import { View } from '@supercharge/facades'
*
* View.share({ key: 'value' })
* ```
*/
share<K extends keyof ViewSharedData> (key: K, value: ViewSharedData[K]): this
share (values: Partial<ViewSharedData>): this
share (key: string | any, value?: any): this

/**
* Returns the shared data.
*/
sharedData(): Record<string, any>
}
47 changes: 47 additions & 0 deletions packages/view/src/engines/handlebars/base-compiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

import { ViewSharedData } from '@supercharge/contracts'

export class ViewBaseCompiler {
/**
* Stores the data that is available to all view templates.
*/
private state: Record<string, any> = {}

/**
* Share a given state of data to all views, across HTTP requests.
*
* @example
* ```
* import { View } from '@supercharge/facades'
*
* View.share({ key: 'value' })
* ```
*/
share<K extends keyof ViewSharedData> (key: K, value: ViewSharedData[K]): this
share (values: Partial<ViewSharedData>): this
share (key: string | any, value?: any): this {
if (this.isObject(key)) {
this.state = { ...this.state, ...key }
} else if (typeof key === 'string') {
this.state[key] = value
} else {
throw new Error(`Failed to set shared view state: the first argument is neither a string nor an object. Received "${typeof key}"`)
}

return this
}

/**
* Returns the shared data.
*/
sharedData (): Record<string, any> {
return this.state
}

/**
* Determine whether the given `input` is an object.
*/
private isObject (input: any): input is Record<string, any> {
return !!input && input.constructor.name === 'Object'
}
}
5 changes: 4 additions & 1 deletion packages/view/src/engines/handlebars/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import { Collect } from '@supercharge/collections'
import Handlebars, { HelperDelegate } from 'handlebars'
import { resolveDefaultImport, tap } from '@supercharge/goodies'
import { Logger, ViewConfig, ViewEngine, ViewResponseConfig } from '@supercharge/contracts'
import { ViewBaseCompiler } from './base-compiler.js'

export class HandlebarsCompiler implements ViewEngine {
export class HandlebarsCompiler extends ViewBaseCompiler implements ViewEngine {
/**
* The handlebars renderer instance.
*/
Expand All @@ -33,6 +34,8 @@ export class HandlebarsCompiler implements ViewEngine {
* Create a new renderer instance.
*/
constructor (logger: Logger, config: ViewConfig['handlebars']) {
super()

this.config = config
this.logger = logger
this.extension = '.hbs'
Expand Down
4 changes: 2 additions & 2 deletions packages/view/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

export { HandlebarsCompiler } from './engines/index.js'
export { ViewManager } from './view-manager.js'
export { ViewConfigBuilder } from './view-config-builder.js'
export { View } from './view-response.js'
export { ViewManager } from './view-manager.js'
export { ViewResponse } from './view-response.js'
export { ViewServiceProvider } from './view-service-provider.js'
27 changes: 26 additions & 1 deletion packages/view/src/view-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { tap } from '@supercharge/goodies'
import { HelperDelegate } from 'handlebars'
import { Manager } from '@supercharge/manager'
import { HandlebarsCompiler } from './engines/handlebars/index.js'
import { Application, ViewConfig, ViewEngine, ViewResponseConfig } from '@supercharge/contracts'
import { Application, ViewConfig, ViewEngine, ViewResponseConfig, ViewSharedData } from '@supercharge/contracts'

export class ViewManager extends Manager<Application> implements ViewEngine {
/**
Expand Down Expand Up @@ -102,6 +102,31 @@ export class ViewManager extends Manager<Application> implements ViewEngine {
return this.driver().hasHelper(name)
}

/**
* Share a given state of data to all views, across HTTP requests.
*
* @example
* ```
* import { View } from '@supercharge/facades'
*
* View.share({ key: 'value' })
* ```
*/
share<K extends keyof ViewSharedData> (key: K, value: ViewSharedData[K]): this
share (values: Partial<ViewSharedData>): this
share (key: string | any, value?: any): this {
this.driver().share(key, value)

return this
}

/**
* Returns the shared data.
*/
sharedData (): Record<string, any> {
return this.driver().sharedData()
}

/**
* Boot the view engine driver.
*/
Expand Down
17 changes: 11 additions & 6 deletions packages/view/src/view-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { ViewConfigBuilder } from './view-config-builder.js'
import { HttpResponse, ViewBuilderCallback, ViewEngine, ViewResponseConfig } from '@supercharge/contracts'

export class View {
export class ViewResponse {
/**
* Stores the HTTP response instance.
*/
Expand All @@ -11,14 +11,14 @@ export class View {
/**
* Stores the view engine instance.
*/
private readonly view: ViewEngine
private readonly viewEngine: ViewEngine

/**
* Create a new view manager instance.
*/
constructor (response: HttpResponse, view: ViewEngine) {
constructor (response: HttpResponse, viewEngine: ViewEngine) {
this.response = response
this.view = view
this.viewEngine = viewEngine
}

/**
Expand All @@ -41,7 +41,12 @@ export class View {
* Assigns the rendered HTML of the given `template` as the response payload.
*/
private async renderView (template: string, data?: any, viewBuilder?: ViewBuilderCallback): Promise<string> {
const viewData = { ...this.response.state().all(), ...data }
const viewData = {
...this.response.state().all(),
...this.viewEngine.sharedData(),
...data
}

const viewConfig: ViewResponseConfig = {}

if (typeof viewBuilder === 'function') {
Expand All @@ -50,6 +55,6 @@ export class View {
)
}

return await this.view.render(template, viewData, viewConfig)
return await this.viewEngine.render(template, viewData, viewConfig)
}
}
2 changes: 1 addition & 1 deletion packages/view/src/view-service-provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import { ViewManager } from './view-manager.js'
import { ViewResponse } from './view-response.js'
import { ServiceProvider } from '@supercharge/support'
import { View as ViewResponse } from './view-response.js'
import { HttpResponse, HttpResponseCtor, ViewBuilderCallback } from '@supercharge/contracts'

/**
Expand Down

0 comments on commit b7f62ca

Please sign in to comment.