feat(nx): support no framework apps
This commit is contained in:
parent
24f31d1495
commit
3bad40ea65
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ node_modules
|
||||
/.vscode
|
||||
dist
|
||||
/build
|
||||
/coverage
|
||||
test
|
||||
.DS_Store
|
||||
tmp
|
||||
|
||||
@ -3,7 +3,8 @@ tmp
|
||||
/build
|
||||
node_modules
|
||||
/package.json
|
||||
packages/schematics/src/collection/**/files/*.json
|
||||
packages/schematics/src/collection/**/files/**/*.json
|
||||
/.vscode
|
||||
/.idea
|
||||
/.github
|
||||
/coverage
|
||||
|
||||
@ -9,6 +9,7 @@ Build a Node application
|
||||
| `externalDependencies` | Dependencies to keep external to the bundle. ("all" (default), "none", or an array of module names) | string | `all` |
|
||||
| `main` | The name of the main entry-point file. | string | `undefined` |
|
||||
| `watch` | Run build when files change. | boolean | `false` |
|
||||
| `poll` | Frequency of file watcher in ms. | number | `undefined` |
|
||||
| `sourceMap` | Produce source maps. | boolean | `true` |
|
||||
| `progress` | Log progress to the console while building. | boolean | `false` |
|
||||
| `tsConfig` | The name of the Typescript configuration file. | string | `undefined` |
|
||||
@ -17,3 +18,4 @@ Build a Node application
|
||||
| `optimization` | Defines the optimization level of the build. | boolean | `false` |
|
||||
| `showCircularDependencies` | Show circular dependency warnings on builds. | boolean | `true` |
|
||||
| `maxWorkers` | Number of workers to use for type checking. (defaults to # of CPUS - 2) | number | `undefined` |
|
||||
| `webpackConfig` | Path to a function which takes a webpack config, context and returns the resulting webpack config | string | `undefined` |
|
||||
|
||||
32
docs/api-builders/web-build.md
Normal file
32
docs/api-builders/web-build.md
Normal file
@ -0,0 +1,32 @@
|
||||
# web-build
|
||||
|
||||
Build a web application
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Description | Type | Default value |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------- |
|
||||
| `namedChunks` | Names the produced bundles according to their entry file | boolean | `true` |
|
||||
| `main` | The name of the main entry-point file. | string | `undefined` |
|
||||
| `watch` | Enable re-building when files change. | boolean | `false` |
|
||||
| `baseHref` | Base url for the application being built. | string | `/` |
|
||||
| `deployUrl` | URL where the application will be deployed. | string | `undefined` |
|
||||
| `vendorChunk` | Use a separate bundle containing only vendor libraries. | boolean | `true` |
|
||||
| `commonChunk` | Use a separate bundle containing code used across multiple bundles. | boolean | `true` |
|
||||
| `sourceMap` | Output sourcemaps. | boolean | `true` |
|
||||
| `progress` | Log progress to the console while building. | boolean | `false` |
|
||||
| `index` | HTML File which will be contain the application | string | `undefined` |
|
||||
| `scripts` | External Scripts which will be included before the main application entry | array | `undefined` |
|
||||
| `styles` | External Styles which will be included with the application | array | `undefined` |
|
||||
| `tsConfig` | The name of the Typescript configuration file. | string | `undefined` |
|
||||
| `outputHashing` | Define the output filename cache-busting hashing mode. | string | `none` |
|
||||
| `optimization` | Enables optimization of the build output. | boolean | `undefined` |
|
||||
| `extractCss` | Extract css into a .css file | boolean | `false` |
|
||||
| `es2015Polyfills` | Conditional polyfills loaded in browsers which do not support ES2015. | string | `undefined` |
|
||||
| `subresourceIntegrity` | Enables the use of subresource integrity validation. | boolean | `false` |
|
||||
| `polyfills` | Polyfills to load before application | string | `undefined` |
|
||||
| `statsJson` | Generates a 'stats.json' file which can be analyzed using tools such as: #webpack-bundle-analyzer' or https://webpack.github.io/analyse. | boolean | `false` |
|
||||
| `extractLicenses` | Extract all licenses in a separate file, in the case of production builds only. | boolean | `false` |
|
||||
| `showCircularDependencies` | Show circular dependency warnings on builds. | boolean | `true` |
|
||||
| `maxWorkers` | Number of workers to use for type checking. (defaults to # of CPUS - 2) | number | `undefined` |
|
||||
| `webpackConfig` | Path to a function which takes a webpack config, some context and returns the resulting webpack config | string | `undefined` |
|
||||
18
docs/api-builders/web-dev-server.md
Normal file
18
docs/api-builders/web-dev-server.md
Normal file
@ -0,0 +1,18 @@
|
||||
# web-dev-server
|
||||
|
||||
Serve a web application
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Description | Type | Default value |
|
||||
| ------------- | -------------------------------------------------------- | ------- | ------------- |
|
||||
| `buildTarget` | Target which builds the application | string | `undefined` |
|
||||
| `port` | Port to listen on. | number | `4200` |
|
||||
| `host` | Host to listen on. | string | `localhost` |
|
||||
| `ssl` | Serve using HTTPS. | boolean | `false` |
|
||||
| `sslKey` | SSL key to use for serving HTTPS. | string | `undefined` |
|
||||
| `sslCert` | SSL certificate to use for serving HTTPS. | string | `undefined` |
|
||||
| `watch` | Watches for changes and rebuilds application | boolean | `true` |
|
||||
| `liveReload` | Whether to reload the page on change, using live-reload. | boolean | `true` |
|
||||
| `publicHost` | Public URL where the application will be served | string | `undefined` |
|
||||
| `open` | Open the application in the browser. | boolean | `false` |
|
||||
@ -13,14 +13,15 @@ ng generate application ...
|
||||
|
||||
| Name | Alias | Description | Type | Default value |
|
||||
| ------------------- | ----- | ------------------------------------------------- | ------- | ------------- |
|
||||
| `style` | | The file extension to be used for style files. | string | `css` |
|
||||
| `name` | | The name of the application. | string | `undefined` |
|
||||
| `prefix` | p | The prefix to apply to generated selectors. | string | `undefined` |
|
||||
| `framework` | | The Framework for the application. | string | `angular` |
|
||||
| `directory` | | The directory of the new application. | string | `undefined` |
|
||||
| `inlineStyle` | s | Specifies if the style will be in the ts file. | boolean | `false` |
|
||||
| `inlineTemplate` | t | Specifies if the template will be in the ts file. | boolean | `false` |
|
||||
| `viewEncapsulation` | | Specifies the view encapsulation strategy. | string | `undefined` |
|
||||
| `routing` | | Generates a routing module. | boolean | `false` |
|
||||
| `prefix` | p | The prefix to apply to generated selectors. | string | `undefined` |
|
||||
| `directory` | | The directory of the new application. | string | `undefined` |
|
||||
| `name` | | The name of the application. | string | `undefined` |
|
||||
| `style` | | The file extension to be used for style files. | string | `css` |
|
||||
| `skipTests` | S | Skip creating spec files. | boolean | `false` |
|
||||
| `skipFormat` | | Skip formatting files | boolean | `false` |
|
||||
| `skipPackageJson` | | Do not add dependencies to package.json. | boolean | `false` |
|
||||
|
||||
@ -11,8 +11,10 @@ ng generate jest-project ...
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Alias | Description | Type | Default value |
|
||||
| ----------------- | ----- | ------------------------------------------------------------ | ------- | ------------- |
|
||||
| `project` | | The name of the project. | string | `undefined` |
|
||||
| `skipSetupFile` | | Skips the setup file required for angular | boolean | `false` |
|
||||
| `skipSerializers` | | Skips the serializers required to snapshot angular templates | boolean | `false` |
|
||||
| Name | Alias | Description | Type | Default value |
|
||||
| ----------------- | ----- | --------------------------------------------------------------------------- | ------- | ------------- |
|
||||
| `project` | | The name of the project. | string | `undefined` |
|
||||
| `skipSetupFile` | | [Deprecated]: Skips the setup file required for angular. (Use --setup-file) | boolean | `false` |
|
||||
| `setupFile` | | The setup file to be generated | string | `angular` |
|
||||
| `skipSerializers` | | Skips the serializers required to snapshot angular templates | boolean | `false` |
|
||||
| `supportTsx` | | Setup tsx support | boolean | `false` |
|
||||
|
||||
88
e2e/schematics/web.test.ts
Normal file
88
e2e/schematics/web.test.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import {
|
||||
ensureProject,
|
||||
runCLI,
|
||||
uniq,
|
||||
newApp,
|
||||
newLib,
|
||||
updateFile,
|
||||
readFile,
|
||||
runCLIAsync,
|
||||
checkFilesExist
|
||||
} from '../utils';
|
||||
|
||||
describe('Web Applications', () => {
|
||||
it('should be able to generate a react application', async () => {
|
||||
ensureProject();
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
newApp(`${appName} --framework react`);
|
||||
newLib(`${libName} --framework none`);
|
||||
|
||||
const mainPath = `apps/${appName}/src/main.tsx`;
|
||||
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
|
||||
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/index.html`,
|
||||
`dist/apps/${appName}/polyfills.js`,
|
||||
`dist/apps/${appName}/runtime.js`,
|
||||
`dist/apps/${appName}/vendor.js`,
|
||||
`dist/apps/${appName}/main.js`,
|
||||
`dist/apps/${appName}/styles.js`
|
||||
);
|
||||
runCLI(`build ${appName} --prod --output-hashing none`);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/index.html`,
|
||||
`dist/apps/${appName}/polyfills.js`,
|
||||
`dist/apps/${appName}/runtime.js`,
|
||||
`dist/apps/${appName}/main.js`,
|
||||
`dist/apps/${appName}/styles.css`
|
||||
);
|
||||
const testResults = await runCLIAsync(`test ${appName}`);
|
||||
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
const lintE2eResults = runCLI(`lint ${appName}-e2e`);
|
||||
expect(lintE2eResults).toContain('All files pass linting.');
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
}, 30000);
|
||||
|
||||
it('should be able to generate a custom-elements application', async () => {
|
||||
ensureProject();
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
newApp(`${appName} --framework custom-elements`);
|
||||
newLib(`${libName} --framework none`);
|
||||
|
||||
const mainPath = `apps/${appName}/src/main.ts`;
|
||||
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
|
||||
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/index.html`,
|
||||
`dist/apps/${appName}/polyfills.js`,
|
||||
`dist/apps/${appName}/runtime.js`,
|
||||
`dist/apps/${appName}/main.js`,
|
||||
`dist/apps/${appName}/styles.js`
|
||||
);
|
||||
runCLI(`build ${appName} --prod --output-hashing none`);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/index.html`,
|
||||
`dist/apps/${appName}/polyfills.js`,
|
||||
`dist/apps/${appName}/runtime.js`,
|
||||
`dist/apps/${appName}/main.js`,
|
||||
`dist/apps/${appName}/styles.css`
|
||||
);
|
||||
const testResults = await runCLIAsync(`test ${appName}`);
|
||||
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
|
||||
const lintE2eResults = runCLI(`lint ${appName}-e2e`);
|
||||
expect(lintE2eResults).toContain('All files pass linting.');
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
}, 30000);
|
||||
});
|
||||
10
e2e/utils.ts
10
e2e/utils.ts
@ -86,7 +86,15 @@ export function copyMissingPackages(): void {
|
||||
'@types/jasminewd2',
|
||||
'@nestjs',
|
||||
'express',
|
||||
'@types/express'
|
||||
'@types/express',
|
||||
|
||||
'react',
|
||||
'react-dom',
|
||||
'@types/react',
|
||||
'@types/react-dom',
|
||||
'react-testing-library',
|
||||
|
||||
'document-register-element'
|
||||
];
|
||||
modulesToCopy.forEach(m => copyNodeModule(projectName, m));
|
||||
updateFile(
|
||||
|
||||
36
package.json
36
package.json
@ -36,17 +36,24 @@
|
||||
"@angular/platform-browser-dynamic": "^7.2.1",
|
||||
"@angular/router": "^7.2.1",
|
||||
"@angular/upgrade": "^7.2.1",
|
||||
"@nestjs/common": "5.5.0",
|
||||
"@nestjs/core": "5.5.0",
|
||||
"@nestjs/schematics": "5.11.2",
|
||||
"@nestjs/testing": "5.5.0",
|
||||
"@ngrx/effects": "7.1.0",
|
||||
"@ngrx/router-store": "7.1.0",
|
||||
"@ngrx/schematics": "7.1.0",
|
||||
"@ngrx/store": "7.1.0",
|
||||
"@ngrx/store-devtools": "7.1.0",
|
||||
"@schematics/angular": "7.2.2",
|
||||
"@types/express": "4.16.0",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/prettier": "^1.10.0",
|
||||
"@types/react": "^16.8.4",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"@types/webpack": "^4.4.24",
|
||||
"@types/yargs": "^11.0.0",
|
||||
"angular": "1.6.6",
|
||||
@ -57,9 +64,13 @@
|
||||
"cosmiconfig": "^4.0.0",
|
||||
"cypress": "3.1.0",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"document-register-element": "^1.13.1",
|
||||
"dotenv": "6.2.0",
|
||||
"express": "4.16.3",
|
||||
"fork-ts-checker-webpack-plugin": "^0.4.9",
|
||||
"fs-extra": "5.0.0",
|
||||
"graphviz": "^0.0.8",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^1.0.0-rc.13",
|
||||
"ignore": "^5.0.4",
|
||||
"jasmine-core": "~2.99.1",
|
||||
@ -70,16 +81,19 @@
|
||||
"jest-preset-angular": "^6.0.2",
|
||||
"karma": "~2.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-jasmine": "~1.1.1",
|
||||
"karma-webpack": "2.0.4",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.1",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-webpack": "2.0.4",
|
||||
"license-webpack-plugin": "^1.4.0",
|
||||
"ng-packagr": "4.3.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"opn": "^5.3.0",
|
||||
"precise-commits": "1.0.2",
|
||||
"prettier": "1.15.3",
|
||||
"react": "^16.8.3",
|
||||
"react-dom": "^16.8.3",
|
||||
"react-testing-library": "6.0.0",
|
||||
"release-it": "^7.4.0",
|
||||
"rxjs": "6.3.3",
|
||||
"semver": "5.4.1",
|
||||
@ -96,14 +110,7 @@
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"yargs": "^11.0.0",
|
||||
"yargs-parser": "10.0.0",
|
||||
"zone.js": "^0.8.26",
|
||||
"dotenv": "6.2.0",
|
||||
"@nestjs/core": "5.5.0",
|
||||
"@nestjs/common": "5.5.0",
|
||||
"@nestjs/testing": "5.5.0",
|
||||
"@nestjs/schematics": "5.11.2",
|
||||
"express": "4.16.3",
|
||||
"@types/express": "4.16.0"
|
||||
"zone.js": "^0.8.26"
|
||||
},
|
||||
"author": "Victor Savkin",
|
||||
"license": "MIT",
|
||||
@ -111,7 +118,12 @@
|
||||
"modulePathIgnorePatterns": [
|
||||
"tmp",
|
||||
"collection/.*/files"
|
||||
]
|
||||
],
|
||||
"collectCoverage": true,
|
||||
"coverageReporters": [
|
||||
"html"
|
||||
],
|
||||
"coverageDirectory": "coverage"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"builders": "./src/builders.json",
|
||||
"dependencies": {
|
||||
"@angular-devkit/architect": "~0.13.1",
|
||||
"@angular-devkit/build-angular": "~0.13.1",
|
||||
"@angular-devkit/build-webpack": "~0.13.1",
|
||||
"copy-webpack-plugin": "4.6.0",
|
||||
"fork-ts-checker-webpack-plugin": "0.4.9",
|
||||
|
||||
@ -11,6 +11,16 @@
|
||||
"schema": "./node/execute/schema.json",
|
||||
"description": "Execute a Node application"
|
||||
},
|
||||
"web-build": {
|
||||
"class": "./web/build/web-build.builder",
|
||||
"schema": "./web/build/schema.json",
|
||||
"description": "Build a web application"
|
||||
},
|
||||
"web-dev-server": {
|
||||
"class": "./web/dev-server/web-dev-server.builder",
|
||||
"schema": "./web/dev-server/schema.json",
|
||||
"description": "Serve a web application"
|
||||
},
|
||||
"jest": {
|
||||
"class": "./jest/jest.builder",
|
||||
"schema": "./jest/schema.json",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { normalize } from '@angular-devkit/core';
|
||||
import { join, normalize } from '@angular-devkit/core';
|
||||
import { TestLogger } from '@angular-devkit/architect/testing';
|
||||
import BuildNodeBuilder from './node-build.builder';
|
||||
import { BuildNodeBuilderOptions } from './node-build.builder';
|
||||
@ -17,7 +17,11 @@ describe('NodeBuildBuilder', () => {
|
||||
workspace: <any>{
|
||||
root: '/root'
|
||||
},
|
||||
architect: <any>{}
|
||||
architect: <any>{},
|
||||
targetSpecifier: {
|
||||
project: 'nodeapp',
|
||||
target: 'build'
|
||||
}
|
||||
});
|
||||
sourceRoot = '/root/apps/nodeapp/src';
|
||||
testOptions = {
|
||||
@ -35,7 +39,8 @@ describe('NodeBuildBuilder', () => {
|
||||
with: 'module2.ts'
|
||||
}
|
||||
],
|
||||
assets: []
|
||||
assets: [],
|
||||
statsJson: false
|
||||
};
|
||||
});
|
||||
|
||||
@ -125,75 +130,45 @@ describe('NodeBuildBuilder', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('options normalization', () => {
|
||||
it('should add the root', () => {
|
||||
const result = (<any>builder).normalizeOptions(testOptions, sourceRoot);
|
||||
expect(result.root).toEqual('/root');
|
||||
});
|
||||
|
||||
it('should resolve main from root', () => {
|
||||
const result = (<any>builder).normalizeOptions(testOptions, sourceRoot);
|
||||
expect(result.main).toEqual('/root/apps/nodeapp/src/main.ts');
|
||||
});
|
||||
|
||||
it('should resolve the output path', () => {
|
||||
const result = (<any>builder).normalizeOptions(testOptions, sourceRoot);
|
||||
expect(result.outputPath).toEqual('/root/dist/apps/nodeapp');
|
||||
});
|
||||
|
||||
it('should resolve the tsConfig path', () => {
|
||||
const result = (<any>builder).normalizeOptions(testOptions, sourceRoot);
|
||||
expect(result.tsConfig).toEqual('/root/apps/nodeapp/tsconfig.app.json');
|
||||
});
|
||||
|
||||
it('should normalize asset patterns', () => {
|
||||
spyOn(fs, 'statSync').and.returnValue({
|
||||
isDirectory: () => true
|
||||
});
|
||||
const result = (<any>builder).normalizeOptions(
|
||||
{
|
||||
...testOptions,
|
||||
assets: [
|
||||
'apps/nodeapp/src/assets',
|
||||
{
|
||||
input: '/outsideroot',
|
||||
output: 'output',
|
||||
glob: '**/*',
|
||||
ignore: ['**/*.json']
|
||||
describe('webpackConfig option', () => {
|
||||
it('should require the specified function and use the return value', async () => {
|
||||
const runWebpack = spyOn(
|
||||
builder.webpackBuilder,
|
||||
'runWebpack'
|
||||
).and.returnValue(
|
||||
of({
|
||||
success: true
|
||||
})
|
||||
);
|
||||
const mockFunction = jest.fn(config => ({
|
||||
config: 'config'
|
||||
}));
|
||||
jest.mock(
|
||||
join(normalize('/root'), 'apps/nodeapp/webpack.config.js'),
|
||||
() => mockFunction,
|
||||
{
|
||||
virtual: true
|
||||
}
|
||||
);
|
||||
await builder
|
||||
.run({
|
||||
root: normalize('/root'),
|
||||
sourceRoot: join(normalize('/root'), 'apps/nodeapp'),
|
||||
projectType: 'application',
|
||||
builder: '@nrwl/builders:node-build',
|
||||
options: {
|
||||
...testOptions,
|
||||
webpackConfig: 'apps/nodeapp/webpack.config.js'
|
||||
}
|
||||
]
|
||||
},
|
||||
sourceRoot
|
||||
);
|
||||
expect(result.assets).toEqual([
|
||||
{
|
||||
input: '/root/apps/nodeapp/src/assets',
|
||||
output: 'assets',
|
||||
glob: '**/*'
|
||||
},
|
||||
{
|
||||
input: '/outsideroot',
|
||||
output: 'output',
|
||||
glob: '**/*',
|
||||
ignore: ['**/*.json']
|
||||
}
|
||||
]);
|
||||
});
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
it('should resolve the file replacement paths', () => {
|
||||
const result = (<any>builder).normalizeOptions(testOptions, sourceRoot);
|
||||
expect(result.fileReplacements).toEqual([
|
||||
{
|
||||
replace: '/root/apps/environment/environment.ts',
|
||||
with: '/root/apps/environment/environment.prod.ts'
|
||||
},
|
||||
{
|
||||
replace: '/root/module1.ts',
|
||||
with: '/root/module2.ts'
|
||||
}
|
||||
]);
|
||||
expect(mockFunction).toHaveBeenCalled();
|
||||
expect(runWebpack.calls.first().args[0]).toEqual({
|
||||
config: 'config'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,47 +4,26 @@ import {
|
||||
BuilderConfiguration,
|
||||
BuilderContext
|
||||
} from '@angular-devkit/architect';
|
||||
import { getSystemPath, normalize, Path } from '@angular-devkit/core';
|
||||
import { getSystemPath } from '@angular-devkit/core';
|
||||
import { WebpackBuilder } from '@angular-devkit/build-webpack';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { writeFileSync, statSync } from 'fs';
|
||||
import { getWebpackConfig, OUT_FILENAME } from './webpack/config';
|
||||
import { resolve, basename, dirname, relative } from 'path';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { OUT_FILENAME } from '../../utils/webpack/config';
|
||||
import { resolve } from 'path';
|
||||
import { map } from 'rxjs/operators';
|
||||
import {
|
||||
AssetPattern,
|
||||
AssetPatternObject
|
||||
} from '@angular-devkit/build-angular';
|
||||
import { getNodeWebpackConfig } from '../../utils/webpack/node.config';
|
||||
import { normalizeBuildOptions } from '../../utils/normalize';
|
||||
import { BuildBuilderOptions } from '../../utils/types';
|
||||
|
||||
try {
|
||||
require('dotenv').config();
|
||||
} catch (e) {}
|
||||
|
||||
export interface BuildNodeBuilderOptions {
|
||||
main: string;
|
||||
outputPath: string;
|
||||
tsConfig: string;
|
||||
watch?: boolean;
|
||||
sourceMap?: boolean;
|
||||
export interface BuildNodeBuilderOptions extends BuildBuilderOptions {
|
||||
optimization?: boolean;
|
||||
sourceMap?: boolean;
|
||||
externalDependencies: 'all' | 'none' | string[];
|
||||
showCircularDependencies?: boolean;
|
||||
maxWorkers?: number;
|
||||
|
||||
fileReplacements: FileReplacement[];
|
||||
assets?: AssetPattern[];
|
||||
|
||||
progress?: boolean;
|
||||
statsJson?: boolean;
|
||||
extractLicenses?: boolean;
|
||||
|
||||
root?: string;
|
||||
}
|
||||
|
||||
export interface FileReplacement {
|
||||
replace: string;
|
||||
with: string;
|
||||
}
|
||||
|
||||
export interface NodeBuildEvent extends BuildEvent {
|
||||
@ -65,12 +44,19 @@ export default class BuildNodeBuilder
|
||||
run(
|
||||
builderConfig: BuilderConfiguration<BuildNodeBuilderOptions>
|
||||
): Observable<NodeBuildEvent> {
|
||||
const options = this.normalizeOptions(
|
||||
const options = normalizeBuildOptions(
|
||||
builderConfig.options,
|
||||
this.root,
|
||||
builderConfig.sourceRoot
|
||||
);
|
||||
|
||||
let config = getWebpackConfig(options);
|
||||
let config = getNodeWebpackConfig(options);
|
||||
if (options.webpackConfig) {
|
||||
config = require(options.webpackConfig)(config, {
|
||||
options,
|
||||
configuration: this.context.targetSpecifier.configuration
|
||||
});
|
||||
}
|
||||
return this.webpackBuilder
|
||||
.runWebpack(config, stats => {
|
||||
if (options.statsJson) {
|
||||
@ -89,69 +75,4 @@ export default class BuildNodeBuilder
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
private normalizeOptions(options: BuildNodeBuilderOptions, sourceRoot: Path) {
|
||||
return {
|
||||
...options,
|
||||
root: this.root,
|
||||
main: resolve(this.root, options.main),
|
||||
outputPath: resolve(this.root, options.outputPath),
|
||||
tsConfig: resolve(this.root, options.tsConfig),
|
||||
fileReplacements: this.normalizeFileReplacements(
|
||||
options.fileReplacements
|
||||
),
|
||||
assets: this.normalizeAssets(options.assets, sourceRoot)
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeAssets(
|
||||
assets: AssetPattern[],
|
||||
sourceRoot: Path
|
||||
): AssetPatternObject[] {
|
||||
return assets.map(asset => {
|
||||
if (typeof asset === 'string') {
|
||||
const assetPath = normalize(asset);
|
||||
const resolvedAssetPath = resolve(this.root, assetPath);
|
||||
const resolvedSourceRoot = resolve(this.root, sourceRoot);
|
||||
|
||||
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
|
||||
throw new Error(
|
||||
`The ${resolvedAssetPath} asset path must start with the project source root: ${sourceRoot}`
|
||||
);
|
||||
}
|
||||
|
||||
const isDirectory = statSync(resolvedAssetPath).isDirectory();
|
||||
const input = isDirectory
|
||||
? resolvedAssetPath
|
||||
: dirname(resolvedAssetPath);
|
||||
const output = relative(resolvedSourceRoot, resolve(this.root, input));
|
||||
const glob = isDirectory ? '**/*' : basename(resolvedAssetPath);
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
glob
|
||||
};
|
||||
} else {
|
||||
if (asset.output.startsWith('..')) {
|
||||
throw new Error(
|
||||
'An asset cannot be written to a location outside of the output path.'
|
||||
);
|
||||
}
|
||||
return {
|
||||
...asset,
|
||||
// Now we remove starting slash to make Webpack place it from the output root.
|
||||
output: asset.output.replace(/^\//, '')
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeFileReplacements(
|
||||
fileReplacements: FileReplacement[]
|
||||
): FileReplacement[] {
|
||||
return fileReplacements.map(fileReplacement => ({
|
||||
replace: resolve(this.root, fileReplacement.replace),
|
||||
with: resolve(this.root, fileReplacement.with)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,10 @@
|
||||
"description": "Run build when files change.",
|
||||
"default": false
|
||||
},
|
||||
"poll": {
|
||||
"type": "number",
|
||||
"description": "Frequency of file watcher in ms."
|
||||
},
|
||||
"sourceMap": {
|
||||
"type": "boolean",
|
||||
"description": "Produce source maps.",
|
||||
@ -91,6 +95,10 @@
|
||||
"required": ["replace", "with"]
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"webpackConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to a function which takes a webpack config, context and returns the resulting webpack config"
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main"],
|
||||
|
||||
102
packages/builders/src/utils/normalize.spec.ts
Normal file
102
packages/builders/src/utils/normalize.spec.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { normalizeBuildOptions } from './normalize';
|
||||
import { BuildBuilderOptions } from './types';
|
||||
import { Path, normalize } from '@angular-devkit/core';
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
describe('normalizeBuildOptions', () => {
|
||||
let testOptions: BuildBuilderOptions;
|
||||
let root: string;
|
||||
let sourceRoot: Path;
|
||||
|
||||
beforeEach(() => {
|
||||
testOptions = {
|
||||
main: 'apps/nodeapp/src/main.ts',
|
||||
tsConfig: 'apps/nodeapp/tsconfig.app.json',
|
||||
outputPath: 'dist/apps/nodeapp',
|
||||
fileReplacements: [
|
||||
{
|
||||
replace: 'apps/environment/environment.ts',
|
||||
with: 'apps/environment/environment.prod.ts'
|
||||
},
|
||||
{
|
||||
replace: 'module1.ts',
|
||||
with: 'module2.ts'
|
||||
}
|
||||
],
|
||||
assets: [],
|
||||
statsJson: false
|
||||
};
|
||||
root = '/root';
|
||||
sourceRoot = normalize('apps/nodeapp/src');
|
||||
});
|
||||
it('should add the root', () => {
|
||||
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
||||
expect(result.root).toEqual('/root');
|
||||
});
|
||||
|
||||
it('should resolve main from root', () => {
|
||||
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
||||
expect(result.main).toEqual('/root/apps/nodeapp/src/main.ts');
|
||||
});
|
||||
|
||||
it('should resolve the output path', () => {
|
||||
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
||||
expect(result.outputPath).toEqual('/root/dist/apps/nodeapp');
|
||||
});
|
||||
|
||||
it('should resolve the tsConfig path', () => {
|
||||
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
||||
expect(result.tsConfig).toEqual('/root/apps/nodeapp/tsconfig.app.json');
|
||||
});
|
||||
|
||||
it('should normalize asset patterns', () => {
|
||||
spyOn(fs, 'statSync').and.returnValue({
|
||||
isDirectory: () => true
|
||||
});
|
||||
const result = normalizeBuildOptions(
|
||||
<BuildBuilderOptions>{
|
||||
...testOptions,
|
||||
root,
|
||||
assets: [
|
||||
'apps/nodeapp/src/assets',
|
||||
{
|
||||
input: '/outsideroot',
|
||||
output: 'output',
|
||||
glob: '**/*',
|
||||
ignore: ['**/*.json']
|
||||
}
|
||||
]
|
||||
},
|
||||
root,
|
||||
sourceRoot
|
||||
);
|
||||
expect(result.assets).toEqual([
|
||||
{
|
||||
input: '/root/apps/nodeapp/src/assets',
|
||||
output: 'assets',
|
||||
glob: '**/*'
|
||||
},
|
||||
{
|
||||
input: '/outsideroot',
|
||||
output: 'output',
|
||||
glob: '**/*',
|
||||
ignore: ['**/*.json']
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should resolve the file replacement paths', () => {
|
||||
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
||||
expect(result.fileReplacements).toEqual([
|
||||
{
|
||||
replace: '/root/apps/environment/environment.ts',
|
||||
with: '/root/apps/environment/environment.prod.ts'
|
||||
},
|
||||
{
|
||||
replace: '/root/module1.ts',
|
||||
with: '/root/module2.ts'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
133
packages/builders/src/utils/normalize.ts
Normal file
133
packages/builders/src/utils/normalize.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import {
|
||||
AssetPattern,
|
||||
AssetPatternObject,
|
||||
NormalizedBrowserBuilderSchema
|
||||
} from '@angular-devkit/build-angular';
|
||||
import { Path, normalize } from '@angular-devkit/core';
|
||||
import { resolve, dirname, relative, basename } from 'path';
|
||||
import { statSync } from 'fs';
|
||||
import { BuildBuilderOptions } from './types';
|
||||
import { WebBuildBuilderOptions } from '../web/build/web-build.builder';
|
||||
import { BuildOptions } from '@angular-devkit/build-angular/src/angular-cli-files/models/build-options';
|
||||
|
||||
export interface FileReplacement {
|
||||
replace: string;
|
||||
with: string;
|
||||
}
|
||||
|
||||
export function normalizeBuildOptions<T extends BuildBuilderOptions>(
|
||||
options: T,
|
||||
root: string,
|
||||
sourceRoot: Path
|
||||
): T {
|
||||
return {
|
||||
...options,
|
||||
root: root,
|
||||
sourceRoot: sourceRoot,
|
||||
main: resolve(root, options.main),
|
||||
outputPath: resolve(root, options.outputPath),
|
||||
tsConfig: resolve(root, options.tsConfig),
|
||||
fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
|
||||
assets: normalizeAssets(options.assets, root, sourceRoot),
|
||||
webpackConfig: options.webpackConfig
|
||||
? resolve(root, options.webpackConfig)
|
||||
: options.webpackConfig
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeWebBuildOptions(
|
||||
options: WebBuildBuilderOptions,
|
||||
root: string,
|
||||
sourceRoot: Path
|
||||
): WebBuildBuilderOptions {
|
||||
return {
|
||||
...normalizeBuildOptions(options, root, sourceRoot),
|
||||
optimization:
|
||||
typeof options.optimization !== 'object'
|
||||
? {
|
||||
scripts: options.optimization,
|
||||
styles: options.optimization
|
||||
}
|
||||
: options.optimization,
|
||||
sourceMap:
|
||||
typeof options.sourceMap === 'object'
|
||||
? options.sourceMap
|
||||
: {
|
||||
scripts: options.sourceMap,
|
||||
styles: options.sourceMap,
|
||||
hidden: false,
|
||||
vendors: false
|
||||
},
|
||||
polyfills: options.polyfills ? resolve(root, options.polyfills) : undefined,
|
||||
es2015Polyfills: options.es2015Polyfills
|
||||
? resolve(root, options.es2015Polyfills)
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function convertBuildOptions(
|
||||
buildOptions: WebBuildBuilderOptions
|
||||
): BuildOptions {
|
||||
const options = buildOptions as any;
|
||||
return <NormalizedBrowserBuilderSchema>{
|
||||
...options,
|
||||
buildOptimizer: options.optimization,
|
||||
aot: false,
|
||||
forkTypeChecker: false,
|
||||
lazyModules: [] as string[],
|
||||
assets: [] as string[]
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAssets(
|
||||
assets: AssetPattern[],
|
||||
root: string,
|
||||
sourceRoot: Path
|
||||
): AssetPatternObject[] {
|
||||
return assets.map(asset => {
|
||||
if (typeof asset === 'string') {
|
||||
const assetPath = normalize(asset);
|
||||
const resolvedAssetPath = resolve(root, assetPath);
|
||||
const resolvedSourceRoot = resolve(root, sourceRoot);
|
||||
|
||||
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
|
||||
throw new Error(
|
||||
`The ${resolvedAssetPath} asset path must start with the project source root: ${sourceRoot}`
|
||||
);
|
||||
}
|
||||
|
||||
const isDirectory = statSync(resolvedAssetPath).isDirectory();
|
||||
const input = isDirectory
|
||||
? resolvedAssetPath
|
||||
: dirname(resolvedAssetPath);
|
||||
const output = relative(resolvedSourceRoot, resolve(root, input));
|
||||
const glob = isDirectory ? '**/*' : basename(resolvedAssetPath);
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
glob
|
||||
};
|
||||
} else {
|
||||
if (asset.output.startsWith('..')) {
|
||||
throw new Error(
|
||||
'An asset cannot be written to a location outside of the output path.'
|
||||
);
|
||||
}
|
||||
return {
|
||||
...asset,
|
||||
// Now we remove starting slash to make Webpack place it from the output root.
|
||||
output: asset.output.replace(/^\//, '')
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeFileReplacements(
|
||||
root: string,
|
||||
fileReplacements: FileReplacement[]
|
||||
): FileReplacement[] {
|
||||
return fileReplacements.map(fileReplacement => ({
|
||||
replace: resolve(root, fileReplacement.replace),
|
||||
with: resolve(root, fileReplacement.with)
|
||||
}));
|
||||
}
|
||||
54
packages/builders/src/utils/serve-path.ts
Normal file
54
packages/builders/src/utils/serve-path.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { WebBuildBuilderOptions } from '../web/build/web-build.builder';
|
||||
|
||||
export function buildServePath(browserOptions: WebBuildBuilderOptions) {
|
||||
let servePath =
|
||||
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
||||
'/';
|
||||
if (servePath.endsWith('/')) {
|
||||
servePath = servePath.substr(0, servePath.length - 1);
|
||||
}
|
||||
if (!servePath.startsWith('/')) {
|
||||
servePath = `/${servePath}`;
|
||||
}
|
||||
|
||||
return servePath;
|
||||
}
|
||||
|
||||
export function _findDefaultServePath(
|
||||
baseHref?: string,
|
||||
deployUrl?: string
|
||||
): string | null {
|
||||
if (!baseHref && !deployUrl) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (
|
||||
/^(\w+:)?\/\//.test(baseHref || '') ||
|
||||
/^(\w+:)?\/\//.test(deployUrl || '')
|
||||
) {
|
||||
// If baseHref or deployUrl is absolute, unsupported by ng serve
|
||||
return null;
|
||||
}
|
||||
|
||||
// normalize baseHref
|
||||
// for ng serve the starting base is always `/` so a relative
|
||||
// and root relative value are identical
|
||||
const baseHrefParts = (baseHref || '').split('/').filter(part => part !== '');
|
||||
if (baseHref && !baseHref.endsWith('/')) {
|
||||
baseHrefParts.pop();
|
||||
}
|
||||
const normalizedBaseHref =
|
||||
baseHrefParts.length === 0 ? '/' : `/${baseHrefParts.join('/')}/`;
|
||||
|
||||
if (deployUrl && deployUrl[0] === '/') {
|
||||
if (baseHref && baseHref[0] === '/' && normalizedBaseHref !== deployUrl) {
|
||||
// If baseHref and deployUrl are root relative and not equivalent, unsupported by ng serve
|
||||
return null;
|
||||
}
|
||||
|
||||
return deployUrl;
|
||||
}
|
||||
|
||||
// Join together baseHref and deployUrl
|
||||
return `${normalizedBaseHref}${deployUrl || ''}`;
|
||||
}
|
||||
39
packages/builders/src/utils/types.ts
Normal file
39
packages/builders/src/utils/types.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { FileReplacement } from './normalize';
|
||||
import { AssetPattern } from '@angular-devkit/build-angular';
|
||||
import { Path } from '@angular-devkit/core';
|
||||
|
||||
export interface OptimizationOptions {
|
||||
scripts: boolean;
|
||||
styles: boolean;
|
||||
}
|
||||
|
||||
export interface SourceMapOptions {
|
||||
scripts: boolean;
|
||||
styles: boolean;
|
||||
vendors: boolean;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface BuildBuilderOptions {
|
||||
main: string;
|
||||
outputPath: string;
|
||||
tsConfig: string;
|
||||
watch?: boolean;
|
||||
sourceMap?: boolean | SourceMapOptions;
|
||||
optimization?: boolean | OptimizationOptions;
|
||||
showCircularDependencies?: boolean;
|
||||
maxWorkers?: number;
|
||||
poll?: number;
|
||||
|
||||
fileReplacements: FileReplacement[];
|
||||
assets?: AssetPattern[];
|
||||
|
||||
progress?: boolean;
|
||||
statsJson?: boolean;
|
||||
extractLicenses?: boolean;
|
||||
|
||||
webpackConfig?: string;
|
||||
|
||||
root?: string;
|
||||
sourceRoot?: Path;
|
||||
}
|
||||
11
packages/builders/src/utils/typescript.ts
Normal file
11
packages/builders/src/utils/typescript.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import * as ts from 'typescript';
|
||||
import { dirname } from 'path';
|
||||
|
||||
export function readTsConfig(tsConfigPath: string) {
|
||||
const readResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
||||
return ts.parseJsonConfigFileContent(
|
||||
readResult.config,
|
||||
ts.sys,
|
||||
dirname(tsConfigPath)
|
||||
);
|
||||
}
|
||||
@ -1,37 +1,41 @@
|
||||
import { getWebpackConfig } from './config';
|
||||
import { BuildNodeBuilderOptions } from '../node-build.builder';
|
||||
import { getBaseWebpackPartial } from './config';
|
||||
import { normalize, getSystemPath } from '@angular-devkit/core';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
||||
import CircularDependencyPlugin = require('circular-dependency-plugin');
|
||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
||||
import { ProgressPlugin } from 'webpack';
|
||||
import { BuildBuilderOptions } from '../types';
|
||||
|
||||
describe('getWebpackConfig', () => {
|
||||
let input: BuildNodeBuilderOptions;
|
||||
describe('getBaseWebpackPartial', () => {
|
||||
let input: BuildBuilderOptions;
|
||||
beforeEach(() => {
|
||||
input = {
|
||||
main: 'main.ts',
|
||||
outputPath: 'dist',
|
||||
tsConfig: 'tsconfig.json',
|
||||
externalDependencies: 'all',
|
||||
fileReplacements: [],
|
||||
root: getSystemPath(normalize('/root'))
|
||||
root: getSystemPath(normalize('/root')),
|
||||
statsJson: false
|
||||
};
|
||||
});
|
||||
|
||||
describe('unconditional options', () => {
|
||||
it('should have output options', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
it('should have output filename', () => {
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.output.filename).toEqual('main.js');
|
||||
expect(result.output.libraryTarget).toEqual('commonjs');
|
||||
});
|
||||
|
||||
it('should have output path', () => {
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.output.path).toEqual('dist');
|
||||
});
|
||||
|
||||
it('should have a rule for typescript', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
const typescriptRule = result.module.rules.find(rule =>
|
||||
(rule.test as RegExp).test('app/main.ts')
|
||||
@ -42,7 +46,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should split typescript type checking into a separate workers', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
const typeCheckerPlugin = result.plugins.find(
|
||||
plugin => plugin instanceof ForkTsCheckerWebpackPlugin
|
||||
@ -50,24 +54,24 @@ describe('getWebpackConfig', () => {
|
||||
expect(typeCheckerPlugin).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should target node', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
|
||||
expect(result.target).toEqual('node');
|
||||
});
|
||||
|
||||
it('should disable performance hints', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.performance).toEqual({
|
||||
hints: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve typescript and javascript', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
it('should resolve ts, tsx, mjs, js, and jsx', () => {
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.resolve.extensions).toEqual(['.ts', '.mjs', '.js']);
|
||||
expect(result.resolve.extensions).toEqual([
|
||||
'.ts',
|
||||
'.tsx',
|
||||
'.mjs',
|
||||
'.js',
|
||||
'.jsx'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should include module and main in mainFields', () => {
|
||||
@ -77,29 +81,25 @@ describe('getWebpackConfig', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
expect(result.resolve.mainFields).toContain('module');
|
||||
expect(result.resolve.mainFields).toContain('main');
|
||||
});
|
||||
|
||||
it('should not polyfill node apis', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
|
||||
expect(result.node).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the main option', () => {
|
||||
it('should set the correct entry options', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.entry).toEqual(['main.ts']);
|
||||
expect(result.entry).toEqual({
|
||||
main: ['main.ts']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('the output option', () => {
|
||||
it('should set the correct output options', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.output.path).toEqual('dist');
|
||||
});
|
||||
@ -107,7 +107,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the tsConfig option', () => {
|
||||
it('should set the correct typescript rule', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(
|
||||
result.module.rules.find(rule => rule.loader === 'ts-loader').options
|
||||
@ -119,7 +119,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should set the correct options for the type checker plugin', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
const typeCheckerPlugin = result.plugins.find(
|
||||
plugin => plugin instanceof ForkTsCheckerWebpackPlugin
|
||||
@ -136,7 +136,7 @@ describe('getWebpackConfig', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
expect(result.resolve.alias).toEqual({
|
||||
'@npmScope/libraryName': '/root/libs/libraryName/src/index.ts'
|
||||
});
|
||||
@ -149,7 +149,7 @@ describe('getWebpackConfig', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
expect(result.resolve.mainFields).toContain('es2015');
|
||||
});
|
||||
});
|
||||
@ -160,7 +160,7 @@ describe('getWebpackConfig', () => {
|
||||
options: {}
|
||||
});
|
||||
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
fileReplacements: [
|
||||
{
|
||||
@ -176,39 +176,9 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('the externalDependencies option', () => {
|
||||
it('should change all node_modules to commonjs imports', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const callback = jest.fn();
|
||||
result.externals[0](null, '@angular/core', callback);
|
||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs @angular/core');
|
||||
});
|
||||
|
||||
it('should change given module names to commonjs imports but not others', () => {
|
||||
const result = getWebpackConfig({
|
||||
...input,
|
||||
externalDependencies: ['module1']
|
||||
});
|
||||
const callback = jest.fn();
|
||||
result.externals[0](null, 'module1', callback);
|
||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs module1');
|
||||
result.externals[0](null, '@angular/core', callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should not change any modules to commonjs imports', () => {
|
||||
const result = getWebpackConfig({
|
||||
...input,
|
||||
externalDependencies: 'none'
|
||||
});
|
||||
|
||||
expect(result.externals).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('the watch option', () => {
|
||||
it('should enable file watching', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
watch: true
|
||||
});
|
||||
@ -217,9 +187,20 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('the poll option', () => {
|
||||
it('should determine the polling rate', () => {
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
poll: 1000
|
||||
});
|
||||
|
||||
expect(result.watchOptions.poll).toEqual(1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the source map option', () => {
|
||||
it('should enable source-map devtool', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
sourceMap: true
|
||||
});
|
||||
@ -228,7 +209,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should enable source-map devtool', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
sourceMap: false
|
||||
});
|
||||
@ -240,7 +221,7 @@ describe('getWebpackConfig', () => {
|
||||
describe('the optimization option', () => {
|
||||
describe('by default', () => {
|
||||
it('should set the mode to development', () => {
|
||||
const result = getWebpackConfig(input);
|
||||
const result = getBaseWebpackPartial(input);
|
||||
|
||||
expect(result.mode).toEqual('development');
|
||||
});
|
||||
@ -248,7 +229,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('when true', () => {
|
||||
it('should set the mode to production', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
optimization: true
|
||||
});
|
||||
@ -257,7 +238,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should not minify', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
optimization: true
|
||||
});
|
||||
@ -266,7 +247,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should not concatenate modules', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
optimization: true
|
||||
});
|
||||
@ -278,7 +259,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the max workers option', () => {
|
||||
it('should set the maximum workers for the type checker', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
maxWorkers: 1
|
||||
});
|
||||
@ -292,7 +273,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the assets option', () => {
|
||||
it('should add a copy-webpack-plugin', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
assets: [
|
||||
{
|
||||
@ -319,7 +300,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the circular dependencies option', () => {
|
||||
it('should show warnings for circular dependencies', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
showCircularDependencies: true
|
||||
});
|
||||
@ -332,7 +313,7 @@ describe('getWebpackConfig', () => {
|
||||
});
|
||||
|
||||
it('should exclude node modules', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
showCircularDependencies: true
|
||||
});
|
||||
@ -348,7 +329,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the extract licenses option', () => {
|
||||
it('should extract licenses to a separate file', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
extractLicenses: true
|
||||
});
|
||||
@ -368,7 +349,7 @@ describe('getWebpackConfig', () => {
|
||||
|
||||
describe('the progress option', () => {
|
||||
it('should show build progress', () => {
|
||||
const result = getWebpackConfig({
|
||||
const result = getBaseWebpackPartial({
|
||||
...input,
|
||||
progress: true
|
||||
});
|
||||
@ -2,39 +2,40 @@ import * as webpack from 'webpack';
|
||||
import { Configuration, ProgressPlugin } from 'webpack';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
||||
import CircularDependencyPlugin = require('circular-dependency-plugin');
|
||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
||||
|
||||
import { BuildNodeBuilderOptions } from '../node-build.builder';
|
||||
import * as nodeExternals from 'webpack-node-externals';
|
||||
import { AssetPatternObject } from '@angular-devkit/build-angular';
|
||||
import { BuildBuilderOptions } from '../types';
|
||||
import { readTsConfig } from '../typescript';
|
||||
|
||||
export const OUT_FILENAME = 'main.js';
|
||||
|
||||
export function getWebpackConfig(
|
||||
options: BuildNodeBuilderOptions
|
||||
export function getBaseWebpackPartial(
|
||||
options: BuildBuilderOptions
|
||||
): Configuration {
|
||||
const compilerOptions = getCompilerOptions(options.tsConfig);
|
||||
const { options: compilerOptions } = readTsConfig(options.tsConfig);
|
||||
const supportsEs2015 =
|
||||
compilerOptions.target !== ts.ScriptTarget.ES3 &&
|
||||
compilerOptions.target !== ts.ScriptTarget.ES5;
|
||||
const webpackConfig: Configuration = {
|
||||
entry: [options.main],
|
||||
entry: {
|
||||
main: [options.main]
|
||||
},
|
||||
devtool: options.sourceMap ? 'source-map' : 'eval',
|
||||
mode: options.optimization ? 'production' : 'development',
|
||||
output: {
|
||||
path: options.outputPath,
|
||||
filename: OUT_FILENAME,
|
||||
libraryTarget: 'commonjs'
|
||||
filename: OUT_FILENAME
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
test: /\.tsx?$/,
|
||||
loader: `ts-loader`,
|
||||
options: {
|
||||
configFile: options.tsConfig,
|
||||
@ -46,12 +47,10 @@ export function getWebpackConfig(
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.mjs', '.js'],
|
||||
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
|
||||
alias: getAliases(options, compilerOptions),
|
||||
mainFields: [...(supportsEs2015 ? ['es2015'] : []), 'module', 'main']
|
||||
},
|
||||
target: 'node',
|
||||
node: false,
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
@ -61,7 +60,10 @@ export function getWebpackConfig(
|
||||
workers: options.maxWorkers || ForkTsCheckerWebpackPlugin.TWO_CPUS_FREE
|
||||
})
|
||||
],
|
||||
watch: options.watch
|
||||
watch: options.watch,
|
||||
watchOptions: {
|
||||
poll: options.poll
|
||||
}
|
||||
};
|
||||
|
||||
const extraPlugins: webpack.Plugin[] = [];
|
||||
@ -88,21 +90,6 @@ export function getWebpackConfig(
|
||||
);
|
||||
}
|
||||
|
||||
if (options.externalDependencies === 'all') {
|
||||
webpackConfig.externals = [nodeExternals()];
|
||||
} else if (Array.isArray(options.externalDependencies)) {
|
||||
webpackConfig.externals = [
|
||||
function(context, request, callback: Function) {
|
||||
if (options.externalDependencies.includes(request)) {
|
||||
// not bundled
|
||||
return callback(null, 'commonjs ' + request);
|
||||
}
|
||||
// bundled
|
||||
callback();
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// process asset entries
|
||||
if (options.assets) {
|
||||
const copyWebpackPluginPatterns = options.assets.map(
|
||||
@ -145,7 +132,7 @@ export function getWebpackConfig(
|
||||
}
|
||||
|
||||
function getAliases(
|
||||
options: BuildNodeBuilderOptions,
|
||||
options: BuildBuilderOptions,
|
||||
compilerOptions: ts.CompilerOptions
|
||||
): { [key: string]: string } {
|
||||
const replacements = [
|
||||
@ -165,13 +152,3 @@ function getAliases(
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
function getCompilerOptions(tsConfigPath: string) {
|
||||
const readResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
||||
const tsConfig = ts.parseJsonConfigFileContent(
|
||||
readResult.config,
|
||||
ts.sys,
|
||||
dirname(tsConfigPath)
|
||||
);
|
||||
return tsConfig.options;
|
||||
}
|
||||
352
packages/builders/src/utils/webpack/devserver.config.spec.ts
Normal file
352
packages/builders/src/utils/webpack/devserver.config.spec.ts
Normal file
@ -0,0 +1,352 @@
|
||||
import { getSystemPath, normalize, join } from '@angular-devkit/core';
|
||||
import { WebBuildBuilderOptions } from '../../web/build/web-build.builder';
|
||||
import { WebDevServerOptions } from '../../web/dev-server/web-dev-server.builder';
|
||||
import { getDevServerConfig } from './devserver.config';
|
||||
import { Logger } from '@angular-devkit/core/src/logger';
|
||||
import * as ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
|
||||
describe('getDevServerConfig', () => {
|
||||
let buildInput: WebBuildBuilderOptions;
|
||||
let serveInput: WebDevServerOptions;
|
||||
let mockCompilerOptions: any;
|
||||
let logger: Logger;
|
||||
|
||||
beforeEach(() => {
|
||||
buildInput = {
|
||||
main: 'main.ts',
|
||||
index: 'index.html',
|
||||
budgets: [],
|
||||
baseHref: '/',
|
||||
deployUrl: '/',
|
||||
sourceMap: {
|
||||
scripts: true,
|
||||
styles: true,
|
||||
hidden: false,
|
||||
vendors: false
|
||||
},
|
||||
optimization: {
|
||||
scripts: false,
|
||||
styles: false
|
||||
},
|
||||
styles: [],
|
||||
scripts: [],
|
||||
outputPath: 'dist',
|
||||
tsConfig: 'tsconfig.json',
|
||||
fileReplacements: [],
|
||||
root: getSystemPath(normalize(__dirname)),
|
||||
sourceRoot: normalize('packages/builders')
|
||||
};
|
||||
|
||||
serveInput = {
|
||||
host: 'localhost',
|
||||
port: 4200,
|
||||
buildTarget: 'webapp:build',
|
||||
ssl: false,
|
||||
liveReload: true,
|
||||
open: false,
|
||||
watch: true
|
||||
};
|
||||
|
||||
mockCompilerOptions = {
|
||||
target: 'es2015',
|
||||
paths: { path: ['mapped/path'] }
|
||||
};
|
||||
|
||||
spyOn(ts, 'readConfigFile').and.callFake(() => ({
|
||||
config: {
|
||||
compilerOptions: mockCompilerOptions
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
describe('unconditional settings', () => {
|
||||
it('should allow requests from any domain', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.headers['Access-Control-Allow-Origin']).toEqual('*');
|
||||
});
|
||||
|
||||
it('should not display warnings in the overlay', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.warnings).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not emit stats', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.stats).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not have a contentBase', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.contentBase).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('host option', () => {
|
||||
it('should set the host option', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.host).toEqual('localhost');
|
||||
});
|
||||
});
|
||||
|
||||
describe('port option', () => {
|
||||
it('should set the port option', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.port).toEqual(4200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('build options', () => {
|
||||
it('should set the history api fallback options', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.historyApiFallback).toEqual({
|
||||
index: '//index.html',
|
||||
disableDotRule: true,
|
||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
||||
});
|
||||
});
|
||||
|
||||
describe('optimization', () => {
|
||||
it('should not compress assets by default', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(false);
|
||||
});
|
||||
|
||||
it('should compress assets if scripts optimization is on', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: true,
|
||||
styles: false
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
});
|
||||
|
||||
it('should compress assets if styles optimization is on', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: false,
|
||||
styles: true
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
});
|
||||
|
||||
it('should compress assets if all optimization is on', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: true,
|
||||
styles: true
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
});
|
||||
|
||||
it('should show an overlay when optimization is off', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: false,
|
||||
styles: false
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.errors).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not show an overlay when optimization is on', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: true,
|
||||
styles: true
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.errors).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('liveReload option', () => {
|
||||
it('should push the live reload entry to the main entry', () => {
|
||||
const result = getDevServerConfig(buildInput, serveInput, logger);
|
||||
|
||||
expect(result.entry['main']).toContain(
|
||||
`${require.resolve('webpack-dev-server/client')}?http://0.0.0.0:0`
|
||||
);
|
||||
});
|
||||
|
||||
it('should push the correct entry when publicHost option is used', () => {
|
||||
const result = getDevServerConfig(
|
||||
buildInput,
|
||||
{
|
||||
...serveInput,
|
||||
publicHost: 'www.example.com'
|
||||
},
|
||||
logger
|
||||
);
|
||||
|
||||
expect(result.entry['main']).toContain(
|
||||
`${require.resolve(
|
||||
'webpack-dev-server/client'
|
||||
)}?http://www.example.com/`
|
||||
);
|
||||
});
|
||||
|
||||
it('should push the correct entry when publicHost and ssl options are used', () => {
|
||||
const result = getDevServerConfig(
|
||||
buildInput,
|
||||
{
|
||||
...serveInput,
|
||||
ssl: true,
|
||||
publicHost: 'www.example.com'
|
||||
},
|
||||
logger
|
||||
);
|
||||
|
||||
expect(result.entry['main']).toContain(
|
||||
`${require.resolve(
|
||||
'webpack-dev-server/client'
|
||||
)}?https://www.example.com/`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ssl option', () => {
|
||||
it('should set https to false if not on', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
{
|
||||
...buildInput,
|
||||
optimization: {
|
||||
scripts: true,
|
||||
styles: true
|
||||
}
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.https).toEqual(false);
|
||||
});
|
||||
|
||||
it('should configure it with the key and cert provided when on', () => {
|
||||
spyOn(fs, 'readFileSync').and.callFake(path => {
|
||||
if (path.endsWith('ssl.key')) {
|
||||
return 'sslKeyContents';
|
||||
} else if (path.endsWith('ssl.cert')) {
|
||||
return 'sslCertContents';
|
||||
}
|
||||
});
|
||||
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
{
|
||||
...serveInput,
|
||||
ssl: true,
|
||||
sslKey: 'ssl.key',
|
||||
sslCert: 'ssl.cert'
|
||||
},
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.https).toEqual({
|
||||
key: 'sslKeyContents',
|
||||
cert: 'sslCertContents'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('proxyConfig option', () => {
|
||||
it('should setProxyConfig', () => {
|
||||
jest.mock(
|
||||
join(normalize(__dirname), 'proxy.conf'),
|
||||
() => ({
|
||||
proxyConfig: 'proxyConfig'
|
||||
}),
|
||||
{
|
||||
virtual: true
|
||||
}
|
||||
);
|
||||
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
buildInput,
|
||||
{
|
||||
...serveInput,
|
||||
proxyConfig: 'proxy.conf'
|
||||
},
|
||||
logger
|
||||
) as any;
|
||||
|
||||
expect(result.proxy).toEqual({
|
||||
proxyConfig: 'proxyConfig'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
108
packages/builders/src/utils/webpack/devserver.config.ts
Normal file
108
packages/builders/src/utils/webpack/devserver.config.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import {
|
||||
Configuration as WebpackDevServerConfiguration,
|
||||
HistoryApiFallbackConfig
|
||||
} from 'webpack-dev-server';
|
||||
import { readFileSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
import { WebBuildBuilderOptions } from '../../web/build/web-build.builder';
|
||||
import { WebDevServerOptions } from '../../web/dev-server/web-dev-server.builder';
|
||||
import { getWebConfig } from './web.config';
|
||||
import { Configuration } from 'webpack';
|
||||
import { Logger } from '@angular-devkit/core/src/logger';
|
||||
import { OptimizationOptions } from '../types';
|
||||
import { buildServePath } from '../serve-path';
|
||||
|
||||
export function getDevServerConfig(
|
||||
buildOptions: WebBuildBuilderOptions,
|
||||
serveOptions: WebDevServerOptions,
|
||||
logger: Logger
|
||||
) {
|
||||
const webpackConfig: Configuration = getWebConfig(buildOptions, logger);
|
||||
(webpackConfig as any).devServer = getDevServerPartial(
|
||||
serveOptions,
|
||||
buildOptions
|
||||
);
|
||||
if (serveOptions.liveReload) {
|
||||
webpackConfig.entry['main'].unshift(getLiveReloadEntry(serveOptions));
|
||||
}
|
||||
return webpackConfig;
|
||||
}
|
||||
|
||||
function getLiveReloadEntry(serveOptions: WebDevServerOptions) {
|
||||
let clientAddress = `${serveOptions.ssl ? 'https' : 'http'}://0.0.0.0:0`;
|
||||
if (serveOptions.publicHost) {
|
||||
let publicHost = serveOptions.publicHost;
|
||||
if (!/^\w+:\/\//.test(publicHost)) {
|
||||
publicHost = `${serveOptions.ssl ? 'https' : 'http'}://${publicHost}`;
|
||||
}
|
||||
const clientUrl = url.parse(publicHost);
|
||||
serveOptions.publicHost = clientUrl.host;
|
||||
clientAddress = url.format(clientUrl);
|
||||
}
|
||||
let webpackDevServerPath;
|
||||
try {
|
||||
webpackDevServerPath = require.resolve('webpack-dev-server/client');
|
||||
} catch {
|
||||
throw new Error('The "webpack-dev-server" package could not be found.');
|
||||
}
|
||||
return `${webpackDevServerPath}?${clientAddress}`;
|
||||
}
|
||||
|
||||
function getDevServerPartial(
|
||||
options: WebDevServerOptions,
|
||||
buildOptions: WebBuildBuilderOptions
|
||||
): WebpackDevServerConfiguration {
|
||||
const servePath = buildServePath(buildOptions);
|
||||
|
||||
const {
|
||||
scripts: scriptsOptimization,
|
||||
styles: stylesOptimization
|
||||
} = buildOptions.optimization as OptimizationOptions;
|
||||
|
||||
const config: WebpackDevServerConfiguration = {
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||
historyApiFallback: {
|
||||
index: `${servePath}/${path.basename(buildOptions.index)}`,
|
||||
disableDotRule: true,
|
||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
||||
} as HistoryApiFallbackConfig,
|
||||
stats: false,
|
||||
compress: scriptsOptimization || stylesOptimization,
|
||||
https: options.ssl,
|
||||
overlay: {
|
||||
errors: !(scriptsOptimization || stylesOptimization),
|
||||
warnings: false
|
||||
},
|
||||
watchOptions: {
|
||||
poll: buildOptions.poll
|
||||
},
|
||||
public: options.publicHost,
|
||||
publicPath: servePath,
|
||||
contentBase: false
|
||||
};
|
||||
|
||||
if (options.ssl && options.sslKey && options.sslCert) {
|
||||
config.https = getSslConfig(buildOptions.root, options);
|
||||
}
|
||||
|
||||
if (options.proxyConfig) {
|
||||
config.proxy = getProxyConfig(buildOptions.root, options);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function getSslConfig(root: string, options: WebDevServerOptions) {
|
||||
return {
|
||||
key: readFileSync(path.resolve(root, options.sslKey), 'utf-8'),
|
||||
cert: readFileSync(path.resolve(root, options.sslCert), 'utf-8')
|
||||
};
|
||||
}
|
||||
|
||||
function getProxyConfig(root: string, options: WebDevServerOptions) {
|
||||
const proxyPath = path.resolve(root, options.proxyConfig as string);
|
||||
return require(proxyPath);
|
||||
}
|
||||
67
packages/builders/src/utils/webpack/node.config.spec.ts
Normal file
67
packages/builders/src/utils/webpack/node.config.spec.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { getNodeWebpackConfig } from './node.config';
|
||||
import { getSystemPath, normalize } from '@angular-devkit/core';
|
||||
import { BuildNodeBuilderOptions } from '../../node/build/node-build.builder';
|
||||
|
||||
describe('getNodePartial', () => {
|
||||
let input: BuildNodeBuilderOptions;
|
||||
beforeEach(() => {
|
||||
input = {
|
||||
main: 'main.ts',
|
||||
outputPath: 'dist',
|
||||
tsConfig: 'tsconfig.json',
|
||||
externalDependencies: 'all',
|
||||
fileReplacements: [],
|
||||
root: getSystemPath(normalize('/root')),
|
||||
statsJson: false
|
||||
};
|
||||
});
|
||||
|
||||
describe('unconditionally', () => {
|
||||
it('should target commonjs', () => {
|
||||
const result = getNodeWebpackConfig(input);
|
||||
expect(result.output.libraryTarget).toEqual('commonjs');
|
||||
});
|
||||
|
||||
it('should target node', () => {
|
||||
const result = getNodeWebpackConfig(input);
|
||||
|
||||
expect(result.target).toEqual('node');
|
||||
});
|
||||
|
||||
it('should not polyfill node apis', () => {
|
||||
const result = getNodeWebpackConfig(input);
|
||||
|
||||
expect(result.node).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the externalDependencies option', () => {
|
||||
it('should change all node_modules to commonjs imports', () => {
|
||||
const result = getNodeWebpackConfig(input);
|
||||
const callback = jest.fn();
|
||||
result.externals[0](null, '@angular/core', callback);
|
||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs @angular/core');
|
||||
});
|
||||
|
||||
it('should change given module names to commonjs imports but not others', () => {
|
||||
const result = getNodeWebpackConfig({
|
||||
...input,
|
||||
externalDependencies: ['module1']
|
||||
});
|
||||
const callback = jest.fn();
|
||||
result.externals[0](null, 'module1', callback);
|
||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs module1');
|
||||
result.externals[0](null, '@angular/core', callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should not change any modules to commonjs imports', () => {
|
||||
const result = getNodeWebpackConfig({
|
||||
...input,
|
||||
externalDependencies: 'none'
|
||||
});
|
||||
|
||||
expect(result.externals).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
38
packages/builders/src/utils/webpack/node.config.ts
Normal file
38
packages/builders/src/utils/webpack/node.config.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Configuration } from 'webpack';
|
||||
import * as mergeWebpack from 'webpack-merge';
|
||||
import * as nodeExternals from 'webpack-node-externals';
|
||||
|
||||
import { BuildNodeBuilderOptions } from '../../node/build/node-build.builder';
|
||||
import { getBaseWebpackPartial } from './config';
|
||||
|
||||
function getNodePartial(options: BuildNodeBuilderOptions) {
|
||||
const webpackConfig: Configuration = {
|
||||
output: {
|
||||
libraryTarget: 'commonjs'
|
||||
},
|
||||
target: 'node',
|
||||
node: false
|
||||
};
|
||||
if (options.externalDependencies === 'all') {
|
||||
webpackConfig.externals = [nodeExternals()];
|
||||
} else if (Array.isArray(options.externalDependencies)) {
|
||||
webpackConfig.externals = [
|
||||
function(context, request, callback: Function) {
|
||||
if (options.externalDependencies.includes(request)) {
|
||||
// not bundled
|
||||
return callback(null, 'commonjs ' + request);
|
||||
}
|
||||
// bundled
|
||||
callback();
|
||||
}
|
||||
];
|
||||
}
|
||||
return webpackConfig;
|
||||
}
|
||||
|
||||
export function getNodeWebpackConfig(options: BuildNodeBuilderOptions) {
|
||||
return mergeWebpack([
|
||||
getBaseWebpackPartial(options),
|
||||
getNodePartial(options)
|
||||
]);
|
||||
}
|
||||
95
packages/builders/src/utils/webpack/web.config.spec.ts
Normal file
95
packages/builders/src/utils/webpack/web.config.spec.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { getSystemPath, normalize, Path } from '@angular-devkit/core';
|
||||
import { getWebConfig as getWebPartial } from './web.config';
|
||||
import { WebBuildBuilderOptions } from '../../web/build/web-build.builder';
|
||||
import { createConsoleLogger } from '@angular-devkit/core/node';
|
||||
import { Logger } from '@angular-devkit/core/src/logger';
|
||||
import * as ts from 'typescript';
|
||||
import { SourceMapDevToolPlugin } from 'webpack';
|
||||
|
||||
describe('getWebConfig', () => {
|
||||
let input: WebBuildBuilderOptions;
|
||||
let logger: Logger;
|
||||
let mockCompilerOptions: any;
|
||||
|
||||
beforeEach(() => {
|
||||
input = {
|
||||
main: 'main.ts',
|
||||
index: 'index.html',
|
||||
budgets: [],
|
||||
baseHref: '/',
|
||||
deployUrl: '/',
|
||||
sourceMap: {
|
||||
scripts: true,
|
||||
styles: true,
|
||||
hidden: false,
|
||||
vendors: false
|
||||
},
|
||||
optimization: {
|
||||
scripts: false,
|
||||
styles: false
|
||||
},
|
||||
styles: [],
|
||||
scripts: [],
|
||||
outputPath: 'dist',
|
||||
tsConfig: 'tsconfig.json',
|
||||
fileReplacements: [],
|
||||
root: getSystemPath(normalize(__dirname)),
|
||||
sourceRoot: normalize('packages/builders')
|
||||
};
|
||||
logger = createConsoleLogger();
|
||||
|
||||
mockCompilerOptions = {
|
||||
target: 'es2015',
|
||||
paths: { path: ['mapped/path'] }
|
||||
};
|
||||
|
||||
spyOn(ts, 'readConfigFile').and.callFake(() => ({
|
||||
config: {
|
||||
compilerOptions: mockCompilerOptions
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
it('should resolve the browser main field', () => {
|
||||
const result = getWebPartial(input, logger);
|
||||
expect(result.resolve.mainFields).toContain('browser');
|
||||
});
|
||||
|
||||
it('should use the style-loader to load styles', () => {
|
||||
const result = getWebPartial(input, logger);
|
||||
expect(
|
||||
result.module.rules.find(rule => rule.test.test('styles.css')).use[0]
|
||||
.loader
|
||||
).toEqual('style-loader');
|
||||
expect(
|
||||
result.module.rules.find(rule => rule.test.test('styles.scss')).use[0]
|
||||
.loader
|
||||
).toEqual('style-loader');
|
||||
});
|
||||
|
||||
describe('polyfills', () => {
|
||||
it('should set the polyfills entry', () => {
|
||||
const result = getWebPartial(
|
||||
{
|
||||
...input,
|
||||
polyfills: 'polyfills.ts'
|
||||
},
|
||||
logger
|
||||
);
|
||||
expect(result.entry.polyfills).toEqual(['polyfills.ts']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('es2015 polyfills', () => {
|
||||
it('should set the es2015-polyfills entry', () => {
|
||||
const result = getWebPartial(
|
||||
{
|
||||
...input,
|
||||
es2015Polyfills: 'polyfills.es2015.ts'
|
||||
},
|
||||
logger
|
||||
);
|
||||
expect(result.entry['es2015-polyfills']).toEqual(['polyfills.es2015.ts']);
|
||||
});
|
||||
});
|
||||
});
|
||||
103
packages/builders/src/utils/webpack/web.config.ts
Normal file
103
packages/builders/src/utils/webpack/web.config.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import * as mergeWebpack from 'webpack-merge';
|
||||
|
||||
import { WebBuildBuilderOptions } from '../../web/build/web-build.builder';
|
||||
import { getBaseWebpackPartial } from './config';
|
||||
import {
|
||||
getBrowserConfig,
|
||||
getStylesConfig,
|
||||
getCommonConfig
|
||||
} from '@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs';
|
||||
import { convertBuildOptions } from '../normalize';
|
||||
import { Configuration } from 'webpack';
|
||||
import { Logger } from '@angular-devkit/core/src/logger';
|
||||
import { readTsConfig } from '../typescript';
|
||||
import { resolve } from 'path';
|
||||
import {
|
||||
WebpackConfigOptions,
|
||||
BuildOptions
|
||||
} from '@angular-devkit/build-angular/src/angular-cli-files/models/build-options';
|
||||
import typescript = require('typescript');
|
||||
|
||||
export function getWebConfig(options: WebBuildBuilderOptions, logger: Logger) {
|
||||
const tsConfig = readTsConfig(options.tsConfig);
|
||||
const supportES2015 =
|
||||
tsConfig.options.target !== typescript.ScriptTarget.ES5 &&
|
||||
tsConfig.options.target !== typescript.ScriptTarget.ES3;
|
||||
const wco: WebpackConfigOptions<BuildOptions> = {
|
||||
root: options.root,
|
||||
projectRoot: resolve(options.root, options.sourceRoot),
|
||||
buildOptions: convertBuildOptions(options),
|
||||
supportES2015,
|
||||
logger,
|
||||
tsConfig,
|
||||
tsConfigPath: options.tsConfig
|
||||
};
|
||||
return mergeWebpack([
|
||||
_getBaseWebpackPartial(options),
|
||||
options.polyfills ? getPolyfillsPartial(options) : {},
|
||||
options.es2015Polyfills ? getEs2015PolyfillsPartial(options) : {},
|
||||
getStylesPartial(wco),
|
||||
getCommonPartial(wco),
|
||||
getBrowserConfig(wco)
|
||||
]);
|
||||
}
|
||||
function _getBaseWebpackPartial(options: WebBuildBuilderOptions) {
|
||||
let partial = getBaseWebpackPartial(options);
|
||||
delete partial.resolve.mainFields;
|
||||
return partial;
|
||||
}
|
||||
|
||||
function getCommonPartial(
|
||||
wco: WebpackConfigOptions<BuildOptions>
|
||||
): Configuration {
|
||||
const commonConfig: Configuration = <Configuration>getCommonConfig(wco);
|
||||
delete commonConfig.entry;
|
||||
// delete commonConfig.devtool;
|
||||
delete commonConfig.resolve.modules;
|
||||
delete commonConfig.resolve.extensions;
|
||||
delete commonConfig.output.path;
|
||||
delete commonConfig.module;
|
||||
return commonConfig;
|
||||
}
|
||||
|
||||
function getStylesPartial(
|
||||
wco: WebpackConfigOptions<BuildOptions>
|
||||
): Configuration {
|
||||
const partial = getStylesConfig(wco);
|
||||
partial.module.rules = partial.module.rules.map(rule => {
|
||||
if (!Array.isArray(rule.use)) {
|
||||
return rule;
|
||||
}
|
||||
rule.use = rule.use.map(loaderConfig => {
|
||||
if (
|
||||
typeof loaderConfig === 'object' &&
|
||||
loaderConfig.loader === 'raw-loader'
|
||||
) {
|
||||
return {
|
||||
loader: 'style-loader'
|
||||
};
|
||||
}
|
||||
return loaderConfig;
|
||||
});
|
||||
return rule;
|
||||
});
|
||||
return partial;
|
||||
}
|
||||
|
||||
function getPolyfillsPartial(options: WebBuildBuilderOptions): Configuration {
|
||||
return {
|
||||
entry: {
|
||||
polyfills: [options.polyfills]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getEs2015PolyfillsPartial(
|
||||
options: WebBuildBuilderOptions
|
||||
): Configuration {
|
||||
return {
|
||||
entry: {
|
||||
['es2015-polyfills']: [options.es2015Polyfills]
|
||||
}
|
||||
};
|
||||
}
|
||||
303
packages/builders/src/web/build/schema.json
Normal file
303
packages/builders/src/web/build/schema.json
Normal file
@ -0,0 +1,303 @@
|
||||
{
|
||||
"title": "Node Application Build Target",
|
||||
"description": "Node application build target options for Build Facade",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"main": {
|
||||
"type": "string",
|
||||
"description": "The name of the main entry-point file."
|
||||
},
|
||||
"tsConfig": {
|
||||
"type": "string",
|
||||
"description": "The name of the Typescript configuration file."
|
||||
},
|
||||
"watch": {
|
||||
"type": "boolean",
|
||||
"description": "Enable re-building when files change.",
|
||||
"default": false
|
||||
},
|
||||
"baseHref": {
|
||||
"type": "string",
|
||||
"description": "Base url for the application being built.",
|
||||
"default": "/"
|
||||
},
|
||||
"deployUrl": {
|
||||
"type": "string",
|
||||
"description": "URL where the application will be deployed."
|
||||
},
|
||||
"vendorChunk": {
|
||||
"type": "boolean",
|
||||
"description": "Use a separate bundle containing only vendor libraries.",
|
||||
"default": true
|
||||
},
|
||||
"commonChunk": {
|
||||
"type": "boolean",
|
||||
"description": "Use a separate bundle containing code used across multiple bundles.",
|
||||
"default": true
|
||||
},
|
||||
"sourceMap": {
|
||||
"description": "Output sourcemaps.",
|
||||
"default": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scripts": {
|
||||
"type": "boolean",
|
||||
"description": "Output sourcemaps for all scripts.",
|
||||
"default": true
|
||||
},
|
||||
"styles": {
|
||||
"type": "boolean",
|
||||
"description": "Output sourcemaps for all styles.",
|
||||
"default": true
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "Output sourcemaps used for error reporting tools.",
|
||||
"default": false
|
||||
},
|
||||
"vendor": {
|
||||
"type": "boolean",
|
||||
"description": "Resolve vendor packages sourcemaps.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"progress": {
|
||||
"type": "boolean",
|
||||
"description": "Log progress to the console while building.",
|
||||
"default": false
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"description": "List of static application assets.",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/assetPattern"
|
||||
}
|
||||
},
|
||||
"index": {
|
||||
"type": "string",
|
||||
"description": "HTML File which will be contain the application"
|
||||
},
|
||||
"scripts": {
|
||||
"type": "array",
|
||||
"description": "External Scripts which will be included before the main application entry",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"styles": {
|
||||
"type": "array",
|
||||
"description": "External Styles which will be included with the application",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"budgets": {
|
||||
"description": "Budget thresholds to ensure parts of your application stay within boundaries which you set.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/budget"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"namedChunks": {
|
||||
"type": "boolean",
|
||||
"description": "Names the produced bundles according to their entry file",
|
||||
"default": true
|
||||
},
|
||||
"outputHashing": {
|
||||
"type": "string",
|
||||
"description": "Define the output filename cache-busting hashing mode.",
|
||||
"default": "none",
|
||||
"enum": ["none", "all", "media", "bundles"]
|
||||
},
|
||||
"stylePreprocessorOptions": {
|
||||
"description": "Options to pass to style preprocessors.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"includePaths": {
|
||||
"description": "Paths to include. Paths will be resolved to project root.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"optimization": {
|
||||
"description": "Enables optimization of the build output.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scripts": {
|
||||
"type": "boolean",
|
||||
"description": "Enables optimization of the scripts output.",
|
||||
"default": true
|
||||
},
|
||||
"styles": {
|
||||
"type": "boolean",
|
||||
"description": "Enables optimization of the styles output.",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"extractCss": {
|
||||
"type": "boolean",
|
||||
"description": "Extract css into a .css file",
|
||||
"default": false
|
||||
},
|
||||
"es2015Polyfills": {
|
||||
"description": "Conditional polyfills loaded in browsers which do not support ES2015.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresourceIntegrity": {
|
||||
"type": "boolean",
|
||||
"description": "Enables the use of subresource integrity validation.",
|
||||
"default": false
|
||||
},
|
||||
"polyfills": {
|
||||
"type": "string",
|
||||
"description": "Polyfills to load before application"
|
||||
},
|
||||
"statsJson": {
|
||||
"type": "boolean",
|
||||
"description": "Generates a 'stats.json' file which can be analyzed using tools such as: #webpack-bundle-analyzer' or https://webpack.github.io/analyse.",
|
||||
"default": false
|
||||
},
|
||||
"extractLicenses": {
|
||||
"type": "boolean",
|
||||
"description": "Extract all licenses in a separate file, in the case of production builds only.",
|
||||
"default": false
|
||||
},
|
||||
"showCircularDependencies": {
|
||||
"type": "boolean",
|
||||
"description": "Show circular dependency warnings on builds.",
|
||||
"default": true
|
||||
},
|
||||
"maxWorkers": {
|
||||
"type": "number",
|
||||
"description": "Number of workers to use for type checking. (defaults to # of CPUS - 2)"
|
||||
},
|
||||
"fileReplacements": {
|
||||
"description": "Replace files with other files in the build.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"replace": {
|
||||
"type": "string"
|
||||
},
|
||||
"with": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["replace", "with"]
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"webpackConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to a function which takes a webpack config, some context and returns the resulting webpack config"
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main", "index"],
|
||||
|
||||
"definitions": {
|
||||
"assetPattern": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"glob": {
|
||||
"type": "string",
|
||||
"description": "The pattern to match."
|
||||
},
|
||||
"input": {
|
||||
"type": "string",
|
||||
"description": "The input directory path in which to apply 'glob'. Defaults to the project root."
|
||||
},
|
||||
"ignore": {
|
||||
"description": "An array of globs to ignore.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"budget": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "The type of budget.",
|
||||
"enum": ["all", "allScript", "any", "anyScript", "bundle", "initial"]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the bundle."
|
||||
},
|
||||
"baseline": {
|
||||
"type": "string",
|
||||
"description": "The baseline size for comparison."
|
||||
},
|
||||
"maximumWarning": {
|
||||
"type": "string",
|
||||
"description": "The maximum threshold for warning relative to the baseline."
|
||||
},
|
||||
"maximumError": {
|
||||
"type": "string",
|
||||
"description": "The maximum threshold for error relative to the baseline."
|
||||
},
|
||||
"minimumWarning": {
|
||||
"type": "string",
|
||||
"description": "The minimum threshold for warning relative to the baseline."
|
||||
},
|
||||
"minimumError": {
|
||||
"type": "string",
|
||||
"description": "The minimum threshold for error relative to the baseline."
|
||||
},
|
||||
"warning": {
|
||||
"type": "string",
|
||||
"description": "The threshold for warning relative to the baseline (min & max)."
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "The threshold for error relative to the baseline (min & max)."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["type"]
|
||||
}
|
||||
}
|
||||
}
|
||||
176
packages/builders/src/web/build/web-build.builder.spec.ts
Normal file
176
packages/builders/src/web/build/web-build.builder.spec.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import { normalize, join } from '@angular-devkit/core';
|
||||
import { TestLogger } from '@angular-devkit/architect/testing';
|
||||
import WebBuildBuilder from './web-build.builder';
|
||||
import { WebBuildBuilderOptions } from './web-build.builder';
|
||||
import { of } from 'rxjs';
|
||||
import * as fs from 'fs';
|
||||
|
||||
describe('WebBuildBuilder', () => {
|
||||
let builder: WebBuildBuilder;
|
||||
let testOptions: WebBuildBuilderOptions;
|
||||
|
||||
beforeEach(() => {
|
||||
builder = new WebBuildBuilder({
|
||||
host: <any>{},
|
||||
logger: new TestLogger('test'),
|
||||
workspace: <any>{
|
||||
root: __dirname
|
||||
},
|
||||
architect: <any>{},
|
||||
targetSpecifier: {
|
||||
project: 'webapp',
|
||||
target: 'build'
|
||||
}
|
||||
});
|
||||
testOptions = {
|
||||
index: 'apps/webapp/src/index.html',
|
||||
budgets: [],
|
||||
baseHref: '/',
|
||||
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
|
||||
};
|
||||
});
|
||||
|
||||
describe('run', () => {
|
||||
it('should call runWebpack', () => {
|
||||
const runWebpack = spyOn(
|
||||
builder.webpackBuilder,
|
||||
'runWebpack'
|
||||
).and.returnValue(
|
||||
of({
|
||||
success: true
|
||||
})
|
||||
);
|
||||
|
||||
builder.run({
|
||||
root: normalize(__dirname),
|
||||
sourceRoot: join(normalize(__dirname), 'apps/webapp'),
|
||||
projectType: 'application',
|
||||
builder: '@nrwl/builders:node-build',
|
||||
options: testOptions
|
||||
});
|
||||
|
||||
expect(runWebpack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit success', async () => {
|
||||
spyOn(builder.webpackBuilder, 'runWebpack').and.returnValue(
|
||||
of({
|
||||
success: true
|
||||
})
|
||||
);
|
||||
|
||||
const buildEvent = await builder
|
||||
.run({
|
||||
root: normalize(__dirname),
|
||||
sourceRoot: join(normalize(__dirname), 'apps/webapp'),
|
||||
projectType: 'application',
|
||||
builder: '@nrwl/builders:node-build',
|
||||
options: testOptions
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
expect(buildEvent.success).toEqual(true);
|
||||
});
|
||||
|
||||
describe('statsJson option', () => {
|
||||
beforeEach(() => {
|
||||
const stats = {
|
||||
stats: 'stats'
|
||||
};
|
||||
spyOn(builder.webpackBuilder, 'runWebpack').and.callFake((opts, cb) => {
|
||||
cb({
|
||||
toJson: () => stats,
|
||||
toString: () => JSON.stringify(stats)
|
||||
});
|
||||
return of({
|
||||
success: true
|
||||
});
|
||||
});
|
||||
spyOn(fs, 'writeFileSync');
|
||||
});
|
||||
|
||||
it('should generate a stats json', async () => {
|
||||
await builder
|
||||
.run({
|
||||
root: normalize(__dirname),
|
||||
sourceRoot: join(normalize(__dirname), 'apps/webapp'),
|
||||
projectType: 'application',
|
||||
builder: '@nrwl/builders:web-build',
|
||||
options: {
|
||||
...testOptions,
|
||||
statsJson: true
|
||||
}
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
join(normalize(__dirname), 'dist/apps/webapp/stats.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
stats: 'stats'
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webpackConfig option', () => {
|
||||
it('should require the specified function and use the return value', async () => {
|
||||
const runWebpack = spyOn(
|
||||
builder.webpackBuilder,
|
||||
'runWebpack'
|
||||
).and.returnValue(
|
||||
of({
|
||||
success: true
|
||||
})
|
||||
);
|
||||
const mockFunction = jest.fn(config => ({
|
||||
config: 'config'
|
||||
}));
|
||||
jest.mock(
|
||||
join(normalize(__dirname), 'apps/webapp/webpack.config.js'),
|
||||
() => mockFunction,
|
||||
{
|
||||
virtual: true
|
||||
}
|
||||
);
|
||||
await builder
|
||||
.run({
|
||||
root: normalize(__dirname),
|
||||
sourceRoot: join(normalize(__dirname), 'apps/webapp'),
|
||||
projectType: 'application',
|
||||
builder: '@nrwl/builders:web-build',
|
||||
options: {
|
||||
...testOptions,
|
||||
webpackConfig: 'apps/webapp/webpack.config.js'
|
||||
}
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
expect(mockFunction).toHaveBeenCalled();
|
||||
expect(runWebpack.calls.first().args[0]).toEqual({
|
||||
config: 'config'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
79
packages/builders/src/web/build/web-build.builder.ts
Normal file
79
packages/builders/src/web/build/web-build.builder.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import {
|
||||
Builder,
|
||||
BuildEvent,
|
||||
BuilderConfiguration,
|
||||
BuilderContext
|
||||
} from '@angular-devkit/architect';
|
||||
import { getSystemPath } from '@angular-devkit/core';
|
||||
import { WebpackBuilder } from '@angular-devkit/build-webpack';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||
import { BuildBuilderOptions } from '../../utils/types';
|
||||
import { getWebConfig } from '../../utils/webpack/web.config';
|
||||
import {
|
||||
OutputHashing,
|
||||
StylePreprocessorOptions,
|
||||
Budget
|
||||
} from '@angular-devkit/build-angular';
|
||||
|
||||
export interface WebBuildBuilderOptions extends BuildBuilderOptions {
|
||||
index: string;
|
||||
budgets: Budget[];
|
||||
baseHref: string;
|
||||
deployUrl: string;
|
||||
|
||||
polyfills?: string;
|
||||
es2015Polyfills?: string;
|
||||
|
||||
scripts: string[];
|
||||
styles: string[];
|
||||
|
||||
vendorChunk?: boolean;
|
||||
commonChunk?: boolean;
|
||||
|
||||
outputHashing?: OutputHashing;
|
||||
stylePreprocessingOptions?: StylePreprocessorOptions;
|
||||
}
|
||||
|
||||
export default class BuildWebBuilder
|
||||
implements Builder<WebBuildBuilderOptions> {
|
||||
webpackBuilder: WebpackBuilder;
|
||||
|
||||
root: string;
|
||||
|
||||
constructor(private context: BuilderContext) {
|
||||
this.webpackBuilder = new WebpackBuilder(this.context);
|
||||
this.root = getSystemPath(this.context.workspace.root);
|
||||
}
|
||||
|
||||
run(
|
||||
builderConfig: BuilderConfiguration<WebBuildBuilderOptions>
|
||||
): Observable<BuildEvent> {
|
||||
const options = normalizeWebBuildOptions(
|
||||
builderConfig.options,
|
||||
this.root,
|
||||
builderConfig.sourceRoot
|
||||
);
|
||||
|
||||
let config = getWebConfig(options, this.context.logger);
|
||||
if (options.webpackConfig) {
|
||||
config = require(options.webpackConfig)(config, {
|
||||
options,
|
||||
configuration: this.context.targetSpecifier.configuration
|
||||
});
|
||||
}
|
||||
return this.webpackBuilder.runWebpack(config, stats => {
|
||||
if (options.statsJson) {
|
||||
writeFileSync(
|
||||
resolve(this.root, options.outputPath, 'stats.json'),
|
||||
JSON.stringify(stats.toJson(), null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
this.context.logger.info(stats.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
53
packages/builders/src/web/dev-server/schema.json
Normal file
53
packages/builders/src/web/dev-server/schema.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"title": "Web Dev Server",
|
||||
"description": "Web Dev Server",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"buildTarget": {
|
||||
"type": "string",
|
||||
"description": "Target which builds the application"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to listen on.",
|
||||
"default": 4200
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"description": "Host to listen on.",
|
||||
"default": "localhost"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "boolean",
|
||||
"description": "Serve using HTTPS.",
|
||||
"default": false
|
||||
},
|
||||
"sslKey": {
|
||||
"type": "string",
|
||||
"description": "SSL key to use for serving HTTPS."
|
||||
},
|
||||
"sslCert": {
|
||||
"type": "string",
|
||||
"description": "SSL certificate to use for serving HTTPS."
|
||||
},
|
||||
"watch": {
|
||||
"type": "boolean",
|
||||
"description": "Watches for changes and rebuilds application",
|
||||
"default": true
|
||||
},
|
||||
"liveReload": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to reload the page on change, using live-reload.",
|
||||
"default": true
|
||||
},
|
||||
"publicHost": {
|
||||
"type": "string",
|
||||
"description": "Public URL where the application will be served"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean",
|
||||
"description": "Open the application in the browser.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
142
packages/builders/src/web/dev-server/web-dev-server.builder.ts
Normal file
142
packages/builders/src/web/dev-server/web-dev-server.builder.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import {
|
||||
Builder,
|
||||
BuildEvent,
|
||||
BuilderConfiguration,
|
||||
BuilderContext
|
||||
} from '@angular-devkit/architect';
|
||||
import { getSystemPath } from '@angular-devkit/core';
|
||||
import { WebpackDevServerBuilder } from '@angular-devkit/build-webpack';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||
import { getDevServerConfig } from '../../utils/webpack/devserver.config';
|
||||
import { concatMap, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { WebBuildBuilderOptions } from '../build/web-build.builder';
|
||||
import { Configuration } from 'webpack';
|
||||
import { writeFileSync } from 'fs';
|
||||
import * as opn from 'opn';
|
||||
import * as url from 'url';
|
||||
import { resolve } from 'path';
|
||||
import { buildServePath } from '../../utils/serve-path';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
|
||||
export interface WebDevServerOptions {
|
||||
host: string;
|
||||
port: number;
|
||||
publicHost?: string;
|
||||
ssl: boolean;
|
||||
sslKey?: string;
|
||||
sslCert?: string;
|
||||
proxyConfig?: string;
|
||||
buildTarget: string;
|
||||
open: boolean;
|
||||
liveReload: boolean;
|
||||
watch: boolean;
|
||||
}
|
||||
|
||||
export default class WebDevServerBuilder
|
||||
implements Builder<WebDevServerOptions> {
|
||||
webpackDevServerBuilder = new WebpackDevServerBuilder(this.context);
|
||||
|
||||
root: string;
|
||||
|
||||
constructor(private context: BuilderContext) {
|
||||
this.root = getSystemPath(this.context.workspace.root);
|
||||
}
|
||||
|
||||
run(
|
||||
builderConfig: BuilderConfiguration<WebDevServerOptions>
|
||||
): Observable<BuildEvent> {
|
||||
const serveOptions = builderConfig.options;
|
||||
return this.getBuildOptions(serveOptions).pipe(
|
||||
map(buildOptions => {
|
||||
buildOptions = normalizeWebBuildOptions(
|
||||
buildOptions,
|
||||
this.root,
|
||||
builderConfig.sourceRoot
|
||||
);
|
||||
let webpackConfig: Configuration = getDevServerConfig(
|
||||
buildOptions,
|
||||
serveOptions,
|
||||
this.context.logger
|
||||
);
|
||||
if (buildOptions.webpackConfig) {
|
||||
webpackConfig = require(buildOptions.webpackConfig)(webpackConfig, {
|
||||
buildOptions,
|
||||
configuration: serveOptions.buildTarget.split(':')[2]
|
||||
});
|
||||
}
|
||||
return [webpackConfig, buildOptions] as [
|
||||
Configuration,
|
||||
WebBuildBuilderOptions
|
||||
];
|
||||
}),
|
||||
tap(([_, options]) => {
|
||||
const path = buildServePath(options);
|
||||
const serverUrl = url.format({
|
||||
protocol: serveOptions.ssl ? 'https' : 'http',
|
||||
hostname:
|
||||
serveOptions.host === '0.0.0.0' ? 'localhost' : serveOptions.host,
|
||||
port: serveOptions.port.toString(),
|
||||
path: path
|
||||
});
|
||||
|
||||
this.context.logger.info(stripIndents`
|
||||
**
|
||||
Web Development Server is listening at ${serverUrl}
|
||||
**
|
||||
`);
|
||||
if (serveOptions.open) {
|
||||
opn(serverUrl, {
|
||||
wait: false
|
||||
});
|
||||
}
|
||||
}),
|
||||
switchMap(([config, options]) => {
|
||||
return this.webpackDevServerBuilder.runWebpackDevServer(
|
||||
config,
|
||||
undefined,
|
||||
stats => {
|
||||
if (options.statsJson) {
|
||||
writeFileSync(
|
||||
resolve(this.root, options.outputPath, 'stats.json'),
|
||||
JSON.stringify(stats.toJson(), null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
this.context.logger.info(stats.toString());
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private getBuildOptions(options: WebDevServerOptions) {
|
||||
const builderConfig = this.getBuildBuilderConfig(options);
|
||||
|
||||
return this.context.architect.getBuilderDescription(builderConfig).pipe(
|
||||
concatMap(buildDescription =>
|
||||
this.context.architect.validateBuilderOptions(
|
||||
builderConfig,
|
||||
buildDescription
|
||||
)
|
||||
),
|
||||
map(builderConfig => builderConfig.options)
|
||||
);
|
||||
}
|
||||
|
||||
private getBuildBuilderConfig(options: WebDevServerOptions) {
|
||||
const [project, target, configuration] = options.buildTarget.split(':');
|
||||
|
||||
return this.context.architect.getBuilderConfiguration<
|
||||
WebBuildBuilderOptions
|
||||
>({
|
||||
project,
|
||||
target,
|
||||
configuration,
|
||||
overrides: {
|
||||
watch: options.watch
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { getFileContent } from '@schematics/angular/utility/test';
|
||||
import * as stripJsonComments from 'strip-json-comments';
|
||||
import { readJsonInTree, updateJsonInTree } from '../../utils/ast-utils';
|
||||
import { NxJson } from '../../command-line/shared';
|
||||
import { Framework } from '../../utils/frameworks';
|
||||
|
||||
describe('app', () => {
|
||||
let appTree: Tree;
|
||||
@ -349,6 +350,144 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('--framework', () => {
|
||||
describe('custom-elements', () => {
|
||||
it('should replace app files', async () => {
|
||||
const tree = await runSchematic(
|
||||
'app',
|
||||
{ name: 'myApp', framework: Framework.CustomElements },
|
||||
appTree
|
||||
);
|
||||
|
||||
expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.component.ts')).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.css')
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.html')
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.spec.ts')
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('react', () => {
|
||||
it('should replace app files', async () => {
|
||||
const tree = await runSchematic(
|
||||
'app',
|
||||
{
|
||||
name: 'my-App',
|
||||
framework: Framework.React
|
||||
},
|
||||
appTree
|
||||
);
|
||||
|
||||
expect(tree.exists('apps/my-app/src/main.ts')).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.component.ts')).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.css')
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.html')
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.component.spec.ts')
|
||||
).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/src/main.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.spec.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.css')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should setup jest with tsx support', async () => {
|
||||
const tree = await runSchematic(
|
||||
'app',
|
||||
{
|
||||
name: 'my-App',
|
||||
framework: Framework.React
|
||||
},
|
||||
appTree
|
||||
);
|
||||
|
||||
expect(tree.readContent('apps/my-app/jest.config.js')).toContain(
|
||||
`moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],`
|
||||
);
|
||||
});
|
||||
|
||||
it('should setup the nrwl web build builder', async () => {
|
||||
const tree = await runSchematic(
|
||||
'app',
|
||||
{
|
||||
name: 'my-App',
|
||||
framework: Framework.React
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const angularJson = readJsonInTree(tree, 'angular.json');
|
||||
const architectConfig = angularJson.projects['my-app'].architect;
|
||||
expect(architectConfig.build.builder).toEqual(
|
||||
'@nrwl/builders:web-build'
|
||||
);
|
||||
expect(architectConfig.build.options).toEqual({
|
||||
assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'],
|
||||
index: 'apps/my-app/src/index.html',
|
||||
main: 'apps/my-app/src/main.tsx',
|
||||
outputPath: 'dist/apps/my-app',
|
||||
polyfills: 'apps/my-app/src/polyfills.ts',
|
||||
scripts: [],
|
||||
styles: ['apps/my-app/src/styles.css'],
|
||||
tsConfig: 'apps/my-app/tsconfig.app.json'
|
||||
});
|
||||
expect(architectConfig.build.configurations.production).toEqual({
|
||||
optimization: true,
|
||||
budgets: [
|
||||
{
|
||||
maximumError: '5mb',
|
||||
maximumWarning: '2mb',
|
||||
type: 'initial'
|
||||
}
|
||||
],
|
||||
extractCss: true,
|
||||
extractLicenses: true,
|
||||
fileReplacements: [
|
||||
{
|
||||
replace: 'apps/my-app/src/environments/environment.ts',
|
||||
with: 'apps/my-app/src/environments/environment.prod.ts'
|
||||
}
|
||||
],
|
||||
namedChunks: false,
|
||||
outputHashing: 'all',
|
||||
sourceMap: false,
|
||||
vendorChunk: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl web dev server builder', async () => {
|
||||
const tree = await runSchematic(
|
||||
'app',
|
||||
{
|
||||
name: 'my-App',
|
||||
framework: Framework.React
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const angularJson = readJsonInTree(tree, 'angular.json');
|
||||
const architectConfig = angularJson.projects['my-app'].architect;
|
||||
expect(architectConfig.serve.builder).toEqual(
|
||||
'@nrwl/builders:web-dev-server'
|
||||
);
|
||||
expect(architectConfig.serve.options).toEqual({
|
||||
buildTarget: 'my-app:build'
|
||||
});
|
||||
expect(architectConfig.serve.configurations.production).toEqual({
|
||||
buildTarget: 'my-app:build:production'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('--unit-test-runner none', () => {
|
||||
it('should not generate test configuration', async () => {
|
||||
const tree = await runSchematic(
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"compilerOptions": {<% if (framework === 'react') { %>
|
||||
"jsx": "react",<% } %><% if (framework === 'custom-elements') { %>
|
||||
"target": "es2015",<% } %>
|
||||
"types": []
|
||||
},
|
||||
"include": ["**/*.ts"<% if (framework === 'react') { %>, "**/*.tsx"<% } %>]
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import { AppElement } from './app.element';
|
||||
|
||||
describe('AppElement', () => {
|
||||
let app: AppElement;
|
||||
|
||||
beforeEach(() => {
|
||||
app = new AppElement();
|
||||
});
|
||||
|
||||
it('should create successfully', () => {
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have a greeting', () => {
|
||||
app.connectedCallback();
|
||||
|
||||
expect(app.querySelector('h1').innerHTML).toEqual(
|
||||
'Welcome to <%= name %>!'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,30 @@
|
||||
export class AppElement extends HTMLElement {
|
||||
public static observedAttributes = [
|
||||
|
||||
];
|
||||
|
||||
connectedCallback() {
|
||||
const title = '<%= name %>';
|
||||
this.innerHTML = `
|
||||
<div style="text-align: center">
|
||||
<h1>Welcome to ${title}!</h1>
|
||||
<img
|
||||
width="300"
|
||||
src="https://raw.githubusercontent.com/nrwl/nx/master/nx-logo.png"
|
||||
/>
|
||||
</div>
|
||||
<p>This is a Custom Elements app built with <a href="https://nx.dev">Nx</a>.</p>
|
||||
<p>🔎 **Nx is a set of Angular CLI power-ups for modern development.**</p>
|
||||
<h2>Quick Start & Documentation</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://nx.dev/getting-started/what-is-nx">30-minute video showing all Nx features</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://nx.dev/tutorial/01-create-application">Interactive tutorial</a>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('<%= prefix %>-root', AppElement);
|
||||
@ -0,0 +1 @@
|
||||
import './app/app.element.ts';
|
||||
@ -0,0 +1,3 @@
|
||||
/**
|
||||
* This file contains polyfills loaded on all browsers
|
||||
**/
|
||||
@ -0,0 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import { render, cleanup } from 'react-testing-library';
|
||||
|
||||
import { App } from './app';
|
||||
|
||||
describe('App', () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<App />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have a greeting as the title', () => {
|
||||
const { getByText } = render(<App />);
|
||||
expect(getByText('Welcome to <%= name %>!')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import { Component } from 'react';
|
||||
|
||||
import './app.<%= style %>';
|
||||
|
||||
export class App extends Component {
|
||||
render() {
|
||||
const title = '<%= name %>';
|
||||
return (
|
||||
<div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<h1>Welcome to {title}!</h1>
|
||||
<img
|
||||
width="300"
|
||||
src="https://raw.githubusercontent.com/nrwl/nx/master/nx-logo.png"
|
||||
/>
|
||||
</div>
|
||||
<p>This is a React app built with <a href="https://nx.dev">Nx</a>.</p>
|
||||
<p>🔎 **Nx is a set of Angular CLI power-ups for modern development.**</p>
|
||||
<h2>Quick Start & Documentation</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://nx.dev/getting-started/what-is-nx">30-minute video showing all Nx features</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://nx.dev/tutorial/01-create-application">Interactive tutorial</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { App } from './app/app';
|
||||
|
||||
ReactDOM.render(<App />, document.querySelector('<%= prefix %>-root'));
|
||||
@ -0,0 +1,3 @@
|
||||
/**
|
||||
* This file contains polyfills loaded on all browsers
|
||||
**/
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@ -34,8 +34,12 @@ import {
|
||||
} from '../../utils/cli-config-utils';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
import { join, normalize } from '@angular-devkit/core';
|
||||
import { readJson } from '../../../../../e2e/utils';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { Framework } from '../../utils/frameworks';
|
||||
import {
|
||||
reactVersions,
|
||||
documentRegisterElementVersion
|
||||
} from '../../lib-versions';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
appProjectRoot: string;
|
||||
@ -141,19 +145,125 @@ function updateComponentTemplate(options: NormalizedSchema): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function updateBuilders(options: NormalizedSchema): Rule {
|
||||
return (host: Tree) => {
|
||||
return updateJsonInTree(getWorkspacePath(host), json => {
|
||||
const project = json.projects[options.name];
|
||||
const buildOptions = project.architect.build;
|
||||
const serveOptions = project.architect.serve;
|
||||
|
||||
buildOptions.builder = '@nrwl/builders:web-build';
|
||||
delete buildOptions.configurations.production.aot;
|
||||
delete buildOptions.configurations.production.buildOptimizer;
|
||||
|
||||
serveOptions.builder = '@nrwl/builders:web-dev-server';
|
||||
serveOptions.options.buildTarget = serveOptions.options.browserTarget;
|
||||
delete serveOptions.options.browserTarget;
|
||||
serveOptions.configurations.production.buildTarget =
|
||||
serveOptions.configurations.production.browserTarget;
|
||||
delete serveOptions.configurations.production.browserTarget;
|
||||
|
||||
if (options.framework === Framework.React) {
|
||||
buildOptions.options.main = buildOptions.options.main.replace(
|
||||
'.ts',
|
||||
'.tsx'
|
||||
);
|
||||
}
|
||||
return json;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function updateApplicationFiles(options: NormalizedSchema): Rule {
|
||||
return chain([
|
||||
deleteAngularApplicationFiles(options),
|
||||
addApplicationFiles(options),
|
||||
updateDependencies(options)
|
||||
]);
|
||||
}
|
||||
|
||||
function addApplicationFiles(options: NormalizedSchema): Rule {
|
||||
return mergeWith(
|
||||
apply(url(`./files/${options.framework}`), [
|
||||
template({
|
||||
...options,
|
||||
tmpl: ''
|
||||
}),
|
||||
move(options.appProjectRoot)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
function deleteAngularApplicationFiles(options: NormalizedSchema): Rule {
|
||||
return (host: Tree) => {
|
||||
const projectRoot = normalize(options.appProjectRoot);
|
||||
[
|
||||
'src/main.ts',
|
||||
'src/polyfills.ts',
|
||||
'src/app/app.module.ts',
|
||||
'src/app/app.component.ts',
|
||||
'src/app/app.component.spec.ts',
|
||||
`src/app/app.component.${options.style}`,
|
||||
'src/app/app.component.html'
|
||||
].forEach(path => {
|
||||
path = join(projectRoot, path);
|
||||
if (host.exists(path)) {
|
||||
host.delete(path);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function updateDependencies(options: NormalizedSchema): Rule {
|
||||
return updateJsonInTree('package.json', (json, context) => {
|
||||
json.dependencies = json.dependencies || {};
|
||||
json.devDependencies = json.devDependencies || {};
|
||||
|
||||
switch (options.framework) {
|
||||
case Framework.React:
|
||||
json.dependencies = {
|
||||
...json.dependencies,
|
||||
react: reactVersions.framework,
|
||||
'react-dom': reactVersions.framework
|
||||
};
|
||||
json.devDependencies = {
|
||||
...json.devDependencies,
|
||||
'@types/react': reactVersions.reactTypes,
|
||||
'@types/react-dom': reactVersions.reactDomTypes,
|
||||
'react-testing-library': reactVersions.testingLibrary
|
||||
};
|
||||
context.addTask(new NodePackageInstallTask());
|
||||
return json;
|
||||
|
||||
case Framework.CustomElements:
|
||||
json.dependencies = {
|
||||
...json.dependencies,
|
||||
'document-register-element': documentRegisterElementVersion
|
||||
};
|
||||
context.addTask(new NodePackageInstallTask());
|
||||
return json;
|
||||
|
||||
default:
|
||||
return json;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addTsconfigs(options: NormalizedSchema): Rule {
|
||||
return chain([
|
||||
mergeWith(
|
||||
apply(url('./files'), [
|
||||
apply(url('./files/app'), [
|
||||
template({
|
||||
...options,
|
||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot)
|
||||
}),
|
||||
move(options.appProjectRoot)
|
||||
])
|
||||
),
|
||||
mergeWith(
|
||||
apply(url('./files'), [
|
||||
apply(url('./files/app'), [
|
||||
template({
|
||||
...options,
|
||||
offsetFromRoot: offsetFromRoot(options.e2eProjectRoot)
|
||||
}),
|
||||
move(options.e2eProjectRoot)
|
||||
@ -246,17 +356,15 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
host.delete(`${options.e2eProjectRoot}/protractor.conf.js`);
|
||||
}
|
||||
},
|
||||
(host, context) => {
|
||||
if (options.e2eTestRunner === 'protractor') {
|
||||
updateJsonInTree('/package.json', json => {
|
||||
options.e2eTestRunner === 'protractor'
|
||||
? updateJsonInTree('/package.json', (json, context) => {
|
||||
if (!json.devDependencies.protractor) {
|
||||
json.devDependencies.protractor = '~5.4.0';
|
||||
context.addTask(new NodePackageInstallTask());
|
||||
}
|
||||
return json;
|
||||
})(host, context);
|
||||
}
|
||||
}
|
||||
})
|
||||
: noop()
|
||||
]);
|
||||
};
|
||||
}
|
||||
@ -361,11 +469,25 @@ export default function(schema: Schema): Rule {
|
||||
move(appProjectRoot, options.appProjectRoot),
|
||||
updateProject(options),
|
||||
|
||||
updateComponentTemplate(options),
|
||||
options.routing ? addRouterRootConfiguration(options) : noop(),
|
||||
options.framework !== Framework.Angular
|
||||
? updateBuilders(options)
|
||||
: noop(),
|
||||
options.framework === Framework.Angular
|
||||
? updateComponentTemplate(options)
|
||||
: updateApplicationFiles(options),
|
||||
options.framework === Framework.Angular && options.routing
|
||||
? addRouterRootConfiguration(options)
|
||||
: noop(),
|
||||
options.unitTestRunner === 'jest'
|
||||
? schematic('jest-project', {
|
||||
project: options.name
|
||||
project: options.name,
|
||||
supportTsx: options.framework === Framework.React,
|
||||
setupFile:
|
||||
options.framework === Framework.Angular
|
||||
? 'angular'
|
||||
: options.framework === Framework.CustomElements
|
||||
? 'custom-elements'
|
||||
: 'none'
|
||||
})
|
||||
: noop(),
|
||||
options.unitTestRunner === 'karma'
|
||||
@ -379,6 +501,12 @@ export default function(schema: Schema): Rule {
|
||||
}
|
||||
|
||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
||||
if (options.framework !== Framework.Angular && options.routing) {
|
||||
throw new Error(
|
||||
`Routing is not supported yet with frameworks other than Angular`
|
||||
);
|
||||
}
|
||||
|
||||
const appDirectory = options.directory
|
||||
? `${toFileName(options.directory)}/${toFileName(options.name)}`
|
||||
: toFileName(options.name);
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { Framework } from './.../utils/frameworks';
|
||||
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||
|
||||
export interface Schema {
|
||||
framework: Framework;
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
inlineStyle?: boolean;
|
||||
|
||||
@ -4,6 +4,30 @@
|
||||
"title": "Nx Application Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"framework": {
|
||||
"description": "The Framework for the application.",
|
||||
"type": "string",
|
||||
"enum": ["angular", "react", "custom-elements"],
|
||||
"default": "angular",
|
||||
"x-prompt": {
|
||||
"message": "What framework would you like to use for the application?",
|
||||
"type": "list",
|
||||
"items": [
|
||||
{
|
||||
"value": "angular",
|
||||
"label": "Angular [ https://angular.io ]"
|
||||
},
|
||||
{
|
||||
"value": "react",
|
||||
"label": "React [ https://reactjs.org/ ]"
|
||||
},
|
||||
{
|
||||
"value": "custom-elements",
|
||||
"label": "Custom Elements"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of the application.",
|
||||
"type": "string",
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
module.exports = {
|
||||
name: '<%= project %>',
|
||||
preset: '<%= offsetFromRoot %>jest.config.js',
|
||||
preset: '<%= offsetFromRoot %>jest.config.js',<% if (supportTsx) { %>
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],<% } %>
|
||||
coverageDirectory: '<%= offsetFromRoot %>coverage/<%= projectRoot %>'<% if(!skipSerializers) { %>,
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/AngularSnapshotSerializer.js',
|
||||
|
||||
@ -1 +1,2 @@
|
||||
import 'jest-preset-angular';
|
||||
<% if (setupFile === 'angular') { %>import 'jest-preset-angular';
|
||||
<% } else if (setupFile === 'custom-elements') { %>import 'document-register-element';<% } %>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc/<%= projectRoot %>",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
<% if(!skipSetupFile) { %>"files": ["src/test-setup.ts"],<% } %>
|
||||
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||
},<% if(setupFile !== 'none') { %>
|
||||
"files": ["src/test-setup.ts"],<% } %>
|
||||
"include": ["**/*.spec.ts", "**/*.d.ts"<% if (supportTsx) { %>, "**/*.spec.tsx", "**/*.d.tsx"<% } %>]
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ import { join, normalize } from '@angular-devkit/core';
|
||||
|
||||
export interface JestProjectSchema {
|
||||
project: string;
|
||||
supportTsx: boolean;
|
||||
skipSetupFile: boolean;
|
||||
setupFile: 'angular' | 'custom-elements' | 'none';
|
||||
skipSerializers: boolean;
|
||||
}
|
||||
|
||||
@ -37,7 +39,7 @@ function generateFiles(options: JestProjectSchema): Rule {
|
||||
projectRoot: projectConfig.root,
|
||||
offsetFromRoot: offsetFromRoot(projectConfig.root)
|
||||
}),
|
||||
options.skipSetupFile
|
||||
options.setupFile === 'none'
|
||||
? filter(file => file !== '/src/test-setup.ts')
|
||||
: noop(),
|
||||
move(projectConfig.root)
|
||||
@ -73,7 +75,7 @@ function updateAngularJson(options: JestProjectSchema): Rule {
|
||||
tsConfig: join(normalize(projectConfig.root), 'tsconfig.spec.json')
|
||||
}
|
||||
};
|
||||
if (!options.skipSetupFile) {
|
||||
if (options.setupFile !== 'none') {
|
||||
projectConfig.architect.test.options.setupFile = join(
|
||||
normalize(projectConfig.root),
|
||||
'src/test-setup.ts'
|
||||
@ -104,7 +106,18 @@ function check(options: JestProjectSchema): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeOptions(options: JestProjectSchema): JestProjectSchema {
|
||||
if (!options.skipSetupFile) {
|
||||
return options;
|
||||
}
|
||||
return {
|
||||
...options,
|
||||
setupFile: 'none'
|
||||
};
|
||||
}
|
||||
|
||||
export default function(options: JestProjectSchema): Rule {
|
||||
options = normalizeOptions(options);
|
||||
return chain([
|
||||
check(options),
|
||||
generateFiles(options),
|
||||
|
||||
@ -108,6 +108,51 @@ describe('jestProject', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('--setup-file', () => {
|
||||
it('should generate src/test-setup.ts', async () => {
|
||||
const resultTree = await runSchematic(
|
||||
'jest-project',
|
||||
{
|
||||
project: 'lib1',
|
||||
setupFile: 'none'
|
||||
},
|
||||
appTree
|
||||
);
|
||||
expect(resultTree.exists('src/test-setup.ts')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not list the setup file in angular.json', async () => {
|
||||
const resultTree = await runSchematic(
|
||||
'jest-project',
|
||||
{
|
||||
project: 'lib1',
|
||||
setupFile: 'none'
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const angularJson = readJsonInTree(resultTree, 'angular.json');
|
||||
expect(
|
||||
angularJson.projects.lib1.architect.test.options.setupFile
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not list the setup file in tsconfig.spec.json', async () => {
|
||||
const resultTree = await runSchematic(
|
||||
'jest-project',
|
||||
{
|
||||
project: 'lib1',
|
||||
setupFile: 'none'
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const tsConfig = readJsonInTree(
|
||||
resultTree,
|
||||
'libs/lib1/tsconfig.spec.json'
|
||||
);
|
||||
expect(tsConfig.files).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('--skip-setup-file', () => {
|
||||
it('should generate src/test-setup.ts', async () => {
|
||||
const resultTree = await runSchematic(
|
||||
@ -172,4 +217,21 @@ describe('jestProject', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--support-tsx', () => {
|
||||
it('should add tsx to moduleExtensions', async () => {
|
||||
const resultTree = await runSchematic(
|
||||
'jest-project',
|
||||
{
|
||||
project: 'lib1',
|
||||
supportTsx: true
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const jestConfig = resultTree.readContent('libs/lib1/jest.config.js');
|
||||
expect(jestConfig).toContain(
|
||||
`moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,13 +13,25 @@
|
||||
},
|
||||
"skipSetupFile": {
|
||||
"type": "boolean",
|
||||
"description": "Skips the setup file required for angular",
|
||||
"default": false
|
||||
"description": "[Deprecated]: Skips the setup file required for angular. (Use --setup-file)",
|
||||
"default": false,
|
||||
"x-deprecated": true
|
||||
},
|
||||
"setupFile": {
|
||||
"type": "string",
|
||||
"enum": ["none", "angular", "custom-elements"],
|
||||
"description": "The setup file to be generated",
|
||||
"default": "angular"
|
||||
},
|
||||
"skipSerializers": {
|
||||
"type": "boolean",
|
||||
"description": "Skips the serializers required to snapshot angular templates",
|
||||
"default": false
|
||||
},
|
||||
"supportTsx": {
|
||||
"type": "boolean",
|
||||
"description": "Setup tsx support",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
@ -24,6 +24,8 @@ export interface KarmaProjectSchema {
|
||||
project: string;
|
||||
}
|
||||
|
||||
// TODO: @jjean implement skipSetupFile
|
||||
|
||||
function generateFiles(options: KarmaProjectSchema): Rule {
|
||||
return (host, context) => {
|
||||
const projectConfig = getProjectConfig(host, options.project);
|
||||
|
||||
@ -446,7 +446,8 @@ export default function(schema: Schema): Rule {
|
||||
options.unitTestRunner === 'jest'
|
||||
? schematic('jest-project', {
|
||||
project: options.name,
|
||||
skipSetupFile: options.framework !== Framework.Angular,
|
||||
setupFile:
|
||||
options.framework === Framework.Angular ? 'angular' : 'none',
|
||||
skipSerializers: options.framework !== Framework.Angular
|
||||
})
|
||||
: noop(),
|
||||
|
||||
@ -233,7 +233,7 @@ export default function(schema: Schema): Rule {
|
||||
options.unitTestRunner === 'jest'
|
||||
? schematic('jest-project', {
|
||||
project: options.name,
|
||||
skipSetupFile: true,
|
||||
setupFile: 'none',
|
||||
skipSerializers: true
|
||||
})
|
||||
: noop(),
|
||||
|
||||
@ -178,7 +178,8 @@ export class DepsCalculator {
|
||||
* Process a file and update it's dependencies
|
||||
*/
|
||||
processFile(filePath: string): void {
|
||||
if (path.extname(filePath) !== '.ts') {
|
||||
const extension = path.extname(filePath);
|
||||
if (extension !== '.ts' && extension !== '.tsx') {
|
||||
return;
|
||||
}
|
||||
const tsFile = ts.createSourceFile(
|
||||
|
||||
@ -21,6 +21,15 @@ export const expressTypingsVersion = '4.16.0';
|
||||
export const nestJsVersion = '5.5.0';
|
||||
export const nestJsSchematicsVersion = '5.11.2';
|
||||
|
||||
export const reactVersions = {
|
||||
framework: '16.8.3',
|
||||
reactTypes: '16.8.4',
|
||||
reactDomTypes: '16.8.2',
|
||||
testingLibrary: '6.0.0'
|
||||
};
|
||||
|
||||
export const documentRegisterElementVersion = '1.13.1';
|
||||
|
||||
export const libVersions = {
|
||||
angularVersion,
|
||||
angularDevkitVersion,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT- style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import { Rule, Tree } from '@angular-devkit/schematics';
|
||||
import { Rule, Tree, SchematicContext } from '@angular-devkit/schematics';
|
||||
import {
|
||||
findNodes,
|
||||
getDecoratorMetadata,
|
||||
@ -593,14 +593,17 @@ export function readJsonInTree<T = any>(host: Tree, path: string): T {
|
||||
*/
|
||||
export function updateJsonInTree<T = any, O = T>(
|
||||
path: string,
|
||||
callback: (json: T) => O
|
||||
callback: (json: T, context: SchematicContext) => O
|
||||
): Rule {
|
||||
return (host: Tree): Tree => {
|
||||
return (host: Tree, context: SchematicContext): Tree => {
|
||||
if (!host.exists(path)) {
|
||||
host.create(path, serializeJson(callback({} as T)));
|
||||
host.create(path, serializeJson(callback({} as T, context)));
|
||||
return host;
|
||||
}
|
||||
host.overwrite(path, serializeJson(callback(readJsonInTree(host, path))));
|
||||
host.overwrite(
|
||||
path,
|
||||
serializeJson(callback(readJsonInTree(host, path), context))
|
||||
);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export const enum Framework {
|
||||
Angular = 'angular',
|
||||
React = 'react',
|
||||
CustomElements = 'custom-elements',
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
@ -18,13 +18,14 @@ import {
|
||||
*/
|
||||
const buildersSourceDirectory = path.join(
|
||||
__dirname,
|
||||
'../../build/packages/builders/src'
|
||||
'../../packages/builders/src'
|
||||
);
|
||||
const buildersOutputDirectory = path.join(__dirname, '../../docs/api-builders');
|
||||
const builderCollectionFile = path.join(
|
||||
buildersSourceDirectory,
|
||||
'builders.json'
|
||||
);
|
||||
fs.removeSync(buildersOutputDirectory);
|
||||
const builderCollection = fs.readJsonSync(builderCollectionFile).builders;
|
||||
const registry = new CoreSchemaRegistry();
|
||||
registry.addFormat(pathFormat);
|
||||
|
||||
@ -19,7 +19,7 @@ const path = require('path');
|
||||
*/
|
||||
const schematicsSourceDirectory = path.join(
|
||||
__dirname,
|
||||
'../../build/packages/schematics/src'
|
||||
'../../packages/schematics/src'
|
||||
);
|
||||
const schematicsOutputDirectory = path.join(
|
||||
__dirname,
|
||||
@ -29,6 +29,7 @@ const schematicCollectionFile = path.join(
|
||||
schematicsSourceDirectory,
|
||||
'collection.json'
|
||||
);
|
||||
fs.removeSync(schematicsOutputDirectory);
|
||||
const schematicCollection = fs.readJsonSync(schematicCollectionFile).schematics;
|
||||
const registry = new CoreSchemaRegistry();
|
||||
registry.addFormat(pathFormat);
|
||||
|
||||
342
yarn.lock
342
yarn.lock
@ -288,6 +288,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd"
|
||||
integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ==
|
||||
|
||||
"@babel/runtime@^7.1.5", "@babel/runtime@^7.3.1":
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
|
||||
integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/template@^7.0.0", "@babel/template@^7.1.0":
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
|
||||
@ -493,6 +500,11 @@
|
||||
semver "5.6.0"
|
||||
semver-intersect "1.4.0"
|
||||
|
||||
"@sheerun/mutationobserver-shim@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
|
||||
integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==
|
||||
|
||||
"@sindresorhus/is@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
@ -637,11 +649,31 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.13.2.tgz#ffe96278e712a8d4e467e367a338b05e22872646"
|
||||
integrity sha512-k6MCN8WuDiCj6O+UJsVMbrreZxkbrhQbO02oDj6yuRu8UAkp0MDdEcDKif8/gBKuJbT84kkO+VHQAqXkumEklg==
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.9.tgz#f2d14df87b0739041bc53a7d75e3d77d726a3ec0"
|
||||
integrity sha512-Nha5b+jmBI271jdTMwrHiNXM+DvThjHOfyZtMX9kj/c/LUj2xiLHsG/1L3tJ8DjAoQN48cHwUwtqBotjyXaSdQ==
|
||||
|
||||
"@types/range-parser@*":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
|
||||
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
|
||||
|
||||
"@types/react-dom@^16.8.2":
|
||||
version "16.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.2.tgz#9bd7d33f908b243ff0692846ef36c81d4941ad12"
|
||||
integrity sha512-MX7n1wq3G/De15RGAAqnmidzhr2Y9O/ClxPxyqaNg96pGyeXUYPSvujgzEVpLo9oIP4Wn1UETl+rxTN02KEpBw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^16.8.4":
|
||||
version "16.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.4.tgz#134307f5266e866d5e7c25e47f31f9abd5b2ea34"
|
||||
integrity sha512-Mpz1NNMJvrjf0GcDqiK8+YeOydXfD8Mgag3UtqQ5lXYTsMnOiHcKmO48LiSWMb1rSHB9MV/jlgyNzeAVxWMZRQ==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/serve-static@*":
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
|
||||
@ -2146,6 +2178,11 @@ bonjour@^3.5.0:
|
||||
multicast-dns "^6.0.1"
|
||||
multicast-dns-service-types "^1.1.0"
|
||||
|
||||
boolbase@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
boom@2.x.x:
|
||||
version "2.10.1"
|
||||
resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
|
||||
@ -2555,6 +2592,14 @@ callsites@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
||||
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
|
||||
|
||||
camel-case@3.0.x:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||
integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
camelcase-keys@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
|
||||
@ -2759,7 +2804,7 @@ class-utils@^0.3.5:
|
||||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
clean-css@4.2.1, clean-css@^4.1.11:
|
||||
clean-css@4.2.1, clean-css@4.2.x, clean-css@^4.1.11:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
|
||||
integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
|
||||
@ -2936,16 +2981,16 @@ commander@2.11.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
|
||||
integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==
|
||||
|
||||
commander@2.17.x, commander@~2.17.1:
|
||||
version "2.17.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commander@^2.12.0, commander@^2.12.1, commander@^2.9.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||
|
||||
commander@~2.17.1:
|
||||
version "2.17.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commitizen@^2.10.1:
|
||||
version "2.10.1"
|
||||
resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-2.10.1.tgz#8c395def34a895f4e94952c2efc3c9eb4c3683bd"
|
||||
@ -3616,6 +3661,21 @@ css-parse@1.7.x:
|
||||
resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b"
|
||||
integrity sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=
|
||||
|
||||
css-select@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
|
||||
integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
|
||||
dependencies:
|
||||
boolbase "~1.0.0"
|
||||
css-what "2.1"
|
||||
domutils "1.5.1"
|
||||
nth-check "~1.0.1"
|
||||
|
||||
css-what@2.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
|
||||
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
|
||||
|
||||
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
|
||||
@ -3628,6 +3688,11 @@ cssstyle@^1.0.0:
|
||||
dependencies:
|
||||
cssom "0.3.x"
|
||||
|
||||
csstype@^2.2.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01"
|
||||
integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==
|
||||
|
||||
cuint@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
|
||||
@ -4077,6 +4142,20 @@ dns-txt@^2.0.2:
|
||||
dependencies:
|
||||
buffer-indexof "^1.0.0"
|
||||
|
||||
document-register-element@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/document-register-element/-/document-register-element-1.13.1.tgz#dad8cb7be38e04ee3f56842e6cf81af46c1249ba"
|
||||
integrity sha512-92ZyLDKg9j4rOll//NNXj25f+8rAzOkYsGJonhugKwXfeqH7bzs8Ucpvey0WzZ2ZzKdrvW9RnUw3UyOZ/uhBFw==
|
||||
dependencies:
|
||||
lightercollective "^0.1.0"
|
||||
|
||||
dom-converter@~0.2:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
|
||||
integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
|
||||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-serialize@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
|
||||
@ -4087,11 +4166,34 @@ dom-serialize@^2.2.0:
|
||||
extend "^3.0.0"
|
||||
void-elements "^2.0.0"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
|
||||
integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
|
||||
dependencies:
|
||||
domelementtype "^1.3.0"
|
||||
entities "^1.1.1"
|
||||
|
||||
dom-testing-library@^3.13.1:
|
||||
version "3.16.8"
|
||||
resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.16.8.tgz#26549b249f131a25e4339ebec9fcaa2e7642527f"
|
||||
integrity sha512-VGn2piehGoN9lmZDYd+xoTZwwcS+FoXebvZMw631UhS5LshiLTFNJs9bxRa9W7fVb1cAn9AYKAKZXh67rCDaqw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.5"
|
||||
"@sheerun/mutationobserver-shim" "^0.3.2"
|
||||
pretty-format "^24.0.0"
|
||||
wait-for-expect "^1.1.0"
|
||||
|
||||
domain-browser@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
|
||||
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
||||
|
||||
domelementtype@1, domelementtype@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
|
||||
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
|
||||
|
||||
domexception@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
|
||||
@ -4099,6 +4201,28 @@ domexception@^1.0.1:
|
||||
dependencies:
|
||||
webidl-conversions "^4.0.2"
|
||||
|
||||
domhandler@2.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594"
|
||||
integrity sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domutils@1.1:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485"
|
||||
integrity sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domutils@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
|
||||
integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
|
||||
dependencies:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
dot-prop@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
|
||||
@ -4269,6 +4393,11 @@ ent@~2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
|
||||
integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0=
|
||||
|
||||
entities@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
|
||||
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
|
||||
|
||||
err-code@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
|
||||
@ -5767,6 +5896,11 @@ hawk@~3.1.3:
|
||||
hoek "2.x.x"
|
||||
sntp "1.x.x"
|
||||
|
||||
he@1.2.x:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hipchat-notifier@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e"
|
||||
@ -5831,6 +5965,42 @@ html-entities@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
|
||||
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
|
||||
|
||||
html-minifier@^3.2.3:
|
||||
version "3.5.21"
|
||||
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
|
||||
integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
|
||||
dependencies:
|
||||
camel-case "3.0.x"
|
||||
clean-css "4.2.x"
|
||||
commander "2.17.x"
|
||||
he "1.2.x"
|
||||
param-case "2.1.x"
|
||||
relateurl "0.2.x"
|
||||
uglify-js "3.4.x"
|
||||
|
||||
html-webpack-plugin@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
|
||||
integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s=
|
||||
dependencies:
|
||||
html-minifier "^3.2.3"
|
||||
loader-utils "^0.2.16"
|
||||
lodash "^4.17.3"
|
||||
pretty-error "^2.0.2"
|
||||
tapable "^1.0.0"
|
||||
toposort "^1.0.0"
|
||||
util.promisify "1.0.0"
|
||||
|
||||
htmlparser2@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe"
|
||||
integrity sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
domhandler "2.1"
|
||||
domutils "1.1"
|
||||
readable-stream "1.0"
|
||||
|
||||
http-cache-semantics@3.8.1, http-cache-semantics@^3.8.1:
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
|
||||
@ -7602,6 +7772,11 @@ license-webpack-plugin@^1.4.0:
|
||||
dependencies:
|
||||
ejs "^2.5.7"
|
||||
|
||||
lightercollective@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lightercollective/-/lightercollective-0.1.0.tgz#70df102c530dcb8d0ccabfe6175a8d00d5f61300"
|
||||
integrity sha512-J9tg5uraYoQKaWbmrzDDexbG6hHnMcWS1qLYgJSWE+mpA3U5OCSeMUhb+K55otgZJ34oFdR0ECvdIb3xuO5JOQ==
|
||||
|
||||
listr-silent-renderer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
|
||||
@ -7698,7 +7873,7 @@ loader-utils@1.2.3:
|
||||
emojis-list "^2.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
loader-utils@^0.2.5:
|
||||
loader-utils@^0.2.16, loader-utils@^0.2.5:
|
||||
version "0.2.17"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
|
||||
integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
|
||||
@ -7818,7 +7993,7 @@ lodash@4.17.10:
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||
integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==
|
||||
|
||||
lodash@4.17.11, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.10:
|
||||
lodash@4.17.11, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.10:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
@ -7894,7 +8069,7 @@ longest@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
|
||||
|
||||
loose-envify@^1.0.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
@ -7909,6 +8084,11 @@ loud-rejection@^1.0.0:
|
||||
currently-unhandled "^0.4.1"
|
||||
signal-exit "^3.0.0"
|
||||
|
||||
lower-case@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||
|
||||
lowercase-keys@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
|
||||
@ -8562,6 +8742,13 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
no-case@^2.2.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
node-fetch-npm@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
|
||||
@ -8926,6 +9113,13 @@ npm-run-path@^2.0.0:
|
||||
gauge "~2.7.3"
|
||||
set-blocking "~2.0.0"
|
||||
|
||||
nth-check@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
|
||||
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
|
||||
dependencies:
|
||||
boolbase "~1.0.0"
|
||||
|
||||
null-check@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
|
||||
@ -8961,7 +9155,7 @@ object-assign@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
|
||||
integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=
|
||||
|
||||
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0:
|
||||
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
@ -9367,6 +9561,13 @@ parallel-transform@^1.1.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.1.5"
|
||||
|
||||
param-case@2.1.x:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
|
||||
integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
parse-asn1@^5.0.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8"
|
||||
@ -9726,6 +9927,14 @@ prettier@1.15.3:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a"
|
||||
integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==
|
||||
|
||||
pretty-error@^2.0.2:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
|
||||
integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=
|
||||
dependencies:
|
||||
renderkid "^2.0.1"
|
||||
utila "~0.4"
|
||||
|
||||
pretty-format@^23.6.0:
|
||||
version "23.6.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
|
||||
@ -9734,6 +9943,14 @@ pretty-format@^23.6.0:
|
||||
ansi-regex "^3.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
pretty-format@^24.0.0:
|
||||
version "24.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0.tgz#cb6599fd73ac088e37ed682f61291e4678f48591"
|
||||
integrity sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==
|
||||
dependencies:
|
||||
ansi-regex "^4.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
private@^0.1.6, private@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
@ -9801,6 +10018,15 @@ prompts@^0.1.9:
|
||||
kleur "^2.0.1"
|
||||
sisteransi "^0.1.1"
|
||||
|
||||
prop-types@^15.6.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
protoduck@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f"
|
||||
@ -10028,6 +10254,39 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
||||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
react-dom@^16.8.3:
|
||||
version "16.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.3.tgz#ae236029e66210783ac81999d3015dfc475b9c32"
|
||||
integrity sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.13.3"
|
||||
|
||||
react-is@^16.8.1:
|
||||
version "16.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d"
|
||||
integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==
|
||||
|
||||
react-testing-library@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-6.0.0.tgz#81edfcfae8a795525f48685be9bf561df45bb35d"
|
||||
integrity sha512-h0h+YLe4KWptK6HxOMnoNN4ngu3W8isrwDmHjPC5gxc+nOZOCurOvbKVYCvvuAw91jdO7VZSm/5KR7TxKnz0qA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
dom-testing-library "^3.13.1"
|
||||
|
||||
react@^16.8.3:
|
||||
version "16.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.3.tgz#c6f988a2ce895375de216edcfaedd6b9a76451d9"
|
||||
integrity sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.13.3"
|
||||
|
||||
read-cache@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
|
||||
@ -10116,6 +10375,16 @@ read-pkg@^4.0.1:
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@1.0:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@1.1.x, "readable-stream@1.x >=1.1.9":
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
@ -10225,6 +10494,11 @@ regenerator-runtime@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
|
||||
regenerator-runtime@^0.12.0:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||
|
||||
regenerator-transform@^0.10.0:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
|
||||
@ -10285,6 +10559,11 @@ regjsparser@^0.1.4:
|
||||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
relateurl@0.2.x:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
|
||||
|
||||
release-it@^7.4.0:
|
||||
version "7.6.2"
|
||||
resolved "https://registry.yarnpkg.com/release-it/-/release-it-7.6.2.tgz#9cdfdcdedc1bfe1889e111f862f0af8499931bb5"
|
||||
@ -10323,6 +10602,17 @@ remove-trailing-separator@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
|
||||
|
||||
renderkid@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.2.tgz#12d310f255360c07ad8fde253f6c9e9de372d2aa"
|
||||
integrity sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==
|
||||
dependencies:
|
||||
css-select "^1.1.0"
|
||||
dom-converter "~0.2"
|
||||
htmlparser2 "~3.3.0"
|
||||
strip-ansi "^3.0.0"
|
||||
utila "^0.4.0"
|
||||
|
||||
repeat-element@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
|
||||
@ -10728,6 +11018,14 @@ sax@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
scheduler@^0.13.3:
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.3.tgz#bed3c5850f62ea9c716a4d781f9daeb9b2a58896"
|
||||
integrity sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
schema-utils@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
|
||||
@ -11918,6 +12216,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
||||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
toposort@^1.0.0:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
|
||||
integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
|
||||
|
||||
tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
|
||||
@ -12100,7 +12403,7 @@ typescript@3.2.4:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
|
||||
integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==
|
||||
|
||||
uglify-js@^3.0.7, uglify-js@^3.1.4:
|
||||
uglify-js@3.4.x, uglify-js@^3.0.7, uglify-js@^3.1.4:
|
||||
version "3.4.9"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
|
||||
integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
|
||||
@ -12200,6 +12503,11 @@ update-notifier@2.5.0, update-notifier@^2.3.0:
|
||||
semver-diff "^2.0.0"
|
||||
xdg-basedir "^3.0.0"
|
||||
|
||||
upper-case@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@ -12270,7 +12578,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
util.promisify@^1.0.0:
|
||||
util.promisify@1.0.0, util.promisify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
|
||||
integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
|
||||
@ -12292,6 +12600,11 @@ util@^0.10.3:
|
||||
dependencies:
|
||||
inherits "2.0.3"
|
||||
|
||||
utila@^0.4.0, utila@~0.4:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
|
||||
integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
@ -12365,6 +12678,11 @@ w3c-hr-time@^1.0.1:
|
||||
dependencies:
|
||||
browser-process-hrtime "^0.1.2"
|
||||
|
||||
wait-for-expect@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.1.0.tgz#6607375c3f79d32add35cd2c87ce13f351a3d453"
|
||||
integrity sha512-vQDokqxyMyknfX3luCDn16bSaRcOyH6gGuUXMIbxBLeTo6nWuEWYqMTT9a+44FmW8c2m6TRWBdNvBBjA1hwEKg==
|
||||
|
||||
walker@~1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user