Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: parseReadme / better feature sections #373

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions boilerplates/aws/files/$TEST.md.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { loadMarkdown, type TransformerProps } from "@batijs/core";

export default async function getReadme(props: TransformerProps) {
const content = await loadMarkdown(props);
//console.log("***aws***", await props.readfile());
//language=Markdown
const todo = `
## *AWS CDK Deployment*

This is a boilerplate for deploying your Vike app to AWS using the AWS Cloud Development Kit (CDK) including creating a custom domain in Route53.

**Architecture:**
- S3 Bucket for static client assets (\`/dist/client/assets\`).
- Lambda function for the backend and SSR.
- CloudFront distribution for CDN and routing requests \`/assets/*\` to the S3 bucket.

This boilerplate is a starting point for deploying your Vike app to AWS. You can customize the deployment by modifying the \`cdk/lib/vike-stack.ts\` file.

### Prerequisites

Before you get started, make sure to configure your AWS credentials.

**Loading from a file:**

You can keep your AWS credentials in a file. The credentials are found at:

\`~/.aws/credentials\` on Linux, Unix, and macOS;
\`C:\\Users\\USER_NAME\\.aws\\credentials\` on Windows

If the credentials file does not exist on your machine:

Download the AWS CLI from [here](https://aws.amazon.com/cli/) and configure your AWS credentials using the following command:
\`aws configure\`

And then use this guide to configure the credentials
The credentials file should look like:

\`
[default]
aws_access_key_id = <YOUR_ACCESS_KEY_ID>
aws_secret_access_key = <YOUR_SECRET_ACCESS_KEY>
\`

**Loading from environment variables:**

AWS SDK automatically detects AWS credentials in your environment and uses them for making requests to AWS. The environment variables that you need to set are:

\`AWS_ACCESS_KEY_ID\`
\`AWS_SECRET_ACCESS_KEY\`
If you are using temporary credentials, also set:

\`AWS_SESSION_TOKEN\`
This is often the most convenient way to configure credentials when deploying your AWS CDK app in a CI environment.

> [!NOTE]
> You should change the stack name to give your app stack a distinctive name in your AWS environment. You can do so by modifying the \`infrastructure.ts.ts\` file in the \`cdk/bin\` directory.

### Deployment to AWS

If you want to have a look at the synthesized CloudFormation template, you can run \`pnpm cdk synth\` and see the template as YAML on screen or in \`cdk.out/VikeStack.template.json\`.

> [!NOTE]
> If this is your **first time deploying a CDK app** in this environment you need to **bootstrap**:
> \`pnpm cdk bootstrap\`. (The default region based on your AWS CLI configuration will be used)

You can deploy your Vike App via the following command:
\`pnpm deploy:aws\` or \`pnpm cdk deploy\`

The URL to the CloudFront distribution will be displayed in the output of the deployment.
You can also access the CloudFront distribution domainname in the AWS SSM registry und \`vike/distribution/url\`.

### Stack Configuration

You can configure the stack in the \`cdk/bin/infrastructure.ts\` file:
| --- | --- | --- |
| Variable | Examples | Description |
| \`domainName: "example.com",\` | "example.com" | |
| \`subDomain: "www",\` |"www" | |
| \`certificate: undefined,\` | "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012" or a certificatemanager.ICertificate | reuse an existing AWS Certificate |
| \`hostedZone: undefined,\` | route53.HostedZone.fromLookup(stack, "MyHostedZone", { domainName: "example.com" }) | |

If \`domainName\` is managed by **Route53**, the \`hostedZone\` will be updated with by lookup in Route53 based on the \`domainName\`.

These scenarios are supported:
A. \`domainName\` exists in Route53, \`subDomain\` is given - the subdomain with the domain are used as alternative domains for the CloudFront Distribution. An new Certificate for the url is created and assigned to the CF-Distribution. An Alias-Record pointing to the CF-Distribution ist created in Route53.
B. \`domainName\` exists in Route53, \`subDomain\` is given - the subdomain with the domain are used as alternative domains for the CloudFront Distribution. If \`certificate\` contains a valid entry it will be assigned to the CF-Distribution. An Alias-Record pointing to the CF-Distribution ist created in Route53.
C. \`domainName\` **does not exist** in Route53, \`subDomain\` is given - the subdomain with the domain are used as alternative domains for the CloudFront Distribution. If \`certificate\` contains a valid entry it will be assigned to the CF-Distribution. A manual created CNAME or A-Record should pointing to the CF-Distribution.

#### Custom Domain

If you have a custom domain, you can add it to the stack configuration in the \`cdk/bin/infrastructure.ts\` file:

> [!NOTE]
> If you deploy your App to a region different than \`us-east-1\` and you have never deployed to this region before, you will need to bootstrap this region too:
\`CDK_DEFAULT_REGION=us-east-1 pnpm deploy:cdk bootstrap\`


### Destroying the Stack on AWS

To destroy the stack on AWS, run the following command:
\`pnpm cdk destroy\`

Or delete the CloudFormation stack which starts with "VikeStack-<Your App Name>" created by this project.

`;

content.addMarkdownFeature(todo);

return content.finalize();
}
45 changes: 45 additions & 0 deletions boilerplates/react/files/$TEST.md.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { loadMarkdown, type TransformerProps } from "@batijs/core";

