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

pull/9379/head
Ben McCann 8 months 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 { 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 =
(!dev || (SUPABASE_URL && SUPABASE_KEY)) &&
client_enabled &&
createClient(SUPABASE_URL, SUPABASE_KEY, {
global: { fetch },
auth: { persistSession: false }

@ -8,6 +8,10 @@ const PAGE_SIZE = 90;
/**
* @param {User} user
* @param {{
* offset: number;
* search: string | null;
* }} opts
*/
export async function list(user, { offset, search }) {
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);
// normalize IDs
data.forEach((gist) => {
gist.id = gist.id.replace(/-/g, '');
});
data.forEach(
/** @param {{id:string}} gist */ (gist) => {
gist.id = gist.id.replace(/-/g, '');
}
);
return {
gists: data.slice(0, PAGE_SIZE),

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

@ -99,6 +99,7 @@ export function get_docs_list(docs_data) {
}));
}
/** @param {string} str */
const titled = async (str) =>
removeMarkdown(
escape(await markedTransform(str, { paragraph: (txt) => txt }))
@ -111,7 +112,10 @@ const titled = async (str) =>
.replace(/<(\/)?(em|b|strong|code)>/g, '')
);
/** @param {string} markdown */
/**
* @param {string} markdown
* @returns {Promise<import('./types').Section[]>}
*/
export async function get_sections(markdown) {
const lines = markdown.split('\n');
const root = /** @type {import('./types').Section} */ ({
@ -141,7 +145,9 @@ export async function get_sections(markdown) {
};
// 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
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 = [];
for (const subdir of await readdir(base)) {
/** @type {import('./types').ExamplesDatum} */
const section = {
title: '', // Initialise with empty
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')
).title;
/**
* @type {Array<{
* name: string;
* type: string;
* content: string;
* }>}
*/
const files = [];
for (const file of (await readdir(example_base_dir)).filter(
(file) => !file.endsWith('meta.json')
)) {
const type = file.split('.').at(-1);
if (!type) {
throw new Error(`Could not determine type from ${file}`);
}
files.push({
name: file,
type: file.split('.').at(-1),
type,
content: await readFile(`${example_base_dir}/${file}`, 'utf-8')
});
}

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

@ -30,6 +30,7 @@ export async function get_tutorial_data(base = CONTENT_BASE_PATHS.TUTORIAL) {
const tutorials = [];
for (const subdir of await readdir(base)) {
/** @type {import('./types').TutorialDatum} */
const section = {
title: '', // Initialise with empty
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);
// Get the contents of the apps.
/**
* @type {{
* initial: import('./types').CompletionState[];
* complete: import('./types').CompletionState[];
* }}
*/
const completion_states_data = { initial: [], complete: [] };
for (const app_dir of await readdir(tutorial_base_dir)) {
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');
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({
name: file,
type: file.split('.').at(-1),
type,
content: await readFile(`${app_dir_path}/${file}`, 'utf-8')
});
}

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

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

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

@ -1,3 +1,11 @@
/**
* @param {Array<{
* content: string;
* name: string;
* source: string;
* type: string;
* }>} files
*/
export function process_example(files) {
return files
.map((file) => {
@ -12,5 +20,7 @@ export function process_example(files) {
if (a.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();
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');
({ gists, next } = await gist.list(user, { offset, search }));

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

@ -189,7 +189,7 @@ export default app;`
<svelte:window on:keydown={handleKeydown} />
<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">
<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) => {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');

@ -46,9 +46,8 @@ export async function GET({ params }) {
.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({
id: params.id,
name: example.title,

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

@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit';
import { client_id, oauth } from '../_config.js';
export const GET = client_id
? ({ url }) => {
? /** @param {{url: URL}} opts */ ({ url }) => {
const Location =
`${oauth}/authorize?` +
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(',');
/** @param {string} str */
function formatPubdate(str) {
const [y, m, d] = str.split('-');
return `${d} ${months[+m]} ${y} 12:00 +0000`;
}
/** @param {string} html */
function escapeHTML(html) {
/** @type {{ [key: string]: string }} */
const chars = {
'"': 'quot',
"'": '#39',

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

Loading…
Cancel
Save