typescript(monadic): feat: basic stuff
Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
parent
7bdf572b2d
commit
fd81cf068e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"id":"index.html","dependencies":[{"name":"./main.ts","dynamic":true,"resolved":"/home/adrielus/Projects/monadic/demos/basic/main.ts","parent":"/home/adrielus/Projects/monadic/demos/basic/index.html"}],"generated":{"html":"<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Monadic-ui basic</title>\n </head>\n <body>\n <div id=\"app\"></div>\n <script src=\"/6427dc610ec40c788cba04a9c39d6dcf.js\"></script>\n </body>\n</html>\n"},"sourceMaps":null,"error":null,"hash":"0737204c3508dc612eec91e4e9a1165d","cacheData":{}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"id":"node_modules/lit-html/lib/part.js","dependencies":[{"name":"/home/adrielus/Projects/monadic/demos/basic/node_modules/lit-html/lib/part.js.map","includedInParent":true,"mtime":499162500000},{"name":"/home/adrielus/Projects/monadic/demos/basic/package.json","includedInParent":true,"mtime":1587734964624},{"name":"/home/adrielus/Projects/monadic/demos/basic/node_modules/lit-html/package.json","includedInParent":true,"mtime":499162500000}],"generated":{"js":"\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.nothing = exports.noChange = void 0;\n\n/**\n * @license\n * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\n/**\n * A sentinel value that signals that a value was handled by a directive and\n * should not be written to the DOM.\n */\nconst noChange = {};\n/**\n * A sentinel value that signals a NodePart to fully clear its content.\n */\n\nexports.noChange = noChange;\nconst nothing = {};\nexports.nothing = nothing;"},"sourceMaps":{"js":{"mappings":[{"source":"../src/lib/part.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":8,"column":0}},{"source":"../src/lib/part.ts","name":null,"original":{"line":45,"column":0},"generated":{"line":22,"column":0}},{"source":"../src/lib/part.ts","name":null,"original":{"line":49,"column":7},"generated":{"line":26,"column":0}},{"source":"../src/lib/part.ts","name":null,"original":{"line":49,"column":13},"generated":{"line":26,"column":6}},{"source":"../src/lib/part.ts","name":null,"original":{"line":49,"column":21},"generated":{"line":26,"column":14}},{"source":"../src/lib/part.ts","name":null,"original":{"line":49,"column":24},"generated":{"line":26,"column":17}},{"source":"../src/lib/part.ts","name":null,"original":{"line":49,"column":7},"generated":{"line":26,"column":19}},{"source":"../src/lib/part.ts","name":null,"original":{"line":51,"column":0},"generated":{"line":27,"column":0}},{"source":"../src/lib/part.ts","name":null,"original":{"line":54,"column":7},"generated":{"line":32,"column":0}},{"source":"../src/lib/part.ts","name":null,"original":{"line":54,"column":13},"generated":{"line":32,"column":6}},{"source":"../src/lib/part.ts","name":null,"original":{"line":54,"column":20},"generated":{"line":32,"column":13}},{"source":"../src/lib/part.ts","name":null,"original":{"line":54,"column":23},"generated":{"line":32,"column":16}},{"source":"../src/lib/part.ts","name":null,"original":{"line":54,"column":7},"generated":{"line":32,"column":18}}],"sources":{"../src/lib/part.ts":"/**\n * @license\n * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at\n * http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at\n * http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at\n * http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at\n * http://polymer.github.io/PATENTS.txt\n */\n\n/**\n * @module lit-html\n */\n\n/**\n * The Part interface represents a dynamic part of a template instance rendered\n * by lit-html.\n */\nexport interface Part {\n readonly value: unknown;\n\n /**\n * Sets the current part value, but does not write it to the DOM.\n * @param value The value that will be committed.\n */\n setValue(value: unknown): void;\n\n /**\n * Commits the current part value, causing it to actually be written to the\n * DOM.\n *\n * Directives are run at the start of `commit`, so that if they call\n * `part.setValue(...)` synchronously that value will be used in the current\n * commit, and there's no need to call `part.commit()` within the directive.\n * If directives set a part value asynchronously, then they must call\n * `part.commit()` manually.\n */\n commit(): void;\n}\n\n/**\n * A sentinel value that signals that a value was handled by a directive and\n * should not be written to the DOM.\n */\nexport const noChange = {};\n\n/**\n * A sentinel value that signals a NodePart to fully clear its content.\n */\nexport const nothing = {};\n"},"lineCount":null}},"error":null,"hash":"05fa49157353fce29752c1b0c5b69944","cacheData":{"env":{}}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"id":"../../src/index.ts","dependencies":[{"name":"/home/adrielus/Projects/monadic/tsconfig.json","includedInParent":true,"mtime":1587733898427},{"name":"/home/adrielus/Projects/monadic/demos/basic/package.json","includedInParent":true,"mtime":1587734964624},{"name":"./environment","loc":{"line":1,"column":14},"parent":"/home/adrielus/Projects/monadic/src/index.ts","resolved":"/home/adrielus/Projects/monadic/src/environment.ts"}],"generated":{"js":"\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _environment = require(\"./environment\");\n\nObject.keys(_environment).forEach(function (key) {\n if (key === \"default\" || key === \"__esModule\") return;\n Object.defineProperty(exports, key, {\n enumerable: true,\n get: function () {\n return _environment[key];\n }\n });\n});"},"sourceMaps":{"js":{"mappings":[{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":7,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":9,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":10,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":11,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":12,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":13,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":14,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":15,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":16,"column":0}},{"source":"../../src/index.ts","name":null,"original":{"line":1,"column":0},"generated":{"line":17,"column":0}}],"sources":{"../../src/index.ts":"export * from './environment';\n"},"lineCount":null}},"error":null,"hash":"4a985b7aa5441bcd69670f6f7e81fde5","cacheData":{"env":{}}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
typescript/monadic/.vscode/settings.json
vendored
Normal file
8
typescript/monadic/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"adriel",
|
||||
"esnext",
|
||||
"matei",
|
||||
"tslib"
|
||||
]
|
||||
}
|
12
typescript/monadic/demos/basic/index.html
Normal file
12
typescript/monadic/demos/basic/index.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Monadic-ui basic</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
38
typescript/monadic/demos/basic/main.ts
Normal file
38
typescript/monadic/demos/basic/main.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { runUi } from '../../src';
|
||||
import { render, html, TemplateResult } from 'lit-html';
|
||||
|
||||
type Action = 'increase' | 'decrease';
|
||||
|
||||
type State = {
|
||||
count: number;
|
||||
};
|
||||
|
||||
runUi<TemplateResult, State, Action>({
|
||||
parent: document.getElementById('app'),
|
||||
render,
|
||||
initialState: { count: 0 },
|
||||
component: {
|
||||
render: (state, dispatch) => {
|
||||
return html`
|
||||
<div>
|
||||
<div>${state.count}</div>
|
||||
<button @click=${dispatch('increase')}>Increase</button>
|
||||
<button @click=${dispatch('decrease')}>Decrease</button>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
handleActions: (action: Action, state) => {
|
||||
if (action === 'increase') {
|
||||
return {
|
||||
count: state.count + 1,
|
||||
};
|
||||
} else if (action === 'decrease') {
|
||||
return {
|
||||
count: state.count - 1,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
},
|
||||
});
|
14
typescript/monadic/demos/basic/package.json
Normal file
14
typescript/monadic/demos/basic/package.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "basic",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Matei Adriel",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"lit-html": "^1.2.1"
|
||||
}
|
||||
}
|
10
typescript/monadic/demos/basic/pnpm-lock.yaml
Normal file
10
typescript/monadic/demos/basic/pnpm-lock.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
dependencies:
|
||||
lit-html: 1.2.1
|
||||
lockfileVersion: 5.1
|
||||
packages:
|
||||
/lit-html/1.2.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-GSJHHXMGLZDzTRq59IUfL9FCdAlGfqNp/dEa7k7aBaaWD+JKaCjsAk9KYm2V12ItonVaYx2dprN66Zdm1AuBTQ==
|
||||
specifiers:
|
||||
lit-html: ^1.2.1
|
|
@ -34,6 +34,7 @@
|
|||
"module": "dist/monadic.esm.js",
|
||||
"devDependencies": {
|
||||
"husky": "^4.2.5",
|
||||
"parcel": "^1.12.4",
|
||||
"tsdx": "^0.13.2",
|
||||
"tslib": "^1.11.1",
|
||||
"typescript": "^3.8.3"
|
||||
|
|
42
typescript/monadic/src/environment.ts
Normal file
42
typescript/monadic/src/environment.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { IterableEmitter } from './iterableEmitter';
|
||||
|
||||
export type EnvConfig<T, S, A> = {
|
||||
render: (template: T, parent: HTMLElement) => void;
|
||||
parent: HTMLElement;
|
||||
component: Component<T, S, A>;
|
||||
initialState: S;
|
||||
};
|
||||
|
||||
export type Component<T, S, A> = {
|
||||
render: (state: S, dispatch: (a: A) => () => void) => T;
|
||||
handleActions: (action: A, state: S) => S;
|
||||
};
|
||||
|
||||
async function* runComponent<T, S, A>(
|
||||
component: Component<T, S, A>,
|
||||
initialState: S
|
||||
): AsyncGenerator<T> {
|
||||
const emitter = new IterableEmitter(initialState);
|
||||
|
||||
const dispatch = (state: S) => (action: A) => {
|
||||
const newState = component.handleActions(action, state);
|
||||
|
||||
return () => {
|
||||
emitter.next(newState);
|
||||
};
|
||||
};
|
||||
|
||||
for await (const state of emitter) {
|
||||
yield component.render(state, dispatch(state));
|
||||
}
|
||||
}
|
||||
|
||||
export const runUi = async <T, S, A>(
|
||||
config: EnvConfig<T, S, A>
|
||||
): Promise<void> => {
|
||||
const component = runComponent(config.component, config.initialState);
|
||||
|
||||
for await (const template of component) {
|
||||
config.render(template, config.parent);
|
||||
}
|
||||
};
|
110
typescript/monadic/src/hkt.ts
Normal file
110
typescript/monadic/src/hkt.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
declare const index: unique symbol;
|
||||
|
||||
/**
|
||||
* Placeholder representing an indexed type variable.
|
||||
*/
|
||||
export interface _<N extends number = 0> {
|
||||
[index]: N;
|
||||
}
|
||||
export type _0 = _<0>;
|
||||
export type _1 = _<1>;
|
||||
export type _2 = _<2>;
|
||||
export type _3 = _<3>;
|
||||
export type _4 = _<4>;
|
||||
export type _5 = _<5>;
|
||||
export type _6 = _<6>;
|
||||
export type _7 = _<7>;
|
||||
export type _8 = _<8>;
|
||||
export type _9 = _<9>;
|
||||
|
||||
declare const fixed: unique symbol;
|
||||
|
||||
/**
|
||||
* Marks a type to be ignored by the application operator `$`. This is used to protect
|
||||
* bound type parameters.
|
||||
*/
|
||||
export interface Fixed<T> {
|
||||
[fixed]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type application (simultaneously substitutes all placeholders within the target type)
|
||||
*/
|
||||
// prettier-ignore
|
||||
export type $<T, S extends any[]> = (
|
||||
T extends Fixed<infer U> ? { [indirect]: U } :
|
||||
T extends _<infer N> ? { [indirect]: S[N] } :
|
||||
T extends undefined | null | boolean | string | number ? { [indirect]: T } :
|
||||
T extends (infer A)[] & { length: infer L } ? {
|
||||
[indirect]: L extends keyof TupleTable
|
||||
? TupleTable<T, S>[L]
|
||||
: $<A, S>[]
|
||||
} :
|
||||
T extends (...x: infer I) => infer O ? { [indirect]: (...x: $<I, S>) => $<O, S> } :
|
||||
T extends object ? { [indirect]: { [K in keyof T]: $<T[K], S> } } :
|
||||
{ [indirect]: T }
|
||||
)[typeof indirect];
|
||||
|
||||
/**
|
||||
* Used as a level of indirection to avoid circularity errors.
|
||||
*/
|
||||
declare const indirect: unique symbol;
|
||||
|
||||
/**
|
||||
* Allows looking up the type for a tuple based on its `length`, instead of trying
|
||||
* each possibility one by one in a single long conditional.
|
||||
*/
|
||||
// prettier-ignore
|
||||
type TupleTable<T extends any[] = any, S extends any[] = any> = {
|
||||
0: [];
|
||||
1: T extends [
|
||||
infer A0
|
||||
] ? [
|
||||
$<A0, S>
|
||||
] : never
|
||||
2: T extends [
|
||||
infer A0, infer A1
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>
|
||||
] : never
|
||||
3: T extends [
|
||||
infer A0, infer A1, infer A2
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>
|
||||
] : never
|
||||
4: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>
|
||||
] : never
|
||||
5: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>
|
||||
] : never
|
||||
6: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4, infer A5
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>, $<A5, S>
|
||||
] : never
|
||||
7: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>, $<A5, S>, $<A6, S>
|
||||
] : never
|
||||
8: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>, $<A5, S>, $<A6, S>, $<A7, S>
|
||||
] : never
|
||||
9: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>, $<A5, S>, $<A6, S>, $<A7, S>, $<A8, S>
|
||||
] : never
|
||||
10: T extends [
|
||||
infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, infer A9
|
||||
] ? [
|
||||
$<A0, S>, $<A1, S>, $<A2, S>, $<A3, S>, $<A4, S>, $<A5, S>, $<A6, S>, $<A7, S>, $<A8, S>, $<A9, S>
|
||||
] : never
|
||||
}
|
|
@ -1,6 +1 @@
|
|||
export const sum = (a: number, b: number) => {
|
||||
if ('development' === process.env.NODE_ENV) {
|
||||
console.log('boop');
|
||||
}
|
||||
return a + b;
|
||||
};
|
||||
export * from './environment';
|
||||
|
|
34
typescript/monadic/src/iterableEmitter.ts
Normal file
34
typescript/monadic/src/iterableEmitter.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
// An event emitter which can be used
|
||||
// with for of loops
|
||||
export class IterableEmitter<T> {
|
||||
// The generation of the emitter
|
||||
// Used to avoid trying to resolve a promise twice
|
||||
private generation = 0;
|
||||
|
||||
// Set the state
|
||||
public next = (_: T) => {};
|
||||
|
||||
public constructor(private state: T) {}
|
||||
|
||||
async *[Symbol.asyncIterator](): AsyncGenerator<T> {
|
||||
const createPromise = () =>
|
||||
new Promise<T>(resolve => {
|
||||
const generation = this.generation;
|
||||
|
||||
this.next = (value: T) => {
|
||||
if (generation !== this.generation) {
|
||||
throw new Error('Cannot resolve the same generation twice');
|
||||
}
|
||||
|
||||
this.generation++;
|
||||
resolve(value);
|
||||
};
|
||||
});
|
||||
|
||||
yield this.state;
|
||||
|
||||
while (true) {
|
||||
yield createPromise();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue