one
Version:
One is a new React Framework that makes Vite serve both native and web.
301 lines (275 loc) • 6.36 kB
text/typescript
import { defineCommand, runMain, showUsage } from 'citty'
import colors from 'picocolors'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
function getPackageVersion() {
let dirname
if (typeof __dirname !== 'undefined') {
// CommonJS
dirname = __dirname
} else {
// ESM
dirname = path.dirname(fileURLToPath(import.meta.url))
}
const packagePath = path.join(dirname, '..', '..', 'package.json')
const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'))
return packageJson.version
}
const version = getPackageVersion()
if (path.sep !== '/') {
console.warn(
colors.bgYellow('WARNING: UNSUPPORTED OS') +
colors.yellow(
' - It appears you’re using Windows, which is currently not supported. You may experience unexpected issues.'
)
)
}
const modes = {
development: 'development',
production: 'production',
} as const
const dev = defineCommand({
meta: {
name: 'dev',
version: version,
description: 'Start the dev server',
},
args: {
clean: {
type: 'boolean',
},
host: {
type: 'string',
},
port: {
type: 'string',
},
https: {
type: 'boolean',
},
mode: {
type: 'string',
description:
'If set to "production" you can run the development server but serve the production bundle',
},
'debug-bundle': {
type: 'string',
description: `Will output the bundle to a temp file and then serve it from there afterwards allowing you to easily edit the bundle to debug problems.`,
},
debug: {
type: 'string',
description: `Pass debug args to Vite`,
},
},
async run({ args }) {
const { dev } = await import('./cli/dev')
await dev({
...args,
debugBundle: args['debug-bundle'],
mode: modes[args.mode],
})
},
})
const buildCommand = defineCommand({
meta: {
name: 'build',
version: version,
description: 'Build your app',
},
args: {
step: {
type: 'string',
required: false,
},
// limit the pages built
only: {
type: 'string',
required: false,
},
platform: {
type: 'string',
description: `One of: web, ios, android`,
default: 'web',
required: false,
},
},
async run({ args }) {
const { build } = await import('./cli/build')
const platforms = {
ios: 'ios',
web: 'web',
android: 'android',
} as const
if (args.platform && !platforms[args.platform]) {
throw new Error(`Invalid platform: ${args.platform}`)
}
const platform = platforms[args.platform as keyof typeof platforms] || 'web'
await build({
only: args.only,
platform,
step: args.step,
})
// TODO somewhere just before 1787f241b79 this stopped exiting, must have some hanging task
process.exit(0)
},
})
const serveCommand = defineCommand({
meta: {
name: 'serve',
version: version,
description: 'Serve a built app for production',
},
args: {
host: {
type: 'string',
},
port: {
type: 'string',
},
compress: {
type: 'boolean',
},
loadEnv: {
type: 'boolean',
},
},
async run({ args }) {
const { serve } = await import('./serve')
await serve({
port: args.port ? +args.port : undefined,
host: args.host,
compress: args.compress,
loadEnv: !!args.loadEnv,
})
},
})
const prebuild = defineCommand({
meta: {
name: 'prebuild',
version: version,
description: 'Prebuild native project',
},
args: {
platform: {
type: 'string',
description: 'ios or android',
},
expo: {
type: 'boolean',
description: 'expo or non-expo folders',
default: true,
},
'no-install': {
type: 'boolean',
description: 'skip installing native dependencies',
default: false,
},
},
async run({ args }) {
if (args.install === false) args['no-install'] = true // citty seems to convert --no-install to install: false, leaving no-install as default
const { run } = await import('./cli/prebuild')
await run(args)
},
})
const runIos = defineCommand({
meta: {
name: 'run:ios',
version: version,
},
args: {},
async run({ args }) {
const { run } = await import('./cli/runIos')
await run(args)
},
})
const runAndroid = defineCommand({
meta: {
name: 'run:android',
version: version,
},
args: {},
async run({ args }) {
const { run } = await import('./cli/runAndroid')
await run(args)
},
})
const clean = defineCommand({
meta: {
name: 'clean',
version: '0.0.0',
description: 'Clean build folders',
},
args: {},
async run() {
const { clean: vxrnClean } = await import('vxrn')
await vxrnClean({
root: process.cwd(),
})
},
})
const patch = defineCommand({
meta: {
name: 'patch',
version: '0.0.0',
description: 'Apply package patches',
},
args: {},
async run({ args }) {
const { run } = await import('./cli/patch')
await run(args)
},
})
const subCommands = {
dev,
clean,
build: buildCommand,
prebuild,
'run:ios': runIos,
'run:android': runAndroid,
patch,
serve: serveCommand,
}
// workaround for having sub-commands but also positional arg for naming in the create flow
const subMain = defineCommand({
meta: {
name: 'main',
version: version,
description: 'Welcome to One',
},
subCommands,
})
const main = defineCommand({
meta: {
name: 'main',
version: version,
description: 'Welcome to One',
},
args: {
name: {
type: 'positional',
description: 'Folder name to place the app into',
required: false,
},
},
async run({ args }) {
if (subCommands[args.name]) {
// run sub command ourselves
runMain(subMain)
return
}
const { cliMain } = await import('./cli/main')
await cliMain(args)
},
})
// workaround for help with our workaround for sub-command + positional arg
const helpIndex = process.argv.indexOf('--help')
if (helpIndex > 0) {
const subCommandName = process.argv[helpIndex - 1]
const subCommand = subCommands[subCommandName]
if (subCommand) {
showUsage(subCommand)
}
} else {
runMain(main)
}