Skip to content

Commit 7fa887c

Browse files
committed
Merge branch 'hhristov/update-angular-templates' of https://github.com/IgniteUI/igniteui-cli into hhristov/update-angular-templates
2 parents c4d27bc + fb6495d commit 7fa887c

File tree

17 files changed

+350
-324
lines changed

17 files changed

+350
-324
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## [14.9.2](https://github.com/IgniteUI/igniteui-cli/compare/14.9.1...14.9.2) (2026-03-11)
2+
3+
### What's Changed
4+
* fix(igx-ts): update igniteui-angular versions and remove overrides by @Hristo313 in https://github.com/IgniteUI/igniteui-cli/pull/1508
5+
* fix(igx-ts): fix eslint configurations by @Hristo313 in https://github.com/IgniteUI/igniteui-cli/pull/1509
6+
* fix(upgrade-packages): correctly glob files on windows by @damyanpetev in https://github.com/IgniteUI/igniteui-cli/pull/1511
7+
* fix(igx-ts-legacy): fix grid type import and add missing override by @Hristo313 in https://github.com/IgniteUI/igniteui-cli/pull/1519
8+
* Fix start and other npm exec commands by @damyanpetev in https://github.com/IgniteUI/igniteui-cli/pull/1520
9+
* fix(schematics): fs writeFile create check by @damyanpetev in https://github.com/IgniteUI/igniteui-cli/pull/1526
10+
* fix(packages): disable non-functional registry login attempt on upgrade by @damyanpetev in https://github.com/IgniteUI/igniteui-cli/pull/1528
11+
12+
**Full Changelog**: https://github.com/IgniteUI/igniteui-cli/compare/14.9.1...14.9.2
13+
114
## [14.9.1](https://github.com/IgniteUI/igniteui-cli/compare/14.9.0...14.9.1) (2026-02-25)
215

316
### What's Changed
@@ -6,7 +19,6 @@
619

720
**Full Changelog**: https://github.com/IgniteUI/igniteui-cli/compare/14.9.0...14.9.1
821

9-
1022
# [14.9.0](https://github.com/IgniteUI/igniteui-cli/compare/v14.8.5...14.9.0) (2026-02-25)
1123

1224
🎉 This update includes:

packages/cli/lib/commands/start.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import { PositionalArgs, StartCommandType } from "./types";
77
import { ArgumentsCamelCase } from "yargs";
88

99
const execSyncNpmStart = (port: number, options: ExecSyncOptions): void => {
10-
const args = ['start'];
1110
if (port) {
1211
// Validate port is a number to prevent command injection
1312
if (!Number.isInteger(port) || port < 0 || port > 65535) {
1413
Util.error(`Invalid port number: ${port}`, "red");
1514
return;
1615
}
17-
args.push('--', `--port=${port}`);
16+
Util.execSync(`npm start -- --port=${port}`, options);
17+
return;
1818
}
19-
Util.spawnSync('npm', args, options);
19+
Util.execSync(`npm start`, options);
2020
};
2121

2222
const command: StartCommandType = {

packages/cli/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "igniteui-cli",
3-
"version": "14.9.1",
3+
"version": "14.9.2",
44
"description": "CLI tool for creating Ignite UI projects",
55
"keywords": [
66
"CLI",
@@ -76,8 +76,8 @@
7676
"all": true
7777
},
7878
"dependencies": {
79-
"@igniteui/angular-templates": "~21.1.1491",
80-
"@igniteui/cli-core": "~14.9.1",
79+
"@igniteui/angular-templates": "~21.1.1492",
80+
"@igniteui/cli-core": "~14.9.2",
8181
"@inquirer/prompts": "^7.9.0",
8282
"@types/yargs": "^17.0.33",
8383
"chalk": "^5.3.0",

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@igniteui/cli-core",
3-
"version": "14.9.1",
3+
"version": "14.9.2",
44
"description": "Base types and functionality for Ignite UI CLI",
55
"repository": {
66
"type": "git",

packages/core/packages/PackageManager.ts

Lines changed: 45 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { spawn } from "child_process";
1+
import { exec } from "child_process";
22
import * as path from "path";
33
import { TemplateManager } from "../../cli/lib/TemplateManager";
44
import { Config, FS_TOKEN, IFileSystem, ProjectTemplate } from "../types";
55
import { App, ProjectConfig, Util } from "../util";
66

77
import componentsConfig = require("./components");
88

9+
/** @internal */
10+
export const REGISTRY_ATTEMPT_LOGIN = false;
11+
912
export class PackageManager {
1013
private static ossPackage: string = "ignite-ui";
1114
private static fullPackage: string = "@infragistics/ignite-ui-full";
@@ -45,6 +48,10 @@ export class PackageManager {
4548
const version = ossVersion ? `@"${ossVersion}"` : "";
4649
const errorMsg = "Something went wrong, " +
4750
"please follow the steps in this guide: https://www.igniteui.com/help/using-ignite-ui-npm-packages";
51+
Util.log(
52+
"The project you've created requires the full version of Ignite UI from Infragistics private feed.",
53+
"gray"
54+
);
4855
// fallback to @latest, in case when igniteui-full does not have a matching version to ossVersion
4956
// ex: "ignite-ui": "^21.1.13" BUT --> ignite-ui-full": "^21.1.11" (no 21.1.13 released).
5057
// TODO: update temp fix - only working in 21.1.11 without errors
@@ -88,9 +95,8 @@ export class PackageManager {
8895
const config = ProjectConfig.localConfig();
8996
if (!config.packagesInstalled) {
9097
let command: string;
91-
let managerCommand: string;
98+
const managerCommand = this.getManager();
9299

93-
managerCommand = this.getManager();
94100
switch (managerCommand) {
95101
case "npm":
96102
/* passes through */
@@ -123,49 +129,52 @@ export class PackageManager {
123129
}
124130

125131
public static removePackage(packageName: string, verbose: boolean = false): boolean {
132+
let command: string;
126133
const managerCommand = this.getManager();
127-
let args: string[];
134+
const sanitizePackage = Util.sanitizeShellArg(packageName);
128135
switch (managerCommand) {
129136
case "npm":
130137
/* passes through */
131138
default:
132-
args = ['uninstall', packageName, '--quiet', '--save'];
139+
command = `${managerCommand} uninstall ${sanitizePackage} --quiet --save`;
133140
break;
134141
}
135142
try {
136143
// tslint:disable-next-line:object-literal-sort-keys
137-
Util.spawnSync(managerCommand, args, { stdio: "pipe", encoding: "utf8" });
144+
Util.execSync(command, { stdio: "pipe", encoding: "utf8" });
138145
} catch (error) {
139-
Util.log(`Error uninstalling package ${packageName} with ${managerCommand}`);
146+
Util.log(`Error uninstalling package ${sanitizePackage} with ${managerCommand}`);
140147
if (verbose) {
141148
Util.log(error.message);
142149
}
143150
return false;
144151
}
145152

146-
Util.log(`Package ${packageName} uninstalled successfully`);
153+
Util.log(`Package ${sanitizePackage} uninstalled successfully`);
147154
return true;
148155
}
149156

150157
public static addPackage(packageName: string, verbose: boolean = false): boolean {
151158
const managerCommand = this.getManager();
152-
const args = this.getInstallArgs(packageName);
159+
const sanitizePackage = Util.sanitizeShellArg(packageName);
160+
const command = this.getInstallCommand(managerCommand, sanitizePackage);
153161
try {
154162
// tslint:disable-next-line:object-literal-sort-keys
155-
Util.spawnSync(managerCommand, args, { stdio: "pipe", encoding: "utf8" });
163+
Util.execSync(command, { stdio: "pipe", encoding: "utf8" });
156164
} catch (error) {
157-
Util.log(`Error installing package ${packageName} with ${managerCommand}`);
165+
Util.log(`Error installing package ${sanitizePackage} with ${managerCommand}`);
158166
if (verbose) {
159167
Util.log(error.message);
160168
}
161169
return false;
162170
}
163-
Util.log(`Package ${packageName} installed successfully`);
171+
Util.log(`Package ${sanitizePackage} installed successfully`);
164172
return true;
165173
}
166174

167175
public static async queuePackage(packageName: string, verbose = false) {
168-
const args = this.getInstallArgs(packageName).map(arg => arg === '--save' ? '--no-save' : arg);
176+
const command = this.getInstallCommand(this.getManager(), Util.sanitizeShellArg(packageName))
177+
.replace("--save", "--no-save");
169178
const [packName, version] = packageName.split(/@(?=[^\/]+$)/);
170179
const packageJSON = this.getPackageJSON();
171180
if (!packageJSON.dependencies) {
@@ -190,24 +199,13 @@ export class PackageManager {
190199
// D.P. Concurrent install runs should be supported
191200
// https://github.com/npm/npm/issues/5948
192201
// https://github.com/npm/npm/issues/2500
193-
const managerCommand = this.getManager();
194202
const task = new Promise<{ packageName, error, stdout, stderr }>((resolve, reject) => {
195-
const child = spawn(managerCommand, args);
196-
let stdout = '';
197-
let stderr = '';
198-
child.stdout?.on('data', (data) => {
199-
stdout += data.toString();
200-
});
201-
child.stderr?.on('data', (data) => {
202-
stderr += data.toString();
203-
});
204-
child.on('close', (code) => {
205-
const error = code !== 0 ? new Error(`Process exited with code ${code}`) : null;
206-
resolve({ packageName, error, stdout, stderr });
207-
});
208-
child.on('error', (err) => {
209-
resolve({ packageName, error: err, stdout, stderr });
210-
});
203+
const child = exec(
204+
command, {},
205+
(error, stdout, stderr) => {
206+
resolve({ packageName, error, stdout, stderr });
207+
}
208+
);
211209
});
212210
task["packageName"] = packName;
213211
this.installQueue.push(task);
@@ -233,16 +231,17 @@ export class PackageManager {
233231
}
234232

235233
public static ensureRegistryUser(config: Config, message: string): boolean {
236-
const fullPackageRegistry = config.igPackageRegistry;
234+
const fullPackageRegistry = Util.sanitizeShellArg(config.igPackageRegistry);
237235
try {
238236
// tslint:disable-next-line:object-literal-sort-keys
239-
Util.spawnSync('npm', ['whoami', `--registry=${fullPackageRegistry}`], { stdio: 'pipe', encoding: 'utf8' });
237+
Util.execSync(`npm whoami --registry=${fullPackageRegistry}`, { stdio: "pipe", encoding: "utf8" });
240238
} catch (error) {
239+
if (!REGISTRY_ATTEMPT_LOGIN) {
240+
Util.log(message, "gray");
241+
return true;
242+
}
243+
241244
// try registering the user:
242-
Util.log(
243-
"The project you've created requires the full version of Ignite UI from Infragistics private feed.",
244-
"gray"
245-
);
246245
Util.log(
247246
"We are initiating the login process for you. This will be required only once per environment.",
248247
"gray"
@@ -257,20 +256,16 @@ export class PackageManager {
257256
if (process.stdin.isTTY) {
258257
process.stdin.setRawMode(true);
259258
}
260-
const cmd = /^win/.test(process.platform) ? "npm.cmd" : "npm"; //https://github.com/nodejs/node/issues/3675
261-
const login = Util.spawnSync(cmd,
262-
["adduser", `--registry=${fullPackageRegistry}`, `--scope=@infragistics`, `--auth-type=legacy`],
263-
{ stdio: "inherit" }
264-
);
265-
if (login?.status === 0) {
259+
260+
try {
261+
Util.execSync(
262+
`npm login --registry=${fullPackageRegistry} --scope=@infragistics --auth-type=legacy`,
263+
{ stdio: "inherit" }
264+
);
266265
//make sure scope is configured:
267-
try {
268-
Util.spawnSync('npm', ['config', 'set', `@infragistics:registry`, fullPackageRegistry]);
269-
return true;
270-
} catch (error) {
271-
return false;
272-
}
273-
} else {
266+
Util.execSync(`npm config set @infragistics:registry ${fullPackageRegistry}`);
267+
return true;
268+
} catch (error) {
274269
Util.log(message, "red");
275270
return false;
276271
}
@@ -292,11 +287,7 @@ export class PackageManager {
292287
}
293288
}
294289

295-
private static getInstallArgs(packageName: string): string[] {
296-
return ['install', packageName, '--quiet', '--save'];
297-
}
298-
299-
private static getManager(/*config:Config*/): string {
290+
private static getManager(/*config:Config*/): 'npm' {
300291
//stub to potentially swap out managers
301292
return "npm";
302293
}

packages/core/update/Update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export async function updateWorkspace(rootPath: string): Promise<boolean> {
5555
}
5656
const pkgJSON = JSON.parse(fileString);
5757

58-
const errorMsg = "Something went wrong, please follow the steps in this guide: " + guideLink;
58+
const errorMsg = "Can't detect/setup Infragistics feed login required for licensed packages.\nSee: " + guideLink;
5959
if (!PackageManager.ensureRegistryUser(config, errorMsg)) {
6060
return false;
6161
}

packages/core/util/Util.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,6 @@ export class Util {
283283
}
284284

285285
for (const key of Object.keys(source)) {
286-
// Skip keys that can affect the prototype of objects to avoid prototype pollution vulnerabilities
287-
if (key === "__proto__" || key === "constructor") {
288-
continue;
289-
}
290-
291286
const sourceKeyIsArray = Array.isArray(source[key]);
292287
const targetHasThisKey = target.hasOwnProperty(key);
293288

@@ -321,6 +316,21 @@ export class Util {
321316
}
322317
}
323318

319+
/**
320+
* Fairly aggressive sanitize removing anything but ASCII, numbers and a few needed chars that have no action:
321+
* - semicolons (:), dots (.), underscores (_) for paths/URLs
322+
* - dashes (-) & forward slashes (/) for packages and paths/URLs
323+
* - at (@), non-leading tilde (~) for package scope & version
324+
* @remarks
325+
* Most shells should be UTF-enabled, but current usage is very limited thus no need for `\p{L}`
326+
*/
327+
public static sanitizeShellArg(arg: string): string {
328+
return arg
329+
.replace(/[^a-z0-9@:_~\^\/\.\-]/gi, '') // remove unsafe chars
330+
.replace(/\^(?!\d)/g, '') // keep only ^<digit>
331+
.replace(/^~/, ''); // remove leading ~
332+
}
333+
324334
/**
325335
* Execute synchronous command with options
326336
* @param command Command to be executed
@@ -353,29 +363,18 @@ export class Util {
353363
}
354364

355365
/**
356-
* Execute synchronous command with options using spawnSync
357-
* @param command Command to be executed
358-
* @param args Command arguments
359-
* @param options Command options
360-
* @throws {Error} On non-zero exit code. Error has 'status', 'signal', 'output', 'stdout', 'stderr'
361-
*/
362-
public static spawnSync(command: string, args: string[], options?: SpawnSyncOptions) {
363-
try {
364-
return spawnSync(command, args, options);
365-
} catch (error) {
366-
// Handle potential process interruption
367-
// Check if the error output ends with "^C"
368-
if (error.stderr && error.stderr.toString().endsWith() === "^C") {
369-
return process.exit();
370-
}
371-
372-
// Handle specific exit codes for different signals
373-
if (error.status === 3221225786 || error.status > 128) {
374-
return process.exit();
375-
}
376-
377-
throw error;
378-
}
366+
* Execute synchronous command with options using spawnSync
367+
* @param command Command to be executed
368+
* NOTE: `spawn` without `shell` (unsafe) is **not** equivalent to `exec` & requires direct path to run the correct process on win,
369+
* e.g. `npm.cmd` but that is also blocked in node@24+ for security reasons
370+
* Do not call with/add commands that are not known binaries without validating first
371+
* @param args Command arguments
372+
* @param options Command options
373+
* @returns {SpawnSyncReturns} object with status and stdout
374+
* @remarks Consuming code MUST handle the result and check for failure status!
375+
*/
376+
public static spawnSync(command: 'node' | 'git', args: string[], options?: Omit<SpawnSyncOptions, 'shell'>) {
377+
return spawnSync(command, args, options);
379378
}
380379

381380
/**
@@ -388,8 +387,7 @@ export class Util {
388387
const options: any = { cwd: path.join(parentRoot, projectName), stdio: [process.stdin, "ignore", "ignore"] };
389388
Util.execSync("git init", options);
390389
Util.execSync("git add .", options);
391-
const commitMessage = `"Initial commit for project: ${projectName}"`;
392-
Util.spawnSync('git', ['commit', '-m', commitMessage], options);
390+
Util.execSync("git commit -m " + "\"Initial commit for project\"", options);
393391
Util.log(Util.greenCheck() + " Git Initialized and Project '" + projectName + "' Committed");
394392
} catch (error) {
395393
Util.error("Git initialization failed. Install Git in order to automatically commit the project.", "yellow");

packages/igx-templates/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@igniteui/angular-templates",
3-
"version": "21.1.1491",
3+
"version": "21.1.1492",
44
"description": "Templates for Ignite UI for Angular projects and components",
55
"repository": {
66
"type": "git",
@@ -12,7 +12,7 @@
1212
"author": "Infragistics",
1313
"license": "MIT",
1414
"dependencies": {
15-
"@igniteui/cli-core": "~14.9.1",
15+
"@igniteui/cli-core": "~14.9.2",
1616
"typescript": "~5.5.4"
1717
}
1818
}

0 commit comments

Comments
 (0)