@dosgato/templating 0.0.41 → 0.0.42

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.
@@ -188,11 +188,6 @@ export declare type AnyMigration = ComponentMigration | PageMigration | DataMigr
188
188
  export declare type LinkGatheringFn = (data: any) => LinkDefinition[];
189
189
  export declare type FulltextGatheringFn = (data: any) => string[];
190
190
  export declare type GraphQLQueryFn = <T>(query: string, variables?: any) => Promise<T>;
191
- /**
192
- * This function is used by API template definitions to help them identify links inside large blocks
193
- * of text and return them for indexing.
194
- */
195
- export declare function extractLinksFromText(text: string | undefined): LinkDefinition[];
196
191
  /**
197
192
  * This function is used by API template definitions to help them identify all the searchable
198
193
  * words in a large block of text and return them for indexing.
@@ -5,16 +5,6 @@ export var ValidationMessageType;
5
5
  ValidationMessageType["WARNING"] = "warning";
6
6
  ValidationMessageType["SUCCESS"] = "success";
7
7
  })(ValidationMessageType || (ValidationMessageType = {}));
8
- /**
9
- * This function is used by API template definitions to help them identify links inside large blocks
10
- * of text and return them for indexing.
11
- */
12
- export function extractLinksFromText(text) {
13
- if (!text)
14
- return [];
15
- const matches = text.matchAll(/{.*"type"\s?:\s+"\w+".*?}/gi);
16
- return Array.from(matches).map(m => JSON.parse(m[0]));
17
- }
18
8
  /**
19
9
  * This function is used by API template definitions to help them identify all the searchable
20
10
  * words in a large block of text and return them for indexing.
@@ -12,39 +12,6 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
12
12
  * Provide this when you create a template to identify what you are defining.
13
13
  */
14
14
  static templateKey: string;
15
- /**
16
- * These functions will be provided by the rendering server
17
- */
18
- static editBar: (path: string, opts: EditBarOpts) => string;
19
- static newBar: (path: string, opts: EditBarOpts) => string;
20
- areas: Map<string, Component<any, any, any>[]>;
21
- data: Omit<DataType, 'areas'>;
22
- fetched: FetchedType;
23
- renderCtx: RenderContextType;
24
- path: string;
25
- parent?: Component;
26
- page?: Page;
27
- hadError: boolean;
28
- autoLabel: () => string;
29
- autoNewLabel: () => string;
30
- /**
31
- * When hydrating an inherited component, the renderer will set this to the id of the page it
32
- * came from. You may use this information in any of the phases to alter your behavior if needed.
33
- *
34
- * For instance, you may decide that your fetch function needs some extra information from the
35
- * originating page instead of the page you're being inherited into (your `this.page` will
36
- * be the page currently being rendered, NOT the page the inheritable component came from).
37
- *
38
- * This property is also used to alter the edit bar. Inherited components may never be edited
39
- * except on their original page, so the edit bar will render with a link to the original page.
40
- */
41
- inheritedFrom?: string;
42
- /**
43
- * This property will be set during page render and you may refer to it at any time to
44
- * determine whether you are doing your work in edit mode or regular rendering mode.
45
- * The editBar and newBar methods will automatically use it to blank out the editing UI.
46
- */
47
- editMode: boolean;
48
15
  /**
49
16
  * The rendering server will provide an instance of the APIClient interface so that
50
17
  * you can run any API GraphQL query you like in your `fetch` function. There are also
@@ -56,32 +23,23 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
56
23
  */
57
24
  api: APIClient;
58
25
  /**
59
- * Retrieve the data for the root page of the page this component is on. Useful for
60
- * implementing inheritance schemes.
61
- *
62
- * This function will be provided by the rendering service.
63
- *
64
- * Do NOT mutate the data returned by this function, as it may be cached and given to
65
- * other Component instances.
26
+ * This property will be set during page render and you may refer to it at any time to
27
+ * determine whether you are doing your work in edit mode or regular rendering mode.
28
+ * The editBar and newBar methods will automatically use it to blank out the editing UI.
66
29
  */
