feat(core): rewrite path mappings when buildings apps
This commit is contained in:
parent
0b7535ae92
commit
2c42431130
@ -64,11 +64,13 @@ The prefix to apply to generated selectors.
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Generate a simple TS library when set to true.
|
||||
Generate a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -78,9 +78,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### service
|
||||
|
||||
|
||||
@ -12,6 +12,14 @@ Type: `array`
|
||||
|
||||
List of static application assets.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### externalDependencies
|
||||
|
||||
Default: `all`
|
||||
|
||||
@ -62,9 +62,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a publishable library.
|
||||
|
||||
### skipFormat
|
||||
|
||||
|
||||
@ -102,9 +102,11 @@ Use pascal case component file name (e.g. App.tsx)
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -26,6 +26,14 @@ Type: `array`
|
||||
|
||||
Budget thresholds to ensure parts of your application stay within boundaries which you set.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### commonChunk
|
||||
|
||||
Default: `true`
|
||||
|
||||
@ -64,11 +64,13 @@ The prefix to apply to generated selectors.
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Generate a simple TS library when set to true.
|
||||
Generate a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -78,9 +78,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### service
|
||||
|
||||
|
||||
@ -13,6 +13,14 @@ Type: `array`
|
||||
|
||||
List of static application assets.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### externalDependencies
|
||||
|
||||
Default: `all`
|
||||
|
||||
@ -62,9 +62,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a publishable library.
|
||||
|
||||
### skipFormat
|
||||
|
||||
|
||||
@ -102,9 +102,11 @@ Use pascal case component file name (e.g. App.tsx)
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -27,6 +27,14 @@ Type: `array`
|
||||
|
||||
Budget thresholds to ensure parts of your application stay within boundaries which you set.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### commonChunk
|
||||
|
||||
Default: `true`
|
||||
|
||||
@ -64,11 +64,13 @@ The prefix to apply to generated selectors.
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Generate a simple TS library when set to true.
|
||||
Generate a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -78,9 +78,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### service
|
||||
|
||||
|
||||
@ -13,6 +13,14 @@ Type: `array`
|
||||
|
||||
List of static application assets.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### externalDependencies
|
||||
|
||||
Default: `all`
|
||||
|
||||
@ -62,9 +62,11 @@ Library name
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a publishable library.
|
||||
|
||||
### skipFormat
|
||||
|
||||
|
||||
@ -102,9 +102,11 @@ Use pascal case component file name (e.g. App.tsx)
|
||||
|
||||
### publishable
|
||||
|
||||
Alias(es): buildable
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Create a publishable library. A "build" architect will be added for this project the workspace configuration.
|
||||
Create a buildable library.
|
||||
|
||||
### routing
|
||||
|
||||
|
||||
@ -27,6 +27,14 @@ Type: `array`
|
||||
|
||||
Budget thresholds to ensure parts of your application stay within boundaries which you set.
|
||||
|
||||
### buildLibsFromSource
|
||||
|
||||
Default: `false`
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Read buildable libraries from source instead of building them separately.
|
||||
|
||||
### commonChunk
|
||||
|
||||
Default: `true`
|
||||
|
||||
427
e2e/node.test.ts
427
e2e/node.test.ts
@ -19,7 +19,8 @@ import {
|
||||
runNgAdd,
|
||||
copyMissingPackages,
|
||||
setMaxWorkers,
|
||||
newProject
|
||||
newProject,
|
||||
checkFilesDoNotExist
|
||||
} from './utils';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { readFile } from './utils';
|
||||
@ -43,6 +44,27 @@ forEachCli(currentCLIName => {
|
||||
const linter = currentCLIName === 'angular' ? 'tslint' : 'eslint';
|
||||
|
||||
describe('Node Applications', () => {
|
||||
it('should be able to generate an empty application', async () => {
|
||||
ensureProject();
|
||||
const nodeapp = uniq('nodeapp');
|
||||
|
||||
runCLI(`generate @nrwl/node:app ${nodeapp} --linter=${linter}`);
|
||||
|
||||
setMaxWorkers(nodeapp);
|
||||
|
||||
const lintResults = runCLI(`lint ${nodeapp}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`);
|
||||
await runCLIAsync(`build ${nodeapp}`);
|
||||
|
||||
checkFilesExist(`dist/apps/${nodeapp}/main.js`);
|
||||
const result = execSync(`node dist/apps/${nodeapp}/main.js`, {
|
||||
cwd: tmpProjPath()
|
||||
}).toString();
|
||||
expect(result).toContain('Hello World!');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate an express application', async done => {
|
||||
ensureProject();
|
||||
const nodeapp = uniq('nodeapp');
|
||||
@ -227,95 +249,8 @@ forEachCli(currentCLIName => {
|
||||
});
|
||||
});
|
||||
}, 120000);
|
||||
|
||||
describe('nest libraries', function() {
|
||||
it('should be able to generate a nest library', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib}`);
|
||||
|
||||
const jestConfigContent = readFile(`libs/${nestlib}/jest.config.js`);
|
||||
|
||||
expect(stripIndents`${jestConfigContent}`).toEqual(
|
||||
stripIndents`module.exports = {
|
||||
name: '${nestlib}',
|
||||
preset: '../../jest.config.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
coverageDirectory: '../../coverage/libs/${nestlib}'
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ service', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --service`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ controller', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --controller`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ controller and service', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --controller --service`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 2 passed, 2 total');
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
it('should be able to generate an empty application', async () => {
|
||||
ensureProject();
|
||||
const nodeapp = uniq('nodeapp');
|
||||
|
||||
runCLI(`generate @nrwl/node:app ${nodeapp} --linter=${linter}`);
|
||||
|
||||
setMaxWorkers(nodeapp);
|
||||
|
||||
const lintResults = runCLI(`lint ${nodeapp}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`);
|
||||
await runCLIAsync(`build ${nodeapp}`);
|
||||
|
||||
checkFilesExist(`dist/apps/${nodeapp}/main.js`);
|
||||
const result = execSync(`node dist/apps/${nodeapp}/main.js`, {
|
||||
cwd: tmpProjPath()
|
||||
}).toString();
|
||||
expect(result).toContain('Hello World!');
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
describe('Node Libraries', () => {
|
||||
it('should be able to generate a node library', async () => {
|
||||
ensureProject();
|
||||
@ -390,148 +325,208 @@ forEachCli(currentCLIName => {
|
||||
runCLI(`build ${nodelib}`);
|
||||
checkFilesExist(`./dist/libs/${nodelib}/esm2015/index.js`);
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
describe('with dependencies', () => {
|
||||
beforeAll(() => {
|
||||
// force a new project to avoid collissions with the npmScope that has been altered before
|
||||
newProject();
|
||||
});
|
||||
describe('nest libraries', function() {
|
||||
it('should be able to generate a nest library', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
/**
|
||||
* Graph:
|
||||
*
|
||||
* childLib
|
||||
* /
|
||||
* parentLib =>
|
||||
* \
|
||||
* \
|
||||
* childLib2
|
||||
*
|
||||
*/
|
||||
let parentLib: string;
|
||||
let childLib: string;
|
||||
let childLib2: string;
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib}`);
|
||||
|
||||
beforeEach(() => {
|
||||
parentLib = uniq('parentlib');
|
||||
childLib = uniq('childlib');
|
||||
childLib2 = uniq('childlib2');
|
||||
const jestConfigContent = readFile(`libs/${nestlib}/jest.config.js`);
|
||||
|
||||
ensureProject();
|
||||
|
||||
runCLI(`generate @nrwl/node:lib ${parentLib} --publishable=true`);
|
||||
runCLI(`generate @nrwl/node:lib ${childLib} --publishable=true`);
|
||||
runCLI(`generate @nrwl/node:lib ${childLib2} --publishable=true`);
|
||||
|
||||
// create dependencies by importing
|
||||
const createDep = (parent, children: string[]) => {
|
||||
updateFile(
|
||||
`libs/${parent}/src/lib/${parent}.ts`,
|
||||
expect(stripIndents`${jestConfigContent}`).toEqual(
|
||||
stripIndents`module.exports = {
|
||||
name: '${nestlib}',
|
||||
preset: '../../jest.config.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
coverageDirectory: '../../coverage/libs/${nestlib}'
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ service', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --service`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ controller', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --controller`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
}, 60000);
|
||||
|
||||
it('should be able to generate a nest library w/ controller and service', async () => {
|
||||
ensureProject();
|
||||
const nestlib = uniq('nestlib');
|
||||
|
||||
runCLI(`generate @nrwl/nest:lib ${nestlib} --controller --service`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nestlib}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
const jestResult = await runCLIAsync(`test ${nestlib}`);
|
||||
expect(jestResult.stderr).toContain('Test Suites: 2 passed, 2 total');
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
describe('with dependencies', () => {
|
||||
/**
|
||||
* Graph:
|
||||
*
|
||||
* childLib
|
||||
* /
|
||||
* app => parentLib =>
|
||||
* \
|
||||
* \
|
||||
* childLib2
|
||||
*
|
||||
*/
|
||||
let app: string;
|
||||
let parentLib: string;
|
||||
let childLib: string;
|
||||
let childLib2: string;
|
||||
|
||||
beforeEach(() => {
|
||||
app = uniq('app');
|
||||
parentLib = uniq('parentlib');
|
||||
childLib = uniq('childlib');
|
||||
childLib2 = uniq('childlib2');
|
||||
|
||||
ensureProject();
|
||||
|
||||
runCLI(`generate @nrwl/express:app ${app}`);
|
||||
setMaxWorkers(app);
|
||||
|
||||
runCLI(`generate @nrwl/node:lib ${parentLib} --publishable=true`);
|
||||
runCLI(`generate @nrwl/node:lib ${childLib} --publishable=true`);
|
||||
runCLI(`generate @nrwl/node:lib ${childLib2} --publishable=true`);
|
||||
|
||||
// create dependencies by importing
|
||||
const createDep = (parent, children: string[]) => {
|
||||
updateFile(
|
||||
`libs/${parent}/src/lib/${parent}.ts`,
|
||||
`
|
||||
${children
|
||||
.map(entry => `import { ${entry} } from '@proj/${entry}';`)
|
||||
.join('\n')}
|
||||
|
||||
export function ${parent}(): string {
|
||||
return '${parent}' + ' ' + ${children
|
||||
.map(entry => `${entry}()`)
|
||||
.join('+')}
|
||||
.map(entry => `${entry}()`)
|
||||
.join('+')}
|
||||
}
|
||||
`
|
||||
);
|
||||
};
|
||||
|
||||
createDep(parentLib, [childLib, childLib2]);
|
||||
});
|
||||
|
||||
it('should throw an error if the dependent library has not been built before building the parent lib', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
try {
|
||||
runCLI(`build ${parentLib}`);
|
||||
} catch (e) {
|
||||
expect(e.stderr.toString()).toContain(
|
||||
`Some of the project ${parentLib}'s dependencies have not been built yet. Please build these libraries before:`
|
||||
);
|
||||
expect(e.stderr.toString()).toContain(`${childLib}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('should build a library without dependencies', () => {
|
||||
const childLibOutput = runCLI(`build ${childLib}`);
|
||||
|
||||
expect(childLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib}`
|
||||
);
|
||||
};
|
||||
|
||||
createDep(parentLib, [childLib, childLib2]);
|
||||
|
||||
updateFile(
|
||||
`apps/${app}/src/main.ts`,
|
||||
`
|
||||
import "@proj/${parentLib}";
|
||||
`
|
||||
);
|
||||
|
||||
// we are setting paths to {} to make sure built libs are read from dist
|
||||
updateFile('tsconfig.json', c => {
|
||||
const json = JSON.parse(c);
|
||||
json.compilerOptions.paths = {};
|
||||
return JSON.stringify(json, null, 2);
|
||||
});
|
||||
|
||||
it('should build a parent library if the dependent libraries have been built before', () => {
|
||||
const childLibOutput = runCLI(`build ${childLib}`);
|
||||
expect(childLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib}`
|
||||
);
|
||||
|
||||
const childLib2Output = runCLI(`build ${childLib2}`);
|
||||
expect(childLib2Output).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib2}`
|
||||
);
|
||||
|
||||
const parentLibOutput = runCLI(`build ${parentLib}`);
|
||||
expect(parentLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${parentLib}`
|
||||
);
|
||||
|
||||
// assert package.json deps have been set
|
||||
const assertPackageJson = (
|
||||
parent: string,
|
||||
lib: string,
|
||||
version: string
|
||||
) => {
|
||||
const jsonFile = readJson(`dist/libs/${parent}/package.json`);
|
||||
const childDependencyVersion = jsonFile.dependencies[`@proj/${lib}`];
|
||||
expect(childDependencyVersion).toBe(version);
|
||||
};
|
||||
|
||||
assertPackageJson(parentLib, childLib, '0.0.1');
|
||||
assertPackageJson(parentLib, childLib2, '0.0.1');
|
||||
});
|
||||
|
||||
// it('should automatically build all deps and update package.json when passing --withDeps flags', () => {
|
||||
// const parentLibOutput = runCLI(`build ${parentLib} --withDeps`);
|
||||
|
||||
// expect(parentLibOutput).toContain(
|
||||
// `Done compiling TypeScript files for library ${parentLib}`
|
||||
// );
|
||||
// expect(parentLibOutput).toContain(
|
||||
// `Done compiling TypeScript files for library ${childLib}`
|
||||
// );
|
||||
// expect(parentLibOutput).toContain(
|
||||
// `Done compiling TypeScript files for library ${childChildLib}`
|
||||
// );
|
||||
// expect(parentLibOutput).toContain(
|
||||
// `Done compiling TypeScript files for library ${childLib2}`
|
||||
// );
|
||||
// expect(parentLibOutput).toContain(
|
||||
// `Done compiling TypeScript files for library ${childLibShared}`
|
||||
// );
|
||||
|
||||
// // // assert package.json deps have been set
|
||||
// const assertPackageJson = (
|
||||
// parent: string,
|
||||
// lib: string,
|
||||
// version: string
|
||||
// ) => {
|
||||
// const jsonFile = readJson(`dist/libs/${parent}/package.json`);
|
||||
// const childDependencyVersion =
|
||||
// jsonFile.dependencies[`@proj/${lib}`];
|
||||
// expect(childDependencyVersion).toBe(version);
|
||||
// };
|
||||
|
||||
// assertPackageJson(parentLib, childLib, '0.0.1');
|
||||
// assertPackageJson(childLib, childChildLib, '0.0.1');
|
||||
// assertPackageJson(childLib, childLibShared, '0.0.1');
|
||||
// assertPackageJson(childLib2, childLibShared, '0.0.1');
|
||||
// });
|
||||
});
|
||||
|
||||
it('should throw an error if the dependent library has not been built before building the parent lib', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
try {
|
||||
runCLI(`build ${parentLib}`);
|
||||
} catch (e) {
|
||||
expect(e.stderr.toString()).toContain(
|
||||
`Some of the project ${parentLib}'s dependencies have not been built yet. Please build these libraries before:`
|
||||
);
|
||||
expect(e.stderr.toString()).toContain(`${childLib}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('should build a library without dependencies', () => {
|
||||
const childLibOutput = runCLI(`build ${childLib}`);
|
||||
|
||||
expect(childLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should build a parent library if the dependent libraries have been built before', () => {
|
||||
const childLibOutput = runCLI(`build ${childLib}`);
|
||||
expect(childLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib}`
|
||||
);
|
||||
|
||||
const childLib2Output = runCLI(`build ${childLib2}`);
|
||||
expect(childLib2Output).toContain(
|
||||
`Done compiling TypeScript files for library ${childLib2}`
|
||||
);
|
||||
|
||||
const parentLibOutput = runCLI(`build ${parentLib}`);
|
||||
expect(parentLibOutput).toContain(
|
||||
`Done compiling TypeScript files for library ${parentLib}`
|
||||
);
|
||||
|
||||
// assert package.json deps have been set
|
||||
const assertPackageJson = (
|
||||
parent: string,
|
||||
lib: string,
|
||||
version: string
|
||||
) => {
|
||||
const jsonFile = readJson(`dist/libs/${parent}/package.json`);
|
||||
const childDependencyVersion = jsonFile.dependencies[`@proj/${lib}`];
|
||||
expect(childDependencyVersion).toBe(version);
|
||||
};
|
||||
|
||||
assertPackageJson(parentLib, childLib, '0.0.1');
|
||||
assertPackageJson(parentLib, childLib2, '0.0.1');
|
||||
});
|
||||
|
||||
if (currentCLIName === 'nx') {
|
||||
it('should build an app composed out of buildable libs', () => {
|
||||
const buildWithDeps = runCLI(`build ${app} --with-deps`);
|
||||
expect(buildWithDeps).toContain(`Running target "build" succeeded`);
|
||||
checkFilesDoNotExist(`apps/${app}/tsconfig/tsconfig.nx-tmp`);
|
||||
|
||||
// we remove all path mappings from the root tsconfig, so when trying to build
|
||||
// libs from source, the builder will throw
|
||||
const failedBuild = runCLI(
|
||||
`build ${app} --with-deps --buildLibsFromSource`,
|
||||
{ silenceError: true }
|
||||
);
|
||||
expect(failedBuild).toContain(`Can't resolve`);
|
||||
}, 1000000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,44 +1,50 @@
|
||||
import {
|
||||
checkFilesDoNotExist,
|
||||
ensureProject,
|
||||
forEachCli,
|
||||
readJson,
|
||||
runCLI,
|
||||
setMaxWorkers,
|
||||
uniq,
|
||||
updateFile
|
||||
} from './utils';
|
||||
|
||||
forEachCli('nx', cli => {
|
||||
describe('Build React library', () => {
|
||||
describe('Build React libraries and apps', () => {
|
||||
/**
|
||||
* Graph:
|
||||
*
|
||||
* childLib
|
||||
* /
|
||||
* parentLib =>
|
||||
* \
|
||||
* \
|
||||
* childLib2
|
||||
* childLib
|
||||
* /
|
||||
* app => parentLib =>
|
||||
* \
|
||||
* childLib2
|
||||
*
|
||||
*/
|
||||
let app: string;
|
||||
let parentLib: string;
|
||||
let childLib: string;
|
||||
let childLib2: string;
|
||||
|
||||
beforeEach(() => {
|
||||
app = uniq('app');
|
||||
parentLib = uniq('parentlib');
|
||||
childLib = uniq('childlib');
|
||||
childLib2 = uniq('childlib2');
|
||||
|
||||
ensureProject();
|
||||
|
||||
runCLI(`generate @nrwl/react:app ${app}`);
|
||||
setMaxWorkers(app);
|
||||
|
||||
runCLI(
|
||||
`generate @nrwl/react:library ${parentLib} --publishable=true --no-interactive`
|
||||
`generate @nrwl/react:library ${parentLib} --buildable --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nrwl/react:library ${childLib} --publishable=true --no-interactive`
|
||||
`generate @nrwl/react:library ${childLib} --buildable --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nrwl/react:library ${childLib2} --publishable=true --no-interactive`
|
||||
`generate @nrwl/react:library ${childLib2} --buildable --no-interactive`
|
||||
);
|
||||
|
||||
// create dependencies by importing
|
||||
@ -53,6 +59,20 @@ forEachCli('nx', cli => {
|
||||
};
|
||||
|
||||
createDep(parentLib, [childLib, childLib2]);
|
||||
|
||||
updateFile(
|
||||
`apps/${app}/src/main.tsx`,
|
||||
`
|
||||
import "@proj/${parentLib}";
|
||||
`
|
||||
);
|
||||
|
||||
// we are setting paths to {} to make sure built libs are read from dist
|
||||
updateFile('tsconfig.json', c => {
|
||||
const json = JSON.parse(c);
|
||||
json.compilerOptions.paths = {};
|
||||
return JSON.stringify(json, null, 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the dependent library has not been built before building the parent lib', () => {
|
||||
@ -70,11 +90,11 @@ forEachCli('nx', cli => {
|
||||
|
||||
it('should build the library when it does not have any deps', () => {
|
||||
const output = runCLI(`build ${childLib}`);
|
||||
expect(output).toContain(`${childLib}.esm5.js`);
|
||||
expect(output).toContain(`${childLib}.esm.js`);
|
||||
expect(output).toContain(`Bundle complete`);
|
||||
});
|
||||
|
||||
fit('should properly add references to any dependency into the parent package.json', () => {
|
||||
it('should properly add references to any dependency into the parent package.json', () => {
|
||||
const childLibOutput = runCLI(`build ${childLib}`);
|
||||
const childLib2Output = runCLI(`build ${childLib2}`);
|
||||
const parentLibOutput = runCLI(`build ${parentLib}`);
|
||||
@ -97,6 +117,20 @@ forEachCli('nx', cli => {
|
||||
[`@proj/${childLib2}`]: '0.0.1'
|
||||
});
|
||||
});
|
||||
|
||||
it('should build an app composed out of buildable libs', () => {
|
||||
const buildWithDeps = runCLI(`build ${app} --with-deps`);
|
||||
expect(buildWithDeps).toContain(`Running target "build" succeeded`);
|
||||
checkFilesDoNotExist(`apps/${app}/tsconfig/tsconfig.nx-tmp`);
|
||||
|
||||
// we remove all path mappings from the root tsconfig, so when trying to build
|
||||
// libs from source, the builder will throw
|
||||
const failedBuild = runCLI(
|
||||
`build ${app} --with-deps --buildLibsFromSource`,
|
||||
{ silenceError: true }
|
||||
);
|
||||
expect(failedBuild).toContain(`Can't resolve`);
|
||||
}, 1000000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
DependentBuildableProjectNode,
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
updatePaths
|
||||
} from '@nrwl/workspace/src/utils/buildale-libs-utils';
|
||||
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
|
||||
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||
|
||||
export interface BuildAngularLibraryBuilderOptions {
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
"publishable": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a simple TS library when set to true."
|
||||
"description": "Generate a buildable library.",
|
||||
"alias": "buildable"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
|
||||
@ -53,7 +53,8 @@
|
||||
},
|
||||
"publishable": {
|
||||
"type": "boolean",
|
||||
"description": "Create a publishable library. A \"build\" architect will be added for this project the workspace configuration."
|
||||
"description": "Create a buildable library.",
|
||||
"alias": "buildable"
|
||||
},
|
||||
"global": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
{
|
||||
"schematics": {}
|
||||
"schematics": {
|
||||
"set-build-libs-from-source": {
|
||||
"version": "9.2.0-beta.1",
|
||||
"description": "Set buildLibsFromSource property to true to not break existing projects.",
|
||||
"factory": "./src/migrations/update-9-2-0/set-build-libs-from-source"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,122 +0,0 @@
|
||||
import { normalize, JsonObject, workspaces } from '@angular-devkit/core';
|
||||
import { join } from 'path';
|
||||
jest.mock('tsconfig-paths-webpack-plugin');
|
||||
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||
import { BuildNodeBuilderOptions } from './build.impl';
|
||||
import { of } from 'rxjs';
|
||||
import * as fs from 'fs';
|
||||
import * as buildWebpack from '@angular-devkit/build-webpack';
|
||||
import { Architect } from '@angular-devkit/architect';
|
||||
import { getTestArchitect } from '../../utils/testing';
|
||||
|
||||
describe('NodeBuildBuilder', () => {
|
||||
let testOptions: BuildNodeBuilderOptions & JsonObject;
|
||||
let architect: Architect;
|
||||
let runWebpack: jest.Mock;
|
||||
|
||||
beforeEach(async () => {
|
||||
[architect] = await getTestArchitect();
|
||||
|
||||
testOptions = {
|
||||
main: 'apps/nodeapp/src/main.ts',
|
||||
tsConfig: 'apps/nodeapp/tsconfig.app.json',
|
||||
outputPath: 'dist/apps/nodeapp',
|
||||
externalDependencies: 'all',
|
||||
fileReplacements: [
|
||||
{
|
||||
replace: 'apps/environment/environment.ts',
|
||||
with: 'apps/environment/environment.prod.ts'
|
||||
},
|
||||
{
|
||||
replace: 'module1.ts',
|
||||
with: 'module2.ts'
|
||||
}
|
||||
],
|
||||
assets: [],
|
||||
statsJson: false
|
||||
};
|
||||
runWebpack = jest.fn().mockImplementation((config, context, options) => {
|
||||
options.logging({
|
||||
toJson: () => ({
|
||||
stats: 'stats'
|
||||
})
|
||||
});
|
||||
return of({ success: true });
|
||||
});
|
||||
(buildWebpack as any).runWebpack = runWebpack;
|
||||
spyOn(workspaces, 'readWorkspace').and.returnValue({
|
||||
workspace: {
|
||||
projects: {
|
||||
get: () => ({
|
||||
sourceRoot: '/root/apps/nodeapp/src'
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
(<any>TsConfigPathsPlugin).mockImplementation(
|
||||
function MockPathsPlugin() {}
|
||||
);
|
||||
});
|
||||
|
||||
describe('run', () => {
|
||||
it('should call runWebpack', async () => {
|
||||
const run = await architect.scheduleBuilder(
|
||||
'@nrwl/node:build',
|
||||
testOptions
|
||||
);
|
||||
await run.output.toPromise();
|
||||
|
||||
await run.stop();
|
||||
|
||||
expect(runWebpack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit the outfile along with success', async () => {
|
||||
const run = await architect.scheduleBuilder(
|
||||
'@nrwl/node:build',
|
||||
testOptions
|
||||
);
|
||||
const output = await run.output.toPromise();
|
||||
|
||||
await run.stop();
|
||||
|
||||
expect(output.success).toEqual(true);
|
||||
expect(output.outfile).toEqual('/root/dist/apps/nodeapp/main.js');
|
||||
});
|
||||
|
||||
describe('webpackConfig option', () => {
|
||||
it('should require the specified function and use the return value', async () => {
|
||||
const mockFunction = jest.fn(config => ({
|
||||
config: 'config'
|
||||
}));
|
||||
jest.mock(
|
||||
join(normalize('/root'), 'apps/nodeapp/webpack.config.js'),
|
||||
() => mockFunction,
|
||||
{
|
||||
virtual: true
|
||||
}
|
||||
);
|
||||
testOptions.webpackConfig = 'apps/nodeapp/webpack.config.js';
|
||||
const run = await architect.scheduleBuilder(
|
||||
'@nrwl/node:build',
|
||||
testOptions
|
||||
);
|
||||
await run.output.toPromise();
|
||||
|
||||
await run.stop();
|
||||
|
||||
expect(mockFunction).toHaveBeenCalled();
|
||||
expect(runWebpack).toHaveBeenCalledWith(
|
||||
{
|
||||
config: 'config'
|
||||
},
|
||||
jasmine.anything(),
|
||||
jasmine.anything()
|
||||
);
|
||||
// expect(runWebpack.calls.first().args[0]).toEqual({
|
||||
// config: 'config'
|
||||
// });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -10,6 +10,11 @@ import { OUT_FILENAME } from '../../utils/config';
|
||||
import { BuildBuilderOptions } from '../../utils/types';
|
||||
import { normalizeBuildOptions } from '../../utils/normalize';
|
||||
import { NodeJsSyncHost } from '@angular-devkit/core/node';
|
||||
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||
import {
|
||||
calculateProjectDependencies,
|
||||
createTmpTsConfig
|
||||
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
|
||||
|
||||
try {
|
||||
require('dotenv').config();
|
||||
@ -19,6 +24,7 @@ export interface BuildNodeBuilderOptions extends BuildBuilderOptions {
|
||||
optimization?: boolean;
|
||||
sourceMap?: boolean;
|
||||
externalDependencies: 'all' | 'none' | string[];
|
||||
buildLibsFromSource?: boolean;
|
||||
}
|
||||
|
||||
export type NodeBuildEvent = BuildResult & {
|
||||
@ -31,6 +37,20 @@ function run(
|
||||
options: JsonObject & BuildNodeBuilderOptions,
|
||||
context: BuilderContext
|
||||
): Observable<NodeBuildEvent> {
|
||||
if (!options.buildLibsFromSource) {
|
||||
const projGraph = createProjectGraph();
|
||||
const { target, dependencies } = calculateProjectDependencies(
|
||||
projGraph,
|
||||
context
|
||||
);
|
||||
options.tsConfig = createTmpTsConfig(
|
||||
options.tsConfig,
|
||||
context.workspaceRoot,
|
||||
target.data.root,
|
||||
dependencies
|
||||
);
|
||||
}
|
||||
|
||||
return from(getSourceRoot(context)).pipe(
|
||||
map(sourceRoot =>
|
||||
normalizeBuildOptions(options, context.workspaceRoot, sourceRoot)
|
||||
|
||||
@ -108,6 +108,11 @@
|
||||
"webpackConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to a function which takes a webpack config, context and returns the resulting webpack config"
|
||||
},
|
||||
"buildLibsFromSource": {
|
||||
"type": "boolean",
|
||||
"description": "Read buildable libraries from source instead of building them separately.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main"],
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { Architect } from '@angular-devkit/architect';
|
||||
import { EventEmitter } from 'events';
|
||||
import { join } from 'path';
|
||||
import { getMockContext, getTestArchitect } from '../../utils/testing';
|
||||
import { getMockContext } from '../../utils/testing';
|
||||
import { MockBuilderContext } from '@nrwl/workspace/testing';
|
||||
import * as projectGraphUtils from '@nrwl/workspace/src/core/project-graph';
|
||||
import {
|
||||
ProjectGraph,
|
||||
ProjectType
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import * as projectGraphUtils from '@nrwl/workspace/src/core/project-graph';
|
||||
|
||||
import {
|
||||
NodePackageBuilderOptions,
|
||||
runNodePackageBuilder
|
||||
} from './package.impl';
|
||||
import * as fsMock from 'fs';
|
||||
|
||||
jest.mock('glob');
|
||||
let glob = require('glob');
|
||||
jest.mock('fs-extra');
|
||||
@ -23,7 +24,6 @@ jest.mock('child_process');
|
||||
let { fork } = require('child_process');
|
||||
jest.mock('tree-kill');
|
||||
let treeKill = require('tree-kill');
|
||||
import * as fsMock from 'fs';
|
||||
|
||||
describe('NodeCompileBuilder', () => {
|
||||
let testOptions: NodePackageBuilderOptions;
|
||||
@ -284,11 +284,7 @@ describe('NodeCompileBuilder', () => {
|
||||
});
|
||||
|
||||
it('should call the tsc compiler with the modified tsconfig.json', done => {
|
||||
let tmpTsConfigPath = join(
|
||||
context.workspaceRoot,
|
||||
'libs/nodelib',
|
||||
'tsconfig.lib.nx-tmp'
|
||||
);
|
||||
let tmpTsConfigPath = join('libs/nodelib', 'tsconfig.nx-tmp');
|
||||
|
||||
runNodePackageBuilder(testOptions, context).subscribe({
|
||||
complete: () => {
|
||||
@ -308,9 +304,6 @@ describe('NodeCompileBuilder', () => {
|
||||
}
|
||||
});
|
||||
fakeEventEmitter.emit('exit', 0);
|
||||
|
||||
// assert temp tsconfig file gets deleted again
|
||||
expect(fsMock.unlinkSync).toHaveBeenCalledWith(tmpTsConfigPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -11,21 +11,19 @@ import { copy, removeSync } from 'fs-extra';
|
||||
import * as glob from 'glob';
|
||||
import { basename, dirname, join, normalize, relative } from 'path';
|
||||
import { Observable, of, Subscriber } from 'rxjs';
|
||||
import { finalize, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { map, switchMap, tap } from 'rxjs/operators';
|
||||
import * as treeKill from 'tree-kill';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraph
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import * as ts from 'typescript';
|
||||
import { unlinkSync } from 'fs';
|
||||
import {
|
||||
calculateProjectDependencies,
|
||||
checkDependentProjectsHaveBeenBuilt,
|
||||
createTmpTsConfig,
|
||||
DependentBuildableProjectNode,
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
updatePaths
|
||||
} from '@nrwl/workspace/src/utils/buildale-libs-utils';
|
||||
updateBuildableProjectPackageJsonDependencies
|
||||
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
|
||||
|
||||
export interface NodePackageBuilderOptions extends JsonObject {
|
||||
main: string;
|
||||
@ -181,30 +179,13 @@ function compileTypeScriptFiles(
|
||||
|
||||
return Observable.create((subscriber: Subscriber<BuilderOutput>) => {
|
||||
if (projectDependencies.length > 0) {
|
||||
// const parsedTSConfig = readTsConfig(tsConfigPath);
|
||||
const parsedTSConfig = ts.readConfigFile(tsConfigPath, ts.sys.readFile)
|
||||
.config;
|
||||
|
||||
// update TSConfig paths to point to the dist folder
|
||||
parsedTSConfig.compilerOptions = parsedTSConfig.compilerOptions || {};
|
||||
parsedTSConfig.compilerOptions.paths =
|
||||
parsedTSConfig.compilerOptions.paths || {};
|
||||
updatePaths(projectDependencies, parsedTSConfig.compilerOptions.paths);
|
||||
|
||||
// find the library root folder
|
||||
const libRoot = projGraph.nodes[context.target.project].data.root;
|
||||
|
||||
// write the tmp tsconfig needed for building
|
||||
const tmpTsConfigPath = join(
|
||||
tsConfigPath = createTmpTsConfig(
|
||||
tsConfigPath,
|
||||
context.workspaceRoot,
|
||||
libRoot,
|
||||
'tsconfig.lib.nx-tmp'
|
||||
projectDependencies
|
||||
);
|
||||
writeJsonFile(tmpTsConfigPath, parsedTSConfig);
|
||||
|
||||
// adjust the tsConfig path s.t. it points to the temporary one
|
||||
// with the adjusted paths
|
||||
tsConfigPath = tmpTsConfigPath;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -248,17 +229,7 @@ function compileTypeScriptFiles(
|
||||
new Error(`Could not compile Typescript files: \n ${error}`)
|
||||
);
|
||||
}
|
||||
}).pipe(
|
||||
finalize(() => {
|
||||
cleanupTmpTsConfigFile(tsConfigPath);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function cleanupTmpTsConfigFile(tsConfigPath) {
|
||||
if (tsConfigPath.indexOf('.nx-tmp') > -1) {
|
||||
unlinkSync(tsConfigPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function killProcess(context: BuilderContext): void {
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { readWorkspace } from '@nrwl/workspace';
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
describe('set buildLibsFromSource to true', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = Tree.empty();
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/web',
|
||||
path.join(__dirname, '../../../migrations.json')
|
||||
);
|
||||
});
|
||||
|
||||
it(`should set buildLibsFromSource to true`, async () => {
|
||||
tree.create(
|
||||
'workspace.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
demo: {
|
||||
root: 'apps/demo',
|
||||
sourceRoot: 'apps/demo/src',
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@nrwl/node:build',
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('set-build-libs-from-source', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const config = readWorkspace(tree);
|
||||
expect(config.projects.demo.architect.build.options).toEqual({
|
||||
buildLibsFromSource: true
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
import { Rule } from '@angular-devkit/schematics';
|
||||
import { updateWorkspaceInTree } from '@nrwl/workspace';
|
||||
|
||||
export default function update(): Rule {
|
||||
return updateWorkspaceInTree(workspaceJson => {
|
||||
Object.entries<any>(workspaceJson.projects).forEach(
|
||||
([projectName, project]) => {
|
||||
Object.entries<any>(project.architect).forEach(
|
||||
([targetName, targetConfig]) => {
|
||||
if (targetConfig.builder === '@nrwl/node:build') {
|
||||
const architect =
|
||||
workspaceJson.projects[projectName].architect[targetName];
|
||||
if (architect && architect.options) {
|
||||
architect.options.buildLibsFromSource = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return workspaceJson;
|
||||
});
|
||||
}
|
||||
@ -53,7 +53,8 @@
|
||||
},
|
||||
"publishable": {
|
||||
"type": "boolean",
|
||||
"description": "Create a publishable library. A \"build\" architect will be added for this project the workspace configuration."
|
||||
"description": "Create a publishable library.",
|
||||
"alias": "buildable"
|
||||
},
|
||||
"testEnvironment": {
|
||||
"type": "string",
|
||||
|
||||
@ -109,7 +109,8 @@
|
||||
},
|
||||
"publishable": {
|
||||
"type": "boolean",
|
||||
"description": "Create a publishable library. A \"build\" architect will be added for this project the workspace configuration."
|
||||
"description": "Create a buildable library.",
|
||||
"alias": "buildable"
|
||||
},
|
||||
"component": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -9,6 +9,11 @@
|
||||
"version": "9.0.0-beta.1",
|
||||
"description": "Rename @nrwl/web:bundle => @nrwl/web:package",
|
||||
"factory": "./src/migrations/update-9-0-0/update-builder-9-0-0"
|
||||
},
|
||||
"set-build-libs-from-source": {
|
||||
"version": "9.2.0-beta.1",
|
||||
"description": "Set buildLibsFromSource property to true to not break existing projects.",
|
||||
"factory": "./src/migrations/update-9-2-0/set-build-libs-from-source"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { workspaces } from '@angular-devkit/core';
|
||||
import { of } from 'rxjs';
|
||||
import * as buildWebpack from '@angular-devkit/build-webpack';
|
||||
jest.mock('tsconfig-paths-webpack-plugin');
|
||||
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||
|
||||
import { MockBuilderContext } from '@nrwl/workspace/testing';
|
||||
|
||||
import { run, WebBuildBuilderOptions } from './build.impl';
|
||||
import * as webConfigUtils from '../../utils/web.config';
|
||||
import { getMockContext } from '../../utils/testing';
|
||||
import * as indexHtmlUtils from '../../utils/third-party/cli-files/utilities/index-file/write-index-html';
|
||||
|
||||
describe('WebBuildBuilder', () => {
|
||||
let context: MockBuilderContext;
|
||||
let testOptions: WebBuildBuilderOptions;
|
||||
let runWebpack: jasmine.Spy;
|
||||
let writeIndexHtml: jasmine.Spy;
|
||||
|
||||
beforeEach(async () => {
|
||||
context = await getMockContext();
|
||||
testOptions = {
|
||||
index: 'apps/webapp/src/index.html',
|
||||
budgets: [],
|
||||
baseHref: '/',
|
||||
optimization: true,
|
||||
deployUrl: '/',
|
||||
scripts: ['apps/webapp/src/scripts.js'],
|
||||
styles: ['apps/webapp/src/styles.css'],
|
||||
main: 'apps/webapp/src/main.ts',
|
||||
tsConfig: 'apps/webapp/tsconfig.app.json',
|
||||
outputPath: 'dist/apps/webapp',
|
||||
fileReplacements: [
|
||||
{
|
||||
replace: 'apps/webapp/environment/environment.ts',
|
||||
with: 'apps/webapp/environment/environment.prod.ts'
|
||||
},
|
||||
{
|
||||
replace: 'module1.ts',
|
||||
with: 'module2.ts'
|
||||
}
|
||||
],
|
||||
assets: [],
|
||||
statsJson: false
|
||||
};
|
||||
const stats = {
|
||||
stats: 'stats'
|
||||
};
|
||||
runWebpack = spyOn(buildWebpack, 'runWebpack').and.callFake(
|
||||
(config, context, opts) => {
|
||||
opts.logging({
|
||||
toJson: () => stats,
|
||||
toString: () => JSON.stringify(stats)
|
||||
});
|
||||
return of({
|
||||
success: true,
|
||||
emittedFiles: [
|
||||
{
|
||||
file: 'scripts.js',
|
||||
extension: '.js'
|
||||
},
|
||||
{
|
||||
file: 'styles.css',
|
||||
extension: '.css'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
);
|
||||
spyOn(workspaces, 'readWorkspace').and.returnValue({
|
||||
workspace: {
|
||||
projects: {
|
||||
get: () => ({
|
||||
sourceRoot: join(__dirname, '../../..')
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
spyOn(webConfigUtils, 'getWebConfig').and.returnValue({
|
||||
config: 'config'
|
||||
});
|
||||
writeIndexHtml = spyOn(indexHtmlUtils, 'writeIndexHtml').and.returnValue(
|
||||
of(null)
|
||||
);
|
||||
(<any>TsConfigPathsPlugin).mockImplementation(
|
||||
function MockPathsPlugin() {}
|
||||
);
|
||||
});
|
||||
|
||||
describe('run', () => {
|
||||
it('should call runWebpack', async () => {
|
||||
await run(testOptions, context).toPromise();
|
||||
|
||||
expect(runWebpack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit success', async () => {
|
||||
const buildEvent = await run(testOptions, context).toPromise();
|
||||
|
||||
expect(buildEvent.success).toEqual(true);
|
||||
});
|
||||
|
||||
it('should write the HTML', async () => {
|
||||
await run(testOptions, context).toPromise();
|
||||
|
||||
expect(writeIndexHtml).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('differential loading', () => {
|
||||
it('should call runWebpack twice', async () => {
|
||||
await run(testOptions, context).toPromise();
|
||||
|
||||
expect(runWebpack).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webpackConfig option', () => {
|
||||
it('should require the specified function and use the return value', async () => {
|
||||
const mockFunction = jest.fn(config => ({
|
||||
config: 'config'
|
||||
}));
|
||||
jest.mock('/root/apps/webapp/webpack.config.js', () => mockFunction, {
|
||||
virtual: true
|
||||
});
|
||||
await run(
|
||||
{
|
||||
...testOptions,
|
||||
webpackConfig: './apps/webapp/webpack.config.js'
|
||||
},
|
||||
context
|
||||
).toPromise();
|
||||
|
||||
expect(mockFunction).toHaveBeenCalled();
|
||||
expect(runWebpack.calls.first().args[0]).toEqual({
|
||||
config: 'config'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -5,7 +5,7 @@ import {
|
||||
normalize
|
||||
} from '@angular-devkit/core';
|
||||
import { BuildResult, runWebpack } from '@angular-devkit/build-webpack';
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
import { from, of } from 'rxjs';
|
||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||
import { getWebConfig } from '../../utils/web.config';
|
||||
import { BuildBuilderOptions } from '../../utils/types';
|
||||
@ -16,6 +16,11 @@ import { NodeJsSyncHost } from '@angular-devkit/core/node';
|
||||
import { execSync } from 'child_process';
|
||||
import { Range, satisfies } from 'semver';
|
||||
import { basename } from 'path';
|
||||
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||
import {
|
||||
calculateProjectDependencies,
|
||||
createTmpTsConfig
|
||||
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
|
||||
|
||||
const IGNORED_WEBPACK_OUTPUT = [/WARNING in The comment file/i];
|
||||
|
||||
@ -38,6 +43,7 @@ export interface WebBuildBuilderOptions extends BuildBuilderOptions {
|
||||
subresourceIntegrity?: boolean;
|
||||
|
||||
verbose?: boolean;
|
||||
buildLibsFromSource?: boolean;
|
||||
}
|
||||
|
||||
export default createBuilder<WebBuildBuilderOptions & JsonObject>(run);
|
||||
@ -62,6 +68,21 @@ export function run(options: WebBuildBuilderOptions, context: BuilderContext) {
|
||||
`Node version ${nodeVersion} is not supported. Supported range is "${supportedRange.raw}".`
|
||||
);
|
||||
}
|
||||
|
||||
if (!options.buildLibsFromSource) {
|
||||
const projGraph = createProjectGraph();
|
||||
const { target, dependencies } = calculateProjectDependencies(
|
||||
projGraph,
|
||||
context
|
||||
);
|
||||
options.tsConfig = createTmpTsConfig(
|
||||
options.tsConfig,
|
||||
context.workspaceRoot,
|
||||
target.data.root,
|
||||
dependencies
|
||||
);
|
||||
}
|
||||
|
||||
return from(getSourceRoot(context, host))
|
||||
.pipe(
|
||||
map(sourceRoot => {
|
||||
|
||||
@ -230,6 +230,11 @@
|
||||
"webpackConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to a function which takes a webpack config, some context and returns the resulting webpack config"
|
||||
},
|
||||
"buildLibsFromSource": {
|
||||
"type": "boolean",
|
||||
"description": "Read buildable libraries from source instead of building them separately.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main", "index"],
|
||||
|
||||
@ -5,8 +5,8 @@ import {
|
||||
createBuilder
|
||||
} from '@angular-devkit/architect';
|
||||
import { JsonObject } from '@angular-devkit/core';
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
import { switchMap, tap, last, mergeMap, catchError } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, last, switchMap, tap } from 'rxjs/operators';
|
||||
import { runRollup } from './run-rollup';
|
||||
import { createBabelConfig as _createBabelConfig } from '../../utils/babel-config';
|
||||
import * as autoprefixer from 'autoprefixer';
|
||||
@ -27,18 +27,14 @@ import {
|
||||
readJsonFile,
|
||||
writeJsonFile
|
||||
} from '@nrwl/workspace/src/utils/fileutils';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraphNode
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||
import {
|
||||
calculateProjectDependencies,
|
||||
checkDependentProjectsHaveBeenBuilt,
|
||||
DependentBuildableProjectNode,
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
updatePaths
|
||||
} from '@nrwl/workspace/src/utils/buildale-libs-utils';
|
||||
import * as ts from 'typescript';
|
||||
readTsConfigWithRemappedPaths,
|
||||
updateBuildableProjectPackageJsonDependencies
|
||||
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
|
||||
|
||||
// These use require because the ES import isn't correct.
|
||||
const resolve = require('@rollup/plugin-node-resolve');
|
||||
@ -151,12 +147,10 @@ function createRollupOptions(
|
||||
context: BuilderContext,
|
||||
packageJson: any
|
||||
): rollup.InputOptions {
|
||||
const parsedTSConfig = ts.readConfigFile(options.tsConfig, ts.sys.readFile)
|
||||
.config;
|
||||
parsedTSConfig.compilerOptions = parsedTSConfig.compilerOptions || {};
|
||||
parsedTSConfig.compilerOptions.paths =
|
||||
parsedTSConfig.compilerOptions.paths || {};
|
||||
updatePaths(dependencies, parsedTSConfig.compilerOptions.paths);
|
||||
const parsedTSConfig = readTsConfigWithRemappedPaths(
|
||||
options.tsConfig,
|
||||
dependencies
|
||||
);
|
||||
|
||||
const plugins = [
|
||||
image(),
|
||||
|
||||
@ -1,17 +1,10 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import {
|
||||
updateJsonInTree,
|
||||
readJsonInTree,
|
||||
updateWorkspaceInTree,
|
||||
readWorkspace,
|
||||
getWorkspacePath
|
||||
} from '@nrwl/workspace';
|
||||
import { readWorkspace } from '@nrwl/workspace';
|
||||
|
||||
import * as path from 'path';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
|
||||
describe('Update 8-5-0', () => {
|
||||
describe('set buildLibsFromSource to true', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
@ -23,7 +16,7 @@ describe('Update 8-5-0', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it(`should remove differentialLoading as an option for build builder`, async () => {
|
||||
it(`should set buildLibsFromSource to true`, async () => {
|
||||
tree.create(
|
||||
'workspace.json',
|
||||
JSON.stringify({
|
||||
@ -34,9 +27,7 @@ describe('Update 8-5-0', () => {
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@nrwl/web:build',
|
||||
options: {
|
||||
differentialLoading: true
|
||||
}
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,10 +36,12 @@ describe('Update 8-5-0', () => {
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('update-builder-8.5.0', {}, tree)
|
||||
.runSchematicAsync('set-build-libs-from-source', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const config = readWorkspace(tree);
|
||||
expect(config.projects.demo.architect.build.options).toEqual({});
|
||||
expect(config.projects.demo.architect.build.options).toEqual({
|
||||
buildLibsFromSource: true
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
import { Rule } from '@angular-devkit/schematics';
|
||||
import { updateWorkspaceInTree } from '@nrwl/workspace';
|
||||
|
||||
export default function update(): Rule {
|
||||
return updateWorkspaceInTree(workspaceJson => {
|
||||
Object.entries<any>(workspaceJson.projects).forEach(
|
||||
([projectName, project]) => {
|
||||
Object.entries<any>(project.architect).forEach(
|
||||
([targetName, targetConfig]) => {
|
||||
if (targetConfig.builder === '@nrwl/web:build') {
|
||||
const architect =
|
||||
workspaceJson.projects[projectName].architect[targetName];
|
||||
if (architect && architect.options) {
|
||||
architect.options.buildLibsFromSource = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return workspaceJson;
|
||||
});
|
||||
}
|
||||
@ -4,7 +4,7 @@ import {
|
||||
ProjectType
|
||||
} from '../core/project-graph';
|
||||
import { BuilderContext } from '@angular-devkit/architect';
|
||||
import { join } from 'path';
|
||||
import { join, resolve, dirname } from 'path';
|
||||
import {
|
||||
fileExists,
|
||||
readJsonFile,
|
||||
@ -12,6 +12,8 @@ import {
|
||||
} from '@nrwl/workspace/src/utils/fileutils';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { getOutputsForTargetAndConfiguration } from '@nrwl/workspace/src/tasks-runner/utils';
|
||||
import * as ts from 'typescript';
|
||||
import { unlinkSync } from 'fs';
|
||||
|
||||
function isBuildable(target: string, node: ProjectGraphNode): boolean {
|
||||
return (
|
||||
@ -33,10 +35,13 @@ export function calculateProjectDependencies(
|
||||
): { target: ProjectGraphNode; dependencies: DependentBuildableProjectNode[] } {
|
||||
const target = projGraph.nodes[context.target.project];
|
||||
// gather the library dependencies
|
||||
const dependencies = (projGraph.dependencies[context.target.project] || [])
|
||||
.map(dependency => {
|
||||
const depNode = projGraph.nodes[dependency.target];
|
||||
|
||||
const dependencies = recursivelyCollectDependencies(
|
||||
context.target.project,
|
||||
projGraph,
|
||||
[]
|
||||
)
|
||||
.map(dep => {
|
||||
const depNode = projGraph.nodes[dep];
|
||||
if (
|
||||
depNode.type === ProjectType.lib &&
|
||||
isBuildable(context.target.target, depNode)
|
||||
@ -62,6 +67,83 @@ export function calculateProjectDependencies(
|
||||
return { target, dependencies };
|
||||
}
|
||||
|
||||
function recursivelyCollectDependencies(
|
||||
project: string,
|
||||
projGraph: ProjectGraph,
|
||||
acc: string[]
|
||||
) {
|
||||
(projGraph.dependencies[project] || []).forEach(dependency => {
|
||||
if (acc.indexOf(dependency.target) === -1) {
|
||||
acc.push(dependency.target);
|
||||
recursivelyCollectDependencies(dependency.target, projGraph, acc);
|
||||
}
|
||||
});
|
||||
return acc;
|
||||
}
|
||||
|
||||
export function readTsConfigWithRemappedPaths(
|
||||
tsConfig: string,
|
||||
dependencies: DependentBuildableProjectNode[]
|
||||
) {
|
||||
const parsedTSConfig = ts.readConfigFile(tsConfig, ts.sys.readFile).config;
|
||||
parsedTSConfig.compilerOptions = parsedTSConfig.compilerOptions || {};
|
||||
parsedTSConfig.compilerOptions.paths = readPaths(tsConfig) || {};
|
||||
updatePaths(dependencies, parsedTSConfig.compilerOptions.paths);
|
||||
return parsedTSConfig;
|
||||
}
|
||||
|
||||
function readPaths(tsConfig: string) {
|
||||
try {
|
||||
const parsedTSConfig = ts.readConfigFile(tsConfig, ts.sys.readFile).config;
|
||||
if (
|
||||
parsedTSConfig.compilerOptions &&
|
||||
parsedTSConfig.compilerOptions.paths
|
||||
) {
|
||||
return parsedTSConfig.compilerOptions.paths;
|
||||
} else if (parsedTSConfig.extends) {
|
||||
return readPaths(resolve(dirname(tsConfig), parsedTSConfig.extends));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function createTmpTsConfig(
|
||||
tsconfigPath: string,
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
dependencies: DependentBuildableProjectNode[]
|
||||
) {
|
||||
const parsedTSConfig = readTsConfigWithRemappedPaths(
|
||||
tsconfigPath,
|
||||
dependencies
|
||||
);
|
||||
const tmpTsConfigPath = join(workspaceRoot, projectRoot, 'tsconfig.nx-tmp');
|
||||
process.on('exit', () => {
|
||||
cleanupTmpTsConfigFile(tmpTsConfigPath);
|
||||
});
|
||||
process.on('SIGTERM', () => {
|
||||
cleanupTmpTsConfigFile(tmpTsConfigPath);
|
||||
process.exit(0);
|
||||
});
|
||||
process.on('SIGINT', () => {
|
||||
cleanupTmpTsConfigFile(tmpTsConfigPath);
|
||||
process.exit(0);
|
||||
});
|
||||
writeJsonFile(tmpTsConfigPath, parsedTSConfig);
|
||||
return join(projectRoot, 'tsconfig.nx-tmp');
|
||||
}
|
||||
|
||||
function cleanupTmpTsConfigFile(tmpTsConfigPath) {
|
||||
try {
|
||||
if (tmpTsConfigPath) {
|
||||
unlinkSync(tmpTsConfigPath);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export function checkDependentProjectsHaveBeenBuilt(
|
||||
context: BuilderContext,
|
||||
projectDependencies: DependentBuildableProjectNode[]
|
||||
@ -86,9 +168,7 @@ export function checkDependentProjectsHaveBeenBuilt(
|
||||
}'s dependencies have not been built yet. Please build these libraries before:
|
||||
${depLibsToBuildFirst.map(x => ` - ${x.node.name}`).join('\n')}
|
||||
|
||||
Try: nx run-many --target ${context.target.target} --projects ${
|
||||
context.target.project
|
||||
},...
|
||||
Try: nx run ${context.target.project}:${context.target.target} --with-deps
|
||||
`);
|
||||
|
||||
return false;
|
||||
Loading…
x
Reference in New Issue
Block a user