@edynamix/mf-isolation 0.0.31 → 0.0.33

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.
@@ -0,0 +1,124 @@
1
+ // Inside a shadow root no element is the document's <html> or <body>, so a rule aimed at the document root never
2
+ // matches. The build-time PostCSS plugins move such rules onto the module boundary; CSS a library injects at runtime
3
+ // skips that step. Angular Material's `html { --mat-focus-indicator-display: block; }` is one: hosted, the variable never
4
+ // reached Material's components and keyboard focus rings stayed off. This is the runtime counterpart for those rules.
5
+ const ROOT_COMPOUND_PATTERN = /^(?:html|body|:root)$/i;
6
+ const ROOT_LED_COMPOUND_PATTERN = /^(?:html|body)(?![\w-])|^:root(?![\w-])/i;
7
+ // A cheap first look: a document root at the start of a selector or after a combinator or comma.
8
+ const ROOT_SELECTOR_HINT_PATTERN = /(?:^|[\s,>+~])(?:html|body|:root)(?![\w-])/i;
9
+ // Rules that already name the boundary were scoped at build time or by the host, and stay as they are.
10
+ const SCOPED_SELECTOR_PATTERN = /data-edyn-mf|:host/i;
11
+ const COMBINATOR_PATTERN = /^[\s>+~]$/;
12
+ // Splits at top level only: commas, combinators and spaces inside (), [] and strings belong to their compound.
13
+ const splitTopLevel = (text, visit) => {
14
+ const parts = [];
15
+ let current = '';
16
+ let depth = 0;
17
+ let quote;
18
+ for (let index = 0; index < text.length; index += 1) {
19
+ const character = text[index] ?? '';
20
+ if (character === '\\') {
21
+ current += character + (text[index + 1] ?? '');
22
+ index += 1;
23
+ continue;
24
+ }
25
+ if (quote) {
26
+ quote = character === quote ? undefined : quote;
27
+ current += character;
28
+ continue;
29
+ }
30
+ if (character === '"' || character === "'") {
31
+ quote = character;
32
+ }
33
+ else if (character === '(' || character === '[') {
34
+ depth += 1;
35
+ }
36
+ else if (character === ')' || character === ']') {
37
+ depth -= 1;
38
+ }
39
+ if (visit(character, depth)) {
40
+ parts.push(current);
41
+ current = character;
42
+ continue;
43
+ }
44
+ current += character;
45
+ }
46
+ parts.push(current);
47
+ return parts;
48
+ };
49
+ const splitSelectorList = (selectorText) => {
50
+ return splitTopLevel(selectorText, (character, depth) => depth === 0 && character === ',').map((part) => part.replace(/^,/, '').trim());
51
+ };
52
+ const parseComplexSelector = (selector) => {
53
+ const tokens = splitTopLevel(selector, (character, depth) => depth === 0 && COMBINATOR_PATTERN.test(character));
54
+ const compounds = [];
55
+ const combinators = [];
56
+ let combinator;
57
+ for (const token of tokens) {
58
+ const separator = token[0] ?? '';
59
+ const compound = COMBINATOR_PATTERN.test(separator) ? token.slice(1) : token;
60
+ if (COMBINATOR_PATTERN.test(separator) && separator.trim()) {
61
+ combinator = separator;
62
+ }
63
+ else if (COMBINATOR_PATTERN.test(separator)) {
64
+ combinator ??= ' ';
65
+ }
66
+ if (!compound) {
67
+ continue;
68
+ }
69
+ if (compounds.length > 0) {
70
+ combinators.push(combinator ?? ' ');
71
+ }
72
+ else if (combinator?.trim()) {
73
+ // A relative selector (`> .x` in nested CSS) has no document root to move.
74
+ return undefined;
75
+ }
76
+ compounds.push(compound);
77
+ combinator = undefined;
78
+ }
79
+ return compounds.length > 0 ? { combinators, compounds } : undefined;
80
+ };
81
+ const rescopeComplexSelector = (selector) => {
82
+ const parsed = parseComplexSelector(selector);
83
+ const first = parsed?.compounds[0];
84
+ if (!parsed || !first || !ROOT_LED_COMPOUND_PATTERN.test(first)) {
85
+ return { qualified: false, selector };
86
+ }
87
+ let rootCompounds = 0;
88
+ while (ROOT_COMPOUND_PATTERN.test(parsed.compounds[rootCompounds] ?? '')) {
89
+ rootCompounds += 1;
90
+ }
91
+ const next = parsed.compounds[rootCompounds];
92
+ if (rootCompounds === 0 || (next !== undefined && ROOT_LED_COMPOUND_PATTERN.test(next))) {
93
+ return { qualified: true, selector };
94
+ }
95
+ let rescoped = ':host';
96
+ for (let index = rootCompounds; index < parsed.compounds.length; index += 1) {
97
+ const combinator = parsed.combinators[index - 1] ?? ' ';
98
+ rescoped += `${combinator === ' ' ? ' ' : ` ${combinator} `}${parsed.compounds[index]}`;
99
+ }
100
+ return { qualified: false, selector: rescoped };
101
+ };
102
+ /**
103
+ * Moves the selectors of a runtime rule that start at the document root onto the shadow host, as the PostCSS plugins
104
+ * do at build time for root-only selectors: `html`, `body`, `:root` and `html body` become `:host`, and `html .x`,
105
+ * `body > .x` or `:root .x` become `:host .x` and `:host > .x`. A root with conditions of its own is left alone and
106
+ * counted, and a list that already names `:host` or the module boundary is returned as it is.
107
+ */
108
+ export const rescopeDocumentRootSelectors = (selectorText) => {
109
+ if (!ROOT_SELECTOR_HINT_PATTERN.test(selectorText) || SCOPED_SELECTOR_PATTERN.test(selectorText) || selectorText.includes('&')) {
110
+ return { qualifiedRoots: 0, selectorText };
111
+ }
112
+ let qualifiedRoots = 0;
113
+ const selectors = [];
114
+ for (const part of splitSelectorList(selectorText)) {
115
+ const { qualified, selector } = rescopeComplexSelector(part);
116
+ qualifiedRoots += qualified ? 1 : 0;
117
+ if (!selectors.includes(selector)) {
118
+ selectors.push(selector);
119
+ }
120
+ }
121
+ const rescoped = selectors.join(', ');
122
+ return { qualifiedRoots, selectorText: rescoped === splitSelectorList(selectorText).join(', ') ? selectorText : rescoped };
123
+ };
124
+ //# sourceMappingURL=document-root-selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-root-selectors.js","sourceRoot":"","sources":["../src/document-root-selectors.ts"],"names":[],"mappings":"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","sourcesContent":["// Inside a shadow root no element is the document's <html> or <body>, so a rule aimed at the document root never\n// matches. The build-time PostCSS plugins move such rules onto the module boundary; CSS a library injects at runtime\n// skips that step. Angular Material's `html { --mat-focus-indicator-display: block; }` is one: hosted, the variable never\n// reached Material's components and keyboard focus rings stayed off. This is the runtime counterpart for those rules.\n\nexport interface IDocumentRootRescope {\n /** The selector list with exact document-root compounds moved onto `:host`; unchanged when nothing could move. */\n selectorText: string;\n /** Selectors led by a document root with conditions of its own (`html.dark`, `body#app`, `:root[dir]`), left alone. */\n qualifiedRoots: number;\n}\n\nconst ROOT_COMPOUND_PATTERN = /^(?:html|body|:root)$/i;\nconst ROOT_LED_COMPOUND_PATTERN = /^(?:html|body)(?![\\w-])|^:root(?![\\w-])/i;\n// A cheap first look: a document root at the start of a selector or after a combinator or comma.\nconst ROOT_SELECTOR_HINT_PATTERN = /(?:^|[\\s,>+~])(?:html|body|:root)(?![\\w-])/i;\n// Rules that already name the boundary were scoped at build time or by the host, and stay as they are.\nconst SCOPED_SELECTOR_PATTERN = /data-edyn-mf|:host/i;\nconst COMBINATOR_PATTERN = /^[\\s>+~]$/;\n\ninterface IComplexSelector {\n combinators: string[];\n compounds: string[];\n}\n\n// Splits at top level only: commas, combinators and spaces inside (), [] and strings belong to their compound.\nconst splitTopLevel = (text: string, visit: (character: string, depth: number) => boolean): string[] => {\n const parts: string[] = [];\n let current = '';\n let depth = 0;\n let quote: string | undefined;\n for (let index = 0; index < text.length; index += 1) {\n const character = text[index] ?? '';\n if (character === '\\\\') {\n current += character + (text[index + 1] ?? '');\n index += 1;\n continue;\n }\n if (quote) {\n quote = character === quote ? undefined : quote;\n current += character;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n } else if (character === '(' || character === '[') {\n depth += 1;\n } else if (character === ')' || character === ']') {\n depth -= 1;\n }\n if (visit(character, depth)) {\n parts.push(current);\n current = character;\n continue;\n }\n current += character;\n }\n parts.push(current);\n return parts;\n};\n\nconst splitSelectorList = (selectorText: string): string[] => {\n return splitTopLevel(selectorText, (character, depth) => depth === 0 && character === ',').map((part) => part.replace(/^,/, '').trim());\n};\n\nconst parseComplexSelector = (selector: string): IComplexSelector | undefined => {\n const tokens = splitTopLevel(selector, (character, depth) => depth === 0 && COMBINATOR_PATTERN.test(character));\n const compounds: string[] = [];\n const combinators: string[] = [];\n let combinator: string | undefined;\n for (const token of tokens) {\n const separator = token[0] ?? '';\n const compound = COMBINATOR_PATTERN.test(separator) ? token.slice(1) : token;\n if (COMBINATOR_PATTERN.test(separator) && separator.trim()) {\n combinator = separator;\n } else if (COMBINATOR_PATTERN.test(separator)) {\n combinator ??= ' ';\n }\n if (!compound) {\n continue;\n }\n if (compounds.length > 0) {\n combinators.push(combinator ?? ' ');\n } else if (combinator?.trim()) {\n // A relative selector (`> .x` in nested CSS) has no document root to move.\n return undefined;\n }\n compounds.push(compound);\n combinator = undefined;\n }\n return compounds.length > 0 ? { combinators, compounds } : undefined;\n};\n\nconst rescopeComplexSelector = (selector: string): { qualified: boolean; selector: string } => {\n const parsed = parseComplexSelector(selector);\n const first = parsed?.compounds[0];\n if (!parsed || !first || !ROOT_LED_COMPOUND_PATTERN.test(first)) {\n return { qualified: false, selector };\n }\n\n let rootCompounds = 0;\n while (ROOT_COMPOUND_PATTERN.test(parsed.compounds[rootCompounds] ?? '')) {\n rootCompounds += 1;\n }\n const next = parsed.compounds[rootCompounds];\n if (rootCompounds === 0 || (next !== undefined && ROOT_LED_COMPOUND_PATTERN.test(next))) {\n return { qualified: true, selector };\n }\n\n let rescoped = ':host';\n for (let index = rootCompounds; index < parsed.compounds.length; index += 1) {\n const combinator = parsed.combinators[index - 1] ?? ' ';\n rescoped += `${combinator === ' ' ? ' ' : ` ${combinator} `}${parsed.compounds[index]}`;\n }\n return { qualified: false, selector: rescoped };\n};\n\n/**\n * Moves the selectors of a runtime rule that start at the document root onto the shadow host, as the PostCSS plugins\n * do at build time for root-only selectors: `html`, `body`, `:root` and `html body` become `:host`, and `html .x`,\n * `body > .x` or `:root .x` become `:host .x` and `:host > .x`. A root with conditions of its own is left alone and\n * counted, and a list that already names `:host` or the module boundary is returned as it is.\n */\nexport const rescopeDocumentRootSelectors = (selectorText: string): IDocumentRootRescope => {\n if (!ROOT_SELECTOR_HINT_PATTERN.test(selectorText) || SCOPED_SELECTOR_PATTERN.test(selectorText) || selectorText.includes('&')) {\n return { qualifiedRoots: 0, selectorText };\n }\n\n let qualifiedRoots = 0;\n const selectors: string[] = [];\n for (const part of splitSelectorList(selectorText)) {\n const { qualified, selector } = rescopeComplexSelector(part);\n qualifiedRoots += qualified ? 1 : 0;\n if (!selectors.includes(selector)) {\n selectors.push(selector);\n }\n }\n const rescoped = selectors.join(', ');\n return { qualifiedRoots, selectorText: rescoped === splitSelectorList(selectorText).join(', ') ? selectorText : rescoped };\n};\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Makes rem lengths in a hosted module's shadow root follow the root font size its own page had, once the module's
3
+ * CSS declares one (see MF_ROOT_FONT_SIZE_PROPERTY): in stylesheets already in the root and every one added or changed
4
+ * later, such as the component styles Angular adds as components first render, and in inline style attributes,
5
+ * including ones inserted or changed later. A module whose CSS never sizes its root is left untouched. Returns a
6
+ * function that stops following; rewritten rules and styles stay rewritten.
7
+ */
8
+ export declare const followModuleRootFontSize: (root: ShadowRoot) => (() => void);
9
+ /**
10
+ * Adapts the CSS a module brings into its shadow root at runtime to the hosted page, for a host to call on each module
11
+ * shadow root it creates and stop on unmount: rem follows the module's own root font size (as followModuleRootFontSize
12
+ * does), and rules a library injects for the document root (`html { --mat-focus-indicator-display: block; }`), which
13
+ * never match inside a shadow root, move onto `:host` (see rescopeDocumentRootSelectors). Safe beside a module's own
14
+ * followModuleRootFontSize on the same root: every step is idempotent, and neither writes anything the other acts on.
15
+ */
16
+ export declare const adaptHostedModuleStyles: (root: ShadowRoot) => (() => void);
17
+ //# sourceMappingURL=hosted-module-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-module-styles.d.ts","sourceRoot":"","sources":["../src/hosted-module-styles.ts"],"names":[],"mappings":"AAiPA;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM,UAAU,KAAG,CAAC,MAAM,IAAI,CAEtE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,UAAU,KAAG,CAAC,MAAM,IAAI,CAErE,CAAC","sourcesContent":["import { rescopeDocumentRootSelectors } from './document-root-selectors.js';\nimport {\n MF_ROOT_FONT_SIZE_PROPERTY,\n REM_LENGTH_HINT_PATTERN,\n ROOT_FONT_SIZE_DECLARATION_PATTERN,\n rewriteRemLengths,\n UNFOLLOWED_REM_PATTERN,\n} from './root-font-size.js';\n\nconst INLINE_REM_SELECTOR = '[style*=\"rem\" i]';\n\n// By class name, not instanceof: a root in another window (a frame, or a test's own window) has its own CSSFontFaceRule.\nconst isFontFaceRule = (rule: CSSRule): boolean => {\n return rule.constructor?.name === 'CSSFontFaceRule';\n};\n\n// Rule kinds are told apart by what they carry, not by class: @import has a sheet, style and keyframe rules have\n// declarations, and grouping rules (@media, @supports, @container, @layer, @keyframes, nesting) have child rules.\nconst importedSheetOf = (rule: CSSRule): CSSStyleSheet | undefined => {\n return 'styleSheet' in rule && rule.styleSheet ? (rule.styleSheet as CSSStyleSheet) : undefined;\n};\n\nconst declarationsOf = (rule: CSSRule): CSSStyleDeclaration | undefined => {\n return 'style' in rule && rule.style && !isFontFaceRule(rule) ? (rule.style as CSSStyleDeclaration) : undefined;\n};\n\nconst childRulesOf = (rule: CSSRule): CSSRuleList | undefined => {\n return 'cssRules' in rule && rule.cssRules ? (rule.cssRules as CSSRuleList) : undefined;\n};\n\nconst readRules = (sheet: CSSStyleSheet): CSSRuleList | undefined => {\n try {\n return sheet.cssRules;\n } catch {\n // A cross-origin sheet (a font service's @font-face list) cannot be read, and none of its rules are module rules.\n return undefined;\n }\n};\n\nconst walkRules = (sheet: CSSStyleSheet, visit: (rule: CSSRule) => void): void => {\n const walk = (rules: CSSRuleList): void => {\n for (const rule of Array.from(rules)) {\n const importedSheet = importedSheetOf(rule);\n const importedRules = importedSheet ? readRules(importedSheet) : undefined;\n if (importedRules) {\n walk(importedRules);\n }\n visit(rule);\n const childRules = childRulesOf(rule);\n if (childRules) {\n walk(childRules);\n }\n }\n };\n const rules = readRules(sheet);\n if (rules) {\n walk(rules);\n }\n};\n\n// Only declaration blocks change, through the CSSOM: @media, @container and @supports conditions keep their rem (var()\n// is invalid there, and a media query rem is the browser's initial size anyway), and !important stays with its value.\n// CSSOM changes are not DOM mutations, so no observer, this one or another on the same root, sees them.\nconst followRemInSheet = (sheet: CSSStyleSheet): void => {\n walkRules(sheet, (rule) => {\n const declarations = declarationsOf(rule);\n if (declarations && REM_LENGTH_HINT_PATTERN.test(declarations.cssText)) {\n const rewritten = rewriteRemLengths(declarations.cssText);\n if (rewritten !== declarations.cssText) {\n declarations.cssText = rewritten;\n }\n }\n });\n};\n\nconst rescopeDocumentRootRules = (sheet: CSSStyleSheet): void => {\n walkRules(sheet, (rule) => {\n if (!('selectorText' in rule) || typeof rule.selectorText !== 'string') {\n return;\n }\n const { selectorText } = rescopeDocumentRootSelectors(rule.selectorText);\n if (selectorText !== rule.selectorText) {\n (rule as CSSStyleRule).selectorText = selectorText;\n }\n });\n};\n\nconst declaresRootFontSize = (sheet: CSSStyleSheet): boolean => {\n let declares = false;\n walkRules(sheet, (rule) => {\n declares ||= !!declarationsOf(rule)?.getPropertyValue(MF_ROOT_FONT_SIZE_PROPERTY);\n });\n return declares;\n};\n\n// Inline styles carry rem too, from templates (`style=\"padding-bottom: 0.25rem !important\"`) and from style bindings\n// and code. The whole declaration block is rewritten through element.style, as for sheet rules: !important stays with\n// its value, and a shorthand that also holds var() (whose longhands read empty) is still seen. Material sets inline\n// styles constantly (ripples, overlay positions), so anything without an unfollowed rem length returns at once, and a\n// followed value never looks like work again, which is also why a second observer on the same root writes nothing.\nconst followInlineStyle = (element: Element): void => {\n const attribute = element.getAttribute('style');\n if (!attribute || !UNFOLLOWED_REM_PATTERN.test(attribute)) {\n return;\n }\n const style = (element as HTMLElement).style;\n const declarations = style?.cssText;\n if (!declarations) {\n return;\n }\n const rewritten = rewriteRemLengths(declarations);\n if (rewritten !== declarations) {\n style.cssText = rewritten;\n }\n};\n\nconst followInlineStylesWithin = (node: Node): void => {\n if (node.nodeType !== 1) {\n return;\n }\n const element = node as Element;\n followInlineStyle(element);\n if (element.firstElementChild) {\n for (const descendant of Array.from(element.querySelectorAll(INLINE_REM_SELECTOR))) {\n followInlineStyle(descendant);\n }\n }\n};\n\ntype TStyleOwner = HTMLLinkElement | HTMLStyleElement;\n\nconst isStyleOwner = (node: Node): node is TStyleOwner => {\n return node.nodeName === 'STYLE' || (node.nodeName === 'LINK' && (node as HTMLLinkElement).rel.split(/\\s+/).includes('stylesheet'));\n};\n\ninterface IAdaptStylesOptions {\n documentRootRules: boolean;\n}\n\nconst adaptShadowRootStyles = (root: ShadowRoot, { documentRootRules }: IAdaptStylesOptions): (() => void) => {\n // The root's own window, which need not be the global one (a frame, or a test's own window).\n const RootMutationObserver = root.ownerDocument.defaultView?.MutationObserver ?? MutationObserver;\n let followingRem = false;\n const remSheets = new WeakSet<CSSStyleSheet>();\n const rescopedSheets = new WeakSet<CSSStyleSheet>();\n const watchedStyles = new WeakSet<HTMLStyleElement>();\n\n const owners = (): TStyleOwner[] => Array.from(root.childNodes).filter(isStyleOwner);\n\n const sheetsOf = (styleOwners: TStyleOwner[]): CSSStyleSheet[] => {\n return [...(root.adoptedStyleSheets ?? []), ...styleOwners.map((owner) => owner.sheet).filter((sheet): sheet is CSSStyleSheet => !!sheet)];\n };\n\n // A <style>'s own text is the cheap check; linked and adopted sheets have only their rules.\n const findsRootFontSize = (styleOwners: TStyleOwner[]): boolean => {\n const styles = styleOwners.filter((owner): owner is HTMLStyleElement => owner.nodeName === 'STYLE');\n if (styles.some((style) => ROOT_FONT_SIZE_DECLARATION_PATTERN.test(style.textContent ?? ''))) {\n return true;\n }\n const linkedSheets = styleOwners.filter((owner) => owner.nodeName === 'LINK').map((owner) => owner.sheet);\n return [...(root.adoptedStyleSheets ?? []), ...linkedSheets].some((sheet) => !!sheet && declaresRootFontSize(sheet));\n };\n\n // Started only once the module's CSS declares a root size, so a module without one pays nothing. An element gets its\n // template styles before it is inserted, so insertions are scanned as well as later style attribute changes.\n let inlineObserver: MutationObserver | undefined;\n const followInlineStyles = (): void => {\n for (const element of Array.from(root.querySelectorAll(INLINE_REM_SELECTOR))) {\n followInlineStyle(element);\n }\n inlineObserver = new RootMutationObserver((records) => {\n for (const record of records) {\n if (record.type === 'attributes') {\n followInlineStyle(record.target as Element);\n } else {\n for (const node of Array.from(record.addedNodes)) {\n followInlineStylesWithin(node);\n }\n }\n }\n // Only this callback ran since the records were delivered, so what is queued now are its own rewrites.\n inlineObserver?.takeRecords();\n });\n inlineObserver.observe(root, { attributeFilter: ['style'], attributes: true, childList: true, subtree: true });\n };\n\n const observer = new RootMutationObserver(() => update());\n\n const update = (): void => {\n const styleOwners = owners();\n for (const owner of styleOwners) {\n if (owner.nodeName === 'STYLE' && !watchedStyles.has(owner as HTMLStyleElement)) {\n // Replacing a <style>'s text gives it a new sheet, which must be adapted again.\n observer.observe(owner, { characterData: true, childList: true, subtree: true });\n watchedStyles.add(owner as HTMLStyleElement);\n }\n }\n\n const sheets = sheetsOf(styleOwners);\n if (documentRootRules) {\n for (const sheet of sheets) {\n if (!rescopedSheets.has(sheet)) {\n rescopeDocumentRootRules(sheet);\n rescopedSheets.add(sheet);\n }\n }\n }\n\n followingRem ||= findsRootFontSize(styleOwners);\n if (!followingRem) {\n return;\n }\n if (!inlineObserver) {\n followInlineStyles();\n }\n for (const sheet of sheets) {\n if (!remSheets.has(sheet)) {\n followRemInSheet(sheet);\n remSheets.add(sheet);\n }\n }\n };\n\n // A <link> sheet has no rules until it loads; load does not bubble, so it is caught on the way down.\n const onLoad = (event: Event): void => {\n if (event.target instanceof Node && isStyleOwner(event.target)) {\n update();\n }\n };\n\n observer.observe(root, { childList: true });\n root.addEventListener('load', onLoad, true);\n update();\n\n return () => {\n observer.disconnect();\n inlineObserver?.disconnect();\n root.removeEventListener('load', onLoad, true);\n };\n};\n\n/**\n * Makes rem lengths in a hosted module's shadow root follow the root font size its own page had, once the module's\n * CSS declares one (see MF_ROOT_FONT_SIZE_PROPERTY): in stylesheets already in the root and every one added or changed\n * later, such as the component styles Angular adds as components first render, and in inline style attributes,\n * including ones inserted or changed later. A module whose CSS never sizes its root is left untouched. Returns a\n * function that stops following; rewritten rules and styles stay rewritten.\n */\nexport const followModuleRootFontSize = (root: ShadowRoot): (() => void) => {\n return adaptShadowRootStyles(root, { documentRootRules: false });\n};\n\n/**\n * Adapts the CSS a module brings into its shadow root at runtime to the hosted page, for a host to call on each module\n * shadow root it creates and stop on unmount: rem follows the module's own root font size (as followModuleRootFontSize\n * does), and rules a library injects for the document root (`html { --mat-focus-indicator-display: block; }`), which\n * never match inside a shadow root, move onto `:host` (see rescopeDocumentRootSelectors). Safe beside a module's own\n * followModuleRootFontSize on the same root: every step is idempotent, and neither writes anything the other acts on.\n */\nexport const adaptHostedModuleStyles = (root: ShadowRoot): (() => void) => {\n return adaptShadowRootStyles(root, { documentRootRules: true });\n};\n"]}
@@ -0,0 +1,226 @@
1
+ import { rescopeDocumentRootSelectors } from './document-root-selectors.js';
2
+ import { MF_ROOT_FONT_SIZE_PROPERTY, REM_LENGTH_HINT_PATTERN, ROOT_FONT_SIZE_DECLARATION_PATTERN, rewriteRemLengths, UNFOLLOWED_REM_PATTERN, } from './root-font-size.js';
3
+ const INLINE_REM_SELECTOR = '[style*="rem" i]';
4
+ // By class name, not instanceof: a root in another window (a frame, or a test's own window) has its own CSSFontFaceRule.
5
+ const isFontFaceRule = (rule) => {
6
+ return rule.constructor?.name === 'CSSFontFaceRule';
7
+ };
8
+ // Rule kinds are told apart by what they carry, not by class: @import has a sheet, style and keyframe rules have
9
+ // declarations, and grouping rules (@media, @supports, @container, @layer, @keyframes, nesting) have child rules.
10
+ const importedSheetOf = (rule) => {
11
+ return 'styleSheet' in rule && rule.styleSheet ? rule.styleSheet : undefined;
12
+ };
13
+ const declarationsOf = (rule) => {
14
+ return 'style' in rule && rule.style && !isFontFaceRule(rule) ? rule.style : undefined;
15
+ };
16
+ const childRulesOf = (rule) => {
17
+ return 'cssRules' in rule && rule.cssRules ? rule.cssRules : undefined;
18
+ };
19
+ const readRules = (sheet) => {
20
+ try {
21
+ return sheet.cssRules;
22
+ }
23
+ catch {
24
+ // A cross-origin sheet (a font service's @font-face list) cannot be read, and none of its rules are module rules.
25
+ return undefined;
26
+ }
27
+ };
28
+ const walkRules = (sheet, visit) => {
29
+ const walk = (rules) => {
30
+ for (const rule of Array.from(rules)) {
31
+ const importedSheet = importedSheetOf(rule);
32
+ const importedRules = importedSheet ? readRules(importedSheet) : undefined;
33
+ if (importedRules) {
34
+ walk(importedRules);
35
+ }
36
+ visit(rule);
37
+ const childRules = childRulesOf(rule);
38
+ if (childRules) {
39
+ walk(childRules);
40
+ }
41
+ }
42
+ };
43
+ const rules = readRules(sheet);
44
+ if (rules) {
45
+ walk(rules);
46
+ }
47
+ };
48
+ // Only declaration blocks change, through the CSSOM: @media, @container and @supports conditions keep their rem (var()
49
+ // is invalid there, and a media query rem is the browser's initial size anyway), and !important stays with its value.
50
+ // CSSOM changes are not DOM mutations, so no observer, this one or another on the same root, sees them.
51
+ const followRemInSheet = (sheet) => {
52
+ walkRules(sheet, (rule) => {
53
+ const declarations = declarationsOf(rule);
54
+ if (declarations && REM_LENGTH_HINT_PATTERN.test(declarations.cssText)) {
55
+ const rewritten = rewriteRemLengths(declarations.cssText);
56
+ if (rewritten !== declarations.cssText) {
57
+ declarations.cssText = rewritten;
58
+ }
59
+ }
60
+ });
61
+ };
62
+ const rescopeDocumentRootRules = (sheet) => {
63
+ walkRules(sheet, (rule) => {
64
+ if (!('selectorText' in rule) || typeof rule.selectorText !== 'string') {
65
+ return;
66
+ }
67
+ const { selectorText } = rescopeDocumentRootSelectors(rule.selectorText);
68
+ if (selectorText !== rule.selectorText) {
69
+ rule.selectorText = selectorText;
70
+ }
71
+ });
72
+ };
73
+ const declaresRootFontSize = (sheet) => {
74
+ let declares = false;
75
+ walkRules(sheet, (rule) => {
76
+ declares ||= !!declarationsOf(rule)?.getPropertyValue(MF_ROOT_FONT_SIZE_PROPERTY);
77
+ });
78
+ return declares;
79
+ };
80
+ // Inline styles carry rem too, from templates (`style="padding-bottom: 0.25rem !important"`) and from style bindings
81
+ // and code. The whole declaration block is rewritten through element.style, as for sheet rules: !important stays with
82
+ // its value, and a shorthand that also holds var() (whose longhands read empty) is still seen. Material sets inline
83
+ // styles constantly (ripples, overlay positions), so anything without an unfollowed rem length returns at once, and a
84
+ // followed value never looks like work again, which is also why a second observer on the same root writes nothing.
85
+ const followInlineStyle = (element) => {
86
+ const attribute = element.getAttribute('style');
87
+ if (!attribute || !UNFOLLOWED_REM_PATTERN.test(attribute)) {
88
+ return;
89
+ }
90
+ const style = element.style;
91
+ const declarations = style?.cssText;
92
+ if (!declarations) {
93
+ return;
94
+ }
95
+ const rewritten = rewriteRemLengths(declarations);
96
+ if (rewritten !== declarations) {
97
+ style.cssText = rewritten;
98
+ }
99
+ };
100
+ const followInlineStylesWithin = (node) => {
101
+ if (node.nodeType !== 1) {
102
+ return;
103
+ }
104
+ const element = node;
105
+ followInlineStyle(element);
106
+ if (element.firstElementChild) {
107
+ for (const descendant of Array.from(element.querySelectorAll(INLINE_REM_SELECTOR))) {
108
+ followInlineStyle(descendant);
109
+ }
110
+ }
111
+ };
112
+ const isStyleOwner = (node) => {
113
+ return node.nodeName === 'STYLE' || (node.nodeName === 'LINK' && node.rel.split(/\s+/).includes('stylesheet'));
114
+ };
115
+ const adaptShadowRootStyles = (root, { documentRootRules }) => {
116
+ // The root's own window, which need not be the global one (a frame, or a test's own window).
117
+ const RootMutationObserver = root.ownerDocument.defaultView?.MutationObserver ?? MutationObserver;
118
+ let followingRem = false;
119
+ const remSheets = new WeakSet();
120
+ const rescopedSheets = new WeakSet();
121
+ const watchedStyles = new WeakSet();
122
+ const owners = () => Array.from(root.childNodes).filter(isStyleOwner);
123
+ const sheetsOf = (styleOwners) => {
124
+ return [...(root.adoptedStyleSheets ?? []), ...styleOwners.map((owner) => owner.sheet).filter((sheet) => !!sheet)];
125
+ };
126
+ // A <style>'s own text is the cheap check; linked and adopted sheets have only their rules.
127
+ const findsRootFontSize = (styleOwners) => {
128
+ const styles = styleOwners.filter((owner) => owner.nodeName === 'STYLE');
129
+ if (styles.some((style) => ROOT_FONT_SIZE_DECLARATION_PATTERN.test(style.textContent ?? ''))) {
130
+ return true;
131
+ }
132
+ const linkedSheets = styleOwners.filter((owner) => owner.nodeName === 'LINK').map((owner) => owner.sheet);
133
+ return [...(root.adoptedStyleSheets ?? []), ...linkedSheets].some((sheet) => !!sheet && declaresRootFontSize(sheet));
134
+ };
135
+ // Started only once the module's CSS declares a root size, so a module without one pays nothing. An element gets its
136
+ // template styles before it is inserted, so insertions are scanned as well as later style attribute changes.
137
+ let inlineObserver;
138
+ const followInlineStyles = () => {
139
+ for (const element of Array.from(root.querySelectorAll(INLINE_REM_SELECTOR))) {
140
+ followInlineStyle(element);
141
+ }
142
+ inlineObserver = new RootMutationObserver((records) => {
143
+ for (const record of records) {
144
+ if (record.type === 'attributes') {
145
+ followInlineStyle(record.target);
146
+ }
147
+ else {
148
+ for (const node of Array.from(record.addedNodes)) {
149
+ followInlineStylesWithin(node);
150
+ }
151
+ }
152
+ }
153
+ // Only this callback ran since the records were delivered, so what is queued now are its own rewrites.
154
+ inlineObserver?.takeRecords();
155
+ });
156
+ inlineObserver.observe(root, { attributeFilter: ['style'], attributes: true, childList: true, subtree: true });
157
+ };
158
+ const observer = new RootMutationObserver(() => update());
159
+ const update = () => {
160
+ const styleOwners = owners();
161
+ for (const owner of styleOwners) {
162
+ if (owner.nodeName === 'STYLE' && !watchedStyles.has(owner)) {
163
+ // Replacing a <style>'s text gives it a new sheet, which must be adapted again.
164
+ observer.observe(owner, { characterData: true, childList: true, subtree: true });
165
+ watchedStyles.add(owner);
166
+ }
167
+ }
168
+ const sheets = sheetsOf(styleOwners);
169
+ if (documentRootRules) {
170
+ for (const sheet of sheets) {
171
+ if (!rescopedSheets.has(sheet)) {
172
+ rescopeDocumentRootRules(sheet);
173
+ rescopedSheets.add(sheet);
174
+ }
175
+ }
176
+ }
177
+ followingRem ||= findsRootFontSize(styleOwners);
178
+ if (!followingRem) {
179
+ return;
180
+ }
181
+ if (!inlineObserver) {
182
+ followInlineStyles();
183
+ }
184
+ for (const sheet of sheets) {
185
+ if (!remSheets.has(sheet)) {
186
+ followRemInSheet(sheet);
187
+ remSheets.add(sheet);
188
+ }
189
+ }
190
+ };
191
+ // A <link> sheet has no rules until it loads; load does not bubble, so it is caught on the way down.
192
+ const onLoad = (event) => {
193
+ if (event.target instanceof Node && isStyleOwner(event.target)) {
194
+ update();
195
+ }
196
+ };
197
+ observer.observe(root, { childList: true });
198
+ root.addEventListener('load', onLoad, true);
199
+ update();
200
+ return () => {
201
+ observer.disconnect();
202
+ inlineObserver?.disconnect();
203
+ root.removeEventListener('load', onLoad, true);
204
+ };
205
+ };
206
+ /**
207
+ * Makes rem lengths in a hosted module's shadow root follow the root font size its own page had, once the module's
208
+ * CSS declares one (see MF_ROOT_FONT_SIZE_PROPERTY): in stylesheets already in the root and every one added or changed
209
+ * later, such as the component styles Angular adds as components first render, and in inline style attributes,
210
+ * including ones inserted or changed later. A module whose CSS never sizes its root is left untouched. Returns a
211
+ * function that stops following; rewritten rules and styles stay rewritten.
212
+ */
213
+ export const followModuleRootFontSize = (root) => {
214
+ return adaptShadowRootStyles(root, { documentRootRules: false });
215
+ };
216
+ /**
217
+ * Adapts the CSS a module brings into its shadow root at runtime to the hosted page, for a host to call on each module
218
+ * shadow root it creates and stop on unmount: rem follows the module's own root font size (as followModuleRootFontSize
219
+ * does), and rules a library injects for the document root (`html { --mat-focus-indicator-display: block; }`), which
220
+ * never match inside a shadow root, move onto `:host` (see rescopeDocumentRootSelectors). Safe beside a module's own
221
+ * followModuleRootFontSize on the same root: every step is idempotent, and neither writes anything the other acts on.
222
+ */
223
+ export const adaptHostedModuleStyles = (root) => {
224
+ return adaptShadowRootStyles(root, { documentRootRules: true });
225
+ };
226
+ //# sourceMappingURL=hosted-module-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-module-styles.js","sourceRoot":"","sources":["../src/hosted-module-styles.ts"],"names":[],"mappings":"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","sourcesContent":["import { rescopeDocumentRootSelectors } from './document-root-selectors.js';\nimport {\n MF_ROOT_FONT_SIZE_PROPERTY,\n REM_LENGTH_HINT_PATTERN,\n ROOT_FONT_SIZE_DECLARATION_PATTERN,\n rewriteRemLengths,\n UNFOLLOWED_REM_PATTERN,\n} from './root-font-size.js';\n\nconst INLINE_REM_SELECTOR = '[style*=\"rem\" i]';\n\n// By class name, not instanceof: a root in another window (a frame, or a test's own window) has its own CSSFontFaceRule.\nconst isFontFaceRule = (rule: CSSRule): boolean => {\n return rule.constructor?.name === 'CSSFontFaceRule';\n};\n\n// Rule kinds are told apart by what they carry, not by class: @import has a sheet, style and keyframe rules have\n// declarations, and grouping rules (@media, @supports, @container, @layer, @keyframes, nesting) have child rules.\nconst importedSheetOf = (rule: CSSRule): CSSStyleSheet | undefined => {\n return 'styleSheet' in rule && rule.styleSheet ? (rule.styleSheet as CSSStyleSheet) : undefined;\n};\n\nconst declarationsOf = (rule: CSSRule): CSSStyleDeclaration | undefined => {\n return 'style' in rule && rule.style && !isFontFaceRule(rule) ? (rule.style as CSSStyleDeclaration) : undefined;\n};\n\nconst childRulesOf = (rule: CSSRule): CSSRuleList | undefined => {\n return 'cssRules' in rule && rule.cssRules ? (rule.cssRules as CSSRuleList) : undefined;\n};\n\nconst readRules = (sheet: CSSStyleSheet): CSSRuleList | undefined => {\n try {\n return sheet.cssRules;\n } catch {\n // A cross-origin sheet (a font service's @font-face list) cannot be read, and none of its rules are module rules.\n return undefined;\n }\n};\n\nconst walkRules = (sheet: CSSStyleSheet, visit: (rule: CSSRule) => void): void => {\n const walk = (rules: CSSRuleList): void => {\n for (const rule of Array.from(rules)) {\n const importedSheet = importedSheetOf(rule);\n const importedRules = importedSheet ? readRules(importedSheet) : undefined;\n if (importedRules) {\n walk(importedRules);\n }\n visit(rule);\n const childRules = childRulesOf(rule);\n if (childRules) {\n walk(childRules);\n }\n }\n };\n const rules = readRules(sheet);\n if (rules) {\n walk(rules);\n }\n};\n\n// Only declaration blocks change, through the CSSOM: @media, @container and @supports conditions keep their rem (var()\n// is invalid there, and a media query rem is the browser's initial size anyway), and !important stays with its value.\n// CSSOM changes are not DOM mutations, so no observer, this one or another on the same root, sees them.\nconst followRemInSheet = (sheet: CSSStyleSheet): void => {\n walkRules(sheet, (rule) => {\n const declarations = declarationsOf(rule);\n if (declarations && REM_LENGTH_HINT_PATTERN.test(declarations.cssText)) {\n const rewritten = rewriteRemLengths(declarations.cssText);\n if (rewritten !== declarations.cssText) {\n declarations.cssText = rewritten;\n }\n }\n });\n};\n\nconst rescopeDocumentRootRules = (sheet: CSSStyleSheet): void => {\n walkRules(sheet, (rule) => {\n if (!('selectorText' in rule) || typeof rule.selectorText !== 'string') {\n return;\n }\n const { selectorText } = rescopeDocumentRootSelectors(rule.selectorText);\n if (selectorText !== rule.selectorText) {\n (rule as CSSStyleRule).selectorText = selectorText;\n }\n });\n};\n\nconst declaresRootFontSize = (sheet: CSSStyleSheet): boolean => {\n let declares = false;\n walkRules(sheet, (rule) => {\n declares ||= !!declarationsOf(rule)?.getPropertyValue(MF_ROOT_FONT_SIZE_PROPERTY);\n });\n return declares;\n};\n\n// Inline styles carry rem too, from templates (`style=\"padding-bottom: 0.25rem !important\"`) and from style bindings\n// and code. The whole declaration block is rewritten through element.style, as for sheet rules: !important stays with\n// its value, and a shorthand that also holds var() (whose longhands read empty) is still seen. Material sets inline\n// styles constantly (ripples, overlay positions), so anything without an unfollowed rem length returns at once, and a\n// followed value never looks like work again, which is also why a second observer on the same root writes nothing.\nconst followInlineStyle = (element: Element): void => {\n const attribute = element.getAttribute('style');\n if (!attribute || !UNFOLLOWED_REM_PATTERN.test(attribute)) {\n return;\n }\n const style = (element as HTMLElement).style;\n const declarations = style?.cssText;\n if (!declarations) {\n return;\n }\n const rewritten = rewriteRemLengths(declarations);\n if (rewritten !== declarations) {\n style.cssText = rewritten;\n }\n};\n\nconst followInlineStylesWithin = (node: Node): void => {\n if (node.nodeType !== 1) {\n return;\n }\n const element = node as Element;\n followInlineStyle(element);\n if (element.firstElementChild) {\n for (const descendant of Array.from(element.querySelectorAll(INLINE_REM_SELECTOR))) {\n followInlineStyle(descendant);\n }\n }\n};\n\ntype TStyleOwner = HTMLLinkElement | HTMLStyleElement;\n\nconst isStyleOwner = (node: Node): node is TStyleOwner => {\n return node.nodeName === 'STYLE' || (node.nodeName === 'LINK' && (node as HTMLLinkElement).rel.split(/\\s+/).includes('stylesheet'));\n};\n\ninterface IAdaptStylesOptions {\n documentRootRules: boolean;\n}\n\nconst adaptShadowRootStyles = (root: ShadowRoot, { documentRootRules }: IAdaptStylesOptions): (() => void) => {\n // The root's own window, which need not be the global one (a frame, or a test's own window).\n const RootMutationObserver = root.ownerDocument.defaultView?.MutationObserver ?? MutationObserver;\n let followingRem = false;\n const remSheets = new WeakSet<CSSStyleSheet>();\n const rescopedSheets = new WeakSet<CSSStyleSheet>();\n const watchedStyles = new WeakSet<HTMLStyleElement>();\n\n const owners = (): TStyleOwner[] => Array.from(root.childNodes).filter(isStyleOwner);\n\n const sheetsOf = (styleOwners: TStyleOwner[]): CSSStyleSheet[] => {\n return [...(root.adoptedStyleSheets ?? []), ...styleOwners.map((owner) => owner.sheet).filter((sheet): sheet is CSSStyleSheet => !!sheet)];\n };\n\n // A <style>'s own text is the cheap check; linked and adopted sheets have only their rules.\n const findsRootFontSize = (styleOwners: TStyleOwner[]): boolean => {\n const styles = styleOwners.filter((owner): owner is HTMLStyleElement => owner.nodeName === 'STYLE');\n if (styles.some((style) => ROOT_FONT_SIZE_DECLARATION_PATTERN.test(style.textContent ?? ''))) {\n return true;\n }\n const linkedSheets = styleOwners.filter((owner) => owner.nodeName === 'LINK').map((owner) => owner.sheet);\n return [...(root.adoptedStyleSheets ?? []), ...linkedSheets].some((sheet) => !!sheet && declaresRootFontSize(sheet));\n };\n\n // Started only once the module's CSS declares a root size, so a module without one pays nothing. An element gets its\n // template styles before it is inserted, so insertions are scanned as well as later style attribute changes.\n let inlineObserver: MutationObserver | undefined;\n const followInlineStyles = (): void => {\n for (const element of Array.from(root.querySelectorAll(INLINE_REM_SELECTOR))) {\n followInlineStyle(element);\n }\n inlineObserver = new RootMutationObserver((records) => {\n for (const record of records) {\n if (record.type === 'attributes') {\n followInlineStyle(record.target as Element);\n } else {\n for (const node of Array.from(record.addedNodes)) {\n followInlineStylesWithin(node);\n }\n }\n }\n // Only this callback ran since the records were delivered, so what is queued now are its own rewrites.\n inlineObserver?.takeRecords();\n });\n inlineObserver.observe(root, { attributeFilter: ['style'], attributes: true, childList: true, subtree: true });\n };\n\n const observer = new RootMutationObserver(() => update());\n\n const update = (): void => {\n const styleOwners = owners();\n for (const owner of styleOwners) {\n if (owner.nodeName === 'STYLE' && !watchedStyles.has(owner as HTMLStyleElement)) {\n // Replacing a <style>'s text gives it a new sheet, which must be adapted again.\n observer.observe(owner, { characterData: true, childList: true, subtree: true });\n watchedStyles.add(owner as HTMLStyleElement);\n }\n }\n\n const sheets = sheetsOf(styleOwners);\n if (documentRootRules) {\n for (const sheet of sheets) {\n if (!rescopedSheets.has(sheet)) {\n rescopeDocumentRootRules(sheet);\n rescopedSheets.add(sheet);\n }\n }\n }\n\n followingRem ||= findsRootFontSize(styleOwners);\n if (!followingRem) {\n return;\n }\n if (!inlineObserver) {\n followInlineStyles();\n }\n for (const sheet of sheets) {\n if (!remSheets.has(sheet)) {\n followRemInSheet(sheet);\n remSheets.add(sheet);\n }\n }\n };\n\n // A <link> sheet has no rules until it loads; load does not bubble, so it is caught on the way down.\n const onLoad = (event: Event): void => {\n if (event.target instanceof Node && isStyleOwner(event.target)) {\n update();\n }\n };\n\n observer.observe(root, { childList: true });\n root.addEventListener('load', onLoad, true);\n update();\n\n return () => {\n observer.disconnect();\n inlineObserver?.disconnect();\n root.removeEventListener('load', onLoad, true);\n };\n};\n\n/**\n * Makes rem lengths in a hosted module's shadow root follow the root font size its own page had, once the module's\n * CSS declares one (see MF_ROOT_FONT_SIZE_PROPERTY): in stylesheets already in the root and every one added or changed\n * later, such as the component styles Angular adds as components first render, and in inline style attributes,\n * including ones inserted or changed later. A module whose CSS never sizes its root is left untouched. Returns a\n * function that stops following; rewritten rules and styles stay rewritten.\n */\nexport const followModuleRootFontSize = (root: ShadowRoot): (() => void) => {\n return adaptShadowRootStyles(root, { documentRootRules: false });\n};\n\n/**\n * Adapts the CSS a module brings into its shadow root at runtime to the hosted page, for a host to call on each module\n * shadow root it creates and stop on unmount: rem follows the module's own root font size (as followModuleRootFontSize\n * does), and rules a library injects for the document root (`html { --mat-focus-indicator-display: block; }`), which\n * never match inside a shadow root, move onto `:host` (see rescopeDocumentRootSelectors). Safe beside a module's own\n * followModuleRootFontSize on the same root: every step is idempotent, and neither writes anything the other acts on.\n */\nexport const adaptHostedModuleStyles = (root: ShadowRoot): (() => void) => {\n return adaptShadowRootStyles(root, { documentRootRules: true });\n};\n"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  export declare const MF_SCOPE_ATTRIBUTE = "data-edyn-mf";
2
2
  export declare const MF_ROOT_SELECTOR = ":mf-root";
