@ng-native/nx 0.17.1 → 0.18.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/application/index.cjs +29 -13
- package/application/schema.json +10 -1
- package/files/AGENTS.md +4 -0
- package/files/src/main.ts +5 -3
- package/migrations.json +6 -1
- package/native-app.cjs +5 -5
- package/package.json +5 -4
package/application/index.cjs
CHANGED
|
@@ -42,9 +42,14 @@ function names(tree, options) {
|
|
|
42
42
|
// A workspace package is named for the workspace's scope, as Nx names its own.
|
|
43
43
|
const projectName = workspaces && scope ? `${scope}/${name}` : name;
|
|
44
44
|
const bundleIdentifier = options.bundleIdentifier ?? native.bundleIdentifier(name, scope);
|
|
45
|
-
|
|
45
|
+
const lint = options.linter === 'eslint';
|
|
46
|
+
return { directory, name, projectName, workspaces, bundleIdentifier, lint };
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/** The app's dev dependencies: with `--linter=eslint`, ESLint and the plugin as well. */
|
|
50
|
+
const devDependencies = (lint) =>
|
|
51
|
+
lint ? { ...native.devDependencies, ...native.lintDevDependencies } : native.devDependencies;
|
|
52
|
+
|
|
48
53
|
/**
|
|
49
54
|
* Whether the workspace's `tsconfig.base.json` is where its libraries' path aliases live, or will.
|
|
50
55
|
* With package-manager workspaces, only when it has a `paths` list, as the `angular-monorepo`
|
|
@@ -125,7 +130,7 @@ function metroPort(tree) {
|
|
|
125
130
|
return port;
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
function targets(directory, port, oneCopy) {
|
|
133
|
+
function targets(directory, port, oneCopy, lint) {
|
|
129
134
|
const run = (command) => ({ executor: 'nx:run-commands', options: { cwd: directory, command } });
|
|
130
135
|
const own = port === DEFAULT_PORT ? '' : ` --port ${port}`;
|
|
131
136
|
// `expo run:ios` and `expo run:android` build the port into the app, so a later app's has to be
|
|
@@ -143,6 +148,7 @@ function targets(directory, port, oneCopy) {
|
|
|
143
148
|
outputs: ['{projectRoot}/.angular-native'],
|
|
144
149
|
},
|
|
145
150
|
test: { ...run('vitest run'), cache: true, inputs: ['default', '^production'] },
|
|
151
|
+
...(lint && { lint: { ...run('eslint .'), cache: true, inputs: ['default', '^production'] } }),
|
|
146
152
|
start: { ...run(`expo start${own}`), continuous: true },
|
|
147
153
|
serve: { ...run(`expo start${own}`), continuous: true },
|
|
148
154
|
...runs,
|
|
@@ -150,13 +156,14 @@ function targets(directory, port, oneCopy) {
|
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
/** The Commands section of the app's `AGENTS.md`, as this workspace runs them. */
|
|
153
|
-
function commands(name) {
|
|
159
|
+
function commands(name, lint) {
|
|
154
160
|
const rows = [
|
|
155
161
|
[`nx start ${name}`, 'Metro; press i or a for a simulator, or scan the QR code with Expo Go'],
|
|
156
162
|
[`nx run ${name}:run-ios`, 'the iOS simulator'],
|
|
157
163
|
[`nx run ${name}:run-android`, 'the Android emulator'],
|
|
158
164
|
[`nx test ${name}`, 'Vitest, in Node against a fake native layer: no simulator needed'],
|
|
159
165
|
[`nx typecheck ${name}`, 'TypeScript, without emitting'],
|
|
166
|
+
...(lint ? [[`nx lint ${name}`, 'ESLint: what compiles but does nothing on a device']] : []),
|
|
160
167
|
];
|
|
161
168
|
const width = Math.max(...rows.map(([command]) => command.length)) + 2;
|
|
162
169
|
return [
|
|
@@ -165,7 +172,10 @@ function commands(name) {
|
|
|
165
172
|
'```',
|
|
166
173
|
'',
|
|
167
174
|
'`src/main.ts` mounts the root component, `src/app/app.ts`. ' +
|
|
168
|
-
|
|
175
|
+
(lint
|
|
176
|
+
? `Run \`nx test ${name}\`, \`nx typecheck ${name}\` and \`nx lint ${name}\` after a change; ` +
|
|
177
|
+
'all three are fast.'
|
|
178
|
+
: `Run \`nx test ${name}\` and \`nx typecheck ${name}\` after a change; both are fast.`),
|
|
169
179
|
].join('\n');
|
|
170
180
|
}
|
|
171
181
|
|
|
@@ -186,14 +196,15 @@ function listed(manifest) {
|
|
|
186
196
|
return Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
|
|
187
197
|
}
|
|
188
198
|
|
|
189
|
-
async function writeFiles(tree, { directory, projectName, workspaces, bundleIdentifier }) {
|
|
199
|
+
async function writeFiles(tree, { directory, projectName, workspaces, bundleIdentifier, lint }) {
|
|
190
200
|
const file = (name, content) => tree.write(joinPathFragments(directory, name), content);
|
|
191
201
|
for (const name of native.SOURCE_FILES) file(name, native.sourceFile(name));
|
|
192
202
|
file('app.json', native.appJson(projectName, bundleIdentifier));
|
|
193
|
-
file('AGENTS.md', native.agentsFile(commands(projectName)));
|
|
203
|
+
file('AGENTS.md', native.agentsFile(commands(projectName, lint)));
|
|
194
204
|
file('CLAUDE.md', '@AGENTS.md\n');
|
|
195
205
|
file('metro.config.js', native.METRO_CONFIG);
|
|
196
206
|
file('.gitignore', native.GITIGNORE);
|
|
207
|
+
if (lint) file('eslint.config.mjs', native.ESLINT_CONFIG);
|
|
197
208
|
|
|
198
209
|
const base = hasPathAliases(tree, workspaces);
|
|
199
210
|
const workspaceBase = base ? `${offsetFromRoot(directory)}tsconfig.base.json` : undefined;
|
|
@@ -210,7 +221,7 @@ async function writeFiles(tree, { directory, projectName, workspaces, bundleIden
|
|
|
210
221
|
const root = readJson(tree, 'package.json');
|
|
211
222
|
Object.assign(manifest, {
|
|
212
223
|
dependencies: await appDependencies(tree, root, native.dependencies),
|
|
213
|
-
devDependencies: await appDependencies(tree, root,
|
|
224
|
+
devDependencies: await appDependencies(tree, root, devDependencies(lint)),
|
|
214
225
|
});
|
|
215
226
|
} else {
|
|
216
227
|
manifest.dependencies = native.prebuildPins(root);
|
|
@@ -228,11 +239,11 @@ function ignoreExpo(tree) {
|
|
|
228
239
|
|
|
229
240
|
/**
|
|
230
241
|
* @param {import('@nx/devkit').Tree} tree
|
|
231
|
-
* @param {{ directory: string, name?: string, bundleIdentifier?: string, tags?: string, skipInstall?: boolean, skipFormat?: boolean }} options
|
|
242
|
+
* @param {{ directory: string, name?: string, bundleIdentifier?: string, tags?: string, linter?: 'eslint' | 'none', skipInstall?: boolean, skipFormat?: boolean }} options
|
|
232
243
|
*/
|
|
233
244
|
async function application(tree, options) {
|
|
234
245
|
const resolved = names(tree, options);
|
|
235
|
-
const { directory, projectName, workspaces } = resolved;
|
|
246
|
+
const { directory, projectName, workspaces, lint } = resolved;
|
|
236
247
|
const problem = native.bundleIdentifierProblem(resolved.bundleIdentifier);
|
|
237
248
|
if (problem) throw new Error(problem);
|
|
238
249
|
if (tree.exists(joinPathFragments(directory, 'package.json'))) {
|
|
@@ -244,12 +255,15 @@ async function application(tree, options) {
|
|
|
244
255
|
if (workspaces) {
|
|
245
256
|
includeInWorkspaces(tree, directory);
|
|
246
257
|
} else {
|
|
247
|
-
|
|
258
|
+
const linter = options.linter;
|
|
259
|
+
for (const problem of native.conflicts(readJson(tree, 'package.json'), { linter })) {
|
|
260
|
+
logger.warn(problem);
|
|
261
|
+
}
|
|
248
262
|
const existing = listed(readJson(tree, 'package.json'));
|
|
249
263
|
addDependenciesToPackageJson(
|
|
250
264
|
tree,
|
|
251
265
|
await asSaved(tree, native.dependencies, existing),
|
|
252
|
-
await asSaved(tree,
|
|
266
|
+
await asSaved(tree, devDependencies(lint), existing),
|
|
253
267
|
'package.json',
|
|
254
268
|
true,
|
|
255
269
|
);
|
|
@@ -270,7 +284,7 @@ async function application(tree, options) {
|
|
|
270
284
|
.split(',')
|
|
271
285
|
.map((tag) => tag.trim())
|
|
272
286
|
.filter(Boolean),
|
|
273
|
-
targets: targets(directory, port, linksLibraries(tree, workspaces)),
|
|
287
|
+
targets: targets(directory, port, linksLibraries(tree, workspaces), lint),
|
|
274
288
|
});
|
|
275
289
|
|
|
276
290
|
ignoreExpo(tree);
|
|
@@ -279,7 +293,9 @@ async function application(tree, options) {
|
|
|
279
293
|
if (!options.skipFormat) await formatFiles(tree);
|
|
280
294
|
logger.info(
|
|
281
295
|
`Created ${directory}. nx start ${projectName} runs Metro; nx run ${projectName}:run-ios, ` +
|
|
282
|
-
`nx test ${projectName}, nx typecheck ${projectName},
|
|
296
|
+
`nx test ${projectName}, nx typecheck ${projectName}, ` +
|
|
297
|
+
(lint ? `nx lint ${projectName}, ` : '') +
|
|
298
|
+
`nx export ${projectName}.`,
|
|
283
299
|
);
|
|
284
300
|
if (options.skipInstall) return initTask;
|
|
285
301
|
return runTasksInSerial(initTask, () => installPackagesTask(tree, true));
|
package/application/schema.json
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
"directory": {
|
|
9
9
|
"type": "string",
|
|
10
10
|
"description": "Where the app goes, relative to the workspace root: apps/mobile.",
|
|
11
|
-
"$default": {
|
|
11
|
+
"$default": {
|
|
12
|
+
"$source": "argv",
|
|
13
|
+
"index": 0
|
|
14
|
+
},
|
|
12
15
|
"x-prompt": "Which directory should the app be created in?"
|
|
13
16
|
},
|
|
14
17
|
"name": {
|
|
@@ -24,6 +27,12 @@
|
|
|
24
27
|
"type": "string",
|
|
25
28
|
"description": "Comma-separated tags for the project."
|
|
26
29
|
},
|
|
30
|
+
"linter": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "eslint adds an eslint.config.mjs with @ng-native/eslint-plugin's recommended rules, its dependencies and a lint target.",
|
|
33
|
+
"enum": ["eslint", "none"],
|
|
34
|
+
"default": "none"
|
|
35
|
+
},
|
|
27
36
|
"skipInstall": {
|
|
28
37
|
"type": "boolean",
|
|
29
38
|
"description": "Write the files without installing.",
|
package/files/AGENTS.md
CHANGED
|
@@ -83,3 +83,7 @@ change; both are fast.
|
|
|
83
83
|
`getByText` or `getByTestId` (which matches `nativeID` and `testID`), and act with
|
|
84
84
|
`userEvent.setup().press(...)` or `.type(...)`, all from `@ng-native/testing`. Tests run in Node
|
|
85
85
|
with no simulator, so write one alongside a change.
|
|
86
|
+
|
|
87
|
+
A service that depends on the platform injects `OS` and `OS_VERSION` from `@ng-native/device`
|
|
88
|
+
rather than importing `Platform` from `react-native`, which a test cannot load. A test provides
|
|
89
|
+
the values it wants: `{ provide: OS, useValue: 'android' }`.
|
package/files/src/main.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { App } from './app/app.ts';
|
|
|
10
10
|
|
|
11
11
|
registerPlatformComponents(Platform.OS);
|
|
12
12
|
|
|
13
|
-
AppRegistry.registerRunnable('main', ({ rootTag }
|
|
13
|
+
AppRegistry.registerRunnable('main', ({ rootTag }) => {
|
|
14
14
|
const app = mount(Number(rootTag), App, getFabricUIManager(), {
|
|
15
15
|
// Colours, as the integers the platform wants.
|
|
16
16
|
processColor,
|
|
@@ -26,6 +26,8 @@ AppRegistry.registerRunnable('main', ({ rootTag }: { rootTag: number | string })
|
|
|
26
26
|
|
|
27
27
|
// Re-resolves the conditions when the device rotates or the theme changes. A rotation dirties
|
|
28
28
|
// no component and no binding, so without this nothing re-renders and `dark:` stops following
|
|
29
|
-
// the system switch.
|
|
30
|
-
|
|
29
|
+
// the system switch. It stops with the app, which Android destroys when it recreates the
|
|
30
|
+
// activity, so the app mounted in its place is the only one still watching.
|
|
31
|
+
const stopWatching = watchConditions(app.engine);
|
|
32
|
+
app.applicationRef.onDestroy(stopWatching);
|
|
31
33
|
});
|
package/migrations.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generators": {
|
|
3
3
|
"sync-app-versions": {
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.0",
|
|
5
5
|
"description": "Move the @ng-native packages each project's own package.json lists to the new version.",
|
|
6
6
|
"implementation": "./migrations/run.cjs#sync-app-versions"
|
|
7
7
|
},
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"version": "0.17.1",
|
|
40
40
|
"description": "Give each Angular Native project in angular.json a typecheck target, run by ngc.",
|
|
41
41
|
"implementation": "./migrations/run.cjs#native-typecheck-target"
|
|
42
|
+
},
|
|
43
|
+
"stop-watching-conditions": {
|
|
44
|
+
"version": "0.17.2",
|
|
45
|
+
"description": "Keep what watchConditions returns in each app's entry point and stop it as the app is destroyed.",
|
|
46
|
+
"implementation": "./migrations/run.cjs#stop-watching-conditions"
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
}
|
package/native-app.cjs
CHANGED
|
@@ -33,10 +33,10 @@ const { dependencies, devDependencies, accepted } = shared;
|
|
|
33
33
|
*/
|
|
34
34
|
const expoCompanions = {
|
|
35
35
|
'react-dom': dependencies.react,
|
|
36
|
-
'@expo/cli': '^
|
|
36
|
+
'@expo/cli': '^58.1.14',
|
|
37
37
|
'@babel/runtime': '^7.20.0',
|
|
38
38
|
'@babel/core': devDependencies['@babel/core'],
|
|
39
|
-
'@expo/metro': '~
|
|
39
|
+
'@expo/metro': '~58.0.0',
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -98,13 +98,13 @@ function tsconfig(workspaceBase, conditions = []) {
|
|
|
98
98
|
const customConditions = ['react-native', ...conditions.filter((c) => c !== 'react-native')];
|
|
99
99
|
if (!workspaceBase) {
|
|
100
100
|
if (customConditions.length > 1) compilerOptions.customConditions = customConditions;
|
|
101
|
-
return { extends: 'expo/tsconfig.base', compilerOptions, angularCompilerOptions };
|
|
101
|
+
return { extends: 'expo/tsconfig.base.json', compilerOptions, angularCompilerOptions };
|
|
102
102
|
}
|
|
103
103
|
// The workspace's base is written for a web build or for emitting declarations, and wins over
|
|
104
104
|
// Expo's where they overlap. These are Expo's, restored: without `DOM` and `ESNext` the framework
|
|
105
105
|
// packages' own source, which the app compiles, fails to typecheck.
|
|
106
106
|
return {
|
|
107
|
-
extends: ['expo/tsconfig.base', workspaceBase],
|
|
107
|
+
extends: ['expo/tsconfig.base.json', workspaceBase],
|
|
108
108
|
compilerOptions: {
|
|
109
109
|
...compilerOptions,
|
|
110
110
|
noEmit: true,
|
|
@@ -122,7 +122,7 @@ function tsconfig(workspaceBase, conditions = []) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** The `@ng-native` packages with entry points beside their main one, each published in `dist`. */
|
|
125
|
-
const WITH_ENTRY_POINTS = ['components', 'expo', 'platform', 'web'];
|
|
125
|
+
const WITH_ENTRY_POINTS = ['components', 'expo', 'platform', 'web', 'web-compat'];
|
|
126
126
|
|
|
127
127
|
/** `nx typecheck` in a workspace whose libraries are the package manager's links. */
|
|
128
128
|
const TYPECHECK_ONE_COPY = TYPECHECK.replace('tsconfig.json', 'tsconfig.typecheck.json');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-native/nx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Nx generators for Angular Native: nx add, an Expo app project whose targets run it, and native libraries and components.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"@ng-native/analog",
|
|
24
24
|
"@ng-native/components",
|
|
25
25
|
"@ng-native/device",
|
|
26
|
+
"@ng-native/eslint-plugin",
|
|
26
27
|
"@ng-native/expo",
|
|
27
28
|
"@ng-native/fabric",
|
|
28
29
|
"@ng-native/icons",
|
|
@@ -39,13 +40,13 @@
|
|
|
39
40
|
]
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
|
-
"@ng-native/migrate": "0.
|
|
43
|
-
"@nx/devkit": "^23.0.0",
|
|
43
|
+
"@ng-native/migrate": "0.18.0",
|
|
44
|
+
"@nx/devkit": "^23.0.0 || ^24.0.0",
|
|
44
45
|
"minimatch": "^10.0.0",
|
|
45
46
|
"semver": "^7.7.0"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|
|
48
|
-
"nx": "^23.0.0"
|
|
49
|
+
"nx": "^23.0.0 || ^24.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/semver": "^7.7.0",
|