Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ jobs:
matrix:
os: [ubuntu-latest]
node: [22]
subset: [yarn, pnpm, bun]
subset: [yarn, pnpm, bun, deno]
shard: [0, 1, 2]
runs-on: ${{ matrix.os }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
matrix:
os: [ubuntu-latest]
node: [22]
subset: [yarn, pnpm, bun]
subset: [yarn, pnpm, bun, deno]
shard: [0, 1, 2]
runs-on: ${{ matrix.os }}
steps:
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/cli/lib/config/workspace-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"packageManager": {
"description": "Specify which package manager tool to use.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"]
"enum": ["npm", "yarn", "pnpm", "bun", "deno"]
},
"warnings": {
"description": "Control CLI specific console warnings",
Expand Down Expand Up @@ -101,7 +101,7 @@
"packageManager": {
"description": "Specify which package manager tool to use.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"]
"enum": ["npm", "yarn", "pnpm", "bun", "deno"]
},
"warnings": {
"description": "Control CLI specific console warnings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"description": "The preferred package manager configuration files to use for registry settings.",
"type": "string",
"default": "npm",
"enum": ["npm", "yarn", "pnpm", "bun"]
"enum": ["npm", "yarn", "pnpm", "bun", "deno"]
}
},
"required": []
Expand Down
40 changes: 35 additions & 5 deletions packages/angular/cli/src/utilities/package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const LOCKFILE_NAMES: Readonly<Record<PackageManager, string | readonly string[]
[PackageManager.Pnpm]: 'pnpm-lock.yaml',
[PackageManager.Bun]: ['bun.lockb', 'bun.lock'],
[PackageManager.Npm]: 'package-lock.json',
[PackageManager.Deno]: 'deno.lock',
};

interface PackageManagerOptions {
Expand Down Expand Up @@ -166,6 +167,14 @@ export class PackageManagerUtils {
prefix: '--cwd',
noLockfile: '--no-save',
};
case PackageManager.Deno:
return {
saveDev: '--dev',
install: 'add',
installAll: 'install',
prefix: '--root',
noLockfile: '--no-lock',
};
default:
return {
saveDev: '--save-dev',
Expand Down Expand Up @@ -212,7 +221,8 @@ export class PackageManagerUtils {
@memoize
private getVersion(name: PackageManager): string | undefined {
try {
return execSync(`${name} --version`, {
const versionArg = name !== PackageManager.Deno ? '--version' : '-v';
const version = execSync(`${name} ${versionArg}`, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
env: {
Expand All @@ -222,6 +232,13 @@ export class PackageManagerUtils {
NPM_CONFIG_UPDATE_NOTIFIER: 'false',
},
}).trim();

if (name === PackageManager.Deno) {
// Deno CLI outputs "deno 2.5.6"
return version.replace('deno ', '');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to have changed.

deno --version
deno 2.5.6 (stable, release, x86_64-unknown-linux-gnu)
v8 14.0.365.5-rusty
typescript 5.9.2

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... maybe this differs by platform? For me on Windows I get a shorter version output with -v, and a more verbose one with --version. That's why I added the ternary to switch to the -v flag on L224:

const versionArg = name !== PackageManager.Deno ? '--version' : '-v';

> PS C:\Users\csvn\dev\angular-cli> deno --version
deno 2.5.6 (stable, release, x86_64-pc-windows-msvc)
v8 14.0.365.5-rusty
typescript 5.9.2

> PS C:\Users\csvn\dev\angular-cli> deno -v
deno 2.5.6

I used the shorter one (-v) to simplify parsing the version correctly. But I could perhaps change to a regex to extract the version when using --version? Something along the lines of /deno (?<version>\S{5,})/.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked into the E2E test errors yet, so I don't know why those fail. But I know it works to create a new Angular project with Deno like this:

> PS C:\Users\ChristianSvensson\Dev\_personal> deno run -Ar npm:@angular/cli new test-project --skip-install --test-runner karma
✔ Which stylesheet system would you like to use? CSS             [ https://developer.mozilla.org/docs/Web/CSS]
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No
✔ Which AI tools do you want to configure with Angular best practices? https://angular.dev/ai/develop-with-ai None
CREATE test-project/angular.json (2166 bytes)
...[snip]...
CREATE test-project/public/favicon.ico (15086 bytes)
    Successfully initialized git.

> PS C:\Users\ChristianSvensson\Dev\_personal>

}

return version;
} catch {
return undefined;
}
Expand All @@ -239,14 +256,21 @@ export class PackageManagerUtils {
const hasYarnLock = this.hasLockfile(PackageManager.Yarn, filesInRoot);
const hasPnpmLock = this.hasLockfile(PackageManager.Pnpm, filesInRoot);
const hasBunLock = this.hasLockfile(PackageManager.Bun, filesInRoot);
const hasDenoLock = this.hasLockfile(PackageManager.Deno, filesInRoot);

// PERF NOTE: `this.getVersion` spawns the package a the child_process which can take around ~300ms at times.
// Therefore, we should only call this method when needed. IE: don't call `this.getVersion(PackageManager.Pnpm)` unless truly needed.
// The result of this method is not stored in a variable because it's memoized.

if (hasNpmLock) {
// Has NPM lock file.
if (!hasYarnLock && !hasPnpmLock && !hasBunLock && this.getVersion(PackageManager.Npm)) {
if (
!hasYarnLock &&
!hasPnpmLock &&
!hasBunLock &&
!hasDenoLock &&
this.getVersion(PackageManager.Npm)
) {
// Only NPM lock file and NPM binary is available.
return PackageManager.Npm;
}
Expand All @@ -261,6 +285,9 @@ export class PackageManagerUtils {
} else if (hasBunLock && this.getVersion(PackageManager.Bun)) {
// Bun lock file and Bun binary is available.
return PackageManager.Bun;
} else if (hasDenoLock && this.getVersion(PackageManager.Deno)) {
// Deno lock file and Deno binary is available.
return PackageManager.Deno;
}
}

Expand All @@ -269,13 +296,16 @@ export class PackageManagerUtils {
const hasYarn = !!this.getVersion(PackageManager.Yarn);
const hasPnpm = !!this.getVersion(PackageManager.Pnpm);
const hasBun = !!this.getVersion(PackageManager.Bun);
const hasDeno = !!this.getVersion(PackageManager.Deno);

if (hasYarn && !hasPnpm && !hasBun) {
if (hasYarn && !hasPnpm && !hasBun && !hasDeno) {
return PackageManager.Yarn;
} else if (hasPnpm && !hasYarn && !hasBun) {
} else if (hasPnpm && !hasYarn && !hasBun && !hasDeno) {
return PackageManager.Pnpm;
} else if (hasBun && !hasYarn && !hasPnpm) {
} else if (hasBun && !hasYarn && !hasPnpm && !hasDeno) {
return PackageManager.Bun;
} else if (hasDeno && !hasYarn && !hasPnpm && !hasBun) {
return PackageManager.Deno;
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/angular/create/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ pnpm create @angular [project-name] [...options]
```
bun create @angular [project-name] [...options]
```

### deno

```
deno init --npm @angular [project-name] [...options]
```
2 changes: 1 addition & 1 deletion packages/angular/create/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const hasPackageManagerArg = args.some((a) => a.startsWith('--package-manager'))
if (!hasPackageManagerArg) {
// Ex: yarn/1.22.18 npm/? node/v16.15.1 linux x64
const packageManager = process.env['npm_config_user_agent']?.split('/')[0];
if (packageManager && ['npm', 'pnpm', 'yarn', 'bun'].includes(packageManager)) {
if (packageManager && ['npm', 'pnpm', 'yarn', 'bun', 'deno'].includes(packageManager)) {
args.push('--package-manager', packageManager);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ const packageManagers: { [name: string]: PackageManagerProfile } = {
installPackage: 'add',
},
},
'deno': {
commands: {
installAll: 'install',
installPackage: 'add',
},
},
'pnpm': {
commands: {
installAll: 'install',
Expand Down
2 changes: 1 addition & 1 deletion packages/angular_devkit/schematics_cli/blank/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"packageManager": {
"description": "The package manager used to install dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"],
"enum": ["npm", "yarn", "pnpm", "bun", "deno"],
"default": "npm"
},
"author": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"packageManager": {
"description": "The package manager used to install dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"],
"enum": ["npm", "yarn", "pnpm", "bun", "deno"],
"default": "npm"
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/schematics/angular/ng-new/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"packageManager": {
"description": "The package manager used to install dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"]
"enum": ["npm", "yarn", "pnpm", "bun", "deno"]
},
"standalone": {
"description": "Creates an application based upon the standalone API, without NgModules.",
Expand Down
2 changes: 1 addition & 1 deletion packages/schematics/angular/workspace/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"packageManager": {
"description": "The package manager to use for installing dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm", "bun"],
"enum": ["npm", "yarn", "pnpm", "bun", "deno"],
"$default": {
"$source": "packageManager"
}
Expand Down
5 changes: 3 additions & 2 deletions tests/legacy-cli/e2e.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def e2e_suites(name, runner, data):

_e2e_suite(name, runner, "npm", data, toolchain_name, toolchain)
_e2e_suite(name, runner, "bun", data, toolchain_name, toolchain)
_e2e_suite(name, runner, "deno", data, toolchain_name, toolchain)
_e2e_suite(name, runner, "pnpm", data, toolchain_name, toolchain)
_e2e_suite(name, runner, "yarn", data, toolchain_name, toolchain)
_e2e_suite(name, runner, "esbuild", data, toolchain_name, toolchain)
Expand Down Expand Up @@ -140,7 +141,7 @@ def _e2e_tests(name, runner, toolchain, **kwargs):

def _e2e_suite(name, runner, type, data, toolchain_name = "", toolchain = None):
"""
Setup a predefined test suite (yarn|pnpm|bun|esbuild|saucelabs|npm).
Setup a predefined test suite (yarn|pnpm|bun|deno|esbuild|saucelabs|npm).
"""
args = []
tests = None
Expand All @@ -149,7 +150,7 @@ def _e2e_suite(name, runner, type, data, toolchain_name = "", toolchain = None):
if toolchain_name:
toolchain_name = "_" + toolchain_name

if type == "yarn" or type == "bun" or type == "pnpm":
if type == "yarn" or type == "bun" or type == "deno" or type == "pnpm":
args.append("--package-manager=%s" % type)
args.append("--esbuild")
tests = PACKAGE_MANAGER_SUBSET_TESTS
Expand Down
1 change: 1 addition & 0 deletions tests/legacy-cli/e2e/setup/100-global-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const PACKAGE_MANAGER_VERSION = {
'yarn': '1.22.22',
'pnpm': '10.17.1',
'bun': '1.3.2',
'deno': '2.5.6',
};

export default async function () {
Expand Down
6 changes: 5 additions & 1 deletion tests/legacy-cli/e2e/tests/misc/create-angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { equal } from 'node:assert';
import { join, resolve } from 'node:path';
import { expectFileToExist, readFile, rimraf } from '../../utils/fs';
import { getActivePackageManager } from '../../utils/packages';
import { silentBun, silentNpm, silentPnpm, silentYarn } from '../../utils/process';
import { silentBun, silentDeno, silentNpm, silentPnpm, silentYarn } from '../../utils/process';

export default async function () {
const currentDirectory = process.cwd();
Expand All @@ -25,6 +25,10 @@ export default async function () {
case 'bun':
await silentBun('create', '@angular', projectName, '--style=scss');

break;
case 'deno':
await silentDeno('init', '--npm', '@angular', projectName, '--style=scss');

break;
case 'pnpm':
await silentPnpm('create', '@angular', projectName, '--style=scss');
Expand Down
12 changes: 10 additions & 2 deletions tests/legacy-cli/e2e/utils/packages.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getGlobalVariable } from './env';
import { ProcessOutput, silentBun, silentNpm, silentPnpm, silentYarn } from './process';
import { ProcessOutput, silentBun, silentDeno, silentNpm, silentPnpm, silentYarn } from './process';

export interface PkgInfo {
readonly name: string;
readonly version: string;
readonly path: string;
}

export function getActivePackageManager(): 'npm' | 'yarn' | 'bun' | 'pnpm' {
export function getActivePackageManager(): 'npm' | 'yarn' | 'bun' | 'deno' | 'pnpm' {
return getGlobalVariable('package-manager');
}

Expand All @@ -29,6 +29,9 @@ export async function installWorkspacePackages(options?: { force?: boolean }): P
case 'bun':
await silentBun('install');
break;
case 'deno':
await silentDeno('install');
break;
}
}

Expand All @@ -41,6 +44,8 @@ export function installPackage(specifier: string, registry?: string): Promise<Pr
return silentYarn('add', specifier, ...registryOption);
case 'bun':
return silentBun('add', specifier, ...registryOption);
case 'deno':
return silentDeno('add', specifier, ...registryOption);
case 'pnpm':
return silentPnpm('add', specifier, ...registryOption);
}
Expand All @@ -58,6 +63,9 @@ export async function uninstallPackage(name: string): Promise<void> {
case 'bun':
await silentBun('remove', name);
break;
case 'deno':
await silentDeno('remove', name);
break;
case 'pnpm':
await silentPnpm('remove', name);
break;
Expand Down
4 changes: 4 additions & 0 deletions tests/legacy-cli/e2e/utils/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ export function silentBun(...args: string[]) {
return _exec({ silent: true }, 'bun', args);
}

export function silentDeno(...args: string[]) {
return _exec({ silent: true }, 'deno', args);
}

export function globalNpm(args: string[], env?: NodeJS.ProcessEnv) {
if (!process.env.LEGACY_CLI_RUNNER) {
throw new Error(
Expand Down