Skip to content

Commit

Permalink
Feature/implement magidoc.js for config and magidoc.config.js for sta…
Browse files Browse the repository at this point in the history
…rters (#11)

* bump deps

* bump deps

* fix typing

* delete logo

* fix header

* fix graphql being external

* patch

* use magidoc.js instead of yaml

* add magidoc config to starter and read from magidoc config to get allowed values

* fix order of tasks

* bump versions
  • Loading branch information
pelletier197 authored Apr 21, 2022
1 parent bb89bee commit 9620aac
Show file tree
Hide file tree
Showing 26 changed files with 236 additions and 125 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "magidoc",
"version": "0.6.2",
"version": "0.7.0",
"description": "MagiDoc is a documentation website generator for GraphQL based on Svelte and that allows for infinite customization through plugins.",
"repository": "git@github.com:pelletier197/magidocql.git",
"author": "Sunny Pelletier",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
docs/
magidoc*.yml
/magidoc*.js
8 changes: 3 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@magidoc/cli",
"description": "Magidoc CLI application responsible for generating GraphQL documentation websites.",
"private": false,
"version": "0.6.2",
"version": "0.7.0",
"type": "module",
"license": "MIT",
"main": "./build/index.js",
Expand All @@ -24,21 +24,19 @@
"release": "pnpm publish --no-git-checks --access public"
},
"dependencies": {
"@magidoc/plugin-starter-variables": "workspace:^0.6.2",
"@magidoc/rollup-plugin-fetch-gql-schema": "workspace:^0.6.2",
"@magidoc/plugin-starter-variables": "workspace:^0.7.0",
"@magidoc/rollup-plugin-fetch-gql-schema": "workspace:^0.7.0",
"axios": "^0.26.1",
"commander": "^9.2.0",
"extract-zip": "^2.0.1",
"fs-extra": "^10.1.0",
"js-yaml": "^4.1.0",
"listr2": "^4.0.5",
"zod": "^3.14.4"
},
"devDependencies": {
"@rollup/plugin-typescript": "^8.3.2",
"@types/fs-extra": "^9.0.13",
"@types/jest": "^27.4.1",
"@types/js-yaml": "^4.0.5",
"jest": "^27.5.1",
"jest-extended": "^2.0.0",
"rollup": "^2.70.2",
Expand Down
24 changes: 10 additions & 14 deletions packages/cli/src/commands/generate/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export default function buildGenerateCommand(program: Command) {
)
.addOption(
new Option(
'-f|--file <magidoc.yml>',
'The magidoc.yml configuration file location',
).default('./magidoc.yml'),
'-f|--file <magidoc.js>',
'The magidoc.js configuration file location',
).default('./magidoc.js'),
)
.addOption(
new Option(
Expand All @@ -35,7 +35,7 @@ export default function buildGenerateCommand(program: Command) {
).default(false),
)
.action(async ({ file, stacktrace, clean }: GenerateCommandOptions) => {
const fileConfiguration = loadFileConfiguration(file)
const fileConfiguration = await loadFileConfiguration(file)
if (!fileConfiguration) {
process.exitCode = 1
return
Expand All @@ -47,16 +47,10 @@ export default function buildGenerateCommand(program: Command) {
templateVersion: fileConfiguration.website.templateVersion,
output: path.resolve(fileConfiguration.website.output),
clean,
options: fileConfiguration.website.options,
fetchConfig: {
url: fileConfiguration.introspection.url,
headers: Object.keys(
fileConfiguration.introspection.headers || {},
).map((header) => {
return {
name: header,
value: (fileConfiguration.introspection.headers || {})[header],
}
}),
headers: fileConfiguration.introspection.headers || {},
method: fileConfiguration.introspection.method,
},
})
Expand All @@ -75,9 +69,11 @@ export default function buildGenerateCommand(program: Command) {
})
}

function loadFileConfiguration(configPath: string): FileConfiguration | null {
async function loadFileConfiguration(
configPath: string,
): Promise<FileConfiguration | null> {
try {
return readConfiguration(configPath)
return await readConfiguration(path.resolve(configPath))
} catch (error) {
if (error instanceof Error) {
console.log(error.message)
Expand Down
24 changes: 22 additions & 2 deletions packages/cli/src/commands/generate/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Method } from '@magidoc/rollup-plugin-fetch-gql-schema'
import type { Template } from '../../template'
import type { FetchConfig } from './schema/fetch'

export type GenerationConfig = {
/**
Expand All @@ -15,7 +15,7 @@ export type GenerationConfig = {
/**
* The configuration used for fetching the GraphQL Schema from the remote server
*/
fetchConfig?: FetchConfig
fetchConfig: FetchConfig

/**
* The output target directory
Expand All @@ -26,4 +26,24 @@ export type GenerationConfig = {
* Wether to clean the existing cache
*/
clean: boolean

/**
* Common and template-specific options
*/
options: Record<string, string | boolean | number>
}

export type FetchConfig = {
/**
* The URL of the target server
*/
url: string
/**
* The HTTP method to to fetch the schema
*/
method: Method
/**
* The headers to pass to the query
*/
headers: Record<string, string>
}
38 changes: 21 additions & 17 deletions packages/cli/src/commands/generate/config/parser.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import yaml from 'js-yaml'
import { FileConfiguration, ZFileConfiguration } from './types'

export function parseConfiguration(content: string): FileConfiguration {
const response = yaml.load(content)
const result = ZFileConfiguration.safeParse(response)

export function parseConfiguration(content: unknown): FileConfiguration {
const result = ZFileConfiguration.safeParse(content)
if (result.success) {
return result.data
}

const issues = result.error.issues
const formattedIssues = issues.map(
(issue) =>
` - ${issue.message} at path ${issue.path.reduce(
(previous: string, current: string | number) => {
if (typeof current === 'number') return previous + `[${current}]`
return previous + String(current)
},
'',
)}`,
)
const formattedIssues = issues.map((issue) => {
const path = formatErrorPath(issue.path)
switch (issue.code) {
case 'invalid_type':
return ` - ${issue.message} '${issue.expected}' but received '${issue.received}' at path ${path}`
default:
return ` - ${issue.message} at path ${path}`
}
})

throw new Error(
`${
issues.length
} issues were found with the YAML configuration provided:\n${formattedIssues.join(
} issues were found with the Magidoc configuration provided:\n${formattedIssues.join(
'\n',
)}}`,
)}`,
)
}

function formatErrorPath(path: (string | number)[]): string {
return path.reduce((previous: string, current: string | number) => {
if (typeof current === 'number') return previous + `[${current}]`
if (previous === '') return String(current)
return `${previous}.${String(current)}`
}, '')
}
19 changes: 13 additions & 6 deletions packages/cli/src/commands/generate/config/read.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { existsSync, readFileSync } from 'fs'
import { existsSync } from 'fs'
import type { FileConfiguration } from './types'
import path from 'path'
import { parseConfiguration } from './parser'

const allowedExtensions = ['.yaml', '.yml']
const allowedExtensions = ['.js', '.cjs']

export function readConfiguration(configPath: string): FileConfiguration {
export async function readConfiguration(
configPath: string,
): Promise<FileConfiguration> {
const extension = path.extname(configPath)
if (!isValidYamlExtension(extension)) {
if (!isValidJsExtension(extension)) {
throw new Error(
`Unrecognized Magidoc configuration file extension: ${extension}... Supported values are ${allowedExtensions.toString()}`,
)
Expand All @@ -19,10 +21,15 @@ export function readConfiguration(configPath: string): FileConfiguration {
)
}

return parseConfiguration(readFileSync(configPath).toString())
const config = ((await import(configPath)) as { default?: unknown }).default
if (!config) {
throw new Error(`File ${configPath} has no default export`)
}

return parseConfiguration(config)
}

export function isValidYamlExtension(extension: string): boolean {
export function isValidJsExtension(extension: string): boolean {
const lower = extension.toLocaleLowerCase()
return allowedExtensions.includes(lower)
}
8 changes: 7 additions & 1 deletion packages/cli/src/commands/generate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ import { buildTemplateTask } from './tasks/buildTemplate'
import { moveOutputTask } from './tasks/moveOutput'
import type { TaskContext } from './task'
import type { GenerationConfig } from './config'
import { warnVersion } from './tasks/warnVersion'
import { loadTemplateConfiguration } from './tasks/loadTemplateConfig'
import { loadGraphQLSchema } from './tasks/loadGraphqlSchema'

export default async function generate(config: GenerationConfig) {
const listr = new Listr<TaskContext>(
[
warnVersion(config),
determineTmpDirectoryTask(config),
cleanTask(config),
selectNpmRunnerTask(),
fetchTemplateTask(config),
unzipTemplateTask(),
loadTemplateConfiguration(),
loadGraphQLSchema(config),
installDependenciesTask(),
buildTemplateTask(),
buildTemplateTask(config),
moveOutputTask(config),
],
{
Expand Down
36 changes: 0 additions & 36 deletions packages/cli/src/commands/generate/schema/fetch.ts

This file was deleted.

7 changes: 7 additions & 0 deletions packages/cli/src/commands/generate/task.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Variable } from '@magidoc/plugin-starter-variables/build/variable'
import type {
ListrContext,
ListrDefaultRenderer,
Expand All @@ -11,6 +12,12 @@ export type TaskContext = {
tmpArchive: TmpLocation
tmpDirectory: TmpLocation
npmRunner: NpmRunner
templateConfiguration: TemplateConfiguration
}

export type TemplateConfiguration = {
supportedOptions: Variable<unknown>[]
schemaTargetLocation: string
}

export function newTask({
Expand Down
40 changes: 37 additions & 3 deletions packages/cli/src/commands/generate/tasks/buildTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import { newTask, Task } from '../task'
import type { GenerationConfig } from '../config'
import { newTask, Task, TaskContext } from '../task'

export function buildTemplateTask(): Task {
export function buildTemplateTask(config: GenerationConfig): Task {
return newTask({
title: `Build template`,
executor: async (ctx) => {
await ctx.npmRunner.buildProject({
cwd: ctx.tmpDirectory.path,
env: {}, // TODO
env: buildEnv(ctx, config),
})
},
})
}

function buildEnv(
ctx: TaskContext,
config: GenerationConfig,
): Record<string, string> {
const newRecord: Record<string, string> = {}
const nonExistingOptions: string[] = []
Object.keys(config.options).forEach((key) => {
const variable = ctx.templateConfiguration.supportedOptions.find((option) =>
option.names.includes(key),
)
if (!variable) {
nonExistingOptions.push(key)
return
}

newRecord[variable.vite.key] = String(config.options[key])
})

if (nonExistingOptions.length > 0) {
throw new Error(
`Options ${nonExistingOptions.toString()} are not supported by template ${
config.template
}... Supported option names are ${
(ctx.templateConfiguration.supportedOptions.flatMap(
(value) => value.names,
),
toString())
}`,
)
}
return newRecord
}
17 changes: 17 additions & 0 deletions packages/cli/src/commands/generate/tasks/loadGraphqlSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { GenerationConfig } from '../config'
import { fetchSchema } from '@magidoc/rollup-plugin-fetch-gql-schema'
import { newTask, Task } from '../task'

export function loadGraphQLSchema(config: GenerationConfig): Task {
return newTask({
title: `Load GraphQL Schema`,
executor: async (ctx) => {
await fetchSchema({
url: config.fetchConfig.url,
method: config.fetchConfig.method,
headers: config.fetchConfig.headers,
target: ctx.templateConfiguration.schemaTargetLocation,
})
},
})
}
Loading

0 comments on commit 9620aac

Please sign in to comment.