├── .editorconfig ├── .gitattributes ├── .github ├── DISCUSSION_TEMPLATE │ └── integration-request.yml ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md ├── actions │ └── bati-run │ │ └── action.yml ├── renovate.json5 └── workflows │ ├── checks.yml │ ├── clear-cache.yml │ ├── pages.yml │ ├── preview.yml │ ├── publish.yml │ ├── tests-entry-os.reusable.yml │ ├── tests-entry.yml │ └── tests-generated.reusable.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── BOILERPLATES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── boilerplates ├── README.md ├── auth0 │ ├── files │ │ ├── $.env.ts │ │ └── $README.md.ts │ ├── package.json │ └── tsconfig.json ├── authjs │ ├── files │ │ ├── $package.json.ts │ │ ├── global.d.ts │ │ └── server │ │ │ └── authjs-handler.ts │ ├── package.json │ └── tsconfig.json ├── aws │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── cdk.json │ │ ├── cdk │ │ │ ├── $stack-name-suffix.json.ts │ │ │ ├── bin │ │ │ │ └── infrastructure.ts │ │ │ └── lib │ │ │ │ └── vike-stack.ts │ │ ├── tests │ │ │ └── aws_handler.spec.ts │ │ └── vitest.config.ts │ ├── package.json │ └── tsconfig.json ├── biome │ ├── files │ │ ├── $package.json.ts │ │ └── biome.json │ ├── package.json │ └── tsconfig.json ├── cloudflare │ ├── files │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $vite.config.ts.ts │ │ └── wrangler.toml │ ├── package.json │ └── tsconfig.json ├── compiled │ ├── files │ │ ├── $package.json.ts │ │ └── $vite.config.ts.ts │ ├── package.json │ └── tsconfig.json ├── d1-sqlite │ ├── files │ │ ├── $package.json.ts │ │ └── database │ │ │ ├── d1 │ │ │ └── queries │ │ │ │ ├── lucia-auth.ts │ │ │ │ └── todos.ts │ │ │ └── migrations │ │ │ ├── lucia-auth.sql │ │ │ └── todos.sql │ ├── package.json │ └── tsconfig.json ├── d1 │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $wrangler.toml.ts │ │ ├── database │ │ │ └── d1 │ │ │ │ └── helpers.ts │ │ └── global.d.ts │ ├── package.json │ └── tsconfig.json ├── drizzle │ ├── .gitignore │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── database │ │ │ └── drizzle │ │ │ │ ├── db.ts │ │ │ │ ├── queries │ │ │ │ ├── lucia-auth.ts │ │ │ │ └── todos.ts │ │ │ │ └── schema │ │ │ │ ├── lucia-auth.ts │ │ │ │ └── todos.ts │ │ └── drizzle.config.ts │ ├── package.json │ └── tsconfig.json ├── eslint │ ├── files │ │ ├── $package.json.ts │ │ └── eslint.config.ts │ ├── package.json │ └── tsconfig.json ├── express │ ├── files │ │ ├── $package.json.ts │ │ ├── express-entry.ts │ │ └── global.d.ts │ ├── package.json │ └── tsconfig.json ├── fastify │ ├── files │ │ ├── $package.json.ts │ │ ├── fastify-entry.ts │ │ └── global.d.ts │ ├── package.json │ └── tsconfig.json ├── firebase-auth │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── firebase │ │ │ └── $service-account.json.ts │ │ ├── global.d.ts │ │ ├── libs │ │ │ ├── firebaseAdmin.ts │ │ │ └── firebaseUI.ts │ │ ├── pages │ │ │ ├── +client.ts │ │ │ └── login │ │ │ │ ├── +config.ts │ │ │ │ └── +guard.ts │ │ └── server │ │ │ └── firebase-auth-middleware.ts │ ├── package.json │ └── tsconfig.json ├── google-analytics │ ├── files │ │ └── $.env.ts │ ├── package.json │ └── tsconfig.json ├── h3 │ ├── files │ │ ├── $package.json.ts │ │ ├── global.d.ts │ │ └── h3-entry.ts │ ├── package.json │ └── tsconfig.json ├── hattip │ ├── files │ │ ├── $package.json.ts │ │ ├── entry_aws_lambda.ts │ │ └── hattip-entry.ts │ ├── package.json │ └── tsconfig.json ├── hono │ ├── files │ │ ├── $package.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── entry_aws_lambda.ts │ │ ├── global.d.ts │ │ ├── hono-entry.node.ts │ │ └── hono-entry.ts │ ├── package.json │ └── tsconfig.json ├── lucia-auth │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── global.d.ts │ │ ├── lib │ │ │ └── lucia-auth.ts │ │ ├── pages │ │ │ └── login │ │ │ │ ├── +guard.ts │ │ │ │ └── style.css │ │ └── server │ │ │ └── lucia-auth-handlers.ts │ ├── package.json │ └── tsconfig.json ├── mantine │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── .vscode │ │ │ └── settings.json │ │ ├── components │ │ │ └── Link.tsx │ │ ├── layouts │ │ │ ├── LayoutDefault.tsx │ │ │ ├── style.css │ │ │ └── theme.ts │ │ └── postcss.config.cjs │ ├── package.json │ └── tsconfig.json ├── panda-css │ ├── files │ │ ├── $package.json.ts │ │ ├── layouts │ │ │ └── panda.css │ │ ├── panda.config.ts │ │ └── postcss.config.js │ ├── package.json │ └── tsconfig.json ├── pnpm │ ├── files │ │ └── $pnpm-workspace.yaml.ts │ ├── package.json │ └── tsconfig.json ├── prettier │ ├── files │ │ ├── $package.json.ts │ │ ├── .prettierignore │ │ └── .prettierrc │ ├── package.json │ └── tsconfig.json ├── prisma │ ├── .gitignore │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ └── $package.json.ts │ ├── package.json │ └── tsconfig.json ├── react-firebase-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.tsx │ ├── package.json │ └── tsconfig.json ├── react-lucia-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.tsx │ ├── package.json │ └── tsconfig.json ├── react-sentry │ ├── files │ │ ├── $package.json.ts │ │ ├── pages │ │ │ └── sentry │ │ │ │ └── +Page.tsx │ │ └── sentry.browser.config.ts │ ├── package.json │ └── tsconfig.json ├── react │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── assets │ │ │ └── logo.svg │ │ ├── components │ │ │ └── Link.tsx │ │ ├── layouts │ │ │ ├── LayoutDefault.tsx │ │ │ └── style.css │ │ └── pages │ │ │ ├── +Head.tsx │ │ │ ├── +config.ts │ │ │ ├── +onPageTransitionEnd.ts │ │ │ ├── +onPageTransitionStart.ts │ │ │ ├── _error │ │ │ └── +Page.tsx │ │ │ ├── index │ │ │ ├── +Page.tsx │ │ │ └── Counter.tsx │ │ │ ├── star-wars │ │ │ ├── @id │ │ │ │ ├── +Page.tsx │ │ │ │ └── +data.ts │ │ │ ├── index │ │ │ │ ├── +Page.tsx │ │ │ │ └── +data.ts │ │ │ └── types.ts │ │ │ └── todo │ │ │ ├── +Page.tsx │ │ │ └── TodoList.tsx │ ├── package.json │ ├── panda.config.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── sentry │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── .env.sentry-build-plugin │ │ └── pages │ │ │ └── $+client.ts.ts │ ├── package.json │ └── tsconfig.json ├── shadcn-ui │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── components.json │ │ ├── layouts │ │ │ └── !tailwind.css │ │ └── lib │ │ │ └── utils.ts │ ├── package.json │ └── tsconfig.json ├── shared-db │ ├── files │ │ └── server │ │ │ └── db-middleware.ts │ ├── package.json │ └── tsconfig.json ├── shared-no-db │ ├── files │ │ └── database │ │ │ └── todoItems.ts │ ├── package.json │ └── tsconfig.json ├── shared-server │ ├── bati.d.ts │ ├── files │ │ ├── $package.json.ts │ │ └── server │ │ │ ├── create-todo-handler.ts │ │ │ └── vike-handler.ts │ ├── package.json │ └── tsconfig.json ├── shared-todo │ ├── bati.d.ts │ ├── files │ │ ├── global.d.ts │ │ └── pages │ │ │ └── todo │ │ │ ├── +config.ts │ │ │ └── +data.ts │ ├── package.json │ └── tsconfig.json ├── shared │ ├── files │ │ ├── $README.md.ts │ │ ├── gitignore │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── hooks │ │ └── after.ts │ ├── package.json │ └── tsconfig.json ├── solid-firebase-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.tsx │ ├── package.json │ └── tsconfig.json ├── solid-lucia-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.tsx │ ├── package.json │ └── tsconfig.json ├── solid-sentry │ ├── files │ │ ├── $package.json.ts │ │ ├── pages │ │ │ └── sentry │ │ │ │ └── +Page.tsx │ │ └── sentry.browser.config.ts │ ├── package.json │ └── tsconfig.json ├── solid │ ├── files │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── assets │ │ │ └── logo.svg │ │ ├── components │ │ │ └── Link.tsx │ │ ├── layouts │ │ │ ├── LayoutDefault.tsx │ │ │ └── style.css │ │ └── pages │ │ │ ├── +Head.tsx │ │ │ ├── +config.ts │ │ │ ├── +onPageTransitionEnd.ts │ │ │ ├── +onPageTransitionStart.ts │ │ │ ├── _error │ │ │ └── +Page.tsx │ │ │ ├── index │ │ │ ├── +Page.tsx │ │ │ └── Counter.tsx │ │ │ ├── star-wars │ │ │ ├── @id │ │ │ │ ├── +Page.tsx │ │ │ │ └── +data.ts │ │ │ ├── index │ │ │ │ ├── +Page.tsx │ │ │ │ └── +data.ts │ │ │ └── types.ts │ │ │ └── todo │ │ │ ├── +Page.tsx │ │ │ └── TodoList.tsx │ ├── package.json │ ├── panda.config.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── sqlite │ ├── files │ │ ├── $.env.ts │ │ ├── $README.md.ts │ │ ├── $package.json.ts │ │ └── database │ │ │ └── sqlite │ │ │ ├── db.ts │ │ │ ├── queries │ │ │ ├── lucia-auth.ts │ │ │ └── todos.ts │ │ │ └── schema │ │ │ ├── all.ts │ │ │ ├── lucia-auth.ts │ │ │ └── todos.ts │ ├── package.json │ └── tsconfig.json ├── tailwindcss │ ├── files │ │ ├── $package.json.ts │ │ ├── $vite.config.ts.ts │ │ └── layouts │ │ │ └── tailwind.css │ ├── package.json │ └── tsconfig.json ├── telefunc │ ├── files │ │ ├── $package.json.ts │ │ ├── $vite.config.ts.ts │ │ ├── global.d.ts │ │ ├── pages │ │ │ └── todo │ │ │ │ └── TodoList.telefunc.ts │ │ └── server │ │ │ └── telefunc-handler.ts │ ├── package.json │ └── tsconfig.json ├── trpc │ ├── files │ │ ├── $package.json.ts │ │ ├── server │ │ │ └── trpc-handler.ts │ │ └── trpc │ │ │ ├── client.ts │ │ │ └── server.ts │ ├── package.json │ └── tsconfig.json ├── ts-rest │ ├── files │ │ ├── $package.json.ts │ │ ├── server │ │ │ └── ts-rest-handler.ts │ │ └── ts-rest │ │ │ ├── client.ts │ │ │ └── contract.ts │ ├── package.json │ └── tsconfig.json ├── tsconfig.base.json ├── vercel │ ├── files │ │ ├── $package.json.ts │ │ ├── $tsconfig.json.ts │ │ ├── $vite.config.ts.ts │ │ └── pages │ │ │ └── $+config.ts.ts │ ├── package.json │ └── tsconfig.json ├── vue-firebase-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.vue │ ├── package.json │ └── tsconfig.json ├── vue-lucia-auth │ ├── files │ │ └── pages │ │ │ └── login │ │ │ └── +Page.vue │ ├── package.json │ └── tsconfig.json ├── vue-sentry │ ├── files │ │ ├── $package.json.ts │ │ ├── pages │ │ │ └── sentry │ │ │ │ └── +Page.vue │ │ └── sentry.browser.config.ts │ ├── package.json │ └── tsconfig.json └── vue │ ├── files │ ├── $README.md.ts │ ├── $package.json.ts │ ├── $tsconfig.json.ts │ ├── $vite.config.ts.ts │ ├── assets │ │ └── logo.svg │ ├── components │ │ ├── Content.vue │ │ ├── Counter.vue │ │ ├── Link.vue │ │ ├── Logo.vue │ │ └── Sidebar.vue │ ├── layouts │ │ └── LayoutDefault.vue │ ├── pages │ │ ├── +Head.vue │ │ ├── +config.ts │ │ ├── +onCreateApp.ts │ │ ├── +onPageTransitionEnd.ts │ │ ├── +onPageTransitionStart.ts │ │ ├── _error │ │ │ └── +Page.vue │ │ ├── index │ │ │ └── +Page.vue │ │ ├── star-wars │ │ │ ├── @id │ │ │ │ ├── +Page.vue │ │ │ │ └── +data.ts │ │ │ ├── index │ │ │ │ ├── +Page.vue │ │ │ │ └── +data.ts │ │ │ └── types.ts │ │ └── todo │ │ │ ├── +Page.vue │ │ │ └── TodoList.vue │ └── vue-shim.d.ts │ ├── package.json │ ├── panda.config.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── bump.config.ts ├── doc ├── bati.png ├── demo.gif ├── demo.yml └── screenshot.png ├── eslint.config.ts ├── package.json ├── packages ├── batijs │ ├── README.md │ ├── index.js │ └── package.json ├── build │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── operations │ │ │ ├── common.ts │ │ │ ├── file.ts │ │ │ ├── merge-dts.ts │ │ │ ├── rearranger.ts │ │ │ └── transform.ts │ │ ├── queue.ts │ │ ├── relations.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── cli │ ├── README.md │ ├── esbuild-bundle-all.ts │ ├── index.ts │ ├── package.json │ ├── rules.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── turbo.json │ └── types.ts ├── compile │ ├── README.md │ ├── clean.ts │ ├── cli.js │ ├── copy.ts │ ├── dts.ts │ ├── esbuild.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.json │ └── tsup.config.ts ├── core │ ├── README.md │ ├── global.d.ts │ ├── package.json │ ├── src │ │ ├── assert.ts │ │ ├── format.ts │ │ ├── index.ts │ │ ├── loaders.ts │ │ ├── magicast.ts │ │ ├── markdown.ts │ │ ├── markdown │ │ │ ├── createTOC.ts │ │ │ ├── markdown.ts │ │ │ ├── types.ts │ │ │ ├── utils.ts │ │ │ └── zone.ts │ │ ├── parse.ts │ │ ├── parse │ │ │ ├── eval.ts │ │ │ ├── linters │ │ │ │ ├── common.ts │ │ │ │ ├── index.ts │ │ │ │ ├── linter-ts.ts │ │ │ │ ├── linter-vue.ts │ │ │ │ ├── plugin-remove-unused-imports.ts │ │ │ │ ├── types.ts │ │ │ │ ├── visit-if-statement.ts │ │ │ │ ├── visitor-global-comments.ts │ │ │ │ ├── visitor-imports.ts │ │ │ │ ├── visitor-statement-with-comments.ts │ │ │ │ └── visitor-ts-types.ts │ │ │ └── squirelly.ts │ │ ├── print.ts │ │ ├── random.ts │ │ ├── relative.ts │ │ ├── runtime.ts │ │ ├── types.ts │ │ ├── utils │ │ │ ├── env.ts │ │ │ └── package.ts │ │ └── which.ts │ ├── tests │ │ ├── markdown │ │ │ ├── markdown.spec.ts │ │ │ ├── toc.spec.ts │ │ │ └── utils.spec.ts │ │ ├── replace-bati-imports.spec.ts │ │ ├── transform-ts.spec.ts │ │ ├── transform-vue.spec.ts │ │ └── utils │ │ │ └── package.spec.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── type-utils.ts ├── create-bati │ ├── README.md │ ├── index.js │ └── package.json ├── create-batijs-app │ ├── README.md │ ├── index.js │ └── package.json ├── features │ ├── README.md │ ├── package.json │ ├── src │ │ ├── categories.ts │ │ ├── features.ts │ │ ├── groups.ts │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── rules │ │ │ ├── enum.ts │ │ │ ├── index.ts │ │ │ ├── rules.ts │ │ │ └── utils.ts │ │ └── types.ts │ ├── tests │ │ └── rules.spec.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── tests-utils │ ├── README.md │ ├── package.json │ ├── src │ │ ├── combinate.ts │ │ ├── describe.ts │ │ ├── exec.ts │ │ ├── index.ts │ │ ├── package-manager.ts │ │ ├── port.ts │ │ ├── prepare.ts │ │ ├── run-build.ts │ │ ├── run-dev.ts │ │ ├── types.ts │ │ ├── wait-for-localhost.ts │ │ └── zx.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── tests │ ├── README.md │ ├── package.json │ ├── rules.local.spec.ts │ ├── src │ │ ├── common.ts │ │ ├── exec-bati.ts │ │ ├── index.ts │ │ ├── load-test-files.ts │ │ ├── prepare.ts │ │ ├── tmp.ts │ │ └── types.ts │ ├── tests │ │ ├── FRAMEWORK+ANALYTICS.spec.ts │ │ ├── FRAMEWORK+CSS.spec.ts │ │ ├── FRAMEWORK+SERVER+AUTH.spec.ts │ │ ├── FRAMEWORK+SERVER+DATA.spec.ts │ │ ├── FRAMEWORK+SERVER+lucia-auth.spec.ts │ │ ├── FRAMEWORK+aws.spec.ts │ │ ├── FRAMEWORK+cloudflare.spec.ts │ │ ├── FRAMEWORK+prettier.spec.ts │ │ ├── FRAMEWORK+prisma.spec.ts │ │ ├── FRAMEWORK+sentry.spec.ts │ │ ├── FRAMEWORK+vercel+express.spec.ts │ │ └── react+UI.spec.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts └── tsconfig.base.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── render-gif.sh ├── scripts ├── gen-composite-workflow-action.js ├── helpers │ └── boilerplates.ts ├── new-boilerplate.ts └── screenshot.js ├── terminalizer-config.yml ├── tsconfig.json ├── turbo.json ├── update-gif.sh └── website ├── README.md ├── assets └── logo.svg ├── components ├── Cli.tsx ├── Copy.tsx ├── Description.tsx ├── Features.tsx ├── Flip.tsx ├── FormControl.tsx ├── Icons.tsx ├── InputGroup.tsx ├── Logo.tsx ├── Messages.tsx ├── Presets.tsx ├── RootContext.tsx ├── RulesMessages.tsx ├── ShieldBadge.tsx ├── Stackblitz.tsx ├── Store.tsx ├── Tooltip.tsx └── Widget.tsx ├── index.html ├── layouts ├── Head.tsx ├── LayoutDefault.tsx └── tailwind.css ├── lib ├── floating-solid.ts └── track.ts ├── package.json ├── pages ├── +config.ts ├── _error │ └── +Page.tsx └── index │ ├── +Page.tsx │ └── +config.ts ├── tsconfig.json ├── types.ts ├── vite.config.ts └── widget ├── AppWidget.tsx └── web-component.index.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/integration-request.yml: -------------------------------------------------------------------------------- 1 | body: 2 | - type: markdown 3 | attributes: 4 | value: | 5 | You want Bati to support a specific lib or framework? Great! 6 | Simply fill in the form below 7 | - type: input 8 | id: name 9 | attributes: 10 | label: Name 11 | description: Name of the lib or framework 12 | placeholder: React 13 | - type: input 14 | id: homepage 15 | attributes: 16 | label: Homepage 17 | description: Homepage of the lib or framework 18 | placeholder: https://react.dev 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['vikejs', 'magne4000'] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: magne4000 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/workflows/clear-cache.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries 2 | name: Cleanup caches on PR closes 3 | on: 4 | pull_request_target: 5 | types: 6 | - closed 7 | 8 | jobs: 9 | cleanup: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Cleanup 13 | run: | 14 | gh extension install actions/gh-actions-cache 15 | 16 | echo "Fetching list of cache key" 17 | cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 ) 18 | 19 | ## Setting this to not fail the workflow while deleting cache keys. 20 | set +e 21 | echo "Deleting caches..." 22 | for cacheKey in $cacheKeysForPR 23 | do 24 | gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm 25 | done 26 | echo "Done" 27 | env: 28 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | REPO: ${{ github.repository }} 30 | BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge 31 | -------------------------------------------------------------------------------- /.github/workflows/tests-entry.yml: -------------------------------------------------------------------------------- 1 | # Step 1 2 | name: Tests 3 | 4 | on: 5 | pull_request: 6 | paths-ignore: 7 | - 'website/**' 8 | push: 9 | # Ensures cache is computed on main branch so that it can be reused on all PRs 10 | branches: [ "main" ] 11 | paths-ignore: 12 | - 'website/**' 13 | 14 | concurrency: 15 | group: ${{ github.ref }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | monorepo: 20 | strategy: 21 | matrix: 22 | # FIXME Windows tests are not terminating, and GH CI terminates them after many hours 23 | os: [ ubuntu-latest, macos-latest ] 24 | # os: [ ubuntu-latest, macos-latest, windows-latest ] 25 | node: [ 20 ] 26 | fail-fast: false 27 | 28 | uses: ./.github/workflows/tests-entry-os.reusable.yml 29 | secrets: inherit 30 | with: 31 | os: ${{ matrix.os }} 32 | node: ${{ matrix.node }} 33 | fast: ${{ matrix.os != 'ubuntu-latest' }} 34 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | 5 | pnpm-lock.yaml 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-present Joël Charles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /boilerplates/auth0/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | const auth0ClientId = process.env.TEST_AUTH0_CLIENT_ID; 5 | const auth0ClientSecret = process.env.TEST_AUTH0_CLIENT_SECRET; 6 | const auth0BaseUrl = process.env.TEST_AUTH0_ISSUER_BASE_URL; 7 | 8 | let envContent = await props.readfile?.(); 9 | 10 | envContent = appendToEnv(envContent, "AUTH0_CLIENT_ID", auth0ClientId ?? "", "Auth0 Client ID"); 11 | envContent = appendToEnv(envContent, "AUTH0_CLIENT_SECRET", auth0ClientSecret ?? "", "Auth0 Client Secret"); 12 | envContent = appendToEnv(envContent, "AUTH0_ISSUER_BASE_URL", auth0BaseUrl ?? "", "Auth0 base URL"); 13 | 14 | return envContent; 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/auth0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/auth0", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103" 17 | }, 18 | "dependencies": { 19 | "@batijs/core": "workspace:*" 20 | }, 21 | "files": [ 22 | "dist/" 23 | ], 24 | "bati": { 25 | "if": { 26 | "flag": "auth0" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /boilerplates/auth0/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/authjs/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@auth/core", "@universal-middleware/core"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/authjs/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { Session } from "@auth/core/types"; 2 | 3 | declare global { 4 | namespace Vike { 5 | interface PageContext { 6 | session?: Session | null; 7 | } 8 | } 9 | } 10 | 11 | export {}; 12 | -------------------------------------------------------------------------------- /boilerplates/authjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/authjs", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@auth/core": "^0.39.1", 16 | "@batijs/compile": "workspace:*", 17 | "@types/node": "^18.19.103", 18 | "@universal-middleware/core": "^0.4.7" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*" 22 | }, 23 | "files": [ 24 | "dist/" 25 | ], 26 | "bati": { 27 | "if": { 28 | "flag": { 29 | "$in": [ 30 | "authjs", 31 | "auth0" 32 | ] 33 | } 34 | } 35 | }, 36 | "exports": { 37 | "./server/authjs-handler": { 38 | "types": "./dist/types/server/authjs-handler.d.ts" 39 | } 40 | }, 41 | "typesVersions": { 42 | "*": { 43 | "server/authjs-handler": [ 44 | "./dist/types/server/authjs-handler.d.ts" 45 | ] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /boilerplates/authjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/aws/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("test", { 8 | value: "vitest", 9 | precedence: 0, 10 | }) 11 | .setScript("deploy:cdk-deploy-all", { 12 | value: "cdk deploy --all", 13 | precedence: 0, 14 | }) 15 | .setScript("deploy:aws", { 16 | value: "run-s build deploy:cdk-deploy-all", 17 | precedence: 0, 18 | }) 19 | .setScript("cdk:app", { 20 | value: "tsx cdk/bin/infrastructure.ts", 21 | precedence: 0, 22 | }) 23 | .setScript("cdk", { 24 | value: "cdk", 25 | precedence: 0, 26 | }) 27 | .addDependencies(["aws-cdk-lib", "constructs", "source-map-support"]) 28 | .addDevDependencies(["cdk", "aws-cdk", "@types/node", "@types/which", "typescript", "esbuild", "vitest", "which"]) 29 | .addDevDependencies(["npm-run-all2"], ["deploy:aws"]) 30 | .addDevDependencies(["tsx"], ["cdk:app"]); 31 | } 32 | -------------------------------------------------------------------------------- /boilerplates/aws/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | tsConfig.compilerOptions.types = [ 6 | ...(tsConfig.compilerOptions.types ?? []), 7 | "@types/node", 8 | "vitest/globals", 9 | "@types/which", 10 | ]; 11 | tsConfig.exclude ??= []; 12 | tsConfig.exclude.push("cdk.out"); 13 | return tsConfig; 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/aws/files/cdk/$stack-name-suffix.json.ts: -------------------------------------------------------------------------------- 1 | export default async function getDataJson() { 2 | const dataJson = { 3 | stackNameSuffix: generateRandomPrefix(8), 4 | }; 5 | return dataJson; 6 | } 7 | 8 | // Function to generate a random string 9 | function generateRandomPrefix(length: number): string { 10 | if (process.env.NODE_ENV === "test") return "TEST"; 11 | const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 12 | let result = ""; 13 | for (let i = 0; i < length; i++) { 14 | result += characters.charAt(Math.floor(Math.random() * characters.length)); 15 | } 16 | return result; 17 | } 18 | -------------------------------------------------------------------------------- /boilerplates/aws/files/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ["tests/**/*.spec.ts"], // Adjust the pattern as needed 6 | watch: false, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /boilerplates/aws/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["node", "vitest/globals", "@types/which", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/biome/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("lint", { 8 | value: "biome lint --write .", 9 | precedence: 0, 10 | }) 11 | .setScript("format", { 12 | value: "biome format --write .", 13 | precedence: 0, 14 | }) 15 | .addDevDependencies(["@biomejs/biome"]); 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/biome/files/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true 10 | } 11 | }, 12 | "files": { 13 | "ignore": ["dist/**", "*.js", "*.cjs", "*.mjs", "*.spec.ts"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/biome/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/biome", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@biomejs/biome": "1.9.4", 17 | "@types/node": "^18.19.103" 18 | }, 19 | "dependencies": { 20 | "@batijs/core": "workspace:*" 21 | }, 22 | "files": [ 23 | "dist/" 24 | ], 25 | "bati": { 26 | "if": { 27 | "flag": "biome" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /boilerplates/biome/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/cloudflare/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.types = [...(tsConfig.compilerOptions.types ?? []), "vike-cloudflare/types"]; 7 | 8 | return tsConfig; 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/cloudflare/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | let options: Parameters[1]["options"] = undefined; 7 | 8 | if (props.meta.BATI.has("hattip") || props.meta.BATI.has("hono")) { 9 | options = { 10 | server: { 11 | kind: props.meta.BATI.has("hono") ? "hono" : "hattip", 12 | entry: props.meta.BATI.has("hono") ? "hono-entry.ts" : "hattip-entry.ts", 13 | }, 14 | }; 15 | } 16 | 17 | addVitePlugin(mod, { 18 | from: "vike-cloudflare", 19 | constructor: "pages", 20 | imported: "pages", 21 | options, 22 | }); 23 | 24 | return mod.generate().code; 25 | } 26 | -------------------------------------------------------------------------------- /boilerplates/cloudflare/files/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "my-app" 2 | compatibility_date = "2024-09-29" 3 | pages_build_output_dir = "./dist/cloudflare" 4 | compatibility_flags = [ "nodejs_compat" ] 5 | -------------------------------------------------------------------------------- /boilerplates/cloudflare/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/cloudflare", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@cloudflare/workers-types": "^4.20250525.0", 17 | "@hattip/adapter-cloudflare-workers": "^0.0.49", 18 | "@types/node": "^18.19.103", 19 | "npm-run-all2": "^8.0.4", 20 | "vike-cloudflare": "^0.1.7", 21 | "wrangler": "^4.16.1" 22 | }, 23 | "dependencies": { 24 | "@batijs/core": "workspace:*" 25 | }, 26 | "files": [ 27 | "dist/" 28 | ], 29 | "bati": { 30 | "if": { 31 | "flag": "cloudflare" 32 | }, 33 | "enforce": "post" 34 | }, 35 | "exports": { 36 | "./test": { 37 | "types": "./dist/types/test.d.ts" 38 | } 39 | }, 40 | "typesVersions": { 41 | "*": { 42 | "test": [ 43 | "./dist/types/test.d.ts" 44 | ] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /boilerplates/cloudflare/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/compiled/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDevDependencies(["vite-plugin-compiled-react"]).addDependencies(["@compiled/react"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/compiled/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "vite-plugin-compiled-react", 8 | constructor: "compiled", 9 | imported: "compiled", 10 | options: { extract: true }, 11 | }); 12 | 13 | return mod.generate().code; 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/compiled/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/compiled", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103", 17 | "vite": "^6.3.5", 18 | "vite-plugin-compiled-react": "^1.3.1" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*", 22 | "@compiled/react": "^0.18.4" 23 | }, 24 | "files": [ 25 | "dist/" 26 | ], 27 | "bati": { 28 | "if": { 29 | "flag": "compiled-css" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/compiled/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/d1-sqlite/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.setScript("d1:migrate", { 7 | value: "wrangler d1 migrations apply YOUR_DATABASE_NAME --local", 8 | precedence: 0, 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/d1-sqlite/files/database/d1/queries/todos.ts: -------------------------------------------------------------------------------- 1 | import type { D1Database } from "@cloudflare/workers-types"; 2 | 3 | export function insertTodo(db: D1Database, text: string) { 4 | return db.prepare("INSERT INTO todos (text) VALUES (?)").bind(text).run(); 5 | } 6 | 7 | export async function getAllTodos(db: D1Database) { 8 | const { results } = await db.prepare("SELECT * FROM todos").all<{ id: number; text: string }>(); 9 | return results; 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/d1-sqlite/files/database/migrations/lucia-auth.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS users ( 2 | id TEXT NOT NULL PRIMARY KEY, 3 | username TEXT NOT NULL UNIQUE, 4 | password TEXT 5 | ); 6 | 7 | CREATE TABLE IF NOT EXISTS oauth_accounts ( 8 | provider_id TEXT NOT NULL, 9 | provider_user_id INTEGER NOT NULL, 10 | user_id TEXT NOT NULL, 11 | PRIMARY KEY (provider_id, provider_user_id), 12 | FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE cascade ON DELETE cascade 13 | ); 14 | 15 | CREATE TABLE IF NOT EXISTS sessions ( 16 | id TEXT NOT NULL PRIMARY KEY, 17 | expires_at INTEGER NOT NULL, 18 | user_id TEXT NOT NULL, 19 | FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE cascade ON DELETE cascade 20 | ); 21 | -------------------------------------------------------------------------------- /boilerplates/d1-sqlite/files/database/migrations/todos.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS todos ( 2 | id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 3 | text TEXT 4 | ); 5 | -------------------------------------------------------------------------------- /boilerplates/d1-sqlite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/d1/files/$README.md.ts: -------------------------------------------------------------------------------- 1 | import { loadMarkdown, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getReadme(props: TransformerProps) { 4 | const content = await loadMarkdown(props); 5 | 6 | //language=Markdown 7 | const todo = ` 8 | ## *Cloudflare D1* 9 | 10 | ### Setup 11 | Create a D1 database with the following command: 12 | \`\`\`sh 13 | wrangler d1 create 14 | \`\`\` 15 | 16 | Then, copy the output to \`wrangler.toml\`. 17 | 18 | Finally, update the \`d1:migrate\` script (in \`package.json\`) to replace \`YOUR_DATABASE_NAME\`, and execute it. 19 | 20 | > [!NOTE] 21 | > For reference, a good database name is: 22 | > - Typically a combination of ASCII characters, shorter than 32 characters, and uses dashes (-) instead of spaces. 23 | > - Descriptive of the use-case and environment. For example, “staging-db-web” or “production-db-backend”. 24 | > - Only used for describing the database, and is not directly referenced in code. 25 | `; 26 | 27 | content.addMarkdownFeature(todo, "sqlite", { 28 | position: "before", 29 | }); 30 | 31 | return content; 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/d1/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDevDependencies(["@universal-middleware/core"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/d1/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.types = [...(tsConfig.compilerOptions.types ?? []), "@cloudflare/workers-types"]; 7 | 8 | return tsConfig; 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/d1/files/$wrangler.toml.ts: -------------------------------------------------------------------------------- 1 | import { type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getToml(props: TransformerProps) { 4 | const content = await props.readfile?.(); 5 | 6 | if (!content) { 7 | throw new Error("wrangler.toml should not be empty"); 8 | } 9 | 10 | //language=toml 11 | const dbSnippet = ` 12 | # https://developers.cloudflare.com/d1/build-with-d1/local-development/#develop-locally-with-pages 13 | [[d1_databases]] 14 | binding = "DB" # Should match preview_database_id 15 | database_name = "YOUR_DATABASE_NAME" 16 | database_id = "the-id-of-your-D1-database-goes-here" # wrangler d1 info YOUR_DATABASE_NAME 17 | preview_database_id = "DB" # Required for Pages local development 18 | migrations_dir = "database/migrations" 19 | `; 20 | 21 | //language=toml 22 | return `${content} 23 | ${dbSnippet}`; 24 | } 25 | -------------------------------------------------------------------------------- /boilerplates/d1/files/database/d1/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { D1Database } from "@cloudflare/workers-types"; 2 | import type { RuntimeAdapter } from "@universal-middleware/core"; 3 | 4 | /** 5 | * Retrieve Cloudflare `env.DB` from `universal-middleware` runtime 6 | */ 7 | export async function getDbFromRuntime(runtime: RuntimeAdapter): Promise { 8 | if (runtime.runtime === "workerd") { 9 | return runtime.env!.DB as D1Database; 10 | } 11 | 12 | // When running on node, simulate Cloudflare environment with "wrangler" 13 | const { getPlatformProxy } = await import("wrangler"); 14 | 15 | const { env } = await getPlatformProxy(); 16 | return env.DB as D1Database; 17 | } 18 | -------------------------------------------------------------------------------- /boilerplates/d1/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import { D1Database } from "@cloudflare/workers-types"; 2 | 3 | // Cloudflare typings 4 | interface Env { 5 | DB: D1Database; 6 | } 7 | 8 | declare global { 9 | namespace Vike { 10 | interface PageContext { 11 | env: Env; 12 | } 13 | } 14 | } 15 | 16 | export {}; 17 | -------------------------------------------------------------------------------- /boilerplates/d1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/drizzle/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | # Do not commit local database 5 | sqlite.db 6 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | if (props.meta.BATI.hasD1) return; 5 | const envContent = await props.readfile?.(); 6 | 7 | return appendToEnv(envContent, "DATABASE_URL", "sqlite.db", "Path to the sqlite database"); 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/$README.md.ts: -------------------------------------------------------------------------------- 1 | import { loadMarkdown, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getReadme(props: TransformerProps) { 4 | const content = await loadMarkdown(props); 5 | 6 | //language=Markdown 7 | const todo = ` 8 | ## *Drizzle* 9 | 10 | First, ensure that \`DATABASE_URL\` is configured in \`.env\` file, then create the database: 11 | \`\`\`bash 12 | pnpm drizzle:generate # a script that executes drizzle-kit generate. 13 | pnpm drizzle:migrate # a script that executes drizzle-kit migrate. 14 | \`\`\` 15 | 16 | > [!NOTE] 17 | > The \`drizzle-kit generate\` command is used to generate SQL migration files based on your Drizzle schema. 18 | > 19 | > The \`drizzle-kit migrate\` command is used to apply the generated migrations to your database. 20 | 21 | Read more on [Drizzle ORM documentation](https://orm.drizzle.team/docs/overview) 22 | `; 23 | 24 | content.addMarkdownFeature(todo, "drizzle"); 25 | 26 | return content; 27 | } 28 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("drizzle:generate", { 8 | value: "drizzle-kit generate", 9 | precedence: 20, 10 | }) 11 | .setScript("drizzle:migrate", { 12 | value: props.meta.BATI.hasD1 ? "wrangler d1 migrations apply YOUR_DATABASE_NAME --local" : "drizzle-kit migrate", 13 | precedence: 20, 14 | }) 15 | .setScript("drizzle:studio", { 16 | value: "drizzle-kit studio", 17 | precedence: 20, 18 | }) 19 | .addDependencies(["drizzle-kit", "drizzle-orm", "dotenv"]) 20 | .addDevDependencies(["@types/better-sqlite3"], !props.meta.BATI.hasD1) 21 | .addDependencies(["better-sqlite3"], !props.meta.BATI.hasD1); 22 | } 23 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/database/drizzle/db.ts: -------------------------------------------------------------------------------- 1 | import Database from "better-sqlite3"; 2 | import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3"; 3 | import { drizzle as drizzleD1 } from "drizzle-orm/d1"; 4 | import type { D1Database } from "@cloudflare/workers-types"; 5 | 6 | //# !BATI.hasD1 7 | export function dbSqlite() { 8 | const sqlite = new Database(process.env.DATABASE_URL); 9 | return drizzleSqlite(sqlite); 10 | } 11 | 12 | //# BATI.hasD1 13 | export function dbD1(d1: D1Database) { 14 | return drizzleD1(d1); 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/database/drizzle/queries/todos.ts: -------------------------------------------------------------------------------- 1 | /*# BATI include-if-imported #*/ 2 | import { todoTable } from "../schema/todos"; 3 | import { dbD1, type dbSqlite } from "../db"; 4 | 5 | export function insertTodo( 6 | db: BATI.If<{ 7 | "!BATI.hasD1": ReturnType; 8 | _: ReturnType; 9 | }>, 10 | text: string, 11 | ) { 12 | return db.insert(todoTable).values({ text }); 13 | } 14 | 15 | export function getAllTodos( 16 | db: BATI.If<{ 17 | "!BATI.hasD1": ReturnType; 18 | _: ReturnType; 19 | }>, 20 | ) { 21 | return db.select().from(todoTable).all(); 22 | } 23 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/database/drizzle/schema/todos.ts: -------------------------------------------------------------------------------- 1 | /*# BATI include-if-imported #*/ 2 | import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; 3 | 4 | // Example of defining a schema in Drizzle ORM: 5 | export const todoTable = sqliteTable("todos", { 6 | id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), 7 | text: text("text", { length: 50 }).notNull(), 8 | }); 9 | 10 | // You can then infer the types for selecting and inserting 11 | export type TodoItem = typeof todoTable.$inferSelect; 12 | export type TodoInsert = typeof todoTable.$inferInsert; 13 | -------------------------------------------------------------------------------- /boilerplates/drizzle/files/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { defineConfig } from "drizzle-kit"; 3 | 4 | if (!BATI.hasD1) { 5 | if (!process.env.DATABASE_URL) { 6 | throw new Error("Missing DATABASE_URL in .env file"); 7 | } 8 | } 9 | 10 | export default defineConfig({ 11 | dialect: "sqlite", 12 | schema: "./database/drizzle/schema/*", 13 | out: "./database/migrations", 14 | //# !BATI.hasD1 15 | dbCredentials: { 16 | url: process.env.DATABASE_URL!, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /boilerplates/drizzle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/eslint/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("lint", { 8 | value: "eslint .", 9 | precedence: 0, 10 | }) 11 | .addDevDependencies(["eslint", "@eslint/js", "typescript-eslint", "globals"]) 12 | .addDevDependencies(["eslint-plugin-prettier", "eslint-config-prettier"], props.meta.BATI.has("prettier")) 13 | .addDevDependencies(["eslint-plugin-vue", "vue-eslint-parser"], props.meta.BATI.has("vue")) 14 | .addDevDependencies(["eslint-plugin-react"], props.meta.BATI.has("react")) 15 | .addDevDependencies(["eslint-plugin-solid"], props.meta.BATI.has("solid")); 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/eslint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/express/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("dev", { 8 | value: "tsx ./express-entry.ts", 9 | precedence: 20, 10 | warnIfReplaced: true, 11 | }) 12 | .setScript("build", { 13 | value: "vike build", 14 | precedence: 1, 15 | warnIfReplaced: true, 16 | }) 17 | .setScript("preview", { 18 | value: "cross-env NODE_ENV=production tsx ./express-entry.ts", 19 | precedence: 20, 20 | }) 21 | .addDevDependencies(["@types/express"]) 22 | .addDependencies(["@universal-middleware/express", "express", "vite", "vike"]) 23 | .addDependencies(["dotenv"], props.meta.BATI.has("auth0") || props.meta.BATI.hasDatabase) 24 | .addDevDependencies(["tsx"], ["dev", "preview"]) 25 | .addDevDependencies(["cross-env"], ["preview"]); 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/express/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { UserRecord } from "firebase-admin/auth"; 2 | 3 | //# BATI.has("firebase-auth") 4 | declare module "express" { 5 | interface Request { 6 | user?: UserRecord | null; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /boilerplates/express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/fastify/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("dev", { 8 | value: "tsx ./fastify-entry.ts", 9 | precedence: 20, 10 | warnIfReplaced: true, 11 | }) 12 | .setScript("build", { 13 | value: "vike build", 14 | precedence: 1, 15 | warnIfReplaced: true, 16 | }) 17 | .setScript("preview", { 18 | value: "cross-env NODE_ENV=production tsx ./fastify-entry.ts", 19 | precedence: 20, 20 | }) 21 | .addDevDependencies(["@types/node"]) 22 | .addDependencies(["@fastify/middie", "@fastify/static", "@universal-middleware/fastify", "fastify", "vike", "vite"]) 23 | .addDependencies(["dotenv"], props.meta.BATI.has("auth0") || props.meta.BATI.hasDatabase) 24 | .addDevDependencies(["tsx"], ["dev", "preview"]) 25 | .addDevDependencies(["cross-env"], ["preview"]); 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/fastify/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { UserRecord } from "firebase-admin/auth"; 2 | 3 | //# BATI.has("firebase-auth") 4 | declare module "fastify" { 5 | interface FastifyRequest { 6 | user: UserRecord | null; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /boilerplates/fastify/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"], 5 | "lib": ["DOM"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | const envContent = await props.readfile?.(); 5 | 6 | return appendToEnv( 7 | envContent, 8 | "GOOGLE_APPLICATION_CREDENTIALS", 9 | "firebase/service-account.json", 10 | `Location of Your Firebase service account (use for firebase-admin). 11 | Download the file from https://console.firebase.google.com/u/0/project/{firebase-project-id}/settings/serviceaccounts/adminsdk`, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies([ 7 | "firebase", 8 | "firebase-admin", 9 | "firebaseui", 10 | "cookie", 11 | "@universal-middleware/core", 12 | "dotenv", 13 | ]); 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/firebase/$service-account.json.ts: -------------------------------------------------------------------------------- 1 | export default async function getPackageJson() { 2 | const firebaseAccountStringified = process.env.TEST_FIREBASE_ACCOUNT; 3 | 4 | return firebaseAccountStringified 5 | ? JSON.parse(firebaseAccountStringified) 6 | : { 7 | type: "", 8 | project_id: "", 9 | private_key_id: "", 10 | private_key: "", 11 | client_email: "", 12 | client_id: "", 13 | auth_uri: "", 14 | token_uri: "", 15 | auth_provider_x509_cert_url: "", 16 | client_x509_cert_url: "", 17 | universe_domain: "", 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { UserRecord } from "firebase-admin/auth"; 2 | 3 | declare global { 4 | namespace Vike { 5 | interface PageContext { 6 | user?: UserRecord | null; 7 | } 8 | } 9 | } 10 | 11 | export {}; 12 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/libs/firebaseAdmin.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { applicationDefault, getApp, getApps, initializeApp, type App } from "firebase-admin/app"; 3 | import { getAuth } from "firebase-admin/auth"; 4 | 5 | let firebaseAdmin: App | undefined; 6 | 7 | if (!getApps().length) { 8 | firebaseAdmin = initializeApp({ 9 | credential: applicationDefault(), 10 | }); 11 | } else { 12 | firebaseAdmin = getApp(); 13 | } 14 | 15 | export { firebaseAdmin, getAuth }; 16 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/pages/+client.ts: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { deleteApp, getApp, getApps, initializeApp, type FirebaseApp } from "firebase/app"; 3 | import { getAuth, inMemoryPersistence } from "firebase/auth"; 4 | 5 | // TODO: Fill your web app's Firebase configuration 6 | // See https://firebase.google.com/docs/web/learn-more?hl=fr#config-object 7 | const firebaseConfig = { 8 | apiKey: "", 9 | authDomain: "", 10 | projectId: "", 11 | storageBucket: "", 12 | messagingSenderId: "", 13 | appId: "", 14 | }; 15 | 16 | let firebaseApp: FirebaseApp | undefined; 17 | // create a singleton client side firebaseApp 18 | if (!getApps().length) { 19 | firebaseApp = initializeApp(firebaseConfig); 20 | } else { 21 | firebaseApp = getApp(); 22 | deleteApp(firebaseApp); 23 | firebaseApp = initializeApp(firebaseConfig); 24 | } 25 | 26 | const auth = getAuth(firebaseApp); 27 | 28 | // As httpOnly cookies are to be used, do not persist any state client side. 29 | // `inMemoryPersistence` is an implementation of Persistence of type 'NONE'. 30 | auth.setPersistence(inMemoryPersistence); 31 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/pages/login/+config.ts: -------------------------------------------------------------------------------- 1 | const config = { 2 | // firebase-ui only supports client-side rendering 3 | ssr: false, 4 | }; 5 | 6 | export default config; 7 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/files/pages/login/+guard.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/guard 2 | import { redirect } from "vike/abort"; 3 | import type { GuardAsync } from "vike/types"; 4 | 5 | const guard: GuardAsync = async (pageContext): ReturnType => { 6 | if (pageContext.user) { 7 | throw redirect("/"); 8 | } 9 | }; 10 | 11 | export { guard }; 12 | -------------------------------------------------------------------------------- /boilerplates/firebase-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/google-analytics/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | const envContent = await props.readfile?.(); 5 | 6 | return appendToEnv( 7 | envContent, 8 | "PUBLIC_ENV__GOOGLE_ANALYTICS", 9 | "G-XXXXXXXXXX", 10 | `Google Analytics 11 | 12 | See the documentation https://support.google.com/analytics/answer/9304153?hl=en#zippy=%2Cweb`, 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/google-analytics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/google-analytics", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103" 17 | }, 18 | "dependencies": { 19 | "@batijs/core": "workspace:*" 20 | }, 21 | "files": [ 22 | "dist/" 23 | ], 24 | "bati": { 25 | "if": { 26 | "flag": "google-analytics" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /boilerplates/google-analytics/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/h3/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { UserRecord } from "firebase-admin/auth"; 2 | 3 | //# BATI.has("firebase-auth") 4 | declare module "h3" { 5 | interface H3EventContext { 6 | user: UserRecord | null; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /boilerplates/h3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/hattip/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types", "@types/aws-lambda"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/hono/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("dev", { 8 | value: "vike dev", 9 | precedence: 20, 10 | warnIfReplaced: true, 11 | }) 12 | .setScript("build", { 13 | value: "vike build", 14 | precedence: 1, 15 | warnIfReplaced: true, 16 | }) 17 | .setScript("preview", { 18 | value: "cross-env NODE_ENV=production tsx ./hono-entry.node.ts", 19 | precedence: 20, 20 | }) 21 | .addDevDependencies(["@hono/vite-dev-server", "@types/node"]) 22 | .addDevDependencies(["@types/aws-lambda"], props.meta.BATI.has("aws")) 23 | .addDependencies(["@hono/node-server", "@universal-middleware/hono", "hono", "vite", "vike"]) 24 | .addDependencies(["dotenv"], props.meta.BATI.has("auth0") || props.meta.BATI.hasDatabase) 25 | .addDevDependencies(["tsx", "cross-env"], ["preview"]); 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/hono/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "@hono/vite-dev-server", 8 | constructor: "devServer", 9 | options: { 10 | entry: "hono-entry.ts", 11 | exclude: [ 12 | /^\/@.+$/, 13 | /.*\.(ts|tsx|vue)($|\?)/, 14 | /.*\.(s?css|less)($|\?)/, 15 | /^\/favicon\.ico$/, 16 | /.*\.(svg|png)($|\?)/, 17 | /^\/(public|assets|static)\/.+/, 18 | /^\/node_modules\/.*/, 19 | ], 20 | injectClientScript: false, 21 | }, 22 | }); 23 | 24 | return mod.generate().code; 25 | } 26 | -------------------------------------------------------------------------------- /boilerplates/hono/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { UserRecord } from "firebase-admin/auth"; 2 | 3 | //# BATI.has("firebase-auth") 4 | declare module "hono" { 5 | interface ContextVariableMap { 6 | user?: UserRecord | null; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /boilerplates/hono/files/hono-entry.node.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "@hono/node-server"; 2 | import { serveStatic } from "@hono/node-server/serve-static"; 3 | import { type Context, Hono } from "hono"; 4 | import { env } from "hono/adapter"; 5 | import { compress } from "hono/compress"; 6 | import app from "./hono-entry.js"; 7 | 8 | const envs = env<{ NODE_ENV?: string; PORT?: string }>({ env: {} } as unknown as Context<{ 9 | Bindings: { NODE_ENV?: string; PORT?: string }; 10 | }>); 11 | 12 | const nodeApp = new Hono(); 13 | 14 | nodeApp.use(compress()); 15 | 16 | nodeApp.use( 17 | "/*", 18 | serveStatic({ 19 | root: `./dist/client/`, 20 | }), 21 | ); 22 | 23 | nodeApp.route("/", app!); 24 | 25 | const port = envs.PORT ? parseInt(envs.PORT, 10) : 3000; 26 | 27 | console.log(`Server listening on http://localhost:${port}`); 28 | serve({ 29 | fetch: nodeApp.fetch, 30 | port: port, 31 | }); 32 | -------------------------------------------------------------------------------- /boilerplates/hono/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types", "@types/aws-lambda"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/lucia-auth/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | const githubClientId = process.env.TEST_GITHUB_CLIENT_ID; 5 | const githubClientSecret = process.env.TEST_GITHUB_CLIENT_SECRET; 6 | 7 | let envContent = await props.readfile?.(); 8 | 9 | envContent = appendToEnv( 10 | envContent, 11 | "GITHUB_CLIENT_ID", 12 | githubClientId ?? "", 13 | "GitHub Client ID. Used for authentication", 14 | ); 15 | envContent = appendToEnv( 16 | envContent, 17 | "GITHUB_CLIENT_SECRET", 18 | githubClientSecret ?? "", 19 | "GitHub Client Secret. Used for authentication", 20 | ); 21 | 22 | return envContent; 23 | } 24 | -------------------------------------------------------------------------------- /boilerplates/lucia-auth/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .addDependencies(["arctic", "cookie", "lucia", "@universal-middleware/core", "dotenv"]) 8 | .addDependencies(["@lucia-auth/adapter-drizzle"], props.meta.BATI.has("drizzle")) 9 | .addDependencies(["@lucia-auth/adapter-sqlite"], !props.meta.BATI.has("drizzle")) 10 | .addDevDependencies(["@types/better-sqlite3"], !props.meta.BATI.hasD1) 11 | .addDependencies(["better-sqlite3"], !props.meta.BATI.hasD1); 12 | } 13 | -------------------------------------------------------------------------------- /boilerplates/lucia-auth/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { User } from "lucia"; 2 | 3 | declare global { 4 | namespace Vike { 5 | interface PageContext { 6 | user?: User; 7 | } 8 | } 9 | } 10 | 11 | export {}; 12 | -------------------------------------------------------------------------------- /boilerplates/lucia-auth/files/pages/login/+guard.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/guard 2 | import { redirect } from "vike/abort"; 3 | import type { GuardAsync } from "vike/types"; 4 | 5 | const guard: GuardAsync = async (pageContext): ReturnType => { 6 | if (pageContext.user) { 7 | throw redirect("/"); 8 | } 9 | }; 10 | 11 | export { guard }; 12 | -------------------------------------------------------------------------------- /boilerplates/lucia-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "lib": ["DOM"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/$README.md.ts: -------------------------------------------------------------------------------- 1 | import { loadMarkdown, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getReadme(props: TransformerProps) { 4 | const content = await loadMarkdown(props); 5 | 6 | //language=Markdown 7 | const todo = ` 8 | ## Mantine 9 | 10 | This is a boilerplate for Mantine based on the [Getting Started](https://mantine.dev/docs/getting-started/) guide. 11 | 12 | The following Packages are installed: 13 | * \`@mantine/hooks\` Hooks for state and UI management 14 | * \`@mantine/core\` Core components library: inputs, buttons, overlays, etc. 15 | 16 | If you add more packages, make sure to update the \`layouts/LayoutDefault.tsx\` file to include the required CSSs. 17 | 18 | The theme is defined in \`layouts/theme.ts\`. 19 | `; 20 | 21 | content.addMarkdownFeature(todo, "mantine"); 22 | 23 | return content; 24 | } 25 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .addDevDependencies(["postcss", "postcss-preset-mantine", "postcss-simple-vars"]) 8 | .addDependencies(["@mantine/core", "@mantine/hooks"]); 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cssVariables.lookupFiles": [ 3 | "**/*.css", 4 | "**/*.scss", 5 | "**/*.sass", 6 | "**/*.less", 7 | "node_modules/@mantine/core/styles.css" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import { usePageContext } from "vike-react/usePageContext"; 2 | import { NavLink } from "@mantine/core"; 3 | 4 | export function Link({ href, label }: { href: string; label: string }) { 5 | const pageContext = usePageContext(); 6 | const { urlPathname } = pageContext; 7 | const isActive = href === "/" ? urlPathname === href : urlPathname.startsWith(href); 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/layouts/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vikejs/bati/d7eb17febeeb7ce3893a2c8c9349f90e057f268f/boilerplates/mantine/files/layouts/style.css -------------------------------------------------------------------------------- /boilerplates/mantine/files/layouts/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mantine/core"; 2 | import type { MantineThemeOverride } from "@mantine/core"; 3 | 4 | const theme: MantineThemeOverride = createTheme({ 5 | /** Put your mantine theme override here */ 6 | primaryColor: "violet", 7 | }); 8 | 9 | export default theme; 10 | -------------------------------------------------------------------------------- /boilerplates/mantine/files/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-preset-mantine": {}, 4 | "postcss-simple-vars": { 5 | variables: { 6 | "mantine-breakpoint-xs": "36em", 7 | "mantine-breakpoint-sm": "48em", 8 | "mantine-breakpoint-md": "62em", 9 | "mantine-breakpoint-lg": "75em", 10 | "mantine-breakpoint-xl": "88em", 11 | }, 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /boilerplates/mantine/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "vike-react", "@batijs/core/types", "vite-plugin-compiled-react"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "react", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 8 | "baseUrl": "." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/panda-css/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("prepare", { 8 | value: "panda codegen", 9 | precedence: 20, 10 | }) 11 | .addDevDependencies(["@pandacss/dev", "postcss"]) 12 | .addDependencies([]); 13 | } 14 | -------------------------------------------------------------------------------- /boilerplates/panda-css/files/layouts/panda.css: -------------------------------------------------------------------------------- 1 | @layer reset, base, tokens, recipes, utilities; 2 | -------------------------------------------------------------------------------- /boilerplates/panda-css/files/panda.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@pandacss/dev"; 2 | 3 | export default defineConfig({ 4 | // Whether to use css reset 5 | preflight: true, 6 | 7 | // Where to look for your css declarations 8 | include: BATI.has("vue") 9 | ? [ 10 | "./components/**/*.{js,jsx,ts,tsx,vue}", 11 | "./layouts/**/*.{js,jsx,ts,tsx,vue}", 12 | "./pages/**/*.{js,jsx,ts,tsx,vue}", 13 | "./src/**/*.{js,jsx,ts,tsx,vue}", 14 | ] 15 | : [ 16 | "./components/**/*.{js,jsx,ts,tsx}", 17 | "./layouts/**/*.{js,jsx,ts,tsx}", 18 | "./pages/**/*.{js,jsx,ts,tsx}", 19 | "./src/**/*.{js,jsx,ts,tsx}", 20 | ], 21 | 22 | // Files to exclude 23 | exclude: [], 24 | 25 | // Useful for theme customization 26 | theme: { 27 | extend: {}, 28 | }, 29 | 30 | // The output directory for your css system 31 | outdir: "styled-system", 32 | }); 33 | -------------------------------------------------------------------------------- /boilerplates/panda-css/files/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@pandacss/dev/postcss": {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /boilerplates/panda-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/panda-css", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@pandacss/dev": "^0.53.7", 17 | "@types/node": "^18.19.103", 18 | "postcss": "^8.5.3" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*" 22 | }, 23 | "files": [ 24 | "dist/" 25 | ], 26 | "bati": { 27 | "if": { 28 | "flag": "panda-css" 29 | } 30 | }, 31 | "exports": { 32 | "./panda.config": { 33 | "types": "./dist/types/panda.config.d.ts" 34 | } 35 | }, 36 | "typesVersions": { 37 | "*": { 38 | "panda.config": [ 39 | "./dist/types/panda.config.d.ts" 40 | ] 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /boilerplates/panda-css/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/pnpm/files/$pnpm-workspace.yaml.ts: -------------------------------------------------------------------------------- 1 | import { loadYaml, type TransformerProps } from "@batijs/core"; 2 | 3 | const dependenciesApproveBuild = [ 4 | "@biomejs/biome", 5 | "@sentry/cli", 6 | "@tailwindcss/oxide", 7 | "better-sqlite3", 8 | "esbuild", 9 | "sharp", 10 | "workerd", 11 | ]; 12 | 13 | function intersect(a: T[], b: T[]) { 14 | return a.filter((x) => b.includes(x)); 15 | } 16 | 17 | export default async function getPnpmWorkspace(props: TransformerProps) { 18 | const deps = [ 19 | ...Object.keys(props.packageJson.dependencies ?? {}), 20 | ...Object.keys(props.packageJson.devDependencies ?? {}), 21 | ]; 22 | const intersection = intersect(dependenciesApproveBuild, deps); 23 | if (intersection.length === 0) return; 24 | 25 | const pnpmWorkspace = await loadYaml(props, { fallbackEmpty: true }); 26 | 27 | if (!pnpmWorkspace.has("onlyBuiltDependencies")) { 28 | pnpmWorkspace.set("onlyBuiltDependencies", []); 29 | } 30 | 31 | for (const pack of intersection) { 32 | pnpmWorkspace.addIn(["onlyBuiltDependencies"], pack); 33 | } 34 | 35 | return pnpmWorkspace; 36 | } 37 | -------------------------------------------------------------------------------- /boilerplates/pnpm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/pnpm", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103" 17 | }, 18 | "dependencies": { 19 | "@batijs/core": "workspace:*" 20 | }, 21 | "files": [ 22 | "dist/" 23 | ], 24 | "bati": { 25 | "if": { 26 | "packageManager": "pnpm" 27 | }, 28 | "enforce": "post" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /boilerplates/pnpm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/prettier/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDevDependencies(["prettier"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/prettier/files/.prettierignore: -------------------------------------------------------------------------------- 1 | ../../shared/files/.gitignore -------------------------------------------------------------------------------- /boilerplates/prettier/files/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/prettier", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103", 17 | "prettier": "^3.5.3" 18 | }, 19 | "dependencies": { 20 | "@batijs/core": "workspace:*" 21 | }, 22 | "files": [ 23 | "dist/" 24 | ], 25 | "bati": { 26 | "if": { 27 | "flag": "prettier" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /boilerplates/prettier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/prisma/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | # Do not commit local database 5 | sqlite.db 6 | -------------------------------------------------------------------------------- /boilerplates/prisma/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | const envContent = await props.readfile?.(); 5 | 6 | return appendToEnv( 7 | envContent, 8 | "DATABASE_URL", 9 | "postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public", 10 | `Prisma 11 | 12 | Environment variables declared in this file are automatically made available to Prisma. 13 | See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 14 | 15 | Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 16 | See the documentation for all the connection string options: https://pris.ly/d/connection-strings`, 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /boilerplates/prisma/files/$README.md.ts: -------------------------------------------------------------------------------- 1 | import { loadMarkdown, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getReadme(props: TransformerProps) { 4 | const content = await loadMarkdown(props); 5 | 6 | //language=Markdown 7 | const todo = ` 8 | ## *Prisma* 9 | ### Setup 10 | Run the following command once: 11 | \`\`\`sh 12 | pnpx prisma init 13 | \`\`\` 14 | 15 | then follow instructions at `; 16 | 17 | content.addMarkdownFeature(todo, "prisma", { 18 | position: "before", 19 | }); 20 | 21 | return content; 22 | } 23 | -------------------------------------------------------------------------------- /boilerplates/prisma/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("prisma:studio", { 8 | value: "prisma studio", 9 | precedence: 1, 10 | }) 11 | .setScript("prisma:generate", { 12 | value: "prisma generate", 13 | precedence: 1, 14 | }) 15 | .addDevDependencies(["prisma"]) 16 | .addDependencies(["@prisma/client"]); 17 | } 18 | -------------------------------------------------------------------------------- /boilerplates/prisma/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/prisma", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@prisma/client": "^6.8.2", 17 | "@types/node": "^18.19.103", 18 | "colorette": "^2.0.20", 19 | "prisma": "^6.8.2" 20 | }, 21 | "dependencies": { 22 | "@batijs/core": "workspace:*" 23 | }, 24 | "files": [ 25 | "dist/" 26 | ], 27 | "bati": { 28 | "if": { 29 | "flag": "prisma" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/prisma/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/react-firebase-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "react", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/react-lucia-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "react", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/react-sentry/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@sentry/react"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-sentry/files/sentry.browser.config.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/react"; 2 | 3 | export const sentryBrowserConfig = () => { 4 | // eslint-disable-next-line 5 | import.meta.env.PROD === true && 6 | Sentry.init({ 7 | dsn: import.meta.env.PUBLIC_ENV__SENTRY_DSN, 8 | environment: "production-frontend", 9 | //enabled: import.meta.env.DEV ? false : true, 10 | integrations: [Sentry.replayIntegration()], 11 | // Set tracesSampleRate to 1.0 to capture 100% 12 | // of transactions for tracing. 13 | tracesSampleRate: 1.0, 14 | // Set `tracePropagationTargets` to control for which URLs trace propagation should be enabled 15 | tracePropagationTargets: [/^\//, /^https:\/\/yourserver\.io\/api/], 16 | // Capture Replay for 10% of all sessions, 17 | // plus for 100% of sessions with an error 18 | replaysSessionSampleRate: 0.1, 19 | replaysOnErrorSampleRate: 1.0, 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /boilerplates/react-sentry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@batijs/core/types", "vite/client"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "react", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/react/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .addDevDependencies(["vite", "@types/react", "@types/react-dom"]) 8 | .addDependencies(["@vitejs/plugin-react", "react", "react-dom", "vike", "vike-react"]); 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/react/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.jsx = "react-jsx"; 7 | tsConfig.compilerOptions.jsxImportSource = "react"; 8 | tsConfig.compilerOptions.types = [...(tsConfig.compilerOptions.types ?? []), "vike-react"]; 9 | 10 | return tsConfig; 11 | } 12 | -------------------------------------------------------------------------------- /boilerplates/react/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | // See https://github.com/vikejs/bati/pull/124 7 | const reactOptions = props.meta.BATI.has("vercel") && props.meta.BATI.has("hattip") ? { jsxRuntime: "classic" } : {}; 8 | 9 | addVitePlugin(mod, { 10 | from: "@vitejs/plugin-react", 11 | constructor: "react", 12 | options: reactOptions, 13 | }); 14 | 15 | return mod.generate().code; 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/react/files/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import { usePageContext } from "vike-react/usePageContext"; 2 | 3 | export function Link({ href, children }: { href: string; children: string }) { 4 | const pageContext = usePageContext(); 5 | const { urlPathname } = pageContext; 6 | const isActive = href === "/" ? urlPathname === href : urlPathname.startsWith(href); 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /boilerplates/react/files/layouts/style.css: -------------------------------------------------------------------------------- 1 | /* Links */ 2 | a { 3 | text-decoration: none; 4 | } 5 | #sidebar a { 6 | padding: 2px 10px; 7 | margin-left: -10px; 8 | } 9 | #sidebar a.is-active { 10 | background-color: #eee; 11 | } 12 | 13 | /* Reset */ 14 | body { 15 | margin: 0; 16 | font-family: sans-serif; 17 | } 18 | * { 19 | box-sizing: border-box; 20 | } 21 | 22 | /* Page Transition Animation */ 23 | #page-content { 24 | opacity: 1; 25 | transition: opacity 0.3s ease-in-out; 26 | } 27 | body.page-is-transitioning #page-content { 28 | opacity: 0; 29 | } 30 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/+config.ts: -------------------------------------------------------------------------------- 1 | import vikeReact from "vike-react/config"; 2 | import type { Config } from "vike/types"; 3 | import Layout from "../layouts/LayoutDefault.js"; 4 | 5 | // Default config (can be overridden by pages) 6 | // https://vike.dev/config 7 | 8 | export default { 9 | // https://vike.dev/Layout 10 | Layout, 11 | 12 | // https://vike.dev/head-tags 13 | title: "My Vike App", 14 | description: "Demo showcasing Vike", 15 | 16 | //# BATI.has("auth0") || BATI.has("firebase-auth") || BATI.has("authjs") || BATI.has("lucia-auth") 17 | passToClient: ["user"], 18 | extends: vikeReact, 19 | } satisfies Config; 20 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/+onPageTransitionEnd.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionEndAsync } from "vike/types"; 2 | 3 | export const onPageTransitionEnd: OnPageTransitionEndAsync = async () => { 4 | console.log("Page transition end"); 5 | document.querySelector("body")?.classList.remove("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/+onPageTransitionStart.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionStartAsync } from "vike/types"; 2 | 3 | export const onPageTransitionStart: OnPageTransitionStartAsync = async () => { 4 | console.log("Page transition start"); 5 | document.querySelector("body")?.classList.add("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/_error/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { usePageContext } from "vike-react/usePageContext"; 2 | 3 | export default function Page() { 4 | const { is404 } = usePageContext(); 5 | if (is404) { 6 | return ( 7 | <> 8 |

