Skip to content

Commit

Permalink
#3748 Add custom template option back to blitz generate command (#3866)
Browse files Browse the repository at this point in the history
  • Loading branch information
siddhsuresh authored Oct 12, 2022
1 parent 149b999 commit f39ba1f
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/slow-impalas-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"blitz": patch
"@blitzjs/generator": patch
---

Allow passing custom templates to the `blitz generate` command. Extend the `generate` command with `custom-templates` to provide an easy starting point for users to customize the default templates: `blitz generate custom-templates`.
5 changes: 5 additions & 0 deletions apps/toolkit-app/app/blitz-server.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { BlitzCliConfig } from "blitz"
import { setupBlitzServer } from "@blitzjs/next"
import { AuthServerPlugin, PrismaStorage } from "@blitzjs/auth"
import db from "db"
Expand All @@ -14,3 +15,7 @@ const { gSSP, gSP, api } = setupBlitzServer({
})

export { gSSP, gSP, api }

export const cliConfig: BlitzCliConfig = {
customTemplates: "app/templates",
}
59 changes: 59 additions & 0 deletions packages/blitz/src/cli/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
MutationsGenerator,
ModelGenerator,
QueryGenerator,
addCustomTemplatesBlitzConfig,
} from "@blitzjs/generator"
import {log} from "../../logging"

const getIsTypeScript = async () =>
require("fs").existsSync(require("path").join(process.cwd(), "tsconfig.json"))
Expand All @@ -30,6 +32,7 @@ enum ResourceType {
Mutations = "mutations",
Mutation = "mutation",
Resource = "resource",
CustomTemplates = "custom-templates",
}

