@_linked/cli 1.11.1 → 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.
Files changed (74) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +48 -0
  3. package/defaults/app-react-native/README.md +56 -0
  4. package/defaults/app-react-native/apps/mobile/App.tsx +26 -0
  5. package/defaults/app-react-native/apps/mobile/__tests__/linkedDefaults.test.tsx +65 -0
  6. package/defaults/app-react-native/apps/mobile/__tests__/shapes.test.ts +27 -0
  7. package/defaults/app-react-native/apps/mobile/app.json +14 -0
  8. package/defaults/app-react-native/apps/mobile/gitignore.template +41 -0
  9. package/defaults/app-react-native/apps/mobile/index.ts +8 -0
  10. package/defaults/app-react-native/apps/mobile/metro.config.js +17 -0
  11. package/defaults/app-react-native/apps/mobile/package.json +41 -0
  12. package/defaults/app-react-native/apps/mobile/src/shell/linkedDefaults.tsx +10 -0
  13. package/defaults/app-react-native/apps/mobile/tsconfig.json +8 -0
  14. package/defaults/app-react-native/gitignore.template +6 -0
  15. package/defaults/app-react-native/package.json +14 -0
  16. package/defaults/app-react-native/packages/app-shapes/package.json +10 -0
  17. package/defaults/app-react-native/packages/app-shapes/src/index.ts +2 -0
  18. package/defaults/app-react-native/packages/app-shapes/src/package.ts +10 -0
  19. package/defaults/app-react-native/packages/app-shapes/src/shapes/Example.ts +17 -0
  20. package/defaults/app-react-native/packages/app-shapes/tsconfig.json +12 -0
  21. package/defaults/app-react-native/services/api/README.md +4 -0
  22. package/defaults/app-react-native/services/api/package.json +5 -0
  23. package/defaults/package/npmignore.template +6 -0
  24. package/defaults/package/src/backend.ts +5 -0
  25. package/defaults/package/src/data/example-ontology.json +10 -0
  26. package/defaults/package/src/data/example-ontology.json.d.ts +1 -0
  27. package/defaults/package/src/index.ts +8 -0
  28. package/defaults/package/src/ontologies/example-ontology.ts +42 -0
  29. package/defaults/package/src/package.ts +16 -0
  30. package/defaults/package/src/shapes/index.ts +5 -0
  31. package/defaults/package/src/types.ts +4 -0
  32. package/defaults/package/tsconfig.json +17 -0
  33. package/defaults/setup-publish/dual-branch/github/workflows/publish.yml +29 -15
  34. package/lib/esm/cli-methods.d.ts +34 -0
  35. package/lib/esm/cli-methods.d.ts.map +1 -1
  36. package/lib/esm/cli-methods.js +224 -11
  37. package/lib/esm/cli-methods.js.map +1 -1
  38. package/lib/esm/cli.d.ts.map +1 -1
  39. package/lib/esm/cli.js +9 -1
  40. package/lib/esm/cli.js.map +1 -1
  41. package/lib/esm/commands/setup-publish.js +23 -2
  42. package/lib/esm/commands/setup-publish.js.map +1 -1
  43. package/lib/esm/utils/packageSetup.d.ts +19 -0
  44. package/lib/esm/utils/packageSetup.d.ts.map +1 -0
  45. package/lib/esm/utils/packageSetup.js +23 -0
  46. package/lib/esm/utils/packageSetup.js.map +1 -0
  47. package/lib/esm/utils/shippedDotfiles.d.ts +2 -0
  48. package/lib/esm/utils/shippedDotfiles.d.ts.map +1 -0
  49. package/lib/esm/utils/shippedDotfiles.js +21 -0
  50. package/lib/esm/utils/shippedDotfiles.js.map +1 -0
  51. package/lib/esm/vite-config.d.ts +14 -0
  52. package/lib/esm/vite-config.d.ts.map +1 -1
  53. package/lib/esm/vite-config.js +7 -3
  54. package/lib/esm/vite-config.js.map +1 -1
  55. package/package.json +9 -1
  56. package/.changeset/README.md +0 -20
  57. package/.changeset/config.json +0 -14
  58. package/.github/workflows/changeset-check.yml +0 -44
  59. package/.github/workflows/ci.yml +0 -51
  60. package/.github/workflows/publish.yml +0 -77
  61. package/.prettierignore +0 -4
  62. package/.prettierrc.json +0 -24
  63. package/babel.config.cjs +0 -11
  64. package/docs/architecture/vite-runtime-config.md +0 -57
  65. package/jest.config.cjs +0 -33
  66. package/lib-template/cjs/package.json +0 -3
  67. package/lib-template/esm/package.json +0 -3
  68. package/test-results/.last-run.json +0 -6
  69. package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/error-context.md +0 -16
  70. package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/trace.zip +0 -0
  71. package/tests/e2e/create-app.spec.ts +0 -157
  72. package/tests/unit/safeYarn.test.ts +0 -55
  73. package/tsconfig-cjs.json +0 -10
  74. package/tsconfig-esm.json +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
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
+
3
66
  ## 1.11.1
