Skip to content

Commit

Permalink
fix: Adds templates to @flare-city/cli to power the init command (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
drewdecarme authored Nov 2, 2023
1 parent 1a483c9 commit 40e49b8
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 76 deletions.
6 changes: 6 additions & 0 deletions .changeset/quick-beans-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@flare-city/test": minor
"@flare-city/cli": minor
---

Removes `@flare-city/cli` dependency from `@flare-city/test` and adds templates to power the `flare-city init` CLI command
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ bin
!.yarn/sdks
!.yarn/versions

.turbo
.turbo

# Placed here to avoid .gitignore for yarn pack
packages/cli/templates/*
packages/cli/!templates/.gitignore
packages/cli/!templates/readme.md
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"description": "FlareCity simplifies API development on Cloudflare Workers, offering a purposeful and efficient framework for building, testing, and deploying serverless APIs.",
"workspaces": {
"packages": [
"packages/**"
"packages/**",
"examples/**"
]
},
"repository": {
Expand All @@ -19,7 +20,7 @@
},
"homepage": "https://github.com/drewdecarme/flare-city#readme",
"scripts": {
"build": "yarn turbo build --filter='!with-*'",
"build": "yarn turbo build --filter='!@flare-city/with-*'",
"packages:version": "yarn changeset version",
"packages:publish": "yarn build && yarn workspaces foreach --all --no-private npm publish --access public",
"packages:tag": "yarn changeset tag"
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
app-city
app-city
bin
9 changes: 6 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
},
"files": [
"bin",
"examples"
"templates"
],
"scripts": {
"build": "yarn tsc",
"dev": "yarn tsc --watch",
"build": "yarn process && yarn tsc --project tsconfig.build.json",
"dev": "yarn build --watch",
"process": "yarn tsx ./scripts/process.ts",
"lint": "npx eslint ./src/**/* --fix"
},
"repository": {
Expand Down Expand Up @@ -41,7 +42,9 @@
"@types/find-node-modules": "^2.1.1",
"@types/fs-extra": "^11.0.3",
"@types/inquirer": "^9.0.6",
"@types/node": "^20.8.10",
"semantic-release": "^22.0.6",
"tsx": "^3.14.0",
"typescript": "5.2.2"
}
}
100 changes: 100 additions & 0 deletions packages/cli/scripts/process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as fs from "fs";
import * as path from "path";

import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

interface WordReplacement {
oldWord: string | RegExp;
newWord: string;
}

function replaceWordsInFile(
filePath: string,
outputFilePath: string,
replacements: WordReplacement[]
) {
const fileContent = fs.readFileSync(filePath, "utf-8");
let updatedContent = fileContent;

replacements.forEach(({ oldWord, newWord }) => {
updatedContent = updatedContent.replace(new RegExp(oldWord, "g"), newWord);
});

fs.writeFileSync(outputFilePath, updatedContent);
}

function processDirectory(
packageSampleDirPath: string,
packageTemplateDirPath: string,
replacements: WordReplacement[],
excludedDirectories: string[]
) {
fs.readdirSync(packageSampleDirPath).forEach((file) => {
const inputFilePath = path.join(packageSampleDirPath, file);
const outputFilePath = path
.join(packageTemplateDirPath, file)
.concat(".hbs");

if (fs.statSync(inputFilePath).isDirectory()) {
if (excludedDirectories.includes(file)) {
// Skip excluded directory
return;
}
const subDirectory = path.join(packageTemplateDirPath, file);
fs.mkdirSync(subDirectory, { recursive: true });
processDirectory(
inputFilePath,
subDirectory,
replacements,
excludedDirectories
);
} else {
replaceWordsInFile(inputFilePath, outputFilePath, replacements);
}
});
}

const examplesDirPath = path.resolve(__dirname, "../examples");
const templatesDirPath = path.resolve(__dirname, "../templates");
const excludedDirectories = [".turbo", "node_modules", ".wrangler"];

try {
const directories = fs
.readdirSync(examplesDirPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);

// create the templates directory
fs.mkdirSync(templatesDirPath, { recursive: true });

directories.forEach((packageName) => {
const packageExampleDirPath = examplesDirPath.concat(`/${packageName}`);
const packageTemplateDirPath = templatesDirPath.concat(`/${packageName}`);

fs.mkdirSync(packageTemplateDirPath, { recursive: true });

const wordReplacements: WordReplacement[] = [
{
oldWord: `${packageName}-project-description`,
newWord: "{{projectDescription}}",
},
{ oldWord: `${packageName}-project-name`, newWord: `{{projectName}}` },
{ oldWord: packageName, newWord: `{{projectName}}` },
{ oldWord: /workspace:\*/, newWord: "latest" },
// Add more replacements as needed
];

processDirectory(
packageExampleDirPath,
packageTemplateDirPath,
wordReplacements,
excludedDirectories
);

console.log(`Complete processing template: ${packageName}`);
});
} catch (error) {
throw new Error(error as string);
}
90 changes: 30 additions & 60 deletions packages/cli/src/init/init.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,80 +39,50 @@ async function init(params: InitOptions) {
})
: join(process.cwd(), params.location, projectName);

