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 +4 -1
- package/dist/index.js +18 -4
- package/dist/patch.js +36 -1
- package/dist/scaffold.js +61 -6
- package/package.json +1 -1
- package/templates/base/src/assets/index.tsx +1 -0
- package/templates/base/src/core/redux/mmkvStorage.ts +5 -5
- package/templates/base/src/ui/theme/colors.ts +0 -44
- package/templates/base/assets/fonts/.gitkeep +0 -0
- package/templates/base/assets/images/.gitkeep +0 -0
- package/templates/base/assets/index.ts +0 -8
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("
|
|
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
|
|
35
|
-
*
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Images = {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// react-native-mmkv
|
|
2
|
-
//
|
|
3
|
-
import {
|
|
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 =
|
|
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.
|
|
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 {};
|