@farm.js/create-app 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/bin/create-farm-app.js +1 -0
- package/dist/index.js +49 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/basic/gitignore +23 -0
- package/templates/basic/package.json +2 -2
- package/templates/basic/public/favicon.svg +13 -0
- package/templates/basic/src/app/layout.tsx +3 -0
- package/templates/basic/src/farm-images.d.ts +0 -59
package/README.md
CHANGED
|
@@ -5,7 +5,12 @@ Create a new Farm.js application
|
|
|
5
5
|
Farm.js is currently in beta.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
pnpm create @farm.js/app my-app
|
|
9
|
+
cd my-app
|
|
10
|
+
pnpm dev
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
The scaffolder installs React, React DOM, Farm.js, TypeScript, and the other starter dependencies
|
|
14
|
+
automatically. Pass `--skip-install` when you only want to generate the project files.
|
|
15
|
+
|
|
11
16
|
See the [Farm.js repository](https://github.com/farming-labs/farm.js) for documentation, examples, and support.
|
package/bin/create-farm-app.js
CHANGED
|
@@ -11,6 +11,7 @@ program
|
|
|
11
11
|
.argument("[project-name]", "Name of the project")
|
|
12
12
|
.option("-t, --template <template>", "Template to use")
|
|
13
13
|
.option("--typescript", "Use TypeScript template")
|
|
14
|
+
.option("--skip-install", "Skip installing dependencies")
|
|
14
15
|
.action(async (projectName, options) => {
|
|
15
16
|
try {
|
|
16
17
|
await createApp(projectName, options);
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ let path = require("path");
|
|
|
7
7
|
path = require_rolldown_runtime.__toESM(path);
|
|
8
8
|
let fs_promises = require("fs/promises");
|
|
9
9
|
fs_promises = require_rolldown_runtime.__toESM(fs_promises);
|
|
10
|
+
let node_child_process = require("node:child_process");
|
|
10
11
|
//#region src/index.ts
|
|
11
12
|
async function createApp(projectName, options = {}) {
|
|
12
13
|
require_utils.showBanner();
|
|
@@ -72,6 +73,7 @@ async function createApp(projectName, options = {}) {
|
|
|
72
73
|
useTypeScript = response.typescript;
|
|
73
74
|
}
|
|
74
75
|
const projectPath = path.default.resolve(process.cwd(), projectName);
|
|
76
|
+
const packageManager = detectPackageManager();
|
|
75
77
|
if (await directoryHasFiles(projectPath)) {
|
|
76
78
|
if (!(await (0, prompts.default)({
|
|
77
79
|
type: "confirm",
|
|
@@ -86,13 +88,18 @@ async function createApp(projectName, options = {}) {
|
|
|
86
88
|
require_utils.logger.info(`Creating Farm.js app in ${projectPath}`);
|
|
87
89
|
await fs_promises.default.mkdir(projectPath, { recursive: true });
|
|
88
90
|
await copyTemplate(template, projectPath, useTypeScript);
|
|
89
|
-
await updatePackageJson(projectPath, projectName);
|
|
91
|
+
await updatePackageJson(projectPath, projectName, packageManager);
|
|
90
92
|
require_utils.logger.success(`🚜 Created ${projectName}`);
|
|
93
|
+
if (!options.skipInstall) {
|
|
94
|
+
require_utils.logger.info(`Installing dependencies with ${packageManager.name}...`);
|
|
95
|
+
await installDependencies(projectPath, packageManager);
|
|
96
|
+
require_utils.logger.success("Dependencies installed");
|
|
97
|
+
}
|
|
91
98
|
require_utils.logger.info("");
|
|
92
99
|
require_utils.logger.info("Next steps");
|
|
93
100
|
require_utils.logger.info(` cd ${projectName}`);
|
|
94
|
-
require_utils.logger.info(
|
|
95
|
-
require_utils.logger.info(
|
|
101
|
+
if (options.skipInstall) require_utils.logger.info(` ${packageManager.name} install`);
|
|
102
|
+
require_utils.logger.info(` ${getDevCommand(packageManager.name)}`);
|
|
96
103
|
require_utils.logger.info("");
|
|
97
104
|
require_utils.logger.info("Tailwind is enabled by default. You only need postcss config for custom plugins.");
|
|
98
105
|
}
|
|
@@ -108,7 +115,8 @@ async function copyDir(src, dest) {
|
|
|
108
115
|
const entries = await fs_promises.default.readdir(src, { withFileTypes: true });
|
|
109
116
|
for (const entry of entries) {
|
|
110
117
|
const srcPath = path.default.join(src, entry.name);
|
|
111
|
-
const
|
|
118
|
+
const destinationName = entry.name === "gitignore" ? ".gitignore" : entry.name;
|
|
119
|
+
const destPath = path.default.join(dest, destinationName);
|
|
112
120
|
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
113
121
|
else await fs_promises.default.copyFile(srcPath, destPath);
|
|
114
122
|
}
|
|
@@ -146,18 +154,53 @@ async function getAvailableTemplates() {
|
|
|
146
154
|
const templatesRoot = path.default.join(__dirname, "..", "templates");
|
|
147
155
|
return (await fs_promises.default.readdir(templatesRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_")).map((entry) => entry.name).sort();
|
|
148
156
|
}
|
|
149
|
-
async function updatePackageJson(projectPath, projectName) {
|
|
157
|
+
async function updatePackageJson(projectPath, projectName, packageManager) {
|
|
150
158
|
const packageJsonPath = path.default.join(projectPath, "package.json");
|
|
151
159
|
try {
|
|
152
160
|
const content = await fs_promises.default.readFile(packageJsonPath, "utf-8");
|
|
153
161
|
const packageJson = JSON.parse(content);
|
|
154
162
|
packageJson.name = projectName;
|
|
163
|
+
if (packageManager.version) packageJson.packageManager = `${packageManager.name}@${packageManager.version}`;
|
|
155
164
|
await fs_promises.default.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
156
|
-
} catch
|
|
165
|
+
} catch {
|
|
157
166
|
require_utils.logger.warn("Could not update package.json");
|
|
158
167
|
}
|
|
159
168
|
}
|
|
169
|
+
function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
|
|
170
|
+
const match = userAgent?.match(/^(npm|pnpm|yarn|bun)\/([^\s]+)/);
|
|
171
|
+
if (!match) return { name: "pnpm" };
|
|
172
|
+
const [, name, version] = match;
|
|
173
|
+
return {
|
|
174
|
+
name,
|
|
175
|
+
version: version === "?" ? void 0 : version
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function installDependencies(projectPath, packageManager) {
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
const command = process.platform === "win32" ? `${packageManager.name}.cmd` : packageManager.name;
|
|
181
|
+
const child = (0, node_child_process.spawn)(command, ["install"], {
|
|
182
|
+
cwd: projectPath,
|
|
183
|
+
env: process.env,
|
|
184
|
+
stdio: "inherit"
|
|
185
|
+
});
|
|
186
|
+
child.on("error", reject);
|
|
187
|
+
child.on("close", (code, signal) => {
|
|
188
|
+
if (code === 0) {
|
|
189
|
+
resolve();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
|
|
193
|
+
reject(/* @__PURE__ */ new Error(`${packageManager.name} install failed with ${reason}.`));
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function getDevCommand(packageManager) {
|
|
198
|
+
if (packageManager === "npm" || packageManager === "bun") return `${packageManager} run dev`;
|
|
199
|
+
return `${packageManager} dev`;
|
|
200
|
+
}
|
|
160
201
|
//#endregion
|
|
161
202
|
exports.createApp = createApp;
|
|
203
|
+
exports.detectPackageManager = detectPackageManager;
|
|
204
|
+
exports.installDependencies = installDependencies;
|
|
162
205
|
|
|
163
206
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["fs","path"],"sources":["../src/index.ts"],"sourcesContent":["import prompts from \"prompts\";\nimport path from \"path\";\nimport fs from \"fs/promises\";\nimport { logger, showBanner } from \"./utils\";\n\ninterface CreateAppOptions {\n template?: string;\n typescript?: boolean;\n}\n\nexport async function createApp(projectName?: string, options: CreateAppOptions = {}) {\n showBanner();\n\n const templates = await getAvailableTemplates();\n if (templates.length === 0) {\n logger.error(\"No templates are available in this package.\");\n process.exit(1);\n }\n\n // Get project name if not provided\n if (!projectName) {\n const response = await prompts({\n type: \"text\",\n name: \"projectName\",\n message: \"What is your project named?\",\n initial: \"my-farm-app\",\n validate: validateProjectName,\n });\n\n if (!response.projectName) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n\n projectName = response.projectName;\n } else {\n const validation = validateProjectPathArg(projectName);\n if (validation !== true) {\n logger.error(validation);\n process.exit(1);\n }\n }\n\n // Get template if not provided\n let template = options.template;\n if (!template) {\n const response = await prompts({\n type: \"select\",\n name: \"template\",\n message: \"Which template would you like to use?\",\n choices: templates.map((name) => ({\n title: prettifyTemplateName(name),\n value: name,\n description:\n name === \"basic\" ? \"A simple Farm.js app with built-in Tailwind support\" : undefined,\n })),\n initial: 0,\n });\n\n if (!response.template) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n template = response.template;\n } else if (!templates.includes(template)) {\n logger.error(\n `Unknown template \"${template}\". Available: ${templates.map((t) => `\"${t}\"`).join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Check TypeScript preference\n let useTypeScript = options.typescript;\n if (useTypeScript === undefined) {\n const response = await prompts({\n type: \"confirm\",\n name: \"typescript\",\n message: \"Would you like to use TypeScript?\",\n initial: true,\n });\n\n if (response.typescript === undefined) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n useTypeScript = response.typescript;\n }\n\n const projectPath = path.resolve(process.cwd(), projectName!);\n const hasExistingFiles = await directoryHasFiles(projectPath);\n if (hasExistingFiles) {\n const overwriteResponse = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" is not empty. Continue and overwrite conflicting files?`,\n initial: false,\n });\n\n if (!overwriteResponse.overwrite) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n }\n\n logger.info(`Creating Farm.js app in ${projectPath}`);\n\n await fs.mkdir(projectPath, { recursive: true });\n\n await copyTemplate(template!, projectPath, useTypeScript!);\n\n await updatePackageJson(projectPath, projectName!);\n\n logger.success(`🚜 Created ${projectName}`);\n logger.info(\"\");\n logger.info(\"Next steps\");\n logger.info(` cd ${projectName}`);\n logger.info(\" pnpm install\");\n logger.info(\" pnpm dev\");\n logger.info(\"\");\n logger.info(\"Tailwind is enabled by default. You only need postcss config for custom plugins.\");\n}\n\nasync function copyTemplate(template: string, projectPath: string, useTypeScript: boolean) {\n const templatePath = path.join(__dirname, \"..\", \"templates\", template);\n\n // Copy base template files\n await copyDir(templatePath, projectPath);\n\n // If TypeScript is requested, copy TS-specific files\n if (useTypeScript) {\n const tsTemplatePath = path.join(__dirname, \"..\", \"templates\", \"_typescript\");\n if (await dirExists(tsTemplatePath)) {\n await copyDir(tsTemplatePath, projectPath);\n }\n }\n}\n\nasync function copyDir(src: string, dest: string) {\n await fs.mkdir(dest, { recursive: true });\n\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n}\n\nasync function dirExists(path: string): Promise<boolean> {\n try {\n const stat = await fs.stat(path);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function directoryHasFiles(dirPath: string): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n const files = await fs.readdir(dirPath);\n return files.length > 0;\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n // npm package-safe pattern\n if (!/^[a-z0-9._-]+$/i.test(trimmed)) {\n return \"Use letters, numbers, hyphens, underscores, or dots\";\n }\n\n if (trimmed.startsWith(\".\") || trimmed.startsWith(\"_\")) {\n return \"Project name cannot start with '.' or '_'\";\n }\n\n return true;\n}\n\nfunction validateProjectPathArg(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n const normalized = trimmed.replace(/[\\\\/]+$/, \"\");\n const baseName = path.basename(normalized);\n if (!baseName || baseName === \".\" || baseName === \"..\") {\n return \"Please provide a valid project directory name\";\n }\n\n return validateProjectName(baseName);\n}\n\nfunction prettifyTemplateName(name: string): string {\n return name\n .split(\"-\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nasync function getAvailableTemplates(): Promise<string[]> {\n const templatesRoot = path.join(__dirname, \"..\", \"templates\");\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\"_\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function updatePackageJson(projectPath: string, projectName: string) {\n const packageJsonPath = path.join(projectPath, \"package.json\");\n\n try {\n const content = await fs.readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n packageJson.name = projectName;\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`);\n } catch (error) {\n logger.warn(\"Could not update package.json\");\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["fs","path","spawn"],"sources":["../src/index.ts"],"sourcesContent":["import prompts from \"prompts\";\nimport path from \"path\";\nimport fs from \"fs/promises\";\nimport { spawn } from \"node:child_process\";\nimport { logger, showBanner } from \"./utils\";\n\ninterface CreateAppOptions {\n template?: string;\n typescript?: boolean;\n skipInstall?: boolean;\n}\n\nexport type PackageManagerName = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport interface PackageManager {\n name: PackageManagerName;\n version?: string;\n}\n\nexport async function createApp(projectName?: string, options: CreateAppOptions = {}) {\n showBanner();\n\n const templates = await getAvailableTemplates();\n if (templates.length === 0) {\n logger.error(\"No templates are available in this package.\");\n process.exit(1);\n }\n\n // Get project name if not provided\n if (!projectName) {\n const response = await prompts({\n type: \"text\",\n name: \"projectName\",\n message: \"What is your project named?\",\n initial: \"my-farm-app\",\n validate: validateProjectName,\n });\n\n if (!response.projectName) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n\n projectName = response.projectName;\n } else {\n const validation = validateProjectPathArg(projectName);\n if (validation !== true) {\n logger.error(validation);\n process.exit(1);\n }\n }\n\n // Get template if not provided\n let template = options.template;\n if (!template) {\n const response = await prompts({\n type: \"select\",\n name: \"template\",\n message: \"Which template would you like to use?\",\n choices: templates.map((name) => ({\n title: prettifyTemplateName(name),\n value: name,\n description:\n name === \"basic\" ? \"A simple Farm.js app with built-in Tailwind support\" : undefined,\n })),\n initial: 0,\n });\n\n if (!response.template) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n template = response.template;\n } else if (!templates.includes(template)) {\n logger.error(\n `Unknown template \"${template}\". Available: ${templates.map((t) => `\"${t}\"`).join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Check TypeScript preference\n let useTypeScript = options.typescript;\n if (useTypeScript === undefined) {\n const response = await prompts({\n type: \"confirm\",\n name: \"typescript\",\n message: \"Would you like to use TypeScript?\",\n initial: true,\n });\n\n if (response.typescript === undefined) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n useTypeScript = response.typescript;\n }\n\n const projectPath = path.resolve(process.cwd(), projectName!);\n const packageManager = detectPackageManager();\n const hasExistingFiles = await directoryHasFiles(projectPath);\n if (hasExistingFiles) {\n const overwriteResponse = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" is not empty. Continue and overwrite conflicting files?`,\n initial: false,\n });\n\n if (!overwriteResponse.overwrite) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n }\n\n logger.info(`Creating Farm.js app in ${projectPath}`);\n\n await fs.mkdir(projectPath, { recursive: true });\n\n await copyTemplate(template!, projectPath, useTypeScript!);\n\n await updatePackageJson(projectPath, projectName!, packageManager);\n\n logger.success(`🚜 Created ${projectName}`);\n\n if (!options.skipInstall) {\n logger.info(`Installing dependencies with ${packageManager.name}...`);\n await installDependencies(projectPath, packageManager);\n logger.success(\"Dependencies installed\");\n }\n\n logger.info(\"\");\n logger.info(\"Next steps\");\n logger.info(` cd ${projectName}`);\n if (options.skipInstall) {\n logger.info(` ${packageManager.name} install`);\n }\n logger.info(` ${getDevCommand(packageManager.name)}`);\n logger.info(\"\");\n logger.info(\"Tailwind is enabled by default. You only need postcss config for custom plugins.\");\n}\n\nasync function copyTemplate(template: string, projectPath: string, useTypeScript: boolean) {\n const templatePath = path.join(__dirname, \"..\", \"templates\", template);\n\n // Copy base template files\n await copyDir(templatePath, projectPath);\n\n // If TypeScript is requested, copy TS-specific files\n if (useTypeScript) {\n const tsTemplatePath = path.join(__dirname, \"..\", \"templates\", \"_typescript\");\n if (await dirExists(tsTemplatePath)) {\n await copyDir(tsTemplatePath, projectPath);\n }\n }\n}\n\nasync function copyDir(src: string, dest: string) {\n await fs.mkdir(dest, { recursive: true });\n\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n // npm excludes .gitignore files from published tarballs. Keep the template\n // file publishable and restore its dot when generating the application.\n const destinationName = entry.name === \"gitignore\" ? \".gitignore\" : entry.name;\n const destPath = path.join(dest, destinationName);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n}\n\nasync function dirExists(path: string): Promise<boolean> {\n try {\n const stat = await fs.stat(path);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function directoryHasFiles(dirPath: string): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n const files = await fs.readdir(dirPath);\n return files.length > 0;\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n // npm package-safe pattern\n if (!/^[a-z0-9._-]+$/i.test(trimmed)) {\n return \"Use letters, numbers, hyphens, underscores, or dots\";\n }\n\n if (trimmed.startsWith(\".\") || trimmed.startsWith(\"_\")) {\n return \"Project name cannot start with '.' or '_'\";\n }\n\n return true;\n}\n\nfunction validateProjectPathArg(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n const normalized = trimmed.replace(/[\\\\/]+$/, \"\");\n const baseName = path.basename(normalized);\n if (!baseName || baseName === \".\" || baseName === \"..\") {\n return \"Please provide a valid project directory name\";\n }\n\n return validateProjectName(baseName);\n}\n\nfunction prettifyTemplateName(name: string): string {\n return name\n .split(\"-\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nasync function getAvailableTemplates(): Promise<string[]> {\n const templatesRoot = path.join(__dirname, \"..\", \"templates\");\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\"_\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function updatePackageJson(\n projectPath: string,\n projectName: string,\n packageManager: PackageManager,\n) {\n const packageJsonPath = path.join(projectPath, \"package.json\");\n\n try {\n const content = await fs.readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n packageJson.name = projectName;\n if (packageManager.version) {\n packageJson.packageManager = `${packageManager.name}@${packageManager.version}`;\n }\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`);\n } catch {\n logger.warn(\"Could not update package.json\");\n }\n}\n\nexport function detectPackageManager(\n userAgent = process.env.npm_config_user_agent,\n): PackageManager {\n const match = userAgent?.match(/^(npm|pnpm|yarn|bun)\\/([^\\s]+)/);\n if (!match) {\n return { name: \"pnpm\" };\n }\n\n const [, name, version] = match;\n return {\n name: name as PackageManagerName,\n version: version === \"?\" ? undefined : version,\n };\n}\n\nexport function installDependencies(\n projectPath: string,\n packageManager: PackageManager,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const command =\n process.platform === \"win32\" ? `${packageManager.name}.cmd` : packageManager.name;\n const child = spawn(command, [\"install\"], {\n cwd: projectPath,\n env: process.env,\n stdio: \"inherit\",\n });\n\n child.on(\"error\", reject);\n child.on(\"close\", (code, signal) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n const reason = signal ? `signal ${signal}` : `exit code ${code ?? \"unknown\"}`;\n reject(new Error(`${packageManager.name} install failed with ${reason}.`));\n });\n });\n}\n\nfunction getDevCommand(packageManager: PackageManagerName) {\n if (packageManager === \"npm\" || packageManager === \"bun\") {\n return `${packageManager} run dev`;\n }\n return `${packageManager} dev`;\n}\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { logger, showBanner } from "./utils.mjs";
|
|
|
2
2
|
import prompts from "prompts";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import fs from "fs/promises";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
5
6
|
//#region src/index.ts
|
|
6
7
|
async function createApp(projectName, options = {}) {
|
|
7
8
|
showBanner();
|
|
@@ -67,6 +68,7 @@ async function createApp(projectName, options = {}) {
|
|
|
67
68
|
useTypeScript = response.typescript;
|
|
68
69
|
}
|
|
69
70
|
const projectPath = path.resolve(process.cwd(), projectName);
|
|
71
|
+
const packageManager = detectPackageManager();
|
|
70
72
|
if (await directoryHasFiles(projectPath)) {
|
|
71
73
|
if (!(await prompts({
|
|
72
74
|
type: "confirm",
|
|
@@ -81,13 +83,18 @@ async function createApp(projectName, options = {}) {
|
|
|
81
83
|
logger.info(`Creating Farm.js app in ${projectPath}`);
|
|
82
84
|
await fs.mkdir(projectPath, { recursive: true });
|
|
83
85
|
await copyTemplate(template, projectPath, useTypeScript);
|
|
84
|
-
await updatePackageJson(projectPath, projectName);
|
|
86
|
+
await updatePackageJson(projectPath, projectName, packageManager);
|
|
85
87
|
logger.success(`🚜 Created ${projectName}`);
|
|
88
|
+
if (!options.skipInstall) {
|
|
89
|
+
logger.info(`Installing dependencies with ${packageManager.name}...`);
|
|
90
|
+
await installDependencies(projectPath, packageManager);
|
|
91
|
+
logger.success("Dependencies installed");
|
|
92
|
+
}
|
|
86
93
|
logger.info("");
|
|
87
94
|
logger.info("Next steps");
|
|
88
95
|
logger.info(` cd ${projectName}`);
|
|
89
|
-
logger.info(
|
|
90
|
-
logger.info(
|
|
96
|
+
if (options.skipInstall) logger.info(` ${packageManager.name} install`);
|
|
97
|
+
logger.info(` ${getDevCommand(packageManager.name)}`);
|
|
91
98
|
logger.info("");
|
|
92
99
|
logger.info("Tailwind is enabled by default. You only need postcss config for custom plugins.");
|
|
93
100
|
}
|
|
@@ -103,7 +110,8 @@ async function copyDir(src, dest) {
|
|
|
103
110
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
104
111
|
for (const entry of entries) {
|
|
105
112
|
const srcPath = path.join(src, entry.name);
|
|
106
|
-
const
|
|
113
|
+
const destinationName = entry.name === "gitignore" ? ".gitignore" : entry.name;
|
|
114
|
+
const destPath = path.join(dest, destinationName);
|
|
107
115
|
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
108
116
|
else await fs.copyFile(srcPath, destPath);
|
|
109
117
|
}
|
|
@@ -141,18 +149,51 @@ async function getAvailableTemplates() {
|
|
|
141
149
|
const templatesRoot = path.join(__dirname, "..", "templates");
|
|
142
150
|
return (await fs.readdir(templatesRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_")).map((entry) => entry.name).sort();
|
|
143
151
|
}
|
|
144
|
-
async function updatePackageJson(projectPath, projectName) {
|
|
152
|
+
async function updatePackageJson(projectPath, projectName, packageManager) {
|
|
145
153
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
146
154
|
try {
|
|
147
155
|
const content = await fs.readFile(packageJsonPath, "utf-8");
|
|
148
156
|
const packageJson = JSON.parse(content);
|
|
149
157
|
packageJson.name = projectName;
|
|
158
|
+
if (packageManager.version) packageJson.packageManager = `${packageManager.name}@${packageManager.version}`;
|
|
150
159
|
await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
151
|
-
} catch
|
|
160
|
+
} catch {
|
|
152
161
|
logger.warn("Could not update package.json");
|
|
153
162
|
}
|
|
154
163
|
}
|
|
164
|
+
function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
|
|
165
|
+
const match = userAgent?.match(/^(npm|pnpm|yarn|bun)\/([^\s]+)/);
|
|
166
|
+
if (!match) return { name: "pnpm" };
|
|
167
|
+
const [, name, version] = match;
|
|
168
|
+
return {
|
|
169
|
+
name,
|
|
170
|
+
version: version === "?" ? void 0 : version
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function installDependencies(projectPath, packageManager) {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
const command = process.platform === "win32" ? `${packageManager.name}.cmd` : packageManager.name;
|
|
176
|
+
const child = spawn(command, ["install"], {
|
|
177
|
+
cwd: projectPath,
|
|
178
|
+
env: process.env,
|
|
179
|
+
stdio: "inherit"
|
|
180
|
+
});
|
|
181
|
+
child.on("error", reject);
|
|
182
|
+
child.on("close", (code, signal) => {
|
|
183
|
+
if (code === 0) {
|
|
184
|
+
resolve();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
|
|
188
|
+
reject(/* @__PURE__ */ new Error(`${packageManager.name} install failed with ${reason}.`));
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function getDevCommand(packageManager) {
|
|
193
|
+
if (packageManager === "npm" || packageManager === "bun") return `${packageManager} run dev`;
|
|
194
|
+
return `${packageManager} dev`;
|
|
195
|
+
}
|
|
155
196
|
//#endregion
|
|
156
|
-
export { createApp };
|
|
197
|
+
export { createApp, detectPackageManager, installDependencies };
|
|
157
198
|
|
|
158
199
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import prompts from \"prompts\";\nimport path from \"path\";\nimport fs from \"fs/promises\";\nimport { logger, showBanner } from \"./utils\";\n\ninterface CreateAppOptions {\n template?: string;\n typescript?: boolean;\n}\n\nexport async function createApp(projectName?: string, options: CreateAppOptions = {}) {\n showBanner();\n\n const templates = await getAvailableTemplates();\n if (templates.length === 0) {\n logger.error(\"No templates are available in this package.\");\n process.exit(1);\n }\n\n // Get project name if not provided\n if (!projectName) {\n const response = await prompts({\n type: \"text\",\n name: \"projectName\",\n message: \"What is your project named?\",\n initial: \"my-farm-app\",\n validate: validateProjectName,\n });\n\n if (!response.projectName) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n\n projectName = response.projectName;\n } else {\n const validation = validateProjectPathArg(projectName);\n if (validation !== true) {\n logger.error(validation);\n process.exit(1);\n }\n }\n\n // Get template if not provided\n let template = options.template;\n if (!template) {\n const response = await prompts({\n type: \"select\",\n name: \"template\",\n message: \"Which template would you like to use?\",\n choices: templates.map((name) => ({\n title: prettifyTemplateName(name),\n value: name,\n description:\n name === \"basic\" ? \"A simple Farm.js app with built-in Tailwind support\" : undefined,\n })),\n initial: 0,\n });\n\n if (!response.template) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n template = response.template;\n } else if (!templates.includes(template)) {\n logger.error(\n `Unknown template \"${template}\". Available: ${templates.map((t) => `\"${t}\"`).join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Check TypeScript preference\n let useTypeScript = options.typescript;\n if (useTypeScript === undefined) {\n const response = await prompts({\n type: \"confirm\",\n name: \"typescript\",\n message: \"Would you like to use TypeScript?\",\n initial: true,\n });\n\n if (response.typescript === undefined) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n useTypeScript = response.typescript;\n }\n\n const projectPath = path.resolve(process.cwd(), projectName!);\n const hasExistingFiles = await directoryHasFiles(projectPath);\n if (hasExistingFiles) {\n const overwriteResponse = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" is not empty. Continue and overwrite conflicting files?`,\n initial: false,\n });\n\n if (!overwriteResponse.overwrite) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n }\n\n logger.info(`Creating Farm.js app in ${projectPath}`);\n\n await fs.mkdir(projectPath, { recursive: true });\n\n await copyTemplate(template!, projectPath, useTypeScript!);\n\n await updatePackageJson(projectPath, projectName!);\n\n logger.success(`🚜 Created ${projectName}`);\n logger.info(\"\");\n logger.info(\"Next steps\");\n logger.info(` cd ${projectName}`);\n logger.info(\" pnpm install\");\n logger.info(\" pnpm dev\");\n logger.info(\"\");\n logger.info(\"Tailwind is enabled by default. You only need postcss config for custom plugins.\");\n}\n\nasync function copyTemplate(template: string, projectPath: string, useTypeScript: boolean) {\n const templatePath = path.join(__dirname, \"..\", \"templates\", template);\n\n // Copy base template files\n await copyDir(templatePath, projectPath);\n\n // If TypeScript is requested, copy TS-specific files\n if (useTypeScript) {\n const tsTemplatePath = path.join(__dirname, \"..\", \"templates\", \"_typescript\");\n if (await dirExists(tsTemplatePath)) {\n await copyDir(tsTemplatePath, projectPath);\n }\n }\n}\n\nasync function copyDir(src: string, dest: string) {\n await fs.mkdir(dest, { recursive: true });\n\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n}\n\nasync function dirExists(path: string): Promise<boolean> {\n try {\n const stat = await fs.stat(path);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function directoryHasFiles(dirPath: string): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n const files = await fs.readdir(dirPath);\n return files.length > 0;\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n // npm package-safe pattern\n if (!/^[a-z0-9._-]+$/i.test(trimmed)) {\n return \"Use letters, numbers, hyphens, underscores, or dots\";\n }\n\n if (trimmed.startsWith(\".\") || trimmed.startsWith(\"_\")) {\n return \"Project name cannot start with '.' or '_'\";\n }\n\n return true;\n}\n\nfunction validateProjectPathArg(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n const normalized = trimmed.replace(/[\\\\/]+$/, \"\");\n const baseName = path.basename(normalized);\n if (!baseName || baseName === \".\" || baseName === \"..\") {\n return \"Please provide a valid project directory name\";\n }\n\n return validateProjectName(baseName);\n}\n\nfunction prettifyTemplateName(name: string): string {\n return name\n .split(\"-\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nasync function getAvailableTemplates(): Promise<string[]> {\n const templatesRoot = path.join(__dirname, \"..\", \"templates\");\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\"_\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function updatePackageJson(projectPath: string, projectName: string) {\n const packageJsonPath = path.join(projectPath, \"package.json\");\n\n try {\n const content = await fs.readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n packageJson.name = projectName;\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`);\n } catch (error) {\n logger.warn(\"Could not update package.json\");\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import prompts from \"prompts\";\nimport path from \"path\";\nimport fs from \"fs/promises\";\nimport { spawn } from \"node:child_process\";\nimport { logger, showBanner } from \"./utils\";\n\ninterface CreateAppOptions {\n template?: string;\n typescript?: boolean;\n skipInstall?: boolean;\n}\n\nexport type PackageManagerName = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport interface PackageManager {\n name: PackageManagerName;\n version?: string;\n}\n\nexport async function createApp(projectName?: string, options: CreateAppOptions = {}) {\n showBanner();\n\n const templates = await getAvailableTemplates();\n if (templates.length === 0) {\n logger.error(\"No templates are available in this package.\");\n process.exit(1);\n }\n\n // Get project name if not provided\n if (!projectName) {\n const response = await prompts({\n type: \"text\",\n name: \"projectName\",\n message: \"What is your project named?\",\n initial: \"my-farm-app\",\n validate: validateProjectName,\n });\n\n if (!response.projectName) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n\n projectName = response.projectName;\n } else {\n const validation = validateProjectPathArg(projectName);\n if (validation !== true) {\n logger.error(validation);\n process.exit(1);\n }\n }\n\n // Get template if not provided\n let template = options.template;\n if (!template) {\n const response = await prompts({\n type: \"select\",\n name: \"template\",\n message: \"Which template would you like to use?\",\n choices: templates.map((name) => ({\n title: prettifyTemplateName(name),\n value: name,\n description:\n name === \"basic\" ? \"A simple Farm.js app with built-in Tailwind support\" : undefined,\n })),\n initial: 0,\n });\n\n if (!response.template) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n template = response.template;\n } else if (!templates.includes(template)) {\n logger.error(\n `Unknown template \"${template}\". Available: ${templates.map((t) => `\"${t}\"`).join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Check TypeScript preference\n let useTypeScript = options.typescript;\n if (useTypeScript === undefined) {\n const response = await prompts({\n type: \"confirm\",\n name: \"typescript\",\n message: \"Would you like to use TypeScript?\",\n initial: true,\n });\n\n if (response.typescript === undefined) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n useTypeScript = response.typescript;\n }\n\n const projectPath = path.resolve(process.cwd(), projectName!);\n const packageManager = detectPackageManager();\n const hasExistingFiles = await directoryHasFiles(projectPath);\n if (hasExistingFiles) {\n const overwriteResponse = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" is not empty. Continue and overwrite conflicting files?`,\n initial: false,\n });\n\n if (!overwriteResponse.overwrite) {\n logger.error(\"Operation cancelled.\");\n process.exit(1);\n }\n }\n\n logger.info(`Creating Farm.js app in ${projectPath}`);\n\n await fs.mkdir(projectPath, { recursive: true });\n\n await copyTemplate(template!, projectPath, useTypeScript!);\n\n await updatePackageJson(projectPath, projectName!, packageManager);\n\n logger.success(`🚜 Created ${projectName}`);\n\n if (!options.skipInstall) {\n logger.info(`Installing dependencies with ${packageManager.name}...`);\n await installDependencies(projectPath, packageManager);\n logger.success(\"Dependencies installed\");\n }\n\n logger.info(\"\");\n logger.info(\"Next steps\");\n logger.info(` cd ${projectName}`);\n if (options.skipInstall) {\n logger.info(` ${packageManager.name} install`);\n }\n logger.info(` ${getDevCommand(packageManager.name)}`);\n logger.info(\"\");\n logger.info(\"Tailwind is enabled by default. You only need postcss config for custom plugins.\");\n}\n\nasync function copyTemplate(template: string, projectPath: string, useTypeScript: boolean) {\n const templatePath = path.join(__dirname, \"..\", \"templates\", template);\n\n // Copy base template files\n await copyDir(templatePath, projectPath);\n\n // If TypeScript is requested, copy TS-specific files\n if (useTypeScript) {\n const tsTemplatePath = path.join(__dirname, \"..\", \"templates\", \"_typescript\");\n if (await dirExists(tsTemplatePath)) {\n await copyDir(tsTemplatePath, projectPath);\n }\n }\n}\n\nasync function copyDir(src: string, dest: string) {\n await fs.mkdir(dest, { recursive: true });\n\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n // npm excludes .gitignore files from published tarballs. Keep the template\n // file publishable and restore its dot when generating the application.\n const destinationName = entry.name === \"gitignore\" ? \".gitignore\" : entry.name;\n const destPath = path.join(dest, destinationName);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n}\n\nasync function dirExists(path: string): Promise<boolean> {\n try {\n const stat = await fs.stat(path);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function directoryHasFiles(dirPath: string): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n const files = await fs.readdir(dirPath);\n return files.length > 0;\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n // npm package-safe pattern\n if (!/^[a-z0-9._-]+$/i.test(trimmed)) {\n return \"Use letters, numbers, hyphens, underscores, or dots\";\n }\n\n if (trimmed.startsWith(\".\") || trimmed.startsWith(\"_\")) {\n return \"Project name cannot start with '.' or '_'\";\n }\n\n return true;\n}\n\nfunction validateProjectPathArg(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"Project name is required\";\n }\n\n const normalized = trimmed.replace(/[\\\\/]+$/, \"\");\n const baseName = path.basename(normalized);\n if (!baseName || baseName === \".\" || baseName === \"..\") {\n return \"Please provide a valid project directory name\";\n }\n\n return validateProjectName(baseName);\n}\n\nfunction prettifyTemplateName(name: string): string {\n return name\n .split(\"-\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nasync function getAvailableTemplates(): Promise<string[]> {\n const templatesRoot = path.join(__dirname, \"..\", \"templates\");\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\"_\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function updatePackageJson(\n projectPath: string,\n projectName: string,\n packageManager: PackageManager,\n) {\n const packageJsonPath = path.join(projectPath, \"package.json\");\n\n try {\n const content = await fs.readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n packageJson.name = projectName;\n if (packageManager.version) {\n packageJson.packageManager = `${packageManager.name}@${packageManager.version}`;\n }\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`);\n } catch {\n logger.warn(\"Could not update package.json\");\n }\n}\n\nexport function detectPackageManager(\n userAgent = process.env.npm_config_user_agent,\n): PackageManager {\n const match = userAgent?.match(/^(npm|pnpm|yarn|bun)\\/([^\\s]+)/);\n if (!match) {\n return { name: \"pnpm\" };\n }\n\n const [, name, version] = match;\n return {\n name: name as PackageManagerName,\n version: version === \"?\" ? undefined : version,\n };\n}\n\nexport function installDependencies(\n projectPath: string,\n packageManager: PackageManager,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const command =\n process.platform === \"win32\" ? `${packageManager.name}.cmd` : packageManager.name;\n const child = spawn(command, [\"install\"], {\n cwd: projectPath,\n env: process.env,\n stdio: \"inherit\",\n });\n\n child.on(\"error\", reject);\n child.on(\"close\", (code, signal) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n const reason = signal ? `signal ${signal}` : `exit code ${code ?? \"unknown\"}`;\n reject(new Error(`${packageManager.name} install failed with ${reason}.`));\n });\n });\n}\n\nfunction getDevCommand(packageManager: PackageManagerName) {\n if (packageManager === \"npm\" || packageManager === \"bun\") {\n return `${packageManager} run dev`;\n }\n return `${packageManager} dev`;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Farm and deployment output
|
|
5
|
+
.farm/
|
|
6
|
+
.output/
|
|
7
|
+
.vercel/
|
|
8
|
+
dist/
|
|
9
|
+
|
|
10
|
+
# Local environment files
|
|
11
|
+
.env
|
|
12
|
+
.env.local
|
|
13
|
+
.env.*.local
|
|
14
|
+
|
|
15
|
+
# Local databases
|
|
16
|
+
*.sqlite
|
|
17
|
+
*.sqlite-*
|
|
18
|
+
*.db
|
|
19
|
+
*.db-*
|
|
20
|
+
|
|
21
|
+
# Logs and TypeScript cache
|
|
22
|
+
*.log
|
|
23
|
+
*.tsbuildinfo
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"type-check": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@farm.js/core": "0.1.0-beta.
|
|
13
|
+
"@farm.js/core": "0.1.0-beta.7",
|
|
14
14
|
"react": "^19.0.0",
|
|
15
15
|
"react-dom": "^19.0.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@farm.js/cli": "0.1.0-beta.
|
|
18
|
+
"@farm.js/cli": "0.1.0-beta.7",
|
|
19
19
|
"@types/react": "^19.0.0",
|
|
20
20
|
"@types/react-dom": "^19.0.0",
|
|
21
21
|
"tailwindcss": "^4.0.0",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" fill="none" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Farming Labs</title>
|
|
3
|
+
<desc id="desc">The three-tone Farming Labs mark on a dark square.</desc>
|
|
4
|
+
<rect width="512" height="512" fill="#0A0A0A"/>
|
|
5
|
+
<svg x="135.25" y="142" width="241.5" height="228" viewBox="125 68 161 152">
|
|
6
|
+
<path d="M157 68H133C128.582 68 125 71.5817 125 76V100C125 104.418 128.582 108 133 108H157C161.418 108 165 104.418 165 100V76C165 71.5817 161.418 68 157 68Z" fill="#F5F5F4"/>
|
|
7
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M182.383 68H280.617C282.045 68 283.414 68.8429 284.423 70.3431C285.433 71.8434 286 73.8783 286 76V100C286 102.122 285.433 104.157 284.423 105.657C283.414 107.157 282.045 108 280.617 108H182.383C180.955 108 179.586 107.157 178.577 105.657C177.567 104.157 177 102.122 177 100V76C177 73.8783 177.567 71.8434 178.577 70.3431C179.586 68.8429 180.955 68 182.383 68ZM271.852 81C270.747 81 269.852 81.8954 269.852 83V93C269.852 94.1046 270.747 95 271.852 95H277.272C278.376 95 279.272 94.1046 279.272 93V83C279.272 81.8954 278.376 81 277.272 81H271.852Z" fill="#F5F5F4"/>
|
|
8
|
+
<path d="M157 124H133C128.582 124 125 127.582 125 132V156C125 160.418 128.582 164 133 164H157C161.418 164 165 160.418 165 156V132C165 127.582 161.418 124 157 124Z" fill="#A3A3A3"/>
|
|
9
|
+
<path d="M280.334 124H182.666C179.537 124 177 127.582 177 132V156C177 160.418 179.537 164 182.666 164H280.334C283.463 164 286 160.418 286 156V132C286 127.582 283.463 124 280.334 124Z" fill="#A3A3A3"/>
|
|
10
|
+
<path d="M157 180H133C128.582 180 125 183.582 125 188V212C125 216.418 128.582 220 133 220H157C161.418 220 165 216.418 165 212V188C165 183.582 161.418 180 157 180Z" fill="#595959"/>
|
|
11
|
+
<path d="M230.189 180H182.811C179.602 180 177 183.582 177 188V212C177 216.418 179.602 220 182.811 220H230.189C233.398 220 236 216.418 236 212V188C236 183.582 233.398 180 230.189 180Z" fill="#595959"/>
|
|
12
|
+
</svg>
|
|
13
|
+
</svg>
|
|
@@ -4,6 +4,9 @@ import "./globals.css";
|
|
|
4
4
|
export const metadata: Metadata = {
|
|
5
5
|
title: "Farm.js App",
|
|
6
6
|
description: "A modern React meta-framework built on Vite",
|
|
7
|
+
icons: {
|
|
8
|
+
icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
|
|
9
|
+
},
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
export default function RootLayout({ children }: LayoutProps) {
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// Generated by Farm.js. Do not edit.
|
|
2
|
-
declare module "*.avif" {
|
|
3
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
4
|
-
export const src: string;
|
|
5
|
-
export const width: number;
|
|
6
|
-
export const height: number;
|
|
7
|
-
export const blurDataURL: string | undefined;
|
|
8
|
-
export default image;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare module "*.gif" {
|
|
12
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
13
|
-
export const src: string;
|
|
14
|
-
export const width: number;
|
|
15
|
-
export const height: number;
|
|
16
|
-
export const blurDataURL: string | undefined;
|
|
17
|
-
export default image;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
declare module "*.jpeg" {
|
|
21
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
22
|
-
export const src: string;
|
|
23
|
-
export const width: number;
|
|
24
|
-
export const height: number;
|
|
25
|
-
export const blurDataURL: string | undefined;
|
|
26
|
-
export default image;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
declare module "*.jpg" {
|
|
30
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
31
|
-
export const src: string;
|
|
32
|
-
export const width: number;
|
|
33
|
-
export const height: number;
|
|
34
|
-
export const blurDataURL: string | undefined;
|
|
35
|
-
export default image;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
declare module "*.png" {
|
|
39
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
40
|
-
export const src: string;
|
|
41
|
-
export const width: number;
|
|
42
|
-
export const height: number;
|
|
43
|
-
export const blurDataURL: string | undefined;
|
|
44
|
-
export default image;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
declare module "*.webp" {
|
|
48
|
-
const image: import("@farm.js/core/image").StaticImageData;
|
|
49
|
-
export const src: string;
|
|
50
|
-
export const width: number;
|
|
51
|
-
export const height: number;
|
|
52
|
-
export const blurDataURL: string | undefined;
|
|
53
|
-
export default image;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
declare module "*?url" {
|
|
57
|
-
const src: string;
|
|
58
|
-
export default src;
|
|
59
|
-
}
|