feat(react): migrate @nrwl/web schematics to devkit (#4666)

This commit is contained in:
Jason Jean 2021-02-02 18:56:35 -05:00 committed by GitHub
parent a500088d36
commit 06f84b3326
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 626 additions and 569 deletions

View File

@ -7,13 +7,14 @@ packages/workspace/src/generators/**/files/**/*.json
packages/workspace/src/core/dep-graph/vendor.js packages/workspace/src/core/dep-graph/vendor.js
packages/angular/src/schematics/**/files/**/*.json packages/angular/src/schematics/**/files/**/*.json
packages/angular/src/migrations/**/files/**/*.json packages/angular/src/migrations/**/files/**/*.json
packages/web/src/schematics/**/files/**/*.json packages/web/src/generators/**/files/**/*.json
packages/node/src/schematics/**/files/**/*.json packages/node/src/schematics/**/files/**/*.json
packages/express/src/schematics/**/files/**/*.json packages/express/src/schematics/**/files/**/*.json
packages/nest/src/schematics/**/files/**/*.json packages/nest/src/schematics/**/files/**/*.json
packages/react/src/schematics/**/files/**/*.json packages/react/src/schematics/**/files/**/*.json
packages/jest/src/schematics/**/files/**/*.json packages/jest/src/schematics/**/files/**/*.json
packages/**/schematics/**/files/**/*.html packages/**/schematics/**/files/**/*.html
packages/**/generators/**/files/**/*.html
/.vscode /.vscode
/.idea /.idea
/.github /.github

View File

@ -3,7 +3,7 @@
"version": "0.1", "version": "0.1",
"schematics": { "schematics": {
"init": { "init": {
"factory": "./src/generators/init/init#initSchematic", "factory": "./src/generators/init/init#cypressInitSchematic",
"schema": "./src/generators/init/schema.json", "schema": "./src/generators/init/schema.json",
"description": "Initialize the @nrwl/cypress plugin", "description": "Initialize the @nrwl/cypress plugin",
"aliases": ["ng-add"], "aliases": ["ng-add"],
@ -18,7 +18,7 @@
}, },
"generators": { "generators": {
"init": { "init": {
"factory": "./src/generators/init/init#initGenerator", "factory": "./src/generators/init/init#cypressInitGenerator",
"schema": "./src/generators/init/schema.json", "schema": "./src/generators/init/schema.json",
"description": "Initialize the @nrwl/cypress plugin", "description": "Initialize the @nrwl/cypress plugin",
"aliases": ["ng-add"], "aliases": ["ng-add"],

View File

@ -1 +1,2 @@
export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project'; export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
export { cypressInitGenerator } from './src/generators/init/init';

View File

@ -76,7 +76,7 @@ async function addLinter(host: Tree, options: CypressProjectSchema) {
], ],
}); });
if (options.linter !== Linter.EsLint) { if (!options.linter || options.linter !== Linter.EsLint) {
return installTask; return installTask;
} }
@ -127,6 +127,8 @@ function normalizeOptions(host: Tree, options: Schema): CypressProjectSchema {
options.name options.name
) )
: joinPathFragments(appsDir, options.name); : joinPathFragments(appsDir, options.name);
options.linter = options.linter || Linter.EsLint;
return { return {
...options, ...options,
projectName, projectName,

View File

@ -4,7 +4,7 @@ export interface Schema {
project?: string; project?: string;
name: string; name: string;
directory?: string; directory?: string;
linter: Linter; linter?: Linter;
js?: boolean; js?: boolean;
skipFormat?: boolean; skipFormat?: boolean;
} }

View File

@ -2,7 +2,7 @@ import { readJson, Tree, updateJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { cypressVersion } from '../../utils/versions'; import { cypressVersion } from '../../utils/versions';
import initGenerator from './init'; import { cypressInitGenerator } from './init';
describe('init', () => { describe('init', () => {
let tree: Tree; let tree: Tree;
@ -21,7 +21,7 @@ describe('init', () => {
json.devDependencies[existing] = existingVersion; json.devDependencies[existing] = existingVersion;
return json; return json;
}); });
initGenerator(tree); cypressInitGenerator(tree);
const packageJson = readJson(tree, 'package.json'); const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies.cypress).toBeDefined(); expect(packageJson.devDependencies.cypress).toBeDefined();

View File

@ -24,9 +24,9 @@ function updateDependencies(host: Tree) {
); );
} }
export function initGenerator(host: Tree) { export function cypressInitGenerator(host: Tree) {
return updateDependencies(host); return updateDependencies(host);
} }
export default initGenerator; export default cypressInitGenerator;
export const initSchematic = convertNxGenerator(initGenerator); export const cypressInitSchematic = convertNxGenerator(cypressInitGenerator);

View File

@ -4,3 +4,4 @@ export {
} from './src/utils/config/update-config'; } from './src/utils/config/update-config';
export { jestConfigObjectAst } from './src/utils/config/functions'; export { jestConfigObjectAst } from './src/utils/config/functions';
export { jestProjectGenerator } from './src/generators/jest-project/jest-project'; export { jestProjectGenerator } from './src/generators/jest-project/jest-project';
export { jestInitGenerator } from './src/generators/init/init';

View File

