diff --git a/.eslintrc b/.eslintrc
index fbceddb..c6e03ed 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,11 +1,19 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
+ "parserOptions": {
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ },
"plugins": ["@typescript-eslint", "prettier"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
- ]
+ ],
+ "rules": {
+ "@typescript-eslint/no-namespace": "off"
+ }
}
diff --git a/src/build.ts b/src/build.ts
index 3e272ab..aa676f6 100644
--- a/src/build.ts
+++ b/src/build.ts
@@ -23,6 +23,7 @@ const renderPagesToHtml = async ({
// Ensure that we don't cache page modules when running in dev server.
purgeModuleAndDepsFromCache(srcPath);
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
const pageSrc = require(srcPath);
if (!("page" in pageSrc)) {
throw new Error(
diff --git a/src/cli/index.ts b/src/cli/index.ts
index c138e28..f7a6151 100644
--- a/src/cli/index.ts
+++ b/src/cli/index.ts
@@ -36,11 +36,12 @@ const parseArgs = (
const opt = args.shift();
switch (opt) {
case "-h":
- case "--help":
+ case "--help": {
options.showHelp = true;
break;
+ }
case "-r":
- case "--require":
+ case "--require": {
const moduleName = args.shift();
if (moduleName == null) {
throw new UsageError(
@@ -50,6 +51,7 @@ const parseArgs = (
}
options.require.push(moduleName);
break;
+ }
default:
throw new UsageError(`unknown option ${opt}`, globalHelpText);
}
diff --git a/src/component.ts b/src/component.ts
index f274d3b..7e579b8 100644
--- a/src/component.ts
+++ b/src/component.ts
@@ -30,7 +30,9 @@ export type Element = HTMLElement | string | boolean | undefined | null;
/**
* Custom HTMLElement factory that can be parameterized by props.
*/
-export interface Component
{
+export interface Component<
+ P extends Record = Record
+> {
(
props: P & {
children?: Element[];
diff --git a/src/config.ts b/src/config.ts
index a210b96..6baa45c 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -47,6 +47,7 @@ export interface Config {
watch: string[];
}
+// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
/**
diff --git a/src/create-element.ts b/src/create-element.ts
index 76cb95e..1163a9a 100644
--- a/src/create-element.ts
+++ b/src/create-element.ts
@@ -16,7 +16,7 @@ import { HTMLAttributes } from "./jsx";
*
* @return Fully-realized HTMLElement, ready for rendering.
*/
-export function createElement(
+export function createElement
>(
comp: Component
,
props: P,
...children: Element[]
@@ -37,8 +37,8 @@ export function createElement(
...children: Element[]
): HTMLElement;
export function createElement(
- type: string | Component,
- props: (HTMLAttributes & Record) | null,
+ type: string | Component>,
+ props: HTMLAttributes | Record | null,
...children: Element[]
): HTMLElement {
// Flatten the children array so we can accept arrays as children.
@@ -50,5 +50,19 @@ export function createElement(
if (type !== type.toLowerCase()) {
console.warn(`constructed HTML5 tag with non-lowercase name ${type}`);
}
- return { tag: type, attributes: props || {}, children: normalizedChildren };
+ const attrs: Record = {};
+ for (const [key, value] of Object.entries(props || {})) {
+ if (
+ typeof value !== "string" &&
+ typeof value !== "number" &&
+ typeof value !== "boolean"
+ ) {
+ console.warn(
+ `non-primitive attribute ${key} = ${JSON.stringify(value)}`
+ );
+ continue;
+ }
+ attrs[key] = value;
+ }
+ return { tag: type, attributes: attrs, children: normalizedChildren };
}
diff --git a/src/index.ts b/src/index.ts
index c82e7dd..2e5606b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,13 +6,4 @@
export { HTMLElement, Component } from "./component";
export { UserConfig as Config } from "./config";
export { createElement } from "./create-element";
-
-import { HTMLElement } from "./component";
-import { IntrinsicElements as JsxIntrinsics } from "./jsx";
-
-declare global {
- namespace JSX {
- type Element = HTMLElement;
- type IntrinsicElements = JsxIntrinsics;
- }
-}
+export * from "./jsx";
diff --git a/src/jsx.ts b/src/jsx.ts
index 4848c47..55d69a9 100644
--- a/src/jsx.ts
+++ b/src/jsx.ts
@@ -3,10 +3,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { HTMLElement } from "./component";
+
export interface RdfaAttributes {
about?: string;
datatype?: string;
- inlist?: any;
+ inlist?: boolean;
prefix?: string;
property?: string;
resource?: string;
@@ -60,7 +63,7 @@ export interface HTMLAttributes extends RdfaAttributes, MicrodataAttributes {
dir?: "auto" | "rtl" | "ltr";
disabled?: boolean;
disableRemotePlayback?: boolean;
- download?: any;
+ download?: boolean | string;
draggable?: boolean;
encType?: string;
form?: string;
@@ -154,120 +157,125 @@ export interface HTMLAttributes extends RdfaAttributes, MicrodataAttributes {
wrap?: string;
}
-export interface IntrinsicElements {
- a: HTMLAttributes;
- abbr: HTMLAttributes;
- address: HTMLAttributes;
- area: HTMLAttributes;
- article: HTMLAttributes;
- aside: HTMLAttributes;
- audio: HTMLAttributes;
- b: HTMLAttributes;
- base: HTMLAttributes;
- bdi: HTMLAttributes;
- bdo: HTMLAttributes;
- big: HTMLAttributes;
- blockquote: HTMLAttributes;
- body: HTMLAttributes;
- br: HTMLAttributes;
- button: HTMLAttributes;
- canvas: HTMLAttributes;
- caption: HTMLAttributes;
- cite: HTMLAttributes;
- code: HTMLAttributes;
- col: HTMLAttributes;
- colgroup: HTMLAttributes;
- data: HTMLAttributes;
- datalist: HTMLAttributes;
- dd: HTMLAttributes;
- del: HTMLAttributes;
- details: HTMLAttributes;
- dfn: HTMLAttributes;
- dialog: HTMLAttributes;
- div: HTMLAttributes;
- dl: HTMLAttributes;
- dt: HTMLAttributes;
- em: HTMLAttributes;
- embed: HTMLAttributes;
- fieldset: HTMLAttributes;
- figcaption: HTMLAttributes;
- figure: HTMLAttributes;
- footer: HTMLAttributes;
- form: HTMLAttributes;
- h1: HTMLAttributes;
- h2: HTMLAttributes;
- h3: HTMLAttributes;
- h4: HTMLAttributes;
- h5: HTMLAttributes;
- h6: HTMLAttributes;
- head: HTMLAttributes;
- header: HTMLAttributes;
- hgroup: HTMLAttributes;
- hr: HTMLAttributes;
- html: HTMLAttributes;
- i: HTMLAttributes;
- iframe: HTMLAttributes;
- img: HTMLAttributes;
- input: HTMLAttributes;
- ins: HTMLAttributes;
- kbd: HTMLAttributes;
- keygen: HTMLAttributes;
- label: HTMLAttributes;
- legend: HTMLAttributes;
- li: HTMLAttributes;
- link: HTMLAttributes;
- main: HTMLAttributes;
- map: HTMLAttributes;
- mark: HTMLAttributes;
- marquee: HTMLAttributes;
- menu: HTMLAttributes;
- menuitem: HTMLAttributes;
- meta: HTMLAttributes;
- meter: HTMLAttributes;
- nav: HTMLAttributes;
- noscript: HTMLAttributes;
- object: HTMLAttributes;
- ol: HTMLAttributes;
- optgroup: HTMLAttributes;
- option: HTMLAttributes;
- output: HTMLAttributes;
- p: HTMLAttributes;
- param: HTMLAttributes;
- picture: HTMLAttributes;
- pre: HTMLAttributes;
- progress: HTMLAttributes;
- q: HTMLAttributes;
- rp: HTMLAttributes;
- rt: HTMLAttributes;
- ruby: HTMLAttributes;
- s: HTMLAttributes;
- samp: HTMLAttributes;
- script: HTMLAttributes;
- section: HTMLAttributes;
- select: HTMLAttributes;
- slot: HTMLAttributes;
- small: HTMLAttributes;
- source: HTMLAttributes;
- span: HTMLAttributes;
- strong: HTMLAttributes;
- style: HTMLAttributes;
- sub: HTMLAttributes;
- summary: HTMLAttributes;
- sup: HTMLAttributes;
- table: HTMLAttributes;
- tbody: HTMLAttributes;
- td: HTMLAttributes;
- textarea: HTMLAttributes;
- tfoot: HTMLAttributes;
- th: HTMLAttributes;
- thead: HTMLAttributes;
- time: HTMLAttributes;
- title: HTMLAttributes;
- tr: HTMLAttributes;
- track: HTMLAttributes;
- u: HTMLAttributes;
- ul: HTMLAttributes;
- var: HTMLAttributes;
- video: HTMLAttributes;
- wbr: HTMLAttributes;
+declare global {
+ namespace JSX {
+ type Element = HTMLElement;
+ type IntrinsicElements = {
+ a: HTMLAttributes;
+ abbr: HTMLAttributes;
+ address: HTMLAttributes;
+ area: HTMLAttributes;
+ article: HTMLAttributes;
+ aside: HTMLAttributes;
+ audio: HTMLAttributes;
+ b: HTMLAttributes;
+ base: HTMLAttributes;
+ bdi: HTMLAttributes;
+ bdo: HTMLAttributes;
+ big: HTMLAttributes;
+ blockquote: HTMLAttributes;
+ body: HTMLAttributes;
+ br: HTMLAttributes;
+ button: HTMLAttributes;
+ canvas: HTMLAttributes;
+ caption: HTMLAttributes;
+ cite: HTMLAttributes;
+ code: HTMLAttributes;
+ col: HTMLAttributes;
+ colgroup: HTMLAttributes;
+ data: HTMLAttributes;
+ datalist: HTMLAttributes;
+ dd: HTMLAttributes;
+ del: HTMLAttributes;
+ details: HTMLAttributes;
+ dfn: HTMLAttributes;
+ dialog: HTMLAttributes;
+ div: HTMLAttributes;
+ dl: HTMLAttributes;
+ dt: HTMLAttributes;
+ em: HTMLAttributes;
+ embed: HTMLAttributes;
+ fieldset: HTMLAttributes;
+ figcaption: HTMLAttributes;
+ figure: HTMLAttributes;
+ footer: HTMLAttributes;
+ form: HTMLAttributes;
+ h1: HTMLAttributes;
+ h2: HTMLAttributes;
+ h3: HTMLAttributes;
+ h4: HTMLAttributes;
+ h5: HTMLAttributes;
+ h6: HTMLAttributes;
+ head: HTMLAttributes;
+ header: HTMLAttributes;
+ hgroup: HTMLAttributes;
+ hr: HTMLAttributes;
+ html: HTMLAttributes;
+ i: HTMLAttributes;
+ iframe: HTMLAttributes;
+ img: HTMLAttributes;
+ input: HTMLAttributes;
+ ins: HTMLAttributes;
+ kbd: HTMLAttributes;
+ keygen: HTMLAttributes;
+ label: HTMLAttributes;
+ legend: HTMLAttributes;
+ li: HTMLAttributes;
+ link: HTMLAttributes;
+ main: HTMLAttributes;
+ map: HTMLAttributes;
+ mark: HTMLAttributes;
+ marquee: HTMLAttributes;
+ menu: HTMLAttributes;
+ menuitem: HTMLAttributes;
+ meta: HTMLAttributes;
+ meter: HTMLAttributes;
+ nav: HTMLAttributes;
+ noscript: HTMLAttributes;
+ object: HTMLAttributes;
+ ol: HTMLAttributes;
+ optgroup: HTMLAttributes;
+ option: HTMLAttributes;
+ output: HTMLAttributes;
+ p: HTMLAttributes;
+ param: HTMLAttributes;
+ picture: HTMLAttributes;
+ pre: HTMLAttributes;
+ progress: HTMLAttributes;
+ q: HTMLAttributes;
+ rp: HTMLAttributes;
+ rt: HTMLAttributes;
+ ruby: HTMLAttributes;
+ s: HTMLAttributes;
+ samp: HTMLAttributes;
+ script: HTMLAttributes;
+ section: HTMLAttributes;
+ select: HTMLAttributes;
+ slot: HTMLAttributes;
+ small: HTMLAttributes;
+ source: HTMLAttributes;
+ span: HTMLAttributes;
+ strong: HTMLAttributes;
+ style: HTMLAttributes;
+ sub: HTMLAttributes;
+ summary: HTMLAttributes;
+ sup: HTMLAttributes;
+ table: HTMLAttributes;
+ tbody: HTMLAttributes;
+ td: HTMLAttributes;
+ textarea: HTMLAttributes;
+ tfoot: HTMLAttributes;
+ th: HTMLAttributes;
+ thead: HTMLAttributes;
+ time: HTMLAttributes;
+ title: HTMLAttributes;
+ tr: HTMLAttributes;
+ track: HTMLAttributes;
+ u: HTMLAttributes;
+ ul: HTMLAttributes;
+ var: HTMLAttributes;
+ video: HTMLAttributes;
+ wbr: HTMLAttributes;
+ };
+ }
}
diff --git a/src/utils.ts b/src/utils.ts
index 78bd64f..c8ff29b 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -35,7 +35,7 @@ export const walkDir = async function* (
* @param modName Name of the module to purge from the require cache.
*/
export const purgeModuleAndDepsFromCache = (modName: string): void => {
- var modPath = require.resolve(modName);
+ const modPath = require.resolve(modName);
if (modPath == null) {
return;
}
diff --git a/test/lib/expect.ts b/test/lib/expect.ts
index 7fe091f..3744310 100644
--- a/test/lib/expect.ts
+++ b/test/lib/expect.ts
@@ -6,7 +6,7 @@
import { areEqual, displayValue, matches } from "./utils";
class ExpectError extends Error {
- public constructor(reason: string, expected: any, actual: any) {
+ public constructor(reason: string, expected: unknown, actual: unknown) {
super(
`${reason}\n` +
`\texpected: ${displayValue(expected)}\n` +
@@ -45,7 +45,7 @@ export class Expect {
*
* @throws ExpectError If the actual value does not equal the expected value.
*/
- public toEqual(expected: T) {
+ public toEqual(expected: T): void {
if (!areEqual(this.value, expected)) {
throw new ExpectError(
`value does not equal expected`,
@@ -201,12 +201,12 @@ export function expect(fn: () => T): FunctionExpect;
*
* @param value Value to place expectations upon.
*/
-export function expect(value: any): Expect {
+export function expect(value: unknown): Expect {
if (typeof value === "string") {
return new StringExpect(value);
}
if (typeof value === "function") {
- return new FunctionExpect(value);
+ return new FunctionExpect(value as () => unknown);
}
return new Expect(value);
}
diff --git a/test/lib/utils.ts b/test/lib/utils.ts
index 5827c8c..cdccd73 100644
--- a/test/lib/utils.ts
+++ b/test/lib/utils.ts
@@ -31,7 +31,10 @@ const areArraysEqual = (a: T[], b: T[]): boolean => {
return true;
};
-const areObjectsEqual = (a: T, b: T): boolean => {
+const areObjectsEqual = >(
+ a: T,
+ b: T
+): boolean => {
const aKeys = Object.keys(a) as Array;
const bKeys = Object.keys(b) as Array;
if (aKeys.length !== bKeys.length) {
@@ -62,7 +65,10 @@ export const areEqual = (a: T, b: T): boolean => {
return a.source === b.source;
}
if (typeof a === "object" && typeof b === "object") {
- return areObjectsEqual(a as any, b);
+ return areObjectsEqual(
+ a as Record,
+ b as Record
+ );
}
return a === b;
};
@@ -95,7 +101,7 @@ export const matches = (value: string, pattern: string | RegExp): boolean => {
*
* @return Rendered value to display.
*/
-export const displayValue = (value: any): string => {
+export const displayValue = (value: unknown): string => {
if (value === undefined) {
return "undefined";
}