Compare commits

...

14 Commits

Author SHA1 Message Date
FrozenPandaz f997744b4d chore(misc): publish 15.8.2 2023-03-02 11:51:39 -05:00
Jack Hsu 04a38649b3 fix(core): mark package.json dependencies as static (#15381)
(cherry picked from commit aaed63ba18)
2023-03-02 10:21:35 -05:00
Jason Jean c351b34f62 fix(js): fix parsing link dependencies in yarn (#15364)
(cherry picked from commit 65aac8ea17)
2023-03-02 10:21:33 -05:00
Jason Jean f111580f28 fix(core): handle file renames properly for affected (#15340)
(cherry picked from commit 7aac67c783)
2023-03-02 10:21:28 -05:00
Craigory Coppola bf9844ec72 fix(core): encapsulated nx repos should invoke wrapper when invoked via npx or global install (#15337)
(cherry picked from commit c9244d2536)
2023-03-02 10:21:26 -05:00
Miroslav Jonaš 6c86cb770c fix(linter): ensure ast file detection supports tsx files (#15345)
(cherry picked from commit e767fa4e2a)
2023-03-02 10:21:20 -05:00
Jack Hsu c06edd9426 fix(bundling): use a range rather than ^ for vitest peer dependency … (#15362)
(cherry picked from commit b7ef261182)
2023-03-02 10:21:17 -05:00
Benjamin Cabanes 1c7406379b chore(core): fix browserslist typo (#15309)
(cherry picked from commit 872dcc8547)
2023-03-02 10:21:07 -05:00
Katerina Skroumpelou f1de248ff7 fix(vite): root for running the builder (#15359)
(cherry picked from commit 3d4e542823)
2023-03-02 10:20:55 -05:00
Chau Tran 5ba8d5aa12 fix(js): handle swcrc path mappings if specified (#15328)
(cherry picked from commit c981ac2e0d)
2023-03-02 10:20:52 -05:00
Nicholas Cunningham 18ff2dbcd4 fix(node): lint generator aware that app is root (#15355)
(cherry picked from commit b40c9fa88f)
2023-03-02 10:20:44 -05:00
Jonathan Cammisuli e9a59d60af fix(core): inline source maps for local nx plugins (#15336)
(cherry picked from commit b87f838e9a)
2023-03-02 10:20:39 -05:00
Caleb Ukle 908cf41d74 fix(testing): only install dependency for the desired --testEnvironment (#15244)
(cherry picked from commit 2d2804b0c1)
2023-03-02 10:20:27 -05:00
Caleb Ukle 2f8ca66797 fix(testing): walk all project deps to check if CT project is used in buildTarget (#15252)
(cherry picked from commit e8f19d8cb0)
2023-03-02 10:20:24 -05:00
38 changed files with 552 additions and 230 deletions
@@ -21,6 +21,13 @@
"description": "Do not add dependencies to `package.json`.",
"x-priority": "internal"
},
"testEnvironment": {
"type": "string",
"enum": ["jsdom", "node", "none"],
"description": "The test environment for jest. This controls which jest-environment-* package is installed",
"default": "jsdom",
"x-priority": "important"
},
"js": {
"type": "boolean",
"default": false,
@@ -41,7 +41,7 @@
},
"testEnvironment": {
"type": "string",
"enum": ["jsdom", "node"],
"enum": ["jsdom", "node", "none"],
"description": "The test environment for jest.",
"default": "jsdom",
"x-priority": "important"
+1 -1
View File
@@ -42,7 +42,7 @@ Sometimes broad configurations like `> 0.5%, not IE 11` can lead to surprising r
To see what browsers your configuration is supporting, run `npx browserslist` in the application's directory to get an output of browsers and versions to support.
```{% command="npx browserlist" %}
```{% command="npx browserslist" %}
and_chr 61
chrome 83
edge 83
@@ -8,6 +8,7 @@ import {
uniq,
updateFile,
updateProjectConfig,
removeFile,
} from '../../utils';
import { names } from '@nrwl/devkit';
@@ -19,154 +20,15 @@ describe('Angular Cypress Component Tests', () => {
beforeAll(async () => {
projectName = newProject({ name: uniq('cy-ng') });
runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`);
runCLI(
`generate @nrwl/angular:component fancy-component --project=${appName} --no-interactive`
);
runCLI(`generate @nrwl/angular:lib ${usedInAppLibName} --no-interactive`);
runCLI(
`generate @nrwl/angular:component btn --project=${usedInAppLibName} --inlineTemplate --inlineStyle --export --no-interactive`
);
runCLI(
`generate @nrwl/angular:component btn-standalone --project=${usedInAppLibName} --inlineTemplate --inlineStyle --export --standalone --no-interactive`
);
updateFile(
`libs/${usedInAppLibName}/src/lib/btn/btn.component.ts`,
`
import { Component, Input } from '@angular/core';
@Component({
selector: '${projectName}-btn',
template: '<button class="text-green-500">{{text}}</button>',
styles: []
})
export class BtnComponent {
@Input() text = 'something';
}
`
);
updateFile(
`libs/${usedInAppLibName}/src/lib/btn-standalone/btn-standalone.component.ts`,
`
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: '${projectName}-btn-standalone',
standalone: true,
imports: [CommonModule],
template: '<button class="text-green-500">standlone-{{text}}</button>',
styles: [],
})
export class BtnStandaloneComponent {
@Input() text = 'something';
}
`
);
// use lib in the app
createFile(
`apps/${appName}/src/app/app.component.html`,
`
<${projectName}-btn></${projectName}-btn>
<${projectName}-btn-standalone></${projectName}-btn-standalone>
<${projectName}-nx-welcome></${projectName}-nx-welcome>
`
);
const btnModuleName = names(usedInAppLibName).className;
updateFile(
`apps/${appName}/src/app/app.component.scss`,
`
@use 'styleguide' as *;
createApp(appName);
h1 {
@include headline;
}`
);
updateFile(
`apps/${appName}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {${btnModuleName}Module} from "@${projectName}/${usedInAppLibName}";
createLib(projectName, appName, usedInAppLibName);
useLibInApp(projectName, appName, usedInAppLibName);
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
createBuildableLib(projectName, buildableLibName);
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
imports: [BrowserModule, ${btnModuleName}Module],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
`
);
runCLI(
`generate @nrwl/angular:lib ${buildableLibName} --buildable --no-interactive`
);
runCLI(
`generate @nrwl/angular:component input --project=${buildableLibName} --inlineTemplate --inlineStyle --export --no-interactive`
);
runCLI(
`generate @nrwl/angular:component input-standalone --project=${buildableLibName} --inlineTemplate --inlineStyle --export --standalone --no-interactive`
);
updateFile(
`libs/${buildableLibName}/src/lib/input/input.component.ts`,
`
import {Component, Input} from '@angular/core';
@Component({
selector: '${projectName}-input',
template: \`<label class="text-green-500">Email: <input class="border-blue-500" type="email" [readOnly]="readOnly"></label>\`,
styles : []
})
export class InputComponent{
@Input() readOnly = false;
}
`
);
updateFile(
`libs/${buildableLibName}/src/lib/input-standalone/input-standalone.component.ts`,
`
import {Component, Input} from '@angular/core';
import {CommonModule} from '@angular/common';
@Component({
selector: '${projectName}-input-standalone',
standalone: true,
imports: [CommonModule],
template: \`<label class="text-green-500">Email: <input class="border-blue-500" type="email" [readOnly]="readOnly"></label>\`,
styles : []
})
export class InputStandaloneComponent{
@Input() readOnly = false;
}
`
);
// make sure assets from the workspace root work.
createFile('libs/assets/data.json', JSON.stringify({ data: 'data' }));
createFile(
'assets/styles/styleguide.scss',
`
@mixin headline {
font-weight: bold;
color: darkkhaki;
background: lightcoral;
font-weight: 24px;
}
`
);
updateProjectConfig(appName, (config) => {
config.targets['build'].options.stylePreprocessorOptions = {
includePaths: ['assets/styles'],
};
config.targets['build'].options.assets.push({
glob: '**/*',
input: 'libs/assets',
output: 'assets',
});
return config;
});
useWorkspaceAssetsInApp(appName);
});
afterAll(() => cleanupProject());
@@ -200,9 +62,226 @@ import {CommonModule} from '@angular/common';
`generate @nrwl/angular:cypress-component-configuration --project=${buildableLibName} --generate-tests --no-interactive`
);
}).toThrow();
createFile(
updateTestToAssertTailwindIsNotApplied(buildableLibName);
runCLI(
`generate @nrwl/angular:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build --no-interactive`
);
if (runCypressTests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!'
);
}
// add tailwind
runCLI(
`generate @nrwl/angular:setup-tailwind --project=${buildableLibName}`
);
updateFile(
`libs/${buildableLibName}/src/lib/input/input.component.cy.ts`,
`
(content) => {
// text-green-500 should now apply
return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)');
}
);
updateFile(
`libs/${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
(content) => {
// text-green-500 should now apply
return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)');
}
);
if (runCypressTests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!'
);
checkFilesDoNotExist(`tmp/libs/${buildableLibName}/ct-styles.css`);
}
}, 300_000);
it('should test lib with implicit dep on buildTarget', () => {
// creates graph like buildableLib -> lib -> app
// updates the apps styles and they should apply to the buildableLib
// even though app is not directly connected to buildableLib
useBuildableLibInLib(projectName, buildableLibName, usedInAppLibName);
updateBuilableLibTestsToAssertAppStyles(appName, buildableLibName);
if (runCypressTests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!'
);
}
});
});
function createApp(appName: string) {
runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`);
runCLI(
`generate @nrwl/angular:component fancy-component --project=${appName} --no-interactive`
);
}
function createLib(projectName: string, appName: string, libName: string) {
runCLI(`generate @nrwl/angular:lib ${libName} --no-interactive`);
runCLI(
`generate @nrwl/angular:component btn --project=${libName} --inlineTemplate --inlineStyle --export --no-interactive`
);
runCLI(
`generate @nrwl/angular:component btn-standalone --project=${libName} --inlineTemplate --inlineStyle --export --standalone --no-interactive`
);
updateFile(
`libs/${libName}/src/lib/btn/btn.component.ts`,
`
import { Component, Input } from '@angular/core';
@Component({
selector: '${projectName}-btn',
template: '<button class="text-green-500">{{text}}</button>',
styles: []
})
export class BtnComponent {
@Input() text = 'something';
}
`
);
updateFile(
`libs/${libName}/src/lib/btn-standalone/btn-standalone.component.ts`,
`
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: '${projectName}-btn-standalone',
standalone: true,
imports: [CommonModule],
template: '<button class="text-green-500">standlone-{{text}}</button>',
styles: [],
})
export class BtnStandaloneComponent {
@Input() text = 'something';
}
`
);
}
function createBuildableLib(projectName: string, libName: string) {
// create lib
runCLI(`generate @nrwl/angular:lib ${libName} --buildable --no-interactive`);
// create cmp for lib
runCLI(
`generate @nrwl/angular:component input --project=${libName} --inlineTemplate --inlineStyle --export --no-interactive`
);
// create standlone cmp for lib
runCLI(
`generate @nrwl/angular:component input-standalone --project=${libName} --inlineTemplate --inlineStyle --export --standalone --no-interactive`
);
// update cmp implmentation to use tailwind clasasserting in tests
updateFile(
`libs/${libName}/src/lib/input/input.component.ts`,
`
import {Component, Input} from '@angular/core';
@Component({
selector: '${projectName}-input',
template: \`<label class="text-green-500">Email: <input class="border-blue-500" type="email" [readOnly]="readOnly"></label>\`,
styles : []
})
export class InputComponent{
@Input() readOnly = false;
}
`
);
updateFile(
`libs/${libName}/src/lib/input-standalone/input-standalone.component.ts`,
`
import {Component, Input} from '@angular/core';
import {CommonModule} from '@angular/common';
@Component({
selector: '${projectName}-input-standalone',
standalone: true,
imports: [CommonModule],
template: \`<label class="text-green-500">Email: <input class="border-blue-500" type="email" [readOnly]="readOnly"></label>\`,
styles : []
})
export class InputStandaloneComponent{
@Input() readOnly = false;
}
`
);
}
function useLibInApp(projectName: string, appName: string, libName: string) {
createFile(
`apps/${appName}/src/app/app.component.html`,
`
<${projectName}-btn></${projectName}-btn>
<${projectName}-btn-standalone></${projectName}-btn-standalone>
<${projectName}-nx-welcome></${projectName}-nx-welcome>
`
);
const btnModuleName = names(libName).className;
updateFile(
`apps/${appName}/src/app/app.component.scss`,
`
@use 'styleguide' as *;
h1 {
@include headline;
}`
);
updateFile(
`apps/${appName}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {${btnModuleName}Module} from "@${projectName}/${libName}";
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
imports: [BrowserModule, ${btnModuleName}Module],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
`
);
}
function useWorkspaceAssetsInApp(appName: string) {
// make sure assets from the workspace root work.
createFile('libs/assets/data.json', JSON.stringify({ data: 'data' }));
createFile(
'assets/styles/styleguide.scss',
`
@mixin headline {
font-weight: bold;
color: darkkhaki;
background: lightcoral;
font-weight: 24px;
}
`
);
updateProjectConfig(appName, (config) => {
config.targets['build'].options.stylePreprocessorOptions = {
includePaths: ['assets/styles'],
};
config.targets['build'].options.assets.push({
glob: '**/*',
input: 'libs/assets',
output: 'assets',
});
return config;
});
}
function updateTestToAssertTailwindIsNotApplied(libName: string) {
createFile(
`libs/${libName}/src/lib/input/input.component.cy.ts`,
`
import { MountConfig } from 'cypress/angular';
import { InputComponent } from './input.component';
@@ -229,11 +308,11 @@ describe(InputComponent.name, () => {
});
});
`
);
);
createFile(
`libs/${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
`
createFile(
`libs/${libName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
`
import { MountConfig } from 'cypress/angular';
import { InputStandaloneComponent } from './input-standalone.component';
@@ -260,39 +339,50 @@ describe(InputStandaloneComponent.name, () => {
});
});
`
);
);
}
function useBuildableLibInLib(
projectName: string,
buildableLibName: string,
libName: string
) {
const buildLibNames = names(buildableLibName);
// use the buildable lib in lib so now buildableLib has an indirect dep on app
updateFile(
`libs/${libName}/src/lib/btn-standalone/btn-standalone.component.ts`,
`
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { InputStandaloneComponent } from '@${projectName}/${buildLibNames.fileName}';
@Component({
selector: '${projectName}-btn-standalone',
standalone: true,
imports: [CommonModule, InputStandaloneComponent],
template: '<button class="text-green-500">standlone-{{text}}</button>${projectName} <${projectName}-input-standalone></${projectName}-input-standalone>',
styles: [],
})
export class BtnStandaloneComponent {
@Input() text = 'something';
}
`
);
}
runCLI(
`generate @nrwl/angular:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build --no-interactive`
);
if (runCypressTests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!'
);
function updateBuilableLibTestsToAssertAppStyles(
appName: string,
buildableLibName: string
) {
updateFile(
`apps/${appName}/src/styles.css`,
`label {color: pink !important;}`
);
removeFile(`libs/${buildableLibName}/src/lib/input/input.component.cy.ts`);
updateFile(
`libs/${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
(content) => {
// app styles should now apply
return content.replace('rgb(34, 197, 94)', 'rgb(255, 192, 203)');
}
// add tailwind
runCLI(
`generate @nrwl/angular:setup-tailwind --project=${buildableLibName}`
);
updateFile(
`libs/${buildableLibName}/src/lib/input/input.component.cy.ts`,
(content) => {
// text-green-500 should now apply
return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)');
}
);
updateFile(
`libs/${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
(content) => {
// text-green-500 should now apply
return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)');
}
);
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!'
);
checkFilesDoNotExist(`tmp/libs/${buildableLibName}/ct-styles.css`);
}, 300_000);
});
);
}
+51 -1
View File
@@ -1,3 +1,5 @@
import { execSync } from 'child_process';
import { writeFileSync } from 'fs-extra';
import {
checkFilesDoNotExist,
checkFilesExist,
@@ -21,7 +23,9 @@ describe('js e2e', () => {
scope = newProject();
});
afterEach(() => cleanupProject());
afterEach(() => {
cleanupProject();
});
it('should create libs with js executors (--compiler=swc)', async () => {
const lib = uniq('lib');
@@ -116,4 +120,50 @@ describe('js e2e', () => {
'peerDependencies.@swc/helpers'
);
}, 240_000);
it('should handle swcrc path mappings', async () => {
const lib = uniq('lib');
runCLI(`generate @nrwl/js:lib ${lib} --compiler=swc --no-interactive`);
// add a dummy x.ts file for path mappings
updateFile(
`libs/${lib}/src/x.ts`,
`
export function x() {
console.log('x');
}
`
);
// update .swcrc to use path mappings
updateJson(`libs/${lib}/.swcrc`, (json) => {
json.jsc.paths = {
'src/*': ['src/*'],
};
return json;
});
// update lib.ts to use x
updateFile(`libs/${lib}/src/lib/${lib}.ts`, () => {
return `
// @ts-ignore
import { x } from 'src/x';
export function myLib() {
console.log(x());
}
myLib();
`;
});
// now run build
runCLI(`build ${lib}`);
// invoke the lib with node
const result = execSync(`node dist/libs/${lib}/src/lib/${lib}.js`, {
cwd: tmpProjPath(),
}).toString();
expect(result).toContain('x');
}, 240_000);
});
+18 -2
View File
@@ -180,8 +180,6 @@ describe('Nx Affected and Graph Tests', () => {
mylib = uniq('mylib');
const nxJson: NxJsonConfiguration = readJson('nx.json');
delete nxJson.implicitDependencies;
updateFile('nx.json', JSON.stringify(nxJson));
runCommand(`git init`);
runCommand(`git config user.email "test@test.com"`);
@@ -266,6 +264,24 @@ describe('Nx Affected and Graph Tests', () => {
implicitDependencies: [],
}));
});
it('should handle file renames', () => {
generateAll();
// Move file
updateFile(
`apps/${myapp2}/src/index.html`,
readFile(`apps/${myapp}/src/index.html`)
);
removeFile(`apps/${myapp}/src/index.html`);
const affectedProjects = runCLI(
'print-affected --uncommitted --select projects'
).split(', ');
expect(affectedProjects).toContain(myapp);
expect(affectedProjects).toContain(myapp2);
});
});
describe('print-affected', () => {
+4
View File
@@ -535,6 +535,10 @@ export function runCypressTests() {
if (process.env.NX_E2E_RUN_CYPRESS === 'true') {
ensureCypressInstallation();
return true;
} else {
console.warn(
'Not running Cypress because NX_E2E_RUN_CYPRESS is not set to true.'
);
}
return false;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
"version": "15.8.1",
"version": "15.8.2",
"granularPathspec": false,
"command": {
"publish": {
+1 -1
View File
@@ -177,7 +177,7 @@
"requires": {
"@angular/core": ">=15.0.0"
},
"description": "Remove browserlist config as it's handled by build-angular",
"description": "Remove browserslist config as it's handled by build-angular",
"factory": "./src/migrations/update-15-2-0/remove-browserlist-config"
},
"update-typescript-target": {
+15 -5
View File
@@ -22,7 +22,7 @@ import {
workspaceRoot,
} from '@nrwl/devkit';
import { existsSync, lstatSync, mkdirSync, writeFileSync } from 'fs';
import { dirname, join, relative } from 'path';
import { dirname, join, relative, sep } from 'path';
import type { BrowserBuilderSchema } from '../src/builders/webpack-browser/webpack-browser.impl';
/**
@@ -168,13 +168,22 @@ function normalizeBuildTargetOptions(
);
const buildOptions = withSchemaDefaults(options);
// polyfill entries might be local files or files that are resolved from node_modules
// like zone.js.
// prevents error from webpack saying can't find <offset>/zone.js.
const handlePolyfillPath = (polyfill: string) => {
const maybeFullPath = join(workspaceRoot, polyfill.split('/').join(sep));
if (existsSync(maybeFullPath)) {
return joinPathFragments(offset, polyfill);
}
return polyfill;
};
// paths need to be unix paths for angular devkit
buildOptions.polyfills =
Array.isArray(buildOptions.polyfills) && buildOptions.polyfills.length > 0
? (buildOptions.polyfills as string[]).map((p) =>
joinPathFragments(offset, p)
)
: joinPathFragments(offset, buildOptions.polyfills as string);
? (buildOptions.polyfills as string[]).map((p) => handlePolyfillPath(p))
: handlePolyfillPath(buildOptions.polyfills as string);
buildOptions.main = joinPathFragments(offset, buildOptions.main);
buildOptions.index =
typeof buildOptions.index === 'string'
@@ -197,6 +206,7 @@ function normalizeBuildTargetOptions(
// then we don't want to have the assets/scripts/styles be included to
// prevent inclusion of unintended stuff like tailwind
if (
buildContext.projectName === ctContext.projectName ||
isCtProjectUsingBuildProject(
ctContext.projectGraph,
buildContext.projectName,
+25 -9
View File
@@ -39,20 +39,36 @@ export function getTempTailwindPath(context: ExecutorContext) {
}
/**
* also returns true if the ct project and build project are the same.
* i.e. component testing inside an app.
*/
* Checks if the childProjectName is a decendent of the parentProjectName
* in the project graph
**/
export function isCtProjectUsingBuildProject(
graph: ProjectGraph,
parentProjectName: string,
childProjectName: string
) {
return (
parentProjectName === childProjectName ||
graph.dependencies[parentProjectName].some(
(p) => p.target === childProjectName
)
): boolean {
const isProjectDirectDep = graph.dependencies[parentProjectName].some(
(p) => p.target === childProjectName
);
if (isProjectDirectDep) {
return true;
}
const maybeIntermediateProjects = graph.dependencies[
parentProjectName
].filter((p) => !graph.externalNodes[p.target]);
for (const maybeIntermediateProject of maybeIntermediateProjects) {
if (
isCtProjectUsingBuildProject(
graph,
maybeIntermediateProject.target,
childProjectName
)
) {
return true;
}
}
return false;
}
export function getProjectConfigByPath(
@@ -202,6 +202,13 @@ export function getRelativeImportPath(exportedMember, filePath, basePath) {
dirname(filePath),
`${modulePath}.ts`
);
if (!existsSync(moduleFilePath)) {
// might be a tsx file
moduleFilePath = joinPathFragments(
dirname(filePath),
`${modulePath}.tsx`
);
}
if (!existsSync(moduleFilePath)) {
// might be a index.ts
moduleFilePath = joinPathFragments(
@@ -131,6 +131,40 @@ export default {
expect(packageJson.devDependencies['@types/jest']).toBeDefined();
expect(packageJson.devDependencies['ts-jest']).toBeDefined();
expect(packageJson.devDependencies['ts-node']).toBeDefined();
expect(packageJson.devDependencies['jest-environment-jsdom']).toBeDefined();
expect(
packageJson.devDependencies['jest-environment-node']
).not.toBeDefined();
});
it('should add dependencies --testEnvironment=node', async () => {
await jestInitGenerator(tree, { testEnvironment: 'node' });
const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies.jest).toBeDefined();
expect(packageJson.devDependencies['@nrwl/jest']).toBeDefined();
expect(packageJson.devDependencies['@types/jest']).toBeDefined();
expect(packageJson.devDependencies['ts-jest']).toBeDefined();
expect(packageJson.devDependencies['ts-node']).toBeDefined();
expect(packageJson.devDependencies['jest-environment-node']).toBeDefined();
expect(
packageJson.devDependencies['jest-environment-jsdom']
).not.toBeDefined();
});
it('should add dependencies --testEnvironment=none', async () => {
await jestInitGenerator(tree, { testEnvironment: 'none' });
const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies.jest).toBeDefined();
expect(packageJson.devDependencies['@nrwl/jest']).toBeDefined();
expect(packageJson.devDependencies['@types/jest']).toBeDefined();
expect(packageJson.devDependencies['ts-jest']).toBeDefined();
expect(packageJson.devDependencies['ts-node']).toBeDefined();
expect(
packageJson.devDependencies['jest-environment-jsdom']
).not.toBeDefined();
expect(
packageJson.devDependencies['jest-environment-node']
).not.toBeDefined();
});
it('should make js jest files', async () => {
+5 -1
View File
@@ -33,6 +33,7 @@ const schemaDefaults = {
compiler: 'tsc',
js: false,
rootProject: false,
testEnvironment: 'jsdom',
} as const;
function generateGlobalConfig(tree: Tree, isJS: boolean) {
@@ -146,7 +147,6 @@ function updateDependencies(tree: Tree, options: NormalizedSchema) {
const devDeps = {
'@nrwl/jest': nxVersion,
jest: jestVersion,
'jest-environment-jsdom': jestVersion,
// because the default jest-preset uses ts-jest,
// jest will throw an error if it's not installed
@@ -154,6 +154,10 @@ function updateDependencies(tree: Tree, options: NormalizedSchema) {
'ts-jest': tsJestVersion,
};
if (options.testEnvironment !== 'none') {
devDeps[`jest-environment-${options.testEnvironment}`] = jestVersion;
}
if (!options.js) {
devDeps['ts-node'] = tsNodeVersion;
devDeps['@types/jest'] = jestTypesVersion;
+1
View File
@@ -2,6 +2,7 @@ export interface JestInitSchema {
compiler?: 'tsc' | 'babel' | 'swc';
js?: boolean;
skipPackageJson?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'none';
/**
* @deprecated
*/
@@ -18,6 +18,13 @@
"description": "Do not add dependencies to `package.json`.",
"x-priority": "internal"
},
"testEnvironment": {
"type": "string",
"enum": ["jsdom", "node", "none"],
"description": "The test environment for jest. This controls which jest-environment-* package is installed",
"default": "jsdom",
"x-priority": "important"
},
"js": {
"type": "boolean",
"default": false,
@@ -18,15 +18,13 @@ const schemaDefaults = {
skipSetupFile: false,
skipSerializers: false,
rootProject: false,
testEnvironment: 'jsdom',
} as const;
function normalizeOptions(options: JestProjectSchema) {
if (!options.testEnvironment) {
options.testEnvironment = 'jsdom';
}
if (options.testEnvironment === 'jsdom') {
options.testEnvironment = '';
}
if (!options.hasOwnProperty('supportTsx')) {
options.supportTsx = false;
@@ -31,6 +31,11 @@ export function createFiles(tree: Tree, options: JestProjectSchema) {
generateFiles(tree, join(__dirname, filesFolder), projectConfig.root, {
tmpl: '',
...options,
// jsdom is the default
testEnvironment:
options.testEnvironment === 'none' || options.testEnvironment === 'jsdom'
? ''
: options.testEnvironment,
transformer,
transformerOptions,
js: !!options.js,
+1 -1
View File
@@ -7,7 +7,7 @@ export interface JestProjectSchema {
skipSetupFile?: boolean;
setupFile?: 'angular' | 'web-components' | 'none';
skipSerializers?: boolean;
testEnvironment?: 'node' | 'jsdom' | '';
testEnvironment?: 'node' | 'jsdom' | 'none';
/**
* @deprecated use compiler: "babel" instead
*/
@@ -40,7 +40,7 @@
},
"testEnvironment": {
"type": "string",
"enum": ["jsdom", "node"],
"enum": ["jsdom", "node", "none"],
"description": "The test environment for jest.",
"default": "jsdom",
"x-priority": "important"
+1 -1
View File
@@ -42,7 +42,7 @@ module.exports = function (api: any, options: NxWebBabelPresetOptions = {}) {
process.env.NODE_ENV === 'test'
? { targets: { node: 'current' }, loose: true }
: {
// Allow importing core-js in entrypoint and use browserlist to select polyfills.
// Allow importing core-js in entrypoint and use browserslist to select polyfills.
useBuiltIns: options.useBuiltIns ?? 'entry',
corejs: 3,
// Do not transform modules to CJS
+18 -3
View File
@@ -1,4 +1,4 @@
import { ExecutorContext } from '@nrwl/devkit';
import { ExecutorContext, readJsonFile, writeJsonFile } from '@nrwl/devkit';
import {
assetGlobsToFiles,
FileInputOutput,
@@ -24,6 +24,7 @@ import {
import { compileSwc, compileSwcWatch } from '../../utils/swc/compile-swc';
import { getSwcrcPath } from '../../utils/swc/get-swcrc-path';
import { generateTmpSwcrc } from '../../utils/swc/inline';
import type { Options } from '@swc/core';
export function normalizeOptions(
options: SwcExecutorOptions,
@@ -68,7 +69,21 @@ export function normalizeOptions(
// default to current directory if projectRootParts is [].
// Eg: when a project is at the root level, outside of layout dir
const swcCwd = projectRootParts.join('/') || '.';
const swcrcPath = getSwcrcPath(options, contextRoot, projectRoot);
let swcrcPath = getSwcrcPath(options, contextRoot, projectRoot);
try {
const swcrcContent = readJsonFile(swcrcPath) as Options;
// if we have path mappings setup but baseUrl isn't specified, then we're proceeding with the following logic
if (
swcrcContent.jsc &&
swcrcContent.jsc.paths &&
!swcrcContent.jsc.baseUrl
) {
swcrcContent.jsc.baseUrl = `./${projectDir}`;
swcrcPath = getSwcrcPath(options, contextRoot, projectRoot, true);
writeJsonFile(swcrcPath, swcrcContent);
}
} catch (e) {}
const swcCliOptions = {
srcPath: projectDir,
@@ -190,7 +205,7 @@ export async function* swcExecutor(
}
function removeTmpSwcrc(swcrcPath: string) {
if (swcrcPath.startsWith('tmp/')) {
if (swcrcPath.includes('tmp/') && swcrcPath.includes('.generated.swcrc')) {
removeSync(dirname(swcrcPath));
}
}
+9 -4
View File
@@ -4,9 +4,14 @@ import { SwcExecutorOptions } from '../schema';
export function getSwcrcPath(
options: SwcExecutorOptions,
contextRoot: string,
projectRoot: string
projectRoot: string,
temp = false
) {
return options.swcrc
? join(contextRoot, options.swcrc)
: join(contextRoot, projectRoot, '.swcrc');
let swcrcPath = options.swcrc ?? join(projectRoot, '.swcrc');
if (temp) {
swcrcPath = join('tmp', swcrcPath.replace('.swcrc', '.generated.swcrc'));
}
return join(contextRoot, swcrcPath);
}
@@ -267,6 +267,7 @@ export async function addLintingToApplication(
unitTestRunner: options.unitTestRunner,
skipFormat: true,
setParserOptionsProject: options.setParserOptionsProject,
rootProject: options.rootProject,
});
return lintTask;
+3 -1
View File
@@ -47,7 +47,9 @@ export async function initGenerator(tree: Tree, schema: Schema) {
})
);
if (options.unitTestRunner === 'jest') {
tasks.push(await jestInitGenerator(tree, schema));
tasks.push(
await jestInitGenerator(tree, { ...schema, testEnvironment: 'node' })
);
}
tasks.push(updateDependencies(tree));
@@ -49,6 +49,12 @@ describe('lib', () => {
},
},
});
expect(
readJson(tree, 'package.json').devDependencies['jest-environment-jsdom']
).not.toBeDefined();
expect(
readJson(tree, 'package.json').devDependencies['jest-environment-node']
).toBeDefined();
});
it('adds srcRootForCompilationRoot', async () => {
+4
View File
@@ -62,6 +62,10 @@ if (
// this file is already in the local workspace
if (localNx === resolveNx(null)) {
if (localNx.includes('.nx') && !process.env.NX_WRAPPER_SET) {
const nxWrapperPath = localNx.replace(/\.nx.*/, '.nx/') + 'nxw.js';
require(nxWrapperPath);
}
initLocal(workspace);
} else {
// Nx is being run from globally installed CLI - hand off to the local
+6
View File
@@ -47,6 +47,12 @@
"version": "15.0.12-beta.1",
"description": "Set project names in project.json files",
"implementation": "./src/migrations/update-15-1-0/set-project-names"
},
"15.8.2-update-nx-wrapper": {
"cli": "nx",
"version": "15.8.2-beta.0",
"description": "Updates the nx wrapper in encapsulated repos.",
"implementation": "./src/migrations/update-15-8-2/update-nxw"
}
}
}
+5 -1
View File
@@ -141,7 +141,11 @@ function findVersion(
) {
return snapshot.resolution.slice(packageName.length + 1);
}
if (!isBerry && !satisfies(snapshot.version, versionRange)) {
if (
!isBerry &&
snapshot.resolved &&
!satisfies(snapshot.version, versionRange)
) {
return snapshot.resolved;
}
// otherwise it's a standard version
@@ -0,0 +1,13 @@
import {
getNxWrapperContents,
nxWrapperPath,
} from 'nx/src/nx-init/encapsulated/add-nx-scripts';
import { normalizePath } from '../../utils/path';
import { Tree } from '../../generators/tree';
export default async function (tree: Tree) {
const wrapperPath = normalizePath(nxWrapperPath());
if (tree.exists(wrapperPath)) {
tree.write(wrapperPath, getNxWrapperContents());
}
}
@@ -11,7 +11,7 @@ import {
} from '../../generators/tree';
import { writeJson } from '../../generators/utils/json';
const nxWrapperPath = (p: typeof import('path') = path) =>
export const nxWrapperPath = (p: typeof import('path') = path) =>
p.join('.nx', 'nxw.js');
const NODE_MISSING_ERR =
@@ -36,7 +36,7 @@ export function generateEncapsulatedNxSetup(version?: string) {
const host = new FsTree(process.cwd(), false);
writeMinimalNxJson(host, version);
updateGitIgnore(host);
host.write(nxWrapperPath(), getNodeScriptContents());
host.write(nxWrapperPath(), getNxWrapperContents());
host.write('nx.bat', BATCH_SCRIPT_CONTENTS);
host.write('nx', SHELL_SCRIPT_CONTENTS, {
mode: FsConstants.S_IXUSR | FsConstants.S_IRUSR | FsConstants.S_IWUSR,
@@ -75,7 +75,7 @@ export function updateGitIgnore(host: Tree) {
);
}
function getNodeScriptContents() {
export function getNxWrapperContents() {
// Read nxw.js, but remove any empty comments or comments that start with `//#: `
// This removes the sourceMapUrl since it is invalid, as well as any internal comments.
return readFileSync(path.join(__dirname, 'nxw.js'), 'utf-8').replace(
+2 -1
View File
@@ -99,7 +99,8 @@ function ensureUpToDateInstallation() {
}
}
if (require.main === module) {
if (require.main === module && !process.env.NX_WRAPPER_SET) {
process.env.NX_WRAPPER_SET = 'true';
ensureUpToDateInstallation();
require('./installation/node_modules/nx/bin/nx');
}
@@ -116,16 +116,19 @@ describe('explicit package json dependencies', () => {
sourceProjectName: 'proj',
targetProjectName: 'proj2',
sourceProjectFile: 'libs/proj/package.json',
type: 'static',
},
{
sourceProjectFile: 'libs/proj/package.json',
sourceProjectName: 'proj',
targetProjectName: 'npm:external',
type: 'static',
},
{
sourceProjectName: 'proj',
targetProjectName: 'proj3',
sourceProjectFile: 'libs/proj/package.json',
type: 'static',
},
]);
});
@@ -1,6 +1,10 @@
import { defaultFileRead } from '../file-utils';
import { join } from 'path';
import { ProjectFileMap, ProjectGraph } from '../../config/project-graph';
import {
DependencyType,
ProjectFileMap,
ProjectGraph,
} from '../../config/project-graph';
import { parseJson } from '../../utils/json';
import { getImportPath, joinPathFragments } from '../../utils/path';
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
@@ -88,12 +92,14 @@ function processPackageJson(
sourceProjectName: sourceProject,
targetProjectName: packageNameMap[d],
sourceProjectFile: fileName,
type: DependencyType.static,
});
} else if (graph.externalNodes[`npm:${d}`]) {
collectedDeps.push({
sourceProjectName: sourceProject,
targetProjectName: `npm:${d}`,
sourceProjectFile: fileName,
type: DependencyType.static,
});
}
});
+2 -2
View File
@@ -222,7 +222,7 @@ export function parseFiles(options: NxArgs): { files: string[] } {
}
function getUncommittedFiles(): string[] {
return parseGitOutput(`git diff --name-only --relative HEAD .`);
return parseGitOutput(`git diff --name-only --no-renames --relative HEAD .`);
}
``;
@@ -249,7 +249,7 @@ function getFilesUsingBaseAndHead(base: string, head: string): string[] {
.trim();
}
return parseGitOutput(
`git diff --name-only --relative "${mergeBase}" "${head}"`
`git diff --name-only --no-renames --relative "${mergeBase}" "${head}"`
);
}
+1
View File
@@ -203,6 +203,7 @@ export function registerPluginTSTranspiler() {
lib: ['es2021'],
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2021,
inlineSourceMap: true,
esModuleInterop: true,
skipLibCheck: true,
experimentalDecorators: true,
+1 -1
View File
@@ -39,7 +39,7 @@
},
"peerDependencies": {
"vite": "^4.0.1",
"vitest": "^0.25.8"
"vitest": ">=0.25.8 <1.0.0"
},
"publishConfig": {
"access": "public"
+7 -1
View File
@@ -19,6 +19,7 @@ import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
import { VitePreviewServerExecutorOptions } from '../executors/preview-server/schema';
import replaceFiles from '../../plugins/rollup-replace-files.plugin';
import { ViteBuildExecutorOptions } from '../executors/build/schema';
import * as path from 'path';
/**
* Returns the path to the vite config file or undefined when not found.
@@ -70,9 +71,14 @@ export function getViteSharedConfig(
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
const root = path.relative(
context.cwd,
joinPathFragments(context.root, projectRoot)
);
return {
mode: options.mode,
root: projectRoot,
root,
base: options.base,
configFile: normalizeViteConfigFilePath(projectRoot, options.configFile),
plugins: [replaceFiles(options.fileReplacements) as PluginOption],