@rws-framework/client 2.9.3 → 2.9.6

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/README.md CHANGED
@@ -379,8 +379,10 @@ const options?: RWSDecoratorOptions;
379
379
  class WebChat extends RWSViewComponent {
380
380
  @attr tagAttr: string; //HTML tag attr
381
381
  @ngAttr fromNgAttr: string; //HTML attr from angular template
382
+ @externalAttr fromExAttr: string; //HTML attr with change observation
382
383
  @sanitizedAttr htmlAttr: string; //HTML attr that's sanitized with every val change
383
384
  @observable someVar: any; //Var for templates/value change observation
385
+ @externalObservable someExVar: string; //Var for templates/value change observation with external watch
384
386
  }
385
387
  ```
386
388
 
@@ -17,6 +17,8 @@ const _DEFAULT_CONFIG_VARS = {
17
17
  //Universal configs
18
18
  transports: ['websocket'],
19
19
  parted: false,
20
+ devRouteProxy: '/api',
21
+ devApiPort: 3002,
20
22
  plugins: []
21
23
  }
22
24
 
@@ -9,11 +9,13 @@ function getRWSLoaders(packageDir, nodeModulesPath, tsConfigPath){
9
9
 
10
10
  return [
11
11
  {
12
- test: /\.scss$/,
13
- use: [
14
- scssLoader,
15
- ],
16
- },
12
+ test: /\.html$/,
13
+ use: [
14
+ {
15
+ loader: htmlLoader,
16
+ },
17
+ ],
18
+ },
17
19
  {
18
20
  test: /\.(ts)$/,
19
21
  use: [
@@ -33,7 +35,13 @@ function getRWSLoaders(packageDir, nodeModulesPath, tsConfigPath){
33
35
  /\.debug\.ts$/,
34
36
  /\.d\.ts$/,
35
37
  ],
36
- }
38
+ },
39
+ {
40
+ test: /\.scss$/i,
41
+ use: [
42
+ scssLoader,
43
+ ],
44
+ },
37
45
  ]
38
46
  }
39
47
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@rws-framework/client",
3
3
  "private": false,
4
- "version": "2.9.3",
5
- "main": "src/index.ts",
4
+ "version": "2.9.6",
5
+ "main": "src/index.ts",
6
6
  "scripts": {
7
7
  "docs": "typedoc --tsconfig ./tsconfig.json"
8
8
  },
@@ -30,27 +30,36 @@
30
30
  "@microsoft/fast-foundation": "^2.46.2",
31
31
  "@rws-framework/console": "*",
32
32
  "@types/moment": "^2.13.0",
33
- "dragula": "^3.7.3",
34
33
  "deepmerge": "^4.3.1",
34
+ "dragula": "^3.7.3",
35
35
  "he": "^1.2.0",
36
36
  "json5": "^2.2.3",
37
37
  "lodash": "^4.17.21",
38
38
  "moment": "^2.29.4",
39
+ "node-sass": "^7.0.3",
39
40
  "partial-json-parser": "^1.0.0",
40
41
  "reflect-metadata": "^0.1.13",
41
42
  "resolve-url-loader": "^5.0.0",
42
43
  "sanitize-html": "^2.12.1",
43
- "sass": "^1.69.7",
44
+ "sass": "1.69.7",
45
+ "sass-loader": "^13.3.2",
44
46
  "scss-loading-animations": "^1.0.1",
45
47
  "socket.io-client": "^4.7.2",
48
+ "source-map": "^0.7.4",
49
+ "source-map-support": "^0.5.21",
50
+ "stacktrace-gps": "^3.1.2",
51
+ "style-loader": "^3.3.3",
52
+ "terser-webpack-plugin": "^5.3.9",
46
53
  "ts-loader": "^9.4.4",
54
+ "ts-transformer-keys": "^0.4.4",
47
55
  "tsc-watch": "^6.0.4",
48
56
  "tslib": "^2.6.2",
57
+ "uglify-js": "^3.17.4",
49
58
  "upload": "^1.3.2",
50
59
  "url-router": "^13.0.0",
51
60
  "uuid": "^9.0.1",
52
- "v4": "^0.0.1"
53
-
61
+ "v4": "^0.0.1",
62
+ "webpack": "^5.89.0"
54
63
  },
55
64
  "devDependencies": {
56
65
  "@types/dragula": "^3.7.4",
@@ -60,7 +69,8 @@
60
69
  "@types/he": "^1.2.3",
61
70
  "@types/sanitize-html": "^2.11.0",
62
71
  "@types/uuid": "^9.0.7",
63
- "@typescript-eslint/parser": "^5.0.0",
72
+ "@typescript-eslint/parser": "^5.0.0",
73
+ "browser-sync": "^2.29.3",
64
74
  "clean-webpack-plugin": "^4.0.0",
65
75
  "css-loader": "^6.8.1",
66
76
  "css-minimizer-webpack-plugin": "^5.0.1",
@@ -71,12 +81,12 @@
71
81
  "mini-css-extract-plugin": "^2.7.6",
72
82
  "minimatch": "^9.0.4",
73
83
  "node-sass": "^9.0.0",
84
+ "raw-loader": "^4.0.2",
74
85
  "sass-loader": "^13.3.2",
75
86
  "source-map": "^0.7.4",
76
87
  "style-loader": "^3.3.3",
77
88
  "terser-webpack-plugin": "^5.3.9",
78
- "ts-loader": "^9.4.4",
79
- "raw-loader": "^4.0.2",
89
+ "ts-loader": "^9.4.4",
80
90
  "ts-node": "^10.9.1",
81
91
  "tsconfig-paths": "^4.2.0",
82
92
  "tsconfig-paths-webpack-plugin": "^4.1.0",
@@ -86,7 +96,7 @@
86
96
  "typedoc-plugin-rename-defaults": "^0.7.0",
87
97
  "typedoc-theme-hierarchy": "^4.1.2",
88
98
  "typescript": "^5.1.6",
89
- "url-loader": "^4.1.1",
99
+ "url-loader": "^4.1.1",
90
100
  "webpack": "^5.75.0",
91
101
  "webpack-bundle-analyzer": "^4.10.1",
92
102
  "webpack-cli": "^5.1.4",
@@ -5,6 +5,8 @@ const webpack = require('webpack');
5
5
  const { rwsPath, RWSConfigBuilder } = require('@rws-framework/console');
6
6
 
7
7
  const HtmlWebpackPlugin = require('html-webpack-plugin');
8
+ const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
9
+
8
10
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
9
11
  const TerserPlugin = require('terser-webpack-plugin');
10
12
  const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
@@ -24,7 +26,7 @@ const { _DEFAULT_CONFIG } = require('./cfg/_default.cfg');
24
26
  const { info } = require('console');
25
27
 
26
28
  const _MAIN_PACKAGE = rwsPath.findRootWorkspacePath(process.cwd());
27
-
29
+ console.log(process.argv);
28
30
  const RWSWebpackWrapper = async (config) => {
29
31
  const BuildConfigurator = new RWSConfigBuilder(rwsPath.findPackageDir(process.cwd()) + '/.rws.json', {..._DEFAULT_CONFIG, ...config});
30
32
 
@@ -52,6 +54,7 @@ const RWSWebpackWrapper = async (config) => {
52
54
 
53
55
  const devTools = isDev ? (BuildConfigurator.get('devtool') || 'source-map') : false;
54
56
  const devDebug = isDev ? (BuildConfigurator.get('devDebug') || config.devDebug || { build: false }) : null;
57
+ const devRouteProxy = BuildConfigurator.get('devRouteProxy') || config.devRouteProxy;
55
58
 
56
59
  const tsConfigPath = rwsPath.relativize(BuildConfigurator.get('tsConfigPath') || config.tsConfigPath, executionDir);
57
60
  const rwsPlugins = {};
@@ -283,7 +286,7 @@ const RWSWebpackWrapper = async (config) => {
283
286
  sourceMapFilename: '[file].map',
284
287
  },
285
288
  resolve: {
286
- extensions: ['.ts', '.js'],
289
+ extensions: ['.ts', '.js', '.scss', '.css'],
287
290
  modules: modules_setup,
288
291
  alias: {
289
292
  ...aliases
@@ -302,18 +305,25 @@ const RWSWebpackWrapper = async (config) => {
302
305
  cfgExport.optimization = optimConfig;
303
306
  }
304
307
 
305
- if (isHotReload) {
306
- cfgExport.devServer = {
307
- hot: true,
308
- static: publicDir
309
- }
310
- }
308
+
311
309
 
312
310
  for (const pluginKey of Object.keys(rwsPlugins)){
313
311
  const plugin = rwsPlugins[pluginKey];
314
312
  cfgExport = await plugin.onBuild(cfgExport);
315
313
  }
316
314
 
315
+ if(isDev){
316
+ const backendUrl = BuildConfigurator.get('backendUrl') || config.backendUrl;
317
+ const apiPort = BuildConfigurator.get('apiPort') || config.apiPort;
318
+
319
+ if(backendUrl && apiPort){
320
+ // cfgExport.devServer = {
321
+ // hot: true, // Enable hot module replacement
322
+ // open: true, // Automatically open the browser
323
+ // }
324
+ }
325
+ }
326
+
317
327
  return cfgExport;
318
328
  }
319
329
 
package/src/client.ts CHANGED
@@ -23,6 +23,7 @@ import ComponentHelper, { ComponentHelperStatic, RWSInfoType } from './client/co
23
23
  import ServicesHelper from './client/services';
24
24
  import ConfigHelper from './client/config';
25
25
  import { DefaultRWSPluginOptionsType, RWSPlugin } from './plugins/_plugin';
26
+ import { IStaticRWSPlugin } from './types/IRWSPlugin'
26
27
 
27
28
  interface IHotModule extends NodeModule {
28
29
  hot?: {
@@ -73,7 +74,7 @@ class RWSClient {
73
74
  }
74
75
  }
75
76
 
76
- addPlugin<T extends DefaultRWSPluginOptionsType>(pluginEntry: RWSPluginEntry<T>)
77
+ addPlugin<T extends DefaultRWSPluginOptionsType>(pluginEntry: IStaticRWSPlugin)
77
78
  {
78
79
  this.config.plugins.push(pluginEntry);
79
80
  }
@@ -0,0 +1,9 @@
1
+ import { Accessor, Observable } from "@microsoft/fast-element";
2
+ import { ExtendedObservableAccessor } from "./_extended_accessor";
3
+
4
+ export class DefaultObservableAccessor extends ExtendedObservableAccessor {
5
+
6
+ constructor(public name: string, protected customGet: (source: any, field: string) => any = null, protected customSet: (source: any, field: string, newVal: any) => boolean | void = null, protected watcher: any = void 0, suffix: string = 'Changed') {
7
+ super(name, customGet, customSet, watcher, suffix);
8
+ }
9
+ }
@@ -0,0 +1,41 @@
1
+ import { Observable, Accessor } from "@microsoft/fast-element";
2
+
3
+ export abstract class ExtendedObservableAccessor implements Accessor {
4
+ protected field: string;
5
+ protected callback: string;
6
+
7
+ constructor(public name: string, protected customGet: (source: any, field: string) => any = null, protected customSet: (source: any, field: string, newVal: any) => boolean | void = null, protected watcher: any = void 0, suffix: string = 'Changed') {
8
+ this.field = `_${name}`;
9
+ this.callback = `${name}${suffix}`;
10
+ }
11
+
12
+ getValue(source: any): any {
13
+ Observable.track(source, this.name);
14
+
15
+ return this.customGet ? this.customGet(source, this.field) : source[this.field];
16
+ }
17
+
18
+ setValue(source: any, newValue: any): void {
19
+ if(this.customSet){
20
+ if(this.customSet(source, this.field, newValue) === false){
21
+ return;
22
+ };
23
+ }
24
+
25
+ const field = this.field;
26
+ const oldValue = source[field];
27
+
28
+ if (oldValue !== newValue) {
29
+ source[field] = newValue;
30
+
31
+ const callback = source[this.callback];
32
+
33
+ if (typeof callback === 'function') {
34
+ callback.call(source, oldValue, newValue);
35
+ }
36
+
37
+ Observable.getNotifier(source).notify(this.name);
38
+ }
39
+ }
40
+
41
+ }
@@ -0,0 +1,8 @@
1
+ import { Observable } from "@microsoft/fast-element";
2
+
3
+ export function handleExternalChange(_target: any, $prop: string)
4
+ {
5
+ if(!!_target['externalChanged']){
6
+ _target['externalChanged'].call(_target, $prop, null, _target[$prop]);
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ import { Accessor, Observable } from "@microsoft/fast-element";
2
+ import { ExtendedObservableAccessor } from "./_extended_accessor";
3
+
4
+ export class ExternalObservableAccessor extends ExtendedObservableAccessor {
5
+
6
+ constructor(public name: string, protected customGet: (source: any, field: string) => any = null, protected customSet: (source: any, field: string, newVal: any) => boolean | void = null, protected watcher: any = void 0) {
7
+ super(name, customGet, customSet, watcher, '');
8
+ }
9
+ }
@@ -1,64 +1,34 @@
1
- import { Observable, AttributeConfiguration, DecoratorAttributeConfiguration } from '@microsoft/fast-element';
2
- import RWSViewComponent from '../_component';
3
-
4
- type TargetType = RWSViewComponent; // Use a more generic type for the target to ensure compatibility
5
-
6
- function ngAttr(configOrTarget?: DecoratorAttributeConfiguration | TargetType, prop?: string): void | any
7
- {
8
- if (arguments.length > 1) {
9
- // Decorator used directly without factory invocation
10
- // Apply the decorator immediately without returning anything
11
- applyDecorator(configOrTarget as RWSViewComponent, prop!);
12
- } else {
13
- // Decorator factory invocation
14
- const config = configOrTarget as AttributeConfiguration;
15
- // Return a function that applies the decorator, conforming to TypeScript's expectations for decorator factories
16
- return (target: TargetType, property: string) => applyDecorator(target, property, config);
17
- }
18
- }
19
-
20
- function applyDecorator(target: TargetType, prop: string, config: AttributeConfiguration | any = {}): void
21
- {
22
- if (arguments.length > 1) {
23
- config.property = prop;
24
- }
25
-
26
- AttributeConfiguration.locate(target.constructor).push(config);
27
- modifyPropertyDescriptor(target, prop);
28
- }
29
-
30
-
31
-
32
- function modifyPropertyDescriptor(target: any, propertyKey: string): void {
33
- const privatePropName = `_${String(propertyKey)}`;
34
- Object.defineProperty(target, privatePropName, {
35
- writable: true,
36
- value: target[propertyKey],
37
- });
38
-
39
- Object.defineProperty(target, propertyKey, {
40
- get() {
41
- const value: string = this[privatePropName];
42
- return isNgValue(value) ? null : value;
43
- },
44
- set(value: any) {
45
- if (typeof value === 'string' && isNgValue(value)) {
46
- this[privatePropName] = null; // Set to null if condition is met
47
- } else {
48
- this[privatePropName] = value;
49
- }
50
- Observable.notify(this, propertyKey);
51
- },
52
- });
1
+ import {
2
+ DecoratorAttributeConfiguration,
3
+ AttributeConfiguration,
4
+ Observable,
5
+ } from "@microsoft/fast-element";
6
+ import { handleExternalChange } from "./_external_handler";
7
+ import RWSViewComponent, { IWithCompose } from "../_component";
8
+ import { externalAttr } from "./external-attr";
9
+
10
+ export function ngAttr(
11
+ config?: DecoratorAttributeConfiguration
12
+ ): (target: {}, property: string) => void;
13
+
14
+ /**
15
+ * Decorator: Specifies an HTML attribute.
16
+ * @param target - The class to define the attribute on.
17
+ * @param prop - The property name to be associated with the attribute.
18
+ * @public
19
+ */
20
+ export function ngAttr(target: {}, prop: string): void;
21
+ export function ngAttr(
22
+ configOrTarget?: DecoratorAttributeConfiguration | {},
23
+ prop?: string
24
+ ): void | ((target: {}, property: string) => void) {
25
+ return externalAttr(configOrTarget, prop, {
26
+ converter: (val: any) => {
27
+ if(val && val.indexOf('{{') > -1){
28
+ return undefined;
29
+ }
30
+
31
+ return val;
32
+ }
33
+ })
53
34
  }
54
-
55
- function isNgValue(input: string): boolean {
56
- // Regular expression to match AngularJS template variable notation
57
- const angularJsVariablePattern = /\{\{([^}]+)\}\}/;
58
-
59
- // Test the input string for the pattern and return the result
60
- return angularJsVariablePattern.test(input);
61
- }
62
-
63
-
64
- export { ngAttr };
@@ -0,0 +1,60 @@
1
+ import {
2
+ DecoratorAttributeConfiguration,
3
+ AttributeConfiguration,
4
+ Observable,
5
+ } from "@microsoft/fast-element";
6
+ import { handleExternalChange } from "./_external_handler";
7
+ import RWSViewComponent, { IWithCompose } from "../_component";
8
+
9
+ type ExAttrOpts = { converter?: (val: any) => string }
10
+ const _default_opts = {
11
+ converter: (val: any) => {
12
+ return val;
13
+ }
14
+ }
15
+
16
+ export function externalAttr(
17
+ config?: DecoratorAttributeConfiguration,
18
+ ): (target: {}, property: string) => void;
19
+
20
+ export function externalAttr(target: {}, property: string, opts?: ExAttrOpts): void;
21
+ export function externalAttr(
22
+ configOrTarget?: DecoratorAttributeConfiguration | {},
23
+ property?: string,
24
+ opts: ExAttrOpts = _default_opts
25
+ ): void | ((target: {}, property: string) => void) {
26
+ let config: AttributeConfiguration;
27
+
28
+ function decorator($target: {}, $prop: string): void {
29
+ if (arguments.length > 1) {
30
+ // Non invocation:
31
+ // - @attr
32
+ // Invocation with or w/o opts:
33
+ // - @attr()
34
+ // - @attr({...opts})
35
+ config.property = $prop;
36
+ }
37
+
38
+ config.mode = 'fromView';
39
+ config.converter = { fromView: opts.converter, toView: null };
40
+
41
+ const attrs = AttributeConfiguration.locate($target.constructor);
42
+ RWSViewComponent.setExternalAttr(($target.constructor as IWithCompose<any>).name, $prop);
43
+
44
+ attrs.push(config);
45
+ }
46
+
47
+ if (arguments.length > 1) {
48
+ // Non invocation:
49
+ // - @attr
50
+ config = {} as any;
51
+ decorator(configOrTarget!, property!);
52
+ return;
53
+ }
54
+
55
+ // Invocation with or w/o opts:
56
+ // - @attr()
57
+ // - @attr({...opts})
58
+ config = configOrTarget === void 0 ? ({} as any) : configOrTarget;
59
+ return decorator;
60
+ }
@@ -1,72 +1,53 @@
1
- import { Observable } from '@microsoft/fast-element';
2
- import RWSViewComponent from '../_component';
3
- import DOMService from '../../services/DOMService';
4
- import RWSContainer from '../_container';
5
- import { IOptions } from 'sanitize-html';
6
-
7
- import * as he from 'he';
8
-
9
- type SanitizeOptions = IOptions & { fullEncode?: boolean };
1
+ import { Observable, Accessor, observable as parentObservable} from '@microsoft/fast-element';
10
2
 
11
- const heOpt: he.EncodeOptions = {
12
- useNamedReferences: false,
13
- encodeEverything: true,
14
- };
15
-
16
- function enc(html: string): string
17
- {
18
- return he.encode(html, heOpt);
19
- }
20
-
21
-
22
- const transformAnyTag = (tagName: string, attribs: { [key: string]: string }) => {
23
- // Example: Wrap the original tag with `span` and indicate the original tag name in a data attribute
24
- return {
25
- tagName: 'span', // Change this to any tag you want to transform to
26
- attribs: {
27
- ...attribs,
28
- 'data-original-tag': tagName
29
- }
30
- };
31
- };
3
+ import RWSViewComponent from '../_component';
4
+ import { DefaultObservableAccessor } from './_default_observable_accessor';
5
+ import { ExternalObservableAccessor } from './_external_observable_accessor';
32
6
 
33
- function applyDecorator(target: RWSViewComponent, prop: string, config: SanitizeOptions = null): void
34
- {
35
- modifyPropertyDescriptor(target, prop, config as IOptions);
36
- }
37
7
 
38
- function modifyPropertyDescriptor(target: any, propertyKey: string, config: IOptions = null): void {
39
- const privatePropName = `_${String(propertyKey)}`;
40
- Object.defineProperty(target, privatePropName, {
41
- writable: true,
42
- value: target[propertyKey],
43
- });
8
+ type ExternalObservableOptions = {} | unknown;
44
9
 
45
- Object.defineProperty(target, propertyKey, {
46
- get() {
47
- return this[privatePropName];
48
- },
49
- set(value: any) {
50
- console.log('[DEBUG] external changed', propertyKey);
51
- this[privatePropName] = value;
52
- Observable.notify(this, propertyKey);
53
- },
54
- });
10
+ function isString(test: unknown){
11
+ return typeof test === 'string';
55
12
  }
56
13
 
57
- function externalAttr(configOrTarget?: SanitizeOptions | RWSViewComponent, prop?: string): void | any
58
- {
59
- if (arguments.length > 1) {
60
- // Decorator used directly without factory invocation
61
- // Apply the decorator immediately without returning anything
62
- applyDecorator(configOrTarget as RWSViewComponent, prop!);
63
- } else {
64
- // Decorator factory invocation
65
- const config = configOrTarget as SanitizeOptions;
66
- // Return a function that applies the decorator, conforming to TypeScript's expectations for decorator factories
67
- return (target: RWSViewComponent, property: string) => applyDecorator(target, property, config);
68
- }
14
+ function externalObservable(targetComponent: RWSViewComponent | unknown, nameOrAccessor: string | Accessor, opts: ExternalObservableOptions = null): void | any
15
+ {
16
+
17
+ const target = targetComponent as any;
18
+ const propName = typeof nameOrAccessor === 'string' ? nameOrAccessor : nameOrAccessor.name;
19
+
20
+
21
+ if (isString(nameOrAccessor)) {
22
+ nameOrAccessor = new DefaultObservableAccessor(propName);
23
+ }
24
+
25
+ const defaultAccessor: Accessor = nameOrAccessor as Accessor;
26
+ const extendedAccessor = new ExternalObservableAccessor(propName);
27
+
28
+ const accessors: Accessor[] = [
29
+ defaultAccessor,
30
+ extendedAccessor
31
+ ];
32
+
33
+ for (const accessor of accessors){
34
+ Observable.getAccessors(target).push(accessor);
35
+
36
+ Reflect.defineProperty(target, accessor.name, {
37
+ enumerable: true,
38
+ get(this: any) {
39
+ return accessor.getValue(this);
40
+ },
41
+ set(this: any, newValue: any) {
42
+ const oldVal = accessor.getValue(this);
43
+ accessor.setValue(this, newValue);
44
+ if(!!this['externalChanged']){
45
+ this['externalChanged'].call(accessor.name, oldVal, newValue);
46
+ }
47
+ },
48
+ });
49
+ }
69
50
  }
70
51
 
71
52
 
72
- export { externalAttr };
53
+ export { externalObservable };
@@ -1,4 +1,4 @@
1
- import { ViewTemplate, ElementStyles, observable, html, Constructable, PartialFASTElementDefinition, attr } from '@microsoft/fast-element';
1
+ import { ViewTemplate, ElementStyles, observable, html, Constructable, PartialFASTElementDefinition, attr, Observable } from '@microsoft/fast-element';
2
2
  import { FoundationElement, FoundationElementDefinition, FoundationElementRegistry, OverrideFoundationElementDefinition } from '../../foundation/rws-foundation';
3
3
  import ConfigService, { ConfigServiceInstance } from '../services/ConfigService';
4
4
  import UtilsService, { UtilsServiceInstance } from '../services/UtilsService';
@@ -9,6 +9,7 @@ import { IRWSViewComponent, IAssetShowOptions } from '../types/IRWSViewComponent
9
9
  import RWSWindow, { RWSWindowComponentInterface, loadRWSRichWindow } from '../types/RWSWindow';
10
10
  import { applyConstructor, RWSInject } from './_decorator';
11
11
  import TheRWSService from '../services/_service';
12
+ import { handleExternalChange } from './_attrs/_external_handler';
12
13
 
13
14
  interface IFastDefinition {
14
15
  name: string;
@@ -32,10 +33,13 @@ export interface IWithCompose<T extends RWSViewComponent> {
32
33
  _verbose: boolean;
33
34
  _toInject: {[key: string]: TheRWSService};
34
35
  _depKeys: {[key: string]: string[]};
36
+ _externalAttrs: { [key:string]: string[] };
37
+ setExternalAttr: (componentName: string, key: string) => void
35
38
  }
36
39
 
37
40
  abstract class RWSViewComponent extends FoundationElement implements IRWSViewComponent {
38
41
  __isLoading: boolean = true;
42
+ __exAttrLoaded: string[] = [];
39
43
  private static instances: RWSViewComponent[] = [];
40
44
  static fileList: string[] = [];
41
45
 
@@ -45,6 +49,7 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
45
49
  static _defined: { [key: string]: boolean } = {};
46
50
  static _toInject: {[key: string]: TheRWSService} = {};
47
51
  static _depKeys: {[key: string]: string[]} = {_all: []};
52
+ static _externalAttrs: { [key: string]: string[] } = {};
48
53
  static _verbose: boolean = false;
49
54
 
50
55
  @RWSInject(ConfigService, true) protected config: ConfigServiceInstance;
@@ -60,34 +65,18 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
60
65
 
61
66
  constructor() {
62
67
  super();
63
- applyConstructor(this);
64
-
68
+ applyConstructor(this);
65
69
  }
66
70
 
67
71
  connectedCallback() {
68
72
  super.connectedCallback();
69
- applyConstructor(this);
70
-
71
- // console.trace(this.config);
72
- // console.log(this.routingService);
73
+ applyConstructor(this);
74
+
73
75
  if (!(this.constructor as IWithCompose<this>).definition && (this.constructor as IWithCompose<this>).autoLoadFastElement) {
74
76
  throw new Error('RWS component is not named. Add `static definition = {name, template};`');
75
77
  }
76
78
 
77
- try {
78
- (this.constructor as IWithCompose<this>).fileList.forEach((file: string) => {
79
- if (this.fileAssets[file]) {
80
- return;
81
- }
82
- this.apiService.pureGet(this.config.get('pubUrlFilePrefix') + file).then((response: string) => {
83
- this.fileAssets = { ...this.fileAssets, [file]: html`${response}` };
84
- });
85
- });
86
-
87
- } catch (e: Error | any) {
88
- console.error('Error loading file content:', e.message);
89
- console.error(e.stack);
90
- }
79
+ this.applyFileList();
91
80
 
92
81
  RWSViewComponent.instances.push(this);
93
82
  }
@@ -214,6 +203,32 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
214
203
  this.$emit(eventName, event);
215
204
  }
216
205
 
206
+ private applyFileList(): void
207
+ {
208
+ try {
209
+ (this.constructor as IWithCompose<this>).fileList.forEach((file: string) => {
210
+ if (this.fileAssets[file]) {
211
+ return;
212
+ }
213
+ this.apiService.pureGet(this.config.get('pubUrlFilePrefix') + file).then((response: string) => {
214
+ this.fileAssets = { ...this.fileAssets, [file]: html`${response}` };
215
+ });
216
+ });
217
+
218
+ } catch (e: Error | any) {
219
+ console.error('Error loading file content:', e.message);
220
+ console.error(e.stack);
221
+ }
222
+ }
223
+
224
+ static setExternalAttr(componentName: string, key: string)
225
+ {
226
+ if(!Object.keys(RWSViewComponent._externalAttrs).includes(componentName)){
227
+ RWSViewComponent._externalAttrs[componentName] = [];
228
+ }
229
+
230
+ RWSViewComponent._externalAttrs[componentName].push(key);
231
+ }
217
232
 
218
233
  static hotReplacedCallback() {
219
234
  this.getInstances().forEach(instance => instance.forceReload());
@@ -5,7 +5,8 @@ import { loadRWSRichWindow } from '../types/RWSWindow';
5
5
  import RWSViewComponent, { IWithCompose } from './_component';
6
6
  import { InterfaceSymbol } from './_container';
7
7
  import { RWSInject } from './_decorators/RWSInject';
8
- import { ElementStyles, ViewTemplate } from '@microsoft/fast-element';
8
+ import { ElementStyles, Observable, ViewTemplate } from '@microsoft/fast-element';
9
+ import { handleExternalChange } from './_attrs/_external_handler';
9
10
 
10
11
  interface RWSDecoratorOptions {
11
12
  template?: string,
@@ -77,10 +78,12 @@ const applyConstructor = (component: RWSViewComponent, x: boolean = false): void
77
78
 
78
79
  type KeyType = {[key: string]: TheRWSService | string };
79
80
 
81
+ const _target = (component as any);
82
+
80
83
  function inject(services: KeyType){
81
84
  for (const prop in services) {
82
85
  const service = (typeof services[prop] === 'string' ? existingInjectedDependencies[prop] : services[prop]) as TheRWSService;
83
- (component as any)[prop] = service;
86
+ _target[prop] = service;
84
87
  }
85
88
  }
86
89
 
@@ -100,6 +103,21 @@ const applyConstructor = (component: RWSViewComponent, x: boolean = false): void
100
103
  inject({
101
104
  config: RWSContainer().get(ConfigService)
102
105
  })
106
+
107
+ if(Object.keys(RWSViewComponent._externalAttrs).includes((_target.constructor as IWithCompose<any>).name)){
108
+ for(const exAttrKey in RWSViewComponent._externalAttrs[(_target.constructor as IWithCompose<any>).name]){
109
+ const exAttr = RWSViewComponent._externalAttrs[(_target.constructor as IWithCompose<any>).name][exAttrKey];
110
+ const notifier = Observable.getNotifier(_target);
111
+ notifier.subscribe({
112
+ handleChange(source, key) {
113
+ if (key === exAttr && !_target.__exAttrLoaded.includes(exAttr)) {
114
+ handleExternalChange(source, key);
115
+ _target.__exAttrLoaded.push(key);
116
+ }
117
+ }
118
+ }, exAttr);
119
+ }
120
+ }
103
121
  };
104
122
 
105
123
  export { RWSView, RWSDecoratorOptions, RWSIgnore, RWSInject, applyConstructor };
@@ -2,6 +2,7 @@ import { Key } from '../_container';
2
2
  import RWSViewComponent, { IWithCompose } from '../_component';
3
3
  import { loadDep, getFunctionParamNames } from './_di';
4
4
  import TheRWSService from '../../services/_service';
5
+ import { handleExternalChange } from '../_attrs/_external_handler';
5
6
 
6
7
 
7
8
  type InjectDecoratorReturnType = (target: any, key?: string | number | undefined, parameterIndex?: number) => void;
@@ -40,7 +41,7 @@ function RWSInject<T extends RWSViewComponent>(dependencyClass: Key, defaultServ
40
41
  const depKey = paramNames[parameterIndex];
41
42
 
42
43
  addToComponentInjection(targetConstructor.name, targetConstructor, depKey, dependencyClass, defaultService);
43
- }
44
+ }
44
45
  };
45
46
  }
46
47
 
package/src/index.ts CHANGED
@@ -11,8 +11,10 @@ import UtilsService, {UtilsServiceInstance} from './services/UtilsService';
11
11
  import ServiceWorkerService, { ServiceWorkerServiceInstance } from './services/ServiceWorkerService';
12
12
  import { sanitizedAttr } from './components/_attrs/sanitize-html';
13
13
  import { ngAttr } from './components/_attrs/angular-attr';
14
- import { externalAttr } from './components/_attrs/external-observable';
14
+ import { externalObservable } from './components/_attrs/external-observable';
15
+ import { externalAttr } from './components/_attrs/external-attr';
15
16
  import { RWSPlugin, DefaultRWSPluginOptionsType } from './plugins/_plugin';
17
+ import { IRWSPlugin, IStaticRWSPlugin } from './types/IRWSPlugin';
16
18
  import RWSClient, { RWSClientInstance } from './client';
17
19
  import { RWSPluginEntry } from './types/IRWSConfig';
18
20
  import IRWSUser from './types/IRWSUser';
@@ -32,6 +34,7 @@ export {
32
34
 
33
35
  RWSPlugin,
34
36
  RWSPluginEntry,
37
+ IRWSPlugin, IStaticRWSPlugin,
35
38
  DefaultRWSPluginOptionsType,
36
39
 
37
40
  NotifyUiType,
@@ -69,6 +72,7 @@ export {
69
72
  RWSIgnore,
70
73
  RWSInject,
71
74
  observable,
75
+ externalObservable,
72
76
  externalAttr,
73
77
  attr,
74
78
  ngAttr,
@@ -3,11 +3,12 @@ import { Container } from "../components/_container";
3
3
  import RWSWindow, {loadRWSRichWindow } from '../types/RWSWindow';
4
4
  import IRWSUser from "../types/IRWSUser";
5
5
  import { RWSInfoType } from "../client/components";
6
+ import { IRWSPlugin } from "../types/IRWSPlugin";
6
7
 
7
8
  type DefaultRWSPluginOptionsType = { enabled: boolean };
8
9
  type PluginInfoType = { name: string }
9
10
  type PluginConstructor<T extends DefaultRWSPluginOptionsType> = new (options: T) => RWSPlugin<T>;
10
- abstract class RWSPlugin<PluginOptions extends DefaultRWSPluginOptionsType> {
11
+ abstract class RWSPlugin<PluginOptions extends DefaultRWSPluginOptionsType> implements IRWSPlugin{
11
12
  protected isLoaded: boolean = false;
12
13
  protected options: PluginOptions;
13
14
  protected container: Container;
@@ -1,9 +1,9 @@
1
1
  import RWSViewComponent from '../components/_component';
2
2
  import { RWSPlugin, DefaultRWSPluginOptionsType } from '../plugins/_plugin';
3
+ import { IStaticRWSPlugin } from '../types/IRWSPlugin';
3
4
 
4
5
  export type IFrontRoutes = Record<string, unknown>;
5
- export type PluginConstructor<T extends DefaultRWSPluginOptionsType> = new (options: T) => RWSPlugin<T>;
6
- export type RWSPluginEntry<T extends DefaultRWSPluginOptionsType> = PluginConstructor<T> | [PluginConstructor<T>, T];
6
+ export type RWSPluginEntry<T extends DefaultRWSPluginOptionsType = DefaultRWSPluginOptionsType> = new (...args: any[]) => RWSPlugin<T>;
7
7
 
8
8
  export default interface IRWSConfig {
9
9
  dev?: boolean
@@ -23,7 +23,7 @@ export default interface IRWSConfig {
23
23
  parted?: boolean
24
24
  partedFileDir?: string
25
25
  partedPrefix?: string
26
- plugins?: RWSPluginEntry<DefaultRWSPluginOptionsType | any>[]
26
+ plugins?: IStaticRWSPlugin[]
27
27
  routing_enabled?: boolean
28
28
  _noLoad?: boolean
29
29
  }
@@ -0,0 +1,19 @@
1
+ import { DefaultRWSPluginOptionsType } from "../plugins/_plugin";
2
+ import IRWSUser from "./IRWSUser";
3
+ import { Container } from "../components/_container";
4
+ import RWSWindow from "../types/RWSWindow";
5
+ import { RWSInfoType } from "../client/components";
6
+
7
+
8
+ export interface IRWSPlugin {
9
+ onClientStart(): Promise<void>
10
+ onPartedComponentsLoad(componentParts: RWSInfoType): Promise<void>
11
+ onComponentsDeclare(): Promise<void>
12
+ onSetUser(user: IRWSUser): Promise<void>
13
+ }
14
+
15
+ export interface IStaticRWSPlugin<PluginOptions extends DefaultRWSPluginOptionsType = DefaultRWSPluginOptionsType> {
16
+ new (...args: any[]): IRWSPlugin;
17
+ container: Container;
18
+ window: RWSWindow;
19
+ }
@@ -0,0 +1,14 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ module.exports = function(content){
5
+ const filePath = this.resourcePath;
6
+ const componentDir = path.dirname(filePath);
7
+ const componentPath = path.resolve(componentDir, 'component.ts');
8
+
9
+ if(fs.existsSync(componentPath)){
10
+ fs.writeFileSync(componentPath, fs.readFileSync(componentPath, 'utf-8'))
11
+ }
12
+
13
+ return '';
14
+ }
@@ -1,65 +1,14 @@
1
- const RWSCssPlugin = require("../rws_scss_plugin");
2
1
  const path = require('path');
3
- const cssLoader = require('css-loader');
4
- // const { css } = require('@microsoft/fast-element')
2
+ const fs = require('fs');
5
3
 
6
- // function makeFastResource(context, code){
7
- // return function(){ return css`${code}`; }.bind(context)();
8
- // }
9
-
10
- module.exports = function(content) {
11
- const plugin = new RWSCssPlugin();
4
+ module.exports = function(content) {
12
5
  const filePath = this.resourcePath;
6
+ const componentDir = path.resolve(path.dirname(filePath), '..');
7
+ const componentPath = path.resolve(componentDir, 'component.ts');
13
8
 
14
- const options = this.getOptions() || { minify: false };
15
-
16
- const isDev = this._compiler.options.mode === 'development';
17
-
18
- const saveFile = content.indexOf('@save') > -1;
19
- let fromTs = false;
20
-
21
- if (plugin.checkForImporterType(this._module, 'ts')) {
22
- fromTs = true;
23
- }
24
-
25
- try {
26
- const codeData = plugin.compileScssCode(content, path.dirname(filePath), null, filePath, !isDev);
27
-
28
- const code = codeData.code;
29
- const deps = codeData.dependencies;
30
-
31
- for (const dependency of deps){
32
- this.addDependency(dependency);
33
- }
34
-
35
- if (fromTs && saveFile && code) {
36
- plugin.writeCssFile(filePath, code);
37
- }
38
-
39
- // if(!fromTs){
40
- // return (context) => makeFastResource(context, code);
41
- // }
42
-
43
- // Properly setup the context for css-loader
44
-
45
- const callback = this.async();
46
- const loaderContext = {
47
- ...this,
48
- query: { sourceMap: isDev },
49
- async: () => (err, output) => {
50
- if (err) {
51
- callback(err);
52
- return;
53
- }
54
- callback(null, output);
55
- }
56
- };
57
-
58
- // Execute css-loader with the generated CSS code
59
- cssLoader.call(loaderContext, code);
60
-
61
- } catch (e) {
62
- console.error(e);
63
- callback(e);
9
+ if(fs.existsSync(componentPath)){
10
+ fs.writeFileSync(componentPath, fs.readFileSync(componentPath, 'utf-8'))
64
11
  }
12
+
13
+ return '';
65
14
  };
@@ -5,6 +5,8 @@ const ts = require('typescript');
5
5
  const tools = require('../../_tools');
6
6
  const chalk = require('chalk');
7
7
  const {html_error_proof} = require('./ts/html_error');
8
+ const RWSCssPlugin = require("../rws_scss_plugin");
9
+ const plugin = new RWSCssPlugin();
8
10
 
9
11
  const _defaultRWSLoaderOptions = {
10
12
  templatePath: 'template.html',
@@ -12,9 +14,8 @@ const _defaultRWSLoaderOptions = {
12
14
  fastOptions: { shadowOptions: { mode: 'open' } }
13
15
  }
14
16
 
15
- let htmlFastImports = null;
16
-
17
- module.exports = async function(content) {
17
+ module.exports = async function(content) {
18
+ let htmlFastImports = null;
18
19
  let processedContent = content;
19
20
  const filePath = this.resourcePath;
20
21
  const isDev = this._compiler.options.mode === 'development';
@@ -70,29 +71,39 @@ module.exports = async function(content) {
70
71
  const tagName = decoratorData.tagName;
71
72
 
72
73
  try {
73
- if(tagName){
74
+ if(tagName){
75
+ const templateName = 'template';
76
+ const templatePath = path.dirname(filePath) + `/${templateName}.html`;
77
+
78
+ const templateExists = fs.existsSync(templatePath);
79
+
80
+ let template = 'const rwsTemplate: null = null;';
74
81
  let styles = 'const styles: null = null;'
75
82
 
76
83
  if(fs.existsSync(path.dirname(filePath) + '/styles')){
77
- styles = `import componentCSS from './${stylesPath}';\n`;
78
- styles += `const styles = T.css\`\${componentCSS}\`;`;
79
- }
84
+ const scsscontent = fs.readFileSync(path.dirname(filePath) + '/' + stylesPath, 'utf-8');
85
+ const codeData = plugin.compileScssCode(scsscontent, path.dirname(filePath) + '/styles', null, filePath, !isDev);
86
+ const cssCode = codeData.code;
87
+
88
+ styles = `import './${stylesPath}';\n`;
89
+ if(!templateExists){
90
+ styles += `import { css } from '@microsoft/fast-element';\n`;
91
+ }
92
+ styles += `const styles = ${templateExists? 'T.': ''}css\`${cssCode}\`;\n`;
93
+
94
+ this.addDependency(path.dirname(filePath) + '/' + stylesPath);
95
+ }
80
96
 
