site: fix type errors in JS files (#9354)

pull/9379/head
Ben McCann 2 years ago committed by GitHub
parent ac7505d81b
commit d8e382712c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,8 +2,14 @@ import { dev } from '$app/environment';
import { SUPABASE_URL, SUPABASE_KEY } from '$env/static/private'; import { SUPABASE_URL, SUPABASE_KEY } from '$env/static/private';
import { createClient } from '@supabase/supabase-js'; import { createClient } from '@supabase/supabase-js';
const client_enabled = !!(!dev || (SUPABASE_URL && SUPABASE_KEY));
/**
* @type {import('@supabase/supabase-js').SupabaseClient<any, "public", any>}
*/
// @ts-ignore-line
export const client = export const client =
(!dev || (SUPABASE_URL && SUPABASE_KEY)) && client_enabled &&
createClient(SUPABASE_URL, SUPABASE_KEY, { createClient(SUPABASE_URL, SUPABASE_KEY, {
global: { fetch }, global: { fetch },
auth: { persistSession: false } auth: { persistSession: false }

@ -8,6 +8,10 @@ const PAGE_SIZE = 90;
/** /**
* @param {User} user * @param {User} user
* @param {{
* offset: number;
* search: string | null;
* }} opts
*/ */
export async function list(user, { offset, search }) { export async function list(user, { offset, search }) {
const { data, error } = await client.rpc('gist_list', { const { data, error } = await client.rpc('gist_list', {
@ -20,9 +24,11 @@ export async function list(user, { offset, search }) {
if (error) throw new Error(error.message); if (error) throw new Error(error.message);
// normalize IDs // normalize IDs
data.forEach((gist) => { data.forEach(
gist.id = gist.id.replace(/-/g, ''); /** @param {{id:string}} gist */ (gist) => {
}); gist.id = gist.id.replace(/-/g, '');
}
);
return { return {
gists: data.slice(0, PAGE_SIZE), gists: data.slice(0, PAGE_SIZE),

@ -5,7 +5,7 @@ import { client } from './client.js';
/** @typedef {import('./types').User} User */ /** @typedef {import('./types').User} User */
/** /**
* @type {import('flru').flruCache<User>} * @type {import('flru').flruCache<User | null>}
*/ */
const session_cache = flru(1000); const session_cache = flru(1000);
@ -39,7 +39,7 @@ export async function create(user) {
/** /**
* @param {string} sessionid * @param {string} sessionid
* @returns {Promise<User>} * @returns {Promise<User | null>}
*/ */
export async function read(sessionid) { export async function read(sessionid) {
if (!sessionid) return null; if (!sessionid) return null;
@ -58,7 +58,7 @@ export async function read(sessionid) {
); );
} }
return session_cache.get(sessionid); return session_cache.get(sessionid) || null;
} }
/** @param {string} sessionid */ /** @param {string} sessionid */

@ -99,6 +99,7 @@ export function get_docs_list(docs_data) {
})); }));
} }
/** @param {string} str */
const titled = async (str) => const titled = async (str) =>
removeMarkdown( removeMarkdown(
escape(await markedTransform(str, { paragraph: (txt) => txt })) escape(await markedTransform(str, { paragraph: (txt) => txt }))
@ -111,7 +112,10 @@ const titled = async (str) =>
.replace(/<(\/)?(em|b|strong|code)>/g, '') .replace(/<(\/)?(em|b|strong|code)>/g, '')
); );
/** @param {string} markdown */ /**
* @param {string} markdown
* @returns {Promise<import('./types').Section[]>}
*/
export async function get_sections(markdown) { export async function get_sections(markdown) {
const lines = markdown.split('\n'); const lines = markdown.split('\n');
const root = /** @type {import('./types').Section} */ ({ const root = /** @type {import('./types').Section} */ ({
@ -141,7 +145,9 @@ export async function get_sections(markdown) {
}; };
// Add the new node to the tree // Add the new node to the tree
currentNodes[level].sections.push(newNode); const sections = currentNodes[level].sections;
if (!sections) throw new Error(`Could not find section ${level}`);
sections.push(newNode);
// Prepare for potential children of the new node // Prepare for potential children of the new node
currentNodes = currentNodes.slice(0, level + 1); currentNodes = currentNodes.slice(0, level + 1);
@ -152,5 +158,5 @@ export async function get_sections(markdown) {
} }
} }
return root.sections; return /** @type {import('./types').Section[]} */ (root.sections);
} }

@ -25,6 +25,7 @@ export async function get_examples_data(base = CONTENT_BASE_PATHS.EXAMPLES) {
const examples = []; const examples = [];
for (const subdir of await readdir(base)) { for (const subdir of await readdir(base)) {
/** @type {import('./types').ExamplesDatum} */
const section = { const section = {
title: '', // Initialise with empty title: '', // Initialise with empty
slug: subdir.split('-').slice(1).join('-'), slug: subdir.split('-').slice(1).join('-'),
@ -51,13 +52,24 @@ export async function get_examples_data(base = CONTENT_BASE_PATHS.EXAMPLES) {
await readFile(`${example_base_dir}/meta.json`, 'utf-8') await readFile(`${example_base_dir}/meta.json`, 'utf-8')
).title; ).title;
/**
* @type {Array<{
* name: string;
* type: string;
* content: string;
* }>}
*/
const files = []; const files = [];
for (const file of (await readdir(example_base_dir)).filter( for (const file of (await readdir(example_base_dir)).filter(
(file) => !file.endsWith('meta.json') (file) => !file.endsWith('meta.json')
)) { )) {
const type = file.split('.').at(-1);
if (!type) {
throw new Error(`Could not determine type from ${file}`);
}
files.push({ files.push({
name: file, name: file,
type: file.split('.').at(-1), type,
content: await readFile(`${example_base_dir}/${file}`, 'utf-8') content: await readFile(`${example_base_dir}/${file}`, 'utf-8')
}); });
} }

@ -1,4 +1,4 @@
export type ExamplesData = { export interface ExamplesDatum {
title: string; title: string;
slug: string; slug: string;
examples: { examples: {
@ -10,7 +10,9 @@ export type ExamplesData = {
name: string; name: string;
}[]; }[];
}[]; }[];
}[]; }
export type ExamplesData = ExamplesDatum[];
export interface Example { export interface Example {
title: string; title: string;

@ -30,6 +30,7 @@ export async function get_tutorial_data(base = CONTENT_BASE_PATHS.TUTORIAL) {
const tutorials = []; const tutorials = [];
for (const subdir of await readdir(base)) { for (const subdir of await readdir(base)) {
/** @type {import('./types').TutorialDatum} */
const section = { const section = {
title: '', // Initialise with empty title: '', // Initialise with empty
slug: subdir.split('-').slice(1).join('-'), slug: subdir.split('-').slice(1).join('-'),
@ -55,6 +56,12 @@ export async function get_tutorial_data(base = CONTENT_BASE_PATHS.TUTORIAL) {
const { metadata, body } = extractFrontmatter(contents); const { metadata, body } = extractFrontmatter(contents);
// Get the contents of the apps. // Get the contents of the apps.
/**
* @type {{
* initial: import('./types').CompletionState[];
* complete: import('./types').CompletionState[];
* }}
*/
const completion_states_data = { initial: [], complete: [] }; const completion_states_data = { initial: [], complete: [] };
for (const app_dir of await readdir(tutorial_base_dir)) { for (const app_dir of await readdir(tutorial_base_dir)) {
if (!app_dir.startsWith('app-')) continue; if (!app_dir.startsWith('app-')) continue;
@ -63,9 +70,13 @@ export async function get_tutorial_data(base = CONTENT_BASE_PATHS.TUTORIAL) {
const app_contents = await readdir(app_dir_path, 'utf-8'); const app_contents = await readdir(app_dir_path, 'utf-8');
for (const file of app_contents) { for (const file of app_contents) {
const type = file.split('.').at(-1);
if (!type) {
throw new Error(`Could not determine type from ${file}`);
}
completion_states_data[app_dir === 'app-a' ? 'initial' : 'complete'].push({ completion_states_data[app_dir === 'app-a' ? 'initial' : 'complete'].push({
name: file, name: file,
type: file.split('.').at(-1), type,
content: await readFile(`${app_dir_path}/${file}`, 'utf-8') content: await readFile(`${app_dir_path}/${file}`, 'utf-8')
}); });
} }

@ -1,4 +1,4 @@
export type TutorialData = { export interface TutorialDatum {
title: string; title: string;
slug: string; slug: string;
tutorials: { tutorials: {
@ -6,10 +6,18 @@ export type TutorialData = {
slug: string; slug: string;
dir: string; dir: string;
content: string; content: string;
initial: { name: string; type: string; content: string }[]; initial: CompletionState[];
complete: { name: string; type: string; content: string }[]; complete: CompletionState[];
}[]; }[];
}[]; }
export interface CompletionState {
name: string;
type: string;
content: string;
}
export type TutorialData = TutorialDatum[];
export interface Tutorial { export interface Tutorial {
title: string; title: string;

@ -1,30 +1,28 @@
// adapted from https://github.com/digplan/time-ago const formatter = new Intl.RelativeTimeFormat(undefined, {
// https://github.com/digplan/time-ago/blob/master/license.txt numeric: 'auto'
const o = { });
second: 1000,
minute: 60 * 1000, const DIVISIONS = {
hour: 60 * 1000 * 60, seconds: 60,
day: 24 * 60 * 1000 * 60, minutes: 60,
week: 7 * 24 * 60 * 1000 * 60, hours: 24,
month: 30 * 24 * 60 * 1000 * 60, days: 7,
year: 365 * 24 * 60 * 1000 * 60 weeks: 4.34524,
months: 12,
years: Number.POSITIVE_INFINITY
}; };
export const ago = (nd, s) => { /**
var r = Math.round, * @param {Date} date
dir = ' ago', */
pl = function (v, n) { export const ago = (date) => {
return s === undefined ? n + ' ' + v + (n > 1 ? 's' : '') + dir : n + v.substring(0, 1); let duration = (date.getTime() - new Date().getTime()) / 1000;
},
ts = Date.now() - new Date(nd).getTime(), for (const [name, amount] of Object.entries(DIVISIONS)) {
ii; if (Math.abs(duration) < amount) {
if (ts < 0) { const format = /** @type {keyof(DIVISIONS)} */ (name);
ts *= -1; return formatter.format(Math.round(duration), format);
dir = ' from now'; }
} duration /= amount;
for (var i in o) {
if (r(ts) < o[i]) return pl(ii || 'm', r(ts / (o[ii] || 1)));
ii = i;
} }
return pl(i, r(ts / o[i]));
}; };

@ -1,7 +1,13 @@
/** @param {number} code */
export function keyEvent(code) { export function keyEvent(code) {
/**
* @param {HTMLInputElement} node
* @param {(event: KeyboardEvent) => void} callback
*/
return function (node, callback) { return function (node, callback) {
node.addEventListener('keydown', handleKeydown); node.addEventListener('keydown', handleKeydown);
/** @param {KeyboardEvent} event */
function handleKeydown(event) { function handleKeydown(event) {
if (event.keyCode === code) { if (event.keyCode === code) {
callback.call(this, event); callback.call(this, event);

@ -1,3 +1,11 @@
/**
* @param {Array<{
* content: string;
* name: string;
* source: string;
* type: string;
* }>} files
*/
export function process_example(files) { export function process_example(files) {
return files return files
.map((file) => { .map((file) => {
@ -12,5 +20,7 @@ export function process_example(files) {
if (a.type === 'svelte') return -1; if (a.type === 'svelte') return -1;
if (b.type === 'svelte') return 1; if (b.type === 'svelte') return 1;
return 0;
}); });
} }

@ -9,7 +9,8 @@ export async function load({ url, parent }) {
const { user } = await parent(); const { user } = await parent();
if (user) { if (user) {
const offset = url.searchParams.get('offset') ? parseInt(url.searchParams.get('offset')) : 0; const offset_param = url.searchParams.get('offset');
const offset = offset_param ? parseInt(offset_param) : 0;
const search = url.searchParams.get('search'); const search = url.searchParams.get('search');
({ gists, next } = await gist.list(user, { offset, search })); ({ gists, next } = await gist.list(user, { offset, search }));

@ -8,17 +8,14 @@ export function load({ url }) {
const vim = query.get('vim'); const vim = query.get('vim');
// redirect to v2 REPL if appropriate // redirect to v2 REPL if appropriate
if (/^[^>]?[12]/.test(version)) { if (version && /^[^>]?[12]/.test(version)) {
throw redirect(302, `https://v2.svelte.dev/repl?${query}`); throw redirect(302, `https://v2.svelte.dev/repl?${query}`);
} }
const id = gist || example || 'hello-world'; const id = gist || example || 'hello-world';
// we need to filter out null values // we need to filter out null values
const q = new URLSearchParams( const q = new URLSearchParams();
Object.entries({ if (version) q.set('version', version);
version, if (vim) q.set('vim', vim);
vim
}).filter(([, value]) => value !== null)
).toString();
throw redirect(301, `/repl/${id}?${q}`); throw redirect(301, `/repl/${id}?${q}`);
} }

@ -189,7 +189,7 @@ export default app;`
<svelte:window on:keydown={handleKeydown} /> <svelte:window on:keydown={handleKeydown} />
<div class="app-controls"> <div class="app-controls">
<input bind:value={name} on:focus={(e) => e.target.select()} use:enter={(e) => e.target.blur()} /> <input bind:value={name} on:focus={(e) => e.target.select()} use:enter={(e) => /** @type {HTMLInputElement} */ (e.target).blur()} />
<div class="buttons"> <div class="buttons">
<button class="icon" on:click={() => (zen_mode = !zen_mode)} title="fullscreen editor"> <button class="icon" on:click={() => (zen_mode = !zen_mode)} title="fullscreen editor">

@ -1,3 +1,7 @@
/**
* @param {Blob} blob
* @param {string} filename
*/
export default (blob, filename) => { export default (blob, filename) => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');

@ -46,9 +46,8 @@ export async function GET({ params }) {
.map((example) => example.slug) .map((example) => example.slug)
); );
if (examples.has(params.id)) { const example = get_example(examples_data, params.id);
const example = get_example(examples_data, params.id); if (example) {
return json({ return json({
id: params.id, id: params.id,
name: example.title, name: example.title,

@ -6,14 +6,13 @@ import { oauth, client_id, client_secret } from '../_config.js';
export async function GET({ url }) { export async function GET({ url }) {
try { try {
// Trade "code" for "access_token" // Trade "code" for "access_token"
const r1 = await fetch( const code = url.searchParams.get('code') || undefined;
`${oauth}/access_token?` + const params = new URLSearchParams({
new URLSearchParams({ client_id,
code: url.searchParams.get('code'), client_secret
client_id, });
client_secret if (code) params.set('code', code);
}).toString() const r1 = await fetch(`${oauth}/access_token?` + params.toString());
);
const access_token = new URLSearchParams(await r1.text()).get('access_token'); const access_token = new URLSearchParams(await r1.text()).get('access_token');
// Now fetch User details // Now fetch User details

@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit';
import { client_id, oauth } from '../_config.js'; import { client_id, oauth } from '../_config.js';
export const GET = client_id export const GET = client_id
? ({ url }) => { ? /** @param {{url: URL}} opts */ ({ url }) => {
const Location = const Location =
`${oauth}/authorize?` + `${oauth}/authorize?` +
new URLSearchParams({ new URLSearchParams({

@ -4,12 +4,15 @@ export const prerender = true;
const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','); const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',');
/** @param {string} str */
function formatPubdate(str) { function formatPubdate(str) {
const [y, m, d] = str.split('-'); const [y, m, d] = str.split('-');
return `${d} ${months[+m]} ${y} 12:00 +0000`; return `${d} ${months[+m]} ${y} 12:00 +0000`;
} }
/** @param {string} html */
function escapeHTML(html) { function escapeHTML(html) {
/** @type {{ [key: string]: string }} */
const chars = { const chars = {
'"': 'quot', '"': 'quot',
"'": '#39', "'": '#39',

@ -26,11 +26,12 @@ export async function content() {
/** @type {import('@sveltejs/site-kit/search').Block[]} */ /** @type {import('@sveltejs/site-kit/search').Block[]} */
const blocks = []; const blocks = [];
/** @type {string[]} */
const breadcrumbs = []; const breadcrumbs = [];
for (const file of await glob('**/*.md', { cwd: `${base}/docs` })) { for (const file of await glob('**/*.md', { cwd: `${base}/docs` })) {
const basename = path_basename(file); const basename = path_basename(file);
const match = /\d{2}-(.+)\.md/.exec(basename); const match = basename && /\d{2}-(.+)\.md/.exec(basename);
if (!match) continue; if (!match) continue;
const slug = match[1]; const slug = match[1];
@ -44,35 +45,45 @@ export async function content() {
const { body, metadata } = extractFrontmatter(markdown); const { body, metadata } = extractFrontmatter(markdown);
const sections = body.trim().split(/^## /m); const sections = body.trim().split(/^## /m);
const intro = sections.shift().trim(); const intro = sections?.shift()?.trim();
const rank = +metadata.rank || undefined; const rank = +metadata.rank;
blocks.push({ if (intro) {
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title ?? '')], blocks.push({
href: get_href([slug]), breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title ?? '')],
content: await plaintext(intro), href: get_href([slug]),
rank content: await plaintext(intro),
}); rank
});
}
for (const section of sections) { for (const section of sections) {
const lines = section.split('\n'); const lines = section.split('\n');
const h2 = lines.shift(); const h2 = lines.shift();
const content = lines.join('\n'); if (!h2) {
console.warn('Could not find expected heading h2');
continue;
}
const content = lines.join('\n');
const subsections = content.trim().split('## '); const subsections = content.trim().split('## ');
const intro = subsections?.shift()?.trim();
const intro = subsections.shift().trim(); if (intro) {
blocks.push({
blocks.push({ breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title), removeMarkdown(h2)],
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title), removeMarkdown(h2)], href: get_href([slug, normalizeSlugify(h2)]),
href: get_href([slug, normalizeSlugify(h2)]), content: await plaintext(intro),
content: await plaintext(intro), rank
rank });
}); }
for (const subsection of subsections) { for (const subsection of subsections) {
const lines = subsection.split('\n'); const lines = subsection.split('\n');
const h3 = lines.shift(); const h3 = lines.shift();
if (!h3) {
console.warn('Could not find expected heading h3');
continue;
}
blocks.push({ blocks.push({
breadcrumbs: [ breadcrumbs: [
@ -102,7 +113,7 @@ async function plaintext(markdown) {
return ( return (
await markedTransform(markdown, { await markedTransform(markdown, {
code: (source) => source.split('// ---cut---\n').pop(), code: (source) => source.split('// ---cut---\n').pop() || 'ERROR: ---cut--- not found',
blockquote: block, blockquote: block,
html: () => '\n', html: () => '\n',
heading: (text) => `${text}\n`, heading: (text) => `${text}\n`,

Loading…
Cancel
Save