2022-10-03 16:11:01 +00:00

286 lines
10 KiB
TypeScript

import { XCircleIcon } from '@heroicons/react/24/solid';
import { getSchemaFromReference } from '@nrwl/nx-dev/data-access-packages';
import { JsonSchema1, NxSchema } from '@nrwl/nx-dev/models-package';
import { renderMarkdown } from '@nrwl/nx-dev/ui-markdoc';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { ReactNode, useState } from 'react';
import { generateJsonExampleFor, isErrors } from './examples';
import { SchemaViewModel } from './get-schema-view-model';
import { SchemaEditor } from './schema-editor';
import { SchemaViewer } from './schema-viewer';
import { Heading2, Heading3 } from './ui/headings';
function pathCleaner(path: string): string {
return path.split('?')[0];
}
export function Content({
schemaViewModel,
}: {
schemaViewModel: SchemaViewModel;
}) {
if (!schemaViewModel.currentSchema)
throw new Error(
'A valid schema has to be defined for the "currentSchema" property'
);
const router = useRouter();
const [presets, setPresets] = useState<string[]>([]);
const filterWithPresets = (
data: Record<string, any>,
wantedProperties: string[]
): Record<string, any> => {
const result: Record<string, any> = {};
if (!wantedProperties.length) return data;
for (const p in data) {
if (wantedProperties.includes(p)) {
result[p] = data[p];
}
}
return result;
};
const vm = {
get fullExample(): Record<string, any> {
const examples = generateJsonExampleFor(
schemaViewModel.currentSchema as NxSchema,
schemaViewModel.lookup,
'both'
);
return isErrors(examples) ? {} : examples.value;
},
get pages(): { name: string; href: string; current: boolean }[] {
return [
{
name: schemaViewModel.packageName,
href: schemaViewModel.packageUrl,
current: false,
},
{
name: schemaViewModel.schemaMetadata.name,
href: pathCleaner(router.asPath),
current: !schemaViewModel.subReference,
},
!!schemaViewModel.subReference
? {
name: schemaViewModel.subReference.split('/')[2],
href: pathCleaner(router.asPath),
current: true,
}
: void 0,
].filter(
(x): x is { name: string; href: string; current: boolean } => !!x
);
},
get markdown(): ReactNode {
return renderMarkdown({
content: getMarkdown({
type: schemaViewModel.type,
packageName: schemaViewModel.packageName,
schemaName: schemaViewModel.schemaMetadata.name,
schemaAlias: schemaViewModel.schemaMetadata.aliases[0] ?? '',
schema: schemaViewModel.currentSchema as NxSchema,
}),
filePath: '',
data: {},
});
},
};
return (
<div className="min-w-0 flex-auto pt-8 pb-24 lg:pb-16">
<div className="mb-8 flex w-full items-center space-x-2">
<div className="w-full flex-grow">
<div
aria-hidden="true"
data-tooltip="Schema type"
className="relative inline-flex rounded-md border border-slate-200 bg-slate-50 px-4 py-2 text-xs font-medium uppercase dark:border-slate-700 dark:bg-slate-800/60"
>
{schemaViewModel.type}
</div>
</div>
<div className="relative z-0 inline-flex flex-shrink-0 rounded-md shadow-sm">
<Link href={schemaViewModel.packageUrl}>
<a
title="See package information"
className="focus:ring-blue-nx-base focus:border-blue-nx-base relative inline-flex items-center rounded-l-md border border-slate-200 bg-white px-4 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50 focus:z-10 focus:outline-none focus:ring-1 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-400 dark:hover:bg-slate-800"
>
{schemaViewModel.packageName}
</a>
</Link>
<a
href={schemaViewModel.schemaGithubUrl}
target="_blank"
rel="noreferrer"
title="See this schema on Github"
className="focus:ring-blue-nx-base focus:border-blue-nx-base relative -ml-px inline-flex items-center rounded-r-md border border-slate-200 bg-white px-4 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50 focus:z-10 focus:outline-none focus:ring-1 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-400 dark:hover:bg-slate-800"
>
<svg
className="mr-2 h-4 w-4"
viewBox="0 0 16 16"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
></path>
</svg>
See schema
</a>
</div>
</div>
{/* We remove the top description on sub property lookup */}
{!schemaViewModel.subReference && (
<>
<div className="prose prose-slate dark:prose-invert max-w-none">
{vm.markdown}
</div>
<div className="h-12">{/* SPACER */}</div>
</>
)}
{/*TODO@ben: create new component*/}
{schemaViewModel.type === 'executors' && !schemaViewModel.subReference && (
<div className="mt-8 hidden md:block">
<Heading2 title="Options playground" />
<p className="my-6">
Try out this interactive editor of the configuration object. Values
are validated as you type and hovering over labels will give you
more information.
</p>
{!!schemaViewModel.currentSchema.presets.length && (
<>
<Heading3 title="Examples" />
<p className="my-4">
These buttons show the config object for specific common tasks.
</p>
<div className="mb-4 flex flex-wrap gap-4">
{schemaViewModel.currentSchema.presets.map((p) => (
<button
key={'preset-' + p.name.toLowerCase()}
onClick={() => {
setPresets(p.keys);
}}
type="button"
className="relative inline-flex items-center rounded-md border border-slate-200 bg-white px-4 py-2 px-4 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-400 dark:hover:bg-slate-800"
>
{p.name}
</button>
))}
{!!presets.length && (
<button
onClick={() => setPresets([])}
type="button"
className="relative inline-flex items-center rounded-md border border-slate-200 bg-white px-4 py-2 px-4 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-400 dark:hover:bg-slate-800"
>
Reset <XCircleIcon className="ml-1.5 h-4 w-4" />
</button>
)}
</div>
</>
)}
<div className="rounded-md border border-slate-200 p-0.5 dark:border-slate-700">
<SchemaEditor
packageName={schemaViewModel.packageName}
schemaName={schemaViewModel.schemaMetadata.name}
type={schemaViewModel.type.replace('s', '') as any}
content={filterWithPresets(vm.fullExample, presets)}
schema={schemaViewModel.currentSchema}
/>
</div>
</div>
)}
<div className="mt-8">
<Heading2 title="Options" />
{!schemaViewModel.subReference && (
<SchemaViewer
schema={schemaViewModel.currentSchema}
lookup={schemaViewModel.lookup}
reference="#"
stage={'both'}
/>
)}
{schemaViewModel.subReference && (
<SchemaViewer
schema={
getSchemaFromReference(
schemaViewModel.subReference,
schemaViewModel.lookup
) as JsonSchema1
}
lookup={schemaViewModel.lookup}
reference={schemaViewModel.subReference}
stage={'both'}
/>
)}
</div>
</div>
);
}
const getMarkdown = (data: {
packageName: string;
schemaAlias: string;
schemaName: string;
schema: NxSchema;
type: 'executors' | 'generators';
}): string => {
const hasExamplesFile = !!data.schema['examplesFile'];
const executorNotice: string = `Options can be configured in \`project.json\` when defining the executor, or when invoking it. Read more about how to configure targets and executors here: [https://nx.dev/reference/project-configuration#targets](https://nx.dev/reference/project-configuration#targets).`;
return [
`# ${data.packageName}:${data.schemaName}`,
`\n\n`,
data.schema.description,
'\n\n',
data.type === 'executors' ? executorNotice : '',
`\n\n`,
hasExamplesFile ? data.schema['examplesFile'] : '',
data.type === 'generators'
? getUsage(data.packageName, data.schemaName, data.schemaAlias)
: '',
!!data.schema['examples']
? `### Examples \n ${data.schema['examples']
.map(
(e: any) => `${e.description}: \n \`\`\`bash\n${e.command}\n\`\`\``
)
.join('\n')}`
: '',
`\n\n`,
].join('');
};
const getUsage = (
packageName: string,
schemaName: string,
schemaAlias: string
): string => `
## Usage
\`\`\`bash
nx generate ${schemaName} ...
\`\`\`
${!!schemaAlias ? `\`\`\`bash\nnx g ${schemaAlias} ... #same\n\`\`\`\n` : ''}
By default, Nx will search for \`${schemaName}\` in the default collection provisioned in workspace.json.
You can specify the collection explicitly as follows:
\`\`\`bash
nx g ${packageName}:${schemaName} ...
\`\`\`
Show what will be generated without writing to disk:
\`\`\`bash
nx g ${schemaName} ... --dry-run
\`\`\`
`;
export default Content;