create-fragno 0.1.6 → 0.1.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > create-fragno@0.1.6 build /home/runner/work/fragno/fragno/apps/create-cli
2
+ > create-fragno@0.1.7 build /home/runner/work/fragno/fragno/apps/create-cli
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.15.12 powered by rolldown v1.0.0-beta.45
@@ -9,8 +9,8 @@
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
11
  ℹ Granting execute permission to dist/index.js
12
- ℹ dist/index.js 3.37 kB │ gzip: 1.12 kB
13
- ℹ dist/index.js.map 5.80 kB │ gzip: 1.73 kB
12
+ ℹ dist/index.js 4.91 kB │ gzip: 1.52 kB
13
+ ℹ dist/index.js.map 8.32 kB │ gzip: 2.31 kB
14
14
  ℹ dist/index.d.ts 0.01 kB │ gzip: 0.03 kB
15
- ℹ 3 files, total: 9.19 kB
16
- ✔ Build complete in 2730ms
15
+ ℹ 3 files, total: 13.24 kB
16
+ ✔ Build complete in 5100ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # create-fragno
2
2
 
3
+ ## 0.1.7
4
+
5
+ ### Patch Changes
6
+
7
+ - c472d52: feat: add --non-interactive flag for scripted project creation
8
+ - Updated dependencies [bcf1742]
9
+ - @fragno-dev/create@0.1.7
10
+
3
11
  ## 0.1.6
4
12
 
5
13
  ### Patch Changes
