Skip to content

Commit

Permalink
feat: more guide; @include inside code block
Browse files Browse the repository at this point in the history
  • Loading branch information
hanayashiki committed Mar 30, 2023
1 parent d45d13d commit d4c10c4
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 93 deletions.
4 changes: 3 additions & 1 deletion packages/mobx-zod-form-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export const useForm = <T extends MobxZodTypes>(
) => {
const form = useMemo(() => new ReactForm(schema, options), []);
useEffect(() => {
return form.startValidationWorker();
if (!form.options.setActionOptions.validateSync) {
return form.startValidationWorker();
}
}, []);

return form;
Expand Down
93 changes: 35 additions & 58 deletions packages/website/scripts/include-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,19 @@

const path = require("path");

const remark = require("remark");
const remarkMdx = require("remark-mdx");
const { readSync } = require("to-vfile");
const flatMap = require("unist-util-flatmap");

const mdAstToMdxAst = require("./md-ast-to-mdx-ast");

module.exports = function includeMarkdownPlugin({
resolveFrom,
resolveMdx,
} = {}) {
module.exports = function includeMarkdownPlugin({ resolveFrom } = {}) {
return function transformer(tree, file) {
return flatMap(tree, (node) => {
if (node.type !== "paragraph") return [node];
if (node.type !== "code") return [node];

const codeContent = node.value.trim();

// detect an `@include` statement
// detect an `@include "path/to/file"` statement
const includeMatch =
node.children[0].value &&
node.children[0].value.match(/^@include\s['"](.*)['"]$/);
codeContent && codeContent.match(/^@include\s['"](.*)['"]$/);
if (!includeMatch) return [node];

const [_includePath, hash] = includeMatch[1].split("#", 2);
Expand All @@ -36,58 +30,41 @@ module.exports = function includeMarkdownPlugin({
);
}

// if we are including a ".md" or ".mdx" file, we add the contents as processed markdown
// if any other file type, they are embedded into a code block
if (includePath.match(/\.md(?:x)?$/)) {
// return the file contents in place of the @include
// (takes a couple steps because we're processing includes with remark)
const processor = remark();
// if the include is MDX, and the plugin consumer has confirmed their
// ability to stringify MDX nodes (eg "jsx"), then use remarkMdx to support
// custom components (which would otherwise appear as likely invalid HTML nodes)
const isMdx = includePath.match(/\.mdx$/);
if (isMdx && resolveMdx) processor.use(remarkMdx).use(mdAstToMdxAst);
// use the includeMarkdown plugin to allow recursive includes
processor.use(includeMarkdownPlugin, { resolveFrom, resolveMdx });
// Process the file contents, then return them
const ast = processor.parse(includeContents);
return processor.runSync(ast, includeContents).children;
} else {
// trim trailing newline
includeContents.contents = includeContents.contents.trim();

if (hash) {
/**
* @type {string[]}
*/
const lines = includeContents.contents.split("\n");

const start = lines.findIndex((l) => l.includes(`/* #${hash} */`));
// trim trailing newline
includeContents.contents = includeContents.contents.trim();

if (start === -1) {
throw new Error(`${hash} is not found`);
}
if (hash) {
/**
* @type {string[]}
*/
const lines = includeContents.contents.split("\n");

const end = lines.findIndex(
(l, index) => index > start && l.includes(`/* #${hash} */`),
);
const start = lines.findIndex((l) => l.includes(`/* #${hash} */`));

includeContents.contents = includeContents.contents
.split("\n")
.slice(start + 1, end)
.filter((s) => !s.includes("/* #")) // Remove markers
.join("\n");
if (start === -1) {
throw new Error(`${hash} is not found`);
}

// return contents wrapped inside a "code" node
return [
{
type: "code",
lang: includePath.match(/\.(\w+)$/)[1],
value: includeContents,
},
];
const end = lines.findIndex(
(l, index) => index > start && l.includes(`/* #${hash} */`),
);

includeContents.contents = includeContents.contents
.split("\n")
.slice(start + 1, end)
.filter((s) => !s.includes("/* #")) // Remove markers
.join("\n");
}

// return contents wrapped inside a "code" node
return [
{
...node,
lang: node.lang || includePath.match(/\.(\w+)$/)[1],
meta: [node.meta, "showLineNumbers"].join(" "),
value: includeContents,
},
];
});
};
};
28 changes: 0 additions & 28 deletions packages/website/scripts/md-ast-to-mdx-ast.js

This file was deleted.

Binary file added packages/website/src/assets/guide-data-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions packages/website/src/components/examples/get-started.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const Form = observer(() => {

const { fields } = form.root;

/* #bindForm */
return (
<form
{...form.bindForm({
Expand All @@ -27,7 +26,6 @@ const Form = observer(() => {
},
})}
>
{/* #bindForm */}
<input {...form.bindField(fields.username)} placeholder="Username" />
<br />
{fields.username.touched &&
Expand Down
42 changes: 42 additions & 0 deletions packages/website/src/components/examples/guide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,45 @@ z.object({

/* #bind-form */
};

() => {
/* #decode-result */
const Form = observer(() => {
const form = useForm(
z.object({
age: z.number().min(20),
}),
);

const { fields } = form.root;

return (
<form
{...form.bindForm({
onSubmit(data /* { age: number; } */) {
alert(`You are ${data.age}-year-old. `);
},
})}
>
<input {...form.bindField(fields.age)} />
<div>
{fields.age.decodeResult.success
? fields.age.type.minValue! > fields.age.decodeResult.data
? `You are ${
fields.age.type.minValue! - fields.age.decodeResult.data
} years younger than 20`
: "Old enough"
: null}
</div>
{fields.age.touched &&
fields.age.errorMessages.map((e, i) => (
<div style={{ color: "red" }} key={i}>
{e}
</div>
))}
</form>
);
});

/* #decode-result */
};
3 changes: 3 additions & 0 deletions packages/website/src/pages/api-reference.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# API Reference

TODO:
6 changes: 5 additions & 1 deletion packages/website/src/pages/get-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ To get started, you should have setup a TypeScript React project (more framework

In your React project's root, copy-paste the following code to your entrypoint:
```
@include "../components/examples/get-started.tsx"
```
Then open your browser, you might see the form UI like this, if everything goes well.
Expand All @@ -48,7 +50,9 @@ This acts like most users expect, and also makes the dev feel at home if they ar
Then, enter complete information, and click submit, you'll see a popup on the screen, as we state in `bindForm`:

@include "../components/examples/get-started.tsx#bindForm"
```tsx {24-26}
@include "../components/examples/get-started.tsx"
```

Then, refresh the page, and click `Create Account` directly, you'll see the following:
Expand Down
Loading

0 comments on commit d4c10c4

Please sign in to comment.