import chalk from 'chalk' import logger from 'loglevel' import { copyFileSync, existsSync, writeFileSync } from 'node:fs' import { basename, isAbsolute, join } from 'node:path' import kebabCase from 'kebab-case' import { userInfo } from 'os' import { ENTER, createFolderIfNotExists, failureDescription, validateName } from '../utils.js' import { PROGRAM_FILE_EXTENSION, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from 'wollok-ts' import { execSync } from 'node:child_process' const { bold, cyan, yellow, green } = chalk export type Options = { project: string, name?: string | undefined, noTest: boolean, noCI: boolean, game: boolean, noGit: boolean, natives?: string } export default function (folder: string | undefined, { project: _project, name, noTest = false, noCI = false, game = false, noGit = false, natives = undefined }: Options): void { try { const project = join(_project, folder ?? '') // Initialization if (existsSync(join(project, 'package.json'))) { throw new Error('There is already a project inside the specified folder') } logger.info(cyan(`Creating project in ${bold(project)}...`)) const exampleName = name ?? 'example' validateName(basename(project)) validateName(exampleName) // Creating folders createFolderIfNotExists(project) if (natives) { const nativesFolder = isAbsolute(natives) ? natives : join(project, natives) createFolderIfNotExists(nativesFolder) } createFolderIfNotExists(join(project, '.github')) createFolderIfNotExists(join(project, '.github', 'workflows')) if (game) { logger.info('Creating assets folder with pepita.png') const assetsFolder = join(project, 'assets') createFolderIfNotExists(assetsFolder) const image = join('public', 'game', 'pepita.png') copyFileSync(image, join(assetsFolder, 'pepita.png')) } // Creating files const definition = game ? wlkDefinitionGame : wlkDefinition logger.info(`Creating definition file ${exampleName}.${WOLLOK_FILE_EXTENSION}`) writeFileSync(join(project, `${exampleName}.${WOLLOK_FILE_EXTENSION}`), definition) if (!noTest) { const definition = game ? testDefinitionGame(exampleName) : testDefinition(exampleName) const testFile = `test${capitalizeFirstLetter(exampleName)}.${TEST_FILE_EXTENSION}` logger.info(`Creating test file ${testFile}`) writeFileSync(join(project, testFile), definition) } if (game) { const gameFile = `main${capitalizeFirstLetter(exampleName)}.${PROGRAM_FILE_EXTENSION}` logger.info(`Creating program file ${gameFile}`) writeFileSync(join(project, `${gameFile}`), gameDefinition(exampleName)) } logger.info('Creating package.json') writeFileSync(join(project, 'package.json'), packageJsonDefinition(project, game, natives )) if (!noCI) { logger.info('Creating CI files') writeFileSync(join(project, '.github', 'workflows', 'ci.yml'), ymlForCI) } logger.info('Creating README') writeFileSync(join(project, 'README.md'), readme(exampleName)) logger.info('Creating Gitignore') writeFileSync(join(project, '.gitignore'), gitignore) if (!noGit) { logger.info('Initializing Git repository') try { execSync('git init --initial-branch=master', { cwd: project }) } catch { logger.error(yellow('🚨 Error initializing git repository, please check if git is installed in your system.')) } } // Finish logger.info(green('✨ Project succesfully created. Happy coding!')) process.exit(0) } catch (error) { logger.error(failureDescription((error as unknown as Error).message)) process.exit(1) } } // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // HELPER FUNCTIONS // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ const capitalizeFirstLetter = (value: string) => value.charAt(0).toUpperCase() + value.slice(1) // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // COMMANDS // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // files made by default const wlkDefinition = `object pepita { var energy = 100 method energy() = energy method fly(minutes) { energy = energy - minutes * 3 } }` const testDefinition = (exampleName: string) => `import ${exampleName}.pepita describe "group of tests for pepita" { test "pepita has initial energy" { assert.equals(100, pepita.energy()) } }` // files made for games const wlkDefinitionGame = `object pepita { var energy = 100 var property position = game.origin() method energy() = energy method image() = "pepita.png" method fly(minutes) { energy = energy - minutes * 3 } }` const testDefinitionGame = (exampleName: string) => `import wollok.game.* import ${exampleName}.pepita describe "group of tests for pepita" { test "pepita has initial energy" { assert.equals(100, pepita.energy()) } test "pepita has an image" { assert.equals("pepita.png", pepita.image()) } }` const gameDefinition = (exampleName: string) => `import wollok.game.* import ${exampleName}.pepita program PepitaGame { game.title("Pepita") game.height(10) game.width(10) game.addVisualCharacter(pepita) game.start() } ` const packageJsonDefinition = (projectName: string, game: boolean, natives?: string) => { const wollokVersion = '4.2.3' // TODO: obtain it from package.json dependency return `{ "name": "${kebabCase(basename(projectName))}", "version": "1.0.0", ${game ? assetsConfiguration() : ''}"wollokVersion": "${wollokVersion}", "author": "${userInfo().username}",${nativesConfiguration(natives)} "license": "ISC" } ` } const assetsConfiguration = () => `"resourceFolder": "assets",${ENTER} ` const nativesConfiguration = (natives?: string) => natives ? `${ENTER} "natives": "${natives}",` : '' const ymlForCI = `name: build on: [push, pull_request] jobs: wollok-ts: runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 - name: Setup Node.js 20 uses: actions/setup-node@v4 with: node-version: 20 - name: Install wollok-ts-cli globally run: npm install -g wollok-ts-cli - run: wollok test --skipValidations -p ./ name: Run tests ` const readme = (exampleName: string) => ` ## ${exampleName} TODO ` const gitignore = ` # Local history .history # Wollok Log log *.log # Dependencies node_modules `