Skip to content

Commit

Permalink
feat: Optimized logic of scoped_css, improve compatibility and perfor…
Browse files Browse the repository at this point in the history
…mance
  • Loading branch information
bailicangdu committed Jan 18, 2022
1 parent 1a42d6f commit 0986725
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 48 deletions.
2 changes: 1 addition & 1 deletion dev/children/react16/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<!-- <script>
console.log(6666666, this)
</script> -->
<link rel="stylesheet" href="http://127.0.0.1:8080/facefont.css">
<!-- <link rel="stylesheet" href="http://127.0.0.1:8080/facefont.css"> -->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
3 changes: 3 additions & 0 deletions docs/en-us/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

- 🐞 Fixed the Error of parsing style when importing `SVG` through `background image` in CSS.

- **Update**

- 🚀 Optimized logic of scoped_css, improve compatibility and performance.

### 0.8.2

Expand Down
3 changes: 3 additions & 0 deletions docs/zh-cn/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

- 🐞 修复了在css中通过`background-image`引入`svg`时,样式隔离解析失败的问题。

- **Update**

- 🚀 优化了样式隔离的逻辑,提高兼容和性能。

### 0.8.2

Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/source/scoped_css.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ describe('source scoped_css', () => {
document.head.appendChild(dynamicStyle5)
expect(dynamicStyle5.textContent).toBe('micro-app[name=test-app11] div{/* scopecss-disable-next-line */background: url(/test.png);}micro-app[name=test-app11] span{}micro-app[name=test-app11] header{}')

// coverage branch of slash in declarations
const dynamicStyle6 = document.createElement('style')
dynamicStyle6.textContent = '.test1{color: re/d;}'
document.head.appendChild(dynamicStyle6)
expect(dynamicStyle6.textContent).toBe('micro-app[name=test-app11] .test1{color: re/d;}')

resolve(true)
}, false)
})
Expand Down
2 changes: 1 addition & 1 deletion src/source/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ export function rejectMicroAppStyle (): void {
hasRejectMicroAppStyle = true
const style = pureCreateElement('style')
globalEnv.rawSetAttribute.call(style, 'type', 'text/css')
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; position: relative; } \nmicro-app-head { display: none; }`
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`
globalEnv.rawDocument.head.appendChild(style)
}
}
79 changes: 33 additions & 46 deletions src/source/scoped_css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import microApp from '../micro_app'
// common reg
const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/
const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/
const cssUrlREG = /url\(["']?([^)"']+)["']?\)/gm

type parseErrorType = Error & { reason: string, filename?: string }
function parseError (msg: string, linkPath?: string): void {
msg = linkPath ? `${linkPath}:${msg}` : msg
msg = linkPath ? `${linkPath} ${msg}` : msg
const err = new Error(msg) as parseErrorType
err.reason = msg
if (linkPath) {
Expand Down Expand Up @@ -93,59 +92,51 @@ class CSSParser {
private styleDeclarations (): boolean | void {
if (!this.matchOpenBrace()) return parseError("Declaration missing '{'", this.linkPath)

this.matchComments()

while (this.styleDeclaration()) {
this.matchComments()
}
this.matchAllDeclarations()

if (!this.matchCloseBrace()) return parseError("Declaration missing '}'", this.linkPath)

return true
}

// match one styleDeclaration at a time
private styleDeclaration (): boolean | void {
// css property
if (!this.commonMatch(/^(\*?[-#+\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/)) return false

// match :
if (!this.commonMatch(/^:\s*/)) return parseError("property missing ':'", this.linkPath)

// match css value
// BUG Fix: background-image:url("data:image/svg+xml...
const r = this.commonMatch(/^((?:'(?:\\'|[^}])*?'|"(?:\\"|[^}])*?"|\([^\)]*?\)|[^};])+)/, true)

let cssValue = r ? r[0] : ''
private matchAllDeclarations (): void {
let cssValue = (this.commonMatch(/^(?:url\(["']?(?:[^)"'}]+)["']?\)|[^}/])*/, true) as RegExpExecArray)[0]

if (cssValue) {
if (
!this.scopecssDisableNextLine &&
(!this.scopecssDisable || this.scopecssDisableSelectors.length)
) {
cssValue = cssValue.replace(/url\(["']?([^)"']+)["']?\)/gm, (all, $1) => {
if (/^((data|blob):|#)/.test($1) || /^(https?:)?\/\//.test($1)) {
return all
}

if (
!this.scopecssDisableNextLine &&
(!this.scopecssDisable || this.scopecssDisableSelectors.length)
) {
cssValue = cssValue.replace(cssUrlREG, (all, $1) => {
if (/^((data|blob):|#)/.test($1) || /^(https?:)?\/\//.test($1)) {
return all
}
// ./a/b.png ../a/b.png a/b.png
if (/^((\.\.?\/)|[^/])/.test($1) && this.linkPath) {
this.baseURI = getLinkFileDir(this.linkPath)
}

// ./a/b.png ../a/b.png a/b.png
if (/^((\.\.?\/)|[^/])/.test($1) && this.linkPath) {
this.baseURI = getLinkFileDir(this.linkPath)
}
return `url("${CompletionPath($1, this.baseURI)}")`
})
}

return `url("${CompletionPath($1, this.baseURI)}")`
})
this.result += cssValue
}

// reset scopecssDisableNextLine
this.scopecssDisableNextLine = false

this.result += cssValue

this.matchLeadingSpaces()
if (!this.cssText || this.cssText.charAt(0) === '}') return

this.commonMatch(/^[;\s]*/)
// extract comments in declarations
if (this.cssText.charAt(0) === '/' && this.cssText.charAt(1) === '*') {
this.matchComments()
} else {
this.commonMatch(/\/+/)
}

return true
return this.matchAllDeclarations()
}

private formatSelector (): boolean | Array<string> {
Expand Down Expand Up @@ -312,11 +303,7 @@ class CSSParser {
private commonHandlerForAtRuleWithSelfRule (name: string): boolean | void {
if (!this.matchOpenBrace()) return parseError(`@${name} missing '{'`, this.linkPath)

this.matchComments()

while (this.styleDeclaration()) {
this.matchComments()
}
this.matchAllDeclarations()

if (!this.matchCloseBrace()) return parseError(`@${name} missing '}'`, this.linkPath)

Expand Down Expand Up @@ -376,7 +363,7 @@ class CSSParser {
return true
}

private commonMatch (reg: RegExp, skip = false): RegExpExecArray | void {
private commonMatch (reg: RegExp, skip = false): RegExpExecArray | null | void {
const matchArray = reg.exec(this.cssText)
if (!matchArray) return
const matchStr = matchArray[0]
Expand Down Expand Up @@ -422,7 +409,7 @@ function commonAction (
parser.reset()
} catch (e) {
parser.reset()
logError('CSSParser: An error occurred while parsing CSS', appName, e)
logError('An error occurred while parsing CSS:\n', appName, e)
}

if (result) styleElement.textContent = result
Expand Down

0 comments on commit 0986725

Please sign in to comment.