Skip to content

Commit

Permalink
add legacy option, use it for setInputType (#773)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Aug 17, 2017
1 parent cd1e8c1 commit f7b8298
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/generators/dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class DomGenerator extends Generator {
metaBindings: string[];

hydratable: boolean;
legacy: boolean;

hasIntroTransitions: boolean;
hasOutroTransitions: boolean;
Expand All @@ -40,6 +41,7 @@ export class DomGenerator extends Generator {
this.readonly = new Set();

this.hydratable = options.hydratable;
this.legacy = options.legacy;
this.needsEncapsulateHelper = false;

// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
Expand Down
17 changes: 13 additions & 4 deletions src/generators/dom/visitors/Element/Attribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default function visitAttribute(
(attribute.value !== true && attribute.value.length > 1) ||
(attribute.value.length === 1 && attribute.value[0].type !== 'Text');

const isLegacyInputType = generator.legacy && name === 'type' && node.name === 'input';

if (isDynamic) {
let value;

Expand Down Expand Up @@ -108,7 +110,12 @@ export default function visitAttribute(
let updater;
const init = shouldCache ? `${last} = ${value}` : value;

if (isSelectValueAttribute) {
if (isLegacyInputType) {
block.builders.hydrate.addLine(
`@setInputType( ${state.parentNode}, ${init} );`
);
updater = `@setInputType( ${state.parentNode}, ${shouldCache ? last : value} );`;
} else if (isSelectValueAttribute) {
// annoying special case
const isMultipleSelect =
node.name === 'select' &&
Expand Down Expand Up @@ -178,9 +185,11 @@ export default function visitAttribute(
? `''`
: stringify(attribute.value[0].data);

const statement = propertyName
? `${state.parentNode}.${propertyName} = ${value};`
: `${method}( ${state.parentNode}, '${name}', ${value} );`;
const statement = (
isLegacyInputType ? `@setInputType( ${state.parentNode}, ${value} );` :
propertyName ? `${state.parentNode}.${propertyName} = ${value};` :
`${method}( ${state.parentNode}, '${name}', ${value} );`
);

block.builders.hydrate.addLine(statement);

Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface CompileOptions {
shared?: boolean | string;
cascade?: boolean;
hydratable?: boolean;
legacy?: boolean;

onerror?: (error: Error) => void;
onwarn?: (warning: Warning) => void;
Expand Down
6 changes: 6 additions & 0 deletions src/shared/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,10 @@ export function claimText (nodes, data) {
}

return createText(data);
}

export function setInputType(input, type) {
try {
input.type = type;
} catch (e) {}
}
21 changes: 13 additions & 8 deletions test/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from "assert";
import * as fs from "fs";
import * as path from "path";
import { rollup } from "rollup";
import { svelte } from "../helpers.js";
import { loadConfig, svelte } from "../helpers.js";

describe("js", () => {
fs.readdirSync("test/js/samples").forEach(dir => {
Expand All @@ -17,27 +17,26 @@ describe("js", () => {

(solo ? it.only : it)(dir, () => {
dir = path.resolve("test/js/samples", dir);
const config = loadConfig(`${dir}/_config.js`);

const input = fs
.readFileSync(`${dir}/input.html`, "utf-8")
.replace(/\s+$/, "");

let actual;

try {
actual = svelte.compile(input, {
const options = Object.assign(config.options || {}, {
shared: true
}).code;
});

actual = svelte.compile(input, options).code;
} catch (err) {
console.log(err.frame);
throw err;
}

fs.writeFileSync(`${dir}/_actual.js`, actual);
const expected = fs.readFileSync(`${dir}/expected.js`, "utf-8");
const expectedBundle = fs.readFileSync(
`${dir}/expected-bundle.js`,
"utf-8"
);

return rollup({
entry: `${dir}/_actual.js`,
Expand All @@ -56,6 +55,12 @@ describe("js", () => {
}).then(({ code }) => {
fs.writeFileSync(`${dir}/_actual-bundle.js`, code);

const expected = fs.readFileSync(`${dir}/expected.js`, "utf-8");
const expectedBundle = fs.readFileSync(
`${dir}/expected-bundle.js`,
"utf-8"
);

assert.equal(
actual.trim().replace(/^\s+$/gm, ""),
expected.trim().replace(/^\s+$/gm, "")
Expand Down
5 changes: 5 additions & 0 deletions test/js/samples/legacy-input-type/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
options: {
legacy: true
}
};
213 changes: 213 additions & 0 deletions test/js/samples/legacy-input-type/expected-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
function noop() {}

function assign(target) {
var k,
source,
i = 1,
len = arguments.length;
for (; i < len; i++) {
source = arguments[i];
for (k in source) target[k] = source[k];
}

return target;
}

function insertNode(node, target, anchor) {
target.insertBefore(node, anchor);
}

function detachNode(node) {
node.parentNode.removeChild(node);
}

function createElement(name) {
return document.createElement(name);
}

function setInputType(input, type) {
try {
input.type = type;
} catch (e) {}
}

function destroy(detach) {
this.destroy = this.set = this.get = noop;
this.fire('destroy');

if (detach !== false) this._fragment.unmount();
this._fragment.destroy();
this._fragment = this._state = null;
}

function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}

function dispatchObservers(component, group, changed, newState, oldState) {
for (var key in group) {
if (!changed[key]) continue;

var newValue = newState[key];
var oldValue = oldState[key];

var callbacks = group[key];
if (!callbacks) continue;

for (var i = 0; i < callbacks.length; i += 1) {
var callback = callbacks[i];
if (callback.__calling) continue;

callback.__calling = true;
callback.call(component, newValue, oldValue);
callback.__calling = false;
}
}
}

function get(key) {
return key ? this._state[key] : this._state;
}

function fire(eventName, data) {
var handlers =
eventName in this._handlers && this._handlers[eventName].slice();
if (!handlers) return;

for (var i = 0; i < handlers.length; i += 1) {
handlers[i].call(this, data);
}
}

function observe(key, callback, options) {
var group = options && options.defer
? this._observers.post
: this._observers.pre;

(group[key] || (group[key] = [])).push(callback);

if (!options || options.init !== false) {
callback.__calling = true;
callback.call(this, this._state[key]);
callback.__calling = false;
}

return {
cancel: function() {
var index = group[key].indexOf(callback);
if (~index) group[key].splice(index, 1);
}
};
}

function on(eventName, handler) {
if (eventName === 'teardown') return this.on('destroy', handler);

var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
handlers.push(handler);

return {
cancel: function() {
var index = handlers.indexOf(handler);
if (~index) handlers.splice(index, 1);
}
};
}

function set(newState) {
this._set(assign({}, newState));
if (this._root._lock) return;
this._root._lock = true;
callAll(this._root._beforecreate);
callAll(this._root._oncreate);
callAll(this._root._aftercreate);
this._root._lock = false;
}

function _set(newState) {
var oldState = this._state,
changed = {},
dirty = false;

for (var key in newState) {
if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
}
if (!dirty) return;

this._state = assign({}, oldState, newState);
this._recompute(changed, this._state, oldState, false);
if (this._bind) this._bind(changed, this._state);
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
this._fragment.update(changed, this._state);
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
}

function callAll(fns) {
while (fns && fns.length) fns.pop()();
}

var proto = {
destroy: destroy,
get: get,
fire: fire,
observe: observe,
on: on,
set: set,
teardown: destroy,
_recompute: noop,
_set: _set
};

function create_main_fragment ( state, component ) {
var input;

return {
create: function () {
input = createElement( 'input' );
this.hydrate();
},

hydrate: function ( nodes ) {
setInputType( input, "search" );
},

mount: function ( target, anchor ) {
insertNode( input, target, anchor );
},

update: noop,

unmount: function () {
detachNode( input );
},

destroy: noop
};
}

function SvelteComponent ( options ) {
options = options || {};
this._state = options.data || {};

this._observers = {
pre: Object.create( null ),
post: Object.create( null )
};

this._handlers = Object.create( null );

this._root = options._root || this;
this._yield = options._yield;
this._bind = options._bind;

this._fragment = create_main_fragment( this._state, this );

if ( options.target ) {
this._fragment.create();
this._fragment.mount( options.target, null );
}
}

assign( SvelteComponent.prototype, proto );

export default SvelteComponent;
Loading

0 comments on commit f7b8298

Please sign in to comment.