From 0a4e62c25b68adf369496a86312342d16c8290f9 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 24 Jan 2025 16:21:47 +0100 Subject: [PATCH] chore(test-project): Switch tui-tasks to TypeScript (#11926) --- tasks/test-project/codemods/models.js | 14 ++--- .../rebuild-fragments-test-project-fixture.ts | 14 ++--- .../rebuild-test-project-fixture.ts | 14 ++--- tasks/test-project/tasks.js | 1 - .../{tui-tasks.js => tui-tasks.ts} | 61 +++++++++---------- tasks/test-project/typing.ts | 14 +++++ tasks/tsconfig.json | 3 +- 7 files changed, 65 insertions(+), 56 deletions(-) rename tasks/test-project/{tui-tasks.js => tui-tasks.ts} (96%) diff --git a/tasks/test-project/codemods/models.js b/tasks/test-project/codemods/models.js index 46bb8ea12b4c..92dcfaba5005 100644 --- a/tasks/test-project/codemods/models.js +++ b/tasks/test-project/codemods/models.js @@ -1,6 +1,4 @@ -/* eslint-env node, es6*/ - -const post = `model Post { +export const post = `model Post { id Int @id @default(autoincrement()) title String body String @@ -9,7 +7,7 @@ const post = `model Post { createdAt DateTime @default(now()) }` -const contact = `model Contact { +export const contact = `model Contact { id Int @id @default(autoincrement()) name String email String @@ -17,7 +15,7 @@ const contact = `model Contact { createdAt DateTime @default(now()) }` -const user = `model User { +export const user = `model User { id Int @id @default(autoincrement()) email String @unique hashedPassword String @@ -29,7 +27,7 @@ const user = `model User { posts Post[] }` -const produce = `model Produce { +export const produce = `model Produce { id String @id @default(cuid()) name String @unique quantity Int @@ -48,11 +46,9 @@ const produce = `model Produce { stallId String }` -const stall = `model Stall { +export const stall = `model Stall { id String @id @default(cuid()) name String stallNumber String @unique produce Produce[] }` - -module.exports = { post, contact, user, produce, stall } diff --git a/tasks/test-project/rebuild-fragments-test-project-fixture.ts b/tasks/test-project/rebuild-fragments-test-project-fixture.ts index 939161fc32fc..920e9373e648 100755 --- a/tasks/test-project/rebuild-fragments-test-project-fixture.ts +++ b/tasks/test-project/rebuild-fragments-test-project-fixture.ts @@ -15,7 +15,7 @@ import { copyFrameworkPackages, } from './frameworkLinking' import { webTasks, apiTasks, fragmentsTasks } from './tui-tasks' -import { isAwaitable } from './typing' +import { isAwaitable, isTuiError } from './typing' import type { TuiTaskDef } from './typing' import { getExecaOptions as utilGetExecaOptions, @@ -140,13 +140,16 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) { 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr, ) } else { + const message = isTuiError(e) ? e.message : '' + tui.displayError( 'Failed ' + title.toLowerCase().replace('...', ''), - e.message, + message || '', ) } - process.exit(e.exitCode) + const exitCode = isTuiError(e) ? e.exitCode : undefined + process.exit(exitCode) } if (isAwaitable(promise)) { @@ -197,11 +200,8 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) { /** * Function that returns a string to show when skipping the task, or just * true|false to indicate whether the task should be skipped or not. - * - * @param {string} startStep - * @param {string} currentStep */ -function skipFn(startStep, currentStep) { +function skipFn(startStep: string, currentStep: string) { const startStepNrs = startStep.split('.').map((s) => parseInt(s, 10)) const currentStepNrs = currentStep.split('.').map((s) => parseInt(s, 10)) diff --git a/tasks/test-project/rebuild-test-project-fixture.ts b/tasks/test-project/rebuild-test-project-fixture.ts index b7e66f932ea8..37157f6dc857 100755 --- a/tasks/test-project/rebuild-test-project-fixture.ts +++ b/tasks/test-project/rebuild-test-project-fixture.ts @@ -15,7 +15,7 @@ import { copyFrameworkPackages, } from './frameworkLinking' import { webTasks, apiTasks } from './tui-tasks' -import { isAwaitable } from './typing' +import { isAwaitable, isTuiError } from './typing' import type { TuiTaskDef } from './typing' import { getExecaOptions as utilGetExecaOptions, @@ -140,13 +140,16 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) { 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr, ) } else { + const message = isTuiError(e) ? e.message : '' + tui.displayError( 'Failed ' + title.toLowerCase().replace('...', ''), - e.message, + message || '', ) } - process.exit(e.exitCode) + const exitCode = isTuiError(e) ? e.exitCode : undefined + process.exit(exitCode) } if (isAwaitable(promise)) { @@ -197,11 +200,8 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) { /** * Function that returns a string to show when skipping the task, or just * true|false to indicate whether the task should be skipped or not. - * - * @param {string} startStep - * @param {string} currentStep */ -function skipFn(startStep, currentStep) { +function skipFn(startStep: string, currentStep: string) { const startStepNrs = startStep.split('.').map((s) => parseInt(s, 10)) const currentStepNrs = currentStep.split('.').map((s) => parseInt(s, 10)) diff --git a/tasks/test-project/tasks.js b/tasks/test-project/tasks.js index 77c995ed7529..b708c0e67cde 100644 --- a/tasks/test-project/tasks.js +++ b/tasks/test-project/tasks.js @@ -388,7 +388,6 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) { await execa( 'yarn rw g dbAuth --no-webauthn --username-label=username --password-label=password', [], - execaOptions, ) // update directive in contacts.sdl.ts diff --git a/tasks/test-project/tui-tasks.js b/tasks/test-project/tui-tasks.ts similarity index 96% rename from tasks/test-project/tui-tasks.js rename to tasks/test-project/tui-tasks.ts index e3354ea4a550..5e6aaffbdf0f 100644 --- a/tasks/test-project/tui-tasks.js +++ b/tasks/test-project/tui-tasks.ts @@ -1,7 +1,11 @@ /* eslint-env node, es2021*/ -//@ts-check -const fs = require('fs') -const path = require('path') + +import fs from 'node:fs' +import path from 'node:path' + +import type { Options as ExecaOptions, ExecaChildProcess } from 'execa' + +import type { TuiTaskList } from './typing.js' const { getExecaOptions: utilGetExecaOptions, @@ -10,18 +14,17 @@ const { exec, } = require('./util') -/** @type {(string) => import('execa').Options} */ -function getExecaOptions(cwd) { +function getExecaOptions(cwd: string): ExecaOptions { return { ...utilGetExecaOptions(cwd), stdio: 'pipe' } } // This variable gets used in other functions // and is set when webTasks or apiTasks are called -let OUTPUT_PATH +let OUTPUT_PATH: string const RW_FRAMEWORK_PATH = path.join(__dirname, '../../') -function fullPath(name, { addExtension } = { addExtension: true }) { +function fullPath(name: string, { addExtension } = { addExtension: true }) { if (addExtension) { if (name.startsWith('api')) { name += '.ts' @@ -34,7 +37,7 @@ function fullPath(name, { addExtension } = { addExtension: true }) { } // TODO: Import from ./util.js when everything is using @rwjs/tui -async function applyCodemod(codemod, target) { +async function applyCodemod(codemod: string, target: string) { const args = [ '--fail-on-error', '-t', @@ -56,14 +59,14 @@ async function applyCodemod(codemod, target) { } /** - * @param {string} cmd The command to run - * @returns {((positionalArguments: string | string[]) => import('execa').ExecaChildProcess) - * & (() => import('execa').ExecaChildProcess)} + * @param cmd The command to run */ -const createBuilder = (cmd) => { +function createBuilder(cmd: string) { const execaOptions = getExecaOptions(OUTPUT_PATH) - return function (positionalArguments) { + return function ( + positionalArguments?: string | string[], + ): ExecaChildProcess { const subprocess = exec( cmd, Array.isArray(positionalArguments) @@ -76,7 +79,10 @@ const createBuilder = (cmd) => { } } -async function webTasks(outputPath, { linkWithLatestFwBuild }) { +export async function webTasks( + outputPath: string, + { linkWithLatestFwBuild }: { linkWithLatestFwBuild: boolean }, +) { OUTPUT_PATH = outputPath const execaOptions = getExecaOptions(outputPath) @@ -84,8 +90,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild }) { const createPages = async () => { const createPage = createBuilder('yarn redwood g page') - /** @type import('./typing').TuiTaskList */ - const tuiTaskList = [ + const tuiTaskList: TuiTaskList = [ { title: 'Creating home page', task: async () => { @@ -317,8 +322,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild }) { ) } - /** @type import('./typing').TuiTaskList */ - const tuiTaskList = [ + const tuiTaskList: TuiTaskList = [ { title: 'Creating pages', task: () => createPages(), @@ -389,7 +393,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild }) { return tuiTaskList } -async function addModel(schema) { +async function addModel(schema: string) { const path = `${OUTPUT_PATH}/api/db/schema.prisma` const current = fs.readFileSync(path, 'utf-8') @@ -397,7 +401,10 @@ async function addModel(schema) { fs.writeFileSync(path, `${current.trim()}\n\n${schema}\n`) } -async function apiTasks(outputPath, { linkWithLatestFwBuild }) { +export async function apiTasks( + outputPath: string, + { linkWithLatestFwBuild }: { linkWithLatestFwBuild: boolean }, +) { OUTPUT_PATH = outputPath const execaOptions = getExecaOptions(outputPath) @@ -714,8 +721,7 @@ export default DoublePage` const generateScaffold = createBuilder('yarn rw g scaffold') - /** @type import('./typing').TuiTaskList */ - const tuiTaskList = [ + const tuiTaskList: TuiTaskList = [ { title: 'Adding post and user model to prisma', task: async () => { @@ -917,11 +923,10 @@ export default DoublePage` * Tasks to add GraphQL Fragments support to the test-project, and some queries * to test fragments */ -async function fragmentsTasks(outputPath) { +export async function fragmentsTasks(outputPath: string) { OUTPUT_PATH = outputPath - /** @type import('./typing').TuiTaskList */ - const tuiTaskList = [ + const tuiTaskList: TuiTaskList = [ { title: 'Enable fragments', task: async () => { @@ -1029,9 +1034,3 @@ async function fragmentsTasks(outputPath) { return tuiTaskList } - -module.exports = { - apiTasks, - webTasks, - fragmentsTasks, -} diff --git a/tasks/test-project/typing.ts b/tasks/test-project/typing.ts index d4b15fb30e89..24d1beced795 100644 --- a/tasks/test-project/typing.ts +++ b/tasks/test-project/typing.ts @@ -16,6 +16,14 @@ export interface TuiTaskDef { task: () => Promise | void } +interface TuiTaskListItem { + title: string + enabled?: boolean | (() => boolean) + task: () => Promise | void +} + +export type TuiTaskList = TuiTaskListItem[] + export function isAwaitable(promise: unknown): promise is Promise { return ( !!promise && @@ -24,3 +32,9 @@ export function isAwaitable(promise: unknown): promise is Promise { typeof promise.then === 'function' ) } + +export function isTuiError( + error: unknown, +): error is { message?: string; exitCode?: number } { + return error instanceof Object +} diff --git a/tasks/tsconfig.json b/tasks/tsconfig.json index 8598869e8c39..c42ff9ecd056 100644 --- a/tasks/tsconfig.json +++ b/tasks/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../tsconfig.compilerOption.json", "compilerOptions": { "moduleResolution": "NodeNext", - "module": "NodeNext" + "module": "NodeNext", + "allowJs": true } }