feat(core): generate inputs configuration for new workspaces (#11856)

This commit is contained in:
Jason Jean 2022-09-07 19:32:59 -07:00 committed by GitHub
parent c334277a93
commit 292f0c14b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 565 additions and 169 deletions

View File

@ -59,14 +59,14 @@ We can define a more precise configuration as follows:
{ {
"namedInputs": { "namedInputs": {
"default": ["{projectRoot}/**/*"], "default": ["{projectRoot}/**/*"],
"prod": ["!{projectRoot}/**/*.spec.tsx"] "production": ["!{projectRoot}/**/*.spec.tsx"]
}, },
"targetDefaults": { "targetDefaults": {
"build": { "build": {
"inputs": ["prod", "^prod"] "inputs": ["production", "^production"]
}, },
"test": { "test": {
"inputs": ["default", "^prod", "{workspaceRoot}/jest.config.ts"] "inputs": ["default", "^production", "{workspaceRoot}/jest.config.ts"]
} }
} }
} }

View File

@ -25,11 +25,11 @@ The following is an expanded version showing all options. Your `nx.json` will li
}, },
"namedInputs": { "namedInputs": {
"default": ["{projectRoot}/**/*"], "default": ["{projectRoot}/**/*"],
"prod": ["!{projectRoot}/**/*.spec.tsx"] "production": ["!{projectRoot}/**/*.spec.tsx"]
}, },
"targetDefaults": { "targetDefaults": {
"build": { "build": {
"inputs": ["prod", "^prod"], "inputs": ["production", "^production"],
"dependsOn": ["^build"] "dependsOn": ["^build"]
} }
}, },
@ -127,7 +127,7 @@ like this (which applies to every project):
"test": { "test": {
"inputs": [ "inputs": [
"default", "default",
"^prod" "^production"
] ]
} }
``` ```
@ -137,7 +137,10 @@ And projects can define their prod fileset, without having to redefine the input
```json title="project.json" ```json title="project.json"
{ {
"namedInputs": { "namedInputs": {
"prod": ["!{projectRoot}/**/*.test.js", "{workspacRoot}/jest.config.js"] "production": [
"!{projectRoot}/**/*.test.js",
"{workspacRoot}/jest.config.js"
]
} }
} }
``` ```

View File

