Skip to content

Commit

Permalink
refactor(@angular/cli): remove use of global require resolve
Browse files Browse the repository at this point in the history
The global require function is not present in Node.js ESM mode. To support
the eventual transition of the `@angular/cli` package to ESM, usage of the
`require.resolve` function has been converted to use locally created `require`
functions via `createRequire` from the `module` builtin.
  • Loading branch information
clydin committed Sep 20, 2022
1 parent 113e2c0 commit 326e923
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 18 deletions.
4 changes: 3 additions & 1 deletion packages/angular/cli/lib/init.ts
Expand Up @@ -9,6 +9,7 @@
import 'symbol-observable';
// symbol polyfill must go first
import { promises as fs } from 'fs';
import { createRequire } from 'module';
import * as path from 'path';
import { SemVer, major } from 'semver';
import { colors } from '../src/utilities/color';
Expand Down Expand Up @@ -47,7 +48,8 @@ let forceExit = false;
try {
// No error implies a projectLocalCli, which will load whatever
// version of ng-cli you have installed in a local package.json
const projectLocalCli = require.resolve('@angular/cli', { paths: [process.cwd()] });
const cwdRequire = createRequire(process.cwd() + '/');
const projectLocalCli = cwdRequire.resolve('@angular/cli');
cli = await import(projectLocalCli);

const globalVersion = new SemVer(VERSION.full);
Expand Down
Expand Up @@ -10,7 +10,7 @@ import { RuleFactory, SchematicsException, Tree } from '@angular-devkit/schemati
import { FileSystemCollectionDesc, NodeModulesEngineHost } from '@angular-devkit/schematics/tools';
import { readFileSync } from 'fs';
import { parse as parseJson } from 'jsonc-parser';
import nodeModule from 'module';
import { createRequire } from 'module';
import { dirname, resolve } from 'path';
import { Script } from 'vm';
import { assertIsError } from '../../utilities/error';
Expand Down Expand Up @@ -63,7 +63,8 @@ export class SchematicEngineHost extends NodeModulesEngineHost {
// Mimic behavior of ExportStringRef class used in default behavior
const fullPath = path[0] === '.' ? resolve(parentPath ?? process.cwd(), path) : path;

const schematicFile = require.resolve(fullPath, { paths: [parentPath] });
const referenceRequire = createRequire(__filename);
const schematicFile = referenceRequire.resolve(fullPath, { paths: [parentPath] });

if (shouldWrapSchematic(schematicFile, !!collectionDescription?.encapsulation)) {
const schematicPath = dirname(schematicFile);
Expand Down Expand Up @@ -128,8 +129,8 @@ function wrap(
moduleCache: Map<string, unknown>,
exportName?: string,
): () => unknown {
const hostRequire = nodeModule.createRequire(__filename);
const schematicRequire = nodeModule.createRequire(schematicFile);
const hostRequire = createRequire(__filename);
const schematicRequire = createRequire(schematicFile);

const customRequire = function (id: string) {
if (legacyModules[id]) {
Expand Down
13 changes: 6 additions & 7 deletions packages/angular/cli/src/commands/add/cli.ts
Expand Up @@ -8,6 +8,7 @@

import { analytics, tags } from '@angular-devkit/core';
import { NodePackageDoesNotSupportSchematics } from '@angular-devkit/schematics/tools';
import { createRequire } from 'module';
import npa from 'npm-package-arg';
import { dirname, join } from 'path';
import { compare, intersects, prerelease, satisfies, valid } from 'semver';
Expand Down Expand Up @@ -61,6 +62,7 @@ export class AddCommandModule
longDescriptionPath = join(__dirname, 'long-description.md');
protected override allowPrivateSchematics = true;
private readonly schematicName = 'ng-add';
private rootRequire = createRequire(this.context.root + '/');

override async builder(argv: Argv): Promise<Argv<AddCommandArgs>> {
const localYargs = (await super.builder(argv))
Expand Down Expand Up @@ -276,9 +278,8 @@ export class AddCommandModule
packageIdentifier.raw,
registry ? [`--registry="${registry}"`] : undefined,
);
const resolvedCollectionPath = require.resolve(join(collectionName, 'package.json'), {
paths: [tempNodeModules],
});
const tempRequire = createRequire(tempNodeModules + '/');
const resolvedCollectionPath = tempRequire.resolve(join(collectionName, 'package.json'));

if (!success) {
return 1;
Expand Down Expand Up @@ -341,7 +342,7 @@ export class AddCommandModule

private isPackageInstalled(name: string): boolean {
try {
require.resolve(join(name, 'package.json'), { paths: [this.context.root] });
this.rootRequire.resolve(join(name, 'package.json'));

return true;
} catch (e) {
Expand Down Expand Up @@ -400,9 +401,7 @@ export class AddCommandModule
const { logger, root } = this.context;
let installedPackage;
try {
installedPackage = require.resolve(join(name, 'package.json'), {
paths: [root],
});
installedPackage = this.rootRequire.resolve(join(name, 'package.json'));
} catch {}

if (installedPackage) {
Expand Down
14 changes: 8 additions & 6 deletions packages/angular/cli/src/commands/update/cli.ts
Expand Up @@ -10,6 +10,7 @@ import { UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics';
import { NodeWorkflow } from '@angular-devkit/schematics/tools';
import { SpawnSyncReturns, execSync, spawnSync } from 'child_process';
import { existsSync, promises as fs } from 'fs';
import { createRequire } from 'module';
import npa from 'npm-package-arg';
import pickManifest from 'npm-pick-manifest';
import * as path from 'path';
Expand Down Expand Up @@ -483,7 +484,8 @@ export class UpdateCommandModule extends CommandModule<UpdateCommandArgs> {
// Try to resolve from package location.
// This avoids issues with package hoisting.
try {
migrations = require.resolve(migrations, { paths: [packagePath] });
const packageRequire = createRequire(packagePath + '/');
migrations = packageRequire.resolve(migrations);
} catch (e) {
assertIsError(e);
if (e.code === 'MODULE_NOT_FOUND') {
Expand Down Expand Up @@ -717,6 +719,7 @@ export class UpdateCommandModule extends CommandModule<UpdateCommandArgs> {
}[];

if (success && migrations) {
const rootRequire = createRequire(this.context.root + '/');
for (const migration of migrations) {
// Resolve the package from the workspace root, as otherwise it will be resolved from the temp
// installed CLI version.
Expand All @@ -728,15 +731,13 @@ export class UpdateCommandModule extends CommandModule<UpdateCommandArgs> {
try {
packagePath = path.dirname(
// This may fail if the `package.json` is not exported as an entry point
require.resolve(path.join(migration.package, 'package.json'), {
paths: [this.context.root],
}),
rootRequire.resolve(path.join(migration.package, 'package.json')),
);
} catch (e) {
assertIsError(e);
if (e.code === 'MODULE_NOT_FOUND') {
// Fallback to trying to resolve the package's main entry point
packagePath = require.resolve(migration.package, { paths: [this.context.root] });
packagePath = rootRequire.resolve(migration.package);
} else {
throw e;
}
Expand Down Expand Up @@ -768,7 +769,8 @@ export class UpdateCommandModule extends CommandModule<UpdateCommandArgs> {
// Try to resolve from package location.
// This avoids issues with package hoisting.
try {
migrations = require.resolve(migration.collection, { paths: [packagePath] });
const packageRequire = createRequire(packagePath + '/');
migrations = packageRequire.resolve(migration.collection);
} catch (e) {
assertIsError(e);
if (e.code === 'MODULE_NOT_FOUND') {
Expand Down

0 comments on commit 326e923

Please sign in to comment.