refactor: dox
This commit is contained in:
parent
7cc206ae6d
commit
4b2f68f048
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -122,5 +122,6 @@ dist
|
||||||
# package-locals
|
# package-locals
|
||||||
/packages/*/yarn.lock
|
/packages/*/yarn.lock
|
||||||
/packages/*/bun.lockb
|
/packages/*/bun.lockb
|
||||||
|
/website/yarn.lock
|
||||||
|
|
||||||
/.nx/cache
|
/.nx/cache
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "lerna run build",
|
"build": "lerna run build",
|
||||||
|
"dox": "flecks dox docusaurus && cd website && DOCUSAURUS_GENERATED_FILES_DIR_NAME=node_modules/.cache/docusaurus node_modules/.bin/docusaurus",
|
||||||
"lint": "lerna run lint",
|
"lint": "lerna run lint",
|
||||||
"publish": "lerna publish --conventional-commits --contents=dist --registry https://registry.npmjs.org"
|
"publish": "lerna publish --conventional-commits --contents=dist --registry https://registry.npmjs.org"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
const {
|
const {mkdir, writeFile} = require('fs/promises');
|
||||||
access,
|
const {join, relative, resolve} = require('path');
|
||||||
cp,
|
|
||||||
mkdir,
|
|
||||||
rename,
|
|
||||||
rmdir,
|
|
||||||
} = require('fs/promises');
|
|
||||||
const {dirname, join} = require('path');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
generate,
|
generateDocusaurus,
|
||||||
resolveSiteDir,
|
generateJson,
|
||||||
spawn,
|
} = require('./generate');
|
||||||
} = require('./docusaurus');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
@ -19,73 +12,55 @@ const {
|
||||||
|
|
||||||
module.exports = (program, flecks) => {
|
module.exports = (program, flecks) => {
|
||||||
const commands = {};
|
const commands = {};
|
||||||
const siteDirArgument = program.createArgument('[siteDir]', 'Docusaurus directory');
|
commands.dox = {
|
||||||
siteDirArgument.defaultValue = 'website';
|
description: 'Generate documentation',
|
||||||
commands.docusaurus = {
|
action: async (subcommand, outputPath) => {
|
||||||
description: [
|
let actualOutputPath = outputPath;
|
||||||
'Create a documentation website for this project.',
|
if (!actualOutputPath) {
|
||||||
'',
|
switch (subcommand) {
|
||||||
'The `build` and `start` subcommands are sugar on top of the corresponding Docusaurus commands.',
|
case 'docusaurus':
|
||||||
'',
|
actualOutputPath = 'website/docs/flecks';
|
||||||
'The `create` subcommand will create a documentation website starter template for you at `siteDir`',
|
break;
|
||||||
"if `siteDir` doesn't already exist (defaults to `website`). A `docusaurus.config.js`",
|
case 'json':
|
||||||
"starter configuration will also be copied to your `build` directory if it doesn't already exist.",
|
actualOutputPath = 'dist/dox';
|
||||||
].join('\n'),
|
break;
|
||||||
action: async (subcommand, siteDir) => {
|
default:
|
||||||
const resolvedSiteDir = resolveSiteDir(siteDir);
|
break;
|
||||||
let siteDirExisted = false;
|
|
||||||
try {
|
|
||||||
const result = await mkdir(resolvedSiteDir);
|
|
||||||
if (undefined === result) {
|
|
||||||
await rmdir(resolvedSiteDir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
actualOutputPath = resolve(FLECKS_CORE_ROOT, actualOutputPath);
|
||||||
siteDirExisted = true;
|
await mkdir(actualOutputPath, {recursive: true});
|
||||||
}
|
let output;
|
||||||
|
const json = await generateJson(flecks);
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
case 'build':
|
case 'docusaurus':
|
||||||
if (!siteDirExisted) {
|
output = Object.fromEntries(
|
||||||
throw new Error(`There's no website directory at ${resolvedSiteDir} to build!`);
|
Object.entries(generateDocusaurus(json))
|
||||||
}
|
.map(([type, page]) => [`${type}.mdx`, page]),
|
||||||
await generate(flecks, resolvedSiteDir);
|
);
|
||||||
spawn('build', resolvedSiteDir);
|
|
||||||
break;
|
break;
|
||||||
case 'create': {
|
case 'json':
|
||||||
if (siteDirExisted) {
|
output = Object.fromEntries(
|
||||||
throw new Error(`A website directory at ${resolvedSiteDir} already exists!`);
|
Object.entries(json)
|
||||||
}
|
.map(([type, value]) => [`${type}.json`, JSON.stringify(value, null, 2)]),
|
||||||
const templateDirectory = dirname(require.resolve('@flecks/dox/website/sidebars.js'));
|
);
|
||||||
await cp(templateDirectory, resolvedSiteDir, {recursive: true});
|
|
||||||
// Copy the docusaurus config if it doesn't already exist.
|
|
||||||
try {
|
|
||||||
await access(join(FLECKS_CORE_ROOT, 'build', 'docusaurus.config.js'));
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
await rename(
|
|
||||||
join(resolvedSiteDir, 'docusaurus.config.js'),
|
|
||||||
join(FLECKS_CORE_ROOT, 'build', 'docusaurus.config.js'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`website directory created at ${resolvedSiteDir}!`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'start':
|
|
||||||
if (!siteDirExisted) {
|
|
||||||
throw new Error(`There's no website directory at ${resolvedSiteDir} to start!`);
|
|
||||||
}
|
|
||||||
await generate(flecks, resolvedSiteDir);
|
|
||||||
spawn('start', resolvedSiteDir);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(output)
|
||||||
|
.map(([filename, output]) => (
|
||||||
|
writeFile(join(actualOutputPath, filename), output)
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log("Output documentation to '%s'", relative(FLECKS_CORE_ROOT, actualOutputPath));
|
||||||
},
|
},
|
||||||
args: [
|
args: [
|
||||||
program.createArgument('subcommand', 'Docusaurus command to run')
|
program.createArgument('subcommand', 'Generation type')
|
||||||
.choices(['build', 'create', 'start']),
|
.choices(['docusaurus', 'json']),
|
||||||
siteDirArgument,
|
program.createArgument('[output path]', 'Where the files are output'),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
return commands;
|
return commands;
|
||||||
|
|
|
@ -1,242 +0,0 @@
|
||||||
const {mkdir, writeFile} = require('fs/promises');
|
|
||||||
const {
|
|
||||||
basename,
|
|
||||||
dirname,
|
|
||||||
extname,
|
|
||||||
isAbsolute,
|
|
||||||
join,
|
|
||||||
resolve,
|
|
||||||
} = require('path');
|
|
||||||
|
|
||||||
const {spawnWith} = require('@flecks/core/server');
|
|
||||||
const {themes: prismThemes} = require('prism-react-renderer');
|
|
||||||
const {rimraf} = require('rimraf');
|
|
||||||
|
|
||||||
const {
|
|
||||||
generateBuildFilesPage,
|
|
||||||
generateConfigPage,
|
|
||||||
generateHookPage,
|
|
||||||
generateTodoPage,
|
|
||||||
} = require('./generate');
|
|
||||||
const {parseFlecks} = require('./parser');
|
|
||||||
|
|
||||||
const {
|
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
exports.configDefaults = function configDefaults() {
|
|
||||||
/** @type {import('@docusaurus/types').Config} */
|
|
||||||
const config = {
|
|
||||||
tagline: 'built with flecks',
|
|
||||||
onBrokenLinks: 'throw',
|
|
||||||
onBrokenMarkdownLinks: 'warn',
|
|
||||||
i18n: {
|
|
||||||
defaultLocale: 'en',
|
|
||||||
locales: ['en'],
|
|
||||||
},
|
|
||||||
presets: [
|
|
||||||
['classic', {
|
|
||||||
docs: {
|
|
||||||
sidebarPath: './sidebars.js',
|
|
||||||
},
|
|
||||||
pages: {
|
|
||||||
path: 'pages',
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
themeConfig: {
|
|
||||||
footer: {
|
|
||||||
style: 'dark',
|
|
||||||
copyright: 'Built with flecks and Docusaurus.',
|
|
||||||
},
|
|
||||||
prism: {
|
|
||||||
theme: prismThemes.github,
|
|
||||||
darkTheme: prismThemes.dracula,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.resolveSiteDir = function resolveSiteDir(siteDir) {
|
|
||||||
return isAbsolute(siteDir)
|
|
||||||
? siteDir
|
|
||||||
: resolve(FLECKS_CORE_ROOT, siteDir);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.generate = async function generate(flecks, siteDir) {
|
|
||||||
// Generate "docs".
|
|
||||||
const docsDirectory = join(siteDir, 'docs', 'flecks');
|
|
||||||
await rimraf(docsDirectory);
|
|
||||||
const generatedDirectory = join(docsDirectory, '@flecks', 'dox');
|
|
||||||
await mkdir(generatedDirectory, {recursive: true});
|
|
||||||
const parsed = await parseFlecks(flecks);
|
|
||||||
const {
|
|
||||||
buildFiles,
|
|
||||||
config,
|
|
||||||
hooks,
|
|
||||||
todos,
|
|
||||||
} = parsed
|
|
||||||
.reduce(
|
|
||||||
(
|
|
||||||
r,
|
|
||||||
[
|
|
||||||
root,
|
|
||||||
sources,
|
|
||||||
],
|
|
||||||
) => {
|
|
||||||
const ensureHook = (hook) => {
|
|
||||||
if (!r.hooks[hook]) {
|
|
||||||
r.hooks[hook] = {
|
|
||||||
implementations: [],
|
|
||||||
invocations: [],
|
|
||||||
specification: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sources.forEach(
|
|
||||||
(
|
|
||||||
[
|
|
||||||
path,
|
|
||||||
{
|
|
||||||
buildFiles = [],
|
|
||||||
config = [],
|
|
||||||
hookImplementations = [],
|
|
||||||
hookInvocations = [],
|
|
||||||
hookSpecifications = [],
|
|
||||||
todos = [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
) => {
|
|
||||||
r.buildFiles.push(...buildFiles);
|
|
||||||
r.todos.push(...todos.map((todo) => ({
|
|
||||||
...todo,
|
|
||||||
filename: join(`**${root}**`, path),
|
|
||||||
})));
|
|
||||||
if (config.length > 0) {
|
|
||||||
let fleck = root;
|
|
||||||
if ('build/flecks.bootstrap.js' !== path) {
|
|
||||||
fleck = join(fleck, path.startsWith('src') ? path.slice(4) : path);
|
|
||||||
fleck = join(dirname(fleck), basename(fleck, extname(fleck)));
|
|
||||||
fleck = fleck.endsWith('/index') ? fleck.slice(0, -6) : fleck;
|
|
||||||
}
|
|
||||||
r.config[fleck] = config;
|
|
||||||
}
|
|
||||||
hookImplementations.forEach(({column, hook, line}) => {
|
|
||||||
ensureHook(hook);
|
|
||||||
r.hooks[hook].implementations.push({
|
|
||||||
column,
|
|
||||||
filename: join(`**${root}**`, path),
|
|
||||||
line,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
hookInvocations.forEach(({
|
|
||||||
column,
|
|
||||||
hook,
|
|
||||||
line,
|
|
||||||
type,
|
|
||||||
}) => {
|
|
||||||
ensureHook(hook);
|
|
||||||
r.hooks[hook].invocations.push({
|
|
||||||
column,
|
|
||||||
filename: join(`**${root}**`, path),
|
|
||||||
line,
|
|
||||||
type,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
hookSpecifications.forEach(({
|
|
||||||
hook,
|
|
||||||
description,
|
|
||||||
example,
|
|
||||||
params,
|
|
||||||
}) => {
|
|
||||||
ensureHook(hook);
|
|
||||||
r.hooks[hook].specification = {
|
|
||||||
description,
|
|
||||||
example,
|
|
||||||
params,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return r;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buildFiles: [],
|
|
||||||
config: {},
|
|
||||||
hooks: {},
|
|
||||||
todos: [],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const sortedHooks = Object.fromEntries(
|
|
||||||
Object.entries(hooks)
|
|
||||||
.map(([hook, {implementations, invocations, specification}]) => (
|
|
||||||
[
|
|
||||||
hook,
|
|
||||||
{
|
|
||||||
implementations: implementations
|
|
||||||
.sort(({filename: l}, {filename: r}) => (l < r ? -1 : 1)),
|
|
||||||
invocations: invocations
|
|
||||||
.sort(({filename: l}, {filename: r}) => (l < r ? -1 : 1)),
|
|
||||||
specification,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
))
|
|
||||||
.sort(([l], [r]) => (l < r ? -1 : 1)),
|
|
||||||
);
|
|
||||||
Object.entries(sortedHooks)
|
|
||||||
.forEach(([hook, {specification}]) => {
|
|
||||||
if (!specification) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn(`Warning: no specification for hook: '${hook}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const hookPage = generateHookPage(sortedHooks, flecks);
|
|
||||||
const todoPage = generateTodoPage(todos, flecks);
|
|
||||||
const buildFilesPage = generateBuildFilesPage(buildFiles);
|
|
||||||
const configPage = generateConfigPage(config);
|
|
||||||
await writeFile(join(generatedDirectory, 'hooks.mdx'), hookPage);
|
|
||||||
await writeFile(join(generatedDirectory, 'TODO.mdx'), todoPage);
|
|
||||||
await writeFile(join(generatedDirectory, 'build-configs.mdx'), buildFilesPage);
|
|
||||||
await writeFile(join(generatedDirectory, 'config.mdx'), configPage);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.spawn = function spawn(subcommand, siteDir) {
|
|
||||||
const args = [];
|
|
||||||
switch (subcommand) {
|
|
||||||
case 'start':
|
|
||||||
args.push('start', '--no-open');
|
|
||||||
break;
|
|
||||||
case 'build':
|
|
||||||
args.push('build', '--out-dir', join(FLECKS_CORE_ROOT, 'dist', 'dox'));
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
const docusaurusCall = `npx docusaurus <subcommand> --config ${join(FLECKS_CORE_ROOT, 'build', 'docusaurus.config.js')}`;
|
|
||||||
throw new Error(`@flecks/dox only supports the 'build' and 'start' subcommands. You can run docusaurus yourself with:\n\n${docusaurusCall}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args.push('--config', join(FLECKS_CORE_ROOT, 'build', 'docusaurus.config.js'));
|
|
||||||
const cacheDirectory = join(FLECKS_CORE_ROOT, 'node_modules', '.cache', '@flecks', 'dox');
|
|
||||||
// Spawn `docusaurus`.
|
|
||||||
const cmd = [
|
|
||||||
// `npx` doesn't propagate signals!
|
|
||||||
// 'npx', 'docusaurus',
|
|
||||||
join(FLECKS_CORE_ROOT, 'node_modules', '.bin', 'docusaurus'),
|
|
||||||
...args,
|
|
||||||
siteDir,
|
|
||||||
];
|
|
||||||
const child = spawnWith(
|
|
||||||
cmd,
|
|
||||||
{
|
|
||||||
env: {
|
|
||||||
// Override docusaurus generation directory for cleanliness.
|
|
||||||
DOCUSAURUS_GENERATED_FILES_DIR_NAME: join(cacheDirectory, '.docusaurus'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// Clean up on exit.
|
|
||||||
process.on('exit', () => {
|
|
||||||
child.kill();
|
|
||||||
});
|
|
||||||
return child;
|
|
||||||
};
|
|
|
@ -2,12 +2,4 @@ const commands = require('./commands');
|
||||||
|
|
||||||
exports.hooks = {
|
exports.hooks = {
|
||||||
'@flecks/build.commands': commands,
|
'@flecks/build.commands': commands,
|
||||||
'@flecks/core.config': () => ({
|
|
||||||
/**
|
|
||||||
* Rewrite the output filenames of source files.
|
|
||||||
*
|
|
||||||
* `filename.replace(new RegExp([key]), [value]);`
|
|
||||||
*/
|
|
||||||
filenameRewriters: {},
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
const makeFilenameRewriter = (filenameRewriters) => (filename, line, column) => (
|
const {
|
||||||
Object.entries(filenameRewriters)
|
basename,
|
||||||
.reduce(
|
dirname,
|
||||||
(filename, [from, to]) => filename.replace(new RegExp(from), to),
|
extname,
|
||||||
[filename, ...(line ? [line, column] : [])].join(':'),
|
join,
|
||||||
)
|
} = require('path');
|
||||||
);
|
|
||||||
|
|
||||||
exports.generateBuildFilesPage = (buildFiles) => {
|
const {parseFlecks} = require('./parser');
|
||||||
|
|
||||||
|
exports.generateDocusaurus = function generate({
|
||||||
|
'build-files': buildFiles,
|
||||||
|
config,
|
||||||
|
hooks,
|
||||||
|
todos,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
'build-files': exports.generateDocusaurusBuildFilesPage(buildFiles),
|
||||||
|
config: exports.generateDocusaurusConfigPage(config),
|
||||||
|
hooks: exports.generateDocusaurusHookPage(hooks),
|
||||||
|
todos: exports.generateDocusaurusTodoPage(todos),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.generateDocusaurusBuildFilesPage = (buildFiles) => {
|
||||||
const source = [];
|
const source = [];
|
||||||
source.push('---');
|
source.push('---');
|
||||||
source.push('title: Build files');
|
source.push('title: Build files');
|
||||||
|
@ -28,7 +42,7 @@ exports.generateBuildFilesPage = (buildFiles) => {
|
||||||
return source.join('\n');
|
return source.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateConfigPage = (configs) => {
|
exports.generateDocusaurusConfigPage = (configs) => {
|
||||||
const source = [];
|
const source = [];
|
||||||
source.push('---');
|
source.push('---');
|
||||||
source.push('title: Fleck configuration');
|
source.push('title: Fleck configuration');
|
||||||
|
@ -67,9 +81,7 @@ exports.generateConfigPage = (configs) => {
|
||||||
return source.join('\n');
|
return source.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateHookPage = (hooks, flecks) => {
|
exports.generateDocusaurusHookPage = (hooks) => {
|
||||||
const {filenameRewriters} = flecks.get('@flecks/dox');
|
|
||||||
const rewriteFilename = makeFilenameRewriter(filenameRewriters);
|
|
||||||
const source = [];
|
const source = [];
|
||||||
source.push('---');
|
source.push('---');
|
||||||
source.push('title: Hooks');
|
source.push('title: Hooks');
|
||||||
|
@ -114,7 +126,7 @@ exports.generateHookPage = (hooks, flecks) => {
|
||||||
source.push('<summary>Implementations</summary>');
|
source.push('<summary>Implementations</summary>');
|
||||||
source.push('<ul>');
|
source.push('<ul>');
|
||||||
implementations.forEach(({filename, column, line}) => {
|
implementations.forEach(({filename, column, line}) => {
|
||||||
source.push(`<li>${rewriteFilename(filename, line, column)}</li>`);
|
source.push(`<li>${[filename, line, column].join(':')}</li>`);
|
||||||
});
|
});
|
||||||
source.push('</ul>');
|
source.push('</ul>');
|
||||||
source.push('</details>');
|
source.push('</details>');
|
||||||
|
@ -125,7 +137,7 @@ exports.generateHookPage = (hooks, flecks) => {
|
||||||
source.push('<summary>Invocations</summary>');
|
source.push('<summary>Invocations</summary>');
|
||||||
source.push('<ul>');
|
source.push('<ul>');
|
||||||
invocations.forEach(({filename, column, line}) => {
|
invocations.forEach(({filename, column, line}) => {
|
||||||
source.push(`<li>${rewriteFilename(filename, line, column)}</li>`);
|
source.push(`<li>${[filename, line, column].join(':')}</li>`);
|
||||||
});
|
});
|
||||||
source.push('</ul>');
|
source.push('</ul>');
|
||||||
source.push('</details>');
|
source.push('</details>');
|
||||||
|
@ -135,9 +147,7 @@ exports.generateHookPage = (hooks, flecks) => {
|
||||||
return source.join('\n');
|
return source.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateTodoPage = (todos, flecks) => {
|
exports.generateDocusaurusTodoPage = (todos) => {
|
||||||
const {filenameRewriters} = flecks.get('@flecks/dox');
|
|
||||||
const rewriteFilename = makeFilenameRewriter(filenameRewriters);
|
|
||||||
const source = [];
|
const source = [];
|
||||||
source.push('---');
|
source.push('---');
|
||||||
source.push('title: TODO list');
|
source.push('title: TODO list');
|
||||||
|
@ -154,7 +164,7 @@ exports.generateTodoPage = (todos, flecks) => {
|
||||||
context,
|
context,
|
||||||
description,
|
description,
|
||||||
}) => {
|
}) => {
|
||||||
source.push(rewriteFilename(filename));
|
source.push(filename);
|
||||||
source.push(`> ## ${description}`);
|
source.push(`> ## ${description}`);
|
||||||
source.push(`> <CodeBlock>${context}</CodeBlock>`);
|
source.push(`> <CodeBlock>${context}</CodeBlock>`);
|
||||||
source.push('');
|
source.push('');
|
||||||
|
@ -163,3 +173,133 @@ exports.generateTodoPage = (todos, flecks) => {
|
||||||
}
|
}
|
||||||
return source.join('\n');
|
return source.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.generateJson = async function generate(flecks) {
|
||||||
|
const parsed = await parseFlecks(flecks);
|
||||||
|
const {
|
||||||
|
buildFiles,
|
||||||
|
config,
|
||||||
|
hooks,
|
||||||
|
todos,
|
||||||
|
} = parsed
|
||||||
|
.reduce(
|
||||||
|
(
|
||||||
|
r,
|
||||||
|
[
|
||||||
|
root,
|
||||||
|
sources,
|
||||||
|
],
|
||||||
|
) => {
|
||||||
|
const ensureHook = (hook) => {
|
||||||
|
if (!r.hooks[hook]) {
|
||||||
|
r.hooks[hook] = {
|
||||||
|
implementations: [],
|
||||||
|
invocations: [],
|
||||||
|
specification: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sources.forEach(
|
||||||
|
(
|
||||||
|
[
|
||||||
|
path,
|
||||||
|
{
|
||||||
|
buildFiles = [],
|
||||||
|
config = [],
|
||||||
|
hookImplementations = [],
|
||||||
|
hookInvocations = [],
|
||||||
|
hookSpecifications = [],
|
||||||
|
todos = [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
) => {
|
||||||
|
r.buildFiles.push(...buildFiles);
|
||||||
|
r.todos.push(...todos.map((todo) => ({
|
||||||
|
...todo,
|
||||||
|
filename: join(`**${root}**`, path),
|
||||||
|
})));
|
||||||
|
if (config.length > 0) {
|
||||||
|
let fleck = root;
|
||||||
|
if ('build/flecks.bootstrap.js' !== path) {
|
||||||
|
fleck = join(fleck, path.startsWith('src') ? path.slice(4) : path);
|
||||||
|
fleck = join(dirname(fleck), basename(fleck, extname(fleck)));
|
||||||
|
fleck = fleck.endsWith('/index') ? fleck.slice(0, -6) : fleck;
|
||||||
|
}
|
||||||
|
r.config[fleck] = config;
|
||||||
|
}
|
||||||
|
hookImplementations.forEach(({column, hook, line}) => {
|
||||||
|
ensureHook(hook);
|
||||||
|
r.hooks[hook].implementations.push({
|
||||||
|
column,
|
||||||
|
filename: join(`**${root}**`, path),
|
||||||
|
line,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
hookInvocations.forEach(({
|
||||||
|
column,
|
||||||
|
hook,
|
||||||
|
line,
|
||||||
|
type,
|
||||||
|
}) => {
|
||||||
|
ensureHook(hook);
|
||||||
|
r.hooks[hook].invocations.push({
|
||||||
|
column,
|
||||||
|
filename: join(`**${root}**`, path),
|
||||||
|
line,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
hookSpecifications.forEach(({
|
||||||
|
hook,
|
||||||
|
description,
|
||||||
|
example,
|
||||||
|
params,
|
||||||
|
}) => {
|
||||||
|
ensureHook(hook);
|
||||||
|
r.hooks[hook].specification = {
|
||||||
|
description,
|
||||||
|
example,
|
||||||
|
params,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buildFiles: [],
|
||||||
|
config: {},
|
||||||
|
hooks: {},
|
||||||
|
todos: [],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const sortedHooks = Object.fromEntries(
|
||||||
|
Object.entries(hooks)
|
||||||
|
.map(([hook, {implementations, invocations, specification}]) => (
|
||||||
|
[
|
||||||
|
hook,
|
||||||
|
{
|
||||||
|
implementations: implementations
|
||||||
|
.sort(({filename: l}, {filename: r}) => (l < r ? -1 : 1)),
|
||||||
|
invocations: invocations
|
||||||
|
.sort(({filename: l}, {filename: r}) => (l < r ? -1 : 1)),
|
||||||
|
specification,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
))
|
||||||
|
.sort(([l], [r]) => (l < r ? -1 : 1)),
|
||||||
|
);
|
||||||
|
Object.entries(sortedHooks)
|
||||||
|
.forEach(([hook, {specification}]) => {
|
||||||
|
if (!specification) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(`Warning: no specification for hook: '${hook}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
'build-files': buildFiles,
|
||||||
|
config,
|
||||||
|
hooks: sortedHooks,
|
||||||
|
todos,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const {readFile} = require('fs/promises');
|
const {readFile} = require('fs/promises');
|
||||||
const {join, relative} = require('path');
|
const {dirname, join, relative} = require('path');
|
||||||
|
|
||||||
const {transformAsync} = require('@babel/core');
|
const {transformAsync} = require('@babel/core');
|
||||||
const {default: traverse} = require('@babel/traverse');
|
const {default: traverse} = require('@babel/traverse');
|
||||||
|
@ -125,6 +125,11 @@ exports.parseFleckRoot = async (request) => (
|
||||||
exports.parseFlecks = async (flecks) => (
|
exports.parseFlecks = async (flecks) => (
|
||||||
Promise.all(
|
Promise.all(
|
||||||
flecks.roots
|
flecks.roots
|
||||||
.map(async ([path, request]) => [path, await exports.parseFleckRoot(request)]),
|
.map(async ([path, request]) => [
|
||||||
|
path,
|
||||||
|
await exports.parseFleckRoot(
|
||||||
|
dirname(await flecks.resolver.resolve(join(request, 'package.json'))),
|
||||||
|
),
|
||||||
|
]),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,13 +25,7 @@
|
||||||
"@babel/core": "^7.17.2",
|
"@babel/core": "^7.17.2",
|
||||||
"@babel/traverse": "^7.17.0",
|
"@babel/traverse": "^7.17.0",
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
"@docusaurus/core": "3.0.1",
|
|
||||||
"@docusaurus/module-type-aliases": "3.0.1",
|
|
||||||
"@docusaurus/preset-classic": "3.0.1",
|
|
||||||
"@docusaurus/types": "3.0.1",
|
|
||||||
"@flecks/core": "^3.0.0",
|
"@flecks/core": "^3.0.0",
|
||||||
"@flecks/react": "^3.0.0",
|
|
||||||
"clsx": "^2.0.0",
|
|
||||||
"comment-parser": "^1.3.0",
|
"comment-parser": "^1.3.0",
|
||||||
"rimraf": "^5.0.5"
|
"rimraf": "^5.0.5"
|
||||||
},
|
},
|
||||||
|
|
23
website/README.md
Normal file
23
website/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Website
|
||||||
|
|
||||||
|
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
(cd website && yarn)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn dox start
|
||||||
|
```
|
||||||
|
|
||||||
|
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn dox build
|
||||||
|
```
|
3
website/babel.config.js
Normal file
3
website/babel.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||||
|
};
|
|
@ -1,23 +1,18 @@
|
||||||
---
|
---
|
||||||
title: Documentation website
|
title: Documentation
|
||||||
description: Document your project.
|
description: Document your project.
|
||||||
---
|
---
|
||||||
|
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
# Documentation
|
||||||
import ImageRow from '@site/helpers/image-row';
|
|
||||||
import InstallPackage from '@site/helpers/install-package';
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
|
||||||
|
|
||||||
# Documentation website
|
|
||||||
|
|
||||||
Ah, the most ~~boring~~ awesome part of development.
|
Ah, the most ~~boring~~ awesome part of development.
|
||||||
|
|
||||||
flecks provides a fleck called `@flecks/dox` that helps you generate a documentation website for
|
flecks provides a fleck called `@flecks/dox` that helps you generate documentation for your
|
||||||
your project.
|
project.
|
||||||
|
|
||||||
:::tip[Mmm, dog food]
|
:::tip[Mmm, dog food]
|
||||||
|
|
||||||
In fact, this very website you're viewing now has been built with the same tooling!
|
In fact, this very website you're viewing uses the same tooling!
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@ -29,65 +24,41 @@ To get started, add `@flecks/dox` to your project:
|
||||||
npx flecks add -d @flecks/dox
|
npx flecks add -d @flecks/dox
|
||||||
```
|
```
|
||||||
|
|
||||||
## Spin up a starter website
|
## Output for Docusaurus
|
||||||
|
|
||||||
Next, run:
|
[Docusaurus](https://docusaurus.io) is a nice way to generate a documentation website.
|
||||||
|
`@flecks/dox` can output MDX files for Docusaurus:
|
||||||
```bash
|
|
||||||
npx flecks docusaurus create
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now have a starter configuration in `build/docusaurus.config.js` and a new directory
|
|
||||||
in your project: `website`. See [the command documentation](/docs/cli#docusaurus) to discover more
|
|
||||||
options.
|
|
||||||
|
|
||||||
Now, start your documentation website's development server with:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
npx flecks docusaurus start
|
npx flecks dox docusaurus
|
||||||
```
|
```
|
||||||
|
|
||||||
You should now see a message similar to:
|
By default this command will output the files to `website/docs/flecks`.
|
||||||
|
|
||||||
```bash
|
As an example, this website defines an entry in `sidebars.js` like so:
|
||||||
[SUCCESS] Docusaurus website is running at: http://localhost:3000/
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Generated details',
|
||||||
|
link: {
|
||||||
|
type: 'generated-index',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
'flecks/hooks',
|
||||||
|
'flecks/config',
|
||||||
|
'flecks/build-files',
|
||||||
|
'flecks/todo',
|
||||||
|
],
|
||||||
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
Go and have a look!
|
## Raw output
|
||||||
|
|
||||||
<ImageRow>
|
You may also output JSON files for your own processing:
|
||||||
<ThemedImage
|
|
||||||
alt="Screenshot of the front page of the generated documentation site"
|
|
||||||
sources={{
|
|
||||||
light: useBaseUrl('/flecks-dox-1-light.png'),
|
|
||||||
dark: useBaseUrl('/flecks-dox-1.png'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ThemedImage
|
|
||||||
alt="Screenshot of the documentation introduction page of the generated documentation site"
|
|
||||||
sources={{
|
|
||||||
light: useBaseUrl('/flecks-dox-2-light.png'),
|
|
||||||
dark: useBaseUrl('/flecks-dox-2.png'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ImageRow>
|
|
||||||
|
|
||||||
Not bad, huh?
|
```
|
||||||
|
npx flecks dox json
|
||||||
## Building it out
|
|
||||||
|
|
||||||
It's only a starter template, of course. You'll want to pop over to
|
|
||||||
[the Docusaurus guides page](https://docusaurus.io/docs/category/guides) to build out your new
|
|
||||||
website. Have fun!
|
|
||||||
|
|
||||||
:::tip[Ready to roll out!]
|
|
||||||
|
|
||||||
When you're ready to build for production, just run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx flecks docusaurus build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
After successfully building, your website files will be located at `dist/dox`.
|
By default this command will output the files to `dist/dox`.
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ flecks is built with supreme attention to the developer and end-user experience.
|
||||||
- The simplest application is two flecks, `core` and `server` (**7 MB** production server size): you don't pay for what you don't buy
|
- The simplest application is two flecks, `core` and `server` (**7 MB** production server size): you don't pay for what you don't buy
|
||||||
- Endlessly configurable through built-in [hooks](./flecks/@flecks/dox/hooks) and then your own
|
- Endlessly configurable through built-in [hooks](./flecks/@flecks/dox/hooks) and then your own
|
||||||
- 🛠️ **Ready to build maintainable and performant production applications**
|
- 🛠️ **Ready to build maintainable and performant production applications**
|
||||||
- [Documentation website](./documentation) generation for your project with no fuss
|
- [Documentation](./documentation) generation for your project with no fuss
|
||||||
- [Write tests](./testing), run on server/in browser/...
|
- [Write tests](./testing), run on server/in browser/...
|
||||||
- [React](./react) / [redux](./redux)
|
- [React](./react) / [redux](./redux)
|
||||||
- [Realtime sockets](./sockets) with lots of goodies like binary packing and packet dispatching
|
- [Realtime sockets](./sockets) with lots of goodies like binary packing and packet dispatching
|
||||||
|
|
67
website/docusaurus.config.js
Normal file
67
website/docusaurus.config.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// @ts-check
|
||||||
|
// `@type` JSDoc annotations allow editor autocompletion and type checking
|
||||||
|
// (when paired with `@ts-check`).
|
||||||
|
// There are various equivalent ways to declare your Docusaurus config.
|
||||||
|
// See: https://docusaurus.io/docs/api/docusaurus-config
|
||||||
|
const {themes: prismThemes} = require('prism-react-renderer');
|
||||||
|
|
||||||
|
// For some reason we get a webpack warning if we use import here...
|
||||||
|
export default async function flecksDocusaurus() {
|
||||||
|
/** @type {import('@docusaurus/types').Config} */
|
||||||
|
const config = {
|
||||||
|
baseUrl: '/flecks/',
|
||||||
|
favicon: 'flecks.png',
|
||||||
|
i18n: {
|
||||||
|
defaultLocale: 'en',
|
||||||
|
locales: ['en'],
|
||||||
|
},
|
||||||
|
markdown: {
|
||||||
|
mermaid: true,
|
||||||
|
},
|
||||||
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
organizationName: 'cha0s', // Usually your GitHub org/user name.
|
||||||
|
presets: [
|
||||||
|
['classic', {
|
||||||
|
docs: {
|
||||||
|
sidebarPath: './sidebars.js',
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
path: 'pages',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
projectName: 'flecks', // Usually your repo name.
|
||||||
|
tagline: 'not static',
|
||||||
|
themes: ['@docusaurus/theme-mermaid'],
|
||||||
|
themeConfig:
|
||||||
|
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||||
|
({
|
||||||
|
navbar: {
|
||||||
|
title: 'flecks',
|
||||||
|
logo: {
|
||||||
|
alt: 'flecks logo',
|
||||||
|
src: 'flecks-textless.png',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
href: 'https://github.com/cha0s/flecks',
|
||||||
|
label: 'GitHub',
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
prism: {
|
||||||
|
theme: prismThemes.github,
|
||||||
|
darkTheme: prismThemes.dracula,
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
style: 'dark',
|
||||||
|
copyright: `Copyright © ${new Date().getFullYear()} cha0s. Built with flecks and Docusaurus.`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
title: 'flecks',
|
||||||
|
url: 'https://cha0s.github.io',
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
}
|
44
website/package.json
Normal file
44
website/package.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "flecks-docusaurus",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"docusaurus": "docusaurus",
|
||||||
|
"start": "docusaurus start",
|
||||||
|
"build": "docusaurus build",
|
||||||
|
"swizzle": "docusaurus swizzle",
|
||||||
|
"deploy": "docusaurus deploy",
|
||||||
|
"clear": "docusaurus clear",
|
||||||
|
"serve": "docusaurus serve",
|
||||||
|
"write-translations": "docusaurus write-translations",
|
||||||
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@docusaurus/core": "3.0.1",
|
||||||
|
"@docusaurus/preset-classic": "3.0.1",
|
||||||
|
"@mdx-js/react": "^3.0.0",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"prism-react-renderer": "^2.3.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@docusaurus/module-type-aliases": "3.0.1",
|
||||||
|
"@docusaurus/types": "3.0.1"
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.5%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 3 chrome version",
|
||||||
|
"last 3 firefox version",
|
||||||
|
"last 5 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,10 +56,10 @@ export default {
|
||||||
type: 'generated-index',
|
type: 'generated-index',
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
'flecks/@flecks/dox/hooks',
|
'flecks/hooks',
|
||||||
'flecks/@flecks/dox/config',
|
'flecks/config',
|
||||||
'flecks/@flecks/dox/build-configs',
|
'flecks/build-files',
|
||||||
'flecks/@flecks/dox/TODO',
|
'flecks/todo',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 77 KiB |
Binary file not shown.
Before Width: | Height: | Size: 77 KiB |
Binary file not shown.
Before Width: | Height: | Size: 77 KiB |
Binary file not shown.
Before Width: | Height: | Size: 77 KiB |
Loading…
Reference in New Issue
Block a user