67
- getRootPageData: () => Promise<PageData>;
30
+ editMode: boolean;
68
31
  /**
69
- * Retrieve the data for all ancestor pages of the page this component is on. Useful
70
- * for implementing inheritance schemes.
32
+ * When hydrating an inherited component, the renderer will set this to the id of the page it
33
+ * came from. You may use this information in any of the phases to alter your behavior if needed.
71
34
  *
72
- * This function will be provided by the rendering service.
35
+ * For instance, you may decide that your fetch function needs some extra information from the
36
+ * originating page instead of the page you're being inherited into (your `this.page` will
37
+ * be the page currently being rendered, NOT the page the inheritable component came from).
73
38
  *
74
- * Do NOT mutate the data returned by this function, as it may be cached and given to
75
- * other Component instances.
76
- */
77
- getAncestorPageData: () => Promise<PageData[]>;
78
- /**
79
- * Some components may be inheritable to subpages within the same site. For instance, a site's
80
- * social media links may appear on every page's footer. To accomplish this in your template,
81
- * you need to fetch the data in your fetch phase and then call this function within your fetch
82
- * to let the renderer know it needs to hydrate them and include them in the render.
39
+ * This property is also used to alter the edit bar. Inherited components may never be edited
40
+ * except on their original page, so the edit bar will render with a link to the original page.
83
41
  */
84
- registerInherited: (area: string, components: ComponentData[], top?: true) => void;
42
+ inheritedFrom?: string;
85
43
  /**
86
44
  * The first phase of rendering a component is the fetch phase. Each component may
87
45
  * provide a fetch method that looks up data it needs from external sources. This step
@@ -91,12 +49,27 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
91
49
  * Place any needed data into the return object, and it will be available to you as `this.fetched`
92
50
  * during the rendering phase.
93
51
  *
94
- * Note that this.page will be available, and getRootPageData and getAncestorPageData are
95
- * available in case there is a need for inheritance. If you need to inherit entire components,
96
- * you may add them to your this.areas map, e.g.
97
- * `this.areas.get('myarea').push(new Component(inheritedData, this.path + '/myarea/inherit1', this))`
52
+ * Note that `this.page` will be available, and `this.api` has dataloaded methods for retrieving
53
+ * data from the API if, for instance, you need to inherit information from a parent or root
54
+ * page. If you need to inherit and render entire components from ancestor pages,
55
+ * you must register them. See the comment for `this.registerInherited`
56
+ *
57
+ * Try to minimize the number of round trips you make here, make use of Promise.all and such;
58
+ * remember that the api functions are mostly dataloaded so calling them simultaneously is
59
+ * advantageous where possible.
98
60
  */
99
61
  fetch(): Promise<FetchedType>;
62
+ /**
63
+ * Some components may be inheritable to subpages within the same site. For instance, a site's
64
+ * social media links may appear on every page's footer. To accomplish this in your template,
65
+ * you need to fetch ancestor page data in your fetch phase, identify the component data you want
66
+ * to inherit, and then call this function within your fetch to let the renderer know it needs to
67
+ * process those components (hydrate them, call their fetch functions, and include them in the render).
68
+ *
69
+ * The inherited components will be added to the appropriate area's array in the renderedAreas
70
+ * parameter of your render function.
71
+ */
72
+ registerInherited: (area: string, components: ComponentData[], top?: true) => void;
100
73
  /**
101
74
  * The second phase of rendering a component is the context phase. This step is TOP-DOWN and
102
75
  * NON-MUTATING. Each component will receive the parent component's context and then pass a
@@ -133,14 +106,6 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
133
106
  * will be skipped.
134
107
  */
135
108
  renderVariation(extension: string, renderedAreas: Map<string, string>): string;
136
- constructor(data: DataType, path: string, parent: Component | undefined, editMode: boolean);
137
- /**
138
- * For logging errors during rendering without crashing the render. If your fetch, setContext,
139
- * render, or renderVariation functions throw, the error will be logged but the page render will
140
- * continue. You generally do not need to use this function, just throw when appropriate.
141
- */
142
- logError(e: Error): void;
143
- protected passError(e: Error, path: string): void;
144
109
  renderComponents(components?: RenderedComponent[], opts?: {
145
110
  hideInheritBars?: boolean;
146
111
  }): string;
@@ -152,6 +117,8 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
152
117
  *
153
118
  * This is evaluated after the fetch and context phases but before the rendering phase. If you
154
119
  * need any async data to make this determination, be sure to fetch it during the fetch phase.
120
+ *
121
+ * You should check `this.editMode` if you need to load CSS that alters edit bars.
155
122
  */
156
123
  cssBlocks(): string[];
157
124
  /**
@@ -195,6 +162,31 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
195
162
  * Generally should not be overridden - override newLabel and newClass instead
196
163
  */
197
164
  newBar(areaName: string, opts?: EditBarOpts): string;
