340 lines
10 KiB
JavaScript
340 lines
10 KiB
JavaScript
import { Syntax } from 'esotope-hammerhead';
|
|
|
|
function property(ctx) {
|
|
const { js } = ctx;
|
|
js.on('MemberExpression', (node, data, type) => {
|
|
if (node.object.type === 'Super') return false;
|
|
|
|
if (type === 'rewrite' && computedProperty(node)) {
|
|
data.changes.push({
|
|
node: '__uv.$wrap((',
|
|
start: node.property.start,
|
|
end: node.property.start,
|
|
});
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
node: '))',
|
|
start: node.property.end,
|
|
end: node.property.end,
|
|
});
|
|
};
|
|
}
|
|
|
|
if (
|
|
(!node.computed &&
|
|
node.property.name === 'location' &&
|
|
type === 'rewrite') ||
|
|
(node.property.name === '__uv$location' && type === 'source')
|
|
) {
|
|
data.changes.push({
|
|
start: node.property.start,
|
|
end: node.property.end,
|
|
node:
|
|
type === 'rewrite'
|
|
? '__uv$setSource(__uv).__uv$location'
|
|
: 'location',
|
|
});
|
|
}
|
|
|
|
if (
|
|
(!node.computed &&
|
|
node.property.name === 'top' &&
|
|
type === 'rewrite') ||
|
|
(node.property.name === '__uv$top' && type === 'source')
|
|
) {
|
|
data.changes.push({
|
|
start: node.property.start,
|
|
end: node.property.end,
|
|
node:
|
|
type === 'rewrite'
|
|
? '__uv$setSource(__uv).__uv$top'
|
|
: 'top',
|
|
});
|
|
}
|
|
|
|
if (
|
|
(!node.computed &&
|
|
node.property.name === 'parent' &&
|
|
type === 'rewrite') ||
|
|
(node.property.name === '__uv$parent' && type === 'source')
|
|
) {
|
|
data.changes.push({
|
|
start: node.property.start,
|
|
end: node.property.end,
|
|
node:
|
|
type === 'rewrite'
|
|
? '__uv$setSource(__uv).__uv$parent'
|
|
: 'parent',
|
|
});
|
|
}
|
|
|
|
if (
|
|
!node.computed &&
|
|
node.property.name === 'postMessage' &&
|
|
type === 'rewrite'
|
|
) {
|
|
data.changes.push({
|
|
start: node.property.start,
|
|
end: node.property.end,
|
|
node: '__uv$setSource(__uv).postMessage',
|
|
});
|
|
}
|
|
|
|
if (
|
|
(!node.computed &&
|
|
node.property.name === 'eval' &&
|
|
type === 'rewrite') ||
|
|
(node.property.name === '__uv$eval' && type === 'source')
|
|
) {
|
|
data.changes.push({
|
|
start: node.property.start,
|
|
end: node.property.end,
|
|
node:
|
|
type === 'rewrite'
|
|
? '__uv$setSource(__uv).__uv$eval'
|
|
: 'eval',
|
|
});
|
|
}
|
|
|
|
if (
|
|
!node.computed &&
|
|
node.property.name === '__uv$setSource' &&
|
|
type === 'source' &&
|
|
node.parent.type === Syntax.CallExpression
|
|
) {
|
|
const { parent, property } = node;
|
|
data.changes.push({
|
|
start: property.start - 1,
|
|
end: parent.end,
|
|
});
|
|
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
start: property.start,
|
|
end: parent.end,
|
|
});
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
function identifier(ctx) {
|
|
const { js } = ctx;
|
|
js.on('Identifier', (node, data, type) => {
|
|
if (type !== 'rewrite') return false;
|
|
const { parent } = node;
|
|
if (!['location', 'eval', 'parent', 'top'].includes(node.name))
|
|
return false;
|
|
if (parent.type === Syntax.VariableDeclarator && parent.id === node)
|
|
return false;
|
|
if (
|
|
(parent.type === Syntax.AssignmentExpression ||
|
|
parent.type === Syntax.AssignmentPattern) &&
|
|
parent.left === node
|
|
)
|
|
return false;
|
|
if (
|
|
(parent.type === Syntax.FunctionExpression ||
|
|
parent.type === Syntax.FunctionDeclaration) &&
|
|
parent.id === node
|
|
)
|
|
return false;
|
|
if (
|
|
parent.type === Syntax.MemberExpression &&
|
|
parent.property === node &&
|
|
!parent.computed
|
|
)
|
|
return false;
|
|
if (
|
|
node.name === 'eval' &&
|
|
parent.type === Syntax.CallExpression &&
|
|
parent.callee === node
|
|
)
|
|
return false;
|
|
if (parent.type === Syntax.Property && parent.key === node)
|
|
return false;
|
|
if (
|
|
parent.type === Syntax.Property &&
|
|
parent.value === node &&
|
|
parent.shorthand
|
|
)
|
|
return false;
|
|
if (
|
|
parent.type === Syntax.UpdateExpression &&
|
|
(parent.operator === '++' || parent.operator === '--')
|
|
)
|
|
return false;
|
|
if (
|
|
(parent.type === Syntax.FunctionExpression ||
|
|
parent.type === Syntax.FunctionDeclaration ||
|
|
parent.type === Syntax.ArrowFunctionExpression) &&
|
|
parent.params.indexOf(node) !== -1
|
|
)
|
|
return false;
|
|
if (parent.type === Syntax.MethodDefinition) return false;
|
|
if (parent.type === Syntax.ClassDeclaration) return false;
|
|
if (parent.type === Syntax.RestElement) return false;
|
|
if (parent.type === Syntax.ExportSpecifier) return false;
|
|
if (parent.type === Syntax.ImportSpecifier) return false;
|
|
|
|
data.changes.push({
|
|
start: node.start,
|
|
end: node.end,
|
|
node: '__uv.$get(' + node.name + ')',
|
|
});
|
|
});
|
|
}
|
|
|
|
function wrapEval(ctx) {
|
|
const { js } = ctx;
|
|
js.on('CallExpression', (node, data, type) => {
|
|
if (type !== 'rewrite') return false;
|
|
if (!node.arguments.length) return false;
|
|
if (node.callee.type !== 'Identifier') return false;
|
|
if (node.callee.name !== 'eval') return false;
|
|
|
|
const [script] = node.arguments;
|
|
|
|
data.changes.push({
|
|
node: '__uv.js.rewrite(',
|
|
start: script.start,
|
|
end: script.start,
|
|
});
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
node: ')',
|
|
start: script.end,
|
|
end: script.end,
|
|
});
|
|
};
|
|
});
|
|
}
|
|
|
|
function importDeclaration(ctx) {
|
|
const { js } = ctx;
|
|
js.on(Syntax.Literal, (node, data, type) => {
|
|
if (
|
|
!(
|
|
(node.parent.type === Syntax.ImportDeclaration ||
|
|
node.parent.type === Syntax.ExportAllDeclaration ||
|
|
node.parent.type === Syntax.ExportNamedDeclaration) &&
|
|
node.parent.source === node
|
|
)
|
|
)
|
|
return false;
|
|
|
|
data.changes.push({
|
|
start: node.start + 1,
|
|
end: node.end - 1,
|
|
node:
|
|
type === 'rewrite'
|
|
? ctx.rewriteUrl(node.value)
|
|
: ctx.sourceUrl(node.value),
|
|
});
|
|
});
|
|
}
|
|
|
|
function dynamicImport(ctx) {
|
|
const { js } = ctx;
|
|
js.on(Syntax.ImportExpression, (node, data, type) => {
|
|
if (type !== 'rewrite') return false;
|
|
data.changes.push({
|
|
node: '__uv.rewriteUrl(',
|
|
start: node.source.start,
|
|
end: node.source.start,
|
|
});
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
node: ')',
|
|
start: node.source.end,
|
|
end: node.source.end,
|
|
});
|
|
};
|
|
});
|
|
}
|
|
|
|
function unwrap(ctx) {
|
|
const { js } = ctx;
|
|
js.on('CallExpression', (node, data, type) => {
|
|
if (type !== 'source') return false;
|
|
if (!isWrapped(node.callee)) return false;
|
|
|
|
switch (node.callee.property.name) {
|
|
case '$wrap':
|
|
if (
|
|
!node.arguments ||
|
|
node.parent.type !== Syntax.MemberExpression ||
|
|
node.parent.property !== node
|
|
)
|
|
return false;
|
|
const [property] = node.arguments;
|
|
|
|
data.changes.push({
|
|
start: node.callee.start,
|
|
end: property.start,
|
|
});
|
|
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
start: node.end - 2,
|
|
end: node.end,
|
|
});
|
|
};
|
|
break;
|
|
case '$get':
|
|
case 'rewriteUrl':
|
|
const [arg] = node.arguments;
|
|
|
|
data.changes.push({
|
|
start: node.callee.start,
|
|
end: arg.start,
|
|
});
|
|
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
start: node.end - 1,
|
|
end: node.end,
|
|
});
|
|
};
|
|
break;
|
|
case 'rewrite':
|
|
const [script] = node.arguments;
|
|
data.changes.push({
|
|
start: node.callee.start,
|
|
end: script.start,
|
|
});
|
|
node.iterateEnd = function () {
|
|
data.changes.push({
|
|
start: node.end - 1,
|
|
end: node.end,
|
|
});
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
function isWrapped(node) {
|
|
if (node.type !== Syntax.MemberExpression) return false;
|
|
if (node.property.name === 'rewrite' && isWrapped(node.object)) return true;
|
|
if (node.object.type !== Syntax.Identifier || node.object.name !== '__uv')
|
|
return false;
|
|
if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
function computedProperty(parent) {
|
|
if (!parent.computed) return false;
|
|
const { property: node } = parent;
|
|
if (node.type === 'Literal' && !['location', 'top', 'parent']) return false;
|
|
return true;
|
|
}
|
|
|
|
export {
|
|
property,
|
|
wrapEval,
|
|
dynamicImport,
|
|
importDeclaration,
|
|
identifier,
|
|
unwrap,
|
|
};
|