export default async function getReadme(props: TransformerProps) {
const content = await loadMarkdown(props);
//console.log("***react***", await props.readfile());
//language=Markdown
const about = `
## React

This app is ready to start. It's powered by [Vike](https://vike.dev) and [React](https://react.dev/learn).

### \`/pages/+config.ts\`

Such \`+\` files are [the interface](https://vike.dev/config) between Vike and your code. It defines:
- A default [\`<Layout>\` component](https://vike.dev/Layout) (that wraps your [\`<Page>\` components](https://vike.dev/Page)).
- A default [\`title\`](https://vike.dev/title).
- Global [\`<head>\` tags](https://vike.dev/head-tags).

### Routing

[Vike's built-in router](https://vike.dev/routing) lets you choose between:
- [Filesystem Routing](https://vike.dev/filesystem-routing) (the URL of a page is determined based on where its \`+Page.jsx\` file is located on the filesystem)
- [Route Strings](https://vike.dev/route-string)
- [Route Functions](https://vike.dev/route-function)

### \`/pages/_error/+Page.jsx\`

The [error page](https://vike.dev/error-page) which is rendered when errors occur.

### \`/pages/+onPageTransitionStart.ts\` and \`/pages/+onPageTransitionEnd.ts\`

The [\`onPageTransitionStart()\` hook](https://vike.dev/onPageTransitionStart), together with [\`onPageTransitionEnd()\`](https://vike.dev/onPageTransitionEnd), enables you to implement page transition animations.

### SSR

SSR is enabled by default. You can [disable it](https://vike.dev/ssr) for all your pages or only for some pages.

### HTML Streaming

You can enable/disable [HTML streaming](https://vike.dev/streaming) for all your pages, or only for some pages while still using it for others.`;

content.addMarkdownFeature(about);

return content.finalize();
}
39 changes: 39 additions & 0 deletions boilerplates/sentry/files/$TEST.md.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { loadMarkdown, type TransformerProps } from "@batijs/core";

export default async function getTest(props: TransformerProps) {
const content = await loadMarkdown(props);
//console.log("***sentry***", await props.readfile());
const doc =
`
## Sentry Browser / Error Tracking & Performance Monitoring

This app is integrated with [Sentry](https://sentry.io) for error tracking.

Add your Sentry DSN to \`.env\` file.
You can configure [Sentry for the browser](` +
(props.meta.BATI.has("react")
? "https://docs.sentry.io/platforms/javascript/guides/react/"
: props.meta.BATI.has("solid")
? "https://docs.sentry.io/platforms/javascript/guides/solid/"
: props.meta.BATI.has("vue")
? "https://docs.sentry.io/platforms/javascript/guides/vue/"
: "https://docs.sentry.io/platforms/javascript/") +
`) in \`sentry.browser.config.ts\`.

Upload of source maps to Sentry is handled by the [\`sentryVitePlugin\`](https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/vite/) in \`vite.config.ts\`.
You have to configure \`SENTRY_ORG\`, \`SENTRY_PROJECT\` and \`SENTRY_AUTH_TOKEN\` in the \`.env.sentry-build-plugin\` file with the values from your Sentry account.

> [!NOTE]
> Sentry Error Tracking is **only activated in production** (\`import.meta.env.PROD === true\`)!

**Testing Sentry** receiving Errors:
1. Build & Start the app \`pnpm build && pnpm preview\`.
2. open Testpage in browser: http://localhost:3000/sentry.
3. check your [Sentry Dashboard](https://sentry.io) for new Errors.

`;

content.addMarkdownFeature(doc);

return content.finalize();
}
45 changes: 45 additions & 0 deletions boilerplates/shared/files/$TEST.md.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { getArgs, getVersion, loadMarkdown, type TransformerProps } from "@batijs/core";

export default async function getReadme(props: TransformerProps) {
const content = await loadMarkdown(props);
const flags = Array.from(props.meta.BATI)
.filter((f) => (f as string) !== "force")
.map((f) => `--${f}`)
.join(" ");
const v = getVersion();

//language=Markdown
const intro = `Generated with [Bati](https://batijs.dev) [${"version " + v.semver.at(-1)}](${"https://www.npmjs.com/package/create-bati/v/" + v.version}) using this command:

\`\`\`sh
${getArgs()} ${flags}
\`\`\`

`;

content.addMarkdown(intro, {
wrapper: {
section: "intro",
},
});
content.addMarkdown("", {
position: "after",
filter: {
section: "intro",
},
wrapper: {
section: "TOC",
},
});
content.addMarkdown("", {
position: "after",
filter: {
section: "document",
},
wrapper: {
section: "features",
},
});

return content.finalize();
}
1 change: 1 addition & 0 deletions packages/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"license": "MIT",
"devDependencies": {
"@batijs/compile": "workspace:*",
"@batijs/features": "workspace:*",
"@types/node": "^18.19.14",
"tsup": "^8.2.4"
},
Expand Down
9 changes: 7 additions & 2 deletions packages/build/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { FileOperation, OperationReport } from "./operations/common.js";
import { executeOperationFile } from "./operations/file.js";
import { executeOperationTransform } from "./operations/transform.js";
import { OperationsRearranger } from "./operations/rearranger.js";
import { features } from "@batijs/features";
import type { Flags, CategoryLabels } from "@batijs/features";

