mirror of https://github.com/sveltejs/svelte
parent
a023f2e049
commit
349aa0bbac
@ -1,17 +0,0 @@
|
|||||||
import { Node } from '../../../../interfaces';
|
|
||||||
import Component from '../../../Component';
|
|
||||||
|
|
||||||
export default function checkForAccessors(
|
|
||||||
component: Component,
|
|
||||||
properties: Node[],
|
|
||||||
label: string
|
|
||||||
) {
|
|
||||||
properties.forEach(prop => {
|
|
||||||
if (prop.kind !== 'init') {
|
|
||||||
component.error(prop, {
|
|
||||||
code: `illegal-accessor`,
|
|
||||||
message: `${label} cannot use getters and setters`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { Node } from '../../../../interfaces';
|
|
||||||
import Component from '../../../Component';
|
|
||||||
|
|
||||||
export default function checkForComputedKeys(
|
|
||||||
component: Component,
|
|
||||||
properties: Node[]
|
|
||||||
) {
|
|
||||||
properties.forEach(prop => {
|
|
||||||
if (prop.key.computed) {
|
|
||||||
component.error(prop, {
|
|
||||||
code: `computed-key`,
|
|
||||||
message: `Cannot use computed keys`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import { Node } from '../../../../interfaces';
|
|
||||||
import getName from '../../../../utils/getName';
|
|
||||||
import Component from '../../../Component';
|
|
||||||
|
|
||||||
export default function checkForDupes(
|
|
||||||
component: Component,
|
|
||||||
properties: Node[]
|
|
||||||
) {
|
|
||||||
const seen = new Set();
|
|
||||||
|
|
||||||
properties.forEach(prop => {
|
|
||||||
const name = getName(prop.key);
|
|
||||||
|
|
||||||
if (seen.has(name)) {
|
|
||||||
component.error(prop, {
|
|
||||||
code: `duplicate-property`,
|
|
||||||
message: `Duplicate property '${name}'`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
seen.add(name);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { walk } from 'estree-walker';
|
|
||||||
import isReference from 'is-reference';
|
|
||||||
import { Node } from '../../../../interfaces';
|
|
||||||
|
|
||||||
export default function usesThisOrArguments(node: Node) {
|
|
||||||
let result = false;
|
|
||||||
|
|
||||||
walk(node, {
|
|
||||||
enter(node: Node, parent: Node) {
|
|
||||||
if (
|
|
||||||
result ||
|
|
||||||
node.type === 'FunctionExpression' ||
|
|
||||||
node.type === 'FunctionDeclaration'
|
|
||||||
) {
|
|
||||||
return this.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.type === 'ThisExpression') {
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
node.type === 'Identifier' &&
|
|
||||||
isReference(node, parent) &&
|
|
||||||
node.name === 'arguments'
|
|
||||||
) {
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import FuzzySet from './FuzzySet';
|
|
||||||
|
|
||||||
export default function fuzzymatch(name: string, names: string[]) {
|
|
||||||
const set = new FuzzySet(names);
|
|
||||||
const matches = set.get(name);
|
|
||||||
|
|
||||||
return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null;
|
|
||||||
}
|
|
@ -1,213 +0,0 @@
|
|||||||
import { parseExpressionAt } from 'acorn';
|
|
||||||
import { Parser } from '../index';
|
|
||||||
|
|
||||||
const DIRECTIVES: Record<string, {
|
|
||||||
names: string[];
|
|
||||||
attribute: (
|
|
||||||
start: number,
|
|
||||||
end: number,
|
|
||||||
type: string,
|
|
||||||
name: string,
|
|
||||||
expression?: any,
|
|
||||||
directiveName?: string
|
|
||||||
) => { start: number, end: number, type: string, name: string, value?: any, expression?: any };
|
|
||||||
allowedExpressionTypes: string[];
|
|
||||||
error: string;
|
|
||||||
}> = {
|
|
||||||
Ref: {
|
|
||||||
names: ['ref'],
|
|
||||||
attribute(start, end, type, name) {
|
|
||||||
return { start, end, type, name };
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: [],
|
|
||||||
error: 'ref directives cannot have a value'
|
|
||||||
},
|
|
||||||
|
|
||||||
EventHandler: {
|
|
||||||
names: ['on'],
|
|
||||||
attribute(start, end, type, lhs, expression) {
|
|
||||||
const [name, ...modifiers] = lhs.split('|');
|
|
||||||
return { start, end, type, name, modifiers, expression };
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['CallExpression'],
|
|
||||||
error: 'Expected a method call'
|
|
||||||
},
|
|
||||||
|
|
||||||
Binding: {
|
|
||||||
names: ['bind'],
|
|
||||||
attribute(start, end, type, name, expression) {
|
|
||||||
return {
|
|
||||||
start, end, type, name,
|
|
||||||
value: expression || {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: start + 5,
|
|
||||||
end,
|
|
||||||
name,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['Identifier', 'MemberExpression'],
|
|
||||||
error: 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)'
|
|
||||||
},
|
|
||||||
|
|
||||||
Transition: {
|
|
||||||
names: ['in', 'out', 'transition'],
|
|
||||||
attribute(start, end, type, name, expression, directiveName) {
|
|
||||||
return {
|
|
||||||
start, end, type, name, expression,
|
|
||||||
intro: directiveName === 'in' || directiveName === 'transition',
|
|
||||||
outro: directiveName === 'out' || directiveName === 'transition',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['ObjectExpression'],
|
|
||||||
error: 'Transition argument must be an object literal, e.g. `{ duration: 400 }`'
|
|
||||||
},
|
|
||||||
|
|
||||||
Animation: {
|
|
||||||
names: ['animate'],
|
|
||||||
attribute(start, end, type, name, expression) {
|
|
||||||
return { start, end, type, name, expression };
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['ObjectExpression'],
|
|
||||||
error: 'Animation argument must be an object literal, e.g. `{ duration: 400 }`'
|
|
||||||
},
|
|
||||||
|
|
||||||
Action: {
|
|
||||||
names: ['use'],
|
|
||||||
attribute(start, end, type, name, expression) {
|
|
||||||
return { start, end, type, name, expression };
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['*'],
|
|
||||||
error: 'Data passed to actions must be an identifier (e.g. `foo`), a member expression ' +
|
|
||||||
'(e.g. `foo.bar` or `foo[baz]`), a method call (e.g. `foo()`), or a literal (e.g. `true` or `\'a string\'`'
|
|
||||||
},
|
|
||||||
|
|
||||||
Class: {
|
|
||||||
names: ['class'],
|
|
||||||
attribute(start, end, type, name, expression) {
|
|
||||||
return { start, end, type, name, expression };
|
|
||||||
},
|
|
||||||
allowedExpressionTypes: ['*'],
|
|
||||||
error: 'Data passed to class directives must be an expression'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const lookupByName = {};
|
|
||||||
|
|
||||||
Object.keys(DIRECTIVES).forEach(name => {
|
|
||||||
const directive = DIRECTIVES[name];
|
|
||||||
directive.names.forEach(type => lookupByName[type] = name);
|
|
||||||
});
|
|
||||||
|
|
||||||
function readExpression(parser: Parser, start: number, quoteMark: string|null) {
|
|
||||||
let i = start;
|
|
||||||
let escaped = false;
|
|
||||||
|
|
||||||
for (; i < parser.template.length; i += 1) {
|
|
||||||
const char = parser.template[i];
|
|
||||||
|
|
||||||
if (quoteMark) {
|
|
||||||
if (char === quoteMark) {
|
|
||||||
if (!escaped) break;
|
|
||||||
} else if (escaped) {
|
|
||||||
escaped = false;
|
|
||||||
} else if (char === '\\') {
|
|
||||||
escaped = true;
|
|
||||||
}
|
|
||||||
} else if (/[\s\/>]/.test(char)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const expression = parseExpressionAt(parser.template.slice(0, i), start, {
|
|
||||||
ecmaVersion: 9,
|
|
||||||
});
|
|
||||||
parser.index = expression.end;
|
|
||||||
|
|
||||||
parser.allowWhitespace();
|
|
||||||
if (quoteMark) parser.eat(quoteMark, true);
|
|
||||||
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readDirective(
|
|
||||||
parser: Parser,
|
|
||||||
start: number,
|
|
||||||
attrName: string
|
|
||||||
) {
|
|
||||||
const [directiveName, name] = attrName.split(':');
|
|
||||||
if (name === undefined) return; // No colon in the name
|
|
||||||
|
|
||||||
if (directiveName === '') {
|
|
||||||
// not a directive — :foo is short for foo={{foo}}
|
|
||||||
return {
|
|
||||||
start: start,
|
|
||||||
end: start + name.length + 1,
|
|
||||||
type: 'Attribute',
|
|
||||||
name,
|
|
||||||
value: getShorthandValue(start + 1, name)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = lookupByName[directiveName];
|
|
||||||
if (!type) return; // not a registered directive
|
|
||||||
|
|
||||||
const directive = DIRECTIVES[type];
|
|
||||||
let expression = null;
|
|
||||||
|
|
||||||
if (parser.eat('=')) {
|
|
||||||
const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
|
|
||||||
|
|
||||||
const expressionStart = parser.index;
|
|
||||||
|
|
||||||
try {
|
|
||||||
expression = readExpression(parser, expressionStart, quoteMark);
|
|
||||||
const allowed = directive.allowedExpressionTypes;
|
|
||||||
if (allowed[0] !== '*' && allowed.indexOf(expression.type) === -1) {
|
|
||||||
parser.error({
|
|
||||||
code: `invalid-directive-value`,
|
|
||||||
message: directive.error
|
|
||||||
}, expressionStart);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (parser.template[expressionStart] === '{') {
|
|
||||||
// assume the mistake was wrapping the directive arguments.
|
|
||||||
// this could yield false positives! but hopefully not too many
|
|
||||||
let message = 'directive values should not be wrapped';
|
|
||||||
const expressionEnd = parser.template.indexOf('}', expressionStart);
|
|
||||||
if (expressionEnd !== -1) {
|
|
||||||
const value = parser.template.slice(expressionStart + 1, expressionEnd);
|
|
||||||
message += ` — use '${value}', not '{${value}}'`;
|
|
||||||
}
|
|
||||||
parser.error({
|
|
||||||
code: `invalid-directive-value`,
|
|
||||||
message
|
|
||||||
}, expressionStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return directive.attribute(start, parser.index, type, name, expression, directiveName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getShorthandValue(start: number, name: string) {
|
|
||||||
const end = start + name.length;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
type: 'AttributeShorthand',
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
expression: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { Node } from '../interfaces';
|
|
||||||
|
|
||||||
export default function getMethodName(node: Node) {
|
|
||||||
if (node.type === 'Identifier') return node.name;
|
|
||||||
if (node.type === 'Literal') return String(node.value);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { Node } from '../interfaces';
|
|
||||||
|
|
||||||
export default function isThisGetCallExpression(node: Node): boolean {
|
|
||||||
return node.type === 'CallExpression' &&
|
|
||||||
node.callee.type === 'MemberExpression' &&
|
|
||||||
node.callee.object.type === 'ThisExpression' &&
|
|
||||||
node.callee.property.name === 'get';
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { Node } from '../interfaces';
|
|
||||||
|
|
||||||
export default function nodeToString(node: Node) {
|
|
||||||
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
||||||
return node.value;
|
|
||||||
} else if (node.type === 'TemplateLiteral'
|
|
||||||
&& node.quasis.length === 1
|
|
||||||
&& node.expressions.length === 0) {
|
|
||||||
return node.quasis[0].value.raw;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
const validCalleeObjects = new Set(['this', 'event', 'console']);
|
|
||||||
|
|
||||||
export default validCalleeObjects;
|
|
@ -1,21 +0,0 @@
|
|||||||
import { Node } from '../interfaces';
|
|
||||||
import { walk } from 'estree-walker';
|
|
||||||
|
|
||||||
export default function walkThroughTopFunctionScope(body: Node, callback: Function) {
|
|
||||||
let lexicalDepth = 0;
|
|
||||||
walk(body, {
|
|
||||||
enter(node: Node) {
|
|
||||||
if (/^Function/.test(node.type)) {
|
|
||||||
lexicalDepth += 1;
|
|
||||||
} else if (lexicalDepth === 0) {
|
|
||||||
callback(node)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
leave(node: Node) {
|
|
||||||
if (/^Function/.test(node.type)) {
|
|
||||||
lexicalDepth -= 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in new issue