165
+ /**
166
+ * These functions will be provided by the rendering server to assist in the
167
+ * rendering process.
168
+ */
169
+ static editBar: (path: string, opts: EditBarOpts) => string;
170
+ static newBar: (path: string, opts: EditBarOpts) => string;
171
+ static repairHTML: (html: string) => string;
172
+ constructor(data: DataType, path: string, parent: Component | undefined, editMode: boolean);
173
+ areas: Map<string, Component<any, any, any>[]>;
174
+ data: Omit<DataType, 'areas'>;
175
+ fetched: FetchedType;
176
+ renderCtx: RenderContextType;
177
+ path: string;
178
+ parent?: Component;
179
+ page?: Page;
180
+ hadError: boolean;
181
+ autoLabel: string;
182
+ autoNewLabel: string;
183
+ /**
184
+ * For logging errors during rendering without crashing the render. If your fetch, setContext,
185
+ * render, or renderVariation functions throw, the error will be logged but the page render will
186
+ * continue. You generally do not need to use this function, just throw when appropriate.
187
+ */
188
+ logError(e: Error): void;
189
+ protected passError(e: Error, path: string): void;
198
190
  }
199
191
  export interface PageRecord<DataType extends PageData = PageData> {
200
192
  id: string;
@@ -238,9 +230,18 @@ export interface RenderedComponent<C extends Component = Component> {
238
230
  export declare abstract class Page<DataType extends PageData = any, FetchedType = any, RenderContextType extends ContextBase = any> extends Component<DataType, FetchedType, RenderContextType> {
239
231
  pagePath: string;
240
232
  /**
241
- * we will fill this before rendering, stuff that dosgato knows needs to be added to
242
- * the <head> element
243
- * the page's render function must include it
233
+ * This will be filled by the rendering server. The template properties are described
234
+ * over in apitemplate.ts in the comment for APIPageTemplate.templateProperties.
235
+ *
236
+ * The properties will appear in the GraphQL API and the rendering server will automatically
237
+ * download them and provide them here so all you need to do as a template developer is
238
+ * reference the values in your fetch/setContext/render functions.
239
+ */
240
+ templateProperties: any;
241
+ /**
242
+ * This is a bunch of javascript and CSS and meta tags managed by the DosGato engine. It will
243
+ * be filled by the rendering server and your render function for your page template
244
+ * should place include it in the <head> element
244
245
  */
245
246
  headContent: string;
246
247
  protected passError(e: Error, path: string): void;
package/dist/component.js CHANGED
@@ -12,9 +12,9 @@ export class Component extends ResourceProvider {
12
12
  // a Component will also construct/hydrate all its child components
13
13
  constructor(data, path, parent, editMode) {
14
14
  super();
15
- // properties for use during hydration, you do not have to provide these when
15
+ // Properties provided during the rendering process. You do not have to provide these when
16
16
  // building a template, but you can use them in the functions you do provide
17
- this.areas = new Map();
17
+ this.areas = new Map(); // a Map of area names and the array of hydrated components in each
18
18
  this.editMode = editMode;
19
19
  this.parent = parent;
20
20
  const { areas, ...ownData } = data;
@@ -37,10 +37,14 @@ export class Component extends ResourceProvider {
37
37
  * Place any needed data into the return object, and it will be available to you as `this.fetched`
38
38
  * during the rendering phase.
39
39
  *
40
- * Note that this.page will be available, and getRootPageData and getAncestorPageData are
41
- * available in case there is a need for inheritance. If you need to inherit entire components,
42
- * you may add them to your this.areas map, e.g.
43
- * `this.areas.get('myarea').push(new Component(inheritedData, this.path + '/myarea/inherit1', this))`
40
+ * Note that `this.page` will be available, and `this.api` has dataloaded methods for retrieving
41
+ * data from the API if, for instance, you need to inherit information from a parent or root
42
+ * page. If you need to inherit and render entire components from ancestor pages,
43
+ * you must register them. See the comment for `this.registerInherited`
44
+ *
45
+ * Try to minimize the number of round trips you make here, make use of Promise.all and such;
46
+ * remember that the api functions are mostly dataloaded so calling them simultaneously is
47
+ * advantageous where possible.
44
48
  */
45
49
  async fetch() {
46
50
  return undefined;
@@ -78,19 +82,6 @@ export class Component extends ResourceProvider {
78
82
  renderVariation(extension, renderedAreas) {
79
83
  return Array.from(renderedAreas.values()).join('');
80
84
  }
81
- /**
82
- * For logging errors during rendering without crashing the render. If your fetch, setContext,
83
- * render, or renderVariation functions throw, the error will be logged but the page render will
84
- * continue. You generally do not need to use this function, just throw when appropriate.
85
- */
86
- logError(e) {
87
- this.hadError = true;
88
- this.passError(e, this.path);
89
- }
90
- // helper function for recursively passing the error up until it reaches the page
91
- passError(e, path) {
92
- this.parent?.passError(e, path);
93
- }
94
85
  // helper function to help you print an area, but you can also override this if you
95
86
  // need to do something advanced like wrap each component in a div
96
87
  renderComponents(components = [], opts) {
@@ -104,6 +95,8 @@ export class Component extends ResourceProvider {
104
95
  *
105
96
  * This is evaluated after the fetch and context phases but before the rendering phase. If you
106
97
  * need any async data to make this determination, be sure to fetch it during the fetch phase.
98
+ *
99
+ * You should check `this.editMode` if you need to load CSS that alters edit bars.
107
100
  */
108
101
  cssBlocks() {
109
102
  return Array.from(this.constructor.cssBlocks.keys());
@@ -120,16 +113,12 @@ export class Component extends ResourceProvider {
120
113
  *
121
114
  * For instance, you could return this.data.title
122
115
  */
123
- editLabel() {
124
- return undefined;
125
- }
116
+ editLabel() { return undefined; }
126
117
  /**
127
118
  * Components may override this function to give their edit bars a custom
128
119
  * CSS class
129
120
  */
130
- editClass() {
131
- return undefined;
132
- }
121
+ editClass() { return undefined; }
133
122
  /**
134
123
  * Components may override this function to give their new bars a custom
135
124
  * label
@@ -137,23 +126,19 @@ export class Component extends ResourceProvider {
137
126
  * For instance, an area that only accepts 'layout' components could
138
127
  * return "Add Layout"
139
128
  */
140
- newLabel(areaName) {
141
- return undefined;
142
- }
129
+ newLabel(areaName) { return undefined; }
143
130
  /**
144
131
  * Components may override this function to give their new bars a custom
145
132
  * CSS class
146
133
  */
147
- newClass(areaName) {
148
- return undefined;
149
- }
134
+ newClass(areaName) { return undefined; }
150
135
  /**
151
136
  * Components may override this function to provide a custom edit bar
152
137
  *
153
138
  * Generally should not be overridden - override editLabel and editClass instead
154
139
  */
155
140
  editBar(opts = {}) {
156
- opts.label ?? (opts.label = this.editLabel() ?? this.autoLabel());
141
+ opts.label ?? (opts.label = this.editLabel() ?? this.autoLabel);
157
142
  opts.extraClass ?? (opts.extraClass = this.editClass());
158
143
  opts.editMode ?? (opts.editMode = this.editMode);
159
144
  opts.inheritedFrom ?? (opts.inheritedFrom = this.inheritedFrom);
@@ -165,12 +150,25 @@ export class Component extends ResourceProvider {
165
150
  * Generally should not be overridden - override newLabel and newClass instead
166
151
  */
167
152
  newBar(areaName, opts = {}) {
168
- opts.label ?? (opts.label = this.newLabel(areaName) ?? this.autoNewLabel());
153
+ opts.label ?? (opts.label = this.newLabel(areaName) ?? this.autoNewLabel);
169
154
  opts.extraClass ?? (opts.extraClass = this.newClass(areaName));
170
155
  opts.editMode ?? (opts.editMode = this.editMode);
171
156
  opts.inheritedFrom ?? (opts.inheritedFrom = this.inheritedFrom);
172
157
  return Component.newBar([this.path, 'areas', areaName].filter(isNotBlank).join('.'), opts);
173
158
  }
159
+ /**
160
+ * For logging errors during rendering without crashing the render. If your fetch, setContext,
161
+ * render, or renderVariation functions throw, the error will be logged but the page render will
162
+ * continue. You generally do not need to use this function, just throw when appropriate.
163
+ */
164
+ logError(e) {
165
+ this.hadError = true;
166
+ this.passError(e, this.path);
167
+ }
168
+ // helper function for recursively passing the error up until it reaches the page
169
+ passError(e, path) {
170
+ this.parent?.passError(e, path);
171
+ }
174
172
  }
175
173
  export class Page extends Component {
176
174
  constructor(page, editMode) {
package/dist/links.d.ts CHANGED
@@ -63,3 +63,13 @@ export interface DataFolderLink {
63
63
  path: string;
64
64
  }
65
65
  export declare type LinkDefinition = AssetLink | AssetFolderLink | PageLink | WebLink | DataLink | DataFolderLink;
66
+ /**
67
+ * This function is used by template definitions to help them identify links inside large blocks
68
+ * of text and return them for indexing, and by render definitions to help replace them with the actual URLs
69
+ */
70
+ export declare function extractLinksFromText(text: string | undefined): LinkDefinition[];
71
+ /**
72
+ * This function is used by render definitions to replace links in large blocks with the actual
73
+ * URLs they point to at render time.
74
+ */
75
+ export declare function replaceLinksInText(text: string, resolved: Map<string, string>): string;
package/dist/links.js CHANGED
@@ -1 +1,19 @@
1
- export {};
1
+ const LinkRegex = /{.*"type"\s?:\s+"\w+".*?}/g;
2
+ /**
3
+ * This function is used by template definitions to help them identify links inside large blocks
4
+ * of text and return them for indexing, and by render definitions to help replace them with the actual URLs
5
+ */
6
+ export function extractLinksFromText(text) {
7
+ if (!text)
8
+ return [];
9
+ const matches = text.matchAll(LinkRegex);
10
+ return Array.from(matches).map(m => JSON.parse(m[0]));
11
+ }
12
+ /**
13
+ * This function is used by render definitions to replace links in large blocks with the actual
14
+ * URLs they point to at render time.
15
+ */
16
+ export function replaceLinksInText(text, resolved) {
17
+ // TODO: figure out a broken link to use instead of '#', so it can be detected later
18
+ return text.replace(LinkRegex, m => resolved.get(m) ?? '#');
19
+ }
package/dist/render.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ContextBase, DataData, PageData } from './component.js';
1
+ import { ContextBase, DataData, PageData, PageRecord } from './component.js';
2
2
  import { AssetLink, DataFolderLink, DataLink, LinkDefinition } from './links.js';
3
3
  export declare function printHeader(ctx: ContextBase, content: string): string;
4
4
  export declare function advanceHeader(ctx: ContextBase, content?: string): {
@@ -42,7 +42,7 @@ export interface APIClient {
42
42
  */
43
43
  query: <T = any>(query: string, variables?: any) => Promise<T>;
44
44
  /**
45
- * This function will be provided by the rendering server and should be used inside your fetch,
45
+ * This function will be provided by the rendering server and should be used inside your fetch
46
46
  * method to convert a link, as input by a user, into a URL suitable for an href, or optionally
47
47
  * an absolute URL suitable for a backend http request or non-HTML document like an RSS feed.
48
48
  */
@@ -61,23 +61,40 @@ export interface APIClient {
61
61
  * The alt text it returns will be the default alternative text from the asset repository. Alt
62
62
  * text gathered from a template's dialog should generally take precedence (though the dialog may
63
63
  * preload the alt text field with the asset repository default).
64
+ *
65
+ * Will be dataloaded.
64
66
  */
65
67
  getImgAttributes: (link: string | AssetLink, absolute?: boolean) => Promise<PictureAttributes>;
66
- /** Get the data for a specific page. Will be dataloaded. */
68
+ /** Get the data for a specific page.
69
+ *
70
+ * Will be dataloaded.
71
+ */
67
72
  getPageData: ({ id, path }: {
68
73
  id?: string;
69
74
  path?: string;
70
75
  }) => Promise<PageData>;
76
+ /** Get all ancestor pages of a specific page. First array element will be the pagetree root page. */
77
+ getAncestors: ({ id, path }: {
78
+ id?: string;
79
+ path?: string;
80
+ }) => Promise<PageRecord[]>;
81
+ /** Get the pagetree root page from which the specified page descends. */
82
+ getRootPageData: ({ id, path }: {
83
+ id?: string;
84
+ path?: string;
85
+ }) => Promise<PageData>;
71
86
  /**
72
- * Get data items
87
+ * Get data entries by link or folder link
73
88
  *
74
89
  * Returns an array in case link is a DataFolderLink. If link is a DataLink, will return an
75
90
  * array with length <= 1.
76
91
  */
77
92
  getDataByLink: (link: string | DataLink | DataFolderLink) => Promise<DataData[]>;
78
93
  /**
79
- * Get data by full path including site. Use '/global' for global data. If path refers
80
- * to a specific data item, will return an array with length <= 1.
94
+ * Get data entries by full path including site
95
+ *
96
+ * Use '/global' for global data. If path refers to a specific data item, will return
97
+ * an array with length <= 1.
81
98
  */
82
99
  getDataByPath: (templateKey: string, path: string) => Promise<DataData[]>;
83
100
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosgato/templating",
3
- "version": "0.0.41",
3
+ "version": "0.0.42",
4
4
  "description": "A library to support building templates for dosgato CMS.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@iconify/svelte": "^2.2.1",
17
17
  "svelte": "^3.48.0",
18
- "txstate-utils": "^1.7.1"
18
+ "txstate-utils": "^1.7.4"
19
19
  },
20
20
  "devDependencies": {
21
21
  "eslint-config-standard-with-typescript": "^22.0.0",