Merge pull request #750 from sveltejs/saner-sigils

saner escaping/unescaping of `@` and `#` sigils
pull/752/head
Rich Harris 7 years ago committed by GitHub
commit 4874dc1d89

@ -355,8 +355,8 @@ export default class Block {
${properties} ${properties}
}; };
} }
`.replace(/(\\\\)?#(\w*)/g, (match, escaped, name) => { `.replace(/(#+)(\w*)/g, (match: string, sigil: string, name: string) => {
return escaped ? match.slice(2) : this.alias(name); return sigil === '#' ? this.alias(name) : sigil.slice(1) + name;
}); });
} }
} }

@ -147,7 +147,7 @@ export default function dom(
const textContent = stringify(options.dev ? const textContent = stringify(options.dev ?
`${css}\n/*# sourceMappingURL=${cssMap.toUrl()} */` : `${css}\n/*# sourceMappingURL=${cssMap.toUrl()} */` :
css); css, { onlyEscapeAtSymbol: true });
builder.addBlock(deindent` builder.addBlock(deindent`
function @add_css () { function @add_css () {
@ -281,9 +281,8 @@ export default function dom(
let result = builder let result = builder
.toString() .toString()
.replace(/(\\\\)?([@#])(\w*)/g, (match: string, escaped: string, sigil: string, name: string) => { .replace(/(@+)(\w*)/g, (match: string, sigil: string, name: string) => {
if (escaped) return match.slice(2); if (sigil !== '@') return sigil.slice(1) + name;
if (sigil !== '@') return match;
if (name in shared) { if (name in shared) {
if (options.dev && `${name}Dev` in shared) name = `${name}Dev`; if (options.dev && `${name}Dev` in shared) name = `${name}Dev`;
@ -302,13 +301,13 @@ export default function dom(
}); });
result = result =
`import { ${names.join(', ')} } from ${stringify(sharedPath)};\n\n` + `import { ${names.join(', ')} } from ${JSON.stringify(sharedPath)};\n\n` +
result; result;
} }
else if (format === 'cjs') { else if (format === 'cjs') {
const SHARED = '__shared'; const SHARED = '__shared';
let requires = `var ${SHARED} = require( ${stringify(sharedPath)} );`; let requires = `var ${SHARED} = require( ${JSON.stringify(sharedPath)} );`;
used.forEach(name => { used.forEach(name => {
const alias = generator.alias(name); const alias = generator.alias(name);
requires += `\nvar ${alias} = ${SHARED}.${name};`; requires += `\nvar ${alias} = ${SHARED}.${name};`;

@ -6,6 +6,7 @@ import preprocess from './preprocess';
import visit from './visit'; import visit from './visit';
import { removeNode, removeObjectKey } from '../../utils/removeNode'; import { removeNode, removeObjectKey } from '../../utils/removeNode';
import { Parsed, Node, CompileOptions } from '../../interfaces'; import { Parsed, Node, CompileOptions } from '../../interfaces';
import { stringify } from '../../utils/stringify';
export class SsrGenerator extends Generator { export class SsrGenerator extends Generator {
bindings: string[]; bindings: string[];
@ -93,7 +94,7 @@ export default function ssr(
var ${name} = {}; var ${name} = {};
${name}.filename = ${JSON.stringify(options.filename)}; ${name}.filename = ${stringify(options.filename)};
${name}.data = function () { ${name}.data = function () {
return ${templateProperties.data ? `@template.data()` : `{}`}; return ${templateProperties.data ? `@template.data()` : `{}`};
@ -133,8 +134,8 @@ export default function ssr(
deindent` deindent`
components.push({ components.push({
filename: ${name}.filename, filename: ${name}.filename,
css: ${JSON.stringify(css)}, css: ${stringify(css)},
map: ${JSON.stringify(cssMap)} map: ${stringify(cssMap.toString())}
}); });
`} `}
@ -169,7 +170,7 @@ export default function ssr(
var escaped = { var escaped = {
'"': '"', '"': '"',
"'": ''', "'": '&##39;',
'&': '&', '&': '&',
'<': '&lt;', '<': '&lt;',
'>': '&gt;' '>': '&gt;'
@ -178,11 +179,8 @@ export default function ssr(
function __escape ( html ) { function __escape ( html ) {
return String( html ).replace( /["'&<>]/g, match => escaped[ match ] ); return String( html ).replace( /["'&<>]/g, match => escaped[ match ] );
} }
`.replace(/(\\)?([@#])(\w*)/g, (match: string, escaped: string, sigil: string, name: string) => { `.replace(/(@+|#+)(\w*)/g, (match: string, sigil: string, name: string) => {
if (escaped) return match.slice(1); return sigil === '@' ? generator.alias(name) : sigil.slice(1) + name;
if (sigil !== '@') return match;
return generator.alias(name);
}); });
return generator.generate(result, options, { name, format }); return generator.generate(result, options, { name, format });

@ -5,6 +5,7 @@ import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import getObject from '../../../utils/getObject'; import getObject from '../../../utils/getObject';
import getTailSnippet from '../../../utils/getTailSnippet'; import getTailSnippet from '../../../utils/getTailSnippet';
import { stringify } from '../../../utils/stringify';
export default function visitComponent( export default function visitComponent(
generator: SsrGenerator, generator: SsrGenerator,
@ -41,7 +42,7 @@ export default function visitComponent(
} else if (attribute.value.length === 1) { } else if (attribute.value.length === 1) {
const chunk = attribute.value[0]; const chunk = attribute.value[0];
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
value = isNaN(chunk.data) ? JSON.stringify(chunk.data) : chunk.data; value = isNaN(chunk.data) ? stringify(chunk.data) : chunk.data;
} else { } else {
const { snippet } = block.contextualise(chunk.expression); const { snippet } = block.contextualise(chunk.expression);
value = snippet; value = snippet;

@ -1,7 +1,9 @@
export function stringify(data: string) { export function stringify(data: string, options = {}) {
return JSON.stringify(escape(data)); return JSON.stringify(escape(data, options));
} }
export function escape(data: string) { export function escape(data: string, { onlyEscapeAtSymbol = false } = {}) {
return data.replace(/([^\\@#])?([@#])/g, '$1\\$2'); return data.replace(onlyEscapeAtSymbol ? /(@+)/g : /(@+|#+)/g, (match: string) => {
return match + match[0];
});
} }

@ -0,0 +1 @@
<a href='mailto:{{address}}'>email</a>

@ -0,0 +1,3 @@
export default {
html: `<a href='mailto:hello@example.com'>email</a>`
};

@ -0,0 +1,9 @@
<Email address='hello@example.com'/>
<script>
import Email from './Email.html';
export default {
components: { Email },
};
</script>
Loading…
Cancel
Save