stack-dev 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 (204) hide show
  1. package/.idea/modules.xml +8 -0
  2. package/.idea/prettier.xml +7 -0
  3. package/.idea/stack-dev.iml +12 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/apps/cli/eslint.config.mjs +3 -0
  6. package/apps/cli/node_modules/.bin/acorn +21 -0
  7. package/apps/cli/node_modules/.bin/esbuild +21 -0
  8. package/apps/cli/node_modules/.bin/eslint +21 -0
  9. package/apps/cli/node_modules/.bin/glob +21 -0
  10. package/apps/cli/node_modules/.bin/json5 +21 -0
  11. package/apps/cli/node_modules/.bin/prettier +21 -0
  12. package/apps/cli/node_modules/.bin/stack-prettier +21 -0
  13. package/apps/cli/node_modules/.bin/tsc +21 -0
  14. package/apps/cli/node_modules/.bin/tsserver +21 -0
  15. package/apps/cli/node_modules/.bin/tsup +21 -0
  16. package/apps/cli/node_modules/.bin/tsup-node +21 -0
  17. package/apps/cli/node_modules/.bin/yaml +21 -0
  18. package/apps/cli/package.json +35 -0
  19. package/apps/cli/prettier.config.mjs +3 -0
  20. package/apps/cli/src/file-generator/file-generator-imp.ts +20 -0
  21. package/apps/cli/src/file-generator/file-generator.ts +5 -0
  22. package/apps/cli/src/file-generator/index.ts +3 -0
  23. package/apps/cli/src/file-generator/package-json-generator.ts +23 -0
  24. package/apps/cli/src/index.ts +185 -0
  25. package/apps/cli/src/link-packages.ts +65 -0
  26. package/apps/cli/src/package-json/dependency.ts +28 -0
  27. package/apps/cli/src/package-json/index.ts +3 -0
  28. package/apps/cli/src/package-json/package-json.ts +269 -0
  29. package/apps/cli/src/packages/create-config-package.ts +29 -0
  30. package/apps/cli/src/packages/index.ts +4 -0
  31. package/apps/cli/src/packages/library-package/create-library-package.ts +85 -0
  32. package/apps/cli/src/packages/library-package/files/add-file-generator.ts +8 -0
  33. package/apps/cli/src/packages/library-package/files/add-spec-file-generator.ts +17 -0
  34. package/apps/cli/src/packages/library-package/files/eslint-config-file-generator.ts +11 -0
  35. package/apps/cli/src/packages/library-package/files/index-file-generator.ts +9 -0
  36. package/apps/cli/src/packages/library-package/files/prettier-config-file-generator.ts +11 -0
  37. package/apps/cli/src/packages/library-package/files/tsconfig-file-generator.ts +15 -0
  38. package/apps/cli/src/packages/library-package/files/tsup-config-file-generator.ts +23 -0
  39. package/apps/cli/src/packages/library-package/files/vitest-config-file-generator.ts +19 -0
  40. package/apps/cli/src/packages/library-package/index.ts +1 -0
  41. package/apps/cli/src/packages/react-package/create-react-package.ts +25 -0
  42. package/apps/cli/src/packages/react-package/create-tailwind-react-package.ts +30 -0
  43. package/apps/cli/src/packages/react-package/create-unstyled-react-package.ts +3 -0
  44. package/apps/cli/src/packages/react-package/css-react-package/create-css-react-package.ts +103 -0
  45. package/apps/cli/src/packages/react-package/css-react-package/files/button-css-module-file-generator.ts +16 -0
  46. package/apps/cli/src/packages/react-package/css-react-package/files/button-file-generator.ts +14 -0
  47. package/apps/cli/src/packages/react-package/css-react-package/files/button-spec-file-generator.ts +33 -0
  48. package/apps/cli/src/packages/react-package/css-react-package/files/eslint-config-file-generator.ts +18 -0
  49. package/apps/cli/src/packages/react-package/css-react-package/files/index-file-generator.ts +9 -0
  50. package/apps/cli/src/packages/react-package/css-react-package/files/prettier-config-file-generator.ts +11 -0
  51. package/apps/cli/src/packages/react-package/css-react-package/files/tsconfig-file-generator.ts +15 -0
  52. package/apps/cli/src/packages/react-package/css-react-package/files/tsup-config-file-generator.ts +24 -0
  53. package/apps/cli/src/packages/react-package/css-react-package/files/vitest-config-file-generator.ts +23 -0
  54. package/apps/cli/src/packages/react-package/index.ts +1 -0
  55. package/apps/cli/src/packages/react-package/styled-components-react-package/create-styled-components-react-package.ts +112 -0
  56. package/apps/cli/src/packages/react-package/styled-components-react-package/files/button-file-generator.ts +30 -0
  57. package/apps/cli/src/packages/react-package/styled-components-react-package/files/button-spec-file-generator.ts +33 -0
  58. package/apps/cli/src/packages/react-package/styled-components-react-package/files/eslint-config-file-generator.ts +18 -0
  59. package/apps/cli/src/packages/react-package/styled-components-react-package/files/index-file-generator.ts +9 -0
  60. package/apps/cli/src/packages/react-package/styled-components-react-package/files/prettier-config-file-generator.ts +11 -0
  61. package/apps/cli/src/packages/react-package/styled-components-react-package/files/tsconfig-file-generator.ts +15 -0
  62. package/apps/cli/src/packages/react-package/styled-components-react-package/files/tsup-config-file-generator.ts +21 -0
  63. package/apps/cli/src/packages/react-package/styled-components-react-package/files/vitest-config-file-generator.ts +23 -0
  64. package/apps/cli/src/packages/vite-react-app/create-vite-react-app.ts +79 -0
  65. package/apps/cli/src/packages/vite-react-app/files/app-file-generator.ts +28 -0
  66. package/apps/cli/src/packages/vite-react-app/files/eslint-config-file-generator.ts +11 -0
  67. package/apps/cli/src/packages/vite-react-app/files/index-html-file-generator.ts +20 -0
  68. package/apps/cli/src/packages/vite-react-app/files/main-file-generator.ts +14 -0
  69. package/apps/cli/src/packages/vite-react-app/files/prettier-config-file-generator.ts +11 -0
  70. package/apps/cli/src/packages/vite-react-app/files/tsconfig-file-generator.ts +15 -0
  71. package/apps/cli/src/packages/vite-react-app/files/vite-config-file-generator.ts +17 -0
  72. package/apps/cli/src/packages/vite-react-app/files/vitest-config-file-generator.ts +19 -0
  73. package/apps/cli/src/tsconfig/compiler-options.ts +83 -0
  74. package/apps/cli/src/tsconfig/index.ts +4 -0
  75. package/apps/cli/src/tsconfig/reference.ts +21 -0
  76. package/apps/cli/src/tsconfig/tsconfig.ts +137 -0
  77. package/apps/cli/src/unlink-packages.ts +47 -0
  78. package/apps/cli/src/utils/package-generator.ts +41 -0
  79. package/apps/cli/src/utils/package-type.ts +44 -0
  80. package/apps/cli/src/utils/package.ts +126 -0
  81. package/apps/cli/src/utils/style-type.ts +41 -0
  82. package/apps/cli/src/utils/utils.ts +28 -0
  83. package/apps/cli/src/utils/workspace.ts +78 -0
  84. package/apps/cli/src/workspace/create-workspace.ts +39 -0
  85. package/apps/cli/src/workspace/index.ts +1 -0
  86. package/apps/cli/src/workspace/root-package.ts +195 -0
  87. package/apps/cli/src/workspace/typescript-config.ts +84 -0
  88. package/apps/cli/tsconfig.json +14 -0
  89. package/apps/cli/tsup.config.ts +16 -0
  90. package/apps/demo/eslint.config.mjs +3 -0
  91. package/apps/demo/index.html +12 -0
  92. package/apps/demo/node_modules/.bin/browserslist +21 -0
  93. package/apps/demo/node_modules/.bin/eslint +21 -0
  94. package/apps/demo/node_modules/.bin/prettier +21 -0
  95. package/apps/demo/node_modules/.bin/tsc +21 -0
  96. package/apps/demo/node_modules/.bin/tsserver +21 -0
  97. package/apps/demo/node_modules/.bin/vite +21 -0
  98. package/apps/demo/node_modules/.vite/deps/_metadata.json +52 -0
  99. package/apps/demo/node_modules/.vite/deps/chunk-IGB2AZES.js +1930 -0
  100. package/apps/demo/node_modules/.vite/deps/chunk-IGB2AZES.js.map +7 -0
  101. package/apps/demo/node_modules/.vite/deps/chunk-NEPDRNXC.js +21626 -0
  102. package/apps/demo/node_modules/.vite/deps/chunk-NEPDRNXC.js.map +7 -0
  103. package/apps/demo/node_modules/.vite/deps/package.json +3 -0
  104. package/apps/demo/node_modules/.vite/deps/react-dom.js +6 -0
  105. package/apps/demo/node_modules/.vite/deps/react-dom.js.map +7 -0
  106. package/apps/demo/node_modules/.vite/deps/react-dom_client.js +38 -0
  107. package/apps/demo/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  108. package/apps/demo/node_modules/.vite/deps/react.js +5 -0
  109. package/apps/demo/node_modules/.vite/deps/react.js.map +7 -0
  110. package/apps/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +911 -0
  111. package/apps/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  112. package/apps/demo/node_modules/.vite/deps/react_jsx-runtime.js +923 -0
  113. package/apps/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  114. package/apps/demo/node_modules/.vite/deps/styled-components.js +1366 -0
  115. package/apps/demo/node_modules/.vite/deps/styled-components.js.map +7 -0
  116. package/apps/demo/package.json +32 -0
  117. package/apps/demo/prettier.config.mjs +3 -0
  118. package/apps/demo/src/App.tsx +50 -0
  119. package/apps/demo/src/main.tsx +9 -0
  120. package/apps/demo/tsconfig.json +17 -0
  121. package/apps/demo/vite.config.ts +9 -0
  122. package/apps/demo/vitest.config.ts +11 -0
  123. package/configs/eslint-config/base.mjs +15 -0
  124. package/configs/eslint-config/node_modules/.bin/acorn +21 -0
  125. package/configs/eslint-config/node_modules/.bin/eslint +21 -0
  126. package/configs/eslint-config/node_modules/.bin/tsc +21 -0
  127. package/configs/eslint-config/node_modules/.bin/tsserver +21 -0
  128. package/configs/eslint-config/package.json +12 -0
  129. package/configs/eslint-config/react.mjs +17 -0
  130. package/configs/prettier-config/base.mjs +11 -0
  131. package/configs/prettier-config/node_modules/.bin/prettier +21 -0
  132. package/configs/prettier-config/node_modules/.bin/tsc +21 -0
  133. package/configs/prettier-config/node_modules/.bin/tsserver +21 -0
  134. package/configs/prettier-config/package.json +9 -0
  135. package/configs/typescript-config/global.d.ts +12 -0
  136. package/configs/typescript-config/node_modules/.bin/tsc +21 -0
  137. package/configs/typescript-config/node_modules/.bin/tsserver +21 -0
  138. package/configs/typescript-config/package.json +7 -0
  139. package/configs/typescript-config/tsconfig.base.json +19 -0
  140. package/configs/typescript-config/tsconfig.node.json +6 -0
  141. package/configs/typescript-config/tsconfig.react.json +8 -0
  142. package/package.json +16 -0
  143. package/packages/core/eslint.config.mjs +3 -0
  144. package/packages/core/node_modules/.bin/eslint +21 -0
  145. package/packages/core/node_modules/.bin/prettier +21 -0
  146. package/packages/core/node_modules/.bin/tsc +21 -0
  147. package/packages/core/node_modules/.bin/tsserver +21 -0
  148. package/packages/core/node_modules/.bin/tsup +21 -0
  149. package/packages/core/node_modules/.bin/tsup-node +21 -0
  150. package/packages/core/node_modules/.bin/vite +21 -0
  151. package/packages/core/node_modules/.bin/vitest +21 -0
  152. package/packages/core/node_modules/.bin/yaml +21 -0
  153. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  154. package/packages/core/package.json +35 -0
  155. package/packages/core/prettier.config.mjs +3 -0
  156. package/packages/core/src/are-objects-equal.ts +26 -0
  157. package/packages/core/src/have-same-items.ts +84 -0
  158. package/packages/core/src/index.ts +6 -0
  159. package/packages/core/src/snapshot.ts +11 -0
  160. package/packages/core/src/sort-keys.ts +7 -0
  161. package/packages/core/src/spec/are-objects-equal.spec.ts +30 -0
  162. package/packages/core/src/spec/have-same-items.spec.ts +29 -0
  163. package/packages/core/src/spec/sort-keys.spec.ts +57 -0
  164. package/packages/core/tsconfig.json +7 -0
  165. package/packages/core/tsup.config.ts +15 -0
  166. package/packages/core/vitest.config.mts +11 -0
  167. package/packages/react-css/eslint.config.mjs +10 -0
  168. package/packages/react-css/node_modules/.bin/esbuild +21 -0
  169. package/packages/react-css/node_modules/.bin/eslint +21 -0
  170. package/packages/react-css/node_modules/.bin/prettier +21 -0
  171. package/packages/react-css/node_modules/.bin/tsc +21 -0
  172. package/packages/react-css/node_modules/.bin/tsserver +21 -0
  173. package/packages/react-css/node_modules/.bin/tsup +21 -0
  174. package/packages/react-css/node_modules/.bin/tsup-node +21 -0
  175. package/packages/react-css/node_modules/.bin/vitest +21 -0
  176. package/packages/react-css/node_modules/.bin/yaml +21 -0
  177. package/packages/react-css/package.json +50 -0
  178. package/packages/react-css/prettier.config.mjs +3 -0
  179. package/packages/react-css/src/button.module.css +8 -0
  180. package/packages/react-css/src/button.spec.tsx +25 -0
  181. package/packages/react-css/src/button.tsx +11 -0
  182. package/packages/react-css/src/index.ts +1 -0
  183. package/packages/react-css/tsconfig.json +7 -0
  184. package/packages/react-css/tsup.config.ts +17 -0
  185. package/packages/react-css/vitest.config.ts +15 -0
  186. package/packages/react-styled-components/eslint.config.mjs +10 -0
  187. package/packages/react-styled-components/node_modules/.bin/eslint +21 -0
  188. package/packages/react-styled-components/node_modules/.bin/prettier +21 -0
  189. package/packages/react-styled-components/node_modules/.bin/tsc +21 -0
  190. package/packages/react-styled-components/node_modules/.bin/tsserver +21 -0
  191. package/packages/react-styled-components/node_modules/.bin/tsup +21 -0
  192. package/packages/react-styled-components/node_modules/.bin/tsup-node +21 -0
  193. package/packages/react-styled-components/node_modules/.bin/vitest +21 -0
  194. package/packages/react-styled-components/node_modules/.bin/yaml +21 -0
  195. package/packages/react-styled-components/package.json +51 -0
  196. package/packages/react-styled-components/prettier.config.mjs +3 -0
  197. package/packages/react-styled-components/src/button.spec.tsx +27 -0
  198. package/packages/react-styled-components/src/button.tsx +21 -0
  199. package/packages/react-styled-components/src/index.ts +1 -0
  200. package/packages/react-styled-components/tsconfig.json +7 -0
  201. package/packages/react-styled-components/tsup.config.ts +16 -0
  202. package/packages/react-styled-components/vitest.config.ts +15 -0
  203. package/pnpm-workspace.yaml +4 -0
  204. package/turbo.json +13 -0
