@stack-dev/cli 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.
Files changed (82) hide show
  1. package/.turbo/daemon/6fa76abe2aa470d0-turbo.log.2025-08-02 +5 -0
  2. package/.turbo/daemon/6fa76abe2aa470d0-turbo.log.2025-12-29 +1 -0
  3. package/.turbo/daemon/6fa76abe2aa470d0-turbo.log.2025-12-30 +0 -0
  4. package/.turbo/turbo-build.log +21 -0
  5. package/.turbo/turbo-check-types.log +5 -0
  6. package/dist/index.d.mts +2 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2097 -0
  9. package/dist/index.mjs +2073 -0
  10. package/eslint.config.mjs +3 -0
  11. package/package.json +35 -0
  12. package/prettier.config.mjs +3 -0
  13. package/src/file-generator/file-generator-imp.ts +20 -0
  14. package/src/file-generator/file-generator.ts +5 -0
  15. package/src/file-generator/index.ts +3 -0
  16. package/src/file-generator/package-json-generator.ts +23 -0
  17. package/src/index.ts +185 -0
  18. package/src/link-packages.ts +65 -0
  19. package/src/package-json/dependency.ts +28 -0
  20. package/src/package-json/index.ts +3 -0
  21. package/src/package-json/package-json.ts +269 -0
  22. package/src/packages/create-config-package.ts +29 -0
  23. package/src/packages/index.ts +4 -0
  24. package/src/packages/library-package/create-library-package.ts +85 -0
  25. package/src/packages/library-package/files/add-file-generator.ts +8 -0
  26. package/src/packages/library-package/files/add-spec-file-generator.ts +17 -0
  27. package/src/packages/library-package/files/eslint-config-file-generator.ts +11 -0
  28. package/src/packages/library-package/files/index-file-generator.ts +9 -0
  29. package/src/packages/library-package/files/prettier-config-file-generator.ts +11 -0
  30. package/src/packages/library-package/files/tsconfig-file-generator.ts +15 -0
  31. package/src/packages/library-package/files/tsup-config-file-generator.ts +23 -0
  32. package/src/packages/library-package/files/vitest-config-file-generator.ts +19 -0
  33. package/src/packages/library-package/index.ts +1 -0
  34. package/src/packages/react-package/create-react-package.ts +25 -0
  35. package/src/packages/react-package/create-tailwind-react-package.ts +30 -0
  36. package/src/packages/react-package/create-unstyled-react-package.ts +3 -0
  37. package/src/packages/react-package/css-react-package/create-css-react-package.ts +103 -0
  38. package/src/packages/react-package/css-react-package/files/button-css-module-file-generator.ts +16 -0
  39. package/src/packages/react-package/css-react-package/files/button-file-generator.ts +14 -0
  40. package/src/packages/react-package/css-react-package/files/button-spec-file-generator.ts +33 -0
  41. package/src/packages/react-package/css-react-package/files/eslint-config-file-generator.ts +18 -0
  42. package/src/packages/react-package/css-react-package/files/index-file-generator.ts +9 -0
  43. package/src/packages/react-package/css-react-package/files/prettier-config-file-generator.ts +11 -0
  44. package/src/packages/react-package/css-react-package/files/tsconfig-file-generator.ts +15 -0
  45. package/src/packages/react-package/css-react-package/files/tsup-config-file-generator.ts +24 -0
  46. package/src/packages/react-package/css-react-package/files/vitest-config-file-generator.ts +23 -0
  47. package/src/packages/react-package/index.ts +1 -0
  48. package/src/packages/react-package/styled-components-react-package/create-styled-components-react-package.ts +112 -0
  49. package/src/packages/react-package/styled-components-react-package/files/button-file-generator.ts +30 -0
  50. package/src/packages/react-package/styled-components-react-package/files/button-spec-file-generator.ts +33 -0
  51. package/src/packages/react-package/styled-components-react-package/files/eslint-config-file-generator.ts +18 -0
  52. package/src/packages/react-package/styled-components-react-package/files/index-file-generator.ts +9 -0
  53. package/src/packages/react-package/styled-components-react-package/files/prettier-config-file-generator.ts +11 -0
  54. package/src/packages/react-package/styled-components-react-package/files/tsconfig-file-generator.ts +15 -0
  55. package/src/packages/react-package/styled-components-react-package/files/tsup-config-file-generator.ts +21 -0
  56. package/src/packages/react-package/styled-components-react-package/files/vitest-config-file-generator.ts +23 -0
  57. package/src/packages/vite-react-app/create-vite-react-app.ts +79 -0
  58. package/src/packages/vite-react-app/files/app-file-generator.ts +28 -0
  59. package/src/packages/vite-react-app/files/eslint-config-file-generator.ts +11 -0
  60. package/src/packages/vite-react-app/files/index-html-file-generator.ts +20 -0
  61. package/src/packages/vite-react-app/files/main-file-generator.ts +14 -0
  62. package/src/packages/vite-react-app/files/prettier-config-file-generator.ts +11 -0
  63. package/src/packages/vite-react-app/files/tsconfig-file-generator.ts +15 -0
  64. package/src/packages/vite-react-app/files/vite-config-file-generator.ts +17 -0
  65. package/src/packages/vite-react-app/files/vitest-config-file-generator.ts +19 -0
  66. package/src/tsconfig/compiler-options.ts +83 -0
  67. package/src/tsconfig/index.ts +4 -0
  68. package/src/tsconfig/reference.ts +21 -0
  69. package/src/tsconfig/tsconfig.ts +137 -0
  70. package/src/unlink-packages.ts +47 -0
  71. package/src/utils/package-generator.ts +41 -0
  72. package/src/utils/package-type.ts +44 -0
  73. package/src/utils/package.ts +126 -0
  74. package/src/utils/style-type.ts +41 -0
  75. package/src/utils/utils.ts +28 -0
  76. package/src/utils/workspace.ts +78 -0
  77. package/src/workspace/create-workspace.ts +39 -0
  78. package/src/workspace/index.ts +1 -0
  79. package/src/workspace/root-package.ts +195 -0
  80. package/src/workspace/typescript-config.ts +84 -0
  81. package/tsconfig.json +14 -0
  82. package/tsup.config.ts +16 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2073 @@
