delacour 0.1.0-alpha.1 → 0.1.0-alpha.2

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 (3) hide show
  1. package/README.md +10 -3
  2. package/dist/index.js +78 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -121,15 +121,22 @@ never written to it.
121
121
 
122
122
  ## After `init`
123
123
 
124
- Three things need a human, because each is a decision inside a file you own:
124
+ Three things need a human, because each is a decision inside a file you own. `init` copies the
125
+ `provider` item in alongside `styles`, so the root every pressable needs is one import away — but
126
+ mounting it is an edit to your layout, and that stays yours:
125
127
 
126
128
  ```tsx
127
129
  // app/_layout.tsx
128
130
  import "@/styles/global.css";
129
- import { GestureHandlerRootView } from "react-native-gesture-handler";
131
+ import { DelacourProvider } from "@/components/ui/provider";
132
+ import { Stack } from "expo-router";
130
133
 
131
134
  export default function RootLayout() {
132
- return <GestureHandlerRootView style={{ flex: 1 }}>{/* … */}</GestureHandlerRootView>;
135
+ return (
136
+ <DelacourProvider>
137
+ <Stack />
138
+ </DelacourProvider>
139
+ );
133
140
  }
134
141
  ```
135
142
 
package/dist/index.js CHANGED
@@ -9943,13 +9943,13 @@ const DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/delacournz/delac
9943
9943
  * Replaced at build time with the git tag being published (see `tsdown.config.ts`).
9944
9944
  * `main` in a working tree, where there is no tag yet.
9945
9945
  */
9946
- const DEFAULT_REGISTRY_REF = "090ab14dc722b9c4de3295b0709e13da4c8afc24";
9946
+ const DEFAULT_REGISTRY_REF = "10f3730f22e550ce8327ff388dacf6191975fda8";
9947
9947
  const RAW_GITHUB = "https://raw.githubusercontent.com";