4
67
 
5
68
  ### Patch 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,14 @@
1
+ {
2
+ "expo": {
3
+ "name": "App",
4
+ "slug": "app",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "userInterfaceStyle": "light",
8
+ "platforms": ["ios"],
9
+ "ios": {
10
+ "supportsTablet": true,
11
+ "bundleIdentifier": "com.example.app"
12
+ }
13
+ }
14
+ }
@@ -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,8 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "experimentalDecorators": true,
6
+ "types": ["jest"]
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ # Workspace packages are enumerated; `packages/*` (not `packages/`) so the negation below works.
3
+ packages/*
4
+ !packages/app-shapes/
5
+ .expo/
6
+ dist/
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "app-monorepo",
3
+ "private": true,
4
+ "workspaces": [
5
+ "apps/mobile",
6
+ "services/api",
7
+ "packages/app-shapes"
8
+ ],
9
+ "overrides": {
10
+ "@_linked/react": {
11
+ "react": "19.2.3"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "app-shapes",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "dependencies": {
8
+ "@_linked/core": "2.18.0"
9
+ }
10
+ }
@@ -0,0 +1,2 @@
1
+ import './package';
2
+ export * from './shapes/Example';
@@ -0,0 +1,10 @@
1
+ import {linkedPackage} from '@_linked/core/utils/Package';
2
+
3
+ export const {
4
+ linkedShape,
5
+ registerPackageExport,
6
+ registerPackageModule,
7
+ packageExports,
8
+ packageName,
9
+ getPackageShape,
10
+ } = linkedPackage('app-shapes');
@@ -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,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "experimentalDecorators": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true
10
+ },
11
+ "include": ["src"]
12
+ }
@@ -0,0 +1,4 @@
1
+ # api
2
+
3
+ Placeholder for the backend service. It is a workspace so the monorepo layout is stable; the server, its
4
+ storage configuration and its dependency on the shapes package are added when the backend is built.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "api",
3
+ "private": true,
4
+ "version": "0.0.0"
5
+ }
@@ -0,0 +1,6 @@
1
+ **/node_modules
2
+ src
3
+ .idea
4
+ Gruntfile.js
5
+ tsconfig.json
6
+ tsconfig-es5.json
@@ -0,0 +1,5 @@
1
+ // Register this package's shapes on the backend boot path so they materialize into app-data.
2
+ import './shapes/index.js';
3
+
4
+ //export all your providers from this package here
5
+ //Format: export * from './shapes/SomeShapeProvider.js';
@@ -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,8 @@
1
+ import './types.js';
2
+ import './ontologies/${hyphen_name}.js';
3
+
4
+ //SHAPES FIRST — all shapes register via the barrel
5
+ import './shapes/index.js';
6
+
7
+ //THEN COMPONENTS
8
+ // import './components/YourComponent';
@@ -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');
@@ -0,0 +1,16 @@
1
+ import {linkedPackage} from '@_linked/core/utils/Package';
2
+
3
+ // `linkedComponent` is deliberately not destructured here: it is not part of core's
4
+ // `LinkedPackageObject`. Component binding lives in `@_linked/react` — import
5
+ // `linkedComponent` from `@_linked/react/utils/LinkedComponent` in a package that needs it,
6
+ // so a package with no UI does not depend on React to declare its shapes.
7
+ export const {
8
+ linkedShape,
9
+ linkedUtil,
10
+ linkedOntology,
11
+ registerPackageExport,
12
+ packageExports,
13
+ packageName,
14
+ getPackageShape
15
+
16
+ } = linkedPackage('${package_name}');