@mostrom/app-shell 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/vite.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
 
4
5
  /**
5
6
  * Returns the monorepo root directory (platform/).
6
7
  * @param {string} currentDir
7
8
  * @returns {string}
8
9
  */
10
+ const APP_SHELL_SRC_DIR = path.dirname(fileURLToPath(import.meta.url));
11
+ const APP_SHELL_PACKAGE_DIR = path.resolve(APP_SHELL_SRC_DIR, "..");
12
+
9
13
  function getMonorepoRoot(currentDir) {
10
14
  let dir = currentDir;
11
15
  while (dir !== "/") {
@@ -97,11 +101,45 @@ function ssrCssStubPlugin() {
97
101
  * });
98
102
  */
99
103
  export function getSharedViteConfig(appDir) {
104
+ const installedAppShellSrc = path.resolve(appDir, "node_modules/@platform/app-shell/src");
105
+ const resolvedAppShellSrc = fs.existsSync(installedAppShellSrc) ? installedAppShellSrc : APP_SHELL_SRC_DIR;
106
+
107
+ // NPM-first shared config: keep only essential aliases/SSR handling.
108
+ return {
109
+ plugins: [ssrCssStubPlugin()],
110
+ resolve: {
111
+ preserveSymlinks: true,
112
+ alias: [
113
+ {
114
+ find: /^@\/(.*)$/,
115
+ replacement: `${resolvedAppShellSrc}/$1`,
116
+ },
117
+ ],
118
+ dedupe: ["react", "react-dom"],
119
+ },
120
+ ssr: {
121
+ external: [
122
+ /^react(\/.*)?$/,
123
+ /^react-dom(\/.*)?$/,
124
+ /^katex\/dist\/.*\.css$/,
125
+ ],
126
+ noExternal: [
127
+ /^@platform\/app-shell/,
128
+ /^@platform\/service-catalog/,
129
+ /^@copilotkit\//,
130
+ /^@copilotkitnext\//,
131
+ /^streamdown/,
132
+ /^rehype-katex/,
133
+ /^katex$/,
134
+ ],
135
+ },
136
+ };
137
+
100
138
  const monorepoRoot = getMonorepoRoot(appDir);
101
139
  const appNodeModules = path.resolve(appDir, "node_modules");
102
- const sharedPackages = path.resolve(monorepoRoot, "shared/packages");
103
- const appShellSrc = path.resolve(sharedPackages, "app-shell/src");
104
- const appShellNodeModules = path.resolve(sharedPackages, "app-shell/node_modules");
140
+ const appShellSrc = APP_SHELL_SRC_DIR;
141
+ const packageNodeModules = path.resolve(APP_SHELL_PACKAGE_DIR, "node_modules");
142
+ const appShellNodeModules = fs.existsSync(packageNodeModules) ? packageNodeModules : appNodeModules;
105
143
  const appHasDndKit = fs.existsSync(path.resolve(appNodeModules, "@dnd-kit/core"));
106
144
  const dndKitNodeModules = appHasDndKit ? appNodeModules : appShellNodeModules;
107
145
 
@@ -111,10 +149,6 @@ export function getSharedViteConfig(appDir) {
111
149
  preserveSymlinks: true,
112
150
  alias: [
113
151
  // Source aliases for HMR during development
114
- {
115
- find: /^@platform\/service-catalog(\/.*)?$/,
116
- replacement: `${path.resolve(sharedPackages, "service-catalog/src")}$1`,
117
- },
118
152
  {
119
153
  find: /^@platform\/app-shell(\/.*)?$/,
120
154
  replacement: `${appShellSrc}$1`,
@@ -206,7 +240,6 @@ export function getSharedViteConfig(appDir) {
206
240
  /^katex\/dist\/.*\.css$/,
207
241
  ],
208
242
  noExternal: [
209
- /^@cloudscape-design\//,
210
243
  /^@radix-ui\//,
211
244
  /^@base-ui\//,
212
245
  /^radix-ui/,
package/bin/init.js DELETED
@@ -1,269 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @platform/app-shell init
5
- *
6
- * Sets up a new app to use the app-shell package correctly.
7
- * Similar to `shadcn init` but configured for our monorepo structure.
8
- *
9
- * Usage (from your app's frontend directory):
10
- * node ../../../shared/packages/app-shell/bin/init.js
11
- * node ../../../shared/packages/app-shell/bin/init.js --force
12
- *
13
- * Note: npx/bunx won't work - this is a private monorepo package, not published to npm.
14
- *
15
- * What it does:
16
- * 1. Creates minimal app/tailwind.css (delegates to app-shell)
17
- * 2. Creates components.json (shadcn/ui configuration)
18
- * 3. Creates app/lib/utils.ts (cn utility + getBasePathHref)
19
- * 4. Creates tailwind.config.ts
20
- * 5. Validates vite.config.ts uses getSharedViteConfig()
21
- */
22
-
23
- import fs from "node:fs";
24
- import path from "node:path";
25
- import { fileURLToPath } from "node:url";
26
-
27
- const __filename = fileURLToPath(import.meta.url);
28
- const __dirname = path.dirname(__filename);
29
- const cwd = process.cwd();
30
-
31
- // ANSI colors
32
- const green = (s) => `\x1b[32m${s}\x1b[0m`;
33
- const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
34
- const red = (s) => `\x1b[31m${s}\x1b[0m`;
35
- const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
36
- const dim = (s) => `\x1b[2m${s}\x1b[0m`;
37
-
38
- console.log();
39
- console.log(cyan("@platform/app-shell init"));
40
- console.log(dim("Setting up app-shell configuration..."));
41
- console.log();
42
-
43
- // Templates
44
- const TAILWIND_CSS = `@import "https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@300;400;500;600;700&display=swap";
45
- @import "tw-animate-css";
46
- @import "tailwindcss";
47
- @import "@platform/app-shell/styles.css";
48
- @config "../tailwind.config.ts";
49
- `;
50
-
51
- // Compute relative path to app-shell from current directory
52
- function getAppShellRelativePath() {
53
- // The init script is at app-shell/bin/init.js
54
- // So app-shell src is at ../src relative to this script
55
- const appShellSrc = path.resolve(__dirname, "../src");
56
- const relativePath = path.relative(cwd, appShellSrc);
57
- return relativePath.replace(/\\/g, "/"); // Normalize for all platforms
58
- }
59
-
60
- const appShellPath = getAppShellRelativePath();
61
-
62
- const TAILWIND_CONFIG = `export default {
63
- darkMode: "class",
64
- content: [
65
- "./app/**/*.{ts,tsx,js,jsx}",
66
- // Include app-shell components for Tailwind to scan
67
- "${appShellPath}/**/*.{ts,tsx}",
68
- ],
69
- };
70
- `;
71
-
72
- const COMPONENTS_JSON = `{
73
- "$schema": "https://ui.shadcn.com/schema.json",
74
- "style": "new-york",
75
- "rsc": false,
76
- "tsx": true,
77
- "tailwind": {
78
- "config": "tailwind.config.ts",
79
- "css": "app/tailwind.css",
80
- "baseColor": "neutral",
81
- "cssVariables": true,
82
- "prefix": ""
83
- },
84
- "iconLibrary": "lucide",
85
- "rtl": false,
86
- "aliases": {
87
- "components": "~/components",
88
- "utils": "~/lib/utils",
89
- "ui": "~/components/ui",
90
- "lib": "~/lib",
91
- "hooks": "~/hooks"
92
- },
93
- "registries": {}
94
- }
95
- `;
96
-
97
- const UTILS_TS = `import { clsx, type ClassValue } from "clsx"
98
- import { twMerge } from "tailwind-merge"
99
-
100
- export function cn(...inputs: ClassValue[]) {
101
- return twMerge(clsx(inputs))
102
- }
103
-
104
- /**
105
- * Returns a path with the VITE_BASE_PATH prefix for use in href attributes.
106
- * React Router's navigate() handles basename automatically, but href attributes
107
- * on Cloudscape Link components (or native <a> tags) do not.
108
- *
109
- * @param path - The relative path (e.g., "/clients/123" or "/scheduling/abc")
110
- * @returns The path prefixed with the base path (e.g., "/client-management/clients/123")
111
- */
112
- export function getBasePathHref(path: string): string {
113
- const configuredBasePath = import.meta.env.VITE_BASE_PATH;
114
- if (typeof configuredBasePath !== "string") {
115
- throw new Error("VITE_BASE_PATH must be defined");
116
- }
117
- const basePath = configuredBasePath.replace(/\\/$/, "");
118
- const normalizedPath = path.startsWith("/") ? path : \`/\${path}\`;
119
- if (basePath === "" || basePath === "/") {
120
- return normalizedPath;
121
- }
122
- return \`\${basePath}\${normalizedPath}\`;
123
- }
124
- `;
125
-
126
- // Helper functions
127
- function ensureDir(dir) {
128
- if (!fs.existsSync(dir)) {
129
- fs.mkdirSync(dir, { recursive: true });
130
- }
131
- }
132
-
133
- function writeFile(filePath, content, overwrite = false) {
134
- const relativePath = path.relative(cwd, filePath);
135
-
136
- if (fs.existsSync(filePath) && !overwrite) {
137
- // Check if content is different
138
- const existing = fs.readFileSync(filePath, "utf-8");
139
- if (existing === content) {
140
- console.log(dim(` ${relativePath} (unchanged)`));
141
- return false;
142
- }
143
- console.log(yellow(` ${relativePath} (exists, skipping - use --force to overwrite)`));
144
- return false;
145
- }
146
-
147
- fs.writeFileSync(filePath, content);
148
- console.log(green(` ${relativePath}`));
149
- return true;
150
- }
151
-
152
- function checkViteConfig() {
153
- const viteConfigPath = path.join(cwd, "vite.config.ts");
154
- if (!fs.existsSync(viteConfigPath)) {
155
- console.log(yellow(" vite.config.ts not found - skipping validation"));
156
- return;
157
- }
158
-
159
- const content = fs.readFileSync(viteConfigPath, "utf-8");
160
-
161
- if (!content.includes("getSharedViteConfig")) {
162
- console.log();
163
- console.log(yellow("Warning: vite.config.ts does not use getSharedViteConfig()"));
164
- console.log(dim("Consider updating it to use the shared config:"));
165
- console.log();
166
- console.log(dim(` import { getSharedViteConfig } from "@platform/app-shell/vite";`));
167
- console.log(dim(` import { mergeConfig } from "vite";`));
168
- console.log();
169
- console.log(dim(` export default defineConfig(({ mode }) => {`));
170
- console.log(dim(` const shared = getSharedViteConfig(__dirname);`));
171
- console.log(dim(` return mergeConfig(shared, { /* app-specific config */ });`));
172
- console.log(dim(` });`));
173
- } else {
174
- console.log(green(" vite.config.ts uses getSharedViteConfig()"));
175
- }
176
- }
177
-
178
- function checkConflictingCss() {
179
- // Check for app.css or other CSS files that might conflict with app-shell
180
- const conflictingFiles = ["app/app.css", "app/global.css", "app/globals.css"];
181
- const found = [];
182
-
183
- for (const file of conflictingFiles) {
184
- const filePath = path.join(cwd, file);
185
- if (fs.existsSync(filePath)) {
186
- const content = fs.readFileSync(filePath, "utf-8");
187
- // Check if it has html/body/root blocks with hardcoded colors
188
- // These patterns specifically target global style overrides
189
- const hasGlobalColorOverride =
190
- /(?:html|body|\:root)\s*\{[^}]*(?:background-color|background|color)\s*:/i.test(content);
191
-
192
- if (hasGlobalColorOverride) {
193
- found.push(file);
194
- }
195
- }
196
- }
197
-
198
- if (found.length > 0) {
199
- console.log();
200
- console.log(yellow("Warning: Found CSS files that may conflict with app-shell:"));
201
- for (const file of found) {
202
- console.log(red(` ${file}`));
203
- }
204
- console.log(dim("These files have html/body/:root blocks with hardcoded colors."));
205
- console.log(dim("Remove these blocks and rely on app-shell/styles.css for theming."));
206
- } else {
207
- console.log(green(" No conflicting CSS files found"));
208
- }
209
- }
210
-
211
- // Check for --force flag
212
- const force = process.argv.includes("--force") || process.argv.includes("-f");
213
-
214
- // Preflight checks
215
- console.log("Preflight checks:");
216
-
217
- // Check if we're in a frontend directory
218
- const packageJsonPath = path.join(cwd, "package.json");
219
- if (!fs.existsSync(packageJsonPath)) {
220
- console.log(red(" package.json not found"));
221
- console.log(dim(" Run this command from your frontend directory"));
222
- process.exit(1);
223
- }
224
- console.log(green(" Found package.json"));
225
-
226
- // Check for app directory (React Router convention)
227
- const appDir = path.join(cwd, "app");
228
- if (!fs.existsSync(appDir)) {
229
- console.log(yellow(" app/ directory not found - will create it"));
230
- }
231
- console.log(green(" Found app/ directory"));
232
-
233
- // Check for @platform/app-shell dependency
234
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
235
- const hasAppShell = packageJson.dependencies?.["@platform/app-shell"] ||
236
- packageJson.devDependencies?.["@platform/app-shell"];
237
- if (!hasAppShell) {
238
- console.log(yellow(" @platform/app-shell not in dependencies"));
239
- console.log(dim(" Add it: \"@platform/app-shell\": \"file:../../../shared/packages/app-shell\""));
240
- } else {
241
- console.log(green(" Found @platform/app-shell dependency"));
242
- }
243
-
244
- console.log();
245
- console.log("Creating files:");
246
-
247
- // Ensure directories exist
248
- ensureDir(path.join(cwd, "app"));
249
- ensureDir(path.join(cwd, "app/lib"));
250
-
251
- // Create files
252
- writeFile(path.join(cwd, "app/tailwind.css"), TAILWIND_CSS, force);
253
- writeFile(path.join(cwd, "tailwind.config.ts"), TAILWIND_CONFIG, force);
254
- writeFile(path.join(cwd, "components.json"), COMPONENTS_JSON, force);
255
- writeFile(path.join(cwd, "app/lib/utils.ts"), UTILS_TS, force);
256
-
257
- console.log();
258
- console.log("Validating configuration:");
259
- checkViteConfig();
260
- checkConflictingCss();
261
-
262
- console.log();
263
- console.log(green("Done!"));
264
- console.log();
265
- console.log("Next steps:");
266
- console.log(dim(" 1. Run 'bun install' to install dependencies"));
267
- console.log(dim(" 2. Import '@platform/app-shell' in your layout component"));
268
- console.log(dim(" 3. Run 'bun run dev' to start the dev server"));
269
- console.log();