@blotch/angular-lynx 22.2.3 → 22.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blotch/angular-lynx",
3
- "version": "22.2.3",
3
+ "version": "22.2.4",
4
4
  "description": "Angular renderer for the Lynx cross-platform native UI framework",
5
5
  "homepage": "https://github.com/Blotch-Smart-Frames/AngularLynx#readme",
6
6
  "bugs": {
@@ -1,23 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const schematics_1 = require("@angular-devkit/schematics");
4
- const dependency_1 = require("@schematics/angular/utility/dependency");
5
4
  const workspace_1 = require("@schematics/angular/utility/workspace");
6
- const versions_1 = require("../ng-add/versions");
5
+ const tailwind_1 = require("../utility/tailwind");
7
6
  /**
8
7
  * Adds Tailwind CSS to an existing AngularLynx project. This is split out
9
8
  * from `ng-add` because users may opt out of Tailwind at scaffold time
10
9
  * (`ng add @blotch/angular-lynx --tailwind=false`) but later change their
11
10
  * minds — running this schematic later is the supported recovery path.
12
11
  *
13
- * Three steps, each idempotent:
14
- * 1. **tailwind.config.ts** — written with `@lynx-js/tailwind-preset` as
15
- * the only preset. The preset narrows Tailwind's class output to the
16
- * subset Lynx's CSS engine can parse (no logical properties, no
17
- * `:has()` selectors, no advanced color functions).
18
- * 2. **styles.css/scss** — replaced with the Tailwind base + utilities
19
- * layer directives (no components layer; see ng-add for why).
20
- * 3. **package.json deps** — adds tailwindcss + the Lynx preset.
12
+ * It sets up the Tailwind version the project already depends on, else
13
+ * Tailwind 4, or the one `--tailwind-version` asks for:
14
+ *
15
+ * - **Tailwind 4**, as Angular CLI's own Tailwind schematic does: the
16
+ * stylesheet imports Tailwind (without its Preflight reset), and
17
+ * `.postcssrc.json` runs `@tailwindcss/postcss`. The build plugin rewrites
18
+ * the CSS it writes into CSS Lynx reads.
19
+ * - **Tailwind 3**, with `@lynx-js/tailwind-preset`, which narrows Tailwind's
20
+ * output to the CSS Lynx reads: `tailwind.config.ts` and the `@tailwind`
21
+ * directives.
22
+ *
23
+ * Each step is idempotent.
21
24
  */
22
25
  exports.default = (options) => async (tree) => {
23
26
  const workspace = await (0, workspace_1.getWorkspace)(tree);
@@ -33,68 +36,5 @@ exports.default = (options) => async (tree) => {
33
36
  }
34
37
  const sourceRoot = (project.sourceRoot ?? `${project.root}/src`);
35
38
  const projectRoot = project.root;
36
- return (0, schematics_1.chain)([
37
- addTailwindConfig(projectRoot),
38
- updateStylesCss(sourceRoot),
39
- addTailwindDependencies(),
40
- ]);
41
- };
42
- const addTailwindConfig = (projectRoot) => {
43
- return (tree) => {
44
- const configPath = `${projectRoot ? projectRoot + '/' : ''}tailwind.config.ts`;
45
- // The build runs Tailwind with this `content`, so it has to take in both
46
- // inline templates (.ts) and the .html templates `ng generate component`
47
- // writes by default, as in Angular's own Tailwind setup.
48
- const content = `import type { Config } from 'tailwindcss';
49
- import preset from '@lynx-js/tailwind-preset';
50
-
51
- const config: Config = {
52
- content: ['./src/**/*.{html,ts}'],
53
- presets: [preset],
54
- };
55
-
56
- export default config;
57
- `;
58
- if (tree.exists(configPath)) {
59
- tree.overwrite(configPath, content);
60
- }
61
- else {
62
- tree.create(configPath, content);
63
- }
64
- };
65
- };
66
- const updateStylesCss = (sourceRoot) => {
67
- return (tree) => {
68
- const cssPath = `${sourceRoot}/styles.css`;
69
- const scssPath = `${sourceRoot}/styles.scss`;
70
- // Prefer styles.scss if it exists (the user picked SCSS at `ng new` time)
71
- // — overwriting it with .css content would break their other imports.
72
- const targetPath = tree.exists(scssPath) ? scssPath : cssPath;
73
- // Only @tailwind base + utilities — no @tailwind components. Lynx's CSS
74
- // engine doesn't parse many component-layer reset rules (focus-visible
75
- // pseudo-class, form input pseudo-classes), which would produce
76
- // diagnostic noise at build time. The base layer ships with the Lynx
77
- // preset's own compatibility-safe normalize rules.
78
- const content = `@tailwind base;
79
- @tailwind utilities;
80
- `;
81
- if (tree.exists(targetPath)) {
82
- tree.overwrite(targetPath, content);
83
- }
84
- else {
85
- tree.create(cssPath, content);
86
- }
87
- };
88
- };
89
- const addTailwindDependencies = () => {
90
- return (0, schematics_1.chain)([
91
- (0, dependency_1.addDependency)('tailwindcss', versions_1.VERSIONS.tailwindcss, {
92
- type: dependency_1.DependencyType.Dev,
93
- install: dependency_1.InstallBehavior.Auto,
94
- }),
95
- (0, dependency_1.addDependency)('@lynx-js/tailwind-preset', versions_1.VERSIONS.tailwindPreset, {
96
- type: dependency_1.DependencyType.Dev,
97
- install: dependency_1.InstallBehavior.Auto,
98
- }),
99
- ]);
39
+ return (0, tailwind_1.setUpTailwind)({ projectName, projectRoot, sourceRoot }, (0, tailwind_1.tailwindVersionFor)(tree, options.tailwindVersion));
100
40
  };
@@ -4,7 +4,7 @@
4
4
  "title": "AngularLynx add-tailwind schematic",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
7
- "description": "Adds Tailwind CSS with @lynx-js/tailwind-preset to an existing AngularLynx project.",
7
+ "description": "Adds Tailwind CSS to an existing AngularLynx project: Tailwind 4 by default, or Tailwind 3 with @lynx-js/tailwind-preset.",
8
8
  "properties": {
9
9
  "project": {
10
10
  "type": "string",
@@ -12,6 +12,11 @@
12
12
  "$default": {
13
13
  "$source": "projectName"
14
14
  }
15
+ },
16
+ "tailwindVersion": {
17
+ "type": ["string", "number"],
18
+ "enum": ["4", "3", 4, 3],
19
+ "description": "The Tailwind CSS major version to set up: 4 (with @tailwindcss/postcss), or 3 (with @lynx-js/tailwind-preset). Defaults to the version the project already depends on, else 4."
15
20
  }
16
21
  },
17
22
  "required": []
@@ -6,7 +6,7 @@
6
6
  "schema": "./ng-add/schema.json"
7
7
  },
