parent
4315e91970
commit
c560b2bba6
@ -66,11 +66,6 @@
|
|||||||
"enum": ["eslint", "none"],
|
"enum": ["eslint", "none"],
|
||||||
"default": "eslint"
|
"default": "eslint"
|
||||||
},
|
},
|
||||||
"strict": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Creates an application with strict mode and strict type checking.",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"skipFormat": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files",
|
"description": "Skip formatting files",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -546,18 +546,6 @@ describe('app', () => {
|
|||||||
expect(tree.exists('my-app/.babelrc')).toBeFalsy();
|
expect(tree.exists('my-app/.babelrc')).toBeFalsy();
|
||||||
expect(tree.exists('my-app/.swcrc')).toBeTruthy();
|
expect(tree.exists('my-app/.swcrc')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be strict by default', async () => {
|
|
||||||
await applicationGenerator(tree, {
|
|
||||||
name: 'my-app',
|
|
||||||
compiler: 'swc',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
addPlugin: true,
|
|
||||||
} as Schema);
|
|
||||||
|
|
||||||
const tsconfig = readJson(tree, 'my-app/tsconfig.json');
|
|
||||||
expect(tsconfig.compilerOptions.strict).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setup web app with --bundler=vite', () => {
|
describe('setup web app with --bundler=vite', () => {
|
||||||
|
|||||||
@ -3,15 +3,29 @@ import {
|
|||||||
addProjectConfiguration,
|
addProjectConfiguration,
|
||||||
ensurePackage,
|
ensurePackage,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
|
generateFiles,
|
||||||
GeneratorCallback,
|
GeneratorCallback,
|
||||||
getPackageManagerCommand,
|
getPackageManagerCommand,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
|
names,
|
||||||
|
offsetFromRoot,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
|
TargetConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
|
updateNxJson,
|
||||||
|
updateProjectConfiguration,
|
||||||
writeJson,
|
writeJson,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import {
|
||||||
|
getRelativePathToRootTsConfig,
|
||||||
|
initGenerator as jsInitGenerator,
|
||||||
|
} from '@nx/js';
|
||||||
import { swcCoreVersion } from '@nx/js/src/utils/versions';
|
import { swcCoreVersion } from '@nx/js/src/utils/versions';
|
||||||
|
import type { Linter } from '@nx/eslint';
|
||||||
|
import { join } from 'path';
|
||||||
import {
|
import {
|
||||||
nxVersion,
|
nxVersion,
|
||||||
swcLoaderVersion,
|
swcLoaderVersion,
|
||||||
@ -20,13 +34,201 @@ import {
|
|||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { webInitGenerator } from '../init/init';
|
import { webInitGenerator } from '../init/init';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
||||||
|
import { hasWebpackPlugin } from '../../utils/has-webpack-plugin';
|
||||||
|
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/add-build-target-defaults';
|
||||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||||
import { addProject } from './lib/add-project';
|
import { VitePluginOptions } from '@nx/vite/src/plugins/plugin';
|
||||||
import { setupBundler } from './lib/setup-bundler';
|
import { WebpackPluginOptions } from '@nx/webpack/src/plugins/plugin';
|
||||||
import { createApplicationFiles } from './lib/create-application-files';
|
|
||||||
import { setDefaults } from './lib/set-defaults';
|
interface NormalizedSchema extends Schema {
|
||||||
import { normalizeOptions } from './lib/normalize-options';
|
projectName: string;
|
||||||
|
appProjectRoot: string;
|
||||||
|
e2eProjectName: string;
|
||||||
|
e2eProjectRoot: string;
|
||||||
|
e2eWebServerAddress: string;
|
||||||
|
e2eWebServerTarget: string;
|
||||||
|
e2ePort: number;
|
||||||
|
parsedTags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
|
||||||
|
if (options.bundler === 'vite') {
|
||||||
|
generateFiles(
|
||||||
|
tree,
|
||||||
|
join(__dirname, './files/app-vite'),
|
||||||
|
options.appProjectRoot,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
...names(options.name),
|
||||||
|
tmpl: '',
|
||||||
|
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||||
|
rootTsConfigPath: getRelativePathToRootTsConfig(
|
||||||
|
tree,
|
||||||
|
options.appProjectRoot
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
generateFiles(
|
||||||
|
tree,
|
||||||
|
join(__dirname, './files/app-webpack'),
|
||||||
|
options.appProjectRoot,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
...names(options.name),
|
||||||
|
tmpl: '',
|
||||||
|
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||||
|
rootTsConfigPath: getRelativePathToRootTsConfig(
|
||||||
|
tree,
|
||||||
|
options.appProjectRoot
|
||||||
|
),
|
||||||
|
webpackPluginOptions: hasWebpackPlugin(tree)
|
||||||
|
? {
|
||||||
|
compiler: options.compiler,
|
||||||
|
target: 'web',
|
||||||
|
outputPath: joinPathFragments(
|
||||||
|
'dist',
|
||||||
|
options.appProjectRoot != '.'
|
||||||
|
? options.appProjectRoot
|
||||||
|
: options.projectName
|
||||||
|
),
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.ts',
|
||||||
|
assets: ['./src/favicon.ico', './src/assets'],
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
styles: [`./src/styles.${options.style}`],
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (options.unitTestRunner === 'none') {
|
||||||
|
tree.delete(
|
||||||
|
join(options.appProjectRoot, './src/app/app.element.spec.ts')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupBundler(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const main = joinPathFragments(options.appProjectRoot, 'src/main.ts');
|
||||||
|
const tsConfig = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'tsconfig.app.json'
|
||||||
|
);
|
||||||
|
const assets = [
|
||||||
|
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
|
||||||
|
joinPathFragments(options.appProjectRoot, 'src/assets'),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (options.bundler === 'webpack') {
|
||||||
|
const { configurationGenerator } = ensurePackage<
|
||||||
|
typeof import('@nx/webpack')
|
||||||
|
>('@nx/webpack', nxVersion);
|
||||||
|
await configurationGenerator(tree, {
|
||||||
|
target: 'web',
|
||||||
|
project: options.projectName,
|
||||||
|
main,
|
||||||
|
tsConfig,
|
||||||
|
compiler: options.compiler ?? 'babel',
|
||||||
|
devServer: true,
|
||||||
|
webpackConfig: joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'webpack.config.js'
|
||||||
|
),
|
||||||
|
skipFormat: true,
|
||||||
|
addPlugin: options.addPlugin,
|
||||||
|
});
|
||||||
|
const project = readProjectConfiguration(tree, options.projectName);
|
||||||
|
if (project.targets.build) {
|
||||||
|
const prodConfig = project.targets.build.configurations.production;
|
||||||
|
const buildOptions = project.targets.build.options;
|
||||||
|
buildOptions.assets = assets;
|
||||||
|
buildOptions.index = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'src/index.html'
|
||||||
|
);
|
||||||
|
buildOptions.baseHref = '/';
|
||||||
|
buildOptions.styles = [
|
||||||
|
joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
`src/styles.${options.style}`
|
||||||
|
),
|
||||||
|
];
|
||||||
|
// We can delete that, because this projest is an application
|
||||||
|
// and applications have a .babelrc file in their root dir.
|
||||||
|
// So Nx will find it and use it
|
||||||
|
delete buildOptions.babelUpwardRootMode;
|
||||||
|
buildOptions.scripts = [];
|
||||||
|
prodConfig.fileReplacements = [
|
||||||
|
{
|
||||||
|
replace: joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
`src/environments/environment.ts`
|
||||||
|
),
|
||||||
|
with: joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
`src/environments/environment.prod.ts`
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
prodConfig.optimization = true;
|
||||||
|
prodConfig.outputHashing = 'all';
|
||||||
|
prodConfig.sourceMap = false;
|
||||||
|
prodConfig.namedChunks = false;
|
||||||
|
prodConfig.extractLicenses = true;
|
||||||
|
prodConfig.vendorChunk = false;
|
||||||
|
updateProjectConfiguration(tree, options.projectName, project);
|
||||||
|
}
|
||||||
|
// TODO(jack): Flush this out... no bundler should be possible for web but the experience isn't holistic due to missing features (e.g. writing index.html).
|
||||||
|
} else if (options.bundler === 'none') {
|
||||||
|
const project = readProjectConfiguration(tree, options.projectName);
|
||||||
|
addBuildTargetDefaults(tree, `@nx/js:${options.compiler}`);
|
||||||
|
project.targets.build = {
|
||||||
|
executor: `@nx/js:${options.compiler}`,
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
options: {
|
||||||
|
main,
|
||||||
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
|
tsConfig,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateProjectConfiguration(tree, options.projectName, project);
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported bundler type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addProject(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const targets: Record<string, TargetConfiguration> = {};
|
||||||
|
|
||||||
|
addProjectConfiguration(
|
||||||
|
tree,
|
||||||
|
options.projectName,
|
||||||
|
{
|
||||||
|
projectType: 'application',
|
||||||
|
root: options.appProjectRoot,
|
||||||
|
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
|
||||||
|
tags: options.parsedTags,
|
||||||
|
targets,
|
||||||
|
},
|
||||||
|
options.standaloneConfig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDefaults(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.generators = nxJson.generators || {};
|
||||||
|
nxJson.generators['@nx/web:application'] = {
|
||||||
|
style: options.style,
|
||||||
|
linter: options.linter,
|
||||||
|
unitTestRunner: options.unitTestRunner,
|
||||||
|
e2eTestRunner: options.e2eTestRunner,
|
||||||
|
...nxJson.generators['@nx/web:application'],
|
||||||
|
};
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
}
|
||||||
|
|
||||||
export async function applicationGenerator(host: Tree, schema: Schema) {
|
export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||||
return await applicationGeneratorInternal(host, {
|
return await applicationGeneratorInternal(host, {
|
||||||
@ -255,4 +457,91 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
return runTasksInSerial(...tasks);
|
return runTasksInSerial(...tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function normalizeOptions(
|
||||||
|
host: Tree,
|
||||||
|
options: Schema
|
||||||
|
): Promise<NormalizedSchema> {
|
||||||
|
const {
|
||||||
|
projectName: appProjectName,
|
||||||
|
projectRoot: appProjectRoot,
|
||||||
|
projectNameAndRootFormat,
|
||||||
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
|
name: options.name,
|
||||||
|
projectType: 'application',
|
||||||
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
callingGenerator: '@nx/web:application',
|
||||||
|
});
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
const nxJson = readNxJson(host);
|
||||||
|
const addPluginDefault =
|
||||||
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||||
|
nxJson.useInferencePlugins !== false;
|
||||||
|
options.addPlugin ??= addPluginDefault;
|
||||||
|
|
||||||
|
let e2eWebServerTarget = 'serve';
|
||||||
|
if (options.addPlugin) {
|
||||||
|
if (nxJson.plugins) {
|
||||||
|
for (const plugin of nxJson.plugins) {
|
||||||
|
if (
|
||||||
|
options.bundler === 'vite' &&
|
||||||
|
typeof plugin === 'object' &&
|
||||||
|
plugin.plugin === '@nx/vite/plugin' &&
|
||||||
|
(plugin.options as VitePluginOptions).serveTargetName
|
||||||
|
) {
|
||||||
|
e2eWebServerTarget = (plugin.options as VitePluginOptions)
|
||||||
|
.serveTargetName;
|
||||||
|
} else if (
|
||||||
|
options.bundler === 'webpack' &&
|
||||||
|
typeof plugin === 'object' &&
|
||||||
|
plugin.plugin === '@nx/webpack/plugin' &&
|
||||||
|
(plugin.options as WebpackPluginOptions).serveTargetName
|
||||||
|
) {
|
||||||
|
e2eWebServerTarget = (plugin.options as WebpackPluginOptions)
|
||||||
|
.serveTargetName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let e2ePort = 4200;
|
||||||
|
if (
|
||||||
|
nxJson.targetDefaults?.[e2eWebServerTarget] &&
|
||||||
|
nxJson.targetDefaults?.[e2eWebServerTarget].options?.port
|
||||||
|
) {
|
||||||
|
e2ePort = nxJson.targetDefaults?.[e2eWebServerTarget].options?.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
const e2eProjectName = `${appProjectName}-e2e`;
|
||||||
|
const e2eProjectRoot = `${appProjectRoot}-e2e`;
|
||||||
|
const e2eWebServerAddress = `http://localhost:${e2ePort}`;
|
||||||
|
|
||||||
|
const npmScope = getNpmScope(host);
|
||||||
|
|
||||||
|
const parsedTags = options.tags
|
||||||
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
|
: [];
|
||||||
|
|
||||||
|
options.style = options.style || 'css';
|
||||||
|
options.linter = options.linter || ('eslint' as Linter.EsLint);
|
||||||
|
options.unitTestRunner = options.unitTestRunner || 'jest';
|
||||||
|
options.e2eTestRunner = options.e2eTestRunner || 'playwright';
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
prefix: options.prefix ?? npmScope ?? 'app',
|
||||||
|
name: names(options.name).fileName,
|
||||||
|
compiler: options.compiler ?? 'babel',
|
||||||
|
bundler: options.bundler ?? 'webpack',
|
||||||
|
projectName: appProjectName,
|
||||||
|
appProjectRoot,
|
||||||
|
e2eProjectRoot,
|
||||||
|
e2eProjectName,
|
||||||
|
e2eWebServerAddress,
|
||||||
|
e2eWebServerTarget,
|
||||||
|
e2ePort,
|
||||||
|
parsedTags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default applicationGenerator;
|
export default applicationGenerator;
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
TargetConfiguration,
|
|
||||||
Tree,
|
|
||||||
addProjectConfiguration,
|
|
||||||
joinPathFragments,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { NormalizedSchema } from '../schema';
|
|
||||||
|
|
||||||
export async function addProject(tree: Tree, options: NormalizedSchema) {
|
|
||||||
const targets: Record<string, TargetConfiguration> = {};
|
|
||||||
|
|
||||||
addProjectConfiguration(
|
|
||||||
tree,
|
|
||||||
options.projectName,
|
|
||||||
{
|
|
||||||
projectType: 'application',
|
|
||||||
root: options.appProjectRoot,
|
|
||||||
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
|
|
||||||
tags: options.parsedTags,
|
|
||||||
targets,
|
|
||||||
},
|
|
||||||
options.standaloneConfig
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import { join } from 'path';
|
|
||||||
import {
|
|
||||||
Tree,
|
|
||||||
generateFiles,
|
|
||||||
names,
|
|
||||||
offsetFromRoot,
|
|
||||||
joinPathFragments,
|
|
||||||
updateJson,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { getRelativePathToRootTsConfig } from '@nx/js';
|
|
||||||
import { NormalizedSchema } from '../schema';
|
|
||||||
import { hasWebpackPlugin } from '../../../utils/has-webpack-plugin';
|
|
||||||
|
|
||||||
export function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
|
|
||||||
if (options.bundler === 'vite') {
|
|
||||||
generateFiles(
|
|
||||||
tree,
|
|
||||||
join(__dirname, '../files/app-vite'),
|
|
||||||
options.appProjectRoot,
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
...names(options.name),
|
|
||||||
tmpl: '',
|
|
||||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
|
||||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
|
||||||
tree,
|
|
||||||
options.appProjectRoot
|
|
||||||
),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
generateFiles(
|
|
||||||
tree,
|
|
||||||
join(__dirname, '../files/app-webpack'),
|
|
||||||
options.appProjectRoot,
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
...names(options.name),
|
|
||||||
tmpl: '',
|
|
||||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
|
||||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
|
||||||
tree,
|
|
||||||
options.appProjectRoot
|
|
||||||
),
|
|
||||||
webpackPluginOptions: hasWebpackPlugin(tree)
|
|
||||||
? {
|
|
||||||
compiler: options.compiler,
|
|
||||||
target: 'web',
|
|
||||||
outputPath: joinPathFragments(
|
|
||||||
'dist',
|
|
||||||
options.appProjectRoot != '.'
|
|
||||||
? options.appProjectRoot
|
|
||||||
: options.projectName
|
|
||||||
),
|
|
||||||
tsConfig: './tsconfig.app.json',
|
|
||||||
main: './src/main.ts',
|
|
||||||
assets: ['./src/favicon.ico', './src/assets'],
|
|
||||||
index: './src/index.html',
|
|
||||||
baseHref: '/',
|
|
||||||
styles: [`./src/styles.${options.style}`],
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (options.unitTestRunner === 'none') {
|
|
||||||
tree.delete(
|
|
||||||
join(options.appProjectRoot, './src/app/app.element.spec.ts')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateJson(
|
|
||||||
tree,
|
|
||||||
joinPathFragments(options.appProjectRoot, 'tsconfig.json'),
|
|
||||||
(json) => {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
compilerOptions: {
|
|
||||||
...(json.compilerOptions || {}),
|
|
||||||
strict: options.strict,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
import { Tree, names, readNxJson } from '@nx/devkit';
|
|
||||||
import { Schema, NormalizedSchema } from '../schema';
|
|
||||||
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
|
||||||
import { VitePluginOptions } from '@nx/vite/src/plugins/plugin';
|
|
||||||
import { WebpackPluginOptions } from '@nx/webpack/src/plugins/plugin';
|
|
||||||
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
|
||||||
import type { Linter } from '@nx/eslint';
|
|
||||||
|
|
||||||
export async function normalizeOptions(
|
|
||||||
host: Tree,
|
|
||||||
options: Schema
|
|
||||||
): Promise<NormalizedSchema> {
|
|
||||||
const {
|
|
||||||
projectName: appProjectName,
|
|
||||||
projectRoot: appProjectRoot,
|
|
||||||
projectNameAndRootFormat,
|
|
||||||
} = await determineProjectNameAndRootOptions(host, {
|
|
||||||
name: options.name,
|
|
||||||
projectType: 'application',
|
|
||||||
directory: options.directory,
|
|
||||||
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
|
||||||
callingGenerator: '@nx/web:application',
|
|
||||||
});
|
|
||||||
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
|
||||||
const nxJson = readNxJson(host);
|
|
||||||
const addPluginDefault =
|
|
||||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
||||||
nxJson.useInferencePlugins !== false;
|
|
||||||
options.addPlugin ??= addPluginDefault;
|
|
||||||
|
|
||||||
let e2eWebServerTarget = 'serve';
|
|
||||||
if (options.addPlugin) {
|
|
||||||
if (nxJson.plugins) {
|
|
||||||
for (const plugin of nxJson.plugins) {
|
|
||||||
if (
|
|
||||||
options.bundler === 'vite' &&
|
|
||||||
typeof plugin === 'object' &&
|
|
||||||
plugin.plugin === '@nx/vite/plugin' &&
|
|
||||||
(plugin.options as VitePluginOptions).serveTargetName
|
|
||||||
) {
|
|
||||||
e2eWebServerTarget = (plugin.options as VitePluginOptions)
|
|
||||||
.serveTargetName;
|
|
||||||
} else if (
|
|
||||||
options.bundler === 'webpack' &&
|
|
||||||
typeof plugin === 'object' &&
|
|
||||||
plugin.plugin === '@nx/webpack/plugin' &&
|
|
||||||
(plugin.options as WebpackPluginOptions).serveTargetName
|
|
||||||
) {
|
|
||||||
e2eWebServerTarget = (plugin.options as WebpackPluginOptions)
|
|
||||||
.serveTargetName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let e2ePort = 4200;
|
|
||||||
if (
|
|
||||||
nxJson.targetDefaults?.[e2eWebServerTarget] &&
|
|
||||||
nxJson.targetDefaults?.[e2eWebServerTarget].options?.port
|
|
||||||
) {
|
|
||||||
e2ePort = nxJson.targetDefaults?.[e2eWebServerTarget].options?.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
const e2eProjectName = `${appProjectName}-e2e`;
|
|
||||||
const e2eProjectRoot = `${appProjectRoot}-e2e`;
|
|
||||||
const e2eWebServerAddress = `http://localhost:${e2ePort}`;
|
|
||||||
|
|
||||||
const npmScope = getNpmScope(host);
|
|
||||||
|
|
||||||
const parsedTags = options.tags
|
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
|
||||||
: [];
|
|
||||||
|
|
||||||
options.style = options.style || 'css';
|
|
||||||
options.linter = options.linter || ('eslint' as Linter.EsLint);
|
|
||||||
options.unitTestRunner = options.unitTestRunner || 'jest';
|
|
||||||
options.e2eTestRunner = options.e2eTestRunner || 'playwright';
|
|
||||||
|
|
||||||
return {
|
|
||||||
...options,
|
|
||||||
prefix: options.prefix ?? npmScope ?? 'app',
|
|
||||||
name: names(options.name).fileName,
|
|
||||||
compiler: options.compiler ?? 'babel',
|
|
||||||
bundler: options.bundler ?? 'webpack',
|
|
||||||
strict: options.strict ?? true,
|
|
||||||
projectName: appProjectName,
|
|
||||||
appProjectRoot,
|
|
||||||
e2eProjectRoot,
|
|
||||||
e2eProjectName,
|
|
||||||
e2eWebServerAddress,
|
|
||||||
e2eWebServerTarget,
|
|
||||||
e2ePort,
|
|
||||||
parsedTags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
|
|
||||||
import { NormalizedSchema } from '../schema';
|
|
||||||
|
|
||||||
export function setDefaults(tree: Tree, options: NormalizedSchema) {
|
|
||||||
const nxJson = readNxJson(tree);
|
|
||||||
nxJson.generators = nxJson.generators || {};
|
|
||||||
nxJson.generators['@nx/web:application'] = {
|
|
||||||
style: options.style,
|
|
||||||
linter: options.linter,
|
|
||||||
unitTestRunner: options.unitTestRunner,
|
|
||||||
e2eTestRunner: options.e2eTestRunner,
|
|
||||||
...nxJson.generators['@nx/web:application'],
|
|
||||||
};
|
|
||||||
updateNxJson(tree, nxJson);
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
import {
|
|
||||||
Tree,
|
|
||||||
ensurePackage,
|
|
||||||
joinPathFragments,
|
|
||||||
readProjectConfiguration,
|
|
||||||
updateProjectConfiguration,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { NormalizedSchema } from '../schema';
|
|
||||||
import { nxVersion } from '../../../utils/versions';
|
|
||||||
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/add-build-target-defaults';
|
|
||||||
|
|
||||||
export async function setupBundler(tree: Tree, options: NormalizedSchema) {
|
|
||||||
const main = joinPathFragments(options.appProjectRoot, 'src/main.ts');
|
|
||||||
const tsConfig = joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
'tsconfig.app.json'
|
|
||||||
);
|
|
||||||
const assets = [
|
|
||||||
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
|
|
||||||
joinPathFragments(options.appProjectRoot, 'src/assets'),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (options.bundler === 'webpack') {
|
|
||||||
const { configurationGenerator } = ensurePackage<
|
|
||||||
typeof import('@nx/webpack')
|
|
||||||
>('@nx/webpack', nxVersion);
|
|
||||||
await configurationGenerator(tree, {
|
|
||||||
target: 'web',
|
|
||||||
project: options.projectName,
|
|
||||||
main,
|
|
||||||
tsConfig,
|
|
||||||
compiler: options.compiler ?? 'babel',
|
|
||||||
devServer: true,
|
|
||||||
webpackConfig: joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
'webpack.config.js'
|
|
||||||
),
|
|
||||||
skipFormat: true,
|
|
||||||
addPlugin: options.addPlugin,
|
|
||||||
});
|
|
||||||
const project = readProjectConfiguration(tree, options.projectName);
|
|
||||||
if (project.targets.build) {
|
|
||||||
const prodConfig = project.targets.build.configurations.production;
|
|
||||||
const buildOptions = project.targets.build.options;
|
|
||||||
buildOptions.assets = assets;
|
|
||||||
buildOptions.index = joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
'src/index.html'
|
|
||||||
);
|
|
||||||
buildOptions.baseHref = '/';
|
|
||||||
buildOptions.styles = [
|
|
||||||
joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
`src/styles.${options.style}`
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// We can delete that, because this projest is an application
|
|
||||||
// and applications have a .babelrc file in their root dir.
|
|
||||||
// So Nx will find it and use it
|
|
||||||
delete buildOptions.babelUpwardRootMode;
|
|
||||||
buildOptions.scripts = [];
|
|
||||||
prodConfig.fileReplacements = [
|
|
||||||
{
|
|
||||||
replace: joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
`src/environments/environment.ts`
|
|
||||||
),
|
|
||||||
with: joinPathFragments(
|
|
||||||
options.appProjectRoot,
|
|
||||||
`src/environments/environment.prod.ts`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
prodConfig.optimization = true;
|
|
||||||
prodConfig.outputHashing = 'all';
|
|
||||||
prodConfig.sourceMap = false;
|
|
||||||
prodConfig.namedChunks = false;
|
|
||||||
prodConfig.extractLicenses = true;
|
|
||||||
prodConfig.vendorChunk = false;
|
|
||||||
updateProjectConfiguration(tree, options.projectName, project);
|
|
||||||
}
|
|
||||||
// TODO(jack): Flush this out... no bundler should be possible for web but the experience isn't holistic due to missing features (e.g. writing index.html).
|
|
||||||
} else if (options.bundler === 'none') {
|
|
||||||
const project = readProjectConfiguration(tree, options.projectName);
|
|
||||||
addBuildTargetDefaults(tree, `@nx/js:${options.compiler}`);
|
|
||||||
project.targets.build = {
|
|
||||||
executor: `@nx/js:${options.compiler}`,
|
|
||||||
outputs: ['{options.outputPath}'],
|
|
||||||
options: {
|
|
||||||
main,
|
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
|
||||||
tsConfig,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
updateProjectConfiguration(tree, options.projectName, project);
|
|
||||||
} else {
|
|
||||||
throw new Error('Unsupported bundler type');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,17 +17,5 @@ export interface Schema {
|
|||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
standaloneConfig?: boolean;
|
standaloneConfig?: boolean;
|
||||||
setParserOptionsProject?: boolean;
|
setParserOptionsProject?: boolean;
|
||||||
strict?: boolean;
|
|
||||||
addPlugin?: boolean;
|
addPlugin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
|
||||||
projectName: string;
|
|
||||||
appProjectRoot: string;
|
|
||||||
e2eProjectName: string;
|
|
||||||
e2eProjectRoot: string;
|
|
||||||
e2eWebServerAddress: string;
|
|
||||||
e2eWebServerTarget: string;
|
|
||||||
e2ePort: number;
|
|
||||||
parsedTags: string[];
|
|
||||||
}
|
|
||||||
|
|||||||
@ -69,11 +69,6 @@
|
|||||||
"enum": ["eslint", "none"],
|
"enum": ["eslint", "none"],
|
||||||
"default": "eslint"
|
"default": "eslint"
|
||||||
},
|
},
|
||||||
"strict": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Creates an application with strict mode and strict type checking.",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"skipFormat": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files",
|
"description": "Skip formatting files",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user