Compare commits
12 Commits
angular-page
...
website
| Author | SHA1 | Date | |
|---|---|---|---|
| 0429728f6d | |||
| 814a12db15 | |||
| 24a9754848 | |||
| eceb35b42e | |||
| 43403ac8a6 | |||
| e2bd49f89e | |||
| beee11ee4d | |||
| 002ab582e9 | |||
| 66ae83e569 | |||
| 5beb8d2127 | |||
| d54c080189 | |||
| 7d7841a71a |
@@ -230,4 +230,32 @@ describe('EsBuild Plugin', () => {
|
||||
const output = runCLI(`build ${myPkg}`);
|
||||
expect(output).toContain('custom config loaded');
|
||||
}, 120_000);
|
||||
|
||||
it('should bundle in non-sensitive NX_ environment variables', () => {
|
||||
const myPkg = uniq('my-pkg');
|
||||
runCLI(`generate @nx/js:lib ${myPkg} --bundler=esbuild`, {});
|
||||
|
||||
updateFile(
|
||||
`libs/${myPkg}/src/index.ts`,
|
||||
`
|
||||
console.log(process.env['NX_CLOUD_ENCRYPTION_KEY']);
|
||||
console.log(process.env['NX_CLOUD_ACCESS_TOKEN']);
|
||||
console.log(process.env['NX_PUBLIC_TEST']);
|
||||
`
|
||||
);
|
||||
|
||||
runCLI(`build ${myPkg} --platform=browser`, {
|
||||
env: {
|
||||
NX_CLOUD_ENCRYPTION_KEY: 'secret',
|
||||
NX_CLOUD_ACCESS_TOKEN: 'secret',
|
||||
NX_PUBLIC_TEST: 'foobar',
|
||||
},
|
||||
});
|
||||
|
||||
const output = runCommand(`node dist/libs/${myPkg}/index.cjs`, {
|
||||
failOnError: true,
|
||||
});
|
||||
expect(output).not.toMatch(/secret/);
|
||||
expect(output).toMatch(/foobar/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,80 +8,89 @@ import {
|
||||
runCommand,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
} from '@nx/e2e/utils';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('Gradle', () => {
|
||||
let gradleProjectName = uniq('my-gradle-project');
|
||||
describe.each([{ type: 'kotlin' }, { type: 'groovy' }])(
|
||||
'$type',
|
||||
({ type }: { type: 'kotlin' | 'groovy' }) => {
|
||||
let gradleProjectName = uniq('my-gradle-project');
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
createGradleProject(gradleProjectName, type);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
createGradleProject(gradleProjectName);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
it('should build', () => {
|
||||
const projects = runCLI(`show projects`);
|
||||
expect(projects).toContain('app');
|
||||
expect(projects).toContain('list');
|
||||
expect(projects).toContain('utilities');
|
||||
expect(projects).toContain(gradleProjectName);
|
||||
|
||||
it('should build', () => {
|
||||
const projects = runCLI(`show projects`);
|
||||
expect(projects).toContain('app');
|
||||
expect(projects).toContain('list');
|
||||
expect(projects).toContain('utilities');
|
||||
expect(projects).toContain(gradleProjectName);
|
||||
const buildOutput = runCLI('build app', { verbose: true });
|
||||
// app depends on list and utilities
|
||||
expect(buildOutput).toContain('nx run list:build');
|
||||
expect(buildOutput).toContain('nx run utilities:build');
|
||||
|
||||
const buildOutput = runCLI('build app', { verbose: true });
|
||||
// app depends on list and utilities
|
||||
expect(buildOutput).toContain('nx run list:build');
|
||||
expect(buildOutput).toContain('nx run utilities:build');
|
||||
checkFilesExist(
|
||||
`app/build/libs/app.jar`,
|
||||
`list/build/libs/list.jar`,
|
||||
`utilities/build/libs/utilities.jar`
|
||||
);
|
||||
});
|
||||
|
||||
checkFilesExist(
|
||||
`app/build/libs/app.jar`,
|
||||
`list/build/libs/list.jar`,
|
||||
`utilities/build/libs/utilities.jar`
|
||||
);
|
||||
});
|
||||
it('should track dependencies for new app', () => {
|
||||
if (type === 'groovy') {
|
||||
createFile(
|
||||
`app2/build.gradle`,
|
||||
`plugins {
|
||||
id 'gradleProject.groovy-application-conventions'
|
||||
}
|
||||
|
||||
it('should track dependencies for new app', () => {
|
||||
createFile(
|
||||
'app2/build.gradle.kts',
|
||||
`
|
||||
plugins {
|
||||
id("gradleProject.kotlin-application-conventions")
|
||||
dependencies {
|
||||
implementation project(':app')
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
createFile(
|
||||
`app2/build.gradle.kts`,
|
||||
`plugins {
|
||||
id("gradleProject.kotlin-library-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app"))
|
||||
}`
|
||||
);
|
||||
}
|
||||
updateFile(
|
||||
`settings.gradle${type === 'kotlin' ? '.kts' : ''}`,
|
||||
(content) => {
|
||||
content += `\r\ninclude("app2")`;
|
||||
return content;
|
||||
}
|
||||
);
|
||||
const buildOutput = runCLI('build app2', { verbose: true });
|
||||
// app2 depends on app
|
||||
expect(buildOutput).toContain('nx run app:build');
|
||||
});
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app"))
|
||||
}
|
||||
`
|
||||
);
|
||||
updateFile(`settings.gradle.kts`, (content) => {
|
||||
content += `\r\ninclude("app2")`;
|
||||
return content;
|
||||
});
|
||||
const buildOutput = runCLI('build app2', { verbose: true });
|
||||
// app2 depends on app
|
||||
expect(buildOutput).toContain('nx run app:build');
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
function createGradleProject(projectName: string) {
|
||||
function createGradleProject(
|
||||
projectName: string,
|
||||
type: 'kotlin' | 'groovy' = 'kotlin'
|
||||
) {
|
||||
e2eConsoleLogger(`Using java version: ${execSync('java --version')}`);
|
||||
e2eConsoleLogger(`Using gradle version: ${execSync('gradle --version')}`);
|
||||
e2eConsoleLogger(execSync(`gradle help --task :init`).toString());
|
||||
e2eConsoleLogger(
|
||||
runCommand(
|
||||
`gradle init --type kotlin-application --dsl kotlin --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
||||
`gradle init --type ${type}-application --dsl ${type} --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
||||
)
|
||||
);
|
||||
updateJson('nx.json', (nxJson) => {
|
||||
nxJson.plugins = ['@nx/gradle'];
|
||||
return nxJson;
|
||||
});
|
||||
createFile(
|
||||
'build.gradle.kts',
|
||||
`allprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`
|
||||
);
|
||||
runCLI(`add @nx/gradle`);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@ import {
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
killPorts,
|
||||
listFiles,
|
||||
newProject,
|
||||
readFile,
|
||||
runCLI,
|
||||
runCommandUntil,
|
||||
tmpProjPath,
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { createFileSync } from 'fs-extra';
|
||||
@@ -106,5 +109,32 @@ describe('Storybook generators and executors for monorepos', () => {
|
||||
runCLI(`run ${reactStorybookApp}:build-storybook --verbose`);
|
||||
checkFilesExist(`${reactStorybookApp}/storybook-static/index.html`);
|
||||
}, 300_000);
|
||||
|
||||
it('should not bundle in sensitive NX_ environment variables', () => {
|
||||
updateFile(
|
||||
`${reactStorybookApp}/.storybook/main.ts`,
|
||||
(content) => `
|
||||
${content}
|
||||
console.log(process.env);
|
||||
`
|
||||
);
|
||||
runCLI(`run ${reactStorybookApp}:build-storybook --verbose`, {
|
||||
env: {
|
||||
NX_CLOUD_ENCRYPTION_KEY: 'MY SECRET',
|
||||
NX_CLOUD_ACCESS_TOKEN: 'MY SECRET',
|
||||
},
|
||||
});
|
||||
|
||||
// Check all output chunks for bundled environment variables
|
||||
const outDir = `${reactStorybookApp}/storybook-static`;
|
||||
const files = listFiles(outDir);
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.js')) continue;
|
||||
const content = readFile(`${outDir}/${file}`);
|
||||
expect(content).not.toMatch(/NX_CLOUD_ENCRYPTION_KEY/);
|
||||
expect(content).not.toMatch(/NX_CLOUD_ACCESS_TOKEN/);
|
||||
expect(content).not.toMatch(/MY SECRET/);
|
||||
}
|
||||
}, 300_000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
listFiles,
|
||||
newProject,
|
||||
readFile,
|
||||
rmDist,
|
||||
runCLI,
|
||||
runCommand,
|
||||
@@ -159,4 +160,32 @@ describe('Webpack Plugin', () => {
|
||||
let output = runCommand(`node dist/${appName}/main.js`);
|
||||
expect(output).toMatch(/Hello/);
|
||||
}, 500_000);
|
||||
|
||||
it('should bundle in non-sensitive NX_ environment variables', () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nx/web:app ${appName} --bundler webpack`);
|
||||
updateFile(
|
||||
`apps/${appName}/src/main.ts`,
|
||||
`
|
||||
console.log(process.env['NX_CLOUD_ENCRYPTION_KEY']);
|
||||
console.log(process.env['NX_CLOUD_ACCESS_TOKEN']);
|
||||
console.log(process.env['NX_PUBLIC_TEST']);
|
||||
`
|
||||
);
|
||||
|
||||
runCLI(`build ${appName}`, {
|
||||
env: {
|
||||
NX_CLOUD_ENCRYPTION_KEY: 'secret',
|
||||
NX_CLOUD_ACCESS_TOKEN: 'secret',
|
||||
NX_PUBLIC_TEST: 'foobar',
|
||||
},
|
||||
});
|
||||
|
||||
const mainFile = listFiles(`dist/apps/${appName}`).filter((f) =>
|
||||
f.startsWith('main.')
|
||||
);
|
||||
const content = readFile(`dist/apps/${appName}/${mainFile}`);
|
||||
expect(content).not.toMatch(/secret/);
|
||||
expect(content).toMatch(/foobar/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { FactoryProvider } from 'injection-js';
|
||||
import type { FactoryProvider } from 'injection-js';
|
||||
import { STYLESHEET_PROCESSOR_TOKEN } from 'ng-packagr/lib/styles/stylesheet-processor.di';
|
||||
import { StylesheetProcessor } from './stylesheet-processor';
|
||||
import { getInstalledPackageVersionInfo } from '../angular-version-utils';
|
||||
import {
|
||||
AsyncStylesheetProcessor,
|
||||
StylesheetProcessor,
|
||||
} from './stylesheet-processor';
|
||||
|
||||
export const STYLESHEET_PROCESSOR: FactoryProvider = {
|
||||
provide: STYLESHEET_PROCESSOR_TOKEN,
|
||||
useFactory: () => StylesheetProcessor,
|
||||
useFactory: () => {
|
||||
const { version: ngPackagrVersion } =
|
||||
getInstalledPackageVersionInfo('ng-packagr');
|
||||
|
||||
return ngPackagrVersion !== '17.2.0'
|
||||
? StylesheetProcessor
|
||||
: AsyncStylesheetProcessor;
|
||||
},
|
||||
deps: [],
|
||||
};
|
||||
|
||||
@@ -53,6 +53,110 @@ export class StylesheetProcessor {
|
||||
];
|
||||
}
|
||||
|
||||
async process({
|
||||
filePath,
|
||||
content,
|
||||
}: {
|
||||
filePath: string;
|
||||
content: string;
|
||||
}): Promise<string> {
|
||||
this.createRenderWorker();
|
||||
|
||||
return this.renderWorker.run({ content, filePath });
|
||||
}
|
||||
|
||||
/** Destory workers in pool. */
|
||||
destroy(): void {
|
||||
void this.renderWorker?.destroy();
|
||||
}
|
||||
|
||||
private createRenderWorker(): Promise<void> {
|
||||
if (this.renderWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
const styleIncludePaths = [...this.includePaths];
|
||||
let prevDir = null;
|
||||
let currentDir = this.basePath;
|
||||
|
||||
while (currentDir !== prevDir) {
|
||||
const p = join(currentDir, 'node_modules');
|
||||
if (existsSync(p)) {
|
||||
styleIncludePaths.push(p);
|
||||
}
|
||||
|
||||
prevDir = currentDir;
|
||||
currentDir = dirname(prevDir);
|
||||
}
|
||||
|
||||
const browserslistData = browserslist(undefined, { path: this.basePath });
|
||||
|
||||
const { version: ngPackagrVersion } =
|
||||
getInstalledPackageVersionInfo('ng-packagr');
|
||||
let postcssConfiguration: PostcssConfiguration | undefined;
|
||||
if (gte(ngPackagrVersion, '17.3.0')) {
|
||||
const {
|
||||
loadPostcssConfiguration,
|
||||
} = require('ng-packagr/lib/styles/postcss-configuration');
|
||||
postcssConfiguration = loadPostcssConfiguration(this.projectBasePath);
|
||||
}
|
||||
|
||||
this.renderWorker = new Piscina({
|
||||
filename: require.resolve(
|
||||
'ng-packagr/lib/styles/stylesheet-processor-worker'
|
||||
),
|
||||
maxThreads,
|
||||
env: {
|
||||
...process.env,
|
||||
FORCE_COLOR: '' + colors.enabled,
|
||||
},
|
||||
workerData: {
|
||||
postcssConfiguration,
|
||||
tailwindConfigPath: getTailwindConfigPath(
|
||||
this.projectBasePath,
|
||||
workspaceRoot
|
||||
),
|
||||
projectBasePath: this.projectBasePath,
|
||||
browserslistData,
|
||||
targets: transformSupportedBrowsersToTargets(browserslistData),
|
||||
cacheDirectory: this.cacheDirectory,
|
||||
cssUrl: this.cssUrl,
|
||||
styleIncludePaths,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used when ng-packagr version is 17.2.0. The async `loadPostcssConfiguration` function
|
||||
* introduced in ng-packagr 17.2.0 causes a memory leak due to multiple workers being created. We must
|
||||
* keep this class to support any workspace that might be using ng-packagr 17.2.0 where that function
|
||||
* need to be awaited.
|
||||
*/
|
||||
export class AsyncStylesheetProcessor {
|
||||
private renderWorker: typeof Piscina | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly projectBasePath: string,
|
||||
private readonly basePath: string,
|
||||
private readonly cssUrl?: CssUrl,
|
||||
private readonly includePaths?: string[],
|
||||
private readonly cacheDirectory?: string | false
|
||||
) {
|
||||
// By default, browserslist defaults are too inclusive
|
||||
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
|
||||
// We change the default query to browsers that Angular support.
|
||||
// https://angular.io/guide/browser-support
|
||||
(browserslist.defaults as string[]) = [
|
||||
'last 2 Chrome versions',
|
||||
'last 1 Firefox version',
|
||||
'last 2 Edge major versions',
|
||||
'last 2 Safari major versions',
|
||||
'last 2 iOS major versions',
|
||||
'Firefox ESR',
|
||||
];
|
||||
}
|
||||
|
||||
async process({
|
||||
filePath,
|
||||
content,
|
||||
@@ -94,7 +198,7 @@ export class StylesheetProcessor {
|
||||
const { version: ngPackagrVersion } =
|
||||
getInstalledPackageVersionInfo('ng-packagr');
|
||||
let postcssConfiguration: PostcssConfiguration | undefined;
|
||||
if (gte(ngPackagrVersion, '17.2.0')) {
|
||||
if (ngPackagrVersion === '17.2.0') {
|
||||
const { loadPostcssConfiguration } = await import(
|
||||
'ng-packagr/lib/styles/postcss-configuration'
|
||||
);
|
||||
|
||||
@@ -166,6 +166,33 @@ describe('determineArtifactNameAndDirectoryOptions', () => {
|
||||
restoreCwd();
|
||||
});
|
||||
|
||||
it('should not duplicate the cwd when the provided directory starts with the cwd and format is "as-provided"', async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
projectType: 'application',
|
||||
});
|
||||
setCwd('apps/app1');
|
||||
|
||||
const result = await determineArtifactNameAndDirectoryOptions(tree, {
|
||||
name: 'myComponent',
|
||||
directory: 'apps/app1',
|
||||
nameAndDirectoryFormat: 'as-provided',
|
||||
artifactType: 'component',
|
||||
callingGenerator: '@my-org/my-plugin:component',
|
||||
});
|
||||
|
||||
expect(result).toStrictEqual({
|
||||
artifactName: 'myComponent',
|
||||
directory: 'apps/app1',
|
||||
fileName: 'myComponent',
|
||||
filePath: 'apps/app1/myComponent.ts',
|
||||
project: 'app1',
|
||||
nameAndDirectoryFormat: 'as-provided',
|
||||
});
|
||||
|
||||
restoreCwd();
|
||||
});
|
||||
|
||||
it('should return the options as provided when directory is provided', async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
|
||||
@@ -237,9 +237,20 @@ function getAsProvidedOptions(
|
||||
): NameAndDirectoryOptions {
|
||||
const relativeCwd = getRelativeCwd();
|
||||
|
||||
const asProvidedDirectory = options.directory
|
||||
? joinPathFragments(relativeCwd, options.directory)
|
||||
: relativeCwd;
|
||||
let asProvidedDirectory: string;
|
||||
if (options.directory) {
|
||||
// append the directory to the current working directory if it doesn't start with it
|
||||
if (
|
||||
options.directory === relativeCwd ||
|
||||
options.directory.startsWith(`${relativeCwd}/`)
|
||||
) {
|
||||
asProvidedDirectory = options.directory;
|
||||
} else {
|
||||
asProvidedDirectory = joinPathFragments(relativeCwd, options.directory);
|
||||
}
|
||||
} else {
|
||||
asProvidedDirectory = relativeCwd;
|
||||
}
|
||||
const asProvidedProject = findProjectFromPath(tree, asProvidedDirectory);
|
||||
|
||||
const asProvidedFileName =
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
// Prevent sensitive keys from being bundled when source code uses entire `process.env` object rather than individual keys (e.g. `process.env.NX_FOO`).
|
||||
// TODO(v19): Only env vars prefixed with NX_PUBLIC should be bundled. This is a breaking change so we won't do it in v18.
|
||||
const excludedKeys = ['NX_CLOUD_ACCESS_TOKEN', 'NX_CLOUD_ENCRYPTION_KEY'];
|
||||
|
||||
export function getClientEnvironment(): Record<string, string> {
|
||||
const NX_APP = /^NX_/i;
|
||||
|
||||
return Object.keys(process.env)
|
||||
.filter((key) => NX_APP.test(key) || key === 'NODE_ENV')
|
||||
.filter(
|
||||
(key) =>
|
||||
!excludedKeys.includes(key) && (NX_APP.test(key) || key === 'NODE_ENV')
|
||||
)
|
||||
.reduce((env, key) => {
|
||||
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
|
||||
return env;
|
||||
|
||||
@@ -496,4 +496,70 @@ describe('convert-to-flat-config generator', () => {
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should handle parser options even if parser is extended', async () => {
|
||||
addProjectConfiguration(tree, 'dx-assets-ui', {
|
||||
root: 'apps/dx-assets-ui',
|
||||
targets: {},
|
||||
});
|
||||
await lintProjectGenerator(tree, {
|
||||
skipFormat: false,
|
||||
linter: Linter.EsLint,
|
||||
|
||||
project: 'dx-assets-ui',
|
||||
setParserOptionsProject: false,
|
||||
});
|
||||
updateJson(tree, 'apps/dx-assets-ui/.eslintrc.json', () => {
|
||||
return {
|
||||
extends: ['../../.eslintrc.json'],
|
||||
ignorePatterns: ['!**/*', '__fixtures__/**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
parserOptions: {
|
||||
project: ['apps/dx-assets-ui/tsconfig.*?.json'],
|
||||
},
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['*.js', '*.jsx'],
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
expect(tree.exists('apps/dx-assets-ui/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.read('apps/dx-assets-ui/eslint.config.js', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
rules: {},
|
||||
languageSettings: {
|
||||
parserOptions: { project: ['apps/dx-assets-ui/tsconfig.*?.json'] },
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
rules: {},
|
||||
},
|
||||
{ ignores: ['__fixtures__/**/*'] },
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -159,6 +159,12 @@ function migrateEslintFile(projectEslintPath: string, tree: Tree) {
|
||||
}
|
||||
// add extends
|
||||
json.extends = json.extends || [];
|
||||
|
||||
// ensure extends is an array
|
||||
if (typeof json.extends === 'string') {
|
||||
json.extends = [json.extends];
|
||||
}
|
||||
|
||||
const pathToRootConfig = `${offsetFromRoot(
|
||||
dirname(projectEslintPath)
|
||||
)}${baseFile}`;
|
||||
|
||||
@@ -760,9 +760,13 @@ export function generateFlatOverride(
|
||||
!override.plugins &&
|
||||
!override.parser
|
||||
) {
|
||||
if (override.parserOptions) {
|
||||
const { parserOptions, ...rest } = override;
|
||||
return generateAst({ ...rest, languageSettings: { parserOptions } });
|
||||
}
|
||||
return generateAst(override);
|
||||
}
|
||||
const { files, excludedFiles, rules, ...rest } = override;
|
||||
const { files, excludedFiles, rules, parserOptions, ...rest } = override;
|
||||
|
||||
const objectLiteralElements: ts.ObjectLiteralElementLike[] = [
|
||||
ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
|
||||
@@ -770,6 +774,11 @@ export function generateFlatOverride(
|
||||
addTSObjectProperty(objectLiteralElements, 'files', files);
|
||||
addTSObjectProperty(objectLiteralElements, 'excludedFiles', excludedFiles);
|
||||
addTSObjectProperty(objectLiteralElements, 'rules', rules);
|
||||
if (parserOptions) {
|
||||
addTSObjectProperty(objectLiteralElements, 'languageSettings', {
|
||||
parserOptions,
|
||||
});
|
||||
}
|
||||
|
||||
return ts.factory.createSpreadElement(
|
||||
ts.factory.createCallExpression(
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Nx Gradle",
|
||||
"version": "0.1",
|
||||
"generators": {
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#initGenerator",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Initializes a Gradle project in the current workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './plugin';
|
||||
export { initGenerator } from './src/generators/init/init';
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
"url": "https://github.com/nrwl/nx/issues"
|
||||
},
|
||||
"homepage": "https://nx.dev",
|
||||
"generators": "./generators.json",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./package.json": "./package.json",
|
||||
"./migrations.json": "./migrations.json",
|
||||
"./generators.json": "./generators.json",
|
||||
"./plugin": "./plugin.js"
|
||||
},
|
||||
"nx-migrate": {
|
||||
"migrations": "./migrations.json"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import { initGenerator } from './init';
|
||||
|
||||
describe('@nx/gradle:init', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('settings.gradle', '');
|
||||
});
|
||||
|
||||
it('should add the plugin', async () => {
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"options": {
|
||||
"buildTargetName": "build",
|
||||
"classesTargetName": "classes",
|
||||
"testTargetName": "test",
|
||||
},
|
||||
"plugin": "@nx/gradle",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not overwrite existing plugins', async () => {
|
||||
updateNxJson(tree, {
|
||||
plugins: ['foo'],
|
||||
});
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
"foo",
|
||||
{
|
||||
"options": {
|
||||
"buildTargetName": "build",
|
||||
"classesTargetName": "classes",
|
||||
"testTargetName": "test",
|
||||
},
|
||||
"plugin": "@nx/gradle",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not add plugin if already in array', async () => {
|
||||
updateNxJson(tree, {
|
||||
plugins: ['@nx/gradle'],
|
||||
});
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
"@nx/gradle",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
logger,
|
||||
readNxJson,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts';
|
||||
import { createNodes } from '../../plugin/nodes';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { InitGeneratorSchema } from './schema';
|
||||
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||
|
||||
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
if (!options.skipPackageJson && tree.exists('package.json')) {
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
'@nx/gradle': nxVersion,
|
||||
},
|
||||
undefined,
|
||||
options.keepExistingVersions
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addPlugin(tree);
|
||||
addProjectReportToBuildGradle(tree);
|
||||
|
||||
if (options.updatePackageScripts && tree.exists('package.json')) {
|
||||
await updatePackageScripts(tree, createNodes);
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
function addPlugin(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
if (!hasGradlePlugin(tree)) {
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push({
|
||||
plugin: '@nx/gradle',
|
||||
options: {
|
||||
testTargetName: 'test',
|
||||
classesTargetName: 'classes',
|
||||
buildTargetName: 'build',
|
||||
},
|
||||
});
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds the project-report plugin to the build.gradle or build.gradle.kts file
|
||||
*/
|
||||
function addProjectReportToBuildGradle(tree: Tree) {
|
||||
let buildGradleFile: string;
|
||||
if (tree.exists('settings.gradle.kts')) {
|
||||
buildGradleFile = 'build.gradle.kts';
|
||||
} else if (tree.exists('settings.gradle')) {
|
||||
buildGradleFile = 'build.gradle';
|
||||
} else {
|
||||
throw new Error(
|
||||
'Could not find settings.gradle or settings.gradle.kts file in your gradle workspace.'
|
||||
);
|
||||
}
|
||||
let buildGradleContent = '';
|
||||
if (tree.exists(buildGradleFile)) {
|
||||
buildGradleContent = tree.read(buildGradleFile).toString();
|
||||
}
|
||||
if (buildGradleContent.includes('allprojects')) {
|
||||
if (!buildGradleContent.includes('"project-report')) {
|
||||
logger.warn(`Please add the project-report plugin to your ${buildGradleFile}:
|
||||
allprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`);
|
||||
}
|
||||
} else {
|
||||
buildGradleContent += `\n\rallprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`;
|
||||
tree.write(buildGradleFile, buildGradleContent);
|
||||
}
|
||||
}
|
||||
|
||||
export default initGenerator;
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface InitGeneratorSchema {
|
||||
skipFormat?: boolean;
|
||||
skipPackageJson?: boolean;
|
||||
keepExistingVersions?: boolean;
|
||||
updatePackageScripts?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxGradleInitSchema",
|
||||
"title": "Gradle Init Generator",
|
||||
"description": "Initializes a Gradle project in the current workspace.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add dependencies to `package.json`.",
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"keepExistingVersions": {
|
||||
"type": "boolean",
|
||||
"x-priority": "internal",
|
||||
"description": "Keep existing dependencies versions",
|
||||
"default": false
|
||||
},
|
||||
"updatePackageScripts": {
|
||||
"type": "boolean",
|
||||
"x-priority": "internal",
|
||||
"description": "Update `package.json` scripts with inferred targets",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
@@ -39,12 +39,14 @@ export const createDependencies: CreateDependencies = async (
|
||||
|
||||
if (projectName && depsFile) {
|
||||
dependencies = dependencies.concat(
|
||||
processGradleDependencies(
|
||||
depsFile,
|
||||
gradleProjectToProjectName,
|
||||
projectName,
|
||||
gradleFile,
|
||||
context
|
||||
Array.from(
|
||||
processGradleDependencies(
|
||||
depsFile,
|
||||
gradleProjectToProjectName,
|
||||
projectName,
|
||||
gradleFile,
|
||||
context
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -85,12 +87,15 @@ function processGradleDependencies(
|
||||
sourceProjectName: string,
|
||||
gradleFile: string,
|
||||
context: CreateDependenciesContext
|
||||
) {
|
||||
const dependencies: RawProjectGraphDependency[] = [];
|
||||
): Set<RawProjectGraphDependency> {
|
||||
const dependencies: Set<RawProjectGraphDependency> = new Set();
|
||||
const lines = readFileSync(depsFile).toString().split('\n');
|
||||
let inDeps = false;
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('implementationDependenciesMetadata')) {
|
||||
if (
|
||||
line.startsWith('implementationDependenciesMetadata') ||
|
||||
line.startsWith('compileClasspath')
|
||||
) {
|
||||
inDeps = true;
|
||||
continue;
|
||||
}
|
||||
@@ -116,7 +121,7 @@ function processGradleDependencies(
|
||||
sourceFile: gradleFile,
|
||||
};
|
||||
validateDependency(dependency, context);
|
||||
dependencies.push(dependency);
|
||||
dependencies.add(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ export const createNodes: CreateNodes<GradlePluginOptions> = [
|
||||
|
||||
try {
|
||||
const {
|
||||
tasksMap,
|
||||
gradleProjectToTasksTypeMap,
|
||||
gradleFileToOutputDirsMap,
|
||||
gradleFileToGradleProjectMap,
|
||||
@@ -96,16 +95,16 @@ export const createNodes: CreateNodes<GradlePluginOptions> = [
|
||||
return;
|
||||
}
|
||||
|
||||
const availableTaskNames = tasksMap.get(gradleFilePath) as string[];
|
||||
const tasksTypeMap = gradleProjectToTasksTypeMap.get(
|
||||
gradleProject
|
||||
) as Map<string, string>;
|
||||
const tasks: GradleTask[] = availableTaskNames.map((taskName) => {
|
||||
return {
|
||||
type: tasksTypeMap.get(taskName) ?? 'Unknown',
|
||||
let tasks: GradleTask[] = [];
|
||||
for (let [taskName, taskType] of tasksTypeMap.entries()) {
|
||||
tasks.push({
|
||||
type: taskType,
|
||||
name: taskName,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const outputDirs = gradleFileToOutputDirsMap.get(gradleFilePath) as Map<
|
||||
string,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { join, relative } from 'node:path';
|
||||
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { hashWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||
|
||||
import { execGradle } from './exec-gradle';
|
||||
|
||||
@@ -11,7 +10,6 @@ interface GradleReport {
|
||||
buildFileToDepsMap: Map<string, string>;
|
||||
gradleFileToOutputDirsMap: Map<string, Map<string, string>>;
|
||||
gradleProjectToTasksTypeMap: Map<string, Map<string, string>>;
|
||||
tasksMap: Map<string, string[]>;
|
||||
gradleProjectToProjectName: Map<string, string>;
|
||||
}
|
||||
|
||||
@@ -54,10 +52,6 @@ function processProjectReports(projectReportLines: string[]): GradleReport {
|
||||
*/
|
||||
const gradleProjectToGradleFileMap = new Map<string, string>();
|
||||
const dependenciesMap = new Map<string, string>();
|
||||
/**
|
||||
* Map of Gradle Build File to available tasks
|
||||
*/
|
||||
const tasksMap = new Map<string, string[]>();
|
||||
/**
|
||||
* Map of Gradle Build File to tasks type map
|
||||
*/
|
||||
@@ -95,7 +89,6 @@ function processProjectReports(projectReportLines: string[]): GradleReport {
|
||||
let projectName: string,
|
||||
absBuildFilePath: string,
|
||||
absBuildDirPath: string;
|
||||
const tasks: string[] = [];
|
||||
const outputDirMap = new Map<string, string>();
|
||||
for (const line of propertyReportLines) {
|
||||
if (line.startsWith('name: ')) {
|
||||
@@ -107,10 +100,6 @@ function processProjectReports(projectReportLines: string[]): GradleReport {
|
||||
if (line.startsWith('buildDir: ')) {
|
||||
absBuildDirPath = line.substring('buildDir: '.length);
|
||||
}
|
||||
if (line.includes(': task ')) {
|
||||
const taskSegments = line.split(': task ');
|
||||
tasks.push(taskSegments[0]);
|
||||
}
|
||||
if (line.includes('Dir: ')) {
|
||||
const [dirName, dirPath] = line.split(': ');
|
||||
const taskName = dirName.replace('Dir', '');
|
||||
@@ -141,7 +130,6 @@ function processProjectReports(projectReportLines: string[]): GradleReport {
|
||||
gradleFileToGradleProjectMap.set(buildFile, gradleProject);
|
||||
gradleProjectToGradleFileMap.set(gradleProject, buildFile);
|
||||
gradleProjectToProjectName.set(gradleProject, projectName);
|
||||
tasksMap.set(buildFile, tasks);
|
||||
}
|
||||
if (line.endsWith('taskReport')) {
|
||||
const gradleProject = line.substring(
|
||||
@@ -179,7 +167,6 @@ function processProjectReports(projectReportLines: string[]): GradleReport {
|
||||
buildFileToDepsMap,
|
||||
gradleFileToOutputDirsMap,
|
||||
gradleProjectToTasksTypeMap,
|
||||
tasksMap,
|
||||
gradleProjectToProjectName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { readNxJson, Tree } from '@nx/devkit';
|
||||
|
||||
export function hasGradlePlugin(tree: Tree): boolean {
|
||||
const nxJson = readNxJson(tree);
|
||||
return !!nxJson.plugins?.some((p) =>
|
||||
typeof p === 'string' ? p === '@nx/gradle' : p.plugin === '@nx/gradle'
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
@@ -360,9 +360,13 @@ export function getNextConfig(
|
||||
};
|
||||
}
|
||||
|
||||
// Prevent sensitive keys from being bundled when source code uses entire `process.env` object rather than individual keys (e.g. `process.env.NX_FOO`).
|
||||
// TODO(v19): BREAKING: Only support NEXT_PUBLIC_ env vars and ignore NX_ vars since this is a standard Next.js feature.
|
||||
const excludedKeys = ['NX_CLOUD_ACCESS_TOKEN', 'NX_CLOUD_ENCRYPTION_KEY'];
|
||||
|
||||
function getNxEnvironmentVariables() {
|
||||
return Object.keys(process.env)
|
||||
.filter((env) => /^NX_/i.test(env))
|
||||
.filter((env) => !excludedKeys.includes(env) && /^NX_/i.test(env))
|
||||
.reduce((env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
|
||||
@@ -63,7 +63,7 @@ async function installPackage(pkgName: string, version: string): Promise<void> {
|
||||
writeJsonFile('nx.json', nxJson);
|
||||
|
||||
try {
|
||||
await runNxAsync('');
|
||||
await runNxAsync('--help', { silent: true });
|
||||
} catch (e) {
|
||||
// revert adding the plugin to nx.json
|
||||
nxJson.installation.plugins[pkgName] = undefined;
|
||||
|
||||
@@ -49,6 +49,13 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
);
|
||||
}
|
||||
generateDotNxSetup(version);
|
||||
const { plugins } = await detectPlugins();
|
||||
plugins.forEach((plugin) => {
|
||||
execSync(`./nx add ${plugin}`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
});
|
||||
|
||||
// invokes the wrapper, thus invoking the initial installation process
|
||||
runNxSync('--version', { stdio: 'ignore' });
|
||||
return;
|
||||
@@ -65,9 +72,9 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
|
||||
output.log({ title: '🧐 Checking dependencies' });
|
||||
|
||||
const detectPluginsResponse = await detectPlugins();
|
||||
const { plugins, updatePackageScripts } = await detectPlugins();
|
||||
|
||||
if (!detectPluginsResponse?.plugins.length) {
|
||||
if (!plugins.length) {
|
||||
// If no plugins are detected/chosen, guide users to setup
|
||||
// their targetDefaults correctly so their package scripts will work.
|
||||
const packageJson: PackageJson = readJsonFile('package.json');
|
||||
@@ -89,19 +96,17 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
createNxJsonFile(repoRoot, [], [], {});
|
||||
updateGitIgnore(repoRoot);
|
||||
|
||||
addDepsToPackageJson(repoRoot, detectPluginsResponse.plugins);
|
||||
addDepsToPackageJson(repoRoot, plugins);
|
||||
|
||||
output.log({ title: '📦 Installing Nx' });
|
||||
|
||||
runInstall(repoRoot, pmc);
|
||||
|
||||
output.log({ title: '🔨 Configuring plugins' });
|
||||
for (const plugin of detectPluginsResponse.plugins) {
|
||||
for (const plugin of plugins) {
|
||||
execSync(
|
||||
`${pmc.exec} nx g ${plugin}:init --keepExistingVersions ${
|
||||
detectPluginsResponse.updatePackageScripts
|
||||
? '--updatePackageScripts'
|
||||
: ''
|
||||
updatePackageScripts ? '--updatePackageScripts' : ''
|
||||
} --no-interactive`,
|
||||
{
|
||||
stdio: [0, 1, 2],
|
||||
@@ -110,7 +115,7 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
if (!detectPluginsResponse.updatePackageScripts) {
|
||||
if (!updatePackageScripts) {
|
||||
const rootPackageJsonPath = join(repoRoot, 'package.json');
|
||||
const json = readJsonFile<PackageJson>(rootPackageJsonPath);
|
||||
json.nx = { includedScripts: [] };
|
||||
@@ -160,9 +165,10 @@ const npmPackageToPluginMap: Record<string, string> = {
|
||||
'@remix-run/dev': '@nx/remix',
|
||||
};
|
||||
|
||||
async function detectPlugins(): Promise<
|
||||
undefined | { plugins: string[]; updatePackageScripts: boolean }
|
||||
> {
|
||||
async function detectPlugins(): Promise<{
|
||||
plugins: string[];
|
||||
updatePackageScripts: boolean;
|
||||
}> {
|
||||
let files = ['package.json'].concat(
|
||||
globWithWorkspaceContext(process.cwd(), ['**/*/package.json'])
|
||||
);
|
||||
@@ -190,10 +196,18 @@ async function detectPlugins(): Promise<
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existsSync('gradlew') || existsSync('gradlew.bat')) {
|
||||
detectedPlugins.add('@nx/gradle');
|
||||
}
|
||||
|
||||
const plugins = Array.from(detectedPlugins);
|
||||
|
||||
if (plugins.length === 0) return undefined;
|
||||
if (plugins.length === 0) {
|
||||
return {
|
||||
plugins: [],
|
||||
updatePackageScripts: false,
|
||||
};
|
||||
}
|
||||
|
||||
output.log({
|
||||
title: `Recommended Plugins:`,
|
||||
@@ -212,27 +226,30 @@ async function detectPlugins(): Promise<
|
||||
},
|
||||
]).then((r) => r.plugins);
|
||||
|
||||
if (pluginsToInstall?.length === 0) return undefined;
|
||||
if (pluginsToInstall?.length === 0)
|
||||
return {
|
||||
plugins: [],
|
||||
updatePackageScripts: false,
|
||||
};
|
||||
|
||||
const updatePackageScripts = await prompt<{ updatePackageScripts: string }>([
|
||||
{
|
||||
name: 'updatePackageScripts',
|
||||
type: 'autocomplete',
|
||||
message: `Do you want to start using Nx in your package.json scripts?`,
|
||||
choices: [
|
||||
{
|
||||
name: 'Yes',
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
},
|
||||
],
|
||||
initial: 0,
|
||||
},
|
||||
]).then((r) => r.updatePackageScripts === 'Yes');
|
||||
const updatePackageScripts =
|
||||
existsSync('package.json') &&
|
||||
(await prompt<{ updatePackageScripts: string }>([
|
||||
{
|
||||
name: 'updatePackageScripts',
|
||||
type: 'autocomplete',
|
||||
message: `Do you want to start using Nx in your package.json scripts?`,
|
||||
choices: [
|
||||
{
|
||||
name: 'Yes',
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
},
|
||||
],
|
||||
initial: 0,
|
||||
},
|
||||
]).then((r) => r.updatePackageScripts === 'Yes'));
|
||||
|
||||
return {
|
||||
plugins: pluginsToInstall,
|
||||
updatePackageScripts,
|
||||
};
|
||||
return { plugins: pluginsToInstall, updatePackageScripts };
|
||||
}
|
||||
|
||||
@@ -436,7 +436,9 @@ function processEnv(color: boolean, cwd: string, env: Record<string, string>) {
|
||||
...localEnv,
|
||||
...env,
|
||||
};
|
||||
res.PATH = localEnv.PATH; // need to override PATH to make sure we are using the local node_modules
|
||||
// need to override PATH to make sure we are using the local node_modules
|
||||
if (localEnv.PATH) res.PATH = localEnv.PATH; // UNIX-like
|
||||
if (localEnv.Path) res.Path = localEnv.Path; // Windows
|
||||
|
||||
if (color) {
|
||||
res.FORCE_COLOR = `${color}`;
|
||||
|
||||
@@ -276,17 +276,20 @@ export class Cache {
|
||||
|
||||
private tryAndRetry<T>(fn: () => Promise<T>): Promise<T> {
|
||||
let attempts = 0;
|
||||
const baseTimeout = 100;
|
||||
const baseTimeout = 5;
|
||||
// Generate a random number between 2 and 4 to raise to the power of attempts
|
||||
const baseExponent = Math.random() * 2 + 2;
|
||||
const _try = async () => {
|
||||
try {
|
||||
attempts++;
|
||||
return await fn();
|
||||
} catch (e) {
|
||||
if (attempts === 10) {
|
||||
// Max time is 5 * 4^3 = 20480ms
|
||||
if (attempts === 6) {
|
||||
// After enough attempts, throw the error
|
||||
throw e;
|
||||
}
|
||||
await new Promise((res) => setTimeout(res, baseTimeout * attempts));
|
||||
await new Promise((res) => setTimeout(res, baseExponent ** attempts));
|
||||
return await _try();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,10 @@ import { mergePlugins } from './merge-plugins';
|
||||
import { withReact } from '../with-react';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
// Prevent sensitive keys from being bundled when source code uses entire `process.env` object rather than individual keys (e.g. `process.env.NX_FOO`).
|
||||
// TODO(v19): BREAKING: Only env vars prefixed with NX_PUBLIC should be bundled. This is a breaking change so we won't do it in v18.
|
||||
const excludedKeys = ['NX_CLOUD_ACCESS_TOKEN', 'NX_CLOUD_ENCRYPTION_KEY'];
|
||||
|
||||
// This is shamelessly taken from CRA and modified for NX use
|
||||
// https://github.com/facebook/create-react-app/blob/4784997f0682e75eb32a897b4ffe34d735912e6c/packages/react-scripts/config/env.js#L71
|
||||
function getClientEnvironment(mode) {
|
||||
@@ -27,7 +31,11 @@ function getClientEnvironment(mode) {
|
||||
const STORYBOOK_PREFIX = /^STORYBOOK_/i;
|
||||
|
||||
const raw = Object.keys(process.env)
|
||||
.filter((key) => NX_PREFIX.test(key) || STORYBOOK_PREFIX.test(key))
|
||||
.filter(
|
||||
(key) =>
|
||||
!excludedKeys.includes(key) &&
|
||||
(NX_PREFIX.test(key) || STORYBOOK_PREFIX.test(key))
|
||||
)
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
|
||||
@@ -170,7 +170,10 @@ export function applyWebConfig(
|
||||
use: [
|
||||
...getCommonLoadersForCssModules(options, includePaths),
|
||||
{
|
||||
loader: path.join(__dirname, 'webpack/deprecated-stylus-loader.js'),
|
||||
loader: path.join(
|
||||
__dirname,
|
||||
'../../../utils/webpack/deprecated-stylus-loader.js'
|
||||
),
|
||||
options: {
|
||||
stylusOptions: {
|
||||
include: includePaths,
|
||||
@@ -230,7 +233,10 @@ export function applyWebConfig(
|
||||
use: [
|
||||
...getCommonLoadersForGlobalCss(options, includePaths),
|
||||
{
|
||||
loader: path.join(__dirname, 'webpack/deprecated-stylus-loader.js'),
|
||||
loader: path.join(
|
||||
__dirname,
|
||||
'../../../utils/webpack/deprecated-stylus-loader.js'
|
||||
),
|
||||
options: {
|
||||
sourceMap: !!options.sourceMap,
|
||||
stylusOptions: {
|
||||
@@ -291,7 +297,10 @@ export function applyWebConfig(
|
||||
use: [
|
||||
...getCommonLoadersForGlobalStyle(options, includePaths),
|
||||
{
|
||||
loader: require.resolve('stylus-loader'),
|
||||
loader: path.join(
|
||||
__dirname,
|
||||
'../../../utils/webpack/deprecated-stylus-loader.js'
|
||||
),
|
||||
options: {
|
||||
sourceMap: !!options.sourceMap,
|
||||
stylusOptions: {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// Prevent sensitive keys from being bundled when source code uses entire `process.env` object rather than individual keys (e.g. `process.env.NX_FOO`).
|
||||
// TODO(v19): Only env vars prefixed with NX_PUBLIC should be bundled. This is a breaking change so we won't do it in v18.
|
||||
const excludedKeys = ['NX_CLOUD_ACCESS_TOKEN', 'NX_CLOUD_ENCRYPTION_KEY'];
|
||||
|
||||
export function getClientEnvironment(mode?: string) {
|
||||
// Grab NODE_ENV and NX_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
const NX_APP = /^NX_/i;
|
||||
|
||||
const raw = Object.keys(process.env)
|
||||
.filter((key) => NX_APP.test(key))
|
||||
.filter((key) => !excludedKeys.includes(key) && NX_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
|
||||
@@ -8,8 +8,12 @@ export function interpolateEnvironmentVariablesToIndex(
|
||||
|
||||
const NX_PREFIX = /^NX_/i;
|
||||
|
||||
// Prevent sensitive keys from being bundled when source code uses entire `process.env` object rather than individual keys (e.g. `process.env.NX_FOO`).
|
||||
// TODO(v19): Only env vars prefixed with NX_PUBLIC should be bundled. This is a breaking change so we won't do it in v18.
|
||||
const excludedKeys = ['NX_CLOUD_ACCESS_TOKEN', 'NX_CLOUD_ENCRYPTION_KEY'];
|
||||
|
||||
function isNxEnvironmentKey(x: string): boolean {
|
||||
return NX_PREFIX.test(x);
|
||||
return !excludedKeys.includes(x) && NX_PREFIX.test(x);
|
||||
}
|
||||
|
||||
function getClientEnvironment(deployUrl: string) {
|
||||
|
||||
@@ -29,6 +29,7 @@ const scopes = [
|
||||
{ value: 'vue', name: 'vue: anything Vue specific' },
|
||||
{ value: 'web', name: 'web: anything Web specific' },
|
||||
{ value: 'webpack', name: 'webpack: anything Webpack specific' },
|
||||
{ value: 'gradle', name: 'gradle: anything Gradle specific'},
|
||||
{value: 'module-federation', name: 'module-federation: anything Module Federation specific'},
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user