9948
9948
  const GITHUB_SHORTHAND = /^github:([^/#]+)\/([^/#]+)(?:#(.+))?$/;
9949
9949
  /** `https://raw.githubusercontent.com/owner/repo` and nothing more. */
9950
9950
  const BARE_RAW_REPO = new RegExp(`^${RAW_GITHUB}/([^/]+)/([^/]+)$`);
9951
9951
  function resolveRegistrySource(options) {
9952
- const ref = options.ref || "090ab14dc722b9c4de3295b0709e13da4c8afc24";
9952
+ const ref = options.ref || "10f3730f22e550ce8327ff388dacf6191975fda8";
9953
9953
  const url = options.url?.trim();
9954
9954
  if (!url) return remote(`${DEFAULT_REGISTRY_URL}/${ref}`);
9955
9955
  const shorthand = GITHUB_SHORTHAND.exec(url);
@@ -12128,23 +12128,40 @@ function matchesEntry(imported, target) {
12128
12128
  * Every pressable in the library is a Gesture Handler detector, and a detector
12129
12129
  * outside a `GestureHandlerRootView` never receives a touch. Nothing throws;
12130
12130
  * the button simply does not respond.
12131
+ *
12132
+ * `DelacourProvider` is that root, plus the other layers, so mounting it
12133
+ * counts. The copied components are skipped on purpose: `provider.tsx` itself
12134
+ * names both, and `init` copies it into every project, so a walk that read it
12135
+ * would pass an app that never mounted anything. The app root is read one level
12136
+ * deep as well as the usual `app/` and `src/` walks, because a blank Expo
12137
+ * template mounts everything from `App.tsx` at the top.
12138
+ *
12139
+ * Exported for the tests; `runChecks` is the entry point.
12131
12140
  */
12132
12141
  async function checkGestureHandlerRoot(config) {
12142
+ const appRoot = config.app.resolved.root;
12143
+ const skip = Object.values(config.directories);
12133
12144
  const roots = [
12134
12145
  "src/app",
12135
12146
  "app",
12136
12147
  "src"
12137
- ].map((path) => join(config.app.resolved.root, path));
12138
- if ((await Promise.all(roots.map((root) => containsText(root, "GestureHandlerRootView")))).some(Boolean)) return {
12139
- name: "Gesture Handler",
12140
- status: "pass",
12141
- detail: "GestureHandlerRootView found at the app root"
12142
- };
12148
+ ].map((path) => join(appRoot, path));
12149
+ for (const needle of ["DelacourProvider", "GestureHandlerRootView"]) {
12150
+ const walked = await Promise.all(roots.map((root) => containsText(root, needle, { skip })));
12151
+ if (await containsText(appRoot, needle, {
12152
+ skip,
12153
+ recursive: false
12154
+ }) || walked.some(Boolean)) return {
12155
+ name: "Gesture Handler",
12156
+ status: "pass",
12157
+ detail: `${needle} found at the app root`
12158
+ };
12159
+ }
12143
12160
  return {
12144
12161
  name: "Gesture Handler",
12145
12162
  status: "warn",
12146
- detail: "no GestureHandlerRootView found",
12147
- fix: "Wrap your root layout in <GestureHandlerRootView style={{ flex: 1 }}> — presses do nothing without it."
12163
+ detail: "no DelacourProvider or GestureHandlerRootView found",
12164
+ fix: "Wrap your root layout in <DelacourProvider> from your ui directory — or, by hand, <GestureHandlerRootView style={{ flex: 1 }}>. Presses do nothing without one of them."
12148
12165
  };
12149
12166
  }
12150
12167
  /**
@@ -12203,16 +12220,18 @@ async function readExpoConfig(cwd) {
12203
12220
  return null;
12204
12221
  }
12205
12222
  }
12206
- async function containsText(directory, needle) {
12223
+ async function containsText(directory, needle, options = {}) {
12207
12224
  let entries;
12208
12225
  try {
12209
- entries = await readdir(directory, { recursive: true });
12226
+ entries = await readdir(directory, { recursive: options.recursive ?? true });
12210
12227
  } catch {
12211
12228
  return false;
12212
12229
  }
12213
12230
  for (const entry of entries) {
12214
- if (!/\.tsx?$/.test(entry)) continue;
12215
- if ((await read$1(join(directory, entry)))?.includes(needle)) return true;
12231
+ if (!/\.tsx?$/.test(entry) || entry.includes("node_modules")) continue;
12232
+ const path = join(directory, entry);
12233
+ if (options.skip?.some((skipped) => path === skipped || path.startsWith(`${skipped}/`))) continue;
12234
+ if ((await read$1(path))?.includes(needle)) return true;
12216
12235
  }
12217
12236
  return false;
12218
12237
  }
@@ -12341,6 +12360,7 @@ function toPosix$1(path) {
12341
12360
  //#region src/commands/init.ts
12342
12361
  var init_exports = /* @__PURE__ */ __exportAll({
12343
12362
  defaultPlacement: () => defaultPlacement,
12363
+ followUps: () => followUps,
12344
12364
  init: () => init
12345
12365
  });
12346
12366
  async function init(components, options) {
@@ -12366,7 +12386,11 @@ async function init(components, options) {
12366
12386
  packageName: placement.packageName
12367
12387
  }), placement.root, output);
12368
12388
  await wireUpApp(resolved, resolved.package ? project.workspaceRoot : null, output);
12369
- await add(["styles", ...components], {
12389
+ await add([
12390
+ "styles",
12391
+ "provider",
12392
+ ...components
12393
+ ], {
12370
12394
  ...options,
12371
12395
  cwd: placement.root,
12372
12396
  overwrite: true
@@ -12509,6 +12533,7 @@ async function wireUpApp(config, workspaceRoot, output) {
12509
12533
  metro.snippet
12510
12534
  ].join("\n"));
12511
12535
  await ensureUniwindEnv(config, output);
12536
+ await ensureExpoEnv(config, output);
12512
12537
  const block = buildStylesBlock({
12513
12538
  cssPath: config.app.resolved.css,
12514
12539
  directories: config.directories
@@ -12540,6 +12565,25 @@ async function ensureUniwindEnv(config, output) {
12540
12565
  output.success(`Wrote ${style.path("uniwind-env.d.ts")} — the app needs it too, not just the package`);
12541
12566
  }
12542
12567
  /**
12568
+ * The file Expo's own CLI writes on first `start`, and nothing before that.
12569
+ *
12570
+ * `expo/types` is what declares `*.css` as a module. A template without a
12571
+ * router — `blank-typescript` — ships no `expo-env.d.ts`, so the CSS import
12572
+ * `init` just asked for fails `tsc` with "Cannot find module … './styles/global.css'"
12573
+ * until the app has been started once. Metro is fine either way; only the
12574
+ * typecheck a reader runs first is not.
12575
+ */
12576
+ async function ensureExpoEnv(config, output) {
12577
+ if (config.framework !== "expo") return;
12578
+ const path = join(config.app.resolved.root, "expo-env.d.ts");
12579
+ if (existsSync(path)) return;
12580
+ await write$1(path, `${EXPO_ENV_REFERENCE}\n`);
12581
+ output.success(`Wrote ${style.path("expo-env.d.ts")} — it declares *.css as a module`);
12582
+ }
12583
+ const EXPO_ENV_REFERENCE = `/// <reference types="expo/types" />
12584
+
12585
+ // NOTE: This file should not be edited and should be in your git ignore`;
12586
+ /**
12543
12587
  * What is left, and why the CLI did not just do it.
12544
12588
  *
12545
12589
  * Each of these needs a decision or an AST edit inside a file the project owns.
@@ -12547,18 +12591,30 @@ async function ensureUniwindEnv(config, output) {
12547
12591
  * the only chance to see it.
12548
12592
  */
12549
12593
  function printFollowUps(config, output) {
12550
- const items = [];
12551
- if (Object.keys(config.aliases).length > 0) items.push(`Set ${style.code("experiments.tsconfigPaths: true")} in app.config — Metro ignores tsconfig paths without it.`);
12552
- items.push(`Import ${style.code(`"${config.aliases.styles ?? "."}/${basename(config.app.resolved.css)}"`)} as the first statement of your root layout — without it every component renders unstyled.`);
12553
- items.push(`Wrap the app root in ${style.code("<GestureHandlerRootView style={{ flex: 1 }}>")} — presses do nothing without it.`);
12554
- items.push(`${style.code("theme.css")} is the file to edit — replace it with the theme.css tab from https://ui.delacour.co.nz/theme, or paste a shadcn or tweakcn globals.css over it and run ${style.code("delacour theme")}.`);
12555
12594
  output.info([
12556
12595
  `A few things need you:`,
12557
- ...items.map((line) => ` • ${line}`),
12596
+ ...followUps(config).map((line) => ` • ${line}`),
12558
12597
  "",
12559
12598
  `Run ${style.code("delacour doctor")} to check.`
12560
12599
  ].join("\n"));
12561
12600
  }
12601
+ /**
12602
+ * The follow-up lines themselves, in the order they are printed.
12603
+ *
12604
+ * Exported so the order is testable: the CSS import goes first because it is
12605
+ * the only one of these that produces no error at all, and the provider before
12606
+ * the theme because a theme is a choice and a root that receives touches is not.
12607
+ */
12608
+ function followUps(config) {
12609
+ const items = [];
12610
+ if (Object.keys(config.aliases).length > 0) items.push(`Set ${style.code("experiments.tsconfigPaths: true")} in app.config — Metro ignores tsconfig paths without it.`);
12611
+ const cssImport = config.aliases.styles ? `${config.aliases.styles}/${basename(config.app.resolved.css)}` : `./${short(config.app.resolved.root, config.app.resolved.css)}`;
12612
+ items.push(`Import ${style.code(`"${cssImport}"`)} as the first statement of your root layout — without it every component renders unstyled.`);
12613
+ const providerImport = `${config.aliases.ui ?? `./${short(config.app.resolved.root, config.directories.ui)}`}/provider`;
12614
+ items.push(`Wrap the app root in ${style.code("<DelacourProvider>")} from ${style.code(`"${providerImport}"`)} — presses do nothing without it.`);
12615
+ items.push(`${style.code("theme.css")} is the file to edit — replace it with the theme.css tab from https://ui.delacour.co.nz/theme, or paste a shadcn or tweakcn globals.css over it and run ${style.code("delacour theme")}.`);
12616
+ return items;
12617
+ }
12562
12618
  function warnAboutStack(project, output) {
12563
12619
  if (!project.packageJson) {
12564
12620
  output.warn("No package.json here. Run this inside your Expo app.");
@@ -28411,7 +28467,7 @@ function report(result, output) {
28411
28467
  }
28412
28468
  //#endregion
28413
28469
  //#region src/index.ts
28414
- const program = new Command().name("delacour").description("Add React Native components to your Expo app. You own the code.").version("0.1.0-alpha.1");
28470
+ const program = new Command().name("delacour").description("Add React Native components to your Expo app. You own the code.").version("0.1.0-alpha.2");
28415
28471
  /** Shared by every command that reads the registry. */
28416
28472
  function withRegistryOptions(command) {
28417
28473
  return command.addOption(new Option("--registry <url>", "registry to read from: a URL, github:owner/repo, or a local path")).addOption(new Option("--ref <ref>", "git ref to read the registry at").default(DEFAULT_REGISTRY_REF)).addOption(new Option("--offline", "use only what is already cached")).addOption(new Option("-c, --cwd <path>", "directory to work in").default(process.cwd()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delacour",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Add React Native components to your Expo app. You own the code.",
5
5
  "type": "module",
6
6
  "license": "MIT",