├── .gitignore
├── .project
├── LICENSE
├── README.md
├── browser
├── switchcase.js
└── switchcase.min.js
├── examples
├── index.html
└── recursion.html
├── index.js
├── package.json
└── test
├── index.html
├── index.js
└── issue3.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | switchcase
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Simon Y. Blackwell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.codacy.com/app/syblackwell/switchcase?utm_source=github.com&utm_medium=referral&utm_content=anywhichway/switchcase&utm_campaign=Badge_Grade)
2 |
3 | # switchcase
4 |
5 | Declarative and functional switch supporting literals, functional tests, regular expressions, and object pattern matching.
6 |
7 | This module can be used to simplify code using `if then else` and `switch` statements.
8 |
9 | It can also be used as a [router](#router), e.g.
10 |
11 | ```javascript
12 | const router = switchcase()
13 | .route("/:id/:name",value => {
14 | console.log(logged = value);
15 | });
16 | router.handle({req:{url:"https://www.somesite.com/1/joe"},res:{}});
17 | ```
18 |
19 | Or, it can be used as a powerful object selection pattern matcher, e.g.
20 |
21 | ```javascript
22 | switchcase([
23 | {name:"joe",age:21,address:{city:"Seattle",zipcode:"98101"}},
24 | {name:"mary",age:20,address:{city:"Seattle",zipcode:"90101"}},
25 | {name:"joan",age:22,address:{city:"Bainbridge Island",zipcode:"98110"}]])
26 | .match({age:(value) => value >=21,address:{city:"Seattle"}});
27 | ```
28 |
29 | # Installation
30 |
31 | npm install switchcase
32 |
33 | For browsers, use the files in the `browser` directory.
34 |
35 | # API
36 |
37 | Let's start with an example:
38 |
39 | ```javascript
40 | let sw = switchcase({
41 | 1: "Case one, a literal",
42 | [(value) => value===2]: "Case two, a function",
43 | [/3/]: "Case three, a regular expression",
44 | [4]: () => "Case four, just demonstrating a functional value",
45 | default: "Defaulted"
46 | });
47 |
48 | console.log(sw(1));
49 | console.log(sw(2));
50 | console.log(sw(3));
51 | console.log(sw(4)()); // note the function invocation
52 | console.log(sw(5));
53 |
54 | ```
55 |
56 | ```javascript
57 | let sw = switchcase({},{call:true}) // if the switch values are functions, call them
58 | .case(1,() => console.log("Case one, a literal"))
59 | .case(value => value===2,() => console.log("Case two, a function"))
60 | .case(/3/, () => console.log("Case three, a regular expression"))
61 | .case(4, () => () => console.log("Case four, just demonstrating a functional value"))
62 | .default(() => console.log("Defaulted"));
63 |
64 | console.log(sw(1));
65 | console.log(sw(2));
66 | console.log(sw(3));
67 | console.log(sw(4)()); // note the function invocation
68 | console.log(sw(5));
69 |
70 | ```
71 |
72 | ```javascript
73 | let sw = switchcase({
74 | 1: () => console.log("Case one, a literal"),
75 | [(value) => value===2]: () => console.log("Case two, a function"),
76 | [/3/]: () => console.log("Case three, a regular expression"),
77 | [4]: () => () => console.log("Case four, just demonstrating a functional value"),
78 | default: () => console.log("Defaulted")
79 | },{call:true}); // if the switch values are functions, call them
80 |
81 | console.log(sw(1));
82 | console.log(sw(2));
83 | console.log(sw(3));
84 | console.log(sw(4)()); // note the function invocation
85 | console.log(sw(5));
86 |
87 | ```
88 |
89 |
90 | will all print
91 |
92 | ```
93 | Case one, a literal
94 |
95 | Case two, a function
96 |
97 | Case three, a regular expression
98 |
99 | Case four, just demonstrating a functional value
100 |
101 | Defaulted
102 | ```
103 |
104 | In short, calling `switchcase` with an object will return a functional switch. By using [ES2015 object initializer syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer), the properties of the object can look like they are functions or regular expressions.
105 |
106 | The returned function can also be enhanced through the use of chained calls:
107 |
108 | 1) `case(test,value)`, which adds a test and value.
109 |
110 | 2) `route(test,handler)`, which turns the switch into a router adds a test and handler.
111 |
112 | 3) `default(value)`, which sets the default result.
113 |
114 | 4) `otherwise(value)`, an alias for `default`.
115 |
116 |
117 | You can also optionaly use `sw.match(value)` or `sw.handle(value)` in place of direct invocation, `sw(value)`, if you think it assists with clarity.
118 |
119 | ## Optional Argument
120 |
121 | `switchcase` can take a second argument `switchcase(,)`.
122 |
123 | If the second argument is an options object it can have the properties `async`, `call`, `continuable`, `pathRouter`, and `strict`. If it is a boolean, it is used as the value of `strict`.
124 |
125 | If `async` is true, then the case tests and actions can be asynchronous and their return values will be awaited.
126 |
127 | If `call` is `true` and a property value is a function, it will be called with the switching value and the result returned. This can be very powerful when used with pattern matching destructuring. You do not need to set this if you explicity set `pathRouter` (see below) or call `route(test,handler)` on your switch.
128 |
129 | If `continuable` is `true`, `call` is set to `true` and any functions that return undefined cascade to the next case. This is useful for adding logging and similar capability for router like functionality. You do not need to set this if you explicity set `pathRouter` (see below) or call `route(test,handler)` on your switch.
130 |
131 | If `strict` is `true`, all property values in the case object that can be converted to integers will only match integers. Otherwise, a soft compare is done and "1" will equal 1.
132 |
133 | ```javascript
134 | const sw = switchcase({},{continuable:true})
135 | .case(()=>true,(value) => console.log(value))
136 | .case(1,value => value)
137 | .case(2,value => value * 2)
138 | ```
139 |
140 | `pathRouter` is used to turn a switchcase into a router. See the section on [routing](#router).
141 |
142 | With the exceptions of `continuable` and `pathRouter`, if you want to defer the resolution type, the same second argument can be provided to the invocation of a `switchcase` to override those provided when the switch was created, e.g.
143 |
144 | ```javascript
145 | const sw = switchcase({1:"case 1"});
146 |
147 | sw(1,{strict:true});
148 | ```
149 |
150 | ## Pattern Matching
151 |
152 | You can use partial objects in cases to match objects:
153 |
154 | ```javascript
155 | const sw = switchcase();
156 |
157 | sw.case({address: {city: "Seattle"}},({name}) => name);
158 |
159 | sw({name:"joe",address:{city: "Seattle"}},{call:true})); // returns "joe"
160 | ```
161 |
162 | If you want to use patterns with object based switching, you will need to stringify them, e.g.
163 |
164 |
165 | ```javascript
166 | const sw = switchcase({
167 | [JSON.stringify({address: {city: "Seattle"}})]: ({name}) => name
168 | });
169 |
170 | ```
171 |
172 | You might also want to explore the use of functional switches:
173 |
174 | ```javascript
175 | const sw = switchcase({
176 | [({address: {city}}) => city==="Seattle"]: ({name}) => name;
177 | });
178 | ```
179 |
180 | Or, functional matching:
181 |
182 | ```javascript
183 | const sw = switchcase();
184 |
185 | sw.case({address: {city: value => value==="Seattle"}},({name}) => name);
186 |
187 | sw({name:"joe",address:{city: "Seattle"}},{call:true,functionalMatch:true})); // returns "joe"
188 | ```
189 |
190 | And even, reverse functional matching:
191 |
192 | ```javascript
193 | const sw = switchcase();
194 |
195 | sw.case({address: {city: "Seattle"}},({name}) => name);
196 |
197 | sw({name:"joe",address:{city: value => value==="Seattle"}},{call:true,functionalMatch:true})); // returns "joe"
198 | ```
199 |
200 | Reverse functional matching is leveraged to support [object selection](#object-selection).
201 |
202 | Also, anywhere you use a function in functional switches or functional matching, you can use a regular expression.
203 |
204 |
205 |
206 | ## Object Selection
207 |
208 | If you pass in an iterable (usually Array) of values, then `switchcase` can be used to return the subset of values that match a pattern. The below will return the object with the name "joe".
209 |
210 | ```javascript
211 | switchcase([
212 | {name:"joe",age:21,address:{city:"Seattle",zipcode:"98101"}},
213 | {name:"mary",age:20,address:{city:"Seattle",zipcode:"90101"}},
214 | {name:"joan",age:22,address:{city:"Bainbridge Island",zipcode:"98110"}]])
215 | .match({age:(value) => value >=21,address:{city:"Seattle"}});
216 | ```
217 |
218 |
219 |
220 | ## Path Router
221 |
222 | You can use `switchcase` like a regular router if you create your switch with the `pathRouter` option or ever call `.route(test,handler)` instead of `case(test,value)`.
223 |
224 | `switchcase` will automatically handle requests in any of these forms:
225 |
226 | ```javascript
227 | {request:{path: },response:}
228 |
229 | {request:{location: },response:}
230 |
231 | {request:{url: },response:}
232 |
233 | {req:{path: },res:}
234 |
235 | {req:{location: },res:}
236 |
237 | {req:{url: },res:}
238 |
239 | {newURL: } // matches a hashchange browser event
240 |
241 | {path: },...}
242 |
243 | {location: },...}
244 |
245 | {url: },...}
246 |
247 | {path: },...}
248 |
249 | ```
250 |
251 | If a `pathRouter` configuration object is not passed in, then `request` objects or the `hashchange` events are automatically enhanced to have the properties shown below if they don't already exist and parameterized paths with colon delimiters are automatically handled, e.g. `/:id/:version/`. This maximizes similarity to routers from other libraries.
252 |
253 |
254 | ```javascript
255 | {
256 | path: ,
257 | pathname: ,
258 | location: [a URL object](https://developer.mozilla.org/en-US/docs/Web/API/URL), // if a url string existed
259 | url: // if location existed
260 | params: {:[,...} // if a parameterized path was matched
261 | }
262 | ```
263 |
264 | Alternatively, an `pathRouter` configuration object can be provided to look-up the path for matching and set parameters when a parameterized path is encountered:
265 |
266 | ```javascript
267 | {
268 | pathRouter:
269 | {
270 | route: object => object.URL.pathname
271 | setParams: (object,params) => object.args = params;
272 | }
273 | }
274 | ```
275 |
276 | When using `switchcase` as a router, any routed functions that return a value other than `undefined` are considered to have succeeded, routing stops and switchcase returns the value. If a routed function returns `undefined`, routing continues.
277 |
278 | ### Path Router Example
279 |
280 | ```javascript
281 | function bodyparser({req}) {
282 | ... ...
283 | return;
284 | }
285 |
286 | const sw = switchcase()
287 | .route(()=>true,bodyparser)
288 | .route("/:id/:name",({req,res}) => {
289 | console.log(req.params,req.path);
290 | return true;
291 | })
292 | .route("/login/",({req,res}) => {
293 | console.log(req.params,req.path);
294 | return true;
295 | })
296 | .default({req} => {
297 | console.log("Not Found",req.params,req.path);
298 | });
299 | sw.handle({req:{url:"https://www.somesite.com/1/joe"},res:{}});
300 | ```
301 |
302 |
303 | # Internals
304 |
305 | Some of the power of `switchcase` comes from the use of the ES2015 object initializer syntax, `[]`. This allows the use of the JavaScript interpreter to validate functions and regular expressions before they get turned into string property names. The `switchcase` function then loops through the properties in the object and converts them back to functions or regular expressions. The rest of the power comes from object destructuring.
306 |
307 | # Rational and Background
308 |
309 | There are a number of articles on the use of decalarative or functional approaches to the replacement of switch. In our opinion, here are three of the best:
310 |
311 | 1) July, 2014 [Replacing switch statements with object literals](https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/) ... plus commentary on [Reddit](http://www.reddit.com/r/javascript/comments/2b4s6r/deprecating_the_switch_statement_for_object).
312 |
313 | 2) Jan, 2017 [Rethinking JavaScript: Eliminate the switch statement for better code](https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d)
314 |
315 | 3) Nov, 2017 [Alternative to JavaScript’s switch statement with a functional twist](https://codeburst.io/alternative-to-javascripts-switch-statement-with-a-functional-twist-3f572787ba1c)
316 |
317 | We simply wanted a switch capability that could support literals, functional tests, regular expressions, and object patterns so that we could build super flexible routers.
318 |
319 | # Release History - Reverse Chronological Order
320 |
321 | 2019-02-13 v1.0.11 Addressed [issue 4](https://github.com/anywhichway/switchcase/issues/4)
322 |
323 | 2019-02-12 v1.0.10 Removed an extra await.
324 |
325 | 2019-02-12 v1.0.9 Added async support. See recursion.html example.
326 |
327 | 2019-02-11 v1.0.8 Patched incomplete example.
328 |
329 | 2019-02-11 v1.0.7 Tested switch recursion and added ability to pass args during recursion. See recursion.html example.
330 |
331 | 2019-02-09 v1.0.6 Corrected issue with pattern matched objects getting frozen.
332 |
333 | 2019-02-09 v1.0.5 Optimized switch creation for large arrays. Can also now pass in a true iterable.
334 |
335 | 2019-02-09 v1.0.4 Added object selection. Enhanced routing with some naming aliases/sugar.
336 |
337 | 2019-02-08 v1.0.3 Added array initialization of switch and the `pathRouter` option.
338 |
339 | 2019-02-06 v1.0.2 Added support for functional object patterns. The values in pattern properties can be functions or RegExp used to test the value passed into the switch. Alternatively, the values passed in to the switch can be used to reverse match to the switch.
340 |
341 | 2019-02-05 v1.0.1 Added support for object pattern matching, ability to call switch values, case continuation, and deferring the strict constraint to switch evaluation time.
342 |
343 | 2018-04-13 v1.0.0 Code style improvements. Fixed node.js unit test config.
344 |
345 | 2017-11-27 v0.0.3 Added unit tests
346 |
347 | 2017-11-27 v0.0.2 Added documentation
348 |
349 | 2017-11-27 v0.0.1 First public release
350 |
--------------------------------------------------------------------------------
/browser/switchcase.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o {
5 | const type = typeof(value);
6 | if(typeof(pattern)!=="object" && type!=="object") {
7 | if(value===pattern) { return true; }
8 | if(routing && type==="string" && typeof(pattern)==="string") {
9 | const pparts = pattern.split("/"),
10 | vparts = value.split("/");
11 | if(pparts[0]==="") { pparts.shift(); }
12 | if(vparts[0]==="") { vparts.shift(); }
13 | let part;
14 | if(pparts.length!==vparts.length) {
15 | return false;
16 | }
17 | while(pparts.length>0) {
18 | const ppart = pparts.shift(),
19 | vpart = vparts.shift();
20 | if(ppart[0]===":") {
21 | if(!routing.params) {
22 | routing.params = {};
23 | }
24 | let value = vpart;
25 | try {
26 | value = JSON.parse(value);
27 | } catch(e) {
28 | true;
29 | }
30 | routing.params[ppart.substring(1)] = value;
31 | } else if(vpart!==ppart) {
32 | return false;
33 | }
34 | }
35 | return true;
36 | }
37 | }
38 | if(pattern instanceof Date) {
39 | if(value instanceof Date) {
40 | return pattern.getTime()===value.getTime();
41 | }
42 | return false;
43 | }
44 | if(pattern instanceof RegExp) {
45 | if(["boolean","number","string"].includes(type)) {
46 | return pattern.test(value);
47 | }
48 | return false;
49 | }
50 | if(pattern instanceof Function) {
51 | return !!pattern(value);
52 | }
53 | return Object.keys(pattern).every((key) => {
54 | let pvalue = pattern[key],
55 | ptype = typeof(pvalue),
56 | test = (value) => value===pvalue;
57 | if(ptype==="undefined" || (pvalue && ptype==="object" && Object.keys(pvalue).length===0)) {
58 | return true;
59 | }
60 | if(key.startsWith("/")) {
61 | const i = key.lastIndexOf("/");
62 | if(i>0) {
63 | try {
64 | const regexp = new RegExp(key.substring(1,i),key.substring(i+1));
65 | test = (key) => regexp.test(key);
66 | } catch(e) {
67 | true;
68 | }
69 | }
70 | } else if(key.includes("=>")) {
71 | try {
72 | test = Function("return " + key)();
73 | } catch(e) {
74 | true;
75 | }
76 | }
77 | return Object.keys(value).every((vkey) => {
78 | let vtest = () => vkey===key;
79 | if(vkey.startsWith("/")) {
80 | const i = vkey.lastIndexOf("/");
81 | if(i>0) {
82 | try {
83 | const regexp = new RegExp(vkey.substring(1,i),vkey.substring(i+1));
84 | vtest = regexp.test(key);
85 | } catch(e) {
86 | true;
87 | }
88 | }
89 | } else if(vkey.includes("=>")) {
90 | try {
91 | vtest = Function("return " + vkey)();
92 | } catch(e) {
93 | true;
94 | }
95 | }
96 | if(test(vkey) || vtest()) {
97 | const vvalue = value[vkey],
98 | vtype = typeof(vvalue);
99 | if(functional && ptype==="function") {
100 | return pvalue(vvalue);
101 | }
102 | if(pvalue && ptype==="object" && vtype==="object") {
103 | return vvalue ? matches(vvalue,pvalue,functional) : false;
104 | }
105 | return pvalue===vvalue;
106 | }
107 | return true;
108 | });
109 | });
110 | },
111 | deepFreeze = (data) => {
112 | if(data && typeof(data)==="object") {
113 | Object.freeze(data);
114 | Object.keys(data).forEach((key) => deepFreeze(data[key]));
115 | }
116 | return data;
117 | },
118 | getPath = (object) => {
119 | if(object.path) {
120 | return object.path;
121 | }
122 | if(object.location && object.location.pathname) {
123 | return object.path = object.location.pathname;
124 | }
125 | if(object.url || object.URL || object.newURL) {
126 | if(!object.location) {
127 | object.location = new URL(object.url || object.URL || object.newURL);
128 | }
129 | if(object.location.pathname) {
130 | return object.path = object.location.pathname;
131 | }
132 | return object.path = new URL(object.url || object.URL || object.newURL).pathname;
133 | }
134 | },
135 | switchcase = (cases={},defaults={}) => {
136 | let switches = [];
137 | if(defaults && typeof(defaults)!=="object") {
138 | defaults = {strict:defaults};
139 | }
140 | if(cases!=null && typeof cases[Symbol.iterator] === "function") {
141 | switches = cases.slice();
142 | } else {
143 | Object.keys(cases).forEach((key) => {
144 | let test = key;
145 | try {
146 | test = Function("return " + key)();
147 | } catch(e) { true; }
148 | switches.push([test,cases[key]]);
149 | });
150 | }
151 | const switcher = Function("defaults","switches","matches","deepFreeze","getPath","switcher",
152 | `return ${defaults.async ? "async " : ""}(value,options={},...args) => {
153 | delete options.pathRouter;
154 | delete options.continuable;
155 | options = Object.assign({},defaults,options);
156 | if(options.pathRouter) { options.continuable = true; }
157 | if(options.continuable) { options.call = true; }
158 | let target = value,
159 | setParams;
160 | if(options.pathRouter) {
161 | const type = typeof(options.pathRouter.route);
162 | if(type==="function") {
163 | target = options.pathRouter.route(value);
164 | } else if(type==="string") {
165 | target = value[options.pathRouter.route];
166 | } else if(value.req) {
167 | target = getPath(value.req);
168 | } else if(value.request) {
169 | target = getPath(value.request);
170 | } else {
171 | target = getPath(value);
172 | }
173 | setParams = options.pathRouter.setParams;
174 | if(!setParams) {
175 | setParams = (value,params) => {
176 | if(value.req) {
177 | value.req.params = Object.assign({},value.req.params,params);
178 | } else if(value.request) {
179 | value.request.params = Object.assign({},value.request.params,params);
180 | } else {
181 | value.params = Object.assign({},value.params,params);
182 | }
183 | };
184 | }
185 | }
186 | const routing = options.pathRouter ? {} : null;
187 | let results; // for collecting items when using as an object matcher
188 | for(let item of switches) {
189 | const key = Array.isArray(item) ? item[0] : item,
190 | type = typeof(key);
191 | let pattern = key,
192 | result = item[1];
193 | if(key===item) { // swap target and pattern if using for object matching
194 | target = key;
195 | pattern = value;
196 | }
197 | if(key && key!==item && type==="object" && !Object.isFrozen(key)) { // doesn't check deep but good enough and fast
198 | deepFreeze(key);
199 | }
200 | if((key && (type==="object" || routing) && matches(target,pattern,result===undefined ? true : options.functionalMatch,routing))
201 | || (result!==undefined && type==="function" && ${defaults.async ? "await " : ""} key(target,...args))
202 | || (result!==undefined && options.strict && key===target)
203 | || (result!==undefined && !options.strict && key==target)) {
204 | if(result===undefined) { // case is an object to match
205 | if(!results) { results = []; }
206 | results.push(target);
207 | continue;
208 | }
209 | if(typeof(result)==="function" && options.call) {
210 | if(setParams && routing.params) {
211 | setParams(value,routing.params);
212 | }
213 | const resolved = ${defaults.async ? "await " : ""} result(value,...args);
214 | if(resolved!==undefined || !options.continuable) { return resolved; }
215 | if(options.continuable) { continue; }
216 | result = resolved;
217 | }
218 | return result;
219 | }
220 | }
221 | const result = options.call && typeof(switcher().otherwise)==="function" ? ${defaults.async ? "await " : ""} switcher().otherwise(value) : switcher().otherwise;
222 | return result === undefined ? results : result;
223 | }`
224 | )(defaults,switches,matches,deepFreeze,getPath,() => switcher);
225 | switcher.otherwise = cases.default;
226 | switcher.case = (test,value) => {
227 | switches.push([test,value]);
228 | return switcher;
229 | };
230 | switcher.route = (test,value) => {
231 | defaults.pathRouter = true;
232 | switches.push([test,value]);
233 | return switcher;
234 | };
235 | switcher.default = (value) => {
236 | switcher.otherwise = value;
237 | return switcher;
238 | };
239 | switcher.match = (value) => switcher(value);
240 | switcher.handle = switcher.match;
241 | return switcher;
242 | }
243 | if(typeof(module)!=="undefined") {
244 | module.exports = switchcase;
245 | }
246 | if(typeof(window)!=="undefined") {
247 | window.switchcase = switchcase;
248 | }
249 | }());
250 | },{}]},{},[1]);
251 |
--------------------------------------------------------------------------------
/browser/switchcase.min.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o{const type=typeof value;if(typeof pattern!=="object"&&type!=="object"){if(value===pattern){return true}if(routing&&type==="string"&&typeof pattern==="string"){const pparts=pattern.split("/"),vparts=value.split("/");if(pparts[0]===""){pparts.shift()}if(vparts[0]===""){vparts.shift()}let part;if(pparts.length!==vparts.length){return false}while(pparts.length>0){const ppart=pparts.shift(),vpart=vparts.shift();if(ppart[0]===":"){if(!routing.params){routing.params={}}let value=vpart;try{value=JSON.parse(value)}catch(e){true}routing.params[ppart.substring(1)]=value}else if(vpart!==ppart){return false}}return true}}if(pattern instanceof Date){if(value instanceof Date){return pattern.getTime()===value.getTime()}return false}if(pattern instanceof RegExp){if(["boolean","number","string"].includes(type)){return pattern.test(value)}return false}if(pattern instanceof Function){return!!pattern(value)}return Object.keys(pattern).every(key=>{let pvalue=pattern[key],ptype=typeof pvalue,test=value=>value===pvalue;if(ptype==="undefined"||pvalue&&ptype==="object"&&Object.keys(pvalue).length===0){return true}if(key.startsWith("/")){const i=key.lastIndexOf("/");if(i>0){try{const regexp=new RegExp(key.substring(1,i),key.substring(i+1));test=(key=>regexp.test(key))}catch(e){true}}}else if(key.includes("=>")){try{test=Function("return "+key)()}catch(e){true}}return Object.keys(value).every(vkey=>{let vtest=()=>vkey===key;if(vkey.startsWith("/")){const i=vkey.lastIndexOf("/");if(i>0){try{const regexp=new RegExp(vkey.substring(1,i),vkey.substring(i+1));vtest=regexp.test(key)}catch(e){true}}}else if(vkey.includes("=>")){try{vtest=Function("return "+vkey)()}catch(e){true}}if(test(vkey)||vtest()){const vvalue=value[vkey],vtype=typeof vvalue;if(functional&&ptype==="function"){return pvalue(vvalue)}if(pvalue&&ptype==="object"&&vtype==="object"){return vvalue?matches(vvalue,pvalue,functional):false}return pvalue===vvalue}return true})})},deepFreeze=data=>{if(data&&typeof data==="object"){Object.freeze(data);Object.keys(data).forEach(key=>deepFreeze(data[key]))}return data},getPath=object=>{if(object.path){return object.path}if(object.location&&object.location.pathname){return object.path=object.location.pathname}if(object.url||object.URL||object.newURL){if(!object.location){object.location=new URL(object.url||object.URL||object.newURL)}if(object.location.pathname){return object.path=object.location.pathname}return object.path=new URL(object.url||object.URL||object.newURL).pathname}},switchcase=(cases={},defaults={})=>{let switches=[];if(defaults&&typeof defaults!=="object"){defaults={strict:defaults}}if(cases!=null&&typeof cases[Symbol.iterator]==="function"){switches=cases.slice()}else{Object.keys(cases).forEach(key=>{let test=key;try{test=Function("return "+key)()}catch(e){true}switches.push([test,cases[key]])})}const switcher=Function("defaults","switches","matches","deepFreeze","getPath","switcher",`return ${defaults.async?"async ":""}(value,options={},...args) => {\n\tdelete options.pathRouter;\n\tdelete options.continuable;\n\toptions = Object.assign({},defaults,options);\n\tif(options.pathRouter) { options.continuable = true; }\n\tif(options.continuable) { options.call = true; }\n\tlet target = value,\n\t\tsetParams;\n\tif(options.pathRouter) {\n\t\tconst type = typeof(options.pathRouter.route);\n\t\tif(type==="function") {\n\t\t\ttarget = options.pathRouter.route(value);\n\t\t} else if(type==="string") {\n\t\t\ttarget = value[options.pathRouter.route];\n\t\t} else if(value.req) {\n\t\t\ttarget = getPath(value.req);\n\t\t} else if(value.request) {\n\t\t\ttarget = getPath(value.request);\n\t\t} else {\n\t\t\ttarget = getPath(value);\n\t\t}\n\t\tsetParams = options.pathRouter.setParams;\n\t\tif(!setParams) {\n\t\t\tsetParams = (value,params) => {\n\t\t\t\tif(value.req) {\n\t\t\t\t\tvalue.req.params = Object.assign({},value.req.params,params);\n\t\t\t\t} else if(value.request) {\n\t\t\t\t\tvalue.request.params = Object.assign({},value.request.params,params);\n\t\t\t\t} else {\n\t\t\t\t\tvalue.params = Object.assign({},value.params,params);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\tconst routing = options.pathRouter ? {} : null;\n\tlet results; // for collecting items when using as an object matcher\n\tfor(let item of switches) {\n\t\tconst key = Array.isArray(item) ? item[0] : item,\n\t\t\ttype = typeof(key);\n\t\tlet pattern = key,\n\t\t\tresult = item[1];\n\t\tif(key===item) { // swap target and pattern if using for object matching\n\t\t\ttarget = key;\n\t\t\tpattern = value;\n\t\t}\n\t\tif(key && key!==item && type==="object" && !Object.isFrozen(key)) { // doesn't check deep but good enough and fast\n\t\t\tdeepFreeze(key);\n\t\t}\n\t\tif((key && (type==="object" || routing) && matches(target,pattern,result===undefined ? true : options.functionalMatch,routing))\n\t\t\t\t|| (result!==undefined && type==="function" && ${defaults.async?"await ":""} key(target,...args))\n\t\t\t\t|| (result!==undefined && options.strict && key===target)\n\t\t\t\t|| (result!==undefined && !options.strict && key==target))\t{\n\t\t\tif(result===undefined) { // case is an object to match\n\t\t\t\tif(!results) { results = []; }\n\t\t\t\tresults.push(target);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(typeof(result)==="function" && options.call) {\n\t\t\t\tif(setParams && routing.params) {\n\t\t\t\t\tsetParams(value,routing.params);\n\t\t\t\t}\n\t\t\t\tconst resolved = ${defaults.async?"await ":""} result(value,...args);\n\t\t\t\tif(resolved!==undefined || !options.continuable) { return resolved; }\n\t\t\t\tif(options.continuable) { continue; }\n\t\t\t\tresult = resolved;\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\tconst result = options.call && typeof(switcher().otherwise)==="function" ? ${defaults.async?"await ":""} switcher().otherwise(value) : switcher().otherwise;\n\treturn result === undefined ? results : result;\n}`)(defaults,switches,matches,deepFreeze,getPath,()=>switcher);switcher.otherwise=cases.default;switcher.case=((test,value)=>{switches.push([test,value]);return switcher});switcher.route=((test,value)=>{defaults.pathRouter=true;switches.push([test,value]);return switcher});switcher.default=(value=>{switcher.otherwise=value;return switcher});switcher.match=(value=>switcher(value));switcher.handle=switcher.match;return switcher};if(typeof module!=="undefined"){module.exports=switchcase}if(typeof window!=="undefined"){window.switchcase=switchcase}})()},{}]},{},[1]);
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
34 |
35 |
--------------------------------------------------------------------------------
/examples/recursion.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
109 |
110 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | const matches = (value,pattern,functional,routing) => {
4 | const type = typeof(value);
5 | if(typeof(pattern)!=="object" && type!=="object") {
6 | if(value===pattern) { return true; }
7 | if(routing && type==="string" && typeof(pattern)==="string") {
8 | const pparts = pattern.split("/"),
9 | vparts = value.split("/");
10 | if(pparts[0]==="") { pparts.shift(); }
11 | if(vparts[0]==="") { vparts.shift(); }
12 | let part;
13 | if(pparts.length!==vparts.length) {
14 | return false;
15 | }
16 | while(pparts.length>0) {
17 | const ppart = pparts.shift(),
18 | vpart = vparts.shift();
19 | if(ppart[0]===":") {
20 | if(!routing.params) {
21 | routing.params = {};
22 | }
23 | let value = vpart;
24 | try {
25 | value = JSON.parse(value);
26 | } catch(e) {
27 | true;
28 | }
29 | routing.params[ppart.substring(1)] = value;
30 | } else if(vpart!==ppart) {
31 | return false;
32 | }
33 | }
34 | return true;
35 | }
36 | }
37 | if(pattern instanceof Date) {
38 | if(value instanceof Date) {
39 | return pattern.getTime()===value.getTime();
40 | }
41 | return false;
42 | }
43 | if(pattern instanceof RegExp) {
44 | if(["boolean","number","string"].includes(type)) {
45 | return pattern.test(value);
46 | }
47 | return false;
48 | }
49 | if(pattern instanceof Function) {
50 | return !!pattern(value);
51 | }
52 | return Object.keys(pattern).every((key) => {
53 | let pvalue = pattern[key],
54 | ptype = typeof(pvalue),
55 | test = (value) => value===pvalue;
56 | if(ptype==="undefined" || (pvalue && ptype==="object" && Object.keys(pvalue).length===0)) {
57 | return true;
58 | }
59 | if(key.startsWith("/")) {
60 | const i = key.lastIndexOf("/");
61 | if(i>0) {
62 | try {
63 | const regexp = new RegExp(key.substring(1,i),key.substring(i+1));
64 | test = (key) => regexp.test(key);
65 | } catch(e) {
66 | true;
67 | }
68 | }
69 | } else if(key.includes("=>")) {
70 | try {
71 | test = Function("return " + key)();
72 | } catch(e) {
73 | true;
74 | }
75 | }
76 | return Object.keys(value).every((vkey) => {
77 | let vtest = () => vkey===key;
78 | if(vkey.startsWith("/")) {
79 | const i = vkey.lastIndexOf("/");
80 | if(i>0) {
81 | try {
82 | const regexp = new RegExp(vkey.substring(1,i),vkey.substring(i+1));
83 | vtest = regexp.test(key);
84 | } catch(e) {
85 | true;
86 | }
87 | }
88 | } else if(vkey.includes("=>")) {
89 | try {
90 | vtest = Function("return " + vkey)();
91 | } catch(e) {
92 | true;
93 | }
94 | }
95 | if(test(vkey) || vtest()) {
96 | const vvalue = value[vkey],
97 | vtype = typeof(vvalue);
98 | if(functional && ptype==="function") {
99 | return pvalue(vvalue);
100 | }
101 | if(pvalue && ptype==="object" && vtype==="object") {
102 | return vvalue ? matches(vvalue,pvalue,functional) : false;
103 | }
104 | return pvalue===vvalue;
105 | }
106 | return true;
107 | });
108 | });
109 | },
110 | deepFreeze = (data) => {
111 | if(data && typeof(data)==="object") {
112 | Object.freeze(data);
113 | Object.keys(data).forEach((key) => deepFreeze(data[key]));
114 | }
115 | return data;
116 | },
117 | getPath = (object) => {
118 | if(object.path) {
119 | return object.path;
120 | }
121 | if(object.location && object.location.pathname) {
122 | return object.path = object.location.pathname;
123 | }
124 | if(object.url || object.URL || object.newURL) {
125 | if(!object.location) {
126 | object.location = new URL(object.url || object.URL || object.newURL);
127 | }
128 | if(object.location.pathname) {
129 | return object.path = object.location.pathname;
130 | }
131 | return object.path = new URL(object.url || object.URL || object.newURL).pathname;
132 | }
133 | },
134 | switchcase = (cases={},defaults={}) => {
135 | let switches = [];
136 | if(defaults && typeof(defaults)!=="object") {
137 | defaults = {strict:defaults};
138 | }
139 | if(cases!=null && typeof cases[Symbol.iterator] === "function") {
140 | switches = cases.slice();
141 | } else {
142 | Object.keys(cases).forEach((key) => {
143 | let test = key;
144 | try {
145 | test = Function("return " + key)();
146 | } catch(e) { true; }
147 | switches.push([test,cases[key]]);
148 | });
149 | }
150 | const switcher = Function("defaults","switches","matches","deepFreeze","getPath","switcher",
151 | `return ${defaults.async ? "async " : ""}(value,options={},...args) => {
152 | delete options.pathRouter;
153 | delete options.continuable;
154 | options = Object.assign({},defaults,options);
155 | if(options.pathRouter) { options.continuable = true; }
156 | if(options.continuable) { options.call = true; }
157 | let target = value,
158 | setParams;
159 | if(options.pathRouter) {
160 | const type = typeof(options.pathRouter.route);
161 | if(type==="function") {
162 | target = options.pathRouter.route(value);
163 | } else if(type==="string") {
164 | target = value[options.pathRouter.route];
165 | } else if(value.req) {
166 | target = getPath(value.req);
167 | } else if(value.request) {
168 | target = getPath(value.request);
169 | } else {
170 | target = getPath(value);
171 | }
172 | setParams = options.pathRouter.setParams;
173 | if(!setParams) {
174 | setParams = (value,params) => {
175 | if(value.req) {
176 | value.req.params = Object.assign({},value.req.params,params);
177 | } else if(value.request) {
178 | value.request.params = Object.assign({},value.request.params,params);
179 | } else {
180 | value.params = Object.assign({},value.params,params);
181 | }
182 | };
183 | }
184 | }
185 | const routing = options.pathRouter ? {} : null;
186 | let results; // for collecting items when using as an object matcher
187 | for(let item of switches) {
188 | const key = Array.isArray(item) ? item[0] : item,
189 | type = typeof(key);
190 | let pattern = key,
191 | result = item[1];
192 | if(key===item) { // swap target and pattern if using for object matching
193 | target = key;
194 | pattern = value;
195 | }
196 | if(key && key!==item && type==="object" && !Object.isFrozen(key)) { // doesn't check deep but good enough and fast
197 | deepFreeze(key);
198 | }
199 | if((key && (type==="object" || routing) && matches(target,pattern,result===undefined ? true : options.functionalMatch,routing))
200 | || (result!==undefined && type==="function" && ${defaults.async ? "await " : ""} key(target,...args))
201 | || (result!==undefined && options.strict && key===target)
202 | || (result!==undefined && !options.strict && key==target)) {
203 | if(result===undefined) { // case is an object to match
204 | if(!results) { results = []; }
205 | results.push(target);
206 | continue;
207 | }
208 | if(typeof(result)==="function" && options.call) {
209 | if(setParams && routing.params) {
210 | setParams(value,routing.params);
211 | }
212 | const resolved = ${defaults.async ? "await " : ""} result(value,...args);
213 | if(resolved!==undefined || !options.continuable) { return resolved; }
214 | if(options.continuable) { continue; }
215 | result = resolved;
216 | }
217 | return result;
218 | }
219 | }
220 | const result = options.call && typeof(switcher().otherwise)==="function" ? ${defaults.async ? "await " : ""} switcher().otherwise(value) : switcher().otherwise;
221 | return result === undefined ? results : result;
222 | }`
223 | )(defaults,switches,matches,deepFreeze,getPath,() => switcher);
224 | switcher.otherwise = cases.default;
225 | switcher.case = (test,value) => {
226 | switches.push([test,value]);
227 | return switcher;
228 | };
229 | switcher.route = (test,value) => {
230 | defaults.pathRouter = true;
231 | switches.push([test,value]);
232 | return switcher;
233 | };
234 | switcher.default = (value) => {
235 | switcher.otherwise = value;
236 | return switcher;
237 | };
238 | switcher.match = (value) => switcher(value);
239 | switcher.handle = switcher.match;
240 | return switcher;
241 | }
242 | if(typeof(module)!=="undefined") {
243 | module.exports = switchcase;
244 | }
245 | if(typeof(window)!=="undefined") {
246 | window.switchcase = switchcase;
247 | }
248 | }());
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "switchcase",
3 | "version": "1.0.11",
4 | "description": "Declarative and functional switch supporting literals, functional tests, regular expressions, object patterns and routes",
5 | "engines": {},
6 | "license": "MIT",
7 | "scripts": {
8 | "prepare": "browserify index.js -o browser/switchcase.js && uglifyjs browser/switchcase.js -o browser/switchcase.min.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/anywhichway/switchcase.git"
13 | },
14 | "keywords": [
15 | "switch",
16 | "case"
17 | ],
18 | "author": "Simon Y. Blackwell (http://www.github.com/anywhichway)",
19 | "bugs": {
20 | "url": "https://github.com/anywhichway/switchcase/issues"
21 | },
22 | "homepage": "https://github.com/anywhichway/switchcase#readme",
23 | "devDependencies": {
24 | "benchmark": "^2.1.3",
25 | "blanket": "^1.2.3",
26 | "chai": "^3.4.1",
27 | "codacy-coverage": "^2.0.0",
28 | "codeclimate-test-reporter": "^0.2.0",
29 | "istanbul": "^0.4.2",
30 | "mocha": "^2.3.4",
31 | "uglify-es": "^3.1.6"
32 | },
33 | "dependencies": {}
34 | }
35 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | var chai,
2 | expect,
3 | unionizor,
4 | _;
5 | if(typeof(window)==="undefined") {
6 | chai = require("chai");
7 | expect = chai.expect;
8 | switchcase = require("../index.js");
9 | }
10 |
11 | let sw1 = switchcase({
12 | 1: "Case one, a literal",
13 | [(value) => value===2]: "Case two, a function",
14 | [/3/]: "Case three, a regular expression",
15 | [JSON.stringify({address: {city: "Seattle"}})]: ({name}) => console.log(`Case four, object pattern matching, name is ${name}`),
16 | default: "Defaulted"
17 | });
18 |
19 | describe("Test",function() {
20 | it("literal",function() {
21 | let sw = switchcase({
22 | 1: "1"
23 | });
24 | expect(sw(1)).to.equal("1");
25 | });
26 | it("function",function() {
27 | let sw = switchcase({
28 | [(value) => value===1]: "1",
29 | });
30 | expect(sw(1)).to.equal("1");
31 | });
32 | it("RegExp",function() {
33 | let sw = switchcase({
34 | [/1/]: "1"
35 | });
36 | expect(sw(1)).to.equal("1");
37 | });
38 | it("pattern matching",function() {
39 | let sw = switchcase({
40 | [JSON.stringify({address: {city: "Seattle"}})]: ({name}) => name
41 | },{call:true});
42 | expect(sw({name:"joe",address:{city: "Seattle"}})).to.equal("joe");
43 | });
44 | it("pattern matching array init",function() {
45 | let sw = switchcase([
46 | [{address: {city: "Seattle"}},({name}) => name]
47 | ],{call:true});
48 | expect(sw({name:"joe",address:{city: "Seattle"}})).to.equal("joe");
49 | });
50 | it("default",function() {
51 | let sw = switchcase({
52 | [/1/]: "1",
53 | default: "1"
54 | });
55 | expect(sw(2)).to.equal("1");
56 | });
57 | it("case literal",function() {
58 | let sw = switchcase();
59 | sw.case(1,"1");
60 | expect(sw(1)).to.equal("1");
61 | });
62 | it("case pattern",function() {
63 | let sw = switchcase({});
64 | sw.case({address: {city: "Seattle"}},({name}) => name)
65 | expect(sw({name:"joe",address:{city: "Seattle"}},{call:true})).to.equal("joe");
66 | });
67 | it("case pattern functional",function() {
68 | let sw = switchcase({});
69 | sw.case({address: {city: value => value==="Seattle"}},({name}) => name)
70 | expect(sw({name:"joe",address:{city: "Seattle"}},{call:true,functionalMatch:true})).to.equal("joe");
71 | });
72 | it("case pattern RegExp",function() {
73 | let sw = switchcase({});
74 | sw.case({address: {city: /Seattle/g}},({name}) => name)
75 | expect(sw({name:"joe",address:{city: "Seattle"}},{call:true,functionalMatch:true})).to.equal("joe");
76 | });
77 | it("case pattern key test",function() {
78 | let sw = switchcase({});
79 | sw.case({address: {city: "Seattle"}},({name}) => name)
80 | expect(sw({name:"joe",[key => key==="address"]:{city: "Seattle"}},{call:true})).to.equal("joe");
81 | });
82 | it("case key test",function() {
83 | let sw = switchcase({});
84 | sw.case({[key => key==="address"]: {city: "Seattle"}},({name}) => name)
85 | expect(sw({name:"joe","address":{city: "Seattle"}},{call:true})).to.equal("joe");
86 | });
87 | it("case destructuring",function() {
88 | const sw = switchcase({
89 | [({address: {city}}) => city==="Seattle"]: ({name}) => name
90 | });
91 | expect(sw({name:"joe",address:{city: "Seattle"}},{call:true})).to.equal("joe");
92 | });
93 | it("case default",function() {
94 | let sw = switchcase({});
95 | sw.default("1");
96 | expect(sw(2)).to.equal("1");
97 | });
98 | it("continuation",function() {
99 | let logged,
100 | sw = switchcase({},{continuable:true});
101 | sw.case(()=>true,value => { logged=value; })
102 | .default("1");
103 | expect(sw(2)).to.equal("1");
104 | expect(logged).to.equal(2);
105 | });
106 | it("route",function() {
107 | let logged,
108 | router = switchcase()
109 | .route("/:id/:name",({req:{params:{id,name}}}) => {
110 | logged = id;
111 | console.log(`You requested the object with id ${id} and name ${name}`);
112 | });
113 | router.handle({req:{url:"https://www.somesite.com/1/joe"},res:{}});
114 | expect(logged!==undefined).equal(true);
115 | })
116 | it("route custom location",function() {
117 | let logged,
118 | router = switchcase({},{pathRouter:{route:object => new URL(object.req.url).pathname}})
119 | .route("/:id/:name",value => {
120 | console.log(logged = value);
121 | });
122 | router({req:{url:"https://www.somesite.com/1/joe"},res:{}});
123 | expect(logged!==undefined).equal(true);
124 | })
125 | it("object matching",function() {
126 | const objects = [
127 | {name:"joe",age:21,address:{city:"Seattle",zipcode:"98101"}},
128 | {name:"mary",age:20,address:{city:"Seattle",zipcode:"90101"}},
129 | {name:"joan",age:22,address:{city:"Bainbridge Island",zipcode:"98110"}}],
130 | matches = switchcase(objects).match({age:(value) => value >=21,address:{city:"Seattle"}});
131 | expect(Array.isArray(matches)).equal(true);
132 | expect(matches.length).equal(1);
133 | expect(matches[0]).equal(objects[0]);
134 | });
135 | });
--------------------------------------------------------------------------------
/test/issue3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
20 |
21 |
22 |
--------------------------------------------------------------------------------