@@ -0,0 +1,28 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const APP = `import React, { useState } from 'react';
4
+
5
+ export function App() {
6
+ const [count, setCount] = useState(0);
7
+
8
+ return (
9
+ <div style={{
10
+ padding: '2rem',
11
+ fontFamily: 'system-ui, sans-serif',
12
+ textAlign: 'center'
13
+ }}>
14
+ <h1>Stack-Dev App</h1>
15
+ <div className="card">
16
+ <button onClick={function() { setCount(count + 1) }}>
17
+ Count is {count}
18
+ </button>
19
+ </div>
20
+ <p>
21
+ Edit <code>src/App.tsx</code> and save to test HMR
22
+ </p>
23
+ </div>
24
+ );
25
+ }
26
+ `;
27
+
28
+ export const APP_FILE_GENERATOR = new FileGeneratorImp('src/App.tsx', APP);
@@ -0,0 +1,11 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const ESLINT_CONFIG = `import base from '@stack-dev/eslint-config/base.mjs';
4
+
5
+ export default [...base, { ignores: ['**/dist/**'] }];
6
+ `;
7
+
8
+ export const ESLINT_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
9
+ 'eslint.config.mjs',
10
+ ESLINT_CONFIG,
11
+ );
@@ -0,0 +1,20 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const INDEX_HTML = `<!doctype html>
4
+ <html lang="en">
5
+ <head>
6
+ <meta charset="UTF-8" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>Vite + React + Stack-Dev</title>
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ <script type="module" src="/src/main.tsx"></script>
13
+ </body>
14
+ </html>
15
+ `;
16
+
17
+ export const INDEX_HTML_FILE_GENERATOR = new FileGeneratorImp(
18
+ 'index.html',
19
+ INDEX_HTML,
20
+ );
@@ -0,0 +1,14 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const MAIN = `import React from 'react';
4
+ import ReactDOM from 'react-dom/client';
5
+ import { App } from './App';
6
+
7
+ ReactDOM.createRoot(document.getElementById('root')!).render(
8
+ <React.StrictMode>
9
+ <App />
10
+ </React.StrictMode>,
11
+ );
12
+ `;
13
+
14
+ export const MAIN_FILE_GENERATOR = new FileGeneratorImp('src/main.tsx', MAIN);
@@ -0,0 +1,11 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const PRETTIER_CONFIG = `import base from '@stack-dev/prettier-config/base.mjs';
4
+
5
+ export default base;
6
+ `;
7
+
8
+ export const PRETTIER_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
9
+ 'prettier.config.mjs',
10
+ PRETTIER_CONFIG,
11
+ );
@@ -0,0 +1,15 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const TSCONFIG = `{
4
+ "extends": "@stack-dev/typescript-config/tsconfig.react.json",
5
+ "compilerOptions": {
6
+ "outDir": "dist"
7
+ },
8
+ "include": ["src"]
9
+ }
10
+ `;
11
+
12
+ export const TSCONFIG_FILE_GENERATOR = new FileGeneratorImp(
13
+ 'tsconfig.json',
14
+ TSCONFIG,
15
+ );
@@ -0,0 +1,17 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const VITE_CONFIG = `import { defineConfig } from 'vite';
4
+ import react from '@vitejs/plugin-react';
5
+
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ server: {
9
+ port: 3000,
10
+ },
11
+ });
12
+ `;
13
+
14
+ export const VITE_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
15
+ 'vite.config.ts',
16
+ VITE_CONFIG,
17
+ );
@@ -0,0 +1,19 @@
1
+ import { FileGeneratorImp } from '../../../file-generator/file-generator-imp';
2
+
3
+ const VITEST_CONFIG = `import { defineConfig } from 'vitest/config';
4
+
5
+ export default defineConfig({
6
+ test: {
7
+ globals: true,
8
+ coverage: {
9
+ provider: 'v8',
10
+ },
11
+ environment: 'node',
12
+ },
13
+ });
14
+ `;
15
+
16
+ export const VITEST_CONFIG_FILE_GENERATOR = new FileGeneratorImp(
17
+ 'vitest.config.ts',
18
+ VITEST_CONFIG,
19
+ );
@@ -0,0 +1,83 @@
1
+ import { Equalable, Snapshot, sortKeys } from '@stack-dev/core';
2
+
3
+ import { isEqual } from 'lodash';
4
+
5
+ export type ConstructorArgs = {
6
+ paths?: Record<string, ReadonlyArray<string>>;
7
+ additionalData?: Snapshot;
8
+ };
9
+
10
+ export class CompilerOptions implements Equalable {
11
+ private readonly _additionalData: Snapshot;
12
+
13
+ private readonly _paths: Record<string, ReadonlyArray<string>>;
14
+
15
+ public constructor(args?: ConstructorArgs) {
16
+ this._paths = args?.paths ?? {};
17
+ this._additionalData = args?.additionalData ?? {};
18
+ }
19
+
20
+ public get paths(): Record<string, ReadonlyArray<string>> {
21
+ return this._paths;
22
+ }
23
+
24
+ public get additionalData(): Snapshot {
25
+ return this._additionalData;
26
+ }
27
+
28
+ public setPaths(
29
+ paths: Record<string, ReadonlyArray<string>>,
30
+ ): CompilerOptions {
31
+ return new CompilerOptions({
32
+ paths,
33
+ additionalData: this._additionalData,
34
+ });
35
+ }
36
+
37
+ public format(): string {
38
+ const json = {
39
+ paths: this._paths,
40
+ ...this._additionalData,
41
+ };
42
+
43
+ const ordered = sortKeys(json, compareKeys);
44
+
45
+ return JSON.stringify(ordered, null, 2);
46
+ }
47
+
48
+ public equals(other: unknown): boolean {
49
+ // TODO: Get rid of lodash.
50
+
51
+ if (other instanceof CompilerOptions) {
52
+ return (
53
+ isEqual(this._paths, other._paths) &&
54
+ isEqual(this._additionalData, other._additionalData)
55
+ );
56
+ } else {
57
+ return false;
58
+ }
59
+ }
60
+ }
61
+
62
+ function compareKeys(a: string, b: string): number {
63
+ return getKeyIndex(a) - getKeyIndex(b);
64
+ }
65
+
66
+ function getKeyIndex(s: string): number {
67
+ const order = [
68
+ 'target',
69
+ 'module',
70
+ 'moduleResolution',
71
+ 'esModuleInterop',
72
+ 'lib',
73
+ 'types',
74
+ 'strict',
75
+ 'allowJs',
76
+ ];
77
+
78
+ if (order.every((key) => key !== s)) {
79
+ return Number.MAX_VALUE;
80
+ } else {
81
+ return order.indexOf(s);
82
+ }
83
+ }
@@ -0,0 +1,4 @@
1
+ export * from './tsconfig';
2
+
3
+ export * from './compiler-options';
4
+ export * from './reference';
@@ -0,0 +1,21 @@
1
+ import { Equalable } from '@stack-dev/core';
2
+
3
+ export class Reference implements Equalable {
4
+ private readonly _path: string;
5
+
6
+ public constructor(path: string) {
7
+ this._path = path;
8
+ }
9
+
10
+ public get path(): string {
11
+ return this._path;
12
+ }
13
+
14
+ equals(other: unknown): boolean {
15
+ if (other instanceof Reference) {
16
+ return this._path === other._path;
17
+ } else {
18
+ return false;
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,137 @@
1
+ import * as JSON5 from 'json5';
2
+
3
+ import { Equalable, haveSameItems, sortKeys } from '@stack-dev/core';
4
+
5
+ import { Snapshot } from '@stack-dev/core';
6
+ import { isEqual } from 'lodash';
7
+ import { CompilerOptions } from './compiler-options';
8
+ import { Reference } from './reference';
9
+
10
+ type ConstructorArgs = {
11
+ compilerOptions?: CompilerOptions;
12
+ references?: ReadonlyArray<Reference>;
13
+ additionalData?: Snapshot;
14
+ };
15
+
16
+ export class TSConfig implements Equalable {
17
+ private readonly _compilerOptions: CompilerOptions;
18
+
19
+ private readonly _references: ReadonlyArray<Reference>;
20
+
21
+ private readonly _additionalData: Snapshot;
22
+
23
+ public constructor(args?: ConstructorArgs) {
24
+ this._compilerOptions = args?.compilerOptions ?? new CompilerOptions();
25
+ this._references = args?.references ?? [];
26
+ this._additionalData = args?.additionalData ?? {};
27
+ }
28
+
29
+ public get compilerOptions(): CompilerOptions {
30
+ return this._compilerOptions;
31
+ }
32
+
33
+ public addReference(reference: Reference): TSConfig {
34
+ return new TSConfig({
35
+ compilerOptions: this._compilerOptions,
36
+ references: [...this._references, reference],
37
+ additionalData: this._additionalData,
38
+ });
39
+ }
40
+
41
+ public setCompilerOptions(compilerOptions: CompilerOptions): TSConfig {
42
+ return new TSConfig({
43
+ compilerOptions,
44
+ references: this._references,
45
+ additionalData: this._additionalData,
46
+ });
47
+ }
48
+
49
+ public static parse(s: string): TSConfig {
50
+ const json = JSON5.parse(s);
51
+
52
+ const references = TSConfig.parseReferences(json);
53
+
54
+ const compilerOptions = new CompilerOptions({
55
+ paths: json.compilerOptions?.paths,
56
+ ...json.compilerOptions,
57
+ });
58
+
59
+ const additionalData = { ...json };
60
+ delete additionalData['compilerOptions'];
61
+ delete additionalData['references'];
62
+
63
+ return new TSConfig({
64
+ compilerOptions,
65
+ references,
66
+ additionalData,
67
+ });
68
+ }
69
+
70
+ private static parseReferences(json: unknown): ReadonlyArray<Reference> {
71
+ if (
72
+ typeof json === 'object' &&
73
+ json !== null &&
74
+ 'references' in json &&
75
+ json.references instanceof Array
76
+ ) {
77
+ return json.references.map(
78
+ (r: Record<string, string>) => new Reference(r.path),
79
+ );
80
+ } else {
81
+ return [];
82
+ }
83
+ }
84
+
85
+ public format(): string {
86
+ const compilerOptions = JSON5.parse(this.compilerOptions.format());
87
+
88
+ const json = {
89
+ compilerOptions,
90
+ references: this._references.map((r) => ({ path: r.path })),
91
+ ...this._additionalData,
92
+ };
93
+
94
+ const ordered = sortKeys(json, compareKeys);
95
+
96
+ return JSON.stringify(ordered, null, 2);
97
+ }
98
+
99
+ public equals(other: unknown): boolean {
100
+ if (other instanceof TSConfig) {
101
+ const sameReferences = haveSameItems(
102
+ this._references,
103
+ other._references,
104
+ (r1, r2) => r1.equals(r2),
105
+ );
106
+
107
+ return (
108
+ this._compilerOptions.equals(other._compilerOptions) &&
109
+ sameReferences &&
110
+ isEqual(this._additionalData, other._additionalData)
111
+ );
112
+ } else {
113
+ return false;
114
+ }
115
+ }
116
+ }
117
+
118
+ // TODO: orderKeys (by array);
119
+ function compareKeys(a: string, b: string): number {
120
+ return getKeyIndex(a) - getKeyIndex(b);
121
+ }
122
+
123
+ function getKeyIndex(s: string): number {
124
+ const order = [
125
+ 'extends',
126
+ 'compilerOptions',
127
+ 'include',
128
+ 'exclude',
129
+ 'references',
130
+ ];
131
+
132
+ if (order.every((key) => key !== s)) {
133
+ return Number.MAX_VALUE;
134
+ } else {
135
+ return order.indexOf(s);
136
+ }
137
+ }
@@ -0,0 +1,47 @@
1
+ import { getDirectoryPackageJson, getPackageJSONPath } from './utils/utils';
2
+
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { TSConfig } from './tsconfig';
6
+ import { Package } from './utils/package';
7
+ import { getNamespace } from './utils/workspace';
8
+
9
+ export async function unlinkPackages(
10
+ current: Package,
11
+ target: Package,
12
+ ): Promise<void> {
13
+ await updatePackageJSON(current, target);
14
+ await updateTSConfig(current, target);
15
+ }
16
+
17
+ async function updatePackageJSON(current: Package, target: Package) {
18
+ const namespace = await getNamespace();
19
+
20
+ const packageJSON = await getDirectoryPackageJson(current.directory);
21
+ const updated = packageJSON
22
+ .removeDependency(target.name)
23
+ .removeDevDependency(target.name);
24
+
25
+ const packageJSONPath = getPackageJSONPath(current.directory);
26
+
27
+ await fs.writeFile(packageJSONPath, updated.format(namespace));
28
+ }
29
+
30
+ async function updateTSConfig(current: Package, target: Package) {
31
+ const tsconfigPath = path.join(current.directory, 'tsconfig.json');
32
+ const tsconfigContents = await fs.readFile(tsconfigPath, 'utf8');
33
+ const tsconfig = TSConfig.parse(tsconfigContents);
34
+
35
+ const updatedPaths = Object.fromEntries(
36
+ Object.entries(tsconfig.compilerOptions.paths).filter(
37
+ ([key]) => key !== target.name,
38
+ ),
39
+ );
40
+
41
+ const updatedCompilerOptions =
42
+ tsconfig.compilerOptions.setPaths(updatedPaths);
43
+
44
+ const updated = tsconfig.setCompilerOptions(updatedCompilerOptions);
45
+
46
+ await fs.writeFile(tsconfigPath, updated.format());
47
+ }
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { FileGenerator, PackageJsonGenerator } from '../file-generator';
4
+
5
+ export class PackageGenerator {
6
+ private readonly _root: string;
7
+
8
+ private readonly _packageJson: PackageJsonGenerator;
9
+
10
+ private readonly _fileGenerators: ReadonlyArray<FileGenerator>;
11
+
12
+ public constructor(
13
+ root: string,
14
+ packageJson: PackageJsonGenerator,
15
+ fileGenerators: ReadonlyArray<FileGenerator> = [],
16
+ ) {
17
+ this._root = root;
18
+ this._packageJson = packageJson;
19
+ this._fileGenerators = fileGenerators;
20
+ }
21
+
22
+ public async generate(): Promise<void> {
23
+ const all = [this._packageJson, ...this._fileGenerators];
24
+
25
+ await fs.mkdir(this._root, { recursive: true });
26
+
27
+ await Promise.all(all.map((gen) => this.write(gen)));
28
+ }
29
+
30
+ private async write(fileGenerator: FileGenerator): Promise<void> {
31
+ const contents = await fileGenerator.generate();
32
+
33
+ const filepath = path.join(this._root, fileGenerator.filepath);
34
+
35
+ const directory = path.dirname(filepath);
36
+
37
+ await fs.mkdir(directory, { recursive: true });
38
+
39
+ await fs.writeFile(filepath, contents);
40
+ }
41
+ }
@@ -0,0 +1,44 @@
1
+ import { prompt } from 'enquirer';
2
+
3
+ export const packageTypes = [
4
+ 'library',
5
+ 'config',
6
+ 'react',
7
+ 'vite',
8
+ 'fastify',
9
+ 'next',
10
+ 'cli',
11
+ ] as const;
12
+
13
+ export type PackageType = (typeof packageTypes)[number];
14
+
15
+ export async function pickPackageType(
16
+ options?: Record<string, string | undefined>,
17
+ ): Promise<PackageType> {
18
+ if (options?.type && isPackageType(options.type)) {
19
+ return options.type;
20
+ } else if (options?.type && !isPackageType(options.type)) {
21
+ throw new Error(
22
+ `--type setting "${options.type}" is invalid, must be one of ${packageTypes.join(', ')}.`,
23
+ );
24
+ }
25
+
26
+ const response = await prompt<{ type: string }>({
27
+ type: 'select',
28
+ name: 'type',
29
+ message: 'What kind of package do you want?',
30
+ choices: [...packageTypes],
31
+ });
32
+
33
+ if (!isPackageType(response.type)) {
34
+ throw new Error(
35
+ `Type "${response.type}" is invalid, must be one of ${packageTypes.join(', ')}.`,
36
+ );
37
+ }
38
+
39
+ return response.type;
40
+ }
41
+
42
+ export function isPackageType(s: string): s is PackageType {
43
+ return packageTypes.some((p) => p === s);
44
+ }
@@ -0,0 +1,126 @@
1
+ import { fileExists, getDirectoryPackageJson } from './utils';
2
+ import { getDirectoryWorkspaceFile, getWorkspaceRoot } from './workspace';
3
+
4
+ import { glob } from 'fast-glob';
5
+ import path from 'node:path';
6
+
7
+ // TODO: make into class with PackageJSON.
8
+ // TODO: Create Workspace/context...
9
+ export type Package = {
10
+ name: string;
11
+
12
+ directory: string;
13
+
14
+ type: 'Config' | 'App' | 'Library' | 'Unknown';
15
+ };
16
+
17
+ export async function getCurrentPackage(
18
+ directory: string = process.cwd(),
19
+ ): Promise<Package> {
20
+ const packageRoot = await getPackageRoot(directory);
21
+ const packageJson = await getDirectoryPackageJson(packageRoot);
22
+
23
+ return getPackageByName(packageJson.name);
24
+ }
25
+
26
+ export async function getPackageRoot(
27
+ directory: string = process.cwd(),
28
+ ): Promise<string> {
29
+ const parent = path.dirname(directory);
30
+
31
+ if (parent === directory) {
32
+ throw new Error('Not a package.');
33
+ }
34
+
35
+ if (await isPackageRoot(directory)) {
36
+ return directory;
37
+ }
38
+
39
+ return getPackageRoot(parent);
40
+ }
41
+
42
+ export async function getPackageByName(name: string): Promise<Package> {
43
+ const all = await getAllPackages();
44
+
45
+ const match = all.find((p) => p.name === name);
46
+
47
+ if (match === undefined) {
48
+ throw new Error(`No package with name "${name}".`);
49
+ }
50
+
51
+ return match;
52
+ }
53
+
54
+ export async function getAllPackages(
55
+ directory: string = process.cwd(),
56
+ ): Promise<ReadonlyArray<Package>> {
57
+ const workspaceRoot = await getWorkspaceRoot(directory);
58
+ const workspaceFile = (await getDirectoryWorkspaceFile(workspaceRoot)) as {
59
+ packages: ReadonlyArray<string>;
60
+ };
61
+
62
+ const results: Array<Package> = [];
63
+
64
+ for (const seg of workspaceFile.packages) {
65
+ const packageType = getPackageType(seg);
66
+
67
+ const packageJsonPaths = await glob(`${workspaceRoot}/${seg}/package.json`);
68
+
69
+ const packageDirectories = packageJsonPaths.map((p) => path.dirname(p));
70
+
71
+ for (const directory of packageDirectories) {
72
+ const name = (await getDirectoryPackageJson(directory)).name;
73
+
74
+ results.push({
75
+ name,
76
+ directory,
77
+ type: packageType,
78
+ });
79
+ }
80
+ }
81
+
82
+ return results;
83
+ }
84
+
85
+ function getPackageType(segment: string): Package['type'] {
86
+ if (segment.startsWith('app')) {
87
+ return 'App';
88
+ } else if (segment.startsWith('config')) {
89
+ return 'Config';
90
+ } else if (segment.startsWith('package') || segment.startsWith('lib')) {
91
+ return 'Library';
92
+ } else {
93
+ return 'Unknown';
94
+ }
95
+ }
96
+
97
+ export function comparePackages(a: Package, b: Package): number {
98
+ const packageTypeDifference = comparePackageTypes(a.type, b.type);
99
+
100
+ if (packageTypeDifference !== 0) {
101
+ return packageTypeDifference;
102
+ } else {
103
+ return a.name.localeCompare(b.name);
104
+ }
105
+ }
106
+
107
+ function comparePackageTypes(a: Package['type'], b: Package['type']): number {
108
+ return getPackageTypeIndex(a) - getPackageTypeIndex(b);
109
+ }
110
+
111
+ function getPackageTypeIndex(packageType: Package['type']): number {
112
+ switch (packageType) {
113
+ case 'Library':
114
+ return 0;
115
+ case 'Config':
116
+ return 1;
117
+ case 'App':
118
+ return 2;
119
+ case 'Unknown':
120
+ return 3;
121
+ }
122
+ }
123
+
124
+ async function isPackageRoot(directory: string): Promise<boolean> {
125
+ return await fileExists(path.join(directory, 'package.json'));
126
+ }
@@ -0,0 +1,41 @@
1
+ import { prompt } from 'enquirer';
2
+
3
+ export const styleTypes = [
4
+ 'tailwind',
5
+ 'css-modules',
6
+ 'styled-components',
7
+ 'none',
8
+ ] as const;
9
+
10
+ export type StyleType = (typeof styleTypes)[number];
11
+
12
+ export async function pickStyleType(
13
+ options?: Record<string, string | undefined>,
14
+ ): Promise<StyleType> {
15
+ if (options?.style && isStyleType(options?.style)) {
16
+ return options?.style;
17
+ } else if (options?.style && !isStyleType(options?.style)) {
18
+ throw new Error(
19
+ `--style setting "${options.style}" is invalid, must be one of ${styleTypes.join(', ')}.`,
20
+ );
21
+ }
22
+
23
+ const response = await prompt<{ type: string }>({
24
+ type: 'select',
25
+ name: 'type',
26
+ message: 'What kind of style do you want?',
27
+ choices: [...styleTypes],
28
+ });
29
+
30
+ if (!isStyleType(response.type)) {
31
+ throw new Error(
32
+ `Type "${response.type}" is invalid, must be one of ${styleTypes.join(', ')}.`,
33
+ );
34
+ }
35
+
36
+ return response.type;
37
+ }
38
+
39
+ export function isStyleType(s: string): s is StyleType {
40
+ return styleTypes.some((p) => p === s);
41
+ }