@blotch/angular-lynx 22.2.2 → 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.
@@ -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
+ };