1
+ // src/utils/utils.ts
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ // src/package-json/package-json.ts
6
+ import { haveSameItems, sortKeys } from "@stack-dev/core";
7
+ import JSON5 from "json5";
8
+ import { isEqual } from "lodash";
9
+
10
+ // src/package-json/dependency.ts
11
+ var Dependency = class _Dependency {
12
+ _name;
13
+ _version;
14
+ constructor(name, version) {
15
+ this._name = name;
16
+ this._version = version;
17
+ }
18
+ get name() {
19
+ return this._name;
20
+ }
21
+ get version() {
22
+ return this._version;
23
+ }
24
+ equals(other) {
25
+ if (other instanceof _Dependency) {
26
+ return this._name === other._name && this._version === other._version;
27
+ } else {
28
+ return false;
29
+ }
30
+ }
31
+ };
32
+
33
+ // src/package-json/package-json.ts
34
+ var PackageJSON = class _PackageJSON {
35
+ _name;
36
+ _dependencies;
37
+ _devDependencies;
38
+ _peerDependencies;
39
+ _additionalData;
40
+ constructor(args) {
41
+ this._name = args.name;
42
+ this._dependencies = args.dependencies ?? [];
43
+ this._devDependencies = args.devDependencies ?? [];
44
+ this._peerDependencies = args.peerDependencies ?? [];
45
+ this._additionalData = args.additionalData ?? {};
46
+ }
47
+ get name() {
48
+ return this._name;
49
+ }
50
+ get dependencies() {
51
+ return this._dependencies;
52
+ }
53
+ get devDependencies() {
54
+ return this._devDependencies;
55
+ }
56
+ get peerDependencies() {
57
+ return this._peerDependencies;
58
+ }
59
+ addDependency(dependency) {
60
+ return new _PackageJSON({
61
+ name: this.name,
62
+ dependencies: [...this.dependencies, dependency],
63
+ devDependencies: this.devDependencies,
64
+ additionalData: this._additionalData
65
+ });
66
+ }
67
+ addDevDependency(dependency) {
68
+ return new _PackageJSON({
69
+ name: this.name,
70
+ dependencies: this.dependencies,
71
+ devDependencies: [...this.devDependencies, dependency],
72
+ additionalData: this._additionalData
73
+ });
74
+ }
75
+ addPeerDependency(dependency) {
76
+ return new _PackageJSON({
77
+ name: this.name,
78
+ dependencies: this.dependencies,
79
+ devDependencies: this.devDependencies,
80
+ peerDependencies: [...this.peerDependencies, dependency],
81
+ additionalData: this._additionalData
82
+ });
83
+ }
84
+ removeDependency(name) {
85
+ return new _PackageJSON({
86
+ name: this.name,
87
+ dependencies: this.dependencies.filter((d) => d.name !== name),
88
+ devDependencies: this.devDependencies,
89
+ additionalData: this._additionalData
90
+ });
91
+ }
92
+ removeDevDependency(name) {
93
+ return new _PackageJSON({
94
+ name: this.name,
95
+ dependencies: this.dependencies,
96
+ devDependencies: this.devDependencies.filter((d) => d.name !== name),
97
+ additionalData: this._additionalData
98
+ });
99
+ }
100
+ removePeerDependency(name) {
101
+ return new _PackageJSON({
102
+ name: this.name,
103
+ dependencies: this.dependencies,
104
+ devDependencies: this.devDependencies,
105
+ peerDependencies: this.peerDependencies.filter((d) => d.name !== name),
106
+ additionalData: this._additionalData
107
+ });
108
+ }
109
+ static parse(s) {
110
+ const json = JSON5.parse(s);
111
+ const name = json.name;
112
+ const dependencies = _PackageJSON.parseDependencies(json);
113
+ const devDependencies = _PackageJSON.parseDevDependencies(json);
114
+ const peerDependencies = _PackageJSON.parsePeerDependencies(json);
115
+ const additionalData = { ...json };
116
+ delete additionalData["name"];
117
+ delete additionalData["dependencies"];
118
+ delete additionalData["devDependencies"];
119
+ delete additionalData["peerDependencies"];
120
+ return new _PackageJSON({
121
+ name,
122
+ dependencies,
123
+ devDependencies,
124
+ peerDependencies,
125
+ additionalData
126
+ });
127
+ }
128
+ static parseDependencies(json) {
129
+ if ("dependencies" in json && typeof json.dependencies === "object") {
130
+ return Object.entries(json.dependencies).map(
131
+ ([name, version]) => new Dependency(name, version)
132
+ );
133
+ } else {
134
+ return [];
135
+ }
136
+ }
137
+ static parseDevDependencies(json) {
138
+ if ("devDependencies" in json && typeof json.devDependencies === "object") {
139
+ return Object.entries(json.devDependencies).map(
140
+ ([name, version]) => new Dependency(name, version)
141
+ );
142
+ } else {
143
+ return [];
144
+ }
145
+ }
146
+ static parsePeerDependencies(json) {
147
+ if ("peerDependencies" in json && typeof json.peerDependencies === "object") {
148
+ return Object.entries(json.peerDependencies).map(
149
+ ([name, version]) => new Dependency(name, version)
150
+ );
151
+ } else {
152
+ return [];
153
+ }
154
+ }
155
+ format(namespace) {
156
+ const json = {
157
+ name: this._name,
158
+ dependencies: makeDependencyObject(this._dependencies, namespace),
159
+ devDependencies: makeDependencyObject(this._devDependencies, namespace),
160
+ peerDependencies: makeDependencyObject(this._peerDependencies, namespace),
161
+ ...this._additionalData
162
+ };
163
+ const ordered = sortKeys(json, comparePackageJSONKeys);
164
+ return JSON.stringify(ordered, null, 2);
165
+ }
166
+ equals(other) {
167
+ if (other instanceof _PackageJSON) {
168
+ const sameDependencies = haveSameItems(
169
+ this._dependencies,
170
+ other._dependencies,
171
+ (d1, d2) => d1.equals(d2)
172
+ );
173
+ const sameDevDependencies = haveSameItems(
174
+ this._devDependencies,
175
+ other._devDependencies,
176
+ (d1, d2) => d1.equals(d2)
177
+ );
178
+ const samePeerDependencies = haveSameItems(
179
+ this._peerDependencies,
180
+ other._peerDependencies,
181
+ (d1, d2) => d1.equals(d2)
182
+ );
183
+ return this._name === other._name && sameDependencies && sameDevDependencies && samePeerDependencies && isEqual(this._additionalData, other._additionalData);
184
+ } else {
185
+ return false;
186
+ }
187
+ }
188
+ };
189
+ function makeDependencyObject(dependencies, namespace) {
190
+ if (dependencies.length === 0) {
191
+ return void 0;
192
+ }
193
+ const result = {};
194
+ dependencies.toSorted((a, b) => comparePackageNames(a.name, b.name, namespace)).forEach((d) => result[d.name] = d.version);
195
+ return result;
196
+ }
197
+ function comparePackageNames(a, b, namespace) {
198
+ if (a.startsWith(namespace) && b.startsWith(namespace)) {
199
+ return a.localeCompare(b);
200
+ } else if (a.startsWith(namespace)) {
201
+ return -1;
202
+ } else if (b.startsWith(namespace)) {
203
+ return 1;
204
+ } else {
205
+ return a.localeCompare(b);
206
+ }
207
+ }
208
+ function comparePackageJSONKeys(a, b) {
209
+ return getKeyIndex(a) - getKeyIndex(b);
210
+ }
211
+ function getKeyIndex(s) {
212
+ switch (s.toLowerCase()) {
213
+ case "name":
214
+ return 0;
215
+ case "version":
216
+ return 1;
217
+ case "private":
218
+ return 2;
219
+ case "bin":
220
+ return 3;
221
+ case "main":
222
+ return 4;
223
+ case "module":
224
+ return 5;
225
+ case "types":
226
+ return 6;
227
+ case "exports":
228
+ return 7;
229
+ case "scripts":
230
+ return 8;
231
+ case "dependencies":
232
+ return 9;
233
+ case "devDependencies":
234
+ return 10;
235
+ case "peerdependencies":
236
+ return 11;
237
+ default:
238
+ return Number.MAX_VALUE;
239
+ }
240
+ }
241
+
242
+ // src/utils/utils.ts
243
+ async function getDirectoryPackageJson(directory) {
244
+ const packageJsonPath = getPackageJSONPath(directory);
245
+ const packageJsonText = await fs.readFile(packageJsonPath, {
246
+ encoding: "utf-8"
247
+ });
248
+ return PackageJSON.parse(packageJsonText);
249
+ }
250
+ function getPackageJSONPath(directory) {
251
+ return path.join(directory, "package.json");
252
+ }
253
+ async function fileExists(filepath) {
254
+ try {
255
+ await fs.access(filepath, fs.constants.F_OK);
256
+ return true;
257
+ } catch {
258
+ return false;
259
+ }
260
+ }
261
+
262
+ // src/utils/workspace.ts
263
+ import fs2 from "node:fs/promises";
264
+ import path2 from "path";
265
+ import yaml from "yaml";
266
+ async function getDirectoryWorkspaceFile(directory) {
267
+ const raw = await getRawDirectoryWorkspaceFile(directory);
268
+ if ("packages" in raw && raw.packages instanceof Array) {
269
+ return {
270
+ packages: raw.packages.filter((p) => typeof p === "string")
271
+ };
272
+ } else {
273
+ return { packages: [] };
274
+ }
275
+ }
276
+ async function getRawDirectoryWorkspaceFile(directory) {
277
+ const case1 = path2.join(directory, "pnpm-workspace.yaml");
278
+ const case2 = path2.join(directory, "pnpm-workspace.yml");
279
+ if (await fileExists(case1)) {
280
+ return yaml.parse(await fs2.readFile(case1, { encoding: "utf-8" }));
281
+ } else if (await fileExists(case2)) {
282
+ return yaml.parse(await fs2.readFile(case2, { encoding: "utf-8" }));
283
+ } else {
284
+ throw new Error(`Directory "${directory}" is not a workspace.`);
285
+ }
286
+ }
287
+ async function isWorkspaceRoot(directory) {
288
+ return await fileExists(path2.join(directory, "pnpm-workspace.yaml")) || await fileExists(path2.join(directory, "pnpm-workspace.yml"));
289
+ }
290
+ async function getWorkspaceRoot(directory = process.cwd()) {
291
+ const parent = path2.dirname(directory);
292
+ if (parent === directory) {
293
+ throw new Error("Not a workspace.");
294
+ }
295
+ if (await isWorkspaceRoot(directory)) {
296
+ return directory;
297
+ }
298
+ return getWorkspaceRoot(parent);
299
+ }
300
+ async function getNamespace(directory = process.cwd()) {
301
+ const root = await getWorkspaceRoot(directory);
302
+ const packageJson = await getDirectoryPackageJson(root);
303
+ const result = packageJson.name;
304
+ if (!result) {
305
+ throw new Error("Missing name.");
306
+ }
307
+ return `@${result}`;
308
+ }
309
+
310
+ // src/packages/library-package/create-library-package.ts
311
+ import path4 from "node:path";
312
+
313
+ // src/file-generator/package-json-generator.ts
314
+ var PackageJsonGenerator = class {
315
+ _packageJson;
316
+ _namespace;
317
+ constructor(packageJson, namespace = "") {
318
+ this._packageJson = packageJson;
319
+ this._namespace = namespace;
320
+ }
321
+ get filepath() {
322
+ return "package.json";
323
+ }
324
+ async generate() {
325
+ return this._packageJson.format(this._namespace);
326
+ }
327
+ };
328
+
329
+ // src/utils/package-generator.ts
330
+ import fs3 from "node:fs/promises";
331
+ import path3 from "node:path";
332
+ var PackageGenerator = class {
333
+ _root;
334
+ _packageJson;
335
+ _fileGenerators;
336
+ constructor(root, packageJson, fileGenerators = []) {
337
+ this._root = root;
338
+ this._packageJson = packageJson;
339
+ this._fileGenerators = fileGenerators;
340
+ }
341
+ async generate() {
342
+ const all = [this._packageJson, ...this._fileGenerators];
343
+ await fs3.mkdir(this._root, { recursive: true });
344
+ await Promise.all(all.map((gen) => this.write(gen)));
345
+ }
346
+ async write(fileGenerator) {
347
+ const contents = await fileGenerator.generate();
348
+ const filepath = path3.join(this._root, fileGenerator.filepath);
349
+ const directory = path3.dirname(filepath);
350
+ await fs3.mkdir(directory, { recursive: true });
351
+ await fs3.writeFile(filepath, contents);
352
+ }
353
+ };
354
+
355
+ // src/file-generator/file-generator-imp.ts
356
+ var FileGeneratorImp = class {
357
+ _filepath;
358
+ _contents;
359
+ constructor(filepath, contents) {
360
+ this._filepath = filepath;
361
+ this._contents = contents;
362
+ }
363
+ get filepath() {
364
+ return this._filepath;
365
+ }
366
+ async generate() {
367
+ return this._contents;
368
+ }
369
+ };
370
+
371
+ // src/packages/library-package/files/add-file-generator.ts
372
+ var ADD_TS = `export function add(n1: number, n2: number): number {
373
+ return n1 + n2;
374
+ }
375
+ `;
376
+ var ADD_FILE_GENERATOR = new FileGeneratorImp("src/add.ts", ADD_TS);
377
+
378
+ // src/packages/library-package/files/add-spec-file-generator.ts
379
+ var ADD_SPEC_TS = `import { describe, it, expect } from 'vitest';
380
+
381
+ import { add } from '../add';
382
+
383
+ describe('add', () => {
384
+ it('adds two numbers', () => {
385
+ expect(add(2, 3)).toBe(5);
386
+ });
387
+ });
388
+ `;
389
+ var ADD_SPEC_FILE_GENERATOR = new FileGeneratorImp(
390
+ "src/spec/add.spec.ts",
391
+ ADD_SPEC_TS
392
+ );
393
+
394
+ // src/packages/library-package/files/eslint-config-file-generator.ts
395
+ var ESLINT_CONFIG = `import base from '@stack-dev/eslint-config/base.mjs';
396
+
397
+ export default [...base, { ignores: ['**/dist/**'] }];
398
+ `;
399
+ var ESLINT_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
400
+ "eslint.config.mjs",
401
+ ESLINT_CONFIG
402
+ );
403
+
404
+ // src/packages/library-package/files/index-file-generator.ts
405
+ var INDEX_TS = `export * from './add';
406
+ `;
407
+ var INDEX_FILE_GENERATOR = new FileGeneratorImp(
408
+ "src/index.ts",
409
+ INDEX_TS
410
+ );
411
+
412
+ // src/packages/library-package/files/prettier-config-file-generator.ts
413
+ var PRETTIER_CONFIG = `import base from '@stack-dev/prettier-config/base.mjs';
414
+
415
+ export default base;
416
+ `;
417
+ var PRETTIER_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
418
+ "prettier.config.mjs",
419
+ PRETTIER_CONFIG
420
+ );
421
+
422
+ // src/packages/library-package/files/tsconfig-file-generator.ts
423
+ var TSCONFIG = `{
424
+ "extends": "@stack-dev/typescript-config/tsconfig.base.json",
425
+ "compilerOptions": {
426
+ "outDir": "dist"
427
+ },
428
+ "include": ["src"]
429
+ }
430
+ `;
431
+ var TSCONFIG_FILE_GENERATOR = new FileGeneratorImp(
432
+ "tsconfig.json",
433
+ TSCONFIG
434
+ );
435
+
436
+ // src/packages/library-package/files/tsup-config-file-generator.ts
437
+ var TSUP_CONFIG = `import { defineConfig } from 'tsup';
438
+
439
+ export default defineConfig({
440
+ entry: ['src/index.ts'],
441
+ format: ['esm', 'cjs'],
442
+ dts: true,
443
+ sourcemap: true,
444
+ clean: true,
445
+ target: 'esnext',
446
+ outExtension({ format }) {
447
+ return {
448
+ js: format === 'esm' ? '.mjs' : '.js',
449
+ };
450
+ },
451
+ });
452
+ `;
453
+ var TSUP_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
454
+ "tsup.config.ts",
455
+ TSUP_CONFIG
456
+ );
457
+
458
+ // src/packages/library-package/files/vitest-config-file-generator.ts
459
+ var VITEST_CONFIG = `import { defineConfig } from 'vitest/config';
460
+
461
+ export default defineConfig({
462
+ test: {
463
+ globals: true,
464
+ coverage: {
465
+ provider: 'v8',
466
+ },
467
+ environment: 'node',
468
+ },
469
+ });
470
+ `;
471
+ var VITEST_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
472
+ "vitest.config.ts",
473
+ VITEST_CONFIG
474
+ );
475
+
476
+ // src/packages/library-package/create-library-package.ts
477
+ async function createLibraryPackage(name) {
478
+ const rootDir = await getWorkspaceRoot();
479
+ const directory = path4.join(rootDir, "packages", name);
480
+ const namespace = await getNamespace(rootDir);
481
+ const packageName = `${namespace}/${name}`;
482
+ console.log(`\u2728 Creating config package: ${packageName}`);
483
+ const generator = new PackageGenerator(
484
+ directory,
485
+ makePackageGenerator(packageName, namespace),
486
+ [
487
+ INDEX_FILE_GENERATOR,
488
+ ADD_FILE_GENERATOR,
489
+ ADD_SPEC_FILE_GENERATOR,
490
+ TSUP_CONFIG_FILE_GENERATOR,
491
+ TSCONFIG_FILE_GENERATOR,
492
+ PRETTIER_CONFIG_FILE_GENERATOR,
493
+ ESLINT_CONFIG_FILE_GENERATOR,
494
+ VITEST_CONFIG_FILE_GENERATOR
495
+ ]
496
+ );
497
+ await generator.generate();
498
+ console.log(`\u2705 Config package created at: ${directory}`);
499
+ }
500
+ function makePackageGenerator(packageName, namespace) {
501
+ const packageJsonModel = new PackageJSON({
502
+ name: packageName,
503
+ devDependencies: [
504
+ new Dependency(`${namespace}/eslint-config`, "workspace:*"),
505
+ new Dependency(`${namespace}/prettier-config`, "workspace:*"),
506
+ new Dependency(`${namespace}/typescript-config`, "workspace:*"),
507
+ new Dependency("eslint", "^9.32.0"),
508
+ new Dependency("prettier", "^3.6.2"),
509
+ new Dependency("prettier-plugin-organize-imports", "^4.2.0"),
510
+ new Dependency("tsup", "^7.3.0"),
511
+ new Dependency("vitest", "^3.2.4"),
512
+ new Dependency("@vitest/coverage-v8", "^3.2.4")
513
+ ],
514
+ additionalData: {
515
+ version: "0.1.0",
516
+ private: true,
517
+ main: "dist/index.js",
518
+ module: "dist/index.mjs",
519
+ types: "dist/index.d.ts",
520
+ exports: {
521
+ ".": {
522
+ development: "./src/index.ts",
523
+ import: "./dist/index.mjs",
524
+ require: "./dist/index.js",
525
+ types: "./dist/index.d.ts"
526
+ }
527
+ },
528
+ scripts: {
529
+ build: "tsup",
530
+ lint: "eslint",
531
+ format: "prettier . --write",
532
+ test: "vitest run",
533
+ "test:watch": "vitest"
534
+ },
535
+ sideEffects: false
536
+ }
537
+ });
538
+ return new PackageJsonGenerator(packageJsonModel, namespace);
539
+ }
540
+
541
+ // src/packages/react-package/create-tailwind-react-package.ts
542
+ async function createTailwindReactPackage(name) {
543
+ throw new Error("Not implemented.");
544
+ }
545
+ var STYLED_BUTTON = `import React from 'react';
546
+
547
+ interface ButtonProps {
548
+ label: string;
549
+ onClick?: () => void;
550
+ }
551
+
552
+ export function TailwindButton(props: ButtonProps) {
553
+ const {label, onClick} = props;
554
+
555
+ return (
556
+ <button
557
+ onClick={onClick}
558
+ className="my-lib-bg-blue-600 my-lib-text-white my-lib-p-[10px] my-lib-border-none my-lib-rounded my-lib-cursor-pointer hover:my-lib-brightness-110"
559
+ >
560
+ {label}
561
+ </button>
562
+ );
563
+ };`;
564
+ var STYLED_BUTTON_FILE_GENERATOR = new FileGeneratorImp(
565
+ "styled-button.ts",
566
+ STYLED_BUTTON
567
+ );
568
+
569
+ // src/packages/react-package/create-unstyled-react-package.ts
570
+ async function createUnstyledReactPackage(name) {
571
+ throw new Error("Not implemented.");
572
+ }
573
+
574
+ // src/packages/react-package/css-react-package/create-css-react-package.ts
575
+ import path5 from "path";
576
+
577
+ // src/packages/react-package/css-react-package/files/button-css-module-file-generator.ts
578
+ var BUTTON_CSS_MODULE = `.styledButton {
579
+ background: blue;
580
+ color: white;
581
+ padding: 10px;
582
+ border: none;
583
+ border-radius: 4px;
584
+ cursor: pointer;
585
+ }
586
+ `;
587
+ var BUTTON_CSS_MODULE_FILE_GENERATOR = new FileGeneratorImp(
588
+ "src/button.module.css",
589
+ BUTTON_CSS_MODULE
590
+ );
591
+
592
+ // src/packages/react-package/css-react-package/files/button-file-generator.ts
593
+ var BUTTON = `import React from 'react';
594
+ import * as styles from './button.module.css';
595
+
596
+ export function Button(props: HTMLAttributes<HTMLButtonElement>) {
597
+ return <button className={styles.styledButton} {...props} />;
598
+ }
599
+ `;
600
+ var BUTTON_FILE_GENERATOR = new FileGeneratorImp(
601
+ "src/button.tsx",
602
+ BUTTON
603
+ );
604
+
605
+ // src/packages/react-package/css-react-package/files/button-spec-file-generator.ts
606
+ var BUTTON_SPEC = `import { render, screen, fireEvent } from '@testing-library/react';
607
+ import { Button } from './button';
608
+
609
+ describe('Button', () => {
610
+ it('renders the label correctly', () => {
611
+ render(<Button>Click Me</Button>);
612
+ expect(screen.getByText('Click Me')).toBeDefined();
613
+ });
614
+
615
+ it('is a button element', () => {
616
+ render(<Button>Submit</Button>);
617
+ const buttonElement = screen.getByRole('button');
618
+ expect(buttonElement.tagName).toBe('BUTTON');
619
+ });
620
+
621
+ /* Note: Testing for specific CSS classes with CSS Modules is tricky
622
+ because class names are mangled (e.g., _styledButton_123).
623
+ Usually, we just test that the class attribute exists.
624
+ */
625
+ it('applies a class name', () => {
626
+ render(<Button>Styled</Button>);
627
+ const buttonElement = screen.getByRole('button');
628
+ expect(buttonElement.className).toBeTruthy();
629
+ });
630
+ });
631
+ `;
632
+ var BUTTON_SPEC_FILE_GENERATOR = new FileGeneratorImp(
633
+ "src/button.spec.tsx",
634
+ BUTTON_SPEC
635
+ );
636
+
637
+ // src/packages/react-package/css-react-package/files/eslint-config-file-generator.ts
638
+ var ESLINT_CONFIG2 = `import base from '@stack-dev/eslint-config/base.mjs';
639
+ import react from '@stack-dev/eslint-config/react.mjs';
640
+
641
+ export default [
642
+ ...base,
643
+ ...react,
644
+ {
645
+ ignores: ['**/dist/**', '**/coverage/**']
646
+ }
647
+ ];
648
+ `;
649
+ var ESLINT_CONFIG_FILE_GENERATOR2 = new FileGeneratorImp(
650
+ "eslint.config.mjs",
651
+ ESLINT_CONFIG2
652
+ );
653
+
654
+ // src/packages/react-package/css-react-package/files/index-file-generator.ts
655
+ var INDEX_TS2 = `export * from './button';
656
+ `;
657
+ var INDEX_FILE_GENERATOR2 = new FileGeneratorImp(
658
+ "src/index.ts",
659
+ INDEX_TS2
660
+ );
661
+
662
+ // src/packages/react-package/css-react-package/files/prettier-config-file-generator.ts
663
+ var PRETTIER_CONFIG2 = `import base from '@stack-dev/prettier-config/base.mjs';
664
+
665
+ export default base;
666
+ `;
667
+ var PRETTIER_CONFIG_FILE_GENERATOR2 = new FileGeneratorImp(
668
+ "prettier.config.mjs",
669
+ PRETTIER_CONFIG2
670
+ );
671
+
672
+ // src/packages/react-package/css-react-package/files/tsconfig-file-generator.ts
673
+ var TSCONFIG2 = `{
674
+ "extends": "@stack-dev/typescript-config/tsconfig.react.json",
675
+ "compilerOptions": {
676
+ "outDir": "dist"
677
+ },
678
+ "include": ["src"]
679
+ }
680
+ `;
681
+ var TSCONFIG_FILE_GENERATOR2 = new FileGeneratorImp(
682
+ "tsconfig.json",
683
+ TSCONFIG2
684
+ );
685
+
686
+ // src/packages/react-package/css-react-package/files/tsup-config-file-generator.ts
687
+ var TSUP_CONFIG2 = `import { defineConfig } from 'tsup';
688
+
689
+ export default defineConfig({
690
+ entry: ['src/index.ts'],
691
+ format: ['cjs', 'esm'],
692
+ dts: true,
693
+ minify: true,
694
+ clean: true,
695
+ injectStyle: true,
696
+ external: ['react', 'react-dom'],
697
+ outExtension({ format }) {
698
+ return {
699
+ js: format === 'esm' ? '.mjs' : '.js',
700
+ };
701
+ },
702
+ });
703
+ `;
704
+ var TSUP_CONFIG_FILE_GENERATOR2 = new FileGeneratorImp(
705
+ "tsup.config.ts",
706
+ TSUP_CONFIG2
707
+ );
708
+
709
+ // src/packages/react-package/css-react-package/files/vitest-config-file-generator.ts
710
+ var VITEST_CONFIG2 = `import { defineConfig } from 'vitest/config';
711
+ import react from '@vitejs/plugin-react';
712
+
713
+ export default defineConfig({
714
+ plugins: [react()],
715
+ test: {
716
+ globals: true,
717
+ environment: 'jsdom',
718
+ coverage: {
719
+ provider: 'v8',
720
+ reporter: ['text', 'json', 'html'],
721
+ },
722
+ css: true,
723
+ },
724
+ });
725
+ `;
726
+ var VITEST_CONFIG_FILE_GENERATOR2 = new FileGeneratorImp(
727
+ "vitest.config.ts",
728
+ VITEST_CONFIG2
729
+ );
730
+
731
+ // src/packages/react-package/css-react-package/create-css-react-package.ts
732
+ async function createCssReactPackage(name) {
733
+ const rootDir = await getWorkspaceRoot();
734
+ const directory = path5.join(rootDir, "packages", name);
735
+ const namespace = await getNamespace(rootDir);
736
+ const packageName = `${namespace}/${name}`;
737
+ console.log(`\u2728 Creating CSS Modules React library: ${packageName}`);
738
+ const generator = new PackageGenerator(
739
+ directory,
740
+ makePackageGenerator2(packageName, namespace),
741
+ [
742
+ INDEX_FILE_GENERATOR2,
743
+ BUTTON_FILE_GENERATOR,
744
+ BUTTON_CSS_MODULE_FILE_GENERATOR,
745
+ BUTTON_SPEC_FILE_GENERATOR,
746
+ TSUP_CONFIG_FILE_GENERATOR2,
747
+ TSCONFIG_FILE_GENERATOR2,
748
+ PRETTIER_CONFIG_FILE_GENERATOR2,
749
+ ESLINT_CONFIG_FILE_GENERATOR2,
750
+ VITEST_CONFIG_FILE_GENERATOR2
751
+ ]
752
+ );
753
+ await generator.generate();
754
+ console.log(`\u2705 Library created at: ${directory}`);
755
+ }
756
+ function makePackageGenerator2(packageName, namespace) {
757
+ const packageJsonModel = new PackageJSON({
758
+ name: packageName,
759
+ peerDependencies: [
760
+ new Dependency("react", ">=18"),
761
+ new Dependency("react-dom", ">=18")
762
+ ],
763
+ devDependencies: [
764
+ new Dependency(`${namespace}/eslint-config`, "workspace:*"),
765
+ new Dependency(`${namespace}/prettier-config`, "workspace:*"),
766
+ new Dependency(`${namespace}/typescript-config`, "workspace:*"),
767
+ // Development React binaries
768
+ new Dependency("react", "^18.3.1"),
769
+ new Dependency("react-dom", "^18.3.1"),
770
+ new Dependency("@types/react", "^18.3.1"),
771
+ new Dependency("@types/react-dom", "^18.3.1"),
772
+ // Linting & Formatting
773
+ new Dependency("eslint", "^9.32.0"),
774
+ new Dependency("prettier", "^3.6.2"),
775
+ new Dependency("prettier-plugin-organize-imports", "^4.2.0"),
776
+ // Build
777
+ new Dependency("tsup", "^8.0.0"),
778
+ new Dependency("postcss", "^8.4.0"),
779
+ // Testing
780
+ new Dependency("vitest", "^3.2.4"),
781
+ new Dependency("@vitest/coverage-v8", "^3.2.4"),
782
+ new Dependency("@testing-library/react", "^16.0.0"),
783
+ new Dependency("@testing-library/jest-dom", "^6.0.0"),
784
+ new Dependency("jsdom", "^25.0.0")
785
+ ],
786
+ additionalData: {
787
+ version: "0.1.0",
788
+ private: true,
789
+ main: "dist/index.js",
790
+ module: "dist/index.mjs",
791
+ types: "dist/index.d.ts",
792
+ exports: {
793
+ ".": {
794
+ development: "./src/index.ts",
795
+ import: "./dist/index.mjs",
796
+ require: "./dist/index.js",
797
+ types: "./dist/index.d.ts"
798
+ },
799
+ "./index.css": "./dist/index.css"
800
+ },
801
+ scripts: {
802
+ build: "tsup",
803
+ lint: "eslint .",
804
+ format: "prettier . --write",
805
+ test: "vitest run",
806
+ "test:watch": "vitest"
807
+ },
808
+ sideEffects: ["**/*.css"]
809
+ }
810
+ });
811
+ return new PackageJsonGenerator(packageJsonModel, namespace);
812
+ }
813
+
814
+ // src/packages/react-package/styled-components-react-package/create-styled-components-react-package.ts
815
+ import path6 from "path";
816
+
817
+ // src/packages/react-package/styled-components-react-package/files/button-file-generator.ts
818
+ var BUTTON2 = `import React, { HTMLAttributes } from 'react';
819
+ import styled from 'styled-components';
820
+
821
+ // This is your "Styled" version of the button
822
+ // No more imports, no more "empty objects"
823
+ const StyledButton = styled.button\`
824
+ background-color: #007bff;
825
+ color: white;
826
+ padding: 10px 20px;
827
+ border: none;
828
+ border-radius: 4px;
829
+ cursor: pointer;
830
+ font-size: 16px;
831
+
832
+ &:hover {
833
+ background-color: #0056b3;
834
+ }
835
+ \`;
836
+
837
+ export function Button(props: HTMLAttributes<HTMLButtonElement>) {
838
+ return <StyledButton {...props} />;
839
+ }
840
+ `;
841
+ var BUTTON_FILE_GENERATOR2 = new FileGeneratorImp(
842
+ "src/button.tsx",
843
+ BUTTON2
844
+ );
845
+
846
+ // src/packages/react-package/styled-components-react-package/files/button-spec-file-generator.ts
847
+ var BUTTON_SPEC2 = `import { render, screen, fireEvent } from '@testing-library/react';
848
+ import { Button } from './button';
849
+
850
+ describe('Button', () => {
851
+ it('renders the label correctly', () => {
852
+ render(<Button>Click Me</Button>);
853
+ expect(screen.getByText('Click Me')).toBeDefined();
854
+ });
855
+
856
+ it('is a button element', () => {
857
+ render(<Button>Submit</Button>);
858
+ const buttonElement = screen.getByRole('button');
859
+ expect(buttonElement.tagName).toBe('BUTTON');
860
+ });
861
+
862
+ /* Note: Testing for specific CSS classes with CSS Modules is tricky
863
+ because class names are mangled (e.g., _styledButton_123).
864
+ Usually, we just test that the class attribute exists.
865
+ */
866
+ it('applies a class name', () => {
867
+ render(<Button>Styled</Button>);
868
+ const buttonElement = screen.getByRole('button');
869
+ expect(buttonElement.className).toBeTruthy();
870
+ });
871
+ });
872
+ `;
873
+ var BUTTON_SPEC_FILE_GENERATOR2 = new FileGeneratorImp(
874
+ "src/button.spec.tsx",
875
+ BUTTON_SPEC2
876
+ );
877
+
878
+ // src/packages/react-package/styled-components-react-package/files/eslint-config-file-generator.ts
879
+ var ESLINT_CONFIG3 = `import base from '@stack-dev/eslint-config/base.mjs';
880
+ import react from '@stack-dev/eslint-config/react.mjs';
881
+
882
+ export default [
883
+ ...base,
884
+ ...react,
885
+ {
886
+ ignores: ['**/dist/**', '**/coverage/**']
887
+ }
888
+ ];
889
+ `;
890
+ var ESLINT_CONFIG_FILE_GENERATOR3 = new FileGeneratorImp(
891
+ "eslint.config.mjs",
892
+ ESLINT_CONFIG3
893
+ );
894
+
895
+ // src/packages/react-package/styled-components-react-package/files/index-file-generator.ts
896
+ var INDEX_TS3 = `export * from './button';
897
+ `;
898
+ var INDEX_FILE_GENERATOR3 = new FileGeneratorImp(
899
+ "src/index.ts",
900
+ INDEX_TS3
901
+ );
902
+
903
+ // src/packages/react-package/styled-components-react-package/files/prettier-config-file-generator.ts
904
+ var PRETTIER_CONFIG3 = `import base from '@stack-dev/prettier-config/base.mjs';
905
+
906
+ export default base;
907
+ `;
908
+ var PRETTIER_CONFIG_FILE_GENERATOR3 = new FileGeneratorImp(
909
+ "prettier.config.mjs",
910
+ PRETTIER_CONFIG3
911
+ );
912
+
913
+ // src/packages/react-package/styled-components-react-package/files/tsconfig-file-generator.ts
914
+ var TSCONFIG3 = `{
915
+ "extends": "@stack-dev/typescript-config/tsconfig.react.json",
916
+ "compilerOptions": {
917
+ "outDir": "dist"
918
+ },
919
+ "include": ["src"]
920
+ }
921
+ `;
922
+ var TSCONFIG_FILE_GENERATOR3 = new FileGeneratorImp(
923
+ "tsconfig.json",
924
+ TSCONFIG3
925
+ );
926
+
927
+ // src/packages/react-package/styled-components-react-package/files/tsup-config-file-generator.ts
928
+ var TSUP_CONFIG3 = `import { defineConfig } from 'tsup';
929
+
930
+ export default defineConfig({
931
+ entry: ['src/index.ts'],
932
+ format: ['esm', 'cjs'],
933
+ dts: true,
934
+ clean: true,
935
+ external: ['react', 'react-dom', 'styled-components'],
936
+ outExtension({ format }) {
937
+ return {
938
+ js: format === 'esm' ? '.mjs' : '.js',
939
+ };
940
+ },
941
+ });`;
942
+ var TSUP_CONFIG_FILE_GENERATOR3 = new FileGeneratorImp(
943
+ "tsup.config.ts",
944
+ TSUP_CONFIG3
945
+ );
946
+
947
+ // src/packages/react-package/styled-components-react-package/files/vitest-config-file-generator.ts
948
+ var VITEST_CONFIG3 = `import { defineConfig } from 'vitest/config';
949
+ import react from '@vitejs/plugin-react';
950
+
951
+ export default defineConfig({
952
+ plugins: [react()],
953
+ test: {
954
+ globals: true,
955
+ environment: 'jsdom',
956
+ coverage: {
957
+ provider: 'v8',
958
+ reporter: ['text', 'json', 'html'],
959
+ },
960
+ css: true,
961
+ },
962
+ });
963
+ `;
964
+ var VITEST_CONFIG_FILE_GENERATOR3 = new FileGeneratorImp(
965
+ "vitest.config.ts",
966
+ VITEST_CONFIG3
967
+ );
968
+
969
+ // src/packages/react-package/styled-components-react-package/create-styled-components-react-package.ts
970
+ async function createStyledComponentsReactPackage(name) {
971
+ const rootDir = await getWorkspaceRoot();
972
+ const directory = path6.join(rootDir, "packages", name);
973
+ const namespace = await getNamespace(rootDir);
974
+ const packageName = `${namespace}/${name}`;
975
+ console.log(
976
+ `\u2728 Creating Styled Components Modules React library: ${packageName}`
977
+ );
978
+ const generator = new PackageGenerator(
979
+ directory,
980
+ makePackageGenerator3(packageName, namespace),
981
+ [
982
+ INDEX_FILE_GENERATOR3,
983
+ BUTTON_FILE_GENERATOR2,
984
+ BUTTON_SPEC_FILE_GENERATOR2,
985
+ TSUP_CONFIG_FILE_GENERATOR3,
986
+ TSCONFIG_FILE_GENERATOR3,
987
+ PRETTIER_CONFIG_FILE_GENERATOR3,
988
+ ESLINT_CONFIG_FILE_GENERATOR3,
989
+ VITEST_CONFIG_FILE_GENERATOR3
990
+ ]
991
+ );
992
+ await generator.generate();
993
+ console.log(`\u2705 Library created at: ${directory}`);
994
+ }
995
+ function makePackageGenerator3(packageName, namespace) {
996
+ const packageJsonModel = new PackageJSON({
997
+ name: packageName,
998
+ // Peer deps are crucial for Styled Components to prevent "Multiple instances" errors
999
+ peerDependencies: [
1000
+ new Dependency("react", ">=18"),
1001
+ new Dependency("react-dom", ">=18"),
1002
+ new Dependency("styled-components", ">=6")
1003
+ ],
1004
+ devDependencies: [
1005
+ new Dependency(`${namespace}/eslint-config`, "workspace:*"),
1006
+ new Dependency(`${namespace}/prettier-config`, "workspace:*"),
1007
+ new Dependency(`${namespace}/typescript-config`, "workspace:*"),
1008
+ // Development React binaries
1009
+ new Dependency("react", "^18.3.1"),
1010
+ new Dependency("react-dom", "^18.3.1"),
1011
+ new Dependency("@types/react", "^18.3.1"),
1012
+ new Dependency("@types/react-dom", "^18.3.1"),
1013
+ // Styled Components types and binary for build time
1014
+ new Dependency("styled-components", "^6.1.13"),
1015
+ new Dependency("@types/styled-components", "^5.1.34"),
1016
+ // Linting & Formatting
1017
+ new Dependency("eslint", "^9.32.0"),
1018
+ new Dependency("prettier", "^3.6.2"),
1019
+ new Dependency("prettier-plugin-organize-imports", "^4.2.0"),
1020
+ // Build
1021
+ new Dependency("tsup", "^8.0.0"),
1022
+ // Testing
1023
+ new Dependency("vitest", "^3.2.4"),
1024
+ new Dependency("@vitest/coverage-v8", "^3.2.4"),
1025
+ new Dependency("@testing-library/react", "^16.0.0"),
1026
+ new Dependency("@testing-library/jest-dom", "^6.0.0"),
1027
+ new Dependency("jsdom", "^25.0.0")
1028
+ ],
1029
+ additionalData: {
1030
+ version: "0.1.0",
1031
+ private: true,
1032
+ type: "module",
1033
+ // Added this to ensure ESM consistency
1034
+ main: "dist/index.js",
1035
+ module: "dist/index.mjs",
1036
+ types: "dist/index.d.ts",
1037
+ exports: {
1038
+ ".": {
1039
+ development: "./src/index.ts",
1040
+ import: "./dist/index.mjs",
1041
+ require: "./dist/index.js",
1042
+ types: "./dist/index.d.ts"
1043
+ }
1044
+ // Removed './index.css' as it's no longer produced by Styled Components
1045
+ },
1046
+ scripts: {
1047
+ build: "tsup",
1048
+ dev: "tsup --watch",
1049
+ // Helpful for local lib dev
1050
+ lint: "eslint .",
1051
+ format: "prettier . --write",
1052
+ test: "vitest run",
1053
+ "test:watch": "vitest"
1054
+ },
1055
+ // Set to false or removed because Styled Components are pure JS/TS
1056
+ sideEffects: false
1057
+ }
1058
+ });
1059
+ return new PackageJsonGenerator(packageJsonModel, namespace);
1060
+ }
1061
+
1062
+ // src/packages/react-package/create-react-package.ts
1063
+ async function createReactPackage(name, style) {
1064
+ switch (style) {
1065
+ case "tailwind":
1066
+ await createTailwindReactPackage(name);
1067
+ break;
1068
+ case "css-modules":
1069
+ await createCssReactPackage(name);
1070
+ break;
1071
+ case "styled-components":
1072
+ await createStyledComponentsReactPackage(name);
1073
+ break;
1074
+ case "none":
1075
+ await createUnstyledReactPackage(name);
1076
+ break;
1077
+ }
1078
+ }
1079
+
1080
+ // src/packages/create-config-package.ts
1081
+ import path7 from "node:path";
1082
+ async function createConfigPackage(name) {
1083
+ const rootDir = await getWorkspaceRoot();
1084
+ const directory = path7.join(rootDir, "configs", name);
1085
+ const namespace = await getNamespace(rootDir);
1086
+ const packageName = `${namespace}/${name}`;
1087
+ const packageJsonModel = new PackageJSON({
1088
+ name: packageName,
1089
+ additionalData: {
1090
+ version: "0.1.0",
1091
+ private: true
1092
+ }
1093
+ });
1094
+ const generator = new PackageGenerator(
1095
+ directory,
1096
+ new PackageJsonGenerator(packageJsonModel, namespace),
1097
+ []
1098
+ );
1099
+ await generator.generate();
1100
+ }
1101
+
1102
+ // src/utils/package.ts
1103
+ import { glob } from "fast-glob";
1104
+ import path8 from "node:path";
1105
+ async function getCurrentPackage(directory = process.cwd()) {
1106
+ const packageRoot = await getPackageRoot(directory);
1107
+ const packageJson = await getDirectoryPackageJson(packageRoot);
1108
+ return getPackageByName(packageJson.name);
1109
+ }
1110
+ async function getPackageRoot(directory = process.cwd()) {
1111
+ const parent = path8.dirname(directory);
1112
+ if (parent === directory) {
1113
+ throw new Error("Not a package.");
1114
+ }
1115
+ if (await isPackageRoot(directory)) {
1116
+ return directory;
1117
+ }
1118
+ return getPackageRoot(parent);
1119
+ }
1120
+ async function getPackageByName(name) {
1121
+ const all = await getAllPackages();
1122
+ const match = all.find((p) => p.name === name);
1123
+ if (match === void 0) {
1124
+ throw new Error(`No package with name "${name}".`);
1125
+ }
1126
+ return match;
1127
+ }
1128
+ async function getAllPackages(directory = process.cwd()) {
1129
+ const workspaceRoot = await getWorkspaceRoot(directory);
1130
+ const workspaceFile = await getDirectoryWorkspaceFile(workspaceRoot);
1131
+ const results = [];
1132
+ for (const seg of workspaceFile.packages) {
1133
+ const packageType = getPackageType(seg);
1134
+ const packageJsonPaths = await glob(`${workspaceRoot}/${seg}/package.json`);
1135
+ const packageDirectories = packageJsonPaths.map((p) => path8.dirname(p));
1136
+ for (const directory2 of packageDirectories) {
1137
+ const name = (await getDirectoryPackageJson(directory2)).name;
1138
+ results.push({
1139
+ name,
1140
+ directory: directory2,
1141
+ type: packageType
1142
+ });
1143
+ }
1144
+ }
1145
+ return results;
1146
+ }
1147
+ function getPackageType(segment) {
1148
+ if (segment.startsWith("app")) {
1149
+ return "App";
1150
+ } else if (segment.startsWith("config")) {
1151
+ return "Config";
1152
+ } else if (segment.startsWith("package") || segment.startsWith("lib")) {
1153
+ return "Library";
1154
+ } else {
1155
+ return "Unknown";
1156
+ }
1157
+ }
1158
+ function comparePackages(a, b) {
1159
+ const packageTypeDifference = comparePackageTypes(a.type, b.type);
1160
+ if (packageTypeDifference !== 0) {
1161
+ return packageTypeDifference;
1162
+ } else {
1163
+ return a.name.localeCompare(b.name);
1164
+ }
1165
+ }
1166
+ function comparePackageTypes(a, b) {
1167
+ return getPackageTypeIndex(a) - getPackageTypeIndex(b);
1168
+ }
1169
+ function getPackageTypeIndex(packageType) {
1170
+ switch (packageType) {
1171
+ case "Library":
1172
+ return 0;
1173
+ case "Config":
1174
+ return 1;
1175
+ case "App":
1176
+ return 2;
1177
+ case "Unknown":
1178
+ return 3;
1179
+ }
1180
+ }
1181
+ async function isPackageRoot(directory) {
1182
+ return await fileExists(path8.join(directory, "package.json"));
1183
+ }
1184
+
1185
+ // src/utils/package-type.ts
1186
+ import { prompt } from "enquirer";
1187
+ var packageTypes = [
1188
+ "library",
1189
+ "config",
1190
+ "react",
1191
+ "vite",
1192
+ "fastify",
1193
+ "next",
1194
+ "cli"
1195
+ ];
1196
+ async function pickPackageType(options) {
1197
+ if (options?.type && isPackageType(options.type)) {
1198
+ return options.type;
1199
+ } else if (options?.type && !isPackageType(options.type)) {
1200
+ throw new Error(
1201
+ `--type setting "${options.type}" is invalid, must be one of ${packageTypes.join(", ")}.`
1202
+ );
1203
+ }
1204
+ const response = await prompt({
1205
+ type: "select",
1206
+ name: "type",
1207
+ message: "What kind of package do you want?",
1208
+ choices: [...packageTypes]
1209
+ });
1210
+ if (!isPackageType(response.type)) {
1211
+ throw new Error(
1212
+ `Type "${response.type}" is invalid, must be one of ${packageTypes.join(", ")}.`
1213
+ );
1214
+ }
1215
+ return response.type;
1216
+ }
1217
+ function isPackageType(s) {
1218
+ return packageTypes.some((p) => p === s);
1219
+ }
1220
+
1221
+ // src/utils/style-type.ts
1222
+ import { prompt as prompt2 } from "enquirer";
1223
+ var styleTypes = [
1224
+ "tailwind",
1225
+ "css-modules",
1226
+ "styled-components",
1227
+ "none"
1228
+ ];
1229
+ async function pickStyleType(options) {
1230
+ if (options?.style && isStyleType(options?.style)) {
1231
+ return options?.style;
1232
+ } else if (options?.style && !isStyleType(options?.style)) {
1233
+ throw new Error(
1234
+ `--style setting "${options.style}" is invalid, must be one of ${styleTypes.join(", ")}.`
1235
+ );
1236
+ }
1237
+ const response = await prompt2({
1238
+ type: "select",
1239
+ name: "type",
1240
+ message: "What kind of style do you want?",
1241
+ choices: [...styleTypes]
1242
+ });
1243
+ if (!isStyleType(response.type)) {
1244
+ throw new Error(
1245
+ `Type "${response.type}" is invalid, must be one of ${styleTypes.join(", ")}.`
1246
+ );
1247
+ }
1248
+ return response.type;
1249
+ }
1250
+ function isStyleType(s) {
1251
+ return styleTypes.some((p) => p === s);
1252
+ }
1253
+
1254
+ // src/index.ts
1255
+ import { Command } from "commander";
1256
+ import { prompt as prompt3 } from "enquirer";
1257
+
1258
+ // src/link-packages.ts
1259
+ import fs4 from "node:fs/promises";
1260
+ import path9 from "node:path";
1261
+
1262
+ // src/tsconfig/tsconfig.ts
1263
+ import * as JSON52 from "json5";
1264
+ import { haveSameItems as haveSameItems2, sortKeys as sortKeys3 } from "@stack-dev/core";
1265
+ import { isEqual as isEqual3 } from "lodash";
1266
+
1267
+ // src/tsconfig/compiler-options.ts
1268
+ import { sortKeys as sortKeys2 } from "@stack-dev/core";
1269
+ import { isEqual as isEqual2 } from "lodash";
1270
+ var CompilerOptions = class _CompilerOptions {
1271
+ _additionalData;
1272
+ _paths;
1273
+ constructor(args) {
1274
+ this._paths = args?.paths ?? {};
1275
+ this._additionalData = args?.additionalData ?? {};
1276
+ }
1277
+ get paths() {
1278
+ return this._paths;
1279
+ }
1280
+ get additionalData() {
1281
+ return this._additionalData;
1282
+ }
1283
+ setPaths(paths) {
1284
+ return new _CompilerOptions({
1285
+ paths,
1286
+ additionalData: this._additionalData
1287
+ });
1288
+ }
1289
+ format() {
1290
+ const json = {
1291
+ paths: this._paths,
1292
+ ...this._additionalData
1293
+ };
1294
+ const ordered = sortKeys2(json, compareKeys);
1295
+ return JSON.stringify(ordered, null, 2);
1296
+ }
1297
+ equals(other) {
1298
+ if (other instanceof _CompilerOptions) {
1299
+ return isEqual2(this._paths, other._paths) && isEqual2(this._additionalData, other._additionalData);
1300
+ } else {
1301
+ return false;
1302
+ }
1303
+ }
1304
+ };
1305
+ function compareKeys(a, b) {
1306
+ return getKeyIndex2(a) - getKeyIndex2(b);
1307
+ }
1308
+ function getKeyIndex2(s) {
1309
+ const order = [
1310
+ "target",
1311
+ "module",
1312
+ "moduleResolution",
1313
+ "esModuleInterop",
1314
+ "lib",
1315
+ "types",
1316
+ "strict",
1317
+ "allowJs"
1318
+ ];
1319
+ if (order.every((key) => key !== s)) {
1320
+ return Number.MAX_VALUE;
1321
+ } else {
1322
+ return order.indexOf(s);
1323
+ }
1324
+ }
1325
+
1326
+ // src/tsconfig/reference.ts
1327
+ var Reference = class _Reference {
1328
+ _path;
1329
+ constructor(path14) {
1330
+ this._path = path14;
1331
+ }
1332
+ get path() {
1333
+ return this._path;
1334
+ }
1335
+ equals(other) {
1336
+ if (other instanceof _Reference) {
1337
+ return this._path === other._path;
1338
+ } else {
1339
+ return false;
1340
+ }
1341
+ }
1342
+ };
1343
+
1344
+ // src/tsconfig/tsconfig.ts
1345
+ var TSConfig = class _TSConfig {
1346
+ _compilerOptions;
1347
+ _references;
1348
+ _additionalData;
1349
+ constructor(args) {
1350
+ this._compilerOptions = args?.compilerOptions ?? new CompilerOptions();
1351
+ this._references = args?.references ?? [];
1352
+ this._additionalData = args?.additionalData ?? {};
1353
+ }
1354
+ get compilerOptions() {
1355
+ return this._compilerOptions;
1356
+ }
1357
+ addReference(reference) {
1358
+ return new _TSConfig({
1359
+ compilerOptions: this._compilerOptions,
1360
+ references: [...this._references, reference],
1361
+ additionalData: this._additionalData
1362
+ });
1363
+ }
1364
+ setCompilerOptions(compilerOptions) {
1365
+ return new _TSConfig({
1366
+ compilerOptions,
1367
+ references: this._references,
1368
+ additionalData: this._additionalData
1369
+ });
1370
+ }
1371
+ static parse(s) {
1372
+ const json = JSON52.parse(s);
1373
+ const references = _TSConfig.parseReferences(json);
1374
+ const compilerOptions = new CompilerOptions({
1375
+ paths: json.compilerOptions?.paths,
1376
+ ...json.compilerOptions
1377
+ });
1378
+ const additionalData = { ...json };
1379
+ delete additionalData["compilerOptions"];
1380
+ delete additionalData["references"];
1381
+ return new _TSConfig({
1382
+ compilerOptions,
1383
+ references,
1384
+ additionalData
1385
+ });
1386
+ }
1387
+ static parseReferences(json) {
1388
+ if (typeof json === "object" && json !== null && "references" in json && json.references instanceof Array) {
1389
+ return json.references.map(
1390
+ (r) => new Reference(r.path)
1391
+ );
1392
+ } else {
1393
+ return [];
1394
+ }
1395
+ }
1396
+ format() {
1397
+ const compilerOptions = JSON52.parse(this.compilerOptions.format());
1398
+ const json = {
1399
+ compilerOptions,
1400
+ references: this._references.map((r) => ({ path: r.path })),
1401
+ ...this._additionalData
1402
+ };
1403
+ const ordered = sortKeys3(json, compareKeys2);
1404
+ return JSON.stringify(ordered, null, 2);
1405
+ }
1406
+ equals(other) {
1407
+ if (other instanceof _TSConfig) {
1408
+ const sameReferences = haveSameItems2(
1409
+ this._references,
1410
+ other._references,
1411
+ (r1, r2) => r1.equals(r2)
1412
+ );
1413
+ return this._compilerOptions.equals(other._compilerOptions) && sameReferences && isEqual3(this._additionalData, other._additionalData);
1414
+ } else {
1415
+ return false;
1416
+ }
1417
+ }
1418
+ };
1419
+ function compareKeys2(a, b) {
1420
+ return getKeyIndex3(a) - getKeyIndex3(b);
1421
+ }
1422
+ function getKeyIndex3(s) {
1423
+ const order = [
1424
+ "extends",
1425
+ "compilerOptions",
1426
+ "include",
1427
+ "exclude",
1428
+ "references"
1429
+ ];
1430
+ if (order.every((key) => key !== s)) {
1431
+ return Number.MAX_VALUE;
1432
+ } else {
1433
+ return order.indexOf(s);
1434
+ }
1435
+ }
1436
+
1437
+ // src/link-packages.ts
1438
+ async function linkPackages(current, target, development) {
1439
+ await updatePackageJSON(current, target, development);
1440
+ await updateTSConfig(current, target);
1441
+ }
1442
+ async function updatePackageJSON(current, target, development) {
1443
+ const namespace = await getNamespace();
1444
+ const packageJSON = await getDirectoryPackageJson(current.directory);
1445
+ const updated = addDependency(packageJSON, target, development);
1446
+ const packageJSONPath = getPackageJSONPath(current.directory);
1447
+ await fs4.writeFile(packageJSONPath, updated.format(namespace));
1448
+ }
1449
+ function addDependency(packageJSON, target, development) {
1450
+ const dependency = new Dependency(target.name, "workspace:*");
1451
+ if (development) {
1452
+ return packageJSON.addDevDependency(dependency);
1453
+ } else {
1454
+ return packageJSON.addDependency(dependency);
1455
+ }
1456
+ }
1457
+ async function updateTSConfig(current, target) {
1458
+ const tsconfigPath = path9.join(current.directory, "tsconfig.json");
1459
+ const tsconfigContents = await fs4.readFile(tsconfigPath, "utf8");
1460
+ const tsconfig = TSConfig.parse(tsconfigContents);
1461
+ const targetDirectory = path9.join(target.directory, "src", "index.ts");
1462
+ const updatedPaths = {
1463
+ ...tsconfig.compilerOptions.paths,
1464
+ [target.name]: [path9.relative(current.directory, targetDirectory)]
1465
+ };
1466
+ const updatedCompilerOptions = tsconfig.compilerOptions.setPaths(updatedPaths);
1467
+ const updated = tsconfig.setCompilerOptions(updatedCompilerOptions);
1468
+ await fs4.writeFile(tsconfigPath, updated.format());
1469
+ }
1470
+
1471
+ // src/packages/vite-react-app/create-vite-react-app.ts
1472
+ import path10 from "path";
1473
+
1474
+ // src/packages/vite-react-app/files/index-html-file-generator.ts
1475
+ var INDEX_HTML = `<!doctype html>
1476
+ <html lang="en">
1477
+ <head>
1478
+ <meta charset="UTF-8" />
1479
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1480
+ <title>Vite + React + Stack-Dev</title>
1481
+ </head>
1482
+ <body>
1483
+ <div id="root"></div>
1484
+ <script type="module" src="/src/main.tsx"></script>
1485
+ </body>
1486
+ </html>
1487
+ `;
1488
+ var INDEX_HTML_FILE_GENERATOR = new FileGeneratorImp(
1489
+ "index.html",
1490
+ INDEX_HTML
1491
+ );
1492
+
1493
+ // src/packages/vite-react-app/files/main-file-generator.ts
1494
+ var MAIN = `import React from 'react';
1495
+ import ReactDOM from 'react-dom/client';
1496
+ import { App } from './App';
1497
+
1498
+ ReactDOM.createRoot(document.getElementById('root')!).render(
1499
+ <React.StrictMode>
1500
+ <App />
1501
+ </React.StrictMode>,
1502
+ );
1503
+ `;
1504
+ var MAIN_FILE_GENERATOR = new FileGeneratorImp("src/main.tsx", MAIN);
1505
+
1506
+ // src/packages/vite-react-app/files/vite-config-file-generator.ts
1507
+ var VITE_CONFIG = `import { defineConfig } from 'vite';
1508
+ import react from '@vitejs/plugin-react';
1509
+
1510
+ export default defineConfig({
1511
+ plugins: [react()],
1512
+ server: {
1513
+ port: 3000,
1514
+ },
1515
+ });
1516
+ `;
1517
+ var VITE_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
1518
+ "vite.config.ts",
1519
+ VITE_CONFIG
1520
+ );
1521
+
1522
+ // src/packages/vite-react-app/files/app-file-generator.ts
1523
+ var APP = `import React, { useState } from 'react';
1524
+
1525
+ export function App() {
1526
+ const [count, setCount] = useState(0);
1527
+
1528
+ return (
1529
+ <div style={{
1530
+ padding: '2rem',
1531
+ fontFamily: 'system-ui, sans-serif',
1532
+ textAlign: 'center'
1533
+ }}>
1534
+ <h1>Stack-Dev App</h1>
1535
+ <div className="card">
1536
+ <button onClick={function() { setCount(count + 1) }}>
1537
+ Count is {count}
1538
+ </button>
1539
+ </div>
1540
+ <p>
1541
+ Edit <code>src/App.tsx</code> and save to test HMR
1542
+ </p>
1543
+ </div>
1544
+ );
1545
+ }
1546
+ `;
1547
+ var APP_FILE_GENERATOR = new FileGeneratorImp("src/App.tsx", APP);
1548
+
1549
+ // src/packages/vite-react-app/files/eslint-config-file-generator.ts
1550
+ var ESLINT_CONFIG4 = `import base from '@stack-dev/eslint-config/base.mjs';
1551
+
1552
+ export default [...base, { ignores: ['**/dist/**'] }];
1553
+ `;
1554
+ var ESLINT_CONFIG_FILE_GENERATOR4 = new FileGeneratorImp(
1555
+ "eslint.config.mjs",
1556
+ ESLINT_CONFIG4
1557
+ );
1558
+
1559
+ // src/packages/vite-react-app/files/prettier-config-file-generator.ts
1560
+ var PRETTIER_CONFIG4 = `import base from '@stack-dev/prettier-config/base.mjs';
1561
+
1562
+ export default base;
1563
+ `;
1564
+ var PRETTIER_CONFIG_FILE_GENERATOR4 = new FileGeneratorImp(
1565
+ "prettier.config.mjs",
1566
+ PRETTIER_CONFIG4
1567
+ );
1568
+
1569
+ // src/packages/vite-react-app/files/tsconfig-file-generator.ts
1570
+ var TSCONFIG4 = `{
1571
+ "extends": "@stack-dev/typescript-config/tsconfig.react.json",
1572
+ "compilerOptions": {
1573
+ "outDir": "dist"
1574
+ },
1575
+ "include": ["src"]
1576
+ }
1577
+ `;
1578
+ var TSCONFIG_FILE_GENERATOR4 = new FileGeneratorImp(
1579
+ "tsconfig.json",
1580
+ TSCONFIG4
1581
+ );
1582
+
1583
+ // src/packages/vite-react-app/files/vitest-config-file-generator.ts
1584
+ var VITEST_CONFIG4 = `import { defineConfig } from 'vitest/config';
1585
+
1586
+ export default defineConfig({
1587
+ test: {
1588
+ globals: true,
1589
+ coverage: {
1590
+ provider: 'v8',
1591
+ },
1592
+ environment: 'node',
1593
+ },
1594
+ });
1595
+ `;
1596
+ var VITEST_CONFIG_FILE_GENERATOR4 = new FileGeneratorImp(
1597
+ "vitest.config.ts",
1598
+ VITEST_CONFIG4
1599
+ );
1600
+
1601
+ // src/packages/vite-react-app/create-vite-react-app.ts
1602
+ async function createViteReactApp(name) {
1603
+ const rootDir = await getWorkspaceRoot();
1604
+ const directory = path10.join(rootDir, "apps", name);
1605
+ const namespace = await getNamespace(rootDir);
1606
+ const packageName = `${namespace}/${name}`;
1607
+ console.log(`\u{1F680} Creating Vite React App: ${packageName}`);
1608
+ const generator = new PackageGenerator(
1609
+ directory,
1610
+ makeAppPackageGenerator(packageName, namespace),
1611
+ [
1612
+ VITE_CONFIG_FILE_GENERATOR,
1613
+ INDEX_HTML_FILE_GENERATOR,
1614
+ MAIN_FILE_GENERATOR,
1615
+ APP_FILE_GENERATOR,
1616
+ TSCONFIG_FILE_GENERATOR4,
1617
+ PRETTIER_CONFIG_FILE_GENERATOR4,
1618
+ ESLINT_CONFIG_FILE_GENERATOR4,
1619
+ VITEST_CONFIG_FILE_GENERATOR4
1620
+ ]
1621
+ );
1622
+ await generator.generate();
1623
+ }
1624
+ function makeAppPackageGenerator(packageName, namespace) {
1625
+ const packageJsonModel = new PackageJSON({
1626
+ name: packageName,
1627
+ dependencies: [
1628
+ new Dependency("react", "^18.3.1"),
1629
+ new Dependency("react-dom", "^18.3.1")
1630
+ ],
1631
+ devDependencies: [
1632
+ new Dependency(`${namespace}/eslint-config`, "workspace:*"),
1633
+ new Dependency(`${namespace}/prettier-config`, "workspace:*"),
1634
+ new Dependency(`${namespace}/typescript-config`, "workspace:*"),
1635
+ new Dependency("@types/react", "^18.3.1"),
1636
+ new Dependency("@types/react-dom", "^18.3.1"),
1637
+ new Dependency("@vitejs/plugin-react", "^4.3.1"),
1638
+ new Dependency("vite", "^5.4.2"),
1639
+ new Dependency("typescript", "^5.5.4"),
1640
+ new Dependency("eslint", "^9.32.0"),
1641
+ new Dependency("prettier", "^3.6.2")
1642
+ ],
1643
+ additionalData: {
1644
+ version: "0.1.0",
1645
+ private: true,
1646
+ type: "module",
1647
+ scripts: {
1648
+ dev: "vite",
1649
+ build: "tsc && vite build",
1650
+ preview: "vite preview",
1651
+ start: "pnpm run preview",
1652
+ lint: "eslint .",
1653
+ format: "prettier . --write"
1654
+ }
1655
+ }
1656
+ });
1657
+ return new PackageJsonGenerator(packageJsonModel, namespace);
1658
+ }
1659
+
1660
+ // src/unlink-packages.ts
1661
+ import fs5 from "node:fs/promises";
1662
+ import path11 from "node:path";
1663
+ async function unlinkPackages(current, target) {
1664
+ await updatePackageJSON2(current, target);
1665
+ await updateTSConfig2(current, target);
1666
+ }
1667
+ async function updatePackageJSON2(current, target) {
1668
+ const namespace = await getNamespace();
1669
+ const packageJSON = await getDirectoryPackageJson(current.directory);
1670
+ const updated = packageJSON.removeDependency(target.name).removeDevDependency(target.name);
1671
+ const packageJSONPath = getPackageJSONPath(current.directory);
1672
+ await fs5.writeFile(packageJSONPath, updated.format(namespace));
1673
+ }
1674
+ async function updateTSConfig2(current, target) {
1675
+ const tsconfigPath = path11.join(current.directory, "tsconfig.json");
1676
+ const tsconfigContents = await fs5.readFile(tsconfigPath, "utf8");
1677
+ const tsconfig = TSConfig.parse(tsconfigContents);
1678
+ const updatedPaths = Object.fromEntries(
1679
+ Object.entries(tsconfig.compilerOptions.paths).filter(
1680
+ ([key]) => key !== target.name
1681
+ )
1682
+ );
1683
+ const updatedCompilerOptions = tsconfig.compilerOptions.setPaths(updatedPaths);
1684
+ const updated = tsconfig.setCompilerOptions(updatedCompilerOptions);
1685
+ await fs5.writeFile(tsconfigPath, updated.format());
1686
+ }
1687
+
1688
+ // src/workspace/create-workspace.ts
1689
+ import fs6 from "fs/promises";
1690
+ import path13 from "path";
1691
+
1692
+ // src/workspace/root-package.ts
1693
+ async function makeRootPackage(directory, name) {
1694
+ const packageJsonModel = new PackageJSON({
1695
+ name,
1696
+ devDependencies: [new Dependency("turbo", "^2.5.4")],
1697
+ additionalData: {
1698
+ description: "",
1699
+ keywords: [],
1700
+ author: "",
1701
+ license: "ISC",
1702
+ packageManager: "pnpm@10.13.1"
1703
+ }
1704
+ });
1705
+ const PNPM_WORKSPACE = new FileGeneratorImp(
1706
+ "pnpm-workspace.yaml",
1707
+ ["packages:", " - apps/*", " - packages/*", " - configs/*"].join("\n")
1708
+ );
1709
+ const GITIGNORE = new FileGeneratorImp(".gitignore", GITIGNORE_CONTENT);
1710
+ const TURBO_JSON = new FileGeneratorImp(
1711
+ "turbo.json",
1712
+ JSON.stringify(
1713
+ {
1714
+ tasks: {
1715
+ build: {
1716
+ dependsOn: ["^build"],
1717
+ outputs: ["dist/**"]
1718
+ },
1719
+ lint: {},
1720
+ test: {}
1721
+ }
1722
+ },
1723
+ null,
1724
+ 2
1725
+ )
1726
+ );
1727
+ return new PackageGenerator(
1728
+ directory,
1729
+ new PackageJsonGenerator(packageJsonModel, ""),
1730
+ [PNPM_WORKSPACE, GITIGNORE, TURBO_JSON]
1731
+ );
1732
+ }
1733
+ var GITIGNORE_CONTENT = `# Logs
1734
+ logs
1735
+ *.log
1736
+ npm-debug.log*
1737
+ yarn-debug.log*
1738
+ yarn-error.log*
1739
+ lerna-debug.log*
1740
+
1741
+ # Diagnostic reports (https://nodejs.org/api/report.html)
1742
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
1743
+
1744
+ # Runtime data
1745
+ pids
1746
+ *.pid
1747
+ *.seed
1748
+ *.pid.lock
1749
+
1750
+ # Directory for instrumented libs generated by jscoverage/JSCover
1751
+ lib-cov
1752
+
1753
+ # Coverage directory used by tools like istanbul
1754
+ coverage
1755
+ *.lcov
1756
+
1757
+ # nyc test coverage
1758
+ .nyc_output
1759
+
1760
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
1761
+ .grunt
1762
+
1763
+ # Bower dependency directory (https://bower.io/)
1764
+ bower_components
1765
+
1766
+ # node-waf configuration
1767
+ .lock-wscript
1768
+
1769
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
1770
+ build/Release
1771
+
1772
+ # Dependency directories
1773
+ node_modules/
1774
+ jspm_packages/
1775
+
1776
+ # Snowpack dependency directory (https://snowpack.dev/)
1777
+ web_modules/
1778
+
1779
+ # TypeScript cache
1780
+ *.tsbuildinfo
1781
+
1782
+ # Optional npm cache directory
1783
+ .npm
1784
+
1785
+ # Optional eslint cache
1786
+ .eslintcache
1787
+
1788
+ # Optional stylelint cache
1789
+ .stylelintcache
1790
+
1791
+ # Optional REPL history
1792
+ .node_repl_history
1793
+
1794
+ # Output of 'npm pack'
1795
+ *.tgz
1796
+
1797
+ # Yarn Integrity file
1798
+ .yarn-integrity
1799
+
1800
+ # dotenv environment variable files
1801
+ .env
1802
+ .env.*
1803
+ !.env.example
1804
+
1805
+ # parcel-bundler cache (https://parceljs.org/)
1806
+ .cache
1807
+ .parcel-cache
1808
+
1809
+ # Next.js build output
1810
+ .next
1811
+ out
1812
+
1813
+ # Nuxt.js build / generate output
1814
+ .nuxt
1815
+ dist
1816
+
1817
+ # Gatsby files
1818
+ .cache/
1819
+ # Comment in the public line in if your project uses Gatsby and not Next.js
1820
+ # https://nextjs.org/blog/next-9-1#public-directory-support
1821
+ # public
1822
+
1823
+ # vuepress build output
1824
+ .vuepress/dist
1825
+
1826
+ # vuepress v2.x temp and cache directory
1827
+ .temp
1828
+ .cache
1829
+
1830
+ # Sveltekit cache directory
1831
+ .svelte-kit/
1832
+
1833
+ # vitepress build output
1834
+ **/.vitepress/dist
1835
+
1836
+ # vitepress cache directory
1837
+ **/.vitepress/cache
1838
+
1839
+ # Docusaurus cache and generated files
1840
+ .docusaurus
1841
+
1842
+ # Serverless directories
1843
+ .serverless/
1844
+
1845
+ # FuseBox cache
1846
+ .fusebox/
1847
+
1848
+ # DynamoDB Local files
1849
+ .dynamodb/
1850
+
1851
+ # Firebase cache directory
1852
+ .firebase/
1853
+
1854
+ # TernJS port file
1855
+ .tern-port
1856
+
1857
+ # Stores VSCode versions used for testing VSCode extensions
1858
+ .vscode-test
1859
+
1860
+ # yarn v3
1861
+ .pnp.*
1862
+ .yarn/*
1863
+ !.yarn/patches
1864
+ !.yarn/plugins
1865
+ !.yarn/releases
1866
+ !.yarn/sdks
1867
+ !.yarn/versions
1868
+
1869
+ # Vite logs files
1870
+ vite.config.js.timestamp-*
1871
+ vite.config.ts.timestamp-*
1872
+
1873
+ .turbo
1874
+ `;
1875
+
1876
+ // src/workspace/typescript-config.ts
1877
+ import path12 from "path";
1878
+ async function makeTypescriptConfig(directory, namespace) {
1879
+ const packageJsonModel = new PackageJSON({
1880
+ name: `${namespace}/typescript-config`,
1881
+ devDependencies: [new Dependency("typescript", "^5.8.3")],
1882
+ additionalData: {
1883
+ version: "0.1.0",
1884
+ private: true,
1885
+ files: ["*.json"]
1886
+ }
1887
+ });
1888
+ const fullPath = path12.join(directory, "configs/typescript-config");
1889
+ return new PackageGenerator(
1890
+ fullPath,
1891
+ new PackageJsonGenerator(packageJsonModel, namespace),
1892
+ [BASE, REACT, NODE]
1893
+ );
1894
+ }
1895
+ var BASE = new FileGeneratorImp(
1896
+ "tsconfig.base.json",
1897
+ JSON.stringify(
1898
+ {
1899
+ compilerOptions: {
1900
+ target: "ES2022",
1901
+ module: "ESNext",
1902
+ moduleResolution: "bundler",
1903
+ resolveJsonModule: true,
1904
+ isolatedModules: true,
1905
+ strict: true,
1906
+ skipLibCheck: true,
1907
+ forceConsistentCasingInFileNames: true,
1908
+ esModuleInterop: true,
1909
+ allowSyntheticDefaultImports: true,
1910
+ noEmit: true,
1911
+ types: []
1912
+ }
1913
+ },
1914
+ null,
1915
+ 2
1916
+ )
1917
+ );
1918
+ var REACT = new FileGeneratorImp(
1919
+ "tsconfig.react.json",
1920
+ JSON.stringify(
1921
+ {
1922
+ extends: "./tsconfig.base.json",
1923
+ compilerOptions: {
1924
+ jsx: "react-jsx",
1925
+ lib: ["DOM", "DOM.Iterable", "ES2022"],
1926
+ types: []
1927
+ }
1928
+ },
1929
+ null,
1930
+ 2
1931
+ )
1932
+ );
1933
+ var NODE = new FileGeneratorImp(
1934
+ "tsconfig.node.json",
1935
+ JSON.stringify(
1936
+ {
1937
+ extends: "./tsconfig.base.json",
1938
+ compilerOptions: {
1939
+ lib: ["ES2022"],
1940
+ types: ["node"]
1941
+ }
1942
+ },
1943
+ null,
1944
+ 2
1945
+ )
1946
+ );
1947
+
1948
+ // src/workspace/create-workspace.ts
1949
+ async function createWorkspace(name, directory) {
1950
+ await validateNotInWorkspace(directory);
1951
+ console.log(`\u2728 Creating workspace: @${name}`);
1952
+ const fullPath = path13.join(directory, name);
1953
+ await fs6.mkdir(fullPath, { recursive: true });
1954
+ const namespace = `@${name}`;
1955
+ const PACKAGES = [
1956
+ await makeRootPackage(fullPath, name),
1957
+ await makeTypescriptConfig(fullPath, namespace)
1958
+ ];
1959
+ await Promise.all(PACKAGES.map((p) => p.generate()));
1960
+ await fs6.mkdir(path13.join(fullPath, "apps"));
1961
+ await fs6.mkdir(path13.join(fullPath, "configs"));
1962
+ await fs6.mkdir(path13.join(fullPath, "packages"));
1963
+ console.log(`\u2705 Workspace created at: ${fullPath}`);
1964
+ console.log("");
1965
+ console.log(`Run "cd ${fullPath}" followed by "pnpm install"`);
1966
+ }
1967
+ async function validateNotInWorkspace(directory) {
1968
+ const namespace = await getNamespace(directory);
1969
+ if (namespace !== void 0) {
1970
+ throw new Error(`Currently in workspace "${namespace}".`);
1971
+ }
1972
+ }
1973
+
1974
+ // src/index.ts
1975
+ var program = new Command();
1976
+ program.name("stack").description("Opinionated TypeScript workspace manager").version("0.1.0");
1977
+ program.command("create <name>").description("Create a new workspace").option(
1978
+ "-o, --output <dir>",
1979
+ "Target directory to create the workspace in",
1980
+ "."
1981
+ ).action(async (name, options) => {
1982
+ const output = options.output ?? process.cwd();
1983
+ await createWorkspace(name, output);
1984
+ });
1985
+ program.command("g <name>").description("Generate a new package or app").option(
1986
+ "-t, --type <type>",
1987
+ `Type of package to generate (${packageTypes.join(", ")})`
1988
+ ).option("--style <style>", `Styling system to use (${styleTypes.join(", ")})`).action(async (name, options) => {
1989
+ const type = await pickPackageType(options);
1990
+ switch (type) {
1991
+ case "library":
1992
+ await createLibraryPackage(name);
1993
+ break;
1994
+ case "config":
1995
+ await createConfigPackage(name);
1996
+ break;
1997
+ case "react":
1998
+ await createReactPackage(name, await pickStyleType(options));
1999
+ break;
2000
+ case "vite":
2001
+ await createViteReactApp(name);
2002
+ break;
2003
+ case "cli":
2004
+ break;
2005
+ case "fastify":
2006
+ break;
2007
+ case "next":
2008
+ break;
2009
+ }
2010
+ console.log("");
2011
+ console.log("Run pnpm install to finish linking.");
2012
+ });
2013
+ program.command("link [name]").alias("l").option("-D, --dev", "Whether to link as a devDependency.", false).description("Link to the specified package").action(async (name, options) => {
2014
+ name = name ?? await promptForPackageToLinkTo();
2015
+ const development = options.dev ?? false;
2016
+ if (!isValidPackageName(name)) {
2017
+ throw new Error(`Package name "${name}" is not a valid option.`);
2018
+ }
2019
+ const current = await getCurrentPackage();
2020
+ const target = await getPackageByName(name);
2021
+ await linkPackages(current, target, development);
2022
+ console.log("");
2023
+ console.log("Run pnpm install to finish linking.");
2024
+ });
2025
+ program.command("unlink [name]").alias("u").description("Unlink the specified package").action(async (name) => {
2026
+ name = name ?? await promptForPackageToUnlinkFrom();
2027
+ if (!await isValidPackageName(name)) {
2028
+ throw new Error(`Package name "${name}" is not a valid option.`);
2029
+ }
2030
+ const current = await getCurrentPackage();
2031
+ const target = await getPackageByName(name);
2032
+ await unlinkPackages(current, target);
2033
+ });
2034
+ async function promptForPackageToLinkTo() {
2035
+ const options = await getAllPackages();
2036
+ const currentPackage = await getCurrentPackage();
2037
+ const linked = await getLinkedPackageNames();
2038
+ const validOptions = options.filter((o) => o.name !== currentPackage.name).filter((o) => !linked.has(o.name)).toSorted(comparePackages);
2039
+ const response = await prompt3({
2040
+ type: "select",
2041
+ name: "packageName",
2042
+ message: "What package do you want to link to?",
2043
+ choices: [...validOptions]
2044
+ });
2045
+ return response.packageName;
2046
+ }
2047
+ async function promptForPackageToUnlinkFrom() {
2048
+ const validOptions = await getLinkedPackageNames();
2049
+ const response = await prompt3({
2050
+ type: "select",
2051
+ name: "packageName",
2052
+ message: "What package do you want to unlink from?",
2053
+ choices: [...validOptions]
2054
+ });
2055
+ return response.packageName;
2056
+ }
2057
+ async function getLinkedPackageNames() {
2058
+ const currentPackage = await getCurrentPackage();
2059
+ const packageJSON = await getDirectoryPackageJson(currentPackage.directory);
2060
+ const namespace = await getNamespace();
2061
+ const names = [...packageJSON.dependencies, ...packageJSON.devDependencies].map((d) => d.name).filter((n) => n.startsWith(namespace));
2062
+ return new Set(names);
2063
+ }
2064
+ program.parse();
2065
+ async function isValidPackageName(packageName) {
2066
+ const options = await getValidPackageNames();
2067
+ return options.some((o) => o === packageName);
2068
+ }
2069
+ async function getValidPackageNames() {
2070
+ const options = await getAllPackages();
2071
+ const currentPackage = await getCurrentPackage();
2072
+ return options.filter((o) => o.name !== currentPackage.name).map((o) => o.name);
2073
+ }