diff --git a/app/package.json b/app/package.json index fb67233..3616372 100644 --- a/app/package.json +++ b/app/package.json @@ -3,13 +3,14 @@ "scripts": { "dev": "concurrently \"pnpm:watch:build\" \"pnpm:preview\"", "build": "tsx src/index.tsx", - "watch:build": "chokidar './src/**/*' '../ensips/*.md' -t 500 --initial -c 'pnpm build'", + "watch:build": "chokidar './src/**/*' './public/**/*' '../ensips/*.md' -t 500 --initial -c 'pnpm build'", "preview": "pnpm run build && pnpm vite preview", "lint": "eslint -c .eslintrc.json --ext .ts,.tsx ./src" }, "dependencies": { "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", + "@types/unist": "^3.0.3", "chokidar-cli": "^3.0.0", "concurrently": "^8.2.2", "react": "^18.3.1", @@ -19,6 +20,7 @@ "remark-html": "^16.0.1", "tsx": "^4.19.0", "unified": "^11.0.5", + "unist": "^0.0.1", "vite": "^5.4.3", "yaml": "^2.5.0", "zod": "^3.23.8" diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 17ff667..a6886d5 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 + '@types/unist': + specifier: ^3.0.3 + version: 3.0.3 chokidar-cli: specifier: ^3.0.0 version: 3.0.0 @@ -41,6 +44,9 @@ importers: unified: specifier: ^11.0.5 version: 11.0.5 + unist: + specifier: ^0.0.1 + version: 0.0.1 vite: specifier: ^5.4.3 version: 5.4.3(@types/node@22.5.2) @@ -1995,6 +2001,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unist@0.0.1: + resolution: {integrity: sha512-bnzuF8b6d47WubA4a5yLqFbuZz/v/NS6eRwUIdOaDmsqzwTlyv8yS1g3M7ISdtBQrigPD3qKK87Cu7zhEfCF3A==} + deprecated: Use @types/unist instead + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -4277,6 +4287,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + unist@0.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/app/public/index.css b/app/public/index.css index d6711a7..3bfdea2 100644 --- a/app/public/index.css +++ b/app/public/index.css @@ -1,4 +1,5 @@ -html, body { +html, +body { background: #f7f7fa; font-family: 'Satoshi', sans-serif; font-size: 16px; @@ -7,11 +8,14 @@ html, body { article { max-width: 800px; margin: 0 auto; + padding-bottom: 420px; } nav { display: flex; gap: 4px; + padding: 4px 0; + align-items: center; } nav ul { @@ -19,6 +23,11 @@ nav ul { list-style: none; } +nav ul li a { + display: block; + padding: 8px 0; +} + .front { border: 1px solid black; padding: 16px; @@ -31,3 +40,9 @@ nav ul { .space-y-4 > :first-child { margin-top: 0; } + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/app/public/normalize.css b/app/public/normalize.css new file mode 100644 index 0000000..92aed47 --- /dev/null +++ b/app/public/normalize.css @@ -0,0 +1,351 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { + /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type='button']:-moz-focusring, +[type='reset']:-moz-focusring, +[type='submit']:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type='checkbox'], +[type='radio'] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/app/src/App.tsx b/app/src/App.tsx index 02bf0c0..aff355d 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -15,7 +15,6 @@ export const App: FC<{ markdown: string; frontmatter: Frontmatter }> = ({
-
ENSIPs
); diff --git a/app/src/Frame.tsx b/app/src/Frame.tsx index b2fe079..0e4b0ae 100644 --- a/app/src/Frame.tsx +++ b/app/src/Frame.tsx @@ -6,6 +6,7 @@ export const Frame: FC = ({ children }) => { ENSIPs + {children} diff --git a/app/src/index.tsx b/app/src/index.tsx index 9251b49..4217a3a 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -11,6 +11,7 @@ import { extractFrontmatter, } from './specs/validateFrontmatter'; import { extractTitle } from './specs/validateTitle'; +import { TracedError } from './util/error'; console.log('Building preview...'); @@ -80,12 +81,21 @@ for (const file of files) { x ); } catch (error) { - console.log(error); - console.log( - '::error file=' + - directPath + - ',line=1,col=1,endColumn=2::Unable to load file' - ); + if (error instanceof TracedError) { + console.log(error.error); + + console.log( + `::error file=${directPath},line=${error.line},col=${error.column},endColumn=${error.columnEnd}::${error.error}` + ); + } else { + console.log(error); + + console.log( + '::error file=' + + directPath + + ',line=1,col=1,endColumn=2::Unable to load file' + ); + } } } @@ -101,7 +111,7 @@ await writeFile( // Render public content -const static_files = ['./public/index.css']; +const static_files = ['./public/index.css', './public/normalize.css']; for (const file of static_files) { const fileData = await readFile(file, 'utf8'); diff --git a/app/src/specs/validateTitle.ts b/app/src/specs/validateTitle.ts index 610c423..6149e5f 100644 --- a/app/src/specs/validateTitle.ts +++ b/app/src/specs/validateTitle.ts @@ -1,7 +1,11 @@ /* eslint-disable unicorn/consistent-function-scoping */ import type { Plugin } from 'unified'; +import type { Parent } from 'unist'; import { z } from 'zod'; +import { TracedError } from '../util/error'; +import { LinePositionZod } from '../util/zod'; + // ENSIP-123: Title must match regex // or // ENSIP-X: Title must match regex @@ -17,17 +21,40 @@ export const TitleZod = z.object({ { type: z.literal('text'), value: z.string().min(5).max(160), - position: z.any(), + position: LinePositionZod, }, { description: 'Not text-based title' } ) ), + position: LinePositionZod, }); +export type TitleNode = z.infer; + export const extractTitle = (directPath: string, callback: (_found: string) => void): Plugin => () => - (tree) => { + (_tree) => { + const tree = _tree as any as Parent; + + // Count the number of h1 headings + const titleCount = tree.children.filter( + // @ts-ignore + (node) => node.type === 'heading' && node.depth === 1 + ) as TitleNode[]; + + if (titleCount.length > 1) { + console.log(titleCount[1]); + + throw new TracedError( + 'More then one h1 (#) heading found, please use h2 (##) or h3 (###) headings', + directPath, + titleCount[1]!.position.start.line, + titleCount[1]!.position.start.column, + titleCount[1]!.position.end.column + ); + } + const first = ( (tree as any)['children'] as [ { diff --git a/app/src/util/error.ts b/app/src/util/error.ts new file mode 100644 index 0000000..de4bca9 --- /dev/null +++ b/app/src/util/error.ts @@ -0,0 +1,28 @@ +// Error wrapper that includes a filename, line number, column number, and column end +export class TracedError extends Error { + constructor( + error: Error | string, + filename: string, + line: number, + column: number, + columnEnd: number + ) { + if (typeof error === 'string') { + super(error); + } else { + super(error.message); + } + + this.error = error; + this.filename = filename; + this.line = line; + this.column = column; + this.columnEnd = columnEnd; + } + + error: Error | string; + filename: string; + line: number; + column: number; + columnEnd: number; +} diff --git a/app/src/util/zod.ts b/app/src/util/zod.ts new file mode 100644 index 0000000..9ed6cf7 --- /dev/null +++ b/app/src/util/zod.ts @@ -0,0 +1,14 @@ +import { z } from 'zod'; + +export const LinePositionZod = z.object({ + start: z.object({ + line: z.number(), + column: z.number(), + offset: z.number(), + }), + end: z.object({ + line: z.number(), + column: z.number(), + offset: z.number(), + }), +});