codingpixel-expo-app 0.1.5 → 0.1.7

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/dist/fonts.js CHANGED
@@ -54,8 +54,11 @@ export function generateUseFontsBlocks(primary, secondary) {
54
54
  fontPairs.push([`${secondary}-Regular`, `${secondary}-Regular.ttf`], [`${secondary}-Medium`, `${secondary}-Medium.ttf`], [`${secondary}-SemiBold`, `${secondary}-SemiBold.ttf`], [`${secondary}-Bold`, `${secondary}-Bold.ttf`]);
55
55
  }
56
56
  const importBlock = `import { useFonts } from "expo-font";`;
57
+ // From `src/app/_layout.tsx` to `src/assets/fonts/`:
58
+ // .. → src → ../assets/fonts/<file>
59
+ // (Was `../../assets/fonts/...` when assets lived at project root pre-Deviation #22.)
57
60
  const mapEntries = fontPairs
58
- .map(([key, file]) => ` "${key}": require("../../assets/fonts/${file}"),`)
61
+ .map(([key, file]) => ` "${key}": require("../assets/fonts/${file}"),`)
59
62
  .join("\n");
60
63
  const hookBlock = ` const [loaded] = useFonts({\n${mapEntries}\n });`;
61
64
  const guardBlock = ` if (!loaded) return null;`;
package/dist/index.js CHANGED
@@ -14,13 +14,13 @@ import path from "node:path";
14
14
  import { fileURLToPath } from "node:url";
15
15
  import { resolveTargetDir } from "./bootstrap.js";
16
16
  import { gatherAnswers, validateEnvVars } from "./prompts.js";
17
- import { cleanupBlankTemplate, runCreateExpoApp } from "./scaffold.js";
17
+ import { cleanupBlankTemplate, moveExpoIconsIntoSrcAssets, runCreateExpoApp, } from "./scaffold.js";
18
18
  import { applyBase, applyBottomSheet, applyImagePicker } from "./overlay.js";
19
- import { patchAppJson, patchAppJsonPlugins, patchConstants, patchExpoRouterEntry, patchLayout, patchPackageJsonScripts, patchTsconfig, } from "./patch.js";
19
+ import { patchAppJson, patchAppJsonAssetPaths, patchAppJsonPlugins, patchConstants, patchExpoRouterEntry, patchLayout, patchPackageJsonScripts, patchTsconfig, } from "./patch.js";
20
20
  import { patchBabel } from "./babel.js";
21
21
  import { buildLayoutReplacements } from "./fonts.js";
22
22
  import { ensureLockfile, installNativeDeps } from "./install.js";
23
- import { log } from "./util.js";
23
+ import { ensureDir, log } from "./util.js";
24
24
  import { readSDKNotes } from "./sdkNotes.js";
25
25
  import { SDK_PROBE_RESULTS } from "./sdkProbeResults.js";
26
26
  const __filename = fileURLToPath(import.meta.url);
