@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 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
- npm create @farm.js/app@beta
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.
@@ -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(" pnpm install");
95
- require_utils.logger.info(" pnpm dev");
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 destPath = path.default.join(dest, entry.name);
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 (error) {
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(" pnpm install");
90
- logger.info(" pnpm dev");
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 destPath = path.join(dest, entry.name);
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 (error) {
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/create-app",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.7",
4
4
  "description": "Create a new Farm.js application",
5
5
  "keywords": [
6
6
  "@farm.js/create-app",
@@ -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.5",
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.5",
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
- }