CLI docs (#121)
* Start of cli docs * Document the CLI Also adds support for the `--config` option and `--port` option for the dev server. * Add tests for --config and --port flags * Add port to validateConfig
This commit is contained in:
parent
a7185735da
commit
da033e27ea
13 changed files with 173 additions and 20 deletions
12
README.md
12
README.md
|
@ -52,8 +52,16 @@ export default {
|
||||||
},
|
},
|
||||||
/** Your public domain, e.g.: https://my-site.dev/ */
|
/** Your public domain, e.g.: https://my-site.dev/ */
|
||||||
site: '',
|
site: '',
|
||||||
/** Generate sitemap (set to "false" to disable) */
|
/** Options specific to `astro build` */
|
||||||
sitemap: true,
|
buildOptions: {
|
||||||
|
/** Generate sitemap (set to "false" to disable) */
|
||||||
|
sitemap: true,
|
||||||
|
},
|
||||||
|
/** Options for the development server run with `astro dev`. */
|
||||||
|
devOptions: {
|
||||||
|
/** The port to run the dev server on. */
|
||||||
|
port: 3000
|
||||||
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
41
docs/cli.md
Normal file
41
docs/cli.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
## 👩🏽💻 Command Line Interface
|
||||||
|
|
||||||
|
### Global Flags
|
||||||
|
|
||||||
|
#### `--config path`
|
||||||
|
|
||||||
|
Specify the path to the config file. Defaults to `astro.config.mjs`. Use this if you use a different name for your configuration file or have your config file in another folder.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
astro --config config/astro.config.mjs dev
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `--project-root path`
|
||||||
|
|
||||||
|
Specify the path to the project root. If not specified the current working directory is assumed to be the root.
|
||||||
|
|
||||||
|
The root is used for finding the Astro configuration file.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
astro --project-root examples/snowpack dev
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `--version`
|
||||||
|
|
||||||
|
Print the Astro version number and exit.
|
||||||
|
|
||||||
|
#### `--help`
|
||||||
|
|
||||||
|
Print the help message and exit.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### `astro dev`
|
||||||
|
|
||||||
|
Runs the Astro development server. This starts an HTTP server that responds to requests for pages stored in `astro/pages` (or which folder is specified in your [configuration](../README.md##%EF%B8%8F-configuration)).
|
||||||
|
|
||||||
|
__Flags__
|
||||||
|
|
||||||
|
##### `--port`
|
||||||
|
|
||||||
|
Specifies should port to run on. Defaults to `3000`.
|
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -1788,13 +1788,6 @@
|
||||||
"onetime": "^5.1.2",
|
"onetime": "^5.1.2",
|
||||||
"signal-exit": "^3.0.3",
|
"signal-exit": "^3.0.3",
|
||||||
"strip-final-newline": "^2.0.0"
|
"strip-final-newline": "^2.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"get-stream": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
|
@ -2011,6 +2004,11 @@
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"get-stream": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
|
||||||
|
},
|
||||||
"getpass": {
|
"getpass": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||||
|
|
|
@ -98,6 +98,7 @@
|
||||||
"eslint": "^7.22.0",
|
"eslint": "^7.22.0",
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"execa": "^5.0.0",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"typescript": "^4.2.3",
|
"typescript": "^4.2.3",
|
||||||
"uvu": "^0.5.1"
|
"uvu": "^0.5.1"
|
||||||
|
|
|
@ -17,7 +17,23 @@ export interface AstroConfig {
|
||||||
/** Public URL base (e.g. 'https://mysite.com'). Used in generating sitemaps and canonical URLs. */
|
/** Public URL base (e.g. 'https://mysite.com'). Used in generating sitemaps and canonical URLs. */
|
||||||
site?: string;
|
site?: string;
|
||||||
/** Generate a sitemap? */
|
/** Generate a sitemap? */
|
||||||
sitemap: boolean;
|
buildOptions: {
|
||||||
|
sitemap: boolean;
|
||||||
|
};
|
||||||
|
devOptions: {
|
||||||
|
port: number;
|
||||||
|
projectRoot?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AstroUserConfig = Omit<AstroConfig, 'buildOptions' | 'devOptions'> & {
|
||||||
|
buildOptions: {
|
||||||
|
sitemap: boolean;
|
||||||
|
};
|
||||||
|
devOptions: {
|
||||||
|
port?: number;
|
||||||
|
projectRoot?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JsxItem {
|
export interface JsxItem {
|
||||||
|
|
23
src/cli.ts
23
src/cli.ts
|
@ -20,14 +20,20 @@ type cliCommand = 'help' | 'version' | 'dev' | 'build';
|
||||||
interface CLIState {
|
interface CLIState {
|
||||||
cmd: cliCommand;
|
cmd: cliCommand;
|
||||||
options: {
|
options: {
|
||||||
|
projectRoot?: string;
|
||||||
sitemap?: boolean;
|
sitemap?: boolean;
|
||||||
|
port?: number;
|
||||||
|
config?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Determine which action the user requested */
|
/** Determine which action the user requested */
|
||||||
function resolveArgs(flags: Arguments): CLIState {
|
function resolveArgs(flags: Arguments): CLIState {
|
||||||
const options: CLIState['options'] = {
|
const options: CLIState['options'] = {
|
||||||
|
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot: undefined,
|
||||||
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
|
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
|
||||||
|
port: typeof flags.port === 'number' ? flags.port : undefined,
|
||||||
|
config: typeof flags.config === 'string' ? flags.config : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
if (flags.version) {
|
if (flags.version) {
|
||||||
|
@ -52,13 +58,15 @@ function printHelp() {
|
||||||
console.error(` ${colors.bold('astro')} - Futuristic web development tool.
|
console.error(` ${colors.bold('astro')} - Futuristic web development tool.
|
||||||
|
|
||||||
${colors.bold('Commands:')}
|
${colors.bold('Commands:')}
|
||||||
astro dev Run Astro in development mode.
|
astro dev Run Astro in development mode.
|
||||||
astro build Build a pre-compiled production version of your site.
|
astro build Build a pre-compiled production version of your site.
|
||||||
|
|
||||||
${colors.bold('Flags:')}
|
${colors.bold('Flags:')}
|
||||||
--version Show the version number and exit.
|
--config <path> Specify the path to the Astro config file.
|
||||||
--help Show this help message.
|
--project-root <path> Specify the path to the project root folder.
|
||||||
--no-sitemap Disable sitemap generation (build only).
|
--no-sitemap Disable sitemap generation (build only).
|
||||||
|
--version Show the version number and exit.
|
||||||
|
--help Show this help message.
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,14 +78,17 @@ async function printVersion() {
|
||||||
|
|
||||||
/** Merge CLI flags & config options (CLI flags take priority) */
|
/** Merge CLI flags & config options (CLI flags take priority) */
|
||||||
function mergeCLIFlags(astroConfig: AstroConfig, flags: CLIState['options']) {
|
function mergeCLIFlags(astroConfig: AstroConfig, flags: CLIState['options']) {
|
||||||
if (typeof flags.sitemap === 'boolean') astroConfig.sitemap = flags.sitemap;
|
if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
|
||||||
|
if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle `astro run` command */
|
/** Handle `astro run` command */
|
||||||
async function runCommand(rawRoot: string, cmd: (a: AstroConfig) => Promise<void>, options: CLIState['options']) {
|
async function runCommand(rawRoot: string, cmd: (a: AstroConfig) => Promise<void>, options: CLIState['options']) {
|
||||||
try {
|
try {
|
||||||
const astroConfig = await loadConfig(rawRoot);
|
const projectRoot = options.projectRoot || rawRoot;
|
||||||
|
const astroConfig = await loadConfig(projectRoot, options.config);
|
||||||
mergeCLIFlags(astroConfig, options);
|
mergeCLIFlags(astroConfig, options);
|
||||||
|
|
||||||
return cmd(astroConfig);
|
return cmd(astroConfig);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(colors.red(err.toString() || err));
|
console.error(colors.red(err.toString() || err));
|
||||||
|
|
|
@ -24,6 +24,10 @@ function validateConfig(config: any): void {
|
||||||
throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected boolean, received ${type(config[key])}.`);
|
throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected boolean, received ${type(config[key])}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(config.devOptions?.port !== 'number') {
|
||||||
|
throw new Error(`[astro config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set default config values */
|
/** Set default config values */
|
||||||
|
@ -34,7 +38,8 @@ function configDefaults(userConfig?: any): any {
|
||||||
if (!config.astroRoot) config.astroRoot = './astro';
|
if (!config.astroRoot) config.astroRoot = './astro';
|
||||||
if (!config.dist) config.dist = './_site';
|
if (!config.dist) config.dist = './_site';
|
||||||
if (!config.public) config.public = './public';
|
if (!config.public) config.public = './public';
|
||||||
|
if (!config.devOptions) config.devOptions = {};
|
||||||
|
if (!config.devOptions.port) config.devOptions.port = 3000;
|
||||||
if (typeof config.sitemap === 'undefined') config.sitemap = true;
|
if (typeof config.sitemap === 'undefined') config.sitemap = true;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
@ -53,13 +58,13 @@ function normalizeConfig(userConfig: any, root: string): AstroConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attempt to load an `astro.config.mjs` file */
|
/** Attempt to load an `astro.config.mjs` file */
|
||||||
export async function loadConfig(rawRoot: string | undefined): Promise<AstroConfig> {
|
export async function loadConfig(rawRoot: string | undefined, configFileName = 'astro.config.mjs'): Promise<AstroConfig> {
|
||||||
if (typeof rawRoot === 'undefined') {
|
if (typeof rawRoot === 'undefined') {
|
||||||
rawRoot = process.cwd();
|
rawRoot = process.cwd();
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = pathResolve(rawRoot);
|
const root = pathResolve(rawRoot);
|
||||||
const astroConfigPath = pathJoin(root, 'astro.config.mjs');
|
const astroConfigPath = pathJoin(root, configFileName);
|
||||||
|
|
||||||
// load
|
// load
|
||||||
let config: any;
|
let config: any;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { defaultLogDestination, error, parseError } from './logger.js';
|
||||||
import { createRuntime } from './runtime.js';
|
import { createRuntime } from './runtime.js';
|
||||||
|
|
||||||
const hostname = '127.0.0.1';
|
const hostname = '127.0.0.1';
|
||||||
const port = 3000;
|
|
||||||
|
|
||||||
// Disable snowpack from writing to stdout/err.
|
// Disable snowpack from writing to stdout/err.
|
||||||
snowpackLogger.level = 'silent';
|
snowpackLogger.level = 'silent';
|
||||||
|
@ -65,6 +64,7 @@ export default async function dev(astroConfig: AstroConfig) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const port = astroConfig.devOptions.port;
|
||||||
server.listen(port, hostname, () => {
|
server.listen(port, hostname, () => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Server running at http://${hostname}:${port}/`);
|
console.log(`Server running at http://${hostname}:${port}/`);
|
||||||
|
|
24
test/config-path.test.js
Normal file
24
test/config-path.test.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { suite } from 'uvu';
|
||||||
|
import * as assert from 'uvu/assert';
|
||||||
|
import { runDevServer } from './helpers.js';
|
||||||
|
|
||||||
|
const ConfigPath = suite('Config path');
|
||||||
|
|
||||||
|
const root = new URL('./fixtures/config-path/', import.meta.url);
|
||||||
|
ConfigPath('can be passed via --config', async (context) => {
|
||||||
|
const configPath = new URL('./config/my-config.mjs', root).pathname;
|
||||||
|
const args = ['--config', configPath];
|
||||||
|
const process = runDevServer(root, args);
|
||||||
|
|
||||||
|
process.stdout.setEncoding('utf8');
|
||||||
|
for await (const chunk of process.stdout) {
|
||||||
|
if(/Server running at/.test(chunk)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.kill();
|
||||||
|
assert.ok(true, 'Server started');
|
||||||
|
});
|
||||||
|
|
||||||
|
ConfigPath.run();
|
29
test/config-port.test.js
Normal file
29
test/config-port.test.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { suite } from 'uvu';
|
||||||
|
import * as assert from 'uvu/assert';
|
||||||
|
import { runDevServer } from './helpers.js';
|
||||||
|
import { loadConfig } from '../lib/config.js';
|
||||||
|
|
||||||
|
const ConfigPort = suite('Config path');
|
||||||
|
|
||||||
|
const root = new URL('./fixtures/config-port/', import.meta.url);
|
||||||
|
ConfigPort('can be specified in the astro config', async (context) => {
|
||||||
|
const astroConfig = await loadConfig(root.pathname);
|
||||||
|
assert.equal(astroConfig.devOptions.port, 3001);
|
||||||
|
});
|
||||||
|
|
||||||
|
ConfigPort('can be specified via --port flag', async (context) => {
|
||||||
|
const args = ['--port', '3002'];
|
||||||
|
const process = runDevServer(root, args);
|
||||||
|
|
||||||
|
process.stdout.setEncoding('utf8');
|
||||||
|
for await (const chunk of process.stdout) {
|
||||||
|
if(/Server running at/.test(chunk)) {
|
||||||
|
assert.ok(/:3002/.test(chunk), 'Using the right port');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
ConfigPort.run();
|
5
test/fixtures/config-path/config/my-config.mjs
vendored
Normal file
5
test/fixtures/config-path/config/my-config.mjs
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
extensions: {
|
||||||
|
'.jsx': 'preact',
|
||||||
|
},
|
||||||
|
};
|
6
test/fixtures/config-port/astro.config.mjs
vendored
Normal file
6
test/fixtures/config-port/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
export default {
|
||||||
|
devOptions: {
|
||||||
|
port: 3001
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import { readFile } from 'fs/promises';
|
||||||
import { createRuntime } from '../lib/runtime.js';
|
import { createRuntime } from '../lib/runtime.js';
|
||||||
import { loadConfig } from '../lib/config.js';
|
import { loadConfig } from '../lib/config.js';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
|
import execa from 'execa';
|
||||||
|
|
||||||
/** setup fixtures for tests */
|
/** setup fixtures for tests */
|
||||||
export function setup(Suite, fixturePath) {
|
export function setup(Suite, fixturePath) {
|
||||||
let runtime, setupError;
|
let runtime, setupError;
|
||||||
|
@ -62,3 +64,10 @@ export function setupBuild(Suite, fixturePath) {
|
||||||
assert.equal(setupError, undefined);
|
assert.equal(setupError, undefined);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cliURL = new URL('../astro.mjs', import.meta.url);
|
||||||
|
export function runDevServer(root, additionalArgs = []) {
|
||||||
|
const args = [cliURL.pathname, 'dev', '--project-root', root.pathname].concat(additionalArgs);
|
||||||
|
const proc = execa('node', args);
|
||||||
|
return proc;
|
||||||
|
}
|
Loading…
Reference in a new issue