@ -23,6 +23,10 @@ function normalizeOptions(options: JestProjectSchema) {
options.testEnvironment = ''; options.testEnvironment = '';
} }
if (!options.hasOwnProperty('supportTsx')) {
options.supportTsx = false;
}
// if we support TSX or babelJest we don't support angular(html templates) // if we support TSX or babelJest we don't support angular(html templates)
if (options.supportTsx || options.babelJest) { if (options.supportTsx || options.babelJest) {
options.skipSerializers = true; options.skipSerializers = true;

View File

@ -1,13 +1,13 @@
export interface JestProjectSchema { export interface JestProjectSchema {
project: string; project: string;
supportTsx: boolean; supportTsx?: boolean;
/** /**
* @deprecated * @deprecated
*/ */
skipSetupFile: boolean; skipSetupFile?: boolean;
setupFile: 'angular' | 'web-components' | 'none'; setupFile?: 'angular' | 'web-components' | 'none';
skipSerializers: boolean; skipSerializers?: boolean;
testEnvironment?: 'node' | 'jsdom' | ''; testEnvironment?: 'node' | 'jsdom' | '';
babelJest: boolean; babelJest?: boolean;
skipFormat: boolean; skipFormat?: boolean;
} }

View File

@ -15,7 +15,7 @@ import {
import { Linter } from '../utils/linter'; import { Linter } from '../utils/linter';
export interface LinterInitOptions { export interface LinterInitOptions {
linter: Linter; linter?: Linter;
} }
const globalTsLintConfiguration = { const globalTsLintConfiguration = {
@ -197,9 +197,9 @@ function initEsLint(tree: Tree) {
} }
export function lintInitGenerator(tree: Tree, options: LinterInitOptions) { export function lintInitGenerator(tree: Tree, options: LinterInitOptions) {
if (options.linter === Linter.TsLint) { if (!options.linter || options.linter === Linter.EsLint) {
return initTsLint(tree);
} else if (options.linter === Linter.EsLint) {
return initEsLint(tree); return initEsLint(tree);
} else if (options.linter === Linter.TsLint) {
return initTsLint(tree);
} }
} }

View File

@ -13,7 +13,7 @@ import { lintInitGenerator } from '../init/init';
interface LintProjectOptions { interface LintProjectOptions {
project: string; project: string;
linter: Linter; linter?: Linter;
eslintFilePatterns?: string[]; eslintFilePatterns?: string[];
tsConfigPaths?: string[]; tsConfigPaths?: string[];
skipFormat: boolean; skipFormat: boolean;

View File

@ -46,10 +46,10 @@ import {
} from '../../utils/versions'; } from '../../utils/versions';
import { Schema } from './schema'; import { Schema } from './schema';
import { libsDir } from '@nrwl/workspace/src/utils/ast-utils'; import { libsDir } from '@nrwl/workspace/src/utils/ast-utils';
import { initRootBabelConfig } from '@nrwl/web/src/utils/rules';
import { updateBabelJestConfig } from '../../rules/update-babel-jest-config'; import { updateBabelJestConfig } from '../../rules/update-babel-jest-config';
import { names, offsetFromRoot } from '@nrwl/devkit'; import { names, offsetFromRoot } from '@nrwl/devkit';
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter'; import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
import init from '../init/init';
export interface NormalizedSchema extends Schema { export interface NormalizedSchema extends Schema {
name: string; name: string;
@ -76,6 +76,11 @@ export default function (schema: Schema): Rule {
options.style = 'none'; options.style = 'none';
} }
return chain([ return chain([
init({
...options,
e2eTestRunner: 'none',
skipFormat: true,
}),
addLintFiles(options.projectRoot, options.linter, { addLintFiles(options.projectRoot, options.linter, {
localConfig: reactEslintJson, localConfig: reactEslintJson,
extraPackageDeps: extraEslintDependencies, extraPackageDeps: extraEslintDependencies,
@ -125,7 +130,6 @@ export default function (schema: Schema): Rule {
{} {}
), ),
updateAppRoutes(options, context), updateAppRoutes(options, context),
initRootBabelConfig(),
formatFiles(options), formatFiles(options),
])(host, context); ])(host, context);
}; };

View File

@ -44,7 +44,10 @@ export interface WorkspaceConfiguration {
/** /**
* Default generator collection. It is used when no collection is provided. * Default generator collection. It is used when no collection is provided.
*/ */
cli?: { defaultCollection: string }; cli?: {
packageManager?: 'npm' | 'yarn' | 'pnpm';
defaultCollection?: string;
};
} }
/** /**

View File

@ -4,15 +4,30 @@
"extends": ["@nrwl/workspace"], "extends": ["@nrwl/workspace"],
"schematics": { "schematics": {
"init": { "init": {
"factory": "./src/schematics/init/init", "factory": "./src/generators/init/init#webInitSchematic",
"schema": "./src/schematics/init/schema.json", "schema": "./src/generators/init/schema.json",
"description": "Add @nrwl/web to a project", "description": "Add @nrwl/web to a project",
"hidden": true "hidden": true
}, },
"application": { "application": {
"factory": "./src/schematics/application/application", "factory": "./src/generators/application/application#applicationSchematic",
"schema": "./src/schematics/application/schema.json", "schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"description": "Create an application"
}
},
"generators": {
"init": {
"factory": "./src/generators/init/init#webInitGenerator",
"schema": "./src/generators/init/schema.json",
"description": "Add @nrwl/web to a project",
"hidden": true
},
"application": {
"factory": "./src/generators/application/application#applicationGenerator",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"], "aliases": ["app"],
"description": "Create an application" "description": "Create an application"
} }

View File

@ -1 +1 @@
export { applicationGenerator } from './src/schematics/application/application'; export { applicationGenerator } from './src/generators/application/application';

View File

@ -33,9 +33,9 @@ import { ExtraEntryPoint } from '../../utils/third-party/browser/schema';
export interface WebBuildBuilderOptions extends BuildBuilderOptions { export interface WebBuildBuilderOptions extends BuildBuilderOptions {
index: string; index: string;
budgets: any[]; budgets?: any[];
baseHref: string; baseHref?: string;
deployUrl: string; deployUrl?: string;
extractCss?: boolean; extractCss?: boolean;
crossOrigin?: CrossOriginValue; crossOrigin?: CrossOriginValue;
@ -49,6 +49,8 @@ export interface WebBuildBuilderOptions extends BuildBuilderOptions {
vendorChunk?: boolean; vendorChunk?: boolean;
commonChunk?: boolean; commonChunk?: boolean;
namedChunks?: boolean;
stylePreprocessingOptions?: any; stylePreprocessingOptions?: any;
subresourceIntegrity?: boolean; subresourceIntegrity?: boolean;

View File

@ -1,23 +1,21 @@
import { Tree } from '@angular-devkit/schematics'; import { readJson, Tree } from '@nrwl/devkit';
import { createEmptyWorkspace } from '@nrwl/workspace/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import * as stripJsonComments from 'strip-json-comments'; import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
import { readJsonInTree, NxJson } from '@nrwl/workspace';
import { runSchematic } from '../../utils/testing';
import { applicationGenerator } from './application';
import { Schema } from './schema'; import { Schema } from './schema';
describe('app', () => { describe('app', () => {
let appTree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
appTree = Tree.empty(); tree = createTreeWithEmptyWorkspace();
appTree = createEmptyWorkspace(appTree);
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update workspace.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); await applicationGenerator(tree, { name: 'myApp' });
const workspaceJson = readJsonInTree(tree, '/workspace.json'); const workspaceJson = readJson(tree, '/workspace.json');
expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app'); expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app');
expect(workspaceJson.projects['my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-app-e2e'].root).toEqual(
@ -27,12 +25,8 @@ describe('app', () => {
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
const tree = await runSchematic( await applicationGenerator(tree, { name: 'myApp', tags: 'one,two' });
'app', const nxJson = readJson<NxJson>(tree, '/nx.json');
{ name: 'myApp', tags: 'one,two' },
appTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson.projects).toEqual({ expect(nxJson.projects).toEqual({
'my-app': { 'my-app': {
tags: ['one', 'two'], tags: ['one', 'two'],
@ -45,7 +39,7 @@ describe('app', () => {
}); });
it('should generate files', async () => { it('should generate files', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); await applicationGenerator(tree, { name: 'myApp' });
expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/app.element.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/app/app.element.ts')).toBeTruthy();
expect( expect(
@ -53,7 +47,7 @@ describe('app', () => {
).toBeTruthy(); ).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/app.element.css')).toBeTruthy(); expect(tree.exists('apps/my-app/src/app/app.element.css')).toBeTruthy();
const tsconfig = readJsonInTree(tree, 'apps/my-app/tsconfig.json'); const tsconfig = readJson(tree, 'apps/my-app/tsconfig.json');
expect(tsconfig.references).toEqual([ expect(tsconfig.references).toEqual([
{ {
path: './tsconfig.app.json', path: './tsconfig.app.json',
@ -63,21 +57,15 @@ describe('app', () => {
}, },
]); ]);
const tsconfigApp = JSON.parse( const tsconfigApp = readJson(tree, 'apps/my-app/tsconfig.app.json');
stripJsonComments(tree.readContent('apps/my-app/tsconfig.app.json'))
);
expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc'); expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc');
expect(tsconfigApp.extends).toEqual('./tsconfig.json'); expect(tsconfigApp.extends).toEqual('./tsconfig.json');
const linter = JSON.parse( const linter = readJson(tree, 'apps/my-app/.eslintrc.json');
stripJsonComments(tree.readContent('apps/my-app/.eslintrc.json')) expect(linter.extends).toEqual(['../../.eslintrc.json']);
);
expect(linter.extends).toEqual('../../.eslintrc.json');
expect(tree.exists('apps/my-app-e2e/cypress.json')).toBeTruthy(); expect(tree.exists('apps/my-app-e2e/cypress.json')).toBeTruthy();
const tsconfigE2E = JSON.parse( const tsconfigE2E = readJson(tree, 'apps/my-app-e2e/tsconfig.e2e.json');
stripJsonComments(tree.readContent('apps/my-app-e2e/tsconfig.e2e.json'))
);
expect(tsconfigE2E.compilerOptions.outDir).toEqual('../../dist/out-tsc'); expect(tsconfigE2E.compilerOptions.outDir).toEqual('../../dist/out-tsc');
expect(tsconfigE2E.extends).toEqual('./tsconfig.json'); expect(tsconfigE2E.extends).toEqual('./tsconfig.json');
}); });
@ -85,12 +73,11 @@ describe('app', () => {
describe('nested', () => { describe('nested', () => {
it('should update workspace.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', directory: 'myDir' }, directory: 'myDir',
appTree });
); const workspaceJson = readJson(tree, '/workspace.json');
const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(workspaceJson.projects['my-dir-my-app'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app'].root).toEqual(
'apps/my-dir/my-app' 'apps/my-dir/my-app'
@ -101,12 +88,12 @@ describe('app', () => {
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', directory: 'myDir', tags: 'one,two' }, directory: 'myDir',
appTree tags: 'one,two',
); });
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json'); const nxJson = readJson<NxJson>(tree, '/nx.json');
expect(nxJson.projects).toEqual({ expect(nxJson.projects).toEqual({
'my-dir-my-app': { 'my-dir-my-app': {
tags: ['one', 'two'], tags: ['one', 'two'],
@ -120,16 +107,14 @@ describe('app', () => {
it('should generate files', async () => { it('should generate files', async () => {
const hasJsonValue = ({ path, expectedValue, lookupFn }) => { const hasJsonValue = ({ path, expectedValue, lookupFn }) => {
const content = tree.readContent(path); const config = readJson(tree, path);
const config = JSON.parse(stripJsonComments(content));
expect(lookupFn(config)).toEqual(expectedValue); expect(lookupFn(config)).toEqual(expectedValue);
}; };
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', directory: 'myDir' }, directory: 'myDir',
appTree });
);
// Make sure these exist // Make sure these exist
[ [
@ -156,62 +141,50 @@ describe('app', () => {
{ {
path: 'apps/my-dir/my-app/.eslintrc.json', path: 'apps/my-dir/my-app/.eslintrc.json',
lookupFn: (json) => json.extends, lookupFn: (json) => json.extends,
expectedValue: '../../../.eslintrc.json', expectedValue: ['../../../.eslintrc.json'],
}, },
].forEach(hasJsonValue); ].forEach(hasJsonValue);
}); });
it('should create Nx specific template', async () => { it('should create Nx specific template', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', directory: 'myDir' }, directory: 'myDir',
appTree });
);
expect( expect(
tree.readContent('apps/my-dir/my-app/src/app/app.element.ts') tree.read('apps/my-dir/my-app/src/app/app.element.ts').toString()
).toBeTruthy(); ).toBeTruthy();
expect( expect(
tree.readContent('apps/my-dir/my-app/src/app/app.element.ts') tree.read('apps/my-dir/my-app/src/app/app.element.ts').toString()
).toContain('Thank you for using and showing some ♥ for Nx.'); ).toContain('Thank you for using and showing some ♥ for Nx.');
}); });
}); });
describe('--style scss', () => { describe('--style scss', () => {
it('should generate scss styles', async () => { it('should generate scss styles', async () => {
const result = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', style: 'scss' }, style: 'scss',
appTree });
); expect(tree.exists('apps/my-app/src/app/app.element.scss')).toEqual(true);
expect(result.exists('apps/my-app/src/app/app.element.scss')).toEqual(
true
);
}); });
}); });
it('should setup jest without serializers', async () => { it('should setup jest without serializers', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app',
{
name: 'my-App', name: 'my-App',
}, });
appTree
);
expect(tree.readContent('apps/my-app/jest.config.js')).not.toContain( expect(tree.read('apps/my-app/jest.config.js').toString()).not.toContain(
`'jest-preset-angular/build/AngularSnapshotSerializer.js',` `'jest-preset-angular/build/AngularSnapshotSerializer.js',`
); );
}); });
it('should setup the nrwl web build builder', async () => { it('should setup the nrwl web build builder', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app',
{
name: 'my-App', name: 'my-App',
}, });
appTree const workspaceJson = readJson(tree, 'workspace.json');
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = workspaceJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.build.builder).toEqual('@nrwl/web:build'); expect(architectConfig.build.builder).toEqual('@nrwl/web:build');
expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']); expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']);
@ -250,14 +223,10 @@ describe('app', () => {
}); });
it('should setup the nrwl web dev server builder', async () => { it('should setup the nrwl web dev server builder', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app',
{
name: 'my-App', name: 'my-App',
}, });
appTree const workspaceJson = readJson(tree, 'workspace.json');
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = workspaceJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server'); expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server');
expect(architectConfig.serve.options).toEqual({ expect(architectConfig.serve.options).toEqual({
@ -269,14 +238,10 @@ describe('app', () => {
}); });
it('should setup the eslint builder', async () => { it('should setup the eslint builder', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app',
{
name: 'my-App', name: 'my-App',
}, });
appTree const workspaceJson = readJson(tree, 'workspace.json');
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(workspaceJson.projects['my-app'].architect.lint).toEqual({ expect(workspaceJson.projects['my-app'].architect.lint).toEqual({
builder: '@nrwl/linter:eslint', builder: '@nrwl/linter:eslint',
@ -288,13 +253,9 @@ describe('app', () => {
describe('--prefix', () => { describe('--prefix', () => {
it('should use the prefix in the index.html', async () => { it('should use the prefix in the index.html', async () => {
const tree = await runSchematic( await applicationGenerator(tree, { name: 'myApp', prefix: 'prefix' });
'app',
{ name: 'myApp', prefix: 'prefix' },
appTree
);
expect(tree.readContent('apps/my-app/src/index.html')).toContain( expect(tree.read('apps/my-app/src/index.html').toString()).toContain(
'<prefix-root></prefix-root>' '<prefix-root></prefix-root>'
); );
}); });
@ -302,16 +263,17 @@ describe('app', () => {
describe('--unit-test-runner none', () => { describe('--unit-test-runner none', () => {
it('should not generate test configuration', async () => { it('should not generate test configuration', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', unitTestRunner: 'none' }, unitTestRunner: 'none',
appTree });
);
expect(tree.exists('jest.config.js')).toBeFalsy(); expect(tree.exists('jest.config.js')).toBeFalsy();
expect(tree.exists('apps/my-app/src/app/app.spec.ts')).toBeFalsy(); expect(
tree.exists('apps/my-app/src/app/app.element.spec.ts')
).toBeFalsy();
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy(); expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy();
const workspaceJson = readJsonInTree(tree, 'workspace.json'); const workspaceJson = readJson(tree, 'workspace.json');
expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined();
expect(workspaceJson.projects['my-app'].architect.lint) expect(workspaceJson.projects['my-app'].architect.lint)
.toMatchInlineSnapshot(` .toMatchInlineSnapshot(`
@ -329,44 +291,40 @@ describe('app', () => {
describe('--e2e-test-runner none', () => { describe('--e2e-test-runner none', () => {
it('should not generate test configuration', async () => { it('should not generate test configuration', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', e2eTestRunner: 'none' }, e2eTestRunner: 'none',
appTree });
);
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const workspaceJson = readJsonInTree(tree, 'workspace.json'); const workspaceJson = readJson(tree, 'workspace.json');
expect(workspaceJson.projects['my-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-app-e2e']).toBeUndefined();
}); });
}); });
describe('--babelJest', () => { describe('--babelJest', () => {
it('should use babel for jest', async () => { it('should use babel for jest', async () => {
const tree = await runSchematic( await applicationGenerator(tree, {
'app', name: 'myApp',
{ name: 'myApp', babelJest: true } as Schema, babelJest: true,
appTree } as Schema);
);
expect(tree.readContent(`apps/my-app/jest.config.js`)) expect(tree.read(`apps/my-app/jest.config.js`).toString())
.toMatchInlineSnapshot(` .toMatchInlineSnapshot(`
"module.exports = { "module.exports = {
displayName: 'my-app', displayName: 'my-app',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
transform: { transform: {
'^.+\\\\\\\\.[tj]s$': [ '^.+\\\\\\\\.[tj]s$': [ 'babel-jest',
'babel-jest', { cwd: __dirname, configFile: './babel-jest.config.json' }]
{ cwd: __dirname, configFile: './babel-jest.config.json' },
],
}, },
moduleFileExtensions: ['ts', 'js', 'html'], moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/my-app', coverageDirectory: '../../coverage/apps/my-app'
}; };
" "
`); `);
expect(readJsonInTree(tree, 'apps/my-app/babel-jest.config.json')) expect(readJson(tree, 'apps/my-app/babel-jest.config.json'))
.toMatchInlineSnapshot(` .toMatchInlineSnapshot(`
Object { Object {
"presets": Array [ "presets": Array [

View File

@ -0,0 +1,243 @@
import {
addProjectConfiguration,
convertNxGenerator,
formatFiles,
generateFiles,
getWorkspaceLayout,
joinPathFragments,
names,
NxJsonProjectConfiguration,
offsetFromRoot,
ProjectConfiguration,
readWorkspaceConfiguration,
TargetConfiguration,
Tree,
updateWorkspaceConfiguration,
} from '@nrwl/devkit';
import { join } from 'path';
import { webInitGenerator } from '../init/init';
import { cypressProjectGenerator } from '@nrwl/cypress';
import { Linter, lintProjectGenerator } from '@nrwl/linter';
import { jestProjectGenerator } from '@nrwl/jest';
import { WebBuildBuilderOptions } from '../../builders/build/build.impl';
import { Schema } from './schema';
interface NormalizedSchema extends Schema {
projectName: string;
appProjectRoot: string;
e2eProjectName: string;
e2eProjectRoot: string;
parsedTags: string[];
}
function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
...options,
...names(options.name),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
});
if (options.unitTestRunner === 'none') {
tree.delete(join(options.appProjectRoot, './src/app/app.element.spec.ts'));
}
}
function addBuildTarget(
project: ProjectConfiguration,
options: NormalizedSchema
): ProjectConfiguration {
const buildOptions: WebBuildBuilderOptions = {
outputPath: joinPathFragments('dist', options.appProjectRoot),
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
main: joinPathFragments(options.appProjectRoot, 'src/main.ts'),
polyfills: joinPathFragments(options.appProjectRoot, 'src/polyfills.ts'),
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
assets: [
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
joinPathFragments(options.appProjectRoot, 'src/assets'),
],
styles: [
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
],
scripts: [],
};
const productionBuildOptions: Partial<WebBuildBuilderOptions> = {
fileReplacements: [
{
replace: joinPathFragments(
options.appProjectRoot,
`src/environments/environment.ts`
),
with: joinPathFragments(
options.appProjectRoot,
`src/environments/environment.prod.ts`
),
},
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
budgets: [
{
type: 'initial',
maximumWarning: '2mb',
maximumError: '5mb',
},
],
};
return {
...project,
targets: {
...project.targets,
build: {
executor: '@nrwl/web:build',
outputs: ['{options.outputPath}'],
options: buildOptions,
configurations: {
production: productionBuildOptions,
},
},
},
};
}
function addServeTarget(
project: ProjectConfiguration,
options: NormalizedSchema
) {
const serveTarget: TargetConfiguration = {
executor: '@nrwl/web:dev-server',
options: {
buildTarget: `${options.projectName}:build`,
},
configurations: {
production: {
buildTarget: `${options.projectName}:build:production`,
},
},
};
return {
...project,
targets: {
...project.targets,
serve: serveTarget,
},
};
}
function addProject(tree: Tree, options: NormalizedSchema) {
const targets: Record<string, TargetConfiguration> = {};
let project: ProjectConfiguration & NxJsonProjectConfiguration = {
projectType: 'application',
root: options.appProjectRoot,
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
tags: options.parsedTags,
targets,
};
project = addBuildTarget(project, options);
project = addServeTarget(project, options);
addProjectConfiguration(tree, options.projectName, project);
const workspace = readWorkspaceConfiguration(tree);
if (!workspace.defaultProject) {
workspace.defaultProject = options.projectName;
updateWorkspaceConfiguration(tree, workspace);
}
}
export async function applicationGenerator(host: Tree, schema: Schema) {
const options = normalizeOptions(host, schema);
let installTask = await webInitGenerator(host, {
...options,
skipFormat: true,
});
createApplicationFiles(host, options);
addProject(host, options);
const lintInstallTask = await lintProjectGenerator(host, {
linter: options.linter,
project: options.projectName,
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`],
skipFormat: true,
});
installTask = lintInstallTask || installTask;
if (options.e2eTestRunner === 'cypress') {
const cypressInstallTask = await cypressProjectGenerator(host, {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName,
});
installTask = cypressInstallTask || installTask;
}
if (options.unitTestRunner === 'jest') {
const jestInstallTask = await jestProjectGenerator(host, {
project: options.projectName,
skipSerializers: true,
setupFile: 'web-components',
babelJest: options.babelJest,
});
installTask = jestInstallTask || installTask;
}
if (!schema.skipFormat) {
await formatFiles(host);
}
return installTask;
}
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const appDirectory = options.directory
? `${names(options.directory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;
const { appsDir, npmScope: defaultPrefix } = getWorkspaceLayout(host);
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
const e2eProjectName = `${appProjectName}-e2e`;
const appProjectRoot = `${appsDir}/${appDirectory}`;
const e2eProjectRoot = `${appsDir}/${appDirectory}-e2e`;
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
options.style = options.style || 'css';
options.linter = options.linter || Linter.EsLint;
options.unitTestRunner = options.unitTestRunner || 'jest';
options.e2eTestRunner = options.e2eTestRunner || 'cypress';
return {
...options,
prefix: options.prefix ? options.prefix : defaultPrefix,
name: names(options.name).fileName,
projectName: appProjectName,
appProjectRoot,
e2eProjectRoot,
e2eProjectName,
parsedTags,
};
}
export default applicationGenerator;
export const applicationSchematic = convertNxGenerator(applicationGenerator);

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,14 @@
import { Linter } from '@nrwl/linter';
export interface Schema {
name: string;
prefix?: string;
style?: string;
skipFormat?: boolean;
directory?: string;
tags?: string;
unitTestRunner?: 'jest' | 'none';
e2eTestRunner?: 'cypress' | 'none';
linter?: Linter;
babelJest?: boolean;
}

View File

@ -0,0 +1,79 @@
import { addDependenciesToPackageJson, readJson, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { nxVersion } from '../../utils/versions';
import webInitGenerator from './init';
describe('init', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should add web dependencies', async () => {
const existing = 'existing';
const existingVersion = '1.0.0';
addDependenciesToPackageJson(
tree,
{
'@nrwl/web': nxVersion,
[existing]: existingVersion,
},
{
[existing]: existingVersion,
}
);
await webInitGenerator(tree, {});
const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies['@nrwl/web']).toBeDefined();
expect(packageJson.devDependencies[existing]).toBeDefined();
expect(packageJson.dependencies['@nrwl/web']).toBeUndefined();
expect(packageJson.dependencies['document-register-element']).toBeDefined();
expect(packageJson.dependencies[existing]).toBeDefined();
});
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
await webInitGenerator(tree, {});
const workspaceJson = readJson(tree, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
});
});
it('should not add jest config if unitTestRunner is none', async () => {
await webInitGenerator(tree, {
unitTestRunner: 'none',
});
expect(tree.exists('jest.config.js')).toBe(false);
});
describe('babel config', () => {
it('should create babel config if not present', async () => {
await webInitGenerator(tree, {
unitTestRunner: 'none',
});
expect(tree.exists('babel.config.json')).toBe(true);
});
it('should not overwrite existing babel config', async () => {
tree.write('babel.config.json', '{ "preset": ["preset-awesome"] }');
await webInitGenerator(tree, {
unitTestRunner: 'none',
});
const existing = readJson(tree, 'babel.config.json');
expect(existing).toEqual({ preset: ['preset-awesome'] });
});
it('should not overwrite existing babel config (.js)', async () => {
tree.write('/babel.config.js', 'module.exports = () => {};');
await webInitGenerator(tree, {
unitTestRunner: 'none',
});
expect(tree.exists('babel.config.json')).toBe(false);
});
});
});

View File

@ -0,0 +1,82 @@
import {
addDependenciesToPackageJson,
convertNxGenerator,
formatFiles,
GeneratorCallback,
readWorkspaceConfiguration,
Tree,
updateJson,
updateWorkspaceConfiguration,
writeJson,
} from '@nrwl/devkit';
import { Schema } from './schema';
import {
documentRegisterElementVersion,
nxVersion,
} from '../../utils/versions';
import { cypressInitGenerator } from '@nrwl/cypress';
import { jestInitGenerator } from '@nrwl/jest';
function updateDependencies(tree: Tree) {
updateJson(tree, 'package.json', (json) => {
delete json.dependencies['@nrwl/web'];
return json;
});
return addDependenciesToPackageJson(
tree,
{
'core-js': '^3.6.5',
'document-register-element': documentRegisterElementVersion,
tslib: '^2.0.0',
},
{
'@nrwl/web': nxVersion,
}
);
}
function setDefaultCollection(tree: Tree) {
const workspace = readWorkspaceConfiguration(tree);
workspace.cli = workspace.cli || {};
const defaultCollection = workspace.cli.defaultCollection;
if (!defaultCollection || defaultCollection === '@nrwl/workspace') {
workspace.cli.defaultCollection = '@nrwl/web';
}
updateWorkspaceConfiguration(tree, workspace);
}
function initRootBabelConfig(tree: Tree) {
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
return;
}
writeJson(tree, '/babel.config.json', {
presets: ['@nrwl/web/babel'],
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
});
}
export async function webInitGenerator(tree: Tree, schema: Schema) {
let installTask: GeneratorCallback;
setDefaultCollection(tree);
if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') {
installTask = jestInitGenerator(tree, {});
}
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
installTask = cypressInitGenerator(tree) || installTask;
}
installTask = updateDependencies(tree) || installTask;
initRootBabelConfig(tree);
if (!schema.skipFormat) {
await formatFiles(tree);
}
return installTask;
}
export default webInitGenerator;
export const webInitSchematic = convertNxGenerator(webInitGenerator);

View File

@ -0,0 +1,5 @@
export interface Schema {
unitTestRunner?: 'jest' | 'none';
e2eTestRunner?: 'cypress' | 'none';
skipFormat?: boolean;
}

View File

@ -1,6 +1,7 @@
{ {
"$schema": "http://json-schema.org/schema", "$schema": "http://json-schema.org/schema",
"id": "NxWebInit", "id": "NxWebInit",
"cli": "nx",
"title": "Init Web Plugin", "title": "Init Web Plugin",
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -1,217 +0,0 @@
import { join, normalize } from '@angular-devkit/core';
import {
chain,
Rule,
Tree,
SchematicContext,
mergeWith,
apply,
template,
move,
url,
externalSchematic,
noop,
filter,
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import {
updateJsonInTree,
NxJson,
getNpmScope,
formatFiles,
updateWorkspaceInTree,
generateProjectLint,
addLintFiles,
} from '@nrwl/workspace';
import init from '../init/init';
import { appsDir } from '@nrwl/workspace/src/utils/ast-utils';
import { names, offsetFromRoot } from '@nrwl/devkit';
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
interface NormalizedSchema extends Schema {
projectName: string;
appProjectRoot: string;
e2eProjectName: string;
e2eProjectRoot: string;
parsedTags: string[];
}
function createApplicationFiles(options: NormalizedSchema): Rule {
return mergeWith(
apply(url(`./files/app`), [
template({
...options,
...names(options.name),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
}),
options.unitTestRunner === 'none'
? filter((file) => file !== '/src/app/app.spec.ts')
: noop(),
move(options.appProjectRoot),
])
);
}
function updateNxJson(options: NormalizedSchema): Rule {
return updateJsonInTree<NxJson>('nx.json', (json) => {
json.projects[options.projectName] = { tags: options.parsedTags };
return json;
});
}
function addProject(options: NormalizedSchema): Rule {
return updateWorkspaceInTree((json) => {
const architect: { [key: string]: any } = {};
architect.build = {
builder: '@nrwl/web:build',
outputs: ['{options.outputPath}'],
options: {
outputPath: join(normalize('dist'), options.appProjectRoot),
index: join(normalize(options.appProjectRoot), 'src/index.html'),
main: join(normalize(options.appProjectRoot), 'src/main.ts'),
polyfills: join(normalize(options.appProjectRoot), 'src/polyfills.ts'),
tsConfig: join(normalize(options.appProjectRoot), 'tsconfig.app.json'),
assets: [
join(normalize(options.appProjectRoot), 'src/favicon.ico'),
join(normalize(options.appProjectRoot), 'src/assets'),
],
styles: [
join(
normalize(options.appProjectRoot),
`src/styles.${options.style}`
),
],
scripts: [],
},
configurations: {
production: {
fileReplacements: [
{
replace: join(
normalize(options.appProjectRoot),
`src/environments/environment.ts`
),
with: join(
normalize(options.appProjectRoot),
`src/environments/environment.prod.ts`
),
},
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
budgets: [
{
type: 'initial',
maximumWarning: '2mb',
maximumError: '5mb',
},
],
},
},
};
architect.serve = {
builder: '@nrwl/web:dev-server',
options: {
buildTarget: `${options.projectName}:build`,
},
configurations: {
production: {
buildTarget: `${options.projectName}:build:production`,
},
},
};
architect.lint = generateProjectLint(
normalize(options.appProjectRoot),
join(normalize(options.appProjectRoot), 'tsconfig.app.json'),
options.linter,
[`${options.appProjectRoot}/**/*.ts`]
);
json.projects[options.projectName] = {
root: options.appProjectRoot,
sourceRoot: join(normalize(options.appProjectRoot), 'src'),
projectType: 'application',
architect,
};
json.defaultProject = json.defaultProject || options.projectName;
return json;
});
}
export default function (schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(host, schema);
return chain([
init({
...options,
skipFormat: true,
}),
addLintFiles(options.appProjectRoot, options.linter),
createApplicationFiles(options),
updateNxJson(options),
addProject(options),
options.e2eTestRunner === 'cypress'
? externalSchematic('@nrwl/cypress', 'cypress-project', {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName,
})
: noop(),
options.unitTestRunner === 'jest'
? externalSchematic('@nrwl/jest', 'jest-project', {
project: options.projectName,
skipSerializers: true,
setupFile: 'web-components',
babelJest: options.babelJest,
})
: noop(),
formatFiles(options),
])(host, context);
};
}
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const appDirectory = options.directory
? `${names(options.directory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
const e2eProjectName = `${appProjectName}-e2e`;
const appProjectRoot = `${appsDir(host)}/${appDirectory}`;
const e2eProjectRoot = `${appsDir(host)}/${appDirectory}-e2e`;
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
const defaultPrefix = getNpmScope(host);
return {
...options,
prefix: options.prefix ? options.prefix : defaultPrefix,
name: names(options.name).fileName,
projectName: appProjectName,
appProjectRoot,
e2eProjectRoot,
e2eProjectName,
parsedTags,
};
}
export const applicationGenerator = wrapAngularDevkitSchematic(
'@nrwl/web',
'application'
);

View File

@ -1,12 +0,0 @@
export interface Schema {
name: string;
prefix?: string;
style?: string;
skipFormat: boolean;
directory?: string;
tags?: string;
unitTestRunner: 'jest' | 'none';
e2eTestRunner: 'cypress' | 'none';
linter: Linter;
babelJest?: boolean;
}

View File

@ -1,93 +0,0 @@
import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { addDepsToPackageJson, readJsonInTree } from '@nrwl/workspace';
import { callRule, runSchematic } from '../../utils/testing';
import { nxVersion } from '../../utils/versions';
describe('init', () => {
let tree: Tree;
beforeEach(() => {
tree = Tree.empty();
tree = createEmptyWorkspace(tree);
});
it('should add web dependencies', async () => {
const existing = 'existing';
const existingVersion = '1.0.0';
await callRule(
addDepsToPackageJson(
{ '@nrwl/web': nxVersion, [existing]: existingVersion },
{ [existing]: existingVersion },
false
),
tree
);
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.devDependencies['@nrwl/web']).toBeDefined();
expect(packageJson.devDependencies[existing]).toBeDefined();
expect(packageJson.dependencies['@nrwl/web']).toBeUndefined();
expect(packageJson.dependencies['document-register-element']).toBeDefined();
expect(packageJson.dependencies[existing]).toBeDefined();
});
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
});
});
it('should not add jest config if unitTestRunner is none', async () => {
const result = await runSchematic(
'init',
{
unitTestRunner: 'none',
},
tree
);
expect(result.exists('jest.config.js')).toBe(false);
});
describe('babel config', () => {
it('should create babel config if not present', async () => {
const result = await runSchematic(
'init',
{
unitTestRunner: 'none',
},
tree
);
expect(result.exists('babel.config.json')).toBe(true);
});
it('should not overwrite existing babel config', async () => {
tree.create('babel.config.json', '{ "preset": ["preset-awesome"] }');
const result = await runSchematic(
'init',
{
unitTestRunner: 'none',
},
tree
);
const existing = result.read('babel.config.json').toString();
expect(existing).toMatch('{ "preset": ["preset-awesome"] }');
});
it('should not overwrite existing babel config (.js)', async () => {
tree.create('/babel.config.js', 'module.exports = () => {};');
const result = await runSchematic(
'init',
{
unitTestRunner: 'none',
},
tree
);
expect(result.exists('babel.config.json')).toBe(false);
});
});
});

View File

@ -1,45 +0,0 @@
import { chain, noop, Rule, Tree } from '@angular-devkit/schematics';
import {
addPackageWithInit,
formatFiles,
setDefaultCollection,
updateJsonInTree,
} from '@nrwl/workspace';
import { Schema } from './schema';
import {
documentRegisterElementVersion,
nxVersion,
} from '../../utils/versions';
import { initRootBabelConfig } from '../../utils/rules';
function updateDependencies(): Rule {
return updateJsonInTree('package.json', (json) => {
delete json.dependencies['@nrwl/web'];
json.dependencies = {
...json.dependencies,
'core-js': '^3.6.5',
'document-register-element': documentRegisterElementVersion,
tslib: '^2.0.0',
};
json.devDependencies = {
...json.devDependencies,
'@nrwl/web': nxVersion,
};
return json;
});
}
export default function (schema: Schema) {
return chain([
setDefaultCollection('@nrwl/web'),
schema.unitTestRunner === 'jest'
? addPackageWithInit('@nrwl/jest')
: noop(),
schema.e2eTestRunner === 'cypress'
? addPackageWithInit('@nrwl/cypress')
: noop(),
updateDependencies(),
initRootBabelConfig(),
formatFiles(schema),
]);
}

View File

@ -1,5 +0,0 @@
export interface Schema {
unitTestRunner: 'jest' | 'none';
e2eTestRunner: 'cypress' | 'none';
skipFormat: boolean;
}

View File

@ -24,7 +24,7 @@ export interface BuildBuilderOptions {
maxWorkers?: number; maxWorkers?: number;
poll?: number; poll?: number;
fileReplacements: FileReplacement[]; fileReplacements?: FileReplacement[];
assets?: any[]; assets?: any[];
progress?: boolean; progress?: boolean;

View File

@ -62,7 +62,9 @@ describe('new', () => {
}); });
describe('--packageManager', () => { describe('--packageManager', () => {
describe.each([['npm'], ['yarn'], ['pnpm']])('%s', (packageManager) => { describe.each([['npm'], ['yarn'], ['pnpm']])(
'%s',
(packageManager: 'npm' | 'yarn' | 'pnpm') => {
it('should set the packageManager in workspace.json', async () => { it('should set the packageManager in workspace.json', async () => {
await newGenerator(tree, { await newGenerator(tree, {
...defaultOptions, ...defaultOptions,
@ -77,7 +79,8 @@ describe('new', () => {
const workspaceJson = readJson(tree, 'my-workspace/angular.json'); const workspaceJson = readJson(tree, 'my-workspace/angular.json');
expect(workspaceJson.cli.packageManager).toEqual(packageManager); expect(workspaceJson.cli.packageManager).toEqual(packageManager);
}); });
}); }
);
}); });
it('should not modify any existing files', async () => { it('should not modify any existing files', async () => {

View File

@ -8,6 +8,9 @@ import {
convertNxGenerator, convertNxGenerator,
names, names,
getPackageManagerCommand, getPackageManagerCommand,
readWorkspaceConfiguration,
updateWorkspaceConfiguration,
WorkspaceConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { join } from 'path'; import { join } from 'path';
@ -45,7 +48,7 @@ export interface Schema {
commit?: { name: string; email: string; message?: string }; commit?: { name: string; email: string; message?: string };
defaultBase: string; defaultBase: string;
linter: 'tslint' | 'eslint'; linter: 'tslint' | 'eslint';
packageManager?: string; packageManager?: 'npm' | 'yarn' | 'pnpm';
} }
function generatePreset(host: Tree, opts: Schema) { function generatePreset(host: Tree, opts: Schema) {
@ -344,13 +347,17 @@ function setDefaultPackageManager(host: Tree, options: Schema) {
return; return;
} }
updateJson(host, getWorkspacePath(host, options), (json) => { updateJson<WorkspaceConfiguration>(
host,
getWorkspacePath(host, options),
(json) => {
if (!json.cli) { if (!json.cli) {
json.cli = {}; json.cli = {};
} }
json.cli['packageManager'] = options.packageManager; json.cli.packageManager = options.packageManager;
return json; return json;
}); }
);
} }
function setDefault( function setDefault(

View File

@ -1,4 +1,4 @@
import { Tree } from '@nrwl/devkit'; import { readJson, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { presetGenerator } from './preset'; import { presetGenerator } from './preset';
@ -9,9 +9,9 @@ describe('preset', () => {
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
}); });
describe('--preset', () => {
// TODO: reenable. This doesn't work because wrapAngularDevkit uses the fs // TODO: reenable. This doesn't work because wrapAngularDevkit uses the fs
xdescribe('--preset', () => { xdescribe('angular', () => {
describe('angular', () => {
it('should create files (preset = angular)', async () => { it('should create files (preset = angular)', async () => {
await presetGenerator(tree, { await presetGenerator(tree, {
name: 'proj', name: 'proj',
@ -29,6 +29,20 @@ describe('preset', () => {
).toBe('@nrwl/angular'); ).toBe('@nrwl/angular');
}); });
}); });
describe('web-components', () => {
it('should create files (preset = web-components)', async () => {
await presetGenerator(tree, {
name: 'proj',
preset: 'web-components',
cli: 'nx',
});
expect(tree.exists('/apps/proj/src/main.ts')).toBe(true);
expect(readJson(tree, '/workspace.json').cli.defaultCollection).toBe(
'@nrwl/web'
);
});
});
}); });
// it('should create files (preset = react)', async () => { // it('should create files (preset = react)', async () => {
@ -43,17 +57,6 @@ describe('preset', () => {
// ).toBe('@nrwl/react'); // ).toBe('@nrwl/react');
// }); // });
// //
// it('should create files (preset = web-components)', async () => {
// const tree = await runSchematic(
// 'preset',
// { name: 'proj', preset: 'web-components' },
// tree
// );
// expect(tree.exists('/apps/proj/src/main.ts')).toBe(true);
// expect(
// JSON.parse(tree.readContent('/workspace.json')).cli.defaultCollection
// ).toBe('@nrwl/web');
// });
// //
// it('should create files (preset = next)', async () => { // it('should create files (preset = next)', async () => {
// const tree = await runSchematic( // const tree = await runSchematic(

View File

@ -24,6 +24,7 @@
"@nrwl/workspace/*": ["./packages/workspace/*"], "@nrwl/workspace/*": ["./packages/workspace/*"],
"@nrwl/cli": ["./packages/cli"], "@nrwl/cli": ["./packages/cli"],
"@nrwl/cli/*": ["./packages/cli/*"], "@nrwl/cli/*": ["./packages/cli/*"],
"@nrwl/cypress": ["./packages/cypress"],
"@nrwl/express": ["./packages/express"], "@nrwl/express": ["./packages/express"],
"@nrwl/nest": ["./packages/nest"], "@nrwl/nest": ["./packages/nest"],
"@nrwl/next": ["./packages/next"], "@nrwl/next": ["./packages/next"],