3
+ export { scopeDocumentQueries } from './document-queries.js';
4
+ export { type IDocumentRootRescope, rescopeDocumentRootSelectors } from './document-root-selectors.js';
5
+ export { adaptHostedModuleStyles, followModuleRootFontSize } from './hosted-module-styles.js';
3
6
  export * from './navigation.js';
4
- export { followModuleRootFontSize, MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';
7
+ export { MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';
5
8
  export interface IPrepareMfBoundaryOptions {
6
9
  scopeId: string;
7
10
  theme?: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI9G,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,KAAG,MAMnD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAEpD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,WAAW,EAAE,oBAAyB,yBAAyB,KAAG,CAAC,MAAM,IAAI,CA2CvH,CAAC","sourcesContent":["export const MF_SCOPE_ATTRIBUTE = 'data-edyn-mf';\nexport const MF_ROOT_SELECTOR = ':mf-root';\n\nexport * from './navigation.js';\nexport { followModuleRootFontSize, MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';\n\nconst SCOPE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nexport interface IPrepareMfBoundaryOptions {\n scopeId: string;\n theme?: Record<string, string>;\n}\n\nexport const validateMfScopeId = (scopeId: string): string => {\n if (!SCOPE_ID_PATTERN.test(scopeId)) {\n throw new Error(`Invalid microfrontend scope id: \"${scopeId}\".`);\n }\n\n return scopeId;\n};\n\nexport const getMfScopeSelector = (scopeId: string): string => {\n return `:where([${MF_SCOPE_ATTRIBUTE}=\"${validateMfScopeId(scopeId)}\"])`;\n};\n\nexport const prepareMfBoundary = (element: HTMLElement, { scopeId, theme = {} }: IPrepareMfBoundaryOptions): (() => void) => {\n validateMfScopeId(scopeId);\n\n for (const name of Object.keys(theme)) {\n if (!name.startsWith('--')) {\n throw new Error(`Microfrontend theme token must be a CSS custom property: \"${name}\".`);\n }\n }\n\n const previousScope = element.getAttribute(MF_SCOPE_ATTRIBUTE);\n const hadRootClass = element.classList.contains('edx-root');\n const previousTheme = new Map<string, { priority: string; value: string }>();\n\n element.setAttribute(MF_SCOPE_ATTRIBUTE, scopeId);\n element.classList.add('edx-root');\n\n for (const [name, value] of Object.entries(theme)) {\n previousTheme.set(name, {\n priority: element.style.getPropertyPriority(name),\n value: element.style.getPropertyValue(name),\n });\n element.style.setProperty(name, value);\n }\n\n return () => {\n if (previousScope === null) {\n element.removeAttribute(MF_SCOPE_ATTRIBUTE);\n } else {\n element.setAttribute(MF_SCOPE_ATTRIBUTE, previousScope);\n }\n\n if (!hadRootClass) {\n element.classList.remove('edx-root');\n }\n\n for (const [name, previous] of previousTheme) {\n if (previous.value) {\n element.style.setProperty(name, previous.value, previous.priority);\n } else {\n element.style.removeProperty(name);\n }\n }\n };\n};\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,KAAK,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACvG,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC9F,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAIpF,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,KAAG,MAMnD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAEpD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,WAAW,EAAE,oBAAyB,yBAAyB,KAAG,CAAC,MAAM,IAAI,CA2CvH,CAAC","sourcesContent":["export const MF_SCOPE_ATTRIBUTE = 'data-edyn-mf';\nexport const MF_ROOT_SELECTOR = ':mf-root';\n\nexport { scopeDocumentQueries } from './document-queries.js';\nexport { type IDocumentRootRescope, rescopeDocumentRootSelectors } from './document-root-selectors.js';\nexport { adaptHostedModuleStyles, followModuleRootFontSize } from './hosted-module-styles.js';\nexport * from './navigation.js';\nexport { MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';\n\nconst SCOPE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nexport interface IPrepareMfBoundaryOptions {\n scopeId: string;\n theme?: Record<string, string>;\n}\n\nexport const validateMfScopeId = (scopeId: string): string => {\n if (!SCOPE_ID_PATTERN.test(scopeId)) {\n throw new Error(`Invalid microfrontend scope id: \"${scopeId}\".`);\n }\n\n return scopeId;\n};\n\nexport const getMfScopeSelector = (scopeId: string): string => {\n return `:where([${MF_SCOPE_ATTRIBUTE}=\"${validateMfScopeId(scopeId)}\"])`;\n};\n\nexport const prepareMfBoundary = (element: HTMLElement, { scopeId, theme = {} }: IPrepareMfBoundaryOptions): (() => void) => {\n validateMfScopeId(scopeId);\n\n for (const name of Object.keys(theme)) {\n if (!name.startsWith('--')) {\n throw new Error(`Microfrontend theme token must be a CSS custom property: \"${name}\".`);\n }\n }\n\n const previousScope = element.getAttribute(MF_SCOPE_ATTRIBUTE);\n const hadRootClass = element.classList.contains('edx-root');\n const previousTheme = new Map<string, { priority: string; value: string }>();\n\n element.setAttribute(MF_SCOPE_ATTRIBUTE, scopeId);\n element.classList.add('edx-root');\n\n for (const [name, value] of Object.entries(theme)) {\n previousTheme.set(name, {\n priority: element.style.getPropertyPriority(name),\n value: element.style.getPropertyValue(name),\n });\n element.style.setProperty(name, value);\n }\n\n return () => {\n if (previousScope === null) {\n element.removeAttribute(MF_SCOPE_ATTRIBUTE);\n } else {\n element.setAttribute(MF_SCOPE_ATTRIBUTE, previousScope);\n }\n\n if (!hadRootClass) {\n element.classList.remove('edx-root');\n }\n\n for (const [name, previous] of previousTheme) {\n if (previous.value) {\n element.style.setProperty(name, previous.value, previous.priority);\n } else {\n element.style.removeProperty(name);\n }\n }\n };\n};\n"]}
package/dist/index.js CHANGED
@@ -1,7 +1,10 @@
1
1
  export const MF_SCOPE_ATTRIBUTE = 'data-edyn-mf';
2
2
  export const MF_ROOT_SELECTOR = ':mf-root';
3
+ export { scopeDocumentQueries } from './document-queries.js';
4
+ export { rescopeDocumentRootSelectors } from './document-root-selectors.js';
5
+ export { adaptHostedModuleStyles, followModuleRootFontSize } from './hosted-module-styles.js';
3
6
  export * from './navigation.js';
4
- export { followModuleRootFontSize, MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';
7
+ export { MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';
5
8
  const SCOPE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
6
9
  export const validateMfScopeId = (scopeId) => {
7
10
  if (!SCOPE_ID_PATTERN.test(scopeId)) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["export const MF_SCOPE_ATTRIBUTE = 'data-edyn-mf';\nexport const MF_ROOT_SELECTOR = ':mf-root';\n\nexport * from './navigation.js';\nexport { followModuleRootFontSize, MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';\n\nconst SCOPE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nexport interface IPrepareMfBoundaryOptions {\n scopeId: string;\n theme?: Record<string, string>;\n}\n\nexport const validateMfScopeId = (scopeId: string): string => {\n if (!SCOPE_ID_PATTERN.test(scopeId)) {\n throw new Error(`Invalid microfrontend scope id: \"${scopeId}\".`);\n }\n\n return scopeId;\n};\n\nexport const getMfScopeSelector = (scopeId: string): string => {\n return `:where([${MF_SCOPE_ATTRIBUTE}=\"${validateMfScopeId(scopeId)}\"])`;\n};\n\nexport const prepareMfBoundary = (element: HTMLElement, { scopeId, theme = {} }: IPrepareMfBoundaryOptions): (() => void) => {\n validateMfScopeId(scopeId);\n\n for (const name of Object.keys(theme)) {\n if (!name.startsWith('--')) {\n throw new Error(`Microfrontend theme token must be a CSS custom property: \"${name}\".`);\n }\n }\n\n const previousScope = element.getAttribute(MF_SCOPE_ATTRIBUTE);\n const hadRootClass = element.classList.contains('edx-root');\n const previousTheme = new Map<string, { priority: string; value: string }>();\n\n element.setAttribute(MF_SCOPE_ATTRIBUTE, scopeId);\n element.classList.add('edx-root');\n\n for (const [name, value] of Object.entries(theme)) {\n previousTheme.set(name, {\n priority: element.style.getPropertyPriority(name),\n value: element.style.getPropertyValue(name),\n });\n element.style.setProperty(name, value);\n }\n\n return () => {\n if (previousScope === null) {\n element.removeAttribute(MF_SCOPE_ATTRIBUTE);\n } else {\n element.setAttribute(MF_SCOPE_ATTRIBUTE, previousScope);\n }\n\n if (!hadRootClass) {\n element.classList.remove('edx-root');\n }\n\n for (const [name, previous] of previousTheme) {\n if (previous.value) {\n element.style.setProperty(name, previous.value, previous.priority);\n } else {\n element.style.removeProperty(name);\n }\n }\n };\n};\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["export const MF_SCOPE_ATTRIBUTE = 'data-edyn-mf';\nexport const MF_ROOT_SELECTOR = ':mf-root';\n\nexport { scopeDocumentQueries } from './document-queries.js';\nexport { type IDocumentRootRescope, rescopeDocumentRootSelectors } from './document-root-selectors.js';\nexport { adaptHostedModuleStyles, followModuleRootFontSize } from './hosted-module-styles.js';\nexport * from './navigation.js';\nexport { MF_ROOT_FONT_SIZE_PROPERTY, rewriteRemLengths } from './root-font-size.js';\n\nconst SCOPE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nexport interface IPrepareMfBoundaryOptions {\n scopeId: string;\n theme?: Record<string, string>;\n}\n\nexport const validateMfScopeId = (scopeId: string): string => {\n if (!SCOPE_ID_PATTERN.test(scopeId)) {\n throw new Error(`Invalid microfrontend scope id: \"${scopeId}\".`);\n }\n\n return scopeId;\n};\n\nexport const getMfScopeSelector = (scopeId: string): string => {\n return `:where([${MF_SCOPE_ATTRIBUTE}=\"${validateMfScopeId(scopeId)}\"])`;\n};\n\nexport const prepareMfBoundary = (element: HTMLElement, { scopeId, theme = {} }: IPrepareMfBoundaryOptions): (() => void) => {\n validateMfScopeId(scopeId);\n\n for (const name of Object.keys(theme)) {\n if (!name.startsWith('--')) {\n throw new Error(`Microfrontend theme token must be a CSS custom property: \"${name}\".`);\n }\n }\n\n const previousScope = element.getAttribute(MF_SCOPE_ATTRIBUTE);\n const hadRootClass = element.classList.contains('edx-root');\n const previousTheme = new Map<string, { priority: string; value: string }>();\n\n element.setAttribute(MF_SCOPE_ATTRIBUTE, scopeId);\n element.classList.add('edx-root');\n\n for (const [name, value] of Object.entries(theme)) {\n previousTheme.set(name, {\n priority: element.style.getPropertyPriority(name),\n value: element.style.getPropertyValue(name),\n });\n element.style.setProperty(name, value);\n }\n\n return () => {\n if (previousScope === null) {\n element.removeAttribute(MF_SCOPE_ATTRIBUTE);\n } else {\n element.setAttribute(MF_SCOPE_ATTRIBUTE, previousScope);\n }\n\n if (!hadRootClass) {\n element.classList.remove('edx-root');\n }\n\n for (const [name, previous] of previousTheme) {\n if (previous.value) {\n element.style.setProperty(name, previous.value, previous.priority);\n } else {\n element.style.removeProperty(name);\n }\n }\n };\n};\n"]}
package/dist/postcss.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Plugin } from 'postcss';
1
+ import type { Plugin } from 'postcss';
2
2
  export interface IScopeRemoteCssOptions {
3
3
  scopeId: string;
4
4
  }