@lynx-js/web-mainthread-apis 0.16.0 → 0.17.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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @lynx-js/web-mainthread-apis
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: \_\_QueryComponentImpl in mts should execute only once for same url ([#1763](https://github.com/lynx-family/lynx-stack/pull/1763))
8
+
9
+ - fix: avoid duplicate style transformation ([#1748](https://github.com/lynx-family/lynx-stack/pull/1748))
10
+
11
+ After this commit, we use DAG methods to handle the styleInfos
12
+
13
+ - feat: support lazy bundle with CSSOG(`enableCSSSelector: false`). ([#1770](https://github.com/lynx-family/lynx-stack/pull/1770))
14
+
15
+ - Updated dependencies [[`93d707b`](https://github.com/lynx-family/lynx-stack/commit/93d707b82a59f7256952e21da6dcad2999f8233d)]:
16
+ - @lynx-js/web-constants@0.17.0
17
+ - @lynx-js/web-style-transformer@0.17.0
18
+
19
+ ## 0.16.1
20
+
21
+ ### Patch Changes
22
+
23
+ - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](https://github.com/lynx-family/lynx-stack/pull/1235))
24
+
25
+ - Updated dependencies [[`608f375`](https://github.com/lynx-family/lynx-stack/commit/608f375e20732cc4c9f141bfbf9800ba6896100b)]:
26
+ - @lynx-js/web-constants@0.16.1
27
+ - @lynx-js/web-style-transformer@0.16.1
28
+
3
29
  ## 0.16.0
4
30
 
5
31
  ### Minor Changes
@@ -1,4 +1,4 @@
1
- import { type LynxTemplate, type PageConfig, type FlushElementTreeOptions, type Cloneable, type BrowserConfig, type publishEventEndpoint, type publicComponentEventEndpoint, type reportErrorEndpoint, type RpcCallType, type LynxContextEventTarget, type MainThreadGlobalThis, type WebFiberElementImpl, type I18nResourceTranslationOptions, type SSRHydrateInfo, type SSRDehydrateHooks, type JSRealm } from '@lynx-js/web-constants';
1
+ import { type LynxTemplate, type PageConfig, type FlushElementTreeOptions, type Cloneable, type BrowserConfig, type publishEventEndpoint, type publicComponentEventEndpoint, type reportErrorEndpoint, type RpcCallType, type LynxContextEventTarget, type MainThreadGlobalThis, type WebFiberElementImpl, type I18nResourceTranslationOptions, type SSRHydrateInfo, type SSRDehydrateHooks, type JSRealm, type QueryComponentPAPI } from '@lynx-js/web-constants';
2
2
  export interface MainThreadRuntimeCallbacks {
3
3
  mainChunkReady: () => void;
4
4
  flushElementTree: (options: FlushElementTreeOptions, timingFlags: string[], exposureChangedElements: WebFiberElementImpl[]) => void;
@@ -8,7 +8,8 @@ export interface MainThreadRuntimeCallbacks {
8
8
  publishEvent: RpcCallType<typeof publishEventEndpoint>;
9
9
  publicComponentEvent: RpcCallType<typeof publicComponentEventEndpoint>;
10
10
  _I18nResourceTranslation: (options: I18nResourceTranslationOptions) => unknown | undefined;
11
- updateCssOGStyle: (uniqueId: number, newClassName: string, cssID: string | null) => void;
11
+ updateCssOGStyle: (uniqueId: number, newClassName: string, cssID: string | null, entryName: string | null) => void;
12
+ __QueryComponent: QueryComponentPAPI;
12
13
  }
13
14
  export interface MainThreadRuntimeConfig {
14
15
  pageConfig: PageConfig;
@@ -1,7 +1,7 @@
1
1
  // Copyright 2023 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { lynxUniqueIdAttribute, systemInfo, parentComponentUniqueIdAttribute, componentIdAttribute, LynxEventNameToW3cByTagName, LynxEventNameToW3cCommon, lynxTagAttribute, W3cEventNameToLynx, cssIdAttribute, lynxDefaultDisplayLinearAttribute, __lynx_timing_flag, lynxDisposedAttribute, } from '@lynx-js/web-constants';
4
+ import { lynxUniqueIdAttribute, systemInfo, parentComponentUniqueIdAttribute, componentIdAttribute, LynxEventNameToW3cByTagName, LynxEventNameToW3cCommon, lynxTagAttribute, W3cEventNameToLynx, cssIdAttribute, lynxDefaultDisplayLinearAttribute, __lynx_timing_flag, lynxDisposedAttribute, lynxEntryNameAttribute, } from '@lynx-js/web-constants';
5
5
  import { createMainThreadLynx } from './createMainThreadLynx.js';
6
6
  import { __AddClass, __AddConfig, __AddDataset, __AddInlineStyle, __AppendElement, __ElementIsEqual, __FirstElement, __GetAttributes, __GetChildren, __GetClasses, __GetComponentID, __GetDataByKey, __GetDataset, __GetElementConfig, __GetElementUniqueID, __GetID, __GetParent, __GetTag, __GetTemplateParts, __InsertElementBefore, __LastElement, __MarkPartElement, __MarkTemplateElement, __NextElement, __RemoveElement, __ReplaceElement, __ReplaceElements, __SetClasses, __SetConfig, __SetCSSId, __SetDataset, __SetID, __SetInlineStyles, __UpdateComponentID, __UpdateComponentInfo, __GetAttributeByName, } from './pureElementPAPIs.js';
7
7
  import { createCrossThreadEvent } from './utils/createCrossThreadEvent.js';
@@ -291,9 +291,10 @@ export function createMainThreadGlobalThis(config) {
291
291
  // @ts-expect-error fixme
292
292
  temp.replaceWith(childB);
293
293
  };
294
- const __SetCSSIdForCSSOG = (elements, cssId) => {
294
+ const __SetCSSIdForCSSOG = (elements, cssId, entryName) => {
295
295
  for (const element of elements) {
296
296
  element.setAttribute(cssIdAttribute, cssId + '');
297
+ entryName && element.setAttribute(lynxEntryNameAttribute, entryName);
297
298
  const cls = element.getAttribute('class');
298
299
  cls && __SetClassesForCSSOG(element, cls);
299
300
  }
@@ -304,13 +305,15 @@ export function createMainThreadGlobalThis(config) {
304
305
  element.setAttribute('class', newClassName);
305
306
  const cssId = element.getAttribute(cssIdAttribute);
306
307
  const uniqueId = Number(element.getAttribute(lynxUniqueIdAttribute));
307
- callbacks.updateCssOGStyle(uniqueId, newClassName, cssId);
308
+ const entryName = element.getAttribute(lynxEntryNameAttribute);
309
+ callbacks.updateCssOGStyle(uniqueId, newClassName, cssId, entryName);
308
310
  };
309
311
  const __SetClassesForCSSOG = (element, classNames) => {
310
312
  __SetClasses(element, classNames);
311
313
  const cssId = element.getAttribute(cssIdAttribute);
312
314
  const uniqueId = Number(element.getAttribute(lynxUniqueIdAttribute));
313
- callbacks.updateCssOGStyle(uniqueId, classNames ?? '', cssId);
315
+ const entryName = element.getAttribute(lynxEntryNameAttribute);
316
+ callbacks.updateCssOGStyle(uniqueId, classNames ?? '', cssId, entryName);
314
317
  };
315
318
  const __LoadLepusChunk = (path) => {
316
319
  try {
@@ -469,6 +472,7 @@ export function createMainThreadGlobalThis(config) {
469
472
  __LoadLepusChunk,
470
473
  __GetPageElement,
471
474
  __globalProps: globalProps,
475
+ __QueryComponent: callbacks.__QueryComponent,
472
476
  SystemInfo,
473
477
  lynx: createMainThreadLynx(config, SystemInfo),
474
478
  _ReportError: (err, _) => callbacks._ReportError(err, _, release),
@@ -0,0 +1,4 @@
1
+ import { type JSRealm, type LynxCrossThreadContext, type MainThreadGlobalThis, type QueryComponentPAPI, type Rpc, type StyleInfo, type TemplateLoader } from '@lynx-js/web-constants';
2
+ export declare function createQueryComponent(loadTemplate: TemplateLoader, updateLazyComponentStyle: (styleInfo: StyleInfo, entryName: string) => void, backgroundThreadRpc: Rpc, mtsGlobalThisRef: {
3
+ mtsGlobalThis: MainThreadGlobalThis;
4
+ }, jsContext: LynxCrossThreadContext, mtsRealm: JSRealm): QueryComponentPAPI;
@@ -0,0 +1,57 @@
1
+ import { queryComponentEndpoint, updateBTSTemplateCacheEndpoint, } from '@lynx-js/web-constants';
2
+ export function createQueryComponent(loadTemplate, updateLazyComponentStyle, backgroundThreadRpc, mtsGlobalThisRef, jsContext, mtsRealm) {
3
+ const updateBTSTemplateCache = backgroundThreadRpc.createCall(updateBTSTemplateCacheEndpoint);
4
+ const lazyCache = new Map();
5
+ const __QueryComponentImpl = (url, callback) => {
6
+ const cacheLazy = lazyCache.get(url);
7
+ const loadPromise = cacheLazy
8
+ ?? loadTemplate(url).then(async (template) => {
9
+ const updateBTSCachePromise = updateBTSTemplateCache(url, template);
10
+ let lepusRootChunkExport = await mtsRealm.loadScript(template.lepusCode.root);
11
+ if (mtsGlobalThisRef.mtsGlobalThis.processEvalResult) {
12
+ lepusRootChunkExport = mtsGlobalThisRef.mtsGlobalThis
13
+ .processEvalResult(lepusRootChunkExport, url);
14
+ }
15
+ updateLazyComponentStyle(template.styleInfo, url);
16
+ await updateBTSCachePromise;
17
+ jsContext.dispatchEvent({
18
+ type: '__OnDynamicJSSourcePrepared',
19
+ data: url,
20
+ });
21
+ return lepusRootChunkExport;
22
+ });
23
+ !cacheLazy && lazyCache.set(url, loadPromise);
24
+ loadPromise.then(lepusRootChunkExport => {
25
+ callback?.({
26
+ code: 0,
27
+ data: {
28
+ url,
29
+ evalResult: lepusRootChunkExport,
30
+ },
31
+ });
32
+ }).catch((error) => {
33
+ console.error(`lynx web: lazy bundle load failed:`, error);
34
+ lazyCache.delete(url);
35
+ callback?.({
36
+ code: -1,
37
+ data: undefined,
38
+ });
39
+ });
40
+ return null;
41
+ };
42
+ backgroundThreadRpc.registerHandler(queryComponentEndpoint, (url) => {
43
+ const ret = new Promise(resolve => {
44
+ __QueryComponentImpl(url, (result) => {
45
+ resolve({
46
+ code: result.code,
47
+ detail: {
48
+ schema: url,
49
+ },
50
+ });
51
+ });
52
+ });
53
+ return ret;
54
+ });
55
+ return __QueryComponentImpl;
56
+ }
57
+ //# sourceMappingURL=createQueryComponent.js.map
@@ -1,4 +1,4 @@
1
- import { type Rpc, type StartMainThreadContextConfig, type RpcCallType, type reportErrorEndpoint, type I18nResourceTranslationOptions, type InitI18nResources, type I18nResources, type SSRHydrateInfo, type SSRDehydrateHooks, type JSRealm } from '@lynx-js/web-constants';
2
- export declare function prepareMainThreadAPIs(backgroundThreadRpc: Rpc, rootDom: Document | ShadowRoot, document: Document, mtsRealm: JSRealm, commitDocument: (exposureChangedElements: HTMLElement[]) => Promise<void> | void, markTimingInternal: (timingKey: string, pipelineId?: string) => void, flushMarkTimingInternal: () => void, reportError: RpcCallType<typeof reportErrorEndpoint>, triggerI18nResourceFallback: (options: I18nResourceTranslationOptions) => void, initialI18nResources: (data: InitI18nResources) => I18nResources, ssrHooks?: SSRDehydrateHooks): {
1
+ import { type Rpc, type StartMainThreadContextConfig, type RpcCallType, type reportErrorEndpoint, type I18nResourceTranslationOptions, type InitI18nResources, type I18nResources, type SSRHydrateInfo, type SSRDehydrateHooks, type JSRealm, type TemplateLoader } from '@lynx-js/web-constants';
2
+ export declare function prepareMainThreadAPIs(backgroundThreadRpc: Rpc, rootDom: Document | ShadowRoot, document: Document, mtsRealm: JSRealm, commitDocument: (exposureChangedElements: HTMLElement[]) => Promise<void> | void, markTimingInternal: (timingKey: string, pipelineId?: string) => void, flushMarkTimingInternal: () => void, reportError: RpcCallType<typeof reportErrorEndpoint>, triggerI18nResourceFallback: (options: I18nResourceTranslationOptions) => void, initialI18nResources: (data: InitI18nResources) => I18nResources, loadTemplate: TemplateLoader, ssrHooks?: SSRDehydrateHooks): {
3
3
  startMainThread: (config: StartMainThreadContextConfig, ssrHydrateInfo?: SSRHydrateInfo) => Promise<void>;
4
4
  };
@@ -8,8 +8,9 @@ import { createMainThreadGlobalThis } from './createMainThreadGlobalThis.js';
8
8
  import { createExposureService } from './utils/createExposureService.js';
9
9
  import { initWasm } from '@lynx-js/web-style-transformer';
10
10
  import { appendStyleElement } from './utils/processStyleInfo.js';
11
+ import { createQueryComponent } from './crossThreadHandlers/createQueryComponent.js';
11
12
  const initWasmPromise = initWasm();
12
- export function prepareMainThreadAPIs(backgroundThreadRpc, rootDom, document, mtsRealm, commitDocument, markTimingInternal, flushMarkTimingInternal, reportError, triggerI18nResourceFallback, initialI18nResources, ssrHooks) {
13
+ export function prepareMainThreadAPIs(backgroundThreadRpc, rootDom, document, mtsRealm, commitDocument, markTimingInternal, flushMarkTimingInternal, reportError, triggerI18nResourceFallback, initialI18nResources, loadTemplate, ssrHooks) {
13
14
  const postTimingFlags = backgroundThreadRpc.createCall(postTimingFlagsEndpoint);
14
15
  const backgroundStart = backgroundThreadRpc.createCall(BackgroundThreadStartEndpoint);
15
16
  const publishEvent = backgroundThreadRpc.createCall(publishEventEndpoint);
@@ -29,7 +30,11 @@ export function prepareMainThreadAPIs(backgroundThreadRpc, rootDom, document, mt
29
30
  sendEventEndpoint: dispatchCoreContextOnBackgroundEndpoint,
30
31
  });
31
32
  const i18nResources = initialI18nResources(initI18nResources);
32
- const { updateCssOGStyle } = appendStyleElement(styleInfo, pageConfig, rootDom, document, undefined, ssrHydrateInfo);
33
+ const { updateCssOGStyle, updateLazyComponentStyle } = appendStyleElement(styleInfo, pageConfig, rootDom, document, ssrHydrateInfo);
34
+ const mtsGlobalThisRef = {
35
+ mtsGlobalThis: undefined,
36
+ };
37
+ const __QueryComponent = createQueryComponent(loadTemplate, updateLazyComponentStyle, backgroundThreadRpc, mtsGlobalThisRef, jsContext, mtsRealm);
33
38
  const mtsGlobalThis = createMainThreadGlobalThis({
34
39
  lynxTemplate: template,
35
40
  mtsRealm,
@@ -127,8 +132,10 @@ export function prepareMainThreadAPIs(backgroundThreadRpc, rootDom, document, mt
127
132
  }
128
133
  return triggerI18nResourceFallback(options);
129
134
  },
135
+ __QueryComponent,
130
136
  },
131
137
  });
138
+ mtsGlobalThisRef.mtsGlobalThis = mtsGlobalThis;
132
139
  markTimingInternal('decode_end');
133
140
  await mtsRealm.loadScript(template.lepusCode.root);
134
141
  jsContext.__start(); // start the jsContext after the runtime is created
@@ -1,7 +1,7 @@
1
1
  // Copyright 2023 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { componentIdAttribute, cssIdAttribute, lynxComponentConfigAttribute, lynxDatasetAttribute, lynxElementTemplateMarkerAttribute, lynxPartIdAttribute, lynxTagAttribute, lynxUniqueIdAttribute, } from '@lynx-js/web-constants';
4
+ import { componentIdAttribute, cssIdAttribute, lynxComponentConfigAttribute, lynxDatasetAttribute, lynxElementTemplateMarkerAttribute, lynxEntryNameAttribute, lynxPartIdAttribute, lynxTagAttribute, lynxUniqueIdAttribute, } from '@lynx-js/web-constants';
5
5
  import { queryCSSProperty } from './style/cssPropertyMap.js';
6
6
  import { transformInlineStyleString, transformParsedStyles, } from './style/transformInlineStyle.js';
7
7
  import hyphenateStyleName from 'hyphenate-style-name';
@@ -92,9 +92,10 @@ export const __UpdateComponentInfo = /*#__PURE__*/ (element, params) => {
92
92
  && element.setAttribute(cssIdAttribute, params.cssID + '');
93
93
  params.name !== undefined && element.setAttribute('name', params.name);
94
94
  };
95
- export const __SetCSSId = /*#__PURE__*/ (elements, cssId) => {
95
+ export const __SetCSSId = /*#__PURE__*/ (elements, cssId, entryName) => {
96
96
  for (const element of elements) {
97
97
  element.setAttribute(cssIdAttribute, cssId + '');
98
+ entryName && element.setAttribute(lynxEntryNameAttribute, entryName);
98
99
  }
99
100
  };
100
101
  export const __SetClasses = /*#__PURE__*/ (element, classname) => {
@@ -1,17 +1,24 @@
1
- import { type StyleInfo, type CssOGInfo, type PageConfig, type SSRHydrateInfo } from '@lynx-js/web-constants';
2
- export declare function flattenStyleInfo(styleInfo: StyleInfo, enableCSSSelector: boolean): void;
1
+ import { type StyleInfo, type CssOGInfo, type PageConfig, type SSRHydrateInfo, type FlattenedStyleInfo } from '@lynx-js/web-constants';
2
+ /**
3
+ * get Transitive Closure of a Direct Acyclic Graph (DAG)
4
+ * 1. for each css, find all the imported by css files (directly and indirectly)
5
+ * 2. for each css, find all the importing css files (directly and indirectly)
6
+ * 3. return the flattened style info, do not modify the content and rules
7
+ */
8
+ export declare function flattenStyleInfo(styleInfo: StyleInfo): FlattenedStyleInfo;
3
9
  /**
4
10
  * apply the lynx css -> web css transformation
5
11
  */
6
- export declare function transformToWebCss(styleInfo: StyleInfo): void;
12
+ export declare function transformToWebCss(styleInfo: FlattenedStyleInfo): void;
7
13
  /**
8
14
  * generate those styles applied by <style>...</style>
9
15
  */
10
- export declare function genCssContent(styleInfo: StyleInfo, pageConfig: PageConfig, entryName?: string): string;
16
+ export declare function genCssContent(styleInfo: FlattenedStyleInfo, pageConfig: PageConfig, entryName?: string): string;
11
17
  /**
12
18
  * generate the css-in-js data
13
19
  */
14
- export declare function genCssOGInfo(styleInfo: StyleInfo): CssOGInfo;
15
- export declare function appendStyleElement(styleInfo: StyleInfo, pageConfig: PageConfig, rootDom: Node, document: Document, entryName?: string, ssrHydrateInfo?: SSRHydrateInfo): {
16
- updateCssOGStyle: (uniqueId: number, newClassName: string, cssID: string | null) => void;
20
+ export declare function genCssOGInfo(styleInfo: FlattenedStyleInfo): CssOGInfo;
21
+ export declare function appendStyleElement(styleInfo: StyleInfo, pageConfig: PageConfig, rootDom: Node, document: Document, ssrHydrateInfo?: SSRHydrateInfo): {
22
+ updateCssOGStyle: (uniqueId: number, newClassName: string, cssID: string | null, entryName: string | null) => void;
23
+ updateLazyComponentStyle: (styleInfo: StyleInfo, entryName: string) => void;
17
24
  };
@@ -4,36 +4,94 @@
4
4
  import { cssIdAttribute, lynxTagAttribute, lynxUniqueIdAttribute, lynxEntryNameAttribute, } from '@lynx-js/web-constants';
5
5
  import { transformParsedStyles } from './tokenizer.js';
6
6
  import { decodeCssOG } from './decodeCssOG.js';
7
- export function flattenStyleInfo(styleInfo, enableCSSSelector) {
8
- function flattenOneStyleInfo(cssId) {
9
- const oneInfo = styleInfo[cssId];
10
- const imports = oneInfo?.imports;
11
- if (oneInfo && imports?.length) {
12
- for (const im of imports) {
13
- const flatInfo = flattenOneStyleInfo(im);
14
- if (flatInfo) {
15
- oneInfo.content.push(...flatInfo.content);
16
- // oneInfo.rules.push(...flatInfo.rules);
17
- oneInfo.rules.push(...(enableCSSSelector
18
- ? flatInfo.rules
19
- // when enableCSSSelector is false, need to make a shallow copy of rules.sel
20
- // otherwise updating `oneCssInfo.sel` in `genCssOGInfo()` will affect other imported cssInfo
21
- : flatInfo.rules.map(i => ({ ...i }))));
7
+ function topologicalSort(styleInfo) {
8
+ /**
9
+ * kahn's algorithm
10
+ * 1. The styleInfo is already equivalent to a adjacency list. (cssId, import)
11
+ * 2. The styleInfo is a DAG therefore we don't need to do cyclic detection
12
+ */
13
+ const queue = [];
14
+ const inDegreeMap = new Map();
15
+ for (const [cssId, oneStyleInfo] of Object.entries(styleInfo)) {
16
+ !inDegreeMap.has(cssId) && inDegreeMap.set(cssId, 0); // initialize
17
+ for (const importCssId of oneStyleInfo.imports ?? []) {
18
+ const currentInDegree = inDegreeMap.get(importCssId) ?? 0;
19
+ inDegreeMap.set(importCssId, currentInDegree + 1);
20
+ }
21
+ }
22
+ for (const [cssId, inDegree] of inDegreeMap.entries()) {
23
+ if (inDegree === 0) {
24
+ queue.push(cssId);
25
+ }
26
+ }
27
+ const sortedCssIds = [];
28
+ while (queue.length > 0) {
29
+ const currentCssId = queue.shift();
30
+ sortedCssIds.push(currentCssId);
31
+ const currentAdjunction = styleInfo[currentCssId]?.imports;
32
+ if (currentAdjunction) {
33
+ for (const importCssId of currentAdjunction) {
34
+ const importInDegree = inDegreeMap.get(importCssId) - 1;
35
+ inDegreeMap.set(importCssId, importInDegree);
36
+ if (importInDegree === 0) {
37
+ queue.push(importCssId);
22
38
  }
23
39
  }
24
- oneInfo.imports = undefined;
25
40
  }
26
- return oneInfo;
27
41
  }
28
- Object.keys(styleInfo).map((cssId) => {
29
- flattenOneStyleInfo(cssId);
42
+ return sortedCssIds;
43
+ }
44
+ function generateImportByMap(styleInfo, sortedCssIds) {
45
+ const cssIdToImportBy = new Map();
46
+ for (const cssId of sortedCssIds) {
47
+ const currentAdjunction = styleInfo[cssId]?.imports;
48
+ if (currentAdjunction) {
49
+ const currentImportBy = cssIdToImportBy.get(cssId) ?? new Set([cssId]);
50
+ for (const importCssId of currentAdjunction) {
51
+ const importDeps = cssIdToImportBy.get(importCssId)
52
+ ?? new Set([importCssId]);
53
+ importDeps.add(cssId);
54
+ cssIdToImportBy.set(importCssId, currentImportBy.union(importDeps));
55
+ }
56
+ cssIdToImportBy.set(cssId, currentImportBy);
57
+ }
58
+ }
59
+ return cssIdToImportBy;
60
+ }
61
+ /**
62
+ * get Transitive Closure of a Direct Acyclic Graph (DAG)
63
+ * 1. for each css, find all the imported by css files (directly and indirectly)
64
+ * 2. for each css, find all the importing css files (directly and indirectly)
65
+ * 3. return the flattened style info, do not modify the content and rules
66
+ */
67
+ export function flattenStyleInfo(styleInfo) {
68
+ // Step 1. Topological sorting
69
+ const sortedCssIds = topologicalSort(styleInfo);
70
+ // Step 2. generate deps;
71
+ const cssIdToImportBy = generateImportByMap(styleInfo, sortedCssIds);
72
+ sortedCssIds.reverse();
73
+ // Step 3. generate the flattened style info
74
+ return sortedCssIds.map(cssId => {
75
+ const oneInfo = styleInfo[cssId];
76
+ const flattenedInfo = oneInfo
77
+ ? {
78
+ content: oneInfo.content,
79
+ rules: oneInfo.rules,
80
+ importBy: Array.from(cssIdToImportBy.get(cssId) ?? [cssId]),
81
+ }
82
+ : {
83
+ content: [],
84
+ rules: [],
85
+ importBy: [cssId],
86
+ };
87
+ return flattenedInfo;
30
88
  });
31
89
  }
32
90
  /**
33
91
  * apply the lynx css -> web css transformation
34
92
  */
35
93
  export function transformToWebCss(styleInfo) {
36
- for (const cssInfos of Object.values(styleInfo)) {
94
+ for (const cssInfos of styleInfo) {
37
95
  for (const rule of cssInfos.rules) {
38
96
  const { sel: selectors, decl: declarations } = rule;
39
97
  const { transformedStyle, childStyle } = transformParsedStyles(declarations);
@@ -56,19 +114,13 @@ export function genCssContent(styleInfo, pageConfig, entryName) {
56
114
  function getExtraSelectors(cssId) {
57
115
  let suffix;
58
116
  if (!pageConfig.enableRemoveCSSScope) {
59
- if (cssId !== undefined) {
60
- suffix = `[${cssIdAttribute}="${cssId}"]`;
61
- }
62
- else {
63
- // To make sure the Specificity correct
64
- suffix = `[${lynxTagAttribute}]`;
65
- }
117
+ suffix = `[${cssIdAttribute}="${cssId}"]`;
66
118
  }
67
119
  else {
68
120
  suffix = `[${lynxTagAttribute}]`;
69
121
  }
70
122
  if (entryName) {
71
- suffix = `${suffix}[${lynxEntryNameAttribute}="${entryName}"]`;
123
+ suffix = `${suffix}[${lynxEntryNameAttribute}=${JSON.stringify(entryName)}]`;
72
124
  }
73
125
  else {
74
126
  suffix = `${suffix}:not([${lynxEntryNameAttribute}])`;
@@ -76,13 +128,15 @@ export function genCssContent(styleInfo, pageConfig, entryName) {
76
128
  return suffix;
77
129
  }
78
130
  const finalCssContent = [];
79
- for (const [cssId, cssInfos] of Object.entries(styleInfo)) {
80
- const suffix = getExtraSelectors(cssId);
131
+ for (const cssInfos of styleInfo) {
81
132
  const declarationContent = cssInfos.rules.map((rule) => {
82
133
  const { sel: selectorList, decl: declarations } = rule;
83
134
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
84
- const selectorString = selectorList.map((selectors) => {
85
- return selectors.toSpliced(-4, 0, [suffix]).flat().join('');
135
+ const selectorString = cssInfos.importBy.map(cssId => {
136
+ const suffix = getExtraSelectors(cssId);
137
+ return selectorList.map((selectors) => {
138
+ return selectors.toSpliced(-4, 0, [suffix]).flat().join('');
139
+ }).join(',');
86
140
  }).join(',');
87
141
  const declarationString = declarations.map(([k, v]) => `${k}:${v};`).join('');
88
142
  return `${selectorString}{${declarationString}}`;
@@ -95,10 +149,10 @@ export function genCssContent(styleInfo, pageConfig, entryName) {
95
149
  * generate the css-in-js data
96
150
  */
97
151
  export function genCssOGInfo(styleInfo) {
98
- return Object.fromEntries(Object.entries(styleInfo).map(([cssId, cssInfos]) => {
99
- const oneCssOGInfo = {};
100
- cssInfos.rules = cssInfos.rules.filter(oneCssInfo => {
101
- oneCssInfo.sel = oneCssInfo.sel.filter(selectorList => {
152
+ const cssOGInfo = {};
153
+ for (const oneInfo of styleInfo) {
154
+ oneInfo.rules = oneInfo.rules.filter(oneRule => {
155
+ oneRule.sel = oneRule.sel.filter(selectorList => {
102
156
  const [classSelectors, pseudoClassSelectors, pseudoElementSelectors, combinator,] = selectorList;
103
157
  if (
104
158
  // only one class selector
@@ -107,23 +161,28 @@ export function genCssOGInfo(styleInfo) {
107
161
  && pseudoElementSelectors.length === 0
108
162
  && combinator.length === 0) {
109
163
  const selectorName = classSelectors[0].substring(1);
110
- const currentDeclarations = oneCssOGInfo[selectorName];
111
- if (currentDeclarations) {
112
- currentDeclarations.push(...oneCssInfo.decl);
113
- }
114
- else {
115
- oneCssOGInfo[selectorName] = oneCssInfo.decl;
164
+ for (const cssId of oneInfo.importBy) {
165
+ if (!cssOGInfo[cssId]) {
166
+ cssOGInfo[cssId] = {};
167
+ }
168
+ const currentDeclarations = cssOGInfo[cssId][selectorName];
169
+ if (currentDeclarations) {
170
+ currentDeclarations.push(...oneRule.decl);
171
+ }
172
+ else {
173
+ cssOGInfo[cssId][selectorName] = oneRule.decl;
174
+ }
116
175
  }
117
176
  return false; // remove this selector from style info
118
177
  }
119
178
  return true;
120
179
  });
121
- return oneCssInfo.sel.length > 0;
180
+ return oneRule.sel.length > 0;
122
181
  });
123
- return [cssId, oneCssOGInfo];
124
- }));
182
+ }
183
+ return cssOGInfo;
125
184
  }
126
- export function appendStyleElement(styleInfo, pageConfig, rootDom, document, entryName, ssrHydrateInfo) {
185
+ export function appendStyleElement(styleInfo, pageConfig, rootDom, document, ssrHydrateInfo) {
127
186
  const lynxUniqueIdToStyleRulesIndex = ssrHydrateInfo?.lynxUniqueIdToStyleRulesIndex ?? [];
128
187
  /**
129
188
  * now create the style content
@@ -133,23 +192,27 @@ export function appendStyleElement(styleInfo, pageConfig, rootDom, document, ent
133
192
  * 4. create the style element
134
193
  * 5. append the style element to the root dom
135
194
  */
136
- flattenStyleInfo(styleInfo, pageConfig.enableCSSSelector);
137
- transformToWebCss(styleInfo);
195
+ const flattenedStyleInfo = flattenStyleInfo(styleInfo);
196
+ transformToWebCss(flattenedStyleInfo);
138
197
  const cssOGInfo = pageConfig.enableCSSSelector
139
198
  ? {}
140
- : genCssOGInfo(styleInfo);
199
+ : genCssOGInfo(flattenedStyleInfo);
200
+ const lazyCSSOGInfo = {};
141
201
  let cardStyleElement;
142
202
  if (ssrHydrateInfo?.cardStyleElement) {
143
203
  cardStyleElement = ssrHydrateInfo.cardStyleElement;
144
204
  }
145
205
  else {
146
206
  cardStyleElement = document.createElement('style');
147
- cardStyleElement.textContent = genCssContent(styleInfo, pageConfig, entryName);
207
+ cardStyleElement.textContent = genCssContent(flattenedStyleInfo, pageConfig, undefined);
148
208
  rootDom.appendChild(cardStyleElement);
149
209
  }
150
- const cardStyleElementSheet = cardStyleElement.sheet;
151
- const updateCssOGStyle = (uniqueId, newClassName, cssID) => {
152
- const newStyles = decodeCssOG(newClassName, cssOGInfo, cssID);
210
+ const updateCssOGStyle = (uniqueId, newClassName, cssID, entryName) => {
211
+ const cardStyleElementSheet = cardStyleElement.sheet;
212
+ const styleMap = entryName && lazyCSSOGInfo[entryName]
213
+ ? lazyCSSOGInfo[entryName]
214
+ : cssOGInfo;
215
+ const newStyles = decodeCssOG(newClassName, styleMap, cssID);
153
216
  if (lynxUniqueIdToStyleRulesIndex[uniqueId] !== undefined) {
154
217
  const rule = cardStyleElementSheet
155
218
  .cssRules[lynxUniqueIdToStyleRulesIndex[uniqueId]];
@@ -160,6 +223,15 @@ export function appendStyleElement(styleInfo, pageConfig, rootDom, document, ent
160
223
  lynxUniqueIdToStyleRulesIndex[uniqueId] = index;
161
224
  }
162
225
  };
163
- return { updateCssOGStyle };
226
+ const updateLazyComponentStyle = (styleInfo, entryName) => {
227
+ const flattenedStyleInfo = flattenStyleInfo(styleInfo);
228
+ transformToWebCss(flattenedStyleInfo);
229
+ if (!pageConfig.enableCSSSelector) {
230
+ lazyCSSOGInfo[entryName] = genCssOGInfo(flattenedStyleInfo);
231
+ }
232
+ const newStyleSheet = genCssContent(flattenedStyleInfo, pageConfig, entryName);
233
+ cardStyleElement.textContent += newStyleSheet;
234
+ };
235
+ return { updateCssOGStyle, updateLazyComponentStyle };
164
236
  }
165
237
  //# sourceMappingURL=processStyleInfo.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynx-js/web-mainthread-apis",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "private": false,
5
5
  "description": "",
6
6
  "keywords": [],
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "hyphenate-style-name": "^1.1.0",
28
- "@lynx-js/web-constants": "0.16.0",
29
- "@lynx-js/web-style-transformer": "0.16.0"
28
+ "@lynx-js/web-constants": "0.17.0",
29
+ "@lynx-js/web-style-transformer": "0.17.0"
30
30
  }
31
31
  }