@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 +12 -9
- package/README.md +14 -11
- package/dist/cli.cjs +98 -14
- package/dist/cli.mjs +98 -14
- package/dist/index.cjs +200 -29
- package/dist/index.d.cts +10 -4
- package/dist/index.d.mts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.mjs +200 -29
- package/dist/templates/react-tsx/index.html.ejs +1 -1
- package/dist/templates/react-tsx/package.json.ejs +1 -1
- package/dist/templates/vanilla-ts/package.json.ejs +1 -1
- package/package.json +8 -6
- package/src/index.ts +241 -30
- package/dist/templates/react-tsx/pnpm-lock.yaml.ejs +0 -6015
- package/dist/templates/react-tsx/pnpm-workspace.yaml.ejs +0 -3
- package/dist/templates/vanilla-ts/pnpm-lock.yaml.ejs +0 -5300
- package/dist/templates/vanilla-ts/pnpm-workspace.yaml.ejs +0 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
2
|
+
type TemplateType = 'vanilla-ts' | 'react-tsx';
|
|
3
|
+
type ClaspOption = 'create' | 'list' | 'input' | 'skip';
|
|
1
4
|
interface ProjectOptions {
|
|
2
5
|
projectName: string;
|
|
3
|
-
packageManager:
|
|
4
|
-
templateType:
|
|
6
|
+
packageManager: PackageManager;
|
|
7
|
+
templateType: TemplateType;
|
|
8
|
+
clasp: ClaspOption;
|
|
9
|
+
claspProjectId?: string | undefined;
|
|
10
|
+
install: boolean;
|
|
5
11
|
}
|
|
6
12
|
|
|
7
|
-
declare function runCommand(command: string, args: string[], cwd: string): Promise<
|
|
8
|
-
declare function generateProject({ projectName, packageManager, templateType, }: ProjectOptions): Promise<void>;
|
|
13
|
+
declare function runCommand(command: string, args: string[], cwd: string, capture?: boolean): Promise<string>;
|
|
14
|
+
declare function generateProject({ projectName, packageManager, templateType, clasp, claspProjectId, install, }: ProjectOptions): Promise<void>;
|
|
9
15
|
|
|
10
16
|
export { generateProject, runCommand };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
2
|
+
type TemplateType = 'vanilla-ts' | 'react-tsx';
|
|
3
|
+
type ClaspOption = 'create' | 'list' | 'input' | 'skip';
|
|
1
4
|
interface ProjectOptions {
|
|
2
5
|
projectName: string;
|
|
3
|
-
packageManager:
|
|
4
|
-
templateType:
|
|
6
|
+
packageManager: PackageManager;
|
|
7
|
+
templateType: TemplateType;
|
|
8
|
+
clasp: ClaspOption;
|
|
9
|
+
claspProjectId?: string | undefined;
|
|
10
|
+
install: boolean;
|
|
5
11
|
}
|
|
6
12
|
|
|
7
|
-
declare function runCommand(command: string, args: string[], cwd: string): Promise<
|
|
8
|
-
declare function generateProject({ projectName, packageManager, templateType, }: ProjectOptions): Promise<void>;
|
|
13
|
+
declare function runCommand(command: string, args: string[], cwd: string, capture?: boolean): Promise<string>;
|
|
14
|
+
declare function generateProject({ projectName, packageManager, templateType, clasp, claspProjectId, install, }: ProjectOptions): Promise<void>;
|
|
9
15
|
|
|
10
16
|
export { generateProject, runCommand };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
2
|
+
type TemplateType = 'vanilla-ts' | 'react-tsx';
|
|
3
|
+
type ClaspOption = 'create' | 'list' | 'input' | 'skip';
|
|
1
4
|
interface ProjectOptions {
|
|
2
5
|
projectName: string;
|
|
3
|
-
packageManager:
|
|
4
|
-
templateType:
|
|
6
|
+
packageManager: PackageManager;
|
|
7
|
+
templateType: TemplateType;
|
|
8
|
+
clasp: ClaspOption;
|
|
9
|
+
claspProjectId?: string | undefined;
|
|
10
|
+
install: boolean;
|
|
5
11
|
}
|
|
6
12
|
|
|
7
|
-
declare function runCommand(command: string, args: string[], cwd: string): Promise<
|
|
8
|
-
declare function generateProject({ projectName, packageManager, templateType, }: ProjectOptions): Promise<void>;
|
|
13
|
+
declare function runCommand(command: string, args: string[], cwd: string, capture?: boolean): Promise<string>;
|
|
14
|
+
declare function generateProject({ projectName, packageManager, templateType, clasp, claspProjectId, install, }: ProjectOptions): Promise<void>;
|
|
9
15
|
|
|
10
16
|
export { generateProject, runCommand };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { select } from '@inquirer/prompts';
|
|
4
5
|
import { consola } from 'consola';
|
|
5
6
|
import ejs from 'ejs';
|
|
6
7
|
import { glob } from 'glob';
|
|
@@ -14,18 +15,30 @@ import __cjs_mod__ from 'module';
|
|
|
14
15
|
const __filename = __cjs_url__.fileURLToPath(import.meta.url);
|
|
15
16
|
const __dirname = __cjs_path__.dirname(__filename);
|
|
16
17
|
const require = __cjs_mod__.createRequire(import.meta.url);
|
|
17
|
-
async function runCommand(command, args, cwd) {
|
|
18
|
+
async function runCommand(command, args, cwd, capture = false) {
|
|
18
19
|
return new Promise((resolve, reject) => {
|
|
19
20
|
const child = spawn(command, args, {
|
|
20
21
|
cwd,
|
|
21
|
-
stdio: "inherit",
|
|
22
|
+
stdio: capture ? "pipe" : "inherit",
|
|
22
23
|
shell: true
|
|
23
24
|
});
|
|
25
|
+
let stdout = "";
|
|
26
|
+
let stderr = "";
|
|
27
|
+
if (capture) {
|
|
28
|
+
child.stdout?.on("data", (data) => {
|
|
29
|
+
stdout += data.toString();
|
|
30
|
+
});
|
|
31
|
+
child.stderr?.on("data", (data) => {
|
|
32
|
+
stderr += data.toString();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
24
35
|
child.on("close", (code) => {
|
|
25
36
|
if (code === 0) {
|
|
26
|
-
resolve();
|
|
37
|
+
resolve(stdout.trim());
|
|
27
38
|
} else {
|
|
28
|
-
|
|
39
|
+
const errorMsg = `Command failed with exit code ${code}${stderr ? `:
|
|
40
|
+
${stderr}` : ""}`;
|
|
41
|
+
reject(new Error(errorMsg));
|
|
29
42
|
}
|
|
30
43
|
});
|
|
31
44
|
child.on("error", (err) => {
|
|
@@ -33,10 +46,132 @@ async function runCommand(command, args, cwd) {
|
|
|
33
46
|
});
|
|
34
47
|
});
|
|
35
48
|
}
|
|
49
|
+
async function handleClaspSetup(claspOption, projectName, outputDir, claspProjectId, packageManager = "npm") {
|
|
50
|
+
if (claspOption === "skip") {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const npxLikeCommand = packageManager === "npm" ? "npx" : packageManager === "pnpm" ? "pnpx" : "yarn";
|
|
54
|
+
consola.start(
|
|
55
|
+
`Setting up .clasp.json with \`${npxLikeCommand} @google/clasp\`...`
|
|
56
|
+
);
|
|
57
|
+
if (claspOption === "create" || claspOption === "list") {
|
|
58
|
+
try {
|
|
59
|
+
await runCommand(npxLikeCommand, ["@google/clasp", "status"], outputDir);
|
|
60
|
+
} catch {
|
|
61
|
+
consola.error(
|
|
62
|
+
`It seems you are not logged in to clasp. Please run \`${npxLikeCommand} @google/clasp login\` and try again.`
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let scriptId;
|
|
68
|
+
let existingClaspJson = null;
|
|
69
|
+
switch (claspOption) {
|
|
70
|
+
case "create":
|
|
71
|
+
try {
|
|
72
|
+
const claspJsonPath = path.join(outputDir, ".clasp.json");
|
|
73
|
+
try {
|
|
74
|
+
const existingContent = await fs.readFile(claspJsonPath, "utf-8");
|
|
75
|
+
existingClaspJson = JSON.parse(existingContent);
|
|
76
|
+
await fs.unlink(claspJsonPath);
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
const result = await runCommand(
|
|
80
|
+
npxLikeCommand,
|
|
81
|
+
[
|
|
82
|
+
"@google/clasp",
|
|
83
|
+
"create",
|
|
84
|
+
"--title",
|
|
85
|
+
`"${projectName}"`,
|
|
86
|
+
"--type",
|
|
87
|
+
"standalone"
|
|
88
|
+
],
|
|
89
|
+
outputDir,
|
|
90
|
+
true
|
|
91
|
+
);
|
|
92
|
+
const match = result.match(/Created new script: .+\/d\/(.+)\/edit/);
|
|
93
|
+
if (match?.[1]) {
|
|
94
|
+
scriptId = match[1];
|
|
95
|
+
consola.info(`Created new Apps Script project with ID: ${scriptId}`);
|
|
96
|
+
} else {
|
|
97
|
+
throw new Error("Could not parse scriptId from clasp output.");
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
consola.error("Failed to create new Apps Script project.", e);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case "list":
|
|
105
|
+
try {
|
|
106
|
+
const listOutput = await runCommand(
|
|
107
|
+
npxLikeCommand,
|
|
108
|
+
["@google/clasp", "list"],
|
|
109
|
+
outputDir,
|
|
110
|
+
true
|
|
111
|
+
);
|
|
112
|
+
const projects = listOutput.split("\n").slice(1).map((line) => {
|
|
113
|
+
const parts = line.split(" - ");
|
|
114
|
+
if (parts.length >= 2) {
|
|
115
|
+
const name = parts[0]?.trim();
|
|
116
|
+
const url = parts[1]?.trim();
|
|
117
|
+
if (!name || !url) return null;
|
|
118
|
+
const match = url.match(/\/d\/(.+)\/edit/);
|
|
119
|
+
const scriptId2 = match?.[1];
|
|
120
|
+
if (scriptId2) {
|
|
121
|
+
return { name, value: scriptId2 };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}).filter((p) => p !== null);
|
|
126
|
+
if (projects.length === 0) {
|
|
127
|
+
consola.warn(
|
|
128
|
+
"No existing Apps Script projects found. Please create one first."
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
scriptId = await select({
|
|
133
|
+
message: "Choose an existing Apps Script project:",
|
|
134
|
+
choices: projects
|
|
135
|
+
});
|
|
136
|
+
} catch (e) {
|
|
137
|
+
consola.error("Failed to list Apps Script projects.", e);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
case "input":
|
|
142
|
+
scriptId = claspProjectId;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if (scriptId) {
|
|
146
|
+
const claspJsonPath = path.join(outputDir, ".clasp.json");
|
|
147
|
+
let claspJson = {
|
|
148
|
+
scriptId
|
|
149
|
+
};
|
|
150
|
+
let successMessage = `.clasp.json created successfully with scriptId: ${scriptId}`;
|
|
151
|
+
if (existingClaspJson) {
|
|
152
|
+
claspJson = { ...existingClaspJson, scriptId };
|
|
153
|
+
successMessage = `.clasp.json updated successfully with scriptId: ${scriptId}`;
|
|
154
|
+
} else {
|
|
155
|
+
try {
|
|
156
|
+
const existingContent = await fs.readFile(claspJsonPath, "utf-8");
|
|
157
|
+
const currentClaspJson = JSON.parse(existingContent);
|
|
158
|
+
claspJson = { ...currentClaspJson, scriptId };
|
|
159
|
+
successMessage = `.clasp.json updated successfully with scriptId: ${scriptId}`;
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const claspJsonContent = JSON.stringify(claspJson, null, 2);
|
|
164
|
+
await fs.writeFile(claspJsonPath, claspJsonContent, { encoding: "utf-8" });
|
|
165
|
+
consola.success(successMessage);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
36
168
|
async function generateProject({
|
|
37
169
|
projectName,
|
|
38
170
|
packageManager,
|
|
39
|
-
templateType
|
|
171
|
+
templateType,
|
|
172
|
+
clasp,
|
|
173
|
+
claspProjectId,
|
|
174
|
+
install
|
|
40
175
|
}) {
|
|
41
176
|
const outputDir = path.resolve(process.cwd(), projectName);
|
|
42
177
|
const templateBaseDir = path.resolve(__dirname, "..", "dist", "templates");
|
|
@@ -45,11 +180,28 @@ async function generateProject({
|
|
|
45
180
|
consola.start(
|
|
46
181
|
`Creating a new Project for GoogleAppsScript in ${outputDir}...`
|
|
47
182
|
);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
183
|
+
if (projectName === ".") {
|
|
184
|
+
try {
|
|
185
|
+
const files = await fs.readdir(outputDir);
|
|
186
|
+
const relevantFiles = files.filter((file) => !file.startsWith("."));
|
|
187
|
+
if (relevantFiles.length > 0) {
|
|
188
|
+
const proceed = await confirm(
|
|
189
|
+
"Current directory is not empty. Proceed anyway?"
|
|
190
|
+
);
|
|
191
|
+
if (!proceed) {
|
|
192
|
+
consola.warn("Operation cancelled.");
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
try {
|
|
200
|
+
await fs.access(outputDir);
|
|
201
|
+
consola.error(`Directory ${projectName} already exists.`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
53
205
|
}
|
|
54
206
|
await fs.mkdir(outputDir, { recursive: true });
|
|
55
207
|
consola.info(`Generating project files from template '${templateType}'...`);
|
|
@@ -59,30 +211,36 @@ async function generateProject({
|
|
|
59
211
|
const files = await glob("./**/*", {
|
|
60
212
|
cwd: dir,
|
|
61
213
|
nodir: true,
|
|
62
|
-
dot: true
|
|
63
|
-
dotRelative: true,
|
|
64
|
-
follow: true,
|
|
65
|
-
windowsPathsNoEscape: true
|
|
214
|
+
dot: true
|
|
66
215
|
});
|
|
67
216
|
for (const file of files) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
217
|
+
const relativePath = path.relative(dir, path.resolve(dir, file));
|
|
218
|
+
const templatePath = path.join(dir, relativePath);
|
|
219
|
+
const outputPath = path.join(outputDir, relativePath.replace(".ejs", ""));
|
|
70
220
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
71
221
|
const templateContent = await fs.readFile(templatePath, {
|
|
72
222
|
encoding: "utf-8"
|
|
73
223
|
});
|
|
74
224
|
const renderedContent = ejs.render(templateContent, ejsData);
|
|
75
|
-
await fs.writeFile(outputPath, renderedContent);
|
|
225
|
+
await fs.writeFile(outputPath, renderedContent, { encoding: "utf-8" });
|
|
76
226
|
}
|
|
77
227
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
228
|
+
await handleClaspSetup(
|
|
229
|
+
clasp,
|
|
230
|
+
projectName,
|
|
231
|
+
outputDir,
|
|
232
|
+
claspProjectId,
|
|
233
|
+
packageManager
|
|
234
|
+
);
|
|
235
|
+
if (install) {
|
|
236
|
+
consola.start(`Installing dependencies with ${packageManager}...`);
|
|
237
|
+
try {
|
|
238
|
+
await runCommand(packageManager, ["install"], outputDir);
|
|
239
|
+
consola.success(`Dependencies installed successfully.`);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
consola.fail("Failed to install dependencies. Please do it manually.");
|
|
242
|
+
consola.error(e);
|
|
243
|
+
}
|
|
86
244
|
}
|
|
87
245
|
consola.start(`Initializing Git repository...`);
|
|
88
246
|
try {
|
|
@@ -94,15 +252,28 @@ async function generateProject({
|
|
|
94
252
|
outputDir
|
|
95
253
|
);
|
|
96
254
|
consola.success(`Git repository initialized successfully.`);
|
|
97
|
-
} catch {
|
|
255
|
+
} catch (e) {
|
|
98
256
|
consola.fail("Failed to initialize Git repository. Please do it manually.");
|
|
257
|
+
consola.error(e);
|
|
99
258
|
}
|
|
100
259
|
consola.success(`Project '${projectName}' created successfully!`);
|
|
101
|
-
|
|
260
|
+
const messages = [];
|
|
261
|
+
projectName !== "." && messages.push(` cd ${projectName}`);
|
|
262
|
+
!install && messages.push(` ${packageManager} install`);
|
|
263
|
+
templateType !== "vanilla-ts" && messages.push(` ${packageManager} dev`);
|
|
264
|
+
if (messages.length > 0) {
|
|
265
|
+
consola.log(`
|
|
102
266
|
To get started, run:
|
|
103
267
|
`);
|
|
104
|
-
|
|
105
|
-
|
|
268
|
+
for (const message of messages) {
|
|
269
|
+
consola.log(message);
|
|
270
|
+
}
|
|
271
|
+
consola.log("");
|
|
272
|
+
consola.log(`...and write your GAS code!`);
|
|
273
|
+
} else {
|
|
274
|
+
consola.log(`
|
|
275
|
+
To get started, write your GAS code in \`src/\`!`);
|
|
276
|
+
}
|
|
106
277
|
}
|
|
107
278
|
|
|
108
279
|
export { generateProject, runCommand };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title
|
|
7
|
+
<title><%= projectName %></title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciderjs/gasbombe",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A TypeScript Project Generator for GoogleAppsScript, available as CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"module": "dist/index.mjs",
|
|
16
16
|
"types": "dist/index.d.ts",
|
|
17
17
|
"scripts": {
|
|
18
|
+
"dev": "jiti src/cli",
|
|
18
19
|
"tsc": "tsgo --noEmit src",
|
|
19
20
|
"check": "biome check --write",
|
|
20
21
|
"generate": "jiti scripts/copyTemplates",
|
|
21
|
-
"
|
|
22
|
-
"build": "unbuild",
|
|
23
|
-
"postbuild": "pnpm run generate",
|
|
22
|
+
"test": "vitest run --coverage",
|
|
23
|
+
"build": "pnpm run tsc && pnpm run test --silent && unbuild && pnpm run generate",
|
|
24
24
|
"postinstall": "pnpm run generate"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typescript",
|
|
30
30
|
"react"
|
|
31
31
|
],
|
|
32
|
-
"author": "luthpg",
|
|
32
|
+
"author": "ciderjs/luthpg",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
@@ -51,8 +51,10 @@
|
|
|
51
51
|
"@types/ejs": "^3.1.5",
|
|
52
52
|
"@types/node": "^24.5.2",
|
|
53
53
|
"@typescript/native-preview": "7.0.0-dev.20250925.1",
|
|
54
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
54
55
|
"jiti": "^2.6.0",
|
|
55
56
|
"typescript": "^5.9.2",
|
|
56
|
-
"unbuild": "^3.6.1"
|
|
57
|
+
"unbuild": "^3.6.1",
|
|
58
|
+
"vitest": "^3.2.4"
|
|
57
59
|
}
|
|
58
60
|
}
|