const templatesDirPath = resolve(__dirname, "../../templates");

const directories = fs
.readdirSync(templatesDirPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);

const template = await select({
message: "Please select a template",
choices: [
{
name: "with-sample-route",
description:
"A simple example that contains 1 route with 2 endpoints at `/sample`.",
value: "with-sample-route",
},
],
choices: directories.map((dir) => ({
name: dir,
value: dir,
})),
});

const srcDir = resolve(__dirname, `../../examples/${template}`);

if (!srcDir) {
throw new Error(`Cannot locate "${srcDir}"`);
}

const filesTs = resolve(srcDir, `./**/*.ts`);
const filesLint = resolve(srcDir, `./.eslintrc.cjs`);
const filesGit = resolve(srcDir, `./.gitignore`);
const filesJson = resolve(srcDir, `./*.json`);
const filesToml = resolve(srcDir, `./*.toml`);

const outputDir = projectLocation;
const templateDirGlob = resolve(templatesDirPath, `./${template}/**/*.*`);
const templateDirDotGlob = resolve(templatesDirPath, `./${template}/**/.*`);

// 2. Process TS files
const filesToProcess = glob.sync([
filesTs,
filesLint,
filesGit,
filesJson,
filesToml,
]);

console.log("Processing and transforming template...");
const filesToProcess = glob.sync([templateDirGlob, templateDirDotGlob]);

console.log("Instantiating template...");
filesToProcess.forEach((sourcePath) => {
const filePath = sourcePath.split(template)[1]; // Extract relative file path from the source path
const outputPath = `${outputDir}${filePath}`;

let existingContent = fs.readFileSync(sourcePath, "utf-8");
existingContent = existingContent.replace(
new RegExp(template, "g"),
`{{projectName}}`
);
existingContent = existingContent.replace(
new RegExp(`${template}-project-name`, "g"),
`{{projectName}}`
);
existingContent = existingContent.replace(
new RegExp(`${template}-project-description`, "g"),
`{{projectDescription}}`
);
existingContent = existingContent.replace(
new RegExp(`workspace:`, "g"),
`latest`
);
const outputPath = `${projectLocation}${filePath}`;

const existingContent = fs.readFileSync(sourcePath, "utf-8");

// Compile the Handlebars template if it exists
const compiledTemplate = existingContent
? handlebars.compile(existingContent)
: undefined;
const compiledTemplate = handlebars.compile(existingContent);

// Replace placeholders with actual values
const replacedContent = compiledTemplate
? compiledTemplate({ projectName, projectDescription })
: existingContent;

// Write the new TypeScript file
fs.ensureFileSync(outputPath);
fs.writeFileSync(outputPath, replacedContent);
const replacedContent = compiledTemplate({
projectName,
projectDescription,
});

// Write the new file
const outputPathSansHbs = outputPath.split(".hbs")[0];
fs.ensureFileSync(outputPathSansHbs);
fs.writeFileSync(outputPathSansHbs, replacedContent);
});

console.log("Processing and transforming template... done.");
console.log("Instantiating template... done.");
console.log(
filesToProcess.length.toString().concat(" files successfully processed.")
);
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "@flare-city/tsconfig/library/build",
"compilerOptions": {
"outDir": "./bin",
"declarationDir": "./bin"
},
"include": ["./src/**/*"]
}
8 changes: 2 additions & 6 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
{
"extends": "@flare-city/tsconfig/library/build",
"compilerOptions": {
"outDir": "./bin",
"declarationDir": "./bin"
},
"include": ["./src/**/*.ts"]
"extends": "@flare-city/tsconfig/library",
"include": ["./src/**/*", "scripts", "examples"]
}
1 change: 1 addition & 0 deletions packages/eslint-config/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
plugins: ["@typescript-eslint"],
ignorePatterns: [
".eslintrc.cjs",
"eslint.config.cjs",
"bin/",
"node_modules/",
"dist/",
Expand Down
1 change: 1 addition & 0 deletions packages/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
},
"homepage": "https://github.com/drewdecarme/flare-city#readme",
"dependencies": {
"@flare-city/cli": "workspace:*",
"@flare-city/core": "workspace:*",
"vite": "4.5.0",
"vitest": "0.34.6",
Expand Down
Loading

0 comments on commit 40e49b8

Please sign in to comment.