@rws-framework/client 2.22.0 → 2.23.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/README.md +160 -872
- package/builder/webpack/loaders/rws_fast_ts_loader.js +14 -21
- package/cfg/build_steps/webpack/_loaders.js +136 -7
- package/package.json +1 -1
- package/src/components/_component.ts +30 -45
- package/src/components/_css_injection.ts +187 -0
- package/src/events.ts +3 -0
- package/src/index.ts +8 -6
- package/src/services/ApiService.ts +11 -8
- package/src/services/DOMService.ts +1 -1
- package/src/services/_api/backend.ts +41 -9
- package/src/types/IRWSViewComponent.ts +2 -2
- package/src/types/IBackendCore.ts +0 -12
- package/src/types/IRWSResource.ts +0 -5
- package/src/types/IReFormerField.ts +0 -5
|
@@ -11,10 +11,17 @@ module.exports = async function(content) {
|
|
|
11
11
|
let processedContent = content;
|
|
12
12
|
const filePath = this.resourcePath;
|
|
13
13
|
const isDev = this._compiler.options.mode === 'development';
|
|
14
|
-
let isIgnored = false;
|
|
15
|
-
let isDebugged = false;
|
|
16
14
|
// timingStart('decorator_extraction');
|
|
17
|
-
const decoratorExtract = LoadersHelper.
|
|
15
|
+
const decoratorExtract = await LoadersHelper.extractRWSViewArgsAsync(
|
|
16
|
+
processedContent,
|
|
17
|
+
false,
|
|
18
|
+
filePath,
|
|
19
|
+
this.addDependency,
|
|
20
|
+
this.query?.rwsWorkspaceDir,
|
|
21
|
+
this.query?.appRootDir,
|
|
22
|
+
isDev,
|
|
23
|
+
this.query?.publicDir
|
|
24
|
+
);
|
|
18
25
|
const decoratorData = decoratorExtract ? decoratorExtract.viewDecoratorData : null;
|
|
19
26
|
|
|
20
27
|
const cachedCode = processedContent;
|
|
@@ -34,19 +41,11 @@ module.exports = async function(content) {
|
|
|
34
41
|
return content;
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
let
|
|
38
|
-
let
|
|
44
|
+
let isIgnored = false;
|
|
45
|
+
let isDebugged = false;
|
|
39
46
|
|
|
40
47
|
if(decoratorData.decoratorArgs){
|
|
41
48
|
const decoratorArgs = decoratorData.decoratorArgs
|
|
42
|
-
|
|
43
|
-
if(decoratorArgs.template){
|
|
44
|
-
templateName = decoratorData.decoratorArgs.template || null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if(decoratorArgs.styles){
|
|
48
|
-
stylesPath = decoratorData.decoratorArgs.styles || null;
|
|
49
|
-
}
|
|
50
49
|
|
|
51
50
|
if(decoratorArgs.ignorePackaging){
|
|
52
51
|
isIgnored = true;
|
|
@@ -64,19 +63,13 @@ module.exports = async function(content) {
|
|
|
64
63
|
|
|
65
64
|
try {
|
|
66
65
|
if(tagName){
|
|
67
|
-
const [template, htmlFastImports, templateExists] = await LoadersHelper.getTemplate(filePath, this.addDependency, className, templateName, isDev);
|
|
68
|
-
|
|
69
|
-
const styles = await LoadersHelper.getStyles(filePath, this.query?.rwsWorkspaceDir, this.query?.appRootDir,this.addDependency, templateExists, stylesPath, isDev, this.query?.publicDir);
|
|
70
|
-
|
|
71
66
|
if(className){
|
|
72
|
-
const replacedViewDecoratorContent =
|
|
67
|
+
const replacedViewDecoratorContent = decoratorExtract.replacedDecorator;
|
|
73
68
|
|
|
74
69
|
if(replacedViewDecoratorContent){
|
|
75
|
-
processedContent =
|
|
70
|
+
processedContent = replacedViewDecoratorContent;
|
|
76
71
|
}
|
|
77
72
|
}
|
|
78
|
-
|
|
79
|
-
processedContent = `${htmlFastImports ? htmlFastImports + '\n' : ''}${processedContent}`;
|
|
80
73
|
}
|
|
81
74
|
|
|
82
75
|
const debugTsPath = filePath.replace('.ts','.debug.ts');
|
|
@@ -125,7 +125,115 @@ function _extractRWSViewDefs(fastOptions = {}, decoratorArgs = {})
|
|
|
125
125
|
return [addedParamDefs, addedParams];
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function extractRWSViewArgs(content, noReplace = false) {
|
|
128
|
+
function extractRWSViewArgs(content, noReplace = false, filePath = null, addDependency = null, rwsWorkspaceDir = null, appRootDir = null, isDev = false, publicDir = null) {
|
|
129
|
+
// If this is being called with only basic parameters (backward compatibility)
|
|
130
|
+
if (filePath === null || addDependency === null) {
|
|
131
|
+
return extractRWSViewArgsSync(content, noReplace);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Otherwise, call the async version
|
|
135
|
+
return extractRWSViewArgsAsync(content, noReplace, filePath, addDependency, rwsWorkspaceDir, appRootDir, isDev, publicDir);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractRWSViewArgsSync(content, noReplace = false) {
|
|
139
|
+
const viewReg = /@RWSView\(\s*["']([^"']+)["'](?:\s*,\s*([\s\S]*?))?\s*\)\s*(.*?\s+)?class\s+([a-zA-Z0-9_-]+)\s+extends\s+RWSViewComponent/gm;
|
|
140
|
+
|
|
141
|
+
let m;
|
|
142
|
+
let tagName = null;
|
|
143
|
+
let className = null;
|
|
144
|
+
let classNamePrefix = null;
|
|
145
|
+
let decoratorArgs = null;
|
|
146
|
+
|
|
147
|
+
const _defaultRWSLoaderOptions = {
|
|
148
|
+
templatePath: 'template.html',
|
|
149
|
+
stylesPath: 'styles.scss',
|
|
150
|
+
fastOptions: { shadowOptions: { mode: 'open' } }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
while ((m = viewReg.exec(content)) !== null) {
|
|
154
|
+
if (m.index === viewReg.lastIndex) {
|
|
155
|
+
viewReg.lastIndex++;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
m.forEach((match, groupIndex) => {
|
|
159
|
+
if (groupIndex === 1) {
|
|
160
|
+
tagName = match;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (groupIndex === 2) {
|
|
164
|
+
if (match) {
|
|
165
|
+
try {
|
|
166
|
+
decoratorArgs = JSON.parse(JSON.stringify(match));
|
|
167
|
+
} catch(e){
|
|
168
|
+
console.log(chalk.red('Decorator options parse error: ') + e.message + '\n Problematic line:');
|
|
169
|
+
console.log(`
|
|
170
|
+
@RWSView(${tagName}, ${match})
|
|
171
|
+
`);
|
|
172
|
+
console.log(chalk.yellowBright(`Decorator options failed to parse for "${tagName}" component.`) + ' { decoratorArgs } defaulting to null.');
|
|
173
|
+
console.log(match);
|
|
174
|
+
|
|
175
|
+
console.error(e);
|
|
176
|
+
|
|
177
|
+
throw new Error('Failed parsing @RWSView')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (groupIndex === 3) {
|
|
183
|
+
if(match){
|
|
184
|
+
classNamePrefix = match;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (groupIndex === 4) {
|
|
189
|
+
className = match;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if(!tagName){
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let processedContent = content;
|
|
199
|
+
let fastOptions = _defaultRWSLoaderOptions.fastOptions;
|
|
200
|
+
|
|
201
|
+
if(decoratorArgs && decoratorArgs !== ''){
|
|
202
|
+
try {
|
|
203
|
+
decoratorArgs = json5.parse(decoratorArgs);
|
|
204
|
+
}catch(e){
|
|
205
|
+
// ignore parse errors for backward compatibility
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (decoratorArgs && decoratorArgs.fastElementOptions) {
|
|
210
|
+
fastOptions = decoratorArgs.fastElementOptions;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let replacedDecorator = null;
|
|
214
|
+
|
|
215
|
+
if(!noReplace){
|
|
216
|
+
const [addedParamDefs, addedParams] = _extractRWSViewDefs(fastOptions, decoratorArgs);
|
|
217
|
+
const replacedViewDecoratorContent = processedContent.replace(
|
|
218
|
+
viewReg,
|
|
219
|
+
`@RWSView('$1', null, { template: rwsTemplate, styles${addedParams.length ? ', options: {' + (addedParams.join(', ')) + '}' : ''} })\n$3class $4 extends RWSViewComponent `
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
replacedDecorator = `${addedParamDefs.join('\n')}\n${replacedViewDecoratorContent}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
viewDecoratorData: {
|
|
227
|
+
tagName,
|
|
228
|
+
className,
|
|
229
|
+
classNamePrefix,
|
|
230
|
+
decoratorArgs
|
|
231
|
+
},
|
|
232
|
+
replacedDecorator
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function extractRWSViewArgsAsync(content, noReplace = false, filePath = null, addDependency = null, rwsWorkspaceDir = null, appRootDir = null, isDev = false, publicDir = null) {
|
|
129
237
|
const viewReg = /@RWSView\(\s*["']([^"']+)["'](?:\s*,\s*([\s\S]*?))?\s*\)\s*(.*?\s+)?class\s+([a-zA-Z0-9_-]+)\s+extends\s+RWSViewComponent/gm;
|
|
130
238
|
|
|
131
239
|
let m;
|
|
@@ -206,16 +314,37 @@ function extractRWSViewArgs(content, noReplace = false) {
|
|
|
206
314
|
|
|
207
315
|
let replacedDecorator = null;
|
|
208
316
|
|
|
209
|
-
if(!noReplace){
|
|
210
|
-
const [addedParamDefs, addedParams] = _extractRWSViewDefs(fastOptions, decoratorArgs);
|
|
211
|
-
|
|
317
|
+
if(!noReplace && filePath && addDependency){
|
|
318
|
+
const [addedParamDefs, addedParams] = _extractRWSViewDefs(fastOptions, decoratorArgs);
|
|
319
|
+
|
|
320
|
+
// Get template name and styles path from decorator args
|
|
321
|
+
let templateName = null;
|
|
322
|
+
let stylesPath = null;
|
|
323
|
+
|
|
324
|
+
if(decoratorArgs && decoratorArgs.template){
|
|
325
|
+
templateName = decoratorArgs.template;
|
|
326
|
+
}
|
|
327
|
+
if(decoratorArgs && decoratorArgs.styles){
|
|
328
|
+
stylesPath = decoratorArgs.styles;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Generate template and styles
|
|
332
|
+
const [template, htmlFastImports, templateExists] = await getTemplate(filePath, addDependency, className, templateName, isDev);
|
|
333
|
+
const styles = await getStyles(filePath, rwsWorkspaceDir, appRootDir, addDependency, templateExists, stylesPath, isDev, publicDir);
|
|
334
|
+
|
|
335
|
+
// Extract original imports (everything before the @RWSView decorator)
|
|
336
|
+
const beforeDecorator = processedContent.substring(0, processedContent.search(/@RWSView/));
|
|
337
|
+
const afterDecoratorMatch = processedContent.match(/@RWSView[\s\S]*$/);
|
|
338
|
+
const afterDecorator = afterDecoratorMatch ? afterDecoratorMatch[0] : '';
|
|
339
|
+
|
|
340
|
+
const replacedViewDecoratorContent = afterDecorator.replace(
|
|
212
341
|
viewReg,
|
|
213
|
-
|
|
342
|
+
`${template}\n${styles}\n${addedParamDefs.join('\n')}\n@RWSView('$1', null, { template: rwsTemplate, styles${addedParams.length ? ', options: {' + (addedParams.join(', ')) + '}' : ''} })\n$3class $4 extends RWSViewComponent `
|
|
214
343
|
);
|
|
215
344
|
|
|
216
345
|
// console.log({replacedViewDecoratorContent});
|
|
217
346
|
|
|
218
|
-
replacedDecorator = `${
|
|
347
|
+
replacedDecorator = `${htmlFastImports ? htmlFastImports + '\n' : ''}${beforeDecorator}${replacedViewDecoratorContent}`;
|
|
219
348
|
}
|
|
220
349
|
|
|
221
350
|
return {
|
|
@@ -290,4 +419,4 @@ let rwsTemplate: any = T.html<${className}>\`${templateContent}\`;
|
|
|
290
419
|
return [template, htmlFastImports, templateExists];
|
|
291
420
|
}
|
|
292
421
|
|
|
293
|
-
module.exports = { getRWSLoaders, extractRWSViewArgs, getTemplate, getStyles }
|
|
422
|
+
module.exports = { getRWSLoaders, extractRWSViewArgs, extractRWSViewArgsAsync, getTemplate, getStyles }
|
package/package.json
CHANGED
|
@@ -13,13 +13,15 @@ import TheRWSService from '../services/_service';
|
|
|
13
13
|
import { handleExternalChange } from './_attrs/_external_handler';
|
|
14
14
|
import { IFastDefinition, isDefined, defineComponent, getDefinition } from './_definitions';
|
|
15
15
|
import { on, $emitDown, observe, sendEventToOutside } from './_event_handling';
|
|
16
|
+
import { domEvents } from '../events';
|
|
17
|
+
import CSSInjectionManager, { CSSInjectMode, ICSSInjectionOptions } from './_css_injection';
|
|
16
18
|
|
|
17
19
|
type ComposeMethodType<
|
|
18
20
|
T extends FoundationElementDefinition,
|
|
19
21
|
K extends Constructable<RWSViewComponent>
|
|
20
22
|
> = (this: K, elementDefinition: T) => (overrideDefinition?: OverrideFoundationElementDefinition<T>) => FoundationElementRegistry<FoundationElementDefinition, T>;
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
const _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS = 1;
|
|
23
25
|
|
|
24
26
|
export interface IWithCompose<T extends RWSViewComponent> {
|
|
25
27
|
[key: string]: any
|
|
@@ -250,56 +252,39 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
|
|
|
250
252
|
return RWSViewComponent.instances;
|
|
251
253
|
}
|
|
252
254
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const db = await this.indexedDBService.openDB(dbName, storeName);
|
|
257
|
-
|
|
258
|
-
let adoptedSheets: CSSStyleSheet[] = [];
|
|
259
|
-
|
|
260
|
-
for (const styleLink of styleLinks) {
|
|
261
|
-
if (mode === 'legacy' || mode === 'both') {
|
|
262
|
-
const link = document.createElement('link');
|
|
263
|
-
link.rel = 'stylesheet';
|
|
264
|
-
link.href = styleLink;
|
|
265
|
-
this.getShadowRoot().appendChild(link);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (mode === 'adopted' || mode === 'both') {
|
|
269
|
-
const entry = await this.indexedDBService.getFromDB(db, storeName, styleLink);
|
|
270
|
-
const maxAgeMs = 1000 * 60 * 60 * 24; // 24h
|
|
255
|
+
static getCachedStyles(styleLinks: string[]): CSSStyleSheet[] {
|
|
256
|
+
return CSSInjectionManager.getCachedStyles(styleLinks);
|
|
257
|
+
}
|
|
271
258
|
|
|
272
|
-
|
|
259
|
+
static hasCachedStyles(styleLinks: string[]): boolean {
|
|
260
|
+
return CSSInjectionManager.hasCachedStyles(styleLinks);
|
|
261
|
+
}
|
|
273
262
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
cssText = entry.css;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
263
|
+
static getStylesOwnerComponent(): any {
|
|
264
|
+
return CSSInjectionManager.getStylesOwnerComponent();
|
|
265
|
+
}
|
|
280
266
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
css: cssText,
|
|
285
|
-
timestamp: Date.now()
|
|
286
|
-
});
|
|
287
|
-
console.log(`System saved stylesheet: ${styleLink} to IndexedDB`)
|
|
288
|
-
}
|
|
267
|
+
static clearCachedStyles(): void {
|
|
268
|
+
CSSInjectionManager.clearCachedStyles();
|
|
269
|
+
}
|
|
289
270
|
|
|
290
|
-
|
|
291
|
-
|
|
271
|
+
protected async injectStyles(styleLinks: string[], mode: CSSInjectMode = 'adopted', maxDaysExp?: number) {
|
|
272
|
+
// Create a bridge object that exposes the necessary properties
|
|
273
|
+
const componentBridge = {
|
|
274
|
+
shadowRoot: this.shadowRoot,
|
|
275
|
+
indexedDBService: this.indexedDBService,
|
|
276
|
+
$emit: this.$emit.bind(this)
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
return CSSInjectionManager.injectStyles(componentBridge, styleLinks, { mode, maxDaysExp });
|
|
280
|
+
}
|
|
292
281
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
protected getInjectedStyles(styleLinks: string[]): CSSStyleSheet[] {
|
|
283
|
+
return CSSInjectionManager.getCachedStyles(styleLinks);
|
|
284
|
+
}
|
|
296
285
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
...adoptedSheets,
|
|
300
|
-
...this.getShadowRoot().adoptedStyleSheets,
|
|
301
|
-
];
|
|
302
|
-
}
|
|
286
|
+
protected hasInjectedStyles(styleLinks: string[]): boolean {
|
|
287
|
+
return CSSInjectionManager.hasCachedStyles(styleLinks);
|
|
303
288
|
}
|
|
304
289
|
}
|
|
305
290
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { domEvents } from '../events';
|
|
2
|
+
import IndexedDBService, { IndexedDBServiceInstance } from '../services/IndexedDBService';
|
|
3
|
+
|
|
4
|
+
type CSSInjectMode = 'adopted' | 'legacy' | 'both';
|
|
5
|
+
|
|
6
|
+
const _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS = 1;
|
|
7
|
+
|
|
8
|
+
interface ICSSInjectionOptions {
|
|
9
|
+
mode?: CSSInjectMode;
|
|
10
|
+
maxDaysExp?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ICSSInjectionComponent {
|
|
14
|
+
shadowRoot: ShadowRoot | null;
|
|
15
|
+
indexedDBService: IndexedDBServiceInstance;
|
|
16
|
+
$emit(eventName: string): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class CSSInjectionManager {
|
|
20
|
+
private static CACHED_STYLES: Map<string, CSSStyleSheet> = new Map();
|
|
21
|
+
private static STYLES_OWNER_COMPONENT: ICSSInjectionComponent | null = null;
|
|
22
|
+
|
|
23
|
+
static getCachedStyles(styleLinks: string[]): CSSStyleSheet[] {
|
|
24
|
+
return styleLinks
|
|
25
|
+
.filter(link => CSSInjectionManager.CACHED_STYLES.has(link))
|
|
26
|
+
.map(link => CSSInjectionManager.CACHED_STYLES.get(link)!);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static hasCachedStyles(styleLinks: string[]): boolean {
|
|
30
|
+
return styleLinks.every(link => CSSInjectionManager.CACHED_STYLES.has(link));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static getStylesOwnerComponent(): ICSSInjectionComponent | null {
|
|
34
|
+
return CSSInjectionManager.STYLES_OWNER_COMPONENT;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static clearCachedStyles(): void {
|
|
38
|
+
CSSInjectionManager.CACHED_STYLES.clear();
|
|
39
|
+
CSSInjectionManager.STYLES_OWNER_COMPONENT = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static async injectStyles(
|
|
43
|
+
component: ICSSInjectionComponent,
|
|
44
|
+
styleLinks: string[],
|
|
45
|
+
options: ICSSInjectionOptions = {}
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
const { mode = 'adopted', maxDaysExp } = options;
|
|
48
|
+
|
|
49
|
+
if (!component.shadowRoot) {
|
|
50
|
+
throw new Error('Component must have a shadow root for CSS injection');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add initial transition styles to host element
|
|
54
|
+
const transitionSheet = new CSSStyleSheet();
|
|
55
|
+
await transitionSheet.replace(`
|
|
56
|
+
:host {
|
|
57
|
+
opacity: 0;
|
|
58
|
+
transition: opacity 0.3s ease-in-out;
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
component.shadowRoot.adoptedStyleSheets = [
|
|
62
|
+
transitionSheet,
|
|
63
|
+
...component.shadowRoot.adoptedStyleSheets,
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
let adoptedSheets: CSSStyleSheet[] = [];
|
|
67
|
+
let doneAdded = false;
|
|
68
|
+
|
|
69
|
+
// Check if we already have cached styles from the owner component
|
|
70
|
+
const cachedSheets: CSSStyleSheet[] = [];
|
|
71
|
+
const uncachedLinks: string[] = [];
|
|
72
|
+
|
|
73
|
+
for (const styleLink of styleLinks) {
|
|
74
|
+
if (CSSInjectionManager.CACHED_STYLES.has(styleLink)) {
|
|
75
|
+
cachedSheets.push(CSSInjectionManager.CACHED_STYLES.get(styleLink)!);
|
|
76
|
+
} else {
|
|
77
|
+
uncachedLinks.push(styleLink);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// If we have cached styles, use them immediately
|
|
82
|
+
if (cachedSheets.length > 0) {
|
|
83
|
+
adoptedSheets.push(...cachedSheets);
|
|
84
|
+
doneAdded = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Only process uncached styles
|
|
88
|
+
if (uncachedLinks.length > 0) {
|
|
89
|
+
// Set this component as the owner if no owner exists yet
|
|
90
|
+
if (!CSSInjectionManager.STYLES_OWNER_COMPONENT) {
|
|
91
|
+
CSSInjectionManager.STYLES_OWNER_COMPONENT = component;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const dbName = 'css-cache';
|
|
95
|
+
const storeName = 'styles';
|
|
96
|
+
const db = await component.indexedDBService.openDB(dbName, storeName);
|
|
97
|
+
const maxAgeMs = 1000 * 60 * 60 * 24; // 24h
|
|
98
|
+
const maxDaysAge = maxDaysExp ? maxDaysExp : _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS;
|
|
99
|
+
const maxAgeDays = maxAgeMs * maxDaysAge;
|
|
100
|
+
|
|
101
|
+
for (const styleLink of uncachedLinks) {
|
|
102
|
+
const loadPromise = new Promise<void>(async (resolve, reject) => {
|
|
103
|
+
if (mode === 'legacy' || mode === 'both') {
|
|
104
|
+
const link = document.createElement('link');
|
|
105
|
+
link.rel = 'stylesheet';
|
|
106
|
+
link.href = styleLink;
|
|
107
|
+
component.shadowRoot!.appendChild(link);
|
|
108
|
+
|
|
109
|
+
link.onload = () => {
|
|
110
|
+
doneAdded = true;
|
|
111
|
+
|
|
112
|
+
if(mode === 'legacy'){
|
|
113
|
+
resolve();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (mode === 'adopted' || mode === 'both') {
|
|
119
|
+
const entry = await component.indexedDBService.getFromDB(db, storeName, styleLink);
|
|
120
|
+
|
|
121
|
+
let cssText: string | null = null;
|
|
122
|
+
|
|
123
|
+
if (entry && typeof entry === 'object' && 'css' in entry && 'timestamp' in entry) {
|
|
124
|
+
const expired = Date.now() - entry.timestamp > maxAgeDays;
|
|
125
|
+
if (!expired) {
|
|
126
|
+
cssText = entry.css;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!cssText) {
|
|
131
|
+
cssText = await fetch(styleLink).then(res => res.text());
|
|
132
|
+
await component.indexedDBService.saveToDB(db, storeName, styleLink, {
|
|
133
|
+
css: cssText,
|
|
134
|
+
timestamp: Date.now()
|
|
135
|
+
});
|
|
136
|
+
console.log(`System saved stylesheet: ${styleLink} to IndexedDB`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sheet = new CSSStyleSheet();
|
|
140
|
+
await sheet.replace(cssText);
|
|
141
|
+
|
|
142
|
+
// Cache the stylesheet for future use
|
|
143
|
+
CSSInjectionManager.CACHED_STYLES.set(styleLink, sheet);
|
|
144
|
+
|
|
145
|
+
adoptedSheets.push(sheet);
|
|
146
|
+
|
|
147
|
+
if(mode === 'adopted' || mode === 'both'){
|
|
148
|
+
resolve();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await loadPromise;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
doneAdded = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (adoptedSheets.length) {
|
|
160
|
+
component.shadowRoot.adoptedStyleSheets = [
|
|
161
|
+
...adoptedSheets,
|
|
162
|
+
...component.shadowRoot.adoptedStyleSheets,
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
doneAdded = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (doneAdded) {
|
|
169
|
+
// Set opacity to 1 to fade in the component
|
|
170
|
+
const opacitySheet = new CSSStyleSheet();
|
|
171
|
+
await opacitySheet.replace(`
|
|
172
|
+
:host {
|
|
173
|
+
opacity: 1 !important;
|
|
174
|
+
}
|
|
175
|
+
`);
|
|
176
|
+
component.shadowRoot.adoptedStyleSheets = [
|
|
177
|
+
opacitySheet,
|
|
178
|
+
...component.shadowRoot.adoptedStyleSheets,
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
component.$emit(domEvents.loadedLinkedStyles);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default CSSInjectionManager;
|
|
187
|
+
export { CSSInjectMode, ICSSInjectionOptions, ICSSInjectionComponent };
|
package/src/events.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -22,14 +22,14 @@ import { RWSIgnore, RWSInject, RWSView } from './components/_decorator';
|
|
|
22
22
|
import type { DefaultRWSPluginOptionsType } from './plugins/_plugin';
|
|
23
23
|
import type { IRWSPlugin, IStaticRWSPlugin, IPluginSpawnOption } from './types/IRWSPlugin';
|
|
24
24
|
import type IRWSUser from './types/IRWSUser';
|
|
25
|
-
import type { IAssetShowOptions } from './components/_component';
|
|
25
|
+
import type { IAssetShowOptions, IRWSViewComponent } from './components/_component';
|
|
26
26
|
import type { RWSDecoratorOptions } from './components/_decorator';
|
|
27
|
-
import type { IKDBTypeInfo, IKDBTypesResponse } from './types/IBackendCore';
|
|
28
27
|
import type { DOMOutputType, TagsProcessorType } from './services/DOMService';
|
|
29
28
|
import type { IBackendRoute, IHTTProute, IPrefixedHTTProutes } from './services/ApiService';
|
|
30
29
|
import type IRWSConfig from './types/IRWSConfig';
|
|
31
30
|
import type RWSNotify from './types/RWSNotify';
|
|
32
31
|
import type { NotifyUiType, NotifyLogType } from './types/RWSNotify';
|
|
32
|
+
import * as RWSEvents from './events';
|
|
33
33
|
|
|
34
34
|
export default RWSClient;
|
|
35
35
|
|
|
@@ -70,11 +70,12 @@ export {
|
|
|
70
70
|
RWSService,
|
|
71
71
|
RWSViewComponent,
|
|
72
72
|
|
|
73
|
-
RWSContainer
|
|
73
|
+
RWSContainer,
|
|
74
|
+
|
|
75
|
+
RWSEvents
|
|
74
76
|
};
|
|
75
77
|
|
|
76
|
-
export type {
|
|
77
|
-
IKDBTypeInfo, IKDBTypesResponse,
|
|
78
|
+
export type {
|
|
78
79
|
NotifyUiType,
|
|
79
80
|
NotifyLogType,
|
|
80
81
|
IBackendRoute as IRWSBackendRoute,
|
|
@@ -84,5 +85,6 @@ export type {
|
|
|
84
85
|
IAssetShowOptions as IRWSAssetShowOptions,
|
|
85
86
|
IRWSConfig,
|
|
86
87
|
IRWSUser,
|
|
87
|
-
TagsProcessorType
|
|
88
|
+
TagsProcessorType,
|
|
89
|
+
IRWSViewComponent
|
|
88
90
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ITypesResponse } from '../../../components/src/types/IBackendCore';
|
|
2
2
|
import TheService from './_service';
|
|
3
3
|
|
|
4
4
|
//@4DI
|
|
@@ -20,11 +20,14 @@ interface IAPIOptions {
|
|
|
20
20
|
routeParams?: {
|
|
21
21
|
[key: string]: string
|
|
22
22
|
},
|
|
23
|
+
queryParams?: {
|
|
24
|
+
[key: string]: string
|
|
25
|
+
}
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
interface IHTTProute<P = {[key: string]: any}> {
|
|
26
29
|
name: string;
|
|
27
|
-
path: string;
|
|
30
|
+
path: string | string[];
|
|
28
31
|
method: string;
|
|
29
32
|
noParams?: boolean;
|
|
30
33
|
options?: any;
|
|
@@ -89,16 +92,16 @@ class ApiService extends TheService {
|
|
|
89
92
|
public delete = calls.delete;
|
|
90
93
|
|
|
91
94
|
public back = {
|
|
92
|
-
get: async <T>(routeName: string, options?: IAPIOptions, token?: string): Promise<T> => calls.get.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), options) as Promise<T>,
|
|
93
|
-
post: async <T, P extends object = object>(routeName: string, payload?: P, options?: IAPIOptions): Promise<T> => calls.post.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), payload, options) as Promise<T>,
|
|
94
|
-
put: async <T, P extends object = object>(routeName: string, payload: P, options?: IAPIOptions): Promise<T> => calls.put.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), payload, options) as Promise<T>,
|
|
95
|
-
delete: async <T>(routeName: string, options?: IAPIOptions): Promise<T> => calls.delete.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), options) as Promise<T>,
|
|
95
|
+
get: async <T>(routeName: string, options?: IAPIOptions, token?: string): Promise<T> => calls.get.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), options) as Promise<T>,
|
|
96
|
+
post: async <T, P extends object = object>(routeName: string, payload?: P, options?: IAPIOptions): Promise<T> => calls.post.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), payload, options) as Promise<T>,
|
|
97
|
+
put: async <T, P extends object = object>(routeName: string, payload: P, options?: IAPIOptions): Promise<T> => calls.put.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), payload, options) as Promise<T>,
|
|
98
|
+
delete: async <T>(routeName: string, options?: IAPIOptions): Promise<T> => calls.delete.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), options) as Promise<T>,
|
|
96
99
|
uploadFile: async (routeName: string, file: File, onProgress: (progress: number) => void, options: IAPIOptions = {}, payload: any = {}): Promise<UploadResponse> => this.uploadFile(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), file, onProgress, payload),
|
|
97
100
|
};
|
|
98
101
|
|
|
99
|
-
async getResource(resourceName: string): Promise<
|
|
102
|
+
async getResource(resourceName: string): Promise<ITypesResponse>
|
|
100
103
|
{
|
|
101
|
-
return calls.get.bind(this)(`${this.config.get('backendUrl')}${this.config.get('apiPrefix') || ''}/api/rws/resource/${resourceName}`) as Promise<
|
|
104
|
+
return calls.get.bind(this)(`${this.config.get('backendUrl')}${this.config.get('apiPrefix') || ''}/api/rws/resource/${resourceName}`) as Promise<ITypesResponse>
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -83,7 +83,7 @@ class DOMService extends RWSService {
|
|
|
83
83
|
sanitizeOptions: DOMPurify.Config = { })
|
|
84
84
|
{
|
|
85
85
|
const output: string = line.trim();
|
|
86
|
-
const sanitized = DOMPurify.sanitize(output, { USE_PROFILES: { html: true }, ...sanitizeOptions});
|
|
86
|
+
const sanitized = DOMPurify.sanitize(output, { USE_PROFILES: { html: true }, ...sanitizeOptions}) as string;
|
|
87
87
|
return sanitized;
|
|
88
88
|
}
|
|
89
89
|
}
|