package/dist/index.js CHANGED
@@ -10,17 +10,73 @@ function isInteractive() {
10
10
  runMain(defineCommand({
11
11
  meta: {
12
12
  name: "create",
13
- description: "Interactively create project from template",
13
+ description: "Create a Fragno project from template",
14
14
  version: process.env["npm_package_version"]
15
15
  },
16
- async run() {
17
- p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█
18
- │ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█
19
- │ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);
16
+ args: {
17
+ "non-interactive": {
18
+ type: "boolean",
19
+ description: "Run in non-interactive mode (no prompts)",
20
+ default: false
21
+ },
22
+ name: {
23
+ type: "string",
24
+ description: "Project name (required in non-interactive mode)"
25
+ },
26
+ path: {
27
+ type: "string",
28
+ description: "Project path (default: ./<name>)"
29
+ },
30
+ template: {
31
+ type: "string",
32
+ description: "Template to use (default: fragment)",
33
+ default: "fragment"
34
+ },
35
+ "build-tool": {
36
+ type: "string",
37
+ description: "Build tool (tsdown, vite, esbuild, rollup, webpack, rspack, none; default: tsdown)",
38
+ default: "tsdown"
39
+ },
40
+ "agent-docs": {
41
+ type: "string",
42
+ description: "Agent docs to include (AGENTS.md, CLAUDE.md, none; default: none)",
43
+ default: "none"
44
+ },
45
+ "with-database": {
46
+ type: "boolean",
47
+ description: "Include database layer (default: true)",
48
+ default: true
49
+ }
50
+ },
51
+ async run({ args }) {
52
+ if (args["non-interactive"]) {
53
+ if (!args.name) {
54
+ console.error("Error: --name is required in non-interactive mode.");
55
+ process.exit(1);
56
+ }
57
+ const options$1 = createOptionsSchema.safeParse({
58
+ name: args.name,
59
+ path: args.path ?? `./${args.name}`,
60
+ template: args.template,
61
+ buildTool: args["build-tool"],
62
+ agentDocs: args["agent-docs"],
63
+ withDatabase: args["with-database"]
64
+ });
65
+ if (!options$1.success) {
66
+ console.error("Invalid options:", options$1.error.format());
67
+ process.exit(1);
68
+ }
69
+ create(options$1.data);
70
+ console.log("Project created successfully!");
71
+ return;
72
+ }
20
73
  if (!isInteractive()) {
21
- p.cancel("Cannot run CLI in non-interactive mode.");
74
+ p.cancel("Cannot run in non-interactive terminal. Use --non-interactive flag instead.");
22
75
  process.exit(1);
23
76
  }
77
+ p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█
78
+ │ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█
79
+ │ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);
24
80
  const template = await p.select({
25
81
  message: "Pick a template",
26
82
  options: [{
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { defineCommand, runMain } from \"citty\";\nimport { create, createOptionsSchema } from \"@fragno-dev/create\";\nimport * as p from \"@clack/prompts\";\n\nfunction isInteractive() {\n return Boolean(\n process.stdin.isTTY &&\n process.stdout.isTTY &&\n !process.env[\"CI\"] &&\n process.env[\"TERM\"] !== \"dumb\",\n );\n}\n\nconst main = defineCommand({\n meta: {\n name: \"create\",\n description: \"Interactively create project from template\",\n version: process.env[\"npm_package_version\"],\n },\n async run() {\n p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█\n│ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█\n│ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);\n\n // TODO: allow to pass all options through args\n if (!isInteractive()) {\n p.cancel(\"Cannot run CLI in non-interactive mode.\");\n process.exit(1);\n }\n\n const template = await p.select({\n message: \"Pick a template\",\n options: [{ value: \"fragment\", label: \"Fragment\" }],\n });\n\n if (p.isCancel(template)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const name = await p.text({\n message: \"What is your project name?\",\n placeholder: \"my-fragment\",\n validate(value) {\n if (value.length === 0) {\n return \"Project name is required!\";\n }\n return undefined;\n },\n });\n\n if (p.isCancel(name)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const projectPath = await p.text({\n message: \"Where should we create your project?\",\n placeholder: `./${name}`,\n initialValue: `./${name}`,\n });\n\n if (p.isCancel(projectPath)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const buildTool = await p.select({\n message: \"Pick a build tool\",\n options: [\n { value: \"tsdown\", label: \"tsdown (recommended)\" },\n { value: \"vite\", label: \"vite\" },\n { value: \"esbuild\", label: \"esbuild\" },\n { value: \"rollup\", label: \"rollup\" },\n { value: \"webpack\", label: \"webpack\" },\n { value: \"rspack\", label: \"rspack\" },\n { value: \"none\", label: \"none (bring your own)\" },\n ],\n initialValue: \"tsdown\",\n });\n\n if (p.isCancel(buildTool)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const agent = await p.select({\n message: \"Add AI Agent README?\",\n options: [\n { value: \"AGENTS.md\", label: \"AGENTS.md (for Cursor, Codex and more)\" },\n { value: \"CLAUDE.md\", label: \"CLAUDE.md (for Claude Code)\" },\n { value: \"none\", label: \"skip\" },\n ],\n initialValue: \"AGENTS.md\",\n });\n\n if (p.isCancel(agent)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const withDatabase = await p.select({\n message: \"Include database layer?\",\n options: [\n { value: true, label: \"Yes\" },\n { value: false, label: \"No (skip)\" },\n ],\n initialValue: false,\n });\n\n if (p.isCancel(withDatabase)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const options = createOptionsSchema.safeParse({\n name: name,\n path: projectPath,\n template: template,\n buildTool: buildTool,\n agentDocs: agent,\n withDatabase: withDatabase,\n });\n\n if (!options.success) {\n p.cancel(\"Invalid options!\");\n process.exit(1);\n }\n\n create(options.data);\n\n p.outro(\"Project created successfully!\");\n },\n});\n\nrunMain(main);\n"],"mappings":";;;;;;AAKA,SAAS,gBAAgB;AACvB,QAAO,QACL,QAAQ,MAAM,SACZ,QAAQ,OAAO,SACf,CAAC,QAAQ,IAAI,SACb,QAAQ,IAAI,YAAY,OAC3B;;AA6HH,QA1Ha,cAAc;CACzB,MAAM;EACJ,MAAM;EACN,aAAa;EACb,SAAS,QAAQ,IAAI;EACtB;CACD,MAAM,MAAM;AACV,IAAE,MAAM;;6BAEiB;AAGzB,MAAI,CAAC,eAAe,EAAE;AACpB,KAAE,OAAO,0CAA0C;AACnD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,WAAW,MAAM,EAAE,OAAO;GAC9B,SAAS;GACT,SAAS,CAAC;IAAE,OAAO;IAAY,OAAO;IAAY,CAAC;GACpD,CAAC;AAEF,MAAI,EAAE,SAAS,SAAS,EAAE;AACxB,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,OAAO,MAAM,EAAE,KAAK;GACxB,SAAS;GACT,aAAa;GACb,SAAS,OAAO;AACd,QAAI,MAAM,WAAW,EACnB,QAAO;;GAIZ,CAAC;AAEF,MAAI,EAAE,SAAS,KAAK,EAAE;AACpB,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,cAAc,MAAM,EAAE,KAAK;GAC/B,SAAS;GACT,aAAa,KAAK;GAClB,cAAc,KAAK;GACpB,CAAC;AAEF,MAAI,EAAE,SAAS,YAAY,EAAE;AAC3B,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,MAAM,EAAE,OAAO;GAC/B,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAU,OAAO;KAAwB;IAClD;KAAE,OAAO;KAAQ,OAAO;KAAQ;IAChC;KAAE,OAAO;KAAW,OAAO;KAAW;IACtC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAW,OAAO;KAAW;IACtC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAQ,OAAO;KAAyB;IAClD;GACD,cAAc;GACf,CAAC;AAEF,MAAI,EAAE,SAAS,UAAU,EAAE;AACzB,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,QAAQ,MAAM,EAAE,OAAO;GAC3B,SAAS;GACT,SAAS;IACP;KAAE,OAAO;KAAa,OAAO;KAA0C;IACvE;KAAE,OAAO;KAAa,OAAO;KAA+B;IAC5D;KAAE,OAAO;KAAQ,OAAO;KAAQ;IACjC;GACD,cAAc;GACf,CAAC;AAEF,MAAI,EAAE,SAAS,MAAM,EAAE;AACrB,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,eAAe,MAAM,EAAE,OAAO;GAClC,SAAS;GACT,SAAS,CACP;IAAE,OAAO;IAAM,OAAO;IAAO,EAC7B;IAAE,OAAO;IAAO,OAAO;IAAa,CACrC;GACD,cAAc;GACf,CAAC;AAEF,MAAI,EAAE,SAAS,aAAa,EAAE;AAC5B,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;EAGjB,MAAM,UAAU,oBAAoB,UAAU;GACtC;GACN,MAAM;GACI;GACC;GACX,WAAW;GACG;GACf,CAAC;AAEF,MAAI,CAAC,QAAQ,SAAS;AACpB,KAAE,OAAO,mBAAmB;AAC5B,WAAQ,KAAK,EAAE;;AAGjB,SAAO,QAAQ,KAAK;AAEpB,IAAE,MAAM,gCAAgC;;CAE3C,CAAC,CAEW"}
1
+ {"version":3,"file":"index.js","names":["options"],"sources":["../index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { defineCommand, runMain } from \"citty\";\nimport { create, createOptionsSchema } from \"@fragno-dev/create\";\nimport * as p from \"@clack/prompts\";\n\nfunction isInteractive() {\n return Boolean(\n process.stdin.isTTY &&\n process.stdout.isTTY &&\n !process.env[\"CI\"] &&\n process.env[\"TERM\"] !== \"dumb\",\n );\n}\n\nconst main = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a Fragno project from template\",\n version: process.env[\"npm_package_version\"],\n },\n args: {\n \"non-interactive\": {\n type: \"boolean\",\n description: \"Run in non-interactive mode (no prompts)\",\n default: false,\n },\n name: {\n type: \"string\",\n description: \"Project name (required in non-interactive mode)\",\n },\n path: {\n type: \"string\",\n description: \"Project path (default: ./<name>)\",\n },\n template: {\n type: \"string\",\n description: \"Template to use (default: fragment)\",\n default: \"fragment\",\n },\n \"build-tool\": {\n type: \"string\",\n description:\n \"Build tool (tsdown, vite, esbuild, rollup, webpack, rspack, none; default: tsdown)\",\n default: \"tsdown\",\n },\n \"agent-docs\": {\n type: \"string\",\n description: \"Agent docs to include (AGENTS.md, CLAUDE.md, none; default: none)\",\n default: \"none\",\n },\n \"with-database\": {\n type: \"boolean\",\n description: \"Include database layer (default: true)\",\n default: true,\n },\n },\n async run({ args }) {\n if (args[\"non-interactive\"]) {\n if (!args.name) {\n console.error(\"Error: --name is required in non-interactive mode.\");\n process.exit(1);\n }\n\n const options = createOptionsSchema.safeParse({\n name: args.name,\n path: args.path ?? `./${args.name}`,\n template: args.template,\n buildTool: args[\"build-tool\"],\n agentDocs: args[\"agent-docs\"],\n withDatabase: args[\"with-database\"],\n });\n\n if (!options.success) {\n console.error(\"Invalid options:\", options.error.format());\n process.exit(1);\n }\n\n create(options.data);\n console.log(\"Project created successfully!\");\n return;\n }\n\n // Interactive mode\n if (!isInteractive()) {\n p.cancel(\"Cannot run in non-interactive terminal. Use --non-interactive flag instead.\");\n process.exit(1);\n }\n\n p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█\n│ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█\n│ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);\n\n const template = await p.select({\n message: \"Pick a template\",\n options: [{ value: \"fragment\", label: \"Fragment\" }],\n });\n\n if (p.isCancel(template)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const name = await p.text({\n message: \"What is your project name?\",\n placeholder: \"my-fragment\",\n validate(value) {\n if (value.length === 0) {\n return \"Project name is required!\";\n }\n return undefined;\n },\n });\n\n if (p.isCancel(name)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const projectPath = await p.text({\n message: \"Where should we create your project?\",\n placeholder: `./${name}`,\n initialValue: `./${name}`,\n });\n\n if (p.isCancel(projectPath)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const buildTool = await p.select({\n message: \"Pick a build tool\",\n options: [\n { value: \"tsdown\", label: \"tsdown (recommended)\" },\n { value: \"vite\", label: \"vite\" },\n { value: \"esbuild\", label: \"esbuild\" },\n { value: \"rollup\", label: \"rollup\" },\n { value: \"webpack\", label: \"webpack\" },\n { value: \"rspack\", label: \"rspack\" },\n { value: \"none\", label: \"none (bring your own)\" },\n ],\n initialValue: \"tsdown\",\n });\n\n if (p.isCancel(buildTool)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const agent = await p.select({\n message: \"Add AI Agent README?\",\n options: [\n { value: \"AGENTS.md\", label: \"AGENTS.md (for Cursor, Codex and more)\" },\n { value: \"CLAUDE.md\", label: \"CLAUDE.md (for Claude Code)\" },\n { value: \"none\", label: \"skip\" },\n ],\n initialValue: \"AGENTS.md\",\n });\n\n if (p.isCancel(agent)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const withDatabase = await p.select({\n message: \"Include database layer?\",\n options: [\n { value: true, label: \"Yes\" },\n { value: false, label: \"No (skip)\" },\n ],\n initialValue: false,\n });\n\n if (p.isCancel(withDatabase)) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const options = createOptionsSchema.safeParse({\n name: name,\n path: projectPath,\n template: template,\n buildTool: buildTool,\n agentDocs: agent,\n withDatabase: withDatabase,\n });\n\n if (!options.success) {\n p.cancel(\"Invalid options!\");\n process.exit(1);\n }\n\n create(options.data);\n\n p.outro(\"Project created successfully!\");\n },\n});\n\nrunMain(main);\n"],"mappings":"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"}
package/index.test.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { test, expect, afterEach } from "vitest";
2
+ import { exec } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+ import fs from "node:fs/promises";
7
+
8
+ const execAsync = promisify(exec);
9
+
10
+ const CLI_PATH = path.resolve(import.meta.dirname, "index.ts");
11
+ const TSX_PATH = path.resolve(import.meta.dirname, "../../node_modules/.bin/tsx");
12
+
13
+ async function createTempDir(): Promise<string> {
14
+ return fs.mkdtemp(path.join(tmpdir(), "create-cli-test-"));
15
+ }
16
+
17
+ async function runCli(args: string, cwd: string): Promise<{ stdout: string; stderr: string }> {
18
+ return execAsync(`${TSX_PATH} ${CLI_PATH} ${args}`, { cwd, encoding: "utf8" });
19
+ }
20
+
21
+ afterEach<{ tempDir?: string }>(async (ctx) => {
22
+ if (ctx.tempDir) {
23
+ await fs.rm(ctx.tempDir, { recursive: true });
24
+ }
25
+ });
26
+
27
+ test("non-interactive mode creates project with defaults", async (ctx) => {
28
+ const tempDir = await createTempDir();
29
+ Object.assign(ctx, { tempDir });
30
+
31
+ const projectPath = path.join(tempDir, "my-test-fragment");
32
+ const { stdout } = await runCli(
33
+ `--non-interactive --name my-test-fragment --path ${projectPath}`,
34
+ tempDir,
35
+ );
36
+
37
+ expect(stdout).toContain("Project created successfully!");
38
+
39
+ // Verify package.json was created with correct name
40
+ const pkg = JSON.parse(await fs.readFile(path.join(projectPath, "package.json"), "utf8"));
41
+ expect(pkg.name).toBe("my-test-fragment");
42
+
43
+ // Verify database files are included (default: withDatabase=true)
44
+ await expect(fs.access(path.join(projectPath, "src", "schema.ts"))).resolves.toBeUndefined();
45
+
46
+ // Verify agent docs are NOT included (default: agentDocs=none)
47
+ await expect(fs.access(path.join(projectPath, "AGENTS.md"))).rejects.toThrow();
48
+ await expect(fs.access(path.join(projectPath, "CLAUDE.md"))).rejects.toThrow();
49
+ });
package/index.ts CHANGED
@@ -15,20 +15,81 @@ function isInteractive() {
15
15
  const main = defineCommand({
16
16
  meta: {
17
17
  name: "create",
18
- description: "Interactively create project from template",
18
+ description: "Create a Fragno project from template",
19
19
  version: process.env["npm_package_version"],
20
20
  },
21
- async run() {
22
- p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█
23
- │ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█
24
- │ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);
21
+ args: {
22
+ "non-interactive": {
23
+ type: "boolean",
24
+ description: "Run in non-interactive mode (no prompts)",
25
+ default: false,
26
+ },
27
+ name: {
28
+ type: "string",
29
+ description: "Project name (required in non-interactive mode)",
30
+ },
31
+ path: {
32
+ type: "string",
33
+ description: "Project path (default: ./<name>)",
34
+ },
35
+ template: {
36
+ type: "string",
37
+ description: "Template to use (default: fragment)",
38
+ default: "fragment",
39
+ },
40
+ "build-tool": {
41
+ type: "string",
42
+ description:
43
+ "Build tool (tsdown, vite, esbuild, rollup, webpack, rspack, none; default: tsdown)",
44
+ default: "tsdown",
45
+ },
46
+ "agent-docs": {
47
+ type: "string",
48
+ description: "Agent docs to include (AGENTS.md, CLAUDE.md, none; default: none)",
49
+ default: "none",
50
+ },
51
+ "with-database": {
52
+ type: "boolean",
53
+ description: "Include database layer (default: true)",
54
+ default: true,
55
+ },
56
+ },
57
+ async run({ args }) {
58
+ if (args["non-interactive"]) {
59
+ if (!args.name) {
60
+ console.error("Error: --name is required in non-interactive mode.");
61
+ process.exit(1);
62
+ }
63
+
64
+ const options = createOptionsSchema.safeParse({
65
+ name: args.name,
66
+ path: args.path ?? `./${args.name}`,
67
+ template: args.template,
68
+ buildTool: args["build-tool"],
69
+ agentDocs: args["agent-docs"],
70
+ withDatabase: args["with-database"],
71
+ });
72
+
73
+ if (!options.success) {
74
+ console.error("Invalid options:", options.error.format());
75
+ process.exit(1);
76
+ }
77
+
78
+ create(options.data);
79
+ console.log("Project created successfully!");
80
+ return;
81
+ }
25
82
 
26
- // TODO: allow to pass all options through args
83
+ // Interactive mode
27
84
  if (!isInteractive()) {
28
- p.cancel("Cannot run CLI in non-interactive mode.");
85
+ p.cancel("Cannot run in non-interactive terminal. Use --non-interactive flag instead.");
29
86
  process.exit(1);
30
87
  }
31
88
 
89
+ p.intro(`░█▀▀░█▀▄░█▀█░█▀▀░█▀█░█▀█
90
+ │ ░█▀▀░█▀▄░█▀█░█░█░█░█░█░█
91
+ │ ░▀░░░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀`);
92
+
32
93
  const template = await p.select({
33
94
  message: "Pick a template",
34
95
  options: [{ value: "fragment", label: "Fragment" }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fragno",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "exports": {
5
5
  ".": {
6
6
  "development": "./index.ts",
@@ -16,14 +16,14 @@
16
16
  "node": ">=22"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/node": "^22",
20
- "@fragno-private/typescript-config": "0.0.1",
21
- "@fragno-private/vitest-config": "0.0.0"
19
+ "@types/node": "^22.19.7",
20
+ "@fragno-private/vitest-config": "0.0.0",
21
+ "@fragno-private/typescript-config": "0.0.1"
22
22
  },
23
23
  "dependencies": {
24
24
  "@clack/prompts": "^0.11.0",
25
25
  "citty": "^0.1.6",
26
- "@fragno-dev/create": "0.1.6"
26
+ "@fragno-dev/create": "0.1.7"
27
27
  },
28
28
  "private": false,
29
29
  "main": "./dist/index.js",
@@ -39,6 +39,7 @@
39
39
  "scripts": {
40
40
  "build": "tsdown",
41
41
  "build:watch": "tsdown --watch",
42
+ "test": "vitest run",
42
43
  "types:check": "tsc --noEmit"
43
44
  }
44
45
  }
package/vitest.config.ts CHANGED
@@ -1,4 +1,12 @@
1
- import { defineConfig } from "vitest/config";
1
+ import { defineConfig, mergeConfig } from "vitest/config";
2
2
  import { baseConfig } from "@fragno-private/vitest-config";
3
3
 
4
- export default defineConfig(baseConfig);
4
+ export default defineConfig(
5
+ mergeConfig(baseConfig, {
6
+ test: {
7
+ coverage: {
8
+ enabled: false,
9
+ },
10
+ },
11
+ }),
12
+ );