fix(js): refactor ensurePackage (#15074)

This commit is contained in:
Jason Jean 2023-02-22 18:47:40 -05:00 committed by GitHub
parent 73bc2e1c91
commit fa6e8ea987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 661 additions and 428 deletions

View File

@ -134,7 +134,7 @@ It only uses language primitives and immutable objects
- [output](../../devkit/documents/nrwl_devkit#output)
- [workspaceRoot](../../devkit/documents/nrwl_devkit#workspaceroot)
### Functions
### Other Functions
- [addDependenciesToPackageJson](../../devkit/documents/nrwl_devkit#adddependenciestopackagejson)
- [addProjectConfiguration](../../devkit/documents/nrwl_devkit#addprojectconfiguration)
@ -149,7 +149,6 @@ It only uses language primitives and immutable objects
- [defaultTasksRunner](../../devkit/documents/nrwl_devkit#defaulttasksrunner)
- [detectPackageManager](../../devkit/documents/nrwl_devkit#detectpackagemanager)
- [detectWorkspaceScope](../../devkit/documents/nrwl_devkit#detectworkspacescope)
- [ensurePackage](../../devkit/documents/nrwl_devkit#ensurepackage)
- [extractLayoutDirectory](../../devkit/documents/nrwl_devkit#extractlayoutdirectory)
- [formatFiles](../../devkit/documents/nrwl_devkit#formatfiles)
- [generateFiles](../../devkit/documents/nrwl_devkit#generatefiles)
@ -203,6 +202,10 @@ It only uses language primitives and immutable objects
- [writeJson](../../devkit/documents/nrwl_devkit#writejson)
- [writeJsonFile](../../devkit/documents/nrwl_devkit#writejsonfile)
### Utils Functions
- [ensurePackage](../../devkit/documents/nrwl_devkit#ensurepackage)
## Project Graph Enumerations
### DependencyType
@ -823,7 +826,7 @@ Implementation of a target of a project that handles multiple projects to be bat
**workspaceRoot**: `string`
## Functions
## Other Functions
### addDependenciesToPackageJson
@ -1182,39 +1185,6 @@ Detect workspace scope from the package.json name
---
### ensurePackage
**ensurePackage**(`tree`, `pkg`, `requiredVersion`, `options?`): `void`
Ensure that dependencies and devDependencies from package.json are installed at the required versions.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
This will check that @nrwl/jest@<nxVersion> exists in devDependencies.
If it exists then function returns, otherwise it will install the package before continuing.
When running with --dryRun, the function will throw when dependencies are missing.
#### Parameters
| Name | Type | Description |
| :------------------------ | :------------------------------------------------ | :----------------------------------------------------------------- |
| `tree` | [`Tree`](../../devkit/documents/nrwl_devkit#tree) | the file system tree |
| `pkg` | `string` | the package to check (e.g. @nrwl/jest) |
| `requiredVersion` | `string` | the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0) |
| `options` | `Object` | |
| `options.dev?` | `boolean` | - |
| `options.throwOnMissing?` | `boolean` | - |
#### Returns
`void`
---
### extractLayoutDirectory
**extractLayoutDirectory**(`directory`): `Object`
@ -2431,3 +2401,66 @@ Serializes the given data to JSON and writes it to a file.
#### Returns
`void`
---
## Utils Functions
### ensurePackage
**ensurePackage**(`tree`, `pkg`, `requiredVersion`, `options?`): `void`
**`deprecated`** Use the other function signature without a Tree
Use a package that has not been installed as a dependency.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
This install the @nrwl/jest@<nxVersion> and return the module
When running with --dryRun, the function will throw when dependencies are missing.
#### Parameters
| Name | Type | Description |
| :------------------------ | :------------------------------------------------ | :----------------------------------------------------------------- |
| `tree` | [`Tree`](../../devkit/documents/nrwl_devkit#tree) | the file system tree |
| `pkg` | `string` | the package to check (e.g. @nrwl/jest) |
| `requiredVersion` | `string` | the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0) |
| `options?` | `Object` | - |
| `options.dev?` | `boolean` | - |
| `options.throwOnMissing?` | `boolean` | - |
#### Returns
`void`
**ensurePackage**<`T`\>(`pkg`, `version`): `T`
Ensure that dependencies and devDependencies from package.json are installed at the required versions.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
#### Type parameters
| Name | Type |
| :--- | :------------------------ |
| `T` | extends `unknown` = `any` |
#### Parameters
| Name | Type | Description |
| :-------- | :------- | :---------------------------------------------------------- |
| `pkg` | `string` | the package to install and require |
| `version` | `string` | the version to install if the package doesn't exist already |
#### Returns
`T`

View File

@ -134,7 +134,7 @@ It only uses language primitives and immutable objects
- [output](../../devkit/documents/nrwl_devkit#output)
- [workspaceRoot](../../devkit/documents/nrwl_devkit#workspaceroot)
### Functions
### Other Functions
- [addDependenciesToPackageJson](../../devkit/documents/nrwl_devkit#adddependenciestopackagejson)
- [addProjectConfiguration](../../devkit/documents/nrwl_devkit#addprojectconfiguration)
@ -149,7 +149,6 @@ It only uses language primitives and immutable objects
- [defaultTasksRunner](../../devkit/documents/nrwl_devkit#defaulttasksrunner)
- [detectPackageManager](../../devkit/documents/nrwl_devkit#detectpackagemanager)
- [detectWorkspaceScope](../../devkit/documents/nrwl_devkit#detectworkspacescope)
- [ensurePackage](../../devkit/documents/nrwl_devkit#ensurepackage)
- [extractLayoutDirectory](../../devkit/documents/nrwl_devkit#extractlayoutdirectory)
- [formatFiles](../../devkit/documents/nrwl_devkit#formatfiles)
- [generateFiles](../../devkit/documents/nrwl_devkit#generatefiles)
@ -203,6 +202,10 @@ It only uses language primitives and immutable objects
- [writeJson](../../devkit/documents/nrwl_devkit#writejson)
- [writeJsonFile](../../devkit/documents/nrwl_devkit#writejsonfile)
### Utils Functions
- [ensurePackage](../../devkit/documents/nrwl_devkit#ensurepackage)
## Project Graph Enumerations
### DependencyType
@ -823,7 +826,7 @@ Implementation of a target of a project that handles multiple projects to be bat
**workspaceRoot**: `string`
## Functions
## Other Functions
### addDependenciesToPackageJson
@ -1182,39 +1185,6 @@ Detect workspace scope from the package.json name
---
### ensurePackage
**ensurePackage**(`tree`, `pkg`, `requiredVersion`, `options?`): `void`
Ensure that dependencies and devDependencies from package.json are installed at the required versions.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
This will check that @nrwl/jest@<nxVersion> exists in devDependencies.
If it exists then function returns, otherwise it will install the package before continuing.
When running with --dryRun, the function will throw when dependencies are missing.
#### Parameters
| Name | Type | Description |
| :------------------------ | :------------------------------------------------ | :----------------------------------------------------------------- |
| `tree` | [`Tree`](../../devkit/documents/nrwl_devkit#tree) | the file system tree |
| `pkg` | `string` | the package to check (e.g. @nrwl/jest) |
| `requiredVersion` | `string` | the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0) |
| `options` | `Object` | |
| `options.dev?` | `boolean` | - |
| `options.throwOnMissing?` | `boolean` | - |
#### Returns
`void`
---
### extractLayoutDirectory
**extractLayoutDirectory**(`directory`): `Object`
@ -2431,3 +2401,66 @@ Serializes the given data to JSON and writes it to a file.
#### Returns
`void`
---
## Utils Functions
### ensurePackage
**ensurePackage**(`tree`, `pkg`, `requiredVersion`, `options?`): `void`
**`deprecated`** Use the other function signature without a Tree
Use a package that has not been installed as a dependency.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
This install the @nrwl/jest@<nxVersion> and return the module
When running with --dryRun, the function will throw when dependencies are missing.
#### Parameters
| Name | Type | Description |
| :------------------------ | :------------------------------------------------ | :----------------------------------------------------------------- |
| `tree` | [`Tree`](../../devkit/documents/nrwl_devkit#tree) | the file system tree |
| `pkg` | `string` | the package to check (e.g. @nrwl/jest) |
| `requiredVersion` | `string` | the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0) |
| `options?` | `Object` | - |
| `options.dev?` | `boolean` | - |
| `options.throwOnMissing?` | `boolean` | - |
#### Returns
`void`
**ensurePackage**<`T`\>(`pkg`, `version`): `T`
Ensure that dependencies and devDependencies from package.json are installed at the required versions.
For example:
```typescript
ensurePackage(tree, '@nrwl/jest', nxVersion);
```
#### Type parameters
| Name | Type |
| :--- | :------------------------ |
| `T` | extends `unknown` = `any` |
#### Parameters
| Name | Type | Description |
| :-------- | :------- | :---------------------------------------------------------- |
| `pkg` | `string` | the package to install and require |
| `version` | `string` | the version to install if the package doesn't exist already |
#### Returns
`T`

View File

@ -17,7 +17,11 @@
"type": "boolean",
"aliases": ["skip-format"],
"description": "Skip formatting files.",
"default": true,
"x-priority": "internal"
},
"skipPackageJson": {
"type": "boolean",
"description": "Skip adding package.json dependencies",
"x-priority": "internal"
},
"tsConfigName": {

View File

@ -34,6 +34,10 @@ describe('nx init (for CRA)', () => {
});
it('should convert to an integrated workspace with Vite', () => {
// TODO investigate why this is broken
const originalPM = process.env.SELECTED_PM;
process.env.SELECTED_PM = originalPM === 'pnpm' ? 'yarn' : originalPM;
const appName = 'my-app';
createReactApp(appName);
@ -53,9 +57,13 @@ describe('nx init (for CRA)', () => {
const unitTestsOutput = runCLI(`test ${appName}`);
expect(unitTestsOutput).toContain('Successfully ran target test');
process.env.SELECTED_PM = originalPM;
});
it('should convert to an integrated workspace with Vite with custom port', () => {
// TODO investigate why this is broken
const originalPM = process.env.SELECTED_PM;
process.env.SELECTED_PM = originalPM === 'pnpm' ? 'yarn' : originalPM;
const appName = 'my-app';
createReactApp(appName);
updateFile(`.env`, `NOT_THE_PORT=8000\nPORT=3000\nSOMETHING_ELSE=whatever`);
@ -71,6 +79,7 @@ describe('nx init (for CRA)', () => {
const unitTestsOutput = runCLI(`test ${appName}`);
expect(unitTestsOutput).toContain('Successfully ran target test');
process.env.SELECTED_PM = originalPM;
});
it('should convert to a standalone workspace with craco (webpack)', () => {

View File

@ -1,6 +1,7 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments } from '@nrwl/devkit';
import type { NormalizedSchema } from './normalized-schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function convertToStandaloneApp(tree: Tree, options: NormalizedSchema) {
const pathToAppModule = joinPathFragments(
@ -23,6 +24,7 @@ function updateMainEntrypoint(
) {
let routerModuleSetup: string;
if (options.routing) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const appModuleContents = tree.read(pathToAppModule, 'utf-8');
const ast = tsquery.ast(appModuleContents);
@ -73,6 +75,7 @@ function updateAppComponent(tree: Tree, options: NormalizedSchema) {
);
const appComponentContents = tree.read(pathToAppComponent, 'utf-8');
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const ast = tsquery.ast(appComponentContents);
const COMPONENT_DECORATOR_SELECTOR =

View File

@ -1,6 +1,7 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments } from '@nrwl/devkit';
import type { NormalizedSchema } from './normalized-schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function convertToStandaloneApp(tree: Tree, options: NormalizedSchema) {
const pathToAppModule = joinPathFragments(
@ -21,6 +22,7 @@ function updateMainEntrypoint(
tree: Tree,
pathToAppModule: string
) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
let routerModuleSetup: string;
if (options.routing) {
@ -63,6 +65,7 @@ bootstrapApplication(AppComponent${
}).catch((err) => console.error(err));`;
function updateAppComponent(tree: Tree, options: NormalizedSchema) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const pathToAppComponent = joinPathFragments(
options.appProjectRoot,

View File

@ -3,6 +3,7 @@ import type { NormalizedSchema } from './normalized-schema';
import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addImportToModule } from '../../../utils/nx-devkit/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -14,7 +15,7 @@ export function addRouterRootConfiguration(
const moduleSource = host.read(modulePath, 'utf-8');
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
let sourceFile = tsModule.createSourceFile(
modulePath,

View File

@ -5,6 +5,7 @@ import { replaceNodeValue } from '@nrwl/workspace/src/utilities/ast-utils';
import { getDecoratorPropertyValueNode } from '../../../utils/nx-devkit/ast-utils';
import { nrwlHomeTemplate } from './nrwl-home-tpl';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -13,7 +14,7 @@ export async function updateAppComponentTemplate(
options: NormalizedSchema
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const content = options.routing

View File

@ -5,12 +5,13 @@ import {
replaceIntoToTestBed,
} from '../../../utils/nx-devkit/ast-utils';
import type { NormalizedSchema } from './normalized-schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
export function updateComponentSpec(host: Tree, options: NormalizedSchema) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
if (options.skipTests !== true) {

View File

@ -7,6 +7,7 @@ import {
} from '@nrwl/workspace/src/utilities/ast-utils';
import { getDecoratorPropertyValueNode } from '../../../utils/nx-devkit/ast-utils';
import { nrwlHomeTemplate } from './nrwl-home-tpl';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -15,7 +16,7 @@ export function updateNxComponentTemplate(
options: NormalizedSchema
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const componentPath = `${options.appProjectRoot}/src/app/nx-welcome.component.ts`;

View File

@ -1,6 +1,7 @@
import type { Tree } from '@nrwl/devkit';
import type { StringLiteral } from 'typescript';
import { getRelativeImportToFile } from '../../utils/path';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function shouldExportInEntryPoint(
tree: Tree,
@ -11,6 +12,7 @@ export function shouldExportInEntryPoint(
return false;
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const moduleImportPath = getRelativeImportToFile(entryPoint, modulePath);
const entryPointContent = tree.read(entryPoint, 'utf-8');

View File

@ -1,9 +1,11 @@
import type { SourceFile } from 'typescript';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function checkOutputNameMatchesProjectName(
ast: SourceFile,
projectName: string
) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const OUTPUT_SELECTOR =
'PropertyAssignment:has(Identifier[name=output]) > ObjectLiteralExpression:has(PropertyAssignment:has(Identifier[name=uniqueName]))';

View File

@ -1,6 +1,8 @@
import type { SourceFile, Node } from 'typescript';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function checkSharedNpmPackagesMatchExpected(ast: SourceFile) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const SHARE_HELPER_SELECTOR =
'PropertyAssignment:has(Identifier[name=shared]) > CallExpression:has(Identifier[name=share])';

View File

@ -1,4 +1,5 @@
import type { SourceFile } from 'typescript';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export type IsHostRemoteConfigResult = 'host' | 'remote' | 'both' | false;
@ -11,6 +12,7 @@ const PROPERTY_SELECTOR = 'ObjectLiteralExpression > PropertyAssignment';
export function isHostRemoteConfig(ast: SourceFile): IsHostRemoteConfigResult {
let isHost = false;
let isRemote = false;
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const remotesNodes = tsquery(ast, REMOTES_EXPRESSION_SELECTOR, {
@ -33,6 +35,7 @@ export function isHostRemoteConfig(ast: SourceFile): IsHostRemoteConfigResult {
}
export function getRemotesFromHost(ast: SourceFile) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const remotesObjectNodes = tsquery(ast, REMOTES_EXPRESSION_SELECTOR, {
visitAllChildren: true,
@ -63,6 +66,7 @@ export function getRemotesFromHost(ast: SourceFile) {
}
export function getExposedModulesFromRemote(ast: SourceFile) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const exposesObjectNodes = tsquery(ast, EXPOSES_EXPRESSION_SELECTOR, {
visitAllChildren: true,

View File

@ -1,4 +1,5 @@
import type { Tree } from '@nrwl/devkit';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function parseASTOfWebpackConfig(
tree: Tree,
@ -9,6 +10,7 @@ export function parseASTOfWebpackConfig(
`Cannot migrate webpack config at \`${pathToWebpackConfig}\` as it does not exist. Please ensure this file exists and that the path to the file is correct.`
);
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const source = tree.read(pathToWebpackConfig, 'utf-8');

View File

@ -1,5 +1,6 @@
import { cypressInitGenerator } from '@nrwl/cypress';
import {
addDependenciesToPackageJson,
ensurePackage,
formatFiles,
GeneratorCallback,
@ -27,13 +28,6 @@ export async function angularInitGenerator(
): Promise<GeneratorCallback> {
const options = normalizeOptions(rawOptions);
setDefaults(host, options);
await jsInitGenerator(host, {
...options,
tsConfigName: options.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
js: false,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
const peerDepsToInstall = [
@ -49,17 +43,36 @@ export async function angularInitGenerator(
devkitVersion ??=
getInstalledPackageVersion(host, '@angular-devkit/build-angular') ??
backwardCompatibleVersions.angularV14.angularDevkitVersion;
ensurePackage(host, pkg, devkitVersion);
try {
ensurePackage(pkg, devkitVersion);
} catch {
// @schematics/angular cannot be required so this fails but this will still allow wrapping the schematic later on
}
if (!options.skipPackageJson) {
tasks.push(
addDependenciesToPackageJson(host, {}, { [pkg]: devkitVersion })
);
}
}
});
const jsTask = await jsInitGenerator(host, {
...options,
js: false,
tsConfigName: options.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
skipFormat: true,
});
tasks.push(jsTask);
if (!options.skipPackageJson) {
tasks.push(updateDependencies(host));
}
const unitTestTask = await addUnitTestRunner(host, options);
tasks.push(unitTestTask);
const e2eTask = addE2ETestRunner(host, options);
const e2eTask = await addE2ETestRunner(host, options);
tasks.push(e2eTask);
addGitIgnoreEntry(host, '.angular');
@ -169,7 +182,10 @@ async function addUnitTestRunner(
}
}
function addE2ETestRunner(tree: Tree, options: Schema): GeneratorCallback {
async function addE2ETestRunner(
tree: Tree,
options: Schema
): Promise<GeneratorCallback> {
switch (options.e2eTestRunner) {
case E2eTestRunner.Protractor:
return !options.skipPackageJson
@ -193,7 +209,7 @@ function addE2ETestRunner(tree: Tree, options: Schema): GeneratorCallback {
)
: () => {};
case E2eTestRunner.Cypress:
return cypressInitGenerator(tree, {
return await cypressInitGenerator(tree, {
skipPackageJson: options.skipPackageJson,
});
default:

View File

@ -2,21 +2,8 @@ jest.mock('@nrwl/devkit', () => ({
...jest.requireActual('@nrwl/devkit'),
// need to mock so it doesn't resolve what the workspace has installed
// and be able to test with different versions
ensurePackage: jest.fn().mockImplementation((tree, pkg, version, options) => {
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
...(options?.dev === false ? { [pkg]: version } : {}),
},
devDependencies: {
...json.devDependencies,
...(options?.dev ?? true ? { [pkg]: version } : {}),
},
}));
}),
ensurePackage: jest.fn(),
}));
import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Linter } from '@nrwl/linter';
@ -30,6 +17,7 @@ describe('init', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
require('@nrwl/devkit').ensurePackage.mockImplementation(() => {});
});
it('should add angular dependencies', async () => {

View File

@ -1,5 +1,6 @@
import { cypressInitGenerator } from '@nrwl/cypress';
import {
addDependenciesToPackageJson,
ensurePackage,
formatFiles,
GeneratorCallback,
@ -50,6 +51,9 @@ export async function angularInitGenerator(
return;
}
const tasks: GeneratorCallback[] = [];
const options = normalizeOptions(rawOptions);
const peerDepsToInstall = [
'@angular-devkit/core',
'@angular-devkit/schematics',
@ -63,27 +67,36 @@ export async function angularInitGenerator(
devkitVersion ??=
getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ??
angularDevkitVersion;
ensurePackage(tree, pkg, devkitVersion);
try {
ensurePackage(pkg, devkitVersion);
} catch {
// @schematics/angular cannot be required so this fails but this will still allow wrapping the schematic later on
}
if (!options.skipPackageJson) {
tasks.push(
addDependenciesToPackageJson(tree, {}, { [pkg]: devkitVersion })
);
}
}
});
const options = normalizeOptions(rawOptions);
setDefaults(tree, options);
await jsInitGenerator(tree, {
const jsTask = await jsInitGenerator(tree, {
...options,
tsConfigName: options.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
js: false,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(jsTask);
if (!options.skipPackageJson) {
tasks.push(updateDependencies(tree));
}
const unitTestTask = await addUnitTestRunner(tree, options);
tasks.push(unitTestTask);
const e2eTask = addE2ETestRunner(tree, options);
const e2eTask = await addE2ETestRunner(tree, options);
tasks.push(e2eTask);
addGitIgnoreEntry(tree, '.angular');
@ -191,7 +204,10 @@ async function addUnitTestRunner(
}
}
function addE2ETestRunner(tree: Tree, options: Schema): GeneratorCallback {
async function addE2ETestRunner(
tree: Tree,
options: Schema
): Promise<GeneratorCallback> {
switch (options.e2eTestRunner) {
case E2eTestRunner.Protractor:
return !options.skipPackageJson

View File

@ -8,6 +8,7 @@ import {
} from '@nrwl/devkit';
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
import { v14TestFile } from './v14-test-file';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function generateKarmaProjectFiles(tree: Tree, project: string): void {
const projectConfig = readProjectConfiguration(tree, project);
@ -75,6 +76,7 @@ function isUsingConfigSetInBaseKarmaConfig(tree: Tree) {
if (!tree.exists('karma.conf.js')) {
return false;
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const CONFIG_SET_SELECTOR =

View File

@ -3,6 +3,7 @@ import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addImportToModule } from '../../../utils/nx-devkit/ast-utils';
import { NormalizedSchema } from './normalized-schema';
import { addRoute } from '../../../utils/nx-devkit/route-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -14,7 +15,7 @@ export function addChildren(
throw new Error(`Cannot find '${options.parent}'`);
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const routeFileSource = tree.read(options.parent, 'utf-8');

View File

@ -3,6 +3,7 @@ import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addImportToModule } from '../../../utils/nx-devkit/ast-utils';
import { NormalizedSchema } from './normalized-schema';
import { dirname } from 'path';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -11,7 +12,7 @@ export function addLazyLoadedRouterConfiguration(
options: NormalizedSchema['libraryOptions']
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const constName = `${names(options.fileName).propertyName}Routes`;
tree.write(

View File

@ -1,6 +1,7 @@
import { names, Tree } from '@nrwl/devkit';
import { NormalizedSchema } from './normalized-schema';
import { addRoute } from '../../../utils/nx-devkit/route-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -12,7 +13,7 @@ export function addLoadChildren(
throw new Error(`Cannot find '${options.parent}'`);
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const moduleSource = tree.read(options.parent, 'utf-8');

View File

@ -4,6 +4,7 @@ import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addImportToModule } from '../../../utils/nx-devkit/ast-utils';
import { NormalizedSchema } from './normalized-schema';
import { dirname } from 'path';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -12,7 +13,7 @@ export function addRouterConfiguration(
options: NormalizedSchema['libraryOptions']
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const constName = `${names(options.fileName).propertyName}Routes`;
const moduleSource = tree.read(options.modulePath, 'utf-8');

View File

@ -29,21 +29,7 @@ jest.mock('@nrwl/devkit', () => {
createProjectGraphAsync: jest.fn().mockImplementation(() => projectGraph),
// need to mock so it doesn't resolve what the workspace has installed
// and be able to test with different versions
ensurePackage: jest
.fn()
.mockImplementation((tree, pkg, version, options) => {
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
...(options?.dev === false ? { [pkg]: version } : {}),
},
devDependencies: {
...json.devDependencies,
...(options?.dev ?? true ? { [pkg]: version } : {}),
},
}));
}),
ensurePackage: jest.fn().mockImplementation((pkg) => require(pkg)),
};
});

View File

@ -45,6 +45,7 @@ import type {
} from '../../utilities';
import { FileChangeRecorder } from '../../utilities';
import { ProjectMigrator } from './project.migrator';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
type SupportedTargets = 'e2e';
const supportedTargets: Record<SupportedTargets, Target> = {
@ -577,6 +578,7 @@ export class E2eMigrator extends ProjectMigrator<SupportedTargets> {
}
private updateCypress10ConfigFile(configFilePath: string): void {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
this.cypressPreset = nxE2EPreset(configFilePath);
@ -657,6 +659,7 @@ export class E2eMigrator extends ProjectMigrator<SupportedTargets> {
recorder: FileChangeRecorder,
{ ...globalConfig }: CypressCommonConfig
): void {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const e2eConfig = {};
const presetSpreadAssignment = `...nxE2EPreset(__dirname),`;

View File

@ -12,6 +12,7 @@ import type * as ts from 'typescript';
import { ArrayLiteralExpression } from 'typescript';
import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addRoute } from '../../../utils/nx-devkit/route-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -121,7 +122,7 @@ function addLazyLoadedRouteToHostAppModule(
hostFederationType: 'dynamic' | 'static'
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const hostAppConfig = readProjectConfiguration(tree, options.host);

View File

@ -1,8 +1,10 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments, readProjectConfiguration } from '@nrwl/devkit';
import type { Schema } from '../schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function updateAppModule(tree: Tree, schema: Schema) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
// read the content of app module
const projectConfig = readProjectConfiguration(tree, schema.project);

View File

@ -1,9 +1,8 @@
import { installedCypressVersion } from '@nrwl/cypress/src/utils/cypress-version';
import { ensurePackage, Tree } from '@nrwl/devkit';
import { Tree } from '@nrwl/devkit';
import { writeJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Linter } from '@nrwl/linter';
import { nxVersion } from '../../utils/versions';
import { componentGenerator } from '../component/component';
import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point';
import { libraryGenerator } from '../library/library';
@ -48,7 +47,6 @@ describe('angularStories generator: libraries', () => {
beforeEach(async () => {
tree = await createStorybookTestWorkspaceForLib(libName);
ensurePackage(tree, '@nrwl/storybook', nxVersion);
cypressProjectGenerator = await (
await import('@nrwl/storybook')
).cypressProjectGenerator;

View File

@ -6,8 +6,10 @@ export async function generateStorybookConfiguration(
tree: Tree,
options: StorybookConfigurationOptions
): Promise<GeneratorCallback> {
ensurePackage(tree, '@nrwl/storybook', nxVersion);
const { configurationGenerator } = await import('@nrwl/storybook');
const { configurationGenerator } = ensurePackage(
'@nrwl/storybook',
nxVersion
);
return await configurationGenerator(tree, {
name: options.name,
uiFramework: '@storybook/angular',

View File

@ -16,6 +16,7 @@ import type {
import { SyntaxKind } from 'typescript';
import { getDecoratorMetadata } from '../../../utils/nx-devkit/ast-utils';
import type { EntryPoint } from './entry-point';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function getModuleDeclaredComponents(
file: SourceFile,
@ -78,6 +79,7 @@ export function getModuleFilePaths(
}
function hasNgModule(tree: Tree, filePath: string): boolean {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const fileContent = tree.read(filePath, 'utf-8');
const ast = tsquery.ast(fileContent);

View File

@ -9,6 +9,7 @@ import {
removeChange,
replaceChange,
} from '@nrwl/workspace/src/utilities/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -19,7 +20,7 @@ function _angularImportsFromNode(
_sourceFile: ts.SourceFile
): { [name: string]: string } {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const ms = node.moduleSpecifier;
let modulePath: string;
@ -89,7 +90,7 @@ export function getDecoratorMetadata(
module: string
): ts.Node[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const angularImports: { [name: string]: string } = findNodes(
source,
@ -169,7 +170,7 @@ function _addSymbolToDecoratorMetadata(
return source;
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
// Get all the children property assignment of object literals.
const matchingProperties: ts.ObjectLiteralElement[] = (
@ -415,7 +416,7 @@ export function addImportToTestBed(
symbolName: string
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allCalls: ts.CallExpression[] = <any>(
findNodes(source, tsModule.SyntaxKind.CallExpression)
@ -456,7 +457,7 @@ export function addDeclarationsToTestBed(
symbolName: string[]
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allCalls: ts.CallExpression[] = <any>(
findNodes(source, tsModule.SyntaxKind.CallExpression)
@ -498,7 +499,7 @@ export function replaceIntoToTestBed(
previousSymbol: string
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allCalls: ts.CallExpression[] = <any>(
findNodes(source, tsModule.SyntaxKind.CallExpression)
@ -592,7 +593,7 @@ function getListOfRoutes(
source: ts.SourceFile
): ts.NodeArray<ts.Expression> | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const imports: any = getMatchingProperty(
source,
@ -647,6 +648,7 @@ export function addProviderToBootstrapApplication(
filePath: string,
providerToAdd: string
) {
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const PROVIDERS_ARRAY_SELECTOR =
'CallExpression:has(Identifier[name=bootstrapApplication]) ObjectLiteralExpression > PropertyAssignment:has(Identifier[name=providers]) > ArrayLiteralExpression';
@ -749,7 +751,7 @@ export function readBootstrapInfo(
bootstrapComponentFileName: string;
} {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const config = readProjectConfiguration(host, app);
@ -831,7 +833,7 @@ export function getDecoratorPropertyValueNode(
module: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const moduleSourceText = host.read(modulePath)!.toString('utf-8');
const moduleSource = tsModule.createSourceFile(
@ -856,7 +858,7 @@ function getMatchingObjectLiteralElement(
property: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
return (
(node as ts.ObjectLiteralExpression).properties
@ -878,7 +880,7 @@ function getMatchingObjectLiteralElement(
export function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const buffer = host.read(path);
if (!buffer) {

View File

@ -1,6 +1,7 @@
import { Tree } from '@nrwl/devkit';
import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
import { addRouteToNgModule } from './ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -20,6 +21,7 @@ export function addRoute(
if (!tsModule) {
tsModule = require('typescript');
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
let routesFileContents = tree.read(routesFile, 'utf-8');
@ -92,6 +94,7 @@ export function addProviderToRoute(
);
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
let routesFileContents = tree.read(routesFile, 'utf-8');

View File

@ -265,16 +265,12 @@ export async function addLinter(host: Tree, options: CypressProjectSchema) {
export async function cypressProjectGenerator(host: Tree, schema: Schema) {
const options = normalizeOptions(host, schema);
await jsInitGenerator(host, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
const cypressVersion = installedCypressVersion();
// if there is an installed cypress version, then we don't call
// init since we want to keep the existing version that is installed
if (!cypressVersion) {
tasks.push(cypressInitGenerator(host, options));
tasks.push(await cypressInitGenerator(host, options));
}
if (schema.bundler === 'vite') {

View File

@ -21,7 +21,7 @@ describe('init', () => {
json.devDependencies[existing] = existingVersion;
return json;
});
cypressInitGenerator(tree, {});
await cypressInitGenerator(tree, {});
const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies.cypress).toBeDefined();
@ -39,7 +39,7 @@ describe('init', () => {
return json;
});
cypressInitGenerator(tree, {});
await cypressInitGenerator(tree, {});
expect(
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults.e2e

View File

@ -1,6 +1,7 @@
import {
addDependenciesToPackageJson,
convertNxGenerator,
GeneratorCallback,
readNxJson,
removeDependenciesFromPackageJson,
Tree,
@ -12,6 +13,8 @@ import {
typesNodeVersion,
} from '../../utils/versions';
import { Schema } from './schema';
import { initGenerator } from '@nrwl/js';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
function setupE2ETargetDefaults(tree: Tree) {
const nxJson = readNxJson(tree);
@ -47,9 +50,23 @@ function updateDependencies(tree: Tree) {
);
}
export function cypressInitGenerator(tree: Tree, options: Schema) {
export async function cypressInitGenerator(tree: Tree, options: Schema) {
setupE2ETargetDefaults(tree);
return !options.skipPackageJson ? updateDependencies(tree) : () => {};
const tasks: GeneratorCallback[] = [];
tasks.push(
await initGenerator(tree, {
...options,
skipFormat: true,
})
);
if (!options.skipPackageJson) {
tasks.push(updateDependencies(tree));
}
return runTasksInSerial(...tasks);
}
export default cypressInitGenerator;

View File

@ -1,3 +1,5 @@
export interface Schema {
skipPackageJson?: boolean;
skipFormat?: boolean;
}

View File

@ -10,6 +10,7 @@ import {
} from '@nrwl/devkit';
import { basename, dirname, extname, relative } from 'path';
import type { StringLiteral } from 'typescript';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
let tsquery: typeof import('@phenomnomnominal/tsquery').tsquery;
@ -269,11 +270,12 @@ export function updateImports(
oldImportPath: string,
newImportPath: string
) {
if (!tsquery) {
tsquery = require('@phenomnomnominal/tsquery').tsquery;
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
if (!tsquery) {
ensureTypescript();
tsquery = require('@phenomnomnominal/tsquery').tsquery;
}
const { isCallExpression, isExportDeclaration, isImportDeclaration } =
tsModule;

View File

@ -30,6 +30,7 @@
"dependencies": {
"ejs": "^3.1.7",
"ignore": "^5.0.4",
"tmp": "~0.2.1",
"tslib": "^2.3.0",
"@phenomnomnominal/tsquery": "4.1.1",
"semver": "7.3.4"

View File

@ -456,30 +456,11 @@ describe('ensurePackage', () => {
tree = createTree();
});
it('should return successfully when package is present', async () => {
it('should return package when present', async () => {
writeJson(tree, 'package.json', {});
expect(
ensurePackage(tree, '@nrwl/devkit', '>=15.0.0', {
throwOnMissing: true,
})
).toBeUndefined(); // return void
});
it('should throw when dependencies are missing', async () => {
writeJson(tree, 'package.json', {});
expect(() =>
ensurePackage(tree, '@nrwl/does-not-exist', '>=15.0.0', {
throwOnMissing: true,
})
).toThrow(/-D( -W)? @nrwl\/does-not-exist@>=15.0.0/);
expect(() =>
ensurePackage(tree, '@nrwl/does-not-exist', '>=15.0.0', {
dev: false,
throwOnMissing: true,
})
).toThrow('@nrwl/does-not-exist@>=15.0.0');
expect(ensurePackage('@nrwl/devkit', '>=15.0.0')).toEqual(
require('@nrwl/devkit')
); // return void
});
});

View File

@ -1,15 +1,17 @@
import { execSync } from 'child_process';
import { Module } from 'module';
import type { Tree } from 'nx/src/generators/tree';
import type { GeneratorCallback } from 'nx/src/config/misc-interfaces';
import { clean, coerce, gt, satisfies } from 'semver';
import { clean, coerce, gt } from 'semver';
import { installPackagesTask } from '../tasks/install-packages-task';
import { requireNx } from '../../nx';
import { dirSync } from 'tmp';
import { join } from 'path';
const { readJson, updateJson, getPackageManagerCommand, workspaceRoot } =
requireNx();
const { readJson, updateJson, getPackageManagerCommand } = requireNx();
const UNIDENTIFIED_VERSION = 'UNIDENTIFIED_VERSION';
const NON_SEMVER_TAGS = {
@ -360,6 +362,8 @@ function requiresRemovingOfPackages(
return needsDepsUpdate || needsDevDepsUpdate;
}
const packageMapCache = new Map<string, any>();
/**
* @typedef EnsurePackageOptions
* @type {object}
@ -367,6 +371,30 @@ function requiresRemovingOfPackages(
* @property {throwOnMissing} boolean throws an error when the package is missing
*/
/**
* @deprecated Use the other function signature without a Tree
*
* Use a package that has not been installed as a dependency.
*
* For example:
* ```typescript
* ensurePackage(tree, '@nrwl/jest', nxVersion)
* ```
* This install the @nrwl/jest@<nxVersion> and return the module
* When running with --dryRun, the function will throw when dependencies are missing.
*
* @param tree the file system tree
* @param pkg the package to check (e.g. @nrwl/jest)
* @param requiredVersion the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0)
* @param {EnsurePackageOptions} options?
*/
export function ensurePackage(
tree: Tree,
pkg: string,
requiredVersion: string,
options?: { dev?: boolean; throwOnMissing?: boolean }
): void;
/**
* Ensure that dependencies and devDependencies from package.json are installed at the required versions.
*
@ -374,85 +402,86 @@ function requiresRemovingOfPackages(
* ```typescript
* ensurePackage(tree, '@nrwl/jest', nxVersion)
* ```
* This will check that @nrwl/jest@<nxVersion> exists in devDependencies.
* If it exists then function returns, otherwise it will install the package before continuing.
* When running with --dryRun, the function will throw when dependencies are missing.
*
* @param tree the file system tree
* @param pkg the package to check (e.g. @nrwl/jest)
* @param requiredVersion the version or semver range to check (e.g. ~1.0.0, >=1.0.0 <2.0.0)
* @param {EnsurePackageOptions} options
* @param pkg the package to install and require
* @param version the version to install if the package doesn't exist already
*/
export function ensurePackage(
tree: Tree,
export function ensurePackage<T extends any = any>(
pkg: string,
requiredVersion: string,
options: {
dev?: boolean;
throwOnMissing?: boolean;
} = {}
): void {
// Read package and version from root package.json file.
const dev = options.dev ?? true;
const throwOnMissing =
options.throwOnMissing ?? process.env.NX_DRY_RUN === 'true'; // NX_DRY_RUN is set in `packages/nx/src/command-line/nx-commands.ts`
const pmc = getPackageManagerCommand();
let version = getPackageVersion(pkg);
// Otherwise try to read in from package.json. This is needed for E2E tests to pass.
if (!version) {
const packageJson = readJson(tree, 'package.json');
const field = dev ? 'devDependencies' : 'dependencies';
version = packageJson[field]?.[pkg];
version: string
): T;
export function ensurePackage<T extends any = any>(
pkgOrTree: string | Tree,
requiredVersionOrPackage: string,
maybeRequiredVersion?: string,
_?: never
): T {
let pkg: string;
let requiredVersion: string;
if (typeof pkgOrTree === 'string') {
pkg = pkgOrTree;
requiredVersion = requiredVersionOrPackage;
} else {
// Old Signature
pkg = requiredVersionOrPackage;
requiredVersion = maybeRequiredVersion;
}
if (
// Special case: When running Nx unit tests, the version read from package.json is "0.0.1".
!(
pkg.startsWith('@nrwl/') &&
(version === '0.0.1' || requiredVersion === '0.0.1')
) &&
// Normal case
!satisfies(version, requiredVersion, { includePrerelease: true })
) {
const installCmd = `${
dev ? pmc.addDev : pmc.add
} ${pkg}@${requiredVersion}`;
if (throwOnMissing) {
if (packageMapCache.has(pkg)) {
return packageMapCache.get(pkg) as T;
}
try {
return require(pkg);
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}
if (process.env.NX_DRY_RUN && process.env.NX_DRY_RUN !== 'false') {
throw new Error(
`Cannot install required package ${pkg} during a dry run. Run the generator without --dryRun, or install the package with "${installCmd}" and try again.`
'NOTE: This generator does not support --dry-run. If you are running this in Nx Console, it should execute fine once you hit the "Run" button.\n'
);
} else {
execSync(installCmd, {
cwd: tree.root,
}
const tempDir = dirSync().name;
execSync(`${getPackageManagerCommand().addDev} ${pkg}@${requiredVersion}`, {
cwd: tempDir,
stdio: [0, 1, 2],
});
}
}
addToNodePath(join(tempDir, 'node_modules'));
// Re-initialize the added paths into require
(Module as any)._initPaths();
const result = require(require.resolve(pkg, {
paths: [tempDir],
}));
packageMapCache.set(pkg, result);
return result;
}
/**
* Use another process to resolve the package.json path of the package (if it exists).
* Cannot use `require.resolve` here since there is an unclearable internal cache used by Node that can lead to issues
* when resolving the package after installation.
*
* See: https://github.com/nodejs/node/issues/31803
*/
function getPackageVersion(pkg: string): undefined | string {
try {
return execSync(
`node -e "console.log(require('${pkg}/package.json').version)"`,
{
cwd: workspaceRoot,
stdio: ['pipe', 'pipe', 'ignore'],
}
)
.toString()
.trim();
} catch (e) {
return undefined;
}
function addToNodePath(dir: string) {
// NODE_PATH is a delimited list of paths.
// The delimiter is different for windows.
const delimiter = require('os').platform() === 'win32' ? ';' : ':';
const paths = process.env.NODE_PATH
? process.env.NODE_PATH.split(delimiter)
: [];
// Add the tmp path
paths.push(dir);
// Update the env variable.
process.env.NODE_PATH = paths.join(delimiter);
}
function getPackageVersion(pkg: string): string {
return require(join(pkg, 'package.json')).version;
}
/**

View File

@ -43,13 +43,16 @@ import { initRootBabelConfig } from './lib/init-root-babel-config';
export async function expoInitGenerator(host: Tree, schema: Schema) {
addGitIgnoreEntry(host);
initRootBabelConfig(host);
await jsInitGenerator(host, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(host, {
...schema,
skipFormat: true,
})
);
if (!schema.skipPackageJson) {
tasks.push(moveDependency(host));
tasks.push(updateDependencies(host));

View File

@ -191,12 +191,15 @@ export async function jestInitGenerator(
schema: JestInitSchema
): Promise<GeneratorCallback> {
const options = normalizeOptions(schema);
await jsInitGenerator(tree, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(tree, {
...schema,
skipFormat: true,
})
);
createJestConfig(tree, options);
if (!options.skipPackageJson) {

View File

@ -3,6 +3,7 @@ import { applyChangesToString, ChangeType, Tree } from '@nrwl/devkit';
import { Config } from '@jest/types';
import { createContext, runInContext } from 'vm';
import { dirname, join } from 'path';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -18,7 +19,7 @@ function findPropertyAssignment(
propertyName: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
return object.properties.find((prop) => {
@ -42,7 +43,7 @@ export function addOrUpdateProperty(
path: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const { SyntaxKind } = tsModule;
@ -153,7 +154,7 @@ export function removeProperty(
properties: string[]
): ts.PropertyAssignment | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const propertyName = properties.shift();
@ -178,7 +179,7 @@ export function removeProperty(
function isModuleExport(node: ts.Statement) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
return (
@ -192,7 +193,7 @@ function isModuleExport(node: ts.Statement) {
function isDefaultExport(node: ts.Statement) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
return (
@ -210,7 +211,7 @@ export function jestConfigObjectAst(
fileContent: string
): ts.ObjectLiteralExpression {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const sourceFile = tsModule.createSourceFile(

View File

@ -1,33 +1,43 @@
import {
addDependenciesToPackageJson,
convertNxGenerator,
ensurePackage,
formatFiles,
generateFiles,
GeneratorCallback,
joinPathFragments,
Tree,
} from '@nrwl/devkit';
import { getRootTsConfigFileName } from '../../utils/typescript/ts-config';
import { typescriptVersion } from '../../utils/versions';
import { typescriptVersion, nxVersion } from '../../utils/versions';
import { InitSchema } from './schema';
export async function initGenerator(
host: Tree,
tree: Tree,
schema: InitSchema
): Promise<void> {
if (!schema.js) {
ensurePackage(host, 'typescript', typescriptVersion);
}
): Promise<GeneratorCallback> {
// add tsconfig.base.json
if (!getRootTsConfigFileName(host)) {
generateFiles(host, joinPathFragments(__dirname, './files'), '.', {
if (!getRootTsConfigFileName(tree)) {
generateFiles(tree, joinPathFragments(__dirname, './files'), '.', {
fileName: schema.tsConfigName ?? 'tsconfig.base.json',
});
}
const devDependencies = {
'@nrwl/js': nxVersion,
};
if (!schema.js) {
devDependencies['typescript'] = typescriptVersion;
}
const installTask = !schema.skipPackageJson
? addDependenciesToPackageJson(tree, {}, devDependencies)
: () => {};
if (!schema.skipFormat) {
await formatFiles(host);
await formatFiles(tree);
}
return installTask;
}
export default initGenerator;

View File

@ -1,5 +1,6 @@
export interface InitSchema {
js?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
tsConfigName?: string;
}

View File

@ -14,7 +14,11 @@
"type": "boolean",
"aliases": ["skip-format"],
"description": "Skip formatting files.",
"default": true,
"x-priority": "internal"
},
"skipPackageJson": {
"type": "boolean",
"description": "Skip adding package.json dependencies",
"x-priority": "internal"
},
"tsConfigName": {

View File

@ -54,11 +54,13 @@ export async function projectGenerator(
destinationDir: string,
filesDir: string
) {
await jsInitGenerator(tree, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(tree, {
...schema,
skipFormat: true,
})
);
const options = normalizeOptions(tree, schema, destinationDir);
createFiles(tree, options, `${filesDir}/lib`);
@ -68,9 +70,10 @@ export async function projectGenerator(
tasks.push(addProjectDependencies(tree, options));
if (options.bundler === 'vite') {
ensurePackage(tree, '@nrwl/vite', nxVersion);
// nx-ignore-next-line
const { viteConfigurationGenerator } = require('@nrwl/vite');
const { viteConfigurationGenerator } = ensurePackage(
'@nrwl/vite',
nxVersion
);
const viteTask = await viteConfigurationGenerator(tree, {
project: options.name,
newProject: true,
@ -98,9 +101,7 @@ export async function projectGenerator(
options.unitTestRunner === 'vitest' &&
options.bundler !== 'vite' // Test would have been set up already
) {
ensurePackage(tree, '@nrwl/vite', nxVersion);
// nx-ignore-next-line
const { vitestGenerator } = require('@nrwl/vite');
const { vitestGenerator } = ensurePackage('@nrwl/vite', nxVersion);
const vitestTask = await vitestGenerator(tree, {
project: options.name,
uiFramework: 'none',
@ -196,8 +197,7 @@ export async function addLint(
tree: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
ensurePackage(tree, '@nrwl/linter', nxVersion);
const { lintProjectGenerator } = require('@nrwl/linter');
const { lintProjectGenerator } = ensurePackage('@nrwl/linter', nxVersion);
return lintProjectGenerator(tree, {
project: options.name,
linter: options.linter,
@ -299,8 +299,7 @@ async function addJest(
tree: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
ensurePackage(tree, '@nrwl/jest', nxVersion);
const { jestProjectGenerator } = require('@nrwl/jest');
const { jestProjectGenerator } = ensurePackage('@nrwl/jest', nxVersion);
return await jestProjectGenerator(tree, {
...options,
project: options.name,

View File

@ -0,0 +1,9 @@
import { ensurePackage } from '@nrwl/devkit';
import { typescriptVersion } from '../versions';
export function ensureTypescript() {
return ensurePackage<typeof import('typescript')>(
'typescript',
typescriptVersion
);
}

View File

@ -24,9 +24,9 @@ export const WORKSPACE_RULES_PROJECT_NAME = 'eslint-rules';
export const WORKSPACE_PLUGIN_DIR = 'tools/eslint-rules';
export async function lintWorkspaceRulesProjectGenerator(tree: Tree) {
ensurePackage(tree, '@nrwl/jest/', nxVersion);
const { addPropertyToJestConfig, jestProjectGenerator } = await import(
'@nrwl/jest'
const { addPropertyToJestConfig, jestProjectGenerator } = ensurePackage(
'@nrwl/jest',
nxVersion
);
// Noop if the workspace rules project already exists

View File

@ -10,8 +10,7 @@ import { nxVersion } from '../../utils/versions';
export default async function eslint8Updates(tree: Tree) {
try {
ensurePackage(tree, '@nrwl/jest/', nxVersion);
const { addPropertyToJestConfig } = await import('@nrwl/jest');
const { addPropertyToJestConfig } = ensurePackage('@nrwl/jest', nxVersion);
const existingJestConfigPath = normalizePath(
'tools/eslint-rules/jest.config.js'
);

View File

@ -4,6 +4,7 @@ import {
removeChange,
} from '@nrwl/workspace/src/utilities/ast-utils';
import type { NormalizedOptions } from '../schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -12,7 +13,7 @@ export function addExportsToBarrelFile(
options: NormalizedOptions
): void {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const indexPath = `${options.projectRoot}/src/index.ts`;
const indexContent = tree.read(indexPath, 'utf-8');

View File

@ -37,18 +37,21 @@ function updateDependencies(host: Tree) {
}
export async function nextInitGenerator(host: Tree, schema: InitSchema) {
await jsInitGenerator(host, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(host, {
...schema,
skipFormat: true,
})
);
if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') {
const jestTask = await jestInitGenerator(host, schema);
tasks.push(jestTask);
}
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
const cypressTask = cypressInitGenerator(host, {});
const cypressTask = await cypressInitGenerator(host, {});
tasks.push(cypressTask);
}

View File

@ -37,14 +37,15 @@ function normalizeOptions(schema: Schema) {
export async function initGenerator(tree: Tree, schema: Schema) {
const options = normalizeOptions(schema);
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(tree, {
...schema,
tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
})
);
if (options.unitTestRunner === 'jest') {
tasks.push(await jestInitGenerator(tree, schema));
}

View File

@ -43,12 +43,15 @@ import { addGitIgnoreEntry } from './lib/add-git-ignore-entry';
export async function reactNativeInitGenerator(host: Tree, schema: Schema) {
addGitIgnoreEntry(host);
addBabelInputs(host);
await jsInitGenerator(host, {
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(host, {
...schema,
skipFormat: true,
})
);
if (!schema.skipPackageJson) {
const installTask = updateDependencies(host);

View File

@ -20,7 +20,7 @@ export async function createAllStories(
projectName: string,
ignorePaths?: string[]
) {
ensurePackage(tree, '@nrwl/storybook', nxVersion);
ensurePackage('@nrwl/storybook', nxVersion);
const { isTheFileAStory } = await import(
'@nrwl/storybook/src/utils/utilities'
);

View File

@ -27,8 +27,10 @@ export async function storybookConfigurationGenerator(
host: Tree,
schema: StorybookConfigureSchema
): Promise<GeneratorCallback> {
ensurePackage(host, '@nrwl/storybook', nxVersion);
const { configurationGenerator } = await import('@nrwl/storybook');
const { configurationGenerator } = ensurePackage(
'@nrwl/storybook',
nxVersion
);
const installTask = await configurationGenerator(host, {
name: schema.name,

View File

@ -105,8 +105,10 @@ export async function applicationGenerator(
addProject(host, options);
if (options.bundler === 'vite') {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { viteConfigurationGenerator } = await import('@nrwl/vite');
const { viteConfigurationGenerator } = ensurePackage(
'@nrwl/vite',
nxVersion
);
// We recommend users use `import.meta.env.MODE` and other variables in their code to differentiate between production and development.
// See: https://vitejs.dev/guide/env-and-mode.html
if (
@ -126,9 +128,7 @@ export async function applicationGenerator(
});
tasks.push(viteTask);
} else if (options.bundler === 'webpack') {
ensurePackage(host, '@nrwl/webpack', nxVersion);
const { webpackInitGenerator } = await import('@nrwl/webpack');
const { webpackInitGenerator } = ensurePackage('@nrwl/webpack', nxVersion);
const webpackInitTask = await webpackInitGenerator(host, {
uiFramework: 'react',
});
@ -136,8 +136,7 @@ export async function applicationGenerator(
}
if (options.bundler !== 'vite' && options.unitTestRunner === 'vitest') {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { vitestGenerator } = await import('@nrwl/vite');
const { vitestGenerator } = ensurePackage('@nrwl/vite', nxVersion);
const vitestTask = await vitestGenerator(host, {
uiFramework: 'react',

View File

@ -7,8 +7,7 @@ export async function addCypress(host: Tree, options: NormalizedSchema) {
return () => {};
}
ensurePackage(host, '@nrwl/cypress', nxVersion);
const { cypressProjectGenerator } = await import('@nrwl/cypress');
const { cypressProjectGenerator } = ensurePackage('@nrwl/cypress', nxVersion);
return await cypressProjectGenerator(host, {
...options,

View File

@ -6,8 +6,7 @@ export async function addJest(
host: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
ensurePackage(host, '@nrwl/jest', nxVersion);
const { jestProjectGenerator } = await import('@nrwl/jest');
const { jestProjectGenerator } = ensurePackage('@nrwl/jest', nxVersion);
if (options.unitTestRunner !== 'jest') {
return () => {};

View File

@ -10,6 +10,7 @@ import {
applyChangesToString,
addDependenciesToPackageJson,
} from '@nrwl/devkit';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -19,7 +20,7 @@ export function addRouting(host: Tree, options: NormalizedSchema) {
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const appPath = joinPathFragments(
options.appProjectRoot,

View File

@ -12,6 +12,7 @@ import {
getComponentNode,
getComponentPropsInterface,
} from '../../utils/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -32,7 +33,7 @@ export function componentCypressGenerator(
// TODO: candidate to refactor with the angular component story
export function getArgsDefaultValue(property: ts.SyntaxKind): string {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const typeNameToDefault: Record<number, any> = {
[tsModule.SyntaxKind.StringKeyword]: '',
@ -55,7 +56,7 @@ export function createComponentSpecFile(
{ project, componentPath, js, cypressProject }: CreateComponentSpecFileSchema
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const e2eProjectName = cypressProject || `${project}-e2e`;
const projects = getProjects(tree);

View File

@ -13,6 +13,7 @@ import {
getComponentNode,
} from '../../utils/ast-utils';
import { getDefaultsForComponent } from '../../utils/component-props';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -26,7 +27,7 @@ export function createComponentStoriesFile(
{ project, componentPath }: CreateComponentStoriesFileSchema
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const proj = getProjects(host).get(project);
const sourceRoot = proj.sourceRoot;

View File

@ -13,6 +13,7 @@ import {
import { getDefaultsForComponent } from '../../utils/component-props';
import { nxVersion } from '../../utils/versions';
import { ComponentTestSchema } from './schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -20,7 +21,7 @@ export async function componentTestGenerator(
tree: Tree,
options: ComponentTestSchema
) {
ensurePackage(tree, '@nrwl/cypress', nxVersion);
ensurePackage('@nrwl/cypress', nxVersion);
const { assertMinimumCypressVersion } = await import(
'@nrwl/cypress/src/utils/cypress-version'
);
@ -46,7 +47,7 @@ export async function componentTestGenerator(
function generateSpecsForComponents(tree: Tree, filePath: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const sourceFile = tsModule.createSourceFile(

View File

@ -24,6 +24,7 @@ import {
import { getComponentTests } from './get-component-tests';
import { NormalizedSchema } from './noramlized-schema';
import { Schema } from './schema';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export async function componentGenerator(host: Tree, schema: Schema) {
const options = await normalizeOptions(host, schema);
@ -105,7 +106,7 @@ function createComponentFiles(host: Tree, options: NormalizedSchema) {
let tsModule: typeof import('typescript');
function addExportsToBarrel(host: Tree, options: NormalizedSchema) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const workspace = getProjects(host);
const isApp = workspace.get(options.project).projectType === 'application';

View File

@ -18,8 +18,7 @@ export async function cypressComponentConfigGenerator(
tree: Tree,
options: CypressComponentConfigurationSchema
) {
ensurePackage(tree, '@nrwl/cypress', nxVersion);
const { cypressComponentProject } = await import('@nrwl/cypress');
const { cypressComponentProject } = ensurePackage('@nrwl/cypress', nxVersion);
const projectConfig = readProjectConfiguration(tree, options.project);
const installTask = await cypressComponentProject(tree, {
project: options.project,

View File

@ -1,4 +1,5 @@
import {
addDependenciesToPackageJson,
createProjectGraphAsync,
ensurePackage,
generateFiles,
@ -15,6 +16,7 @@ import { getComponentNode } from '../../../utils/ast-utils';
import { componentTestGenerator } from '../../component-test/component-test';
import { CypressComponentConfigurationSchema } from '../schema';
import { FoundTarget } from './update-configs';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -58,7 +60,7 @@ export async function addFiles(
options.bundler === 'webpack' ||
(!options.bundler && actualBundler === 'webpack')
) {
ensurePackage(tree, '@nrwl/webpack', nxVersion);
addDependenciesToPackageJson(tree, {}, { '@nrwl/webpack': nxVersion });
}
if (options.generateTests) {
@ -98,7 +100,7 @@ async function getBundler(
function isComponent(tree: Tree, filePath: string): boolean {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
if (isSpecFile.test(filePath) || !allowedFileExt.test(filePath)) {

View File

@ -14,6 +14,7 @@ import {
import { Schema } from './schema';
import { addImport } from '../../utils/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
interface NormalizedSchema extends Schema {
projectSourceRoot: string;
@ -62,7 +63,7 @@ function createFiles(host: Tree, options: NormalizedSchema) {
let tsModule: typeof import('typescript');
function addExportsToBarrel(host: Tree, options: NormalizedSchema) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const workspace = getProjects(host);
const isApp = workspace.get(options.project).projectType === 'application';

View File

@ -86,22 +86,24 @@ function initRootBabelConfig(tree: Tree, schema: InitSchema) {
}
export async function reactInitGenerator(host: Tree, schema: InitSchema) {
await jsInitGenerator(host, {
const tasks: GeneratorCallback[] = [];
const jsInitTask = await jsInitGenerator(host, {
...schema,
tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
js: schema.js,
skipFormat: true,
});
const tasks: GeneratorCallback[] = [];
tasks.push(jsInitTask);
setDefault(host);
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
ensurePackage(host, '@nrwl/cypress', nxVersion);
ensurePackage('@nrwl/cypress', nxVersion);
const { cypressInitGenerator } = await import(
'@nrwl/cypress/src/generators/init/init'
);
const cypressTask = cypressInitGenerator(host, {});
const cypressTask = await cypressInitGenerator(host, {});
tasks.push(cypressTask);
}

View File

@ -20,8 +20,7 @@ export async function addRollupBuildTarget(
host: Tree,
options: NormalizedSchema
) {
ensurePackage(host, '@nrwl/rollup', nxVersion);
const { rollupInitGenerator } = await import('@nrwl/rollup');
const { rollupInitGenerator } = ensurePackage('@nrwl/rollup', nxVersion);
// These are used in `@nrwl/react/plugins/bundle-rollup`
addDependenciesToPackageJson(

View File

@ -20,6 +20,7 @@ import {
reactRouterDomVersion,
typesReactRouterDomVersion,
} from '../../../utils/versions';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -100,7 +101,7 @@ function readComponent(
throw new Error(`Cannot find ${path}`);
}
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const content = host.read(path, 'utf-8');

View File

@ -62,8 +62,10 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
// Set up build target
if (options.buildable && options.bundler === 'vite') {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { viteConfigurationGenerator } = await import('@nrwl/vite');
const { viteConfigurationGenerator } = ensurePackage(
'@nrwl/vite',
nxVersion
);
const viteTask = await viteConfigurationGenerator(host, {
uiFramework: 'react',
project: options.name,
@ -80,8 +82,7 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
// Set up test target
if (options.unitTestRunner === 'jest') {
ensurePackage(host, '@nrwl/jest', nxVersion);
const { jestProjectGenerator } = await import('@nrwl/jest');
const { jestProjectGenerator } = ensurePackage('@nrwl/jest', nxVersion);
const jestTask = await jestProjectGenerator(host, {
...options,
@ -106,8 +107,7 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
options.unitTestRunner === 'vitest' &&
options.bundler !== 'vite' // tests are already configured if bundler is vite
) {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { vitestGenerator } = await import('@nrwl/vite');
const { vitestGenerator } = ensurePackage('@nrwl/vite', nxVersion);
const vitestTask = await vitestGenerator(host, {
uiFramework: 'react',
project: options.name,

View File

@ -20,6 +20,7 @@ import {
Tree,
} from '@nrwl/devkit';
import { getRootTsConfigPathInTree } from '@nrwl/js';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -65,7 +66,7 @@ function addReduxPackageDependencies(host: Tree) {
function addExportsToBarrel(host: Tree, options: NormalizedSchema) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const indexFilePath = path.join(
@ -116,7 +117,7 @@ function addStoreConfiguration(host: Tree, options: NormalizedSchema) {
function updateReducerConfiguration(host: Tree, options: NormalizedSchema) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
if (!options.appProjectSourcePath) {

View File

@ -11,6 +11,7 @@ import {
addRemoteRoute,
addRemoteToConfig,
} from '../../../module-federation/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -20,7 +21,7 @@ export function updateHostWithRemote(
remoteName: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const hostConfig = readProjectConfiguration(host, hostName);

View File

@ -23,6 +23,7 @@ import {
typesExpressVersion,
} from '../../utils/versions';
import { addStaticRouter } from '../../utils/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -31,7 +32,7 @@ function readEntryFile(
path: string
): { content: string; source: ts.SourceFile } {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const content = host.read(path, 'utf-8');

View File

@ -15,6 +15,7 @@ import {
} from '@nrwl/devkit';
import { basename, join } from 'path';
import minimatch = require('minimatch');
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -57,7 +58,7 @@ export function containsComponentDeclaration(
componentPath: string
): boolean {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const contents = tree.read(componentPath, 'utf-8');

View File

@ -10,7 +10,7 @@ import {
import { nxVersion } from '../../utils/versions';
async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
ensurePackage(host, '@nrwl/cypress', nxVersion);
ensurePackage('@nrwl/cypress', nxVersion);
const { getE2eProjectName } = await import(
'@nrwl/cypress/src/utils/project-name'
);
@ -34,8 +34,10 @@ export async function storybookConfigurationGenerator(
host: Tree,
schema: StorybookConfigureSchema
) {
ensurePackage(host, '@nrwl/storybook', nxVersion);
const { configurationGenerator } = await import('@nrwl/storybook');
const { configurationGenerator } = ensurePackage(
'@nrwl/storybook',
nxVersion
);
let bundler = schema.bundler ?? 'webpack';
const projectConfig = readProjectConfiguration(host, schema.name);

View File

@ -6,6 +6,7 @@ import {
findElements,
addImport,
} from '../utils/ast-utils';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -14,7 +15,7 @@ export function addRemoteToConfig(
app: string
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const assignments = findNodes(

View File

@ -6,6 +6,7 @@ import {
StringChange,
StringInsertion,
} from '@nrwl/devkit';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
@ -14,7 +15,7 @@ export function addImport(
statement: string
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(source, tsModule.SyntaxKind.ImportDeclaration);
@ -42,7 +43,7 @@ export function findMainRenderStatement(
source: ts.SourceFile
): ts.CallExpression | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
// 1. Try to find ReactDOM.render.
@ -117,7 +118,7 @@ export function findDefaultExportDeclaration(
| ts.ClassDeclaration
| null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const identifier = findDefaultExportIdentifier(source);
if (identifier) {
@ -147,7 +148,7 @@ export function findExportDeclarationsForJsx(
ts.VariableDeclaration | ts.FunctionDeclaration | ts.ClassDeclaration
> | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const variables = findNodes(source, tsModule.SyntaxKind.VariableDeclaration);
const variableStatements = findNodes(
@ -231,7 +232,7 @@ export function findDefaultExportIdentifier(
source: ts.SourceFile
): ts.Identifier | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const exports = findNodes(
source,
@ -248,7 +249,7 @@ export function findDefaultClassOrFunction(
source: ts.SourceFile | null
): ts.FunctionDeclaration | ts.ClassDeclaration | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const fns = findNodes(
source,
@ -270,7 +271,7 @@ function hasDefaultExportModifier(
x: ts.ClassDeclaration | ts.FunctionDeclaration
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
return (
x.modifiers &&
@ -284,7 +285,7 @@ export function findComponentImportPath(
source: ts.SourceFile
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(
source,
@ -308,7 +309,7 @@ export function findComponentImportPath(
export function findElements(source: ts.SourceFile, tagName: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const nodes = findNodes(source, [
tsModule.SyntaxKind.JsxSelfClosingElement,
@ -331,7 +332,7 @@ export function findClosestOpening(tagName: string, node: ts.Node) {
export function isTag(tagName: string, node: ts.Node) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
if (tsModule.isJsxOpeningLikeElement(node)) {
return (
@ -355,7 +356,7 @@ export function addInitialRoutes(
source: ts.SourceFile
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const jsxClosingElements = findNodes(source, [
@ -581,7 +582,7 @@ export function updateReduxStore(
}
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const calls = findNodes(
source,
@ -651,7 +652,7 @@ export function updateReduxStore(
export function getComponentNode(sourceFile: ts.SourceFile): ts.Node | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const defaultExport = findDefaultExport(sourceFile);
@ -674,7 +675,7 @@ export function getComponentPropsInterface(
cmpDeclaration: ts.Node
): ts.InterfaceDeclaration | null {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
let propsTypeName: string = null;

View File

@ -1,3 +1,4 @@
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
import type * as ts from 'typescript';
import { getComponentPropsInterface } from './ast-utils';
@ -6,7 +7,7 @@ let tsModule: typeof import('typescript');
// TODO: candidate to refactor with the angular component story
export function getArgsDefaultValue(property: ts.SyntaxKind): string {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const typeNameToDefault: Record<number, any> = {
[tsModule.SyntaxKind.StringKeyword]: "''",
@ -27,7 +28,7 @@ export function getDefaultsForComponent(
cmpDeclaration: ts.Node
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const propsInterface = getComponentPropsInterface(sourceFile, cmpDeclaration);

View File

@ -44,7 +44,7 @@ export async function cypressProjectGenerator(
const tasks: GeneratorCallback[] = [];
if (!projectAlreadyHasCypress(tree)) {
tasks.push(_cypressInitGenerator(tree, {}));
tasks.push(await _cypressInitGenerator(tree, {}));
}
const installTask = await _cypressProjectGenerator(tree, {

View File

@ -7,6 +7,7 @@ Object {
},
"devDependencies": Object {
"@angular/forms": "*",
"@nrwl/js": "0.0.1",
"@nrwl/storybook": "^6.5.15",
"@storybook/addon-essentials": "^6.5.15",
"@storybook/angular": "^6.5.15",
@ -15,6 +16,7 @@ Object {
"@storybook/manager-webpack5": "^6.5.15",
"existing": "1.0.0",
"html-webpack-plugin": "^5.5.0",
"typescript": "~4.9.5",
"webpack": "^5.64.0",
},
"name": "test-name",

View File

@ -1,6 +1,7 @@
import {
addDependenciesToPackageJson,
convertNxGenerator,
GeneratorCallback,
readJson,
readNxJson,
Tree,
@ -26,6 +27,7 @@ import {
webpack5Version,
} from '../../utils/versions';
import { Schema } from './schema';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
function checkDependenciesInstalled(host: Tree, schema: Schema) {
const packageJson = readJson(host, 'package.json');
@ -212,15 +214,18 @@ function editRootTsConfig(tree: Tree) {
}
export async function initGenerator(tree: Tree, schema: Schema) {
const tasks: GeneratorCallback[] = [];
tasks.push(
await jsInitGenerator(tree, {
js: schema.js,
...schema,
skipFormat: true,
});
const installTask = checkDependenciesInstalled(tree, schema);
})
);
tasks.push(checkDependenciesInstalled(tree, schema));
moveToDevDependencies(tree);
editRootTsConfig(tree);
addCacheableOperation(tree);
return installTask;
return runTasksInSerial(...tasks);
}
export default initGenerator;

View File

@ -74,8 +74,10 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) {
];
if (options.bundler === 'webpack') {
ensurePackage(tree, '@nrwl/webpack', nxVersion);
const { webpackProjectGenerator } = require('@nrwl/webpack');
const { webpackProjectGenerator } = ensurePackage(
'@nrwl/webpack',
nxVersion
);
await webpackProjectGenerator(tree, {
project: options.projectName,
main,
@ -200,8 +202,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
await addProject(host, options);
if (options.bundler === 'vite') {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { viteConfigurationGenerator } = require('@nrwl/vite');
const { viteConfigurationGenerator } = ensurePackage(
'@nrwl/vite',
nxVersion
);
// We recommend users use `import.meta.env.MODE` and other variables in their code to differentiate between production and development.
// See: https://vitejs.dev/guide/env-and-mode.html
if (
@ -223,8 +227,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
}
if (options.bundler !== 'vite' && options.unitTestRunner === 'vitest') {
ensurePackage(host, '@nrwl/vite', nxVersion);
const { vitestGenerator } = require('@nrwl/vite');
const { vitestGenerator } = ensurePackage('@nrwl/vite', nxVersion);
const vitestTask = await vitestGenerator(host, {
uiFramework: 'none',
project: options.projectName,

View File

@ -50,7 +50,7 @@ export async function webInitGenerator(tree: Tree, schema: Schema) {
tasks.push(jestTask);
}
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
const cypressTask = cypressInitGenerator(tree, {
const cypressTask = await cypressInitGenerator(tree, {
skipPackageJson: schema.skipPackageJson,
});
tasks.push(cypressTask);

View File

@ -70,8 +70,7 @@ export async function addLint(
tree: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
ensurePackage(tree, '@nrwl/linter', nxVersion);
const { lintProjectGenerator } = require('@nrwl/linter');
const { lintProjectGenerator } = ensurePackage('@nrwl/linter', nxVersion);
return lintProjectGenerator(tree, {
project: options.name,
linter: options.linter,
@ -172,8 +171,7 @@ async function addJest(
tree: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
ensurePackage(tree, '@nrwl/jest', nxVersion);
const { jestProjectGenerator } = require('@nrwl/jest');
const { jestProjectGenerator } = ensurePackage('@nrwl/jest', nxVersion);
return await jestProjectGenerator(tree, {
...options,
project: options.name,
@ -187,14 +185,14 @@ async function addJest(
}
function addTypescript(tree: Tree, options: NormalizedSchema) {
if (!options.js) {
ensurePackage(tree, 'typescript', typescriptVersion);
}
// add tsconfig.base.json
if (!options.skipTsConfig && !getRootTsConfigFileName()) {
generateFiles(tree, joinPathFragments(__dirname, './files/root'), '.', {});
}
return !options.js
? addDependenciesToPackageJson(tree, {}, { typescript: typescriptVersion })
: () => {};
}
export async function libraryGenerator(tree: Tree, schema: Schema) {

View File

@ -18,6 +18,7 @@ import { findNodes } from 'nx/src/utils/typescript';
import { NormalizedSchema } from '../schema';
import { normalizeSlashes } from './utils';
import { relative } from 'path';
import { ensureTypescript } from '../../../utilities/typescript';
let tsModule: typeof import('typescript');
@ -121,7 +122,7 @@ export function updateImports(
*/
function updateImportPaths(tree: Tree, path: string, from: string, to: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const contents = tree.read(path, 'utf-8');
const sourceFile = tsModule.createSourceFile(
@ -149,7 +150,7 @@ function updateImportDeclarations(
to: string
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const importDecls = findNodes(
sourceFile,
@ -176,7 +177,7 @@ function updateDynamicImports(
to: string
): StringChange[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const expressions = findNodes(
sourceFile,

View File

@ -13,6 +13,7 @@ import type {
StringLiteral,
} from 'typescript';
import { join } from 'path';
import { ensureTypescript } from '../../../utilities/typescript';
let tsModule: typeof import('typescript');
@ -29,7 +30,7 @@ export function updateJestConfig(
projectConfig: ProjectConfiguration
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const {
createSourceFile,

View File

@ -1,6 +1,7 @@
import { insertStatement } from './insert-statement';
import { applyChangesToString, ChangeType, Tree } from '@nrwl/devkit';
import type { NamedImports } from 'typescript';
import { ensureTypescript } from '../../utilities/typescript';
let tsModule: typeof import('typescript');
@ -11,7 +12,7 @@ export function insertImport(
modulePath: string
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const {
createSourceFile,

View File

@ -1,4 +1,5 @@
import { applyChangesToString, ChangeType, Tree } from '@nrwl/devkit';
import { ensureTypescript } from '../../utilities/typescript';
let tsModule: typeof import('typescript');
@ -7,7 +8,7 @@ let tsModule: typeof import('typescript');
*/
export function insertStatement(tree: Tree, path: string, statement: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const { createSourceFile, isImportDeclaration, ScriptTarget } = tsModule;

View File

@ -1,6 +1,6 @@
import type { Tree } from '@nrwl/devkit';
import type * as ts from 'typescript';
import { getSourceNodes } from './typescript';
import { ensureTypescript, getSourceNodes } from './typescript';
import { findNodes } from 'nx/src/utils/typescript';
let tsModule: typeof import('typescript');
@ -89,7 +89,7 @@ export function insertImport(
isDefault = false
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const rootNode = source;
const allImports = findNodes(rootNode, tsModule.SyntaxKind.ImportDeclaration);
@ -216,7 +216,7 @@ export function addGlobal(
statement: string
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(source, tsModule.SyntaxKind.ImportDeclaration);
if (allImports.length > 0) {
@ -238,7 +238,7 @@ export function getImport(
predicate: (a: any) => boolean
): { moduleSpec: string; bindings: string[] }[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(source, tsModule.SyntaxKind.ImportDeclaration);
const matching = allImports.filter((i: ts.ImportDeclaration) =>
@ -283,7 +283,7 @@ export function addParameterToConstructor(
opts: { className: string; param: string }
): ts.SourceFile {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const clazz = findClass(source, opts.className);
const constructor = clazz.members.filter(
@ -326,7 +326,7 @@ export function findClass(
silent: boolean = false
): ts.ClassDeclaration {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const nodes = getSourceNodes(source);

View File

@ -12,6 +12,7 @@ import type * as ts from 'typescript';
import { unlinkSync } from 'fs';
import { output } from './output';
import { isNpmProject } from 'nx/src/project-graph/operators';
import { ensureTypescript } from './typescript';
let tsModule: typeof import('typescript');
@ -194,7 +195,7 @@ export function computeCompilerOptionsPaths(
function readPaths(tsConfig: string | ts.ParsedCommandLine) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
try {
let config: ts.ParsedCommandLine;

View File

@ -1,12 +1,13 @@
import { offsetFromRoot, Tree, workspaceRoot } from '@nrwl/devkit';
import { existsSync } from 'fs';
import { dirname, join } from 'path';
import { ensureTypescript } from './typescript';
let tsModule: typeof import('typescript');
export function readTsConfig(tsConfigPath: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const readResult = tsModule.readConfigFile(
tsConfigPath,

View File

@ -1,6 +1,7 @@
import { workspaceRoot } from '@nrwl/devkit';
import { ensurePackage, workspaceRoot } from '@nrwl/devkit';
import { dirname } from 'path';
import type * as ts from 'typescript';
import { typescriptVersion } from '../utils/versions';
export { compileTypeScript } from './typescript/compilation';
export type { TypeScriptCompilationOptions } from './typescript/compilation';
export { findNodes } from './typescript/find-nodes'; // TODO(v16): remove this
@ -12,7 +13,7 @@ let tsModule: typeof import('typescript');
function readTsConfigOptions(tsConfigPath: string) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const readResult = tsModule.readConfigFile(
@ -79,3 +80,10 @@ function getCompilerHost(tsConfigPath: string) {
);
return { options, host, moduleResolutionCache };
}
export function ensureTypescript() {
return ensurePackage<typeof import('typescript')>(
'typescript',
typescriptVersion
);
}

View File

@ -3,6 +3,7 @@ import { rmSync } from 'fs';
import type * as ts from 'typescript';
import type { CustomTransformers, Diagnostic, Program } from 'typescript';
import { readTsConfig } from '../ts-config';
import { ensureTypescript } from '../typescript';
let tsModule: typeof import('typescript');
@ -47,7 +48,7 @@ export function compileTypeScriptWatcher(
) => void | Promise<void>
) {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const normalizedOptions = normalizeOptions(options);
const tsConfig = getNormalizedTsConfig(normalizedOptions);
@ -159,7 +160,7 @@ function createProgram(
{ projectName, getCustomTransformers }: TypeScriptCompilationOptions
): { success: boolean } {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const host = tsModule.createCompilerHost(tsconfig.options);
const program = tsModule.createProgram({

View File

@ -20,6 +20,7 @@ import type { NxJsonConfiguration } from '@nrwl/devkit';
import { addInstallTask } from './rules/add-install-task';
import { findNodes } from 'nx/src/utils/typescript';
import { getSourceNodes } from '../utilities/typescript/get-source-nodes';
import { ensureTypescript } from '../utilities/typescript';
let tsModule: typeof import('typescript');
@ -174,7 +175,7 @@ export function addParameterToConstructor(
opts: { className: string; param: string }
): Change[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const clazz = findClass(source, opts.className);
const constructor = clazz.members.filter(
@ -217,7 +218,7 @@ export function findClass(
silent: boolean = false
): ts.ClassDeclaration {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const nodes = getSourceNodes(source);
@ -275,7 +276,7 @@ export function getImport(
predicate: (a: any) => boolean
): { moduleSpec: string; bindings: string[] }[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(source, tsModule.SyntaxKind.ImportDeclaration);
const matching = allImports.filter((i: ts.ImportDeclaration) =>
@ -302,7 +303,7 @@ export function addGlobal(
statement: string
): Change[] {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const allImports = findNodes(source, tsModule.SyntaxKind.ImportDeclaration);
if (allImports.length > 0) {
@ -581,7 +582,7 @@ export function insertImport(
isDefault = false
): Change {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const rootNode = source;
const allImports = findNodes(rootNode, tsModule.SyntaxKind.ImportDeclaration);

View File

@ -8,12 +8,13 @@ import {
} from '@angular-devkit/schematics';
import { normalize } from '@angular-devkit/core';
import { updateJsonInTree } from '../ast-utils';
import { ensureTypescript } from '../../utilities/typescript';
let tsModule: typeof import('typescript');
export function toJS(): Rule {
if (!tsModule) {
tsModule = require('typescript');
tsModule = ensureTypescript();
}
const { transpile, JsxEmit, ScriptTarget } = tsModule;