structured-fw 0.9.8 → 0.9.9

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.
@@ -18,3 +18,4 @@ export declare function attributeValueEscape(str: string): string;
18
18
  export declare function isObject(item: any): boolean;
19
19
  export declare function equalDeep(a: LooseObject, b: LooseObject): boolean;
20
20
  export declare function mergeDeep(target: any, ...sources: Array<any>): LooseObject;
21
+ export declare function stripBOM(str: string): string;
@@ -335,3 +335,7 @@ export function mergeDeep(target, ...sources) {
335
335
  }
336
336
  return mergeDeep(target, ...sources);
337
337
  }
338
+ export function stripBOM(str) {
339
+ const includesBOM = str.charCodeAt(0) === 0xFEFF;
340
+ return includesBOM ? str.slice(1) : str;
341
+ }
@@ -59,7 +59,7 @@ export class Component extends EventEmitter {
59
59
  }
60
60
  this.dom.setAttribute(`data-${this.document.application.config.components.componentNameAttribute}`, this.name);
61
61
  if (typeof this.attributes.componentId !== 'string') {
62
- this.id = this.document.allocateId(this);
62
+ this.id = this.document.allocateId();
63
63
  this.dom.setAttribute('data-component-id', this.id);
64
64
  }
65
65
  else {
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
2
  import * as path from 'node:path';
3
+ import { stripBOM } from '../Util.js';
3
4
  export class Components {
4
5
  constructor(app) {
5
6
  this.components = {};
@@ -72,7 +73,10 @@ export class Components {
72
73
  return this.components[name.toUpperCase()] || null;
73
74
  }
74
75
  loadHTML(path) {
75
- return this.stripComments(readFileSync(path).toString());
76
+ const html = readFileSync(path, {
77
+ encoding: 'utf-8'
78
+ }).toString();
79
+ return this.stripComments(stripBOM(html).replace(/\r/g, ''));
76
80
  }
77
81
  stripComments(html) {
78
82
  return html.replaceAll(/<!--(?!-?>)(?!.*--!>)(?!.*<!--(?!>)).*?(?<!<!-)-->/g, '');
@@ -20,7 +20,7 @@ export declare class Document extends Component<{
20
20
  initInitializers(): Record<string, string>;
21
21
  private initClientConfig;
22
22
  toString(): string;
23
- allocateId(component: Component): string;
23
+ allocateId(): string;
24
24
  loadView(pathRelative: string, data?: LooseObject): Promise<boolean>;
25
25
  loadComponent(componentName: string, data?: LooseObject): Promise<void>;
26
26
  }
@@ -1,9 +1,9 @@
1
- import { Md5 } from 'ts-md5';
2
1
  import { DocumentHead } from './DocumentHead.js';
3
2
  import { Component } from './Component.js';
4
- import { attributeValueToString, randomString } from '../Util.js';
3
+ import { attributeValueToString, stripBOM } from '../Util.js';
5
4
  import path from 'node:path';
6
5
  import { existsSync, readFileSync } from 'node:fs';
6
+ import { randomUUID } from 'node:crypto';
7
7
  export class Document extends Component {
8
8
  constructor(app, title, ctx) {
9
9
  super('root');
@@ -63,26 +63,8 @@ export class Document extends Component {
63
63
  </body>
64
64
  </html>`;
65
65
  }
66
- allocateId(component) {
67
- if (!this.componentIds) {
68
- this.componentIds = [];
69
- }
70
- let id = Md5.hashStr(`${component.name}:${'id' in component.attributes ? component.attributes.id : `${component.path.join('/')}:${JSON.stringify(component.attributesRaw)}`}`);
71
- if (this.componentIds.includes(id)) {
72
- let current = component.parent;
73
- do {
74
- if (current === null || current.isRoot) {
75
- console.error(`Could not define an unique ID for component ${component.name}, path: ${component.path}`);
76
- id = randomString(16);
77
- }
78
- else {
79
- id += '-' + Md5.hashStr(current.dom.outerHTML);
80
- }
81
- current = current?.parent || null;
82
- } while (this.componentIds.includes(id));
83
- }
84
- this.componentIds.push(id);
85
- return id;
66
+ allocateId() {
67
+ return randomUUID();
86
68
  }
87
69
  async loadView(pathRelative, data) {
88
70
  const viewPath = path.resolve('../' + this.application.config.components.path + '/' + pathRelative + (pathRelative.endsWith('.html') ? '' : '.html'));
@@ -90,8 +72,10 @@ export class Document extends Component {
90
72
  console.warn(`Couldn't load document ${this.document.head.title}: ${viewPath}`);
91
73
  return false;
92
74
  }
93
- const html = readFileSync(viewPath).toString();
94
- await this.init(html, data);
75
+ const html = readFileSync(viewPath, {
76
+ encoding: 'utf-8'
77
+ }).toString();
78
+ await this.init(stripBOM(html).replace(/\r/g, ''), data);
95
79
  return true;
96
80
  }
97
81
  async loadComponent(componentName, data) {
@@ -14,6 +14,7 @@ export declare class HTMLParser {
14
14
  private lastChar;
15
15
  parse(): boolean;
16
16
  private line;
17
+ private lineChar;
17
18
  private isLetter;
18
19
  private isNumber;
19
20
  dom(): DOMFragment;
@@ -181,6 +181,9 @@ export class HTMLParser {
181
181
  line() {
182
182
  return this.html.substring(0, this.offset).split('\n').length;
183
183
  }
184
+ lineChar() {
185
+ return this.offset % this.html.split('\n').slice(0, this.line()).length;
186
+ }
184
187
  isLetter(charCode) {
185
188
  const isLowerCase = charCode > 96 && charCode < 123;
186
189
  if (isLowerCase) {
@@ -199,6 +202,11 @@ export class HTMLParser {
199
202
  return this.fragment;
200
203
  }
201
204
  error(message) {
202
- return new Error(`HTMLParser: ${message}\nLine ${this.line()}\nHTML:\n${this.html}\n`);
205
+ return new Error(`
206
+ HTMLParser: ${message}
207
+ Line ${this.line()}, col ${this.lineChar()}
208
+ Char ${this.char()}, code ${this.char().charCodeAt(0)}
209
+ HTML:
210
+ ${this.html}`);
203
211
  }
204
212
  }
package/package.json CHANGED
@@ -2,7 +2,12 @@
2
2
  "name": "structured-fw",
3
3
  "displayName": "Structured framework",
4
4
  "description": "Production-tested Node.js framework for creating performant server-side rendered web apps and APIs, with a sane amount of client side abstraction.",
5
- "keywords": ["typescript", "framework", "ssr", "component"],
5
+ "keywords": [
6
+ "typescript",
7
+ "framework",
8
+ "ssr",
9
+ "component"
10
+ ],
6
11
  "repository": {
7
12
  "type": "git",
8
13
  "url": "git+https://github.com/julijan/structured"
@@ -14,7 +19,7 @@
14
19
  "license": "MIT",
15
20
  "type": "module",
16
21
  "main": "build/index",
17
- "version": "0.9.8",
22
+ "version": "0.9.9",
18
23
  "scripts": {
19
24
  "develop": "tsc --watch",
20
25
  "startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,css index.js",
@@ -42,7 +47,7 @@
42
47
  "README.md",
43
48
  "LICENSE"
44
49
  ],
45
- "exports" : {
50
+ "exports": {
46
51
  "./Types": "./build/system/Types.js",
47
52
  "./Symbols": "./build/system/Symbols.js",
48
53
  "./Util": "./build/system/Util.js",