function modelName(input: string = "") {
Expand All @@ -45,6 +48,39 @@ function ModelNames(input: string = "") {
return pluralPascal(input)
}

const createCustomTemplates = async () => {
const continuePrompt = await prompts({
type: "confirm",
name: "value",
message: `This will copy the default templates to your app/templates folder. Do you want to continue?`,
})
if (!continuePrompt.value) {
process.exit(0)
}
const templatesPath = await prompts({
type: "text",
name: "value",
message: `Enter the path to save the custom templates folder`,
initial: "app/templates",
})
const templatesPathValue: string = templatesPath.value
const isTypeScript = await getIsTypeScript()
addCustomTemplatesBlitzConfig(templatesPathValue, isTypeScript)
log.success(`🚀 Custom templates path added/updated in app/blitz-server file`)
const customTemplatesPath = require("path").join(process.cwd(), templatesPathValue)
const fsExtra = await import("fs-extra")
const blitzGeneratorPath = require.resolve("@blitzjs/generator")
const templateFolder = ["form", "page", "query", "mutation", "queries", "mutations"]
for (const template of templateFolder) {
await fsExtra.copy(
require("path").join(blitzGeneratorPath, "..", "templates", template),
require("path").join(customTemplatesPath, template),
)
}
log.success(`🚀 Custom templates created in ${templatesPathValue} directory`)
process.exit(0)
}

const generatorMap = {
[ResourceType.All]: [
PageGenerator,
Expand All @@ -61,6 +97,7 @@ const generatorMap = {
[ResourceType.Mutations]: [MutationsGenerator],
[ResourceType.Mutation]: [MutationGenerator],
[ResourceType.Resource]: [QueriesGenerator, MutationsGenerator, ModelGenerator],
[ResourceType.CustomTemplates]: [],
}

const args = arg(
Expand Down Expand Up @@ -199,6 +236,13 @@ const getHelp = async () => {
> blitz generate all tasks --parent=projects
# To customize the templates used by the blitz generate command,
> blitz generate custom-templates
This command will copy the default templates to your app and update the app/blitz-server file to enable
the custom templating feature of the blitz CLI
# Database models can also be generated directly from the CLI.
Model fields can be specified with any generator that generates a database model ("all", "model", "resource").
Both of the commands below will generate the proper database model for a Task.
Expand All @@ -216,6 +260,9 @@ const getHelp = async () => {
const generate: CliCommand = async () => {
await getHelp()
await determineType()
if (selectedType === "custom-templates") {
await createCustomTemplates()
}
if (!selectedModelName) {
await determineName()
}
Expand All @@ -227,9 +274,21 @@ const generate: CliCommand = async () => {

const generators = generatorMap[selectedType as keyof typeof generatorMap]

const isTypeScript = await getIsTypeScript()
const blitzServerPath = isTypeScript ? "app/blitz-server.ts" : "app/blitz-server.js"
const blitzServer = require("path").join(process.cwd(), blitzServerPath)
const {register} = require("esbuild-register/dist/node")
const {unregister} = register({
target: "es6",
})
const blitzConfig = require(blitzServer)
const {cliConfig} = blitzConfig
unregister()

for (const GeneratorClass of generators) {
const generator = new GeneratorClass({
destinationRoot: require("path").resolve(),
templateDir: cliConfig?.customTemplates,
extraArgs: args["_"].slice(3) as string[],
modelName: singularRootContext,
modelNames: modelNames(singularRootContext),
Expand Down
4 changes: 4 additions & 0 deletions packages/blitz/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export interface RouteUrlObject extends Pick<UrlObject, "pathname" | "query"> {
pathname: string
}

export type BlitzCliConfig = {
customTemplates?: string
}

export const isRouteUrlObject = (x: any): x is RouteUrlObject => {
return typeof x === "object" && "pathname" in x && typeof x.pathname === "string"
}
Expand Down
82 changes: 82 additions & 0 deletions packages/generator/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,88 @@ import {readdirRecursive} from "./utils/readdir-recursive"
import prettier from "prettier"
const debug = require("debug")("blitz:generator")

export const addCustomTemplatesBlitzConfig = (
customTemplatesPath: string,
isTypeScript: boolean,
) => {
const blitzServer = isTypeScript ? "app/blitz-server.ts" : "app/blitz-server.js"
const blitzServerPath = require("path").join(process.cwd(), blitzServer)
const userConfigModuleSource = fs.readFileSync(blitzServerPath, {encoding: "utf-8"})
const userConfigModule = j(userConfigModuleSource, {parser: customTsParser})
const program = userConfigModule.get()
const cliConfigDeclaration = userConfigModule
.find(j.ExportNamedDeclaration, {
declaration: {
type: "VariableDeclaration",
declarations: [
{
id: {
name: "cliConfig",
},
},
],
},
})
.paths()
.at(0)
if (!cliConfigDeclaration) {
const config = j.identifier("cliConfig")
const configVariable = j.variableDeclaration("const", [
j.variableDeclarator(
config,
j.objectExpression([
j.objectProperty(j.identifier("customTemplates"), j.literal(customTemplatesPath)),
]),
),
])
if (isTypeScript) {
const type = j.tsTypeAnnotation(j.tsTypeReference(j.identifier("BlitzCliConfig")))
const declaration: any = configVariable?.declarations
declaration[0].id.typeAnnotation = type
const typeImport = j.importDeclaration(
[j.importSpecifier(j.identifier("BlitzCliConfig"))],
j.literal("blitz"),
)
typeImport.importKind = "type"
program.node.program.body.unshift(typeImport)
}
const exportConfig = j.exportNamedDeclaration(configVariable)
program.node.program.body.push(exportConfig)
} else {
const configType = cliConfigDeclaration.value.declaration?.type
if (configType === "VariableDeclaration") {
const config = cliConfigDeclaration.value.declaration.declarations[0]
if (config?.type === "VariableDeclarator") {
const configProperties = config.init
if (configProperties?.type === "ObjectExpression") {
const customTemplatesProperty = configProperties.properties.find((property) => {
if (property.type === "ObjectProperty") {
const key = property.key
if (key.type === "Identifier") {
return key.name === "customTemplates"
}
}
})
if (!customTemplatesProperty) {
configProperties.properties.push(
j.objectProperty(j.identifier("customTemplates"), j.literal(customTemplatesPath)),
)
} else {
if (customTemplatesProperty.type === "ObjectProperty") {
const customValue = customTemplatesProperty.value
if (customValue.type === "StringLiteral") {
customValue.value = customTemplatesPath
}
}
}
}
}
}
}
const newSource = userConfigModule.toSource()
fs.writeFileSync(blitzServerPath, newSource)
}

export const customTsParser = {
parse(source: string, options?: Overrides) {
const babelOptions = getBabelOptions(options)
Expand Down

0 comments on commit f39ba1f

Please sign in to comment.