@@ -49,6 +49,7 @@ async function main() {
49
49
  log.info(`App name: ${target.name}`);
50
50
  await runCreateExpoApp(target.dir, target.name);
51
51
  cleanupBlankTemplate(target.dir);
52
+ moveExpoIconsIntoSrcAssets(target.dir);
52
53
  const answers = await gatherAnswers();
53
54
  log.info(`Answers: primaryFont="${answers.primaryFont}" secondaryFont="${answers.secondaryFont}" ` +
54
55
  `bottomSheet=${answers.bottomSheet} imagePicker=${answers.imagePicker} pm=${answers.packageManager}`);
@@ -56,6 +57,18 @@ async function main() {
56
57
  const templatesRoot = resolveTemplatesRoot();
57
58
  log.step("Overlaying templates/base/ …");
58
59
  applyBase(target.dir, templatesRoot);
60
+ // Ensure empty dirs that npm strips from the tarball still exist in the
61
+ // generated app (Deviation #22 + extension). All four are intentionally
62
+ // empty placeholders apps fill as features grow / they drop fonts/images.
63
+ // No `.gitkeep` is shipped — `ensureDir` creates the bare directory.
64
+ for (const rel of [
65
+ "src/features",
66
+ "src/core/hooks",
67
+ "src/assets/fonts",
68
+ "src/assets/images",
69
+ ]) {
70
+ ensureDir(path.join(target.dir, rel));
71
+ }
59
72
  if (answers.bottomSheet) {
60
73
  log.step("Overlaying templates/bottom-sheet/ …");
61
74
  applyBottomSheet(target.dir, templatesRoot);
@@ -67,6 +80,7 @@ async function main() {
67
80
  // ---- Patches ----
68
81
  log.step("Patching app.json + expo-router entry …");
69
82
  patchAppJson(target.dir, target.name, answers);
83
+ patchAppJsonAssetPaths(target.dir);
70
84
  patchExpoRouterEntry(target.dir);
71
85
  patchAppJsonPlugins(target.dir, answers);
72
86
  log.step("Splicing constants + layout sentinels …");
@@ -105,7 +119,7 @@ async function main() {
105
119
  "@components": "./src/ui/components",
106
120
  "@icons": "./src/ui/iconComponents",
107
121
  "@features": "./src/features",
108
- "@assets": "./assets",
122
+ "@assets": "./src/assets",
109
123
  },
110
124
  });
111
125
  // ---- Install + verify ----
package/dist/patch.js CHANGED
@@ -84,6 +84,41 @@ export function patchAppJson(target, name, _answers) {
84
84
  }
85
85
  writeJson(p, json);
86
86
  }
87
+ /**
88
+ * Rewrite `app.json` asset paths from create-expo-app's `./assets/<file>`
89
+ * defaults to `./src/assets/<file>` (Deviation #22 — unified asset layout).
90
+ *
91
+ * Pairs with `moveExpoIconsIntoSrcAssets` in scaffold.ts (which moves the
92
+ * actual PNG files). Idempotent: only rewrites paths that start with `./assets/`,
93
+ * so re-running on an already-patched app.json is a no-op.
94
+ */
95
+ export function patchAppJsonAssetPaths(target) {
96
+ const p = path.join(target, "app.json");
97
+ if (!fileExists(p))
98
+ return;
99
+ const json = readJson(p);
100
+ json.expo ??= {};
101
+ const rewrite = (value) => {
102
+ if (!value)
103
+ return value;
104
+ if (value.startsWith("./assets/")) {
105
+ return value.replace(/^\.\/assets\//, "./src/assets/");
106
+ }
107
+ return value;
108
+ };
109
+ if (json.expo.icon)
110
+ json.expo.icon = rewrite(json.expo.icon);
111
+ if (json.expo.splash?.image) {
112
+ json.expo.splash.image = rewrite(json.expo.splash.image);
113
+ }
114
+ if (json.expo.android?.adaptiveIcon?.foregroundImage) {
115
+ json.expo.android.adaptiveIcon.foregroundImage = rewrite(json.expo.android.adaptiveIcon.foregroundImage);
116
+ }
117
+ if (json.expo.web?.favicon) {
118
+ json.expo.web.favicon = rewrite(json.expo.web.favicon);
119
+ }
120
+ writeJson(p, json);
121
+ }
87
122
  /**
88
123
  * Phase 5 step 5 — add image-picker plugin entry conditionally. Idempotent via
89
124
  * `nameOf` equality (user-customized options object preserved).
@@ -214,7 +249,7 @@ const SPEC_PATHS = {
214
249
  "@components/*": ["src/ui/components/*"],
215
250
  "@icons/*": ["src/ui/iconComponents/*"],
216
251
  "@features/*": ["src/features/*"],
217
- "@assets": ["assets"],
252
+ "@assets": ["src/assets"],
218
253
  };
219
254
  /**
220
255
  * Phase 7 step 3 — patch tsconfig:
package/dist/scaffold.js CHANGED
@@ -31,13 +31,68 @@ export async function runCreateExpoApp(dir, _name) {
31
31
  ], { stdio: "inherit" });
32
32
  }
33
33
  /**
34
- * Delete `App.tsx` shipped by blank-typescript collides with expo-router's
35
- * auto-detection of `src/app/`. Idempotent (no-op if already absent).
34
+ * Delete blank-typescript leftovers that conflict with our expo-router setup:
35
+ *
36
+ * - `App.tsx` — root component for non-expo-router apps; collides with
37
+ * expo-router's auto-detection of `src/app/`.
38
+ * - `index.ts` — root entry shipped by blank-typescript: imports `./App`
39
+ * and calls `registerRootComponent`. We set `package.json#main` to
40
+ * `expo-router/entry` so this file is unused at runtime, but TypeScript
41
+ * still type-checks it and errors with `Cannot find module './App'`
42
+ * after we delete the file above.
43
+ *
44
+ * Idempotent (no-op if already absent).
36
45
  */
37
46
  export function cleanupBlankTemplate(target) {
38
- const appTsx = path.join(target, "App.tsx");
39
- if (fileExists(appTsx)) {
40
- fs.rmSync(appTsx);
41
- log.step("Removed blank-typescript App.tsx (replaced by expo-router src/app/).");
47
+ const removable = ["App.tsx", "index.ts"];
48
+ for (const rel of removable) {
49
+ const p = path.join(target, rel);
50
+ if (fileExists(p)) {
51
+ fs.rmSync(p);
52
+ log.step(`Removed blank-typescript ${rel} (replaced by expo-router src/app/).`);
53
+ }
54
+ }
55
+ }
56
+ /**
57
+ * Move create-expo-app's root `assets/*.png` icons (icon, splash-icon,
58
+ * adaptive-icon, favicon) into `src/assets/` so the project has a single
59
+ * unified assets layout. Caller updates `app.json` paths separately (see
60
+ * `patchAppJsonAssetPaths` in patch.ts).
61
+ *
62
+ * Idempotent — if the source file is missing (already moved or never existed),
63
+ * skip it without erroring.
64
+ */
65
+ export function moveExpoIconsIntoSrcAssets(target) {
66
+ const rootAssets = path.join(target, "assets");
67
+ const srcAssets = path.join(target, "src", "assets");
68
+ if (!fs.existsSync(rootAssets))
69
+ return;
70
+ fs.mkdirSync(srcAssets, { recursive: true });
71
+ const movableFiles = [
72
+ "icon.png",
73
+ "splash-icon.png",
74
+ "adaptive-icon.png",
75
+ "favicon.png",
76
+ ];
77
+ let moved = 0;
78
+ for (const file of movableFiles) {
79
+ const from = path.join(rootAssets, file);
80
+ const to = path.join(srcAssets, file);
81
+ if (fileExists(from) && !fileExists(to)) {
82
+ fs.renameSync(from, to);
83
+ moved++;
84
+ }
85
+ }
86
+ // If root `assets/` is now empty (only icons lived there), remove it.
87
+ try {
88
+ const remaining = fs.readdirSync(rootAssets);
89
+ if (remaining.length === 0)
90
+ fs.rmdirSync(rootAssets);
91
+ }
92
+ catch {
93
+ // Not empty (user-added content) → leave alone.
94
+ }
95
+ if (moved > 0) {
96
+ log.step(`Moved ${moved} icon file(s) from assets/ to src/assets/.`);
42
97
  }
43
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codingpixel-expo-app",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Opinionated Expo app scaffolder mirroring MyRoster conventions.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ export const Images = {};
@@ -1,8 +1,8 @@
1
- // react-native-mmkv 3.x+ exports `MMKV` as a class (was `createMMKV()` factory
2
- // in older versions). MyRoster's source predates the API change.
3
- import { MMKV } from "react-native-mmkv";
1
+ // react-native-mmkv 4.x exports `createMMKV()` factory + `MMKV` as a TYPE only.
2
+ // (Earlier 3.x exported `MMKV` as a class; the API was reverted in 4.x.)
3
+ import { createMMKV } from "react-native-mmkv";
4
4
 
5
- const storage = new MMKV();
5
+ const storage = createMMKV();
6
6
 
7
7
  export const reduxStorage = {
8
8
  setItem: (key: string, value: string) => {
@@ -14,7 +14,7 @@ export const reduxStorage = {
14
14
  return Promise.resolve(value);
15
15
  },
16
16
  removeItem: (key: string) => {
17
- storage.delete(key);
17
+ storage.remove(key);
18
18
  return Promise.resolve();
19
19
  },
20
20
  };
@@ -1,54 +1,10 @@
1
1
  export const enum Colors {
2
2
  PRIMARY = "#2E5A88",
3
- PRIMARY_1 = "#436B94",
4
- PRIMARY_2 = "#587BA0",
5
- PRIMARY_3 = "#829CB8",
6
- PRIMARY_4 = "#97ADC4",
7
- PRIMARY_5 = "#ABBDCF",
8
- PRIMARY_6 = "#C1CEDC",
9
- PRIMARY_7 = "#D5DEE7",
10
- PRIMARY_8 = "#EBEFF4",
11
3
 
12
4
  SECONDARY = "#2D85B7",
13
- SECONDARY_1 = "#4292BF",
14
- SECONDARY_2 = "#579DC5",
15
- SECONDARY_3 = "#6CAACD",
16
- SECONDARY_4 = "#81B6D4",
17
- SECONDARY_5 = "#96C2DB",
18
- SECONDARY_6 = "#ABCEE2",
19
- SECONDARY_7 = "#C0DBEA",
20
- SECONDARY_8 = "#D5E7F1",
21
- SECONDARY_9 = "#EAF3F8",
22
-
23
- SUCCESS = "#22BB33",
24
- SUCCESS_10_PERCENT = "#E9F9EB",
25
- INFO = "#246BFD",
26
- WARNING = "#BF6A02",
27
- WARNING_10_PERCENT = "#FEF7EE",
28
- ERROR = "#BB2124",
29
- ERROR_10_PERCENT = "#F9E9EA",
30
- DISABLED = "#AAAAAA",
31
- DISABLED_BUTTON = "#693EC9",
32
- NOTIFICATION = "#F75555",
33
-
34
- PURPLE_TEN_PERCENT = "#F0ECFA",
35
- PURPLE = "#693EC9",
36
-
37
- GRAY_10 = "#303030",
38
- GRAY_9 = "#3C3D3D",
39
- GRAY_8 = "#444546",
40
- GRAY_7 = "#4E4F50",
41
- GRAY_6 = "#5B5D5F",
42
- GRAY_5 = "#86888A",
43
- GRAY_4 = "#AEB0B2",
44
- GRAY_3 = "#D0D1D1",
45
- GRAY_2 = "#E6E7E7",
46
- GRAY_1 = "#F5F6F6",
47
5
 
48
6
  WHITE = "#FFFFFF",
49
- BORDER = "#D1D0D0",
50
7
  BLACK = "#1A1A1A",
51
8
 
52
9
  TRANSPARENT = "transparent",
53
- BG_COLOR = "#FDFDFD",
54
10
  }
File without changes
File without changes
@@ -1,8 +0,0 @@
1
- // Asset registry shim. Apps add real exports as they bundle .png / .ttf etc.
2
- // Example:
3
- // import notification from "./images/notification.png";
4
- // export const Images = { notification };
5
- //
6
- // `@assets` resolves here via tsconfig path mapping.
7
- export const Images = {} as Record<string, number>;
8
- export {};