Skip to content

Commit

Permalink
chore: types
Browse files Browse the repository at this point in the history
  • Loading branch information
thepassle committed Apr 3, 2024
1 parent d09f0be commit c0a2346
Show file tree
Hide file tree
Showing 17 changed files with 422 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.DS_Store
.DS_Store
packages/*/dist-types/
24 changes: 19 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
"url": "https://github.com/thepassle/swtl.git"
},
"scripts": {
"test": "npm run test --workspaces"
"test": "npm run test --workspaces",
"lint:types": "npm run lint:types --workspaces"
},
"dependencies": {},
"devDependencies": {
"esbuild": "^0.19.0",
"lit": "^3.1.2"
"lit": "^3.1.2",
"typescript": "^5.4.3"
},
"workspaces": [
"packages/*"
]
}
}
23 changes: 22 additions & 1 deletion packages/core/await.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import { AWAIT_SYMBOL } from './symbol.js';

/**
* @typedef {import('./types.js').Children} Children
* @typedef {import('./html.js').html} html
*/

/**
* @param {{
* promise: () => Promise<unknown>,
* children: Children
* }} args
* @returns {{
* promise: () => Promise<unknown>,
* template: () => ReturnType<html>
* }}
*/
function Await({promise, children}) {
return {
promise,
template: children.find(c => typeof c === 'function')
template: /** @type {() => ReturnType<html>} */ (children.find(c => typeof c === 'function'))
};
}

Await.kind = AWAIT_SYMBOL;

/**
*
* @param {boolean} condition
* @param {() => ReturnType<html>} template
* @returns
*/
const when = (condition, template) => condition ? template() : '';

export { Await, when };
61 changes: 47 additions & 14 deletions packages/core/html.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { COMPONENT_SYMBOL, CUSTOM_ELEMENT_SYMBOL } from './symbol.js';

/**
* @typedef {import('./types.js').Component} Component
* @typedef {import('./types.js').CustomElement} CustomElement
* @typedef {import('./types.js').HtmlResult} HtmlResult
*/

const TEXT = 'TEXT';
const COMPONENT = 'COMPONENT';
const TAG_OPEN = 'TAG_OPEN';
Expand All @@ -12,13 +18,31 @@ const SET_PROP = 'SET_PROP';
const PROP_VAL = 'PROP_VAL';

const customElementTagRegex = /^([a-z0-9]+-[a-z0-9-]*)/;
/** @param {string} t */
const noSelfClosing = t => `Custom elements cannot be self-closing: "${t}"`;

