@_linked/cli 1.11.0 → 1.12.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.
- package/CHANGELOG.md +69 -0
- package/README.md +48 -0
- package/defaults/app-react-native/README.md +56 -0
- package/defaults/app-react-native/apps/mobile/App.tsx +26 -0
- package/defaults/app-react-native/apps/mobile/__tests__/linkedDefaults.test.tsx +65 -0
- package/defaults/app-react-native/apps/mobile/__tests__/shapes.test.ts +27 -0
- package/defaults/app-react-native/apps/mobile/app.json +14 -0
- package/defaults/app-react-native/apps/mobile/gitignore.template +41 -0
- package/defaults/app-react-native/apps/mobile/index.ts +8 -0
- package/defaults/app-react-native/apps/mobile/metro.config.js +17 -0
- package/defaults/app-react-native/apps/mobile/package.json +41 -0
- package/defaults/app-react-native/apps/mobile/src/shell/linkedDefaults.tsx +10 -0
- package/defaults/app-react-native/apps/mobile/tsconfig.json +8 -0
- package/defaults/app-react-native/gitignore.template +6 -0
- package/defaults/app-react-native/package.json +14 -0
- package/defaults/app-react-native/packages/app-shapes/package.json +10 -0
- package/defaults/app-react-native/packages/app-shapes/src/index.ts +2 -0
- package/defaults/app-react-native/packages/app-shapes/src/package.ts +10 -0
- package/defaults/app-react-native/packages/app-shapes/src/shapes/Example.ts +17 -0
- package/defaults/app-react-native/packages/app-shapes/tsconfig.json +12 -0
- package/defaults/app-react-native/services/api/README.md +4 -0
- package/defaults/app-react-native/services/api/package.json +5 -0
- package/defaults/package/npmignore.template +6 -0
- package/defaults/package/src/backend.ts +5 -0
- package/defaults/package/src/data/example-ontology.json +10 -0
- package/defaults/package/src/data/example-ontology.json.d.ts +1 -0
- package/defaults/package/src/index.ts +8 -0
- package/defaults/package/src/ontologies/example-ontology.ts +42 -0
- package/defaults/package/src/package.ts +16 -0
- package/defaults/package/src/shapes/index.ts +5 -0
- package/defaults/package/src/types.ts +4 -0
- package/defaults/package/tsconfig.json +17 -0
- package/defaults/setup-publish/dual-branch/github/workflows/publish.yml +29 -15
- package/lib/esm/cli-methods.d.ts +34 -0
- package/lib/esm/cli-methods.d.ts.map +1 -1
- package/lib/esm/cli-methods.js +286 -11
- package/lib/esm/cli-methods.js.map +1 -1
- package/lib/esm/cli.d.ts.map +1 -1
- package/lib/esm/cli.js +9 -1
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/commands/setup-publish.js +23 -2
- package/lib/esm/commands/setup-publish.js.map +1 -1
- package/lib/esm/utils/packageSetup.d.ts +19 -0
- package/lib/esm/utils/packageSetup.d.ts.map +1 -0
- package/lib/esm/utils/packageSetup.js +23 -0
- package/lib/esm/utils/packageSetup.js.map +1 -0
- package/lib/esm/utils/shippedDotfiles.d.ts +2 -0
- package/lib/esm/utils/shippedDotfiles.d.ts.map +1 -0
- package/lib/esm/utils/shippedDotfiles.js +21 -0
- package/lib/esm/utils/shippedDotfiles.js.map +1 -0
- package/lib/esm/vite-config.d.ts +14 -0
- package/lib/esm/vite-config.d.ts.map +1 -1
- package/lib/esm/vite-config.js +7 -3
- package/lib/esm/vite-config.js.map +1 -1
- package/package.json +9 -1
- package/.changeset/README.md +0 -20
- package/.changeset/config.json +0 -14
- package/.github/workflows/changeset-check.yml +0 -44
- package/.github/workflows/ci.yml +0 -51
- package/.github/workflows/publish.yml +0 -77
- package/.prettierignore +0 -4
- package/.prettierrc.json +0 -24
- package/babel.config.cjs +0 -11
- package/docs/architecture/vite-runtime-config.md +0 -57
- package/jest.config.cjs +0 -33
- package/lib-template/cjs/package.json +0 -3
- package/lib-template/esm/package.json +0 -3
- package/test-results/.last-run.json +0 -6
- package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/error-context.md +0 -16
- package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/trace.zip +0 -0
- package/tests/e2e/create-app.spec.ts +0 -157
- package/tests/unit/safeYarn.test.ts +0 -55
- package/tsconfig-cjs.json +0 -10
- package/tsconfig-esm.json +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#77](https://github.com/linked-cm/cli/pull/77) [`38cf9a7`](https://github.com/linked-cm/cli/commit/38cf9a77dbeab632e617846a51d75c41a6f6edda) Thanks [@flyon](https://github.com/flyon)! - Add `linked create-app <name> --template react-native`, which scaffolds a Linked React Native monorepo.
|
|
8
|
+
|
|
9
|
+
The template ships in the CLI as `defaults/app-react-native` and needs no git clone. It generates an npm
|
|
10
|
+
workspaces monorepo:
|
|
11
|
+
|
|
12
|
+
- `apps/mobile`: an Expo SDK 57 / React Native 0.86 / React 19.2 app, with its Metro and Jest configuration
|
|
13
|
+
already set up for `@_linked/*`, and render defaults that replace `@_linked/react`'s `<svg>` loader and error
|
|
14
|
+
elements. Tests cover shape registration and those defaults.
|
|
15
|
+
- `packages/<prefix>-shapes`: a Linked shapes package, consumed as TypeScript source.
|
|
16
|
+
- `services/api`: a backend stub.
|
|
17
|
+
|
|
18
|
+
The app's identity is stamped into named places only: the shapes package name, the monorepo name, and
|
|
19
|
+
`app.json` `name`, `slug` and the reverse-domain `ios.bundleIdentifier`. The `${…}` substitution the web template
|
|
20
|
+
uses is not applied, so template literals in the sources stay intact. Dependencies install with npm. `ios/` is
|
|
21
|
+
generated by `expo run:ios` and is not committed. `--template web` stays the default and is unchanged.
|
|
22
|
+
|
|
23
|
+
Before writing anything, the React Native path checks that `--app-prefix` matches `^[a-z][a-z0-9-]*$` (it becomes
|
|
24
|
+
a directory, an npm package name, a bundle-ID label and part of a Jest pattern) and refuses a target folder that
|
|
25
|
+
exists and is not empty. A failed `npm install` keeps the files but fails the command, and `create-app` exits
|
|
26
|
+
non-zero whenever it fails. The template pins `expo-dev-client` and `@react-native/jest-preset` explicitly, so it
|
|
27
|
+
does not depend on npm auto-installing peers.
|
|
28
|
+
|
|
29
|
+
Also fixes `linked create-package` when the CLI is installed from npm. npm honoured the template's nested
|
|
30
|
+
`.npmignore` when packing the CLI, so `defaults/package/src` was missing from the published tarball and new
|
|
31
|
+
packages had no `src/`. Yarn-packed builds were unaffected. The template now ships the file as
|
|
32
|
+
`npmignore.template`, and `create-package` renames it back. `.gitignore` files in templates get the same
|
|
33
|
+
treatment.
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- [#77](https://github.com/linked-cm/cli/pull/77) [`32791fd`](https://github.com/linked-cm/cli/commit/32791fd80b3c2b8849a35dfa4483785b601cc8b2) Thanks [@flyon](https://github.com/flyon)! - Publish only what the CLI needs at runtime: a `files` allowlist (`lib`, `defaults`, README, CHANGELOG, LICENSE) keeps test fixtures, Playwright `test-results/` and `package-lock.json` out of the tarball.
|
|
38
|
+
|
|
39
|
+
Fix `linked create-package` failing at its final step. With Yarn 2+ the new package was installed with Plug'n'Play (no `node_modules`), so `npm exec linked build` could not find the binary. Scaffolded packages now get `nodeLinker: node-modules` under Yarn 2+, the initial build runs through the CLI that is already executing, and a failed install or build sets a non-zero exit code.
|
|
40
|
+
|
|
41
|
+
## 1.11.2
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- [#74](https://github.com/linked-cm/cli/pull/74) [`b6969b1`](https://github.com/linked-cm/cli/commit/b6969b174a7e0c571a318aff7b69c601d97f3e41) Thanks [@flyon](https://github.com/flyon)! - Make `linked create-package` produce a package that builds.
|
|
46
|
+
|
|
47
|
+
Four things stopped it, none of which the scaffolded output survived:
|
|
48
|
+
|
|
49
|
+
- The final step ran `npm exec lincd build`. `lincd` is the **old** CLI's binary, and its
|
|
50
|
+
compiled output imports `lincd/lib/esm/utils/LinkedFileStorage.js`, which the current
|
|
51
|
+
workspace does not ship. The result was `Could not install dependencies` and no build.
|
|
52
|
+
It now runs `linked build`.
|
|
53
|
+
- The template's `src/package.ts` destructured `linkedComponent` from `linkedPackage()`.
|
|
54
|
+
That is not part of core's `LinkedPackageObject` — component binding lives in
|
|
55
|
+
`@_linked/react` — so every new package failed to compile on its own boilerplate.
|
|
56
|
+
- The ontology template imported `NamedNode` from bare `@_linked/core`. The symbol no
|
|
57
|
+
longer exists there, and the bare specifier cannot resolve under the template's
|
|
58
|
+
`moduleResolution: "node"` anyway. It now uses `NodeReferenceValue`, matching how the
|
|
59
|
+
framework's own ontologies are written.
|
|
60
|
+
- `src/index.ts` had `import './types'` with no extension, which the CLI's own import check
|
|
61
|
+
rejects.
|
|
62
|
+
|
|
63
|
+
Verified by scaffolding a package and building it: ESM, CJS, dual-package output, import
|
|
64
|
+
check and dependency check all pass with no edits.
|
|
65
|
+
|
|
66
|
+
## 1.11.1
|
|
67
|
+
|
|
68
|
+
### Patch Changes
|
|
69
|
+
|
|
70
|
+
- [#68](https://github.com/linked-cm/cli/pull/68) [`441372f`](https://github.com/linked-cm/cli/commit/441372f82706abb7b89d08d5b3b4348df04c5554) Thanks [@flyon](https://github.com/flyon)! - `linked create-app` now stamps the chosen app identity into the scaffolded app. The app template ships without `${var}` scaffold placeholders (so a raw clone boots on defaults), so create-app writes the real per-app values after cloning: `.env`/`.env.example` (`APP_NAME`, `APP_PREFIX`), `package.json` `name`/`displayName`, the runtime `linkedPackage(...)` id in `src/package.ts`, and the pm2 / VS Code launch names. Both the `--app-name` flag and the interactive prompt now produce an app that carries the chosen name (previously every scaffolded app inherited the template defaults).
|
|
71
|
+
|
|
3
72
|
## 1.11.0
|
|
4
73
|
|
|
5
74
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -125,6 +125,54 @@ Conventions:
|
|
|
125
125
|
- The backend dispatcher is registry-free (dynamic `await import(entry.store)`). The frontend hardcodes each `new StoreClass(config)` because webpack can't bundle dynamic imports of arbitrary npm specifiers.
|
|
126
126
|
- Each store class accepts a single config-object constructor argument — `new FusekiStore({ endpoint, credentials? })`, `new BackendAPIStore({ name?, id? })`.
|
|
127
127
|
|
|
128
|
+
### `linked create-app --template react-native`
|
|
129
|
+
|
|
130
|
+
`linked create-app <name> --template react-native` (with the usual `--app-name`, `--app-prefix`, `--app-domain`,
|
|
131
|
+
`--skip-install`) copies `defaults/app-react-native/` instead of cloning the web template. The result is an npm
|
|
132
|
+
workspaces monorepo for Expo SDK 57 / React Native 0.86 / React 19.2:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
<name>/
|
|
136
|
+
package.json workspaces (enumerated) + overrides pinning @_linked/react's react to 19.2.3
|
|
137
|
+
.gitignore node_modules/, packages/*, !packages/<prefix>-shapes/
|
|
138
|
+
apps/mobile/ Expo app: app.json, App.tsx, index.ts, metro.config.js, jest config in package.json
|
|
139
|
+
.gitignore includes /ios and /android
|
|
140
|
+
src/shell/linkedDefaults.tsx React Native loader/error defaults for @_linked/react
|
|
141
|
+
__tests__/ shapes.test.ts, linkedDefaults.test.tsx
|
|
142
|
+
packages/<prefix>-shapes/ Linked shapes package, TypeScript source, extensionless imports, no build step
|
|
143
|
+
services/api/ backend stub
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
What the scaffold does:
|
|
147
|
+
|
|
148
|
+
- Refuses, before writing anything, a prefix that does not match `^[a-z][a-z0-9-]*$`, and a target folder that
|
|
149
|
+
exists and is not empty.
|
|
150
|
+
- Renames `gitignore.template` files to `.gitignore`. npm strips real `.gitignore` files from the CLI tarball.
|
|
151
|
+
- Renames `packages/app-shapes` to `packages/<prefix>-shapes`, and replaces the literal `app-shapes` token in
|
|
152
|
+
every text file.
|
|
153
|
+
- Sets the root `name` to `<hyphen-name>-monorepo`, and `app.json` `expo.name`, `expo.slug` and
|
|
154
|
+
`expo.ios.bundleIdentifier`. The bundle ID is the reversed domain plus the prefix, e.g.
|
|
155
|
+
`com.formaestudios.formae`.
|
|
156
|
+
- Never runs the `${…}` placeholder substitution, so template literals in the sources are left alone.
|
|
157
|
+
- Installs with `npm install` (never Yarn) unless `--skip-install` is given. If the install fails, the files
|
|
158
|
+
stay and the command exits non-zero; run `npm install` in the folder to retry.
|
|
159
|
+
|
|
160
|
+
`ios/` (and `android/`) are **generated, not committed**: `npx expo run:ios` (or `npx expo prebuild`) creates
|
|
161
|
+
them from `app.json`, and `apps/mobile/.gitignore` ignores them.
|
|
162
|
+
|
|
163
|
+
Quick commands in the generated app:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npm ls react # exactly one react@19.2.3
|
|
167
|
+
npm test -w apps/mobile # Jest: shape registration + render defaults
|
|
168
|
+
cd apps/mobile && npx expo export --platform ios --output-dir /tmp/export # Metro bundle check
|
|
169
|
+
cd apps/mobile && npx expo run:ios # build the dev client and run on the iOS Simulator
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Tests for this template in this repo: `yarn test:unit` covers the scaffold offline. It scaffolds the fixture in
|
|
173
|
+
`tests/fixtures/app-react-native-min` and the real template. `npm run test:template` builds nothing itself. It
|
|
174
|
+
runs the built CLI with install, then the three checks above. It needs network and takes several minutes.
|
|
175
|
+
|
|
128
176
|
## Repository
|
|
129
177
|
|
|
130
178
|
`linked-cm/cli` on GitHub. License: MPL-2.0.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Linked React Native monorepo
|
|
2
|
+
|
|
3
|
+
An npm-workspaces monorepo for a [Linked](https://github.com/linked-cm) app on Expo SDK 57 / React Native 0.86 /
|
|
4
|
+
React 19.2.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
apps/mobile/ Expo app (continuous native generation: ios/ is generated, not committed)
|
|
8
|
+
src/shell/ app shell, including the React Native render defaults for @_linked/react
|
|
9
|
+
__tests__/ shape registration and render-default tests
|
|
10
|
+
packages/app-shapes/ Linked shapes package, consumed as TypeScript source (no build step)
|
|
11
|
+
services/api/ backend stub
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Run
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
npm test -w apps/mobile # Jest
|
|
19
|
+
cd apps/mobile && npx expo run:ios # generates ios/, builds a development build with expo-dev-client, starts Metro
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`expo-dev-client` is a dependency of `apps/mobile`, so `expo run:ios` produces a custom dev client (not Expo Go),
|
|
23
|
+
which is what native modules need.
|
|
24
|
+
|
|
25
|
+
Requires Node `^22.13 || >=24`. iOS builds need macOS 26.2+ and Xcode 26.4+ with the iOS platform component
|
|
26
|
+
installed.
|
|
27
|
+
|
|
28
|
+
## Quick checks
|
|
29
|
+
|
|
30
|
+
| Command | What it proves |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `npm ls react` (root) | exactly one React 19.2.3 in the tree |
|
|
33
|
+
| `npm test -w apps/mobile` | shapes register at runtime; Linked components render React Native elements while loading and on error |
|
|
34
|
+
| `cd apps/mobile && npx expo export --platform ios --output-dir /tmp/export` | Metro resolves `@_linked/*` (including subpaths) and the workspace packages |
|
|
35
|
+
|
|
36
|
+
## React Native specifics
|
|
37
|
+
|
|
38
|
+
- **One React.** `@_linked/react` still declares a React 18 peer, so the root `package.json` `overrides` pins its
|
|
39
|
+
`react` to 19.2.3. Remove the override once the peer range is widened upstream. Do not use
|
|
40
|
+
`--legacy-peer-deps`: it would hide a duplicate React.
|
|
41
|
+
- **Workspaces are enumerated**, never globbed, so foreign repositories checked out under `packages/` are not
|
|
42
|
+
adopted. `.gitignore` uses `packages/*` plus a negation per workspace package (`packages/` cannot be negated).
|
|
43
|
+
- **Metro** (`apps/mobile/metro.config.js`) watches the workspace root and searches both `node_modules`
|
|
44
|
+
directories. It deliberately does **not** set `disableHierarchicalLookup`, which breaks npm's nested
|
|
45
|
+
dependencies (e.g. `expo-asset` under `expo`). `unstable_conditionNames` covers `require()` of `@_linked/*`
|
|
46
|
+
subpaths.
|
|
47
|
+
- **Jest** maps `@_linked/*` to its ESM build with a scoped `moduleNameMapper`. A global
|
|
48
|
+
`customExportConditions` would break `@babel/runtime`. Add every workspace package to
|
|
49
|
+
`transformIgnorePatterns`.
|
|
50
|
+
- **Render defaults.** `@_linked/react`'s built-in loader and error elements are `<svg>`, which crash on React
|
|
51
|
+
Native. `apps/mobile/src/shell/linkedDefaults.tsx` replaces them app-wide and must be the first import of
|
|
52
|
+
`App.tsx`. `__tests__/linkedDefaults.test.tsx` fails if the defaults are not applied.
|
|
53
|
+
- **Shapes package imports are extensionless** (`./shapes/Example`, not `./shapes/Example.js`): Metro does not
|
|
54
|
+
map `.js` specifiers to `.ts` source. For the same reason the package is never built with `linked build`,
|
|
55
|
+
whose import check rejects extensionless imports.
|
|
56
|
+
- **Decorators** work with the stock `babel-preset-expo`; no extra Babel plugin is needed.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Must stay the first import: replaces @_linked/react's <svg> loader and error defaults app-wide.
|
|
2
|
+
import './src/shell/linkedDefaults';
|
|
3
|
+
|
|
4
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import { getShapeClass } from '@_linked/core/utils/ShapeClass';
|
|
6
|
+
import { Example } from 'app-shapes';
|
|
7
|
+
|
|
8
|
+
export default function App() {
|
|
9
|
+
const registered = getShapeClass(Example.shape.id) === Example;
|
|
10
|
+
return (
|
|
11
|
+
<View style={styles.container}>
|
|
12
|
+
{/* @linkedShape adds static packageName at runtime; it is not on the class type. */}
|
|
13
|
+
<Text>{(Example as any).packageName}</Text>
|
|
14
|
+
<Text>{registered ? 'shapes registered' : 'shapes not registered'}</Text>
|
|
15
|
+
</View>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const styles = StyleSheet.create({
|
|
20
|
+
container: {
|
|
21
|
+
flex: 1,
|
|
22
|
+
backgroundColor: '#fff',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Asserts the cause of the React Native crash in @_linked/react's default loader and error
|
|
2
|
+
// elements: they render DOM host elements (<svg>), which have no native view config.
|
|
3
|
+
// Removing the import below must make both tests fail with Received: ["svg", ...].
|
|
4
|
+
import '../src/shell/linkedDefaults';
|
|
5
|
+
|
|
6
|
+
import { act, render, screen } from '@testing-library/react-native';
|
|
7
|
+
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
8
|
+
import { setQueryDispatch } from '@_linked/core/queries/queryDispatch';
|
|
9
|
+
import { linkedComponent } from '@_linked/react';
|
|
10
|
+
import { Text } from 'react-native';
|
|
11
|
+
import { Example } from 'app-shapes';
|
|
12
|
+
|
|
13
|
+
type Mode = 'loading' | 'error';
|
|
14
|
+
let mode: Mode = 'loading';
|
|
15
|
+
|
|
16
|
+
// A stub store: the query never settles while loading, and rejects in error mode.
|
|
17
|
+
const stubStore: any = {
|
|
18
|
+
selectQuery: () =>
|
|
19
|
+
mode === 'loading' ? new Promise(() => {}) : Promise.reject(new Error('stub store: query failed')),
|
|
20
|
+
askQuery: async () => false,
|
|
21
|
+
createQuery: async () => ({}),
|
|
22
|
+
updateQuery: async () => ({}),
|
|
23
|
+
deleteQuery: async () => ({}),
|
|
24
|
+
};
|
|
25
|
+
LinkedStorage.setDefaultDataset(stubStore);
|
|
26
|
+
setQueryDispatch(stubStore);
|
|
27
|
+
|
|
28
|
+
const ExampleLabel = linkedComponent(Example.select((s) => s.label), ({ label }: any) => (
|
|
29
|
+
<Text>{label}</Text>
|
|
30
|
+
));
|
|
31
|
+
|
|
32
|
+
type Node = { type: string; children: (Node | string)[] | null };
|
|
33
|
+
|
|
34
|
+
function lowercaseHostTypes(tree: Node | Node[] | null): string[] {
|
|
35
|
+
const found: string[] = [];
|
|
36
|
+
const visit = (node: Node | string | null) => {
|
|
37
|
+
if (!node || typeof node === 'string') return;
|
|
38
|
+
if (/^[a-z]/.test(node.type)) found.push(node.type);
|
|
39
|
+
node.children?.forEach(visit);
|
|
40
|
+
};
|
|
41
|
+
(Array.isArray(tree) ? tree : [tree]).forEach(visit);
|
|
42
|
+
return found;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
46
|
+
|
|
47
|
+
describe('Linked render defaults on React Native', () => {
|
|
48
|
+
test('a loading component renders only React Native elements', async () => {
|
|
49
|
+
mode = 'loading';
|
|
50
|
+
await render(<ExampleLabel of={{ id: 'urn:example:1' }} />);
|
|
51
|
+
|
|
52
|
+
expect(lowercaseHostTypes(screen.toJSON() as any)).toEqual([]);
|
|
53
|
+
expect(screen.getByTestId('linked-loader')).toBeTruthy();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('a failed component renders only React Native elements', async () => {
|
|
57
|
+
mode = 'error';
|
|
58
|
+
await render(<ExampleLabel of={{ id: 'urn:example:1' }} />);
|
|
59
|
+
// Let the rejected query settle and the component re-render into its error state.
|
|
60
|
+
await act(flush);
|
|
61
|
+
|
|
62
|
+
expect(lowercaseHostTypes(screen.toJSON() as any)).toEqual([]);
|
|
63
|
+
expect(screen.getByTestId('linked-error')).toBeTruthy();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Shape } from '@_linked/core/shapes/Shape';
|
|
2
|
+
import { getShapeClass } from '@_linked/core/utils/ShapeClass';
|
|
3
|
+
import { Example } from 'app-shapes';
|
|
4
|
+
|
|
5
|
+
// A @linkedShape-decorated class gains static packageName, targetClass and shape;
|
|
6
|
+
// shape.id is the key into the global shape registry.
|
|
7
|
+
describe('shapes package', () => {
|
|
8
|
+
test('exports the shape class', () => {
|
|
9
|
+
expect(typeof Example).toBe('function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('the shape class extends Shape', () => {
|
|
13
|
+
expect(Example.prototype instanceof Shape).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('the shape belongs to the shapes package', () => {
|
|
17
|
+
expect((Example as any).packageName).toBe('app-shapes');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('the shape is registered at runtime', () => {
|
|
21
|
+
expect(getShapeClass((Example as any).shape.id)).toBe(Example);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('the decorated properties are registered', () => {
|
|
25
|
+
expect([...(Example as any).shape.propertyShapes].length).toBeGreaterThan(0);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
node_modules/
|
|
5
|
+
|
|
6
|
+
# Expo
|
|
7
|
+
.expo/
|
|
8
|
+
dist/
|
|
9
|
+
web-build/
|
|
10
|
+
expo-env.d.ts
|
|
11
|
+
|
|
12
|
+
# Native
|
|
13
|
+
.kotlin/
|
|
14
|
+
*.orig.*
|
|
15
|
+
*.jks
|
|
16
|
+
*.p8
|
|
17
|
+
*.p12
|
|
18
|
+
*.key
|
|
19
|
+
*.mobileprovision
|
|
20
|
+
|
|
21
|
+
# Metro
|
|
22
|
+
.metro-health-check*
|
|
23
|
+
|
|
24
|
+
# debug
|
|
25
|
+
npm-debug.*
|
|
26
|
+
yarn-debug.*
|
|
27
|
+
yarn-error.*
|
|
28
|
+
|
|
29
|
+
# macOS
|
|
30
|
+
.DS_Store
|
|
31
|
+
*.pem
|
|
32
|
+
|
|
33
|
+
# local env files
|
|
34
|
+
.env*.local
|
|
35
|
+
|
|
36
|
+
# typescript
|
|
37
|
+
*.tsbuildinfo
|
|
38
|
+
|
|
39
|
+
# generated native folders
|
|
40
|
+
/ios
|
|
41
|
+
/android
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { registerRootComponent } from 'expo';
|
|
2
|
+
|
|
3
|
+
import App from './App';
|
|
4
|
+
|
|
5
|
+
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
|
6
|
+
// It also ensures that whether you load the app in Expo Go or in a native build,
|
|
7
|
+
// the environment is set up appropriately
|
|
8
|
+
registerRootComponent(App);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const projectRoot = __dirname;
|
|
5
|
+
const workspaceRoot = path.resolve(projectRoot, '../..');
|
|
6
|
+
const config = getDefaultConfig(projectRoot);
|
|
7
|
+
|
|
8
|
+
config.watchFolders = [workspaceRoot];
|
|
9
|
+
config.resolver.nodeModulesPaths = [
|
|
10
|
+
path.resolve(projectRoot, 'node_modules'),
|
|
11
|
+
path.resolve(workspaceRoot, 'node_modules'),
|
|
12
|
+
];
|
|
13
|
+
// Covers the require() path into @_linked/* subpaths. Do not add disableHierarchicalLookup:
|
|
14
|
+
// it breaks npm workspaces, which nest some dependencies (e.g. expo-asset under expo).
|
|
15
|
+
config.resolver.unstable_conditionNames = ['react-native', 'import', 'require'];
|
|
16
|
+
|
|
17
|
+
module.exports = config;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mobile",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "expo start",
|
|
8
|
+
"ios": "expo run:ios",
|
|
9
|
+
"android": "expo run:android",
|
|
10
|
+
"test": "jest"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@_linked/core": "2.18.0",
|
|
14
|
+
"@_linked/react": "1.4.2",
|
|
15
|
+
"app-shapes": "*",
|
|
16
|
+
"expo": "57.0.22",
|
|
17
|
+
"expo-dev-client": "57.0.19",
|
|
18
|
+
"react": "19.2.3",
|
|
19
|
+
"react-native": "0.86.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@react-native/jest-preset": "0.86.3",
|
|
23
|
+
"@testing-library/react-native": "14.0.1",
|
|
24
|
+
"@types/jest": "29.5.14",
|
|
25
|
+
"@types/react": "19.2.18",
|
|
26
|
+
"jest": "29.7.0",
|
|
27
|
+
"jest-expo": "57.0.5",
|
|
28
|
+
"test-renderer": "1.2.0",
|
|
29
|
+
"typescript": "6.0.3"
|
|
30
|
+
},
|
|
31
|
+
"jest": {
|
|
32
|
+
"preset": "jest-expo",
|
|
33
|
+
"transformIgnorePatterns": [
|
|
34
|
+
"node_modules/(?!((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|@_linked/.*|app-shapes))"
|
|
35
|
+
],
|
|
36
|
+
"moduleNameMapper": {
|
|
37
|
+
"^@_linked/([^/]+)$": "<rootDir>/../../node_modules/@_linked/$1/lib/esm/index.js",
|
|
38
|
+
"^@_linked/([^/]+)/(.*)$": "<rootDir>/../../node_modules/@_linked/$1/lib/esm/$2.js"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @_linked/react's built-in loader and error elements render <svg>, which crashes on React Native
|
|
2
|
+
// ("View config getter callback for component 'svg'"). Replace them app-wide. App.tsx imports this
|
|
3
|
+
// module first. Delete it once @_linked/react ships React Native defaults.
|
|
4
|
+
import { LinkedComponentDefaults } from '@_linked/react';
|
|
5
|
+
import { ActivityIndicator, Text } from 'react-native';
|
|
6
|
+
|
|
7
|
+
LinkedComponentDefaults.loader = <ActivityIndicator testID="linked-loader" />;
|
|
8
|
+
LinkedComponentDefaults.errorElement = (
|
|
9
|
+
<Text testID="linked-error" accessibilityRole="alert">Failed to load</Text>
|
|
10
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {Shape} from '@_linked/core/shapes/Shape';
|
|
2
|
+
import {literalProperty} from '@_linked/core/shapes/SHACL';
|
|
3
|
+
import {createNameSpace} from '@_linked/core/utils/NameSpace';
|
|
4
|
+
// Extensionless on purpose: Metro does not map `.js` specifiers to `.ts` source.
|
|
5
|
+
import {linkedShape} from '../package';
|
|
6
|
+
|
|
7
|
+
const ns = createNameSpace('https://example.com/app/');
|
|
8
|
+
|
|
9
|
+
@linkedShape
|
|
10
|
+
export class Example extends Shape {
|
|
11
|
+
static targetClass = ns('Example');
|
|
12
|
+
|
|
13
|
+
@literalProperty({path: ns('label'), maxCount: 1})
|
|
14
|
+
get label(): string {
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"dc": "http://purl.org/dc/elements/1.1/",
|
|
4
|
+
"owl": "http://www.w3.org/2002/07/owl#",
|
|
5
|
+
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
|
6
|
+
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
|
7
|
+
"${hyphen_name}": "${uri_base}"
|
|
8
|
+
},
|
|
9
|
+
"@graph": []
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var json: string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {NodeReferenceValue} from '@_linked/core/utils/NodeReference';
|
|
2
|
+
import {createNameSpace} from '@_linked/core/utils/NameSpace';
|
|
3
|
+
import {linkedOntology} from '../package.js';
|
|
4
|
+
//import all the exports of this file as one variable called _this (we need this at the end)
|
|
5
|
+
import * as _this from './${hyphen_name}.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load the data of this ontology into memory, thus adding the properties of the entities of this ontology to the local graph.
|
|
9
|
+
*/
|
|
10
|
+
export var loadData = () => {
|
|
11
|
+
if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
|
|
12
|
+
// CommonJS import
|
|
13
|
+
return import('../data/${hyphen_name}.json');
|
|
14
|
+
} else {
|
|
15
|
+
// ESM import
|
|
16
|
+
//@ts-ignore
|
|
17
|
+
return import('../data/${hyphen_name}.json',{ with: { type: "json" } }).then((data) => data.defauilt);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The namespace of this ontology, which can be used to create NamedNodes with IRIs not listed in this file
|
|
23
|
+
*/
|
|
24
|
+
export var ns = createNameSpace('${uri_base}');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A reference to the ontology itself.
|
|
28
|
+
*/
|
|
29
|
+
export var _self: NodeReferenceValue = ns('');
|
|
30
|
+
|
|
31
|
+
//Every class and property of this ontology, each exported as a node reference.
|
|
32
|
+
// export var ExampleClass: NodeReferenceValue = ns('ExampleClass');
|
|
33
|
+
// export var exampleProperty: NodeReferenceValue = ns('exampleProperty');
|
|
34
|
+
|
|
35
|
+
//An extra grouping object so all the entities can be accessed from the prefix/name
|
|
36
|
+
export const ${camel_name} = {
|
|
37
|
+
// ExampleClass,
|
|
38
|
+
// exampleProperty,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//Registers this ontology to LINCD.JS, so that data loading can be automated amongst other things
|
|
42
|
+
linkedOntology(_this, ns, '${hyphen_name}', loadData, '../data/${hyphen_name}.json');
|