commit b9d2db7ac97be655a3ba4ed2d1ded638ecad2fe3 Author: Zoë Date: Mon Jun 29 15:47:16 2026 +0200 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c1322dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6340407 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 100, + "tabWidth": 4, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8abd2c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,55 @@ +{ + "name": "expo-kit", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "expo-kit", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "child_process": "^1.0.2", + "commander": "^15.0.0" + }, + "bin": { + "expo-kit": "dist/index.js" + }, + "devDependencies": { + "@types/node": "^26.0.1" + } + }, + "node_modules/@types/node": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", + "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", + "license": "ISC" + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3041580 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "expo-kit", + "version": "1.0.0", + "description": "Expo starter kit CLI", + "repository": { + "type": "git", + "url": "ssh://git@git.justzoe101.nl:2222/Expo-Starter-Kit/CLI.git" + }, + "license": "ISC", + "author": "JustZoe101", + "type": "commonjs", + "main": "index.js", + "scripts": { + "start": "npm run clean; npm run build; node ./dist/index.js", + "build": "tsc", + "clean": "rm -rf ./dist" + }, + "bin": "./dist/index.js", + "dependencies": { + "child_process": "^1.0.2", + "commander": "^15.0.0" + }, + "devDependencies": { + "@types/node": "^26.0.1" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..74b20ec --- /dev/null +++ b/src/index.ts @@ -0,0 +1,133 @@ +import { Command, program } from 'commander'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import fs from 'fs'; + +const execAsync = promisify(exec); + +program.name('Expo kit').description('Expo framework for multiple plugins').version('1.0.0'); + +program + .command('new') + .argument('', 'Project name') + .option('--reactnavigation', 'Adds reactnavigation to project') + .option('--nativewind', 'Adds nativewind to project') + .option('--i18next', 'Adds i18next to project') + .action(createProject); + +async function createProject(name: string, ...args: any[]) { + const dir = `${__dirname}/${name}`; + const options: { + reactnavigation: boolean; + nativewind: boolean; + i18next: boolean; + } = args[0]; + + console.log('📦 Installing starter template...'); + + await execAsync( + `git clone https://git.justzoe101.nl/Expo-Starter-Kit/Template.git ${dir} --depth 1` + ); + + combineAppJson(dir, { + appJson: { + expo: { + name, + slug: name.toLowerCase(), + }, + }, + }); + + await execAsync('npm i', { + cwd: dir, + }); + + console.log('✅ Starter template installed!\r\n'); + + if (options.reactnavigation) { + console.log('📦 Installing reactnavigation dependencies...'); + const plugin = require(`${dir}/plugins/reactnavigation/plugin.json`); + await _installDependencies(dir, plugin); + console.log('✅ Reactnavigation installed!\r\n'); + } + + if (options.nativewind) { + console.log('📦 Installing nativewind dependencies...'); + const plugin = require(`${dir}/plugins/nativewind/plugin.json`); + await _installDependencies(dir, plugin); + console.log('✅ Nativewind installed!\r\n'); + } + + if (options.i18next) { + console.log('📦 Installing i18next dependencies...'); + const plugin = require(`${dir}/plugins/i18next/plugin.json`); + await _installDependencies(dir, plugin); + combineAppJson(dir, plugin); + console.log('✅ I18next installed!\r\n'); + } +} + +async function _installDependencies(dir: string, plugin: Plugin) { + const keys = Object.keys(plugin); + + if (keys.includes('dependencies')) { + let dependencies: any = Object.entries(plugin.dependencies!); + dependencies = dependencies.flatMap((d: any[]) => `${d[0]}@${d[1]}`).join(' '); + + await execAsync(`npm i ${dependencies}`, { + cwd: dir, + }); + } + + if (keys.includes('devDependencies')) { + let dependencies: any = Object.entries(plugin.devDependencies!); + dependencies = dependencies.flatMap((d: any[]) => `${d[0]}@${d[1]}`).join(' '); + + await execAsync(`npm i ${dependencies} --save-dev`, { + cwd: dir, + }); + } + + if (keys.includes('expoDependencies')) { + let dependencies: any = plugin.expoDependencies!.join(' '); + + await execAsync(`npx expo install ${dependencies}`, { + cwd: dir, + }); + } +} + +function combineAppJson(dir: string, plugin: Plugin) { + fs.writeFileSync( + `${dir}/app.json`, + JSON.stringify(deepMerge(require(`${dir}/app.json`), plugin.appJson), null, 4), + 'utf8' + ); +} + +function deepMerge(target: any, source: any) { + if (Array.isArray(target) && Array.isArray(source)) { + return source; // or target.concat(source) if that's what you want + } + + if (target && source && typeof target === 'object' && typeof source === 'object') { + const result = { ...target }; + + for (const key of Object.keys(source)) { + result[key] = key in target ? deepMerge(target[key], source[key]) : source[key]; + } + + return result; + } + + return source; +} + +type Plugin = { + dependencies?: Record; + devDependencies?: Record; + expoDependencies?: string[]; + appJson?: object; +}; + +program.parse(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5ee394d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,46 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": ["node"], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": false, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["./src/**/*.json", "./src/**/*.ts"] +}