@ng-native/nx 0.13.0 → 0.14.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/files/AGENTS.md +1 -1
- package/migrations.json +6 -1
- package/package.json +2 -2
- package/tailwind/index.cjs +58 -10
package/files/AGENTS.md
CHANGED
|
@@ -29,7 +29,7 @@ change; both are fast.
|
|
|
29
29
|
- **Element names are lowercase:** `<view>`, `<text>`, `<pressable>`, `<scroll-view>`,
|
|
30
30
|
`<text-input>`, `<image>`, `<switch>`, `<safe-area-view>`, `<virtual-list>`, `<modal>`. Each is
|
|
31
31
|
imported from `@ng-native/components` into the component's `imports`, like any Angular
|
|
32
|
-
component: `imports: [View, Text]`. `<View>`
|
|
32
|
+
component: `imports: [View, Text]`. `<View>` matches no component and fails the build, and an element used
|
|
33
33
|
without its import renders as a plain view.
|
|
34
34
|
- **There is no DOM.** No `document`, no `window`, no `<button>`, `<a>`, `<input>` or `<img>`, no
|
|
35
35
|
`@angular/platform-browser`, and no `@angular/animations`. Use the native elements above. HTML's
|
package/migrations.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generators": {
|
|
3
3
|
"sync-app-versions": {
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.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
|
},
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"version": "0.3.0",
|
|
20
20
|
"description": "Note each import of reactNative or ReactNative from @ng-native/device, which no longer exports them.",
|
|
21
21
|
"implementation": "./migrations/run.cjs#device-react-native-import"
|
|
22
|
+
},
|
|
23
|
+
"engine-node-tag": {
|
|
24
|
+
"version": "0.14.0",
|
|
25
|
+
"description": "Read an engine node's element tag as tag rather than name.",
|
|
26
|
+
"implementation": "./migrations/run.cjs#engine-node-tag"
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-native/nx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
]
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@ng-native/migrate": "0.
|
|
41
|
+
"@ng-native/migrate": "0.14.0",
|
|
42
42
|
"@nx/devkit": "^23.0.0",
|
|
43
43
|
"minimatch": "^10.0.0",
|
|
44
44
|
"semver": "^7.7.0"
|
package/tailwind/index.cjs
CHANGED
|
@@ -55,6 +55,7 @@ const POSTCSS_CONFIGS = [
|
|
|
55
55
|
...['', '.json', '.yaml', '.yml', '.js', '.cjs', '.mjs'].map((ext) => `.postcssrc${ext}`),
|
|
56
56
|
];
|
|
57
57
|
const V3_DIRECTIVES = ['@tailwind base;', '@tailwind components;', '@tailwind utilities;'];
|
|
58
|
+
const COMMONJS_NAMES = ['require', 'module', 'exports', '__dirname', '__filename'];
|
|
58
59
|
const PRESET_ALLOWED = String.raw`'^.*/tailwind\\.preset\\.cjs$'`;
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -245,9 +246,11 @@ function writeV4Stylesheet(tree, file, base, themes) {
|
|
|
245
246
|
const lines =
|
|
246
247
|
existing === null ? ["/* The app's Tailwind entry, without preflight. */", ...base] : [];
|
|
247
248
|
const text = existing ?? '';
|
|
249
|
+
// Compared in one quote: a formatter may have rewritten the file since.
|
|
250
|
+
const written = text.replace(/"/g, "'");
|
|
248
251
|
const imports = themes
|
|
249
252
|
.map((theme) => `@import '${path.relative(path.dirname(file), theme)}';`)
|
|
250
|
-
.filter((line) => !
|
|
253
|
+
.filter((line) => !written.includes(line));
|
|
251
254
|
if (existing !== null && imports.length === 0) return;
|
|
252
255
|
const last = [...text.matchAll(/^@import .+;$/gm)].at(-1);
|
|
253
256
|
const at = last ? last.index + last[0].length + 1 : text.length;
|
|
@@ -262,13 +265,13 @@ function writeV4Stylesheet(tree, file, base, themes) {
|
|
|
262
265
|
function writeWebV4Stylesheet(tree, file, themes) {
|
|
263
266
|
const existing = tree.read(file, 'utf-8');
|
|
264
267
|
if (existing !== null && !existing.includes('@ng-native/tailwind/web.css')) {
|
|
265
|
-
if (existing
|
|
268
|
+
if (importsTailwind(existing)) {
|
|
266
269
|
return byHand(
|
|
267
270
|
file,
|
|
268
271
|
`import '@ng-native/tailwind/web.css' after Tailwind's theme and utilities.`,
|
|
269
272
|
);
|
|
270
273
|
}
|
|
271
|
-
tree.write(file,
|
|
274
|
+
tree.write(file, prependedTo(existing, V4_WEB_IMPORTS));
|
|
272
275
|
}
|
|
273
276
|
writeV4Stylesheet(tree, file, V4_WEB_IMPORTS, themes);
|
|
274
277
|
}
|
|
@@ -323,9 +326,43 @@ function linkStylesheet(tree, root, stylesheet) {
|
|
|
323
326
|
tree.write(file, `${html.slice(0, head.index)}${indent}${link}\n${html.slice(head.index)}`);
|
|
324
327
|
}
|
|
325
328
|
|
|
329
|
+
function withoutComments(css) {
|
|
330
|
+
return css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Whether `css` imports Tailwind 4, in a quote or `url()`; a comment that names it does not. */
|
|
334
|
+
function importsTailwind(css) {
|
|
335
|
+
return /@import\s+(?:url\(\s*)?["']tailwindcss(?:\/[^"']*)?["']/.test(withoutComments(css));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** `css` with `lines` ahead of its rules, after a leading `@charset`, which must stay first. */
|
|
339
|
+
function prependedTo(css, lines) {
|
|
340
|
+
const charset = /^@charset\s+(["'])[^"'\n]*\1\s*;[ \t]*\r?\n?/.exec(css)?.[0] ?? '';
|
|
341
|
+
const head = charset && !charset.endsWith('\n') ? `${charset}\n` : charset;
|
|
342
|
+
return `${head}${lines.join('\n')}\n\n${css.slice(charset.length)}`;
|
|
343
|
+
}
|
|
344
|
+
|
|
326
345
|
function writeV3Stylesheet(tree, root) {
|
|
327
346
|
const file = joinPathFragments(root, 'src/styles.css');
|
|
328
|
-
|
|
347
|
+
const existing = tree.read(file, 'utf-8');
|
|
348
|
+
if (existing === null) tree.write(file, `${V3_DIRECTIVES.join('\n')}\n`);
|
|
349
|
+
// One the app has already, without Tailwind, gets the directives ahead of its rules.
|
|
350
|
+
else if (!/@tailwind\s/.test(withoutComments(existing))) {
|
|
351
|
+
tree.write(file, prependedTo(existing, V3_DIRECTIVES));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* The device build's Tailwind 4 stylesheet. One the app has already, without Tailwind, gets the
|
|
357
|
+
* native preset's imports ahead of its rules, where CSS requires an `@import`: Metro is given
|
|
358
|
+
* this file as Tailwind's input, and with no Tailwind in it every class would draw unstyled.
|
|
359
|
+
*/
|
|
360
|
+
function writeDeviceV4Stylesheet(tree, file, themes) {
|
|
361
|
+
const existing = tree.read(file, 'utf-8');
|
|
362
|
+
if (existing !== null && !importsTailwind(existing)) {
|
|
363
|
+
tree.write(file, prependedTo(existing, V4_IMPORTS));
|
|
364
|
+
}
|
|
365
|
+
writeV4Stylesheet(tree, file, V4_IMPORTS, themes);
|
|
329
366
|
}
|
|
330
367
|
|
|
331
368
|
/**
|
|
@@ -345,8 +382,9 @@ function writeV3Config(tree, file, presets, options) {
|
|
|
345
382
|
}
|
|
346
383
|
return;
|
|
347
384
|
}
|
|
348
|
-
|
|
349
|
-
|
|
385
|
+
// In either quote: a formatter may have rewritten the file since.
|
|
386
|
+
const had = [...existing.matchAll(/require\((['"])([^'"]+\/tailwind\.preset\.cjs)\1\)/g)].map(
|
|
387
|
+
(match) => match[2],
|
|
350
388
|
);
|
|
351
389
|
wanted.unshift(...had.filter((preset) => !wanted.includes(preset)));
|
|
352
390
|
}
|
|
@@ -464,10 +502,21 @@ function setUpWeb(tree, root, vite, version, libraries, besideDevice) {
|
|
|
464
502
|
linkStylesheet(tree, root, file);
|
|
465
503
|
}
|
|
466
504
|
|
|
467
|
-
/**
|
|
505
|
+
/**
|
|
506
|
+
* A JavaScript name for a library's preset: `ui`, `sharedUi`. A directory named for a word the
|
|
507
|
+
* language keeps, `switch` or `default`, gets `switchPreset`.
|
|
508
|
+
*/
|
|
468
509
|
function identifier(name) {
|
|
469
510
|
const camel = name.replace(/[^a-zA-Z0-9]+(.)/g, (_, next) => next.toUpperCase());
|
|
470
|
-
|
|
511
|
+
const named = /^[a-zA-Z_$]/.test(camel) ? camel : `_${camel}`;
|
|
512
|
+
// The config runs in CommonJS's wrapper, which binds these names as parameters.
|
|
513
|
+
if (COMMONJS_NAMES.includes(named)) return `${named}Preset`;
|
|
514
|
+
try {
|
|
515
|
+
new Function(`'use strict'; const ${named} = 0;`);
|
|
516
|
+
return named;
|
|
517
|
+
} catch {
|
|
518
|
+
return `${named}Preset`;
|
|
519
|
+
}
|
|
471
520
|
}
|
|
472
521
|
|
|
473
522
|
/** Adds the preset's file name to `@nx/enforce-module-boundaries`' `allow` list, where there is one. */
|
|
@@ -550,10 +599,9 @@ async function tailwind(tree, options) {
|
|
|
550
599
|
libraries.map((library) => libraryPreset(tree, library)),
|
|
551
600
|
);
|
|
552
601
|
} else {
|
|
553
|
-
|
|
602
|
+
writeDeviceV4Stylesheet(
|
|
554
603
|
tree,
|
|
555
604
|
joinPathFragments(app.root, 'src/styles.css'),
|
|
556
|
-
V4_IMPORTS,
|
|
557
605
|
libraries.map((library) => libraryTheme(tree, library)),
|
|
558
606
|
);
|
|
559
607
|
}
|