generated from bywhitebird/starter-node
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from bywhitebird/improve-ux-dx
Improve ux dx
- Loading branch information
Showing
102 changed files
with
2,303 additions
and
1,266 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import process from 'node:process' | ||
|
||
import { program } from 'commander' | ||
|
||
import { generateCommand } from './commands/generate' | ||
|
||
program | ||
.addCommand(generateCommand) | ||
|
||
export const runCli = () => program.parse(process.argv) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import * as fs from 'node:fs' | ||
|
||
import { loadConfig } from 'c12' | ||
import { Command } from 'commander' | ||
import { type AppProps, render } from 'ink' | ||
import React from 'react' | ||
|
||
import { generate } from '../../../application/usecases/generate' | ||
import type { KazamConfig } from '../../../types/kazam-config' | ||
import { GenerateView } from '../views/generate' | ||
|
||
export const generateCommand = new Command() | ||
.command('generate') | ||
.description('Generate code from your Kazam files') | ||
.option('-c, --config <path>', 'Path to the config file', (path) => { | ||
if (!fs.existsSync(path)) | ||
throw new Error(`Could not find config file at ${path}`) | ||
|
||
return path | ||
}) | ||
.action(async (options) => { | ||
let exit: AppProps['exit'] | undefined | ||
const setExit = (_exit: AppProps['exit']) => exit = _exit | ||
|
||
const { config, configFile } = await loadConfig<KazamConfig>({ | ||
name: 'kazam', | ||
...(options.config && { configFile: options.config }), | ||
}) | ||
|
||
if (config === null || configFile === undefined) | ||
throw new Error('Could not load config') | ||
|
||
const generatePromise = generate(config, configFile, fs) | ||
|
||
render( | ||
<GenerateView | ||
generatePromise={generatePromise} | ||
setExit={setExit} | ||
/>, | ||
) | ||
|
||
generatePromise.finally(exit) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import Spinner_ from 'ink-spinner' | ||
import React from 'react' | ||
|
||
export const Spinner = () => <Spinner_ /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// declare module 'commander' { | ||
// export * from '@commander-js/extra-typings' | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import path from 'node:path' | ||
|
||
import { type AppProps, Text, useApp } from 'ink' | ||
import React, { useEffect, useState } from 'react' | ||
|
||
import type { generate } from '../../../application/usecases/generate' | ||
import { generateEvents } from '../../../core/events/generate' | ||
import { Spinner } from '../components/spinner' | ||
|
||
export const GenerateView = ( | ||
{ generatePromise, setExit }: | ||
{ generatePromise: ReturnType<typeof generate>; setExit?: (exit: AppProps['exit']) => void }, | ||
) => { | ||
const { exit } = useApp() | ||
|
||
const [status, setStatus] = useState< | ||
| { success: true } | ||
| { error: string } | ||
| { pending: true } | ||
>({ pending: true }) | ||
const [writtenPaths, setWrittenPaths] = useState<string[]>([]) | ||
|
||
useEffect(() => { | ||
setExit?.(exit) | ||
|
||
generatePromise | ||
.then(() => setStatus({ success: true })) | ||
.catch((error) => { | ||
setStatus({ error }) | ||
console.error('ERROR', error) | ||
}) | ||
|
||
generateEvents.on('file-written', filePath => | ||
setWrittenPaths(writtenPaths => [...writtenPaths, filePath]), | ||
) | ||
}, []) | ||
|
||
if ('success' in status) { | ||
return <> | ||
<Text><Text color="green">✔</Text> Successfully generated components</Text> | ||
{writtenPaths.map(writtenPath => | ||
<Text key={writtenPath} color='gray'>{' '}{path.normalize(writtenPath)}</Text>, | ||
)} | ||
</> | ||
} | ||
|
||
if ('error' in status) { | ||
return <> | ||
<Text><Text color="red">✖</Text> {status.error}</Text> | ||
</> | ||
} | ||
|
||
return <> | ||
<Text> | ||
<Text color="blue"> | ||
<Spinner /> | ||
</Text> | ||
{' Generating components...'} | ||
</Text> | ||
</> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { defineConfig as defineConfig_ } from '../../application/usecases/define-config' | ||
import type { UserConfig } from '../../types/kazam-config' | ||
|
||
export const defineConfig = (config: UserConfig) => defineConfig_(config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type * as fs from 'node:fs' | ||
|
||
import { generate as generate_ } from '../../application/usecases/generate' | ||
import type { KazamConfig } from '../../types/kazam-config' | ||
|
||
export const generate = ( | ||
{ rootDir, ...config }: KazamConfig & { rootDir: string }, | ||
fileSystem?: typeof fs | undefined, | ||
) => generate_(config, rootDir, fileSystem) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { defineConfig } from './define-config' | ||
export { generate } from './generate' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { kazamConfigSchema } from '../../core/schemas/kazam-config' | ||
import type { UserConfig } from '../../types/kazam-config' | ||
|
||
export const defineConfig = (config: UserConfig) => kazamConfigSchema.parse(config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import type * as fs from 'node:fs' | ||
|
||
import type { TransformerOutput } from '@whitebird/kazam-transformer-base' | ||
import kebabCase from 'just-kebab-case' | ||
|
||
import { generateEvents } from '../../core/events/generate' | ||
import type { KazamConfig } from '../../types/kazam-config' | ||
import type { Transformer } from '../../types/transformer' | ||
|
||
const writeResults = ( | ||
files: Map<string, string>, | ||
fileSystem: typeof fs, | ||
) => { | ||
files.forEach((fileContents, filePath) => { | ||
const directoryPath = filePath.split('/').slice(0, -1).join('/') | ||
|
||
fileSystem.mkdirSync(directoryPath, { recursive: true }) | ||
fileSystem.writeFileSync(filePath, fileContents) | ||
|
||
generateEvents.emit('file-written', filePath) | ||
}) | ||
} | ||
|
||
const formatResults = ( | ||
transformerResult: TransformerOutput<{ outputFileNameFormat: string }>, | ||
transformer: Transformer, | ||
config: Exclude<KazamConfig, unknown[]>, | ||
): Parameters<typeof writeResults>[0] => { | ||
const formattedTransformerResult = new Map<string, string>() | ||
|
||
const transformerDirectory = [ | ||
config.output, | ||
kebabCase(transformer.name.replace(/^Transformer/, '')), | ||
].join('/') | ||
|
||
transformerResult.forEach(({ filePath, content }, sourceFilePath) => { | ||
const sourceExtension = `.${sourceFilePath.split('.').slice(-1)[0]}` ?? '' | ||
const transformedExtension = `.${filePath.split('.').slice(-1)[0]}` ?? '' | ||
|
||
const outputFilePath = [ | ||
transformerDirectory, | ||
// The following line replaces `.kaz.tsx` with `.tsx` (for example) | ||
filePath.replace( | ||
new RegExp(`${sourceExtension.replace('.', '\\.')}${transformedExtension.replace('.', '\\.')}$`), | ||
transformedExtension, | ||
), | ||
].join('/') | ||
|
||
formattedTransformerResult.set(outputFilePath, content) | ||
}) | ||
|
||
return formattedTransformerResult | ||
} | ||
|
||
const generateForConfig = async ( | ||
config: Exclude<KazamConfig, unknown[]>, | ||
configPath: string, | ||
fileSystem?: typeof fs | undefined, | ||
) => { | ||
return Promise.all( | ||
config.parsers.map(async (ParserClass) => { | ||
const parser = new ParserClass() | ||
|
||
const transformerInput = await parser.loadAndParse({ | ||
...config, | ||
configPath, | ||
}) | ||
|
||
return config.transformers.map((TransformerClass) => { | ||
const transformer = new TransformerClass(transformerInput, {}) | ||
|
||
const transformerResult = transformer.transform() | ||
|
||
if (fileSystem) { | ||
writeResults( | ||
formatResults(transformerResult, TransformerClass, config), | ||
fileSystem, | ||
) | ||
} | ||
|
||
return transformerResult | ||
}) | ||
}), | ||
) | ||
.then(results => results.flat()) | ||
} | ||
|
||
export const generate = async ( | ||
config: KazamConfig, | ||
configPath: string, | ||
fileSystem?: typeof fs | undefined, | ||
) => { | ||
if (!Array.isArray(config)) | ||
config = [config] | ||
|
||
const results = await Promise.all( | ||
config.map(config => generateForConfig(config, configPath, fileSystem)), | ||
) | ||
.then(results => results.flat()) | ||
|
||
return results | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,3 @@ | ||
#!/usr/bin/env node | ||
import { runCli } from './adapters/cli/cli' | ||
|
||
import process from 'node:process' | ||
|
||
import { program } from 'commander' | ||
|
||
import { generateCommand } from './commands/generate' | ||
|
||
program | ||
.addCommand(generateCommand) | ||
|
||
program.parse(process.argv) | ||
runCli() |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { registerEventEmitter } from '../lib/typed-event-emitter' | ||
|
||
const generateEvents = registerEventEmitter<{ | ||
'file-written': string | ||
}>() | ||
|
||
export { generateEvents } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { EventEmitter } from 'node:events' | ||
|
||
class ReturnedEmitter<Events extends Record<string, unknown>> { | ||
#eventEmitter = new EventEmitter() | ||
|
||
emit<EventName extends keyof Events>(eventName: EventName extends string ? EventName : never, data: Events[EventName]) { | ||
return this.#eventEmitter.emit(eventName, data) | ||
} | ||
|
||
on<EventName extends keyof Events>(eventName: EventName extends string ? EventName : never, listener: (data: Events[EventName]) => void): this { | ||
this.#eventEmitter.on(eventName, listener) | ||
return this | ||
} | ||
|
||
once<EventName extends keyof Events>(eventName: EventName extends string ? EventName : never, listener: (data: Events[EventName]) => void): this { | ||
this.#eventEmitter.once(eventName, listener) | ||
return this | ||
} | ||
} | ||
|
||
export const registerEventEmitter = <Events extends Record<string, unknown>>() => { | ||
return new ReturnedEmitter<Events>() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ParserBase } from '@whitebird/kazam-parser-base' | ||
import { TransformerBase } from '@whitebird/kazam-transformer-base' | ||
import { z } from 'zod' | ||
|
||
import type { Parser } from '../../types/parser' | ||
import type { Transformer } from '../../types/transformer' | ||
|
||
const transformerSchema = z.custom<Transformer>( | ||
v => typeof v === 'function' && v.prototype instanceof TransformerBase, | ||
{ message: 'Must be an instance of TransformerBase' }, | ||
) | ||
|
||
const parserSchema = z.custom<Parser>( | ||
v => typeof v === 'function' && v.prototype instanceof ParserBase, | ||
{ message: 'Must be an instance of ParserBase' }, | ||
) | ||
|
||
const singleKazamConfigSchema = z.object({ | ||
input: z.string().array(), | ||
output: z.string(), | ||
transformers: z.array(transformerSchema), | ||
parsers: z.array(parserSchema), | ||
}) | ||
|
||
export const kazamConfigSchema = z.union([ | ||
singleKazamConfigSchema, | ||
z.array(singleKazamConfigSchema), | ||
]) |
Oops, something went wrong.