404 Page Not Found

9 |

This page could not be found.

10 | 11 | ); 12 | } 13 | return ( 14 | <> 15 |

500 Internal Server Error

16 |

Something went wrong.

17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/index/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { Counter } from "./Counter.js"; 2 | import { css } from "../../styled-system/css"; 3 | 4 | export default function Page() { 5 | return ( 6 | <> 7 |

17 | My Vike app 18 |

19 | This page is: 20 |
    21 |
  • Rendered to HTML.
  • 22 |
  • 23 | Interactive. 24 |
  • 25 |
26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/star-wars/@id/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { useData } from "vike-react/useData"; 2 | import type { Data } from "./+data.js"; 3 | 4 | export default function Page() { 5 | const movie = useData(); 6 | return ( 7 | <> 8 |

{movie.title}

9 | Release Date: {movie.release_date} 10 |
11 | Director: {movie.director} 12 |
13 | Producer: {movie.producer} 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/star-wars/@id/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { PageContextServer } from "vike/types"; 4 | import type { MovieDetails } from "../types.js"; 5 | import { useConfig } from "vike-react/useConfig"; 6 | 7 | export type Data = Awaited>; 8 | 9 | export const data = async (pageContext: PageContextServer) => { 10 | // https://vike.dev/useConfig 11 | const config = useConfig(); 12 | 13 | const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`); 14 | let movie = (await response.json()) as MovieDetails; 15 | 16 | config({ 17 | // Set 18 | title: movie.title, 19 | }); 20 | 21 | // We remove data we don't need because the data is passed to 22 | // the client; we should minimize what is sent over the network. 23 | movie = minimize(movie); 24 | 25 | return movie; 26 | }; 27 | 28 | function minimize(movie: MovieDetails): MovieDetails { 29 | const { id, title, release_date, director, producer } = movie; 30 | const minimizedMovie = { id, title, release_date, director, producer }; 31 | return minimizedMovie; 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/star-wars/index/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { useData } from "vike-react/useData"; 2 | import type { Data } from "./+data.js"; 3 | 4 | export default function Page() { 5 | const movies = useData<Data>(); 6 | return ( 7 | <> 8 | <h1>Star Wars Movies</h1> 9 | <ol> 10 | {movies.map(({ id, title, release_date }) => ( 11 | <li key={id}> 12 | <a href={`/star-wars/${id}`}>{title}</a> ({release_date}) 13 | </li> 14 | ))} 15 | </ol> 16 | <p> 17 | Source: <a href="https://brillout.github.io/star-wars">brillout.github.io/star-wars</a>. 18 | </p> 19 | </> 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/star-wars/index/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { Movie, MovieDetails } from "../types.js"; 4 | import { useConfig } from "vike-react/useConfig"; 5 | 6 | export type Data = Awaited<ReturnType<typeof data>>; 7 | 8 | export const data = async () => { 9 | // https://vike.dev/useConfig 10 | const config = useConfig(); 11 | 12 | const response = await fetch("https://brillout.github.io/star-wars/api/films.json"); 13 | const moviesData = (await response.json()) as MovieDetails[]; 14 | 15 | config({ 16 | // Set <title> 17 | title: `${moviesData.length} Star Wars Movies`, 18 | }); 19 | 20 | // We remove data we don't need because the data is passed to the client; we should 21 | // minimize what is sent over the network. 22 | const movies = minimize(moviesData); 23 | 24 | return movies; 25 | }; 26 | 27 | function minimize(movies: MovieDetails[]): Movie[] { 28 | return movies.map((movie) => { 29 | const { title, release_date, id } = movie; 30 | return { title, release_date, id }; 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/star-wars/types.ts: -------------------------------------------------------------------------------- 1 | export type Movie = { 2 | id: string; 3 | title: string; 4 | release_date: string; 5 | }; 6 | 7 | export type MovieDetails = Movie & { 8 | director: string; 9 | producer: string; 10 | }; 11 | -------------------------------------------------------------------------------- /boilerplates/react/files/pages/todo/+Page.tsx: -------------------------------------------------------------------------------- 1 | import type { Data } from "@batijs/shared-todo/pages/todo/+data"; 2 | import { useData } from "vike-react/useData"; 3 | import { TodoList } from "./TodoList.js"; 4 | 5 | export default function Page() { 6 | const data = useData<Data>(); 7 | return ( 8 | <> 9 | <h1>To-do List</h1> 10 | <TodoList initialTodoItems={data.todo} /> 11 | </> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /boilerplates/react/panda.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@pandacss/dev"; 2 | 3 | export default defineConfig({ 4 | outdir: "files/styled-system", 5 | }); 6 | -------------------------------------------------------------------------------- /boilerplates/react/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./files/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /boilerplates/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "vike-react", "@batijs/core/types", "vite-plugin-compiled-react"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "react", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 8 | "baseUrl": "." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/sentry/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getServerlessEnv(props: TransformerProps) { 4 | const sentryDNS = process.env.TEST_SENTRY_DSN; 5 | 6 | let envContent = await props.readfile?.(); 7 | 8 | envContent = appendToEnv( 9 | envContent, 10 | "SENTRY_DSN", 11 | sentryDNS ?? "", 12 | "Sentry DNS. Used for Error Reporting on the Server", 13 | ); 14 | envContent = appendToEnv( 15 | envContent, 16 | "PUBLIC_ENV__SENTRY_DSN", 17 | "", 18 | "Sentry DNS. Used for Error Reporting in the Browser", 19 | ); 20 | 21 | return envContent; 22 | } 23 | -------------------------------------------------------------------------------- /boilerplates/sentry/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDevDependencies(["@sentry/vite-plugin"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/sentry/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, deepMergeObject, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "@sentry/vite-plugin", 8 | constructor: "sentryVitePlugin", 9 | imported: "sentryVitePlugin", 10 | options: { 11 | sourcemaps: { disable: false }, 12 | }, 13 | }); 14 | 15 | // activate sourcemaps 16 | //@ts-ignore 17 | deepMergeObject(mod.exports.default.$args[0], { build: { sourcemap: true } }); 18 | 19 | return mod.generate().code; 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/sentry/files/.env.sentry-build-plugin: -------------------------------------------------------------------------------- 1 | # Sentry Organization Slug. Used for Upload of Source Maps 2 | SENTRY_ORG= 3 | 4 | # Sentry Project Slug. Used for Upload of Source Maps 5 | SENTRY_PROJECT= 6 | 7 | # Sentry Auth Token. Used for Upload of Source Maps 8 | SENTRY_AUTH_TOKEN= 9 | -------------------------------------------------------------------------------- /boilerplates/sentry/files/pages/$+client.ts.ts: -------------------------------------------------------------------------------- 1 | import { builders, generateCode, loadAsMagicast, parseModule, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | let mod; 5 | try { 6 | mod = await loadAsMagicast(props); 7 | } catch { 8 | // create an empty module if the file is empty 9 | mod = parseModule(""); 10 | } 11 | 12 | if (!props.meta.BATI.has("vue")) { 13 | // add `import { sentryBrowserConfig } from "../sentry.browser.config";` to the top of the file 14 | mod.imports.$prepend({ 15 | from: "../sentry.browser.config", 16 | imported: "sentryBrowserConfig", 17 | }); 18 | 19 | // add `sentryBrowserConfig()` function call to initialize Sentry to the end of the file 20 | const e = builders.functionCall("sentryBrowserConfig"); 21 | const c = generateCode(e).code; 22 | //@ts-ignore 23 | mod.$ast.body.splice(mod.$ast.body.length - 1, 0, c); 24 | } 25 | return mod.generate().code; 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/sentry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/sentry", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@sentry/vite-plugin": "^3.5.0", 17 | "@types/node": "^18.19.103", 18 | "vite": "^6.3.5" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*" 22 | }, 23 | "files": [ 24 | "dist/" 25 | ], 26 | "bati": { 27 | "if": { 28 | "flag": "sentry" 29 | } 30 | }, 31 | "exports": { 32 | "./sentry.browser.config copy": { 33 | "types": "./dist/types/sentry.browser.config copy.d.ts" 34 | } 35 | }, 36 | "typesVersions": { 37 | "*": { 38 | "sentry.browser.config copy": [ 39 | "./dist/types/sentry.browser.config copy.d.ts" 40 | ] 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /boilerplates/sentry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types", "vite/client"], 5 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/shadcn-ui/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("shadcn", { 8 | value: "npx shadcn@latest", 9 | precedence: 1, 10 | warnIfReplaced: true, 11 | }) 12 | .addDependencies([ 13 | "class-variance-authority", 14 | "clsx", 15 | "tailwind-merge", 16 | "lucide-react", 17 | "@radix-ui/react-icons", 18 | "tw-animate-css", 19 | ]); 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/shadcn-ui/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.paths = { 7 | ...(tsConfig.compilerOptions?.paths ?? {}), 8 | "@/*": ["./*"], 9 | }; 10 | 11 | return tsConfig; 12 | } 13 | -------------------------------------------------------------------------------- /boilerplates/shadcn-ui/files/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "tailwind": { 5 | "config": "tailwind.config.ts", 6 | "css": "layouts/tailwind.css", 7 | "baseColor": "gray", 8 | "cssVariables": true 9 | }, 10 | "rsc": true, 11 | "tsx": true, 12 | "aliases": { 13 | "utils": "@/lib/utils", 14 | "components": "@/components" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/shadcn-ui/files/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/shadcn-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/shared-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/shared-db", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@cloudflare/workers-types": "^4.20250525.0", 17 | "@types/node": "^18.19.103", 18 | "@universal-middleware/core": "^0.4.7" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*" 22 | }, 23 | "files": [ 24 | "dist/" 25 | ], 26 | "bati": { 27 | "if": { 28 | "flag": { 29 | "$in": [ 30 | "drizzle", 31 | "sqlite", 32 | "prisma" 33 | ] 34 | } 35 | } 36 | }, 37 | "exports": { 38 | "./server/db-middleware": { 39 | "types": "./dist/types/server/db-middleware.d.ts" 40 | } 41 | }, 42 | "typesVersions": { 43 | "*": { 44 | "server/db-middleware": [ 45 | "./dist/types/server/db-middleware.d.ts" 46 | ] 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /boilerplates/shared-db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/shared-no-db/files/database/todoItems.ts: -------------------------------------------------------------------------------- 1 | interface TodoItem { 2 | text: string; 3 | } 4 | 5 | const todosDefault = [{ text: "Buy milk" }, { text: "Buy strawberries" }]; 6 | 7 | const database = 8 | // We create an in-memory database. 9 | // - We use globalThis so that the database isn't reset upon HMR. 10 | // - The database is reset when restarting the server, use a proper database (SQLite/PostgreSQL/...) if you want persistent data. 11 | // biome-ignore lint: 12 | ((globalThis as unknown as { __database: { todos: TodoItem[] } }).__database ??= { todos: todosDefault }); 13 | 14 | const { todos } = database; 15 | 16 | export { todos }; 17 | export type { TodoItem }; 18 | -------------------------------------------------------------------------------- /boilerplates/shared-no-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/shared-no-db", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103" 17 | }, 18 | "dependencies": { 19 | "@batijs/core": "workspace:*" 20 | }, 21 | "files": [ 22 | "dist/" 23 | ], 24 | "bati": { 25 | "if": { 26 | "flag": { 27 | "$not": { 28 | "$in": [ 29 | "drizzle", 30 | "sqlite" 31 | ] 32 | } 33 | } 34 | } 35 | }, 36 | "exports": { 37 | "./database/todoItems": { 38 | "types": "./dist/types/database/todoItems.d.ts" 39 | } 40 | }, 41 | "typesVersions": { 42 | "*": { 43 | "database/todoItems": [ 44 | "./dist/types/database/todoItems.d.ts" 45 | ] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /boilerplates/shared-no-db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/shared-server/bati.d.ts: -------------------------------------------------------------------------------- 1 | import { D1Database } from "@cloudflare/workers-types"; 2 | 3 | // Cloudflare typings 4 | interface Env { 5 | DB: D1Database; 6 | } 7 | 8 | declare global { 9 | namespace Vike { 10 | interface PageContext { 11 | env: Env; 12 | } 13 | } 14 | } 15 | 16 | export {}; 17 | -------------------------------------------------------------------------------- /boilerplates/shared-server/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@universal-middleware/core"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/shared-server/files/server/vike-handler.ts: -------------------------------------------------------------------------------- 1 | /*# BATI include-if-imported #*/ 2 | /// <reference lib="webworker" /> 3 | import { renderPage } from "vike/server"; 4 | // TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.) 5 | import type { Get, UniversalHandler } from "@universal-middleware/core"; 6 | 7 | export const vikeHandler: Get<[], UniversalHandler> = () => async (request, context, runtime) => { 8 | const pageContextInit = { ...context, ...runtime, urlOriginal: request.url, headersOriginal: request.headers }; 9 | const pageContext = await renderPage(pageContextInit); 10 | const response = pageContext.httpResponse; 11 | 12 | const { readable, writable } = new TransformStream(); 13 | response.pipe(writable); 14 | 15 | return new Response(readable, { 16 | status: response.statusCode, 17 | headers: response.headers, 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /boilerplates/shared-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/shared-todo/bati.d.ts: -------------------------------------------------------------------------------- 1 | import { D1Database } from "@cloudflare/workers-types"; 2 | 3 | // Cloudflare typings 4 | interface Env { 5 | DB: D1Database; 6 | } 7 | 8 | declare global { 9 | namespace Vike { 10 | interface PageContext { 11 | env: Env; 12 | } 13 | } 14 | } 15 | 16 | export {}; 17 | -------------------------------------------------------------------------------- /boilerplates/shared-todo/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import { dbD1, dbSqlite } from "@batijs/drizzle/database/drizzle/db"; 2 | import { db as sqliteDb } from "@batijs/sqlite/database/sqlite/db"; 3 | import { D1Database } from "@cloudflare/workers-types"; 4 | 5 | //# BATI.hasDatabase 6 | declare global { 7 | namespace Vike { 8 | interface PageContext { 9 | db: BATI.If<{ 10 | 'BATI.has("sqlite") && !BATI.hasD1': ReturnType<typeof sqliteDb>; 11 | 'BATI.has("drizzle") && !BATI.hasD1': ReturnType<typeof dbSqlite>; 12 | 'BATI.has("drizzle")': ReturnType<typeof dbD1>; 13 | "BATI.hasD1": D1Database; 14 | }>; 15 | } 16 | } 17 | } 18 | 19 | export {}; 20 | -------------------------------------------------------------------------------- /boilerplates/shared-todo/files/pages/todo/+config.ts: -------------------------------------------------------------------------------- 1 | export const config = { 2 | prerender: false, 3 | }; 4 | -------------------------------------------------------------------------------- /boilerplates/shared-todo/files/pages/todo/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | import { todos } from "@batijs/shared-no-db/database/todoItems"; 3 | import * as drizzleQueries from "@batijs/drizzle/database/drizzle/queries/todos"; 4 | import * as sqliteQueries from "@batijs/sqlite/database/sqlite/queries/todos"; 5 | import * as d1Queries from "@batijs/d1-sqlite/database/d1/queries/todos"; 6 | import type { PageContextServer } from "vike/types"; 7 | 8 | export type Data = { 9 | todo: { text: string }[]; 10 | }; 11 | 12 | export default async function data(_pageContext: PageContextServer): Promise<Data> { 13 | if (BATI.has("drizzle")) { 14 | const todo = await drizzleQueries.getAllTodos(_pageContext.db); 15 | 16 | return { todo }; 17 | } else if (BATI.has("sqlite") && !BATI.hasD1) { 18 | const todo = sqliteQueries.getAllTodos(_pageContext.db); 19 | 20 | return { todo }; 21 | } else if (BATI.hasD1) { 22 | const todo = await d1Queries.getAllTodos(_pageContext.db); 23 | 24 | return { todo }; 25 | } else { 26 | return { todo: todos }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /boilerplates/shared-todo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@batijs/core/types", "vike-cloudflare/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/shared/files/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "vike dev", 4 | "build": "vike build", 5 | "preview": "vike preview" 6 | }, 7 | "dependencies": { 8 | "vike": "^0.4.230" 9 | }, 10 | "devDependencies": { 11 | "typescript": "^5.8.3", 12 | "vite": "^6.3.5" 13 | }, 14 | "type": "module" 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/shared/files/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "resolveJsonModule": true, 7 | "skipLibCheck": true, 8 | "sourceMap": true, 9 | "module": "ESNext", 10 | "noEmit": true, 11 | "moduleResolution": "Bundler", 12 | "target": "ES2022", 13 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 14 | "types": ["vite/client"] 15 | }, 16 | "exclude": ["dist"] 17 | } 18 | -------------------------------------------------------------------------------- /boilerplates/shared/hooks/after.ts: -------------------------------------------------------------------------------- 1 | import type { VikeMeta } from "@batijs/core"; 2 | import { readFile, rename, writeFile } from "node:fs/promises"; 3 | import { join } from "node:path"; 4 | 5 | async function cleanupReadme(cwd: string) { 6 | const content = await readFile(join(cwd, "README.md"), "utf8"); 7 | await writeFile( 8 | join(cwd, "README.md"), 9 | content 10 | .replaceAll(/<!--bati:.*-->/g, "") 11 | .replaceAll(/\n\n+/g, "\n\n") 12 | .trimStart(), 13 | "utf-8", 14 | ); 15 | } 16 | 17 | // Rename gitignore after the fact to prevent npm from renaming it to .npmignore 18 | // See: https://github.com/npm/npm/issues/1862 19 | async function renameGitIgnore(cwd: string) { 20 | await rename(join(cwd, "gitignore"), join(cwd, ".gitignore")); 21 | } 22 | 23 | export default async function onafter(cwd: string, _meta: VikeMeta) { 24 | await cleanupReadme(cwd); 25 | await renameGitIgnore(cwd); 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/shared", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "types": "./dist/index.d.ts", 8 | "scripts": { 9 | "check-types": "tsc --noEmit", 10 | "build": "bati-compile-boilerplate" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@batijs/compile": "workspace:*", 17 | "@hattip/vite": "^0.0.49", 18 | "@types/node": "^18.19.103", 19 | "vike": "^0.4.230", 20 | "vite": "^6.3.5", 21 | "vite-plugin-vercel": "^9.0.6" 22 | }, 23 | "files": [ 24 | "dist/", 25 | "dist/files/.gitignore" 26 | ], 27 | "bati": { 28 | "enforce": "pre" 29 | }, 30 | "exports": { 31 | "./vite.config": { 32 | "types": "./dist/types/vite.config.d.ts" 33 | } 34 | }, 35 | "typesVersions": { 36 | "*": { 37 | "vite.config": [ 38 | "./dist/types/vite.config.d.ts" 39 | ] 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /boilerplates/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/solid-firebase-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 7 | "jsx": "preserve", 8 | "jsxImportSource": "solid-js", 9 | "lib": ["DOM", "DOM.Iterable", "ESNext"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /boilerplates/solid-lucia-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/solid-lucia-auth", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103", 17 | "solid-js": "^1.9.7", 18 | "vike": "^0.4.230", 19 | "vite": "^6.3.5" 20 | }, 21 | "dependencies": { 22 | "@batijs/core": "workspace:*" 23 | }, 24 | "files": [ 25 | "dist/" 26 | ], 27 | "bati": { 28 | "if": { 29 | "flag": { 30 | "$all": [ 31 | "solid", 32 | "lucia-auth" 33 | ] 34 | } 35 | } 36 | }, 37 | "exports": { 38 | "./pages/login/+Page": { 39 | "types": "./dist/types/pages/login/+Page.d.ts" 40 | } 41 | }, 42 | "typesVersions": { 43 | "*": { 44 | "pages/login/+Page": [ 45 | "./dist/types/pages/login/+Page.d.ts" 46 | ] 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /boilerplates/solid-lucia-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 7 | "jsx": "preserve", 8 | "jsxImportSource": "solid-js", 9 | "lib": ["DOM", "DOM.Iterable", "ESNext"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /boilerplates/solid-sentry/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@sentry/solid"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/solid-sentry/files/sentry.browser.config.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/solid"; 2 | 3 | export const sentryBrowserConfig = () => { 4 | // eslint-disable-next-line 5 | import.meta.env.PROD === true && 6 | Sentry.init({ 7 | dsn: import.meta.env.PUBLIC_ENV__SENTRY_DSN, 8 | environment: "production-frontend", 9 | //enabled: import.meta.env.DEV ? false : true, 10 | integrations: [Sentry.replayIntegration()], 11 | // Set tracesSampleRate to 1.0 to capture 100% 12 | // of transactions for tracing. 13 | tracesSampleRate: 1.0, 14 | // Set `tracePropagationTargets` to control for which URLs trace propagation should be enabled 15 | tracePropagationTargets: [/^\//, /^https:\/\/yourserver\.io\/api/], 16 | // Capture Replay for 10% of all sessions, 17 | // plus for 100% of sessions with an error 18 | replaysSessionSampleRate: 0.1, 19 | replaysOnErrorSampleRate: 1.0, 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /boilerplates/solid-sentry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "vike-solid/client", "@batijs/core/types"], 5 | "jsx": "preserve", 6 | "jsxImportSource": "solid-js", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/solid/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDevDependencies(["vite"]).addDependencies(["solid-js", "vike-solid", "vike"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/solid/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.jsx = "react-jsx"; 7 | tsConfig.compilerOptions.jsxImportSource = "solid-js"; 8 | tsConfig.compilerOptions.types = [...(tsConfig.compilerOptions.types ?? []), "vike-solid/client"]; 9 | 10 | return tsConfig; 11 | } 12 | -------------------------------------------------------------------------------- /boilerplates/solid/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "vike-solid/vite", 8 | constructor: "vikeSolid", 9 | }); 10 | 11 | return mod.generate().code; 12 | } 13 | -------------------------------------------------------------------------------- /boilerplates/solid/files/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import { createMemo } from "solid-js"; 2 | import { usePageContext } from "vike-solid/usePageContext"; 3 | 4 | export function Link(props: { href: string; children: string }) { 5 | const pageContext = usePageContext(); 6 | const isActive = createMemo(() => 7 | props.href === "/" ? pageContext.urlPathname === props.href : pageContext.urlPathname.startsWith(props.href), 8 | ); 9 | return ( 10 | <a href={props.href} class={isActive() ? "is-active" : undefined}> 11 | {props.children} 12 | </a> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/solid/files/layouts/style.css: -------------------------------------------------------------------------------- 1 | /* Links */ 2 | a { 3 | text-decoration: none; 4 | } 5 | #sidebar a { 6 | padding: 2px 10px; 7 | margin-left: -10px; 8 | } 9 | #sidebar a.is-active { 10 | background-color: #eee; 11 | } 12 | 13 | /* Reset */ 14 | body { 15 | margin: 0; 16 | font-family: sans-serif; 17 | } 18 | * { 19 | box-sizing: border-box; 20 | } 21 | 22 | /* Page Transition Animation */ 23 | #page-content { 24 | opacity: 1; 25 | transition: opacity 0.3s ease-in-out; 26 | } 27 | body.page-is-transitioning #page-content { 28 | opacity: 0; 29 | } 30 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/+config.ts: -------------------------------------------------------------------------------- 1 | import vikeSolid from "vike-solid/config"; 2 | import type { Config } from "vike/types"; 3 | import Layout from "../layouts/LayoutDefault.js"; 4 | 5 | // Default config (can be overridden by pages) 6 | // https://vike.dev/config 7 | 8 | export default { 9 | // https://vike.dev/Layout 10 | Layout, 11 | 12 | // https://vike.dev/head-tags 13 | title: "My Vike App", 14 | description: "Demo showcasing Vike", 15 | 16 | //# BATI.has("auth0") || BATI.has("firebase-auth") || BATI.has("authjs") || BATI.has("lucia-auth") 17 | passToClient: ["user"], 18 | extends: vikeSolid, 19 | } satisfies Config; 20 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/+onPageTransitionEnd.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionEndAsync } from "vike/types"; 2 | 3 | export const onPageTransitionEnd: OnPageTransitionEndAsync = async () => { 4 | console.log("Page transition end"); 5 | document.querySelector("body")?.classList.remove("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/+onPageTransitionStart.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionStartAsync } from "vike/types"; 2 | 3 | export const onPageTransitionStart: OnPageTransitionStartAsync = async () => { 4 | console.log("Page transition start"); 5 | document.querySelector("body")?.classList.add("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/_error/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { Show } from "solid-js"; 2 | import { usePageContext } from "vike-solid/usePageContext"; 3 | 4 | export default function Page() { 5 | const { is404 } = usePageContext(); 6 | return ( 7 | <Show 8 | when={is404} 9 | fallback={ 10 | <> 11 | <h1>500 Internal Server Error</h1> 12 | <p>Something went wrong.</p> 13 | </> 14 | } 15 | > 16 | <h1>404 Page Not Found</h1> 17 | <p>This page could not be found.</p> 18 | </Show> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/index/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { Counter } from "./Counter.js"; 2 | import { css } from "../../styled-system/css"; 3 | 4 | export default function Page() { 5 | return ( 6 | <> 7 | <h1 8 | //# BATI.has("tailwindcss") || BATI.has("panda-css") 9 | class={ 10 | BATI.has("tailwindcss") 11 | ? "font-bold text-3xl pb-4" 12 | : css({ font: "bold 2em sans-serif", marginBlock: "0.67em" }) 13 | } 14 | > 15 | My Vike app 16 | </h1> 17 | This page is: 18 | <ul> 19 | <li>Rendered to HTML.</li> 20 | <li> 21 | Interactive. <Counter /> 22 | </li> 23 | </ul> 24 | </> 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/index/Counter.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from "solid-js"; 2 | import { css } from "../../styled-system/css"; 3 | 4 | export { Counter }; 5 | 6 | function Counter() { 7 | const [count, setCount] = createSignal(0); 8 | 9 | return ( 10 | <button 11 | type="button" 12 | //# BATI.has("tailwindcss") || BATI.has("panda-css") 13 | class={ 14 | BATI.has("daisyui") 15 | ? "btn" 16 | : BATI.has("tailwindcss") 17 | ? "inline-block border border-black rounded bg-gray-200 px-2 py-1 text-xs font-medium uppercase leading-normal" 18 | : css({ 19 | display: "inline-block", 20 | border: "1px solid black", 21 | rounded: "sm", 22 | bg: "gray.200", 23 | px: 1, 24 | py: 0.5, 25 | fontSize: 12, 26 | fontWeight: 500, 27 | lineHeight: "16px", 28 | }) 29 | } 30 | onClick={() => setCount((count) => count + 1)} 31 | > 32 | Counter {count()} 33 | </button> 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/star-wars/@id/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { useData } from "vike-solid/useData"; 2 | import type { Data } from "./+data.js"; 3 | 4 | export default function Page() { 5 | const movie = useData<Data>(); 6 | return ( 7 | <> 8 | <h1>{movie.title}</h1> 9 | Release Date: {movie.release_date} 10 | <br /> 11 | Director: {movie.director} 12 | <br /> 13 | Producer: {movie.producer} 14 | </> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/star-wars/@id/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { PageContextServer } from "vike/types"; 4 | import type { MovieDetails } from "../types.js"; 5 | import { useConfig } from "vike-solid/useConfig"; 6 | 7 | export type Data = Awaited<ReturnType<typeof data>>; 8 | 9 | export const data = async (pageContext: PageContextServer) => { 10 | // https://vike.dev/useConfig 11 | const config = useConfig(); 12 | 13 | const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`); 14 | let movie = (await response.json()) as MovieDetails; 15 | 16 | config({ 17 | // Set <title> 18 | title: movie.title, 19 | }); 20 | 21 | // We remove data we don't need because the data is passed to 22 | // the client; we should minimize what is sent over the network. 23 | movie = minimize(movie); 24 | 25 | return movie; 26 | }; 27 | 28 | function minimize(movie: MovieDetails): MovieDetails { 29 | const { id, title, release_date, director, producer } = movie; 30 | const minimizedMovie = { id, title, release_date, director, producer }; 31 | return minimizedMovie; 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/star-wars/index/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { For } from "solid-js"; 2 | import { useData } from "vike-solid/useData"; 3 | import type { Data } from "./+data.js"; 4 | 5 | export default function Page() { 6 | const movies = useData<Data>(); 7 | return ( 8 | <> 9 | <h1>Star Wars Movies</h1> 10 | <ol> 11 | <For each={movies}> 12 | {(movie) => ( 13 | <li> 14 | <a href={`/star-wars/${movie.id}`}>{movie.title}</a> ({movie.release_date}) 15 | </li> 16 | )} 17 | </For> 18 | </ol> 19 | <p> 20 | Source: <a href="https://brillout.github.io/star-wars">brillout.github.io/star-wars</a>. 21 | </p> 22 | </> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/star-wars/index/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { Movie, MovieDetails } from "../types.js"; 4 | import { useConfig } from "vike-solid/useConfig"; 5 | 6 | export type Data = Awaited<ReturnType<typeof data>>; 7 | 8 | export const data = async () => { 9 | // https://vike.dev/useConfig 10 | const config = useConfig(); 11 | 12 | const response = await fetch("https://brillout.github.io/star-wars/api/films.json"); 13 | const moviesData = (await response.json()) as MovieDetails[]; 14 | 15 | config({ 16 | // Set <title> 17 | title: `${moviesData.length} Star Wars Movies`, 18 | }); 19 | 20 | // We remove data we don't need because the data is passed to the client; we should 21 | // minimize what is sent over the network. 22 | const movies = minimize(moviesData); 23 | 24 | return movies; 25 | }; 26 | 27 | function minimize(movies: MovieDetails[]): Movie[] { 28 | return movies.map((movie) => { 29 | const { title, release_date, id } = movie; 30 | return { title, release_date, id }; 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/star-wars/types.ts: -------------------------------------------------------------------------------- 1 | export type Movie = { 2 | id: string; 3 | title: string; 4 | release_date: string; 5 | }; 6 | 7 | export type MovieDetails = Movie & { 8 | director: string; 9 | producer: string; 10 | }; 11 | -------------------------------------------------------------------------------- /boilerplates/solid/files/pages/todo/+Page.tsx: -------------------------------------------------------------------------------- 1 | import type { Data } from "@batijs/shared-todo/pages/todo/+data"; 2 | import { useData } from "vike-solid/useData"; 3 | import { TodoList } from "./TodoList.js"; 4 | 5 | export default function Page() { 6 | const data = useData<Data>(); 7 | return ( 8 | <> 9 | <h1>To-do List</h1> 10 | <TodoList initialTodoItems={data.todo} /> 11 | </> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /boilerplates/solid/panda.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@pandacss/dev"; 2 | 3 | export default defineConfig({ 4 | outdir: "files/styled-system", 5 | }); 6 | -------------------------------------------------------------------------------- /boilerplates/solid/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./files/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /boilerplates/solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "vike-solid/client", "@batijs/core/types"], 5 | "jsx": "preserve", 6 | "jsxImportSource": "solid-js", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/$.env.ts: -------------------------------------------------------------------------------- 1 | import { appendToEnv, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getEnv(props: TransformerProps) { 4 | if (props.meta.BATI.hasD1) return; 5 | const envContent = await props.readfile?.(); 6 | 7 | return appendToEnv(envContent, "DATABASE_URL", "sqlite.db", "Path to the sqlite database"); 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/$README.md.ts: -------------------------------------------------------------------------------- 1 | import { loadMarkdown, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getReadme(props: TransformerProps) { 4 | const content = await loadMarkdown(props); 5 | 6 | //language=Markdown 7 | const todo = ` 8 | ## *Sqlite* 9 | 10 | First, ensure that \`DATABASE_URL\` is configured in \`.env\` file, then create the database: 11 | \`\`\`bash 12 | pnpm sqlite:migrate # creates sqlite tables 13 | \`\`\` 14 | `; 15 | 16 | content.addMarkdownFeature(todo, "sqlite"); 17 | 18 | return content; 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .setScript("sqlite:migrate", { 8 | value: "tsx ./database/sqlite/schema/all.ts", 9 | precedence: 1, 10 | }) 11 | .addDevDependencies(["@types/better-sqlite3"]) 12 | .addDevDependencies(["tsx"], ["sqlite:migrate"]) 13 | .addDependencies(["better-sqlite3", "dotenv"]); 14 | } 15 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/database/sqlite/db.ts: -------------------------------------------------------------------------------- 1 | /*# BATI include-if-imported #*/ 2 | import sqlite, { type Database } from "better-sqlite3"; 3 | 4 | let singleton: Database | undefined = undefined; 5 | 6 | export function db(): Database { 7 | if (!singleton) { 8 | if (!process.env.DATABASE_URL) { 9 | throw new Error("Missing DATABASE_URL in .env file"); 10 | } 11 | 12 | singleton = sqlite(process.env.DATABASE_URL); 13 | } 14 | return singleton; 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/database/sqlite/queries/todos.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from "better-sqlite3"; 2 | 3 | export function insertTodo(db: Database, text: string) { 4 | return db.prepare("INSERT INTO todos (text) VALUES (?)").run(text); 5 | } 6 | 7 | export function getAllTodos(db: Database) { 8 | return db.prepare<[], { id: number; text: string }>("SELECT * FROM todos").all(); 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/database/sqlite/schema/all.ts: -------------------------------------------------------------------------------- 1 | //# BATI.has("lucia-auth") 2 | import "./lucia-auth"; 3 | import "./todos"; 4 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/database/sqlite/schema/lucia-auth.ts: -------------------------------------------------------------------------------- 1 | /*{ @if (it.BATI.has("lucia-auth")) }*/ 2 | import "dotenv/config"; 3 | import { db } from "../db"; 4 | 5 | const client = db(); 6 | 7 | /** 8 | * SQLite Schema 9 | * 10 | * @link {@see https://lucia-auth.com/database/sqlite#schema} 11 | */ 12 | client.exec(`CREATE TABLE IF NOT EXISTS users ( 13 | id TEXT NOT NULL PRIMARY KEY, 14 | username TEXT NOT NULL UNIQUE, 15 | password TEXT 16 | )`); 17 | 18 | client.exec(`CREATE TABLE IF NOT EXISTS oauth_accounts ( 19 | provider_id TEXT NOT NULL, 20 | provider_user_id INTEGER NOT NULL, 21 | user_id TEXT NOT NULL, 22 | PRIMARY KEY (provider_id, provider_user_id), 23 | FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE cascade ON DELETE cascade 24 | )`); 25 | 26 | client.exec(`CREATE TABLE IF NOT EXISTS sessions ( 27 | id TEXT NOT NULL PRIMARY KEY, 28 | expires_at INTEGER NOT NULL, 29 | user_id TEXT NOT NULL, 30 | FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE cascade ON DELETE cascade 31 | )`); 32 | /*{ /if }*/ 33 | -------------------------------------------------------------------------------- /boilerplates/sqlite/files/database/sqlite/schema/todos.ts: -------------------------------------------------------------------------------- 1 | /*# BATI include-if-imported #*/ 2 | import "dotenv/config"; 3 | import { db } from "../db"; 4 | 5 | const client = db(); 6 | 7 | /** 8 | * SQLite Schema 9 | * `todos` example 10 | */ 11 | client.exec(`CREATE TABLE IF NOT EXISTS todos ( 12 | id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 13 | text TEXT 14 | )`); 15 | -------------------------------------------------------------------------------- /boilerplates/sqlite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/tailwindcss/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const pkgjson = await import("../package.json").then((x) => x.default); 5 | const packageJson = await loadPackageJson(props, pkgjson); 6 | 7 | return packageJson 8 | .addDevDependencies(["tailwindcss", "@tailwindcss/vite"]) 9 | .addDevDependencies(["daisyui"], props.meta.BATI.has("daisyui")); 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/tailwindcss/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "@tailwindcss/vite", 8 | constructor: "tailwindcss", 9 | }); 10 | 11 | return mod.generate().code; 12 | } 13 | -------------------------------------------------------------------------------- /boilerplates/tailwindcss/files/layouts/tailwind.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | /*{ @if (it.BATI.has("daisyui")) }*/ 3 | @plugin "daisyui"; 4 | /*{ /if }*/ 5 | -------------------------------------------------------------------------------- /boilerplates/tailwindcss/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/tailwindcss", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@tailwindcss/vite": "^4.1.7", 17 | "@types/node": "^18.19.103", 18 | "autoprefixer": "^10.4.21", 19 | "daisyui": "^5.0.37", 20 | "tailwindcss": "^4.1.7", 21 | "vike": "^0.4.230", 22 | "vite": "^6.3.5" 23 | }, 24 | "dependencies": { 25 | "@batijs/core": "workspace:*" 26 | }, 27 | "files": [ 28 | "dist/" 29 | ], 30 | "bati": { 31 | "if": { 32 | "flag": "tailwindcss" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /boilerplates/tailwindcss/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/telefunc/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["telefunc", "@universal-middleware/core"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/telefunc/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "telefunc/vite", 8 | constructor: "telefunc", 9 | imported: "telefunc", 10 | }); 11 | 12 | return mod.generate().code; 13 | } 14 | -------------------------------------------------------------------------------- /boilerplates/telefunc/files/global.d.ts: -------------------------------------------------------------------------------- 1 | import { D1Database } from "@cloudflare/workers-types"; 2 | import { dbD1, dbSqlite } from "@batijs/drizzle/database/drizzle/db"; 3 | import { db as sqliteDb } from "@batijs/sqlite/database/sqlite/db"; 4 | 5 | //# BATI.hasDatabase 6 | declare module "telefunc" { 7 | namespace Telefunc { 8 | interface Context { 9 | db: BATI.If<{ 10 | 'BATI.has("sqlite") && !BATI.hasD1': ReturnType<typeof sqliteDb>; 11 | 'BATI.has("drizzle") && !BATI.hasD1': ReturnType<typeof dbSqlite>; 12 | 'BATI.has("drizzle")': ReturnType<typeof dbD1>; 13 | "BATI.hasD1": D1Database; 14 | _: object; 15 | }>; 16 | } 17 | } 18 | } 19 | 20 | export {}; 21 | -------------------------------------------------------------------------------- /boilerplates/telefunc/files/pages/todo/TodoList.telefunc.ts: -------------------------------------------------------------------------------- 1 | // We use Telefunc (https://telefunc.com) for data mutations. Being able to use Telefunc for fetching initial data is work-in-progress (https://vike.dev/data-fetching#tools). 2 | 3 | import { todos } from "@batijs/shared-no-db/database/todoItems"; 4 | import * as drizzleQueries from "@batijs/drizzle/database/drizzle/queries/todos"; 5 | import * as sqliteQueries from "@batijs/sqlite/database/sqlite/queries/todos"; 6 | import * as d1Queries from "@batijs/d1-sqlite/database/d1/queries/todos"; 7 | import { getContext } from "telefunc"; 8 | 9 | export async function onNewTodo({ text }: { text: string }) { 10 | if (BATI.has("drizzle")) { 11 | const context = getContext(); 12 | await drizzleQueries.insertTodo(context.db, text); 13 | } else if (BATI.has("sqlite") && !BATI.hasD1) { 14 | const context = getContext(); 15 | sqliteQueries.insertTodo(context.db, text); 16 | } else if (BATI.hasD1) { 17 | const context = getContext(); 18 | await d1Queries.insertTodo(context.db, text); 19 | } else { 20 | todos.push({ text }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /boilerplates/telefunc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["@types/node", "@batijs/core/types"] 4 | }, 5 | "extends": ["../tsconfig.base.json"] 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/trpc/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@trpc/server", "@trpc/client"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/trpc/files/trpc/client.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCProxyClient, httpBatchLink } from "@trpc/client"; 2 | import type { AppRouter } from "./server.js"; 3 | 4 | export const trpc = createTRPCProxyClient<AppRouter>({ 5 | links: [ 6 | httpBatchLink({ 7 | url: "/api/trpc", 8 | }), 9 | ], 10 | }); 11 | -------------------------------------------------------------------------------- /boilerplates/trpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/ts-rest/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .addDevDependencies(["zod"]) 8 | .addDependencies(["@ts-rest/core", "@ts-rest/serverless", "@universal-middleware/core"]); 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/ts-rest/files/ts-rest/client.ts: -------------------------------------------------------------------------------- 1 | import { initClient } from "@ts-rest/core"; 2 | import { contract } from "./contract"; 3 | 4 | /** 5 | * ts-rest client 6 | * 7 | * This is the basic client, using fetch under the hood which is exported from @ts-rest/core 8 | * @link {@see https://ts-rest.com/docs/core/fetch/} 9 | **/ 10 | export const client = initClient(contract, { 11 | baseUrl: "", 12 | baseHeaders: {}, 13 | }); 14 | -------------------------------------------------------------------------------- /boilerplates/ts-rest/files/ts-rest/contract.ts: -------------------------------------------------------------------------------- 1 | import { initContract } from "@ts-rest/core"; 2 | 3 | const c = initContract(); 4 | 5 | /** 6 | * ts-rest contract 7 | * 8 | * Create a contract, this should ideally be shared between your consumers and producers 9 | * Think of this as your HTTP Schema that both your client and backend can use. 10 | * @link {@see https://ts-rest.com/docs/core/} 11 | **/ 12 | export const contract = c.router( 13 | { 14 | demo: { 15 | method: "GET", 16 | path: "/demo", 17 | responses: { 18 | 200: c.type<{ demo: boolean }>(), 19 | }, 20 | }, 21 | createTodo: { 22 | method: "POST", 23 | path: "/todo/create", 24 | body: c.type<{ text: string }>(), 25 | responses: { 26 | 200: c.type<{ status: string }>(), 27 | }, 28 | summary: "Create a Todo", 29 | }, 30 | }, 31 | { 32 | pathPrefix: "/api", 33 | }, 34 | ); 35 | -------------------------------------------------------------------------------- /boilerplates/ts-rest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "types": ["@batijs/core/types"] 7 | }, 8 | "exclude": ["*/dist/**/*", "*/node_modules/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.json"], 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "types": ["@batijs/core/types"] 7 | }, 8 | "exclude": ["*/dist/**/*", "*/node_modules/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/vercel/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["vite-plugin-vercel", "@vite-plugin-vercel/vike"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/vercel/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.types = [...(tsConfig.compilerOptions.types ?? []), "vite-plugin-vercel/types"]; 7 | 8 | return tsConfig; 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/vercel/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | const options = 7 | props.meta.BATI.has("express") || props.meta.BATI.has("fastify") 8 | ? { 9 | source: "/.*", 10 | } 11 | : undefined; 12 | 13 | addVitePlugin(mod, { 14 | from: "vite-plugin-vercel", 15 | constructor: "vercel", 16 | options, 17 | }); 18 | 19 | return mod.generate().code; 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/vercel/files/pages/$+config.ts.ts: -------------------------------------------------------------------------------- 1 | import { loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | // @ts-ignore 7 | mod.exports.default.prerender = true; 8 | 9 | return mod.generate().code; 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/vercel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/vercel", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@types/node": "^18.19.103", 17 | "@vite-plugin-vercel/vike": "^9.0.5", 18 | "vike": "^0.4.230", 19 | "vite": "^6.3.5", 20 | "vite-plugin-vercel": "^9.0.6" 21 | }, 22 | "dependencies": { 23 | "@batijs/core": "workspace:*" 24 | }, 25 | "files": [ 26 | "dist/" 27 | ], 28 | "bati": { 29 | "if": { 30 | "flag": "vercel" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /boilerplates/vercel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/vue-firebase-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/vue-firebase-auth", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "build": "bati-compile-boilerplate" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@batijs/compile": "workspace:*", 15 | "@types/node": "^18.19.103", 16 | "firebase": "^11.8.1", 17 | "firebaseui": "^6.1.0", 18 | "vike": "^0.4.230", 19 | "vite": "^6.3.5", 20 | "vue": "^3.5.14" 21 | }, 22 | "dependencies": { 23 | "@batijs/core": "workspace:*" 24 | }, 25 | "files": [ 26 | "dist/" 27 | ], 28 | "bati": { 29 | "if": { 30 | "flag": { 31 | "$all": [ 32 | "vue", 33 | "firebase-auth" 34 | ] 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /boilerplates/vue-firebase-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplates/vue-lucia-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/vue-lucia-auth", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "build": "bati-compile-boilerplate" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@batijs/compile": "workspace:*", 15 | "@types/node": "^18.19.103", 16 | "vike": "^0.4.230", 17 | "vite": "^6.3.5", 18 | "vue": "^3.5.14" 19 | }, 20 | "dependencies": { 21 | "@batijs/core": "workspace:*" 22 | }, 23 | "files": [ 24 | "dist/" 25 | ], 26 | "bati": { 27 | "if": { 28 | "flag": { 29 | "$all": [ 30 | "vue", 31 | "lucia-auth" 32 | ] 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /boilerplates/vue-lucia-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 5 | "jsx": "preserve", 6 | "jsxImportSource": "vue", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/vue-sentry/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson.addDependencies(["@sentry/vue"]); 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/vue-sentry/files/pages/sentry/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div> 3 | <h1>Sentry Test Page</h1> 4 | <div v-if="!isSentryClientInitialized" style="color: red"> 5 | Sentry Client is not initialized! Vite Mode: {{ isProdMode ? "PROD" : "DEV" }} 6 | </div> 7 | 8 | <div> 9 | <button @click="throwError">Throw Javascript Error</button> 10 | </div> 11 | </div> 12 | </template> 13 | 14 | <script> 15 | import * as Sentry from "@sentry/vue"; 16 | export default { 17 | data() { 18 | return { 19 | isSentryClientInitialized: false, 20 | isProdMode: import.meta.env.PROD, 21 | }; 22 | }, 23 | mounted() { 24 | this.isSentryClientInitialized = !!Sentry.getClient(); 25 | }, 26 | methods: { 27 | throwError() { 28 | const mode = import.meta.env.DEV ? "DEV Mode" : "PROD Mode"; 29 | throw new Error(`This is a Vue SENTRY Browser Vue Test! [${mode}]`); 30 | }, 31 | }, 32 | }; 33 | </script> 34 | -------------------------------------------------------------------------------- /boilerplates/vue-sentry/files/sentry.browser.config.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/vue"; 2 | import { usePageContext } from "vike-vue/usePageContext"; 3 | 4 | export const sentryBrowserConfig = () => { 5 | // eslint-disable-next-line 6 | import.meta.env.PROD === true && 7 | Sentry.init({ 8 | // @ts-ignore 9 | app: usePageContext().app, 10 | dsn: import.meta.env.PUBLIC_ENV__SENTRY_DSN, 11 | environment: "production-frontend", 12 | //enabled: import.meta.env.DEV ? false : true, 13 | integrations: [Sentry.replayIntegration()], 14 | // Set tracesSampleRate to 1.0 to capture 100% 15 | // of transactions for tracing. 16 | tracesSampleRate: 1.0, 17 | // Set `tracePropagationTargets` to control for which URLs trace propagation should be enabled 18 | tracePropagationTargets: [/^\//, /^https:\/\/yourserver\.io\/api/], 19 | // Capture Replay for 10% of all sessions, 20 | // plus for 100% of sessions with an error 21 | replaysSessionSampleRate: 0.1, 22 | replaysOnErrorSampleRate: 1.0, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /boilerplates/vue-sentry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/vue-sentry", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "build": "bati-compile-boilerplate" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@batijs/compile": "workspace:*", 16 | "@sentry/vue": "^9.22.0", 17 | "@types/node": "^18.19.103", 18 | "vike-vue": "^0.9.1", 19 | "vite": "^6.3.5", 20 | "vue": "^3.5.14" 21 | }, 22 | "dependencies": { 23 | "@batijs/core": "workspace:*" 24 | }, 25 | "files": [ 26 | "dist/" 27 | ], 28 | "bati": { 29 | "if": { 30 | "flag": { 31 | "$all": [ 32 | "vue", 33 | "sentry" 34 | ] 35 | } 36 | } 37 | }, 38 | "exports": { 39 | "./sentry.browser.config": { 40 | "types": "./dist/types/sentry.browser.config.d.ts" 41 | } 42 | }, 43 | "typesVersions": { 44 | "*": { 45 | "sentry.browser.config": [ 46 | "./dist/types/sentry.browser.config.d.ts" 47 | ] 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /boilerplates/vue-sentry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 5 | "jsx": "preserve", 6 | "jsxImportSource": "vue", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /boilerplates/vue/files/$package.json.ts: -------------------------------------------------------------------------------- 1 | import { loadPackageJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getPackageJson(props: TransformerProps) { 4 | const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default)); 5 | 6 | return packageJson 7 | .addDevDependencies(["vite"]) 8 | .addDependencies([ 9 | "@vitejs/plugin-vue", 10 | "@vue/compiler-sfc", 11 | "@vue/server-renderer", 12 | "unplugin-vue-markdown", 13 | "vike-vue", 14 | "vike", 15 | "vue", 16 | ]) 17 | .addDependencies(["vue-gtag"], props.meta.BATI.has("google-analytics")); 18 | } 19 | -------------------------------------------------------------------------------- /boilerplates/vue/files/$tsconfig.json.ts: -------------------------------------------------------------------------------- 1 | import { loadAsJson, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getTsConfig(props: TransformerProps) { 4 | const tsConfig = await loadAsJson(props); 5 | 6 | tsConfig.compilerOptions.jsx = "react-jsx"; 7 | tsConfig.compilerOptions.jsxImportSource = "vue"; 8 | 9 | return tsConfig; 10 | } 11 | -------------------------------------------------------------------------------- /boilerplates/vue/files/$vite.config.ts.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, loadAsMagicast, type TransformerProps } from "@batijs/core"; 2 | 3 | export default async function getViteConfig(props: TransformerProps) { 4 | const mod = await loadAsMagicast(props); 5 | 6 | addVitePlugin(mod, { 7 | from: "@vitejs/plugin-vue", 8 | constructor: "vue", 9 | options: { 10 | include: [/\.vue$/, /\.md$/], 11 | }, 12 | }); 13 | addVitePlugin(mod, { 14 | from: "unplugin-vue-markdown/vite", 15 | constructor: "md", 16 | options: {}, 17 | }); 18 | 19 | return mod.generate().code; 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/vue/files/components/Content.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div id="page-container"> 3 | <!-- BATI.has("tailwindcss") --> 4 | <div id="page-content" class="p-5 pb-12 min-h-screen"> 5 | <slot /> 6 | </div> 7 | <!-- BATI.has("panda-css") --> 8 | <div id="page-content" :class="css({ p: '20px', pb: '50px', minH: '100vh' })"> 9 | <slot /> 10 | </div> 11 | <!-- !BATI.has("tailwindcss") && !BATI.has("panda-css") --> 12 | <div id="page-content" style="padding: 20px; padding-bottom: 50px; min-height: 100vh"> 13 | <slot /> 14 | </div> 15 | </div> 16 | </template> 17 | 18 | <script setup lang="ts"> 19 | import { css } from "../styled-system/css"; 20 | </script> 21 | 22 | <style> 23 | /* Page Transition Animation */ 24 | body.page-is-transitioning #page-content { 25 | opacity: 0; 26 | } 27 | </style> 28 | 29 | <style scoped> 30 | /* Page Transition Animation */ 31 | #page-content { 32 | opacity: 1; 33 | transition: opacity 0.3s ease-in-out; 34 | } 35 | </style> 36 | -------------------------------------------------------------------------------- /boilerplates/vue/files/components/Link.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <a :class="{ active: isActive }"> 3 | <slot /> 4 | </a> 5 | </template> 6 | 7 | <script lang="ts" setup> 8 | import { usePageContext } from "vike-vue/usePageContext"; 9 | import { computed, useAttrs } from "vue"; 10 | 11 | const pageContext = usePageContext(); 12 | const { href } = useAttrs(); 13 | const isActive = computed(() => { 14 | const { urlPathname } = pageContext; 15 | return href === "/" ? urlPathname === href : urlPathname.startsWith(href); 16 | }); 17 | </script> 18 | 19 | <style scoped> 20 | a { 21 | padding: 2px 10px; 22 | margin-left: -10px; 23 | } 24 | a.active { 25 | background-color: #eee; 26 | } 27 | </style> 28 | -------------------------------------------------------------------------------- /boilerplates/vue/files/components/Logo.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <!-- BATI.has("tailwindcss") --> 3 | <div class="p-5 mb-2"> 4 | <a href="/"> 5 | <img src="../assets/logo.svg" height="64" width="64" /> 6 | </a> 7 | </div> 8 | <!-- BATI.has("panda-css") --> 9 | <div :class="css({ p: '20px', mb: '10px' })"> 10 | <a href="/"> 11 | <img src="../assets/logo.svg" height="64" width="64" /> 12 | </a> 13 | </div> 14 | <!-- !BATI.has("tailwindcss") && !BATI.has("panda-css") --> 15 | <div style="margin-top: 20px; margin-bottom: 10px"> 16 | <a href="/"> 17 | <img src="../assets/logo.svg" height="64" width="64" /> 18 | </a> 19 | </div> 20 | </template> 21 | <script setup lang="ts"> 22 | import { css } from "../styled-system/css"; 23 | </script> 24 | -------------------------------------------------------------------------------- /boilerplates/vue/files/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <!-- BATI.has("tailwindcss") --> 3 | <div id="sidebar" class="p-5 flex flex-col shrink-0 border-r-2 border-r-gray-200"> 4 | <slot /> 5 | </div> 6 | <!-- BATI.has("panda-css") --> 7 | <div 8 | id="sidebar" 9 | :class=" 10 | css({ 11 | p: '20px', 12 | display: 'flex', 13 | flexShrink: 0, 14 | flexDir: 'column', 15 | lineHeight: '1.8em', 16 | borderRight: '2px solid #eee', 17 | }) 18 | " 19 | > 20 | <slot /> 21 | </div> 22 | <!-- !BATI.has("tailwindcss") && !BATI.has("panda-css") --> 23 | <div 24 | id="sidebar" 25 | style=" 26 | padding: 20px; 27 | flex-shrink: 0; 28 | display: flex; 29 | flex-direction: column; 30 | line-height: 1.8em; 31 | border-right: 2px solid #eee; 32 | " 33 | > 34 | <slot /> 35 | </div> 36 | </template> 37 | <script setup lang="ts"> 38 | import { css } from "../styled-system/css"; 39 | </script> 40 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/+Head.vue: -------------------------------------------------------------------------------- 1 | <!-- https://vike.dev/Head --> 2 | 3 | <template> 4 | <link rel="icon" :href="logoUrl" /> 5 | <!-- BATI.has("tailwindcss") --> 6 | <link rel="stylesheet" :href="tailwindCss" /> 7 | 8 | <!-- BATI.has("plausible.io") --> 9 | <!-- See https://plausible.io/docs/plausible-script --> 10 | <!-- TODO: update data-domain --> 11 | <component :is="'script'" defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js" /> 12 | </template> 13 | 14 | <script setup lang="ts"> 15 | import logoUrl from "../assets/logo.svg"; 16 | //# BATI.has("tailwindcss") 17 | import * as tailwindCss from "@batijs/tailwindcss/layouts/tailwind.css"; 18 | </script> 19 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/+config.ts: -------------------------------------------------------------------------------- 1 | import vikeVue from "vike-vue/config"; 2 | import type { Config } from "vike/types"; 3 | import Layout from "../layouts/LayoutDefault.vue"; 4 | 5 | // Default config (can be overridden by pages) 6 | // https://vike.dev/config 7 | 8 | export default { 9 | // https://vike.dev/Layout 10 | Layout, 11 | 12 | // https://vike.dev/head-tags 13 | title: "My Vike App", 14 | description: "Demo showcasing Vike", 15 | 16 | //# BATI.has("auth0") || BATI.has("firebase-auth") || BATI.has("authjs") || BATI.has("lucia-auth") 17 | passToClient: ["user"], 18 | extends: vikeVue as typeof vikeVue, 19 | } satisfies Config; 20 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/+onCreateApp.ts: -------------------------------------------------------------------------------- 1 | import type { OnCreateAppSync } from "vike-vue/types"; 2 | import { createGtag } from "vue-gtag"; 3 | 4 | // BATI.has("google-analytics") 5 | export const onCreateApp: OnCreateAppSync = (pageContext): ReturnType<OnCreateAppSync> => { 6 | const { app } = pageContext; 7 | 8 | // See https://matteo-gabriele.gitbook.io/vue-gtag/ 9 | const gtag = createGtag({ 10 | tagId: import.meta.env.PUBLIC_ENV__GOOGLE_ANALYTICS, 11 | }); 12 | 13 | app.use(gtag); 14 | }; 15 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/+onPageTransitionEnd.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionEndAsync } from "vike/types"; 2 | 3 | export const onPageTransitionEnd: OnPageTransitionEndAsync = async () => { 4 | console.log("Page transition end"); 5 | document.querySelector("body")?.classList.remove("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/+onPageTransitionStart.ts: -------------------------------------------------------------------------------- 1 | import type { OnPageTransitionStartAsync } from "vike/types"; 2 | 3 | export const onPageTransitionStart: OnPageTransitionStartAsync = async () => { 4 | console.log("Page transition start"); 5 | document.querySelector("body")?.classList.add("page-is-transitioning"); 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/_error/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <h1>{{ heading }}</h1> 3 | <p>{{ abortReason }}</p> 4 | </template> 5 | 6 | <script lang="ts" setup> 7 | import { usePageContext } from "vike-vue/usePageContext"; 8 | 9 | const ctx = usePageContext(); 10 | let { is404, abortReason } = ctx; 11 | if (!abortReason) { 12 | abortReason = is404 ? "This page could not be found." : "Something went wrong."; 13 | } 14 | const heading = is404 ? "404 Page Not Found" : "500 Internal Server Error"; 15 | </script> 16 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/index/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <!-- BATI.has("tailwindcss") --> 3 | <h1 class="font-bold text-3xl pb-4">My Vike app</h1> 4 | <!-- BATI.has("panda-css") --> 5 | <h1 :class="css({ font: 'bold 2em sans-serif', marginBlock: '0.67em' })">My Vike app</h1> 6 | <!-- !BATI.has("tailwindcss") && !BATI.has("panda-css") --> 7 | <h1>My Vike app</h1> 8 | This page is: 9 | <ul> 10 | <li>Rendered to HTML.</li> 11 | <li>Interactive. <Counter /></li> 12 | </ul> 13 | </template> 14 | 15 | <script setup lang="ts"> 16 | import Counter from "../../components/Counter.vue"; 17 | import { css } from "../../styled-system/css"; 18 | </script> 19 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/star-wars/@id/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <h1>{{ movie.title }}</h1> 3 | Release Date: {{ movie.release_date }} 4 | <br /> 5 | Director: {{ movie.director }} 6 | <br /> 7 | Producer: {{ movie.producer }} 8 | </template> 9 | 10 | <script lang="ts" setup> 11 | import { useData } from "vike-vue/useData"; 12 | import type { Data } from "./+data.js"; 13 | 14 | const movie = useData<Data>(); 15 | </script> 16 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/star-wars/@id/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { PageContextServer } from "vike/types"; 4 | import type { MovieDetails } from "../types.js"; 5 | import { useConfig } from "vike-vue/useConfig"; 6 | 7 | export type Data = Awaited<ReturnType<typeof data>>; 8 | 9 | export const data = async (pageContext: PageContextServer) => { 10 | // https://vike.dev/useConfig 11 | const config = useConfig(); 12 | 13 | const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`); 14 | let movie = (await response.json()) as MovieDetails; 15 | 16 | config({ 17 | // Set <title> 18 | title: movie.title, 19 | }); 20 | 21 | // We remove data we don't need because the data is passed to 22 | // the client; we should minimize what is sent over the network. 23 | movie = minimize(movie); 24 | 25 | return movie; 26 | }; 27 | 28 | function minimize(movie: MovieDetails): MovieDetails { 29 | const { id, title, release_date, director, producer } = movie; 30 | const minimizedMovie = { id, title, release_date, director, producer }; 31 | return minimizedMovie; 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/star-wars/index/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <h1>Star Wars Movies</h1> 3 | <ol> 4 | <li v-for="item in movies.data" :key="item.id"> 5 | <a :href="'/star-wars/' + item.id">{{ item.title }}</a> ({{ item.release_date }}) 6 | </li> 7 | </ol> 8 | <p>Source: <a href="https://brillout.github.io/star-wars">brillout.github.io/star-wars</a>.</p> 9 | </template> 10 | 11 | <script lang="ts" setup> 12 | import { useData } from "vike-vue/useData"; 13 | import type { Data } from "./+data.js"; 14 | 15 | const movies = useData<Data>(); 16 | </script> 17 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/star-wars/index/+data.ts: -------------------------------------------------------------------------------- 1 | // https://vike.dev/data 2 | 3 | import type { Movie, MovieDetails } from "../types.js"; 4 | import { useConfig } from "vike-vue/useConfig"; 5 | 6 | export type Data = Awaited<ReturnType<typeof data>>; 7 | 8 | export const data = async () => { 9 | // https://vike.dev/useConfig 10 | const config = useConfig(); 11 | 12 | const response = await fetch("https://brillout.github.io/star-wars/api/films.json"); 13 | const moviesData = (await response.json()) as MovieDetails[]; 14 | 15 | config({ 16 | // Set <title> 17 | title: `${moviesData.length} Star Wars Movies`, 18 | }); 19 | 20 | // We remove data we don't need because the data is passed to the client; we should 21 | // minimize what is sent over the network. 22 | const movies = minimize(moviesData); 23 | 24 | return { data: movies }; 25 | }; 26 | 27 | function minimize(movies: MovieDetails[]): Movie[] { 28 | return movies.map((movie) => { 29 | const { title, release_date, id } = movie; 30 | return { title, release_date, id }; 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/star-wars/types.ts: -------------------------------------------------------------------------------- 1 | export type Movie = { 2 | id: string; 3 | title: string; 4 | release_date: string; 5 | }; 6 | 7 | export type MovieDetails = Movie & { 8 | director: string; 9 | producer: string; 10 | }; 11 | -------------------------------------------------------------------------------- /boilerplates/vue/files/pages/todo/+Page.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div> 3 | <h1>To-do List</h1> 4 | <TodoList :initial-todo-items="data.todo" /> 5 | </div> 6 | </template> 7 | 8 | <script lang="ts" setup> 9 | import type { Data } from "@batijs/shared-todo/pages/todo/+data"; 10 | import { useData } from "vike-vue/useData"; 11 | import TodoList from "./TodoList.vue"; 12 | 13 | const data = useData<Data>(); 14 | </script> 15 | -------------------------------------------------------------------------------- /boilerplates/vue/files/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | // Without this file, `tsc` will fail with such errors: 2 | // pages/+config.ts:2:20 - error TS2307: Cannot find module '../layouts/LayoutDefault.vue' or its corresponding type declarations. 3 | // import Layout from "../layouts/LayoutDefault.vue"; 4 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | // 6 | // See https://stackoverflow.com/questions/71477277/typescript-cannot-find-module-in-vue-project 7 | 8 | declare module "*.vue" { 9 | import Vue from "vue"; 10 | export default Vue; 11 | } 12 | -------------------------------------------------------------------------------- /boilerplates/vue/panda.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@pandacss/dev"; 2 | 3 | export default defineConfig({ 4 | outdir: "files/styled-system", 5 | }); 6 | -------------------------------------------------------------------------------- /boilerplates/vue/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./files/**/*.{js,ts,jsx,tsx,vue}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /boilerplates/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["vite/client", "@types/node", "@batijs/core/types"], 5 | "jsx": "preserve", 6 | "jsxImportSource": "vue", 7 | "lib": ["DOM", "DOM.Iterable", "ES2022"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bump.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "bumpp"; 2 | 3 | export default defineConfig({ 4 | files: ["package.json", "packages/*/package.json"], 5 | release: "patch", 6 | commit: true, 7 | push: true, 8 | tag: true, 9 | }); 10 | -------------------------------------------------------------------------------- /doc/bati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vikejs/bati/d7eb17febeeb7ce3893a2c8c9349f90e057f268f/doc/bati.png -------------------------------------------------------------------------------- /doc/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vikejs/bati/d7eb17febeeb7ce3893a2c8c9349f90e057f268f/doc/demo.gif -------------------------------------------------------------------------------- /doc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vikejs/bati/d7eb17febeeb7ce3893a2c8c9349f90e057f268f/doc/screenshot.png -------------------------------------------------------------------------------- /packages/batijs/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "@batijs/cli"; 3 | -------------------------------------------------------------------------------- /packages/batijs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batijs", 3 | "private": true, 4 | "version": "0.0.437", 5 | "type": "module", 6 | "scripts": {}, 7 | "keywords": [], 8 | "description": "Next-gen scaffolder. Get started with fully-functional apps, and choose any tool you want", 9 | "author": "Joël Charles <joel.charles91@gmail.com>", 10 | "repository": "https://github.com/vikejs/bati", 11 | "license": "MIT", 12 | "bin": "./index.js", 13 | "devDependencies": {}, 14 | "dependencies": { 15 | "@batijs/cli": "workspace:*" 16 | }, 17 | "files": [ 18 | "index.js" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/build/README.md: -------------------------------------------------------------------------------- 1 | # @batijs/build 2 | 3 | [bati](https://batijs.dev) internal utils. 4 | -------------------------------------------------------------------------------- /packages/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/build", 3 | "version": "0.0.437", 4 | "description": "", 5 | "type": "module", 6 | "scripts": { 7 | "check-types": "tsc --noEmit", 8 | "prepublishOnly": "pnpm run build", 9 | "build": "tsup" 10 | }, 11 | "keywords": [], 12 | "author": "Joël Charles <joel.charles91@gmail.com>", 13 | "repository": "https://github.com/vikejs/bati", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@batijs/compile": "workspace:*", 17 | "@batijs/features": "workspace:*", 18 | "@types/node": "^18.19.103", 19 | "tsup": "^8.5.0" 20 | }, 21 | "dependencies": { 22 | "@batijs/core": "workspace:*" 23 | }, 24 | "main": "./dist/index.js", 25 | "module": "./dist/index.js", 26 | "exports": { 27 | ".": "./dist/index.js" 28 | }, 29 | "typesVersions": { 30 | "*": { 31 | ".": [ 32 | "./dist/index.d.ts" 33 | ] 34 | } 35 | }, 36 | "files": [ 37 | "dist/" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /packages/build/src/operations/common.ts: -------------------------------------------------------------------------------- 1 | import type { ParsedPath } from "node:path"; 2 | import type { FileContext } from "@batijs/core"; 3 | 4 | export interface FileOperation { 5 | source: string; 6 | sourceAbsolute: string; 7 | destination: string; 8 | destinationAbsolute: string; 9 | parsed: ParsedPath; 10 | kind: "file" | "transform"; 11 | important?: boolean; 12 | } 13 | 14 | export interface OperationReport { 15 | context?: FileContext; 16 | content?: string; 17 | } 18 | -------------------------------------------------------------------------------- /packages/build/src/queue.ts: -------------------------------------------------------------------------------- 1 | export type Task = () => Promise<unknown>; 2 | 3 | export function queue() { 4 | const tasks: Task[] = []; 5 | 6 | return { 7 | add(task: Task) { 8 | tasks.push(task); 9 | }, 10 | async run() { 11 | let task: Task | undefined; 12 | while ((task = tasks.shift())) { 13 | await task(); 14 | } 15 | }, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/build/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function orderBy<T, U>(array: T[], getter: (item: T) => U): T[] { 2 | return array.slice().sort((a, b) => { 3 | const valueA = getter(a); 4 | const valueB = getter(b); 5 | 6 | if (valueA < valueB) return -1; 7 | if (valueA > valueB) return 1; 8 | return 0; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/build/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "types": ["@types/node", "@batijs/core/types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/build/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["./src/index.ts"], 5 | bundle: true, 6 | clean: true, 7 | dts: true, 8 | format: ["esm"], 9 | outDir: "./dist", 10 | }); 11 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cli/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | import esbuildBundleAllPlugin from "./esbuild-bundle-all.js"; 3 | import { purgePolyfills } from "unplugin-purge-polyfills"; 4 | 5 | export default defineConfig({ 6 | entry: { 7 | index: "index.ts", 8 | }, 9 | format: ["esm"], 10 | outDir: "./dist", 11 | clean: true, 12 | bundle: true, 13 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 14 | esbuildPlugins: [esbuildBundleAllPlugin, purgePolyfills.esbuild({}) as any], 15 | platform: "node", 16 | banner: { 17 | js: `#!/usr/bin/env node 18 | import { createRequire as createRequire_BATI_CLI } from 'module'; 19 | const require = createRequire_BATI_CLI(import.meta.url); 20 | `, 21 | }, 22 | noExternal: ["@batijs/core", "@batijs/features"], 23 | }); 24 | -------------------------------------------------------------------------------- /packages/cli/types.ts: -------------------------------------------------------------------------------- 1 | import type { VikeMeta } from "@batijs/core"; 2 | 3 | export interface BatiConfig { 4 | if?: Record<string, unknown>; 5 | enforce?: "pre" | "post"; 6 | } 7 | 8 | export interface BoilerplateDef { 9 | folder: string; 10 | config: BatiConfig; 11 | subfolders: string[]; 12 | } 13 | 14 | export interface ToBeCopied extends BoilerplateDef { 15 | source?: string; 16 | } 17 | 18 | export type Hook = (cwd: string, meta: VikeMeta) => Promise<void> | void; 19 | -------------------------------------------------------------------------------- /packages/compile/README.md: -------------------------------------------------------------------------------- 1 | # @batijs/compile 2 | 3 | [bati](https://batijs.dev) internal utils. 4 | -------------------------------------------------------------------------------- /packages/compile/clean.ts: -------------------------------------------------------------------------------- 1 | import { rm } from "node:fs/promises"; 2 | import { join } from "node:path"; 3 | 4 | export async function clean() { 5 | await rm(join(process.cwd(), "dist"), { recursive: true, force: true }); 6 | } 7 | -------------------------------------------------------------------------------- /packages/compile/cli.js: -------------------------------------------------------------------------------- 1 | import "./dist/index.js"; 2 | -------------------------------------------------------------------------------- /packages/compile/copy.ts: -------------------------------------------------------------------------------- 1 | import { copyFile, mkdir } from "node:fs/promises"; 2 | import * as path from "path"; 3 | import { globby } from "globby"; 4 | 5 | export async function copyFilesToDist() { 6 | const files = await globby(["./files/**/!($*)", "./files/**/$$*", "!**/styled-system"], { 7 | cwd: process.cwd(), 8 | }); 9 | 10 | for (const file of files) { 11 | const dist = path.join("dist", file); 12 | const distDirname = path.dirname(dist); 13 | 14 | await mkdir(distDirname, { recursive: true }); 15 | await copyFile(file, dist); 16 | } 17 | 18 | console.log("Files copied to", path.join(process.cwd(), "dist")); 19 | } 20 | -------------------------------------------------------------------------------- /packages/compile/index.ts: -------------------------------------------------------------------------------- 1 | import { clean } from "./clean.js"; 2 | import { copyFilesToDist } from "./copy.js"; 3 | import { buildTypes } from "./dts.js"; 4 | import { build } from "./esbuild.js"; 5 | 6 | await clean(); 7 | await build(); 8 | await copyFilesToDist(); 9 | await buildTypes(); 10 | -------------------------------------------------------------------------------- /packages/compile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/compile", 3 | "private": true, 4 | "version": "0.0.437", 5 | "description": "", 6 | "type": "module", 7 | "scripts": { 8 | "check-types": "tsc --noEmit", 9 | "prepublishOnly": "tsup", 10 | "build": "tsup" 11 | }, 12 | "keywords": [], 13 | "author": "Joël Charles <joel.charles91@gmail.com>", 14 | "repository": "https://github.com/vikejs/bati", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@types/node": "^18.19.103", 18 | "typescript": "^5.8.3", 19 | "unplugin-purge-polyfills": "^0.1.0" 20 | }, 21 | "dependencies": { 22 | "esbuild": "^0.25.4", 23 | "globby": "^14.1.0", 24 | "tsc-prog": "^2.3.0", 25 | "tsup": "^8.5.0" 26 | }, 27 | "bin": { 28 | "bati-compile-boilerplate": "./cli.js" 29 | }, 30 | "files": [ 31 | "dist/", 32 | "cli.js" 33 | ], 34 | "bati": false 35 | } 36 | -------------------------------------------------------------------------------- /packages/compile/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "target": "ES2022" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/compile/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | import { purgePolyfills } from "unplugin-purge-polyfills"; 3 | 4 | export default defineConfig({ 5 | entry: ["index.ts"], 6 | clean: true, 7 | format: "esm", 8 | dts: true, 9 | outDir: "./dist", 10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 | esbuildPlugins: [purgePolyfills.esbuild({}) as any], 12 | }); 13 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @batijs/core 2 | 3 | [bati](https://batijs.dev) internal utils. 4 | -------------------------------------------------------------------------------- /packages/core/src/assert.ts: -------------------------------------------------------------------------------- 1 | export function assert(condition: unknown, message: string): asserts condition { 2 | if (condition) { 3 | return; 4 | } 5 | throw new Error(message); 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/format.ts: -------------------------------------------------------------------------------- 1 | import * as prettierPluginBabel from "prettier/plugins/babel"; 2 | import * as prettierPluginEstree from "prettier/plugins/estree"; 3 | import * as prettierPluginHtml from "prettier/plugins/html"; 4 | import * as prettierPluginCss from "prettier/plugins/postcss"; 5 | import * as prettierPluginTs from "prettier/plugins/typescript"; 6 | import { format } from "prettier/standalone"; 7 | 8 | export function formatCode(code: string, options: { filepath: string }): Promise<string> { 9 | return format(code, { 10 | plugins: [ 11 | prettierPluginBabel, 12 | prettierPluginTs, 13 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 14 | prettierPluginEstree as any, 15 | prettierPluginHtml, 16 | prettierPluginCss, 17 | ], 18 | filepath: options.filepath, 19 | printWidth: 120, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { transformAndFormat, type FileContext } from "./parse.js"; 2 | export { formatCode } from "./format.js"; 3 | export * from "./loaders.js"; 4 | export * from "./magicast.js"; 5 | export * from "./utils/env.js"; 6 | export * from "./which.js"; 7 | export * from "./print.js"; 8 | export * from "./markdown.js"; 9 | export * from "./markdown/markdown.js"; 10 | export * from "./random.js"; 11 | export * from "./runtime.js"; 12 | export type * from "./types.js"; 13 | -------------------------------------------------------------------------------- /packages/core/src/magicast.ts: -------------------------------------------------------------------------------- 1 | export { loadFile, parseModule, generateCode, builders } from "magicast"; 2 | export { addVitePlugin, deepMergeObject } from "magicast/helpers"; 3 | -------------------------------------------------------------------------------- /packages/core/src/markdown/createTOC.ts: -------------------------------------------------------------------------------- 1 | import type { Root, Nodes } from "mdast"; 2 | import { text, heading } from "mdast-builder"; 3 | import { toc } from "mdast-util-toc"; 4 | 5 | const tocHeading = "Contents"; 6 | 7 | export function createTOC(tree: Root): Nodes[] | null { 8 | const tocItems = toc(tree, { maxDepth: 4, minDepth: 2, skip: tocHeading }); 9 | if (tocItems?.map === undefined) { 10 | return null; 11 | } 12 | return [heading(2, text(tocHeading)), tocItems.map] as Nodes[]; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/parse/linters/index.ts: -------------------------------------------------------------------------------- 1 | import type { VikeMeta } from "../../types.js"; 2 | import { verifyAndFix } from "./common.js"; 3 | import tsLinterConfig from "./linter-ts.js"; 4 | import vueLinterConfig from "./linter-vue.js"; 5 | import pluginRemoveUnusedImports from "./plugin-remove-unused-imports.js"; 6 | 7 | export function transform(code: string, filename: string, meta: VikeMeta) { 8 | return verifyAndFix( 9 | code, 10 | [...tsLinterConfig(meta).config, ...vueLinterConfig(meta).config, ...pluginRemoveUnusedImports().config], 11 | filename, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/parse/linters/types.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from "@typescript-eslint/utils"; 2 | import type * as ESTree from "estree"; 3 | import type { AST } from "vue-eslint-parser"; 4 | 5 | export type Node = (AST.ESLintNode & TSESTree.Node) | AST.VNode | TSESTree.Node; 6 | 7 | export type Visitors<N extends ESTree.Node | AST.Node | TSESTree.Node = Node> = 8 | | { 9 | [P in N as P["type"]]?: (node: P) => void; 10 | } 11 | | { 12 | ":statement"?: (node: N) => void; 13 | ":expression"?: (node: N) => void; 14 | ":declaration"?: (node: N) => void; 15 | ":function"?: (node: N) => void; 16 | ":pattern"?: (node: N) => void; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/core/src/parse/squirelly.ts: -------------------------------------------------------------------------------- 1 | import { render } from "squirrelly"; 2 | import type { VikeMeta } from "../types.js"; 3 | 4 | // We use /*{ and }*/ as Squirrelly delimiters so that {{ and }} remain untouched in Vue SFC files, in which 5 | // they are used in Vue <template>s. 6 | // Also, /* */ is considered comments in CSS and JS, so it doesn't break syntax coloration on those files 7 | export const tags: [string, string] = ["/*{", "}*/"]; 8 | 9 | export function renderSquirrelly(template: string, meta: VikeMeta): string { 10 | return render(template, meta, { 11 | tags, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/print.ts: -------------------------------------------------------------------------------- 1 | import type { Color } from "colorette"; 2 | 3 | export * from "colorette"; 4 | 5 | export function withIcon(icon: string, iconColor?: Color, indentLevel = 0) { 6 | const pre = `${" ".repeat(indentLevel * 2)}${iconColor ? iconColor(icon) : icon}`; 7 | return (str: string) => `${pre} ${str}`; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/random.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "node:crypto"; 2 | 3 | export { randomBytes }; 4 | -------------------------------------------------------------------------------- /packages/core/src/relative.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | export function relative(a: string, b: string): string { 4 | const x = a.replace(/[\\/]+/g, "/"); 5 | const y = b.replace(/[\\/]+/g, "/"); 6 | 7 | const rel = path.posix.relative(path.dirname(x), y); 8 | 9 | return rel.startsWith("../") ? rel : "./" + rel; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/types.ts: -------------------------------------------------------------------------------- 1 | import { BatiSet } from "@batijs/features"; 2 | 3 | export type ContentGetter = () => string | Promise<string>; 4 | 5 | export interface VikeMeta { 6 | BATI: BatiSet; 7 | BATI_TEST?: boolean; 8 | } 9 | 10 | export type TransformerProps = { 11 | readfile?: ContentGetter; 12 | target: string; 13 | source: string; 14 | meta: VikeMeta; 15 | packageJson: PackageJson; 16 | }; 17 | 18 | export type Transformer = (props: TransformerProps) => unknown; 19 | 20 | export interface PackageJson { 21 | dependencies?: Record<string, string>; 22 | devDependencies?: Record<string, string>; 23 | } 24 | 25 | export interface StringTransformer { 26 | finalize(): string; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/utils/env.ts: -------------------------------------------------------------------------------- 1 | export function appendToEnv(envContent: string | undefined | null, key: string, value: unknown, comment?: string) { 2 | envContent ??= ""; 3 | if (envContent.endsWith("\n\n")) { 4 | // do nothing 5 | } else if (envContent.endsWith("\n")) { 6 | envContent = envContent + "\n"; 7 | } else if (envContent) { 8 | envContent = envContent + "\n\n"; 9 | } 10 | 11 | const prefixedComment = comment ? comment.replace(/^(.+)/gm, "# $1") + "\n" : ""; 12 | const newConf = `${key}=${formatValue(value)}\n`; 13 | 14 | return envContent + prefixedComment + newConf; 15 | } 16 | 17 | function formatValue(value: unknown): string { 18 | let strval = ""; 19 | switch (typeof value) { 20 | case "string": 21 | if (value) { 22 | strval = JSON.stringify(value); 23 | } 24 | break; 25 | case "boolean": 26 | case "number": 27 | strval = String(value); 28 | break; 29 | case "undefined": 30 | strval = ""; 31 | break; 32 | case "object": 33 | if (value !== null) { 34 | strval = JSON.stringify(value); 35 | } 36 | break; 37 | } 38 | 39 | return strval; 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/src/which.ts: -------------------------------------------------------------------------------- 1 | import which from "which"; 2 | 3 | export { which }; 4 | -------------------------------------------------------------------------------- /packages/core/tests/replace-bati-imports.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { relative } from "../src/relative.js"; 3 | 4 | describe.each([ 5 | { a: "pages/todo-trpc/+Page.tsx", b: "server/trpc/trpc", expected: "../../server/trpc/trpc" }, 6 | { a: "pages\\todo-trpc\\+Page.tsx", b: "server/trpc/trpc", expected: "../../server/trpc/trpc" }, 7 | { a: "pages/todo-trpc/+Page.tsx", b: "express-entry", expected: "../../express-entry" }, 8 | { a: "pages\\todo-trpc\\+Page.tsx", b: "express-entry", expected: "../../express-entry" }, 9 | { a: "+Page.tsx", b: "server/trpc/trpc", expected: "./server/trpc/trpc" }, 10 | { a: "+Page.tsx", b: "express-entry", expected: "./express-entry" }, 11 | { a: "pages/trpc/+Page.tsx", b: "pages/trpc/trpc", expected: "./trpc" }, 12 | { a: "pages\\trpc\\+Page.tsx", b: "pages/trpc/trpc", expected: "./trpc" }, 13 | { a: "pages/trpc/+Page.tsx", b: "pages/another-page/page", expected: "../another-page/page" }, 14 | ])("rewrite $a: $b", ({ a, b, expected }: Record<"a" | "b" | "expected", string>) => { 15 | test(`returns ${expected}`, () => { 16 | expect(relative(a, b)).toBe(expected); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/type-utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unused-vars */ 2 | 3 | export type Values<T> = T extends Record<any, infer T> ? T : never; 4 | export type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends (arg: infer I) => void 5 | ? I 6 | : never; 7 | -------------------------------------------------------------------------------- /packages/create-bati/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "@batijs/cli"; 3 | -------------------------------------------------------------------------------- /packages/create-bati/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-vike", 3 | "version": "0.0.437", 4 | "type": "module", 5 | "scripts": {}, 6 | "keywords": [], 7 | "description": "Next-gen scaffolder. Get started with fully-functional apps, and choose any tool you want", 8 | "author": "Joël Charles <joel.charles91@gmail.com>", 9 | "repository": "https://github.com/vikejs/bati", 10 | "license": "MIT", 11 | "bin": "./index.js", 12 | "devDependencies": {}, 13 | "dependencies": { 14 | "@batijs/cli": "workspace:*" 15 | }, 16 | "files": [ 17 | "index.js" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/create-batijs-app/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "@batijs/cli"; 3 | -------------------------------------------------------------------------------- /packages/create-batijs-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/create-app", 3 | "version": "0.0.437", 4 | "type": "module", 5 | "scripts": {}, 6 | "keywords": [], 7 | "description": "Next-gen scaffolder. Get started with fully-functional apps, and choose any tool you want", 8 | "author": "Joël Charles <joel.charles91@gmail.com>", 9 | "repository": "https://github.com/vikejs/bati", 10 | "license": "MIT", 11 | "bin": "./index.js", 12 | "devDependencies": {}, 13 | "dependencies": { 14 | "@batijs/cli": "workspace:*" 15 | }, 16 | "files": [ 17 | "index.js" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/features/README.md: -------------------------------------------------------------------------------- 1 | # @batijs/features 2 | 3 | All features that should be visible in the WebUI and the CLI are defined in [src/features.ts](src/features.ts). 4 | 5 | All rules (conflicts/dependencies between features, features in beta, etc.) are defined in [src/rules](src/rules). 6 | -------------------------------------------------------------------------------- /packages/features/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/features", 3 | "version": "0.0.437", 4 | "description": "", 5 | "type": "module", 6 | "scripts": { 7 | "test": "vitest run", 8 | "test:ci": "vitest run", 9 | "prepublishOnly": "pnpm run build", 10 | "build": "tsup", 11 | "watch": "tsup --watch" 12 | }, 13 | "keywords": [], 14 | "author": "Joël Charles <joel.charles91@gmail.com>", 15 | "repository": "https://github.com/vikejs/bati", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@types/node": "^18.19.103", 19 | "tsup": "^8.5.0", 20 | "vitest": "^3.1.4" 21 | }, 22 | "main": "./dist/index.js", 23 | "module": "./dist/index.js", 24 | "exports": { 25 | ".": "./dist/index.js", 26 | "./rules": "./dist/rules.js" 27 | }, 28 | "typesVersions": { 29 | "*": { 30 | ".": [ 31 | "./dist/index.d.ts" 32 | ], 33 | "rules": [ 34 | "./dist/rules.d.ts" 35 | ] 36 | } 37 | }, 38 | "files": [ 39 | "dist/" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /packages/features/src/groups.ts: -------------------------------------------------------------------------------- 1 | export enum categoriesGroups { 2 | Frontend = "Frontend", 3 | Data = "Data", 4 | Deployment = "Deployment", 5 | Utilities = "Utilities", 6 | } 7 | -------------------------------------------------------------------------------- /packages/features/src/index.ts: -------------------------------------------------------------------------------- 1 | export type * from "./types.js"; 2 | export * from "./groups.js"; 3 | export * from "./categories.js"; 4 | export * from "./features.js"; 5 | export * from "./helpers.js"; 6 | -------------------------------------------------------------------------------- /packages/features/src/rules/enum.ts: -------------------------------------------------------------------------------- 1 | export enum RulesMessage { 2 | // --- ERROR 3 | // A Server is required when using Auth 4 | ERROR_AUTH_R_SERVER, 5 | // React is required when using Compiled CSS 6 | ERROR_COMPILED_R_REACT, 7 | // A Server is required when using Drizzle 8 | ERROR_DRIZZLE_R_SERVER, 9 | // A Server is required when using Data fetching / RPC 10 | ERROR_DATA_R_SERVER, 11 | // A compabible Database is required when using lucia-auth 12 | ERROR_LUCIA_R_COMPAT_DATABASE, 13 | // A compabible Server (or no Server) is required when using Cloudflare 14 | ERROR_CLOUDFLARE_R_COMPAT_SERVER, 15 | // A compabible Server (or no Server) is required when using Cloudflare 16 | ERROR_AWS_R_COMPAT_SERVER, 17 | // React is required when using Mantine UI Components Framework 18 | ERROR_MANTINE_R_REACT, 19 | // shadcn/ui is only compatible with React 20 | ERROR_SHADCN_R_REACT, 21 | 22 | // --- WARNING 23 | // shadcn/ui integration is tailored for tailwind 24 | WARN_SHADCN_R_TAILWINDCSS, 25 | 26 | // --- INFO 27 | // HatTip is an experimental project 28 | INFO_HATTIP, 29 | // Some tools do not work on Stackblitz 30 | INFO_STACKBLITZ_COMPAT, 31 | } 32 | -------------------------------------------------------------------------------- /packages/features/src/rules/index.ts: -------------------------------------------------------------------------------- 1 | import { RulesMessage } from "./enum.js"; 2 | import rules from "./rules.js"; 3 | import { prepare, type FeatureOrCategory } from "./utils.js"; 4 | 5 | export { rules, RulesMessage }; 6 | 7 | export function execRules<T>(fts: FeatureOrCategory[], rulesMessages: Record<RulesMessage, T>) { 8 | const sfts = prepare(fts); 9 | const messages: T[] = []; 10 | 11 | for (const rule of rules) { 12 | const result = rule(sfts); 13 | if (typeof result === "number") { 14 | if (result in rulesMessages) { 15 | messages.push(rulesMessages[result]); 16 | } else { 17 | console.warn("No handler defined for rule", result); 18 | } 19 | } 20 | } 21 | 22 | return messages; 23 | } 24 | -------------------------------------------------------------------------------- /packages/features/src/types.ts: -------------------------------------------------------------------------------- 1 | import { categoriesGroups } from "./groups.js"; 2 | 3 | export interface Feature<C = string> { 4 | label: string; 5 | flag: string; 6 | category: C; 7 | image?: string; 8 | url?: string; 9 | description?: string; 10 | dependsOn?: ReadonlyArray<string>; 11 | spectrum?: "beaten_path" | "bleeding_edge"; 12 | tagline?: string; 13 | links?: FeatureLink[]; 14 | repo?: string; 15 | // if true, it means that the feature is not yet implemented, but could be displayed in the UI 16 | disabled?: boolean; 17 | // if true, do not display in the CLI 18 | invisibleCli?: boolean; 19 | // if true, cannot be toggled off (implies selected by default, otherwise use `disabled`) 20 | readonly?: boolean; 21 | selected?: boolean; 22 | } 23 | 24 | export interface FeatureLink { 25 | label: string; 26 | href: string; 27 | } 28 | 29 | export interface Category { 30 | label: string; 31 | group: categoriesGroups; 32 | // like <select multiple/> 33 | multiple?: boolean; 34 | required?: boolean; 35 | description?: string; 36 | } 37 | -------------------------------------------------------------------------------- /packages/features/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/features/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: { index: "./src/index.ts", rules: "./src/rules/index.ts" }, 5 | platform: "neutral", 6 | format: "esm", 7 | target: "es2022", 8 | bundle: true, 9 | clean: true, 10 | dts: true, 11 | outDir: "./dist", 12 | esbuildOptions(options) { 13 | options.mainFields = ["module", "main"]; 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/tests-utils/README.md: -------------------------------------------------------------------------------- 1 | Utils for https://github.com/vikejs/bati/tree/main/packages/tests 2 | -------------------------------------------------------------------------------- /packages/tests-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/tests-utils", 3 | "private": true, 4 | "version": "0.0.437", 5 | "type": "module", 6 | "scripts": { 7 | "check-types": "tsc --noEmit", 8 | "build": "tsup" 9 | }, 10 | "keywords": [], 11 | "description": "Next-gen scaffolder. Get started with fully-functional apps, and choose any tool you want", 12 | "author": "Joël Charles <joel.charles91@gmail.com>", 13 | "repository": "https://github.com/vikejs/bati", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@types/node": "^18.19.103", 17 | "@types/which": "^3.0.4", 18 | "tsup": "^8.5.0", 19 | "tsx": "^4.19.4", 20 | "typescript": "^5.8.3" 21 | }, 22 | "dependencies": { 23 | "get-port": "^7.1.0", 24 | "node-fetch": "^3.3.2", 25 | "vitest": "^3.1.4", 26 | "which": "^5.0.0", 27 | "zx": "^8.5.4" 28 | }, 29 | "main": "./dist/index.js", 30 | "module": "./dist/index.js", 31 | "types": "./dist/index.d.ts", 32 | "exports": { 33 | ".": "./dist/index.js" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/tests-utils/src/combinate.ts: -------------------------------------------------------------------------------- 1 | import type { FlagMatrix } from "./types.js"; 2 | 3 | export function combinate<O extends FlagMatrix>(obj: O) { 4 | let combos: string[][] = []; 5 | if (obj.length === 0) return [[]]; 6 | 7 | for (const val of obj) { 8 | const values = Array.isArray(val) ? val : [val]; 9 | const all: string[][] = []; 10 | for (const x of values) { 11 | if (combos.length === 0) { 12 | all.push([x].filter(Boolean)); 13 | } else { 14 | for (let j = 0; j < combos.length; j++) { 15 | all.push([...combos[j], x].filter(Boolean)); 16 | } 17 | } 18 | } 19 | combos = all; 20 | } 21 | return combos; 22 | } 23 | -------------------------------------------------------------------------------- /packages/tests-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./combinate.js"; 2 | export * from "./describe.js"; 3 | export * from "./exec.js"; 4 | export * from "./package-manager.js"; 5 | export * from "./port.js"; 6 | export * from "./prepare.js"; 7 | export * from "./run-build.js"; 8 | export * from "./run-dev.js"; 9 | export type * from "./types.js"; 10 | export * from "./wait-for-localhost.js"; 11 | export * from "./zx.js"; 12 | -------------------------------------------------------------------------------- /packages/tests-utils/src/package-manager.ts: -------------------------------------------------------------------------------- 1 | import which from "which"; 2 | 3 | export const bunExists = which.sync("bun", { nothrow: true }) !== null; 4 | export const npmCli = bunExists && !process.env.FORCE_PNPM ? "bun" : "pnpm"; 5 | -------------------------------------------------------------------------------- /packages/tests-utils/src/port.ts: -------------------------------------------------------------------------------- 1 | import getPort from "get-port"; 2 | import type { GlobalContext } from "./types.js"; 3 | 4 | export async function initPort(context: GlobalContext) { 5 | context.port = await getPort(); 6 | context.port_1 = await getPort(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/tests-utils/src/run-build.ts: -------------------------------------------------------------------------------- 1 | import { exec } from "./exec.js"; 2 | import { npmCli } from "./package-manager.js"; 3 | import type { GlobalContext } from "./types.js"; 4 | 5 | export async function runBuild(context: GlobalContext) { 6 | context.server = exec(npmCli, ["run", "build"], { 7 | timeout: 60 * 1000, // 1min 8 | env: { 9 | NODE_ENV: "production", 10 | }, 11 | }); 12 | 13 | await context.server; 14 | 15 | return { server: context.server }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/tests-utils/src/run-dev.ts: -------------------------------------------------------------------------------- 1 | import { exec } from "./exec.js"; 2 | import { npmCli } from "./package-manager.js"; 3 | import type { GlobalContext } from "./types.js"; 4 | import { waitForLocalhost } from "./wait-for-localhost.js"; 5 | 6 | export async function runDevServer(context: GlobalContext) { 7 | context.server = exec(npmCli, ["run", "dev", "--port", String(context.port)], { 8 | env: { 9 | PORT: String(context.port), 10 | HMR_PORT: String(context.port_1), 11 | VITE_CONFIG: JSON.stringify({ server: { port: context.port, strictPort: true } }), 12 | }, 13 | }); 14 | 15 | const res = await Promise.race([ 16 | // wait for port 17 | waitForLocalhost({ port: context.port, useGet: true, timeout: process.env.CI ? 20000 : 5000 }), 18 | // or for server to crash 19 | context.server, 20 | ]); 21 | 22 | if (!res) { 23 | throw new Error("Server stopped before tests could run"); 24 | } 25 | 26 | return { server: context.server, port: context.port }; 27 | } 28 | -------------------------------------------------------------------------------- /packages/tests-utils/src/zx.ts: -------------------------------------------------------------------------------- 1 | import * as zx from "zx"; 2 | 3 | export { zx }; 4 | -------------------------------------------------------------------------------- /packages/tests-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/tests-utils/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["./src/index.ts"], 5 | platform: "node", 6 | format: "esm", 7 | target: "es2022", 8 | outDir: "./dist", 9 | dts: true, 10 | bundle: true, 11 | minify: false, 12 | clean: true, 13 | esbuildOptions(options) { 14 | options.mainFields = ["module", "main"]; 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/tests/README.md: -------------------------------------------------------------------------------- 1 | This repo doesn't run tests directly unless they are `*.local.spec.ts` files. 2 | All other tests in `tests` folder follow these steps: 3 | - Create temp folder for a monorepo basis (usually _/tmp/bati_) 4 | - Call Bati CLI for all combinations defined by tests `matrix` exports in _<temp folder>/packages/_ 5 | - Copy necessary tests to the right _<temp folder>/packages/<repo hash>_ folder 6 | - Update package.json of those repos 7 | - Create a vitest config in each of those repos 8 | - Create package.json amd Turborepo config in workspace root 9 | - Call _pnpm/bun install_ in monorepo 10 | - Call `turborepo run test lint build` in monorepo 11 | - Handles Turborepo cache via _/tmp/bati-cache_ folder 12 | -------------------------------------------------------------------------------- /packages/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@batijs/tests", 3 | "private": true, 4 | "version": "0.0.437", 5 | "type": "module", 6 | "scripts": { 7 | "check-types": "tsc --noEmit", 8 | "start": "tsx src/index.ts", 9 | "test": "vitest run", 10 | "test:e2e": "tsx src/index.ts", 11 | "build": "tsup" 12 | }, 13 | "keywords": [], 14 | "description": "Next-gen scaffolder. Get started with fully-functional apps, and choose any tool you want", 15 | "author": "Joël Charles <joel.charles91@gmail.com>", 16 | "repository": "https://github.com/vikejs/bati", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@actions/core": "^1.11.1", 20 | "@batijs/core": "workspace:*", 21 | "@batijs/features": "workspace:^", 22 | "@batijs/tests-utils": "workspace:*", 23 | "@types/node": "^18.19.103", 24 | "@types/which": "^3.0.4", 25 | "dotenv": "^16.5.0", 26 | "fast-glob": "^3.3.3", 27 | "knip": "^5.58.0", 28 | "mri": "^1.2.0", 29 | "p-limit": "^6.2.0", 30 | "tsup": "^8.5.0", 31 | "tsx": "^4.19.4", 32 | "turbo": "2.4.4", 33 | "typescript": "^5.8.3", 34 | "vitest": "^3.1.4", 35 | "yaml": "^2.8.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/tests/src/tmp.ts: -------------------------------------------------------------------------------- 1 | import { mkdir, rm } from "node:fs/promises"; 2 | import { tmpdir } from "node:os"; 3 | import { join } from "node:path"; 4 | import type { GlobalContext } from "./types.js"; 5 | import process from "process"; 6 | 7 | export async function initTmpDir(context: GlobalContext) { 8 | // turborepo hash seems to include cwd(), so always use the same temp folder 9 | // context.tmpdir = await mkdtemp(join(tmpdir(), "bati-")); 10 | context.tmpdir = join(process.env.CI ? process.env.RUNNER_TEMP || tmpdir() : tmpdir(), "bati"); 11 | 12 | // remove previous tests if any 13 | await rm(context.tmpdir, { recursive: true, force: true, maxRetries: 2 }); 14 | 15 | // create directories 16 | await mkdir(context.tmpdir); 17 | await mkdir(join(context.tmpdir, "packages")); 18 | } 19 | -------------------------------------------------------------------------------- /packages/tests/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface GlobalContext { 2 | localRepository?: boolean; 3 | tmpdir: string; 4 | } 5 | -------------------------------------------------------------------------------- /packages/tests/tests/FRAMEWORK+CSS.spec.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from "node:fs/promises"; 2 | import { describeBati } from "@batijs/tests-utils"; 3 | import { existsSync } from "node:fs"; 4 | import path from "node:path"; 5 | 6 | export const matrix = [["solid", "react", "vue"], ["tailwindcss", "daisyui", "panda-css"], "eslint"]; 7 | 8 | await describeBati(({ test, expect, fetch, testMatch, context }) => { 9 | test("home", async () => { 10 | const res = await fetch("/"); 11 | expect(res.status).toBe(200); 12 | expect(await res.text()).not.toContain('{"is404":true}'); 13 | }); 14 | 15 | testMatch<typeof matrix>("config exists", { 16 | daisyui: async () => { 17 | const content = await readFile("layouts/tailwind.css", "utf-8"); 18 | expect(content.includes("daisyui")).toBe(context.flags.includes("daisyui")); 19 | }, 20 | "panda-css": async () => { 21 | expect(existsSync(path.join(process.cwd(), "panda.config.ts"))).toBe(true); 22 | expect(existsSync(path.join(process.cwd(), "layouts", "panda.css"))).toBe(true); 23 | }, 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/tests/tests/FRAMEWORK+prettier.spec.ts: -------------------------------------------------------------------------------- 1 | import { describeBati } from "@batijs/tests-utils"; 2 | 3 | export const matrix = [["solid", "react", "vue"], ["eslint", "biome"], "prettier"]; 4 | 5 | await describeBati(({ test, expect, fetch }) => { 6 | test("home", async () => { 7 | const res = await fetch("/"); 8 | expect(res.status).toBe(200); 9 | expect(await res.text()).not.toContain('{"is404":true}'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/tests/tests/FRAMEWORK+prisma.spec.ts: -------------------------------------------------------------------------------- 1 | import { describeBati } from "@batijs/tests-utils"; 2 | 3 | export const matrix = [["solid", "react", "vue"], "prisma", "eslint"]; 4 | 5 | await describeBati(({ test, expect, fetch }) => { 6 | test("home", async () => { 7 | const res = await fetch("/"); 8 | expect(res.status).toBe(200); 9 | expect(await res.text()).not.toContain('{"is404":true}'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/tests/tests/FRAMEWORK+vercel+express.spec.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from "node:fs"; 2 | import { readFile } from "node:fs/promises"; 3 | import path from "node:path"; 4 | import * as process from "process"; 5 | import { describeBati } from "@batijs/tests-utils"; 6 | 7 | export const matrix = ["react", "vercel", "express", "eslint"]; 8 | 9 | await describeBati( 10 | ({ test, expect }) => { 11 | test("express dev script prevails", async () => { 12 | const json = JSON.parse(await readFile(path.join(process.cwd(), "package.json"), "utf-8")); 13 | 14 | expect(json.scripts.dev).toContain("tsx ./express-entry.ts"); 15 | }); 16 | 17 | test("vercel files are present", async () => { 18 | expect(existsSync(path.join(process.cwd(), ".vercel", "output", "config.json"))).toBe(true); 19 | expect( 20 | existsSync(path.join(process.cwd(), ".vercel", "output", "functions", "ssr_.func", ".vc-config.json")), 21 | ).toBe(true); 22 | }); 23 | }, 24 | { 25 | mode: "build", 26 | retry: 3, 27 | }, 28 | ); 29 | -------------------------------------------------------------------------------- /packages/tests/tests/react+UI.spec.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from "node:fs/promises"; 2 | import { describeBati } from "@batijs/tests-utils"; 3 | 4 | export const matrix = ["react", ["compiled-css", "mantine"], "eslint"] as const; 5 | 6 | await describeBati(({ test, expect, fetch, testMatch }) => { 7 | test("home", async () => { 8 | const res = await fetch("/"); 9 | expect(res.status).toBe(200); 10 | expect(await res.text()).not.toContain('{"is404":true}'); 11 | }); 12 | 13 | testMatch<typeof matrix>("ui lib", { 14 | "compiled-css": async () => { 15 | const content = await readFile("package.json", "utf-8"); 16 | expect(content.includes("@compiled/react")).toBe(true); 17 | }, 18 | mantine: async () => { 19 | const content = await readFile("layouts/LayoutDefault.tsx", "utf-8"); 20 | expect(content.includes("@mantine/core/styles.css")).toBe(true); 21 | }, 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.base.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/tests/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["./src/prepare.ts"], 5 | platform: "node", 6 | format: "esm", 7 | target: "es2022", 8 | outDir: "./dist", 9 | dts: false, 10 | bundle: true, 11 | minify: false, 12 | clean: true, 13 | esbuildOptions(options) { 14 | options.mainFields = ["module", "main"]; 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/tests/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vitest" /> 2 | import { defineConfig } from "vitest/config"; 3 | 4 | export default defineConfig({ 5 | test: { 6 | include: ["*.local.spec.ts"], 7 | testTimeout: 100000, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../tsconfig.json"], 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "preserveSymlinks": true 7 | }, 8 | "exclude": ["*/dist/**/*", "*/node_modules/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | - "boilerplates/*" 4 | - "website" 5 | -------------------------------------------------------------------------------- /render-gif.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | terminalizer render doc/demo --output doc/demo.gif 4 | -------------------------------------------------------------------------------- /scripts/helpers/boilerplates.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from "node:fs"; 2 | import { opendir, readFile } from "node:fs/promises"; 3 | import { dirname, join, resolve } from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = dirname(__filename); 8 | const __boilerplates = resolve(__dirname, "..", "..", "boilerplates"); 9 | 10 | export async function* walk(dir: string, maxDepth = Infinity): AsyncGenerator<string> { 11 | if (maxDepth < 0 || !existsSync(dir)) return; 12 | for await (const d of await opendir(dir)) { 13 | const entry = join(dir, d.name); 14 | if (d.isDirectory()) { 15 | yield* walk(entry, maxDepth - 1); 16 | } else if (d.isFile()) yield entry; 17 | } 18 | } 19 | 20 | export async function* listBoilerplates(): AsyncGenerator<string> { 21 | const gen = walk(__boilerplates, 1); 22 | 23 | for await (const filepath of gen) { 24 | if (filepath.endsWith("package.json")) { 25 | const content = JSON.parse(await readFile(filepath, "utf-8")); 26 | 27 | yield content.name; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scripts/screenshot.js: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import createBrowser from "browserless"; 3 | import { writeFile } from "node:fs/promises"; 4 | 5 | const browser = createBrowser(); 6 | 7 | const browserless = await browser.createContext(); 8 | 9 | const buffer = await browserless.screenshot("http://batijs.dev", { 10 | element: ".bati-widget", 11 | type: "png", 12 | click: ["[data-flag=react]", "[data-flag=tailwindcss]", "[data-flag=telefunc]", "[data-flag=hono]"], 13 | viewport: { 14 | deviceScaleFactor: 1, 15 | }, 16 | }); 17 | 18 | await writeFile("./doc/screenshot.png", buffer); 19 | 20 | await browserless.destroyContext(); 21 | 22 | // At the end, gracefully shutdown the browser process 23 | await browser.close(); 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "resolveJsonModule": true, 7 | "skipLibCheck": true, 8 | "sourceMap": true, 9 | "verbatimModuleSyntax": true, 10 | "noEmit": true, 11 | "module": "NodeNext", 12 | "moduleResolution": "NodeNext", 13 | "target": "ES2022", 14 | "lib": ["ES2022"] 15 | }, 16 | "exclude": ["**/dist/**/*"] 17 | } 18 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "tasks": { 4 | "build": { 5 | "inputs": ["$TURBO_DEFAULT$", "files/**", "hooks/**"], 6 | "dependsOn": ["^build"], 7 | "outputs": ["dist/**"] 8 | }, 9 | "test": { 10 | "dependsOn": ["build"] 11 | }, 12 | "check-types": {} 13 | }, 14 | "daemon": false 15 | } 16 | -------------------------------------------------------------------------------- /update-gif.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | scriptDir=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") 4 | tempD=$(mktemp -d) 5 | 6 | cd $tempD 7 | 8 | # Then run the following commands 9 | # pnpm --loglevel=error create bati --react --tailwindcss --telefunc --hono 10 | # cd my-app 11 | # pnpm install 12 | # pnpm run dev 13 | terminalizer record $scriptDir/doc/demo -k -c $scriptDir/terminalizer-config.yml 14 | 15 | # Then some manual edit is necessary 16 | 17 | cd - 18 | 19 | rm -fr $tempD 20 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # batijs.dev 2 | -------------------------------------------------------------------------------- /website/components/Flip.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | /** 4 | * @see {@link https://developer.chrome.com/docs/web-platform/view-transitions} 5 | */ 6 | 7 | import type { Accessor } from "solid-js"; 8 | 9 | declare module "solid-js" { 10 | namespace JSX { 11 | interface Directives { 12 | flip: string; 13 | } 14 | } 15 | } 16 | 17 | export function flip(el: HTMLElement, accessor: Accessor<string | undefined>) { 18 | const name = accessor(); 19 | if (!name) return; 20 | el.setAttribute("data-flip-name", name); 21 | (el.style as any).viewTransitionName = name; 22 | } 23 | 24 | export function startViewTransition(name: string, callback: () => void) { 25 | const ref: HTMLElement | null = document.querySelector(`[data-flip-name="${name}"]`); 26 | 27 | if (!ref || !("startViewTransition" in document)) { 28 | callback(); 29 | return; 30 | } 31 | 32 | (ref.style as any).viewTransitionName = name; 33 | 34 | (document.startViewTransition as any)(() => { 35 | (ref.style as any).viewTransitionName = ""; 36 | 37 | callback(); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /website/components/Logo.tsx: -------------------------------------------------------------------------------- 1 | import logoUrl from "#assets/logo.svg"; 2 | 3 | export function Logo(props: { size: number; class?: string }) { 4 | return <img alt="Bati logo" src={logoUrl} height={props.size} width={props.size} class={props.class} />; 5 | } 6 | -------------------------------------------------------------------------------- /website/layouts/LayoutDefault.tsx: -------------------------------------------------------------------------------- 1 | import "./tailwind.css"; 2 | import { StoreProvider } from "#components/Store"; 3 | import type { JSX } from "solid-js"; 4 | 5 | export default function LayoutDefault(props: { children?: JSX.Element }) { 6 | return ( 7 | <StoreProvider> 8 | <div class="flex flex-col mx-auto">{props.children}</div> 9 | </StoreProvider> 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /website/pages/+config.ts: -------------------------------------------------------------------------------- 1 | import faviconUrl from "#assets/logo.svg"; 2 | import vikeSolid from "vike-solid/config"; 3 | import type { Config } from "vike/types"; 4 | 5 | export default { 6 | prerender: true, 7 | favicon: faviconUrl, 8 | extends: vikeSolid, 9 | } satisfies Config; 10 | -------------------------------------------------------------------------------- /website/pages/_error/+Page.tsx: -------------------------------------------------------------------------------- 1 | import { usePageContext } from "vike-solid/usePageContext"; 2 | 3 | export default function Page() { 4 | const { is404 } = usePageContext(); 5 | if (is404) { 6 | return ( 7 | <> 8 | <h1>404 Page Not Found</h1> 9 | <p>This page could not be found.</p> 10 | </> 11 | ); 12 | } else { 13 | return ( 14 | <> 15 | <h1>500 Internal Server Error</h1> 16 | <p>Something went wrong.</p> 17 | </> 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /website/pages/index/+config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "vike/types"; 2 | import Head from "../../layouts/Head.js"; 3 | import Layout from "../../layouts/LayoutDefault.js"; 4 | 5 | export default { 6 | Layout: Layout, 7 | Head: Head, 8 | } satisfies Config; 9 | -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "resolveJsonModule": true, 7 | "skipLibCheck": true, 8 | "sourceMap": true, 9 | "noEmit": true, 10 | "module": "ESNext", 11 | "moduleResolution": "Bundler", 12 | "verbatimModuleSyntax": true, 13 | "target": "ES2022", 14 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 15 | "types": ["vite/client", "vike-solid/client"], 16 | "jsx": "preserve", 17 | "jsxImportSource": "solid-js", 18 | "paths": { 19 | "#components/*": ["./components/*"], 20 | "#assets/*": ["./assets/*"], 21 | "#layouts/*": ["./layouts/*"] 22 | } 23 | }, 24 | "exclude": ["./dist"] 25 | } 26 | -------------------------------------------------------------------------------- /website/types.ts: -------------------------------------------------------------------------------- 1 | import type { Feature as FeatureBase } from "@batijs/features"; 2 | 3 | export interface Feature extends FeatureBase { 4 | alt?: string; 5 | selected?: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /website/widget/AppWidget.tsx: -------------------------------------------------------------------------------- 1 | import { Widget } from "#components/Widget"; 2 | import css from "#layouts/tailwind.css?inline"; 3 | 4 | import { StoreProvider } from "#components/Store"; 5 | import type { JSX } from "solid-js"; 6 | 7 | function LayoutWidget(props: { children?: JSX.Element }) { 8 | return ( 9 | <StoreProvider> 10 | <style>{css}</style> 11 | {props.children} 12 | </StoreProvider> 13 | ); 14 | } 15 | 16 | export default function AppWidget(props: { theme?: string }) { 17 | return ( 18 | <LayoutWidget> 19 | <Widget widget={true} theme={props.theme} /> 20 | </LayoutWidget> 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /website/widget/web-component.index.ts: -------------------------------------------------------------------------------- 1 | import { customElement } from "solid-element"; 2 | import AppWidget from "./AppWidget.js"; 3 | 4 | customElement("bati-widget", { theme: "" }, AppWidget); 5 | --------------------------------------------------------------------------------