@micro-zoe/micro-app 0.7.1 → 0.8.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/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.7.1';
1
+ const version = '0.8.0';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -252,10 +252,6 @@ function getCurrentAppName() {
252
252
  function removeDomScope() {
253
253
  setCurrentAppName(null);
254
254
  }
255
- // is safari browser
256
- function isSafari() {
257
- return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
258
- }
259
255
  /**
260
256
  * Create pure elements
261
257
  */
@@ -304,6 +300,12 @@ function isUniqueElement(key) {
304
300
  function getRootContainer(target) {
305
301
  return (isShadowRoot(target) ? target.host : target);
306
302
  }
303
+ /**
304
+ * trim start & end
305
+ */
306
+ function trim(str) {
307
+ return str ? str.replace(/^\s+|\s+$/g, '') : '';
308
+ }
307
309
 
308
310
  var ObservedAttrName;
309
311
  (function (ObservedAttrName) {
@@ -357,221 +359,355 @@ function fetchSource(url, appName = null, options = {}) {
357
359
  });
358
360
  }
359
361
 
360
- const globalEnv = {};
361
- /**
362
- * Note loop nesting
363
- * Only prototype or unique values can be put here
364
- */
365
- function initGlobalEnv() {
366
- if (isBrowser) {
367
- /**
368
- * save patch raw methods
369
- * pay attention to this binding
370
- */
371
- const rawSetAttribute = Element.prototype.setAttribute;
372
- const rawAppendChild = Element.prototype.appendChild;
373
- const rawInsertBefore = Element.prototype.insertBefore;
374
- const rawReplaceChild = Element.prototype.replaceChild;
375
- const rawRemoveChild = Element.prototype.removeChild;
376
- const rawAppend = Element.prototype.append;
377
- const rawPrepend = Element.prototype.prepend;
378
- const rawCloneNode = Element.prototype.cloneNode;
379
- const rawCreateElement = Document.prototype.createElement;
380
- const rawCreateElementNS = Document.prototype.createElementNS;
381
- const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
382
- const rawQuerySelector = Document.prototype.querySelector;
383
- const rawQuerySelectorAll = Document.prototype.querySelectorAll;
384
- const rawGetElementById = Document.prototype.getElementById;
385
- const rawGetElementsByClassName = Document.prototype.getElementsByClassName;
386
- const rawGetElementsByTagName = Document.prototype.getElementsByTagName;
387
- const rawGetElementsByName = Document.prototype.getElementsByName;
388
- const ImageProxy = new Proxy(Image, {
389
- construct(Target, args) {
390
- const elementImage = new Target(...args);
391
- elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
392
- return elementImage;
393
- },
394
- });
395
- const rawWindow = Function('return window')();
396
- const rawDocument = Function('return document')();
397
- const supportModuleScript = isSupportModuleScript();
398
- const templateStyle = rawDocument.body.querySelector('#micro-app-template-style');
399
- /**
400
- * save effect raw methods
401
- * pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
402
- */
403
- const rawWindowAddEventListener = rawWindow.addEventListener;
404
- const rawWindowRemoveEventListener = rawWindow.removeEventListener;
405
- const rawSetInterval = rawWindow.setInterval;
406
- const rawSetTimeout = rawWindow.setTimeout;
407
- const rawClearInterval = rawWindow.clearInterval;
408
- const rawClearTimeout = rawWindow.clearTimeout;
409
- const rawDocumentAddEventListener = rawDocument.addEventListener;
410
- const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
411
- // mark current application as base application
412
- window.__MICRO_APP_BASE_APPLICATION__ = true;
413
- Object.assign(globalEnv, {
414
- // source/patch
415
- rawSetAttribute,
416
- rawAppendChild,
417
- rawInsertBefore,
418
- rawReplaceChild,
419
- rawRemoveChild,
420
- rawAppend,
421
- rawPrepend,
422
- rawCloneNode,
423
- rawCreateElement,
424
- rawCreateElementNS,
425
- rawCreateDocumentFragment,
426
- rawQuerySelector,
427
- rawQuerySelectorAll,
428
- rawGetElementById,
429
- rawGetElementsByClassName,
430
- rawGetElementsByTagName,
431
- rawGetElementsByName,
432
- ImageProxy,
433
- // common global vars
434
- rawWindow,
435
- rawDocument,
436
- supportModuleScript,
437
- templateStyle,
438
- // sandbox/effect
439
- rawWindowAddEventListener,
440
- rawWindowRemoveEventListener,
441
- rawSetInterval,
442
- rawSetTimeout,
443
- rawClearInterval,
444
- rawClearTimeout,
445
- rawDocumentAddEventListener,
446
- rawDocumentRemoveEventListener,
447
- });
448
- }
449
- }
450
-
451
- /**
452
- * Bind css scope
453
- * Special case:
454
- * 1. html-abc | abc-html
455
- * 2. html body.abc
456
- * 3. abchtml | htmlabc | abcbody | bodyabc
457
- * 4. html + body | html > body | html.body | html[name=xx] | body[name=xx]
458
- * 5. xxx, html xxx, body xxx
459
- *
460
- * TODO: BUG
461
- .test-b {
462
- border: 1px solid var(--color-a);
463
- border-bottom-color: var(--color-b);
464
- }
465
- */
466
- function scopedStyleRule(rule, prefix) {
467
- const { selectorText, cssText } = rule;
468
- if (/^((html[\s>~,]+body)|(html|body|:root))$/.test(selectorText)) {
469
- return cssText.replace(/^((html[\s>~,]+body)|(html|body|:root))/, prefix);
470
- }
471
- else if (selectorText === '*') {
472
- return cssText.replace('*', `${prefix} *`);
473
- }
474
- const builtInRootSelectorRE = /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/;
475
- return cssText.replace(/^[\s\S]+{/, (selectors) => {
476
- return selectors.replace(/(^|,)([^,]+)/g, (all, $1, $2) => {
477
- if (builtInRootSelectorRE.test($2)) {
478
- // body[name=xx]|body.xx|body#xx etc. do not need to handle
479
- return all.replace(builtInRootSelectorRE, prefix);
480
- }
481
- return `${$1} ${prefix} ${$2.replace(/^\s*/, '')}`;
482
- });
483
- });
362
+ // common reg
363
+ const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
364
+ const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
365
+ const cssUrlREG = /url\(["']?([^)"']+)["']?\)/gm;
366
+ function parseError(msg, linkpath) {
367
+ msg = linkpath ? `${linkpath}:${msg}` : msg;
368
+ const err = new Error(msg);
369
+ err.reason = msg;
370
+ if (linkpath) {
371
+ err.filename = linkpath;
372
+ }
373
+ throw err;
484
374
  }
485
375
  /**
486
- * Complete static resource address
487
- * @param cssText css content
488
- * @param baseURI domain name
489
- * @param textContent origin content
490
- * @param linkpath link resource address, if it is the style converted from link, it will have linkpath
376
+ * Reference resources https://github.com/reworkcss/css
377
+ * CSSParser mainly deals with 3 scenes: styleRule, @, and comment
378
+ * And scopecss deals with 2 scenes: selector & url
379
+ * It can also disable scopecss with inline comments
491
380
  */
492
- function scopedHost(cssText, baseURI, textContent, linkpath) {
493
- return cssText.replace(/url\(["']?([^)"']+)["']?\)/gm, (all, $1) => {
494
- if (/^((data|blob):|#)/.test($1)) {
495
- return all;
496
- }
497
- else if (/^(https?:)?\/\//.test($1)) {
498
- if (isSafari()) {
499
- const purePath = $1.replace(/^https?:/, '');
500
- if (textContent.indexOf(purePath) === -1) {
501
- $1 = $1.replace(window.location.origin, '');
502
- }
503
- else {
381
+ class CSSParser {
382
+ constructor() {
383
+ this.cssText = ''; // css content
384
+ this.prefix = ''; // prefix as micro-app[name=xxx]
385
+ this.baseURI = ''; // domain name
386
+ this.linkpath = ''; // link resource address, if it is the style converted from link, it will have linkpath
387
+ this.result = ''; // parsed cssText
388
+ this.scopecssDisable = false; // use block comments /* scopecss-disable */ to disable scopecss in your file, and use /* scopecss-enable */ to enable scopecss
389
+ this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
390
+ this.scopecssDisableNextLine = false; // use block comments /* scopecss-disable-next-line */ to disable scopecss on a specific line
391
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
392
+ this.mediaRule = this.createMatcherForAtRuleWithChildRule(/^@media *([^{]+)/, 'media');
393
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSSupportsRule
394
+ this.supportsRule = this.createMatcherForAtRuleWithChildRule(/^@supports *([^{]+)/, 'supports');
395
+ this.documentRule = this.createMatcherForAtRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, 'document');
396
+ this.hostRule = this.createMatcherForAtRuleWithChildRule(/^@host\s*/, 'host');
397
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule
398
+ this.importRule = this.createMatcherForNoneBraceAtRule('import');
399
+ // Removed in most browsers
400
+ this.charsetRule = this.createMatcherForNoneBraceAtRule('charset');
401
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSNamespaceRule
402
+ this.namespaceRule = this.createMatcherForNoneBraceAtRule('namespace');
403
+ }
404
+ exec(cssText, prefix, baseURI, linkpath) {
405
+ this.cssText = cssText;
406
+ this.prefix = prefix;
407
+ this.baseURI = baseURI;
408
+ this.linkpath = linkpath || '';
409
+ this.matchRules();
410
+ return this.result;
411
+ }
412
+ reset() {
413
+ this.cssText = this.prefix = this.baseURI = this.linkpath = this.result = '';
414
+ this.scopecssDisable = this.scopecssDisableNextLine = false;
415
+ this.scopecssDisableSelectors = [];
416
+ }
417
+ // core action for match rules
418
+ matchRules() {
419
+ this.matchLeadingSpaces();
420
+ this.matchComments();
421
+ while (this.cssText.length &&
422
+ this.cssText.charAt(0) !== '}' &&
423
+ (this.matchAtRule() || this.matchStyleRule())) {
424
+ this.matchComments();
425
+ }
426
+ }
427
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule
428
+ matchStyleRule() {
429
+ const selectorList = this.formatSelector();
430
+ // reset scopecssDisableNextLine
431
+ this.scopecssDisableNextLine = false;
432
+ if (!selectorList)
433
+ return parseError('selector missing', this.linkpath);
434
+ this.result += selectorList.join(', ');
435
+ this.matchComments();
436
+ this.styleDeclarations();
437
+ this.matchLeadingSpaces();
438
+ return true;
439
+ }
440
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
441
+ styleDeclarations() {
442
+ if (!this.matchOpenBrace())
443
+ return parseError("Declaration missing '{'", this.linkpath);
444
+ this.matchComments();
445
+ while (this.styleDeclaration()) {
446
+ this.matchComments();
447
+ }
448
+ if (!this.matchCloseBrace())
449
+ return parseError("Declaration missing '}'", this.linkpath);
450
+ return true;
451
+ }
452
+ // match one styleDeclaration at a time
453
+ styleDeclaration() {
454
+ // css property
455
+ if (!this.commonMatch(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/))
456
+ return false;
457
+ // match :
458
+ if (!this.commonMatch(/^:\s*/))
459
+ return parseError("property missing ':'", this.linkpath);
460
+ // match css value
461
+ const r = this.commonMatch(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/, true);
462
+ let cssValue = r ? r[0] : '';
463
+ if (!this.scopecssDisableNextLine &&
464
+ (!this.scopecssDisable || this.scopecssDisableSelectors.length)) {
465
+ cssValue = cssValue.replace(cssUrlREG, (all, $1) => {
466
+ if (/^((data|blob):|#)/.test($1) || /^(https?:)?\/\//.test($1)) {
504
467
  return all;
505
468
  }
469
+ // ./a/b.png ../a/b.png a/b.png
470
+ if (/^((\.\.?\/)|[^/])/.test($1) && this.linkpath) {
471
+ this.baseURI = getLinkFileDir(this.linkpath);
472
+ }
473
+ return `url("${CompletionPath($1, this.baseURI)}")`;
474
+ });
475
+ }
476
+ // reset scopecssDisableNextLine
477
+ this.scopecssDisableNextLine = false;
478
+ this.result += cssValue;
479
+ this.matchLeadingSpaces();
480
+ this.commonMatch(/^[;\s]*/);
481
+ return true;
482
+ }
483
+ formatSelector() {
484
+ const m = this.commonMatch(/^([^{]+)/, true);
485
+ if (!m)
486
+ return false;
487
+ return trim(m[0])
488
+ .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
489
+ .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (r) => {
490
+ return r.replace(/,/g, '\u200C');
491
+ })
492
+ .split(/\s*(?![^(]*\)),\s*/)
493
+ .map((s) => {
494
+ const selectorText = s.replace(/\u200C/g, ',');
495
+ if (this.scopecssDisableNextLine) {
496
+ return selectorText;
497
+ }
498
+ else if (this.scopecssDisable) {
499
+ if (!this.scopecssDisableSelectors.length ||
500
+ this.scopecssDisableSelectors.includes(selectorText)) {
501
+ return selectorText;
502
+ }
503
+ }
504
+ if (selectorText === '*') {
505
+ return this.prefix + ' *';
506
+ }
507
+ else if (bodySelectorREG.test(selectorText)) {
508
+ return selectorText.replace(bodySelectorREG, this.prefix + ' micro-app-body');
509
+ }
510
+ else if (rootSelectorREG.test(selectorText)) { // ignore root selector
511
+ return selectorText;
512
+ }
513
+ return this.prefix + ' ' + selectorText;
514
+ });
515
+ }
516
+ matchAtRule() {
517
+ if (this.cssText[0] !== '@')
518
+ return false;
519
+ // reset scopecssDisableNextLine
520
+ this.scopecssDisableNextLine = false;
521
+ return this.keyframesRule() ||
522
+ this.mediaRule() ||
523
+ this.custommediaRule() ||
524
+ this.supportsRule() ||
525
+ this.importRule() ||
526
+ this.charsetRule() ||
527
+ this.namespaceRule() ||
528
+ this.documentRule() ||
529
+ this.pageRule() ||
530
+ this.hostRule() ||
531
+ this.fontfaceRule();
532
+ }
533
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
534
+ keyframesRule() {
535
+ if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
536
+ return false;
537
+ if (!this.commonMatch(/^([-\w]+)\s*/))
538
+ return parseError('@keyframes missing name', this.linkpath);
539
+ this.matchComments();
540
+ if (!this.matchOpenBrace())
541
+ return parseError("@keyframes missing '{'", this.linkpath);
542
+ this.matchComments();
543
+ while (this.keyframeRule()) {
544
+ this.matchComments();
545
+ }
546
+ if (!this.matchCloseBrace())
547
+ return parseError("@keyframes missing '}'", this.linkpath);
548
+ this.matchLeadingSpaces();
549
+ return true;
550
+ }
551
+ keyframeRule() {
552
+ let r;
553
+ const valList = [];
554
+ while (r = this.commonMatch(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/)) {
555
+ valList.push(r[1]);
556
+ this.commonMatch(/^,\s*/);
557
+ }
558
+ if (!valList.length)
559
+ return false;
560
+ this.styleDeclarations();
561
+ this.matchLeadingSpaces();
562
+ return true;
563
+ }
564
+ // https://github.com/postcss/postcss-custom-media
565
+ custommediaRule() {
566
+ if (!this.commonMatch(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/))
567
+ return false;
568
+ this.matchLeadingSpaces();
569
+ return true;
570
+ }
571
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSPageRule
572
+ pageRule() {
573
+ if (!this.commonMatch(/^@page */))
574
+ return false;
575
+ this.formatSelector();
576
+ // reset scopecssDisableNextLine
577
+ this.scopecssDisableNextLine = false;
578
+ return this.commonHandlerForAtRuleWithSelfRule('page');
579
+ }
580
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSFontFaceRule
581
+ fontfaceRule() {
582
+ if (!this.commonMatch(/^@font-face\s*/))
583
+ return false;
584
+ return this.commonHandlerForAtRuleWithSelfRule('font-face');
585
+ }
586
+ // common matcher for @media, @supports, @document, @host
587
+ createMatcherForAtRuleWithChildRule(reg, name) {
588
+ return () => {
589
+ if (!this.commonMatch(reg))
590
+ return false;
591
+ if (!this.matchOpenBrace())
592
+ return parseError(`@${name} missing '{'`, this.linkpath);
593
+ this.matchComments();
594
+ this.matchRules();
595
+ if (!this.matchCloseBrace())
596
+ return parseError(`@${name} missing '}'`, this.linkpath);
597
+ this.matchLeadingSpaces();
598
+ return true;
599
+ };
600
+ }
601
+ // common matcher for @import, @charset, @namespace
602
+ createMatcherForNoneBraceAtRule(name) {
603
+ const reg = new RegExp('^@' + name + '\\s*([^;]+);');
604
+ return () => {
605
+ if (!this.commonMatch(reg))
606
+ return false;
607
+ this.matchLeadingSpaces();
608
+ return false;
609
+ };
610
+ }
611
+ // common handler for @font-face, @page
612
+ commonHandlerForAtRuleWithSelfRule(name) {
613
+ if (!this.matchOpenBrace())
614
+ return parseError(`@${name} missing '{'`, this.linkpath);
615
+ this.matchComments();
616
+ while (this.styleDeclaration()) {
617
+ this.matchComments();
618
+ }
619
+ if (!this.matchCloseBrace())
620
+ return parseError(`@${name} missing '}'`, this.linkpath);
621
+ this.matchLeadingSpaces();
622
+ return true;
623
+ }
624
+ // match and slice comments
625
+ matchComments() {
626
+ while (this.matchComment())
627
+ ;
628
+ }
629
+ // css comment
630
+ matchComment() {
631
+ if (this.cssText.charAt(0) !== '/' || this.cssText.charAt(1) !== '*')
632
+ return false;
633
+ // reset scopecssDisableNextLine
634
+ this.scopecssDisableNextLine = false;
635
+ let i = 2;
636
+ while (this.cssText.charAt(i) !== '' && (this.cssText.charAt(i) !== '*' || this.cssText.charAt(i + 1) !== '/'))
637
+ ++i;
638
+ i += 2;
639
+ if (this.cssText.charAt(i - 1) === '') {
640
+ return parseError('End of comment missing', this.linkpath);
641
+ }
642
+ // get comment content
643
+ let commentText = this.cssText.slice(2, i - 2);
644
+ this.result += `/*${commentText}*/`;
645
+ commentText = trim(commentText.replace(/^\s*!/, ''));
646
+ // set ignore config
647
+ if (commentText === 'scopecss-disable-next-line') {
648
+ this.scopecssDisableNextLine = true;
649
+ }
650
+ else if (/^scopecss-disable/.test(commentText)) {
651
+ if (commentText === 'scopecss-disable') {
652
+ this.scopecssDisable = true;
506
653
  }
507
654
  else {
508
- return all;
655
+ this.scopecssDisable = true;
656
+ const ignoreRules = commentText.replace('scopecss-disable', '').split(',');
657
+ ignoreRules.forEach((rule) => {
658
+ this.scopecssDisableSelectors.push(trim(rule));
659
+ });
509
660
  }
510
661
  }
511
- // ./a/b.png ../a/b.png a/b.png
512
- if (/^((\.\.?\/)|[^/])/.test($1) && linkpath) {
513
- baseURI = getLinkFileDir(linkpath);
662
+ else if (commentText === 'scopecss-enable') {
663
+ this.scopecssDisable = false;
664
+ this.scopecssDisableSelectors = [];
514
665
  }
515
- return `url("${CompletionPath($1, baseURI)}")`;
516
- });
517
- }
518
- // handle media and supports
519
- function scopedPackRule(rule, prefix, packName) {
520
- const result = scopedRule(Array.from(rule.cssRules), prefix);
521
- return `@${packName} ${rule.conditionText} {${result}}`;
522
- }
523
- /**
524
- * Process each cssrule
525
- * @param rules cssRule
526
- * @param prefix prefix as micro-app[name=xxx]
527
- */
528
- function scopedRule(rules, prefix) {
529
- let result = '';
530
- for (const rule of rules) {
531
- // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSRule
532
- switch (rule.constructor.name) {
533
- case 'CSSStyleRule':
534
- result += scopedStyleRule(rule, prefix);
535
- break;
536
- case 'CSSMediaRule':
537
- result += scopedPackRule(rule, prefix, 'media');
538
- break;
539
- case 'CSSSupportsRule':
540
- result += scopedPackRule(rule, prefix, 'supports');
541
- break;
542
- default:
543
- result += rule.cssText;
544
- break;
545
- }
546
- }
547
- return result.replace(/^\s+/, '');
666
+ this.cssText = this.cssText.slice(i);
667
+ this.matchLeadingSpaces();
668
+ return true;
669
+ }
670
+ commonMatch(reg, skip = false) {
671
+ const matchArray = reg.exec(this.cssText);
672
+ if (!matchArray)
673
+ return;
674
+ const matchStr = matchArray[0];
675
+ this.cssText = this.cssText.slice(matchStr.length);
676
+ if (!skip)
677
+ this.result += matchStr;
678
+ return matchArray;
679
+ }
680
+ matchOpenBrace() {
681
+ return this.commonMatch(/^{\s*/);
682
+ }
683
+ matchCloseBrace() {
684
+ return this.commonMatch(/^}/);
685
+ }
686
+ // match and slice the leading spaces
687
+ matchLeadingSpaces() {
688
+ this.commonMatch(/^\s*/);
689
+ }
548
690
  }
549
691
  /**
550
692
  * common method of bind CSS
551
693
  */
552
- function commonAction(templateStyle, styleElement, originContent, prefix, baseURI, linkpath) {
553
- var _a, _b;
694
+ function commonAction(styleElement, appName, prefix, baseURI, linkpath) {
554
695
  if (!styleElement.__MICRO_APP_HAS_SCOPED__) {
555
- const rules = Array.from((_b = (_a = templateStyle.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) !== null && _b !== void 0 ? _b : []);
556
- let result = scopedHost(scopedRule(rules, prefix), baseURI, originContent, linkpath);
557
- /**
558
- * Solve the problem of missing content quotes in some Safari browsers
559
- * docs: https://developer.mozilla.org/zh-CN/docs/Web/CSS/content
560
- * If there are still problems, it is recommended to use the attr()
561
- */
562
- if (isSafari()) {
563
- result = result.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => {
564
- if ($2 === 'none' ||
565
- /^(url\()|(counter\()|(attr\()|(open-quote)|(close-quote)/.test($2)) {
566
- return all;
567
- }
568
- return `${$1}"${$2}"`;
569
- });
570
- }
571
- styleElement.textContent = result;
572
696
  styleElement.__MICRO_APP_HAS_SCOPED__ = true;
697
+ let result = null;
698
+ try {
699
+ result = parser.exec(styleElement.textContent, prefix, baseURI, linkpath);
700
+ parser.reset();
701
+ }
702
+ catch (e) {
703
+ parser.reset();
704
+ logError('CSSParser: An error occurred while parsing CSS', appName, e);
705
+ }
706
+ if (result)
707
+ styleElement.textContent = result;
573
708
  }
574
709
  }
710
+ let parser;
575
711
  /**
576
712
  * scopedCSS
577
713
  * @param styleElement target style element
@@ -580,27 +716,18 @@ function commonAction(templateStyle, styleElement, originContent, prefix, baseUR
580
716
  function scopedCSS(styleElement, app) {
581
717
  if (app.scopecss) {
582
718
  const prefix = `${microApp.tagName}[name=${app.name}]`;
583
- let templateStyle = globalEnv.templateStyle;
584
- if (!templateStyle) {
585
- globalEnv.templateStyle = templateStyle = pureCreateElement('style');
586
- templateStyle.setAttribute('id', 'micro-app-template-style');
587
- globalEnv.rawDocument.body.appendChild(templateStyle);
588
- templateStyle.sheet.disabled = true;
589
- }
719
+ if (!parser)
720
+ parser = new CSSParser();
590
721
  if (styleElement.textContent) {
591
- templateStyle.textContent = styleElement.textContent;
592
- commonAction(templateStyle, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
593
- templateStyle.textContent = '';
722
+ commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
594
723
  }
595
724
  else {
596
725
  const observer = new MutationObserver(function () {
597
- var _a, _b;
598
726
  observer.disconnect();
599
- // styled-component will not be processed temporarily
600
- if ((!styleElement.textContent && ((_b = (_a = styleElement.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) === null || _b === void 0 ? void 0 : _b.length)) ||
601
- styleElement.hasAttribute('data-styled'))
602
- return;
603
- commonAction(styleElement, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
727
+ // styled-component will be ignore
728
+ if (styleElement.textContent && !styleElement.hasAttribute('data-styled')) {
729
+ commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
730
+ }
604
731
  });
605
732
  observer.observe(styleElement, { childList: true });
606
733
  }
@@ -785,6 +912,95 @@ function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
785
912
  });
786
913
  }
787
914
 
915
+ const globalEnv = {};
916
+ /**
917
+ * Note loop nesting
918
+ * Only prototype or unique values can be put here
919
+ */
920
+ function initGlobalEnv() {
921
+ if (isBrowser) {
922
+ /**
923
+ * save patch raw methods
924
+ * pay attention to this binding
925
+ */
926
+ const rawSetAttribute = Element.prototype.setAttribute;
927
+ const rawAppendChild = Element.prototype.appendChild;
928
+ const rawInsertBefore = Element.prototype.insertBefore;
929
+ const rawReplaceChild = Element.prototype.replaceChild;
930
+ const rawRemoveChild = Element.prototype.removeChild;
931
+ const rawAppend = Element.prototype.append;
932
+ const rawPrepend = Element.prototype.prepend;
933
+ const rawCloneNode = Element.prototype.cloneNode;
934
+ const rawCreateElement = Document.prototype.createElement;
935
+ const rawCreateElementNS = Document.prototype.createElementNS;
936
+ const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
937
+ const rawQuerySelector = Document.prototype.querySelector;
938
+ const rawQuerySelectorAll = Document.prototype.querySelectorAll;
939
+ const rawGetElementById = Document.prototype.getElementById;
940
+ const rawGetElementsByClassName = Document.prototype.getElementsByClassName;
941
+ const rawGetElementsByTagName = Document.prototype.getElementsByTagName;
942
+ const rawGetElementsByName = Document.prototype.getElementsByName;
943
+ const ImageProxy = new Proxy(Image, {
944
+ construct(Target, args) {
945
+ const elementImage = new Target(...args);
946
+ elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
947
+ return elementImage;
948
+ },
949
+ });
950
+ const rawWindow = Function('return window')();
951
+ const rawDocument = Function('return document')();
952
+ const supportModuleScript = isSupportModuleScript();
953
+ /**
954
+ * save effect raw methods
955
+ * pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
956
+ */
957
+ const rawWindowAddEventListener = rawWindow.addEventListener;
958
+ const rawWindowRemoveEventListener = rawWindow.removeEventListener;
959
+ const rawSetInterval = rawWindow.setInterval;
960
+ const rawSetTimeout = rawWindow.setTimeout;
961
+ const rawClearInterval = rawWindow.clearInterval;
962
+ const rawClearTimeout = rawWindow.clearTimeout;
963
+ const rawDocumentAddEventListener = rawDocument.addEventListener;
964
+ const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
965
+ // mark current application as base application
966
+ window.__MICRO_APP_BASE_APPLICATION__ = true;
967
+ Object.assign(globalEnv, {
968
+ // source/patch
969
+ rawSetAttribute,
970
+ rawAppendChild,
971
+ rawInsertBefore,
972
+ rawReplaceChild,
973
+ rawRemoveChild,
974
+ rawAppend,
975
+ rawPrepend,
976
+ rawCloneNode,
977
+ rawCreateElement,
978
+ rawCreateElementNS,
979
+ rawCreateDocumentFragment,
980
+ rawQuerySelector,
981
+ rawQuerySelectorAll,
982
+ rawGetElementById,
983
+ rawGetElementsByClassName,
984
+ rawGetElementsByTagName,
985
+ rawGetElementsByName,
986
+ ImageProxy,
987
+ // common global vars
988
+ rawWindow,
989
+ rawDocument,
990
+ supportModuleScript,
991
+ // sandbox/effect
992
+ rawWindowAddEventListener,
993
+ rawWindowRemoveEventListener,
994
+ rawSetInterval,
995
+ rawSetTimeout,
996
+ rawClearInterval,
997
+ rawClearTimeout,
998
+ rawDocumentAddEventListener,
999
+ rawDocumentRemoveEventListener,
1000
+ });
1001
+ }
1002
+ }
1003
+
788
1004
  // Global scripts, reuse across apps
789
1005
  const globalScripts = new Map();
790
1006
  /**