diff --git a/assets/scripts/GitAgent.js b/assets/scripts/GitAgent.ts similarity index 76% rename from assets/scripts/GitAgent.js rename to assets/scripts/GitAgent.ts index 51328d4e..0d4e8f16 100644 --- a/assets/scripts/GitAgent.js +++ b/assets/scripts/GitAgent.ts @@ -1,5 +1,3 @@ -//@ts-check - /** * Ce fichier gère les interactions avec git (contenu, commits, branches, remotes, pull/pull, etc.) * et aussi le contenu sous-jacent et le filesystem @@ -17,8 +15,8 @@ import http from 'isomorphic-git/http/web' const DEFAULT_CORS_PROXY_URL = 'https://cors.isomorphic-git.org' -/** @typedef {import('isomorphic-git')} isomorphicGit */ -/** @typedef {import('isomorphic-git').GitAuth} GitAuth */ +import type { CommitObject, GitAuth } from 'isomorphic-git' +import type { ResolutionOption } from './store.ts' export default class GitAgent { #fs @@ -33,20 +31,20 @@ export default class GitAgent { #hostname #repoDirectory - /** - * @param { object } _ - * @param { string } _.repoId - * @param { string } _.remoteURL - * @param { string } [_.corsProxyURL] - * @param { GitAuth } _.auth - * @param {((resolutionOptions: import('./store.js').ResolutionOption[]) => void) | undefined } [_.onMergeConflict] - */ constructor({ repoId, remoteURL, corsProxyURL = DEFAULT_CORS_PROXY_URL, auth, onMergeConflict, + }: { + repoId: string + remoteURL: string + corsProxyURL?: string + auth: GitAuth + onMergeConflict?: + | ((resolutionOptions: ResolutionOption[]) => void) + | undefined }) { this.#fs = new FS('scribouilli') @@ -63,31 +61,18 @@ export default class GitAgent { this.#repoDirectory = `/${this.#hostname}/${this.#repoId}` } - /** - * - * @param {string} filename - * @returns {string} - */ - #path(filename) { + #path(filename: string): string { return `${this.#repoDirectory}/${filename}` } /** * @summary helper to create ref strings for remotes - * - * @param {string} remote - * @param {string} ref - * @returns {string} */ - #createRemoteRef(remote, ref) { + #createRemoteRef(remote: string, ref: string): string { return `remotes/${remote}/${ref}` } - /** - * - * @returns {ReturnType} - */ - clone() { + clone(): ReturnType { console.info('clone', this.#remoteURL) return git.clone({ fs: this.#fs, @@ -101,25 +86,18 @@ export default class GitAgent { }) } - /** - * - * @returns {ReturnType} - */ - currentBranch() { + currentBranch(): ReturnType { return git.currentBranch({ fs: this.#fs, dir: this.#repoDirectory, }) } - /** - * - * @param {string} branch - * @param {boolean} [force] - * @param {boolean} [checkout] - * @returns {ReturnType} - */ - branch(branch, force = false, checkout = true) { + branch( + branch: string, + force: boolean = false, + checkout: boolean = true, + ): ReturnType { return git.branch({ fs: this.#fs, dir: this.#repoDirectory, @@ -129,23 +107,14 @@ export default class GitAgent { }) } - /** - * - * @returns {ReturnType} - */ - listRemotes() { + listRemotes(): ReturnType { return git.listRemotes({ fs: this.#fs, dir: this.#repoDirectory, }) } - /** - * - * @param {string} [remote] - * @returns {ReturnType} - */ - listBranches(remote) { + listBranches(remote: string): ReturnType { return git.listBranches({ fs: this.#fs, dir: this.#repoDirectory, @@ -156,10 +125,8 @@ export default class GitAgent { /** * @summary This version of git push may fail if the remote repo * has unmerged changes - * - * @returns {ReturnType} */ - falliblePush() { + falliblePush(): ReturnType { console.info('falliblePush') return git.push({ fs: this.#fs, @@ -176,10 +143,8 @@ export default class GitAgent { * @summary This version of git push tries to push * then tries to pull if the push fails * and tries again to push if the pull succeeded - * - * @returns {Promise} */ - safePush() { + safePush(): Promise { console.info('safePush') return this.falliblePush() .catch(err => { @@ -203,10 +168,8 @@ export default class GitAgent { /** * @summary like git push --force - * - * @returns {ReturnType} */ - forcePush() { + forcePush(): ReturnType { console.info('forcePush') return git.push({ fs: this.#fs, @@ -220,11 +183,7 @@ export default class GitAgent { }) } - /** - * - * @returns {ReturnType} - */ - async fetch() { + async fetch(): ReturnType { return git.fetch({ fs: this.#fs, http, @@ -235,12 +194,9 @@ export default class GitAgent { }) } - /** - * - * @param {string} [ref] - * @returns {Promise} - */ - currentCommit(ref = undefined) { + currentCommit( + ref: string | undefined = undefined, + ): Promise { return git .log({ fs: this.#fs, @@ -251,12 +207,9 @@ export default class GitAgent { .then(commits => ({ oid: commits[0].oid, ...commits[0].commit })) } - /** - * - * @param {string} [ref] - * @returns {ReturnType} - */ - checkout(ref = undefined) { + checkout( + ref: string | undefined = undefined, + ): ReturnType { return git.checkout({ fs: this.#fs, dir: this.#repoDirectory, @@ -265,13 +218,10 @@ export default class GitAgent { } /** - * * @summary This function tries to merge * If it fails, it forwards the conflict to this.onMergeConflict with resolution propositions - * - * @returns {Promise} */ - async tryMerging() { + async tryMerging(): Promise { console.info('tryMerging') const [currentBranch, remotes] = await Promise.all([ this.currentBranch(), @@ -338,12 +288,8 @@ export default class GitAgent { /** * @summary Create a commit with the given message. - * - * @param {string} message - * - * @returns {ReturnType} sha of the commit */ - commit(message) { + commit(message: string): ReturnType { return git.commit({ fs: this.#fs, dir: this.#repoDirectory, @@ -353,11 +299,8 @@ export default class GitAgent { /** * @summary Remove file from git tree and from the file system - * - * @param {string} fileName - * @returns {ReturnType} */ - async removeFile(fileName) { + async removeFile(fileName: string): ReturnType { const path = this.#path(fileName) await this.#fs.promises.unlink(path) return await git.remove({ @@ -369,19 +312,13 @@ export default class GitAgent { /** * @summary like a git pull but the merge is better customized - * - * @returns {Promise} */ - async fetchAndTryMerging() { + async fetchAndTryMerging(): Promise { await this.fetch() await this.tryMerging() } - /** - * - * @return {Promise} - */ - async pullOrCloneRepo() { + async pullOrCloneRepo(): Promise { let dirExists = true try { const stat = await this.#fs.promises.stat(this.#repoDirectory) @@ -408,12 +345,11 @@ export default class GitAgent { * https://isomorphic-git.org/docs/en/setConfig * * Alors, on doit passer le repoName - * - * @param {string} login - * @param {string} email - * @returns {Promise>} */ - async setAuthor(login, email) { + async setAuthor( + login: string, + email: string, + ): Promise> { if (!login || !email) { return } @@ -434,11 +370,8 @@ export default class GitAgent { /** * @summary Get file informations - * - * @param {string} fileName - * @returns {Promise} */ - async getFile(fileName) { + async getFile(fileName: string): Promise { const content = await this.#fs.promises.readFile(this.#path(fileName), { encoding: 'utf8', }) @@ -450,13 +383,11 @@ export default class GitAgent { /** * @summary Create or update a file and add it to the git staging area - * - * @param {string | Uint8Array} content - * @param {string} fileName - * - * @returns {Promise} */ - async writeFile(fileName, content) { + async writeFile( + fileName: string, + content: string | Uint8Array, + ): Promise { // This condition is here just in case, but it should not happen in practice // Having an empty file name will not lead immediately to a crash but will result in // some bugs later, see https://github.com/Scribouilli/scribouilli/issues/49#issuecomment-1648226372 @@ -472,21 +403,11 @@ export default class GitAgent { }) } - /** - * - * @param {string} dir - * @returns - */ - listFiles(dir) { + listFiles(dir: string) { return this.#fs.promises.readdir(this.#path(dir)) } - /** - * - * @param {string} filename - * @returns - */ - async checkFileExistence(filename) { + async checkFileExistence(filename: string) { const stat = await this.#fs.promises.stat(this.#path(filename)) return stat.isFile() } diff --git a/assets/scripts/actions/article.js b/assets/scripts/actions/article.ts similarity index 75% rename from assets/scripts/actions/article.js rename to assets/scripts/actions/article.ts index e5192029..e51c1c77 100644 --- a/assets/scripts/actions/article.js +++ b/assets/scripts/actions/article.ts @@ -1,18 +1,15 @@ -//@ts-check - import lireFrontMatter from 'front-matter' -import store from './../store.js' -import { deleteFileAndPushChanges, writeFileAndPushChanges } from './file.js' -import { keepMarkdownAndHTMLFiles } from './page.js' -import { makeArticleFileName, makeArticleFrontMatter } from './../utils.js' +import store, { type ScribouilliState } from './../store.ts' +import { deleteFileAndPushChanges, writeFileAndPushChanges } from './file.ts' +import { keepMarkdownAndHTMLFiles } from './page.ts' +import { makeArticleFileName, makeArticleFrontMatter } from './../utils.ts' +import type { Article } from '../types/atelier.ts' /** Return whether the blog is enabled or not and whether the "Articles" section * should be available. - * - * @param {import("../store.js").ScribouilliState} state */ -export function showArticles(state) { +export function showArticles(state: ScribouilliState) { const index = blogIndex(state) return index !== undefined } @@ -22,11 +19,8 @@ export function showArticles(state) { * * It is true iff there is a file in the root of the project that has `blogIndex: true` * in its front-matter. - * - * @param {import("../store.js").ScribouilliState} state - * @returns {string | undefined} */ -export function blogIndex(state) { +export function blogIndex(state: ScribouilliState): string | undefined { const pages = state.pages ?? [] const index = pages.find(p => p.blogIndex ?? false)?.path // In previous versions we assumed that the blog index was always @@ -34,11 +28,7 @@ export function blogIndex(state) { return index ?? pages.find(p => p.path === 'blog.md')?.path } -/** - * - * @returns {Promise} - */ -export async function getArticlesList() { +export async function getArticlesList(): Promise { const { gitAgent } = store.state if (!gitAgent) { @@ -57,7 +47,13 @@ export async function getArticlesList() { content.toString(), ) return { - title: data?.title, + title: + typeof data === 'object' && + data !== null && + 'title' in data && + typeof data.title === 'string' + ? data.title + : '', path: fullName, content: markdownContent, } @@ -65,12 +61,9 @@ export async function getArticlesList() { ) } -/** - * @param {string} fileName - * - * @returns {ReturnType} - */ -export const deleteArticle = fileName => { +export const deleteArticle = ( + fileName: string, +): ReturnType => { const { state } = store store.mutations.setArticles( @@ -85,13 +78,10 @@ export const deleteArticle = fileName => { ) } -/** - * @param {string} title - * @param {string} content - * - * @returns {ReturnType} - */ -export const createArticle = (title, content) => { +export const createArticle = ( + title: string, + content: string, +): ReturnType => { const { state } = store const date = new Date() @@ -116,14 +106,11 @@ export const createArticle = (title, content) => { ) } -/** - * @param {string} fileName - * @param {string} content - * @param {string} title - * - * @returns {ReturnType} - */ -export const updateArticle = async (fileName, title, content) => { +export const updateArticle = async ( + fileName: string, + title: string, + content: string, +): ReturnType => { const { gitAgent } = store.state if (!gitAgent) { diff --git a/assets/scripts/actions/current-repository.js b/assets/scripts/actions/current-repository.ts similarity index 72% rename from assets/scripts/actions/current-repository.js rename to assets/scripts/actions/current-repository.ts index a0decf5f..1075b45e 100644 --- a/assets/scripts/actions/current-repository.js +++ b/assets/scripts/actions/current-repository.ts @@ -1,22 +1,24 @@ -//@ts-check - import page from 'page' import yaml from 'js-yaml' -import store from './../store.js' +import store, { type PartialStore } from './../store.ts' import ScribouilliGitRepo, { makeRepoId, makePublicRepositoryURL, -} from './../scribouilliGitRepo.js' -import GitAgent from '../GitAgent.js' -import { handleErrors, logMessage } from './../utils.js' -import { fetchAuthenticatedUserLogin } from './current-user.js' -import makeBuildStatus from './../buildStatus.js' -import { file } from './file.js' -import { getPagesList } from './page.js' -import { getArticlesList } from './article.js' -import { getOAuthServiceAPI } from '../oauth-services-api/index.js' -import { CUSTOM_CSS_PATH } from '../config.js' +} from './../scribouilliGitRepo.ts' +import GitAgent from '../GitAgent.ts' +import { handleErrors, logMessage } from './../utils.ts' +import { fetchAuthenticatedUserLogin } from './current-user.ts' +import { + scheduleCheck, + setBuildingAndCheckStatusLater, +} from './../buildStatus.ts' +import { file } from './file.ts' +import { getPagesList } from './page.ts' +import { getArticlesList } from './article.ts' +import { getOAuthServiceAPI } from '../oauth-services-api/index.ts' +import { CUSTOM_CSS_PATH } from '../config.ts' +import type { BuildStatus } from '../types/git.ts' export const getCurrentRepoPages = () => { return getPagesList().then(store.mutations.setPages).catch(handleErrors) @@ -34,11 +36,10 @@ export const getCurrentRepoArticles = () => { * repository to be functionnal. It sets the current repository in the store, * but also the build status and the site repo config. If the user is not * logged in, it redirects to the authentication page. - * - * @param {string} querystring - * @returns {Promise} */ -export const setCurrentRepositoryFromQuerystring = async querystring => { +export const setCurrentRepositoryFromQuerystring = async ( + querystring: string, +): Promise => { const params = new URLSearchParams(querystring) const repoName = params.get('repoName') const owner = params.get('account') @@ -101,29 +102,36 @@ export const setCurrentRepositoryFromQuerystring = async querystring => { setBuildStatus(scribouilliGitRepo, gitAgent) } -/** - * @param {ScribouilliGitRepo} scribouilliGitRepo - * @param {GitAgent} gitAgent - */ -export const setBuildStatus = (scribouilliGitRepo, gitAgent) => { - store.mutations.setBuildStatus(makeBuildStatus(scribouilliGitRepo, gitAgent)) - /* - Appel sans vérification, - On suppose qu'au chargement initial, - on peut faire confiance à ce que renvoit l'API - */ - store.state.buildStatus.checkStatus() +export const setBuildStatus = ( + scribouilliGitRepo: ScribouilliGitRepo, + gitAgent: GitAgent, +) => { + store.mutations.setBuildStatus('in_progress') + let lastBuildStatus: BuildStatus | undefined = undefined + store.subscribe(state => { + if (state.buildStatus !== lastBuildStatus) { + lastBuildStatus = state.buildStatus + + if ( + state.buildStatus === 'in_progress' || + state.buildStatus === 'needs_account_verification' + ) { + scheduleCheck(scribouilliGitRepo, gitAgent) + } + } + }) + + scheduleCheck(scribouilliGitRepo, gitAgent) } /** * @description if baseurl param is set, always update the config with it * otherwise, wait for currentRepository.publishedWebsiteURL and * compute the new config.baseurl from it - * - * @param {string} [baseUrl] - * @returns {Promise} */ -export const setBaseUrlInConfigIfNecessary = async baseUrl => { +export const setBaseUrlInConfigIfNecessary = async ( + baseUrl?: string, +): Promise => { const currentRepository = store.state.currentRepository if (!currentRepository) { @@ -147,7 +155,6 @@ export const setBaseUrlInConfigIfNecessary = async baseUrl => { } const config = await getCurrentRepoConfig() - /** @type {string} */ const currentBaseURL = config.baseurl || '' if (currentBaseURL === newBaseUrl) { @@ -172,15 +179,14 @@ export const setBaseUrlInConfigIfNecessary = async baseUrl => { ) } } - /** - * @param {string} plugin - * @param {string} version - * @returns {Promise} true if the plugin was installed, false if it was already here. + * @returns true if the plugin was installed, false if it was already here. */ -export async function installPluginIfNecessary(plugin, version) { - /** @type {{ plugins: string[] }} */ - const config = await getCurrentRepoConfig() +export async function installPluginIfNecessary( + plugin: string, + version: string, +): Promise { + const config: { plugins: string[] } = await getCurrentRepoConfig() if (config.plugins.includes(plugin)) { return false } @@ -226,10 +232,7 @@ export async function installPluginIfNecessary(plugin, version) { return true } -/** - * @returns {Promise} - */ -const getCurrentRepoConfig = () => { +const getCurrentRepoConfig = (): Promise => { const { currentRepository, gitAgent } = store.state if (!currentRepository) { @@ -245,13 +248,24 @@ const getCurrentRepoConfig = () => { .catch(handleErrors) } -/** - * @param {string} css - * @returns {ReturnType} - */ -export function saveCustomCSS(css, localStore = store) { - localStore.mutations.setTheme(css) - localStore.state.buildStatus.setBuildingAndCheckStatusLater(10000) +const defaultStore = store +export function saveCustomCSS( + css: string, + store: PartialStore< + 'currentRepository' | 'gitAgent', + 'setTheme' + > = defaultStore, +): ReturnType | Promise { + if (!store.state.currentRepository || !store.state.gitAgent) { + return Promise.resolve() + } + + store.mutations.setTheme(css) + setBuildingAndCheckStatusLater( + store.state.currentRepository, + store.state.gitAgent, + 10000, + ) return file.writeFileAndPushChanges( CUSTOM_CSS_PATH, css, diff --git a/assets/scripts/actions/current-user.js b/assets/scripts/actions/current-user.ts similarity index 71% rename from assets/scripts/actions/current-user.js rename to assets/scripts/actions/current-user.ts index 5d7be9a5..a353e767 100644 --- a/assets/scripts/actions/current-user.js +++ b/assets/scripts/actions/current-user.ts @@ -1,14 +1,10 @@ -//@ts-check - import page from 'page' import { forget } from 'remember' -import { getOAuthServiceAPI } from '../oauth-services-api/index.js' -import store from '../store.js' -import { logMessage } from '../utils.js' -import { OAUTH_PROVIDER_STORAGE_KEY } from '../config.js' - - +import { getOAuthServiceAPI } from '../oauth-services-api/index.ts' +import store, { type ResolutionOption } from '../store.ts' +import { logMessage } from '../utils.ts' +import { OAUTH_PROVIDER_STORAGE_KEY } from '../config.ts' const logout = () => { forget(OAUTH_PROVIDER_STORAGE_KEY) @@ -24,11 +20,14 @@ const logout = () => { * It returns the login of the user or the organization. If the user is not * logged in, it redirects to the authentication page. * - * @returns {Promise<{login: string, email: string}>} A promise that resolves to the login of the - * authenticated user or organization. + * @returns A promise that resolves to the login of the authenticated user or + * organization. * */ -export const fetchAuthenticatedUserLogin = () => { +export const fetchAuthenticatedUserLogin = (): Promise<{ + login: string + email: string +}> => { const loginP = getOAuthServiceAPI() .getAuthenticatedUser() .then(({ login = '' }) => { @@ -45,8 +44,8 @@ export const fetchAuthenticatedUserLogin = () => { switch (errorMessage) { case 'INVALIDATE_TOKEN': case 'NO_LOGIN': { - logout(); - break; + logout() + break } default: @@ -83,19 +82,17 @@ export const fetchAuthenticatedUserLogin = () => { } /** - * @summary Fetch the list of repositories for the current user - * and set it in the store. + * @summary Fetch the list of repositories for the current user and set it in + * the store. * * @description This function is called on every page that needs the list of * repositories for the current user. It returns a promise that resolves to the * list of repositories. If the user is not logged in, it redirects to the * authentication page. * - * @returns A promise that resolves to the list of - * repositories for the current user. - * + * @returns A promise that resolves to the list of repositories for the current + * user. */ - export const fetchCurrentUserRepositories = async () => { const { login } = await fetchAuthenticatedUserLogin() const currentUserRepositoriesP = getOAuthServiceAPI() @@ -110,16 +107,12 @@ export const fetchCurrentUserRepositories = async () => { return currentUserRepositoriesP } -/** - * - * @param {import('./../store.js').ResolutionOption['resolution']} resolution - * @returns {import('./../store.js').ResolutionOption['resolution']} - */ -export function addConflictRemovalAndRedirectToResolution(resolution) { - return function (/** @type {any} */ ...args) { - return resolution(...args).then(() => { - store.mutations.setConflict(undefined) - history.back() - }) +export function addConflictRemovalAndRedirectToResolution( + resolution: ResolutionOption['resolution'], +): ResolutionOption['resolution'] { + return async function (...args: any) { + await resolution(...args) + store.mutations.setConflict(undefined) + history.back() } } diff --git a/assets/scripts/actions/file.js b/assets/scripts/actions/file.ts similarity index 61% rename from assets/scripts/actions/file.js rename to assets/scripts/actions/file.ts index 3ee49ec7..6aefe96d 100644 --- a/assets/scripts/actions/file.js +++ b/assets/scripts/actions/file.ts @@ -1,21 +1,12 @@ -//@ts-check +import GitAgent from '../GitAgent.ts' +import store, { type PartialStore } from '../store.ts' -import GitAgent from '../GitAgent.js' -import store from './../store.js' - -/** - * @param {string} fileName - * @param {string|Uint8Array} content - * @param {string} [commitMessage] - * - * @returns {Promise} - */ export const writeFileAndCommit = async ( - fileName, - content, - commitMessage, - localStore = store, -) => { + fileName: string, + content: string | Uint8Array, + commitMessage?: string, + localStore: PartialStore<'gitAgent', never> = store, +): Promise => { if (typeof commitMessage !== 'string' || commitMessage === '') { commitMessage = `Modification du fichier ${fileName}` } @@ -29,19 +20,12 @@ export const writeFileAndCommit = async ( return gitAgent.commit(commitMessage) } -/** - * @param {string} fileName - * @param {string|Uint8Array} content - * @param {string} [commitMessage] - * - * @returns {ReturnType} - */ export const writeFileAndPushChanges = async ( - fileName, - content, - commitMessage = '', + fileName: string, + content: string | Uint8Array, + commitMessage: string = '', localStore = store, -) => { +): ReturnType => { const { gitAgent } = localStore.state if (!gitAgent) { @@ -52,17 +36,11 @@ export const writeFileAndPushChanges = async ( return gitAgent.safePush() } -/** - * @param {string} fileName - * @param {string} [commitMessage] - * - * @returns {ReturnType} - */ export const deleteFileAndCommit = async ( - fileName, - commitMessage = '', + fileName: string, + commitMessage: string = '', localStore = store, -) => { +): ReturnType => { const { gitAgent } = localStore.state if (!gitAgent) { @@ -77,17 +55,11 @@ export const deleteFileAndCommit = async ( return gitAgent.commit(commitMessage) } -/** - * @param {string} fileName - * @param {string} [commitMessage] - * - * @returns {ReturnType} - */ export const deleteFileAndPushChanges = ( - fileName, - commitMessage, + fileName: string, + commitMessage: string, localStore = store, -) => { +): ReturnType => { const { gitAgent } = localStore.state if (!gitAgent) { diff --git a/assets/scripts/actions/page.js b/assets/scripts/actions/page.ts similarity index 72% rename from assets/scripts/actions/page.js rename to assets/scripts/actions/page.ts index f0bcce55..06e6159c 100644 --- a/assets/scripts/actions/page.js +++ b/assets/scripts/actions/page.ts @@ -1,25 +1,15 @@ -//@ts-check - import lireFrontMatter from 'front-matter' -import store from './../store.js' -import { makeFileNameFromTitle, makePageFrontMatter } from './../utils.js' -import { deleteFileAndPushChanges, writeFileAndPushChanges } from './file.js' +import store from './../store.ts' +import { makeFileNameFromTitle, makePageFrontMatter } from './../utils.ts' +import { deleteFileAndPushChanges, writeFileAndPushChanges } from './file.ts' +import type { Page } from '../types/atelier.ts' -/** - * - * @param {string} filename - * @returns {boolean} - */ -export function keepMarkdownAndHTMLFiles(filename) { +export function keepMarkdownAndHTMLFiles(filename: string): boolean { return filename.endsWith('.md') || filename.endsWith('.html') } -/** - * - * @returns {Promise} - */ -export async function getPagesList() { +export async function getPagesList(): Promise { const { gitAgent } = store.state if (!gitAgent) { @@ -31,9 +21,13 @@ export async function getPagesList() { return Promise.all( allFiles.filter(keepMarkdownAndHTMLFiles).map(async filename => { const content = await gitAgent.getFile(filename) - const { attributes: data, body: markdownContent } = lireFrontMatter( - content.toString(), - ) + // TODO: validate data with zod or equivalent + const { attributes: data, body: markdownContent } = lireFrontMatter<{ + title: string + order: number + in_menu: boolean | undefined + blog_index: boolean | undefined + }>(content.toString()) return { title: data?.title, @@ -48,12 +42,9 @@ export async function getPagesList() { ) } -/** - * @param {string} fileName - * - * @returns {ReturnType} - */ -export const deletePage = fileName => { +export const deletePage = ( + fileName: string, +): ReturnType => { const { state } = store store.mutations.setPages( @@ -69,14 +60,11 @@ export const deletePage = fileName => { ) } -/** - * @param {string} content - * @param {string} title - * @param {number} index - * - * @returns {ReturnType} - */ -export const createPage = (content, title, index) => { +export const createPage = ( + content: string, + title: string, + index: number, +): ReturnType => { const { state } = store const fileName = makeFileNameFromTitle(title) @@ -99,22 +87,13 @@ export const createPage = (content, title, index) => { ) } -/** - * @param {string} fileName - * @param {string} content - * @param {string} title - * @param {number} index - * @param {boolean} blogIndex - * - * @returns {ReturnType} - */ export const updatePage = async ( - fileName, - title, - content, - index, - blogIndex, -) => { + fileName: string, + title: string, + content: string, + index: number, + blogIndex: boolean, +): ReturnType => { const { gitAgent } = store.state if (!gitAgent) { diff --git a/assets/scripts/actions/setup.js b/assets/scripts/actions/setup.ts similarity index 76% rename from assets/scripts/actions/setup.js rename to assets/scripts/actions/setup.ts index 87d8d06e..73726ec2 100644 --- a/assets/scripts/actions/setup.js +++ b/assets/scripts/actions/setup.ts @@ -1,25 +1,21 @@ -//@ts-check - import page from 'page' -import store from './../store.js' +import store, { ResolutionOption } from './../store.ts' import ScribouilliGitRepo, { makePublicRepositoryURL, makeRepoId, -} from './../scribouilliGitRepo.js' -import { getOAuthServiceAPI } from './../oauth-services-api/index.js' -import { makeAtelierListPageURL } from './../routes/urls.js' -import { logMessage } from './../utils.js' -import { setBaseUrlInConfigIfNecessary } from './current-repository.js' -import GitAgent from '../GitAgent.js' - -/** @typedef {import('isomorphic-git')} isomorphicGit */ - -/** - * @param {ScribouilliGitRepo} scribouilliGitRepo - * @returns {Promise} - */ -const waitRepoReady = scribouilliGitRepo => { +} from './../scribouilliGitRepo.ts' +import { getOAuthServiceAPI } from './../oauth-services-api/index.ts' +import { makeAtelierListPageURL } from './../routes/urls.ts' +import { logMessage } from './../utils.ts' +import { setBaseUrlInConfigIfNecessary } from './current-repository.ts' +import GitAgent from '../GitAgent.ts' +import git from 'isomorphic-git' +import type { GitSiteTemplate } from '../types/git.ts' + +const waitRepoReady = ( + scribouilliGitRepo: ScribouilliGitRepo, +): Promise => { return new Promise(resolve => { const timer = setInterval(() => { getOAuthServiceAPI() @@ -35,10 +31,7 @@ const waitRepoReady = scribouilliGitRepo => { }) } -/** - * @returns {Promise} - */ -export const waitOauthProvider = () => { +export const waitOauthProvider = (): Promise => { return new Promise(resolve => { if (store.state.oAuthProvider) resolve() else { @@ -52,10 +45,9 @@ export const waitOauthProvider = () => { }) } -/** - * @returns {Promise>} - */ -export const setupLocalRepository = async () => { +export const setupLocalRepository = async (): Promise< + ReturnType +> => { const login = await store.state.login const { gitAgent, email } = store.state @@ -76,11 +68,12 @@ export const setupLocalRepository = async () => { /** * @summary guess the published URL until a call to OAuthServiceAPI.getPublishedWebsiteURL is made - * - * @param {ScribouilliGitRepo} _ - * @returns {string} */ -export function guessBaseURL({ owner, repoName, origin }) { +export function guessBaseURL({ + owner, + repoName, + origin, +}: ScribouilliGitRepo): string { if (origin === 'https://github.com') { const publishedHostname = `${owner.toLowerCase()}.github.io` repoName = repoName.toLowerCase() @@ -104,16 +97,19 @@ export function guessBaseURL({ owner, repoName, origin }) { * and set a GitHub Pages branch. It redirects to the * list of pages for the atelier. * - * @param {string} repoName - The name of the repository to create - * @param {GitSiteTemplate} template - The git site template to use + * @param repoName The name of the repository to create + * @param template The git site template to use * - * @returns {Promise} A promise that resolves when the repository + * @returns A promise that resolves when the repository * is created. * - * @throws {string} An error message if the repository cannot be created. + * @throws An error message if the repository cannot be created. * */ -export const createRepositoryForCurrentAccount = async (repoName, template) => { +export const createRepositoryForCurrentAccount = async ( + repoName: string, + template: GitSiteTemplate, +): Promise => { const owner = await store.state.login if (!owner) { @@ -157,7 +153,7 @@ export const createRepositoryForCurrentAccount = async (repoName, template) => { repoId: makeRepoId(owner, escapedRepoName), remoteURL: remoteURL, onMergeConflict: ( - /** @type {import("./../store.js").ResolutionOption[] | undefined} */ resolutionOptions, + resolutionOptions: ResolutionOption[] | undefined, ) => { store.mutations.setConflict(resolutionOptions) }, diff --git a/assets/scripts/buildStatus.js b/assets/scripts/buildStatus.ts similarity index 58% rename from assets/scripts/buildStatus.js rename to assets/scripts/buildStatus.ts index 0f02a5e2..ef22c46c 100644 --- a/assets/scripts/buildStatus.js +++ b/assets/scripts/buildStatus.ts @@ -1,87 +1,49 @@ -//@ts-check - -import GitAgent from './GitAgent.js' -import { getOAuthServiceAPI } from './oauth-services-api/index.js' -import { isItStillCompiling } from './utils.js' - -/** - * - * @param {ScribouilliGitRepo} scribouilliGitRepo - * @param {GitAgent} gitAgent - */ -export default function (scribouilliGitRepo, gitAgent) { - /** @type {BuildStatus} */ - let repoStatus = 'in_progress' - /** @type {(status: BuildStatus) => any} */ - let reaction - /** @type {ReturnType | undefined} */ - let timeout - - function scheduleCheck(delay = 5000) { - if (!timeout) { - timeout = setTimeout(() => { - buildStatusObject.checkStatus() - timeout = undefined - }, delay) - } - } - - const buildStatusObject = { - get status() { - return repoStatus - }, - /** - * - * @param {(status: BuildStatus) => any} callback - */ - subscribe(callback) { - reaction = callback - }, - checkStatus() { - return getBuildStatus(scribouilliGitRepo, gitAgent) - .then(status => { - repoStatus = status - if (reaction) { - reaction(repoStatus) - } - - if ( - repoStatus === 'in_progress' || - repoStatus === 'needs_account_verification' - ) { - scheduleCheck() - } - }) - .catch(() => { - repoStatus = 'error' - if (reaction) { - reaction(repoStatus) - } - }) - }, - setBuildingAndCheckStatusLater(t = 30000) { - repoStatus = 'in_progress' - // @ts-ignore - clearTimeout(timeout) +import GitAgent from './GitAgent.ts' +import { getOAuthServiceAPI } from './oauth-services-api/index.ts' +import ScribouilliGitRepo from './scribouilliGitRepo.ts' +import store from './store.ts' +import type { BuildStatus } from './types/git.ts' +import { isItStillCompiling } from './utils.ts' + +let timeout: ReturnType | undefined + +export function scheduleCheck( + scribouilliGitRepo: ScribouilliGitRepo, + gitAgent: GitAgent, + delay = 5000, +) { + if (!timeout) { + timeout = setTimeout(async () => { + const status = await getBuildStatus(scribouilliGitRepo, gitAgent).catch( + () => 'error' as const, + ) + store.mutations.setBuildStatus(status) timeout = undefined - scheduleCheck(t) - }, + }, delay) } +} - buildStatusObject.checkStatus() - return buildStatusObject +export function setBuildingAndCheckStatusLater( + scribouilliGitRepo: ScribouilliGitRepo, + gitAgent: GitAgent, + t = 30000, +) { + store.mutations.setBuildStatus('in_progress') + // @ts-ignore + clearTimeout(timeout) + timeout = undefined + scheduleCheck(scribouilliGitRepo, gitAgent, t) } /** * mimoza includes the hash of the latest built commit in a comment in the HTML * of each page. We use that to know which version is currently online, and * whether the last build succeeded or not. - * - * @param {ScribouilliGitRepo} currentRepository - * @param {GitAgent} gitAgent - * @returns {Promise} */ -async function getBuildStatus(currentRepository, gitAgent) { +async function getBuildStatus( + currentRepository: ScribouilliGitRepo, + gitAgent: GitAgent, +): Promise { const publishedWebsiteURL = await currentRepository.publishedWebsiteURL const lastCommit = await gitAgent.currentCommit() @@ -161,10 +123,7 @@ async function getBuildStatus(currentRepository, gitAgent) { return 'success' } -/** - * @param {ScribouilliGitRepo} scribouilliGitRepo - */ -function getViaApi(scribouilliGitRepo) { +function getViaApi(scribouilliGitRepo: ScribouilliGitRepo) { return getOAuthServiceAPI().getPagesWebsiteDeploymentStatus( scribouilliGitRepo, ) diff --git a/assets/scripts/components/Header.svelte b/assets/scripts/components/Header.svelte index bc207178..2eae015a 100644 --- a/assets/scripts/components/Header.svelte +++ b/assets/scripts/components/Header.svelte @@ -1,81 +1,37 @@ -
diff --git a/assets/scripts/components/Skeleton.svelte b/assets/scripts/components/Skeleton.svelte index 260f7f31..a98e2cc7 100644 --- a/assets/scripts/components/Skeleton.svelte +++ b/assets/scripts/components/Skeleton.svelte @@ -1,26 +1,25 @@ -
diff --git a/assets/scripts/components/screens/Account.svelte b/assets/scripts/components/screens/Account.svelte index f3562b63..01277ddd 100644 --- a/assets/scripts/components/screens/Account.svelte +++ b/assets/scripts/components/screens/Account.svelte @@ -1,13 +1,11 @@ - diff --git a/assets/scripts/components/screens/AfterOauthLogin.svelte b/assets/scripts/components/screens/AfterOauthLogin.svelte index 76dcfc59..facb75da 100644 --- a/assets/scripts/components/screens/AfterOauthLogin.svelte +++ b/assets/scripts/components/screens/AfterOauthLogin.svelte @@ -1,14 +1,13 @@ - diff --git a/assets/scripts/components/screens/ArticleContenu.svelte b/assets/scripts/components/screens/ArticleContenu.svelte index 38e10c79..f1eb29b8 100644 --- a/assets/scripts/components/screens/ArticleContenu.svelte +++ b/assets/scripts/components/screens/ArticleContenu.svelte @@ -1,19 +1,20 @@ - + + + diff --git a/assets/scripts/components/screens/CreateAccount.svelte b/assets/scripts/components/screens/CreateAccount.svelte index bd33d55d..219e6c66 100644 --- a/assets/scripts/components/screens/CreateAccount.svelte +++ b/assets/scripts/components/screens/CreateAccount.svelte @@ -1,13 +1,11 @@ - diff --git a/assets/scripts/components/screens/CreateNewSite.svelte b/assets/scripts/components/screens/CreateNewSite.svelte index 4e40f22d..d09a380b 100644 --- a/assets/scripts/components/screens/CreateNewSite.svelte +++ b/assets/scripts/components/screens/CreateNewSite.svelte @@ -1,15 +1,15 @@ - diff --git a/assets/scripts/components/screens/PageContenu.svelte b/assets/scripts/components/screens/PageContenu.svelte index a7e6bc80..ba93b64d 100644 --- a/assets/scripts/components/screens/PageContenu.svelte +++ b/assets/scripts/components/screens/PageContenu.svelte @@ -1,18 +1,19 @@ - + diff --git a/assets/scripts/components/screens/SelectCurrentSite.svelte b/assets/scripts/components/screens/SelectCurrentSite.svelte index 6b68e670..b6c89bc6 100644 --- a/assets/scripts/components/screens/SelectCurrentSite.svelte +++ b/assets/scripts/components/screens/SelectCurrentSite.svelte @@ -1,21 +1,18 @@ - diff --git a/assets/scripts/components/screens/Settings.svelte b/assets/scripts/components/screens/Settings.svelte index 96f45d23..880a935a 100644 --- a/assets/scripts/components/screens/Settings.svelte +++ b/assets/scripts/components/screens/Settings.svelte @@ -1,24 +1,18 @@ - diff --git a/assets/scripts/components/screens/intern/Editeur.svelte b/assets/scripts/components/screens/intern/Editeur.svelte index d29c0013..a6e2d14d 100644 --- a/assets/scripts/components/screens/intern/Editeur.svelte +++ b/assets/scripts/components/screens/intern/Editeur.svelte @@ -1,5 +1,4 @@ -