diff --git a/astro.mjs b/astro.mjs index a65dfa33d5..1aa8873736 100755 --- a/astro.mjs +++ b/astro.mjs @@ -1,30 +1,4 @@ #!/usr/bin/env node -import { join as pathJoin, resolve as pathResolve } from 'path'; +import { cli } from './lib/cli.js'; -import generate from './lib/generate.js'; -import devServer from './lib/dev.js'; - -const root = pathResolve(process.argv[2]); - -if(!root) { - console.error('Must provide a project root'); - process.exit(1); -} - -const fileProtocolRoot = `file://${root}/`; - -async function run() { - const astroConfig = (await import(pathJoin(root, 'astro.config.mjs'))).default; - astroConfig.projectRoot = new URL(astroConfig.projectRoot + '/', fileProtocolRoot); - astroConfig.hmxRoot = new URL(astroConfig.hmxRoot + '/', fileProtocolRoot); - - - // Should use an args parser eventually - if(process.argv.includes('--generate')) { - return generate(astroConfig); - } else { - return devServer(astroConfig); - } -} - -run().catch(err => setTimeout(() => {throw err})); \ No newline at end of file +cli(process.argv); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 823e51f4e4..037d9f8d7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "hmx-v2", + "name": "astro", "version": "1.0.0", "lockfileVersion": 1, "requires": true, @@ -29,6 +29,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.34.tgz", "integrity": "sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA==" }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, "@vue/compiler-core": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.7.tgz", @@ -650,6 +656,11 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "kleur": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", + "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==" + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1220,8 +1231,7 @@ "yargs-parser": { "version": "20.2.7", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "dev": true + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" } } } diff --git a/package.json b/package.json index 606f80aa0d..ed792e8275 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "1.0.0", + "version": "0.0.1", "main": "index.js", "private": true, "author": "Skypack", @@ -30,6 +30,7 @@ "es-module-lexer": "^0.4.1", "gray-matter": "^4.0.2", "htmlparser2": "^6.0.0", + "kleur": "^4.1.4", "magic-string": "^0.25.3", "micromark": "^2.11.4", "micromark-extension-gfm": "^0.3.3", @@ -37,9 +38,11 @@ "react-dom": "^17.0.1", "snowpack": "^3.1.0-pre.12", "svelte": "^3.35.0", - "vue": "^3.0.7" + "vue": "^3.0.7", + "yargs-parser": "^20.2.7" }, "devDependencies": { + "@types/yargs-parser": "^20.2.0", "copyfiles": "^2.4.1", "estree-walker": "^3.0.0", "preact": "^10.5.12", diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000000..c153878700 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,105 @@ +import type { AstroConfig } from './@types/astro'; + +import * as colors from 'kleur/colors'; +import { join as pathJoin, resolve as pathResolve } from 'path'; +import { existsSync, promises as fsPromises } from 'fs'; +import yargs from 'yargs-parser'; + +import generate from './generate.js'; +import devServer from './dev.js'; + +const { readFile } = fsPromises; + +type Arguments = yargs.Arguments; +type cliState = 'help' | 'version' | 'dev' | 'build'; + +function resolveArgs(flags: Arguments): cliState { + if(flags.version) { + return 'version'; + } else if(flags.help) { + return 'help'; + } + + const cmd = flags._[2]; + switch(cmd) { + case 'dev': return 'dev'; + case 'build': return 'build'; + default: return 'help'; + } +} + +function printHelp() { + console.error(` ${colors.bold('astro')} - Futuristic web development tool. + + ${colors.bold('Commands:')} + astro dev Run astro in development mode. + astro build Build a pre-compiled production version of your site. + + ${colors.bold('Flags:')} + --version Show the version number and exit. + --help Show this help message. +`); +} + +async function printVersion() { + const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf-8')); + console.error(pkg.version); +} + +async function loadConfig(rawRoot: string | undefined): Promise { + if(typeof rawRoot === 'undefined') { + rawRoot = process.cwd(); + } + + const root = pathResolve(rawRoot); + const fileProtocolRoot = `file://${root}/`; + const astroConfigPath = pathJoin(root, 'astro.config.mjs'); + + if(!existsSync(astroConfigPath)) { + return undefined; + } + + const astroConfig: AstroConfig = (await import(astroConfigPath)).default; + astroConfig.projectRoot = new URL(astroConfig.projectRoot + '/', fileProtocolRoot); + astroConfig.hmxRoot = new URL(astroConfig.hmxRoot + '/', fileProtocolRoot); + return astroConfig; +} + +async function runCommand(rawRoot: string, cmd: (a: AstroConfig) => Promise) { + const astroConfig = await loadConfig(rawRoot); + if(typeof astroConfig === 'undefined') { + console.error(colors.red(' An astro.config.mjs file is required.\n')); + printHelp(); + process.exit(1); + } + + return cmd(astroConfig); +} + +const cmdMap = new Map([ + ['build', generate], + ['dev', devServer] +]); + +export async function cli(args: string[]) { + const flags = yargs(args); + const state = resolveArgs(flags); + + switch(state) { + case 'help': { + printHelp(); + process.exit(1); + break; + } + case 'version': { + await printVersion(); + process.exit(0); + break; + } + case 'build': + case 'dev': { + const cmd = cmdMap.get(state)!; + runCommand(flags._[3], cmd); + } + } +} \ No newline at end of file