mirror of https://github.com/sveltejs/svelte
commit
fe75570b91
@ -1,9 +0,0 @@
|
||||
const glob = require( 'glob' );
|
||||
const fs = require( 'fs' );
|
||||
|
||||
glob.sync( 'src/**/*.js' ).forEach( file => {
|
||||
console.log( file );
|
||||
const js = fs.readFileSync( file, 'utf-8' );
|
||||
fs.writeFileSync( file.replace( /\.js$/, '.ts' ), js );
|
||||
fs.unlinkSync( file );
|
||||
});
|
@ -1,39 +1,52 @@
|
||||
import deindent from '../../../../utils/deindent.js';
|
||||
import deindent from '../../../../utils/deindent';
|
||||
import { DomGenerator } from '../../index';
|
||||
import Block from '../../Block';
|
||||
import { Node } from '../../../../interfaces';
|
||||
import { State } from '../../interfaces';
|
||||
|
||||
export default function visitEventHandler ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node, local ) {
|
||||
export default function visitEventHandler(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node,
|
||||
attribute: Node,
|
||||
local
|
||||
) {
|
||||
// TODO verify that it's a valid callee (i.e. built-in or declared method)
|
||||
generator.addSourcemapLocations( attribute.expression );
|
||||
generator.code.prependRight( attribute.expression.start, `${block.component}.` );
|
||||
generator.addSourcemapLocations(attribute.expression);
|
||||
generator.code.prependRight(
|
||||
attribute.expression.start,
|
||||
`${block.component}.`
|
||||
);
|
||||
|
||||
const usedContexts: string[] = [];
|
||||
attribute.expression.arguments.forEach( ( arg: Node ) => {
|
||||
const { contexts } = block.contextualise( arg, null, true );
|
||||
attribute.expression.arguments.forEach((arg: Node) => {
|
||||
const { contexts } = block.contextualise(arg, null, true);
|
||||
|
||||
contexts.forEach( context => {
|
||||
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );
|
||||
if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context );
|
||||
contexts.forEach(context => {
|
||||
if (!~usedContexts.indexOf(context)) usedContexts.push(context);
|
||||
if (!~local.allUsedContexts.indexOf(context))
|
||||
local.allUsedContexts.push(context);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO hoist event handlers? can do `this.__component.method(...)`
|
||||
const declarations = usedContexts.map( name => {
|
||||
if ( name === 'state' ) return 'var state = this._context.state;';
|
||||
const declarations = usedContexts.map(name => {
|
||||
if (name === 'state') return 'var state = this._context.state;';
|
||||
|
||||
const listName = block.listNames.get( name );
|
||||
const indexName = block.indexNames.get( name );
|
||||
const listName = block.listNames.get(name);
|
||||
const indexName = block.indexNames.get(name);
|
||||
|
||||
return `var ${listName} = this._context.${listName}, ${indexName} = this._context.${indexName}, ${name} = ${listName}[${indexName}]`;
|
||||
});
|
||||
|
||||
const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
|
||||
const handlerBody =
|
||||
(declarations.length ? declarations.join('\n') + '\n\n' : '') +
|
||||
`[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
|
||||
|
||||
local.create.addBlock( deindent`
|
||||
local.create.addBlock(deindent`
|
||||
${local.name}.on( '${attribute.name}', function ( event ) {
|
||||
${handlerBody}
|
||||
});
|
||||
` );
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
@ -1,17 +1,24 @@
|
||||
import deindent from '../../../../utils/deindent.js';
|
||||
import deindent from '../../../../utils/deindent';
|
||||
import { DomGenerator } from '../../index';
|
||||
import Block from '../../Block';
|
||||
import { Node } from '../../../../interfaces';
|
||||
import { State } from '../../interfaces';
|
||||
|
||||
export default function visitRef ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node, local ) {
|
||||
export default function visitRef(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node,
|
||||
attribute: Node,
|
||||
local
|
||||
) {
|
||||
generator.usesRefs = true;
|
||||
|
||||
local.create.addLine(
|
||||
`${block.component}.refs.${attribute.name} = ${local.name};`
|
||||
);
|
||||
|
||||
block.builders.destroy.addLine( deindent`
|
||||
block.builders.destroy.addLine(deindent`
|
||||
if ( ${block.component}.refs.${attribute.name} === ${local.name} ) ${block.component}.refs.${attribute.name} = null;
|
||||
` );
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
@ -1,19 +1,25 @@
|
||||
import deindent from '../../../../utils/deindent.js';
|
||||
import deindent from '../../../../utils/deindent';
|
||||
import { DomGenerator } from '../../index';
|
||||
import Block from '../../Block';
|
||||
import { Node } from '../../../../interfaces';
|
||||
import { State } from '../../interfaces';
|
||||
|
||||
export default function visitRef ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node ) {
|
||||
export default function visitRef(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node,
|
||||
attribute: Node
|
||||
) {
|
||||
const name = attribute.name;
|
||||
|
||||
block.builders.create.addLine(
|
||||
`${block.component}.refs.${name} = ${state.parentNode};`
|
||||
);
|
||||
|
||||
block.builders.destroy.addLine( deindent`
|
||||
block.builders.destroy.addLine(deindent`
|
||||
if ( ${block.component}.refs.${name} === ${state.parentNode} ) ${block.component}.refs.${name} = null;
|
||||
` );
|
||||
`);
|
||||
|
||||
generator.usesRefs = true; // so this component.refs object is created
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,91 @@
|
||||
import deindent from '../../../../utils/deindent.js';
|
||||
import deindent from '../../../../utils/deindent';
|
||||
import { DomGenerator } from '../../index';
|
||||
import Block from '../../Block';
|
||||
import { Node } from '../../../../interfaces';
|
||||
import { State } from '../../interfaces';
|
||||
|
||||
export default function addTransitions ( generator: DomGenerator, block: Block, state: State, node: Node, intro, outro ) {
|
||||
const wrapTransition = generator.helper( 'wrapTransition' );
|
||||
export default function addTransitions(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node,
|
||||
intro,
|
||||
outro
|
||||
) {
|
||||
const wrapTransition = generator.helper('wrapTransition');
|
||||
|
||||
if ( intro === outro ) {
|
||||
const name = block.getUniqueName( `${state.name}_transition` );
|
||||
const snippet = intro.expression ? block.contextualise( intro.expression ).snippet : '{}';
|
||||
if (intro === outro) {
|
||||
const name = block.getUniqueName(`${state.name}_transition`);
|
||||
const snippet = intro.expression
|
||||
? block.contextualise(intro.expression).snippet
|
||||
: '{}';
|
||||
|
||||
block.addVariable( name );
|
||||
block.addVariable(name);
|
||||
|
||||
const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`;
|
||||
const fn = `${generator.alias('template')}.transitions.${intro.name}`;
|
||||
|
||||
block.builders.intro.addBlock( deindent`
|
||||
block.builders.intro.addBlock(deindent`
|
||||
${block.component}._renderHooks.push( function () {
|
||||
if ( !${name} ) ${name} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null );
|
||||
${name}.run( true, function () {
|
||||
${block.component}.fire( 'intro.end', { node: ${state.name} });
|
||||
});
|
||||
});
|
||||
` );
|
||||
`);
|
||||
|
||||
block.builders.outro.addBlock( deindent`
|
||||
block.builders.outro.addBlock(deindent`
|
||||
${name}.run( false, function () {
|
||||
${block.component}.fire( 'outro.end', { node: ${state.name} });
|
||||
if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}();
|
||||
if ( --${block.alias('outros')} === 0 ) ${block.alias('outrocallback')}();
|
||||
${name} = null;
|
||||
});
|
||||
` );
|
||||
}
|
||||
|
||||
else {
|
||||
const introName = intro && block.getUniqueName( `${state.name}_intro` );
|
||||
const outroName = outro && block.getUniqueName( `${state.name}_outro` );
|
||||
`);
|
||||
} else {
|
||||
const introName = intro && block.getUniqueName(`${state.name}_intro`);
|
||||
const outroName = outro && block.getUniqueName(`${state.name}_outro`);
|
||||
|
||||
if ( intro ) {
|
||||
block.addVariable( introName );
|
||||
const snippet = intro.expression ? block.contextualise( intro.expression ).snippet : '{}';
|
||||
if (intro) {
|
||||
block.addVariable(introName);
|
||||
const snippet = intro.expression
|
||||
? block.contextualise(intro.expression).snippet
|
||||
: '{}';
|
||||
|
||||
const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; // TODO add built-in transitions?
|
||||
const fn = `${generator.alias('template')}.transitions.${intro.name}`; // TODO add built-in transitions?
|
||||
|
||||
if ( outro ) {
|
||||
block.builders.intro.addBlock( deindent`
|
||||
if (outro) {
|
||||
block.builders.intro.addBlock(deindent`
|
||||
if ( ${introName} ) ${introName}.abort();
|
||||
if ( ${outroName} ) ${outroName}.abort();
|
||||
` );
|
||||
`);
|
||||
}
|
||||
|
||||
block.builders.intro.addBlock( deindent`
|
||||
block.builders.intro.addBlock(deindent`
|
||||
${block.component}._renderHooks.push( function () {
|
||||
${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null );
|
||||
${introName}.run( true, function () {
|
||||
${block.component}.fire( 'intro.end', { node: ${state.name} });
|
||||
});
|
||||
});
|
||||
` );
|
||||
`);
|
||||
}
|
||||
|
||||
if ( outro ) {
|
||||
block.addVariable( outroName );
|
||||
const snippet = outro.expression ? block.contextualise( outro.expression ).snippet : '{}';
|
||||
if (outro) {
|
||||
block.addVariable(outroName);
|
||||
const snippet = outro.expression
|
||||
? block.contextualise(outro.expression).snippet
|
||||
: '{}';
|
||||
|
||||
const fn = `${generator.alias( 'template' )}.transitions.${outro.name}`;
|
||||
const fn = `${generator.alias('template')}.transitions.${outro.name}`;
|
||||
|
||||
// TODO hide elements that have outro'd (unless they belong to a still-outroing
|
||||
// group) prior to their removal from the DOM
|
||||
block.builders.outro.addBlock( deindent`
|
||||
block.builders.outro.addBlock(deindent`
|
||||
${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, false, null );
|
||||
${outroName}.run( false, function () {
|
||||
${block.component}.fire( 'outro.end', { node: ${state.name} });
|
||||
if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}();
|
||||
if ( --${block.alias('outros')} === 0 ) ${block.alias('outrocallback')}();
|
||||
});
|
||||
` );
|
||||
`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Node } from '../../../../interfaces';
|
||||
|
||||
export default function getStaticAttributeValue ( node: Node, name: string ) {
|
||||
const attribute = node.attributes.find( ( attr: Node ) => attr.name.toLowerCase() === name );
|
||||
if ( !attribute ) return null;
|
||||
export default function getStaticAttributeValue(node: Node, name: string) {
|
||||
const attribute = node.attributes.find(
|
||||
(attr: Node) => attr.name.toLowerCase() === name
|
||||
);
|
||||
if (!attribute) return null;
|
||||
|
||||
if ( attribute.value.length !== 1 || attribute.value[0].type !== 'Text' ) {
|
||||
if (attribute.value.length !== 1 || attribute.value[0].type !== 'Text') {
|
||||
// TODO catch this in validation phase, give a more useful error (with location etc)
|
||||
throw new Error( `'${name} must be a static attribute` );
|
||||
throw new Error(`'${name} must be a static attribute`);
|
||||
}
|
||||
|
||||
return attribute.value[0].data;
|
||||
}
|
||||
}
|
||||
|
@ -1,122 +1,235 @@
|
||||
// source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
|
||||
const lookup = {
|
||||
accept: { appliesTo: [ 'form', 'input' ] },
|
||||
'accept-charset': { propertyName: 'acceptCharset', appliesTo: [ 'form' ] },
|
||||
accept: { appliesTo: ['form', 'input'] },
|
||||
'accept-charset': { propertyName: 'acceptCharset', appliesTo: ['form'] },
|
||||
accesskey: { propertyName: 'accessKey' },
|
||||
action: { appliesTo: [ 'form' ] },
|
||||
align: { appliesTo: [ 'applet', 'caption', 'col', 'colgroup', 'hr', 'iframe', 'img', 'table', 'tbody', 'td', 'tfoot' , 'th', 'thead', 'tr' ] },
|
||||
allowfullscreen: { propertyName: 'allowFullscreen', appliesTo: [ 'iframe' ] },
|
||||
alt: { appliesTo: [ 'applet', 'area', 'img', 'input' ] },
|
||||
async: { appliesTo: [ 'script' ] },
|
||||
autocomplete: { appliesTo: [ 'form', 'input' ] },
|
||||
autofocus: { appliesTo: [ 'button', 'input', 'keygen', 'select', 'textarea' ] },
|
||||
autoplay: { appliesTo: [ 'audio', 'video' ] },
|
||||
autosave: { appliesTo: [ 'input' ] },
|
||||
bgcolor: { propertyName: 'bgColor', appliesTo: [ 'body', 'col', 'colgroup', 'marquee', 'table', 'tbody', 'tfoot', 'td', 'th', 'tr' ] },
|
||||
border: { appliesTo: [ 'img', 'object', 'table' ] },
|
||||
buffered: { appliesTo: [ 'audio', 'video' ] },
|
||||
challenge: { appliesTo: [ 'keygen' ] },
|
||||
charset: { appliesTo: [ 'meta', 'script' ] },
|
||||
checked: { appliesTo: [ 'command', 'input' ] },
|
||||
cite: { appliesTo: [ 'blockquote', 'del', 'ins', 'q' ] },
|
||||
action: { appliesTo: ['form'] },
|
||||
align: {
|
||||
appliesTo: [
|
||||
'applet',
|
||||
'caption',
|
||||
'col',
|
||||
'colgroup',
|
||||
'hr',
|
||||
'iframe',
|
||||
'img',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'tr',
|
||||
],
|
||||
},
|
||||
allowfullscreen: { propertyName: 'allowFullscreen', appliesTo: ['iframe'] },
|
||||
alt: { appliesTo: ['applet', 'area', 'img', 'input'] },
|
||||
async: { appliesTo: ['script'] },
|
||||
autocomplete: { appliesTo: ['form', 'input'] },
|
||||
autofocus: { appliesTo: ['button', 'input', 'keygen', 'select', 'textarea'] },
|
||||
autoplay: { appliesTo: ['audio', 'video'] },
|
||||
autosave: { appliesTo: ['input'] },
|
||||
bgcolor: {
|
||||
propertyName: 'bgColor',
|
||||
appliesTo: [
|
||||
'body',
|
||||
'col',
|
||||
'colgroup',
|
||||
'marquee',
|
||||
'table',
|
||||
'tbody',
|
||||
'tfoot',
|
||||
'td',
|
||||
'th',
|
||||
'tr',
|
||||
],
|
||||
},
|
||||
border: { appliesTo: ['img', 'object', 'table'] },
|
||||
buffered: { appliesTo: ['audio', 'video'] },
|
||||
challenge: { appliesTo: ['keygen'] },
|
||||
charset: { appliesTo: ['meta', 'script'] },
|
||||
checked: { appliesTo: ['command', 'input'] },
|
||||
cite: { appliesTo: ['blockquote', 'del', 'ins', 'q'] },
|
||||
class: { propertyName: 'className' },
|
||||
code: { appliesTo: [ 'applet' ] },
|
||||
codebase: { propertyName: 'codeBase', appliesTo: [ 'applet' ] },
|
||||
color: { appliesTo: [ 'basefont', 'font', 'hr' ] },
|
||||
cols: { appliesTo: [ 'textarea' ] },
|
||||
colspan: { propertyName: 'colSpan', appliesTo: [ 'td', 'th' ] },
|
||||
content: { appliesTo: [ 'meta' ] },
|
||||
code: { appliesTo: ['applet'] },
|
||||
codebase: { propertyName: 'codeBase', appliesTo: ['applet'] },
|
||||
color: { appliesTo: ['basefont', 'font', 'hr'] },
|
||||
cols: { appliesTo: ['textarea'] },
|
||||
colspan: { propertyName: 'colSpan', appliesTo: ['td', 'th'] },
|
||||
content: { appliesTo: ['meta'] },
|
||||
contenteditable: { propertyName: 'contentEditable' },
|
||||
contextmenu: {},
|
||||
controls: { appliesTo: [ 'audio', 'video' ] },
|
||||
coords: { appliesTo: [ 'area' ] },
|
||||
data: { appliesTo: [ 'object' ] },
|
||||
datetime: { propertyName: 'dateTime', appliesTo: [ 'del', 'ins', 'time' ] },
|
||||
default: { appliesTo: [ 'track' ] },
|
||||
defer: { appliesTo: [ 'script' ] },
|
||||
controls: { appliesTo: ['audio', 'video'] },
|
||||
coords: { appliesTo: ['area'] },
|
||||
data: { appliesTo: ['object'] },
|
||||
datetime: { propertyName: 'dateTime', appliesTo: ['del', 'ins', 'time'] },
|
||||
default: { appliesTo: ['track'] },
|
||||
defer: { appliesTo: ['script'] },
|
||||
dir: {},
|
||||
dirname: { propertyName: 'dirName', appliesTo: [ 'input', 'textarea' ] },
|
||||
disabled: { appliesTo: [ 'button', 'command', 'fieldset', 'input', 'keygen', 'optgroup', 'option', 'select', 'textarea' ] },
|
||||
download: { appliesTo: [ 'a', 'area' ] },
|
||||
dirname: { propertyName: 'dirName', appliesTo: ['input', 'textarea'] },
|
||||
disabled: {
|
||||
appliesTo: [
|
||||
'button',
|
||||
'command',
|
||||
'fieldset',
|
||||
'input',
|
||||
'keygen',
|
||||
'optgroup',
|
||||
'option',
|
||||
'select',
|
||||
'textarea',
|
||||
],
|
||||
},
|
||||
download: { appliesTo: ['a', 'area'] },
|
||||
draggable: {},
|
||||
dropzone: {},
|
||||
enctype: { appliesTo: [ 'form' ] },
|
||||
for: { propertyName: 'htmlFor', appliesTo: [ 'label', 'output' ] },
|
||||
form: { appliesTo: [ 'button', 'fieldset', 'input', 'keygen', 'label', 'meter', 'object', 'output', 'progress', 'select', 'textarea' ] },
|
||||
formaction: { appliesTo: [ 'input', 'button' ] },
|
||||
headers: { appliesTo: [ 'td', 'th' ] },
|
||||
height: { appliesTo: [ 'canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video' ] },
|
||||
enctype: { appliesTo: ['form'] },
|
||||
for: { propertyName: 'htmlFor', appliesTo: ['label', 'output'] },
|
||||
form: {
|
||||
appliesTo: [
|
||||
'button',
|
||||
'fieldset',
|
||||
'input',
|
||||
'keygen',
|
||||
'label',
|
||||
'meter',
|
||||
'object',
|
||||
'output',
|
||||
'progress',
|
||||
'select',
|
||||
'textarea',
|
||||
],
|
||||
},
|
||||
formaction: { appliesTo: ['input', 'button'] },
|
||||
headers: { appliesTo: ['td', 'th'] },
|
||||
height: {
|
||||
appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
},
|
||||
hidden: {},
|
||||
high: { appliesTo: [ 'meter' ] },
|
||||
href: { appliesTo: [ 'a', 'area', 'base', 'link' ] },
|
||||
hreflang: { appliesTo: [ 'a', 'area', 'link' ] },
|
||||
'http-equiv': { propertyName: 'httpEquiv', appliesTo: [ 'meta' ] },
|
||||
icon: { appliesTo: [ 'command' ] },
|
||||
high: { appliesTo: ['meter'] },
|
||||
href: { appliesTo: ['a', 'area', 'base', 'link'] },
|
||||
hreflang: { appliesTo: ['a', 'area', 'link'] },
|
||||
'http-equiv': { propertyName: 'httpEquiv', appliesTo: ['meta'] },
|
||||
icon: { appliesTo: ['command'] },
|
||||
id: {},
|
||||
ismap: { propertyName: 'isMap', appliesTo: [ 'img' ] },
|
||||
ismap: { propertyName: 'isMap', appliesTo: ['img'] },
|
||||
itemprop: {},
|
||||
keytype: { appliesTo: [ 'keygen' ] },
|
||||
kind: { appliesTo: [ 'track' ] },
|
||||
label: { appliesTo: [ 'track' ] },
|
||||
keytype: { appliesTo: ['keygen'] },
|
||||
kind: { appliesTo: ['track'] },
|
||||
label: { appliesTo: ['track'] },
|
||||
lang: {},
|
||||
language: { appliesTo: [ 'script' ] },
|
||||
loop: { appliesTo: [ 'audio', 'bgsound', 'marquee', 'video' ] },
|
||||
low: { appliesTo: [ 'meter' ] },
|
||||
manifest: { appliesTo: [ 'html' ] },
|
||||
max: { appliesTo: [ 'input', 'meter', 'progress' ] },
|
||||
maxlength: { propertyName: 'maxLength', appliesTo: [ 'input', 'textarea' ] },
|
||||
media: { appliesTo: [ 'a', 'area', 'link', 'source', 'style' ] },
|
||||
method: { appliesTo: [ 'form' ] },
|
||||
min: { appliesTo: [ 'input', 'meter' ] },
|
||||
multiple: { appliesTo: [ 'input', 'select' ] },
|
||||
muted: { appliesTo: [ 'video' ] },
|
||||
name: { appliesTo: [ 'button', 'form', 'fieldset', 'iframe', 'input', 'keygen', 'object', 'output', 'select', 'textarea', 'map', 'meta', 'param' ] },
|
||||
novalidate: { propertyName: 'noValidate', appliesTo: [ 'form' ] },
|
||||
open: { appliesTo: [ 'details' ] },
|
||||
optimum: { appliesTo: [ 'meter' ] },
|
||||
pattern: { appliesTo: [ 'input' ] },
|
||||
ping: { appliesTo: [ 'a', 'area' ] },
|
||||
placeholder: { appliesTo: [ 'input', 'textarea' ] },
|
||||
poster: { appliesTo: [ 'video' ] },
|
||||
preload: { appliesTo: [ 'audio', 'video' ] },
|
||||
radiogroup: { appliesTo: [ 'command' ] },
|
||||
readonly: { propertyName: 'readOnly', appliesTo: [ 'input', 'textarea' ] },
|
||||
rel: { appliesTo: [ 'a', 'area', 'link' ] },
|
||||
required: { appliesTo: [ 'input', 'select', 'textarea' ] },
|
||||
reversed: { appliesTo: [ 'ol' ] },
|
||||
rows: { appliesTo: [ 'textarea' ] },
|
||||
rowspan: { propertyName: 'rowSpan', appliesTo: [ 'td', 'th' ] },
|
||||
sandbox: { appliesTo: [ 'iframe' ] },
|
||||
scope: { appliesTo: [ 'th' ] },
|
||||
scoped: { appliesTo: [ 'style' ] },
|
||||
seamless: { appliesTo: [ 'iframe' ] },
|
||||
selected: { appliesTo: [ 'option' ] },
|
||||
shape: { appliesTo: [ 'a', 'area' ] },
|
||||
size: { appliesTo: [ 'input', 'select' ] },
|
||||
sizes: { appliesTo: [ 'link', 'img', 'source' ] },
|
||||
span: { appliesTo: [ 'col', 'colgroup' ] },
|
||||
language: { appliesTo: ['script'] },
|
||||
loop: { appliesTo: ['audio', 'bgsound', 'marquee', 'video'] },
|
||||
low: { appliesTo: ['meter'] },
|
||||
manifest: { appliesTo: ['html'] },
|
||||
max: { appliesTo: ['input', 'meter', 'progress'] },
|
||||
maxlength: { propertyName: 'maxLength', appliesTo: ['input', 'textarea'] },
|
||||
media: { appliesTo: ['a', 'area', 'link', 'source', 'style'] },
|
||||
method: { appliesTo: ['form'] },
|
||||
min: { appliesTo: ['input', 'meter'] },
|
||||
multiple: { appliesTo: ['input', 'select'] },
|
||||
muted: { appliesTo: ['video'] },
|
||||
name: {
|
||||
appliesTo: [
|
||||
'button',
|
||||
'form',
|
||||
'fieldset',
|
||||
'iframe',
|
||||
'input',
|
||||
'keygen',
|
||||
'object',
|
||||
'output',
|
||||
'select',
|
||||
'textarea',
|
||||
'map',
|
||||
'meta',
|
||||
'param',
|
||||
],
|
||||
},
|
||||
novalidate: { propertyName: 'noValidate', appliesTo: ['form'] },
|
||||
open: { appliesTo: ['details'] },
|
||||
optimum: { appliesTo: ['meter'] },
|
||||
pattern: { appliesTo: ['input'] },
|
||||
ping: { appliesTo: ['a', 'area'] },
|
||||
placeholder: { appliesTo: ['input', 'textarea'] },
|
||||
poster: { appliesTo: ['video'] },
|
||||
preload: { appliesTo: ['audio', 'video'] },
|
||||
radiogroup: { appliesTo: ['command'] },
|
||||
readonly: { propertyName: 'readOnly', appliesTo: ['input', 'textarea'] },
|
||||
rel: { appliesTo: ['a', 'area', 'link'] },
|
||||
required: { appliesTo: ['input', 'select', 'textarea'] },
|
||||
reversed: { appliesTo: ['ol'] },
|
||||
rows: { appliesTo: ['textarea'] },
|
||||
rowspan: { propertyName: 'rowSpan', appliesTo: ['td', 'th'] },
|
||||
sandbox: { appliesTo: ['iframe'] },
|
||||
scope: { appliesTo: ['th'] },
|
||||
scoped: { appliesTo: ['style'] },
|
||||
seamless: { appliesTo: ['iframe'] },
|
||||
selected: { appliesTo: ['option'] },
|
||||
shape: { appliesTo: ['a', 'area'] },
|
||||
size: { appliesTo: ['input', 'select'] },
|
||||
sizes: { appliesTo: ['link', 'img', 'source'] },
|
||||
span: { appliesTo: ['col', 'colgroup'] },
|
||||
spellcheck: {},
|
||||
src: { appliesTo: [ 'audio', 'embed', 'iframe', 'img', 'input', 'script', 'source', 'track', 'video' ] },
|
||||
srcdoc: { appliesTo: [ 'iframe' ] },
|
||||
srclang: { appliesTo: [ 'track' ] },
|
||||
srcset: { appliesTo: [ 'img' ] },
|
||||
start: { appliesTo: [ 'ol' ] },
|
||||
step: { appliesTo: [ 'input' ] },
|
||||
src: {
|
||||
appliesTo: [
|
||||
'audio',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'script',
|
||||
'source',
|
||||
'track',
|
||||
'video',
|
||||
],
|
||||
},
|
||||
srcdoc: { appliesTo: ['iframe'] },
|
||||
srclang: { appliesTo: ['track'] },
|
||||
srcset: { appliesTo: ['img'] },
|
||||
start: { appliesTo: ['ol'] },
|
||||
step: { appliesTo: ['input'] },
|
||||
style: { propertyName: 'style.cssText' },
|
||||
summary: { appliesTo: [ 'table' ] },
|
||||
summary: { appliesTo: ['table'] },
|
||||
tabindex: { propertyName: 'tabIndex' },
|
||||
target: { appliesTo: [ 'a', 'area', 'base', 'form' ] },
|
||||
target: { appliesTo: ['a', 'area', 'base', 'form'] },
|
||||
title: {},
|
||||
type: { appliesTo: [ 'button', 'input', 'command', 'embed', 'object', 'script', 'source', 'style', 'menu' ] },
|
||||
usemap: { propertyName: 'useMap', appliesTo: [ 'img', 'input', 'object' ] },
|
||||
value: { appliesTo: [ 'button', 'option', 'input', 'li', 'meter', 'progress', 'param', 'select', 'textarea' ] },
|
||||
width: { appliesTo: [ 'canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video' ] },
|
||||
wrap: { appliesTo: [ 'textarea' ] }
|
||||
type: {
|
||||
appliesTo: [
|
||||
'button',
|
||||
'input',
|
||||
'command',
|
||||
'embed',
|
||||
'object',
|
||||
'script',
|
||||
'source',
|
||||
'style',
|
||||
'menu',
|
||||
],
|
||||
},
|
||||
usemap: { propertyName: 'useMap', appliesTo: ['img', 'input', 'object'] },
|
||||
value: {
|
||||
appliesTo: [
|
||||
'button',
|
||||
'option',
|
||||
'input',
|
||||
'li',
|
||||
'meter',
|
||||
'progress',
|
||||
'param',
|
||||
'select',
|
||||
'textarea',
|
||||
],
|
||||
},
|
||||
width: {
|
||||
appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
},
|
||||
wrap: { appliesTo: ['textarea'] },
|
||||
};
|
||||
|
||||
Object.keys( lookup ).forEach( name => {
|
||||
const metadata = lookup[ name ];
|
||||
if ( !metadata.propertyName ) metadata.propertyName = name;
|
||||
Object.keys(lookup).forEach(name => {
|
||||
const metadata = lookup[name];
|
||||
if (!metadata.propertyName) metadata.propertyName = name;
|
||||
});
|
||||
|
||||
export default lookup;
|
||||
|
@ -1,21 +1,31 @@
|
||||
import deindent from '../../../utils/deindent.js';
|
||||
import deindent from '../../../utils/deindent';
|
||||
import { DomGenerator } from '../index';
|
||||
import Block from '../Block';
|
||||
import { Node } from '../../../interfaces';
|
||||
import { State } from '../interfaces';
|
||||
|
||||
export default function visitMustacheTag ( generator: DomGenerator, block: Block, state: State, node: Node ) {
|
||||
export default function visitMustacheTag(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node
|
||||
) {
|
||||
const name = node._state.name;
|
||||
const value = block.getUniqueName( `${name}_value` );
|
||||
const value = block.getUniqueName(`${name}_value`);
|
||||
|
||||
const { snippet } = block.contextualise( node.expression );
|
||||
const { snippet } = block.contextualise(node.expression);
|
||||
|
||||
block.addVariable( value );
|
||||
block.addElement( name, `${generator.helper( 'createText' )}( ${value} = ${snippet} )`, state.parentNode, true );
|
||||
block.addVariable(value);
|
||||
block.addElement(
|
||||
name,
|
||||
`${generator.helper('createText')}( ${value} = ${snippet} )`,
|
||||
state.parentNode,
|
||||
true
|
||||
);
|
||||
|
||||
block.builders.update.addBlock( deindent`
|
||||
block.builders.update.addBlock(deindent`
|
||||
if ( ${value} !== ( ${value} = ${snippet} ) ) {
|
||||
${name}.data = ${value};
|
||||
}
|
||||
` );
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
@ -1,40 +1,57 @@
|
||||
import deindent from '../../../utils/deindent.js';
|
||||
import deindent from '../../../utils/deindent';
|
||||
import { DomGenerator } from '../index';
|
||||
import Block from '../Block';
|
||||
import { Node } from '../../../interfaces';
|
||||
import { State } from '../interfaces';
|
||||
|
||||
export default function visitRawMustacheTag ( generator: DomGenerator, block: Block, state: State, node: Node ) {
|
||||
export default function visitRawMustacheTag(
|
||||
generator: DomGenerator,
|
||||
block: Block,
|
||||
state: State,
|
||||
node: Node
|
||||
) {
|
||||
const name = node._state.basename;
|
||||
const before = node._state.name;
|
||||
const value = block.getUniqueName( `${name}_value` );
|
||||
const after = block.getUniqueName( `${name}_after` );
|
||||
const value = block.getUniqueName(`${name}_value`);
|
||||
const after = block.getUniqueName(`${name}_after`);
|
||||
|
||||
const { snippet } = block.contextualise( node.expression );
|
||||
const { snippet } = block.contextualise(node.expression);
|
||||
|
||||
// we would have used comments here, but the `insertAdjacentHTML` api only
|
||||
// exists for `Element`s.
|
||||
block.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true );
|
||||
block.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true );
|
||||
block.addElement(
|
||||
before,
|
||||
`${generator.helper('createElement')}( 'noscript' )`,
|
||||
state.parentNode,
|
||||
true
|
||||
);
|
||||
block.addElement(
|
||||
after,
|
||||
`${generator.helper('createElement')}( 'noscript' )`,
|
||||
state.parentNode,
|
||||
true
|
||||
);
|
||||
|
||||
const isToplevel = !state.parentNode;
|
||||
|
||||
block.builders.create.addLine( `var ${value} = ${snippet};` );
|
||||
block.builders.create.addLine(`var ${value} = ${snippet};`);
|
||||
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} );`;
|
||||
const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`;
|
||||
const detachStatement = `${generator.helper(
|
||||
'detachBetween'
|
||||
)}( ${before}, ${after} );`;
|
||||
|
||||
if ( isToplevel ) {
|
||||
block.builders.mount.addLine( mountStatement );
|
||||
if (isToplevel) {
|
||||
block.builders.mount.addLine(mountStatement);
|
||||
} else {
|
||||
block.builders.create.addLine( mountStatement );
|
||||
block.builders.create.addLine(mountStatement);
|
||||
}
|
||||
|
||||
block.builders.update.addBlock( deindent`
|
||||
block.builders.update.addBlock(deindent`
|
||||
if ( ${value} !== ( ${value} = ${snippet} ) ) {
|
||||
${detachStatement}
|
||||
${mountStatement}
|
||||
}
|
||||
` );
|
||||
`);
|
||||
|
||||
block.builders.detachRaw.addBlock( detachStatement );
|
||||
}
|
||||
block.builders.detachRaw.addBlock(detachStatement);
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export default function visitComment () {
|
||||
export default function visitComment() {
|
||||
// do nothing
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SsrGenerator } from '../index';
|
||||
|
||||
export default function visitYieldTag ( generator: SsrGenerator ) {
|
||||
generator.append( `\${options && options.yield ? options.yield() : ''}` );
|
||||
}
|
||||
export default function visitYieldTag(generator: SsrGenerator) {
|
||||
generator.append(`\${options && options.yield ? options.yield() : ''}`);
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export default function visitWindow () {
|
||||
export default function visitWindow() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,36 @@
|
||||
import getGlobals from './getGlobals';
|
||||
|
||||
export default function getOutro ( format: string, name: string, options, imports ) {
|
||||
if ( format === 'es' ) {
|
||||
export default function getOutro(
|
||||
format: string,
|
||||
name: string,
|
||||
options,
|
||||
imports
|
||||
) {
|
||||
if (format === 'es') {
|
||||
return `export default ${name};`;
|
||||
}
|
||||
|
||||
if ( format === 'amd' ) {
|
||||
if (format === 'amd') {
|
||||
return `return ${name};\n\n});`;
|
||||
}
|
||||
|
||||
if ( format === 'cjs' ) {
|
||||
if (format === 'cjs') {
|
||||
return `module.exports = ${name};`;
|
||||
}
|
||||
|
||||
if ( format === 'iife' ) {
|
||||
const globals = getGlobals( imports, options );
|
||||
return `return ${name};\n\n}(${globals.join( ', ' )}));`;
|
||||
if (format === 'iife') {
|
||||
const globals = getGlobals(imports, options);
|
||||
return `return ${name};\n\n}(${globals.join(', ')}));`;
|
||||
}
|
||||
|
||||
if ( format === 'eval' ) {
|
||||
const globals = getGlobals( imports, options );
|
||||
return `return ${name};\n\n}(${globals.join( ', ' )}));`;
|
||||
if (format === 'eval') {
|
||||
const globals = getGlobals(imports, options);
|
||||
return `return ${name};\n\n}(${globals.join(', ')}));`;
|
||||
}
|
||||
|
||||
if ( format === 'umd' ) {
|
||||
if (format === 'umd') {
|
||||
return `return ${name};\n\n})));`;
|
||||
}
|
||||
|
||||
throw new Error( `Not implemented: ${format}` );
|
||||
throw new Error(`Not implemented: ${format}`);
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { Node } from '../../../interfaces';
|
||||
|
||||
export default function walkHtml ( html: Node, visitors ) {
|
||||
function visit ( node: Node ) {
|
||||
const visitor = visitors[ node.type ];
|
||||
if ( !visitor ) throw new Error( `Not implemented: ${node.type}` );
|
||||
export default function walkHtml(html: Node, visitors) {
|
||||
function visit(node: Node) {
|
||||
const visitor = visitors[node.type];
|
||||
if (!visitor) throw new Error(`Not implemented: ${node.type}`);
|
||||
|
||||
if ( visitor.enter ) visitor.enter( node );
|
||||
if (visitor.enter) visitor.enter(node);
|
||||
|
||||
if ( node.children ) {
|
||||
node.children.forEach( ( child: Node ) => {
|
||||
visit( child );
|
||||
if (node.children) {
|
||||
node.children.forEach((child: Node) => {
|
||||
visit(child);
|
||||
});
|
||||
}
|
||||
|
||||
if ( visitor.leave ) visitor.leave( node );
|
||||
if (visitor.leave) visitor.leave(node);
|
||||
}
|
||||
|
||||
visit( html );
|
||||
visit(html);
|
||||
}
|
||||
|
@ -1,36 +1,37 @@
|
||||
import { parse } from 'acorn';
|
||||
import spaces from '../../utils/spaces.js';
|
||||
import spaces from '../../utils/spaces';
|
||||
import { Parser } from '../index';
|
||||
|
||||
const scriptClosingTag = '<\/script>';
|
||||
const scriptClosingTag = '</script>';
|
||||
|
||||
export default function readScript ( parser: Parser, start: number, attributes ) {
|
||||
export default function readScript(parser: Parser, start: number, attributes) {
|
||||
const scriptStart = parser.index;
|
||||
const scriptEnd = parser.template.indexOf( scriptClosingTag, scriptStart );
|
||||
const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart);
|
||||
|
||||
if ( scriptEnd === -1 ) parser.error( `<script> must have a closing tag` );
|
||||
if (scriptEnd === -1) parser.error(`<script> must have a closing tag`);
|
||||
|
||||
const source = spaces( scriptStart ) + parser.template.slice( scriptStart, scriptEnd );
|
||||
const source =
|
||||
spaces(scriptStart) + parser.template.slice(scriptStart, scriptEnd);
|
||||
parser.index = scriptEnd + scriptClosingTag.length;
|
||||
|
||||
let ast;
|
||||
|
||||
try {
|
||||
ast = parse( source, {
|
||||
ast = parse(source, {
|
||||
ecmaVersion: 8,
|
||||
sourceType: 'module'
|
||||
sourceType: 'module',
|
||||
});
|
||||
} catch ( err ) {
|
||||
parser.acornError( err );
|
||||
} catch (err) {
|
||||
parser.acornError(err);
|
||||
}
|
||||
|
||||
if ( !ast.body.length ) return null;
|
||||
if (!ast.body.length) return null;
|
||||
|
||||
ast.start = scriptStart;
|
||||
return {
|
||||
start,
|
||||
end: parser.index,
|
||||
attributes,
|
||||
content: ast
|
||||
content: ast,
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// https://github.com/darkskyapp/string-hash/blob/master/index.js
|
||||
export default function hash ( str: string ) :number {
|
||||
export default function hash(str: string): number {
|
||||
let hash = 5381;
|
||||
let i = str.length;
|
||||
|
||||
while ( i-- ) hash = ( hash * 33 ) ^ str.charCodeAt( i );
|
||||
while (i--) hash = (hash * 33) ^ str.charCodeAt(i);
|
||||
return hash >>> 0;
|
||||
}
|
||||
|
@ -1,35 +1,38 @@
|
||||
const fs = require( 'fs' );
|
||||
const path = require( 'path' );
|
||||
const acorn = require( 'acorn' );
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const acorn = require('acorn');
|
||||
|
||||
const declarations = {};
|
||||
|
||||
fs.readdirSync( __dirname ).forEach( file => {
|
||||
if ( !/^[a-z]+\.js$/.test( file ) ) return;
|
||||
fs.readdirSync(__dirname).forEach(file => {
|
||||
if (!/^[a-z]+\.js$/.test(file)) return;
|
||||
|
||||
const source = fs.readFileSync( path.join( __dirname, file ), 'utf-8' );
|
||||
const ast = acorn.parse( source, {
|
||||
const source = fs.readFileSync(path.join(__dirname, file), 'utf-8');
|
||||
const ast = acorn.parse(source, {
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module'
|
||||
});
|
||||
|
||||
ast.body.forEach( node => {
|
||||
if ( node.type !== 'ExportNamedDeclaration' ) return;
|
||||
ast.body.forEach(node => {
|
||||
if (node.type !== 'ExportNamedDeclaration') return;
|
||||
|
||||
const declaration = node.declaration;
|
||||
if ( !declaration ) return;
|
||||
if (!declaration) return;
|
||||
|
||||
const name = declaration.type === 'VariableDeclaration' ?
|
||||
declaration.declarations[0].id.name :
|
||||
declaration.id.name;
|
||||
const name = declaration.type === 'VariableDeclaration'
|
||||
? declaration.declarations[0].id.name
|
||||
: declaration.id.name;
|
||||
|
||||
const value = declaration.type === 'VariableDeclaration' ?
|
||||
declaration.declarations[0].init :
|
||||
declaration;
|
||||
const value = declaration.type === 'VariableDeclaration'
|
||||
? declaration.declarations[0].init
|
||||
: declaration;
|
||||
|
||||
declarations[ name ] = source.slice( value.start, value.end );
|
||||
declarations[name] = source.slice(value.start, value.end);
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync( 'src/generators/dom/shared.ts', `// this file is auto-generated, do not edit it
|
||||
export default ${JSON.stringify( declarations, null, '\t' )};` );
|
||||
fs.writeFileSync(
|
||||
'src/generators/dom/shared.ts',
|
||||
`// this file is auto-generated, do not edit it
|
||||
export default ${JSON.stringify(declarations, null, '\t')};`
|
||||
);
|
||||
|
@ -1,68 +1,68 @@
|
||||
export function appendNode ( node, target ) {
|
||||
target.appendChild( node );
|
||||
export function appendNode(node, target) {
|
||||
target.appendChild(node);
|
||||
}
|
||||
|
||||
export function insertNode ( node, target, anchor ) {
|
||||
target.insertBefore( node, anchor );
|
||||
export function insertNode(node, target, anchor) {
|
||||
target.insertBefore(node, anchor);
|
||||
}
|
||||
|
||||
export function detachNode ( node ) {
|
||||
node.parentNode.removeChild( node );
|
||||
export function detachNode(node) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
export function detachBetween ( before, after ) {
|
||||
while ( before.nextSibling && before.nextSibling !== after ) {
|
||||
before.parentNode.removeChild( before.nextSibling );
|
||||
export function detachBetween(before, after) {
|
||||
while (before.nextSibling && before.nextSibling !== after) {
|
||||
before.parentNode.removeChild(before.nextSibling);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is out of date
|
||||
export function destroyEach ( iterations, detach, start ) {
|
||||
for ( var i = start; i < iterations.length; i += 1 ) {
|
||||
if ( iterations[i] ) iterations[i].destroy( detach );
|
||||
export function destroyEach(iterations, detach, start) {
|
||||
for (var i = start; i < iterations.length; i += 1) {
|
||||
if (iterations[i]) iterations[i].destroy(detach);
|
||||
}
|
||||
}
|
||||
|
||||
export function createElement ( name ) {
|
||||
return document.createElement( name );
|
||||
export function createElement(name) {
|
||||
return document.createElement(name);
|
||||
}
|
||||
|
||||
export function createSvgElement ( name ) {
|
||||
return document.createElementNS( 'http://www.w3.org/2000/svg', name );
|
||||
export function createSvgElement(name) {
|
||||
return document.createElementNS('http://www.w3.org/2000/svg', name);
|
||||
}
|
||||
|
||||
export function createText ( data ) {
|
||||
return document.createTextNode( data );
|
||||
export function createText(data) {
|
||||
return document.createTextNode(data);
|
||||
}
|
||||
|
||||
export function createComment () {
|
||||
return document.createComment( '' );
|
||||
export function createComment() {
|
||||
return document.createComment('');
|
||||
}
|
||||
|
||||
export function addEventListener ( node, event, handler ) {
|
||||
node.addEventListener( event, handler, false );
|
||||
export function addEventListener(node, event, handler) {
|
||||
node.addEventListener(event, handler, false);
|
||||
}
|
||||
|
||||
export function removeEventListener ( node, event, handler ) {
|
||||
node.removeEventListener( event, handler, false );
|
||||
export function removeEventListener(node, event, handler) {
|
||||
node.removeEventListener(event, handler, false);
|
||||
}
|
||||
|
||||
export function setAttribute ( node, attribute, value ) {
|
||||
node.setAttribute( attribute, value );
|
||||
export function setAttribute(node, attribute, value) {
|
||||
node.setAttribute(attribute, value);
|
||||
}
|
||||
|
||||
export function setXlinkAttribute ( node, attribute, value ) {
|
||||
node.setAttributeNS( 'http://www.w3.org/1999/xlink', attribute, value );
|
||||
export function setXlinkAttribute(node, attribute, value) {
|
||||
node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
|
||||
}
|
||||
|
||||
export function getBindingGroupValue ( group ) {
|
||||
export function getBindingGroupValue(group) {
|
||||
var value = [];
|
||||
for ( var i = 0; i < group.length; i += 1 ) {
|
||||
if ( group[i].checked ) value.push( group[i].__value );
|
||||
for (var i = 0; i < group.length; i += 1) {
|
||||
if (group[i].checked) value.push(group[i].__value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function toNumber ( value ) {
|
||||
export function toNumber(value) {
|
||||
return value === '' ? undefined : +value;
|
||||
}
|
||||
}
|
||||
|
@ -1,107 +1,103 @@
|
||||
import { walk } from 'estree-walker';
|
||||
import { Node } from '../interfaces';
|
||||
|
||||
export default function annotateWithScopes ( expression: Node ) {
|
||||
let scope = new Scope( null, false );
|
||||
|
||||
walk( expression, {
|
||||
enter ( node: Node ) {
|
||||
if ( /Function/.test( node.type ) ) {
|
||||
if ( node.type === 'FunctionDeclaration' ) {
|
||||
scope.declarations.add( node.id.name );
|
||||
export default function annotateWithScopes(expression: Node) {
|
||||
let scope = new Scope(null, false);
|
||||
|
||||
walk(expression, {
|
||||
enter(node: Node) {
|
||||
if (/Function/.test(node.type)) {
|
||||
if (node.type === 'FunctionDeclaration') {
|
||||
scope.declarations.add(node.id.name);
|
||||
} else {
|
||||
node._scope = scope = new Scope( scope, false );
|
||||
if ( node.id ) scope.declarations.add( node.id.name );
|
||||
node._scope = scope = new Scope(scope, false);
|
||||
if (node.id) scope.declarations.add(node.id.name);
|
||||
}
|
||||
|
||||
node.params.forEach( ( param: Node ) => {
|
||||
extractNames( param ).forEach( name => {
|
||||
scope.declarations.add( name );
|
||||
node.params.forEach((param: Node) => {
|
||||
extractNames(param).forEach(name => {
|
||||
scope.declarations.add(name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
else if ( /For(?:In|Of)Statement/.test( node.type ) ) {
|
||||
node._scope = scope = new Scope( scope, true );
|
||||
}
|
||||
|
||||
else if ( node.type === 'BlockStatement' ) {
|
||||
node._scope = scope = new Scope( scope, true );
|
||||
}
|
||||
|
||||
else if ( /(Function|Class|Variable)Declaration/.test( node.type ) ) {
|
||||
scope.addDeclaration( node );
|
||||
} else if (/For(?:In|Of)Statement/.test(node.type)) {
|
||||
node._scope = scope = new Scope(scope, true);
|
||||
} else if (node.type === 'BlockStatement') {
|
||||
node._scope = scope = new Scope(scope, true);
|
||||
} else if (/(Function|Class|Variable)Declaration/.test(node.type)) {
|
||||
scope.addDeclaration(node);
|
||||
}
|
||||
},
|
||||
|
||||
leave ( node: Node ) {
|
||||
if ( node._scope ) {
|
||||
leave(node: Node) {
|
||||
if (node._scope) {
|
||||
scope = scope.parent;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
class Scope {
|
||||
parent: Scope
|
||||
block: boolean
|
||||
declarations: Set<string>
|
||||
parent: Scope;
|
||||
block: boolean;
|
||||
declarations: Set<string>;
|
||||
|
||||
constructor ( parent: Scope, block: boolean ) {
|
||||
constructor(parent: Scope, block: boolean) {
|
||||
this.parent = parent;
|
||||
this.block = block;
|
||||
this.declarations = new Set();
|
||||
}
|
||||
|
||||
addDeclaration ( node: Node ) {
|
||||
if ( node.kind === 'var' && !this.block && this.parent ) {
|
||||
this.parent.addDeclaration( node );
|
||||
} else if ( node.type === 'VariableDeclaration' ) {
|
||||
node.declarations.forEach( ( declarator: Node ) => {
|
||||
extractNames( declarator.id ).forEach( name => {
|
||||
this.declarations.add( name );
|
||||
addDeclaration(node: Node) {
|
||||
if (node.kind === 'var' && !this.block && this.parent) {
|
||||
this.parent.addDeclaration(node);
|
||||
} else if (node.type === 'VariableDeclaration') {
|
||||
node.declarations.forEach((declarator: Node) => {
|
||||
extractNames(declarator.id).forEach(name => {
|
||||
this.declarations.add(name);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.declarations.add( node.id.name );
|
||||
this.declarations.add(node.id.name);
|
||||
}
|
||||
}
|
||||
|
||||
has ( name: string ) :boolean {
|
||||
return this.declarations.has( name ) || this.parent && this.parent.has( name );
|
||||
has(name: string): boolean {
|
||||
return (
|
||||
this.declarations.has(name) || (this.parent && this.parent.has(name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function extractNames ( param: Node ) {
|
||||
function extractNames(param: Node) {
|
||||
const names: string[] = [];
|
||||
extractors[ param.type ]( names, param );
|
||||
extractors[param.type](names, param);
|
||||
return names;
|
||||
}
|
||||
|
||||
const extractors = {
|
||||
Identifier ( names: string[], param: Node ) {
|
||||
names.push( param.name );
|
||||
Identifier(names: string[], param: Node) {
|
||||
names.push(param.name);
|
||||
},
|
||||
|
||||
ObjectPattern ( names: string[], param: Node ) {
|
||||
param.properties.forEach( ( prop: Node ) => {
|
||||
extractors[ prop.value.type ]( names, prop.value );
|
||||
ObjectPattern(names: string[], param: Node) {
|
||||
param.properties.forEach((prop: Node) => {
|
||||
extractors[prop.value.type](names, prop.value);
|
||||
});
|
||||
},
|
||||
|
||||
ArrayPattern ( names: string[], param: Node ) {
|
||||
param.elements.forEach( ( element: Node ) => {
|
||||
if ( element ) extractors[ element.type ]( names, element );
|
||||
ArrayPattern(names: string[], param: Node) {
|
||||
param.elements.forEach((element: Node) => {
|
||||
if (element) extractors[element.type](names, element);
|
||||
});
|
||||
},
|
||||
|
||||
RestElement ( names: string[], param: Node ) {
|
||||
extractors[ param.argument.type ]( names, param.argument );
|
||||
RestElement(names: string[], param: Node) {
|
||||
extractors[param.argument.type](names, param.argument);
|
||||
},
|
||||
|
||||
AssignmentPattern ( names: string[], param: Node ) {
|
||||
extractors[ param.left.type ]( names, param.left );
|
||||
}
|
||||
AssignmentPattern(names: string[], param: Node) {
|
||||
extractors[param.left.type](names, param.left);
|
||||
},
|
||||
};
|
||||
|
@ -1,40 +0,0 @@
|
||||
const start = /\n(\t+)/;
|
||||
|
||||
export default function deindent ( strings, ...values ) {
|
||||
const indentation = start.exec( strings[0] )[1];
|
||||
const pattern = new RegExp( `^${indentation}`, 'gm' );
|
||||
|
||||
let result = strings[0].replace( start, '' ).replace( pattern, '' );
|
||||
|
||||
let trailingIndentation = getTrailingIndentation( result );
|
||||
|
||||
for ( let i = 1; i < strings.length; i += 1 ) {
|
||||
let expression = values[ i - 1 ];
|
||||
const string = strings[i].replace( pattern, '' );
|
||||
|
||||
if ( Array.isArray( expression ) ) {
|
||||
expression = expression.length ? expression.join( '\n' ) : null;
|
||||
}
|
||||
|
||||
if ( expression || expression === '' ) {
|
||||
const value = String( expression ).replace( /\n/g, `\n${trailingIndentation}` );
|
||||
result += value + string;
|
||||
}
|
||||
|
||||
else {
|
||||
let c = result.length;
|
||||
while ( /\s/.test( result[ c - 1 ] ) ) c -= 1;
|
||||
result = result.slice( 0, c ) + string;
|
||||
}
|
||||
|
||||
trailingIndentation = getTrailingIndentation( result );
|
||||
}
|
||||
|
||||
return result.trim().replace( /\t+$/gm, '' );
|
||||
}
|
||||
|
||||
function getTrailingIndentation ( str ) {
|
||||
let i = str.length;
|
||||
while ( str[ i - 1 ] === ' ' || str[ i - 1 ] === '\t' ) i -= 1;
|
||||
return str.slice( i, str.length );
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
const start = /\n(\t+)/;
|
||||
|
||||
export default function deindent(strings: string[], ...values: any[]) {
|
||||
const indentation = start.exec(strings[0])[1];
|
||||
const pattern = new RegExp(`^${indentation}`, 'gm');
|
||||
|
||||
let result = strings[0].replace(start, '').replace(pattern, '');
|
||||
|
||||
let trailingIndentation = getTrailingIndentation(result);
|
||||
|
||||
for (let i = 1; i < strings.length; i += 1) {
|
||||
let expression = values[i - 1];
|
||||
const string = strings[i].replace(pattern, '');
|
||||
|
||||
if (Array.isArray(expression)) {
|
||||
expression = expression.length ? expression.join('\n') : null;
|
||||
}
|
||||
|
||||
if (expression || expression === '') {
|
||||
const value = String(expression).replace(
|
||||
/\n/g,
|
||||
`\n${trailingIndentation}`
|
||||
);
|
||||
result += value + string;
|
||||
} else {
|
||||
let c = result.length;
|
||||
while (/\s/.test(result[c - 1])) c -= 1;
|
||||
result = result.slice(0, c) + string;
|
||||
}
|
||||
|
||||
trailingIndentation = getTrailingIndentation(result);
|
||||
}
|
||||
|
||||
return result.trim().replace(/\t+$/gm, '');
|
||||
}
|
||||
|
||||
function getTrailingIndentation(str: string) {
|
||||
let i = str.length;
|
||||
while (str[i - 1] === ' ' || str[i - 1] === '\t') i -= 1;
|
||||
return str.slice(i, str.length);
|
||||
}
|
@ -1,21 +1,23 @@
|
||||
import { Node } from '../interfaces';
|
||||
|
||||
export default function flatten ( node: Node ) {
|
||||
export default function flatten(node: Node) {
|
||||
const parts = [];
|
||||
const propEnd = node.end;
|
||||
|
||||
while ( node.type === 'MemberExpression' ) {
|
||||
if ( node.computed ) return null;
|
||||
parts.unshift( node.property.name );
|
||||
while (node.type === 'MemberExpression') {
|
||||
if (node.computed) return null;
|
||||
parts.unshift(node.property.name);
|
||||
|
||||
node = node.object;
|
||||
}
|
||||
|
||||
const propStart = node.end;
|
||||
const name = node.type === 'Identifier' ? node.name : node.type === 'ThisExpression' ? 'this' : null;
|
||||
const name = node.type === 'Identifier'
|
||||
? node.name
|
||||
: node.type === 'ThisExpression' ? 'this' : null;
|
||||
|
||||
if ( !name ) return null;
|
||||
if (!name) return null;
|
||||
|
||||
parts.unshift( name );
|
||||
parts.unshift(name);
|
||||
return { name, parts, keypath: `${name}[✂${propStart}-${propEnd}✂]` };
|
||||
}
|
||||
|
@ -1,31 +1,36 @@
|
||||
import spaces from './spaces.js';
|
||||
import spaces from './spaces';
|
||||
|
||||
function tabsToSpaces ( str: string ) {
|
||||
return str.replace( /^\t+/, match => match.split( '\t' ).join( ' ' ) );
|
||||
function tabsToSpaces(str: string) {
|
||||
return str.replace(/^\t+/, match => match.split('\t').join(' '));
|
||||
}
|
||||
|
||||
export default function getCodeFrame ( source: string, line: number, column: number ) {
|
||||
const lines = source.split( '\n' );
|
||||
export default function getCodeFrame(
|
||||
source: string,
|
||||
line: number,
|
||||
column: number
|
||||
) {
|
||||
const lines = source.split('\n');
|
||||
|
||||
const frameStart = Math.max( 0, line - 2 );
|
||||
const frameEnd = Math.min( line + 3, lines.length );
|
||||
const frameStart = Math.max(0, line - 2);
|
||||
const frameEnd = Math.min(line + 3, lines.length);
|
||||
|
||||
const digits = String( frameEnd + 1 ).length;
|
||||
const digits = String(frameEnd + 1).length;
|
||||
|
||||
return lines
|
||||
.slice( frameStart, frameEnd )
|
||||
.map( ( str, i ) => {
|
||||
.slice(frameStart, frameEnd)
|
||||
.map((str, i) => {
|
||||
const isErrorLine = frameStart + i === line;
|
||||
|
||||
let lineNum = String( i + frameStart + 1 );
|
||||
while ( lineNum.length < digits ) lineNum = ` ${lineNum}`;
|
||||
let lineNum = String(i + frameStart + 1);
|
||||
while (lineNum.length < digits) lineNum = ` ${lineNum}`;
|
||||
|
||||
if ( isErrorLine ) {
|
||||
const indicator = spaces( digits + 2 + tabsToSpaces( str.slice( 0, column ) ).length ) + '^';
|
||||
return `${lineNum}: ${tabsToSpaces( str )}\n${indicator}`;
|
||||
if (isErrorLine) {
|
||||
const indicator =
|
||||
spaces(digits + 2 + tabsToSpaces(str.slice(0, column)).length) + '^';
|
||||
return `${lineNum}: ${tabsToSpaces(str)}\n${indicator}`;
|
||||
}
|
||||
|
||||
return `${lineNum}: ${tabsToSpaces( str )}`;
|
||||
return `${lineNum}: ${tabsToSpaces(str)}`;
|
||||
})
|
||||
.join( '\n' );
|
||||
.join('\n');
|
||||
}
|
||||
|
@ -1 +1,26 @@
|
||||
export default new Set( [ 'Array', 'Boolean', 'console', 'Date', 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'Infinity', 'Intl', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'parseFloat', 'parseInt', 'RegExp', 'Set', 'String', 'undefined' ] );
|
||||
export default new Set([
|
||||
'Array',
|
||||
'Boolean',
|
||||
'console',
|
||||
'Date',
|
||||
'decodeURI',
|
||||
'decodeURIComponent',
|
||||
'encodeURI',
|
||||
'encodeURIComponent',
|
||||
'Infinity',
|
||||
'Intl',
|
||||
'isFinite',
|
||||
'isNaN',
|
||||
'JSON',
|
||||
'Map',
|
||||
'Math',
|
||||
'NaN',
|
||||
'Number',
|
||||
'Object',
|
||||
'parseFloat',
|
||||
'parseInt',
|
||||
'RegExp',
|
||||
'Set',
|
||||
'String',
|
||||
'undefined',
|
||||
]);
|
||||
|
@ -1,5 +1,5 @@
|
||||
const voidElementNames = /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
|
||||
|
||||
export default function isVoidElementName ( name: string ) {
|
||||
return voidElementNames.test( name ) || name.toLowerCase() === '!doctype';
|
||||
export default function isVoidElementName(name: string) {
|
||||
return voidElementNames.test(name) || name.toLowerCase() === '!doctype';
|
||||
}
|
||||
|
@ -1,13 +1,23 @@
|
||||
export const html = 'http://www.w3.org/1999/xhtml';
|
||||
export const html = 'http://www.w3.org/1999/xhtml';
|
||||
export const mathml = 'http://www.w3.org/1998/Math/MathML';
|
||||
export const svg = 'http://www.w3.org/2000/svg';
|
||||
export const xlink = 'http://www.w3.org/1999/xlink';
|
||||
export const xml = 'http://www.w3.org/XML/1998/namespace';
|
||||
export const xmlns = 'http://www.w3.org/2000/xmlns';
|
||||
export const svg = 'http://www.w3.org/2000/svg';
|
||||
export const xlink = 'http://www.w3.org/1999/xlink';
|
||||
export const xml = 'http://www.w3.org/XML/1998/namespace';
|
||||
export const xmlns = 'http://www.w3.org/2000/xmlns';
|
||||
|
||||
export const validNamespaces = [
|
||||
'html', 'mathml', 'svg', 'xlink', 'xml', 'xmlns',
|
||||
html, mathml, svg, xlink, xml, xmlns
|
||||
'html',
|
||||
'mathml',
|
||||
'svg',
|
||||
'xlink',
|
||||
'xml',
|
||||
'xmlns',
|
||||
html,
|
||||
mathml,
|
||||
svg,
|
||||
xlink,
|
||||
xml,
|
||||
xmlns,
|
||||
];
|
||||
|
||||
export default { html, mathml, svg, xlink, xml, xmlns };
|
||||
|
@ -1,6 +1,55 @@
|
||||
const reservedNames = new Set( [ 'arguments', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield' ] );
|
||||
const reservedNames = new Set([
|
||||
'arguments',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'enum',
|
||||
'eval',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'implements',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'interface',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
]);
|
||||
|
||||
// prevent e.g. `{{#each states as state}}` breaking
|
||||
reservedNames.add( 'state' );
|
||||
reservedNames.add('state');
|
||||
|
||||
export default reservedNames;
|
||||
export default reservedNames;
|
||||
|
@ -1,5 +0,0 @@
|
||||
export default function spaces ( i ) {
|
||||
let result = '';
|
||||
while ( i-- ) result += ' ';
|
||||
return result;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export default function spaces(i: number) {
|
||||
let result = '';
|
||||
while (i--) result += ' ';
|
||||
return result;
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
import { whitespace } from './patterns';
|
||||
|
||||
export function trimStart ( str: string ) {
|
||||
export function trimStart(str: string) {
|
||||
let i = 0;
|
||||
while ( whitespace.test( str[i] ) ) i += 1;
|
||||
while (whitespace.test(str[i])) i += 1;
|
||||
|
||||
return str.slice( i );
|
||||
return str.slice(i);
|
||||
}
|
||||
|
||||
export function trimEnd ( str: string ) {
|
||||
export function trimEnd(str: string) {
|
||||
let i = str.length;
|
||||
while ( whitespace.test( str[ i - 1 ] ) ) i -= 1;
|
||||
while (whitespace.test(str[i - 1])) i -= 1;
|
||||
|
||||
return str.slice( 0, i );
|
||||
return str.slice(0, i);
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { Validator } from '../../';
|
||||
import { Node } from '../../../interfaces';
|
||||
|
||||
const disallowed = new Set([ 'Literal', 'ObjectExpression', 'ArrayExpression' ]);
|
||||
const disallowed = new Set(['Literal', 'ObjectExpression', 'ArrayExpression']);
|
||||
|
||||
export default function data ( validator: Validator, prop: Node ) {
|
||||
while ( prop.type === 'ParenthesizedExpression' ) prop = prop.expression;
|
||||
export default function data(validator: Validator, prop: Node) {
|
||||
while (prop.type === 'ParenthesizedExpression') prop = prop.expression;
|
||||
|
||||
// TODO should we disallow references and expressions as well?
|
||||
|
||||
if ( disallowed.has( prop.value.type ) ) {
|
||||
validator.error( `'data' must be a function`, prop.value.start );
|
||||
if (disallowed.has(prop.value.type)) {
|
||||
validator.error(`'data' must be a function`, prop.value.start);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue