├── .dockerignore ├── .gitignore ├── README.md ├── __tests__ ├── utils_test.re └── web │ └── components │ ├── header_test.re │ └── jsSnippet_test.re ├── bsconfig.json ├── dockerfile ├── graphql_schema.json ├── package.json ├── snippets ├── SyntaxPatternMatchWithoutSwitch.re ├── listMap.re ├── listReduce.re ├── operator__and.re ├── operator__applicationOperator.re ├── operator__comparisonGreaterThan.re ├── operator__comparisonGreaterThanOrEqualTo.re ├── operator__comparisonLessOrEqualTo.re ├── operator__comparisonLessThan.re ├── operator__equals.re ├── operator__exponentiation.re ├── operator__floatAddition.re ├── operator__floatDivision.re ├── operator__floatMultiplication.re ├── operator__floatSubtraction.re ├── operator__integerAddition.re ├── operator__integerDivision.re ├── operator__integerMultiplication.re ├── operator__integerSubtraction.re ├── operator__listConcatination.re ├── operator__logicalAnd.re ├── operator__logicalOr.re ├── operator__logicalXor.re ├── operator__modulus.re ├── operator__notEquals.re ├── operator__pipe.re ├── operator__referenceAssignment.re ├── operator__referenceContent.re ├── operator__shiftLeft.re ├── operator__shiftRight.re └── operator__stringConcatination.re ├── src ├── bindings │ ├── apollo.re │ └── apolloLinkSchema.re ├── config.re ├── index.re ├── mocks │ └── apolloMock.re ├── server │ ├── apolloServer.re │ ├── graphql.re │ └── snippet.re ├── utils.re └── web │ ├── apolloBrowser.re │ ├── app.re │ ├── browser.re │ ├── components │ ├── apolloContext.re │ ├── header.re │ ├── jsSnippet.re │ ├── query.re │ ├── search.re │ ├── snippetItem.re │ └── snippetList.re │ ├── elements │ ├── background.re │ ├── card.re │ ├── code.re │ ├── divider.re │ ├── h1.re │ ├── h2.re │ ├── h3.re │ ├── h4.re │ ├── loadingAnimation.re │ ├── p.re │ └── pageFrame.re │ ├── template.re │ └── theme.re ├── tasks.json ├── webpack.config.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .merlin 3 | .bsb.lock 4 | npm-debug.log 5 | /node_modules/ 6 | 7 | # Build assets 8 | /lib/* 9 | /build/* 10 | /dist/* 11 | 12 | # My pesky vim 13 | *.swp 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .merlin 3 | .bsb.lock 4 | npm-debug.log 5 | /node_modules/ 6 | 7 | # Build assets 8 | /lib/* 9 | /build/* 10 | /dist/* 11 | 12 | # My pesky vim 13 | *.swp 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic Reason Template 2 | 3 | Hello! This project allows you to quickly get started with Reason and BuckleScript. If you wanted a more sophisticated version, try the `react` template (`bsb -theme react -init .`). 4 | 5 | # Build 6 | ``` 7 | npm run build 8 | ``` 9 | 10 | # Build + Watch 11 | 12 | ``` 13 | npm run watch 14 | ``` 15 | 16 | 17 | # Editor 18 | If you use `vscode`, Press `Windows + Shift + B` it will build automatically 19 | -------------------------------------------------------------------------------- /__tests__/utils_test.re: -------------------------------------------------------------------------------- 1 | open Jest; 2 | 3 | open Utils; 4 | 5 | /* Just simple test incase if I accidentally change these functions */ 6 | describe("ele_of_ functions", () => { 7 | open Expect; 8 | test("str", () => 9 | ele_of_str("test") |> expect |> toEqual(ReasonReact.stringToElement("test")) 10 | ); 11 | test("arr", () => { 12 | let arr = [|
|]; 13 | ele_of_arr(arr) |> expect |> toEqual(ReasonReact.arrayToElement(arr)); 14 | }); 15 | test("list", () => { 16 | let l = [
, ,
]; 17 | ele_of_list(l) |> expect |> toEqual(ReasonReact.arrayToElement(Array.of_list(l))); 18 | }); 19 | }); 20 | 21 | describe("Debounce call", () => { 22 | open Expect; 23 | let count = ref(0); 24 | beforeEachAsync( 25 | ~timeout=10, 26 | finish => { 27 | let f = () => { 28 | count := count^ + 1; 29 | finish(); 30 | }; 31 | let dF = Debounce.make(f); 32 | Debounce.call((), dF); 33 | Debounce.call((), dF); 34 | Debounce.call((), dF); 35 | Debounce.call((), dF); 36 | Debounce.call((), dF); 37 | (); 38 | } 39 | ); 40 | test("First group of calls", () => 41 | count^ |> expect |> toBe(1) 42 | ); 43 | test("Second group of calls", () => 44 | count^ |> expect |> toBe(2) 45 | ); 46 | test("Third group of calls", () => 47 | count^ |> expect |> toBe(3) 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /__tests__/web/components/header_test.re: -------------------------------------------------------------------------------- 1 | open Jest; 2 | 3 | Enzyme.configureEnzyme(Enzyme.react_16_adapter()); 4 | 5 | describe("Header", () => { 6 | open Expect; 7 | test("component renders", () => { 8 | Enzyme.shallow(
) |> Enzyme.exists |> expect |> toBe(true); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /__tests__/web/components/jsSnippet_test.re: -------------------------------------------------------------------------------- 1 | open Jest; 2 | 3 | Enzyme.configureEnzyme(Enzyme.react_16_adapter()); 4 | 5 | /* Todo: I need to figure out a way to mock more behaviour in the system */ 6 | describe("JsSnippet", () => { 7 | open Expect; 8 | test("renderLoading renders", () => 9 |
(JsSnippet.renderLoading())
10 | |> Enzyme.shallow 11 | |> Enzyme.exists 12 | |> expect 13 | |> toBe(true) 14 | ); 15 | test("renderFailed renders", () => 16 |
(JsSnippet.renderFailed())
17 | |> Enzyme.shallow 18 | |> Enzyme.exists 19 | |> expect 20 | |> toBe(true) 21 | ); 22 | test("renderLoaded renders", () => 23 | JsSnippet.renderLoaded("test") 24 | |> Enzyme.shallow 25 | |> Enzyme.exists 26 | |> expect 27 | |> toBe(true) 28 | ); 29 | test("renders with mock", () => 30 | 31 | |> Enzyme.shallow 32 | |> Enzyme.exists 33 | |> expect 34 | |> toBe(true) 35 | ); 36 | }); 37 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "__tests__", 11 | "subdirs": true, 12 | "type": "dev" 13 | }, 14 | "snippets" 15 | ], 16 | "package-specs": { 17 | "module": "commonjs" 18 | }, 19 | "suffix": ".bs.js", 20 | "bs-dependencies": [ 21 | "reason-react-context", 22 | "bs-algolia", 23 | "bs-express", 24 | "bs-graphql", 25 | "bs-graphql-tools", 26 | "bs-apollo-server-express", 27 | "reason-react", 28 | "reason-apollo", 29 | "bs-nice", 30 | "bs-nice-components" 31 | ], 32 | "bs-dev-dependencies": ["@glennsl/bs-jest", "bs-enzyme"], 33 | "reason": { 34 | "react-jsx": 2 35 | }, 36 | "ppx-flags": ["graphql_ppx/ppx"], 37 | "namespace": true, 38 | "refmt": 3 39 | } 40 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:9 2 | 3 | ADD package.json /app/package.json 4 | WORKDIR /app 5 | RUN yarn install 6 | 7 | ADD . /app 8 | RUN yarn build 9 | EXPOSE 3000 10 | 11 | CMD ["yarn", "start-server"] 12 | -------------------------------------------------------------------------------- /graphql_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "__schema": { 4 | "queryType": { 5 | "name": "Query" 6 | }, 7 | "mutationType": null, 8 | "subscriptionType": null, 9 | "types": [ 10 | { 11 | "kind": "OBJECT", 12 | "name": "Query", 13 | "description": "", 14 | "fields": [ 15 | { 16 | "name": "allSnippets", 17 | "description": "", 18 | "args": [ 19 | { 20 | "name": "query", 21 | "description": "", 22 | "type": { 23 | "kind": "SCALAR", 24 | "name": "String", 25 | "ofType": null 26 | }, 27 | "defaultValue": null 28 | } 29 | ], 30 | "type": { 31 | "kind": "NON_NULL", 32 | "name": null, 33 | "ofType": { 34 | "kind": "LIST", 35 | "name": null, 36 | "ofType": { 37 | "kind": "OBJECT", 38 | "name": "Snippet", 39 | "ofType": null 40 | } 41 | } 42 | }, 43 | "isDeprecated": false, 44 | "deprecationReason": null 45 | }, 46 | { 47 | "name": "snippet", 48 | "description": "", 49 | "args": [ 50 | { 51 | "name": "id", 52 | "description": "", 53 | "type": { 54 | "kind": "NON_NULL", 55 | "name": null, 56 | "ofType": { 57 | "kind": "SCALAR", 58 | "name": "ID", 59 | "ofType": null 60 | } 61 | }, 62 | "defaultValue": null 63 | } 64 | ], 65 | "type": { 66 | "kind": "NON_NULL", 67 | "name": null, 68 | "ofType": { 69 | "kind": "OBJECT", 70 | "name": "Snippet", 71 | "ofType": null 72 | } 73 | }, 74 | "isDeprecated": false, 75 | "deprecationReason": null 76 | } 77 | ], 78 | "inputFields": null, 79 | "interfaces": [], 80 | "enumValues": null, 81 | "possibleTypes": null 82 | }, 83 | { 84 | "kind": "SCALAR", 85 | "name": "String", 86 | "description": 87 | "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", 88 | "fields": null, 89 | "inputFields": null, 90 | "interfaces": null, 91 | "enumValues": null, 92 | "possibleTypes": null 93 | }, 94 | { 95 | "kind": "OBJECT", 96 | "name": "Snippet", 97 | "description": "", 98 | "fields": [ 99 | { 100 | "name": "id", 101 | "description": "", 102 | "args": [], 103 | "type": { 104 | "kind": "NON_NULL", 105 | "name": null, 106 | "ofType": { 107 | "kind": "SCALAR", 108 | "name": "ID", 109 | "ofType": null 110 | } 111 | }, 112 | "isDeprecated": false, 113 | "deprecationReason": null 114 | }, 115 | { 116 | "name": "title", 117 | "description": "", 118 | "args": [], 119 | "type": { 120 | "kind": "NON_NULL", 121 | "name": null, 122 | "ofType": { 123 | "kind": "SCALAR", 124 | "name": "String", 125 | "ofType": null 126 | } 127 | }, 128 | "isDeprecated": false, 129 | "deprecationReason": null 130 | }, 131 | { 132 | "name": "content", 133 | "description": "", 134 | "args": [], 135 | "type": { 136 | "kind": "NON_NULL", 137 | "name": null, 138 | "ofType": { 139 | "kind": "SCALAR", 140 | "name": "String", 141 | "ofType": null 142 | } 143 | }, 144 | "isDeprecated": false, 145 | "deprecationReason": null 146 | }, 147 | { 148 | "name": "description", 149 | "description": "", 150 | "args": [], 151 | "type": { 152 | "kind": "NON_NULL", 153 | "name": null, 154 | "ofType": { 155 | "kind": "SCALAR", 156 | "name": "String", 157 | "ofType": null 158 | } 159 | }, 160 | "isDeprecated": false, 161 | "deprecationReason": null 162 | }, 163 | { 164 | "name": "example", 165 | "description": "", 166 | "args": [], 167 | "type": { 168 | "kind": "NON_NULL", 169 | "name": null, 170 | "ofType": { 171 | "kind": "SCALAR", 172 | "name": "String", 173 | "ofType": null 174 | } 175 | }, 176 | "isDeprecated": false, 177 | "deprecationReason": null 178 | }, 179 | { 180 | "name": "jsOutput", 181 | "description": "", 182 | "args": [], 183 | "type": { 184 | "kind": "NON_NULL", 185 | "name": null, 186 | "ofType": { 187 | "kind": "SCALAR", 188 | "name": "String", 189 | "ofType": null 190 | } 191 | }, 192 | "isDeprecated": false, 193 | "deprecationReason": null 194 | }, 195 | { 196 | "name": "section", 197 | "description": "", 198 | "args": [], 199 | "type": { 200 | "kind": "NON_NULL", 201 | "name": null, 202 | "ofType": { 203 | "kind": "SCALAR", 204 | "name": "String", 205 | "ofType": null 206 | } 207 | }, 208 | "isDeprecated": false, 209 | "deprecationReason": null 210 | }, 211 | { 212 | "name": "typeSpec", 213 | "description": "", 214 | "args": [], 215 | "type": { 216 | "kind": "NON_NULL", 217 | "name": null, 218 | "ofType": { 219 | "kind": "SCALAR", 220 | "name": "String", 221 | "ofType": null 222 | } 223 | }, 224 | "isDeprecated": false, 225 | "deprecationReason": null 226 | } 227 | ], 228 | "inputFields": null, 229 | "interfaces": [], 230 | "enumValues": null, 231 | "possibleTypes": null 232 | }, 233 | { 234 | "kind": "SCALAR", 235 | "name": "ID", 236 | "description": 237 | "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", 238 | "fields": null, 239 | "inputFields": null, 240 | "interfaces": null, 241 | "enumValues": null, 242 | "possibleTypes": null 243 | }, 244 | { 245 | "kind": "OBJECT", 246 | "name": "__Schema", 247 | "description": 248 | "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", 249 | "fields": [ 250 | { 251 | "name": "types", 252 | "description": "A list of all types supported by this server.", 253 | "args": [], 254 | "type": { 255 | "kind": "NON_NULL", 256 | "name": null, 257 | "ofType": { 258 | "kind": "LIST", 259 | "name": null, 260 | "ofType": { 261 | "kind": "NON_NULL", 262 | "name": null, 263 | "ofType": { 264 | "kind": "OBJECT", 265 | "name": "__Type", 266 | "ofType": null 267 | } 268 | } 269 | } 270 | }, 271 | "isDeprecated": false, 272 | "deprecationReason": null 273 | }, 274 | { 275 | "name": "queryType", 276 | "description": 277 | "The type that query operations will be rooted at.", 278 | "args": [], 279 | "type": { 280 | "kind": "NON_NULL", 281 | "name": null, 282 | "ofType": { 283 | "kind": "OBJECT", 284 | "name": "__Type", 285 | "ofType": null 286 | } 287 | }, 288 | "isDeprecated": false, 289 | "deprecationReason": null 290 | }, 291 | { 292 | "name": "mutationType", 293 | "description": 294 | "If this server supports mutation, the type that mutation operations will be rooted at.", 295 | "args": [], 296 | "type": { 297 | "kind": "OBJECT", 298 | "name": "__Type", 299 | "ofType": null 300 | }, 301 | "isDeprecated": false, 302 | "deprecationReason": null 303 | }, 304 | { 305 | "name": "subscriptionType", 306 | "description": 307 | "If this server support subscription, the type that subscription operations will be rooted at.", 308 | "args": [], 309 | "type": { 310 | "kind": "OBJECT", 311 | "name": "__Type", 312 | "ofType": null 313 | }, 314 | "isDeprecated": false, 315 | "deprecationReason": null 316 | }, 317 | { 318 | "name": "directives", 319 | "description": 320 | "A list of all directives supported by this server.", 321 | "args": [], 322 | "type": { 323 | "kind": "NON_NULL", 324 | "name": null, 325 | "ofType": { 326 | "kind": "LIST", 327 | "name": null, 328 | "ofType": { 329 | "kind": "NON_NULL", 330 | "name": null, 331 | "ofType": { 332 | "kind": "OBJECT", 333 | "name": "__Directive", 334 | "ofType": null 335 | } 336 | } 337 | } 338 | }, 339 | "isDeprecated": false, 340 | "deprecationReason": null 341 | } 342 | ], 343 | "inputFields": null, 344 | "interfaces": [], 345 | "enumValues": null, 346 | "possibleTypes": null 347 | }, 348 | { 349 | "kind": "OBJECT", 350 | "name": "__Type", 351 | "description": 352 | "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", 353 | "fields": [ 354 | { 355 | "name": "kind", 356 | "description": null, 357 | "args": [], 358 | "type": { 359 | "kind": "NON_NULL", 360 | "name": null, 361 | "ofType": { 362 | "kind": "ENUM", 363 | "name": "__TypeKind", 364 | "ofType": null 365 | } 366 | }, 367 | "isDeprecated": false, 368 | "deprecationReason": null 369 | }, 370 | { 371 | "name": "name", 372 | "description": null, 373 | "args": [], 374 | "type": { 375 | "kind": "SCALAR", 376 | "name": "String", 377 | "ofType": null 378 | }, 379 | "isDeprecated": false, 380 | "deprecationReason": null 381 | }, 382 | { 383 | "name": "description", 384 | "description": null, 385 | "args": [], 386 | "type": { 387 | "kind": "SCALAR", 388 | "name": "String", 389 | "ofType": null 390 | }, 391 | "isDeprecated": false, 392 | "deprecationReason": null 393 | }, 394 | { 395 | "name": "fields", 396 | "description": null, 397 | "args": [ 398 | { 399 | "name": "includeDeprecated", 400 | "description": null, 401 | "type": { 402 | "kind": "SCALAR", 403 | "name": "Boolean", 404 | "ofType": null 405 | }, 406 | "defaultValue": "false" 407 | } 408 | ], 409 | "type": { 410 | "kind": "LIST", 411 | "name": null, 412 | "ofType": { 413 | "kind": "NON_NULL", 414 | "name": null, 415 | "ofType": { 416 | "kind": "OBJECT", 417 | "name": "__Field", 418 | "ofType": null 419 | } 420 | } 421 | }, 422 | "isDeprecated": false, 423 | "deprecationReason": null 424 | }, 425 | { 426 | "name": "interfaces", 427 | "description": null, 428 | "args": [], 429 | "type": { 430 | "kind": "LIST", 431 | "name": null, 432 | "ofType": { 433 | "kind": "NON_NULL", 434 | "name": null, 435 | "ofType": { 436 | "kind": "OBJECT", 437 | "name": "__Type", 438 | "ofType": null 439 | } 440 | } 441 | }, 442 | "isDeprecated": false, 443 | "deprecationReason": null 444 | }, 445 | { 446 | "name": "possibleTypes", 447 | "description": null, 448 | "args": [], 449 | "type": { 450 | "kind": "LIST", 451 | "name": null, 452 | "ofType": { 453 | "kind": "NON_NULL", 454 | "name": null, 455 | "ofType": { 456 | "kind": "OBJECT", 457 | "name": "__Type", 458 | "ofType": null 459 | } 460 | } 461 | }, 462 | "isDeprecated": false, 463 | "deprecationReason": null 464 | }, 465 | { 466 | "name": "enumValues", 467 | "description": null, 468 | "args": [ 469 | { 470 | "name": "includeDeprecated", 471 | "description": null, 472 | "type": { 473 | "kind": "SCALAR", 474 | "name": "Boolean", 475 | "ofType": null 476 | }, 477 | "defaultValue": "false" 478 | } 479 | ], 480 | "type": { 481 | "kind": "LIST", 482 | "name": null, 483 | "ofType": { 484 | "kind": "NON_NULL", 485 | "name": null, 486 | "ofType": { 487 | "kind": "OBJECT", 488 | "name": "__EnumValue", 489 | "ofType": null 490 | } 491 | } 492 | }, 493 | "isDeprecated": false, 494 | "deprecationReason": null 495 | }, 496 | { 497 | "name": "inputFields", 498 | "description": null, 499 | "args": [], 500 | "type": { 501 | "kind": "LIST", 502 | "name": null, 503 | "ofType": { 504 | "kind": "NON_NULL", 505 | "name": null, 506 | "ofType": { 507 | "kind": "OBJECT", 508 | "name": "__InputValue", 509 | "ofType": null 510 | } 511 | } 512 | }, 513 | "isDeprecated": false, 514 | "deprecationReason": null 515 | }, 516 | { 517 | "name": "ofType", 518 | "description": null, 519 | "args": [], 520 | "type": { 521 | "kind": "OBJECT", 522 | "name": "__Type", 523 | "ofType": null 524 | }, 525 | "isDeprecated": false, 526 | "deprecationReason": null 527 | } 528 | ], 529 | "inputFields": null, 530 | "interfaces": [], 531 | "enumValues": null, 532 | "possibleTypes": null 533 | }, 534 | { 535 | "kind": "ENUM", 536 | "name": "__TypeKind", 537 | "description": 538 | "An enum describing what kind of type a given `__Type` is.", 539 | "fields": null, 540 | "inputFields": null, 541 | "interfaces": null, 542 | "enumValues": [ 543 | { 544 | "name": "SCALAR", 545 | "description": "Indicates this type is a scalar.", 546 | "isDeprecated": false, 547 | "deprecationReason": null 548 | }, 549 | { 550 | "name": "OBJECT", 551 | "description": 552 | "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 553 | "isDeprecated": false, 554 | "deprecationReason": null 555 | }, 556 | { 557 | "name": "INTERFACE", 558 | "description": 559 | "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 560 | "isDeprecated": false, 561 | "deprecationReason": null 562 | }, 563 | { 564 | "name": "UNION", 565 | "description": 566 | "Indicates this type is a union. `possibleTypes` is a valid field.", 567 | "isDeprecated": false, 568 | "deprecationReason": null 569 | }, 570 | { 571 | "name": "ENUM", 572 | "description": 573 | "Indicates this type is an enum. `enumValues` is a valid field.", 574 | "isDeprecated": false, 575 | "deprecationReason": null 576 | }, 577 | { 578 | "name": "INPUT_OBJECT", 579 | "description": 580 | "Indicates this type is an input object. `inputFields` is a valid field.", 581 | "isDeprecated": false, 582 | "deprecationReason": null 583 | }, 584 | { 585 | "name": "LIST", 586 | "description": 587 | "Indicates this type is a list. `ofType` is a valid field.", 588 | "isDeprecated": false, 589 | "deprecationReason": null 590 | }, 591 | { 592 | "name": "NON_NULL", 593 | "description": 594 | "Indicates this type is a non-null. `ofType` is a valid field.", 595 | "isDeprecated": false, 596 | "deprecationReason": null 597 | } 598 | ], 599 | "possibleTypes": null 600 | }, 601 | { 602 | "kind": "SCALAR", 603 | "name": "Boolean", 604 | "description": 605 | "The `Boolean` scalar type represents `true` or `false`.", 606 | "fields": null, 607 | "inputFields": null, 608 | "interfaces": null, 609 | "enumValues": null, 610 | "possibleTypes": null 611 | }, 612 | { 613 | "kind": "OBJECT", 614 | "name": "__Field", 615 | "description": 616 | "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", 617 | "fields": [ 618 | { 619 | "name": "name", 620 | "description": null, 621 | "args": [], 622 | "type": { 623 | "kind": "NON_NULL", 624 | "name": null, 625 | "ofType": { 626 | "kind": "SCALAR", 627 | "name": "String", 628 | "ofType": null 629 | } 630 | }, 631 | "isDeprecated": false, 632 | "deprecationReason": null 633 | }, 634 | { 635 | "name": "description", 636 | "description": null, 637 | "args": [], 638 | "type": { 639 | "kind": "SCALAR", 640 | "name": "String", 641 | "ofType": null 642 | }, 643 | "isDeprecated": false, 644 | "deprecationReason": null 645 | }, 646 | { 647 | "name": "args", 648 | "description": null, 649 | "args": [], 650 | "type": { 651 | "kind": "NON_NULL", 652 | "name": null, 653 | "ofType": { 654 | "kind": "LIST", 655 | "name": null, 656 | "ofType": { 657 | "kind": "NON_NULL", 658 | "name": null, 659 | "ofType": { 660 | "kind": "OBJECT", 661 | "name": "__InputValue", 662 | "ofType": null 663 | } 664 | } 665 | } 666 | }, 667 | "isDeprecated": false, 668 | "deprecationReason": null 669 | }, 670 | { 671 | "name": "type", 672 | "description": null, 673 | "args": [], 674 | "type": { 675 | "kind": "NON_NULL", 676 | "name": null, 677 | "ofType": { 678 | "kind": "OBJECT", 679 | "name": "__Type", 680 | "ofType": null 681 | } 682 | }, 683 | "isDeprecated": false, 684 | "deprecationReason": null 685 | }, 686 | { 687 | "name": "isDeprecated", 688 | "description": null, 689 | "args": [], 690 | "type": { 691 | "kind": "NON_NULL", 692 | "name": null, 693 | "ofType": { 694 | "kind": "SCALAR", 695 | "name": "Boolean", 696 | "ofType": null 697 | } 698 | }, 699 | "isDeprecated": false, 700 | "deprecationReason": null 701 | }, 702 | { 703 | "name": "deprecationReason", 704 | "description": null, 705 | "args": [], 706 | "type": { 707 | "kind": "SCALAR", 708 | "name": "String", 709 | "ofType": null 710 | }, 711 | "isDeprecated": false, 712 | "deprecationReason": null 713 | } 714 | ], 715 | "inputFields": null, 716 | "interfaces": [], 717 | "enumValues": null, 718 | "possibleTypes": null 719 | }, 720 | { 721 | "kind": "OBJECT", 722 | "name": "__InputValue", 723 | "description": 724 | "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", 725 | "fields": [ 726 | { 727 | "name": "name", 728 | "description": null, 729 | "args": [], 730 | "type": { 731 | "kind": "NON_NULL", 732 | "name": null, 733 | "ofType": { 734 | "kind": "SCALAR", 735 | "name": "String", 736 | "ofType": null 737 | } 738 | }, 739 | "isDeprecated": false, 740 | "deprecationReason": null 741 | }, 742 | { 743 | "name": "description", 744 | "description": null, 745 | "args": [], 746 | "type": { 747 | "kind": "SCALAR", 748 | "name": "String", 749 | "ofType": null 750 | }, 751 | "isDeprecated": false, 752 | "deprecationReason": null 753 | }, 754 | { 755 | "name": "type", 756 | "description": null, 757 | "args": [], 758 | "type": { 759 | "kind": "NON_NULL", 760 | "name": null, 761 | "ofType": { 762 | "kind": "OBJECT", 763 | "name": "__Type", 764 | "ofType": null 765 | } 766 | }, 767 | "isDeprecated": false, 768 | "deprecationReason": null 769 | }, 770 | { 771 | "name": "defaultValue", 772 | "description": 773 | "A GraphQL-formatted string representing the default value for this input value.", 774 | "args": [], 775 | "type": { 776 | "kind": "SCALAR", 777 | "name": "String", 778 | "ofType": null 779 | }, 780 | "isDeprecated": false, 781 | "deprecationReason": null 782 | } 783 | ], 784 | "inputFields": null, 785 | "interfaces": [], 786 | "enumValues": null, 787 | "possibleTypes": null 788 | }, 789 | { 790 | "kind": "OBJECT", 791 | "name": "__EnumValue", 792 | "description": 793 | "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", 794 | "fields": [ 795 | { 796 | "name": "name", 797 | "description": null, 798 | "args": [], 799 | "type": { 800 | "kind": "NON_NULL", 801 | "name": null, 802 | "ofType": { 803 | "kind": "SCALAR", 804 | "name": "String", 805 | "ofType": null 806 | } 807 | }, 808 | "isDeprecated": false, 809 | "deprecationReason": null 810 | }, 811 | { 812 | "name": "description", 813 | "description": null, 814 | "args": [], 815 | "type": { 816 | "kind": "SCALAR", 817 | "name": "String", 818 | "ofType": null 819 | }, 820 | "isDeprecated": false, 821 | "deprecationReason": null 822 | }, 823 | { 824 | "name": "isDeprecated", 825 | "description": null, 826 | "args": [], 827 | "type": { 828 | "kind": "NON_NULL", 829 | "name": null, 830 | "ofType": { 831 | "kind": "SCALAR", 832 | "name": "Boolean", 833 | "ofType": null 834 | } 835 | }, 836 | "isDeprecated": false, 837 | "deprecationReason": null 838 | }, 839 | { 840 | "name": "deprecationReason", 841 | "description": null, 842 | "args": [], 843 | "type": { 844 | "kind": "SCALAR", 845 | "name": "String", 846 | "ofType": null 847 | }, 848 | "isDeprecated": false, 849 | "deprecationReason": null 850 | } 851 | ], 852 | "inputFields": null, 853 | "interfaces": [], 854 | "enumValues": null, 855 | "possibleTypes": null 856 | }, 857 | { 858 | "kind": "OBJECT", 859 | "name": "__Directive", 860 | "description": 861 | "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", 862 | "fields": [ 863 | { 864 | "name": "name", 865 | "description": null, 866 | "args": [], 867 | "type": { 868 | "kind": "NON_NULL", 869 | "name": null, 870 | "ofType": { 871 | "kind": "SCALAR", 872 | "name": "String", 873 | "ofType": null 874 | } 875 | }, 876 | "isDeprecated": false, 877 | "deprecationReason": null 878 | }, 879 | { 880 | "name": "description", 881 | "description": null, 882 | "args": [], 883 | "type": { 884 | "kind": "SCALAR", 885 | "name": "String", 886 | "ofType": null 887 | }, 888 | "isDeprecated": false, 889 | "deprecationReason": null 890 | }, 891 | { 892 | "name": "locations", 893 | "description": null, 894 | "args": [], 895 | "type": { 896 | "kind": "NON_NULL", 897 | "name": null, 898 | "ofType": { 899 | "kind": "LIST", 900 | "name": null, 901 | "ofType": { 902 | "kind": "NON_NULL", 903 | "name": null, 904 | "ofType": { 905 | "kind": "ENUM", 906 | "name": "__DirectiveLocation", 907 | "ofType": null 908 | } 909 | } 910 | } 911 | }, 912 | "isDeprecated": false, 913 | "deprecationReason": null 914 | }, 915 | { 916 | "name": "args", 917 | "description": null, 918 | "args": [], 919 | "type": { 920 | "kind": "NON_NULL", 921 | "name": null, 922 | "ofType": { 923 | "kind": "LIST", 924 | "name": null, 925 | "ofType": { 926 | "kind": "NON_NULL", 927 | "name": null, 928 | "ofType": { 929 | "kind": "OBJECT", 930 | "name": "__InputValue", 931 | "ofType": null 932 | } 933 | } 934 | } 935 | }, 936 | "isDeprecated": false, 937 | "deprecationReason": null 938 | }, 939 | { 940 | "name": "onOperation", 941 | "description": null, 942 | "args": [], 943 | "type": { 944 | "kind": "NON_NULL", 945 | "name": null, 946 | "ofType": { 947 | "kind": "SCALAR", 948 | "name": "Boolean", 949 | "ofType": null 950 | } 951 | }, 952 | "isDeprecated": true, 953 | "deprecationReason": "Use `locations`." 954 | }, 955 | { 956 | "name": "onFragment", 957 | "description": null, 958 | "args": [], 959 | "type": { 960 | "kind": "NON_NULL", 961 | "name": null, 962 | "ofType": { 963 | "kind": "SCALAR", 964 | "name": "Boolean", 965 | "ofType": null 966 | } 967 | }, 968 | "isDeprecated": true, 969 | "deprecationReason": "Use `locations`." 970 | }, 971 | { 972 | "name": "onField", 973 | "description": null, 974 | "args": [], 975 | "type": { 976 | "kind": "NON_NULL", 977 | "name": null, 978 | "ofType": { 979 | "kind": "SCALAR", 980 | "name": "Boolean", 981 | "ofType": null 982 | } 983 | }, 984 | "isDeprecated": true, 985 | "deprecationReason": "Use `locations`." 986 | } 987 | ], 988 | "inputFields": null, 989 | "interfaces": [], 990 | "enumValues": null, 991 | "possibleTypes": null 992 | }, 993 | { 994 | "kind": "ENUM", 995 | "name": "__DirectiveLocation", 996 | "description": 997 | "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", 998 | "fields": null, 999 | "inputFields": null, 1000 | "interfaces": null, 1001 | "enumValues": [ 1002 | { 1003 | "name": "QUERY", 1004 | "description": "Location adjacent to a query operation.", 1005 | "isDeprecated": false, 1006 | "deprecationReason": null 1007 | }, 1008 | { 1009 | "name": "MUTATION", 1010 | "description": "Location adjacent to a mutation operation.", 1011 | "isDeprecated": false, 1012 | "deprecationReason": null 1013 | }, 1014 | { 1015 | "name": "SUBSCRIPTION", 1016 | "description": "Location adjacent to a subscription operation.", 1017 | "isDeprecated": false, 1018 | "deprecationReason": null 1019 | }, 1020 | { 1021 | "name": "FIELD", 1022 | "description": "Location adjacent to a field.", 1023 | "isDeprecated": false, 1024 | "deprecationReason": null 1025 | }, 1026 | { 1027 | "name": "FRAGMENT_DEFINITION", 1028 | "description": "Location adjacent to a fragment definition.", 1029 | "isDeprecated": false, 1030 | "deprecationReason": null 1031 | }, 1032 | { 1033 | "name": "FRAGMENT_SPREAD", 1034 | "description": "Location adjacent to a fragment spread.", 1035 | "isDeprecated": false, 1036 | "deprecationReason": null 1037 | }, 1038 | { 1039 | "name": "INLINE_FRAGMENT", 1040 | "description": "Location adjacent to an inline fragment.", 1041 | "isDeprecated": false, 1042 | "deprecationReason": null 1043 | }, 1044 | { 1045 | "name": "SCHEMA", 1046 | "description": "Location adjacent to a schema definition.", 1047 | "isDeprecated": false, 1048 | "deprecationReason": null 1049 | }, 1050 | { 1051 | "name": "SCALAR", 1052 | "description": "Location adjacent to a scalar definition.", 1053 | "isDeprecated": false, 1054 | "deprecationReason": null 1055 | }, 1056 | { 1057 | "name": "OBJECT", 1058 | "description": "Location adjacent to an object type definition.", 1059 | "isDeprecated": false, 1060 | "deprecationReason": null 1061 | }, 1062 | { 1063 | "name": "FIELD_DEFINITION", 1064 | "description": "Location adjacent to a field definition.", 1065 | "isDeprecated": false, 1066 | "deprecationReason": null 1067 | }, 1068 | { 1069 | "name": "ARGUMENT_DEFINITION", 1070 | "description": "Location adjacent to an argument definition.", 1071 | "isDeprecated": false, 1072 | "deprecationReason": null 1073 | }, 1074 | { 1075 | "name": "INTERFACE", 1076 | "description": "Location adjacent to an interface definition.", 1077 | "isDeprecated": false, 1078 | "deprecationReason": null 1079 | }, 1080 | { 1081 | "name": "UNION", 1082 | "description": "Location adjacent to a union definition.", 1083 | "isDeprecated": false, 1084 | "deprecationReason": null 1085 | }, 1086 | { 1087 | "name": "ENUM", 1088 | "description": "Location adjacent to an enum definition.", 1089 | "isDeprecated": false, 1090 | "deprecationReason": null 1091 | }, 1092 | { 1093 | "name": "ENUM_VALUE", 1094 | "description": "Location adjacent to an enum value definition.", 1095 | "isDeprecated": false, 1096 | "deprecationReason": null 1097 | }, 1098 | { 1099 | "name": "INPUT_OBJECT", 1100 | "description": 1101 | "Location adjacent to an input object type definition.", 1102 | "isDeprecated": false, 1103 | "deprecationReason": null 1104 | }, 1105 | { 1106 | "name": "INPUT_FIELD_DEFINITION", 1107 | "description": 1108 | "Location adjacent to an input object field definition.", 1109 | "isDeprecated": false, 1110 | "deprecationReason": null 1111 | } 1112 | ], 1113 | "possibleTypes": null 1114 | } 1115 | ], 1116 | "directives": [ 1117 | { 1118 | "name": "skip", 1119 | "description": 1120 | "Directs the executor to skip this field or fragment when the `if` argument is true.", 1121 | "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], 1122 | "args": [ 1123 | { 1124 | "name": "if", 1125 | "description": "Skipped when true.", 1126 | "type": { 1127 | "kind": "NON_NULL", 1128 | "name": null, 1129 | "ofType": { 1130 | "kind": "SCALAR", 1131 | "name": "Boolean", 1132 | "ofType": null 1133 | } 1134 | }, 1135 | "defaultValue": null 1136 | } 1137 | ] 1138 | }, 1139 | { 1140 | "name": "include", 1141 | "description": 1142 | "Directs the executor to include this field or fragment only when the `if` argument is true.", 1143 | "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], 1144 | "args": [ 1145 | { 1146 | "name": "if", 1147 | "description": "Included when true.", 1148 | "type": { 1149 | "kind": "NON_NULL", 1150 | "name": null, 1151 | "ofType": { 1152 | "kind": "SCALAR", 1153 | "name": "Boolean", 1154 | "ofType": null 1155 | } 1156 | }, 1157 | "defaultValue": null 1158 | } 1159 | ] 1160 | }, 1161 | { 1162 | "name": "deprecated", 1163 | "description": 1164 | "Marks an element of a GraphQL schema as no longer supported.", 1165 | "locations": ["FIELD_DEFINITION", "ENUM_VALUE"], 1166 | "args": [ 1167 | { 1168 | "name": "reason", 1169 | "description": 1170 | "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).", 1171 | "type": { 1172 | "kind": "SCALAR", 1173 | "name": "String", 1174 | "ofType": null 1175 | }, 1176 | "defaultValue": "\"No longer supported\"" 1177 | } 1178 | ] 1179 | } 1180 | ] 1181 | } 1182 | } 1183 | } 1184 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "build": "bsb -make-world && webpack --mode=production", 6 | "start": "bsb -make-world -w", 7 | "start-server": "nodemon lib/js/src/index.bs.js", 8 | "start-web": "webpack --watch --mode=development", 9 | "start-reason": "bsb -make-world -w", 10 | "clean": "bsb -clean-world", 11 | "test": "jest --watch", 12 | "deploy": 13 | "now -e ALGOLIA_API_KEY=@algolia-api-key -e ALGOLIA_APPLICATION_ID=@algolia-application-id --docker" 14 | }, 15 | "husky": { 16 | "hooks": { 17 | "pre-commit": "lint-staged" 18 | } 19 | }, 20 | "lint-staged": { 21 | "*.{re,rei}": ["refmt --in-place", "git add"], 22 | "*.{js,json,css,md}": ["prettier --write", "git add"] 23 | }, 24 | "keywords": ["BuckleScript"], 25 | "author": "", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@glennsl/bs-jest": "^0.4.1", 29 | "bs-enzyme": "^0.3.1", 30 | "bs-platform": "^2.1.0", 31 | "enzyme": "^3.3.0", 32 | "enzyme-adapter-react-16": "^1.1.1", 33 | "graphql_ppx": "^0.2.1", 34 | "husky": "^0.15.0-rc.8", 35 | "jest": "^22.3.0", 36 | "lint-staged": "^7.0.0", 37 | "nodemon": "^1.14.11", 38 | "prettier": "^1.10.2", 39 | "webpack": "^4.0.0", 40 | "webpack-cli": "^2.0.8" 41 | }, 42 | "dependencies": { 43 | "algoliasearch": "^3.24.9", 44 | "apollo-cache-inmemory": "^1.1.7", 45 | "apollo-client": "^2.2.2", 46 | "apollo-link-context": "^1.0.3", 47 | "apollo-link-error": "^1.0.3", 48 | "apollo-link-http": "^1.3.2", 49 | "apollo-link-schema": "^1.0.3", 50 | "body-parser": "^1.18.2", 51 | "bs-algolia": "^0.0.1", 52 | "bs-apollo-server-express": "^0.3.1", 53 | "bs-express": "^0.0.6", 54 | "bs-graphql": "^0.3.1", 55 | "bs-graphql-tools": "^0.3.1", 56 | "bs-nice": "http://github.com/hehk/bs-nice", 57 | "bs-nice-components": "http://github.com/hehk/bs-nice-components", 58 | "express": "^4.16.2", 59 | "graphql": "^0.12.3", 60 | "graphql-tag": "^2.6.1", 61 | "graphql-tools": "^2.19.0", 62 | "lodash.debounce": "^4.0.8", 63 | "reason-apollo": "^0.6.17", 64 | "reason-react": "^0.3.1", 65 | "reason-react-context": "^0.1.0" 66 | }, 67 | "jest": { 68 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.bs.js?$" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /snippets/SyntaxPatternMatchWithoutSwitch.re: -------------------------------------------------------------------------------- 1 | /* @title Pattern match without switch */ 2 | /* @section Syntax */ 3 | /* @description Cannot believe i forgot this purty */ 4 | /* @type none */ 5 | /* @content */ 6 | let patternMatch = 7 | fun 8 | | 0 => "zero" 9 | | 1 => "one" 10 | | _ => "way too big"; 11 | 12 | let funnyAdd = x => 13 | fun 14 | | 0 => 0 15 | | 1 => 1 16 | | n => x + n; 17 | 18 | /* @example */ 19 | patternMatch(0); /* zero */ 20 | 21 | patternMatch(1); /* one */ 22 | 23 | patternMatch(1337); /* way too big */ 24 | -------------------------------------------------------------------------------- /snippets/listMap.re: -------------------------------------------------------------------------------- 1 | /* @title Map */ 2 | /* @section List */ 3 | /* @description This function does stuff */ 4 | /* @type let map: ('a => 'b, list('a)) => list('b) */ 5 | /* @content */ 6 | let a = [1, 2, 3]; 7 | 8 | let b = List.map(x => x + 1, a); 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/listReduce.re: -------------------------------------------------------------------------------- 1 | /* @title Reduce */ 2 | /* @section List */ 3 | /* @description hope this works */ 4 | /* @type let reduce: (('a, 'b) => 'a, 'a, list('b)) => 'b; */ 5 | /* @content */ 6 | let reduce = List.fold_left; 7 | 8 | /* @example */ 9 | reduce((a, b) => a + b, 0, [1, 2, 3]); /* 6 */ 10 | -------------------------------------------------------------------------------- /snippets/operator__and.re: -------------------------------------------------------------------------------- 1 | /* @title (&&) And Operator */ 2 | /* @section Operator */ 3 | /* @description Standard and boolean operator. */ 4 | /* @type let (&&): (bool, bool) => bool */ 5 | /* @content */ 6 | let a = true && false; /* false */ 7 | 8 | let b = true && true; /* true */ 9 | 10 | let c = false && false; /* false */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__applicationOperator.re: -------------------------------------------------------------------------------- 1 | /* @title (@@) Application Operator */ 2 | /* @section Operator */ 3 | /* @description Takes a function and applies the next parameter to it. */ 4 | /* @type let (@@): ('a => 'b, 'a) => 'b; */ 5 | /* @content */ 6 | let add = (a, b) => a + b; 7 | 8 | let add5 = add @@ 5; /* (5, b) => 5 + b */ 9 | 10 | let a = add5 @@ 10; /* 15 */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__comparisonGreaterThan.re: -------------------------------------------------------------------------------- 1 | /* @title (>) Greater Than Operator */ 2 | /* @section Operator */ 3 | /* @description Returns if a variable is greater than another. */ 4 | /* @type let (>): ('a, 'a) => bool; */ 5 | /* @content */ 6 | let a = 5 > 4; /* true */ 7 | 8 | let b = 2. > 3.; /* false */ 9 | 10 | let x = "a" > "b"; /* false */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__comparisonGreaterThanOrEqualTo.re: -------------------------------------------------------------------------------- 1 | /* @title (>=) Greater Than Or Equal to Operator */ 2 | /* @section Operator */ 3 | /* @description Returns if a variable is greater than or equal to another. */ 4 | /* @type let (>=): ('a, 'a) => bool; */ 5 | /* @content */ 6 | let a = 5 >= 4; /* true */ 7 | 8 | let b = 2. >= 2.; /* true */ 9 | 10 | let x = "a" >= "b"; /* false */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__comparisonLessOrEqualTo.re: -------------------------------------------------------------------------------- 1 | /* @title (<=) Less Than or Equal to Operator */ 2 | /* @section Operator */ 3 | /* @description Returns if a variable is less than or equal to another. */ 4 | /* @type let (<=): ('a, 'a) => bool; */ 5 | /* @content */ 6 | let a = 5 <= 4; /* false */ 7 | 8 | let b = 2. <= 2.; /* true */ 9 | 10 | let x = "a" <= "b"; /* true */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__comparisonLessThan.re: -------------------------------------------------------------------------------- 1 | /* @title (<) Less Than Operator */ 2 | /* @section Operator */ 3 | /* @description Returns if a variable is less than another. */ 4 | /* @type let (<): ('a, 'a) => bool; */ 5 | /* @content */ 6 | let a = 5 < 4; /* false */ 7 | 8 | let b = 2. < 3.; /* true */ 9 | 10 | let x = "a" < "b"; /* true */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__equals.re: -------------------------------------------------------------------------------- 1 | /* @title (===) Equals Operator */ 2 | /* @section Operator */ 3 | /* @description Compares if two values are equal. */ 4 | /* @type let (===): ('a, 'a) => bool */ 5 | /* @content */ 6 | let a = 5 === 4; /* false */ 7 | 8 | let b = 5 === 5; /* true */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__exponentiation.re: -------------------------------------------------------------------------------- 1 | /* @title (**) Exponentiation */ 2 | /* @section Operator */ 3 | /* @description An operator to handle float exponentiation.*/ 4 | /* @type let (**): (float, float) => float */ 5 | /* @content */ 6 | let a = 5. ** 5.; 7 | 8 | let b = 2. ** 0.5; 9 | 10 | /* @example */ 11 | let x = 2. ** 0.3; /* 1.2311444133449163 */ 12 | -------------------------------------------------------------------------------- /snippets/operator__floatAddition.re: -------------------------------------------------------------------------------- 1 | /* @title (+.) Float Addition Operator */ 2 | /* @section Operator */ 3 | /* @description Handles adding two floats together. */ 4 | /* @type let (+.): (float, float) => float */ 5 | /* @content */ 6 | let a = 5. +. 5.; /* 10. */ 7 | 8 | let b = 1. +. 0.5; /* 1.5 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__floatDivision.re: -------------------------------------------------------------------------------- 1 | /* @title (/.) Float Division Operator */ 2 | /* @section Operator */ 3 | /* @description Handles dividing two floats together.*/ 4 | /* @type let (/.): (float, float) => float */ 5 | /* @content */ 6 | let a = 5. /. 2.; /* 2.5 */ 7 | 8 | let b = 1. /. 0.5; /* 2.0 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__floatMultiplication.re: -------------------------------------------------------------------------------- 1 | /* @title (*.) Float Multiplication */ 2 | /* @section Operator */ 3 | /* @description Handles mutlication of floating point numbers.*/ 4 | /* @type let (*.): (float, float) => float */ 5 | /* @content */ 6 | let a = 5. *. 5.; 7 | 8 | let b = 2. *. 0.5; 9 | 10 | /* @example */ 11 | let x = 2. *. 0.3; /* 0.6 */ 12 | -------------------------------------------------------------------------------- /snippets/operator__floatSubtraction.re: -------------------------------------------------------------------------------- 1 | /* @title (-.) Float Subtraction Operator */ 2 | /* @section Operator */ 3 | /* @description Handles subtracting two floats together. */ 4 | /* @type let (-.): (float, float) => float */ 5 | /* @content */ 6 | let a = 5. -. 5.; /* 0. */ 7 | 8 | let b = 1. -. 0.5; /* 0.5 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__integerAddition.re: -------------------------------------------------------------------------------- 1 | /* @title (+) Integer Addition Operator */ 2 | /* @section Operator */ 3 | /* @description Handles adding two integers together. */ 4 | /* @type let (+): (int, int) => int */ 5 | /* @content */ 6 | let a = 5 + 5; /* 10 */ 7 | 8 | let b = 1 + 2; /* 3 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__integerDivision.re: -------------------------------------------------------------------------------- 1 | /* @title (/) Integer Division Operator */ 2 | /* @section Operator */ 3 | /* @description Handles division two integers together. */ 4 | /* @type let (/): (int, int) => int */ 5 | /* @content */ 6 | let a = 5 / 2; /* 2 */ 7 | 8 | let b = 2 / 2; /* 1 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__integerMultiplication.re: -------------------------------------------------------------------------------- 1 | /* @title (*) Multiplication Operator */ 2 | /* @section Operator */ 3 | /* @description An operator for handling integer multiplication. */ 4 | /* @type let (*): (int, int) => int */ 5 | /* @content */ 6 | let a = 5 * 5; 7 | 8 | let b = 1 * 2; 9 | 10 | /* @example */ 11 | let x = 2 * 3; /* 6 */ 12 | -------------------------------------------------------------------------------- /snippets/operator__integerSubtraction.re: -------------------------------------------------------------------------------- 1 | /* @title (-) Integer Subtraction Operator */ 2 | /* @section Operator */ 3 | /* @description Handles subtracting two integers together. */ 4 | /* @type let (-): (int, int) => int */ 5 | /* @content */ 6 | let a = 5 - 5; /* 0 */ 7 | 8 | let b = 1 - 2; /* -1 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__listConcatination.re: -------------------------------------------------------------------------------- 1 | /* @title (@) List Concatination Operator */ 2 | /* @section Operator */ 3 | /* @description Concat two lists. */ 4 | /* @type let (@): (list('a), list('a)) => list('a) */ 5 | /* @content */ 6 | 7 | let a = [1] @ [2]; /* [1, 2] */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__logicalAnd.re: -------------------------------------------------------------------------------- 1 | /* @title (land) Logical And Operator */ 2 | /* @section Operator */ 3 | /* @description Bitwise logical and. */ 4 | /* @type let (land): (int, int) => int */ 5 | /* @content */ 6 | 7 | let a = 12 land 10; /* 8 */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__logicalOr.re: -------------------------------------------------------------------------------- 1 | /* @title (lor) Logical Or Operator */ 2 | /* @section Operator */ 3 | /* @description Bitwise logical or. */ 4 | /* @type let (lor): (int, int) => int */ 5 | /* @content */ 6 | 7 | let a = 12 lor 10; /* 14 */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__logicalXor.re: -------------------------------------------------------------------------------- 1 | /* @title (lxor) Logical Exclusive Or Operator */ 2 | /* @section Operator */ 3 | /* @description Bitwise logical exclusive or.*/ 4 | /* @type let (lxor): (int, int) => int */ 5 | /* @content */ 6 | 7 | let a = 12 lxor 10; /* 6 */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__modulus.re: -------------------------------------------------------------------------------- 1 | /* @title (mod) Modulus Operator */ 2 | /* @section Operator */ 3 | /* @description Integer remainder. */ 4 | /* @type let (mod): (int, int) => int */ 5 | /* @content */ 6 | 7 | let a = 12 mod 10; /* 2 */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__notEquals.re: -------------------------------------------------------------------------------- 1 | /* @title (!==) Not Equals Operator */ 2 | /* @section Operator */ 3 | /* @description Negation of the === operator. */ 4 | /* @type let (!==): ('a, 'a) => bool; */ 5 | /* @content */ 6 | let a = 5 !== 4; /* true */ 7 | 8 | let b = 5 !== 5; /* false */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__pipe.re: -------------------------------------------------------------------------------- 1 | /* @title (|>) Pipe Operator */ 2 | /* @section Operator */ 3 | /* @description Reverse-application operator. */ 4 | /* @type let (|>): ('a, 'a => 'b) => 'b; */ 5 | /* @content */ 6 | let add = (a, b) => a + b; 7 | 8 | let add5 = add(5); 9 | 10 | let a = 10 |> add5; /* 15 */ 11 | /* @example */ 12 | -------------------------------------------------------------------------------- /snippets/operator__referenceAssignment.re: -------------------------------------------------------------------------------- 1 | /* @title (:=) Reference Assignment Operator */ 2 | /* @section Operator */ 3 | /* @description Changes the value stored in a reference. */ 4 | /* @type let (:=): (ref('a), 'a) => unit; */ 5 | /* @content */ 6 | let a = ref(5); 7 | 8 | a := 4; 9 | 10 | let b = a^ == 4; /* true */ 11 | 12 | /* @example */ 13 | Js.log(a^); 14 | -------------------------------------------------------------------------------- /snippets/operator__referenceContent.re: -------------------------------------------------------------------------------- 1 | /* @title (^) Reference Content Operator */ 2 | /* @section Operator */ 3 | /* @description returns the content of a reference. */ 4 | /* @type let (^): ref('a) => 'a; */ 5 | /* @content */ 6 | let a = ref(5); 7 | 8 | let b = a^; 9 | 10 | /* @example */ 11 | Js.log(a^); 12 | -------------------------------------------------------------------------------- /snippets/operator__shiftLeft.re: -------------------------------------------------------------------------------- 1 | /* @title (asr) Shift Left Operator */ 2 | /* @section Operator */ 3 | /* @description (n lsl m) Shift n to the left by m bits. */ 4 | /* @type let (asr): (int, int) => int; */ 5 | /* @content */ 6 | 7 | let a = 7 lsl 1; /* 14 */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /snippets/operator__shiftRight.re: -------------------------------------------------------------------------------- 1 | /* @title (asr) or (lsr) Shift Right Operator */ 2 | /* @section Operator */ 3 | /* @description (n asr m) or (n lsr m) Shift n to the right by m bits. */ 4 | /* @type let (asr): (int, int) => int; */ 5 | /* @content */ 6 | let a = 7 asr 1; /* 3 */ 7 | 8 | let b = 7 lsr 1; /* 3 */ 9 | /* @example */ 10 | -------------------------------------------------------------------------------- /snippets/operator__stringConcatination.re: -------------------------------------------------------------------------------- 1 | /* @title (++) String Concatination Operator */ 2 | /* @section Operator */ 3 | /* @description Concat two strings. */ 4 | /* @type let (++): (string, string) => string */ 5 | /* @content */ 6 | 7 | let a = "abc" ++ "def"; /* "abcdef" */ 8 | /* @example */ 9 | -------------------------------------------------------------------------------- /src/bindings/apollo.re: -------------------------------------------------------------------------------- 1 | module type Client = { 2 | module type Query = { 3 | type response = 4 | | Loading 5 | | Loaded(Js.Json.t) 6 | | Failed(string); 7 | type state = { 8 | response, 9 | variables: Js.Json.t 10 | }; 11 | type action = 12 | | Result(string) 13 | | Error(string); 14 | let sendQuery: (~query: 'query, ~reducer: 'reduce) => unit; 15 | let component: 16 | string => 17 | ReasonReact.componentSpec( 18 | state, 19 | ReasonReact.stateless, 20 | ReasonReact.noRetainedProps, 21 | ReasonReact.noRetainedProps, 22 | action 23 | ); 24 | let make: (~query: 'query, 'children) => ReasonReact.reactClass; 25 | }; 26 | module type Mutation = {}; 27 | }; 28 | 29 | module type Query = { 30 | type response = 31 | | Loading 32 | | Loaded(Js.Json.t) 33 | | Failed(string); 34 | type state = { 35 | response, 36 | variables: Js.Json.t 37 | }; 38 | type action = 39 | | Result(string) 40 | | Error(string); 41 | let sendQuery: 42 | ( 43 | ~query: { 44 | .. 45 | "query": string, 46 | "variables": Js.Json.t 47 | }, 48 | ~reduce: (unit => action, unit) => 'a 49 | ) => 50 | unit; 51 | let component: 52 | ReasonReact.componentSpec( 53 | state, 54 | ReasonReact.stateless, 55 | ReasonReact.noRetainedProps, 56 | ReasonReact.noRetainedProps, 57 | action 58 | ); 59 | let make: 60 | ( 61 | ~query: { 62 | .. 63 | "query": string, 64 | "variables": Js.Json.t, 65 | "parse": 'a 66 | }, 67 | (response, 'a) => ReasonReact.reactElement 68 | ) => 69 | ReasonReact.componentSpec( 70 | state, 71 | state, 72 | ReasonReact.noRetainedProps, 73 | ReasonReact.noRetainedProps, 74 | action 75 | ); 76 | }; 77 | 78 | type apolloState = 79 | | None 80 | | Some((module Query)); 81 | 82 | module Context = 83 | ReasonReactContext.CreateContext( 84 | { 85 | type state = apolloState; 86 | let name = "Apollo"; 87 | let defaultValue = None; 88 | } 89 | ); 90 | -------------------------------------------------------------------------------- /src/bindings/apolloLinkSchema.re: -------------------------------------------------------------------------------- 1 | type schema('a) = { 2 | . 3 | "resolvers": {. "Query": 'a}, 4 | "typeDefs": string 5 | }; 6 | 7 | type config = {. "schema": GraphQL.Type.schema}; 8 | 9 | [@bs.module "apollo-link-schema"] [@bs.new] 10 | external _make : config => ReasonApolloTypes.apolloLink = "SchemaLink"; 11 | 12 | let make = _make; 13 | -------------------------------------------------------------------------------- /src/config.re: -------------------------------------------------------------------------------- 1 | type t = { 2 | port: int, 3 | algoliaApplicationId: string, 4 | algoliaAPIKey: string 5 | }; 6 | 7 | let getEnvVar = (key) => 8 | switch (key |> Js.Dict.get(Node.Process.process##env)) { 9 | | None => "" 10 | | Some(x) => x 11 | }; 12 | 13 | let env = { 14 | port: 3000, 15 | algoliaApplicationId: getEnvVar("ALGOLIA_APPLICATION_ID"), 16 | algoliaAPIKey: getEnvVar("ALGOLIA_API_KEY") 17 | }; 18 | -------------------------------------------------------------------------------- /src/index.re: -------------------------------------------------------------------------------- 1 | let app = Express.App.make(); 2 | 3 | [@bs.module "body-parser"] 4 | external bodyParserJson : unit => Express.Middleware.t = "json"; 5 | 6 | let graphqlMiddleware = 7 | GraphQLTools.makeExecutableSchema(Graphql.schema) 8 | |> ApolloServerExpress.createGraphQLExpressMiddleware; 9 | 10 | let graphiqlMiddleware = 11 | ApolloServerExpress.createGraphiQLExpressMiddleware("/graphql"); 12 | 13 | Express.App.use(app, bodyParserJson()); 14 | 15 | Express.App.useOnPath(app, graphqlMiddleware, ~path="/graphql"); 16 | 17 | Express.App.useOnPath(app, graphiqlMiddleware, ~path="/graphiql"); 18 | 19 | Express.App.useOnPath( 20 | app, 21 | ~path="/dist", 22 | { 23 | let options = Express.Static.defaultOptions(); 24 | Express.Static.make("dist", options) |> Express.Static.asMiddleware; 25 | } 26 | ); 27 | 28 | Express.App.useOnPath( 29 | app, 30 | Express.Middleware.from( 31 | { 32 | let body = 33 | ReactDOMServerRe.renderToString( 34 | 35 | ); 36 | let styles = Template.generateStyles(~html=body, ()); 37 | let html = Template.make(~body, ~styles, ~title="30s of Reason", ()); 38 | (_req, res, _next) => Express.Response.sendString(res, html); 39 | } 40 | ), 41 | ~path="/" 42 | ); 43 | 44 | let onListen = e => { 45 | let port = string_of_int(Config.env.port); 46 | let message = {j| 47 | GraphQL => localhost:$port/graphql 48 | GraphiQL => localhost:$port/graphiql 49 | Web => localhost:$port/ 50 | |j}; 51 | switch e { 52 | | exception (Js.Exn.Error(e)) => 53 | Js.log(e); 54 | Node.Process.exit(1); 55 | | _ => Js.log(message) 56 | }; 57 | }; 58 | 59 | Express.App.listen(app, ~onListen, ()); 60 | -------------------------------------------------------------------------------- /src/mocks/apolloMock.re: -------------------------------------------------------------------------------- 1 | type dataObject = { 2 | . 3 | "__typename": string, 4 | "id": string, 5 | "key": string 6 | }; 7 | 8 | let cache = 9 | ApolloInMemoryCache.createInMemoryCache( 10 | ~dataIdFromObject=(obj: dataObject) => obj##id, 11 | () 12 | ); 13 | 14 | type schema('a) = { 15 | . 16 | "resolvers": {. "Query": 'a}, 17 | "typeDefs": string 18 | }; 19 | 20 | [@bs.module "graphql-tools"] 21 | external addMockFunctionsToSchema : 22 | {. "schema": schema('a)} => GraphQL.Type.schema = 23 | "addMockFunctionsToSchema"; 24 | 25 | [@bs.module "graphql-tools"] 26 | external makeExecutableSchema : {. "typeDefs": string} => schema(unit) = 27 | "makeExecutableSchema"; 28 | 29 | let schema = { 30 | let executableSchema = makeExecutableSchema({"typeDefs": Graphql.types}); 31 | addMockFunctionsToSchema({"schema": executableSchema}); 32 | }; 33 | 34 | let link = ApolloLinkSchema.make({"schema": schema}); 35 | 36 | module Client = 37 | ReasonApollo.CreateClient( 38 | { 39 | let apolloClient = 40 | ReasonApollo.createApolloClient( 41 | ~cache, 42 | ~link, 43 | ~ssrMode=Js.Boolean.to_js_boolean(true), 44 | () 45 | ); 46 | } 47 | ); 48 | -------------------------------------------------------------------------------- /src/server/apolloServer.re: -------------------------------------------------------------------------------- 1 | type dataObject = { 2 | . 3 | "__typename": string, 4 | "id": string, 5 | "key": string 6 | }; 7 | 8 | let cache = 9 | ApolloInMemoryCache.createInMemoryCache( 10 | ~dataIdFromObject=(obj: dataObject) => obj##id, 11 | () 12 | ); 13 | 14 | let link = 15 | ApolloLinkSchema.make({ 16 | "schema": GraphQLTools.makeExecutableSchema(Graphql.schema) 17 | }); 18 | 19 | module Client = 20 | ReasonApollo.CreateClient( 21 | { 22 | let apolloClient = 23 | ReasonApollo.createApolloClient(~cache, ~link, ~ssrMode=Js.true_, ()); 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /src/server/graphql.re: -------------------------------------------------------------------------------- 1 | let types = Snippet.graphQLType; 2 | 3 | let query = {| 4 | type Query { 5 | allSnippets(query: String): [Snippet]! 6 | snippet(id: ID!): Snippet! 7 | } 8 | |}; 9 | 10 | let resolvers = { 11 | let snippet = Snippet.Handler.make(); 12 | {"Query": Js.Obj.empty() |> Js.Obj.assign(snippet.queries)}; 13 | }; 14 | 15 | let schema = {"typeDefs": types ++ query, "resolvers": resolvers}; 16 | -------------------------------------------------------------------------------- /src/server/snippet.re: -------------------------------------------------------------------------------- 1 | type t = { 2 | . 3 | "id": string, 4 | "title": string, 5 | "content": string, 6 | "description": string, 7 | "example": string, 8 | "jsOutput": string, 9 | "objectID": string, 10 | "section": string, 11 | "typeSpec": string 12 | }; 13 | 14 | let graphQLType = {| 15 | type Snippet { 16 | id: ID! @unique 17 | title: String! 18 | content: String! 19 | description: String! 20 | example: String! 21 | jsOutput: String! 22 | section: String! 23 | typeSpec: String! 24 | } 25 | |}; 26 | 27 | module Scraper = { 28 | let workingReDir = "./snippets/"; 29 | let workingJsDir = "./lib/js/snippets/"; 30 | let listSnippetNames = () => 31 | Node.Fs.readdirSync(workingReDir) 32 | |> Array.map(name => String.sub(name, 0, String.length(name) - 3)); 33 | let loadSnippetRawRe = name => 34 | Node.Fs.readFileAsUtf8Sync(workingReDir ++ name ++ ".re"); 35 | let loadSnippetJsOutput = name => 36 | Node.Fs.readFileAsUtf8Sync(workingJsDir ++ name ++ ".bs.js"); 37 | let createSnippet = (~id, ~rawRe, ~jsOutput, ~name, ()) => { 38 | let pattern = 39 | Js.Re.fromString( 40 | "(\\/\\* @title )([\\s\\S]*)(\\*\\/[\\s\\S]*)" 41 | ++ "(\\/\\* @section )([\\s\\S]*)(\\*\\/[\\s\\S]*)" 42 | ++ "(\\/\\* @description )([\\s\\S]*)(\\*\\/[\\s\\S]*)" 43 | ++ "(\\/\\* @type )([\\s\\S]*)(\\*\\/[\\s\\S]*)" 44 | ++ "(\\/\\* @content \\*\\/)([\\s\\S]*)" 45 | ++ "(\\/\\* @example \\*\\/)([\\s\\S]*)" 46 | ); 47 | /* God i hate regex in Reason */ 48 | let segments = 49 | switch (Js.Re.exec(rawRe, pattern)) { 50 | | None => [||] 51 | | Some(result) => 52 | result 53 | |> Js.Re.captures 54 | |> Array.map(Js.Nullable.to_opt) 55 | |> Array.map(segment => 56 | switch segment { 57 | | None => "" 58 | | Some(x) => x 59 | } 60 | ) 61 | }; 62 | switch segments { 63 | | [| 64 | _all, 65 | _titleHeader, 66 | title, 67 | _titleEnd, 68 | _sectionHeader, 69 | section, 70 | _sectionEnd, 71 | _descHeader, 72 | description, 73 | _descEnd, 74 | _typeHeader, 75 | typeSpec, 76 | _typeEnd, 77 | _contentHeader, 78 | content, 79 | _exampleHeader, 80 | example 81 | |] => { 82 | "id": id, 83 | "title": String.trim(title), 84 | "description": String.trim(description), 85 | "typeSpec": String.trim(typeSpec), 86 | "section": String.trim(section), 87 | "content": String.trim(content), 88 | "example": String.trim(example), 89 | "jsOutput": jsOutput, 90 | "objectID": name 91 | } 92 | | _ => { 93 | "id": "", 94 | "description": "", 95 | "title": "", 96 | "content": "", 97 | "example": "", 98 | "jsOutput": "", 99 | "section": "", 100 | "typeSpec": "", 101 | "objectID": name 102 | } 103 | }; 104 | }; 105 | let loadSnippets = () => 106 | listSnippetNames() 107 | |> Array.mapi((i, name) => 108 | createSnippet( 109 | ~id={j|$(i)-$(name)|j}, 110 | ~rawRe=loadSnippetRawRe(name), 111 | ~jsOutput=loadSnippetJsOutput(name), 112 | ~name, 113 | () 114 | ) 115 | ) 116 | |> Array.to_list 117 | |> List.filter(snippet => snippet##id === "" ? false : true) 118 | |> Array.of_list; 119 | }; 120 | 121 | module Store = { 122 | let algoliaClient = 123 | Algolia.Client.make( 124 | ~applicationId=Config.env.algoliaApplicationId, 125 | ~apiKey=Config.env.algoliaAPIKey, 126 | () 127 | ); 128 | let algoliaIndex = Algolia.Index.make("30s-snippets", algoliaClient); 129 | let local = Scraper.loadSnippets(); 130 | Algolia.Index.addObjects(local, algoliaIndex); 131 | let getByQuery = query => 132 | algoliaIndex 133 | |> Algolia.Index.search({"query": query}) 134 | |> Js.Promise.then_(x => Js.Promise.resolve(x##hits)); 135 | let getById = id => 136 | local 137 | |> Array.to_list 138 | |> List.filter(snippet => snippet##id == id) 139 | |> List.hd; 140 | }; 141 | 142 | module Handler = { 143 | type graphQLContext; 144 | type resolvers('root) = { 145 | queries: { 146 | . 147 | "allSnippets": 148 | ( 149 | Js.Nullable.t('root), 150 | {. "query": Js.Nullable.t(string)}, 151 | Js.t(graphQLContext) 152 | ) => 153 | Js.Promise.t(array(t)), 154 | "snippet": 155 | (Js.Nullable.t('root), {. "id": string}, Js.t(graphQLContext)) => t 156 | } 157 | }; 158 | let make = () => { 159 | queries: { 160 | "allSnippets": (_root, input, _context) => 161 | switch (Js.Nullable.to_opt(input##query)) { 162 | | Some(query) => Store.getByQuery(query) 163 | | None => Js.Promise.resolve([||]) 164 | }, 165 | "snippet": (_root, input, _context) => Store.getById(input##id) 166 | } 167 | }; 168 | }; 169 | -------------------------------------------------------------------------------- /src/utils.re: -------------------------------------------------------------------------------- 1 | let ele_of_str = ReasonReact.stringToElement; 2 | 3 | let ele_of_arr = ReasonReact.arrayToElement; 4 | 5 | let ele_of_list = l => l |> Array.of_list |> ele_of_arr; 6 | 7 | type debounceOptions = { 8 | . 9 | "leading": bool, 10 | "maxWait": int, 11 | "trailing": bool 12 | }; 13 | 14 | module Debounce = { 15 | [@bs.module] 16 | external _make : 17 | ('fArgs => 'fOutput, int, debounceOptions) => [@bs] ('fArgs => 'fOutput) = 18 | "lodash.debounce"; 19 | let make = (~wait=0, ~options=Js.Obj.empty(), f) => _make(f, wait, options); 20 | let call = (input, f) => [@bs] f(input); 21 | }; 22 | -------------------------------------------------------------------------------- /src/web/apolloBrowser.re: -------------------------------------------------------------------------------- 1 | type dataObject = { 2 | . 3 | "__typename": string, 4 | "id": string, 5 | "key": string 6 | }; 7 | 8 | let cache = ApolloInMemoryCache.createInMemoryCache(~dataIdFromObject=(obj: dataObject) => obj##id, ()); 9 | 10 | let link = ApolloLinks.createHttpLink(~uri="/graphql", ()); 11 | 12 | module Client = 13 | ReasonApollo.CreateClient( 14 | { 15 | let apolloClient = ReasonApollo.createApolloClient(~cache, ~link, ()); 16 | } 17 | ); 18 | -------------------------------------------------------------------------------- /src/web/app.re: -------------------------------------------------------------------------------- 1 | type state = {search: string}; 2 | 3 | type actions = 4 | | ChangeSearch(string); 5 | 6 | let changeSearch = send => { 7 | open Utils.Debounce; 8 | let update = make(newValue => send(ChangeSearch(newValue)), ~wait=250); 9 | newValue => update |> call(newValue); 10 | }; 11 | 12 | let reducer = (action, _state) => 13 | switch action { 14 | | ChangeSearch(newValue) => ReasonReact.Update({search: newValue}) 15 | }; 16 | 17 | let initialState = () => {search: ""}; 18 | 19 | let component = ReasonReact.reducerComponent("App"); 20 | 21 | let make = (~query, _children) => { 22 | ...component, 23 | initialState, 24 | reducer, 25 | render: ({state, send}) => 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /src/web/browser.re: -------------------------------------------------------------------------------- 1 | [@bs.val] [@bs.module "react-dom"] external hydrate : (ReasonReact.reactElement, Dom.element) => unit = "hydrate"; 2 | 3 | [@bs.val] [@bs.return nullable] external _getElementById : string => option(Dom.element) = "document.getElementById"; 4 | 5 | let hydrateToElementWithId = (reactElement, id) => 6 | switch (_getElementById(id)) { 7 | | None => 8 | raise(Invalid_argument("ReactDOMRE.renderToElementWithId : no element of id " ++ (id ++ " found in the HTML."))) 9 | | Some(element) => hydrate(reactElement, element) 10 | }; 11 | 12 | hydrateToElementWithId(, "react-root"); 13 | -------------------------------------------------------------------------------- /src/web/components/apolloContext.re: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/web/components/header.re: -------------------------------------------------------------------------------- 1 | open Nice; 2 | 3 | open Utils; 4 | 5 | open Theme; 6 | 7 | let component = ReasonReact.statelessComponent("Header"); 8 | 9 | module Wrapper = ( 10 | val NiceComponents.header( 11 | ~debugName="HeaderWrapper", 12 | [| 13 | BackgroundColor(Colors.red), 14 | Color(Colors.white), 15 | Padding(Spacing.large), 16 | Raw("font-size", Font.Size.large) 17 | |] 18 | ) 19 | ); 20 | 21 | module Logo = ( 22 | val NiceComponents.div( 23 | ~debugName="HeaderLogo", 24 | [|Color(Colors.white), Raw("display", "inline"), FontWeight(Bold)|] 25 | ) 26 | ); 27 | 28 | module Nav = ( 29 | val NiceComponents.ol( 30 | ~debugName="HeaderNav", 31 | [| 32 | Raw("list-style", "none"), 33 | Raw("float", "right"), 34 | Padding(Spacing.none), 35 | Margin(Spacing.none) 36 | |] 37 | ) 38 | ); 39 | 40 | module NavItem = ( 41 | val NiceComponents.li( 42 | ~debugName="HeaderNavItem", 43 | [|Raw("float", "right"), MarginLeft(Spacing.large)|] 44 | ) 45 | ); 46 | 47 | module NavLink = ( 48 | val NiceComponents.a( 49 | ~debugName="HeaderNavLink", 50 | [|Raw("color", "inherit"), TextDecorationLine(None_)|] 51 | ) 52 | ); 53 | 54 | let make = _children => { 55 | ...component, 56 | render: _self => 57 | 58 | 59 | 60 | (ele_of_str("30s of Reason")) 61 | 62 | 72 | 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /src/web/components/jsSnippet.re: -------------------------------------------------------------------------------- 1 | open Utils; 2 | 3 | module JsSnippetQuery = [%graphql 4 | {| 5 | query getJsSnippet($id: ID!) { 6 | snippet(id: $id) { 7 | jsOutput 8 | } 9 | } 10 | |} 11 | ]; 12 | 13 | let component = ReasonReact.statelessComponent("JsSnippet"); 14 | 15 | let title = "JavaScript Output"; 16 | 17 | let renderLoading = () => 18 | 19 | 20 | ; 21 | 22 | let renderFailed = () => ele_of_str("failed"); 23 | 24 | let renderLoaded = text => ; 25 | 26 | let make = (~id, _children) => { 27 | ...component, 28 | render: _self => { 29 | let query = JsSnippetQuery.make(~id, ()); 30 | 34 | switch response { 35 | | Loading => renderLoading() 36 | | Failed(_error) => renderFailed() 37 | | Loaded(result) => renderLoaded(parse(result)##snippet##jsOutput) 38 | } 39 | ) 40 | />; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/web/components/query.re: -------------------------------------------------------------------------------- 1 | open Utils; 2 | 3 | /* Currently has to be done because the response type is internal to the Query module */ 4 | type response = 5 | | Loading 6 | | Loaded(Js.Json.t) 7 | | Failed(string); 8 | 9 | let component = ReasonReact.statelessComponent("Query"); 10 | 11 | let make = (~query, ~render, _children) => { 12 | ...component, 13 | render: _self => 14 | 15 | ...( 16 | client => 17 | switch client { 18 | | None => ele_of_str("No Client") 19 | | Some(apolloQuery) => 20 | module ReasonApolloQuery = (val apolloQuery); 21 | 22 | ...( 23 | (response, parse) => 24 | switch response { 25 | | Loading => render(Loading, parse) 26 | | Loaded(x) => render(Loaded(x), parse) 27 | | Failed(x) => render(Failed(x), parse) 28 | } 29 | ) 30 | ; 31 | } 32 | ) 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /src/web/components/search.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | module Wrapper = (val NiceComponents.div([|Position(Relative)|])); 4 | 5 | module SearchIcon = ( 6 | val NiceComponents.div([| 7 | Position(Absolute), 8 | Top(Percent(50.)), 9 | Raw("transform", "translateY(-50%)"), 10 | PaddingLeft(Spacing.normal) 11 | |]) 12 | ); 13 | 14 | module Input = ( 15 | val NiceComponents.input( 16 | ~debugName="SearchInput", 17 | [| 18 | Raw("outline", "none"), 19 | Raw("border", "none"), 20 | Raw("box-sizing", "border-box"), 21 | BorderRadius(Frame.borderRadius), 22 | Display(Block), 23 | Width(Percent(100.)), 24 | Raw("box-shadow", Frame.Shadow.normal), 25 | Padding(Spacing.normal), 26 | PaddingLeft(Rem(3.)), 27 | MarginTop(Spacing.normal), 28 | Raw("font-size", Font.Size.normal), 29 | Raw("transition", Animation.Transition.normal), 30 | Select(":focus", [|Raw("box-shadow", Frame.Shadow.red)|]) 31 | |] 32 | ) 33 | ); 34 | 35 | type state = {value: string}; 36 | 37 | type action = 38 | | ChangeValue(string); 39 | 40 | let component = ReasonReact.reducerComponent("Search"); 41 | 42 | let reducer = (action, _state) => 43 | switch action { 44 | | ChangeValue(newValue) => ReasonReact.Update({value: newValue}) 45 | }; 46 | 47 | let make = (~initialValue="", ~onChange=_newValue => (), _children) => { 48 | ...component, 49 | initialState: () => {value: initialValue}, 50 | reducer, 51 | render: ({send, state}) => 52 | 53 |
54 | { 58 | let value = e##target##value; 59 | send(ChangeValue(value)); 60 | onChange(value); 61 | } 62 | } 63 | /> 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /src/web/components/snippetItem.re: -------------------------------------------------------------------------------- 1 | open Utils; 2 | 3 | open Theme; 4 | 5 | module Toggle = ( 6 | val NiceComponents.span( 7 | ~debugName="SnippetToggle", 8 | [| 9 | Raw("float", "right"), 10 | Color(Colors.red), 11 | Raw("transition", Animation.Transition.normal), 12 | Padding(Spacing.small), 13 | BorderRadius(Frame.borderRadius), 14 | Select( 15 | ":hover", 16 | [| 17 | BackgroundColor(Colors.red), 18 | Color(Colors.white), 19 | Raw("box-shadow", Frame.Shadow.red) 20 | |] 21 | ) 22 | |] 23 | ) 24 | ); 25 | 26 | type state = {jsOutput: bool}; 27 | 28 | let initialState = () => {jsOutput: false}; 29 | 30 | type action = 31 | | ToggleJsOutput; 32 | 33 | let reducer = (action, state) => 34 | switch action { 35 | | ToggleJsOutput => ReasonReact.Update({jsOutput: ! state.jsOutput}) 36 | }; 37 | 38 | let component = ReasonReact.reducerComponent("Snippet"); 39 | 40 | let make = (~id, ~title, ~description, ~content, ~typeSpec, _children) => { 41 | ...component, 42 | initialState, 43 | reducer, 44 | render: ({state: {jsOutput}, send}) => 45 | 46 | send(ToggleJsOutput)}> 47 | (jsOutput ? ele_of_str("Hide JS") : ele_of_str("Show JS")) 48 | 49 |

(ele_of_str(title))

50 |

(ele_of_str(description))

51 | 52 | 53 | (jsOutput ? : ele_of_str("")) 54 |
55 | }; 56 | 57 | module Loading = { 58 | let component = ReasonReact.statelessComponent("SnippetLoading"); 59 | let make = _children => { 60 | ...component, 61 | render: _self => 62 | 63 | 64 |

(ele_of_str("..."))

65 | 66 |
67 |
68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /src/web/components/snippetList.re: -------------------------------------------------------------------------------- 1 | open Utils; 2 | 3 | module SnippetQuery = [%graphql 4 | {| 5 | query getAllSnippets($filter: String!) { 6 | allSnippets(query: $filter) { 7 | content, 8 | title, 9 | description, 10 | id, 11 | section, 12 | typeSpec 13 | } 14 | } 15 | |} 16 | ]; 17 | 18 | let rec createSections = (~sections=[], snippets) => 19 | switch snippets { 20 | | [] => List.rev(sections) 21 | | [hd, ..._] => 22 | let (newSection, tl) = 23 | List.partition(x => x##section === hd##section, snippets); 24 | createSections(tl, ~sections=[newSection, ...sections]); 25 | }; 26 | 27 | let rec filterOutVariant = (acc, l) => 28 | switch l { 29 | | [] => List.rev(acc) 30 | | [hd, ...tl] => 31 | switch hd { 32 | | None => filterOutVariant(acc, tl) 33 | | Some(x) => filterOutVariant([x, ...acc], tl) 34 | } 35 | }; 36 | 37 | let renderSections = snippets => 38 | snippets 39 | |> List.map( 40 | fun 41 | | [] => [] 42 | | [hd, ..._] as section => { 43 | let sectionName = hd##section; 44 | [ 45 |

46 | (ele_of_str(sectionName)) 47 |

, 48 | ...section 49 | |> List.map(x => 50 | 58 | ) 59 | ]; 60 | } 61 | ) 62 | |> List.flatten; 63 | 64 | let component = ReasonReact.statelessComponent("SnippetList"); 65 | 66 | let renderLoading = () => 67 | [ 68 |

(ele_of_str("Loading"))

, 69 | , 70 | 71 | ] 72 | |> ele_of_list; 73 | 74 | let renderFailed = () => ele_of_str("error"); 75 | 76 | let renderLoaded = snippets => 77 | snippets 78 | |> Array.to_list 79 | |> filterOutVariant([]) 80 | |> createSections 81 | |> renderSections 82 | |> ele_of_list; 83 | 84 | let make = (~filter="", _children) => { 85 | ...component, 86 | render: _self => { 87 | let snippetQuery = SnippetQuery.make(~filter, ()); 88 | 89 | 93 | switch response { 94 | | Loading => renderLoading() 95 | | Failed(_error) => renderFailed() 96 | | Loaded(result) => renderLoaded(parse(result)##allSnippets) 97 | } 98 | ) 99 | /> 100 | ; 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /src/web/elements/background.re: -------------------------------------------------------------------------------- 1 | include ( 2 | val NiceComponents.div( 3 | ~debugName="Background", 4 | [|MinHeight(Vh(100.)), FontFamily("Karla, sans-serif"), BackgroundColor(Theme.Colors.secondary), Height(Percent(100.))|] 5 | ) 6 | ); 7 | -------------------------------------------------------------------------------- /src/web/elements/card.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.div( 5 | ~debugName="Card", 6 | [| 7 | Raw("box-shadow", Frame.Shadow.normal), 8 | BackgroundColor(White), 9 | BorderRadius(Frame.borderRadius), 10 | Margin(Spacing.none), 11 | MarginTop(Spacing.normal), 12 | MarginBottom(Spacing.normal), 13 | Padding(Spacing.normal), 14 | PaddingBottom(Px(1)), 15 | |] 16 | ) 17 | ); 18 | -------------------------------------------------------------------------------- /src/web/elements/code.re: -------------------------------------------------------------------------------- 1 | open Utils; 2 | 3 | open Theme; 4 | 5 | module Wrapper = ( 6 | val NiceComponents.code( 7 | ~debugName="Code", 8 | [| 9 | Display(Block), 10 | Padding(Spacing.normal), 11 | PaddingTop(Spacing.none), 12 | BorderRadius(Frame.borderRadius), 13 | MarginBottom(Spacing.normal), 14 | BackgroundColor(Colors.black), 15 | Color(Colors.gray), 16 | Raw("box-shadow", Frame.Shadow.normal), 17 | Raw("font-size", "1rem"), 18 | LineHeight(1.5) 19 | |] 20 | ) 21 | ); 22 | 23 | module Line = ( 24 | val NiceComponents.div(~debugName="Line", [|Raw("white-space", "pre")|]) 25 | ); 26 | 27 | module LineNumber = ( 28 | val NiceComponents.span( 29 | ~debugName="LineNumber", 30 | [| 31 | MinWidth(Rem(2.)), 32 | Display(InlineBlock), 33 | Raw("user-select", "none"), 34 | Color(Colors.darkGray) 35 | |] 36 | ) 37 | ); 38 | 39 | module Language = ( 40 | val NiceComponents.span( 41 | ~debugName="Language", 42 | [|Raw("float", "right"), Color(Colors.red)|] 43 | ) 44 | ); 45 | 46 | module Header = ( 47 | val NiceComponents.div( 48 | ~debugName="CodeHeader", 49 | [| 50 | MarginBottom(Spacing.small), 51 | PaddingBottom(Spacing.small), 52 | PaddingTop(Spacing.small), 53 | Raw("border-bottom", "solid 1px gray") 54 | |] 55 | ) 56 | ); 57 | 58 | let component = ReasonReact.statelessComponent("Code"); 59 | 60 | let make = (~text, ~language="re", ~title="Sample ReasonML", _children) => { 61 | ...component, 62 | render: _self => 63 | 64 |
65 | (ele_of_str(title)) 66 | (ele_of_str(language)) 67 |
68 | ( 69 | ele_of_arr( 70 | text 71 | |> Js.String.split("\n") 72 | |> Array.mapi((i, line) => 73 | 74 | (ele_of_str(string_of_int(i))) 75 | (ele_of_str(line)) 76 | 77 | ) 78 | ) 79 | ) 80 |
81 | }; 82 | -------------------------------------------------------------------------------- /src/web/elements/divider.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.div(~debugName="divider", [|Height(Px(1)), BackgroundColor(Colors.red), Margin(Spacing.normal)|]) 5 | ); 6 | -------------------------------------------------------------------------------- /src/web/elements/h1.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.h1( 5 | ~debugName="H1", 6 | [| 7 | Raw("font-size", Font.Size.huge), 8 | MarginBottom(Spacing.normal), 9 | MarginTop(Spacing.large), 10 | Color(Colors.red) 11 | |] 12 | ) 13 | ); 14 | -------------------------------------------------------------------------------- /src/web/elements/h2.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.h2( 5 | ~debugName="H2", 6 | [| 7 | Raw("font-size", Font.Size.large), 8 | Margin(Spacing.none), 9 | MarginBottom(Spacing.normal), 10 | MarginTop(Spacing.large), 11 | Color(Colors.red) 12 | |] 13 | ) 14 | ); 15 | -------------------------------------------------------------------------------- /src/web/elements/h3.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.h3(~debugName="H3", [|Raw("font-size", Font.Size.normal), Margin(Px(0)), MarginBottom(Rem(1.))|]) 5 | ); 6 | -------------------------------------------------------------------------------- /src/web/elements/h4.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.h4(~debugName="H4", [|Raw("font-size", Font.Size.small), Margin(Px(0)), MarginBottom(Rem(1.))|]) 5 | ); 6 | -------------------------------------------------------------------------------- /src/web/elements/loadingAnimation.re: -------------------------------------------------------------------------------- 1 | include ( 2 | val NiceComponents.div( 3 | ~debugName="LoadingTransition", 4 | [|Raw("animation", "loadingAnimation 3s infinite ease")|] 5 | ) 6 | ); 7 | -------------------------------------------------------------------------------- /src/web/elements/p.re: -------------------------------------------------------------------------------- 1 | open Theme; 2 | 3 | include ( 4 | val NiceComponents.p( 5 | ~debugName="P", 6 | [|Margin(Spacing.none), MarginBottom(Spacing.normal)|] 7 | ) 8 | ); 9 | -------------------------------------------------------------------------------- /src/web/elements/pageFrame.re: -------------------------------------------------------------------------------- 1 | include ( 2 | val NiceComponents.div( 3 | ~debugName="PageFrame", 4 | [|MaxWidth(Px(960)), Raw("margin", "auto")|] 5 | ) 6 | ); 7 | -------------------------------------------------------------------------------- /src/web/template.re: -------------------------------------------------------------------------------- 1 | let globalStyles = "body { margin: 0; padding: 0;} @keyframes loadingAnimation { 0%, 100% { opacity: 0.25;} 50% {opacity:\n0.5;}}"; 2 | 3 | let make = (~body, ~styles, ~title, ()) => {j| 4 | 5 | 6 | 7 | $title 8 | 9 | $styles 10 | 11 | 12 | 13 |
$body
14 | 15 | 16 | 17 | 18 | 19 | |j}; 20 | 21 | let generateStyles = (~html="", ()) => { 22 | let css = NiceComponents.getStyles(html); 23 | {j||j}; 24 | }; 25 | -------------------------------------------------------------------------------- /src/web/theme.re: -------------------------------------------------------------------------------- 1 | open Nice; 2 | 3 | module Colors = { 4 | let red = Hex("db4d3f"); 5 | let lightRed = Hex("F2826E"); 6 | let secondary = Hex("f8faff"); 7 | let white = White; 8 | let black = Hex("282C34"); 9 | let gray = Hex("ABB2BF"); 10 | let darkGray = Hex("5C6370"); 11 | }; 12 | 13 | module Font = { 14 | let family = "Karla, sans-serif"; 15 | module Size = { 16 | let huge = "2rem"; 17 | let large = "1.5rem"; 18 | let normal = "1.25rem"; 19 | let small = "1rem"; 20 | }; 21 | }; 22 | 23 | module Spacing = { 24 | let normal = Rem(1.); 25 | let small = Rem(0.5); 26 | let none = Rem(0.); 27 | let large = Rem(2.0); 28 | }; 29 | 30 | module Frame = { 31 | let borderRadius = Rem(0.3); 32 | module Shadow = { 33 | let normal = "0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06)"; 34 | let darker = "0 10px 40px 0 rgba(62,57,107,0.20), 0 2px 9px 0 rgba(62,57,107,0.20)"; 35 | let red = "0 10px 40px 0 rgba(219,77,63,0.37), 0 2px 9px 0 rgba(219,77,63,0.36);"; 36 | }; 37 | 38 | }; 39 | 40 | module Animation = { 41 | module Transition = { 42 | let normal = "0.25s ease"; 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "npm", 4 | "options": { 5 | "cwd": "${workspaceRoot}" 6 | }, 7 | "isShellCommand": true, 8 | "args": [ 9 | "run", 10 | "watch" 11 | ], 12 | "showOutput": "always", 13 | "isBackground": true, 14 | "problemMatcher": { 15 | "fileLocation": "absolute", 16 | "owner": "ocaml", 17 | "watching": { 18 | "activeOnStart": false, 19 | "beginsPattern": ">>>> Start compiling", 20 | "endsPattern": ">>>> Finish compiling" 21 | }, 22 | "pattern": [ 23 | { 24 | "regexp": "^File \"(.*)\", line (\\d+)(?:, characters (\\d+)-(\\d+))?:$", 25 | "file": 1, 26 | "line": 2, 27 | "column": 3, 28 | "endColumn": 4 29 | }, 30 | { 31 | "regexp": "^(?:(?:Parse\\s+)?(Warning|[Ee]rror)(?:\\s+\\d+)?:)?\\s+(.*)$", 32 | "severity": 1, 33 | "message": 2, 34 | "loop": true 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const UglifyWebpackPlugin = require("uglifyjs-webpack-plugin"); 4 | 5 | const dir = p => path.resolve(__dirname, p); 6 | module.exports = { 7 | entry: "./lib/js/src/web/browser.bs.js", 8 | output: { 9 | path: dir("./dist"), 10 | publicPath: "/", 11 | filename: "bundle.js" 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.js$/, 17 | exclude: /node_modules/ 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | extensions: [".js", ".bs.js"] 23 | } 24 | }; 25 | --------------------------------------------------------------------------------