8
8
  "add-tailwind": {
9
- "description": "Adds Tailwind CSS with @lynx-js/tailwind-preset to an existing project",
9
+ "description": "Adds Tailwind CSS to an existing project: Tailwind 4, or Tailwind 3 with @lynx-js/tailwind-preset",
10
10
  "factory": "./add-tailwind/index",
11
11
  "schema": "./add-tailwind/schema.json"
12
12
  },
@@ -5,6 +5,7 @@ const dependency_1 = require("@schematics/angular/utility/dependency");
5
5
  const json_file_1 = require("@schematics/angular/utility/json-file");
6
6
  const workspace_1 = require("@schematics/angular/utility/workspace");
7
7
  const dev_logs_gitignore_1 = require("../utility/dev-logs-gitignore");
8
+ const tailwind_1 = require("../utility/tailwind");
8
9
  const extract_i18n_target_1 = require("../utility/extract-i18n-target");
9
10
  const versions_1 = require("./versions");
10
11
  /**
@@ -14,8 +15,8 @@ const versions_1 = require("./versions");
14
15
  * `ng test` and `ng extract-i18n` at the AngularLynx builders and
15
16
  * `ng generate` at its schematics, git-ignores the `.dev-logs` file that
16
17
  * `ng serve` writes `LynxLogger`'s output to, and optionally sets up Tailwind
17
- * with the Lynx preset. Everything else stays as `ng new` left it: the app
18
- * needs no build config besides angular.json.
18
+ * CSS (4, or 3 with the Lynx preset). Everything else stays as `ng new` left
19
+ * it: the app needs no build config besides angular.json.
19
20
  * This is a one-shot destructive transform — the old browser-Angular files
20
21
  * are deleted because they reference DOM APIs and HTML elements that don't
21
22
  * exist on the Lynx runtime.
@@ -57,7 +58,7 @@ exports.default = (options) => async (tree) => {
57
58
  ];
58
59
  // `"false"` is how `ng add ... --tailwind=false` arrives (see schema.ts).
59
60
  if (options.tailwind !== false && options.tailwind !== 'false') {
60
- rules.push(addTailwindConfig(projectRoot), updateStylesCss(sourceRoot), addTailwindDependencies());
61
+ rules.push((0, tailwind_1.setUpTailwind)({ projectName, projectRoot, sourceRoot }, (0, tailwind_1.tailwindVersionFor)(tree, options.tailwindVersion)));
61
62
  }
62
63
  return (0, schematics_1.chain)(rules);
63
64
  };
@@ -423,58 +424,3 @@ const addCoreDependencies = () => {
423
424
  }),
424
425
  ]);
425
426
  };
426
- const addTailwindConfig = (projectRoot) => {
427
- return (tree) => {
428
- const configPath = `${projectRoot ? projectRoot + '/' : ''}tailwind.config.ts`;
429
- const content = `import type { Config } from 'tailwindcss';
430
- import preset from '@lynx-js/tailwind-preset';
431
-
432
- const config: Config = {
433
- content: ['./src/**/*.{html,ts}'],
434
- presets: [preset],
435
- };
436
-
437
- export default config;
438
- `;
439
- if (tree.exists(configPath)) {
440
- tree.overwrite(configPath, content);
441
- }
442
- else {
443
- tree.create(configPath, content);
444
- }
445
- };
446
- };
447
- const updateStylesCss = (sourceRoot) => {
448
- return (tree) => {
449
- const cssPath = `${sourceRoot}/styles.css`;
450
- const scssPath = `${sourceRoot}/styles.scss`;
451
- const targetPath = tree.exists(scssPath) ? scssPath : cssPath;
452
- // Only @tailwind base + utilities — no @tailwind components. Lynx doesn't
453
- // support all of Tailwind's component-layer reset rules (e.g. button
454
- // resets that reference focus-visible, form input pseudo-classes), and
455
- // bundling them in would generate CSS the runtime can't parse, producing
456
- // diagnostic noise. The base layer ships with @lynx-js/tailwind-preset's
457
- // own Lynx-compatible normalize rules.
458
- const content = `@tailwind base;
459
- @tailwind utilities;
460
- `;
461
- if (tree.exists(targetPath)) {
462
- tree.overwrite(targetPath, content);
463
- }
464
- else {
465
- tree.create(cssPath, content);
466
- }
467
- };
468
- };
469
- const addTailwindDependencies = () => {
470
- return (0, schematics_1.chain)([
471
- (0, dependency_1.addDependency)('tailwindcss', versions_1.VERSIONS.tailwindcss, {
472
- type: dependency_1.DependencyType.Dev,
473
- install: dependency_1.InstallBehavior.Auto,
474
- }),
475
- (0, dependency_1.addDependency)('@lynx-js/tailwind-preset', versions_1.VERSIONS.tailwindPreset, {
476
- type: dependency_1.DependencyType.Dev,
477
- install: dependency_1.InstallBehavior.Auto,
478
- }),
479
- ]);
480
- };
@@ -16,12 +16,17 @@
16
16
  "tailwind": {
17
17
  "type": ["boolean", "string"],
18
18
  "pattern": "^(true|false)$",
19
- "description": "Whether to add Tailwind CSS with @lynx-js/tailwind-preset.",
19
+ "description": "Whether to add Tailwind CSS.",
20
20
  "default": true,
21
21
  "x-prompt": {
22
- "message": "Would you like to add Tailwind CSS with Lynx preset?",
22
+ "message": "Would you like to add Tailwind CSS?",
23
23
  "type": "confirmation"
24
24
  }
25
+ },
26
+ "tailwindVersion": {
27
+ "type": ["string", "number"],
28
+ "enum": ["4", "3", 4, 3],
29
+ "description": "The Tailwind CSS major version to set up: 4 (with @tailwindcss/postcss), or 3 (with @lynx-js/tailwind-preset). Defaults to the version the project already depends on, else 4."
25
30
  }
26
31
  },
27
32
  "required": []
@@ -19,11 +19,17 @@ exports.VERSIONS = {
19
19
  */
20
20
  lynxTypes: '^4.3.0',
21
21
  /**
22
- * tailwindcss — utility-first CSS framework (optional, controlled by the `tailwind` schema option)
22
+ * tailwindcss and @tailwindcss/postcss, for Tailwind CSS 4: the default when
23
+ * the `tailwind` schema option is on. The build plugin rewrites what it
24
+ * writes into CSS Lynx reads.
25
+ */
26
+ tailwindcss4: '^4.3.3',
27
+ /**
28
+ * tailwindcss, for Tailwind CSS 3 (`--tailwind-version=3`)
23
29
  */
24
30
  tailwindcss: '^3.4.19',
25
31
  /**
26
- * @lynx-js/tailwind-preset — Lynx-specific Tailwind preset with native design tokens
32
+ * @lynx-js/tailwind-preset — Lynx's preset for Tailwind CSS 3
27
33
  */
28
34
  tailwindPreset: '^0.6.0',
29
35
  /**
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TAILWIND_4_STYLESHEET = exports.setUpTailwind = exports.tailwindVersionFor = exports.installedTailwindVersion = void 0;
4
+ const schematics_1 = require("@angular-devkit/schematics");
5
+ const dependency_1 = require("@schematics/angular/utility/dependency");
6
+ const json_file_1 = require("@schematics/angular/utility/json-file");
7
+ const workspace_1 = require("@schematics/angular/utility/workspace");
8
+ const versions_1 = require("../ng-add/versions");
9
+ /**
10
+ * The Tailwind CSS major version the project already depends on, if any. A
11
+ * range such as `^4.1.0` gives 4; `@tailwindcss/postcss`, Tailwind 4's
12
+ * PostCSS plugin, means 4 too.
13
+ */
14
+ const installedTailwindVersion = (tree) => {
15
+ if (!tree.exists('package.json')) {
16
+ return undefined;
17
+ }
18
+ const { dependencies = {}, devDependencies = {} } = tree.readJson('package.json');
19
+ const all = { ...dependencies, ...devDependencies };
20
+ if (all['@tailwindcss/postcss']) {
21
+ return '4';
22
+ }
23
+ const major = /\d+/.exec(all['tailwindcss'] ?? '')?.[0];
24
+ return major === '3' || major === '4' ? major : undefined;
25
+ };
26
+ exports.installedTailwindVersion = installedTailwindVersion;
27
+ /**
28
+ * The version to set up: the one asked for, else the one the project already
29
+ * has, else 4. An app that `ng new --style=tailwind` made, or one that is
30
+ * already on Tailwind 3, keeps its version, so running `ng add` again never
31
+ * moves it to another.
32
+ */
33
+ const tailwindVersionFor = (tree, requested) => requested === undefined
34
+ ? ((0, exports.installedTailwindVersion)(tree) ?? '4')
35
+ : String(requested);
36
+ exports.tailwindVersionFor = tailwindVersionFor;
37
+ /**
38
+ * Sets Tailwind CSS up in a project: Tailwind 4 as Angular CLI sets it up
39
+ * (`ng new --style=tailwind`), or Tailwind 3 with Lynx's preset.
40
+ */
41
+ const setUpTailwind = (target, version) => (version === '3' ? setUpTailwind3(target) : setUpTailwind4(target));
42
+ exports.setUpTailwind = setUpTailwind;
43
+ const prefixed = (projectRoot, file) => projectRoot ? `${projectRoot}/${file}` : file;
44
+ /**
45
+ * Tailwind 4, without its Preflight reset, whose rules are for HTML elements
46
+ * (`h1`, `button`, `img`) that a Lynx app doesn't have: Lynx drops most of
47
+ * them, and the web preview would apply them, so the two would differ.
48
+ * Tailwind documents this way of leaving Preflight out.
49
+ */
50
+ exports.TAILWIND_4_STYLESHEET = `@layer theme, base, components, utilities;
51
+ @import 'tailwindcss/theme.css' layer(theme);
52
+ @import 'tailwindcss/utilities.css' layer(utilities);
53
+ `;
54
+ /** The PostCSS config files Angular CLI reads, and its Tailwind schematic writes. */
55
+ const POSTCSS_CONFIG_FILES = ['.postcssrc.json', 'postcss.config.json'];
56
+ const setUpTailwind4 = ({ projectName, projectRoot, sourceRoot, }) => {
57
+ const installed = (tree) => (0, exports.installedTailwindVersion)(tree) === '4';
58
+ return (0, schematics_1.chain)([
59
+ writeTailwind4Stylesheet(projectName, sourceRoot),
60
+ addTailwindPostcssPlugin(projectRoot),
61
+ (tree) => (0, schematics_1.chain)(['tailwindcss', '@tailwindcss/postcss'].map((name) => (0, dependency_1.addDependency)(name, versions_1.VERSIONS.tailwindcss4, {
62
+ type: dependency_1.DependencyType.Dev,
63
+ install: dependency_1.InstallBehavior.Auto,
64
+ // Keep the version of a project that already has Tailwind 4, as
65
+ // Angular CLI does; replace a Tailwind 3 one.
66
+ existing: installed(tree)
67
+ ? dependency_1.ExistingBehavior.Skip
68
+ : dependency_1.ExistingBehavior.Replace,
69
+ }))),
70
+ (tree, context) => {
71
+ const config = ['ts', 'js', 'mjs', 'cjs']
72
+ .map((extension) => prefixed(projectRoot, `tailwind.config.${extension}`))
73
+ .find((file) => tree.exists(file));
74
+ if (config) {
75
+ context.logger.warn(`Tailwind CSS 4 doesn't read ${config}. Move its theme into @theme in the stylesheet, load its plugins with @plugin, and delete it.`);
76
+ }
77
+ },
78
+ ]);
79
+ };
80
+ /**
81
+ * Writes the Tailwind 4 stylesheet: over `styles.css`, or, in a project
82
+ * styled with Sass (which would read Tailwind's `@import`s as its own), to a
83
+ * `tailwind.css` added to the build's styles, as Angular CLI does.
84
+ */
85
+ const writeTailwind4Stylesheet = (projectName, sourceRoot) => (tree) => {
86
+ const cssPath = `${sourceRoot}/styles.css`;
87
+ if (tree.exists(cssPath) || !tree.exists(`${sourceRoot}/styles.scss`)) {
88
+ writeFile(tree, cssPath, exports.TAILWIND_4_STYLESHEET);
89
+ return;
90
+ }
91
+ const tailwindPath = `${sourceRoot}/tailwind.css`;
92
+ writeFile(tree, tailwindPath, exports.TAILWIND_4_STYLESHEET);
93
+ return (0, workspace_1.updateWorkspace)((workspace) => {
94
+ const build = workspace.projects.get(projectName)?.targets.get('build');
95
+ if (!build) {
96
+ return;
97
+ }
98
+ const options = (build.options ??= {});
99
+ const styles = options['styles'] ?? [];
100
+ if (!styles.includes(tailwindPath)) {
101
+ options['styles'] = [tailwindPath, ...styles];
102
+ }
103
+ });
104
+ };
105
+ /**
106
+ * Lists `@tailwindcss/postcss` in the project's PostCSS config, which the
107
+ * build reads, creating `.postcssrc.json` if there's none, as Angular CLI's
108
+ * Tailwind schematic does.
109
+ */
110
+ const addTailwindPostcssPlugin = (projectRoot) => (tree) => {
111
+ const existing = [...new Set(['', projectRoot])]
112
+ .flatMap((root) => POSTCSS_CONFIG_FILES.map((file) => prefixed(root, file)))
113
+ .find((file) => tree.exists(file));
114
+ if (!existing) {
115
+ tree.create(prefixed(projectRoot, '.postcssrc.json'), `${JSON.stringify({ plugins: { '@tailwindcss/postcss': {} } }, null, 2)}\n`);
116
+ return;
117
+ }
118
+ const config = new json_file_1.JSONFile(tree, existing);
119
+ if (config.get(['plugins', '@tailwindcss/postcss']) === undefined) {
120
+ config.modify(['plugins', '@tailwindcss/postcss'], {});
121
+ }
122
+ };
123
+ const setUpTailwind3 = ({ projectRoot, sourceRoot }) => (0, schematics_1.chain)([
124
+ (tree) => {
125
+ // The build runs Tailwind with this `content`, so it has to take in
126
+ // both inline templates (.ts) and the .html templates `ng generate
127
+ // component` writes by default, as in Angular's own Tailwind setup.
128
+ writeFile(tree, prefixed(projectRoot, 'tailwind.config.ts'), `import type { Config } from 'tailwindcss';
129
+ import preset from '@lynx-js/tailwind-preset';
130
+
131
+ const config: Config = {
132
+ content: ['./src/**/*.{html,ts}'],
133
+ presets: [preset],
134
+ };
135
+
136
+ export default config;
137
+ `);
138
+ // Prefer styles.scss if it exists (the user picked SCSS at `ng new`
139
+ // time): overwriting it with .css content would break their other
140
+ // imports.
141
+ //
142
+ // Only @tailwind base + utilities, no @tailwind components. Lynx doesn't
143
+ // support all of Tailwind's component-layer reset rules (button resets
144
+ // that reference focus-visible, form input pseudo-classes), and bundling
145
+ // them in would generate CSS the runtime can't parse, producing
146
+ // diagnostic noise. The base layer ships with @lynx-js/tailwind-preset's
147
+ // own Lynx-compatible normalize rules.
148
+ const scssPath = `${sourceRoot}/styles.scss`;
149
+ writeFile(tree, tree.exists(scssPath) ? scssPath : `${sourceRoot}/styles.css`, '@tailwind base;\n@tailwind utilities;\n');
150
+ },
151
+ (0, dependency_1.addDependency)('tailwindcss', versions_1.VERSIONS.tailwindcss, {
152
+ type: dependency_1.DependencyType.Dev,
153
+ install: dependency_1.InstallBehavior.Auto,
154
+ }),
155
+ (0, dependency_1.addDependency)('@lynx-js/tailwind-preset', versions_1.VERSIONS.tailwindPreset, {
156
+ type: dependency_1.DependencyType.Dev,
157
+ install: dependency_1.InstallBehavior.Auto,
158
+ }),
159
+ ]);
160
+ const writeFile = (tree, path, content) => {
161
+ if (tree.exists(path)) {
162
+ tree.overwrite(path, content);
163
+ }
164
+ else {
165
+ tree.create(path, content);
166
+ }
167
+ };