@ciderjs/gasbombe 0.1.0 → 0.2.1

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.ja.md CHANGED
@@ -44,8 +44,9 @@ gasbombe
44
44
  以下の項目について質問されます。
45
45
 
46
46
  1. プロジェクト名
47
- 2. パッケージマネージャー(npm, yarn, pnpm
48
- 3. テンプレート(Vanilla TS, React)
47
+ 2. プロジェクトテンプレート(Vanilla TS, React
48
+ 3. Apps Scriptプロジェクトのセットアップ方法(`.clasp.json`)
49
+ 4. パッケージマネージャー(npm, yarn, pnpm)
49
50
 
50
51
  このツールは、指定されたプロジェクト名で新しいディレクトリを作成し、テンプレートファイルを生成して、依存関係をインストールします。
51
52
 
@@ -54,15 +55,17 @@ gasbombe
54
55
  コマンドラインオプションを指定することで、対話型のプロンプトを省略できます。これは、スクリプトや自動化に便利です。
55
56
 
56
57
  ```bash
57
- # 例: pnpmを使用して新しいReactプロジェクトを作成する
58
- gasbombe --name my-react-app --pkg pnpm --template react-tsx
58
+ # 例: pnpmを使用して新しいReactプロジェクトを作成し、同時に新しいApps Scriptプロジェクトも作成する
59
+ gasbombe --name my-react-app --template react-tsx --clasp create --pkg pnpm
59
60
  ```
60
61
 
61
- | オプション | 引数 | 説明 | 選択肢 |
62
- | :--- | :--- | :--- | :--- |
63
- | `--name` | `[projectName]` | 生成するプロジェクトの名前。 | - |
64
- | `--pkg` | `[packageManager]` | 使用するパッケージマネージャー。 | `npm`, `pnpm`, `yarn` |
65
- | `--template` | `[templateType]` | 使用するプロジェクトテンプレート。 | `vanilla-ts`, `react-tsx` |
62
+ | オプション | エイリアス | 引数 | 説明 | 選択肢 |
63
+ | :--- | :--- | :--- | :--- | :--- |
64
+ | `--name` | `-n` | `[projectName]` | 生成するプロジェクトの名前。 | - |
65
+ | `--template` | `-t` | `[templateType]` | 使用するプロジェクトテンプレート。 | `vanilla-ts`, `react-tsx` |
66
+ | `--clasp` | `-c` | `[claspOption]` | `.clasp.json`のセットアップ方法。<br/>`create`と`list`は事前にclaspへのログインが必要です。 | `create`, `list`, `input`, `skip` |
67
+ | `--pkg` | `-p` | `[packageManager]` | 使用するパッケージマネージャー。 | `npm`, `pnpm`, `yarn` |
68
+ | `--skipInstall` | | | 依存関係のインストールをスキップします。 | - |
66
69
 
67
70
  これらのオプションのいずれかが省略された場合、対話形式で値を入力するよう求められます。
68
71
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # **GasBombe**
1
+ # **Gasbombe**
2
2
 
3
- [![README-ja](https://img.shields.io/badge/English-blue?logo=ReadMe)](./README.ja.md)
3
+ [![README-ja](https://img.shields.io/badge/日本語-blue?logo=ReadMe)](./README.ja.md)
4
4
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
  [![npm version](https://img.shields.io/npm/v/@ciderjs/gasbombe.svg)](https://www.npmjs.com/package/@ciderjs/gasbombe)
6
6
  [![GitHub issues](https://img.shields.io/github/issues/luthpg/gasbombe.svg)](https://github.com/luthpg/gasbombe/issues)
@@ -44,8 +44,9 @@ gasbombe
44
44
  You will be asked for:
45
45
 
46
46
  1. Project name
47
- 2. Package manager (npm, yarn, pnpm)
48
- 3. Template (Vanilla TS, React)
47
+ 2. Project template (Vanilla TS, React)
48
+ 3. How to set up the Apps Script project (`.clasp.json`)
49
+ 4. Package manager (npm, yarn, pnpm)
49
50
 
50
51
  The tool will create a new directory with the specified project name, generate the template files, and install the dependencies.
51
52
 
@@ -54,15 +55,17 @@ The tool will create a new directory with the specified project name, generate t
54
55
  You can bypass the interactive prompts by providing command-line options. This is useful for scripting and automation.
55
56
 
56
57
  ```bash
57
- # Example: Create a new React project with pnpm
58
- gasbombe --name my-react-app --pkg pnpm --template react-tsx
58
+ # Example: Create a new React project with pnpm, creating a new Apps Script project along with it
59
+ gasbombe --name my-react-app --template react-tsx --clasp create --pkg pnpm
59
60
  ```
60
61
 
61
- | Option | Argument | Description | Choices |
62
- | ---------- | ------------------ | -------------------------------- | ------------------------- |
63
- | `--name` | `[projectName]` | The name of the project to generate. | - |
64
- | `--pkg` | `[packageManager]` | The package manager to use. | `npm`, `pnpm`, `yarn` |
65
- | `--template` | `[templateType]` | The project template to use. | `vanilla-ts`, `react-tsx` |
62
+ | Option | Alias | Argument | Description | Choices |
63
+ | :--- | :--- | :--- | :--- | :--- |
64
+ | `--name` | `-n` | `[projectName]` | The name of the project to generate. | - |
65
+ | `--template` | `-t` | `[templateType]` | The project template to use. | `vanilla-ts`, `react-tsx` |
66
+ | `--clasp` | `-c` | `[claspOption]` | How to set up the `.clasp.json` file.<br/>`create` and `list` require prior login to clasp. | `create`, `list`, `input`, `skip` |
67
+ | `--pkg` | `-p` | `[packageManager]` | The package manager to use. | `npm`, `pnpm`, `yarn` |
68
+ | `--skipInstall` | | | Skip installing dependencies. | - |
66
69
 
67
70
  If any of these options are omitted, you will be prompted to enter the value interactively.
68
71
 
package/dist/cli.cjs CHANGED
@@ -11,46 +11,130 @@ require('consola');
11
11
  require('ejs');
12
12
  require('glob');
13
13
 
14
- const version = "0.1.0";
14
+ const version = "0.2.1";
15
15
 
16
16
  async function main() {
17
17
  const program = new commander.Command();
18
18
  program.version(version, "-v, --version");
19
19
  program.description("Create project for GoogleAppsScript").option(
20
- "--name [projectName]",
21
- "Project name what you want to generate",
20
+ "-n, --name [projectName]",
21
+ 'Project name what you want to generate\n* Project name must be "." or a valid folder name without path characters.',
22
22
  ""
23
23
  ).option(
24
- "--pkg [packageManager]",
24
+ "-p, --pkg [packageManager]",
25
25
  'Package manager what you want to use ("npm" | "pnpm" | "yarn")',
26
26
  ""
27
27
  ).option(
28
- "--template [templateType]",
28
+ "-t, --template [templateType]",
29
29
  'Project template label ("vanilla-ts" | "react-tsx")',
30
30
  ""
31
+ ).option(
32
+ "-c, --clasp [claspSetupCommand]",
33
+ 'Setup command how you want to use for Apps Script Project (create project | select project | input project id | skip)\n* If you use "create" or "select", you need to login to clasp first',
34
+ ""
35
+ ).option(
36
+ "--skipInstall",
37
+ "Skip install dependencies after generating the project",
38
+ false
31
39
  ).action(async (_param, command) => {
32
40
  let {
33
41
  name: projectName,
34
42
  pkg: packageManager,
35
- template: templateType
43
+ template: templateType,
44
+ clasp,
45
+ skipInstall
36
46
  } = command.opts();
47
+ let claspProjectId;
37
48
  try {
49
+ const validProjectNameRegex = /^(?!.*\.\.)[a-zA-Z0-9_-]+$/;
38
50
  projectName ||= await prompts.input({
39
- message: "Input project name what you want to generate..."
40
- });
41
- packageManager ||= await prompts.select({
42
- message: "Choise package manager what you want to use...",
43
- choices: ["npm", "pnpm", "yarn"]
51
+ message: "Input project name what you want to generate...",
52
+ validate: (value) => {
53
+ if (value === ".") {
54
+ return true;
55
+ }
56
+ if (validProjectNameRegex.test(value) && !value.includes("/") && !value.includes("\\")) {
57
+ return true;
58
+ }
59
+ return 'Project name must be "." or a valid folder name without path characters.';
60
+ }
44
61
  });
62
+ if (!validProjectNameRegex.test(projectName)) {
63
+ throw Error("Invalid project name");
64
+ }
45
65
  templateType ||= await prompts.select({
46
- message: "Choise project template...",
47
- choices: ["vanilla-ts", "react-tsx"]
66
+ message: "Choice project template...",
67
+ choices: [
68
+ { name: "vanilla-ts", value: "vanilla-ts" },
69
+ { name: "react-tsx", value: "react-tsx" }
70
+ ]
71
+ });
72
+ const templateTypes = ["vanilla-ts", "react-tsx"];
73
+ if (!templateTypes.includes(templateType)) {
74
+ throw Error("Invalid project template");
75
+ }
76
+ clasp ||= await prompts.select({
77
+ message: 'How do you want to set up the Apps Script project?\n* If you use "create" or "select", you need to login to clasp first',
78
+ choices: [
79
+ {
80
+ name: "[NEW] Create a new Apps Script project (need `npx @google/clasp login`)",
81
+ value: "create",
82
+ description: "Runs `npx @google/clasp create` to generate a new project."
83
+ },
84
+ {
85
+ name: "[SELECT] Use an existing Apps Script project (need `npx @google/clasp login`)",
86
+ value: "list",
87
+ description: "Runs `npx @google/clasp list` and lets you choose from your projects."
88
+ },
89
+ {
90
+ name: "[INPUT] Input Script ID manually",
91
+ value: "input",
92
+ description: "Manually provide an existing Script ID."
93
+ },
94
+ {
95
+ name: "[NONE] Skip for now",
96
+ value: "skip",
97
+ description: "Create a `.clasp.json` file without Apps Script project ID."
98
+ }
99
+ ]
100
+ });
101
+ const claspOptions = ["create", "list", "input", "skip"];
102
+ if (!claspOptions.includes(clasp)) {
103
+ throw Error("Invalid clasp option");
104
+ }
105
+ if (clasp === "input") {
106
+ claspProjectId = await prompts.input({
107
+ message: "Input Apps Script project ID...",
108
+ required: false
109
+ });
110
+ }
111
+ if (packageManager === "" && clasp !== "create" && clasp !== "list" && skipInstall) {
112
+ packageManager = "npm";
113
+ }
114
+ packageManager ||= await prompts.select({
115
+ message: "Choice package manager what you want to use...",
116
+ choices: [
117
+ { name: "npm", value: "npm" },
118
+ { name: "pnpm", value: "pnpm" },
119
+ { name: "yarn", value: "yarn" }
120
+ ]
48
121
  });
122
+ const packageManagers = ["npm", "pnpm", "yarn"];
123
+ if (!packageManagers.includes(packageManager)) {
124
+ throw Error("Invalid package manager");
125
+ }
49
126
  } catch (e) {
50
127
  e.message === "User force closed the prompt with SIGINT" && process.exit(0);
51
128
  throw e;
52
129
  }
53
- await index.generateProject({ projectName, packageManager, templateType });
130
+ await index.generateProject({
131
+ projectName,
132
+ packageManager,
133
+ templateType,
134
+ clasp,
135
+ claspProjectId,
136
+ install: !skipInstall
137
+ });
54
138
  });
55
139
  program.parse(process.argv);
56
140
  }
package/dist/cli.mjs CHANGED
@@ -9,46 +9,130 @@ import 'consola';
9
9
  import 'ejs';
10
10
  import 'glob';
11
11
 
12
- const version = "0.1.0";
12
+ const version = "0.2.1";
13
13
 
14
14
  async function main() {
15
15
  const program = new Command();
16
16
  program.version(version, "-v, --version");
17
17
  program.description("Create project for GoogleAppsScript").option(
18
- "--name [projectName]",
19
- "Project name what you want to generate",
18
+ "-n, --name [projectName]",
19
+ 'Project name what you want to generate\n* Project name must be "." or a valid folder name without path characters.',
20
20
  ""
21
21
  ).option(
22
- "--pkg [packageManager]",
22
+ "-p, --pkg [packageManager]",
23
23
  'Package manager what you want to use ("npm" | "pnpm" | "yarn")',
24
24
  ""
25
25
  ).option(
26
- "--template [templateType]",
26
+ "-t, --template [templateType]",
27
27
  'Project template label ("vanilla-ts" | "react-tsx")',
28
28
  ""
29
+ ).option(
30
+ "-c, --clasp [claspSetupCommand]",
31
+ 'Setup command how you want to use for Apps Script Project (create project | select project | input project id | skip)\n* If you use "create" or "select", you need to login to clasp first',
32
+ ""
33
+ ).option(
34
+ "--skipInstall",
35
+ "Skip install dependencies after generating the project",
36
+ false
29
37
  ).action(async (_param, command) => {
30
38
  let {
31
39
  name: projectName,
32
40
  pkg: packageManager,
33
- template: templateType
41
+ template: templateType,
42
+ clasp,
43
+ skipInstall
34
44
  } = command.opts();
45
+ let claspProjectId;
35
46
  try {
47
+ const validProjectNameRegex = /^(?!.*\.\.)[a-zA-Z0-9_-]+$/;
36
48
  projectName ||= await input({
37
- message: "Input project name what you want to generate..."
38
- });
39
- packageManager ||= await select({
40
- message: "Choise package manager what you want to use...",
41
- choices: ["npm", "pnpm", "yarn"]
49
+ message: "Input project name what you want to generate...",
50
+ validate: (value) => {
51
+ if (value === ".") {
52
+ return true;
53
+ }
54
+ if (validProjectNameRegex.test(value) && !value.includes("/") && !value.includes("\\")) {
55
+ return true;
56
+ }
57
+ return 'Project name must be "." or a valid folder name without path characters.';
58
+ }
42
59
  });
60
+ if (!validProjectNameRegex.test(projectName)) {
61
+ throw Error("Invalid project name");
62
+ }
43
63
  templateType ||= await select({
44
- message: "Choise project template...",
45
- choices: ["vanilla-ts", "react-tsx"]
64
+ message: "Choice project template...",
65
+ choices: [
66
+ { name: "vanilla-ts", value: "vanilla-ts" },
67
+ { name: "react-tsx", value: "react-tsx" }
68
+ ]
69
+ });
70
+ const templateTypes = ["vanilla-ts", "react-tsx"];
71
+ if (!templateTypes.includes(templateType)) {
72
+ throw Error("Invalid project template");
73
+ }
74
+ clasp ||= await select({
75
+ message: 'How do you want to set up the Apps Script project?\n* If you use "create" or "select", you need to login to clasp first',
76
+ choices: [
77
+ {
78
+ name: "[NEW] Create a new Apps Script project (need `npx @google/clasp login`)",
79
+ value: "create",
80
+ description: "Runs `npx @google/clasp create` to generate a new project."
81
+ },
82
+ {
83
+ name: "[SELECT] Use an existing Apps Script project (need `npx @google/clasp login`)",
84
+ value: "list",
85
+ description: "Runs `npx @google/clasp list` and lets you choose from your projects."
86
+ },
87
+ {
88
+ name: "[INPUT] Input Script ID manually",
89
+ value: "input",
90
+ description: "Manually provide an existing Script ID."
91
+ },
92
+ {
93
+ name: "[NONE] Skip for now",
94
+ value: "skip",
95
+ description: "Create a `.clasp.json` file without Apps Script project ID."
96
+ }
97
+ ]
98
+ });
99
+ const claspOptions = ["create", "list", "input", "skip"];
100
+ if (!claspOptions.includes(clasp)) {
101
+ throw Error("Invalid clasp option");
102
+ }
103
+ if (clasp === "input") {
104
+ claspProjectId = await input({
105
+ message: "Input Apps Script project ID...",
106
+ required: false
107
+ });
108
+ }
109
+ if (packageManager === "" && clasp !== "create" && clasp !== "list" && skipInstall) {
110
+ packageManager = "npm";
111
+ }
112
+ packageManager ||= await select({
113
+ message: "Choice package manager what you want to use...",
114
+ choices: [
115
+ { name: "npm", value: "npm" },
116
+ { name: "pnpm", value: "pnpm" },
117
+ { name: "yarn", value: "yarn" }
118
+ ]
46
119
  });
120
+ const packageManagers = ["npm", "pnpm", "yarn"];
121
+ if (!packageManagers.includes(packageManager)) {
122
+ throw Error("Invalid package manager");
123
+ }
47
124
  } catch (e) {
48
125
  e.message === "User force closed the prompt with SIGINT" && process.exit(0);
49
126
  throw e;
50
127
  }
51
- await generateProject({ projectName, packageManager, templateType });
128
+ await generateProject({
129
+ projectName,
130
+ packageManager,
131
+ templateType,
132
+ clasp,
133
+ claspProjectId,
134
+ install: !skipInstall
135
+ });
52
136
  });
53
137
  program.parse(process.argv);
54
138
  }
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  const node_child_process = require('node:child_process');
4
4
  const fs = require('node:fs/promises');
5
5
  const path = require('node:path');
6
+ const prompts = require('@inquirer/prompts');
6
7
  const consola = require('consola');
7
8
  const ejs = require('ejs');
8
9
  const glob = require('glob');
@@ -13,18 +14,30 @@ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
13
14
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
14
15
  const ejs__default = /*#__PURE__*/_interopDefaultCompat(ejs);
15
16
 
16
- async function runCommand(command, args, cwd) {
17
+ async function runCommand(command, args, cwd, capture = false) {
17
18
  return new Promise((resolve, reject) => {
18
19
  const child = node_child_process.spawn(command, args, {
19
20
  cwd,
20
- stdio: "inherit",
21
+ stdio: capture ? "pipe" : "inherit",
21
22
  shell: true
22
23
  });
24
+ let stdout = "";
25
+ let stderr = "";
26
+ if (capture) {
27
+ child.stdout?.on("data", (data) => {
28
+ stdout += data.toString();
29
+ });
30
+ child.stderr?.on("data", (data) => {
31
+ stderr += data.toString();
32
+ });
33
+ }
23
34
  child.on("close", (code) => {
24
35
  if (code === 0) {
25
- resolve();
36
+ resolve(stdout.trim());
26
37
  } else {
27
- reject(new Error(`Command failed with exit code ${code}`));
38
+ const errorMsg = `Command failed with exit code ${code}${stderr ? `:
39
+ ${stderr}` : ""}`;
40
+ reject(new Error(errorMsg));
28
41
  }
29
42
  });
30
43
  child.on("error", (err) => {
@@ -32,10 +45,132 @@ async function runCommand(command, args, cwd) {
32
45
  });
33
46
  });
34
47
  }
48
+ async function handleClaspSetup(claspOption, projectName, outputDir, claspProjectId, packageManager = "npm") {
49
+ if (claspOption === "skip") {
50
+ return;
51
+ }
52
+ const npxLikeCommand = packageManager === "npm" ? "npx" : packageManager === "pnpm" ? "pnpx" : "yarn";
53
+ consola.consola.start(
54
+ `Setting up .clasp.json with \`${npxLikeCommand} @google/clasp\`...`
55
+ );
56
+ if (claspOption === "create" || claspOption === "list") {
57
+ try {
58
+ await runCommand(npxLikeCommand, ["@google/clasp", "status"], outputDir);
59
+ } catch {
60
+ consola.consola.error(
61
+ `It seems you are not logged in to clasp. Please run \`${npxLikeCommand} @google/clasp login\` and try again.`
62
+ );
63
+ return;
64
+ }
65
+ }
66
+ let scriptId;
67
+ let existingClaspJson = null;
68
+ switch (claspOption) {
69
+ case "create":
70
+ try {
71
+ const claspJsonPath = path__default.join(outputDir, ".clasp.json");
72
+ try {
73
+ const existingContent = await fs__default.readFile(claspJsonPath, "utf-8");
74
+ existingClaspJson = JSON.parse(existingContent);
75
+ await fs__default.unlink(claspJsonPath);
76
+ } catch {
77
+ }
78
+ const result = await runCommand(
79
+ npxLikeCommand,
80
+ [
81
+ "@google/clasp",
82
+ "create",
83
+ "--title",
84
+ `"${projectName}"`,
85
+ "--type",
86
+ "standalone"
87
+ ],
88
+ outputDir,
89
+ true
90
+ );
91
+ const match = result.match(/Created new script: .+\/d\/(.+)\/edit/);
92
+ if (match?.[1]) {
93
+ scriptId = match[1];
94
+ consola.consola.info(`Created new Apps Script project with ID: ${scriptId}`);
95
+ } else {
96
+ throw new Error("Could not parse scriptId from clasp output.");
97
+ }
98
+ } catch (e) {
99
+ consola.consola.error("Failed to create new Apps Script project.", e);
100
+ return;
101
+ }
102
+ break;
103
+ case "list":
104
+ try {
105
+ const listOutput = await runCommand(
106
+ npxLikeCommand,
107
+ ["@google/clasp", "list"],
108
+ outputDir,
109
+ true
110
+ );
111
+ const projects = listOutput.split("\n").slice(1).map((line) => {
112
+ const parts = line.split(" - ");
113
+ if (parts.length >= 2) {
114
+ const name = parts[0]?.trim();
115
+ const url = parts[1]?.trim();
116
+ if (!name || !url) return null;
117
+ const match = url.match(/\/d\/(.+)\/edit/);
118
+ const scriptId2 = match?.[1];
119
+ if (scriptId2) {
120
+ return { name, value: scriptId2 };
121
+ }
122
+ }
123
+ return null;
124
+ }).filter((p) => p !== null);
125
+ if (projects.length === 0) {
126
+ consola.consola.warn(
127
+ "No existing Apps Script projects found. Please create one first."
128
+ );
129
+ return;
130
+ }
131
+ scriptId = await prompts.select({
132
+ message: "Choose an existing Apps Script project:",
133
+ choices: projects
134
+ });
135
+ } catch (e) {
136
+ consola.consola.error("Failed to list Apps Script projects.", e);
137
+ return;
138
+ }
139
+ break;
140
+ case "input":
141
+ scriptId = claspProjectId;
142
+ break;
143
+ }
144
+ if (scriptId) {
145
+ const claspJsonPath = path__default.join(outputDir, ".clasp.json");
146
+ let claspJson = {
147
+ scriptId
148
+ };
149
+ let successMessage = `.clasp.json created successfully with scriptId: ${scriptId}`;
150
+ if (existingClaspJson) {
151
+ claspJson = { ...existingClaspJson, scriptId };
152
+ successMessage = `.clasp.json updated successfully with scriptId: ${scriptId}`;
153
+ } else {
154
+ try {
155
+ const existingContent = await fs__default.readFile(claspJsonPath, "utf-8");
156
+ const currentClaspJson = JSON.parse(existingContent);
157
+ claspJson = { ...currentClaspJson, scriptId };
158
+ successMessage = `.clasp.json updated successfully with scriptId: ${scriptId}`;
159
+ } catch {
160
+ }
161
+ }
162
+ const claspJsonContent = JSON.stringify(claspJson, null, 2);
163
+ await fs__default.writeFile(claspJsonPath, claspJsonContent, { encoding: "utf-8" });
164
+ consola.consola.success(successMessage);
165
+ }
166
+ }
35
167
  async function generateProject({
36
168
  projectName,
37
169
  packageManager,
38
- templateType
170
+ templateType,
171
+ clasp,
172
+ claspProjectId,
173
+ install
39
174
  }) {
40
175
  const outputDir = path__default.resolve(process.cwd(), projectName);
41
176
  const templateBaseDir = path__default.resolve(__dirname, "..", "dist", "templates");
@@ -44,11 +179,28 @@ async function generateProject({
44
179
  consola.consola.start(
45
180
  `Creating a new Project for GoogleAppsScript in ${outputDir}...`
46
181
  );
47
- try {
48
- await fs__default.access(outputDir);
49
- consola.consola.error(`Directory ${projectName} already exists.`);
50
- process.exit(1);
51
- } catch {
182
+ if (projectName === ".") {
183
+ try {
184
+ const files = await fs__default.readdir(outputDir);
185
+ const relevantFiles = files.filter((file) => !file.startsWith("."));
186
+ if (relevantFiles.length > 0) {
187
+ const proceed = await confirm(
188
+ "Current directory is not empty. Proceed anyway?"
189
+ );
190
+ if (!proceed) {
191
+ consola.consola.warn("Operation cancelled.");
192
+ process.exit(0);
193
+ }
194
+ }
195
+ } catch {
196
+ }
197
+ } else {
198
+ try {
199
+ await fs__default.access(outputDir);
200
+ consola.consola.error(`Directory ${projectName} already exists.`);
201
+ process.exit(1);
202
+ } catch {
203
+ }
52
204
  }
53
205
  await fs__default.mkdir(outputDir, { recursive: true });
54
206
  consola.consola.info(`Generating project files from template '${templateType}'...`);
@@ -58,30 +210,36 @@ async function generateProject({
58
210
  const files = await glob.glob("./**/*", {
59
211
  cwd: dir,
60
212
  nodir: true,
61
- dot: true,
62
- dotRelative: true,
63
- follow: true,
64
- windowsPathsNoEscape: true
213
+ dot: true
65
214
  });
66
215
  for (const file of files) {
67
- const templatePath = path__default.join(dir, file);
68
- const outputPath = path__default.join(outputDir, file.replace(".ejs", ""));
216
+ const relativePath = path__default.relative(dir, path__default.resolve(dir, file));
217
+ const templatePath = path__default.join(dir, relativePath);
218
+ const outputPath = path__default.join(outputDir, relativePath.replace(".ejs", ""));
69
219
  await fs__default.mkdir(path__default.dirname(outputPath), { recursive: true });
70
220
  const templateContent = await fs__default.readFile(templatePath, {
71
221
  encoding: "utf-8"
72
222
  });
73
223
  const renderedContent = ejs__default.render(templateContent, ejsData);
74
- await fs__default.writeFile(outputPath, renderedContent);
224
+ await fs__default.writeFile(outputPath, renderedContent, { encoding: "utf-8" });
75
225
  }
76
226
  }
77
- consola.consola.start(`Installing dependencies with ${packageManager}...`);
78
- try {
79
- await runCommand(packageManager, ["install"], outputDir);
80
- consola.consola.success(`Dependencies installed successfully.`);
81
- } catch (e) {
82
- consola.consola.fail("Failed to install dependencies.");
83
- consola.consola.error(e);
84
- process.exit(1);
227
+ await handleClaspSetup(
228
+ clasp,
229
+ projectName,
230
+ outputDir,
231
+ claspProjectId,
232
+ packageManager
233
+ );
234
+ if (install) {
235
+ consola.consola.start(`Installing dependencies with ${packageManager}...`);
236
+ try {
237
+ await runCommand(packageManager, ["install"], outputDir);
238
+ consola.consola.success(`Dependencies installed successfully.`);
239
+ } catch (e) {
240
+ consola.consola.fail("Failed to install dependencies. Please do it manually.");
241
+ consola.consola.error(e);
242
+ }
85
243
  }
86
244
  consola.consola.start(`Initializing Git repository...`);
87
245
  try {
@@ -93,15 +251,28 @@ async function generateProject({
93
251
  outputDir
94
252
  );
95
253
  consola.consola.success(`Git repository initialized successfully.`);
96
- } catch {
254
+ } catch (e) {
97
255
  consola.consola.fail("Failed to initialize Git repository. Please do it manually.");
256
+ consola.consola.error(e);
98
257
  }
99
258
  consola.consola.success(`Project '${projectName}' created successfully!`);
100
- consola.consola.log(`
259
+ const messages = [];
260
+ projectName !== "." && messages.push(` cd ${projectName}`);
261
+ !install && messages.push(` ${packageManager} install`);
262
+ templateType !== "vanilla-ts" && messages.push(` ${packageManager} dev`);
263
+ if (messages.length > 0) {
264
+ consola.consola.log(`
101
265
  To get started, run:
102
266
  `);
103
- projectName !== "." && consola.consola.log(` cd ${projectName}`);
104
- templateType !== "vanilla-ts" ? consola.consola.log(` ${packageManager} dev`) : consola.consola.log(` ...and write your GAS code!`);
267
+ for (const message of messages) {
268
+ consola.consola.log(message);
269
+ }
270
+ consola.consola.log("");
271
+ consola.consola.log(`...and write your GAS code!`);
272
+ } else {
273
+ consola.consola.log(`
274
+ To get started, write your GAS code in \`src/\`!`);
275
+ }
105
276
  }
106
277
 
107
278
  exports.generateProject = generateProject;