Skip to content

Commit 59d01f7

Browse files
committed
Added setup function which can supply static methods/properties. Closes sveltejs#480. Closes sveltejs#578.
1 parent 4874dc1 commit 59d01f7

File tree

6 files changed

+296
-0
lines changed

6 files changed

+296
-0
lines changed

src/generators/dom/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ export default function dom(
275275
this._state = {};
276276
this._destroyed = true;
277277
};
278+
279+
${templateProperties.setup && `@template.setup.call( ${name} );`}
278280
`);
279281

280282
const usedHelpers = new Set();

src/validate/js/propValidators/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import components from './components';
1010
import events from './events';
1111
import namespace from './namespace';
1212
import transitions from './transitions';
13+
import setup from './setup';
1314

1415
export default {
1516
data,
@@ -24,4 +25,5 @@ export default {
2425
events,
2526
namespace,
2627
transitions,
28+
setup,
2729
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import usesThisOrArguments from '../utils/usesThisOrArguments';
2+
import { Validator } from '../../';
3+
import { Node } from '../../../interfaces';
4+
5+
export default function setup(validator: Validator, prop: Node) {
6+
if (prop.value.type === 'ArrowFunctionExpression') {
7+
if (usesThisOrArguments(prop.value.body)) {
8+
validator.error(
9+
`'setup' should be a function expression, not an arrow function expression`,
10+
prop.start
11+
);
12+
}
13+
}
14+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
function noop() {}
2+
3+
function assign(target) {
4+
var k,
5+
source,
6+
i = 1,
7+
len = arguments.length;
8+
for (; i < len; i++) {
9+
source = arguments[i];
10+
for (k in source) target[k] = source[k];
11+
}
12+
13+
return target;
14+
}
15+
16+
function differs(a, b) {
17+
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
18+
}
19+
20+
function dispatchObservers(component, group, newState, oldState) {
21+
for (var key in group) {
22+
if (!(key in newState)) continue;
23+
24+
var newValue = newState[key];
25+
var oldValue = oldState[key];
26+
27+
if (differs(newValue, oldValue)) {
28+
var callbacks = group[key];
29+
if (!callbacks) continue;
30+
31+
for (var i = 0; i < callbacks.length; i += 1) {
32+
var callback = callbacks[i];
33+
if (callback.__calling) continue;
34+
35+
callback.__calling = true;
36+
callback.call(component, newValue, oldValue);
37+
callback.__calling = false;
38+
}
39+
}
40+
}
41+
}
42+
43+
function get(key) {
44+
return key ? this._state[key] : this._state;
45+
}
46+
47+
function fire(eventName, data) {
48+
var handlers =
49+
eventName in this._handlers && this._handlers[eventName].slice();
50+
if (!handlers) return;
51+
52+
for (var i = 0; i < handlers.length; i += 1) {
53+
handlers[i].call(this, data);
54+
}
55+
}
56+
57+
function observe(key, callback, options) {
58+
var group = options && options.defer
59+
? this._observers.post
60+
: this._observers.pre;
61+
62+
(group[key] || (group[key] = [])).push(callback);
63+
64+
if (!options || options.init !== false) {
65+
callback.__calling = true;
66+
callback.call(this, this._state[key]);
67+
callback.__calling = false;
68+
}
69+
70+
return {
71+
cancel: function() {
72+
var index = group[key].indexOf(callback);
73+
if (~index) group[key].splice(index, 1);
74+
}
75+
};
76+
}
77+
78+
function on(eventName, handler) {
79+
if (eventName === 'teardown') return this.on('destroy', handler);
80+
81+
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
82+
handlers.push(handler);
83+
84+
return {
85+
cancel: function() {
86+
var index = handlers.indexOf(handler);
87+
if (~index) handlers.splice(index, 1);
88+
}
89+
};
90+
}
91+
92+
function set(newState) {
93+
this._set(assign({}, newState));
94+
if (this._root._lock) return;
95+
this._root._lock = true;
96+
callAll(this._root._beforecreate);
97+
callAll(this._root._oncreate);
98+
callAll(this._root._aftercreate);
99+
this._root._lock = false;
100+
}
101+
102+
function callAll(fns) {
103+
while (fns && fns.length) fns.pop()();
104+
}
105+
106+
var proto = {
107+
get: get,
108+
fire: fire,
109+
observe: observe,
110+
on: on,
111+
set: set
112+
};
113+
114+
var template = (function () {
115+
return {
116+
methods: {
117+
foo ( bar ) {
118+
console.log( bar );
119+
}
120+
},
121+
setup () {
122+
this.SOME_CONSTANT = 42;
123+
this.prototype.foo( 'baz' );
124+
}
125+
};
126+
}());
127+
128+
function create_main_fragment ( state, component ) {
129+
130+
return {
131+
create: noop,
132+
133+
mount: noop,
134+
135+
unmount: noop,
136+
137+
destroy: noop
138+
};
139+
}
140+
141+
function SvelteComponent ( options ) {
142+
options = options || {};
143+
this._state = options.data || {};
144+
145+
this._observers = {
146+
pre: Object.create( null ),
147+
post: Object.create( null )
148+
};
149+
150+
this._handlers = Object.create( null );
151+
152+
this._root = options._root || this;
153+
this._yield = options._yield;
154+
155+
this._destroyed = false;
156+
157+
this._fragment = create_main_fragment( this._state, this );
158+
159+
if ( options.target ) {
160+
this._fragment.create();
161+
this._fragment.mount( options.target, null );
162+
}
163+
}
164+
165+
assign( SvelteComponent.prototype, template.methods, proto );
166+
167+
SvelteComponent.prototype._set = function _set ( newState ) {
168+
var oldState = this._state;
169+
this._state = assign( {}, oldState, newState );
170+
dispatchObservers( this, this._observers.pre, newState, oldState );
171+
dispatchObservers( this, this._observers.post, newState, oldState );
172+
};
173+
174+
SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = function destroy ( detach ) {
175+
if ( this._destroyed ) return;
176+
this.fire( 'destroy' );
177+
178+
if ( detach !== false ) this._fragment.unmount();
179+
this._fragment.destroy();
180+
this._fragment = null;
181+
182+
this._state = {};
183+
this._destroyed = true;
184+
};
185+
186+
template.setup.call( SvelteComponent );
187+
188+
export default SvelteComponent;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { assign, dispatchObservers, noop, proto } from "svelte/shared.js";
2+
3+
var template = (function () {
4+
return {
5+
methods: {
6+
foo ( bar ) {
7+
console.log( bar );
8+
}
9+
},
10+
setup () {
11+
this.SOME_CONSTANT = 42;
12+
this.prototype.foo( 'baz' );
13+
}
14+
};
15+
}());
16+
17+
function create_main_fragment ( state, component ) {
18+
19+
return {
20+
create: noop,
21+
22+
mount: noop,
23+
24+
unmount: noop,
25+
26+
destroy: noop
27+
};
28+
}
29+
30+
function SvelteComponent ( options ) {
31+
options = options || {};
32+
this._state = options.data || {};
33+
34+
this._observers = {
35+
pre: Object.create( null ),
36+
post: Object.create( null )
37+
};
38+
39+
this._handlers = Object.create( null );
40+
41+
this._root = options._root || this;
42+
this._yield = options._yield;
43+
44+
this._destroyed = false;
45+
46+
this._fragment = create_main_fragment( this._state, this );
47+
48+
if ( options.target ) {
49+
this._fragment.create();
50+
this._fragment.mount( options.target, null );
51+
}
52+
}
53+
54+
assign( SvelteComponent.prototype, template.methods, proto );
55+
56+
SvelteComponent.prototype._set = function _set ( newState ) {
57+
var oldState = this._state;
58+
this._state = assign( {}, oldState, newState );
59+
dispatchObservers( this, this._observers.pre, newState, oldState );
60+
dispatchObservers( this, this._observers.post, newState, oldState );
61+
};
62+
63+
SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = function destroy ( detach ) {
64+
if ( this._destroyed ) return;
65+
this.fire( 'destroy' );
66+
67+
if ( detach !== false ) this._fragment.unmount();
68+
this._fragment.destroy();
69+
this._fragment = null;
70+
71+
this._state = {};
72+
this._destroyed = true;
73+
};
74+
75+
template.setup.call( SvelteComponent );
76+
77+
export default SvelteComponent;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
export default {
3+
methods: {
4+
foo ( bar ) {
5+
console.log( bar );
6+
}
7+
},
8+
setup () {
9+
this.SOME_CONSTANT = 42;
10+
this.prototype.foo( 'baz' );
11+
}
12+
};
13+
</script>

0 commit comments

Comments
 (0)