const reIgnoreFile = /^(chunk-|asset-|#)/gi;

Expand Down Expand Up @@ -135,17 +137,20 @@ Please report this issue to https://github.com/vikejs/bati`,
if (previousOp?.destination !== op.destination) {
previousOp = undefined;
}
const currentBoilerplate = op.source.split("/").at(-2) as Flags;
const currentCategory = features.find((f) => f.flag === currentBoilerplate)?.category as CategoryLabels;
const metaContext = { ...meta, localContext: { flag: currentBoilerplate, category: currentCategory } };
let report: OperationReport = {};
if (op.kind === "file") {
report = await executeOperationFile(op, {
meta,
meta: metaContext,
previousOperationSameDestination: previousOp,
});

updateAllImports(op.destinationAbsolute, report.context?.imports);
} else if (op.kind === "transform") {
report = await executeOperationTransform(op, {
meta,
meta: metaContext,
previousOperationSameDestination: previousOp,
});
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@
"@types/which": "^3.0.4",
"@typescript-eslint/parser": "^8.4.0",
"@typescript-eslint/utils": "^8.4.0",
"attributes-parser": "^2.2.3",
"colorette": "^2.0.20",
"esbuild": "^0.23.1",
"eslint": "^9.10.0",
"eslint-plugin-solid": "^0.14.3",
"eslint-rule-composer": "^0.3.0",
"espree": "^10.1.0",
"magicast": "^0.3.5",
"mdast-builder": "^1.1.1",
"mdast-util-from-markdown": "^2.0.1",
"mdast-util-to-markdown": "^2.1.0",
"mdast-util-to-string": "^4.0.0",
"prettier": "^3.3.3",
"squirrelly": "^9.1.0",
"tsup": "^8.2.4",
"tsx": "^4.19.0",
"typescript": "^5.5.4",
"unist-util-visit": "^5.0.0",
"unplugin-purge-polyfills": "^0.0.5",
"vitest": "^2.0.5",
"vue-eslint-parser": "^9.4.3",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./utils/env.js";
export * from "./which.js";
export * from "./print.js";
export * from "./markdown.js";
export * from "./markdown/markdown.js";
export * from "./random.js";
export * from "./runtime.js";
export type * from "./types.js";
6 changes: 6 additions & 0 deletions packages/core/src/loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type SchemaOptions,
} from "yaml";
import type { TransformerProps } from "./types.js";
import { parseMarkdown } from "./markdown/markdown.js";

export type { YAMLDocument };

Expand All @@ -20,6 +21,11 @@ export async function loadReadme({ readfile }: TransformerProps) {
return parseReadme(content);
}

export async function loadMarkdown({ readfile, meta }: TransformerProps) {
const content = await readfile?.();
return parseMarkdown(content ?? "", meta?.localContext);
}

export async function loadAsJson({ readfile, source, target }: TransformerProps) {
const content = await readfile?.();

Expand Down
52 changes: 52 additions & 0 deletions packages/core/src/markdown/createTOC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Root, Nodes, Heading, List, ListItem } from "mdast";
import { visit } from "unist-util-visit";
import { toString } from "mdast-util-to-string";
import { list, listItem, paragraph, text, link, heading } from "mdast-builder";

export function createTOC(tree: Root): Nodes[] | null {
const tocItems: Nodes[] = [];

// grap all headings with depth between 2 and 4
let inCommentMarkerTOC = false;
visit(tree, (node) => {
if (node.type === "html" && node.value.includes('<!--bati:start section="TOC"-->')) {
inCommentMarkerTOC = true;
return;
}
if (node.type === "html" && node.value.includes('<!--bati:end section="TOC"-->')) {
inCommentMarkerTOC = false;
}
if (inCommentMarkerTOC) return;
if (node.type !== "heading") return;
if (node.depth >= 2 && node.depth <= 4) {
tocItems.push(node);
}
});
if (tocItems.length === 0) return null;
const tocTree = buildTOCTree(tocItems as Heading[]);
return [heading(2, text("Contents")), tocTree] as Nodes[];
}

function buildTOCTree(items: Heading[]) {
let currentPosition = 0;

function walk(): List {
const result: ListItem[] = [];
const currentDepth = items[currentPosition].depth;
while (currentPosition < items.length) {
const item = items[currentPosition];
if (item.depth === currentDepth) {
const headingText = toString(item);
const headingId = headingText.toLowerCase().replace(/\s+/g, "-");
result.push(listItem([paragraph([link(`#${headingId}`, "", [text(headingText)])])]) as ListItem);
currentPosition++;
} else if (item.depth > currentDepth) {
result[result.length - 1].children.push(walk());
} else {
return list("unordered", result) as List;
}
}
return list("unordered", result) as List;
}
return walk();
}
Loading
Loading