@canonical/summon-package 0.1.0
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 +496 -0
- package/biome.json +6 -0
- package/package.json +42 -0
- package/src/__tests__/generators.test.ts +295 -0
- package/src/index.ts +14 -0
- package/src/package/index.ts +306 -0
- package/src/shared/index.ts +330 -0
- package/src/templates/README.md.ejs +29 -0
- package/src/templates/biome.json.ejs +6 -0
- package/src/templates/cli.ts.ejs +8 -0
- package/src/templates/index.css.ejs +5 -0
- package/src/templates/index.ts.ejs +7 -0
- package/src/templates/package.json.ejs +112 -0
- package/src/templates/storybook-main.ts.ejs +5 -0
- package/src/templates/storybook-preview.ts.ejs +26 -0
- package/src/templates/tsconfig-react.json.ejs +8 -0
- package/src/templates/tsconfig.json.ejs +8 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for summon-package generator
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { dryRun } from "@canonical/summon";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { generator } from "../package/index.js";
|
|
8
|
+
import {
|
|
9
|
+
createTemplateContext,
|
|
10
|
+
getEntryPoints,
|
|
11
|
+
getLicense,
|
|
12
|
+
getRuleset,
|
|
13
|
+
type MonorepoInfo,
|
|
14
|
+
type PackageAnswers,
|
|
15
|
+
validatePackageName,
|
|
16
|
+
} from "../shared/index.js";
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Shared Utilities Tests
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
describe("validatePackageName", () => {
|
|
23
|
+
it("accepts valid package names", () => {
|
|
24
|
+
expect(validatePackageName("my-package")).toBe(true);
|
|
25
|
+
expect(validatePackageName("package")).toBe(true);
|
|
26
|
+
expect(validatePackageName("my-cool-package")).toBe(true);
|
|
27
|
+
expect(validatePackageName("pkg123")).toBe(true);
|
|
28
|
+
expect(validatePackageName("a")).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("rejects invalid package names", () => {
|
|
32
|
+
expect(validatePackageName("")).not.toBe(true);
|
|
33
|
+
expect(validatePackageName("-package")).not.toBe(true);
|
|
34
|
+
expect(validatePackageName("package-")).not.toBe(true);
|
|
35
|
+
expect(validatePackageName("My-Package")).not.toBe(true);
|
|
36
|
+
expect(validatePackageName("my_package")).not.toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("strips @canonical/ prefix for validation", () => {
|
|
40
|
+
expect(validatePackageName("@canonical/my-package")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("getLicense", () => {
|
|
45
|
+
it("returns GPL-3.0 for tool-ts", () => {
|
|
46
|
+
expect(getLicense("tool-ts")).toBe("GPL-3.0");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("returns LGPL-3.0 for library", () => {
|
|
50
|
+
expect(getLicense("library")).toBe("LGPL-3.0");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns LGPL-3.0 for css", () => {
|
|
54
|
+
expect(getLicense("css")).toBe("LGPL-3.0");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("getEntryPoints", () => {
|
|
59
|
+
it("returns src/ paths for tool-ts", () => {
|
|
60
|
+
const entry = getEntryPoints("tool-ts");
|
|
61
|
+
expect(entry.module).toBe("src/index.ts");
|
|
62
|
+
expect(entry.types).toBe("src/index.ts");
|
|
63
|
+
expect(entry.files).toContain("src");
|
|
64
|
+
expect(entry.needsBuild).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("returns dist/ paths for library", () => {
|
|
68
|
+
const entry = getEntryPoints("library");
|
|
69
|
+
expect(entry.module).toBe("dist/esm/index.js");
|
|
70
|
+
expect(entry.types).toBe("dist/types/index.d.ts");
|
|
71
|
+
expect(entry.files).toContain("dist");
|
|
72
|
+
expect(entry.needsBuild).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("returns src/index.css for css packages", () => {
|
|
76
|
+
const entry = getEntryPoints("css");
|
|
77
|
+
expect(entry.module).toBe("src/index.css");
|
|
78
|
+
expect(entry.types).toBeNull();
|
|
79
|
+
expect(entry.files).toContain("src");
|
|
80
|
+
expect(entry.needsBuild).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("getRuleset", () => {
|
|
85
|
+
it("returns package-react when withReact is true", () => {
|
|
86
|
+
expect(getRuleset("tool-ts", true)).toBe("package-react");
|
|
87
|
+
expect(getRuleset("library", true)).toBe("package-react");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("returns package type when withReact is false", () => {
|
|
91
|
+
expect(getRuleset("tool-ts", false)).toBe("tool-ts");
|
|
92
|
+
expect(getRuleset("library", false)).toBe("library");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("returns base for css packages", () => {
|
|
96
|
+
expect(getRuleset("css", false)).toBe("base");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("createTemplateContext", () => {
|
|
101
|
+
const baseAnswers: PackageAnswers = {
|
|
102
|
+
name: "@canonical/test-pkg",
|
|
103
|
+
type: "tool-ts",
|
|
104
|
+
description: "Test package",
|
|
105
|
+
withReact: false,
|
|
106
|
+
withStorybook: false,
|
|
107
|
+
withCli: false,
|
|
108
|
+
runInstall: false,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
it("creates context with monorepo version", () => {
|
|
112
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: true, version: "1.2.3" };
|
|
113
|
+
const ctx = createTemplateContext(baseAnswers, monorepoInfo);
|
|
114
|
+
|
|
115
|
+
expect(ctx.name).toBe("@canonical/test-pkg");
|
|
116
|
+
expect(ctx.shortName).toBe("test-pkg");
|
|
117
|
+
expect(ctx.version).toBe("1.2.3");
|
|
118
|
+
expect(ctx.license).toBe("GPL-3.0");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("creates context with default version when not in monorepo", () => {
|
|
122
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: false };
|
|
123
|
+
const ctx = createTemplateContext(baseAnswers, monorepoInfo);
|
|
124
|
+
|
|
125
|
+
expect(ctx.version).toBe("0.1.0");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("handles unscoped package names", () => {
|
|
129
|
+
const unscopedAnswers: PackageAnswers = {
|
|
130
|
+
...baseAnswers,
|
|
131
|
+
name: "my-package",
|
|
132
|
+
};
|
|
133
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: false };
|
|
134
|
+
const ctx = createTemplateContext(unscopedAnswers, monorepoInfo);
|
|
135
|
+
|
|
136
|
+
expect(ctx.name).toBe("my-package");
|
|
137
|
+
expect(ctx.shortName).toBe("my-package");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("sets correct entry points for tool-ts", () => {
|
|
141
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: false };
|
|
142
|
+
const ctx = createTemplateContext(baseAnswers, monorepoInfo);
|
|
143
|
+
|
|
144
|
+
expect(ctx.module).toBe("src/index.ts");
|
|
145
|
+
expect(ctx.types).toBe("src/index.ts");
|
|
146
|
+
expect(ctx.needsBuild).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("sets correct entry points for library", () => {
|
|
150
|
+
const answers: PackageAnswers = { ...baseAnswers, type: "library" };
|
|
151
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: false };
|
|
152
|
+
const ctx = createTemplateContext(answers, monorepoInfo);
|
|
153
|
+
|
|
154
|
+
expect(ctx.module).toBe("dist/esm/index.js");
|
|
155
|
+
expect(ctx.types).toBe("dist/types/index.d.ts");
|
|
156
|
+
expect(ctx.license).toBe("LGPL-3.0");
|
|
157
|
+
expect(ctx.needsBuild).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("sets correct entry points for css", () => {
|
|
161
|
+
const answers: PackageAnswers = { ...baseAnswers, type: "css" };
|
|
162
|
+
const monorepoInfo: MonorepoInfo = { isMonorepo: false };
|
|
163
|
+
const ctx = createTemplateContext(answers, monorepoInfo);
|
|
164
|
+
|
|
165
|
+
expect(ctx.module).toBe("src/index.css");
|
|
166
|
+
expect(ctx.types).toBeNull();
|
|
167
|
+
expect(ctx.license).toBe("LGPL-3.0");
|
|
168
|
+
expect(ctx.needsBuild).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// Generator Dry-Run Tests
|
|
174
|
+
// =============================================================================
|
|
175
|
+
|
|
176
|
+
describe("package generator", () => {
|
|
177
|
+
it("has correct meta information", () => {
|
|
178
|
+
expect(generator.meta.name).toBe("package");
|
|
179
|
+
expect(generator.meta.version).toBe("0.1.0");
|
|
180
|
+
expect(generator.meta.description).toBeDefined();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("defines required prompts", () => {
|
|
184
|
+
const promptNames = generator.prompts.map((p) => p.name);
|
|
185
|
+
|
|
186
|
+
expect(promptNames).toContain("name");
|
|
187
|
+
expect(promptNames).toContain("type");
|
|
188
|
+
expect(promptNames).toContain("description");
|
|
189
|
+
expect(promptNames).toContain("withReact");
|
|
190
|
+
expect(promptNames).toContain("withCli");
|
|
191
|
+
expect(promptNames).toContain("runInstall");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("generates expected files for tool-ts package", () => {
|
|
195
|
+
const answers: PackageAnswers = {
|
|
196
|
+
name: "@canonical/my-tool",
|
|
197
|
+
type: "tool-ts",
|
|
198
|
+
description: "My tool",
|
|
199
|
+
withReact: false,
|
|
200
|
+
withStorybook: false,
|
|
201
|
+
withCli: false,
|
|
202
|
+
runInstall: false,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const task = generator.generate(answers);
|
|
206
|
+
const result = dryRun(task);
|
|
207
|
+
|
|
208
|
+
const writePaths = result.effects
|
|
209
|
+
.filter((e) => e._tag === "WriteFile")
|
|
210
|
+
.map((e) => (e as { path: string }).path);
|
|
211
|
+
|
|
212
|
+
// Check core files are created
|
|
213
|
+
expect(writePaths.some((p) => p.endsWith("package.json"))).toBe(true);
|
|
214
|
+
expect(writePaths.some((p) => p.endsWith("tsconfig.json"))).toBe(true);
|
|
215
|
+
expect(writePaths.some((p) => p.endsWith("biome.json"))).toBe(true);
|
|
216
|
+
expect(writePaths.some((p) => p.endsWith("index.ts"))).toBe(true);
|
|
217
|
+
expect(writePaths.some((p) => p.endsWith("README.md"))).toBe(true);
|
|
218
|
+
|
|
219
|
+
// Check CLI is NOT created when withCli is false
|
|
220
|
+
expect(writePaths.some((p) => p.endsWith("cli.ts"))).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("generates CLI file when withCli is true", () => {
|
|
224
|
+
const answers: PackageAnswers = {
|
|
225
|
+
name: "@canonical/my-cli",
|
|
226
|
+
type: "tool-ts",
|
|
227
|
+
description: "My CLI",
|
|
228
|
+
withReact: false,
|
|
229
|
+
withStorybook: false,
|
|
230
|
+
withCli: true,
|
|
231
|
+
runInstall: false,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const task = generator.generate(answers);
|
|
235
|
+
const result = dryRun(task);
|
|
236
|
+
|
|
237
|
+
const writePaths = result.effects
|
|
238
|
+
.filter((e) => e._tag === "WriteFile")
|
|
239
|
+
.map((e) => (e as { path: string }).path);
|
|
240
|
+
|
|
241
|
+
expect(writePaths.some((p) => p.endsWith("cli.ts"))).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("generates CSS package with index.css", () => {
|
|
245
|
+
const answers: PackageAnswers = {
|
|
246
|
+
name: "@canonical/my-styles",
|
|
247
|
+
type: "css",
|
|
248
|
+
description: "My styles",
|
|
249
|
+
withReact: false,
|
|
250
|
+
withStorybook: false,
|
|
251
|
+
withCli: false,
|
|
252
|
+
runInstall: false,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const task = generator.generate(answers);
|
|
256
|
+
const result = dryRun(task);
|
|
257
|
+
|
|
258
|
+
const writePaths = result.effects
|
|
259
|
+
.filter((e) => e._tag === "WriteFile")
|
|
260
|
+
.map((e) => (e as { path: string }).path);
|
|
261
|
+
|
|
262
|
+
// Check CSS-specific files
|
|
263
|
+
expect(writePaths.some((p) => p.endsWith("index.css"))).toBe(true);
|
|
264
|
+
expect(writePaths.some((p) => p.endsWith("package.json"))).toBe(true);
|
|
265
|
+
expect(writePaths.some((p) => p.endsWith("biome.json"))).toBe(true);
|
|
266
|
+
expect(writePaths.some((p) => p.endsWith("README.md"))).toBe(true);
|
|
267
|
+
|
|
268
|
+
// Check no TypeScript files for CSS package
|
|
269
|
+
expect(writePaths.some((p) => p.endsWith("index.ts"))).toBe(false);
|
|
270
|
+
expect(writePaths.some((p) => p.endsWith("tsconfig.json"))).toBe(false);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("creates directory structure using short name", () => {
|
|
274
|
+
const answers: PackageAnswers = {
|
|
275
|
+
name: "@canonical/my-pkg",
|
|
276
|
+
type: "tool-ts",
|
|
277
|
+
description: "",
|
|
278
|
+
withReact: false,
|
|
279
|
+
withStorybook: false,
|
|
280
|
+
withCli: false,
|
|
281
|
+
runInstall: false,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const task = generator.generate(answers);
|
|
285
|
+
const result = dryRun(task);
|
|
286
|
+
|
|
287
|
+
const mkdirPaths = result.effects
|
|
288
|
+
.filter((e) => e._tag === "MakeDir")
|
|
289
|
+
.map((e) => (e as { path: string }).path);
|
|
290
|
+
|
|
291
|
+
// Directory should be the short name (without scope)
|
|
292
|
+
expect(mkdirPaths.some((p) => p === "my-pkg")).toBe(true);
|
|
293
|
+
expect(mkdirPaths.some((p) => p.endsWith("src"))).toBe(true);
|
|
294
|
+
});
|
|
295
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @canonical/summon-package
|
|
3
|
+
*
|
|
4
|
+
* Package generator for Summon - scaffold new npm packages with proper configuration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { AnyGenerator } from "@canonical/summon";
|
|
8
|
+
import { generator as packageGenerator } from "./package/index.js";
|
|
9
|
+
|
|
10
|
+
export const generators: Record<string, AnyGenerator> = {
|
|
11
|
+
package: packageGenerator as unknown as AnyGenerator,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default generators;
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates a new npm package with proper configuration for the pragma monorepo.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import {
|
|
10
|
+
exec,
|
|
11
|
+
flatMap,
|
|
12
|
+
type GeneratorDefinition,
|
|
13
|
+
info,
|
|
14
|
+
mkdir,
|
|
15
|
+
type PromptDefinition,
|
|
16
|
+
sequence_,
|
|
17
|
+
template,
|
|
18
|
+
when,
|
|
19
|
+
} from "@canonical/summon";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
createTemplateContext,
|
|
23
|
+
detectMonorepo,
|
|
24
|
+
detectPackageManager,
|
|
25
|
+
getPackageShortName,
|
|
26
|
+
type PackageAnswers,
|
|
27
|
+
validatePackageName,
|
|
28
|
+
} from "../shared/index.js";
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Template Paths
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
const templatesDir = path.join(__dirname, "..", "templates");
|
|
36
|
+
|
|
37
|
+
const templates = {
|
|
38
|
+
packageJson: path.join(templatesDir, "package.json.ejs"),
|
|
39
|
+
tsconfig: path.join(templatesDir, "tsconfig.json.ejs"),
|
|
40
|
+
tsconfigReact: path.join(templatesDir, "tsconfig-react.json.ejs"),
|
|
41
|
+
biome: path.join(templatesDir, "biome.json.ejs"),
|
|
42
|
+
indexTs: path.join(templatesDir, "index.ts.ejs"),
|
|
43
|
+
indexCss: path.join(templatesDir, "index.css.ejs"),
|
|
44
|
+
cliTs: path.join(templatesDir, "cli.ts.ejs"),
|
|
45
|
+
readme: path.join(templatesDir, "README.md.ejs"),
|
|
46
|
+
storybookMain: path.join(templatesDir, "storybook-main.ts.ejs"),
|
|
47
|
+
storybookPreview: path.join(templatesDir, "storybook-preview.ts.ejs"),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// Prompts
|
|
52
|
+
// =============================================================================
|
|
53
|
+
|
|
54
|
+
const prompts: PromptDefinition[] = [
|
|
55
|
+
{
|
|
56
|
+
name: "name",
|
|
57
|
+
type: "text",
|
|
58
|
+
message: "Package name:",
|
|
59
|
+
default: "@canonical/my-package",
|
|
60
|
+
validate: validatePackageName,
|
|
61
|
+
group: "Package",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "type",
|
|
65
|
+
type: "select",
|
|
66
|
+
message: "Package type:",
|
|
67
|
+
choices: [
|
|
68
|
+
{
|
|
69
|
+
label: "tool-ts - TypeScript tool (runs from src/, no build)",
|
|
70
|
+
value: "tool-ts",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: "library - Publishable library (dist/ build output)",
|
|
74
|
+
value: "library",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: "css - CSS package (src/index.css, no build)",
|
|
78
|
+
value: "css",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
default: "tool-ts",
|
|
82
|
+
group: "Package",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "description",
|
|
86
|
+
type: "text",
|
|
87
|
+
message: "Package description:",
|
|
88
|
+
default: "",
|
|
89
|
+
group: "Package",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "withReact",
|
|
93
|
+
type: "confirm",
|
|
94
|
+
message: "Include React dependencies?",
|
|
95
|
+
default: false,
|
|
96
|
+
group: "Options",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "withStorybook",
|
|
100
|
+
type: "confirm",
|
|
101
|
+
message: "Include Storybook setup?",
|
|
102
|
+
default: false,
|
|
103
|
+
group: "Options",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "withCli",
|
|
107
|
+
type: "confirm",
|
|
108
|
+
message: "Include CLI binary entry point?",
|
|
109
|
+
default: false,
|
|
110
|
+
group: "Options",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "runInstall",
|
|
114
|
+
type: "confirm",
|
|
115
|
+
message: "Run package manager install after creation?",
|
|
116
|
+
default: true,
|
|
117
|
+
group: "Post-setup",
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Generator Definition
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
export const generator: GeneratorDefinition<PackageAnswers> = {
|
|
126
|
+
meta: {
|
|
127
|
+
name: "package",
|
|
128
|
+
description:
|
|
129
|
+
"Generate a new npm package with proper configuration for the pragma monorepo",
|
|
130
|
+
version: "0.1.0",
|
|
131
|
+
help: `Generate a new npm package with proper configuration.
|
|
132
|
+
|
|
133
|
+
PACKAGE TYPES:
|
|
134
|
+
tool-ts TypeScript tool that runs directly from src/ (no build step)
|
|
135
|
+
License: GPL-3.0, Entry: src/index.ts
|
|
136
|
+
Examples: summon, webarchitect
|
|
137
|
+
|
|
138
|
+
library Publishable library with dist/ build output
|
|
139
|
+
License: LGPL-3.0, Entry: dist/esm/index.js
|
|
140
|
+
Examples: utils, ds-types
|
|
141
|
+
|
|
142
|
+
css CSS-only package (no TypeScript, no build)
|
|
143
|
+
License: LGPL-3.0, Entry: src/index.css
|
|
144
|
+
Examples: styles/primitives, styles/modes
|
|
145
|
+
|
|
146
|
+
OPTIONS:
|
|
147
|
+
--with-react Add React dependencies and TypeScript React config
|
|
148
|
+
--with-storybook Add Storybook configuration
|
|
149
|
+
--with-cli Add CLI binary entry point (src/cli.ts)
|
|
150
|
+
|
|
151
|
+
The generator auto-detects:
|
|
152
|
+
- Monorepo: Uses lerna.json version when in pragma monorepo
|
|
153
|
+
- Package manager: Detects bun/yarn/pnpm (defaults to bun)`,
|
|
154
|
+
examples: [
|
|
155
|
+
"summon package --name=@canonical/my-tool --type=tool-ts",
|
|
156
|
+
"summon package --name=@canonical/my-lib --type=library --with-react",
|
|
157
|
+
"summon package --name=@canonical/my-cli --type=tool-ts --with-cli",
|
|
158
|
+
"summon package --name=my-styles --type=css",
|
|
159
|
+
"summon package --name=@canonical/my-pkg --type=library --no-run-install",
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
prompts,
|
|
164
|
+
|
|
165
|
+
generate: (answers) => {
|
|
166
|
+
const packageDir = getPackageShortName(answers.name);
|
|
167
|
+
const cwd = process.cwd();
|
|
168
|
+
const isCss = answers.type === "css";
|
|
169
|
+
const needsTs = !isCss;
|
|
170
|
+
|
|
171
|
+
return flatMap(detectMonorepo(cwd), (monorepoInfo) => {
|
|
172
|
+
const ctx = createTemplateContext(answers, monorepoInfo);
|
|
173
|
+
|
|
174
|
+
return sequence_([
|
|
175
|
+
info(`Creating package: ${answers.name}`),
|
|
176
|
+
info(`Type: ${answers.type}`),
|
|
177
|
+
when(
|
|
178
|
+
monorepoInfo.isMonorepo,
|
|
179
|
+
info(`Monorepo detected, using version: ${monorepoInfo.version}`),
|
|
180
|
+
),
|
|
181
|
+
|
|
182
|
+
// Create directory structure
|
|
183
|
+
mkdir(packageDir),
|
|
184
|
+
mkdir(path.join(packageDir, "src")),
|
|
185
|
+
|
|
186
|
+
// Create package.json
|
|
187
|
+
template({
|
|
188
|
+
source: templates.packageJson,
|
|
189
|
+
dest: path.join(packageDir, "package.json"),
|
|
190
|
+
vars: ctx,
|
|
191
|
+
}),
|
|
192
|
+
|
|
193
|
+
// Create tsconfig.json (only for non-CSS packages)
|
|
194
|
+
when(
|
|
195
|
+
needsTs && answers.withReact,
|
|
196
|
+
template({
|
|
197
|
+
source: templates.tsconfigReact,
|
|
198
|
+
dest: path.join(packageDir, "tsconfig.json"),
|
|
199
|
+
vars: ctx,
|
|
200
|
+
}),
|
|
201
|
+
),
|
|
202
|
+
when(
|
|
203
|
+
needsTs && !answers.withReact,
|
|
204
|
+
template({
|
|
205
|
+
source: templates.tsconfig,
|
|
206
|
+
dest: path.join(packageDir, "tsconfig.json"),
|
|
207
|
+
vars: ctx,
|
|
208
|
+
}),
|
|
209
|
+
),
|
|
210
|
+
|
|
211
|
+
// Create biome.json
|
|
212
|
+
template({
|
|
213
|
+
source: templates.biome,
|
|
214
|
+
dest: path.join(packageDir, "biome.json"),
|
|
215
|
+
vars: ctx,
|
|
216
|
+
}),
|
|
217
|
+
|
|
218
|
+
// Create src/index.ts (for TS packages)
|
|
219
|
+
when(
|
|
220
|
+
needsTs,
|
|
221
|
+
template({
|
|
222
|
+
source: templates.indexTs,
|
|
223
|
+
dest: path.join(packageDir, "src", "index.ts"),
|
|
224
|
+
vars: ctx,
|
|
225
|
+
}),
|
|
226
|
+
),
|
|
227
|
+
|
|
228
|
+
// Create src/index.css (for CSS packages)
|
|
229
|
+
when(
|
|
230
|
+
isCss,
|
|
231
|
+
template({
|
|
232
|
+
source: templates.indexCss,
|
|
233
|
+
dest: path.join(packageDir, "src", "index.css"),
|
|
234
|
+
vars: ctx,
|
|
235
|
+
}),
|
|
236
|
+
),
|
|
237
|
+
|
|
238
|
+
// Create src/cli.ts (conditional, only for TS packages)
|
|
239
|
+
when(
|
|
240
|
+
needsTs && answers.withCli,
|
|
241
|
+
template({
|
|
242
|
+
source: templates.cliTs,
|
|
243
|
+
dest: path.join(packageDir, "src", "cli.ts"),
|
|
244
|
+
vars: ctx,
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
|
|
248
|
+
// Create README.md
|
|
249
|
+
template({
|
|
250
|
+
source: templates.readme,
|
|
251
|
+
dest: path.join(packageDir, "README.md"),
|
|
252
|
+
vars: ctx,
|
|
253
|
+
}),
|
|
254
|
+
|
|
255
|
+
// Create .storybook folder (conditional)
|
|
256
|
+
when(answers.withStorybook, mkdir(path.join(packageDir, ".storybook"))),
|
|
257
|
+
when(
|
|
258
|
+
answers.withStorybook,
|
|
259
|
+
mkdir(path.join(packageDir, "src", "assets")),
|
|
260
|
+
),
|
|
261
|
+
when(answers.withStorybook, mkdir(path.join(packageDir, "public"))),
|
|
262
|
+
when(
|
|
263
|
+
answers.withStorybook,
|
|
264
|
+
template({
|
|
265
|
+
source: templates.storybookMain,
|
|
266
|
+
dest: path.join(packageDir, ".storybook", "main.ts"),
|
|
267
|
+
vars: ctx,
|
|
268
|
+
}),
|
|
269
|
+
),
|
|
270
|
+
when(
|
|
271
|
+
answers.withStorybook,
|
|
272
|
+
template({
|
|
273
|
+
source: templates.storybookPreview,
|
|
274
|
+
dest: path.join(packageDir, ".storybook", "preview.ts"),
|
|
275
|
+
vars: ctx,
|
|
276
|
+
}),
|
|
277
|
+
),
|
|
278
|
+
|
|
279
|
+
info(`Package created at ./${packageDir}`),
|
|
280
|
+
|
|
281
|
+
// Run install (conditional)
|
|
282
|
+
when(
|
|
283
|
+
answers.runInstall,
|
|
284
|
+
flatMap(detectPackageManager(cwd), (pm) => {
|
|
285
|
+
return sequence_([
|
|
286
|
+
info(`Running ${pm} install...`),
|
|
287
|
+
flatMap(exec(pm, ["install"], packageDir), () =>
|
|
288
|
+
info(`Dependencies installed successfully`),
|
|
289
|
+
),
|
|
290
|
+
]);
|
|
291
|
+
}),
|
|
292
|
+
),
|
|
293
|
+
|
|
294
|
+
when(!answers.runInstall, info("Skipping install step")),
|
|
295
|
+
|
|
296
|
+
info(""),
|
|
297
|
+
info("Next steps:"),
|
|
298
|
+
info(` cd ${packageDir}`),
|
|
299
|
+
info(" bun run check"),
|
|
300
|
+
info(""),
|
|
301
|
+
]);
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export default generator;
|