/**
* @param {TemplateStringsArray} statics
* @param {...unknown} dynamics
* @returns {HtmlResult}
*/
export function* html(statics, ...dynamics) {
/**
* @type {TEXT | COMPONENT | TAG_OPEN}
*/
let MODE = TEXT;
/**
* @type {NONE | PROP | CHILDREN}
*/
let COMPONENT_MODE = NONE;
/**
* @type {SET_PROP | PROP_VAL | NONE}
*/
let PROP_MODE = NONE;

/**
* @type {Array<Component | CustomElement>}
*/
const componentStack = [];

/**
Expand All @@ -30,6 +54,9 @@ export function* html(statics, ...dynamics) {
for (let i = 0; i < statics.length; i++) {
let result = "";
let tag = "";
/**
* @type {Component}
*/
const component = {
kind: COMPONENT_SYMBOL,
slots: {},
Expand Down Expand Up @@ -59,7 +86,7 @@ export function* html(statics, ...dynamics) {
!statics[i][j + 1] && typeof dynamics[i] === "function"
) {
MODE = COMPONENT;
component.fn = dynamics[i];
component.fn = /** @type {Component["fn"]} */ (dynamics[i]);
componentStack.push(component);
} else {
result += c;
Expand Down Expand Up @@ -126,6 +153,7 @@ export function* html(statics, ...dynamics) {
if (COMPONENT_MODE === PROP) {
const component = componentStack[componentStack.length - 1];
const attrOrProp = component?.kind === COMPONENT_SYMBOL ? 'properties' : 'attributes';
/** @ts-expect-error */
const property = component?.[`${attrOrProp}`][component[`${attrOrProp}`].length - 1];
if (PROP_MODE === SET_PROP) {
let property = "";
Expand Down Expand Up @@ -155,7 +183,7 @@ export function* html(statics, ...dynamics) {
} else if (statics[i][j] === "/" && COMPONENT_MODE === PROP) {
COMPONENT_MODE = NONE;
PROP_MODE = NONE;
const component = componentStack.pop();
const component = /** @type {Component} */ (componentStack.pop());
if (!componentStack.length) {
result = '';
yield component;
Expand All @@ -170,8 +198,10 @@ export function* html(statics, ...dynamics) {
}

if (property === '...') {
/** @ts-expect-error */
component[`${attrOrProp}`].push(...Object.entries(dynamics[i]).map(([name,value])=> ({name, value})));
} else if (property) {
/** @ts-expect-error */
component[`${attrOrProp}`].push({name: property, value: true});
}
} else if (PROP_MODE === PROP_VAL) {
Expand Down Expand Up @@ -228,8 +258,8 @@ export function* html(statics, ...dynamics) {
* Yield if we finished the component
* Swtl Component only, custom elements can't be self-closing
*/
} else if (statics[i][j] === '/' && componentStack.at(-1).kind === COMPONENT_SYMBOL) {
const component = componentStack.pop();
} else if (statics[i][j] === '/' && componentStack.at(-1)?.kind === COMPONENT_SYMBOL) {
const component = /** @type {Component} */ (componentStack.pop());
if (!componentStack.length) {
PROP_MODE = NONE;
COMPONENT_MODE = NONE;
Expand Down Expand Up @@ -266,17 +296,18 @@ export function* html(statics, ...dynamics) {
* Yield if we finished the component
* Swtl Component only, custom elements can't be self-closing
*/
if (statics[i][j] === '/' && componentStack.at(-1).kind === COMPONENT_SYMBOL) {
const component = componentStack.pop();
if (statics[i][j] === '/' && componentStack.at(-1)?.kind === COMPONENT_SYMBOL) {
const component = /** @type {Component} */ (componentStack.pop());
if (!componentStack.length) {
yield component;
}
/**
* @example <my-el bar=hi/>
* ^
*/
} else if (statics[i][j] === '/' && componentStack.at(-1).kind === CUSTOM_ELEMENT_SYMBOL) {
throw new Error(noSelfClosing(componentStack.at(-1).tag));
} else if (statics[i][j] === '/' && componentStack.at(-1)?.kind === CUSTOM_ELEMENT_SYMBOL) {
// @ts-expect-error we already know its a custom element because of the symbol
throw new Error(noSelfClosing(componentStack.at(-1)?.tag));
} else if (statics[i][j] === '>') {
result = "";
COMPONENT_MODE = CHILDREN;
Expand All @@ -302,7 +333,7 @@ export function* html(statics, ...dynamics) {
* If there are no components on the stack, this is a top level
* component, and we can yield
*/
const component = componentStack.pop();
const component = /** @type {Component} */ (componentStack.pop());
if (!componentStack.length) {
MODE = TEXT;
COMPONENT_MODE = NONE;
Expand All @@ -318,7 +349,7 @@ export function* html(statics, ...dynamics) {
}
COMPONENT_MODE = PROP;
PROP_MODE = SET_PROP;
component.fn = dynamics[i];
component.fn = /** @type {Component["fn"]} */ (dynamics[i]);
componentStack.push(component);
} else if (!statics[i][j+1]) {
/**
Expand Down Expand Up @@ -346,7 +377,7 @@ export function* html(statics, ...dynamics) {
* If there are no components on the stack, this is a top level
* component, and we can yield
*/
const component = componentStack.pop();
const component = /** @type {Component | CustomElement} */ (componentStack.pop());
if (!componentStack.length) {
MODE = TEXT;
COMPONENT_MODE = NONE;
Expand All @@ -356,7 +387,9 @@ export function* html(statics, ...dynamics) {
* Otherwise we need to add the component to the parent's children
*/
const parentComponent = componentStack[componentStack.length - 1];
parentComponent.children.push(component);
if (component) {
parentComponent.children.push(component);
}
}
}
} else if (statics[i][j] === '<') {
Expand Down Expand Up @@ -399,14 +432,14 @@ export function* html(statics, ...dynamics) {
} else if (c === " ") {
COMPONENT_MODE = PROP;
PROP_MODE = SET_PROP;
} else if (c === "/" && statics[i][j + 1] === ">" && componentStack.at(-1).kind === COMPONENT_SYMBOL) {
} else if (c === "/" && statics[i][j + 1] === ">" && componentStack.at(-1)?.kind === COMPONENT_SYMBOL) {
MODE = TEXT;
COMPONENT_MODE = NONE;
/**
* If there are no components on the stack, this is a top level
* component, and we can yield
*/
const component = componentStack.pop();
const component = /** @type {Component} */ (componentStack.pop());
if (!componentStack.length) {
result = '';
yield component;
Expand Down
16 changes: 16 additions & 0 deletions packages/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,19 @@ export {
CacheOnly,
NetworkOnly,
} from './strategies.js';

/**
* @typedef {import('./types.js').Attribute} Attribute
* @typedef {import('./types.js').Property} Property
* @typedef {import('./types.js').HtmlValue} HtmlValue
* @typedef {import('./types.js').Children} Children
* @typedef {import('./types.js').HtmlResult} HtmlResult
* @typedef {import('./types.js').Component} Component
* @typedef {import('./types.js').CustomElement} CustomElement
* @typedef {import('./types.js').CustomElementRenderer} CustomElementRenderer
* @typedef {import('./types.js').RouteResult} RouteResult
* @typedef {import('./types.js').RouteArgs} RouteArgs
* @typedef {import('./types.js').Plugin} Plugin
* @typedef {import('./types.js').Route} Route
* @typedef {import('./types.js').MatchedRoute} MatchedRoute
*/
48 changes: 37 additions & 11 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
{
"name": "swtl",
"version": "0.3.1",
"version": "0.3.2",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "esbuild ./demo/sw.js --bundle --outfile=./demo/bundled-sw.js --watch --format=iife --servedir=demo",
"start": "node --watch dev.js",
"test": "node --test test/*.test.js",
"test:watch": "node --watch --test tests/tests.js"
"test:watch": "node --watch --test tests/tests.js",
"lint:types": "tsc",
"lint:types:watch": "tsc --watch"
},
"exports": {
".": "./index.js",
"./html.js": "./html.js",
"./await.js": "./await.js",
"./render.js": "./render.js",
"./router.js": "./router.js",
"./strategies.js": "./strategies.js",
"./slot.js": "./slot.js",
"./ssr/*.js": "./ssr/*.js",
".": {
"types": "./dist-types/index.d.ts",
"default": "./index.js"
},
"./html.js": {
"types": "./dist-types/html.d.ts",
"default": "./html.js"
},
"./await.js": {
"types": "./dist-types/await.d.ts",
"default": "./await.js"
},
"./render.js": {
"types": "./dist-types/render.d.ts",
"default": "./render.js"
},
"./router.js": {
"types": "./dist-types/router.d.ts",
"default": "./router.js"
},
"./strategies.js": {
"types": "./dist-types/strategies.d.ts",
"default": "./strategies.js"
},
"./slot.js": {
"types": "./dist-types/slot.d.ts",
"default": "./slot.js"
},
"./ssr/*.js": {
"types": "./dist-types/ssr/*.d.ts",
"default": "./ssr/*.js"
},
"./package.json": "./package.json"
},
"files": [
Expand All @@ -37,7 +63,7 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@swtl/lit": "^0.1.3"
"@swtl/lit": "^0.1.4"
},
"dependencies": {}
}
Loading

0 comments on commit c0a2346

Please sign in to comment.