Skip to content

Commit

Permalink
feat(cli): Add local developer preview (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
tchock authored Apr 20, 2020
1 parent 24fb1f1 commit 0e7b4d3
Show file tree
Hide file tree
Showing 56 changed files with 1,381 additions and 336 deletions.
7 changes: 6 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@
"axios": "^0.19.2",
"chalk": "2.4.2",
"commander": "4.1.1",
"connect-history-api-fallback": "^1.6.0",
"cors": "^2.8.5",
"date-fns": "^2.12.0",
"etag": "^1.8.1",
"expose-loader": "^0.7.5",
"express": "^4.17.1",
"glob": "^7.1.6",
"ink": "^2.7.1",
"ink-box": "^1.0.0",
Expand All @@ -52,6 +56,7 @@
"@types/node-emoji": "^1.8.1",
"@types/react": "^16.9.34",
"@types/react-intl": "^3.0.0",
"@types/webpack": "4.41.6"
"@types/webpack": "4.41.6",
"@types/webpack-dev-server": "^3.10.1"
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/bundle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { StepRunnerStep } from '../containers/StepRunner';
import { scanStep } from '../steps/scan';
import { compileStep } from '../steps/compile';
import { StepContainer } from '../containers/StepContainer';
import { cleanupStep } from '../steps/cleanup';

import program = require('commander');

const steps: StepRunnerStep[] = [scanStep, compileStep];
const steps: StepRunnerStep[] = [cleanupStep, scanStep, compileStep];
program
.command('bundle')
.option('-d, --dir [dir]', 'The root folder to search components in')
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/src/commands/cleanup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';
import { render } from 'ink';
import { StepRunnerStep } from '../containers/StepRunner';
import { StepContainer } from '../containers/StepContainer';
import { cleanupStep } from '../steps/cleanup';

import program = require('commander');

const steps: StepRunnerStep[] = [cleanupStep];
program
.command('cleanup')
.description('Deletes the bojagi temp folder')
.action(args => {
render(<StepContainer steps={steps} commandArgs={args} />);
});
2 changes: 2 additions & 0 deletions packages/cli/src/commands/deploy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { collectStep } from '../steps/collect';
import { createComponentsStep } from '../steps/createComponents';
import { uploadComponentsStep } from '../steps/uploadComponents';
import { uploadValidator } from '../validators/uploadValidator';
import { cleanupStep } from '../steps/cleanup';

import program = require('commander');

const steps: StepRunnerStep[] = [
cleanupStep,
scanStep,
compileStep,
collectStep,
Expand Down
23 changes: 23 additions & 0 deletions packages/cli/src/commands/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { render } from 'ink';
import { PreviewContainer } from '../containers/PreviewContainer';
import { ConfigProvider } from '../context/configContext';

import program = require('commander');

program
.command('preview')
.option('-d, --dir [dir]', 'The root folder to search components in')
.option(
'--webpack-config [path]',
'Path to the webpack config file, defaults to webpack.config.js'
)
.option('--port [port]', 'Port that the preview server is going to be available in')
.description('starts a local preview server')
.action(({ port }) => {
render(
<ConfigProvider config={{ dryRun: true, previewPort: port }}>
<PreviewContainer />
</ConfigProvider>
);
});
21 changes: 21 additions & 0 deletions packages/cli/src/commands/scan.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { render } from 'ink';
import { StepRunnerStep } from '../containers/StepRunner';
import { scanStep } from '../steps/scan';
import { StepContainer } from '../containers/StepContainer';
import { cleanupStep } from '../steps/cleanup';

import program = require('commander');

const steps: StepRunnerStep[] = [cleanupStep, scanStep];
program
.command('scan')
.option('-d, --dir [dir]', 'The root folder to search components in')
.option(
'--webpack-config [path]',
'Path to the webpack config file, defaults to webpack.config.js'
)
.description('Scans for components')
.action(args => {
render(<StepContainer steps={steps} commandArgs={args} />);
});
6 changes: 0 additions & 6 deletions packages/cli/src/components/Emoji.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { Box } from 'ink';
import * as nodeEmoji from 'node-emoji';
import { useConfig } from '../context/configContext';

export type EmojiCode = keyof typeof nodeEmoji.emoji | 'woman-shrugging';

Expand All @@ -11,11 +10,6 @@ export type EmojiProps = {
};

export function Emoji({ code, marginRight = 0 }: EmojiProps) {
const config = useConfig();
if (config.ci) {
return null;
}

const emoji = nodeEmoji.get(code);

return (
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/components/ErrorMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import * as React from 'react';
import { Box, Color } from 'ink';
import { Message } from './Message';
import { ValidationError } from '../validators/uploadValidator';
import { NonVerboseError } from '../errors';

export type ErrorMessageProps = {
error: Error | ValidationError;
error: Error | NonVerboseError;
};

export function ErrorMessage({ error }: ErrorMessageProps) {
return (
<Box flexDirection="column">
<Message emoji="x">Error: {error.message}</Message>
{!(error as ValidationError).hideStackTrace && (
{!(error as NonVerboseError).hideStackTrace && (
<Box marginX={3} marginBottom={1}>
<Color red>{error.stack}</Color>
</Box>
Expand Down
16 changes: 9 additions & 7 deletions packages/cli/src/components/Step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type StepProps = {
emoji: EmojiCode;
state?: StepState;
children: React.ReactNode;
hideStepCount?: boolean;
};

export function Step({
Expand All @@ -25,26 +26,27 @@ export function Step({
children,
emoji,
state = StepState.PENDING,
hideStepCount = false,
}: StepProps) {
if (state === StepState.PENDING) {
return null;
}

return (
<Box margin={0}>
<Box marginBottom={1}>
<Box marginRight={1}>
<StepStateComponent state={state} />
</Box>
{maxSteps !== 1 && (
<Box marginRight={1}>
<Emoji code={emoji} marginRight={1} />
<Box>{children}</Box>
{!hideStepCount && maxSteps !== 1 && (
<Box marginLeft={1}>
<Color grey>
[{stepNumber}
{maxSteps && <>/{maxSteps}</>}]
({stepNumber}
{maxSteps && <>/{maxSteps}</>})
</Color>
</Box>
)}
<Emoji code={emoji} marginRight={1} />
<Box>{children}</Box>
</Box>
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/components/Steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function Steps({ children }: StepsProps) {
);
}, [children, setInnerChildren]);
return (
<Box flexDirection="column" marginX={1} marginBottom={1}>
<Box flexDirection="column" marginX={1}>
{innerChildren}
</Box>
);
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ export type CollectorTuple = [string, Record<string, any>];
export type BaseConfig = {
componentMarker: string;
dir: string;
dryRun: boolean;
webpackConfig: string;
executionPath: string;
decoratorPath: string;
storyPath: string;
uploadApiUrl: string;
previewPort: number;
previewDownloadUrl: string;
collectors: (string | CollectorTuple)[];
};

Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ import * as path from 'path';

export const EXECUTION_PATH = process.cwd();
export const BOJAGI_FOLDER = path.join(EXECUTION_PATH, '.bojagi');
export const TEMP_FOLDER = path.join(EXECUTION_PATH, '.bojagi', 'tmp');
export const TEMP_FOLDER = path.join(BOJAGI_FOLDER, 'tmp');
export const PREVIEW_CLIENT_FOLDER = path.join(TEMP_FOLDER, 'preview-client');
export const PREVIEW_CLIENT_OUTPUT_FOLDER = path.join(PREVIEW_CLIENT_FOLDER, 'local-dev-latest');
export const PREVIEW_CLIENT_VERSION = '0.1';
59 changes: 20 additions & 39 deletions packages/cli/src/containers/ListContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,38 @@
import * as React from 'react';
import { Box, Text, Color } from 'ink';
import { ComponentExportDescription } from '@bojagi/types';

import { Message } from '../components/Message';
import { StepRunner, StepRunnerStep } from './StepRunner';
import { scanStep, ScanStepOutput } from '../steps/scan';
import { useConfig } from '../context/configContext';
import { SuccessMessage } from '../components/SuccessMessage';

const steps: StepRunnerStep[] = [scanStep];

type FoundComponent = {
name: string;
filePath: string;
components: ComponentExportDescription[];
};
import { useComponentScan } from '../utils/useComponentScan';

export function ListContainer() {
const config = useConfig();
const [foundComponents, setFoundComponents] = React.useState<FoundComponent[]>();
const handleStepSuccess = React.useCallback(
(options: { stepOutputs: { scan: ScanStepOutput } }) => {
console.log('options.istepOutputs.scan.entrypointsWithMetadata', options.stepOutputs.scan);
const { components, getCurrentMessage } = useComponentScan();

setFoundComponents(
Object.entries(options.stepOutputs.scan.entrypointsWithMetadata).map(
([name, entrypoint]) => {
const prefixPath = `${config.executionPath}/`;
const filePath = entrypoint.filePath.replace(new RegExp(`^${prefixPath}`), '');
return {
name,
filePath,
components: entrypoint.components,
};
}
)
const groupedComponentsByFile: Record<string, any> = !components
? {}
: components.reduce(
(acc, component) => ({
...acc,
[component.fileName]: acc[component.fileName]
? [...acc[component.fileName], component]
: [component],
}),
{}
);
},
[config.executionPath]
);

return (
<Box flexDirection="column" marginTop={1}>
<Message emoji="wave">Welcome back!</Message>
<StepRunner steps={steps} onSuccess={handleStepSuccess} />
{foundComponents && foundComponents.length && (
{getCurrentMessage()}
{components && components.length && (
<>
<Box flexDirection="column" marginX={3} marginBottom={1}>
{foundComponents.map(({ name, filePath, components }) => (
<Box flexDirection="column" marginBottom={1}>
<EntryPointTitle name={name} filePath={filePath} />
{Object.entries(groupedComponentsByFile).map(([fileName, componentsOfFile]) => (
<Box key={fileName} flexDirection="column" marginBottom={1}>
<EntryPointTitle name={fileName} filePath={componentsOfFile[0].filePath} />
<Box marginLeft={2} flexDirection="column">
{components.map(component => (
<EntrypointComponent component={component} />
{componentsOfFile.map(component => (
<EntrypointComponent key={component.symbol} component={component} />
))}
</Box>
</Box>
Expand Down
35 changes: 35 additions & 0 deletions packages/cli/src/containers/PreviewContainer/DevServerMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import { Box, Color } from 'ink';
import Spinner from 'ink-spinner';
import { Message } from '../../components/Message';

export function DevServerMessage({ foundComponents, devServer, established, ready, errors }) {
if (!foundComponents || !devServer) {
return null;
}
if (!established) {
return (
<Message emoji="hatching_chick">
Starting Bojagi Preview <Spinner type="dots3" />
</Message>
);
}
if (!ready) {
return (
<Message emoji="hatching_chick">
Bundling <Spinner type="dots3" />
</Message>
);
}
if (errors.length > 0) {
return (
<Box marginX={3} marginBottom={1} flexDirection="column">
<Color red>Following compile errors happened:</Color>
{errors.map(err => (
<Box key={err.message}>{err.message}</Box>
))}
</Box>
);
}
return <Message emoji="chicken">Bojagi Preview server started</Message>;
}
Loading

0 comments on commit 0e7b4d3

Please sign in to comment.