81
- const templateName = 'template';
82
- const templatePath = path.dirname(filePath) + `/${templateName}.html`;
83
- const templateExists = fs.existsSync(templatePath, 'utf-8');
84
- let template = 'const rwsTemplate: null = null;';
85
-
86
- if(templateExists){
87
- const templateContent = fs.readFileSync(templatePath);
88
- htmlFastImports = `import * as T from '@microsoft/fast-element';\nimport RWSTemplateHTML from './${templateName}.html';`;
89
- this.addDependency(templatePath);
97
+ if(templateExists){
98
+ const templateContent = fs.readFileSync(templatePath, 'utf-8').replace(/<!--[\s\S]*?-->/g, '');
99
+ htmlFastImports = `import * as T from '@microsoft/fast-element';\nimport './${templateName}.html';\n`;
90
100
  template = `
91
101
  //@ts-ignore
92
102
  let rwsTemplate: any = T.html\`${templateContent}\`;
93
103
  `;
104
+ this.addDependency(templatePath);
94
105
  }
95
-
106
+
96
107
  const viewReg = /@RWSView\(['"]([a-zA-Z0-9_-]+)['"],?.*\)\sclass\s([a-zA-Z0-9_-]+) extends RWSViewComponent/gs
97
108
 
98
109
  let m;