Skip to content

Commit

Permalink
fix: script 内容跳过转义,见 #73
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed Nov 20, 2020
1 parent 57015b1 commit 3ea06a4
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 14 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ writeFileSync('ssr.php', targetCode)
npm i san-ssr san-ssr-target-php
```

## 已知问题

- script 元素内容不可在组件间传递。即 script 内不可引用组件,也不可包含 slot。

## 贡献指南

### 开发起步
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"jest": "^26.2.2",
"mustache": "^4.0.1",
"san": "^3.10.0",
"san-html-cases": "^3.10.2",
"san-html-cases": "^3.10.4",
"san-ssr-target-fake-cmd": "^1.0.0",
"san-ssr-target-fake-esm": "^1.0.0",
"semantic-release": "^17.1.1",
Expand Down
25 changes: 15 additions & 10 deletions src/target-js/compilers/anode-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expr } from './expr-compiler'
import assert from 'assert'
import { camelCase } from 'lodash'
import { JSEmitter } from '../js-emitter'
import { ANode, AIfNode, AForNode, ASlotNode, ATemplateNode, AFragmentNode, ATextNode } from 'san'
Expand All @@ -16,6 +17,7 @@ import * as TypeGuards from '../../utils/type-guards'
export class ANodeCompiler<T extends 'none' | 'typed'> {
private ssrIndex = 0
private elementCompiler: ElementCompiler
private inScript = false

/**
* @param componentInfo 要被编译的节点所在组件的信息
Expand Down Expand Up @@ -58,10 +60,11 @@ export class ANodeCompiler<T extends 'none' | 'typed'> {

private compileText (aNode: ATextNode) {
const { emitter } = this
const shouldEmitComment = TypeGuards.isExprTextNode(aNode.textExpr) && aNode.textExpr.original && !this.ssrOnly
const shouldEmitComment = TypeGuards.isExprTextNode(aNode.textExpr) && aNode.textExpr.original && !this.ssrOnly && !this.inScript
const outputType = this.inScript ? 'rawhtml' : 'html'

if (shouldEmitComment) emitter.writeHTMLLiteral('<!--s-text-->')
emitter.writeHTMLExpression(expr(aNode.textExpr, 'html'))
emitter.writeHTMLExpression(expr(aNode.textExpr, outputType))
if (shouldEmitComment) emitter.writeHTMLLiteral('<!--/s-text-->')
}

Expand All @@ -72,11 +75,11 @@ export class ANodeCompiler<T extends 'none' | 'typed'> {
}

compileFragment (aNode: AFragmentNode) {
if (TypeGuards.isATextNode(aNode.children[0]) && !this.ssrOnly) {
if (TypeGuards.isATextNode(aNode.children[0]) && !this.ssrOnly && !this.inScript) {
this.emitter.writeHTMLLiteral('<!--s-frag-->')
}
this.elementCompiler.inner(aNode)
if (TypeGuards.isATextNode(aNode.children[aNode.children.length - 1]) && !this.ssrOnly) {
if (TypeGuards.isATextNode(aNode.children[aNode.children.length - 1]) && !this.ssrOnly && !this.inScript) {
this.emitter.writeHTMLLiteral('<!--/s-frag-->')
}
}
Expand Down Expand Up @@ -143,6 +146,7 @@ export class ANodeCompiler<T extends 'none' | 'typed'> {

private compileSlot (aNode: ASlotNode) {
const { emitter } = this
assert(!this.inScript, '<slot> is not allowed inside <script>')

emitter.nextLine('(')
emitter.writeAnonymousFunction([], () => {
Expand Down Expand Up @@ -170,21 +174,22 @@ export class ANodeCompiler<T extends 'none' | 'typed'> {

private compileElement (aNode: ANode, isRootElement: boolean) {
this.elementCompiler.tagStart(aNode)
if (isRootElement && !this.ssrOnly) this.outputData()
if (aNode.tagName === 'script') this.inScript = true
if (isRootElement && !this.ssrOnly && !this.inScript) {
this.emitter.writeIf('!noDataOutput', () => this.emitter.writeDataComment())
}
this.elementCompiler.inner(aNode)
this.inScript = false
this.elementCompiler.tagEnd(aNode)
}

private outputData () {
this.emitter.writeIf('!noDataOutput', () => this.emitter.writeDataComment())
}

private compileComponent (aNode: ANode, ref: string, isRootElement: boolean) {
const { emitter } = this

const defaultSourceSlots: ANode[] = []
const sourceSlotCodes = new Map()

assert(!this.inScript, 'component reference is not allowed inside <script>')

for (const child of aNode.children!) { // nodes without children (like pATextNode) has been taken over by other methods
const slotBind = !child.textExpr && getANodePropByName(child, 'slot')
if (slotBind) {
Expand Down
10 changes: 10 additions & 0 deletions test/unit/target-js/compilers/expr-compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ describe('target-js/compilers/expr-compiler', () => {
const exp = e.children[0].textExpr
expect(expr(exp)).toContain('ctx.instance.filters["bar"].call(ctx.instance, "foo", "coo")')
})
it('should escape text value by default', () => {
const e = parseTemplate('{{"<"}}<')
const exp = e.children[0].textExpr
expect(expr(exp, 'html')).toEqual('_.output("<", true) + "&lt;"')
})
it('should not escape text value if raw specified', () => {
const e = parseTemplate('{{"\'foo\'" | raw}}')
const exp = e.children[0].textExpr
expect(expr(exp, 'html')).toEqual('_.output("\'foo\'", false)')
})
})
describe('.text()', () => {
it('should compile to empty string for empty text', () => {
Expand Down

0 comments on commit 3ea06a4

Please sign in to comment.