@ -64,18 +64,18 @@ You can add Nx-specific configuration as follows:
"default": [ "default": [
"{projectRoot}/**/*" "{projectRoot}/**/*"
], ],
"prod": [ "production": [
"!{projectRoot}/**/*.spec.tsx" "!{projectRoot}/**/*.spec.tsx"
] ]
}, },
"targets": { "targets": {
"build": { "build": {
"inputs": ["prod", "^prod"], "inputs": ["production", "^production"],
"outputs": ["dist/libs/mylib"], "outputs": ["dist/libs/mylib"],
"dependsOn": ["^build"] "dependsOn": ["^build"]
}, },
"test": { "test": {
"inputs": ["default", "^prod"], "inputs": ["default", "^production"],
"outputs": [], "outputs": [],
"dependsOn": ["build"] "dependsOn": ["build"]
} }
@ -94,19 +94,19 @@ You can add Nx-specific configuration as follows:
"projectType": "library", "projectType": "library",
"namedInputs": { "namedInputs": {
"default": ["{projectRoot}/**/*"], "default": ["{projectRoot}/**/*"],
"prod": ["!{projectRoot}/**/*.spec.tsx"] "production": ["!{projectRoot}/**/*.spec.tsx"]
}, },
"targets": { "targets": {
"test": { "test": {
"executor": "@nrwl/jest:jest", "executor": "@nrwl/jest:jest",
"inputs": ["default", "^prod"], "inputs": ["default", "^production"],
"outputs": [], "outputs": [],
"dependsOn": ["build"], "dependsOn": ["build"],
"options": {} "options": {}
}, },
"build": { "build": {
"executor": "@nrwl/js:tsc", "executor": "@nrwl/js:tsc",
"inputs": ["prod", "^prod"], "inputs": ["production", "^production"],
"outputs": ["dist/libs/mylib"], "outputs": ["dist/libs/mylib"],
"dependsOn": ["^build"], "dependsOn": ["^build"],
"options": {} "options": {}
@ -154,8 +154,8 @@ _Named Inputs_
Examples: Examples:
- `inputs: ["prod"]` - `inputs: ["production"]`
- same as `inputs: [{input: "prod", projects: "self"}]` - same as `inputs: [{input: "production", projects: "self"}]`
Often the same glob will appear in many places (e.g., prod fileset will exclude spec files for all projects). Because Often the same glob will appear in many places (e.g., prod fileset will exclude spec files for all projects). Because
keeping them in sync is error-prone, we recommend defining named inputs, which you can then reference in all of those keeping them in sync is error-prone, we recommend defining named inputs, which you can then reference in all of those
@ -165,15 +165,15 @@ places.
Examples: Examples:
- `inputs: ["^prod"]` - `inputs: ["^production"]`
- same as `inputs: [{input: "prod", projects: "dependencies"}]` - same as `inputs: [{input: "production", projects: "dependencies"}]`
Similar to `dependsOn`, the "^" symbols means "dependencies". This is a very important idea, so let's illustrate it with Similar to `dependsOn`, the "^" symbols means "dependencies". This is a very important idea, so let's illustrate it with
an example. an example.
``` ```
"test": { "test": {
"inputs": [ "default", "^prod" ] "inputs": [ "default", "^production" ]
} }
``` ```

13
nx.json
View File

@ -30,19 +30,22 @@
}, },
"namedInputs": { "namedInputs": {
"default": ["{projectRoot}/**/*"], "default": ["{projectRoot}/**/*"],
"prod": ["!{projectRoot}/**/*.spec.ts{,.snap}"] "production": [
"default",
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)"
]
}, },
"targetDefaults": { "targetDefaults": {
"build": { "build": {
"dependsOn": ["build-base"], "dependsOn": ["build-base"],
"inputs": ["prod", "^prod"] "inputs": ["production", "^production"]
}, },
"build-base": { "build-base": {
"dependsOn": ["^build-base"], "dependsOn": ["^build-base"],
"inputs": ["prod", "^prod"] "inputs": ["production", "^production"]
}, },
"test": { "test": {
"inputs": ["default", "^prod", "{workspaceRoot}/jest.config.ts"] "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"]
}, },
"lint": { "lint": {
"inputs": [ "inputs": [
@ -54,7 +57,7 @@
"e2e": { "e2e": {
"inputs": [ "inputs": [
"default", "default",
"^prod", "^production",
{ {
"env": "SELECTED_CLI" "env": "SELECTED_CLI"
}, },

View File

@ -1,12 +1,13 @@
import * as devkit from '@nrwl/devkit'; import * as devkit from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { karmaGenerator } from './karma'; import { karmaGenerator } from './karma';
import { NxJsonConfiguration, readJson, updateJson } from '@nrwl/devkit';
describe('karma', () => { describe('karma', () => {
let tree: devkit.Tree; let tree: devkit.Tree;
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
it('should do nothing when karma is already installed and karma.conf.js exists', () => { it('should do nothing when karma is already installed and karma.conf.js exists', () => {
@ -57,4 +58,27 @@ describe('karma', () => {
expect(tree.exists('karma.conf.js')).toBeTruthy(); expect(tree.exists('karma.conf.js')).toBeTruthy();
}); });
it('should add inputs for test targets', () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default', '^production'];
return json;
});
karmaGenerator(tree, {});
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
expect(nxJson.namedInputs.production).toContain(
'!{projectRoot}/karma.conf.js'
);
expect(nxJson.namedInputs.production).toContain(
'!{projectRoot}/tsconfig.spec.json'
);
expect(nxJson.namedInputs.production).toContain(
'!{projectRoot}/**/*.spec.[jt]s'
);
expect(nxJson.targetDefaults.test).toEqual({
inputs: ['default', '^production', '{workspaceRoot}/karma.conf.js'],
});
});
}); });

View File

@ -4,6 +4,8 @@ import {
generateFiles, generateFiles,
joinPathFragments, joinPathFragments,
readJson, readJson,
readWorkspaceConfiguration,
updateWorkspaceConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { import {
jasmineCoreVersion, jasmineCoreVersion,
@ -18,6 +20,39 @@ import {
} from '../../utils/versions'; } from '../../utils/versions';
import { GeneratorOptions } from './schema'; import { GeneratorOptions } from './schema';
function addTestInputs(tree: Tree) {
const workspaceConfiguration = readWorkspaceConfiguration(tree);
const productionFileSet = workspaceConfiguration.namedInputs?.production;
if (productionFileSet) {
productionFileSet.push(
// Exclude spec files from production fileset
'!{projectRoot}/**/*.spec.[jt]s',
// Remove tsconfig.spec.json
'!{projectRoot}/tsconfig.spec.json',
// Remove karma.conf.js
'!{projectRoot}/karma.conf.js'
);
// Dedupe and set
workspaceConfiguration.namedInputs.production = Array.from(
new Set(productionFileSet)
);
}
// Test targets depend on all their project's sources + production sources of dependencies
workspaceConfiguration.targetDefaults ??= {};
workspaceConfiguration.targetDefaults.test ??= {};
workspaceConfiguration.targetDefaults.test.inputs ??= [
'default',
productionFileSet ? '^production' : '^default',
];
workspaceConfiguration.targetDefaults.test.inputs.push(
'{workspaceRoot}/karma.conf.js'
);
updateWorkspaceConfiguration(tree, workspaceConfiguration);
}
export function karmaGenerator(tree: Tree, options: GeneratorOptions) { export function karmaGenerator(tree: Tree, options: GeneratorOptions) {
const packageJson = readJson(tree, 'package.json'); const packageJson = readJson(tree, 'package.json');
@ -25,11 +60,14 @@ export function karmaGenerator(tree: Tree, options: GeneratorOptions) {
generateFiles(tree, joinPathFragments(__dirname, 'files'), '.', { generateFiles(tree, joinPathFragments(__dirname, 'files'), '.', {
tmpl: '', tmpl: '',
}); });
addTestInputs(tree);
} }
if (options.skipPackageJson || packageJson.devDependencies['karma']) { if (options.skipPackageJson || packageJson.devDependencies['karma']) {
return () => {}; return () => {};
} }
return addDependenciesToPackageJson( return addDependenciesToPackageJson(
tree, tree,
{}, {},

View File

@ -176,6 +176,12 @@ Object {
"^build", "^build",
], ],
}, },
"lint": Object {
"inputs": Array [
"default",
"{workspaceRoot}/.eslintrc.json",
],
},
}, },
"tasksRunnerOptions": Object { "tasksRunnerOptions": Object {
"default": Object { "default": Object {

View File

@ -1,5 +1,5 @@
import { readJson, Tree, updateJson } from '@nrwl/devkit'; import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { cypressVersion } from '../../utils/versions'; import { cypressVersion } from '../../utils/versions';
import { cypressInitGenerator } from './init'; import { cypressInitGenerator } from './init';
@ -8,7 +8,7 @@ describe('init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
it('should add dependencies into `package.json` file', async () => { it('should add dependencies into `package.json` file', async () => {
@ -31,4 +31,20 @@ describe('init', () => {
expect(packageJson.dependencies['@nrwl/cypress']).toBeUndefined(); expect(packageJson.dependencies['@nrwl/cypress']).toBeUndefined();
expect(packageJson.dependencies[existing]).toBeDefined(); expect(packageJson.dependencies[existing]).toBeDefined();
}); });
it('should setup e2e target defaults', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default'];
return json;
});
cypressInitGenerator(tree, {});
expect(
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults.e2e
).toEqual({
inputs: ['default', '^production'],
});
});
}); });

View File

@ -1,8 +1,10 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
convertNxGenerator, convertNxGenerator,
readWorkspaceConfiguration,
removeDependenciesFromPackageJson, removeDependenciesFromPackageJson,
Tree, Tree,
updateWorkspaceConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { import {
cypressVersion, cypressVersion,
@ -11,11 +13,27 @@ import {
} from '../../utils/versions'; } from '../../utils/versions';
import { Schema } from './schema'; import { Schema } from './schema';
function updateDependencies(host: Tree) { function setupE2ETargetDefaults(tree: Tree) {
removeDependenciesFromPackageJson(host, ['@nrwl/cypress'], []); const workspaceConfiguration = readWorkspaceConfiguration(tree);
// E2e targets depend on all their project's sources + production sources of dependencies
workspaceConfiguration.targetDefaults ??= {};
const productionFileSet = !!workspaceConfiguration.namedInputs?.production;
workspaceConfiguration.targetDefaults.e2e ??= {};
workspaceConfiguration.targetDefaults.e2e.inputs ??= [
'default',
productionFileSet ? '^production' : '^default',
];
updateWorkspaceConfiguration(tree, workspaceConfiguration);
}
function updateDependencies(tree: Tree) {
removeDependenciesFromPackageJson(tree, ['@nrwl/cypress'], []);
return addDependenciesToPackageJson( return addDependenciesToPackageJson(
host, tree,
{}, {},
{ {
['@nrwl/cypress']: nxVersion, ['@nrwl/cypress']: nxVersion,
@ -25,8 +43,9 @@ function updateDependencies(host: Tree) {
); );
} }
export function cypressInitGenerator(host: Tree, options: Schema) { export function cypressInitGenerator(tree: Tree, options: Schema) {
return !options.skipPackageJson ? updateDependencies(host) : () => {}; setupE2ETargetDefaults(tree);
return !options.skipPackageJson ? updateDependencies(tree) : () => {};
} }
export default cypressInitGenerator; export default cypressInitGenerator;

View File

@ -1,5 +1,6 @@
export const nxPreset = { export const nxPreset = {
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], // This is one of the patterns that jest finds by default https://jestjs.io/docs/configuration#testmatch-arraystring
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
resolver: '@nrwl/jest/plugins/resolver', resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'mjs', 'html'], moduleFileExtensions: ['ts', 'js', 'mjs', 'html'],
coverageReporters: ['html'], coverageReporters: ['html'],

View File

@ -1,4 +1,11 @@
import { readJson, stripIndents, Tree, writeJson } from '@nrwl/devkit'; import {
NxJsonConfiguration,
readJson,
stripIndents,
Tree,
updateJson,
writeJson,
} from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
import { jestInitGenerator } from './init'; import { jestInitGenerator } from './init';
@ -39,6 +46,61 @@ describe('jest', () => {
expect(tree.read('jest.config.ts', 'utf-8')).toEqual('test'); expect(tree.read('jest.config.ts', 'utf-8')).toEqual('test');
}); });
it('should add target defaults for test', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default'];
return json;
});
jestInitGenerator(tree, {});
const productionFileSet = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.production;
const testDefaults = readJson<NxJsonConfiguration>(tree, 'nx.json')
.targetDefaults.test;
expect(productionFileSet).toContain(
'!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)'
);
expect(productionFileSet).toContain('!{projectRoot}/tsconfig.spec.json');
expect(productionFileSet).toContain('!{projectRoot}/jest.config.[jt]s');
expect(testDefaults).toEqual({
inputs: ['default', '^production', '{workspaceRoot}/jest.preset.js'],
});
});
it('should not alter target defaults if jest.preset.js already exists', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default', '^production'];
return json;
});
jestInitGenerator(tree, {});
let nxJson: NxJsonConfiguration;
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs.production = [
'default',
'^production',
'!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
'!{projectRoot}/**/*.md',
];
json.targetDefaults.test = {
inputs: [
'default',
'^production',
'{workspaceRoot}/jest.preset.js',
'{workspaceRoot}/testSetup.ts',
],
};
nxJson = json;
return json;
});
jestInitGenerator(tree, {});
expect(readJson<NxJsonConfiguration>(tree, 'nx.json')).toEqual(nxJson);
});
it('should add dependencies', async () => { it('should add dependencies', async () => {
jestInitGenerator(tree, {}); jestInitGenerator(tree, {});
const packageJson = readJson(tree, 'package.json'); const packageJson = readJson(tree, 'package.json');

View File

@ -2,10 +2,12 @@ import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
convertNxGenerator, convertNxGenerator,
GeneratorCallback, GeneratorCallback,
readWorkspaceConfiguration,
removeDependenciesFromPackageJson, removeDependenciesFromPackageJson,
stripIndents, stripIndents,
Tree, Tree,
updateJson, updateJson,
updateWorkspaceConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { import {
babelJestVersion, babelJestVersion,
@ -26,9 +28,9 @@ const schemaDefaults = {
js: false, js: false,
} as const; } as const;
function createJestConfig(host: Tree, js: boolean = false) { function createJestConfig(tree: Tree, js: boolean = false) {
// if the root ts config already exists then don't make a js one or vice versa // if the root ts config already exists then don't make a js one or vice versa
if (!host.exists('jest.config.ts') && !host.exists('jest.config.js')) { if (!tree.exists('jest.config.ts') && !tree.exists('jest.config.js')) {
const contents = js const contents = js
? stripIndents` ? stripIndents`
const { getJestProjects } = require('@nrwl/jest'); const { getJestProjects } = require('@nrwl/jest');
@ -42,21 +44,57 @@ function createJestConfig(host: Tree, js: boolean = false) {
export default { export default {
projects: getJestProjects() projects: getJestProjects()
};`; };`;
host.write(`jest.config.${js ? 'js' : 'ts'}`, contents); tree.write(`jest.config.${js ? 'js' : 'ts'}`, contents);
} }
if (!host.exists('jest.preset.js')) { if (!tree.exists('jest.preset.js')) {
// preset is always js file. // preset is always js file.
host.write( tree.write(
`jest.preset.js`, `jest.preset.js`,
` `
const nxPreset = require('@nrwl/jest/preset').default; const nxPreset = require('@nrwl/jest/preset').default;
module.exports = { ...nxPreset }` module.exports = { ...nxPreset }`
); );
addTestInputs(tree);
} }
} }
function addTestInputs(tree: Tree) {
const workspaceConfiguration = readWorkspaceConfiguration(tree);
const productionFileSet = workspaceConfiguration.namedInputs?.production;
if (productionFileSet) {
// This is one of the patterns in the default jest patterns
productionFileSet.push(
// Remove spec, test, and snapshots from the production fileset
'!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
// Remove tsconfig.spec.json
'!{projectRoot}/tsconfig.spec.json',
// Remove jest.config.js/ts
'!{projectRoot}/jest.config.[jt]s'
);
// Dedupe and set
workspaceConfiguration.namedInputs.production = Array.from(
new Set(productionFileSet)
);
}
// Test targets depend on all their project's sources + production sources of dependencies
workspaceConfiguration.targetDefaults ??= {};
workspaceConfiguration.targetDefaults.test ??= {};
workspaceConfiguration.targetDefaults.test.inputs ??= [
'default',
productionFileSet ? '^production' : '^default',
];
workspaceConfiguration.targetDefaults.test.inputs.push(
'{workspaceRoot}/jest.preset.js'
);
updateWorkspaceConfiguration(tree, workspaceConfiguration);
}
function updateDependencies(tree: Tree, options: NormalizedSchema) { function updateDependencies(tree: Tree, options: NormalizedSchema) {
const dependencies = { const dependencies = {
tslib: tslibVersion, tslib: tslibVersion,

View File

@ -1,13 +1,13 @@
import { Linter } from '../utils/linter'; import { Linter } from '../utils/linter';
import { Tree } from '@nrwl/devkit'; import { readJson, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { lintInitGenerator } from './init'; import { lintInitGenerator } from './init';
describe('@nrwl/linter:init', () => { describe('@nrwl/linter:init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
describe('--linter', () => { describe('--linter', () => {
@ -20,6 +20,16 @@ describe('@nrwl/linter:init', () => {
expect(tree.read('.eslintrc.json', 'utf-8')).toMatchSnapshot(); expect(tree.read('.eslintrc.json', 'utf-8')).toMatchSnapshot();
}); });
it('should add the root eslint config to the lint targetDefaults for lint', async () => {
await lintInitGenerator(tree, {
linter: Linter.EsLint,
});
expect(readJson(tree, 'nx.json').targetDefaults.lint).toEqual({
inputs: ['default', '{workspaceRoot}/.eslintrc.json'],
});
});
it('should not generate the global eslint config if it already exist', async () => { it('should not generate the global eslint config if it already exist', async () => {
tree.write('.eslintrc.js', '{}'); tree.write('.eslintrc.js', '{}');

View File

@ -1,8 +1,10 @@
import type { GeneratorCallback, Tree } from '@nrwl/devkit'; import type { GeneratorCallback, Tree } from '@nrwl/devkit';
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
readWorkspaceConfiguration,
removeDependenciesFromPackageJson, removeDependenciesFromPackageJson,
updateJson, updateJson,
updateWorkspaceConfiguration,
writeJson, writeJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { import {
@ -15,7 +17,7 @@ import {
} from '../../utils/versions'; } from '../../utils/versions';
import { Linter } from '../utils/linter'; import { Linter } from '../utils/linter';
import { containsEslint } from '../utils/eslint-file'; import { findEslintFile } from '../utils/eslint-file';
import { ESLint } from 'eslint'; import { ESLint } from 'eslint';
export interface LinterInitOptions { export interface LinterInitOptions {
@ -180,8 +182,31 @@ function initTsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
: () => {}; : () => {};
} }
function addTargetDefaults(tree: Tree) {
const workspaceConfiguration = readWorkspaceConfiguration(tree);
const productionFileSet = workspaceConfiguration.namedInputs?.production;
if (productionFileSet) {
// Remove .eslintrc.json
productionFileSet.push('!{projectRoot}/.eslintrc.json');
// Dedupe and set
workspaceConfiguration.namedInputs.production = Array.from(
new Set(productionFileSet)
);
}
workspaceConfiguration.targetDefaults ??= {};
workspaceConfiguration.targetDefaults.lint ??= {};
workspaceConfiguration.targetDefaults.lint.inputs ??= [
'default',
`{workspaceRoot}/.eslintrc.json`,
];
updateWorkspaceConfiguration(tree, workspaceConfiguration);
}
function initEsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback { function initEsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
if (containsEslint(tree)) { if (findEslintFile(tree)) {
return () => {}; return () => {};
} }
@ -194,6 +219,7 @@ function initEsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
'.eslintrc.json', '.eslintrc.json',
getGlobalEsLintConfiguration(options.unitTestRunner) getGlobalEsLintConfiguration(options.unitTestRunner)
); );
addTargetDefaults(tree);
if (tree.exists('.vscode/extensions.json')) { if (tree.exists('.vscode/extensions.json')) {
updateJson(tree, '.vscode/extensions.json', (json) => { updateJson(tree, '.vscode/extensions.json', (json) => {

View File

@ -42,8 +42,11 @@ function createEsLintConfiguration(
projectConfig: ProjectConfiguration, projectConfig: ProjectConfiguration,
setParserOptionsProject: boolean setParserOptionsProject: boolean
) { ) {
const eslintConfig = findEslintFile(tree);
writeJson(tree, join(projectConfig.root, `.eslintrc.json`), { writeJson(tree, join(projectConfig.root, `.eslintrc.json`), {
extends: [`${offsetFromRoot(projectConfig.root)}${findEslintFile(tree)}`], extends: eslintConfig
? [`${offsetFromRoot(projectConfig.root)}${eslintConfig}`]
: undefined,
// Include project files to be linted since the global one excludes all files. // Include project files to be linted since the global one excludes all files.
ignorePatterns: ['!**/*'], ignorePatterns: ['!**/*'],
overrides: [ overrides: [

View File

@ -1,4 +1,4 @@
import { containsEslint, findEslintFile } from './eslint-file'; import { findEslintFile } from './eslint-file';
import { Tree } from '@nrwl/devkit'; import { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
@ -10,30 +10,9 @@ describe('@nrwl/linter:eslint-file', () => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyV1Workspace();
}); });
describe('containsEslint', () => {
it('should return false when calling containsEslint without a eslint config', () => {
expect(containsEslint(tree)).toBe(false);
});
it('should return true when calling containsEslint with a .eslintrc.json config', () => {
tree.write('.eslintrc.json', '{}');
expect(containsEslint(tree)).toBe(true);
});
it('should return true when calling containsEslint with a .eslintrc.js config', () => {
tree.write('.eslintrc.js', '{}');
expect(containsEslint(tree)).toBe(true);
});
it('should return false when calling containsEslint witn an incorrect eslint file name', () => {
tree.write('.eslintrc.yaml', '{}');
expect(containsEslint(tree)).toBe(false);
});
});
describe('findEslintFile', () => { describe('findEslintFile', () => {
it('should return default name when calling findEslintFile when no eslint is found', () => { it('should return null when calling findEslintFile when no eslint is found', () => {
expect(findEslintFile(tree)).toBe('.eslintrc.json'); expect(findEslintFile(tree)).toBe(null);
}); });
it('should return the name of the eslint config when calling findEslintFile', () => { it('should return the name of the eslint config when calling findEslintFile', () => {
@ -49,7 +28,7 @@ describe('@nrwl/linter:eslint-file', () => {
it('should return default name when calling findEslintFile when no eslint is found', () => { it('should return default name when calling findEslintFile when no eslint is found', () => {
tree.write('.eslintrc.yaml', '{}'); tree.write('.eslintrc.yaml', '{}');
expect(findEslintFile(tree)).toBe('.eslintrc.json'); expect(findEslintFile(tree)).toBe(null);
}); });
}); });
}); });

View File

@ -2,21 +2,11 @@ import type { Tree } from '@nrwl/devkit';
const eslintFileList = ['.eslintrc.json', '.eslintrc.js']; const eslintFileList = ['.eslintrc.json', '.eslintrc.js'];
export function containsEslint(tree: Tree): boolean { export function findEslintFile(tree: Tree): string | null {
for (const file of eslintFileList) {
if (tree.exists(file)) {
return true;
}
}
return false;
}
export function findEslintFile(tree: Tree): string {
for (const file of eslintFileList) { for (const file of eslintFileList) {
if (tree.exists(file)) { if (tree.exists(file)) {
return file; return file;
} }
} }
// Default file return null;
return '.eslintrc.json';
} }

View File

@ -1,12 +1,12 @@
import { Tree } from '@nrwl/devkit'; import { NxJsonConfiguration, readJson, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { lintWorkspaceRuleGenerator } from './workspace-rule'; import { lintWorkspaceRuleGenerator } from './workspace-rule';
describe('@nrwl/linter:workspace-rule', () => { describe('@nrwl/linter:workspace-rule', () => {
let tree: Tree; let tree: Tree;
beforeEach(async () => { beforeEach(async () => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
it('should generate the required files', async () => { it('should generate the required files', async () => {

View File

@ -1,10 +1,12 @@
import { import {
addProjectConfiguration, addProjectConfiguration,
NxJsonConfiguration,
readJson, readJson,
readProjectConfiguration, readProjectConfiguration,
Tree, Tree,
updateJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { import {
lintWorkspaceRulesProjectGenerator, lintWorkspaceRulesProjectGenerator,
WORKSPACE_RULES_PROJECT_NAME, WORKSPACE_RULES_PROJECT_NAME,
@ -14,22 +16,23 @@ describe('@nrwl/linter:workspace-rules-project', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
it('should update implicitDependencies in nx.json', async () => { it('should add lint project files to lint inputs', async () => {
expect( updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
readJson(tree, 'nx.json').implicitDependencies json.targetDefaults = {
).toMatchInlineSnapshot(`undefined`); lint: {
inputs: ['default', '{workspaceRoot}/.eslintrc.json'],
},
};
return json;
});
await lintWorkspaceRulesProjectGenerator(tree); await lintWorkspaceRulesProjectGenerator(tree);
expect(readJson(tree, 'nx.json').implicitDependencies) expect(
.toMatchInlineSnapshot(` readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults.lint.inputs
Object { ).toContain('{workspaceRoot}/tools/eslint-rules/**/*');
"tools/eslint-rules/**/*": "*",
}
`);
}); });
it('should generate the required files', async () => { it('should generate the required files', async () => {
@ -72,6 +75,7 @@ describe('@nrwl/linter:workspace-rules-project', () => {
expect(readProjectConfiguration(tree, WORKSPACE_RULES_PROJECT_NAME)) expect(readProjectConfiguration(tree, WORKSPACE_RULES_PROJECT_NAME))
.toMatchInlineSnapshot(` .toMatchInlineSnapshot(`
Object { Object {
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "tools/eslint-rules", "root": "tools/eslint-rules",
"sourceRoot": "tools/eslint-rules", "sourceRoot": "tools/eslint-rules",
"targets": Object { "targets": Object {

View File

@ -47,13 +47,14 @@ export async function lintWorkspaceRulesProjectGenerator(tree: Tree) {
* TODO: Explore writing a ProjectGraph plugin to make this more surgical. * TODO: Explore writing a ProjectGraph plugin to make this more surgical.
*/ */
const workspaceConfig = readWorkspaceConfiguration(tree); const workspaceConfig = readWorkspaceConfiguration(tree);
updateWorkspaceConfiguration(tree, {
...workspaceConfig, if (workspaceConfig.targetDefaults?.lint?.inputs) {
implicitDependencies: { workspaceConfig.targetDefaults.lint.inputs.push(
...workspaceConfig.implicitDependencies, `{workspaceRoot}/${WORKSPACE_PLUGIN_DIR}/**/*`
[`${WORKSPACE_PLUGIN_DIR}/**/*`]: '*', );
},
}); updateWorkspaceConfiguration(tree, workspaceConfig);
}
// Add jest to the project and return installation task // Add jest to the project and return installation task
const installTask = await jestProjectGenerator(tree, { const installTask = await jestProjectGenerator(tree, {

View File

@ -1,4 +1,4 @@
import { logger } from '@nrwl/devkit'; import { NxJsonConfiguration } from '@nrwl/devkit';
import { Tree, readJson, updateJson } from '@nrwl/devkit'; import { Tree, readJson, updateJson } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
import { reactNativeInitGenerator } from './init'; import { reactNativeInitGenerator } from './init';
@ -38,11 +38,23 @@ describe('init', () => {
describe('babel config', () => { describe('babel config', () => {
it('should create babel config if not present', async () => { it('should create babel config if not present', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await reactNativeInitGenerator(tree, { await reactNativeInitGenerator(tree, {
unitTestRunner: 'none', unitTestRunner: 'none',
e2eTestRunner: 'none', e2eTestRunner: 'none',
}); });
expect(tree.exists('babel.config.json')).toBe(true); expect(tree.exists('babel.config.json')).toBe(true);
const sharedGloabls = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGloabls).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGloabls).toContain('{workspaceRoot}/babel.config.json');
}); });
it('should not overwrite existing babel config', async () => { it('should not overwrite existing babel config', async () => {

View File

@ -1,4 +1,9 @@
import { Tree, writeJson } from '@nrwl/devkit'; import {
readWorkspaceConfiguration,
Tree,
updateWorkspaceConfiguration,
writeJson,
} from '@nrwl/devkit';
export function initRootBabelConfig(tree: Tree) { export function initRootBabelConfig(tree: Tree) {
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) { if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
@ -8,4 +13,13 @@ export function initRootBabelConfig(tree: Tree) {
writeJson(tree, '/babel.config.json', { writeJson(tree, '/babel.config.json', {
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
}); });
const workspaceConfiguration = readWorkspaceConfiguration(tree);
if (workspaceConfiguration.namedInputs?.sharedGlobals) {
workspaceConfiguration.namedInputs.sharedGlobals.push(
'{workspaceRoot}/babel.config.json'
);
}
updateWorkspaceConfiguration(tree, workspaceConfiguration);
} }

View File

@ -39,7 +39,46 @@ Object {
"affected": Object { "affected": Object {
"defaultBase": "main", "defaultBase": "main",
}, },
"namedInputs": Object {
"production": Array [
"default",
"!{projectRoot}/.eslintrc.json",
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/jest.config.[jt]s",
"!{projectRoot}/.storybook/**/*",
"!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
],
},
"npmScope": "proj", "npmScope": "proj",
"targetDefaults": Object {
"build-storybook": Object {
"inputs": Array [
"default",
"^production",
"{workspaceRoot}/.storybook/**/*",
],
},
"e2e": Object {
"inputs": Array [
"default",
"^production",
],
},
"lint": Object {
"inputs": Array [
"default",
"{workspaceRoot}/.eslintrc.json",
],
},
"test": Object {
"inputs": Array [
"default",
"^production",
"{workspaceRoot}/jest.preset.js",
],
},
},
"tasksRunnerOptions": Object { "tasksRunnerOptions": Object {
"default": Object { "default": Object {
"options": Object { "options": Object {

View File

@ -1,11 +1,12 @@
import { import {
NxJsonConfiguration,
readJson, readJson,
readProjectConfiguration, readProjectConfiguration,
Tree, Tree,
updateJson, updateJson,
writeJson, writeJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Linter } from '@nrwl/linter'; import { Linter } from '@nrwl/linter';
import { libraryGenerator } from '@nrwl/workspace/generators'; import { libraryGenerator } from '@nrwl/workspace/generators';
@ -18,7 +19,13 @@ describe('@nrwl/storybook:configuration', () => {
let tree: Tree; let tree: Tree;
beforeEach(async () => { beforeEach(async () => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
production: ['default'],
};
return json;
});
await libraryGenerator(tree, { await libraryGenerator(tree, {
name: 'test-ui-lib', name: 'test-ui-lib',
standaloneConfig: false, standaloneConfig: false,
@ -425,7 +432,7 @@ describe('@nrwl/storybook:configuration', () => {
describe('for js Storybook configurations', () => { describe('for js Storybook configurations', () => {
let tree: Tree; let tree: Tree;
beforeAll(async () => { beforeAll(async () => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
writeJson(tree, 'workspace.json', workspaceConfiguration); writeJson(tree, 'workspace.json', workspaceConfiguration);
writeJson(tree, 'apps/nxapp/tsconfig.json', {}); writeJson(tree, 'apps/nxapp/tsconfig.json', {});
writeJson(tree, 'apps/reapp/tsconfig.json', {}); writeJson(tree, 'apps/reapp/tsconfig.json', {});
@ -523,7 +530,7 @@ describe('@nrwl/storybook:configuration', () => {
describe('for TypeScript Storybook configurations', () => { describe('for TypeScript Storybook configurations', () => {
let tree: Tree; let tree: Tree;
beforeAll(async () => { beforeAll(async () => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
writeJson(tree, 'workspace.json', workspaceConfiguration); writeJson(tree, 'workspace.json', workspaceConfiguration);
writeJson(tree, 'apps/nxapp/tsconfig.json', {}); writeJson(tree, 'apps/nxapp/tsconfig.json', {});
writeJson(tree, 'apps/reapp/tsconfig.json', {}); writeJson(tree, 'apps/reapp/tsconfig.json', {});

View File

@ -5,10 +5,12 @@ import {
offsetFromRoot, offsetFromRoot,
readJson, readJson,
readProjectConfiguration, readProjectConfiguration,
readWorkspaceConfiguration,
toJS, toJS,
Tree, Tree,
updateJson, updateJson,
updateProjectConfiguration, updateProjectConfiguration,
updateWorkspaceConfiguration,
writeJson, writeJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { Linter } from '@nrwl/linter'; import { Linter } from '@nrwl/linter';
@ -263,6 +265,31 @@ export function createRootStorybookDir(
rootTsConfigPath: getRootTsConfigPathInTree(tree), rootTsConfigPath: getRootTsConfigPathInTree(tree),
}); });
const workspaceConfiguration = readWorkspaceConfiguration(tree);
const hasProductionFileset = !!workspaceConfiguration.namedInputs?.production;
if (hasProductionFileset) {
workspaceConfiguration.namedInputs.production.push(
'!{projectRoot}/.storybook/**/*'
);
workspaceConfiguration.namedInputs.production.push(
'!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)'
);
}
workspaceConfiguration.targetDefaults ??= {};
workspaceConfiguration.targetDefaults['build-storybook'] ??= {};
workspaceConfiguration.targetDefaults['build-storybook'].inputs ??= [
'default',
hasProductionFileset ? '^production' : '^default',
];
workspaceConfiguration.targetDefaults['build-storybook'].inputs.push(
'{workspaceRoot}/.storybook/**/*'
);
updateWorkspaceConfiguration(tree, workspaceConfiguration);
if (js) { if (js) {
toJS(tree); toJS(tree);
} }

View File

@ -5,7 +5,7 @@ import {
Tree, Tree,
updateJson, updateJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { nxVersion } from '../../utils/versions'; import { nxVersion } from '../../utils/versions';
@ -15,7 +15,7 @@ describe('init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyV1Workspace(); tree = createTreeWithEmptyWorkspace();
}); });
it('should add web dependencies', async () => { it('should add web dependencies', async () => {
@ -48,10 +48,22 @@ describe('init', () => {
describe('babel config', () => { describe('babel config', () => {
it('should create babel config if not present', async () => { it('should create babel config if not present', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await webInitGenerator(tree, { await webInitGenerator(tree, {
unitTestRunner: 'none', unitTestRunner: 'none',
}); });
expect(tree.exists('babel.config.json')).toBe(true); expect(tree.exists('babel.config.json')).toBe(true);
const sharedGloabls = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGloabls).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGloabls).toContain('{workspaceRoot}/babel.config.json');
}); });
it('should not overwrite existing babel config', async () => { it('should not overwrite existing babel config', async () => {

View File

@ -4,8 +4,10 @@ import {
convertNxGenerator, convertNxGenerator,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
readWorkspaceConfiguration,
removeDependenciesFromPackageJson, removeDependenciesFromPackageJson,
Tree, Tree,
updateWorkspaceConfiguration,
writeJson, writeJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { jestInitGenerator } from '@nrwl/jest'; import { jestInitGenerator } from '@nrwl/jest';
@ -42,6 +44,15 @@ function initRootBabelConfig(tree: Tree) {
writeJson(tree, '/babel.config.json', { writeJson(tree, '/babel.config.json', {
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
}); });
const workspaceConfiguration = readWorkspaceConfiguration(tree);
if (workspaceConfiguration.namedInputs?.sharedGlobals) {
workspaceConfiguration.namedInputs.sharedGlobals.push(
'{workspaceRoot}/babel.config.json'
);
}
updateWorkspaceConfiguration(tree, workspaceConfiguration);
} }
export async function webInitGenerator(tree: Tree, schema: Schema) { export async function webInitGenerator(tree: Tree, schema: Schema) {

View File

@ -75,12 +75,15 @@ Object {
"affected": Object { "affected": Object {
"defaultBase": "main", "defaultBase": "main",
}, },
"implicitDependencies": Object { "namedInputs": Object {
".eslintrc.json": "*", "default": Array [
"package.json": Object { "{projectRoot}/**/*",
"dependencies": "*", "sharedGlobals",
"devDependencies": "*", ],
}, "production": Array [
"default",
],
"sharedGlobals": Array [],
}, },
"npmScope": "npmScope", "npmScope": "npmScope",
"targetDefaults": Object { "targetDefaults": Object {
@ -88,6 +91,10 @@ Object {
"dependsOn": Array [ "dependsOn": Array [
"^build", "^build",
], ],
"inputs": Array [
"production",
"^production",
],
}, },
}, },
"tasksRunnerOptions": Object { "tasksRunnerOptions": Object {

View File

@ -1,32 +0,0 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"npmScope": "<%= npmScope %>",
"affected": {
"defaultBase": "<%= defaultBase %>"
},
<% if (packageManager && cli === 'angular') { -%>
"cli": {
"packageManager": "<%=packageManager%>"
},
<% } -%>
"implicitDependencies": {
"package.json": {
"dependencies": "*",
"devDependencies": "*"
},
".eslintrc.json": "*"
},
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"]
}
}
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
}
}
}

View File

@ -1,3 +1,5 @@
import { PackageManager } from '@nrwl/devkit';
export interface Schema { export interface Schema {
name: string; name: string;
directory: string; directory: string;
@ -9,5 +11,5 @@ export interface Schema {
cli: 'nx' | 'angular'; cli: 'nx' | 'angular';
preset: string; preset: string;
defaultBase: string; defaultBase: string;
packageManager?: string; packageManager?: PackageManager;
} }

View File

@ -45,13 +45,6 @@ describe('@nrwl/workspace:workspace', () => {
affected: { affected: {
defaultBase: 'main', defaultBase: 'main',
}, },
implicitDependencies: {
'package.json': {
dependencies: '*',
devDependencies: '*',
},
'.eslintrc.json': '*',
},
tasksRunnerOptions: { tasksRunnerOptions: {
default: { default: {
runner: 'nx/tasks-runners/default', runner: 'nx/tasks-runners/default',
@ -60,11 +53,6 @@ describe('@nrwl/workspace:workspace', () => {
}, },
}, },
}, },
targetDefaults: {
build: {
dependsOn: ['^build'],
},
},
}); });
const validateNxJson = ajv.compile(nxSchema); const validateNxJson = ajv.compile(nxSchema);
expect(validateNxJson(nxJson)).toEqual(true); expect(validateNxJson(nxJson)).toEqual(true);
@ -79,6 +67,43 @@ describe('@nrwl/workspace:workspace', () => {
expect(validateWorkspaceJson(workspaceJson)).toEqual(true); expect(validateWorkspaceJson(workspaceJson)).toEqual(true);
}); });
it('should setup named inputs and target defaults for non-empty presets', async () => {
await workspaceGenerator(tree, {
name: 'proj',
directory: 'proj',
cli: 'nx',
preset: Preset.React,
defaultBase: 'main',
});
const nxJson = readJson<NxJsonConfiguration>(tree, '/proj/nx.json');
expect(nxJson).toEqual({
$schema: './node_modules/nx/schemas/nx-schema.json',
npmScope: 'proj',
affected: {
defaultBase: 'main',
},
tasksRunnerOptions: {
default: {
runner: 'nx/tasks-runners/default',
options: {
cacheableOperations: ['build', 'lint', 'test', 'e2e'],
},
},
},
namedInputs: {
default: ['{projectRoot}/**/*', 'sharedGlobals'],
production: ['default'],
sharedGlobals: [],
},
targetDefaults: {
build: {
dependsOn: ['^build'],
inputs: ['production', '^production'],
},
},
});
});
it('should create a prettierrc file', async () => { it('should create a prettierrc file', async () => {
await workspaceGenerator(tree, { await workspaceGenerator(tree, {
name: 'proj', name: 'proj',

View File

@ -8,6 +8,7 @@ import {
formatFiles, formatFiles,
getPackageManagerVersion, getPackageManagerVersion,
PackageManager, PackageManager,
NxJsonConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { Schema } from './schema'; import { Schema } from './schema';
import { import {
@ -59,8 +60,54 @@ function createAppsAndLibsFolders(host: Tree, options: Schema) {
} }
} }
function createNxJson(
host: Tree,
{ directory, npmScope, cli, packageManager, defaultBase, preset }: Schema
) {
const nxJson: NxJsonConfiguration & { $schema: string } = {
$schema: './node_modules/nx/schemas/nx-schema.json',
npmScope: npmScope,
affected: {
defaultBase,
},
tasksRunnerOptions: {
default: {
runner: 'nx/tasks-runners/default',
options: {
cacheableOperations: ['build', 'lint', 'test', 'e2e'],
},
},
},
};
if (
preset !== Preset.Core &&
preset !== Preset.NPM &&
preset !== Preset.Empty
) {
nxJson.namedInputs = {
default: ['{projectRoot}/**/*', 'sharedGlobals'],
production: ['default'],
sharedGlobals: [],
};
nxJson.targetDefaults = {
build: {
dependsOn: ['^build'],
inputs: ['production', '^production'],
},
};
}
if (packageManager && cli === 'angular') {
nxJson.cli = {
packageManager: packageManager,
};
}
writeJson<NxJsonConfiguration>(host, join(directory, 'nx.json'), nxJson);
}
function createFiles(host: Tree, options: Schema) { function createFiles(host: Tree, options: Schema) {
const npmScope = options.npmScope ?? options.name;
const formattedNames = names(options.name); const formattedNames = names(options.name);
generateFiles(host, pathJoin(__dirname, './files'), options.directory, { generateFiles(host, pathJoin(__dirname, './files'), options.directory, {
formattedNames, formattedNames,
@ -75,7 +122,6 @@ function createFiles(host: Tree, options: Schema) {
angularCliVersion, angularCliVersion,
...(options as object), ...(options as object),
nxVersion, nxVersion,
npmScope,
packageManager: options.packageManager, packageManager: options.packageManager,
}); });
} }
@ -157,6 +203,7 @@ export async function workspaceGenerator(host: Tree, options: Schema) {
} }
options = normalizeOptions(options); options = normalizeOptions(options);
createFiles(host, options); createFiles(host, options);
createNxJson(host, options);
createPrettierrc(host, options); createPrettierrc(host, options);
if (options.cli === 'angular') { if (options.cli === 'angular') {
decorateAngularClI(host, options); decorateAngularClI(host, options);
@ -193,6 +240,7 @@ function addPropertyWithStableKeys(obj: any, key: string, value: string) {
function normalizeOptions(options: Schema) { function normalizeOptions(options: Schema) {
let defaultBase = options.defaultBase || deduceDefaultBase(); let defaultBase = options.defaultBase || deduceDefaultBase();
return { return {
npmScope: options.name,
...options, ...options,
defaultBase, defaultBase,
}; };

View File

@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
import * as yargs from 'yargs'; import * as yargs from 'yargs';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { readFileSync, writeFileSync } from 'fs'; import { existsSync, readFileSync, writeFileSync } from 'fs';
import { URL } from 'url'; import { URL } from 'url';
import { join } from 'path'; import { join } from 'path';
@ -75,7 +75,8 @@ function hideFromGitIndex(uncommittedFiles: string[]) {
const uncommittedFiles = execSync('git diff --name-only --relative HEAD .') const uncommittedFiles = execSync('git diff --name-only --relative HEAD .')
.toString() .toString()
.split('\n') .split('\n')
.filter((i) => i.length > 0); .filter((i) => i.length > 0)
.filter((f) => existsSync(f));
const unhideFromGitIndex = hideFromGitIndex(uncommittedFiles); const unhideFromGitIndex = hideFromGitIndex(uncommittedFiles);
process.on('exit', unhideFromGitIndex); process.on('exit', unhideFromGitIndex);