@resolve-components/theme 1.2.1 → 1.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,8 +1,8 @@
1
1
  {
2
2
  "name": "@resolve-components/theme",
3
- "version": "1.2.1",
3
+ "version": "1.2.4",
4
4
  "schematics": "./collection.json",
5
- "description": "🔥 Fully customizable Angular UI components made with ❤️ using Angular CDK 🔥",
5
+ "description": "🔥 Fully customizable Angular UI components made with ❤️ using Angular CDK 🔥",
6
6
  "peerDependencies": {
7
7
  "@angular/common": ">=17.0.0",
8
8
  "@angular/core": ">=17.0.0",
@@ -7,6 +7,16 @@ const tasks_1 = require("@angular-devkit/schematics/tasks");
7
7
  // Helpers
8
8
  // ---------------------------------------------------------------------------
9
9
  const isNxWorkspace = (tree) => tree.exists('nx.json');
10
+ /** Returns true if a project's build target uses an Angular builder. */
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ function isAngularProject(content) {
13
+ const buildExecutor = content?.['architect']?.['build']?.['executor'] ??
14
+ content?.['targets']?.['build']?.['executor'] ??
15
+ '';
16
+ return (buildExecutor.startsWith('@angular-devkit/build-angular') ||
17
+ buildExecutor.startsWith('@angular/build') ||
18
+ buildExecutor.startsWith('@nx/angular'));
19
+ }
10
20
  /**
11
21
  * Try to resolve project config from a root angular.json / workspace.json.
12
22
  * Works for standard Angular workspaces AND Nx workspaces that still maintain
@@ -46,47 +56,116 @@ function getAngularWorkspaceProject(tree, projectName) {
46
56
  isNxProjectJson: false,
47
57
  };
48
58
  }
59
+ /** Scan all apps/ and projects/ subdirectories and return every application project.json. */
60
+ function discoverNxApplicationProjects(tree) {
61
+ const results = [];
62
+ for (const root of ['apps', 'projects']) {
63
+ const dir = tree.getDir(root);
64
+ for (const sub of dir.subdirs) {
65
+ const path = `${root}/${sub}/project.json`;
66
+ if (!tree.exists(path))
67
+ continue;
68
+ try {
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const content = JSON.parse(tree.read(path).toString('utf-8'));
71
+ if (content['projectType'] && content['projectType'] !== 'application')
72
+ continue;
73
+ const name = content['name'] ?? sub;
74
+ const defaultSourceRoot = `${root}/${sub}/src`;
75
+ const sourceRoot = content['sourceRoot'] ?? defaultSourceRoot;
76
+ results.push({ path, name, sourceRoot, content });
77
+ }
78
+ catch {
79
+ continue;
80
+ }
81
+ }
82
+ }
83
+ return results;
84
+ }
49
85
  /**
50
86
  * In Nx v16+ workspaces there is no writable root angular.json.
51
87
  * Discover project.json files under apps/ or projects/.
88
+ * Filters to Angular projects, asks for --project when multiple match.
52
89
  */
53
90
  function getNxProjectJsonConfig(tree, projectName) {
54
- const candidates = [];
91
+ const allApps = discoverNxApplicationProjects(tree);
92
+ // If a project name was explicitly given, find it (any executor — user knows best)
55
93
  if (projectName) {
56
- candidates.push(`apps/${projectName}/project.json`, `libs/${projectName}/project.json`, `${projectName}/project.json`);
57
- }
58
- // Auto-discover under apps/ and projects/
59
- for (const root of ['apps', 'projects']) {
60
- const dir = tree.getDir(root);
61
- for (const sub of dir.subdirs) {
62
- const p = `${root}/${sub}/project.json`;
63
- if (!candidates.includes(p))
64
- candidates.push(p);
94
+ // Try exact path candidates first
95
+ for (const root of ['apps', 'projects']) {
96
+ const path = `${root}/${projectName}/project.json`;
97
+ if (tree.exists(path)) {
98
+ try {
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ const content = JSON.parse(tree.read(path).toString('utf-8'));
101
+ const name = content['name'] ?? projectName;
102
+ const sourceRoot = content['sourceRoot'] ?? `${root}/${projectName}/src`;
103
+ return {
104
+ file: path,
105
+ projectName: name,
106
+ content,
107
+ sourceRoot,
108
+ isNxProjectJson: true,
109
+ };
110
+ }
111
+ catch {
112
+ /* skip */
113
+ }
114
+ }
65
115
  }
66
- }
67
- for (const path of candidates) {
68
- if (!tree.exists(path))
69
- continue;
70
- try {
71
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
- const content = JSON.parse(tree.read(path).toString('utf-8'));
73
- // Skip non-application projects (libraries, e2e, etc.)
74
- if (content['projectType'] && content['projectType'] !== 'application')
75
- continue;
76
- const projName = content['name'] ?? path.split('/')[1];
77
- const defaultSourceRoot = path.split('/').slice(0, -1).join('/') + '/src';
78
- const sourceRoot = content['sourceRoot'] ?? defaultSourceRoot;
116
+ // Also search by name in discovered list
117
+ const byName = allApps.find((p) => p.name === projectName || p.path.includes(`/${projectName}/`));
118
+ if (byName) {
79
119
  return {
80
- file: path,
81
- projectName: projName,
82
- content,
83
- sourceRoot,
120
+ file: byName.path,
121
+ projectName: byName.name,
122
+ content: byName.content,
123
+ sourceRoot: byName.sourceRoot,
84
124
  isNxProjectJson: true,
85
125
  };
86
126
  }
87
- catch {
88
- continue;
89
- }
127
+ // Not found — throw with available list
128
+ const available = allApps.map((p) => ` • ${p.name}`).join('\n');
129
+ throw new schematics_1.SchematicsException(`Project "${projectName}" not found.\n` +
130
+ `Available application projects:\n${available || ' (none found)'}\n\n` +
131
+ `Re-run with: ng add @resolve-components/theme --project=<name>`);
132
+ }
133
+ // No project specified — filter to Angular projects only
134
+ const angularApps = allApps.filter((p) => isAngularProject(p.content));
135
+ if (angularApps.length === 1) {
136
+ // Exactly one Angular app → auto-pick
137
+ const found = angularApps[0];
138
+ return {
139
+ file: found.path,
140
+ projectName: found.name,
141
+ content: found.content,
142
+ sourceRoot: found.sourceRoot,
143
+ isNxProjectJson: true,
144
+ };
145
+ }
146
+ if (angularApps.length > 1) {
147
+ // Multiple Angular apps — cannot guess, ask user to specify
148
+ const list = angularApps.map((p) => ` • ${p.name}`).join('\n');
149
+ throw new schematics_1.SchematicsException(`Multiple Angular applications found. Please specify which one to configure:\n\n` +
150
+ `${list}\n\n` +
151
+ `Re-run with: ng add @resolve-components/theme --project=<name>`);
152
+ }
153
+ // No Angular apps found — fall back to any application project
154
+ if (allApps.length === 1) {
155
+ const found = allApps[0];
156
+ return {
157
+ file: found.path,
158
+ projectName: found.name,
159
+ content: found.content,
160
+ sourceRoot: found.sourceRoot,
161
+ isNxProjectJson: true,
162
+ };
163
+ }
164
+ if (allApps.length > 1) {
165
+ const list = allApps.map((p) => ` • ${p.name}`).join('\n');
166
+ throw new schematics_1.SchematicsException(`Could not auto-detect an Angular project. Found these application projects:\n\n` +
167
+ `${list}\n\n` +
168
+ `Re-run with: ng add @resolve-components/theme --project=<name>`);
90
169
  }
91
170
  return null;
92
171
  }
@@ -207,6 +286,67 @@ function addGlobalStyles(options) {
207
286
  };
208
287
  }
209
288
  // ---------------------------------------------------------------------------
289
+ // Rule: wrap app.html with <rc-layout>
290
+ // ---------------------------------------------------------------------------
291
+ function addRcLayoutToTemplate(options) {
292
+ return (tree, ctx) => {
293
+ const cfg = resolveProjectConfig(tree, options);
294
+ const sourceRoot = cfg?.sourceRoot ?? 'src';
295
+ const templatePath = `${sourceRoot}/app/app.html`;
296
+ if (!tree.exists(templatePath)) {
297
+ ctx.logger.warn(`⚠ ${templatePath} not found — wrap your root template with <rc-layout> manually.`);
298
+ return tree;
299
+ }
300
+ const content = tree.read(templatePath).toString('utf-8');
301
+ if (content.includes('<rc-layout'))
302
+ return tree;
303
+ const wrapped = `<rc-layout>\n${content.trimEnd()}\n</rc-layout>\n`;
304
+ tree.overwrite(templatePath, wrapped);
305
+ ctx.logger.info(` Wrapped ${templatePath} with <rc-layout>`);
306
+ return tree;
307
+ };
308
+ }
309
+ // ---------------------------------------------------------------------------
310
+ // Rule: add RcLayout to app.ts imports
311
+ // ---------------------------------------------------------------------------
312
+ function addRcLayoutToAppComponent(options) {
313
+ return (tree, ctx) => {
314
+ const cfg = resolveProjectConfig(tree, options);
315
+ const sourceRoot = cfg?.sourceRoot ?? 'src';
316
+ // Support both app.ts and app.component.ts
317
+ const appTsPath = tree.exists(`${sourceRoot}/app/app.ts`)
318
+ ? `${sourceRoot}/app/app.ts`
319
+ : tree.exists(`${sourceRoot}/app/app.component.ts`)
320
+ ? `${sourceRoot}/app/app.component.ts`
321
+ : null;
322
+ if (!appTsPath) {
323
+ ctx.logger.warn(`⚠ app.ts / app.component.ts not found — add RcLayout to your root component imports manually.`);
324
+ return tree;
325
+ }
326
+ let content = tree.read(appTsPath).toString('utf-8');
327
+ if (content.includes('RcLayout'))
328
+ return tree;
329
+ // Add import statement after last existing import
330
+ const importLine = `import { RcLayout } from '@resolve-components/theme';\n`;
331
+ const lastImportMatch = [...content.matchAll(/^import .*?;\n/gms)]
332
+ .filter((m) => !m[0].includes('\n '))
333
+ .pop();
334
+ if (lastImportMatch) {
335
+ const insertAt = lastImportMatch.index + lastImportMatch[0].length;
336
+ content =
337
+ content.slice(0, insertAt) + importLine + content.slice(insertAt);
338
+ }
339
+ else {
340
+ content = importLine + content;
341
+ }
342
+ // Add RcLayout to the component's imports array
343
+ content = content.replace(/(imports:\s*\[)/, `$1RcLayout, `);
344
+ tree.overwrite(appTsPath, content);
345
+ ctx.logger.info(` Added RcLayout import → ${appTsPath}`);
346
+ return tree;
347
+ };
348
+ }
349
+ // ---------------------------------------------------------------------------
210
350
  // Exported schematic factory
211
351
  // ---------------------------------------------------------------------------
212
352
  function ngAdd(options) {
@@ -217,6 +357,8 @@ function ngAdd(options) {
217
357
  addBuildConfig(options),
218
358
  addProviderToConfig(options),
219
359
  addGlobalStyles(options),
360
+ addRcLayoutToTemplate(options),
361
+ addRcLayoutToAppComponent(options),
220
362
  (_tree, ctx) => {
221
363
  ctx.addTask(new tasks_1.NodePackageInstallTask());
222
364
  ctx.logger.info('\n✅ @resolve-components/theme setup complete!');